Skip to content

Commit 71bbafb

Browse files
authored
[LLD][COFF] Add basic ARM64X dynamic relocations support (#118035)
This 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. Move clearing of the .reloc section from addBaserels to assignAddresses to ensure it is always cleared, regardless of the relocatable configuration. This change also clarifies the reasoning for adding the dynamic relocations chunk in that location.
1 parent 59720dc commit 71bbafb

File tree

5 files changed

+244
-4
lines changed

5 files changed

+244
-4
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: 78 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,81 @@ 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+
for (const Arm64XDynamicRelocEntry &entry : arm64xRelocs) {
1218+
entry.writeTo(buf + pageHeader->BlockSize);
1219+
pageHeader->BlockSize += entry.getSize();
1220+
}
1221+
pageHeader->BlockSize = alignTo(pageHeader->BlockSize, sizeof(uint32_t));
1222+
1223+
header->BaseRelocSize = pageHeader->BlockSize;
1224+
table->Size += header->BaseRelocSize;
1225+
assert(size == sizeof(*table) + sizeof(*header) + header->BaseRelocSize);
1226+
}
1227+
11501228
} // namespace lld::coff

lld/COFF/Chunks.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,42 @@ 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+
void add(llvm::COFF::Arm64XFixupType type, uint8_t size, uint32_t offset,
865+
uint64_t value) {
866+
arm64xRelocs.emplace_back(type, size, offset, value);
867+
}
868+
869+
private:
870+
std::vector<Arm64XDynamicRelocEntry> arm64xRelocs;
871+
size_t size;
872+
};
873+
838874
// MinGW specific, for the "automatic import of variables from DLLs" feature.
839875
// This provides the table of runtime pseudo relocations, for variable
840876
// references that turned out to need to be imported from a DLL even though

