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
.