Skip to content

Commit cafb6cd

Browse files
committed
[lld/mac] Add some support for dynamic lookup symbols, and implement -U
Dynamic lookup symbols are symbols that work like dynamic symbols in ELF: They're not bound to a dylib like normal Mach-O twolevel lookup symbols, but they live in a global pool and dyld resolves them against exported symbols from all loaded dylibs. This adds support for dynamical lookup symbols to lld/mac. They are represented as DylibSymbols with file set to nullptr. This also uses this support to implement the -U flag, which makes a specific symbol that's undefined at the end of the link a dynamic lookup symbol. For -U, it'd be sufficient to just to a pass over remaining undefined symbols at the end of the link and to replace them with dynamic lookup symbols then. But I'd like to use this code to implement flat_namespace too, and that will require real support for resolving dynamic lookup symbols in SymbolTable. So this patch adds this now already. While writing tests for this, I noticed that we didn't set N_WEAK_DEF in the symbol table for DylibSymbols, so this fixes that too. Differential Revision: https://reviews.llvm.org/D97521
1 parent 30cd3dd commit cafb6cd

File tree

8 files changed

+127
-12
lines changed

8 files changed

+127
-12
lines changed

lld/MachO/Driver.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,10 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
758758
config->explicitUndefineds.push_back(symtab->addUndefined(
759759
arg->getValue(), /*file=*/nullptr, /*isWeakRef=*/false));
760760
}
761+
762+
for (auto *arg : args.filtered(OPT_U))
763+
symtab->addDynamicLookup(arg->getValue());
764+
761765
config->outputFile = args.getLastArgValue(OPT_o, "a.out");
762766
config->installName =
763767
args.getLastArgValue(OPT_install_name, config->outputFile);

lld/MachO/Options.td

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,6 @@ def u : Separate<["-"], "u">,
460460
def U : Separate<["-"], "U">,
461461
MetaVarName<"<symbol>">,
462462
HelpText<"Allow <symbol> to have no definition">,
463-
Flags<[HelpHidden]>,
464463
Group<grp_resolve>;
465464
def undefined : Separate<["-"], "undefined">,
466465
MetaVarName<"<treatment>">,

lld/MachO/SymbolTable.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,20 @@ Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef,
131131
}
132132
}
133133

134+
bool isDynamicLookup = file == nullptr;
134135
if (wasInserted || isa<Undefined>(s) ||
135-
(isa<DylibSymbol>(s) && !isWeakDef && s->isWeakDef()))
136+
(isa<DylibSymbol>(s) &&
137+
((!isWeakDef && s->isWeakDef()) ||
138+
(!isDynamicLookup && cast<DylibSymbol>(s)->isDynamicLookup()))))
136139
replaceSymbol<DylibSymbol>(s, file, name, isWeakDef, refState, isTlv);
137140

138141
return s;
139142
}
140143

