[llvm] r318324 - [llvm-objcopy] Change -O binary to respect section removal and behave like GNU objcopy

Jake Ehrlich via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 15 11:13:31 PST 2017


Author: jakehehrlich
Date: Wed Nov 15 11:13:31 2017
New Revision: 318324

URL: http://llvm.org/viewvc/llvm-project?rev=318324&view=rev
Log:
[llvm-objcopy] Change -O binary to respect section removal and behave like GNU objcopy

The original -O binary implementation just copied segment data from the
object and dumped it into a file. This doesn't take into account any
operations performed on objects such as section removal. GNU objcopy has
some specific behavior that we'd also like to respect. For instance
using -O binary and -j <some_section> will dump <some_section> to a
file. This change implements GNU objcopy style -O binary to as close of
an approximation as I can determine.

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

Added:
    llvm/trunk/test/tools/llvm-objcopy/binary-first-seg-offset-zero.test
    llvm/trunk/test/tools/llvm-objcopy/binary-remove-all-but-one.test
    llvm/trunk/test/tools/llvm-objcopy/binary-remove-end.test
    llvm/trunk/test/tools/llvm-objcopy/binary-remove-middle.test
    llvm/trunk/test/tools/llvm-objcopy/parent-loop-check.test
    llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-end.test
    llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-first.test
    llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-third-sec.test
Modified:
    llvm/trunk/tools/llvm-objcopy/Object.cpp

Added: llvm/trunk/test/tools/llvm-objcopy/binary-first-seg-offset-zero.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/binary-first-seg-offset-zero.test?rev=318324&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/binary-first-seg-offset-zero.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/binary-first-seg-offset-zero.test Wed Nov 15 11:13:31 2017
@@ -0,0 +1,34 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy -R=.note -O binary %t %t2
+# RUN: od -Ax -t x1 %t2 | FileCheck %s
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .note
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x1000
+    AddressAlign:    0x1000
+    Content:         "32323232"
+    Size:            32
+  - Name:            .rodata
+    Flags:           [ SHF_ALLOC ]
+    Type:            SHT_PROGBITS
+    Address:         0x1020
+    Size:            4064
+    Content:         "DEADBEEF"
+ProgramHeaders:
+  - Type: PT_LOAD
+    Flags: [ PF_R ]
+    VAddr: 0x1000
+    PAddr: 0x1000
+    Sections:
+      - Section: .note
+      - Section: .rodata
+
+# CHECK: 000000 de ad be ef

Added: llvm/trunk/test/tools/llvm-objcopy/binary-remove-all-but-one.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/binary-remove-all-but-one.test?rev=318324&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/binary-remove-all-but-one.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/binary-remove-all-but-one.test Wed Nov 15 11:13:31 2017
@@ -0,0 +1,46 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy -R .text -R .text3 -O binary %t %t2
+# RUN: od -Ax -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: 000000 de ad be ef
+
+# SIZE: 4096

Added: llvm/trunk/test/tools/llvm-objcopy/binary-remove-end.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/binary-remove-end.test?rev=318324&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/binary-remove-end.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/binary-remove-end.test Wed Nov 15 11:13:31 2017
@@ -0,0 +1,47 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy -R .text3 -O binary %t %t2
+# RUN: od -Ax -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: 000000 c3 c3 c3 c3
+# CHECK: 001000 de ad be ef
+
+# SIZE: 8192

Added: llvm/trunk/test/tools/llvm-objcopy/binary-remove-middle.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/binary-remove-middle.test?rev=318324&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/binary-remove-middle.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/binary-remove-middle.test Wed Nov 15 11:13:31 2017
@@ -0,0 +1,48 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy -R .text2 -O binary %t %t2
+# RUN: od -Ax -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: 000000 c3 c3 c3 c3
+# CHECK: 001000 00 00 00 00
+# CHECK: 002000 32 32 32 32
+
+# SIZE: 12288

