简体   繁体   English

对于另一个模块中定义的符号,insmod 失败并显示“模块中的未知符号”

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

I am working in Ubuntu.我在 Ubuntu 工作。 I am trying to make two kernel modules which uses each other functions.我正在尝试制作两个相互使用功能的内核模块。 My problem is that I got modules properly compiled, but the symbol is not resolved for one of them.我的问题是我正确编译了模块,但没有为其中之一解析符号。

To make things simple, let's call these modules as m1 and m2 .为简单起见,我们将这些模块称为m1m2

m2 is exporting function void func_m2(void) . m2 正在导出函数void func_m2(void) The m1 is calling this function. m1正在调用这个函数。 Both modules properly compile.两个模块都正确编译。

After it all compiles, I need to load first the m2 module (because it has exported func_m2 function) and afterwards m1 module.全部编译完成后,我需要先加载m2模块(因为它已经导出了func_m2函数),然后是m1模块。 So, let's make it:所以,让我们实现:

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

Now, lets load m1 module which is trying to use func_m2 :现在,让我们加载试图使用func_m2 m1模块:

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

Following is what I see in logs:以下是我在日志中看到的内容:

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

So, it seems like the references to symbol func_m2 is not resolved.因此,似乎未解析对符号func_m2的引用。 Interesting.有趣的。 Let's check if it is present in symbol 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]

As you can see, the func_m2 is actually present in symbol table.如您所见, func_m2实际上存在于符号表中。 So why m1 can't be loaded?那么为什么不能加载m1呢?

I have installed properly Linux headers for my kernel and Linux sources.我已经为我的内核和 Linux 源正确安装了 Linux 头文件。 I did not make any modifications in kernel, it is untouched, and it's version is: 2.6.31-16-generic (I run x64)我没有对内核做任何修改,它没有受到影响,它的版本是:2.6.31-16-generic(我运行 x64)

Now, to show you full picture I am putting here the source code and Makefile I used for this test for both m1 and m2 modules.现在,为了向您展示全貌,我将用于m1m2模块的测试的源代码和 Makefile 放在这里。

m1 module: m1模块:

m1.c: 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");

Makefile:生成文件:

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 module: m2模块:

m2.c: 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);

Makefile:生成文件:

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

Basically my question is: Why can't m1 be loaded?基本上我的问题是:为什么不能加载m1

It would be helpful if someone could answer.如果有人可以回答会很有帮助。

When you build m2, it creates a Module.symvers file. 构建m2时,它会创建一个Module.symvers文件。

Copy this file to where you are building m1. 将此文件复制到您正在构建m1的位置。 Then make m1, and insmod it. 然后制作m1,然后输入。

You probably had a warning when you were building m1 before, something like: 您之前构建m1时可能会收到警告,例如:

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

This should disappear once you use the Module.symvers from the m2 module. 一旦您使用m2模块中的Module.symvers ,这应该会消失。

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

--- 6.2 Symbols and External Modules --- 6.2符号和外部模块

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步骤中完成的。 modpost obtains the symbols by reading Module.symvers from the kernel source tree. modpost通过从内核源代码树中读取Module.symvers来获取符号。 If a Module.symvers file is present in the directory where the external module is being built, this file will be read too. 如果在构建外部模块的目录中存在Module.symvers文件,则也将读取此文件。 During the MODPOST step, a new Module.symvers file will be written containing all exported symbols that were not defined in the kernel. 在MODPOST步骤中,将编写一个新的Module.symvers文件,其中包含未在内核中定义的所有导出符号。

And this is also worth reading, from the same file: 这也值得一读,来自同一个文件:

--- 6.3 Symbols From Another External Module --- 6.3来自另一个外部模块的符号

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. kbuild需要完全了解所有符号,以避免吐出有关未定义符号的警告。 Three solutions exist for this situation. 这种情况存在三种解决方案。

NOTE: The method with a top-level kbuild file is recommended but may be impractical in certain situations. 注意:建议使用顶级kbuild文件的方法,但在某些情况下可能不切实际。

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. 使用顶级kbuild文件如果你有两个模块,foo.ko和bar.ko,其中foo.ko需要来自bar.ko的符号,你可以使用一个通用的顶级kbuild文件,所以这两个模块都是用同一个编译的建立。 Consider the following directory layout: 请考虑以下目录布局:

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

