summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Config.h1
-rw-r--r--lld/ELF/Driver.cpp2
-rw-r--r--lld/ELF/Options.td6
-rw-r--r--lld/ELF/Writer.cpp82
-rw-r--r--lld/test/ELF/arm-exidx-dedup.s126
-rw-r--r--lld/test/ELF/arm-exidx-gc.s2
-rw-r--r--lld/test/ELF/arm-exidx-order.s4
-rw-r--r--lld/test/ELF/arm-exidx-sentinel-orphan.s2
-rw-r--r--lld/test/ELF/arm-static-defines.s2
-rw-r--r--lld/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s2
10 files changed, 223 insertions, 6 deletions
diff --git a/lld/ELF/Config.h b/lld/ELF/Config.h
index 7ceab51b3ef..74c325cb7cb 100644
--- a/lld/ELF/Config.h
+++ b/lld/ELF/Config.h
@@ -127,6 +127,7 @@ struct Configuration {
bool HasDynSymTab;
bool ICF;
bool ICFData;
+ bool MergeArmExidx;
bool MipsN32Abi = false;
bool NoGnuUnique;
bool NoUndefinedVersion;
diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp
index 5d6ef6df08a..2b6925031b0 100644
--- a/lld/ELF/Driver.cpp
+++ b/lld/ELF/Driver.cpp
@@ -626,6 +626,8 @@ void LinkerDriver::readConfigs(opt::InputArgList &Args) {
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);
diff --git a/lld/ELF/Options.td b/lld/ELF/Options.td
index 03155bfd106..20027e90aef 100644
--- a/lld/ELF/Options.td
+++ b/lld/ELF/Options.td
@@ -163,6 +163,9 @@ def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">;
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">;
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 2eece96d0ef..c7a3cae49ae 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1158,6 +1158,60 @@ static bool compareByFilePosition(InputSection *A, InputSection *B) {
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>::resolveShfLinkOrder() {
}
}
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());
}
}
diff --git a/lld/test/ELF/arm-exidx-dedup.s b/lld/test/ELF/arm-exidx-dedup.s
new file mode 100644
index 00000000000..1648f77152e
--- /dev/null
+++ b/lld/test/ELF/arm-exidx-dedup.s
@@ -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
diff --git a/lld/test/ELF/arm-exidx-gc.s b/lld/test/ELF/arm-exidx-gc.s
index 1336c256f7c..34bd9dbe37b 100644
--- a/lld/test/ELF/arm-exidx-gc.s
+++ b/lld/test/ELF/arm-exidx-gc.s
@@ -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
diff --git a/lld/test/ELF/arm-exidx-order.s b/lld/test/ELF/arm-exidx-order.s
index 951c71a4c33..c988ad8a2cf 100644
--- a/lld/test/ELF/arm-exidx-order.s
+++ b/lld/test/ELF/arm-exidx-order.s
@@ -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
diff --git a/lld/test/ELF/arm-exidx-sentinel-orphan.s b/lld/test/ELF/arm-exidx-sentinel-orphan.s
index c054fe940db..0e68c245dd1 100644
--- a/lld/test/ELF/arm-exidx-sentinel-orphan.s
+++ b/lld/test/ELF/arm-exidx-sentinel-orphan.s
@@ -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
diff --git a/lld/test/ELF/arm-static-defines.s b/lld/test/ELF/arm-static-defines.s
index 0012841fb32..815c20ca945 100644
--- a/lld/test/ELF/arm-static-defines.s
+++ b/lld/test/ELF/arm-static-defines.s
@@ -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
diff --git a/lld/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s b/lld/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s
index cbf295aa706..8cee22f7fed 100644
--- a/lld/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s
+++ b/lld/test/ELF/linkerscript/arm-exidx-sentinel-and-assignment.s
@@ -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
OpenPOWER on IntegriCloud