[英]insmod fails with "Unknown symbol in module" for a symbol defined in another module
我在 Ubuntu 工作。 我正在嘗試制作兩個相互使用功能的內核模塊。 我的問題是我正確編譯了模塊,但沒有為其中之一解析符號。
為簡單起見,我們將這些模塊稱為m1
和m2
。
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
的引用。 有趣的。 讓我們檢查它是否存在於符號表中:
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)
現在,為了向您展示全貌,我將用於m1
和m2
模塊的測試的源代碼和 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,然后輸入。
您之前構建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/ <=包含foo.ko ./bar/ <=包含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文件,其中包含已定義的所有符號的總和,而不是內核的一部分。
使用“make”變量KBUILD_EXTRA_SYMBOLS如果從另一個模塊復制Module.symvers是不切實際的,則可以在構建文件中將空格分隔的文件列表分配給KBUILD_EXTRA_SYMBOLS。 這些文件將在其符號表初始化期間由modpost加載。
以下是我在您的代碼中發現的一些問題:
(一個)。 您的初始化和終止函數應聲明為靜態並正確識別。 例如,在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文件,請將所有代碼放在一個源文件中。
最簡單的方法是同時為兩個模塊創建一個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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.