这一节我们来学编译与链接。一部分介绍一下GCC,一边是介绍一下ELF。
一.GCC
1.什么是gcc?
gnu compiler collection,由GNU开发的,遵循GPL许可证发布的编译器套件。支持C,C++,objectiveC,Fortran,ada和Go语言等等语言前端。已经被移植到多种计算机体系架构上。X86,arm,risc-v。
gcc的初衷是为GNU操作系统专门编写一款编译器,现在已经被大多数unix-like操作系统采纳为标准编译器。
2.gcc的使用
gcc option filename
-E:只做预处理
-c:只编译不链接,生成目标文件.o
-S:生成汇编代码。
-o file:把输出生成到指定的文件file中。
-g:在输出的文件中增加支持调试的信息
-v:显示输出详细的命令执行过程信息。
例如 gcc -E h.c -o h.o
把文件h.c预处理输出到h.i文件。预处理和编译阶段都是由一个CC1程序执行的。预处理(gcc -E)将#include 或者#define等转化为纯c语言。然后编译(gcc -S)就是把c语言(h.i)转为汇编语言(h.s)。然后由as程序把汇编语言转化为机器语言(h.o)。最后由ld程序进行链接(和一些标准库链接,最终形成执行程序h.out)。
c->i->s->o->out
细心往下看能找到.s的中间文件:
图中的cc1和as程序,最后还有一个collect2程序,包含了链接ld程序。
这里提出一个问题: .s与.S的区别?同学们可以自己去了解。
各种文件的后缀:
.a
.s/.S
.c
.cc/.cpp/.cxx
.i
.h
.o
.a/.so
a.out
多个源文件的处理事实上就是链接部分的作用。分别生成.o文件链接为out文件。
二.ELF介绍
executable linkable format是一种unix-like系统上的二进制标准。也就是可执行可链接格式。
elf文件类型 | 说明 | 实例 |
---|---|---|
可重定位文件(relocatable file) | 包含了代码和数据,可以被链接成可执行文件或者共享目标文件。 | Linux上的.o |
可执行文件(executable file) | 可以直接执行的程序。 | linux上的a.out |
共享目标文件(share object file) | 包含代码和数据,可以作为链接器的输入,在链接阶段和其他的可重定位文件和共享目标文件链接成为新的object。或者在运行阶段,作为动态链接器的输入,和可执行文件结合,作为进程的一部分来运行。 | Linux上的.so |
核心转储文件(core dump file) | 进程意外终止时,系统可以将该进程部分内容和终止时的其他状态信息保存到该文件中以供调试。 | linux上的core文件 |
1.ELF文件结构
elf文件也是有结构的:header,program header table,.text,.init,.data。(节)这些节都会在最后一个叫做section header table(链接视图),每个节都要按一定宽度对齐。为了实际节省空间等目的,还会有归并的操作,也就是合成为segment(段),实际加载是按照段加载。这里是由节的属性来归并,具体怎么归并定义在program header table(运行视图)中。
2.操作ELF文件:binutils工具包(binary utility)
ar:归档文件。
as:被gcc调用输入汇编文件,输出目标文件供ld链接。
ld:被gcc调用,把目标文件和各种库文件结合在一起。重定位数据,并链接符号引用。
objcopy:执行文件格式转换。
objdump:显示ELF文件的信息。
readelf:显示更多elf文件的信息。(包括DWAF调试信息。)
3.实际操作
下面是一个c程序文件。
#include <stdio.h>
int global_init = 0x11111111;
const int global_const = 0x22222222;
void main()
{
static int static_var = 0x33333333;
static int static_var_uninit;
int auto_var = 0x44444444;
printf("hello world!\n");
return;
}
- 编写⼀个简单的打印 “hello world!” 的程序源文件:
- hello.c 对源文件进⾏本地编译,⽣成针对⽀持 x86_64 指令集架构处理器的⽬标文件 hello.o。
- 查看 hello.o 的文件的文件头信息。
- 查看 hello.o 的 Section header table。
- 对 hello.o 反汇编,并查看 hello.c 的 C 程序源码和机器指令的对应关系。
查看文件头:
注意这里是小写s。
查看链接视图
这里是大写S,这才是我们要的section视图(链接视图)。
SW选项则是让输出宽度对齐。我们可以看到节的类型,序号,名字等等等等。
反汇编
这里给出源c代码对比:
#include <stdio.h>
int global_init = 0x11111111;
const int global_const = 0x22222222;
void main()
{
static int static_var = 0x33333333;
static int static_var_uninit;
int auto_var = 0x44444444;
printf("hello world!\n");
return;
}
学号编译和反编译的好处很多不一一列举了。这一章很重要,希望大家不要觉得无用或者无聊。下一节来讲嵌入式开发。