[ELF][ARM] Do not create .ARM.exidx sections for out of range inputs
authorPeter Smith <peter.smith@arm.com>
Sat, 2 May 2020 10:16:45 +0000 (11:16 +0100)
committerPeter Smith <peter.smith@arm.com>
Tue, 5 May 2020 08:59:45 +0000 (09:59 +0100)
A linker will create .ARM.exidx sections for InputSections that don't
have them. This can cause a relocation out of range error If the
InputSection happens to be extremely far away from the other sections.
This is often the case for the vector table on older ARM CPUs as the only
two places that the table can be placed is 0 or 0xffff0000. We fix this
by removing InputSections that need a linker generated .ARM.exidx
section if that would cause an error.

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

lld/ELF/SyntheticSections.cpp
lld/test/ELF/arm-exidx-range.s [new file with mode: 0644]

index e1322cd..bffea23 100644 (file)
@@ -3363,8 +3363,19 @@ void ARMExidxSyntheticSection::finalizeContents() {
   // ICF may remove executable InputSections and their dependent .ARM.exidx
   // section that we recorded earlier.
   auto isDiscarded = [](const InputSection *isec) { return !isec->isLive(); };
-  llvm::erase_if(executableSections, isDiscarded);
   llvm::erase_if(exidxSections, isDiscarded);
+  // We need to remove discarded InputSections and InputSections without
+  // .ARM.exidx sections that if we generated the .ARM.exidx it would be out
+  // of range.
+  auto isDiscardedOrOutOfRange = [this](InputSection *isec) {
+    if (!isec->isLive())
+      return true;
+    if (findExidxSection(isec))
+      return false;
+    int64_t off = static_cast<int64_t>(isec->getVA() - getVA());
+    return off != llvm::SignExtend64(off, 31);
+  };
+  llvm::erase_if(executableSections, isDiscardedOrOutOfRange);
 
   // Sort the executable sections that may or may not have associated
   // .ARM.exidx sections by order of ascending address. This requires the
diff --git a/lld/test/ELF/arm-exidx-range.s b/lld/test/ELF/arm-exidx-range.s
new file mode 100644 (file)
index 0000000..6976370
--- /dev/null
@@ -0,0 +1,35 @@
+// REQUIRES: arm
+// RUN: llvm-mc --arm-add-build-attributes --triple=armv7a-linux-gnueabihf -filetype=obj %s -o %t.o
+// RUN: echo "SECTIONS { \
+// RUN:         . = 0x80000000; \
+// RUN:         .text : { *(.text) } \
+// RUN:         .vectors 0xffff0000 : { *(.vectors) } \
+// RUN: } " > %t.script
+// RUN: ld.lld --script %t.script %t.o -o %t
+// RUN: llvm-readobj -x .ARM.exidx %t | FileCheck %s
+/// Adapted from Linux kernel linker script failing due to out of range
+/// relocation. The .vectors at 0xffff0000 is a common occurrence as the vector
+/// table can only be placed at either 0 or 0xffff0000 in older ARM CPUs.
+/// In the example the .vectors won't have an exception table so if LLD creates
+/// one then we'll get a relocation out of range error. Check that we don't
+/// synthesise a table entry or place a sentinel out of range.
+
+/// Expect only .ARM.exidx from _start and sentinel
+// CHECK: Hex dump of section '.ARM.exidx':
+// CHECK-NEXT: 0x80000000 10000000 01000000 0c000000 01000000
+// CHECK-NOT:  0x80000010
+
+ .text
+ .global _start
+ .type _start, %function
+_start:
+ .fnstart
+ bx lr
+ .cantunwind
+ .fnend
+
+ .section .vectors, "ax", %progbits
+ .global vecs
+ .type vecs, %function
+vecs:
+ bx lr