mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-02-18 16:58:23 +00:00
[lld-macho] Emit binding opcodes for defined symbols that override weak dysyms
These opcodes tell dyld to coalesce the overridden weak dysyms to this particular symbol definition. Reviewed By: #lld-macho, smeenai Differential Revision: https://reviews.llvm.org/D86575
This commit is contained in:
parent
3da2130e45
commit
2a38dba7dd
@ -40,6 +40,7 @@ Symbol *SymbolTable::addDefined(StringRef name, InputSection *isec,
|
||||
uint32_t value, bool isWeakDef) {
|
||||
Symbol *s;
|
||||
bool wasInserted;
|
||||
bool overridesWeakDef = false;
|
||||
std::tie(s, wasInserted) = insert(name);
|
||||
|
||||
if (!wasInserted) {
|
||||
@ -48,12 +49,16 @@ Symbol *SymbolTable::addDefined(StringRef name, InputSection *isec,
|
||||
return s;
|
||||
if (!defined->isWeakDef())
|
||||
error("duplicate symbol: " + name);
|
||||
} else if (auto *dysym = dyn_cast<DylibSymbol>(s)) {
|
||||
overridesWeakDef = !isWeakDef && dysym->isWeakDef();
|
||||
}
|
||||
// Defined symbols take priority over other types of symbols, so in case
|
||||
// of a name conflict, we fall through to the replaceSymbol() call below.
|
||||
}
|
||||
|
||||
replaceSymbol<Defined>(s, name, isec, value, isWeakDef, /*isExternal=*/true);
|
||||
Defined *defined = replaceSymbol<Defined>(s, name, isec, value, isWeakDef,
|
||||
/*isExternal=*/true);
|
||||
defined->overridesWeakDef = overridesWeakDef;
|
||||
return s;
|
||||
}
|
||||
|
||||
@ -75,6 +80,11 @@ Symbol *SymbolTable::addDylib(StringRef name, DylibFile *file, bool isWeakDef,
|
||||
bool wasInserted;
|
||||
std::tie(s, wasInserted) = insert(name);
|
||||
|
||||
if (!wasInserted && isWeakDef)
|
||||
if (auto *defined = dyn_cast<Defined>(s))
|
||||
if (!defined->isWeakDef())
|
||||
defined->overridesWeakDef = true;
|
||||
|
||||
if (wasInserted || isa<Undefined>(s) ||
|
||||
(isa<DylibSymbol>(s) && !isWeakDef && s->isWeakDef()))
|
||||
replaceSymbol<DylibSymbol>(s, file, name, isWeakDef, isTlv);
|
||||
|
@ -81,8 +81,8 @@ class Defined : public Symbol {
|
||||
public:
|
||||
Defined(StringRefZ name, InputSection *isec, uint32_t value, bool isWeakDef,
|
||||
bool isExternal)
|
||||
: Symbol(DefinedKind, name), isec(isec), value(value), weakDef(isWeakDef),
|
||||
external(isExternal) {}
|
||||
: Symbol(DefinedKind, name), isec(isec), value(value),
|
||||
overridesWeakDef(false), weakDef(isWeakDef), external(isExternal) {}
|
||||
|
||||
bool isWeakDef() const override { return weakDef; }
|
||||
|
||||
@ -101,9 +101,11 @@ public:
|
||||
InputSection *isec;
|
||||
uint32_t value;
|
||||
|
||||
bool overridesWeakDef : 1;
|
||||
|
||||
private:
|
||||
const bool weakDef;
|
||||
const bool external;
|
||||
const bool weakDef : 1;
|
||||
const bool external : 1;
|
||||
};
|
||||
|
||||
class Undefined : public Symbol {
|
||||
@ -182,14 +184,14 @@ union SymbolUnion {
|
||||
};
|
||||
|
||||
template <typename T, typename... ArgT>
|
||||
void replaceSymbol(Symbol *s, ArgT &&... arg) {
|
||||
T *replaceSymbol(Symbol *s, ArgT &&... arg) {
|
||||
static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
|
||||
static_assert(alignof(T) <= alignof(SymbolUnion),
|
||||
"SymbolUnion not aligned enough");
|
||||
assert(static_cast<Symbol *>(static_cast<T *>(nullptr)) == nullptr &&
|
||||
"Not a Symbol");
|
||||
|
||||
new (s) T(std::forward<ArgT>(arg)...);
|
||||
return new (s) T(std::forward<ArgT>(arg)...);
|
||||
}
|
||||
|
||||
} // namespace macho
|
||||
|
@ -63,12 +63,10 @@ void MachHeaderSection::writeTo(uint8_t *buf) const {
|
||||
if (config->outputType == MachO::MH_DYLIB && !config->hasReexports)
|
||||
hdr->flags |= MachO::MH_NO_REEXPORTED_DYLIBS;
|
||||
|
||||
// TODO: ld64 also sets this flag if we have defined a non-weak symbol that
|
||||
// overrides a weak symbol from an imported dylib.
|
||||
if (in.exports->hasWeakSymbol)
|
||||
if (in.exports->hasWeakSymbol || in.weakBinding->hasNonWeakDefinition())
|
||||
hdr->flags |= MachO::MH_WEAK_DEFINES;
|
||||
|
||||
if (in.exports->hasWeakSymbol || in.weakBinding->isNeeded())
|
||||
if (in.exports->hasWeakSymbol || in.weakBinding->hasEntry())
|
||||
hdr->flags |= MachO::MH_BINDS_TO_WEAK;
|
||||
|
||||
for (OutputSegment *seg : outputSegments) {
|
||||
@ -178,6 +176,14 @@ static void encodeDylibOrdinal(const DylibSymbol *dysym, Binding &lastBinding,
|
||||
}
|
||||
}
|
||||
|
||||
static void encodeWeakOverride(const Defined *defined,
|
||||
raw_svector_ostream &os) {
|
||||
using namespace llvm::MachO;
|
||||
os << static_cast<uint8_t>(BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM |
|
||||
BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION)
|
||||
<< defined->getName() << '\0';
|
||||
}
|
||||
|
||||
uint64_t BindingTarget::getVA() const {
|
||||
if (auto *isec = section.dyn_cast<const InputSection *>())
|
||||
return isec->getVA() + offset;
|
||||
@ -233,6 +239,9 @@ void WeakBindingSection::finalizeContents() {
|
||||
raw_svector_ostream os{contents};
|
||||
Binding lastBinding;
|
||||
|
||||
for (const Defined *defined : definitions)
|
||||
encodeWeakOverride(defined, os);
|
||||
|
||||
// Since bindings are delta-encoded, sorting them allows for a more compact
|
||||
// result.
|
||||
llvm::sort(bindings,
|
||||
@ -249,7 +258,7 @@ void WeakBindingSection::finalizeContents() {
|
||||
lastBinding, os);
|
||||
}
|
||||
}
|
||||
if (!bindings.empty())
|
||||
if (!bindings.empty() || !definitions.empty())
|
||||
os << static_cast<uint8_t>(MachO::BIND_OPCODE_DONE);
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ constexpr const char threadPtrs[] = "__thread_ptrs";
|
||||
|
||||
} // namespace section_names
|
||||
|
||||
class Defined;
|
||||
class DylibSymbol;
|
||||
class LoadCommand;
|
||||
|
||||
@ -189,9 +190,16 @@ struct WeakBindingEntry {
|
||||
: symbol(symbol), target(std::move(target)) {}
|
||||
};
|
||||
|
||||
// Stores bind opcodes for telling dyld which weak symbols to load. Note that
|
||||
// the bind opcodes will only refer to these symbols by name, but will not
|
||||
// specify which dylib to load them from.
|
||||
// Stores bind opcodes for telling dyld which weak symbols need coalescing.
|
||||
// There are two types of entries in this section:
|
||||
//
|
||||
// 1) Non-weak definitions: This is a symbol definition that weak symbols in
|
||||
// other dylibs should coalesce to.
|
||||
//
|
||||
// 2) Weak bindings: These tell dyld that a given symbol reference should
|
||||
// coalesce to a non-weak definition if one is found. Note that unlike in the
|
||||
// entries in the BindingSection, the bindings here only refer to these
|
||||
// symbols by name, but do not specify which dylib to load them from.
|
||||
class WeakBindingSection : public LinkEditSection {
|
||||
public:
|
||||
WeakBindingSection();
|
||||
@ -201,7 +209,9 @@ public:
|
||||
// offsets are recorded in the LC_DYLD_INFO_ONLY load command, instead of in
|
||||
// section headers.
|
||||
bool isHidden() const override { return true; }
|
||||
bool isNeeded() const override { return !bindings.empty(); }
|
||||
bool isNeeded() const override {
|
||||
return !bindings.empty() || !definitions.empty();
|
||||
}
|
||||
|
||||
void writeTo(uint8_t *buf) const override;
|
||||
|
||||
@ -210,8 +220,17 @@ public:
|
||||
bindings.emplace_back(symbol, BindingTarget(section, offset, addend));
|
||||
}
|
||||
|
||||
bool hasEntry() const { return !bindings.empty(); }
|
||||
|
||||
void addNonWeakDefinition(const Defined *defined) {
|
||||
definitions.emplace_back(defined);
|
||||
}
|
||||
|
||||
bool hasNonWeakDefinition() const { return !definitions.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<WeakBindingEntry> bindings;
|
||||
std::vector<const Defined *> definitions;
|
||||
SmallVector<char, 128> contents;
|
||||
};
|
||||
|
||||
|
@ -561,6 +561,11 @@ void Writer::run() {
|
||||
if (in.stubHelper->isNeeded())
|
||||
in.stubHelper->setup();
|
||||
|
||||
for (const macho::Symbol *sym : symtab->getSymbols())
|
||||
if (const auto *defined = dyn_cast<Defined>(sym))
|
||||
if (defined->overridesWeakDef)
|
||||
in.weakBinding->addNonWeakDefinition(defined);
|
||||
|
||||
// Sort and assign sections to their respective segments. No more sections nor
|
||||
// segments may be created after these methods run.
|
||||
createOutputSections();
|
||||
|
60
lld/test/MachO/nonweak-definition-override.s
Normal file
60
lld/test/MachO/nonweak-definition-override.s
Normal file
@ -0,0 +1,60 @@
|
||||
# REQUIRES: x86
|
||||
# RUN: split-file %s %t
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/libfoo.s -o %t/libfoo.o
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/nonweakdef.s -o %t/nonweakdef.o
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/weakdef.s -o %t/weakdef.o
|
||||
# RUN: lld -flavor darwinnew -syslibroot %S/Inputs/MacOSX.sdk -dylib %t/libfoo.o -o %t/libfoo.dylib
|
||||
|
||||
## Check that non-weak defined symbols override weak dylib symbols.
|
||||
# RUN: lld -flavor darwinnew -syslibroot %S/Inputs/MacOSX.sdk %t/nonweakdef.o -L%t -lfoo -o %t/nonweakdef -lSystem
|
||||
# RUN: llvm-objdump --macho --weak-bind %t/nonweakdef | FileCheck %s
|
||||
|
||||
## Test loading the dylib before the obj file.
|
||||
# RUN: lld -flavor darwinnew -syslibroot %S/Inputs/MacOSX.sdk -L%t -lfoo %t/nonweakdef.o -o %t/nonweakdef -lSystem
|
||||
# RUN: llvm-objdump --macho --weak-bind %t/nonweakdef | FileCheck %s
|
||||
|
||||
# CHECK: Weak bind table:
|
||||
# CHECK-NEXT: segment section address type addend symbol
|
||||
# CHECK-NEXT: strong _weak_in_dylib
|
||||
# CHECK-EMPTY:
|
||||
|
||||
## Check that weak defined symbols do not override weak dylib symbols.
|
||||
# RUN: lld -flavor darwinnew -syslibroot %S/Inputs/MacOSX.sdk %t/weakdef.o -L%t -lfoo -o %t/weakdef -lSystem
|
||||
# RUN: llvm-objdump --macho --weak-bind %t/weakdef | FileCheck %s --check-prefix=NO-WEAK-OVERRIDE
|
||||
|
||||
## Test loading the dylib before the obj file.
|
||||
# RUN: lld -flavor darwinnew -syslibroot %S/Inputs/MacOSX.sdk -L%t -lfoo %t/weakdef.o -o %t/weakdef -lSystem
|
||||
# RUN: llvm-objdump --macho --weak-bind %t/weakdef | FileCheck %s --check-prefix=NO-WEAK-OVERRIDE
|
||||
|
||||
# NO-WEAK-OVERRIDE: Weak bind table:
|
||||
# NO-WEAK-OVERRIDE-NEXT: segment section address type addend symbol
|
||||
# NO-WEAK-OVERRIDE-EMPTY:
|
||||
|
||||
#--- libfoo.s
|
||||
|
||||
.globl _weak_in_dylib, _nonweak_in_dylib
|
||||
.weak_definition _weak_in_dylib
|
||||
|
||||
_weak_in_dylib:
|
||||
_nonweak_in_dylib:
|
||||
|
||||
#--- nonweakdef.s
|
||||
|
||||
.globl _main, _weak_in_dylib, _nonweak_in_dylib
|
||||
|
||||
_weak_in_dylib:
|
||||
_nonweak_in_dylib:
|
||||
|
||||
_main:
|
||||
ret
|
||||
|
||||
#--- weakdef.s
|
||||
|
||||
.globl _main, _weak_in_dylib, _nonweak_in_dylib
|
||||
.weak_definition _weak_in_dylib, _nonweak_in_dylib
|
||||
|
||||
_weak_in_dylib:
|
||||
_nonweak_in_dylib:
|
||||
|
||||
_main:
|
||||
ret
|
@ -9,6 +9,10 @@
|
||||
# RUN: lld -flavor darwinnew -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem -L%t -lweak-defines -o %t/binds-to-weak %t/binds-to-weak.o
|
||||
# RUN: llvm-readobj --file-headers %t/binds-to-weak | FileCheck %s --check-prefix=WEAK-BINDS-ONLY
|
||||
|
||||
# RUN: llvm-mc -filetype=obj -triple=x86_64-apple-darwin %t/overrides-weak.s -o %t/overrides-weak.o
|
||||
# RUN: lld -flavor darwinnew -L%S/Inputs/MacOSX.sdk/usr/lib -lSystem -L%t -lweak-defines -o %t/overrides-weak %t/overrides-weak.o
|
||||
# RUN: llvm-readobj --file-headers %t/overrides-weak | FileCheck %s --check-prefix=WEAK-DEFINES-ONLY
|
||||
|
||||
# WEAK-DEFINES-AND-BINDS: MH_BINDS_TO_WEAK
|
||||
# WEAK-DEFINES-AND-BINDS: MH_WEAK_DEFINES
|
||||
|
||||
@ -16,6 +20,10 @@
|
||||
# WEAK-BINDS-ONLY: MH_BINDS_TO_WEAK
|
||||
# WEAK-BINDS-ONLY-NOT: MH_WEAK_DEFINES
|
||||
|
||||
# WEAK-DEFINES-ONLY-NOT: MH_BINDS_TO_WEAK
|
||||
# WEAK-DEFINES-ONLY: MH_WEAK_DEFINES
|
||||
# WEAK-DEFINES-ONLY-NOT: MH_BINDS_TO_WEAK
|
||||
|
||||
#--- libweak-defines.s
|
||||
|
||||
.globl _foo
|
||||
@ -33,3 +41,11 @@ _main:
|
||||
## Don't generate MH_WEAK_DEFINES for weak locals
|
||||
.weak_definition _weak_local
|
||||
_weak_local:
|
||||
|
||||
#--- overrides-weak.s
|
||||
|
||||
.globl _main, _foo
|
||||
_foo:
|
||||
|
||||
_main:
|
||||
ret
|
||||
|
Loading…
x
Reference in New Issue
Block a user