[lld] [LLD][COFF] Add support for including native ARM64 objects in ARM64EC images (PR #137653)
Jacek Caban via llvm-commits
llvm-commits at lists.llvm.org
Wed May 14 14:18:43 PDT 2025
https://github.com/cjacek updated https://github.com/llvm/llvm-project/pull/137653
>From 17a43f99d455fc48707df45b6d46db6d54995811 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Mon, 7 Apr 2025 20:47:47 +0200
Subject: [PATCH 1/2] [LLD][COFF] Add support for including native ARM64
objects in ARM64EC images
MSVC linker accepts native ARM64 object files as input with -machine:arm64ec,
similar to -machine:arm64x. Its usefulness is very limited; for example, both
exports and imports are not reflected in the PE structures and can't work.
However, their symbol tables are otherwise functional.
Since we already have handling of multiple symbol tables implemented for ARM64X,
the required changes are mostly about adjusting relevant checks to account for
them on the ARM64EC target.
Delay-load helper handling is a bit of a shortcut. The patch never pulls it for
native object files and just ensures that the code is fine with that. In general,
I think it would be nice to adjust the driver to pull it only when it's actually
referenced, which would allow applying the same logic to the native symbol table
on ARM64EC without worrying about pulling too much.
---
lld/COFF/COFFLinkerContext.h | 8 ++
lld/COFF/Chunks.cpp | 2 +-
lld/COFF/DLL.cpp | 74 ++++++----
lld/COFF/Driver.cpp | 31 ++--
lld/COFF/InputFiles.cpp | 4 +-
lld/COFF/SymbolTable.cpp | 2 +-
lld/COFF/Writer.cpp | 13 +-
lld/test/COFF/arm64ec-entry-mangle.test | 2 +-
lld/test/COFF/arm64ec-hybmp.s | 4 +-
lld/test/COFF/arm64ec-lib.test | 4 +-
lld/test/COFF/arm64ec-patchable-thunks.test | 2 +-
lld/test/COFF/arm64ec-range-thunks.s | 5 +
lld/test/COFF/arm64ec.test | 9 +-
lld/test/COFF/arm64x-altnames.s | 6 +
lld/test/COFF/arm64x-buildid.s | 3 +
lld/test/COFF/arm64x-comm.s | 3 +
lld/test/COFF/arm64x-crt-sec.s | 3 +
lld/test/COFF/arm64x-ctors-sec.s | 4 +
lld/test/COFF/arm64x-guardcf.s | 42 +++---
lld/test/COFF/arm64x-import.test | 150 ++++++++++++--------
lld/test/COFF/arm64x-symtab.s | 16 +++
lld/test/COFF/arm64x-wrap.s | 4 +
lld/test/COFF/autoimport-arm64ec-data.test | 2 +-
23 files changed, 252 insertions(+), 141 deletions(-)
diff --git a/lld/COFF/COFFLinkerContext.h b/lld/COFF/COFFLinkerContext.h
index 2c5f6415e5d4b..f45b754384ef9 100644
--- a/lld/COFF/COFFLinkerContext.h
+++ b/lld/COFF/COFFLinkerContext.h
@@ -50,6 +50,14 @@ class COFFLinkerContext : public CommonLinkerContext {
f(symtab);
}
+ // Invoke the specified callback for each active symbol table,
+ // skipping the native symbol table on pure ARM64EC targets.
+ void forEachActiveSymtab(std::function<void(SymbolTable &symtab)> f) {
+ if (symtab.ctx.config.machine == ARM64X)
+ f(*hybridSymtab);
+ f(symtab);
+ }
+
std::vector<ObjFile *> objFileInstances;
std::map<std::string, PDBInputFile *> pdbInputFileInstances;
std::vector<ImportFile *> importFileInstances;
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index ff2bc40932c04..01752cdc6a9da 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -580,7 +580,7 @@ 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.hybridSymtab->loadConfigSym &&
+ if (ctx.config.machine == ARM64X && ctx.hybridSymtab->loadConfigSym &&
ctx.hybridSymtab->loadConfigSym->getChunk() == this &&
ctx.symtab.loadConfigSym &&
ctx.hybridSymtab->loadConfigSize >=
diff --git a/lld/COFF/DLL.cpp b/lld/COFF/DLL.cpp
index 0440507b71756..c327da28ce138 100644
--- a/lld/COFF/DLL.cpp
+++ b/lld/COFF/DLL.cpp
@@ -560,7 +560,8 @@ class TailMergeChunkARM64 : public NonSectionCodeChunk {
memcpy(buf, tailMergeARM64, sizeof(tailMergeARM64));
applyArm64Addr(buf + 44, desc->getRVA(), rva + 44, 12);
applyArm64Imm(buf + 48, desc->getRVA() & 0xfff, 0);
- applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52);
+ if (helper)
+ applyArm64Branch26(buf + 52, helper->getRVA() - rva - 52);
}
Chunk *desc = nullptr;
@@ -781,6 +782,7 @@ void IdataContents::create(COFFLinkerContext &ctx) {
// ordinal values to the table.
size_t base = lookups.size();
Chunk *lookupsTerminator = nullptr, *addressesTerminator = nullptr;
+ uint32_t nativeOnly = 0;
for (DefinedImportData *s : syms) {
uint16_t ord = s->getOrdinal();
HintNameChunk *hintChunk = nullptr;
@@ -806,8 +808,8 @@ void IdataContents::create(COFFLinkerContext &ctx) {
// the native terminator, they will be ignored in the native view.
// In the EC view, they should act as terminators, so emit ZEROFILL
// relocations overriding them.
- if (ctx.hybridSymtab && !lookupsTerminator && s->file->isEC() &&
- !s->file->hybridFile) {
+ if (ctx.config.machine == ARM64X && !lookupsTerminator &&
+ s->file->isEC() && !s->file->hybridFile) {
lookupsTerminator = lookupsChunk;
addressesTerminator = addressesChunk;
lookupsChunk = make<NullChunk>(ctx);
@@ -841,6 +843,7 @@ void IdataContents::create(COFFLinkerContext &ctx) {
// Fill the auxiliary IAT with null chunks for native-only imports.
auxIat.push_back(make<NullChunk>(ctx));
auxIatCopy.push_back(make<NullChunk>(ctx));
+ ++nativeOnly;
}
}
// Terminate with null values.
@@ -862,18 +865,15 @@ void IdataContents::create(COFFLinkerContext &ctx) {
// Create the import table header.
dllNames.push_back(make<StringChunk>(syms[0]->getDLLName()));
auto *dir = make<ImportDirectoryChunk>(dllNames.back());
- dir->lookupTab = lookups[base];
- dir->addressTab = addresses[base];
- dirs.push_back(dir);
- if (ctx.hybridSymtab) {
- // If native-only imports exist, they will appear as a prefix to all
- // imports. Emit ARM64X relocations to skip them in the EC view.
- uint32_t nativeOnly =
- llvm::find_if(syms,
- [](DefinedImportData *s) { return s->file->isEC(); }) -
- syms.begin();
- if (nativeOnly) {
+ if (ctx.hybridSymtab && nativeOnly) {
+ if (ctx.config.machine != ARM64X)
+ // On pure ARM64EC targets, skip native-only imports in the import
+ // directory.
+ base += nativeOnly;
+ else if (nativeOnly) {
+ // If native-only imports exist, they will appear as a prefix to all
+ // imports. Emit ARM64X relocations to skip them in the EC view.
ctx.dynamicRelocs->add(
IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
Arm64XRelocVal(
@@ -886,6 +886,10 @@ void IdataContents::create(COFFLinkerContext &ctx) {
nativeOnly * sizeof(uint64_t));
}
}
+
+ dir->lookupTab = lookups[base];
+ dir->addressTab = addresses[base];
+ dirs.push_back(dir);
}
// Add null terminator.
dirs.push_back(make<NullChunk>(sizeof(ImportDirectoryTableEntry), 4));
@@ -922,21 +926,25 @@ void DelayLoadContents::create() {
size_t base = addresses.size();
ctx.forEachSymtab([&](SymbolTable &symtab) {
- if (ctx.hybridSymtab && symtab.isEC()) {
- // For hybrid images, emit null-terminated native import entries
- // followed by null-terminated EC entries. If a view is missing imports
- // for a given module, only terminators are emitted. Emit ARM64X
- // relocations to skip native entries in the EC view.
- ctx.dynamicRelocs->add(
- IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
- Arm64XRelocVal(dir, offsetof(delay_import_directory_table_entry,
- DelayImportAddressTable)),
- (addresses.size() - base) * sizeof(uint64_t));
- ctx.dynamicRelocs->add(
- IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
- Arm64XRelocVal(dir, offsetof(delay_import_directory_table_entry,
- DelayImportNameTable)),
- (addresses.size() - base) * sizeof(uint64_t));
+ if (symtab.isEC()) {
+ if (ctx.config.machine == ARM64X) {
+ // For hybrid images, emit null-terminated native import entries
+ // followed by null-terminated EC entries. If a view is missing
+ // imports for a given module, only terminators are emitted. Emit
+ // ARM64X relocations to skip native entries in the EC view.
+ ctx.dynamicRelocs->add(
+ IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
+ Arm64XRelocVal(dir, offsetof(delay_import_directory_table_entry,
+ DelayImportAddressTable)),
+ (addresses.size() - base) * sizeof(uint64_t));
+ ctx.dynamicRelocs->add(
+ IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA, 0,
+ Arm64XRelocVal(dir, offsetof(delay_import_directory_table_entry,
+ DelayImportNameTable)),
+ (addresses.size() - base) * sizeof(uint64_t));
+ } else {
+ base = addresses.size();
+ }
}
Chunk *tm = nullptr;
@@ -981,7 +989,7 @@ void DelayLoadContents::create() {
chunk = make<AuxImportChunk>(s->file);
auxIatCopy.push_back(chunk);
s->file->auxImpCopySym->setLocation(chunk);
- } else if (ctx.hybridSymtab) {
+ } else if (ctx.config.machine == ARM64X) {
// Fill the auxiliary IAT with null chunks for native imports.
auxIat.push_back(make<NullChunk>(ctx));
auxIatCopy.push_back(make<NullChunk>(ctx));
@@ -995,6 +1003,10 @@ void DelayLoadContents::create() {
symtab.addSynthetic(tmName, tm);
}
+ // Skip terminators on pure ARM64EC target if there are no native imports.
+ if (!tm && !symtab.isEC() && ctx.config.machine != ARM64X)
+ return;
+
// Terminate with null values.
addresses.push_back(make<NullChunk>(ctx, 8));
names.push_back(make<NullChunk>(ctx, 8));
@@ -1024,7 +1036,7 @@ void DelayLoadContents::create() {
}
Chunk *DelayLoadContents::newTailMergeChunk(SymbolTable &symtab, Chunk *dir) {
- auto helper = cast<Defined>(symtab.delayLoadHelper);
+ auto helper = cast_or_null<Defined>(symtab.delayLoadHelper);
switch (symtab.machine) {
case AMD64:
case ARM64EC:
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 4c296da35d667..6e7fc376fb9c1 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -190,7 +190,6 @@ static bool compatibleMachineType(COFFLinkerContext &ctx, MachineTypes mt) {
case ARM64:
return mt == ARM64 || mt == ARM64X;
case ARM64EC:
- return isArm64EC(mt) || mt == AMD64;
case ARM64X:
return isAnyArm64(mt) || mt == AMD64;
case IMAGE_FILE_MACHINE_UNKNOWN:
@@ -499,7 +498,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
case OPT_entry:
if (!arg->getValue()[0])
Fatal(ctx) << "missing entry point symbol name";
- ctx.forEachSymtab([&](SymbolTable &symtab) {
+ ctx.forEachActiveSymtab([&](SymbolTable &symtab) {
symtab.entry = symtab.addGCRoot(symtab.mangle(arg->getValue()), true);
});
break;
@@ -657,7 +656,7 @@ void LinkerDriver::setMachine(MachineTypes machine) {
ctx.config.machine = machine;
- if (machine != ARM64X) {
+ if (!isArm64EC(machine)) {
ctx.symtab.machine = machine;
} else {
ctx.symtab.machine = ARM64EC;
@@ -979,7 +978,7 @@ void LinkerDriver::createImportLibrary(bool asLib) {
};
getExports(ctx.symtab, exports);
- if (ctx.hybridSymtab)
+ if (ctx.config.machine == ARM64X)
getExports(*ctx.hybridSymtab, nativeExports);
std::string libName = getImportName(asLib);
@@ -1383,13 +1382,13 @@ void LinkerDriver::maybeExportMinGWSymbols(const opt::InputArgList &args) {
return;
if (ctx.symtab.hadExplicitExports ||
- (ctx.hybridSymtab && ctx.hybridSymtab->hadExplicitExports))
+ (ctx.config.machine == ARM64X && ctx.hybridSymtab->hadExplicitExports))
return;
if (args.hasArg(OPT_exclude_all_symbols))
return;
}
- ctx.forEachSymtab([&](SymbolTable &symtab) {
+ ctx.forEachActiveSymtab([&](SymbolTable &symtab) {
AutoExporter exporter(symtab, excludedSymbols);
for (auto *arg : args.filtered(OPT_wholearchive_file))
@@ -2305,7 +2304,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (auto *arg = args.getLastArg(OPT_deffile)) {
// parseModuleDefs mutates Config object.
ctx.symtab.parseModuleDefs(arg->getValue());
- if (ctx.hybridSymtab) {
+ if (ctx.config.machine == ARM64X) {
// 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))
@@ -2332,7 +2331,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
}
// Handle /entry and /dll
- ctx.forEachSymtab([&](SymbolTable &symtab) {
+ ctx.forEachActiveSymtab([&](SymbolTable &symtab) {
llvm::TimeTraceScope timeScope("Entry point");
if (auto *arg = args.getLastArg(OPT_entry)) {
if (!arg->getValue()[0])
@@ -2364,7 +2363,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
llvm::TimeTraceScope timeScope("Delay load");
for (auto *arg : args.filtered(OPT_delayload)) {
config->delayLoads.insert(StringRef(arg->getValue()).lower());
- ctx.forEachSymtab([&](SymbolTable &symtab) {
+ ctx.forEachActiveSymtab([&](SymbolTable &symtab) {
if (symtab.machine == I386) {
symtab.delayLoadHelper = symtab.addGCRoot("___delayLoadHelper2 at 8");
} else {
@@ -2538,7 +2537,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
u->setWeakAlias(symtab.addUndefined(to));
}
}
+ });
+ ctx.forEachActiveSymtab([&](SymbolTable &symtab) {
// If any inputs are bitcode files, the LTO code generator may create
// references to library functions that are not explicit in the bitcode
// file's symbol table. If any of those library functions are defined in
@@ -2550,7 +2551,6 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
for (auto *s : lto::LTO::getRuntimeLibcallSymbols(TT))
symtab.addLibcall(s);
}
-
// Windows specific -- if __load_config_used can be resolved, resolve
// it.
if (symtab.findUnderscore("_load_config_used"))
@@ -2568,7 +2568,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// Handle /includeglob
for (StringRef pat : args::getStrings(args, OPT_incl_glob))
- ctx.forEachSymtab(
+ ctx.forEachActiveSymtab(
[&](SymbolTable &symtab) { symtab.addUndefinedGlob(pat); });
// Create wrapped symbols for -wrap option.
@@ -2685,12 +2685,12 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
// need to create a .lib file. In MinGW mode, we only do that when the
// -implib option is given explicitly, for compatibility with GNU ld.
if (config->dll || !ctx.symtab.exports.empty() ||
- (ctx.hybridSymtab && !ctx.hybridSymtab->exports.empty())) {
+ (ctx.config.machine == ARM64X && !ctx.hybridSymtab->exports.empty())) {
llvm::TimeTraceScope timeScope("Create .lib exports");
- ctx.forEachSymtab([](SymbolTable &symtab) { symtab.fixupExports(); });
+ ctx.forEachActiveSymtab([](SymbolTable &symtab) { symtab.fixupExports(); });
if (!config->noimplib && (!config->mingw || !config->implib.empty()))
createImportLibrary(/*asLib=*/false);
- ctx.forEachSymtab(
+ ctx.forEachActiveSymtab(
[](SymbolTable &symtab) { symtab.assignExportOrdinals(); });
}
@@ -2756,7 +2756,8 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (ctx.symtab.isEC())
ctx.symtab.initializeECThunks();
- ctx.forEachSymtab([](SymbolTable &symtab) { symtab.initializeLoadConfig(); });
+ ctx.forEachActiveSymtab(
+ [](SymbolTable &symtab) { symtab.initializeLoadConfig(); });
// Identify unreferenced COMDAT sections.
if (config->doGC) {
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index 7fb42bb681939..e10b6419b5ad5 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -137,10 +137,8 @@ void ArchiveFile::parse() {
ctx.symtab.addLazyArchive(this, sym);
// Read both EC and native symbols on ARM64X.
- if (!ctx.hybridSymtab)
- return;
archiveSymtab = &*ctx.hybridSymtab;
- } else if (ctx.hybridSymtab) {
+ } else {
// 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
// of the object containing a symbol to determine which symbol table to
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 8fb0ee4e890d6..d6f771284aa83 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -551,7 +551,7 @@ void SymbolTable::initializeLoadConfig() {
Warn(ctx) << "EC version of '_load_config_used' is missing";
return;
}
- if (ctx.hybridSymtab) {
+ if (ctx.config.machine == ARM64X) {
Warn(ctx) << "native version of '_load_config_used' is missing for "
"ARM64X target";
return;
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index f3cf4902e6ecf..db6133e20a037 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1374,7 +1374,7 @@ void Writer::createExportTable() {
}
}
}
- ctx.forEachSymtab([&](SymbolTable &symtab) {
+ ctx.forEachActiveSymtab([&](SymbolTable &symtab) {
if (symtab.edataStart) {
if (symtab.hadExplicitExports)
Warn(ctx) << "literal .edata sections override exports";
@@ -1776,7 +1776,8 @@ template <typename PEHeaderTy> void Writer::writeHeader() {
assert(coffHeaderOffset == buf - buffer->getBufferStart());
auto *coff = reinterpret_cast<coff_file_header *>(buf);
buf += sizeof(*coff);
- SymbolTable &symtab = ctx.hybridSymtab ? *ctx.hybridSymtab : ctx.symtab;
+ SymbolTable &symtab =
+ ctx.config.machine == ARM64X ? *ctx.hybridSymtab : ctx.symtab;
coff->Machine = symtab.isEC() ? AMD64 : symtab.machine;
coff->NumberOfSections = ctx.outputSections.size();
coff->Characteristics = IMAGE_FILE_EXECUTABLE_IMAGE;
@@ -2433,7 +2434,7 @@ void Writer::setECSymbols() {
return a.first->getRVA() < b.first->getRVA();
});
- ChunkRange &chpePdata = ctx.hybridSymtab ? hybridPdata : pdata;
+ ChunkRange &chpePdata = ctx.config.machine == ARM64X ? hybridPdata : pdata;
Symbol *rfeTableSym = ctx.symtab.findUnderscore("__arm64x_extra_rfe_table");
replaceSymbol<DefinedSynthetic>(rfeTableSym, "__arm64x_extra_rfe_table",
chpePdata.first);
@@ -2478,7 +2479,7 @@ void Writer::setECSymbols() {
delayIdata.getAuxIatCopy().empty() ? nullptr
: delayIdata.getAuxIatCopy().front());
- if (ctx.hybridSymtab) {
+ if (ctx.config.machine == ARM64X) {
// 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.
@@ -2868,7 +2869,7 @@ void Writer::fixTlsAlignment() {
}
void Writer::prepareLoadConfig() {
- ctx.forEachSymtab([&](SymbolTable &symtab) {
+ ctx.forEachActiveSymtab([&](SymbolTable &symtab) {
if (!symtab.loadConfigSym)
return;
@@ -2928,7 +2929,7 @@ void Writer::prepareLoadConfig(SymbolTable &symtab, T *loadConfig) {
IF_CONTAINS(CHPEMetadataPointer) {
// 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() &&
+ if (ctx.config.machine == ARM64X && !symtab.isEC() &&
ctx.symtab.loadConfigSize >=
offsetof(T, CHPEMetadataPointer) + sizeof(T::CHPEMetadataPointer)) {
OutputSection *sec =
diff --git a/lld/test/COFF/arm64ec-entry-mangle.test b/lld/test/COFF/arm64ec-entry-mangle.test
index 6db16ef218dc8..1f029077ba51d 100644
--- a/lld/test/COFF/arm64ec-entry-mangle.test
+++ b/lld/test/COFF/arm64ec-entry-mangle.test
@@ -97,7 +97,7 @@ RUN: not lld-link -machine:arm64ec -dll -out:test.dll demangled-func.obj loadcon
RUN: "-entry:#func" 2>&1 | FileCheck -check-prefix=FUNC-NOT-FOUND %s
RUN: not lld-link -machine:arm64ec -dll -out:test.dll demangled-func.obj loadconfig-arm64ec.obj \
RUN: -noentry "-export:#func" 2>&1 | FileCheck -check-prefix=FUNC-NOT-FOUND %s
-FUNC-NOT-FOUND: undefined symbol: #func
+FUNC-NOT-FOUND: undefined symbol: #func (EC symbol)
Verify that the linker recognizes the demangled x86_64 _DllMainCRTStartup.
RUN: lld-link -machine:arm64ec -dll -out:test.dll x64-dll-main.obj loadconfig-arm64ec.obj
diff --git a/lld/test/COFF/arm64ec-hybmp.s b/lld/test/COFF/arm64ec-hybmp.s
index 5fc24d4250704..670ee3926ab5c 100644
--- a/lld/test/COFF/arm64ec-hybmp.s
+++ b/lld/test/COFF/arm64ec-hybmp.s
@@ -62,7 +62,7 @@ thunk:
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows undef-func.s -o undef-func.obj
// RUN: not lld-link -machine:arm64ec -dll -noentry -out:test.dll undef-func.obj 2>&1 | FileCheck -check-prefix=UNDEF-FUNC %s
-// UNDEF-FUNC: error: undefined symbol: func
+// UNDEF-FUNC: error: undefined symbol: func (EC symbol)
#--- undef-thunk.s
.section .text,"xr",discard,func
@@ -79,7 +79,7 @@ func:
// RUN: llvm-mc -filetype=obj -triple=arm64ec-windows undef-thunk.s -o undef-thunk.obj
// RUN: not lld-link -machine:arm64ec -dll -noentry -out:test.dll undef-thunk.obj 2>&1 | FileCheck -check-prefix=UNDEF-THUNK %s
-// UNDEF-THUNK: error: undefined symbol: thunk
+// UNDEF-THUNK: error: undefined symbol: thunk (EC symbol)
#--- invalid-type.s
.section .text,"xr",discard,func
diff --git a/lld/test/COFF/arm64ec-lib.test b/lld/test/COFF/arm64ec-lib.test
index 8698a5ceccbe7..1e6fa60209d94 100644
--- a/lld/test/COFF/arm64ec-lib.test
+++ b/lld/test/COFF/arm64ec-lib.test
@@ -29,11 +29,13 @@ RUN: lld-link -machine:arm64ec -dll -noentry -out:test2.dll symref-arm64ec.obj s
Verify that both native and EC symbols can be referenced in a hybrid target.
RUN: lld-link -machine:arm64x -dll -noentry -out:test3.dll symref-arm64ec.obj nsymref-aarch64.obj sym-arm64ec.lib \
RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj
+RUN: lld-link -machine:arm64ec -dll -noentry -out:test3ec.dll symref-arm64ec.obj nsymref-aarch64.obj sym-arm64ec.lib \
+RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj
Ensure that an EC symbol is not resolved using a regular symbol map.
RUN: not lld-link -machine:arm64ec -dll -noentry -out:test-err.dll nsymref-arm64ec.obj sym-arm64ec.lib loadconfig-arm64ec.obj 2>&1 |\
RUN: FileCheck --check-prefix=ERR %s
-ERR: error: undefined symbol: nsym
+ERR: error: undefined symbol: nsym (EC symbol)
Verify that a library symbol can be referenced, even if its name conflicts with an anti-dependency alias.
RUN: lld-link -machine:arm64ec -dll -noentry -out:ref-alias-1.dll ref-alias.obj func.lib loadconfig-arm64ec.obj
diff --git a/lld/test/COFF/arm64ec-patchable-thunks.test b/lld/test/COFF/arm64ec-patchable-thunks.test
index 1e1ff1f7f2ee4..593405775ba87 100644
--- a/lld/test/COFF/arm64ec-patchable-thunks.test
+++ b/lld/test/COFF/arm64ec-patchable-thunks.test
@@ -57,7 +57,7 @@ RUN: llvm-readobj --coff-load-config test3.dll | FileCheck -check-prefix=PATCH-C
RUN: not lld-link -out:test4.dll -machine:arm64ec test-sec.obj loadconfig-arm64ec.obj -dll -noentry 2>&1 | FileCheck --check-prefix=ERR %s
-ERR: error: undefined symbol: EXP+#patchable_func
+ERR: error: undefined symbol: EXP+#patchable_func (EC symbol)
RUN: lld-link -out:testx.dll -machine:arm64x arm64ec-patchable.obj test-sec.obj \
diff --git a/lld/test/COFF/arm64ec-range-thunks.s b/lld/test/COFF/arm64ec-range-thunks.s
index dcfa6365b4e3a..955e19c30c2ae 100644
--- a/lld/test/COFF/arm64ec-range-thunks.s
+++ b/lld/test/COFF/arm64ec-range-thunks.s
@@ -79,7 +79,11 @@
# RUN: -out:testx2.dll -verbose 2>&1 | FileCheck -check-prefix=VERBOSEX %s
# VERBOSEX: Added 5 thunks with margin {{.*}} in 1 passes
+# RUN: lld-link -machine:arm64ec -noentry -dll funcs-arm64ec.obj funcs-aarch64.obj loadconfig-arm64.obj loadconfig-arm64ec.obj \
+# RUN: -out:testx2ec.dll -verbose 2>&1 | FileCheck -check-prefix=VERBOSEX %s
+
# RUN: llvm-objdump -d testx2.dll | FileCheck --check-prefix=DISASMX %s
+# RUN: llvm-objdump -d testx2ec.dll | FileCheck --check-prefix=DISASMX %s
# DISASMX: Disassembly of section .code1:
# DISASMX-EMPTY:
@@ -126,6 +130,7 @@
# DISASMX-NEXT: 180016010: d61f0200 br x16
# RUN: llvm-readobj --coff-load-config testx2.dll | FileCheck --check-prefix=LOADCFGX2 %s
+# RUN: llvm-readobj --coff-load-config testx2ec.dll | FileCheck --check-prefix=LOADCFGX2 %s
# LOADCFGX2: CodeMap [
# LOADCFGX2-NEXT: 0x4000 - 0x4014 ARM64EC
diff --git a/lld/test/COFF/arm64ec.test b/lld/test/COFF/arm64ec.test
index 75288e97e598d..ea92689250063 100644
--- a/lld/test/COFF/arm64ec.test
+++ b/lld/test/COFF/arm64ec.test
@@ -35,14 +35,15 @@ RUN: llvm-readobj --file-headers test.dll | FileCheck -check-prefix=ARM64X-HEADE
RUN: llvm-readobj --hex-dump=.data test.dll | FileCheck -check-prefix=ARM64X-DATA %s
ARM64X-DATA: 03030303 01010101 02020202
+RUN: lld-link -out:test.dll -machine:arm64ec x86_64-data-sym.obj arm64-data-sym.obj \
+RUN: arm64ec-data-sym.obj arm64x-resource.obj -dll -noentry
+RUN: llvm-readobj --file-headers test.dll | FileCheck -check-prefix=ARM64EC-HEADER %s
+RUN: llvm-readobj --hex-dump=.data test.dll | FileCheck -check-prefix=ARM64X-DATA %s
+
RUN: not lld-link -out:test.dll -machine:arm64 arm64-data-sym.obj arm64ec-data-sym.obj \
RUN: -dll -noentry 2>&1 | FileCheck -check-prefix=INCOMPAT1 %s
INCOMPAT1: lld-link: error: arm64ec-data-sym.obj: machine type arm64ec conflicts with arm64
-RUN: not lld-link -out:test.dll -machine:arm64ec arm64ec-data-sym.obj arm64-data-sym.obj \
-RUN: -dll -noentry 2>&1 | FileCheck -check-prefix=INCOMPAT2 %s
-INCOMPAT2: lld-link: error: arm64-data-sym.obj: machine type arm64 conflicts with arm64ec
-
RUN: not lld-link -out:test.dll -machine:arm64 arm64-data-sym.obj x86_64-data-sym.obj \
RUN: -dll -noentry 2>&1 | FileCheck -check-prefix=INCOMPAT3 %s
INCOMPAT3: lld-link: error: x86_64-data-sym.obj: machine type x64 conflicts with arm64
diff --git a/lld/test/COFF/arm64x-altnames.s b/lld/test/COFF/arm64x-altnames.s
index 43a3f89db9a03..db348f85b6b00 100644
--- a/lld/test/COFF/arm64x-altnames.s
+++ b/lld/test/COFF/arm64x-altnames.s
@@ -10,6 +10,8 @@
// RUN: not lld-link -out:out.dll -machine:arm64x -dll -noentry test-arm64.obj test-arm64ec.obj -alternatename:sym=altsym \
// RUN: 2>&1 | FileCheck --check-prefix=ERR-NATIVE %s
+// RUN: not lld-link -out:out.dll -machine:arm64ec -dll -noentry test-arm64.obj test-arm64ec.obj -alternatename:sym=altsym \
+// RUN: 2>&1 | FileCheck --check-prefix=ERR-NATIVE %s
// ERR-NATIVE-NOT: test-arm64ec.obj
// ERR-NATIVE: lld-link: error: undefined symbol: sym (native symbol)
@@ -20,9 +22,13 @@
// RUN: not lld-link -out:out.dll -machine:arm64x -dll -noentry test-arm64.obj test-arm64ec.obj drectve-arm64ec.obj \
// RUN: 2>&1 | FileCheck --check-prefix=ERR-NATIVE %s
+// RUN: not lld-link -out:out.dll -machine:arm64ec -dll -noentry test-arm64.obj test-arm64ec.obj drectve-arm64ec.obj \
+// RUN: 2>&1 | FileCheck --check-prefix=ERR-NATIVE %s
// RUN: not lld-link -out:out.dll -machine:arm64x -dll -noentry test-arm64.obj test-arm64ec.obj drectve-arm64.obj \
// RUN: 2>&1 | FileCheck --check-prefix=ERR-EC %s
+// RUN: not lld-link -out:out.dll -machine:arm64ec -dll -noentry test-arm64.obj test-arm64ec.obj drectve-arm64.obj \
+// RUN: 2>&1 | FileCheck --check-prefix=ERR-EC %s
// ERR-EC-NOT: test-arm64.obj
// ERR-EC: lld-link: error: undefined symbol: sym (EC symbol)
diff --git a/lld/test/COFF/arm64x-buildid.s b/lld/test/COFF/arm64x-buildid.s
index 99f50073eaa69..857bcae2c0566 100644
--- a/lld/test/COFF/arm64x-buildid.s
+++ b/lld/test/COFF/arm64x-buildid.s
@@ -6,6 +6,9 @@
# RUN: llvm-readobj --hex-dump=.test %t.dll | FileCheck %s
# CHECK: 0x180003000 3c100000 3c100000
+# RUN: lld-link -machine:arm64ec -dll -noentry %t-arm64.obj %t-arm64ec.obj -debug -build-id -Brepro -out:%t-ec.dll
+# RUN: llvm-readobj --hex-dump=.test %t-ec.dll | FileCheck %s
+
.section .test,"dr"
.rva __buildid
diff --git a/lld/test/COFF/arm64x-comm.s b/lld/test/COFF/arm64x-comm.s
index 830e3d3fdaaa1..b950af5b70a44 100644
--- a/lld/test/COFF/arm64x-comm.s
+++ b/lld/test/COFF/arm64x-comm.s
@@ -8,6 +8,9 @@
// RUN: llvm-readobj --hex-dump=.test %t.dll | FileCheck %s
// CHECK: 0x180004000 10200000 18200000 20200000 28200000
+// RUN: lld-link -machine:arm64ec -lldmingw -dll -noentry -out:%t-ec.dll %t-arm64.obj %t-arm64ec.obj
+// RUN: llvm-readobj --hex-dump=.test %t-ec.dll | FileCheck %s
+
.data
.word 0
diff --git a/lld/test/COFF/arm64x-crt-sec.s b/lld/test/COFF/arm64x-crt-sec.s
index 5be70a1845f12..45141ec238aea 100644
--- a/lld/test/COFF/arm64x-crt-sec.s
+++ b/lld/test/COFF/arm64x-crt-sec.s
@@ -17,6 +17,9 @@
// RUN: lld-link -out:out3.dll -machine:arm64x -dll -noentry crt2-amd64.obj crt1-arm64ec.obj crt2-arm64.obj crt1-arm64.obj
// RUN: llvm-readobj --hex-dump=.CRT out3.dll | FileCheck %s
+// RUN: lld-link -out:out4.dll -machine:arm64ec -dll -noentry crt2-amd64.obj crt1-arm64ec.obj crt2-arm64.obj crt1-arm64.obj
+// RUN: llvm-readobj --hex-dump=.CRT out4.dll | FileCheck %s
+
// CHECK: 0x180002000 01000000 00000000 02000000 00000000
// CHECK-NEXT: 0x180002010 03000000 00000000 11000000 00000000
// CHECK-NEXT: 0x180002020 12000000 00000000 13000000 00000000
diff --git a/lld/test/COFF/arm64x-ctors-sec.s b/lld/test/COFF/arm64x-ctors-sec.s
index 283d5f045260d..3295b3f20b8b2 100644
--- a/lld/test/COFF/arm64x-ctors-sec.s
+++ b/lld/test/COFF/arm64x-ctors-sec.s
@@ -22,6 +22,10 @@
// RUN: ctor2-arm64.obj ctor1-arm64ec.obj ctor2-amd64.obj ctor1-arm64.obj
// RUN: llvm-readobj --hex-dump=.rdata --hex-dump=.test out3.dll | FileCheck %s
+// RUN: lld-link -out:out4.dll -machine:arm64ec -lldmingw -dll -noentry test-arm64.obj test-arm64ec.obj \
+// RUN: ctor2-arm64.obj ctor1-arm64ec.obj ctor2-amd64.obj ctor1-arm64.obj
+// RUN: llvm-readobj --hex-dump=.rdata --hex-dump=.test out4.dll | FileCheck %s
+
// CHECK: Hex dump of section '.rdata':
// CHECK-NEXT: 0x180001000 ffffffff ffffffff 01000000 00000000
// CHECK-NEXT: 0x180001010 02000000 00000000 03000000 00000000
diff --git a/lld/test/COFF/arm64x-guardcf.s b/lld/test/COFF/arm64x-guardcf.s
index 750bf0b3862c5..9d307bde276e1 100644
--- a/lld/test/COFF/arm64x-guardcf.s
+++ b/lld/test/COFF/arm64x-guardcf.s
@@ -16,7 +16,7 @@
// RUN: lld-link -dll -noentry -machine:arm64x func-gfids-arm64.obj func-gfids-arm64ec.obj func-amd64.obj -guard:cf -out:out.dll \
// RUN: loadconfig-arm64ec.obj loadconfig-arm64.obj
-// RUN: llvm-readobj --coff-load-config out.dll | FileCheck --check-prefix=LOADCFG %s
+// RUN: llvm-readobj --coff-load-config out.dll | FileCheck --check-prefixes=LOADCFG,LOADCFGX %s
// LOADCFG: LoadConfig [
// LOADCFG: GuardCFFunctionCount: 3
@@ -31,28 +31,36 @@
// LOADCFG-NEXT: 0x180002000
// LOADCFG-NEXT: 0x180003000
// LOADCFG-NEXT: ]
-// LOADCFG: HybridObject {
-// LOADCFG: LoadConfig [
-// LOADCFG: GuardCFFunctionCount: 3
-// LOADCFG-NEXT: GuardFlags [ (0x10500)
-// LOADCFG-NEXT: CF_FUNCTION_TABLE_PRESENT (0x400)
-// LOADCFG-NEXT: CF_INSTRUMENTED (0x100)
-// LOADCFG-NEXT: CF_LONGJUMP_TABLE_PRESENT (0x10000)
-// LOADCFG-NEXT: ]
-// LOADCFG: ]
-// LOADCFG: GuardFidTable [
-// LOADCFG-NEXT: 0x180001000
-// LOADCFG-NEXT: 0x180002000
-// LOADCFG-NEXT: 0x180003000
-// LOADCFG-NEXT: ]
-// LOADCFG: ]
+// LOADCFGX: HybridObject {
+// LOADCFGX: LoadConfig [
+// LOADCFGX: GuardCFFunctionCount: 3
+// LOADCFG-NEXTX: GuardFlags [ (0x10500)
+// LOADCFG-NEXTX: CF_FUNCTION_TABLE_PRESENT (0x400)
+// LOADCFG-NEXTX: CF_INSTRUMENTED (0x100)
+// LOADCFG-NEXTX: CF_LONGJUMP_TABLE_PRESENT (0x10000)
+// LOADCFG-NEXTX: ]
+// LOADCFGX: ]
+// LOADCFGX: GuardFidTable [
+// LOADCFG-NEXTX: 0x180001000
+// LOADCFG-NEXTX: 0x180002000
+// LOADCFG-NEXTX: 0x180003000
+// LOADCFG-NEXTX: ]
+// LOADCFGX: ]
+
+// RUN: lld-link -dll -noentry -machine:arm64ec func-gfids-arm64.obj func-gfids-arm64ec.obj func-amd64.obj -guard:cf -out:out-ec.dll \
+// RUN: loadconfig-arm64ec.obj loadconfig-arm64.obj
+// RUN: llvm-readobj --coff-load-config out-ec.dll | FileCheck --check-prefix=LOADCFG %s
// Check that exports from both views are present in CF guard tables.
// RUN: lld-link -dll -noentry -machine:arm64x func-exp-arm64.obj func-exp-arm64ec.obj -guard:cf -out:out-exp.dll \
// RUN: loadconfig-arm64ec.obj loadconfig-arm64.obj
-// RUN: llvm-readobj --coff-load-config out-exp.dll | FileCheck --check-prefix=LOADCFG %s
+// RUN: llvm-readobj --coff-load-config out-exp.dll | FileCheck --check-prefixes=LOADCFG,LOADCFGX %s
+
+// RUN: lld-link -dll -noentry -machine:arm64ec func-exp-arm64.obj func-exp-arm64ec.obj -guard:cf -out:out-exp-ec.dll \
+// RUN: loadconfig-arm64ec.obj loadconfig-arm64.obj
+// RUN: llvm-readobj --coff-load-config out-exp-ec.dll | FileCheck --check-prefixes=LOADCFG %s
// Check that entry points from both views are present in CF guard tables.
diff --git a/lld/test/COFF/arm64x-import.test b/lld/test/COFF/arm64x-import.test
index 7441c71d87710..612b5f9b71de1 100644
--- a/lld/test/COFF/arm64x-import.test
+++ b/lld/test/COFF/arm64x-import.test
@@ -56,7 +56,7 @@ DISASM-12T-NEXT: 180002040: d65f03c0 ret
DISASM-12T-NEXT: ...
DISASM-12T-NEXT: 180003000: ff 25 fa 0f 00 00 jmpq *0xffa(%rip) # 0x180004000
-RUN: llvm-readobj --coff-imports test-12-thunks.dll | FileCheck --check-prefix=IMPORTS-12 %s
+RUN: llvm-readobj --coff-imports test-12-thunks.dll | FileCheck --check-prefixes=IMPORTS-12,IMPORTS-12-EC %s
IMPORTS-12: Import {
IMPORTS-12-NEXT: Name: test.dll
IMPORTS-12-NEXT: ImportLookupTableRVA: 0x5348
@@ -65,13 +65,13 @@ IMPORTS-12-NEXT: Symbol: func1 (0)
IMPORTS-12-NEXT: Symbol: func2 (0)
IMPORTS-12-NEXT: }
IMPORTS-12-NEXT: HybridObject {
-IMPORTS-12: Import {
-IMPORTS-12-NEXT: Name: test.dll
-IMPORTS-12-NEXT: ImportLookupTableRVA: 0x5348
-IMPORTS-12-NEXT: ImportAddressTableRVA: 0x4000
-IMPORTS-12-NEXT: Symbol: func1 (0)
-IMPORTS-12-NEXT: Symbol: func2 (0)
-IMPORTS-12-NEXT: }
+IMPORTS-12-EC: Import {
+IMPORTS-12-EC-NEXT: Name: test.dll
+IMPORTS-12-EC-NEXT: ImportLookupTableRVA: 0x5348
+IMPORTS-12-EC-NEXT: ImportAddressTableRVA: 0x4000
+IMPORTS-12-EC-NEXT: Symbol: func1 (0)
+IMPORTS-12-EC-NEXT: Symbol: func2 (0)
+IMPORTS-12-EC-NEXT:}
IMPORTS-12-NEXT: }
RUN: llvm-readobj --hex-dump=.test test-12-thunks.dll | FileCheck --check-prefix=FUNC-12-THUNKS %s
@@ -81,6 +81,13 @@ FUNC-12-THUNKS-NEXT: 0x180009010 08600000 08400000
RUN: llvm-readobj --hex-dump=.testa test-12-thunks.dll | FileCheck --check-prefix=FUNC-12-THUNKSA %s
FUNC-12-THUNKSA: 0x18000a000 00400000 08400000 00100000
+RUN: lld-link -machine:arm64ec -dll -noentry -out:test-12-thunks-ec.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
+RUN: icall.obj func12-thunks-arm64ec.obj func12-thunks-arm64.obj imp-arm64ec.lib imp-arm64.lib
+RUN: llvm-objdump -d test-12-thunks-ec.dll | FileCheck --check-prefix=DISASM-12T %s
+RUN: llvm-readobj --hex-dump=.test test-12-thunks-ec.dll | FileCheck --check-prefix=FUNC-12-THUNKS %s
+RUN: llvm-readobj --hex-dump=.testa test-12-thunks-ec.dll | FileCheck --check-prefix=FUNC-12-THUNKSA %s
+RUN: llvm-readobj --coff-imports test-12-thunks-ec.dll | FileCheck --check-prefix=IMPORTS-12-EC %s
+
# If the ordinals of named imports don't match, use the EC value.
@@ -146,7 +153,7 @@ IMPORTS-ORD2-NEXT: }
RUN: lld-link -machine:arm64x -dll -noentry -out:test2.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func12-arm64ec.obj func123-arm64.obj imp-arm64x.lib
-RUN: llvm-readobj --coff-imports test2.dll | FileCheck --check-prefix=IMPORTS-123-12 %s
+RUN: llvm-readobj --coff-imports test2.dll | FileCheck --check-prefixes=IMPORTS-123-12,IMPORTS-123-12-EC %s
IMPORTS-123-12: Import {
IMPORTS-123-12-NEXT: Name: test.dll
IMPORTS-123-12-NEXT: ImportLookupTableRVA: 0x3338
@@ -156,13 +163,13 @@ IMPORTS-123-12-NEXT: Symbol: func1 (0)
IMPORTS-123-12-NEXT: Symbol: func2 (0)
IMPORTS-123-12-NEXT: }
IMPORTS-123-12-NEXT: HybridObject {
-IMPORTS-123-12: Import {
-IMPORTS-123-12-NEXT: Name: test.dll
-IMPORTS-123-12-NEXT: ImportLookupTableRVA: 0x3340
-IMPORTS-123-12-NEXT: ImportAddressTableRVA: 0x2008
-IMPORTS-123-12-NEXT: Symbol: func1 (0)
-IMPORTS-123-12-NEXT: Symbol: func2 (0)
-IMPORTS-123-12-NEXT: }
+IMPORTS-123-12-EC: Import {
+IMPORTS-123-12-EC-NEXT: Name: test.dll
+IMPORTS-123-12-EC-NEXT: ImportLookupTableRVA: 0x3340
+IMPORTS-123-12-EC-NEXT: ImportAddressTableRVA: 0x2008
+IMPORTS-123-12-EC-NEXT: Symbol: func1 (0)
+IMPORTS-123-12-EC-NEXT: Symbol: func2 (0)
+IMPORTS-123-12-EC-NEXT:}
IMPORTS-123-12-NEXT: }
RUN: llvm-readobj --hex-dump=.test test2.dll | FileCheck --check-prefix=TEST-123-12 %s
@@ -175,13 +182,20 @@ RUN: llvm-readobj --hex-dump=.rdata test2.dll | FileCheck --check-prefix=TEST-12
TEST-123-12AUX: 0x180004000 00000000 00000000 08100080 01000000
TEST-123-12AUX-NEXT: 0x180004010 1c100080 01000000 00000000 00000000
+RUN: lld-link -machine:arm64ec -dll -noentry -out:test2-ec.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
+RUN: icall.obj func12-arm64ec.obj func123-arm64.obj imp-arm64x.lib
+RUN: llvm-readobj --coff-imports test2-ec.dll | FileCheck --check-prefix=IMPORTS-123-12-EC %s
+RUN: llvm-readobj --hex-dump=.test test2-ec.dll | FileCheck --check-prefix=TEST-123-12 %s
+RUN: llvm-readobj --hex-dump=.testa test2-ec.dll | FileCheck --check-prefix=TEST-123-12A %s
+RUN: llvm-readobj --hex-dump=.rdata test2-ec.dll | FileCheck --check-prefix=TEST-123-12AUX %s
+
# Link to the imported func1 and func2 from both native and EC code, and func3 from EC code.
RUN: lld-link -machine:arm64x -dll -noentry -out:func-12-123.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func123-arm64ec.obj func12-arm64.obj imp-arm64x.lib
-RUN: llvm-readobj --coff-imports func-12-123.dll | FileCheck --check-prefix=IMPORTS-12-123 %s
+RUN: llvm-readobj --coff-imports func-12-123.dll | FileCheck --check-prefixes=IMPORTS-12-123,IMPORTS-12-123-EC %s
IMPORTS-12-123: Import {
IMPORTS-12-123-NEXT: Name: test.dll
IMPORTS-12-123-NEXT: ImportLookupTableRVA: 0x3338
@@ -190,14 +204,14 @@ IMPORTS-12-123-NEXT: Symbol: func1 (0)
IMPORTS-12-123-NEXT: Symbol: func2 (0)
IMPORTS-12-123-NEXT: }
IMPORTS-12-123-NEXT: HybridObject {
-IMPORTS-12-123: Import {
-IMPORTS-12-123-NEXT: Name: test.dll
-IMPORTS-12-123-NEXT: ImportLookupTableRVA: 0x3338
-IMPORTS-12-123-NEXT: ImportAddressTableRVA: 0x2000
-IMPORTS-12-123-NEXT: Symbol: func1 (0)
-IMPORTS-12-123-NEXT: Symbol: func2 (0)
-IMPORTS-12-123-NEXT: Symbol: func3 (0)
-IMPORTS-12-123-NEXT: }
+IMPORTS-12-123-EC: Import {
+IMPORTS-12-123-EC-NEXT: Name: test.dll
+IMPORTS-12-123-EC-NEXT: ImportLookupTableRVA: 0x3338
+IMPORTS-12-123-EC-NEXT: ImportAddressTableRVA: 0x2000
+IMPORTS-12-123-EC-NEXT: Symbol: func1 (0)
+IMPORTS-12-123-EC-NEXT: Symbol: func2 (0)
+IMPORTS-12-123-EC-NEXT: Symbol: func3 (0)
+IMPORTS-12-123-EC-NEXT:}
IMPORTS-12-123-NEXT: }
RUN: llvm-readobj --hex-dump=.test func-12-123.dll | FileCheck --check-prefix=TEST-12-123 %s
@@ -211,6 +225,12 @@ RUN: llvm-readobj --hex-dump=.rdata func-12-123.dll | FileCheck --check-prefix=T
TEST-12-123AUX: 0x180004000 08100080 01000000 1c100080 01000000
TEST-12-123AUX-NEXT: 0x180004010 30100080 01000000 00000000 00000000
+RUN: lld-link -machine:arm64ec -dll -noentry -out:func-12-123-ec.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
+RUN: icall.obj func123-arm64ec.obj func12-arm64.obj imp-arm64x.lib
+RUN: llvm-readobj --coff-imports func-12-123-ec.dll | FileCheck --check-prefix=IMPORTS-12-123-EC %s
+RUN: llvm-readobj --hex-dump=.test func-12-123-ec.dll | FileCheck --check-prefix=TEST-12-123 %s
+RUN: llvm-readobj --hex-dump=.testa func-12-123-ec.dll | FileCheck --check-prefix=TEST-12-123A %s
+RUN: llvm-readobj --hex-dump=.rdata func-12-123-ec.dll | FileCheck --check-prefix=TEST-12-123AUX %s
# Link to the imported func2 and func3 from both native and EC code, func4 from native code,
# and func1 from EC code.
@@ -218,7 +238,7 @@ TEST-12-123AUX-NEXT: 0x180004010 30100080 01000000 00000000 00000000
RUN: lld-link -machine:arm64x -dll -noentry -out:test-234-123.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func123-arm64ec.obj func234-arm64.obj imp-arm64x.lib
-RUN: llvm-readobj --coff-imports test-234-123.dll | FileCheck --check-prefix=IMPORTS-234-123 %s
+RUN: llvm-readobj --coff-imports test-234-123.dll | FileCheck --check-prefixes=IMPORTS-234-123,IMPORTS-234-123-EC %s
IMPORTS-234-123: Import {
IMPORTS-234-123-NEXT: Name: test.dll
IMPORTS-234-123-NEXT: ImportLookupTableRVA: 0x3338
@@ -228,14 +248,14 @@ IMPORTS-234-123-NEXT: Symbol: func2 (0)
IMPORTS-234-123-NEXT: Symbol: func3 (0)
IMPORTS-234-123-NEXT: }
IMPORTS-234-123-NEXT: HybridObject {
-IMPORTS-234-123: Import {
-IMPORTS-234-123-NEXT: Name: test.dll
-IMPORTS-234-123-NEXT: ImportLookupTableRVA: 0x3340
-IMPORTS-234-123-NEXT: ImportAddressTableRVA: 0x2008
-IMPORTS-234-123-NEXT: Symbol: func2 (0)
-IMPORTS-234-123-NEXT: Symbol: func3 (0)
-IMPORTS-234-123-NEXT: Symbol: func1 (0)
-IMPORTS-234-123-NEXT: }
+IMPORTS-234-123-EC: Import {
+IMPORTS-234-123-EC-NEXT: Name: test.dll
+IMPORTS-234-123-EC-NEXT: ImportLookupTableRVA: 0x3340
+IMPORTS-234-123-EC-NEXT: ImportAddressTableRVA: 0x2008
+IMPORTS-234-123-EC-NEXT: Symbol: func2 (0)
+IMPORTS-234-123-EC-NEXT: Symbol: func3 (0)
+IMPORTS-234-123-EC-NEXT: Symbol: func1 (0)
+IMPORTS-234-123-EC-NEXT:}
IMPORTS-234-123-NEXT: }
RUN: llvm-readobj --hex-dump=.test test-234-123.dll | FileCheck --check-prefix=TEST-234-123 %s
@@ -245,13 +265,19 @@ TEST-234-123-NEXT: 0x180007010 10400000 1020000
RUN: llvm-readobj --hex-dump=.testa test-234-123.dll | FileCheck --check-prefix=TEST-234-123A %s
TEST-234-123A: 0x180008000 08200000 10200000 00200000
+RUN: lld-link -machine:arm64ec -dll -noentry -out:test-234-123-ec.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
+RUN: icall.obj func123-arm64ec.obj func234-arm64.obj imp-arm64x.lib
+RUN: llvm-readobj --coff-imports test-234-123-ec.dll | FileCheck --check-prefix=IMPORTS-234-123-EC %s
+RUN: llvm-readobj --hex-dump=.test test-234-123-ec.dll | FileCheck --check-prefix=TEST-234-123 %s
+RUN: llvm-readobj --hex-dump=.testa test-234-123-ec.dll | FileCheck --check-prefix=TEST-234-123A %s
+
# Link to the imported func3 and func4 from native code, and func1 and func2 from EC code.
RUN: lld-link -machine:arm64x -dll -noentry -out:test-34-12.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: icall.obj func12o-arm64ec.obj func34o-arm64.obj imp-arm64x.lib imp2.lib
-RUN: llvm-readobj --coff-imports test-34-12.dll | FileCheck --check-prefix=IMPORTS-34-12 %s
+RUN: llvm-readobj --coff-imports test-34-12.dll | FileCheck --check-prefixes=IMPORTS-34-12,IMPORTS-34-12-EC %s
IMPORTS-34-12: Import {
IMPORTS-34-12-NEXT: Name: test.dll
IMPORTS-34-12-NEXT: ImportLookupTableRVA: 0x3350
@@ -266,19 +292,19 @@ IMPORTS-34-12-NEXT: ImportAddressTableRVA: 0x2028
IMPORTS-34-12-NEXT: Symbol: otherfunc (0)
IMPORTS-34-12-NEXT: }
IMPORTS-34-12-NEXT: HybridObject {
-IMPORTS-34-12: Import {
-IMPORTS-34-12-NEXT: Name: test.dll
-IMPORTS-34-12-NEXT: ImportLookupTableRVA: 0x3360
-IMPORTS-34-12-NEXT: ImportAddressTableRVA: 0x2010
-IMPORTS-34-12-NEXT: Symbol: func1 (0)
-IMPORTS-34-12-NEXT: Symbol: func2 (0)
-IMPORTS-34-12-NEXT: }
-IMPORTS-34-12-NEXT: Import {
-IMPORTS-34-12-NEXT: Name: test2.dll
-IMPORTS-34-12-NEXT: ImportLookupTableRVA: 0x3378
-IMPORTS-34-12-NEXT: ImportAddressTableRVA: 0x2028
-IMPORTS-34-12-NEXT: Symbol: otherfunc (0)
-IMPORTS-34-12-NEXT: }
+IMPORTS-34-12-EC: Import {
+IMPORTS-34-12-EC-NEXT: Name: test.dll
+IMPORTS-34-12-EC-NEXT: ImportLookupTableRVA: 0x3360
+IMPORTS-34-12-EC-NEXT: ImportAddressTableRVA: 0x2010
+IMPORTS-34-12-EC-NEXT: Symbol: func1 (0)
+IMPORTS-34-12-EC-NEXT: Symbol: func2 (0)
+IMPORTS-34-12-EC-NEXT:}
+IMPORTS-34-12-EC-NEXT:Import {
+IMPORTS-34-12-EC-NEXT: Name: test2.dll
+IMPORTS-34-12-EC-NEXT: ImportLookupTableRVA: 0x3378
+IMPORTS-34-12-EC-NEXT: ImportAddressTableRVA: 0x2028
+IMPORTS-34-12-EC-NEXT: Symbol: otherfunc (0)
+IMPORTS-34-12-EC-NEXT:}
IMPORTS-34-12-NEXT: }
RUN: llvm-readobj --hex-dump=.test test-34-12.dll | FileCheck --check-prefix=TEST-23-12 %s
@@ -288,6 +314,12 @@ TEST-23-12-NEXT: 0x180007010 28400000 28200000
RUN: llvm-readobj --hex-dump=.testa test-34-12.dll | FileCheck --check-prefix=TEST-23-12A %s
TEST-23-12A: 0x180008000 00200000 08200000 28200000
+RUN: lld-link -machine:arm64ec -dll -noentry -out:test-34-12-ec.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
+RUN: icall.obj func12o-arm64ec.obj func34o-arm64.obj imp-arm64x.lib imp2.lib
+RUN: llvm-readobj --coff-imports test-34-12-ec.dll | FileCheck --check-prefix=IMPORTS-34-12-EC %s
+RUN: llvm-readobj --hex-dump=.test test-34-12-ec.dll | FileCheck --check-prefix=TEST-23-12 %s
+RUN: llvm-readobj --hex-dump=.testa test-34-12-ec.dll | FileCheck --check-prefix=TEST-23-12A %s
+
# Link only to imported EC functions, with no native imports.
@@ -335,7 +367,7 @@ IMPORTS-EC12-NEXT: }
RUN: lld-link -machine:arm64x -dll -noentry -out:test-n12.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
RUN: func12-arm64.obj imp-arm64x.lib
-RUN: llvm-readobj --coff-imports test-n12.dll | FileCheck --check-prefix=IMPORTS-N12 %s
+RUN: llvm-readobj --coff-imports test-n12.dll | FileCheck --check-prefixes=IMPORTS-N12,IMPORTS-N12-EC %s
IMPORTS-N12: Arch: aarch64
IMPORTS-N12-NEXT: AddressSize: 64bit
@@ -347,16 +379,20 @@ IMPORTS-N12-NEXT: Symbol: func1 (0)
IMPORTS-N12-NEXT: Symbol: func2 (0)
IMPORTS-N12-NEXT: }
IMPORTS-N12-NEXT: HybridObject {
-IMPORTS-N12-NEXT: Format: COFF-ARM64EC
-IMPORTS-N12-NEXT: Arch: aarch64
-IMPORTS-N12-NEXT: AddressSize: 64bit
-IMPORTS-N12-NEXT: Import {
-IMPORTS-N12-NEXT: Name: test.dll
-IMPORTS-N12-NEXT: ImportLookupTableRVA: 0x2340
-IMPORTS-N12-NEXT: ImportAddressTableRVA: 0x1010
-IMPORTS-N12-NEXT: }
+IMPORTS-N12-EC: Format: COFF-ARM64EC
+IMPORTS-N12-EC-NEXT: Arch: aarch64
+IMPORTS-N12-EC-NEXT: AddressSize: 64bit
+IMPORTS-N12-EC-NEXT: Import {
+IMPORTS-N12-EC-NEXT: Name: test.dll
+IMPORTS-N12-EC-NEXT: ImportLookupTableRVA: 0x2340
+IMPORTS-N12-EC-NEXT: ImportAddressTableRVA: 0x1010
+IMPORTS-N12-EC-NEXT: }
IMPORTS-N12-NEXT: }
+RUN: lld-link -machine:arm64ec -dll -noentry -out:test-n12-ec.dll loadconfig-arm64.obj loadconfig-arm64ec.obj \
+RUN: func12-arm64.obj imp-arm64x.lib
+RUN: llvm-readobj --coff-imports test-n12-ec.dll | FileCheck --check-prefix=IMPORTS-N12-EC %s
+
RUN: lld-link -machine:arm64x -dll -noentry -out:test-dup.dll loadconfig-arm64.obj loadconfig-arm64ec.obj icall.obj \
RUN: func12-arm64ec.obj func34-arm64.obj dup.lib
diff --git a/lld/test/COFF/arm64x-symtab.s b/lld/test/COFF/arm64x-symtab.s
index c634f8a6ed4c5..176e81a23be16 100644
--- a/lld/test/COFF/arm64x-symtab.s
+++ b/lld/test/COFF/arm64x-symtab.s
@@ -18,6 +18,8 @@
// RUN: not lld-link -machine:arm64x -dll -noentry -out:err1.dll symref-aarch64.obj sym-arm64ec.obj \
// RUN: 2>&1 | FileCheck --check-prefix=UNDEF %s
+// RUN: not lld-link -machine:arm64ec -dll -noentry -out:err1.dll symref-aarch64.obj sym-arm64ec.obj \
+// RUN: 2>&1 | FileCheck --check-prefix=UNDEF %s
// UNDEF: lld-link: error: undefined symbol: sym (native symbol)
// UNDEF-NEXT: >>> referenced by symref-aarch64.obj:(.data)
@@ -25,25 +27,34 @@
// RUN: not lld-link -machine:arm64x -dll -noentry -out:out.dll symref-arm64ec.obj sym-aarch64.obj \
// RUN: 2>&1 | FileCheck --check-prefix=UNDEFEC %s
+// RUN: not lld-link -machine:arm64ec -dll -noentry -out:out.dll symref-arm64ec.obj sym-aarch64.obj \
+// RUN: 2>&1 | FileCheck --check-prefix=UNDEFEC %s
// UNDEFEC: lld-link: error: undefined symbol: sym (EC symbol)
// UNDEFEC-NEXT: >>> referenced by symref-arm64ec.obj:(.data)
// RUN: not lld-link -machine:arm64x -dll -noentry -out:out.dll symref-x86_64.obj sym-aarch64.obj \
// RUN: 2>&1 | FileCheck --check-prefix=UNDEFX86 %s
+// RUN: not lld-link -machine:arm64ec -dll -noentry -out:out.dll symref-x86_64.obj sym-aarch64.obj \
+// RUN: 2>&1 | FileCheck --check-prefix=UNDEFX86 %s
// UNDEFX86: lld-link: error: undefined symbol: sym (EC symbol)
// UNDEFX86-NEXT: >>> referenced by symref-x86_64.obj:(.data)
// RUN: not lld-link -machine:arm64x -dll -noentry -out:err2.dll symref-aarch64.obj sym-x86_64.obj \
// RUN: 2>&1 | FileCheck --check-prefix=UNDEF %s
+// RUN: not lld-link -machine:arm64ec -dll -noentry -out:err2.dll symref-aarch64.obj sym-x86_64.obj \
+// RUN: 2>&1 | FileCheck --check-prefix=UNDEF %s
// Check that ARM64X target can have the same symbol names in both native and EC namespaces.
// RUN: lld-link -machine:arm64x -dll -noentry -out:out.dll symref-aarch64.obj sym-aarch64.obj \
// RUN: symref-arm64ec.obj sym-x86_64.obj
+// RUN: lld-link -machine:arm64ec -dll -noentry -out:out.dll symref-aarch64.obj sym-aarch64.obj \
+// RUN: symref-arm64ec.obj sym-x86_64.obj
// Check that ARM64X target can reference both native and EC symbols from an archive.
// RUN: lld-link -machine:arm64x -dll -noentry -out:out2.dll symref-aarch64.obj symref-arm64ec.obj sym.lib
+// RUN: lld-link -machine:arm64ec -dll -noentry -out:out2.dll symref-aarch64.obj symref-arm64ec.obj sym.lib
// Check that EC object files can reference x86_64 library symbols.
@@ -55,15 +66,20 @@
// RUN: not lld-link -machine:arm64x -dll -noentry -out:err3.dll symref-aarch64.obj sym-x86_64.lib \
// RUN: 2>&1 | FileCheck --check-prefix=UNDEF %s
+// RUN: not lld-link -machine:arm64ec -dll -noentry -out:err3.dll symref-aarch64.obj sym-x86_64.lib \
+// RUN: 2>&1 | FileCheck --check-prefix=UNDEF %s
// Check that native object files can reference native library symbols.
// RUN: lld-link -machine:arm64x -dll -noentry -out:out6.dll symref-aarch64.obj sym-aarch64.lib
+// RUN: lld-link -machine:arm64ec -dll -noentry -out:out6.dll symref-aarch64.obj sym-aarch64.lib
// Check that EC object files can't reference native ARM64 library symbols.
// RUN: not lld-link -machine:arm64x -dll -noentry -out:err4.dll symref-arm64ec.obj sym-aarch64.lib \
// RUN: 2>&1 | FileCheck --check-prefix=UNDEFEC %s
+// RUN: not lld-link -machine:arm64ec -dll -noentry -out:err4.dll symref-arm64ec.obj sym-aarch64.lib \
+// RUN: 2>&1 | FileCheck --check-prefix=UNDEFEC %s
#--- symref.s
.data
diff --git a/lld/test/COFF/arm64x-wrap.s b/lld/test/COFF/arm64x-wrap.s
index 4f600e38f7a83..5530bc47c884e 100644
--- a/lld/test/COFF/arm64x-wrap.s
+++ b/lld/test/COFF/arm64x-wrap.s
@@ -15,6 +15,10 @@
// CHECK: 0x180004000 02000000 02000000 01000000 02000000
// CHECK: 0x180004010 02000000 01000000
+// RUN: lld-link -machine:arm64ec -dll -noentry test-arm64.obj test-arm64ec.obj other-arm64.obj other-arm64ec.obj \
+// RUN: loadconfig-arm64.obj loadconfig-arm64ec.obj -out:out-ec.dll -wrap:sym -wrap:nosuchsym
+// RUN: llvm-readobj --hex-dump=.test out-ec.dll | FileCheck %s
+
#--- test.s
.section .test,"dr"
.word sym
diff --git a/lld/test/COFF/autoimport-arm64ec-data.test b/lld/test/COFF/autoimport-arm64ec-data.test
index 1f22ca4917557..52e64d6020a83 100644
--- a/lld/test/COFF/autoimport-arm64ec-data.test
+++ b/lld/test/COFF/autoimport-arm64ec-data.test
@@ -12,7 +12,7 @@ RUN: llvm-objdump -s out.dll | FileCheck --check-prefix=CONTENTS %s
IMPORTS: Import {
IMPORTS-NEXT: Name: test.dll
-IMPORTS-NEXT: ImportLookupTableRVA: 0x40E0
+IMPORTS-NEXT: ImportLookupTableRVA: 0x4100
IMPORTS-NEXT: ImportAddressTableRVA: 0x3000
IMPORTS-NEXT: Symbol: variable (0)
IMPORTS-NEXT: }
>From ee79800f5a70ac92c618e0e318de11dc93017204 Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Wed, 14 May 2025 23:16:32 +0200
Subject: [PATCH 2/2] Address review comments
---
lld/COFF/Driver.cpp | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 6e7fc376fb9c1..7250fdc86719f 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -659,6 +659,10 @@ void LinkerDriver::setMachine(MachineTypes machine) {
if (!isArm64EC(machine)) {
ctx.symtab.machine = machine;
} else {
+ // Set up a hybrid symbol table on ARM64EC/ARM64X. This is primarily useful
+ // on ARM64X, where both the native and EC symbol tables are meaningful.
+ // However, since ARM64EC can include native object files, we also need to
+ // support a hybrid symbol table there.
ctx.symtab.machine = ARM64EC;
ctx.hybridSymtab.emplace(ctx, ARM64);
}
@@ -2551,6 +2555,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
for (auto *s : lto::LTO::getRuntimeLibcallSymbols(TT))
symtab.addLibcall(s);
}
+
// Windows specific -- if __load_config_used can be resolved, resolve
// it.
if (symtab.findUnderscore("_load_config_used"))
More information about the llvm-commits
mailing list