(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程序执行的流程,定义了函数mainCRTStartup
和WinMainCRTStartup
,这两个函数也被称作启动函数
。它们的作用在函数注释中已经写的很清楚:
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 退出程序
分享到:
相关推荐
设置Entry Point的方法, 默认情况是main, 你可以再VC属性里面修改。
BookB_Entry_Point_Specification_v2_6
港股公司研究-西牛证券-家乡互动03798.HKAn attractive entry point of a cash cow
win10安装mysql报错The procedure entry point fesetround could not be located in the dynamic link library-附件资源
看说明后,修改配置,再双击批处理文件即可创建系统还原节点,还可以创建系统计划定期执行它来创建系统还原节点
慢慢的回味.entry_point–JVM Java栈桢的创建1
C#通过NCO组建,直接调用SAP BAPI接口。app.config中配置SAP连接信息,代码中加入调用函数。注意引用的DLL是针对64位操作系统的。
如qsort 等函数需要函数指针才能回调 用此函数库可以将成员函数指针转为普通函数指针 测试代码如下 #include #include #include #include #include #include using cmpfunc = int(__cdecl*)(const void*, ...
在Linux内核调试中,经常用到的打印函数调用堆栈的方法非常简单,只需在需要查看堆栈的函数中加入: dump_stack(); 或 __backtrace(); 即可 dump_stack()在~/kernel/ lib/Dump_stack.c中定义 void dump_stack(void)...
In the module entry point, create a linked list containing five struct birthday elements. Traverse the linked list and output its contents to the kernel log buffer. Invoke the dmesg command to ensure ...
比如用户想要在thread1里调用engOpen创建一个MatLab Engine,然后在thread2里利用创建的Engine来调用MatLab函数,这时会出现Engine函数调用无效,即使Engine指针仍然在thread2里正确存在着。 如下: Class ...
Data Entry ActiveX控件既可以作为控件被别的程序调用,也可以当做一个完整的应用程序来用;支持布尔运算,而且在用户输入数据时能自动搜索数据库之前输入的数据进行匹配比较供用户选择,大大加快了数据输入的速度;...
本文档以PDF格式,分类清晰,注解详细,从应用程序开发,到系统驱动开发,从进程,文件处理,到虚拟内存,即插即用,电源管理,注册表,NTFS文件格式,无所不包。总共分成21个大类,几千个没有公开的API函数的详细...
碰到了 #1062 – Duplicate entry ‘1′ for key ‘PRIMARY’ 当时那个急啊,原本的数据我已经全部删除了,没办法只有请求万能的百度了。我找了大半天终于给我给我找到了。兴奋ing,马上测试,O(∩_∩)O哈哈~成功了...
Android 报错:Entry name ‘AndroidManifest.xml’ collided前言解决方法1、直接退回3.5.32、排除打包文件3、使用旧打包工具4、最终解决方法完事 前言 这两天 Android studio 更新了 3.6 版本,天天在右下角提示 ...
《TCP-IP详解》共3卷,其他卷请到我空间下载,第2卷共分两个part,请下载完两个part后在解压。本书完整而详细地介绍了TCP/IP协议是如何实现的。书中给出了约500个图例,15 000行实际操作的C代码,采用举例教学的方法...
Thumb指令集, 非常详细