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

(1)   

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

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

C代码   收藏代码
  1. 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程序执行的流程,定义了函数mainCRTStartup 和WinMainCRTStartup ,这两个函数也被称作启动函数 。它们的作用在函数注释中已经写的很清楚:

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

 

mainCRTStartup函数大概形如:

C代码   收藏代码
  1. void mainCRTStartup(void){  
  2.     int mainret;  
  3.     ……  
  4.     __try {  
  5.         ……  
  6.         mainret = main(__argc, __argv, _environ); //在这里调用用户写的main函数   
  7.         exit(mainret);  
  8.     }  
  9.     __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )  
  10.     {  
  11.         _exit( GetExceptionCode() );  
  12.     }  
  13. }  

当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 中的源码如下:

Cpp代码   收藏代码
  1. #ifdef _WINMAIN_                   /* _WINMAIN_被定义时,表示GUI程序 */  
  2.   
  3. #ifdef WPRFLAG                     /* WPRFLAG被定义时,表示Unicode字符 */  
  4. void wWinMainCRTStartup(  
  5. #else   
  6. void WinMainCRTStartup(  
  7. #endif  
  8.   
  9. #else                              /* 下面为CUI程序 */  
  10.   
  11. #ifdef WPRFLAG  
  12. void wmainCRTStartup(  
  13. #else   
  14. void mainCRTStartup(  
  15. #endif   
  16.   
  17. #endif  
  18.   
  19. void){  
  20. ……  
  21. }  

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

mainCRTStartupConsole appsANSI
wmainCRTStartupConsole appsUnicode
WinMainCRTStartupWindows appsANSI
wWinMainCRTStartupWindows appsUnicode

 

(3)

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

C代码   收藏代码
  1. int mainret;  
  2.   
  3. // 获取Win32的版本  
  4. _osver = GetVersion();  
  5. _winminor = (_osver >> 8) & 0x00FF ;  
  6. _winmajor = _osver & 0x00FF ;  
  7. _winver = (_winmajor << 8) + _winminor;  
  8. _osver = (_osver >> 16) & 0x00FFFF ;  
  9.   
  10. // 创建了一个属于该进程的私有堆  
  11. if ( !_heap_init(0) )  
  12.     fast_error_exit(_RT_HEAPINIT);  
  13.   
  14.   
  15. __try {  
  16.     // 初始化低级IO  
  17.     _ioinit();  
  18.   
  19.     // 获取命令行缓冲区指针  
  20.     _acmdln = (char *)GetCommandLineA();  
  21.   
  22.     // 获取环境变量指针  
  23.     _aenvptr = (char *)__crtGetEnvironmentStringsA();  
  24.       
  25.     // 设置argv参数  
  26.     _setargv();  
  27.   
  28.     // 设置环境变量  
  29.     _setenvp();  
  30.   
  31.     // 初始化C数据  
  32.     _cinit();  
  33.   
  34.     __initenv = _environ;  
  35.   
  36.     // 调用main函数  
  37.     mainret = main(__argc, __argv, _environ);  
  38.     exit(mainret);  
  39. }  
  40. __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) )  
  41. {  
  42.     _exit( GetExceptionCode() );  
  43. }  

 

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

1.获取WIN32平台的版本

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

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

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

6.初始化C数据

7.调用main函数

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

 

转自:http://driftcloudy.iteye.com/blog/1048750

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值