2013-12-11 8 views
9

私はASMに呼び出し規約について読んでてきた、これは私がこれまでに得たものである:ASM呼び出し規則

  x86(userland) x86(kernel) x64(userland) x64(kernel) 

1st arg   Stack   EBX    RDI   RDI 
2nd arg   Stack   ECX    RSI   RSI 
3rd arg   Stack   EDX    RDX   RDX 
4th arg   Stack   ESI    RCX   R10 
5th arg   Stack   EDI    R8    R8 
6th arg   Stack   EBP    R9    R9 

result   EAX    EAX    RAX   RAX 

私の質問は以下のとおりです。

  1. は、私がこれまで正しい学んだことです?

  2. x86(カーネル)とx64(両方)で6つ以上の引数を渡すにはどうすればよいですか?スタックを使用していますか?私に小さな例を教えてください。

  3. 私はカーネルモジュールを持っており、そのモジュールの機能をASMから呼び出すつもりです。私はどのような大会を使うべきですか?カーネルまたはユーザランド?

+0

私の理解では、ユーザとカーネルの間の 'syscall'インタフェースでは4番目のカラム(" x64(kernel) "と表示されています)が使われています。カーネル内の関数間の呼び出しでは、標準のABIが使用されます( "x64(userland)"というラベルが付いています)。しかし、私はこれについての専門家ではないので、誰かが私が間違っている場合は私を修正してください。 –

+1

特別なケースでは、研究__syscall –

+0

@MarcovandeVoort:詳細を教えてください。 –

答えて

2

私はx86のみをコードしており、そのアーキテクチャ(最初の2つの列)についていくつかのフィードバックを与えることができます。

3.については、それがカーネル関数(例えばlibc関数とは対照的)であれば、カーネル規則(カラム2)を使用します。

1.については、6番目の引数にebxを使用しないことを除いて、正しいものとします。伝統的な関数プロローグは、それが実際のebpであると仮定してこの議論を押し進めるだろう。カットオフは実際には5つの引数です。

2.については、5つ以上の引数がある場合、それらを連続してメモリに格納し、ebxのこのメモリ領域の先頭へのポインタを渡します。

4

1)はい、それはLinuxだけのようです。私はあなたがここに記述されているLinuxの規則に頼ることができると思います:http://www.x86-64.org/documentation/abi.pdf。しかし、実際には引数を渡すことは自由ですそれはスタックを使用してintel assembly manual章6.3.3

2)に記載された方法は、コンパイラがそれをしない方法です。そして、

int func(int i, int j, int k, int l, int m, int n, int o, int p, int q) { return q; } 
void func2() { func(1, 2, 3, 4, 5, 6, 7, 8, 9); } 

$ gcc -c func.c && objdump -d func.o 
私x86_64のマシン上で出力

0000000000000000 <func>: 
    0: 55      push %rbp 
    1: 48 89 e5    mov %rsp,%rbp 
    4: 89 7d fc    mov %edi,-0x4(%rbp) 
    7: 89 75 f8    mov %esi,-0x8(%rbp) 
    a: 89 55 f4    mov %edx,-0xc(%rbp) 
    d: 89 4d f0    mov %ecx,-0x10(%rbp) 
    10: 44 89 45 ec    mov %r8d,-0x14(%rbp) 
    14: 44 89 4d e8    mov %r9d,-0x18(%rbp) 
    18: 8b 45 20    mov 0x20(%rbp),%eax 
    1b: 5d      pop %rbp 
    1c: c3      retq 

000000000000001d <func2>: 
    1d: 55      push %rbp 
    1e: 48 89 e5    mov %rsp,%rbp 
    21: 48 83 ec 18    sub $0x18,%rsp 
    25: c7 44 24 10 09 00 00 movl $0x9,0x10(%rsp) 
    2c: 00 
    2d: c7 44 24 08 08 00 00 movl $0x8,0x8(%rsp) 
    34: 00 
    35: c7 04 24 07 00 00 00 movl $0x7,(%rsp) 
    3c: 41 b9 06 00 00 00  mov $0x6,%r9d 
    42: 41 b8 05 00 00 00  mov $0x5,%r8d 
    48: b9 04 00 00 00   mov $0x4,%ecx 
    4d: ba 03 00 00 00   mov $0x3,%edx 
    52: be 02 00 00 00   mov $0x2,%esi 
    57: bf 01 00 00 00   mov $0x1,%edi 
    5c: e8 00 00 00 00   callq 61 <func2+0x44> 
    61: c9      leaveq 
    62: c3      retq 

