Index: lld/ELF/SyntheticSections.h =================================================================== --- lld/ELF/SyntheticSections.h +++ lld/ELF/SyntheticSections.h @@ -585,6 +585,7 @@ unsigned getNumSymbols() const { return symbols.size() + 1; } size_t getSymbolIndex(Symbol *sym); ArrayRef getSymbols() const { return symbols; } + void removeDeadLocalSymbols(); protected: void sortSymTabSymbols(); Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -2021,6 +2021,21 @@ } } +// Remove local symbols which belong to dead sections. +// The method is used to clean up the table after merging .ARM.exidx sections. +// The symbols are added earlier than the merging takes place, and it is not +// known at that time which of them will become dangling. Thus, we first add +// all the symbols, and eliminate them when the dead sections are known. +void SymbolTableBaseSection::removeDeadLocalSymbols() { + llvm::erase_if(symbols, [](const SymbolTableEntry &s) { + if (!s.sym->isLocal()) + return false; + auto *d = dyn_cast(s.sym); + assert(d); + return d->section && !d->section->repl->isLive(); + }); +} + // The ELF spec requires that all local symbols precede global symbols, so we // sort symbol entries in this function. (For .dynsym, we don't do that because // symbols for dynamic linking are inherently all globals.) @@ -3405,6 +3420,15 @@ } // Size includes Sentinel. size = offset + 8; + + // Mark merged input .ARM.exidx sections as dead. + if (config->mergeArmExidx) { + for (InputSection *isec : exidxSections) + if (!isec->getParent()) + isec->markDead(); + if (in.symTab) + in.symTab->removeDeadLocalSymbols(); + } } InputSection *ARMExidxSyntheticSection::getLinkOrderDep() const { Index: lld/test/ELF/arm-exidx-mapping-symbols.s =================================================================== --- /dev/null +++ lld/test/ELF/arm-exidx-mapping-symbols.s @@ -0,0 +1,41 @@ +// REQUIRES: arm +// Test that symbols which point to merged .ARM.exidx sections are eliminated. +// These symbols might be produced, for example, by GNU tools. + +// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t +// RUN: ld.lld %t -o %t2 +// RUN: llvm-readelf -s %t2 | FileCheck %s + +// CHECK-NOT: $d.exidx.bar + + .syntax unified + .section .text.foo,"axG",%progbits,foo,comdat +foo: + bx lr + +// GNU as adds a mapping symbol "$d" for .ARM.exidx sections it generate. +// llvm-mc does not do that, so reproduce such sections manually. +// Use an explicit name of the symbol for the test purpose and because all +// symbols in the assembly file should have unique names. + .section .ARM.exidx.text.foo,"ao?",%0x70000001,.text.foo +$d.exidx.foo: + .reloc 0, R_ARM_NONE, __aeabi_unwind_cpp_pr0 + .long .text.foo(PREL31) + .long 0x80b0b0b0 + + .section .text.bar,"axG",%progbits,bar,comdat +bar: + bx lr + +// This section is the same as the one for foo so it is a subject to merge. + .section .ARM.exidx.text.bar,"ao?",%0x70000001,.text.bar +$d.exidx.bar: + .reloc 0, R_ARM_NONE, __aeabi_unwind_cpp_pr0 + .long .text.bar(PREL31) + .long 0x80b0b0b0 + + .section .text.h + .global __aeabi_unwind_cpp_pr0 +__aeabi_unwind_cpp_pr0: + nop + bx lr