Index: test/tools/llvm-objcopy/binary-remove-all-but-one.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/binary-remove-all-but-one.test @@ -0,0 +1,46 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy -R .text -R .text3 -O binary %t %t2 +# RUN: od -t x1 %t2 | FileCheck %s +# RUN: wc -c %t2 | FileCheck %s -check-prefix=SIZE + +!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: 0x1000 + AddressAlign: 0x1000 + Content: "c3c3c3c3" + Size: 0x1000 + - Name: .text2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x2000 + AddressAlign: 0x1000 + Content: "DEADBEEF" + Size: 0x1000 + - Name: .text3 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x3000 + AddressAlign: 0x1000 + Content: "32323232" + Size: 0x1000 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .text + - Section: .text2 + - Section: .text3 + +# CHECK: 0000000 de ad be ef + +# SIZE: 4096 Index: test/tools/llvm-objcopy/binary-remove-end.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/binary-remove-end.test @@ -0,0 +1,47 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy -R .text3 -O binary %t %t2 +# RUN: od -v -t x1 %t2 | FileCheck %s +# RUN: wc -c %t2 | FileCheck %s -check-prefix=SIZE + +!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: 0x1000 + AddressAlign: 0x1000 + Content: "c3c3c3c3" + Size: 0x1000 + - Name: .text2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x2000 + AddressAlign: 0x1000 + Content: "DEADBEEF" + Size: 0x1000 + - Name: .text3 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x3000 + AddressAlign: 0x1000 + Content: "32323232" + Size: 0x1000 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .text + - Section: .text2 + - Section: .text3 + +# CHECK: 0000000 c3 c3 c3 c3 +# CHECK: 0010000 de ad be ef + +# SIZE: 8192 Index: test/tools/llvm-objcopy/binary-remove-middle.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/binary-remove-middle.test @@ -0,0 +1,48 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy -R .text2 -O binary %t %t2 +# RUN: od -v -t x1 %t2 | FileCheck %s +# RUN: wc -c %t2 | FileCheck %s -check-prefix=SIZE + +!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: 0x1000 + AddressAlign: 0x1000 + Content: "c3c3c3c3" + Size: 0x1000 + - Name: .text2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x2000 + AddressAlign: 0x1000 + Content: "DEADBEEF" + Size: 0x1000 + - Name: .text3 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x3000 + AddressAlign: 0x1000 + Content: "32323232" + Size: 0x1000 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .text + - Section: .text2 + - Section: .text3 + +# CHECK: 0000000 c3 c3 c3 c3 +# CHECK: 0010000 00 00 00 00 +# CHECK: 0020000 32 32 32 32 + +# SIZE: 12288 Index: test/tools/llvm-objcopy/two-seg-remove-end.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/two-seg-remove-end.test @@ -0,0 +1,61 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy -R .text4 -O binary %t %t2 +# RUN: od -v -t x1 %t2 | FileCheck %s +# RUN: wc -c %t2 | FileCheck %s -check-prefix=SIZE + +!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: 0x1000 + AddressAlign: 0x1000 + Content: "c3c3c3c3" + Size: 0x1000 + - Name: .text2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x2000 + AddressAlign: 0x1000 + Content: "DEADBEEF" + Size: 0x1000 + - Name: .text3 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x3000 + AddressAlign: 0x1000 + Content: "32323232" + Size: 0x1000 + - Name: .text4 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x4000 + AddressAlign: 0x1000 + Content: "FFFFFFFF" + Size: 0x1000 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .text + - Section: .text2 + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .text3 + - Section: .text4 + +# CHECK: 0000000 c3 c3 c3 c3 +# CHECK: 0010000 de ad be ef +# CHECK: 0020000 32 32 32 32 + +# SIZE: 12288 Index: test/tools/llvm-objcopy/two-seg-remove-third-sec.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/two-seg-remove-third-sec.test @@ -0,0 +1,62 @@ +# RUN: yaml2obj %s -o %t +# RUN: llvm-objcopy -R .text3 -O binary %t %t2 +# RUN: od -v -t x1 %t2 | FileCheck %s +# RUN: wc -c %t2 | FileCheck %s -check-prefix=SIZE + +!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: 0x1000 + AddressAlign: 0x1000 + Content: "c3c3c3c3" + Size: 0x1000 + - Name: .text2 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x2000 + AddressAlign: 0x1000 + Content: "DEADBEEF" + Size: 0x1000 + - Name: .text3 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x3000 + AddressAlign: 0x1000 + Content: "32323232" + Size: 0x1000 + - Name: .text4 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + Address: 0x4000 + AddressAlign: 0x1000 + Content: "FFFFFFFF" + Size: 0x1000 +ProgramHeaders: + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .text + - Section: .text2 + - Type: PT_LOAD + Flags: [ PF_R ] + VAddr: 0x1000 + PAddr: 0x1000 + Sections: + - Section: .text3 + - Section: .text4 + +# CHECK: 0000000 c3 c3 c3 c3 +# CHECK: 0010000 de ad be ef +# CHECK: 0020000 00 00 00 00 +# CHECK: 0030000 ff ff ff ff + +# SIZE: 16384 Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -696,13 +696,8 @@ return Offset + Diff; } -template void ELFObject::assignOffsets() { - // 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 it's offset properly set. - std::vector OrderedSegments; - for (auto &Segment : this->Segments) - OrderedSegments.push_back(Segment.get()); +// Orders segments such that if x = y->ParentSegment then y comes before x. +static void OrderSegments(std::vector &Segments) { auto CompareSegments = [](const Segment *A, const Segment *B) { // Any segment without a parent segment should come before a segment // that has a parent segment. @@ -712,24 +707,20 @@ return false; return A->Index < B->Index; }; - std::stable_sort(std::begin(OrderedSegments), std::end(OrderedSegments), - CompareSegments); - // The size of ELF + program headers will not change so it is ok to assume - // that the first offset of the first segment is a good place to start - // outputting sections. This covers both the standard case and the PT_PHDR - // case. - uint64_t Offset; - if (!OrderedSegments.empty()) { - Offset = OrderedSegments[0]->Offset; - } else { - Offset = sizeof(Elf_Ehdr); - } + std::stable_sort(std::begin(Segments), std::end(Segments), CompareSegments); +} + +// Finds a consistent layout for a of list segments starting from an Offset. +// This function assumes that Segments have been sorted by OrderSegments. This +// function returns an Offset past any segment thus far layed out. +static uint64_t LayoutSegments(std::vector &Segments, + uint64_t Offset) { // The only way a segment should move is if a section was between two // segments and that section was removed. If that section isn't in a segment // then it's acceptable, but not ideal, to simply move it to after the // segments. So we can simply layout segments one after the other accounting // for alignment. - for (auto &Segment : OrderedSegments) { + for (auto &Segment : Segments) { // We assume that segments have been ordered by OriginalOffset and Index // such that a parent segment will always come before a child segment in // OrderedSegments. This means that the Offset of the ParentSegment should @@ -744,6 +735,15 @@ } Offset = std::max(Offset, Segment->Offset + Segment->FileSize); } + return Offset; +} + +// Finds a consistent layout of a list of sections. This function assumes that +// the ->ParentSegment of each section has already been layed out in some +// consistent fashion. The supplied starting Offset is used for the starting +// offset of any section that does not have a ParentSegment. +template +static uint64_t LayoutSections(std::vector &Sections, uint64_t Offset) { // Now the offset of every segment has been set we can assign the offsets // of each section. For sections that are covered by a segment we should use // the segment's original offset and the section's original offset to compute @@ -751,7 +751,7 @@ // of the segment we can assign a new offset to the section. For sections not // covered by segments we can just bump Offset to the next valid location. uint32_t Index = 1; - for (auto &Section : this->Sections) { + for (auto &Section : Sections) { Section->Index = Index++; if (Section->ParentSegment != nullptr) { auto Segment = Section->ParentSegment; @@ -764,10 +764,35 @@ Offset += Section->Size; } } + return Offset; +} - if (this->WriteSectionHeaders) { - Offset = alignTo(Offset, sizeof(typename ELFT::Addr)); +template void ELFObject::assignOffsets() { + // 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 it's offset properly set. + std::vector OrderedSegments; + for (auto &Segment : this->Segments) + OrderedSegments.push_back(Segment.get()); + OrderSegments(OrderedSegments); + // The size of ELF + program headers will not change so it is ok to assume + // that the first offset of the first segment is a good place to start + // outputting sections. This covers both the standard case and the PT_PHDR + // case. + uint64_t Offset; + if (!OrderedSegments.empty()) { + Offset = OrderedSegments[0]->Offset; + } else { + Offset = sizeof(Elf_Ehdr); } + // Now we can decide segment layout. + Offset = LayoutSegments(OrderedSegments, Offset); + // Now we can decide section layout. + Offset = LayoutSections(this->Sections, Offset); + // If we need to write the section header table out then we need align the + // Offset so that SHOffset is valid. + if (this->WriteSectionHeaders) + Offset = alignTo(Offset, sizeof(typename ELFT::Addr)); this->SHOffset = Offset; } @@ -821,33 +846,69 @@ template void BinaryObject::write(FileOutputBuffer &Out) const { - for (auto &Segment : this->Segments) { - // GNU objcopy does not output segments that do not cover a section. Such - // segments can sometimes be produced by LLD due to how LLD handles PT_PHDR. - if (Segment->Type == PT_LOAD && Segment->firstSection() != nullptr) { - Segment->writeSegment(Out); - } + for (auto &Section : this->Sections) { + if ((Section->Flags & SHF_ALLOC) == 0) + continue; + Section->writeSection(Out); } } template void BinaryObject::finalize() { - // Put all segments in offset order. - auto CompareSegments = [](const SegPtr &A, const SegPtr &B) { - return A->Offset < B->Offset; - }; - std::sort(std::begin(this->Segments), std::end(this->Segments), - CompareSegments); - - uint64_t Offset = 0; - for (auto &Segment : this->Segments) { - if (Segment->Type == llvm::ELF::PT_LOAD && - Segment->firstSection() != nullptr) { - Offset = alignToAddr(Offset, Segment->VAddr, Segment->Align); - Segment->Offset = Offset; - Offset += Segment->FileSize; + // 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 it's offset properly set. We only want to consider the segments + // that will affect layout of allocated sections so we only add those. + std::vector OrderedSegments; + for (auto &Section : this->Sections) { + if ((Section->Flags & SHF_ALLOC) != 0 && + Section->ParentSegment != nullptr) { + OrderedSegments.push_back(Section->ParentSegment); } } - TotalSize = Offset; + OrderSegments(OrderedSegments); + auto End = + std::unique(std::begin(OrderedSegments), std::end(OrderedSegments)); + OrderedSegments.erase(End, std::end(OrderedSegments)); + + // Modify the first segment so that p_offset = sh_offset of same segment by + // chopping the front part off. This allows our layout algorithm to proceed as + // expected while chopping off the front unused part. + if (!OrderedSegments.empty()) { + auto Seg = OrderedSegments[0]; + auto Sec = Seg->firstSection(); + auto Diff = Sec->OriginalOffset - Seg->OriginalOffset; + Seg->OriginalOffset += Diff; + // The size needs to be shrunk as well + Seg->FileSize -= Diff; + Seg->MemSize -= Diff; + // The VAddr needs to be adjusted so that the alignment is correct as well + Seg->VAddr += Diff; + Seg->PAddr = Seg->VAddr; + } + + // Now we can use the existing layout algorithm. + uint64_t Offset = LayoutSegments(OrderedSegments, 0); + + // TODO: generalize LayoutSections to take a range. Pass a special range + // constructed from an iterator that skips values for which a predicate does + // not hold. Then pass such a range to LayoutSections instead of cosntructing + // AllocatedSections here. + std::vector AllocatedSections; + for (auto &Section : this->Sections) { + if ((Section->Flags & SHF_ALLOC) == 0) + continue; + AllocatedSections.push_back(Section.get()); + } + LayoutSections(AllocatedSections, Offset); + + // Now that every section has been layed out we just need to compute the total + // size. This might not have anything to do with Offset however because a + // segment might go well past a used allocated section. + TotalSize = 0; + for (const auto Section : AllocatedSections) { + if (Section->Type != SHT_NOBITS) + TotalSize = std::max(TotalSize, Section->Offset + Section->Size); + } } namespace llvm {