Skip to content

Commit 16ef239

Browse files
committed
[LLD][COFF] Introduce hybrid symbol table for EC input files on ARM64X (#119294)
1 parent 0b91d77 commit 16ef239

File tree

9 files changed

+143
-63
lines changed

9 files changed

+143
-63
lines changed

lld/COFF/COFFLinkerContext.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,27 @@ class COFFLinkerContext : public CommonLinkerContext {
3232
SymbolTable symtab;
3333
COFFOptTable optTable;
3434

35+
// A hybrid ARM64EC symbol table on ARM64X target.
36+
std::optional<SymbolTable> hybridSymtab;
37+
38+
// Pointer to the ARM64EC symbol table: either symtab for an ARM64EC target or
39+
// hybridSymtab for an ARM64X target.
40+
SymbolTable *symtabEC = nullptr;
41+
42+
// Returns the appropriate symbol table for the specified machine type.
43+
SymbolTable &getSymtab(llvm::COFF::MachineTypes machine) {
44+
if (hybridSymtab && (machine == ARM64EC || machine == AMD64))
45+
return *hybridSymtab;
46+
return symtab;
47+
}
48+
49+
// Invoke the specified callback for each symbol table.
50+
void forEachSymtab(std::function<void(SymbolTable &symtab)> f) {
51+
f(symtab);
52+
if (hybridSymtab)
53+
f(*hybridSymtab);
54+
}
55+
3556
std::vector<ObjFile *> objFileInstances;
3657
std::map<std::string, PDBInputFile *> pdbInputFileInstances;
3758
std::vector<ImportFile *> importFileInstances;

lld/COFF/Driver.cpp

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,17 @@ void LinkerDriver::setMachine(MachineTypes machine) {
595595
assert(machine != IMAGE_FILE_MACHINE_UNKNOWN);
596596

597597
ctx.config.machine = machine;
598-
ctx.symtab.machine = machine;
598+
599+
if (machine != ARM64X) {
600+
ctx.symtab.machine = machine;
601+
if (machine == ARM64EC)
602+
ctx.symtabEC = &ctx.symtab;
603+
} else {
604+
ctx.symtab.machine = ARM64;
605+
ctx.hybridSymtab.emplace(ctx, ARM64EC);
606+
ctx.symtabEC = &*ctx.hybridSymtab;
607+
}
608+
599609
addWinSysRootLibSearchPaths();
600610
}
601611

@@ -2518,54 +2528,56 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
25182528
if (config->imageBase == uint64_t(-1))
25192529
config->imageBase = getDefaultImageBase();
25202530

2521-
ctx.symtab.addSynthetic(mangle("__ImageBase"), nullptr);
2522-
if (config->machine == I386) {
2523-
ctx.symtab.addAbsolute("___safe_se_handler_table", 0);
2524-
ctx.symtab.addAbsolute("___safe_se_handler_count", 0);
2525-
}
2526-
2527-
ctx.symtab.addAbsolute(mangle("__guard_fids_count"), 0);
2528-
ctx.symtab.addAbsolute(mangle("__guard_fids_table"), 0);
2529-
ctx.symtab.addAbsolute(mangle("__guard_flags"), 0);
2530-
ctx.symtab.addAbsolute(mangle("__guard_iat_count"), 0);
2531-
ctx.symtab.addAbsolute(mangle("__guard_iat_table"), 0);
2532-
ctx.symtab.addAbsolute(mangle("__guard_longjmp_count"), 0);
2533-
ctx.symtab.addAbsolute(mangle("__guard_longjmp_table"), 0);
2534-
// Needed for MSVC 2017 15.5 CRT.
2535-
ctx.symtab.addAbsolute(mangle("__enclave_config"), 0);
2536-
// Needed for MSVC 2019 16.8 CRT.
2537-
ctx.symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0);
2538-
ctx.symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0);
2539-
2540-
if (isArm64EC(config->machine)) {
2541-
ctx.symtab.addAbsolute("__arm64x_extra_rfe_table", 0);
2542-
ctx.symtab.addAbsolute("__arm64x_extra_rfe_table_size", 0);
2543-
ctx.symtab.addAbsolute("__arm64x_redirection_metadata", 0);
2544-
ctx.symtab.addAbsolute("__arm64x_redirection_metadata_count", 0);
2545-
ctx.symtab.addAbsolute("__hybrid_auxiliary_delayload_iat_copy", 0);
2546-
ctx.symtab.addAbsolute("__hybrid_auxiliary_delayload_iat", 0);
2547-
ctx.symtab.addAbsolute("__hybrid_auxiliary_iat", 0);
2548-
ctx.symtab.addAbsolute("__hybrid_auxiliary_iat_copy", 0);
2549-
ctx.symtab.addAbsolute("__hybrid_code_map", 0);
2550-
ctx.symtab.addAbsolute("__hybrid_code_map_count", 0);
2551-
ctx.symtab.addAbsolute("__hybrid_image_info_bitfield", 0);
2552-
ctx.symtab.addAbsolute("__x64_code_ranges_to_entry_points", 0);
2553-
ctx.symtab.addAbsolute("__x64_code_ranges_to_entry_points_count", 0);
2554-
ctx.symtab.addSynthetic("__guard_check_icall_a64n_fptr", nullptr);
2555-
ctx.symtab.addSynthetic("__arm64x_native_entrypoint", nullptr);
2556-
}
2557-
2558-
if (config->pseudoRelocs) {
2559-
ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
2560-
ctx.symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
2561-
}
2562-
if (config->mingw) {
2563-
ctx.symtab.addAbsolute(mangle("__CTOR_LIST__"), 0);
2564-
ctx.symtab.addAbsolute(mangle("__DTOR_LIST__"), 0);
2565-
}
2566-
if (config->debug || config->buildIDHash != BuildIDHash::None)
2567-
if (ctx.symtab.findUnderscore("__buildid"))
2568-
ctx.symtab.addUndefined(mangle("__buildid"));
2531+
ctx.forEachSymtab([&](SymbolTable &symtab) {
2532+
symtab.addSynthetic(mangle("__ImageBase"), nullptr);
2533+
if (symtab.machine == I386) {
2534+
symtab.addAbsolute("___safe_se_handler_table", 0);
2535+
symtab.addAbsolute("___safe_se_handler_count", 0);
2536+
}
2537+
2538+
symtab.addAbsolute(mangle("__guard_fids_count"), 0);
2539+
symtab.addAbsolute(mangle("__guard_fids_table"), 0);
2540+
symtab.addAbsolute(mangle("__guard_flags"), 0);
2541+
symtab.addAbsolute(mangle("__guard_iat_count"), 0);
2542+
symtab.addAbsolute(mangle("__guard_iat_table"), 0);
2543+
symtab.addAbsolute(mangle("__guard_longjmp_count"), 0);
2544+
symtab.addAbsolute(mangle("__guard_longjmp_table"), 0);
2545+
// Needed for MSVC 2017 15.5 CRT.
2546+
symtab.addAbsolute(mangle("__enclave_config"), 0);
2547+
// Needed for MSVC 2019 16.8 CRT.
2548+
symtab.addAbsolute(mangle("__guard_eh_cont_count"), 0);
2549+
symtab.addAbsolute(mangle("__guard_eh_cont_table"), 0);
2550+
2551+
if (isArm64EC(ctx.config.machine)) {
2552+
symtab.addAbsolute("__arm64x_extra_rfe_table", 0);
2553+
symtab.addAbsolute("__arm64x_extra_rfe_table_size", 0);
2554+
symtab.addAbsolute("__arm64x_redirection_metadata", 0);
2555+
symtab.addAbsolute("__arm64x_redirection_metadata_count", 0);
2556+
symtab.addAbsolute("__hybrid_auxiliary_delayload_iat_copy", 0);
2557+
symtab.addAbsolute("__hybrid_auxiliary_delayload_iat", 0);
2558+
symtab.addAbsolute("__hybrid_auxiliary_iat", 0);
2559+
symtab.addAbsolute("__hybrid_auxiliary_iat_copy", 0);
2560+
symtab.addAbsolute("__hybrid_code_map", 0);
2561+
symtab.addAbsolute("__hybrid_code_map_count", 0);
2562+
symtab.addAbsolute("__hybrid_image_info_bitfield", 0);
2563+
symtab.addAbsolute("__x64_code_ranges_to_entry_points", 0);
2564+
symtab.addAbsolute("__x64_code_ranges_to_entry_points_count", 0);
2565+
symtab.addSynthetic("__guard_check_icall_a64n_fptr", nullptr);
2566+
symtab.addSynthetic("__arm64x_native_entrypoint", nullptr);
2567+
}
2568+
2569+
if (config->pseudoRelocs) {
2570+
symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST__"), 0);
2571+
symtab.addAbsolute(mangle("__RUNTIME_PSEUDO_RELOC_LIST_END__"), 0);
2572+
}
2573+
if (config->mingw) {
2574+
symtab.addAbsolute(mangle("__CTOR_LIST__"), 0);
2575+
symtab.addAbsolute(mangle("__DTOR_LIST__"), 0);
2576+
}
2577+
if (config->debug || config->buildIDHash != BuildIDHash::None)
2578+
if (symtab.findUnderscore("__buildid"))
2579+
symtab.addUndefined(mangle("__buildid"));
2580+
});
25692581

