linux 系统内核的调试.htm

来自「linux 内核调试.rar linux 内核调试.rar」· HTM 代码 · 共 753 行 · 第 1/5 页

HTM
753
字号
                        <DIV>kgdb也支持使用以太网接口作为调试器的连接端口。在对Linux内核应用补丁包时,需应用eth.patch补丁文件。配置内核时在Kernel 
                        hacking中选择kgdb调试项,配置kgdb调试端口为以太网接口,例如:</DIV><BR><A 
                        name=IDADBAZB><B></B></A><BR>
                        <TABLE cellSpacing=0 cellPadding=5 width="100%" 
                        bgColor=#eeeeee border=1>
                          <TBODY>
                          <TR>
                            <TD><PRE><CODE>
[*]KGDB: kernel debugging with remote gdb
Method for KGDB communication (KGDB: On ethernet)  ---&gt; 
( ) KGDB: On generic serial port (8250)
(X) KGDB: On ethernet
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
                        <DIV>另外使用eth0网口作为调试端口时,grub.list的配置如下:</DIV><BR><A 
                        name=IDALBAZB><B></B></A><BR>
                        <TABLE cellSpacing=0 cellPadding=5 width="100%" 
                        bgColor=#eeeeee border=1>
                          <TBODY>
                          <TR>
                            <TD><PRE><CODE>
title 2.6.7 kgdb
root (hd0,0)
kernel /boot/vmlinuz-2.6.7-kgdb ro root=/dev/hda1 kgdbwait kgdboe=@192.168.
5.13/,@192.168. 6.13/ 
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
                        <DIV>其他的过程与使用串口作为连接端口时的设置过程相同。</DIV>
                        <DIV>注意:尽管可以使用以太网口作为kgdb的调试端口,使用串口作为连接端口更加简单易行,kgdb项目组推荐使用串口作为调试端口。</DIV>
                        <DIV><B>2.2.4 模块的调试方法</B></DIV>
                        <DIV>内核可加载模块的调试具有其特殊性。由于内核模块中各段的地址是在模块加载进内核的时候才最终确定的,所以develop机的gdb无法得到各种符号地址信息。所以,使用kgdb调试模块所需要解决的一个问题是,需要通过某种方法获得可加载模块的最终加载地址信息,并把这些信息加入到gdb环境中。</DIV>
                        <DIV>I、在Linux 2.4内核中的内核模块调试方法</DIV>
                        <DIV>在Linux2.4.x内核中,可以使用insmod 
                        -m命令输出模块的加载信息,例如:</DIV><BR><A 
                        name=IDAZBAZB><B></B></A><BR>
                        <TABLE cellSpacing=0 cellPadding=5 width="100%" 
                        bgColor=#eeeeee border=1>
                          <TBODY>
                          <TR>
                            <TD><PRE><CODE>
[root@lisl tmp]# insmod -m hello.ko &gt;modaddr
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
                        <DIV>查看模块加载信息文件modaddr如下:</DIV><BR><A 
                        name=IDABCAZB><B></B></A><BR>
                        <TABLE cellSpacing=0 cellPadding=5 width="100%" 
                        bgColor=#eeeeee border=1>
                          <TBODY>
                          <TR>
                            <TD><PRE><CODE>
.this           00000060  c88d8000  2**2
.text           00000035  c88d8060  2**2
.rodata         00000069  c88d80a0  2**5
……
.data           00000000  c88d833c  2**2
.bss            00000000  c88d833c  2**2
……
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
                        <DIV>在这些信息中,我们关心的只有4个段的地址:.text、.rodata、.data、.bss。在development机上将以上地址信息加入到gdb中,这样就可以进行模块功能的测试了。</DIV><BR><A 
                        name=IDAJCAZB><B></B></A><BR>
                        <TABLE cellSpacing=0 cellPadding=5 width="100%" 
                        bgColor=#eeeeee border=1>
                          <TBODY>
                          <TR>
                            <TD><PRE><CODE>
