Versions can be assigned to symbols in two different ways.
One is the usual version scripts, and the other is special
symbol suffix '@'. If a symbol contains '@', the string after
that is considered to specify a version name.
Previously, we look for '@' for all symbols.
Anything that works on every symbol can be expensive because
the linker has to handle a lot of symbols. The search for '@'
was not an exception.
In this patch, I made two optimizations.
The first optimization is to handle '@' only when at least one
version is defined. If no versions are defined, no versions can
be assigned to any symbols, so it's waste of time to search for '@'.
The second optimization is to scan only suffixes of symbol names
instead of entire symbol names. Symbol names can be very long, but
symbol versions are usually short, so scanning entire symbol names
is waste of time, too.
There are some error cases which we no longer be able to detect
with this patch. I don't think it's a major drawback because they
are minor errors. Speed is more important.
This change improves LLD with debug info self-link time from
6.6993 seconds to 6.3426 seconds (or -5.3%).
Differential Revision: https://reviews.llvm.org/D22433
llvm-svn: 275711
bool FatalWarnings;
bool GcSections;
bool GnuHash = false;
- bool HasVersionScript = false;
bool ICF;
bool Mips64EL = false;
bool NoGnuUnique;
for (auto *Arg : Args.filtered(OPT_export_dynamic_symbol))
Config->DynamicList.push_back(Arg->getValue());
- if (auto *Arg = Args.getLastArg(OPT_version_script)) {
- Config->HasVersionScript = true;
+ if (auto *Arg = Args.getLastArg(OPT_version_script))
if (Optional<MemoryBufferRef> Buffer = readFile(Arg->getValue()))
parseVersionScript(*Buffer);
- }
for (auto *Arg : Args.filtered(OPT_trace_symbol))
Config->TraceSymbol.insert(Arg->getValue());
Symtab.scanShlibUndefined();
Symtab.scanDynamicList();
Symtab.scanVersionScript();
+ Symtab.scanSymbolVersions();
Symtab.traceDefined();
Symtab.addCombinedLtoObject();
return std::min(VA, VB);
}
-// A symbol version may be included in a symbol name as a suffix after '@'.
-// This function parses that part and returns a version ID number.
-static uint16_t getVersionId(Symbol *Sym, StringRef Name) {
- size_t VersionBegin = Name.find('@');
- if (VersionBegin == StringRef::npos)
- return Config->DefaultSymbolVersion;
-
- // If symbol name contains '@' or '@@' we can assign its version id right
- // here. '@@' means the default version. It is usually the most recent one.
- // VERSYM_HIDDEN flag should be set for all non-default versions.
- StringRef Version = Name.drop_front(VersionBegin + 1);
- bool Default = Version.startswith("@");
- if (Default)
- Version = Version.drop_front();
-
- for (VersionDefinition &V : Config->VersionDefinitions)
- if (V.Name == Version)
- return Default ? V.Id : (V.Id | VERSYM_HIDDEN);
-
-
- // If we are not building shared and version script
- // is not specified, then it is not a error, it is
- // in common not to use script for linking executables.
- // In this case we just create new version.
- if (!Config->Shared && !Config->HasVersionScript) {
- size_t Id = defineSymbolVersion(Version);
- return Default ? Id : (Id | VERSYM_HIDDEN);
- }
-
- error("symbol " + Name + " has undefined version " + Version);
- return 0;
-}
-
// Find an existing symbol or create and insert a new one.
template <class ELFT>
std::pair<Symbol *, bool> SymbolTable<ELFT>::insert(StringRef Name) {
Sym->Visibility = STV_DEFAULT;
Sym->IsUsedInRegularObj = false;
Sym->ExportDynamic = false;
- Sym->VersionId = getVersionId(Sym, Name);
- Sym->VersionedName =
- Sym->VersionId != VER_NDX_LOCAL && Sym->VersionId != VER_NDX_GLOBAL;
+ Sym->VersionId = Config->DefaultSymbolVersion;
SymVector.push_back(Sym);
} else {
Sym = SymVector[P.first->second];
}
}
+// 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;
+ }
+}
+
// Print the module names which define the notified
// symbols provided through -y or --trace-symbol option.
template <class ELFT> void SymbolTable<ELFT>::traceDefined() {
void scanShlibUndefined();
void scanDynamicList();
void scanVersionScript();
+ void scanSymbolVersions();
void traceDefined();
SymbolBody *find(StringRef Name);
StringRef SymbolBody::getName() const {
assert(!isLocal());
- StringRef S = StringRef(Name.S, Name.Len);
- if (!symbol()->VersionedName)
- return S;
- return S.substr(0, S.find('@'));
+ return StringRef(Name.S, Name.Len);
+}
+
+void SymbolBody::setName(StringRef S) {
+ Name.S = S.data();
+ Name.Len = S.size();
}
// Returns true if a symbol can be replaced at load-time by a symbol
bool isLocal() const { return IsLocal; }
bool isPreemptible() const;
- // Returns the symbol name.
StringRef getName() const;
+ void setName(StringRef S);
+
uint32_t getNameOffset() const {
assert(isLocal());
return NameOffset;
// --export-dynamic, and by dynamic lists.
unsigned ExportDynamic : 1;
- // If this flag is true then symbol name also contains version name. Such
- // name can have single or double symbol @. Double means that version is
- // used as default. Single signals about depricated symbol version.
- unsigned VersionedName : 1;
-
bool includeInDynsym() const;
bool isWeak() const { return Binding == llvm::ELF::STB_WEAK; }
# EXE-NEXT: }
# EXE-NEXT: }
-# RUN: not ld.lld -shared %t1 -o %terr.so 2>&1 | \
-# RUN: FileCheck -check-prefix=ERR1 %s
-# ERR1: symbol b@@LIBSAMPLE_2.0 has undefined version LIBSAMPLE_2.0
-
.globl _start
_start:
callq a
+++ /dev/null
-# REQUIRES: x86
-
-# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1
-# RUN: ld.lld %t1 -o %t
-# RUN: llvm-readobj -t -V -dyn-symbols %t | FileCheck --check-prefix=EXE %s
-
-# EXE: Symbols [
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name:
-# EXE-NEXT: Value: 0x0
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Local
-# EXE-NEXT: Type: None (0x0)
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: Undefined (0x0)
-# EXE-NEXT: }
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: _start
-# EXE-NEXT: Value: 0x11004
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Global
-# EXE-NEXT: Type: None
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: .text
-# EXE-NEXT: }
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: a
-# EXE-NEXT: Value: 0x11000
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Global
-# EXE-NEXT: Type: Function
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: .text
-# EXE-NEXT: }
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: b
-# EXE-NEXT: Value: 0x11002
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Global
-# EXE-NEXT: Type: Function
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: .text
-# EXE-NEXT: }
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: b
-# EXE-NEXT: Value: 0x11001
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Global
-# EXE-NEXT: Type: Function
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: .text
-# EXE-NEXT: }
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: b_1
-# EXE-NEXT: Value: 0x11001
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Global
-# EXE-NEXT: Type: Function
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: .text
-# EXE-NEXT: }
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: b_2
-# EXE-NEXT: Value: 0x11002
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Global
-# EXE-NEXT: Type: Function
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: .text
-# EXE-NEXT: }
-# EXE-NEXT: Symbol {
-# EXE-NEXT: Name: c
-# EXE-NEXT: Value: 0x11003
-# EXE-NEXT: Size: 0
-# EXE-NEXT: Binding: Global
-# EXE-NEXT: Type: Function
-# EXE-NEXT: Other: 0
-# EXE-NEXT: Section: .text
-# EXE-NEXT: }
-# EXE-NEXT: ]
-# EXE-NEXT: DynamicSymbols [
-# EXE-NEXT: ]
-# EXE-NEXT: Version symbols {
-# EXE-NEXT: }
-# EXE-NEXT: SHT_GNU_verdef {
-# EXE-NEXT: }
-# EXE-NEXT: SHT_GNU_verneed {
-# EXE-NEXT: }
-
-# RUN: ld.lld -pie --export-dynamic %t1 -o %t2
-# RUN: llvm-readobj -t -V -dyn-symbols %t2 | \
-# RUN: FileCheck --check-prefix=EXEDYN %s
-
-# EXEDYN: DynamicSymbols [
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Name: @
-# EXEDYN-NEXT: Value: 0x0
-# EXEDYN-NEXT: Size: 0
-# EXEDYN-NEXT: Binding: Local
-# EXEDYN-NEXT: Type: None
-# EXEDYN-NEXT: Other: 0
-# EXEDYN-NEXT: Section: Undefined
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Name: _start@
-# EXEDYN-NEXT: Value: 0x1004
-# EXEDYN-NEXT: Size: 0
-# EXEDYN-NEXT: Binding: Global
-# EXEDYN-NEXT: Type: None
-# EXEDYN-NEXT: Other: 0
-# EXEDYN-NEXT: Section: .text
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Name: a@
-# EXEDYN-NEXT: Value: 0x1000
-# EXEDYN-NEXT: Size: 0
-# EXEDYN-NEXT: Binding: Global
-# EXEDYN-NEXT: Type: Function
-# EXEDYN-NEXT: Other: 0
-# EXEDYN-NEXT: Section: .text
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Name: b@@LIBSAMPLE_2.0
-# EXEDYN-NEXT: Value: 0x1002
-# EXEDYN-NEXT: Size: 0
-# EXEDYN-NEXT: Binding: Global
-# EXEDYN-NEXT: Type: Function
-# EXEDYN-NEXT: Other: 0
-# EXEDYN-NEXT: Section: .text
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Name: b@LIBSAMPLE_1.0
-# EXEDYN-NEXT: Value: 0x1001
-# EXEDYN-NEXT: Size: 0
-# EXEDYN-NEXT: Binding: Global
-# EXEDYN-NEXT: Type: Function
-# EXEDYN-NEXT: Other: 0
-# EXEDYN-NEXT: Section: .text
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Name: b_1@
-# EXEDYN-NEXT: Value: 0x1001
-# EXEDYN-NEXT: Size: 0
-# EXEDYN-NEXT: Binding: Global
-# EXEDYN-NEXT: Type: Function
-# EXEDYN-NEXT: Other: 0
-# EXEDYN-NEXT: Section: .text
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Name: b_2@
-# EXEDYN-NEXT: Value: 0x1002
-# EXEDYN-NEXT: Size: 0
-# EXEDYN-NEXT: Binding: Global
-# EXEDYN-NEXT: Type: Function
-# EXEDYN-NEXT: Other: 0
-# EXEDYN-NEXT: Section: .text
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Name: c@
-# EXEDYN-NEXT: Value: 0x1003
-# EXEDYN-NEXT: Size: 0
-# EXEDYN-NEXT: Binding: Global
-# EXEDYN-NEXT: Type: Function
-# EXEDYN-NEXT: Other: 0
-# EXEDYN-NEXT: Section: .text
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: ]
-# EXEDYN-NEXT: Version symbols {
-# EXEDYN-NEXT: Section Name: .gnu.version
-# EXEDYN-NEXT: Address: 0x288
-# EXEDYN-NEXT: Offset: 0x288
-# EXEDYN-NEXT: Link: 1
-# EXEDYN-NEXT: Symbols [
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Version: 0
-# EXEDYN-NEXT: Name: @
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Version: 1
-# EXEDYN-NEXT: Name: _start@
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Version: 1
-# EXEDYN-NEXT: Name: a@
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Version: 2
-# EXEDYN-NEXT: Name: b@@LIBSAMPLE_2.0
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Version: 3
-# EXEDYN-NEXT: Name: b@LIBSAMPLE_1.0
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Version: 1
-# EXEDYN-NEXT: Name: b_1@
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Version: 1
-# EXEDYN-NEXT: Name: b_2@
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Symbol {
-# EXEDYN-NEXT: Version: 1
-# EXEDYN-NEXT: Name: c@
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: ]
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: SHT_GNU_verdef {
-# EXEDYN-NEXT: Definition {
-# EXEDYN-NEXT: Version: 1
-# EXEDYN-NEXT: Flags: Base
-# EXEDYN-NEXT: Index: 1
-# EXEDYN-NEXT: Hash:
-# EXEDYN-NEXT: Name:
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Definition {
-# EXEDYN-NEXT: Version: 1
-# EXEDYN-NEXT: Flags: 0x0
-# EXEDYN-NEXT: Index: 2
-# EXEDYN-NEXT: Hash: 98456416
-# EXEDYN-NEXT: Name: LIBSAMPLE_2.0
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: Definition {
-# EXEDYN-NEXT: Version: 1
-# EXEDYN-NEXT: Flags: 0x0
-# EXEDYN-NEXT: Index: 3
-# EXEDYN-NEXT: Hash: 98457184
-# EXEDYN-NEXT: Name: LIBSAMPLE_1.0
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: }
-# EXEDYN-NEXT: SHT_GNU_verneed {
-# EXEDYN-NEXT: }
-
-## Check when linking executable that error produced if version script
-## was specified and there are symbols with version in name that
-## is absent in script.
-# RUN: echo "VERSION { \
-# RUN: global: foo; \
-# RUN: local: *; }; " > %t.script
-# RUN: not ld.lld --version-script %t.script %t1 -o %t3 2>&1 | \
-# RUN: FileCheck -check-prefix=ERR %s
-# ERR: symbol b@LIBSAMPLE_1.0 has undefined version LIBSAMPLE_1.0
-
-b@LIBSAMPLE_1.0 = b_1
-b@@LIBSAMPLE_2.0 = b_2
-
-.globl a
-.type a,@function
-a:
-retq
-
-.globl b_1
-.type b_1,@function
-b_1:
-retq
-
-.globl b_2
-.type b_2,@function
-b_2:
-retq
-
-.globl c
-.type c,@function
-c:
-retq
-
-.globl _start
-_start:
- nop