diff options
Diffstat (limited to 'gnu/llvm/tools/lld/ELF/SymbolTable.cpp')
| -rw-r--r-- | gnu/llvm/tools/lld/ELF/SymbolTable.cpp | 713 |
1 files changed, 713 insertions, 0 deletions
diff --git a/gnu/llvm/tools/lld/ELF/SymbolTable.cpp b/gnu/llvm/tools/lld/ELF/SymbolTable.cpp new file mode 100644 index 00000000000..78c1298df42 --- /dev/null +++ b/gnu/llvm/tools/lld/ELF/SymbolTable.cpp @@ -0,0 +1,713 @@ +//===- SymbolTable.cpp ----------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Symbol table is a bag of all known symbols. We put all symbols of +// all input files to the symbol table. The symbol table is basically +// a hash table with the logic to resolve symbol name conflicts using +// the symbol types. +// +//===----------------------------------------------------------------------===// + +#include "SymbolTable.h" +#include "Config.h" +#include "Error.h" +#include "LinkerScript.h" +#include "Strings.h" +#include "SymbolListFile.h" +#include "Symbols.h" +#include "llvm/Bitcode/ReaderWriter.h" +#include "llvm/Support/StringSaver.h" + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::ELF; + +using namespace lld; +using namespace lld::elf; + +// All input object files must be for the same architecture +// (e.g. it does not make sense to link x86 object files with +// MIPS object files.) This function checks for that error. +template <class ELFT> static bool isCompatible(InputFile *F) { + if (!isa<ELFFileBase<ELFT>>(F) && !isa<BitcodeFile>(F)) + return true; + if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) + return true; + StringRef A = F->getName(); + StringRef B = Config->Emulation; + if (B.empty()) + B = Config->FirstElf->getName(); + error(A + " is incompatible with " + B); + return false; +} + +// Add symbols in File to the symbol table. +template <class ELFT> +void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) { + InputFile *FileP = File.get(); + if (!isCompatible<ELFT>(FileP)) + return; + + // .a file + if (auto *F = dyn_cast<ArchiveFile>(FileP)) { + ArchiveFiles.emplace_back(cast<ArchiveFile>(File.release())); + F->parse<ELFT>(); + return; + } + + // Lazy object file + if (auto *F = dyn_cast<LazyObjectFile>(FileP)) { + LazyObjectFiles.emplace_back(cast<LazyObjectFile>(File.release())); + F->parse<ELFT>(); + return; + } + + if (Config->Trace) + outs() << getFilename(FileP) << "\n"; + + // .so file + if (auto *F = dyn_cast<SharedFile<ELFT>>(FileP)) { + // DSOs are uniquified not by filename but by soname. + F->parseSoName(); + if (!SoNames.insert(F->getSoName()).second) + return; + + SharedFiles.emplace_back(cast<SharedFile<ELFT>>(File.release())); + F->parseRest(); + return; + } + + // LLVM bitcode file + if (auto *F = dyn_cast<BitcodeFile>(FileP)) { + BitcodeFiles.emplace_back(cast<BitcodeFile>(File.release())); + F->parse<ELFT>(ComdatGroups); + return; + } + + // Regular object file + auto *F = cast<ObjectFile<ELFT>>(FileP); + ObjectFiles.emplace_back(cast<ObjectFile<ELFT>>(File.release())); + F->parse(ComdatGroups); +} + +// This function is where all the optimizations of link-time +// optimization happens. When LTO is in use, some input files are +// not in native object file format but in the LLVM bitcode format. +// This function compiles bitcode files into a few big native files +// using LLVM functions and replaces bitcode symbols with the results. +// Because all bitcode files that consist of a program are passed +// to the compiler at once, it can do whole-program optimization. +template <class ELFT> void SymbolTable<ELFT>::addCombinedLtoObject() { + if (BitcodeFiles.empty()) + return; + + // Compile bitcode files. + Lto.reset(new BitcodeCompiler); + for (const std::unique_ptr<BitcodeFile> &F : BitcodeFiles) + Lto->add(*F); + std::vector<std::unique_ptr<InputFile>> IFs = Lto->compile(); + + // Replace bitcode symbols. + for (auto &IF : IFs) { + ObjectFile<ELFT> *Obj = cast<ObjectFile<ELFT>>(IF.release()); + + DenseSet<StringRef> DummyGroups; + Obj->parse(DummyGroups); + ObjectFiles.emplace_back(Obj); + } +} + +template <class ELFT> +DefinedRegular<ELFT> *SymbolTable<ELFT>::addAbsolute(StringRef Name, + uint8_t Visibility) { + return cast<DefinedRegular<ELFT>>( + addRegular(Name, STB_GLOBAL, Visibility)->body()); +} + +// Add Name as an "ignored" symbol. An ignored symbol is a regular +// linker-synthesized defined symbol, but is only defined if needed. +template <class ELFT> +DefinedRegular<ELFT> *SymbolTable<ELFT>::addIgnored(StringRef Name, + uint8_t Visibility) { + if (!find(Name)) + return nullptr; + return addAbsolute(Name, Visibility); +} + +// Set a flag for --trace-symbol so that we can print out a log message +// if a new symbol with the same name is inserted into the symbol table. +template <class ELFT> void SymbolTable<ELFT>::trace(StringRef Name) { + Symtab.insert({Name, {-1, true}}); +} + +// Rename SYM as __wrap_SYM. The original symbol is preserved as __real_SYM. +// Used to implement --wrap. +template <class ELFT> void SymbolTable<ELFT>::wrap(StringRef Name) { + SymbolBody *B = find(Name); + if (!B) + return; + StringSaver Saver(Alloc); + Symbol *Sym = B->symbol(); + Symbol *Real = addUndefined(Saver.save("__real_" + Name)); + Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name)); + // We rename symbols by replacing the old symbol's SymbolBody with the new + // symbol's SymbolBody. This causes all SymbolBody pointers referring to the + // old symbol to instead refer to the new symbol. + memcpy(Real->Body.buffer, Sym->Body.buffer, sizeof(Sym->Body)); + memcpy(Sym->Body.buffer, Wrap->Body.buffer, sizeof(Wrap->Body)); +} + +static uint8_t getMinVisibility(uint8_t VA, uint8_t VB) { + if (VA == STV_DEFAULT) + return VB; + if (VB == STV_DEFAULT) + return VA; + return std::min(VA, VB); +} + +// Find an existing symbol or create and insert a new one. +template <class ELFT> +std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) { + auto P = Symtab.insert({Name, {(int)SymVector.size(), false}}); + SymIndex &V = P.first->second; + bool IsNew = P.second; + + if (V.Idx == -1) { + IsNew = true; + V = {(int)SymVector.size(), true}; + } + + Symbol *Sym; + if (IsNew) { + Sym = new (Alloc) Symbol; + Sym->Binding = STB_WEAK; + Sym->Visibility = STV_DEFAULT; + Sym->IsUsedInRegularObj = false; + Sym->ExportDynamic = false; + Sym->VersionId = Config->DefaultSymbolVersion; + Sym->Traced = V.Traced; + SymVector.push_back(Sym); + } else { + Sym = SymVector[V.Idx]; + } + return {Sym, IsNew}; +} + +// Find an existing symbol or create and insert a new one, then apply the given +// attributes. +template <class ELFT> +std::pair<Symbol *, bool> +SymbolTable<ELFT>::insert(StringRef Name, uint8_t Type, uint8_t Visibility, + bool CanOmitFromDynSym, bool IsUsedInRegularObj, + InputFile *File) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + + // Merge in the new symbol's visibility. + S->Visibility = getMinVisibility(S->Visibility, Visibility); + if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic)) + S->ExportDynamic = true; + if (IsUsedInRegularObj) + S->IsUsedInRegularObj = true; + if (!WasInserted && S->body()->Type != SymbolBody::UnknownType && + ((Type == STT_TLS) != S->body()->isTls())) + error("TLS attribute mismatch for symbol: " + + conflictMsg(S->body(), File)); + + return {S, WasInserted}; +} + +// Construct a string in the form of "Sym in File1 and File2". +// Used to construct an error message. +template <typename ELFT> +std::string SymbolTable<ELFT>::conflictMsg(SymbolBody *Existing, + InputFile *NewFile) { + std::string Sym = Existing->getName(); + if (Config->Demangle) + Sym = demangle(Sym); + return Sym + " in " + getFilename(Existing->File) + " and " + + getFilename(NewFile); +} + +template <class ELFT> Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name) { + return addUndefined(Name, STB_GLOBAL, STV_DEFAULT, /*Type*/ 0, + /*CanOmitFromDynSym*/ false, /*File*/ nullptr); +} + +template <class ELFT> +Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name, uint8_t Binding, + uint8_t StOther, uint8_t Type, + bool CanOmitFromDynSym, + InputFile *File) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = + insert(Name, Type, StOther & 3, CanOmitFromDynSym, + /*IsUsedInRegularObj*/ !File || !isa<BitcodeFile>(File), File); + if (WasInserted) { + S->Binding = Binding; + replaceBody<Undefined>(S, Name, StOther, Type, File); + return S; + } + if (Binding != STB_WEAK) { + if (S->body()->isShared() || S->body()->isLazy()) + S->Binding = Binding; + if (auto *SS = dyn_cast<SharedSymbol<ELFT>>(S->body())) + SS->file()->IsUsed = true; + } + if (auto *L = dyn_cast<Lazy>(S->body())) { + // An undefined weak will not fetch archive members, but we have to remember + // its type. See also comment in addLazyArchive. + if (S->isWeak()) + L->Type = Type; + else if (auto F = L->fetch()) + addFile(std::move(F)); + } + return S; +} + +// We have a new defined symbol with the specified binding. Return 1 if the new +// symbol should win, -1 if the new symbol should lose, or 0 if both symbols are +// strong defined symbols. +static int compareDefined(Symbol *S, bool WasInserted, uint8_t Binding) { + if (WasInserted) + return 1; + SymbolBody *Body = S->body(); + if (Body->isLazy() || Body->isUndefined() || Body->isShared()) + return 1; + if (Binding == STB_WEAK) + return -1; + if (S->isWeak()) + return 1; + return 0; +} + +// We have a new non-common defined symbol with the specified binding. Return 1 +// if the new symbol should win, -1 if the new symbol should lose, or 0 if there +// is a conflict. If the new symbol wins, also update the binding. +static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding) { + if (int Cmp = compareDefined(S, WasInserted, Binding)) { + if (Cmp > 0) + S->Binding = Binding; + return Cmp; + } + if (isa<DefinedCommon>(S->body())) { + // Non-common symbols take precedence over common symbols. + if (Config->WarnCommon) + warning("common " + S->body()->getName() + " is overridden"); + return 1; + } + return 0; +} + +template <class ELFT> +Symbol *SymbolTable<ELFT>::addCommon(StringRef N, uint64_t Size, + uint64_t Alignment, uint8_t Binding, + uint8_t StOther, uint8_t Type, + InputFile *File) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = + insert(N, Type, StOther & 3, /*CanOmitFromDynSym*/ false, + /*IsUsedInRegularObj*/ true, File); + int Cmp = compareDefined(S, WasInserted, Binding); + if (Cmp > 0) { + S->Binding = Binding; + replaceBody<DefinedCommon>(S, N, Size, Alignment, StOther, Type, File); + } else if (Cmp == 0) { + auto *C = dyn_cast<DefinedCommon>(S->body()); + if (!C) { + // Non-common symbols take precedence over common symbols. + if (Config->WarnCommon) + warning("common " + S->body()->getName() + " is overridden"); + return S; + } + + if (Config->WarnCommon) + warning("multiple common of " + S->body()->getName()); + + C->Size = std::max(C->Size, Size); + C->Alignment = std::max(C->Alignment, Alignment); + } + return S; +} + +template <class ELFT> +void SymbolTable<ELFT>::reportDuplicate(SymbolBody *Existing, + InputFile *NewFile) { + std::string Msg = "duplicate symbol: " + conflictMsg(Existing, NewFile); + if (Config->AllowMultipleDefinition) + warning(Msg); + else + error(Msg); +} + +template <typename ELFT> +Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, const Elf_Sym &Sym, + InputSectionBase<ELFT> *Section) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = + insert(Name, Sym.getType(), Sym.getVisibility(), + /*CanOmitFromDynSym*/ false, /*IsUsedInRegularObj*/ true, + Section ? Section->getFile() : nullptr); + int Cmp = compareDefinedNonCommon(S, WasInserted, Sym.getBinding()); + if (Cmp > 0) + replaceBody<DefinedRegular<ELFT>>(S, Name, Sym, Section); + else if (Cmp == 0) + reportDuplicate(S->body(), Section->getFile()); + return S; +} + +template <typename ELFT> +Symbol *SymbolTable<ELFT>::addRegular(StringRef Name, uint8_t Binding, + uint8_t StOther) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = + insert(Name, STT_NOTYPE, StOther & 3, /*CanOmitFromDynSym*/ false, + /*IsUsedInRegularObj*/ true, nullptr); + int Cmp = compareDefinedNonCommon(S, WasInserted, Binding); + if (Cmp > 0) + replaceBody<DefinedRegular<ELFT>>(S, Name, StOther); + else if (Cmp == 0) + reportDuplicate(S->body(), nullptr); + return S; +} + +template <typename ELFT> +Symbol *SymbolTable<ELFT>::addSynthetic(StringRef N, + OutputSectionBase<ELFT> *Section, + uintX_t Value) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = + insert(N, STT_NOTYPE, STV_HIDDEN, /*CanOmitFromDynSym*/ false, + /*IsUsedInRegularObj*/ true, nullptr); + int Cmp = compareDefinedNonCommon(S, WasInserted, STB_GLOBAL); + if (Cmp > 0) + replaceBody<DefinedSynthetic<ELFT>>(S, N, Value, Section); + else if (Cmp == 0) + reportDuplicate(S->body(), nullptr); + return S; +} + +template <typename ELFT> +void SymbolTable<ELFT>::addShared(SharedFile<ELFT> *F, StringRef Name, + const Elf_Sym &Sym, + const typename ELFT::Verdef *Verdef) { + // DSO symbols do not affect visibility in the output, so we pass STV_DEFAULT + // as the visibility, which will leave the visibility in the symbol table + // unchanged. + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = + insert(Name, Sym.getType(), STV_DEFAULT, /*CanOmitFromDynSym*/ true, + /*IsUsedInRegularObj*/ false, F); + // Make sure we preempt DSO symbols with default visibility. + if (Sym.getVisibility() == STV_DEFAULT) + S->ExportDynamic = true; + if (WasInserted || isa<Undefined>(S->body())) { + replaceBody<SharedSymbol<ELFT>>(S, F, Name, Sym, Verdef); + if (!S->isWeak()) + F->IsUsed = true; + } +} + +template <class ELFT> +Symbol *SymbolTable<ELFT>::addBitcode(StringRef Name, bool IsWeak, + uint8_t StOther, uint8_t Type, + bool CanOmitFromDynSym, BitcodeFile *F) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name, Type, StOther & 3, CanOmitFromDynSym, + /*IsUsedInRegularObj*/ false, F); + int Cmp = + compareDefinedNonCommon(S, WasInserted, IsWeak ? STB_WEAK : STB_GLOBAL); + if (Cmp > 0) + replaceBody<DefinedBitcode>(S, Name, StOther, Type, F); + else if (Cmp == 0) + reportDuplicate(S->body(), F); + return S; +} + +template <class ELFT> SymbolBody *SymbolTable<ELFT>::find(StringRef Name) { + auto It = Symtab.find(Name); + if (It == Symtab.end()) + return nullptr; + SymIndex V = It->second; + if (V.Idx == -1) + return nullptr; + return SymVector[V.Idx]->body(); +} + +// Returns a list of defined symbols that match with a given glob pattern. +template <class ELFT> +std::vector<SymbolBody *> SymbolTable<ELFT>::findAll(StringRef Pattern) { + std::vector<SymbolBody *> Res; + for (Symbol *Sym : SymVector) { + SymbolBody *B = Sym->body(); + if (!B->isUndefined() && globMatch(Pattern, B->getName())) + Res.push_back(B); + } + return Res; +} + +template <class ELFT> +void SymbolTable<ELFT>::addLazyArchive(ArchiveFile *F, + const object::Archive::Symbol Sym) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Sym.getName()); + if (WasInserted) { + replaceBody<LazyArchive>(S, *F, Sym, SymbolBody::UnknownType); + return; + } + if (!S->body()->isUndefined()) + return; + + // Weak undefined symbols should not fetch members from archives. If we were + // to keep old symbol we would not know that an archive member was available + // if a strong undefined symbol shows up afterwards in the link. If a strong + // undefined symbol never shows up, this lazy symbol will get to the end of + // the link and must be treated as the weak undefined one. We already marked + // this symbol as used when we added it to the symbol table, but we also need + // to preserve its type. FIXME: Move the Type field to Symbol. + if (S->isWeak()) { + replaceBody<LazyArchive>(S, *F, Sym, S->body()->Type); + return; + } + MemoryBufferRef MBRef = F->getMember(&Sym); + if (!MBRef.getBuffer().empty()) + addFile(createObjectFile(MBRef, F->getName())); +} + +template <class ELFT> +void SymbolTable<ELFT>::addLazyObject(StringRef Name, LazyObjectFile &Obj) { + Symbol *S; + bool WasInserted; + std::tie(S, WasInserted) = insert(Name); + if (WasInserted) { + replaceBody<LazyObject>(S, Name, Obj, SymbolBody::UnknownType); + return; + } + if (!S->body()->isUndefined()) + return; + + // See comment for addLazyArchive above. + if (S->isWeak()) { + replaceBody<LazyObject>(S, Name, Obj, S->body()->Type); + } else { + MemoryBufferRef MBRef = Obj.getBuffer(); + if (!MBRef.getBuffer().empty()) + addFile(createObjectFile(MBRef)); + } +} + +// Process undefined (-u) flags by loading lazy symbols named by those flags. +template <class ELFT> void SymbolTable<ELFT>::scanUndefinedFlags() { + for (StringRef S : Config->Undefined) + if (auto *L = dyn_cast_or_null<Lazy>(find(S))) + if (std::unique_ptr<InputFile> File = L->fetch()) + addFile(std::move(File)); +} + +// This function takes care of the case in which shared libraries depend on +// the user program (not the other way, which is usual). Shared libraries +// may have undefined symbols, expecting that the user program provides +// the definitions for them. An example is BSD's __progname symbol. +// We need to put such symbols to the main program's .dynsym so that +// shared libraries can find them. +// Except this, we ignore undefined symbols in DSOs. +template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() { + for (std::unique_ptr<SharedFile<ELFT>> &File : SharedFiles) + for (StringRef U : File->getUndefinedSymbols()) + if (SymbolBody *Sym = find(U)) + if (Sym->isDefined()) + Sym->symbol()->ExportDynamic = true; +} + +// This function process the dynamic list option by marking all the symbols +// to be exported in the dynamic table. +template <class ELFT> void SymbolTable<ELFT>::scanDynamicList() { + for (StringRef S : Config->DynamicList) + if (SymbolBody *B = find(S)) + B->symbol()->ExportDynamic = true; +} + +static bool hasWildcard(StringRef S) { + return S.find_first_of("?*") != StringRef::npos; +} + +static void setVersionId(SymbolBody *Body, StringRef VersionName, + StringRef Name, uint16_t Version) { + if (!Body || Body->isUndefined()) { + if (Config->NoUndefinedVersion) + error("version script assignment of " + VersionName + " to symbol " + + Name + " failed: symbol not defined"); + return; + } + + Symbol *Sym = Body->symbol(); + if (Sym->VersionId != Config->DefaultSymbolVersion) + warning("duplicate symbol " + Name + " in version script"); + Sym->VersionId = Version; +} + +template <class ELFT> +std::map<std::string, SymbolBody *> SymbolTable<ELFT>::getDemangledSyms() { + std::map<std::string, SymbolBody *> Result; + for (Symbol *Sym : SymVector) { + SymbolBody *B = Sym->body(); + Result[demangle(B->getName())] = B; + } + return Result; +} + +static bool hasExternCpp() { + for (VersionDefinition &V : Config->VersionDefinitions) + for (SymbolVersion Sym : V.Globals) + if (Sym.IsExternCpp) + return true; + return false; +} + +// This function processes the --version-script option by marking all global +// symbols with the VersionScriptGlobal flag, which acts as a filter on the +// dynamic symbol table. +template <class ELFT> void SymbolTable<ELFT>::scanVersionScript() { + // If version script does not contain versions declarations, + // we just should mark global symbols. + if (!Config->VersionScriptGlobals.empty()) { + for (SymbolVersion &Sym : Config->VersionScriptGlobals) + if (SymbolBody *B = find(Sym.Name)) + B->symbol()->VersionId = VER_NDX_GLOBAL; + return; + } + + if (Config->VersionDefinitions.empty()) + return; + + // If we have symbols version declarations, we should + // assign version references for each symbol. + // Current rules are: + // * If there is an exact match for the mangled name or we have extern C++ + // exact match, then we use it. + // * Otherwise, we look through the wildcard patterns. We look through the + // version tags in reverse order. We use the first match we find (the last + // matching version tag in the file). + // Handle exact matches and build a map of demangled externs for + // quick search during next step. + std::map<std::string, SymbolBody *> Demangled; + if (hasExternCpp()) + Demangled = getDemangledSyms(); + + for (VersionDefinition &V : Config->VersionDefinitions) { + for (SymbolVersion Sym : V.Globals) { + if (hasWildcard(Sym.Name)) + continue; + SymbolBody *B = Sym.IsExternCpp ? Demangled[Sym.Name] : find(Sym.Name); + setVersionId(B, V.Name, Sym.Name, V.Id); + } + } + + // Handle wildcards. + for (size_t I = Config->VersionDefinitions.size() - 1; I != (size_t)-1; --I) { + VersionDefinition &V = Config->VersionDefinitions[I]; + for (SymbolVersion &Sym : V.Globals) + if (hasWildcard(Sym.Name)) + for (SymbolBody *B : findAll(Sym.Name)) + if (B->symbol()->VersionId == Config->DefaultSymbolVersion) + B->symbol()->VersionId = V.Id; + } +} + +// Returns the size of the longest version name. +static int getMaxVersionLen() { + size_t Len = 0; + for (VersionDefinition &V : Config->VersionDefinitions) + Len = std::max(Len, V.Name.size()); + return Len; +} + +// Parses a symbol name in the form of <name>@<version> or <name>@@<version>. +static std::pair<StringRef, uint16_t> +getSymbolVersion(SymbolBody *B, int MaxVersionLen) { + StringRef S = B->getName(); + + // MaxVersionLen was passed so that we don't need to scan + // all characters in a symbol name. It is effective because + // versions are usually short and symbol names can be very long. + size_t Pos = S.find('@', std::max(0, int(S.size()) - MaxVersionLen - 2)); + if (Pos == 0 || Pos == StringRef::npos) + return {"", 0}; + + StringRef Name = S.substr(0, Pos); + StringRef Verstr = S.substr(Pos + 1); + if (Verstr.empty()) + return {"", 0}; + + // '@@' in a symbol name means the default version. + // It is usually the most recent one. + bool IsDefault = (Verstr[0] == '@'); + if (IsDefault) + Verstr = Verstr.substr(1); + + for (VersionDefinition &V : Config->VersionDefinitions) { + if (V.Name == Verstr) + return {Name, IsDefault ? V.Id : (V.Id | VERSYM_HIDDEN)}; + } + + // It is an error if the specified version was not defined. + error("symbol " + S + " has undefined version " + Verstr); + return {"", 0}; +} + +// Versions are usually assigned to symbols using version scripts, +// but there's another way to assign versions to symbols. +// If a symbol name contains '@', the string after it is not +// actually a part of the symbol name but specifies a version. +// This function takes care of it. +template <class ELFT> void SymbolTable<ELFT>::scanSymbolVersions() { + if (Config->VersionDefinitions.empty()) + return; + + int MaxVersionLen = getMaxVersionLen(); + + // Unfortunately there's no way other than iterating over all + // symbols to look for '@' characters in symbol names. + // So this is inherently slow. A good news is that we do this + // only when versions have been defined. + for (Symbol *Sym : SymVector) { + // Symbol versions for exported symbols are by nature + // only for defined global symbols. + SymbolBody *B = Sym->body(); + if (!B->isDefined()) + continue; + uint8_t Visibility = B->getVisibility(); + if (Visibility != STV_DEFAULT && Visibility != STV_PROTECTED) + continue; + + // Look for '@' in the symbol name. + StringRef Name; + uint16_t Version; + std::tie(Name, Version) = getSymbolVersion(B, MaxVersionLen); + if (Name.empty()) + continue; + + B->setName(Name); + Sym->VersionId = Version; + } +} + +template class elf::SymbolTable<ELF32LE>; +template class elf::SymbolTable<ELF32BE>; +template class elf::SymbolTable<ELF64LE>; +template class elf::SymbolTable<ELF64BE>; |
