Дальнейшее чтение по любой из тем здесь: Полное руководство по системным вызовам 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 для Linux на i386 int 0x80
: Hello, world на ассемблере с системными вызовами Linux?
Существует более быстрый способ совершать 32-битные системные вызовы: использование sysenter
. Ядро отображает страницу памяти в каждый процесс (vDSO) со стороной sysenter
танца в пользовательской области, которая должна взаимодействовать с ядром, чтобы он мог найти адрес возврата. Arg для регистрации сопоставления такой же, как и для int $0x80
. Обычно вы должны звонить в vDSO, а не sysenter
напрямую. (См . Полное руководство по системным вызовам Linux для получения информации о подключении и вызове в vDSO, а также для получения дополнительной информации sysenter
и всего остального, что связано с системными вызовами.)
x86-32 [Free | Open | Net | DragonFly] BSD UNIX Системный вызов UNIX:
Параметры передаются в стек. Перенесите параметры (последний параметр вставлен первым) в стек. Затем добавьте дополнительные 32-битные фиктивные данные (на самом деле это не фиктивные данные. Обратитесь к следующей ссылке для получения дополнительной информации), а затем дайте инструкцию системного вызова.int $0x80
http://www.int80h.org/bsdasm/#default-calling-convention
Соглашение о системных вызовах Linux x86-64:
x86-64 Mac OS X похожа, но отличается . ТОДО: проверь, что делает * 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
перед a call
, как всегда требовалось для x86-64 System V ABI. Вызывающим лицам разрешается предполагать, что они используют 16-байтовые загрузки / хранилища SSE, которые выдают ошибку при невыравнивании. Но исторически, Linux требовал только 4-байтового выравнивания стека, поэтому потребовалась дополнительная работа, чтобы зарезервировать естественно выровненное пространство даже для 8-байтового double
или чего-то еще.
Некоторые другие современные 32-разрядные системы все еще не требуют выравнивания стека более 4 байтов.
x86-64 System V user-space Соглашение о вызове функций:
x86-64 System V передает аргументы в регистрах, что более эффективно, чем в i386 в соглашении о стеке в System v. Это позволяет избежать задержек и дополнительных инструкций по сохранению аргументов в памяти (кеш), а затем снова загружать их в вызываемый объект. Это хорошо работает, потому что доступно больше регистров, и лучше для современных высокопроизводительных ЦП, где важны задержки и неупорядоченное выполнение. (I386 ABI очень старый).
В этом новом механизме: сначала параметры делятся на классы. Класс каждого параметра определяет способ его передачи в вызываемую функцию.
Для получения полной информации см. «3.2 Последовательность вызова функций» Приложения к архитектуре AMD64 для двоичного интерфейса приложения System V, которое гласит:
После классификации аргументов регистры назначаются (в порядке слева направо) для передачи следующим образом:
- Если класс MEMORY, передайте аргумент в стек.
- Если класс INTEGER, используется следующий доступный регистр последовательности% rdi,% rsi,% rdx,% rcx,% r8 и% r9
Так же %rdi, %rsi, %rdx, %rcx, %r8 and %r9
как и регистры , используемые для передачи параметров целочисленного значения / указателя (т. Е. Класса INTEGER) в любую функцию libc из сборки. % rdi используется для первого параметра INTEGER. % rsi для 2-го,% rdx для 3-го и так далее. Тогда call
инструкция должна быть дана. Стек ( %rsp
) должен быть выровнен по 16B при выполнении call
.
Если имеется более 6 параметров INTEGER, 7-й параметр INTEGER и более поздние передаются в стек. (Звонящий звонит, так же, как x86-32.)
Первые 8 аргументов с плавающей запятой передаются в% xmm0-7, а затем в стеке. Не существует сохраняемых при вызове векторных регистров. (Функция с сочетанием FP и целочисленных аргументов может иметь более 8 аргументов в регистре.)
Функции Variadic ( напримерprintf
) всегда должны %al
= число аргументов регистра FP.
Существуют правила, когда упаковывать структуры в регистры ( rdx:rax
при возврате) и в память. Посмотрите ABI для деталей и проверьте выходные данные компилятора, чтобы убедиться, что ваш код согласен с компиляторами о том, как что-то должно быть передано / возвращено.
Обратите внимание, что соглашение о вызове функций в Windows x64 имеет несколько существенных отличий от x86-64 System V, таких как теневое пространство, которое должно быть зарезервировано вызывающей стороной (вместо красной зоны), и сохраняемый вызов xmm6-xmm15. И совсем другие правила, по которым arg идет в каком регистре.