[lld] r320803 - [ELF] Remove Duplicate .ARM.exidx sections

Peter Smith via llvm-commits llvm-commits at lists.llvm.org
Fri Dec 15 03:09:41 PST 2017


Author: psmith
Date: Fri Dec 15 03:09:41 2017
New Revision: 320803

URL: http://llvm.org/viewvc/llvm-project?rev=320803&view=rev
Log:
[ELF] Remove Duplicate .ARM.exidx sections

The ARM.exidx section contains a table of 8-byte entries with the first
word of each entry an offset to the function it describes and the second
word instructions for unwinding if an exception is thrown from that
function. The SHF_LINK_ORDER processing will order the table in ascending
order of the functions described by the exception table entries. As the
address range of an exception table entry is terminated by the next table
entry, it is possible to merge consecutive table entries that have
identical unwind instructions.

For this implementation we define a table entry to be identical if:
- Both entries are the special EXIDX_CANTUNWIND.
- Both entries have the same inline unwind instructions.
We do not attempt to establish if table entries that are references to
.ARM.extab sections are identical.

This implementation works at a granularity of a single .ARM.exidx
InputSection. If all entries in the InputSection are identical to the
previous table entry we can remove the InputSection. A more sophisticated
but more complex implementation would rewrite InputSection contents so that
duplicates within a .ARM.exidx InputSection can be merged.

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

Added:
    lld/trunk/test/ELF/arm-exidx-dedup.s
Modified:
    lld/trunk/ELF/Config.h
    lld/trunk/ELF/Driver.cpp
    lld/trunk/ELF/Options.td
    lld/trunk/ELF/Writer.cpp
    lld/trunk/test/ELF/arm-exidx-gc.s
    lld/trunk/test/ELF/arm-exidx-order.s
    lld/trunk/test/ELF/arm-exidx-sentinel-orphan.s
    lld/trunk/test/ELF/arm-static-defines.s
    lld/trunk/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s

