diff --git a/lld/MachO/ConcatOutputSection.cpp b/lld/MachO/ConcatOutputSection.cpp --- a/lld/MachO/ConcatOutputSection.cpp +++ b/lld/MachO/ConcatOutputSection.cpp @@ -329,7 +329,7 @@ thunkName, /*file=*/nullptr, thunkInfo.isec, /*value=*/0, /*size=*/thunkSize, /*isWeakDef=*/false, /*isPrivateExtern=*/true, /*isThumb=*/false, /*isReferencedDynamically=*/false, - /*noDeadStrip=*/false); + /*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false); thunkInfo.sym->used = true; target->populateThunk(thunkInfo.isec, funcSym); finalizeOne(thunkInfo.isec); diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp --- a/lld/MachO/Driver.cpp +++ b/lld/MachO/Driver.cpp @@ -1472,8 +1472,16 @@ StringRef symbolName = defined->getName(); if (config->exportedSymbols.match(symbolName)) { if (defined->privateExtern) { - warn("cannot export hidden symbol " + symbolName + - "\n>>> defined in " + toString(defined->getFile())); + if (!defined->weakDefCanBeHidden) + warn("cannot export hidden symbol " + symbolName + + "\n>>> defined in " + toString(defined->getFile())); + else + // weak_def_can_be_hidden symbols behaves similarly to + // private_extern symbols in most cases, except for when + // it is explicitly exported. + // LD64 allows exporting the former, but not the latter. + // So we do the same here. + defined->privateExtern = false; } } else { defined->privateExtern = true; diff --git a/lld/MachO/InputFiles.cpp b/lld/MachO/InputFiles.cpp --- a/lld/MachO/InputFiles.cpp +++ b/lld/MachO/InputFiles.cpp @@ -556,15 +556,16 @@ // definition will continue to take priority if more private extern // definitions are encountered. With lld's semantics there's no observable // difference between a symbol that's isWeakDefCanBeHidden or one that's - // privateExtern -- neither makes it into the dynamic symbol table. So just - // promote isWeakDefCanBeHidden to isPrivateExtern here. + // privateExtern -- neither makes it into the dynamic symbol table, + // unless the autohide symbol is explicitly exported. + // So just promote isWeakDefCanBeHidden to isPrivateExtern here. if (isWeakDefCanBeHidden) isPrivateExtern = true; - return symtab->addDefined( name, isec->getFile(), isec, value, size, sym.n_desc & N_WEAK_DEF, isPrivateExtern, sym.n_desc & N_ARM_THUMB_DEF, - sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP); + sym.n_desc & REFERENCED_DYNAMICALLY, sym.n_desc & N_NO_DEAD_STRIP, + isWeakDefCanBeHidden); } assert(!isWeakDefCanBeHidden && "weak_def_can_be_hidden on already-hidden symbol?"); @@ -584,7 +585,8 @@ return symtab->addDefined( name, file, nullptr, sym.n_value, /*size=*/0, /*isWeakDef=*/false, sym.n_type & N_PEXT, sym.n_desc & N_ARM_THUMB_DEF, - /*isReferencedDynamically=*/false, sym.n_desc & N_NO_DEAD_STRIP); + /*isReferencedDynamically=*/false, sym.n_desc & N_NO_DEAD_STRIP, + /*isWeakDefCanBeHidden=*/false); } return make(name, file, nullptr, sym.n_value, /*size=*/0, /*isWeakDef=*/false, @@ -1407,7 +1409,8 @@ /*size=*/0, objSym.isWeak(), isPrivateExtern, /*isThumb=*/false, /*isReferencedDynamically=*/false, - /*noDeadStrip=*/false); + /*noDeadStrip=*/false, + /*isWeakDefCanBeHidden=*/false); } BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName, diff --git a/lld/MachO/MarkLive.cpp b/lld/MachO/MarkLive.cpp --- a/lld/MachO/MarkLive.cpp +++ b/lld/MachO/MarkLive.cpp @@ -67,7 +67,7 @@ // FIXME: Instead of doing this here, maybe the Driver code doing // the matching should add them to explicitUndefineds? Then the // explicitUndefineds code below would handle this automatically. - assert(!defined->privateExtern && + assert((!defined->privateExtern || defined->weakDefCanBeHidden) && "should have been rejected by driver"); addSym(defined); continue; diff --git a/lld/MachO/SymbolTable.h b/lld/MachO/SymbolTable.h --- a/lld/MachO/SymbolTable.h +++ b/lld/MachO/SymbolTable.h @@ -40,7 +40,8 @@ Defined *addDefined(StringRef name, InputFile *, InputSection *, uint64_t value, uint64_t size, bool isWeakDef, bool isPrivateExtern, bool isThumb, - bool isReferencedDynamically, bool noDeadStrip); + bool isReferencedDynamically, bool noDeadStrip, + bool isWeakDefCanBeHidden); Symbol *addUndefined(StringRef name, InputFile *, bool isWeakRef); diff --git a/lld/MachO/SymbolTable.cpp b/lld/MachO/SymbolTable.cpp --- a/lld/MachO/SymbolTable.cpp +++ b/lld/MachO/SymbolTable.cpp @@ -49,8 +49,8 @@ InputSection *isec, uint64_t value, uint64_t size, bool isWeakDef, bool isPrivateExtern, bool isThumb, - bool isReferencedDynamically, - bool noDeadStrip) { + bool isReferencedDynamically, bool noDeadStrip, + bool isWeakDefCanBeHidden) { Symbol *s; bool wasInserted; bool overridesWeakDef = false; @@ -96,6 +96,7 @@ s, name, file, isec, value, size, isWeakDef, /*isExternal=*/true, isPrivateExtern, isThumb, isReferencedDynamically, noDeadStrip); defined->overridesWeakDef = overridesWeakDef; + defined->weakDefCanBeHidden = isWeakDefCanBeHidden; return defined; } @@ -191,10 +192,11 @@ uint64_t value, bool isPrivateExtern, bool includeInSymtab, bool referencedDynamically) { - Defined *s = addDefined(name, nullptr, isec, value, /*size=*/0, - /*isWeakDef=*/false, isPrivateExtern, - /*isThumb=*/false, referencedDynamically, - /*noDeadStrip=*/false); + Defined *s = + addDefined(name, nullptr, isec, value, /*size=*/0, + /*isWeakDef=*/false, isPrivateExtern, + /*isThumb=*/false, referencedDynamically, + /*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false); s->includeInSymtab = includeInSymtab; return s; } diff --git a/lld/MachO/Symbols.h b/lld/MachO/Symbols.h --- a/lld/MachO/Symbols.h +++ b/lld/MachO/Symbols.h @@ -160,6 +160,8 @@ // to the output. bool noDeadStrip : 1; + bool weakDefCanBeHidden : 1; + private: const bool weakDef : 1; const bool external : 1; diff --git a/lld/test/MachO/weak-def-can-be-hidden.s b/lld/test/MachO/weak-def-can-be-hidden.s --- a/lld/test/MachO/weak-def-can-be-hidden.s +++ b/lld/test/MachO/weak-def-can-be-hidden.s @@ -42,6 +42,14 @@ # EXPORTS-LABEL: Exports trie: # EXPORTS-NOT: 0x{{0*}}[[#%X, FOO_ADDR]] _foo +## .weak_def_can_be_hidden symbols can still be exported, if explicitly +## specified via --exported_symbols. +# RUN: %lld -dylib -exported_symbol "_foo" %t/weak-autohide-foo.o -o %t/exp-autohide.dylib +# RUN: llvm-nm -g %t/exp-autohide.dylib | FileCheck %s --check-prefix=EXP-AUTOHIDE + +EXP-AUTOHIDE: T _foo + + ## nm output for .weak_def_can_be_hidden says "was a private external" even ## though it wasn't .private_extern: It was just .weak_def_can_be_hidden. ## This matches ld64.