Skip to content

[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

Merged
merged 1 commit into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 29 additions & 8 deletions lld/COFF/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Copy link
Member

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 :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks!

Symbol *us = ctx.symtab.addUndefined(saver().save(*demangledName));
auto u = dyn_cast<Undefined>(us);
if (u && !u->weakAlias)
u->setWeakAlias(b, true);
}
}
return b;
}

Expand Down Expand Up @@ -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);
}
}
Expand All @@ -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);
}
}
}
Expand Down Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion lld/COFF/Driver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
50 changes: 38 additions & 12 deletions lld/test/COFF/arm64ec-delayimport.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down
129 changes: 129 additions & 0 deletions lld/test/COFF/arm64ec-entry-mangle.test
Original file line number Diff line number Diff line change
@@ -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"
Loading