【问题标题】:insmod fails with "Unknown symbol in module" for a symbol defined in another module对于在另一个模块中定义的符号,insmod 失败并显示“模块中的未知符号”
【发布时间】:2015-11-15 09:51:02
【问题描述】:

我在 Ubuntu 中工作。我正在尝试制作两个使用彼此功能的内核模块。我的问题是我得到了正确编译的模块,但是其中一个没有解析符号。

为简单起见,我们将这些模块称为m1m2

m2 正在导出函数void func_m2(void)m1 正在调用此函数。两个模块都能正确编译。

编译完成后,我需要先加载m2 模块(因为它已经导出了func_m2 函数),然后是m1 模块。所以,让我们来吧:

volodymyr@sv1:~/development/kmodules/m2$ sudo insmod ./m2.ko

现在,让我们加载正在尝试使用func_m2m1 模块:

volodymyr@sv1:~/development/kmodules/m1$ sudo insmod ./m1.ko
insmod: error inserting './m1.ko': -1 Unknown symbol in module

以下是我在日志中看到的:

volodymyr@sv1:~/development/kmodules/m1$ dmesg | tail
[ 3938.166616] Loading m2 module ...
[ 3963.078055] m1: no symbol version for func_m2
[ 3963.078059] m1: Unknown symbol func_m2

所以,对符号func_m2 的引用似乎没有解决。有趣的。让我们检查它是否存在于符号表中:

volodymyr@sv1:~/development/kmodules$ cat /proc/kallsyms | grep 'func_m2'
ffffffffa00530d0 r __ksymtab_func_m2    [m2]
ffffffffa00530e8 r __kstrtab_func_m2    [m2]
ffffffffa00530e0 r __kcrctab_func_m2    [m2]
ffffffffa0053000 T func_m2      [m2]
000000004edd543f a __crc_func_m2        [m2]

如您所见,func_m2 实际上存在于符号表中。那么为什么m1无法加载呢?

我已经为我的内核和 Linux 源正确安装了 Linux 头文件。我没有对内核进行任何修改,它没有被修改,它的版本是:2.6.31-16-generic (我运行 x64)

现在,为了向您展示全貌,我将我用于此测试的源代码和 Makefile 用于 m1m2 模块。

m1模块:

m1.c:

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

extern void func_m2(void);

int hello_start(void)
{
    printk(KERN_INFO "Loading m1 module ...\n");

    func_m2();

    return 0;
 }

 void hello_end(void)
 {
    printk(KERN_INFO "Unloading m1 ...\n");
 }

module_init(hello_start);
module_exit(hello_end);

MODULE_LICENSE("GPL");

生成文件:

obj-m := m1.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

m2模块:

m2.c:

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

int hello_start(void)
{
    printk(KERN_INFO "Loading m2 module ...\n");

    return 0;
}

void hello_end(void)
{
    printk(KERN_INFO "Unloading m2 ...\n");
}

void func_m2(void)
{
    printk(KERN_INFO "This a function in m2\n");
}

module_init(hello_start);
module_exit(hello_end);

MODULE_LICENSE("GPL");
EXPORT_SYMBOL(func_m2);

生成文件:

obj-m := m2.o
export-objs := m2.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

基本上我的问题是:为什么不能加载m1

如果有人能回答会很有帮助。

【问题讨论】:

标签: c linux linux-kernel kernel-module insmod


【解决方案1】:

当您构建 m2 时,它会创建一个 Module.symvers 文件。

将此文件复制到您正在构建 m1 的位置。然后制作 m1 并安装它。

您之前在构建 m1 时可能遇到过警告,例如:

警告:“func_m2”[/tmp/m1/m1.ko] 未定义!

一旦您使用 m2 模块中的 Module.symvers,这应该会消失。

来自http://www.kernel.org/doc/Documentation/kbuild/modules.txt

--- 6.2 符号和外部模块

构建外部模块时,构建系统需要访问权限 到内核​​的符号以检查是否所有外部符号 被定义。这是在 MODPOST 步骤中完成的。 modpost 获得 通过从内核源代码读取 Module.symvers 的符号 树。如果目录中存在 Module.symvers 文件 在构建外部模块的地方,这个文件将是 也读。在 MODPOST 步骤中,一个新的 Module.symvers 文件 将写入包含所有未导出的符号 在内核中定义。

这也值得一读,来自同一个文件:

--- 6.3 来自另一个外部模块的符号

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

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

使用顶级的 kbuild 文件 如果你有两个模块,foo.ko 和 bar.ko,其中 foo.ko 需要来自 bar.ko 的符号,你可以使用 通用顶级 kbuild 文件,因此两个模块都是 在同一个版本中编译。考虑以下 目录布局:

./foo/

顶级 kbuild 文件如下所示:

$ ./Kbuild(或 ./Makefile): obj-y := foo/bar/

并执行

$ make -C $KDIR M=$PWD

然后将执行预期并编译两个模块 充分了解任一模块的符号。

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

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

