[LLD][COFF] Create EC alias symbols for entry points and exports (#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.
This commit is contained in:
Jacek Caban 2024-10-31 18:05:54 +01:00 committed by GitHub
parent e7080fd735
commit 4d4a43dc51
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 197 additions and 21 deletions

View File

@ -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> demangledName =
getArm64ECDemangledFunctionName(name);
Symbol *us = ctx.symtab.addUndefined(saver().save(*demangledName));
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);
}

View File

@ -170,7 +170,7 @@ private:
std::set<std::string> visitedLibs;
Symbol *addUndefined(StringRef sym);
Symbol *addUndefined(StringRef sym, bool aliasEC = false);
void addUndefinedGlob(StringRef arg);

View File

@ -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

View File

@ -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"