通过 GDB 我们可以跟踪程序的执行,
并且能够看到寄存器的状态,
但是,上面这种调试方式,没有和代码对应起来,以致于我们无法直观地知道 CPU 当前执行的是哪条指令。
下面我们就让调试和代码对应起来。
在这之前,我们需要做两件事情
ld -Ttext=0x7c00
链接时使用 -Ttext 选项指定代码段的起始地址。
as -g
汇编时使用 -g 选项,添加调试信息
Makefile
OUTPUT=output
all: clean
as --32 -g -o ${OUTPUT}/mbr.o mbr.S # 汇编
ld -m elf_i386 -Ttext=0x7c00 ${OUTPUT}/mbr.o -o ${OUTPUT}/mbr.elf # 链接
objcopy -O binary ${OUTPUT}/mbr.elf ${OUTPUT}/mbr.bin # elf --> bin
dd if=${OUTPUT}/mbr.bin of=${OUTPUT}/mbr.img conv=notrunc # bin --> image
clean:
-rm ${OUTPUT}/*
run:
qemu-system-i386 -daemonize -m 128M -drive file=${OUTPUT}/mbr.img,index=0,media=disk,format=raw \
-s -S
可以看到,这样调试就和代码关联起来了。
(gdb) b *0x7c00
Breakpoint 1 at 0x7c00: file mbr.S, line 4.
Breakpoint 1, _start () at mbr.S:4
4 jmp _start=
其实,指定代码入口地址 和 添加调试信息 对本工程运行没有任何作用,这两个信息只体现在 elf 文件中,这种格式的文件是给操作系统使用的(在本篇文章中是给 gdb 读取符号信息用的,使之能够将调试和代码对应,即使没有 elf 文件,gdb 照样可以调试程序,如前言中的调试方式)。而我们编写的 MBR 是直接给 CPU 运行的,它是一个 bin 文件。关于 elf 和 bin 的区别,我们在后续的文章中单独介绍。