Linux i386 では、int $0x80
syscall ABI により、有効なユーザー空間スタックがなくても簡単に syscall を実行できます。一方、vdso/vsyscall インターフェイスは、スタックへのアクセスを必要とします。他の Linux ポート、特に x86_64 はこの点でどうなっていますか? スタックなしでシステムコールを行う方法はありますか? 各アーチで利用可能な syscall メソッドに関するリファレンスはありますか?
1 に答える
一般的に:わかりません。i386 でも、6 番目の引数がある場合は、スタックに渡す必要があります (例: for mmap
)。
特に x86_64 の場合: syscall 番号を入れます(注意: syscall 番号は 32 ビットのものとはまったく異なる方法で%rax
割り当てられます) 。レジスタでパラメータを渡すための通常の ABI - ) の代わりに を使用することに注意し、命令を使用します。結果は に返され、およびが上書きされます。%rdi
%rsi
%rdx
%r10
%r8
%r9
%r10
%rcx
syscall
%rax
%rcx
%r11
x86_64 ABI 情報はhttp://www.x86-64.org/documentation/abi.pdfにあります。Linux ABI は付録に記載されています。(x86_64 ABI 情報を他の場所で探している場合は、64 ビット Windows が独自の異なる ABI を使用していることに注意してください。)
syscall
ユーザースタックフレームが適切に機能するための要件はないと思います。シグナルによって中断された場合、ハンドラーには明らかに健全なスタックが必要です。%rsp
しかし、別のシグナル スタックを使用し、故意に を破棄する次の実験は、syscall
私にとってはうまく機能します。
$ cat syscall_sig.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <time.h>
#include <unistd.h>
#define __NR_nanosleep 35
static sig_atomic_t alrm = 0;
void handler(int sig)
{
if (sig == SIGALRM)
alrm = 1;
}
int main(void)
{
stack_t ss;
struct sigaction sa;
struct timespec req, rem;
long ret;
ss.ss_flags = 0;
ss.ss_size = SIGSTKSZ;
ss.ss_sp = malloc(ss.ss_size);
sigaltstack(&ss, NULL);
memset(&sa, 0, sizeof(sa));
sa.sa_handler = handler;
sa.sa_flags = SA_ONSTACK;
sigaction(SIGALRM, &sa, NULL);
alarm(1);
req.tv_sec = 5;
req.tv_nsec = 0;
asm("xorq $0x12345678, %%rsp ; syscall ; xorq $0x12345678, %%rsp"
: "=a" (ret)
: "0" (__NR_nanosleep), "D" (&req), "S" (&rem)
: "rcx", "r11", "memory");
printf("syscall return code %ld, alarm flag %d\n", ret, alrm);
return 0;
}
$ gcc -Wall -o syscall_sig syscall_sig.c
$ ./syscall_sig
syscall return code -4, alarm flag 1
$