diff --git a/lld/MachO/UnwindInfoSection.h b/lld/MachO/UnwindInfoSection.h --- a/lld/MachO/UnwindInfoSection.h +++ b/lld/MachO/UnwindInfoSection.h @@ -23,7 +23,7 @@ // section entirely. bool isNeeded() const override { return !allEntriesAreOmitted; } void addSymbol(const Defined *); - virtual void prepareRelocations() = 0; + virtual void prepare() = 0; protected: UnwindInfoSection(); diff --git a/lld/MachO/UnwindInfoSection.cpp b/lld/MachO/UnwindInfoSection.cpp --- a/lld/MachO/UnwindInfoSection.cpp +++ b/lld/MachO/UnwindInfoSection.cpp @@ -150,7 +150,7 @@ public: UnwindInfoSectionImpl() : cuOffsets(target->wordSize) {} uint64_t getSize() const override { return unwindInfoSize; } - void prepareRelocations() override; + void prepare() override; void finalize() override; void writeTo(uint8_t *buf) const override; @@ -158,6 +158,7 @@ void prepareRelocations(ConcatInputSection *); void relocateCompactUnwind(std::vector &); void encodePersonalities(); + Symbol *canonicalizePersonality(Symbol *); uint64_t unwindInfoSize = 0; std::vector symbolsVec; @@ -210,14 +211,24 @@ } } -void UnwindInfoSectionImpl::prepareRelocations() { +void UnwindInfoSectionImpl::prepare() { // This iteration needs to be deterministic, since prepareRelocations may add // entries to the GOT. Hence the use of a MapVector for // UnwindInfoSection::symbols. for (const Defined *d : make_second_range(symbols)) - if (d->unwindEntry && - d->unwindEntry->getName() == section_names::compactUnwind) - prepareRelocations(d->unwindEntry); + if (d->unwindEntry) { + if (d->unwindEntry->getName() == section_names::compactUnwind) { + prepareRelocations(d->unwindEntry); + } else { + // We don't have to add entries to the GOT here because FDEs have + // explicit GOT relocations, so Writer::scanRelocations() will add those + // GOT entries. However, we still need to canonicalize the personality + // pointers (like prepareRelocations() does for CU entries) in order + // to avoid overflowing the 3-personality limit. + FDE &fde = cast(d->getFile())->fdes[d->unwindEntry]; + fde.personality = canonicalizePersonality(fde.personality); + } + } } // Compact unwind relocations have different semantics, so we handle them in a @@ -271,6 +282,7 @@ continue; } + // Similar to canonicalizePersonality(), but we also register a GOT entry. if (auto *defined = dyn_cast(s)) { // Check if we have created a synthetic symbol at the same address. Symbol *&personality = @@ -283,6 +295,7 @@ } continue; } + assert(isa(s)); in.got->addEntry(s); continue; @@ -312,6 +325,18 @@ } } +Symbol *UnwindInfoSectionImpl::canonicalizePersonality(Symbol *personality) { + if (auto *defined = dyn_cast_or_null(personality)) { + // Check if we have created a synthetic symbol at the same address. + Symbol *&synth = personalityTable[{defined->isec, defined->value}]; + if (synth == nullptr) + synth = defined; + else if (synth != defined) + return synth; + } + return personality; +} + // We need to apply the relocations to the pre-link compact unwind section // before converting it to post-link form. There should only be absolute // relocations here: since we are not emitting the pre-link CU section, there diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp --- a/lld/MachO/Writer.cpp +++ b/lld/MachO/Writer.cpp @@ -702,7 +702,7 @@ } } - in.unwindInfo->prepareRelocations(); + in.unwindInfo->prepare(); } static void addNonWeakDefinition(const Defined *defined) { diff --git a/lld/test/MachO/eh-frame-personality-dedup.s b/lld/test/MachO/eh-frame-personality-dedup.s new file mode 100644 --- /dev/null +++ b/lld/test/MachO/eh-frame-personality-dedup.s @@ -0,0 +1,43 @@ +# REQUIRES: x86 +# RUN: rm -rf %t; split-file %s %t +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin19.0.0 %t/eh-frame.s -o %t/eh-frame.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin19.0.0 %t/cu.s -o %t/cu.o +# RUN: %lld -dylib %t/cu.o %t/eh-frame.o -o %t/out + +## Sanity check: we want our input to contain a section (and not symbol) +## relocation for the personality reference. +# RUN: llvm-readobj --relocations %t/cu.o | FileCheck %s --check-prefix=SECT-RELOC +# SECT-RELOC: Section __compact_unwind { +# SECT-RELOC-NEXT: __text +# SECT-RELOC-NEXT: __text +# SECT-RELOC-NEXT: } + +## Verify that the personality referenced via a symbol reloc in eh-frame.s gets +## dedup'ed with the personality referenced via a section reloc in cu.s. +# RUN: llvm-objdump --macho --unwind-info %t/out | FileCheck %s +# CHECK: Personality functions: (count = 1) + +#--- eh-frame.s +_fun: + .cfi_startproc + .cfi_personality 155, _my_personality + ## cfi_escape cannot be encoded in compact unwind + .cfi_escape 0 + ret + .cfi_endproc + +.subsections_via_symbols + +#--- cu.s +.globl _my_personality +_fun: + .cfi_startproc + .cfi_personality 155, _my_personality + .cfi_def_cfa_offset 16 + ret + .cfi_endproc + +_my_personality: + nop + +.subsections_via_symbols