| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- #!/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))
|