Index: test/tools/llvm-objcopy/ELF/fixup-segment.test =================================================================== --- test/tools/llvm-objcopy/ELF/fixup-segment.test +++ test/tools/llvm-objcopy/ELF/fixup-segment.test @@ -0,0 +1,140 @@ +# RUN: yaml2obj %s -o %t +# Remove section from the middle of the segment. Segment table should not change +# RUN: llvm-objcopy --remove-section=.data.2 %t %t2 +# RUN: llvm-readobj --sections --program-headers %t2 | FileCheck %s --check-prefix=REM-DATA2 + +# Remove NOBITS section which is last in the segment. Second segment's file and memory size +# should change after that. +# RUN: llvm-objcopy --remove-section=.bss %t2 %t3 +# RUN: llvm-readobj --sections --program-headers %t3 | FileCheck %s --check-prefix=REM-BSS + +# Remove the last section from segment. Segment will be deleted after that. +# RUN: llvm-objcopy --remove-section=.data.1 %t3 %t4 +# RUN: llvm-readobj --sections --program-headers %t4 | FileCheck %s --check-prefix=REM-DATA1 + +!ELF +FileHeader: + Class: ELFCLASS32 + Data: ELFDATA2LSB + Type: ET_EXEC + Machine: EM_ARM +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x1000 + AddressAlign: 0x8 + Size: 64 + - Name: .data.1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x10000 + AddressAlign: 0x8 + Size: 0x1000 + - Name: .data.2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + Address: 0x11000 + AddressAlign: 0x8 + Size: 0x1000 + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_ALLOC ] + Address: 0x12000 + AddressAlign: 0x8 + Size: 0x1000 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_X, PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Align: 0x1000 + Sections: + - Section: .text + - Type: PT_LOAD + Flags: [ PF_R, PF_W ] + VAddr: 0x10000 + PAddr: 0x10000 + Align: 0x1000 + Sections: + - Section: .data.1 + - Section: .data.2 + - Section: .bss + +#REM-DATA2-NOT: .data.2 +#REM-DATA2: ProgramHeaders [ +#REM-DATA2-NEXT: ProgramHeader { +#REM-DATA2-NEXT: Type: PT_LOAD (0x1) +#REM-DATA2-NEXT: Offset: 0x1000 +#REM-DATA2-NEXT: VirtualAddress: 0x1000 +#REM-DATA2-NEXT: PhysicalAddress: 0x1000 +#REM-DATA2-NEXT: FileSize: 64 +#REM-DATA2-NEXT: MemSize: 64 +#REM-DATA2-NEXT: Flags [ (0x5) +#REM-DATA2-NEXT: PF_R (0x4) +#REM-DATA2-NEXT: PF_X (0x1) +#REM-DATA2-NEXT: ] +#REM-DATA2-NEXT: Alignment: 4096 +#REM-DATA2-NEXT: } +#REM-DATA2-NEXT: ProgramHeader { +#REM-DATA2-NEXT: Type: PT_LOAD (0x1) +#REM-DATA2-NEXT: Offset: 0x2000 +#REM-DATA2-NEXT: VirtualAddress: 0x10000 +#REM-DATA2-NEXT: PhysicalAddress: 0x10000 +#REM-DATA2-NEXT: FileSize: 8192 +#REM-DATA2-NEXT: MemSize: 12288 +#REM-DATA2-NEXT: Flags [ (0x6) +#REM-DATA2-NEXT: PF_R (0x4) +#REM-DATA2-NEXT: PF_W (0x2) +#REM-DATA2-NEXT: ] +#REM-DATA2-NEXT: Alignment: 4096 +#REM-DATA2-NEXT: } +#REM-DATA2-NEXT: ] + +#REM-BSS-NOT: .bss +#REM-BSS: ProgramHeaders [ +#REM-BSS-NEXT: ProgramHeader { +#REM-BSS-NEXT: Type: PT_LOAD (0x1) +#REM-BSS-NEXT: Offset: 0x1000 +#REM-BSS-NEXT: VirtualAddress: 0x1000 +#REM-BSS-NEXT: PhysicalAddress: 0x1000 +#REM-BSS-NEXT: FileSize: 64 +#REM-BSS-NEXT: MemSize: 64 +#REM-BSS-NEXT: Flags [ (0x5) +#REM-BSS-NEXT: PF_R (0x4) +#REM-BSS-NEXT: PF_X (0x1) +#REM-BSS-NEXT: ] +#REM-BSS-NEXT: Alignment: 4096 +#REM-BSS-NEXT: } +#REM-BSS-NEXT: ProgramHeader { +#REM-BSS-NEXT: Type: PT_LOAD (0x1) +#REM-BSS-NEXT: Offset: 0x2000 +#REM-BSS-NEXT: VirtualAddress: 0x10000 +#REM-BSS-NEXT: PhysicalAddress: 0x10000 +#REM-BSS-NEXT: FileSize: 4096 +#REM-BSS-NEXT: MemSize: 4096 +#REM-BSS-NEXT: Flags [ (0x6) +#REM-BSS-NEXT: PF_R (0x4) +#REM-BSS-NEXT: PF_W (0x2) +#REM-BSS-NEXT: ] +#REM-BSS-NEXT: Alignment: 4096 +#REM-BSS-NEXT: } +#REM-BSS-NEXT: ] + +#REM-DATA1-NOT: .data +#REM-DATA1: ProgramHeaders [ +#REM-DATA1-NEXT: ProgramHeader { +#REM-DATA1-NEXT: Type: PT_LOAD (0x1) +#REM-DATA1-NEXT: Offset: 0x1000 +#REM-DATA1-NEXT: VirtualAddress: 0x1000 +#REM-DATA1-NEXT: PhysicalAddress: 0x1000 +#REM-DATA1-NEXT: FileSize: 64 +#REM-DATA1-NEXT: MemSize: 64 +#REM-DATA1-NEXT: Flags [ (0x5) +#REM-DATA1-NEXT: PF_R (0x4) +#REM-DATA1-NEXT: PF_X (0x1) +#REM-DATA1-NEXT: ] +#REM-DATA1-NEXT: Alignment: 4096 +#REM-DATA1-NEXT: } +#REM-DATA1-NEXT: ] + Index: tools/llvm-objcopy/ELF/Object.h =================================================================== --- tools/llvm-objcopy/ELF/Object.h +++ tools/llvm-objcopy/ELF/Object.h @@ -216,6 +216,7 @@ void writeShdrs(); void writeSectionData(); + void setParentSegment(Segment &Child); void assignOffsets(); std::unique_ptr> SecWriter; @@ -314,9 +315,10 @@ Segment() {} const SectionBase *firstSection() const { - if (!Sections.empty()) - return *Sections.begin(); - return nullptr; + return !Sections.empty() ? *Sections.begin() : nullptr; + } + const SectionBase *lastSection() const { + return !Sections.empty() ? *--Sections.end() : nullptr; } void removeSection(const SectionBase *Sec) { Sections.erase(Sec); } @@ -724,7 +726,6 @@ const ELFFile &ElfFile; Object &Obj; - void setParentSegment(Segment &Child); void readProgramHeaders(); void initGroupSection(GroupSection *GroupSec); void initSymbolTable(SymbolTableSection *SymTab); @@ -816,6 +817,8 @@ Segments.emplace_back(llvm::make_unique(Data)); return *Segments.back(); } + bool isParentForPhdrs(const Segment *Seg); + void fixupSegments(); }; } // end namespace elf Index: tools/llvm-objcopy/ELF/Object.cpp =================================================================== --- tools/llvm-objcopy/ELF/Object.cpp +++ tools/llvm-objcopy/ELF/Object.cpp @@ -761,8 +761,9 @@ // segments and ensures that the section "belongs" to the second segment and // not the first. uint64_t SecSize = Section.Size ? Section.Size : 1; - return Segment.Offset <= Section.OriginalOffset && - Segment.Offset + Segment.FileSize >= Section.OriginalOffset + SecSize; + return (Section.Flags & ELF::SHF_ALLOC) && + Segment.Offset <= Section.OriginalOffset && + Segment.Offset + Segment.MemSize >= Section.OriginalOffset + SecSize; } // Returns true IFF a segment's original offset is inside of another segment's @@ -865,22 +866,6 @@ 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 - // be it's own parent so we avoid that situation. - if (&Child != &Parent && segmentOverlapsSegment(Child, Parent)) { - // We want a canonical "most parental" segment but this requires - // inspecting the ParentSegment. - if (compareSegmentsByOffset(&Parent, &Child)) - if (Child.ParentSegment == nullptr || - compareSegmentsByOffset(&Parent, Child.ParentSegment)) { - Child.ParentSegment = &Parent; - } - } - } -} - template void ELFBuilder::readProgramHeaders() { uint32_t Index = 0; for (const auto &Phdr : unwrapOrError(ElfFile.program_headers())) { @@ -925,13 +910,6 @@ // The spec requires us to naturally align all the fields. PrHdr.Align = sizeof(Elf_Addr); PrHdr.Index = Index++; - - // Now we do an O(n^2) loop through the segments in order to match up - // segments. - for (auto &Child : Obj.segments()) - setParentSegment(Child); - setParentSegment(ElfHdr); - setParentSegment(PrHdr); } template @@ -1381,6 +1359,32 @@ CompareSections); } +bool Object::isParentForPhdrs(const Segment *Seg) { + return ElfHdrSegment.ParentSegment == Seg || + ProgramHdrSegment.ParentSegment == Seg; +} + +void Object::fixupSegments() { + for (auto &Seg : Segments) + if (const SectionBase *LastSec = Seg->lastSection()) { + // We should be called before layouting. + assert(LastSec->Offset == LastSec->OriginalOffset); + Seg->MemSize = LastSec->Offset + LastSec->Size - Seg->Offset; + Seg->FileSize = LastSec->Type == SHT_NOBITS ? Seg->MemSize - LastSec->Size + : Seg->MemSize; + } + + // Remove every PT_LOAD segment which doesn't contain sections if + // it doesn't overlap with PT_PHDRS. + Segments.erase(std::remove_if(Segments.begin(), Segments.end(), + [this](const SegPtr &Seg) { + return Seg->Type == PT_LOAD && + Seg->firstSection() == nullptr && + !isParentForPhdrs(Seg.get()); + }), + Segments.end()); +} + static uint64_t alignToAddr(uint64_t Offset, uint64_t Addr, uint64_t Align) { // Calculate Diff such that (Offset + Diff) & -Align == Addr & -Align. if (Align == 0) @@ -1472,7 +1476,34 @@ ElfHdr.Align = 0; } +template void ELFWriter::setParentSegment(Segment &Child) { + for (auto &Parent : Obj.segments()) { + // Every segment will overlap with itself but we don't want a segment to + // be it's own parent so we avoid that situation. + if (&Child != &Parent && segmentOverlapsSegment(Child, Parent)) { + // We want a canonical "most parental" segment but this requires + // inspecting the ParentSegment. + if (compareSegmentsByOffset(&Parent, &Child)) + if (Child.ParentSegment == nullptr || + compareSegmentsByOffset(&Parent, Child.ParentSegment)) { + Child.ParentSegment = &Parent; + } + } + } +} + template void ELFWriter::assignOffsets() { + // Set parent segment for ELF and program headers, so we don't + // occasionally remove load segment containing them in fixupSegments. + setParentSegment(Obj.ElfHdrSegment); + setParentSegment(Obj.ProgramHdrSegment); + // Fixup segments: set correct file and memory sizes and remove + // empty segments if any + Obj.fixupSegments(); + // Now we do an O(n^2) loop through the segments in order to match up + // segments. + for (auto &Child : Obj.segments()) + setParentSegment(Child); // We need a temporary list of segments that has a special order to it // so that we know that anytime ->ParentSegment is set that segment has // already had its offset properly set.