From 0ae7990b6071b941995183d872b928bba3a5d3b8 Mon Sep 17 00:00:00 2001 From: Peter Smith Date: Fri, 8 May 2020 13:19:12 +0100 Subject: [PATCH] [ELF][ARM] Support /DISCARD/ of subset of .ARM.exidx sections Both the .ARM.exidx and .eh_frame sections have a custom SyntheticSection that acts as a container for the InputSections. The InputSections are added to the SyntheticSection prior to /DISCARD/ which limits the affect a /DISCARD/ can have to the whole SyntheticSection. In the majority of cases this is sufficient as it is not common to discard subsets of the InputSections. The Linux kernel has one of these scripts which has something like: /DISCARD/ : { *(.ARM.exidx.exit.text) *(.ARM.extab.exit.text) ... } The .ARM.exidx.exit.text are not discarded because the InputSection has been transferred to the Synthetic Section. The *(.ARM.extab.exit.text) sections have not so they are discarded. When we come to write out the .ARM.exidx sections the dangling references from .ARM.exidx.exit.text to .ARM.extab.exit.text currently cause relocation out of range errors, but could as easily cause a fatal error message if we check for dangling references at relocation time. This patch attempts to respect the /DISCARD/ command by running it on the .ARM.exidx InputSections stored in the SyntheticSection. The .eh_frame is in theory affected by this problem, but I don't think that there is a dangling reference problem that can happen with these sections. Fixes remaining part of pr44824 Differential Revision: https://reviews.llvm.org/D79687 --- lld/ELF/LinkerScript.cpp | 24 ++++++++-- lld/ELF/LinkerScript.h | 5 +- lld/ELF/SyntheticSections.cpp | 2 +- .../ELF/arm-exidx-partial-discard-exidx.s | 48 +++++++++++++++++++ 4 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 lld/test/ELF/arm-exidx-partial-discard-exidx.s diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp index bb98c27d3253..77955d0e37a4 100644 --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -407,14 +407,15 @@ static void sortInputSections(MutableArrayRef vec, // Compute and remember which sections the InputSectionDescription matches. std::vector -LinkerScript::computeInputSections(const InputSectionDescription *cmd) { +LinkerScript::computeInputSections(const InputSectionDescription *cmd, + ArrayRef sections) { std::vector ret; // Collects all sections that satisfy constraints of Cmd. for (const SectionPattern &pat : cmd->sectionPatterns) { size_t sizeBefore = ret.size(); - for (InputSectionBase *sec : inputSections) { + for (InputSectionBase *sec : sections) { if (!sec->isLive() || sec->parent) continue; @@ -465,13 +466,29 @@ void LinkerScript::discard(InputSectionBase *s) { discard(ds); } +void LinkerScript::discardSynthetic(OutputSection &outCmd) { + for (Partition &part : partitions) { + if (!part.armExidx || !part.armExidx->isLive()) + continue; + std::vector secs(part.armExidx->exidxSections.begin(), + part.armExidx->exidxSections.end()); + for (BaseCommand *base : outCmd.sectionCommands) + if (auto *cmd = dyn_cast(base)) { + std::vector matches = + computeInputSections(cmd, secs); + for (InputSectionBase *s : matches) + discard(s); + } + } +} + std::vector LinkerScript::createInputSectionList(OutputSection &outCmd) { std::vector ret; for (BaseCommand *base : outCmd.sectionCommands) { if (auto *cmd = dyn_cast(base)) { - cmd->sectionBases = computeInputSections(cmd); + cmd->sectionBases = computeInputSections(cmd, inputSections); for (InputSectionBase *s : cmd->sectionBases) s->parent = &outCmd; ret.insert(ret.end(), cmd->sectionBases.begin(), cmd->sectionBases.end()); @@ -492,6 +509,7 @@ void LinkerScript::processSectionCommands() { if (sec->name == "/DISCARD/") { for (InputSectionBase *s : v) discard(s); + discardSynthetic(*sec); sec->sectionCommands.clear(); continue; } diff --git a/lld/ELF/LinkerScript.h b/lld/ELF/LinkerScript.h index b21a6f35170e..ec4fc22db486 100644 --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -245,10 +245,13 @@ class LinkerScript final { void expandMemoryRegions(uint64_t size); std::vector - computeInputSections(const InputSectionDescription *); + computeInputSections(const InputSectionDescription *, + ArrayRef); std::vector createInputSectionList(OutputSection &cmd); + void discardSynthetic(OutputSection &); + std::vector getPhdrIndices(OutputSection *sec); MemoryRegion *findMemoryRegion(OutputSection *sec); diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp index c60aaab63af7..bb8203f3bba0 100644 --- a/lld/ELF/SyntheticSections.cpp +++ b/lld/ELF/SyntheticSections.cpp @@ -3263,7 +3263,7 @@ ARMExidxSyntheticSection::ARMExidxSyntheticSection() static InputSection *findExidxSection(InputSection *isec) { for (InputSection *d : isec->dependentSections) - if (d->type == SHT_ARM_EXIDX) + if (d->type == SHT_ARM_EXIDX && d->isLive()) return d; return nullptr; } diff --git a/lld/test/ELF/arm-exidx-partial-discard-exidx.s b/lld/test/ELF/arm-exidx-partial-discard-exidx.s new file mode 100644 index 000000000000..ae855dcaef6e --- /dev/null +++ b/lld/test/ELF/arm-exidx-partial-discard-exidx.s @@ -0,0 +1,48 @@ +// REQUIRES: arm +// RUN: llvm-mc --arm-add-build-attributes --triple=armv7a-linux-gnueabihf -filetype=obj %s -o %t.o +// RUN: echo "SECTIONS { \ +// RUN: /DISCARD/ : { *(.ARM.exidx.exit.text) *(.ARM.extab.exit.text)} \ +// RUN: . = 0x90000000; \ +// RUN: .ARM.exidx : { *(.ARM.exidx) } \ +// RUN: .text : { *(.text) } \ +// RUN: .exit.text : { *(.exit.text) } \ +// RUN: .rodata : { *(.rodata) } \ +// RUN: } " > %t.script +// RUN: ld.lld --script %t.script %t.o -o %t +// RUN: llvm-readelf -x .ARM.exidx %t | FileCheck %s + +/// The linker script /DISCARDS/ the .ARM.exidx and .ARM.extab for the +/// .exit.text . If we do not discard both sections we will end up with +/// a dangling reference. We expect no linker error for an out of range +/// relocation/dangling reference and just a single .ARM.exidx entry +/// for _start and an entry for the terminating sentinel. + +// CHECK: Hex dump of section '.ARM.exidx': +// CHECK-NEXT: 0x90000000 10000000 01000000 10000000 01000000 +// CHECK-NOT: 0x90000010 + .text + .global _start + .type _start, %function +_start: + .fnstart + bx lr + .cantunwind + .fnend + + .section .exit.text, "ax", %progbits + .global exit_text + .type exit_text, %function +exit_text: + .fnstart + bx lr + .personality __gxx_personality_v0 + .handlerdata + .long 0 + .fnend + +/// Dummy definition for a reference from the personality routine created by +/// the assembler, use .data to avoid generating a cantunwind table. + .section .rodata + .global __aeabi_unwind_cpp_pr0 +__aeabi_unwind_cpp_pr0: + .word 0