Skip to content

[AUTOMATED] fix(ghidra): fetch p-code injects from the host instead of failing the function - #427

Merged
mahaloz merged 2 commits into
mainfrom
fix/ghidra-pcode-inject
Sep 6, 2026
Merged

[AUTOMATED] fix(ghidra): fetch p-code injects from the host instead of failing the function#427
mahaloz merged 2 commits into
mainfrom
fix/ghidra-pcode-inject

Conversation

@mahaloz

@mahaloz mahaloz commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

The problem

With kuna installed as Ghidra's decompiler core, most ARM and MIPS functions
produce nothing at all: six of fmt_arm's seven functions and plt_mips32/main
come back with an empty payload, which Ghidra reports as a decode error.

$ GHIDRA_INSTALL_DIR=<ghidra> \
  KUNA_GHIDRA_EXE=decompiler/target/release/kuna_ghidra \
  KUNA_SMOKE_BINARY=decompiler/crates/kuna-analysis/tests/fixtures/fmt_arm \
  KUNA_SMOKE_FUNCTIONS=main \
  python3 integrations/ghidra/live-smoke/kuna_vs_stock.py
main: completed=False  err='Pcode: Decoding error: Expecting <doc> but did not scan an element'

kuna's own warning on that run names the cause: could not decompile the function at r0x00000504 (inject_userop: callother-fixup template not compiled). Ghidra mode has no local .sla, so no <callotherfixup> body is
ever compiled, and ARM.cspec's setISAMode — raised by every interworking
BX/BLX — fails the whole function. All nine vendored MIPS cspecs declare the
same fixup. The same functions decompile fine on the CLI, where the template
exists.

The fix

  • When the injection library holds no compiled template, the two consumers in
    decompile_drive.rs fall through to a new defaulted
    EngineTranslate::fetch_inject_pcode, which the ghidra translator answers
    with a getPcodeInject query — the same seam shape as
    probe_register_varnode.
  • The response is streamed straight into the emitter and never cached. It is
    p-code the host has already lifted against one call site: two answers for the
    same setISAMode differ in both the fall-through offset and the <addr>
    stamped on every op, so keying a cache by payload id would silently
    miscompile any body that uses inst_start/inst_next/the call address.
  • A host exception is caught and re-raised as a low-level error naming the
    payload, as upstream does, so the warning says which injection failed instead
    of repeating the host's bare text.
  • No option: this only corrects wrong output. The standalone path keeps the
    identical template arm and its C is byte-for-byte unchanged; ghidra mode goes
    from failing the function to the CLI's answer. A flag would also be
    unreachable from the GUI, which is where the affected user is.

The tests

tests/inject_e2e.rs drives the six failing ARM functions plus frame_dummy,
MIPS main, and AArch64 (whose cspec declares no fixup) through the ghidra-sim
harness; without the fix six of seven ARM and the MIPS function come back with a
0-byte payload. Each drive also pins the getCallOtherFixup traffic, so a
degrade that emitted a placeholder without touching the wire would fail. Two
more cover the host declining, by exception and by empty response. In
protocol_e2e.rs, a round trip over bytes captured from a live Ghidra 12.1.2:
the request kuna builds is byte-identical to the one Ghidra received, and
Ghidra's own response decodes to the r0 = r0 fixup body. kuna decompile fmt_arm <fn> is unchanged on all seven functions.

Closes #407.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TzfafnkDMQiYoifXP5HSC8

mahaloz and others added 2 commits September 6, 2026 00:19
…f failing the function

In ghidra mode there is no local `.sla`, so no injection payload ever gets a
compiled template and the first CALLOTHER carrying a cspec `<callotherfixup>`
hard-errored the whole function. `ARM.cspec` and all nine vendored MIPS cspecs
declare a `setISAMode` fixup that every interworking branch raises, so six of
`fmt_arm`'s seven functions and `plt_mips32/main` produced nothing at all.

The two template consumers in `decompile_drive.rs` now fall through to a new
defaulted `EngineTranslate::fetch_inject_pcode`, which the ghidra translator
answers with a `getPcodeInject` query carrying the live injection context. The
response is p-code the host already lifted against that one call site, so it is
streamed straight into the emitter and never cached. A host exception becomes a
low-level error naming the payload rather than a passed Java exception, so an
unsupplied payload costs one function and not the command.

No option: this is a strict bug fix. The standalone path keeps the identical
`Some(tpl)` arm and its CLI output is byte-for-byte unchanged.

Closes #407

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TzfafnkDMQiYoifXP5HSC8
…ect seam

Dropping the Java-exception arm does not abort the command: the DecompileAt
handler turns any Err into the incomplete-function shape, and pass_java_exception
only sees the dispatcher's own error. The arm still matches upstream and still
names the payload in the warning — that is all it buys.

The integration table's call-fixup half is wired but unreachable: remote functions
decode with func_inject_id -1 and the remote prototype has no inject arm.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TzfafnkDMQiYoifXP5HSC8
@mahaloz
mahaloz force-pushed the fix/ghidra-pcode-inject branch from 8c087a6 to a34970d Compare September 6, 2026 00:19
@mahaloz mahaloz added the full-ci Run the full cargo workspace suite on this PR before merge (internal PRs skip it by default) label Sep 6, 2026
@mahaloz
mahaloz merged commit c7e1d51 into main Sep 6, 2026
10 of 11 checks passed
@mahaloz
mahaloz deleted the fix/ghidra-pcode-inject branch September 6, 2026 00:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

full-ci Run the full cargo workspace suite on this PR before merge (internal PRs skip it by default)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[AUTOMATED] ghidra mode: ARM/MIPS functions fail to decompile — p-code injects are never compiled or fetched

1 participant