The top-level kbuild file would then look like: 顶级kbuild文件将如下所示:

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

And executing 并执行

$ make -C $KDIR M=$PWD $ 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. 使用额外的Module.symvers文件构建外部模块时,会生成Module.symvers文件,其中包含未在内核中定义的所有导出符号。 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. 要从bar.ko访问符号,请将module.symvers文件从bar.ko的编译复制到构建foo.ko的目录。 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. 在模块构建期间,kbuild将读取外部模块目录中的Module.symvers文件,并且在构建完成时,将创建一个新的Module.symvers文件,其中包含已定义的所有符号的总和,而不是内核的一部分。

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. 使用“make”变量KBUILD_EXTRA_SYMBOLS如果从另一个模块复制Module.symvers是不切实际的,则可以在构建文件中将空格分隔的文件列表分配给KBUILD_EXTRA_SYMBOLS。 These files will be loaded by modpost during the initialization of its symbol tables. 这些文件将在其符号表初始化期间由modpost加载。

Here are some issues I found with your code: 以下是我在您的代码中发现的一些问题:

(a). (一个)。 Your initialization and termination functions should be declared static and properly identified. 您的初始化和终止函数应声明为静态并正确识别。 For example, in m1.c - 例如,在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");
}

Repeat this for m2.c 对m2.c重复此操作

(b). (b)中。 Build both of your modules together, using the same Makefile. 使用相同的Makefile一起构建两个模块。 I bet if you look closely at the output from your existing Makefile for m1.c, you will see a warning indicating that func_m2() is undefined. 我敢打赌,如果你仔细查看现有的Makefile for m1.c的输出,你会看到一个警告,指出func_m2()未定义。 Anyhow, the consolidated Makefile should look like - 无论如何,整合的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

After both modules are built, run insmod on 'm2.ko' before issuing the insmod for 'm1.ko'. 构建两个模块后,在发出'm1.ko'的insmod之前,在'm2.ko'上运行insmod。 Check results via dmesg. 通过dmesg检查结果。

Also, over here I am assuming that both m1.c and m2.c are in the same directory. 另外,在这里我假设m1.c和m2.c都在同一个目录中。 Even if they are in different directories, this technique will work, but it will be messy. 即使它们位于不同的目录中,这种技术也会起作用,但它会很混乱。 If they are in different directories, do the following. 如果它们位于不同的目录中,请执行以下操作。

I did little research and found a way to build modules in separate directories. 我做了很少的研究,并找到了在不同目录中构建模块的方法。 The example I used is much simpler than what you have, but perhaps it is adaptable. 我使用的示例比您拥有的示例简单得多,但也许它具有适应性。

I have following manifest of files in a directory called ExportSymbol ... 我在名为ExportSymbol的目录中有以下文件清单 ...

$ ls -CFR
.:
include/  Makefile  mod1/  mod2/

./include:
m2_func.h

./mod1:
Makefile  module1.c

./mod2:
Makefile  module2.c

The m2_func.h appears as: m2_func.h显示为:

#ifndef M2_FUNC_H
#define M2_FUNC_H

void m2_func(void);

#endif

The top-level Makefile appears as: 顶级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

The Makefile and module1.c, which are in mod1/, appear as: 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");

The Makefile and module2.c, which are in mod2/, appear as: 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);

NOTE: I can't use your makefile as it generates *.ko per each c file. 注意:我不能使用您的makefile,因为它会为每个c文件生成* .ko。 The Makefile is doing its job. Makefile正在完成它的工作。 A 'ko' file is a kernel object file; 'ko'文件是内核对象文件; you will have one for each .c source file. 每个.c源文件都有一个。 There's no way around this. 没有办法解决这个问题。 If you do not want multiple ko-files, then put all of your code in one source file. 如果您不想要多个ko文件,请将所有代码放在一个源文件中。

The easiest way is to create a Makefile for two modules at the same time最简单的方法是同时为两个模块创建一个Makefile

example :例子 :

 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

First Module hello.c第一个模块 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");

Second Module world.c第二个模块 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");

then然后

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM