diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -1669,14 +1669,6 @@ addUndefined(mangle("_load_config_used")); } while (run()); - if (errorCount()) - return; - - // Do LTO by compiling bitcode input files to a set of native COFF files then - // link those files. - Symtab->addCombinedLTOObjects(); - run(); - if (Config->MinGW) { // Load any further object files that might be needed for doing automatic // imports. @@ -1695,8 +1687,20 @@ run(); } - // Make sure we have resolved all symbols. - Symtab->reportRemainingUndefines(); + // At this point, we should not have any symbols that cannot be resolved. + if (!Config->ForceUnresolved) + Symtab->reportUnresolvable(); + + if (errorCount()) + return; + + // Do LTO by compiling bitcode input files to a set of native COFF files then + // link those files. + Symtab->addCombinedLTOObjects(); + run(); + + // Resolve remaining undefined symbols and warn about imported locals. + Symtab->resolveRemainingUndefines(); if (errorCount()) return; diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h --- a/lld/COFF/SymbolTable.h +++ b/lld/COFF/SymbolTable.h @@ -49,10 +49,13 @@ public: void addFile(InputFile *File); + // Emit errors for symbols that cannot be resolved. + void reportUnresolvable(); + // Try to resolve any undefined symbols and update the symbol table // accordingly, then print an error message for any remaining undefined - // symbols. - void reportRemainingUndefines(); + // symbols and warn about imported local symbols. + void resolveRemainingUndefines(); void loadMinGWAutomaticImports(); bool handleMinGWAutomaticImport(Symbol *Sym, StringRef Name); diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -68,7 +68,8 @@ for (Symbol *S : SC->File->getSymbols()) { auto *D = dyn_cast_or_null(S); - if (!D || D->getChunk() != SC || D->getValue() > Addr || + if (!D || D->File != SC->File || D->getChunk() != SC || + D->getValue() > Addr || (Candidate && D->getValue() < Candidate->getValue())) continue; @@ -78,6 +79,15 @@ return Candidate; } +static std::string getSymbolLocations(BitcodeFile *File) { + std::string Result = "\n>>> referenced by "; + StringRef Source = File->Obj->getSourceFileName(); + if (!Source.empty()) + Result += Source.str() + "\n>>> "; + Result += toString(File); + return Result; +} + std::string getSymbolLocations(ObjFile *File, uint32_t SymIndex) { struct Location { Symbol *Sym; @@ -192,7 +202,57 @@ return true; } -void SymbolTable::reportRemainingUndefines() { +void SymbolTable::reportUnresolvable() { + SmallPtrSet Undefs; + for (auto &I : SymMap) { + Symbol *Sym = I.second; + auto *Undef = dyn_cast(Sym); + if (!Undef) + continue; + if (Defined *D = Undef->getWeakAlias()) + continue; + StringRef Name = Undef->getName(); + if (Name.startswith("__imp_")) { + Symbol *Imp = find(Name.substr(strlen("__imp_"))); + if (Imp && isa(Imp)) + continue; + } + if (Name.contains("_PchSym_")) + continue; + if (Config->MinGW && handleMinGWAutomaticImport(Sym, Name)) + continue; + Undefs.insert(Sym); + } + + for (Symbol *B : Config->GCRoot) + if (Undefs.count(B)) + error(": undefined symbol: " + toString(*B)); + + for (ObjFile *File : ObjFile::Instances) { + size_t SymIndex = (size_t)-1; + for (Symbol *Sym : File->getSymbols()) { + ++SymIndex; + if (!Sym) + continue; + if (Undefs.count(Sym)) { + error("undefined symbol: " + toString(*Sym) + + getSymbolLocations(File, SymIndex)); + } + } + } + + for (BitcodeFile *File : BitcodeFile::Instances) { + for (Symbol *Sym : File->getSymbols()) { + if (!Sym) + continue; + if (Undefs.count(Sym)) { + error("undefined symbol: " + toString(*Sym) + getSymbolLocations(File)); + } + } + } +} + +void SymbolTable::resolveRemainingUndefines() { SmallPtrSet Undefs; DenseMap LocalImports; @@ -240,9 +300,6 @@ if (Name.contains("_PchSym_")) continue; - if (Config->MinGW && handleMinGWAutomaticImport(Sym, Name)) - continue; - // Remaining undefined symbols are not fatal if /force is specified. // They are replaced with dummy defined symbols. if (Config->ForceUnresolved) diff --git a/lld/test/COFF/unresolved-lto-bitcode.ll b/lld/test/COFF/unresolved-lto-bitcode.ll new file mode 100644 --- /dev/null +++ b/lld/test/COFF/unresolved-lto-bitcode.ll @@ -0,0 +1,31 @@ +; REQUIRES: x86 +; RUN: rm -fr %t +; RUN: mkdir %t +; RUN: opt -thinlto-bc -o %t/main.obj %s +; RUN: opt -thinlto-bc -o %t/foo.obj %S/Inputs/lto-dep.ll +; RUN: not lld-link -lldsavetemps -out:%t/main.exe -entry:main \ +; RUN: -subsystem:console %t/main.obj %t/foo.obj 2>&1 | FileCheck %s +; RUN: ls %t | sort | FileCheck --check-prefix=FILE %s + +; Check that the undefined symbol is reported, and that only the two +; object files we created are present in the directory (indicating that +; LTO did not run). +; CHECK: undefined symbol: bar +; CHECK: referenced by {{.*}}unresolved-lto-bitcode.ll +; CHECK: >>> {{.*}}unresolved-lto-bitcode.ll.tmp/main.obj +; FILE-NOT: {{.}} +; FILE: foo.obj +; FILE: main.obj +; FILE-NOT: {{.}} + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define i32 @main() { + call void @foo() + call void @bar() + ret i32 0 +} + +declare void @bar() +declare void @foo() diff --git a/lld/test/COFF/unresolved-lto.ll b/lld/test/COFF/unresolved-lto.ll new file mode 100644 --- /dev/null +++ b/lld/test/COFF/unresolved-lto.ll @@ -0,0 +1,29 @@ +; REQUIRES: x86 +; RUN: rm -fr %t +; RUN: mkdir %t +; RUN: llc -filetype=obj -o %t/main.obj %s +; RUN: opt -thinlto-bc -o %t/foo.obj %S/Inputs/lto-dep.ll +; RUN: not lld-link -lldsavetemps -out:%t/main.exe -entry:main \ +; RUN: -subsystem:console %t/main.obj %t/foo.obj 2>&1 | FileCheck %s +; RUN: ls %t | sort | FileCheck --check-prefix=FILE %s + +; Check that the undefined symbol is reported, and that only the two +; object files we created are present in the directory (indicating that +; LTO did not run). +; CHECK: undefined symbol: bar +; FILE-NOT: {{.}} +; FILE: foo.obj +; FILE: main.obj +; FILE-NOT: {{.}} + +target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-windows-msvc" + +define i32 @main() { + call void @foo() + call void @bar() + ret i32 0 +} + +declare void @bar() +declare void @foo()