为啥我的 x64 程序在 Windows 上静默崩溃?

Posted

技术标签:

【中文标题】为啥我的 x64 程序在 Windows 上静默崩溃?【英文标题】:Why does my x64 program crash silently on Windows?为什么我的 x64 程序在 Windows 上静默崩溃? 【发布时间】:2019-03-14 01:31:10 【问题描述】:

考虑这个小程序:

int main(int argc, char *argv[])

    *((int *) 0) = 1;
    return 0;

显然,写入 $0 会导致内存访问错误。当使用 Visual C 将此程序编译为 32 位可执行文件并运行它时,Windows 7 清楚地显示该程序已崩溃:(德国系统)

但是,当使用 Visual C 将程序编译为 64 位可执行文件时,它只是静默崩溃。不会弹出系统对话框通知用户程序刚刚崩溃。

这是 x64 二进制文件的正常行为还是我的 Windows 7 配置有问题?我认为操作系统应该在程序崩溃时清楚地向用户显示,而不是默默地杀死它们......

编辑:对于所有声称编译器可能只是优化空指针访问的人,这里是程序集。可以看到非法内存访问存在,运行程序后也会显示在Windows的应用程序日志中。

; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01 

include listing.inc

INCLUDELIB LIBCMT
INCLUDELIB OLDNAMES

PUBLIC  main
; Function compile flags: /Odtp
_TEXT   SEGMENT
argc$ = 8
argv$ = 16
main    PROC
; File d:\test.c
; Line 2
    mov QWORD PTR [rsp+16], rdx
    mov DWORD PTR [rsp+8], ecx
; Line 3
    mov DWORD PTR ds:0, 1      ; here we go folks
; Line 4
    xor eax, eax
; Line 5
    ret 0
main    ENDP
_TEXT   ENDS
END

编辑 2:我还验证了 HKEY_CURRENT_USER\Software\Microsoft\Windows\Windows Error Reporting\DontShowUI 设置为 0。因此 Windows 已明确配置为报告崩溃。但它仅适用于 x86 二进制文件,而不适用于 x64 二进制文件。是的,对于 x64 二进制文件,崩溃会在应用程序日志中报告,但错误对话框(请参见上面的屏幕截图)仅显示 x86 二进制文件,而不显示 x64 二进制文件,尽管DontShowUI 明确设置为 0。

EDIT 3:我已经在不同的 Windows 系统上进行了测试。结果如下:

Windows 7: x86 shows crash dialog, x64 doesn't (as described above)
Windows 8: both x86 and x64 show the crash dialog
Windows 10: neither x86 nor x64 show the crash dialog

因此,这里唯一表现不一致的系统实际上是 Windows 7。在所有其他系统上,崩溃对话框要么显示要么不显示,而在 Windows 7 上,x86 和 x64 程序的崩溃之间的行为不同。很奇怪。

【问题讨论】:

“这是正常行为吗”,再次询问未定义行为的行为没有意义。 “显然,写入 $0 会导致内存访问错误。”不,那个未定义的行为。 @Stargateur 即使它在技术上是 UB,在 Windows 上它应该在 x86 和 x64 中崩溃,因为它应该在 Linux 上崩溃 那是 WER 对话框,它是否显示是操作系统配置选项。顺便说一句,它在 Win10 机器上不再默认了。仔细检查您在应用程序日志中是否收到了崩溃事件。在 superuser.com 上询问 WER 配置。并考虑继续前进,您要确保调试器可以帮助您诊断此错误。 @Stargateur:从编程语言的角度来看,你是对的:思考展示 UB 的代码的潜在结果是没有实际意义的。但是,平台可以将保证扩展到编程语言所建立的保证之外。在 Windows(以及任何针对 Windows 的编译器)上,取消引用指向无效地址的指针将确定性地触发访问冲突,报告为 SEH 异常。因此,问题中的代码就语言而言表现出 UB,但在 Windows 上仍会表现出明确定义的行为。 @sta:引发 SEH 异常以响应对不可访问内存的写入或读取是 Windows ABI 的一部分。 任何面向 Windows 的编译器必须实现该协定。我在之前的评论中明确地说 “平台” 是指操作系统和编译器的组合。这不仅限于 Visual Studio。此外,指向堆栈的指针始终有效。堆栈内存是可访问的。它可能不是指向正确地址的指针,但这并不意味着操作系统、运行时或编译器需要引发 SEH 异常。您在这里混淆了不同的方面。 【参考方案1】:

*((int *) 0) = 1; 的行为以及您的整个程序是未定义的

各种崩溃都是这种未定义行为的表现。

