aboutsummaryrefslogtreecommitdiffstats
path: root/idatui
diff options
context:
space:
mode:
Diffstat (limited to 'idatui')
-rw-r--r--idatui/app.py23
-rw-r--r--idatui/domain.py8
2 files changed, 30 insertions, 1 deletions
diff --git a/idatui/app.py b/idatui/app.py
index 2190643..ff67eef 100644
--- a/idatui/app.py
+++ b/idatui/app.py
@@ -738,6 +738,7 @@ class ListingView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=Tru
Binding("a", "make_string", "Str", show=False),
Binding("p", "define_func", "Func", show=False),
Binding("u", "undefine", "Undef", show=False),
+ Binding("t", "toggle_thumb", "ARM/Thumb", show=False),
*SearchMixin.SEARCH_BINDINGS,
*NavMixin.NAV_BINDINGS,
*ColumnCursor.COL_BINDINGS,
@@ -1126,6 +1127,9 @@ class ListingView(SearchMixin, NavMixin, ColumnCursor, ScrollView, can_focus=Tru
def action_undefine(self) -> None:
self.post_message(EditItemRequested(self, "undef"))
+ def action_toggle_thumb(self) -> None:
+ self.post_message(EditItemRequested(self, "thumb"))
+
def action_make_data(self) -> None:
self.post_message(MakeDataRequested(self))
@@ -5150,7 +5154,8 @@ class IdaTui(App):
anchor: ViewAnchor | None = None) -> None: # worker context
assert self.program is not None
verb = {"code": "defined code", "func": "created function",
- "undef": "undefined", "string": "made string"}[kind]
+ "undef": "undefined", "string": "made string",
+ "thumb": "switched decoding"}[kind]
try:
if kind == "code":
# Keep going until something stops it: one instruction is rarely
@@ -5175,6 +5180,22 @@ class IdaTui(App):
"limit": "instruction limit"}.get(why, why)
verb = (f"defined {n} instruction{'s' if n != 1 else ''} "
f"({ea:#x}\u2013{end:#x}) \u2014 {reason}")
+ elif kind == "thumb":
+ # Switch the mode, then disassemble in it: flipping T and
+ # leaving the bytes undefined shows nothing, and the reason you
+ # flipped it was to read the code.
+ r = self.program.set_thumb(ea)
+ run = self.program.define_code_run(ea)
+ n = int(run.get("count", 0))
+ mode = "Thumb" if r.get("thumb") else "ARM"
+ verb = f"{mode} @ {ea:#x}"
+ if r.get("forced_32bit"):
+ verb += " (segment set to 32-bit; Thumb needs ARM32)"
+ verb += (f" \u2014 {n} instruction{'s' if n != 1 else ''}"
+ if n else " \u2014 still doesn't decode")
+ # falls through to the shared reload: same cache bump, same
+ # anchor restore, same flash. That is the whole point of having
+ # one path.
elif kind == "func":
self.program.define_func(ea)
elif kind == "string":
diff --git a/idatui/domain.py b/idatui/domain.py
index 606cf42..8787431 100644
--- a/idatui/domain.py
+++ b/idatui/domain.py
@@ -1373,6 +1373,14 @@ class Program:
if res.get("error"):
raise IDAToolError("define_code", f"@ {ea:#x}: {res['error']}")
+ def set_thumb(self, ea: int, mode: str = "toggle") -> dict:
+ """Switch ARM/Thumb decoding at ``ea``. Returns the resulting state."""
+ r = self.client.call("set_thumb", addr=hex(ea), mode=mode)
+ if not isinstance(r, dict) or r.get("error"):
+ raise IDAToolError("set_thumb",
+ f"@ {ea:#x}: {(r or {}).get('error', 'failed')}")
+ return r
+
def define_code_run(self, ea: int, limit: int = 20000) -> dict:
"""Disassemble consecutively from ``ea`` until something stops it.