Index: test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml =================================================================== --- test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml +++ test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-pt-null.yaml @@ -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 Index: test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml =================================================================== --- test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml +++ test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections.yaml @@ -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 Index: test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml =================================================================== --- test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml +++ test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-sections2.yaml @@ -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" Index: test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml =================================================================== --- test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml +++ test/tools/llvm-objcopy/ELF/Inputs/ihex-elf-segments.yaml @@ -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: .data + 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 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,129 @@ +# 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 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 + +# 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 + +# 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 Index: test/tools/llvm-objcopy/ELF/ihex-writer.test =================================================================== --- test/tools/llvm-objcopy/ELF/ihex-writer.test +++ test/tools/llvm-objcopy/ELF/ihex-writer.test @@ -0,0 +1,92 @@ +# RUN: yaml2obj %p/Inputs/ihex-elf-sections.yaml -o %t +# RUN: llvm-objcopy -O ihex %t %t2.hex +# RUN: cat %t2.hex | 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 %t2-segs.hex +# RUN: cat %t2-segs.hex | 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 %t3-segs.hex +# RUN: cat %t3-segs.hex | 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 %t-sec2.hex +# RUN: cat %t-sec2.hex | 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 %t-sec2-4.hex +# RUN: cat %t-sec2-4.hex | 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 %t3.hex +# RUN: cat %t3.hex | 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 %t4.hex +# RUN: cat %t4.hex | FileCheck %s --check-prefix=START2 + +# We allow sign extended 32 bit start addresses as well. +# RUN: llvm-objcopy -O ihex --set-start=0xFFFFFFFF80001000 %t %t5.hex +# RUN: cat %t5.hex | 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: :0400000300000000F9 +# CHECK-NEXT: :00000001FF + +# SEGMENTS: :020000040010EA +# SEGMENTS-NEXT: :1002F800000102030405060708090A0B0C0D0E0F7E +# SEGMENTS-NEXT: :05030800101112131496 +# SEGMENTS-NEXT: :0B031000303132333435363738394095 +# SEGMENTS-NEXT: :0403200040414243D3 +# SEGMENTS-NEXT: :0B03280050515253545556575859601D +# 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: :0400000300000000F9 +# 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: :0400000300000000F9 +# ZERO_SIZE_SEC-NEXT: :00000001FF + +# START1: :040000030000FFFFFB +# START2: :0400000500100000E7 +# START3: :040000058000100067 +# BAD-START: error: Entry point address 0xf00000000 overflows 32 bits Index: tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- tools/llvm-objcopy/CopyConfig.cpp +++ tools/llvm-objcopy/CopyConfig.cpp @@ -459,7 +459,8 @@ return MI.takeError(); Config.BinaryArch = *MI; } - if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") { + if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary" && + Config.OutputFormat != "ihex") { Expected MI = getOutputFormatMachineInfo(Config.OutputFormat); if (!MI) 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 @@ -102,12 +102,9 @@ return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; } -static std::unique_ptr createWriter(const CopyConfig &Config, - Object &Obj, Buffer &Buf, - ElfType OutputElfType) { - if (Config.OutputFormat == "binary") { - return llvm::make_unique(Obj, Buf); - } +static std::unique_ptr 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: @@ -126,6 +123,17 @@ llvm_unreachable("Invalid output format"); } +static std::unique_ptr createWriter(const CopyConfig &Config, + Object &Obj, Buffer &Buf, + ElfType OutputElfType) { + using Functor = std::function()>; + return StringSwitch(Config.OutputFormat) + .Case("binary", [&] { return llvm::make_unique(Obj, Buf); }) + .Case("ihex", [&] { return llvm::make_unique(Obj, Buf); }) + .Default( + [&] { return createELFWriter(Config, Obj, Buf, OutputElfType); })(); +} + template static Expected> findBuildID(const object::ELFFile &In) { @@ -613,6 +621,26 @@ return Error::success(); } +static Error writeOutput(const CopyConfig &Config, Object &Obj, Buffer &Out, + ElfType OutputElfType) { + std::unique_ptr Writer = + createWriter(Config, Obj, Out, OutputElfType); + if (Error E = Writer->finalize()) + return E; + 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 ? Config.OutputArch.getValue() : 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); @@ -624,11 +652,7 @@ Config.OutputArch ? Config.OutputArch.getValue() : Config.BinaryArch); if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType)) return E; - std::unique_ptr 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, @@ -658,11 +682,7 @@ if (Error E = handleArgs(Config, *Obj, Reader, OutputElfType)) return E; - std::unique_ptr Writer = - createWriter(Config, *Obj, Out, OutputElfType); - if (Error E = Writer->finalize()) - return E; - if (Error E = Writer->write()) + if (Error E = writeOutput(Config, *Obj, Out, OutputElfType)) return E; if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) if (Error E = Index: tools/llvm-objcopy/ELF/Object.h =================================================================== --- tools/llvm-objcopy/ELF/Object.h +++ tools/llvm-objcopy/ELF/Object.h @@ -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 "llvm/Support/JamCRC.h" #include @@ -168,6 +169,8 @@ #define MAKE_SEC_WRITER_FRIEND \ friend class SectionWriter; \ + friend class IHexSectionWriterBase; \ + friend class IHexSectionWriter; \ template friend class ELFSectionWriter; \ template friend class ELFSectionSizer; @@ -186,6 +189,114 @@ explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {} }; +using IHexLineData = SmallVector; + +struct IHexRecord { + // Memory address of the record. + uint32_t Addr : 16; + // Record type (see below). + uint32_t Type : 16; + // 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 + CR). + static size_t getLineLength(size_t DataSize) { + return getLength(DataSize) + 1; + } + + // Given type, address and data returns line which can + // be written to output file. + static IHexLineData getLine(uint8_t Type, uint16_t Addr, + ArrayRef 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 Data); + virtual void writeData(uint8_t Type, uint16_t Addr, ArrayRef 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 Data) override; + void visit(const StringTableSection &Sec) override; +}; + class Writer { protected: Object &Obj; @@ -246,6 +357,25 @@ 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 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 @@ OriginalOffset = std::numeric_limits::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; }; @@ -713,21 +853,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(); }; @@ -765,6 +925,25 @@ std::unique_ptr create() const override; }; +class IHexReader : public Reader { + MemoryBuffer *MemBuf; + + Error checkChars(StringRef S, size_t LineNo) const; + Error checkRecord(const IHexRecord &R, size_t LineNo) const; + Expected> parse() const; + template + Error parseError(char const *Fmt, const Ts &... Vals) const { + return createFileError( + MemBuf->getBufferIdentifier(), + createStringError(errc::invalid_argument, Fmt, Vals...)); + } + +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 @@ -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" @@ -149,6 +149,154 @@ llvm::copy(Sec.Contents, Buf); } +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 static T checkedGetHex(StringRef S) { + T Value; + bool Fail = S.getAsInteger(16, Value); + assert(!Fail); + return Value; +} + +// Fills exactly Len bytes of buffer with hexadecimal characters +// representing value 'X' +template +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(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(S.take_front(2)); + S = S.drop_front(2); + } + return -Checksum; +} + +IHexLineData IHexRecord::getLine(uint8_t Type, uint16_t Addr, + ArrayRef 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++ = '\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 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(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((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(Base >> 24), + static_cast((Base >> 16) & 0xFF)}; + writeData(4, 0, Data); + return Base; +} + +void IHexSectionWriterBase::writeData(uint8_t Type, uint16_t Addr, + ArrayRef 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 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 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); } @@ -221,6 +369,15 @@ Visitor.visit(*this); } +void OwnedDataSection::appendHexData(StringRef HexData) { + assert((HexData.size() & 1) == 0); + while (!HexData.empty()) { + Data.push_back(checkedGetHex(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 + "' "); } @@ -814,7 +971,7 @@ return A->Index < B->Index; } -void BinaryELFBuilder::initFileHeader() { +void BasicELFBuilder::initFileHeader() { Obj->Flags = 0x0; Obj->Type = ET_REL; Obj->OSABI = ELFOSABI_NONE; @@ -824,9 +981,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"; @@ -834,7 +991,7 @@ return &StrTab; } -SymbolTableSection *BinaryELFBuilder::addSymTab(StringTableSection *StrTab) { +SymbolTableSection *BasicELFBuilder::addSymTab(StringTableSection *StrTab) { auto &SymTab = Obj->addSection(); SymTab.Name = ".symtab"; @@ -847,6 +1004,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()), @@ -870,12 +1032,6 @@ /*Value=*/DataSection.Size, STV_DEFAULT, SHN_ABS, 0); } -void BinaryELFBuilder::initSections() { - for (auto &Section : Obj->sections()) { - Section.initialize(Obj->sections()); - } -} - std::unique_ptr BinaryELFBuilder::build() { initFileHeader(); initHeaderSegment(); @@ -887,6 +1043,61 @@ 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 RecAddr, SegmentAddr = 0, BaseAddr = 0; + uint32_t SecNo = 1; + + for (const IHexRecord &R : Records) { + 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 (auto &Parent : Obj.segments()) { // Every segment will overlap with itself but we don't want a segment to @@ -1236,6 +1447,104 @@ return BinaryELFBuilder(MInfo.EMachine, MemBuf).build(); } +Error IHexReader::checkChars(StringRef Line, size_t LineNo) const { + if (Line[0] != ':') + return parseError("line %zu: missing ':' in the beginning of line.", + LineNo); + + for (size_t Pos = 1; Pos < Line.size(); ++Pos) + if (hexDigitValue(Line[Pos]) == -1U) + return parseError("line %zu: invalid character at position %zu.", LineNo, + Pos + 1); + return Error::success(); +} + +Error IHexReader::checkRecord(const IHexRecord &R, size_t LineNo) const { + switch (R.Type) { + case IHexRecord::Data: + if (R.HexData.size() == 0) + return parseError( + "line %zu: zero data length is not allowed for data records", LineNo); + 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 parseError( + "line %zu: segment address data should be 2 bytes in size", LineNo); + break; + case IHexRecord::StartAddr80x86: + case IHexRecord::StartAddr: + if (R.HexData.size() != 8) + return parseError( + "line %zu: start address data should be 4 bytes in size", LineNo); + // 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 parseError("line %zu: start address exceeds 20 bit for 80x86", + LineNo); + break; + case IHexRecord::ExtendedAddr: + // 16-31 bits of linear base address + if (R.HexData.size() != 4) + return parseError( + "line %zu: extended address data should be 2 bytes in size", LineNo); + break; + default: + // Unknown record type + return parseError("line %zu: unknown record type: %u", LineNo, + static_cast(R.Type)); + } + return Error::success(); +} + +Expected> IHexReader::parse() const { + SmallVector Lines; + std::vector Records; + + MemBuf->getBuffer().split(Lines, '\n'); + for (size_t LineNo = 1; LineNo <= Lines.size(); ++LineNo) { + StringRef Line = Lines[LineNo - 1].trim(); + if (Line.empty()) + continue; + + // ':' + Length + Address + Type + Checksum with empty data ':LLAAAATTCC' + if (Line.size() < 11) + return parseError("line %zu: line is too short: %zu chars.", LineNo, + Line.size()); + + if (Error E = checkChars(Line, LineNo)) + return std::move(E); + + IHexRecord Rec; + size_t DataLen = checkedGetHex(Line.substr(1, 2)); + if (Line.size() != IHexRecord::getLength(DataLen)) + return parseError("line %zu: invalid line length %zu (should be %zu)", + LineNo, Line.size(), IHexRecord::getLength(DataLen)); + + Rec.Addr = checkedGetHex(Line.substr(3, 4)); + Rec.Type = checkedGetHex(Line.substr(7, 2)); + Rec.HexData = Line.substr(9, DataLen * 2); + + if (IHexRecord::getChecksum(Line.drop_front(1)) != 0) + return parseError("line %zu: incorrect checksum.", LineNo); + if (Error E = checkRecord(Rec, LineNo)) + return std::move(E); + Records.push_back(Rec); + } + 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)) { @@ -1771,6 +2080,104 @@ 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] = {}; + if (Obj.Entry <= 0xFFFFFU) { + Data[0] = ((Obj.Entry & 0xF0000U) >> 12) & 0xFF; + support::endian::write(&Data[2], static_cast(Obj.Entry), + support::big); + HexData = IHexRecord::getLine(IHexRecord::StartAddr80x86, 0, Data); + } else { + support::endian::write(Data, static_cast(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 + + // EndOfFile record. + TotalSize = LengthCalc.getBufferOffset() + IHexRecord::getLineLength(4) + + IHexRecord::getLineLength(0); + if (Error E = Buf.allocate(TotalSize)) + return E; + return Error::success(); +} + template class ELFBuilder; template class ELFBuilder; template class ELFBuilder; Index: tools/llvm-objcopy/llvm-objcopy.h =================================================================== --- tools/llvm-objcopy/llvm-objcopy.h +++ tools/llvm-objcopy/llvm-objcopy.h @@ -28,7 +28,7 @@ // [see here](llvm/tools/llvm-readobj/llvm-readobj.h:38) template T unwrapOrError(Expected EO) { if (EO) - return *EO; + return std::move(*EO); std::string Buf; raw_string_ostream OS(Buf); logAllUnhandledErrors(EO.takeError(), OS); 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 =