2016-07-25 3 views
10

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

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

+0

В [другом ответе ABI] (http://stackoverflow.com/a/35619528/224132) я выкопал некоторые ссылки на сообщения рассылки amd64 от разработчиков AMD и разработчиков ядра Linux до того, как был выпущен первый силикон AMD64 , Там есть интересные материалы, такие как экспериментальные результаты (от компиляции SPECint и просмотр кода и количества инструкций), которые привели к выбору x86-64 SysV ABI, для которого регистр используется для чего. –

ответ

5

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

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

В частности, syscall хранит две части пользовательского пространства в регистрах. RIP для возврата после вызова сохраняется в rcx, а флаги хранятся в R11 (because RFLAGS is masked with a kernel-supplied value before entry to the kernel). Это означает, что оба этих регистра сгруппированы по инструкции.

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

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


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

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

+0

Это более тонкий, чем просто замена нескольких магазинов регистрами. Он не изменяет 'rsp', чтобы указывать на стек ядра, поэтому не было бы разумного места, чтобы нажать все, что он хотел сохранить. Код ядра в точке входа должен сделать это сам. (использование 'swapgs' позволяет' [gs: absolute_address] 'получать доступ к данным ядра каждой задачи). ЦП не также внутренне держит указатель стека ядра для использования для 'syscall', а только сохраненное значение' gs'. Я думаю, что именно здесь происходит снижение сложности реализации. (И это 'swapgs' - отдельная инструкция). –

+0

Часть о C/C++, не использующая 'r10', не имеет смысла. Ядру не разрешено предполагать, какой язык выполняет вызов. –

+0

Все в порядке. Я отредактировал его. –

4

AMD syscall clobbers rcx register, таким образом, используется r10.

+1

И 'r10' - это чистый регистр нуля: не используется для функции arg-pass, а не для вызова. Это позволяет другим функциям-оболочкам, таким как окупители динамического компоновщика, использовать его как временное и по-прежнему иметь возможность перехватывать вызов с помощью 'jmp' вместо' call'/'pop' /' ret'. Поэтому 'r10' является хорошим выбором для системных вызовов. 'syscall' /' sysret' также используют r11. –

+0

К сожалению, я думал о 'r11'. ABI говорит, что 'r10' используется для передачи« указателя статической цепочки ». C/C++ этого не используют, поэтому на практике «r10» также является чистым регистром нуля. –

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