[lld-macho] Better deduplication of personality pointers

{D95809} introduced a mechanism for synthetic symbol creation of personality
pointers. When multiple section relocations referred to the same personality
pointer, it would deduplicate them. However, it neglected to consider that we
could have symbol relocations that also refer to the same personality pointer.
This diff fixes it.

In practice, this mix of relocations arises when there is a statically-linked
personality routine that is referenced from multiple object files. Within the
same object file, it will be referred to via section relocations, but
(obviously) other object files will refer to it via symbol relocations. Failing
to deduplicate these references resulted in us going over the
3-personality-pointer limit when linking some larger applications.

Fixes llvm.org/PR48389.

Reviewed By: #lld-macho, thakis, alexshap

Differential Revision: https://reviews.llvm.org/D97245
This commit is contained in:
Jez Ng 2021-02-23 21:42:02 -05:00
parent 4752cdc9a2
commit 4a5e111aea
2 changed files with 73 additions and 31 deletions

View File

@ -100,6 +100,9 @@ bool UnwindInfoSection::isNeeded() const {
return (compactUnwindSection != nullptr);
}
SmallDenseMap<std::pair<InputSection *, uint64_t /* addend */>, macho::Symbol *>
personalityTable;
// Compact unwind relocations have different semantics, so we handle them in a
// separate code path from regular relocations. First, we do not wish to add
// rebase opcodes for __LD,__compact_unwind, because that section doesn't
@ -109,25 +112,39 @@ void macho::prepareCompactUnwind(InputSection *isec) {
assert(isec->segname == segment_names::ld &&
isec->name == section_names::compactUnwind);
DenseMap<std::pair<InputSection *, uint64_t /* addend */>, macho::Symbol *>
anonPersonalitySymbols;
for (Reloc &r : isec->relocs) {
// TODO: generalize for other archs
assert(r.type == X86_64_RELOC_UNSIGNED);
assert(target->hasAttr(r.type, RelocAttrBits::UNSIGNED));
if (r.offset % sizeof(CompactUnwindEntry64) !=
offsetof(struct CompactUnwindEntry64, personality))
continue;
if (auto *s = r.referent.dyn_cast<lld::macho::Symbol *>()) {
if (auto *undefined = dyn_cast<Undefined>(s))
if (auto *undefined = dyn_cast<Undefined>(s)) {
treatUndefinedSymbol(*undefined);
else
in.got->addEntry(s);
} else if (auto *referentIsec = r.referent.dyn_cast<InputSection *>()) {
continue;
}
if (auto *defined = dyn_cast<Defined>(s)) {
// Check if we have created a synthetic symbol at the same address.
macho::Symbol *&personality =
personalityTable[{defined->isec, defined->value}];
if (personality == nullptr) {
personality = defined;
in.got->addEntry(defined);
} else if (personality != defined) {
r.referent = personality;
}
continue;
}
assert(isa<DylibSymbol>(s));
in.got->addEntry(s);
continue;
}
if (auto *referentIsec = r.referent.dyn_cast<InputSection *>()) {
// Personality functions can be referenced via section relocations
// if they live in an object file (instead of a dylib). Create
// placeholder synthetic symbols for them in the GOT.
macho::Symbol *&s = anonPersonalitySymbols[{referentIsec, r.addend}];
// if they live in the same object file. Create placeholder synthetic
// symbols for them in the GOT.
macho::Symbol *&s = personalityTable[{referentIsec, r.addend}];
if (s == nullptr) {
s = make<Defined>("<internal>", nullptr, referentIsec, r.addend, false,
false, false);
@ -225,7 +242,6 @@ void UnwindInfoSection::finalize() {
size_t cuCount =
compactUnwindSection->getSize() / sizeof(CompactUnwindEntry64);
cuVector.resize(cuCount);
// Relocate all __LD,__compact_unwind entries
relocateCompactUnwind(compactUnwindSection, cuVector);
// Rather than sort & fold the 32-byte entries directly, we create a

View File

@ -1,39 +1,48 @@
# REQUIRES: x86
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin19.0.0 %s -o %t.o
# RUN: %lld -pie -lSystem -lc++ %t.o -o %t
# RUN: llvm-objdump --macho --unwind-info --syms --indirect-symbols --rebase %t | FileCheck %s
# RUN: rm -rf %t; split-file %s %t
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin19.0.0 %t/my_personality.s -o %t/my_personality.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin19.0.0 %t/main.s -o %t/main.o
# RUN: %lld -pie -lSystem -lc++ %t/my_personality.o %t/main.o -o %t/personality-first
# RUN: llvm-objdump --macho --unwind-info --syms --indirect-symbols --rebase %t/personality-first | FileCheck %s --check-prefixes=FIRST,CHECK
# RUN: %lld -pie -lSystem -lc++ %t/main.o %t/my_personality.o -o %t/personality-second
# RUN: llvm-objdump --macho --unwind-info --syms --indirect-symbols --rebase %t/personality-second | FileCheck %s --check-prefixes=SECOND,CHECK
# CHECK: Indirect symbols for (__DATA_CONST,__got)
# CHECK-NEXT: address index name
# CHECK-DAG: 0x[[#%x,GXX_PERSONALITY:]] [[#]] ___gxx_personality_v0
# CHECK-DAG: 0x[[#%x,MY_PERSONALITY:]] LOCAL
# FIRST: Indirect symbols for (__DATA_CONST,__got)
# FIRST-NEXT: address index name
# FIRST-DAG: 0x[[#%x,GXX_PERSONALITY:]] [[#]] ___gxx_personality_v0
# FIRST-DAG: 0x[[#%x,MY_PERSONALITY:]] LOCAL
# SECOND: Indirect symbols for (__DATA_CONST,__got)
# SECOND-NEXT: address index name
# SECOND-DAG: 0x[[#%x,GXX_PERSONALITY:]] [[#]] ___gxx_personality_v0
# SECOND-DAG: 0x[[#%x,MY_PERSONALITY:]] [[#]] _my_personality
# CHECK: SYMBOL TABLE:
# CHECK-DAG: [[#%x,MAIN:]] g F __TEXT,__text _main
# CHECK-DAG: [[#%x,FOO:]] g F __TEXT,__text _foo
# CHECK-DAG: [[#%x,FOO:]] l F __TEXT,__text _foo
# CHECK-DAG: [[#%x,EXCEPTION0:]] g O __TEXT,__gcc_except_tab _exception0
# CHECK-DAG: [[#%x,EXCEPTION1:]] g O __TEXT,__gcc_except_tab _exception1
# CHECK: Contents of __unwind_info section:
# CHECK: Personality functions: (count = 2)
# CHECK-NEXT: personality[1]: 0x{{0*}}[[#MY_PERSONALITY-0x100000000]]
# CHECK-NEXT: personality[2]: 0x{{0*}}[[#GXX_PERSONALITY-0x100000000]]
# CHECK-DAG: personality[{{[0-9]+}}]: 0x{{0*}}[[#MY_PERSONALITY-0x100000000]]
# CHECK-DAG: personality[{{[0-9]+}}]: 0x{{0*}}[[#GXX_PERSONALITY-0x100000000]]
# CHECK: LSDA descriptors:
# CHECK-NEXT: [0]: function offset=0x{{0*}}[[#FOO-0x100000000]], LSDA offset=0x{{0*}}[[#EXCEPTION0-0x100000000]]
# CHECK-NEXT: [1]: function offset=0x{{0*}}[[#MAIN-0x100000000]], LSDA offset=0x{{0*}}[[#EXCEPTION1-0x100000000]]
# CHECK-DAG: function offset=0x{{0*}}[[#FOO-0x100000000]], LSDA offset=0x{{0*}}[[#EXCEPTION0-0x100000000]]
# CHECK-DAG: function offset=0x{{0*}}[[#MAIN-0x100000000]], LSDA offset=0x{{0*}}[[#EXCEPTION1-0x100000000]]
## Check that we do not add rebase opcodes to the compact unwind section.
# CHECK: Rebase table:
# CHECK-NEXT: segment section address type
# CHECK-NEXT: __DATA_CONST __got 0x{{[0-9a-f]*}} pointer
# CHECK-NEXT: __DATA_CONST __got 0x{{[0-9a-f]*}} pointer
# CHECK-EMPTY:
.globl _main, _foo, _my_personality, _bar, _exception0, _exception1
# CHECK-NOT: __TEXT
#--- my_personality.s
.globl _my_personality, _exception0
.text
_foo:
.cfi_startproc
## This will generate a section relocation.
.cfi_personality 155, _my_personality
.cfi_lsda 16, _exception0
.cfi_def_cfa_offset 16
@ -49,6 +58,17 @@ _bar:
retq
.cfi_endproc
_my_personality:
retq
.section __TEXT,__gcc_except_tab
_exception0:
.space 1
#--- main.s
.globl _main, _my_personality, _exception1
.text
_main:
.cfi_startproc
.cfi_personality 155, ___gxx_personality_v0
@ -57,11 +77,17 @@ _main:
retq
.cfi_endproc
_my_personality:
_baz:
.cfi_startproc
## This will generate a symbol relocation. Check that we reuse the personality
## referenced by the section relocation in my_personality.s.
.cfi_personality 155, _my_personality
.cfi_lsda 16, _exception1
.cfi_def_cfa_offset 16
retq
.cfi_endproc
.section __TEXT,__gcc_except_tab
_exception0:
.space 1
_exception1:
.space 1