目录
基本知识
术语
本文中用的术语:源文件、目标文件、可执行文件。
1. 源文件:通常指编辑代码的文件;
2. 目标文件:经过编译器的编译生成的CPU可识别的二进制代码,一般是不能执行的;
3. 可执行文件:目标文件与相关的库链接后的文件,顾名思义可执行文件是可以执行的。
编译过程
GCC编译器对程序的编译分为4个阶段:预编译、编译和优化、汇编以及链接。GCC编译器可以将这4个步骤合并为一个。
- 预编译
将程序中引用的头文件包含进源代码中,并对一些宏进行替换。 - 编译和优化
将用户可识别的鱼眼翻译成一组处理器可识别的操作码,生成目标文件,通常翻译成汇编语言。 - 汇编
将编译输出的汇编代码翻译成符合一定格式的机器代码,在Linux系统上一般表现位ELF目标文件(OBJ文件)。 - 链接
就是将汇编生成的OBJ文件、系统库的OBJ文件、库文件链接起来,最终生成可以在特定平台运行的可执行程序。
自动编译
自动编译的过程包括头文件扩展、目标文件编译,以及链接默认的系统库生成可执行文件,最后生成系统默认的可执行程序a.out。
以“Hello World!”源码为例
/*hello-world.c*/
#include <stdio.h> //头文件包含
int main(void)
{
printf("Hello World!\n"); //打印“Hello World!”
return 0;
}
运行如下命令直接编译成可执行文件:
$gcc hello-world.c
执行生成的可执行文件a.out如下:
$ ./a.out
Hello World!
在编译过程中会生成一个对应的a.out.h文件一般存放在/usr/lib/gcc/i486-linux-gnu/4.4.3/include-fixed/linux路径下,其中i486-linux-gnu/4.4.3为对应的gcc版本可通过执行gcc -v查看,如下:
$ gcc -v
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.4.3-4ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-plugin --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5)
分步编译
预处理
通过在编译过程中使用编译选项-E告诉编译器进行预编译操作,继续以“hello-world.c”为例,输入如下指令,可以将预处理结果输出到屏幕上;
$ gcc -E hello-world.c
# 1 "hello-world.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "hello-world.c"
……//省略部分内容
# 3 "hello-world.c" 2
int main(void)
{
printf("Hello World!\n");
return 0;
}
一般使用如下命令生成预编译后的中间文件,如下:
$ gcc -o hello-world.i -E hello-world.c
编译和优化
编译生成汇编语言,使用的GCC选项是-S,默认情况下声场的文件名和源文件名一致,扩展名为.s,既可以通过源文件直接生成汇编文件,也也可以使用预处理后的文件生成汇编文件,使用源文件的指令如下:
$ gcc-S hello-world.c
使用预处理文件编译指令如下:
$ gcc -o hello-world.s -S hello-world.i
输出的汇编文件如下:
.file "hello-world.c"
.section .rodata
.LC0:
.string "Hello World!"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
call puts
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
.section .note.GNU-stack,"",@progbits
汇编
汇编生成机器文件的指令如下
$gcc -o hello-world.o -c hello-world.s
生成的.o文件可以通过如下指令进行查看:
$ hexdump -C hello-world.o
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 01 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 d4 00 00 00 00 00 00 00 34 00 00 00 00 00 28 00 |........4.....(.|
00000030 0b 00 08 00 55 89 e5 83 e4 f0 83 ec 10 c7 04 24 |....U..........$|
00000040 00 00 00 00 e8 fc ff ff ff b8 00 00 00 00 c9 c3 |................|
00000050 48 65 6c 6c 6f 20 57 6f 72 6c 64 21 00 00 47 43 |Hello World!..GC|
00000060 43 3a 20 28 55 62 75 6e 74 75 20 34 2e 34 2e 33 |C: (Ubuntu 4.4.3|
00000070 2d 34 75 62 75 6e 74 75 35 29 20 34 2e 34 2e 33 |-4ubuntu5) 4.4.3|
00000080 00 00 2e 73 79 6d 74 61 62 00 2e 73 74 72 74 61 |...symtab..strta|
00000090 62 00 2e 73 68 73 74 72 74 61 62 00 2e 72 65 6c |b..shstrtab..rel|
000000a0 2e 74 65 78 74 00 2e 64 61 74 61 00 2e 62 73 73 |.text..data..bss|
000000b0 00 2e 72 6f 64 61 74 61 00 2e 63 6f 6d 6d 65 6e |..rodata..commen|
000000c0 74 00 2e 6e 6f 74 65 2e 47 4e 55 2d 73 74 61 63 |t..note.GNU-stac|
链接
通过.o文件可以使用如下命令生成可执行文件hello-world:
$gcc -o hello-world hello-world.o
同样.o文件换成源文件等任意的一个中间版本均可生成可执行文件。
输入如下指令可以查看链接的过程:
$ gcc -v -o hello hello-world.o
Using built-in specs.
Target: i486-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.4.3-4ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-plugin --enable-objc-gc --enable-targets=all --disable-werror --with-arch-32=i486 --with-tune=generic --enable-checking=release --build=i486-linux-gnu --host=i486-linux-gnu --target=i486-linux-gnu
Thread model: posix
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5)
COMPILER_PATH=/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/:/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/4.4.3/:/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/:/lib/../lib/:/usr/lib/../lib/:/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../:/lib/:/usr/lib/:/usr/lib/i486-linux-gnu/
COLLECT_GCC_OPTIONS='-v' '-o' 'hello' '-mtune=generic' '-march=i486'
/usr/lib/gcc/i486-linux-gnu/4.4.3/collect2 --build-id --eh-frame-hdr -m elf_i386 --hash-style=both -dynamic-linker /lib/ld-linux.so.2 -o hello -z relro /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crt1.o /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crti.o /usr/lib/gcc/i486-linux-gnu/4.4.3/crtbegin.o -L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux-gnu/4.4.3 -L/usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/i486-linux-gnu/4.4.3/../../.. -L/usr/lib/i486-linux-gnu hello-world.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i486-linux-gnu/4.4.3/crtend.o /usr/lib/gcc/i486-linux-gnu/4.4.3/../../../../lib/crtn.o
- crt1.o、crti.o、crtbegin.o、crtend.o、crtn.o是gcc加入的系统标准启动文件,对于一般应用程序,这些启动是必需的。
- lc:链接libc库文件,其中libc库文件中就实现了printf等函数。