Skip to content

Commit b85c828

Browse files
committed
[LLD][COFF] Swap the meaning of symtab and hybridSymtab in hybrid images
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 b85c828

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)