Skip to content

Commit b93baf6

Browse files
committed
[LLD][COFF] Generate X64 thunks for ARM64EC entry points and patchable functions.
1 parent 1a2a18f commit b93baf6

File tree

10 files changed

+345
-6
lines changed

10 files changed

+345
-6
lines changed

lld/COFF/Chunks.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,4 +1073,9 @@ void AbsolutePointerChunk::writeTo(uint8_t *buf) const {
10731073
}
10741074
}
10751075

1076+
void ECExportThunkChunk::writeTo(uint8_t *buf) const {
1077+
memcpy(buf, ECExportThunkCode, sizeof(ECExportThunkCode));
1078+
write32le(buf + 10, target->getRVA() - rva - 14);
1079+
}
1080+
10761081
} // namespace lld::coff

lld/COFF/Chunks.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,26 @@ class ECCodeMapChunk : public NonSectionChunk {
749749
std::vector<ECCodeMapEntry> &map;
750750
};
751751

752+
static const uint8_t ECExportThunkCode[] = {
753+
0x48, 0x8b, 0xc4, // movq %rsp, %rax
754+
0x48, 0x89, 0x58, 0x20, // movq %rbx, 0x20(%rax)
755+
0x55, // pushq %rbp
756+
0x5d, // popq %rbp
757+
0xe9, 0, 0, 0, 0, // jmp *0x0
758+
0xcc, // int3
759+
0xcc // int3
760+
};
761+
762+
class ECExportThunkChunk : public NonSectionCodeChunk {
763+
public:
764+
explicit ECExportThunkChunk(Defined *targetSym) : target(targetSym) {}
765+
size_t getSize() const override { return sizeof(ECExportThunkCode); };
766+
void writeTo(uint8_t *buf) const override;
767+
MachineTypes getMachine() const override { return AMD64; }
768+
769+
Defined *target;
770+
};
771+
752772
// MinGW specific, for the "automatic import of variables from DLLs" feature.
753773
// This provides the table of runtime pseudo relocations, for variable
754774
// references that turned out to need to be imported from a DLL even though

lld/COFF/Driver.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1317,6 +1317,70 @@ void LinkerDriver::convertResources() {
13171317
f->includeResourceChunks();
13181318
}
13191319

1320+
void LinkerDriver::maybeCreateECExportThunk(StringRef name, Symbol *&sym) {
1321+
Defined *def;
1322+
if (!sym)
1323+
return;
1324+
if (auto undef = dyn_cast<Undefined>(sym))
1325+
def = undef->getWeakAlias();
1326+
else
1327+
def = dyn_cast<Defined>(sym);
1328+
if (!def)
1329+
return;
1330+
1331+
if (def->getChunk()->getArm64ECRangeType() != chpe_range_type::Arm64EC)
1332+
return;
1333+
StringRef expName;
1334+
if (auto mangledName = getArm64ECMangledFunctionName(name))
1335+
expName = saver().save("EXP+" + *mangledName);
1336+
else
1337+
expName = saver().save("EXP+" + name);
1338+
sym = addUndefined(expName);
1339+
if (auto undef = dyn_cast<Undefined>(sym)) {
1340+
if (!undef->getWeakAlias()) {
1341+
auto thunk = make<ECExportThunkChunk>(def);
1342+
replaceSymbol<DefinedSynthetic>(undef, undef->getName(), thunk);
1343+
}
1344+
}
1345+
}
1346+
1347+
void LinkerDriver::createECExportThunks() {
1348+
// Check if EXP+ symbols have corresponding $hp_target symbols and use them
1349+
// to create export thunks when available.
1350+
for (auto &s : ctx.symtab.expSymbols) {
1351+
if (!s->isUsedInRegularObj)
1352+
continue;
1353+
auto targetName = s->getName().substr(strlen("EXP+")) + "$hp_target";
1354+
Symbol *expSym = ctx.symtab.find(toString(targetName));
1355+
if (!expSym)
1356+
continue;
1357+
Defined *targetSym;
1358+
if (auto undef = dyn_cast<Undefined>(expSym))
1359+
targetSym = undef->getWeakAlias();
1360+
else
1361+
targetSym = dyn_cast<Defined>(expSym);
1362+
if (!targetSym)
1363+
continue;
1364+
1365+
auto *undef = dyn_cast<Undefined>(s);
1366+
if (undef && !undef->getWeakAlias()) {
1367+
auto thunk = make<ECExportThunkChunk>(targetSym);
1368+
replaceSymbol<DefinedSynthetic>(undef, undef->getName(), thunk);
1369+
}
1370+
if (!targetSym->isGCRoot) {
1371+
targetSym->isGCRoot = true;
1372+
ctx.config.gcroot.push_back(targetSym);
1373+
}
1374+
}
1375+
1376+
if (ctx.config.entry)
1377+
maybeCreateECExportThunk(ctx.config.entry->getName(), ctx.config.entry);
1378+
for (Export &e : ctx.config.exports) {
1379+
if (!e.data)
1380+
maybeCreateECExportThunk(e.extName.empty() ? e.name : e.extName, e.sym);
1381+
}
1382+
}
1383+
13201384
// In MinGW, if no symbols are chosen to be exported, then all symbols are
13211385
// automatically exported by default. This behavior can be forced by the
13221386
// -export-all-symbols option, so that it happens even when exports are
@@ -2520,6 +2584,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
25202584
if (!wrapped.empty())
25212585
wrapSymbols(ctx, wrapped);
25222586

