Skip to content

Commit 8c6d4b2

Browse files
committed
[LLD][COFF] Add basic ARM64X dynamic relocations support
This update modifies the machine field in the hybrid view to be AMD64, aligning it with expectations from ARM64EC modules. While this provides initial support, additional relocations will be necessary for full functionality. Many of these cases depend on implementing separate namespace support first.
1 parent f840fe7 commit 8c6d4b2

File tree

5 files changed

+243
-3
lines changed

5 files changed

+243
-3
lines changed

lld/COFF/COFFLinkerContext.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ class COFFLinkerContext : public CommonLinkerContext {
8888
Timer diskCommitTimer;
8989

9090
Configuration config;
91+
92+
DynamicRelocsChunk *dynamicRelocs = nullptr;
9193
};
9294

9395
} // namespace lld::coff

lld/COFF/Chunks.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
using namespace llvm;
2727
using namespace llvm::object;
28+
using namespace llvm::support;
2829
using namespace llvm::support::endian;
2930
using namespace llvm::COFF;
3031
using llvm::support::ulittle32_t;
@@ -1147,4 +1148,85 @@ uint32_t ImportThunkChunkARM64EC::extendRanges() {
11471148
return sizeof(arm64Thunk) - sizeof(uint32_t);
11481149
}
11491150

1151+
size_t Arm64XDynamicRelocEntry::getSize() const {
1152+
switch (type) {
1153+
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE:
1154+
return sizeof(uint16_t) + size; // A header and a payload.
1155+
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA:
1156+
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL:
1157+
llvm_unreachable("unsupported type");
1158+
}
1159+
}
1160+
1161+
void Arm64XDynamicRelocEntry::writeTo(uint8_t *buf) const {
1162+
auto out = reinterpret_cast<ulittle16_t *>(buf);
1163+
*out = (offset & 0xfff) | (type << 12);
1164+
1165+
switch (type) {
1166+
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE:
1167+
*out |= ((bit_width(size) - 1) << 14); // Encode the size.
1168+
switch (size) {
1169+
case 2:
1170+
out[1] = value;
1171+
break;
1172+
case 4:
1173+
*reinterpret_cast<ulittle32_t *>(out + 1) = value;
1174+
break;
1175+
case 8:
1176+
*reinterpret_cast<ulittle64_t *>(out + 1) = value;
1177+
break;
1178+
default:
1179+
llvm_unreachable("invalid size");
1180+
}
1181+
break;
1182+
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA:
1183+
case IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL:
1184+
llvm_unreachable("unsupported type");
1185+
}
1186+
}
1187+
1188+
void DynamicRelocsChunk::finalize() {
1189+
llvm::stable_sort(arm64xRelocs, [=](const Arm64XDynamicRelocEntry &a,
1190+
const Arm64XDynamicRelocEntry &b) {
1191+
return a.offset < b.offset;
1192+
});
1193+
1194+
size = sizeof(coff_dynamic_reloc_table) + sizeof(coff_dynamic_relocation64) +
1195+
sizeof(coff_base_reloc_block_header);
1196+
1197+
for (const Arm64XDynamicRelocEntry &entry : arm64xRelocs) {
1198+
assert(!(entry.offset & ~0xfff)); // Not yet supported.
1199+
size += entry.getSize();
1200+
}
1201+
1202+
size = alignTo(size, sizeof(uint32_t));
1203+
}
1204+
1205+
void DynamicRelocsChunk::writeTo(uint8_t *buf) const {
1206+
auto table = reinterpret_cast<coff_dynamic_reloc_table *>(buf);
1207+
table->Version = 1;
1208+
table->Size = sizeof(coff_dynamic_relocation64);
1209+
buf += sizeof(*table);
1210+
1211+
auto header = reinterpret_cast<coff_dynamic_relocation64 *>(buf);
1212+
header->Symbol = IMAGE_DYNAMIC_RELOCATION_ARM64X;
1213+
buf += sizeof(*header);
1214+
1215+
auto pageHeader = reinterpret_cast<coff_base_reloc_block_header *>(buf);
1216+
pageHeader->BlockSize = sizeof(*pageHeader);
1217+
size_t relocSize = sizeof(*pageHeader);
1218+
for (const Arm64XDynamicRelocEntry &entry : arm64xRelocs) {
1219+
entry.writeTo(buf + relocSize);
1220+
size_t entrySize = entry.getSize();
1221+
pageHeader->BlockSize += entrySize;
1222+
relocSize += entrySize;
1223+
}
1224+
pageHeader->BlockSize = alignTo(pageHeader->BlockSize, sizeof(uint32_t));
1225+
relocSize = alignTo(relocSize, sizeof(uint32_t));
1226+
1227+
header->BaseRelocSize = relocSize;
1228+
table->Size += relocSize;
1229+
assert(size == sizeof(*table) + sizeof(*header) + relocSize);
1230+
}
1231+
11501232
} // namespace lld::coff

lld/COFF/Chunks.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,43 @@ class ECExportThunkChunk : public NonSectionCodeChunk {
835835
Defined *target;
836836
};
837837

