diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp index 2807c89452051..be44950a1720e 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 df311524a8d18..5443d4619a977 100644 --- a/lld/COFF/Chunks.h +++ b/lld/COFF/Chunks.h @@ -749,6 +749,26 @@ class ECCodeMapChunk : public NonSectionChunk { std::vector ↦ }; +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 9e28b1c50be50..c09c91fe4b171 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(sym)) + def = undef->getWeakAlias(); + else + def = dyn_cast(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(sym)) { + if (!undef->getWeakAlias()) { + auto thunk = make(def); + replaceSymbol(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(sym)) + targetSym = undef->getWeakAlias(); + else + targetSym = dyn_cast(sym); + if (!targetSym) + continue; + + auto *undef = dyn_cast(s); + if (undef && !undef->getWeakAlias()) { + auto thunk = make(targetSym); + replaceSymbol(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 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 fa54de05befb5..b5cf8e2f18fd4 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 mbs, ArrayRef 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 6c3c4e3931aa8..1dfff0a90f4ae 100644 --- a/lld/COFF/SymbolTable.cpp +++ b/lld/COFF/SymbolTable.cpp @@ -551,6 +551,9 @@ std::pair 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 93b376b69f7ec..b5f95d2ad7f11 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 localImportChunks; + // A list of EC EXP+ symbols. + std::vector expSymbols; + // Iterates symbols in non-determinstic hash table order. template void forEachSymbol(T callback) { for (auto &pair : symMap) diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp index f776c76a47ae9..776595d98c391 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(s); + if (!sym || !sym->getChunk()) + continue; + if (auto thunk = dyn_cast(sym->getChunk())) + hexpthkSec->addChunk(thunk); + } + auto codeMapChunk = make(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 0000000000000..6ed0514d4b17f --- /dev/null +++ b/lld/test/COFF/arm64ec-export-thunks.test @@ -0,0 +1,188 @@ +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=arm64ec-windows arm64ec-data-sym.s -o arm64ec-data-sym.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 : +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 : +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 : +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: ] + + +Test exporting data symbol as a function: + +RUN: lld-link -out:data-func.dll -machine:arm64ec arm64ec-data-sym.obj loadconfig-arm64ec.obj -dll -noentry -export:data_sym + +RUN: llvm-readobj --hex-dump=.test data-func.dll | FileCheck --check-prefix=DATAFUNC-TEST %s +DATAFUNC-TEST: Hex dump of section '.test': +DATAFUNC-TEST-NEXT: 0x180003000 00000000 .... + +RUN: llvm-readobj --coff-exports --hex-dump=.test data-func.dll | FileCheck --check-prefix=DATAFUNC-EXP %s +DATAFUNC-EXP: Export { +DATAFUNC-EXP-NEXT: Ordinal: 1 +DATAFUNC-EXP-NEXT: Name: data_sym +DATAFUNC-EXP-NEXT: RVA: 0x3000 +DATAFUNC-EXP-NEXT: } + + +Test mingw-style auto-export: + +RUN: lld-link -out:export-all.dll -machine:arm64ec arm64ec-func.obj loadconfig-arm64ec.obj -dll -noentry -lldmingw +RUN: llvm-objdump -d export-all.dll | FileCheck --check-prefix=EXPORT-ALL %s + +EXPORT-ALL: Disassembly of section .text: +EXPORT-ALL-EMPTY: +EXPORT-ALL-NEXT: 0000000180001000 <.text>: +EXPORT-ALL-NEXT: 180001000: 90000008 adrp x8, 0x180001000 <.text> +EXPORT-ALL-NEXT: 180001004: 52800040 mov w0, #0x2 // =2 +EXPORT-ALL-NEXT: 180001008: d65f03c0 ret +EXPORT-ALL-EMPTY: +EXPORT-ALL-NEXT: Disassembly of section .hexpthk: +EXPORT-ALL-EMPTY: +EXPORT-ALL-NEXT: 0000000180002000 : +EXPORT-ALL-NEXT: 180002000: 48 8b c4 movq %rsp, %rax +EXPORT-ALL-NEXT: 180002003: 48 89 58 20 movq %rbx, 0x20(%rax) +EXPORT-ALL-NEXT: 180002007: 55 pushq %rbp +EXPORT-ALL-NEXT: 180002008: 5d popq %rbp +EXPORT-ALL-NEXT: 180002009: e9 f2 ef ff ff jmp 0x180001000 <.text> +EXPORT-ALL-NEXT: 18000200e: cc int3 +EXPORT-ALL-NEXT: 18000200f: cc int3 + + +#--- 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 + +#--- arm64ec-data-sym.s + .section .test, "r" + .globl data_sym + .p2align 2, 0x0 +data_sym: + .word 0 diff --git a/lld/test/COFF/arm64ec-exports.s b/lld/test/COFF/arm64ec-exports.s index a48211e6fb76c..870540d610462 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 0000000000000..cccd42eebfd36 --- /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"