Added: llvm/trunk/test/tools/llvm-objcopy/parent-loop-check.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/parent-loop-check.test?rev=318324&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/parent-loop-check.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/parent-loop-check.test Wed Nov 15 11:13:31 2017
@@ -0,0 +1,50 @@
+# This test has a subtle purpose. Because of the specifics of the layout
+# algorithm if we're not careful cycles can occur when we resolve the order
+# that we should layout segments in. In this test we're making sure that the
+# PT_LOAD segment won't set the PT_NOTE segment as the first to be laid out
+# while the PT_NOTE segment sets the PT_LOAD load to be the first to be laid
+# out. This problem becomes visible if the layout of the file changes due to
+# the removal of a section. We use -O binary here because removing a section
+# in this way won't cause the location of the PT_LOAD segment to change if we
+# don't.
+
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy -R=.note -O binary %t %t2
+# RUN: od -Ax -t x1 %t2 | FileCheck %s
+
+!ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .note
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC ]
+    Address:         0x1000
+    AddressAlign:    0x1000
+    Content:         "32323232"
+    Size:            32
+  - Name:            .rodata
+    Flags:           [ SHF_ALLOC ]
+    Type:            SHT_PROGBITS
+    Address:         0x1020
+    Size:            4064
+    Content:         "DEADBEEF"
+ProgramHeaders:
+  - Type: PT_LOAD
+    Flags: [ PF_R ]
+    VAddr: 0x1000
+    PAddr: 0x1000
+    Sections:
+      - Section: .note
+      - Section: .rodata
+  - Type: PT_NOTE
+    Flags: [ PF_R ]
+    VAddr: 0x1000
+    PAddr: 0x1000
+    Sections:
+      - Section: .note
+
+# CHECK: 000000 de ad be ef

Added: llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-end.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-end.test?rev=318324&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-end.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-end.test Wed Nov 15 11:13:31 2017
@@ -0,0 +1,61 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy -R .text4 -O binary %t %t2
+# RUN: od -Ax -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: 000000 c3 c3 c3 c3
+# CHECK: 001000 de ad be ef
+# CHECK: 002000 32 32 32 32
+
+# SIZE: 12288

Added: llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-first.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-first.test?rev=318324&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-first.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-first.test Wed Nov 15 11:13:31 2017
@@ -0,0 +1,61 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy -R .text -O binary %t %t2
+# RUN: od -Ax -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: 000000 de ad be ef
+# CHECK: 001000 32 32 32 32
+# CHECK: 002000 ff ff ff ff
+
+# SIZE: 12288

Added: llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-third-sec.test
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-third-sec.test?rev=318324&view=auto
==============================================================================
--- llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-third-sec.test (added)
+++ llvm/trunk/test/tools/llvm-objcopy/two-seg-remove-third-sec.test Wed Nov 15 11:13:31 2017
@@ -0,0 +1,62 @@
+# RUN: yaml2obj %s -o %t
+# RUN: llvm-objcopy -R .text3 -O binary %t %t2
+# RUN: od -Ax -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: 000000 c3 c3 c3 c3
+# CHECK: 001000 de ad be ef
+# CHECK: 002000 00 00 00 00
+# CHECK: 003000 ff ff ff ff
+
+# SIZE: 16384

Modified: llvm/trunk/tools/llvm-objcopy/Object.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-objcopy/Object.cpp?rev=318324&r1=318323&r2=318324&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-objcopy/Object.cpp (original)
+++ llvm/trunk/tools/llvm-objcopy/Object.cpp Wed Nov 15 11:13:31 2017
@@ -339,6 +339,16 @@ static bool segmentOverlapsSegment(const
          Parent.OriginalOffset + Parent.FileSize > Child.OriginalOffset;
 }
 