838+
// ARM64X entry for dynamic relocations.
839+
class Arm64XDynamicRelocEntry {
840+
public:
841+
Arm64XDynamicRelocEntry(llvm::COFF::Arm64XFixupType type, uint8_t size,
842+
uint32_t offset, uint64_t value)
843+
: offset(offset), value(value), type(type), size(size) {}
844+
845+
size_t getSize() const;
846+
void writeTo(uint8_t *buf) const;
847+
848+
uint32_t offset;
849+
uint64_t value;
850+
851+
private:
852+
llvm::COFF::Arm64XFixupType type;
853+
uint8_t size;
854+
};
855+
856+
// Dynamic relocation chunk containing ARM64X relocations for the hybrid image.
857+
class DynamicRelocsChunk : public NonSectionChunk {
858+
public:
859+
DynamicRelocsChunk() {}
860+
size_t getSize() const override { return size; }
861+
void writeTo(uint8_t *buf) const override;
862+
void finalize();
863+
864+
uint32_t add(llvm::COFF::Arm64XFixupType type, uint8_t size, uint32_t offset,
865+
uint64_t value) {
866+
arm64xRelocs.emplace_back(type, size, offset, value);
867+
return arm64xRelocs.size() - 1;
868+
}
869+
870+
private:
871+
std::vector<Arm64XDynamicRelocEntry> arm64xRelocs;
872+
size_t size;
873+
};
874+
838875
// MinGW specific, for the "automatic import of variables from DLLs" feature.
839876
// This provides the table of runtime pseudo relocations, for variable
840877
// references that turned out to need to be imported from a DLL even though

