Skip to content

Commit 6252d72

Browse files
committed
[lld] Add support for EC code map.
1 parent 3594769 commit 6252d72

File tree

6 files changed

+125
-55
lines changed

6 files changed

+125
-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
@@ -2355,6 +2355,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
23552355
ctx.symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0);
23562356
ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0);
23572357

2358+
if (isArm64EC(config->machine)) {
2359+
ctx.symtab.addAbsolute("__hybrid_code_map", 0);
2360+
ctx.symtab.addAbsolute("__hybrid_code_map_count", 0);
2361+
}
2362+
23582363
if (config->pseudoRelocs) {
23592364
ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
23602365
ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);

lld/COFF/Writer.cpp

Lines changed: 66 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();
@@ -271,6 +273,7 @@ class Writer {
271273
std::map<PartialSectionKey, PartialSection *> partialSections;
272274
std::vector<char> strtab;
273275
std::vector<llvm::object::coff_symbol16> outputSymtab;
276+
std::vector<ECCodeMapEntry> codeMap;
274277
IdataContents idata;
275278
Chunk *importTableStart = nullptr;
276279
uint64_t importTableSize = 0;
@@ -528,6 +531,53 @@ bool Writer::createThunks(OutputSection *os, int margin) {
528531
return addressesChanged;
529532
}
530533

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

1130+
if (isArm64EC(config->machine))
1131+
createECChunks();
1132+
10801133
if (config->autoImport)
10811134
createRuntimePseudoRelocs();
10821135

@@ -1402,6 +1455,10 @@ void Writer::assignAddresses() {
14021455
llvm::TimeTraceScope timeScope("Assign addresses");
14031456
Configuration *config = &ctx.config;
14041457

1458+
// We need to create EC code map so that ECCodeMapChunk knows its size.
1459+
// We do it here to make sure that we account for range extension chunks.
1460+
createECCodeMap();
1461+
14051462
sizeOfHeaders = dosStubSize + sizeof(PEMagic) + sizeof(coff_file_header) +
14061463
sizeof(data_directory) * numberOfDataDirectory +
14071464
sizeof(coff_section) * ctx.outputSections.size();
@@ -1937,6 +1994,15 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
19371994
cast<DefinedAbsolute>(c)->setVA(tableChunk->getSize() / (hasFlag ? 5 : 4));
19381995
}
19391996

1997+
// Create CHPE metadata chunks.
1998+
void Writer::createECChunks() {
1999+
auto codeMapChunk = make<ECCodeMapChunk>(codeMap);
2000+
rdataSec->addChunk(codeMapChunk);
2001+
Symbol *codeMapSym = ctx.symtab.findUnderscore("__hybrid_code_map");
2002+
replaceSymbol<DefinedSynthetic>(codeMapSym, codeMapSym->getName(),
2003+
codeMapChunk);
2004+
}
2005+
19402006
// MinGW specific. Gather all relocations that are imported from a DLL even
19412007
// though the code didn't expect it to, produce the table that the runtime
19422008
// 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: 17 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,15 @@ RUN: split-file %s %t.dir && cd %t.dir
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
66
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows data-sec.s -o data-sec.obj
7+
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows empty-sec.s -o arm64ec-empty-sec.obj
78
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
9+
RUN: llvm-mc -filetype=obj -triple=x86_64-windows empty-sec.s -o x86_64-empty-sec.obj
1110
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
1211

1312
Link ARM64EC DLL and verify that the code is arranged as expected.
1413

1514
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
15+
RUN: loadconfig-arm64ec.obj -dll -noentry
1716

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

4746
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
47+
RUN: loadconfig-arm64ec.obj -dll -noentry
4948
RUN: llvm-readobj --coff-load-config test2.dll | FileCheck -check-prefix=CODEMAP %s
5049
RUN: llvm-objdump -d test2.dll | FileCheck -check-prefix=DISASM %s
5150

5251
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
52+
RUN: x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
53+
54+
Adding empty chunks does not affect code map ranges.
55+
56+
RUN: lld-link -out:test3.dll -machine:arm64ec x86_64-empty-sec.obj arm64ec-empty-sec.obj \
57+
RUN: arm64ec-func-sym.obj x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
58+
RUN: llvm-readobj --coff-load-config test3.dll | FileCheck -check-prefix=CODEMAP %s
5459

5560
Do the same with ARM64X target.
5661

@@ -88,7 +93,7 @@ DISASMX-NEXT: 180007005: c3 retq
8893
Test merged sections.
8994

9095
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
96+
RUN: loadconfig-arm64ec.obj -dll -noentry -merge:test=.text
9297

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

114119
Merging data sections into code sections causes data to be separated from the code when sorting chunks.
115120

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

119124
RUN: llvm-readobj --coff-load-config testdm.dll | FileCheck -check-prefix=CODEMAPDM %s
@@ -187,48 +192,7 @@ x86_64_func_sym2:
187192
.section .testdata, "rd"
188193
.xword 1
189194

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
195+
#--- empty-sec.s
196+
.section .empty1, "xr"
197+
.section .empty2, "xr"
198+
.section .empty3, "xr"

0 commit comments

Comments
 (0)