Index: test/tools/llvm-objcopy/ELF/Inputs/sections.hex =================================================================== --- test/tools/llvm-objcopy/ELF/Inputs/sections.hex +++ test/tools/llvm-objcopy/ELF/Inputs/sections.hex @@ -0,0 +1,15 @@ +:020000021000EC +:08FFF8000001020304050607E5 +:020000022000DC +:0300000008090AE2 +:02000002F0000C +:08FFF800303132333435363765 +:020000020000FC +:020000040010EA +:030000003839404C +:02000004001FDB +:08FFF8004041424344454647E5 +:020000040020DA +:030000004849501C +:0400000300000000F9 +:00000001FF Index: test/tools/llvm-objcopy/ELF/ihex-reader.test =================================================================== --- test/tools/llvm-objcopy/ELF/ihex-reader.test +++ test/tools/llvm-objcopy/ELF/ihex-reader.test @@ -0,0 +1,221 @@ +# Check section headers when converting from hex to ELF +# RUN: yaml2obj %p/Inputs/ihex-elf-sections.yaml -o %t +# RUN: llvm-objcopy -O ihex %t %t.hex +# RUN: llvm-objcopy -I ihex %t.hex %t2 +# RUN: llvm-readobj -section-headers %t2 | FileCheck %s + +# Check section contents +# RUN: llvm-objcopy -O binary --only-section=.text %t %t.text +# RUN: llvm-objcopy -O binary --only-section=.sec1 %t2 %t2.sec1 +# RUN: cmp %t.text %t2.sec1 +# RUN: llvm-objcopy -O binary --only-section=.data %t %t.data +# RUN: llvm-objcopy -O binary --only-section=.sec2 %t2 %t2.sec2 +# RUN: cmp %t.data %t2.sec2 +# RUN: llvm-objcopy -O binary --only-section=.data2 %t %t.data2 +# RUN: llvm-objcopy -O binary --only-section=.sec3 %t2 %t2.sec3 +# RUN: cmp %t.data2 %t2.sec3 +# RUN: llvm-objcopy -O binary --only-section=.data3 %t %t.data3 +# RUN: llvm-objcopy -O binary --only-section=.sec4 %t2 %t2.sec4 +# RUN: cmp %t.data3 %t2.sec4 + +# Check loading from raw hex file +# RUN: llvm-objcopy -I ihex %p/Inputs/sections.hex %t-raw +# RUN: llvm-readobj -section-headers %t-raw | FileCheck %s --check-prefix=RAW + +# Check section contents +# RUN: llvm-objcopy -O ihex --only-section=.sec1 %t-raw %t-raw-sec1.hex +# RUN: cat %t-raw-sec1.hex | FileCheck %s --check-prefix=RAW-SEC1 +# RUN: llvm-objcopy -O ihex --only-section=.sec2 %t-raw %t-raw-sec2.hex +# RUN: cat %t-raw-sec2.hex | FileCheck %s --check-prefix=RAW-SEC2 +# RUN: llvm-objcopy -O ihex --only-section=.sec3 %t-raw %t-raw-sec3.hex +# RUN: cat %t-raw-sec3.hex | FileCheck %s --check-prefix=RAW-SEC3 + +# Check that line is trimmed from whitespace +# RUN: echo " :0100000001FE " > %t-spaced.hex +# RUN: llvm-objcopy -I ihex %t-spaced.hex %t-spaced +# RUN: llvm-objcopy -O ihex %t-spaced %t-spaced2.hex +# RUN: cat %t-spaced2.hex | FileCheck %s --check-prefix=SPACES + +# Check for various parsing errors +# 1. String too short +# RUN: echo "01000000FF" > %t-bad.hex +# RUN: not llvm-objcopy -I ihex %t-bad.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_LENGTH + +# 2. missing ':' +# RUN: echo "0100000000FF" > %t-bad2.hex +# RUN: not llvm-objcopy -I ihex %t-bad2.hex %t-none 2>&1 | FileCheck %s --check-prefix=MISSING_COLON + +# 3. invalid charatcer +# RUN: echo ":01000000xF" > %t-bad3.hex +# RUN: not llvm-objcopy -I ihex %t-bad3.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_CHAR + +# 4. incorrect string length +# RUN: echo ":010000000000000F" > %t-bad4.hex +# RUN: not llvm-objcopy -I ihex %t-bad4.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_LENGTH2 + +# 5. invalid type (06) +# RUN: echo ":00000006FA" > %t-bad5.hex +# RUN: not llvm-objcopy -I ihex %t-bad5.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_TYPE + +# 6. invalid checksum +# RUN: echo ":00000001FA" > %t-bad6.hex +# RUN: not llvm-objcopy -I ihex %t-bad6.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_CKSUM + +# 7. zero data length +# RUN: echo ":00010000FF" > %t-bad7.hex +# RUN: not llvm-objcopy -I ihex %t-bad7.hex %t-none 2>&1 | FileCheck %s --check-prefix=ZERO_DATA_LEN + +# 8. Bad data length for '02' (SegmentAddr) record +# RUN: echo ":03000002000000FB" > %t-bad8.hex +# RUN: not llvm-objcopy -I ihex %t-bad8.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_SEGADDR_LEN + +# 9. Bad data length for '03' (StartAddr80x86) record +# RUN: echo ":03000003000000FA" > %t-bad9.hex +# RUN: not llvm-objcopy -I ihex %t-bad9.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_STARTADDR_LEN + +# 10. Bad data length for '05' (StartAddr) record +# RUN: echo ":03000005000000F8" > %t-bad10.hex +# RUN: not llvm-objcopy -I ihex %t-bad10.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_STARTADDR_LEN + +# 11. Address value for 'StartAddr80x86' is greater then 0xFFFFFU +# RUN: echo ":04000003FFFFFFFFFD" > %t-bad11.hex +# RUN: not llvm-objcopy -I ihex %t-bad11.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_STARTADDR + +# 12. Invalid extended address data size +# RUN: echo ":04000004FFFFFFFFFC" > %t-bad12.hex +# RUN: not llvm-objcopy -I ihex %t-bad12.hex %t-none 2>&1 | FileCheck %s --check-prefix=BAD_EXTADDR_LEN + +# 13. no sections in the hex file +# a) try empty file: +# RUN: echo "" > %t-bad13.hex +# RUN: not llvm-objcopy -I ihex %t-bad13.hex %t-none 2>&1 | FileCheck %s --check-prefix=NO_SECTIONS +# b) EOF record should cancel processing further records. Not having any section data +# before EOF should trigger an error +# RUN: echo ":00000001FF" > %t-bad14.hex +# RUN: echo ":0100000001FE" >> %t-bad14.hex +# RUN: not llvm-objcopy -I ihex %t-bad14.hex %t-none 2>&1 | FileCheck %s --check-prefix=NO_SECTIONS + +# CHECK: Index: 1 +# CHECK-NEXT: Name: .sec1 (35) +# CHECK-NEXT: Type: SHT_PROGBITS (0x1) +# CHECK-NEXT: Flags [ (0x3) +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: SHF_WRITE (0x1) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x0 +# CHECK-NEXT: Offset: 0x34 +# CHECK-NEXT: Size: 21 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 1 +# CHECK-NEXT: EntrySize: 0 + +# CHECK: Index: 2 +# CHECK-NEXT: Name: .sec2 (29) +# CHECK-NEXT: Type: SHT_PROGBITS (0x1) +# CHECK-NEXT: Flags [ (0x3) +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: SHF_WRITE (0x1) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0xFFF8 +# CHECK-NEXT: Offset: 0x49 +# CHECK-NEXT: Size: 11 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 1 +# CHECK-NEXT: EntrySize: 0 + +# CHECK: Index: 3 +# CHECK-NEXT: Name: .sec3 (23) +# CHECK-NEXT: Type: SHT_PROGBITS (0x1) +# CHECK-NEXT: Flags [ (0x3) +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: SHF_WRITE (0x1) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x10100 +# CHECK-NEXT: Offset: 0x54 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 1 +# CHECK-NEXT: EntrySize: 0 + +# CHECK: Index: 4 +# CHECK-NEXT: Name: .sec4 (17) +# CHECK-NEXT: Type: SHT_PROGBITS (0x1) +# CHECK-NEXT: Flags [ (0x3) +# CHECK-NEXT: SHF_ALLOC (0x2) +# CHECK-NEXT: SHF_WRITE (0x1) +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x10FFF8 +# CHECK-NEXT: Offset: 0x58 +# CHECK-NEXT: Size: 11 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 1 +# CHECK-NEXT: EntrySize: 0 + +# RAW: Index: 1 +# RAW-NEXT: Name: .sec1 (29) +# RAW-NEXT: Type: SHT_PROGBITS (0x1) +# RAW-NEXT: Flags [ (0x3) +# RAW-NEXT: SHF_ALLOC (0x2) +# RAW-NEXT: SHF_WRITE (0x1) +# RAW-NEXT: ] +# RAW-NEXT: Address: 0x1FFF8 +# RAW-NEXT: Offset: 0x34 +# RAW-NEXT: Size: 11 + +# RAW: Index: 2 +# RAW-NEXT: Name: .sec2 (23) +# RAW-NEXT: Type: SHT_PROGBITS (0x1) +# RAW-NEXT: Flags [ (0x3) +# RAW-NEXT: SHF_ALLOC (0x2) +# RAW-NEXT: SHF_WRITE (0x1) +# RAW-NEXT: ] +# RAW-NEXT: Address: 0xFFFF8 +# RAW-NEXT: Offset: 0x3F +# RAW-NEXT: Size: 11 + +# RAW: Index: 3 +# RAW-NEXT: Name: .sec3 (17) +# RAW-NEXT: Type: SHT_PROGBITS (0x1) +# RAW-NEXT: Flags [ (0x3) +# RAW-NEXT: SHF_ALLOC (0x2) +# RAW-NEXT: SHF_WRITE (0x1) +# RAW-NEXT: ] +# RAW-NEXT: Address: 0x1FFFF8 +# RAW-NEXT: Offset: 0x4A +# RAW-NEXT: Size: 11 + +# RAW-SEC1: :020000021000EC +# RAW-SEC1-NEXT: :08FFF8000001020304050607E5 +# RAW-SEC1-NEXT: :020000022000DC +# RAW-SEC1-NEXT: :0300000008090AE2 + +# RAW-SEC2: :02000002F0000C +# RAW-SEC2-NEXT: :08FFF800303132333435363765 +# RAW-SEC2-NEXT: :020000020000FC +# RAW-SEC2-NEXT: :020000040010EA +# RAW-SEC2-NEXT: :030000003839404C + +# RAW-SEC3: :02000004001FDB +# RAW-SEC3-NEXT: :08FFF8004041424344454647E5 +# RAW-SEC3-NEXT: :020000040020DA +# RAW-SEC3-NEXT: :030000004849501C + +# SPACES: :0100000001FE +# SPACES-NEXT: :00000001FF + +# BAD_LENGTH: error: '{{.*}}.hex': line 1: line is too short: 10 chars +# MISSING_COLON: error: '{{.*}}.hex': line 1: missing ':' in the beginning of line +# BAD_CHAR: error: '{{.*}}.hex': line 1: invalid character at position 10 +# BAD_LENGTH2: error: '{{.*}}.hex': line 1: invalid line length 17 (should be 13) +# BAD_TYPE: error: '{{.*}}.hex': line 1: unknown record type: 6 +# BAD_CKSUM: error: '{{.*}}.hex': line 1: incorrect checksum +# ZERO_DATA_LEN: error: '{{.*}}.hex': line 1: zero data length is not allowed for data records +# BAD_SEGADDR_LEN: error: '{{.*}}.hex': line 1: segment address data should be 2 bytes in size +# BAD_STARTADDR_LEN: error: '{{.*}}.hex': line 1: start address data should be 4 bytes in size +# BAD_STARTADDR: error: '{{.*}}.hex': line 1: start address exceeds 20 bit for 80x86 +# BAD_EXTADDR_LEN: error: '{{.*}}.hex': line 1: extended address data should be 2 bytes in size +# NO_SECTIONS: error: '{{.*}}.hex': no sections Index: tools/llvm-objcopy/ELF/ELFObjcopy.h =================================================================== --- tools/llvm-objcopy/ELF/ELFObjcopy.h +++ tools/llvm-objcopy/ELF/ELFObjcopy.h @@ -22,6 +22,8 @@ class Buffer; namespace elf { +Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out); Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, Buffer &Out); Error executeObjcopyOnBinary(const CopyConfig &Config, Index: tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -731,6 +731,17 @@ return Writer->write(); } +Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + IHexReader Reader(&In); + std::unique_ptr Obj = Reader.create(); + const ElfType OutputElfType = + getOutputElfType(Config.OutputArch.getValueOr(Config.BinaryArch)); + if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType)) + return E; + return writeOutput(Config, *Obj, Out, OutputElfType); +} + Error executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, Buffer &Out) { BinaryReader Reader(Config.BinaryArch, &In); Index: tools/llvm-objcopy/ELF/Object.h =================================================================== --- tools/llvm-objcopy/ELF/Object.h +++ tools/llvm-objcopy/ELF/Object.h @@ -216,6 +216,10 @@ static IHexLineData getLine(uint8_t Type, uint16_t Addr, ArrayRef Data); + // Parses the line and returns record if possible. + // Line should be trimmed from whitespace characters. + static Expected parse(StringRef Line); + // Calculates checksum of stringified record representation // S must NOT contain leading ':' and trailing whitespace // characters @@ -821,15 +825,15 @@ public: explicit DynamicRelocationSection(ArrayRef Data) : Contents(Data) {} - - void accept(SectionVisitor &) const override; - void accept(MutableSectionVisitor &Visitor) override; - Error removeSectionReferences( - bool AllowBrokenLinks, - function_ref ToRemove) override; - - static bool classof(const SectionBase *S) { - if (!(S->Flags & ELF::SHF_ALLOC)) + + void accept(SectionVisitor &) const override; + void accept(MutableSectionVisitor &Visitor) override; + Error removeSectionReferences( + bool AllowBrokenLinks, + function_ref ToRemove) override; + + static bool classof(const SectionBase *S) { + if (!(S->Flags & ELF::SHF_ALLOC)) return false; return S->Type == ELF::SHT_REL || S->Type == ELF::SHT_RELA; } @@ -862,21 +866,41 @@ using object::ELFObjectFile; using object::OwningBinary; -class BinaryELFBuilder { +class BasicELFBuilder { +protected: uint16_t EMachine; - MemoryBuffer *MemBuf; std::unique_ptr Obj; void initFileHeader(); void initHeaderSegment(); StringTableSection *addStrTab(); SymbolTableSection *addSymTab(StringTableSection *StrTab); - void addData(SymbolTableSection *SymTab); void initSections(); public: + BasicELFBuilder(uint16_t EM) + : EMachine(EM), Obj(llvm::make_unique()) {} +}; + +class BinaryELFBuilder : public BasicELFBuilder { + MemoryBuffer *MemBuf; + void addData(SymbolTableSection *SymTab); + +public: BinaryELFBuilder(uint16_t EM, MemoryBuffer *MB) - : EMachine(EM), MemBuf(MB), Obj(llvm::make_unique()) {} + : BasicELFBuilder(EM), MemBuf(MB) {} + + std::unique_ptr build(); +}; + +class IHexELFBuilder : public BasicELFBuilder { + const std::vector &Records; + + void addDataSections(); + +public: + IHexELFBuilder(const std::vector &Records) + : BasicELFBuilder(ELF::EM_386), Records(Records) {} std::unique_ptr build(); }; @@ -914,6 +938,28 @@ std::unique_ptr create() const override; }; +class IHexReader : public Reader { + MemoryBuffer *MemBuf; + + Expected> parse() const; + Error parseError(size_t LineNo, Error E) const { + return LineNo == -1U + ? createFileError(MemBuf->getBufferIdentifier(), std::move(E)) + : createFileError(MemBuf->getBufferIdentifier(), LineNo, + std::move(E)); + } + template + Error parseError(size_t LineNo, char const *Fmt, const Ts &... Vals) const { + Error E = createStringError(errc::invalid_argument, Fmt, Vals...); + return parseError(LineNo, std::move(E)); + } + +public: + IHexReader(MemoryBuffer *MB) : MemBuf(MB) {} + + std::unique_ptr create() const override; +}; + class ELFReader : public Reader { Binary *Bin; Index: tools/llvm-objcopy/ELF/Object.cpp =================================================================== --- tools/llvm-objcopy/ELF/Object.cpp +++ tools/llvm-objcopy/ELF/Object.cpp @@ -205,6 +205,98 @@ return Line; } +static Error checkRecord(const IHexRecord &R) { + switch (R.Type) { + case IHexRecord::Data: + if (R.HexData.size() == 0) + return createStringError( + errc::invalid_argument, + "zero data length is not allowed for data records"); + break; + case IHexRecord::EndOfFile: + break; + case IHexRecord::SegmentAddr: + // 20-bit segment address. Data length must be 2 bytes + // (4 bytes in hex) + if (R.HexData.size() != 4) + return createStringError( + errc::invalid_argument, + "segment address data should be 2 bytes in size"); + break; + case IHexRecord::StartAddr80x86: + case IHexRecord::StartAddr: + if (R.HexData.size() != 8) + return createStringError(errc::invalid_argument, + "start address data should be 4 bytes in size"); + // According to Intel HEX specification '03' record + // only specifies the code address within the 20-bit + // segmented address space of the 8086/80186. This + // means 12 high order bits should be zeroes. + if (R.Type == IHexRecord::StartAddr80x86 && + R.HexData.take_front(3) != "000") + return createStringError(errc::invalid_argument, + "start address exceeds 20 bit for 80x86"); + break; + case IHexRecord::ExtendedAddr: + // 16-31 bits of linear base address + if (R.HexData.size() != 4) + return createStringError( + errc::invalid_argument, + "extended address data should be 2 bytes in size"); + break; + default: + // Unknown record type + return createStringError(errc::invalid_argument, "unknown record type: %u", + static_cast(R.Type)); + } + return Error::success(); +} + +// Checks that IHEX line contains valid characters. +// This allows converting hexadecimal data to integers +// without extra verification. +static Error checkChars(StringRef Line) { + assert(!Line.empty()); + if (Line[0] != ':') + return createStringError(errc::invalid_argument, + "missing ':' in the beginning of line."); + + for (size_t Pos = 1; Pos < Line.size(); ++Pos) + if (hexDigitValue(Line[Pos]) == -1U) + return createStringError(errc::invalid_argument, + "invalid character at position %zu.", Pos + 1); + return Error::success(); +} + +Expected IHexRecord::parse(StringRef Line) { + assert(!Line.empty()); + + // ':' + Length + Address + Type + Checksum with empty data ':LLAAAATTCC' + if (Line.size() < 11) + return createStringError(errc::invalid_argument, + "line is too short: %zu chars.", Line.size()); + + if (Error E = checkChars(Line)) + return std::move(E); + + IHexRecord Rec; + size_t DataLen = checkedGetHex(Line.substr(1, 2)); + if (Line.size() != getLength(DataLen)) + return createStringError(errc::invalid_argument, + "invalid line length %zu (should be %zu)", + Line.size(), getLength(DataLen)); + + Rec.Addr = checkedGetHex(Line.substr(3, 4)); + Rec.Type = checkedGetHex(Line.substr(7, 2)); + Rec.HexData = Line.substr(9, DataLen * 2); + + if (getChecksum(Line.drop_front(1)) != 0) + return createStringError(errc::invalid_argument, "incorrect checksum."); + if (Error E = checkRecord(Rec)) + return std::move(E); + return Rec; +} + static uint64_t sectionPhysicalAddr(const SectionBase *Sec) { Segment *Seg = Sec->ParentSegment; if (Seg && Seg->Type != ELF::PT_LOAD) @@ -829,32 +921,33 @@ } void DynamicRelocationSection::accept(MutableSectionVisitor &Visitor) { - Visitor.visit(*this); -} - -Error DynamicRelocationSection::removeSectionReferences( - bool AllowBrokenLinks, function_ref ToRemove) { - if (ToRemove(Symbols)) { - if (!AllowBrokenLinks) - return createStringError( - llvm::errc::invalid_argument, + Visitor.visit(*this); +} + +Error DynamicRelocationSection::removeSectionReferences( + bool AllowBrokenLinks, function_ref ToRemove) { + if (ToRemove(Symbols)) { + if (!AllowBrokenLinks) + return createStringError( + llvm::errc::invalid_argument, "symbol table '%s' cannot be removed because it is " "referenced by the relocation section '%s'", - Symbols->Name.data(), this->Name.data()); - Symbols = nullptr; - } - - // SecToApplyRel contains a section referenced by sh_info field. It keeps - // a section to which the relocation section applies. When we remove any - // sections we also remove their relocation sections. Since we do that much - // earlier, this assert should never be triggered. - assert(!SecToApplyRel || !ToRemove(SecToApplyRel)); - return Error::success(); -} - -Error Section::removeSectionReferences(bool AllowBrokenDependency, - function_ref ToRemove) { - if (ToRemove(LinkSection)) { + Symbols->Name.data(), this->Name.data()); + Symbols = nullptr; + } + + // SecToApplyRel contains a section referenced by sh_info field. It keeps + // a section to which the relocation section applies. When we remove any + // sections we also remove their relocation sections. Since we do that much + // earlier, this assert should never be triggered. + assert(!SecToApplyRel || !ToRemove(SecToApplyRel)); + return Error::success(); +} + +Error Section::removeSectionReferences( + bool AllowBrokenDependency, + function_ref ToRemove) { + if (ToRemove(LinkSection)) { if (!AllowBrokenDependency) return createStringError(llvm::errc::invalid_argument, "section '%s' cannot be removed because it is " @@ -1013,7 +1106,7 @@ return A->Index < B->Index; } -void BinaryELFBuilder::initFileHeader() { +void BasicELFBuilder::initFileHeader() { Obj->Flags = 0x0; Obj->Type = ET_REL; Obj->OSABI = ELFOSABI_NONE; @@ -1023,9 +1116,9 @@ Obj->Version = 1; } -void BinaryELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; } +void BasicELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; } -StringTableSection *BinaryELFBuilder::addStrTab() { +StringTableSection *BasicELFBuilder::addStrTab() { auto &StrTab = Obj->addSection(); StrTab.Name = ".strtab"; @@ -1033,7 +1126,7 @@ return &StrTab; } -SymbolTableSection *BinaryELFBuilder::addSymTab(StringTableSection *StrTab) { +SymbolTableSection *BasicELFBuilder::addSymTab(StringTableSection *StrTab) { auto &SymTab = Obj->addSection(); SymTab.Name = ".symtab"; @@ -1046,6 +1139,11 @@ return &SymTab; } +void BasicELFBuilder::initSections() { + for (auto &Section : Obj->sections()) + Section.initialize(Obj->sections()); +} + void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { auto Data = ArrayRef( reinterpret_cast(MemBuf->getBufferStart()), @@ -1069,11 +1167,6 @@ /*Value=*/DataSection.Size, STV_DEFAULT, SHN_ABS, 0); } -void BinaryELFBuilder::initSections() { - for (SectionBase &Section : Obj->sections()) - Section.initialize(Obj->sections()); -} - std::unique_ptr BinaryELFBuilder::build() { initFileHeader(); initHeaderSegment(); @@ -1085,6 +1178,62 @@ return std::move(Obj); } +// Adds sections from IHEX data file. Data should have been +// fully validated by this time. +void IHexELFBuilder::addDataSections() { + OwnedDataSection *Section = nullptr; + uint64_t SegmentAddr = 0, BaseAddr = 0; + uint32_t SecNo = 1; + + for (const IHexRecord &R : Records) { + uint64_t RecAddr; + switch (R.Type) { + case IHexRecord::Data: + // Ignore empty data records + if (R.HexData.empty()) + continue; + RecAddr = R.Addr + SegmentAddr + BaseAddr; + if (!Section || Section->Addr + Section->Size != RecAddr) + // OriginalOffset field is only used to sort section properly, so + // instead of keeping track of real offset in IHEX file, we use + // section number. + Section = &Obj->addSection( + ".sec" + std::to_string(SecNo++), RecAddr, + ELF::SHF_ALLOC | ELF::SHF_WRITE, SecNo); + Section->appendHexData(R.HexData); + break; + case IHexRecord::EndOfFile: + break; + case IHexRecord::SegmentAddr: + // 20-bit segment address. + SegmentAddr = checkedGetHex(R.HexData) << 4; + break; + case IHexRecord::StartAddr80x86: + case IHexRecord::StartAddr: + Obj->Entry = checkedGetHex(R.HexData); + assert(Obj->Entry <= 0xFFFFFU); + break; + case IHexRecord::ExtendedAddr: + // 16-31 bits of linear base address + BaseAddr = checkedGetHex(R.HexData) << 16; + break; + default: + llvm_unreachable("unknown record type"); + } + } +} + +std::unique_ptr IHexELFBuilder::build() { + initFileHeader(); + initHeaderSegment(); + StringTableSection *StrTab = addStrTab(); + addSymTab(StrTab); + initSections(); + addDataSections(); + + return std::move(Obj); +} + template void ELFBuilder::setParentSegment(Segment &Child) { for (Segment &Parent : Obj.segments()) { // Every segment will overlap with itself but we don't want a segment to @@ -1432,6 +1581,37 @@ return BinaryELFBuilder(MInfo.EMachine, MemBuf).build(); } +Expected> IHexReader::parse() const { + SmallVector Lines; + std::vector Records; + bool HasSections = false; + + MemBuf->getBuffer().split(Lines, '\n'); + Records.reserve(Lines.size()); + for (size_t LineNo = 1; LineNo <= Lines.size(); ++LineNo) { + StringRef Line = Lines[LineNo - 1].trim(); + if (Line.empty()) + continue; + + Expected R = IHexRecord::parse(Line); + if (!R) + return parseError(LineNo, R.takeError()); + if (R->Type == IHexRecord::EndOfFile) + break; + HasSections |= (R->Type == IHexRecord::Data); + Records.push_back(*R); + } + if (!HasSections) + return parseError(-1U, "no sections"); + + return std::move(Records); +} + +std::unique_ptr IHexReader::create() const { + std::vector Records = unwrapOrError(parse()); + return IHexELFBuilder(Records).build(); +} + std::unique_ptr ELFReader::create() const { auto Obj = llvm::make_unique(); if (auto *O = dyn_cast>(Bin)) { Index: tools/llvm-objcopy/llvm-objcopy.cpp =================================================================== --- tools/llvm-objcopy/llvm-objcopy.cpp +++ tools/llvm-objcopy/llvm-objcopy.cpp @@ -123,6 +123,14 @@ return Error::success(); } +/// The function executeObjcopyOnIHex does the dispatch based on the format +/// of the output specified by the command line options. +static Error executeObjcopyOnIHex(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + // TODO: support output formats other than ELF. + return elf::executeObjcopyOnIHex(Config, In, Out); +} + /// The function executeObjcopyOnRawBinary does the dispatch based on the format /// of the output specified by the command line options. static Error executeObjcopyOnRawBinary(const CopyConfig &Config, @@ -210,12 +218,18 @@ if (auto EC = sys::fs::status(Config.InputFilename, Stat)) return createFileError(Config.InputFilename, EC); - if (Config.InputFormat == "binary") { + typedef Error (*ProcessRawFn)(const CopyConfig &, MemoryBuffer &, Buffer &); + auto ProcessRaw = StringSwitch(Config.InputFormat) + .Case("binary", executeObjcopyOnRawBinary) + .Case("ihex", executeObjcopyOnIHex) + .Default(nullptr); + + if (ProcessRaw) { auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename); if (!BufOrErr) return createFileError(Config.InputFilename, BufOrErr.getError()); FileBuffer FB(Config.OutputFilename); - if (Error E = executeObjcopyOnRawBinary(Config, *BufOrErr->get(), FB)) + if (Error E = ProcessRaw(Config, *BufOrErr->get(), FB)) return E; } else { Expected> BinaryOrErr =