GCC编译全过程解析

一、GCC参数介绍

2.1 -x

2.1.1 -x language filename

设定文件所使用的语言,使后缀名无效,对以后的多个有效。也就是根据约定C语言的后缀是.c的,而c++的后缀名是.c或者.cpp,如果我们决定C代码文件的后缀是.pig,那我们就要使用这个参数,这个参数对后面的文件名都起作用。language可以使用的参数有如下这些:‘c’, ‘objective-c’, ‘c-header’, ‘c++’, ‘cpp-output’, ‘assembler’和’assembler-with-cpp’。

代码例子:

gcc -x c hello.pig

2.1.1 -x none filename

关闭上一个选项,也就是让gcc根据文件后缀,自动识别文件类型。

代码例子:

gcc none hello2.c

2.2 -c

只激活预处理,编译和汇编,也就是只把程序做成obj文件

代码例子:

gcc -c hello.c

处理后将生成.o的obj文件

2.3 -S

只激活预处理和编译,就是指把文件编译为汇编代码。

代码例子:

gcc -S hello.c

处理后将生成.s的汇编代码,可以用文本编辑器查看

2.4 -E

只激活预处理,不生成文件,我们需要把它重定向到一个输出文件里面。

代码例子:

gcc -E hello.c > pianoapan.txt
gcc -E hello.c | more

2.5 -o

自定义目标文件名称,默认的时候,gcc编译出来的文件名称是a.out,如果对这个名字有修改需求,那么可以利用这个参数进行修改。

代码例子:

gcc -o hello.asm -S hello.c

2.6 -m

2.6.1 -m32

gcc提供了编译选项,可以为指定架构生成汇编代码,比如linux下,-m32生成32位机器的汇编代码,-m64则生成64位机器汇编代码,由于64位机器的寄存器比32位机器多很多,所以GCC编译器会尽量使用寄存器来传递参数,而不是32位机器下的压栈。

GCC参数详解

二、GCC四步详解

2.1 预处理(也叫预编译)

gcc -E hello.c -o hello.i
//cpp是预编译器
cpp hello.c>hello.i

在这步中:

  • 将所有#define删除,并且展开所有的宏定义
  • 处理所有的条件预编译指令,如:
    #if #ifdef #undef #ifndef #endif #elif
  • 处理#include,将包含的文件插入此处,这是一个递归的过程
  • 删除所有注释 // /* */
  • 添加行号和文件名标识,以便于编译时产生的错误警告能显示行号
  • 保留#pragma编译器指令

2.2 编译

gcc -S hello.i -o hello.s

这步中将预处理完的.i文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件,这是整个程序构建的最核心的部分,也是最复杂的部分

2.3 汇编

gcc -c hello.s -o hello.o

汇编是将第二步生成的汇编代码变成机器可执行的指令,每一个汇编语句都几乎对应一条机器指令

2.4 链接

链接动态库和静态库

三、目标文件

3.1 生成的目标文件有什么,什么是目标文件

目标文件就是源代码经过编译后但未进行链接的那些中间文件,Linux下的.o文件就是目标文件,目标文件和可执行文件的内容和格式几乎都一样,所有我们可以广义地将目标文件和可执行文件看成一种类型的文件。他们都是按照ELF文件格式存储的

3.2 Linux下有哪些ELF类型的文件?

.o文件、可执行文件、核心转储文件(core dump)、.so文件(动态链接库)

3.3 可执行文件的概貌详解

  • File Header:文件头,描述了整个文件的文件属性,包括目标文件是否可执行、是静态链接还是动态链接及入口地址、目标硬件、目标操作系统等信息、段表(描述文件中各个段的偏移位置及属性等)
  • .text:代码段,存放了程序源代码编译后生成的机器指令
  • .data:数据段,存放已初始化的全局静态与非静态变量和已初始化的局部静态变量
  • .bss:存放未初始化的全局变量(全局静态和非静态变量)和局部静态变量,但是.bss段只是为这些变量预留位置,并没有内容,所以这些变量在.bss段中也不占据空间。

四、深入挖掘.o文件

4.1 要用到的命令

  • objdump -h XXXX.o:打印主要段的信息
  • objdump -x XXXX.o:打印更多的详细信息
  • objdump -s XXXX.o:将所有段的内容以16进制方式打印出来
  • objdump -d XXXX.o:这里也可以使用-S,将所有包含指令的段反汇编
  • objdump -t XXXX.o:查看所有的符号以及他们所在段
  • readelf -h XXXX.o:查看.o文件的文件头详细信息
  • readelf -S XXXX.o:显示.o文件中的所有段,即查看段表
  • size XXXX.o:查看.o文件中各个段所占大小
  • nm XXXX.o:查看.o文件中所有的符号

4.2 代码例子

// test.c 
 
int printf(const char *format, ...);
 
int g_var2 = 10;
int g_var2;
 
void func(int i)
{
    printf("%d\n",i);
}
 
int main(void)
{
    static int static_var1 = 20;
    static int static_var2;
    
    int var3 = 1;
    int var4;
    func(static_var1 + static_var2 + var3 + var4);
    return var3;
}

接下来使用gcc -c test.c 编译上面的文件生成test.o文件,然后查看test.o文件结构:
在这里插入图片描述
行:

  • .text:代码段(存放函数的二进制机器指令)
  • .data:数据段(存放已初始化的局部/全局静态变量、未初始化的全局静态变量)
  • .bss:bss段(声明未初始化变量所占大小)
  • .rodata:只读数据段(存放" "引住的只读字符串)
  • .comment:注释信息段
  • .node.GUN-stack:堆栈提示段

列:

  • Size:段的长度
  • File Off:段的所在位置(即距离文件头的偏移位置)
  • CONTENTS:表示该段在文件中存在
  • ALLOC:表示只分配了大小,但没有存内容

4.3 关于.bss段

GCC编译过程解析
Linux段管理,BSS段,data段,.rodata段,text段
数据段(BSS段、DATA段)、代码段(.RODATA)、堆栈段的区别

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值