1. ホーム
  2. c

[解決済み] 他のモジュールで定義されたシンボルに対して、insmod が "Unknown symbol in module" で失敗する。

2022-02-11 23:34:38

質問

私はUbuntuで仕事をしています。私は、互いの機能を使用する2つのカーネルモジュールを作成しようとしています。私の問題は、私はモジュールを適切にコンパイルしましたが、それらのうちの1つのためにシンボルが解決されていないことです。

簡単に説明するために、これらのモジュールの名前を m1m2 .

m2 は関数をエクスポートしています。 void func_m2(void) . また m1 がこの関数を呼び出しています。どちらのモジュールも正しくコンパイルされます。

すべてのコンパイルが完了したら、最初に m2 モジュール(これは func_m2 関数)の後に m1 モジュールになります。では、作ってみましょう。

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

では、次のようにロードします。 m1 を使おうとしているモジュールは 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 は実際にシンボルテーブルに存在する。では、なぜ m1 を読み込むことができないのですか?

カーネルとLinuxソースのLinuxヘッダーはきちんとインストールしました。カーネルには何も手を加えておらず、バージョンは 2.6.31-16-generic です (x64 を使用しています)。

さて、全体像をお見せするために、このテストに使用したソースコードと Makefile を以下に掲載します。 m1m2 モジュールになります。

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 が読み込まれるのですか?

どなたか回答していただけると助かります。

どのように解決するのですか?

以下は、あなたのコードで見つけたいくつかの問題点です。

(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 を使って、両方のモジュールを一緒にビルドしてください。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

両方のモジュールがビルドされたら、'm1.ko' の insmod を実行する前に 'm2.ko' の insmod を実行します。dmesg で結果を確認します。

また、ここでは、m1.cとm2.cが同じディレクトリにあるものと仮定しています。異なるディレクトリにある場合でも、このテクニックは有効ですが、面倒なことになります。異なるディレクトリにある場合は、次のようにしてください。

少し調べてみたところ、モジュールを別々のディレクトリにビルドする方法がありました。私が使った例は、あなたが持っているものよりもずっとシンプルですが、おそらく適応可能でしょう。

というディレクトリに、次のようなファイルのマニフェストがあります。 エクスポートシンボル ...

$ 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);

NOTE cファイルごとに*.koを生成してしまうので、あなたのmakefileは使えません。Makefile はちゃんと仕事をしていますよ。ko' ファイルはカーネルオブジェクトファイルで、.c ソースファイルごとに 1 つ生成されます。これを回避する方法はありません。もし、複数の ko ファイルを必要としないのであれば、すべてのコードを 1つのソースファイルにまとめてください。