asmlinkageってなに?
Rev.5を表示中。最新版はこちら。
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を取り出しています。受け取り側でわざわざスタックに戻して、処理をおこなうのは、このようなケースでも柔軟に対応する仕組みだったんですね。補足
呼び出し側の引数はpushでスタックされるものと思ってましたが、関数入り口で引数も考慮した形でスタックを確保(SPの減算)し、movで直接スタックにセットしているのは意外でした。後記
引数はかならずスタック渡しを保障するための指定らしい。処理系によってはCの引数渡しは最適化でレジスタ渡しで行うものがあるのかもしれない・・・。よろしくさんへ
void test(void);
main()
{
test();
}
void test()
{
int a, b;
unsigned char c;
a = 0xaea4a4af;
c = 1;
b = c;
}
080483a1 <test>: 80483a1: 55 push %ebp 80483a2: 89 e5 mov %esp,%ebp 80483a4: 83 ec 10 sub $0x10,%esp 80483a7: c7 45 fc af a4 a4 ae movl $0xaea4a4af,-0x4(%ebp) 80483ae: c6 45 fb 01 movb $0x1,-0x5(%ebp) 80483b2: 0f b6 45 fb movzbl -0x5(%ebp),%eax 80483b6: 89 45 f4 mov %eax,-0xc(%ebp)movl %ebp, %esp 80483b9: c9 leave 80483ba: c3 retleaveはmovl %ebp,%esp/popl %ebpです。movl %ebp,%espでpush %ebpした時のスタックポインタに戻し、pop %epbでebpを復帰します。これでスタックポインタはコールされた時のそれとなり、この時のスタックポインタの内容は返りアドレスとなるわけで、retで呼び出し元に復帰できるわけです。






