Main和WinMain函数执行前,系统进行的运行时裤的执行代码分析

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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值