Как вы используете printf в сборке x86 в Visual Studio 2017? - PullRequest
2 голосов
/ 12 мая 2019

Необработанное исключение в 0x777745BA (ntdll.dll) в MASM1.exe: 0xC0000005: Место записи нарушения прав доступа 0x00000014. Я использую сборку x86 в Visual Studio 2017, и она продолжает возвращать эту ошибку

Я включил все библиотеки и установил Windows 10 SDK. Я в основном озадачен тем, почему это возвращает эту ошибку в строке 21. Он даже открывает пустое окно, а затем сразу же закрывает его, возвращая ошибку.

            .586
            .MODEL FLAT
            .STACK 4096
            includelib libcmt.lib
            includelib libvcruntime.lib
            includelib libucrt.lib
            includelib legacy_stdio_definitions.lib
            EXTERN  printf:PROC
            EXTERN  scanf:PROC

            .DATA
                format BYTE "Enter a number", 0

            .CODE

            main PROC
                sub esp, 4
                push offset format
                call printf
                add esp, 4
                ret
            main ENDP
            END

Я создал проект VS 2017 C ++, генерирующий консольную программу Win32. В свойствах проекта / Linker / Advanced / entry point я установил точку входа main.

Ответы [ 2 ]

5 голосов
/ 12 мая 2019

У вас есть 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).

4 голосов
/ 12 мая 2019

Это решение, предложенное ФП в редакции их вопроса:

Я решил проблему, очистив параметр linker -> advanced -> Entry point, который я установил наmain в свойствах проекта.

Как @PeterCordes предлагает в своем ответе - лучшее решение для устранения проблемы, связанной со стеком, - это удалить sub esp, 4.

...