У вас есть sub esp,4
и push
перед вызовом, поэтому чтобы восстановить указатель стека, чтобы он указывал на обратный адрес, вам нужно add esp,8
до ret
вместо add esp, 4
(printf
- это функция varargs, поэтому она не выводит свои собственные аргументы из стека. Она использует соглашение о вызовах cdecl.)
Или лучше удалить sub esp,4
.
32-разрядная ОС Windows поддерживает только 4-байтовое выравнивание стека, поэтому вам не нужно ничего делать с ESP до push
/ call
, чтобы повторно выровнять указатель стека до call
. И вы не используете эти 4 байта, которые вы зарезервировали для чего-либо.
Обновление: MichaelPetch заметил, что ваша программа, вероятно, дает сбой внутри printf
, потому что вы вызывали ее без инициализации libc. Вероятно, вы создаете свою программу с этой функцией в качестве точки входа, не вызываемой из обычного кода запуска C. (и что отладчик Visual Studio ошибочно сообщает о сбое как о строке после call
, вместо этого где на самом деле произошла авария.)
Ваше сообщение об ошибке по-прежнему относится к первой версии вопроса, где вы пропустили ret
! В этом случае выполнение просто падает с конца main
на следующие байты, декодируя их как инструкции. Вероятно, нули.
00 00
декодируется как add [eax], al
, а eax
содержит 14 из возвращаемого значения printf. (printf
возвращает количество символов printf, а длина строки вашего формата - 14 байтов).
Но сообщение об ошибке касается записи адреса 0x14
, который является десятичным 20
(16 + 4), поэтому мое первое предположение не совсем складывается. Если вы хотите знать, используйте отладчик, чтобы найти инструкцию, которая действительно не работает, и посмотрите значения регистра. Возможно, вам придется использовать представление дизассемблирования вместо представления исходного кода asm, особенно для версии, в которую вы попали от конца main
.
Возможно, вы не получите вывод на экран, если stdout
буферизован строкой, и ваша строка формата printf не заканчивается новой строкой. Таким образом, строка по-прежнему находится в буфере ввода-вывода при сбое. (Хотя IIRC, printf
в Windows не такой, и делает fflush()
буфер, даже если он не заканчивается переводом строки.)
Используйте puts
для печати фиксированной строки (без %
преобразований) и добавления новой строки. то есть puts(x)
похоже на printf("%s\n", x)
.