28

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

ノート:

  • 関数はmycode.hでexternとして宣言されています
  • 関数は、mycode.cのEXPORT_SYMBOL(func_name)を使用してエクスポートされます。
  • コマンドnmmycode.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

最上位のkbuildファイルは次のようになります。

  #./Kbuild (or ./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によってロードされます。

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

4

3 に答える 3

8

私の研究から、この状況に対処する方法は3つしかないようで、それぞれを機能させることができたので、その中から好きなものを選んでみようと思います。

于 2012-09-08T14:34:42.390 に答える
3

最小限のQEMU+Buildrootの例

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

GitHubアップストリームはファイルを中心にしています:

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にはカーネル関数のすべてのシンボルがありますか?

于 2017-06-18T10:55:39.093 に答える
0

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

関数がある場所では、fooなどの「EXPORT_SYMBOL( "関数の名前")」を使用する必要があります。したがって、 "c"ファイルには、関数 "foo"が定義​​され、次のように配置されます。EXPORT_SYMBOL(foo)

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

次に、それを必要とする他のモジュールが「foo」を呼び出すだけで、問題ありません。

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

于 2018-03-25T22:29:24.140 に答える