Skip to content

Commit 13f439a

Browse files
committed
[lld/mac] Implement support for private extern symbols
Private extern symbols are used for things scoped to the linkage unit. They cause duplicate symbol errors (so they're in the symbol table, unlike TU-scoped truly local symbols), but they don't make it into the export trie. They are created e.g. by compiling with -fvisibility=hidden. If two weak symbols have differing privateness, the combined symbol is non-private external. (Example: inline functions and some TUs that include the header defining it were built with -fvisibility-inlines-hidden and some weren't). A weak private external symbol implicitly has its "weak" dropped and behaves like a regular strong private external symbol: Weak is an export trie concept, and private symbols are not in the export trie. If a weak and a strong symbol have different privateness, the strong symbol wins. If two common symbols have differing privateness, the larger symbol wins. If they have the same size, the privateness of the symbol seen later during the link wins (!) -- this is a bit lame, but it matches ld64 and this behavior takes 2 lines less to implement than the less surprising "result is non-private external), so match ld64. (Example: `int a` in two .c files, both built with -fcommon, one built with -fvisibility=hidden and one without.) This also makes `__dyld_private` a true TU-local symbol, matching ld64. To make this work, make the `const char*` StringRefZ ctor to correctly set `size` (without this, writing the string table crashed when calling getName() on the __dyld_private symbol). Mention in CommonSymbol's comment that common symbols are now disabled by default in clang. Mention in -keep_private_externs's HelpText that the flag only has an effect with `-r` (which we don't implement yet -- so this patch here doesn't regress any behavior around -r + -keep_private_externs)). ld64 doesn't explicitly document it, but the commit text of http://reviews.llvm.org/rL216146 does, and ld64's OutputFile::buildSymbolTable() checks `_options.outputKind() == Options::kObjectFile` before calling `_options.keepPrivateExterns()` (the only reference to that function). Fixes PR48536. Differential Revision: https://reviews.llvm.org/D93609
1 parent b15ba2c commit 13f439a

12 files changed

+299
-62
lines changed

lld/MachO/Driver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,7 @@ static void replaceCommonSymbols() {
524524

525525
replaceSymbol<Defined>(sym, sym->getName(), isec, /*value=*/0,
526526
/*isWeakDef=*/false,
527-
/*isExternal=*/true);
527+
/*isExternal=*/true, common->privateExtern);
528528
}
529529
}
530530

lld/MachO/InputFiles.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -280,22 +280,33 @@ void ObjFile::parseRelocations(const section_64 &sec,
280280
static macho::Symbol *createDefined(const structs::nlist_64 &sym,
281281
StringRef name, InputSection *isec,
282282
uint32_t value) {
283-
if (sym.n_type & N_EXT)
284-
// Global defined symbol
285-
return symtab->addDefined(name, isec, value, sym.n_desc & N_WEAK_DEF);
286-
// Local defined symbol
283+
// Symbol scope is determined by sym.n_type & (N_EXT | N_PEXT):
284+
// N_EXT: Global symbols
285+
// N_EXT | N_PEXT: Linkage unit (think: dylib) scoped
286+
// N_PEXT: Does not occur in input files in practice,
287+
// a private extern must be external.
288+
// 0: Translation-unit scoped. These are not in the symbol table.
289+
290+
if (sym.n_type & (N_EXT | N_PEXT)) {
291+
assert((sym.n_type & N_EXT) && "invalid input");
292+
return symtab->addDefined(name, isec, value, sym.n_desc & N_WEAK_DEF,
293+
sym.n_type & N_PEXT);
294+
}
287295
return make<Defined>(name, isec, value, sym.n_desc & N_WEAK_DEF,
288-
/*isExternal=*/false);
296+
/*isExternal=*/false, /*isPrivateExtern=*/false);
289297
}
290298

