18

アセンブリコードから呼び出されるC関数を書いています。

(具体的には、Linuxカーネルのシステムコール処理のパスでチェックジョブを実行したいので、entry_32.Sでシステムコールがディスパッチされる前にc関数を呼び出します)

c関数を定義するときに、「asmlinkage」修飾子と混同されます。

asmlinkageは、パラメーターがスタックを通過することをコンパイラーに通知することです。

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

質問:

(1)アセンブリコードから呼び出される関数を定義する場合、asmlinkageは必要ですか?

(2)gccのデフォルトの呼び出し規約は何ですか。ac関数を定義するときに「asmlinkage」を省略した場合、それは_cdeclまたはfastcallを意味しますか?

(3)デフォルトの呼び出し規約がcdeclの場合、cdeclがasmlinkage修飾子と等しいと考えると、なぜasmlinkageが必要なのですか?(私はここで正しいですか?)

(4)これらのシステムコール関数がすべてasmlinkageで宣言されているのはなぜですか。最初にパラメータをレジスタにコピーしてから、それらのシステムコール関数を呼び出すことはできますか?私の見解では、x86では、システムコールを発行するときに、パラメーターはレジスターに簡単に保存されます。では、なぜわざわざスタックに保存して、スタック規則を介してそのような受け渡しパラメーターを強制するのでしょうか。

最後に、そのようなミックスアセンブリ/ cプログラミングのために私が参照できるいくつかのリソース/本を誰かが推薦できますか?

4

3 に答える 3

17

数時間の調査の後、次の経験的ポイントを得ました。

(1) アセンブリコードから呼び出される関数を定義する場合、asmlinkage は必要ですか?

いいえ、実際には fastcall が頻繁に使用されます。

たとえば、entry_32.S で「call」を検索すると、このアセンブリ ファイルから呼び出されるすべての c 関数を取得できます。すると、多くの人が呼び出し規約として asmlinkage の代わりに fastcall を使用していることがわかります。例えば、

    /*in entry_32.S*/
    movl PT_OLDESP(%esp), %eax
    movl %esp, %edx
    call patch_espfix_desc

    /*in traps_32.c*/
    fastcall unsigned long patch_espfix_desc(unsigned long uesp,
                  unsigned long kesp)

(2) gcc のデフォルトの呼び出し規約は何ですか。ac 関数を定義するときに「asmlinkage」を省略した場合、_cdecl または fastcall を意味しますか?

(3) デフォルトの呼び出し規約が cdecl の場合、cdecl が asmlinkage 修飾子と等しいと考えると、なぜ asmlinkage が必要なのですか? (ここで正しいですか?)

アセンブリ コードから呼び出されない C 関数の場合、デフォルトの呼び出し規則は cdecl (または高速呼び出しです。gcc がパラメーターの受け渡しのために呼び出し元と呼び出し先を処理するため、問題ではありません。デフォルトの呼び出し規則は次のようになります。コンパイル時に指定します)。ただし、アセンブリ コードから呼び出される C 関数については、関数の呼び出し規則を明示的に宣言する必要があります。これは、アセンブリ側のパラメーター受け渡しコードが修正されているためです。たとえば、patch_espfix_desc が asmlinkage として宣言されている場合、gcc は関数をコンパイルしてスタックからパラメーターを取得します。これは、パラメーターをレジスターに入れるアセンブリー側と矛盾しています。

しかし、いつ asmlinkage を使用し、いつ fastcall を使用するかはまだ明確ではありません。参照するガイドラインとリソースが本当に必要です。

于 2012-04-08T14:37:43.673 に答える
14

質問(4)に自分で答えたいと思います。

すべてのシステムコール関数sys_*(sys_gettimeofdayなど)がスタックを使用してパラメーターを渡すのはなぜですか?

その理由は、ユーザースペースからのシステムコール要求を処理するときに、カーネルが(ユーザースペースに戻る前に環境を復元するために)すべてのレジスターをスタックに保存する必要があるためです。その後、パラメーターはスタックで使用可能になります。つまり、余分な労力は必要ありません。

一方、呼び出し規約にfastcallを使用する場合は、さらに多くの作業を行う必要があります。最初に、ユーザープログラムがシステムコールを発行するときに、x86-linuxでは%eaxがsyscall番号用であり、%ebx、%ecx、%edx、%esi、%edi、%ebpが6の受け渡しに使用されることを知る必要があります。システムコールのパラメータ(「int80h」または「sysenter」の前)。ただし、fastcallの呼び出し規約では、最初のパラメーターを%eaxに、2番目のパラメーターを%edxに、3番目のパラメーターを%ecxに渡し、その他は右から左にスタックにプッシュされます。このように、カーネルでこのようなfastcall規則を適用するには、スタック上のすべてのレジスタを保存することに加えて、これらを何らかの方法で配置する必要があります。

于 2012-04-08T05:29:59.630 に答える
3

これは、デフォルトの呼び出し規約をより効率的なもの (つまり、より多くの引数をレジスターに渡す) に変更する gcc オプションを使用してカーネルをコンパイルできるようにすることだと思います。ただし、asm から呼び出す必要がある関数は、使用中の gcc オプションに基づいて呼び出し規則を変えることはできません。そうしないと、サポートされている gcc オプションのセットごとに別のバージョンの asm が必要になります。したがって、固定された呼び出し規則 (特別な gcc オプションなしでデフォルトに一致する) を使用する必要がある関数は、呼び出し規則が固定されたままになるように、特別な属性で宣言されます。

于 2012-04-08T04:04:14.603 に答える