aboutsummaryrefslogtreecommitdiffstats
path: root/tools/verify_procs.py
blob: 323bf1fdcddd589c6b9600b6f341e9fbe2c12fd4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#!/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()
        # "arm:ARMv7-A" selects a VARIANT: inf_get_procname() reports the base
        # module ("ARM"), so compare that and check the bitness separately —
        # the variant is only worth offering if it actually changes something.
        base = name.split(":", 1)[0]
        bits = ""
        if rc == 0:
            import ida_ida
            bits = f" bitness={ida_ida.inf_get_app_bitness()}"
        ok = rc == 0 and got.lower() == base.lower()
        print(f"  {'ok  ' if ok else 'BAD '} {name:<14} rc={rc} -> {got!r}{bits}   {desc}")
        if rc == 0:
            idapro.close_database(save=False)
        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())