[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