insmod失败,"模块中的未知符号"对于在另一个模块中定义的符号

时间:2015-11-15 09:51:02

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

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

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

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

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

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

现在,让我们加载试图使用m1的{​​{1}}模块:

func_m2

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

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无法加载?

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

现在,为了向您展示完整的图片,我在这里放置了我用于m1m1模块的测试的源代码和Makefile。

m2模块:

m1.c:

m1

生成文件:

#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.c:

m2

生成文件:

#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

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

3 个答案:

答案 0 :(得分:5)

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

将此文件复制到您正在构建m1的位置。然后制作m1,然后输入。

您之前构建m1时可能会收到警告,例如:

  

警告:&#34; func_m2&#34; [/tmp/m1/m1.ko] undefined!

一旦使用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 /&lt; =包含foo.ko   ./bar/&lt; =包含bar.ko

     

顶级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文件,包含总和   所有符号都已定义,而不是内核的一部分。

     

使用&#34;制作&#34;变量KBUILD_EXTRA_SYMBOLS   如果从中复制Module.symvers是不切实际的   另一个模块,您可以分配一个空格分隔列表   文件到构建文件中的KBUILD_EXTRA_SYMBOLS。   这些文件将在mod期间由modpost加载   初始化其符号表。

答案 1 :(得分:3)

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

(a)中。您的初始化和终止函数应声明为静态并正确识别。例如,在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

在构建两个模块之后,在发出'm1.ko'的insmod之前,在'm2.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文件,请将所有代码放在一个源文件中。

答案 2 :(得分:0)

最简单的方法是同时为两个模块创建一个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