Modified: lld/trunk/ELF/Config.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Config.h?rev=320803&r1=320802&r2=320803&view=diff
==============================================================================
--- lld/trunk/ELF/Config.h (original)
+++ lld/trunk/ELF/Config.h Fri Dec 15 03:09:41 2017
@@ -127,6 +127,7 @@ struct Configuration {
   bool HasDynSymTab;
   bool ICF;
   bool ICFData;
+  bool MergeArmExidx;
   bool MipsN32Abi = false;
   bool NoGnuUnique;
   bool NoUndefinedVersion;

Modified: lld/trunk/ELF/Driver.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Driver.cpp?rev=320803&r1=320802&r2=320803&view=diff
==============================================================================
--- lld/trunk/ELF/Driver.cpp (original)
+++ lld/trunk/ELF/Driver.cpp Fri Dec 15 03:09:41 2017
@@ -626,6 +626,8 @@ void LinkerDriver::readConfigs(opt::Inpu
   Config->LTOPartitions = args::getInteger(Args, OPT_lto_partitions, 1);
   Config->MapFile = Args.getLastArgValue(OPT_Map);
   Config->NoGnuUnique = Args.hasArg(OPT_no_gnu_unique);
+  Config->MergeArmExidx =
+      Args.hasFlag(OPT_merge_exidx_entries, OPT_no_merge_exidx_entries, true);
   Config->NoUndefinedVersion = Args.hasArg(OPT_no_undefined_version);
   Config->NoinhibitExec = Args.hasArg(OPT_noinhibit_exec);
   Config->Nostdlib = Args.hasArg(OPT_nostdlib);

Modified: lld/trunk/ELF/Options.td
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Options.td?rev=320803&r1=320802&r2=320803&view=diff
==============================================================================
--- lld/trunk/ELF/Options.td (original)
+++ lld/trunk/ELF/Options.td Fri Dec 15 03:09:41 2017
@@ -163,6 +163,9 @@ def m: JoinedOrSeparate<["-"], "m">, Hel
 
 defm Map: Eq<"Map">, HelpText<"Print a link map to the specified file">;
 
+def merge_exidx_entries: F<"merge-exidx-entries">,
+  HelpText<"Enable merging .ARM.exidx entries">;
+
 def nostdlib: F<"nostdlib">,
   HelpText<"Only search directories specified on the command line">;
 
@@ -196,6 +199,9 @@ def no_gdb_index: F<"no-gdb-index">,
 def no_gnu_unique: F<"no-gnu-unique">,
   HelpText<"Disable STB_GNU_UNIQUE symbol binding">;
 
+def no_merge_exidx_entries: F<"no-merge-exidx-entries">,
+  HelpText<"Disable merging .ARM.exidx entries">;
+
 def no_threads: F<"no-threads">,
   HelpText<"Do not run the linker multi-threaded">;
 

Modified: lld/trunk/ELF/Writer.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/ELF/Writer.cpp?rev=320803&r1=320802&r2=320803&view=diff
==============================================================================
--- lld/trunk/ELF/Writer.cpp (original)
+++ lld/trunk/ELF/Writer.cpp Fri Dec 15 03:09:41 2017
@@ -1158,6 +1158,60 @@ static bool compareByFilePosition(InputS
   return LA->OutSecOff < LB->OutSecOff;
 }
 
+// This function is used by the --merge-exidx-entries to detect duplicate
+// .ARM.exidx sections. It is Arm only.
+//
+// The .ARM.exidx section is of the form:
+// | PREL31 offset to function | Unwind instructions for function |
+// where the unwind instructions are either a small number of unwind
+// instructions inlined into the table entry, the special CANT_UNWIND value of
+// 0x1 or a PREL31 offset into a .ARM.extab Section that contains unwind
+// instructions.
+//
+// We return true if all the unwind instructions in the .ARM.exidx entries of
+// Cur can be merged into the last entry of Prev.
+static bool isDuplicateArmExidxSec(InputSection *Prev, InputSection *Cur) {
+
+  // References to .ARM.Extab Sections have bit 31 clear and are not the
+  // special EXIDX_CANTUNWIND bit-pattern.
+  auto IsExtabRef = [](uint32_t Unwind) {
+    return (Unwind & 0x80000000) == 0 && Unwind != 0x1;
+  };
+
+  struct ExidxEntry {
+    ulittle32_t Fn;
+    ulittle32_t Unwind;
+  };
+
+  // Get the last table Entry from the previous .ARM.exidx section.
+  const ExidxEntry &PrevEntry = *reinterpret_cast<const ExidxEntry *>(
+      Prev->Data.data() + Prev->getSize() - sizeof(ExidxEntry));
+  if (IsExtabRef(PrevEntry.Unwind))
+    return false;
+
+  // We consider the unwind instructions of an .ARM.exidx table entry
+  // a duplicate if the previous unwind instructions if:
+  // - Both are the special EXIDX_CANTUNWIND.
+  // - Both are the same inline unwind instructions.
+  // We do not attempt to follow and check links into .ARM.extab tables as
+  // consecutive identical entries are rare and the effort to check that they
+  // are identical is high.
+
+  if (isa<SyntheticSection>(Cur))
+    // Exidx sentinel section has implicit EXIDX_CANTUNWIND;
+    return PrevEntry.Unwind == 0x1;
+
+  ArrayRef<const ExidxEntry> Entries(
+      reinterpret_cast<const ExidxEntry *>(Cur->Data.data()),
+      Cur->getSize() / sizeof(ExidxEntry));
+  for (const ExidxEntry &Entry : Entries)
+    if (IsExtabRef(Entry.Unwind) || Entry.Unwind != PrevEntry.Unwind)
+      return false;
+  // All table entries in this .ARM.exidx Section can be merged into the
+  // previous Section.
+  return true;
+}
+
 template <class ELFT> void Writer<ELFT>::resolveShfLinkOrder() {
   for (OutputSection *Sec : OutputSections) {
     if (!(Sec->Flags & SHF_LINK_ORDER))
@@ -1176,8 +1230,36 @@ template <class ELFT> void Writer<ELFT>:
       }
     }
     std::stable_sort(Sections.begin(), Sections.end(), compareByFilePosition);
+
+    if (Config->MergeArmExidx && !Config->Relocatable &&
+        Config->EMachine == EM_ARM && Sec->Type == SHT_ARM_EXIDX) {
+      // The EHABI for the Arm Architecture permits consecutive identical
+      // table entries to be merged. We use a simple implementation that
+      // removes a .ARM.exidx Input Section if it can be merged into the
+      // previous one. This does not require any rewriting of InputSection
+      // contents but misses opportunities for fine grained deduplication where
+      // only a subset of the InputSection contents can be merged.
+      int Cur = 1;
+      int Prev = 0;
+      int N = Sections.size();
+      while (Cur < N) {
+        if (isDuplicateArmExidxSec(Sections[Prev], Sections[Cur]))
+          Sections[Cur] = nullptr;
+        else
+          Prev = Cur;
+        ++Cur;
+      }
+    }
+
     for (int I = 0, N = Sections.size(); I < N; ++I)
       *ScriptSections[I] = Sections[I];
+
+    // Remove the Sections we marked as duplicate earlier.
+    for (BaseCommand *Base : Sec->SectionCommands)
+      if (auto *ISD = dyn_cast<InputSectionDescription>(Base))
+        ISD->Sections.erase(
+            std::remove(ISD->Sections.begin(), ISD->Sections.end(), nullptr),
+            ISD->Sections.end());
   }
 }
 

