diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-16 14:11:20 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-16 14:11:20 +0000 |
| commit | 6f83e58c47ca4712b95104669a997f911c38ed46 (patch) | |
| tree | b7494af52f25dc203f91b79209c25b640dae17bc | |
| parent | 3001dfeb97adebf0cb326d9aa664098e0aced5b5 (diff) | |
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 <noreply@anthropic.com>
| -rw-r--r-- | AGENTS/ARCH.md | 5 | ||||
| -rw-r--r-- | tools/driver/EmbeddedRunner.cc | 81 |
2 files changed, 86 insertions, 0 deletions
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 <cxxabi.h> #include <typeinfo> #include <unwind.h> +#if defined(_WIN32) && defined(__x86_64__) +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include <windows.h> +#include <limits> +#include <vector> + +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<uintptr_t>::max(); + std::vector<PRUNTIME_FUNCTION> FunctionTables; + std::string Error; + + uint8_t *recordSection(uint8_t *Address, uintptr_t Size) { + auto Base = reinterpret_cast<uintptr_t>(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<DWORD>::max() || + ImageBase == std::numeric_limits<uintptr_t>::max()) { + Error = "invalid embedded SEH function table"; + return; + } + auto *Table = reinterpret_cast<PRUNTIME_FUNCTION>(Address); + if (!RtlAddFunctionTable(Table, static_cast<DWORD>(Size / sizeof(RUNTIME_FUNCTION)), + static_cast<DWORD64>(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<WindowsJITMemoryManager>(); + auto *WindowsMemory = MemoryManager.get(); + Builder.setMCJITMemoryManager(std::move(MemoryManager)); +#else Builder.setMCJITMemoryManager(std::make_unique<llvm::SectionMemoryManager>()); +#endif std::unique_ptr<llvm::ExecutionEngine> 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; |
