Skip to content

Commit 58e2dde

Browse files
authored
[lld:MachO] Allow independent override of weak symbols aliased via .set (#167825)
Currently, if multiple external weak symbols are defined at the same address in an object file (e.g., by using the .set assembler directive to alias them to a single weak variable), ld64.lld treats them as a single unit. When any one of these symbols is overridden by a strong definition, all of the original weak symbols resolve to the strong definition. This patch changes the behavior in `transplantSymbolsAtOffset`. When a weak symbol is being replaced by a strong one, only non-external (local) symbols at the same offset are moved to the new symbol's section. Other *external* symbols are no longer transplanted. This allows each external weak symbol to be overridden independently. This behavior is consistent with Apple's ld-classic, but diverges from ld-prime in one case, as noted on #167262 (this discrepancy has recently been reported to Apple). ### Backward Compatibility This change alters linker behavior for a specific scenario. The creation of multiple external weak symbols aliased to the same address via assembler directives is primarily an advanced technique. It's unlikely that existing builds rely on the current behavior of all aliases being overridden together. If there are concerns, this could be put behind a linker option, but the new default seems more correct, less surprising, and is consistent with ld-classic. ### Testing The new lit test `test/MachO/weak-alias-override.s` verifies this behavior using llvm-nm. Fixes #167262
1 parent 99120bb commit 58e2dde

File tree

2 files changed

+115
-17
lines changed

2 files changed

+115
-17
lines changed

lld/MachO/SymbolTable.cpp

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ struct DuplicateSymbolDiag {
6161
SmallVector<DuplicateSymbolDiag> dupSymDiags;
6262
} // namespace
6363

64-
// Move symbols at \p fromOff in \p fromIsec into \p toIsec, unless that symbol
65-
// is \p skip.
64+
// Move local symbols at \p fromOff in \p fromIsec into \p toIsec, unless that
65+
// symbol is \p skip, in which case we just remove it.
6666
static void transplantSymbolsAtOffset(InputSection *fromIsec,
6767
InputSection *toIsec, Defined *skip,
6868
uint64_t fromOff, uint64_t toOff) {
@@ -78,22 +78,23 @@ static void transplantSymbolsAtOffset(InputSection *fromIsec,
7878
auto insertIt = llvm::upper_bound(toIsec->symbols, toOff, symSucceedsOff);
7979
llvm::erase_if(fromIsec->symbols, [&](Symbol *s) {
8080
auto *d = cast<Defined>(s);
81-
if (d->value != fromOff)
81+
if (d == skip)
82+
return true;
83+
if (d->value != fromOff || d->isExternal())
8284
return false;
83-
if (d != skip) {
84-
// This repeated insertion will be quadratic unless insertIt is the end
85-
// iterator. However, that is typically the case for files that have
86-
// .subsections_via_symbols set.
87-
insertIt = toIsec->symbols.insert(insertIt, d);
88-
d->originalIsec = toIsec;
89-
d->value = toOff;
90-
// We don't want to have more than one unwindEntry at a given address, so
91-
// drop the redundant ones. We We can safely drop the unwindEntries of
92-
// the symbols in fromIsec since we will be adding another unwindEntry as
93-
// we finish parsing toIsec's file. (We can assume that toIsec has its
94-
// own unwindEntry because of the ODR.)
95-
d->originalUnwindEntry = nullptr;
96-
}
85+
86+
// This repeated insertion will be quadratic unless insertIt is the end
87+
// iterator. However, that is typically the case for files that have
88+
// .subsections_via_symbols set.
89+
insertIt = toIsec->symbols.insert(insertIt, d);
90+
d->originalIsec = toIsec;
91+
d->value = toOff;
92+
// We don't want to have more than one unwindEntry at a given address, so
93+
// drop the redundant ones. We can safely drop the unwindEntries of the
94+
// symbols in fromIsec since we will be adding another unwindEntry as we
95+
// finish parsing toIsec's file. (We can assume that toIsec has its own
96+
// unwindEntry because of the ODR.)
97+
d->originalUnwindEntry = nullptr;
9798
return true;
9899
});
99100
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# REQUIRES: x86
2+
# RUN: rm -rf %t; split-file %s %t
3+
# RUN: mkdir -p %t/bin
4+
5+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/weak.o %t/weak.s
6+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/strong_a.o %t/strong_a.s
7+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/strong_b.o %t/strong_b.s
8+
9+
# --- Test Case 1: No overrides
10+
# RUN: %lld %t/weak.o -o %t/bin/alone -e _s
11+
# RUN: llvm-nm -am %t/bin/alone | FileCheck --check-prefix=NM_ALONE %s
12+
13+
# NM_ALONE: [[#%x, P_ADDR:]] (__TEXT,__const) weak external _placeholder_int
14+
# NM_ALONE: [[#P_ADDR]] (__TEXT,__const) weak external _weak_a
15+
# NM_ALONE: [[#P_ADDR]] (__TEXT,__const) weak external _weak_b
16+
17+
# --- Test Case 2: Override weak_a
18+
# RUN: %lld %t/weak.o %t/strong_a.o -o %t/bin/with_a -e _s
19+
# RUN: llvm-nm -am %t/bin/with_a | FileCheck --check-prefix=NM_WITH_A %s
20+
# RUN: llvm-nm -am %t/bin/with_a | FileCheck --check-prefix=NM_WITH_A_BAD %s
21+
22+
# NM_WITH_A: [[#%x, P_ADDR:]] (__TEXT,__const) weak external _placeholder_int
23+
# NM_WITH_A: [[#%x, A_ADDR:]] (__TEXT,__const) external _strong_a
24+
# NM_WITH_A: [[#A_ADDR]] (__TEXT,__const) external _weak_a
25+
# NM_WITH_A: [[#P_ADDR]] (__TEXT,__const) weak external _weak_b
26+
27+
# --- Addresses of _placeholder_int and _strong_a must not match.
28+
# NM_WITH_A_BAD: [[#%x, P_ADDR:]] (__TEXT,__const) weak external _placeholder_int
29+
# NM_WITH_A_BAD-NOT: [[#P_ADDR]] (__TEXT,__const) external _strong_a
30+
31+
# --- Test Case 3: Override weak_b
32+
# RUN: %lld %t/weak.o %t/strong_b.o -o %t/bin/with_b -e _s
33+
# RUN: llvm-nm -am %t/bin/with_b | FileCheck --check-prefix=NM_WITH_B %s
34+
# RUN: llvm-nm -am %t/bin/with_b | FileCheck --check-prefix=NM_WITH_B_BAD %s
35+
36+
# NM_WITH_B: [[#%x, P_ADDR:]] (__TEXT,__const) weak external _placeholder_int
37+
# NM_WITH_B: [[#%x, B_ADDR:]] (__TEXT,__const) external _strong_b
38+
# NM_WITH_B: [[#P_ADDR]] (__TEXT,__const) weak external _weak_a
39+
# NM_WITH_B: [[#B_ADDR]] (__TEXT,__const) external _weak_b
40+
41+
# --- Addresses of _placeholder_int and _strong_a must not match.
42+
# NM_WITH_B_BAD: [[#%x, P_ADDR:]] (__TEXT,__const) weak external _placeholder_int
43+
# NM_WITH_B_BAD-NOT: [[#P_ADDR]] (__TEXT,__const) external _strong_b
44+
45+
# --- Test Case 4: Override weak_a and weak_b
46+
# RUN: %lld %t/weak.o %t/strong_a.o %t/strong_b.o -o %t/bin/with_ab -e _s
47+
# RUN: llvm-nm -am %t/bin/with_ab | FileCheck --check-prefix=NM_WITH_AB %s
48+
# RUN: llvm-nm -am %t/bin/with_ab | FileCheck --check-prefix=NM_WITH_AB_BAD %s
49+
50+
# NM_WITH_AB: [[#%x, P_ADDR:]] (__TEXT,__const) weak external _placeholder_int
51+
# NM_WITH_AB: [[#%x, A_ADDR:]] (__TEXT,__const) external _strong_a
52+
# NM_WITH_AB: [[#%x, B_ADDR:]] (__TEXT,__const) external _strong_b
53+
# NM_WITH_AB: [[#A_ADDR]] (__TEXT,__const) external _weak_a
54+
# NM_WITH_AB: [[#B_ADDR]] (__TEXT,__const) external _weak_b
55+
56+
# --- Addresses of _placeholder_int, _strong_a, and _strong_b must all be distinct
57+
# NM_WITH_AB_BAD: [[#%x, P_ADDR:]] (__TEXT,__const) weak external _placeholder_int
58+
# NM_WITH_AB_BAD-NOT: [[#P_ADDR]] (__TEXT,__const) external _strong_a
59+
# NM_WITH_AB_BAD-NOT: [[#P_ADDR]] (__TEXT,__const) external _strong_b
60+
61+
#--- weak.s
62+
.section __TEXT,__const
63+
.globl _placeholder_int
64+
.weak_definition _placeholder_int
65+
_placeholder_int:
66+
.long 0
67+
68+
.globl _weak_a
69+
.set _weak_a, _placeholder_int
70+
.weak_definition _weak_a
71+
72+
.globl _weak_b
73+
.set _weak_b, _placeholder_int
74+
.weak_definition _weak_b
75+
76+
.globl _s
77+
_s:
78+
.quad _weak_a
79+
.quad _weak_b
80+
81+
#--- strong_a.s
82+
.section __TEXT,__const
83+
.globl _strong_a
84+
_strong_a:
85+
.long 1
86+
87+
.globl _weak_a
88+
_weak_a = _strong_a
89+
90+
#--- strong_b.s
91+
.section __TEXT,__const
92+
.globl _strong_b
93+
_strong_b:
94+
.long 2
95+
96+
.globl _weak_b
97+
_weak_b = _strong_b

0 commit comments

Comments
 (0)