Вы правильно вызываете scanf
, используя соглашение о вызовах x86-64 System V. Возвращаемое значение остается в eax
. После успешного преобразования одного операнда (%d
) он возвращается с eax
= 1.
... correct setup for scanf, including zeroing AL.
call scanf ; correct
int 80h ; insane: system call with eax = scanf return value
Затем вы запускаете int 80h
, который выполняет 32-разрядный системный вызов устаревшего ABI с использованием eax=1
в качестве кода для определения какой системного вызова. (см. Что произойдет, если вы используете 32-битный int 0x80 Linux ABI в 64-битном коде?).
eax=1
/ int 80h
- это sys_exit
в Linux. (unistd_32.h
имеет __NR_exit
= 1). Используйте отладчик; это показало бы вам, какая инструкция вызвала выход из вашей программы.
В вашем заголовке (до того, как я его исправил) говорилось, что у вас ошибка сегментации, но я тестировал на своем рабочем столе x86-64, и это не так. Он завершается чисто с помощью системного вызова int 80h
exit. (Но в коде, который выполняет segfault, используйте отладчик, чтобы узнать, какая инструкция.) strace
неправильно декодирует int 0x80
системные вызовы в 64-битных процессах < / a>, используя 64-битные syscall
номера вызовов из unistd_64.h
, а не 32-битные unistd_32.h
номера вызовов.
Ваш код был близок к работе: вы правильно используете int 0x80
32-битный ABI для sys_write
и передаете ему только 32-битные аргументы. (Аргументы указателя умещаются в 32 бита, потому что статический код / данные всегда помещаются в 2 ГБ виртуального адресного пространства в модели кода по умолчанию на x86-64. Именно по этой причине вы можете использовать компактные инструкции, такие как mov edi, formatin
, для размещения адресов в регистрах, или использовать их как непосредственные или подписанные смещения rel32.)
OTOH Я думаю, вы сделали это не по той причине. И, как указывает @prl, вы забыли поддерживать выравнивание стека по 16 байт.
Кроме того, смешивание системных вызовов с функциями C stdio обычно является плохой идеей. Stdio использует внутренние буферы вместо того, чтобы всегда выполнять системный вызов при каждом вызове функции, поэтому что-то может отображаться не по порядку, или read
может ждать ввода пользователя, когда в буфере stdio уже есть данные для stdin
.
Ваша петля также разорвана несколькими способами. Кажется, вы пытаетесь вызвать printf
с 32-битным соглашением о вызовах (аргументы в стеке).
Даже в 32-битном коде это не работает, потому что значение возврата printf
находится в eax
. Итак, ваш цикл бесконечен, потому что printf
возвращает количество напечатанных символов. Это как минимум два из строки формата %d\n
, поэтому dec rax
/ jnz
всегда будет прыгать.
В x86-64 SysV ABI вам нужно обнулить al
перед вызовом printf
(с xor eax,eax
), если вы не передали никаких аргументов FP в регистры XMM. Вы также должны передавать аргументы в rdi
, rsi
, ..., как для scanf.
Вы также add rsp, 8
после вставки двух 8-байтовых значений, поэтому стек постоянно увеличивается. (Но вы никогда не вернетесь, поэтому возможный segfault будет при переполнении стека, а не при попытке возврата, если rsp
не указывает на адрес возврата.)
Решите, создаете ли вы 32-битный или 64-битный код, и копируйте / вставляйте только примеры для режима и ОС, на которые вы нацеливаетесь. (Обратите внимание, что 64-битный код может и часто использует в основном 32-битные регистры.)
См. Также Сборка 32-разрядной версии двоичные файлы в 64-разрядной системе (набор инструментов GNU) (который включает раздел NASM с удобным asm-link
скриптом, который собирает и связывает в статический двоичный файл). Но поскольку вы пишете main
вместо _start
и используете функции libc, вам следует просто связать с gcc -m32
(если вы решите использовать 32-битный код вместо замены 32-битных частей вашей программы 64-битным вызовом функций и соглашения о системных вызовах).
См. Каковы соглашения о вызовах для системных вызовов UNIX и Linux на i386 и x86-64.
person
Peter Cordes
schedule
20.11.2017
push
аргументы заprintf
в стек? Какой источник информации вы использовали для этого? (У меня есть подозрение, что вы меняете код / учебник 32b на 64b, но это не будет работать так просто, это сложнее ... на данный момент, если у вас есть хороший ресурс 32b asm для обучения, было бы намного проще научить вы, как собрать 32-битный двоичный файл под 64-битным Linux и вместо этого работать с ним). ... либо так, либо ваш ресурс 64b некачественный, попробуйте какой-нибудь получше ... - person Ped7g   schedule 20.11.2017printf
- один из наиболее сложных для правильного понимания, поскольку он имеет переменное количество аргументов, поэтому вам необходимо знать правильное соглашение о вызовах, помимо основ. Вы также можете проверить это руководство (ориентировано именно на nasm + libc + 32b и хорошо прокомментировано): csee.umbc.edu/portal/help/nasm/sample.shtml И если вы только начинаете со сборки, я бы полностью пропустил вызов функций libc и поигрался с чистым x86 инструкции (выполняя некоторую математику), проверяя значения только в отладчике. - person Ped7g   schedule 20.11.2017printf
: stackoverflow.com/questions/47362660/ ... не стесняйтесь спрашивать там, если что-то не понятно или работает для вас. (насчет длинных командных строк ... выглядит, вероятно, утомительно, но я использую текстовый редактор Kate с настройкой методов сборки, поэтому я не возражаю против этих длинных имен ... опять же, вы также можете сохранить эти команды в сценарии оболочки или даже сделать файл). К сожалению, я не удосужился добавить вариант 64b. - person Ped7g   schedule 20.11.2017printf
из сборки ... извините: D ... все же есть В Интернете много руководств, и я слишком устал, чтобы писать здесь полный ответ. - person Ped7g   schedule 20.11.2017int 0x80
в 64-битном режиме (stackoverflow.com / questions / 46087730 /). На самом деле проблема не в том, следует ли использовать системные вызовы или функции библиотеки stdio. И что еще хуже, использованиеcall scanf
/int 80h
, поэтому номер системного вызова определяется возвращаемым значением scanf !!!! - person Peter Cordes   schedule 20.11.2017