前面看到在使用静态运行库的EXE的启动代码做了很多的工作,下面再来看看使用动态库的EXE的启动代码
使用/mtd选项生产一个EXE,在我们的main函数开头下断,查看调用堆栈可以看到程序在被系统加载完成之后,我们程序的入口由Crtexe.c文件中的__tmainCRTStartup开始
下面是代码
static
int
__tmainCRTStartup(
void
);
#ifdef _WINMAIN_
#ifdef WPRFLAG
int wWinMainCRTStartup(
#else /* WPRFLAG */
int WinMainCRTStartup(
#endif /* WPRFLAG */
#else /* _WINMAIN_ */
#ifdef WPRFLAG
int wmainCRTStartup(
#else /* WPRFLAG */
int mainCRTStartup(
#endif /* WPRFLAG */
#endif /* _WINMAIN_ */
void
)
{
__security_init_cookie();
return __tmainCRTStartup();
}
跟使用静态运行库时一样的 同样先是创建了一个cookier然后 四种情况下调用的是同一个函数__tmainCRTStartup
直接上代码 在代码中注释,相比使用静态库,使用动态库的启动代码只完成了很有限的工作。
__declspec(noinline)
int
__tmainCRTStartup(
void
)
{
#ifdef _WINMAIN_
_TUCHAR *lpszCommandLine;
STARTUPINFOW StartupInfo;
BOOL inDoubleQuote=FALSE;
GetStartupInfoW( &StartupInfo );
#endif /* _WINMAIN_ */
#ifdef _M_IX86
/*
* Enable app termination when heap corruption is detected on
* Windows Vista and above. This is a no-op on down-level OS's
* and enabled by default for 64-bit processes.
*/
//在XPSP3 VISTA 及之后的版本中增加了一个Heap管理特性,当Heap管理器发现进程中有任何使用中的Heap出现错误的时候,系统终止进程
if (!_NoHeapEnableTerminationOnCorruption) //如果该全局标准为0表示 需要开启该特性
{
HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); //设置开启
}
#endif /* _M_IX86 */
/*
__try //
{
void *lock_free=0;
void *fiberid=((PNT_TIB)NtCurrentTeb())->StackBase; 获取栈基址
int nested=FALSE;
while((lock_free=InterlockedCompareExchangePointer((volatile PVOID *)&__native_startup_lock, fiberid, 0))!=0)
{ //如果__native_startup_lock为0 把fiberid的值给它
if(lock_free==fiberid)
{
nested=TRUE;
break;
}
Sleep(1000);
}
if (__native_startup_state == __initializing) //在混合模式的情况下如果其他本地地方在初始化本地代码,那么出错,不能同时初始化
{
_amsg_exit( _RT_CRT_INIT_CONFLICT);
}
else if (__native_startup_state == __uninitialized)
{
__native_startup_state = __initializing;
#ifndef _SYSCRT
if (_initterm_e( __xi_a, __xi_z ) != 0)
{
return 255; //初始化C全局对象
}
#else /* _SYSCRT */
_initterm((_PVFV *)(void *)__xi_a, (_PVFV *)(void *)__xi_z);
#endif /* _SYSCRT */
}
else
{
has_cctor = 1;
}
/*
* do C++ constructors (initializers) specific to this EXE
*/
if (__native_startup_state == __initializing)
{
_initterm( __xc_a, __xc_z ); //初始化C++全局对象
__native_startup_state = __initialized;
}
_ASSERTE(__native_startup_state == __initialized);
if(!nested)
{
/* For X86, the definition of InterlockedExchangePointer wrongly causes warning C4312 */
#pragma warning(push)
#pragma warning(disable:4312)
InterlockedExchangePointer((volatile PVOID *)&__native_startup_lock, 0);
#pragma warning(pop)
}
*/
if (__dyn_tls_init_callback != NULL &&
_IsNonwritableInCurrentImage((PBYTE)&__dyn_tls_init_callback))
{
__dyn_tls_init_callback(NULL, DLL_THREAD_ATTACH, NULL); //初始化TLS相关的
}
/* Enable buffer count checking if linking against static lib */
_CrtSetCheckCount(TRUE);
#ifdef _WINMAIN_
/*
* Skip past program name (first token in command line).
* Check for and handle quoted program name.
*/
#ifdef WPRFLAG
lpszCommandLine = (wchar_t *)_wcmdln;
#else /* WPRFLAG */
lpszCommandLine = (unsigned char *)_acmdln;
#endif /* WPRFLAG */
while (*lpszCommandLine > SPACECHAR ||
(*lpszCommandLine&&inDoubleQuote)) {
/*
* Flip the count from 1 to 0 or 0 to 1 if current character
* is DOUBLEQUOTE
*/
if (*lpszCommandLine==DQUOTECHAR) inDoubleQuote=!inDoubleQuote;
#ifdef _MBCS
if (_ismbblead(*lpszCommandLine)) {
if (lpszCommandLine) {
lpszCommandLine++;
}
}
#endif /* _MBCS */
++lpszCommandLine;
}
/*
* Skip past any white space preceeding the second token.
*/
while (*lpszCommandLine && (*lpszCommandLine <= SPACECHAR)) {
lpszCommandLine++;
} //去掉命令行参数中程序自身路径的部分
#ifdef WPRFLAG
mainret = wWinMain(
#else /* WPRFLAG */
mainret = WinMain(
#endif /* WPRFLAG */
(HINSTANCE)&__ImageBase,
NULL,
lpszCommandLine,
StartupInfo.dwFlags & STARTF_USESHOWWINDOW
? StartupInfo.wShowWindow
: SW_SHOWDEFAULT //调用我们的main函数
);
#else /* _WINMAIN_ */
#ifdef WPRFLAG
__winitenv = envp;
mainret = wmain(argc, argv, envp);
#else /* WPRFLAG */
__initenv = envp;
mainret = main(argc, argv, envp);
#endif /* WPRFLAG */
#endif /* _WINMAIN_ */
/*
* Note that if the exe is managed app, we don't really need to
* call exit or _c_exit. .cctor should be able to take care of
* this.
*/
if ( !managedapp )
exit(mainret);
if (has_cctor == 0)
_cexit(); //退出代码,
}
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
{
/* //发生异常调用退出函数 终止进程
* Should never reach here
*/
mainret = GetExceptionCode();
/*
* Note that if the exe is managed app, we don't really need to
* call exit or _c_exit. .cctor should be able to take care of
* this.
*/
if ( !managedapp )
_exit(mainret);
if (has_cctor == 0)
_cexit();
} /* end of try - except */
return mainret;
}
使用动态库的EXE的启动代码 除了 初始化全局对象外,并没有做其他实质性的初始化工作,然后就调用了main函数 将执行权限交给了我们
因此可以推断对CRT的初始化工作,运行库的DLL文件中,当Msvcrtxx.DLL被加载到后,DLL的启动代码完成了之前我们分析的,使用静态库的exe的启动代码所做的工作。
于是我在VC\crt\src 下搜索包含_ioinit的文件,发现crtlib.c文件中的_CRTDLL_INIT 函数调用了这个初始化io的函数
__CRTDLL_INIT 开始地方下断 果然在这里
代码
BOOL WINAPI
_CRTDLL_INIT(
HANDLE hDllHandle,
DWORD dwReason,
LPVOID lpreserved
)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
/*
* The /GS security cookie must be initialized before any exception
* handling targetting the current image is registered. No function
* using exception handling can be called in the current image until
* after __security_init_cookie has been called.
*/
__security_init_cookie();
}
return __CRTDLL_INIT(hDllHandle, dwReason, lpreserved);
很明显就是这个运行库DLL的启动代码了 跟EXE的如出一辙
__declspec(noinline)
BOOL __cdecl
__CRTDLL_INIT(
HANDLE hDllHandle,
DWORD dwReason,
LPVOID lpreserved
)
{
if ( dwReason == DLL_PROCESS_ATTACH ) {
if ( !_heap_init() ) /* initialize heap */ //分配堆
return FALSE;
if(!_mtinit()) /* initialize multi-thread */ 多线程支持
{
_heap_term(); /* heap is now invalid! */
return FALSE; /* fail DLL load on failure */
}
if (_ioinit() < 0) { /* inherit file info */ 初始IO
_mtterm();
_heap_term(); /* heap is now invalid! */
return FALSE; /* fail DLL load on failure */
}
_aenvptr = (char *)__crtGetEnvironmentStringsA(); 获取环境变量
_acmdln = GetCommandLineA();
_wcmdln = GetCommandLineW();
#ifdef _MBCS
__initmbctable();
#endif /* _MBCS */
if (_setenvp() < 0 || /* get environ info */
_cinit(FALSE) != 0) /* do C data initialize */
{
_ioterm(); /* shut down lowio */
_mtterm(); /* free TLS index, call _mtdeletelocks() */
_heap_term(); /* heap is now invalid! */
return FALSE; /* fail DLL load on failure */
}
proc_attached++;
}
else if ( dwReason == DLL_PROCESS_DETACH ) {
.................................. 清理工作省略
}
else if ( dwReason == DLL_THREAD_ATTACH )
{...........................................清理工作省略
}
return TRUE;
}
一切都是那么的熟悉有木有,所有的初始化工作这个DLL都给做了,EXE安心的调用它提供的库函数就OK了