Linux kernel 如何从Linux内核模块转储/列出包含地址的所有内核符号?

Linux kernel 如何从Linux内核模块转储/列出包含地址的所有内核符号?,linux-kernel,kernel-module,Linux Kernel,Kernel Module,在内核模块中,如何列出所有内核符号及其地址? 不应重新编译内核 我知道接口中的“cat/proc/kallsyms”,但如何使用kallsyms\u lookup\u name等函数直接从内核数据结构获取它们呢 工作模块代码: #包括 #包括 静态int-prsyms\u-print\u符号(void*数据、常量字符*namebuf、, 结构模块*模块,无符号长地址) { pr#U信息(“###%lx\t%s\n”,地址,名称buf); 返回0; } 静态整型初始化prsyms初始化(无效) {

在内核模块中,如何列出所有内核符号及其地址? 不应重新编译内核

我知道接口中的“cat/proc/kallsyms”,但如何使用
kallsyms\u lookup\u name
等函数直接从内核数据结构获取它们呢 工作模块代码:

#包括
#包括
静态int-prsyms\u-print\u符号(void*数据、常量字符*namebuf、,
结构模块*模块,无符号长地址)
{
pr#U信息(“###%lx\t%s\n”,地址,名称buf);
返回0;
}
静态整型初始化prsyms初始化(无效)
{
每个符号上的kallsyms(打印符号,空);
返回0;
}
静态void uu退出prsyms u退出(void)
{
}
模块初始化(prsyms初始化);
模块退出(prsyms退出);
模块作者(“Sam Protsenko”);
模块描述(“用于打印所有内核符号的模块”);
模块许可证(“GPL”);
解释 实现
/proc/kallsyms
。它的一些功能可供外部使用。它们通过
EXPORT\u SYMBOL\u GPL()
宏导出。是的,您的模块应该有
GPL
许可证才能使用它。这些职能是:

  • kallsyms\u lookup\u name()
  • 每个符号上的kallsyms()
  • sprint\u symbol()
  • sprint\u symbol\u no\u offset()
要使用这些功能,请包含在模块中。应该提到的是,在内核配置中必须启用
CONFIG\u KALLSYMS
=y

要打印所有符号,显然必须对每个符号()使用
kallsyms\u函数。文档中接下来介绍了它:

/*对核心内核中的每个kallsyms符号调用一个函数*/
每个符号上的int kallsyms(int(*fn)(void*,const char*,struct module*,
无符号(长),无效*数据);
其中,
fn
是应该为找到的每个符号调用的回调函数,
data
是指向您的一些私有数据的指针(将作为第一个参数传递给回调函数)

回调函数必须具有下一个签名:

intfn(void*数据、const char*namebuf、结构模块*module、,
无符号长地址);
将为每个内核符号调用此函数,并使用以下参数:

  • data
    :将包含指向您作为最后一个参数传递给每个符号上的
    kallsyms\u()的私有数据的指针。
  • namebuf
    :将包含当前内核符号的名称
  • module
    :将始终为
    NULL
    ,只需忽略该值即可
  • 地址
    :将包含当前内核符号的地址
返回值应始终为
0
(对于非零返回值,通过符号的迭代将被中断)

补充的 回答你评论中的问题

还有,是否有方法输出每个函数的大小

是的,您可以使用上面提到的
sprint\u symbol()
函数来实现这一点。它将以下一种格式打印符号信息:

symbol_name+offset/size [module_name]
ffffffffc01dc0d0    psmouse_poll+0x0/0x30 [psmouse]
例如:

psmouse_poll+0x0/0x30 [psmouse]
如果符号是内置的,则可以省略模块名称部分

我尝试了这个模块,并用“dmesg”看到了结果。但很多符号都缺失了,比如“futex_requeue”。输出符号数约为10K,而使用“nm vmlinux”时为100K

这很可能是因为您的内存不足,无法存储上述模块的所有输出

让我们对上面的模块稍加改进,这样它就可以通过提供符号信息。另外,让我们根据请求向输出中添加函数大小。守则如下:

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#定义设备名称“prsyms2”
/*16 MiB足以存储约200K符号的信息*/
#定义符号大小SZ_16M
结构符号{
char*buf;
尺寸和位置;
};
静态结构符号;
/*----其他字符设备定义----*/
静态ssize_t prsyms2_读取(结构文件*文件,字符*用户*buf,大小\u t计数,
loff_t*pos)
{
从缓冲区返回简单的读取(buf、count、pos、symbols.buf、,
符号(pos);
}
静态常量结构文件\u操作prsyms2\u fops={
.owner=此_模块,
.read=prsyms2\u read,
};
静态结构杂项设备prsyms2_杂项={
.minor=杂项动力minor,
.name=设备名称,
.fops=&prsyms2_fops,
};
/*----模块初始化/退出定义----*/
静态整型prsyms2存储符号(void*数据,const char*namebuf,
结构模块*模块,无符号长地址)
{
结构符号*s=数据;
整数计数;
/*当前符号的附加地址*/
count=sprintf(s->buf+s->pos,“%lx\t”,地址);
s->pos+=计数;
/*追加当前符号的名称、偏移量、大小和模块名称*/
计数=冲刺符号(s->buf+s->pos,地址);
s->pos+=计数;
s->buf[s->pos++]='\n';
如果(s->pos>=符号大小)
return-ENOMEM;
返回0;
}
静态整数初始化prsyms2初始化(无效)
{
int ret;
ret=杂项寄存器(&prsyms2\u杂项);
如果(ret)
返回ret;
符号0.pos=0;
symbols.buf=vmalloc(symbols\u buf\u SIZE);
if(symbols.buf==NULL){
ret=-ENOMEM;
后藤err1;
}
dev_info(prsyms2_misc.this_设备,“填充符号缓冲区…\n”);
ret=每个符号上的所有符号(prsyms2存储符号和符号);
如果(ret!=0){
ret=-EINVAL;
后藤err2;
}
symbols.buf[symbols.pos]='\0';
dev_info(prsyms2_misc.this_设备,“符号缓冲区准备就绪!\n”);
返回0;
错误2:
vfree(symbols.buf);
错误1:
杂项注销(&prsyms2\u杂项);
返回ret;
}
静态无效退出prsyms2\U退出(无效)
{
vfree(symbols.buf);
杂项注销(&prsyms2\u杂项);
}
模数
ffffffffc01dc0d0    psmouse_poll+0x0/0x30 [psmouse]