gdbforge — why another debugger UI?
If you spend your days on embedded Linux boards, Zynq MPSoC, STM32, or kernel kgdb, you already know the pattern: one terminal for GDB, another for minicom, a third for gdbserver logs, and an editor somewhere else. gdbforge is a project I built to keep the debugger’s power in the TTY while adding a Vim-inspired workspace — source, GDB console, IO, threads, call stack, and breakpoints in one keyboard-driven session.
It is not trying to replace your IDE. It is a cgdb-like terminal front-end for GDB (and optionally Delve for Go), extended with Lua scripts that automate the boring bring-up steps: spawn J-Link or OpenOCD, deploy to a board over scp, start gdbserver, or break into kgdb over UART.
In this article you’ll find:
- What gdbforge is and who it is for
- Where program I/O goes: internal
:b io pane vs external terminal - The four Lua workflow families: embedded Linux, MPSoC, STM32, kernel kgdb
- Links to the full documentation (with copy-paste recipes)
Quick start (PC)
git clone https://github.com/yairgd/gdbforge.git
cd gdbforge
go build -o bin/gdbforge ./cmd/gdbforge
gcc -O0 -g -o hello hello.c # or your cross-built ELF
./bin/gdbforge ./hello
Inside the app: :help for the manual, n / s / c to step, :quit to exit.
Program I/O — internal tty vs external terminal
This matters for every user-space debug session — local or on a board.
| Where stdio goes | When to use | How |
|---|
| Internal IO pane | Simple printf, line input | Default — :b io (alias :b output) |
| External terminal | Full TUI, curses, flood printing | :lua external_tty, :lua terminal_debug, or :set inferior-tty |
| gdbserver window | Remote or local RSP debug | :lua remotegdb (board) or :lua gdbserver_tui (same PC) |
When stdio is routed externally, :b io shows a note only — you type in the other window. Switch back with :set inferior-tty internal.
export GDBFORGE_TERMINAL=mate-terminal # or kitty, xterm, …
./bin/gdbforge ./my_tui
:lua terminal_debug ./my_tui run
Full write-up: Embedded Linux app debug guide
Scripts live under lua/ in the repo. Copy what you need into .gdbforge/lua/, then run :lua <name> (Tab completes). Command names are the script basename — unchanged after install.
1. Embedded Linux user-space — :lua remotegdb
Deploy your binary to the board (scp when MD5 differs), start gdbserver over SSH in an external terminal, attach with target remote.
mkdir -p .gdbforge/lua
cp -r lua/embedded/remotegdb .gdbforge/lua/
export GDBFORGE_REMOTE_HOST=192.168.20.50
./bin/gdbforge ./hello
:lua remotegdb
:lua remotegdb ./hello 192.168.20.50 1234
📖 Embedded Linux debug guide
2. Zynq UltraScale+ MPSoC (Cortex-A53 / R5) — J-Link & OpenOCD
Bare-metal load, OpenAMP attach, and A53 kernel workflows. Scripts spawn JLinkGDBServer or OpenOCD + Digilent HS2 in the background; gdbforge stays responsive.
cp -r lua/mpsoc/cortex_r5 .gdbforge/lua/
export GDBFORGE_JLINK=/opt/JLink_Linux_V914a_x86_64/JLinkGDBServer
./bin/gdbforge ./your_app.elf
:lua r5_baremetal_jlink
Examples: a53_baremetal_jlink, r5_openamp_jlink, OpenOCD variants.
📖 MPSoC debug guide
Short, self-contained scripts (~100 lines) for STM32F405 — easy to copy and edit for other F4 parts.
cp -r lua/stm32/stm32f405 .gdbforge/lua/
./bin/gdbforge ./build/firmware.elf
:lua stm32f405_jlink # J-Link SWD
:lua stm32f405_stlink # ST-Link + OpenOCD
📖 STM32 debug guide
4. Linux kernel kgdb — UART, kdmx, Ethernet
One-shot UART break-in with :lua kgdb_kdmx, module symbols with lx-symbols, or Ethernet kgdboe with :lua kgdb_net. This is the workflow behind the kernel demo screencasts in the repo.
cp -r lua/kernel/kgdb_common lua/kernel/kgdb_kdmx .gdbforge/lua/
export GDBFORGE_KGDB_UART=/dev/ttyUSB0
export GDBFORGE_KGDB_VMLINUX=/path/to/vmlinux
./bin/gdbforge -g gdb /path/to/vmlinux
:lua kgdb_kdmx
📖 Kernel kgdb guide
What I like about this design
- Keyboard-first — normal / insert / command modes, pane splits,
:layout wide - Lua as glue — not a monolithic IDE; each workflow is a small script you can edit
- Same UI for GDB and Delve —
-g dlv for Go, :lua dlv_ext_port for external Delve TTY - Probe in background — J-Link / OpenOCD spawn via
gdbforge.spawn; Code pane stays usable
If you have used cgdb or live in the terminal during bring-up, the layout should feel familiar — with more structure for threads, breakpoints, and automation.
Documentation & source
Platform guides (good starting points):
Closing
gdbforge started as a way to stop juggling terminals during embedded bring-up. The Lua catalog grew organically — remotegdb for boards, MPSoC/STM32 probe scripts, kgdb for kernel work — each in its own directory with documentation and YAML meta for the docs site.
If you try it on a board or probe you care about, issues and PRs are welcome on GitHub. And if a workflow you use every week is missing, the intended extension point is simple: copy an existing script, edit the env defaults at the top, and :lua it.
Related on this blog: Python script plug-in to GDB · ZynqMP R5 RemoteProc notes · Debug Linux kernel with QEMU