kgdb内核调试freebsd8.3

来源:互联网 发布:com的域名不能买永久吗 编辑:程序博客网 时间:2024/06/04 23:26

            编译内核并用KGDB调试

1. 配置vmware调试机与目标机的串口
[虚拟机上添加串口]
"power off"关闭虚拟机
右击虚拟机
-->选择"settings..."
-->hardware-->Add...
-->Serial Port
-->output to named pipe-->两虚拟机都使用名称"\\.\pipe\com_1",
对调试机使用"This end is the client",
对目标机使用"This end is the server",
两个虚拟机均使用"The other end is a virtual machine"
把Connect at power on 勾选上
-->完成后在"I/O mode" 组合框里把Yield CPU on poll勾选上

如果虚拟机是vSphere配置基本差不多,vSphere的配置更简单些

[sio驱动]
查man sio可知串口设备名

[uart驱动]
查man uart可知串口设备名

[调试串口例子]
目标:
cat /dev/tty*

调试机
echo test > /dev/cua*

在目标机上会出现test则表示串口调试成功

在远程调试开始时,您可能遇到 Ignoring packet error... 错误。
该错误表示您的开发计算机和目标计算机具有不同的波特率。计算机的波特率是指每秒传输的符号数目。波特率与比特率可能并不相同,
因为一个符号可能包含两个以上的状态。需要为两台计算机设置相同的波特率。
使用以下命令可以实现这一目标:
set baudrate 9600

2. 编译内核
注:两种方法,有的版本适合方法一,有一适合用方法二

[方法一]:
官方的教程http://www.freebsd.org/doc/handbook/kernelconfig-building.html
--<在调试机上>
cd /usr/src/sys/i386/conf
mkdir /root/kernels
cp GENERIC /root/kernels/MYKERNEL
ln -s /root/kernels/MYKERNEL
ln -s /vte/86DX
vi DEBUG_KERNEL
  加入如下内容:
      makeoptions      DEBUG=-g
      options          KDB
      options          KDB_TRACE
      options          DDB
      options          GDB

cd /usr/src
make buildkernel KERNCONF=MYKERNEL
make installkernel KERNCONF=MYKERNEL

完成上述步骤之后,在/boot下原来的kernel内核被放到/boot/kernel.old/kernel下,
安装后的内核即为/boot/kernel

[编译Freebsd8.3内核]
--<在调试机上>
cd /usr/src/sys/i386/conf
mkdir /root/kernels
cp GENERIC /root/kernels/DEBUG_KERNEL
ln -s /root/kernels/DEBUG_KERNEL

vi DEBUG_KERNEL
  加入如下内容:
      makeoptions      DEBUG=-g
      options          KDB
      options          KDB_TRACE
      options          DDB
      options          GDB

config DEBUG_KERNEL
cd ../compile/DEBUG_KERNEL
make cleandepend
make depend
make

完成上述步骤后,在调试机的当前目录下(/usr/src/sys/i386/compile/DEBUG_KERNEL)
目录下会生成kernel.debug文件夹,把该文件夹拷贝到目标机上,放到/boot/kernel/目录下,
并更名为kernel,并使用strip -x去掉其中的调试符号

3. 内核调试
--<在目标机上>
cp /boot/device.hints /boot/device.hints.bk
vi /boot/device.hints
    加入如下内容:
    如果串口是sio驱动就修改如下内容为0x80端口:
    hint.sio.0.flags="0x80"
    如果串口是uart驱动就修改如下内容为0x80端口:
    hint.uart.0.flags="0x80"
   
    如果因为freebsd版本在进入DDB模式之后会失去键盘响应,这主要是由于DDB和kbdmux共存还
    有问题导致的,解决办法是加入如下内容:
    hint.kbdmux.0.disabled="1"

reboot

选择6.Escape to loader prompt
在"OK"提示符后加入boot -d
然后再依次输入"gdb"和"s"命令,使其进入等待远端gdb调试连接的状态

