Дополнительная информация по любой из тем здесь: Полное руководство по системным вызовам Linux
Я проверил это с помощью GNU Assembler (gas) в Linux.
Интерфейс ядра
x86-32 aka i386 Соглашение о системных вызовах Linux:
В x86-32 параметры для системного вызова Linux передаются с использованием регистров. %eax
для syscall_number. % ebx,% ecx,% edx,% esi,% edi,% ebp используются для передачи 6 параметров системным вызовам.
Возвращаемое значение в %eax
. Все остальные регистры (включая EFLAGS) сохраняются в int $0x80
.
Я взял следующий фрагмент из Учебника по сборке Linux , но я сомневаюсь в этом. Если кто-то может показать пример, было бы здорово.
Если существует более шести аргументов,
%ebx
должен содержать память
место, где список аргументов
хранится - но не беспокойтесь об этом
потому что вряд ли вы будете использовать
системный вызов с более чем шестью
аргументы.
Пример и немного больше чтения см. В http://www.int80h.org/bsdasm/#alternate-calling-convention. Другой пример Hello World для i386 Linux с использованием int 0x80
: Какие части этого кода сборки HelloWorld необходимы, если бы я написать программу в сборке?
Существует более быстрый способ совершать 32-битные системные вызовы: использование sysenter
. Ядро отображает страницу памяти в каждый процесс (vDSO) со стороной пользовательского пространства танца sysenter
, который должен взаимодействовать с ядром, чтобы он мог найти адрес возврата. Аргумент для регистрации сопоставления такой же, как для int $0x80
. Обычно вы должны звонить в vDSO, а не использовать sysenter
напрямую. (См. Подробное руководство по системным вызовам Linux для получения информации о подключении и вызове в vDSO, а также для получения дополнительной информации о sysenter
и обо всем остальном, что касается системных вызовов.)
x86-32 [Free | Open | Net | DragonFly] Соглашение о системных вызовах BSD UNIX:
Параметры передаются в стек. Перенесите параметры (последний параметр вставлен первым) в стек. Затем добавьте дополнительные 32-битные фиктивные данные (это не фиктивные данные. Для получения дополнительной информации обратитесь к следующей ссылке), а затем дайте инструкцию системного вызова int $0x80
http://www.int80h.org/bsdasm/#default-calling-convention
x86-64 Соглашение о системных вызовах Linux:
x86-64 Mac OS X похожа, но отличается . TODO: проверь, что делает * BSD.
См. Раздел: «A.2 AMD64 Linux Соглашения о ядре» из Двоичный интерфейс приложения System V Приложение AMD64 для архитектуры архитектуры . Последние версии psABI для i386 и x86-64 System V можно найти на этой странице в репозитории сопровождающего ABI . (См. Также вики-тег x86 для последних ссылок на ABI и много других полезных вещей о x86 asm.)
Вот фрагмент из этого раздела:
- Пользовательские приложения используют в качестве целочисленных регистров для передачи
последовательность% rdi,% rsi,% rdx,% rcx,
% r8 и% r9. Интерфейс ядра использует% rdi,% rsi,% rdx,% r10,% r8 и% r9.
- Системный вызов выполняется с помощью инструкции
syscall
. Это clobbers% rcx и% r11 , а также возвращаемое значение% rax, но другие регистры сохраняются.
- Номер системного вызова должен быть передан в регистр% rax.
- Системные вызовы ограничены шестью аргументами, аргумент не передается
прямо в стеке.
- Возвращаясь из системного вызова, регистр% rax содержит результат
системный вызов. Значение в диапазоне от -4095 до -1 указывает
ошибка, это
-errno
.
- В ядро передаются только значения класса INTEGER или класса MEMORY.
Помните, что это из специфичного для Linux приложения к ABI, и даже для Linux это информативно, а не нормативно. (Но на самом деле это точно.)
Этот 32-битный int $0x80
ABI может использоваться в 64-битном коде (но настоятельно не рекомендуется). Что произойдет, если вы используете 32-битный int 0x80 Linux ABI в 64-битном коде? Он все еще усекает свои входные данные до 32-битного, поэтому он не подходит для указателей и имеет нули r8-r11.
Пользовательский интерфейс: вызов функции
x86-32 Соглашение о вызове функции:
В x86-32 параметры были переданы в стек. Последний параметр сначала помещался в стек, пока все параметры не были выполнены, а затем была выполнена инструкция call
. Это используется для вызова функций библиотеки C (libc) в Linux из сборки.
Современные версии i386 System V ABI (используемые в Linux) требуют 16-байтового выравнивания %esp
перед call
, как всегда требовалось для x86-64 System V ABI. Вызывающим разрешено допускать, что это происходит, и использовать 16-байтовые загрузки / хранилища SSE, в которых произошел сбой при выравнивании. Но исторически в Linux требовалось только 4-байтовое выравнивание стека, поэтому потребовалась дополнительная работа, чтобы зарезервировать естественно выровненное пространство даже для 8-байтового double
или чего-то еще.
Некоторые другие современные 32-разрядные системы все еще не требуют выравнивания стека более 4 байтов.
x86-64 System V пользовательское пространство Соглашение о вызове функций:
x86-64 System V передает аргументы в регистрах, что более эффективно, чем в соглашении i386 System V. Это позволяет избежать задержек и дополнительных инструкций по сохранению аргументов в памяти (кеш), а затем снова загружать их в вызываемый объект. Это хорошо работает, потому что доступно больше регистров, и лучше для современных высокопроизводительных ЦП, где важны задержки и неупорядоченное выполнение. (I386 ABI очень старый).
В этом новом механизме: сначала параметры делятся на классы. Класс каждого параметра определяет способ его передачи в вызываемую функцию.
Для получения полной информации см. «3.2 Последовательность вызова функций» из Двоичный интерфейс приложения System V Приложение к архитектуре AMD64 , которое гласит:
После классификации аргументов регистры присваиваются (в
порядок слева направо) для прохождения следующим образом:
- Если класс MEMORY, передать аргумент в стек.
- Если класс INTEGER, следующий доступный регистр
используется последовательность% rdi,% rsi,% rdx,% rcx,% r8 и% r9
То есть %rdi, %rsi, %rdx, %rcx, %r8 and %r9
- это регистры в порядке , используемые для передачи параметров целочисленного значения / указателя (т. Е. Класса INTEGER) в любую функцию libc из сборки. % rdi используется для первого параметра INTEGER. % rsi для 2-го,% rdx для 3-го и так далее. Затем call
инструкция должна быть дана. При выполнении call
стек (%rsp
) должен быть выровнен по 16B.
Если имеется более 6 параметров INTEGER, 7-й параметр INTEGER и более поздние передаются в стек. (Звонящий звонит, как x86-32.)
Первые 8 аргументов с плавающей запятой передаются в% xmm0-7, позже в стеке. Не существует сохраняемых при вызове векторных регистров. (Функция с сочетанием FP и целочисленных аргументов может иметь более 8 аргументов в регистре.)
Вариативным функциям ( подобно printf
) всегда требуется %al
= количество аргументов регистра FP.
Существуют правила, когда упаковывать структуры в регистры (rdx:rax
при возврате) по сравнению с памятью. Подробности смотрите в ABI и проверьте выходные данные компилятора, чтобы убедиться, что ваш код согласен с компиляторами относительно того, как что-то должно быть передано / возвращено.
Обратите внимание, что соглашение о вызове функций Windows x64 имеет несколько существенных отличий от x86-64 System V, как и теневое пространство, которое должно быть зарезервировано вызывающей стороной (вместо красного зона) и сохраненный вызов xmm6-xmm15. И совсем другие правила, по которым arg идет в какой регистр.