在Linux i386上, int $0x80
系统调用ABI可以很容易地执行系统调用而不需要有效的用户空间栈。 另一方面,vdso / vsyscall接口需要访问堆栈。 其他Linux端口如何在这方面,特别是x86_64? 他们有办法让系统调用没有堆栈? 是否有可用的系统调用每个拱门的方法?
一般来说:不知道。 即使在i386上,如果有第六个参数,它也必须传递到堆栈上(例如mmap
)。
特别是对于x86_64:将系统调用号码放入%rax
(注意:系统调用号码的分配完全不同于32位系统号码),最多6个参数( %rdi
, %rsi
, %rdx
, %r10
, %r8
和%r9
(与通常的ABI参数传递寄存器差不多,但不完全相同 – 注意使用%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
的用户堆栈框架上有任何要求才能正常工作。 在被信号中断的情况下,处理者显然需要一个理智的堆栈; 但是下面的实验使用了一个交替的信号堆栈,故意在syscall
周围渲染%rsp
,对我来说工作正常:
$ 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 $