Peter
Peter

Reputation: 3164

Acessing $eip via gdb fails in real mode

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.

Upvotes: 2

Views: 267

Answers (1)

Michael Petch
Michael Petch

Reputation: 47593

The script was designed to be used while GDB is running in 32-bit mode. GDB is assuming 64-bit code because you are using a 64-bit ELF file to debug from. Since this isn't 64-bit code you could change NASM's command line to use -f elf32 instead of -f elf64 and then use the LD option -melf_i386 instead of -melf_x86_64. Doing this should generate a 32-bit ELF executable and the script should work.

Alternatively if you want to use that script with a 64-bit ELF you will probably have to changed $eip to $rip.

I would also recommend then using qemu-system-i386 when debugging 32-bit code via GDB. You may run into more issues if you use qemu-system-x86_64 when trying to debug 32-bit ELF executables running in 16-bit mode.

Upvotes: 3

Related Questions