Skip to content

Commit 4d4a43d

Browse files
authored
[LLD][COFF] Create EC alias symbols for entry points and exports (#114297)
On ARM64EC, a symbol may be defined in either its mangled or demangled form (or both). To ensure consistent linking for entry points and exports, define an anti-dependency symbol that binds both forms, similar to how compiler-generated code references external functions.
1 parent e7080fd commit 4d4a43d

File tree

4 files changed

+197
-21
lines changed

4 files changed

+197
-21
lines changed

lld/COFF/Driver.cpp

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
415415
case OPT_entry:
416416
if (!arg->getValue()[0])
417417
fatal("missing entry point symbol name");
418-
ctx.config.entry = addUndefined(mangle(arg->getValue()));
418+
ctx.config.entry = addUndefined(mangle(arg->getValue()), true);
419419
break;
420420
case OPT_failifmismatch:
421421
checkFailIfMismatch(arg->getValue(), file);
@@ -696,12 +696,33 @@ void LinkerDriver::addLibSearchPaths() {
696696
}
697697
}
698698

699-
Symbol *LinkerDriver::addUndefined(StringRef name) {
699+
Symbol *LinkerDriver::addUndefined(StringRef name, bool aliasEC) {
700700
Symbol *b = ctx.symtab.addUndefined(name);
701701
if (!b->isGCRoot) {
702702
b->isGCRoot = true;
703703
ctx.config.gcroot.push_back(b);
704704
}
705+
706+
// On ARM64EC, a symbol may be defined in either its mangled or demangled form
707+
// (or both). Define an anti-dependency symbol that binds both forms, similar
708+
// to how compiler-generated code references external functions.
709+
if (aliasEC && isArm64EC(ctx.config.machine)) {
710+
if (std::optional<std::string> mangledName =
711+
getArm64ECMangledFunctionName(name)) {
712+
auto u = dyn_cast<Undefined>(b);
713+
if (u && !u->weakAlias) {
714+
Symbol *t = ctx.symtab.addUndefined(saver().save(*mangledName));
715+
u->setWeakAlias(t, true);
716+
}
717+
} else {
718+
std::optional<std::string> demangledName =
719+
getArm64ECDemangledFunctionName(name);
720+
Symbol *us = ctx.symtab.addUndefined(saver().save(*demangledName));
721+
auto u = dyn_cast<Undefined>(us);
722+
if (u && !u->weakAlias)
723+
u->setWeakAlias(b, true);
724+
}
725+
}
705726
return b;
706727
}
707728

@@ -2342,22 +2363,22 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
23422363
if (auto *arg = args.getLastArg(OPT_entry)) {
23432364
if (!arg->getValue()[0])
23442365
fatal("missing entry point symbol name");
2345-
config->entry = addUndefined(mangle(arg->getValue()));
2366+
config->entry = addUndefined(mangle(arg->getValue()), true);
23462367
} else if (!config->entry && !config->noEntry) {
23472368
if (args.hasArg(OPT_dll)) {
23482369
StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12"
23492370
: "_DllMainCRTStartup";
2350-
config->entry = addUndefined(s);
2371+
config->entry = addUndefined(s, true);
23512372
} else if (config->driverWdm) {
23522373
// /driver:wdm implies /entry:_NtProcessStartup
2353-
config->entry = addUndefined(mangle("_NtProcessStartup"));
2374+
config->entry = addUndefined(mangle("_NtProcessStartup"), true);
23542375
} else {
23552376
// Windows specific -- If entry point name is not given, we need to
23562377
// infer that from user-defined entry name.
23572378
StringRef s = findDefaultEntry();
23582379
if (s.empty())
23592380
fatal("entry point must be defined");
2360-
config->entry = addUndefined(s);
2381+
config->entry = addUndefined(s, true);
23612382
log("Entry name inferred: " + s);
23622383
}
23632384
}
@@ -2371,7 +2392,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
23712392
if (config->machine == I386) {
23722393
config->delayLoadHelper = addUndefined("___delayLoadHelper2@8");
23732394
} else {
2374-
config->delayLoadHelper = addUndefined("__delayLoadHelper2");
2395+
config->delayLoadHelper = addUndefined("__delayLoadHelper2", true);
23752396
}
23762397
}
23772398
}
@@ -2505,7 +2526,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
25052526
for (Export &e : config->exports) {
25062527
if (!e.forwardTo.empty())
25072528
continue;
2508-
e.sym = addUndefined(e.name);
2529+
e.sym = addUndefined(e.name, !e.data);
25092530
if (e.source != ExportSource::Directives)
25102531
e.symbolName = mangleMaybe(e.sym);
25112532
}

