[llvm] r311825 - [llvm-objcopy] New layout algorithm that lays out segments first

Petr Hosek via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 25 18:32:20 PDT 2017


Author: phosek
Date: Fri Aug 25 18:32:20 2017
New Revision: 311825

URL: http://llvm.org/viewvc/llvm-project?rev=311825&view=rev
Log:
[llvm-objcopy] New layout algorithm that lays out segments first

The current file layout algorithm in llvm-objcopy is simple but
difficult to reason about. It also makes it very complicated to support
nested segments and to support segments that have offsets that come
before a point after the program headers. To support these cases and
simplify one of the most critical parts llvm-objcopy I rewrote the
layout algorithm. Laying out segments first solves most of the issues
encountered by the previous algorithm.

Patch by Jake Ehrlich

Differential Revision: https://reviews.llvm.org/D36494

Modified:
    llvm/trunk/tools/llvm-objcopy/Object.cpp
    llvm/trunk/tools/llvm-objcopy/Object.h

Modified: llvm/trunk/tools/llvm-objcopy/Object.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/Object.cpp?rev=311825&r1=311824&r2=311825&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/Object.cpp (original)
+++ llvm/trunk/tools/llvm-objcopy/Object.cpp Fri Aug 25 18:32:20 2017
@@ -112,6 +112,7 @@ void Object<ELFT>::readProgramHeaders(co
     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 <class ELFT> void ELFObject<ELF
 }
 
 template <class ELFT> void ELFObject<ELFT>::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;
 }

Modified: llvm/trunk/tools/llvm-objcopy/Object.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/Object.h?rev=311825&r1=311824&r2=311825&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/Object.h (original)
+++ llvm/trunk/tools/llvm-objcopy/Object.h Fri Aug 25 18:32:20 2017
@@ -71,6 +71,8 @@ public:
   uint64_t Type;
   uint64_t VAddr;
 
+  uint64_t OriginalOffset;
+
   Segment(llvm::ArrayRef<uint8_t> Data) : Contents(Data) {}
   void finalize();
   const SectionBase *firstSection() const {




More information about the llvm-commits mailing list