diff --git a/lld/ELF/Driver.h b/lld/ELF/Driver.h --- a/lld/ELF/Driver.h +++ b/lld/ELF/Driver.h @@ -17,6 +17,9 @@ namespace lld { namespace elf { +class InputFile; +class InputSectionBase; +class Symbol; extern std::unique_ptr driver; @@ -45,6 +48,9 @@ public: SmallVector, 0> archiveFiles; + SmallVector< + std::tuple, 0> + duplicates; }; // Parses command line options. diff --git a/lld/ELF/Driver.cpp b/lld/ELF/Driver.cpp --- a/lld/ELF/Driver.cpp +++ b/lld/ELF/Driver.cpp @@ -2379,6 +2379,10 @@ parallelForEach(objectFiles, initializeLocalSymbols); parallelForEach(objectFiles, postParseObjectFile); parallelForEach(bitcodeFiles, [](BitcodeFile *file) { file->postParse(); }); + for (std::tuple d : duplicates) + reportDuplicate(*std::get<0>(d), std::get<1>(d), std::get<2>(d), + std::get<3>(d)); + duplicates.clear(); // Return if there were name resolution errors. if (errorCount()) @@ -2450,6 +2454,10 @@ auto newObjectFiles = makeArrayRef(objectFiles).slice(numObjsBeforeLTO); parallelForEach(newObjectFiles, initializeLocalSymbols); parallelForEach(newObjectFiles, postParseObjectFile); + for (std::tuple d : + duplicates) + reportDuplicate(*std::get<0>(d), std::get<1>(d), std::get<2>(d), + std::get<3>(d)); // Handle --exclude-libs again because lto.tmp may reference additional // libcalls symbols defined in an excluded archive. This may override diff --git a/lld/ELF/InputFiles.cpp b/lld/ELF/InputFiles.cpp --- a/lld/ELF/InputFiles.cpp +++ b/lld/ELF/InputFiles.cpp @@ -1016,7 +1016,6 @@ // its corresponding ELF symbol table. template void ObjFile::initializeSymbols(const object::ELFFile &obj) { - ArrayRef sections(this->sections); SymbolTable &symtab = *elf::symtab; ArrayRef eSyms = this->getELFSyms(); @@ -1044,13 +1043,6 @@ continue; } - if (LLVM_UNLIKELY(secIdx == SHN_XINDEX)) - secIdx = check(getExtendedSymbolTableIndex(eSym, i, shndxTable)); - else if (secIdx >= SHN_LORESERVE) - secIdx = 0; - if (LLVM_UNLIKELY(secIdx >= sections.size())) - fatal(toString(this) + ": invalid section index: " + Twine(secIdx)); - InputSectionBase *sec = sections[secIdx]; uint8_t stOther = eSym.st_other; uint8_t type = eSym.getType(); uint64_t value = eSym.st_value; @@ -1068,30 +1060,11 @@ continue; } - // If a defined symbol is in a discarded section, handle it as if it - // were an undefined symbol. Such symbol doesn't comply with the - // standard, but in practice, a .eh_frame often directly refer - // COMDAT member sections, and if a comdat group is discarded, some - // defined symbol in a .eh_frame becomes dangling symbols. - if (sec == &InputSection::discarded) { - Undefined und{this, StringRef(), binding, stOther, type, secIdx}; - // !LazyObjFile::lazy indicates that the file containing this object has - // not finished processing, i.e. this symbol is a result of a lazy symbol - // extract. We should demote the lazy symbol to an Undefined so that any - // relocations outside of the group to it will trigger a discarded section - // error. - if (sym->symbolKind == Symbol::LazyObjectKind && !sym->file->lazy) - sym->replace(und); - else - sym->resolve(und); - continue; - } - - // Handle global defined symbols. + // Handle global defined symbols. Defined::section will be set in postParse. if (binding == STB_GLOBAL || binding == STB_WEAK || binding == STB_GNU_UNIQUE) { - sym->resolve( - Defined{this, StringRef(), binding, stOther, type, value, size, sec}); + sym->resolve(Defined{this, StringRef(), binding, stOther, type, value, + size, nullptr}); continue; } @@ -1159,7 +1132,8 @@ 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]; + Symbol &sym = *symbols[i]; + uint32_t secIdx = eSym.st_shndx; // st_value of STT_TLS represents the assigned offset, not the actual // address which is used by STT_FUNC and STT_OBJECT. STT_TLS symbols can @@ -1170,23 +1144,45 @@ errorOrWarn("TLS attribute mismatch: " + toString(sym) + "\n>>> in " + toString(sym.file) + "\n>>> in " + toString(this)); - // !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) + // Handle non-COMMON defined symbol below. !sym.file allows a symbol + // assignment redefines a symbol without an error. + if (!sym.file || !sym.isDefined() || secIdx == SHN_UNDEF || + secIdx == SHN_COMMON) continue; - uint32_t secIdx = eSym.st_shndx; + if (LLVM_UNLIKELY(secIdx == SHN_XINDEX)) - secIdx = cantFail(getExtendedSymbolTableIndex(eSym, i, shndxTable)); + secIdx = check(getExtendedSymbolTableIndex(eSym, i, shndxTable)); else if (secIdx >= SHN_LORESERVE) secIdx = 0; - if (sections[secIdx] == &InputSection::discarded) + if (LLVM_UNLIKELY(secIdx >= sections.size())) + fatal(toString(this) + ": invalid section index: " + Twine(secIdx)); + InputSectionBase *sec = sections[secIdx]; + if (sec == &InputSection::discarded) { + if (sym.traced) { + printTraceSymbol(Undefined{this, sym.getName(), sym.binding, + sym.stOther, sym.type, secIdx}, + sym.getName()); + } + if (sym.file == this) + sym.replace(Undefined{this, sym.getName(), sym.binding, sym.stOther, + sym.type, secIdx}); + continue; + } + + if (sym.file == this) { + cast(sym).section = sec; + continue; + } + + if (eSym.getBinding() == STB_WEAK) continue; // Allow absolute symbols with the same value for GNU ld compatibility. - if (!cast(sym).section && !sections[secIdx] && + if (!cast(sym).section && !sec && cast(sym).value == eSym.st_value) continue; - reportDuplicate(sym, this, sections[secIdx], eSym.st_value); + static std::mutex mu; + std::lock_guard lock(mu); + driver->duplicates.emplace_back(&sym, this, sec, eSym.st_value); } } diff --git a/lld/ELF/Symbols.cpp b/lld/ELF/Symbols.cpp --- a/lld/ELF/Symbols.cpp +++ b/lld/ELF/Symbols.cpp @@ -555,7 +555,11 @@ InputSectionBase *errSec, uint64_t errOffset) { if (config->allowMultipleDefinition) return; - const Defined *d = cast(&sym); + // With concurrency, sym might be Defined in postParse but later got changed + // to Undefined. + const Defined *d = dyn_cast(&sym); + if (!d) + return; if (!d->section || !errSec) { error("duplicate symbol: " + toString(sym) + "\n>>> defined in " + toString(sym.file) + "\n>>> defined in " + toString(newFile)); diff --git a/lld/test/ELF/comdat-discarded-lazy.s b/lld/test/ELF/comdat-discarded-lazy.s --- a/lld/test/ELF/comdat-discarded-lazy.s +++ b/lld/test/ELF/comdat-discarded-lazy.s @@ -47,12 +47,12 @@ # ZZ-NEXT: >>> prevailing definition is in {{.*}}1.o # ZZ-NEXT: >>> referenced by {{.*}}zz.o:(.text+0x1) -## Don't error if the symbol which would cause "discarded section" -## was inserted before %tzz.o +## The definition in %tdef.o is outside a group. Currently we give an error +## because %tdef.o is not extracted. # RUN: echo '.globl zz; zz:' | llvm-mc -filetype=obj -triple=x86_64 - -o %tdef.o -# RUN: ld.lld %t.o --start-lib %t1.o %tdef.o %tzz.o --end-lib -o /dev/null +# RUN: not ld.lld %t.o --start-lib %t1.o %tdef.o %tzz.o --end-lib -o /dev/null 2>&1 | FileCheck --check-prefix=ZZ %s # RUN: rm -f %tdef.a && llvm-ar rc %tdef.a %tdef.o -# RUN: ld.lld %t.o --start-lib %t1.o %tdef.a %tzz.o --end-lib -o /dev/null +# RUN: not ld.lld %t.o --start-lib %t1.o %tdef.a %tzz.o --end-lib -o /dev/null 2>&1 | FileCheck --check-prefix=ZZ %s .globl _start _start: diff --git a/lld/test/ELF/duplicated-synthetic-sym.s b/lld/test/ELF/duplicated-synthetic-sym.s --- a/lld/test/ELF/duplicated-synthetic-sym.s +++ b/lld/test/ELF/duplicated-synthetic-sym.s @@ -9,8 +9,8 @@ // RUN: not ld.lld %t.o --format binary file.bin -o /dev/null 2>&1 | FileCheck %s // CHECK: duplicate symbol: _binary_file_bin_start -// CHECK-NEXT: defined at {{.*}}.o:(.text+0x0) -// CHECK-NEXT: defined at file.bin:(.data+0x0) +// CHECK-NEXT: defined in {{.*}}.o +// CHECK-NEXT: defined in .globl _binary_file_bin_start _binary_file_bin_start: diff --git a/lld/test/ELF/exclude-discarded-error2.s b/lld/test/ELF/exclude-discarded-error2.s --- a/lld/test/ELF/exclude-discarded-error2.s +++ b/lld/test/ELF/exclude-discarded-error2.s @@ -1,14 +1,14 @@ # REQUIRES: x86 # RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o -# RUN: echo '.section .foo,"ae"; .weak foo; foo:' | \ +# RUN: echo '.section .foo,"aG",@progbits,zz,comdat; .weak foo; foo:' | \ # RUN: llvm-mc -filetype=obj -triple=x86_64 - -o %t1.o -# RUN: not ld.lld %t.o %t1.o -o /dev/null 2>&1 | FileCheck %s +# RUN: ld.lld %t.o %t1.o -o %t +# RUN: llvm-readelf -s %t | FileCheck %s -# Because foo defined in %t1.o is weak, it does not override global undefined -# in %t.o -# CHECK-NOT: discarded section -# CHECK: undefined symbol: foo +# CHECK: NOTYPE WEAK DEFAULT UND foo .globl _start _start: jmp foo + +.section .foo,"aG",@progbits,zz,comdat diff --git a/lld/test/ELF/lto/comdat-mixed-archive.test b/lld/test/ELF/lto/comdat-mixed-archive.test --- a/lld/test/ELF/lto/comdat-mixed-archive.test +++ b/lld/test/ELF/lto/comdat-mixed-archive.test @@ -31,8 +31,8 @@ TRACE-NEXT: lib.a(obj.o): lazy definition of bar TRACE-NEXT: lib.a(bc.bc): definition of foo TRACE-NEXT: lib.a(bc.bc): reference to bar -TRACE-NEXT: lib.a(obj.o): reference to foo TRACE-NEXT: lib.a(obj.o): definition of bar +TRACE-NEXT: lib.a(obj.o): reference to foo TRACE-NEXT: : reference to foo ;; The definition of "foo" is visible outside the LTO result. TRACE-NEXT: lto.tmp: definition of foo