lld/COFF/Writer.cpp

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ class Writer {
272272
OutputSection *findSection(StringRef name);
273273
void addBaserels();
274274
void addBaserelBlocks(std::vector<Baserel> &v);
275+
void createDynamicRelocs();
275276

276277
uint32_t getSizeOfInitializedData();
277278

@@ -753,6 +754,8 @@ void Writer::run() {
753754
llvm::TimeTraceScope timeScope("Write PE");
754755
ScopedTimer t1(ctx.codeLayoutTimer);
755756

757+
if (ctx.config.machine == ARM64X)
758+
ctx.dynamicRelocs = make<DynamicRelocsChunk>();
756759
createImportTables();
757760
createSections();
758761
appendImportThunks();
@@ -763,6 +766,7 @@ void Writer::run() {
763766
mergeSections();
764767
sortECChunks();
765768
appendECImportTables();
769+
createDynamicRelocs();
766770
removeUnusedSections();
767771
finalizeAddresses();
768772
removeEmptySections();
@@ -1595,8 +1599,13 @@ void Writer::assignAddresses() {
15951599

15961600
for (OutputSection *sec : ctx.outputSections) {
15971601
llvm::TimeTraceScope timeScope("Section: ", sec->name);
1598-
if (sec == relocSec)
1602+
if (sec == relocSec) {
15991603
addBaserels();
1604+
if (ctx.dynamicRelocs) {
1605+
ctx.dynamicRelocs->finalize();
1606+
relocSec->addChunk(ctx.dynamicRelocs);
1607+
}
1608+
}
16001609
uint64_t rawSize = 0, virtualSize = 0;
16011610
sec->header.VirtualAddress = rva;
16021611

@@ -1797,9 +1806,12 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
17971806
exceptionTable.last->getSize() -
17981807
exceptionTable.first->getRVA();
17991808
}
1800-
if (relocSec->getVirtualSize()) {
1809+
size_t relocSize = relocSec->getVirtualSize();
1810+
if (ctx.dynamicRelocs)
1811+
relocSize -= ctx.dynamicRelocs->getSize();
1812+
if (relocSize) {
18011813
dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
1802-
dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize();
1814+
dir[BASE_RELOCATION_TABLE].Size = relocSize;
18031815
}
18041816
if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) {
18051817
if (Defined *b = dyn_cast<Defined>(sym)) {
@@ -2554,6 +2566,33 @@ void Writer::addBaserelBlocks(std::vector<Baserel> &v) {
25542566
relocSec->addChunk(make<BaserelChunk>(page, &v[i], &v[0] + j));
25552567
}
25562568

2569+
void Writer::createDynamicRelocs() {
2570+
if (!ctx.dynamicRelocs)
2571+
return;
2572+
2573+
const uint32_t coffHeaderOffset = dosStubSize + sizeof(PEMagic);
2574+
const uint32_t peHeaderOffset = coffHeaderOffset + sizeof(coff_file_header);
2575+
const uint32_t dataDirOffset = peHeaderOffset + sizeof(pe32plus_header);
2576+
2577+
// Adjust the Machine field in the COFF header to AMD64.
2578+
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint16_t),
2579+
coffHeaderOffset + offsetof(coff_file_header, Machine),
2580+
AMD64);
2581+
2582+
// Adjust the load config directory.
2583+
// FIXME: Use the hybrid load config value instead.
2584+
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
2585+
dataDirOffset +
2586+
LOAD_CONFIG_TABLE * sizeof(data_directory) +
2587+
offsetof(data_directory, RelativeVirtualAddress),
2588+
0);
2589+
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
2590+
dataDirOffset +
2591+
LOAD_CONFIG_TABLE * sizeof(data_directory) +
2592+
offsetof(data_directory, Size),
2593+
0);
2594+
}
2595+
25572596
PartialSection *Writer::createPartialSection(StringRef name,
25582597
uint32_t outChars) {
25592598
PartialSection *&pSec = partialSections[{name, outChars}];
@@ -2656,6 +2695,18 @@ template <typename T> void Writer::prepareLoadConfig(T *loadConfig) {
26562695
loadConfig->DependentLoadFlags = ctx.config.dependentLoadFlags;
26572696
}
26582697

2698+
if (ctx.dynamicRelocs) {
2699+
IF_CONTAINS(DynamicValueRelocTableSection) {
2700+
loadConfig->DynamicValueRelocTableSection = relocSec->sectionIndex;
2701+
loadConfig->DynamicValueRelocTableOffset =
2702+
ctx.dynamicRelocs->getRVA() - relocSec->getRVA();
2703+
}
2704+
else {
2705+
warn("'_load_config_used' structure too small to include dynamic "
2706+
"relocations");
2707+
}
2708+
}
2709+
26592710
if (ctx.config.guardCF == GuardCFLevel::Off)
26602711
return;
26612712
RETURN_IF_NOT_CONTAINS(GuardFlags)

lld/test/COFF/arm64x-loadconfig.s

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// REQUIRES: aarch64
2+
// RUN: split-file %s %t.dir && cd %t.dir
3+
4+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows test.s -o test.obj
5+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows loadconfig.s -o loadconfig.obj
6+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows loadconfig-short.s -o loadconfig-short.obj
7+
8+
// RUN: lld-link -machine:arm64x -out:out.dll -dll -noentry loadconfig.obj test.obj
9+
10+
// RUN: llvm-readobj --coff-load-config out.dll | FileCheck -check-prefix=DYNRELOCS %s
11+
// DYNRELOCS: DynamicValueRelocTableOffset: 0xC
12+
// DYNRELOCS-NEXT: DynamicValueRelocTableSection: 4
13+
// DYNRELOCS: DynamicRelocations [
14+
// DYNRELOCS-NEXT: Version: 0x1
15+
// DYNRELOCS-NEXT: Arm64X [
16+
// DYNRELOCS-NEXT: Entry [
17+
// DYNRELOCS-NEXT: RVA: 0x7C
18+
// DYNRELOCS-NEXT: Type: VALUE
19+
// DYNRELOCS-NEXT: Size: 0x2
20+
// DYNRELOCS-NEXT: Value: 0x8664
21+
// DYNRELOCS-NEXT: ]
22+
// DYNRELOCS-NEXT: Entry [
23+
// DYNRELOCS-NEXT: RVA: 0x150
24+
// DYNRELOCS-NEXT: Type: VALUE
25+
// DYNRELOCS-NEXT: Size: 0x4
26+
// DYNRELOCS-NEXT: Value: 0x0
27+
// DYNRELOCS-NEXT: ]
28+
// DYNRELOCS-NEXT: Entry [
29+
// DYNRELOCS-NEXT: RVA: 0x154
30+
// DYNRELOCS-NEXT: Type: VALUE
31+
// DYNRELOCS-NEXT: Size: 0x4
32+
// DYNRELOCS-NEXT: Value: 0x0
33+
// DYNRELOCS-NEXT: ]
34+
// DYNRELOCS-NEXT: ]
35+
// DYNRELOCS-NEXT: ]
36+
37+
// RUN: llvm-readobj --headers out.dll | FileCheck -check-prefix=HEADERS %s
38+
// HEADERS: BaseRelocationTableRVA: 0x4000
39+
// HEADERS-NEXT: BaseRelocationTableSize: 0xC
40+
// HEADERS: LoadConfigTableRVA: 0x1000
41+
// HEADERS-NEXT: LoadConfigTableSize: 0x140
42+
// HEADERS: Name: .reloc (2E 72 65 6C 6F 63 00 00)
43+
// HEADERS-NEXT: VirtualSize: 0x38
44+
45+
// RUN: lld-link -machine:arm64x -out:out-short.dll -dll -noentry loadconfig-short.obj 2>&1 | FileCheck --check-prefix=WARN-RELOC-SIZE %s
46+
// WARN-RELOC-SIZE: lld-link: warning: '_load_config_used' structure too small to include dynamic relocations
47+
48+
#--- test.s
49+
.data
50+
sym:
51+
// Emit a basereloc to make the loadconfig test more meaningful.
52+
.xword sym
53+
54+
#--- loadconfig.s
55+
.section .rdata,"dr"
56+
.globl _load_config_used
57+
.p2align 3, 0
58+
_load_config_used:
59+
.word 0x140
60+
.fill 0x13c,1,0
61+
62+
#--- loadconfig-short.s
63+
.section .rdata,"dr"
64+
.globl _load_config_used
65+
.p2align 3, 0
66+
_load_config_used:
67+
.word 0xe4
68+
.fill 0xe0,1,0

0 commit comments

Comments
 (0)