X86_64 Сборка Linux System Call Confusion

В настоящее время я изучаю язык ассемблера в Linux. Я использую книгу "Программирование с нуля" , а все примеры - 32-битные. Моя ОС 64-разрядная, и я пытаюсь сделать все примеры в 64-разрядной версии. У меня возникают проблемы:

.section .data

.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rbx
int $0x80

Это просто просто вызывает системный вызов выхода Linux или он должен. Вместо этого он вызывает SEG FAULT, и когда я вместо этого делаю это

.section .data

.section .text
.global _start
_start:
movq $1, %rax
movq $2, %rbx
int $0x80

он работает. Ясно, что проблема заключается в том, что я перехожу к% rax. Значение $1, которое я использую во втором примере, - это то, что "Программирование с нуля" говорит, что использование нескольких источников в Интернете говорит о том, что 64-битный системный номер вызова составляет 60 долларов. Ссылка Что я делаю не так? И какие еще вопросы я должен отслеживать и что я должен использовать для ссылки? На всякий случай вам нужно знать, что я нахожусь в главе 5 "Программирование с нуля" .

Ответ 1

Вы столкнулись с одним удивительным различием между i386 и x86_64: они не используют один и тот же механизм системных вызовов. Правильный код:

movq $60, %rax
movq $2,  %rdi   ; not %rbx!
syscall

Прерывание 0x80 всегда вызывает 32-разрядные системные вызовы. Он позволял 32-разрядным приложениям работать в 64-разрядных системах.

В целях обучения вам, вероятно, следует попытаться точно следовать учебнику, а не переводить "на лету" в 64-битный - есть несколько других существенных поведенческих различий, с которыми вы, вероятно, столкнетесь. Когда вы знакомы с i386, вы можете выбрать x86_64 отдельно.

Ответ 2

пожалуйста, прочтите Каковы соглашения о вызовах для системных вызовов UNIX и Linux на x86-64

и обратите внимание, что использование int 0x80 для syscall в системах x64 - это старый уровень совместимости. вы должны использовать инструкцию syscall для систем x64.

вы все равно можете использовать этот старый метод, но вам нужно скомпилировать свои двоичные файлы в режиме x86, подробнее см. в руководстве для компилятора/ассемблера.

Ответ 3

В i386 и x86_64 многое изменилось, включая как инструкцию, используемую для перехода в ядро, так и регистры, используемые для переноса аргументов системных вызовов. Вот код, эквивалентный вашему:

.section .data

.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rdi
syscall

Цитата из этого ответа к соответствующему вопросу:

Номера системных вызовов находятся в исходном коде Linux в файле arch/x86/include/asm/unistd_64.h. Номер syscall передается в регистре rax. Параметры находятся в rdi, rsi, rdx, r10, r8, r9. Вызов вызывается командой "syscall". Syscall перезаписывает регистр rcx. Возврат в rax.

Ответ 4

duskwuff ответ правильно указывает механизм для системы звонки разные для 64-разрядного x86 Linux или 32-разрядного Linux.

Однако этот ответ неполный и вводящий в заблуждение по нескольким причинам:

Поскольку указано в комментариях, SYSENTER фактически не работает на многих 64-битных Linux-системах, а именно 64-битных AMD.

Это, по общему признанию, запутанная ситуация. подробности gory здесь, но к чему это идет:

Для 32-битного ядра SYSENTER/SYSEXIT являются единственной совместимой парой [между процессорами AMD и Intel]

Для 64-битного ядра в длинном режиме... SYSCALL/SYSRET - единственная совместимая пара [между процессорами AMD и Intel]

Похоже, что на процессоре Intel в 64-разрядном режиме вы можете избежать использования SYSENTER, потому что он делает то же самое, что и SYSCALL, однако это не относится к системам AMD.

Нижняя строка: всегда использовать SYSCALL для Linux на 64-разрядных системах x86. Это то, что на самом деле указывает x86-64 ABI. (Подробнее см. В этом замечательном вики-ответ.

Ответ 5

Если вы проверите /usr/include/asm/unistd_32.h, выход соответствует 1, но в Выход /usr/include/asm/unistd_64.h соответствует 60.