import gdb, sys inf = gdb.selected_inferior() pid = inf.pid base = None for line in open("/proc/%d/maps" % pid): if "libnvidia-eglcore.so.570.144" in line: parts = line.split() lo, hi = [int(x, 16) for x in parts[0].split('-')] off = int(parts[2], 16) if off == 0: base = lo break if base is None: print("EGLBASE not found") gdb.execute("detach") sys.exit(1) print("EGLBASE 0x%x PID %d" % (base, pid)) def rd(a, n): return inf.read_memory(a, n).tobytes() def u32(a): return int.from_bytes(rd(a, 4), 'little') def u64(a): return int.from_bytes(rd(a, 8), 'little') state = {"out": 0, "E": 0, "S": 0, "armed150": False, "armedS": False, "bt_done": False, "post_done": False} class B(gdb.Breakpoint): def __init__(self, spec, fn, limit=100000): super().__init__(spec, internal=False) self.fn = fn; self.limit = limit; self.hits = 0 self.silent = True def stop(self): self.hits += 1 try: stop = bool(self.fn(self)) except Exception as e: print("ERR@0x%x %s" % (int(gdb.parse_and_eval("$pc")), e)) stop = False return stop or self.hits >= self.limit def a97(bp): rbp = int(gdb.parse_and_eval("$rbp")) r14 = int(gdb.parse_and_eval("$r14")) out = u64(rbp+0x10) state["out"] = out q = rd(out, 0x28) rec_ptr = int.from_bytes(q[8:16], 'little') print("A97 out=0x%x count=%d f10=%08x f20=%08x f24=%08x recp=0x%x" % (out, int.from_bytes(q[0:4], 'little'), int.from_bytes(q[0x10:0x14], 'little'), int.from_bytes(q[0x20:0x24], 'little'), int.from_bytes(q[0x24:0x28], 'little'), rec_ptr)) if rec_ptr and rec_ptr > 0x1000: try: rec = rd(rec_ptr, 16) print("A97 rec=%08x %08x %08x %08x" % tuple(int.from_bytes(rec[i*4:i*4+4], 'little') for i in range(4))) except Exception as e: print("A97 rec-unreadable", e) return False def post(bp): if state["post_done"]: return False state["post_done"] = True rsp = int(gdb.parse_and_eval("$rsp")) q = rd(rsp, 0x40) print("POST rsp=0x%x out=0x%x" % (rsp, state["out"])) for i in range(0, 0x40, 8): v = int.from_bytes(q[i:i+8], 'little') print("POST rsp+%02x = 0x%016x" % (i, v)) if state["out"]: oq = rd(state["out"], 0x40) for i in range(0, 0x40, 8): v = int.from_bytes(oq[i:i+8], 'little') print("POST out+%02x = 0x%016x" % (i, v)) return False def t150(bp): rbp = int(gdb.parse_and_eval("$rbp")) if not state.get("vt_done"): state["vt_done"] = True rdi = u64(rbp+0x148) vt = u64(rdi) if rdi else 0 m118 = u64(vt+0x118) if vt else 0 m170 = u64(vt+0x170) if vt else 0 print("VT E148=0x%x vt=0x%x m118=0x%x m170=0x%x" % (rdi, vt, m118, m170)) if not state.get("eng_done"): state["eng_done"] = True n = u32(rbp+8) ids = [u32(rbp+0x54+i*4) for i in range(min(n,8))] print("ENG n=%d ids=%s" % (n, ",".join("0x%x" % x for x in ids))) # cfg area pointer is rip-relative 0x2019320 at runtime = base + (0x2019320 - 0) # eglcore base == min load addr; file offset == vaddr for the .text; data seg present? try: vals = rd(base + 0x2019320 + 0xf80, 0xe0) for i in range(0, 0xe0, 0x10): v4 = [int.from_bytes(vals[i+j:i+j+4],'little') for j in (0,4,8,12)] print("CFG +0x%03x = %s" % (0xf80+i, ["0x%x"%x for x in v4])) except Exception as e: print("CFG dump err", e) v = u32(rbp+0x150) b38 = rd(rbp+0x1b038, 1)[0]; b39 = rd(rbp+0x1b039, 1)[0] p = u64(rbp+0x1b030); s8 = u64(p+8) if p else 0 print("T150 E=0x%x v=0x%08x b038=%d b039=%d p=0x%x p8=0x%x" % (rbp, v, b38, b39, p, s8)) if not state["bt_done"]: state["bt_done"] = True print(gdb.execute("bt 8", to_string=True)) if not state["armed150"]: state["armed150"] = True state["E"] = rbp try: gdb.execute("frame 1") S = int(gdb.parse_and_eval("$rbp")) gdb.execute("frame 0") print("S=0x%x S120=%08x S128=%08x" % (S, u32(S+0x120), u32(S+0x128))) state["S"] = S except Exception as e: print("S capture err", e) S = 0 W("*(unsigned int *)0x%x" % (rbp+0x150), rbp, 0x150, 4, "W150") W("*(unsigned char *)0x%x" % (rbp+0x1b038), rbp, 0x1b038, 1, "W038") W("*(unsigned char *)0x%x" % (rbp+0x1b039), rbp, 0x1b039, 1, "W039") if S: W("*(unsigned int *)0x%x" % (S+0x120), S, 0x120, 4, "W120") print("WATCHES armed E=0x%x S=0x%x" % (rbp, S)) return False def sl(bp): rbp = int(gdb.parse_and_eval("$rbp")) print("SLOOP S=0x%x S120=%08x" % (rbp, u32(rbp+0x120))) if not state["armedS"]: state["armedS"] = True W("*(unsigned int *)0x%x" % (rbp+0x120), rbp, 0x120, 4, "W120") print("W120 armed on S+0x120") return False class W(gdb.Breakpoint): def __init__(self, spec, E, off, size, tag): super().__init__(spec, type=gdb.BP_WATCHPOINT) self.silent = True; self.E = E; self.off = off; self.size = size self.tag = tag; self.hits = 0 def stop(self): self.hits += 1 cur = int.from_bytes(rd(self.E+self.off, self.size), 'little') pc = int(gdb.parse_and_eval("$pc")) print("%s v=0x%x pc=0x%x (rel 0x%x)" % (self.tag, cur, pc, pc-base)) if self.hits <= 8: print(gdb.execute("bt 10", to_string=True)) return self.hits >= 400 B("*0x%x" % (base+0xecd43d), a97, 220) B("*0x%x" % (base+0xd5e4d3), post, 100000) B("*0x%x" % (base+0xd5e449), t150, 220) B("*0x%x" % (base+0xd6ef76), sl, 240) try: gdb.execute("continue") except gdb.error as e: print("CONT-ERR %s" % e) print("TRACE-END") try: gdb.execute("detach") except gdb.error as e: print("DETACH-ERR %s" % e) gdb.execute("quit")