Skip to content

Commit 72c6ca6

Browse files
authored
[lld][COFF] Support .pdata section on ARM64EC targets. (llvm#72521)
ARM64EC needs to handle both ARM and x86_64 exception tables. This is achieved by separating their chunks and sorting them separately. EXCEPTION_TABLE directory references x86_64 variant, while ARM variant is exposed using CHPE metadata, which references __arm64x_extra_rfe_table and __arm64x_extra_rfe_table_size symbols.
1 parent 7788ef4 commit 72c6ca6

File tree

4 files changed

+194
-8
lines changed

4 files changed

+194
-8
lines changed

lld/COFF/Driver.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2361,6 +2361,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
23612361
ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0);
23622362

23632363
if (isArm64EC(config->machine)) {
2364+
ctx.symtab.addAbsolute("__arm64x_extra_rfe_table", 0);
2365+
ctx.symtab.addAbsolute("__arm64x_extra_rfe_table_size", 0);
23642366
ctx.symtab.addAbsolute("__hybrid_code_map", 0);
23652367
ctx.symtab.addAbsolute("__hybrid_code_map_count", 0);
23662368
}

lld/COFF/Writer.cpp

Lines changed: 58 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,7 @@ class Writer {
247247
void maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
248248
StringRef countSym, bool hasFlag=false);
249249
void setSectionPermissions();
250+
void setECSymbols();
250251
void writeSections();
251252
void writeBuildId();
252253
void writePEChecksum();
@@ -326,6 +327,9 @@ class Writer {
326327
// files, so we need to keep track of them separately.
327328
ChunkRange pdata;
328329

330+
// x86_64 .pdata sections on ARM64EC/ARM64X targets.
331+
ChunkRange hybridPdata;
332+
329333
COFFLinkerContext &ctx;
330334
};
331335
} // anonymous namespace
@@ -741,6 +745,7 @@ void Writer::run() {
741745
removeEmptySections();
742746
assignOutputSectionIndices();
743747
setSectionPermissions();
748+
setECSymbols();
744749
createSymbolAndStringTable();
745750

746751
if (fileSize > UINT32_MAX)
@@ -1411,8 +1416,28 @@ void Writer::createSymbolAndStringTable() {
14111416
void Writer::mergeSections() {
14121417
llvm::TimeTraceScope timeScope("Merge sections");
14131418
if (!pdataSec->chunks.empty()) {
1414-
pdata.first = pdataSec->chunks.front();
1415-
pdata.last = pdataSec->chunks.back();
1419+
if (isArm64EC(ctx.config.machine)) {
1420+
// On ARM64EC .pdata may contain both ARM64 and X64 data. Split them by
1421+
// sorting and store their regions separately.
1422+
llvm::stable_sort(pdataSec->chunks, [=](const Chunk *a, const Chunk *b) {
1423+
return (a->getMachine() == AMD64) < (b->getMachine() == AMD64);
1424+
});
1425+
1426+
for (auto chunk : pdataSec->chunks) {
1427+
if (chunk->getMachine() == AMD64) {
1428+
hybridPdata.first = chunk;
1429+
hybridPdata.last = pdataSec->chunks.back();
1430+
break;
1431+
}
1432+
1433+
if (!pdata.first)
1434+
pdata.first = chunk;
1435+
pdata.last = chunk;
1436+
}
1437+
} else {
1438+
pdata.first = pdataSec->chunks.front();
1439+
pdata.last = pdataSec->chunks.back();
1440+
}
14161441
}
14171442

14181443
for (auto &p : ctx.config.merge) {
@@ -1668,10 +1693,15 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
16681693
dir[RESOURCE_TABLE].RelativeVirtualAddress = rsrcSec->getRVA();
16691694
dir[RESOURCE_TABLE].Size = rsrcSec->getVirtualSize();
16701695
}
1671-
if (pdata.first) {
1672-
dir[EXCEPTION_TABLE].RelativeVirtualAddress = pdata.first->getRVA();
1673-
dir[EXCEPTION_TABLE].Size =
1674-
pdata.last->getRVA() + pdata.last->getSize() - pdata.first->getRVA();
1696+
// ARM64EC (but not ARM64X) contains x86_64 exception table in data directory.
1697+
ChunkRange &exceptionTable =
1698+
ctx.config.machine == ARM64EC ? hybridPdata : pdata;
1699+
if (exceptionTable.first) {
1700+
dir[EXCEPTION_TABLE].RelativeVirtualAddress =
1701+
exceptionTable.first->getRVA();
1702+
dir[EXCEPTION_TABLE].Size = exceptionTable.last->getRVA() +
1703+
exceptionTable.last->getSize() -
1704+
exceptionTable.first->getRVA();
16751705
}
16761706
if (relocSec->getVirtualSize()) {
16771707
dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
@@ -2084,6 +2114,24 @@ void Writer::setSectionPermissions() {
20842114
}
20852115
}
20862116

2117+
// Set symbols used by ARM64EC metadata.
2118+
void Writer::setECSymbols() {
2119+
if (!isArm64EC(ctx.config.machine))
2120+
return;
2121+
2122+
Symbol *rfeTableSym = ctx.symtab.findUnderscore("__arm64x_extra_rfe_table");
2123+
replaceSymbol<DefinedSynthetic>(rfeTableSym, "__arm64x_extra_rfe_table",
2124+
pdata.first);
2125+
2126+
if (pdata.first) {
2127+
Symbol *rfeSizeSym =
2128+
ctx.symtab.findUnderscore("__arm64x_extra_rfe_table_size");
2129+
cast<DefinedAbsolute>(rfeSizeSym)
2130+
->setVA(pdata.last->getRVA() + pdata.last->getSize() -
2131+
pdata.first->getRVA());
2132+
}
2133+
}
2134+
20872135
// Write section contents to a mmap'ed file.
20882136
void Writer::writeSections() {
20892137
llvm::TimeTraceScope timeScope("Write sections");
@@ -2206,6 +2254,10 @@ void Writer::sortExceptionTables() {
22062254
case AMD64:
22072255
sortExceptionTable<EntryX64>(pdata);
22082256
break;
2257+
case ARM64EC:
2258+
case ARM64X:
2259+
sortExceptionTable<EntryX64>(hybridPdata);
2260+
[[fallthrough]];
22092261
case ARMNT:
22102262
case ARM64:
22112263
sortExceptionTable<EntryArm>(pdata);

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 0 // __arm64x_redirection_metadata_count
8080
.rva __os_arm64x_get_x64_information
8181
.rva __os_arm64x_set_x64_information
82-
.word 0 // __arm64x_extra_rfe_table
83-
.word 0 // __arm64x_extra_rfe_table_size
82+
.rva __arm64x_extra_rfe_table
83+
.word __arm64x_extra_rfe_table_size
8484
.rva __os_arm64x_dispatch_fptr
8585
.word 0 // __hybrid_auxiliary_iat_copy
8686
.rva __os_arm64x_helper0

lld/test/COFF/pdata-arm64ec.test

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
REQUIRES: aarch64, x86
2+
RUN: split-file %s %t.dir && cd %t.dir
3+
4+
Test handlign of hybrid .pdata section on ARM64EC target.
5+
6+
RUN: llvm-mc -filetype=obj -triple=arm64-windows arm64-func-sym.s -o arm64-func-sym.obj
7+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func-sym.s -o arm64ec-func-sym.obj
8+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows x86_64-func-sym.s -o x86_64-func-sym.obj
9+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %p/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
10+
11+
Only arm64ec code:
12+
13+
RUN: lld-link -out:test1.dll -machine:arm64ec arm64ec-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
14+
15+
RUN: llvm-readobj --coff-load-config test1.dll | FileCheck -check-prefix=LOADCFG %s
16+
LOADCFG: ExtraRFETable: 0x4000
17+
LOADCFG-NEXT: ExtraRFETableSize: 0x8
18+
19+
RUN: llvm-readobj --headers test1.dll | FileCheck -check-prefix=NODIR %s
20+
NODIR: ExceptionTableSize: 0x0
21+
22+
RUN: llvm-objdump -s --section=.pdata test1.dll | FileCheck -check-prefix=DATA %s
23+
DATA: 180004000 00100000 11000001
24+
25+
Only x86_64 code:
26+
27+
RUN: lld-link -out:test2.dll -machine:arm64ec x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
28+
29+
RUN: llvm-readobj --coff-load-config test2.dll | FileCheck -check-prefix=NOLOADCFG %s
30+
NOLOADCFG: ExtraRFETableSize: 0x0
31+
32+
RUN: llvm-readobj --headers test2.dll | FileCheck -check-prefix=DIR %s
33+
DIR: ExceptionTableRVA: 0x4000
34+
DIR-NEXT: ExceptionTableSize: 0xC
35+
36+
RUN: llvm-objdump -s --section=.pdata test2.dll | FileCheck -check-prefix=DATA2 %s
37+
DATA2: 180004000 00100000 0e100000
38+
39+
Mixed arm64ec and x86_64 code:
40+
41+
RUN: lld-link -out:test3.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj \
42+
RUN: loadconfig-arm64ec.obj -dll -noentry
43+
44+
RUN: llvm-readobj --coff-load-config test3.dll | FileCheck -check-prefix=LOADCFG2 %s
45+
LOADCFG2: ExtraRFETable: 0x5000
46+
LOADCFG2-NEXT: ExtraRFETableSize: 0x8
47+
48+
RUN: llvm-readobj --headers test3.dll | FileCheck -check-prefix=DIR2 %s
49+
DIR2: ExceptionTableRVA: 0x5008
50+
DIR2-NEXT: ExceptionTableSize: 0xC
51+
52+
RUN: llvm-objdump -s --section=.pdata test3.dll | FileCheck -check-prefix=DATA3 %s
53+
DATA3: 180005000 00100000 11000001 00200000 0e200000
54+
55+
Mixed arm64x code:
56+
57+
RUN: lld-link -out:test4.dll -machine:arm64x arm64-func-sym.obj arm64ec-func-sym.obj \
58+
RUN: x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
59+
60+
RUN: llvm-readobj --headers test4.dll | FileCheck -check-prefix=DIR3 %s
61+
DIR3: ExceptionTableRVA: 0x6000
62+
DIR3-NEXT: ExceptionTableSize: 0x10
63+
64+
RUN: llvm-objdump -s --section=.pdata test4.dll | FileCheck -check-prefix=DATA4 %s
65+
DATA4: 180006000 00100000 11000001 00200000 11000001 ......... ......
66+
DATA4: 180006010 00300000 0e300000
67+
68+
Order of inputs doesn't matter, the data is sorted by type and RVA:
69+
70+
RUN: lld-link -out:test5.dll -machine:arm64ec x86_64-func-sym.obj arm64ec-func-sym.obj \
71+
RUN: loadconfig-arm64ec.obj -dll -noentry
72+
RUN: llvm-readobj --coff-load-config test5.dll | FileCheck -check-prefix=LOADCFG2 %s
73+
RUN: llvm-readobj --headers test5.dll | FileCheck -check-prefix=DIR2 %s
74+
RUN: llvm-objdump -s --section=.pdata test5.dll | FileCheck -check-prefix=DATA3 %s
75+
76+
RUN: lld-link -out:test6.dll -machine:arm64x arm64ec-func-sym.obj x86_64-func-sym.obj \
77+
RUN: arm64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
78+
RUN: llvm-readobj --headers test6.dll | FileCheck -check-prefix=DIR3 %s
79+
RUN: llvm-objdump -s --section=.pdata test6.dll | FileCheck -check-prefix=DATA4 %s
80+
81+
RUN: lld-link -out:test7.dll -machine:arm64x x86_64-func-sym.obj arm64ec-func-sym.obj \
82+
RUN: arm64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
83+
RUN: llvm-readobj --headers test7.dll | FileCheck -check-prefix=DIR3 %s
84+
RUN: llvm-objdump -s --section=.pdata test7.dll | FileCheck -check-prefix=DATA4 %s
85+
86+
#--- arm64-func-sym.s
87+
.text
88+
.globl arm64_func_sym
89+
.p2align 2, 0x0
90+
arm64_func_sym:
91+
.seh_proc arm64_func_sym
92+
sub sp, sp, #32
93+
.seh_stackalloc 32
94+
.seh_endprologue
95+
mov w0, #2
96+
.seh_startepilogue
97+
add sp, sp, #32
98+
.seh_stackalloc 32
99+
.seh_endepilogue
100+
ret
101+
.seh_endproc
102+
103+
#--- arm64ec-func-sym.s
104+
.text
105+
.globl arm64ec_func_sym
106+
.p2align 2, 0x0
107+
arm64ec_func_sym:
108+
.seh_proc arm64ec_func_sym
109+
sub sp, sp, #32
110+
.seh_stackalloc 32
111+
.seh_endprologue
112+
mov w0, #3
113+
.seh_startepilogue
114+
add sp, sp, #32
115+
.seh_stackalloc 32
116+
.seh_endepilogue
117+
ret
118+
.seh_endproc
119+
120+
#--- x86_64-func-sym.s
121+
.text
122+
.globl x86_64_func_sym
123+
.p2align 2, 0x0
124+
x86_64_func_sym:
125+
.seh_proc x86_64_func_sym
126+
subq $40, %rsp
127+
.seh_stackalloc 40
128+
.seh_endprologue
129+
movl $4, %eax
130+
addq $40, %rsp
131+
retq
132+
.seh_endproc

0 commit comments

Comments
 (0)