`
driftcloudy
  • 浏览: 132077 次
  • 性别: 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 退出程序

 

 

分享到:
评论

相关推荐

    windows设置Entry Point的方法

    请注意,改变Entry Point可能会影响到C++运行时库的初始化和清理过程,因此在进行此类更改时需要确保你的代码能够正确处理这些变化。如果自定义的入口点没有调用`mainCRTStartup`或`WinMainCRTStartup`,可能会导致...

    (12.2)--可执行文件的加载1

    3. 加载完成和执行main函数:加载完成后,将PC(EIP)设定指向Entry point(即符号_start处),最终执行main函数,以启动程序执行。 ELF头信息 ELF头信息是可执行文件的元数据,用于描述可执行文件的结构和组织...

    通过SMBios读UUID源码(C语言)

    这将允许你从命令行或其他应用程序接口调用`read_smbios_uuid`函数,从而获取系统的UUID信息。 总的来说,理解SMBIOS和UUID在系统管理中的作用,以及如何使用C语言来处理它们,是系统级编程和硬件诊断的重要技能。...

    fn-args-main-源码.rar

    2. **主入口点(Main Entry Point)** `main`通常指的是程序的主要执行入口。在`fn-args-main`中,`main`可能是库的核心功能,负责接收用户输入,处理参数,然后启动相应的功能。这个主入口点可能会调用一系列内部...

    OSE操作系统学习总结借鉴.pdf

    OSE 的全局变量与初始化OSE 的 Main 函数就调用一个函数start_OSE。在 start_OSE 函数中首先调用 odo_config_start_handler1 对系统的硬件进行初始化。再调用 odo_init_os 进行 OSE 操作系统初始化。odo_init_os 这...

    python 波形生成-26-管理系统框架之程序入口函数.ev4.rar

    通常,程序入口函数(entry point)是整个系统开始运行的地方,它负责调用不同模块,初始化系统,以及处理命令行参数。在Python中,`if __name__ == '__main__':` 是定义程序入口的常见方式,这样可以确保只有在直接...

    HOOK-IAT.rar_IAT_iat hook_pe iat_pe 入口点hook_入口点

    IAT Hook是一种代码注入技术,用于修改目标程序的IAT,将函数调用重定向到自定义的处理函数,而不是原始的函数实现。这通常用于调试、监控、安全检测或恶意软件行为。例如,一个常见的应用是防止病毒通过替换系统API...

    VC下Dll的创建以及使用

    显式调用则是通过运行时动态加载 DLL,并手动调用其中的函数。 - **加载 DLL**:使用 `LoadLibrary` 函数加载 DLL 文件。 - **获取函数地址**:使用 `GetProcAddress` 函数获取导出函数的地址。 - **释放 DLL**:...

    Windows以后台服务的形式启动程序

    3. **服务初始化函数(Service Entry Point)**:在创建服务时,操作系统会调用这个函数,通常是`WinMain()`或`DllMain()`。 4. **服务注册(Registering the Service)**:通过`CreateService()`函数将服务添加到...

    masm.rar_汇编 dos

    4. **堆栈段(Stack Segment)**:用于存储函数调用时的参数、返回地址等临时数据。 5. **程序入口点(Entry Point)**:程序开始执行的地方。 6. **DOS中断调用(DOS Integers)**:DOS提供了一系列中断调用来执行...

    11A_VenusFW_Customization.pdf

    3. **启动函数调用**:在`vtst_rt_my_first_app_launch`函数中调用实际的启动函数,以显示应用。 #### 三、定义应用类(Step3: Define the App Class: VappMyFirstApp) 定义应用的具体类,以便更好地管理和控制...

    VC6.0链接错误的一些解决办法

    1. **Windows子系统设置错误**:当你试图运行一个原本应该作为Windows应用程序的项目,但链接器设置为使用控制台子系统时,会遇到`unresolved external symbol _main`的错误。解决方法是进入项目的属性设置,选择...

    《你必须知道的495个C语言问题》

    可我找不到任何方法来声明这样的函数——感觉我需要一个返回指针的函数,返回的指针指向的又是返回指针的函数……,如此往复,以至无穷。 12  数组大小 13 1.23 能否声明和传入数组大小一致的局部数组,或者由...

    嵌入式+QT实验.docx

    - 需要在.pro文件中添加`QT += widgets`,以便能够在main函数中引入`QApplication`。 - **实验二:纯代码方式编写Hello World**:通过纯代码的方式构建QT程序,了解不同构建方法的区别。 - **实验三:多窗口程序**...

    C语言FAQ 常见问题列表

    o 5.8 我看到了用指针调用函数的不同语法形式。到底怎么回事? o 5.9 我怎样把一个 int 变量转换为 char * 型?我试了类型转换, 但是不行。 * 6. 空 (null) 指针 o 6.1 臭名昭著的空指针到底是什么? o 6.2 ...

    金盾提取脚本

    对于未经过封装的程序而言,OEP通常是程序的主函数(main函数)的地址。在Molebox封装的情况下,OEP被修改,因此需要通过特定的技术手段来恢复它。 ### 3. 脚本解析 #### 脚本流程概述 脚本首先检查当前环境是否...

    PE文件节区添加并弹出简单的对话框

    同时,可能还需要调整入口点(Entry Point)以便程序运行时调用对话框代码。这通常涉及到对PE文件的二进制级操作,需要谨慎处理,避免破坏原始文件的完整性。 在提供的压缩包文件6d17ec4037f449ee902613fec0d6cc12...

    C# for CSDN 乱七八糟的看不懂

    -128 到 127 -32,768 到 32,767 -2,147,483,648 2,147,483,647 -9,223,372,036,854,775,808 到 第1页 C#(WINFORM)学习 long val2 = 34L; 到 9,223,372,036,854,775,807 byte ushort 8 位无符号整型 16 位无符号...

    通过编程获取Linux文件系统使用的详细信息.pdf

    printf("Mount Point: %s\n", mnt_entry-&gt;mnt_dir); printf("File System Type: %s\n", mnt_entry-&gt;mnt_type); // 使用statfs()获取更详细的文件系统信息 // ... } endmntent(mnt_file); return 0; } ``` ...

Global site tag (gtag.js) - Google Analytics