Mixed debug and release runtimes

EDN Admin

Well-known member
Joined
Aug 7, 2010
Messages
12,794
Location
In the Machine
We are in the process of migrating from VC9 to VC11. I just noticed that our debug CRT leak reporting was not working. I think Ive traced it down to the fact that VC11 is getting confused when both the debug and release runtime dll is loaded in our process.
Our project is quite large and loads many dlls. Some of these dlls are pre-built as a release config and committed to our repo. As such, these dlls will pull in the release crt (msvcr110.dll). All of our projects link to the dynamic versions of the runtime
dlls (but debug or release depending on the config). The upshot of it is that both the debug and release versions of the crt dll will be loaded into our process. Under VC9, this seemed to be ok. At process shutdown, the crt would eventually call _CrtDumpMemoryLeaks():
><span style="white-space:pre msvcr90d.dll!_CrtDumpMemoryLeaks() Line 2538<span style="white-space:pre
C++<br/>
<span style="white-space:pre msvcr90d.dll!__CRTDLL_INIT(void * hDllHandle=0x0f800000, unsigned long dwReason=0, void * lpreserved=0x00000001) Line 345<span style="white-space:pre
C<br/>
<span style="white-space:pre msvcr90d.dll!_CRTDLL_INIT(void * hDllHandle=0x0f800000, unsigned long dwReason=0, void * lpreserved=0x00000001) Line 214 + 0x11 bytes<span style="white-space:pre
C<br/>
<span style="white-space:pre ntdll.dll!_LdrpCallInitRoutine@16() + 0x14 bytes<span style="white-space:pre
<br/>
<span style="white-space:pre ntdll.dll!_LdrShutdownProcess@0() + 0x141 bytes<span style="white-space:pre
<br/>
<span style="white-space:pre ntdll.dll!_RtlExitUserProcess@4() + 0x74 bytes<span style="white-space:pre
<br/>
<span style="white-space:pre kernel32.dll!76697a0d() <span style="white-space:pre
<br/>
<span style="white-space:pre msvcr90d.dll!__crtExitProcess(int status=0) Line 732<span style="white-space:pre
C<br/>
<span style="white-space:pre msvcr90d.dll!doexit(int code=0, int quick=0, int retcaller=0) Line 644 + 0x9 bytes<span style="white-space:pre
C<br/>
<span style="white-space:pre msvcr90d.dll!exit(int code=0) Line 412 + 0xd bytes<span style="white-space:pre
C<br/>
<span style="white-space:pre myapp.exe!__tmainCRTStartup() Line 599<span style="white-space:pre
C<br/>
<span style="white-space:pre myapp.exe!WinMainCRTStartup() Line 403<span style="white-space:pre
C<br/>
<span style="white-space:pre kernel32.dll!@BaseThreadInitThunk@12() + 0x12 bytes<span style="white-space:pre
<br/>
<span style="white-space:pre ntdll.dll!___RtlUserThreadStart@8() + 0x27 bytes<span style="white-space:pre
<br/>
<span style="white-space:pre ntdll.dll!__RtlUserThreadStart@8() + 0x1b bytes<span style="white-space:pre
<br/>

Under VC11, the process exit sequence makes calls into both msvcr110d.dll and msvcr110.dll:
><span style="white-space:pre msvcr110.dll!__CRTDLL_INIT(void * hDllHandle, unsigned long dwReason, void * lpreserved=0x00000001) Line 335<span style="white-space:pre
C<br/>
<span style="white-space:pre <span style="text-decoration:underline msvcr110.dll!_CRTDLL_INIT(void * hDllHandle=0x6afc0000, unsigned long dwReason=0, void * lpreserved=0x00000001) Line 210<span style="white-space:pre
C<br/>
<span style="white-space:pre ntdll.dll!_LdrpCallInitRoutine@16()<span style="white-space:pre
Unknown<br/>
<span style="white-space:pre ntdll.dll!_LdrShutdownProcess@0()<span style="white-space:pre
Unknown<br/>
<span style="white-space:pre ntdll.dll!_RtlExitUserProcess@4()<span style="white-space:pre
Unknown<br/>
<span style="white-space:pre kernel32.dll!_ExitProcessStub@4()<span style="white-space:pre
Unknown<br/>
<span style="white-space:pre msvcr110d.dll!__crtExitProcess(int status=0) Line 726<span style="white-space:pre
C<br/>
<span style="white-space:pre msvcr110d.dll!doexit(int code=0, int quick=0, int retcaller=0) Line 639<span style="white-space:pre
C<br/>
<span style="white-space:pre <span style="text-decoration:underline msvcr110d.dll!exit(int code=0) Line 395<span style="white-space:pre
C<br/>
<span style="white-space:pre myapp.exe!__tmainCRTStartup() Line 549<span style="white-space:pre
C<br/>
<span style="white-space:pre myapp.exe!WinMainCRTStartup() Line 377<span style="white-space:pre
C<br/>
<span style="white-space:pre kernel32.dll!@BaseThreadInitThunk@12()<span style="white-space:pre
Unknown<br/>
<span style="white-space:pre ntdll.dll!___RtlUserThreadStart@8()<span style="white-space:pre
Unknown<br/>
<span style="white-space:pre ntdll.dll!__RtlUserThreadStart@8()<span style="white-space:pre
Unknown<br/>

The fact that its using a release version of the crt probably cant be good. At the very least it causes the leak check to be skipped since that call is debug only (crtlib.c, line 301):
#ifdef _DEBUG<br/>
/* Dump all memory leaks */<br/>
if (_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) & _CRTDBG_LEAK_CHECK_DF)<br/>
{<br/>
_CrtSetDumpClient(NULL);<br/>
_CrtDumpMemoryLeaks();<br/>
}<br/>
#endif /* _DEBUG */<br/>

Any advice on how to make this work again? Is the new behavior expected? Our app consumes dlls built by third parties so we dont have control over what version of the crt they use. In fact, our installer ships all versions of the crt since 7.1. This seems
to be a growing issue for distributing Windows applications.
Thanks,
Paul


View the full article
 
Back
Top