`
driftcloudy
  • 浏览: 130739 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

从Entry Point到main函数调用(1)

    博客分类:
  • C
阅读更多

(1)   

之前在调试exe时感觉很奇怪,为什么Entry Point并非直接进入到main函数。

举例来说,如果将一段空的C代码build为exe:

void main(){ }

编译环境为:VC6 release。

再将该exe文件进行反汇编,那么从EP开始的代码部分大概形如:

ASM代码
push ebp
……
一段汇编代码
……
call Main函数
……
另一段汇编代码
……
retn

 

也就是说, 在执行一个exe文件时,总是要先运行一些指令,才能够开始调用Main函数。同样,当main函数执行完毕后,还需要运行一些指令完成收尾。为了弄清楚main函数调用前这些代码以及main函数执行后的代码,需要从CRT(C RunTime ,C的运行时库)开始研究。

 

(2)

Visual Studio自带了CRT的源码,VC6中CRT位于“VC98\CRT\SRC”目录。CRT 中的 crt0.c 文件规定了一整套C程序固定的执行流程。在 crt0.c 开头的注释部分有如下描述:

This the actual startup routine for apps. It calls the user's main routine [w]main() or [w]WinMain after performing C Run-Time Library initialization.

大概意思是,当C Run-Time Library 完成了初始化工作之后,才开始执行用户自定义的main 函数、WinMain 函数。

 

crt0.c 为了规定C程序执行的流程,定义了函数mainCRTStartupWinMainCRTStartup ,这两个函数也被称作启动函数 。它们的作用在函数注释中已经写的很清楚:

This routine does the C runtime initialization, calls main(), and then exits.

 

mainCRTStartup函数大概形如:

void mainCRTStartup(void){
	int mainret;
	……
	__try {
		……
		mainret = main(__argc, __argv, _environ); //在这里调用用户写的main函数 
		exit(mainret);
	}
	__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
	{
		_exit( GetExceptionCode() );
	}
}

当Windows系统执行一个C程序时,真正首先执行的是(win)mainCRTStartup函数。mainCRTStartup首先进行了一系列准备工作,例如heap的初始化、IO的初始化、获得命令行参数等等。当所有的准备工作都完成之后,再去调用用户自定义的main函数。最后,执行exit函数退出程序。因此对于exe,(win)mainCRTStartup函数才是真正的Entry point。

 

另外,crt0.c 中还有相似的函数:wWinMainCRTStartup、wmainCRTStartup,它们是Unicode版本程序的EP,这里可以暂时不用去管。windows为了照顾Unicode程序,很多API都提供了两种版本,一种是针对ANSI字符,还有一种是针对Unicode字符。

 

这四个函数是放在一起定义的,crt0.c 中的源码如下:

#ifdef _WINMAIN_                   /* _WINMAIN_被定义时,表示GUI程序 */

#ifdef WPRFLAG                     /* WPRFLAG被定义时,表示Unicode字符 */
void wWinMainCRTStartup(
#else 
void WinMainCRTStartup(
#endif

#else                              /* 下面为CUI程序 */

#ifdef WPRFLAG
void wmainCRTStartup(
#else 
void mainCRTStartup(
#endif 

#endif

void){
……
}

可以根据上面的源代码总结如下:

mainCRTStartup Console apps ANSI
wmainCRTStartup Console apps Unicode
WinMainCRTStartup Windows apps ANSI
wWinMainCRTStartup Windows apps Unicode

 

(3)

来具体看一下(win)mainCRTStartup函数。下面将mainCRTStartup函数的主要语句摘录了出来,这里去除了一些条件编译的代码,忽略了windows apps(_WINMAIN_)、Unicode版本的程序(WPRFLAG)、多线程(_MT),仅仅分析Console apps。

int mainret;

// 获取Win32的版本
_osver = GetVersion();
_winminor = (_osver >> 8) & 0x00FF ;
_winmajor = _osver & 0x00FF ;
_winver = (_winmajor << 8) + _winminor;
_osver = (_osver >> 16) & 0x00FFFF ;

// 创建了一个属于该进程的私有堆
if ( !_heap_init(0) )
    fast_error_exit(_RT_HEAPINIT);


__try {
    // 初始化低级IO
    _ioinit();

    // 获取命令行缓冲区指针
    _acmdln = (char *)GetCommandLineA();

    // 获取环境变量指针
    _aenvptr = (char *)__crtGetEnvironmentStringsA();
    
    // 设置argv参数
    _setargv();

    // 设置环境变量
    _setenvp();

    // 初始化C数据
    _cinit();

    __initenv = _environ;

    // 调用main函数
    mainret = main(__argc, __argv, _environ);
    exit(mainret);
}
__except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )
{
    _exit( GetExceptionCode() );
}

 

从这段代码可以大体窥视出 C 程序的启动流程:

1.获取WIN32平台的版本

2.调用_heap_init函数创建一个私有堆

3.初始化低级IO
4.获取命令行缓冲区、环境变量的指针

5.设置命令行参数与环境变量

6.初始化C数据

7.调用main函数

8.将main函数的调用结果传入exit 退出程序

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics