c関数をアセンブリから呼び出すにはasmlinkageが必要ですか?



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


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


C 関数を定義するときに「asmlinkage」修飾子と混同しています。


asmlinkage は、パラメーターがスタックを介して渡されることをコンパイラーに通知することを知っています。


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


質問:


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


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


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


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


最後に、このようなアセンブリ/C プログラミングが混在する場合に参照できるリソース/書籍を推奨してくれる人はいますか?


答え:


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


(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 のデフォルトの呼び出し規約は何ですか。 C 関数を定義するときに「asmlinkage」を省略した場合、_cdecl または fastcall を意味しますか?


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


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


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