Chinaunix首页 | 论坛 | 博客
  • 博客访问: 345365
  • 博文数量: 88
  • 博客积分: 2011
  • 博客等级: 大尉
  • 技术积分: 885
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-21 14:50
文章分类

全部博文(88)

文章存档

2010年(88)

我的朋友

分类: LINUX

2010-10-11 23:01:31

很早的时候写的关于minix3调试的文章了,现在项目已经结束了大半年了,正值找工作,翻出来温习下。

早期探讨:
1.程序的调试
  1.1 一般调试的机制
  1.2 本地调试
  1.3 远程调试
2.minix3内核的调试
  2.1 存在的问题
  2.2 深入分析
  2.3 实现细节
      2.3.1 汇编级代码的调试
      2.3.2 c级代码的调试
  2.4 未完成工作
  

1. 操作系统内核的调试
------------------
1.1 一般调试的机制
---------------
   调试器根据目标文件的调试信息找到源码和机器码之间的映射关系,并把它关心的指令替换成一条断点指令,x86对应的就是asm("int 3"),它的二进制代码是0xcc,当程序执行到这个地方时断点指令被执行,程序被中断,调试起接管它的主控权,这时可以查看内存信息甚至修改内存,当调试器完成任务后把断点指令替换回原来的指令,并把pc寄存器减1,让cpu从被中断的那条指令开始执行。

1.2 本地调试
----------------
  本地调试的方法较多,而不同的操作系统平台又有着不同的方法,以Linux平台为例,用户空间的调试都基于系统调用ptrace()来实现的,流行的调试器gdb也不例外,这里就不具体介绍ptrace()函数本身了,大概说下思想,ptrace能将进程切换到挂起状态然后再继续执行,从被调试的进程地址空间中读取或向其中写入数据。

1.3 远程调试
----------------
   对内核作调试和应用程序完全不同,因为调试器需要调试的目标正是它所在的运行环境,它对调试环境的调试很可能会影响到本身的运行情况。所以对内核的调试必须使用其它办法,使用printk()函数打印信息是一种方法,但是其功能有限,这种方法不能控制内核的运行,现在使用得较多的内核调试方法是GDB远程调试。
   一般情况下,如果需要调试的程序和当前GDB所在的环境是一样的,就可以直接使用GDB命令调试;如果需要调试的程序和GDB的运行环境不同,或者说需要调试的环境无法运行GDB,就无法直接使用GDB来调试了,这样,就需要使用远程调试功能,通过一台可以使用GDB的机器,通过串口通讯协议或者TCP/IP网络协议和被调试的程序所在的机器连接,GDB命令控制的都是从串口或者网络那边得来的数据。当调试一个系统时,由于这个系统无法自行完成调试的功能,只能通过外部和这个系统的连接完成调试的过程,在GDB中可以通过target命令完成这个过程。
   指定需要调试的远程主机的方法是使用target remote命令,后面紧接着和远程主机连接的设备。在GDB中内嵌着远程串行协议,规范了远程机器的调试命令的一些数据传输包的格式;在远程主机上,需要实现一个stub,其中需要提供和本地主机连接的协议,以及传送数据信息的方法(当然还要有设置断点以及中断处理的功能),也就是说stub实现两台机器的连接。这样只要在调试前从本地导入符号表数据,调试的时候就可以对源代码进行调试了。
  SGI公司为Linux内核开发了kgdb补丁,通过对内核的修改,在内核的运行过程中截获运行态数据,用户可以通过运行和这个内核的机器相连的方式来得到这些数据,并发送命令要求这台主机停止运行。

2.minix3内核的调试
--------------------
   对问题的分析以及部分想法参考了Bob Zulawink为minix2设计的remgdb(很遗憾,由于minix3的体系结构相比minix2来说变动很大,Bob的remgdb在minix3上面无法使用)以及Jonathan在他的blog上有关使用GDB debug minix3's kernel and application的想法。
--------------------
2.1 存在的问题
--------------
a. GDB不支持minix3的可执行文件格式,因此,它也就不能从中读取符号和调试信息;而且minix3可执行文件中符号信息大量丢失,不能用来调试。
b. 需要相应的stub来实现minix3 kernel与gdb通信;
c. 由于minix3 I&D的分离,所以有可能出现当用户要求调试起打印某个地址的内存信息时,调试器无法确定用户指的代码段还是数据段。

