asmlinkage_protectマクロ


システムコールの引数はレジスタで渡たされ、従ってその関数にはasmlinkageの宣言が必要です。asmlinkageを宣言すると、改めてレジスタをスタックにセットし、あたかもスタック渡しでコールされたようにいたします。

asmlinkage_protectマクロは、openシステムコールのように、その引数をそのまま他の関数の引数となる関数をコールするだけの関数内で使われ、tail callと呼ばれる最適化に起因する問題を回避します。
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
{
       long ret;

       if (force_o_largefile())
               flags |= O_LARGEFILE;

       ret = do_sys_open(AT_FDCWD, filename, flags, mode);
       /* avoid REGPARM breakage on x86: */
       asmlinkage_protect(3, ret, filename, flags, mode);
       return ret;
}
まずtail callについてです。下記サンプルはgcc -O2でコンパイルした内容です。test1()からtest2()をコールする場合、callでなくjmpしています。
#include        "stdio.h"

void    test1(int *a);
void    test2(int *a);
int     dummy;

void    main()
{
       int     val;
       test1(&val);
}

void    test1(int *val)
{
       dummy = 1;  <-これを入れないとmainからtest1をスキップして直接test2をコールしてしいました。
       test2(val);
}

void    test2(int *val)
{
       printf("%d\n",  *val);
}

08048310 <main>:
8048310:       55                      push   %ebp
8048311:       89 e5                   mov    %esp,%ebp
8048313:       83 e4 f0                and    $0xfffffff0,%esp
8048316:       83 ec 20                sub    $0x20,%esp
8048319:       8d 44 24 1c             lea    0x1c(%esp),%eax
804831d:       89 04 24                mov    %eax,(%esp)
8048320:       e8 db 00 00 00          call   8048400 <test1>
8048325:       c9                      leave
8048326:       c3                      ret
8048327:       90                      nop

080483e0 <test2>:
80483e0:       83 ec 1c                sub    $0x1c,%esp
80483e3:       8b 44 24 20             mov    0x20(%esp),%eax
80483e7:       8b 00                   mov    (%eax),%eax
80483e9:       c7 04 24 e4 84 04 08    movl   $0x80484e4,(%esp)
80483f0:       89 44 24 04             mov    %eax,0x4(%esp)
80483f4:       e8 e7 fe ff ff          call   80482e0 <printf@plt>
80483f9:       83 c4 1c                add    $0x1c,%esp
80483fc:       c3                      ret
80483fd:       8d 76 00                lea    0x0(%esi),%esi

08048400 <test1>:
8048400:       c7 05 20 97 04 08 01    movl   $0x1,0x8049720
8048407:       00 00 00
804840a:       e9 d1 ff ff ff          jmp    80483e0 <test2>
804840f:       90                      nop
この最適化は、先のopenシステムコール関数では問題です。コンパイラの処理系によっては、asmlinkageの処理をすることなく、open()がdo_sys_open()をjmpで呼び出す可能性があるからです。たぶん。do_sys_open()はasmlinkage宣言されていません。しかもこの引数はスタック上にありません。

この回避として、引数のfilename/flags/modeを参照する命令を記述しておけばいいわけですが、その為ダミーの変数を定義し、それに代入すると、メモリtoメモリで無駄が発生します。また最適化でスキップされるかもしれない。asmlinkage_protectは直接レジスタに代入する事で実現します。
#define asmlinkage_protect(n, ret, args...) \
       __asmlinkage_protect##n(ret, ##args)
#define __asmlinkage_protect_n(ret, args...) \
       __asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), ##args)
#define __asmlinkage_protect0(ret) \
       __asmlinkage_protect_n(ret)
#define __asmlinkage_protect1(ret, arg1) \
       __asmlinkage_protect_n(ret, "g" (arg1))
#define __asmlinkage_protect2(ret, arg1, arg2) \
       __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2))
#define __asmlinkage_protect3(ret, arg1, arg2, arg3) \
       __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3))
#define __asmlinkage_protect4(ret, arg1, arg2, arg3, arg4) \
       __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3), \
                             "g" (arg4))
#define __asmlinkage_protect5(ret, arg1, arg2, arg3, arg4, arg5) \
       __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3), \
                             "g" (arg4), "g" (arg5))
#define __asmlinkage_protect6(ret, arg1, arg2, arg3, arg4, arg5, arg6) \
       __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3), \
                             "g" (arg4), "g" (arg5), "g" (arg6))

#define __asmlinkage_protect3(ret, arg1, arg2, arg3) \
        __asmlinkage_protect_n(ret, "g" (arg1), "g" (arg2), "g" (arg3))
opneシステムコールの場合、__asmlinkage_protect3マクロで以下の様な拡張インラインアセンブラに展開されます。asmlinkage故、各引数はレジスタに設定するため参照されます。従っていったんスタックに設定することが保障されます。そこで、do_sys_open()をjmpで呼び出されても問題ないわけです。
__asm__ __volatile__ ("" : "=r" (ret) : "0" (ret), "g" (filename), "g" (flags), "g" (mode))
filename, flags, modeを参照する。と言う実だけが必要なので、任意の汎用レジスタに代入すればいいわけですが、retは返り値となるため、セットしたレジスタでretに代入しなければなりません。

最終更新 2013/07/13 00:39:51 - north
(2013/07/05 01:48:03 作成)


検索

アクセス数
3697902
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。