aboutsummaryrefslogtreecommitdiffstats
path: root/server/patch_server.py
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-26 15:38:06 +0200
committerblasty <blasty@local>2026-07-26 15:38:06 +0200
commitfb275522b143d3891f5f8e9b1e6eaa635537ec15 (patch)
tree28dc600876269e32758aa7429ca492e779ca1cc1 /server/patch_server.py
parentapp: rebuild the function index after an edit changes it (diff)
downloadida-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.py41
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"