All references to interposable symbols can be redirected at runtime to point to a different symbol definition (with the same name). For example, if both dylib A and B define symbol _foo, and we load A before B at runtime, then all references to _foo within dylib B will point to the definition in dylib A. ld64 makes all extern symbols interposable when linking with `-flat_namespace`. TODO 1: Support `-interposable` and `-interposable_list`, which should just be a matter of parsing those CLI flags and setting the `Defined::interposable` bit. TODO 2: Set Reloc::FinalDefinitionInLinkageUnit correctly with this info (we are currently not setting it at all, so we're erring on the conservative side, but we should help the LTO backend generate more optimal code.) Reviewed By: modimo, MaskRay Differential Revision: https://reviews.llvm.org/D119294
107 lines
3.7 KiB
C++
107 lines
3.7 KiB
C++
//===- Symbols.cpp --------------------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "Symbols.h"
|
|
#include "InputFiles.h"
|
|
#include "SyntheticSections.h"
|
|
#include "lld/Common/Strings.h"
|
|
|
|
using namespace llvm;
|
|
using namespace lld;
|
|
using namespace lld::macho;
|
|
|
|
static_assert(sizeof(void *) != 8 || sizeof(Symbol) == 56,
|
|
"Try to minimize Symbol's size; we create many instances");
|
|
|
|
// The Microsoft ABI doesn't support using parent class tail padding for child
|
|
// members, hence the _MSC_VER check.
|
|
#if !defined(_MSC_VER)
|
|
static_assert(sizeof(void *) != 8 || sizeof(Defined) == 88,
|
|
"Try to minimize Defined's size; we create many instances");
|
|
#endif
|
|
|
|
static_assert(sizeof(SymbolUnion) == sizeof(Defined),
|
|
"Defined should be the largest Symbol kind");
|
|
|
|
std::string lld::toString(const Symbol &sym) {
|
|
return demangle(sym.getName(), config->demangle);
|
|
}
|
|
|
|
std::string lld::toMachOString(const object::Archive::Symbol &b) {
|
|
return demangle(b.getName(), config->demangle);
|
|
}
|
|
|
|
uint64_t Symbol::getStubVA() const { return in.stubs->getVA(stubsIndex); }
|
|
uint64_t Symbol::getGotVA() const { return in.got->getVA(gotIndex); }
|
|
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 canOverrideWeakDef, bool isWeakDefCanBeHidden,
|
|
bool interposable)
|
|
: Symbol(DefinedKind, name, file), overridesWeakDef(canOverrideWeakDef),
|
|
privateExtern(isPrivateExtern), includeInSymtab(true), thumb(isThumb),
|
|
referencedDynamically(isReferencedDynamically), noDeadStrip(noDeadStrip),
|
|
interposable(interposable), weakDefCanBeHidden(isWeakDefCanBeHidden),
|
|
weakDef(isWeakDef), external(isExternal), isec(isec), value(value),
|
|
size(size) {
|
|
if (isec) {
|
|
isec->symbols.push_back(this);
|
|
// Maintain sorted order.
|
|
for (auto it = isec->symbols.rbegin(), rend = isec->symbols.rend();
|
|
it != rend; ++it) {
|
|
auto next = std::next(it);
|
|
if (next == rend)
|
|
break;
|
|
if ((*it)->value < (*next)->value)
|
|
std::swap(*next, *it);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Defined::isTlv() const {
|
|
return !isAbsolute() && isThreadLocalVariables(isec->getFlags());
|
|
}
|
|
|
|
uint64_t Defined::getVA() const {
|
|
assert(isLive() && "this should only be called for live symbols");
|
|
|
|
if (isAbsolute())
|
|
return value;
|
|
|
|
if (!isec->isFinal) {
|
|
// A target arch that does not use thunks ought never ask for
|
|
// the address of a function that has not yet been finalized.
|
|
assert(target->usesThunks());
|
|
|
|
// ConcatOutputSection::finalize() can seek the address of a
|
|
// function before its address is assigned. The thunking algorithm
|
|
// knows that unfinalized functions will be out of range, so it is
|
|
// expedient to return a contrived out-of-range address.
|
|
return TargetInfo::outOfRangeVA;
|
|
}
|
|
return isec->getVA(value);
|
|
}
|
|
|
|
void Defined::canonicalize() {
|
|
if (unwindEntry)
|
|
unwindEntry = unwindEntry->canonical();
|
|
if (isec)
|
|
isec = isec->canonical();
|
|
}
|
|
|
|
uint64_t DylibSymbol::getVA() const {
|
|
return isInStubs() ? getStubVA() : Symbol::getVA();
|
|
}
|
|
|
|
void LazyArchive::fetchArchiveMember() { getFile()->fetch(sym); }
|