Linux 内核模块调试方法
1 lsmod使用方法
1) 查看当前安装的内核模块:lsmod
# lsmod
Module Size Used by Tainted: G
mailbox_test 16384 0
spinor_blk 49152 1
-
查看当前某个模块是否安装
# lsmod | grep "spinor_blk" spinor_blk 49152 1
2 内核模块加载方法
1) insmod使用方法
insmod加载模块,需要指定完整的路径和模块名字、依赖的模块,才可以成功的加载需要的模块,如下:
insmod /tmp/ mailbox_test.ko
2) modprobe使用方法
modprobe [-acdlrtvV][--help][模块文件][符号名称 = 符号值]
参数:
-a或--all 载入全部的模块。
-c或--show-conf 显示所有模块的设置信息。
-d或--debug 使用排错模式。
-l或--list 显示可用的模块。
-r或--remove 模块闲置不用时,即自动卸载模块。
-t或--type 指定模块类型。
-v或--verbose 执行时显示详细的信息。
-V或--version 显示版本信息。
-help 显示帮助。
安装内核模块:
modprobe –v mailbox_test
卸载内核模块:
modprobe –v –r mailbox_test
Note:mobprobe使用失败
# modprobe -v mailbox_test
modprobe: can't change directory to '/lib/modules': No such file or directory
导致该问题出现, 主要是由于我当前所使用的根文件系统为只读文件系统,未能创建'/lib/modules'目录所导致的。
3 rmmod卸载内核模块
rmmod [-wfas] [模块名]...
-v:显示指令执行的详细信息;
-f:强制移除模块,使用此选项比较危险;
-w:等待着,直到模块能够被除时在移除模块;
-s:向系统日志(syslog)发送错误信息。
如下:
rmmod mailbox_test
Note:不需要指定完成路径,只需模块名即可。
4 GDB调试内核模块
由于内核模块是单独编译的,其符号表未加入到vmlinux中,当使用GDB调试内核模块的时候,需单独的将对应的内核模块的调试信息加入到GDB中,才能精准的调试该模块。如何正确的导入内核模块的符号表信息,主要涉及到以下两个方面:
4.1 获取内核模块段信息
4.1.1 Objdump使用方法
所使用的命令是:
arm-linux-objdump
参数选项:
-a
--archive-headers
显示档案库的成员信息,类似ls -l将lib*.a的信息列出。
-C
--demangle
将底层的符号名解码成用户级名字,除了去掉所开头的下划线之外,还使得C++函数名以可理解的方式显示出来。
-g
--debugging
显示调试信息。企图解析保存在文件中的调试信息并以C语言的语法显示出来。仅仅支持某些类型的调试信息。有些其他的格式被readelf -w支持。
-e
--debugging-tags
类似-g选项,但是生成的信息是和ctags工具相兼容的格式。
-d
--disassemble
从objfile中反汇编那些特定指令机器码的section。
-D
--disassemble-all
与 -d 类似,但反汇编所有section.
--prefix-addresses
反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。
-EB
-EL
--endian={big|little}
指定目标文件的小端。这个项将影响反汇编出来的指令。在反汇编的文件没描述小端信息的时候用。例如S-records.
-f
--file-headers
显示objfile中每个文件的整体头部摘要信息。
-h
--section-headers
--headers
显示目标文件各个section的头部摘要信息。
-H
--help
简短的帮助信息。
-i
--info
显示对于 -b 或者 -m 选项可用的架构和目标格式列表。
-j name
--section=name
仅仅显示指定名称为name的section的信息
-l
--line-numbers
用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求编译时使用了-g之类的调试编译选项。
-m machine
--architecture=machine
指定反汇编目标文件时使用的架构,当待反汇编文件本身没描述架构信息的时候(比如S-records),这个选项很有用。可以用-i选项列出这里能够指定的架构.
-r
--reloc
显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇编后的格式显示出来。
-R
--dynamic-reloc
显示文件的动态重定位入口,仅仅对于动态目标文件意义,比如某些共享库。
-s
--full-contents
显示指定section的完整内容。默认所有的非空section都会被显示。
-S
--source
尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,效果比较明显。隐含了-d参数。
--show-raw-insn
反汇编的时候,显示每条汇编指令对应的机器码,如不指定--prefix-addresses,这将是缺省选项。
--no-show-raw-insn
反汇编时,不显示汇编指令的机器码,如不指定--prefix-addresses,这将是缺省选项。
--start-address=address
从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。
--stop-address=address
显示数据直到指定地址为止,该项影响-d、-r和-s选项的输出。
-t
--syms
显示文件的符号表入口。类似于nm -s提供的信息
-T
--dynamic-syms
显示文件的动态符号表入口,仅仅对动态目标文件意义,比如某些共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息。
-V
--version
版本信息
-x
--all-headers
显示所可用的头信息,包括符号表、重定位入口。-x 等价于-a -f -h -r -t 同时指定。
-z
--disassemble-zeroes
一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。
@file
可以将选项集中到一个文件中,然后使用这个@file选项载入。
4.1.2 C语言程序段的分类
- .text:已编译程序的机器代码。 .rodata:只读数据,比如printf语句中的格式串和开关(switch)语句的跳转表。
- .data:已初始化的全局C变量。局部C变量在运行时被保存在栈中,既不出现在.data中,也不出现在.bss节中。
- .bss:未初始化的全局C变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分初始化和未初始化变量是为了空间效率在:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。
- .symtab:一个符号表(symbol table),它存放在程序中被定义和引用的函数和全局变量的信息。一些程序员错误地认为必须通过-g选项来编译一个程序,得到符号表信息。实际上,每个可重定位目标文件在.symtab中都有一张符号表。然而,和编译器中的符号表不同,.symtab符号表不包含局部变量的表目。
- .rel.text:当链接噐把这个目标文件和其他文件结合时,.text节中的许多位置都需要修改。一般而言,任何调用外部函数或者引用全局变量的指令都需要修改。另一方面调用本地函数的指令则不需要修改。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非使用者显式地指示链接器包含这些信息。
- .rel.data:被模块定义或引用的任何全局变量的信息。一般而言,任何已初始化全局变量的初始值是全局变量或者外部定义函数的地址都需要被修改。
- .debug:一个调试符号表,其有些表目是程序中定义的局部变量和类型定义,有些表目是程序中定义和引用的全局变量,有些是原始的C源文件。只有以-g选项调用编译驱动程序时,才会得到这张表。
- .line:原始C源程序中的行号和.text节中机器指令之间的映射。只有以-g选项调用编译驱动程序时,才会得到这张表。
- .strtab:一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串序列。
4.1.3 内核模块段信息获取
# arm-linux-objdump --section-headers module_test.ko
Module_test.ko: file format elf32-littlearm
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000da4 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
1 .init.text 00000024 00000000 00000000 00000dd8 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
2 .exit.text 0000001c 00000000 00000000 00000dfc 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
3 .ARM.extab 00000084 00000000 00000000 00000e18 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .ARM.exidx 00000058 00000000 00000000 00000e9c 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
5 .ARM.extab.init.text 0000000c 00000000 00000000 00000ef4 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
6 .ARM.exidx.init.text 00000008 00000000 00000000 00000f00 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
7 .ARM.extab.exit.text 0000000c 00000000 00000000 00000f08 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
8 .ARM.exidx.exit.text 00000008 00000000 00000000 00000f14 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
9 .rodata 00000278 00000000 00000000 00000f1c 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
10 .modinfo 00000132 00000000 00000000 00001194 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
11 .rodata.str1.1 000002d4 00000000 00000000 000012c6 2**0
CONTENTS, ALLOC, LOAD, READONLY, DATA
12 __mcount_loc 0000002c 00000000 00000000 0000159c 2**2
CONTENTS, ALLOC, LOAD, RELOC, READONLY, DATA
13 __versions 00000bc0 00000000 00000000 000015c8 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
14 .note.gnu.build-id 00000024 00000000 00000000 00002188 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
15 __jump_table 00000018 00000000 00000000 000021b0 2**3
CONTENTS, ALLOC, LOAD, RELOC, DATA
16 .data 0000005c 00000000 00000000 000021c8 2**2
CONTENTS, ALLOC, LOAD, RELOC, DATA
17 __verbose 00000040 00000000 00000000 00002228 2**3
CONTENTS, ALLOC, LOAD, RELOC, DATA
18 .gnu.linkonce.this_module 00000200 00000000 00000000 00002280 2**6
CONTENTS, ALLOC, LOAD, RELOC, DATA, LINK_ONCE_DISCARD
19 .bss 00000008 00000000 00000000 00002480 2**2
ALLOC
20 .debug_frame 00000280 00000000 00000000 00002480 2**2
CONTENTS, RELOC, READONLY, DEBUGGING
21 .debug_info 0000defa 00000000 00000000 00002700 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
22 .debug_abbrev 00000949 00000000 00000000 000105fa 2**0
CONTENTS, READONLY, DEBUGGING
23 .debug_loc 0000067e 00000000 00000000 00010f43 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
24 .debug_aranges 00000048 00000000 00000000 000115c1 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
25 .debug_ranges 00000200 00000000 00000000 00011609 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
26 .debug_line 00001026 00000000 00000000 00011809 2**0
CONTENTS, RELOC, READONLY, DEBUGGING
27 .debug_str 00007b34 00000000 00000000 0001282f 2**0
CONTENTS, READONLY, DEBUGGING
28 .comment 0000008e 00000000 00000000 0001a363 2**0
CONTENTS, READONLY
29 .note.GNU-stack 00000000 00000000 00000000 0001a3f1 2**0
CONTENTS, READONLY
30 .ARM.attributes 0000002f 00000000 00000000 0001a3f1 2**0
CONTENTS, READONLY
涉及内核模块调试需用到的段信息是以下几个:
Arm-linux-objdump --section-headers module_test.ko | grep .text
0 .text 00000da4 00000000 00000000 00000034 2**2
Arm-linux-objdump --section-headers module_test.ko | grep .text
9 .rodata 00000278 00000000 00000000 00000f1c 2**2
16 .data 0000005c 00000000 00000000 000021c8 2**2
Arm-linux-objdump --section-headers module_test.ko | grep .bss
19 .bss 00000008 00000000 00000000 00002480 2**2
4.1.4 内核模块的加载地址查询
内核模块的信息主要保存在/proc/modules中,如下:
cat /proc/modules
modules_test 16384 0 - Live 0xbf013000
spinor_blk 49152 1 - Live 0xbf000000 (O)
0xbf013000 为module_test的加载地址(install_addr)
0xbf000000 为spinor_blk的加载地址(install_addr)
4.2 GDB导入内核模块符号表
GDB导入vmlinux符号表:
arm-none-linux-gnueabi-gdb vmlinux
导入内核模块符号表:
add-symbol-file ./drivers/mailbox/module_test.ko 0xbf013034 -s .data 0xbf0151c8 -s .bss 0xbf045480
具体使用方法:
Add-symbol-file *.ko text_addr –s .section0 section_addr0 -s .section1 section_addr1
Text_addr = install_addr + file_off
.section_addr0 = install_addr + file_off
如:.data 段
Section_addr = install_addr + file_off = 0xbf013000 + 0x21c8 = 0xbf0151c8
一般来说,正常调试内核模块所需要的段信息有.text、.data、.bss等几个就可以正常调试内核模块了。
版权声明:本文为u013836909原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。