Skip to content

Commit 1c05c61

Browse files
authored
[LLD][COFF] Swap the meaning of symtab and hybridSymtab in hybrid images (#135093)
Originally, the intent behind symtab was to represent the symbol table seen in the PE header (without applying ARM64X relocations). However, in most cases outside of `writeHeader()`, the code references either both symbol tables or only the EC one, for example, `mainSymtab` in `linkerMain()` maps to `hybridSymtab` on ARM64X. MSVC's link.exe allows pure ARM64EC images to include native ARM64 files. This patch prepares LLD to support the same, which will require `hybridSymtab` to be available even for ARM64EC. At that point, `writeHeader()` will need to use the EC symbol table, and the original reasoning for keeping it in `hybridSymtab` no longer applies. Given this, it seems cleaner to treat the EC symbol table as the “main” one, assigning it to `symtab`, and use `hybridSymtab` for the native symbol table instead. Since `writeHeader()` will need to be conditional anyway, this change simplifies the rest of the code by allowing other parts to consistently treat `ctx.symtab` as the main symbol table. As a further simplification, this also allows us to eliminate `symtabEC` and use `symtab` directly; I’ll submit that as a separate PR. The map file now uses the EC symbol table for printed entry points and exports, matching MSVC behavior.
1 parent 0f615fb commit 1c05c61

File tree

6 files changed

+111
-56
lines changed

6 files changed

+111
-56
lines changed

lld/COFF/COFFLinkerContext.h

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

35-
// A hybrid ARM64EC symbol table on ARM64X target.
35+
// A native ARM64 symbol table on ARM64X target.
3636
std::optional<SymbolTable> hybridSymtab;
3737

3838
// Pointer to the ARM64EC symbol table: either symtab for an ARM64EC target or
@@ -41,16 +41,17 @@ class COFFLinkerContext : public CommonLinkerContext {
4141

4242
// Returns the appropriate symbol table for the specified machine type.
4343
SymbolTable &getSymtab(llvm::COFF::MachineTypes machine) {
44-
if (hybridSymtab && (machine == ARM64EC || machine == AMD64))
44+
if (hybridSymtab && machine == ARM64)
4545
return *hybridSymtab;
4646
return symtab;
4747
}
4848

4949
// Invoke the specified callback for each symbol table.
5050
void forEachSymtab(std::function<void(SymbolTable &symtab)> f) {
51-
f(symtab);
51+
// If present, process the native symbol table first.
5252
if (hybridSymtab)
5353
f(*hybridSymtab);
54+
f(symtab);
5455
}
5556

5657
std::vector<ObjFile *> objFileInstances;

lld/COFF/Chunks.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -570,14 +570,14 @@ void SectionChunk::getBaserels(std::vector<Baserel> *res) {
570570
// to match the value in the EC load config, which is expected to be
571571
// a relocatable pointer to the __chpe_metadata symbol.
572572
COFFLinkerContext &ctx = file->symtab.ctx;
573-
if (ctx.hybridSymtab && ctx.symtab.loadConfigSym &&
574-
ctx.symtab.loadConfigSym->getChunk() == this &&
575-
ctx.hybridSymtab->loadConfigSym &&
576-
ctx.symtab.loadConfigSize >=
573+
if (ctx.hybridSymtab && ctx.hybridSymtab->loadConfigSym &&
574+
ctx.hybridSymtab->loadConfigSym->getChunk() == this &&
575+
ctx.symtab.loadConfigSym &&
576+
ctx.hybridSymtab->loadConfigSize >=
577577
offsetof(coff_load_configuration64, CHPEMetadataPointer) +
578578
sizeof(coff_load_configuration64::CHPEMetadataPointer))
579579
res->emplace_back(
580-
ctx.symtab.loadConfigSym->getRVA() +
580+
ctx.hybridSymtab->loadConfigSym->getRVA() +
581581
offsetof(coff_load_configuration64, CHPEMetadataPointer),
582582
IMAGE_REL_BASED_DIR64);
583583
}

lld/COFF/Driver.cpp

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -662,9 +662,9 @@ void LinkerDriver::setMachine(MachineTypes machine) {
662662
if (machine == ARM64EC)
663663
ctx.symtabEC = &ctx.symtab;
664664
} else {
665-
ctx.symtab.machine = ARM64;
666-
ctx.hybridSymtab.emplace(ctx, ARM64EC);
667-
ctx.symtabEC = &*ctx.hybridSymtab;
665+
ctx.symtab.machine = ARM64EC;
666+
ctx.hybridSymtab.emplace(ctx, ARM64);
667+
ctx.symtabEC = &ctx.symtab;
668668
}
669669

670670
addWinSysRootLibSearchPaths();
@@ -981,12 +981,9 @@ void LinkerDriver::createImportLibrary(bool asLib) {
981981
}
982982
};
983983

984-
if (ctx.hybridSymtab) {
985-
getExports(ctx.symtab, nativeExports);
986-
getExports(*ctx.hybridSymtab, exports);
987-
} else {
988-
getExports(ctx.symtab, exports);
989-
}
984+
getExports(ctx.symtab, exports);
985+
if (ctx.hybridSymtab)
986+
getExports(*ctx.hybridSymtab, nativeExports);
990987

991988
std::string libName = getImportName(asLib);
992989
std::string path = getImplibPath();
@@ -1818,10 +1815,6 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
18181815
}
18191816
}
18201817

