运行和调试功能

      gdbStub

 

      20的调试器使用的是GNUGDBGDB本身不但可以作为本地调试器,也可以远程调试。在远程调试的时候,必须在远程系统上嵌入stub,这个stub代码量不多,本文所说的KDBG就是这个stub,也是2.0内核态的stub

           总体设计

                                                

      GDBstub之间的通信是通过远程通讯协议实现的。GDB远程串行协议RSP(Remote Serial Protocol)是一种基于消息的ASCII码协议,包含了诸如读写内存、查询寄存器、运行程序等命令.

RSP包格式: $ <data> # CSUM1 CSUM2

<data>ASCLL串,但不包含$#. CSUM1CSUM2分别是一个ASCLL十六进制的对<data>的校验数。

接收方以+-响应RSP包的发送方。

   + :如果校验数字正确,接收方准备发送下一个RSP包。

-         :校验数错误

Stubkernel初始化的时候就初始化了,一旦kernel运行出现了异常,kernel就会进入异常处理函数,异常处理函数会将kernel的异常信息通过调试端口发送给主机,如果在主机端启动GDBGDB就会和stub建立连接,这时异常处理函数就会进入一个GDBstub的会话循环,通过这个循环,我们就可通过GDB来调试kernel

 具体实现:

stub实现分为三个部分:通讯命令处理底层通讯上下文(contect)接口。其关系如下:

上下文(contect)接口stubGDB对寄存器信息的交流是通过指定寄存器号来实现的(寄存器编号是GDB定义的),应此KDBG定义了一个KDBCONTEXT:

struct KDBCONTEXT {

 

    uint32_t EAX,ECX;

    uint32_t EDX,EBX;

    uint32_t ESP,EBP,ESI,EDI;

    uint32_t EIP,EFLA

GS,CS;

    uint32_t SS,DS;car

}

那么EAX寄存器的编号就是0ECX就是1,依次类推。另外定义了几个上下文接口,stub的命令处理部分在对上下文的读写时会调用这些接口。

ESP,EBP,EIP的操作:

   GetESP()GetEBP()GetEIP()  :分别得到三个寄存器的值

   SetEIP()  :设置EIP的值

   GetESPIdx()GetEBPIdx()GetEIPIdx() :分别得到三个寄存器的编号

 

 KDBCONTEXT结构和系统上下文的转化:

       ExpCxtToKdbCxt () :系统上下文的值写入KDBCONTEXT

       KdbCxtToExpCxt () KDBCONTEXT的值写入系统上下文

 

 对陷阱标志位的控制:

       DisableTrace()

       EnableTrace()

 

通讯命令处理stubGDB之间只是交流一些基本的信息,具体是:内存的读写,寄存器的读写,被调试程序状态信息,断点维护以及一些基本的程序控制信息(程序单步执行,继续执行,退出);至于GDB的许多并且有点复杂的命令,除了stub对这些基本信息的维护,GDB都自己处理了(有关GDB命令的说明,现在还没有文档,不过可以参看GDBhelp)

   命令处理是由一个KdbLoop函数实现的,这个函数里有个循环是为stubGDB的会话而设置的。每次进入KdbLoop函数,stub首先会将当前被调试的kernel的状态告诉GDB,具体是stub将当前的ESP,EBPEIP的值发给GDB,之后stub就等待GDB的响应,如果GDB给出了回应,stub就会进入会话循环,也就是对通讯命令的处理。如下图的会话循环:

“?”命令GDB用此命令来找出目标机的当前状态。

stub收到这个命令的时候,stub返回当前的ESP,EBP,EIP三个寄存器的值给GDB,让GDB知道当前的被调试的kernel的状态。

 

“g”命令:读所有寄存器的值,stub返回KDBCONTEXT结构所有内容给GDB

 

“q”命令:RSP包格式:$qOffsets#xx GDB发送此命令来决定重定位。不用于嵌入式系统中,在kernel中保留此命令,只是为了提高GDB的启动速度。 stub返回给GDB代码,数据以及BSS的偏移,三者都为0(不解)

 

