visual-studio - 如何在 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
选项中Advanced
,entry point
我已将入口点设置为main
.
解决方案
您在调用之前有 asub esp,4
和 a push
,因此要将堆栈指针恢复为指向您需要add esp,8
before的返回地址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)
。
推荐阅读
- reactjs - 从组件 A 到 B 的可拖动项
- docker - 向世界服务器发送命令而不附加它
- java - 如何在 C#/Xamarin 中使用带有来自 android 钥匙串的私钥的 X.509 证书?
- python - 替换 python 中的 \G 锚点
- javascript - 从服务器发送 2 组不同的错误消息
- encryption - 如何使用 Yubikey 签署任意数据
- performance - 使用 JProfiler 监控生产中的应用程序
- python - 多处理引发操作系统错误,但仍然给出结果?
- python - TensorFlow Keras CuDNNGRU 到 GRU 的转换
- excel-formula - Excel - 累积季度储蓄分月