1821-
// Most of main arguments apply either to both or only to EC symbol table on
1822-
// ARM64X target.
1823-
SymbolTable &mainSymtab = ctx.hybridSymtab ? *ctx.hybridSymtab : ctx.symtab;
1824-
18251818
// Handle /nodefaultlib:<filename>
18261819
{
18271820
llvm::TimeTraceScope timeScope2("Nodefaultlib");
@@ -1903,11 +1896,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
19031896

19041897
// Handle /alternatename
19051898
for (auto *arg : args.filtered(OPT_alternatename))
1906-
mainSymtab.parseAlternateName(arg->getValue());
1899+
ctx.symtab.parseAlternateName(arg->getValue());
19071900

19081901
// Handle /include
19091902
for (auto *arg : args.filtered(OPT_incl))
1910-
mainSymtab.addGCRoot(arg->getValue());
1903+
ctx.symtab.addGCRoot(arg->getValue());
19111904

19121905
// Handle /implib
19131906
if (auto *arg = args.getLastArg(OPT_implib))
@@ -2056,7 +2049,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
20562049

20572050
// Handle /aligncomm
20582051
for (auto *arg : args.filtered(OPT_aligncomm))
2059-
mainSymtab.parseAligncomm(arg->getValue());
2052+
ctx.symtab.parseAligncomm(arg->getValue());
20602053

20612054
// Handle /manifestdependency.
20622055
for (auto *arg : args.filtered(OPT_manifestdependency))
@@ -2307,19 +2300,19 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
23072300
if (!e.extName.empty() && !isDecorated(e.extName))
23082301
e.extName = saver().save("_" + e.extName);
23092302
}
2310-
mainSymtab.exports.push_back(e);
2303+
ctx.symtab.exports.push_back(e);
23112304
}
23122305
}
23132306

23142307
// Handle /def
23152308
if (auto *arg = args.getLastArg(OPT_deffile)) {
23162309
// parseModuleDefs mutates Config object.
2317-
mainSymtab.parseModuleDefs(arg->getValue());
2310+
ctx.symtab.parseModuleDefs(arg->getValue());
23182311
if (ctx.hybridSymtab) {
23192312
// MSVC ignores the /defArm64Native argument on non-ARM64X targets.
23202313
// It is also ignored if the /def option is not specified.
23212314
if (auto *arg = args.getLastArg(OPT_defarm64native))
2322-
ctx.symtab.parseModuleDefs(arg->getValue());
2315+
ctx.hybridSymtab->parseModuleDefs(arg->getValue());
23232316
}
23242317
}
23252318

@@ -2336,7 +2329,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
23362329
// and after the early return when just writing an import library.
23372330
if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
23382331
llvm::TimeTraceScope timeScope("Infer subsystem");
2339-
config->subsystem = mainSymtab.inferSubsystem();
2332+
config->subsystem = ctx.symtab.inferSubsystem();
23402333
if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
23412334
Fatal(ctx) << "subsystem must be defined";
23422335
}
@@ -2702,7 +2695,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
27022695

