linux 单步启动_linux内核2.6.29下的kgdb调试

Date : Sunday, July 20 2008

* 简介

从 2.6.25 开始,Linux 主干内核开始内置了代码级调试器 kgdb。通过 kgdb,开发者就可以在内核代码中设置断点,单步调试和观察变量。为了使用 kgdb,你需要有两个系统。一个作为开发系统,一个作为测试系统嗯。两台机器通过串口线连接。需要调试的内核运行在测试己其上。串口线用于 gdb 连接远程目标。kgdb 已经还可以支持通过以太网连接两台机器,不过目前内核中(2.6.26)还没有这部分代码。

本文通过 qemu 模拟器运行 2.6.26 的 内核作为测试系统,并通过 qemu 的虚拟TCP串口设备功能,使开发系统的 gdb 可以通过连接本地 tcp 端口调试内核。本文使用的开发系统:

Fedora 9, 2.6.25.10-86.fc9.i686

QEMU PC emulator version 0.9.1, Copyright (c) 2003-2008 Fabrice Bellard

GNU gdb Fedora (6.8-11.fc9)

gcc (GCC) 4.3.0 20080428 (Red Hat 4.3.0-8)

注:当使用 qemu 的时候, 可以直接使用 qemu -s -S 功能调试,不需要 KGDB 的支持,这里的例子是模拟一个类似调试真实平台的例子。

* 配置和启动

1. 内核选项

kgdb 选项 :

CONFIG_KGDB=y

CONFIG_KGDB_SERIAL_CONSOLE=y

选项在 Kernel hacking 里可以找到

同时,为了能在系统运行时中断系统并出发远程 gdb,必须打开内核 Magic Sys-Rq 键选项 :

CONFIG_MAGIC_SYSRQ=y

打开内核符号调试:

CONFIG_DEBUG_INFO=y

注意事项:

CONFIG_DEBUG_RODATA 必须被禁用,否则kgdb讲无法正常使用。

2. 启动 qemu

代码:qemu ${QEMU_ARGS} \

-kernel "$VMLINUZ" \

-append "${KERNEL_CMDLINE} kgdboc=ttyS0,115200 kgdbwait" \

-serial tcp::${KGDB_TCPPORT},server \

-hda "${QEMU_HDA}" >/dev/null &qemu 选项中:

-kernel 编译好的内核,压缩过的

-append 传给 kernel 的命令行选项

-serial 使系统的第一个串口与本地的 TCP 端口关联起来,用于模拟终端,这样gdb就可以连接到目标系统而不用串口线了。

-hda 用于运行开发系统的Linux系统qemu硬盘。你也可以在用 nfs root 启动系统,但qemu必须要求你指定至少一个qemu硬盘,你可以使用空的硬盘,用qemu-img工具创建。

kernel 选项中:

kgdboc 的意思是 kgdb over console,这里将kgdb连接的console设置为ttyS0,波特率为115200

kgdbwait 使 kernel 在启动过程中等待 gdb 的连接。

3. gdb 连接测试系统

qemu 启动后会等待 gdb 连接 tcp 端口以启动模拟串口。

运行下面的命令启动gdb,如果没设置KGDB_GDB变量,默认用 gdbtui 启动,其中 VMLINUX 是未压缩的 kernel 文件。

${KGDB_GDB:-gdbtui} $VMLINUX

设置波特率

(gdb) set remotebaud 115200

连接目标

(gdb) target remote tcp:localhost:${KGDB_TCPPORT}

连接成功后由于启动了 kgdbwait 选项,kgdb 会在内核启动的某个阶段得到控制权:

代码:Remote debugging using tcp:localhost:4555

kgdb_register_io_module (new_kgdb_io_ops=)

at /data/home/hellwolf/mydoc/prog/linux/linux-2.6/kernel/kgdb.c:1674

(gdb)现在就可以开始使用 kgdb 调试内核了。

* 基本功能

1. 杀死 gdb

如果 gdb 遇到问题无法相应了,可以在测试系统上运行下列命令中断调试:

代码:echo -e "\003" > /dev/ttyS1如果你重启系统了,gdb 也需要重新连接系统。

