summaryrefslogtreecommitdiff
path: root/lld
diff options
context:
space:
mode:
authorPeter Smith <peter.smith@arm.com>2023-04-11 18:14:40 +0100
committerPeter Smith <peter.smith@arm.com>2023-04-14 10:09:46 +0100
commit7a2000ac5301dbdef8c3495fbeb49cab4ac439cb (patch)
treed1182d5be6bec55f518e480d3bce0f862275a5bf /lld
parent6b19efe252583a3db16112fc309b6560900f25e1 (diff)
downloadllvm-7a2000ac5301dbdef8c3495fbeb49cab4ac439cb.tar.gz
[LLD][ARM] Handle .ARM.exidx sections at non-zero output sec offset
Embedded systems that do not use an ELF loader locate the .ARM.exidx exception table via linker defined __exidx_start and __exidx_end rather than use the PT_ARM_EXIDX program header. This means that some linker scripts such as the picolibc C library's linker script, do not have the .ARM.exidx sections at offset 0 in the OutputSection. For example: .except_unordered : { . = ALIGN(8); PROVIDE(__exidx_start = .); *(.ARM.exidx*) PROVIDE(__exidx_end = .); } >flash AT>flash :text This is within the specification of Arm exception tables, and is handled correctly by ld.bfd. This patch has 2 parts. The first updates the writing of the data of the .ARM.exidx SyntheticSection to account for a non-zero OutputSection offset. The second part makes the PT_ARM_EXIDX program header generation a special case so that it covers only the SyntheticSection and not the parent OutputSection. While not strictly necessary for programs locating the exception tables via the symbols it may cause ELF utilities that locate the exception tables via the PT_ARM_EXIDX program header to fail. This does not seem to be the case for GNU and LLVM readelf which seems to look for the SHT_ARM_EXIDX section. Differential Revision: https://reviews.llvm.org/D148033
Diffstat (limited to 'lld')
-rw-r--r--lld/ELF/SyntheticSections.cpp7
-rw-r--r--lld/ELF/Writer.cpp17
-rw-r--r--lld/docs/ReleaseNotes.rst6
-rw-r--r--lld/test/ELF/arm-exidx-nonzero-offset.s173
4 files changed, 201 insertions, 2 deletions
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 1820e3cd9d0c..e95cca993bec 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -3496,7 +3496,7 @@ void ARMExidxSyntheticSection::finalizeContents() {
}
executableSections = std::move(selectedSections);
}
-
+ // offset is within the SyntheticSection.
size_t offset = 0;
size = 0;
for (InputSection *isec : executableSections) {
@@ -3538,7 +3538,10 @@ void ARMExidxSyntheticSection::writeTo(uint8_t *buf) {
dataOffset += 4)
write32(buf + offset + dataOffset,
read32(d->content().data() + dataOffset));
- target->relocateAlloc(*d, buf + d->outSecOff);
+ // Recalculate outSecOff as finalizeAddressDependentContent()
+ // may have altered syntheticSection outSecOff.
+ d->outSecOff = offset + outSecOff;
+ target->relocateAlloc(*d, buf + offset);
offset += d->getSize();
} else {
// A Linker generated CANTUNWIND section.
diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index f6b62b778b36..ddfda6ca34b8 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -2635,6 +2635,23 @@ template <class ELFT> void Writer<ELFT>::setPhdrs(Partition &part) {
OutputSection *first = p->firstSec;
OutputSection *last = p->lastSec;
+ // .ARM.exidx sections may not be within a single .ARM.exidx
+ // output section. We always want to describe just the
+ // SyntheticSection.
+ if (part.armExidx && p->p_type == PT_ARM_EXIDX) {
+ p->p_filesz = part.armExidx->getSize();
+ p->p_memsz = part.armExidx->getSize();
+ p->p_offset = first->offset + part.armExidx->outSecOff;
+ p->p_vaddr = first->addr + part.armExidx->outSecOff;
+ p->p_align = part.armExidx->addralign;
+ if (part.elfHeader)
+ p->p_offset -= part.elfHeader->getParent()->offset;
+
+ if (!p->hasLMA)
+ p->p_paddr = first->getLMA() + part.armExidx->outSecOff;
+ return;
+ }
+
if (first) {
p->p_filesz = last->offset - first->offset;
if (last->type != SHT_NOBITS)
diff --git a/lld/docs/ReleaseNotes.rst b/lld/docs/ReleaseNotes.rst
index 21dc9826db15..50c556bb8b31 100644
--- a/lld/docs/ReleaseNotes.rst
+++ b/lld/docs/ReleaseNotes.rst
@@ -41,3 +41,9 @@ MachO Improvements
WebAssembly Improvements
------------------------
+Fixes
+#####
+
+* Arm exception index tables (.ARM.exidx sections) are now ouptut
+ correctly when they are at a non zero offset within their output
+ section. (`D148033 <https://reviews.llvm.org/D148033>`_)
diff --git a/lld/test/ELF/arm-exidx-nonzero-offset.s b/lld/test/ELF/arm-exidx-nonzero-offset.s
new file mode 100644
index 000000000000..30b92f54f623
--- /dev/null
+++ b/lld/test/ELF/arm-exidx-nonzero-offset.s
@@ -0,0 +1,173 @@
+// REQUIRES: arm
+// RUN: rm -rf %t && split-file %s %t
+
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi --arm-add-build-attributes %t/a.s -o %t/a.o
+// RUN: ld.lld %t/a.o -T %t/exidx-non-zero-offset.t -o %t/non-zero
+// RUN: llvm-readelf --program-headers --unwind --symbols -x .exceptions %t/non-zero | FileCheck %s
+// RUN: ld.lld %t/a.o -T %t/exidx-zero-offset.t -o %t/zero
+// RUN: llvm-readelf --program-headers --unwind --symbols -x .exceptions %t/zero | FileCheck %s
+
+/// On platforms that load ELF files directly the ARM.exidx sections
+/// are located with the PT_ARM_EXIDX program header. This requires
+/// all .ARM.exidx input sections to be placed in a single
+/// .ARM.exidx output section. Embedded systems that do not load
+/// from an ELF file use the linker defined symbols __exidx_start and
+/// __exidx_stop. There is no requirement to place the .ARM.exidx
+/// input sections in their own output section. This test case checks
+/// that a .ARM.exidx synthetic section that isn't at a zero offset
+/// within the output section gets the correct offsets. We check
+/// this by checking that equal amounts of alignment padding
+/// inserted before and after the section start produce the same
+/// results.
+
+/// For the two linker scripts, one with the .ARM.exidx table
+/// at a zero offset within its output section and one with a
+/// non-zero offset, but both at the same file and address;
+/// the PT_ARM_EXIDX, symbols and table contents should be the
+/// same.
+// CHECK: EXIDX 0x010080 0x00000080 0x00000080 0x00028 0x00028 R 0x4
+
+/// unwind entries starting from the right address are identical.
+/// llvm-readelf does not seem to be able to detect Thumb functionNames
+/// whereas arm-none-eabi-readelf can. Used CHECK rather than CHECK-NEXT
+/// to cope with possible improvements llvm-readelf.
+// CHECK: FunctionAddress: 0x0
+// CHECK-NEXT: FunctionName: f1
+// CHECK-NEXT: Model: Compact (Inline)
+// CHECK-NEXT: PersonalityIndex: 0
+// CHECK-NEXT: Opcodes [
+// CHECK-NEXT: 0x97 ; vsp = r7
+// CHECK-NEXT: 0x84 0x08 ; pop {r7, lr}
+// CHECK-NEXT: ]
+// CHECK-NEXT: }
+// CHECK-NEXT: Entry {
+// CHECK-NEXT: FunctionAddress: 0x8
+// CHECK-NEXT: FunctionName: f2
+// CHECK-NEXT: Model: CantUnwind
+// CHECK-NEXT: }
+// CHECK-NEXT: Entry {
+// CHECK-NEXT: FunctionAddress: 0xE
+// CHECK: ExceptionHandlingTable: .ARM.extab
+// CHECK-NEXT: TableEntryAddress: 0xA8
+// CHECK-NEXT: Model: Generic
+// CHECK-NEXT: PersonalityRoutineAddress: 0x12
+// CHECK-NEXT: }
+// CHECK-NEXT: Entry {
+// CHECK-NEXT: FunctionAddress: 0x10
+// CHECK: Model: CantUnwind
+// CHECK-NEXT: }
+// CHECK-NEXT: Entry {
+// CHECK-NEXT: FunctionAddress: 0x14
+// CHECK-NEXT: FunctionName: __ARMv7ABSLongThunk_f3
+// CHECK-NEXT: Model: CantUnwind
+// CHECK-NEXT: }
+
+// CHECK: {{[0-9]+}}: 00000080 {{.*}} __exidx_start
+// CHECK-NEXT: {{[0-9]+}}: 000000a8 {{.*}} __exidx_end
+
+// CHECK: 0x00000080 80ffff7f 08849780 80ffff7f 01000000
+// CHECK-NEXT: 0x00000090 7effff7f 14000000 78ffff7f 01000000
+// CHECK-NEXT: 0x000000a0 74ffff7f 01000000
+
+//--- exidx-non-zero-offset.t
+SECTIONS {
+ .text : { *(.text .text.*) }
+ /* Addition of thunk in .text changes alignment padding */
+ .exceptions : {
+ /* Alignment padding within .exceptions */
+ . = ALIGN(128);
+ /* Embedded C libraries find exceptions via linker defined
+ symbols */
+ __exidx_start = .;
+ *(.ARM.exidx) ;
+ __exidx_end = .;
+ }
+ .ARM.extab : { *(.ARM.extab .ARM.extab.*) }
+}
+
+//--- exidx-zero-offset.t
+SECTIONS {
+ .text : { *(.text .text.*) }
+ /* Addition of thunk in .text changes alignment padding */
+ /* alignment padding before .exceptions starts */
+ .exceptions : ALIGN(128) {
+ /* Embedded C libraries find exceptions via linker defined
+ symbols */
+ __exidx_start = .;
+ *(.ARM.exidx) ;
+ __exidx_end = .;
+ }
+ .ARM.extab : { *(.ARM.extab .ARM.extab.*) }
+}
+
+//--- a.s
+.syntax unified
+
+/// Expect inline unwind instructions.
+.section .text.01, "ax", %progbits
+.arm
+.balign 4
+.global f1
+.type f1, %function
+f1:
+.fnstart
+/// provoke an interworking thunk.
+b f3
+bx lr
+.save {r7, lr}
+.setfp r7, sp, #0
+.fnend
+
+/// Expect no unwind information from assembler. The linker must
+/// synthesise an EXIDX_CANTUNWIND entry to prevent an exception
+/// thrown through f2 from matching against the unwind instructions
+/// for f1.
+.section .text.02, "ax", %progbits
+.global f2
+.type f2, %function
+.balign 4
+f2:
+bx lr
+
+/// Expect 1 EXIDX_CANTUNWIND entry that can be merged into the linker
+/// generated EXIDX_CANTUNWIND as if the assembler had generated it.
+.section .text.03, "ax",%progbits
+.global f3
+.type f3, %function
+.thumb
+.balign 2
+f3:
+.fnstart
+bx lr
+.cantunwind
+.fnend
+
+/// Expect a section with a reference to an .ARM.extab.
+.section .text.04, "ax",%progbits
+.global f4
+.balign 2
+.type f4, %function
+f4:
+.fnstart
+bx lr
+.personality __gxx_personality_v0
+.handlerdata
+.long 0
+.fnend
+
+
+/// Dummy implementation of personality routines to satisfy reference
+/// from exception tables, linker will generate EXIDX_CANTUNWIND.
+.section .text.__aeabi_unwind_cpp_pr0, "ax", %progbits
+.global __aeabi_unwind_cpp_pr0
+.type __aeabi_unwind_cpp_pr0, %function
+.balign 2
+__aeabi_unwind_cpp_pr0:
+bx lr
+
+.section .text.__gcc_personality_v0, "ax", %progbits
+.global __gxx_personality_v0
+.type __gcc_personality_v0, %function
+.balign 2
+__gxx_personality_v0:
+bx lr