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.