アセンブリ コードから呼び出される 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 を使用するかはまだ明確ではありません。参照するガイドラインとリソースがどうしても必要です。