kgdb源代码分析(2.6.27)
本文由penny编写, 你可以通过发邮件到[email protected]联系penny,
也可以直接评论本文章与penny交流.
0. 概述
1. 异步通知
2. 准备工作
3.gdb 远程串行协议(GDB remote serial protocol)
4. 命令实现:
4.1 断点.
4.2 continue 和 step
5 初始化时机.
4. 命令实现:
kgdb 实现了众多的命令,这里不一一进行说明,有些实现十分简单,有些就需要仔细看
看,不过其实很多工作 gdb 端已经完成了,到 kgdb 这边的都是很直接的,比如用户试图在
gdb中打印一个局部变量的值,gdb 通过和 kgdb 得到当前进程寄存器的信息,最终计算出要
读的内存地址和长度,kgdb 只要应 gdb 的要求提供相应的信息和设置相应的地址就够了,所
以 gdb 是调试里面的主角.不过我们这里关心的还是 kgdb, gdb 的工作就忽略了.
我们不妨用一个 gdb 用户的角度来看看,大多数情况下我们只要:设置断点,让程序继续,
单步调试,打印内存这些功能.对应 gdb 发给 kgdb 的命令并不就是'Z','c','s','m'命令,一般一
个 gdb 的用户的命令都对上十几二十条 kgdb 命令的发送和接收.
4.1 断点:
先从数据结构入手,kgdb用一个数组kgdb_break[]来描述软件断点(我们这里暂且只讨论软断点):
1 2 3 4 5 6 7 8 | (kernel/kgdb.c) 107 /* 108 * Holds information about breakpoints in a kernel. These breakpoints are 109 * added and removed by gdb. 110 */ 111 static struct kgdb_bkpt kgdb_break[KGDB_MAX_BREAKPOINTS] = { 112 [0 ... KGDB_MAX_BREAKPOINTS-1] = { .state = BP_UNDEFINED } 113 }; |
每个元素为kgdb_bkpt定义在
1 2 3 4 5 6 7 | (include/linux/kgdb.h) 98 struct kgdb_bkpt { 99 unsigned long bpt_addr; 100 unsigned char saved_instr[BREAK_INSTR_SIZE]; 101 enum kgdb_bptype type; 102 enum kgdb_bpstate state; 103 }; |
结构体里面的每个成员都很好理解,bpt_addr就是断点设置的地址,saved_instr[]放的
是被断点指令所替换的真实指令.大家也知道一般调试器的实质就是在断点对于的内存地址
中把原来的指令或部分指令替换成一条断点指令,对于x86就是"int 3",这条指令要能让系统
进入被gdb控制的状态.type对应上面所说的'Z'命令的type字段,state标志每个断点的状态4中状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | (include/linux/kgdb.h) 83 enum kgdb_bptype { 84 BP_BREAKPOINT = 0, 85 BP_HARDWARE_BREAKPOINT, 86 BP_WRITE_WATCHPOINT, 87 BP_READ_WATCHPOINT, 88 BP_ACCESS_WATCHPOINT 89 }; 90 91 enum kgdb_bpstate { 92 BP_UNDEFINED = 0, 93 BP_REMOVED, 94 BP_SET, 95 BP_ACTIVE 96 }; |
在 kgdb_break[]被初始化时 state 成员都被设置成 BP_UNDEFINED,表明这个数组元
素可用,当gdb设置一个断点时,一个数组元素会被设成BP_SET,相反取消断点时则把相应的
数组项设成 BP_REMOVED. 当断点被激活时元素被设置为 BP_ACTIVE.
几个断点状态的重要切换点:
1. BP_UNDEFINED/BP_REMOTED -> DB_SET 和 BP_SET -> BP_REMOVED分别发生
在收到gdb的'Z'和'z'指令时.由函数kgdb_set_sw_break()和 kgdb_remove_sw_break()实现
2. BP_SET -> DB_ACTIVE 一个断点处于BP_SET状态并没有生效,只是说明这个断点是有
效的.当gdb发来's'或'c'命令恢复原来程序执行时才会让断点变成 BP_ACTIVE.函数
kgdb_activate_sw_breakpoints()实现了这个状态转换.
3. BP_ACTIVE -> BP_SET 发生在进入和gdb_serial_stub()之前,先把断点都失效在与远
端的gdb通信,在讲主函数的时候几经提过.
4. 其它状态 -> DB_UNDEFINED 函数remove_all_break()完成这个功能,它主要是在远
程gdb与kgdb断开链接时,即进行'D','k','C15'等命令时kgdb对所有断点进行重置.
下面具体看看 kgdb 是怎样设置断点的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | (kernel/kgdb.c) 616 /* 617 * SW breakpoint management: 618 */ 619 static int kgdb_activate_sw_breakpoints(void) 620 { 621 unsigned long addr; 622 int error = 0; 623 int i; 624 625 for (i = 0; i < KGDB_MAX_BREAKPOINTS; i++) { 626 if (kgdb_break[i].state != BP_SET) 627 continue; 628 629 addr = kgdb_break[i].bpt_addr; 630 error = kgdb_arch_set_breakpoint(addr, 631 kgdb_break[i].saved_instr); 632 if (error) 633 return error; 634 635 kgdb_flush_swbreak_addr(addr); 636 kgdb_break[i].state = BP_ACTIVE; 637 } 638 return 0; 639 } |
kgdb_arch_set_breakpoint()是一个和体系结构相关的函数,但是暂时在内核代码里
面只有一个地方实现了, 这里所做的就是读取断点地址对应的指令并保存起来, 然后用一条
可以触发断点的指令替换这段内存:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | (kernel/kgdb.c) 167 /* 168 * Weak aliases for breakpoint management, 169 * can be overriden by architectures when needed: 170 */ 171 int __weak kgdb_arch_set_breakpoint(unsigned long addr, char *saved_instr) 172 { 173 int err; 174 175 err = probe_kernel_read(saved_instr, (char *)addr, BREAK_INSTR_SIZE); 176 if (err) 177 return err; 178 179 return probe_kernel_write((char *)addr, arch_kgdb_ops.gdb_bpt_instr, 180 BREAK_INSTR_SIZE); 181 } |
这里出来一个有趣的单词__weak,它是一个宏,和__packed是同一种东西都是gcc的扩展属性:
#define __packed __attribute__((packed))
#define __weak __attribute__((weak))
如果这个关键字用在函数定义上面,一般情况下和一般函数没有两样。但是当有一个同
名函数但是不带__weak被定义时,所有对这个函数的调用都是指向后者(不带__weak那个),
如果有两个一样的函数都用了__weak,那么真正调用那个,就要看连接器了。
设置断点的过程其实就是像大家想像的那样,把相应地址的指令读上来并保存起来,把
一条断点指令写到这个地址上去.
关于断点的函数像kgdb_set_sw_break(),kgdb_remove_sw_break()这些函数的实
现都是直接了当的,这里就不一一说明了.
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. 4.2 continue 和 step 5 […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. 4.2 continue 和 step 5 […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. 4.2 continue 和 step 5 […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. 4.2 continue 和 step 5 […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. 4.2 continue 和 step 5 […]
[…] 概述 1. 异步通知 2. 准备工作 3.gdb 远程串行协议(GDB remote serial protocol) 4. 命令实现: 4.1 断点. 4.2 continue 和 step 5 […]