次のリンクは、UNIX(BSDフレーバー)とLinuxの両方のx86-32システム呼び出し規則を説明しています。
しかし、UNIXとLinuxの両方でのx86-64システム呼び出し規則は何ですか?
ここでのトピックの詳細については、 Linuxシステムコールの決定版ガイド
LinuxでGNU Assembler(gas)を使用してこれらを検証しました。
x86-32別名i386 Linuxシステムコール規約:
X86-32では、Linuxシステムコールのパラメーターはレジスタを使用して渡されます。 syscall_numberの%eax
。 %ebx、%ecx、%edx、%esi、%edi、%ebpは、6つのパラメーターをシステムコールに渡すために使用されます。
戻り値は%eax
にあります。他のすべてのレジスタ(EFLAGSを含む)は、int $0x80
全体にわたって保持されます。
Linux Assembly Tutorial から次のスニペットを取りましたが、これについては疑わしいです。誰かが例を示すことができれば、それは素晴らしいことです。
6つ以上の引数がある場合、
%ebx
には引数のリストが保存されているメモリの場所が含まれている必要があります。
例ともう少し読むには、 http://www.int80h.org/bsdasm/#alternate-calling-convention を参照してください。 int 0x80
を使用したi386 LinuxのHello Worldの別の例: このHelloWorldアセンブリコードのどの部分が、プログラムをアセンブリで記述する場合に不可欠ですか?
sysenter
を使用して、32ビットシステムコールを行うより高速な方法があります。カーネルは、sysenter
ダンスのユーザー空間側で、メモリのページをすべてのプロセス(vDSO)にマッピングします。これは、リターンアドレスを見つけるためにカーネルと協力する必要があります。引数からレジスタへのマッピングは、int $0x80
の場合と同じです。通常、sysenter
を直接使用する代わりに、vDSOを呼び出す必要があります。 (vDSOへのリンクと呼び出しの詳細、およびsysenter
の詳細については、 Linuxシステムコールの最終ガイド を参照してください、およびシステムコールに関連するその他すべて)
x86-32 [Free | Open | Net | DragonFly] BSD UNIXシステムコール規則:
パラメーターはスタックで渡されます。パラメーター(最後にプッシュされたパラメーター)をスタックにプッシュします。次に、追加の32ビットのダミーデータをプッシュし(実際にはダミーデータではありません。詳細については、次のリンクを参照してください)、システムコール命令を与えますint $0x80
http://www.int80h.org/bsdasm/#default-calling-convention
x86-64 Mac OS Xは似ていますが異なります 。 TODO:* BSDの機能を確認してください。
System V Application Binary Interface AMD64 Architecture Processor Supplement の「A.2 AMD64Linuxカーネル規約」セクションを参照してください。 i386およびx86-64 System V psABIの最新バージョンは、 ABIメンテナーのリポジトリのこのページからリンクされています にあります。 ( x86 タグwikiも参照して、最新のABIリンクや、x86 asmに関するその他の多くの優れた情報を入手してください。)
このセクションの抜粋は次のとおりです。
- ユーザーレベルのアプリケーションは、シーケンス%rdi、%rsi、%rdx、%rcx、%r8、および%r9を渡すための整数レジスタとして使用します。 カーネルインターフェイスは、%rdi、%rsi、%rdx、%r10、%r8、および%r9を使用します。
- システムコールは、
syscall
命令を介して行われます。この clobbers%rcxおよび%r11 および%raxの戻り値ですが、他のレジスタは保持されます。- Syscallの番号は、レジスタ%raxで渡す必要があります。
- システムコールは6つの引数に制限され、引数はスタックに直接渡されません。
- システムコールから戻ると、レジスタ%raxにはシステムコールの結果が含まれます。 -4095〜-1の範囲の値はエラーを示し、
-errno
です。- クラスINTEGERまたはクラスMEMORYの値のみがカーネルに渡されます。
これは、ABIのLinux固有の付録からのものであり、Linuxの場合でも、参考ではなく参考になります。 (しかし、実際は正確です。)
この32ビットint $0x80
ABIisは、64ビットコードで使用できます(ただし、強くお勧めしません)。 64ビットコードで32ビットint 0x80 Linux ABIを使用するとどうなりますか? 入力が32ビットに切り捨てられるため、ポインターに適さず、r8-r11がゼロになります。
x86-32関数呼び出し規約:
X86-32では、パラメータがスタックで渡されました。最後のパラメーターは、すべてのパラメーターが完了するまで最初にスタックにプッシュされ、その後call
命令が実行されました。これは、アセンブリからLinux上のCライブラリ(libc)関数を呼び出すために使用されます。
最新バージョンのi386 System V ABI(Linuxで使用)では、x86-64 System V ABIが常に必要とするように、call
の前に%esp
の16バイトのアライメントが必要です。呼び出し先は、アラインされていないときにフォールトするSSE 16バイトのロード/ストアを想定して使用することができます。しかし歴史的に、Linuxは4バイトのスタックアライメントのみを必要としていたため、8バイトのdouble
などでも、自然にアライメントされたスペースを確保するには余分な作業が必要でした。
他の一部の最新の32ビットシステムでは、依然として4バイトを超えるスタックアライメントは必要ありません。
x86-64 System Vは、引数をレジスタに渡します。これは、i386 System Vのスタック引数規則よりも効率的です。 argsをメモリ(キャッシュ)に保存し、呼び出し先で再度ロードするというレイテンシと余分な命令を回避します。より多くのレジスタが利用可能であるため、これはうまく機能します。また、レイテンシと異常な実行が問題となる最新の高性能CPUにはより適しています。 (i386 ABIは非常に古いです)。
このnewメカニズム:最初に、パラメーターはクラスに分割されます。各パラメーターのクラスは、呼び出された関数に渡される方法を決定します。
詳細については、「 System V Application Binary Interface AMD64 Architecture Processor Supplement 」の「3.2関数呼び出しシーケンス」を参照してください。
引数が分類されると、レジスタは次のように渡すために(左から右の順序で)割り当てられます。
- クラスがMEMORYの場合、引数をスタックに渡します。
- クラスがINTEGERの場合、シーケンス%rdi、%rsi、%rdx、%rcx、%r8、および%r9の次に使用可能なレジスタが使用されます
したがって、%rdi, %rsi, %rdx, %rcx, %r8 and %r9
は、レジスタin orderです。整数/ポインタ(つまりINTEGERクラス)パラメータをアセンブリからlibc関数に渡すために使用されます。 %rdiは、最初のINTEGERパラメーターに使用されます。 2番目は%rsi、3番目は%rdxなどです。次に、call
命令を指定する必要があります。 call
を実行するとき、スタック(%rsp
)は16Bに揃える必要があります。
6個を超えるINTEGERパラメーターがある場合、7番目のINTEGERパラメーター以降がスタックに渡されます。 (呼び出し元は、x86-32と同じようにポップします。)
最初の8つの浮動小数点引数は、後でスタックで%xmm0-7に渡されます。呼び出し保存されたベクトルレジスタはありません。 (FPと整数の引数が混在する関数は、合計で8個を超えるレジスタ引数を持つことができます。)
可変長関数( printf
など )には常に%al
= FPレジスタ引数の数が必要です。
構造体をレジスタ(rdx:rax
戻り時)とメモリ内のどちらにパックするかについてのルールがあります。詳細についてはABIを参照し、コンパイラの出力をチェックして、コードがコンパイラとどのように渡されるか/返されるかについて合意していることを確認してください。
Windows x64関数呼び出し規約 にはx86-64 System Vと複数の重要な違いがあることに注意してください。たとえば、シャドウスペースは、呼び出し元によってmustが予約されます(レッドゾーンの代わりに)、コール保存されたxmm6-xmm15。そして、どの引数がどのレジスタに入るかについての非常に異なるルール。
おそらく、x86_64 ABIをお探しですか?
それが正確にあなたが望んでいるものではない場合、代替検索を見つけるためにあなたの好みの検索エンジンで「x86_64 abi」を使用してください。
呼び出し規約は、他のプログラムを呼び出したり呼び出したりするときに、レジスターでパラメーターを渡す方法を定義します。そして、これらの規約の最良のソースは、これらのハードウェアごとに定義されたABI標準の形式です。コンパイルを簡単にするために、同じABIがユーザー空間とカーネルプログラムでも使用されます。 Linux/Freebsdは、x86-64では同じABIを、32ビットでは別のセットを使用します。ただし、x86-64 ABI for WindowsはLinux/FreeBSDとは異なります。通常、ABIはシステムコールと通常の「関数呼び出し」を区別しません。つまり、x86_64呼び出し規約の特定の例であり、Linuxユーザー空間とカーネルの両方で同じです: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on -x86-64 / (パラメータのシーケンスa、b、c、d、e、fに注意してください):
パフォーマンスは、これらのABIの理由の1つです(たとえば、メモリスタックに保存する代わりにレジスタを介してパラメーターを渡す)
ARMには、さまざまなABIがあります。
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.swdev.abi/index.html
ARM64の規則:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf
Linux on PowerPCの場合:
http://refspecs.freestandards.org/elf/elfspec_ppc.pdf
http://www.0x04.net/doc/elf/psABI-ppc64.pdf
また、埋め込み用にはPPC EABIがあります。
http://www.freescale.com/files/32bit/doc/app_note/PPCEABI.pdf
このドキュメントは、すべての異なる規則の概要です。
Linuxカーネル5.0ソースコメント
X86の仕様はArch/x86
の下にあり、syscallのものはArch/x86/entry
の下にあることを知っていました。したがって、そのディレクトリ内の簡単なgit grep rdi
は、私を Arch/x86/entry/entry_64.S に導きます。
/*
* 64-bit SYSCALL instruction entry. Up to 6 arguments in registers.
*
* This is the only entry point used for 64-bit system calls. The
* hardware interface is reasonably well designed and the register to
* argument mapping Linux uses fits well with the registers that are
* available when SYSCALL is used.
*
* SYSCALL instructions can be found inlined in libc implementations as
* well as some other programs and libraries. There are also a handful
* of SYSCALL instructions in the vDSO used, for example, as a
* clock_gettimeofday fallback.
*
* 64-bit SYSCALL saves rip to rcx, clears rflags.RF, then saves rflags to r11,
* then loads new ss, cs, and rip from previously programmed MSRs.
* rflags gets masked by a value from another MSR (so CLD and CLAC
* are not needed). SYSCALL does not save anything on the stack
* and does not change rsp.
*
* Registers on entry:
* rax system call number
* rcx return address
* r11 saved rflags (note: r11 is callee-clobbered register in C ABI)
* rdi arg0
* rsi arg1
* rdx arg2
* r10 arg3 (needs to be moved to rcx to conform to C ABI)
* r8 arg4
* r9 arg5
* (note: r12-r15, rbp, rbx are callee-preserved in C ABI)
*
* Only called from user space.
*
* When user can change pt_regs->foo always force IRET. That is because
* it deals with uncanonical addresses better. SYSRET has trouble
* with them due to bugs in both AMD and Intel CPUs.
*/
32ビットの場合 Arch/x86/entry/entry_32.S :
/*
* 32-bit SYSENTER entry.
*
* 32-bit system calls through the vDSO's __kernel_vsyscall enter here
* if X86_FEATURE_SEP is available. This is the preferred system call
* entry on 32-bit systems.
*
* The SYSENTER instruction, in principle, should *only* occur in the
* vDSO. In practice, a small number of Android devices were shipped
* with a copy of Bionic that inlined a SYSENTER instruction. This
* never happened in any of Google's Bionic versions -- it only happened
* in a narrow range of Intel-provided versions.
*
* SYSENTER loads SS, ESP, CS, and EIP from previously programmed MSRs.
* IF and VM in RFLAGS are cleared (IOW: interrupts are off).
* SYSENTER does not save anything on the stack,
* and does not save old EIP (!!!), ESP, or EFLAGS.
*
* To avoid losing track of EFLAGS.VM (and thus potentially corrupting
* user and/or vm86 state), we explicitly disable the SYSENTER
* instruction in vm86 mode by reprogramming the MSRs.
*
* Arguments:
* eax system call number
* ebx arg1
* ecx arg2
* edx arg3
* esi arg4
* edi arg5
* ebp user stack
* 0(%ebp) arg6
*/
glibc 2.29 Linux x86_64システムコールの実装
それでは、主要なlibc実装を見て、それらが何をしているのか見てみましょう。
この答えを書いているときに、私が今使っているglibcを調べるよりも良いことはありますか? :-)
glibc 2.29は、x86_64 syscallsを sysdeps/unix/sysv/linux/x86_64/sysdep.h
で定義し、それにはいくつかの興味深いコードが含まれています。
/* The Linux/x86-64 kernel expects the system call parameters in
registers according to the following table:
syscall number rax
arg 1 rdi
arg 2 rsi
arg 3 rdx
arg 4 r10
arg 5 r8
arg 6 r9
The Linux kernel uses and destroys internally these registers:
return address from
syscall rcx
eflags from syscall r11
Normal function call, including calls to the system call stub
functions in the libc, get the first six parameters passed in
registers and the seventh parameter and later on the stack. The
register use is as follows:
system call number in the DO_CALL macro
arg 1 rdi
arg 2 rsi
arg 3 rdx
arg 4 rcx
arg 5 r8
arg 6 r9
We have to take care that the stack is aligned to 16 bytes. When
called the stack is not aligned since the return address has just
been pushed.
Syscalls of more than 6 arguments are not supported. */
そして:
/* Registers clobbered by syscall. */
# define REGISTERS_CLOBBERED_BY_SYSCALL "cc", "r11", "cx"
#undef internal_syscall6
#define internal_syscall6(number, err, arg1, arg2, arg3, arg4, arg5, arg6) \
({ \
unsigned long int resultvar; \
TYPEFY (arg6, __arg6) = ARGIFY (arg6); \
TYPEFY (arg5, __arg5) = ARGIFY (arg5); \
TYPEFY (arg4, __arg4) = ARGIFY (arg4); \
TYPEFY (arg3, __arg3) = ARGIFY (arg3); \
TYPEFY (arg2, __arg2) = ARGIFY (arg2); \
TYPEFY (arg1, __arg1) = ARGIFY (arg1); \
register TYPEFY (arg6, _a6) asm ("r9") = __arg6; \
register TYPEFY (arg5, _a5) asm ("r8") = __arg5; \
register TYPEFY (arg4, _a4) asm ("r10") = __arg4; \
register TYPEFY (arg3, _a3) asm ("rdx") = __arg3; \
register TYPEFY (arg2, _a2) asm ("rsi") = __arg2; \
register TYPEFY (arg1, _a1) asm ("rdi") = __arg1; \
asm volatile ( \
"syscall\n\t" \
: "=a" (resultvar) \
: "0" (number), "r" (_a1), "r" (_a2), "r" (_a3), "r" (_a4), \
"r" (_a5), "r" (_a6) \
: "memory", REGISTERS_CLOBBERED_BY_SYSCALL); \
(long int) resultvar; \
})
私はかなり自明だと感じています。これが、通常のSystem V AMD64 ABI関数の呼び出し規約に正確に一致するように設計されているように見えることに注意してください。 https://en.wikipedia.org/wiki/X86_calling_conventions#List_of_x86_calling_conventions
クラッバーの簡単なリマインダー:
cc
はフラグレジスタを意味します。しかし Peter Cordesのコメント これはここでは不要です。memory
は、ポインタがアセンブリに渡され、メモリへのアクセスに使用されることを意味しますゼロからの明示的な最小限の実行可能な例については、この回答を参照してください。 インラインアセンブリでsysenterを介してシステムコールを呼び出す方法
アセンブリ内のいくつかのsyscallを手動で作成します
それほど科学的ではないが、楽しい:
x86_64.S
.text
.global _start
_start:
asm_main_after_prologue:
/* write */
mov $1, %rax /* syscall number */
mov $1, %rdi /* stdout */
mov $msg, %rsi /* buffer */
mov $len, %rdx /* len */
syscall
/* exit */
mov $60, %rax /* syscall number */
mov $0, %rdi /* exit status */
syscall
msg:
.ascii "hello\n"
len = . - msg
aarch64
最小限の実行可能なユーザーランドの例を以下に示しました: https://reverseengineering.stackexchange.com/questions/16917/arm64-syscalls-table/18834#18834 TODO grepカーネルコードは、簡単なはずです。