Skip to content

Commit c5b3de6

Browse files
committed
[COFF] Emit embedded -exclude-symbols: directives for hidden visibility for MinGW
This works with the automatic export of all symbols; in MinGW mode, when a DLL has no explicit dllexports, it exports all symbols (except for some that are hardcoded to be excluded, including some toolchain libraries). By hooking up the hidden visibility to the -exclude-symbols: directive, the automatic export of all symbols can be controlled in an easier way (with a mechanism that doesn't require strict annotation of every single symbol, but which allows gradually marking more unnecessary symbols as hidden). The primary use case is dylib builds of LLVM/Clang. These can be done in MinGW mode but not in MSVC mode, as MinGW builds can export all symbols (and the calling code can use APIs without corresponding dllimport directives). However, as all symbols are exported, it can easily overflow the max number of exported symbols in a DLL (65536). In the llvm-mingw distribution, only the X86, ARM and AArch64 backends are enabled; for the LLVM 13.0.0 release, libLLVM-13.dll ended up with 58112 exported symbols. For LLVM 14.0.0, it was 62015 symbols. Current builds of the 15.x branch end up at around 64650 symbols - i.e. extremely close to the limit. The msys2 packages of LLVM have had to progressively disable more of their backends in their builds, to be able to keep building with a dylib. This allows improving the current mingw dylib situation significantly, by using the same hidden visibility options and attributes as on Unix. With those in place, a current build of LLVM git main ends up at 35142 symbols instead of 64650. For code using hidden visibility, this now requires linking with either a current git lld or ld.bfd. (Older lld error out on the unknown directives, older ld.bfd will successfully link, but will print huge amounts of warnings.) Differential Revision: https://reviews.llvm.org/D130121
1 parent 5d513ef commit c5b3de6

File tree

3 files changed

+129
-20
lines changed

3 files changed

+129
-20
lines changed

llvm/docs/ReleaseNotes.rst

+8
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,14 @@ Changes to the WebAssembly Backend
108108

109109
* ...
110110

111+
Changes to the Windows Target
112+
-----------------------------
113+
114+
* For MinGW, generate embedded ``-exclude-symbols:`` directives for symbols
115+
with hidden visibility, omitting them from automatic export of all symbols.
116+
This roughly makes hidden visibility work like it does for other object
117+
file formats.
118+
111119
Changes to the X86 Backend
112120
--------------------------
113121

llvm/lib/IR/Mangler.cpp

+40-20
Original file line numberDiff line numberDiff line change
@@ -210,18 +210,46 @@ static bool canBeUnquotedInDirective(StringRef Name) {
210210

211211
void llvm::emitLinkerFlagsForGlobalCOFF(raw_ostream &OS, const GlobalValue *GV,
212212
const Triple &TT, Mangler &Mangler) {
213-
if (!GV->hasDLLExportStorageClass() || GV->isDeclaration())
214-
return;
213+
if (GV->hasDLLExportStorageClass() && !GV->isDeclaration()) {
215214

216-
if (TT.isWindowsMSVCEnvironment())
217-
OS << " /EXPORT:";
218-
else
219-
OS << " -export:";
215+
if (TT.isWindowsMSVCEnvironment())
216+
OS << " /EXPORT:";
217+
else
218+
OS << " -export:";
219+
220+
bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName());
221+
if (NeedQuotes)
222+
OS << "\"";
223+
if (TT.isWindowsGNUEnvironment() || TT.isWindowsCygwinEnvironment()) {
224+
std::string Flag;
225+
raw_string_ostream FlagOS(Flag);
226+
Mangler.getNameWithPrefix(FlagOS, GV, false);
227+
FlagOS.flush();
228+
if (Flag[0] == GV->getParent()->getDataLayout().getGlobalPrefix())
229+
OS << Flag.substr(1);
230+
else
231+
OS << Flag;
232+
} else {
233+
Mangler.getNameWithPrefix(OS, GV, false);
234+
}
235+
if (NeedQuotes)
236+
OS << "\"";
237+
238+
if (!GV->getValueType()->isFunctionTy()) {
239+
if (TT.isWindowsMSVCEnvironment())
240+
OS << ",DATA";
241+
else
242+
OS << ",data";
243+
}
244+
}
245+
if (GV->hasHiddenVisibility() && !GV->isDeclaration() && TT.isOSCygMing()) {
246+
247+
OS << " -exclude-symbols:";
248+
249+
bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName());
250+
if (NeedQuotes)
251+
OS << "\"";
220252

221-
bool NeedQuotes = GV->hasName() && !canBeUnquotedInDirective(GV->getName());
222-
if (NeedQuotes)
223-
OS << "\"";
224-
if (TT.isWindowsGNUEnvironment() || TT.isWindowsCygwinEnvironment()) {
225253
std::string Flag;
226254
raw_string_ostream FlagOS(Flag);
227255
Mangler.getNameWithPrefix(FlagOS, GV, false);
@@ -230,17 +258,9 @@ void llvm::emitLinkerFlagsForGlobalCOFF(raw_ostream &OS, const GlobalValue *GV,
230258
OS << Flag.substr(1);
231259
else
232260
OS << Flag;
233-
} else {
234-
Mangler.getNameWithPrefix(OS, GV, false);
235-
}
236-
if (NeedQuotes)
237-
OS << "\"";
238261

239-
if (!GV->getValueType()->isFunctionTy()) {
240-
if (TT.isWindowsMSVCEnvironment())
241-
OS << ",DATA";
242-
else
243-
OS << ",data";
262+
if (NeedQuotes)
263+
OS << "\"";
244264
}
245265
}
246266

llvm/test/CodeGen/X86/mingw-hidden.ll

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
; RUN: llc -mtriple i386-pc-win32 < %s \
2+
; RUN: | FileCheck --check-prefixes=CHECK,CHECK-MSVC %s
3+
; RUN: llc -mtriple i386-pc-mingw32 < %s \
4+
; RUN: | FileCheck --check-prefixes=CHECK,CHECK-MINGW %s
5+
; RUN: llc -mtriple i386-pc-mingw32 < %s \
6+
; RUN: | FileCheck --check-prefix=NOTEXPORTED %s
7+
8+
; CHECK: .text
9+
10+
; CHECK: .globl _notHidden
11+
define void @notHidden() {
12+
ret void
13+
}
14+
15+
; CHECK: .globl _f1
16+
define hidden void @f1() {
17+
ret void
18+
}
19+
20+
; CHECK: .globl _f2
21+
define hidden void @f2() unnamed_addr {
22+
ret void
23+
}
24+
25+
declare hidden void @notDefined()
26+
27+
; CHECK: .globl _stdfun@0
28+
define hidden x86_stdcallcc void @stdfun() nounwind {
29+
ret void
30+
}
31+
32+
; CHECK: .globl _lnk1
33+
$lnk1 = comdat any
34+
35+
define linkonce_odr hidden void @lnk1() comdat {
36+
ret void
37+
}
38+
39+
; CHECK: .globl _lnk2
40+
$lnk2 = comdat any
41+
42+
define linkonce_odr hidden void @lnk2() alwaysinline comdat {
43+
ret void
44+
}
45+
46+
; CHECK: .data
47+
; CHECK: .globl _Var1
48+
@Var1 = hidden global i32 1, align 4
49+
50+
; CHECK: .rdata,"dr"
51+
; CHECK: .globl _Var2
52+
@Var2 = hidden unnamed_addr constant i32 1
53+
54+
; CHECK: .comm _Var3
55+
@Var3 = common hidden global i32 0, align 4
56+
57+
; CHECK: .globl "_complex-name"
58+
@"complex-name" = hidden global i32 1, align 4
59+
60+
; CHECK: .globl _complex.name
61+
@"complex.name" = hidden global i32 1, align 4
62+
63+
64+
; Verify items that should not be marked hidden do not appear in the directives.
65+
; We use a separate check prefix to avoid confusion between -NOT and -SAME.
66+
; NOTEXPORTED: .section .drectve
67+
; NOTEXPORTED-NOT: :notHidden
68+
; NOTEXPORTED-NOT: :notDefined
69+
70+
; CHECK-MSVC-NOT: .section .drectve
71+
; CHECK-MINGW: .section .drectve
72+
; CHECK-MINGW: .ascii " -exclude-symbols:f1"
73+
; CHECK-MINGW: .ascii " -exclude-symbols:f2"
74+
; CHECK-MINGW: .ascii " -exclude-symbols:stdfun@0"
75+
; CHECK-MINGW: .ascii " -exclude-symbols:lnk1"
76+
; CHECK-MINGW: .ascii " -exclude-symbols:lnk2"
77+
; CHECK-MINGW: .ascii " -exclude-symbols:Var1"
78+
; CHECK-MINGW: .ascii " -exclude-symbols:Var2"
79+
; CHECK-MINGW: .ascii " -exclude-symbols:Var3"
80+
; CHECK-MINGW: .ascii " -exclude-symbols:\"complex-name\""
81+
; CHECK-MINGW: .ascii " -exclude-symbols:\"complex.name\""

0 commit comments

Comments
 (0)