SYSCALL(2)

ИМЯ
syscall - непрямой системный вызов

ОБЗОР
#define _GNU_SOURCE         /* см. feature_test_macros(7) */
#include <unistd.h>
#include <sys/syscall.h>   /* для определений SYS_xxx */
long syscall(long number, ...);

ОПИСАНИЕ
syscall() — это маленькая библиотечная функция, которая делает системный вызов, чей интерфейс ассемблерного языка указывается в number, с дополнительными аргументами. Выполнение syscall() нужно, например, для запуска системного вызова, у которого нет обёрточной функции в библиотеке C. При вызове syscall() сохраняет регистры ЦП до выполнения системного вызова, восстанавливает регистры при возврате из системного вызова и если возникла ошибка, то сохраняет любой код, полученный от системного вызова, в errno(3). Символьные константы для системных вызовов можно найти в заголовочном файле <sys/syscall.h>.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
Возвращаемое значение определяется вызываемым системным вызовом. При успешном выполнении обычно возвращается 0. При ошибке возвращается -1, при этом код ошибки сохраняется в errno.

ЗАМЕЧАНИЯ
Вызов syscall() впервые появился в 4BSD.

  Требования, зависящие от архитектуры
Каждый ABI архитектуры имеет свои собственные требования по передаче аргументов системного вызова в ядро. Для системных вызовов, имеющих обёртку в glibc (большинство системных вызовов), копирование аргументов в правильные регистры с учётом архитектуры выполняется в самой glibc. Однако при выполнении системного вызова через syscall(), вызывающий сам должен учитывать особенности архитектуры; чаще всего это относится к 32-битным архитектурам. Например, на архитектуре ARM Embedded ABI (EABI) 64-битное значение (long long) должно быть выровнено по чётной паре регистров. То есть, при использовании syscall() вместо обёрточной функции glibc системный вызов readahead() на ARM вызывался бы с учётом EABI следующим образом:
syscall(SYS_readahead, fd, 0,
        (unsigned int) (offset >> 32),
        (unsigned int) (offset & 0xFFFFFFFF),
        count);

Так как смещение аргумента 64 бита, и первый аргумент (fd) передаётся в регистре r0, вызывающий должен разделить и выровнять 64-битное значение так, чтобы оно передавалось в паре регистров r2/r3. Это выполняется вставкой пустого значения в r1 (второго аргумент 0). Подобные сложности можно видеть на MIPS с O32 ABI, на PowerPC с 32-битным ABI и на Xtensa. Это относится к системным вызовам fadvise64_64(2), ftruncate64(2), posix_fadvise(2), pread64(2), pwrite64(2), readahead(2), sync_file_range(2) и truncate64(2).

  Архитектурные соглашения по вызовам
В каждой архитектуре есть собственный способ передачи аргументов вызову ядра. Особенности различных архитектур перечислены в двух таблицах ниже. Поля первой таблицы: инструкция для перехода в режим ядра (может быть не быстрым или лучшим способом перехода в ядро, лучше использовать vdso(7)), регистр для указания номера системного вызова и регистр возврата результата работы системного вызова.
Арх/ABI | Инструкция | № syscall | Возвр | Замечания
arm/OABI | swi NR | - | a1 | NR — № syscall
arm/EABI | swi 0x0 | r7 | r0
arm64 | svc #0 | x8 | x0
blackfin | excpt 0x0 | P0 | R0
i386 | int $0x80 | eax | eax
ia64 | break 0x100000 | r15 | r8 | См. ниже
mips | syscall | v0 | v0 | См. ниже
parisc | ble 0x100(%sr2, %r0) | r20 | r28
s390 | svc 0 | r1 | r2 | Смотрите далее
s390x | svc 0 | r1 | r2 | Смотрите далее
sparc/32 | t 0x10 | g1 | o0
sparc/64 | t 0x6d | g1 | o0
x86_64 | syscall | rax | rax

Для s390 и s390x, NR (номер системного вызова) может передаваться напрямую с помощью «svc NR», если он меньше 256. На некоторых архитектурах для показа отказа системного вызова используется регистр с простым логическим значением: в ia64 для этого используется r10, а в mips — a3.

Во второй таблице показаны регистры, которые используются для передачи аргументов в системный вызов.
arch/ABI | arg1 | arg2 | arg3 | arg4 | arg5 | arg6 | arg7 | Примечания
arm/OABI | a1 | a2 | a3 | a4 | v1 | v2 | v3
arm/EABI | r0 | r1 | r2 | r3 | r4 | r5 | r6
arm64 | x0 | x1 | x2 | x3 | x4 | x5 | -
blackfin | R0 | R1 | R2 | R3 | R4 | R5 | -
i386 | ebx | ecx | edx | esi | edi | ebp | -
ia64 | out0 | out1 | out2 | out3 | out4 | out5 | -
mips/o32 | a0 | a1 | a2 | a3 | - | - | - | см. ниже
mips/n32,64 | a0 | a1 | a2 | a3 | a4 | a5 | -
parisc | r26 | r25 | r24 | r23 | r22 | r21 | -
s390 | r2 | r3 | r4 | r5 | r6 | r7 | -
s390x | r2 | r3 | r4 | r5 | r6 | r7 | -
sparc/32 | o0 | o1 | o2 | o3 | o4 | o5 | -
sparc/64 | o0 | o1 | o2 | o3 | o4 | o5 | -
x86_64 | rdi | rsi | rdx | r10 | r8 | r9 | -

По соглашению в mips/o32 аргументы системных вызовов с 5 по 8 передаются через пользовательский стек.

Заметим, что эти таблицы не описывают полное соглашение о вызове — некоторые архитектуры могут затирать другие регистры и это здесь не описано.

ПРИМЕР
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <signal.h>
int
main(int argc, char *argv[])
{
    pid_t tid;
    tid = syscall(SYS_gettid);
    tid = syscall(SYS_tgkill, getpid(), tid, SIGHUP);
}

СМОТРИТЕ ТАКЖЕ
_syscall(2), intro(2), syscalls(2), errno(3), vdso(7)