web-dev-qa-db-ja.com

別のモジュールで定義されているシンボルの「モジュール内の不明なシンボル」でinsmodが失敗する

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

物事を簡単にするために、これらのモジュールを_m1_および_m2_として呼び出しましょう。

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を実行しています)

ここで、全体像を示すために、ここで_m1_および_m2_モジュールの両方のこのテストに使用したソースコードとMakefileを配置します。

_m1_ module:

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_ module:

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_をロードできないのですか?

誰かが答えてくれると助かります。

16
user5485048

あなたのコードで見つけたいくつかの問題は次のとおりです。

(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の両方が同じディレクトリにあると想定しています。それらが異なるディレクトリにある場合でも、この手法は機能しますが、面倒です。それらが異なるディレクトリにある場合は、次の手順を実行します。

私はほとんど調査をせず、別々のディレクトリにモジュールを構築する方法を見つけました。私が使用した例は、あなたが持っているものよりもはるかに単純ですが、おそらく適応可能です。

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

注:各cファイルごとに* .koを生成するため、メイクファイルを使用できません。 Makefileはその仕事をしています。 「ko」ファイルはカーネルオブジェクトファイルです。各.cソースファイルに1つずつあります。これを回避する方法はありません。複数のkoファイルが必要ない場合は、すべてのコードを1つのソースファイルに入れます。

3
Ashish Ahuja

M2をビルドすると、Module.symversファイルが作成されます。

このファイルをm1を構築している場所にコピーします。次に、m1を作成し、それをinsmodします。

前にm1をビルドしていたときに、おそらく次のような警告が表示されました。

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

これは、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は、未定義のシンボルに関する警告を吐き出すことを避けるために、すべてのシンボルの完全な知識が必要です。この状況には3つの解決策があります。

注:最上位のkbuildファイルを使用する方法が推奨されますが、特定の状況では実用的ではない場合があります。

トップレベルのkbuildファイルを使用するfoo.koとbar.koの2つのモジュールがあり、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からシンボルにアクセスするには、bar.koのコンパイルからModule.symversファイルをfoo.koが構築されているディレクトリにコピーします。モジュールのビルド中、kbuildは外部モジュールのディレクトリにあるModule.symversファイルを読み取り、ビルドが完了すると、カーネルの一部ではなく定義されたすべてのシンボルの合計を含む新しいModule.symversファイルが作成されます。

「make」変数KBUILD_EXTRA_SYMBOLSを使用するModule.symversを別のモジュールからコピーするのが現実的でない場合は、スペースで区切られたファイルのリストをビルドファイルのKBUILD_EXTRA_SYMBOLSに割り当てることができます。これらのファイルは、シンボルテーブルの初期化中にmodpostによってロードされます。

6
Michael