build: a gdb target, because the tree had no source-level debugger

QEMU carries a gdb stub and exposing it costs two flags. `make qemu-gdb` holds
the machine before its first instruction and waits on port 26000; attach and
you get breakpoints, single-stepping and symbols in kernel code -- including
the early boot the crash screen cannot reach and the serial console has not
started for.

.gdbinit does the connecting, and documents the one confusing part rather than
leaving it to be rediscovered: at the reset vector a breakpoint on a kernel
symbol fails with "Cannot access memory at 0xffff8000...", because the kernel
links into the high half and nothing is mapped there until it enables paging.
Symbols resolve fine -- gdb knows kmain is main.c:32 -- the address just is not
addressable yet. Continue, let it boot, interrupt, and everything works.

This is for QEMU. On hardware with no serial port the tools are the on-screen
kernel log and PSOGM's QR code, which is what that QR code has always been for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
This commit is contained in:
Nexoniarz 2026-09-13 15:05:46 +02:00
commit 6c70040f57
2 changed files with 63 additions and 0 deletions

39
.gdbinit Normal file
View file

@ -0,0 +1,39 @@
# SchOS kernel debugging.
#
# gdb refuses to auto-load this unless the directory is trusted. Once, in
# ~/.gdbinit:
#
# add-auto-load-safe-path /home/nexoniarz/Dokumenty/Projects/SchOS
#
# Then: make qemu-gdb (one terminal)
# gdb build/kernel/kernel (another)
set confirm off
set architecture i386:x86-64
target remote 127.0.0.1:26000
# You are stopped at the reset vector, 0xfff0, with the firmware not yet run.
#
# A breakpoint on a kernel symbol will NOT take here, and the error is
# confusing rather than wrong:
#
# Cannot insert breakpoint 1. Cannot access memory at 0xffff800000104fc2
#
# The kernel links at 0xffff8000_00000000 and nothing is mapped there until it
# turns paging on. Symbols resolve -- gdb knows kmain is main.c:32 -- but the
# address is not addressable yet.
#
# So: `continue`, let the firmware and the loader run, and interrupt with ^C
# once SchOS is up. Every kernel symbol works from then on, because the high
# half is mapped and stays mapped.
#
# (gdb) continue
# ... SchOS boots ...
# ^C
# (gdb) break vt_switch
# (gdb) continue
#
# For the early boot itself -- entry.S, the long-mode switch, the handoff from
# the loader -- break on the *physical* address instead: the kernel is loaded
# at 1 MB, so `break *0x100000` catches the entry point before paging.
echo \n=== SchOS: stopped at reset. `continue`, then ^C once it boots. ===\n\n

View file

@ -1052,6 +1052,30 @@ OVMF_DRIVES = -drive if=pflash,format=raw,unit=0,readonly=on,file=$(OVMF_CODE) \
# whenever /dev/kvm exists -- see QEMU_ACCEL. This target differs only in # whenever /dev/kvm exists -- see QEMU_ACCEL. This target differs only in
# insisting on it, which is what you want when the point is to find out why the # insisting on it, which is what you want when the point is to find out why the
# host has no /dev/kvm rather than to quietly get a slow machine. # host has no /dev/kvm rather than to quietly get a slow machine.
# Source-level debugging, which the tree had no way to do.
#
# QEMU carries a gdb stub and it costs nothing to expose: -S holds the machine
# before its first instruction, -gdb waits for a connection. Attach and you get
# breakpoints, single-stepping and symbols in kernel code -- including the
# early boot the crash screen cannot reach and the serial console has not
# started for.
#
# terminal 1: make qemu-gdb
# terminal 2: gdb build/kernel/kernel
# (gdb) target remote :26000
# (gdb) break kmain
# (gdb) continue
#
# .gdbinit in the repo root does the connecting, so `gdb` alone is enough if
# gdb is allowed to load it -- see the file for the one line that needs.
GDBPORT ?= 26000
qemu-gdb: esp $(BUILD)/schfs.img $(OVMF_CODE) $(OVMF_VARS)
@echo "waiting for gdb on port $(GDBPORT) -- attach, then 'continue'"
$(QEMU) -serial mon:stdio $(QEMU_DISPLAY) $(QEMU_INPUT) $(QEMU_ACCEL) $(QEMU_NET) \
$(OVMF_DRIVES) -m 1G -smp 1 -machine pc-i440fx-9.2 \
-S -gdb tcp::$(GDBPORT)
qemu-kvm: esp $(BUILD)/schfs.img $(OVMF_CODE) $(OVMF_VARS) qemu-kvm: esp $(BUILD)/schfs.img $(OVMF_CODE) $(OVMF_VARS)
$(QEMU) -enable-kvm -serial mon:stdio $(QEMU_DISPLAY) $(QEMU_INPUT) $(QEMU_NET) -cpu host \ $(QEMU) -enable-kvm -serial mon:stdio $(QEMU_DISPLAY) $(QEMU_INPUT) $(QEMU_NET) -cpu host \
$(OVMF_DRIVES) -m 1G -smp $(SMP) -machine pc-i440fx-9.2 $(OVMF_DRIVES) -m 1G -smp $(SMP) -machine pc-i440fx-9.2