[lld] [LLD][COFF] Swap the meaning of symtab and hybridSymtab in hybrid images (PR #135093)
via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 9 16:37:12 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lld
Author: Jacek Caban (cjacek)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/135093.diff
6 Files Affected:
- (modified) lld/COFF/COFFLinkerContext.h (+3-3)
- (modified) lld/COFF/Chunks.cpp (+5-5)
- (modified) lld/COFF/Driver.cpp (+14-21)
- (modified) lld/COFF/InputFiles.cpp (+1)
- (modified) lld/COFF/Writer.cpp (+27-27)
- (added) lld/test/COFF/arm64x-map.s (+60)
``````````diff
diff --git a/lld/COFF/COFFLinkerContext.h b/lld/COFF/COFFLinkerContext.h
index 8322f829d4055..d01a34899c57d 100644
--- a/lld/COFF/COFFLinkerContext.h
+++ b/lld/COFF/COFFLinkerContext.h
@@ -32,7 +32,7 @@ class COFFLinkerContext : public CommonLinkerContext {
SymbolTable symtab;
COFFOptTable optTable;
- // A hybrid ARM64EC symbol table on ARM64X target.
+ // A native ARM64 symbol table on ARM64X target.
std::optional<SymbolTable> hybridSymtab;
// Pointer to the ARM64EC symbol table: either symtab for an ARM64EC target or
@@ -41,16 +41,16 @@ class COFFLinkerContext : public CommonLinkerContext {
// Returns the appropriate symbol table for the specified machine type.
SymbolTable &getSymtab(llvm::COFF::MachineTypes machine) {
- if (hybridSymtab && (machine == ARM64EC || machine == AMD64))
+ if (hybridSymtab && machine == ARM64)
return *hybridSymtab;
return symtab;
}
// Invoke the specified callback for each symbol table.
void forEachSymtab(std::function<void(SymbolTable &symtab)> f) {
- f(symtab);
if (hybridSymtab)
f(*hybridSymtab);
+ f(symtab);
}
std::vector<ObjFile *> objFileInstances;
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 3494d1ba0ac02..3920158923858 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -570,14 +570,14 @@ void SectionChunk::getBaserels(std::vector<Baserel> *res) {
// to match the value in the EC load config, which is expected to be
// a relocatable pointer to the __chpe_metadata symbol.
COFFLinkerContext &ctx = file->symtab.ctx;
- if (ctx.hybridSymtab && ctx.symtab.loadConfigSym &&
- ctx.symtab.loadConfigSym->getChunk() == this &&
- ctx.hybridSymtab->loadConfigSym &&
- ctx.symtab.loadConfigSize >=
+ if (ctx.hybridSymtab && ctx.hybridSymtab->loadConfigSym &&
+ ctx.hybridSymtab->loadConfigSym->getChunk() == this &&
+ ctx.symtab.loadConfigSym &&
+ ctx.hybridSymtab->loadConfigSize >=
offsetof(coff_load_configuration64, CHPEMetadataPointer) +
sizeof(coff_load_configuration64::CHPEMetadataPointer))
res->emplace_back(
- ctx.symtab.loadConfigSym->getRVA() +
+ ctx.hybridSymtab->loadConfigSym->getRVA() +
offsetof(coff_load_configuration64, CHPEMetadataPointer),
IMAGE_REL_BASED_DIR64);
}
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 7aa13bdce488e..d9e70a5be3261 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -662,9 +662,9 @@ void LinkerDriver::setMachine(MachineTypes machine) {
if (machine == ARM64EC)
ctx.symtabEC = &ctx.symtab;
} else {
- ctx.symtab.machine = ARM64;
- ctx.hybridSymtab.emplace(ctx, ARM64EC);
- ctx.symtabEC = &*ctx.hybridSymtab;
+ ctx.symtab.machine = ARM64EC;
+ ctx.hybridSymtab.emplace(ctx, ARM64);
+ ctx.symtabEC = &ctx.symtab;
}
addWinSysRootLibSearchPaths();
@@ -981,12 +981,9 @@ void LinkerDriver::createImportLibrary(bool asLib) {
}
};
- if (ctx.hybridSymtab) {
- getExports(ctx.symtab, nativeExports);
- getExports(*ctx.hybridSymtab, exports);
- } else {
- getExports(ctx.symtab, exports);
- }
+ getExports(ctx.symtab, exports);
+ if (ctx.hybridSymtab)
+ getExports(*ctx.hybridSymtab, nativeExports);
std::string libName = getImportName(asLib);
std::string path = getImplibPath();
@@ -1818,10 +1815,6 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
}
}
- // Most of main arguments apply either to both or only to EC symbol table on
- // ARM64X target.
- SymbolTable &mainSymtab = ctx.hybridSymtab ? *ctx.hybridSymtab : ctx.symtab;
-
// Handle /nodefaultlib:<filename>
{
llvm::TimeTraceScope timeScope2("Nodefaultlib");
@@ -1903,11 +1896,11 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// Handle /alternatename
for (auto *arg : args.filtered(OPT_alternatename))
- mainSymtab.parseAlternateName(arg->getValue());
+ ctx.symtab.parseAlternateName(arg->getValue());
// Handle /include
for (auto *arg : args.filtered(OPT_incl))
- mainSymtab.addGCRoot(arg->getValue());
+ ctx.symtab.addGCRoot(arg->getValue());
// Handle /implib
if (auto *arg = args.getLastArg(OPT_implib))
@@ -2056,7 +2049,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// Handle /aligncomm
for (auto *arg : args.filtered(OPT_aligncomm))
- mainSymtab.parseAligncomm(arg->getValue());
+ ctx.symtab.parseAligncomm(arg->getValue());
// Handle /manifestdependency.
for (auto *arg : args.filtered(OPT_manifestdependency))
@@ -2307,19 +2300,19 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (!e.extName.empty() && !isDecorated(e.extName))
e.extName = saver().save("_" + e.extName);
}
- mainSymtab.exports.push_back(e);
+ ctx.symtab.exports.push_back(e);
}
}
// Handle /def
if (auto *arg = args.getLastArg(OPT_deffile)) {
// parseModuleDefs mutates Config object.
- mainSymtab.parseModuleDefs(arg->getValue());
+ ctx.symtab.parseModuleDefs(arg->getValue());
if (ctx.hybridSymtab) {
// MSVC ignores the /defArm64Native argument on non-ARM64X targets.
// It is also ignored if the /def option is not specified.
if (auto *arg = args.getLastArg(OPT_defarm64native))
- ctx.symtab.parseModuleDefs(arg->getValue());
+ ctx.hybridSymtab->parseModuleDefs(arg->getValue());
}
}
@@ -2336,7 +2329,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// and after the early return when just writing an import library.
if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN) {
llvm::TimeTraceScope timeScope("Infer subsystem");
- config->subsystem = mainSymtab.inferSubsystem();
+ config->subsystem = ctx.symtab.inferSubsystem();
if (config->subsystem == IMAGE_SUBSYSTEM_UNKNOWN)
Fatal(ctx) << "subsystem must be defined";
}
@@ -2702,7 +2695,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// Handle /output-def (MinGW specific).
if (auto *arg = args.getLastArg(OPT_output_def))
- writeDefFile(ctx, arg->getValue(), mainSymtab.exports);
+ writeDefFile(ctx, arg->getValue(), ctx.symtab.exports);
// Set extra alignment for .comm symbols
ctx.forEachSymtab([&](SymbolTable &symtab) {
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index 256bec6bb636c..7916e3fd4ea1f 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -139,6 +139,7 @@ void ArchiveFile::parse() {
// Read both EC and native symbols on ARM64X.
if (!ctx.hybridSymtab)
return;
+ archiveSymtab = &*ctx.hybridSymtab;
} else if (ctx.hybridSymtab) {
// If the ECSYMBOLS section is missing in the archive, the archive could
// be either a native-only ARM64 or x86_64 archive. Check the machine type
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 6ed1f884a9636..31c686761d74f 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1359,14 +1359,14 @@ void Writer::createExportTable() {
for (auto chunk : edataSec->chunks) {
if (chunk->getMachine() != ARM64) {
- ctx.hybridSymtab->edataStart = chunk;
- ctx.hybridSymtab->edataEnd = edataSec->chunks.back();
+ ctx.symtab.edataStart = chunk;
+ ctx.symtab.edataEnd = edataSec->chunks.back();
break;
}
- if (!ctx.symtab.edataStart)
- ctx.symtab.edataStart = chunk;
- ctx.symtab.edataEnd = chunk;
+ if (!ctx.hybridSymtab->edataStart)
+ ctx.hybridSymtab->edataStart = chunk;
+ ctx.hybridSymtab->edataEnd = chunk;
}
}
}
@@ -1760,7 +1760,8 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
assert(coffHeaderOffset == buf - buffer->getBufferStart());
auto *coff = reinterpret_cast<coff_file_header *>(buf);
buf += sizeof(*coff);
- coff->Machine = ctx.symtab.isEC() ? AMD64 : ctx.symtab.machine;
+ SymbolTable &symtab = ctx.hybridSymtab ? *ctx.hybridSymtab : ctx.symtab;
+ coff->Machine = symtab.isEC() ? AMD64 : symtab.machine;
coff->NumberOfSections = ctx.outputSections.size();
coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
if (config->largeAddressAware)
@@ -1807,7 +1808,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
pe->SizeOfImage = sizeOfImage;
pe->SizeOfHeaders = sizeOfHeaders;
if (!config->noEntry) {
- Defined *entry = cast<Defined>(ctx.symtab.entry);
+ Defined *entry = cast<Defined>(symtab.entry);
pe->AddressOfEntryPoint = entry->getRVA();
// Pointer to thumb code must have the LSB set, so adjust it.
if (config->machine == ARMNT)
@@ -1851,11 +1852,11 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
dataDirOffset64 == buf - buffer->getBufferStart());
auto *dir = reinterpret_cast<data_directory *>(buf);
buf += sizeof(*dir) * numberOfDataDirectory;
- if (ctx.symtab.edataStart) {
- dir[EXPORT_TABLE].RelativeVirtualAddress = ctx.symtab.edataStart->getRVA();
- dir[EXPORT_TABLE].Size = ctx.symtab.edataEnd->getRVA() +
- ctx.symtab.edataEnd->getSize() -
- ctx.symtab.edataStart->getRVA();
+ if (symtab.edataStart) {
+ dir[EXPORT_TABLE].RelativeVirtualAddress = symtab.edataStart->getRVA();
+ dir[EXPORT_TABLE].Size = symtab.edataEnd->getRVA() +
+ symtab.edataEnd->getSize() -
+ symtab.edataStart->getRVA();
}
if (importTableStart) {
dir[IMPORT_TABLE].RelativeVirtualAddress = importTableStart->getRVA();
@@ -1886,7 +1887,7 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
dir[BASE_RELOCATION_TABLE].RelativeVirtualAddress = relocSec->getRVA();
dir[BASE_RELOCATION_TABLE].Size = relocSize;
}
- if (Symbol *sym = ctx.symtab.findUnderscore("_tls_used")) {
+ if (Symbol *sym = symtab.findUnderscore("_tls_used")) {
if (Defined *b = dyn_cast<Defined>(sym)) {
dir[TLS_TABLE].RelativeVirtualAddress = b->getRVA();
dir[TLS_TABLE].Size = config->is64()
@@ -1898,10 +1899,10 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
dir[DEBUG_DIRECTORY].RelativeVirtualAddress = debugDirectory->getRVA();
dir[DEBUG_DIRECTORY].Size = debugDirectory->getSize();
}
- if (ctx.symtab.loadConfigSym) {
+ if (symtab.loadConfigSym) {
dir[LOAD_CONFIG_TABLE].RelativeVirtualAddress =
- ctx.symtab.loadConfigSym->getRVA();
- dir[LOAD_CONFIG_TABLE].Size = ctx.symtab.loadConfigSize;
+ symtab.loadConfigSym->getRVA();
+ dir[LOAD_CONFIG_TABLE].Size = symtab.loadConfigSize;
}
if (!delayIdata.empty()) {
dir[DELAY_IMPORT_DESCRIPTOR].RelativeVirtualAddress =
@@ -2442,7 +2443,7 @@ void Writer::setECSymbols() {
// For the hybrid image, set the alternate entry point to the EC entry
// point. In the hybrid view, it is swapped to the native entry point
// using ARM64X relocations.
- if (auto altEntrySym = cast_or_null<Defined>(ctx.hybridSymtab->entry)) {
+ if (auto altEntrySym = cast_or_null<Defined>(ctx.symtab.entry)) {
// If the entry is an EC export thunk, use its target instead.
if (auto thunkChunk =
dyn_cast<ECExportThunkChunk>(altEntrySym->getChunk()))
@@ -2711,7 +2712,7 @@ void Writer::createDynamicRelocs() {
if (ctx.symtab.entry != ctx.hybridSymtab->entry ||
pdata.first != hybridPdata.first) {
chpeSym = cast_or_null<DefinedRegular>(
- ctx.hybridSymtab->findUnderscore("__chpe_metadata"));
+ ctx.symtab.findUnderscore("__chpe_metadata"));
if (!chpeSym)
Warn(ctx) << "'__chpe_metadata' is missing for ARM64X target";
}
@@ -2720,14 +2721,14 @@ void Writer::createDynamicRelocs() {
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
peHeaderOffset +
offsetof(pe32plus_header, AddressOfEntryPoint),
- cast_or_null<Defined>(ctx.hybridSymtab->entry));
+ cast_or_null<Defined>(ctx.symtab.entry));
// Swap the alternate entry point in the CHPE metadata.
if (chpeSym)
ctx.dynamicRelocs->add(
IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
Arm64XRelocVal(chpeSym, offsetof(chpe_metadata, AlternateEntryPoint)),
- cast_or_null<Defined>(ctx.symtab.entry));
+ cast_or_null<Defined>(ctx.hybridSymtab->entry));
}
if (ctx.symtab.edataStart != ctx.hybridSymtab->edataStart) {
@@ -2735,7 +2736,7 @@ void Writer::createDynamicRelocs() {
dataDirOffset64 +
EXPORT_TABLE * sizeof(data_directory) +
offsetof(data_directory, RelativeVirtualAddress),
- ctx.hybridSymtab->edataStart);
+ ctx.symtab.edataStart);
// The Size value is assigned after addresses are finalized.
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
dataDirOffset64 +
@@ -2773,12 +2774,12 @@ void Writer::createDynamicRelocs() {
dataDirOffset64 +
LOAD_CONFIG_TABLE * sizeof(data_directory) +
offsetof(data_directory, RelativeVirtualAddress),
- ctx.hybridSymtab->loadConfigSym);
+ ctx.symtab.loadConfigSym);
ctx.dynamicRelocs->add(IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE, sizeof(uint32_t),
dataDirOffset64 +
LOAD_CONFIG_TABLE * sizeof(data_directory) +
offsetof(data_directory, Size),
- ctx.hybridSymtab->loadConfigSize);
+ ctx.symtab.loadConfigSize);
}
PartialSection *Writer::createPartialSection(StringRef name,
@@ -2889,15 +2890,14 @@ void Writer::prepareLoadConfig(SymbolTable &symtab, T *loadConfig) {
// On ARM64X, only the EC version of the load config contains
// CHPEMetadataPointer. Copy its value to the native load config.
if (ctx.hybridSymtab && !symtab.isEC() &&
- ctx.hybridSymtab->loadConfigSize >=
+ ctx.symtab.loadConfigSize >=
offsetof(T, CHPEMetadataPointer) + sizeof(T::CHPEMetadataPointer)) {
OutputSection *sec =
- ctx.getOutputSection(ctx.hybridSymtab->loadConfigSym->getChunk());
+ ctx.getOutputSection(ctx.symtab.loadConfigSym->getChunk());
uint8_t *secBuf = buffer->getBufferStart() + sec->getFileOff();
auto hybridLoadConfig =
reinterpret_cast<const coff_load_configuration64 *>(
- secBuf +
- (ctx.hybridSymtab->loadConfigSym->getRVA() - sec->getRVA()));
+ secBuf + (ctx.symtab.loadConfigSym->getRVA() - sec->getRVA()));
loadConfig->CHPEMetadataPointer = hybridLoadConfig->CHPEMetadataPointer;
}
}
diff --git a/lld/test/COFF/arm64x-map.s b/lld/test/COFF/arm64x-map.s
new file mode 100644
index 0000000000000..956c52a93b041
--- /dev/null
+++ b/lld/test/COFF/arm64x-map.s
@@ -0,0 +1,60 @@
+// REQUIRES: aarch64
+// RUN: split-file %s %t.dir && cd %t.dir
+
+// RUN: llvm-mc -filetype=obj -triple=aarch64-windows arm64-data-sym.s -o arm64-data-sym.obj
+// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-data-sym.s -o arm64ec-data-sym.obj
+// RUN: lld-link -machine:arm64x -dll -out:out.dll -map -mapinfo:exports arm64-data-sym.obj arm64ec-data-sym.obj
+// RUN: FileCheck %s < out.map
+
+// CHECK: Start Length Name Class
+// CHECK-NEXT: 0001:00000000 00001004H .text CODE
+// CHECK-NEXT: 0004:00000000 00000008H .data DATA
+// CHECK-NEXT: 0004:00000008 00000000H .bss DATA
+// CHECK-EMPTY:
+// CHECK-NEXT: Address Publics by Value Rva+Base Lib:Object
+// CHECK-EMPTY:
+// CHECK-NEXT: 0001:00000000 _DllMainCRTStartup 0000000180001000 arm64-data-sym.obj
+// CHECK-NEXT: 0001:00001000 _DllMainCRTStartup 0000000180002000 arm64ec-data-sym.obj
+// CHECK-NEXT: 0004:00000000 arm64_data_sym 0000000180005000 arm64-data-sym.obj
+// CHECK-NEXT: 0004:00000004 arm64ec_data_sym 0000000180005004 arm64ec-data-sym.obj
+// CHECK-EMPTY:
+// CHECK-NEXT: entry point at 0002:00000000
+// CHECK-EMPTY:
+// CHECK-NEXT: Static symbols
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: Exports
+// CHECK-EMPTY:
+// CHECK-NEXT: ordinal name
+// CHECK-EMPTY:
+// CHECK-NEXT: 1 arm64ec_data_sym
+
+#--- arm64ec-data-sym.s
+ .text
+ .globl _DllMainCRTStartup
+_DllMainCRTStartup:
+ ret
+
+ .data
+ .globl arm64ec_data_sym
+ .p2align 2, 0x0
+arm64ec_data_sym:
+ .word 0x02020202
+
+ .section .drectve
+ .ascii "-export:arm64ec_data_sym,DATA"
+
+#--- arm64-data-sym.s
+ .text
+ .globl _DllMainCRTStartup
+_DllMainCRTStartup:
+ ret
+
+ .data
+ .globl arm64_data_sym
+ .p2align 2, 0x0
+arm64_data_sym:
+ .word 0x01010101
+
+ .section .drectve
+ .ascii "-export:arm64_data_sym,DATA"
``````````
</details>
https://github.com/llvm/llvm-project/pull/135093
More information about the llvm-commits
mailing list