(gdb) Add-symbol-file hello.o 0xc88d8060 -s .data 0xc88d80a0 -s 
.rodata 0xc88d80a0 -s .bss 0x c88d833c
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
                        <DIV>这种方法也存在一定的不足,它不能调试模块初始化的代码,因为此时模块初始化代码已经执行过了。而如果不执行模块的加载又无法获得模块插入地址,更不可能在模块初始化之前设置断点了。对于这种调试要求可以采用以下替代方法。</DIV>
                        <DIV>在target机上用上述方法得到模块加载的地址信息,然后再用rmmod卸载模块。在development机上将得到的模块地址信息导入到gdb环境中,在内核代码的调用初始化代码之前设置断点。这样,在target机上再次插入模块时,代码将在执行模块初始化之前停下来,这样就可以使用gdb命令调试模块初始化代码了。</DIV>
                        <DIV>另外一种调试模块初始化函数的方法是:当插入内核模块时,内核模块机制将调用函数sys_init_module(kernel/modle.c)执行对内核模块的初始化,该函数将调用所插入模块的初始化函数。程序代码片断如下:</DIV><BR><A 
                        name=IDATCAZB><B></B></A><BR>
                        <TABLE cellSpacing=0 cellPadding=5 width="100%" 
                        bgColor=#eeeeee border=1>
                          <TBODY>
                          <TR>
                            <TD><PRE><CODE>
……	……
	if (mod-&gt;init != NULL)
		ret = mod-&gt;init();
……	……
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
                        <DIV>在该语句上设置断点,也能在执行模块初始化之前停下来。</DIV>
                        <DIV>II、在Linux 2.6.x内核中的内核模块调试方法</DIV>
                        <DIV>Linux 
                        2.6之后的内核中,由于module-init-tools工具的更改,insmod命令不再支持-m参数,只有采取其他的方法来获取模块加载到内核的地址。通过分析ELF文件格式,我们知道程序中各段的意义如下:</DIV>
                        <DIV>.text(代码段):用来存放可执行文件的操作指令,也就是说是它是可执行程序在内存种的镜像。</DIV>
                        <DIV>.data(数据段):数据段用来存放可执行文件中已初始化全局变量,也就是存放程序静态分配的变量和全局变量。</DIV>
                        <DIV>.bss(BSS段):BSS段包含了程序中未初始化全局变量,在内存中 bss段全部置零。</DIV>
                        <DIV>.rodata(只读段):该段保存着只读数据,在进程映象中构造不可写的段。</DIV>
                        <DIV>通过在模块初始化函数中放置一下代码,我们可以很容易地获得模块加载到内存中的地址。</DIV><BR><A 
                        name=IDACDAZB><B></B></A><BR>
                        <TABLE cellSpacing=0 cellPadding=5 width="100%" 
                        bgColor=#eeeeee border=1>
                          <TBODY>
                          <TR>
                            <TD><PRE><CODE>
