動的にロードされたモジュールがシンボルにアクセスできるようにします (ただし、モジュールが extern
を追加する場合) 宣言)
少し前に、誰かがその使い方を尋ねました.
ここに良い説明があります。
https://www.quora.com/What-is-the-difference-between-extern-and-EXPORT_SYMBOL-in-Linux-kernel-codes
<ブロック引用>Extern は C ストレージ クラスのキーワードです。カーネルでは、他の C コードと同様に、修飾する変数または関数の定義が別の「ファイル」に実装されていることをコンパイラに伝えます。より正確には、変換ユニット (プログラミング) - ウィキペディア.それを定義する翻訳単位は、静的修飾子を使用すべきではありません。したがって、記号テーブルにはそれに対応するエントリがあります。リンク時に、シンボルは通常どおり解決されます。 「extern」についてカーネル固有のものは何もありません。
EXPORT_SYMBOL() は、Linux カーネル ヘッダーが定義するマクロです。 extern との共通点はあまりありません。これは、参照されるシンボルがカーネルシンボルのグローバルリストの一部であることを kbuild メカニズムに伝えます。これにより、カーネルモジュールがそれらにアクセスできるようになります。もちろん、(モジュールではなく) カーネル自体に組み込まれたコードは、通常の C に従って、extern 宣言を介して任意の非静的シンボルにアクセスできます。EXPORT_SYMBOL() メカニズムにより、ロード可能なモジュールで使用するシンボルを良い。興味深いことに、あるモジュールによってこのようにエクスポートされたシンボルは、それに依存する可能性のある別のモジュールからアクセスできるようになります!
要約すると、 extern はカーネル固有ではありません。これは、別の変換単位からの非静的シンボルへの宣言を修飾するために使用されます。EXPORT_SYMBOL() は Linux カーネルに固有です。シンボルをロード可能なモジュールで使用できるようにするために、定義の翻訳単位で使用されます。
したがって、EXPORT_SYMBOL は extern のような単なるメカニズムですが、ファイルではなくロード可能なモジュール間の参照用です。
先に進むには、extern が基盤であるフォーム C であるため、extern によって達成されると推測できます。
ここに手がかりがあります。
https://elixir.bootlin.com/linux/v4.6.7/source/include/linux/export.h#L56
#define EXPORT_SYMBOL(sym) \
__EXPORT_SYMBOL(sym, "")
/* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec) \
extern typeof(sym) sym; \
__CRC_SYMBOL(sym, sec) \
static const char __kstrtab_##sym[] __attribute__((section("__ksymtab_strings"), aligned(1))) = VMLINUX_SYMBOL_STR(sym); \
extern const struct kernel_symbol __ksymtab_##sym; \
__visible const struct kernel_symbol __ksymtab_##sym __used __attribute__((section("___ksymtab" sec "+" #sym), unused)) = { (unsigned long)&sym, __kstrtab_##sym }
最初に extern sym を宣言します。
次に、文字列 __kstrtab_##sym ==VMLINUX_SYMBOL_STR(sym)。
最後に extern struct kernel_symbol __ksymtab_##sym ={ (unsigned long)&sym , __kstrtab_##sym }。 &sym 関数や変数などの sym の実アドレスを記録する _kstrtab ##sym 名前の文字列を記録してください。
それ自体は答えではありませんが、私のコメントから約束されているように、エクスポートされたシンボルは そうではありません 非静的である必要があります。以下の 2 つのモジュールはこれを示しています:
/* mod1.c */
#include <linux/module.h>
static int mod1_exp_func(int i)
{
pr_info("%s:%d the value passed in is %d\n",
__func__, __LINE__, i);
return i;
}
EXPORT_SYMBOL(mod1_exp_func); /* export static symbol */
static int __init mod1_init(void)
{
pr_info("Initializing simple mod\n");
return 0;
}
static void __exit mod1_exit(void)
{
pr_info("This module is exiting\n");
}
module_init(mod1_init);
module_exit(mod1_exit);
MODULE_LICENSE("GPL v2");
そして2番目のモジュール
/* mod2.c */
#include <linux/module.h>
extern int mod1_exp_func(int);
static int __init mod2_init(void)
{
pr_info("Initializing mod2\n");
pr_info("Calling exported function in mod1\n");
mod1_exp_func(3);
return 0;
}
static void __exit mod2_exit(void)
{
pr_info("mod2 exiting\n");
}
module_init(mod2_init);
module_exit(mod2_exit);
MODULE_LICENSE("GPL v2");
これらは、CentOS 6 および CentOS 7:カーネル 2.6.32 および 3.10 (それぞれ) でテストされました。 mod1.ko をロードしてから mod2.ko をロードすると、mod1_exp_func() に渡された値がカーネル ログ バッファに出力されます。