[GH-ISSUE #7121] [Bug] Windows Ghidra lane refuses DOS MZ (16-bit real mode) targets and cannot open an existing Ghidra project #4279

Open
opened 2026-10-31 00:48:27 +00:00 by bsakaguchi · 10 comments
Owner

Originally created by @pietrondo on GitHub (Oct 25, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/7121

Observed behavior

open_binary detects the target as a DOS executable, but no provider can analyse it on Windows, and every subsequent operation fails with provider_unavailable. The Ghidra lane refuses the format because the Windows admission boundary (inspectWindowsP0TargetSupport in dist/ghidra/GhidraProvider.js) only admits native PE and explicitly selected raw 6502 images.

Observed MCP result for open_binary:

{"path": "C:\\Users\\pietr\\Downloads\\simsearth\\simearth.exe",
 "kind": "executable", "format": "dos-mz", "architecture": "x86"}

and the next call (search_strings / binary_overview / any read) fails with:

{"error": {"code": "provider_unavailable", "retryable": true,
  "message": "The selected analysis provider is unavailable. ...",
  "details": {"requested_provider_id": "auto",
   "candidate_ids": ["ghidra", "hopper", "ida", "kuna"],
   "rejections": [
    {"provider_id": "ghidra", "code": "target_format_unsupported",
     "reason": "Windows Ghidra P0 accepts native PE and explicitly selected raw 6502 targets only.",
     "diagnostics": {"host_platform": "win32", "target_kind": "executable",
                     "target_format": "dos-mz", "architecture": "x86"}},
    {"provider_id": "hopper", "code": "unsupported_host",
     "reason": "Hopper integration is not supported on win32."},
    {"provider_id": "ida", "code": "not_configured",
     "reason": "Set REA_IDA_MCP_CONFIG to an upstream IDA MCP JSON registration; ..."},
    {"provider_id": "kuna", "code": "not_configured",
     "reason": "Set REA_KUNA_COMMAND to the absolute path of a caller-supplied kuna executable ..."}]}}}

The dos-com format documented on open_binary is refused by the same check on Windows.

Reproduction

  1. open_binary with path = a 16-bit DOS MZ executable (DOS MZ, x86 real mode).
  2. Observe the dos-mz / x86 detection.
  3. Call any provider operation (binary_overview, search_strings, list_procedures, procedure_pseudo_code, ...).
  4. Observe provider_unavailable with the four provider rejections quoted above. Reproduced on every attempt (5/5).

Variants that were also run:

call result
open_binary(path=<MZ exe>) detected as dos-mz; every later operation fails as above
open_binary(path=<MZ exe>, format="raw-binary", architecture="x86", entry_point="0xAE00") accepted by open_binary, then binary_overview fails with the same P0 refusal for target_format: "raw-binary"
open_binary(path="...\\SimEarth.gpr") (an existing Ghidra project) target_unavailable - "Cannot open artifact: truncated or unsupported binary header."
open_binary(path="...\\SimEarth.rep") (the project directory) "open_binary accepts files and macOS app bundles. This target is a directory; ..."
rea doctor ghidra: healthy (12.1.2, win32 x64), ghidra-platform: healthy, ghidra-architecture: healthy - the installation is fine, the format is not admitted

Expected behavior

The DOS lanes that REA supports elsewhere (dos-mz, dos-com) are reachable on Windows, as the GameCube DOL comment in the same source file implies they are on POSIX ("DOL is verified on POSIX hosts only; Windows P0 does not admit it, like the other non-PE lanes"). Ghidra itself supports the target language (x86:LE:16:Real Mode), so the admission boundary - not the engine - is what refuses it.

A second, independent path would be to accept an existing Ghidra project as an analysis-database target, so an analysis that was already computed with analyzeHeadless can be reused instead of recomputed.

This expectation is inferred from the provider's own POSIX behaviour and from #3767 (which analyses a DOS MZ image); it was not verified against a known-good Windows build.

Impact and scope

A 16-bit DOS executable cannot be analysed through REA on Windows at all - there is no provider to fall back to (Hopper is macOS-only, IDA and Kuna are not configured here). The workaround is a separate Ghidra headless installation plus a project file, and REA cannot read that result either, so every observation has to leave the MCP tool. Frequency: every attempt, 5/5.

REA version

6.8.0 (rea --version, npm package rea-agents)

Runtime and platform

Windows, win32 x64; Node 24.14.0; Ghidra 12.1.2 at C:\Users\pietr\Downloads\ghidra_12.1.2_PUBLIC; no Hopper, no IDA, no Kuna on this host.

Target and artifact context

A 16-bit DOS MZ executable, x86 real mode: simearth.exe (Maxis SimEarth, 1990). The image is loaded at PSP+0x10 with a 0x5200-byte header, so the code lives at file offset 0x5200 and the analysis of interest starts there. Its analysis already exists in a Ghidra project (analyzeHeadless, language x86:LE:16:Real Mode).

Interface

MCP

Earliest failure stage

Setup or provider admission

Relevant output or evidence

Command or MCP request actually run:

open_binary(path="C:\\Users\\pietr\\Downloads\\simsearth\\simearth.exe")
search_strings(pattern="Save", mode="literal")
open_binary(path="...\\SimEarth.gpr")
rea doctor

Relevant result, error, or artifact: the JSON blocks quoted in "Observed behavior" above, and the source-derived admission rule:

// dist/ghidra/GhidraProvider.js
const inspectWindowsP0TargetSupport = (target, diagnostics) => {
    if (target.format === "raw-binary" && target.architecture === "6502")
        return { status: "supported", ... };
    if (target.format === "gamecube-dol")
        return { status: "unsupported", ...,
                 reason: "GameCube DOL analysis is not part of Windows Ghidra P0; it is verified on POSIX hosts only." };
    if (target.format !== "pe")
        return { status: "unsupported", code: "target_format_unsupported",
                 reason: "Windows Ghidra P0 accepts native PE and explicitly selected raw 6502 targets only." };

Source-only findings are labelled as such: the code path above is from the installed 6.8.0 package, not from a repository revision.

Missing evidence or remaining uncertainty: I did not test a POSIX host, so I cannot confirm from observation that the same dos-mz target is admitted there; the DOS MZ support referenced in #3767 was not re-verified.

Typed outcome or recovery expected

Either open_binary accepts dos-mz/dos-com on Windows with a caller-supplied language and entry point (the shape REA_GHIDRA_LANGUAGE_ID and format="raw-binary" already have), or the failure names the supported Windows formats as a typed target_format_unsupported at open_binary time instead of at the first provider operation - the target is detected successfully, so the refusal arrives one step later than the user expects.

Analysis or suspected cause

The refusal is by design in the Windows P0 boundary, not a broken installation: rea doctor reports Ghidra healthy, and the same function that rejects DOS MZ also rejects DOL with the comment that non-PE lanes are "verified on POSIX hosts only". The DOS MZ lane is therefore reachable through Ghidra on POSIX, and unreachable on Windows.

Additional context

This blocks an active reconstruction task: the target is a 1990 DOS game whose per-turn simulation has to be validated against the original binary, and every code observation currently has to be made outside REA with a hand-driven Ghidra headless project.

Originally created by @pietrondo on GitHub (Oct 25, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7121 ### Observed behavior `open_binary` detects the target as a DOS executable, but **no provider can analyse it on Windows**, and every subsequent operation fails with `provider_unavailable`. The Ghidra lane refuses the format because the Windows admission boundary (`inspectWindowsP0TargetSupport` in `dist/ghidra/GhidraProvider.js`) only admits native PE and explicitly selected raw 6502 images. Observed MCP result for `open_binary`: ```json {"path": "C:\\Users\\pietr\\Downloads\\simsearth\\simearth.exe", "kind": "executable", "format": "dos-mz", "architecture": "x86"} ``` and the next call (`search_strings` / `binary_overview` / any read) fails with: ```json {"error": {"code": "provider_unavailable", "retryable": true, "message": "The selected analysis provider is unavailable. ...", "details": {"requested_provider_id": "auto", "candidate_ids": ["ghidra", "hopper", "ida", "kuna"], "rejections": [ {"provider_id": "ghidra", "code": "target_format_unsupported", "reason": "Windows Ghidra P0 accepts native PE and explicitly selected raw 6502 targets only.", "diagnostics": {"host_platform": "win32", "target_kind": "executable", "target_format": "dos-mz", "architecture": "x86"}}, {"provider_id": "hopper", "code": "unsupported_host", "reason": "Hopper integration is not supported on win32."}, {"provider_id": "ida", "code": "not_configured", "reason": "Set REA_IDA_MCP_CONFIG to an upstream IDA MCP JSON registration; ..."}, {"provider_id": "kuna", "code": "not_configured", "reason": "Set REA_KUNA_COMMAND to the absolute path of a caller-supplied kuna executable ..."}]}}} ``` The `dos-com` format documented on `open_binary` is refused by the same check on Windows. ### Reproduction 1. `open_binary` with `path` = a 16-bit DOS MZ executable (DOS MZ, x86 real mode). 2. Observe the `dos-mz` / `x86` detection. 3. Call any provider operation (`binary_overview`, `search_strings`, `list_procedures`, `procedure_pseudo_code`, ...). 4. Observe `provider_unavailable` with the four provider rejections quoted above. Reproduced on every attempt (5/5). Variants that were also run: | call | result | |---|---| | `open_binary(path=<MZ exe>)` | detected as `dos-mz`; every later operation fails as above | | `open_binary(path=<MZ exe>, format="raw-binary", architecture="x86", entry_point="0xAE00")` | accepted by `open_binary`, then `binary_overview` fails with the same P0 refusal for `target_format: "raw-binary"` | | `open_binary(path="...\\SimEarth.gpr")` (an existing Ghidra project) | `target_unavailable` - "Cannot open artifact: truncated or unsupported binary header." | | `open_binary(path="...\\SimEarth.rep")` (the project directory) | "open_binary accepts files and macOS app bundles. This target is a directory; ..." | | `rea doctor` | `ghidra: healthy` (12.1.2, win32 x64), `ghidra-platform: healthy`, `ghidra-architecture: healthy` - the installation is fine, the format is not admitted | ### Expected behavior The DOS lanes that REA supports elsewhere (`dos-mz`, `dos-com`) are reachable on Windows, as the GameCube DOL comment in the same source file implies they are on POSIX ("DOL is verified on POSIX hosts only; Windows P0 does not admit it, like the other non-PE lanes"). Ghidra itself supports the target language (`x86:LE:16:Real Mode`), so the admission boundary - not the engine - is what refuses it. A second, independent path would be to accept an **existing Ghidra project** as an `analysis-database` target, so an analysis that was already computed with `analyzeHeadless` can be reused instead of recomputed. This expectation is inferred from the provider's own POSIX behaviour and from #3767 (which analyses a DOS MZ image); it was not verified against a known-good Windows build. ### Impact and scope A 16-bit DOS executable cannot be analysed through REA on Windows at all - there is no provider to fall back to (Hopper is macOS-only, IDA and Kuna are not configured here). The workaround is a separate Ghidra headless installation plus a project file, and REA cannot read that result either, so every observation has to leave the MCP tool. Frequency: every attempt, 5/5. ### REA version 6.8.0 (`rea --version`, npm package `rea-agents`) ### Runtime and platform Windows, win32 x64; Node 24.14.0; Ghidra 12.1.2 at `C:\Users\pietr\Downloads\ghidra_12.1.2_PUBLIC`; no Hopper, no IDA, no Kuna on this host. ### Target and artifact context A 16-bit DOS MZ executable, x86 real mode: `simearth.exe` (Maxis SimEarth, 1990). The image is loaded at `PSP+0x10` with a `0x5200`-byte header, so the code lives at file offset `0x5200` and the analysis of interest starts there. Its analysis already exists in a Ghidra project (`analyzeHeadless`, language `x86:LE:16:Real Mode`). ### Interface MCP ### Earliest failure stage Setup or provider admission ### Relevant output or evidence Command or MCP request actually run: ```text open_binary(path="C:\\Users\\pietr\\Downloads\\simsearth\\simearth.exe") search_strings(pattern="Save", mode="literal") open_binary(path="...\\SimEarth.gpr") rea doctor ``` Relevant result, error, or artifact: the JSON blocks quoted in "Observed behavior" above, and the source-derived admission rule: ```js // dist/ghidra/GhidraProvider.js const inspectWindowsP0TargetSupport = (target, diagnostics) => { if (target.format === "raw-binary" && target.architecture === "6502") return { status: "supported", ... }; if (target.format === "gamecube-dol") return { status: "unsupported", ..., reason: "GameCube DOL analysis is not part of Windows Ghidra P0; it is verified on POSIX hosts only." }; if (target.format !== "pe") return { status: "unsupported", code: "target_format_unsupported", reason: "Windows Ghidra P0 accepts native PE and explicitly selected raw 6502 targets only." }; ``` Source-only findings are labelled as such: the code path above is from the installed 6.8.0 package, not from a repository revision. Missing evidence or remaining uncertainty: I did not test a POSIX host, so I cannot confirm from observation that the same `dos-mz` target is admitted there; the DOS MZ support referenced in #3767 was not re-verified. ### Typed outcome or recovery expected Either `open_binary` accepts `dos-mz`/`dos-com` on Windows with a caller-supplied language and entry point (the shape `REA_GHIDRA_LANGUAGE_ID` and `format="raw-binary"` already have), or the failure names the supported Windows formats as a typed `target_format_unsupported` at `open_binary` time instead of at the first provider operation - the target is detected successfully, so the refusal arrives one step later than the user expects. ### Analysis or suspected cause The refusal is by design in the Windows P0 boundary, not a broken installation: `rea doctor` reports Ghidra healthy, and the same function that rejects DOS MZ also rejects DOL with the comment that non-PE lanes are "verified on POSIX hosts only". The DOS MZ lane is therefore reachable through Ghidra on POSIX, and unreachable on Windows. ### Additional context This blocks an active reconstruction task: the target is a 1990 DOS game whose per-turn simulation has to be validated against the original binary, and every code observation currently has to be made outside REA with a hand-driven Ghidra headless project.
Author
Owner

@XinwangTao commented on GitHub (Oct 25, 2026):

这个 bug 确认是真实的,而且是文档与 admission 边界不一致的典型:open_binary 能识别并对外宣称支持 dos-mz / x86,但 Windows Ghidra lane 的 P0 边界只放行 native PE 和显式 6502,等于"前端承诺、后端拒绝",用户拿到的 provider_unavailable 也没有给出可操作的补救路径。你列出的三种变体(原始 MZ、raw-binary 强改、已存在 .gpr 项目)都失败,说明问题不在单点格式,而是 admission 判定维度太窄。建议从两个方向考虑:(1) 把 dos-mz / raw-binary 也纳入 Windows Ghidra P0 的接受集合,或用 target_format 之外的入口点/架构参数做更精确的放行,Ghidra 本身完全能加载 16-bit MZ;(2) 若短期不做,至少让拒绝信息返回可操作的 fallback 提示(如建议用户以 raw-binary + entry_point 重开、或改用支持 win32 的 provider),而不是四个拒绝理由里三个是 not_configured。另外 .gpr 项目打不开这点值得单独验证——Ghidra headless 导入已有项目属于常规能力,若只是桥接层没传对路径/项目名参数,修起来成本很低。可以对照 #3767 看看 Linux 侧是否已有类似放行逻辑可复用。

<!-- gh-comment-id:6396680521 --> @XinwangTao commented on GitHub (Oct 25, 2026): 这个 bug 确认是真实的,而且是文档与 admission 边界不一致的典型:open_binary 能识别并对外宣称支持 dos-mz / x86,但 Windows Ghidra lane 的 P0 边界只放行 native PE 和显式 6502,等于"前端承诺、后端拒绝",用户拿到的 provider_unavailable 也没有给出可操作的补救路径。你列出的三种变体(原始 MZ、raw-binary 强改、已存在 .gpr 项目)都失败,说明问题不在单点格式,而是 admission 判定维度太窄。建议从两个方向考虑:(1) 把 dos-mz / raw-binary 也纳入 Windows Ghidra P0 的接受集合,或用 target_format 之外的入口点/架构参数做更精确的放行,Ghidra 本身完全能加载 16-bit MZ;(2) 若短期不做,至少让拒绝信息返回可操作的 fallback 提示(如建议用户以 raw-binary + entry_point 重开、或改用支持 win32 的 provider),而不是四个拒绝理由里三个是 not_configured。另外 .gpr 项目打不开这点值得单独验证——Ghidra headless 导入已有项目属于常规能力,若只是桥接层没传对路径/项目名参数,修起来成本很低。可以对照 #3767 看看 Linux 侧是否已有类似放行逻辑可复用。
Author
Owner

@pietrondo commented on GitHub (Oct 25, 2026):

Thanks - I went through 6.8.0 (the current release) to aim the two suggestions at the right lines, and two of them need a correction.

The blocker is one short-circuit, and the DOS loader support is already there

dist/ghidra/GhidraProvider.js reaches the target support like this:

if (hostPlatform === "win32")
    return inspectWindowsP0TargetSupport(target, diagnostics);
// ... the POSIX path that admits dos-mz, psp, gamecube-dol ...

and inspectWindowsP0TargetSupport accepts format === "pe" (x86, x86-64, ARM, ARM64, application/shared-library role, non-managed) plus format === "raw-binary" && architecture === "6502". Every check after the format gate is PE-specific, so a DOS branch would sit before the target.format !== "pe" line, exactly like the gamecube-dol branch does.

The rest of the Ghidra lane already speaks DOS - dist/ghidra/GhidraLauncher.js:

: options.dosMz === true
    ? ["-loader", "MzLoader", "-processor", "x86:LE:16:Real Mode", "-cspec", "default"]
: options.dosCom === true
    ? ["-loader", "BinaryLoader", "-loader-baseAddr", "1000:0100",
       "-processor", "x86:LE:16:Real Mode", "-cspec", "default"]

dosMz: true / dosCom: true are already plumbed from the target format into the launcher (GhidraProviderClient.js, GhidraLauncher.js), dos-com even gets its own prepareCom pre-script, and the DOS import commits its language (a generic override is refused for it: "A Ghidra language override cannot be combined with a DOS import"). So admitting dos-mz and dos-com in the Windows set reads like a set-membership change, not a new lane - and dos-com would bring #3767's seeded-image workaround with it, unchanged.

raw-binary would not be a valid fallback for a DOS MZ

I would keep it out of the error text. ghidraRawBinaryLanguage("x86") returns x86:LE:32:default with cspec gcc - 16-bit real-mode code decoded as 32-bit - and the override is explicitly refused for raw imports:

if (override !== undefined && rawBinary !== undefined)
    throw new Error("A Ghidra language override cannot be combined with a raw-binary import.");

so REA_GHIDRA_LANGUAGE_ID cannot turn the raw lane into a real-mode one either. What does work on a Windows host today is the Rizin lane, which reads the same file:

execute_rizin_command(path="...\\simsearth\\simearth.exe", command="px 16 @ 0x68e1")
0000:68e1  44 24 57 56 a0 65 9c 98 3d 08 00 76 03 e9 36 02

(Rizin maps the MZ flat, so its addresses are file offsets.)

The .gpr hypothesis does not hold, I think

There is no Ghidra-project import path to fix. dist/domain/binaryTarget.js parseExecutableHeader recognises ELF, MZ, Mach-O, FAT and PE and nothing else:

if (bytes.length < 8)
    return err("truncated or unsupported binary header");

and the analysis-database value in the target-format enum is REA's own session analysis database - the Ghidra program's ephemeral database, written by close_binary(snapshot_path) and re-imported by open_binary(snapshot_path=...) - not a Ghidra project.

Concretely, in Ghidra 12 the SimEarth.gpr file is a zero-byte marker and the project data lives in SimEarth.rep/, so my attempt fell into the bytes.length < 8 branch above and never reached a path or argument problem. Opening an existing Ghidra project would be a new feature (a project-directory target), not a bridge fix.

On the actionable message

Agreed. Three things would have helped me, in this order:

  1. name the formats the Windows lane does take, and the lane that works for a DOS MZ on this host (dos-com once admitted - the #3767 seeded image - and the Rizin lane today);
  2. say that the target was refused by the admission boundary, not by the engine, so nobody goes looking for a broken Ghidra install (rea doctor already says the install is healthy);
  3. stop listing not_configured providers as if they were refusals - IDA and Kuna are not configured on my host, and their absence is not why the target was refused.

One measurement, so "Ghidra can load it" is not taken on faith

On this Windows host, Ghidra 12.1.2 imports the very same MZ with REA's own parameters (-loader MzLoader -processor x86:LE:16:Real Mode -cspec default) and analyses it: the decompiler runs over the image at 1000:02a9, 1000:2602, 1000:4427, 1000:de8b and more - the MZ loaded at segment 0x1000, the same place the MzLoader lane puts it. The 874-function inventory this repository measured earlier came from the same file with the same processor, with Ghidra auto-detecting the MZ loader.

So the engine is not the obstacle on Windows; the admission set is.

<!-- gh-comment-id:6398979676 --> @pietrondo commented on GitHub (Oct 25, 2026): Thanks - I went through 6.8.0 (the current release) to aim the two suggestions at the right lines, and two of them need a correction. ## The blocker is one short-circuit, and the DOS loader support is already there `dist/ghidra/GhidraProvider.js` reaches the target support like this: ```js if (hostPlatform === "win32") return inspectWindowsP0TargetSupport(target, diagnostics); // ... the POSIX path that admits dos-mz, psp, gamecube-dol ... ``` and `inspectWindowsP0TargetSupport` accepts `format === "pe"` (x86, x86-64, ARM, ARM64, application/shared-library role, non-managed) plus `format === "raw-binary" && architecture === "6502"`. Every check after the format gate is PE-specific, so a DOS branch would sit before the `target.format !== "pe"` line, exactly like the `gamecube-dol` branch does. The rest of the Ghidra lane already speaks DOS - `dist/ghidra/GhidraLauncher.js`: ```js : options.dosMz === true ? ["-loader", "MzLoader", "-processor", "x86:LE:16:Real Mode", "-cspec", "default"] : options.dosCom === true ? ["-loader", "BinaryLoader", "-loader-baseAddr", "1000:0100", "-processor", "x86:LE:16:Real Mode", "-cspec", "default"] ``` `dosMz: true` / `dosCom: true` are already plumbed from the target format into the launcher (`GhidraProviderClient.js`, `GhidraLauncher.js`), `dos-com` even gets its own `prepareCom` pre-script, and the DOS import **commits** its language (a generic override is refused for it: "A Ghidra language override cannot be combined with a DOS import"). So admitting `dos-mz` and `dos-com` in the Windows set reads like a set-membership change, not a new lane - and `dos-com` would bring #3767's seeded-image workaround with it, unchanged. ## `raw-binary` would not be a valid fallback for a DOS MZ I would keep it out of the error text. `ghidraRawBinaryLanguage("x86")` returns `x86:LE:32:default` with cspec `gcc` - 16-bit real-mode code decoded as 32-bit - and the override is explicitly refused for raw imports: ```js if (override !== undefined && rawBinary !== undefined) throw new Error("A Ghidra language override cannot be combined with a raw-binary import."); ``` so `REA_GHIDRA_LANGUAGE_ID` cannot turn the raw lane into a real-mode one either. What *does* work on a Windows host today is the **Rizin** lane, which reads the same file: ```text execute_rizin_command(path="...\\simsearth\\simearth.exe", command="px 16 @ 0x68e1") 0000:68e1 44 24 57 56 a0 65 9c 98 3d 08 00 76 03 e9 36 02 ``` (Rizin maps the MZ flat, so its addresses are file offsets.) ## The `.gpr` hypothesis does not hold, I think There is no Ghidra-project import path to fix. `dist/domain/binaryTarget.js` `parseExecutableHeader` recognises ELF, MZ, Mach-O, FAT and PE and nothing else: ```js if (bytes.length < 8) return err("truncated or unsupported binary header"); ``` and the `analysis-database` value in the target-format enum is REA's own **session analysis database** - the Ghidra program's ephemeral database, written by `close_binary(snapshot_path)` and re-imported by `open_binary(snapshot_path=...)` - not a Ghidra project. Concretely, in Ghidra 12 the `SimEarth.gpr` file is a **zero-byte marker** and the project data lives in `SimEarth.rep/`, so my attempt fell into the `bytes.length < 8` branch above and never reached a path or argument problem. Opening an existing Ghidra project would be a new feature (a project-directory target), not a bridge fix. ## On the actionable message Agreed. Three things would have helped me, in this order: 1. name the formats the Windows lane does take, and the lane that works for a DOS MZ on this host (`dos-com` once admitted - the #3767 seeded image - and the Rizin lane today); 2. say that the target was refused by the *admission boundary*, not by the engine, so nobody goes looking for a broken Ghidra install (`rea doctor` already says the install is healthy); 3. stop listing `not_configured` providers as if they were refusals - IDA and Kuna are not configured on my host, and their absence is not why the target was refused. ## One measurement, so "Ghidra can load it" is not taken on faith On this Windows host, Ghidra 12.1.2 imports the very same MZ with **REA's own parameters** (`-loader MzLoader -processor x86:LE:16:Real Mode -cspec default`) and analyses it: the decompiler runs over the image at `1000:02a9`, `1000:2602`, `1000:4427`, `1000:de8b` and more - the MZ loaded at segment `0x1000`, the same place the MzLoader lane puts it. The 874-function inventory this repository measured earlier came from the same file with the same processor, with Ghidra auto-detecting the MZ loader. So the engine is not the obstacle on Windows; the admission set is.
Author
Owner

@pietrondo commented on GitHub (Oct 25, 2026):

One more number, since I had the import running anyway: the Windows Ghidra 12.1.2 on this host imports the MZ with REA's own parameters and the analysis succeeds.

analyzeHeadless <proj> MzRealMode3 -import "...\simearth.exe" \
    -loader MzLoader -processor "x86:LE:16:Real Mode" -cspec default
    -scriptPath tools\ghidra -postScript SimEarthInventory.java

INFO  REPORT: Analysis succeeded for file: file:///.../simearth.exe
INFO  SimEarthInventory.java> INVENTORY functions=983

983 functions on Windows with the MzLoader lane's parameters - against the 874 the earlier import of the same file found with Ghidra auto-detecting the loader. So the engine path is complete on Windows; only inspectWindowsP0TargetSupport stands in front of it.

(The decompiler itself is the usual 16-bit-real-mode story: WARN Decompiling ... pcode error at 1000:02a9 and friends, which is why the inventory is the usable output.)

<!-- gh-comment-id:6399009064 --> @pietrondo commented on GitHub (Oct 25, 2026): One more number, since I had the import running anyway: the Windows Ghidra 12.1.2 on this host imports the MZ with **REA's own parameters** and the analysis succeeds. ```text analyzeHeadless <proj> MzRealMode3 -import "...\simearth.exe" \ -loader MzLoader -processor "x86:LE:16:Real Mode" -cspec default -scriptPath tools\ghidra -postScript SimEarthInventory.java INFO REPORT: Analysis succeeded for file: file:///.../simearth.exe INFO SimEarthInventory.java> INVENTORY functions=983 ``` 983 functions on Windows with the MzLoader lane's parameters - against the 874 the earlier import of the same file found with Ghidra auto-detecting the loader. So the engine path is complete on Windows; only `inspectWindowsP0TargetSupport` stands in front of it. (The decompiler itself is the usual 16-bit-real-mode story: `WARN Decompiling ... pcode error at 1000:02a9` and friends, which is why the inventory is the usable output.)
Author
Owner

@dark13b commented on GitHub (Oct 25, 2026):

I'm working on this in draft #7144.

It admits dos-mz in inspectWindowsP0TargetSupport, before the PE-only checks, and nothing else changes in the provider. The MzLoader import, the committed x86:LE:16:Real Mode language and the load-image attestation are the existing DOS path. On Windows 11 x64 with Ghidra 12.1.4, the generated-fixture lane npm run verify:ghidra:dos passes. It uses no third-party binary and covers:

  • load image verified;
  • near and segment-relocated far calls;
  • function bodies and pseudocode;
  • CLI/MCP parity;
  • unchanged source bytes and owned cleanup.

Windows P0 stays read-only, so annotation and naming edits are refused there. The lane now checks that refusal.

Thanks for the measurements and the corrections, which match what I found in the source:

  • dos-com is not in this PR. The import code would accept it, but verify:ghidra:com is built around annotation edits, which Windows P0 doesn't allow. I'd rather port that lane to read-only checks in a follow-up than admit COM without real-provider evidence.
  • Existing Ghidra project. Agreed that it's a new feature, not a bridge bug: the lane always imports into an ephemeral project, and .gpr is a zero-byte marker. I've left it out.
  • The message. With dos-mz admitted, your target no longer reaches that path. The registry wording, where unconfigured providers turn a format refusal into provider_unavailable, is unchanged here and could be its own issue.
<!-- gh-comment-id:6399400082 --> @dark13b commented on GitHub (Oct 25, 2026): I'm working on this in draft #7144. It admits `dos-mz` in `inspectWindowsP0TargetSupport`, before the PE-only checks, and nothing else changes in the provider. The MzLoader import, the committed `x86:LE:16:Real Mode` language and the load-image attestation are the existing DOS path. On Windows 11 x64 with Ghidra 12.1.4, the generated-fixture lane `npm run verify:ghidra:dos` passes. It uses no third-party binary and covers: - load image `verified`; - near and segment-relocated far calls; - function bodies and pseudocode; - CLI/MCP parity; - unchanged source bytes and owned cleanup. Windows P0 stays read-only, so annotation and naming edits are refused there. The lane now checks that refusal. Thanks for the measurements and the corrections, which match what I found in the source: - **`dos-com` is not in this PR.** The import code would accept it, but `verify:ghidra:com` is built around annotation edits, which Windows P0 doesn't allow. I'd rather port that lane to read-only checks in a follow-up than admit COM without real-provider evidence. - **Existing Ghidra project.** Agreed that it's a new feature, not a bridge bug: the lane always imports into an ephemeral project, and `.gpr` is a zero-byte marker. I've left it out. - **The message.** With `dos-mz` admitted, your target no longer reaches that path. The registry wording, where unconfigured providers turn a format refusal into `provider_unavailable`, is unchanged here and could be its own issue.
Author
Owner

@XinwangTao commented on GitHub (Oct 25, 2026):

Confirmed bug shape: the admission boundary rejects dos-mz outright, so open_binary succeeds (format detection) but every follow-up fails with provider_unavailable — the user ends up with a dead-end state instead of a clear 'unsupported on this platform' error. Suggestions: (1) have open_binary return a meaningful unsupported-format error for DOS MZ on Windows rather than accepting then failing later; (2) if 16-bit real-mode is genuinely out of scope, say so in the format matrix so callers can route to another lane; (3) alternatively extend admission to treat dos-mz like raw x86 with a documented caveat (Ghidra can still load MZ with a DOS loader if the language is set correctly).

<!-- gh-comment-id:6399620298 --> @XinwangTao commented on GitHub (Oct 25, 2026): Confirmed bug shape: the admission boundary rejects dos-mz outright, so open_binary succeeds (format detection) but every follow-up fails with provider_unavailable — the user ends up with a dead-end state instead of a clear 'unsupported on this platform' error. Suggestions: (1) have open_binary return a meaningful unsupported-format error for DOS MZ on Windows rather than accepting then failing later; (2) if 16-bit real-mode is genuinely out of scope, say so in the format matrix so callers can route to another lane; (3) alternatively extend admission to treat dos-mz like raw x86 with a documented caveat (Ghidra can still load MZ with a DOS loader if the language is set correctly).
Author
Owner

@pietrondo commented on GitHub (Oct 25, 2026):

On the second round of suggestions: three, and what #7144 does to each.

1. open_binary accepting and then failing. Agreed for the formats that stay refused, and #7144 removes the case for dos-mz. The general shape survives it, though: open_binary detects the format and the provider is only consulted later, so a target no configured lane admits is accepted and then fails. The cheapest fix is in the failure, not in the admission: when provider_unavailable lists not_configured providers, name the lane that does work for the target. For a DOS MZ on Windows that is the Rizin lane today (execute_rizin_command, the MZ mapped flat, addresses are file offsets). That one line is what turned my own session around, and it costs nothing.

2. "If 16-bit real mode is out of scope, say so in the matrix." It is not out of scope, and the matrix should not say so. Measured on this host (Windows 11, Ghidra 12.1.2) with REA's own parameters: the import succeeds, the analysis succeeds, and the function inventory is 983 (-loader MzLoader -processor "x86:LE:16:Real Mode" -cspec default). What is partial is decompilation: the decompiler emits pcode error at 16-bit addresses (1000:02a9 and friends), so procedure_pseudo_code is unreliable even where list_procedures, the call refs and the inventory are correct. That is the caveat worth one line in the DOS row — analysis yes, pseudocode partial — and it would have saved me from concluding the whole lane was broken.

3. "Treat dos-mz like raw x86, with a caveat." This one cannot work, and it is worth recording why in the issue so nobody tries it: ghidraRawBinaryLanguage("x86") resolves to x86:LE:32:default with the gcc cspec, and the language override is refused for raw imports (A Ghidra language override cannot be combined with a raw-binary import.). A raw import of an MZ decodes 16-bit real-mode code as 32-bit. The MzLoader lane, which is what #7144 admits, is the right path and needs no caveat of that shape.

On #7144's "not included": the provider_unavailable wording. That is the one item of the original report left out, and it is the one that turns a five-minute detour into an hour: three of the four rejection reasons are not_configured, which reads as "install IDA" when the real answer is "the Windows admission set is PE-only". If it is out of scope for this PR it deserves its own issue rather than being dropped silently.

One more datum for the scope question, since widening the documented format set is what maintainers are being asked to approve: on Windows the MzLoader lane imports the MZ at segment 0x1000, the same place the POSIX lane puts it, so the load-image attestation and the address space execute_read / list_procedures report are host-independent. Nothing in the evidence changes with the platform; only the admission set does.

<!-- gh-comment-id:6403520123 --> @pietrondo commented on GitHub (Oct 25, 2026): On the second round of suggestions: three, and what #7144 does to each. **1. `open_binary` accepting and then failing.** Agreed for the formats that stay refused, and #7144 removes the case for `dos-mz`. The general shape survives it, though: `open_binary` detects the format and the *provider* is only consulted later, so a target no configured lane admits is accepted and then fails. The cheapest fix is in the failure, not in the admission: when `provider_unavailable` lists `not_configured` providers, name the lane that does work for the target. For a DOS MZ on Windows that is the Rizin lane today (`execute_rizin_command`, the MZ mapped flat, addresses are file offsets). That one line is what turned my own session around, and it costs nothing. **2. "If 16-bit real mode is out of scope, say so in the matrix."** It is not out of scope, and the matrix should not say so. Measured on this host (Windows 11, Ghidra 12.1.2) with REA's own parameters: the import succeeds, the analysis succeeds, and the function inventory is **983** (`-loader MzLoader -processor "x86:LE:16:Real Mode" -cspec default`). What is partial is **decompilation**: the decompiler emits `pcode error` at 16-bit addresses (`1000:02a9` and friends), so `procedure_pseudo_code` is unreliable even where `list_procedures`, the call refs and the inventory are correct. That is the caveat worth one line in the DOS row — analysis yes, pseudocode partial — and it would have saved me from concluding the whole lane was broken. **3. "Treat `dos-mz` like raw x86, with a caveat."** This one cannot work, and it is worth recording why in the issue so nobody tries it: `ghidraRawBinaryLanguage("x86")` resolves to `x86:LE:32:default` with the `gcc` cspec, and the language override is refused for raw imports (`A Ghidra language override cannot be combined with a raw-binary import.`). A raw import of an MZ decodes 16-bit real-mode code as 32-bit. The MzLoader lane, which is what #7144 admits, is the right path and needs no caveat of that shape. **On #7144's "not included": the `provider_unavailable` wording.** That is the one item of the original report left out, and it is the one that turns a five-minute detour into an hour: three of the four rejection reasons are `not_configured`, which reads as "install IDA" when the real answer is "the Windows admission set is PE-only". If it is out of scope for this PR it deserves its own issue rather than being dropped silently. One more datum for the scope question, since widening the documented format set is what maintainers are being asked to approve: on Windows the MzLoader lane imports the MZ at segment `0x1000`, the same place the POSIX lane puts it, so the load-image attestation and the address space `execute_read` / `list_procedures` report are host-independent. Nothing in the evidence changes with the platform; only the admission set does.
Author
Owner

@XinwangTao commented on GitHub (Oct 26, 2026):

Thanks for the detailed verification — the concrete numbers (983-function inventory with Ghidra 12.1.2, decompiler pcode errors at 16-bit addresses) settle the scope nicely: the DOS row in the format matrix should say "analysis yes, pseudocode partial", and the Rizin-lane pointer for the failure message is a cheap, high-value fix. Also agree the provider_unavailable wording should not be dropped — if it stays out of #7144 it deserves its own issue, so nobody else loses an hour on what is really a PE-only admission set. Happy to help file that if useful.

<!-- gh-comment-id:6405953917 --> @XinwangTao commented on GitHub (Oct 26, 2026): Thanks for the detailed verification — the concrete numbers (983-function inventory with Ghidra 12.1.2, decompiler pcode errors at 16-bit addresses) settle the scope nicely: the DOS row in the format matrix should say "analysis yes, pseudocode partial", and the Rizin-lane pointer for the failure message is a cheap, high-value fix. Also agree the provider_unavailable wording should not be dropped — if it stays out of #7144 it deserves its own issue, so nobody else loses an hour on what is really a PE-only admission set. Happy to help file that if useful.
Author
Owner

@pietrondo commented on GitHub (Oct 26, 2026):

Filed it as #7214, with the scope you named: when the rejection set holds no format-level refusal, say that once and name a lane that works for the target (Rizin for a DOS image on Windows), and keep a provider that declined the format saying so - the distinction is the point. The dos-com case on Windows is the one that survives #7144, so the issue is framed around it rather than around dos-mz.

Thanks for taking #7144 through to a non-draft PR; the erify:ghidra:dos lane running on Windows is more than I would have asked for, and it is what makes the DOS row in the matrix credible rather than aspirational.

<!-- gh-comment-id:6411224329 --> @pietrondo commented on GitHub (Oct 26, 2026): Filed it as #7214, with the scope you named: when the rejection set holds no format-level refusal, say that once and name a lane that works for the target (Rizin for a DOS image on Windows), and keep a provider that *declined the format* saying so - the distinction is the point. The dos-com case on Windows is the one that survives #7144, so the issue is framed around it rather than around dos-mz. Thanks for taking #7144 through to a non-draft PR; the erify:ghidra:dos lane running on Windows is more than I would have asked for, and it is what makes the DOS row in the matrix credible rather than aspirational.
Author
Owner

@pietrondo commented on GitHub (Oct 27, 2026):

One lane does read this target on Windows today: execute_rizin_command. rizin -N -q -c "<cmd>" simearth.exe gets the MZ mapped flat, so the addresses in its output are file offsets, and that was enough to triage the binary:

izz~DAT; izz~OVL
0x0002fa15  ... GR.OVL
0x0002fa95  ... GLOBE.OVL
0x0002fb15  ... MAP.OVL
0x0002fb95  ... SIM.OVL
0x0005873c  ... UNABLE TO CREATE RTGLOBE.DAT

So the dos-mz refusal here is the selection layer, not a missing path: open_binary binds the target and execute_rizin_command runs on the same file while binary_overview / search_strings / list_strings return provider_unavailable. Two things from this session:

  • The message sends you looking for an install problem. With candidate_ids: [ghidra, hopper, ida, kuna], two of which were never configured, provider_unavailable reads as "nothing can open this file" when one lane can. That is the conflation in #7214.
  • If the rizin lane is the intentional raw fallback, naming it in the rejection details (e.g. available_lanes: ["rizin"]) would have saved this detour.

Nothing else: the Ghidra 16-bit lane in the draft PR is what I need for pseudocode; rizin gives strings and disassembly only.

<!-- gh-comment-id:6455497556 --> @pietrondo commented on GitHub (Oct 27, 2026): One lane does read this target on Windows today: `execute_rizin_command`. `rizin -N -q -c "<cmd>" simearth.exe` gets the MZ mapped flat, so the addresses in its output are file offsets, and that was enough to triage the binary: ``` izz~DAT; izz~OVL 0x0002fa15 ... GR.OVL 0x0002fa95 ... GLOBE.OVL 0x0002fb15 ... MAP.OVL 0x0002fb95 ... SIM.OVL 0x0005873c ... UNABLE TO CREATE RTGLOBE.DAT ``` So the `dos-mz` refusal here is the selection layer, not a missing path: `open_binary` binds the target and `execute_rizin_command` runs on the same file while `binary_overview` / `search_strings` / `list_strings` return `provider_unavailable`. Two things from this session: - The message sends you looking for an install problem. With `candidate_ids: [ghidra, hopper, ida, kuna]`, two of which were never configured, `provider_unavailable` reads as "nothing can open this file" when one lane can. That is the conflation in #7214. - If the rizin lane is the intentional raw fallback, naming it in the rejection details (e.g. `available_lanes: ["rizin"]`) would have saved this detour. Nothing else: the Ghidra 16-bit lane in the draft PR is what I need for pseudocode; rizin gives strings and disassembly only.
Author
Owner

@XinwangTao commented on GitHub (Oct 28, 2026):

Agreed — rizin covers the triage (strings/disassembly) but not pseudocode, so #7144's Ghidra 16-bit lane is still the one that unblocks the reconstruction task. Listing available_lanes (e.g. ["rizin"]) in the rejection details is a cheap, high-value improvement: right now a user seeing three not_configured reasons is sent hunting for an install problem when one lane can already read the file. Looking forward to the DOS row in the format matrix flipping to verified once #7144 lands.

<!-- gh-comment-id:6457824918 --> @XinwangTao commented on GitHub (Oct 28, 2026): Agreed — rizin covers the triage (strings/disassembly) but not pseudocode, so #7144's Ghidra 16-bit lane is still the one that unblocks the reconstruction task. Listing available_lanes (e.g. ["rizin"]) in the rejection details is a cheap, high-value improvement: right now a user seeing three not_configured reasons is sent hunting for an install problem when one lane can already read the file. Looking forward to the DOS row in the format matrix flipping to verified once #7144 lands.
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#4279
No description provided.