diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp --- a/lld/COFF/Driver.cpp +++ b/lld/COFF/Driver.cpp @@ -1751,24 +1751,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 (unless -thinlto-index-only was given, in which case we - // resolve symbols and write indices, but don't generate native code or link). - symtab->addCombinedLTOObjects(); - - // If -thinlto-index-only is given, we should create only "index - // files" and not object files. Index file creation is already done - // in addCombinedLTOObject, so we are done if that's the case. - if (config->thinLTOIndexOnly) - return; - - // If we generated native object files from bitcode files, this resolves - // references to the symbols we use from them. - run(); - if (args.hasArg(OPT_include_optional)) { // Handle /includeoptional for (auto *arg : args.filtered(OPT_include_optional)) @@ -1795,8 +1777,29 @@ 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 (unless -thinlto-index-only was given, in which case we + // resolve symbols and write indices, but don't generate native code or link). + symtab->addCombinedLTOObjects(); + + // If -thinlto-index-only is given, we should create only "index + // files" and not object files. Index file creation is already done + // in addCombinedLTOObject, so we are done if that's the case. + if (config->thinLTOIndexOnly) + return; + + // If we generated native object files from bitcode files, this resolves + // references to the symbols we use from them. + 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 @@ -69,7 +69,7 @@ for (Symbol *s : sc->file->getSymbols()) { auto *d = dyn_cast_or_null(s); - if (!d || !d->data || d->getChunk() != sc || d->getValue() > addr || + if (!d || !d->data || d->file != sc->file || d->getChunk() != sc || d->getValue() > addr || (candidate && d->getValue() < candidate->getValue())) continue; @@ -79,6 +79,17 @@ return candidate; } +static std::vector getSymbolLocations(BitcodeFile *File) { + std::vector res(1); + llvm::raw_string_ostream os(res[0]); + os << "\n>>> referenced by "; + StringRef Source = File->obj->getSourceFileName(); + if (!Source.empty()) + os << Source.str() << "\n>>> "; + os << toString(File); + return res; +} + // Given a file and the index of a symbol in that file, returns a description // of all references to that symbol from that file. If no debug information is // available, returns just the name of the file, else one string per actual @@ -123,13 +134,23 @@ return symbolLocations; } +std::vector getSymbolLocations(InputFile *file, uint32_t symIndex) { + assert(file->kind() == InputFile::Kind::ObjectKind || + file->kind() == InputFile::Kind::BitcodeKind); + if (auto *o = dyn_cast(file)) + return getSymbolLocations(o, symIndex); + if (auto *b = dyn_cast(file)) + return getSymbolLocations(b); + return {}; +} + // For an undefined symbol, stores all files referencing it and the index of // the undefined symbol in each file. struct UndefinedDiag { Symbol *sym; struct File { - ObjFile *oFile; - uint64_t symIndex; + InputFile *file; + uint32_t symIndex; }; std::vector files; }; @@ -143,7 +164,7 @@ size_t i = 0, numRefs = 0; for (const UndefinedDiag::File &ref : undefDiag.files) { std::vector symbolLocations = - getSymbolLocations(ref.oFile, ref.symIndex); + getSymbolLocations(ref.file, ref.symIndex); numRefs += symbolLocations.size(); for (const std::string &s : symbolLocations) { if (i >= maxUndefReferences) @@ -232,7 +253,74 @@ 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)); + + std::vector undefDiags; + DenseMap firstDiag; + + for (ObjFile *file : ObjFile::instances) { + uint32_t symIndex = (uint32_t) -1; + for (Symbol *sym : file->getSymbols()) { + ++symIndex; + if (!sym) + continue; + if (undefs.count(sym)) { + auto it = firstDiag.find(sym); + if (it == firstDiag.end()) { + firstDiag[sym] = undefDiags.size(); + undefDiags.push_back({sym, {{file, symIndex}}}); + } else { + undefDiags[it->second].files.push_back({file, symIndex}); + } + } + } + } + + for (BitcodeFile *file : BitcodeFile::instances) { + for (Symbol *sym : file->getSymbols()) { + if (!sym) + continue; + if (undefs.count(sym)) { + auto it = firstDiag.find(sym); + if (it == firstDiag.end()) { + firstDiag[sym] = undefDiags.size(); + undefDiags.push_back({sym, {{file, 0}}}); + } else { + undefDiags[it->second].files.push_back({file, 0}); + } + } + } + } + + for (const UndefinedDiag& undefDiag : undefDiags) + reportUndefinedSymbol(undefDiag); +} + +void SymbolTable::resolveRemainingUndefines() { SmallPtrSet undefs; DenseMap localImports; @@ -280,9 +368,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) @@ -306,7 +391,7 @@ DenseMap firstDiag; for (ObjFile *file : ObjFile::instances) { - size_t symIndex = (size_t)-1; + uint32_t symIndex = (uint32_t) -1; for (Symbol *sym : file->getSymbols()) { ++symIndex; if (!sym) 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,30 @@ +; 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 +; RUN: ls %t | count 2 + +; 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: foo.obj +; FILE: main.obj + +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 +; RUN: ls %t | count 2 + +; 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 {{.*}}main.obj +; FILE: foo.obj +; FILE: main.obj + +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()