一些编译器可能会将您的代码优化为int main:gcc 已经在高优化设置下优化未定义语句已有一段时间了。也许这就是 64 位编译行为的原因?

【讨论】:

这很容易检查:在崩溃行之后显示一个消息框。如果你能看到它,那么编译器已经优化了访问。 这不会尝试回答所提出的问题。 OP 已验证,正在发出写入地址 0 的目标代码。戴着语言律师有色眼镜看这是否是UB没有区别。如果您不再超越语言规范,该平台已决定将定义良好的语义和可重现的行为归因于 UB。 @P__J__ clang 确实如此,这就是为什么它是 UB @sta:godbolt 上的 Clang 不针对 Windows,所以这是一个不同的平台,它根本不适用于这里提出的问题。这个问题也的话题。它不是询问如何配置系统,而是询问系统的行为方式。这个问题的答案将作为决定在应用程序中实现错误发现和报告的基础。这显然是以开发人员为中心的问答,即使它是解决方案的一部分,涉及更改(全局)系统设置。 @sta:再一次,不正确。异常在 Windows 上的处理方式不同,具体取决于您是否在 WoW64 模拟器中运行。对此的洞察会产生信息,开发人员可以采取行动。这也是您无法更改的内容,因此向超级用户询问此内容不会产生任何有意义的信息。【参考方案2】:

假设发出的代码确实包含对地址零处内存的访问,那么二进制文件就会崩溃。不保证 Windows 会显示崩溃消息 - 例如,它可以是 suppressed via group policy。

进程本身可以使用SetErrorModeWerRegisterRuntimeExceptionModule 对其进行控制。另见werapi.h

【讨论】:

我的代码包含对地址为零的内存的访问并崩溃。我验证了显示崩溃的 Windows 应用程序日志。我已阅读您发布的链接中的文章,并且已验证 HKEY_CURRENT_USER\Software\Microsoft\Windows\Windows Error Reporting\DontShowUI 设置为 0。因此,Windows 已明确配置为显示崩溃。但它仅适用于 x86 二进制文件,而不适用于 x64 二进制文件。是的,对于 x64 二进制文件,崩溃显示在应用程序日志中,但为什么当 DontShowUI 明确设置为 0 时,对话框只显示 x86 二进制文件?对我来说没有意义。 64 位代码可能使用不同的标志。您还可以在自己的代码中进行设置,以在运行时显式控制 Windows 崩溃消息行为。我手边没有链接,但也许这会帮助你找到它。 @KubaOber 显然不是。至少在我的 Windows 上HKEY_CURRENT_USER\Software\Microsoft\Windows\Windows Error Reporting\DontShowUI 不会改变 x86 和 x64 程序的行为。 有运行时 api 可以控制这一点并覆盖注册表。您可能需要从您的进程中检查是否已经安装了 WER 处理程序,这些处理程序可能会实现这种“无显示”行为。您可能还想从您的进程中检查注册表值是否与您在注册​​表编辑器中看到的相同:它可能不同! 我在运行时检查了注册表项的值,它是 0,所以这里没有运气。然后我检查了GetErrorMode()的返回值。对于 x86 和 x64,它是 SEM_FAILCRITICALERRORS (1)。这很奇怪,因为它被描述为The system does not display the critical-error-handler message box. Instead, the system sends the error to the calling process,但是当 x86 二进制文件崩溃时会显示错误消息框 is!接下来我尝试的是明确调用SetErrorMode(0),但这也没有改变行为。错误消息框仍然只显示在 x86 上,而不是在 x64 上。【参考方案3】:

Windows 10 上的 WER 在不可见进程崩溃时从不显示对话框,无论 DontShowUI 值如何。 DontShowUI 仅对显示用户界面的进程有影响。我仔细看了看,但找不到类似的不可见进程设置。在这些情况下,WER 的静默崩溃处理似乎是设计使然。

所以我编写了一个工具,它可以将自身挂接到 WER 并修改其行为以显示所有崩溃的对话框,包括那些在不可见进程中的崩溃:WerTweak。另外,它使 WER 显示与以前的 Windows 版本相同的旧式崩溃报告对话框,而不是通常在 Windows 10 中显示的残缺的新对话框。

【讨论】:

以上是关于为啥我的 x64 程序在 Windows 上静默崩溃?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的exe文件没有在其他电脑上运行? [调试]

为啥我在 Windows 10 UWP 上收到“无法注册包”部署错误?

Windows 7 x64 上带有 libNFC 的 RFIDIOt

为啥在 x64 Java 中 long 比 int 慢?

为啥在 x64 程序集中将复制变量地址移动到寄存器?

NSIS 构建不会在 Windows 上安装 Electron 应用程序。安装程序静默退出并产生 1620 错​​误