第三次讲座盲点整理(编译与链接)——入口函数一定是main?

来源:互联网 发布:sqsx免费代理软件 编辑:程序博客网 时间:2024/06/15 00:01

        自打从上大学C语言课的第一天起(不是第一天也是第二天了),我们就知道了这样一句话:一个C程序总是以main函数为入口,从main函数开始执行。昨晚刘欢学长为今天的讲座做铺垫,留下了几个问题,其中就有这样一个问题:C程序的入口是main函数?一定是main函数?

        难不成不是?难不成这又会成为一个颠覆我三观的事实?!——函数入口可以不是main?!

        带着疑惑,请教百度,对着屏幕瞠目结舌了半天,原来一个C程序的入口真的可以不是main

        先看这样一个程序:

#include <stdio.h>int junco(void){    printf("Fantastic!\n");    return 0;}
        显然,一眼就可以看出,这个程序有main函数。一定不会被正常编译通过。

        在用GCC进行编译时,会给出下列提示信息:

        /usr/lib/gcc/i686-linux-gnu/4.7/../../../i386-linux-gnu/crt1.o:在函数‘_start’中:
        (.text+0x18):对‘main’未定义的引用
        collect2: 错误: ld 返回 1

        可以看到错误信息提示,提到了一个“crt1.o”这个文件,其中crt
“Cruntime library”的缩写,其含义是“C运时库”。

        C运行时库除了给我们提供必要的库函数调用(如memcpy、printf、malloc等)之外,它提供的另一个最重要的功能是为应用程序添加启动函数。C运行时库启动函数的主要功能为进行程序的初始化,对全局变量进行赋初值,加载用户程序的入口函数。

从给出的错误提示信息中还可以得知,在crt1.o中,有一个名为“_start”的函数。现在网上找到一份crt1.o的伪代码

    section .text:
    __start:
    
     :
     init stack;
     init heap;
     open stdin;
     open stdout;
     open stderr;
     :
     push argv;
     push argc;
     call _main; (调用 main)
     :
     destory heap;
     close stdin;
     close stdout;
     close stderr;
     :
     call __exit;

        从伪代码可以看,在这个_start函数中,调用了main函数。那么我们可不可以用什么手段,去修改入口函数,把入口函数改成junco呢?

        可以执行下面的命令:

        gcc1.c -e junco -nostartfiles

        其中-e选项为修改函数的入口地址,可惜在网上和man手册里都没有找到有关-e这个选项的解释,只搜索到了-E这个选项。问了好多人才明白原来-e指的就是entrance。这里把entrance指定为junco函数。-nostartfiles选项的作用是通知编译器不自动加入启动函数以及别的库级别的初始化,这样就不会调用到crt1.o中的_start函数。

        此时,编译通过。可是执行程序,可以成功打印出Fantastic!字样,但是却会提示“段错误”

Fantastic!段错误 (核心已转储)

        原来,在编译的时候加上了-nostartfiles这个选项的同时,使得最后的return语句不能正常执行。解决方案是,把程序最后的return语句改成exit语句,让exit语句来做最后的“善后工作”

#include <stdio.h>int junco(void){    printf("Fantastic!\n");    exit(0);}

        这次则会正常执行, 没有任何错误!





原创粉丝点击