1.1.1. KGDB
1.1.1.1. Kgdb配置
KGDB必须使能的内核选项:
CONFIG_EXPERIMENTAL= y
Location:
-> General setup
-> Prompt for development and/or incomplete code/drivers
CONFIG_KGDB= y
Location:
-> Kernel hacking
-> KGDB: kernel debugger
CONFIG_KGDB_SERIAL_CONSOLE= y (使用串口进行通信)
Location:
-> Kernel hacking
-> KGDB: kernel debugger
-> KGDB: use kgdb over theserial console
建议关闭的选项:
CONFIG_DEBUG_RODATA = n
该选项是将内核的一些内存区域空间设置为只读,这样可能导致kgdb的
设置软断点功能失效。所以推荐将该选项关闭。
Location:
-> Kernel hacking
建议打开的选项:
CONFIG_KGDB_LOW_LEVEL_TRAP= y
使能该选项可以kgdb不依赖notifier_call_chain()机制来获取断点异常,
这样就可以对notifier_call_chain()机制实现相关的函数进行单步调试。
Dependson: KGDB [=y] && (X86 [=y] || MIPS [=MIPS])
Location:
-> Kernel hacking
-> KGDB: kernel debugger (KGDB [=y])
CONFIG_DEBUG_INFO= y
该选项可以使得编译的内核包含一些调试信息,使得调试更容易。
Location:
-> Kernel hacking
CONFIG_FRAME_POINTER= y
该选项将使得内核使用帧指针寄存器来维护堆栈,从而就可以正确地执行堆栈回溯,即函数调用栈信息。
Location:
-> Kernel hacking
CONFIG_MAGIC_SYSRQ= y (如果你选择了KGDB_SERIAL_CONSOLE,这个选项将自动被选上)
激活"魔术 SysRq"键. 该选项对kgdboc调试非常有用,kgdb向其注册了‘g’魔术键来激活kgdb 。
Location:
-> Kernel hacking
当你想手动激活kgdb时,你可以触发SysRq的g键,如
$ echo"g" > /proc/sysrq-trigger
1.1.1.2. Kernel参数
console=ttyS0,115200n8 kgdboc=ttyS0,115200 kgdbwait
1.1.1.3. kgdb over ttysx
<drivers/tty/serial/kgdboc.c>
1.1.1.4. 原理
1.1.1.4.1. 断点
将目的地址指令替换为BKPT(ARM)断点指令,然后当执行到此时产生未定义指令异常,进入kgdb回调处理流程
1.1.1.4.2. 单步执行
l x86
使用 TF 标志位实现,这个
Tflag 寄存器
TF(Trap Flag)—— 位 8 ,跟踪标志。置 1 则开启单步执行调试模式,置 0 则关闭。在单步执行模式下,处理器在每条指令后产生一个调试异常,这样在每条指令执行后都可以查看执行程序的状态。如果程序用 POPF 、 POPFD 或者 ET 指令设置 TF 标志,那么这之后的第一条指令就会产生调试异常。
这样我们只需在调试异常中断时 suspend 被调试程序,通知调试程序即可实现单步。
l ARM
由于 arm 硬件没有单步的实现机制,故还是得像断点一样,将下一次要执行的指令地址设置为断点,这个断点和一般断点的区别是它是临时的,执行完下一指令后不用再恢复为断点,既然要知道下一条指令的地址,就需要有预测机制,即从当前指令判断出下次会执行哪一条指令,由于预测一是涉及到 b xx,ld pc 等指令还有是条件项,由于判断条件得不偿失,我们一般设置两个断点,即在条件两个分支都设置断点,这样不管条件是什么,都会进入 debug 。
1.1.1.5. 源码
1.1.1.5.1. 注册异常回调
注册KGDB异常处理notify
<drivers/serial/tty/kgdboc.c>
init_kgdboc{
configure_kgdboc(){
kgdb_register_io_module{
kgdb_register_callbacks{
kgdb_arch_init{
register_die_notifier(&kgdb_notifier);
}
}
}
}
}
1.1.1.5.2. 未定义指令异常
<arch/arm/kernel/kernel/entry-armv.S>
__vectors_start{
vector_und+ stubs_offset{
vector_stub und, UND_MODE, 0{
__und_svc{
do_undefinstr{
}
}
}
}
}
<arch/arm/kernel/kernel/traps.c>
do_undefinstr{
arm_notify_die{
die{
__die{
notify_die
}
}
}
}
<kernel/notifier.c>
notify_die{
kgdb_notifier->kgdb_notify
}
1.1.1.5.3. 系统调用用户空间GDB
<Arch/arm /kernel/ entry-common.S>
vector_swi{
arm_syscall{<arch/arm/kernel/traps.c>
bad_syscall{
arm_notify_die
}
}
}
1.1.1.5.4. Kgdb初始化
<init/main.c>
Start_kernel{
dbg_late_init{
kdb_init
}
}
void __init kdb_init(int lvl)
{
staticint kdb_init_lvl = KDB_NOT_INITIALIZED;
int i;
if(kdb_init_lvl == KDB_INIT_FULL || lvl <= kdb_init_lvl)
return;
for (i= kdb_init_lvl; i < lvl; i++) {
switch(i) {
caseKDB_NOT_INITIALIZED:
kdb_inittab(); /*初始化命令表 */
kdb_initbptab(); /* 初始化断点命令*/
break;
caseKDB_INIT_EARLY:
kdb_cmd_init(); /* Build kdb_cmds tables */
break;
}
}
kdb_init_lvl= lvl;
}
1.1.1.5.5. 设置断点
将目的地址指令替换为BKPT端点指令,然后当执行到此时产生未定义指令异常,进入GDB主处理流程
<kernel/debug/debug_core.c>
int dbg_activate_sw_breakpoints(void)
{
unsignedlong addr;
interror;
intret = 0;
int i;
for (i= 0; i < KGDB_MAX_BREAKPOINTS; i++) {
if(kgdb_break[i].state != BP_SET)
continue;
addr= kgdb_break[i].bpt_addr;
error= kgdb_arch_set_breakpoint(addr, kgdb_break[i].saved_instr);
if(error) {
ret= error;
printk(KERN_INFO"KGDB: BP install failed: %lx", addr);
continue;
}
kgdb_flush_swbreak_addr(addr);
kgdb_break[i].state= BP_ACTIVE;
}
returnret;
}
int __weak kgdb_arch_set_breakpoint(unsignedlong addr, char *saved_instr)
{
interr;
//将原指令保存
err =probe_kernel_read(saved_instr, (char *)addr, BREAK_INSTR_SIZE);
if(err)
returnerr;
//替换为BKPT指令
returnprobe_kernel_write((char *)addr, arch_kgdb_ops.gdb_bpt_instr,
BREAK_INSTR_SIZE);
}
比较好的资料:http://www.kgdb.info