#!/usr/bin/env python3.12 """Symbolize mint hook DLL crash RVAs. Usage: python3.12 symbolize_hook.py [rva ...] RVAs are relative to the DLL's image base (e.g. from a wine/proton crash line "X3DAudio1_7.dll + 0x521F15"). The script prints the containing symbol for each RVA. Run it against a debug-symbol hook build (see README "Symbol build"), NOT the stripped release artifact. Optional env: HOOK_IMAGE_BASE override base (default 0x180000000) """ import bisect, os, re, subprocess, sys def main(): if len(sys.argv) < 3: print(__doc__); sys.exit(1) dll = sys.argv[1] base = int(os.environ.get("HOOK_IMAGE_BASE", "0x180000000"), 16) rv = [int(x, 16) for x in sys.argv[2:]] sym_out = subprocess.run( ["x86_64-w64-mingw32-nm", "-n", "-C", dll], capture_output=True, text=True) syms = [] for line in sym_out.stdout.splitlines(): m = re.match(r"^([0-9a-f]+) [TtDdWw] (.+)$", line) if m: a = int(m.group(1), 16) if a >= base: syms.append((a, m.group(2).strip())) if not syms: print("no symbols found — is this a debug build? (see README)"); sys.exit(2) addrs = [a for a, _ in syms] syms.sort() for r in rv: va = base + r i = bisect.bisect_right(addrs, va) - 1 if i < 0: print(f"+{r:06x} NOT FOUND"); continue fa, name = syms[i] leaf = subprocess.run( ["x86_64-w64-mingw32-addr2line", "-Cfie", dll, hex(va)], capture_output=True, text=True).stdout leaf_frames = [l for l in leaf.splitlines() if "at " in l] print(f"=== +{r:06x} (va {va:#x}) ===") print(f" container: {name[:160]}{'…' if len(name) > 160 else ''}") print(f" +{va - fa:#x} into symbol") if leaf_frames: print(f" leaf: {leaf_frames[0][:160]}") # outermost non-inlined frame if leaf_frames: print(f" outer: {leaf_frames[-1][:160]}") print() if __name__ == "__main__": main()