Создание системных вызовов в Linux без стека - PullRequest
3 голосов
/ 11 февраля 2011

В Linux i386 ABI системного вызова int $0x80 упрощает выполнение системных вызовов, не имея действительного стека в пользовательском пространстве. Интерфейс vdso / vsyscall, с другой стороны, требует доступа к стеку. Как обстоят дела в этом отношении с другими портами Linux, особенно с x86_64? У них есть способы сделать системные вызовы без стека? Есть ли ссылка на доступные методы системных вызовов для каждой арки?

1 Ответ

4 голосов
/ 11 февраля 2011

В общем: без понятия.Даже на i386, если есть 6-й аргумент, он должен быть передан в стек (например, для 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 документирован в приложении.(И если вы ищете информацию о ABI x86_64 в другом месте, учтите, что 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
$ 
...