291299
// Absolute symbols are defined symbols that do not have an associated
292300
// InputSection. They cannot be weak.
293301
static macho::Symbol *createAbsolute(const structs::nlist_64 &sym,
294302
StringRef name) {
295-
if (sym.n_type & N_EXT)
296-
return symtab->addDefined(name, nullptr, sym.n_value, /*isWeakDef=*/false);
303+
if (sym.n_type & (N_EXT | N_PEXT)) {
304+
assert((sym.n_type & N_EXT) && "invalid input");
305+
return symtab->addDefined(name, nullptr, sym.n_value, /*isWeakDef=*/false,
306+
sym.n_type & N_PEXT);
307+
}
297308
return make<Defined>(name, nullptr, sym.n_value, /*isWeakDef=*/false,
298-
/*isExternal=*/false);
309+
/*isExternal=*/false, /*isPrivateExtern=*/false);
299310
}
300311

301312
macho::Symbol *ObjFile::parseNonSectionSymbol(const structs::nlist_64 &sym,
@@ -306,7 +317,8 @@ macho::Symbol *ObjFile::parseNonSectionSymbol(const structs::nlist_64 &sym,
306317
return sym.n_value == 0
307318
? symtab->addUndefined(name, sym.n_desc & N_WEAK_REF)
308319
: symtab->addCommon(name, this, sym.n_value,
309-
1 << GET_COMM_ALIGN(sym.n_desc));
320+
1 << GET_COMM_ALIGN(sym.n_desc),
321+
sym.n_type & N_PEXT);
310322
case N_ABS:
311323
return createAbsolute(sym, name);
312324
case N_PBUD:

lld/MachO/Options.td

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ def bundle_loader : Separate<["-"], "bundle_loader">,
346346
def grp_object : OptionGroup<"object">, HelpText<"CREATING AN OBJECT FILE">;
347347

348348
def keep_private_externs : Flag<["-"], "keep_private_externs">,
349-
HelpText<"Do not convert private external symbols to static symbols">,
349+
HelpText<"Do not convert private external symbols to static symbols (only valid with -r)">,
350350
Flags<[HelpHidden]>,
351351
Group<grp_object>;
352352
def d : Flag<["-"], "d">,

lld/MachO/SymbolTable.cpp

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,22 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
3838
}
3939

