Index: tools/llvm-objcopy/Object.h =================================================================== --- tools/llvm-objcopy/Object.h +++ tools/llvm-objcopy/Object.h @@ -71,6 +71,8 @@ uint64_t Type; uint64_t VAddr; + uint64_t OriginalOffset; + Segment(llvm::ArrayRef Data) : Contents(Data) {} void finalize(); const SectionBase *firstSection() const { Index: tools/llvm-objcopy/Object.cpp =================================================================== --- tools/llvm-objcopy/Object.cpp +++ tools/llvm-objcopy/Object.cpp @@ -112,6 +112,7 @@ Segment &Seg = *Segments.back(); Seg.Type = Phdr.p_type; Seg.Flags = Phdr.p_flags; + Seg.OriginalOffset = Phdr.p_offset; Seg.Offset = Phdr.p_offset; Seg.VAddr = Phdr.p_vaddr; Seg.PAddr = Phdr.p_paddr; @@ -254,57 +255,47 @@ } template void ELFObject::assignOffsets() { - // Decide file offsets and indexes. - size_t PhdrSize = this->Segments.size() * sizeof(Elf_Phdr); - // We can put section data after the ELF header and the program headers. - uint64_t Offset = sizeof(Elf_Ehdr) + PhdrSize; - uint64_t Index = 1; + // 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; + } else { + Offset = sizeof(Elf_Ehdr); + } + // 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; + } + // 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 + // the offset from the start of the segment. Using the offset from the start + // 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) { - // The segment can have a different alignment than the section. In the case - // that there is a parent segment then as long as we satisfy the alignment - // of the segment it should follow that that the section is aligned. - if (Section->ParentSegment) { - auto FirstInSeg = Section->ParentSegment->firstSection(); - if (FirstInSeg == Section.get()) { - Offset = alignTo(Offset, Section->ParentSegment->Align); - // There can be gaps at the start of a segment before the first section. - // So first we assign the alignment of the segment and then assign the - // location of the section from there - Section->Offset = - Offset + Section->OriginalOffset - Section->ParentSegment->Offset; - } - // We should respect interstitial gaps of allocated sections. We *must* - // maintain the memory image so that addresses are preserved. As, with the - // exception of SHT_NOBITS sections at the end of segments, the memory - // image is a copy of the file image, we preserve the file image as well. - // There's a strange case where a thread local SHT_NOBITS can cause the - // memory image and file image to not be the same. This occurs, on some - // systems, when a thread local SHT_NOBITS is between two SHT_PROGBITS - // and the thread local SHT_NOBITS section is at the end of a TLS segment. - // In this case to faithfully copy the segment file image we must use - // relative offsets. In any other case this would be the same as using the - // relative addresses so this should maintian the memory image as desired. - Offset = FirstInSeg->Offset + Section->OriginalOffset - - FirstInSeg->OriginalOffset; - } - // Alignment should have already been handled by the above if statement if - // this if this section is in a segment. Technically this shouldn't do - // anything bad if the alignments of the sections are all correct and the - // file image isn't corrupted. Still in sticking with the motto "maintain - // the file image" we should avoid messing up the file image if the - // alignment disagrees with the file image. - if (!Section->ParentSegment && Section->Align) - Offset = alignTo(Offset, Section->Align); - Section->Offset = Offset; Section->Index = Index++; - if (Section->Type != SHT_NOBITS) - Offset += Section->Size; + if (Section->ParentSegment != nullptr) { + auto Segment = Section->ParentSegment; + Section->Offset = + Segment->Offset + (Section->OriginalOffset - Segment->OriginalOffset); + } else { + Offset = alignTo(Offset, Section->Offset); + Section->Offset = Offset; + if (Section->Type != SHT_NOBITS) + Offset += Section->Size; + } } - // 'offset' should now be just after all the section data so we should set the - // section header table offset to be exactly here. This spot might not be - // aligned properly however so we should align it as needed. For 32-bit ELF - // this needs to be 4-byte aligned and on 64-bit it needs to be 8-byte aligned - // so the size of ELFT::Addr is used to ensure this. + Offset = alignTo(Offset, sizeof(typename ELFT::Addr)); this->SHOffset = Offset; }