How to dump/list all kernel symbols with addresses from Linux kernel module?

三世轮回 提交于 2019-12-22 00:23:23

问题


In a kernel module, how to list all the kernel symbols with their addresses? The kernel should not be re-compiled.

I know "cat /proc/kallsyms" in an interface, but how to get them directly from kernel data structures, using functions like kallsyms_lookup_name.


回答1:


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");

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()

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.

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);

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: will contain pointer to your private data you passed as last argument to kallsyms_on_each_symbol()
  • namebuf: will contain name of current kernel symbol
  • module: will always be NULL, just ignore that
  • address: will contain address of current kernel symbol

Return value should always be 0 (on non-zero return value the iteration through symbols will be interrupted).

Supplemental

Answering the questions in your comment.

Also, is there a way to output the size of each function?

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]

Example:

psmouse_poll+0x0/0x30 [psmouse]

Module name part can be omitted if symbol is built-in.

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".

This is most likely because your printk buffer size is insufficient to store all the output of module above.

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");

And here is how to use it:

$ sudo insmod prsyms2.ko
$ sudo cat /dev/prsyms2 >symbols.txt
$ wc -l symbols.txt
$ sudo rmmod prsyms2

File symbols.txt will contain all kernel symbols (both built-in and from loaded modules) in next format:

ffffffffc01dc0d0    psmouse_poll+0x0/0x30 [psmouse]

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");


来源:https://stackoverflow.com/questions/37978245/how-to-dump-list-all-kernel-symbols-with-addresses-from-linux-kernel-module

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!