如何从另一个模块调用导出的内核模块函数?

时间:2012-09-07 04:46:46

标签: c module kernel symbol-table

我正在编写一个API作为内核模块,为设备驱动程序提供各种功能。我在 mycode.c 中写了三个函数。然后我构建并加载了模块,然后将 mycode.h 复制到<内核> / include / linux 。在设备驱动程序中,我有一个 #include< linux / mycode.h> 并调用这三个函数。但是当我构建驱动程序模块时,我收到三个链接器警告,说明那些函数未定义

注意:

  • 函数在mycode.h中声明为 extern
  • 使用mycode.c中的 EXPORT_SYMBOL(func_name)导出函数
  • 运行命令nm mycode.ko显示符号表中可用的所有三个函数(它们旁边的大写字母T,表示符号可在文本(代码)部分找到)
  • 加载模块后,命令 grep func_name / proc / kallsyms 显示所有三个函数都已加载

很明显,函数正在正确导出,内核知道它们的位置和位置。那么为什么司机不能看到他们的定义呢?知道我错过了什么吗?


编辑:我在此处找到了一些相关信息: http://www.kernel.org/doc/Documentation/kbuild/modules.txt

  

有时,外部模块使用另一个模块中的导出符号   外部模块。 kbuild需要完全了解所有符号   避免吐出有关未定义符号的警告。三   这种情况存在解决方案。

     

注意:建议使用顶级kbuild文件的方法,但可以   在某些情况下是不切实际的。

     

使用顶级kbuild文件如果你有两个模块,foo.ko和   bar.ko,其中foo.ko需要来自bar.ko的符号,你可以使用a         常见的顶级kbuild文件,因此两个模块都在   相同的构建。请考虑以下目录布局:

  ./foo/ <= contains foo.ko       ./bar/ <= contains bar.ko

  The top-level kbuild file would then look like:

  #./Kbuild (or ./Makefile):          obj-y := foo/ bar/

  And executing

      $ make -C $KDIR M=$PWD

  will then do the expected and compile both modules with         full
     

来自任何一个模块的符号知识。

     

使用额外的Module.symvers文件构建外部模块时,   生成包含所有导出符号的Module.symvers文件   它们没有在内核中定义。要访问符号   bar.ko,从bar.ko的编译中复制Module.symvers文件   到构建foo.ko的目录。在模块构建期间,   kbuild将读取目录中的Module.symvers文件   外部模块,当构建完成时,一个新的         创建Module.symvers文件,其中包含所有符号的总和   已定义,而不是内核的一部分。

     

使用“make”变量KBUILD_EXTRA_SYMBOLS如果它不切实际   从另一个模块复制Module.symvers,可以分配一个空格   在构建文件中将文件分隔为KBUILD_EXTRA_SYMBOLS。         在初始化期间,这些文件将由modpost加载   它的符号表。

但是对于所有这三种解决方案,为了让任何驱动程序都使用我的API,它必须要么创建一个新的Makefile,要么可以直接访问我的Module.symvers文件?这看起来有点不方便。我希望他们能够#include我的头文件并且好好去。不存在其他替代方案吗?

3 个答案:

答案 0 :(得分:6)

从我的研究来看,似乎这是处理这种情况的唯一三种方法,我已经让它们各自起作用了,所以我想我会选择我最喜欢的那些。

答案 1 :(得分:1)

最小QEMU + Buildroot示例

我已在完全可重现的QEMU + Buildroot环境中对以下内容进行了测试,因此,使用此工作版本版本可以帮助您找到代码中的内容。

GitHub上游以文件为中心:dep.c | dep2.c | Makefile

dep.c:

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

int lkmc_dep = 0;
EXPORT_SYMBOL(lkmc_dep);
static struct task_struct *kthread;

static int work_func(void *data)
{
    while (!kthread_should_stop()) {
        printk(KERN_INFO "%d\n", lkmc_dep);
        usleep_range(1000000, 1000001);
    }
    return 0;
}

static int myinit(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

static void myexit(void)
{
    kthread_stop(kthread);
}

module_init(myinit)
module_exit(myexit)

dep2.c:

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

extern int lkmc_dep;
static struct task_struct *kthread;

static int work_func(void *data)
{
    while (!kthread_should_stop()) {
        usleep_range(1000000, 1000001);
        lkmc_dep++;
    }
    return 0;
}

static int myinit(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

static void myexit(void)
{
    kthread_stop(kthread);
}

module_init(myinit)
module_exit(myexit)

现在你可以这样做:

insmod dep.ko
insmod dep2.ko

但Buildroot也已经使用依赖项配置depmod /lib/module/*/depmod,所以这足以加载两者:

modprobe dep

如果您使用CONFIG_KALLSYMS_ALL=y,则可以看到符号:

grep lkmc_dep /proc/kallsyms

另见:Does kallsyms have all the symbol of kernel functions?

答案 2 :(得分:0)

好的:你有一个功能所在的模块和一个想要导入它的地方吗?

你必须在函数所在的位置使用“EXPORT_SYMBOL(”函数的名称“),例如foo。所以在”c“文件中定义了函数”foo“并放入: EXPORT_SYMBOL(富)

确保在一个公共头文件中有“foo”的原型(你可以将它放在每个模块的不同位置,它会起作用但是如果签名改变你会遇到麻烦)。所以说:void foo(void * arg);

然后另一个想要它的模块只是调用“foo”并且你很好。

另外:确保先用foo加载模块。如果你有像module2这样的交叉依赖,需要来自module1的foo,而module1需要来自module2的bar,你需要有一个寄存器功能与另一个。如果您想了解,请另外询问问题。