0%

linux-kallsyms

在2.6版的内核中,为了更方便的调试内核代码,开发者考虑将内核代码中所有函数以及所有非栈变量的地址抽取出来,形成是一个简单的数据块(data blob:符号和地址对应),并将此链接进 vmlinux 中去。即模块kernel.kallsyms

在需要的时候,内核就可以将符号地址信息以及符号名称都显示出来,方便开发者对内核代码的调试。完成这一地址抽取+数据快组织封装功能的相关子系统就称之为 kallsyms

反之,如果没有 kallsyms 的帮助,内核只能将十六进制的符号地址呈现给外界,因为它能理解的只有符号地址,并不能显示各种函数名等符号

kallsyms 抽取了内核用到的所有函数地址(全局的、静态的)和非栈数据变量地址,生成一个数据块,作为只读数据链接进kernel image,相当于内核中存了一个System.map

配置kallsyms

make menuconfig
General setup  --->
    [*] Configure standard kernel features (for small systems)  --->
        [*]   Load all symbols for debugging/ksymoops
        [ ]     Include all symbols in kallsyms
        [ ]     Do an extra kallsyms pass

内核启动之后会解析uImage形成/proc/kallsyms

/proc/kallsyms包含了内核中的函数符号(包括没有EXPORT_SYMBOL)、全局变量(用EXPORT_SYMBOL导出的全局变量)

如果需要景内核中的函数、全局变量、静态变量都导出到/proc/kallsyms 需要配置 Include all symbols in kallsyms

解析

$ less /proc/kallsyms
000000000000a018 D per_cpu__xen_vcpu
000000000000a020 D per_cpu__xen_vcpu_info
000000000000a060 d per_cpu__mc_buffer
000000000000b570 D per_cpu__xen_mc_irq_flags
000000000000b578 D per_cpu__xen_cr3
000000000000b580 D per_cpu__xen_current_cr3
000000000000b5a0 d per_cpu__xen_runstate
000000000000b5e0 d per_cpu__xen_runstate_snapshot
000000000000b610 d per_cpu__xen_residual_stolen
000000000000b618 d per_cpu__xen_residual_blocked
000000000000b620 d per_cpu__xen_clock_events
000000000000b6a0 d per_cpu__xen_debug_irq
000000000000b6a4 d per_cpu__xen_resched_irq
000000000000b6a8 d per_cpu__xen_callfunc_irq
000000000000b6ac d per_cpu__xen_callfuncsingle_irq

第一列为符号地址,第二列为类型,第三列为符号名

大写符号为全局,类型如下:

  • A = Absolute
  • B = Uninitialised data (.bss)
  • C = Comonsymbol
  • D = Initialised data
  • G = Initialised data for small objects
  • I = Indirectreference to another symbol
  • N = Debugging symbol
  • R = Readonly
  • S = Uninitialised data for small objects
  • T = Textcode symbol
  • U = Undefined symbol
  • V = Weaksymbol
  • W = Weaksymbol
  • Corresponding small letters are local symbols

更多的符号定义 man nm

内核符号表的生成和查找

因为有kallsyms的存在可以使得内核可以在运行过程中随时获得一个符号地址对应的符号名。而内核代码中可以通过 printk("%pS\n", addr) 打印符号名

System.map 是编译内核时生成的,它记录了内核中的符号列表,以及符号在内存中的虚拟地址。这个文件通过 nm 命令生成,具体可参考内核目录下的 scripts/mksysmap 脚本

Ref

  1. 内核符号表的生成和查找过程