Added: lld/trunk/test/ELF/arm-exidx-dedup.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/arm-exidx-dedup.s?rev=320803&view=auto
==============================================================================
--- lld/trunk/test/ELF/arm-exidx-dedup.s (added)
+++ lld/trunk/test/ELF/arm-exidx-dedup.s Fri Dec 15 03:09:41 2017
@@ -0,0 +1,126 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t --no-merge-exidx-entries -o %t2
+// RUN: llvm-objdump -s %t2 | FileCheck --check-prefix CHECK-DUPS %s
+// RUN: ld.lld %t -o %t3
+// RUN: llvm-objdump -s %t3 | FileCheck %s
+// REQUIRES: arm
+// Test that lld can at least remove duplicate .ARM.exidx sections. A more
+// fine grained implementation will be able to remove duplicate entries within
+// a .ARM.exidx section.
+
+// With duplicate entries
+// CHECK-DUPS: Contents of section .ARM.exidx:
+// CHECK-DUPS-NEXT:  100d4 2c0f0000 01000000 280f0000 01000000
+// CHECK-DUPS-NEXT:  100e4 240f0000 01000000 200f0000 01000000
+// CHECK-DUPS-NEXT:  100f4 1c0f0000 08849780 180f0000 08849780
+// CHECK-DUPS-NEXT:  10104 140f0000 08849780 100f0000 14000000
+// CHECK-DUPS-NEXT:  10114 0c0f0000 18000000 080f0000 01000000
+// CHECK-DUPS-NEXT: Contents of section .ARM.extab:
+
+// After duplicate entry removal
+// CHECK: Contents of section .ARM.exidx:
+// CHECK-NEXT:  100d4 2c0f0000 01000000 340f0000 08849780
+// CHECK-NEXT:  100e4 380f0000 14000000 340f0000 18000000
+// CHECK-NEXT:  100f4 300f0000 01000000
+// CHECK-NEXT: Contents of section .ARM.extab:
+        .syntax unified
+
+        // Expect 1 EXIDX_CANTUNWIND entry.
+        .section .text.00, "ax", %progbits
+        .globl _start
+_start:
+        .fnstart
+        bx lr
+        .cantunwind
+        .fnend
+
+        // Expect .ARM.exidx.text.01 to be identical to .ARM.exidx.text.00
+        .section .text.01, "ax", %progbits
+        .globl f1
+f1:
+        .fnstart
+        bx lr
+        .cantunwind
+        .fnend
+
+        // Expect 2 EXIDX_CANTUNWIND entries, these can be duplicated into
+        // .ARM.exid.text.00
+        .section .text.02, "ax", %progbits
+        .globl f2
+f2:
+        .fnstart
+        bx lr
+        .cantunwind
+        .fnend
+
+        .globl f3
+f3:
+        .fnstart
+        bx lr
+        .cantunwind
+        .fnend
+
+        // Expect inline unwind instructions, not a duplicate of previous entry.
+        .section .text.03, "ax", %progbits
+        .global f4
+f4:
+        .fnstart
+        bx lr
+        .save {r7, lr}
+        .setfp r7, sp, #0
+        .fnend
+
+        // Expect 2 inline unwind entries that are a duplicate of
+        // .ARM.exidx.text.03
+        .section .text.04, "ax", %progbits
+        .global f5
+f5:
+        .fnstart
+        bx lr
+        .save {r7, lr}
+        .setfp r7, sp, #0
+        .fnend
+
+        .global f6
+f6:
+        .fnstart
+        bx lr
+        .save {r7, lr}
+        .setfp r7, sp, #0
+        .fnend
+
+        // Expect a section with a reference to an .ARM.extab. Not a duplicate
+        // of previous inline table entry.
+        .section .text.05, "ax",%progbits
+        .global f7
+f7:
+        .fnstart
+        bx lr
+        .personality __gxx_personality_v0
+        .handlerdata
+        .long 0
+        .fnend
+
+        // Expect a reference to an identical .ARM.extab. We do not try to
+        // deduplicate references to .ARM.extab sections.
+        .section .text.06, "ax",%progbits
+        .global f8
+f8:
+        .fnstart
+        bx lr
+        .personality __gxx_personality_v0
+        .handlerdata
+        .long 0
+        .fnend
+
+ // Dummy implementation of personality routines to satisfy reference from
+ // exception tables
+        .section .text.__gcc_personality_v0, "ax", %progbits
+        .global __gxx_personality_v0
+__gxx_personality_v0:
+        bx lr
+
+        .section .text.__aeabi_unwind_cpp_pr0, "ax", %progbits
+        .global __aeabi_unwind_cpp_pr0
+__aeabi_unwind_cpp_pr0:
+        bx lr