2. 中断测试系统

为了在测试系统运行的时候让 gdb 获得控制权,需要使用 Sys-Rq。关于 Sys-Rq 的详细设置方法,见 Documentation/sysrq.txt。 你可以在系统运行时键盘上输入 Sys Rq+g,然后 gdb 就会出现:

代码:Program received signal SIGTRAP, Trace/breakpoint trap.

[Switching to Thread -1]

sysrq_handle_gdb (key=103, tty=0xc7869800)

at /data/home/hellwolf/mydoc/prog/linux/linux-2.6/kernel/kgdb.c:1674

(gdb)如果你不知道如何在键盘上输入,或者你需要程序控制,则可以用下列等价命令模拟:

代码:# echo g > /proc/sysrq-trigger3. 继续执行测试系统

代码:(gdb) c即可

4. 设置断点,查询变量,单步调试,和调用堆栈

这些都和调试普通程序一样,比如:

代码:(gdb) bsys_open

Breakpoint 1 at 0xc046d268: file /data/home/hellwolf/mydoc/prog/linux/linux-2.6/

fs/open.c, line 1107.

(gdb) c

Continuing.

[New Thread 612]

[Switching to Thread 612]

Breakpoint 1, sys_open (filename=0xb7f68a9c "/etc/ld.so.cache", flags=0,

mode=0) at /data/home/hellwolf/mydoc/prog/linux/linux-2.6/fs/open.c:1107

(gdb) p filename

$1 = 0xb7f68a9c "/etc/ld.so.cache"

(gdb)n

(gdb)bt

#0 sys_open (filename=0xb7f68a9c "/etc/ld.so.cache", flags=0, mode=0)

at /data/home/hellwolf/mydoc/prog/linux/linux-2.6/fs/open.c:1113

#1 0xc0403976 in system_call ()

at /data/home/hellwolf/mydoc/prog/linux/linux-2.6/arch/x86/kernel/entry_32.S

:377

#2 0x00000000 in ?? ()5. 局限性

kgdb 不支持 tracepoints 和 watchpoints,除非有硬件调试器支持。

kgdb 的 threads 也无法正常使用。

* GDB 脚本

gdb 可以定义 sequences 脚本, 功能局限,但是可以用来简化一些工作。比如在 gdb 调试状态下 lsmod 的脚本:

代码:set $_lsmod_modules = modules->next

# 1 10 15

printf "Module Size\n"

while $_lsmod_modules != &modules

set $_lsmod_module = (struct module*)((unsigned char*)$_lsmod_modules-(unsigned int)&(((struct module*)0)->list))

printf "%-15s %d\n", $_lsmod_module->name, $_lsmod_module->init_size + $_lsmod_module->core_size

set $_lsmod_modules = $_lsmod_modules->next

end用 source 命令可以读入外部的 gdb 命令序列文件。

gdb 脚本最大的局限性在于,没有内置的字符串比较功能,比如我要得到一个(struct module*),我必须遍历模块列表然后做字符串匹配。这里我用了一个比较特殊的解决办法,我定义了两个gdb sequences :

代码:define xsrun

if $argc == 0

printf "Usage : xsrun xsourcecmd args...\n"

else

