现在的位置: 首页 > 综合 > 正文

内核符号表和kallsyms

2018年01月16日 ⁄ 综合 ⁄ 共 2904字 ⁄ 字号 评论关闭

版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://www.blogbus.com/wanderer-zjhit-logs/172382425.html

1 内核符号表(kernel symbol table)作用
变量名或者函数名组成,每一项是符号和地址的序对,就像域名和ip地址,格式如下:
[root@rx6600 boot]# head System.map
000000000479c4a0 A phys_start
a000000000000600 A __start_gate_mckinley_e9_patchlist
a000000000000604 A __end_gate_mckinley_e9_patchlist
a000000000000604 A __end_gate_vtop_patchlist
a000000000000604 A __start_gate_fsyscall_patchlist
a000000000000604 A __start_gate_vtop_patchlist
对于系统的oop消息、或者通过gdb的调试消息,都需要根据该对照表,将内核熟悉的函数地址转化为用户熟悉的函数名称,便于用户进行故障定位、运行监控。
2 内核符号表存储位置
2.1 System.map
盘中真实存在的文件,存储内核中静态编译的函数和变量地址,每个新编译内核对应一个System.map文件,klogd输出内核消息时,会通过/boot/System.map来将函数、变量地址转换为名称,方便用户理解。该文件对应不同的编译内核有对应的实现文件。
2.2 /proc/kallsyms
内核启动时候创建,共oops时定位错误,文件大小总为0,包含当前内核导出的、可供使用的变量或者函数
相似点:都是内核函数、变量的符号表,结构一致;对于可导出的内核变量、函数,其运行时在物理内存中的位置是一样的。
区别:
两者侧重点不同,System.map文件面向内核,对于内核中的没有导出的变量或者函数名,比如kthread_create_list链表头指针,也有其相应的内核地址,该文件一般是只读的、固定大小的,没有动态添加模块中的变量、函数名;而System.map在内核启动过程中创建,并实时更新,反映的是系统的当前最新情况,其内部也包含内核或者是已加载模块导出的函数、变量名称。所以和System.map文件有差别,且文件动态变化,大小不固定。
注:/proc/kmsg文件保存了内核从最开始启动到正常运行时的所有内核输出消息,是内核在运行过程中通过printk输出的
    如果klogd启动,klogd读取/proc/kmsg文件的内容,然后通过syslogd程序,写到/var/log/messages文件中,当然,syslogd可以通过syslogd.conf文件进行配置。
     利用dmesg,其实也是读取/proc/kmsg文件内容,然后显示到终端。
     dmesg和klogd都是利用了System.map文件将内核地址转化为对应的函数名称,方便用户调试
     在内核运行出现问题时,一般由于引用了一个无效指针造成的oops错误,如果在应用层,一般应用程序不可能从段错误(即引用无效地址)中恢复,但是由于内核稳定性比较高,一般只是会将该内核模块杀死,并使系统维持在一个稳定状态;如果出现更严重情况,即内核出现panic,就会宕机重启。

3 /proc/kallsyms文件分析 
查看kallsyms导出的符号类型,共有下面几种:
[root@rx6600 createKernel]# cat /proc/kallsyms|awk '{print $2}'|sort|uniq

a
A
b
d
D
g
r
s
S
t
T
u
W
3.1 分析./scripts/mksysmap
系统根据
该脚本文件是生成/System.map文件,kallsyms文件在启动时根据System.map形成

# $NM produces the following output:
# f0081e80 T alloc_vfsmnt
#   The second row specify the type of the symbol:
#   A = Absolute 绝对
#   B = Uninitialised data (.bss) 未初始化数据
#   C = Comon symbol  
#   D = Initialised data 
#   G = Initialised data for small objects
#   I = Indirect reference to another symbol
#   N = Debugging symbol
#   R = Read only
#   S = Uninitialised data for small objects
#   T = Text code symbol  代码段,定义的函数名称
#   U = Undefined symbol  引用外部的函数名称
#   V = Weak symbol
#   W = Weak symbol
#   Corresponding small letters are local symbols

# For System.map filter away:
#   a - local absolute symbols
#   U - undefined global symbols
#   N - debugging symbols
#   w - local weak symbols

# readprofile starts reading symbols when _stext is found, and
# continue until it finds a symbol which is not either of 'T', 't',
# 'W' or 'w'. __crc_ are 'A' and placed in the middle
# so we just ignore them to let readprofile continue to work.
# (At least sparc64 has __crc_ in the middle).

$NM -n $1 | grep -v '\( [aNUw] \)\|\(__crc_\)\|\( \$[adt]\)' > $2 该脚本实际上执行的命令
实质上,该文件由NM生成,所以man nm得到:
符号类型:大写为全局符号,小写为局部符号
A:该符号的值是不能改变的,等于const
B:该符号来自于未初始化代码段bss段
C: 该符号是通用的,通用的符号指未初始化的数据。当链接时,多个通用符号可能对应一个名称,如果该符号在某一个位置定义,这个通用符号被当做未定义的引用。不明白,内核中也没有该类型的符号
D: 该符号位于初始化的数据段
G: 位于初始化数据段,专门对应小的数据对象,比如global int x,对应的大数据对象为 数组类型等
I: 到其他符号的间接引用,是对于a.out文件的GNU扩展,使用非常少
N:调试符号
R:只读代码段的符号
S:BSS段(未初始化数据段)的小对象符号
T:代码段符号,全局函数,t为局部函数
U:未定义的符号
V:该符号是一个weak object,当其连接到为定义的对象上上,该符号的值变为0
W: 类似于V
—: 该符号是a.out文件中的一个stabs symbol,获取调试信息
?: 未知类型的符号

 

抱歉!评论已关闭.