10b57cec5SDimitry Andric //===- SymbolTable.cpp ----------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // Symbol table is a bag of all known symbols. We put all symbols of
100b57cec5SDimitry Andric // all input files to the symbol table. The symbol table is basically
110b57cec5SDimitry Andric // a hash table with the logic to resolve symbol name conflicts using
120b57cec5SDimitry Andric // the symbol types.
130b57cec5SDimitry Andric //
140b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
150b57cec5SDimitry Andric
160b57cec5SDimitry Andric #include "SymbolTable.h"
170b57cec5SDimitry Andric #include "Config.h"
1881ad6265SDimitry Andric #include "InputFiles.h"
190b57cec5SDimitry Andric #include "Symbols.h"
200b57cec5SDimitry Andric #include "lld/Common/ErrorHandler.h"
210b57cec5SDimitry Andric #include "lld/Common/Memory.h"
220b57cec5SDimitry Andric #include "lld/Common/Strings.h"
230b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
24bdd1243dSDimitry Andric #include "llvm/Demangle/Demangle.h"
250b57cec5SDimitry Andric
260b57cec5SDimitry Andric using namespace llvm;
270b57cec5SDimitry Andric using namespace llvm::object;
280b57cec5SDimitry Andric using namespace llvm::ELF;
295ffd83dbSDimitry Andric using namespace lld;
305ffd83dbSDimitry Andric using namespace lld::elf;
310b57cec5SDimitry Andric
32bdd1243dSDimitry Andric SymbolTable elf::symtab;
330b57cec5SDimitry Andric
wrap(Symbol * sym,Symbol * real,Symbol * wrap)340b57cec5SDimitry Andric void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) {
35349cc55cSDimitry Andric // Redirect __real_foo to the original foo and foo to the original __wrap_foo.
360b57cec5SDimitry Andric int &idx1 = symMap[CachedHashStringRef(sym->getName())];
370b57cec5SDimitry Andric int &idx2 = symMap[CachedHashStringRef(real->getName())];
380b57cec5SDimitry Andric int &idx3 = symMap[CachedHashStringRef(wrap->getName())];
390b57cec5SDimitry Andric
400b57cec5SDimitry Andric idx2 = idx1;
410b57cec5SDimitry Andric idx1 = idx3;
420b57cec5SDimitry Andric
4381ad6265SDimitry Andric // Propagate symbol usage information to the redirected symbols.
4481ad6265SDimitry Andric if (sym->isUsedInRegularObj)
4581ad6265SDimitry Andric wrap->isUsedInRegularObj = true;
4681ad6265SDimitry Andric if (real->isUsedInRegularObj)
4781ad6265SDimitry Andric sym->isUsedInRegularObj = true;
4881ad6265SDimitry Andric else if (!sym->isDefined())
4981ad6265SDimitry Andric // Now that all references to sym have been redirected to wrap, if there are
5081ad6265SDimitry Andric // no references to real (which has been redirected to sym), we only need to
5181ad6265SDimitry Andric // keep sym if it was defined, otherwise it's unused and can be dropped.
52e8d8bef9SDimitry Andric sym->isUsedInRegularObj = false;
535ffd83dbSDimitry Andric
545ffd83dbSDimitry Andric // Now renaming is complete, and no one refers to real. We drop real from
555ffd83dbSDimitry Andric // .symtab and .dynsym. If real is undefined, it is important that we don't
565ffd83dbSDimitry Andric // leave it in .dynsym, because otherwise it might lead to an undefined symbol
575ffd83dbSDimitry Andric // error in a subsequent link. If real is defined, we could emit real as an
585ffd83dbSDimitry Andric // alias for sym, but that could degrade the user experience of some tools
595ffd83dbSDimitry Andric // that can print out only one symbol for each location: sym is a preferred
605ffd83dbSDimitry Andric // name than real, but they might print out real instead.
610b57cec5SDimitry Andric memcpy(real, sym, sizeof(SymbolUnion));
625ffd83dbSDimitry Andric real->isUsedInRegularObj = false;
630b57cec5SDimitry Andric }
640b57cec5SDimitry Andric
650b57cec5SDimitry Andric // Find an existing symbol or create a new one.
insert(StringRef name)660b57cec5SDimitry Andric Symbol *SymbolTable::insert(StringRef name) {
670b57cec5SDimitry Andric // <name>@@<version> means the symbol is the default version. In that
680b57cec5SDimitry Andric // case <name>@@<version> will be used to resolve references to <name>.
690b57cec5SDimitry Andric //
700b57cec5SDimitry Andric // Since this is a hot path, the following string search code is
710b57cec5SDimitry Andric // optimized for speed. StringRef::find(char) is much faster than
720b57cec5SDimitry Andric // StringRef::find(StringRef).
730eae32dcSDimitry Andric StringRef stem = name;
740b57cec5SDimitry Andric size_t pos = name.find('@');
750b57cec5SDimitry Andric if (pos != StringRef::npos && pos + 1 < name.size() && name[pos + 1] == '@')
760eae32dcSDimitry Andric stem = name.take_front(pos);
770b57cec5SDimitry Andric
780eae32dcSDimitry Andric auto p = symMap.insert({CachedHashStringRef(stem), (int)symVector.size()});
790eae32dcSDimitry Andric if (!p.second) {
800eae32dcSDimitry Andric Symbol *sym = symVector[p.first->second];
8104eeddc0SDimitry Andric if (stem.size() != name.size()) {
820eae32dcSDimitry Andric sym->setName(name);
8304eeddc0SDimitry Andric sym->hasVersionSuffix = true;
8404eeddc0SDimitry Andric }
850eae32dcSDimitry Andric return sym;
860eae32dcSDimitry Andric }
870b57cec5SDimitry Andric
880b57cec5SDimitry Andric Symbol *sym = reinterpret_cast<Symbol *>(make<SymbolUnion>());
890b57cec5SDimitry Andric symVector.push_back(sym);
900b57cec5SDimitry Andric
91bdd1243dSDimitry Andric // *sym was not initialized by a constructor. Initialize all Symbol fields.
92bdd1243dSDimitry Andric memset(sym, 0, sizeof(Symbol));
930b57cec5SDimitry Andric sym->setName(name);
9481ad6265SDimitry Andric sym->partition = 1;
9581ad6265SDimitry Andric sym->versionId = VER_NDX_GLOBAL;
9604eeddc0SDimitry Andric if (pos != StringRef::npos)
9704eeddc0SDimitry Andric sym->hasVersionSuffix = true;
980b57cec5SDimitry Andric return sym;
990b57cec5SDimitry Andric }
1000b57cec5SDimitry Andric
10181ad6265SDimitry Andric // This variant of addSymbol is used by BinaryFile::parse to check duplicate
10281ad6265SDimitry Andric // symbol errors.
addAndCheckDuplicate(const Defined & newSym)10381ad6265SDimitry Andric Symbol *SymbolTable::addAndCheckDuplicate(const Defined &newSym) {
10481ad6265SDimitry Andric Symbol *sym = insert(newSym.getName());
10581ad6265SDimitry Andric if (sym->isDefined())
10681ad6265SDimitry Andric sym->checkDuplicate(newSym);
10781ad6265SDimitry Andric sym->resolve(newSym);
10881ad6265SDimitry Andric sym->isUsedInRegularObj = true;
10981ad6265SDimitry Andric return sym;
11081ad6265SDimitry Andric }
11181ad6265SDimitry Andric
find(StringRef name)1120b57cec5SDimitry Andric Symbol *SymbolTable::find(StringRef name) {
1130b57cec5SDimitry Andric auto it = symMap.find(CachedHashStringRef(name));
1140b57cec5SDimitry Andric if (it == symMap.end())
1150b57cec5SDimitry Andric return nullptr;
1160eae32dcSDimitry Andric return symVector[it->second];
1170b57cec5SDimitry Andric }
1180b57cec5SDimitry Andric
1195ffd83dbSDimitry Andric // A version script/dynamic list is only meaningful for a Defined symbol.
1205ffd83dbSDimitry Andric // A CommonSymbol will be converted to a Defined in replaceCommonSymbols().
1214824e7fdSDimitry Andric // A lazy symbol may be made Defined if an LTO libcall extracts it.
canBeVersioned(const Symbol & sym)1225ffd83dbSDimitry Andric static bool canBeVersioned(const Symbol &sym) {
1235ffd83dbSDimitry Andric return sym.isDefined() || sym.isCommon() || sym.isLazy();
1245ffd83dbSDimitry Andric }
1255ffd83dbSDimitry Andric
1260b57cec5SDimitry Andric // Initialize demangledSyms with a map from demangled symbols to symbol
1270b57cec5SDimitry Andric // objects. Used to handle "extern C++" directive in version scripts.
1280b57cec5SDimitry Andric //
1290b57cec5SDimitry Andric // The map will contain all demangled symbols. That can be very large,
1300b57cec5SDimitry Andric // and in LLD we generally want to avoid do anything for each symbol.
1310b57cec5SDimitry Andric // Then, why are we doing this? Here's why.
1320b57cec5SDimitry Andric //
1330b57cec5SDimitry Andric // Users can use "extern C++ {}" directive to match against demangled
1340b57cec5SDimitry Andric // C++ symbols. For example, you can write a pattern such as
1350b57cec5SDimitry Andric // "llvm::*::foo(int, ?)". Obviously, there's no way to handle this
1360b57cec5SDimitry Andric // other than trying to match a pattern against all demangled symbols.
1370b57cec5SDimitry Andric // So, if "extern C++" feature is used, we need to demangle all known
1380b57cec5SDimitry Andric // symbols.
getDemangledSyms()1390eae32dcSDimitry Andric StringMap<SmallVector<Symbol *, 0>> &SymbolTable::getDemangledSyms() {
1400b57cec5SDimitry Andric if (!demangledSyms) {
1410b57cec5SDimitry Andric demangledSyms.emplace();
1426e75b2fbSDimitry Andric std::string demangled;
1435ffd83dbSDimitry Andric for (Symbol *sym : symVector)
1446e75b2fbSDimitry Andric if (canBeVersioned(*sym)) {
1456e75b2fbSDimitry Andric StringRef name = sym->getName();
1466e75b2fbSDimitry Andric size_t pos = name.find('@');
14706c3fb27SDimitry Andric std::string substr;
1486e75b2fbSDimitry Andric if (pos == std::string::npos)
14906c3fb27SDimitry Andric demangled = demangle(name);
15006c3fb27SDimitry Andric else if (pos + 1 == name.size() || name[pos + 1] == '@') {
15106c3fb27SDimitry Andric substr = name.substr(0, pos);
15206c3fb27SDimitry Andric demangled = demangle(substr);
15306c3fb27SDimitry Andric } else {
15406c3fb27SDimitry Andric substr = name.substr(0, pos);
15506c3fb27SDimitry Andric demangled = (demangle(substr) + name.substr(pos)).str();
15606c3fb27SDimitry Andric }
1576e75b2fbSDimitry Andric (*demangledSyms)[demangled].push_back(sym);
1586e75b2fbSDimitry Andric }
1590b57cec5SDimitry Andric }
1600b57cec5SDimitry Andric return *demangledSyms;
1610b57cec5SDimitry Andric }
1620b57cec5SDimitry Andric
findByVersion(SymbolVersion ver)1630eae32dcSDimitry Andric SmallVector<Symbol *, 0> SymbolTable::findByVersion(SymbolVersion ver) {
1640b57cec5SDimitry Andric if (ver.isExternCpp)
1650b57cec5SDimitry Andric return getDemangledSyms().lookup(ver.name);
1665ffd83dbSDimitry Andric if (Symbol *sym = find(ver.name))
1675ffd83dbSDimitry Andric if (canBeVersioned(*sym))
1685ffd83dbSDimitry Andric return {sym};
1690b57cec5SDimitry Andric return {};
1700b57cec5SDimitry Andric }
1710b57cec5SDimitry Andric
findAllByVersion(SymbolVersion ver,bool includeNonDefault)1720eae32dcSDimitry Andric SmallVector<Symbol *, 0> SymbolTable::findAllByVersion(SymbolVersion ver,
1736e75b2fbSDimitry Andric bool includeNonDefault) {
1740eae32dcSDimitry Andric SmallVector<Symbol *, 0> res;
1755ffd83dbSDimitry Andric SingleStringMatcher m(ver.name);
17606c3fb27SDimitry Andric auto check = [&](const Symbol &sym) -> bool {
1776e75b2fbSDimitry Andric if (!includeNonDefault)
17806c3fb27SDimitry Andric return !sym.hasVersionSuffix;
17906c3fb27SDimitry Andric StringRef name = sym.getName();
18006c3fb27SDimitry Andric size_t pos = name.find('@');
1816e75b2fbSDimitry Andric return !(pos + 1 < name.size() && name[pos + 1] == '@');
1826e75b2fbSDimitry Andric };
1830b57cec5SDimitry Andric
1840b57cec5SDimitry Andric if (ver.isExternCpp) {
1850b57cec5SDimitry Andric for (auto &p : getDemangledSyms())
1860b57cec5SDimitry Andric if (m.match(p.first()))
1876e75b2fbSDimitry Andric for (Symbol *sym : p.second)
18806c3fb27SDimitry Andric if (check(*sym))
1896e75b2fbSDimitry Andric res.push_back(sym);
1900b57cec5SDimitry Andric return res;
1910b57cec5SDimitry Andric }
1920b57cec5SDimitry Andric
1930b57cec5SDimitry Andric for (Symbol *sym : symVector)
19406c3fb27SDimitry Andric if (canBeVersioned(*sym) && check(*sym) && m.match(sym->getName()))
1950b57cec5SDimitry Andric res.push_back(sym);
1960b57cec5SDimitry Andric return res;
1970b57cec5SDimitry Andric }
1980b57cec5SDimitry Andric
handleDynamicList()1990b57cec5SDimitry Andric void SymbolTable::handleDynamicList() {
2000eae32dcSDimitry Andric SmallVector<Symbol *, 0> syms;
2010b57cec5SDimitry Andric for (SymbolVersion &ver : config->dynamicList) {
2020b57cec5SDimitry Andric if (ver.hasWildcard)
2036e75b2fbSDimitry Andric syms = findAllByVersion(ver, /*includeNonDefault=*/true);
2040b57cec5SDimitry Andric else
2050b57cec5SDimitry Andric syms = findByVersion(ver);
2060b57cec5SDimitry Andric
20785868e8aSDimitry Andric for (Symbol *sym : syms)
20885868e8aSDimitry Andric sym->inDynamicList = true;
2090b57cec5SDimitry Andric }
2100b57cec5SDimitry Andric }
2110b57cec5SDimitry Andric
2126e75b2fbSDimitry Andric // Set symbol versions to symbols. This function handles patterns containing no
2136e75b2fbSDimitry Andric // wildcard characters. Return false if no symbol definition matches ver.
assignExactVersion(SymbolVersion ver,uint16_t versionId,StringRef versionName,bool includeNonDefault)2146e75b2fbSDimitry Andric bool SymbolTable::assignExactVersion(SymbolVersion ver, uint16_t versionId,
2156e75b2fbSDimitry Andric StringRef versionName,
2166e75b2fbSDimitry Andric bool includeNonDefault) {
2170b57cec5SDimitry Andric // Get a list of symbols which we need to assign the version to.
2180eae32dcSDimitry Andric SmallVector<Symbol *, 0> syms = findByVersion(ver);
2190b57cec5SDimitry Andric
2200b57cec5SDimitry Andric auto getName = [](uint16_t ver) -> std::string {
2210b57cec5SDimitry Andric if (ver == VER_NDX_LOCAL)
2220b57cec5SDimitry Andric return "VER_NDX_LOCAL";
2230b57cec5SDimitry Andric if (ver == VER_NDX_GLOBAL)
2240b57cec5SDimitry Andric return "VER_NDX_GLOBAL";
22585868e8aSDimitry Andric return ("version '" + config->versionDefinitions[ver].name + "'").str();
2260b57cec5SDimitry Andric };
2270b57cec5SDimitry Andric
2280b57cec5SDimitry Andric // Assign the version.
2290b57cec5SDimitry Andric for (Symbol *sym : syms) {
2306e75b2fbSDimitry Andric // For a non-local versionId, skip symbols containing version info because
2316e75b2fbSDimitry Andric // symbol versions specified by symbol names take precedence over version
2326e75b2fbSDimitry Andric // scripts. See parseSymbolVersion().
2336e75b2fbSDimitry Andric if (!includeNonDefault && versionId != VER_NDX_LOCAL &&
2346e75b2fbSDimitry Andric sym->getName().contains('@'))
2350b57cec5SDimitry Andric continue;
2360b57cec5SDimitry Andric
2375f757f3fSDimitry Andric // If the version has not been assigned, assign versionId to the symbol.
2385f757f3fSDimitry Andric if (!sym->versionScriptAssigned) {
2395f757f3fSDimitry Andric sym->versionScriptAssigned = true;
2400b57cec5SDimitry Andric sym->versionId = versionId;
24185868e8aSDimitry Andric }
2420b57cec5SDimitry Andric if (sym->versionId == versionId)
2430b57cec5SDimitry Andric continue;
2440b57cec5SDimitry Andric
2450b57cec5SDimitry Andric warn("attempt to reassign symbol '" + ver.name + "' of " +
2460b57cec5SDimitry Andric getName(sym->versionId) + " to " + getName(versionId));
2470b57cec5SDimitry Andric }
2486e75b2fbSDimitry Andric return !syms.empty();
2490b57cec5SDimitry Andric }
2500b57cec5SDimitry Andric
assignWildcardVersion(SymbolVersion ver,uint16_t versionId,bool includeNonDefault)2516e75b2fbSDimitry Andric void SymbolTable::assignWildcardVersion(SymbolVersion ver, uint16_t versionId,
2526e75b2fbSDimitry Andric bool includeNonDefault) {
253480093f4SDimitry Andric // Exact matching takes precedence over fuzzy matching,
2540b57cec5SDimitry Andric // so we set a version to a symbol only if no version has been assigned
2550b57cec5SDimitry Andric // to the symbol. This behavior is compatible with GNU.
2566e75b2fbSDimitry Andric for (Symbol *sym : findAllByVersion(ver, includeNonDefault))
2575f757f3fSDimitry Andric if (!sym->versionScriptAssigned) {
2585f757f3fSDimitry Andric sym->versionScriptAssigned = true;
25985868e8aSDimitry Andric sym->versionId = versionId;
26085868e8aSDimitry Andric }
2610b57cec5SDimitry Andric }
2620b57cec5SDimitry Andric
2630b57cec5SDimitry Andric // This function processes version scripts by updating the versionId
2640b57cec5SDimitry Andric // member of symbols.
2650b57cec5SDimitry Andric // If there's only one anonymous version definition in a version
2660b57cec5SDimitry Andric // script file, the script does not actually define any symbol version,
2670b57cec5SDimitry Andric // but just specifies symbols visibilities.
scanVersionScript()2680b57cec5SDimitry Andric void SymbolTable::scanVersionScript() {
2696e75b2fbSDimitry Andric SmallString<128> buf;
2700b57cec5SDimitry Andric // First, we assign versions to exact matching symbols,
2710b57cec5SDimitry Andric // i.e. version definitions not containing any glob meta-characters.
2726e75b2fbSDimitry Andric for (VersionDefinition &v : config->versionDefinitions) {
2736e75b2fbSDimitry Andric auto assignExact = [&](SymbolVersion pat, uint16_t id, StringRef ver) {
2746e75b2fbSDimitry Andric bool found =
2756e75b2fbSDimitry Andric assignExactVersion(pat, id, ver, /*includeNonDefault=*/false);
2766e75b2fbSDimitry Andric buf.clear();
2776e75b2fbSDimitry Andric found |= assignExactVersion({(pat.name + "@" + v.name).toStringRef(buf),
2786e75b2fbSDimitry Andric pat.isExternCpp, /*hasWildCard=*/false},
2796e75b2fbSDimitry Andric id, ver, /*includeNonDefault=*/true);
2806e75b2fbSDimitry Andric if (!found && !config->undefinedVersion)
28106c3fb27SDimitry Andric errorOrWarn("version script assignment of '" + ver + "' to symbol '" +
2826e75b2fbSDimitry Andric pat.name + "' failed: symbol not defined");
2836e75b2fbSDimitry Andric };
2846e75b2fbSDimitry Andric for (SymbolVersion &pat : v.nonLocalPatterns)
2856e75b2fbSDimitry Andric if (!pat.hasWildcard)
2866e75b2fbSDimitry Andric assignExact(pat, v.id, v.name);
2876e75b2fbSDimitry Andric for (SymbolVersion pat : v.localPatterns)
2886e75b2fbSDimitry Andric if (!pat.hasWildcard)
2896e75b2fbSDimitry Andric assignExact(pat, VER_NDX_LOCAL, "local");
2906e75b2fbSDimitry Andric }
2910b57cec5SDimitry Andric
29285868e8aSDimitry Andric // Next, assign versions to wildcards that are not "*". Note that because the
29385868e8aSDimitry Andric // last match takes precedence over previous matches, we iterate over the
29485868e8aSDimitry Andric // definitions in the reverse order.
2956e75b2fbSDimitry Andric auto assignWildcard = [&](SymbolVersion pat, uint16_t id, StringRef ver) {
2966e75b2fbSDimitry Andric assignWildcardVersion(pat, id, /*includeNonDefault=*/false);
2976e75b2fbSDimitry Andric buf.clear();
2986e75b2fbSDimitry Andric assignWildcardVersion({(pat.name + "@" + ver).toStringRef(buf),
2996e75b2fbSDimitry Andric pat.isExternCpp, /*hasWildCard=*/true},
3006e75b2fbSDimitry Andric id,
3016e75b2fbSDimitry Andric /*includeNonDefault=*/true);
3026e75b2fbSDimitry Andric };
3036e75b2fbSDimitry Andric for (VersionDefinition &v : llvm::reverse(config->versionDefinitions)) {
3046e75b2fbSDimitry Andric for (SymbolVersion &pat : v.nonLocalPatterns)
30585868e8aSDimitry Andric if (pat.hasWildcard && pat.name != "*")
3066e75b2fbSDimitry Andric assignWildcard(pat, v.id, v.name);
3076e75b2fbSDimitry Andric for (SymbolVersion &pat : v.localPatterns)
3086e75b2fbSDimitry Andric if (pat.hasWildcard && pat.name != "*")
3096e75b2fbSDimitry Andric assignWildcard(pat, VER_NDX_LOCAL, v.name);
3106e75b2fbSDimitry Andric }
31185868e8aSDimitry Andric
31285868e8aSDimitry Andric // Then, assign versions to "*". In GNU linkers they have lower priority than
31385868e8aSDimitry Andric // other wildcards.
3145f757f3fSDimitry Andric for (VersionDefinition &v : llvm::reverse(config->versionDefinitions)) {
3156e75b2fbSDimitry Andric for (SymbolVersion &pat : v.nonLocalPatterns)
31685868e8aSDimitry Andric if (pat.hasWildcard && pat.name == "*")
3176e75b2fbSDimitry Andric assignWildcard(pat, v.id, v.name);
3186e75b2fbSDimitry Andric for (SymbolVersion &pat : v.localPatterns)
3196e75b2fbSDimitry Andric if (pat.hasWildcard && pat.name == "*")
3206e75b2fbSDimitry Andric assignWildcard(pat, VER_NDX_LOCAL, v.name);
3216e75b2fbSDimitry Andric }
3220b57cec5SDimitry Andric
3230b57cec5SDimitry Andric // Symbol themselves might know their versions because symbols
3240b57cec5SDimitry Andric // can contain versions in the form of <name>@<version>.
3250b57cec5SDimitry Andric // Let them parse and update their names to exclude version suffix.
3260b57cec5SDimitry Andric for (Symbol *sym : symVector)
32704eeddc0SDimitry Andric if (sym->hasVersionSuffix)
3280b57cec5SDimitry Andric sym->parseSymbolVersion();
3290b57cec5SDimitry Andric
3300b57cec5SDimitry Andric // isPreemptible is false at this point. To correctly compute the binding of a
3310b57cec5SDimitry Andric // Defined (which is used by includeInDynsym()), we need to know if it is
33285868e8aSDimitry Andric // VER_NDX_LOCAL or not. Compute symbol versions before handling
33385868e8aSDimitry Andric // --dynamic-list.
3340b57cec5SDimitry Andric handleDynamicList();
3350b57cec5SDimitry Andric }
336*0fca6ea1SDimitry Andric
addUnusedUndefined(StringRef name,uint8_t binding)337*0fca6ea1SDimitry Andric Symbol *SymbolTable::addUnusedUndefined(StringRef name, uint8_t binding) {
338*0fca6ea1SDimitry Andric return addSymbol(Undefined{ctx.internalFile, name, binding, STV_DEFAULT, 0});
339*0fca6ea1SDimitry Andric }
340