Linux 内核模块调试方法

Linux 内核模块调试方法

1 lsmod使用方法

1) 查看当前安装的内核模块:lsmod

# lsmod
Module              Size  Used by    Tainted: G  
mailbox_test          16384  0 
spinor_blk            49152  1
  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

Linux 内核模块调试方法_第1张图片
导入内核模块符号表:

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等几个就可以正常调试内核模块了。

你可能感兴趣的:(Linux,GDB调试)