diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:48:50 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2016-07-23 20:48:50 +0000 |
| commit | 1c98619801a5705c688e683be3ef9d70169a0686 (patch) | |
| tree | 8422105cd1a94c368315f2db16b9ac746cf7c000 /ELF/SymbolTable.cpp | |
| parent | f4f3ce4613680903220815690ad79fc7ba0a2e26 (diff) | |
Notes
Diffstat (limited to 'ELF/SymbolTable.cpp')
| -rw-r--r-- | ELF/SymbolTable.cpp | 715 |
1 files changed, 577 insertions, 138 deletions
diff --git a/ELF/SymbolTable.cpp b/ELF/SymbolTable.cpp index 65f5dff9d7a3..78c1298df427 100644 --- a/ELF/SymbolTable.cpp +++ b/ELF/SymbolTable.cpp @@ -17,7 +17,11 @@ #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; @@ -25,40 +29,48 @@ using namespace llvm::object; using namespace llvm::ELF; using namespace lld; -using namespace lld::elf2; +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 void checkCompatibility(InputFile *FileP) { - auto *F = dyn_cast<ELFFileBase<ELFT>>(FileP); - if (!F) - return; - if (F->getELFKind() == Config->EKind && F->getEMachine() == Config->EMachine) - return; +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(); - checkCompatibility<ELFT>(FileP); + if (!isCompatible<ELFT>(FileP)) + return; // .a file if (auto *F = dyn_cast<ArchiveFile>(FileP)) { ArchiveFiles.emplace_back(cast<ArchiveFile>(File.release())); - F->parse(); - for (Lazy &Sym : F->getLazySymbols()) - addLazy(&Sym); + 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. @@ -68,189 +80,443 @@ void SymbolTable<ELFT>::addFile(std::unique_ptr<InputFile> File) { SharedFiles.emplace_back(cast<SharedFile<ELFT>>(File.release())); F->parseRest(); - for (SharedSymbol<ELFT> &B : F->getSharedSymbols()) - resolve(&B); return; } - // .o file + // 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); - for (SymbolBody *B : F->getSymbols()) - resolve(B); } -// Add an undefined symbol. -template <class ELFT> -SymbolBody *SymbolTable<ELFT>::addUndefined(StringRef Name) { - auto *Sym = new (Alloc) Undefined(Name, false, STV_DEFAULT, false); - resolve(Sym); - return Sym; -} +// 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; -// Add an undefined symbol. Unlike addUndefined, that symbol -// doesn't have to be resolved, thus "opt" (optional). -template <class ELFT> -SymbolBody *SymbolTable<ELFT>::addUndefinedOpt(StringRef Name) { - auto *Sym = new (Alloc) Undefined(Name, false, STV_HIDDEN, true); - resolve(Sym); - return Sym; -} + // 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(); -template <class ELFT> -SymbolBody *SymbolTable<ELFT>::addAbsolute(StringRef Name, Elf_Sym &ESym) { - // Pass nullptr because absolute symbols have no corresponding input sections. - auto *Sym = new (Alloc) DefinedRegular<ELFT>(Name, ESym, nullptr); - resolve(Sym); - return Sym; + // 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> -SymbolBody *SymbolTable<ELFT>::addSynthetic(StringRef Name, - OutputSectionBase<ELFT> &Section, - uintX_t Value) { - auto *Sym = new (Alloc) DefinedSynthetic<ELFT>(Name, Value, Section); - resolve(Sym); - return Sym; +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 it is not recorded to the output -// file's symbol table. Such symbols are useful for some linker-defined symbols. +// linker-synthesized defined symbol, but is only defined if needed. template <class ELFT> -SymbolBody *SymbolTable<ELFT>::addIgnored(StringRef Name) { - return addAbsolute(Name, ElfSym<ELFT>::IgnoredWeak); +DefinedRegular<ELFT> *SymbolTable<ELFT>::addIgnored(StringRef Name, + uint8_t Visibility) { + if (!find(Name)) + return nullptr; + return addAbsolute(Name, Visibility); } -// The 'strong' variant of the addIgnored. Adds symbol which has a global -// binding and cannot be substituted. -template <class ELFT> -SymbolBody *SymbolTable<ELFT>::addIgnoredStrong(StringRef Name) { - return addAbsolute(Name, ElfSym<ELFT>::Ignored); +// 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) { - if (Symtab.count(Name) == 0) + SymbolBody *B = find(Name); + if (!B) return; StringSaver Saver(Alloc); - Symbol *Sym = addUndefined(Name)->getSymbol(); - Symbol *Real = addUndefined(Saver.save("__real_" + Name))->getSymbol(); - Symbol *Wrap = addUndefined(Saver.save("__wrap_" + Name))->getSymbol(); - Real->Body = Sym->Body; - Sym->Body = Wrap->Body; + 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); } -// Returns a file from which symbol B was created. -// If B does not belong to any file, returns a nullptr. +// Find an existing symbol or create and insert a new one. template <class ELFT> -ELFFileBase<ELFT> *SymbolTable<ELFT>::findFile(SymbolBody *B) { - for (const std::unique_ptr<ObjectFile<ELFT>> &F : ObjectFiles) { - ArrayRef<SymbolBody *> Syms = F->getSymbols(); - if (std::find(Syms.begin(), Syms.end(), B) != Syms.end()) - return F.get(); +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 nullptr; + 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 <class ELFT> -std::string SymbolTable<ELFT>::conflictMsg(SymbolBody *Old, SymbolBody *New) { - ELFFileBase<ELFT> *OldFile = findFile(Old); - ELFFileBase<ELFT> *NewFile = findFile(New); +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); +} - StringRef Sym = Old->getName(); - StringRef F1 = OldFile ? OldFile->getName() : "(internal)"; - StringRef F2 = NewFile ? NewFile->getName() : "(internal)"; - return (Sym + " in " + F1 + " and " + F2).str(); +template <class ELFT> Symbol *SymbolTable<ELFT>::addUndefined(StringRef Name) { + return addUndefined(Name, STB_GLOBAL, STV_DEFAULT, /*Type*/ 0, + /*CanOmitFromDynSym*/ false, /*File*/ nullptr); } -// This function resolves conflicts if there's an existing symbol with -// the same name. Decisions are made based on symbol type. -template <class ELFT> void SymbolTable<ELFT>::resolve(SymbolBody *New) { - Symbol *Sym = insert(New); - if (Sym->Body == New) - return; +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; +} - SymbolBody *Existing = Sym->Body; +// 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; +} - if (Lazy *L = dyn_cast<Lazy>(Existing)) { - if (auto *Undef = dyn_cast<Undefined>(New)) { - addMemberFile(Undef, L); - return; +// 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; } - // Found a definition for something also in an archive. - // Ignore the archive definition. - Sym->Body = New; - return; + + 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; +} - if (New->isTls() != Existing->isTls()) - error("TLS attribute mismatch for symbol: " + conflictMsg(Existing, New)); +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); +} - // compare() returns -1, 0, or 1 if the lhs symbol is less preferable, - // equivalent (conflicting), or more preferable, respectively. - int Comp = Existing->compare<ELFT>(New); - if (Comp == 0) { - std::string S = "duplicate symbol: " + conflictMsg(Existing, New); - if (!Config->AllowMultipleDefinition) - error(S); - warning(S); - return; +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; } - if (Comp < 0) - Sym->Body = New; } -// Find an existing symbol or create and insert a new one. -template <class ELFT> Symbol *SymbolTable<ELFT>::insert(SymbolBody *New) { - StringRef Name = New->getName(); - Symbol *&Sym = Symtab[Name]; - if (!Sym) - Sym = new (Alloc) Symbol{New}; - New->setBackref(Sym); - return Sym; +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; - return It->second->Body; + 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>::addLazy(Lazy *L) { - Symbol *Sym = insert(L); - if (Sym->Body == L) +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 (auto *Undef = dyn_cast<Undefined>(Sym->Body)) { - Sym->Body = L; - addMemberFile(Undef, L); } + 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>::addMemberFile(Undefined *Undef, Lazy *L) { - // 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 set UsedInRegularObj in a similar way to what is done with shared - // symbols and mark it as weak to reduce how many special cases are needed. - if (Undef->isWeak()) { - L->setUsedInRegularObj(); - L->setWeak(); +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)); + } +} - // Fetch a member file that has the definition for L. - // getMember returns nullptr if the member was already read from the library. - if (std::unique_ptr<InputFile> File = L->getMember()) - addFile(std::move(File)); +// 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 @@ -265,10 +531,183 @@ template <class ELFT> void SymbolTable<ELFT>::scanShlibUndefined() { for (StringRef U : File->getUndefinedSymbols()) if (SymbolBody *Sym = find(U)) if (Sym->isDefined()) - Sym->setUsedInDynamicReloc(); + 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 elf2::SymbolTable<ELF32LE>; -template class elf2::SymbolTable<ELF32BE>; -template class elf2::SymbolTable<ELF64LE>; -template class elf2::SymbolTable<ELF64BE>; +template class elf::SymbolTable<ELF32LE>; +template class elf::SymbolTable<ELF32BE>; +template class elf::SymbolTable<ELF64LE>; +template class elf::SymbolTable<ELF64BE>; |
