Index: llvm/docs/CommandGuide/llvm-objcopy.rst =================================================================== --- llvm/docs/CommandGuide/llvm-objcopy.rst +++ llvm/docs/CommandGuide/llvm-objcopy.rst @@ -63,6 +63,10 @@ Enable deterministic mode when copying archives, i.e. use 0 for archive member header UIDs, GIDs and timestamp fields. On by default. +.. option:: --gap-fill + + Fill gaps between two adjacent loadable sections with 8 bit ````. + .. option:: --help, -h Print a summary of command line options. @@ -75,6 +79,11 @@ For MachO objects, ``
`` must be formatted as ``,
``. +.. option:: --pad-to
+ + Pad the last loadable section to ``
`` using a value of zero or + the value specified by :option:`--gap-fill`. + .. option:: --regex If specified, symbol and section names specified by other switches are treated Index: llvm/test/tools/llvm-objcopy/ELF/gap-fill.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/gap-fill.test @@ -0,0 +1,212 @@ +## This file contains regression tests for the --gap-fill option. +## Strip the .space* sections out, now the object has spaces (that do not belong to any section) +## at start, end or between sections of a segement serving as 'gaps'. Check that these gaps are +## filled with the value specified by the --gap-fill option. +## +## Seg1: 0x108 0x10F +## | .gap1 | +## +## Seg2: 0x100 0x102 0x108 0x10F 0x110 0x114 0x116 +## | .space1 | .nogap | .gap1 | .space2 | .gap2 | .space3 | +## +## Seg3: 0x200 0x204 0x20A 0x212 +## | .space4 | .foo | | .nobit_bss | +## +## Seg4: 0x200 0x218 +## | .nobit_tbss | + + +# RUN: yaml2obj %s > %t.init +# RUN: llvm-objcopy %t.init %t --regex -R .space.* + +# RUN: not llvm-objcopy --gap-fill %t 2>&1 | FileCheck %s --check-prefix=EMPTY +# RUN: not llvm-objcopy --gap-fill= %t 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT +# RUN: not llvm-objcopy --gap-fill=x %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT +# RUN: not llvm-objcopy --gap-fill=0x1G %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT2 + +## Verify truncating is not allowed. +# RUN: not llvm-objcopy --gap-fill=0x1122 %t 2>&1 | FileCheck %s --check-prefix=BAD-NUMBER + +# RUN: llvm-objcopy --gap-fill=0xe9 %t %t-filled + +## Verify the headers are expected. +# RUN: llvm-readelf -S %t-filled | FileCheck %s --check-prefix=SHDR +# RUN: llvm-readelf -l %t-filled | FileCheck %s --check-prefix=PHDR + +## Verify the following section contents are not altered. +# RUN: llvm-objcopy -j .nogap -j .foo -O binary %t %t1 +# RUN: llvm-objcopy -j .nogap -j .foo -O binary %t-filled %t2 +# RUN: cmp %t1 %t2 + +## Verify the preserve area at start of the first allocable seciton .nogap is not altered +# RUN: od -An -x -j 0x120 -N 0x2 %t > %t.space1 +# RUN: od -An -x -j 0x200 -N 0x2 %t-filled > %t-filled.space1 +# RUN: cmp %t.space1 %t-filled.space1 + +## Verify all section gaps are filled with 0xe9. The verification is done by two steps: +## First read the section original data and ensure they are not altered. +## Second, read the gaps and ensure they have the filled value. +# RUN: llvm-objcopy -j .gap1 -O binary %t %t.1 +# RUN: llvm-objcopy -j .gap1 -O binary %t-filled %t-filled.1 +# RUN: od -v -Ax -x -N 0x7 %t.1 > %t.gap1 +# RUN: od -v -Ax -x -N 0x7 %t-filled.1 > %t-filled.gap1 +# RUN: cmp %t.gap1 %t-filled.gap1 +# RUN: od -v -Ax -t x1 -j 0x7 -N 1 %t-filled.1 | FileCheck %s --check-prefix=GAP1 + +# RUN: llvm-objcopy -j .gap2 -O binary %t %t.2 +# RUN: llvm-objcopy -j .gap2 -O binary %t-filled %t-filled.2 +# RUN: od -v -Ax -x -N 0x4 %t.2 > %t.gap2 +# RUN: od -v -Ax -x -N 0x4 %t-filled.2 > %t-filled.gap2 +# RUN: cmp %t.gap2 %t-filled.gap2 + +## Read 0xf0 bytes (equals to 0xf4-0x4, i.e 240) +# RUN: od -v -An -t x1 -j 0x4 -N 240 %t-filled.2 | grep "e9" | wc -w | FileCheck %s --check-prefix=GAP2 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .space1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0100 + AddressAlign: 0x0001 + Content: 'ABCD' + - Name: .nogap + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0102 + AddressAlign: 0x0001 + Content: 'EEFF11223344' + - Name: .gap1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0108 + AddressAlign: 0x0001 + Content: 'AABBCCDDFEDCBA' + - Name: .space2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x010F + AddressAlign: 0x0001 + Content: 'DC' + - Name: .gap2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0110 + AddressAlign: 0x0001 + Content: 'A1B2C3D4' + - Name: .space3 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0114 + AddressAlign: 0x0001 + Content: 'FE' + - Name: .nobit_tbss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + Address: 0x0200 + AddressAlign: 0x0008 + Size: 0x0018 + - Name: .space4 # in different segment + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x0200 + AddressAlign: 0x0001 + Content: '01234567' + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x0204 + AddressAlign: 0x0001 + Content: '89ABCDEF' + - Name: .nobit_bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x020A + AddressAlign: 0x0001 + Size: 0x0008 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0001 + EntSize: 0x0001 + Content: 4743433A +ProgramHeaders: + - Type: PT_LOAD # Seg1 is nested in Seg2. + Flags: [ ] + VAddr: 0x0108 + PAddr: 0x0108 + Align: 0x0004 + Sections: + - Section: .gap1 + - Type: PT_LOAD # Seg2 has space at both start and end, also between sections. + Flags: [ ] + VAddr: 0x0100 + PAddr: 0x0100 + Align: 0x0100 + Sections: + - Section: .space1 + - Section: .nogap + - Section: .gap1 + - Section: .space2 + - Section: .gap2 + - Section: .space3 + - Type: PT_LOAD # Seg3 + Flags: [ ] + VAddr: 0x0200 + PAddr: 0x0200 + Align: 0x0100 + FileSize: 0x0008 + MemSize: 0x0012 + Sections: + - Section: .space4 # This will be gap-filled. + - Section: .foo # .foo is the last allocable section (with contents) that shall not be gap-filled. + - Section: .nobit_bss + - Type: PT_TLS # Seg4 + Flags: [ ] + VAddr: 0x0200 # Overlaps Seg3 + PAddr: 0x0200 + Align: 0x0008 + FileSize: 0x0000 + MemSize: 0x0018 + Sections: + - Section: .nobit_tbss +... + +# EMPTY: no input file specified +# BAD-FORMAT: bad number for --gap-fill: +# BAD-INPUT: bad number for --gap-fill: x +# BAD-INPUT2: bad number for --gap-fill: 0x1G +# BAD-NUMBER: bad number for --gap-fill: 0x1122 + +# SHDR: Section Headers: +# SHDR: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# SHDR: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# SHDR: [ 1] .nogap PROGBITS 0000000000000102 000202 000006 00 A 0 0 1 +# SHDR: [ 2] .gap1 PROGBITS 0000000000000108 000208 000008 00 AX 0 0 1 +# SHDR: [ 3] .gap2 PROGBITS 0000000000000110 000210 0000f4 00 A 0 0 1 +# SHDR: [ 4] .nobit_tbss NOBITS 0000000000000200 000400 000018 00 WAT 0 0 8 +# SHDR: [ 5] .foo PROGBITS 0000000000000204 000404 000004 00 WA 0 0 1 +# SHDR: [ 6] .nobit_bss NOBITS 000000000000020a 000408 000008 00 WA 0 0 1 +# SHDR: [ 7] .comment PROGBITS 0000000000000000 000408 000004 01 MS 0 0 1 + +# PHDR: Program Headers: +# PHDR: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# PHDR: LOAD 0x000208 0x0000000000000108 0x0000000000000108 0x000008 0x000008 0x4 +# PHDR: LOAD 0x000200 0x0000000000000100 0x0000000000000100 0x000104 0x000104 0x100 +# PHDR: LOAD 0x000400 0x0000000000000200 0x0000000000000200 0x000008 0x000012 0x100 +# PHDR: TLS 0x000400 0x0000000000000200 0x0000000000000200 0x000000 0x000018 0x8 +# PHDR: Section to Segment mapping: +# PHDR: Segment Sections... +# PHDR: 00 .gap1 +# PHDR: 01 .nogap .gap1 .gap2 +# PHDR: 02 .foo .nobit_bss +# PHDR: 03 .nobit_tbss +# PHDR: None .comment + +# GAP1: 000007 e9 +# GAP2: 240 Index: llvm/test/tools/llvm-objcopy/ELF/pad-to.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/pad-to.test @@ -0,0 +1,98 @@ +# RUN: llvm-objcopy %p/Inputs/gapfillpadto.elf %t +# RUN: not llvm-objcopy --pad-to %t 2>&1 | FileCheck %s --check-prefix=EMPTY +# RUN: not llvm-objcopy --pad-to= %t 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT +# RUN: not llvm-objcopy --pad-to=x %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT +# RUN: not llvm-objcopy --pad-to=0x1G %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT2 +# RUN: not llvm-objcopy --pad-to=0x112233445566778899 %t 2>&1 | FileCheck %s --check-prefix=BAD-NUMBER + +## The last loadable section is .data (0x601018 - 0x601028). +## Verify that nothing is done if provided any pad-to address lower than 0x601028. +# RUN: llvm-objcopy --pad-to=0x20 %t %t-p1 +# RUN: cmp %t %t-p1 +# RUN: llvm-objcopy --pad-to=0x600000 %t %t-p2 +# RUN: cmp %t %t-p2 +# RUN: llvm-objcopy --pad-to=0x601011 %t %t-p3 +# RUN: cmp %t %t-p3 + +## Pad .data to a valid address. +# RUN: llvm-objcopy --pad-to=0x601101 %t %t-pad + +## Verify the following section contents are not altered. +# RUN: llvm-objcopy -j .interp -j .note.ABI-tag -j .note.gnu.build-id -j .gnu_version -j .gnu_version_r \ +# RUN: -j .init -j .fini -j .rodata -j .eh_frame_hdr -j .eh_frame -j .init_array -j .fini_array \ +# RUN: -j .got -j .got.plt -O binary %t %t1 +# RUN: llvm-objcopy -j .interp -j .note.ABI-tag -j .note.gnu.build-id -j .gnu_version -j .gnu_version_r \ +# RUN: -j .init -j .fini -j .rodata -j .eh_frame_hdr -j .eh_frame -j .init_array -j .fini_array \ +# RUN: -j .got -j .got.plt -O binary %t-pad %t2 +# RUN: cmp %t1 %t2 +# RUN: llvm-objcopy -j .gnu_hash -j .dynstr -j .dynsym -j .rela.dyn -j .dynamic --allow-broken-links -O binary %t %t3 +# RUN: llvm-objcopy -j .gnu_hash -j .dynstr -j .dynsym -j .rela.dyn -j .dynamic --allow-broken-links -O binary %t-pad %t4 +# RUN: cmp %t3 %t4 + +## Verify .data is padded to the address 0x601101 with a default value 0. +# RUN: llvm-objcopy -j .data -O binary %t %t.data +# RUN: llvm-objcopy -j .data -O binary %t-pad %t-pad.data +# RUN: od -v -Ax -x -N 0x10 %t.data > %t.data1 +# RUN: od -v -Ax -x -N 0x10 %t-pad.data > %t.data2 +## The original data is not altered. +# RUN: cmp %t.data1 %t.data2 +## .data is padded from 0x601028-0x601101. Read 0xd9 bytes (equals to 0x601101 - 0x601028, i.e 217) +# RUN: od -v -An -t x1 -j 0x10 -N 217 %t-pad.data | grep "00" | wc -w | FileCheck %s --check-prefix=DATA + +## Verify the headers are expected. +# RUN: llvm-readelf -h %t-pad | FileCheck %s --check-prefix=FHDR +# RUN: llvm-readelf -S %t-pad | FileCheck %s --check-prefix=SHDR +# RUN: llvm-readelf -l %t-pad | FileCheck %s --check-prefix=PHDR + +# EMPTY: no input file specified +# BAD-FORMAT: bad address for --pad-to: +# BAD-INPUT: bad address for --pad-to: x +# BAD-INPUT2: bad address for --pad-to: 0x1G +# BAD-NUMBER: bad address for --pad-to: 0x112233445566778899 + +# FHDR: Start of section headers: 6608 (bytes into file) + +# SHDR: Section Headers: +# SHDR: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# SHDR: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# SHDR: [ 1] .interp PROGBITS 0000000000400238 000238 00001c 00 A 0 0 1 +# SHDR: [ 2] .note.ABI-tag NOTE 0000000000400254 000254 000020 00 A 0 0 4 +# SHDR: [ 3] .note.gnu.build-id NOTE 0000000000400274 000274 000024 00 A 0 0 4 +# SHDR: [ 4] .gnu.hash GNU_HASH 0000000000400298 000298 00001c 00 A 5 0 8 +# SHDR: [ 5] .dynsym DYNSYM 00000000004002b8 0002b8 000048 18 A 6 1 8 +# SHDR: [ 6] .dynstr STRTAB 0000000000400300 000300 00005f 00 A 0 0 1 +# SHDR: [ 7] .gnu.version VERSYM 0000000000400360 000360 000006 02 A 5 0 2 +# SHDR: [ 8] .gnu.version_r VERNEED 0000000000400368 000368 000020 00 A 6 1 8 +# SHDR: [ 9] .rela.dyn RELA 0000000000400388 000388 000030 18 A 5 0 8 +# SHDR: [10] .init PROGBITS 00000000004003b8 0003b8 000017 00 AX 0 0 4 +# SHDR: [11] .text PROGBITS 00000000004003d0 0003d0 0001d2 00 AX 0 0 16 +# SHDR: [12] .fini PROGBITS 00000000004005a4 0005a4 000009 00 AX 0 0 4 +# SHDR: [13] .rodata PROGBITS 00000000004005b0 0005b0 000004 04 AM 0 0 4 +# SHDR: [14] .eh_frame_hdr PROGBITS 00000000004005b4 0005b4 00003c 00 A 0 0 4 +# SHDR: [15] .eh_frame PROGBITS 00000000004005f0 0005f0 0000f8 00 A 0 0 8 +# SHDR: [16] .init_array INIT_ARRAY 0000000000600e20 000e20 000008 08 WA 0 0 8 +# SHDR: [17] .fini_array FINI_ARRAY 0000000000600e28 000e28 000008 08 WA 0 0 8 +# SHDR: [18] .dynamic DYNAMIC 0000000000600e30 000e30 0001c0 10 WA 6 0 8 +# SHDR: [19] .got PROGBITS 0000000000600ff0 000ff0 000010 08 WA 0 0 8 +# SHDR: [20] .got.plt PROGBITS 0000000000601000 001000 000018 08 WA 0 0 8 +# SHDR: [21] .data PROGBITS 0000000000601018 001018 0000e9 00 WA 0 0 8 +# SHDR: [22] .bss NOBITS 0000000000601028 001101 000008 00 WA 0 0 1 +# SHDR: [23] .comment PROGBITS 0000000000000000 001101 000078 01 MS 0 0 1 +# SHDR: [24] .symtab SYMTAB 0000000000000000 001180 000588 18 25 41 8 +# SHDR: [25] .strtab STRTAB 0000000000000000 001708 0001cc 00 0 0 1 +# SHDR: [26] .shstrtab STRTAB 0000000000000000 0018d4 0000f9 00 0 0 1 + +# PHDR: Program Headers: +# PHDR: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# PHDR: PHDR 0x000040 0x0000000000400040 0x0000000000400040 0x0001f8 0x0001f8 R 0x8 +# PHDR: INTERP 0x000238 0x0000000000400238 0x0000000000400238 0x00001c 0x00001c R 0x1 +# PHDR: [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] +# PHDR: LOAD 0x000000 0x0000000000400000 0x0000000000400000 0x0006e8 0x0006e8 R E 0x200000 +# PHDR: LOAD 0x000e20 0x0000000000600e20 0x0000000000600e20 0x0002e1 0x0002e9 RW 0x200000 +# PHDR: DYNAMIC 0x000e30 0x0000000000600e30 0x0000000000600e30 0x0001c0 0x0001c0 RW 0x8 +# PHDR: NOTE 0x000254 0x0000000000400254 0x0000000000400254 0x000044 0x000044 R 0x4 +# PHDR: GNU_EH_FRAME 0x0005b4 0x00000000004005b4 0x00000000004005b4 0x00003c 0x00003c R 0x4 +# PHDR: GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10 +# PHDR: GNU_RELRO 0x000e20 0x0000000000600e20 0x0000000000600e20 0x0001e0 0x0001e0 R 0x1 + +# DATA: 217 Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -165,6 +165,8 @@ Optional BuildIdLinkInput; Optional BuildIdLinkOutput; Optional ExtractPartition; + Optional GapFill; + Optional PadTo; StringRef SplitDWO; StringRef SymbolsPrefix; StringRef AllocSectionsPrefix; Index: llvm/tools/llvm-objcopy/CopyConfig.cpp =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.cpp +++ llvm/tools/llvm-objcopy/CopyConfig.cpp @@ -518,7 +518,21 @@ InputArgs.getLastArgValue(OBJCOPY_prefix_alloc_sections); if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition)) Config.ExtractPartition = Arg->getValue(); - + if (auto Arg = InputArgs.getLastArg(OBJCOPY_gap_fill)) { + // Truncation is not allowed. + ErrorOr Val = getAsInteger(Arg->getValue()); + if (!Val) + return createStringError(Val.getError(), "bad number for --gap-fill: %s", + Arg->getValue()); + Config.GapFill = *Val; + } + if (auto Arg = InputArgs.getLastArg(OBJCOPY_pad_to)) { + ErrorOr Addr = getAsInteger(Arg->getValue()); + if (!Addr) + return createStringError(Addr.getError(), "bad address for --pad-to: %s", + Arg->getValue()); + Config.PadTo = *Addr; + } for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) { if (!StringRef(Arg->getValue()).contains('=')) return createStringError(errc::invalid_argument, Index: llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp =================================================================== --- llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp +++ llvm/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -589,6 +589,150 @@ return Obj.removeSections(Config.AllowBrokenLinks, RemovePred); } +static Error padSections(const CopyConfig &Config, Object &Obj) { + SmallVector LoadableSections; + for (SectionBase &Sec : Obj.sections()) { + if ((Sec.Flags & SectionFlag::SecLoad) && (Sec.Type != SHT_NOBITS)) + LoadableSections.push_back(&Sec); + } + + if (LoadableSections.empty()) + return Error::success(); + + llvm::stable_sort(LoadableSections, + [](const SectionBase *LHS, const SectionBase *RHS) { + if (LHS->Addr < RHS->Addr) + return true; + if (LHS->Addr > RHS->Addr) + return false; + return (LHS->Size < RHS->Size); + }); + + std::map SectionGapMap; + // Default filled value for '--pad-to' option. + uint8_t Value = 0; + + // Fill the gaps if the section's not in any segement. Otherwise just record + // the gaps. + auto setSectionGaps = [&SectionGapMap](SectionBase *Sec, uint32_t Value, + uint64_t GapsSize) { + if (Sec->ParentSegment) + SectionGapMap[Sec] = GapsSize; + else { + Sec->Size += GapsSize; + uint8_t *N = new uint8_t[Sec->Size]; + llvm::copy(Sec->OriginalData, &N[0]); + memset(N + Sec->OriginalData.size(), Value, GapsSize); + Sec->OriginalData = makeArrayRef(N, Sec->Size); + } + }; + + if (Config.GapFill) { + Value = Config.GapFill.getValue(); + for (SmallVectorImpl::const_iterator + I = LoadableSections.begin(), + E = LoadableSections.end() - 1; + I != E; ++I) { + if ((*I)->Size == 0) + continue; + uint64_t GapBegin = (*I)->Addr + (*I)->Size; + uint64_t GapEnd = (*(I + 1))->Addr; + if (GapEnd <= GapBegin) + continue; + setSectionGaps(*I, Value, GapEnd - GapBegin); + } + } + + if (Config.PadTo) { + SectionBase *LastSec = *(LoadableSections.end() - 1); + uint64_t SecEndAddr = LastSec->Addr + LastSec->Size; + if (LastSec->Size && SecEndAddr < Config.PadTo.getValue()) + setSectionGaps(LastSec, Value, Config.PadTo.getValue() - SecEndAddr); + } + + // Populate section and segment content. + // First, resize the section and its containing segment if needed. + std::map NewSegments; + for (auto &It : SectionGapMap) { + SectionBase *Sec = It.first; + uint64_t GapsSize = It.second; + // Two adjacent sections might overlap in the same segment. Adjust its + // layout in next step. + Sec->Size += GapsSize; + + Segment* SecParent = Sec->ParentSegment; + assert(SecParent != nullptr); + if (!SecParent->ParentSegment) + NewSegments[SecParent] = nullptr; + for (Segment &Seg : Obj.segments()) { + uint64_t SegmentEndAddr = Seg.VAddr + Seg.FileSize; + uint64_t NewEnd = Sec->Addr + Sec->Size; + if (Seg.hasSection(Sec) && NewEnd > SegmentEndAddr) { + Seg.FileSize += (NewEnd - SegmentEndAddr); + Seg.MemSize += (NewEnd - SegmentEndAddr); + } + } + } + + for (auto &It : NewSegments) { + It.second = new uint8_t[It.first->FileSize]; + llvm::copy(It.first->getContents(), NewSegments[It.first]); + } + + // Second, adjust segments and sections layouts after the resizing. + auto PreSec = *(LoadableSections.end() - 1); + auto Index = PreSec->Index; + auto PreSecEnd = PreSec->OriginalOffset + PreSec->Size; + Index++; + Twine Msg; + for (auto Size = Obj.sections().size(); Index < Size; ++Index) { + auto CurSec = Obj.sections().getSection(Index, Msg); + if (CurSec->OriginalOffset < PreSecEnd) { + CurSec->OriginalOffset = PreSecEnd; + PreSecEnd = CurSec->OriginalOffset + CurSec->Size; + } + } + + // Third, set the new segment's content. + for (auto &It : SectionGapMap) { + SectionBase *Sec = It.first; + uint64_t GapsSize = It.second; + auto SecParent = It.first->ParentSegment; + const auto Iter = NewSegments.find(SecParent); + if (Iter != NewSegments.end()) { + uint64_t Offset = Sec->OriginalOffset - SecParent->OriginalOffset; + std::memset(Iter->second + Offset + Sec->OriginalData.size(), Value, + GapsSize); + } + } + + for (auto &It : NewSegments) + It.first->Contents = makeArrayRef(It.second, It.first->FileSize); + + // Fourth, set the new segment's child content. + for (auto &Seg : Obj.segments()) { + auto SegParent = Seg.ParentSegment; + const auto Iter = NewSegments.find(SegParent); + if (Iter != NewSegments.end()) { + uint64_t Offset = Seg.OriginalOffset - SegParent->OriginalOffset; + Seg.Contents = makeArrayRef(Iter->second + Offset, Seg.FileSize); + } + } + + // Last step, set the section content. + for (auto &Sec : Obj.sections()) { + auto SecParent = Sec.ParentSegment; + const auto Iter = NewSegments.find(SecParent); + if (Iter != NewSegments.end() && Sec.Type != SHT_NOBITS) { + uint64_t Offset = + Sec.OriginalOffset - SecParent->OriginalOffset + SecParent->Offset; + Sec.OriginalData = makeArrayRef(Iter->second + Offset, Sec.Size); + } + } + + return Error::success(); +} + // This function handles the high level operations of GNU objcopy including // handling command line options. It's important to outline certain properties // we expect to hold of the command line operations. Any operation that "keeps" @@ -732,6 +876,11 @@ Sec ? (uint16_t)SYMBOL_SIMPLE_INDEX : (uint16_t)SHN_ABS, 0); } + if ((Config.GapFill || Config.PadTo) && Obj.sections().size()) { + if (Error E = padSections(Config, Obj)) + return E; + } + if (Config.EntryExpr) Obj.Entry = Config.EntryExpr(Obj.Entry); return Error::success(); Index: llvm/tools/llvm-objcopy/ELF/Object.h =================================================================== --- llvm/tools/llvm-objcopy/ELF/Object.h +++ llvm/tools/llvm-objcopy/ELF/Object.h @@ -458,6 +458,10 @@ return nullptr; } + bool hasSection(const SectionBase *Sec) { + return Sections.find(Sec) != Sections.end(); + } + void removeSection(const SectionBase *Sec) { Sections.erase(Sec); } void addSection(const SectionBase *Sec) { Sections.insert(Sec); } Index: llvm/tools/llvm-objcopy/ObjcopyOpts.td =================================================================== --- llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -227,3 +227,13 @@ "compatibility: debug, constructor, warning, indirect, synthetic, " "unique-object, before.">, MetaVarName<"name=[section:]value[,flags]">; + +defm gap_fill + : Eq<"gap-fill", + "Fill gaps between two loadable sections with .">, + MetaVarName<"val">; + +defm pad_to + : Eq<"pad-to", + "Pad the last loadable section up to with the value specified by --gap-fill if present or zero by default.">, + MetaVarName<"addr">;