Skip to content

Commit cb11f22

Browse files
committed
[LLD][COFF] Disallow importing DllMain from import libraries (llvm#146610)
This is a workaround for llvm#82050 by skipping the `DllMain` symbol if seen in aimport library. If this situation occurs, after this commit a warning will also be displayed. The warning can be silenced with `/ignore:exporteddllmain`
1 parent 6146a88 commit cb11f22

File tree

4 files changed

+99
-1
lines changed

4 files changed

+99
-1
lines changed

lld/COFF/Config.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ struct Configuration {
313313
bool warnDebugInfoUnusable = true;
314314
bool warnLongSectionNames = true;
315315
bool warnStdcallFixup = true;
316+
bool warnExportedDllMain = true;
316317
bool incremental = true;
317318
bool integrityCheck = false;
318319
bool killAt = false;

lld/COFF/Driver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1625,6 +1625,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
16251625
config->warnLocallyDefinedImported = false;
16261626
else if (s == "longsections")
16271627
config->warnLongSectionNames = false;
1628+
else if (s == "exporteddllmain")
1629+
config->warnExportedDllMain = false;
16281630
// Other warning numbers are ignored.
16291631
}
16301632
}

lld/COFF/InputFiles.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,35 @@ static coff_symbol_generic *cloneSymbol(COFFSymbolRef sym) {
117117
}
118118
}
119119

120+
// Skip importing DllMain thunks from import libraries.
121+
static bool fixupDllMain(COFFLinkerContext &ctx, llvm::object::Archive *file,
122+
const Archive::Symbol &sym, bool &skipDllMain) {
123+
if (skipDllMain)
124+
return true;
125+
const Archive::Child &c =
126+
CHECK(sym.getMember(), file->getFileName() +
127+
": could not get the member for symbol " +
128+
toCOFFString(ctx, sym));
129+
MemoryBufferRef mb =
130+
CHECK(c.getMemoryBufferRef(),
131+
file->getFileName() +
132+
": could not get the buffer for a child buffer of the archive");
133+
if (identify_magic(mb.getBuffer()) == file_magic::coff_import_library) {
134+
if (ctx.config.warnExportedDllMain) {
135+
// We won't place DllMain symbols in the symbol table if they are
136+
// coming from a import library. This message can be ignored with the flag
137+
// '/ignore:exporteddllmain'
138+
Warn(ctx)
139+
<< file->getFileName()
140+
<< ": skipping exported DllMain symbol [exporteddllmain]\nNOTE: this "
141+
"might be a mistake when the DLL/library was produced.";
142+
}
143+
skipDllMain = true;
144+
return true;
145+
}
146+
return false;
147+
}
148+
120149
ArchiveFile::ArchiveFile(COFFLinkerContext &ctx, MemoryBufferRef m)
121150
: InputFile(ctx.symtab, ArchiveKind, m) {}
122151

@@ -140,8 +169,17 @@ void ArchiveFile::parse() {
140169
}
141170

142171
// Read the symbol table to construct Lazy objects.
143-
for (const Archive::Symbol &sym : file->symbols())
172+
bool skipDllMain = false;
173+
for (const Archive::Symbol &sym : file->symbols()) {
174+
// If the DllMain symbol was exported by mistake, skip importing it
175+
// otherwise we might end up with a import thunk in the final binary which
176+
// is wrong.
177+
if (sym.getName() == "__imp_DllMain" || sym.getName() == "DllMain") {
178+
if (fixupDllMain(ctx, file.get(), sym, skipDllMain))
179+
continue;
180+
}
144181
ctx.symtab.addLazyArchive(this, sym);
182+
}
145183
}
146184

147185
// Returns a buffer pointing to a member file containing a given symbol.

lld/test/COFF/exported-dllmain.test

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
REQUIRES: x86
2+
RUN: split-file %s %t.dir && cd %t.dir
3+
4+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows a.s -o a.obj
5+
6+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows b1.s -o b1.obj
7+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows b2.s -o b2.obj
8+
9+
### This is the line where our problem occurs. Here, we export the DllMain symbol which shouldn't happen normally.
10+
RUN: lld-link b1.obj b2.obj -out:b.dll -dll -implib:b.lib -entry:DllMain -export:bar -export:DllMain
11+
12+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows c.s -o c.obj
13+
RUN: lld-link -lib c.obj -out:c.lib
14+
15+
### Later, if b.lib is provided before other libs/objs that export DllMain statically, we previously were using the dllimported DllMain from b.lib, which is wrong.
16+
RUN: lld-link a.obj b.lib c.lib -dll -out:out.dll -entry:DllMain 2>&1 | FileCheck -check-prefix=WARN %s
17+
RUN: lld-link a.obj b.lib c.lib -dll -out:out.dll -entry:DllMain -ignore:exporteddllmain 2>&1 | FileCheck -check-prefix=IGNORED --allow-empty %s
18+
RUN: llvm-objdump --private-headers -d out.dll | FileCheck -check-prefix=DISASM %s
19+
20+
WARN: lld-link: warning: b.lib: skipping exported DllMain symbol [exporteddllmain]
21+
IGNORED-NOT: lld-link: warning: b.lib: skipping exported DllMain symbol [exporteddllmain]
22+
23+
DISASM: The Import Tables:
24+
DISASM: DLL Name: b.dll
25+
DISASM-NOT: DllMain
26+
DISASM: bar
27+
DISASM: Disassembly of section .text:
28+
DISASM-EMPTY:
29+
DISASM: b8 01 00 00 00 movl $0x1, %eax
30+
DISASM-NEXT: c3 retq
31+
32+
#--- a.s
33+
.text
34+
.globl foo
35+
foo:
36+
call *__imp_bar(%rip)
37+
ret
38+
39+
#--- b1.s
40+
.text
41+
.globl bar
42+
bar:
43+
ret
44+
45+
#--- b2.s
46+
.text
47+
.globl DllMain
48+
DllMain:
49+
xor %eax, %eax
50+
ret
51+
52+
#--- c.s
53+
.text
54+
.globl DllMain
55+
DllMain:
56+
movl $1, %eax
57+
ret

0 commit comments

Comments
 (0)