diff options
| author | blasty <blasty@local> | 2026-07-09 19:31:03 +0200 |
|---|---|---|
| committer | blasty <blasty@local> | 2026-07-09 19:31:03 +0200 |
| commit | 2d9ee753da26250eb577f5a4b4792df1220d2a76 (patch) | |
| tree | b08a8c865572bf89cc91f2c5a26087dfa59c12d3 | |
| parent | cap the function pane width so it doesn't dominate wide terminals (diff) | |
| download | ida-tui-2d9ee753da26250eb577f5a4b4792df1220d2a76.tar.gz ida-tui-2d9ee753da26250eb577f5a4b4792df1220d2a76.tar.xz ida-tui-2d9ee753da26250eb577f5a4b4792df1220d2a76.zip | |
fix: decomp follow fails on a just-renamed symbol (stale name)
Right after a rename there's a window where the pseudocode cursor word is still
the OLD name while the symbol table already has the new one, so name-based decomp
follow (refs / resolve / _ref_on_line) all miss -> 'nothing to follow'. Disasm
never hit this because it falls back to an address xref.
Add the same address-based fallback to _follow_decomp: parse the line's /*0xEA*/
marker and follow a code xref from that statement — immune to a stale name.
Verified: with the old token 'sub_53D20' (renamed away) it still follows to the
right address. pilot suite 58/58.
| -rw-r--r-- | idatui/app.py | 8 | ||||
| -rw-r--r-- | tests/test_tui.py | 19 |
2 files changed, 27 insertions, 0 deletions
diff --git a/idatui/app.py b/idatui/app.py index b5bb5f2..702f59a 100644 --- a/idatui/app.py +++ b/idatui/app.py @@ -1347,6 +1347,14 @@ class IdaTui(App): if addr is None: addr = self._ref_on_line(line) if addr is None: + # Address-based fallback: use the line's /*0xEA*/ marker and follow a + # code xref from that statement. Immune to a stale name (e.g. right + # after a rename, before the pseudocode text catches up). + m = re.search(r"/\*\s*0x([0-9A-Fa-f]+)\s*\*/", line or "") + if m: + xr = self.program.xrefs_from(int(m.group(1), 16)) + addr = next((x.to for x in xr if x.type == "code" and x.to), None) + if addr is None: self.app.call_from_thread(self._status, "nothing to follow on this line") return self._do_navigate(addr, push=True) diff --git a/tests/test_tui.py b/tests/test_tui.py index 0419842..f43c4a2 100644 --- a/tests/test_tui.py +++ b/tests/test_tui.py @@ -421,6 +421,25 @@ async def run(db): dec.cursor == drow and dec.word_under_cursor() == dsym, f"cursor={dec.cursor} (want {drow}) word={dec.word_under_cursor()!r}") + # Follow must still work when the name is stale (as right after a + # rename, before the pseudocode text catches up): the ea-marker + # fallback follows by address regardless of the (old) token. + dstale = app.program.resolve(dsym) + old_line = dec._texts[drow] if drow < len(dec._texts) else "" + if "/*0x" in old_line and dsym in old_line: + tmpname = f"stale_{os.getpid()}" + app.program.client.call( + "rename", batch={"func": {"addr": hex(dstale), "name": tmpname}}) + app.program.bump_names() + d2 = len(app._nav) + app._follow_decomp(old_line, dsym) # dsym is now the OLD name + await wait_until(pilot, lambda: len(app._nav) > d2, timeout=25) + check("decomp follow works with a stale name (ea-marker fallback)", + app._cur.ea == dstale, f"cur={app._cur.ea:#x} want={dstale:#x}") + app.program.client.call( + "rename", batch={"func": {"addr": hex(dstale), "name": dsym}}) + app.program.bump_names() + # Column sort: click headers to sort by name / address. if app._filter_term: app._apply_filter("") |