2587+
if (isArm64EC(config->machine))
2588+
createECExportThunks();
2589+
25232590
// Resolve remaining undefined symbols and warn about imported locals.
25242591
ctx.symtab.resolveRemainingUndefines();
25252592
if (errorCount())

lld/COFF/Driver.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,10 @@ class LinkerDriver {
270270
// Convert Windows resource files (.res files) to a .obj file.
271271
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
272272
ArrayRef<ObjFile *> objs);
273+
274+
// Create export thunks for exported and patchable Arm64EC function symbols.
275+
void createECExportThunks();
276+
void maybeCreateECExportThunk(StringRef name, Symbol *&sym);
273277
};
274278

275279
// Create enum with OPT_xxx values for each option in Options.td

lld/COFF/SymbolTable.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,9 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
551551
sym->pendingArchiveLoad = false;
552552
sym->canInline = true;
553553
inserted = true;
554+
555+
if (isArm64EC(ctx.config.machine) && name.starts_with("EXP+"))
556+
expSymbols.push_back(sym);
554557
}
555558
return {sym, inserted};
556559
}

lld/COFF/SymbolTable.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ class SymbolTable {
116116
// A list of chunks which to be added to .rdata.
117117
std::vector<Chunk *> localImportChunks;
118118

119+
// A list of EC EXP+ symbols.
120+
std::vector<Symbol *> expSymbols;
121+
119122
// Iterates symbols in non-determinstic hash table order.
120123
template <typename T> void forEachSymbol(T callback) {
121124
for (auto &pair : symMap)

lld/COFF/Writer.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ class Writer {
304304
uint64_t sizeOfHeaders;
305305

306306
OutputSection *textSec;
307+
OutputSection *hexpthkSec;
307308
OutputSection *rdataSec;
308309
OutputSection *buildidSec;
309310
OutputSection *dataSec;
@@ -984,6 +985,8 @@ void Writer::createSections() {
984985

985986
// Try to match the section order used by link.exe.
986987
textSec = createSection(".text", code | r | x);
988+
if (isArm64EC(ctx.config.machine))
989+
hexpthkSec = createSection(".hexpthk", code | r | x);
987990
createSection(".bss", bss | r | w);
988991
rdataSec = createSection(".rdata", data | r);
989992
buildidSec = createSection(".buildid", data | r);
@@ -2046,6 +2049,14 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
20462049

20472050
// Create CHPE metadata chunks.
20482051
void Writer::createECChunks() {
2052+
for (Symbol *s : ctx.symtab.expSymbols) {
2053+
auto sym = dyn_cast<Defined>(s);
2054+
if (!sym || !sym->getChunk())
2055+
continue;
2056+
if (auto thunk = dyn_cast<ECExportThunkChunk>(sym->getChunk()))
2057+
hexpthkSec->addChunk(thunk);
2058+
}
2059+
20492060
auto codeMapChunk = make<ECCodeMapChunk>(codeMap);
20502061
rdataSec->addChunk(codeMapChunk);
20512062
Symbol *codeMapSym = ctx.symtab.findUnderscore("__hybrid_code_map");
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
REQUIRES: aarch64, x86
2+
RUN: split-file %s %t.dir && cd %t.dir
3+
4+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-data.s -o arm64ec-data.obj
5+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func.s -o arm64ec-func.obj
6+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows antidep-func.s -o antidep-func.obj
7+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows x86_64-func.s -o x86_64-func.obj
8+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
9+
10+
RUN: lld-link -out:exports.dll -machine:arm64ec arm64ec-func.obj x86_64-func.obj loadconfig-arm64ec.obj \
11+
RUN: arm64ec-data.obj -dll -noentry -export:arm64ec_func -export:func=arm64ec_func \
12+
RUN: -export:x86_64_func -export:data_sym,DATA
13+
14+
RUN: llvm-objdump -d exports.dll | FileCheck -check-prefix=EXP-DISASM %s
15+
EXP-DISASM: Disassembly of section .text:
16+
EXP-DISASM-EMPTY:
17+
EXP-DISASM-NEXT: 0000000180001000 <.text>:
18+
EXP-DISASM-NEXT: 180001000: 90000008 adrp x8, 0x180001000 <.text>
19+
EXP-DISASM-NEXT: 180001004: 52800040 mov w0, #0x2
20+
EXP-DISASM-NEXT: 180001008: d65f03c0 ret
21+
EXP-DISASM-NEXT: ...
22+
EXP-DISASM-EMPTY:
23+
EXP-DISASM-NEXT: 0000000180002000 <x86_64_func>:
24+
EXP-DISASM-NEXT: 180002000: e8 fb ef ff ff callq 0x180001000 <.text>
25+
EXP-DISASM-NEXT: 180002005: b8 03 00 00 00 movl $0x3, %eax
26+
EXP-DISASM-NEXT: 18000200a: c3 retq
27+
EXP-DISASM-EMPTY:
28+
EXP-DISASM-NEXT: Disassembly of section .hexpthk:
29+
EXP-DISASM-EMPTY:
30+
EXP-DISASM-NEXT: 0000000180003000 <func>:
31+
EXP-DISASM-NEXT: 180003000: 48 8b c4 movq %rsp, %rax
32+
EXP-DISASM-NEXT: 180003003: 48 89 58 20 movq %rbx, 0x20(%rax)
33+
EXP-DISASM-NEXT: 180003007: 55 pushq %rbp
34+
EXP-DISASM-NEXT: 180003008: 5d popq %rbp
35+
EXP-DISASM-NEXT: 180003009: e9 f2 df ff ff jmp 0x180001000 <.text>
36+
EXP-DISASM-NEXT: 18000300e: cc int3
37+
EXP-DISASM-NEXT: 18000300f: cc int3
38+
EXP-DISASM-EMPTY:
39+
EXP-DISASM-NEXT: 0000000180003010 <arm64ec_func>:
40+
EXP-DISASM-NEXT: 180003010: 48 8b c4 movq %rsp, %rax
41+
EXP-DISASM-NEXT: 180003013: 48 89 58 20 movq %rbx, 0x20(%rax)
42+
EXP-DISASM-NEXT: 180003017: 55 pushq %rbp
43+
EXP-DISASM-NEXT: 180003018: 5d popq %rbp
44+
EXP-DISASM-NEXT: 180003019: e9 e2 df ff ff jmp 0x180001000 <.text>
45+
EXP-DISASM-NEXT: 18000301e: cc int3
46+
EXP-DISASM-NEXT: 18000301f: cc int3
47+
48+
RUN: llvm-objdump -p exports.dll | FileCheck -check-prefix=EXP-EXPORT %s
49+
EXP-EXPORT: Ordinal RVA Name
50+
EXP-EXPORT-NEXT: 1 0x3010 arm64ec_func
51+
EXP-EXPORT-NEXT: 2 0x6000 data_sym
52+
EXP-EXPORT-NEXT: 3 0x3000 func
53+
EXP-EXPORT-NEXT: 4 0x2000 x86_64_func
54+
55+
RUN: llvm-readobj --coff-load-config exports.dll | FileCheck -check-prefix=EXP-CHPE %s
56+
EXP-CHPE: CodeMap [
57+
EXP-CHPE-NEXT: 0x1000 - 0x100C ARM64EC
58+
EXP-CHPE-NEXT: 0x2000 - 0x3020 X64
59+
EXP-CHPE-NEXT: ]
60+
61+
RUN: llvm-objdump -s --section=.test exports.dll | FileCheck --check-prefix=EXP-DATA %s
62+
EXP-DATA: 180006000 00300000 10300000
63+
64+
RUN: lld-link -out:exports2.dll -machine:arm64ec antidep-func.obj x86_64-func.obj loadconfig-arm64ec.obj \
65+
RUN: arm64ec-data.obj -dll -noentry -export:arm64ec_func -export:func=arm64ec_func \
66+
RUN: -export:x86_64_func -export:data_sym,DATA
67+
68+
RUN: llvm-objdump -d exports2.dll | FileCheck -check-prefix=EXP-DISASM %s
69+
RUN: llvm-objdump -p exports2.dll | FileCheck -check-prefix=EXP-EXPORT %s
70+
RUN: llvm-objdump -s --section=.test exports2.dll | FileCheck --check-prefix=EXP-DATA %s
71+
RUN: llvm-readobj --coff-load-config exports2.dll | FileCheck -check-prefix=EXP-CHPE %s
72+
73+
RUN: lld-link -out:entry.dll -machine:arm64ec arm64ec-func.obj loadconfig-arm64ec.obj -dll -entry:arm64ec_func
74+
75+
RUN: llvm-objdump -d entry.dll | FileCheck -check-prefix=ENTRY-DISASM %s
76+
ENTRY-DISASM: Disassembly of section .text:
77+
ENTRY-DISASM-EMPTY:
78+
ENTRY-DISASM-NEXT: 0000000180001000 <.text>:
79+
ENTRY-DISASM-NEXT: 180001000: 90000008 adrp x8, 0x180001000 <.text>
80+
ENTRY-DISASM-NEXT: 180001004: 52800040 mov w0, #0x2 // =2
81+
ENTRY-DISASM-NEXT: 180001008: d65f03c0 ret
82+
ENTRY-DISASM-EMPTY:
83+
ENTRY-DISASM-NEXT: Disassembly of section .hexpthk:
84+
ENTRY-DISASM-EMPTY:
85+
ENTRY-DISASM-NEXT: 0000000180002000 <.hexpthk>:
86+
ENTRY-DISASM-NEXT: 180002000: 48 8b c4 movq %rsp, %rax
87+
ENTRY-DISASM-NEXT: 180002003: 48 89 58 20 movq %rbx, 0x20(%rax)
88+
ENTRY-DISASM-NEXT: 180002007: 55 pushq %rbp
89+
ENTRY-DISASM-NEXT: 180002008: 5d popq %rbp
90+
ENTRY-DISASM-NEXT: 180002009: e9 f2 ef ff ff jmp 0x180001000 <.text>
91+
ENTRY-DISASM-NEXT: 18000200e: cc int3
92+
ENTRY-DISASM-NEXT: 18000200f: cc int3
93+
94+
RUN: llvm-readobj --headers entry.dll | FileCheck -check-prefix=ENTRY %s
95+
ENTRY: AddressOfEntryPoint: 0x2000
96+
97+
RUN: llvm-readobj --coff-load-config entry.dll | FileCheck -check-prefix=ENTRY-CHPE %s
98+
ENTRY-CHPE: CodeMap [
99+
ENTRY-CHPE-NEXT: 0x1000 - 0x100C ARM64EC
100+
ENTRY-CHPE-NEXT: 0x2000 - 0x2010 X64
101+
ENTRY-CHPE-NEXT: ]
102+
103+
104+
#--- arm64ec-func.s
105+
.text
106+
.globl arm64ec_func
107+
.p2align 2, 0x0
108+
arm64ec_func:
109+
adrp x8,arm64ec_func
110+
mov w0, #2
111+
ret
112+
113+
#--- antidep-func.s
114+
.text
115+
.globl "#arm64ec_func"
116+
.p2align 2, 0x0
117+
"#arm64ec_func":
118+
adrp x8,arm64ec_func
119+
mov w0, #2
120+
ret
121+
122+
.weak_anti_dep arm64ec_func
123+
arm64ec_func = "#arm64ec_func"
124+
125+
#--- arm64ec-data.s
126+
.section .test, "r"
127+
.globl data_sym
128+
.p2align 2, 0x0
129+
data_sym:
130+
.rva "EXP+#func"
131+
.rva "EXP+#arm64ec_func"
132+
133+
#--- x86_64-func.s
134+
.text
135+
.globl x86_64_func
136+
.p2align 2, 0x0
137+
x86_64_func:
138+
call arm64ec_func
139+
movl $3, %eax
140+
retq

lld/test/COFF/arm64ec-exports.s

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,32 @@
1616
; EXP: Export {
1717
; EXP-NEXT: Ordinal: 1
1818
; EXP-NEXT: Name: #mangled_data_sym
19-
; EXP-NEXT: RVA: 0x3000
19+
; EXP-NEXT: RVA: 0x4000
2020
; EXP-NEXT: }
2121
; EXP-NEXT: Export {
2222
; EXP-NEXT: Ordinal: 2
2323
; EXP-NEXT: Name: ?cxx_func@@YAHXZ
24-
; EXP-NEXT: RVA: 0x1018
24+
; EXP-NEXT: RVA: 0x2030
2525
; EXP-NEXT: }
2626
; EXP-NEXT: Export {
2727
; EXP-NEXT: Ordinal: 3
2828
; EXP-NEXT: Name: data_sym
29-
; EXP-NEXT: RVA: 0x3004
29+
; EXP-NEXT: RVA: 0x4004
3030
; EXP-NEXT: }
3131
; EXP-NEXT: Export {
3232
; EXP-NEXT: Ordinal: 4
3333
; EXP-NEXT: Name: exportas_func
34-
; EXP-NEXT: RVA: 0x1010
34+
; EXP-NEXT: RVA: 0x2020
3535
; EXP-NEXT: }
3636
; EXP-NEXT: Export {
3737
; EXP-NEXT: Ordinal: 5
3838
; EXP-NEXT: Name: mangled_func
39-
; EXP-NEXT: RVA: 0x1008
39+
; EXP-NEXT: RVA: 0x2010
4040
; EXP-NEXT: }
4141
; EXP-NEXT: Export {
4242
; EXP-NEXT: Ordinal: 6
4343
; EXP-NEXT: Name: unmangled_func
44-
; EXP-NEXT: RVA: 0x1000
44+
; EXP-NEXT: RVA: 0x2000
4545
; EXP-NEXT: }
4646

4747
; RUN: llvm-nm --print-armap out.lib | FileCheck --check-prefix=IMPLIB %s

0 commit comments

Comments
 (0)