一、GCC简介
GCC是以GPL许可证所发行的自由软件,也是GNU计划的关键部分。GCC的初衷是为GNU操作系统专门编写一款编译器,现已被大多数类Unix操作系统(如Linux、BSD、MacOS X等)采纳为标准的编译器,甚至在微软的
Windows上也可以使用GCC。GCC支持多种计算机体系结构芯片,如x86、ARM、MIPS等,并已被移植到其他多种硬件平台 。
GCC原名为GNU C语言编译器(GNU C Compiler),只能处理C语言。但其很快扩展,变得可处理C++,后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective -C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)
二、GCC常用命令
1.简单编译
test.c 代码如下(示例):
#include <stdio>
void main()
{
printf("Hello Gcc!!!");
}
进行一步到位的编译
gcc test.c -o test
test可执行文件生成成功
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译 (Compilation)、汇编 (Assembly)和连接(Linking)。
1.1 预处理
gcc -E test.c test.i
或者
gcc -E test.c
test.i 文件中存放着 test.c 经预处理之后的代码,通过预编译生成 .i 文件,查看 .i 文件部分内容
1.2 编译为汇编代码
预处理之后,可直接对生成的 test.i 文件编译,生成汇编代码:
gcc -S test.i -o test.s
gcc 的-S 选项,表示在程序编译期间,在生成汇编代码后,停止,-o 输出汇编代码文件。
查看test.s内容
1.3 汇编
生成的汇编代码文件 test.s,gas 汇编器负责将其编译为目标文件
gcc -c test.s -o test.o
1.4 链接
将生成的 test.o,将其与C标准输入输出库进行连接,最终生成程序 test
gcc test.o -o test
生成的文件通过 ./test 就可以执行
2. 多个程序文件的编译
如果同时处理的文件不止一个,GCC 仍然会按照预处理、编译和链接的过程依次进行。如果深究起 来,上面这条命令大致相当于依次执行如下三条命令:
1.gcc 源文件1 源文件2 -o 生成的可执行文件
gcc test1.c test2.c -o test
2.同时处理的文件不止一个时,GCC 仍然会按照预处理、编译和链接的过程依次进行
gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc test1.o test2.o -o test
3.检错
gcc -pedantic illcode.c -o illcode
-pedantic 选项能够帮助程序员发现一些不符合 ANSI/ISO C 标准的代码,但不是全部,事实上只有 ANSI/ISO C 语言标准中要求进行编译器诊断的 那些情况,才有可能被 GCC 发现并提出警告
gcc -Wall illcode.c -o illcode
-Wall 能够使 GCC 产生尽可能多的警告信息
gcc -Werror test.c -o test
-Werror 选项会使GCC 会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改
4.库文件连接
函数库实际上就是一些头文件(.h)和库文件(so、或 lib、dll)的集合,Linux 下的大多数函数都默认将头文件放到/usr/include/目录下,而库文 件则放到/usr/lib/目录下,当我们需要调用其它函数库时,GCC在编译时就需要使用自己方法进行查找
假设我们要调用目录 /usr/lib 下的库文件libtest.so
①编译成可执行文件
首先我们要进行编译 test.c 为目标文件
gcc -c -I /usr/lib test.c -o test.o
②链接
gcc -L /usr/lib -ltest test.o -o test
③强制链接时使用静态链接库
GCC 在链接时优先使用动态链接库,如果需要的话可以在编译时加上-static 选项,强制使用静态链接库
假设目录 /usr/lib 存在动态库文件libtest.so和静态库文件libtest.a,我们想要调用对应静态库文件,可以用:
gcc -L /usr/lib -static -ltest test.o -o test
静态库链接时搜索路径顺序:
- ld 会去找 GCC 命令中的参数-L
- 再找 gcc 的环境变量 LIBRARY_PATH
- 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初 compile gcc 时写在程序内的
动态链接时、执行时搜索路径顺序:
- 编译目标代码时指定的动态库搜索路径
- 环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径
- 配置文件/etc/ld.so.conf 中指定的动态库搜索路径
- 默认的动态库搜索路径/lib
- 默认的动态库搜索路径/usr/lib
三、合作伙伴
GCC不是单枪匹马在战斗,他还有其它合作伙伴。
Binutils:
一组二进制程序处理工具,包括:addr2line、ar、objcopy、objdump、as、ld、 ldd、readelf、 size 等,这 一组工具 是开发和 调试不可 缺少的工具。
(1) addr2line:用来将程序地址转换成其所对应的程序源文件及所对应的代码行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对应的源代码位置。
(2) ar:用于创建静态链接库。
(3) ld:主要用于链接。
(4) as:主要用于汇编。
(5) ldd:可以用于查看一个可执行程序依赖的共享库。
(6) objcopy:将一种对象文件翻译成另一种格式,譬如将.bin 转换成.elf、或 者将.elf 转换成.bin 等。
(7) objdump:主要的作用是反汇编。
(8) readelf:显示有关 ELF 文件的信息。
(9) size:列出可执行文件每个部分的尺寸和总尺寸,代码段、数据段、总大小 等。
分析 ELF 文件
链接器链接后生成的最终文件为 ELF 格式可执行文件,一个 ELF 可执行文件通常 被链接为不同的段,常见的段譬如.text、.data、.rodata、.bss 等段。
1.ELF 文件的段
ELF 文件格式如下图所示,位于 ELF Header 和 Section Header Table 之间的都 是段(Section)
.text:已编译程序的指令代码段。
.rodata:ro 代表 read only,即只读数据(譬如常数 const)。
.data:已初始化的 C 程序全局变量和静态局部变量。
.bss:未初始化的 C 程序全局变量和静态局部变量。
.debug:调试符号表,调试器用此段的信息帮助调试。
使用readelf工具查看各个selection的信息
readelf -S test
反汇编 ELF
我们如果需要去使用普通文档打开ELF文件,查看相关数据以及指令就需要使用反汇编进行处理
使用objdump工具将ELF文件进行反汇编:
objdump -D test
可以看到ELF已经被反汇编成功,可以查看相关指令信息了