Skip to content

Supporting Windows SEH without the ORC runtime #163503

Open

Description

@lhames

It'd be nice to get Windows SEH working without requiring the ORC runtime.

This would bring Windows into line with Darwin and Linux, both of which support exceptions in-process without the runtime.

RtlAddFunctionTable (https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtladdfunctiontable) and RtlDeleteFunctionTable seem to be the registration APIs.

RtlAddFunctionTable takes a base address and a pointer to a table where function addresses are expressed as 32-bit offsets from the base:

NTSYSAPI BOOLEAN RtlAddFunctionTable(
  [in] PRUNTIME_FUNCTION FunctionTable,
  [in] DWORD             EntryCount,
  [in] DWORD64           BaseAddress
);

I do not know whether the SEH runtime looks at any metadata at the base address, or whether it's used purely for address calculations, but I suspect the latter (or the various out-of-tree SEH experiments that people have gotten working would have blown up differently).

Assuming that BaseAddress is only used for address calculation then we can probably just find the lowest address in the graph and use that for each graph.

If BaseAddress needs to contain useful metadata (e.g. a COFF header) then we can do something similar to what we do for compact-unwind (see

Error getOrCreateCompactUnwindBase(LinkGraph &G) {
)

Activity

  1. llvmbot commented on Oct 15, 2025

    @llvmbot
    Member

    @llvm/issue-subscribers-orcjit

    Author: Lang Hames (lhames)

    Details It'd be nice to get Windows SEH working without requiring the ORC runtime.

    This would bring Windows into line with Darwin and Linux, both of which support exceptions in-process without the runtime.

    RtlAddFunctionTable (https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-rtladdfunctiontable) and RtlDeleteFunctionTable seem to be the registration APIs.

    RtlAddFunctionTable takes a base address and a pointer to a table where function addresses are expressed as 32-bit offsets from the base:

    NTSYSAPI BOOLEAN RtlAddFunctionTable(
      [in] PRUNTIME_FUNCTION FunctionTable,
      [in] DWORD             EntryCount,
      [in] DWORD64           BaseAddress
    );
    

    I do not know whether the SEH runtime looks at any metadata at the base address, or whether it's used purely for address calculations, but I suspect the latter (or the various out-of-tree SEH experiments that people have gotten working would have blown up differently).

    Assuming that BaseAddress is only used for address calculation then we can probably just find the lowest address in the graph and use that for each grab.

    If BaseAddress needs to contain useful metadata (e.g. a COFF header) then we can do something similar to what we do for compact-unwind (see

    Error getOrCreateCompactUnwindBase(LinkGraph &G) {
    )

  2. jeaye commented on Oct 15, 2025

    @jeaye
    Contributor

    Thanks, Lang! @ikappaki for vis.

  3. ikappaki commented on Oct 16, 2025

    @ikappaki

    Thanks both! Linking initial issue with steps to reproduce: #126365

  4. lhames commented on Oct 17, 2025

    @lhames
    ContributorAuthor

    #158270 also probably involves constructing a valid __ImageBase.

  5. lhames commented on Apr 30, 2026

    @lhames
    ContributorAuthor

    I've posted a sketch of what simple in-process SEH registration might look like in lhames@fe70221.

    You can ignore the code in the #else branch with the // for testing purposes only comment: that was just so that I could build this at all on Darwin. On Windows I'd expect those types to come from winnt.h.

    The plugin logic is simple: registerFrameInfo is registered as a post-fixup pass in modifyPassConfig, which means that registerFrameInfo will run at the end of the linker pipeline after all addresses have been assigned and content written to the linker's working memory. The registerFrameInfo pass looks for a .pdata section and, if one exists, then it (a) finds the effective base address of the image (from __ImageBase if defined, otherwise taking the lowest address in the object file), then (b) adds allocation actions to call RtlAddFunctionTable (when the JIT'd memory is finalised) and RtlDeleteFunctionTable (when the JIT'd memory is deallocated).

    The allocation actions are implemented as static methods (registerPData and deregisterPData) in the plugin class itself, so this will only work in-process.

  6. ikappaki commented on May 3, 2026

    @ikappaki

    Hi @lhames, thanks for the sketch and guidance, I’m taking a look . The code compiles fine (the correct header is <windows.h>) and I’m making good initial progress.

  7. ikappaki commented on May 21, 2026

    @ikappaki

    Hi @lhames, there were several issues that needed to be addressed to fully support SEH: COMDAT section symbols, GOT/PLT handling, ADDR32NB stubs, filtering DLLImportDefinitionGenerators, and keep alives for COMDAT sections.

    I’ve opened a PR at lhames#5 for you to get a preliminary look. I’m happy to answer any questions or explain the fixes in more detail. I’ll flesh out the PR description in the next day or so, just wanted to give you a quick heads up first.

    I’ve also bundled a set of tests, with one covering each issue addressed. The good news is that all exception tests are now passing in jank, which is a pretty significant milestone.

    I did have to backport the changes to LLVM 22, though, because LLVM 23 currently can't compile jank. It gets stuck compiling a plain C++ file, unrelated to the JIT itself.

    Thanks

  8. ikappaki commented on May 22, 2026

    @ikappaki

    Hi @lhames, I've updated the PR with explanations for each of the fixes. When you have a chance, could you please take a look and suggest how we might best move it forward? thanks

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions