如何使用Linux内核模块中的地址转储/列出所有内核符号? [英] How to dump/list all kernel symbols with addresses from Linux kernel module?
问题描述
在内核模块中,如何列出所有内核符号及其地址? 不应重新编译内核.
In a kernel module, how to list all the kernel symbols with their addresses? The kernel should not be re-compiled.
我在一个界面中知道"cat/proc/kallsyms",但是如何使用kallsyms_lookup_name
之类的函数直接从内核数据结构中获取它们.
I know "cat /proc/kallsyms" in an interface, but how to get them directly from kernel data structures, using functions like kallsyms_lookup_name
.
推荐答案
示例
工作模块代码:
Example
Working module code:
#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");
说明
kernel/kallsyms.c 实现了/proc/kallsyms
.它的某些功能可供外部使用.它们通过EXPORT_SYMBOL_GPL()
宏导出.是的,您的模块应该具有GPL
许可证才能使用它.这些功能是:
Explanation
kernel/kallsyms.c implements /proc/kallsyms
. Some of its functions are available for external usage. They are exported via EXPORT_SYMBOL_GPL()
macro. Yes, your module should have GPL
license to use it. Those functions are:
-
kallsyms_lookup_name()
-
kallsyms_on_each_symbol()
-
sprint_symbol()
-
sprint_symbol_no_offset()
kallsyms_lookup_name()
kallsyms_on_each_symbol()
sprint_symbol()
sprint_symbol_no_offset()
要使用这些功能,请在其中包含 <linux/kallsyms.h>
您的模块.应该提到的是,必须在内核配置中启用CONFIG_KALLSYMS
(=y
).
To use those functions, include <linux/kallsyms.h>
in your module. It should be mentioned that CONFIG_KALLSYMS
must be enabled (=y
) in your kernel configuration.
要打印所有符号,您显然必须使用 kallsyms_on_each_symbol()
功能.文档接下来对此进行了说明:
To print all the symbols you obviously have to use kallsyms_on_each_symbol()
function. The documentation says next about it:
/* 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);
其中fn
是应为找到的每个符号调用的回调函数,而data
是指向您的某些私有数据的指针(将作为第一个参数传递给您的回调函数).
where fn
is your callback function that should be called for each symbol found, and data
is a pointer to some private data of yours (will be passed as first parameter to your callback function).
回调函数必须具有下一个签名:
Callback function must have next signature:
int fn(void *data, const char *namebuf, struct module *module,
unsigned long address);
每个带有下一个参数的内核符号都会调用此函数:
This function will be called for each kernel symbol with next parameters:
-
data
:将包含指向您作为最后一个参数传递给kallsyms_on_each_symbol()
的私有数据的指针
-
namebuf
:将包含当前内核符号的名称 -
module
:将始终为NULL
,只需忽略 -
address
:将包含当前内核符号的地址
data
: will contain pointer to your private data you passed as last argument tokallsyms_on_each_symbol()
namebuf
: will contain name of current kernel symbolmodule
: will always beNULL
, just ignore thataddress
: will contain address of current kernel symbol
返回值应始终为0
(对于非零返回值,通过符号进行的迭代将被中断).
Return value should always be 0
(on non-zero return value the iteration through symbols will be interrupted).
回答您评论中的问题.
还可以输出每个函数的大小吗?
Also, is there a way to output the size of each function?
是的,您可以使用我上面提到的sprint_symbol()
函数来执行此操作.它将以以下格式打印符号信息:
Yes, you can use sprint_symbol()
function I mentioned above to do that. It will print symbol information in next format:
symbol_name+offset/size [module_name]
示例:
psmouse_poll+0x0/0x30 [psmouse]
如果符号是内置的,则可以省略模块名称部分.
Module name part can be omitted if symbol is built-in.
我尝试了该模块,并使用"dmesg"查看了结果.但是缺少许多符号,例如"futex_requeue".输出的符号数约为10K,而当我使用"nm vmlinux"时则为100K.
I tried the module and see the result with "dmesg". But a lot of symbols are missing such as "futex_requeue". The output symbol number is about 10K, while it is 100K when I use "nm vmlinux".
这很可能是因为您的 printk缓冲区大小不足以存储模块的所有输出以上.
This is most likely because your printk buffer size is insufficient to store all the output of module above.
让我们对上述模块进行一些改进,以便它通过 miscdevice 提供符号信息.另外,我们还根据要求将函数大小添加到输出中.代码如下:
Let's improve above module a bit, so it provides symbols information via miscdevice. Also let's add function size to the output, as requested. The code as follows:
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kallsyms.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/sizes.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#define DEVICE_NAME "prsyms2"
/* 16 MiB is sufficient to store information about approx. 200K symbols */
#define SYMBOLS_BUF_SIZE SZ_16M
struct symbols {
char *buf;
size_t pos;
};
static struct symbols symbols;
/* ---- misc char device definitions ---- */
static ssize_t prsyms2_read(struct file *file, char __user *buf, size_t count,
loff_t *pos)
{
return simple_read_from_buffer(buf, count, pos, symbols.buf,
symbols.pos);
}
static const struct file_operations prsyms2_fops = {
.owner = THIS_MODULE,
.read = prsyms2_read,
};
static struct miscdevice prsyms2_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &prsyms2_fops,
};
/* ---- module init/exit definitions ---- */
static int prsyms2_store_symbol(void *data, const char *namebuf,
struct module *module, unsigned long address)
{
struct symbols *s = data;
int count;
/* Append address of current symbol */
count = sprintf(s->buf + s->pos, "%lx\t", address);
s->pos += count;
/* Append name, offset, size and module name of current symbol */
count = sprint_symbol(s->buf + s->pos, address);
s->pos += count;
s->buf[s->pos++] = '\n';
if (s->pos >= SYMBOLS_BUF_SIZE)
return -ENOMEM;
return 0;
}
static int __init prsyms2_init(void)
{
int ret;
ret = misc_register(&prsyms2_misc);
if (ret)
return ret;
symbols.pos = 0;
symbols.buf = vmalloc(SYMBOLS_BUF_SIZE);
if (symbols.buf == NULL) {
ret = -ENOMEM;
goto err1;
}
dev_info(prsyms2_misc.this_device, "Populating symbols buffer...\n");
ret = kallsyms_on_each_symbol(prsyms2_store_symbol, &symbols);
if (ret != 0) {
ret = -EINVAL;
goto err2;
}
symbols.buf[symbols.pos] = '\0';
dev_info(prsyms2_misc.this_device, "Symbols buffer is ready!\n");
return 0;
err2:
vfree(symbols.buf);
err1:
misc_deregister(&prsyms2_misc);
return ret;
}
static void __exit prsyms2_exit(void)
{
vfree(symbols.buf);
misc_deregister(&prsyms2_misc);
}
module_init(prsyms2_init);
module_exit(prsyms2_exit);
MODULE_AUTHOR("Sam Protsenko");
MODULE_DESCRIPTION("Module for printing all kernel symbols");
MODULE_LICENSE("GPL");
这是使用方法:
$ sudo insmod prsyms2.ko
$ sudo cat /dev/prsyms2 >symbols.txt
$ wc -l symbols.txt
$ sudo rmmod prsyms2
文件symbols.txt
将包含以下格式的所有内核符号(内置和来自加载的模块):
File symbols.txt
will contain all kernel symbols (both built-in and from loaded modules) in next format:
ffffffffc01dc0d0 psmouse_poll+0x0/0x30 [psmouse]
似乎我可以使用
kallsyms_lookup_name()
查找函数的地址,然后可以使用函数指针来调用该函数?
It seems that I can use
kallsyms_lookup_name()
to find the address of the function, can then use a function pointer to call the function?
是的,可以.如果我没记错的话,它叫做反射.下面是如何执行此操作的示例:
Yes, you can. If I recall correctly, it's called reflection. Below is an example how to do so:
typedef int (*custom_print)(const char *fmt, ...);
custom_print my_print;
my_print = (custom_print)kallsyms_lookup_name("printk");
if (my_print == 0) {
pr_err("Unable to find printk\n");
return -EINVAL;
}
my_print(KERN_INFO "### printk found!\n");
这篇关于如何使用Linux内核模块中的地址转储/列出所有内核符号?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!