I'm trying to debug an x86 bootloader I am writing using gdb. Since gdb doesn't seem to handle 16-bit real mode very well I am using a gdb script someone else wrote for this purpose.
A minimal example of the code I'm trying to debug looks like this (file asm/boot.asm
):
bits 16
global _start
_start:
; zero DS
xor ax, ax
mov ds, ax
; TODO
sleep:
jmp sleep
times 510-($-$$) db 0 ; zero out rest of section
dw 0xAA55 ; add boot signature (needed by qemu)
I execute the following commands to create a debuggable elf file from this as well as a flat binary that I copy to the beginning of a dummy floppy disk image:
nasm -i asm -f elf64 -g -F dwarf asm/boot.asm -o out/boot.o
ld -Ttext=0x7c00 -melf_x86_64 out/boot.o -o out/boot.elf
objcopy -O binary out/boot.elf out/boot.img
dd if=/dev/zero of=imgs/os.flp bs=512 count=1000
dd if=out/boot.img of=imgs/os.flp bs=512 count=1 conv=notrunc
I run this image under qemu
with:
qemu-system-x86_64 -nographic -drive format=raw,file=imgs/os.flp,index=0,if=floppy -S -s &
And attach gdb with:
gdb out/boot.elf \
-ex "target remote localhost:1234" \
-x gdbinit_real_mode.txt \
-ex "break _start" \
-ex "continue"
where gdbinit_real_mode.txt
is the linked script.
This doesn't really work, because the script contains a function compute_regs
that set (among others) $rip
to $cs * 16 + $eip
. I understand that this is done because in real mode memory is addressed using segment + offset registers but gdb is not aware of this by itself. However, the following gdb command inside compute_regs
fails with "Invalid cast" (and it seems that actually any access to $eip
fails in this manner):
set $rip = ((((unsigned long)$cs & 0xFFFF) << 4) + ((unsigned long)$eip & 0xFFFF)) & $ADDRESS_MASK
Why is that and how can I fix it? I'm using nasm 2.15.05
, qemu 5.1.0
and gdb 9.2
on a 64 bit Linux host.