diff options
Diffstat (limited to 'ELF/Symbols.h')
-rw-r--r-- | ELF/Symbols.h | 399 |
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 |