Хорошие ссылки для системных вызовов - PullRequest
6 голосов
/ 02 августа 2011

Мне нужна какая-то ссылка, но хорошая, возможно, с хорошими примерами.Мне это нужно, потому что я начинаю писать код на ассемблере, используя ассемблер NASM.У меня есть эта ссылка:

http://bluemaster.iu.hio.no/edu/dark/lin-asm/syscalls.html

, что довольно приятно и полезно, но у него много ограничений, потому что он не объясняет поля в других регистрах.Например, если я использую системный вызов write, я знаю, что должен поставить 1 в регистр EAX, и ECX, вероятно, является указателем на строку, но как насчет EBX и EDX?Мне бы тоже хотелось, чтобы это было объяснено, что EBX определяет ввод (0 для стандартного ввода, 1 для чего-то еще и т. Д.), А EDX - длина вводимой строки и т. Д. И т. Д.Я не мог найти такие материалы, поэтому пишу здесь.Заранее спасибо.

Ответы [ 2 ]

10 голосов
/ 14 августа 2011

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

0 голосов
/ 02 августа 2011

Если вы загрузите эту веб-страницу (как это предлагается во втором абзаце) и загрузите исходные коды ядра, вы можете щелкнуть ссылки в столбце «Источник» и перейти непосредственно к исходному файлу, который реализует системные вызовы.Вы можете прочитать их сигнатуры C, чтобы увидеть, для чего используется каждый параметр.

Если вы просто ищете краткий справочник, то каждый из этих системных вызовов имеет интерфейс библиотеки C с тем же именем, за исключением * 1003.*.Так, например, вы можете проверить man 2 lseek, чтобы получить информацию о параметрах для sys_lseek:

off_t lseek(int fd, off_t offset, int whence);

, где, как вы можете видеть, параметры соответствуютиз вашей таблицы HTML:

%ebx           %ecx    %edx
unsigned int   off_t   unsigned int
...