Index: COFF/Driver.cpp =================================================================== --- COFF/Driver.cpp +++ COFF/Driver.cpp @@ -594,10 +594,6 @@ } } - // Make sure we have resolved all symbols. - if (Symtab.reportRemainingUndefines()) - return false; - // Do LTO by compiling bitcode input files to a native COFF file // then link that file. if (auto EC = Symtab.addCombinedLTOObject()) { @@ -605,6 +601,10 @@ return false; } + // Make sure we have resolved all symbols. + if (Symtab.reportRemainingUndefines(/*Resolve=*/true)) + return false; + // Windows specific -- if no /subsystem is given, we need to infer // that from entry point name. if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { Index: COFF/InputFiles.cpp =================================================================== --- COFF/InputFiles.cpp +++ COFF/InputFiles.cpp @@ -293,8 +293,11 @@ if (SymbolDef == LTO_SYMBOL_DEFINITION_UNDEFINED) { SymbolBodies.push_back(new (Alloc) Undefined(SymName)); } else { - bool Replaceable = (SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || - (Attrs & LTO_SYMBOL_COMDAT)); + bool Replaceable = + (SymbolDef == LTO_SYMBOL_DEFINITION_TENTATIVE || // common + (Attrs & LTO_SYMBOL_COMDAT) || // comdat + (SymbolDef == LTO_SYMBOL_DEFINITION_WEAK && // weak external + (Attrs & LTO_SYMBOL_ALIAS))); SymbolBodies.push_back(new (Alloc) DefinedBitcode(this, SymName, Replaceable)); } Index: COFF/SymbolTable.h =================================================================== --- COFF/SymbolTable.h +++ COFF/SymbolTable.h @@ -46,8 +46,9 @@ std::error_code run(); bool queueEmpty(); - // Print an error message on undefined symbols. - bool reportRemainingUndefines(); + // Print an error message on undefined symbols. If Resolve is true, try to + // resolve any undefined symbols and update the symbol table accordingly. + bool reportRemainingUndefines(bool Resolve); // Returns a list of chunks of selected symbols. std::vector getChunks(); Index: COFF/SymbolTable.cpp =================================================================== --- COFF/SymbolTable.cpp +++ COFF/SymbolTable.cpp @@ -118,7 +118,7 @@ return ArchiveQueue.empty() && ObjectQueue.empty(); } -bool SymbolTable::reportRemainingUndefines() { +bool SymbolTable::reportRemainingUndefines(bool Resolve) { bool Ret = false; for (auto &I : Symtab) { Symbol *Sym = I.second; @@ -126,20 +126,19 @@ if (!Undef) continue; StringRef Name = Undef->getName(); - // A weak alias may have been resolved, so check for that. A weak alias - // may be a weak alias to another symbol, so check recursively. - for (SymbolBody *A = Undef->WeakAlias; A; - A = cast(A)->WeakAlias) { - if (auto *D = dyn_cast(A->repl())) { + // A weak alias may have been resolved, so check for that. + if (Defined *D = Undef->resolveWeakAlias()) { + if (Resolve) Sym->Body = D; - goto next; - } + continue; } // If we can resolve a symbol by removing __imp_ prefix, do that. // This odd rule is for compatibility with MSVC linker. if (Name.startswith("__imp_")) { Symbol *Imp = find(Name.substr(strlen("__imp_"))); if (Imp && isa(Imp->Body)) { + if (!Resolve) + continue; auto *D = cast(Imp->Body); auto *S = new (Alloc) DefinedLocalImport(Name, D); LocalImportChunks.push_back(S->getChunk()); @@ -151,11 +150,11 @@ // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. if (Config->Force) { - Sym->Body = new (Alloc) DefinedAbsolute(Name, 0); + if (Resolve) + Sym->Body = new (Alloc) DefinedAbsolute(Name, 0); continue; } Ret = true; - next:; } return Ret; } @@ -295,6 +294,12 @@ if (BitcodeFiles.empty()) return std::error_code(); + // Diagnose any undefined symbols early, but do not resolve weak externals, + // as resolution breaks the invariant that each Symbol points to a unique + // SymbolBody, which we rely on to replace DefinedBitcode symbols correctly. + if (reportRemainingUndefines(/*Resolve=*/false)) + return make_error_code(LLDError::BrokenFile); + // Create an object file and add it to the symbol table by replacing any // DefinedBitcode symbols with the definitions in the object file. LTOCodeGenerator CG; @@ -306,21 +311,12 @@ for (SymbolBody *Body : Obj->getSymbols()) { if (!Body->isExternal()) continue; - // Find an existing Symbol. We should not see any new undefined symbols at - // this point. + // We should not see any new undefined symbols at this point, but we'll + // diagnose them later in reportRemainingUndefines(). StringRef Name = Body->getName(); Symbol *Sym = insert(Body); - if (Sym->Body == Body && !isa(Body)) { - llvm::errs() << "LTO: undefined symbol: " << Name << '\n'; - return make_error_code(LLDError::BrokenFile); - } if (isa(Sym->Body)) { - // The symbol should now be defined. - if (!isa(Body)) { - llvm::errs() << "LTO: undefined symbol: " << Name << '\n'; - return make_error_code(LLDError::BrokenFile); - } Sym->Body = Body; continue; } @@ -349,9 +345,6 @@ return make_error_code(LLDError::BrokenFile); } - // New runtime library symbol references may have created undefined references. - if (reportRemainingUndefines()) - return make_error_code(LLDError::BrokenFile); return std::error_code(); } @@ -371,9 +364,12 @@ CG->addMustPreserveSymbol(Body->getName()); // Likewise for other symbols that must be preserved. - for (Undefined *U : Config->GCRoot) - if (isa(U->repl())) - CG->addMustPreserveSymbol(U->getName()); + for (Undefined *U : Config->GCRoot) { + if (auto *S = dyn_cast(U->repl())) + CG->addMustPreserveSymbol(S->getName()); + else if (auto *S = dyn_cast_or_null(U->resolveWeakAlias())) + CG->addMustPreserveSymbol(S->getName()); + } CG->setModule(BitcodeFiles[0]->releaseModule()); for (unsigned I = 1, E = BitcodeFiles.size(); I != E; ++I) Index: COFF/Symbols.h =================================================================== --- COFF/Symbols.h +++ COFF/Symbols.h @@ -254,6 +254,11 @@ // If it remains undefined, it'll be replaced with whatever the // Alias pointer points to. SymbolBody *WeakAlias = nullptr; + + // If this symbol is external weak, try to resolve it to a defined + // symbol by searching the chain of fallback symbols. Returns the symbol if + // successful, otherwise returns null. + Defined *resolveWeakAlias(); }; // Windows-specific classes. Index: COFF/Symbols.cpp =================================================================== --- COFF/Symbols.cpp +++ COFF/Symbols.cpp @@ -236,5 +236,14 @@ return std::move(Obj); } +Defined *Undefined::resolveWeakAlias() { + // A weak alias may be a weak alias to another symbol, so check recursively. + for (SymbolBody *A = WeakAlias; A; A = cast(A)->WeakAlias) { + if (auto *D = dyn_cast(A->repl())) + return D; + } + return nullptr; +} + } // namespace coff } // namespace lld Index: test/COFF/Inputs/entry-mangled.ll =================================================================== --- /dev/null +++ test/COFF/Inputs/entry-mangled.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc18.0.0" + +define void @"\01?main@@YAHXZ"() { + ret void +} Index: test/COFF/Inputs/weak-external.ll =================================================================== --- /dev/null +++ test/COFF/Inputs/weak-external.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define void @g() { + ret void +} Index: test/COFF/Inputs/weak-external2.ll =================================================================== --- /dev/null +++ test/COFF/Inputs/weak-external2.ll @@ -0,0 +1,6 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define void @f() { + ret void +} Index: test/COFF/Inputs/weak-external3.ll =================================================================== --- /dev/null +++ test/COFF/Inputs/weak-external3.ll @@ -0,0 +1,8 @@ +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +@f = weak alias void()* @g + +define void @g() { + ret void +} Index: test/COFF/entry-mangled.test =================================================================== --- test/COFF/entry-mangled.test +++ test/COFF/entry-mangled.test @@ -1,5 +1,7 @@ # RUN: yaml2obj < %s > %t.obj # RUN: lld -flavor link2 /out:%t.exe /entry:main %t.obj +# RUN: llvm-as -o %t.lto.obj %S/Inputs/entry-mangled.ll +# RUN: lld -flavor link2 /out:%t.exe /entry:main %t.lto.obj --- header: Index: test/COFF/weak-external.test =================================================================== --- test/COFF/weak-external.test +++ test/COFF/weak-external.test @@ -1,5 +1,12 @@ # RUN: yaml2obj %s > %t.obj -# RUN: lld -flavor link2 /out:%t.exe /entry:g /subsystem:console %t.obj +# RUN: llvm-as -o %t.lto.obj %S/Inputs/weak-external.ll +# RUN: lld -flavor link2 /out:%t1.exe /entry:g /subsystem:console %t.obj +# RUN: lld -flavor link2 /out:%t2.exe /entry:g /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj +# RUN: FileCheck %s < %t2.map + +# CHECK: lto-llvm{{.*}}: +# CHECK-NOT: : +# CHECK: {{ g$}} --- header: Index: test/COFF/weak-external2.test =================================================================== --- test/COFF/weak-external2.test +++ test/COFF/weak-external2.test @@ -1,5 +1,6 @@ # RUN: yaml2obj %s > %t.obj -# RUN: lld -flavor link2 /out:%t.exe /entry:g /subsystem:console %t.obj +# RUN: llvm-as -o %t.lto.obj %S/Inputs/weak-external2.ll +# RUN: lld -flavor link2 /out:%t.exe /entry:g /subsystem:console %t.obj %t.lto.obj --- header: @@ -13,7 +14,7 @@ symbols: - Name: 'f' Value: 0 - SectionNumber: 1 + SectionNumber: 0 SimpleType: IMAGE_SYM_TYPE_NULL ComplexType: IMAGE_SYM_DTYPE_FUNCTION StorageClass: IMAGE_SYM_CLASS_EXTERNAL Index: test/COFF/weak-external3.test =================================================================== --- test/COFF/weak-external3.test +++ test/COFF/weak-external3.test @@ -1,5 +1,17 @@ # RUN: yaml2obj %s > %t.obj -# RUN: lld -flavor link2 /out:%t.exe /entry:g /subsystem:console %t.obj +# RUN: llvm-as -o %t.lto.obj %S/Inputs/weak-external3.ll +# RUN: lld -flavor link2 /out:%t1.exe /entry:f /subsystem:console /lldmap:%t1.map %t.lto.obj +# RUN: FileCheck --check-prefix=CHECK1 %s < %t1.map +# RUN: lld -flavor link2 /out:%t2.exe /entry:f /subsystem:console /lldmap:%t2.map %t.obj %t.lto.obj +# RUN: FileCheck --check-prefix=CHECK2 %s < %t2.map + +# CHECK1: lto-llvm{{.*}}: +# CHECK1-NOT: : +# CHECK1: {{ g$}} + +# CHECK2: weak-external3{{.*}}: +# CHECK2-NOT: : +# CHECK2: {{ f$}} --- header: @@ -17,13 +29,4 @@ SimpleType: IMAGE_SYM_TYPE_NULL ComplexType: IMAGE_SYM_DTYPE_FUNCTION StorageClass: IMAGE_SYM_CLASS_EXTERNAL - - Name: 'g' - Value: 0 - SectionNumber: 0 - SimpleType: IMAGE_SYM_TYPE_NULL - ComplexType: IMAGE_SYM_DTYPE_FUNCTION - StorageClass: IMAGE_SYM_CLASS_WEAK_EXTERNAL - WeakExternal: - TagIndex: 0 - Characteristics: IMAGE_WEAK_EXTERN_SEARCH_LIBRARY ...