Skip to content

Commit a33c2ff

Browse files
committed
[LLD][COFF] Add support for range extension thunks for ARM64EC targets.
Thunks themselves are the same as regular ARM64 thunks; they just need to report the correct machine type. When processing the code, we also need to use the current chunk's machine type instead of the global one: we don't want to treat x86_64 thunks as ARM64EC, and we need to report the correct machine type in hybrid binaries.
1 parent 26c582b commit a33c2ff

File tree

4 files changed

+215
-26
lines changed

4 files changed

+215
-26
lines changed

lld/COFF/Chunks.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -842,14 +842,9 @@ const uint8_t arm64Thunk[] = {
842842
0x00, 0x02, 0x1f, 0xd6, // br x16
843843
};
844844

845-
size_t RangeExtensionThunkARM64::getSize() const {
846-
assert(ctx.config.machine == ARM64);
847-
(void)&ctx;
848-
return sizeof(arm64Thunk);
849-
}
845+
size_t RangeExtensionThunkARM64::getSize() const { return sizeof(arm64Thunk); }
850846

851847
void RangeExtensionThunkARM64::writeTo(uint8_t *buf) const {
852-
assert(ctx.config.machine == ARM64);
853848
memcpy(buf, arm64Thunk, sizeof(arm64Thunk));
854849
applyArm64Addr(buf + 0, target->getRVA(), rva, 12);
855850
applyArm64Imm(buf + 4, target->getRVA() & 0xfff, 0);

lld/COFF/Chunks.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -615,20 +615,22 @@ class RangeExtensionThunkARM : public NonSectionCodeChunk {
615615
COFFLinkerContext &ctx;
616616
};
617617

618+
// A ragnge extension thunk used for both ARM64EC and ARM64 machine types.
618619
class RangeExtensionThunkARM64 : public NonSectionCodeChunk {
619620
public:
620-
explicit RangeExtensionThunkARM64(COFFLinkerContext &ctx, Defined *t)
621-
: target(t), ctx(ctx) {
621+
explicit RangeExtensionThunkARM64(MachineTypes machine, Defined *t)
622+
: target(t), machine(machine) {
622623
setAlignment(4);
624+
assert(llvm::COFF::isAnyArm64(machine));
623625
}
624626
size_t getSize() const override;
625627
void writeTo(uint8_t *buf) const override;
626-
MachineTypes getMachine() const override { return ARM64; }
628+
MachineTypes getMachine() const override { return machine; }
627629

628630
Defined *target;
629631

630632
private:
631-
COFFLinkerContext &ctx;
633+
MachineTypes machine;
632634
};
633635

634636
// Windows-specific.

lld/COFF/Writer.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,12 @@ class Writer {
219219
void sortECChunks();
220220
void removeUnusedSections();
221221
void assignAddresses();
222-
bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin);
222+
bool isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin,
223+
MachineTypes machine);
223224
std::pair<Defined *, bool> getThunk(DenseMap<uint64_t, Defined *> &lastThunks,
224225
Defined *target, uint64_t p,
225-
uint16_t type, int margin);
226+
uint16_t type, int margin,
227+
MachineTypes machine);
226228
bool createThunks(OutputSection *os, int margin);
227229
bool verifyRanges(const std::vector<Chunk *> chunks);
228230
void createECCodeMap();
@@ -396,8 +398,9 @@ void OutputSection::addContributingPartialSection(PartialSection *sec) {
396398

397399
// Check whether the target address S is in range from a relocation
398400
// of type relType at address P.
399-
bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
400-
if (ctx.config.machine == ARMNT) {
401+
bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin,
402+
MachineTypes machine) {
403+
if (machine == ARMNT) {
401404
int64_t diff = AbsoluteDifference(s, p + 4) + margin;
402405
switch (relType) {
403406
case IMAGE_REL_ARM_BRANCH20T:
@@ -408,7 +411,7 @@ bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
408411
default:
409412
return true;
410413
}
411-
} else if (ctx.config.machine == ARM64) {
414+
} else if (isAnyArm64(machine)) {
412415
int64_t diff = AbsoluteDifference(s, p) + margin;
413416
switch (relType) {
414417
case IMAGE_REL_ARM64_BRANCH26:
@@ -421,25 +424,25 @@ bool Writer::isInRange(uint16_t relType, uint64_t s, uint64_t p, int margin) {
421424
return true;
422425
}
423426
} else {
424-
llvm_unreachable("Unexpected architecture");
427+
return true;
425428
}
426429
}
427430

428431
// Return the last thunk for the given target if it is in range,
429432
// or create a new one.
430433
std::pair<Defined *, bool>
431434
Writer::getThunk(DenseMap<uint64_t, Defined *> &lastThunks, Defined *target,
432-
uint64_t p, uint16_t type, int margin) {
435+
uint64_t p, uint16_t type, int margin, MachineTypes machine) {
433436
Defined *&lastThunk = lastThunks[target->getRVA()];
434-
if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin))
437+
if (lastThunk && isInRange(type, lastThunk->getRVA(), p, margin, machine))
435438
return {lastThunk, false};
436439
Chunk *c;
437-
switch (ctx.config.machine) {
438-
case ARMNT:
440+
switch (getMachineArchType(machine)) {
441+
case Triple::thumb:
439442
c = make<RangeExtensionThunkARM>(ctx, target);
440443
break;
441-
case ARM64:
442-
c = make<RangeExtensionThunkARM64>(ctx, target);
444+
case Triple::aarch64:
445+
c = make<RangeExtensionThunkARM64>(machine, target);
443446
break;
444447
default:
445448
llvm_unreachable("Unexpected architecture");
@@ -471,6 +474,7 @@ bool Writer::createThunks(OutputSection *os, int margin) {
471474
SectionChunk *sc = dyn_cast_or_null<SectionChunk>(os->chunks[i]);
472475
if (!sc)
473476
continue;
477+
MachineTypes machine = sc->getMachine();
474478
size_t thunkInsertionSpot = i + 1;
475479

476480
// Try to get a good enough estimate of where new thunks will be placed.
@@ -497,11 +501,12 @@ bool Writer::createThunks(OutputSection *os, int margin) {
497501

498502
uint64_t s = sym->getRVA();
499503

500-
if (isInRange(rel.Type, s, p, margin))
504+
if (isInRange(rel.Type, s, p, margin, machine))
501505
continue;
502506

503507
// If the target isn't in range, hook it up to an existing or new thunk.
504-
auto [thunk, wasNew] = getThunk(lastThunks, sym, p, rel.Type, margin);
508+
auto [thunk, wasNew] =
509+
getThunk(lastThunks, sym, p, rel.Type, margin, machine);
505510
if (wasNew) {
506511
Chunk *thunkChunk = thunk->getChunk();
507512
thunkChunk->setRVA(
@@ -603,6 +608,7 @@ bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
603608
SectionChunk *sc = dyn_cast_or_null<SectionChunk>(c);
604609
if (!sc)
605610
continue;
611+
MachineTypes machine = sc->getMachine();
606612

607613
ArrayRef<coff_relocation> relocs = sc->getRelocs();
608614
for (const coff_relocation &rel : relocs) {
@@ -615,7 +621,7 @@ bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
615621
uint64_t p = sc->getRVA() + rel.VirtualAddress;
616622
uint64_t s = sym->getRVA();
617623

618-
if (!isInRange(rel.Type, s, p, 0))
624+
if (!isInRange(rel.Type, s, p, 0, machine))
619625
return false;
620626
}
621627
}
@@ -625,7 +631,7 @@ bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
625631
// Assign addresses and add thunks if necessary.
626632
void Writer::finalizeAddresses() {
627633
assignAddresses();
628-
if (ctx.config.machine != ARMNT && ctx.config.machine != ARM64)
634+
if (ctx.config.machine != ARMNT && !isAnyArm64(ctx.config.machine))
629635
return;
630636

631637
size_t origNumChunks = 0;

lld/test/COFF/arm64ec-range-thunks.s

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# REQUIRES: aarch64, x86
2+
# RUN: split-file %s %t.dir && cd %t.dir
3+
4+
# RUN: llvm-mc -filetype=obj -triple=arm64ec-windows funcs.s -o funcs-arm64ec.obj
5+
# RUN: llvm-mc -filetype=obj -triple=aarch64-windows native-funcs.s -o funcs-aarch64.obj
6+
# RUN: llvm-mc -filetype=obj -triple=x86_64-windows space.s -o space-x86_64.obj
7+
# RUN: llvm-mc -filetype=obj -triple=aarch64-windows space.s -o space-aarch64.obj
8+
# RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
9+
10+
11+
# Test generating range extension thunks for ARM64EC code. Place some x86_64 chunks in a middle
12+
# and make sure that thunks stay in ARM64EC code range.
13+
14+
# RUN: lld-link -machine:arm64ec -noentry -dll funcs-arm64ec.obj space-x86_64.obj loadconfig-arm64ec.obj -out:test.dll \
15+
# RUN: -verbose 2>&1 | FileCheck -check-prefix=VERBOSE %s
16+
# VERBOSE: Added 3 thunks with margin {{.*}} in 1 passes
17+
18+
# RUN: llvm-objdump -d test.dll | FileCheck --check-prefix=DISASM %s
19+
20+
# DISASM: Disassembly of section .code1:
21+
# DISASM-EMPTY:
22+
# DISASM-NEXT: 0000000180003000 <.code1>:
23+
# DISASM-NEXT: 180003000: 36000040 tbz w0, #0x0, 0x180003008 <.code1+0x8>
24+
# DISASM-NEXT: 180003004: d65f03c0 ret
25+
# DISASM-NEXT: 180003008: b0000050 adrp x16, 0x18000c000
26+
# DISASM-NEXT: 18000300c: 91000210 add x16, x16, #0x0
27+
# DISASM-NEXT: 180003010: d61f0200 br x16
28+
# DISASM-EMPTY:
29+
# DISASM-NEXT: Disassembly of section .code2:
30+
# DISASM-EMPTY:
31+
# DISASM-NEXT: 0000000180004000 <.code2>:
32+
# DISASM-NEXT: ...
33+
# DISASM-EMPTY:
34+
# DISASM-NEXT: Disassembly of section .code3:
35+
# DISASM-EMPTY:
36+
# DISASM-NEXT: 0000000180005000 <.code3>:
37+
# DISASM-NEXT: ...
38+
# DISASM-NEXT: 18000c000: 36000060 tbz w0, #0x0, 0x18000c00c <.code3+0x700c>
39+
# DISASM-NEXT: 18000c004: d65f03c0 ret
40+
# DISASM-NEXT: 18000c008: 00000000 udf #0x0
41+
# DISASM-NEXT: 18000c00c: 90000050 adrp x16, 0x180014000 <.code3+0xf000>
42+
# DISASM-NEXT: 18000c010: 91006210 add x16, x16, #0x18
43+
# DISASM-NEXT: 18000c014: d61f0200 br x16
44+
# DISASM-NEXT: ...
45+
# DISASM-NEXT: 180014018: 36000040 tbz w0, #0x0, 0x180014020 <.code3+0xf020>
46+
# DISASM-NEXT: 18001401c: d65f03c0 ret
47+
# DISASM-NEXT: 180014020: f0ffff70 adrp x16, 0x180003000 <.code1>
48+
# DISASM-NEXT: 180014024: 91000210 add x16, x16, #0x0
49+
# DISASM-NEXT: 180014028: d61f0200 br x16
50+
51+
# RUN: llvm-readobj --coff-load-config test.dll | FileCheck --check-prefix=LOADCFG %s
52+
53+
# LOADCFG: CodeMap [
54+
# LOADCFG-NEXT: 0x3000 - 0x3014 ARM64EC
55+
# LOADCFG-NEXT: 0x4000 - 0x4300 X64
56+
# LOADCFG-NEXT: 0x5000 - 0x1402C ARM64EC
57+
# LOADCFG-NEXT: ]
58+
59+
60+
# A similar test using a hybrid binary and native placeholder chunks.
61+
62+
# RUN: lld-link -machine:arm64x -noentry -dll funcs-arm64ec.obj space-aarch64.obj loadconfig-arm64ec.obj -out:testx.dll \
63+
# RUN: -verbose 2>&1 | FileCheck -check-prefix=VERBOSE %s
64+
# RUN: llvm-objdump -d testx.dll | FileCheck --check-prefix=DISASM %s
65+
66+
# RUN: llvm-readobj --coff-load-config testx.dll | FileCheck --check-prefix=LOADCFGX %s
67+
68+
# LOADCFGX: CodeMap [
69+
# LOADCFGX-NEXT: 0x3000 - 0x3014 ARM64EC
70+
# LOADCFGX-NEXT: 0x4000 - 0x4300 ARM64
71+
# LOADCFGX-NEXT: 0x5000 - 0x1402C ARM64EC
72+
# LOADCFGX-NEXT: ]
73+
74+
75+
# Test a hybrid ARM64X binary which requires range extension thunks for both native and EC relocations.
76+
77+
# RUN: lld-link -machine:arm64x -noentry -dll funcs-arm64ec.obj funcs-aarch64.obj loadconfig-arm64ec.obj -out:testx2.dll \
78+
# RUN: -verbose 2>&1 | FileCheck -check-prefix=VERBOSEX %s
79+
# VERBOSEX: Added 5 thunks with margin {{.*}} in 1 passes
80+
81+
# RUN: llvm-objdump -d testx2.dll | FileCheck --check-prefix=DISASMX %s
82+
83+
# DISASMX: Disassembly of section .code1:
84+
# DISASMX-EMPTY:
85+
# DISASMX-NEXT: 0000000180003000 <.code1>:
86+
# DISASMX-NEXT: 180003000: 36000040 tbz w0, #0x0, 0x180003008 <.code1+0x8>
87+
# DISASMX-NEXT: 180003004: d65f03c0 ret
88+
# DISASMX-NEXT: 180003008: b0000050 adrp x16, 0x18000c000
89+
# DISASMX-NEXT: 18000300c: 91000210 add x16, x16, #0x0
90+
# DISASMX-NEXT: 180003010: d61f0200 br x16
91+
# DISASMX-EMPTY:
92+
# DISASMX-NEXT: Disassembly of section .code2:
93+
# DISASMX-EMPTY:
94+
# DISASMX-NEXT: 0000000180004000 <.code2>:
95+
# DISASMX-NEXT: 180004000: 36000040 tbz w0, #0x0, 0x180004008 <.code2+0x8>
96+
# DISASMX-NEXT: 180004004: d65f03c0 ret
97+
# DISASMX-NEXT: 180004008: b0000090 adrp x16, 0x180015000
98+
# DISASMX-NEXT: 18000400c: 91000210 add x16, x16, #0x0
99+
# DISASMX-NEXT: 180004010: d61f0200 br x16
100+
# DISASMX-EMPTY:
101+
# DISASMX-NEXT: Disassembly of section .code3:
102+
# DISASMX-EMPTY:
103+
# DISASMX-NEXT: 0000000180005000 <.code3>:
104+
# DISASMX-NEXT: ...
105+
# DISASMX-NEXT: 18000c000: 36000060 tbz w0, #0x0, 0x18000c00c <.code3+0x700c>
106+
# DISASMX-NEXT: 18000c004: d65f03c0 ret
107+
# DISASMX-NEXT: 18000c008: 00000000 udf #0x0
108+
# DISASMX-NEXT: 18000c00c: 90000050 adrp x16, 0x180014000 <.code3+0xf000>
109+
# DISASMX-NEXT: 18000c010: 91006210 add x16, x16, #0x18
110+
# DISASMX-NEXT: 18000c014: d61f0200 br x16
111+
# DISASMX-NEXT: ...
112+
# DISASMX-NEXT: 180014018: 36000040 tbz w0, #0x0, 0x180014020 <.code3+0xf020>
113+
# DISASMX-NEXT: 18001401c: d65f03c0 ret
114+
# DISASMX-NEXT: 180014020: f0ffff70 adrp x16, 0x180003000 <.code1>
115+
# DISASMX-NEXT: 180014024: 91000210 add x16, x16, #0x0
116+
# DISASMX-NEXT: 180014028: d61f0200 br x16
117+
# DISASMX-EMPTY:
118+
# DISASMX-NEXT: Disassembly of section .code4:
119+
# DISASMX-EMPTY:
120+
# DISASMX-NEXT: 0000000180015000 <.code4>:
121+
# DISASMX-NEXT: 180015000: 36000040 tbz w0, #0x0, 0x180015008 <.code4+0x8>
122+
# DISASMX-NEXT: 180015004: d65f03c0 ret
123+
# DISASMX-NEXT: 180015008: f0ffff70 adrp x16, 0x180004000 <.code2>
124+
# DISASMX-NEXT: 18001500c: 91000210 add x16, x16, #0x0
125+
# DISASMX-NEXT: 180015010: d61f0200 br x16
126+
127+
# RUN: llvm-readobj --coff-load-config testx2.dll | FileCheck --check-prefix=LOADCFGX2 %s
128+
129+
# LOADCFGX2: CodeMap [
130+
# LOADCFGX2-NEXT: 0x3000 - 0x3014 ARM64EC
131+
# LOADCFGX2-NEXT: 0x4000 - 0x4014 ARM64
132+
# LOADCFGX2-NEXT: 0x5000 - 0x1402C ARM64EC
133+
# LOADCFGX2-NEXT: 0x15000 - 0x15014 ARM64
134+
# LOADCFGX2-NEXT: ]
135+
136+
137+
#--- funcs.s
138+
.globl main
139+
.globl func1
140+
.globl func2
141+
142+
.section .code1, "xr"
143+
main:
144+
tbz w0, #0, func1
145+
ret
146+
147+
.section .code3$a, "xr"
148+
.space 0x7000
149+
150+
.section .code3$b, "xr"
151+
func1:
152+
tbz w0, #0, func2
153+
ret
154+
.space 1
155+
156+
.section .code3$c, "xr"
157+
.space 0x8000
158+
159+
.section .code3$d, "xr"
160+
.align 2
161+
func2:
162+
tbz w0, #0, main
163+
ret
164+
165+
#--- space.s
166+
.section .code2$a, "xr"
167+
.space 0x100
168+
.section .code2$b, "xr"
169+
.space 0x100
170+
.section .code2$c, "xr"
171+
.space 0x100
172+
173+
#--- native-funcs.s
174+
.globl nmain
175+
.globl nfunc
176+
177+
.section .code2, "xr"
178+
nmain:
179+
tbz w0, #0, nfunc
180+
ret
181+
182+
.section .code4, "xr"
183+
.align 2
184+
nfunc:
185+
tbz w0, #0, nmain
186+
ret

0 commit comments

Comments
 (0)