Index: llvm/docs/CommandGuide/llvm-objcopy.rst =================================================================== --- llvm/docs/CommandGuide/llvm-objcopy.rst +++ llvm/docs/CommandGuide/llvm-objcopy.rst @@ -78,6 +78,11 @@ 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 the value ````. + The value will be truncated to 8 bits or 0xff if larger than 64-bit integer. + .. option:: --help, -h Print a summary of command line options. @@ -99,6 +104,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:: --redefine-sym = Rename symbols called ```` to ```` in the output. Can be specified Index: llvm/test/tools/llvm-objcopy/ELF/gap-fill.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/gap-fill.test @@ -0,0 +1,220 @@ +## This file contains tests for the --gap-fill option. +## .space* are of Type::Fill residing at the start or the end of a segment, +## or between sections serving as 'gaps'. Check that these gaps are filled +## with the value specified by the --gap-fill option except the gaps at the +## start of the first allocable section (.space1) which is preserved. + +## Section/Segment layout by yaml +## +## 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 + +## Verify section headers before we perform several testings. +# RUN: llvm-readelf -S %t | FileCheck %s --check-prefix=ORG-SHDR +# ORG-SHDR: Section Headers: +# ORG-SHDR: [Nr] Name Type Address Off Size ES Flg Lk Inf Al +# ORG-SHDR: [ 0] NULL 0000000000000000 000000 000000 00 0 0 0 +# ORG-SHDR: [ 1] .nogap PROGBITS 0000000000000102 000122 000006 00 A 0 0 1 +# ORG-SHDR: [ 2] .gap1 PROGBITS 0000000000000108 000128 000007 00 AX 0 0 1 +# ORG-SHDR: [ 3] .gap2 PROGBITS 0000000000000110 000130 000004 00 A 0 0 1 +# ORG-SHDR: [ 4] .nobit_tbss NOBITS 0000000000000200 000138 000018 00 WAT 0 0 8 +# ORG-SHDR: [ 5] .foo PROGBITS 0000000000000204 00013c 000004 00 WA 0 0 1 +# ORG-SHDR: [ 6] .nobit_bss NOBITS 000000000000020a 000140 000008 00 WA 0 0 1 + +# RUN: not llvm-objcopy --gap-fill %t 2>&1 | FileCheck %s --check-prefix=EMPTY +# EMPTY: no input file specified + +# RUN: not llvm-objcopy --gap-fill= %t 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT +# BAD-FORMAT: bad number for --gap-fill: + +# RUN: not llvm-objcopy --gap-fill=x %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT +# BAD-INPUT: bad number for --gap-fill: x + +# RUN: not llvm-objcopy --gap-fill=0x1G %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT2 +# BAD-INPUT2: bad number for --gap-fill: 0x1G + +## Verify no error since truncating happens +# RUN: llvm-objcopy --gap-fill=0x1122 %t %t-val16 +# RUN: llvm-objcopy --gap-fill=0x112233445566778899aabbccddeeff00cccc %t %t-llvalue + +# 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 + +# 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 + +## The 'Offset' of segment1 appears to be wrong when using objcopy. Here we checkes the result of llvm-objcopy. +# PHDR: Program Headers: +# PHDR: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# PHDR: LOAD 0x000208 0x0000000000000108 0x0000000000000108 0x000008 0x000008 0x1 +# PHDR: LOAD 0x000200 0x0000000000000100 0x0000000000000100 0x000105 0x000105 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 + +## 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 preserved area (.space1) at the start of the first allocable seciton .nogap is not altered +## .space1 from the original object starts at 0x120 (i.e. the offset of .nogap 0x122 - 2) +## .space1 from the filled object starts at 0x200 (i.e. 0x202 - 2) +# 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. + +## Check all original data of .gap1 not altered (the first 7 bytes) +# 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 + +## Check the last 1 byte of .gap1 is filled with 0xe9. +# RUN: od -v -Ax -t x1 -j 0x7 -N 1 %t-filled.1 | FileCheck %s --check-prefix=GAP1 +# GAP1: 000007 e9 + +## Check all original data of .gap2 not altered (the first 4 bytes) +# 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 + +## Check the rest of .gap2 (0xf4 - 0x4, i.e. 240 bytes) are filled with 0xe9 +# RUN: od -v -An -t x1 -j 0x4 -N 240 %t-filled.2 | grep "e9" | wc -w | FileCheck %s --check-prefix=GAP2 +# GAP2: 240 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .space1 + Type: Fill + Pattern: 'ABCD' + Size: 0x2 + - Name: .nogap + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0102 + AddressAlign: 0x0001 + Size: 0x6 + Content: 'EEFF11223344' + - Name: .gap1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x0108 + AddressAlign: 0x0001 + Content: 'AABBCCDDFEDCBA' + - Name: .space2 + Type: Fill + Pattern: 'DC' + Size: 1 + - Name: .gap2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0110 + AddressAlign: 0x0001 + Content: 'A1B2C3D4' + - Name: .space3 + Type: Fill + Pattern: 'FE' + Size: 0x1 + - Name: .nobit_tbss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC, SHF_TLS ] + Address: 0x0200 + AddressAlign: 0x0008 + Size: 0x0018 + - Name: .space4 # in the different segment + Type: Fill + Pattern: '01234567' + Size: 0x4 + - 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 # Segment1 is nested in Seg2. + Flags: [ ] + VAddr: 0x0108 + PAddr: 0x0108 + Align: 0x0001 + FirstSec: .gap1 + LastSec: .gap1 + - Type: PT_LOAD # Segment2 has spaces at both start and end, also between sections. + Flags: [ ] + VAddr: 0x0100 + PAddr: 0x0100 + Align: 0x0100 + FirstSec: .space1 #.space1, .nogap, .gap1, .space2, .gap2, .space3 + LastSec: .space3 + - Type: PT_LOAD # Segment3 + Flags: [ ] + VAddr: 0x0200 + PAddr: 0x0200 + Align: 0x0100 + FileSize: 0x0008 + MemSize: 0x0012 + FirstSec: .space4 # This will be gap-filled. + LastSec: .nobit_bss # .foo is the last allocable section (with contents) that shall not be gap-filled. + - Type: PT_TLS # Segment4 + Flags: [ ] + VAddr: 0x0200 # Overlaps Segment3 + PAddr: 0x0200 + Align: 0x0008 + FileSize: 0x0000 + MemSize: 0x0018 + FirstSec: .nobit_tbss + LastSec: .nobit_tbss +... Index: llvm/test/tools/llvm-objcopy/ELF/pad-to.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-objcopy/ELF/pad-to.test @@ -0,0 +1,128 @@ + +# RUN: yaml2obj %s > %t-yaml + +## Use Sections/Segment layouts by llvm-objcopy instead of by yaml2obj. +# RUN: llvm-objcopy %t-yaml %t + +# RUN: not llvm-objcopy --pad-to= %t 2>&1 | FileCheck %s --check-prefix=BAD-FORMAT +# BAD-FORMAT: bad address for --pad-to: + +# RUN: not llvm-objcopy --pad-to=x %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT +# BAD-INPUT: bad address for --pad-to: x + +# RUN: not llvm-objcopy --pad-to=0x1G %t 2>&1 | FileCheck %s --check-prefix=BAD-INPUT2 +# BAD-INPUT2: bad address for --pad-to: 0x1G + +# RUN: not llvm-objcopy --pad-to=0x112233445566778899 %t 2>&1 | FileCheck %s --check-prefix=BAD-NUMBER +# BAD-NUMBER: bad address for --pad-to: 0x112233445566778899 + +## The last loadable section is .foo (0x200 - 0x204). +## Verify that nothing is done if any pad-to address is lower than 0x204. +## Just use 'cmp' to compare the objects since both are generated by llvm-objcopy. We don't worry about +## the difference in file offsets or section/segment layouts. + +# RUN: llvm-objcopy --pad-to=0x20 %t %t-p1 +# RUN: cmp %t %t-p1 +# RUN: llvm-objcopy --pad-to=0x200 %t %t-p2 +# RUN: cmp %t %t-p2 + +## Pad .foo to a valid address. +# RUN: llvm-objcopy --pad-to=0x214 %t %t-pad + +## Verify the following section contents are not altered. +# RUN: llvm-objcopy -j .bar -O binary %t %t1 +# RUN: llvm-objcopy -j .bar -O binary %t-pad %t2 +# RUN: cmp %t1 %t2 + +## Verify .foo is padded to the address 0x214 with a default value 0. +## First, check the original data of .foo is not altered. +# RUN: llvm-objcopy -j .foo -O binary %t %t.foo +# RUN: llvm-objcopy -j .foo -O binary %t-pad %t-pad.foo +# RUN: od -v -Ax -x -N 0x4 %t.foo > %t.foo1 +# RUN: od -v -Ax -x -N 0x4 %t-pad.foo > %t.foo2 +# RUN: cmp %t.foo1 %t.foo2 +## Second, check .foo is padded from 0x204-0x214 (16 bytes) with 0 +# RUN: od -v -An -t x1 -j 0x4 -N 16 %t-pad.foo | grep "00" | wc -w | FileCheck %s --check-prefix=DATA +# DATA: 16 + +## Pad .foo to a valid address with a value specified by --gap-fill option. +# RUN: llvm-objcopy --pad-to=0x214 --gap-fill=0xcc %t %t-gap-pad + +## Verify .foo is padded as expected +# RUN: llvm-objcopy -j .foo -O binary %t-gap-pad %t-gap-pad.foo +# RUN: od -v -An -t x1 -j 0x4 -N 16 %t-gap-pad.foo | grep "cc" | wc -w | FileCheck %s --check-prefix=VAL +# VAL: 16 + +## 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 + +# FHDR: Start of section headers: 256 (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] .bar PROGBITS 0000000000000100 0000b0 000006 00 A 0 0 1 +# SHDR: [ 2] .foo PROGBITS 0000000000000200 0000b6 000014 00 WA 0 0 1 + +# PHDR: Program Headers: +# PHDR: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align +# PHDR: LOAD 0x0000b0 0x0000000000000100 0x0000000000000100 0x000006 0x000006 0x1 +# PHDR: LOAD 0x0000b6 0x0000000000000200 0x0000000000000200 0x000014 0x000022 0x1 + +--- !ELF +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_X86_64 +Sections: + - Name: .bar + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x0100 + AddressAlign: 0x0001 + Size: 0x6 + Content: 'EEFF11223344' + - Name: .space + Type: Fill + Pattern: '01234567' + Size: 0x4 + - Name: .foo + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + Address: 0x0200 + AddressAlign: 0x0001 + Size: 0x4 + 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 + Flags: [ ] + VAddr: 0x0100 + PAddr: 0x0100 + Align: 0x0001 + FirstSec: .bar + LastSec: .bar + - Type: PT_LOAD # Seg2 + Flags: [ ] + VAddr: 0x0200 + PAddr: 0x0200 + Align: 0x0001 + FileSize: 0x0004 + MemSize: 0x0012 + FirstSec: .foo # The last allocable section (with contents) that can be paded to a new address. + LastSec: .nobit_bss +... Index: llvm/tools/llvm-objcopy/CopyConfig.h =================================================================== --- llvm/tools/llvm-objcopy/CopyConfig.h +++ llvm/tools/llvm-objcopy/CopyConfig.h @@ -167,6 +167,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 @@ -621,6 +621,24 @@ if (auto Arg = InputArgs.getLastArg(OBJCOPY_extract_partition)) Config.ExtractPartition = Arg->getValue(); + if (auto Arg = InputArgs.getLastArg(OBJCOPY_gap_fill)) { + ErrorOr Val = getAsInteger(Arg->getValue()); + if (!Val) + return createStringError(Val.getError(), "bad number for --gap-fill: %s", + Arg->getValue()); + // Truncation if needed. + if (Val.get().getBitWidth() > 64) + Config.GapFill = 0xff; + else + Config.GapFill = static_cast(Val.get().getZExtValue()); + } + 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 @@ -613,6 +613,155 @@ 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); + }); + + DenseMap 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. + DenseMap 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; + + auto SecParent = Sec->ParentSegment; + assert(SecParent != nullptr); + for (auto &Seg : Obj.segments()) { + auto SegmentEndAddr = Seg.VAddr + Seg.Contents.size(); + if (Sec->Addr >= Seg.VAddr && Sec->Addr < SegmentEndAddr && + (Sec->Addr + Sec->Size) > SegmentEndAddr) { + Seg.FileSize += GapsSize; + Seg.MemSize += GapsSize; + + if (!SecParent->ParentSegment && SecParent == &Seg) { + if (NewSegments.find(SecParent) == NewSegments.end()) { + NewSegments[SecParent] = new uint8_t[SecParent->FileSize]; + llvm::copy(SecParent->getContents(), NewSegments[SecParent]); + } + } + } + } + } + + if (NewSegments.empty()) + return Error::success(); + + // 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) { + if (auto ExpectedSec = Obj.sections().getSection(Index, Msg)) { + SectionBase *CurSec = ExpectedSec.get(); + 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" @@ -771,6 +920,11 @@ } } + 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/ObjcopyOpts.td =================================================================== --- llvm/tools/llvm-objcopy/ObjcopyOpts.td +++ llvm/tools/llvm-objcopy/ObjcopyOpts.td @@ -227,3 +227,14 @@ "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 . " + "The value will be truncated to 8 bits or 0xff if larger " + "than 64-bit integer.">, + 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">;