[lld] [LLD][COFF] Generate X64 thunks for ARM64EC entry points and patchable functions. (PR #105499)
Jacek Caban via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 22 01:18:45 PDT 2024
https://github.com/cjacek updated https://github.com/llvm/llvm-project/pull/105499
>From 7c03bba34fcc3a82811838cea55aafde7ad13b0b Mon Sep 17 00:00:00 2001
From: Jacek Caban <jacek at codeweavers.com>
Date: Fri, 16 Jun 2023 20:05:39 +0200
Subject: [PATCH] [LLD][COFF] Generate X64 thunks for ARM64EC entry points and
patchable functions.
---
lld/COFF/Chunks.cpp | 5 +
lld/COFF/Chunks.h | 20 +++
lld/COFF/Driver.cpp | 69 ++++++++++
lld/COFF/Driver.h | 4 +
lld/COFF/SymbolTable.cpp | 3 +
lld/COFF/SymbolTable.h | 3 +
lld/COFF/Writer.cpp | 11 ++
lld/test/COFF/arm64ec-export-thunks.test | 140 ++++++++++++++++++++
lld/test/COFF/arm64ec-exports.s | 12 +-
lld/test/COFF/arm64ec-patchable-thunks.test | 86 ++++++++++++
10 files changed, 347 insertions(+), 6 deletions(-)
create mode 100644 lld/test/COFF/arm64ec-export-thunks.test
create mode 100644 lld/test/COFF/arm64ec-patchable-thunks.test
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 2807c894520512..be44950a1720e3 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -1073,4 +1073,9 @@ void AbsolutePointerChunk::writeTo(uint8_t *buf) const {
}
}
+void ECExportThunkChunk::writeTo(uint8_t *buf) const {
+ memcpy(buf, ECExportThunkCode, sizeof(ECExportThunkCode));
+ write32le(buf + 10, target->getRVA() - rva - 14);
+}
+
} // namespace lld::coff
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index df311524a8d185..5443d4619a977e 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -749,6 +749,26 @@ class ECCodeMapChunk : public NonSectionChunk {
std::vector<ECCodeMapEntry> ↦
};
+static const uint8_t ECExportThunkCode[] = {
+ 0x48, 0x8b, 0xc4, // movq %rsp, %rax
+ 0x48, 0x89, 0x58, 0x20, // movq %rbx, 0x20(%rax)
+ 0x55, // pushq %rbp
+ 0x5d, // popq %rbp
+ 0xe9, 0, 0, 0, 0, // jmp *0x0
+ 0xcc, // int3
+ 0xcc // int3
+};
+
+class ECExportThunkChunk : public NonSectionCodeChunk {
+public:
+ explicit ECExportThunkChunk(Defined *targetSym) : target(targetSym) {}
+ size_t getSize() const override { return sizeof(ECExportThunkCode); };
+ void writeTo(uint8_t *buf) const override;
+ MachineTypes getMachine() const override { return AMD64; }
+
+ Defined *target;
+};
+
// MinGW specific, for the "automatic import of variables from DLLs" feature.
// This provides the table of runtime pseudo relocations, for variable
// references that turned out to need to be imported from a DLL even though
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 9e28b1c50be504..1a2db562760849 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1317,6 +1317,72 @@ void LinkerDriver::convertResources() {
f->includeResourceChunks();
}
+void LinkerDriver::maybeCreateECExportThunk(StringRef name, Symbol *&sym) {
+ Defined *def;
+ if (!sym)
+ return;
+ if (auto undef = dyn_cast<Undefined>(sym))
+ def = undef->getWeakAlias();
+ else
+ def = dyn_cast<Defined>(sym);
+ if (!def)
+ return;
+
+ if (def->getChunk()->getArm64ECRangeType() != chpe_range_type::Arm64EC)
+ return;
+ StringRef expName;
+ if (auto mangledName = getArm64ECMangledFunctionName(name))
+ expName = saver().save("EXP+" + *mangledName);
+ else
+ expName = saver().save("EXP+" + name);
+ sym = addUndefined(expName);
+ if (auto undef = dyn_cast<Undefined>(sym)) {
+ if (!undef->getWeakAlias()) {
+ auto thunk = make<ECExportThunkChunk>(def);
+ replaceSymbol<DefinedSynthetic>(undef, undef->getName(), thunk);
+ }
+ }
+}
+
+void LinkerDriver::createECExportThunks() {
+ // Check if EXP+ symbols have corresponding $hp_target symbols and use them
+ // to create export thunks when available.
+ for (Symbol *s : ctx.symtab.expSymbols) {
+ if (!s->isUsedInRegularObj)
+ continue;
+ assert(s->getName().starts_with "EXP+");
+ std::string targetName =
+ (s->getName().substr(strlen("EXP+")) + "$hp_target").str();
+ Symbol *sym = ctx.symtab.find(targetName);
+ if (!sym)
+ continue;
+ Defined *targetSym;
+ if (auto undef = dyn_cast<Undefined>(sym))
+ targetSym = undef->getWeakAlias();
+ else
+ targetSym = dyn_cast<Defined>(sym);
+ if (!targetSym)
+ continue;
+
+ auto *undef = dyn_cast<Undefined>(s);
+ if (undef && !undef->getWeakAlias()) {
+ auto thunk = make<ECExportThunkChunk>(targetSym);
+ replaceSymbol<DefinedSynthetic>(undef, undef->getName(), thunk);
+ }
+ if (!targetSym->isGCRoot) {
+ targetSym->isGCRoot = true;
+ ctx.config.gcroot.push_back(targetSym);
+ }
+ }
+
+ if (ctx.config.entry)
+ maybeCreateECExportThunk(ctx.config.entry->getName(), ctx.config.entry);
+ for (Export &e : ctx.config.exports) {
+ if (!e.data)
+ maybeCreateECExportThunk(e.extName.empty() ? e.name : e.extName, e.sym);
+ }
+}
+
// In MinGW, if no symbols are chosen to be exported, then all symbols are
// automatically exported by default. This behavior can be forced by the
// -export-all-symbols option, so that it happens even when exports are
@@ -2520,6 +2586,9 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (!wrapped.empty())
wrapSymbols(ctx, wrapped);
+ if (isArm64EC(config->machine))
+ createECExportThunks();
+
// Resolve remaining undefined symbols and warn about imported locals.
ctx.symtab.resolveRemainingUndefines();
if (errorCount())
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index fa54de05befb58..b5cf8e2f18fd4e 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -270,6 +270,10 @@ class LinkerDriver {
// Convert Windows resource files (.res files) to a .obj file.
MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,
ArrayRef<ObjFile *> objs);
+
+ // Create export thunks for exported and patchable Arm64EC function symbols.
+ void createECExportThunks();
+ void maybeCreateECExportThunk(StringRef name, Symbol *&sym);
};
// Create enum with OPT_xxx values for each option in Options.td
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index 6c3c4e3931aa84..1dfff0a90f4aee 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -551,6 +551,9 @@ std::pair<Symbol *, bool> SymbolTable::insert(StringRef name) {
sym->pendingArchiveLoad = false;
sym->canInline = true;
inserted = true;
+
+ if (isArm64EC(ctx.config.machine) && name.starts_with("EXP+"))
+ expSymbols.push_back(sym);
}
return {sym, inserted};
}
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index 93b376b69f7ecf..b5f95d2ad7f112 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -116,6 +116,9 @@ class SymbolTable {
// A list of chunks which to be added to .rdata.
std::vector<Chunk *> localImportChunks;
+ // A list of EC EXP+ symbols.
+ std::vector<Symbol *> expSymbols;
+
// Iterates symbols in non-determinstic hash table order.
template <typename T> void forEachSymbol(T callback) {
for (auto &pair : symMap)
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index f776c76a47ae96..776595d98c391d 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -304,6 +304,7 @@ class Writer {
uint64_t sizeOfHeaders;
OutputSection *textSec;
+ OutputSection *hexpthkSec;
OutputSection *rdataSec;
OutputSection *buildidSec;
OutputSection *dataSec;
@@ -984,6 +985,8 @@ void Writer::createSections() {
// Try to match the section order used by link.exe.
textSec = createSection(".text", code | r | x);
+ if (isArm64EC(ctx.config.machine))
+ hexpthkSec = createSection(".hexpthk", code | r | x);
createSection(".bss", bss | r | w);
rdataSec = createSection(".rdata", data | r);
buildidSec = createSection(".buildid", data | r);
@@ -2046,6 +2049,14 @@ void Writer::maybeAddRVATable(SymbolRVASet tableSymbols, StringRef tableSym,
// Create CHPE metadata chunks.
void Writer::createECChunks() {
+ for (Symbol *s : ctx.symtab.expSymbols) {
+ auto sym = dyn_cast<Defined>(s);
+ if (!sym || !sym->getChunk())
+ continue;
+ if (auto thunk = dyn_cast<ECExportThunkChunk>(sym->getChunk()))
+ hexpthkSec->addChunk(thunk);
+ }
+
auto codeMapChunk = make<ECCodeMapChunk>(codeMap);
rdataSec->addChunk(codeMapChunk);
Symbol *codeMapSym = ctx.symtab.findUnderscore("__hybrid_code_map");
diff --git a/lld/test/COFF/arm64ec-export-thunks.test b/lld/test/COFF/arm64ec-export-thunks.test
new file mode 100644
index 00000000000000..7bb2bfad1f89f7
--- /dev/null
+++ b/lld/test/COFF/arm64ec-export-thunks.test
@@ -0,0 +1,140 @@
+REQUIRES: aarch64, x86
+RUN: split-file %s %t.dir && cd %t.dir
+
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-data.s -o arm64ec-data.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-func.s -o arm64ec-func.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows antidep-func.s -o antidep-func.obj
+RUN: llvm-mc -filetype=obj -triple=x86_64-windows x86_64-func.s -o x86_64-func.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+
+RUN: lld-link -out:exports.dll -machine:arm64ec arm64ec-func.obj x86_64-func.obj loadconfig-arm64ec.obj \
+RUN: arm64ec-data.obj -dll -noentry -export:arm64ec_func -export:func=arm64ec_func \
+RUN: -export:x86_64_func -export:data_sym,DATA
+
+RUN: llvm-objdump -d exports.dll | FileCheck -check-prefix=EXP-DISASM %s
+EXP-DISASM: Disassembly of section .text:
+EXP-DISASM-EMPTY:
+EXP-DISASM-NEXT: 0000000180001000 <.text>:
+EXP-DISASM-NEXT: 180001000: 90000008 adrp x8, 0x180001000 <.text>
+EXP-DISASM-NEXT: 180001004: 52800040 mov w0, #0x2
+EXP-DISASM-NEXT: 180001008: d65f03c0 ret
+EXP-DISASM-NEXT: ...
+EXP-DISASM-EMPTY:
+EXP-DISASM-NEXT: 0000000180002000 <x86_64_func>:
+EXP-DISASM-NEXT: 180002000: e8 fb ef ff ff callq 0x180001000 <.text>
+EXP-DISASM-NEXT: 180002005: b8 03 00 00 00 movl $0x3, %eax
+EXP-DISASM-NEXT: 18000200a: c3 retq
+EXP-DISASM-EMPTY:
+EXP-DISASM-NEXT: Disassembly of section .hexpthk:
+EXP-DISASM-EMPTY:
+EXP-DISASM-NEXT: 0000000180003000 <func>:
+EXP-DISASM-NEXT: 180003000: 48 8b c4 movq %rsp, %rax
+EXP-DISASM-NEXT: 180003003: 48 89 58 20 movq %rbx, 0x20(%rax)
+EXP-DISASM-NEXT: 180003007: 55 pushq %rbp
+EXP-DISASM-NEXT: 180003008: 5d popq %rbp
+EXP-DISASM-NEXT: 180003009: e9 f2 df ff ff jmp 0x180001000 <.text>
+EXP-DISASM-NEXT: 18000300e: cc int3
+EXP-DISASM-NEXT: 18000300f: cc int3
+EXP-DISASM-EMPTY:
+EXP-DISASM-NEXT: 0000000180003010 <arm64ec_func>:
+EXP-DISASM-NEXT: 180003010: 48 8b c4 movq %rsp, %rax
+EXP-DISASM-NEXT: 180003013: 48 89 58 20 movq %rbx, 0x20(%rax)
+EXP-DISASM-NEXT: 180003017: 55 pushq %rbp
+EXP-DISASM-NEXT: 180003018: 5d popq %rbp
+EXP-DISASM-NEXT: 180003019: e9 e2 df ff ff jmp 0x180001000 <.text>
+EXP-DISASM-NEXT: 18000301e: cc int3
+EXP-DISASM-NEXT: 18000301f: cc int3
+
+RUN: llvm-objdump -p exports.dll | FileCheck -check-prefix=EXP-EXPORT %s
+EXP-EXPORT: Ordinal RVA Name
+EXP-EXPORT-NEXT: 1 0x3010 arm64ec_func
+EXP-EXPORT-NEXT: 2 0x6000 data_sym
+EXP-EXPORT-NEXT: 3 0x3000 func
+EXP-EXPORT-NEXT: 4 0x2000 x86_64_func
+
+RUN: llvm-readobj --coff-load-config exports.dll | FileCheck -check-prefix=EXP-CHPE %s
+EXP-CHPE: CodeMap [
+EXP-CHPE-NEXT: 0x1000 - 0x100C ARM64EC
+EXP-CHPE-NEXT: 0x2000 - 0x3020 X64
+EXP-CHPE-NEXT: ]
+
+RUN: llvm-objdump -s --section=.test exports.dll | FileCheck --check-prefix=EXP-DATA %s
+EXP-DATA: 180006000 00300000 10300000
+
+RUN: lld-link -out:exports2.dll -machine:arm64ec antidep-func.obj x86_64-func.obj loadconfig-arm64ec.obj \
+RUN: arm64ec-data.obj -dll -noentry -export:arm64ec_func -export:func=arm64ec_func \
+RUN: -export:x86_64_func -export:data_sym,DATA
+
+RUN: llvm-objdump -d exports2.dll | FileCheck -check-prefix=EXP-DISASM %s
+RUN: llvm-objdump -p exports2.dll | FileCheck -check-prefix=EXP-EXPORT %s
+RUN: llvm-objdump -s --section=.test exports2.dll | FileCheck --check-prefix=EXP-DATA %s
+RUN: llvm-readobj --coff-load-config exports2.dll | FileCheck -check-prefix=EXP-CHPE %s
+
+RUN: lld-link -out:entry.dll -machine:arm64ec arm64ec-func.obj loadconfig-arm64ec.obj -dll -entry:arm64ec_func
+
+RUN: llvm-objdump -d entry.dll | FileCheck -check-prefix=ENTRY-DISASM %s
+ENTRY-DISASM: Disassembly of section .text:
+ENTRY-DISASM-EMPTY:
+ENTRY-DISASM-NEXT: 0000000180001000 <.text>:
+ENTRY-DISASM-NEXT: 180001000: 90000008 adrp x8, 0x180001000 <.text>
+ENTRY-DISASM-NEXT: 180001004: 52800040 mov w0, #0x2 // =2
+ENTRY-DISASM-NEXT: 180001008: d65f03c0 ret
+ENTRY-DISASM-EMPTY:
+ENTRY-DISASM-NEXT: Disassembly of section .hexpthk:
+ENTRY-DISASM-EMPTY:
+ENTRY-DISASM-NEXT: 0000000180002000 <.hexpthk>:
+ENTRY-DISASM-NEXT: 180002000: 48 8b c4 movq %rsp, %rax
+ENTRY-DISASM-NEXT: 180002003: 48 89 58 20 movq %rbx, 0x20(%rax)
+ENTRY-DISASM-NEXT: 180002007: 55 pushq %rbp
+ENTRY-DISASM-NEXT: 180002008: 5d popq %rbp
+ENTRY-DISASM-NEXT: 180002009: e9 f2 ef ff ff jmp 0x180001000 <.text>
+ENTRY-DISASM-NEXT: 18000200e: cc int3
+ENTRY-DISASM-NEXT: 18000200f: cc int3
+
+RUN: llvm-readobj --headers entry.dll | FileCheck -check-prefix=ENTRY %s
+ENTRY: AddressOfEntryPoint: 0x2000
+
+RUN: llvm-readobj --coff-load-config entry.dll | FileCheck -check-prefix=ENTRY-CHPE %s
+ENTRY-CHPE: CodeMap [
+ENTRY-CHPE-NEXT: 0x1000 - 0x100C ARM64EC
+ENTRY-CHPE-NEXT: 0x2000 - 0x2010 X64
+ENTRY-CHPE-NEXT: ]
+
+
+#--- arm64ec-func.s
+ .text
+ .globl arm64ec_func
+ .p2align 2, 0x0
+arm64ec_func:
+ adrp x8,arm64ec_func
+ mov w0, #2
+ ret
+
+#--- antidep-func.s
+ .text
+ .globl "#arm64ec_func"
+ .p2align 2, 0x0
+"#arm64ec_func":
+ adrp x8,arm64ec_func
+ mov w0, #2
+ ret
+
+ .weak_anti_dep arm64ec_func
+arm64ec_func = "#arm64ec_func"
+
+#--- arm64ec-data.s
+ .section .test, "r"
+ .globl data_sym
+ .p2align 2, 0x0
+data_sym:
+ .rva "EXP+#func"
+ .rva "EXP+#arm64ec_func"
+
+#--- x86_64-func.s
+ .text
+ .globl x86_64_func
+ .p2align 2, 0x0
+x86_64_func:
+ call arm64ec_func
+ movl $3, %eax
+ retq
diff --git a/lld/test/COFF/arm64ec-exports.s b/lld/test/COFF/arm64ec-exports.s
index a48211e6fb76c1..870540d6104621 100644
--- a/lld/test/COFF/arm64ec-exports.s
+++ b/lld/test/COFF/arm64ec-exports.s
@@ -16,32 +16,32 @@
; EXP: Export {
; EXP-NEXT: Ordinal: 1
; EXP-NEXT: Name: #mangled_data_sym
-; EXP-NEXT: RVA: 0x3000
+; EXP-NEXT: RVA: 0x4000
; EXP-NEXT: }
; EXP-NEXT: Export {
; EXP-NEXT: Ordinal: 2
; EXP-NEXT: Name: ?cxx_func@@YAHXZ
-; EXP-NEXT: RVA: 0x1018
+; EXP-NEXT: RVA: 0x2030
; EXP-NEXT: }
; EXP-NEXT: Export {
; EXP-NEXT: Ordinal: 3
; EXP-NEXT: Name: data_sym
-; EXP-NEXT: RVA: 0x3004
+; EXP-NEXT: RVA: 0x4004
; EXP-NEXT: }
; EXP-NEXT: Export {
; EXP-NEXT: Ordinal: 4
; EXP-NEXT: Name: exportas_func
-; EXP-NEXT: RVA: 0x1010
+; EXP-NEXT: RVA: 0x2020
; EXP-NEXT: }
; EXP-NEXT: Export {
; EXP-NEXT: Ordinal: 5
; EXP-NEXT: Name: mangled_func
-; EXP-NEXT: RVA: 0x1008
+; EXP-NEXT: RVA: 0x2010
; EXP-NEXT: }
; EXP-NEXT: Export {
; EXP-NEXT: Ordinal: 6
; EXP-NEXT: Name: unmangled_func
-; EXP-NEXT: RVA: 0x1000
+; EXP-NEXT: RVA: 0x2000
; EXP-NEXT: }
; RUN: llvm-nm --print-armap out.lib | FileCheck --check-prefix=IMPLIB %s
diff --git a/lld/test/COFF/arm64ec-patchable-thunks.test b/lld/test/COFF/arm64ec-patchable-thunks.test
new file mode 100644
index 00000000000000..cccd42eebfd367
--- /dev/null
+++ b/lld/test/COFF/arm64ec-patchable-thunks.test
@@ -0,0 +1,86 @@
+REQUIRES: aarch64, x86
+RUN: split-file %s %t.dir && cd %t.dir
+
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-patchable.s -o arm64ec-patchable.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows arm64ec-alias.s -o arm64ec-alias.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows test-sec.s -o test-sec.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows %S/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+
+RUN: lld-link -out:test.dll -machine:arm64ec arm64ec-patchable.obj test-sec.obj loadconfig-arm64ec.obj -dll -noentry
+
+RUN: llvm-objdump -d test.dll | FileCheck -check-prefix=PATCH-DISASM %s
+PATCH-DISASM: Disassembly of section .text:
+PATCH-DISASM-EMPTY:
+PATCH-DISASM-NEXT: 0000000180001000 <.text>:
+PATCH-DISASM-NEXT: 180001000: 52800040 mov w0, #0x2 // =2
+PATCH-DISASM-NEXT: 180001004: d65f03c0 ret
+PATCH-DISASM-EMPTY:
+PATCH-DISASM-NEXT: Disassembly of section .hexpthk:
+PATCH-DISASM-EMPTY:
+PATCH-DISASM-NEXT: 0000000180002000
+PATCH-DISASM-NEXT: 180002000: 48 8b c4 movq %rsp, %rax
+PATCH-DISASM-NEXT: 180002003: 48 89 58 20 movq %rbx, 0x20(%rax)
+PATCH-DISASM-NEXT: 180002007: 55 pushq %rbp
+PATCH-DISASM-NEXT: 180002008: 5d popq %rbp
+PATCH-DISASM-NEXT: 180002009: e9 f2 ef ff ff jmp 0x180001000 <.text>
+PATCH-DISASM-NEXT: 18000200e: cc int3
+PATCH-DISASM-NEXT: 18000200f: cc int3
+
+RUN: llvm-readobj --hex-dump=.test test.dll | FileCheck -check-prefix=RVA %s
+RVA: 0x180005000 00200000
+
+RUN: llvm-readobj --coff-load-config test.dll | FileCheck -check-prefix=PATCH-CHPE %s
+PATCH-CHPE: CodeMap [
+PATCH-CHPE-NEXT: 0x1000 - 0x1008 ARM64EC
+PATCH-CHPE-NEXT: 0x2000 - 0x2010 X64
+PATCH-CHPE-NEXT: ]
+
+
+RUN: lld-link -out:test2.dll -machine:arm64ec arm64ec-alias.obj test-sec.obj loadconfig-arm64ec.obj -dll -noentry
+
+RUN: llvm-objdump -d test2.dll | FileCheck -check-prefix=PATCH-DISASM %s
+RUN: llvm-readobj --hex-dump=.test test2.dll | FileCheck -check-prefix=RVA %s
+RUN: llvm-readobj --coff-load-config test2.dll | FileCheck -check-prefix=PATCH-CHPE %s
+
+RUN: lld-link -out:test3.dll -machine:arm64ec arm64ec-alias.obj test-sec.obj loadconfig-arm64ec.obj -dll -noentry -export:patchable_func
+
+RUN: llvm-objdump -d test3.dll | FileCheck -check-prefix=PATCH-DISASM %s
+RUN: llvm-readobj --hex-dump=.test test3.dll | FileCheck -check-prefix=RVA %s
+RUN: llvm-readobj --coff-load-config test3.dll | FileCheck -check-prefix=PATCH-CHPE %s
+
+
+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
+
+
+#--- arm64ec-patchable.s
+ .section ".text", "x", discard, "#patchable_func$hp_target"
+ .globl "#patchable_func$hp_target"
+ .p2align 2, 0x0
+"#patchable_func$hp_target":
+ mov w0, #2
+ ret
+
+ .def "EXP+#patchable_func"
+ .scl 2
+ .type 32
+ .endef
+
+#--- arm64ec-alias.s
+ .section ".text", "x", discard, "#patchable_func$hp_target"
+ .globl "#patchable_func$hp_target"
+ .p2align 2, 0x0
+"#patchable_func$hp_target":
+ mov w0, #2
+ ret
+
+ .def "EXP+#patchable_func"
+ .scl 2
+ .type 32
+ .endef
+ .weak patchable_func
+patchable_func = "EXP+#patchable_func"
+
+#--- test-sec.s
+ .section ".test", "rd"
+ .rva "EXP+#patchable_func"
More information about the llvm-commits
mailing list