Skip to content

Commit 89efffd

Browse files
authored
[LTO] [LLD] Don't alias the __imp_func and func symbol resolutions (#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.
1 parent 527fcb8 commit 89efffd

File tree

4 files changed

+30
-16
lines changed

4 files changed

+30
-16
lines changed

lld/COFF/SymbolTable.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,10 @@ void SymbolTable::reportUnresolvable() {
462462
StringRef name = undef->getName();
463463
if (name.starts_with("__imp_")) {
464464
Symbol *imp = find(name.substr(strlen("__imp_")));
465-
if (imp && isa<Defined>(imp))
465+
if (Defined *def = dyn_cast_or_null<Defined>(imp)) {
466+
def->isUsedInRegularObj = true;
466467
continue;
468+
}
467469
}
468470
if (name.contains("_PchSym_"))
469471
continue;

lld/test/COFF/lto-dllimport.ll

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,7 @@
99
; RUN: lld-link %t.main.obj %t.other.obj -entry:main -out:%t.exe
1010
; RUN: lld-link %t.main.bc %t.other.bc -entry:main -out:%t.exe
1111
; RUN: lld-link %t.main.bc %t.other.obj -entry:main -out:%t.exe
12-
13-
;; This test currently fails if combining the regular object file main.obj
14-
;; with the bitcode object file other.bc.
15-
; RUN-skipped: lld-link %t.main.obj %t.other.bc -entry:main -out:%t.exe
12+
; RUN: lld-link %t.main.obj %t.other.bc -entry:main -out:%t.exe
1613

1714
;--- main.ll
1815
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"

lld/test/COFF/lto-imp-prefix.ll

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@
33
; RUN: rm -rf %t.dir
44
; RUN: split-file %s %t.dir
55
; RUN: llvm-as %t.dir/main.ll -o %t.main.obj
6-
; RUN: llvm-as %t.dir/other.ll -o %t.other.obj
6+
; RUN: llvm-as %t.dir/other1.ll -o %t.other1.obj
7+
; RUN: llvm-as %t.dir/other2.ll -o %t.other2.obj
78

8-
; RUN: lld-link /entry:entry %t.main.obj %t.other.obj /out:%t.exe /subsystem:console /debug:symtab
9+
; RUN: lld-link /entry:entry %t.main.obj %t.other1.obj /out:%t1.exe /subsystem:console /debug:symtab
910

1011
;; The current implementation for handling __imp_ symbols retains all of them.
1112
;; Observe that this currently produces __imp_unusedFunc even if nothing
1213
;; references unusedFunc in any form.
1314

14-
; RUN: llvm-nm %t.exe | FileCheck %s
15+
; RUN: llvm-nm %t1.exe | FileCheck %s
1516

1617
; CHECK: __imp_unusedFunc
1718

19+
; RUN: lld-link /entry:entry %t.main.obj %t.other2.obj /out:%t2.exe /subsystem:console
20+
1821
;--- main.ll
1922
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
2023
target triple = "x86_64-w64-windows-gnu"
@@ -30,7 +33,7 @@ declare dllimport void @importedFunc()
3033

3134
declare void @other()
3235

33-
;--- other.ll
36+
;--- other1.ll
3437
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
3538
target triple = "x86_64-w64-windows-gnu"
3639

@@ -52,3 +55,21 @@ define void @other() {
5255
entry:
5356
ret void
5457
}
58+
59+
;--- other2.ll
60+
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
61+
target triple = "x86_64-w64-windows-gnu"
62+
63+
@__imp_importedFunc = global ptr @importedFunc
64+
65+
; Test with two external symbols with the same name, with/without the __imp_
66+
; prefix.
67+
define void @importedFunc() {
68+
entry:
69+
ret void
70+
}
71+
72+
define void @other() {
73+
entry:
74+
ret void
75+
}

llvm/lib/LTO/LTO.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -610,13 +610,7 @@ void LTO::addModuleToGlobalRes(ArrayRef<InputFile::Symbol> Syms,
610610
assert(ResI != ResE);
611611
SymbolResolution Res = *ResI++;
612612

613-
StringRef Name = Sym.getName();
614-
// Strip the __imp_ prefix from COFF dllimport symbols (similar to the
615-
// way they are handled by lld), otherwise we can end up with two
616-
// global resolutions (one with and one for a copy of the symbol without).
617-
if (TT.isOSBinFormatCOFF() && Name.startswith("__imp_"))
618-
Name = Name.substr(strlen("__imp_"));
619-
auto &GlobalRes = GlobalResolutions[Name];
613+
auto &GlobalRes = GlobalResolutions[Sym.getName()];
620614
GlobalRes.UnnamedAddr &= Sym.isUnnamedAddr();
621615
if (Res.Prevailing) {
622616
assert(!GlobalRes.Prevailing &&

0 commit comments

Comments
 (0)