diff options
Diffstat (limited to 'lld/ELF/Symbols.cpp')
-rw-r--r-- | lld/ELF/Symbols.cpp | 90 |
1 files changed, 64 insertions, 26 deletions
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp index f0f6121009a5..8f2f55418df5 100644 --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -16,27 +16,39 @@ #include "lld/Common/ErrorHandler.h" #include "lld/Common/Strings.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" #include <cstring> using namespace llvm; using namespace llvm::object; using namespace llvm::ELF; +using namespace lld; +using namespace lld::elf; -namespace lld { // Returns a symbol for an error message. static std::string demangle(StringRef symName) { if (elf::config->demangle) return demangleItanium(symName); - return symName; + return std::string(symName); } -std::string toString(const elf::Symbol &b) { return demangle(b.getName()); } -std::string toELFString(const Archive::Symbol &b) { +std::string lld::toString(const elf::Symbol &sym) { + StringRef name = sym.getName(); + std::string ret = demangle(name); + + // If sym has a non-default version, its name may have been truncated at '@' + // by Symbol::parseSymbolVersion(). Add the trailing part. This check is safe + // because every symbol name ends with '\0'. + if (name.data()[name.size()] == '@') + ret += name.data() + name.size(); + return ret; +} + +std::string lld::toELFString(const Archive::Symbol &b) { return demangle(b.getName()); } -namespace elf { Defined *ElfSym::bss; Defined *ElfSym::etext1; Defined *ElfSym::etext2; @@ -52,6 +64,7 @@ Defined *ElfSym::relaIpltStart; Defined *ElfSym::relaIpltEnd; Defined *ElfSym::riscvGlobalPointer; Defined *ElfSym::tlsModuleBase; +DenseMap<const Symbol *, const InputFile *> elf::backwardReferences; static uint64_t getSymVA(const Symbol &sym, int64_t &addend) { switch (sym.kind()) { @@ -99,7 +112,7 @@ static uint64_t getSymVA(const Symbol &sym, int64_t &addend) { // MIPS relocatable files can mix regular and microMIPS code. // Linker needs to distinguish such code. To do so microMIPS // symbols has the `STO_MIPS_MICROMIPS` flag in the `st_other` - // field. Unfortunately, the `MIPS::relocateOne()` method has + // field. Unfortunately, the `MIPS::relocate()` method has // a symbol value only. To pass type of the symbol (regular/microMIPS) // to that routine as well as other places where we write // a symbol value as-is (.dynamic section, `Elf_Ehdr::e_entry` @@ -265,7 +278,7 @@ uint8_t Symbol::computeBinding() const { if (config->relocatable) return binding; if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) || - versionId == VER_NDX_LOCAL) + (versionId == VER_NDX_LOCAL && isDefined())) return STB_LOCAL; if (!config->gnuUnique && binding == STB_GNU_UNIQUE) return STB_GLOBAL; @@ -278,13 +291,17 @@ bool Symbol::includeInDynsym() const { if (computeBinding() == STB_LOCAL) return false; if (!isDefined() && !isCommon()) - return true; + // This should unconditionally return true, unfortunately glibc -static-pie + // expects undefined weak symbols not to exist in .dynsym, e.g. + // __pthread_mutex_lock reference in _dl_add_to_namespace_list, + // __pthread_initialize_minimal reference in csu/libc-start.c. + return !(config->noDynamicLinker && isUndefWeak()); return exportDynamic || inDynamicList; } // Print out a log message for --trace-symbol. -void printTraceSymbol(const Symbol *sym) { +void elf::printTraceSymbol(const Symbol *sym) { std::string s; if (sym->isUndefined()) s = ": reference to "; @@ -300,7 +317,7 @@ void printTraceSymbol(const Symbol *sym) { message(toString(sym->file) + s + sym->getName()); } -void maybeWarnUnorderableSymbol(const Symbol *sym) { +void elf::maybeWarnUnorderableSymbol(const Symbol *sym) { if (!config->warnSymbolOrdering) return; @@ -332,7 +349,7 @@ void maybeWarnUnorderableSymbol(const Symbol *sym) { // Returns true if a symbol can be replaced at load-time by a symbol // with the same name defined in other ELF executable or DSO. -bool computeIsPreemptible(const Symbol &sym) { +bool elf::computeIsPreemptible(const Symbol &sym) { assert(!sym.isLocal()); // Only symbols with default visibility that appear in dynsym can be @@ -348,16 +365,22 @@ bool computeIsPreemptible(const Symbol &sym) { if (!config->shared) return false; - // If the dynamic list is present, it specifies preemptable symbols in a DSO. - if (config->hasDynamicList) + // If -Bsymbolic or --dynamic-list is specified, or -Bsymbolic-functions is + // specified and the symbol is STT_FUNC, the symbol is preemptible iff it is + // in the dynamic list. + if (config->symbolic || (config->bsymbolicFunctions && sym.isFunc())) return sym.inDynamicList; - - // -Bsymbolic means that definitions are not preempted. - if (config->bsymbolic || (config->bsymbolicFunctions && sym.isFunc())) - return false; return true; } +void elf::reportBackrefs() { + for (auto &it : backwardReferences) { + const Symbol &sym = *it.first; + warn("backward reference detected: " + sym.getName() + " in " + + toString(it.second) + " refers to " + toString(sym.file)); + } +} + static uint8_t getMinVisibility(uint8_t va, uint8_t vb) { if (va == STV_DEFAULT) return vb; @@ -490,13 +513,28 @@ void Symbol::resolveUndefined(const Undefined &other) { // group assignment rule simulates the traditional linker's semantics. bool backref = config->warnBackrefs && other.file && file->groupId < other.file->groupId; + if (backref) { + // Some libraries have known problems and can cause noise. Filter them out + // with --warn-backrefs-exclude=. + StringRef name = + !file->archiveName.empty() ? file->archiveName : file->getName(); + for (const llvm::GlobPattern &pat : config->warnBackrefsExclude) + if (pat.match(name)) { + backref = false; + break; + } + } fetch(); // We don't report backward references to weak symbols as they can be // overridden later. + // + // A traditional linker does not error for -ldef1 -lref -ldef2 (linking + // sandwich), where def2 may or may not be the same as def1. We don't want + // to warn for this case, so dismiss the warning if we see a subsequent lazy + // definition. if (backref && !isWeak()) - warn("backward reference detected: " + other.getName() + " in " + - toString(other.file) + " refers to " + toString(file)); + backwardReferences.try_emplace(this, other.file); return; } @@ -510,7 +548,6 @@ void Symbol::resolveUndefined(const Undefined &other) { // reference is weak. if (other.binding != STB_WEAK || !referenced) binding = other.binding; - referenced = true; } } @@ -654,8 +691,12 @@ void Symbol::resolveDefined(const Defined &other) { } template <class LazyT> void Symbol::resolveLazy(const LazyT &other) { - if (!isUndefined()) + if (!isUndefined()) { + // See the comment in resolveUndefined(). + if (isDefined()) + backwardReferences.erase(this); return; + } // An undefined weak will not fetch archive members. See comment on Lazy in // Symbols.h for the details. @@ -683,9 +724,6 @@ void Symbol::resolveShared(const SharedSymbol &other) { uint8_t bind = binding; replace(other); binding = bind; - referenced = true; - } + } else if (traced) + printTraceSymbol(&other); } - -} // namespace elf -} // namespace lld |