У вас есть sub esp,4
и push
перед вызовом, поэтому, чтобы восстановить указатель стека, указывающий на адрес возврата, вам нужно add esp,8
перед ret
вместо add esp, 4
(printf
— это функция с переменными аргументами, поэтому она не извлекает собственные аргументы из стека. Она использует соглашение о вызовах cdecl.)
Или лучше удалить sub esp,4
.
32-разрядная версия Windows поддерживает только 4-байтовое выравнивание стека, поэтому вам не нужно делать ничего дополнительно с ESP до push
/call
, чтобы повторно выровнять указатель стека до call
. И вы не используете эти 4 байта, которые вы зарезервировали для чего-либо.
Обновление: МайклПетч заметил, что ваша программа, вероятно, дает сбой внутри printf
, потому что вы вызвали ее без инициализации libc. Вероятно, вы создаете свою программу с этой функцией в качестве точки входа, а не с вызовом из обычного кода запуска C. (И что отладчик Visual Studio ошибочно сообщает, что сбой произошел в строке после call
, а не где на самом деле произошла авария.)
Похоже, ваше сообщение об ошибке все еще относится к первой версии вопроса, где вы пропустили ret
! В этом случае выполнение просто падает с конца main
на следующие байты, декодируя их как инструкции. Наверное нули.
00 00
декодируется как add [eax], al
, а eax
содержит 14 из возвращаемого значения printf. (printf
возвращает количество символов printf, а ваша строка формата имеет длину 14 байтов).
Но сообщение об ошибке касается записи адреса 0x14
, который представляет собой десятичное число 20
(16 + 4), так что мое первое предположение не совсем совпадает. Если вы хотите знать, используйте отладчик, чтобы найти инструкцию, которая на самом деле дает сбой, и посмотрите значения регистров. Возможно, вам придется использовать представление дизассемблирования вместо представления исходного кода, особенно для версии, в которой вы упасть с конца main
.
Вероятно, вы не получите вывода на экран, если stdout
буферизуется строкой, а ваша строка формата printf не заканчивается новой строкой. Таким образом, строка все еще находится в буфере ввода-вывода при сбое. (Хотя IIRC, printf
в Windows не такой, и делает fflush()
буфер, даже если он не заканчивается новой строкой.)
Используйте puts
для печати фиксированной строки (без преобразования %
) и добавления новой строки. то есть puts(x)
похоже на printf("%s\n", x)
.
person
Peter Cordes
schedule
12.05.2019
ret
; эта версия имеет очевидную огромную ошибку. - person Peter Cordes   schedule 12.05.2019writing location 0x00000014
как ошибку сret
? По какой инструкции? Это внутриprintf
или после основного возврата? - person Peter Cordes   schedule 12.05.2019printf
. Таким образом, вам нужноadd esp, 8
перед возвратом очистить как переданный аргументformat
, так и скорректироватьsub esp, 4
- person Michael Burr   schedule 12.05.2019.MODEL FLAT, C
, чтобы правильно связатьmain
, поскольку он действительно должен разрешаться в_main
. Вы говорите, что используете Visual Studio 2017. Используете ли вы пользовательские команды сборки и компоновки, и если да, то какие команды вы используете для сборки и компоновки? У меня есть подозрение, что вы не создали проект MSVC VS2017 с IDE обычным способом (добавив MASM в качестве зависимости сборки), а затем добавили файл ASM с этим кодом. - person Michael Petch   schedule 12.05.2019printf
не будет работать должным образом), и вам удалось сделать точку входа для вашей программы основной (с параметр командной строки?). Это помимо того факта, чтоadd esp, 4
должно бытьadd esp, 8
. Я смог воспроизвести вашу проблему, заставивmain
быть точкой входа, связавшись с опцией/ENTRY
. Другая возможность заключается в том, что то, что вы показываете здесь для ассемблерного кода, не является тем, что вы на самом деле используете. - person Michael Petch   schedule 12.05.2019.MODEL FLAT
на.MODEL FLAT, C
, затем изменитеADD ESP, 4
(послеprintf
) наADD ESP, 8
и, если вы связываетесь с/ENTRY
, удалите эту опцию. Это то, что я рекомендую, если вы не показываете свои команды сборки и компоновки (и все варианты). - person Michael Petch   schedule 12.05.2019.MODEL FLAT, C
, поэтому для того, чтобы этот код собирался и связывался без неопределенных ссылок, необходимо было установить что-то внешнее, чтобы переопределить поведение по умолчанию. Например, без изменения поведения по умолчаниюEXTERN printf:PROC
должно было бытьEXTERN _printf:PROC
, аmain PROC
должно было быть_main PROC
и т. д. - person Michael Petch   schedule 12.05.2019