#ifndef LLD_MACHO_SYMBOLS_H
#define LLD_MACHO_SYMBOLS_H
#include "Config.h"
#include "InputFiles.h"
#include "Target.h"
#include "llvm/Object/Archive.h"
#include "llvm/Support/MathExtras.h"
namespace lld {
namespace macho {
class MachHeaderSection;
struct StringRefZ {
StringRefZ(const char *s) : data(s), size(-1) {}
StringRefZ(StringRef s) : data(s.data()), size(s.size()) {}
const char *data;
const uint32_t size;
};
class Symbol {
public:
enum Kind {
DefinedKind,
UndefinedKind,
CommonKind,
DylibKind,
LazyArchiveKind,
LazyObjectKind,
AliasKind,
};
virtual ~Symbol() {}
Kind kind() const { return symbolKind; }
StringRef getName() const {
if (nameSize == (uint32_t)-1)
nameSize = strlen(nameData);
return {nameData, nameSize};
}
bool isLive() const { return used; }
bool isLazy() const {
return symbolKind == LazyArchiveKind || symbolKind == LazyObjectKind;
}
virtual uint64_t getVA() const { return 0; }
virtual bool isWeakDef() const { return false; }
virtual bool isWeakRef() const { return false; }
virtual bool isTlv() const { return false; }
bool isInGot() const { return gotIndex != UINT32_MAX; }
bool isInStubs() const { return stubsIndex != UINT32_MAX; }
uint64_t getStubVA() const;
uint64_t getLazyPtrVA() const;
uint64_t getGotVA() const;
uint64_t getTlvVA() const;
uint64_t resolveBranchVA() const {
assert(isa<Defined>(this) || isa<DylibSymbol>(this));
return isInStubs() ? getStubVA() : getVA();
}
uint64_t resolveGotVA() const { return isInGot() ? getGotVA() : getVA(); }
uint64_t resolveTlvVA() const { return isInGot() ? getTlvVA() : getVA(); }
uint32_t gotIndex = UINT32_MAX;
uint32_t lazyBindOffset = UINT32_MAX;
uint32_t stubsHelperIndex = UINT32_MAX;
uint32_t stubsIndex = UINT32_MAX;
uint32_t symtabIndex = UINT32_MAX;
InputFile *getFile() const { return file; }
protected:
Symbol(Kind k, StringRefZ name, InputFile *file)
: symbolKind(k), nameData(name.data), file(file), nameSize(name.size),
isUsedInRegularObj(!file || isa<ObjFile>(file)),
used(!config->deadStrip) {}
Kind symbolKind;
const char *nameData;
InputFile *file;
mutable uint32_t nameSize;
public:
bool isUsedInRegularObj : 1;
bool used : 1;
};
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 includeInSymtab, bool isReferencedDynamically, bool noDeadStrip,
bool canOverrideWeakDef = false, bool isWeakDefCanBeHidden = false,
bool interposable = false);
bool isWeakDef() const override { return weakDef; }
bool isExternalWeakDef() const {
return isWeakDef() && isExternal() && !privateExtern;
}
bool isTlv() const override;
bool isExternal() const { return external; }
bool isAbsolute() const { return originalIsec == nullptr; }
uint64_t getVA() const override;
ObjFile *getObjectFile() const;
std::string getSourceLocation();
InputSection *isec() const;
ConcatInputSection *unwindEntry() const;
static bool classof(const Symbol *s) { return s->kind() == DefinedKind; }
bool overridesWeakDef : 1;
bool privateExtern : 1;
bool includeInSymtab : 1;
bool wasIdenticalCodeFolded : 1;
bool referencedDynamically : 1;
bool noDeadStrip : 1;
bool interposable : 1;
bool weakDefCanBeHidden : 1;
private:
const bool weakDef : 1;
const bool external : 1;
public:
InputSection *originalIsec;
uint64_t value;
uint64_t size;
ConcatInputSection *originalUnwindEntry = nullptr;
};
enum class RefState : uint8_t { Unreferenced = 0, Weak = 1, Strong = 2 };
class Undefined : public Symbol {
public:
Undefined(StringRefZ name, InputFile *file, RefState refState,
bool wasBitcodeSymbol)
: Symbol(UndefinedKind, name, file), refState(refState),
wasBitcodeSymbol(wasBitcodeSymbol) {
assert(refState != RefState::Unreferenced);
}
bool isWeakRef() const override { return refState == RefState::Weak; }
static bool classof(const Symbol *s) { return s->kind() == UndefinedKind; }
RefState refState : 2;
bool wasBitcodeSymbol;
};
class CommonSymbol : public Symbol {
public:
CommonSymbol(StringRefZ name, InputFile *file, uint64_t size, uint32_t align,
bool isPrivateExtern)
: Symbol(CommonKind, name, file), size(size),
align(align != 1 ? align : llvm::PowerOf2Ceil(size)),
privateExtern(isPrivateExtern) {
}
static bool classof(const Symbol *s) { return s->kind() == CommonKind; }
const uint64_t size;
const uint32_t align;
const bool privateExtern;
};
class DylibSymbol : public Symbol {
public:
DylibSymbol(DylibFile *file, StringRefZ name, bool isWeakDef,
RefState refState, bool isTlv)
: Symbol(DylibKind, name, file), shouldReexport(false),
refState(refState), weakDef(isWeakDef), tlv(isTlv) {
if (file && refState > RefState::Unreferenced)
file->numReferencedSymbols++;
}
uint64_t getVA() const override;
bool isWeakDef() const override { return weakDef; }
bool isWeakRef() const override {
return refState == RefState::Weak ||
(file && getFile()->umbrella->forceWeakImport);
}
bool isReferenced() const { return refState != RefState::Unreferenced; }
bool isTlv() const override { return tlv; }
bool isDynamicLookup() const { return file == nullptr; }
bool hasStubsHelper() const { return stubsHelperIndex != UINT32_MAX; }
DylibFile *getFile() const {
assert(!isDynamicLookup());
return cast<DylibFile>(file);
}
static bool classof(const Symbol *s) { return s->kind() == DylibKind; }
RefState getRefState() const { return refState; }
void reference(RefState newState) {
assert(newState > RefState::Unreferenced);
if (refState == RefState::Unreferenced && file)
getFile()->numReferencedSymbols++;
refState = std::max(refState, newState);
}
void unreference() {
if (refState > RefState::Unreferenced && file) {
assert(getFile()->numReferencedSymbols > 0);
getFile()->numReferencedSymbols--;
}
}
bool shouldReexport : 1;
private:
RefState refState : 2;
const bool weakDef : 1;
const bool tlv : 1;
};
class LazyArchive : public Symbol {
public:
LazyArchive(ArchiveFile *file, const llvm::object::Archive::Symbol &sym)
: Symbol(LazyArchiveKind, sym.getName(), file), sym(sym) {}
ArchiveFile *getFile() const { return cast<ArchiveFile>(file); }
void fetchArchiveMember();
static bool classof(const Symbol *s) { return s->kind() == LazyArchiveKind; }
private:
const llvm::object::Archive::Symbol sym;
};
class LazyObject : public Symbol {
public:
LazyObject(InputFile &file, StringRef name)
: Symbol(LazyObjectKind, name, &file) {
isUsedInRegularObj = false;
}
static bool classof(const Symbol *s) { return s->kind() == LazyObjectKind; }
};
class AliasSymbol final : public Symbol {
public:
AliasSymbol(InputFile *file, StringRef name, StringRef aliasedName,
bool isPrivateExtern)
: Symbol(AliasKind, name, file), privateExtern(isPrivateExtern),
aliasedName(aliasedName) {}
StringRef getAliasedName() const { return aliasedName; }
static bool classof(const Symbol *s) { return s->kind() == AliasKind; }
const bool privateExtern;
private:
StringRef aliasedName;
};
union SymbolUnion {
alignas(Defined) char a[sizeof(Defined)];
alignas(Undefined) char b[sizeof(Undefined)];
alignas(CommonSymbol) char c[sizeof(CommonSymbol)];
alignas(DylibSymbol) char d[sizeof(DylibSymbol)];
alignas(LazyArchive) char e[sizeof(LazyArchive)];
alignas(LazyObject) char f[sizeof(LazyObject)];
alignas(AliasSymbol) char g[sizeof(AliasSymbol)];
};
template <typename T, typename... ArgT>
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");
bool isUsedInRegularObj = s->isUsedInRegularObj;
bool used = s->used;
T *sym = new (s) T(std::forward<ArgT>(arg)...);
sym->isUsedInRegularObj |= isUsedInRegularObj;
sym->used |= used;
return sym;
}
inline bool needsBinding(const Symbol *sym) {
if (isa<DylibSymbol>(sym))
return true;
if (const auto *defined = dyn_cast<Defined>(sym))
return defined->isExternalWeakDef() || defined->interposable;
return false;
}
inline bool isPrivateLabel(StringRef name) {
return name.starts_with("l") || name.starts_with("L");
}
}
std::string toString(const macho::Symbol &);
std::string toMachOString(const llvm::object::Archive::Symbol &);
}
#endif