如何在 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], aleax 保存 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 在编辑他们的问题时提出的解决方案:

我通过清除我在项目属性中设置为mainlinker->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 构建

visual studio 2017 如何下载和安装

如何在 Visual Studio 2017 中创建 Visual c++ MFC 控制台项目

无法找到visual studio 2017生成工具

如何在 Visual Studio 2017 中启用 WiX 项目