25702582
// This code may add new undefined symbols to the link, which may enqueue more
25712583
// symbol resolution tasks, so we need to continue executing tasks until we
@@ -2808,7 +2820,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
28082820
if (auto *arg = args.getLastArg(OPT_print_symbol_order))
28092821
config->printSymbolOrder = arg->getValue();
28102822

2811-
ctx.symtab.initializeECThunks();
2823+
if (ctx.symtabEC)
2824+
ctx.symtabEC->initializeECThunks();
28122825

28132826
// Identify unreferenced COMDAT sections.
28142827
if (config->doGC) {

lld/COFF/InputFiles.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,15 @@ void ArchiveFile::parse() {
114114
file = CHECK(Archive::create(mb), this);
115115

116116
// Try to read symbols from ECSYMBOLS section on ARM64EC.
117-
if (isArm64EC(ctx.config.machine)) {
117+
if (ctx.symtabEC) {
118118
iterator_range<Archive::symbol_iterator> symbols =
119119
CHECK(file->ec_symbols(), this);
120120
if (!symbols.empty()) {
121121
for (const Archive::Symbol &sym : symbols)
122-
ctx.symtab.addLazyArchive(this, sym);
122+
ctx.symtabEC->addLazyArchive(this, sym);
123123

124124
// Read both EC and native symbols on ARM64X.
125-
if (ctx.config.machine != ARM64X)
125+
if (!ctx.hybridSymtab)
126126
return;
127127
}
128128
}
@@ -177,7 +177,8 @@ ObjFile *ObjFile::create(COFFLinkerContext &ctx, MemoryBufferRef m, bool lazy) {
177177
Fatal(ctx) << m.getBufferIdentifier() << " is not a COFF file";
178178

179179
bin->release();
180-
return make<ObjFile>(ctx.symtab, obj, lazy);
180+
return make<ObjFile>(ctx.getSymtab(MachineTypes(obj->getMachine())), obj,
181+
lazy);
181182
}
182183

183184
void ObjFile::parseLazy() {

lld/COFF/SymbolTable.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ class Symbol;
4747
// There is one add* function per symbol type.
4848
class SymbolTable {
4949
public:
50-
SymbolTable(COFFLinkerContext &c) : ctx(c) {}
50+
SymbolTable(COFFLinkerContext &c,
51+
llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN)
52+
: ctx(c), machine(machine) {}
5153

5254
void addFile(InputFile *file);
5355

@@ -120,7 +122,7 @@ class SymbolTable {
120122
uint32_t newSectionOffset = 0);
121123

122124
COFFLinkerContext &ctx;
123-
llvm::COFF::MachineTypes machine = IMAGE_FILE_MACHINE_UNKNOWN;
125+
llvm::COFF::MachineTypes machine;
124126

125127
bool isEC() const { return machine == ARM64EC; }
126128

lld/test/COFF/arm64ec-codemap.test

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ RUN: llvm-mc -filetype=obj -triple=arm64ec-windows data-sec2.s -o data-sec2.obj
99
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows empty-sec.s -o arm64ec-empty-sec.obj
1010
RUN: llvm-mc -filetype=obj -triple=x86_64-windows x86_64-func-sym.s -o x86_64-func-sym.obj
1111
RUN: llvm-mc -filetype=obj -triple=x86_64-windows empty-sec.s -o x86_64-empty-sec.obj
12+
RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64.obj
1213
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
1314

1415
Link ARM64EC DLL and verify that the code is arranged as expected.
@@ -51,7 +52,7 @@ RUN: llvm-readobj --coff-load-config test2.dll | FileCheck -check-prefix=CODEMAP
5152
RUN: llvm-objdump -d test2.dll | FileCheck -check-prefix=DISASM %s
5253

5354
RUN: lld-link -out:testx.dll -machine:arm64x arm64-func-sym.obj arm64ec-func-sym.obj \
54-
RUN: x86_64-func-sym.obj loadconfig-arm64ec.obj -dll -noentry
55+
RUN: x86_64-func-sym.obj loadconfig-arm64.obj loadconfig-arm64ec.obj -dll -noentry
5556

5657
Adding empty chunks does not affect code map ranges.
5758

lld/test/COFF/arm64ec-entry-thunk.s

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ thunk:
2727
.rva func
2828

2929
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadcfg.obj
30+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64ec.s -o native-loadcfg.obj
3031
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-simple.s -o test-simple.obj
3132
// RUN: lld-link -machine:arm64ec -dll -noentry -out:out-simple.dll loadcfg.obj test-simple.obj
3233
// RUN: llvm-objdump -d out-simple.dll | FileCheck --check-prefix=DISASM %s
@@ -43,7 +44,7 @@ thunk:
4344
// RUN: llvm-readobj --sections out-simple.dll | FileCheck --check-prefix=HYBMP %s
4445
// HYBMP-NOT: .hybmp
4546

46-
// RUN: lld-link -machine:arm64x -dll -noentry -out:out-simplex.dll loadcfg.obj test-simple.obj
47+
// RUN: lld-link -machine:arm64x -dll -noentry -out:out-simplex.dll native-loadcfg.obj loadcfg.obj test-simple.obj
4748
// RUN: llvm-objdump -d out-simplex.dll | FileCheck --check-prefix=DISASM %s
4849

4950
#--- test-split-func.s

lld/test/COFF/arm64ec-lib.test

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-alias.s -o ref-alias.obj
1111
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-thunk.s -o ref-thunk.obj
1212
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows func.s -o func.obj
1313
RUN: llvm-mc -filetype=obj -triple=x86_64-windows func-x86_64.s -o func-x86_64.obj
14+
RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64.obj
1415
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
1516

1617
RUN: llvm-lib -machine:arm64ec -out:sym-arm64ec.lib sym-arm64ec.obj nsym-aarch64.obj
@@ -26,7 +27,8 @@ Verify that a symbol can be referenced from a regular archive map when ECSYMBOLS
2627
RUN: lld-link -machine:arm64ec -dll -noentry -out:test2.dll symref-arm64ec.obj sym-x86_64.lib loadconfig-arm64ec.obj
2728

2829
Verify that both native and EC symbols can be referenced in a hybrid target.
29-
RUN: lld-link -machine:arm64x -dll -noentry -out:test3.dll symref-arm64ec.obj nsymref-aarch64.obj sym-arm64ec.lib loadconfig-arm64ec.obj
30+
RUN: lld-link -machine:arm64x -dll -noentry -out:test3.dll symref-arm64ec.obj nsymref-aarch64.obj sym-arm64ec.lib \
31+
RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj
3032

3133
Ensure that an EC symbol is not resolved using a regular symbol map.
3234
RUN: not lld-link -machine:arm64ec -dll -noentry -out:test-err.dll nsymref-arm64ec.obj sym-arm64ec.lib loadconfig-arm64ec.obj 2>&1 |\

lld/test/COFF/arm64ec-range-thunks.s

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# RUN: llvm-mc -filetype=obj -triple=aarch64-windows native-funcs.s -o funcs-aarch64.obj
66
# RUN: llvm-mc -filetype=obj -triple=x86_64-windows space.s -o space-x86_64.obj
77
# RUN: llvm-mc -filetype=obj -triple=aarch64-windows space.s -o space-aarch64.obj
8+
# RUN: llvm-mc -filetype=obj -triple=aarch64-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64.obj
89
# RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
910

1011

@@ -59,8 +60,8 @@
5960

6061
# A similar test using a hybrid binary and native placeholder chunks.
6162

62-
# RUN: lld-link -machine:arm64x -noentry -dll funcs-arm64ec.obj space-aarch64.obj loadconfig-arm64ec.obj -out:testx.dll \
63-
# RUN: -verbose 2>&1 | FileCheck -check-prefix=VERBOSE %s
63+
# RUN: lld-link -machine:arm64x -noentry -dll funcs-arm64ec.obj space-aarch64.obj loadconfig-arm64.obj loadconfig-arm64ec.obj \
64+
# RUN: -out:testx.dll -verbose 2>&1 | FileCheck -check-prefix=VERBOSE %s
6465
# RUN: llvm-objdump -d testx.dll | FileCheck --check-prefix=DISASM %s
6566

6667
# RUN: llvm-readobj --coff-load-config testx.dll | FileCheck --check-prefix=LOADCFGX %s
@@ -74,8 +75,8 @@
7475

7576
# Test a hybrid ARM64X binary which requires range extension thunks for both native and EC relocations.
7677

77-
# RUN: lld-link -machine:arm64x -noentry -dll funcs-arm64ec.obj funcs-aarch64.obj loadconfig-arm64ec.obj -out:testx2.dll \
78-
# RUN: -verbose 2>&1 | FileCheck -check-prefix=VERBOSEX %s
78+
# RUN: lld-link -machine:arm64x -noentry -dll funcs-arm64ec.obj funcs-aarch64.obj loadconfig-arm64.obj loadconfig-arm64ec.obj \
79+
# RUN: -out:testx2.dll -verbose 2>&1 | FileCheck -check-prefix=VERBOSEX %s
7980
# VERBOSEX: Added 5 thunks with margin {{.*}} in 1 passes
8081

8182
# RUN: llvm-objdump -d testx2.dll | FileCheck --check-prefix=DISASMX %s

lld/test/COFF/arm64x-symtab.s

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// REQUIRES: aarch64, x86
2+
// RUN: split-file %s %t.dir && cd %t.dir
3+
4+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows sym.s -o sym-aarch64.obj
5+
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows sym.s -o sym-arm64ec.obj
6+
// RUN: llvm-mc -filetype=obj -triple=x86_64-windows sym.s -o sym-x86_64.obj
7+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows symref.s -o symref-aarch64.obj
8+
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows symref.s -o symref-arm64ec.obj
9+
// RUN: llvm-lib -machine:arm64x -out:sym.lib sym-aarch64.obj sym-arm64ec.obj
10+
11+
// Check that native object files can't reference EC symbols.
12+
13+
// RUN: not lld-link -machine:arm64x -dll -noentry -out:err1.dll symref-aarch64.obj sym-arm64ec.obj \
14+
// RUN: 2>&1 | FileCheck --check-prefix=UNDEF %s
15+
// UNDEF: lld-link: error: undefined symbol: sym
16+
// UNDEF-NEXT: >>> referenced by symref-aarch64.obj:(.data)
17+
18+
// RUN: not lld-link -machine:arm64x -dll -noentry -out:err2.dll symref-aarch64.obj sym-x86_64.obj \
19+
// RUN: 2>&1 | FileCheck --check-prefix=UNDEF %s
20+
21+
// Check that ARM64X target can have the same symbol names in both native and EC namespaces.
22+
23+
// RUN: lld-link -machine:arm64x -dll -noentry -out:out.dll symref-aarch64.obj sym-aarch64.obj \
24+
// RUN: symref-arm64ec.obj sym-x86_64.obj
25+
26+
// Check that ARM64X target can reference both native and EC symbols from an archive.
27+
28+
// RUN: lld-link -machine:arm64x -dll -noentry -out:out2.dll symref-aarch64.obj symref-arm64ec.obj sym.lib
29+
30+
#--- symref.s
31+
.data
32+
.rva sym
33+
34+
#--- sym.s
35+
.data
36+
.globl sym
37+
sym:
38+
.word 0

0 commit comments

Comments
 (0)