X86 通过gdb访问$eip在实际模式下失败

X86 通过gdb访问$eip在实际模式下失败,x86,gdb,nasm,ld,real-mode,X86,Gdb,Nasm,Ld,Real Mode,我正在尝试调试一个使用gdb编写的x86引导加载程序。因为gdb似乎不能很好地处理16位实模式,所以我使用了一个其他人为此编写的程序 我试图调试的代码的一个最小示例如下所示(文件asm/boot.asm): 我执行以下命令,从该文件以及复制到虚拟软盘映像开头的平面二进制文件创建可调试elf文件: nasm -i asm -f elf64 -g -F dwarf asm/boot.asm -o out/boot.o ld -Ttext=0x7c00 -melf_x86_64 out/boot.o

我正在尝试调试一个使用gdb编写的x86引导加载程序。因为gdb似乎不能很好地处理16位实模式,所以我使用了一个其他人为此编写的程序

我试图调试的代码的一个最小示例如下所示(文件
asm/boot.asm
):

我执行以下命令,从该文件以及复制到虚拟软盘映像开头的平面二进制文件创建可调试elf文件:

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
我在
qemu
下运行此图像,使用:

qemu-system-x86_64 -nographic -drive format=raw,file=imgs/os.flp,index=0,if=floppy -S -s &
并随附gdb:

gdb out/boot.elf \
    -ex "target remote localhost:1234" \
    -x gdbinit_real_mode.txt \
    -ex "break _start" \
    -ex "continue"
其中,
gdbinit\u real\u mode.txt
是链接脚本

这实际上不起作用,因为脚本包含一个函数
compute_regs
,该函数将
$rip
设置为
$cs*16+$eip
。我知道这样做是因为在实模式中,内存是使用段+偏移寄存器寻址的,但gdb本身并不知道这一点。但是,
compute_regs
中的以下gdb命令失败,并显示“无效强制转换”(实际上,任何对
$eip
的访问都会以这种方式失败):


set$rip=(((无符号长)$cs&0xFFFF)该脚本设计为在GDB以32位模式运行时使用。GDB采用64位代码,因为您使用的是64位ELF文件进行调试。由于这不是64位代码,您可以将NASM的命令行更改为使用
-f elf32
而不是
-f elf64
,然后使用LD选项
-melf_i386
而不是
-melf_x86_64
。这样做应该会生成一个32位ELF可执行文件,脚本应该可以运行

或者,如果要将该脚本与64位ELF一起使用,则可能必须将
$eip
更改为
$rip


我还建议在通过GDB调试32位代码时使用
qemu-system-i386
。如果在调试以16位模式运行的32位ELF可执行文件时使用
qemu-system-x86_64
,您可能会遇到更多问题。

@MichaelPetch:这的确是个问题,为i386构建可以工作,我将尝试调整脚本r x86_64也是。
gdb out/boot.elf \
    -ex "target remote localhost:1234" \
    -x gdbinit_real_mode.txt \
    -ex "break _start" \
    -ex "continue"
set $rip = ((((unsigned long)$cs & 0xFFFF) << 4) + ((unsigned long)$eip & 0xFFFF)) & $ADDRESS_MASK