Skip to content
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

LTO builds fail when dllimport functions are called locally #37453

Closed
llvmbot opened this issue Jul 9, 2018 · 7 comments
Closed

LTO builds fail when dllimport functions are called locally #37453

llvmbot opened this issue Jul 9, 2018 · 7 comments
Labels
bugzilla Issues migrated from bugzilla

Comments

@llvmbot
Copy link
Collaborator

llvmbot commented Jul 9, 2018

Bugzilla Link 38105
Resolution FIXED
Resolved on Jul 23, 2018 15:35
Version unspecified
OS Windows NT
Reporter LLVM Bugzilla Contributor
CC @pcc,@rnk

Extended Description

When a function is declared dllimport but called directly (i.e. in the same binary), a non-LTO build will issue a warning but still succeed (same as MSVC). LTO and ThinLTO builds fail with linkage complaints.

This is preventing Firefox's use of LTO on Windows: https://bugzilla.mozilla.org/show_bug.cgi?id=1448976

clang version 7.0.0 (trunk 336407)

$ cat a.cpp
__declspec(dllimport) int foo();
int main() { return foo(); }

$ cat b.cpp
int foo() { return 42; }

$ cat test.sh
#!/bin/bash
rm -rf *.obj *.exe
clang-cl $FLAG -O2 -c a.cpp b.cpp
lld-link -nodefaultlib -entry:main a.obj b.obj

$ ./test.sh
lld-link.exe: warning: a.obj: locally defined symbol imported: ?foo@@yahxz (defined in b.obj) [LNK4217]

$ FLAG=-flto ./test.sh
Global is external, but doesn't have external or weak linkage!
i32 ()* @"?foo@@yahxz"
LLVM ERROR: Broken module found, compilation aborted!

$ FLAG=-flto=thin ./test.sh
lld-link.exe: error: undefined symbol: ?foo@@yahxz

referenced by lto.tmp:(main)

@llvmbot
Copy link
Collaborator Author

llvmbot commented Jul 9, 2018

Hopefully Peter who I cc'ed on this can take a look. It is the index-based computeDeadSymbols that is causing the issue for both regular and thin LTO. Disabling it via /mllvm:-compute-dead=false makes the issue go away.

The problem is that LTO::run is getting 2 GlobalResolutions for foo, one prevailing from b.obj and one non-prevailing from a.obj. It processes the prevailing first and this line sets the GUID to prevailing:

GUIDPrevailingResolutions[GUID] =
    Res.second.Prevailing ? PrevailingType::Yes : PrevailingType::No;

When the non-prevailing a.obj copy is processed, it is overwritten with No.

Peter - what should be happening here? Should we never reset to No after setting to Yes?

@pcc
Copy link
Contributor

pcc commented Jul 9, 2018

The issue is that there are two separate symbols involved, which leads to there being two entries in GlobalResolutions for the same IR symbol:

$ ~/src2/llvm-project/ra/bin/llvm-nm a.obj
U _imp?foo@@yahxz
---------------- T main
$ ~/src2/llvm-project/ra/bin/llvm-nm b.obj
---------------- T ?foo@@yahxz

The same root cause was probably also causing this code: http://llvm-cs.pcc.me.uk/lib/LTO/LTO.cpp#858 to be run twice for the same symbol in the -flto case, causing an incorrect linkage to be set on the dllimport symbol (although I couldn't reproduce this myself).

I'm not sure what the right fix is yet.

@llvmbot
Copy link
Collaborator Author

llvmbot commented Jul 9, 2018

lld has special handling to disregard the _imp prefix if necessary: llvm-mirror/lld@add5dd0#diff-e248c0b838e4ca91656fc436f8f5e9ac

That even allows for a case of calling an _imp function directly without dllimport keywords. (yuck! but compat, I guess?)

Should the LTO code do something similar?

@llvmbot
Copy link
Collaborator Author

llvmbot commented Jul 9, 2018

Indeed, the following patch fixes the issue:

