获取内核符号地址或符号名

获取内核符号地址或符号名

Posted by Albert on September 10, 2020

获取内核符号地址或符号名

前言

​ 为什么要去学习这个内容呢,之前在讲解netfilter函数丢包定位的时候讲到过,在分析网络协议栈的问题的时候,经常出现netfilter钩子函数丢包的情况,特别是我们自己在内核中注册了大量钩子函数的时候,然而钩子函数是通过遍历链表的方式执行,这个时候我们通过栈回溯无法知道是哪一个具体函数丢弃了数据包,这个时候我们可以先打印出函数指针的地址。然后再去匹配函数的符号。之前的文章中我们是通过addr2line工具来排查的。如果netfilter钩子是在内核模块中的我们需要遍历所有的模块才可以将函数名字找出来,这种方法比较低效。这里我们讲解另外一种找内核函数符号或者地址的方式。

一、已知内核符号地址,获取内核符号名

1.1 使用sprint_symbol内核函数

#include <linux/kallsyms.h>

int sprint_symbol(char *buffer, unsigned long address)

函数功能描述:

该函数根据一个内存中的地址address查找一个内核符号,并将该符号的基本信息,如符号名name,它在内核符号表中的偏移offset和大小size ,所属的模块名(如果有的话)等信息连接成字符串赋值给文本缓冲区buffer其中所查找的内核符号可以是原本就存在于内核中的符号,也可以是位于动态插入的模块中的符号

输入参数说明:

buffer :文本缓冲区,它用来记录内核符号的信息,它是一个输出型参数。

address:内核符号中的某一地址,为输入型参数。

返回参数说明:

返回值是一个int型,它表示内核符号基本信息串的长度,也即是buffer所表示的字符串的长度。

二、已知内核符号,获取内核符号地址

2.1 使用 kallsyms_lookup_name()

​ 该函数在kernel/kallsyms.c文件中定义的,要使用它必须启用CONFIG_KALLSYMS编译内核。 kallsyms_lookup_name()接受一个字符串格式内核函数名,返回那个内核函数的地址。例如:kallsyms_lookup_name("函数名");

三、其他方式

3.1 利用System.map

$ grep “函数名或地址” /usr/src/linux/System.map

​ 这种方式的缺点是,只能查找编译进内核的符号或者地址,以及用EXPORT导出的函数或者地址。无法查找模块的符号或者地址,如果要查找内核符号的模块和地址需要使用nm查找: nm module_name | grep "函数名或者地址",当我们不知道地址是属于哪一个模块的时候,需要遍历所有的模块,来查找对应的函数。

3.2 使用nm 命令

$ nm vmlinuz | grep “函数名或地址”

3.3 、利用 /proc/kallsyms

$ cat /proc/kallsyms | grep “函数名或地址”

其中所查找的内核符号可以是原本就存在于内核中的符号,也可以是位于动态插入的模块中的符号。所以我们在netfilter函数丢包定位中,只要打印出了函数地址,然后执行cat /proc/kallsyms | grep “打印出的地址”,就可以定位到丢包的函数符号以及该函数属于哪一个内核模块。剩下的就是分析具体的函数了。