From 6f83e58c47ca4712b95104669a997f911c38ed46 Mon Sep 17 00:00:00 2001 From: _ov4 Date: Fri, 16 Oct 2026 14:11:20 +0000 Subject: fix(jit): register Windows x64 SEH function tables Keep JIT sections contiguous and use RuntimeDyld's image base for SEH registration. Unregister tables before freeing memory and stop execution when registration fails. Co-Authored-By: Claude Fable 5.1 --- AGENTS/ARCH.md | 5 +++ tools/driver/EmbeddedRunner.cc | 81 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/AGENTS/ARCH.md b/AGENTS/ARCH.md index 58338f3..7a48303 100644 --- a/AGENTS/ARCH.md +++ b/AGENTS/ARCH.md @@ -118,6 +118,11 @@ Driver behavior (`tools/driver/main.cc`): On macOS the JIT explicitly emits DWARF unwind frames even when compact unwind records would suffice for native linking; MCJIT does not register compact unwind records. macOS execution still needs platform verification. + Windows x86_64 reserves contiguous JIT memory and registers relocated COFF + `.pdata` tables with `RtlAddFunctionTable`, using RuntimeDyld's lowest-section + image base. Tables are unregistered before their memory is released, and + registration failures stop execution. Windows execution still needs platform + verification. The host target is initialized for native output or embedded execution. No external runner or PATH lookup is used; `-r` alone runs in memory. - In NONE mode, `-r` returns 1 before compiling or writing output; native and IR diff --git a/tools/driver/EmbeddedRunner.cc b/tools/driver/EmbeddedRunner.cc index 05890ac..09b33b2 100644 --- a/tools/driver/EmbeddedRunner.cc +++ b/tools/driver/EmbeddedRunner.cc @@ -14,6 +14,73 @@ #include #include #include +#if defined(_WIN32) && defined(__x86_64__) +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#include + +namespace { + +// RuntimeDyld relocates COFF unwind entries relative to the lowest loaded +// section. Keep sections together and give Windows that same image base. +class WindowsJITMemoryManager final : public llvm::SectionMemoryManager { + uintptr_t ImageBase = std::numeric_limits::max(); + std::vector FunctionTables; + std::string Error; + + uint8_t *recordSection(uint8_t *Address, uintptr_t Size) { + auto Base = reinterpret_cast(Address); + if (Address && Size && Base < ImageBase) ImageBase = Base; + return Address; + } + +public: + WindowsJITMemoryManager() : SectionMemoryManager(nullptr, true) {} + ~WindowsJITMemoryManager() override { deregisterEHFrames(); } + + uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, llvm::StringRef Name) override { + return recordSection(SectionMemoryManager::allocateCodeSection( + Size, Alignment, SectionID, Name), Size); + } + + uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, llvm::StringRef Name, + bool IsReadOnly) override { + return recordSection(SectionMemoryManager::allocateDataSection( + Size, Alignment, SectionID, Name, IsReadOnly), Size); + } + + void registerEHFrames(uint8_t *Address, uint64_t, size_t Size) override { + if (!Size || !Error.empty()) return; + if (Size % sizeof(RUNTIME_FUNCTION) || + Size / sizeof(RUNTIME_FUNCTION) > std::numeric_limits::max() || + ImageBase == std::numeric_limits::max()) { + Error = "invalid embedded SEH function table"; + return; + } + auto *Table = reinterpret_cast(Address); + if (!RtlAddFunctionTable(Table, static_cast(Size / sizeof(RUNTIME_FUNCTION)), + static_cast(ImageBase))) { + Error = "cannot register embedded SEH function table"; + return; + } + FunctionTables.push_back(Table); + } + + void deregisterEHFrames() override { + for (auto *Table : FunctionTables) RtlDeleteFunctionTable(Table); + FunctionTables.clear(); + } + + const std::string &getError() const { return Error; } +}; + +} // namespace +#endif // Static runtime symbols are not available through dlsym. Register their // actual addresses so the JIT and its unwind tables use the host's runtime. @@ -73,13 +140,27 @@ int runEmbedded(const cps::CodeGen &CG) { Options.MCOptions.EmitDwarfUnwind = llvm::EmitDwarfUnwindType::Always; Builder.setTargetOptions(Options); #endif +#if defined(_WIN32) && defined(__x86_64__) + auto MemoryManager = std::make_unique(); + auto *WindowsMemory = MemoryManager.get(); + Builder.setMCJITMemoryManager(std::move(MemoryManager)); +#else Builder.setMCJITMemoryManager(std::make_unique()); +#endif std::unique_ptr Engine(Builder.create()); if (!Engine) { llvm::errs() << "error: cannot create the embedded LLVM engine: " << Error << "\n"; return 1; } Engine->finalizeObject(); +#if defined(_WIN32) && defined(__x86_64__) + // MCJIT does not propagate memory-manager registration failures. + if (!WindowsMemory->getError().empty()) { + llvm::errs() << "error: embedded LLVM execution failed: " + << WindowsMemory->getError() << "\n"; + return 1; + } +#endif if (Engine->hasError()) { llvm::errs() << "error: embedded LLVM execution failed: " << Engine->getErrorMessage() << "\n"; return 1; -- cgit v1.3.1-4-g156e