asmlinkage

LilyVMのコード読んでいるとき、asmlinkageのマクロが出てきたが定義がされていない。Linuxカーネルなどにも使用されているので調べてみた。

asmlinkageマクロとは

Linuxカーネルなどによく見かけるマクロの一つとして、asmlinkageマクロがある。このマクロは関数の整数引数を確実に、スタックに渡すことを保証するマクロである。Linuxではシステムコールなどに使われる。システムコールの引数の渡しかたは次の様になる。

  1. システムコールの引数を各レジスタに代入
  2. ソフトウェア割り込み(int0x80)を行う。
  3. カーネル権限に移行後、レジスタから各引数を取り出す。

システムコールの引数は、スタックから各レジスタに代入を行う実装なので、asmlinkageマクロを使用する。

GCC拡張 regparm (number)

asmlinkageのコードは以下の様になっている。

#define asmlinkage __attribute__((regparm(0)))

regparm (number)はコンパイラにnumberの数だけ引数をレジスタに渡します。i386では最大3個まで渡すことが可能です。eax, edx, ecxレジスタを使用します。x86_64環境では最大6個まで渡すことが可能です。edi, esi, edx, ecx,r8d, r9dレジスタを使用します。

regparm使用した結果

i386環境
#include <stdio.h>

__attribute__((regparm(3))) int add(int a1, int a2, int a3, int a4,
				    int a5, int a6, int a7, int a8, int a9)
{
        return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9;
}

int main(void)
{
        int ret = add(1, 2, 3, 4, 5, 6, 7, 8, 9);

        printf("ret: %d\n", ret);

        return 0;
}

/*
$ objdump -d a.out

08048354 <add>:
 8048354:       55                      push   %ebp
 8048355:       89 e5                   mov    %esp,%ebp
 8048357:       83 ec 0c                sub    $0xc,%esp
 804835a:       89 45 fc                mov    %eax,0xfffffffc(%ebp)
 804835d:       89 55 f8                mov    %edx,0xfffffff8(%ebp)
 8048360:       89 4d f4                mov    %ecx,0xfffffff4(%ebp)
 8048363:       8b 45 f8                mov    0xfffffff8(%ebp),%eax
 8048366:       03 45 fc                add    0xfffffffc(%ebp),%eax
 8048369:       03 45 f4                add    0xfffffff4(%ebp),%eax
 804836c:       03 45 08                add    0x8(%ebp),%eax
 804836f:       03 45 0c                add    0xc(%ebp),%eax
 8048372:       03 45 10                add    0x10(%ebp),%eax
 8048375:       03 45 14                add    0x14(%ebp),%eax
 8048378:       03 45 18                add    0x18(%ebp),%eax
 804837b:       03 45 1c                add    0x1c(%ebp),%eax
 804837e:       c9                      leave
 804837f:       c3                      ret
*/
x86_64環境
#include <stdio.h>
__attribute__((regparm(3))) int add(int a1, int a2, int a3, int a4,
				    int a5, int a6, int a7, int a8, int a9)
{
        return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9;
}

int main(void)
{
        int ret = add(1, 2, 3, 4, 5, 6, 7, 8, 9);

        printf("ret: %d\n", ret);

        return 0;
}

/*
$ objdump -d a.out

0000000000400478 <add>:
  400478:       55                      push   %rbp
  400479:       48 89 e5                mov    %rsp,%rbp
  40047c:       89 7d fc                mov    %edi,0xfffffffffffffffc(%rbp)
  40047f:       89 75 f8                mov    %esi,0xfffffffffffffff8(%rbp)
  400482:       89 55 f4                mov    %edx,0xfffffffffffffff4(%rbp)
  400485:       89 4d f0                mov    %ecx,0xfffffffffffffff0(%rbp)
  400488:       44 89 45 ec             mov    %r8d,0xffffffffffffffec(%rbp)
  40048c:       44 89 4d e8             mov    %r9d,0xffffffffffffffe8(%rbp)
  400490:       8b 45 f8                mov    0xfffffffffffffff8(%rbp),%eax
  400493:       03 45 fc                add    0xfffffffffffffffc(%rbp),%eax
  400496:       03 45 f4                add    0xfffffffffffffff4(%rbp),%eax
  400499:       03 45 f0                add    0xfffffffffffffff0(%rbp),%eax
  40049c:       03 45 ec                add    0xffffffffffffffec(%rbp),%eax
  40049f:       03 45 e8                add    0xffffffffffffffe8(%rbp),%eax
  4004a2:       03 45 10                add    0x10(%rbp),%eax
  4004a5:       03 45 18                add    0x18(%rbp),%eax
  4004a8:       03 45 20                add    0x20(%rbp),%eax
  4004ab:       c9                      leaveq
  4004ac:       c3                      retq
*/