简介
前提是搭建好环境,可以参考之前的链接
uboot
在uboot目录下输入gdb-multiarch,而不是gdb-multiarch u-boot,因为uboot会重定位,而且之前这么操作总是有奇奇怪怪的问题
gdb-multiarch

连接openocd内置的gdbserver,端口号是3333
target retmoe :3333

然后uboot下输入bdinfo命令,找到重定位后的地址

找到地址,输入下列命令,加载符号表,输入bt查看函数调用栈,这里连函数的第几行都显示出来了
add-symbol-file ~/raspi4b-project/uboot/u-boot 0x7f21000

当然还有另外的方法,不用输入bdinfo也可以知道重定位后的地址,因为bd_info是在结构体gd_t中的,而gd_t的地址存放在x18寄存器中,所以我们偏移一下就可以算出来了

跟bdinfo命令中的信息对上了

kernel
如果想要gdb调试kernel,需要打开如下内核配置
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_FRAME_POINTER=y
CONFIG_GDB_SCRIPTS=y
CONFIG_DEBUG_INFO_REDUCED=n
CONFIG_KGDB=y
CONFIG_RANDOMIZE_BASE=n /*非常重要*/
CONFIG_DEBUG_VM=y
以下为内核配置的说明
CONFIG_DEBUG_INFO: 在内核和内核模块中包含调试信息,这个选项在幕后为gcc使用的编译器参数增加了-g选项。CONFIG_FRAME_POINTER:这个选项会将调用帧信息保存在寄存器或堆栈上的不同位置,使gdb在调试内核时可以更准确地构造堆栈回溯跟踪(stack back traces)。- 启用
CONFIG_GDB_SCRIPTS,但要关闭CONFIG_DEBUG_INFO_REDUCED。 CONFIG_KGDB:启用内置的内核调试器,该调试器允许进行远程调试。- 关闭
CONFIG_RANDOMIZE_BASE设置:KASLR会更改引导时放置内核代码的基地址。如果你在内核配置中启用了KASLR(CONFIG_RANDOMIZE_BASE=y),则无法从gdb设置断点。 CONFIG_DEBUG_VM
配置编译好kernel后,输入下列命令
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- scripts_gdb -j16

执行gdb的时候,可能会有类似的报错
warning: File "/home/sammi/raspi4b-project/raspi_sdk/raspi_linux/scripts/gdb/vmlinux-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
To enable execution of this file add
add-auto-load-safe-path /home/sammi/raspi4b-project/raspi_sdk/raspi_linux/scripts/gdb/vmlinux-gdb.py
line to your configuration file "/home/sammi/.gdbinit".

那就按照提示语句add-auto-load-safe-path /home/sammi/raspi4b-project/raspi_sdk/raspi_linux/scripts/gdb/vmlinux-gdb.py加到文件/home/sammi/.gdbinit

未使能MMU的kernel
kernel前期在汇编中的代码是没有开启MMU的,而vmlinux调试文件中都是虚拟地址,所以我们需要将.head.text,text ,rodata这三个段重新映射(为什么是这三个段等以后研究下kernel启动的汇编脚本)
输入下列命令获取三个段的虚拟地址,然后用kernel启动的基地址加上这三个段减去.head.text的地址的偏移量就是我们要重定位的地址了
readelf -S vmlinux
这里可以看出.head.text是最开始的第一个段,为什么.head.text是第一段呢?

其实是由文件arch/arm64/kernel/vmlinux.lds.S 布局决定的

通过虚拟地址跟kernel在ddr中的地址相减获取真实的物理地址,树莓派中kernel的启动地址是0x1000000,如下计算可以得到真实段的地址
.head.text = 0x1000000 + (ffffffc080000000 - ffffffc080000000)
.text = 0x1000000 + (ffffffc080010000 - ffffffc080000000)
.rodata = 0x1000000 + (ffffffc080d30000 - ffffffc080000000)
接着我们输入如下命令
gdb-multiarch vmlinux
target remote :3333
add-symbol-file vmlinux -s .head.text 0x1000000 -s .text 0x1010000 -s .rodata 0x1d30000

输入layout split即可快乐调试

使能MMU的kernel
直接输入命令
gdb-mulitiarch vmlinux
target remote :3333

调试ko
调试ko的话需要知道text段的地址,有两种方法可以查看
cat /proc/modules
cat /sys/module/general/sections/.text

加载命令如下,注意这里用的是.o文件
add-symbol-file modules/general/general.o 0xffffffc0799a0000

打断点如下

如果需要调试静态符号,需要加载.bss段


add-symbol-file modules/general/general.o 0xffffffc0799a0000 -s .bss 0xffffffc0799a2640
调试技巧
调用栈
这个比较常见的用法,通常想知道函数跑到什么位置
bt
断点
具体到某个文件的某一行

其他用法
(gdb) b main #到某个函数
(gdb) b *0x80483f #在某个内存
查看断点

删除断点
delete breakpoints N

单指令调试
输入layout split命令可以列出src跟asm两个框


可以用step(si)跳入调用函数并执行,next(ni)不跳入调用函数并执行
si 3 #执行三行
ni 3 #执行三行
退出图形界面可以按Ctrl+X 然后再按A
kernel专属
GDB提供了Python接口来扩展功能,内核基于Python接口实现了一系列辅助脚本,简化内核调试,开启CONFIG_GDB_SCRIPTS参数就可以使用了。内核中的文档信息如下
Documentation/translations/zh_CN/dev-tools/gdb-kernel-debugging.rst
Documentation/dev-tools/gdb-kernel-debugging.rst
输入apropos lx获取帮助信息

dmesg
lx-dmesg

virt2phy和virt2page
lx-virt_to_phys
lx-virt_to_page

mount
lx-mount
就是不知道这些虚拟地址指的是哪个地方的,等以后研究文件系统的时候可以回头看看

fdtdump
lx-fdtdump

最后能找到当前目录下生成的fdtdump.dtb

至于原理怎么实现还不了解
clk-tree
lx-clk
lx-clk-summary

cmline
lx-cmdline

其他
之前自己写的博客
参考链接
问题
目前调试使能MMU后使用虚拟地址的内核,无法打断点调试,会提示下列报错,还不清楚是什么问题
