Я хочу написать общую функцию, которая выполняет системный вызов. Что-то вродеКаков тип аргументов системного вызова в Linux?
long my_syscall2(long number, long arg1, long arg2);
Я хочу, чтобы он был как можно более переносимым. Реализация, очевидно, различна для всех архитектур. Нужна ли другая подпись для функции? Могу ли я использовать long
, или я должен использовать что-то еще?
Вот возможные решения, которые я нашел:
- Ядро использует некоторые dark magic: (__SYSCALL_DEFINEx называет __SC_LONG получить тип, __SC_LONG содержит магию). Я где-то слышал, что типы в пользовательском пространстве не всегда такие же, как и в пространстве ядра, поэтому я не знаю, могу ли я его использовать.
- musl-libc uses long for all architectures that it supports except x32: (определено в [arch] /syscall_arch.h).
- Я мог найти документацию для всех процессорных архитектур и компиляторов, которые я хочу поддерживать, посмотреть размеры регистров и размеры целочисленных типов и выбрать любой целочисленный тип с тем же размером, что и регистры.
Так что я думаю, вопрос в том, «Есть ли какое-то правило, которое говорит„тип аргументов системных вызовов всегда long
с некоторыми исключениями, такими как x32“или мне нужно искать документацию для каждой архитектуры и компилятора?»
Редактировать: Я знаю, что некоторые системные вызовы принимают указатели и другие типы в качестве параметров. Я хочу написать общие функции, которые могут вызывать любой системный вызов, с типичными типами параметров. Эти общие типы параметров должны быть достаточно большими, чтобы удерживать любой из фактических типов параметров. Я знаю, это возможно, потому что syscall() функция существует.
Редактировать: Предлагается другое частичное решение этой проблемы.
Реализация этих функций в настоящее время выглядит следующим образом:
static __inline long my_syscall2(long number, long arg1, long arg2)
{
unsigned long ret;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(number), "D"(arg1), "S"(arg2)
: "rcx", "r11", "memory");
return ret;
}
Интересная часть "=a"(ret)
, это означает, что возвращение системного вызова значение, которое хранится в регистре a
должно быть сохранено в переменную ret
. Вместо того, чтобы писать функцию, которая создает локальную переменную, делает syscall, сохраняет возвращаемое значение в переменной и возвращает переменную, я могу написать макрос, который делает syscall, и сохраняет результат в переменной, предоставляемой вызывающим. Это будет выглядеть следующим образом:
#define my_syscall2(RET, NUMBER, ARG1, ARG2) \
__asm__ __volatile__ ("syscall" : "=a"(RET) : "a"(NUMBER), "D"(ARG1), "S"(ARG2) \
: "rcx", "r11", "memory");
И было бы использовать так:
long result;
void * arg1;
int arg2;
my_syscall2(result, <syscall number>, arg1, arg2);
Таким образом, мне не нужно знать, зарегистрировать размер и тип целого числа, что является достаточно большим, чтобы вместить значение регистра.
Как бы вы обрабатывали системные вызовы, которые не имеют параметров? – edmz
@black Я бы определил 6 функций my_syscall {0-5} – alkedr
Это на самом деле сложнее, чем можно ответить здесь кратко (если вы не указали архитектуру), но не стесняйтесь использовать мои общедоступные реализации макросов syscall() и syscall0 () -syscall6() на https://github.com/technosaurus/BQC ... все из которых отбрасываются для параметров – technosaurus