说明:本文的讨论基于一个运行linux的x86系统环境,使用标准ELF文件格式。讨论集中在32位代码,在x86-64系统上用gcc -m32产生32位代码。若编译时发生/usr/include/features.h:364:25: fatal error: sys/cdefs.h: 没有那个文件或目录,则使用命令sudo apt-get install libc6-dev-i386解决。 ELF文件格式与相关命令详见http://blog.youkuaiyun.com/li_xiang_li/article/details/50528307
一. 由源代码到可执行程序经历了什么
源代码:
//main.c
int add(int a,int b);
static int si;//.bss
extern int buf[];
int *copy = &buf[0];//.rel.data
int main()
{
int a = 3;
int b = 5;
int c = add(a,b);//.rel.text
char *s = "hello c";//.rodata
static int si;//.bss
return 0;
}
//add.c
int buf[2];
int add(int a,int b)
{
return (a+b);
}
//makefile(为了简化讨论,makefile文件中没有添加-g选项)
all:main
main:main.o add.o
gcc -o main main.o add.o -m32
main.o:main.c
gcc -c main.c -m32
add.o:add.c
gcc -c add.c -m32
clean:
rm -rf *.o main
从源代码到可执行程序所要经历的过程概述:
源代码(.c .cpp .h)经过c预处理器(cpp)后生成.i文件,编译器(cc1、cc1plus)编译.i文件后生成.s文件,汇编器(as)汇编.s文件后生成.o文件,链接器(ld)链接.o文件生成可执行文件。gcc是对cpp、cc1(cc1plus)、as、ld这些后台程序的包装,它会根据不同的参数要求去调用后台程序。以helloworld程序为例,使用gcc -o hello hello.c时加上-v选项可观察到详细的步骤。也可使用gcc分别进行以上四步骤,预编译gcc -E hello.c -o hello.i,编译gcc -S hello.i -o hello.s,汇编gcc -c hello.s -o hello.o,链接gcc -o hello hello.o
有兴趣的话可以尝试验证以下预处理、编译、汇编、链接的详细过程及结果。使用cpp对c文件进行预处理用vim查看.i文件,使用cc1编译.i文件用vim查看.s文件,使用as汇编.s文件,最终使用ld手动链接相关文件生成可执行文件。(过程较为复杂,可参考-v选项后的结果)
二、 由ELF文件看程序的编译链接
程序的编译涉及词法分析、语法分析、语义分析等过程,详情参见《编译原理》。
1.以下分析一下编译完成之后的ELF文件:
readelf -S add.o查看section header table