簡體   English   中英

對於另一個模塊中定義的符號,insmod 失敗並顯示“模塊中的未知符號”

[英]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的引用。 有趣的。 讓我們檢查它是否存在於符號表中:

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)

現在,為了向您展示全貌,我將用於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,然后輸入。

您之前構建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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM