Разница в ABI между x86_64 функциями Linux и syscalls

x86_64 SysV ABI вызов вызова функции определяет целочисленный аргумент # 4, который должен быть передан в регистре rcx. С другой стороны, с помощью командной строки ядра Linux ABI используется r10 для этой же цели. Все остальные аргументы передаются в одних и тех же регистрах для обеих функций и системных вызовов.

Это приводит к некоторым странным вещам. Проверьте, например, реализацию mmap в glibc для платформы x32 (для которой существует такое же несоответствие):

00432ce0 <__mmap>:
  432ce0:       49 89 ca                mov    %rcx,%r10
  432ce3:       b8 09 00 00 40          mov    $0x40000009,%eax
  432ce8:       0f 05                   syscall

Итак, все регистры уже установлены, за исключением того, что мы перемещаем rcx в r10.

Мне интересно, почему бы не определить, что syscall ABI будет таким же, как вызов функции ABI, учитывая, что они уже настолько похожи.

Ответ 1

syscall инструкция предназначена для обеспечения более быстрого метода ввода Ring-0 для выполнения системного вызова. Это должно быть улучшением по сравнению со старым методом, который должен был поднять программное прерывание (int 0x80 на Linux).

Часть причины, по которой команда быстрее, состоит в том, что она не меняет память или даже меняет rsp на то, чтобы указать на стек ядра. В отличие от программного прерывания, когда ЦПУ вынужден разрешить ОС возобновить работу без каких-либо сбоев, для этой команды CPU может предположить, что программное обеспечение знает, что что-то происходит здесь.

В частности, syscall хранит две части состояния пользовательского пространства в регистрах. RIP для возврата после вызова сохраняется в rcx, а флаги хранятся в R11 (потому что RFLAGS замаскирован ядром- поставляемое значение перед входом в ядро ​​). Это означает, что оба этих регистра сбиты инструкцией.

Поскольку они сбиты, syscall ABI использует другой регистр вместо rcx, следовательно, использование r10 для 4-го аргумента.

r10 является естественным выбором, поскольку в x86-64 SystemV ABI он не используется для передачи функций args, а функции don ' t необходимо сохранить значение вызывающего абонента r10. Таким образом, функция оболочки syscall может mov %rcx, %r10 без сохранения/восстановления. Это было бы невозможно с любым другим регистром, для 6-arg syscalls и для вызова функции SysV ABI.


BTW, 32-битный системный вызов ABI также доступен с помощью sysenter, который требует сотрудничества между пространством пользователя и ядром, чтобы разрешить возврат в пользовательское пространство после sysenter. (т.е. сохранить некоторое состояние в пользовательском пространстве перед запуском sysenter). Это более высокая производительность, чем int 0x80, но неудобно. Тем не менее glibc использует его (путем перехода на код пользовательского пространства на страницах vdso, которые ядро ​​отображает в адресное пространство каждого процесса).

AMD syscall - это еще один подход к той же идее, что и Intel sysenter: сделать вход/выход из ядра менее дорогостоящим, не сохраняя абсолютно все.

Ответ 2

AMD syscall сжимает регистр rcx, поэтому вместо него используется r10.