KGDB原理

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时,你可以触发SysRqg,

$ 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 ,跟踪标志。置 则开启单步执行调试模式,置 则关闭。在单步执行模式下,处理器在每条指令后产生一个调试异常,这样在每条指令执行后都可以查看执行程序的状态。如果程序用 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值