[lld-macho] Allow exporting weak_def_can_be_hidden(AKA "autohide") symbols

autohide symbols behaves similarly to private_extern symbols.
However, LD64 allows exporting autohide symbols. LLD currently does not.
This patch allows LLD to export them.

Differential Revision: https://reviews.llvm.org/D113167
This commit is contained in:
Vy Nguyen 2021-11-08 19:50:34 -05:00
parent 4d8fff477e
commit 9b29dae3ca
8 changed files with 101 additions and 25 deletions

View File

@ -329,7 +329,7 @@ void ConcatOutputSection::finalize() {
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);

View File

@ -1467,8 +1467,16 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
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) {
// weak_def_can_be_hidden symbols behave similarly to
// private_extern symbols in most cases, except for when
// it is explicitly exported.
// The former can be exported but the latter cannot.
defined->privateExtern = false;
} else {
warn("cannot export hidden symbol " + symbolName +
"\n>>> defined in " + toString(defined->getFile()));
}
}
} else {
defined->privateExtern = true;

View File

@ -567,16 +567,22 @@ static macho::Symbol *createDefined(const NList &sym, StringRef name,
// with ld64's semantics, because it means the non-private-extern
// 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.
if (isWeakDefCanBeHidden)
// difference between a symbol that's isWeakDefCanBeHidden(autohide) or one
// that's privateExtern -- neither makes it into the dynamic symbol table,
// unless the autohide symbol is explicitly exported.
// But if a symbol is both privateExtern and autohide then it can't
// be exported.
// So we nullify the autohide flag when privateExtern is present
// and promote the symbol to privateExtern when it is not already.
if (isWeakDefCanBeHidden && isPrivateExtern)
isWeakDefCanBeHidden = false;
else 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?");
@ -596,7 +602,8 @@ static macho::Symbol *createAbsolute(const NList &sym, InputFile *file,
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<Defined>(name, file, nullptr, sym.n_value, /*size=*/0,
/*isWeakDef=*/false,
@ -1448,7 +1455,8 @@ static macho::Symbol *createBitcodeSymbol(const lto::InputFile::Symbol &objSym,
/*size=*/0, objSym.isWeak(), isPrivateExtern,
/*isThumb=*/false,
/*isReferencedDynamically=*/false,
/*noDeadStrip=*/false);
/*noDeadStrip=*/false,
/*isWeakDefCanBeHidden=*/false);
}
BitcodeFile::BitcodeFile(MemoryBufferRef mb, StringRef archiveName,

View File

@ -49,8 +49,8 @@ Defined *SymbolTable::addDefined(StringRef name, InputFile *file,
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;
@ -62,10 +62,10 @@ Defined *SymbolTable::addDefined(StringRef name, InputFile *file,
if (!wasInserted) {
if (auto *defined = dyn_cast<Defined>(s)) {
if (isWeakDef) {
// See further comment in createDefined() in InputFiles.cpp
if (defined->isWeakDef()) {
defined->privateExtern &= isPrivateExtern;
defined->weakDefCanBeHidden &= isWeakDefCanBeHidden;
defined->referencedDynamically |= isReferencedDynamically;
defined->noDeadStrip |= noDeadStrip;
}
@ -98,8 +98,8 @@ Defined *SymbolTable::addDefined(StringRef name, InputFile *file,
Defined *defined = replaceSymbol<Defined>(
s, name, file, isec, value, size, isWeakDef, /*isExternal=*/true,
isPrivateExtern, isThumb, isReferencedDynamically, noDeadStrip);
defined->overridesWeakDef = overridesWeakDef;
isPrivateExtern, isThumb, isReferencedDynamically, noDeadStrip,
overridesWeakDef, isWeakDefCanBeHidden);
return defined;
}
@ -195,10 +195,11 @@ Defined *SymbolTable::addSynthetic(StringRef name, InputSection *isec,
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;
}

View File

@ -40,7 +40,8 @@ public:
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);

View File

@ -34,12 +34,14 @@ uint64_t Symbol::getTlvVA() const { return in.tlvPointers->getVA(gotIndex); }
Defined::Defined(StringRefZ name, InputFile *file, InputSection *isec,
uint64_t value, uint64_t size, bool isWeakDef, bool isExternal,
bool isPrivateExtern, bool isThumb,
bool isReferencedDynamically, bool noDeadStrip)
bool isReferencedDynamically, bool noDeadStrip,
bool canOverrideWeakDef, bool isWeakDefCanBeHidden)
: Symbol(DefinedKind, name, file), isec(isec), value(value), size(size),
overridesWeakDef(false), privateExtern(isPrivateExtern),
overridesWeakDef(canOverrideWeakDef), privateExtern(isPrivateExtern),
includeInSymtab(true), thumb(isThumb),
referencedDynamically(isReferencedDynamically), noDeadStrip(noDeadStrip),
weakDef(isWeakDef), external(isExternal) {
weakDef(isWeakDef), external(isExternal),
weakDefCanBeHidden(isWeakDefCanBeHidden) {
if (isec) {
isec->symbols.push_back(this);
// Maintain sorted order.

View File

@ -113,7 +113,8 @@ class Defined : public Symbol {
public:
Defined(StringRefZ name, InputFile *file, InputSection *isec, uint64_t value,
uint64_t size, bool isWeakDef, bool isExternal, bool isPrivateExtern,
bool isThumb, bool isReferencedDynamically, bool noDeadStrip);
bool isThumb, bool isReferencedDynamically, bool noDeadStrip,
bool canOverrideWeakDef = false, bool isWeakDefCanBeHidden = false);
bool isWeakDef() const override { return weakDef; }
bool isExternalWeakDef() const {
@ -160,6 +161,8 @@ public:
// to the output.
bool noDeadStrip : 1;
bool weakDefCanBeHidden : 1;
private:
const bool weakDef : 1;
const bool external : 1;

View File

@ -119,6 +119,33 @@
# GLOBBY-DAG: globby_also
# GLOBBY-NOT: literal_only
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
# RUN: %t/autohide.s -o %t/autohide.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
# RUN: %t/autohide-private-extern.s -o %t/autohide-private-extern.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
# RUN: %t/glob-private-extern.s -o %t/glob-private-extern.o
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-macos \
# RUN: %t/weak-private-extern.s -o %t/weak-private-extern.o
## Test that we can export the autohide symbol but not when it's also
## private-extern
# RUN: %lld -dylib -exported_symbol "_foo" %t/autohide.o -o %t/exp-autohide.dylib
# RUN: llvm-nm -g %t/exp-autohide.dylib | FileCheck %s --check-prefix=EXP-AUTOHIDE
# RUN: not %lld -dylib -exported_symbol "_foo" %t/autohide-private-extern.o \
# RUN: -o /dev/null 2>&1 | FileCheck %s --check-prefix=AUTOHIDE-PRIVATE
# RUN: not %lld -dylib -exported_symbol "_foo" %t/autohide.o \
# RUN: %t/glob-private-extern.o -o /dev/null 2>&1 | \
# RUN: FileCheck %s --check-prefix=AUTOHIDE-PRIVATE
# RUN: not %lld -dylib -exported_symbol "_foo" %t/autohide.o \
# RUN: %t/weak-private-extern.o -o /dev/null 2>&1 | \
# RUN: FileCheck %s --check-prefix=AUTOHIDE-PRIVATE
# EXP-AUTOHIDE: T _foo
# AUTOHIDE-PRIVATE: error: cannot export hidden symbol _foo
#--- default.s
.globl _keep_globl, _hide_globl
@ -164,3 +191,29 @@ globby_also:
l?ter[aeiou]l_*[^y] # comment
*gl?bby_*
#--- autohide.s
.globl _foo
.weak_def_can_be_hidden _foo
_foo:
retq
#--- autohide-private-extern.s
.globl _foo
.weak_def_can_be_hidden _foo
.private_extern _foo
_foo:
retq
#--- glob-private-extern.s
.global _foo
.private_extern _foo
_foo:
retq
#--- weak-private-extern.s
.global _foo
.weak_definition _foo
.private_extern _foo
_foo:
retq