2016-02-25 3 views
5

システムコールを行う汎用関数を記述したいと思います。何かのようにLinux上のシステムコール引数のタイプは?

long my_syscall2(long number, long arg1, long arg2); 

できるだけポータブルにしたいです。実装は明らかにすべてのアーキテクチャで異なります。関数のシグネチャも異なる必要がありますか? longを使用することはできますか?何か他のものを使用する必要がありますか?

  • カーネルは、いくつかのdark magicを使用しています:(__SYSCALL_DEFINExは、__SC_LONGは魔法が含まれているタイプを取得するために__SC_LONGを呼び出して)ここ

    は、私が見つけた可能な解決策です。私はどこかで、ユーザ空間の型がカーネル空間の型と同じではないと聞いたので、私はそれを使うことができるかどうかわかりません。
  • musl-libc uses long for all architectures that it supports except x32:([arch] /syscall_arch.hで定義されています)。
  • 私は、サポートするすべてのプロセッサアーキテクチャとコンパイラのドキュメント、レジスタサイズと整数型サイズを検索し、レジスタと同じサイズの整数型を選択できます。だから、

私は質問があると思います「『システムコールの引数の型は常にx32のようないくつかの例外を除いてlongある』または私はすべてのアーキテクチャとコンパイラのドキュメントを検索しないといけないと言ういくつかのルールがありますか?」

編集:私はいくつかのシステムコールをパラメータとしてポインタと他のタイプを取ることを知っています。私はジェネリックパラメータ型を使って任意のシステムコールを呼び出すことができるジェネリック関数を記述したいと思います。これらの汎用パラメータ型は、実際のパラメータ型を保持するのに十分な大きさでなければなりません。 syscall()機能があるので可能です。

Edit2:これは、この問題の別の部分的な解決方法です。現在、次のようになり、これらの機能の

実装:

static __inline long my_syscall2(long number, long arg1, long arg2) 
{ 
    unsigned long ret; 
    __asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(number), "D"(arg1), "S"(arg2) 
         : "rcx", "r11", "memory"); 
    return ret; 
} 

は興味深い部分は、これがレジスタaに格納されたシステムコールの戻り値が変数retに保存する必要があることを意味し、"=a"(ret)です。ローカル変数を作成する関数を書く代わりに、syscallを作成し、その戻り値を変数に保存し、変数を返します。これは、システムコールを作成し、その結果を呼び出し側が提供する変数に格納する変数です。それは次のようになります。

#define my_syscall2(RET, NUMBER, ARG1, ARG2) \ 
    __asm__ __volatile__ ("syscall" : "=a"(RET) : "a"(NUMBER), "D"(ARG1), "S"(ARG2) \ 
         : "rcx", "r11", "memory"); 

そして、それは次のように使用されるでしょう:私は値を保持するのに十分な大きさのサイズと整数型を登録知る必要はありません

long result; 
void * arg1; 
int arg2; 
my_syscall2(result, <syscall number>, arg1, arg2); 

この方法レジスタの

+0

パラメータを使用しないシステムコールはどのように処理しますか? – edmz

+0

@black私は6つの関数を定義しますmy_syscall {0-5} – alkedr

+1

実際にはここでは簡単に答えられるよりも複雑ですが(アーキテクチャを指定しない限り)、私のパブリックドメイン実装のsyscall()マクロとsyscall0 )-syscall6()関数はhttps://github.com/technosaurus/BQC ...すべてがパラメータのために長くキャストされています – technosaurus

答えて

1

一般的な解決策はありません。

#if ARCH_WITH_32BIT_REGS 
typedef uint32_t reg_size_int_t; 
#elif ARCH_WITH_64BIT_REGS 
typedef uint64_t reg_size_int_t; 
#elif ARCH_WITH_16BIT_REGS 
typedef uint16_t reg_size_int_t; 
.... 
#endif 

reg_size_int_t syscall_1(reg_size_t nr, reg_size_t arg0); 
... 

をしかし、最も一般的に使用されるアーキテクチャ用レジスタのサイズが長いと同じである:あなたのコード超multiarchを作りたい場合は、ちょうどそのような何かを行うことができます。

3

自分で書き込むのではなく、既存のsyscallシステムコールを使用することをお勧めします。あなたの望むものとまったく同じように思えます。あなたが提起した正当な質問については、マニュアルページの「アーキテクチャ特有の要件」を参照してください。

+2

Funfully、 'syscall syscallではなく、関連するsyscall命令のライブラリラッパーです。 – EOF

+1

はい、gnu拡張子を持つ標準のcライブラリも必要で、グローバルなerrno変数にエラーコードを返します。私は、これらは大きな問題ではないことを認めますが、すべてを使用しない簡単な方法があれば、私はさまよっていました。 – alkedr

+1

あなたの特定のニーズに合わせて 'syscall'ソースコードを修正することもできます。 –

4

システムコール引数はレジスタで渡されます。したがって、サイズはCPUレジスタのサイズに制限されます。つまり、32ビットアーキテクチャでは32ビット、64ビットアーキテクチャでは64ビットです。浮動小数点数はこのようにカーネルに渡すことはできません。伝統的に、カーネルは浮動小数点命令を使用しません(浮動小数点命令は通常はカーネルへの入力時に保存されないため、できない場合があります)ので、システムコールでは浮動小数点数を避けてください。

より小さい型の引数を使用するシステムコールでは、ゼロまたは符号が拡張されます。より大きい引数型を使用するシステムコールでは、引数を複数のレジスタに分割できます。

多くのパラメータを持つシステムコール(mmap()など)は、パラメータを構造体へのポインタとして渡すことで実装できますが、これは測定可能なパフォーマンスオーバーヘッドがあるため、5つ以上のパラメータでシステムコールを設計することは避けてください。

1日の終わりに、送信する値に適したタイプを使用します。 libcが適切な場所にデータを置くようにしましょう。

関連する問題