.gdbinit.tmpl-riscv
About this file
A template for .gdbinit, the file of commands gdb (the GNU debugger) runs when it starts. It sets gdb
up to debug the xv6 kernel running inside QEMU: connect to QEMU, load the kernel’s
symbols, and choose settings that suit RISC-V.
You do not use this file directly. The Makefile copies it to .gdbinit, replacing the
port number 1234 with your own GDBPORT (Makefile:185). The workflow is:
- In one terminal,
make qemu-gdb(Makefile:188). This generates.gdbinit, then starts QEMU with-S(every hart paused before its first instruction) and a gdb server on your port. Nothing runs yet. - In a second terminal, in the same directory, run a RISC-V gdb:
${TOOLPREFIX}gdb(TOOLPREFIXis your RISC-V toolchain’s prefix, see Tour 1: From make qemu to a disk image and a kernel), orgdb-multiarchon Debian/Ubuntu/WSL. It reads.gdbinit, connects, and stops at address0x1000, where QEMU’s built-in boot code starts. - Set breakpoints (
b main,b kvminithart) andcto continue.
Recent versions of gdb refuse to run a .gdbinit found in the current directory unless
you allow it; gdb prints the exact add-auto-load-safe-path line to put in your own
~/.gdbinit.
The gdb start-up commands
Six commands, run in order. The first two prepare gdb, the third connects to QEMU, the fourth loads symbols, and the last two adjust how gdb displays and sets breakpoints. Each has its own line note.
Note what is not here: no load command. gdb does not send the kernel to the
machine; QEMU already loaded kernel/kernel into memory with its -kernel option.
gdb only needs the file’s symbols and debug information to translate addresses into
function names and C lines.
Turn off “Are you sure?” questions, such as the one gdb asks when you quit while still connected to a running target. Handy when you restart debugging sessions often.
Tell gdb the machine is 64-bit RISC-V before it connects, so it interprets registers and instructions correctly. Without it, a multi-architecture gdb may guess wrong until it has read the symbol file.
Connect to QEMU’s built-in gdb server over TCP on this computer (127.0.0.1). In the
generated .gdbinit, 1234 has been replaced by your GDBPORT, which matches
the port in QEMU’s -gdb tcp::PORT option (QEMUGDB). From here on, gdb
commands such as “stop”, “step” or “read register” are carried out by QEMU on the
emulated machine; each hart appears to gdb as a thread (info threads).
Read the symbol table and debug information from kernel/kernel, the ELF file the
Makefile linked (Makefile:93). That is what lets gdb show main instead of
0x80000xxx, and the C line it is on. The kernel was compiled with -ggdb and frame
pointers for this purpose (-g / -ggdb / -gdwarf-2).
Each time execution stops, show the disassembly of the next instruction, but only when
gdb cannot show a C source line, for example in kernel/entry.S or in QEMU’s boot
code at 0x1000. In C code you still see the source line. (on would always show
both, off, the default, never shows the disassembly.)
Always use the 2-byte compressed breakpoint (c.ebreak) rather than the 4-byte
ebreak. The kernel is compiled with the compressed “C” extension
(rv64gc), so many instructions are only 2 bytes long. On a target
where gdb writes the breakpoint into memory, a 4-byte breakpoint on a 2-byte
instruction would also overwrite the next instruction; the default, auto, avoids
that by looking at the instruction at the address. Under QEMU it makes little
difference: QEMU’s gdb server implements breakpoints itself without changing guest
memory. The line is harmless and pins the behavior regardless of gdb version.