mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 03:02:36 +01:00
[llvm-objcopy] Implement IHEX writer
Differential revision: https://reviews.llvm.org/D60270 llvm-svn: 361949
This commit is contained in:
parent
5c1671ce9e
commit
eabc336677
@ -1177,11 +1177,14 @@ Error createStringError(std::error_code EC, char const *Msg);
|
||||
class FileError final : public ErrorInfo<FileError> {
|
||||
|
||||
friend Error createFileError(const Twine &, Error);
|
||||
friend Error createFileError(const Twine &, size_t, Error);
|
||||
|
||||
public:
|
||||
void log(raw_ostream &OS) const override {
|
||||
assert(Err && !FileName.empty() && "Trying to log after takeError().");
|
||||
OS << "'" << FileName << "': ";
|
||||
if (Line.hasValue())
|
||||
OS << "line " << Line.getValue() << ": ";
|
||||
Err->log(OS);
|
||||
}
|
||||
|
||||
@ -1193,26 +1196,36 @@ public:
|
||||
static char ID;
|
||||
|
||||
private:
|
||||
FileError(const Twine &F, std::unique_ptr<ErrorInfoBase> E) {
|
||||
FileError(const Twine &F, Optional<size_t> LineNum,
|
||||
std::unique_ptr<ErrorInfoBase> E) {
|
||||
assert(E && "Cannot create FileError from Error success value.");
|
||||
assert(!F.isTriviallyEmpty() &&
|
||||
"The file name provided to FileError must not be empty.");
|
||||
FileName = F.str();
|
||||
Err = std::move(E);
|
||||
Line = std::move(LineNum);
|
||||
}
|
||||
|
||||
static Error build(const Twine &F, Error E) {
|
||||
return Error(std::unique_ptr<FileError>(new FileError(F, E.takePayload())));
|
||||
static Error build(const Twine &F, Optional<size_t> Line, Error E) {
|
||||
return Error(
|
||||
std::unique_ptr<FileError>(new FileError(F, Line, E.takePayload())));
|
||||
}
|
||||
|
||||
std::string FileName;
|
||||
Optional<size_t> Line;
|
||||
std::unique_ptr<ErrorInfoBase> Err;
|
||||
};
|
||||
|
||||
/// Concatenate a source file path and/or name with an Error. The resulting
|
||||
/// Error is unchecked.
|
||||
inline Error createFileError(const Twine &F, Error E) {
|
||||
return FileError::build(F, std::move(E));
|
||||
return FileError::build(F, Optional<size_t>(), std::move(E));
|
||||
}
|
||||
|
||||
/// Concatenate a source file path and/or name with line number and an Error.
|
||||
/// The resulting Error is unchecked.
|
||||
inline Error createFileError(const Twine &F, size_t Line, Error E) {
|
||||
return FileError::build(F, Optional<size_t>(Line), std::move(E));
|
||||
}
|
||||
|
||||
/// Concatenate a source file path and/or name with a std::error_code
|
||||
@ -1221,6 +1234,12 @@ inline Error createFileError(const Twine &F, std::error_code EC) {
|
||||
return createFileError(F, errorCodeToError(EC));
|
||||
}
|
||||
|
||||
/// Concatenate a source file path and/or name with line number and
|
||||
/// std::error_code to form an Error object.
|
||||
inline Error createFileError(const Twine &F, size_t Line, std::error_code EC) {
|
||||
return createFileError(F, Line, errorCodeToError(EC));
|
||||
}
|
||||
|
||||
Error createFileError(const Twine &F, ErrorSuccess) = delete;
|
||||
|
||||
/// Helper for check-and-exit error handling.
|
||||
|
20
test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml
Normal file
20
test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
!ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_EXEC
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .text
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
|
||||
Address: 0x0
|
||||
AddressAlign: 0x8
|
||||
Content: "0001020304"
|
||||
ProgramHeaders:
|
||||
- Type: PT_NULL
|
||||
Flags: [ PF_X, PF_R ]
|
||||
VAddr: 0xF00000000
|
||||
PAddr: 0x100000
|
||||
Sections:
|
||||
- Section: .text
|
60
test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml
Normal file
60
test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml
Normal file
@ -0,0 +1,60 @@
|
||||
!ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_EXEC
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .text
|
||||
# This section contents exceeds default IHex line length of 16 bytes
|
||||
# so we expect two lines created for it.
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
|
||||
Address: 0x0
|
||||
AddressAlign: 0x8
|
||||
Content: "000102030405060708090A0B0C0D0E0F1011121314"
|
||||
- Name: .data
|
||||
# This section overlap 16-bit segment boundary, so we expect
|
||||
# additional 'SegmentAddr' record of type '02'
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Content: "3031323334353637383940"
|
||||
Address: 0xFFF8
|
||||
AddressAlign: 0x8
|
||||
- Name: .data2
|
||||
# Previous section '.data' should have forced creation of
|
||||
# 'SegmentAddr'(02) record with segment address of 0x10000,
|
||||
# so this section should have address of 0x100.
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Content: "40414243"
|
||||
Address: 0x10100
|
||||
AddressAlign: 0x8
|
||||
- Name: .data3
|
||||
# The last section not only overlaps segment boundary, but
|
||||
# also has linear address which doesn't fit 20 bits. The
|
||||
# following records should be craeted:
|
||||
# 'SegmentAddr'(02) record with address 0x0
|
||||
# 'ExtendedAddr'(04) record with address 0x100000
|
||||
# 'Data'(00) record with 8 bytes of section data
|
||||
# 'SegmentAddr'(02) record with address 0x10000
|
||||
# 'Data'(00) record with remaining 3 bytes of data.
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Content: "5051525354555657585960"
|
||||
Address: 0x10FFF8
|
||||
AddressAlign: 0x8
|
||||
- Name: .bss
|
||||
# NOBITS sections are not written to IHex
|
||||
Type: SHT_NOBITS
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Address: 0x10100
|
||||
Size: 0x1000
|
||||
AddressAlign: 0x8
|
||||
- Name: .dummy
|
||||
# Non-allocatable sections are not written to IHex
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ ]
|
||||
Address: 0x20FFF8
|
||||
Size: 65536
|
||||
AddressAlign: 0x8
|
39
test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml
Normal file
39
test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml
Normal file
@ -0,0 +1,39 @@
|
||||
!ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_EXEC
|
||||
Machine: EM_X86_64
|
||||
Sections:
|
||||
- Name: .text
|
||||
# Zero length sections are not exported to IHex
|
||||
# 'SegmentAddr' and 'ExtendedAddr' records aren't
|
||||
# created either.
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
|
||||
Address: 0x7FFFFFFF
|
||||
AddressAlign: 0x8
|
||||
Size: 0
|
||||
- Name: .text1
|
||||
# Section address is sign-extended 32-bit address
|
||||
# Data fits 32-bit range
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
|
||||
Address: 0xFFFFFFFF80001000
|
||||
AddressAlign: 0x8
|
||||
Content: "0001020304"
|
||||
- Name: .text2
|
||||
# Part of section data is in 32-bit address range
|
||||
# and part isn't.
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
|
||||
Address: 0xFFFFFFF8
|
||||
AddressAlign: 0x8
|
||||
Content: "000102030405060708"
|
||||
- Name: .text3
|
||||
# Entire secion is outside of 32-bit range
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
|
||||
Address: 0xFFFFFFFF0
|
||||
AddressAlign: 0x8
|
||||
Content: "0001020304"
|
60
test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml
Normal file
60
test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml
Normal file
@ -0,0 +1,60 @@
|
||||
# Here we use yaml from ihex-elf-sections.yaml, but add single load
|
||||
# segment containing all exported sections. In such case we should
|
||||
# use physical address of a section intead of virtual address. Physical
|
||||
# addresses start from 0x100000, so we create two additional 'ExtenededAddr'
|
||||
# (03) record in the beginning of IHex file with that physical address
|
||||
!ELF
|
||||
FileHeader:
|
||||
Class: ELFCLASS64
|
||||
Data: ELFDATA2LSB
|
||||
Type: ET_EXEC
|
||||
Machine: EM_X86_64
|
||||
Entry: 0x100000
|
||||
Sections:
|
||||
- Name: .text
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC, SHF_EXECINSTR ]
|
||||
Address: 0x0
|
||||
AddressAlign: 0x8
|
||||
Content: "000102030405060708090A0B0C0D0E0F1011121314"
|
||||
- Name: .data1
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Content: "3031323334353637383940"
|
||||
Address: 0xFFF8
|
||||
AddressAlign: 0x8
|
||||
- Name: .data2
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Content: "40414243"
|
||||
Address: 0x10100
|
||||
AddressAlign: 0x8
|
||||
- Name: .data3
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Content: "5051525354555657585960"
|
||||
Address: 0x10FFF8
|
||||
AddressAlign: 0x8
|
||||
- Name: .bss
|
||||
Type: SHT_NOBITS
|
||||
Flags: [ SHF_ALLOC ]
|
||||
Address: 0x10100
|
||||
Size: 0x1000
|
||||
AddressAlign: 0x8
|
||||
- Name: .dummy
|
||||
Type: SHT_PROGBITS
|
||||
Flags: [ ]
|
||||
Address: 0x20FFF8
|
||||
Size: 65536
|
||||
AddressAlign: 0x8
|
||||
ProgramHeaders:
|
||||
- Type: PT_LOAD
|
||||
Flags: [ PF_X, PF_R ]
|
||||
VAddr: 0xF00000000
|
||||
PAddr: 0x100000
|
||||
Sections:
|
||||
- Section: .text
|
||||
- Section: .data1
|
||||
- Section: .data2
|
||||
- Section: .data3
|
||||
- Section: .bss
|
81
test/tools/llvm-objcopy/ELF/ihex-writer.test
Normal file
81
test/tools/llvm-objcopy/ELF/ihex-writer.test
Normal file
@ -0,0 +1,81 @@
|
||||
# RUN: yaml2obj %p/Inputs/ihex-elf-sections.yaml -o %t
|
||||
# RUN: llvm-objcopy -O ihex %t - | FileCheck %s
|
||||
|
||||
# Check ihex output, when we have segments in ELF file
|
||||
# In such case only sections in PT_LOAD segments will
|
||||
# be exported and their physical addresses will be used
|
||||
# RUN: yaml2obj %p/Inputs/ihex-elf-segments.yaml -o %t-segs
|
||||
# RUN: llvm-objcopy -O ihex %t-segs - | FileCheck %s --check-prefix=SEGMENTS
|
||||
|
||||
# Check that non-load segments are ignored:
|
||||
# RUN: yaml2obj %p/Inputs/ihex-elf-pt-null.yaml -o %t2-segs
|
||||
# RUN: llvm-objcopy -O ihex %t2-segs - | FileCheck %s --check-prefix=PT_NULL
|
||||
|
||||
# Check that sign-extended 32-bit section addresses are processed
|
||||
# correctly
|
||||
# RUN: yaml2obj %p/Inputs/ihex-elf-sections2.yaml -o %t-sec2
|
||||
# RUN: llvm-objcopy -O ihex --only-section=.text1 %t-sec2 - | FileCheck %s --check-prefix=SIGN_EXTENDED
|
||||
|
||||
# Check that section address range overlapping 32 bit range
|
||||
# triggers an error
|
||||
# RUN: not llvm-objcopy -O ihex --only-section=.text2 %t-sec2 %t-sec2-2.hex 2>&1 | FileCheck %s --check-prefix=BAD-ADDR
|
||||
# RUN: not llvm-objcopy -O ihex --only-section=.text3 %t-sec2 %t-sec2-3.hex 2>&1 | FileCheck %s --check-prefix=BAD-ADDR2
|
||||
|
||||
# Check that zero length section is not written
|
||||
# RUN: llvm-objcopy -O ihex --only-section=.text %t-sec2 - | FileCheck %s --check-prefix=ZERO_SIZE_SEC
|
||||
|
||||
# Check 80x86 start address record. It is created for start
|
||||
# addresses less than 0x100000
|
||||
# RUN: llvm-objcopy -O ihex --set-start=0xFFFF %t - | FileCheck %s --check-prefix=START1
|
||||
|
||||
# Check i386 start address record (05). It is created for
|
||||
# start addresses which doesn't fit 20 bits
|
||||
# RUN: llvm-objcopy -O ihex --set-start=0x100000 %t - | FileCheck %s --check-prefix=START2
|
||||
|
||||
# We allow sign extended 32 bit start addresses as well.
|
||||
# RUN: llvm-objcopy -O ihex --set-start=0xFFFFFFFF80001000 %t - | FileCheck %s --check-prefix=START3
|
||||
|
||||
# Start address which exceeds 32 bit range triggers an error
|
||||
# RUN: not llvm-objcopy -O ihex --set-start=0xF00000000 %t %t6.hex 2>&1 | FileCheck %s --check-prefix=BAD-START
|
||||
|
||||
# CHECK: :10000000000102030405060708090A0B0C0D0E0F78
|
||||
# CHECK-NEXT: :05001000101112131491
|
||||
# CHECK-NEXT: :08FFF800303132333435363765
|
||||
# CHECK-NEXT: :020000021000EC
|
||||
# CHECK-NEXT: :030000003839404C
|
||||
# CHECK-NEXT: :0401000040414243F5
|
||||
# CHECK-NEXT: :020000020000FC
|
||||
# CHECK-NEXT: :020000040010EA
|
||||
# CHECK-NEXT: :08FFF800505152535455565765
|
||||
# CHECK-NEXT: :020000040011E9
|
||||
# CHECK-NEXT: :03000000585960EC
|
||||
# CHECK-NEXT: :00000001FF
|
||||
|
||||
# SEGMENTS: :020000040010EA
|
||||
# SEGMENTS-NEXT: :10000000000102030405060708090A0B0C0D0E0F78
|
||||
# SEGMENTS-NEXT: :05001000101112131491
|
||||
# SEGMENTS-NEXT: :0B001800303132333435363738394090
|
||||
# SEGMENTS-NEXT: :0400280040414243CE
|
||||
# SEGMENTS-NEXT: :0B003000505152535455565758596018
|
||||
# SEGMENTS-NEXT: :0400000500100000E7
|
||||
# SEGMENTS-NEXT: :00000001FF
|
||||
|
||||
# 'ExtendedAddr' (04) record shouldn't be created
|
||||
# PT_NULL-NOT: :02000004
|
||||
|
||||
# SIGN_EXTENDED: :0200000480007A
|
||||
# SIGN_EXTENDED-NEXT: :051000000001020304E1
|
||||
# SIGN_EXTENDED-NEXT: :00000001FF
|
||||
|
||||
# BAD-ADDR: error: {{.*}}: Section '.text2' address range [0xfffffff8, 0x100000000] is not 32 bit
|
||||
# BAD-ADDR2: error: {{.*}}: Section '.text3' address range [0xffffffff0, 0xffffffff4] is not 32 bit
|
||||
|
||||
# There shouldn't be 'ExtendedAddr' nor 'Data' records
|
||||
# ZERO_SIZE_SEC-NOT: :02000004
|
||||
# ZERO_SIZE_SEC-NOT: :00FFFF00
|
||||
# ZERO_SIZE_SEC: :00000001FF
|
||||
|
||||
# START1: :040000030000FFFFFB
|
||||
# START2: :0400000500100000E7
|
||||
# START3: :040000058000100067
|
||||
# BAD-START: error: {{.*}}: Entry point address 0xf00000000 overflows 32 bits
|
@ -458,7 +458,8 @@ Expected<DriverConfig> parseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
|
||||
return MI.takeError();
|
||||
Config.BinaryArch = *MI;
|
||||
}
|
||||
if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") {
|
||||
if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary" &&
|
||||
Config.OutputFormat != "ihex") {
|
||||
Expected<MachineInfo> MI = getOutputFormatMachineInfo(Config.OutputFormat);
|
||||
if (!MI)
|
||||
return MI.takeError();
|
||||
|
@ -130,12 +130,9 @@ static ElfType getOutputElfType(const MachineInfo &MI) {
|
||||
return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
|
||||
Object &Obj, Buffer &Buf,
|
||||
ElfType OutputElfType) {
|
||||
if (Config.OutputFormat == "binary") {
|
||||
return llvm::make_unique<BinaryWriter>(Obj, Buf);
|
||||
}
|
||||
static std::unique_ptr<Writer> createELFWriter(const CopyConfig &Config,
|
||||
Object &Obj, Buffer &Buf,
|
||||
ElfType OutputElfType) {
|
||||
// Depending on the initial ELFT and OutputFormat we need a different Writer.
|
||||
switch (OutputElfType) {
|
||||
case ELFT_ELF32LE:
|
||||
@ -154,6 +151,17 @@ static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
|
||||
llvm_unreachable("Invalid output format");
|
||||
}
|
||||
|
||||
static std::unique_ptr<Writer> createWriter(const CopyConfig &Config,
|
||||
Object &Obj, Buffer &Buf,
|
||||
ElfType OutputElfType) {
|
||||
using Functor = std::function<std::unique_ptr<Writer>()>;
|
||||
return StringSwitch<Functor>(Config.OutputFormat)
|
||||
.Case("binary", [&] { return llvm::make_unique<BinaryWriter>(Obj, Buf); })
|
||||
.Case("ihex", [&] { return llvm::make_unique<IHexWriter>(Obj, Buf); })
|
||||
.Default(
|
||||
[&] { return createELFWriter(Config, Obj, Buf, OutputElfType); })();
|
||||
}
|
||||
|
||||
template <class ELFT>
|
||||
static Expected<ArrayRef<uint8_t>>
|
||||
findBuildID(const CopyConfig &Config, const object::ELFFile<ELFT> &In) {
|
||||
@ -714,6 +722,15 @@ static Error handleArgs(const CopyConfig &Config, Object &Obj,
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out,
|
||||
ElfType OutputElfType) {
|
||||
std::unique_ptr<Writer> Writer =
|
||||
createWriter(Config, Obj, Out, OutputElfType);
|
||||
if (Error E = Writer->finalize())
|
||||
return E;
|
||||
return Writer->write();
|
||||
}
|
||||
|
||||
Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
|
||||
Buffer &Out) {
|
||||
BinaryReader Reader(Config.BinaryArch, &In);
|
||||
@ -721,15 +738,11 @@ Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In,
|
||||
|
||||
// Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch
|
||||
// (-B<arch>).
|
||||
const ElfType OutputElfType = getOutputElfType(
|
||||
Config.OutputArch ? Config.OutputArch.getValue() : Config.BinaryArch);
|
||||
const ElfType OutputElfType =
|
||||
getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch));
|
||||
if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType))
|
||||
return E;
|
||||
std::unique_ptr<Writer> Writer =
|
||||
createWriter(Config, *Obj, Out, OutputElfType);
|
||||
if (Error E = Writer->finalize())
|
||||
return E;
|
||||
return Writer->write();
|
||||
return writeOutput(Config, *Obj, Out, OutputElfType);
|
||||
}
|
||||
|
||||
Error executeObjcopyOnBinary(const CopyConfig &Config,
|
||||
@ -764,12 +777,8 @@ Error executeObjcopyOnBinary(const CopyConfig &Config,
|
||||
if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType))
|
||||
return createFileError(Config.InputFilename, std::move(E));
|
||||
|
||||
std::unique_ptr<Writer> Writer =
|
||||
createWriter(Config, *Obj, Out, OutputElfType);
|
||||
if (Error E = Writer->finalize())
|
||||
if (Error E = writeOutput(Config, *Obj, Out, OutputElfType))
|
||||
return createFileError(Config.InputFilename, std::move(E));
|
||||
if (Error E = Writer->write())
|
||||
return E;
|
||||
if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput)
|
||||
if (Error E =
|
||||
linkToBuildIdDir(Config, Config.OutputFilename,
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "llvm/MC/MCTargetOptions.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
#include "llvm/Support/Compression.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
#include "llvm/Support/Endian.h"
|
||||
#include "llvm/Support/ErrorHandling.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include "llvm/Support/Path.h"
|
||||
@ -147,6 +147,156 @@ void SectionWriter::visit(const Section &Sec) {
|
||||
llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset);
|
||||
}
|
||||
|
||||
static bool addressOverflows32bit(uint64_t Addr) {
|
||||
// Sign extended 32 bit addresses (e.g 0xFFFFFFFF80000000) are ok
|
||||
return Addr > UINT32_MAX && Addr + 0x80000000 > UINT32_MAX;
|
||||
}
|
||||
|
||||
template <class T> static T checkedGetHex(StringRef S) {
|
||||
T Value;
|
||||
bool Fail = S.getAsInteger(16, Value);
|
||||
assert(!Fail);
|
||||
(void)Fail;
|
||||
return Value;
|
||||
}
|
||||
|
||||
// Fills exactly Len bytes of buffer with hexadecimal characters
|
||||
// representing value 'X'
|
||||
template <class T, class Iterator>
|
||||
static Iterator utohexstr(T X, Iterator It, size_t Len) {
|
||||
// Fill range with '0'
|
||||
std::fill(It, It + Len, '0');
|
||||
|
||||
for (long I = Len - 1; I >= 0; --I) {
|
||||
unsigned char Mod = static_cast<unsigned char>(X) & 15;
|
||||
*(It + I) = hexdigit(Mod, false);
|
||||
X >>= 4;
|
||||
}
|
||||
assert(X == 0);
|
||||
return It + Len;
|
||||
}
|
||||
|
||||
uint8_t IHexRecord::getChecksum(StringRef S) {
|
||||
assert((S.size() & 1) == 0);
|
||||
uint8_t Checksum = 0;
|
||||
while (!S.empty()) {
|
||||
Checksum += checkedGetHex<uint8_t>(S.take_front(2));
|
||||
S = S.drop_front(2);
|
||||
}
|
||||
return -Checksum;
|
||||
}
|
||||
|
||||
IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr,
|
||||
ArrayRef<uint8_t> Data) {
|
||||
IHexLineData Line(getLineLength(Data.size()));
|
||||
assert(Line.size());
|
||||
auto Iter = Line.begin();
|
||||
*Iter++ = ':';
|
||||
Iter = utohexstr(Data.size(), Iter, 2);
|
||||
Iter = utohexstr(Addr, Iter, 4);
|
||||
Iter = utohexstr(Type, Iter, 2);
|
||||
for (uint8_t X : Data)
|
||||
Iter = utohexstr(X, Iter, 2);
|
||||
StringRef S(Line.data() + 1, std::distance(Line.begin() + 1, Iter));
|
||||
Iter = utohexstr(getChecksum(S), Iter, 2);
|
||||
*Iter++ = '\r';
|
||||
*Iter++ = '\n';
|
||||
assert(Iter == Line.end());
|
||||
return Line;
|
||||
}
|
||||
|
||||
static uint64_t sectionPhysicalAddr(const SectionBase *Sec) {
|
||||
Segment *Seg = Sec->ParentSegment;
|
||||
if (Seg && Seg->Type != ELF::PT_LOAD)
|
||||
Seg = nullptr;
|
||||
return Seg ? Seg->PAddr + Sec->OriginalOffset - Seg->OriginalOffset
|
||||
: Sec->Addr;
|
||||
}
|
||||
|
||||
void IHexSectionWriterBase::writeSection(const SectionBase *Sec,
|
||||
ArrayRef<uint8_t> Data) {
|
||||
assert(Data.size() == Sec->Size);
|
||||
const uint32_t ChunkSize = 16;
|
||||
uint32_t Addr = sectionPhysicalAddr(Sec) & 0xFFFFFFFFU;
|
||||
while (!Data.empty()) {
|
||||
uint64_t DataSize = std::min<uint64_t>(Data.size(), ChunkSize);
|
||||
if (Addr > SegmentAddr + BaseAddr + 0xFFFFU) {
|
||||
if (Addr > 0xFFFFFU) {
|
||||
// Write extended address record, zeroing segment address
|
||||
// if needed.
|
||||
if (SegmentAddr != 0)
|
||||
SegmentAddr = writeSegmentAddr(0U);
|
||||
BaseAddr = writeBaseAddr(Addr);
|
||||
} else {
|
||||
// We can still remain 16-bit
|
||||
SegmentAddr = writeSegmentAddr(Addr);
|
||||
}
|
||||
}
|
||||
uint64_t SegOffset = Addr - BaseAddr - SegmentAddr;
|
||||
assert(SegOffset <= 0xFFFFU);
|
||||
DataSize = std::min(DataSize, 0x10000U - SegOffset);
|
||||
writeData(0, SegOffset, Data.take_front(DataSize));
|
||||
Addr += DataSize;
|
||||
Data = Data.drop_front(DataSize);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t IHexSectionWriterBase::writeSegmentAddr(uint64_t Addr) {
|
||||
assert(Addr <= 0xFFFFFU);
|
||||
uint8_t Data[] = {static_cast<uint8_t>((Addr & 0xF0000U) >> 12), 0};
|
||||
writeData(2, 0, Data);
|
||||
return Addr & 0xF0000U;
|
||||
}
|
||||
|
||||
uint64_t IHexSectionWriterBase::writeBaseAddr(uint64_t Addr) {
|
||||
assert(Addr <= 0xFFFFFFFFU);
|
||||
uint64_t Base = Addr & 0xFFFF0000U;
|
||||
uint8_t Data[] = {static_cast<uint8_t>(Base >> 24),
|
||||
static_cast<uint8_t>((Base >> 16) & 0xFF)};
|
||||
writeData(4, 0, Data);
|
||||
return Base;
|
||||
}
|
||||
|
||||
void IHexSectionWriterBase::writeData(uint8_t Type, uint16_t Addr,
|
||||
ArrayRef<uint8_t> Data) {
|
||||
Offset += IHexRecord::getLineLength(Data.size());
|
||||
}
|
||||
|
||||
void IHexSectionWriterBase::visit(const Section &Sec) {
|
||||
writeSection(&Sec, Sec.Contents);
|
||||
}
|
||||
|
||||
void IHexSectionWriterBase::visit(const OwnedDataSection &Sec) {
|
||||
writeSection(&Sec, Sec.Data);
|
||||
}
|
||||
|
||||
void IHexSectionWriterBase::visit(const StringTableSection &Sec) {
|
||||
// Check that sizer has already done its work
|
||||
assert(Sec.Size == Sec.StrTabBuilder.getSize());
|
||||
// We are free to pass an invalid pointer to writeSection as long
|
||||
// as we don't actually write any data. The real writer class has
|
||||
// to override this method .
|
||||
writeSection(&Sec, {nullptr, Sec.Size});
|
||||
}
|
||||
|
||||
void IHexSectionWriterBase::visit(const DynamicRelocationSection &Sec) {
|
||||
writeSection(&Sec, Sec.Contents);
|
||||
}
|
||||
|
||||
void IHexSectionWriter::writeData(uint8_t Type, uint16_t Addr,
|
||||
ArrayRef<uint8_t> Data) {
|
||||
IHexLineData HexData = IHexRecord::getLine(Type, Addr, Data);
|
||||
memcpy(Out.getBufferStart() + Offset, HexData.data(), HexData.size());
|
||||
Offset += HexData.size();
|
||||
}
|
||||
|
||||
void IHexSectionWriter::visit(const StringTableSection &Sec) {
|
||||
assert(Sec.Size == Sec.StrTabBuilder.getSize());
|
||||
std::vector<uint8_t> Data(Sec.Size);
|
||||
Sec.StrTabBuilder.write(Data.data());
|
||||
writeSection(&Sec, Data);
|
||||
}
|
||||
|
||||
void Section::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); }
|
||||
|
||||
void Section::accept(MutableSectionVisitor &Visitor) { Visitor.visit(*this); }
|
||||
@ -217,6 +367,15 @@ void OwnedDataSection::accept(MutableSectionVisitor &Visitor) {
|
||||
Visitor.visit(*this);
|
||||
}
|
||||
|
||||
void OwnedDataSection::appendHexData(StringRef HexData) {
|
||||
assert((HexData.size() & 1) == 0);
|
||||
while (!HexData.empty()) {
|
||||
Data.push_back(checkedGetHex<uint8_t>(HexData.take_front(2)));
|
||||
HexData = HexData.drop_front(2);
|
||||
}
|
||||
Size = Data.size();
|
||||
}
|
||||
|
||||
void BinarySectionWriter::visit(const CompressedSection &Sec) {
|
||||
error("cannot write compressed section '" + Sec.Name + "' ");
|
||||
}
|
||||
@ -1807,6 +1966,109 @@ Error BinaryWriter::finalize() {
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
bool IHexWriter::SectionCompare::operator()(const SectionBase *Lhs,
|
||||
const SectionBase *Rhs) const {
|
||||
return (sectionPhysicalAddr(Lhs) & 0xFFFFFFFFU) <
|
||||
(sectionPhysicalAddr(Rhs) & 0xFFFFFFFFU);
|
||||
}
|
||||
|
||||
uint64_t IHexWriter::writeEntryPointRecord(uint8_t *Buf) {
|
||||
IHexLineData HexData;
|
||||
uint8_t Data[4] = {};
|
||||
// We don't write entry point record if entry is zero.
|
||||
if (Obj.Entry == 0)
|
||||
return 0;
|
||||
|
||||
if (Obj.Entry <= 0xFFFFFU) {
|
||||
Data[0] = ((Obj.Entry & 0xF0000U) >> 12) & 0xFF;
|
||||
support::endian::write(&Data[2], static_cast<uint16_t>(Obj.Entry),
|
||||
support::big);
|
||||
HexData = IHexRecord::getLine(IHexRecord::StartAddr80x86, 0, Data);
|
||||
} else {
|
||||
support::endian::write(Data, static_cast<uint32_t>(Obj.Entry),
|
||||
support::big);
|
||||
HexData = IHexRecord::getLine(IHexRecord::StartAddr, 0, Data);
|
||||
}
|
||||
memcpy(Buf, HexData.data(), HexData.size());
|
||||
return HexData.size();
|
||||
}
|
||||
|
||||
uint64_t IHexWriter::writeEndOfFileRecord(uint8_t *Buf) {
|
||||
IHexLineData HexData = IHexRecord::getLine(IHexRecord::EndOfFile, 0, {});
|
||||
memcpy(Buf, HexData.data(), HexData.size());
|
||||
return HexData.size();
|
||||
}
|
||||
|
||||
Error IHexWriter::write() {
|
||||
IHexSectionWriter Writer(Buf);
|
||||
// Write sections.
|
||||
for (const SectionBase *Sec : Sections)
|
||||
Sec->accept(Writer);
|
||||
|
||||
uint64_t Offset = Writer.getBufferOffset();
|
||||
// Write entry point address.
|
||||
Offset += writeEntryPointRecord(Buf.getBufferStart() + Offset);
|
||||
// Write EOF.
|
||||
Offset += writeEndOfFileRecord(Buf.getBufferStart() + Offset);
|
||||
assert(Offset == TotalSize);
|
||||
return Buf.commit();
|
||||
}
|
||||
|
||||
Error IHexWriter::checkSection(const SectionBase &Sec) {
|
||||
uint64_t Addr = sectionPhysicalAddr(&Sec);
|
||||
if (addressOverflows32bit(Addr) || addressOverflows32bit(Addr + Sec.Size - 1))
|
||||
return createStringError(
|
||||
errc::invalid_argument,
|
||||
"Section '%s' address range [%p, %p] is not 32 bit", Sec.Name.c_str(),
|
||||
Addr, Addr + Sec.Size - 1);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error IHexWriter::finalize() {
|
||||
bool UseSegments = false;
|
||||
auto ShouldWrite = [](const SectionBase &Sec) {
|
||||
return (Sec.Flags & ELF::SHF_ALLOC) && (Sec.Type != ELF::SHT_NOBITS);
|
||||
};
|
||||
auto IsInPtLoad = [](const SectionBase &Sec) {
|
||||
return Sec.ParentSegment && Sec.ParentSegment->Type == ELF::PT_LOAD;
|
||||
};
|
||||
|
||||
// We can't write 64-bit addresses.
|
||||
if (addressOverflows32bit(Obj.Entry))
|
||||
return createStringError(errc::invalid_argument,
|
||||
"Entry point address %p overflows 32 bits.",
|
||||
Obj.Entry);
|
||||
|
||||
// If any section we're to write has segment then we
|
||||
// switch to using physical addresses. Otherwise we
|
||||
// use section virtual address.
|
||||
for (auto &Section : Obj.sections())
|
||||
if (ShouldWrite(Section) && IsInPtLoad(Section)) {
|
||||
UseSegments = true;
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto &Section : Obj.sections())
|
||||
if (ShouldWrite(Section) && (!UseSegments || IsInPtLoad(Section))) {
|
||||
if (Error E = checkSection(Section))
|
||||
return E;
|
||||
Sections.insert(&Section);
|
||||
}
|
||||
|
||||
IHexSectionWriterBase LengthCalc(Buf);
|
||||
for (const SectionBase *Sec : Sections)
|
||||
Sec->accept(LengthCalc);
|
||||
|
||||
// We need space to write section records + StartAddress record
|
||||
// (if start adress is not zero) + EndOfFile record.
|
||||
TotalSize = LengthCalc.getBufferOffset() +
|
||||
(Obj.Entry ? IHexRecord::getLineLength(4) : 0) +
|
||||
IHexRecord::getLineLength(0);
|
||||
if (Error E = Buf.allocate(TotalSize))
|
||||
return E;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
template class ELFBuilder<ELF64LE>;
|
||||
template class ELFBuilder<ELF64BE>;
|
||||
template class ELFBuilder<ELF32LE>;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "llvm/BinaryFormat/ELF.h"
|
||||
#include "llvm/MC/StringTableBuilder.h"
|
||||
#include "llvm/Object/ELFObjectFile.h"
|
||||
#include "llvm/Support/Errc.h"
|
||||
#include "llvm/Support/FileOutputBuffer.h"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@ -168,6 +169,8 @@ public:
|
||||
|
||||
#define MAKE_SEC_WRITER_FRIEND \
|
||||
friend class SectionWriter; \
|
||||
friend class IHexSectionWriterBase; \
|
||||
friend class IHexSectionWriter; \
|
||||
template <class ELFT> friend class ELFSectionWriter; \
|
||||
template <class ELFT> friend class ELFSectionSizer;
|
||||
|
||||
@ -186,6 +189,114 @@ public:
|
||||
explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
|
||||
};
|
||||
|
||||
using IHexLineData = SmallVector<char, 64>;
|
||||
|
||||
struct IHexRecord {
|
||||
// Memory address of the record.
|
||||
uint16_t Addr;
|
||||
// Record type (see below).
|
||||
uint16_t Type;
|
||||
// Record data in hexadecimal form.
|
||||
StringRef HexData;
|
||||
|
||||
// Helper method to get file length of the record
|
||||
// including newline character
|
||||
static size_t getLength(size_t DataSize) {
|
||||
// :LLAAAATT[DD...DD]CC'
|
||||
return DataSize * 2 + 11;
|
||||
}
|
||||
|
||||
// Gets length of line in a file (getLength + CRLF).
|
||||
static size_t getLineLength(size_t DataSize) {
|
||||
return getLength(DataSize) + 2;
|
||||
}
|
||||
|
||||
// Given type, address and data returns line which can
|
||||
// be written to output file.
|
||||
static IHexLineData getLine(uint8_t Type, uint16_t Addr,
|
||||
ArrayRef<uint8_t> Data);
|
||||
|
||||
// Calculates checksum of stringified record representation
|
||||
// S must NOT contain leading ':' and trailing whitespace
|
||||
// characters
|
||||
static uint8_t getChecksum(StringRef S);
|
||||
|
||||
enum Type {
|
||||
// Contains data and a 16-bit starting address for the data.
|
||||
// The byte count specifies number of data bytes in the record.
|
||||
Data = 0,
|
||||
// Must occur exactly once per file in the last line of the file.
|
||||
// The data field is empty (thus byte count is 00) and the address
|
||||
// field is typically 0000.
|
||||
EndOfFile = 1,
|
||||
// The data field contains a 16-bit segment base address (thus byte
|
||||
// count is always 02) compatible with 80x86 real mode addressing.
|
||||
// The address field (typically 0000) is ignored. The segment address
|
||||
// from the most recent 02 record is multiplied by 16 and added to each
|
||||
// subsequent data record address to form the physical starting address
|
||||
// for the data. This allows addressing up to one megabyte of address
|
||||
// space.
|
||||
SegmentAddr = 2,
|
||||
// or 80x86 processors, specifies the initial content of the CS:IP
|
||||
// registers. The address field is 0000, the byte count is always 04,
|
||||
// the first two data bytes are the CS value, the latter two are the
|
||||
// IP value.
|
||||
StartAddr80x86 = 3,
|
||||
// Allows for 32 bit addressing (up to 4GiB). The record's address field
|
||||
// is ignored (typically 0000) and its byte count is always 02. The two
|
||||
// data bytes (big endian) specify the upper 16 bits of the 32 bit
|
||||
// absolute address for all subsequent type 00 records
|
||||
ExtendedAddr = 4,
|
||||
// The address field is 0000 (not used) and the byte count is always 04.
|
||||
// The four data bytes represent a 32-bit address value. In the case of
|
||||
// 80386 and higher CPUs, this address is loaded into the EIP register.
|
||||
StartAddr = 5,
|
||||
// We have no other valid types
|
||||
InvalidType = 6
|
||||
};
|
||||
};
|
||||
|
||||
// Base class for IHexSectionWriter. This class implements writing algorithm,
|
||||
// but doesn't actually write records. It is used for output buffer size
|
||||
// calculation in IHexWriter::finalize.
|
||||
class IHexSectionWriterBase : public BinarySectionWriter {
|
||||
// 20-bit segment address
|
||||
uint32_t SegmentAddr = 0;
|
||||
// Extended linear address
|
||||
uint32_t BaseAddr = 0;
|
||||
|
||||
// Write segment address corresponding to 'Addr'
|
||||
uint64_t writeSegmentAddr(uint64_t Addr);
|
||||
// Write extended linear (base) address corresponding to 'Addr'
|
||||
uint64_t writeBaseAddr(uint64_t Addr);
|
||||
|
||||
protected:
|
||||
// Offset in the output buffer
|
||||
uint64_t Offset = 0;
|
||||
|
||||
void writeSection(const SectionBase *Sec, ArrayRef<uint8_t> Data);
|
||||
virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data);
|
||||
|
||||
public:
|
||||
explicit IHexSectionWriterBase(Buffer &Buf) : BinarySectionWriter(Buf) {}
|
||||
|
||||
uint64_t getBufferOffset() const { return Offset; }
|
||||
void visit(const Section &Sec) final;
|
||||
void visit(const OwnedDataSection &Sec) final;
|
||||
void visit(const StringTableSection &Sec) override;
|
||||
void visit(const DynamicRelocationSection &Sec) final;
|
||||
using BinarySectionWriter::visit;
|
||||
};
|
||||
|
||||
// Real IHEX section writer
|
||||
class IHexSectionWriter : public IHexSectionWriterBase {
|
||||
public:
|
||||
IHexSectionWriter(Buffer &Buf) : IHexSectionWriterBase(Buf) {}
|
||||
|
||||
void writeData(uint8_t Type, uint16_t Addr, ArrayRef<uint8_t> Data) override;
|
||||
void visit(const StringTableSection &Sec) override;
|
||||
};
|
||||
|
||||
class Writer {
|
||||
protected:
|
||||
Object &Obj;
|
||||
@ -245,6 +356,25 @@ public:
|
||||
BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
|
||||
};
|
||||
|
||||
class IHexWriter : public Writer {
|
||||
struct SectionCompare {
|
||||
bool operator()(const SectionBase *Lhs, const SectionBase *Rhs) const;
|
||||
};
|
||||
|
||||
std::set<const SectionBase *, SectionCompare> Sections;
|
||||
size_t TotalSize;
|
||||
|
||||
Error checkSection(const SectionBase &Sec);
|
||||
uint64_t writeEntryPointRecord(uint8_t *Buf);
|
||||
uint64_t writeEndOfFileRecord(uint8_t *Buf);
|
||||
|
||||
public:
|
||||
~IHexWriter() {}
|
||||
Error finalize() override;
|
||||
Error write() override;
|
||||
IHexWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
|
||||
};
|
||||
|
||||
class SectionBase {
|
||||
public:
|
||||
std::string Name;
|
||||
@ -361,6 +491,16 @@ public:
|
||||
OriginalOffset = std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
OwnedDataSection(const Twine &SecName, uint64_t SecAddr, uint64_t SecFlags,
|
||||
uint64_t SecOff) {
|
||||
Name = SecName.str();
|
||||
Type = ELF::SHT_PROGBITS;
|
||||
Addr = SecAddr;
|
||||
Flags = SecFlags;
|
||||
OriginalOffset = SecOff;
|
||||
}
|
||||
|
||||
void appendHexData(StringRef HexData);
|
||||
void accept(SectionVisitor &Sec) const override;
|
||||
void accept(MutableSectionVisitor &Visitor) override;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user