Skip to content

Commit f661e69

Browse files
authored
[LLD][COFF] Add support for ARM64EC import call thunks with extended range (#109703)
The MSVC linker generates range extensions for these thunks when needed. This commit inlines the range extension into the thunk, making it both slightly more optimal and easier to implement in LLD.
1 parent 2803905 commit f661e69

File tree

4 files changed

+91
-4
lines changed

4 files changed

+91
-4
lines changed

lld/COFF/Chunks.cpp

+30-1
Original file line numberDiff line numberDiff line change
@@ -1100,6 +1100,13 @@ void CHPERedirectionChunk::writeTo(uint8_t *buf) const {
11001100
ImportThunkChunkARM64EC::ImportThunkChunkARM64EC(ImportFile *file)
11011101
: ImportThunkChunk(file->ctx, file->impSym), file(file) {}
11021102

1103+
size_t ImportThunkChunkARM64EC::getSize() const {
1104+
if (!extended)
1105+
return sizeof(importThunkARM64EC);
1106+
// The last instruction is replaced with an inline range extension thunk.
1107+
return sizeof(importThunkARM64EC) + sizeof(arm64Thunk) - sizeof(uint32_t);
1108+
}
1109+
11031110
void ImportThunkChunkARM64EC::writeTo(uint8_t *buf) const {
11041111
memcpy(buf, importThunkARM64EC, sizeof(importThunkARM64EC));
11051112
applyArm64Addr(buf, file->impSym->getRVA(), rva, 12);
@@ -1116,7 +1123,29 @@ void ImportThunkChunkARM64EC::writeTo(uint8_t *buf) const {
11161123
applyArm64Imm(buf + 12, exitThunkRVA & 0xfff, 0);
11171124

11181125
Defined *helper = cast<Defined>(file->ctx.config.arm64ECIcallHelper);
1119-
applyArm64Branch26(buf + 16, helper->getRVA() - rva - 16);
1126+
if (extended) {
1127+
// Replace last instruction with an inline range extension thunk.
1128+
memcpy(buf + 16, arm64Thunk, sizeof(arm64Thunk));
1129+
applyArm64Addr(buf + 16, helper->getRVA(), rva + 16, 12);
1130+
applyArm64Imm(buf + 20, helper->getRVA() & 0xfff, 0);
1131+
} else {
1132+
applyArm64Branch26(buf + 16, helper->getRVA() - rva - 16);
1133+
}
1134+
}
1135+
1136+
bool ImportThunkChunkARM64EC::verifyRanges() {
1137+
if (extended)
1138+
return true;
1139+
auto helper = cast<Defined>(file->ctx.config.arm64ECIcallHelper);
1140+
return isInt<28>(helper->getRVA() - rva - 16);
1141+
}
1142+
1143+
uint32_t ImportThunkChunkARM64EC::extendRanges() {
1144+
if (extended || verifyRanges())
1145+
return 0;
1146+
extended = true;
1147+
// The last instruction is replaced with an inline range extension thunk.
1148+
return sizeof(arm64Thunk) - sizeof(uint32_t);
11201149
}
11211150

11221151
} // namespace lld::coff

lld/COFF/Chunks.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,13 @@ class NonSectionChunk : public Chunk {
185185
// bytes, so this is used only for logging or debugging.
186186
virtual StringRef getDebugName() const { return ""; }
187187

188+
// Verify that chunk relocations are within their ranges.
189+
virtual bool verifyRanges() { return true; };
190+
191+
// If needed, extend the chunk to ensure all relocations are within the
192+
// allowed ranges. Return the additional space required for the extension.
193+
virtual uint32_t extendRanges() { return 0; };
194+
188195
static bool classof(const Chunk *c) { return c->kind() >= OtherKind; }
189196

190197
protected:
@@ -620,12 +627,15 @@ class ImportThunkChunkARM64 : public ImportThunkChunk {
620627
class ImportThunkChunkARM64EC : public ImportThunkChunk {
621628
public:
622629
explicit ImportThunkChunkARM64EC(ImportFile *file);
623-
size_t getSize() const override { return sizeof(importThunkARM64EC); };
630+
size_t getSize() const override;
624631
MachineTypes getMachine() const override { return ARM64EC; }
625632
void writeTo(uint8_t *buf) const override;
633+
bool verifyRanges() override;
634+
uint32_t extendRanges() override;
626635

627636
Defined *exitThunk;
628637
Defined *sym = nullptr;
638+
bool extended = false;
629639

630640
private:
631641
ImportFile *file;

lld/COFF/Writer.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -473,8 +473,14 @@ bool Writer::createThunks(OutputSection *os, int margin) {
473473
// elements into it.
474474
for (size_t i = 0; i != os->chunks.size(); ++i) {
475475
SectionChunk *sc = dyn_cast<SectionChunk>(os->chunks[i]);
476-
if (!sc)
476+
if (!sc) {
477+
auto chunk = cast<NonSectionChunk>(os->chunks[i]);
478+
if (uint32_t size = chunk->extendRanges()) {
479+
thunksSize += size;
480+
addressesChanged = true;
481+
}
477482
continue;
483+
}
478484
MachineTypes machine = sc->getMachine();
479485
size_t thunkInsertionSpot = i + 1;
480486

@@ -607,8 +613,11 @@ void Writer::createECCodeMap() {
607613
bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
608614
for (Chunk *c : chunks) {
609615
SectionChunk *sc = dyn_cast<SectionChunk>(c);
610-
if (!sc)
616+
if (!sc) {
617+
if (!cast<NonSectionChunk>(c)->verifyRanges())
618+
return false;
611619
continue;
620+
}
612621
MachineTypes machine = sc->getMachine();
613622

614623
ArrayRef<coff_relocation> relocs = sc->getRelocs();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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.lib
7+
8+
RUN: lld-link -machine:arm64ec -dll -noentry -out:out.dll loadconfig-arm64ec.obj test.obj test.lib
9+
10+
RUN: llvm-objdump -d out.dll | FileCheck --check-prefix=DISASM %s
11+
DISASM: 0000000180001000 <.text>:
12+
DISASM-NEXT: 180001000: 52800000 mov w0, #0x0 // =0
13+
DISASM-NEXT: 180001004: d65f03c0 ret
14+
DISASM-NEXT: ...
15+
DISASM-NEXT: 188001008: b000000b adrp x11, 0x188002000
16+
DISASM-NEXT: 18800100c: f940016b ldr x11, [x11]
17+
DISASM-NEXT: 188001010: f0fbffea adrp x10, 0x180000000
18+
DISASM-NEXT: 188001014: 9100014a add x10, x10, #0x0
19+
DISASM-NEXT: 188001018: 90fc0010 adrp x16, 0x180001000 <.text>
20+
DISASM-NEXT: 18800101c: 91000210 add x16, x16, #0x0
21+
DISASM-NEXT: 188001020: d61f0200 br x16
22+
23+
#--- test.s
24+
.text
25+
.globl __icall_helper_arm64ec
26+
.p2align 2, 0x0
27+
__icall_helper_arm64ec:
28+
mov w0, #0
29+
ret
30+
31+
.space 0x8000000
32+
33+
.data
34+
.rva __imp_func
35+
36+
#--- test.def
37+
NAME test.dll
38+
EXPORTS
39+
func

0 commit comments

Comments
 (0)