在内核模块中,如何列出所有内核符号及其地址? 内核不应该重新编译。
我在界面中知道“cat / proc / kallsyms”,但是如何使用kallsyms_lookup_name
之类的函数直接从内核数据结构中获取它们。
答案 0 :(得分:4)
工作模块代码:
#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
许可证才能使用它。这些功能是:
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);
其中fn
是您应该为找到的每个符号调用的回调函数,data
是指向您的某些私有数据的指针(将作为第一个参数传递给您的回调函数)。
回调函数必须具有下一个签名:
int fn(void *data, const char *namebuf, struct module *module,
unsigned long address);
将使用下一个参数为每个内核符号调用此函数:
data
:将包含指向您作为kallsyms_on_each_symbol()
的最后一个参数传递的私人数据的指针namebuf
:将包含当前内核符号的名称module
:永远是NULL
,只是忽略address
:将包含当前内核符号的地址返回值应始终为0
(在非零返回值上,符号的迭代将被中断)。
回答评论中的问题。
另外,有没有办法输出每个函数的大小?
是的,你可以使用我上面提到的sprint_symbol()
功能来做到这一点。它将以下一格式打印符号信息:
symbol_name+offset/size [module_name]
示例:
psmouse_poll+0x0/0x30 [psmouse]
如果符号是内置的,则可以省略模块名称部分。
我尝试了模块并使用&#34; dmesg&#34;查看结果。但是缺少很多符号,例如&#34; futex_requeue&#34;。输出符号数约为10K,而使用&#34; nm vmlinux&#34;时为100K。
这很可能是因为您的printk buffer size不足以存储上述模块的所有输出。
让我们稍微改进一下模块,因此它通过miscdevice提供符号信息。还可以根据要求将功能大小添加到输出中。代码如下:
#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
将包含下一格式的所有内核符号(内置和已加载模块):
ffffffffc01dc0d0 psmouse_poll+0x0/0x30 [psmouse]
似乎我可以使用kallsyms_lookup_name()
来查找函数的地址,然后可以使用函数指针来调用函数吗?
是的,你可以。如果我没记错的话,它被称为reflection。以下是如何执行此操作的示例:
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");