Skip to content

Commit 18fa9fa

Browse files
authored
[LLD][COFF] Add support for ARM64EC delay-load imports (#110042)
Fill the regular delay-load IAT with x86_64 delay-load thunks. Similarly to regular imports, create an auxiliary IAT and its copy for ARM64EC calls. These are filled with the same `__impchk_` thunks used for regular imports, which perform an indirect call with `__icall_helper_arm64ec` on the regular delay-load IAT. These auxiliary IATs are exposed via CHPE metadata starting from version 2. The MSVC linker creates one more copy of the auxiliary IAT. `__imp_func` symbols refer to that hidden IAT, while the `#func` thunk performs a call with the public auxiliary IAT. If the public auxiliary IAT is fine for `#func`, it should be fine for calls using the `__imp_func` symbol as well. Therefore, I made `__imp_func` refer to that IAT too.
1 parent 9e85937 commit 18fa9fa

File tree

6 files changed

+248
-2
lines changed

6 files changed

+248
-2
lines changed

lld/COFF/DLL.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,16 @@ void DelayLoadContents::create(Defined *h) {
812812
s->loadThunkSym =
813813
cast<DefinedSynthetic>(ctx.symtab.addSynthetic(symName, t));
814814
}
815+
816+
if (s->file->impECSym) {
817+
auto chunk = make<AuxImportChunk>(s->file);
818+
auxIat.push_back(chunk);
819+
s->file->impECSym->setLocation(chunk);
820+
821+
chunk = make<AuxImportChunk>(s->file);
822+
auxIatCopy.push_back(chunk);
823+
s->file->auxImpCopySym->setLocation(chunk);
824+
}
815825
}
816826
thunks.push_back(tm);
817827
if (pdataChunk)
@@ -822,6 +832,10 @@ void DelayLoadContents::create(Defined *h) {
822832
// Terminate with null values.
823833
addresses.push_back(make<NullChunk>(8));
824834
names.push_back(make<NullChunk>(8));
835+
if (ctx.config.machine == ARM64EC) {
836+
auxIat.push_back(make<NullChunk>(8));
837+
auxIatCopy.push_back(make<NullChunk>(8));
838+
}
825839

826840
for (int i = 0, e = syms.size(); i < e; ++i)
827841
syms[i]->setLocation(addresses[base + i]);
@@ -845,6 +859,7 @@ void DelayLoadContents::create(Defined *h) {
845859
Chunk *DelayLoadContents::newTailMergeChunk(Chunk *dir) {
846860
switch (ctx.config.machine) {
847861
case AMD64:
862+
case ARM64EC:
848863
return make<TailMergeChunkX64>(dir, helper);
849864
case I386:
850865
return make<TailMergeChunkX86>(ctx, dir, helper);
@@ -880,6 +895,7 @@ Chunk *DelayLoadContents::newThunkChunk(DefinedImportData *s,
880895
Chunk *tailMerge) {
881896
switch (ctx.config.machine) {
882897
case AMD64:
898+
case ARM64EC:
883899
return make<ThunkChunkX64>(s, tailMerge);
884900
case I386:
885901
return make<ThunkChunkX86>(ctx, s, tailMerge);

lld/COFF/DLL.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class DelayLoadContents {
4848
ArrayRef<Chunk *> getCodeChunks() { return thunks; }
4949
ArrayRef<Chunk *> getCodePData() { return pdata; }
5050
ArrayRef<Chunk *> getCodeUnwindInfo() { return unwindinfo; }
51+
ArrayRef<Chunk *> getAuxIat() { return auxIat; }
52+
ArrayRef<Chunk *> getAuxIatCopy() { return auxIatCopy; }
5153

5254
uint64_t getDirRVA() { return dirs[0]->getRVA(); }
5355
uint64_t getDirSize();
@@ -69,6 +71,8 @@ class DelayLoadContents {
6971
std::vector<Chunk *> pdata;
7072
std::vector<Chunk *> unwindinfo;
7173
std::vector<Chunk *> dllNames;
74+
std::vector<Chunk *> auxIat;
75+
std::vector<Chunk *> auxIatCopy;
7276

7377
COFFLinkerContext &ctx;
7478
};

lld/COFF/Driver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2465,6 +2465,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
24652465
ctx.symtab.addAbsolute("__arm64x_extra_rfe_table_size", 0);
24662466
ctx.symtab.addAbsolute("__arm64x_redirection_metadata", 0);
24672467
ctx.symtab.addAbsolute("__arm64x_redirection_metadata_count", 0);
2468+
ctx.symtab.addAbsolute("__hybrid_auxiliary_delayload_iat_copy", 0);
2469+
ctx.symtab.addAbsolute("__hybrid_auxiliary_delayload_iat", 0);
24682470
ctx.symtab.addAbsolute("__hybrid_auxiliary_iat", 0);
24692471
ctx.symtab.addAbsolute("__hybrid_auxiliary_iat_copy", 0);
24702472
ctx.symtab.addAbsolute("__hybrid_code_map", 0);

lld/COFF/Writer.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,13 @@ void Writer::appendECImportTables() {
958958
auxIat->chunks.end());
959959
rdataSec->addContributingPartialSection(auxIat);
960960
}
961+
962+
if (!delayIdata.getAuxIat().empty()) {
963+
delayIdata.getAuxIat().front()->setAlignment(0x1000);
964+
rdataSec->chunks.insert(rdataSec->chunks.end(),
965+
delayIdata.getAuxIat().begin(),
966+
delayIdata.getAuxIat().end());
967+
}
961968
}
962969

963970
// Locate the first Chunk and size of the import directory list and the
@@ -1294,6 +1301,8 @@ void Writer::appendImportThunks() {
12941301
textSec->addChunk(c);
12951302
for (Chunk *c : delayIdata.getCodePData())
12961303
pdataSec->addChunk(c);
1304+
for (Chunk *c : delayIdata.getAuxIatCopy())
1305+
rdataSec->addChunk(c);
12971306
for (Chunk *c : delayIdata.getCodeUnwindInfo())
12981307
rdataSec->addChunk(c);
12991308
}
@@ -2295,6 +2304,20 @@ void Writer::setECSymbols() {
22952304
replaceSymbol<DefinedSynthetic>(
22962305
iatCopySym, "__hybrid_auxiliary_iat_copy",
22972306
idata.auxIatCopy.empty() ? nullptr : idata.auxIatCopy.front());
2307+
2308+
Symbol *delayIatSym =
2309+
ctx.symtab.findUnderscore("__hybrid_auxiliary_delayload_iat");
2310+
replaceSymbol<DefinedSynthetic>(
2311+
delayIatSym, "__hybrid_auxiliary_delayload_iat",
2312+
delayIdata.getAuxIat().empty() ? nullptr
2313+
: delayIdata.getAuxIat().front());
2314+
2315+
Symbol *delayIatCopySym =
2316+
ctx.symtab.findUnderscore("__hybrid_auxiliary_delayload_iat_copy");
2317+
replaceSymbol<DefinedSynthetic>(
2318+
delayIatCopySym, "__hybrid_auxiliary_delayload_iat_copy",
2319+
delayIdata.getAuxIatCopy().empty() ? nullptr
2320+
: delayIdata.getAuxIatCopy().front());
22982321
}
22992322

23002323
// Write section contents to a mmap'ed file.

lld/test/COFF/Inputs/loadconfig-arm64ec.s

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ __chpe_metadata:
7979
.word __arm64x_extra_rfe_table_size
8080
.rva __os_arm64x_dispatch_fptr
8181
.rva __hybrid_auxiliary_iat_copy
82-
.word 0 // __hybrid_auxiliary_delayload_iat
83-
.word 0 // __hybrid_auxiliary_delayload_iat_copy
82+
.rva __hybrid_auxiliary_delayload_iat
83+
.rva __hybrid_auxiliary_delayload_iat_copy
8484
.word 0 // __hybrid_image_info_bitfield
8585
.rva __os_arm64x_helper3
8686
.rva __os_arm64x_helper4
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
REQUIRES: aarch64, x86
2+
RUN: split-file %s %t.dir && cd %t.dir
3+
4+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test.s -o test.obj
5+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
6+
RUN: llvm-lib -machine:arm64ec -def:test.def -out:test-arm64ec.lib
7+
RUN: llvm-lib -machine:arm64ec -def:test2.def -out:test2-arm64ec.lib
8+
9+
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
11+
12+
RUN: llvm-readobj --hex-dump=.test out.dll | FileCheck --check-prefix=TESTSEC %s
13+
TESTSEC: 0x180008000 00600000 88700000 00200000 10100000
14+
TESTSEC-NEXT: 0x180008010 08600000 90700000 10200000 30100000
15+
TESTSEC-NEXT: 0x180008020 1c100000 3c100000 00300000
16+
17+
RUN: llvm-objdump -d out.dll | FileCheck --check-prefix=DISASM %s
18+
DISASM: 0000000180001000 <.text>:
19+
DISASM-NEXT: 80001000: 52800000 mov w0, #0x0 // =0
20+
DISASM-NEXT: 180001004: d65f03c0 ret
21+
DISASM-NEXT: 180001008: 52800020 mov w0, #0x1 // =1
22+
DISASM-NEXT: 18000100c: d65f03c0 ret
23+
DISASM-NEXT: 180001010: b0000030 adrp x16, 0x180006000
24+
DISASM-NEXT: 180001014: f9400210 ldr x16, [x16]
25+
DISASM-NEXT: 180001018: d61f0200 br x16
26+
DISASM-NEXT: 18000101c: d000002b adrp x11, 0x180007000
27+
DISASM-NEXT: 180001020: f940456b ldr x11, [x11, #0x88]
28+
DISASM-NEXT: 180001024: 9000000a adrp x10, 0x180001000 <.text>
29+
DISASM-NEXT: 180001028: 9101414a add x10, x10, #0x50
30+
DISASM-NEXT: 18000102c: 17fffff5 b 0x180001000 <.text>
31+
DISASM-NEXT: 180001030: b0000030 adrp x16, 0x180006000
32+
DISASM-NEXT: 180001034: f9400610 ldr x16, [x16, #0x8]
33+
DISASM-NEXT: 180001038: d61f0200 br x16
34+
DISASM-NEXT: 18000103c: d000002b adrp x11, 0x180007000
35+
DISASM-NEXT: 180001040: f940496b ldr x11, [x11, #0x90]
36+
DISASM-NEXT: 180001044: 9000000a adrp x10, 0x180001000 <.text>
37+
DISASM-NEXT: 180001048: 9101614a add x10, x10, #0x58
38+
DISASM-NEXT: 18000104c: 17ffffed b 0x180001000 <.text>
39+
DISASM-NEXT: 180001050: 52800040 mov w0, #0x2 // =2
40+
DISASM-NEXT: 180001054: d65f03c0 ret
41+
DISASM-NEXT: 180001058: 52800060 mov w0, #0x3 // =3
42+
DISASM-NEXT: 18000105c: d65f03c0 ret
43+
DISASM-NEXT: ...
44+
DISASM-NEXT: 180002000: ff 25 82 50 00 00 jmpq *0x5082(%rip) # 0x180007088
45+
DISASM-NEXT: ...
46+
DISASM-NEXT: 18000200e: 00 00 addb %al, (%rax)
47+
DISASM-NEXT: 180002010: ff 25 7a 50 00 00 jmpq *0x507a(%rip) # 0x180007090
48+
DISASM-NEXT: 180002016: 48 8d 05 6b 50 00 00 leaq 0x506b(%rip), %rax # 0x180007088
49+
DISASM-NEXT: 18000201d: e9 0c 00 00 00 jmp 0x18000202e <.text+0x102e>
50+
DISASM-NEXT: 180002022: 48 8d 05 67 50 00 00 leaq 0x5067(%rip), %rax # 0x180007090
51+
DISASM-NEXT: 180002029: e9 00 00 00 00 jmp 0x18000202e <.text+0x102e>
52+
DISASM-NEXT: 18000202e: 51 pushq %rcx
53+
DISASM-NEXT: 18000202f: 52 pushq %rdx
54+
DISASM-NEXT: 180002030: 41 50 pushq %r8
55+
DISASM-NEXT: 180002032: 41 51 pushq %r9
56+
DISASM-NEXT: 180002034: 48 83 ec 48 subq $0x48, %rsp
57+
DISASM-NEXT: 180002038: 66 0f 7f 04 24 movdqa %xmm0, (%rsp)
58+
DISASM-NEXT: 18000203d: 66 0f 7f 4c 24 10 movdqa %xmm1, 0x10(%rsp)
59+
DISASM-NEXT: 180002043: 66 0f 7f 54 24 20 movdqa %xmm2, 0x20(%rsp)
60+
DISASM-NEXT: 180002049: 66 0f 7f 5c 24 30 movdqa %xmm3, 0x30(%rsp)
61+
DISASM-NEXT: 18000204f: 48 8b d0 movq %rax, %rdx
62+
DISASM-NEXT: 180002052: 48 8d 0d 97 21 00 00 leaq 0x2197(%rip), %rcx # 0x1800041f0
63+
DISASM-NEXT: 180002059: e8 aa ef ff ff callq 0x180001008 <.text+0x8>
64+
DISASM-NEXT: 18000205e: 66 0f 6f 04 24 movdqa (%rsp), %xmm0
65+
DISASM-NEXT: 180002063: 66 0f 6f 4c 24 10 movdqa 0x10(%rsp), %xmm1
66+
DISASM-NEXT: 180002069: 66 0f 6f 54 24 20 movdqa 0x20(%rsp), %xmm2
67+
DISASM-NEXT: 18000206f: 66 0f 6f 5c 24 30 movdqa 0x30(%rsp), %xmm3
68+
DISASM-NEXT: 180002075: 48 83 c4 48 addq $0x48, %rsp
69+
DISASM-NEXT: 180002079: 41 59 popq %r9
70+
DISASM-NEXT: 18000207b: 41 58 popq %r8
71+
DISASM-NEXT: 18000207d: 5a popq %rdx
72+
DISASM-NEXT: 18000207e: 59 popq %rcx
73+
DISASM-NEXT: 18000207f: ff e0 jmpq *%rax
74+
75+
RUN: llvm-readobj --coff-load-config out.dll | FileCheck --check-prefix=LOADCFG %s
76+
LOADCFG: CHPEMetadata [
77+
LOADCFG: AuxiliaryDelayloadIAT: 0x6000
78+
LOADCFG-NEXT: AuxiliaryDelayloadIATCopy: 0x4000
79+
80+
RUN: llvm-readobj --coff-imports out.dll | FileCheck --check-prefix=IMPORTS %s
81+
IMPORTS: DelayImport {
82+
IMPORTS-NEXT: Name: test.dll
83+
IMPORTS-NEXT: Attributes: 0x1
84+
IMPORTS-NEXT: ModuleHandle: 0x7080
85+
IMPORTS-NEXT: ImportAddressTable: 0x7088
86+
IMPORTS-NEXT: ImportNameTable: 0x4230
87+
IMPORTS-NEXT: BoundDelayImportTable: 0x0
88+
IMPORTS-NEXT: UnloadDelayImportTable: 0x0
89+
IMPORTS-NEXT: Import {
90+
IMPORTS-NEXT: Symbol: func (0)
91+
IMPORTS-NEXT: Address: 0x180002016
92+
IMPORTS-NEXT: }
93+
IMPORTS-NEXT: Import {
94+
IMPORTS-NEXT: Symbol: func2 (0)
95+
IMPORTS-NEXT: Address: 0x180002022
96+
IMPORTS-NEXT: }
97+
IMPORTS-NEXT: }
98+
99+
RUN: FileCheck --check-prefix=MAP %s < out.map
100+
MAP: 0001:00000008 #__delayLoadHelper2 0000000180001008 test.obj
101+
MAP: 0001:00000010 #func 0000000180001010 test-arm64ec:test.dll
102+
MAP-NEXT: 0001:0000001c __impchk_func 000000018000101c test-arm64ec:test.dll
103+
MAP-NEXT: 0001:00000030 #func2 0000000180001030 test-arm64ec:test.dll
104+
MAP-NEXT: 0001:0000003c __impchk_func2 000000018000103c test-arm64ec:test.dll
105+
MAP-NEXT: 0001:00000050 func_exit_thunk 0000000180001050 test.obj
106+
MAP-NEXT: 0001:00000058 func2_exit_thunk 0000000180001058 test.obj
107+
MAP-NEXT: 0001:00001000 func 0000000180002000 test-arm64ec:test.dll
108+
MAP-NEXT: 0001:00001010 func2 0000000180002010 test-arm64ec:test.dll
109+
MAP-NEXT: 0002:00000000 __imp_data 0000000180003000 test2-arm64ec:test2.dll
110+
MAP-NEXT: 0000:00000000 __hybrid_auxiliary_delayload_iat_copy 0000000180004000 <linker-defined>
111+
MAP-NEXT: 0002:00001000 __auximpcopy_func 0000000180004000 test-arm64ec:test.dll
112+
MAP-NEXT: 0002:00001008 __auximpcopy_func2 0000000180004008 test-arm64ec:test.dll
113+
MAP: 0002:00003000 __imp_func 0000000180006000 test-arm64ec:test.dll
114+
MAP-NEXT: 0002:00003008 __imp_func2 0000000180006008 test-arm64ec:test.dll
115+
MAP: 0003:00000088 __imp_aux_func 0000000180007088 test-arm64ec:test.dll
116+
MAP-NEXT: 0003:00000090 __imp_aux_func2 0000000180007090 test-arm64ec:test.dll
117+
118+
RUN: llvm-readobj --hex-dump=.rdata out.dll | FileCheck --check-prefix=RDATA %s
119+
RDATA: 0x180004000 1c100080 01000000 3c100080 01000000
120+
RDATA-NEXT: 0x180004010 00000000 00000000
121+
RDATA: 0x180006000 1c100080 01000000 3c100080 01000000
122+
RDATA-NEXT: 0x180006010 00000000 00000000
123+
124+
RUN: llvm-readobj --coff-basereloc out.dll | FileCheck --check-prefix=RELOC %s
125+
RELOC: BaseReloc [
126+
RELOC-NEXT: Entry {
127+
RELOC-NEXT: Type: DIR64
128+
RELOC-NEXT: Address: 0x4000
129+
RELOC-NEXT: }
130+
RELOC-NEXT: Entry {
131+
RELOC-NEXT: Type: DIR64
132+
RELOC-NEXT: Address: 0x4008
133+
RELOC-NEXT: }
134+
RELOC: Address: 0x6000
135+
RELOC-NEXT: }
136+
RELOC-NEXT: Entry {
137+
RELOC-NEXT: Type: DIR64
138+
RELOC-NEXT: Address: 0x6008
139+
RELOC-NEXT: }
140+
141+
#--- test.s
142+
.section .test,"r"
143+
.rva __imp_func
144+
.rva __imp_aux_func
145+
.rva func
146+
.rva "#func"
147+
.rva __imp_func2
148+
.rva __imp_aux_func2
149+
.rva func2
150+
.rva "#func2"
151+
.rva __impchk_func
152+
.rva __impchk_func2
153+
.rva __imp_data
154+
155+
.section .text,"xr",discard,__icall_helper_arm64ec
156+
.globl __icall_helper_arm64ec
157+
.p2align 2, 0x0
158+
__icall_helper_arm64ec:
159+
mov w0, #0
160+
ret
161+
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+
172+
.section .hybmp$x, "yi"
173+
.symidx __imp_func
174+
.symidx func_exit_thunk
175+
.word 4
176+
.symidx __imp_func2
177+
.symidx func2_exit_thunk
178+
.word 4
179+
180+
.section .wowthk$aa,"xr",discard,func_exit_thunk
181+
.globl func_exit_thunk
182+
func_exit_thunk:
183+
mov w0, #2
184+
ret
185+
186+
.section .wowthk$aa,"xr",discard,func2_exit_thunk
187+
.globl func2_exit_thunk
188+
func2_exit_thunk:
189+
mov w0, #3
190+
ret
191+
192+
#--- test.def
193+
NAME test.dll
194+
EXPORTS
195+
func
196+
func2
197+
198+
#--- test2.def
199+
NAME test2.dll
200+
EXPORTS
201+
data DATA

0 commit comments

Comments
 (0)