+static bool 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;
+  if (A->OriginalOffset > B->OriginalOffset)
+    return false;
+  return A->Index < B->Index;
+}
+
 template <class ELFT>
 void Object<ELFT>::readProgramHeaders(const ELFFile<ELFT> &ElfFile) {
   uint32_t Index = 0;
@@ -376,18 +386,11 @@ void Object<ELFT>::readProgramHeaders(co
       if (&Child != &Parent && segmentOverlapsSegment(*Child, *Parent)) {
         // We want a canonical "most parental" segment but this requires
         // inspecting the ParentSegment.
-        if (Child->ParentSegment != nullptr) {
-          if (Child->ParentSegment->OriginalOffset > Parent->OriginalOffset) {
-            Child->ParentSegment = Parent.get();
-          } else if (Child->ParentSegment->Index > Parent->Index) {
-            // They must have equal OriginalOffsets so we need to disambiguate.
-            // To decide which is the parent we'll choose the one with the
-            // higher index.
+        if (compareSegments(Parent.get(), Child.get()))
+          if (Child->ParentSegment == nullptr ||
+              compareSegments(Parent.get(), Child->ParentSegment)) {
             Child->ParentSegment = Parent.get();
           }
-        } else {
-          Child->ParentSegment = Parent.get();
-        }
       }
     }
   }
@@ -698,40 +701,24 @@ static uint64_t alignToAddr(uint64_t Off
   return Offset + Diff;
 }
 
-template <class ELFT> void ELFObject<ELFT>::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<Segment *> OrderedSegments;
-  for (auto &Segment : this->Segments)
-    OrderedSegments.push_back(Segment.get());
-  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;
-    if (A->OriginalOffset > B->OriginalOffset)
-      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);
-  }
+// Orders segments such that if x = y->ParentSegment then y comes before x.
+static void OrderSegments(std::vector<Segment *> &Segments) {
+  std::stable_sort(std::begin(Segments), std::end(Segments), compareSegments);
+}
+
+// This function finds a consistent layout for a list of segments starting from
+// an Offset. It assumes that Segments have been sorted by OrderSegments and
+// returns an Offset one past the end of the last segment.
+static uint64_t LayoutSegments(std::vector<Segment *> &Segments,
+                               uint64_t Offset) {
+  assert(std::is_sorted(std::begin(Segments), std::end(Segments),
+                        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 : 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
@@ -746,6 +733,17 @@ template <class ELFT> void ELFObject<ELF
     }
     Offset = std::max(Offset, Segment->Offset + Segment->FileSize);
   }
+  return Offset;
+}
+
+// This function finds a consistent layout for a list of sections. It assumes
+// that the ->ParentSegment of each section has already been laid out. The
+// supplied starting Offset is used for the starting offset of any section that
+// does not have a ParentSegment. It returns either the offset given if all
+// sections had a ParentSegment or an offset one past the last section if there
+// was a section that didn't have a ParentSegment.
+template <class SecPtr>
+static uint64_t LayoutSections(std::vector<SecPtr> &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
@@ -753,7 +751,7 @@ template <class ELFT> void ELFObject<ELF
   // 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;
@@ -766,10 +764,33 @@ template <class ELFT> void ELFObject<ELF
         Offset += Section->Size;
     }
   }
+  return Offset;
+}
 
-  if (this->WriteSectionHeaders) {
-    Offset = alignTo(Offset, sizeof(typename ELFT::Addr));
+template <class ELFT> void ELFObject<ELFT>::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 its offset properly set.
+  std::vector<Segment *> 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);
   }
+  Offset = LayoutSegments(OrderedSegments, Offset);
+  Offset = LayoutSections(this->Sections, Offset);
+  // If we need to write the section header table out then we need to align the
+  // Offset so that SHOffset is valid.
+  if (this->WriteSectionHeaders)
+    Offset = alignTo(Offset, sizeof(typename ELFT::Addr));
   this->SHOffset = Offset;
 }
 
@@ -823,33 +844,78 @@ template <class ELFT> size_t BinaryObjec
 
 template <class ELFT>
 void BinaryObject<ELFT>::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 <class ELFT> void BinaryObject<ELFT>::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);
+  // TODO: Create a filter range to construct OrderedSegments from so that this
+  // code can be deduped with assignOffsets above. This should also solve the
+  // todo below for LayoutSections.
+  // 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<Segment *> OrderedSegments;
+  for (auto &Section : this->Sections) {
+    if ((Section->Flags & SHF_ALLOC) != 0 &&
+        Section->ParentSegment != nullptr) {
+      OrderedSegments.push_back(Section->ParentSegment);
+    }
+  }
+  OrderSegments(OrderedSegments);
+  // Because we add a ParentSegment for each section we might have duplicate
+  // segments in OrderedSegments. If there were duplicates then LayoutSegments
+  // would do very strange things.
+  auto End =
+      std::unique(std::begin(OrderedSegments), std::end(OrderedSegments));
+  OrderedSegments.erase(End, std::end(OrderedSegments));
+
+  // Modify the first segment so that there is no gap at the start. This allows
+  // our layout algorithm to proceed as expected while not out writing out the
+  // gap at the start.
+  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;
+    // We don't want this to be shifted by alignment so we need to set the
+    // alignment to zero.
+    Seg->Align = 0;
+  }
+
+  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 constructing
+  // AllocatedSections here.
+  std::vector<SectionBase *> AllocatedSections;
+  for (auto &Section : this->Sections) {
+    if ((Section->Flags & SHF_ALLOC) == 0)
+      continue;
+    AllocatedSections.push_back(Section.get());
+  }
+  LayoutSections(AllocatedSections, Offset);
 
-  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;
-    }
+  // Now that every section has been laid out we just need to compute the total
+  // file size. This might not be the same as the offset returned by
+  // LayoutSections, because we want to truncate the last segment to the end of
+  // its last section, to match GNU objcopy's behaviour.
+  TotalSize = 0;
+  for (const auto &Section : AllocatedSections) {
+    if (Section->Type != SHT_NOBITS)
+      TotalSize = std::max(TotalSize, Section->Offset + Section->Size);
   }
-  TotalSize = Offset;
 }
 
 namespace llvm {




More information about the llvm-commits mailing list