aboutsummaryrefslogtreecommitdiff
path: root/lld/ELF/Symbols.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lld/ELF/Symbols.cpp')
-rw-r--r--lld/ELF/Symbols.cpp270
1 files changed, 107 insertions, 163 deletions
diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp
index d943c1996422..985e483df2ad 100644
--- a/lld/ELF/Symbols.cpp
+++ b/lld/ELF/Symbols.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "Symbols.h"
+#include "Driver.h"
#include "InputFiles.h"
#include "InputSection.h"
#include "OutputSections.h"
@@ -15,9 +16,7 @@
#include "Writer.h"
#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 "llvm/Support/Compiler.h"
#include <cstring>
using namespace llvm;
@@ -26,6 +25,24 @@ using namespace llvm::ELF;
using namespace lld;
using namespace lld::elf;
+static_assert(sizeof(SymbolUnion) <= 64, "SymbolUnion too large");
+
+template <typename T> struct AssertSymbol {
+ static_assert(std::is_trivially_destructible<T>(),
+ "Symbol types must be trivially destructible");
+ static_assert(sizeof(T) <= sizeof(SymbolUnion), "SymbolUnion too small");
+ static_assert(alignof(T) <= alignof(SymbolUnion),
+ "SymbolUnion not aligned enough");
+};
+
+LLVM_ATTRIBUTE_UNUSED static inline void assertSymbols() {
+ AssertSymbol<Defined>();
+ AssertSymbol<CommonSymbol>();
+ AssertSymbol<Undefined>();
+ AssertSymbol<SharedSymbol>();
+ AssertSymbol<LazyObject>();
+}
+
std::string lld::toString(const elf::Symbol &sym) {
StringRef name = sym.getName();
std::string ret = demangle(name, config->demangle);
@@ -36,10 +53,6 @@ std::string lld::toString(const elf::Symbol &sym) {
return ret;
}
-std::string lld::toELFString(const Archive::Symbol &b) {
- return demangle(b.getName(), config->demangle);
-}
-
Defined *ElfSym::bss;
Defined *ElfSym::etext1;
Defined *ElfSym::etext2;
@@ -55,10 +68,6 @@ Defined *ElfSym::relaIpltStart;
Defined *ElfSym::relaIpltEnd;
Defined *ElfSym::riscvGlobalPointer;
Defined *ElfSym::tlsModuleBase;
-DenseMap<const Symbol *, std::pair<const InputFile *, const InputFile *>>
- elf::backwardReferences;
-SmallVector<std::tuple<std::string, const InputFile *, const Symbol &>, 0>
- elf::whyExtract;
SmallVector<SymbolAux, 0> elf::symAux;
static uint64_t getSymVA(const Symbol &sym, int64_t addend) {
@@ -132,7 +141,6 @@ static uint64_t getSymVA(const Symbol &sym, int64_t addend) {
case Symbol::SharedKind:
case Symbol::UndefinedKind:
return 0;
- case Symbol::LazyArchiveKind:
case Symbol::LazyObjectKind:
llvm_unreachable("lazy symbol reached writer");
case Symbol::CommonKind:
@@ -249,24 +257,12 @@ void Symbol::parseSymbolVersion() {
}
void Symbol::extract() const {
- if (auto *sym = dyn_cast<LazyArchive>(this)) {
- cast<ArchiveFile>(sym->file)->extract(sym->sym);
- } else if (file->lazy) {
+ if (file->lazy) {
file->lazy = false;
parseFile(file);
}
}
-MemoryBufferRef LazyArchive::getMemberBuffer() {
- Archive::Child c =
- CHECK(sym.getMember(),
- "could not get the member for symbol " + toELFString(sym));
-
- return CHECK(c.getMemoryBufferRef(),
- "could not get the buffer for the member defining symbol " +
- toELFString(sym));
-}
-
uint8_t Symbol::computeBinding() const {
if ((visibility != STV_DEFAULT && visibility != STV_PROTECTED) ||
versionId == VER_NDX_LOCAL)
@@ -290,25 +286,25 @@ bool Symbol::includeInDynsym() const {
}
// Print out a log message for --trace-symbol.
-void elf::printTraceSymbol(const Symbol *sym) {
+void elf::printTraceSymbol(const Symbol &sym, StringRef name) {
std::string s;
- if (sym->isUndefined())
+ if (sym.isUndefined())
s = ": reference to ";
- else if (sym->isLazy())
+ else if (sym.isLazy())
s = ": lazy definition of ";
- else if (sym->isShared())
+ else if (sym.isShared())
s = ": shared definition of ";
- else if (sym->isCommon())
+ else if (sym.isCommon())
s = ": common definition of ";
else
s = ": definition of ";
- message(toString(sym->file) + s + sym->getName());
+ message(toString(sym.file) + s + name);
}
static void recordWhyExtract(const InputFile *reference,
const InputFile &extracted, const Symbol &sym) {
- whyExtract.emplace_back(toString(reference), &extracted, sym);
+ ctx->whyExtractRecords.emplace_back(toString(reference), &extracted, sym);
}
void elf::maybeWarnUnorderableSymbol(const Symbol *sym) {
@@ -371,24 +367,6 @@ bool elf::computeIsPreemptible(const Symbol &sym) {
return true;
}
-void elf::reportBackrefs() {
- for (auto &it : backwardReferences) {
- const Symbol &sym = *it.first;
- std::string to = toString(it.second.second);
- // Some libraries have known problems and can cause noise. Filter them out
- // with --warn-backrefs-exclude=. to may look like *.o or *.a(*.o).
- bool exclude = false;
- for (const llvm::GlobPattern &pat : config->warnBackrefsExclude)
- if (pat.match(to)) {
- exclude = true;
- break;
- }
- if (!exclude)
- warn("backward reference detected: " + sym.getName() + " in " +
- toString(it.second.first) + " refers to " + to);
- }
-}
-
static uint8_t getMinVisibility(uint8_t va, uint8_t vb) {
if (va == STV_DEFAULT)
return vb;
@@ -405,8 +383,6 @@ static uint8_t getMinVisibility(uint8_t va, uint8_t vb) {
void Symbol::mergeProperties(const Symbol &other) {
if (other.exportDynamic)
exportDynamic = true;
- if (other.isUsedInRegularObj)
- isUsedInRegularObj = true;
// DSO symbols do not affect visibility in the output.
if (!other.isShared())
@@ -431,9 +407,6 @@ void Symbol::resolve(const Symbol &other) {
case Symbol::DefinedKind:
resolveDefined(cast<Defined>(other));
break;
- case Symbol::LazyArchiveKind:
- resolveLazy(cast<LazyArchive>(other));
- break;
case Symbol::LazyObjectKind:
resolveLazy(cast<LazyObject>(other));
break;
@@ -458,7 +431,7 @@ void Symbol::resolveUndefined(const Undefined &other) {
}
if (traced)
- printTraceSymbol(&other);
+ printTraceSymbol(other, getName());
if (isLazy()) {
// An undefined weak will not extract archive members. See comment on Lazy
@@ -535,7 +508,8 @@ void Symbol::resolveUndefined(const Undefined &other) {
// definition. this->file needs to be saved because in the case of LTO it
// may be reset to nullptr or be replaced with a file named lto.tmp.
if (backref && !isWeak())
- backwardReferences.try_emplace(this, std::make_pair(other.file, file));
+ ctx->backwardReferences.try_emplace(this,
+ std::make_pair(other.file, file));
return;
}
@@ -552,71 +526,52 @@ void Symbol::resolveUndefined(const Undefined &other) {
}
}
-// Compare two symbols. Return 1 if the new symbol should win, -1 if
-// the new symbol should lose, or 0 if there is a conflict.
-int Symbol::compare(const Symbol *other) const {
- assert(other->isDefined() || other->isCommon());
-
- if (!isDefined() && !isCommon())
- return 1;
+// Compare two symbols. Return true if the new symbol should win.
+bool Symbol::shouldReplace(const Defined &other) const {
+ if (LLVM_UNLIKELY(isCommon())) {
+ if (config->warnCommon)
+ warn("common " + getName() + " is overridden");
+ return !other.isWeak();
+ }
+ if (!isDefined())
+ return true;
// .symver foo,foo@@VER unfortunately creates two defined symbols: foo and
// foo@@VER. In GNU ld, if foo and foo@@VER are in the same file, foo is
// ignored. In our implementation, when this is foo, this->getName() may still
- // contain @@, return 1 in this case as well.
- if (file == other->file) {
- if (other->getName().contains("@@"))
- return 1;
+ // contain @@, return true in this case as well.
+ if (LLVM_UNLIKELY(file == other.file)) {
+ if (other.getName().contains("@@"))
+ return true;
if (getName().contains("@@"))
- return -1;
- }
-
- if (other->isWeak())
- return -1;
-
- if (isWeak())
- return 1;
-
- if (isCommon() && other->isCommon()) {
- if (config->warnCommon)
- warn("multiple common of " + getName());
- return 0;
- }
-
- if (isCommon()) {
- if (config->warnCommon)
- warn("common " + getName() + " is overridden");
- return 1;
- }
-
- if (other->isCommon()) {
- if (config->warnCommon)
- warn("common " + getName() + " is overridden");
- return -1;
+ return false;
}
- auto *oldSym = cast<Defined>(this);
- auto *newSym = cast<Defined>(other);
-
- if (isa_and_nonnull<BitcodeFile>(other->file))
- return 0;
-
- if (!oldSym->section && !newSym->section && oldSym->value == newSym->value &&
- newSym->binding == STB_GLOBAL)
- return -1;
-
- return 0;
+ // Incoming STB_GLOBAL overrides STB_WEAK/STB_GNU_UNIQUE. -fgnu-unique changes
+ // some vague linkage data in COMDAT from STB_WEAK to STB_GNU_UNIQUE. Treat
+ // STB_GNU_UNIQUE like STB_WEAK so that we prefer the first among all
+ // STB_WEAK/STB_GNU_UNIQUE copies. If we prefer an incoming STB_GNU_UNIQUE to
+ // an existing STB_WEAK, there may be discarded section errors because the
+ // selected copy may be in a non-prevailing COMDAT.
+ return !isGlobal() && other.isGlobal();
}
-static void reportDuplicate(Symbol *sym, InputFile *newFile,
- InputSectionBase *errSec, uint64_t errOffset) {
+void elf::reportDuplicate(const Symbol &sym, const InputFile *newFile,
+ InputSectionBase *errSec, uint64_t errOffset) {
if (config->allowMultipleDefinition)
return;
-
- Defined *d = cast<Defined>(sym);
+ // In glibc<2.32, crti.o has .gnu.linkonce.t.__x86.get_pc_thunk.bx, which
+ // is sort of proto-comdat. There is actually no duplicate if we have
+ // full support for .gnu.linkonce.
+ const Defined *d = dyn_cast<Defined>(&sym);
+ if (!d || d->getName() == "__x86.get_pc_thunk.bx")
+ return;
+ // Allow absolute symbols with the same value for GNU ld compatibility.
+ if (!d->section && !errSec && errOffset && d->value == errOffset)
+ return;
if (!d->section || !errSec) {
- error("duplicate symbol: " + toString(*sym) + "\n>>> defined in " +
- toString(sym->file) + "\n>>> defined in " + toString(newFile));
+ error("duplicate symbol: " + toString(sym) + "\n>>> defined in " +
+ toString(sym.file) + "\n>>> defined in " + toString(newFile));
return;
}
@@ -628,12 +583,12 @@ static void reportDuplicate(Symbol *sym, InputFile *newFile,
// >>> defined at baz.c:563
// >>> baz.o in archive libbaz.a
auto *sec1 = cast<InputSectionBase>(d->section);
- std::string src1 = sec1->getSrcMsg(*sym, d->value);
+ std::string src1 = sec1->getSrcMsg(sym, d->value);
std::string obj1 = sec1->getObjMsg(d->value);
- std::string src2 = errSec->getSrcMsg(*sym, errOffset);
+ std::string src2 = errSec->getSrcMsg(sym, errOffset);
std::string obj2 = errSec->getObjMsg(errOffset);
- std::string msg = "duplicate symbol: " + toString(*sym) + "\n>>> defined at ";
+ std::string msg = "duplicate symbol: " + toString(sym) + "\n>>> defined at ";
if (!src1.empty())
msg += src1 + "\n>>> ";
msg += obj1 + "\n>>> defined at ";
@@ -643,76 +598,65 @@ static void reportDuplicate(Symbol *sym, InputFile *newFile,
error(msg);
}
+void Symbol::checkDuplicate(const Defined &other) const {
+ if (isDefined() && !isWeak() && !other.isWeak())
+ reportDuplicate(*this, other.file,
+ dyn_cast_or_null<InputSectionBase>(other.section),
+ other.value);
+}
+
void Symbol::resolveCommon(const CommonSymbol &other) {
- int cmp = compare(&other);
- if (cmp < 0)
+ if (isDefined() && !isWeak()) {
+ if (config->warnCommon)
+ warn("common " + getName() + " is overridden");
return;
+ }
- if (cmp > 0) {
- if (auto *s = dyn_cast<SharedSymbol>(this)) {
- // Increase st_size if the shared symbol has a larger st_size. The shared
- // symbol may be created from common symbols. The fact that some object
- // files were linked into a shared object first should not change the
- // regular rule that picks the largest st_size.
- uint64_t size = s->size;
- replace(other);
- if (size > cast<CommonSymbol>(this)->size)
- cast<CommonSymbol>(this)->size = size;
- } else {
- replace(other);
+ if (CommonSymbol *oldSym = dyn_cast<CommonSymbol>(this)) {
+ if (config->warnCommon)
+ warn("multiple common of " + getName());
+ oldSym->alignment = std::max(oldSym->alignment, other.alignment);
+ if (oldSym->size < other.size) {
+ oldSym->file = other.file;
+ oldSym->size = other.size;
}
return;
}
- CommonSymbol *oldSym = cast<CommonSymbol>(this);
-
- oldSym->alignment = std::max(oldSym->alignment, other.alignment);
- if (oldSym->size < other.size) {
- oldSym->file = other.file;
- oldSym->size = other.size;
+ if (auto *s = dyn_cast<SharedSymbol>(this)) {
+ // Increase st_size if the shared symbol has a larger st_size. The shared
+ // symbol may be created from common symbols. The fact that some object
+ // files were linked into a shared object first should not change the
+ // regular rule that picks the largest st_size.
+ uint64_t size = s->size;
+ replace(other);
+ if (size > cast<CommonSymbol>(this)->size)
+ cast<CommonSymbol>(this)->size = size;
+ } else {
+ replace(other);
}
}
void Symbol::resolveDefined(const Defined &other) {
- int cmp = compare(&other);
- if (cmp > 0)
+ if (shouldReplace(other))
replace(other);
- else if (cmp == 0)
- reportDuplicate(this, other.file,
- dyn_cast_or_null<InputSectionBase>(other.section),
- other.value);
-}
-
-template <class LazyT>
-static void replaceCommon(Symbol &oldSym, const LazyT &newSym) {
- backwardReferences.erase(&oldSym);
- oldSym.replace(newSym);
- newSym.extract();
}
-template <class LazyT> void Symbol::resolveLazy(const LazyT &other) {
+void Symbol::resolveLazy(const LazyObject &other) {
// For common objects, we want to look for global or weak definitions that
// should be extracted as the canonical definition instead.
- if (isCommon() && elf::config->fortranCommon) {
- if (auto *laSym = dyn_cast<LazyArchive>(&other)) {
- ArchiveFile *archive = cast<ArchiveFile>(laSym->file);
- const Archive::Symbol &archiveSym = laSym->sym;
- if (archive->shouldExtractForCommon(archiveSym)) {
- replaceCommon(*this, other);
- return;
- }
- } else if (auto *loSym = dyn_cast<LazyObject>(&other)) {
- if (loSym->file->shouldExtractForCommon(loSym->getName())) {
- replaceCommon(*this, other);
- return;
- }
- }
+ if (LLVM_UNLIKELY(isCommon()) && elf::config->fortranCommon &&
+ other.file->shouldExtractForCommon(getName())) {
+ ctx->backwardReferences.erase(this);
+ replace(other);
+ other.extract();
+ return;
}
if (!isUndefined()) {
// See the comment in resolveUndefined().
if (isDefined())
- backwardReferences.erase(this);
+ ctx->backwardReferences.erase(this);
return;
}
@@ -746,5 +690,5 @@ void Symbol::resolveShared(const SharedSymbol &other) {
replace(other);
binding = bind;
} else if (traced)
- printTraceSymbol(&other);
+ printTraceSymbol(other, getName());
}