首页 > 解决方案 > 如何在 Visual Studio 2017 的 x86 程序集中使用 printf?

问题描述

MASM1.exe 中 0x777745BA (ntdll.dll) 处的未处理异常:0xC0000005:访问冲突写入位置 0x00000014。我在 Visual Studio 2017 中使用 x86 程序集,它不断返回此错误

我已经包含了所有的库并安装了 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

我创建了一个生成 Win32 控制台程序的 VS 2017 C++ 项目。在项目属性///Linker选项中Advancedentry point我已将入口点设置为main.

标签: visual-studioassemblyx86masm

解决方案


您在调用之前有 asub esp,4和 a push,因此要将堆栈指针恢复为指向您需要add esp,8before的返回地址ret,而不是add esp, 4

printf是一个可变参数函数,因此它不会自己的参数从堆栈中弹出。它使用 cdecl 调用约定。)

或者更好的是,删除sub esp,4.

32 位 Windows 仅维护 4 字节堆栈对齐,因此您不需要在push/之前使用 ESP 做任何额外的事情call来让堆栈指针在call. 而且你没有使用你为任何东西保留的 4 个字节。


更新:MichaelPetch 观察到您的程序可能在内部 printf崩溃,因为您在没有初始化 libc 的情况下调用了它。可能你正在使用这个函数作为入口点来构建你的程序,而不是从正常的 C 启动代码中调用。 (并且 Visual Studio 调试器错误地将崩溃报告为在 之后的行call,而不是实际发生崩溃的位置。)


您的错误消息似乎仍然来自问题的第一个版本,您遗漏了ret! 在这种情况下,执行只是落在下main一个字节的末尾,将它们解码为指令。应该是零。

00 00解码为add [eax], al,并eax从 printf 的返回值中保存 14。(printf返回 printf 的字符数,您的格式字符串为 14 个字节长)。

但是错误消息是关于写地址0x14的,它是十进制20(16 + 4),所以我的第一个猜测并没有完全加起来。 如果您想知道,请使用调试器查找实际出错的指令,并查看寄存器值。 您可能必须使用反汇编视图而不是 asm 源代码视图,尤其是对于您从main.


如果是行缓冲的,您可能在屏幕上没有输出stdout,并且您的 printf 格式字符串不以换行符结尾。因此,当您崩溃时,字符串仍然位于 IO 缓冲区中。(尽管 IIRC,printf在 Windows 上不是那样的,并且fflush()即使它不以换行符结尾,它也会缓冲。)

用于puts打印固定字符串(无%转换)并附加换行符。即puts(x)就像printf("%s\n", x)


推荐阅读