【讨论】:

    【解决方案2】:

    以下是我在您的代码中发现的一些问题:

    (一)。您的初始化和终止函数应声明为静态并正确标识。例如,在 m1.c -

    static int __init hello_start(void)
    {
         printk(KERN_INFO "Loading m1 module ...\n");
    
        func_m2();
    
        return 0;
    }
    
    static void __exit hello_end(void)
    {
        printk(KERN_INFO "Unloading m1 ...\n");
    }
    

    对 m2.c 重复此操作

    (b)。使用相同的 Makefile 一起构建两个模块。我敢打赌,如果您仔细查看现有 Makefile for m1.c 的输出,您会看到一条警告,表明 func_m2() 未定义。无论如何,合并后的 Makefile 应该看起来像 -

    SRCS   = m1.c m2.c
    OBJS   = $(SRCS:.c=.o)
    
    obj-m += $(OBJS)
    
    EXTRA_CFLAGS = -O2
    
    
    all:
        $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules
    
    clean:
        $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean
        $(RM) Module.markers modules.order
    

    在构建两个模块后,在“m2.ko”上运行 insmod,然后再为“m1.ko”发出 insmod。通过 dmesg 检查结果。

    另外,在这里我假设 m1.c 和 m2.c 都在同一个目录中。即使它们在不同的目录中,这种技术也可以工作,但会很混乱。如果它们位于不同的目录中,请执行以下操作。

    我几乎没有做任何研究,并找到了一种在不同目录中构建模块的方法。我使用的示例比您的示例要简单得多,但也许它具有适应性。

    我在一个名为 ExportSymbol...

    的目录中有以下文件清单
    $ ls -CFR
    .:
    include/  Makefile  mod1/  mod2/
    
    ./include:
    m2_func.h
    
    ./mod1:
    Makefile  module1.c
    
    ./mod2:
    Makefile  module2.c
    

    m2_func.h 显示为:

    #ifndef M2_FUNC_H
    #define M2_FUNC_H
    
    void m2_func(void);
    
    #endif
    

    顶层 Makefile 显示为:

    obj-y := mod1/ mod2/
    
    all:
        $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules
    
    clean:
        $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean
        $(RM) Module.markers modules.order
    

    在 mod1/ 中的 Makefile 和 module1.c 显示为:

    SRCS   = module1.c
    OBJS   = $(SRCS:.c=.o)
    
    obj-m += $(OBJS)
    
    EXTRA_CFLAGS += -I${PWD}/include
    
    all:
        $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules
    
    clean:
        $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean
        $(RM) Module.markers modules.order
    

    #include <linux/module.h>
    #include <linux/kernel.h>
    
    static int __init hello_start(void)
    {
     printk(KERN_INFO "Loading m1 module ...\n");
    
     m2_func();
    
     return 0;
    }
    
    static void __exit hello_end(void)
    {
     printk(KERN_INFO "Unloading m1 ...\n");
    }
    
    module_init(hello_start);
    module_exit(hello_end);
    
    MODULE_LICENSE("GPL");
    

    在 mod2/ 中的 Makefile 和 module2.c 显示为:

    SRCS   = module2.c
    OBJS   = $(SRCS:.c=.o)
    
    obj-m += $(OBJS)
    
    EXTRA_CFLAGS += -I${PWD}/include
    
    all:
        $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) modules
    
    clean:
        $(MAKE) -C /lib/modules/`uname -r`/build M=$(PWD) clean
        $(RM) Module.markers modules.order
    

    #include "m2_func.h"
    #include <linux/module.h>
    #include <linux/kernel.h>
    
    static int __init hello_start(void)
    {
     printk(KERN_INFO "Loading m2 module ...\n");
    
     return 0;
    }
    
    static void __exit hello_end(void)
    {
     printk(KERN_INFO "Unloading m2 ...\n");
    }
    
    void m2_func(void)
    {
     printk(KERN_INFO "This a function in m2\n");
    } 
    
    module_init(hello_start);
    module_exit(hello_end);
    
    MODULE_LICENSE("GPL");
    EXPORT_SYMBOL(m2_func);
    

    注意:我不能使用您的 makefile,因为它会为每个 c 文件生成 *.ko。 Makefile 正在完成它的工作。 “ko”文件是内核对象文件;每个 .c 源文件都有一个。没有办法解决这个问题。如果您不想要多个 ko 文件,请将所有代码放在一个源文件中。

    【讨论】:

    • 就我而言,有一种方法可以制作多个 C 文件,而不是制作多个 .ko 文件。我记得我在旧项目中制作了许多 .c 文件,但只有一个 .ko 文件。顺便谢谢你的回答。
    【解决方案3】:

    最简单的方法是同时为两个模块创建一个Makefile

    示例:

     obj-m += hello.o
     obj-m += world.o
     
     all:
          make -C /lib/modules/`uname -r`/build M=$(PWD) modules
    install: 
          make -C /lib/modules/`uname -r`/build M=$(PWD) modules_install
    clean:
          make -C /lib/modules/`uname -r`/build M=$(PWD) clean
    

    第一个模块 hello.c

    #inluce  <linux/module.h>
    #include <linux/init.h>
    
    int hello_print(){
     printk("<0> Hello -> %s : %d",__FUNCTION__,__LINE__);
    return 0;
    }
    EXPORT_SYMBOL(hello_print),
    
    static int __init open(void)
    {
        printk("<0> Module Hello Start!");
        return 0;
    }
    
    static void __exit close(void) {
        printk("<0> Module Hello Stop!");
    }
    
    module_init(open);
    module_exit(close);
    MODULE_LICENSE("GPL v2");
    

    第二个模块 world.c

    #inluce  <linux/module.h>
    #include <linux/init.h>
    
    extern int hello_print();
    
    static int __init open(void)
    {
        printk("<0> Init World Module");
        printk("<0> Call hello_print() from Hello Module");
        hello_print();
        return 0;
    }
    
    static void __exit close(void) {
        printk("<0> Module Hello Stop!");
    }
    
    module_init(open);
    module_exit(close);
    MODULE_LICENSE("GPL v2");
    

    然后

    $ make 
    $ insmod hello.ko
    $ insmod world.ko
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-15
      • 1970-01-01
      • 1970-01-01
      • 2017-03-31
      • 1970-01-01
      • 2012-08-06
      相关资源
      最近更新 更多