在linux下开发难免会用到gcc编译,GCC(GNU Compiler Collection,GNU编译器套装),是由 GNU 开发的编程语言编译器。它是GNU编译器套装以GPL许可证所发行的自由软件,也是 GNU计划的关键部分。使用GCC编译程序时,编译过程可以被细分为四个阶段:
◆ 预处理(Pre-Processing)
◆ 编译(Compiling)
◆ 汇编(Assembling)
◆ 链接(Linking)
1、预处理 对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析,编译选项为gcc -E *.c
#define DEBUG "debug" int main() { char *a = DEBUG; return 1; }经过上面的预处理后,可以看到DEBUG被替换成了预定义的内容
# 1 "hello.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "hello.c" int main() { char *a = "debug"; return 1; }2、编译 调用cc1进行编译,使用gcc -S选项就可以生成汇编代码,这个阶段根据输入文件生成以.o为后缀的目标文件。生成的汇编代码如下:
.file "hello.c" .section .rodata .LC0: .string "debug" .text .globl main .type main, @function main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movq $.LC0, -8(%rbp) movl $1, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size main, .-main .ident "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)" .section .note.GNU-stack,"",@progbits3、汇编 汇编过程是针对汇编语言的步骤,调用as进行工作,一般来讲,.S为后缀的汇编语言源代码文件和汇编、.s为后缀的汇编语言文件经过预编译和汇编之后都生成以.o为后缀的目标文件,此过程生成ELF格式的目标代码,使用gcc -c进行汇编
使用readelf -a hello.o可以看到详细的elf信息
ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x0 Start of program headers: 0 (bytes into file) Start of section headers: 296 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 0 (bytes) Number of program headers: 0 Size of section headers: 64 (bytes) Number of section headers: 13 Section header string table index: 10 Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .text PROGBITS 0000000000000000 00000040 0000000000000013 0000000000000000 AX 0 0 4 [ 2] .rela.text RELA 0000000000000000 00000568 0000000000000018 0000000000000018 11 1 8 [ 3] .data PROGBITS 0000000000000000 00000054 0000000000000000 0000000000000000 WA 0 0 4 [ 4] .bss NOBITS 0000000000000000 00000054 0000000000000000 0000000000000000 WA 0 0 4 [ 5] .rodata PROGBITS 0000000000000000 00000054 0000000000000006 0000000000000000 A 0 0 1 [ 6] .comment PROGBITS 0000000000000000 0000005a 000000000000002d 0000000000000001 MS 0 0 1 [ 7] .note.GNU-stack PROGBITS 0000000000000000 00000087 0000000000000000 0000000000000000 0 0 1 [ 8] .eh_frame PROGBITS 0000000000000000 00000088 0000000000000038 0000000000000000 A 0 0 8 [ 9] .rela.eh_frame RELA 0000000000000000 00000580 0000000000000018 0000000000000018 11 8 8 [10] .shstrtab STRTAB 0000000000000000 000000c0 0000000000000061 0000000000000000 0 0 1 [11] .symtab SYMTAB 0000000000000000 00000468 00000000000000f0 0000000000000018 12 9 8 [12] .strtab STRTAB 0000000000000000 00000558 000000000000000e 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings) I (info), L (link order), G (group), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific) There are no section groups in this file. There are no program headers in this file. Relocation section '.rela.text' at offset 0x568 contains 1 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000008 00050000000b R_X86_64_32S 0000000000000000 .rodata + 0 Relocation section '.rela.eh_frame' at offset 0x580 contains 1 entries: Offset Info Type Sym. Value Sym. Name + Addend 000000000020 000200000002 R_X86_64_PC32 0000000000000000 .text + 0 There are no unwind sections in this file. Symbol table '.symtab' contains 10 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000000 0 FILE LOCAL DEFAULT ABS hello.c 2: 0000000000000000 0 SECTION LOCAL DEFAULT 1 3: 0000000000000000 0 SECTION LOCAL DEFAULT 3 4: 0000000000000000 0 SECTION LOCAL DEFAULT 4 5: 0000000000000000 0 SECTION LOCAL DEFAULT 5 6: 0000000000000000 0 SECTION LOCAL DEFAULT 7 7: 0000000000000000 0 SECTION LOCAL DEFAULT 8 8: 0000000000000000 0 SECTION LOCAL DEFAULT 6 9: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 main
4、链接 链接过程。生成可执行代码。链接分为两种,一种是静态链接,另外一种是动态链接。使用静态链接的好处是,依赖的动态链接库较少,对动态链接库的版本不会很敏感,具有较好的兼容性;缺点是生成的程序比较大。使用动态链接的好处是,生成的程序比较小,占用较少的内存。
gcc hello.o -o hello 就可以完成最后的链接操作并生成可执行文件,至于如何生成动态库和静态库,以及如何链接动态库和静态库,以后会再作介绍。