Skip to content

[LLD][COFF] Add support for ARM64EC import call thunks. #107931

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
Sep 11, 2024

Conversation

cjacek
Copy link
Contributor

@cjacek cjacek commented Sep 9, 2024

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.

@llvmbot
Copy link
Member

llvmbot commented Sep 9, 2024

@llvm/pr-subscribers-platform-windows

@llvm/pr-subscribers-lld-coff

Author: Jacek Caban (cjacek)

Changes

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

@cjacek
Copy link
Contributor Author

cjacek commented Sep 9, 2024

Depends on #107929.

In its current form, this may cause a relocation error if the offset
to __icall_helper_arm64ec is too large. MSVC handles this by generating
range extension thunks in such cases. However, implementing this with the
current LLD architecture is tricky. My plan is to inline the range extension
thunks instead, which is both simpler and slightly more performant. See
cjacek@9310498 for a draft.

The loadconfig-arm64ec.s change is not strictly necessary with the current
implementation, but it is required when testing with MSVC. For some reason,
the MSVC linker requires the __os_arm64x_dispatch_icall symbol to be provided
by the CRT. Normally, it would be pulled in with the __icall_helper_arm64ec
implementation, but it is still required even when using the __icall_helper_arm64ec
stub, as in the tests for this PR.

Copy link
Member

@mstorsjo mstorsjo left a comment

Choose a reason for hiding this comment

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

Looks good overall. I'm not entirely following 100% the bits about exit thunks here (and how it is handled through MarkLive) though, but the code looks reasonable. Some questions on the writing of the thunk.

applyArm64Addr(buf, file->impSym->getRVA(), rva, 12);
applyArm64Ldr(buf + 4, file->impSym->getRVA() & 0xfff);

uint32_t exitThunkRVA = exitThunk ? exitThunk->getRVA() : 0;
Copy link
Member

Choose a reason for hiding this comment

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

If this is 0, won't this end up with a rather bogus instruction sequence? Shouldn't we replace the adrp+add with a mov x0, #0 or similar instead, in that case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, this behavior would be bogus. I followed MSVC's approach in these cases and implemented it the same way (since it's an RVA, it sets x10 to the image base, which requires addrp + add). We could either zero x10 or leave it unchanged in these cases. It's unlikely to matter for the OS loader, so I can make this change if you prefer.

Emitting a warning in such scenarios might be tempting, but we can’t do that because there are valid situations where an exit thunk may be missing. For example, if an application only ever takes the function’s address and never calls it directly via the import table. In these cases, the application uses __imp_aux_* symbols instead of the regular __imp_* symbols. We still need to populate the auxiliary IAT, which requires those thunks, even if they’re never used.

If we tracked __imp_ and __imp_aux_ usage separately, we might be able to skip the thunk and fill the auxiliary IAT with zeroes or something similar. However, since this behavior isn’t documented, skipping thunks would require ensuring the OS loader can handle it in all cases, which is tricky to verify with black-box testing. Therefore, it feels safer to follow MSVC’s behavior.

Another situation where exit thunks may not be available is when hand-written assembly is used to make the call. In such cases, if the callee is ARM64EC, it should still work because the thunk would usually be skipped. It’s possible the thunk wouldn’t be skipped (for example, if patching is detected in the callee’s area but not the callee itself). If the call goes through the thunk, __icall_helper_arm64ec wouldn’t use an exit thunk if the callee is ARM64EC. It would only fail if the callee isn’t ARM64EC, which the developer may know will never be the case.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, fair enough. So if the symbol is missing, we end up with RVA 0, which ends up in producing an adrp+add pair that produces the image base address, which is passed to __icall_helper_arm64ec, which wouldn't use it in this case - that sounds ok with me.

Perhaps a comment, saying that this 0 case can happen, but in these cases, the address ends up ignored - if I'm understanding the issue correctly?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it may happen in valid cases, but be either ignored by __icall_helper_arm64ec or the thole thunk may be unused/skipped.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will add a comment, thanks!


Defined *helper = dyn_cast<Defined>(file->ctx.config.arm64ECIcallHelper);
if (helper)
applyArm64Branch26(buf + 16, helper->getRVA() - rva - 16);
Copy link
Member

