-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[LLD][COFF] Create EC alias symbols for entry points and exports #114297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-platform-windows @llvm/pr-subscribers-lld-coff Author: Jacek Caban (cjacek) ChangesOn ARM64EC, a symbol may be defined in either its mangled or demangled form (or both). To ensure consistent linking for entry points and exports, define an anti-dependency symbol that binds both forms, similar to how compiler-generated code references external functions. Full diff: https://github.com/llvm/llvm-project/pull/114297.diff 4 Files Affected:
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 08c1476a595f64..7c66be5d3b7da9 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -415,7 +415,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
case OPT_entry:
if (!arg->getValue()[0])
fatal("missing entry point symbol name");
- ctx.config.entry = addUndefined(mangle(arg->getValue()));
+ ctx.config.entry = addUndefined(mangle(arg->getValue()), true);
break;
case OPT_failifmismatch:
checkFailIfMismatch(arg->getValue(), file);
@@ -696,12 +696,33 @@ void LinkerDriver::addLibSearchPaths() {
}
}
-Symbol *LinkerDriver::addUndefined(StringRef name) {
+Symbol *LinkerDriver::addUndefined(StringRef name, bool aliasEC) {
Symbol *b = ctx.symtab.addUndefined(name);
if (!b->isGCRoot) {
b->isGCRoot = true;
ctx.config.gcroot.push_back(b);
}
+
+ // On ARM64EC, a symbol may be defined in either its mangled or demangled form
+ // (or both). Define an anti-dependency symbol that binds both forms, similar
+ // to how compiler-generated code references external functions.
+ if (aliasEC && isArm64EC(ctx.config.machine)) {
+ if (std::optional<std::string> mangledName =
+ getArm64ECMangledFunctionName(name)) {
+ auto u = dyn_cast<Undefined>(b);
+ if (u && !u->weakAlias) {
+ Symbol *t = ctx.symtab.addUndefined(saver().save(*mangledName));
+ u->setWeakAlias(t, true);
+ }
+ } else {
+ std::optional<std::string> unmangledName =
+ getArm64ECDemangledFunctionName(name);
+ Symbol *us = ctx.symtab.addUndefined(saver().save(*unmangledName));
+ auto u = dyn_cast<Undefined>(us);
+ if (u && !u->weakAlias)
+ u->setWeakAlias(b, true);
+ }
+ }
return b;
}
@@ -2342,22 +2363,22 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (auto *arg = args.getLastArg(OPT_entry)) {
if (!arg->getValue()[0])
fatal("missing entry point symbol name");
- config->entry = addUndefined(mangle(arg->getValue()));
+ config->entry = addUndefined(mangle(arg->getValue()), true);
} else if (!config->entry && !config->noEntry) {
if (args.hasArg(OPT_dll)) {
StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12"
: "_DllMainCRTStartup";
- config->entry = addUndefined(s);
+ config->entry = addUndefined(s, true);
} else if (config->driverWdm) {
// /driver:wdm implies /entry:_NtProcessStartup
- config->entry = addUndefined(mangle("_NtProcessStartup"));
+ config->entry = addUndefined(mangle("_NtProcessStartup"), true);
} else {
// Windows specific -- If entry point name is not given, we need to
// infer that from user-defined entry name.
StringRef s = findDefaultEntry();
if (s.empty())
fatal("entry point must be defined");
- config->entry = addUndefined(s);
+ config->entry = addUndefined(s, true);
log("Entry name inferred: " + s);
}
}
@@ -2371,7 +2392,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (config->machine == I386) {
config->delayLoadHelper = addUndefined("___delayLoadHelper2@8");
} else {
- config->delayLoadHelper = addUndefined("__delayLoadHelper2");
+ config->delayLoadHelper = addUndefined("__delayLoadHelper2", true);
}
}
}
@@ -2505,7 +2526,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
for (Export &e : config->exports) {
if (!e.forwardTo.empty())
continue;
- e.sym = addUndefined(e.name);
+ e.sym = addUndefined(e.name, !e.data);
if (e.source != ExportSource::Directives)
e.symbolName = mangleMaybe(e.sym);
}
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 58a2ed23106243..3889feb7511c0a 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -170,7 +170,7 @@ class LinkerDriver {
std::set<std::string> visitedLibs;
- Symbol *addUndefined(StringRef sym);
+ Symbol *addUndefined(StringRef sym, bool aliasEC = false);
void addUndefinedGlob(StringRef arg);
diff --git a/lld/test/COFF/arm64ec-delayimport.test b/lld/test/COFF/arm64ec-delayimport.test
index a0236d902eeaba..6797d84e088686 100644
--- a/lld/test/COFF/arm64ec-delayimport.test
+++ b/lld/test/COFF/arm64ec-delayimport.test
@@ -2,12 +2,14 @@ REQUIRES: aarch64, x86
RUN: split-file %s %t.dir && cd %t.dir
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test.s -o test.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows helper-mangled.s -o helper-mangled.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows helper-demangled.s -o helper-demangled.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
RUN: llvm-lib -machine:arm64ec -def:test.def -out:test-arm64ec.lib
RUN: llvm-lib -machine:arm64ec -def:test2.def -out:test2-arm64ec.lib
RUN: lld-link -machine:arm64ec -dll -noentry -out:out.dll loadconfig-arm64ec.obj test.obj \
-RUN: test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll -map
+RUN: helper-mangled.obj test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll -map
RUN: llvm-readobj --hex-dump=.test out.dll | FileCheck --check-prefix=TESTSEC %s
TESTSEC: 0x180008000 00600000 88700000 00200000 10100000
@@ -97,7 +99,7 @@ IMPORTS-NEXT: }
IMPORTS-NEXT: }
RUN: FileCheck --check-prefix=MAP %s < out.map
-MAP: 0001:00000008 #__delayLoadHelper2 0000000180001008 test.obj
+MAP: 0001:00000008 #__delayLoadHelper2 0000000180001008 helper-mangled.obj
MAP: 0001:00000010 #func 0000000180001010 test-arm64ec:test.dll
MAP-NEXT: 0001:0000001c __impchk_func 000000018000101c test-arm64ec:test.dll
MAP-NEXT: 0001:00000030 #func2 0000000180001030 test-arm64ec:test.dll
@@ -138,6 +140,21 @@ RELOC-NEXT: Type: DIR64
RELOC-NEXT: Address: 0x6008
RELOC-NEXT: }
+Verify that a demangled version of __delayLoadHelper2 can be used.
+
+RUN: lld-link -machine:arm64ec -dll -noentry -out:out2.dll loadconfig-arm64ec.obj test.obj \
+RUN: helper-demangled.obj test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll
+RUN: llvm-objdump -d out2.dll | FileCheck --check-prefix=DISASM %s
+
+Verify that the mangled version of __delayLoadHelper2 can be used from a library.
+Even if an anti-dependency alias is defined by the helper, it won't appear in
+the archive index, so we need to locate it by its mangled name.
+
+RUN: llvm-lib -machine:arm64ec -out:helper.lib helper-mangled.obj
+RUN: lld-link -machine:arm64ec -dll -noentry -out:out3.dll loadconfig-arm64ec.obj test.obj \
+RUN: helper.lib test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll
+RUN: llvm-objdump -d out3.dll | FileCheck --check-prefix=DISASM %s
+
#--- test.s
.section .test,"r"
.rva __imp_func
@@ -159,16 +176,6 @@ __icall_helper_arm64ec:
mov w0, #0
ret
- .section .text,"xr",discard,"#__delayLoadHelper2"
- .globl "#__delayLoadHelper2"
- .p2align 2, 0x0
-"#__delayLoadHelper2":
- mov w0, #1
- ret
-
- .weak_anti_dep __delayLoadHelper2
-.set __delayLoadHelper2,"#__delayLoadHelper2"
-
.section .hybmp$x, "yi"
.symidx __imp_func
.symidx func_exit_thunk
@@ -189,6 +196,25 @@ func2_exit_thunk:
mov w0, #3
ret
+#--- helper-mangled.s
+ .section .text,"xr",discard,"#__delayLoadHelper2"
+ .globl "#__delayLoadHelper2"
+ .p2align 2, 0x0
+"#__delayLoadHelper2":
+ mov w0, #1
+ ret
+
+ .weak_anti_dep __delayLoadHelper2
+.set __delayLoadHelper2,"#__delayLoadHelper2"
+
+#--- helper-demangled.s
+ .section .text,"xr",discard,__delayLoadHelper2
+ .globl __delayLoadHelper2
+ .p2align 2, 0x0
+__delayLoadHelper2:
+ mov w0, #1
+ ret
+
#--- test.def
NAME test.dll
EXPORTS
diff --git a/lld/test/COFF/arm64ec-entry-mangle.test b/lld/test/COFF/arm64ec-entry-mangle.test
new file mode 100644
index 00000000000000..65283f16d02fa9
--- /dev/null
+++ b/lld/test/COFF/arm64ec-entry-mangle.test
@@ -0,0 +1,129 @@
+REQUIRES: aarch64, x86
+RUN: split-file %s %t.dir && cd %t.dir
+
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows demangled-dll-main.s -o demangled-dll-main.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows mangled-dll-main.s -o mangled-dll-main.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows demangled-func.s -o demangled-func.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows mangled-func.s -o mangled-func.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-demangled.s -o ref-demangled.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows demangled-entry-drectve.s -o demangled-entry-drectve.obj
+RUN: llvm-mc -filetype=obj -triple=x86_64-windows demangled-dll-main.s -o x64-dll-main.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+
+RUN: llvm-lib -machine:arm64ec -out:func.lib mangled-func.obj
+RUN: llvm-lib -machine:arm64ec -out:dllmain.lib mangled-dll-main.obj
+
+Ensure that the linker recognizes the demangled version of _DllMainCRTStartup.
+RUN: lld-link -machine:arm64ec -dll -out:demangled-main.dll demangled-dll-main.obj loadconfig-arm64ec.obj
+RUN: llvm-objdump -d demangled-main.dll | FileCheck -check-prefix=DISASM %s
+
+DISASM: 0000000180001000 <.text>:
+DISASM-NEXT: 180001000: d65f03c0 ret
+DISASM-EMPTY:
+DISASM-NEXT: Disassembly of section .hexpthk:
+DISASM-EMPTY:
+DISASM: 180002000: 48 8b c4 movq %rsp, %rax
+DISASM-NEXT: 180002003: 48 89 58 20 movq %rbx, 0x20(%rax)
+DISASM-NEXT: 180002007: 55 pushq %rbp
+DISASM-NEXT: 180002008: 5d popq %rbp
+DISASM-NEXT: 180002009: e9 f2 ef ff ff jmp 0x180001000 <.text>
+DISASM-NEXT: 18000200e: cc int3
+DISASM-NEXT: 18000200f: cc int3
+
+Ensure that the linker recognizes the mangled version of #_DllMainCRTStartup.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-dllmain.dll mangled-dll-main.obj loadconfig-arm64ec.obj
+RUN: llvm-objdump -d mangled-dllmain.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled version of _DllMainCRTStartup from an archive.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-lib-dllmain.dll dllmain.lib loadconfig-arm64ec.obj
+RUN: llvm-objdump -d mangled-lib-dllmain.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the demangled entry function.
+RUN: lld-link -machine:arm64ec -dll -out:demangled-entry.dll demangled-func.obj loadconfig-arm64ec.obj -entry:func
+RUN: llvm-objdump -d demangled-entry.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled entry function when it is referenced by its demangled name.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-entry.dll mangled-func.obj loadconfig-arm64ec.obj -entry:func
+RUN: llvm-objdump -d mangled-entry.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled entry function when it is referenced by its demangled
+name in drectve section.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-entry.dll mangled-func.obj loadconfig-arm64ec.obj demangled-entry-drectve.obj
+RUN: llvm-objdump -d mangled-entry.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled entry function from an archive.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-lib-entry.dll func.lib loadconfig-arm64ec.obj -entry:func
+RUN: llvm-objdump -d mangled-lib-entry.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the entry function when referenced by its mangled name.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-entry2.dll mangled-func.obj loadconfig-arm64ec.obj "-entry:#func"
+RUN: llvm-objdump -d mangled-entry2.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the demangled exported function.
+RUN: lld-link -machine:arm64ec -dll -out:demangled-export.dll demangled-func.obj \
+RUN: loadconfig-arm64ec.obj -noentry -export:func
+RUN: llvm-objdump -d demangled-export.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled exported function when referenced by its demangled name.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-export.dll mangled-func.obj \
+RUN: loadconfig-arm64ec.obj -noentry -export:func
+RUN: llvm-objdump -d mangled-export.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled exported function when referenced by its mangled name.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-export2.dll mangled-func.obj \
+RUN: loadconfig-arm64ec.obj -noentry "-export:#func"
+RUN: llvm-objdump -d mangled-export2.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled exported function when referenced
+by its mangled name and creates a demangled alias for it.
+RUN: lld-link -machine:arm64ec -dll -noentry -out:demangled-export-ref.dll mangled-func.obj \
+RUN: ref-demangled.obj loadconfig-arm64ec.obj "-export:#func"
+RUN: llvm-objdump -d demangled-export-ref.dll | FileCheck -check-prefix=DISASM %s
+
+DISASM2: 0000000180001000 <.text>:
+DISASM2-NEXT: 180001000: d65f03c0 ret
+
+Verify that the linker emits appropriate errors for mismatched mangling.
+RUN: not lld-link -machine:arm64ec -dll -out:test.dll demangled-func.obj loadconfig-arm64ec.obj \
+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
+
+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
+RUN: llvm-objdump -d test.dll | FileCheck -check-prefix=DISASM-X64 %s
+DISASM-X64: 0000000180001000 <.text>:
+DISASM-X64-NEXT: 180001000: c3 retq
+
+#--- demangled-dll-main.s
+ .text
+ .globl _DllMainCRTStartup
+_DllMainCRTStartup:
+ ret
+
+#--- mangled-dll-main.s
+ .text
+ .globl "#_DllMainCRTStartup"
+"#_DllMainCRTStartup":
+ ret
+
+#--- demangled-func.s
+ .text
+ .globl func
+func:
+ ret
+
+#--- mangled-func.s
+ .text
+ .globl "#func"
+"#func":
+ ret
+
+#--- ref-demangled.s
+ .data
+ .rva func
+
+#--- demangled-entry-drectve.s
+ .section .drectve,"rd"
+ .ascii " -entry:func"
|
@llvm/pr-subscribers-lld Author: Jacek Caban (cjacek) ChangesOn ARM64EC, a symbol may be defined in either its mangled or demangled form (or both). To ensure consistent linking for entry points and exports, define an anti-dependency symbol that binds both forms, similar to how compiler-generated code references external functions. Full diff: https://github.com/llvm/llvm-project/pull/114297.diff 4 Files Affected:
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 08c1476a595f64..7c66be5d3b7da9 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -415,7 +415,7 @@ void LinkerDriver::parseDirectives(InputFile *file) {
case OPT_entry:
if (!arg->getValue()[0])
fatal("missing entry point symbol name");
- ctx.config.entry = addUndefined(mangle(arg->getValue()));
+ ctx.config.entry = addUndefined(mangle(arg->getValue()), true);
break;
case OPT_failifmismatch:
checkFailIfMismatch(arg->getValue(), file);
@@ -696,12 +696,33 @@ void LinkerDriver::addLibSearchPaths() {
}
}
-Symbol *LinkerDriver::addUndefined(StringRef name) {
+Symbol *LinkerDriver::addUndefined(StringRef name, bool aliasEC) {
Symbol *b = ctx.symtab.addUndefined(name);
if (!b->isGCRoot) {
b->isGCRoot = true;
ctx.config.gcroot.push_back(b);
}
+
+ // On ARM64EC, a symbol may be defined in either its mangled or demangled form
+ // (or both). Define an anti-dependency symbol that binds both forms, similar
+ // to how compiler-generated code references external functions.
+ if (aliasEC && isArm64EC(ctx.config.machine)) {
+ if (std::optional<std::string> mangledName =
+ getArm64ECMangledFunctionName(name)) {
+ auto u = dyn_cast<Undefined>(b);
+ if (u && !u->weakAlias) {
+ Symbol *t = ctx.symtab.addUndefined(saver().save(*mangledName));
+ u->setWeakAlias(t, true);
+ }
+ } else {
+ std::optional<std::string> unmangledName =
+ getArm64ECDemangledFunctionName(name);
+ Symbol *us = ctx.symtab.addUndefined(saver().save(*unmangledName));
+ auto u = dyn_cast<Undefined>(us);
+ if (u && !u->weakAlias)
+ u->setWeakAlias(b, true);
+ }
+ }
return b;
}
@@ -2342,22 +2363,22 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (auto *arg = args.getLastArg(OPT_entry)) {
if (!arg->getValue()[0])
fatal("missing entry point symbol name");
- config->entry = addUndefined(mangle(arg->getValue()));
+ config->entry = addUndefined(mangle(arg->getValue()), true);
} else if (!config->entry && !config->noEntry) {
if (args.hasArg(OPT_dll)) {
StringRef s = (config->machine == I386) ? "__DllMainCRTStartup@12"
: "_DllMainCRTStartup";
- config->entry = addUndefined(s);
+ config->entry = addUndefined(s, true);
} else if (config->driverWdm) {
// /driver:wdm implies /entry:_NtProcessStartup
- config->entry = addUndefined(mangle("_NtProcessStartup"));
+ config->entry = addUndefined(mangle("_NtProcessStartup"), true);
} else {
// Windows specific -- If entry point name is not given, we need to
// infer that from user-defined entry name.
StringRef s = findDefaultEntry();
if (s.empty())
fatal("entry point must be defined");
- config->entry = addUndefined(s);
+ config->entry = addUndefined(s, true);
log("Entry name inferred: " + s);
}
}
@@ -2371,7 +2392,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (config->machine == I386) {
config->delayLoadHelper = addUndefined("___delayLoadHelper2@8");
} else {
- config->delayLoadHelper = addUndefined("__delayLoadHelper2");
+ config->delayLoadHelper = addUndefined("__delayLoadHelper2", true);
}
}
}
@@ -2505,7 +2526,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
for (Export &e : config->exports) {
if (!e.forwardTo.empty())
continue;
- e.sym = addUndefined(e.name);
+ e.sym = addUndefined(e.name, !e.data);
if (e.source != ExportSource::Directives)
e.symbolName = mangleMaybe(e.sym);
}
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index 58a2ed23106243..3889feb7511c0a 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -170,7 +170,7 @@ class LinkerDriver {
std::set<std::string> visitedLibs;
- Symbol *addUndefined(StringRef sym);
+ Symbol *addUndefined(StringRef sym, bool aliasEC = false);
void addUndefinedGlob(StringRef arg);
diff --git a/lld/test/COFF/arm64ec-delayimport.test b/lld/test/COFF/arm64ec-delayimport.test
index a0236d902eeaba..6797d84e088686 100644
--- a/lld/test/COFF/arm64ec-delayimport.test
+++ b/lld/test/COFF/arm64ec-delayimport.test
@@ -2,12 +2,14 @@ REQUIRES: aarch64, x86
RUN: split-file %s %t.dir && cd %t.dir
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test.s -o test.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows helper-mangled.s -o helper-mangled.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows helper-demangled.s -o helper-demangled.obj
RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
RUN: llvm-lib -machine:arm64ec -def:test.def -out:test-arm64ec.lib
RUN: llvm-lib -machine:arm64ec -def:test2.def -out:test2-arm64ec.lib
RUN: lld-link -machine:arm64ec -dll -noentry -out:out.dll loadconfig-arm64ec.obj test.obj \
-RUN: test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll -map
+RUN: helper-mangled.obj test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll -map
RUN: llvm-readobj --hex-dump=.test out.dll | FileCheck --check-prefix=TESTSEC %s
TESTSEC: 0x180008000 00600000 88700000 00200000 10100000
@@ -97,7 +99,7 @@ IMPORTS-NEXT: }
IMPORTS-NEXT: }
RUN: FileCheck --check-prefix=MAP %s < out.map
-MAP: 0001:00000008 #__delayLoadHelper2 0000000180001008 test.obj
+MAP: 0001:00000008 #__delayLoadHelper2 0000000180001008 helper-mangled.obj
MAP: 0001:00000010 #func 0000000180001010 test-arm64ec:test.dll
MAP-NEXT: 0001:0000001c __impchk_func 000000018000101c test-arm64ec:test.dll
MAP-NEXT: 0001:00000030 #func2 0000000180001030 test-arm64ec:test.dll
@@ -138,6 +140,21 @@ RELOC-NEXT: Type: DIR64
RELOC-NEXT: Address: 0x6008
RELOC-NEXT: }
+Verify that a demangled version of __delayLoadHelper2 can be used.
+
+RUN: lld-link -machine:arm64ec -dll -noentry -out:out2.dll loadconfig-arm64ec.obj test.obj \
+RUN: helper-demangled.obj test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll
+RUN: llvm-objdump -d out2.dll | FileCheck --check-prefix=DISASM %s
+
+Verify that the mangled version of __delayLoadHelper2 can be used from a library.
+Even if an anti-dependency alias is defined by the helper, it won't appear in
+the archive index, so we need to locate it by its mangled name.
+
+RUN: llvm-lib -machine:arm64ec -out:helper.lib helper-mangled.obj
+RUN: lld-link -machine:arm64ec -dll -noentry -out:out3.dll loadconfig-arm64ec.obj test.obj \
+RUN: helper.lib test-arm64ec.lib test2-arm64ec.lib -delayload:test.dll
+RUN: llvm-objdump -d out3.dll | FileCheck --check-prefix=DISASM %s
+
#--- test.s
.section .test,"r"
.rva __imp_func
@@ -159,16 +176,6 @@ __icall_helper_arm64ec:
mov w0, #0
ret
- .section .text,"xr",discard,"#__delayLoadHelper2"
- .globl "#__delayLoadHelper2"
- .p2align 2, 0x0
-"#__delayLoadHelper2":
- mov w0, #1
- ret
-
- .weak_anti_dep __delayLoadHelper2
-.set __delayLoadHelper2,"#__delayLoadHelper2"
-
.section .hybmp$x, "yi"
.symidx __imp_func
.symidx func_exit_thunk
@@ -189,6 +196,25 @@ func2_exit_thunk:
mov w0, #3
ret
+#--- helper-mangled.s
+ .section .text,"xr",discard,"#__delayLoadHelper2"
+ .globl "#__delayLoadHelper2"
+ .p2align 2, 0x0
+"#__delayLoadHelper2":
+ mov w0, #1
+ ret
+
+ .weak_anti_dep __delayLoadHelper2
+.set __delayLoadHelper2,"#__delayLoadHelper2"
+
+#--- helper-demangled.s
+ .section .text,"xr",discard,__delayLoadHelper2
+ .globl __delayLoadHelper2
+ .p2align 2, 0x0
+__delayLoadHelper2:
+ mov w0, #1
+ ret
+
#--- test.def
NAME test.dll
EXPORTS
diff --git a/lld/test/COFF/arm64ec-entry-mangle.test b/lld/test/COFF/arm64ec-entry-mangle.test
new file mode 100644
index 00000000000000..65283f16d02fa9
--- /dev/null
+++ b/lld/test/COFF/arm64ec-entry-mangle.test
@@ -0,0 +1,129 @@
+REQUIRES: aarch64, x86
+RUN: split-file %s %t.dir && cd %t.dir
+
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows demangled-dll-main.s -o demangled-dll-main.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows mangled-dll-main.s -o mangled-dll-main.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows demangled-func.s -o demangled-func.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows mangled-func.s -o mangled-func.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows ref-demangled.s -o ref-demangled.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows demangled-entry-drectve.s -o demangled-entry-drectve.obj
+RUN: llvm-mc -filetype=obj -triple=x86_64-windows demangled-dll-main.s -o x64-dll-main.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+
+RUN: llvm-lib -machine:arm64ec -out:func.lib mangled-func.obj
+RUN: llvm-lib -machine:arm64ec -out:dllmain.lib mangled-dll-main.obj
+
+Ensure that the linker recognizes the demangled version of _DllMainCRTStartup.
+RUN: lld-link -machine:arm64ec -dll -out:demangled-main.dll demangled-dll-main.obj loadconfig-arm64ec.obj
+RUN: llvm-objdump -d demangled-main.dll | FileCheck -check-prefix=DISASM %s
+
+DISASM: 0000000180001000 <.text>:
+DISASM-NEXT: 180001000: d65f03c0 ret
+DISASM-EMPTY:
+DISASM-NEXT: Disassembly of section .hexpthk:
+DISASM-EMPTY:
+DISASM: 180002000: 48 8b c4 movq %rsp, %rax
+DISASM-NEXT: 180002003: 48 89 58 20 movq %rbx, 0x20(%rax)
+DISASM-NEXT: 180002007: 55 pushq %rbp
+DISASM-NEXT: 180002008: 5d popq %rbp
+DISASM-NEXT: 180002009: e9 f2 ef ff ff jmp 0x180001000 <.text>
+DISASM-NEXT: 18000200e: cc int3
+DISASM-NEXT: 18000200f: cc int3
+
+Ensure that the linker recognizes the mangled version of #_DllMainCRTStartup.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-dllmain.dll mangled-dll-main.obj loadconfig-arm64ec.obj
+RUN: llvm-objdump -d mangled-dllmain.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled version of _DllMainCRTStartup from an archive.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-lib-dllmain.dll dllmain.lib loadconfig-arm64ec.obj
+RUN: llvm-objdump -d mangled-lib-dllmain.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the demangled entry function.
+RUN: lld-link -machine:arm64ec -dll -out:demangled-entry.dll demangled-func.obj loadconfig-arm64ec.obj -entry:func
+RUN: llvm-objdump -d demangled-entry.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled entry function when it is referenced by its demangled name.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-entry.dll mangled-func.obj loadconfig-arm64ec.obj -entry:func
+RUN: llvm-objdump -d mangled-entry.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled entry function when it is referenced by its demangled
+name in drectve section.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-entry.dll mangled-func.obj loadconfig-arm64ec.obj demangled-entry-drectve.obj
+RUN: llvm-objdump -d mangled-entry.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled entry function from an archive.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-lib-entry.dll func.lib loadconfig-arm64ec.obj -entry:func
+RUN: llvm-objdump -d mangled-lib-entry.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the entry function when referenced by its mangled name.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-entry2.dll mangled-func.obj loadconfig-arm64ec.obj "-entry:#func"
+RUN: llvm-objdump -d mangled-entry2.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the demangled exported function.
+RUN: lld-link -machine:arm64ec -dll -out:demangled-export.dll demangled-func.obj \
+RUN: loadconfig-arm64ec.obj -noentry -export:func
+RUN: llvm-objdump -d demangled-export.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled exported function when referenced by its demangled name.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-export.dll mangled-func.obj \
+RUN: loadconfig-arm64ec.obj -noentry -export:func
+RUN: llvm-objdump -d mangled-export.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled exported function when referenced by its mangled name.
+RUN: lld-link -machine:arm64ec -dll -out:mangled-export2.dll mangled-func.obj \
+RUN: loadconfig-arm64ec.obj -noentry "-export:#func"
+RUN: llvm-objdump -d mangled-export2.dll | FileCheck -check-prefix=DISASM %s
+
+Verify that the linker recognizes the mangled exported function when referenced
+by its mangled name and creates a demangled alias for it.
+RUN: lld-link -machine:arm64ec -dll -noentry -out:demangled-export-ref.dll mangled-func.obj \
+RUN: ref-demangled.obj loadconfig-arm64ec.obj "-export:#func"
+RUN: llvm-objdump -d demangled-export-ref.dll | FileCheck -check-prefix=DISASM %s
+
+DISASM2: 0000000180001000 <.text>:
+DISASM2-NEXT: 180001000: d65f03c0 ret
+
+Verify that the linker emits appropriate errors for mismatched mangling.
+RUN: not lld-link -machine:arm64ec -dll -out:test.dll demangled-func.obj loadconfig-arm64ec.obj \
+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
+
+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
+RUN: llvm-objdump -d test.dll | FileCheck -check-prefix=DISASM-X64 %s
+DISASM-X64: 0000000180001000 <.text>:
+DISASM-X64-NEXT: 180001000: c3 retq
+
+#--- demangled-dll-main.s
+ .text
+ .globl _DllMainCRTStartup
+_DllMainCRTStartup:
+ ret
+
+#--- mangled-dll-main.s
+ .text
+ .globl "#_DllMainCRTStartup"
+"#_DllMainCRTStartup":
+ ret
+
+#--- demangled-func.s
+ .text
+ .globl func
+func:
+ ret
+
+#--- mangled-func.s
+ .text
+ .globl "#func"
+"#func":
+ ret
+
+#--- ref-demangled.s
+ .data
+ .rva func
+
+#--- demangled-entry-drectve.s
+ .section .drectve,"rd"
+ .ascii " -entry:func"
|
I still have a few more fixes queued, but this is the final feature needed to link ARM64EC msvcrt.lib properly, making the linker genuinely useful. The underlying issue is that entry points are provided by archives, which only contain the mangled name in the index. Although the object files that implement these symbols include the demangled alias, they aren’t visible until the object file is pulled in. This change creates aliases for entry point and exported function symbols to ensure they can be properly included. This behavior is mostly compatible with what the MSVC linker does: all tests pass except the one where |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks! One minor nit, but otherwise this looks quite straightforward, implementation wise.
} | ||
} else { | ||
std::optional<std::string> unmangledName = | ||
getArm64ECDemangledFunctionName(name); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: There's a small inconsistency here - is it "unmangled" or "demangled"? It would be more consistent if the variable and the function would use the same form :-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed, thanks!
On ARM64EC, a symbol may be defined in either its mangled or demangled form (or both). To ensure consistent linking for entry points and exports, define an anti-dependency symbol that binds both forms, similar to how compiler-generated code references external functions.
11cb331
to
185bd67
Compare
…m#114297) On ARM64EC, a symbol may be defined in either its mangled or demangled form (or both). To ensure consistent linking for entry points and exports, define an anti-dependency symbol that binds both forms, similar to how compiler-generated code references external functions.
…m#114297) On ARM64EC, a symbol may be defined in either its mangled or demangled form (or both). To ensure consistent linking for entry points and exports, define an anti-dependency symbol that binds both forms, similar to how compiler-generated code references external functions.
On ARM64EC, a symbol may be defined in either its mangled or demangled form (or both). To ensure consistent linking for entry points and exports, define an anti-dependency symbol that binds both forms, similar to how compiler-generated code references external functions.