2016-02-25 3 views
5

Я хочу написать общую функцию, которая выполняет системный вызов. Что-то вродеКаков тип аргументов системного вызова в 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); 

Таким образом, мне не нужно знать, зарегистрировать размер и тип целого числа, что является достаточно большим, чтобы вместить значение регистра.

+0

Как бы вы обрабатывали системные вызовы, которые не имеют параметров? – edmz

+0

@black Я бы определил 6 функций my_syscall {0-5} – alkedr

+1

Это на самом деле сложнее, чем можно ответить здесь кратко (если вы не указали архитектуру), но не стесняйтесь использовать мои общедоступные реализации макросов syscall() и syscall0 () -syscall6() на https://github.com/technosaurus/BQC ... все из которых отбрасываются для параметров – technosaurus

ответ

1

Нет общего решения. Если вы хотите сделать свой код ультра-мультиархитектурным вы можете просто сделать что-то подобное:

#if ARCH_WITH_32BIT_REGS 
typedef uint32_t reg_size_int_t; 
#elif ARCH_WITH_64BIT_REGS 
typedef uint64_t reg_size_int_t; 
#elif ARCH_WITH_16BIT_REGS 
typedef uint16_t reg_size_int_t; 
.... 
#endif 

reg_size_int_t syscall_1(reg_size_t nr, reg_size_t arg0); 
... 

Но для наиболее распространенных используемых архитектур размера регистра равно долго.

3

Я предлагаю вам использовать существующий системный вызов syscall, а не пытаться написать его по своему усмотрению. Кажется, он делает именно то, что вы хотите. Посмотрите раздел «Требования к архитектуре» на странице руководства для обсуждения действительных вопросов, которые вы подняли.

+2

Как ни странно, 'syscall()' не является syscall, но обертка библиотеки вокруг соответствующей команды syscall. – EOF

+1

Да, для этого также требуется стандартная библиотека c с расширениями gnu, и она сохраняет код ошибки в глобальной переменной errno вместо того, чтобы возвращать ее. Я признаю, что это не серьезные проблемы, но я блуждал, если есть простой способ не использовать все это. – alkedr

+1

Вы также можете изменить исходный код 'syscall' в соответствии с вашими потребностями. –

4

Аргументы системного вызова передаются в регистры. Таким образом, размер ограничивается размером регистра процессора.То есть 32-разрядная версия в 32-битной архитектуре, 64-разрядная версия в 64-битной архитектуре. Номера плавающей точки не могут быть переданы ядру таким образом. Традиционно ядро ​​не использует инструкции с плавающей запятой (и может не иметь возможности, поскольку состояние FPU обычно не сохраняется при вводе в ядро), поэтому старайтесь избегать чисел с плавающей запятой в ваших собственных системных вызовах.

Системные вызовы, использующие аргументы меньших типов нуль или знак, расширяют их. Системные вызовы, использующие более крупные типы аргументов, могут разделить аргумент на несколько регистров.

Системные вызовы (например, mmap()), которые имеют множество параметров, могут быть реализованы путем передачи параметров в качестве указателя на структуру, но это имеет измеримые служебные накладные расходы, поэтому избегайте проектирования системных вызовов с более чем пятью параметрами.

В конце дня используйте типы, соответствующие значению, которое вы хотите отправить. Пусть libc имеет дело с помещением данных в нужные места.

Смежные вопросы