[PR #1531] [MERGED] fix(windows): load native controls in Node-API hosts not named node.exe #2948

Closed
opened 2026-10-17 21:12:08 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1531
Author: @xeropresence
Created: 10/10/2026
Status: ✅ Merged
Merged: 10/11/2026
Merged by: @morluto

Base: main ← Head: bun-native-delay-load


📝 Commits (1)

  • 884e400 fix(windows): resolve Node-API imports from the host executable

📊 Changes

2 files changed (+15 additions, -2 deletions)

View changed files

📝 native/windows/src/addon.cc (+12 -0)
📝 scripts/build-windows-native.mjs (+3 -2)

📄 Description

Summary

The bundled Windows addon (rea-windows-x64.node) imports its Node-API functions from a module named exactly node.exe. When REA runs in any other Node-API host, Windows can't resolve that import and the addon fails to
load. I hit this with Bun 1.4.2 on Windows x64. REA then reports Windows native controls as unavailable. That breaks private runtime roots (including the headless IDA provider's private workspace), owned processes, and
the JADX runtime check.

This PR delay-loads the Node-API imports and points node.exe at the running executable. node-gyp addons use the same approach (win_delay_load_hook.cc).

I didn't open an issue first. The reproduction is below.

Problem and expected behavior

Trigger: run REA on Windows in a host whose executable isn't node.exe. For example, a Bun global install runs rea.mjs with bun.exe when no node is on PATH.

  • rea doctor --provider ida → ida-private_workspace: REA Windows native controls are unavailable: LoadLibrary failed: The specified module could not be found.
  • MCP open_binary with a headless IDA registration → provider_unavailable.

Expected: the addon loads in whatever process loads it, as long as that process exports the required Node-API functions. bun.exe exports every napi_* function the addon imports.

Cause: scripts/build-windows-native.mjs writes LIBRARY node.exe into the .def file and builds a normal import library with dlltool -l. That puts node.exe in the addon's PE import directory. This build has no
delay-load hook, which node-gyp builds use to avoid the problem.

Change and scope

  • scripts/build-windows-native.mjs: dlltool -l → -y (--output-delaylib), which builds a delay-import library for the same symbol list.
  • native/windows/src/addon.cc: sets __pfnDliNotifyHook2. When the dliNotePreLoadLibrary notification is for node.exe, the hook returns GetModuleHandleW(nullptr) (the host executable). For everything else it
    returns nullptr, so the default MinGW delay-load helper handles it.

No changes to TypeScript, the manifest schema, the ABI version, or the Node-API version. This PR doesn't claim Bun as a supported runtime and doesn't change engines.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Windows native addon build (build:windows-native) and addon import resolution.
  • CLI and MCP/tool-catalog contract: none.
  • Provider, bridge, target-format, or platform compatibility: Windows x64 only. Under Node.js, node.exe resolves to the same module as before. Linux and macOS never load this artifact.
  • Evidence, artifact, provenance, or reconstruction contract: none.
  • Process execution, authorization, cleanup, or containment impact: native operations are unchanged. When the host has a different name, the loader no longer searches the DLL search path for a node.exe.
  • Generated metadata, package, or installation impact: rea-windows-x64.node and the artifactSha256 in manifest.json change. Nothing else in the package changes.

Evidence and regression coverage

  • Tests added or updated: none.
  • Base reproduction: REA 6.3.0 with the stock artifact (fedfd4ce…), Bun 1.4.2, Windows x64. The doctor and open_binary failures are quoted above.
  • PE import directory: the stock artifact imports node.exe, ADVAPI32.dll, bcrypt.dll, KERNEL32.dll, msvcrt.dll and ntdll.dll. The patched artifact imports only the five system DLLs; its Node-API calls go
    through __delayLoadHelper2.
  • Remaining proof gaps: there's no automated regression test. A Windows lane step could load the artifact from a copy of node.exe saved under a different file name and call inspect. That would fail on the base
    revision and pass here. Happy to add it. Bun on Windows is the only non-Node host I tested.

Not evidence-bearing; the evidence checklist doesn't apply.

Validation performed

Fork CI run: https://github.com/xeropresence/rea/actions/runs/38042302533. It tested the PR merged into main at 46c9cba0, one commit behind the current main. That newer commit doesn't touch the native code or its
build scripts, and the branch merges cleanly.

  • Build bundled Windows x64 native controls (npm run build:windows-native, npm run verify:windows-native:artifact) — passed
  • Windows capability and package lane (npm run verify:windows-native under Node on windows-latest) — passed
  • Static checks, Build package, Native package baseline (linux x64, linux arm64, darwin arm64), test shards 1, 2 and 4 — passed
  • Test shard 3/4 — failed on a timing check in code this PR doesn't touch: javascriptSemanticAnalysis.member_mutations.test.ts:645, expected 2573.855838999996 to be less than 2000. Test (coverage + timing) also
    failed; I didn't investigate it separately.

Local test on Windows x64: REA 6.3.0 with the CI-built artifact installed, run with bun.exe 1.4.2:

  • rea doctor --provider ida — healthy: Private workspace authority is available (native-authority).
  • MCP open_binary (provider ida, headless idalib-mcp from current ida-pro-mcp main) → analyze_function wmain → close_binary on a copy of C:\Windows\System32\whoami.exe — all succeeded, and the private
    workspace was removed.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: real headless IDA on Windows x64 under Bun. The Node path is covered by the Windows capability lane. I didn't run verify:ghidra:windows.
  • Package or release metadata impact: only the artifact digest changes.
  • Security, privacy, process, or containment review: the hook only handles node.exe, and it returns the host module that is already loaded; it never loads a file from disk. One behavior change: if a host is missing a
    Node-API function, the error now happens the first time that function is called, as a delay-load exception, not when LoadLibrary loads the addon. Node.js exports every function the addon uses, so Node behavior is
    unchanged. If failing at load time matters, module initialization could force all delay imports to resolve up front.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1531 **Author:** [@xeropresence](https://github.com/xeropresence) **Created:** 10/10/2026 **Status:** ✅ Merged **Merged:** 10/11/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `bun-native-delay-load` --- ### 📝 Commits (1) - [`884e400`](https://github.com/morluto/rea/commit/884e400017b1ce81667f29f43bde7c64189d4230) fix(windows): resolve Node-API imports from the host executable ### 📊 Changes **2 files changed** (+15 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `native/windows/src/addon.cc` (+12 -0) 📝 `scripts/build-windows-native.mjs` (+3 -2) </details> ### 📄 Description ## Summary The bundled Windows addon (`rea-windows-x64.node`) imports its Node-API functions from a module named exactly `node.exe`. When REA runs in any other Node-API host, Windows can't resolve that import and the addon fails to load. I hit this with Bun 1.4.2 on Windows x64. REA then reports Windows native controls as unavailable. That breaks private runtime roots (including the headless IDA provider's private workspace), owned processes, and the JADX runtime check. This PR delay-loads the Node-API imports and points `node.exe` at the running executable. node-gyp addons use the same approach (`win_delay_load_hook.cc`). I didn't open an issue first. The reproduction is below. ## Problem and expected behavior Trigger: run REA on Windows in a host whose executable isn't `node.exe`. For example, a Bun global install runs `rea.mjs` with `bun.exe` when no `node` is on PATH. - `rea doctor --provider ida` → `ida-private_workspace`: `REA Windows native controls are unavailable: LoadLibrary failed: The specified module could not be found.` - MCP `open_binary` with a headless IDA registration → `provider_unavailable`. Expected: the addon loads in whatever process loads it, as long as that process exports the required Node-API functions. `bun.exe` exports every `napi_*` function the addon imports. Cause: `scripts/build-windows-native.mjs` writes `LIBRARY node.exe` into the `.def` file and builds a normal import library with `dlltool -l`. That puts `node.exe` in the addon's PE import directory. This build has no delay-load hook, which node-gyp builds use to avoid the problem. ## Change and scope - `scripts/build-windows-native.mjs`: `dlltool -l` → `-y` (`--output-delaylib`), which builds a delay-import library for the same symbol list. - `native/windows/src/addon.cc`: sets `__pfnDliNotifyHook2`. When the `dliNotePreLoadLibrary` notification is for `node.exe`, the hook returns `GetModuleHandleW(nullptr)` (the host executable). For everything else it returns `nullptr`, so the default MinGW delay-load helper handles it. No changes to TypeScript, the manifest schema, the ABI version, or the Node-API version. This PR doesn't claim Bun as a supported runtime and doesn't change `engines`. ## Contract and boundary impact - Semantic owner and earliest changed stage: Windows native addon build (`build:windows-native`) and addon import resolution. - CLI and MCP/tool-catalog contract: none. - Provider, bridge, target-format, or platform compatibility: Windows x64 only. Under Node.js, `node.exe` resolves to the same module as before. Linux and macOS never load this artifact. - Evidence, artifact, provenance, or reconstruction contract: none. - Process execution, authorization, cleanup, or containment impact: native operations are unchanged. When the host has a different name, the loader no longer searches the DLL search path for a `node.exe`. - Generated metadata, package, or installation impact: `rea-windows-x64.node` and the `artifactSha256` in `manifest.json` change. Nothing else in the package changes. ## Evidence and regression coverage - Tests added or updated: none. - Base reproduction: REA 6.3.0 with the stock artifact (`fedfd4ce…`), Bun 1.4.2, Windows x64. The doctor and `open_binary` failures are quoted above. - PE import directory: the stock artifact imports `node.exe`, `ADVAPI32.dll`, `bcrypt.dll`, `KERNEL32.dll`, `msvcrt.dll` and `ntdll.dll`. The patched artifact imports only the five system DLLs; its Node-API calls go through `__delayLoadHelper2`. - Remaining proof gaps: there's no automated regression test. A Windows lane step could load the artifact from a copy of `node.exe` saved under a different file name and call `inspect`. That would fail on the base revision and pass here. Happy to add it. Bun on Windows is the only non-Node host I tested. Not evidence-bearing; the evidence checklist doesn't apply. ## Validation performed Fork CI run: https://github.com/xeropresence/rea/actions/runs/38042302533. It tested the PR merged into `main` at `46c9cba0`, one commit behind the current `main`. That newer commit doesn't touch the native code or its build scripts, and the branch merges cleanly. - `Build bundled Windows x64 native controls` (`npm run build:windows-native`, `npm run verify:windows-native:artifact`) — passed - `Windows capability and package lane` (`npm run verify:windows-native` under Node on `windows-latest`) — passed - `Static checks`, `Build package`, `Native package baseline` (linux x64, linux arm64, darwin arm64), test shards 1, 2 and 4 — passed - Test shard 3/4 — failed on a timing check in code this PR doesn't touch: `javascriptSemanticAnalysis.member_mutations.test.ts:645`, `expected 2573.855838999996 to be less than 2000`. `Test (coverage + timing)` also failed; I didn't investigate it separately. Local test on Windows x64: REA 6.3.0 with the CI-built artifact installed, run with `bun.exe` 1.4.2: - `rea doctor --provider ida` — healthy: `Private workspace authority is available (native-authority).` - MCP `open_binary` (provider `ida`, headless `idalib-mcp` from current ida-pro-mcp `main`) → `analyze_function wmain` → `close_binary` on a copy of `C:\Windows\System32\whoami.exe` — all succeeded, and the private workspace was removed. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: real headless IDA on Windows x64 under Bun. The Node path is covered by the Windows capability lane. I didn't run `verify:ghidra:windows`. - Package or release metadata impact: only the artifact digest changes. - Security, privacy, process, or containment review: the hook only handles `node.exe`, and it returns the host module that is already loaded; it never loads a file from disk. One behavior change: if a host is missing a Node-API function, the error now happens the first time that function is called, as a delay-load exception, not when `LoadLibrary` loads the addon. Node.js exports every function the addon uses, so Node behavior is unchanged. If failing at load time matters, module initialization could force all delay imports to resolve up front. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:12:08 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#2948
No description provided.