4040
Symbol *SymbolTable::addDefined(StringRef name, InputSection *isec,
41-
uint32_t value, bool isWeakDef) {
41+
uint32_t value, bool isWeakDef,
42+
bool isPrivateExtern) {
4243
Symbol *s;
4344
bool wasInserted;
4445
bool overridesWeakDef = false;
4546
std::tie(s, wasInserted) = insert(name);
4647

4748
if (!wasInserted) {
4849
if (auto *defined = dyn_cast<Defined>(s)) {
49-
if (isWeakDef)
50+
if (isWeakDef) {
51+
// Both old and new symbol weak (e.g. inline function in two TUs):
52+
// If one of them isn't private extern, the merged symbol isn't.
53+
if (defined->isWeakDef())
54+
defined->privateExtern &= isPrivateExtern;
5055
return s;
56+
}
5157
if (!defined->isWeakDef())
5258
error("duplicate symbol: " + name);
5359
} else if (auto *dysym = dyn_cast<DylibSymbol>(s)) {
@@ -57,8 +63,9 @@ Symbol *SymbolTable::addDefined(StringRef name, InputSection *isec,
5763
// of a name conflict, we fall through to the replaceSymbol() call below.
5864
}
5965

60-
Defined *defined = replaceSymbol<Defined>(s, name, isec, value, isWeakDef,
61-
/*isExternal=*/true);
66+
Defined *defined =
67+
replaceSymbol<Defined>(s, name, isec, value, isWeakDef,
68+
/*isExternal=*/true, isPrivateExtern);
6269
defined->overridesWeakDef = overridesWeakDef;
6370
return s;
6471
}
@@ -82,7 +89,7 @@ Symbol *SymbolTable::addUndefined(StringRef name, bool isWeakRef) {
8289
}
8390

8491
Symbol *SymbolTable::addCommon(StringRef name, InputFile *file, uint64_t size,
85-
uint32_t align) {
92+
uint32_t align, bool isPrivateExtern) {
8693
Symbol *s;
8794
bool wasInserted;
8895
std::tie(s, wasInserted) = insert(name);
@@ -98,7 +105,7 @@ Symbol *SymbolTable::addCommon(StringRef name, InputFile *file, uint64_t size,
98105
// a name conflict, we fall through to the replaceSymbol() call below.
99106
}
100107

101-
replaceSymbol<CommonSymbol>(s, name, file, size, align);
108+
replaceSymbol<CommonSymbol>(s, name, file, size, align, isPrivateExtern);
102109
return s;
103110
}
104111

lld/MachO/SymbolTable.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,12 @@ class Symbol;
3333
class SymbolTable {
3434
public:
3535
Symbol *addDefined(StringRef name, InputSection *isec, uint32_t value,
36-
bool isWeakDef);
36+
bool isWeakDef, bool isPrivateExtern);
3737

3838
Symbol *addUndefined(StringRef name, bool isWeakRef);
3939

40-
Symbol *addCommon(StringRef name, InputFile *, uint64_t size, uint32_t align);
40+
Symbol *addCommon(StringRef name, InputFile *, uint64_t size, uint32_t align,
41+
bool isPrivateExtern);
4142

4243
Symbol *addDylib(StringRef name, DylibFile *file, bool isWeakDef, bool isTlv);
4344

lld/MachO/Symbols.h

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,11 @@ class Symbol {
4747

4848
Kind kind() const { return static_cast<Kind>(symbolKind); }
4949

50-
StringRef getName() const { return {name.data, name.size}; }
50+
StringRef getName() const {
51+
if (nameSize == (uint32_t)-1)
52+
nameSize = strlen(nameData);
53+
return {nameData, nameSize};
54+
}
5155

5256
virtual uint64_t getVA() const { return 0; }
5357

@@ -80,20 +84,26 @@ class Symbol {
8084
uint32_t symtabIndex = UINT32_MAX;
8185

8286
protected:
83-
Symbol(Kind k, StringRefZ name) : symbolKind(k), name(name) {}
87+
Symbol(Kind k, StringRefZ name)
88+
: symbolKind(k), nameData(name.data), nameSize(name.size) {}
8489

8590
Kind symbolKind;
86-
StringRefZ name;
91+
const char *nameData;
92+
mutable uint32_t nameSize;
8793
};
8894

8995
class Defined : public Symbol {
9096
public:
9197
Defined(StringRefZ name, InputSection *isec, uint32_t value, bool isWeakDef,
92-
bool isExternal)
98+
bool isExternal, bool isPrivateExtern)
9399
: Symbol(DefinedKind, name), isec(isec), value(value),
94-
overridesWeakDef(false), weakDef(isWeakDef), external(isExternal) {}
100+
overridesWeakDef(false), privateExtern(isPrivateExtern),
101+
weakDef(isWeakDef), external(isExternal) {}
95102

96103
bool isWeakDef() const override { return weakDef; }
104+
bool isExternalWeakDef() const {
105+
return isWeakDef() && isExternal() && !privateExtern;
106+
}
97107
bool isTlv() const override {
98108
return !isAbsolute() && isThreadLocalVariables(isec->flags);
99109
}
@@ -110,6 +120,7 @@ class Defined : public Symbol {
110120
uint32_t value;
111121

112122
bool overridesWeakDef : 1;
123+
bool privateExtern : 1;
113124

114125
private:
115126
const bool weakDef : 1;
@@ -148,14 +159,17 @@ class Undefined : public Symbol {
148159
//
149160
// The compiler creates common symbols when it sees tentative definitions.
150161
// (You can suppress this behavior and let the compiler create a regular
151-
// defined symbol by passing -fno-common.) When linking the final binary, if
152-
// there are remaining common symbols after name resolution is complete, the
153-
// linker converts them to regular defined symbols in a __common section.
162+
// defined symbol by passing -fno-common. -fno-common is the default in clang
163+
// as of LLVM 11.0.) When linking the final binary, if there are remaining
164+
// common symbols after name resolution is complete, the linker converts them
165+
// to regular defined symbols in a __common section.
154166
class CommonSymbol : public Symbol {
155167
public:
156-
CommonSymbol(StringRefZ name, InputFile *file, uint64_t size, uint32_t align)
168+
CommonSymbol(StringRefZ name, InputFile *file, uint64_t size, uint32_t align,
169+
bool isPrivateExtern)
157170
: Symbol(CommonKind, name), file(file), size(size),
158-
align(align != 1 ? align : llvm::PowerOf2Ceil(size)) {
171+
align(align != 1 ? align : llvm::PowerOf2Ceil(size)),
172+
privateExtern(isPrivateExtern) {
159173
// TODO: cap maximum alignment
160174
}
161175

@@ -164,6 +178,7 @@ class CommonSymbol : public Symbol {
164178
InputFile *const file;
165179
const uint64_t size;
166180
const uint32_t align;
181+
const bool privateExtern;
167182
};
168183

169184
class DylibSymbol : public Symbol {

lld/MachO/SyntheticSections.cpp

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -362,12 +362,10 @@ void WeakBindingSection::writeTo(uint8_t *buf) const {
362362
}
363363

364364
bool macho::needsBinding(const Symbol *sym) {
365-
if (isa<DylibSymbol>(sym)) {
365+
if (isa<DylibSymbol>(sym))
366366
return true;
367-
} else if (const auto *defined = dyn_cast<Defined>(sym)) {
368-
if (defined->isWeakDef() && defined->isExternal())
369-
return true;
370-
}
367+
if (const auto *defined = dyn_cast<Defined>(sym))
368+
return defined->isExternalWeakDef();
371369
return false;
372370
}
373371

@@ -380,7 +378,7 @@ void macho::addNonLazyBindingEntries(const Symbol *sym,
380378
in.weakBinding->addEntry(sym, section, offset, addend);
381379
} else if (auto *defined = dyn_cast<Defined>(sym)) {
382380
in.rebase->addEntry(section, offset);
383-
if (defined->isWeakDef() && defined->isExternal())
381+
if (defined->isExternalWeakDef())
384382
in.weakBinding->addEntry(sym, section, offset, addend);
385383
} else if (isa<DSOHandle>(sym)) {
386384
error("cannot bind to " + DSOHandle::name);
@@ -446,8 +444,10 @@ void StubHelperSection::setup() {
446444
in.got->addEntry(stubBinder);
447445

448446
inputSections.push_back(in.imageLoaderCache);
449-
symtab->addDefined("__dyld_private", in.imageLoaderCache, 0,
450-
/*isWeakDef=*/false);
447+
dyldPrivate =
448+
make<Defined>("__dyld_private", in.imageLoaderCache, 0,
449+
/*isWeakDef=*/false,
450+
/*isExternal=*/false, /*isPrivateExtern=*/false);
451451
}
452452

453453
ImageLoaderCacheSection::ImageLoaderCacheSection() {
@@ -555,7 +555,7 @@ void macho::prepareBranchTarget(Symbol *sym) {
555555
}
556556
}
557557
} else if (auto *defined = dyn_cast<Defined>(sym)) {
558-
if (defined->isWeakDef() && defined->isExternal()) {
558+
if (defined->isExternalWeakDef()) {
559559
if (in.stubs->addEntry(sym)) {
560560
in.rebase->addEntry(in.lazyPointers, sym->stubsIndex * WordSize);
561561
in.weakBinding->addEntry(sym, in.lazyPointers,
@@ -570,9 +570,10 @@ ExportSection::ExportSection()
570570

571571
void ExportSection::finalizeContents() {
572572
trieBuilder.setImageBase(in.header->addr);
573-
// TODO: We should check symbol visibility.
574573
for (const Symbol *sym : symtab->getSymbols()) {
575574
if (const auto *defined = dyn_cast<Defined>(sym)) {
575+
if (defined->privateExtern)
576+
continue;
576577
trieBuilder.addSymbol(*defined);
577578
hasWeakSymbol = hasWeakSymbol || sym->isWeakDef();
578579
}
@@ -710,6 +711,13 @@ void SymtabSection::finalizeContents() {
710711
}
711712
}
712713

714+
// __dyld_private is a local symbol too. It's linker-created and doesn't
715+
// exist in any object file.
716+
if (Defined* dyldPrivate = in.stubHelper->dyldPrivate) {
717+
uint32_t strx = stringTableSection.addString(dyldPrivate->getName());
718+
localSymbols.push_back({dyldPrivate, strx});
719+
}
720+
713721
for (Symbol *sym : symtab->getSymbols()) {
714722
uint32_t strx = stringTableSection.addString(sym->getName());
715723
if (auto *defined = dyn_cast<Defined>(sym)) {
@@ -752,18 +760,31 @@ void SymtabSection::writeTo(uint8_t *buf) const {
752760
nList->n_strx = entry.strx;
753761
// TODO populate n_desc with more flags
754762
if (auto *defined = dyn_cast<Defined>(entry.sym)) {
763+
uint8_t scope = 0;
764+
if (defined->privateExtern) {
765+
// Private external -- dylib scoped symbol.
766+
// Promote to non-external at link time.
767+
assert(defined->isExternal() && "invalid input file");
768+
scope = MachO::N_PEXT;
769+
} else if (defined->isExternal()) {
770+
// Normal global symbol.
771+
scope = MachO::N_EXT;
772+
} else {
773+
// TU-local symbol from localSymbols.
774+
scope = 0;
775+
}
776+
755777
if (defined->isAbsolute()) {
756-
nList->n_type = MachO::N_EXT | MachO::N_ABS;
778+
nList->n_type = scope | MachO::N_ABS;
757779
nList->n_sect = MachO::NO_SECT;
758780
nList->n_value = defined->value;
759781
} else {
760-
nList->n_type =
761-
(defined->isExternal() ? MachO::N_EXT : 0) | MachO::N_SECT;
782+
nList->n_type = scope | MachO::N_SECT;
762783
nList->n_sect = defined->isec->parent->index;
763784
// For the N_SECT symbol type, n_value is the address of the symbol
764785
nList->n_value = defined->getVA();
765786
}
766-
nList->n_desc |= defined->isWeakDef() ? MachO::N_WEAK_DEF : 0;
787+
nList->n_desc |= defined->isExternalWeakDef() ? MachO::N_WEAK_DEF : 0;
767788
} else if (auto *dysym = dyn_cast<DylibSymbol>(entry.sym)) {
768789
uint16_t n_desc = nList->n_desc;
769790
MachO::SET_LIBRARY_ORDINAL(n_desc, dysym->file->ordinal);

lld/MachO/SyntheticSections.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ class StubHelperSection : public SyntheticSection {
327327
void setup();
328328

329329
DylibSymbol *stubBinder = nullptr;
330+
Defined *dyldPrivate = nullptr;
330331
};
331332

332333
// This section contains space for just a single word, and will be used by dyld

lld/test/MachO/dylink-lazy.s

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
# RUN: llvm-objdump --macho --rebase %t/dylink-lazy-pie | FileCheck %s --check-prefix=PIE
2828

2929
# CHECK-LABEL: SYMBOL TABLE:
30-
# CHECK: {{0*}}[[#%x, IMGLOADER:]] {{.*}} __DATA,__data __dyld_private
30+
# CHECK: {{0*}}[[#%x, IMGLOADER:]] l {{.*}} __DATA,__data __dyld_private
3131

3232
# CHECK-LABEL: Disassembly of section __TEXT,__text:
3333
# CHECK: callq 0x[[#%x, HELLO_STUB:]]

0 commit comments

Comments
 (0)