3)私が言うのカーネルあなたがカーネルモジュールの中で関数を呼び出しているからです。完全な有効な例を得るためには、モジュール内のCから関数を呼び出すことができ、コンパイラがどのように処理するのかと同じ方法で.koを逆アセンブルすることができます。それはまっすぐでなければなりません。

+0

私ができるなら、私はあなたの答えを数回アップ投票します:) – alexandernst

+0

ハム...私は質問があります。どのように '' 'callq 61 ' ''は '' 'e8 00 00 00 00'''としてダンプされていますか?つまり、「e8」は「電話」を意味しますが、「」はどこから来ますか? – alexandernst

+0

この場合、objdumpはバイナリオブジェクト上で実行されるため、コンパイラはfunc1の最終仮想アドレスを認識しません。リンク処理中に解決されます。この場合、それまでは0で埋められています。だから、objdumpは次の命令を参照するcallq 61、61を表示するだけです。これは、生成されたバイナリとは何の関係もありません(hexダンプを試してみてください)。しかし、最後のa.outでobjdump -dを実行すると、0ではなくfunc1の実際の仮想アドレスに気づくでしょう。 gcc -S funcを実行することによって、gccが構築するアセンブリコードを表示する方が面白いかもしれません.c && cat func.s – Ervadac

1

カーネル呼び出し規約に関しては、効率のためにレジスタを使用します。さらに、システムコールは特権レベルの変更が必要な特別な呼び出しであり、特権レベルの変更が必要な呼び出しは別のスタックを使用するため、ebpはアクセスできないため、通常の関数プロローグ(push ebpmov ebp,espなど)ユーザパラメータ。

カーネル関数は、必要な引数を取るためにユーザースタックを覗くかもしれませんが、アーキテクチャによっては、カーネルコードからユーザーメモリにアクセスするのがx86と同じではなく、簡単で速く、Linuxは意味しません多くのアーキテクチャに移植可能です。したがって、レジスタはx86アーキテクチャにはある程度制限されていますが、引数を渡すための便利で高速なメソッドです。システムコールに6つ以上の引数が必要な場合、そのうちの1つはユーザメモリに格納されているstructへのポインタになります。カーネルは、copy_from_user()関数を使用して、必要に応じて構造体をカーネルメモリにコピーします(通常は、ioctl() syscallで行われます)。

+0

私はカーネルモジュールの中からそのASMコードを実行しているので、コードにはすでに権限があります。 – alexandernst

+0

次に、afaik、あなたは標準のCの呼び出し規約を使用します:パラメータは、スタックにプッシュされ、右から左へ。カーネルモジュール内の関数を定義する方法です。 –

+0

私は右から左へ変数にすべての私のアドレスを '' '押して' '' 'myfunc'''をコールし、次に' 'pop'''を呼び出すでしょう。 ? – alexandernst

1

Agner FogさんのCalling conventions for different C++ compilers and operating systemsの表4と表5を参照してください。これらは、C++のさまざまなコンパイラとオペレーティングシステムのためのレジスタ使用法と呼び出し規約を素早くまとめたものです。

x86-64の場合:WindowsとLinuxは1つの呼び出し規約しかありませんが、それらは異なります。 Windowsは6つのレジスタとLinux 14のレジスタを使用します。

x86の場合:WindowsとLinuxは同じ呼び出し規約を使用していますが、いくつかの呼び出し規則はcdecl, stdcall, pascal and fastcallです。コンビネーションcdecl, stdcall, and pascalはスタックを使用しますが、fastcallは2(またはコンパイラに応じて3つの)整数レジスタを使用します。デフォルトはcdeclです。

WindowsおよびLinuxには、いくつかの異なるリターンレジスタもあります。リストEAXRAXのみがありますが、 XMM0またはYMMOまたはST(0)

これらの結果は、ASM用に作成したものと似ています。

関連する問題