DLL为啥要使用MD模式--运行时对象越过DLL边界的潜在错误

现象:

当传递一个c运行时对象,例如句柄,locales,环境变量时,进入或者传出一个Dll(函数调用越过了Dll边界),那么如果这个动态库也正将文件调入动态库,且使用了不同的CRT库的副本,那么可能会有无法预期的现象出现。 当分配内存(显式的使用malloc或者new,以及隐式的使用strup等)然后将指针传过一个动态库的边界去释放时,可能会有一个于此相关联的问题出现。如果这个动态库和他的用户使用的是crt库的不同副本时,这可能会引起一个内存访问越界或者堆损坏。另一个在调试时可能会出现在输出窗口的该错误的症状现象是:

HEAP[]: Invalid Address specified to RtlValidateHeap(#,#)

 

原因

        每份特定crt库的副本都有一个单独且不同的状态。因此,crt对象例如file handles, environment variables, 和 locales都只对这些对象分配或设定时的crt的副本才是有效的。当一个动态库和其用户使用crt库的不同副本时,你是不能传递这些crt对象越过动态库边界的而且还奢望他们能在另一边被正确的接收到。

        同理,因为一个crt库的每份副本都有其自己的堆管理器,在一个crt库中分配内存并越过库边界传输到这个crt库的另一份的副本去释放是造成堆损坏的一个潜在因素。 如果设计的动态库想要越过边界传递crt对象或者分配空间后让该动态库外去释放,你需要限制动态库用户使用和该动态库同一份的crt库的副本。而这种情况只有当两者都使用crt的动态库dll的相同版本时才可以。

 

需要注意的情况:

这样的话,如果使用Visual C++ 5.0构建应用程序的同时又使用采用Visual C++ 4.1或者更早版本构建的动态链接库DLL,则可能会有问题。因为vc++4.1的CRT库的DLL的版本是msvcrt40.dll,而采用vc++5.0的则使用的是msvcrt.dll,那么你不可能使用这些DLL来构建你的应用程序时使用来自CRT库的同一份的副本!!!.

 

原文:

Potential Errors Passing CRT Objects Across DLL Boundaries

When you pass C Run-time (CRT) objects such as file handles, locales, and environment variables into or out of a DLL (function calls across the DLL boundary), unexpected behavior can occur if the DLL, as well as the files calling into the DLL, use different copies of the CRT libraries.

A related problem can occur when you allocate memory (either explicitly with new or malloc, or implicitly withstrdup, strstreambuf::str, and so on) and then pass a pointer across a DLL boundary to be freed. This can cause a memory access violation or heap corruption if the DLL and its users use different copies of the CRT libraries.

Another symptom of this problem can be an error in the output window during debugging such as:

HEAP[]: Invalid Address specified to RtlValidateHeap(#,#)

Each copy of the CRT library has a separate and distinct state. As such, CRT objects such as file handles, environment variables, and locales are only valid for the copy of the CRT where these objects are allocated or set. When a DLL and its users use different copies of the CRT library, you cannot pass these CRT objects across the DLL boundary and expect them to be picked up correctly on the other side.

Also, because each copy of the CRT library has its own heap manager, allocating memory in one CRT library and passing the pointer across a DLL boundary to be freed by a different copy of the CRT library is a potential cause for heap corruption.

If you design your DLL so that it passes CRT objects across the boundary or allocates memory and expects it to be freed outside the DLL, you restrict the DLL users to use the same copy of the CRT library as the DLL. The DLL and its users use the same copy of the CRT library only if both are linked with the same version of the CRT DLL. This could be a problem if you mix applications built with Visual C++ 5.0 with DLLs that are built by Visual C++ 4.1 or earlier. Because the DLL version of the CRT library used by Visual C++ 4.1 is msvcrt40.dll and the one used by Visual 5.0 is msvcrt.dll, you cannot build your application to use the same copy of the CRT library as these DLLs.

However, there is an exception. In US English version and some other localized versions of Windows 2000, such as German, French, and Czech, a forwarder version of the msvcrt40.dll (version 4.20) is shipped. As a result, even though the DLL is linked with msvcrt40.dll and its user is linked with msvcrt.dll, you are still using the same copy of the CRT library because all calls made to msvcrt40.dll are forwarded to msvcrt.dll.

However this forwarder version of msvcrt40.dll is not available in some localized versions of Windows 2000, such as Japanese, Korean, and Chinese. So, if your application targets these operating systems, you need to either obtain an upgraded version of the DLL that doesn't rely on msvcrt40.dll or alter your application to not rely on using the same copy of the CRT libraries. If you have developed the DLL, this means rebuilding it with Visual C++ 4.2 or later. If it is a third- party DLL, you need to contact your vendor for an upgrade.

Please note that this forwarder DLL version of msvcrt40.dll (version 4.20) cannot be redistributed.

This example passes a file handle across a DLL boundary.

The DLL and .exe file are built with /MD, so they share a single copy of the CRT.

If you rebuild with /MT so that they use separate copies of the CRT, running the resulting test1Main.exe results in an access violation.

// test1Dll.cpp
// compile with: /MD /LD
#include <stdio.h>
__declspec(dllexport) void writeFile(FILE *stream)
{
   char   s[] = "this is a string\n";
   fprintf( stream, "%s", s );
   fclose( stream );
}

// test1Main.cpp
// compile with: /MD test1dll.lib
#include <stdio.h>
#include <process.h>
void writeFile(FILE *stream);

int main(void)
{
   FILE  * stream;
   errno_t err = fopen_s( &stream, "fprintf.out", "w" );
   writeFile(stream);
   system( "type fprintf.out" );
}

this is a string

This example passes environment variables across a DLL boundary.

// test2Dll.cpp
// compile with: /MT /LD
#include <stdio.h>
#include <stdlib.h>

__declspec(dllexport) void readEnv()
{
   char *libvar;
   size_t libvarsize;

   /* Get the value of the MYLIB environment variable. */ 
   _dupenv_s( &libvar, &libvarsize, "MYLIB" );

   if( libvar != NULL )
      printf( "New MYLIB variable is: %s\n", libvar);
   else
      printf( "MYLIB has not been set.\n");
   free( libvar );
}

// test2Main.cpp
// compile with: /MT /link test2dll.lib
#include <stdlib.h>
#include <stdio.h>

void readEnv();

int main( void )
{
   _putenv( "MYLIB=c:\\mylib;c:\\yourlib" );
   readEnv();
}

MYLIB has not been set.

If both the DLL and .exe file are built with /MD so that only one copy of the CRT is used, the program runs successfully and produces the following output:

New MYLIB variable is: c:\mylib;c:\yourlib

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值