glibc 启动过程在不同的情况下差别很大,静/动态和库/执行文件四种组合都不一样

在静态可执行文件的情况下:

  • bp 清零
  • 在调用 _start 之前,装载器会将用户的参数和环境变量压入栈中,pop 出 argc 给 si
  • 将栈顶地址(sp)也就是 argv 地址传给 c
  • 调用 __libc_start_main(main,argc,argv,__libc_csu_init,__libc_csu_fini,edx,top of the stack)

argv 里紧跟着环境变量表 initmain 调用前的初始化工作,finimain 结束之后的收尾工作,rtld_fini 是和动态加载有关的收尾工作(runtime loader)

最后的 stack_end 标明了栈底地址

__libc_start_main 中:先找到环境表,赋值给一些全局变量

__pthread_initialize_minimal();
__cxa_atexit(rtld_fini,...);
__libc_init_first(fini,...);
(*init)(argc,argv,__environ);

安排一些关键函数执行

最后在 __libc_start_main 的末尾,最关键的两行代码:

result = main(argc,argv,__environ);
exit(result);

其中 exit 按照 __cxa_atexit()atexit 注册的函数的量表循环调用这些注册的函数

最后的 _exit 函数由汇编实现,且和平台相关,一般是调用系统调用