shell rm -f /tmp/*.xsource

shell echo 0 > /tmp/seq.xsource

if $argc == 1

xsource '$arg0'

end

if $argc == 2

xsource '$arg0' '$arg1'

end

if $argc == 3

xsource '$arg0' '$arg1' '$arg2'

end

if $argc == 4

xsource '$arg0' '$arg1' '$arg2' '$arg3'

end

if $argc == 5

xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4'

end

if $argc == 6

xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5'

end

if $argc == 7

xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5' '$arg6'

end

if $argc == 8

xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5' '$arg6' '$arg7'

end

if $argc == 9

xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5' '$arg6' '$arg7' '$arg8'

end

if $argc == 10

xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5' '$arg6' '$arg7' '$arg8' '$arg9'

end

end

end

define xsource

if $argc == 1

shell $TOOLSDIR/kgdb-xsource '$arg0'

end

if $argc == 2

shell $TOOLSDIR/kgdb-xsource '$arg0' '$arg1'

end

if $argc == 3

shell $TOOLSDIR/kgdb-xsource '$arg0' '$arg1' '$arg2'

end

if $argc == 4

shell $TOOLSDIR/kgdb-xsource '$arg0' '$arg1' '$arg2' '$arg3'

end

if $argc == 5

shell $TOOLSDIR/kgdb-xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4'

end

if $argc == 6

shell $TOOLSDIR/kgdb-xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5'

end

if $argc == 7

shell $TOOLSDIR/kgdb-xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5' '$arg6'

end

if $argc == 8

shell $TOOLSDIR/kgdb-xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5' '$arg6' '$arg7'

end

if $argc == 9

shell $TOOLSDIR/kgdb-xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5' '$arg6' '$arg7' '$arg8'

end

if $argc == 10

shell $TOOLSDIR/kgdb-xsource '$arg0' '$arg1' '$arg2' '$arg3' '$arg4' '$arg5' '$arg6' '$arg7' '$arg8' '$arg9'

end

if $argc > 0

source /tmp/current.xsource

end

end其中 $TOOLSDIR/kgdb-xsource 是我自定义的一个寻找名字为 '$arg0' 的 xsource 脚本的工具,我的实现就是在某个目录下找名字为 $arg0 的文件:

代码:$ cat tools/kgdb-xsource

#!/bin/bash

. $(dirname "$0")/common.sh

if [ "$#" == 0 ];then

echo "kgdb-xsource xsourcecmd args...\\n\"" > /tmp/current.xsource

else

XSOURCECMD=$1

shift

XSOURCESEQ=$(cat /tmp/seq.xsource)

XSOURCESEQ=$((${XSOURCESEQ}+1))

echo ${XSOURCESEQ} > /tmp/seq.xsource

XSOURCEOFILE=/tmp/${XSOURCESEQ}-${XSOURCECMD}.xsource

if [ -f "$XSOURCEDIR/$XSOURCECMD" ] && [ -x "$XSOURCEDIR/$XSOURCECMD" ];then

$XSOURCEDIR/$XSOURCECMD "$@" > ${XSOURCEOFILE}

rm -f /tmp/current.xsource

ln -s ${XSOURCEOFILE} /tmp/current.xsource

else

echo "printf \"No such xsource command : $XSOURCECMD\\n\"" > /tmp/current.xsource

fi

fi这些脚本的目的就是,通过外部脚本生成 gdb 脚本,然后再让 gdb 执行。他首先寻找 xsource 脚本,然后运行它,得到的输出(gdb sequences)保存到 /tmp 下的某个文件中,并链接为 /tmp/current.xsource, 然后让 gdb 执行改文件。

比如 strcmp_vs 脚本,这个脚本就是为了比较内部变量和一个字符串的:

代码:$ cat tools/xsource.d/strcmp_vs

#!/usr/bin/perl

if (@ARGV != 3) {

print "printf \"Usage : strcmp result var str\\n\"";

exit 1

}

($result, $var, $str) = @ARGV;

print 'if ';

$i = 0;

for (0..length($str)-1) {

$i = $_;

$c = substr($str, $i, 1);

print ' && ' if $i;

print "${var}[$i] == '$c'";

}

++$i;

print " && ${var}[$i] == '\\0'

set \$$result = 0

else

set \$$result = -1

end

";利用这个基本命令,我们现在就可以实现 getmod 功能拉:

代码:$ cat tools/xsource.d/getmod

#!/bin/sh

[ $# != 2 ] && echo "printf \"Usage : getmod modvar modname\n\"" && exit 1

MODVAR=$1

MODNAME=$2

cat next

set \$_getmod_modfound = 0

while \$_getmod_modules != &modules

set \$_getmod_module = (struct module*)((unsigned char*)\$_getmod_modules-(unsigned int)&(((struct module*)0)->list))

xsource strcmp_vs _getmod_modfound \$_getmod_module->name $MODNAME

if \$_getmod_modfound == 0

set \$$MODVAR = \$_getmod_module

set \$_getmod_modfound = 1

loop_break

end

set \$_getmod_modules = \$_getmod_modules->next

end

if \$_getmod_modfound != 1

printf "Module \\"$MODNAME\\" cannot be found\\n"

set \$$MODVAR = 0

end

EOF运行范例:

代码:(gdb) xsource getmod modvar hello

(gdb) print $modvar->sect_attrs->attrs[0]->name

$9 = 0xc7855b40 ".note.gnu.build-id"* 调试内核模块

调试内核模块需要额外的步骤,因为内核模块是动态载入的,gdb 一开始无法从内核 VMLINUX 文件中得到模块中的函数和变量地址信息。这里必须用 gdb 的 add-symbol-file 命令:

代码:add-symbol-file $MODFILE \\

$TEXT_ADDR \\

-s .data $DATA_ADDR \\

-s .bss $BSS_ADDR其中 MODFILE 是 .ko 文件, 如何获得那三个地址是这里最关键的。这里有两种方法:

1. 从 sysfs 中获得

这种方法比较简单:

代码:$ cat /sys/module/snd/sections/.text

0xf8f83000

$ cat /sys/module/snd/sections/.data

0xf8f8d040

$ cat /sys/module/snd/sections/.bss

0xf8f8e2802. 用 gdb 脚本

这个脚本先通过上面的 getmod 脚本得到(struct module*),然后从里面得到所需要的 sections 的地址,最后调用 add-symbol-file 命令。不过因为有大量的 gdb 交互,所以会比较慢

代码:$ cat tools/xsource.d/addmod2gdb

#!/bin/sh

# addmod2gdb($modname)

[ $# != 1 ] && echo "printf \"Usage : addmod2gdb modname\n\"" && exit 1

MODNAME=$1

MODFILE=$KMODSDIR/$MODNAME/${MODNAME}.ko

if [ -f $MODFILE ];then

cat

if \$_addmod2gdb_mod != 0

set \$_addmod2gdb_i = 0

set \$_addmod2gdb_c = 0

while \$_addmod2gdb_i < \$_addmod2gdb_mod->sect_attrs->nsections

if \$_addmod2gdb_c == 3

loop_break

end

xsource strcmp_vs _addmod2gdb_r \$_addmod2gdb_mod->sect_attrs->attrs[\$_addmod2gdb_i].name .text

if \$_addmod2gdb_r == 0

set \$_addmod2gdb_text = \$_addmod2gdb_mod->sect_attrs->attrs[\$_addmod2gdb_i].address

set \$_addmod2gdb_c = \$_addmod2gdb_c + 1

set \$_addmod2gdb_i = \$_addmod2gdb_i + 1

loop_continue

end

xsource strcmp_vs _addmod2gdb_r \$_addmod2gdb_mod->sect_attrs->attrs[\$_addmod2gdb_i].name .data

if \$_addmod2gdb_r == 0

set \$_addmod2gdb_data = \$_addmod2gdb_mod->sect_attrs->attrs[\$_addmod2gdb_i].address

set \$_addmod2gdb_c = \$_addmod2gdb_c + 1

set \$_addmod2gdb_i = \$_addmod2gdb_i + 1

loop_continue

end

xsource strcmp_vs _addmod2gdb_r \$_addmod2gdb_mod->sect_attrs->attrs[\$_addmod2gdb_i].name .bss

if \$_addmod2gdb_r == 0

set \$_addmod2gdb_bss = \$_addmod2gdb_mod->sect_attrs->attrs[\$_addmod2gdb_i].address

set \$_addmod2gdb_c = \$_addmod2gdb_c + 1

set \$_addmod2gdb_i = \$_addmod2gdb_i + 1

loop_continue

end

set \$_addmod2gdb_i = \$_addmod2gdb_i + 1

end

add-symbol-file $MODFILE \\

\$_addmod2gdb_text \\

-s .data \$_addmod2gdb_data \\

-s .bss \$_addmod2gdb_bss

end

EOF

else

echo "printf \"No such module file : $MODFILE\n\""

fi* 参考资料

官方网站 http://kgdb.linsyssoft.com/

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值