aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/PROJECTS.md9
-rw-r--r--idatui/formats.py20
-rw-r--r--tests/test_formats.py29
-rw-r--r--tools/verify_procs.py64
4 files changed, 118 insertions, 4 deletions
diff --git a/docs/PROJECTS.md b/docs/PROJECTS.md
index 5f8e327..88ce2f6 100644
--- a/docs/PROJECTS.md
+++ b/docs/PROJECTS.md
@@ -258,6 +258,15 @@ when no loader matches:
│ Enter accept · Tab base address · Esc load as IDA │
└─────────────────────────────────────────────────────┘
+Every name in that list is verified against a real IDA by `tools/verify_procs.py`
+(open a scratch blob with `-p<name>`, read back `inf_get_procname()`). A wrong
+name is *rejected* — rc=4, nothing useful said — which would be a dead end handed
+out from inside the dialog meant to rescue you. Two of the first twenty were
+wrong: `h8` and `sparc` are module filenames (`procs/h8.so`), not processor
+names; the real ones are `h8300` and `sparcb`/`sparcl`. The aliases people reach
+for first — `arm64`, `aarch64`, `mips`, `m68k` — are all invalid too, so they
+live in the human labels where the filter still finds them.
+
`formats.sniff()` decides whether to ask, and only recognises formats IDA
definitely handles (ELF, PE, Mach-O, dex, wasm, ar, COFF, Intel HEX, S-records).
Being cautious the wrong way costs one dismissible dialog; being cautious the
diff --git a/idatui/formats.py b/idatui/formats.py
index 744276d..b2f784d 100644
--- a/idatui/formats.py
+++ b/idatui/formats.py
@@ -82,8 +82,19 @@ def needs_load_options(path: str) -> bool:
#: long is a worse answer than a short one plus free text. These are the targets
#: that actually turn up in firmware work, endianness spelled out because
#: getting it wrong is the most common way to end up with zero functions.
+#:
+#: EVERY NAME HERE IS VERIFIED against a real IDA by opening a scratch blob with
+#: ``-p<name>`` and reading back ``inf_get_procname()`` — see
+#: ``tools/verify_procs.py``. That is not ceremony: a wrong name is REJECTED by
+#: IDA (rc=4) with no useful message, which is the same silent-failure class this
+#: dialog exists to prevent. Two of the first twenty were wrong ("h8" and
+#: "sparc" are module filenames, not processor names), and the aliases people
+#: reach for first — arm64, aarch64, mips, m68k — are all invalid. Re-run the
+#: script before adding to this list.
PROCESSORS: tuple[tuple[str, str], ...] = (
- ("arm", "ARM / AArch64 — little-endian"),
+ # 'arm' covers AArch64 too; arm64/aarch64 are NOT valid -p names, so they
+ # live in the label where the filter can still find them.
+ ("arm", "ARM / AArch64 / arm64 — little-endian"),
("armb", "ARM — big-endian"),
("metapc", "x86 / x86-64"),
("mipsl", "MIPS — little-endian"),
@@ -91,7 +102,7 @@ PROCESSORS: tuple[tuple[str, str], ...] = (
("ppc", "PowerPC — big-endian"),
("ppcl", "PowerPC — little-endian"),
("sh4", "SuperH SH-4"),
- ("68k", "Motorola 68000"),
+ ("68k", "Motorola 68000 / m68k"),
("riscv", "RISC-V"),
("tricore", "Infineon TriCore"),
("xtensa", "Tensilica Xtensa (ESP32 etc.)"),
@@ -100,8 +111,9 @@ PROCESSORS: tuple[tuple[str, str], ...] = (
("tms320c6", "TI TMS320C6x DSP"),
("m32r", "Renesas M32R"),
("arc", "Synopsys ARC"),
- ("h8", "Renesas H8"),
- ("sparc", "SPARC"),
+ ("h8300", "Renesas H8/300"),
+ ("sparcb", "SPARC — big-endian"),
+ ("sparcl", "SPARC — little-endian"),
("s390", "IBM S/390"),
)
diff --git a/tests/test_formats.py b/tests/test_formats.py
index f88021c..ab72e8d 100644
--- a/tests/test_formats.py
+++ b/tests/test_formats.py
@@ -79,6 +79,35 @@ def main() -> int:
check("the processor list leads with the common targets",
[n for n, _ in PROCESSORS[:3]] == ["arm", "armb", "metapc"],
f"{[n for n, _ in PROCESSORS[:3]]}")
+
+ # 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
+ # output of tools/verify_procs.py; adding a processor without re-running it
+ # fails here on purpose.
+ VERIFIED = {
+ "arm", "armb", "metapc", "mipsl", "mipsb", "ppc", "ppcl", "sh4", "68k",
+ "riscv", "tricore", "xtensa", "avr", "z80", "tms320c6", "m32r", "arc",
+ "h8300", "sparcb", "sparcl", "s390",
+ }
+ offered = {n for n, _ in PROCESSORS}
+ check("every offered processor name is IDA-verified",
+ offered <= VERIFIED, f"unverified: {sorted(offered - VERIFIED)}")
+
+ # These are module FILENAMES or common aliases, not -p names. IDA refuses
+ # them; they were in the list until a real run said otherwise.
+ for wrong in ("h8", "sparc", "arm64", "aarch64", "mips", "m68k"):
+ check(f"{wrong!r} is not offered (IDA rejects it)", wrong not in offered)
+
+ # ...but someone WILL type them, so the labels have to carry the alias.
+ def finds(q):
+ ql = q.lower()
+ return [n for n, d in PROCESSORS if ql in n.lower() or ql in d.lower()]
+ check("typing 'arm64' still finds ARM", "arm" in finds("arm64"), f"{finds('arm64')}")
+ check("typing 'aarch64' still finds ARM", "arm" in finds("aarch64"))
+ check("typing 'm68k' still finds 68k", "68k" in finds("m68k"), f"{finds('m68k')}")
+ check("typing 'mips' finds both endiannesses",
+ set(finds("mips")) == {"mipsl", "mipsb"}, f"{finds('mips')}")
check("every processor entry has a human label",
all(n and d for n, d in PROCESSORS))
check("endianness is spelled out where it matters",
diff --git a/tools/verify_procs.py b/tools/verify_procs.py
new file mode 100644
index 0000000..8012931
--- /dev/null
+++ b/tools/verify_procs.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python3
+"""Check every name in ``formats.PROCESSORS`` against a real IDA.
+
+Run this before adding a processor to the list.
+
+A wrong ``-p`` name is not a soft failure: IDA refuses to open the database
+(rc=4) with nothing useful said, which is the same silent-failure class the load
+dialog exists to prevent. Offering a name that doesn't work would hand the user
+a dead end from inside the very UI meant to rescue them.
+
+Two of the first twenty entries were wrong — ``h8`` and ``sparc`` are module
+FILENAMES (procs/h8.so, procs/sparc.so), not processor names; the real ones are
+``h8300`` and ``sparcb``/``sparcl``. The aliases people reach for first
+(``arm64``, ``aarch64``, ``mips``, ``m68k``) are all invalid too, which is why
+they appear in the human labels instead, where the filter can still find them.
+
+ /usr/bin/python tools/verify_procs.py # needs idalib, not textual
+"""
+
+from __future__ import annotations
+
+import os
+import sys
+import tempfile
+
+sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+
+from idatui.formats import PROCESSORS # noqa: E402
+
+
+def main() -> int:
+ import idapro
+ idapro.enable_console_messages(False)
+ import ida_auto
+ import ida_ida
+
+ blob = bytes(range(256)) * 8
+ bad: list[tuple[str, int, str]] = []
+ for name, desc in PROCESSORS:
+ # A fresh directory each time: once a database exists IDA ignores the
+ # load switches, and every name after the first would "pass".
+ d = tempfile.mkdtemp()
+ path = os.path.join(d, "probe.bin")
+ with open(path, "wb") as f:
+ f.write(blob)
+ rc = idapro.open_database(path, run_auto_analysis=False, args=f"-p{name}")
+ got = ""
+ if rc == 0:
+ ida_auto.auto_wait()
+ got = ida_ida.inf_get_procname()
+ idapro.close_database(save=False)
+ ok = rc == 0 and got.lower() == name.lower()
+ print(f" {'ok ' if ok else 'BAD '} {name:<12} rc={rc} -> {got!r} {desc}")
+ if not ok:
+ bad.append((name, rc, got))
+
+ print(f"\n{len(PROCESSORS) - len(bad)}/{len(PROCESSORS)} verified")
+ for name, rc, got in bad:
+ print(f" {name}: rc={rc} procname={got!r}")
+ return 1 if bad else 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())