asmlinkageってなに?
Rev.3を表示中。最新版はこちら。
asmlinkageというマクロは下記のように定義されています。CPP_ASMLINKAGEはC++でコンパイルした時の関数名をC表記とする宣言です。asmlinkageはregparmと展開されています。regparmは指定する数値分のパラメータの引数をレジスタ渡しとすることです。なお、カーネルソースでasmlinkageで宣言している関数をみると、引数はありません。regparmは0として展開しています。で、これって意味あるのでしょうか?#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) ifdef __cplusplus #define CPP_ASMLINKAGE extern "C" #else #define CPP_ASMLINKAGE #endifまず、実際regparmがどのように展開されるか見てみようと思います。
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(2))) int _a, _b; asmlinkage void asmlinkage_test(int a, int b) { _a = a; _b = b; } calltest() { asmlinkage_test(1, 2); } <asmlinkage_test>: 55 push %ebp 89 e5 mov %esp,%ebp 83 ec 08 sub $0x8,%esp 89 45 fc mov %eax,-0x4(%ebp) <- 第1引数をスタックへ 89 55 f8 mov %edx,-0x8(%ebp) <- 第2引数をスタックへ 8b 45 fc mov -0x4(%ebp),%eax <- スタック上引数をAXへ a3 2c 96 04 08 mov %eax,0x804962c 8b 45 f8 mov -0x8(%ebp),%eax <- スタック上引数をAXへ a3 28 96 04 08 mov %eax,0x8049628 c9 leave c3 ret <calltest>: 55 push %ebp 89 e5 mov %esp,%ebp 83 ec 04 sub $0x4,%esp ba 02 00 00 00 mov $0x2,%edx <- 第2引数 b8 01 00 00 00 mov $0x1,%eax <- 第一引数 e8 cd ff ff ff call 8048394 <asmlinkage_test> c9 leave c3 ret呼び出し元の第1引数をAXに、第二引数をDXにセットし、asmlinkage_testを呼んでいます(本来はスタック)。受け取り側は、引数が設定されているレジスターをわざわざスタックに戻して、いかにも通常のスタックで渡された物として処理しています(引数の数とregparamの数値が異なっても、柔軟に対応するためだと思います。)。ともかく、呼び出し側では、確かにレジスターで引数を渡せばいいようです。
では、カーネルでの使われ方を見てみようと思いますが・・・。あえて言うなら、regparmは引数にかかる処理をコントロールするもので、引数のない関数に適用しても意味のないのは明白です。ひょっといたらアーキテクチャーで違うのかもしれませんが、少なくともIA-32では意味がない。と言えそうですが・・・。
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) int _a, _b; asmlinkage void asmlinkage_test(void) { _a = 1; } calltest() { asmlinkage_test(); } <asmlinkage_test>: 55 push %ebp 89 e5 mov %esp,%ebp c7 05 0c 96 04 08 01 movl $0x1,0x804960c 00 00 00 5d pop %ebp c3 ret <calltest>: 55 push %ebp 89 e5 mov %esp,%ebp 83 ec 04 sub $0x4,%esp e8 e6 ff ff ff call 8048394 <asmlinkage_test> c9 leave c3 ret関数引数が2つでregparmの設定が1の時どうなるか見てみました。
#define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(1))) int _a, _b; asmlinkage void asmlinkage_test(int a, int b) { _a = a; _b = b; } calltest() { asmlinkage_test(1, 2); } <asmlinkage_test>: 55 push %ebp 89 e5 mov %esp,%ebp 83 ec 04 sub $0x4,%esp 89 45 fc mov %eax,-0x4(%ebp)<- 第一引数をスタックへ 8b 45 fc mov -0x4(%ebp),%eax<- 第一引数の取り出し a3 2c 96 04 08 mov %eax,0x804962c 8b 45 08 mov 0x8(%ebp),%eax <- 第二引数の取り出し a3 28 96 04 08 mov %eax,0x8049628 c9 leave c3 ret <calltest>: 55 push %ebp 89 e5 mov %esp,%ebp 83 ec 08 sub $0x8,%esp c7 04 24 02 00 00 00 movl $0x2,(%esp) <- 第二引数はスタック b8 01 00 00 00 mov $0x1,%eax <- 第一引数はAX e8 ce ff ff ff call 8048394 <asmlinkage_test> c9 leave c3 ret引数1はAXに、引数2はスタックで渡しています。受け取り側では、AXをスタックに設定しなおして、スタックから引数1と引数2を取り出しています。受け取り側でわざわざスタックに戻して、処理をおこなうのは、このようなケースでも柔軟に対応する仕組みだったんですね。