2.2 深入分析
---------------
   首先,minix3的可执行文件格式为a.out,但是不是UNIX兼容的a.out可执行文件格式,所以无法被GDB解析,与其修改GDB使其去解析minix3的可执行文件格式,不如修改minix3的可执行文件格式,使其能被GDB所识别。
   其次,既然minix3的a.out中的符号信息残缺不全,我们如何获得相应的符号信息呢?事实上,minix3的工具链(toolchain)生成了源码级调试所需要的所有信息,然而,这些符号信息在最终的ACK可执行文件向a.out可执行文件转换的时候丢失了,原因在于:minix3的a.out格式定义符号名为8个字节的字符串,因此符号名大于8字节的符号(比如那些用于调试的符号的符号名)将会被修剪为只有8个字节,这样这些符号信息对于调试器来说就没有用了。还好可以修改ACK执行编译的配置文件将中间的ACK可执行文件保存下来,然后再从中提取出符号表信息。
   结合以上两点,要做的就是在minix3的a.out可执行文件中加入ACK中提取出来的符号表信息,然后再重组其布局,使其与unix的a.out兼容。
----------------------------------------------------  
   有关远程主机的选择有两种方案:采用真实的机器;采用虚拟机。采用真实的机器则一定要在minix3内核实现一个gdbstub,否则gdb无法和其通信;采用虚拟机原则上也有同样的问题,但是有的虚拟机中实现了gdbstub,从而gdb可以与虚拟机通信,但缺点是虚拟机可能会有bug。
   由于实现一个minix3的gdb stub工作量较大,短期看来完成不了,于是考虑使用虚拟机自带的gdbstub。这里我选用bochs虚拟机,bochs是一个完全模拟x86指令的虚拟机,调试功能十分强大。
-----------------------------------------------------
   由于minix3的I&D分离的特殊情况,我觉得可以修改gdb的通信协议,使得数据包中对代码段和数据段的数据加上标识,这样gdb就可以识别了。

2.3 实现细节
-------------
2.3.1 汇编级代码的调试
-----------------
  GDB并不能调试minix3的boot部分的汇编代码,还好bochs的内置调试器可以进行汇编级的控制,不过好像它只能进行汇编级的控制。
2.3.2 c级代码的调试
-------------------
    这里就可以使用GDB了,但是主要工作还是集中在ACK可执行文件中符号信息的抽取以及a.out可执行文件格式的转换。
--------------------
a.保存ACK可执行文件
   只要如下修改下ACK编译的配置文件/usr/lib/descr:
   在    $ACK_CV -x -m$ARCH $EXE $OUT下一行
   添加 cp $EXE ${OUT}.ack
   即可,这样每当你编译一个程序xx时候就会多出个xx.ack。
--------------------------------------------
b.可执行文件格式的转换
---------------------
大概介绍下,要了解细节我们可以再讨论.
unix a.out可执行文件布局

a.out header
text section
data section
text relocation
data relocation
symbol table(供链接程序使用)
string table(此部分含有与符号名相对应的字符串,供调试程序调试目标代码,与链接过程无关。这些信息可包含源程序代码,局部符号以及数据结构描述信息等等)

 大致流程:
  a) 将minix的a.out文件头转换成unix的a.out文件头;
  b) 从minix的a.out文件中抽取出.text区和.date区;
  c) 从ack可执行文件中抽取出符号表并将起转换成unix a.out中     格式的符号表;
  d) 抽取ack可执行文件中的字符串表,并加以修改使其类似于unix中的字符串表
  e) 将以上的各个部分重组,形成unix兼容的a.out文件格式。
----------------------------------------------
    这样只要在bochs的configure文件中enable gdbstub就行了。
    在本地gdb读取符号信息后target remote xxx即可实现与远程机的连接,之后就像本地使用gdb一样调试minix了。

2.4 未完成的工作
  ---------------
   也就是deal with minix3 I&D分离的情况,由于项目最终并不将minix3编译为I&D分离的情况,所以有关修改gdb远程通信协议的工作暂时也就没有做.

---------------------------------------------------------------------
  
实际采用的方案:

   实践表明,GDB调试虽然强大,但不适用于minix3内核,主要是minix3的微内核架构:另外,采用kprintf又出现问题,似乎minix3.1.1串口输出有问题,最后不得不借助于xen虚拟机,通过修改xen的内核,使得系统在执行到一条特权指令(我们用的CPUID指令)时候陷入到xen虚拟机中,由xen虚拟机解析相关值(这里要感谢下虚拟机小组一哥们的技术支持...).嗯,调试就这样,上面一大段是以前探讨调试方案的时候写的文档,虽然最后没有采用,但是对于调试的理解多少有点帮助吧。
阅读(2801) | 评论(2) | 转发(0) |
0

上一篇:C语言版 Kruskal

下一篇:Make命令行选项

给主人留下些什么吧!~~

chinaunix网友2010-10-13 19:56:55

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com

chinaunix网友2010-10-13 19:56:15

很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com