diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2118,6 +2118,8 @@ map.try_emplace(sym, sym2); // If both foo@v1 and foo@@v1 are defined and non-weak, report a duplicate // definition error. + if (sym->isDefined()) + sym2->checkDuplicate(cast(*sym)); sym2->resolve(*sym); // Eliminate foo@v1 from the symbol table. sym->symbolKind = Symbol::PlaceholderKind; @@ -2340,6 +2342,10 @@ for (auto *s : lto::LTO::getRuntimeLibcallSymbols()) handleLibcall(s); + // No more lazy bitcode can be extracted at this point. Check duplicate + // symbols. + parallelForEach(bitcodeFiles, [](BitcodeFile *file) { file->postParse(); }); + // Return if there were name resolution errors. if (errorCount()) return; @@ -2404,6 +2410,27 @@ if (skipLinkedOutput) return; + // compileBitcodeFiles may have produced lto.tmp object files. No more file is + // added at this point. + parallelForEach(objectFiles, [](ELFFileBase *file) { + switch (config->ekind) { + case ELF32LEKind: + cast>(file)->postParse(); + break; + case ELF32BEKind: + cast>(file)->postParse(); + break; + case ELF64LEKind: + cast>(file)->postParse(); + break; + case ELF64BEKind: + cast>(file)->postParse(); + break; + default: + llvm_unreachable(""); + } + }); + // Handle --exclude-libs again because lto.tmp may reference additional // libcalls symbols defined in an excluded archive. This may override // versionId set by scanVersionScript(). diff --git a/lld/ELF/InputFiles.h b/lld/ELF/InputFiles.h --- a/lld/ELF/InputFiles.h +++ b/lld/ELF/InputFiles.h @@ -273,6 +273,8 @@ // Get cached DWARF information. DWARFCache *getDwarf(); + void postParse(); + private: void initializeSections(bool ignoreComdats, const llvm::object::ELFFile &obj); @@ -315,7 +317,9 @@ static bool classof(const InputFile *f) { return f->kind() == BitcodeKind; } template void parse(); void parseLazy(); + void postParse(); std::unique_ptr obj; + std::vector keptComdats; }; // .so file. diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1149,6 +1149,33 @@ } } +// Called after all ObjFile::parse is called for all ObjFiles. This checks +// duplicate symbols and may do symbol property merge in the future. +template void ObjFile::postParse() { + ArrayRef eSyms = this->getELFSyms(); + for (size_t i = firstGlobal, end = eSyms.size(); i != end; ++i) { + const Elf_Sym &eSym = eSyms[i]; + const Symbol &sym = *symbols[i]; + // !sym.file allows a symbol assignment redefines a symbol without an error. + if (sym.file == this || !sym.file || !sym.isDefined() || + eSym.st_shndx == SHN_UNDEF || eSym.st_shndx == SHN_COMMON || + eSym.getBinding() == STB_WEAK) + continue; + uint32_t secIdx = eSym.st_shndx; + if (LLVM_UNLIKELY(secIdx == SHN_XINDEX)) + secIdx = cantFail(getExtendedSymbolTableIndex(eSym, i, shndxTable)); + else if (secIdx >= SHN_LORESERVE) + secIdx = 0; + if (sections[secIdx] == &InputSection::discarded) + continue; + // Allow absolute symbols with the same value for GNU ld compatibility. + if (!cast(sym).section && !sections[secIdx] && + cast(sym).value == eSym.st_value) + continue; + reportDuplicate(sym, this, sections[secIdx], eSym.st_value); + } +} + // The handling of tentative definitions (COMMON symbols) in archives is murky. // A tentative definition will be promoted to a global definition if there are // no non-tentative definitions to dominate it. When we hold a tentative @@ -1617,7 +1644,6 @@ } template void BitcodeFile::parse() { - std::vector keptComdats; for (std::pair s : obj->getComdatTable()) { keptComdats.push_back( s.second == Comdat::NoDeduplicate || @@ -1646,6 +1672,20 @@ } } +void BitcodeFile::postParse() { + for (auto it : llvm::enumerate(obj->symbols())) { + const Symbol &sym = *symbols[it.index()]; + const auto &objSym = it.value(); + if (sym.file == this || !sym.isDefined() || objSym.isUndefined() || + objSym.isCommon() || objSym.isWeak()) + continue; + int c = objSym.getComdatIndex(); + if (c != -1 && !keptComdats[c]) + continue; + reportDuplicate(sym, this, nullptr, 0); + } +} + void BinaryFile::parse() { ArrayRef data = arrayRefFromStringRef(mb.getBuffer()); auto *section = make(this, SHF_ALLOC | SHF_WRITE, SHT_PROGBITS, @@ -1663,12 +1703,15 @@ llvm::StringSaver &saver = lld::saver(); - symtab->addSymbol(Defined{nullptr, saver.save(s + "_start"), STB_GLOBAL, - STV_DEFAULT, STT_OBJECT, 0, 0, section}); - symtab->addSymbol(Defined{nullptr, saver.save(s + "_end"), STB_GLOBAL, - STV_DEFAULT, STT_OBJECT, data.size(), 0, section}); - symtab->addSymbol(Defined{nullptr, saver.save(s + "_size"), STB_GLOBAL, - STV_DEFAULT, STT_OBJECT, data.size(), 0, nullptr}); + symtab->addAndCheckDuplicate(Defined{nullptr, saver.save(s + "_start"), + STB_GLOBAL, STV_DEFAULT, STT_OBJECT, 0, + 0, section}); + symtab->addAndCheckDuplicate(Defined{nullptr, saver.save(s + "_end"), + STB_GLOBAL, STV_DEFAULT, STT_OBJECT, + data.size(), 0, section}); + symtab->addAndCheckDuplicate(Defined{nullptr, saver.save(s + "_size"), + STB_GLOBAL, STV_DEFAULT, STT_OBJECT, + data.size(), 0, nullptr}); } InputFile *elf::createObjectFile(MemoryBufferRef mb, StringRef archiveName, diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -39,6 +39,7 @@ Symbol *insert(StringRef name); Symbol *addSymbol(const Symbol &newSym); + Symbol *addAndCheckDuplicate(const Defined &newSym); void scanVersionScript(); diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -102,6 +102,16 @@ return sym; } +// This variant of addSymbol is used by BinaryFile::parse to check duplicate +// symbol errors. +Symbol *SymbolTable::addAndCheckDuplicate(const Defined &newSym) { + Symbol *sym = insert(newSym.getName()); + if (sym->isDefined()) + sym->checkDuplicate(newSym); + sym->resolve(newSym); + return sym; +} + Symbol *SymbolTable::find(StringRef name) { auto it = symMap.find(CachedHashStringRef(name)); if (it == symMap.end()) diff --git a/lld/ELF/Symbols.h b/lld/ELF/Symbols.h --- a/lld/ELF/Symbols.h +++ b/lld/ELF/Symbols.h @@ -213,6 +213,8 @@ // non-lazy object causes a runtime error. void extract() const; + void checkDuplicate(const Defined &other) const; + private: void resolveUndefined(const Undefined &other); void resolveCommon(const CommonSymbol &other); @@ -569,6 +571,8 @@ Defined(std::forward(args)...); } +void reportDuplicate(const Symbol &sym, InputFile *newFile, + InputSectionBase *errSec, uint64_t errOffset); void maybeWarnUnorderableSymbol(const Symbol *sym); bool computeIsPreemptible(const Symbol &sym); void reportBackrefs(); diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -572,21 +572,11 @@ return -1; } - auto *oldSym = cast(this); - auto *newSym = cast(other); - - if (isa_and_nonnull(other->file)) - return 0; - - if (!oldSym->section && !newSym->section && oldSym->value == newSym->value && - newSym->binding == STB_GLOBAL) - return -1; - return 0; } -static void reportDuplicate(const Symbol &sym, InputFile *newFile, - InputSectionBase *errSec, uint64_t errOffset) { +void elf::reportDuplicate(const Symbol &sym, InputFile *newFile, + InputSectionBase *errSec, uint64_t errOffset) { if (config->allowMultipleDefinition) return; const Defined *d = cast(&sym); @@ -619,6 +609,13 @@ error(msg); } +void Symbol::checkDuplicate(const Defined &other) const { + if (compare(&other) == 0) + reportDuplicate(*this, other.file, + dyn_cast_or_null(other.section), + other.value); +} + void Symbol::resolveCommon(const CommonSymbol &other) { int cmp = compare(&other); if (cmp < 0) @@ -653,10 +650,10 @@ int cmp = compare(&other); if (cmp > 0) replace(other); - else if (cmp == 0) - reportDuplicate(*this, other.file, - dyn_cast_or_null(other.section), - other.value); + // else if (cmp == 0) + // reportDuplicate(this, other.file, + // dyn_cast_or_null(other.section), + // other.value); } template diff --git a/lld/test/ELF/invalid/symtab-sh-info-dup.test b/lld/test/ELF/invalid/symtab-sh-info-dup.test --- a/lld/test/ELF/invalid/symtab-sh-info-dup.test +++ b/lld/test/ELF/invalid/symtab-sh-info-dup.test @@ -9,11 +9,8 @@ # RUN: not ld.lld %t.o %t.o -o /dev/null 2>&1 | FileCheck %s # CHECK: error: {{.*}}.o: STB_LOCAL symbol (2) found at index >= .symtab's sh_info (1) -# CHECK-NEXT: error: duplicate symbol: _start -# CHECK-NEXT: >>> defined at {{.*}}.o:(.text+0x0) -# CHECK-NEXT: >>> defined at {{.*}}.o:(.text+0x0) -# CHECK-EMPTY: # CHECK-NEXT: error: {{.*}}.o: STB_LOCAL symbol (2) found at index >= .symtab's sh_info (1) +# CHECK-EMPTY: # RUN: ld.lld --noinhibit-exec %t.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=WARN # WARN: warning: {{.*}}.o: STB_LOCAL symbol (2) found at index >= .symtab's sh_info (1) diff --git a/lld/test/ELF/lto/duplicated.ll b/lld/test/ELF/lto/duplicated.ll --- a/lld/test/ELF/lto/duplicated.ll +++ b/lld/test/ELF/lto/duplicated.ll @@ -8,6 +8,10 @@ ;; --thinlto-index-only skips some passes. Test the error is present. ; RUN: not ld.lld %t/a.bc %t/a.bc --thinlto-index-only -o /dev/null 2>&1 | FileCheck %s ; RUN: not ld.lld %t/b.o %t/a.bc --lto-emit-asm -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK2 +; RUN: not ld.lld %t/a.bc %t/b.o -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK2 + +;; Ideally this should report an error. +; RUN: ld.lld %t/a.bc %t/b.o --thinlto-index-only -o /dev/null 2>&1 | count 0 ;; --undefined-glob g extracts %t/c.bc which causes a duplicate symbol error. ; RUN: not ld.lld %t/a.bc --start-lib %t/c.bc --undefined-glob g --thinlto-index-only -o /dev/null 2>&1 | FileCheck %s --check-prefix=CHECK @@ -17,8 +21,6 @@ ; CHECK-NEXT: >>> defined in {{.*}}.bc ; CHECK2: duplicate symbol: f -; CHECK2-NEXT: >>> defined in {{.*}}.o -; CHECK2-NEXT: >>> defined in {{.*}}.bc ;--- a.ll target triple = "x86_64-unknown-linux-gnu" diff --git a/lld/test/ELF/vs-diagnostics-duplicate.s b/lld/test/ELF/vs-diagnostics-duplicate.s --- a/lld/test/ELF/vs-diagnostics-duplicate.s +++ b/lld/test/ELF/vs-diagnostics-duplicate.s @@ -2,7 +2,7 @@ // RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t1.o // RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/vs-diagnostics-duplicate2.s -o %t2.o // RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %S/Inputs/vs-diagnostics-duplicate3.s -o %t3.o -// RUN: not ld.lld --vs-diagnostics %t1.o %t2.o %t3.o -o /dev/null 2>&1 | FileCheck %s +// RUN: not ld.lld --vs-diagnostics --threads=1 %t1.o %t2.o %t3.o -o /dev/null 2>&1 | FileCheck %s // Case 1. The source locations are unknown for both symbols. // CHECK: {{.*}}ld.lld{{.*}}: error: duplicate symbol: foo