diff --git a/lib/LTO/LTO.cpp b/lib/LTO/LTO.cpp
index 6077b3b1ba4..05c75b77b31 100644
--- a/lib/LTO/LTO.cpp
+++ b/lib/LTO/LTO.cpp
@@ -428,7 +428,10 @@ void LTO::addModuleToGlobalRes(ArrayRefInputFile::Symbol Syms,
assert(ResI != ResE);
SymbolResolution Res = *ResI++;

  • auto &GlobalRes = GlobalResolutions[Sym.getName()];
  • StringRef Name = Sym.getName();
  • if (Name.startswith("_imp"))
  •  Name = Name.substr(strlen("__imp_"));
    
  • auto &GlobalRes = GlobalResolutions[Name];
    GlobalRes.UnnamedAddr &= Sym.isUnnamedAddr();
    if (Res.Prevailing) {
    assert(!GlobalRes.Prevailing &&

I can send a patch with test later today or tomorrow.

@llvmbot
Copy link
Collaborator Author

llvmbot commented Jul 10, 2018

Fix mailed in D49138, plus lld test in D49140.

@llvmbot
Copy link
Collaborator Author

llvmbot commented Jul 10, 2018

Thank you Teresa! This patch fixes all but one of the undefined symbols in the Firefox LTO build.

I'll try to reduce the one remaining failure (may take some time) and file a new bug if it turns out to be unrelated.

@llvmbot
Copy link
Collaborator Author

llvmbot commented Jul 23, 2018

Fixed in r337762.

@llvmbot llvmbot transferred this issue from llvm/llvm-bugzilla-archive Dec 10, 2021
mstorsjo added a commit to mstorsjo/llvm-project that referenced this issue Nov 21, 2023
Commit b963c0b fixed LTO
compilation of cases where one translation unit is calling a
function with the dllimport attribute, and another translation
unit provides this function locally within the same linked module
(i.e. not actually dllimported); see
llvm#37453 or
https://bugs.llvm.org/show_bug.cgi?id=38105 for full context.

This was fixed by aliasing their GlobalResolution structs, for
the __imp_ prefixed and non prefixed symbols.

I believe this fix to be wrong.

This patch reverts that fix, and fixes the same issue differently,
within LLD instead.

The fix assumed that one can treat the __imp_ prefixed and
unprefixed symbols as equal, referencing SVN r240620
(d766653). However that referenced
commit had mistaken how this logic works, which was corrected
later in SVN r240622 (88e0f92);
those symbols aren't direct aliases for each other - but if
there's a need for the __imp_ prefixed one and the other one
exists, the __imp_ prefixed one is created, as a pointer to
the other one.

However this fix only works if both translation units are compiled
as LTO; if the caller is compiled as a regular object file and
the callee is compiled as LTO, the fix fails, as the LTO compilation
doesn't know that the unprefixed symbol is needed.

The only level that knows of the potential relationship between
the __imp_ prefixed and unprefixed symbol, across regular and
bitcode object files, is LLD itself.

Therefore, revert the original fix from
b963c0b, and
fix the issue differently - when concluding that we can
fulfill an undefined symbol starting with __imp_, mark the
corresponding non prefixed symbol as used in a regular object
for the LTO compilation, to make sure that this non prefixed
symbol exists after the LTO compilation, to let LLD do the
fixup of the local import.

Extend the testcase to test a regular object file calling an
LTO object file, which previously failed.

This change also fixes another issue; an object file can provide
both unprefixed and prefixed versions of the same symbol, like
this:

    void importedFunc(void) {
    }
    void (*__imp_importedFunc)(void) = importedFunc;

That allows the function to be called both with and without
dllimport markings. (The concept of automatically resolving
a reference to __imp_func to a locally defined func only is
done in MSVC style linkers, but not in GNU ld, therefore MinGW
mode code often uses this construct.)

Previously, the aliasing of global resolutions at the LTO level
would trigger a failed assert with "Multiple prevailing defs are
not allowed" for this case, as both importedFunc and
__imp_importedFunc could be prevailing. Add a case to the existing
LLD test case lto-imp-prefix.ll to test this as well.

This change (together with previous change in
3ab6209) completes LLD to work
with mingw-w64-crt files (the base glue code for a mingw-w64
toolchain) built with LTO.
mstorsjo added a commit that referenced this issue Nov 21, 2023
…71376)

Commit b963c0b fixed LTO compilation of
cases where one translation unit is calling a function with the
dllimport attribute, and another translation unit provides this function
locally within the same linked module (i.e. not actually dllimported);
see #37453 or
https://bugs.llvm.org/show_bug.cgi?id=38105 for full context.

This was fixed by aliasing their GlobalResolution structs, for the
`__imp_` prefixed and non prefixed symbols.

I believe this fix to be wrong.

This patch reverts that fix, and fixes the same issue differently,
within LLD instead.

The fix assumed that one can treat the `__imp_` prefixed and unprefixed
symbols as equal, referencing SVN r240620
(d766653). However that referenced
commit had mistaken how this logic works, which was corrected later in
SVN r240622 (88e0f92); those symbols
aren't direct aliases for each other - but if there's a need for the
`__imp_` prefixed one and the other one exists, the `__imp_` prefixed
one is created, as a pointer to the other one.

However this fix only works if both translation units are compiled as
LTO; if the caller is compiled as a regular object file and the callee
is compiled as LTO, the fix fails, as the LTO compilation doesn't know
that the unprefixed symbol is needed.

The only level that knows of the potential relationship between the
`__imp_` prefixed and unprefixed symbol, across regular and bitcode
object files, is LLD itself.

Therefore, revert the original fix from
b963c0b, and fix the issue differently
- when concluding that we can fulfill an undefined symbol starting with
`__imp_`, mark the corresponding non prefixed symbol as used in a
regular object for the LTO compilation, to make sure that this non
prefixed symbol exists after the LTO compilation, to let LLD do the
fixup of the local import.

Extend the testcase to test a regular object file calling an LTO object
file, which previously failed.

This change also fixes another issue; an object file can provide both
unprefixed and prefixed versions of the same symbol, like this:

    void importedFunc(void) { 
    }
    void (*__imp_importedFunc)(void) = importedFunc;

That allows the function to be called both with and without dllimport
markings. (The concept of automatically resolving a reference to
`__imp_func` to a locally defined `func` only is done in MSVC style
linkers, but not in GNU ld, therefore MinGW mode code often uses this
construct.)

Previously, the aliasing of global resolutions at the LTO level would
trigger a failed assert with "Multiple prevailing defs are not allowed"
for this case, as both `importedFunc` and `__imp_importedFunc` could be
prevailing. Add a case to the existing LLD test case lto-imp-prefix.ll
to test this as well.

This change (together with previous change in
3ab6209) completes LLD to work with
mingw-w64-crt files (the base glue code for a mingw-w64 toolchain) built
with LTO.
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugzilla Issues migrated from bugzilla
Projects
None yet
Development

No branches or pull requests

2 participants