--<在调试机上>
cd /usr/src/sys/i386/compile/DEBUG_KERNEL

如果是sio驱动输入如下命令
kgdb -r /dev/cuad0 kernel.debug
如果是uart驱动输入如下命令
kgdb -r /dev/cuau0 kernel.debug

稍等片刻就可以完成和目标机的链接,并可以调试.
进入调试模式后做如下断点:
(kgdb) b kldload
这一步是为今后调试可动态装载模块即非内核模块的调试方便使用

4. 用 GDB 调试可加载模块 --- 以nullfs为例
[freebsd可加载模块结构]:
    所有的可动态加载模块均存放在/usr/src/sys/modules目录对应的模块名文件夹下例如nullfs等,
    编译这些模块会自动找到代码所在的位置。

在调试发生于模块中的 panic, 或者使用远程 GDB 调试使用动态模块的机器时, 需要告诉 GDB 如何获取这些模块的符号信息。
首先, 需要在编译模块时加入调试信息:

# cd /sys/modules/nullfs
# make clean; make COPTS=-g

把开发机上编译出来的nullfs.ko拷贝到目标机上的任何位置如/tmp,在目标机上安装nullfs.ko执行命令
# cd /tmp
# kldload ./nullfs.ko

回到开发机上会发现kldload断点出现了,但这时nullfs.ko还没有被加载成功,直接执行gdb命令c继续进行
执行完后在目标机上再加载一次加载命令如下:
# kldload ./nullfs.ko

在开发机上出现断点后通过以下过程加载nullfs的符号链接信息,并具体调试,如下:

如果使用远程 GDB, 您可以在目标机上执行 kldstat 来了解模块的加载位置:
# kldstat
Id Refs Address    Size     Name
 1    4 0xc0100000 1c1678   kernel
 2    1 0xc0a9e000 6000     linprocfs.ko
 3    1 0xc0ad7000 2000     warp_saver.ko
 4    1 0xc0adc000 11000    nullfs.ko
如果您正调试内核崩溃转存数据, 则需要访问 linker_files 表, 从 linker_files->tqh_first 开始,
并沿 link.tqe_next 指针寻找包含您所查找的 filename 的项。 那个项的 address 成员就是模块的加载地址。

接下来, 您需要找出模块中代码节 (text section) 的偏移量:

# objdump --section-headers /sys/modules/nullfs/nullfs.ko | grep text
  3 .rel.text     000016e0  000038e0  000038e0  000038e0  2**2
 10 .text         00007f34  000062d0  000062d0  000062d0  2**2
您需要的是 .text 节, 在前述例子中, 是 10 号节。第四个十六进制字段 (或者说从左往右数第六个字段) 是代码节在文件中的偏移量。
将这一偏移量与模块的加载地址相加, 就可以得到模块的代码在重定位之后的地址了。
在我们的例子中, 可以得到 0xc0adc000 + 0x62d0 = 0xc0ae22d0。
接下来就可以用 add-symbol-file 来告诉
GDB 关于调试模块的信息:

(kgdb) add-symbol-file /sys/modules/nullfs/nullfs.ko 0xc0ae22d0
add symbol table from file "/sys/modules/nullfs/nullfs.ko" at text_addr = 0xc0ae22d0?
(y or n) y
Reading symbols from /sys/modules/nullfs/nullfs.ko...done.
(kgdb) l nullfs_mount
(kgdb) b nullfs_mount
(kgdb) c
现在就可以使用模块的全部符号了。

在目标机上:
# cd /mnt
# mkdir null
# mkdir nullmount
# mount_nullfs null nullmount

回到开发机上发现断点出现在nullfs_mount上,这时就可以继续调试该模块的函数了。
如果kgdb在运行中要通过执行kldload ./nullfs.ko来实现断点出现,而不是通过ctrl+c命令来实现断点出现,
后面的这个方法实验是不成功的,几乎花费了我一整天的时间摸清楚这个事情。

 

 

 

 

 

 

 


 

原创粉丝点击