简介
前提是搭建好环境,可以参考之前的链接
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后使用虚拟地址的内核,无法打断点调试,会提示下列报错,还不清楚是什么问题