为什么我的无CRT应用程序在启动时会间歇性崩溃?

例如,这个应用程序:

#define _WIN32_WINNT 0x0500 #include <windows.h> int __stdcall NoCRTMain(void) { int result; PWSTR lpCmdLine = GetCommandLine(); for (;;) { if (*lpCmdLine == L'"') { lpCmdLine++; for (;;) { if (*lpCmdLine == L'"') break; if (*lpCmdLine == L'\0') break; lpCmdLine++; } } if (*lpCmdLine == L' ') break; if (*lpCmdLine == L'\0') break; lpCmdLine++; } while (*lpCmdLine == ' ') lpCmdLine++; result = MessageBox(NULL, lpCmdLine, L"Scripting Engine", MB_OK | MB_SYSTEMMODAL); if (result != IDOK) for (;;) Sleep(INFINITE); ExitProcess(0); } 

在Visual Studio 2010中,作为一个没有C运行时的64位应用程序构build,这在大多数情况下是完美的。 有时它会在启动时崩溃,因为没有明显的原因。 这个崩溃发生在应用程序的任何代码运行之前(见下文)。

当问题发生时,它只发生在一个特定的可执行文件实例上,即一个特定的可执行文件。 文件的一个字节一样的相同副本将正常运行。 计算机重新启动时,问题可能会(将?)消失。 我正在运行testing以尝试可靠地重现问题,以便我可以确定问题可能发生的情况,例如,只有在安装了Visual Studio的情况下才可以发现问题。 只有安装了防病毒软件? 但迄今为止,除了运气不佳以外,我还没有取得任何成功。

大多数情况下,debugging显示kernel32!BaseThreadInitThunk正在调用一个无效的地址,而不是NoCRTMain的地址,尽pipe一些最近的运行早于这个失败了,显然是在加载DLL的时候。

我相信我已经跟踪到这个问题了,当模块被加载时ImageBase被错误地设置了。 在工作实例上,相对于可执行模块的0x00D8内存转储,来自winnt.h的结构_IMAGE_OPTIONAL_HEADER64:

 00000001`3f5900d8 0b 02 0a 00 00 02 00 00 00 06 00 00 00 00 00 00 00000001`3f5900e8 00 10 00 00 00 10 00 00 00 00 59 3f 01 00 00 00 

显示ImageBase(最后八个字节)包含模块的开始地址,在这种情况下是1`3f590000。 在失败的实例上,同一个内存转储

 00000001`3fc600d8 0b 02 0a 00 00 02 00 00 00 06 00 00 00 00 00 00 00000001`3fc600e8 00 10 00 00 00 10 00 00 00 00 8f 3f 01 00 00 00 

表明ImageBase,而不是像预期的那样,是1f3f80000。

这似乎发生在debugging器可以检查进程的最早点之前,所以我不知道如何继续。 也许我需要做内核debugging? 我目前有一个VMWare vSphere虚拟机展示了这个问题,并且我有了一个可以恢复的快照,所以我可以进行实验。

所以:

  • 有没有人知道这种行为的原因,更重要的是,如何预防呢?

  • 是我对内存转储错误的解释?

  • 任何debugging/故障排除build议?

编译器选项:

 /Zi /nologo /W3 /WX- /O2 /Oi /GL /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_UNICODE" /D "UNICODE" /Gm- /EHsc /MT /GS- /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Fp"x64\Release\sehalt.pch" /Fa"x64\Release\" /Fo"x64\Release\" /Fd"x64\Release\vc100.pdb" /Gd /errorReport:queue 

链接器选项:

 /OUT:"C:\documents\code\w7lab-scripting\sehalt\x64\Release\sehalt.exe" /INCREMENTAL:NO /NOLOGO "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /NODEFAULTLIB /MANIFEST /ManifestFile:"x64\Release\sehalt.exe.intermediate.manifest" /ALLOWISOLATION /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /DEBUG /PDB:"C:\documents\code\w7lab-scripting\sehalt\x64\Release\sehalt.pdb" /SUBSYSTEM:WINDOWS /OPT:REF /OPT:ICF /PGD:"C:\documents\code\w7lab-scripting\sehalt\x64\Release\sehalt.pgd" /LTCG /TLBID:1 /ENTRY:"NoCRTMain" /DYNAMICBASE /NXCOMPAT /MACHINE:X64 /ERRORREPORT:QUEUE 

PS:看看我的哪个应用程序以这种方式已知失败,哪些不是,我怀疑这个问题只发生在小于一页(4096字节)大小的可执行文件上。

一年之后,我终于有信心宣称汉斯的建议已经完美地运行了:如果应用程序是使用/DYNAMICBASE:NO/FIXED:YES选项构建的,则不会出现问题。