summaryrefslogtreecommitdiff
path: root/lld/ELF/Symbols.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/ELF/Symbols.cpp')
-rw-r--r--lld/ELF/Symbols.cpp90
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