summaryrefslogtreecommitdiff
path: root/ELF/Symbols.h
diff options
context:
space:
mode:
Diffstat (limited to 'ELF/Symbols.h')
-rw-r--r--ELF/Symbols.h399
1 files changed, 182 insertions, 217 deletions
diff --git a/ELF/Symbols.h b/ELF/Symbols.h
index a1b3a6fba911..f4bb245f955d 100644
--- a/ELF/Symbols.h
+++ b/ELF/Symbols.h
@@ -18,7 +18,7 @@
#include "InputSection.h"
#include "Strings.h"
-#include "lld/Core/LLVM.h"
+#include "lld/Common/LLVM.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ELF.h"
@@ -27,53 +27,86 @@ namespace elf {
class ArchiveFile;
class BitcodeFile;
+class BssSection;
class InputFile;
-class LazyObjectFile;
-template <class ELFT> class ObjectFile;
+class LazyObjFile;
+template <class ELFT> class ObjFile;
class OutputSection;
template <class ELFT> class SharedFile;
-struct Symbol;
-
// The base class for real symbol classes.
-class SymbolBody {
+class Symbol {
public:
enum Kind {
- DefinedFirst,
- DefinedRegularKind = DefinedFirst,
+ DefinedKind,
SharedKind,
- DefinedCommonKind,
- DefinedLast = DefinedCommonKind,
UndefinedKind,
LazyArchiveKind,
LazyObjectKind,
};
- SymbolBody(Kind K) : SymbolKind(K) {}
+ Kind kind() const { return static_cast<Kind>(SymbolKind); }
- Symbol *symbol();
- const Symbol *symbol() const {
- return const_cast<SymbolBody *>(this)->symbol();
- }
+ // Symbol binding. This is not overwritten by replaceSymbol to track
+ // changes during resolution. In particular:
+ // - An undefined weak is still weak when it resolves to a shared library.
+ // - An undefined weak will not fetch archive members, but we have to
+ // remember it is weak.
+ uint8_t Binding;
- Kind kind() const { return static_cast<Kind>(SymbolKind); }
+ // Version definition index.
+ uint16_t VersionId;
+
+ // Symbol visibility. This is the computed minimum visibility of all
+ // observed non-DSO symbols.
+ unsigned Visibility : 2;
+
+ // True if the symbol was used for linking and thus need to be added to the
+ // output file's symbol table. This is true for all symbols except for
+ // unreferenced DSO symbols and bitcode symbols that are unreferenced except
+ // by other bitcode objects.
+ unsigned IsUsedInRegularObj : 1;
+
+ // If this flag is true and the symbol has protected or default visibility, it
+ // will appear in .dynsym. This flag is set by interposable DSO symbols in
+ // executables, by most symbols in DSOs and executables built with
+ // --export-dynamic, and by dynamic lists.
+ unsigned ExportDynamic : 1;
+
+ // False if LTO shouldn't inline whatever this symbol points to. If a symbol
+ // is overwritten after LTO, LTO shouldn't inline the symbol because it
+ // doesn't know the final contents of the symbol.
+ unsigned CanInline : 1;
+
+ // True if this symbol is specified by --trace-symbol option.
+ unsigned Traced : 1;
+
+ // This symbol version was found in a version script.
+ unsigned InVersionScript : 1;
+
+ // The file from which this symbol was created.
+ InputFile *File;
+
+ bool includeInDynsym() const;
+ uint8_t computeBinding() const;
+ bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
bool isUndefined() const { return SymbolKind == UndefinedKind; }
- bool isDefined() const { return SymbolKind <= DefinedLast; }
- bool isCommon() const { return SymbolKind == DefinedCommonKind; }
+ bool isDefined() const { return SymbolKind == DefinedKind; }
+ bool isShared() const { return SymbolKind == SharedKind; }
+ bool isLocal() const { return Binding == llvm::ELF::STB_LOCAL; }
+
bool isLazy() const {
return SymbolKind == LazyArchiveKind || SymbolKind == LazyObjectKind;
}
- bool isShared() const { return SymbolKind == SharedKind; }
- bool isInCurrentDSO() const {
- return !isUndefined() && !isShared() && !isLazy();
- }
- bool isLocal() const { return IsLocal; }
- bool isPreemptible() const;
+
+ // True is this is an undefined weak symbol. This only works once
+ // all input files have been added.
+ bool isUndefWeak() const;
+
StringRef getName() const { return Name; }
uint8_t getVisibility() const { return StOther & 0x3; }
void parseSymbolVersion();
- void copy(SymbolBody *Other);
bool isInGot() const { return GotIndex != -1U; }
bool isInPlt() const { return PltIndex != -1U; }
@@ -85,12 +118,9 @@ public:
uint64_t getGotPltOffset() const;
uint64_t getGotPltVA() const;
uint64_t getPltVA() const;
- template <class ELFT> typename ELFT::uint getSize() const;
+ uint64_t getSize() const;
OutputSection *getOutputSection() const;
- // The file from which this symbol was created.
- InputFile *File = nullptr;
-
uint32_t DynsymIndex = 0;
uint32_t GotIndex = -1;
uint32_t GotPltIndex = -1;
@@ -98,23 +128,19 @@ public:
uint32_t GlobalDynIndex = -1;
protected:
- SymbolBody(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther,
- uint8_t Type);
+ Symbol(Kind K, InputFile *File, StringRefZ Name, uint8_t Binding,
+ uint8_t StOther, uint8_t Type)
+ : Binding(Binding), File(File), SymbolKind(K), NeedsPltAddr(false),
+ IsInGlobalMipsGot(false), Is32BitMipsGot(false), IsInIplt(false),
+ IsInIgot(false), IsPreemptible(false), Used(!Config->GcSections),
+ Type(Type), StOther(StOther), Name(Name) {}
const unsigned SymbolKind : 8;
public:
- // True if the linker has to generate a copy relocation.
- // For SharedSymbol only.
- unsigned NeedsCopy : 1;
-
// True the symbol should point to its PLT entry.
// For SharedSymbol only.
unsigned NeedsPltAddr : 1;
-
- // True if this is a local symbol.
- unsigned IsLocal : 1;
-
// True if this symbol has an entry in the global part of MIPS GOT.
unsigned IsInGlobalMipsGot : 1;
@@ -127,6 +153,11 @@ public:
// True if this symbol is in the Igot sub-section of the .got.plt or .got.
unsigned IsInIgot : 1;
+ unsigned IsPreemptible : 1;
+
+ // True if an undefined or shared symbol is used from a live section.
+ unsigned Used : 1;
+
// The following fields have the same meaning as the ELF symbol attributes.
uint8_t Type; // symbol type
uint8_t StOther; // st_other field value
@@ -149,139 +180,111 @@ protected:
StringRefZ Name;
};
-// The base class for any defined symbols.
-class Defined : public SymbolBody {
-public:
- Defined(Kind K, StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type);
- static bool classof(const SymbolBody *S) { return S->isDefined(); }
-};
-
-class DefinedCommon : public Defined {
-public:
- DefinedCommon(StringRef N, uint64_t Size, uint32_t Alignment, uint8_t StOther,
- uint8_t Type, InputFile *File);
-
- static bool classof(const SymbolBody *S) {
- return S->kind() == SymbolBody::DefinedCommonKind;
- }
-
- // The output offset of this common symbol in the output bss. Computed by the
- // writer.
- uint64_t Offset;
-
- // The maximum alignment we have seen for this symbol.
- uint32_t Alignment;
-
- uint64_t Size;
-};
-
-// Regular defined symbols read from object file symbol tables.
-class DefinedRegular : public Defined {
+// Represents a symbol that is defined in the current output file.
+class Defined : public Symbol {
public:
- DefinedRegular(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
- uint64_t Value, uint64_t Size, SectionBase *Section,
- InputFile *File)
- : Defined(SymbolBody::DefinedRegularKind, Name, IsLocal, StOther, Type),
- Value(Value), Size(Size), Section(Section) {
- this->File = File;
- }
-
- // Return true if the symbol is a PIC function.
- template <class ELFT> bool isMipsPIC() const;
+ Defined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
+ uint8_t Type, uint64_t Value, uint64_t Size, SectionBase *Section)
+ : Symbol(DefinedKind, File, Name, Binding, StOther, Type), Value(Value),
+ Size(Size), Section(Section) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() == SymbolBody::DefinedRegularKind;
- }
+ static bool classof(const Symbol *S) { return S->isDefined(); }
uint64_t Value;
uint64_t Size;
SectionBase *Section;
};
-class Undefined : public SymbolBody {
+class Undefined : public Symbol {
public:
- Undefined(StringRefZ Name, bool IsLocal, uint8_t StOther, uint8_t Type,
- InputFile *F);
+ Undefined(InputFile *File, StringRefZ Name, uint8_t Binding, uint8_t StOther,
+ uint8_t Type)
+ : Symbol(UndefinedKind, File, Name, Binding, StOther, Type) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() == UndefinedKind;
- }
+ static bool classof(const Symbol *S) { return S->kind() == UndefinedKind; }
};
-class SharedSymbol : public Defined {
+class SharedSymbol : public Symbol {
public:
- static bool classof(const SymbolBody *S) {
- return S->kind() == SymbolBody::SharedKind;
- }
-
- SharedSymbol(InputFile *File, StringRef Name, uint8_t StOther, uint8_t Type,
- const void *ElfSym, const void *Verdef)
- : Defined(SymbolBody::SharedKind, Name, /*IsLocal=*/false, StOther, Type),
- Verdef(Verdef), ElfSym(ElfSym) {
- // IFuncs defined in DSOs are treated as functions by the static linker.
- if (isGnuIFunc())
+ static bool classof(const Symbol *S) { return S->kind() == SharedKind; }
+
+ SharedSymbol(InputFile *File, StringRef Name, uint8_t Binding,
+ uint8_t StOther, uint8_t Type, uint64_t Value, uint64_t Size,
+ uint32_t Alignment, uint32_t VerdefIndex)
+ : Symbol(SharedKind, File, Name, Binding, StOther, Type), Value(Value),
+ Size(Size), VerdefIndex(VerdefIndex), Alignment(Alignment) {
+ // GNU ifunc is a mechanism to allow user-supplied functions to
+ // resolve PLT slot values at load-time. This is contrary to the
+ // regualr symbol resolution scheme in which symbols are resolved just
+ // by name. Using this hook, you can program how symbols are solved
+ // for you program. For example, you can make "memcpy" to be resolved
+ // to a SSE-enabled version of memcpy only when a machine running the
+ // program supports the SSE instruction set.
+ //
+ // Naturally, such symbols should always be called through their PLT
+ // slots. What GNU ifunc symbols point to are resolver functions, and
+ // calling them directly doesn't make sense (unless you are writing a
+ // loader).
+ //
+ // For DSO symbols, we always call them through PLT slots anyway.
+ // So there's no difference between GNU ifunc and regular function
+ // symbols if they are in DSOs. So we can handle GNU_IFUNC as FUNC.
+ if (this->Type == llvm::ELF::STT_GNU_IFUNC)
this->Type = llvm::ELF::STT_FUNC;
- this->File = File;
}
- template <class ELFT> uint64_t getShndx() const {
- return getSym<ELFT>().st_shndx;
+ template <class ELFT> SharedFile<ELFT> *getFile() const {
+ return cast<SharedFile<ELFT>>(File);
}
- template <class ELFT> uint64_t getValue() const {
- return getSym<ELFT>().st_value;
- }
-
- template <class ELFT> uint64_t getSize() const {
- return getSym<ELFT>().st_size;
- }
-
- template <class ELFT> uint32_t getAlignment() const;
+ // If not null, there is a copy relocation to this section.
+ InputSection *CopyRelSec = nullptr;
- // This field is a pointer to the symbol's version definition.
- const void *Verdef;
+ uint64_t Value; // st_value
+ uint64_t Size; // st_size
- // CopyRelSec and CopyRelSecOff are significant only when NeedsCopy is true.
- InputSection *CopyRelSec;
- uint64_t CopyRelSecOff;
+ // This field is a index to the symbol's version definition.
+ uint32_t VerdefIndex;
-private:
- template <class ELFT> const typename ELFT::Sym &getSym() const {
- return *(const typename ELFT::Sym *)ElfSym;
- }
-
- const void *ElfSym;
+ uint32_t Alignment;
};
-// This class represents a symbol defined in an archive file. It is
-// created from an archive file header, and it knows how to load an
-// object file from an archive to replace itself with a defined
-// symbol. If the resolver finds both Undefined and Lazy for
-// the same name, it will ask the Lazy to load a file.
-class Lazy : public SymbolBody {
+// This represents a symbol that is not yet in the link, but we know where to
+// find it if needed. If the resolver finds both Undefined and Lazy for the same
+// name, it will ask the Lazy to load a file.
+//
+// A special complication is the handling of weak undefined symbols. They should
+// not load a file, but we have to remember we have seen both the weak undefined
+// and the lazy. We represent that with a lazy symbol with a weak binding. This
+// means that code looking for undefined symbols normally also has to take lazy
+// symbols into consideration.
+class Lazy : public Symbol {
public:
- static bool classof(const SymbolBody *S) { return S->isLazy(); }
+ static bool classof(const Symbol *S) { return S->isLazy(); }
// Returns an object file for this symbol, or a nullptr if the file
// was already returned.
InputFile *fetch();
protected:
- Lazy(SymbolBody::Kind K, StringRef Name, uint8_t Type)
- : SymbolBody(K, Name, /*IsLocal=*/false, llvm::ELF::STV_DEFAULT, Type) {}
+ Lazy(Kind K, InputFile *File, StringRef Name, uint8_t Type)
+ : Symbol(K, File, Name, llvm::ELF::STB_GLOBAL, llvm::ELF::STV_DEFAULT,
+ Type) {}
};
-// LazyArchive symbols represents symbols in archive files.
+// This class represents a symbol defined in an archive file. It is
+// created from an archive file header, and it knows how to load an
+// object file from an archive to replace itself with a defined
+// symbol.
class LazyArchive : public Lazy {
public:
- LazyArchive(ArchiveFile &File, const llvm::object::Archive::Symbol S,
- uint8_t Type);
+ LazyArchive(InputFile *File, const llvm::object::Archive::Symbol S,
+ uint8_t Type)
+ : Lazy(LazyArchiveKind, File, S.getName(), Type), Sym(S) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() == LazyArchiveKind;
- }
+ static bool classof(const Symbol *S) { return S->kind() == LazyArchiveKind; }
- ArchiveFile *file() { return (ArchiveFile *)this->File; }
+ ArchiveFile *getFile();
InputFile *fetch();
private:
@@ -292,123 +295,85 @@ private:
// --start-lib and --end-lib options.
class LazyObject : public Lazy {
public:
- LazyObject(StringRef Name, LazyObjectFile &File, uint8_t Type);
+ LazyObject(InputFile *File, StringRef Name, uint8_t Type)
+ : Lazy(LazyObjectKind, File, Name, Type) {}
- static bool classof(const SymbolBody *S) {
- return S->kind() == LazyObjectKind;
- }
+ static bool classof(const Symbol *S) { return S->kind() == LazyObjectKind; }
- LazyObjectFile *file() { return (LazyObjectFile *)this->File; }
+ LazyObjFile *getFile();
InputFile *fetch();
};
// Some linker-generated symbols need to be created as
-// DefinedRegular symbols.
+// Defined symbols.
struct ElfSym {
// __bss_start
- static DefinedRegular *Bss;
+ static Defined *Bss;
// etext and _etext
- static DefinedRegular *Etext1;
- static DefinedRegular *Etext2;
+ static Defined *Etext1;
+ static Defined *Etext2;
// edata and _edata
- static DefinedRegular *Edata1;
- static DefinedRegular *Edata2;
+ static Defined *Edata1;
+ static Defined *Edata2;
// end and _end
- static DefinedRegular *End1;
- static DefinedRegular *End2;
+ static Defined *End1;
+ static Defined *End2;
// The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to
// be at some offset from the base of the .got section, usually 0 or
// the end of the .got.
- static DefinedRegular *GlobalOffsetTable;
+ static Defined *GlobalOffsetTable;
// _gp, _gp_disp and __gnu_local_gp symbols. Only for MIPS.
- static DefinedRegular *MipsGp;
- static DefinedRegular *MipsGpDisp;
- static DefinedRegular *MipsLocalGp;
+ static Defined *MipsGp;
+ static Defined *MipsGpDisp;
+ static Defined *MipsLocalGp;
};
-// A real symbol object, SymbolBody, is usually stored within a Symbol. There's
-// always one Symbol for each symbol name. The resolver updates the SymbolBody
-// stored in the Body field of this object as it resolves symbols. Symbol also
-// holds computed properties of symbol names.
-struct Symbol {
- // Symbol binding. This is on the Symbol to track changes during resolution.
- // In particular:
- // An undefined weak is still weak when it resolves to a shared library.
- // An undefined weak will not fetch archive members, but we have to remember
- // it is weak.
- uint8_t Binding;
-
- // Version definition index.
- uint16_t VersionId;
-
- // Symbol visibility. This is the computed minimum visibility of all
- // observed non-DSO symbols.
- unsigned Visibility : 2;
-
- // True if the symbol was used for linking and thus need to be added to the
- // output file's symbol table. This is true for all symbols except for
- // unreferenced DSO symbols and bitcode symbols that are unreferenced except
- // by other bitcode objects.
- unsigned IsUsedInRegularObj : 1;
-
- // If this flag is true and the symbol has protected or default visibility, it
- // will appear in .dynsym. This flag is set by interposable DSO symbols in
- // executables, by most symbols in DSOs and executables built with
- // --export-dynamic, and by dynamic lists.
- unsigned ExportDynamic : 1;
-
- // True if this symbol is specified by --trace-symbol option.
- unsigned Traced : 1;
-
- // This symbol version was found in a version script.
- unsigned InVersionScript : 1;
-
- bool includeInDynsym() const;
- uint8_t computeBinding() const;
- bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
-
- // This field is used to store the Symbol's SymbolBody. This instantiation of
- // AlignedCharArrayUnion gives us a struct with a char array field that is
- // large and aligned enough to store any derived class of SymbolBody.
- llvm::AlignedCharArrayUnion<DefinedCommon, DefinedRegular, Undefined,
- SharedSymbol, LazyArchive, LazyObject>
- Body;
-
- SymbolBody *body() { return reinterpret_cast<SymbolBody *>(Body.buffer); }
- const SymbolBody *body() const { return const_cast<Symbol *>(this)->body(); }
+// A buffer class that is large enough to hold any Symbol-derived
+// object. We allocate memory using this class and instantiate a symbol
+// using the placement new.
+union SymbolUnion {
+ alignas(Defined) char A[sizeof(Defined)];
+ alignas(Undefined) char C[sizeof(Undefined)];
+ alignas(SharedSymbol) char D[sizeof(SharedSymbol)];
+ alignas(LazyArchive) char E[sizeof(LazyArchive)];
+ alignas(LazyObject) char F[sizeof(LazyObject)];
};
void printTraceSymbol(Symbol *Sym);
template <typename T, typename... ArgT>
-void replaceBody(Symbol *S, ArgT &&... Arg) {
- static_assert(sizeof(T) <= sizeof(S->Body), "Body too small");
- static_assert(alignof(T) <= alignof(decltype(S->Body)),
- "Body not aligned enough");
- assert(static_cast<SymbolBody *>(static_cast<T *>(nullptr)) == nullptr &&
- "Not a SymbolBody");
+void 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");
+
+ Symbol Sym = *S;
- new (S->Body.buffer) T(std::forward<ArgT>(Arg)...);
+ new (S) T(std::forward<ArgT>(Arg)...);
+
+ S->VersionId = Sym.VersionId;
+ S->Visibility = Sym.Visibility;
+ S->IsUsedInRegularObj = Sym.IsUsedInRegularObj;
+ S->ExportDynamic = Sym.ExportDynamic;
+ S->CanInline = Sym.CanInline;
+ S->Traced = Sym.Traced;
+ S->InVersionScript = Sym.InVersionScript;
// Print out a log message if --trace-symbol was specified.
// This is for debugging.
if (S->Traced)
printTraceSymbol(S);
}
-
-inline Symbol *SymbolBody::symbol() {
- assert(!isLocal());
- return reinterpret_cast<Symbol *>(reinterpret_cast<char *>(this) -
- offsetof(Symbol, Body));
-}
} // namespace elf
-std::string toString(const elf::SymbolBody &B);
+std::string toString(const elf::Symbol &B);
} // namespace lld
#endif