Win32入口函数main或者WinMain被调用前,系统先利用Kernel32.dll来调用mainCRTStartup()函数,这个函数在crtexe.c中被实现,代码如下:
intmainCRTStartup()
{
/*
* The /GS security cookie must beinitialized before any exception
* handling targetting the currentimage is registered. No function
* using exception handling can becalled in the current image until
* after __security_init_cookie hasbeen called.
*/
//下面这个函数,为程序的异常处理,指定默认处理方式。另外还启动程序缓冲区的写检查功能,防止程序对缓冲区外的内存进行写入操作。
/*The global security cookieis used for buffer overrun protection in code compiled with /GS(Buffer Security Check) and in code that uses exception handling.Essentially, on entry to an overrun-protected function, the cookie is put onthe stack, and on exit, the value on the stack is compared against the globalcookie. Any difference between them indicates that a buffer overrun hasoccurred and results in immediate termination of the program.*/
__security_init_cookie();
return __tmainCRTStartup();
}
__declspec(noinline) int __tmainCRTStartup( )
{
//如果入口函数式WinMain:
#ifdef_WINMAIN_
_TUCHAR *lpszCommandLine;
STARTUPINFO StartupInfo;
BOOL inDoubleQuote=FALSE;
__try {
/*
为什么GetStartupInfo函数要放在__try内,因为这个函数不会反悔失败,但在出问题时,会抛出失败的异常。
*/
GetStartupInfo( &StartupInfo );
} __except(EXCEPTION_EXECUTE_HANDLER) {
return255;
}
#endif /* _WINMAIN_ */
/*要求在__try块内调用main或者WinMain函数。而对于程序初始化的代码,必须进行保护,使得程序的初始化代码在整个机器中,只能有一个实例在运行,也就是说,一个系统,同一时间,只能有一个程序处于初始化状态(Initializing state)
*/
__try
{
/*
* There is a possiblity that themodule where this object is
* linked into is a mixed module.In all the cases we gurantee that
* native initialization will occurbefore managed initialization.
* Also in anycase this code shouldnever be called when some other
* code is initializing nativecode, that's why we exit in that case.
*
* Do runtime startup initializers.
*
* Note: the only possible entrywe'll be executing here is for
* __lconv_init, pulled in fromcharmax.obj only if the EXE was
* compiled with -J. All other .CRT$XI* initializers are only
* run as part of the CRT itself,and so for the CRT DLL model
* are not found in the EXE. For that reason, we call _initterm,
* not _initterm_e, because__lconv_init will never return failure,
* and _initterm_e is not exportedfrom the CRT DLL.
*
* Note further that, when usingthe CRT DLL, executing the
* .CRT$XI* initializers is onlydone for an EXE, not for a DLL
* using the CRT DLL. That is to make sure the -J setting for
* the EXE is not overriden by thatof any DLL.
*/
void*lock_free=0;
/*返回指向线程环境块的指针,最终返回的是线程栈的基地址,该结构如下:
typedef struct _TEB{
BYTE Reserved1[1952];
PVOID Reserved2[412];
PVOID TlsSlots[64];//指向线程本地存储器的指针,
BYTE Reserved3[8];
PVOID Reserved4[26];
PVOID ReservedForOle;
PVOID Reserved5[4];
PVOID TlsExpansionSlots;// 指向线程本地存储器扩展区域的指针
} TEB, *PTEB;
注意:在该结构中,没有发现StackBase成员,那么我们看看这个结构被强制转换成的NT_TIB结构:如下
NT_TIB结构在windbg中调试如下:
kd> dt nt!_NT_TIB
+0x000ExceptionList : Ptr32_EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
+0x00c SubSystemTib : Ptr32 Void
+0x010 FiberData : Ptr32 Void
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32Void
+0x018 Self : Ptr32 _NT_TIB
这说明,TEB的第一个数组中,实际包含了这个值!
*/
void*fiberid=((PNT_TIB)NtCurrentTeb())->StackBase;
intnested=FALSE;
//使用一个原子操作函数,实现同一个系统内,初始化工作的同步排斥,同一时//间,只能有一个程序的初始化工作在系统中执行,__native_startup_lock如果不为0,那么说明有其他线程在进行原始的初始化工作(while循环不断执行,进行判断),如果为0,则表明本线程的原始初始化工作可以进行,while循环将不再执行。 while((lock_free=InterlockedCompareExchangePointer((volatile PVOID *)&__native_startup_lock, fiberid,0))!=0)
{
//如果fiberid为0,那么嵌套为真,接着往下执行。否则,
if(lock_free==fiberid)
{
nested=TRUE;
break;
}
/*some other thread is running native startup/shutdown during a cctor/domainunload.
Should only happen if thisDLL was built using the Everett-compat loader lock fix in vcclrit.h
*/
/*wait for the other thread to complete init before we return */
Sleep(1000);
}
//将程序启动状态设置为初始化执行状态,并准备函数跳转表,然后再执行C++全局类实例的构造函数,将全局类实例化。
if(__native_startup_state == __initializing)
{
_amsg_exit(_RT_CRT_INIT_CONFLICT);
}
elseif (__native_startup_state == __uninitialized)
{
__native_startup_state =__initializing;
#ifndef_SYSCRT
if(_initterm_e( __xi_a, __xi_z ) != 0)
{
return255;
}
#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 );
__native_startup_state =__initialized;
}
_ASSERTE(__native_startup_state ==__initialized);
if(!nested)
{
/*For X86, the definition of InterlockedExchangePointer wrongly causes warningC4312 */
//InterlockedExchangePoineter函数退出关键代码,使得初始化工作可以并发执行。
#pragma warning(push)
#pragma warning(disable:4312)
InterlockedExchangePointer((volatile PVOID *)&__native_startup_lock, 0);
#pragma warning(pop)
}
/*
* If we have any dynamicallyinitialized __declspec(thread)
* variables, then invoke theirinitialization for the primary
* thread used to start theprocess, by calling __dyn_tls_init
* through a callback defined intlsdyn.obj.
*/
//如果线程有动态变量,执行动态初始化工作。
if(__dyn_tls_init_callback != NULL &&
_IsNonwritableInCurrentImage((PBYTE)&__dyn_tls_init_callback))
{
__dyn_tls_init_callback(NULL,DLL_THREAD_ATTACH, NULL);
}
/* Enablebuffer count checking if linking against static lib */
//如果是静态连接,执行缓冲区计数检查
_CrtSetCheckCount(TRUE);
//以下的程序,准备调用WinMain或者main函数,这就是我们自己写的函数了。
#ifdef_WINMAIN_
/*
* Skip past program name (firsttoken in command line).
* Check for and handle quotedprogram name.
*/
#ifdefWPRFLAG
/* OS maynot support "W" flavors */
if(_wcmdln == NULL)
return255;
lpszCommandLine = (wchar_t *)_wcmdln;
#else /* WPRFLAG */
lpszCommandLine = (unsigned char*)_acmdln;
#endif /* WPRFLAG */
while(*lpszCommandLine > SPACECHAR ||
(*lpszCommandLine&&inDoubleQuote)) {
/*
* Flip the count from 1 to 0or 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 spacepreceeding the second token.
*/
while(*lpszCommandLine && (*lpszCommandLine <= SPACECHAR)) {
lpszCommandLine++;
}
#ifdefWPRFLAG
mainret = wWinMain(
#else /* WPRFLAG */
mainret = WinMain(
#endif /* WPRFLAG */
(HINSTANCE)&__ImageBase,
NULL,
lpszCommandLine,
StartupInfo.dwFlags& STARTF_USESHOWWINDOW
?StartupInfo.wShowWindow
: SW_SHOWDEFAULT
);
#else /* _WINMAIN_ */
#ifdefWPRFLAG
__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 managedapp, we don't really need to
* call exit or _c_exit. .cctorshould 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 managedapp, we don't
Win32入口函数main或者WinMain被调用前,系统先利用Kernel32.dll来调用mainCRTStartup()函数,这个函数在crtexe.c中被实现,代码如下:
intmainCRTStartup()
{
/*
* The /GS security cookie must beinitialized before any exception
* handling targetting the currentimage is registered. No function
* using exception handling can becalled in the current image until
* after __security_init_cookie hasbeen called.
*/
//下面这个函数,为程序的异常处理,指定默认处理方式。另外还启动程序缓冲区的写保护功能,防止程序对缓冲区外的内存进行写入操作。
/*The global security cookieis used for buffer overrun protection in code compiled with /GS(Buffer Security Check) and in code that uses exception handling.Essentially, on entry to an overrun-protected function, the cookie is put onthe stack, and on exit, the value on the stack is compared against the globalcookie. Any difference between them indicates that a buffer overrun hasoccurred and results in immediate termination of the program.*/
__security_init_cookie();
return __tmainCRTStartup();
}
__declspec(noinline) int __tmainCRTStartup( )
{
//如果入口函数式WinMain:
#ifdef_WINMAIN_
_TUCHAR *lpszCommandLine;
STARTUPINFO StartupInfo;
BOOL inDoubleQuote=FALSE;
__try {
/*
为什么GetStartupInfo函数要放在__try内,因为这个函数不会反悔失败,但在出问题时,会抛出失败的异常。
*/
GetStartupInfo( &StartupInfo );
} __except(EXCEPTION_EXECUTE_HANDLER) {
return255;
}
#endif /* _WINMAIN_ */
/*要求在__try块内调用main或者WinMain函数。而对于程序初始化的代码,必须进行保护,使得程序的初始化代码在整个机器中,只能有一个实例在运行,也就是说,一个系统,同一时间,只能有一个程序处于初始化状态(Initializing state)
*/
__try
{
/*
* There is a possiblity that themodule where this object is
* linked into is a mixed module.In all the cases we gurantee that
* native initialization will occurbefore managed initialization.
* Also in anycase this code shouldnever be called when some other
* code is initializing nativecode, that's why we exit in that case.
*
* Do runtime startup initializers.
*
* Note: the only possible entrywe'll be executing here is for
* __lconv_init, pulled in fromcharmax.obj only if the EXE was
* compiled with -J. All other .CRT$XI* initializers are only
* run as part of the CRT itself,and so for the CRT DLL model
* are not found in the EXE. For that reason, we call _initterm,
* not _initterm_e, because__lconv_init will never return failure,
* and _initterm_e is not exportedfrom the CRT DLL.
*
* Note further that, when usingthe CRT DLL, executing the
* .CRT$XI* initializers is onlydone for an EXE, not for a DLL
* using the CRT DLL. That is to make sure the -J setting for
* the EXE is not overriden by thatof any DLL.
*/
void*lock_free=0;
/*返回指向线程环境块的指针,最终返回的是线程栈的基地址,该结构如下:
typedef struct _TEB{
BYTE Reserved1[1952];
PVOID Reserved2[412];
PVOID TlsSlots[64];//指向线程本地存储器的指针,
BYTE Reserved3[8];
PVOID Reserved4[26];
PVOID ReservedForOle;
PVOID Reserved5[4];
PVOID TlsExpansionSlots;// 指向线程本地存储器扩展区域的指针
} TEB, *PTEB;
注意:在该结构中,没有发现StackBase成员,那么我们看看这个结构被强制转换成的NT_TIB结构:如下
NT_TIB结构在windbg中调试如下:
kd> dt nt!_NT_TIB
+0x000ExceptionList : Ptr32_EXCEPTION_REGISTRATION_RECORD
+0x004 StackBase : Ptr32 Void
+0x008 StackLimit : Ptr32 Void
+0x00c SubSystemTib : Ptr32 Void
+0x010 FiberData : Ptr32 Void
+0x010 Version : Uint4B
+0x014 ArbitraryUserPointer : Ptr32Void
+0x018 Self : Ptr32 _NT_TIB
这说明,TEB的第一个数组中,实际包含了这个值!
*/
void*fiberid=((PNT_TIB)NtCurrentTeb())->StackBase;
intnested=FALSE;
//使用一个原子操作函数,实现同一个系统内,初始化工作的同步排斥,同一时间,只能有一个程序的初始化工作在系统中执行,__native_startup_lock如果不为0,那么说明有其他线程在进行原始的初始化工作(while循环不断执行,进行判断),如果为0,则表明本线程的原始初始化工作可以进行,while循环将不再执行。
while((lock_free=InterlockedCompareExchangePointer((volatile PVOID *)&__native_startup_lock, fiberid,0))!=0)
{
//如果fiberid为0,那么嵌套为真,退出while循环,接着往下执行,这里笔者猜测,可能和Dll的加载有关,不知道对不对,这是只是判断是否是进行DLL加载。
if(lock_free==fiberid)
{
nested=TRUE;
break;
}
/*some other thread is running native startup/shutdown during a cctor/domainunload.
Should only happen if thisDLL was built using the Everett-compat loader lock fix in vcclrit.h
*/
/*wait for the other thread to complete init before we return */
Sleep(1000);
}
//将程序启动状态设置为初始化执行状态,并准备函数跳转表,然后再执行C++全局类实例的构造函数,将全局类实例化。
if(__native_startup_state == __initializing)
{
_amsg_exit(_RT_CRT_INIT_CONFLICT);
}
elseif (__native_startup_state == __uninitialized)
{
__native_startup_state =__initializing;
#ifndef_SYSCRT
if(_initterm_e( __xi_a, __xi_z ) != 0)
{
return255;
}
#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 );
__native_startup_state =__initialized;
}
_ASSERTE(__native_startup_state ==__initialized);
if(!nested)
{
/*For X86, the definition of InterlockedExchangePointer wrongly causes warningC4312 */
//InterlockedExchangePoineter函数退出关键代码,使得初始化工作可以并发执行。
#pragma warning(push)
#pragma warning(disable:4312)
InterlockedExchangePointer((volatile PVOID *)&__native_startup_lock, 0);
#pragma warning(pop)
}
/*
* If we have any dynamicallyinitialized __declspec(thread)
* variables, then invoke theirinitialization for the primary
* thread used to start theprocess, by calling __dyn_tls_init
* through a callback defined intlsdyn.obj.
*/
//如果线程有动态变量,执行动态初始化工作。
if(__dyn_tls_init_callback != NULL &&
_IsNonwritableInCurrentImage((PBYTE)&__dyn_tls_init_callback))
{
__dyn_tls_init_callback(NULL,DLL_THREAD_ATTACH, NULL);
}
/* Enablebuffer count checking if linking against static lib */
//如果是静态连接,执行缓冲区计数检查
_CrtSetCheckCount(TRUE);
//以下的程序,准备调用WinMain或者main函数,这就是我们自己写的函数了。
#ifdef_WINMAIN_
/*
* Skip past program name (firsttoken in command line).
* Check for and handle quotedprogram name.
*/
#ifdefWPRFLAG
/* OS maynot support "W" flavors */
if(_wcmdln == NULL)
return255;
lpszCommandLine = (wchar_t *)_wcmdln;
#else /* WPRFLAG */
lpszCommandLine = (unsigned char*)_acmdln;
#endif /* WPRFLAG */
while(*lpszCommandLine > SPACECHAR ||
(*lpszCommandLine&&inDoubleQuote)) {
/*
* Flip the count from 1 to 0or 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 spacepreceeding the second token.
*/
while(*lpszCommandLine && (*lpszCommandLine <= SPACECHAR)) {
lpszCommandLine++;
}
#ifdefWPRFLAG
mainret = wWinMain(
#else /* WPRFLAG */
mainret = WinMain(
#endif /* WPRFLAG */
(HINSTANCE)&__ImageBase,
NULL,
lpszCommandLine,
StartupInfo.dwFlags& STARTF_USESHOWWINDOW
?StartupInfo.wShowWindow
: SW_SHOWDEFAULT
);
#else /* _WINMAIN_ */
#ifdefWPRFLAG
__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 managedapp, we don't really need to
* call exit or _c_exit. .cctorshould 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 managedapp, we don't really need to
* call exit or _c_exit. .cctorshould be able to take care of
* this.
*/
if( !managedapp )
_exit(mainret);
if(has_cctor == 0)
_cexit();
} /* end of try - except */
return mainret;
}
really need to
* call exit or _c_exit. .cctorshould be able to take care of
* this.
*/
if( !managedapp )
_exit(mainret);
if(has_cctor == 0)
_cexit();
} /* end of try - except */
return mainret;
}