lld/COFF/Driver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ class LinkerDriver {
170170

171171
std::set<std::string> visitedLibs;
172172

173-
Symbol *addUndefined(StringRef sym);
173+
Symbol *addUndefined(StringRef sym, bool aliasEC = false);
174174

175175
void addUndefinedGlob(StringRef arg);
176176

lld/test/COFF/arm64ec-delayimport.test

Lines changed: 38 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ REQUIRES: aarch64, x86
22
RUN: split-file %s %t.dir && cd %t.dir
33

44
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test.s -o test.obj
5+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows helper-mangled.s -o helper-mangled.obj
6+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows helper-demangled.s -o helper-demangled.obj
57
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
68
RUN: llvm-lib -machine:arm64ec -def:test.def -out:test-arm64ec.lib
79
RUN: llvm-lib -machine:arm64ec -def:test2.def -out:test2-arm64ec.lib
810

911
RUN: lld-link -machine:arm64ec -dll -noentry -out:out.dll loadconfig-arm64ec.obj test.obj \
10-
RUN: test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll -map
12+
RUN: helper-mangled.obj test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll -map
1113

1214
RUN: llvm-readobj --hex-dump=.test out.dll | FileCheck --check-prefix=TESTSEC %s
1315
TESTSEC: 0x180008000 00600000 88700000 00200000 10100000
@@ -97,7 +99,7 @@ IMPORTS-NEXT: }
9799
IMPORTS-NEXT: }
98100

99101
RUN: FileCheck --check-prefix=MAP %s < out.map
100-
MAP: 0001:00000008 #__delayLoadHelper2 0000000180001008 test.obj
102+
MAP: 0001:00000008 #__delayLoadHelper2 0000000180001008 helper-mangled.obj
101103
MAP: 0001:00000010 #func 0000000180001010 test-arm64ec:test.dll
102104
MAP-NEXT: 0001:0000001c __impchk_func 000000018000101c test-arm64ec:test.dll
103105
MAP-NEXT: 0001:00000030 #func2 0000000180001030 test-arm64ec:test.dll
@@ -138,6 +140,21 @@ RELOC-NEXT: Type: DIR64
138140
RELOC-NEXT: Address: 0x6008
139141
RELOC-NEXT: }
140142

143+
Verify that a demangled version of __delayLoadHelper2 can be used.
144+
145+
RUN: lld-link -machine:arm64ec -dll -noentry -out:out2.dll loadconfig-arm64ec.obj test.obj \
146+
RUN: helper-demangled.obj test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll
147+
RUN: llvm-objdump -d out2.dll | FileCheck --check-prefix=DISASM %s
148+
149+
Verify that the mangled version of __delayLoadHelper2 can be used from a library.
150+
Even if an anti-dependency alias is defined by the helper, it won't appear in
151+
the archive index, so we need to locate it by its mangled name.
152+
153+
RUN: llvm-lib -machine:arm64ec -out:helper.lib helper-mangled.obj
154+
RUN: lld-link -machine:arm64ec -dll -noentry -out:out3.dll loadconfig-arm64ec.obj test.obj \
155+
RUN: helper.lib test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll
156+
RUN: llvm-objdump -d out3.dll | FileCheck --check-prefix=DISASM %s
157+
141158
#--- test.s
142159
.section .test,"r"
143160
.rva __imp_func
@@ -159,16 +176,6 @@ __icall_helper_arm64ec:
159176
mov w0, #0
160177
ret
161178

162-
.section .text,"xr",discard,"#__delayLoadHelper2"
163-
.globl "#__delayLoadHelper2"
164-
.p2align 2, 0x0
165-
"#__delayLoadHelper2":
166-
mov w0, #1
167-
ret
168-
169-
.weak_anti_dep __delayLoadHelper2
170-
.set __delayLoadHelper2,"#__delayLoadHelper2"
171-
172179
.section .hybmp$x, "yi"
173180
.symidx __imp_func
174181
.symidx func_exit_thunk
@@ -189,6 +196,25 @@ func2_exit_thunk:
189196
mov w0, #3
190197
ret
191198

