[lld] [LLD][COFF] Add support for ARM64EC import call thunks. (PR #107931)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Sep 9 15:44:59 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lld-coff
Author: Jacek Caban (cjacek)
<details>
<summary>Changes</summary>
These thunks can be accessed using `__impchk_*` symbols, though they are
typically not called directly. Instead, they are used to populate the
auxiliary IAT. When the imported function is x86_64 (or an ARM64EC
function with a patched export thunk), the thunk is used to call it.
Otherwise, the OS may replace the thunk at runtime with a direct
pointer to the ARM64EC function to avoid the overhead.
---
Full diff: https://github.com/llvm/llvm-project/pull/107931.diff
15 Files Affected:
- (modified) lld/COFF/Chunks.cpp (+17)
- (modified) lld/COFF/Chunks.h (+24)
- (modified) lld/COFF/Config.h (+1)
- (modified) lld/COFF/Driver.cpp (+6-1)
- (modified) lld/COFF/Driver.h (+2)
- (modified) lld/COFF/InputFiles.cpp (+27-2)
- (modified) lld/COFF/InputFiles.h (+2)
- (modified) lld/COFF/MarkLive.cpp (+16-6)
- (modified) lld/COFF/SymbolTable.cpp (+17-3)
- (modified) lld/COFF/SymbolTable.h (+5-2)
- (modified) lld/COFF/Symbols.cpp (+3-15)
- (modified) lld/COFF/Symbols.h (+1-1)
- (modified) lld/COFF/Writer.cpp (+2)
- (modified) lld/test/COFF/Inputs/loadconfig-arm64ec.s (+2)
- (modified) lld/test/COFF/arm64ec-import.test (+72-10)
``````````diff
diff --git a/lld/COFF/Chunks.cpp b/lld/COFF/Chunks.cpp
index 060eb6c32004d2..2bf0d644f9820d 100644
--- a/lld/COFF/Chunks.cpp
+++ b/lld/COFF/Chunks.cpp
@@ -1093,4 +1093,21 @@ void CHPERedirectionChunk::writeTo(uint8_t *buf) const {
}
}
+ImportThunkChunkARM64EC::ImportThunkChunkARM64EC(ImportFile *file)
+ : ImportThunkChunk(file->ctx, file->impSym), file(file) {}
+
+void ImportThunkChunkARM64EC::writeTo(uint8_t *buf) const {
+ memcpy(buf, importThunkARM64EC, sizeof(importThunkARM64EC));
+ applyArm64Addr(buf, file->impSym->getRVA(), rva, 12);
+ applyArm64Ldr(buf + 4, file->impSym->getRVA() & 0xfff);
+
+ uint32_t exitThunkRVA = exitThunk ? exitThunk->getRVA() : 0;
+ applyArm64Addr(buf + 8, exitThunkRVA, rva + 8, 12);
+ applyArm64Imm(buf + 12, exitThunkRVA & 0xfff, 0);
+
+ Defined *helper = dyn_cast<Defined>(file->ctx.config.arm64ECIcallHelper);
+ if (helper)
+ applyArm64Branch26(buf + 16, helper->getRVA() - rva - 16);
+}
+
} // namespace lld::coff
diff --git a/lld/COFF/Chunks.h b/lld/COFF/Chunks.h
index 30e5b538c352ea..28e0fd68ac5159 100644
--- a/lld/COFF/Chunks.h
+++ b/lld/COFF/Chunks.h
@@ -544,6 +544,14 @@ static const uint8_t importThunkARM64[] = {
0x00, 0x02, 0x1f, 0xd6, // br x16
};
+static const uint32_t importThunkARM64EC[] = {
+ 0x9000000b, // adrp x11, 0x0
+ 0xf940016b, // ldr x11, [x11]
+ 0x9000000a, // adrp x10, 0x0
+ 0x9100014a, // add x10, x10, #0x0
+ 0x14000000 // b 0x0
+};
+
// Windows-specific.
// A chunk for DLL import jump table entry. In a final output, its
// contents will be a JMP instruction to some __imp_ symbol.
@@ -599,6 +607,22 @@ class ImportThunkChunkARM64 : public ImportThunkChunk {
MachineTypes getMachine() const override { return ARM64; }
};
+// ARM64EC __impchk_* thunk implementation.
+// Performs an indirect call to an imported function pointer
+// using the __icall_helper_arm64ec helper function.
+class ImportThunkChunkARM64EC : public ImportThunkChunk {
+public:
+ explicit ImportThunkChunkARM64EC(ImportFile *file);
+ size_t getSize() const override { return sizeof(importThunkARM64EC); };
+ MachineTypes getMachine() const override { return ARM64EC; }
+ void writeTo(uint8_t *buf) const override;
+
+ Defined *exitThunk;
+
+private:
+ ImportFile *file;
+};
+
class RangeExtensionThunkARM : public NonSectionCodeChunk {
public:
explicit RangeExtensionThunkARM(COFFLinkerContext &ctx, Defined *t)
diff --git a/lld/COFF/Config.h b/lld/COFF/Config.h
index 947f3fead54e03..738776a971ea39 100644
--- a/lld/COFF/Config.h
+++ b/lld/COFF/Config.h
@@ -164,6 +164,7 @@ struct Configuration {
std::set<std::string> delayLoads;
std::map<std::string, int> dllOrder;
Symbol *delayLoadHelper = nullptr;
+ Symbol *arm64ECIcallHelper = nullptr;
bool saveTemps = false;
diff --git a/lld/COFF/Driver.cpp b/lld/COFF/Driver.cpp
index 3ef9fa3f65c6a6..a1fe6444991a36 100644
--- a/lld/COFF/Driver.cpp
+++ b/lld/COFF/Driver.cpp
@@ -1383,6 +1383,11 @@ void LinkerDriver::createECExportThunks() {
}
}
+void LinkerDriver::pullArm64ECIcallHelper() {
+ if (!ctx.config.arm64ECIcallHelper)
+ ctx.config.arm64ECIcallHelper = addUndefined("__icall_helper_arm64ec");
+}
+
// 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
@@ -2685,7 +2690,7 @@ void LinkerDriver::linkerMain(ArrayRef<const char *> argsArr) {
if (auto *arg = args.getLastArg(OPT_print_symbol_order))
config->printSymbolOrder = arg->getValue();
- ctx.symtab.initializeEntryThunks();
+ ctx.symtab.initializeECThunks();
// Identify unreferenced COMDAT sections.
if (config->doGC) {
diff --git a/lld/COFF/Driver.h b/lld/COFF/Driver.h
index b5cf8e2f18fd4e..0c195a7cc3148c 100644
--- a/lld/COFF/Driver.h
+++ b/lld/COFF/Driver.h
@@ -101,6 +101,8 @@ class LinkerDriver {
std::unique_ptr<llvm::TarWriter> tar; // for /linkrepro
+ void pullArm64ECIcallHelper();
+
private:
// Searches a file from search paths.
std::optional<StringRef> findFileIfNew(StringRef filename);
diff --git a/lld/COFF/InputFiles.cpp b/lld/COFF/InputFiles.cpp
index fa2d230075d9d3..e3cf5f03630b06 100644
--- a/lld/COFF/InputFiles.cpp
+++ b/lld/COFF/InputFiles.cpp
@@ -190,6 +190,8 @@ void ObjFile::initializeECThunks() {
ctx.symtab.addEntryThunk(getSymbol(entry->src), getSymbol(entry->dst));
break;
case Arm64ECThunkType::Exit:
+ ctx.symtab.addExitThunk(getSymbol(entry->src), getSymbol(entry->dst));
+ break;
case Arm64ECThunkType::GuestExit:
break;
default:
@@ -1069,10 +1071,33 @@ void ImportFile::parse() {
// DLL functions just like regular non-DLL functions.)
if (hdr->getType() == llvm::COFF::IMPORT_CODE) {
if (ctx.config.machine != ARM64EC) {
- thunkSym = ctx.symtab.addImportThunk(name, impSym, hdr->Machine);
+ ImportThunkChunk *chunk;
+ switch (hdr->Machine) {
+ case AMD64:
+ chunk = make<ImportThunkChunkX64>(ctx, impSym);
+ break;
+ case I386:
+ chunk = make<ImportThunkChunkX86>(ctx, impSym);
+ break;
+ case ARM64:
+ chunk = make<ImportThunkChunkARM64>(ctx, impSym);
+ break;
+ case ARMNT:
+ chunk = make<ImportThunkChunkARM>(ctx, impSym);
+ break;
+ default:
+ llvm_unreachable("unknown machine type");
+ }
+ thunkSym = ctx.symtab.addImportThunk(name, impSym, chunk);
} else {
- thunkSym = ctx.symtab.addImportThunk(name, impSym, AMD64);
+ thunkSym = ctx.symtab.addImportThunk(
+ name, impSym, make<ImportThunkChunkX64>(ctx, impSym));
// FIXME: Add aux IAT symbols.
+
+ StringRef impChkName = saver().save("__impchk_" + name);
+ impchkThunk = make<ImportThunkChunkARM64EC>(this);
+ ctx.symtab.addImportThunk(impChkName, impSym, impchkThunk);
+ ctx.driver.pullArm64ECIcallHelper();
}
}
}
diff --git a/lld/COFF/InputFiles.h b/lld/COFF/InputFiles.h
index 8b3303a8d87f45..af2f85739febbb 100644
--- a/lld/COFF/InputFiles.h
+++ b/lld/COFF/InputFiles.h
@@ -55,6 +55,7 @@ class Defined;
class DefinedImportData;
class DefinedImportThunk;
class DefinedRegular;
+class ImportThunkChunkARM64EC;
class SectionChunk;
class Symbol;
class Undefined;
@@ -348,6 +349,7 @@ class ImportFile : public InputFile {
DefinedImportData *impSym = nullptr;
Symbol *thunkSym = nullptr;
+ ImportThunkChunkARM64EC *impchkThunk = nullptr;
std::string dllName;
private:
diff --git a/lld/COFF/MarkLive.cpp b/lld/COFF/MarkLive.cpp
index 06079a98f2d00a..8af58780e13582 100644
--- a/lld/COFF/MarkLive.cpp
+++ b/lld/COFF/MarkLive.cpp
@@ -43,13 +43,23 @@ void markLive(COFFLinkerContext &ctx) {
worklist.push_back(c);
};
- auto addSym = [&](Symbol *b) {
- if (auto *sym = dyn_cast<DefinedRegular>(b))
+ std::function<void(Symbol *)> addSym;
+
+ auto addImportFile = [&](ImportFile *file) {
+ file->live = true;
+ if (file->impchkThunk && file->impchkThunk->exitThunk)
+ addSym(file->impchkThunk->exitThunk);
+ };
+
+ addSym = [&](Symbol *b) {
+ if (auto *sym = dyn_cast<DefinedRegular>(b)) {
enqueue(sym->getChunk());
- else if (auto *sym = dyn_cast<DefinedImportData>(b))
- sym->file->live = true;
- else if (auto *sym = dyn_cast<DefinedImportThunk>(b))
- sym->wrappedSym->file->live = sym->wrappedSym->file->thunkLive = true;
+ } else if (auto *sym = dyn_cast<DefinedImportData>(b)) {
+ addImportFile(sym->file);
+ } else if (auto *sym = dyn_cast<DefinedImportThunk>(b)) {
+ addImportFile(sym->wrappedSym->file);
+ sym->wrappedSym->file->thunkLive = true;
+ }
};
// Add GC root chunks.
diff --git a/lld/COFF/SymbolTable.cpp b/lld/COFF/SymbolTable.cpp
index bb7583bb9a7df4..a6575ecac3bb44 100644
--- a/lld/COFF/SymbolTable.cpp
+++ b/lld/COFF/SymbolTable.cpp
@@ -557,7 +557,11 @@ void SymbolTable::addEntryThunk(Symbol *from, Symbol *to) {
entryThunks.push_back({from, to});
}
-void SymbolTable::initializeEntryThunks() {
+void SymbolTable::addExitThunk(Symbol *from, Symbol *to) {
+ exitThunks[from] = to;
+}
+
+void SymbolTable::initializeECThunks() {
for (auto it : entryThunks) {
auto *to = dyn_cast<Defined>(it.second);
if (!to)
@@ -573,6 +577,16 @@ void SymbolTable::initializeEntryThunks() {
}
from->getChunk()->setEntryThunk(to);
}
+
+ for (ImportFile *file : ctx.importFileInstances) {
+ if (!file->impchkThunk)
+ continue;
+
+ Symbol *sym = exitThunks.lookup(file->thunkSym);
+ if (!sym)
+ sym = exitThunks.lookup(file->impSym);
+ file->impchkThunk->exitThunk = dyn_cast_or_null<Defined>(sym);
+ }
}
Symbol *SymbolTable::addUndefined(StringRef name, InputFile *f,
@@ -784,11 +798,11 @@ DefinedImportData *SymbolTable::addImportData(StringRef n, ImportFile *f) {
}
Symbol *SymbolTable::addImportThunk(StringRef name, DefinedImportData *id,
- uint16_t machine) {
+ ImportThunkChunk *chunk) {
auto [s, wasInserted] = insert(name, nullptr);
s->isUsedInRegularObj = true;
if (wasInserted || isa<Undefined>(s) || s->isLazy()) {
- replaceSymbol<DefinedImportThunk>(s, ctx, name, id, machine);
+ replaceSymbol<DefinedImportThunk>(s, ctx, name, id, chunk);
return s;
}
diff --git a/lld/COFF/SymbolTable.h b/lld/COFF/SymbolTable.h
index 51c6c79ec14463..13e151e3a8c501 100644
--- a/lld/COFF/SymbolTable.h
+++ b/lld/COFF/SymbolTable.h
@@ -28,6 +28,7 @@ class COFFLinkerContext;
class Defined;
class DefinedAbsolute;
class DefinedRegular;
+class ImportThunkChunk;
class LazyArchive;
class SectionChunk;
class Symbol;
@@ -104,10 +105,11 @@ class SymbolTable {
CommonChunk *c = nullptr);
DefinedImportData *addImportData(StringRef n, ImportFile *f);
Symbol *addImportThunk(StringRef name, DefinedImportData *s,
- uint16_t machine);
+ ImportThunkChunk *chunk);
void addLibcall(StringRef name);
void addEntryThunk(Symbol *from, Symbol *to);
- void initializeEntryThunks();
+ void addExitThunk(Symbol *from, Symbol *to);
+ void initializeECThunks();
void reportDuplicate(Symbol *existing, InputFile *newFile,
SectionChunk *newSc = nullptr,
@@ -140,6 +142,7 @@ class SymbolTable {
std::unique_ptr<BitcodeCompiler> lto;
bool ltoCompilationDone = false;
std::vector<std::pair<Symbol *, Symbol *>> entryThunks;
+ llvm::DenseMap<Symbol *, Symbol *> exitThunks;
COFFLinkerContext &ctx;
};
diff --git a/lld/COFF/Symbols.cpp b/lld/COFF/Symbols.cpp
index b098abb80d6f1e..5f4d797f74a2dd 100644
--- a/lld/COFF/Symbols.cpp
+++ b/lld/COFF/Symbols.cpp
@@ -107,22 +107,10 @@ COFFSymbolRef DefinedCOFF::getCOFFSymbol() {
uint64_t DefinedAbsolute::getRVA() { return va - ctx.config.imageBase; }
-static Chunk *makeImportThunk(COFFLinkerContext &ctx, DefinedImportData *s,
- uint16_t machine) {
- if (machine == AMD64)
- return make<ImportThunkChunkX64>(ctx, s);
- if (machine == I386)
- return make<ImportThunkChunkX86>(ctx, s);
- if (machine == ARM64)
- return make<ImportThunkChunkARM64>(ctx, s);
- assert(machine == ARMNT);
- return make<ImportThunkChunkARM>(ctx, s);
-}
-
DefinedImportThunk::DefinedImportThunk(COFFLinkerContext &ctx, StringRef name,
- DefinedImportData *s, uint16_t machine)
- : Defined(DefinedImportThunkKind, name), wrappedSym(s),
- data(makeImportThunk(ctx, s, machine)) {}
+ DefinedImportData *s,
+ ImportThunkChunk *chunk)
+ : Defined(DefinedImportThunkKind, name), wrappedSym(s), data(chunk) {}
Defined *Undefined::getWeakAlias() {
// A weak alias may be a weak alias to another symbol, so check recursively.
diff --git a/lld/COFF/Symbols.h b/lld/COFF/Symbols.h
index c427a062dc82b2..724330e4bab958 100644
--- a/lld/COFF/Symbols.h
+++ b/lld/COFF/Symbols.h
@@ -388,7 +388,7 @@ class DefinedImportData : public Defined {
class DefinedImportThunk : public Defined {
public:
DefinedImportThunk(COFFLinkerContext &ctx, StringRef name,
- DefinedImportData *s, uint16_t machine);
+ DefinedImportData *s, ImportThunkChunk *chunk);
static bool classof(const Symbol *s) {
return s->kind() == DefinedImportThunkKind;
diff --git a/lld/COFF/Writer.cpp b/lld/COFF/Writer.cpp
index 3cb9b3b512ead6..b589a16bca32a3 100644
--- a/lld/COFF/Writer.cpp
+++ b/lld/COFF/Writer.cpp
@@ -1248,6 +1248,8 @@ void Writer::appendImportThunks() {
DefinedImportThunk *thunk = cast<DefinedImportThunk>(file->thunkSym);
if (file->thunkLive)
textSec->addChunk(thunk->getChunk());
+ if (file->impchkThunk)
+ textSec->addChunk(file->impchkThunk);
}
if (!delayIdata.empty()) {
diff --git a/lld/test/COFF/Inputs/loadconfig-arm64ec.s b/lld/test/COFF/Inputs/loadconfig-arm64ec.s
index 78e7fba43a0a4d..75dc6105301d00 100644
--- a/lld/test/COFF/Inputs/loadconfig-arm64ec.s
+++ b/lld/test/COFF/Inputs/loadconfig-arm64ec.s
@@ -30,6 +30,8 @@ __os_arm64x_dispatch_ret:
.xword 0
__os_arm64x_check_call:
.xword 0
+ .globl __os_arm64x_dispatch_icall
+__os_arm64x_dispatch_icall:
__os_arm64x_check_icall:
.xword 0
__os_arm64x_get_x64_information:
diff --git a/lld/test/COFF/arm64ec-import.test b/lld/test/COFF/arm64ec-import.test
index b1c47d785e445b..3a7ef74900f679 100644
--- a/lld/test/COFF/arm64ec-import.test
+++ b/lld/test/COFF/arm64ec-import.test
@@ -2,17 +2,19 @@ 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 icall.s -o icall.obj
+RUN: llvm-mc -filetype=obj -triple=arm64ec-windows hybmp.s -o hybmp.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: llvm-lib -machine:x64 -def:test.def -out:test-x86_64.lib
Link using ARM64EC import library:
-RUN: lld-link -machine:arm64ec -dll -noentry -out:out.dll loadconfig-arm64ec.obj \
+RUN: lld-link -machine:arm64ec -dll -noentry -out:out.dll loadconfig-arm64ec.obj icall.obj hybmp.obj \
RUN: test.obj test-arm64ec.lib test2-arm64ec.lib
Link using x86_64 import library:
-RUN: lld-link -machine:arm64ec -dll -noentry -out:out2.dll loadconfig-arm64ec.obj \
+RUN: lld-link -machine:arm64ec -dll -noentry -out:out2.dll loadconfig-arm64ec.obj icall.obj hybmp.obj \
RUN: test.obj test-x86_64.lib test2-arm64ec.lib
RUN: llvm-readobj --coff-imports out.dll | FileCheck --check-prefix=IMPORTS %s
@@ -20,7 +22,7 @@ RUN: llvm-readobj --coff-imports out2.dll | FileCheck --check-prefix=IMPORTS %s
IMPORTS: Import {
IMPORTS-NEXT: Name: test.dll
IMPORTS-NEXT: ImportLookupTableRVA:
-IMPORTS-NEXT: ImportAddressTableRVA: 0x2000
+IMPORTS-NEXT: ImportAddressTableRVA: 0x3000
IMPORTS-NEXT: Symbol: data (0)
IMPORTS-NEXT: Symbol: func (0)
IMPORTS-NEXT: Symbol: func2 (0)
@@ -28,24 +30,43 @@ IMPORTS-NEXT: }
IMPORTS-NEXT: Import {
IMPORTS-NEXT: Name: test2.dll
IMPORTS-NEXT: ImportLookupTableRVA:
-IMPORTS-NEXT: ImportAddressTableRVA: 0x2020
+IMPORTS-NEXT: ImportAddressTableRVA: 0x3020
IMPORTS-NEXT: Symbol: t2func (0)
IMPORTS-NEXT: }
RUN: llvm-objdump -d out.dll | FileCheck --check-prefix=DISASM %s
RUN: llvm-objdump -d out2.dll | FileCheck --check-prefix=DISASM %s
-DISASM: 0000000180001000 <.text>:
-DISASM-NEXT: 180001000: ff 25 02 10 00 00 jmpq *0x1002(%rip) # 0x180002008
+DISASM: 180001000: 52800000 mov w0, #0x0 // =0
+DISASM-NEXT: 180001004: d65f03c0 ret
+DISASM-NEXT: 180001008: d000000b adrp x11, 0x180003000
+DISASM-NEXT: 18000100c: f940056b ldr x11, [x11, #0x8]
+DISASM-NEXT: 180001010: 9000000a adrp x10, 0x180001000 <.text>
+DISASM-NEXT: 180001014: 9101114a add x10, x10, #0x44
+DISASM-NEXT: 180001018: 17fffffa b 0x180001000 <.text>
+DISASM-NEXT: 18000101c: d000000b adrp x11, 0x180003000
+DISASM-NEXT: 180001020: f940096b ldr x11, [x11, #0x10]
+DISASM-NEXT: 180001024: f0ffffea adrp x10, 0x180000000
+DISASM-NEXT: 180001028: 9100014a add x10, x10, #0x0
+DISASM-NEXT: 18000102c: 17fffff5 b 0x180001000 <.text>
+DISASM-NEXT: 180001030: d000000b adrp x11, 0x180003000
+DISASM-NEXT: 180001034: f940116b ldr x11, [x11, #0x20]
+DISASM-NEXT: 180001038: 9000000a adrp x10, 0x180001000 <.text>
+DISASM-NEXT: 18000103c: 9101314a add x10, x10, #0x4c
+DISASM-NEXT: 180001040: 17fffff0 b 0x180001000 <.text>
+DISASM-NEXT: 180001044: 52800020 mov w0, #0x1 // =1
+DISASM-NEXT: 180001048: d65f03c0 ret
+DISASM-NEXT: 18000104c: 52800040 mov w0, #0x2 // =2
+DISASM-NEXT: 180001050: d65f03c0 ret
RUN: llvm-readobj --hex-dump=.test out.dll | FileCheck --check-prefix=TESTSEC %s
RUN: llvm-readobj --hex-dump=.test out2.dll | FileCheck --check-prefix=TESTSEC %s
-TESTSEC: 0x180005000 08200000 00200000 10200000 20200000
-TESTSEC-NEXT: 0x180005010 00100000
+TESTSEC: 0x180006000 08300000 00300000 10300000 20300000
+TESTSEC-NEXT: 0x180006010 08100000 1c100000 00200000
RUN: llvm-readobj --headers out.dll | FileCheck -check-prefix=HEADERS %s
-HEADERS: LoadConfigTableRVA: 0x3008
-HEADERS: IATRVA: 0x2000
+HEADERS: LoadConfigTableRVA: 0x4010
+HEADERS: IATRVA: 0x3000
HEADERS: IATSize: 0x1000
#--- test.s
@@ -57,8 +78,49 @@ arm64ec_data_sym:
.rva __imp_data
.rva __imp_func2
.rva __imp_t2func
+ .rva __impchk_func
+ .rva __impchk_func2
.rva func
+#--- icall.s
+ .text
+ .globl __icall_helper_arm64ec
+ .p2align 2, 0x0
+__icall_helper_arm64ec:
+ mov w0, #0
+ ret
+
+#--- hybmp.s
+ .section .hybmp$x, "yi"
+ // __imp_func exit thunk is ignored when func is defined as well
+ .symidx __imp_func
+ .symidx dead_exit_thunk
+ .word 4
+ .symidx func
+ .symidx func_exit_thunk
+ .word 4
+ .symidx __imp_t2func
+ .symidx t2func_exit_thunk
+ .word 4
+
+ .section .wowthk$aa,"xr",discard,func_exit_thunk
+ .globl func_exit_thunk
+func_exit_thunk:
+ mov w0, #1
+ ret
+
+ .section .wowthk$aa,"xr",discard,t2func_exit_thunk
+ .globl t2func_exit_thunk
+t2func_exit_thunk:
+ mov w0, #2
+ ret
+
+ .section .wowthk$aa,"xr",discard,dead_exit_thunk
+ .globl dead_exit_thunk
+dead_exit_thunk:
+ mov w0, #0xdead
+ ret
+
#--- test.def
NAME test.dll
EXPORTS
``````````
</details>
https://github.com/llvm/llvm-project/pull/107931
More information about the llvm-commits
mailing list