在没有堆栈的情况下在Linux上进行系统调用

Linux i386上,使用int $0x80 syscall ABI可以很容易地在没有有效用户空间堆栈的情况下执行syscall.另一方面,vdso / vsyscall接口需要访问堆栈.在这方面,其他Linux端口(特别是x86_64)如何?他们有没有堆栈的方式进行系统调用的方法吗?是否有关于每个架构的可用syscall方法的参考?

解决方法:

一般来说:不知道.即使在i386上,如果有第6个参数,它也必须在堆栈上传递(例如mmap).

专门针对x86_64:将系统调用号放入%rax(请注意:系统调用号分配给32位的方式完全不同),在%rdi,%rsi,%rdx,%r10,%r8和%中最多包含6个参数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
$
上一篇:Kip Irvine-X86处理器的汇编语言-与Linux相关性如何?


下一篇:Visual Studio2015 Nuget安装OpencvSharp包