“H”命令:RSP包格式:$H<线程号>#xx   此命令的功能是将所有的命令都限制于此命令参数中指定的线程,这对于运行在实时操作系统上多线程应用程序是非常重要的。线程号由用户指定,如果线程号为-1,说明GDB命令用于所有的线程。 stub给出一个OK响应。

 

“G”命令:写所有寄存器。RSP包格式:$G<data1><data2>…#xx

(设置 0个寄存器(EAX)内容为: 0x12345678,第 1个寄存器(ECX)内容为:0x9abcdef0, 依次类推。)

GDB把按照寄存器编号顺序的寄存器的值发给stubstub按照寄存器编号顺序一一将值写入寄存器(通过上下文接口KdbCxtToExpCxt()),如果成功给出ok应答,否则给出错误应答。

 

“P”命令:写单个寄存器。RSP包格式:$P<寄存器编号>=<data>#xx GDB发给stub某个寄存器编号和要写入的值,stub根据寄存器编号找到KDBCONTEXT中相应的寄存器并写入值,然后通过上下文接口KdbCxtToExpCxt()完成对相应寄存器的写入。如果成功给出ok应答,否则给出错误应答。

 

“m”命令:读内存。RSP包格式:$m<address>,<bytecount>#xx  GDB发给stub要读的内存地址以及要读的字节数,由于内核的可执行映像是1:1的映射,故stub根据此内存地址读出请求的字节数内容并返回给GDB

 

“M”命令:写内存。RSP包格式:$m<address>,<bytecount><data>#xx  GDB发给stub要写的内存地址,要写入的字节数和要写入的值,stub根据这些要求擦写相应的内存,成功会给GDB一个OK应答。

 

 

“Z”命令和“z”命令:GDB允许用户在调试程序时设置断点,GDB会纪录端点信息。当GDB把控制权交给被调试程序的时候,在断点处程序会停止。这个过程的实现原理是:stub维护一个 断点列表

struct KdbBreakpoint : public DLinkNode {

      unsigned char  *pAddr; //保存断点所在地址

      char           buf[2];           //保存断点所在地址的内容。

};

GDB把控制权交给被调试程序的时候,如:用户输入了continue命令,GDB首先用“Z”命令通知stub“Z”命令的RSP包如下:

 $ Z<type>,<address>,<len>#xx

type是断点指令类型为0len是断点指令长度为1address是用户设置的断点所在地址。Stub首先会在断点列表里查找是否有相同的断点地址(用户可能在同一处设置断点),如果没有,stub保存了address以及address的内容,把这个断点信息加入断点列表,并且stub设置address的内容为一条断点指令cc(int $3的机器码)stub处理完“Z”命令后给GDB一个成功响应,GDBstub发送continue命令,这时GDB把控制权交给被调试程序,但是由于addressstub设置为断点指令,程序在address处会进入breakpoint异常,GDB会向stub发送“z”命令, “z”命令的RSP包格式和“Z”命令一样,stub会根据address查找断点列表,如果有address,那么会把这个断点信息从断点列表中摘掉,并把保存的address原来的内容buf回写到address

 

“c”命令:GDB发出此命令让被调试的目标继续往下执行,直到遇到新的断点或者运行过程中产生异常,程序就停止,否则程序会一直运行到结束。 stub在处理此命令的时候只是将EFLAGS寄存器的trap标志位禁止。

 

“s”命令:单步运行命令。 stub置位EFLAGS寄存器的trap标志位,这样被调试目标执行一条指令后又会产生一个调试异常,以此来完成单步调试任务。

 

“k”命令和“D”命令:杀死调试目标,stub处理此命令是通过调用kernelReboot()函数实现的。

 

底层通讯:这个模块的功能就是完成对RSP包的解包和打包。为此它提供了两个接口:

        void AtomicGetDebugPacket (char * buffer)  :完成接收RSP包并解包

        void AtomicPutDebugPacket (const char * buffer) :对命令打包成RSP包并完成发送。

这两个函数的实现是根据RSP包的格式来实现的(RSP格式在本文开头有说明),实现比较简单,在此不再说明。

 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值