web-dev-qa-db-ja.com

別のモジュールからエクスポートされたカーネルモジュール関数を呼び出す方法は?

デバイスドライバーにさまざまな機能を提供するカーネルモジュールとしてAPIを作成しています。 mycode.cに3つの関数を記述しました。次に、モジュールをビルドしてロードし、mycode.h<kernel>/include/linux。デバイスドライバーでは、#include <linux/mycode.h>があり、これら3つの関数を呼び出します。しかし、ドライバーモジュールをビルドすると、それらの関数が未定義であるという3つのリンカー警告が表示されます。

ノート:

  • 関数はmycode.hでexternとして宣言されます
  • 関数は、mycode.cのEXPORT_SYMBOL(func_name)を使用してエクスポートされます。
  • Nm mycode.koコマンドを実行すると、3つの関数すべてがシンボルテーブルで使用可能であることが示されます(それらの横に大文字のTがあり、シンボルはテキスト(コード)セクションにあります)。
  • モジュールをロードした後、コマンドgrep func_name/proc/kallsymsは3つの関数すべてがロードされていることを示します

そのため、関数が正しくエクスポートされており、カーネルはそれらがどこにあるかを認識しています。なぜ運転手は彼らの定義を見ることができないのですか?何が欠けているのですか?


編集:これに関するいくつかの情報をここに見つけました:http://www.kernel.org/doc/Documentation/kbuild/modules。 txt

場合によっては、外部モジュールが別の外部モジュールからエクスポートされたシンボルを使用することがあります。 kbuildは、未定義のシンボルに関する警告を吐き出さないように、すべてのシンボルの完全な知識を持っている必要があります。この状況には3つの解決策があります。

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

トップレベルのkbuildファイルを使用するfoo.koとbar.koの2つのモジュールがあり、foo.koがbar.koからのシンボルを必要とする場合、共通のトップレベルのkbuildファイルを使用して、両方のモジュールを同じようにコンパイルできます。ビルド。次のディレクトリレイアウトを検討してください。

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

  The top-level kbuild file would then look like:

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

  And executing

      $ make -C $KDIR M=$PWD

  will then do the expected and compile both modules with         full

いずれかのモジュールからのシンボルの知識。

追加の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によって読み込まれます。

しかし、これらの3つのソリューションすべてで、ドライバーが私のAPIを使用するには、新しいMakefileを作成するか、私のModule.symversファイルに直接アクセスする必要がありますか?それは少し不便なようです。私は、ヘッダーファイルを#includeして、問題なく動作することを望んでいました。他に選択肢はありませんか?

25
jacobsowles

私の調査では、この3つがこの状況に対処する唯一の方法であるように思われ、それぞれを機能させることができました。

8
jacobsowles

最小限のQEMU + Buildrootの例

私は完全に再現可能なQEMU + Buildroot環境で以下をテストしたので、この作業バージョンのバージョンがあると、コードの何が問題なのかを見つけるのに役立つでしょう。

GitHubアップストリームはファイルを中心にしています: dep.c | dep2.c | メイクファイル

dep.c:

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

int lkmc_dep = 0;
EXPORT_SYMBOL(lkmc_dep);
static struct task_struct *kthread;

static int work_func(void *data)
{
    while (!kthread_should_stop()) {
        printk(KERN_INFO "%d\n", lkmc_dep);
        usleep_range(1000000, 1000001);
    }
    return 0;
}

static int myinit(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

static void myexit(void)
{
    kthread_stop(kthread);
}

module_init(myinit)
module_exit(myexit)

dep2.c:

#include <linux/delay.h> /* usleep_range */
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>

MODULE_LICENSE("GPL");

extern int lkmc_dep;
static struct task_struct *kthread;

static int work_func(void *data)
{
    while (!kthread_should_stop()) {
        usleep_range(1000000, 1000001);
        lkmc_dep++;
    }
    return 0;
}

static int myinit(void)
{
    kthread = kthread_create(work_func, NULL, "mykthread");
    wake_up_process(kthread);
    return 0;
}

static void myexit(void)
{
    kthread_stop(kthread);
}

module_init(myinit)
module_exit(myexit)

今、あなたはどちらかを行うことができます:

insmod dep.ko
insmod dep2.ko

ただし、Buildrootはすでに依存関係を使用してdepmod /lib/module/*/depmodも構成しているため、両方をロードするにはこれで十分です。

modprobe dep

CONFIG_KALLSYMS_ALL=yを使用すると、シンボルは次のように表示されます。

grep lkmc_dep /proc/kallsyms

参照: kallsymsはカーネル関数のすべてのシンボルを持っていますか?

OK:関数があるモジュールと、それを正しくインポートしたい場所が1つありますか?

関数のある場所では、fooなどの「EXPORT_SYMBOL( "関数の名前")」を使用する必要があるため、「c」ファイルでは、関数「foo」を定義して次のように入力します:EXPORT_SYMBOL(foo)

共通のヘッダーファイルに「foo」のプロトタイプがあることを確認してください(モジュールごとに別々の場所に置くことができ、機能しますが、シグネチャが変更された場合に問題が発生します)。だから言う:void foo(void * arg);

次に、それを必要とする他のモジュールは "foo"を呼び出すだけでよい。

また、最初にfooでモジュールをロードするようにしてください。 module2がmodule1からのfooを必要とし、module1がmodule2からのbarを必要とするような相互依存関係がある場合、あるレジスター関数を別のレジスター関数とする必要があります。知りたい方は別途Qにお尋ねください。

0
user5078679