aboutsummaryrefslogtreecommitdiffstats
path: root/tests/test_formats.py
diff options
context:
space:
mode:
authorblasty <blasty@local>2026-07-26 15:04:51 +0200
committerblasty <blasty@local>2026-07-26 15:04:51 +0200
commit2c2124aff2f4ed6df23512603b6e1ecdcd4b699b (patch)
tree2b2c2afe69e7f33e87ccb58e3bc466a94a4e689e /tests/test_formats.py
parentarm: switch ARM/Thumb decoding with `t` (diff)
downloadida-tui-2c2124aff2f4ed6df23512603b6e1ecdcd4b699b.tar.gz
ida-tui-2c2124aff2f4ed6df23512603b6e1ecdcd4b699b.tar.xz
ida-tui-2c2124aff2f4ed6df23512603b6e1ecdcd4b699b.zip
arm: offer 32-bit ARM at load, and fix `p` on carved code
Reported as "after c a few times and p at the entry point, Tab just flashes and nothing decompiles". Three separate things, found by following it down: **1. `p` failed on hand-carved code.** ida_funcs.add_func(ea) asks IDA to find the function's end and on carved code it often can't — a run ending in a tail call, or whose last instruction isn't recognised as a return, fails with no reason given. add_func(ea, end) with an explicit end succeeds. define_func_run tries IDA's way first, then falls back to the end of the contiguous instruction run, and says which it used. **2. The database was 64-bit, so Hex-Rays refused it regardless.** Bare `-parm` gives an AArch64 database. Ask Hex-Rays for the failure object rather than reading None as "dunno" and it says exactly what's wrong: "only 64-bit functions can be decompiled in the current database". So the disassembly looked right and F5 could never work. That is decided at LOAD and cannot be corrected — inf_set_app_bitness(32) afterwards makes the decompiler INTERR 50735. The fix is at the load dialog: arm:ARMv7-A (most firmware), arm:ARMv7-M / arm:ARMv6-M (Cortex-M, Thumb only) and arm:ARMv5TE now sit alongside 64-bit `arm`, labelled with their bitness. With arm:ARMv7-A, experiments/fibonacci.bin decompiles: void __fastcall __noreturn sub_0(int a1) { int v2; v2 = sub_E3C(a1, 0); ... } — and IDA's own auto-analysis finds 54 Thumb functions on load, versus none as plain `arm`. **3. `t` was silently building an undecompilable state.** It forced the SEGMENT to 32-bit in a 64-bit database, which produces correct-looking disassembly that F5 will never touch. It now says so and names the fix (Ctrl+L, arm:ARMv7-A) rather than leaving you to discover it. tools/verify_procs.py now reports each processor's resulting bitness, since that is the reason the variants exist — and it compares against the base module name, because a variant reports "ARM". tests: test_thumb_ui.py +5 (13 total) — a 64-bit database warns and names the fix, a 32-bit one finds functions by itself, Tab decompiles a Thumb function and the result reads like C. test_formats.py +2 (34) pinning that a 32-bit variant is offered and the ARM labels state their bitness. 209/0 scenarios, 26/0 blob, 30/0 project UI.
Diffstat (limited to 'tests/test_formats.py')
-rw-r--r--tests/test_formats.py18
1 files changed, 17 insertions, 1 deletions
diff --git a/tests/test_formats.py b/tests/test_formats.py
index ab72e8d..1045703 100644
--- a/tests/test_formats.py
+++ b/tests/test_formats.py
@@ -77,9 +77,22 @@ def main() -> int:
load_args("arm", 0, "-T binary") == "-parm -T binary")
check("the processor list leads with the common targets",
- [n for n, _ in PROCESSORS[:3]] == ["arm", "armb", "metapc"],
+ [n for n, _ in PROCESSORS[:2]] == ["arm", "arm:ARMv7-A"],
f"{[n for n, _ in PROCESSORS[:3]]}")
+ # 32-bit ARM has to be offered SEPARATELY from bare 'arm', which gives a
+ # 64-bit database. That isn't cosmetic: Hex-Rays refuses a 32-bit function
+ # in a 64-bit database, and Thumb doesn't exist in AArch64 at all, so a
+ # firmware image loaded as plain 'arm' can never be decompiled — and the
+ # database's bitness cannot be corrected after load.
+ check("a 32-bit ARM variant is offered",
+ any(n.startswith("arm:ARMv") for n, _ in PROCESSORS),
+ f"{[n for n, _ in PROCESSORS if n.startswith('arm')]}")
+ check("and the labels say which is 32- vs 64-bit",
+ all(("32-bit" in d or "64-bit" in d)
+ for n, d in PROCESSORS if n == "arm" or n.startswith("arm:")),
+ f"{[(n, d) for n, d in PROCESSORS if n.startswith('arm')]}")
+
# Every offered name must have been checked against a real IDA, because a
# wrong one is REJECTED (rc=4) with nothing useful said — handing the user a
# dead end from inside the dialog meant to rescue them. This list is the
@@ -89,6 +102,9 @@ def main() -> int:
"arm", "armb", "metapc", "mipsl", "mipsb", "ppc", "ppcl", "sh4", "68k",
"riscv", "tricore", "xtensa", "avr", "z80", "tms320c6", "m32r", "arc",
"h8300", "sparcb", "sparcl", "s390",
+ # variants: tools/verify_procs.py checks these report the base module
+ # AND change the database bitness, which is the reason they exist
+ "arm:ARMv7-A", "arm:ARMv7-M", "arm:ARMv6-M", "arm:ARMv5TE",
}
offered = {n for n, _ in PROCESSORS}
check("every offered processor name is IDA-verified",