……
int bss_var;
static int hello_init(void)
{
printk(KERN_ALERT "Text location .text(Code Segment):%p\n",hello_init);

static int data_var=0;
printk(KERN_ALERT "Data Location .data(Data Segment):%p\n",&amp;data_var);

printk(KERN_ALERT "BSS Location: .bss(BSS Segment):%p\n",&amp;bss_var);
……
}
Module_init(hello_init);
</CODE></PRE></TD></TR></TBODY></TABLE><BR>
                        <DIV>这里,通过在模块的初始化函数中添加一段简单的程序,使模块在加载时打印出在内核中的加载地址。.rodata段的地址可以通过执行命令readelf 
                        -e hello.ko,取得.rodata在文件中的偏移量并加上段的align值得出。</DIV>
                        <DIV>为了使读者能够更好地进行模块的调试,kgdb项目还发布了一些脚本程序能够自动探测模块的插入并自动更新gdb中模块的符号信息。这些脚本程序的工作原理与前面解释的工作过程相似,更多的信息请阅读参考资料[4]。</DIV>
                        <DIV><B>2.2.5 硬件断点</B></DIV>
                        <DIV>kgdb提供对硬件调试寄存器的支持。在kgdb中可以设置三种硬件断点:执行断点(Execution 
                        Breakpoint)、写断点(Write Breakpoint)、访问断点(Access 
                        Breakpoint)但不支持I/O访问的断点。目前,kgdb对硬件断点的支持是通过宏来实现的,最多可以设置4个硬件断点,这些宏的用法如下:</DIV><BR><A 
                        name=IDAPDAZB><B></B></A><BR><IMG height=222 alt="" 
                        src="Linux 系统内核的调试.files/table3.gif" width=513 
                        border=0><BR>
                        <DIV>在有些情况下,硬件断点的使用对于内核的调试是非常方便的。有关硬件断点的定义和具体的使用说明见参考资料[4]</DIV>。 

                        <DIV><A name=IDA3DAZB>2.3.在VMware中搭建调试环境</A></DIV>
                        <DIV>kgdb调试环境需要使用两台微机分别充当development机和target机,使用VMware后我们只使用一台计算机就可以顺利完成kgdb调试环境的搭建。以windows下的环境为例,创建两台虚拟机,一台作为开发机,一台作为目标机。</DIV>
                        <DIV><B>2.3.1虚拟机之间的串口连接</B></DIV>
                        <DIV>虚拟机中的串口连接可以采用两种方法。一种是指定虚拟机的串口连接到实际的COM上,例如开发机连接到COM1,目标机连接到COM2,然后把两个串口通过串口线相连接。另一种更为简便的方法是:在较高一些版本的VMware中都支持把串口映射到命名管道,把两个虚拟机的串口映射到同一个命名管道。例如,在两个虚拟机中都选定同一个命名管道 
                        \\.\pipe\com_1,指定target机的COM口为server端,并选择"The other end 
                        is a virtual 
                        machine"属性;指定development机的COM口端为client端,同样指定COM口的"The 
                        other end is a virtual machine"属性。对于IO 
                        mode属性,在target上选中"Yield CPU on 
                        poll"复选择框,development机不选。这样,可以无需附加任何硬件,利用虚拟机就可以搭建kgdb调试环境。即降低了使用kgdb进行调试的硬件要求,也简化了建立调试环境的过程。</DIV><BR><A 
                        name=IDAHEAZB><B></B></A><BR><IMG height=424 alt="" 
                        src="Linux 系统内核的调试.files/image002.jpg" width=600 
                        border=0><BR>
                        <DIV><B>2.3.2 VMware的使用技巧</B></DIV>
                        <DIV>VMware虚拟机是比较占用资源的,尤其是象上面那样在Windows中使用两台虚拟机。因此,最好为系统配备512M以上的内存,每台虚拟机至少分配128M的内存。这样的硬件要求,对目前主流配置的PC而言并不是过高的要求。出于系统性能的考虑,在VMware中尽量使用字符界面进行调试工作。同时,Linux系统默认情况下开启了sshd服务,建议使用SecureCRT登陆到Linux进行操作,这样可以有较好的用户使用界面。</DIV>
                        <DIV><B>2.3.3 在Linux下的虚拟机中使用kgdb</B></DIV>
                        <DIV>对于在Linux下面使用VMware虚拟机的情况,笔者没有做过实际的探索。从原理上而言,只需要在Linux下只要创建一台虚拟机作为target机,开发机的工作可以在实际的Linux环境中进行,搭建调试环境的过程与上面所述的过程类似。由于只需要创建一台虚拟机,所以使用Linux下的虚拟机搭建kgdb调试环境对系统性能的要求较低。(vmware已经推出了Linux下的版本)还可以在development机上配合使用一些其他的调试工具,例如功能更强大的cgdb、图形界面的DDD调试器等,以方便内核的调试工作。</DIV><BR><A 
                        name=IDA0EAZB><B></B></A><BR><IMG height=463 alt="" 
                        src="Linux 系统内核的调试.files/image004.jpg" width=527 
                        border=0><BR>
                        <DIV><A name=IDAGFAZB>2.4 kgdb的一些特点和不足</A></DIV>
                        <DIV>使用kgdb作为内核调试环境最大的不足在于对kgdb硬件环境的要求较高,必须使用两台计算机分别作为target和development机。尽管使用虚拟机的方法可以只用一台PC即能搭建调试环境,但是对系统其他方面的性能也提出了一定的要求,同时也增加了搭建调试环境时复杂程度。另外,kgdb内核的编译、配置也比较复杂,需要一定的技巧,笔者当时做的时候也是费了很多周折。当调试过程结束后时,还需要重新制作所要发布的内核。使用kgdb并不能进行全程调试,也就是说kgdb并不能用于调试系统一开始的初始化引导过程。</DIV>
                        <DIV>不过,kgdb是一个不错的内核调试工具,使用它可以进行对内核的全面调试,甚至可以调试内核的中断处理程序。如果在一些图形化的开发工具的帮助下,对内核的调试将更方便。</DIV><BR>
                        <TABLE cellSpacing=0 cellPadding=0 width="100%" 
border=0>
                          <TBODY>
                          <TR>
                            <TD><IMG height=1 alt="" 
                              src="Linux 系统内核的调试.files/blue_rule.gif" 
                              width="100%"></TD></TR></TBODY></TABLE><BR><BR>
                        <DIV><A name=IDANFAZB>3. 使用SkyEye构建Linux内核调试环境</A></DIV>

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?