对于另一个模块中定义的符号,insmod 失败并显示 "Unknown symbol in module"

insmod fails with "Unknown symbol in module" for a symbol defined in another module

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

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

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

全部编译完成后,我需要先加载 m2 模块(因为它导出了 func_m2 函数),然后再加载 m1 模块。那么,让我们来吧:

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

现在,让我们加载试图使用 func_m2:

m1 模块
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 的引用没有解析。有趣的。让我们检查它是否存在于符号 table:

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 实际上存在于符号 table 中。那为什么m1加载不出来呢?

我已经为我的内核和 Linux 源安装了正确的 Linux 头文件。我没有在内核中做任何修改,它是原封不动的,它的版本是:2.6.31-16-generic (I 运行 x64)

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

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

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

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

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

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

WARNING: "func_m2" [/tmp/m1/m1.ko] undefined!

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

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

--- 6.2 Symbols and External Modules

When building an external module, the build system needs access to the symbols from the kernel to check if all external symbols are defined. This is done in the MODPOST step. modpost obtains the symbols by reading Module.symvers from the kernel source tree. If a Module.symvers file is present in the directory where the external module is being built, this file will be read too. During the MODPOST step, a new Module.symvers file will be written containing all exported symbols that were not defined in the kernel.

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

--- 6.3 Symbols From Another External Module

Sometimes, an external module uses exported symbols from another external module. kbuild needs to have full knowledge of all symbols to avoid spitting out warnings about undefined symbols. Three solutions exist for this situation.

NOTE: The method with a top-level kbuild file is recommended but may be impractical in certain situations.

Use a top-level kbuild file If you have two modules, foo.ko and bar.ko, where foo.ko needs symbols from bar.ko, you can use a common top-level kbuild file so both modules are compiled in the same build. Consider the following directory layout:

./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 knowledge of symbols from either module.

Use an extra Module.symvers file When an external module is built, a Module.symvers file is generated containing all exported symbols which are not defined in the kernel. To get access to symbols from bar.ko, copy the Module.symvers file from the compilation of bar.ko to the directory where foo.ko is built. During the module build, kbuild will read the Module.symvers file in the directory of the external module, and when the build is finished, a new Module.symvers file is created containing the sum of all symbols defined and not part of the kernel.

Use "make" variable KBUILD_EXTRA_SYMBOLS If it is impractical to copy Module.symvers from another module, you can assign a space separated list of files to KBUILD_EXTRA_SYMBOLS in your build file. These files will be loaded by modpost during the initialization of its symbol tables.

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

(一)。您的初始化和终止函数应声明为静态的并正确标识。例如,在 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 将两个模块构建在一起。我敢打赌,如果您仔细查看 m1.c 的现有 Makefile 的输出,您会看到一条警告,表明 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");

Makefile 和 module2.c 位于 mod2/ 中,显示为:

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 文件,请将所有代码放在一个源文件中。

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