-
Notifications
You must be signed in to change notification settings - Fork 13.4k
[LLD][COFF] Don't dllimport from static libraries #134443
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This reverts commit 6a1bdd9 and re-instate behavior that matches what MSVC link.exe does, that is error out when trying to dllimport a symbol from a static library. We also now hint in stdout that we should rather dllimport the symbol from a import library. Fixes llvm#131807
@llvm/pr-subscribers-platform-windows @llvm/pr-subscribers-lld Author: Alexandre Ganea (aganea) ChangesThis reverts commit 6a1bdd9 and re-instate behavior that matches what MSVC link.exe does, that is, error out when trying to dllimport a symbol from a static library. We also now hint in stdout that we should rather dllimport the symbol from a import library. Fixes #131807 Full diff: https://github.com/llvm/llvm-project/pull/134443.diff 5 Files Affected:
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 9bbab524b1f9a..7aa13bdce488e 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2663,10 +2663,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
createECExportThunks();
// Resolve remaining undefined symbols and warn about imported locals.
- ctx.forEachSymtab([&](SymbolTable &symtab) {
- while (symtab.resolveRemainingUndefines())
- run();
- });
+ ctx.forEachSymtab(
+ [&](SymbolTable &symtab) { symtab.resolveRemainingUndefines(); });
if (errorCount())
return;
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 16afcc64316e3..d1fb6e05a0320 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -231,6 +231,17 @@ void SymbolTable::reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
}
if (numDisplayedRefs < numRefs)
diag << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times";
+
+ // Hints
+ StringRef name = undefDiag.sym->getName();
+ if (name.starts_with("__imp_")) {
+ Symbol *imp = find(name.substr(strlen("__imp_")));
+ if (imp && imp->isLazy()) {
+ diag << "\nNOTE: a relevant symbol '" << imp->getName()
+ << "' is available in " << toString(imp->getFile())
+ << " but cannot be used because it is not a import library.";
+ }
+ }
}
void SymbolTable::loadMinGWSymbols() {
@@ -432,11 +443,10 @@ void SymbolTable::reportUnresolvable() {
reportProblemSymbols(undefs, /*localImports=*/nullptr, true);
}
-bool SymbolTable::resolveRemainingUndefines() {
+void SymbolTable::resolveRemainingUndefines() {
llvm::TimeTraceScope timeScope("Resolve remaining undefined symbols");
SmallPtrSet<Symbol *, 8> undefs;
DenseMap<Symbol *, Symbol *> localImports;
- bool foundLazy = false;
for (auto &i : symMap) {
Symbol *sym = i.second;
@@ -481,11 +491,6 @@ bool SymbolTable::resolveRemainingUndefines() {
imp = findLocalSym(*mangledName);
}
}
- if (imp && imp->isLazy()) {
- forceLazy(imp);
- foundLazy = true;
- continue;
- }
if (imp && isa<Defined>(imp)) {
auto *d = cast<Defined>(imp);
replaceSymbol<DefinedLocalImport>(sym, ctx, name, d);
@@ -513,7 +518,6 @@ bool SymbolTable::resolveRemainingUndefines() {
reportProblemSymbols(
undefs, ctx.config.warnLocallyDefinedImported ? &localImports : nullptr,
false);
- return foundLazy;
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index da19bf2800ecf..15e2644a6f519 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -67,10 +67,7 @@ class SymbolTable {
// Try to resolve any undefined symbols and update the symbol table
// accordingly, then print an error message for any remaining undefined
// symbols and warn about imported local symbols.
- // Returns whether more files might need to be linked in to resolve lazy
- // symbols, in which case the caller is expected to call the function again
- // after linking those files.
- bool resolveRemainingUndefines();
+ void resolveRemainingUndefines();
// Load lazy objects that are needed for MinGW automatic import and for
// doing stdcall fixups.
diff --git a/lld/test/COFF/imports-static-lib.test b/lld/test/COFF/imports-static-lib.test
new file mode 100644
index 0000000000000..63539cab9d860
--- /dev/null
+++ b/lld/test/COFF/imports-static-lib.test
@@ -0,0 +1,32 @@
+# REQUIRES: x86
+
+# Ensure that we don't import dllimport symbols from static (non-import) libraries
+# RUN: split-file %s %t.dir
+# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/foo.s -o %t.foo.obj
+# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/main.s -o %t.main.obj
+# RUN: llvm-lib %t.foo.obj -out:%t.foo.lib
+# RUN: not lld-link %t.foo.lib %t.main.obj -out:%t.dll -dll 2>&1 | FileCheck %s
+
+CHECK: error: undefined symbol: __declspec(dllimport) foo
+
+# Now do the same thing, but import the symbol from a import library.
+# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/foo_dll_main.s -o %t.foo_dll_main.obj
+# RUN: lld-link /out:%t.dll /dll %t.foo.obj %t.foo_dll_main.obj /export:foo /implib:%t.foo.imp.lib
+# RUN: lld-link %t.main.obj %t.foo.imp.lib -out:%t.exe -dll
+
+#--- foo.s
+.text
+.globl foo
+foo:
+ ret
+#--- foo_dll_main.s
+.text
+.global _DllMainCRTStartup
+_DllMainCRTStartup:
+ ret
+#--- main.s
+.text
+.global _DllMainCRTStartup
+_DllMainCRTStartup:
+ call *__imp_foo(%rip)
+ ret
diff --git a/lld/test/COFF/undefined_lazy.test b/lld/test/COFF/undefined_lazy.test
deleted file mode 100644
index ed5cd358b5cd9..0000000000000
--- a/lld/test/COFF/undefined_lazy.test
+++ /dev/null
@@ -1,26 +0,0 @@
-# REQUIRES: x86
-
-# RUN: split-file %s %t.dir
-# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/foo.s -o %t.foo.obj
-# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/bar.s -o %t.bar.obj
-# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/qux.s -o %t.qux.obj
-# RUN: llvm-lib %t.foo.obj -out:%t.foo.lib
-# RUN: llvm-lib %t.bar.obj -out:%t.bar.lib
-# RUN: lld-link %t.foo.lib %t.bar.lib %t.qux.obj -out:%t.dll -dll
-#
-#--- foo.s
-.text
-.globl foo
-foo:
- call bar
-#--- bar.s
-.text
-.globl bar
-bar:
- ret
-#--- qux.s
-.text
-.global _DllMainCRTStartup
-_DllMainCRTStartup:
- call *__imp_foo(%rip)
- ret
|
@llvm/pr-subscribers-lld-coff Author: Alexandre Ganea (aganea) ChangesThis reverts commit 6a1bdd9 and re-instate behavior that matches what MSVC link.exe does, that is, error out when trying to dllimport a symbol from a static library. We also now hint in stdout that we should rather dllimport the symbol from a import library. Fixes #131807 Full diff: https://github.com/llvm/llvm-project/pull/134443.diff 5 Files Affected:
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 9bbab524b1f9a..7aa13bdce488e 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -2663,10 +2663,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
createECExportThunks();
// Resolve remaining undefined symbols and warn about imported locals.
- ctx.forEachSymtab([&](SymbolTable &symtab) {
- while (symtab.resolveRemainingUndefines())
- run();
- });
+ ctx.forEachSymtab(
+ [&](SymbolTable &symtab) { symtab.resolveRemainingUndefines(); });
if (errorCount())
return;
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 16afcc64316e3..d1fb6e05a0320 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -231,6 +231,17 @@ void SymbolTable::reportUndefinedSymbol(const UndefinedDiag &undefDiag) {
}
if (numDisplayedRefs < numRefs)
diag << "\n>>> referenced " << numRefs - numDisplayedRefs << " more times";
+
+ // Hints
+ StringRef name = undefDiag.sym->getName();
+ if (name.starts_with("__imp_")) {
+ Symbol *imp = find(name.substr(strlen("__imp_")));
+ if (imp && imp->isLazy()) {
+ diag << "\nNOTE: a relevant symbol '" << imp->getName()
+ << "' is available in " << toString(imp->getFile())
+ << " but cannot be used because it is not a import library.";
+ }
+ }
}
void SymbolTable::loadMinGWSymbols() {
@@ -432,11 +443,10 @@ void SymbolTable::reportUnresolvable() {
reportProblemSymbols(undefs, /*localImports=*/nullptr, true);
}
-bool SymbolTable::resolveRemainingUndefines() {
+void SymbolTable::resolveRemainingUndefines() {
llvm::TimeTraceScope timeScope("Resolve remaining undefined symbols");
SmallPtrSet<Symbol *, 8> undefs;
DenseMap<Symbol *, Symbol *> localImports;
- bool foundLazy = false;
for (auto &i : symMap) {
Symbol *sym = i.second;
@@ -481,11 +491,6 @@ bool SymbolTable::resolveRemainingUndefines() {
imp = findLocalSym(*mangledName);
}
}
- if (imp && imp->isLazy()) {
- forceLazy(imp);
- foundLazy = true;
- continue;
- }
if (imp && isa<Defined>(imp)) {
auto *d = cast<Defined>(imp);
replaceSymbol<DefinedLocalImport>(sym, ctx, name, d);
@@ -513,7 +518,6 @@ bool SymbolTable::resolveRemainingUndefines() {
reportProblemSymbols(
undefs, ctx.config.warnLocallyDefinedImported ? &localImports : nullptr,
false);
- return foundLazy;
}
std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index da19bf2800ecf..15e2644a6f519 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -67,10 +67,7 @@ class SymbolTable {
// Try to resolve any undefined symbols and update the symbol table
// accordingly, then print an error message for any remaining undefined
// symbols and warn about imported local symbols.
- // Returns whether more files might need to be linked in to resolve lazy
- // symbols, in which case the caller is expected to call the function again
- // after linking those files.
- bool resolveRemainingUndefines();
+ void resolveRemainingUndefines();
// Load lazy objects that are needed for MinGW automatic import and for
// doing stdcall fixups.
diff --git a/lld/test/COFF/imports-static-lib.test b/lld/test/COFF/imports-static-lib.test
new file mode 100644
index 0000000000000..63539cab9d860
--- /dev/null
+++ b/lld/test/COFF/imports-static-lib.test
@@ -0,0 +1,32 @@
+# REQUIRES: x86
+
+# Ensure that we don't import dllimport symbols from static (non-import) libraries
+# RUN: split-file %s %t.dir
+# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/foo.s -o %t.foo.obj
+# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/main.s -o %t.main.obj
+# RUN: llvm-lib %t.foo.obj -out:%t.foo.lib
+# RUN: not lld-link %t.foo.lib %t.main.obj -out:%t.dll -dll 2>&1 | FileCheck %s
+
+CHECK: error: undefined symbol: __declspec(dllimport) foo
+
+# Now do the same thing, but import the symbol from a import library.
+# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/foo_dll_main.s -o %t.foo_dll_main.obj
+# RUN: lld-link /out:%t.dll /dll %t.foo.obj %t.foo_dll_main.obj /export:foo /implib:%t.foo.imp.lib
+# RUN: lld-link %t.main.obj %t.foo.imp.lib -out:%t.exe -dll
+
+#--- foo.s
+.text
+.globl foo
+foo:
+ ret
+#--- foo_dll_main.s
+.text
+.global _DllMainCRTStartup
+_DllMainCRTStartup:
+ ret
+#--- main.s
+.text
+.global _DllMainCRTStartup
+_DllMainCRTStartup:
+ call *__imp_foo(%rip)
+ ret
diff --git a/lld/test/COFF/undefined_lazy.test b/lld/test/COFF/undefined_lazy.test
deleted file mode 100644
index ed5cd358b5cd9..0000000000000
--- a/lld/test/COFF/undefined_lazy.test
+++ /dev/null
@@ -1,26 +0,0 @@
-# REQUIRES: x86
-
-# RUN: split-file %s %t.dir
-# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/foo.s -o %t.foo.obj
-# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/bar.s -o %t.bar.obj
-# RUN: llvm-mc --filetype=obj -triple=x86_64-windows-msvc %t.dir/qux.s -o %t.qux.obj
-# RUN: llvm-lib %t.foo.obj -out:%t.foo.lib
-# RUN: llvm-lib %t.bar.obj -out:%t.bar.lib
-# RUN: lld-link %t.foo.lib %t.bar.lib %t.qux.obj -out:%t.dll -dll
-#
-#--- foo.s
-.text
-.globl foo
-foo:
- call bar
-#--- bar.s
-.text
-.globl bar
-bar:
- ret
-#--- qux.s
-.text
-.global _DllMainCRTStartup
-_DllMainCRTStartup:
- call *__imp_foo(%rip)
- ret
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lld/COFF/SymbolTable.cpp
Outdated
if (imp && imp->isLazy()) { | ||
diag << "\nNOTE: a relevant symbol '" << imp->getName() | ||
<< "' is available in " << toString(imp->getFile()) | ||
<< " but cannot be used because it is not a import library."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: an import library
Initially I felt confused by this message, but I think it may be the best way to phrase it. The other angle at this is, that if this wasn't a static library but object files, or members in a static library that we've already pulled in, then we'd actually link it successfully. But it's hard to suggest that in the diagnostic, and we probably don't want to push users that way anyway. So I guess this wording is fine.
I don't see this diagnostic tested though - I think it would be good to check that we do hit it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see updated version. I've added another testcase to also validate your suggestion in #131807 (comment) -- which happens to only generate a warning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good from my point of view now, thanks! But please wait for @glandium to reconfirm before landing. (I'm holding off of the approve-button until then.)
I /think/ this is fine. I'll look out for the fallout after it lands. |
As this is a crash fix for a regression in 20.x, we should probably consider backporting this to 20.x (especially as this doesn't seem to be the only case that hits this bug, see #134843), but we probably also first should give it a couple of days so that @glandium may pick it up and do some building with it. |
/cherry-pick c75eac7 |
Error: Command failed due to missing milestone. |
/cherry-pick c75eac7 |
Failed to cherry-pick: c75eac7 https://github.com/llvm/llvm-project/actions/runs/14480041593 Please manually backport the fix and push it to your github fork. Once this is done, please create a pull request |
@aganea Were you able to manually backport this? |
Ah I see, there's an ambiguity:
I'll try again with the longer ID. |
/cherry-pick c75eac7 |
Don't seem to be working. I'll send a PR. |
This reverts commit 6a1bdd9 and re-instate behavior that matches what MSVC link.exe does, that is, error out when trying to dllimport a symbol from a static library. A hint is now displayed in stdout, mentioning that we should rather dllimport the symbol from a import library. Fixes llvm#131807
This reverts commit 6a1bdd9 and re-instate behavior that matches what MSVC link.exe does, that is, error out when trying to dllimport a symbol from a static library.
We also now hint in stdout that we should rather dllimport the symbol from a import library.
Fixes #131807