199+
#--- helper-mangled.s
200+
.section .text,"xr",discard,"#__delayLoadHelper2"
201+
.globl "#__delayLoadHelper2"
202+
.p2align 2, 0x0
203+
"#__delayLoadHelper2":
204+
mov w0, #1
205+
ret
206+
207+
.weak_anti_dep __delayLoadHelper2
208+
.set __delayLoadHelper2,"#__delayLoadHelper2"
209+
210+
#--- helper-demangled.s
211+
.section .text,"xr",discard,__delayLoadHelper2
212+
.globl __delayLoadHelper2
213+
.p2align 2, 0x0
214+
__delayLoadHelper2:
215+
mov w0, #1
216+
ret
217+
192218
#--- test.def
193219
NAME test.dll
194220
EXPORTS
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
REQUIRES: aarch64, x86
2+
RUN: split-file %s %t.dir && cd %t.dir
3+
4+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows demangled-dll-main.s -o demangled-dll-main.obj
5+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows mangled-dll-main.s -o mangled-dll-main.obj
6+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows demangled-func.s -o demangled-func.obj
7+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows mangled-func.s -o mangled-func.obj
8+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-demangled.s -o ref-demangled.obj
9+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows demangled-entry-drectve.s -o demangled-entry-drectve.obj
10+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows demangled-dll-main.s -o x64-dll-main.obj
11+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
12+
13+
RUN: llvm-lib -machine:arm64ec -out:func.lib mangled-func.obj
14+
RUN: llvm-lib -machine:arm64ec -out:dllmain.lib mangled-dll-main.obj
15+
16+
Ensure that the linker recognizes the demangled version of _DllMainCRTStartup.
17+
RUN: lld-link -machine:arm64ec -dll -out:demangled-main.dll demangled-dll-main.obj loadconfig-arm64ec.obj
18+
RUN: llvm-objdump -d demangled-main.dll | FileCheck -check-prefix=DISASM %s
19+
20+
DISASM: 0000000180001000 <.text>:
21+
DISASM-NEXT: 180001000: d65f03c0 ret
22+
DISASM-EMPTY:
23+
DISASM-NEXT: Disassembly of section .hexpthk:
24+
DISASM-EMPTY:
25+
DISASM: 180002000: 48 8b c4 movq %rsp, %rax
26+
DISASM-NEXT: 180002003: 48 89 58 20 movq %rbx, 0x20(%rax)
27+
DISASM-NEXT: 180002007: 55 pushq %rbp
28+
DISASM-NEXT: 180002008: 5d popq %rbp
29+
DISASM-NEXT: 180002009: e9 f2 ef ff ff jmp 0x180001000 <.text>
30+
DISASM-NEXT: 18000200e: cc int3
31+
DISASM-NEXT: 18000200f: cc int3
32+
33+
Ensure that the linker recognizes the mangled version of #_DllMainCRTStartup.
34+
RUN: lld-link -machine:arm64ec -dll -out:mangled-dllmain.dll mangled-dll-main.obj loadconfig-arm64ec.obj
35+
RUN: llvm-objdump -d mangled-dllmain.dll | FileCheck -check-prefix=DISASM %s
36+
37+
Verify that the linker recognizes the mangled version of _DllMainCRTStartup from an archive.
38+
RUN: lld-link -machine:arm64ec -dll -out:mangled-lib-dllmain.dll dllmain.lib loadconfig-arm64ec.obj
39+
RUN: llvm-objdump -d mangled-lib-dllmain.dll | FileCheck -check-prefix=DISASM %s
40+
41+
Verify that the linker recognizes the demangled entry function.
42+
RUN: lld-link -machine:arm64ec -dll -out:demangled-entry.dll demangled-func.obj loadconfig-arm64ec.obj -entry:func
43+
RUN: llvm-objdump -d demangled-entry.dll | FileCheck -check-prefix=DISASM %s
44+
45+
Verify that the linker recognizes the mangled entry function when it is referenced by its demangled name.
46+
RUN: lld-link -machine:arm64ec -dll -out:mangled-entry.dll mangled-func.obj loadconfig-arm64ec.obj -entry:func
47+
RUN: llvm-objdump -d mangled-entry.dll | FileCheck -check-prefix=DISASM %s
48+
49+
Verify that the linker recognizes the mangled entry function when it is referenced by its demangled
50+
name in drectve section.
51+
RUN: lld-link -machine:arm64ec -dll -out:mangled-entry.dll mangled-func.obj loadconfig-arm64ec.obj demangled-entry-drectve.obj
52+
RUN: llvm-objdump -d mangled-entry.dll | FileCheck -check-prefix=DISASM %s
53+
54+
Verify that the linker recognizes the mangled entry function from an archive.
55+
RUN: lld-link -machine:arm64ec -dll -out:mangled-lib-entry.dll func.lib loadconfig-arm64ec.obj -entry:func
56+
RUN: llvm-objdump -d mangled-lib-entry.dll | FileCheck -check-prefix=DISASM %s
57+
58+
Verify that the linker recognizes the entry function when referenced by its mangled name.
59+
RUN: lld-link -machine:arm64ec -dll -out:mangled-entry2.dll mangled-func.obj loadconfig-arm64ec.obj "-entry:#func"
60+
RUN: llvm-objdump -d mangled-entry2.dll | FileCheck -check-prefix=DISASM %s
61+
62+
Verify that the linker recognizes the demangled exported function.
63+
RUN: lld-link -machine:arm64ec -dll -out:demangled-export.dll demangled-func.obj \
64+
RUN: loadconfig-arm64ec.obj -noentry -export:func
65+
RUN: llvm-objdump -d demangled-export.dll | FileCheck -check-prefix=DISASM %s
66+
67+
Verify that the linker recognizes the mangled exported function when referenced by its demangled name.
68+
RUN: lld-link -machine:arm64ec -dll -out:mangled-export.dll mangled-func.obj \
69+
RUN: loadconfig-arm64ec.obj -noentry -export:func
70+
RUN: llvm-objdump -d mangled-export.dll | FileCheck -check-prefix=DISASM %s
71+
72+
Verify that the linker recognizes the mangled exported function when referenced by its mangled name.
73+
RUN: lld-link -machine:arm64ec -dll -out:mangled-export2.dll mangled-func.obj \
74+
RUN: loadconfig-arm64ec.obj -noentry "-export:#func"
75+
RUN: llvm-objdump -d mangled-export2.dll | FileCheck -check-prefix=DISASM %s
76+
77+
Verify that the linker recognizes the mangled exported function when referenced
78+
by its mangled name and creates a demangled alias for it.
79+
RUN: lld-link -machine:arm64ec -dll -noentry -out:demangled-export-ref.dll mangled-func.obj \
80+
RUN: ref-demangled.obj loadconfig-arm64ec.obj "-export:#func"
81+
RUN: llvm-objdump -d demangled-export-ref.dll | FileCheck -check-prefix=DISASM %s
82+
83+
DISASM2: 0000000180001000 <.text>:
84+
DISASM2-NEXT: 180001000: d65f03c0 ret
85+
86+
Verify that the linker emits appropriate errors for mismatched mangling.
87+
RUN: not lld-link -machine:arm64ec -dll -out:test.dll demangled-func.obj loadconfig-arm64ec.obj \
88+
RUN: "-entry:#func" 2>&1 | FileCheck -check-prefix=FUNC-NOT-FOUND %s
89+
RUN: not lld-link -machine:arm64ec -dll -out:test.dll demangled-func.obj loadconfig-arm64ec.obj \
90+
RUN: -noentry "-export:#func" 2>&1 | FileCheck -check-prefix=FUNC-NOT-FOUND %s
91+
FUNC-NOT-FOUND: undefined symbol: #func
92+
93+
Verify that the linker recognizes the demangled x86_64 _DllMainCRTStartup.
94+
RUN: lld-link -machine:arm64ec -dll -out:test.dll x64-dll-main.obj loadconfig-arm64ec.obj
95+
RUN: llvm-objdump -d test.dll | FileCheck -check-prefix=DISASM-X64 %s
96+
DISASM-X64: 0000000180001000 <.text>:
97+
DISASM-X64-NEXT: 180001000: c3 retq
98+
99+
#--- demangled-dll-main.s
100+
.text
101+
.globl _DllMainCRTStartup
102+
_DllMainCRTStartup:
103+
ret
104+
105+
#--- mangled-dll-main.s
106+
.text
107+
.globl "#_DllMainCRTStartup"
108+
"#_DllMainCRTStartup":
109+
ret
110+
111+
#--- demangled-func.s
112+
.text
113+
.globl func
114+
func:
115+
ret
116+
117+
#--- mangled-func.s
118+
.text
119+
.globl "#func"
120+
"#func":
121+
ret
122+
123+
#--- ref-demangled.s
124+
.data
125+
.rva func
126+
127+
#--- demangled-entry-drectve.s
128+
.section .drectve,"rd"
129+
.ascii " -entry:func"

0 commit comments

Comments
 (0)