程序入口函数与初始化

来源:互联网 发布:linux安装两个mysql 编辑:程序博客网 时间:2024/06/11 02:04

1.概述程序执行过程:

1)操作系统在创建进程后,将控制权交给了程序的入口,这个入口通常是运行库的某个入口函数

2)入口函数对运行库和程序运行环境进行初始化,包括堆,IO,线程,全局变量的构成

3)入口函数完成初始化后,调用 main 函数,正式执行程序主题

4)main函数执行完毕后,返回到入口函数,入口函数进行清理,包括全局变量析构,堆销毁,关闭I/O等,然后进行系统调用结束进程。


2.linux 中 glibc入口函数

1)操作系统将控制权交给进程入口,_start 函数,由汇编实现,与平台相关,以下代码仅供参考:

_start:xorl %ebp,%ebp//将寄存器 ebp 清零popl %esi// 弹出 argc,将其存放在 esi 中movl %esp,%ecp// 使寄存器 ecp 指向 argv 与环境变量数组//以下为调用函数 __libc_start_main 压入参数pushl %esp//压入栈底地址,即栈的最高地址pushl %edx//压入函数指针,该函数完成与动态加载有关的收尾工作pushl $__libc_csu_fini//压入函数指针,该函数完成 main 结束后的收尾工作pushl $__libc_csu_init//压入函数指针,该函数完成 main 调用前的初始化工作pushl %ecs//压入argv 与环境变量数组pushl %esi//压入argcpushl main// 压入 main 函数的指针call __libc_start_main//调用另一个初始化函数,参数为上述压栈数据,从右至左压栈hlt// 避免调用 __libc_start_main 函数中没有成功调用 exit ,hlt 将强制终止程序

2
)_start 函数调用另一个函数 __libc_start_main,完成对运行环境的初始化,且传入该函数的参数为七个参数,上面代码中压入栈的七个数据从上到下对应调用函数中从右至左的参数。__lib_start_main函数的代码与解释如下:

 

int __libc_start_main(int(*main)(int, char**, char**),//第一个参数,main 函数的指针int argc,char * __unbounded * __unbounded ubp_av,// 第三个参数,包含了 argv 与环境变量的数组_typeof(main) init,//第四个参数,初始函数指针void(*fini)(void),//第五个参数,结束函数指针void(*rtld_fini)(void),//第六个参数,处理动态链接的函数void * __unbounded stack_end//第七个参数,栈底地址){char** ubp_ev = &ubp_av[argc + 1];//使 ubp_ev 执行 argv__environ = ubp_env;// 使 __environ 执行环境变量__libc_stack_end = stack_end;//存储栈底地址//...省略许多步骤__cxa_atexit(rtld_fini, NULL, NULL);//注册在 main 结束后的函数调用//..._cxa_atexit(fini, NULL, NULL);//...result = main(argc, argv, __environ);//调用 main 函数exit(result);//执行结束函数}

函数在执行完 main 函数后,将调用 exit 退出进程,而 exit 中,将执行注册的收尾函数,对于非正常结束的进程,也会调用 exit,因此确保注册的收尾函数无论是在正常结束函数非正常结束时均会被调用。

* exit 调用所有注册的收尾函数后,将调用 _exit() 函数,该函数将直接结束,且该函数最后也有一个 "hlt" ,其功能与 _start 中的一样,预防程序调用 exit 系统调用失败时,无法结束进程。

0 0
原创粉丝点击