diff options
Diffstat (limited to 'lld/ELF/Symbols.cpp')
| -rw-r--r-- | lld/ELF/Symbols.cpp | 270 |
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()); } |
