diff options
Diffstat (limited to 'ELF/SymbolTable.cpp')
-rw-r--r-- | ELF/SymbolTable.cpp | 835 |
1 files changed, 143 insertions, 692 deletions
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp index 7615e12199fa..3faeed8c2bdc 100644 --- a/ELF/SymbolTable.cpp +++ b/ELF/SymbolTable.cpp @@ -1,9 +1,8 @@ //===- SymbolTable.cpp ----------------------------------------------------===// // -// The LLVM Linker -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 // //===----------------------------------------------------------------------===// // @@ -31,576 +30,76 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; -SymbolTable *elf::Symtab; - -static InputFile *getFirstElf() { - if (!ObjectFiles.empty()) - return ObjectFiles[0]; - if (!SharedFiles.empty()) - return SharedFiles[0]; - return BitcodeFiles[0]; -} - -// 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. -static bool isCompatible(InputFile *F) { - if (!F->isElf() && !isa<BitcodeFile>(F)) - return true; - - if (F->EKind == Config->EKind && F->EMachine == Config->EMachine) { - if (Config->EMachine != EM_MIPS) - return true; - if (isMipsN32Abi(F) == Config->MipsN32Abi) - return true; - } - - if (!Config->Emulation.empty()) - error(toString(F) + " is incompatible with " + Config->Emulation); - else - error(toString(F) + " is incompatible with " + toString(getFirstElf())); - return false; -} - -// Add symbols in File to the symbol table. -template <class ELFT> void SymbolTable::addFile(InputFile *File) { - if (!isCompatible(File)) - return; - - // Binary file - if (auto *F = dyn_cast<BinaryFile>(File)) { - BinaryFiles.push_back(F); - F->parse(); - return; - } - - // .a file - if (auto *F = dyn_cast<ArchiveFile>(File)) { - F->parse<ELFT>(); - return; - } - - // Lazy object file - if (auto *F = dyn_cast<LazyObjFile>(File)) { - LazyObjFiles.push_back(F); - F->parse<ELFT>(); - return; - } - - if (Config->Trace) - message(toString(File)); - - // .so file - if (auto *F = dyn_cast<SharedFile<ELFT>>(File)) { - // DSOs are uniquified not by filename but by soname. - F->parseSoName(); - if (errorCount()) - return; - - // If a DSO appears more than once on the command line with and without - // --as-needed, --no-as-needed takes precedence over --as-needed because a - // user can add an extra DSO with --no-as-needed to force it to be added to - // the dependency list. - DenseMap<StringRef, InputFile *>::iterator It; - bool WasInserted; - std::tie(It, WasInserted) = SoNames.try_emplace(F->SoName, F); - cast<SharedFile<ELFT>>(It->second)->IsNeeded |= F->IsNeeded; - if (!WasInserted) - return; - - SharedFiles.push_back(F); - F->parseRest(); - return; - } - - // LLVM bitcode file - if (auto *F = dyn_cast<BitcodeFile>(File)) { - BitcodeFiles.push_back(F); - F->parse<ELFT>(ComdatGroups); - return; - } - - // Regular object file - ObjectFiles.push_back(File); - cast<ObjFile<ELFT>>(File)->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 the program consists of are passed -// to the compiler at once, it can do whole-program optimization. -template <class ELFT> void SymbolTable::addCombinedLTOObject() { - if (BitcodeFiles.empty()) - return; - - // Compile bitcode files and replace bitcode symbols. - LTO.reset(new BitcodeCompiler); - for (BitcodeFile *F : BitcodeFiles) - LTO->add(*F); - - for (InputFile *File : LTO->compile()) { - DenseSet<CachedHashStringRef> DummyGroups; - auto *Obj = cast<ObjFile<ELFT>>(File); - Obj->parse(DummyGroups); - for (Symbol *Sym : Obj->getGlobalSymbols()) - Sym->parseSymbolVersion(); - ObjectFiles.push_back(File); - } -} - -// 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. -void SymbolTable::trace(StringRef Name) { - SymMap.insert({CachedHashStringRef(Name), -1}); -} +SymbolTable *elf::symtab; -void SymbolTable::wrap(Symbol *Sym, Symbol *Real, Symbol *Wrap) { +void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { // Swap symbols as instructed by -wrap. - int &Idx1 = SymMap[CachedHashStringRef(Sym->getName())]; - int &Idx2 = SymMap[CachedHashStringRef(Real->getName())]; - int &Idx3 = SymMap[CachedHashStringRef(Wrap->getName())]; + int &idx1 = symMap[CachedHashStringRef(sym->getName())]; + int &idx2 = symMap[CachedHashStringRef(real->getName())]; + int &idx3 = symMap[CachedHashStringRef(wrap->getName())]; - Idx2 = Idx1; - Idx1 = Idx3; + idx2 = idx1; + idx1 = idx3; // Now renaming is complete. No one refers Real symbol. We could leave // Real as-is, but if Real is written to the symbol table, that may // contain irrelevant values. So, we copy all values from Sym to Real. - StringRef S = Real->getName(); - memcpy(Real, Sym, sizeof(SymbolUnion)); - Real->setName(S); -} - -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); + StringRef s = real->getName(); + memcpy(real, sym, sizeof(SymbolUnion)); + real->setName(s); } -// Find an existing symbol or create and insert a new one. -std::pair<Symbol *, bool> SymbolTable::insertName(StringRef Name) { +// Find an existing symbol or create a new one. +Symbol *SymbolTable::insert(StringRef name) { // <name>@@<version> means the symbol is the default version. In that // case <name>@@<version> will be used to resolve references to <name>. // // Since this is a hot path, the following string search code is // optimized for speed. StringRef::find(char) is much faster than // StringRef::find(StringRef). - size_t Pos = Name.find('@'); - if (Pos != StringRef::npos && Pos + 1 < Name.size() && Name[Pos + 1] == '@') - Name = Name.take_front(Pos); - - auto P = SymMap.insert({CachedHashStringRef(Name), (int)SymVector.size()}); - int &SymIndex = P.first->second; - bool IsNew = P.second; - bool Traced = false; - - if (SymIndex == -1) { - SymIndex = SymVector.size(); - IsNew = true; - Traced = true; - } - - if (!IsNew) - return {SymVector[SymIndex], false}; - - auto *Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); - Sym->SymbolKind = Symbol::PlaceholderKind; - Sym->Visibility = STV_DEFAULT; - Sym->IsUsedInRegularObj = false; - Sym->ExportDynamic = false; - Sym->CanInline = true; - Sym->Traced = Traced; - Sym->VersionId = Config->DefaultSymbolVersion; - SymVector.push_back(Sym); - return {Sym, true}; + size_t pos = name.find('@'); + if (pos != StringRef::npos && pos + 1 < name.size() && name[pos + 1] == '@') + name = name.take_front(pos); + + auto p = symMap.insert({CachedHashStringRef(name), (int)symVector.size()}); + int &symIndex = p.first->second; + bool isNew = p.second; + + if (!isNew) + return symVector[symIndex]; + + Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>()); + symVector.push_back(sym); + + sym->setName(name); + sym->symbolKind = Symbol::PlaceholderKind; + sym->versionId = config->defaultSymbolVersion; + sym->visibility = STV_DEFAULT; + sym->isUsedInRegularObj = false; + sym->exportDynamic = false; + sym->canInline = true; + sym->scriptDefined = false; + sym->partition = 1; + return sym; } -// Find an existing symbol or create and insert a new one, then apply the given -// attributes. -std::pair<Symbol *, bool> SymbolTable::insert(StringRef Name, - uint8_t Visibility, - bool CanOmitFromDynSym, - InputFile *File) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insertName(Name); - - // Merge in the new symbol's visibility. - S->Visibility = getMinVisibility(S->Visibility, Visibility); - - if (!CanOmitFromDynSym && (Config->Shared || Config->ExportDynamic)) - S->ExportDynamic = true; - - if (!File || File->kind() == InputFile::ObjKind) - S->IsUsedInRegularObj = true; - - return {S, WasInserted}; +Symbol *SymbolTable::addSymbol(const Symbol &New) { + Symbol *sym = symtab->insert(New.getName()); + sym->resolve(New); + return sym; } -static uint8_t getVisibility(uint8_t StOther) { return StOther & 3; } - -template <class ELFT> -Symbol *SymbolTable::addUndefined(StringRef Name, uint8_t Binding, - uint8_t StOther, uint8_t Type, - bool CanOmitFromDynSym, InputFile *File) { - Symbol *S; - bool WasInserted; - uint8_t Visibility = getVisibility(StOther); - std::tie(S, WasInserted) = insert(Name, Visibility, CanOmitFromDynSym, File); - - // An undefined symbol with non default visibility must be satisfied - // in the same DSO. - if (WasInserted || (isa<SharedSymbol>(S) && Visibility != STV_DEFAULT)) { - replaceSymbol<Undefined>(S, File, Name, Binding, StOther, Type); - return S; - } - - if (S->isShared() || S->isLazy() || (S->isUndefined() && Binding != STB_WEAK)) - S->Binding = Binding; - - if (S->isLazy()) { - // An undefined weak will not fetch archive members. See comment on Lazy in - // Symbols.h for the details. - if (Binding == STB_WEAK) { - S->Type = Type; - return S; - } - - // Do extra check for --warn-backrefs. - // - // --warn-backrefs is an option to prevent an undefined reference from - // fetching an archive member written earlier in the command line. It can be - // used to keep compatibility with GNU linkers to some degree. - // I'll explain the feature and why you may find it useful in this comment. - // - // lld's symbol resolution semantics is more relaxed than traditional Unix - // linkers. For example, - // - // ld.lld foo.a bar.o - // - // succeeds even if bar.o contains an undefined symbol that has to be - // resolved by some object file in foo.a. Traditional Unix linkers don't - // allow this kind of backward reference, as they visit each file only once - // from left to right in the command line while resolving all undefined - // symbols at the moment of visiting. - // - // In the above case, since there's no undefined symbol when a linker visits - // foo.a, no files are pulled out from foo.a, and because the linker forgets - // about foo.a after visiting, it can't resolve undefined symbols in bar.o - // that could have been resolved otherwise. - // - // That lld accepts more relaxed form means that (besides it'd make more - // sense) you can accidentally write a command line or a build file that - // works only with lld, even if you have a plan to distribute it to wider - // users who may be using GNU linkers. With --warn-backrefs, you can detect - // a library order that doesn't work with other Unix linkers. - // - // The option is also useful to detect cyclic dependencies between static - // archives. Again, lld accepts - // - // ld.lld foo.a bar.a - // - // even if foo.a and bar.a depend on each other. With --warn-backrefs, it is - // handled as an error. - // - // Here is how the option works. We assign a group ID to each file. A file - // with a smaller group ID can pull out object files from an archive file - // with an equal or greater group ID. Otherwise, it is a reverse dependency - // and an error. - // - // A file outside --{start,end}-group gets a fresh ID when instantiated. All - // files within the same --{start,end}-group get the same group ID. E.g. - // - // ld.lld A B --start-group C D --end-group E - // - // A forms group 0. B form group 1. C and D (including their member object - // files) form group 2. E forms group 3. I think that you can see how this - // group assignment rule simulates the traditional linker's semantics. - bool Backref = - Config->WarnBackrefs && File && S->File->GroupId < File->GroupId; - fetchLazy<ELFT>(S); - - // We don't report backward references to weak symbols as they can be - // overridden later. - if (Backref && S->Binding != STB_WEAK) - warn("backward reference detected: " + Name + " in " + toString(File) + - " refers to " + toString(S->File)); - } - return S; -} - -// Using .symver foo,foo@@VER unfortunately creates two symbols: foo and -// foo@@VER. We want to effectively ignore foo, so give precedence to -// foo@@VER. -// FIXME: If users can transition to using -// .symver foo,foo@@@VER -// we can delete this hack. -static int compareVersion(Symbol *S, StringRef Name) { - bool A = Name.contains("@@"); - bool B = S->getName().contains("@@"); - if (A && !B) - return 1; - if (!A && B) - return -1; - return 0; -} - -// 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, - StringRef Name) { - if (WasInserted) - return 1; - if (!S->isDefined()) - return 1; - if (int R = compareVersion(S, Name)) - return R; - 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, - bool IsAbsolute, uint64_t Value, - StringRef Name) { - if (int Cmp = compareDefined(S, WasInserted, Binding, Name)) - return Cmp; - if (auto *R = dyn_cast<Defined>(S)) { - if (R->Section && isa<BssSection>(R->Section)) { - // Non-common symbols take precedence over common symbols. - if (Config->WarnCommon) - warn("common " + S->getName() + " is overridden"); - return 1; - } - if (R->Section == nullptr && Binding == STB_GLOBAL && IsAbsolute && - R->Value == Value) - return -1; - } - return 0; -} - -Symbol *SymbolTable::addCommon(StringRef N, uint64_t Size, uint32_t Alignment, - uint8_t Binding, uint8_t StOther, uint8_t Type, - InputFile &File) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(N, getVisibility(StOther), - /*CanOmitFromDynSym*/ false, &File); - - int Cmp = compareDefined(S, WasInserted, Binding, N); - if (Cmp < 0) - return S; - - if (Cmp > 0) { - auto *Bss = make<BssSection>("COMMON", Size, Alignment); - Bss->File = &File; - Bss->Live = !Config->GcSections; - InputSections.push_back(Bss); - - replaceSymbol<Defined>(S, &File, N, Binding, StOther, Type, 0, Size, Bss); - return S; - } - - auto *D = cast<Defined>(S); - auto *Bss = dyn_cast_or_null<BssSection>(D->Section); - if (!Bss) { - // Non-common symbols take precedence over common symbols. - if (Config->WarnCommon) - warn("common " + S->getName() + " is overridden"); - return S; - } - - if (Config->WarnCommon) - warn("multiple common of " + D->getName()); - - Bss->Alignment = std::max(Bss->Alignment, Alignment); - if (Size > Bss->Size) { - D->File = Bss->File = &File; - D->Size = Bss->Size = Size; - } - return S; -} - -static void reportDuplicate(Symbol *Sym, InputFile *NewFile, - InputSectionBase *ErrSec, uint64_t ErrOffset) { - if (Config->AllowMultipleDefinition) - return; - - Defined *D = cast<Defined>(Sym); - if (!D->Section || !ErrSec) { - error("duplicate symbol: " + toString(*Sym) + "\n>>> defined in " + - toString(Sym->File) + "\n>>> defined in " + toString(NewFile)); - return; - } - - // Construct and print an error message in the form of: - // - // ld.lld: error: duplicate symbol: foo - // >>> defined at bar.c:30 - // >>> bar.o (/home/alice/src/bar.o) - // >>> defined at baz.c:563 - // >>> baz.o in archive libbaz.a - auto *Sec1 = cast<InputSectionBase>(D->Section); - std::string Src1 = Sec1->getSrcMsg(*Sym, D->Value); - std::string Obj1 = Sec1->getObjMsg(D->Value); - std::string Src2 = ErrSec->getSrcMsg(*Sym, ErrOffset); - std::string Obj2 = ErrSec->getObjMsg(ErrOffset); - - std::string Msg = "duplicate symbol: " + toString(*Sym) + "\n>>> defined at "; - if (!Src1.empty()) - Msg += Src1 + "\n>>> "; - Msg += Obj1 + "\n>>> defined at "; - if (!Src2.empty()) - Msg += Src2 + "\n>>> "; - Msg += Obj2; - error(Msg); -} - -Defined *SymbolTable::addDefined(StringRef Name, uint8_t StOther, uint8_t Type, - uint64_t Value, uint64_t Size, uint8_t Binding, - SectionBase *Section, InputFile *File) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insert(Name, getVisibility(StOther), - /*CanOmitFromDynSym*/ false, File); - int Cmp = compareDefinedNonCommon(S, WasInserted, Binding, Section == nullptr, - Value, Name); - if (Cmp > 0) - replaceSymbol<Defined>(S, File, Name, Binding, StOther, Type, Value, Size, - Section); - else if (Cmp == 0) - reportDuplicate(S, File, dyn_cast_or_null<InputSectionBase>(Section), - Value); - return cast<Defined>(S); -} - -template <typename ELFT> -void SymbolTable::addShared(StringRef Name, SharedFile<ELFT> &File, - const typename ELFT::Sym &Sym, uint32_t Alignment, - uint32_t VerdefIndex) { - // 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, STV_DEFAULT, - /*CanOmitFromDynSym*/ true, &File); - // Make sure we preempt DSO symbols with default visibility. - if (Sym.getVisibility() == STV_DEFAULT) - S->ExportDynamic = true; - - // An undefined symbol with non default visibility must be satisfied - // in the same DSO. - auto Replace = [&](uint8_t Binding) { - replaceSymbol<SharedSymbol>(S, File, Name, Binding, Sym.st_other, - Sym.getType(), Sym.st_value, Sym.st_size, - Alignment, VerdefIndex); - }; - - if (WasInserted) - Replace(Sym.getBinding()); - else if (S->Visibility == STV_DEFAULT && (S->isUndefined() || S->isLazy())) - Replace(S->Binding); -} - -Symbol *SymbolTable::addBitcode(StringRef Name, uint8_t Binding, - uint8_t StOther, uint8_t Type, - bool CanOmitFromDynSym, BitcodeFile &F) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = - insert(Name, getVisibility(StOther), CanOmitFromDynSym, &F); - int Cmp = compareDefinedNonCommon(S, WasInserted, Binding, - /*IsAbs*/ false, /*Value*/ 0, Name); - if (Cmp > 0) - replaceSymbol<Defined>(S, &F, Name, Binding, StOther, Type, 0, 0, nullptr); - else if (Cmp == 0) - reportDuplicate(S, &F, nullptr, 0); - return S; -} - -Symbol *SymbolTable::find(StringRef Name) { - auto It = SymMap.find(CachedHashStringRef(Name)); - if (It == SymMap.end()) +Symbol *SymbolTable::find(StringRef name) { + auto it = symMap.find(CachedHashStringRef(name)); + if (it == symMap.end()) return nullptr; - if (It->second == -1) + Symbol *sym = symVector[it->second]; + if (sym->isPlaceholder()) return nullptr; - return SymVector[It->second]; + return sym; } -template <class ELFT> -void SymbolTable::addLazyArchive(StringRef Name, ArchiveFile &File, - const object::Archive::Symbol Sym) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insertName(Name); - if (WasInserted) { - replaceSymbol<LazyArchive>(S, File, STT_NOTYPE, Sym); - return; - } - if (!S->isUndefined()) - return; - - // An undefined weak will not fetch archive members. See comment on Lazy in - // Symbols.h for the details. - if (S->isWeak()) { - replaceSymbol<LazyArchive>(S, File, S->Type, Sym); - S->Binding = STB_WEAK; - return; - } - - if (InputFile *F = File.fetch(Sym)) - addFile<ELFT>(F); -} - -template <class ELFT> -void SymbolTable::addLazyObject(StringRef Name, LazyObjFile &File) { - Symbol *S; - bool WasInserted; - std::tie(S, WasInserted) = insertName(Name); - if (WasInserted) { - replaceSymbol<LazyObject>(S, File, STT_NOTYPE, Name); - return; - } - if (!S->isUndefined()) - return; - - // An undefined weak will not fetch archive members. See comment on Lazy in - // Symbols.h for the details. - if (S->isWeak()) { - replaceSymbol<LazyObject>(S, File, S->Type, Name); - S->Binding = STB_WEAK; - return; - } - - if (InputFile *F = File.fetch()) - addFile<ELFT>(F); -} - -template <class ELFT> void SymbolTable::fetchLazy(Symbol *Sym) { - if (auto *S = dyn_cast<LazyArchive>(Sym)) { - if (InputFile *File = S->fetch()) - addFile<ELFT>(File); - return; - } - - auto *S = cast<LazyObject>(Sym); - if (InputFile *File = cast<LazyObjFile>(S->File)->fetch()) - addFile<ELFT>(File); -} - -// Initialize DemangledSyms with a map from demangled symbols to symbol +// Initialize demangledSyms with a map from demangled symbols to symbol // objects. Used to handle "extern C++" directive in version scripts. // // The map will contain all demangled symbols. That can be very large, @@ -614,204 +113,156 @@ template <class ELFT> void SymbolTable::fetchLazy(Symbol *Sym) { // So, if "extern C++" feature is used, we need to demangle all known // symbols. StringMap<std::vector<Symbol *>> &SymbolTable::getDemangledSyms() { - if (!DemangledSyms) { - DemangledSyms.emplace(); - for (Symbol *Sym : SymVector) { - if (!Sym->isDefined()) + if (!demangledSyms) { + demangledSyms.emplace(); + for (Symbol *sym : symVector) { + if (!sym->isDefined() && !sym->isCommon()) continue; - if (Optional<std::string> S = demangleItanium(Sym->getName())) - (*DemangledSyms)[*S].push_back(Sym); + if (Optional<std::string> s = demangleItanium(sym->getName())) + (*demangledSyms)[*s].push_back(sym); else - (*DemangledSyms)[Sym->getName()].push_back(Sym); + (*demangledSyms)[sym->getName()].push_back(sym); } } - return *DemangledSyms; + return *demangledSyms; } -std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion Ver) { - if (Ver.IsExternCpp) - return getDemangledSyms().lookup(Ver.Name); - if (Symbol *B = find(Ver.Name)) - if (B->isDefined()) - return {B}; +std::vector<Symbol *> SymbolTable::findByVersion(SymbolVersion ver) { + if (ver.isExternCpp) + return getDemangledSyms().lookup(ver.name); + if (Symbol *b = find(ver.name)) + if (b->isDefined() || b->isCommon()) + return {b}; return {}; } -std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion Ver) { - std::vector<Symbol *> Res; - StringMatcher M(Ver.Name); +std::vector<Symbol *> SymbolTable::findAllByVersion(SymbolVersion ver) { + std::vector<Symbol *> res; + StringMatcher m(ver.name); - if (Ver.IsExternCpp) { - for (auto &P : getDemangledSyms()) - if (M.match(P.first())) - Res.insert(Res.end(), P.second.begin(), P.second.end()); - return Res; + if (ver.isExternCpp) { + for (auto &p : getDemangledSyms()) + if (m.match(p.first())) + res.insert(res.end(), p.second.begin(), p.second.end()); + return res; } - for (Symbol *Sym : SymVector) - if (Sym->isDefined() && M.match(Sym->getName())) - Res.push_back(Sym); - return Res; -} - -// If there's only one anonymous version definition in a version -// script file, the script does not actually define any symbol version, -// but just specifies symbols visibilities. -void SymbolTable::handleAnonymousVersion() { - for (SymbolVersion &Ver : Config->VersionScriptGlobals) - assignExactVersion(Ver, VER_NDX_GLOBAL, "global"); - for (SymbolVersion &Ver : Config->VersionScriptGlobals) - assignWildcardVersion(Ver, VER_NDX_GLOBAL); - for (SymbolVersion &Ver : Config->VersionScriptLocals) - assignExactVersion(Ver, VER_NDX_LOCAL, "local"); - for (SymbolVersion &Ver : Config->VersionScriptLocals) - assignWildcardVersion(Ver, VER_NDX_LOCAL); + for (Symbol *sym : symVector) + if ((sym->isDefined() || sym->isCommon()) && m.match(sym->getName())) + res.push_back(sym); + return res; } // Handles -dynamic-list. void SymbolTable::handleDynamicList() { - for (SymbolVersion &Ver : Config->DynamicList) { - std::vector<Symbol *> Syms; - if (Ver.HasWildcard) - Syms = findAllByVersion(Ver); + for (SymbolVersion &ver : config->dynamicList) { + std::vector<Symbol *> syms; + if (ver.hasWildcard) + syms = findAllByVersion(ver); else - Syms = findByVersion(Ver); + syms = findByVersion(ver); - for (Symbol *B : Syms) { - if (!Config->Shared) - B->ExportDynamic = true; - else if (B->includeInDynsym()) - B->IsPreemptible = true; + for (Symbol *b : syms) { + if (!config->shared) + b->exportDynamic = true; + else if (b->includeInDynsym()) + b->isPreemptible = true; } } } // Set symbol versions to symbols. This function handles patterns // containing no wildcard characters. -void SymbolTable::assignExactVersion(SymbolVersion Ver, uint16_t VersionId, - StringRef VersionName) { - if (Ver.HasWildcard) +void SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId, + StringRef versionName) { + if (ver.hasWildcard) return; // Get a list of symbols which we need to assign the version to. - std::vector<Symbol *> Syms = findByVersion(Ver); - if (Syms.empty()) { - if (!Config->UndefinedVersion) - error("version script assignment of '" + VersionName + "' to symbol '" + - Ver.Name + "' failed: symbol not defined"); + std::vector<Symbol *> syms = findByVersion(ver); + if (syms.empty()) { + if (!config->undefinedVersion) + error("version script assignment of '" + versionName + "' to symbol '" + + ver.name + "' failed: symbol not defined"); return; } + auto getName = [](uint16_t ver) -> std::string { + if (ver == VER_NDX_LOCAL) + return "VER_NDX_LOCAL"; + if (ver == VER_NDX_GLOBAL) + return "VER_NDX_GLOBAL"; + return ("version '" + config->versionDefinitions[ver - 2].name + "'").str(); + }; + // Assign the version. - for (Symbol *Sym : Syms) { + for (Symbol *sym : syms) { // Skip symbols containing version info because symbol versions // specified by symbol names take precedence over version scripts. // See parseSymbolVersion(). - if (Sym->getName().contains('@')) + if (sym->getName().contains('@')) + continue; + + if (sym->versionId == config->defaultSymbolVersion) + sym->versionId = versionId; + if (sym->versionId == versionId) continue; - if (Sym->VersionId != Config->DefaultSymbolVersion && - Sym->VersionId != VersionId) - error("duplicate symbol '" + Ver.Name + "' in version script"); - Sym->VersionId = VersionId; + warn("attempt to reassign symbol '" + ver.name + "' of " + + getName(sym->versionId) + " to " + getName(versionId)); } } -void SymbolTable::assignWildcardVersion(SymbolVersion Ver, uint16_t VersionId) { - if (!Ver.HasWildcard) +void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId) { + if (!ver.hasWildcard) return; // Exact matching takes precendence over fuzzy matching, // so we set a version to a symbol only if no version has been assigned // to the symbol. This behavior is compatible with GNU. - for (Symbol *B : findAllByVersion(Ver)) - if (B->VersionId == Config->DefaultSymbolVersion) - B->VersionId = VersionId; + for (Symbol *b : findAllByVersion(ver)) + if (b->versionId == config->defaultSymbolVersion) + b->versionId = versionId; } -// This function processes version scripts by updating VersionId +// This function processes version scripts by updating the versionId // member of symbols. +// If there's only one anonymous version definition in a version +// script file, the script does not actually define any symbol version, +// but just specifies symbols visibilities. void SymbolTable::scanVersionScript() { - // Handle edge cases first. - handleAnonymousVersion(); - handleDynamicList(); - - // Now we have version definitions, so we need to set version ids to symbols. - // Each version definition has a glob pattern, and all symbols that match - // with the pattern get that version. - // First, we assign versions to exact matching symbols, // i.e. version definitions not containing any glob meta-characters. - for (VersionDefinition &V : Config->VersionDefinitions) - for (SymbolVersion &Ver : V.Globals) - assignExactVersion(Ver, V.Id, V.Name); + for (SymbolVersion &ver : config->versionScriptGlobals) + assignExactVersion(ver, VER_NDX_GLOBAL, "global"); + for (SymbolVersion &ver : config->versionScriptLocals) + assignExactVersion(ver, VER_NDX_LOCAL, "local"); + for (VersionDefinition &v : config->versionDefinitions) + for (SymbolVersion &ver : v.globals) + assignExactVersion(ver, v.id, v.name); // Next, we assign versions to fuzzy matching symbols, // i.e. version definitions containing glob meta-characters. + for (SymbolVersion &ver : config->versionScriptGlobals) + assignWildcardVersion(ver, VER_NDX_GLOBAL); + for (SymbolVersion &ver : config->versionScriptLocals) + assignWildcardVersion(ver, VER_NDX_LOCAL); + // Note that because the last match takes precedence over previous matches, // we iterate over the definitions in the reverse order. - for (VersionDefinition &V : llvm::reverse(Config->VersionDefinitions)) - for (SymbolVersion &Ver : V.Globals) - assignWildcardVersion(Ver, V.Id); + for (VersionDefinition &v : llvm::reverse(config->versionDefinitions)) + for (SymbolVersion &ver : v.globals) + assignWildcardVersion(ver, v.id); // Symbol themselves might know their versions because symbols // can contain versions in the form of <name>@<version>. // Let them parse and update their names to exclude version suffix. - for (Symbol *Sym : SymVector) - Sym->parseSymbolVersion(); -} + for (Symbol *sym : symVector) + sym->parseSymbolVersion(); -template void SymbolTable::addFile<ELF32LE>(InputFile *); -template void SymbolTable::addFile<ELF32BE>(InputFile *); -template void SymbolTable::addFile<ELF64LE>(InputFile *); -template void SymbolTable::addFile<ELF64BE>(InputFile *); - -template Symbol *SymbolTable::addUndefined<ELF32LE>(StringRef, uint8_t, uint8_t, - uint8_t, bool, InputFile *); -template Symbol *SymbolTable::addUndefined<ELF32BE>(StringRef, uint8_t, uint8_t, - uint8_t, bool, InputFile *); -template Symbol *SymbolTable::addUndefined<ELF64LE>(StringRef, uint8_t, uint8_t, - uint8_t, bool, InputFile *); -template Symbol *SymbolTable::addUndefined<ELF64BE>(StringRef, uint8_t, uint8_t, - uint8_t, bool, InputFile *); - -template void SymbolTable::addCombinedLTOObject<ELF32LE>(); -template void SymbolTable::addCombinedLTOObject<ELF32BE>(); -template void SymbolTable::addCombinedLTOObject<ELF64LE>(); -template void SymbolTable::addCombinedLTOObject<ELF64BE>(); - -template void -SymbolTable::addLazyArchive<ELF32LE>(StringRef, ArchiveFile &, - const object::Archive::Symbol); -template void -SymbolTable::addLazyArchive<ELF32BE>(StringRef, ArchiveFile &, - const object::Archive::Symbol); -template void -SymbolTable::addLazyArchive<ELF64LE>(StringRef, ArchiveFile &, - const object::Archive::Symbol); -template void -SymbolTable::addLazyArchive<ELF64BE>(StringRef, ArchiveFile &, - const object::Archive::Symbol); - -template void SymbolTable::addLazyObject<ELF32LE>(StringRef, LazyObjFile &); -template void SymbolTable::addLazyObject<ELF32BE>(StringRef, LazyObjFile &); -template void SymbolTable::addLazyObject<ELF64LE>(StringRef, LazyObjFile &); -template void SymbolTable::addLazyObject<ELF64BE>(StringRef, LazyObjFile &); - -template void SymbolTable::fetchLazy<ELF32LE>(Symbol *); -template void SymbolTable::fetchLazy<ELF32BE>(Symbol *); -template void SymbolTable::fetchLazy<ELF64LE>(Symbol *); -template void SymbolTable::fetchLazy<ELF64BE>(Symbol *); - -template void SymbolTable::addShared<ELF32LE>(StringRef, SharedFile<ELF32LE> &, - const typename ELF32LE::Sym &, - uint32_t Alignment, uint32_t); -template void SymbolTable::addShared<ELF32BE>(StringRef, SharedFile<ELF32BE> &, - const typename ELF32BE::Sym &, - uint32_t Alignment, uint32_t); -template void SymbolTable::addShared<ELF64LE>(StringRef, SharedFile<ELF64LE> &, - const typename ELF64LE::Sym &, - uint32_t Alignment, uint32_t); -template void SymbolTable::addShared<ELF64BE>(StringRef, SharedFile<ELF64BE> &, - const typename ELF64BE::Sym &, - uint32_t Alignment, uint32_t); + // isPreemptible is false at this point. To correctly compute the binding of a + // Defined (which is used by includeInDynsym()), we need to know if it is + // VER_NDX_LOCAL or not. If defaultSymbolVersion is VER_NDX_LOCAL, we should + // compute symbol versions before handling --dynamic-list. + handleDynamicList(); +} |