#!/usr/bin/env python3 """fcml_vs_objdump.py -- differential test between two 16-bit disassemblors. Compares the *length of the first instruction* over many byte windows. A length disagreement is the sharpest possible signal that the two tools read the ModRM/displacement/SIB fields differently, because if the lengths agree the tools walked the same opcode table. Usage: fcml_vs_objdump.py [trials] [--seed N] """ import os import random import re import subprocess import sys import tempfile RE_LEN = re.compile(r"^\s*Instruction code length:\s*(\d+)\s*$", re.M) RE_ADDR = re.compile(r"^\s*([0-9a-f]+):\t", re.M) def fcml_first_len(win): hexs = "".join("%02X" % b for b in win) out = subprocess.run(["fcml-disasm", "-m16", "-rh", "-rz", "0x" + hexs], capture_output=True, text=True) if out.returncode != 0: return None m = RE_LEN.search(out.stdout) return int(m.group(1)) if m else None def objdump_first_len(win, tmp): with open(tmp, "wb") as f: f.write(win) out = subprocess.run(["objdump", "-D", "-b", "binary", "-m", "i8086", tmp], capture_output=True, text=True) if out.returncode != 0: return None addrs = [int(a, 16) for a in RE_ADDR.findall(out.stdout)] if not addrs: return None if len(addrs) == 1: # single instruction covering the whole window return len(win) return addrs[1] - addrs[0] def main(argv): trials = 3000 seed = 1 args = [a for a in argv[1:]] if "--seed" in args: i = args.index("--seed") seed = int(args[i + 1]) del args[i:i + 2] if args: trials = int(args[0]) rnd = random.Random(seed) tmp = os.path.join(tempfile.gettempdir(), "diffprobe.bin") agree = 0 one_err = 0 diffs = [] for t in range(trials): n = rnd.choice([4, 6, 8, 10, 12, 15]) win = bytes(rnd.randrange(256) for _ in range(n)) a = fcml_first_len(win) b = objdump_first_len(win, tmp) if a is None or b is None: one_err += 1 continue if a == b: agree += 1 else: diffs.append((win.hex(" ").upper(), a, b)) total = agree + len(diffs) print("seed=%d trials=%d compared=%d agree=%d (%d skipped: one tool " "rejected the input) disagreements=%d" % (seed, trials, total, agree, one_err, len(diffs))) for h, a, b in diffs[:25]: print(" %-32s fcml=%-3s objdump=%-3s" % (h, a, b)) return 1 if diffs else 0 if __name__ == "__main__": sys.exit(main(sys.argv))