diff options
| author | blasty <blasty@local> | 2026-07-26 15:38:06 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-26 15:38:06 +0200 |
| commit | fb275522b143d3891f5f8e9b1e6eaa635537ec15 (patch) | |
| tree | 28dc600876269e32758aa7429ca492e779ca1cc1 /server/patch_server.py | |
| parent | app: rebuild the function index after an edit changes it (diff) | |
| download | ida-tui-fb275522b143d3891f5f8e9b1e6eaa635537ec15.tar.gz ida-tui-fb275522b143d3891f5f8e9b1e6eaa635537ec15.tar.xz ida-tui-fb275522b143d3891f5f8e9b1e6eaa635537ec15.zip | |
decomp: say WHY a function won't decompile
"I can't decompile my freshly defined function" — and the app's answer was to
flash and go back to the listing. It knew the reason and threw it away twice.
The plain decompile tool reports "Decompilation failed at 0x0 (address: 0x0)".
Hex-Rays itself fills in a hexrays_failure_t with the actual sentence, and in
this case it is the whole answer: "only 64-bit functions can be decompiled in
the current database". Nobody can guess that from a flash, and it is not fixable
in place — the database's bitness is set at load — so without the message there
is no way forward at all.
New decomp_error tool returns Hex-Rays' own description; _load_decomp asks for it
in the same worker when a decompile fails, and the status now reads:
sub_0: cannot decompile — only 64-bit functions can be decompiled in the
current database — Ctrl+L and pick arm:ARMv7-A
Then it got thrown away a second time, by the reload. Falling back to the
listing reopens it, and the reload writes its own status afterwards — the same
clobber that has now bitten four times. The fix this round is the last one:
_flash is SHOWN by idle status writes but no longer CONSUMED by them, because a
reload emits several (prime, then cursor) and consuming on the first meant the
second erased the message. It clears on the next keypress instead — when the
user has actually moved on.
(That also fixes a self-inflicted "status: None": the old code read _flash back
after something else had already consumed it.)
tests: +2 thumb (15) — a failed decompile says why in Hex-Rays' words, and the
reason survives the view reloading under it. 209/0 scenarios, 30/0 blob, 30/0
project UI.
Diffstat (limited to 'server/patch_server.py')
| -rw-r--r-- | server/patch_server.py | 41 |
1 files changed, 41 insertions, 0 deletions
diff --git a/server/patch_server.py b/server/patch_server.py index ee7600a..2de852f 100644 --- a/server/patch_server.py +++ b/server/patch_server.py @@ -1083,6 +1083,47 @@ def define_func_run( f = idaapi.get_func(ea) return {"addr": hex(ea), "ok": True, "start": hex(f.start_ea), "end": hex(f.end_ea), "how": "explicit-end"} + +@tool +@idasync +def decomp_error( + addr: Annotated[str, "Address of the function that failed to decompile"], +) -> dict: + """Why Hex-Rays refused this function, in its own words. + + The plain decompile tool reports "Decompilation failed at 0x0" and drops the + reason, which is the only useful part. Hex-Rays fills in a hexrays_failure_t + saying things like "only 64-bit functions can be decompiled in the current + database" — that one is unfixable in place (the database's bitness is set at + load), so a user who can't see it has no way to know they must reload.""" + import ida_funcs + import ida_hexrays + import ida_ida + + try: + ea = parse_address(addr) + except Exception as e: + return {"addr": str(addr), "error": str(e)} + out = {"addr": hex(ea), "bitness": ida_ida.inf_get_app_bitness()} + fn = ida_funcs.get_func(ea) + if fn is None: + out["reason"] = "no function here" + return out + try: + if not ida_hexrays.init_hexrays_plugin(): + out["reason"] = "the decompiler is not available for this processor" + return out + hf = ida_hexrays.hexrays_failure_t() + cf = ida_hexrays.decompile_func(fn, hf) + if cf is not None: + out["reason"] = "" # it decompiles now + return out + out["reason"] = hf.desc() or f"error {hf.code}" + out["code"] = int(hf.code) + out["errea"] = hex(hf.errea) + except Exception as e: # noqa: BLE001 + out["reason"] = f"{type(e).__name__}: {e}" + return out ''' SNIPPET = f"{BEGIN}\n{BODY.strip()}\n{END}\n" |