144+
Symbol *SymbolTable::addDynamicLookup(StringRef name) {
145+
return addDylib(name, /*file=*/nullptr, /*isWeakDef=*/false, /*isTlv=*/false);
146+
}
147+
141148
Symbol *SymbolTable::addLazy(StringRef name, ArchiveFile *file,
142149
const object::Archive::Symbol &sym) {
143150
Symbol *s;
@@ -158,7 +165,7 @@ Symbol *SymbolTable::addDSOHandle(const MachHeaderSection *header) {
158165
if (!wasInserted) {
159166
// FIXME: Make every symbol (including absolute symbols) contain a
160167
// reference to their originating file, then add that file name to this
161-
// error message.
168+
// error message. dynamic_lookup symbols don't have an originating file.
162169
if (isa<Defined>(s))
163170
error("found defined symbol with illegal name " + DSOHandle::name);
164171
}

lld/MachO/SymbolTable.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class SymbolTable {
4343
bool isPrivateExtern);
4444

4545
Symbol *addDylib(StringRef name, DylibFile *file, bool isWeakDef, bool isTlv);
46+
Symbol *addDynamicLookup(StringRef name);
4647

4748
Symbol *addLazy(StringRef name, ArchiveFile *file,
4849
const llvm::object::Archive::Symbol &sym);

lld/MachO/Symbols.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,13 @@ class DylibSymbol : public Symbol {
194194
bool isWeakRef() const override { return refState == RefState::Weak; }
195195
bool isReferenced() const { return refState != RefState::Unreferenced; }
196196
bool isTlv() const override { return tlv; }
197+
bool isDynamicLookup() const { return file == nullptr; }
197198
bool hasStubsHelper() const { return stubsHelperIndex != UINT32_MAX; }
198-
DylibFile *getFile() const { return cast<DylibFile>(file); }
199+
200+
DylibFile *getFile() const {
201+
assert(!isDynamicLookup());
202+
return cast<DylibFile>(file);
203+
}
199204

200205
static bool classof(const Symbol *s) { return s->kind() == DylibKind; }
201206

lld/MachO/SyntheticSections.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -282,21 +282,25 @@ static void encodeBinding(const Symbol *sym, const OutputSection *osec,
282282
static void encodeDylibOrdinal(const DylibSymbol *dysym, Binding *lastBinding,
283283
raw_svector_ostream &os) {
284284
using namespace llvm::MachO;
285+
286+
int16_t ordinal = dysym->isDynamicLookup() ? BIND_SPECIAL_DYLIB_FLAT_LOOKUP
287+
: dysym->getFile()->ordinal;
288+
285289
if (lastBinding == nullptr ||
286-
lastBinding->ordinal != dysym->getFile()->ordinal) {
287-
if (dysym->getFile()->ordinal <= 0) {
290+
lastBinding->ordinal != ordinal) {
291+
if (ordinal <= 0) {
288292
os << static_cast<uint8_t>(
289293
BIND_OPCODE_SET_DYLIB_SPECIAL_IMM |
290-
(dysym->getFile()->ordinal & BIND_IMMEDIATE_MASK));
291-
} else if (dysym->getFile()->ordinal <= BIND_IMMEDIATE_MASK) {
294+
(ordinal & BIND_IMMEDIATE_MASK));
295+
} else if (ordinal <= BIND_IMMEDIATE_MASK) {
292296
os << static_cast<uint8_t>(BIND_OPCODE_SET_DYLIB_ORDINAL_IMM |
293-
dysym->getFile()->ordinal);
297+
ordinal);
294298
} else {
295299
os << static_cast<uint8_t>(BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB);
296-
encodeULEB128(dysym->getFile()->ordinal, os);
300+
encodeULEB128(ordinal, os);
297301
}
298302
if (lastBinding != nullptr)
299-
lastBinding->ordinal = dysym->getFile()->ordinal;
303+
lastBinding->ordinal = ordinal;
300304
}
301305
}
302306

@@ -815,13 +819,16 @@ void SymtabSection::writeTo(uint8_t *buf) const {
815819
nList->n_desc |= defined->isExternalWeakDef() ? MachO::N_WEAK_DEF : 0;
816820
} else if (auto *dysym = dyn_cast<DylibSymbol>(entry.sym)) {
817821
uint16_t n_desc = nList->n_desc;
818-
if (dysym->getFile()->isBundleLoader)
822+
if (dysym->isDynamicLookup())
823+
MachO::SET_LIBRARY_ORDINAL(n_desc, MachO::DYNAMIC_LOOKUP_ORDINAL);
824+
else if (dysym->getFile()->isBundleLoader)
819825
MachO::SET_LIBRARY_ORDINAL(n_desc, MachO::EXECUTABLE_ORDINAL);
820826
else
821827
MachO::SET_LIBRARY_ORDINAL(
822828
n_desc, static_cast<uint8_t>(dysym->getFile()->ordinal));
823829

824830
nList->n_type = MachO::N_EXT;
831+
n_desc |= dysym->isWeakDef() ? MachO::N_WEAK_DEF : 0;
825832
n_desc |= dysym->isWeakRef() ? MachO::N_WEAK_REF : 0;
826833
nList->n_desc = n_desc;
827834
}

lld/MachO/Writer.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,8 @@ void Writer::scanSymbols() {
472472
if (defined->overridesWeakDef)
473473
in.weakBinding->addNonWeakDefinition(defined);
474474
} else if (const auto *dysym = dyn_cast<DylibSymbol>(sym)) {
475+
if (dysym->isDynamicLookup())
476+
continue;
475477
dysym->getFile()->refState =
476478
std::max(dysym->getFile()->refState, dysym->refState);
477479
}

lld/test/MachO/U-dynamic-lookup.s

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# REQUIRES: x86
2+
# RUN: rm -rf %t
3+
# RUN: split-file %s %t
4+
5+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/foo.o %t/foo.s
6+
# RUN: %lld -dylib -o %t/foo.dylib %t/foo.o
7+
8+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/main.o %t/main.s
9+
10+
# _foo starts out as a (non-weak) dynamically looked up symbol and is merged
11+
# against the Undefined from foo.o. _bar isn't referenced in any object file,
12+
# but starts out as Undefined because of the -u flag. _baz isn't referenced
13+
# at all.
14+
# RUN: %lld -lSystem %t/main.o -U _foo -U _bar -u _bar -U _baz -o %t/out
15+
# RUN: llvm-objdump --macho --lazy-bind %t/out | FileCheck --check-prefix=DYNAMIC %s
16+
# RUN: llvm-nm -m %t/out | FileCheck --check-prefix=DYNAMICSYM %s
17+
18+
# Same thing should happen if _foo starts out as an Undefined.
19+
# `-U _foo` being passed twice shouldn't have an effect either.
20+
# RUN: %lld -lSystem %t/main.o -u _foo -U _foo -U _foo -u _bar -U _bar -U _baz -o %t/out
21+
# RUN: llvm-objdump --macho --lazy-bind %t/out | FileCheck --check-prefix=DYNAMIC %s
22+
# RUN: llvm-nm -m %t/out | FileCheck --check-prefix=DYNAMICSYM %s
23+
24+
# Unreferenced dynamic lookup symbols don't make it into the bind tables, but
25+
# they do make it into the symbol table in ld64 if they're an undefined from -u
26+
# for some reason. lld happens to have the same behavior when no explicit code
27+
# handles this case, so match ld64's behavior.
28+
29+
# DYNAMIC-NOT: _bar
30+
# DYNAMIC-NOT: _baz
31+
# DYNAMIC: flat-namespace _foo
32+
33+
# DYNAMICSYM: (undefined) external _bar (dynamically looked up)
34+
# DYNAMICSYM-NOT: (undefined) external _bar (dynamically looked up)
35+
# DYNAMICSYM-NEXT: (undefined) external _foo (dynamically looked up)
36+
37+
# Test with a Defined. Here, foo.o provides _foo and the symbol doesn't need
38+
# to be imported.
39+
# RUN: %lld -lSystem %t/main.o %t/foo.o -U _foo -o %t/out
40+
# RUN: llvm-objdump --macho --lazy-bind %t/out | FileCheck --check-prefix=NOTDYNAMIC %s
41+
42+
# NOTDYNAMIC-NOT: _foo
43+
44+
# Here, foo.dylib provides _foo and the symbol doesn't need to be imported
45+
# dynamically.
46+
# RUN: %lld -lSystem %t/main.o %t/foo.dylib -U _foo -o %t/out
47+
# RUN: llvm-objdump --macho --lazy-bind %t/out | FileCheck --check-prefix=TWOLEVEL %s
48+
# RUN: llvm-nm -m %t/out | FileCheck --check-prefix=TWOLEVELSYM %s
49+
50+
# TWOLEVEL: foo _foo
51+
# TWOLEVELSYM: (undefined) external _foo (from foo)
52+
53+
# Test resolving dynamic lookup symbol with weak defined.
54+
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos -o %t/weak-foo.o %t/weak-foo.s
55+
# RUN: %lld -dylib -o %t/weak-foo.dylib %t/weak-foo.o -U _foo
56+
# RUN: llvm-objdump --macho --bind --lazy-bind --weak-bind %t/weak-foo.dylib | FileCheck --check-prefix=WEAKDEF %s
57+
# RUN: llvm-nm -m %t/weak-foo.dylib | FileCheck --check-prefix=WEAKDEFSYM %s
58+
# WEAKDEF-NOT: _foo
59+
# WEAKDEFSYM: weak external _foo
60+
61+
# Same if foo.dylib provides _foo weakly, except that the symbol is weak then.
62+
# RUN: %lld -lSystem %t/main.o %t/weak-foo.dylib -U _foo -o %t/out
63+
# RUN: llvm-objdump --macho --bind --lazy-bind --weak-bind %t/out | FileCheck --check-prefix=TWOLEVELWEAK %s
64+
# RUN: llvm-nm -m %t/out | FileCheck --check-prefix=TWOLEVELWEAKSYM %s
65+
66+
# TWOLEVELWEAK-LABEL: Bind table:
67+
# TWOLEVELWEAK: __DATA __la_symbol_ptr 0x[[#%x,ADDR:]] pointer 0 weak-foo _foo
68+
# TWOLEVELWEAK-LABEL: Lazy bind table:
69+
# TWOLEVELWEAK-NOT: weak-foo _foo
70+
# TWOLEVELWEAK-LABEL: Weak bind table:
71+
# TWOLEVELWEAK: __DATA __la_symbol_ptr 0x[[#ADDR]] pointer 0 _foo
72+
73+
# TWOLEVELWEAKSYM: (undefined) weak external _foo (from weak-foo)
74+
75+
#--- foo.s
76+
.globl _foo
77+
_foo:
78+
ret
79+
80+
#--- weak-foo.s
81+
.globl _foo
82+
.weak_definition _foo
83+
_foo:
84+
ret
85+
86+
#--- main.s
87+
.globl _main
88+
_main:
89+
callq _foo
90+
ret

0 commit comments

Comments
 (0)