首页 > *nix技术, 仿真虚拟, 内核技术, 跟踪调试 > 利用UML调试内核

利用UML调试内核

2012年4月21日 发表评论 阅读评论 3,335 次浏览

利用UML调试内核的方便之处在于我们可以直接使用gdb(前面推荐过cgdb)来进行,但不方便之处在于只能调试与硬件不相干的内核代码。前面有文章描述过UML内核的编译,如果要调试UML内核,我们得选上如下两项,否则调试时将提示找不到符号:
Kernel hacking —>
[*] Compile the kernel with debug info
[*] Compile the kernel with frame pointers
既然UML内核可以当一个应用程序执行,所以和普通的gdb调试并没有什么不同,我们可以直接在gdb里运行,这个无需多说,不过需要提前设置:(gdb) set follow-fork-mode parent,因为在启动的过程中UML会进行fork操作,不做这个设置将导致调试跟丢。如果UML内核已经在执行了,那么可以通过-p来进行attach,此时需要知道UML内核进程的主线程,只有对主线程以及另外少数几个线程才能进行调试,而其它普通应用线程已经被这个主线程监控而无法再被gdb调试。获取UML内核进程的主线程可以执行如下命令:

[root@localhost ~]# ps uf | grep linux
root      2946  0.0  0.0 103200   800 pts/0    S+   15:06   0:00  \_ grep linux
root      1849  1.1  1.1 270320 44844 tty1     S+   14:49   0:12  \_ ./linux ubda=./rootfs mem=256m
root      1856  0.0  1.1 270320 44844 tty1     S+   14:49   0:00      \_ ./linux ubda=./rootfs mem=256m
root      1857  0.0  1.1 270320 44844 tty1     S+   14:49   0:00      \_ ./linux ubda=./rootfs mem=256m
root      1858  0.0  1.1 270320 44844 tty1     S+   14:49   0:00      \_ ./linux ubda=./rootfs mem=256m
root      1859  0.0  0.0   3772  1516 tty1     t+   14:49   0:00      \_ ./linux ubda=./rootfs mem=256m
root      2010  0.0  0.0   2924   500 tty1     t+   14:49   0:00      \_ ./linux ubda=./rootfs mem=256m
root      2290  0.0  0.0   2976   620 tty1     t+   14:49   0:00      \_ ./linux ubda=./rootfs mem=256m
root      2592  0.0  0.0   3400   660 tty1     t+   14:49   0:00      \_ ./linux ubda=./rootfs mem=256m
root      2624  0.0  0.0   3964  1384 tty1     t+   14:50   0:00      \_ ./linux ubda=./rootfs mem=256m
root      2626  0.0  0.0   4044  1764 tty1     t+   14:50   0:00      \_ ./linux ubda=./rootfs mem=256m

那么主线程的pid为1849,此时可执行“gdb -p 1849”开始调试,如果gdb提示“ptrace: Operation not permitted.”,那么肯定是attach错线程了:

