IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    C/C++编写的程序启动过程

    远行发表于 2016-01-16 09:21:46
    love 0

    记得以前学汇编和PE文件的时候知道,系统不会直接调用我们编写的main,而是调用指定的入口地址。实际上这个入口地址,是在链接时候指定的,MS C/C++中使用链接命令/entry:function可以修改默认设置。

    那么,默认情况下,我们使用VC编写的应用程序使用的是什么入口函数了?

    函数 默认
    mainCRTStartup (or wmainCRTStartup) An application using /SUBSYSTEM:CONSOLE; calls main (or wmain)
    WinMainCRTStartup (or wWinMainCRTStartup) An application using /SUBSYSTEM:WINDOWS; calls WinMain (or wWinMain), which must be defined with __stdcall

    注意,区分入口函数和主函数(main,WinMain)。

    默认情况下,控制台程序使用mainCRTStartup作为入口函数,窗口程序使用WinMainCRTStartup作为入口函数。同时,这两个函数都有对应的Unicode版本(前缀加w)。

    现在要考虑的是,这些启动函数都做了什么事情?

    在vs2008中的crtexe.c文件中可以找到这几个启动函数的定义,如下:

    #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
            )
    {
            /*
             * 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 __tmainCRTStartup();
    }
    
    __declspec(noinline)
    int
    __tmainCRTStartup(
            void
            )
    {
    #ifdef _WINMAIN_
            _TUCHAR *lpszCommandLine;
            STARTUPINFO StartupInfo;
            BOOL inDoubleQuote=FALSE;
    
            __try {
                            /*
                            Note: MSDN specifically notes that GetStartupInfo returns no error, and throws unspecified SEH if it fails, so
                            the very general exception handler below is appropriate
                            */
                GetStartupInfo( &StartupInfo );
            } __except(EXCEPTION_EXECUTE_HANDLER) {
                return 255;
            }
    #endif  /* _WINMAIN_ */
    
    
            /*
             * Guard the initialization code and the call to user's main, or
             * WinMain, function in a __try/__except statement.
             */
    
            __try
            {
                /*
                 * There is a possiblity that the module where this object is
                 * linked into is a mixed module. In all the cases we gurantee that
                 * native initialization will occur before managed initialization.
                 * Also in anycase this code should never be called when some other
                 * code is initializing native code, that's why we exit in that case.
                 *
                 * Do runtime startup initializers.
                 *
                 * Note: the only possible entry we'll be executing here is for
                 * __lconv_init, pulled in from charmax.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 exported from the CRT DLL.
                 *
                 * Note further that, when using the CRT DLL, executing the
                 * .CRT$XI* initializers is only done 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 that of any DLL.
                 */
                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)
                {
                    if(lock_free==fiberid)
                    {
                        nested=TRUE;
                        break;
                    }
    
                    /* some other thread is running native startup/shutdown during a cctor/domain unload.
                        Should only happen if this DLL 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);
                }
    
                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;
                    }
    #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 warning C4312 */
    #pragma warning(push)
    #pragma warning(disable:4312)
                    InterlockedExchangePointer((volatile PVOID *)&__native_startup_lock, 0);
    #pragma warning(pop)
                }
    
                /*
                 * If we have any dynamically initialized __declspec(thread)
                 * variables, then invoke their initialization for the primary
                 * thread used to start the process, by calling __dyn_tls_init
                 * through a callback defined in tlsdyn.obj.
                 */
                if (__dyn_tls_init_callback != NULL &&
                    _IsNonwritableInCurrentImage((PBYTE)&__dyn_tls_init_callback))
                {
                    __dyn_tls_init_callback(NULL, DLL_THREAD_ATTACH, NULL);
                }
    
                /* 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
                /* OS may not support "W" flavors */
                if (_wcmdln == NULL)
                    return 255;
                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
                          );
    #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;
    }
    


沪ICP备19023445号-2号
友情链接