调试PIE内核时GDB无法在断点处停止的问题咨询
问题场景
我编译位置独立(PIE)内核的命令如下:
gcc -Og -g -Werror -nostdlib -o kernel.o -c kernel/main.c gcc -nostdlib -ffreestanding -e kmain -o kernel.elf kernel.o
通过QEMU启动引导加载程序:
qemu-system-x86_64 -s -S -bios /usr/share/ovmf/OVMF.fd -drive file=/os.img,format=raw
在GDB中执行以下操作后,断点并未在kmain处停止,程序直接继续运行:
target remote :1234 symbol-file kernel.elf break kmain continue layout next
引导加载程序中调用kmain的代码:
int (*kmain)(void*) = (int(*)(void*)) (buf + elf->entry); stat = uefi_call_wrapper(BS->ExitBootServices, 2, IH, mapkey); kmain(&bootinfo);
其中buf是kernel.elf文件加载到内存的起始位置,kmain的定义:
int kmain(Bootinfo *Bootinfo) { int x = 1; int y = x; return 0; }
有人提示:
如果你的内核是位置独立或可重定位的,且引导加载程序能处理其运行时需求,你需要告知GDB内核加载到了不同的基地址。
核心疑问
- 如何告知GDB内核的实际加载基地址?
- 此处的基地址是否为
buf?
补充:GDB中反汇编kmain的结果显示,所有指令均使用相对rip的寻址方式,符合PIE程序的特征。
解答
关于基地址的确认
是的,此处的实际加载基地址就是buf——也就是你在引导加载程序中把kernel.elf整个文件加载到内存的起始地址。PIE程序的入口点elf->entry是相对于这个基地址的偏移,所以buf + elf->entry才是kmain在内存中的实际运行地址。
告知GDB实际加载地址的两种方法
方法1:使用add-symbol-file加载符号并指定基地址
直接用add-symbol-file替代symbol-file,命令格式为:
add-symbol-file kernel.elf <buf的实际地址>
例如,假设buf的地址是0x100000,则执行:
add-symbol-file kernel.elf 0x100000
这个命令会告诉GDB,kernel.elf的符号对应的内存地址都要加上这个基地址偏移。
方法2:加载符号后调整偏移
如果已经用symbol-file加载了符号,可以通过set link-map-offset命令设置偏移值:
set link-map-offset <buf的实际地址 - PIE默认基地址>
PIE程序的默认基地址通常是0x0或0x400000(取决于编译环境),比如buf是0x100000、默认基地址是0x0,则执行:
set link-map-offset 0x100000
验证操作
设置完成后,重新执行break kmain,再continue,GDB应该就能在kmain处命中断点了。你也可以用info symbol kmain命令查看符号对应的实际内存地址,确认是否和buf + elf->entry的计算结果一致。
内容的提问来源于stack exchange,提问作者runningupthatroad

