Skip to content

Latest commit

 

History

History
60 lines (39 loc) · 2.2 KB

File metadata and controls

60 lines (39 loc) · 2.2 KB

为什么main"不是"程序的入口?

今天在听jyy的操作系统公开课,他当时举了一个例子,让我一下子回忆起了之前对于《程序员的自我修养》里面的一些知识点,于是想记录下来。

刚学C语言的时候,几乎国内所有的老师都会告诉你“main函数是程序的入口”,这句话听起来也是没有什么错的,但是随着学习的深入,我认为一个合格的大学生应该不能停留在如此肤浅的认识,而应该能够彻底理解程序到底是怎样执行的,至少要到程序是需要装载才能执行的层面。

话不多说,jyy举的例子是这样的

int main(){

}

这是一个什么都不做的程序,讲道理来说这个函数因为不依赖于任何的库函数,那么我们编译之后再用链接器按照静态链接的方法将其生成可执行文件应该是没有任何问题的,但是当我们执行下面的指令之后却发生了错误。

gcc -c test.c
ld -static -e  _main test.o

出现了段错误.

[1]    15456 segmentation fault  ./a.out

按照jyy说的,那肯定得上gdb了,我们用starti指令,确实一开始就进入到了main函数里面去,但是main函数的最后一句是一个ret指令,学过汇编语言的人都知道,在x86体系结构里面,ret会弹出栈顶元素同时跳转到该位置。那么因为没有函数调用过main函数,那么肯定会发生错误,所以在程序装载的时候,操作系统肯定是加入了其他的代码来跳转到 main函数的执行的,加入的这些代码负责准备好main函数执行所需要的环境,包括堆、I/O、线程、全局变量构造,通过正常的代码,以glibc为例,我们可以发现glibc程序的入口叫做_start,我们可以看到核心代码是

xorl %ebp, %ebp
popl %esi
movl %esp, %ecx

pushl %esp
pushl %edx
pushl $__libc_csu_fini
pushl $__libc_csu_init
pushl %ecx
pushl %esi
push %main

call __libc_start_main

我们可以把这段代码改写成更直观的C代码

%ebp = 0;
int argc = pop from stack
char** argv = top of stack

__lib_start_main(main,argc,argv,__libc_csu_init,__libc_csu_fini,edx,top of stack)