aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-24 00:18:18 +0200
committerblasty <blasty@local>2026-07-24 00:18:18 +0200
commit59726b4c151ae59170cf2975774d89e20d162e0d (patch)
treeee495da79d4b2c10b0452425106b51b9c900f5a3
parentnav: xref jump into pseudocode lands on the referenced symbol, not column 0 (diff)
downloadida-tui-59726b4c151ae59170cf2975774d89e20d162e0d.tar.gz
ida-tui-59726b4c151ae59170cf2975774d89e20d162e0d.tar.xz
ida-tui-59726b4c151ae59170cf2975774d89e20d162e0d.zip
nav: snap an xref-into-pseudocode jump to the line that holds the symbol
Hex-Rays can attribute an address to a pseudocode line a step off from where the referenced symbol actually appears, so an xref jump could land on a line that doesn't contain the token (cursor at column 0, wrong line). Add _decomp_locate: anchor on the /*0xEA*/ marker line for the address, but when a token is known, snap to the whole-word occurrence NEAREST the anchor (preferring the anchor line, then the line just below). This fixes the marker/ symbol line discrepancy and disambiguates a symbol that appears multiple times by picking the occurrence closest to the address. The mid-function decomp-jump branch now uses it. Verified: real xref (sub_2060 from sub_20DD) still lands on 'sub_2060(a2);' cursor_x=4; synthetic discrepancy (marker on a line without the symbol) snaps to the nearest call-site line, not the far unrelated occurrence. Full suite green.
-rw-r--r--idatui/app.py45
1 files changed, 40 insertions, 5 deletions
diff --git a/idatui/app.py b/idatui/app.py
index 57ee5c9..6a19142 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -3577,13 +3577,14 @@ class IdaTui(App):
# Jumping to the function itself: land on its name in the
# prototype (line 0), not column 0.
dec_idx = 0
- token = fn.name
+ col = self._decomp_col_for(fn.addr, 0, fn.name)
else:
# A mid-function site (e.g. an xref jump to a call site):
- # land on the referenced symbol on that line, not column 0.
- dec_idx = max(self._decomp_line_for(fn.addr, ea), 0)
- token = focus_name or fn.name
- col = self._decomp_col_for(fn.addr, dec_idx, token) if token else 0
+ # anchor on the address but snap to the nearest line that
+ # actually holds the referenced symbol (the marker line and
+ # the symbol's line can differ), landing on the token.
+ dec_idx, col = self._decomp_locate(fn.addr, ea,
+ focus_name or fn.name)
self.app.call_from_thread(
self._open_decomp_entry, fn.addr, fn.name, dec_idx, col, push)
return
@@ -3638,6 +3639,40 @@ class IdaTui(App):
m = re.search(rf"\b{re.escape(name)}\b", clean)
return m.start() if m else 0
+ def _decomp_locate(self, fn_addr: int, ea: int,
+ token: str | None) -> tuple[int, int]:
+ """Best (line, column) for address ``ea`` in ``fn_addr``'s pseudocode.
+
+ Anchors on the /*0xEA*/ marker line for ``ea``, but Hex-Rays can attribute
+ an address to a line a step off from where the referenced symbol actually
+ appears. So when ``token`` is given, snap to the whole-word occurrence of
+ it NEAREST the anchor line (preferring the anchor line itself, then the
+ line just below), which disambiguates repeated symbols by address."""
+ assert self.program is not None
+ try:
+ dec = self.program.decompile(fn_addr)
+ except Exception: # noqa: BLE001
+ return (0, 0)
+ lines = (dec.code or "").splitlines()
+ if not lines:
+ return (0, 0)
+ anchor = self._decomp_line_for(fn_addr, ea)
+ anchor = anchor if anchor >= 0 else 0
+ if not token:
+ return (anchor, 0)
+ pat = re.compile(rf"\b{re.escape(token)}\b")
+ best: tuple[int, int] | None = None
+ best_key: tuple[int, int] | None = None
+ for i, ln in enumerate(lines):
+ m = pat.search(_ADDR_MARK_STRIP_RE.sub("", ln))
+ if not m:
+ continue
+ # rank: nearest to the anchor; tie -> the line at/after the anchor.
+ key = (abs(i - anchor), 0 if i >= anchor else 1)
+ if best_key is None or key < best_key:
+ best_key, best = key, (i, m.start())
+ return best if best is not None else (anchor, 0)
+
def _decomp_line_for(self, fn_addr: int, ea: int) -> int:
"""Pseudocode line index best matching address ``ea``: the line whose
/*0xEA*/ marker is the largest address <= ``ea``. -1 if unavailable