在实模式下通过 gdb 访问 $api 失败
Acessing $eip via gdb fails in real mode
我正在尝试调试我正在使用 gdb 编写的 x86 引导加载程序。由于 gdb 似乎不能很好地处理 16 位实模式,所以我正在使用 gdb script 其他人为此目的编写的
我尝试调试的代码的最小示例如下所示(文件 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)
我执行以下命令从中创建可调试的 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_real_mode.txt
是链接脚本。
这实际上不起作用,因为脚本包含一个函数 compute_regs
,该函数将(除其他外)$rip
设置为 $cs * 16 + $eip
。我知道这样做是因为在实模式下内存是使用段+偏移寄存器寻址的,但 gdb 本身并不知道这一点。但是,compute_regs
中的以下 gdb 命令因“无效转换”而失败(似乎实际上对 $eip
的任何访问都以这种方式失败):
set $rip = ((((unsigned long)$cs & 0xFFFF) << 4) + ((unsigned long)$eip & 0xFFFF)) & $ADDRESS_MASK
为什么会这样,我该如何解决?我在 64 位 Linux 主机上使用 nasm 2.15.05
、qemu 5.1.0
和 gdb 9.2
。
脚本设计为在 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 可执行文件 运行ning 时使用 qemu-system-x86_64
,您可能 运行 会遇到更多问题。
我正在尝试调试我正在使用 gdb 编写的 x86 引导加载程序。由于 gdb 似乎不能很好地处理 16 位实模式,所以我正在使用 gdb script 其他人为此目的编写的
我尝试调试的代码的最小示例如下所示(文件 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)
我执行以下命令从中创建可调试的 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_real_mode.txt
是链接脚本。
这实际上不起作用,因为脚本包含一个函数 compute_regs
,该函数将(除其他外)$rip
设置为 $cs * 16 + $eip
。我知道这样做是因为在实模式下内存是使用段+偏移寄存器寻址的,但 gdb 本身并不知道这一点。但是,compute_regs
中的以下 gdb 命令因“无效转换”而失败(似乎实际上对 $eip
的任何访问都以这种方式失败):
set $rip = ((((unsigned long)$cs & 0xFFFF) << 4) + ((unsigned long)$eip & 0xFFFF)) & $ADDRESS_MASK
为什么会这样,我该如何解决?我在 64 位 Linux 主机上使用 nasm 2.15.05
、qemu 5.1.0
和 gdb 9.2
。
脚本设计为在 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 可执行文件 运行ning 时使用 qemu-system-x86_64
,您可能 运行 会遇到更多问题。