#!/usr/bin/env python3 # gdb script (run with: sudo gdb -p -batch -x /tmp/dumpctx.py) # Dumps the libcuda client wait-loop context: the ctx object's queue/entry # array (ctx+0x10 = count, ctx+0x18 = array of 0x28-byte entries) plus each # entry's target object, so the host emulation knows which tracker / # notification memory it must write to complete the wait. import gdb def r32(a): return int.from_bytes(gdb.selected_inferior().read_memory(a, 4).tobytes(), "little") def r64(a): return int.from_bytes(gdb.selected_inferior().read_memory(a, 8).tobytes(), "little") def rmem(a, n): return gdb.selected_inferior().read_memory(a, n).tobytes() out = gdb.execute("info proc mappings", to_string=True) base = None for line in out.splitlines(): if "libcuda" in line and "r-xp" in line: f = line.split() # gdb format: start end size offset objfile (offset is field 3) base = int(f[0], 16) - int(f[3], 16) print("MAPLINE " + line.strip()) break if base is None: print("NO_LIBCUDA_BASE") raise SystemExit LO = base + 0x23f700 HI = base + 0x23fd10 print("LIBBASE=0x%x SPIN=[0x%x,0x%x)" % (base, LO, HI)) found = None for t in gdb.selected_inferior().threads(): t.switch() f = gdb.newest_frame() for i in range(14): if f is None: break try: pc = int(f.pc()) except Exception: pc = 0 if LO <= pc < HI: try: rb = int(f.read_register("rbx")) & 0xffffffffffffffff except Exception: rb = None found = (t, pc, rb, i) break f = f.older() if found: break if not found: print("NO_SPIN_FRAME") for t in gdb.selected_inferior().threads(): t.switch() top = "" try: f0 = gdb.newest_frame() top = f0.name() or "" except Exception: pass print("== tid=%s top=%s" % (t.num, top)) try: print(gdb.execute("bt 6", to_string=True).rstrip()) except Exception as e: print("bt_fail %s" % e) try: print(gdb.execute("info registers rdi rsi rdx rax rip", to_string=True).rstrip()) except Exception as e: print("regs_fail %s" % e) if "ioctl" in top or "poll" in top: try: rax = int(gdb.parse_and_eval("$rax")) & 0xffffffffffffffff rdx = int(gdb.parse_and_eval("$rdx")) & 0xffffffffffffffff print("ARG64@0x%x=%s" % (rdx, rmem(rdx, 0x40).hex())) except Exception as e: print("arg_fail %s" % e) print("REGS_HINT") try: print(gdb.execute("thread apply all info registers rip rbx r13 r12", to_string=True).rstrip()) except Exception as e: print("regs_fail %s" % e) print("DONE_NOSPIN") raise SystemExit t, pc, ctx, depth = found print("SPINNER tid=%s pc=0x%x depth=%d ctx=0x%x" % (t.num, pc, depth, ctx)) if ctx is None: raise SystemExit try: count = r32(ctx + 0x10) ent = r64(ctx + 0x18) ctx0 = r64(ctx) gpu = r64(ctx0 + 8) print("CTX=0x%x count=%d entries=0x%x ctx0=0x%x gpu=0x%x gpu+3de0=0x%x" % (ctx, count, ent, ctx0, gpu, r64(gpu + 0x3de0))) except Exception as e: print("CTX_READ_FAIL %s" % e) raise SystemExit n = min(count, 32) for i in range(n): e = ent + i * 0x28 try: typ = r32(e + 0) p = r64(e + 8) v = r32(e + 0x10) x18 = r64(e + 0x18) x20 = r64(e + 0x20) except Exception as ex: print("e%d @0x%x UNREADABLE %s" % (i, e, ex)) continue line = "e%d @0x%x type=%d p=0x%x v=0x%x x18=0x%x x20=0x%x" % (i, e, typ, p, v, x18, x20) try: hdr = rmem(p, 0x20) line += " p32=%d p64=0x%x p[0:0x20]=%s" % ( int.from_bytes(hdr[0:4], "little"), int.from_bytes(hdr[0:8], "little"), hdr.hex()) except Exception: line += " p_UNREADABLE" if typ == 1: try: trk = rmem(p + 0x9410, 0x40) vals = [int.from_bytes(trk[o:o+8], "little") for o in (0, 8, 0x10, 0x18, 0x20, 0x28, 0x30, 0x38)] line += " TRK=%s q=%s" % (trk.hex(), ",".join("0x%x" % x for x in vals)) except Exception: line += " TRK_UNREADABLE" print(line) print("DONE_SPIN") import os if os.environ.get("CTX_POKE"): inf = gdb.selected_inferior() npk = 0 for i in range(n): e = ent + i * 0x28 try: if r32(e) != 1: continue p = r64(e + 8) inf.write_memory(p + 0x9410 + 0x10, (1).to_bytes(8, "little")) inf.write_memory(p + 0x9410 + 0x18, (1).to_bytes(8, "little")) npk += 1 except Exception as ex: print("poke e%d fail %s" % (i, ex)) print("POKED=%d" % npk)