llvm-project/lld/ELF/SymbolTable.cpp

274 lines
9.2 KiB
C++
Raw Normal View History

//===- SymbolTable.cpp ----------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
2015-10-14 03:51:57 +08:00
//
// Symbol table is a bag of all known symbols. We put all symbols of
2016-01-06 04:47:37 +08:00
// all input files to the symbol table. The symbol table is basically
2015-10-14 03:51:57 +08:00
// a hash table with the logic to resolve symbol name conflicts using
// the symbol types.
//
//===----------------------------------------------------------------------===//
#include "SymbolTable.h"
#include "Config.h"
#include "LinkerScript.h"
#include "Symbols.h"
#include "SyntheticSections.h"
#include "lld/Common/ErrorHandler.h"
#include "lld/Common/Memory.h"
#include "lld/Common/Strings.h"
#include "llvm/ADT/STLExtras.h"
using namespace llvm;
using namespace llvm::object;
using namespace llvm::ELF;
using namespace lld;
2016-02-28 08:25:54 +08:00
using namespace lld::elf;
SymbolTable *elf::Symtab;
// 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});
}
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())];
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);
}
// 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();
2018-10-10 06:44:42 +08:00
IsNew = true;
Traced = true;
}
if (!IsNew)
return SymVector[SymIndex];
Symbol *Sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
SymVector.push_back(Sym);
Speed up --start-lib and --end-lib. --{start,end}-lib give files grouped by the options the archive file semantics. That is, each object file between them acts as if it were in an archive file whose sole member is the file. Therefore, files between --{start,end}-lib are linked to the final output only if they are needed to resolve some undefined symbols. Previously, the feature was implemented this way: 1. We read a symbol table and insert defined symbols to the symbol table as lazy symbols. 2. If an undefind symbol is resolved to a lazy symbol, that lazy symbol instantiate ObjFile class for that symbol, which re-insert all defined symbols to the symbol table. So, if an ObjFile is instantiated, defined symbols are inserted to the symbol table twice. Since inserting long symbol names is not cheap, there's a room to optimize here. This patch optimzies it. Now, LazyObjFile remembers symbol handles and passed them over to a new ObjFile instance, so that the ObjFile doesn't insert the same strings. Here is a quick benchmark to link clang. "Original" is the original lld with unmodified command line options. For "Case 1" and "Case 2", I extracted all files from archive files and replace .a's in a command line with .o's wrapped with --{start,end}-lib. I used the original lld for Case 1" and use this patch for Case 2. Original: 5.892 Case 1: 6.001 (+1.8%) Case 2: 5.701 (-3.2%) So, interestingly, --{start,end}-lib are now faster than the regular linking scheme with archive files. That's perhaps not too surprising, though, because for regular archive files, we look up the symbol table with the same string twice. Differential Revision: https://reviews.llvm.org/D62188 llvm-svn: 361473
2019-05-23 17:53:30 +08:00
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->Traced = Traced;
Sym->ScriptDefined = false;
return Sym;
}
Symbol *SymbolTable::addSymbol(const Symbol &New) {
Symbol *Sym = Symtab->insert(New.getName());
Sym->resolve(New);
return Sym;
ELF: New symbol table design. This patch implements a new design for the symbol table that stores SymbolBodies within a memory region of the Symbol object. Symbols are mutated by constructing SymbolBodies in place over existing SymbolBodies, rather than by mutating pointers. As mentioned in the initial proposal [1], this memory layout helps reduce the cache miss rate by improving memory locality. Performance numbers: old(s) new(s) Without debug info: chrome 7.178 6.432 (-11.5%) LLVMgold.so 0.505 0.502 (-0.5%) clang 0.954 0.827 (-15.4%) llvm-as 0.052 0.045 (-15.5%) With debug info: scylla 5.695 5.613 (-1.5%) clang 14.396 14.143 (-1.8%) Performance counter results show that the fewer required indirections is indeed the cause of the improved performance. For example, when linking chrome, stalled cycles decreases from 14,556,444,002 to 12,959,238,310, and instructions per cycle increases from 0.78 to 0.83. We are also executing many fewer instructions (15,516,401,933 down to 15,002,434,310), probably because we spend less time allocating SymbolBodies. The new mechanism by which symbols are added to the symbol table is by calling add* functions on the SymbolTable. In this patch, I handle local symbols by storing them inside "unparented" SymbolBodies. This is suboptimal, but if we do want to try to avoid allocating these SymbolBodies, we can probably do that separately. I also removed a few members from the SymbolBody class that were only being used to pass information from the input file to the symbol table. This patch implements the new design for the ELF linker only. I intend to prepare a similar patch for the COFF linker. [1] http://lists.llvm.org/pipermail/llvm-dev/2016-April/098832.html Differential Revision: http://reviews.llvm.org/D19752 llvm-svn: 268178
2016-05-01 12:55:03 +08:00
}
Symbol *SymbolTable::find(StringRef Name) {
auto It = SymMap.find(CachedHashStringRef(Name));
if (It == SymMap.end())
return nullptr;
if (It->second == -1)
return nullptr;
return SymVector[It->second];
}
// 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,
// and in LLD we generally want to avoid do anything for each symbol.
// Then, why are we doing this? Here's why.
//
// Users can use "extern C++ {}" directive to match against demangled
// C++ symbols. For example, you can write a pattern such as
// "llvm::*::foo(int, ?)". Obviously, there's no way to handle this
// other than trying to match a pattern against all demangled symbols.
// 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() && !Sym->isCommon())
continue;
if (Optional<std::string> S = demangleItanium(Sym->getName()))
(*DemangledSyms)[*S].push_back(Sym);
else
(*DemangledSyms)[Sym->getName()].push_back(Sym);
}
}
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() || B->isCommon())
return {B};
return {};
}
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;
}
for (Symbol *Sym : SymVector)
if ((Sym->isDefined() || Sym->isCommon()) && 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);
}
// Handles -dynamic-list.
void SymbolTable::handleDynamicList() {
for (SymbolVersion &Ver : Config->DynamicList) {
std::vector<Symbol *> Syms;
if (Ver.HasWildcard)
Syms = findAllByVersion(Ver);
else
Syms = findByVersion(Ver);
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)
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");
return;
}
// Assign the version.
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('@'))
continue;
if (Sym->VersionId != Config->DefaultSymbolVersion &&
Sym->VersionId != VersionId)
error("duplicate symbol '" + Ver.Name + "' in version script");
Sym->VersionId = VersionId;
}
}
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;
}
2016-09-03 06:15:08 +08:00
// This function processes version scripts by updating VersionId
// member of symbols.
void SymbolTable::scanVersionScript() {
// Handle edge cases first.
handleAnonymousVersion();
handleDynamicList();
2016-09-03 06:15:08 +08:00
// 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);
[ELF] - Fixed incorrect logic of version assignments when mixing wildcards with values matching. Previously we had incorrect logic here. Imagine we would have the next script: LIBSAMPLE_1.0 { global: a_2; local: *; }; LIBSAMPLE_2.0 { global: a*; }; According to previous logic it would assign version 1 to a_2 and then would try to reassign it to version 2 because of applying wildcard a*. And show a warning about that. Generally Ian Lance Tailor wrote about next rules that should be applied: (http://www.airs.com/blog/archives/300) Here are the current rules for gold: "If there is an exact match for the mangled name, we use it. If there is more than one exact match, we give a warning, and we use the first tag in the script which matches. If a symbol has an exact match as both global and local for the same version tag, we give an error. Otherwise, we look for an extern C++ or an extern Java exact match. If we find an exact match, we use it. If there is more than one exact match, we give a warning, and we use the first tag in the script which matches. If a symbol has an exact match as both global and local for the same version tag, we give an error. Otherwise, we look through the wildcard patterns, ignoring “*” patterns. We look through the version tags in reverse order. For each version tag, we look through the global patterns and then the local patterns. We use the first match we find (i.e., the last matching version tag in the file). Otherwise, we use the “*” pattern if there is one. We give a warning if there are multiple “*” patterns." Patch makes wildcard matching to be in revered order and to follow after the regular naming matching. Differential revision: http://reviews.llvm.org/D21894 llvm-svn: 274739
2016-07-07 15:45:27 +08:00
2016-09-03 06:15:08 +08:00
// Next, we assign versions to fuzzy matching symbols,
// i.e. version definitions containing glob meta-characters.
// 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);
// 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();
}