Choose a reason for hiding this comment

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

Same here - if this is undefined, won't the instruction end up quite bogus? But I guess this shouldn't really happen?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I intended to handle the -force argument but made an error. I'll drop the check. In its current form, it will fail to relocate if the helper isn't defined, as 0 is out of range, but it will work once range extension is implemented.

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.
@cjacek cjacek merged commit 99a2354 into llvm:main Sep 11, 2024
5 of 7 checks passed
@cjacek cjacek deleted the arm64ec-imp-thunk branch September 11, 2024 12:46
@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 11, 2024

LLVM Buildbot has detected a new failure on builder clang-aarch64-sve-vls running on linaro-g3-02 while building lld at step 13 "test-suite".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/143/builds/2076

Here is the relevant piece of the build log for the reference
Step 13 (test-suite) failure: 1200 seconds without output running [b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/sandbox/bin/python', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/sandbox/bin/lnt', b'runtest', b'test-suite', b'--no-timestamp', b'--sandbox', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/sandbox', b'--test-suite', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/test-suite', b'--cc', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/stage1.install/bin/clang', b'--cxx', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/stage1.install/bin/clang++', b'--use-lit', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/stage1/bin/llvm-lit', b'--cmake-define=TEST_SUITE_FORTRAN:STRING=ON', b'--cmake-define=CMAKE_Fortran_COMPILER=/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/stage1.install/bin/flang-new', b'--cppflags', b'-mcpu=neoverse-512tvb -msve-vector-bits=256 -mllvm -treat-scalable-fixed-error-as-warning=false -O3', b'--threads=32', b'--build-threads=32'], attempting to kill
...
-- Found compiler-rt builtin library: libclang_rt.builtins.a
-- Performing Test COMPILER_HAS_MATRIX_FLAG
-- Performing Test COMPILER_HAS_MATRIX_FLAG - Success
-- Looking for mmap
-- Looking for mmap - found
-- Configuring done (27.6s)
-- Generating done (18.1s)
-- Build files have been written to: /home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/sandbox/build
2024-09-11 15:56:56 INFO: Execute: /usr/local/bin/cmake --build . -t clean
2024-09-11 15:56:56 INFO:           (In /home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/sandbox/build)
command timed out: 1200 seconds without output running [b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/sandbox/bin/python', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/sandbox/bin/lnt', b'runtest', b'test-suite', b'--no-timestamp', b'--sandbox', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/sandbox', b'--test-suite', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/test/test-suite', b'--cc', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/stage1.install/bin/clang', b'--cxx', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/stage1.install/bin/clang++', b'--use-lit', b'/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/stage1/bin/llvm-lit', b'--cmake-define=TEST_SUITE_FORTRAN:STRING=ON', b'--cmake-define=CMAKE_Fortran_COMPILER=/home/tcwg-buildbot/worker/clang-aarch64-sve-vls/stage1.install/bin/flang-new', b'--cppflags', b'-mcpu=neoverse-512tvb -msve-vector-bits=256 -mllvm -treat-scalable-fixed-error-as-warning=false -O3', b'--threads=32', b'--build-threads=32'], attempting to kill
process killed by signal 9
program finished with exit code -1
elapsedTime=1264.072232

@llvm-ci
Copy link
Collaborator

llvm-ci commented Sep 11, 2024

LLVM Buildbot has detected a new failure on builder clang-aarch64-sve-vla running on linaro-g3-03 while building lld at step 7 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/17/builds/2598

Here is the relevant piece of the build log for the reference
Step 7 (ninja check 1) failure: stage 1 checked (failure)
******************** TEST 'libFuzzer-aarch64-default-Linux :: sigusr.test' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 6: rm -rf /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp
+ rm -rf /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp
RUN: at line 7: mkdir -p /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp
+ mkdir -p /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp
RUN: at line 8: /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/./bin/clang    -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta   --driver-mode=g++ -O2 -gline-tables-only -fsanitize=address,fuzzer -I/home/tcwg-buildbot/worker/clang-aarch64-sve-vla/llvm/compiler-rt/lib/fuzzer  -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta  /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/llvm/compiler-rt/test/fuzzer/SleepOneSecondTest.cpp -o /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp/LFSIGUSR
+ /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/./bin/clang -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta --driver-mode=g++ -O2 -gline-tables-only -fsanitize=address,fuzzer -I/home/tcwg-buildbot/worker/clang-aarch64-sve-vla/llvm/compiler-rt/lib/fuzzer -Wthread-safety -Wthread-safety-reference -Wthread-safety-beta /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/llvm/compiler-rt/test/fuzzer/SleepOneSecondTest.cpp -o /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp/LFSIGUSR
RUN: at line 10: /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp/LFSIGUSR 2> /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp/log & export PID=$!
+ export PID=1297756
+ PID=1297756
RUN: at line 11: sleep 2
+ sleep 2
+ /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp/LFSIGUSR
RUN: at line 12: kill -SIGUSR1 $PID
+ kill -SIGUSR1 1297756
RUN: at line 13: sleep 3
+ sleep 3
RUN: at line 14: cat /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp/log | FileCheck /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/llvm/compiler-rt/test/fuzzer/sigusr.test
+ cat /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/stage1/runtimes/runtimes-bins/compiler-rt/test/fuzzer/AARCH64DefaultLinuxConfig/Output/sigusr.test.tmp/log
+ FileCheck /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/llvm/compiler-rt/test/fuzzer/sigusr.test
/home/tcwg-buildbot/worker/clang-aarch64-sve-vla/llvm/compiler-rt/test/fuzzer/sigusr.test:17:8: error: CHECK: expected string not found in input
CHECK: INFO: libFuzzer: exiting as requested
       ^
<stdin>:7:49: note: scanning from here
INFO: signal received, trying to exit gracefully
                                                ^

Input file: <stdin>
Check file: /home/tcwg-buildbot/worker/clang-aarch64-sve-vla/llvm/compiler-rt/test/fuzzer/sigusr.test

-dump-input=help explains the following input dump.

Input was:
<<<<<<
          1: INFO: Running with entropic power schedule (0xFF, 100). 
          2: INFO: Seed: 2497399819 
          3: INFO: Loaded 1 modules (4 inline 8-bit counters): 4 [0xaaaaace3ce98, 0xaaaaace3ce9c),  
          4: INFO: Loaded 1 PC tables (4 PCs): 4 [0xaaaaace3cea0,0xaaaaace3cee0),  
          5: INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes 
          6: INFO: A corpus is not provided, starting from an empty corpus 
          7: INFO: signal received, trying to exit gracefully 
check:17                                                     X error: no match found
>>>>>>

--

...

@nikic
Copy link
Contributor

nikic commented Sep 12, 2024

The test is failing on s390x with this output:


******************** TEST 'lld :: COFF/arm64ec-import.test' FAILED ********************
Exit Code: 1

Command Output (stderr):
--
RUN: at line 2: split-file /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/tools/lld/test/COFF/Output/arm64ec-import.test.tmp.dir && cd /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/tools/lld/test/COFF/Output/arm64ec-import.test.tmp.dir
+ split-file /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/tools/lld/test/COFF/Output/arm64ec-import.test.tmp.dir
+ cd /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/tools/lld/test/COFF/Output/arm64ec-import.test.tmp.dir
RUN: at line 4: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-mc -filetype=obj -triple=arm64ec-windows test.s -o test.obj
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-mc -filetype=obj -triple=arm64ec-windows test.s -o test.obj
RUN: at line 5: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-mc -filetype=obj -triple=arm64ec-windows icall.s -o icall.obj
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-mc -filetype=obj -triple=arm64ec-windows icall.s -o icall.obj
RUN: at line 6: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-mc -filetype=obj -triple=arm64ec-windows hybmp.s -o hybmp.obj
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-mc -filetype=obj -triple=arm64ec-windows hybmp.s -o hybmp.obj
RUN: at line 7: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-mc -filetype=obj -triple=arm64ec-windows /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-mc -filetype=obj -triple=arm64ec-windows /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/Inputs/loadconfig-arm64ec.s -o loadconfig-arm64ec.obj
RUN: at line 8: llvm-lib -machine:arm64ec -def:test.def -out:test-arm64ec.lib
+ llvm-lib -machine:arm64ec -def:test.def -out:test-arm64ec.lib
RUN: at line 9: llvm-lib -machine:arm64ec -def:test2.def -out:test2-arm64ec.lib
+ llvm-lib -machine:arm64ec -def:test2.def -out:test2-arm64ec.lib
RUN: at line 10: llvm-lib -machine:x64 -def:test.def -out:test-x86_64.lib
+ llvm-lib -machine:x64 -def:test.def -out:test-x86_64.lib
RUN: at line 13: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/lld-link -machine:arm64ec -dll -noentry -out:out.dll loadconfig-arm64ec.obj icall.obj hybmp.obj           test.obj test-arm64ec.lib test2-arm64ec.lib
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/lld-link -machine:arm64ec -dll -noentry -out:out.dll loadconfig-arm64ec.obj icall.obj hybmp.obj test.obj test-arm64ec.lib test2-arm64ec.lib
RUN: at line 17: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/lld-link -machine:arm64ec -dll -noentry -out:out2.dll loadconfig-arm64ec.obj icall.obj hybmp.obj           test.obj test-x86_64.lib test2-arm64ec.lib
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/lld-link -machine:arm64ec -dll -noentry -out:out2.dll loadconfig-arm64ec.obj icall.obj hybmp.obj test.obj test-x86_64.lib test2-arm64ec.lib
RUN: at line 20: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-readobj --coff-imports out.dll | /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/FileCheck --check-prefix=IMPORTS /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-readobj --coff-imports out.dll
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/FileCheck --check-prefix=IMPORTS /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test
RUN: at line 21: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-readobj --coff-imports out2.dll | /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/FileCheck --check-prefix=IMPORTS /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-readobj --coff-imports out2.dll
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/FileCheck --check-prefix=IMPORTS /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test
RUN: at line 37: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-objdump -d out.dll | /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/FileCheck --check-prefix=DISASM /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/llvm-objdump -d out.dll
+ /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/llvm/redhat-linux-build/bin/FileCheck --check-prefix=DISASM /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test
/builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test:42:14: error: DISASM-NEXT: expected string not found in input
DISASM-NEXT: 180001008: d000000b adrp x11, 0x180003000
             ^
<stdin>:8:24: note: scanning from here
180001004: d65f03c0 ret
                       ^
<stdin>:9:1: note: possible intended match here
180001008: 4b000010 sub w16, w0, w0
^

Input file: <stdin>
Check file: /builddir/build/BUILD/llvm-project-8625eb0b87c86d3ef42a365d7593eed664b379e8/lld/test/COFF/arm64ec-import.test

-dump-input=help explains the following input dump.

Input was:
<<<<<<
           1:  
           2: out.dll: file format coff-arm64ec 
           3:  
           4: Disassembly of section .text: 
           5:  
           6: 0000000180001000 <.text>: 
           7: 180001000: 52800000 mov w0, #0x0 // =0 
           8: 180001004: d65f03c0 ret 
next:42'0                            X error: no match found
           9: 180001008: 4b000010 sub w16, w0, w0 
next:42'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
next:42'1     ?                                    possible intended match
          10: 18000100c: 6b0150f9 subs w25, w7, w1, lsl #20 
next:42'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          11: 180001010: 0a000010 and w16, w0, w0 
next:42'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          12: 180001014: 4a021091 eor w17, w4, w2, lsl #4 
next:42'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          13: 180001018: 03fffffe <unknown> 
next:42'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          14: 18000101c: 4b000010 sub w16, w0, w0 
next:42'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
           .
           .
           .
>>>>>>

--

I expect the issue is that you're assuming a little-endian host -- the data in importThunkARM64EC should probably be specified using bytes instead of int32_t?

@cjacek
Copy link
Contributor Author

cjacek commented Sep 12, 2024

Yes, good point, it should use bytes instead, sorry about that. I will push a fixup, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants