“ucrtbase.dll”中的 _snprintf_s() 崩溃,状态为 STATUS_ILLEGAL_INSTRUCTION

Posted

技术标签:

【中文标题】“ucrtbase.dll”中的 _snprintf_s() 崩溃,状态为 STATUS_ILLEGAL_INSTRUCTION【英文标题】:Crash in _snprintf_s() in "ucrtbase.dll" with STATUS_ILLEGAL_INSTRUCTION 【发布时间】:2018-08-26 11:34:44 【问题描述】:

我注意到我的二进制文件将在 CPU 上崩溃没有 SSE 支持,异常代码 0xC000001D (STATUS_ILLEGAL_INSTRUCTION),尽管我使用选项 /arch:IA32 进行编译。而且我已经能够追踪到它崩溃的确切位置:第一次调用 _snprintf_s() 时,它就会崩溃。崩溃发生在 ucrtbase.dll 内部,不是我自己的代码。

现在,有趣部分是,当我使用编译器选项 /MT 进行“完全静态”构建时,以避免显式依赖 ucrtbase.dll,生成的二进制文件就可以了!但是,一旦我使用选项 /MD 将某些代码编译为“共享”构建,它将在 ucrtbase.dll 中再次崩溃。

所以看起来 UCRT 的“静态”版本仍然可以在 CPU 上运行 SSE 支持,但是“共享" (DLL) 版本可以。这种不一致对我来说显然是个错误!

有什么想法吗?


构建环境:

Windows 10 v1803 Visual Studio 2017.8 (v15.8.1) Windows SDK v10.0.17134.12 工具集:v141_xp 编译器选项:/arch:IA32

测试机(仅用于兼容性测试):

CPU:奔腾 II 操作系统:带有 Service-Pack 3 的 Windows XP

注意: 用于设置“共享”构建的 Redist DLL (ucrtbase.dll +api-ms-win-*.dll) 已直接从 C:\Program Files (x86)\Windows Kits\10\Redist\ucrt\DLLs\x86 目录复制!据我所知,这是这些 DLL 的最新可用版本 (v10.0.17134.12)。


即使是这个最小的测试程序也会重现崩溃:

#include <stdio.h>

int main()

    char buffer[128];
    _snprintf_s(buffer, 128, _TRUNCATE, "Hello %s!\n", "World!");
    fputs(buffer, stdout);
    getc(stdin);
    return 0;

【问题讨论】:

我已经用调试器追踪了。它在 _snprintf_s() 的实现内部崩溃,在系统 DLL ucrtbase.dll 内部崩溃。代码是 STATUS_ILLEGAL_INSTRUCTION,所以几乎可以肯定 不是我的代码中的问题,而是在系统 DLL ucrtbase.dll 内的预先存在的代码中使用 SSE 指令。即使是带有 nothingmain() 调用 _snprintf_s() 的最小测试应用程序也会重现崩溃!并且仅在 CPU 没有 SSE 上;否则它工作正常! 你试过standard snprintf()吗?如果你可以使用 C++11,它是可用的。 您也应该将此情况报告给 Microsoft。 @KhouriGiordano 他们可能会告诉 OP,Windows XP 支持已于 2014 年结束,并且什么也不做。截至今天,所有受支持的 Windows 版本(即 8.1 和 10)都需要 SSE。 @SkepticalEmpiricist:调用snprintf() 会导致与调用_snprintf_s() 相同的崩溃。查看调用堆栈,似乎它们都在 ucrtbase.dll 内部名为 _stdio_common_vsprintf 的同一个内部例程中结束 - 它崩溃了。 【参考方案1】:

更新:

经过更多调试和摆弄,我做了一个非常有趣的观察:最新 vcredist_x86.exe (Microsoft Visual C++ 2017) 中包含的 UCRT“Redist”DLL Redistributable installer),即 VS2017.8 (v14.15.26706) 附带的版本,实际上与 latestRedist\ucrt\DLLs\x86 目录中的那些 UCRT "Redist" DLL 完全不同Windows SDK (v10.0.17134.12):

ucrtbase.dll 来自 最新 Windows SDK v10.0.17134.12

ucrtbase.dll 来自 最新 Visual C++ 2017 可再发行安装程序: v10.0.10586.15

确实,我现在可以确认使用 最新 Visual C++ 2017 (v14.15.26706) 编译并使用 /MD 选项编译的应用程序在 non-SSE CPU,只要我们使用“旧”ucrtbase.dll 版本,从最新 vcredist_x86.exe 安装程序。

如果 Windows SDK 已经提供了更新的版本,我有点担心使用这种旧版本的 UCRT。但是,显然,这就是微软使用 Visual C++ 2017 Redistributable 安装程序所做的事情。所以,我想,这是我们应该使用的......


以防万一微软的任何人正在阅读此内容:

如果在 Windows SDK 的 Redist\ucrt 目录中,有单独的子UCRT 的每个“化身”的文件夹——UCRT 的最新“尖端”版本和我们实际上应该重新分发的 UCRT 的“兼容”版本。如果那么微软的某个人花了三分钟时间编写一个小的 README 文件,告诉我们应该选择 CRT 的哪个“化身”并将其放在Redist\ucrt\README.txt,几乎不可能出错...

【讨论】:

从 VS 可再发行组件中提取文件并不是很简单。有关使用 WiX 工具集的过程,请参阅此 answer

以上是关于“ucrtbase.dll”中的 _snprintf_s() 崩溃,状态为 STATUS_ILLEGAL_INSTRUCTION的主要内容,如果未能解决你的问题,请参考以下文章

python生成可执行文件时遇到的问题

找出调用 __fastfail 的原因

Python 使用pyinstaller打包程序失败提示找不到c盘的某个dll文件解决方法,FileNotFoundError: [WinError 2] 系统找不到指定的文件

Python 使用pyinstaller打包程序失败提示找不到c盘的某个dll文件解决方法,FileNotFoundError: [WinError 2] 系统找不到指定的文件

windows10 无法启动wamp64的wampapache64

CImg 在 Debug 模式下抛出异常,在 Release 中工作正常