Modified: lld/trunk/test/ELF/arm-exidx-gc.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/arm-exidx-gc.s?rev=320803&r1=320802&r2=320803&view=diff
==============================================================================
--- lld/trunk/test/ELF/arm-exidx-gc.s (original)
+++ lld/trunk/test/ELF/arm-exidx-gc.s Fri Dec 15 03:09:41 2017
@@ -1,5 +1,5 @@
 // RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
-// RUN: ld.lld %t -o %t2 --gc-sections 2>&1
+// RUN: ld.lld %t --no-merge-exidx-entries -o %t2 --gc-sections 2>&1
 // RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
 // RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-EXIDX %s
 // REQUIRES: arm

Modified: lld/trunk/test/ELF/arm-exidx-order.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/arm-exidx-order.s?rev=320803&r1=320802&r2=320803&view=diff
==============================================================================
--- lld/trunk/test/ELF/arm-exidx-order.s (original)
+++ lld/trunk/test/ELF/arm-exidx-order.s Fri Dec 15 03:09:41 2017
@@ -1,6 +1,6 @@
 // RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
 // RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %S/Inputs/arm-exidx-cantunwind.s -o %tcantunwind
-// RUN: ld.lld %t %tcantunwind -o %t2 2>&1
+// RUN: ld.lld --no-merge-exidx-entries %t %tcantunwind -o %t2 2>&1
 // RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
 // RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck -check-prefix=CHECK-EXIDX %s
 // RUN: llvm-readobj --program-headers --sections %t2 | FileCheck -check-prefix=CHECK-PT %s
@@ -8,7 +8,7 @@
 // RUN: echo "SECTIONS { \
 // RUN:          .text 0x11000 : { *(.text*) } \
 // RUN:          .ARM.exidx : { *(.ARM.exidx) } } " > %t.script
-// RUN: ld.lld --script %t.script %tcantunwind %t -o %t3 2>&1
+// RUN: ld.lld --no-merge-exidx-entries --script %t.script %tcantunwind %t -o %t3 2>&1
 // RUN: llvm-objdump -d -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-SCRIPT %s
 // RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t3 | FileCheck -check-prefix=CHECK-SCRIPT-EXIDX %s
 // REQUIRES: arm

Modified: lld/trunk/test/ELF/arm-exidx-sentinel-orphan.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/arm-exidx-sentinel-orphan.s?rev=320803&r1=320802&r2=320803&view=diff
==============================================================================
--- lld/trunk/test/ELF/arm-exidx-sentinel-orphan.s (original)
+++ lld/trunk/test/ELF/arm-exidx-sentinel-orphan.s Fri Dec 15 03:09:41 2017
@@ -4,7 +4,7 @@
 // RUN: echo "SECTIONS { \
 // RUN:          .text 0x11000 : { *(.text*) } \
 // RUN:          } " > %t.script
-// RUN: ld.lld --script %t.script %t -o %t2
+// RUN: ld.lld --no-merge-exidx-entries --script %t.script %t -o %t2
 // RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t2 | FileCheck %s
 // REQUIRES: arm
 

Modified: lld/trunk/test/ELF/arm-static-defines.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/arm-static-defines.s?rev=320803&r1=320802&r2=320803&view=diff
==============================================================================
--- lld/trunk/test/ELF/arm-static-defines.s (original)
+++ lld/trunk/test/ELF/arm-static-defines.s Fri Dec 15 03:09:41 2017
@@ -1,5 +1,5 @@
 // RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
-// RUN: ld.lld %t --static -o %t2 2>&1
+// RUN: ld.lld --no-merge-exidx-entries %t --static -o %t2 2>&1
 // RUN: llvm-readobj --symbols %t2 | FileCheck %s
 // REQUIRES: arm
 

Modified: lld/trunk/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s?rev=320803&r1=320802&r2=320803&view=diff
==============================================================================
--- lld/trunk/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s (original)
+++ lld/trunk/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s Fri Dec 15 03:09:41 2017
@@ -6,7 +6,7 @@
 # RUN:       }" > %t.script
 ## We used to crash if the last output section command for .ARM.exidx
 ## was anything but an input section description.
-# RUN: ld.lld -T %t.script %t.o -shared -o %t.so
+# RUN: ld.lld --no-merge-exidx-entries -T %t.script %t.o -shared -o %t.so
 # RUN: llvm-objdump -s -triple=armv7a-none-linux-gnueabi %t.so | FileCheck %s
 
  .syntax unified




More information about the llvm-commits mailing list