無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

asmlinkageってなに?


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) ->a = 0xaea4a4af
 80483ae:       c6 45 fb 01             movb   $0x1,-0x5(%ebp)        ->c = 1
 80483b2:       0f b6 45 fb             movzbl -0x5(%ebp),%eax        ->b = c
 80483b6:       89 45 f4                mov    %eax,-0xc(%ebp)        ->b = c
 80483b9:       c9                      leave
 80483ba:       c3                      ret
leaveはmovl %ebp,%esp/popl %ebpです。movl %ebp,%espでpush %ebpした時のスタックポインタに戻し、pop %epbでebpを復帰します。これでスタックポインタはコールされた時のそれとなり、この時のスタックポインタの内容は返りアドレスとなるわけで、retで呼び出し元に復帰できるわけです。

最終更新 2013/06/03 18:32:05 - north
(2010/03/05 15:06:23 作成)