x86_64 Сборка Linux Системный вызов Путаница
В настоящее время я изучаю ассемблер на Linux. Я использую книгу "Программирование с нуля", и все примеры 32-битные. Моя ОС 64-битная, и я пытаюсь сделать все примеры в 64-битной. У меня возникли проблемы, однако:
.section .data
.section .text
.global _start
_start:
movq $60, %rax
movq $2, %rbx
int $0x80
Это просто вызывает системный вызов выхода из Linux или должен. Вместо этого это вызывает ошибку SEG, и когда я вместо этого делаю это
.section .data
.section .text
.global _start
_start:
movq $1, %rax
movq $2, %rbx
int $0x80
оно работает. Очевидно, проблема заключается в значении, которое я перемещаю в%rax. Значение $1, которое я использую во втором примере, - это то, что "Программирование с нуля" сказал использовать, однако несколько источников в Интернете сказали, что номер 64-битного системного вызова составляет $60. Ссылка Что я делаю не так? Кроме того, какие другие вопросы я должен остерегаться и что я должен использовать для справки? На всякий случай, если вам нужно знать, я в главе 5 "Программирование с нуля".
5 ответов
Вы столкнулись с одной удивительной разницей между i386 и x86_64: они не используют один и тот же механизм системных вызовов. Правильный код:
movq $60, %rax
movq $2, %rdi ; not %rbx!
syscall
Прерывание 0x80
всегда вызывает 32-битные системные вызовы. Он используется для запуска 32-битных приложений в 64-битных системах.
В целях обучения вам, вероятно, следует стараться точно следовать учебному руководству, а не переводить на лету 64-разрядную версию - есть несколько других существенных поведенческих различий, с которыми вы, вероятно, столкнетесь. Когда вы ознакомитесь с i386, вы можете выбрать x86_64 отдельно.
Прочтите это. Каковы соглашения о вызовах для системных вызовов UNIX и Linux на x86-64?
и обратите внимание, что с помощью int 0x80
для системного вызова в системах x64 - старый уровень совместимости. ты должен использовать syscall
инструкция по x64 системам.
вы все еще можете использовать этот старый метод, но вам нужно скомпилировать ваши двоичные файлы в режиме x86, подробности смотрите в руководстве к вашему компилятору / ассемблеру.
duskwuff правильно указывает на то, что механизм для системных вызовов отличается для 64-битной x86 Linux и 32-битной Linux.
Однако этот ответ является неполным и вводящим в заблуждение по нескольким причинам:
- Это изменение было фактически введено до того, как 64-битные системы стали популярными, мотивируя это тем, что
int 0x80
был очень медленным на Pentium 4. Линус Торвальдс закодировал решение, используяSYSENTER
/SYSEXIT
инструкции (которые были введены Intel в эпоху Pentium Pro, но были ошибочными и не давали практической пользы). Поэтому современные 32-битные системы Linux на самом деле используютSYSENTER
неint 0x80
, - 64-битные ядра Linux x86 на самом деле не используют
SYSENTER
а такжеSYSEXIT
, Они на самом деле используют очень похожиеSYSCALL
/SYSRET
инструкции.
Как указано в комментариях, SYSENTER
на самом деле не работает на многих 64-битных системах Linux, а именно на 64-битных системах AMD.
Это заведомо запутанная ситуация. Ужасные детали здесь, но к чему это сводится это:
Для 32-битного ядра SYSENTER/SYSEXIT являются единственной совместимой парой [между процессорами AMD и Intel]
Только для 64-битного ядра в длинном режиме… SYSCALL/SYSRET - единственная совместимая пара [между процессорами AMD и Intel]
Похоже, что на процессоре Intel в 64-битном режиме вы можете избежать использования SYSENTER
потому что он делает то же самое, что и SYSCALL
Однако это не относится к системам AMD.
Итог: всегда используйте SYSCALL
в Linux на 64-битных системах x86. Это то, что на самом деле указывает ABI x86-64. (Смотрите этот отличный ответ вики для более подробной информации.)
Многое изменилось между 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. Номер системного вызова передается в регистр rax. Параметры указаны в rdi, rsi, rdx, r10, r8, r9. Вызов вызывается с помощью инструкции "syscall". Системный вызов перезаписывает регистр rcx. Возвращение в rax.
Если вы проверите /usr/include/asm/unistd_32.h
выход соответствует 1
но в/usr/include/asm/unistd_64.h
выход соответствует 60
,