lld/COFF/Writer.cpp

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ static_assert(sizeof(dosProgram) % 8 == 0,
7979

8080
static const int dosStubSize = sizeof(dos_header) + sizeof(dosProgram);
8181
static_assert(dosStubSize % 8 == 0, "DOSStub size must be multiple of 8");
82+
static const uint32_t coffHeaderOffset = dosStubSize + sizeof(PEMagic);
83+
static const uint32_t peHeaderOffset =
84+
coffHeaderOffset + sizeof(coff_file_header);
85+
static const uint32_t dataDirOffset64 =
86+
peHeaderOffset + sizeof(pe32plus_header);
8287

8388
static const int numberOfDataDirectory = 16;
8489

@@ -272,6 +277,7 @@ class Writer {
272277
OutputSection *findSection(StringRef name);
273278
void addBaserels();
274279
void addBaserelBlocks(std::vector<Baserel> &v);
280+
void createDynamicRelocs();
275281

276282
uint32_t getSizeOfInitializedData();
277283

@@ -754,6 +760,8 @@ void Writer::run() {
754760
llvm::TimeTraceScope timeScope("Write PE");
755761
ScopedTimer t1(ctx.codeLayoutTimer);
756762

763+
if (ctx.config.machine == ARM64X)
764+
ctx.dynamicRelocs = make<DynamicRelocsChunk>();
757765
createImportTables();
758766
createSections();
759767
appendImportThunks();
@@ -764,6 +772,7 @@ void Writer::run() {
764772
mergeSections();
765773
sortECChunks();
766774
appendECImportTables();
775+
createDynamicRelocs();
767776
removeUnusedSections();
768777
finalizeAddresses();
769778
removeEmptySections();
@@ -1597,8 +1606,14 @@ void Writer::assignAddresses() {
15971606

15981607
for (OutputSection *sec : ctx.outputSections) {
15991608
llvm::TimeTraceScope timeScope("Section: ", sec->name);
1600-
if (sec == relocSec)
1609+
if (sec == relocSec) {
1610+
sec->chunks.clear();
16011611
addBaserels();
1612+
if (ctx.dynamicRelocs) {
1613+
ctx.dynamicRelocs->finalize();
1614+
relocSec->addChunk(ctx.dynamicRelocs);
1615+
}
1616+
}
16021617
uint64_t rawSize = 0, virtualSize = 0;
16031618
sec->header.VirtualAddress = rva;
16041619

@@ -1673,6 +1688,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
16731688
buf += sizeof(PEMagic);
16741689

16751690
// Write COFF header
1691+
assert(coffHeaderOffset == buf - buffer->getBufferStart());
16761692
auto *coff = reinterpret_cast<coff_file_header *>(buf);
16771693
buf += sizeof(*coff);
16781694
switch (config->machine) {
@@ -1705,6 +1721,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
17051721
sizeof(PEHeaderTy) + sizeof(data_directory) * numberOfDataDirectory;
17061722

17071723
// Write PE header
1724+
assert(peHeaderOffset == buf - buffer->getBufferStart());
17081725
auto *pe = reinterpret_cast<PEHeaderTy *>(buf);
17091726
buf += sizeof(*pe);
17101727
pe->Magic = config->is64() ? PE32Header::PE32_PLUS : PE32Header::PE32;
@@ -1770,6 +1787,8 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
17701787
pe->SizeOfInitializedData = getSizeOfInitializedData();
17711788

17721789
// Write data directory
1790+
assert(!ctx.config.is64() ||
1791+
dataDirOffset64 == buf - buffer->getBufferStart());
17731792
auto *dir = reinterpret_cast<data_directory *>(buf);
17741793
buf += sizeof(*dir) * numberOfDataDirectory;
17751794
if (edataStart) {
@@ -1799,9 +1818,12 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
17991818
exceptionTable.last->getSize() -
18001819
exceptionTable.first->getRVA();
18011820
}
1802-
if (relocSec->getVirtualSize()) {
1821+
size_t relocSize = relocSec->getVirtualSize();
1822+
if (ctx.dynamicRelocs)
1823+
relocSize -= ctx.dynamicRelocs->getSize();
1824+
if (relocSize) {
18031825
dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
1804-
dir[BASE_RELOCATION_TABLE].Size = relocSec->getVirtualSize();
1826+
dir[BASE_RELOCATION_TABLE].Size = relocSize;
18051827
}
18061828
if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) {
18071829
if (Defined *b = dyn_cast<Defined>(sym)) {
@@ -2523,7 +2545,6 @@ uint32_t Writer::getSizeOfInitializedData() {
25232545
void Writer::addBaserels() {
25242546
if (!ctx.config.relocatable)
25252547
return;
2526-
relocSec->chunks.clear();
25272548
std::vector<Baserel> v;
25282549
for (OutputSection *sec : ctx.outputSections) {
25292550
if (sec->header.Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
@@ -2557,6 +2578,29 @@ void Writer::addBaserelBlocks(std::vector<Baserel> &v) {
25572578
relocSec->addChunk(make<BaserelChunk>(page, &v[i], &v[0] + j));
25582579
}
25592580

2581+
void Writer::createDynamicRelocs() {
2582+
if (!ctx.dynamicRelocs)
2583+
return;
2584+
2585+
// Adjust the Machine field in the COFF header to AMD64.
2586+
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint16_t),
2587+
coffHeaderOffset + offsetof(coff_file_header, Machine),
2588+
AMD64);
2589+
2590+
// Clear the load config directory.
2591+
// FIXME: Use the hybrid load config value instead.
2592+
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
2593+
dataDirOffset64 +
2594+
LOAD_CONFIG_TABLE * sizeof(data_directory) +
2595+
offsetof(data_directory, RelativeVirtualAddress),
2596+
0);
2597+
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
2598+
dataDirOffset64 +
2599+
LOAD_CONFIG_TABLE * sizeof(data_directory) +
2600+
offsetof(data_directory, Size),
2601+
0);
2602+
}
2603+
25602604
PartialSection *Writer::createPartialSection(StringRef name,
25612605
uint32_t outChars) {
25622606
PartialSection *&pSec = partialSections[{name, outChars}];
@@ -2660,6 +2704,18 @@ template <typename T> void Writer::prepareLoadConfig(T *loadConfig) {
26602704
loadConfig->DependentLoadFlags = ctx.config.dependentLoadFlags;
26612705
}
26622706

2707+
if (ctx.dynamicRelocs) {
2708+
IF_CONTAINS(DynamicValueRelocTableSection) {
2709+
loadConfig->DynamicValueRelocTableSection = relocSec->sectionIndex;
2710+
loadConfig->DynamicValueRelocTableOffset =
2711+
ctx.dynamicRelocs->getRVA() - relocSec->getRVA();
2712+
}
2713+
else {
2714+
warn("'_load_config_used' structure too small to include dynamic "
2715+
"relocations");
2716+
}
2717+
}
2718+
26632719
if (ctx.config.guardCF == GuardCFLevel::Off)
26642720
return;
26652721
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)