aboutsummaryrefslogtreecommitdiff
path: root/ELF/SymbolTable.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2016-07-23 20:48:50 +0000
committerDimitry Andric <dim@FreeBSD.org>2016-07-23 20:48:50 +0000
commit1c98619801a5705c688e683be3ef9d70169a0686 (patch)
tree8422105cd1a94c368315f2db16b9ac746cf7c000 /ELF/SymbolTable.cpp
parentf4f3ce4613680903220815690ad79fc7ba0a2e26 (diff)
Notes
Diffstat (limited to 'ELF/SymbolTable.cpp')
-rw-r--r--ELF/SymbolTable.cpp715
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>;