[root@localhost ~]# gdb -p 2624
GNU gdb (GDB) Red Hat Enterprise Linux (7.1-29.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Attaching to process 2624
ptrace: Operation not permitted.
(gdb)

进入gdb后也需设置(gdb) set follow-fork-mode parent,理由前面已经说过了。
在内核函数__schedule加个断点试试,按c继续后马上被中断(这是当然的):

(gdb) c
Continuing.

Breakpoint 1, __schedule () at kernel/sched.c:5015
5015    kernel/sched.c: No such file or directory.
        in kernel/sched.c

提示没有xxx文件:kernel/sched.c: No such file or directory.,这只是因为gdb没有找到对应的源文件,利用directory命令把内核源码增加gdb的搜索路径即可:

(gdb) directory /usr/src/linux-2.6.38.8
Source directories searched: /usr/src/linux-2.6.38.8:$cdir:$cwd
(gdb)


如果是UML的模块,那么调试的准备工作有一点麻烦,首先得找到对应模块的地址:

(gdb) p modules
$1 = {next = 0x70e1f388, prev = 0x70e1f388}

由于我当前只安插了一个模块,而上面显示当前模块的列表结构头节点,所以下面直接进行偏移(64bit下指针是8字节)查看:

(gdb) p *((struct module *)0x70e1f380)
$3 = {state = MODULE_STATE_LIVE, list = {next = 0x603646f0, prev = 0x603646f0}, name = "switch", '\000' <repeats 49 times>, mkobj = {kobj = {name = 0x6fb37460 "switch", entry = {next = 0x6f828840, prev = 0x6f8c0ec8}, parent = 0x6f828850, kset = 0x6f828840, ktype = 0x60363bc0, sd =  0x6fb49140, kref = {refcount = {counter = 3}}, state_initialized = 1, state_in_sysfs = 1, state_add_uevent_sent = 1, state_remove_uevent_sent = 0, uevent_suppress
= 0}, mod = 0x70e1f380, drivers_dir = 0x0, mp =	 0x6e5d3840}, modinfo_attrs = 0x6fb8c600,
version = 0x6fb37340 "0.1", srcversion = 0x6fb37d60 "CA6E78E13F72ABAFF1CDD34", holders_dir = 0x6f8f24c0, syms = 0x70e1a1f0, crcs = 0x0, num_syms = 7, kp = 0x70e1a260, num_kp = 1,num_gpl_syms = 0, gpl_syms = 0x0, gpl_crcs = 0x0, gpl_future_syms = 0x0, gpl_future_crcs= 0x0, num_gpl_future_syms = 0, num_exentries = 0, extable = 0x0, init = 0x70e0f580,  module_init = 0x0, module_core =  0x70e0f000, init_size = 0, core_size = 429112, init_text_size = 0, core_text_size = 41664,	 arch = {<No data fields>}, taints = 0, num_bugs = 31, bug_list = {next = 0x60379f00, prev = 0x60379f00 }, bug_table = 0x70e1a2df, symtab = 0x70e1c130, num_symtab = 314, strtab = 0x70e1dea0 "", sect_attrs = 0x6f3fc800, notes_attrs =  0x6e5d3740, percpu = 0x0, args = 0x6fb373c0 "", markers = 0x0, num_markers = 0, tracepoints =0x0, num_tracepoints = 0, modules_which_use_me = {next = 0x70e1f540, prev = 0x70e1f540},waiter = 0x6f9c70c0, exit = 0x70e0f0d0, ref = {a = {counter = 0}}}

找到对应的module_core字段值,这个字段记录的是实际代码和数据地址:

struct module
{
…
	/* Here is the actual code + data, vfree'd on unload. */
	void *module_core;
…
}

给这段代码加载符号:

(gdb) add-symbol-file /home/gqk/xx/xxxx.ko 0x70e0f000 add symbol table from file "/home/gqk/xx/xxxx.ko" at.text_addr = 0x70e0f000
(y or n) y Reading symbols from /home/gqk/xx/xxxx.ko... done.

同样利用directory /home/gqk/xx/xxxx把xxxx.ko的源码加入到gdb的搜索目录,这样才能进行方便的模块代码调试。
另外,对于UML发生oops和宕机的情况,外部gdb都能捕获到,关于这点,感兴趣的实际操作一下就知道了。

转载请保留地址:http://www.lenky.info/archives/2012/04/1565http://lenky.info/?p=1565


备注:如无特殊说明,文章内容均出自Lenky个人的真实理解而并非存心妄自揣测来故意愚人耳目。由于个人水平有限,虽力求内容正确无误,但仍然难免出错,请勿见怪,如果可以则请留言告之,并欢迎来讨论。另外值得说明的是,Lenky的部分文章以及部分内容参考借鉴了网络上各位网友的热心分享,特别是一些带有完全参考的文章,其后附带的链接内容也许更直接、更丰富,而我只是做了一下归纳&转述,在此也一并表示感谢。关于本站的所有技术文章,欢迎转载,但请遵从CC创作共享协议,而一些私人性质较强的心情随笔,建议不要转载。

法律:根据最新颁布的《信息网络传播权保护条例》,如果您认为本文章的任何内容侵犯了您的权利,请以Email或书面等方式告知,本站将及时删除相关内容或链接。

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.