Index: test/tools/llvm-objcopy/pt-phdr.test =================================================================== --- /dev/null +++ test/tools/llvm-objcopy/pt-phdr.test @@ -0,0 +1,68 @@ +# RUN: llvm-objcopy %p/Inputs/pt-phdr.elf %t +# RUN: llvm-readobj -program-headers %t | FileCheck %s + +#CHECK: ProgramHeaders [ +#CHECK-NEXT: ProgramHeader { +#CHECK-NEXT: Type: PT_PHDR +#CHECK-NEXT: Offset: 0x40 +#CHECK-NEXT: VirtualAddress: 0x200040 +#CHECK-NEXT: PhysicalAddress: 0x200040 +#CHECK-NEXT: FileSize: 280 +#CHECK-NEXT: MemSize: 280 +#CHECK-NEXT: Flags [ +#CHECK-NEXT: PF_R +#CHECK-NEXT: ] +#CHECK-NEXT: Alignment: 8 +#CHECK-NEXT: } +#CHECK-NEXT: ProgramHeader { +#CHECK-NEXT: Type: PT_LOAD +#CHECK-NEXT: Offset: 0x0 +#CHECK-NEXT: VirtualAddress: 0x200000 +#CHECK-NEXT: PhysicalAddress: 0x200000 +#CHECK-NEXT: FileSize: 344 +#CHECK-NEXT: MemSize: 344 +#CHECK-NEXT: Flags [ +#CHECK-NEXT: PF_R +#CHECK-NEXT: ] +#CHECK-NEXT: Alignment: 4096 +#CHECK-NEXT: } +#CHECK-NEXT: ProgramHeader { +#CHECK-NEXT: Type: PT_LOAD +#CHECK-NEXT: Offset: 0x1000 +#CHECK-NEXT: VirtualAddress: 0x201000 +#CHECK-NEXT: PhysicalAddress: 0x201000 +#CHECK-NEXT: FileSize: 1 +#CHECK-NEXT: MemSize: 1 +#CHECK-NEXT: Flags [ +#CHECK-NEXT: PF_R +#CHECK-NEXT: PF_X +#CHECK-NEXT: ] +#CHECK-NEXT: Alignment: 4096 +#CHECK-NEXT: } +#CHECK-NEXT: ProgramHeader { +#CHECK-NEXT: Type: PT_LOAD +#CHECK-NEXT: Offset: 0x2000 +#CHECK-NEXT: VirtualAddress: 0x202000 +#CHECK-NEXT: PhysicalAddress: 0x202000 +#CHECK-NEXT: FileSize: 14 +#CHECK-NEXT: MemSize: 14 +#CHECK-NEXT: Flags [ +#CHECK-NEXT: PF_R +#CHECK-NEXT: PF_W +#CHECK-NEXT: ] +#CHECK-NEXT: Alignment: 4096 +#CHECK-NEXT: } +#CHECK-NEXT: ProgramHeader { +#CHECK-NEXT: Type: PT_GNU_STACK (0x6474E551) +#CHECK-NEXT: Offset: 0x0 +#CHECK-NEXT: VirtualAddress: 0x0 +#CHECK-NEXT: PhysicalAddress: 0x0 +#CHECK-NEXT: FileSize: 0 +#CHECK-NEXT: MemSize: 0 +#CHECK-NEXT: Flags [ +#CHECK-NEXT: PF_R +#CHECK-NEXT: PF_W +#CHECK-NEXT: ] +#CHECK-NEXT: Alignment: 0 +#CHECK-NEXT: } +#CHECK-NEXT:] Index: tools/llvm-objcopy/Object.h =================================================================== --- tools/llvm-objcopy/Object.h +++ tools/llvm-objcopy/Object.h @@ -72,6 +72,7 @@ uint64_t VAddr; uint64_t OriginalOffset; + Segment *ParentSegment; Segment(llvm::ArrayRef Data) : Contents(Data) {} void finalize(); Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -213,6 +213,15 @@ Segment.Offset + Segment.FileSize >= Section.OriginalOffset + SecSize; } +// Returns true IFF a segment's original offset is inside of another segment's +// range +static bool segmentOverlappsSegment(const Segment &Child, + const Segment &Parent) { + + return Parent.OriginalOffset <= Child.OriginalOffset && + Parent.OriginalOffset + Parent.FileSize >= Child.OriginalOffset; +} + template void Object::readProgramHeaders(const ELFFile &ElfFile) { uint32_t Index = 0; @@ -241,8 +250,32 @@ } } } + // Now we do an O(n^2) loop though the segments in order to match up nested + // segments up. + for (auto &Child : Segments) { + for (auto &Parent : Segments) { + // Every segment will be a child of itself but we don't want a segment + // to be it's own parent so we avoid that situation. + if (&Child != &Parent && segmentOverlappsSegment(*Child, *Parent)) { + // We want a cononical "most parental" segment but this requires + // inspecting the ParentSegment + if (Child->ParentSegment != nullptr) { + if (Child->ParentSegment->OriginalOffset > Parent->OriginalOffset) { + Child->ParentSegment = Parent.get(); + } + if (Child->ParentSegment->OriginalOffset == Parent->OriginalOffset && + Child->ParentSegment->FileSize < Parent->FileSize) { + Child->ParentSegment = Parent.get(); + } + } else { + Child->ParentSegment = Parent.get(); + } + } + } + } } + template void Object::initSymbolTable(const llvm::object::ELFFile &ElfFile, SymbolTableSection *SymTab) { @@ -439,25 +472,51 @@ } 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 properlly set. + std::vector OrderedSegments; + for (auto &Segment : this->Segments) + OrderedSegments.push_back(Segment.get()); // 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(!this->Segments.empty()) { - Offset = this->Segments[0]->Offset; + if (!OrderedSegments.empty()) { + Offset = OrderedSegments[0]->Offset; } else { Offset = sizeof(Elf_Ehdr); } + auto CompareSegments = [](const Segment *A, const Segment *B) { + // Any segment without a parent segment should come before a segment + // that has a parent segment + if (A->OriginalOffset < B->OriginalOffset) + return true; + else if (A->OriginalOffset > B->OriginalOffset) + return false; + return A->FileSize > B->FileSize; + }; + std::stable_sort(std::begin(OrderedSegments), std::end(OrderedSegments), + CompareSegments); // 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 : this->Segments) { - Offset = alignTo(Offset, Segment->Align); - Segment->Offset = Offset; - Offset += Segment->FileSize; + for (auto &Segment : OrderedSegments) { + // We assume that segments have been ordered by OriginalOffset and Size + // such that a parrent segment will always come before a child segment in + // Segments. This means that the Offset of the ParentSegment should already + // be set and we can set our offset relative to it + if (Segment->ParentSegment != nullptr) { + auto PSeg = Segment->ParentSegment; + Segment->Offset = PSeg->Offset + Segment->Offset - PSeg->Offset; + } else { + Offset = alignTo(Offset, Segment->Align); + Segment->Offset = Offset; + Offset += Segment->FileSize; + } } // Now the offset of every segmnet has been set we can assign the offsets // of each section. For sections that are covered by a segment we should use