Skip to content

Commit fe2bd12

Browse files
authored
[lld] Add support for EC code map. (#69101)
1 parent 0b2c3c6 commit fe2bd12

File tree

6 files changed

+182
-55
lines changed

6 files changed

+182
-55
lines changed

lld/COFF/Chunks.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -896,6 +896,20 @@ void RVAFlagTableChunk::writeTo(uint8_t *buf) const {
896896
"RVA tables should be de-duplicated");
897897
}
898898

899+
size_t ECCodeMapChunk::getSize() const {
900+
return map.size() * sizeof(chpe_range_entry);
901+
}
902+
903+
void ECCodeMapChunk::writeTo(uint8_t *buf) const {
904+
auto table = reinterpret_cast<chpe_range_entry *>(buf);
905+
for (uint32_t i = 0; i < map.size(); i++) {
906+
const ECCodeMapEntry &entry = map[i];
907+
uint32_t start = entry.first->getRVA();
908+
table[i].StartOffset = start | entry.type;
909+
table[i].Length = entry.last->getRVA() + entry.last->getSize() - start;
910+
}
911+
}
912+
899913
// MinGW specific, for the "automatic import of variables from DLLs" feature.
900914
size_t PseudoRelocTableChunk::getSize() const {
901915
if (relocs.empty())

lld/COFF/Chunks.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,6 +703,27 @@ class EmptyChunk : public NonSectionChunk {
703703
void writeTo(uint8_t *buf) const override {}
704704
};
705705

706+
class ECCodeMapEntry {
707+
public:
708+
ECCodeMapEntry(Chunk *first, Chunk *last, chpe_range_type type)
709+
: first(first), last(last), type(type) {}
710+
Chunk *first;
711+
Chunk *last;
712+
chpe_range_type type;
713+
};
714+
715+
// This is a chunk containing CHPE code map on EC targets. It's a table
716+
// of address ranges and their types.
717+
class ECCodeMapChunk : public NonSectionChunk {
718+
public:
719+
ECCodeMapChunk(std::vector<ECCodeMapEntry> &map) : map(map) {}
720+
size_t getSize() const override;
721+
void writeTo(uint8_t *buf) const override;
722+
723+
private:
724+
std::vector<ECCodeMapEntry> &map;
725+
};
726+
706727
// MinGW specific, for the "automatic import of variables from DLLs" feature.
707728
// This provides the table of runtime pseudo relocations, for variable
708729
// references that turned out to need to be imported from a DLL even though

lld/COFF/Driver.cpp

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

2363+
if (isArm64EC(config->machine)) {
2364+
ctx.symtab.addAbsolute("__hybrid_code_map", 0);
2365+
ctx.symtab.addAbsolute("__hybrid_code_map_count", 0);
2366+
}
2367+
23632368
if (config->pseudoRelocs) {
23642369
ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
23652370
ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);

lld/COFF/Writer.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ class Writer {
221221
uint16_t type, int margin);
222222
bool createThunks(OutputSection *os, int margin);
223223
bool verifyRanges(const std::vector<Chunk *> chunks);
224+
void createECCodeMap();
224225
void finalizeAddresses();
225226
void removeEmptySections();
226227
void assignOutputSectionIndices();
@@ -229,6 +230,7 @@ class Writer {
229230
template <typename PEHeaderTy> void writeHeader();
230231
void createSEHTable();
231232
void createRuntimePseudoRelocs();
233+
void createECChunks();
232234
void insertCtorDtorSymbols();
233235
void markSymbolsWithRelocations(ObjFile *file, SymbolRVASet &usedSymbols);
234236
void createGuardCFTables();
@@ -272,6 +274,7 @@ class Writer {
272274
std::map<PartialSectionKey, PartialSection *> partialSections;
273275
std::vector<char> strtab;
274276
std::vector<llvm::object::coff_symbol16> outputSymtab;
277+
std::vector<ECCodeMapEntry> codeMap;
275278
IdataContents idata;
276279
Chunk *importTableStart = nullptr;
277280
uint64_t importTableSize = 0;
@@ -537,6 +540,48 @@ bool Writer::createThunks(OutputSection *os, int margin) {
537540
return addressesChanged;
538541
}
539542

543+
// Create a code map for CHPE metadata.
544+
void Writer::createECCodeMap() {
545+
if (!isArm64EC(ctx.config.machine))
546+
return;
547+
548+
// Clear the map in case we were're recomputing the map after adding
549+
// a range extension thunk.
550+
codeMap.clear();
551+
552+
std::optional<chpe_range_type> lastType;
553+
Chunk *first, *last;
554+
555+
auto closeRange = [&]() {
556+
if (lastType) {
557+
codeMap.push_back({first, last, *lastType});
558+
lastType.reset();
559+
}
560+
};
561+
562+
for (OutputSection *sec : ctx.outputSections) {
563+
for (Chunk *c : sec->chunks) {
564+
// Skip empty section chunks. MS link.exe does not seem to do that and
565+
// generates empty code ranges in some cases.
566+
if (isa<SectionChunk>(c) && !c->getSize())
567+
continue;
568+
569+
std::optional<chpe_range_type> chunkType = c->getArm64ECRangeType();
570+
if (chunkType != lastType) {
571+
closeRange();
572+
first = c;
573+
lastType = chunkType;
574+
}
575+
last = c;
576+
}
577+
}
578+
579+
closeRange();
580+
581+
Symbol *tableCountSym = ctx.symtab.findUnderscore("__hybrid_code_map_count");
582+
cast<DefinedAbsolute>(tableCountSym)->setVA(codeMap.size());
583+
}
584+
540585
// Verify that all relocations are in range, with no extra margin requirements.
541586
bool Writer::verifyRanges(const std::vector<Chunk *> chunks) {
542587
for (Chunk *c : chunks) {
@@ -1086,6 +1131,9 @@ void Writer::createMiscChunks() {
10861131
if (config->guardCF != GuardCFLevel::Off)
10871132
createGuardCFTables();
10881133

1134+
if (isArm64EC(config->machine))
1135+
createECChunks();
1136+
10891137
if (config->autoImport)
10901138
createRuntimePseudoRelocs();
10911139

@@ -1411,6 +1459,10 @@ void Writer::assignAddresses() {
14111459
llvm::TimeTraceScope timeScope("Assign addresses");
14121460
Configuration *config = &ctx.config;
14131461

1462+
// We need to create EC code map so that ECCodeMapChunk knows its size.
1463+
// We do it here to make sure that we account for range extension chunks.
1464+
createECCodeMap();
1465+
14141466
sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
14151467
sizeof(data_directory) * numberOfDataDirectory +
14161468
sizeof(coff_section) * ctx.outputSections.size();
@@ -1946,6 +1998,15 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
19461998
cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4));
19471999
}
19482000

2001+
// Create CHPE metadata chunks.
2002+
void Writer::createECChunks() {
2003+
auto codeMapChunk = make<ECCodeMapChunk>(codeMap);
2004+
rdataSec->addChunk(codeMapChunk);
2005+
Symbol *codeMapSym = ctx.symtab.findUnderscore("__hybrid_code_map");
2006+
replaceSymbol<DefinedSynthetic>(codeMapSym, codeMapSym->getName(),
2007+
codeMapChunk);
2008+
}
2009+
19492010
// MinGW specific. Gather all relocations that are imported from a DLL even
19502011
// though the code didn't expect it to, produce the table that the runtime
19512012
// uses for fixing them up, and provide the synthetic symbols that the

lld/test/COFF/Inputs/loadconfig-arm64ec.s

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ __os_arm64x_helper8:
6464
.p2align 3, 0
6565
__chpe_metadata:
6666
.word 1
67-
.rva code_map
68-
.word code_map_count
67+
.rva __hybrid_code_map
68+
.word __hybrid_code_map_count
6969
.word 0 // __x64_code_ranges_to_entry_points
7070
.word 0 //__arm64x_redirection_metadata
7171
.rva __os_arm64x_dispatch_call_no_redirect

lld/test/COFF/arm64ec-codemap.test

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ RUN: split-file %s %t.dir && cd %t.dir
33

44
RUN: llvm-mc -filetype=obj -triple=arm64-windows arm64-func-sym.s -o arm64-func-sym.obj
55
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func-sym.s -o arm64ec-func-sym.obj
6+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func-sym2.s -o arm64ec-func-sym2.obj
67
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows data-sec.s -o data-sec.obj
8+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows empty-sec.s -o arm64ec-empty-sec.obj
79
RUN: llvm-mc -filetype=obj -triple=x86_64-windows x86_64-func-sym.s -o x86_64-func-sym.obj
8-
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows codemap.s -o codemap.obj
9-
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows codemap2.s -o codemap2.obj
10-
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows codemap3.s -o codemap3.obj
10+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows empty-sec.s -o x86_64-empty-sec.obj
1111
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
1212

1313
Link ARM64EC DLL and verify that the code is arranged as expected.
1414

1515
RUN: lld-link -out:test.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj \
16-
RUN: codemap.obj loadconfig-arm64ec.obj -dll -noentry
16+
RUN: loadconfig-arm64ec.obj -dll -noentry
1717

1818
RUN: llvm-readobj --coff-load-config test.dll | FileCheck -check-prefix=CODEMAP %s
1919
CODEMAP: CodeMap [
@@ -45,12 +45,18 @@ DISASM-NEXT: 180006005: c3 retq
4545
Order of arguments doesn't matter in this case, chunks are sorted by target type anyway.
4646

4747
RUN: lld-link -out:test2.dll -machine:arm64ec x86_64-func-sym.obj arm64ec-func-sym.obj \
48-
RUN: codemap.obj loadconfig-arm64ec.obj -dll -noentry
48+
RUN: loadconfig-arm64ec.obj -dll -noentry
4949
RUN: llvm-readobj --coff-load-config test2.dll | FileCheck -check-prefix=CODEMAP %s
5050
RUN: llvm-objdump -d test2.dll | FileCheck -check-prefix=DISASM %s
5151

5252
RUN: lld-link -out:testx.dll -machine:arm64x arm64-func-sym.obj arm64ec-func-sym.obj \
53-
RUN: x86_64-func-sym.obj codemap2.obj loadconfig-arm64ec.obj -dll -noentry
53+
RUN: x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
54+
55+
Adding empty chunks does not affect code map ranges.
56+
57+
RUN: lld-link -out:test3.dll -machine:arm64ec x86_64-empty-sec.obj arm64ec-empty-sec.obj \
58+
RUN: arm64ec-func-sym.obj x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
59+
RUN: llvm-readobj --coff-load-config test3.dll | FileCheck -check-prefix=CODEMAP %s
5460

5561
Do the same with ARM64X target.
5662

@@ -88,7 +94,7 @@ DISASMX-NEXT: 180007005: c3 retq
8894
Test merged sections.
8995

9096
RUN: lld-link -out:testm.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj \
91-
RUN: codemap3.obj loadconfig-arm64ec.obj -dll -noentry -merge:test=.text
97+
RUN: loadconfig-arm64ec.obj -dll -noentry -merge:test=.text
9298

9399
RUN: llvm-readobj --coff-load-config testm.dll | FileCheck -check-prefix=CODEMAPM %s
94100
CODEMAPM: CodeMap [
@@ -113,7 +119,7 @@ DISASMM-NEXT: 18000200d: c3 retq
113119

114120
Merging data sections into code sections causes data to be separated from the code when sorting chunks.
115121

116-
RUN: lld-link -out:testdm.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj codemap.obj \
122+
RUN: lld-link -out:testdm.dll -machine:arm64ec arm64ec-func-sym.obj x86_64-func-sym.obj \
117123
RUN: data-sec.obj loadconfig-arm64ec.obj -dll -noentry -merge:.testdata=.text -merge:.rdata=test
118124

119125
RUN: llvm-readobj --coff-load-config testdm.dll | FileCheck -check-prefix=CODEMAPDM %s
@@ -145,6 +151,59 @@ DISASMDM-NEXT: ...
145151
DISASMDM-NEXT: 180007000: b8 06 00 00 00 movl $0x6, %eax
146152
DISASMDM-NEXT: 180007005: c3 retq
147153

154+
Merging a code section into data section produces a valid code map.
155+
156+
RUN: lld-link -out:testcm.dll -machine:arm64ec x86_64-func-sym.obj data-sec.obj \
157+
RUN: loadconfig-arm64ec.obj -dll -noentry -merge:test=.testdata -merge:.text=.testdata
158+
159+
RUN: llvm-readobj --coff-load-config testcm.dll | FileCheck -check-prefix=CODEMAPCM %s
160+
CODEMAPCM: CodeMap [
161+
CODEMAPCM-NEXT: 0x3008 - 0x3016 X64
162+
CODEMAPCM-NEXT: ]
163+
164+
RUN: llvm-objdump -d testcm.dll | FileCheck -check-prefix=DISASMCM %s
165+
DISASMCM: Disassembly of section .testdat:
166+
DISASMCM-EMPTY:
167+
DISASMCM-NEXT: 0000000180003000 <.testdat>:
168+
DISASMCM-NEXT: 180003000: 00000001 udf #0x1
169+
DISASMCM-NEXT: 180003004: 00000000 udf #0x0
170+
DISASMCM-NEXT: 180003008: b8 03 00 00 00 movl $0x3, %eax
171+
DISASMCM-NEXT: 18000300d: c3 retq
172+
DISASMCM-NEXT: 18000300e: 00 00 addb %al, (%rax)
173+
DISASMCM-NEXT: 180003010: b8 06 00 00 00 movl $0x6, %eax
174+
DISASMCM-NEXT: 180003015: c3
175+
176+
Test code map range entry spanning over multiple sections.
177+
178+
RUN: lld-link -out:testms.dll -machine:arm64ec arm64ec-func-sym.obj arm64ec-func-sym2.obj \
179+
RUN: loadconfig-arm64ec.obj -dll -noentry
180+
181+
RUN: llvm-readobj --coff-load-config testms.dll | FileCheck -check-prefix=CODEMAPMS %s
182+
CODEMAPMS: CodeMap [
183+
CODEMAPMS-NEXT: 0x1000 - 0x1008 ARM64EC
184+
CODEMAPMS-NEXT: 0x4000 - 0x5008 ARM64EC
185+
CODEMAPMS-NEXT: ]
186+
187+
RUN: llvm-objdump -d testms.dll | FileCheck -check-prefix=DISASMMS %s
188+
DISASMMS: Disassembly of section .text:
189+
DISASMMS-EMPTY:
190+
DISASMMS-NEXT: 0000000180001000 <.text>:
191+
DISASMMS-NEXT: 180001000: 52800040 mov w0, #0x2 // =2
192+
DISASMMS-NEXT: 180001004: d65f03c0 ret
193+
DISASMMS-EMPTY:
194+
DISASMMS-NEXT: Disassembly of section test:
195+
DISASMMS-EMPTY:
196+
DISASMMS-NEXT: 0000000180004000 <test>:
197+
DISASMMS-NEXT: 180004000: 528000a0 mov w0, #0x5 // =5
198+
DISASMMS-NEXT: 180004004: d65f03c0 ret
199+
DISASMMS-EMPTY:
200+
DISASMMS-NEXT: Disassembly of section test2:
201+
DISASMMS-EMPTY:
202+
DISASMMS-NEXT: 0000000180005000 <test2>:
203+
DISASMMS-NEXT: 180005000: 528000a0 mov w0, #0x5 // =5
204+
DISASMMS-NEXT: 180005004: d65f03c0 ret
205+
206+
148207
#--- arm64-func-sym.s
149208
.text
150209
.globl arm64_func_sym
@@ -168,6 +227,14 @@ arm64ec_func_sym2:
168227
mov w0, #5
169228
ret
170229

230+
#--- arm64ec-func-sym2.s
231+
.section test2, "xr"
232+
.globl arm64ec_func_sym3
233+
.p2align 2, 0x0
234+
arm64ec_func_sym3:
235+
mov w0, #5
236+
ret
237+
171238
#--- x86_64-func-sym.s
172239
.text
173240
.globl x86_64_func_sym
@@ -187,48 +254,7 @@ x86_64_func_sym2:
187254
.section .testdata, "rd"
188255
.xword 1
189256

190-
#--- codemap.s
191-
.section .rdata,"dr"
192-
.globl code_map
193-
code_map:
194-
.rva arm64ec_func_sym + 1
195-
.word 8
196-
.rva x86_64_func_sym + 2
197-
.word 6
198-
.rva arm64ec_func_sym2 + 1
199-
.word 8
200-
.rva x86_64_func_sym2 + 2
201-
.word 6
202-
203-
.globl code_map_count
204-
code_map_count = 4
205-
206-
#--- codemap2.s
207-
.section .rdata,"dr"
208-
.globl code_map
209-
code_map:
210-
.rva arm64_func_sym
211-
.word 8
212-
.rva arm64ec_func_sym + 1
213-
.word 8
214-
.rva x86_64_func_sym + 2
215-
.word 6
216-
.rva arm64ec_func_sym2 + 1
217-
.word 8
218-
.rva x86_64_func_sym2 + 2
219-
.word 6
220-
221-
.globl code_map_count
222-
code_map_count = 5
223-
224-
#--- codemap3.s
225-
.section .rdata,"dr"
226-
.globl code_map
227-
code_map:
228-
.rva arm64ec_func_sym + 1
229-
.word 16
230-
.rva x86_64_func_sym + 2
231-
.word 14
232-
233-
.globl code_map_count
234-
code_map_count = 2
257+
#--- empty-sec.s
258+
.section .empty1, "xr"
259+
.section .empty2, "xr"
260+
.section .empty3, "xr"

0 commit comments

Comments
 (0)