27032696
// Handle /output-def (MinGW specific).
27042697
if (auto *arg = args.getLastArg(OPT_output_def))
2705-
writeDefFile(ctx, arg->getValue(), mainSymtab.exports);
2698+
writeDefFile(ctx, arg->getValue(), ctx.symtab.exports);
27062699

27072700
// Set extra alignment for .comm symbols
27082701
ctx.forEachSymtab([&](SymbolTable &symtab) {

lld/COFF/InputFiles.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ void ArchiveFile::parse() {
139139
// Read both EC and native symbols on ARM64X.
140140
if (!ctx.hybridSymtab)
141141
return;
142+
archiveSymtab = &*ctx.hybridSymtab;
142143
} else if (ctx.hybridSymtab) {
143144
// If the ECSYMBOLS section is missing in the archive, the archive could
144145
// be either a native-only ARM64 or x86_64 archive. Check the machine type

lld/COFF/Writer.cpp

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,14 +1359,14 @@ void Writer::createExportTable() {
13591359

13601360
for (auto chunk : edataSec->chunks) {
13611361
if (chunk->getMachine() != ARM64) {
1362-
ctx.hybridSymtab->edataStart = chunk;
1363-
ctx.hybridSymtab->edataEnd = edataSec->chunks.back();
1362+
ctx.symtab.edataStart = chunk;
1363+
ctx.symtab.edataEnd = edataSec->chunks.back();
13641364
break;
13651365
}
13661366

1367-
if (!ctx.symtab.edataStart)
1368-
ctx.symtab.edataStart = chunk;
1369-
ctx.symtab.edataEnd = chunk;
1367+
if (!ctx.hybridSymtab->edataStart)
1368+
ctx.hybridSymtab->edataStart = chunk;
1369+
ctx.hybridSymtab->edataEnd = chunk;
13701370
}
13711371
}
13721372
}
@@ -1760,7 +1760,8 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
17601760
assert(coffHeaderOffset == buf - buffer->getBufferStart());
17611761
auto *coff = reinterpret_cast<coff_file_header *>(buf);
17621762
buf += sizeof(*coff);
1763-
coff->Machine = ctx.symtab.isEC() ? AMD64 : ctx.symtab.machine;
1763+
SymbolTable &symtab = ctx.hybridSymtab ? *ctx.hybridSymtab : ctx.symtab;
1764+
coff->Machine = symtab.isEC() ? AMD64 : symtab.machine;
17641765
coff->NumberOfSections = ctx.outputSections.size();
17651766
coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
17661767
if (config->largeAddressAware)
@@ -1807,7 +1808,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
18071808
pe->SizeOfImage = sizeOfImage;
18081809
pe->SizeOfHeaders = sizeOfHeaders;
18091810
if (!config->noEntry) {
1810-
Defined *entry = cast<Defined>(ctx.symtab.entry);
1811+
Defined *entry = cast<Defined>(symtab.entry);
18111812
pe->AddressOfEntryPoint = entry->getRVA();
18121813
// Pointer to thumb code must have the LSB set, so adjust it.
18131814
if (config->machine == ARMNT)
@@ -1851,11 +1852,11 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
18511852
dataDirOffset64 == buf - buffer->getBufferStart());
18521853
auto *dir = reinterpret_cast<data_directory *>(buf);
18531854
buf += sizeof(*dir) * numberOfDataDirectory;
1854-
if (ctx.symtab.edataStart) {
1855-
dir[EXPORT_TABLE].RelativeVirtualAddress = ctx.symtab.edataStart->getRVA();
1856-
dir[EXPORT_TABLE].Size = ctx.symtab.edataEnd->getRVA() +
1857-
ctx.symtab.edataEnd->getSize() -
1858-
ctx.symtab.edataStart->getRVA();
1855+
if (symtab.edataStart) {
1856+
dir[EXPORT_TABLE].RelativeVirtualAddress = symtab.edataStart->getRVA();
1857+
dir[EXPORT_TABLE].Size = symtab.edataEnd->getRVA() +
1858+
symtab.edataEnd->getSize() -
1859+
symtab.edataStart->getRVA();
18591860
}
18601861
if (importTableStart) {
18611862
dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
@@ -1886,7 +1887,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
18861887
dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
18871888
dir[BASE_RELOCATION_TABLE].Size = relocSize;
18881889
}
1889-
if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) {
1890+
if (Symbol *sym = symtab.findUnderscore("_tls_used")) {
18901891
if (Defined *b = dyn_cast<Defined>(sym)) {
18911892
dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA();
18921893
dir[TLS_TABLE].Size = config->is64()
@@ -1898,10 +1899,10 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
18981899
dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA();
18991900
dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize();
19001901
}
1901-
if (ctx.symtab.loadConfigSym) {
1902+
if (symtab.loadConfigSym) {
19021903
dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress =
1903-
ctx.symtab.loadConfigSym->getRVA();
1904-
dir[LOAD_CONFIG_TABLE].Size = ctx.symtab.loadConfigSize;
1904+
symtab.loadConfigSym->getRVA();
1905+
dir[LOAD_CONFIG_TABLE].Size = symtab.loadConfigSize;
19051906
}
19061907
if (!delayIdata.empty()) {
19071908
dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
@@ -2442,7 +2443,7 @@ void Writer::setECSymbols() {
24422443
// For the hybrid image, set the alternate entry point to the EC entry
24432444
// point. In the hybrid view, it is swapped to the native entry point
24442445
// using ARM64X relocations.
2445-
if (auto altEntrySym = cast_or_null<Defined>(ctx.hybridSymtab->entry)) {
2446+
if (auto altEntrySym = cast_or_null<Defined>(ctx.symtab.entry)) {
24462447
// If the entry is an EC export thunk, use its target instead.
24472448
if (auto thunkChunk =
24482449
dyn_cast<ECExportThunkChunk>(altEntrySym->getChunk()))
@@ -2711,7 +2712,7 @@ void Writer::createDynamicRelocs() {
27112712
if (ctx.symtab.entry != ctx.hybridSymtab->entry ||
27122713
pdata.first != hybridPdata.first) {
27132714
chpeSym = cast_or_null<DefinedRegular>(
2714-
ctx.hybridSymtab->findUnderscore("__chpe_metadata"));
2715+
ctx.symtab.findUnderscore("__chpe_metadata"));
27152716
if (!chpeSym)
27162717
Warn(ctx) << "'__chpe_metadata' is missing for ARM64X target";
27172718
}
@@ -2720,22 +2721,22 @@ void Writer::createDynamicRelocs() {
27202721
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
27212722
peHeaderOffset +
27222723
offsetof(pe32plus_header, AddressOfEntryPoint),
2723-
cast_or_null<Defined>(ctx.hybridSymtab->entry));
2724+
cast_or_null<Defined>(ctx.symtab.entry));
27242725

27252726
// Swap the alternate entry point in the CHPE metadata.
27262727
if (chpeSym)
27272728
ctx.dynamicRelocs->add(
27282729
IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
27292730
Arm64XRelocVal(chpeSym, offsetof(chpe_metadata, AlternateEntryPoint)),
2730-
cast_or_null<Defined>(ctx.symtab.entry));
2731+
cast_or_null<Defined>(ctx.hybridSymtab->entry));
27312732
}
27322733

27332734
if (ctx.symtab.edataStart != ctx.hybridSymtab->edataStart) {
27342735
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
27352736
dataDirOffset64 +
27362737
EXPORT_TABLE * sizeof(data_directory) +
27372738
offsetof(data_directory, RelativeVirtualAddress),
2738-
ctx.hybridSymtab->edataStart);
2739+
ctx.symtab.edataStart);
27392740
// The Size value is assigned after addresses are finalized.
27402741
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
27412742
dataDirOffset64 +
@@ -2773,12 +2774,12 @@ void Writer::createDynamicRelocs() {
27732774
dataDirOffset64 +
27742775
LOAD_CONFIG_TABLE * sizeof(data_directory) +
27752776
offsetof(data_directory, RelativeVirtualAddress),
2776-
ctx.hybridSymtab->loadConfigSym);
2777+
ctx.symtab.loadConfigSym);
27772778
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
27782779
dataDirOffset64 +
27792780
LOAD_CONFIG_TABLE * sizeof(data_directory) +
27802781
offsetof(data_directory, Size),
2781-
ctx.hybridSymtab->loadConfigSize);
2782+
ctx.symtab.loadConfigSize);
27822783
}
27832784

27842785
PartialSection *Writer::createPartialSection(StringRef name,
@@ -2889,15 +2890,14 @@ void Writer::prepareLoadConfig(SymbolTable &symtab, T *loadConfig) {
28892890
// On ARM64X, only the EC version of the load config contains
28902891
// CHPEMetadataPointer. Copy its value to the native load config.
28912892
if (ctx.hybridSymtab && !symtab.isEC() &&
2892-
ctx.hybridSymtab->loadConfigSize >=
2893+
ctx.symtab.loadConfigSize >=
28932894
offsetof(T, CHPEMetadataPointer) + sizeof(T::CHPEMetadataPointer)) {
28942895
OutputSection *sec =
2895-
ctx.getOutputSection(ctx.hybridSymtab->loadConfigSym->getChunk());
2896+
ctx.getOutputSection(ctx.symtab.loadConfigSym->getChunk());
28962897
uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();
28972898
auto hybridLoadConfig =
28982899
reinterpret_cast<const coff_load_configuration64 *>(
2899-
secBuf +
2900-
(ctx.hybridSymtab->loadConfigSym->getRVA() - sec->getRVA()));
2900+
secBuf + (ctx.symtab.loadConfigSym->getRVA() - sec->getRVA()));
29012901
loadConfig->CHPEMetadataPointer = hybridLoadConfig->CHPEMetadataPointer;
29022902
}
29032903
}

lld/test/COFF/arm64x-map.s

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// REQUIRES: aarch64
2+
// RUN: split-file %s %t.dir && cd %t.dir
3+
4+
// RUN: llvm-mc -filetype=obj -triple=aarch64-windows arm64-data-sym.s -o arm64-data-sym.obj
5+
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-data-sym.s -o arm64ec-data-sym.obj
6+
// RUN: lld-link -machine:arm64x -dll -out:out.dll -map -mapinfo:exports arm64-data-sym.obj arm64ec-data-sym.obj
7+
// RUN: FileCheck %s < out.map
8+
9+
// CHECK: Start Length Name Class
10+
// CHECK-NEXT: 0001:00000000 00001004H .text CODE
11+
// CHECK-NEXT: 0004:00000000 00000008H .data DATA
12+
// CHECK-NEXT: 0004:00000008 00000000H .bss DATA
13+
// CHECK-EMPTY:
14+
// CHECK-NEXT: Address Publics by Value Rva+Base Lib:Object
15+
// CHECK-EMPTY:
16+
// CHECK-NEXT: 0001:00000000 _DllMainCRTStartup 0000000180001000 arm64-data-sym.obj
17+
// CHECK-NEXT: 0001:00001000 _DllMainCRTStartup 0000000180002000 arm64ec-data-sym.obj
18+
// CHECK-NEXT: 0004:00000000 arm64_data_sym 0000000180005000 arm64-data-sym.obj
19+
// CHECK-NEXT: 0004:00000004 arm64ec_data_sym 0000000180005004 arm64ec-data-sym.obj
20+
// CHECK-EMPTY:
21+
// CHECK-NEXT: entry point at 0002:00000000
22+
// CHECK-EMPTY:
23+
// CHECK-NEXT: Static symbols
24+
// CHECK-EMPTY:
25+
// CHECK-EMPTY:
26+
// CHECK-NEXT: Exports
27+
// CHECK-EMPTY:
28+
// CHECK-NEXT: ordinal name
29+
// CHECK-EMPTY:
30+
// CHECK-NEXT: 1 arm64ec_data_sym
31+
32+
#--- arm64ec-data-sym.s
33+
.text
34+
.globl _DllMainCRTStartup
35+
_DllMainCRTStartup:
36+
ret
37+
38+
.data
39+
.globl arm64ec_data_sym
40+
.p2align 2, 0x0
41+
arm64ec_data_sym:
42+
.word 0x02020202
43+
44+
.section .drectve
45+
.ascii "-export:arm64ec_data_sym,DATA"
46+
47+
#--- arm64-data-sym.s
48+
.text
49+
.globl _DllMainCRTStartup
50+
_DllMainCRTStartup:
51+
ret
52+
53+
.data
54+
.globl arm64_data_sym
55+
.p2align 2, 0x0
56+
arm64_data_sym:
57+
.word 0x01010101
58+
59+
.section .drectve
60+
.ascii "-export:arm64_data_sym,DATA"

0 commit comments

Comments
 (0)