Skip to content

winegdb ​

Tool tools/live/winegdb.py (program, The running game). Attach to a program running under Wine or CrossOver through winedbg's GDB server, read its registers and memory, wait at breakpoints, and always detach so the program runs on.

Run it with ./gameframe run winegdb ARGS or python3 tools/live/winegdb.py. Source on GitHub.

Its own help ​

This is the help text in the tool itself, as ./gameframe tool winegdb prints it.

text
Attach to a program running under Wine or CrossOver through winedbg's GDB server, read its
registers and memory, wait at breakpoints, and always detach so the program runs on.

Usage:
  winegdb.py procs                          WHERE
  winegdb.py regs   TARGET [--thread ID] [--all-threads]
  winegdb.py read   TARGET ADDR LEN [--out FILE]
  winegdb.py break  TARGET ADDR [ADDR ...] [--hits N] [--timeout S] [--dump WHAT:LEN ...]
  winegdb.py watch  TARGET ADDR [--len N] [--hits N] [--timeout S] [--dump WHAT:LEN ...]

  WHERE     --bottle NAME (a CrossOver bottle) or --prefix DIR (a WINEPREFIX), and
            --wine PATH when wine is not CrossOver's or not on PATH
  TARGET    --pid PID or --name EXE (a Windows process id in hex as `procs` prints it, or the
            executable's name), with WHERE; or --connect HOST:PORT for a GDB server that is
            already listening (winedbg --gdb, QEMU -gdb, VMware's debugStub, 86Box)
  ADDR      a number: 0x140118340. The game's own addresses from Ghidra work as they are when
            Wine loads it at its preferred base; `read TARGET 0x140000000 2` shows "MZ" then
  break     waits at code addresses. winedbg refuses software breakpoints (Z0), so the tool
            writes an int3 byte itself, moves rip back onto the address at a hit, and puts the
            original byte back before it detaches
  watch     waits for a write to ADDR (--len 1, 2, 4 or 8 bytes, default 4), with the CPU's
            debug registers, in every thread that exists at the attach. The registers are
            those after the writing instruction. Under Rosetta it never fires: see Pitfalls
  --hits    stop after N hits (default 1); each hit prints the registers and the dumps
  --timeout seconds to wait for each hit (default 30); then the program is stopped, the
            breakpoints are removed and the tool detaches
  --dump    memory to print at each hit: ADDR:LEN, or REG:LEN for the memory a register points
            at, or REG+OFF:LEN (rcx+0x40:16)
  --json    one JSON object per line instead of text
  --all-regs  text output with the x87, SSE and control registers too (JSON always has them)

Output: text, or JSON lines with --json. `regs` prints the stopped thread's registers. `read`
prints a hex dump or writes the bytes to --out.

How it works: `wine winedbg --gdb --no-start PID` attaches winedbg to the process and starts a
GDB server on a free port; this tool reads the port from winedbg's output and talks to it with
gdbremote.py. Attaching stops the program until the tool detaches.

Pitfalls, from Banished under CrossOver 26.3 on Apple silicon (docs/knowhow/winedbg-gdb-server.md):
- Ending winedbg while it is attached ends the program. This tool never kills it; it detaches,
  and winedbg then exits by itself. A winedbg that stays is reported, with its process id.
- A breakpoint left set when the client goes away stays in the code as an int3. The tool
  removes its breakpoints on every exit path, including Ctrl-C and a timeout.
- A bare `c` resumes only one thread there, and the game stays frozen. gdbremote.py sends
  `vCont;c`, which resumes them all.
- winedbg answers Z0 (a software breakpoint) with an empty reply: not supported. It accepts
  hardware breakpoints (Z1) and watchpoints (Z2 to Z4), but under Rosetta they never fired,
  and the game ended soon after each try. `watch` is for winedbg on an x86 host, or for a
  stub given with --connect.
- winedbg's module list (qXfer:libraries) has empty names and the Mac's own library
  addresses, so neither this tool nor lldb lists modules. Use the addresses from Ghidra.
- Started from Steam, Banished shows its launcher first, in the same executable. The launcher
  runs none of the game's frame loop, so a breakpoint there waits for nothing.

What it does not do: write memory or registers, start a program under the debugger, or 32-bit programs' addresses in a 64-bit Wine beyond what winedbg itself gives.

Code: Apache 2.0. Content: CC0 1.0. No game material: bring your own copy.