From 185bd67e7a054bd8e175f047255964a0d9725c9f Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Wed, 27 Sep 2023 23:48:49 +0200 Subject: [PATCH] [LLD][COFF] Create EC alias symbols for entry points and exports 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. --- lld/COFF/Driver.cpp | 37 +++++-- lld/COFF/Driver.h | 2 +- lld/test/COFF/arm64ec-delayimport.test | 50 ++++++--- lld/test/COFF/arm64ec-entry-mangle.test | 129 ++++++++++++++++++++++++ 4 files changed, 197 insertions(+), 21 deletions(-) create mode 100644 lld/test/COFF/arm64ec-entry-mangle.test diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp index 08c1476a595f6..d717afac47389 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 mangledName = + getArm64ECMangledFunctionName(name)) { + auto u = dyn_cast(b); + if (u && !u->weakAlias) { + Symbol *t = ctx.symtab.addUndefined(saver().save(*mangledName)); + u->setWeakAlias(t, true); + } + } else { + std::optional demangledName = + getArm64ECDemangledFunctionName(name); + Symbol *us = ctx.symtab.addUndefined(saver().save(*demangledName)); + auto u = dyn_cast(us); + if (u && !u->weakAlias) + u->setWeakAlias(b, true); + } + } return b; } @@ -2342,22 +2363,22 @@ void LinkerDriver::linkerMain(ArrayRef 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 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 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 58a2ed2310624..3889feb7511c0 100644 --- a/lld/COFF/Driver.h +++ b/lld/COFF/Driver.h @@ -170,7 +170,7 @@ class LinkerDriver { std::set 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 a0236d902eeab..6797d84e08868 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 0000000000000..65283f16d02fa --- /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"