如何在 Visual Studio 2017 的 x86 程序集中使用 printf?
Posted
技术标签:
【中文标题】如何在 Visual Studio 2017 的 x86 程序集中使用 printf?【英文标题】:How do you use printf in x86 assembly in Visual Studio 2017? 【发布时间】:2019-05-12 15:23:44 【问题描述】: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
。
【问题讨论】:
是的,我已经用过 ret 并没有解决任何问题 然后用ret
的版本更新你的问题;这个版本有一个明显的大bug。
真的,您仍然得到writing location 0x00000014
作为ret
的错误?在哪个指令上?是在printf
里面,还是在主返回之后?
只是对此感到抱歉。还有其他建议吗?我一直在使用调试器试图解决这个问题,但似乎找不到错误的解决方案
我有点惊讶这个集合和链接。我原以为您需要.MODEL FLAT, C
才能正确链接main
,因为它确实应该解析为_main
。您说您使用的是 Visual Studio 2017。您是否使用自定义组装和链接命令,如果是,您用于组装和链接的命令是什么?我怀疑您没有以正常方式使用 IDE 创建 VS2017 MSVC 项目(通过添加 MASM 作为构建依赖项),然后使用此代码添加 ASM 文件。
【参考方案1】:
你在调用之前有一个sub esp,4
和一个push
,所以要恢复堆栈指针指向返回地址你需要在ret
之前add esp,8
,而不是add esp, 4
(printf
是一个可变参数函数,因此它不会将自己的参数从堆栈中弹出。它使用 cdecl 调用约定。)
或者更好,删除sub esp,4
。
32 位 Windows 仅维护 4 字节堆栈对齐,因此您无需在 push
/call
之前对 ESP 执行任何额外操作,即可在 call
之前重新对齐堆栈指针。而且你没有使用你为任何东西保留的那 4 个字节。
更新:MichaelPetch 观察到您的程序可能在 inside 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,Windows 上的 printf
不是这样,即使 fflush()
不是以换行符结尾的缓冲区也是如此。)
使用puts
打印固定字符串(无%
转换)并附加换行符。即puts(x)
就像printf("%s\n", x)
。
【讨论】:
调试器将错误地显示第 21 行的错误(add esp, 4
),而实际上它实际上发生在 printf
调用中以及我复制它的方式(一直到值 0x00000014 ) 是为了防止执行 C 运行时启动代码(通过使程序的入口点main
)而不是 C 中的默认入口点是什么运行时代码。我的观点是,问题更多在于他们如何组装和链接他们没有提供的信息:(
很抱歉没有提供我的项目构建依赖项设置为 masm 复选框。我的链接器高级入口点设置为 main。链接器系统仍然是控制台。我不确定您希望我提供哪些其他信息来说明我正在链接的内容
@MikeD:使用@username
通知您正在回复的人。你在我的回答下回复,所以我得到了一个 ping,但 MichaelPetch 可能没有。也就是说,将这些信息放在您的问题中将是一个开始。您通常希望入口点是 CRT(C 运行时启动)启动代码,因此它可以在调用您的 main
之前运行库初始化函数。
@Michael Petch 和 Peter Cordes 这是我删除了主入口点的入口点,这就是修复它的原因。谢谢你们的时间和信息。你们都帮了我很多。再次感谢您【参考方案2】:
这是 OP 在编辑他们的问题时提出的解决方案:
我通过清除我在项目属性中设置为main
的linker
->advanced
->Entry point
选项解决了这个问题。
正如@PeterCordes 在他的回答中所建议的那样 - 解决堆栈相关问题的最佳解决方案是删除 sub esp, 4
。
【讨论】:
以上是关于如何在 Visual Studio 2017 的 x86 程序集中使用 printf?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用Visual Studio 2013或Visual Studio 2017设置TFS 2013
如何设置 TFS 2013 以使用 Visual Studio 2013 或 Visual Studio 2017 构建