diff options
| -rw-r--r-- | idatui/rpc.py | 10 | ||||
| -rw-r--r-- | tests/test_rawimage_rpc.py | 17 |
2 files changed, 26 insertions, 1 deletions
diff --git a/idatui/rpc.py b/idatui/rpc.py index c356f12..9a60faa 100644 --- a/idatui/rpc.py +++ b/idatui/rpc.py @@ -593,7 +593,15 @@ class RpcServer: failed = [r for r in (res.get("func") or []) if isinstance(r, dict) and r.get("error")] if isinstance(res, dict) else [] - # Names live in the IDB, but every cache in front of it is now stale. + # Names live in the IDB, but every cache in front of it is now stale -- + # including Hex-Rays', which is per-function and does NOT notice that a + # *callee* was renamed. That cache is persisted in the .i64, so without + # this a batch import leaves pseudocode calling sub_98C0 forever while + # the listing (and every readback) says memset. + try: + await asyncio.to_thread(app.program.client.call, "force_recompile") + except Exception: # noqa: BLE001 -- older worker without the tool + pass app.program.bump_names() app.program.invalidate_functions() app._func_index = None diff --git a/tests/test_rawimage_rpc.py b/tests/test_rawimage_rpc.py index ab7eb96..fe2629f 100644 --- a/tests/test_rawimage_rpc.py +++ b/tests/test_rawimage_rpc.py @@ -151,6 +151,23 @@ def main() -> int: check("rename_many takes inline items", r["rename_many"]["ok"] == 1, json.dumps(r["rename_many"])) + # -- the stale-pseudocode trap ------------------------------ # + # Hex-Rays caches per function and does not notice that a + # CALLEE was renamed -- and that cache is persisted in the + # .i64. Decompile first, then rename, then read it back: the + # call site must show the new name. (fibonacci is recursive, so + # the function's own body cites it.) + before = c.call("pseudocode", target=hex(ea)) + pc_before = json.dumps(before) + r = c.call("rename_many", items=[{"addr": hex(ea), + "name": "after_cache_fn"}]) + pc_after = json.dumps(c.call("pseudocode", target=hex(ea))) + check("pseudocode was cached before the rename", + "inline_named_fn" in pc_before, pc_before[:200]) + check("rename_many invalidates the decompile cache", + "after_cache_fn" in pc_after + and "inline_named_fn" not in pc_after, pc_after[:300]) + empty = None try: c.call("rename_many") |
