Стандартным языком программирования в Linux является C. Из-за этого лучшие описания системных вызовов будут показывать их как функции C для вызова.Учитывая их описание в качестве функции C и знание того, как сопоставить их с фактическим системным вызовом в сборке, вы сможете легко использовать любой системный вызов.
Во-первых, вам нужна ссылка для всехсистемные вызовы такими, какими они кажутся программисту Си.Лучший из известных мне - проект man-страниц Linux , в частности раздел системных вызовов .
Давайте рассмотрим write
системный вызов в качестве примера, так как он в вашем вопросе.Как видите, первый параметр - это целое число со знаком, обычно это дескриптор файла, возвращаемый системным вызовом open
.Эти файловые дескрипторы также могли быть унаследованы от вашего родительского процесса, как это обычно бывает для первых трех файловых дескрипторов (0 = stdin, 1 = stdout, 2 = stderr).Второй параметр - это указатель на буфер, а третий параметр - размер буфера (как целое число без знака).Наконец, функция возвращает целое число со знаком, которое является числом записанных байтов, или отрицательное число для ошибки.
Теперь, как сопоставить это с действительным системным вызовом?Есть много способов сделать системный вызов на 32-битном x86 (который, вероятно, вы используете, основываясь на ваших именах регистров);будьте осторожны, чтобы на 64-битном x86 он был совершенно другим (убедитесь, что вы собираете в 32-битном режиме и связываете 32-битный исполняемый файл; см. этот вопрос для примера того, как все может пойти не так, как надо).Самый старый, самый простой и самый медленный из них в 32-битном x86 - это метод int $0x80
.
Для метода int $0x80
номер системного вызова вводится в %eax
, а параметры в %ebx
, %ecx
, %edx
, %esi
, %edi
и %ebp
в указанном порядке.Затем вы звоните int $0x80
, и возвращаемое значение из системного вызова - %eax
.Обратите внимание, что это возвращаемое значение отличается от того, что указано в ссылке;ссылка показывает, как библиотека C будет возвращать ее, но системный вызов возвращает -errno
при ошибке (например, -EINVAL
).Библиотека C переместит это в errno
и вернет -1
в этом случае.Подробности смотрите в syscalls (2) и intro (2) .
Так, в примере write
вы бы поместили системный вызов write
число в %eax
, первый параметр (номер дескриптора файла) в %ebx
, второй параметр (указатель на строку) в %ecx
и третий параметр (длина строки) в %edx
.Системный вызов вернет в %eax
либо количество записанных байтов, либо номер ошибки с отрицанием (если возвращаемое значение находится в диапазоне от -1 до -4095, это отрицательный номер ошибки).
Наконец,как вы находите номера системных звонков?Их можно найти на /usr/include/linux/unistd.h
.В моей системе это включает в себя /usr/include/asm/unistd.h
, что в конечном итоге включает /usr/include/asm/unistd_32.h
, так что цифры есть (для write
, вы можете увидеть, __NR_write
это 4
).То же самое относится и к номерам ошибок, которые приходят из /usr/include/linux/errno.h
(в моей системе после погони за цепочкой включения я нахожу первые в /usr/include/asm-generic/errno-base.h
, а остальные в /usr/include/asm-generic/errno.h
).Для системных вызовов, которые используют другие константы или структуры, их документация говорит, какие заголовки вы должны посмотреть, чтобы найти соответствующие определения.
Теперь, как я уже сказал, int $0x80
- самый старый и самый медленный метод,Более новые процессоры имеют специальные инструкции системного вызова, которые работают быстрее.Чтобы использовать их, ядро делает доступным виртуальный динамический разделяемый объект (vDSO
; он похож на разделяемую библиотеку, но только в памяти) с функцией, которую можно вызывать для выполнения системного вызова, используя лучший метод, доступный для вашего оборудования.,Он также делает доступными специальные функции для получения текущего времени даже без необходимости делать системный вызов и некоторые другие вещи.Конечно, это немного сложнее использовать, если вы не используете динамический компоновщик.
Существует также другой более старый метод, vsyscall
, который похож на vDSO
, но использует одну страницу с фиксированным адресом. Этот метод устарел, приведет к появлению предупреждений в системном журнале, если вы используете последние версии ядер, может быть отключен при загрузке на более поздних версиях и может быть удален в будущем. Не используйте его.