如何使用Linux内核模块中的地址转储/列出所有内核符号?

Win*_*ser 1 kernel-module linux-kernel

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

我知道接口中的“ cat / proc / kallsyms”,但是如何使用诸如之类的函数直接从内核数据结构中获取它们kallsyms_lookup_name

Sam*_*nko 6

工作模块代码:

#include <linux/module.h>
#include <linux/kallsyms.h>

static int prsyms_print_symbol(void *data, const char *namebuf,
                               struct module *module, unsigned long address)
{
    pr_info("### %lx\t%s\n", address, namebuf);
    return 0;
}

static int __init prsyms_init(void)
{
    kallsyms_on_each_symbol(prsyms_print_symbol, NULL);
    return 0;
}

static void __exit prsyms_exit(void)
{
}

module_init(prsyms_init);
module_exit(prsyms_exit);

MODULE_AUTHOR("Sam Protsenko");
MODULE_DESCRIPTION("Module for printing all kernel symbols");
MODULE_LICENSE("GPL");
Run Code Online (Sandbox Code Playgroud)

说明

kernel / kallsyms.c实现/proc/kallsyms。它的某些功能可供外部使用。它们通过EXPORT_SYMBOL_GPL()宏导出。是的,您的模块应具有GPL使用许可。这些功能是:

  • kallsyms_lookup_name()
  • kallsyms_on_each_symbol()
  • sprint_symbol()
  • sprint_symbol_no_offset()

要使用这些功能,请包含<linux/kallsyms.h>在模块中。应该提到的是,CONFIG_KALLSYMS必须=y在内核配置中启用()。

要打印所有符号,您显然必须使用kallsyms_on_each_symbol()函数。文档接下来会说:

/* Call a function on each kallsyms symbol in the core kernel */
int kallsyms_on_each_symbol(int (*fn)(void *, const char *, struct module *,
                            unsigned long), void *data);
Run Code Online (Sandbox Code Playgroud)

在哪里fn应该为找到的每个符号调用您的回调函数,并且data是指向您的一些私有数据的指针(将作为第一个参数传递给您的回调函数)。

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

int fn(void *data, const char *namebuf, struct module *module,
       unsigned long address);
Run Code Online (Sandbox Code Playgroud)

将使用以下参数为每个内核符号调用此函数:

  • data:将包含指向您作为最后一个参数传递给您的私人数据的指针 kallsyms_on_each_symbol()
  • namebuf:将包含当前内核符号的名称
  • module:永远是NULL,只要忽略那个
  • address:将包含当前内核符号的地址

返回值应始终为0(在非零返回值时,通过符号进行的迭代将被中断)。

补充性

回答您评论中的问题。

另外,有没有一种方法可以输出每个函数的大小?

是的,您可以使用sprint_symbol()我上面提到的功能来做到这一点。它将以以下格式打印符号信息:

symbol_name+offset/size [module_name]
Run Code Online (Sandbox Code Playgroud)

例:

psmouse_poll+0x0/0x30 [psmouse]
Run Code Online (Sandbox Code Playgroud)

如果符号是内置的,则可以省略模块名称部分。

我尝试了该模块,并使用“ dmesg”查看了结果。但是缺少许多符号,例如“ futex_requeue”。输出符号数约为10K,而当我使用“ nm vmlinux”时,输出符号数为100K。

这很可能是因为您的printk缓冲区大小不足以存储上面模块的所有输出。

让我们对上述模块进行一些改进,以便它通过miscdevice提供符号信息。另外,我们还根据要求将函数大小添加到输出中。代码如下:

symbol_name+offset/size [module_name]
Run Code Online (Sandbox Code Playgroud)

这是如何使用它:

$ sudo insmod prsyms2.ko
$ sudo cat /dev/prsyms2 >symbols.txt
$ wc -l symbols.txt
$ sudo rmmod prsyms2
Run Code Online (Sandbox Code Playgroud)

文件symbols.txt将以以下格式包含所有内核符号(内置和来自加载的模块):

ffffffffc01dc0d0    psmouse_poll+0x0/0x30 [psmouse]
Run Code Online (Sandbox Code Playgroud)

看来我可以kallsyms_lookup_name()用来查找函数的地址,然后可以使用函数指针来调用该函数吗?

是的你可以。如果我没记错的话,那就是反射。以下是如何执行此操作的示例:

psmouse_poll+0x0/0x30 [psmouse]
Run Code Online (Sandbox Code Playgroud)

  • 看起来这个答案需要针对 Linux 5.7 进行相当大的修改:https://lwn.net/Articles/813350/ (3认同)