gcc(一)编译过程

一    编译过程

(1)    问题引入

问题: C语言程序从'源代码'到'二进制程序'都'经历了'那些过程?

说明: 本文以'Centos7.7'下C语言的'编译过程'为例

备注: 以一个'初学者的角度'尽可能详细解读

(2)    源码准备

(3)    编译过程

①    编译前预处理

预处理'处理什么': 读取c源程序,对其中的'伪指令(以# 开头的指令)'和'特殊符号'进行处理

++++++++++++++++++'伪指令'主要包括以下'四个方面'++++++++++++++++++

1) '宏'定义指令    -->'替换'

如: '# define PRICE 12'、'# undef'等

   -- 对于'前一个'伪指令,预编译所要做的是将程序中的'所有PRICE'用12'替换',但作为'字符串常量'的 Name则'不被'替换

   -- 对于'后者',则将'取消'对某个宏的定义,使'以后'该串的出现不再被替换

2) '条件编译'指令   -->'过滤'

如:'#if 0'、'#ifdef'、'#ifndef'、'#else'、'#elif'、'#endif'等。

   -- 作用:这些'伪指令'的引入使得程序员可以'通过定义不同的宏'来决定'编译程序'对'哪些代码进行处理'

   -- 效果:预编译程序将根据有关的文件,将那些'不必要'的代码'过滤'掉

3) '头文件'包含指令  -->'展开到ceshi.c中'

如: '#include "FileName"' 、'#include < FileName>' 等

备注: 在头文件中一般用伪指令'#define'定义了'大量的宏(最常见的是字符常量)',同时包含有各种'外部符号'的声明

   -- 目的: 采用'头文件的目的'主要是为了使'某些定义'可以供'多个不同'的C源程序使用

   -- 使用: 在需要'用到这些定义'的C源程序中,只需加上一条'# include语句'即可,而'不必'再在此文件中将这些定义'重复'一遍

   -- 预编译程序将把'头文件中的定义'统统都'加入'到它所产生的'输出文件'中,以供编译程序对之进行处理

补充: 包含到c源程序中的'头文件'可以是'系统提供'的,这些头文件一般被'放在/usr/include'目录下

细节: 在程序中'# include'它们要使用'<尖括号>'。另外开发人员也可以'定义自己的头文件',这些文件一般与c源程序'放在同一目录下',此时在# include中要用"双引号"

4) '特殊符号',预编译程序可以'识别'一些特殊的符号

   --  在源程序中'出现的LINE标识'将被解释为'当前行号(十进制数)'

   --  FILE则被解释为当前被编译的'C源程序的名称',预编译程序对于在源程序中出现的这些串将用'合适的值'进行'替换'

5) '小结'

   -- 作用: 预编译程序所完成的'基本上'是对源程序的"替代"工作

   -- 效果: 经过此种替代,生成一个'没有'宏定义、没有条件编译指令、没有特殊符号的输出文件

   -- 补充: 这个文件的含义'同没有经过预处理'的源文件是相同的,但'内容有所不同'

1)进行预处理

gcc -E ceshi.c -o ceshi.i

备注: -E 实质是调用'cpp'命令来完成'预处理'

'等价': cpp ceshi.c  -o test.i

2)对比文件大小类型

1)预处理之后'文件体积变大'

2)预处理之后的程序'还是文本',可以用'文本编辑器'打开

3)查看预处理后的文件

'#include' 头文件处理:直接把'stdio.h'从操作系统中找出来,把文件的内容'复制'一份,然后粘贴到'ceshi.h'中

4)对比系统stdio.h文件

②    编译

1)进行编译

1)这里的编译'不是'指程序从源文件到二进制程序的全部过程,'而是'指将经过'预处理之后的程序'转换成'特定汇编代码'(assembly code)的过程

编译的'指令'如下: gcc -S  ceshi.i -o ceshi.s

强调: 上述命令中'-S'让编译器在'编译之后停止','不进行'后续过程

效果: 编译过程'完成后',将生成程序的'汇编代码ceshi.s',也是'文本'文件

实质:  编译是将'预处理过的c语言文件'编译为'汇编语言',等待'汇编时'转换为'机器码(二进制)'

2) -S参数

-S参数: '只'编译、'不'汇编'、不'链接

cc、gcc、g++、CC的区别

2)汇编作用

+++++++++++'汇编阶段的作用'+++++++++++

1)检查语法,语法'有错误',则退出

2)语法正确,则生成对应的'汇编文件'

3)查看汇编文件

汇编指令参考

③    汇编

linux下两种汇编工具

1)-c参数

效果: 进行'编译'和'汇编',不进行'链接' --> 翻译成为'机器指令'

备注: 如果已经'编译'过则不会再'编译'

2)进行汇编

汇编过程将上一步的'汇编代码(assemble code)'转换成'机器码(machine code)',这一步产生的文件叫做'目标文件',是'二进制'格式

 gcc -c ceshi.s -o ceshi.o

备注: gcc汇编过程底层'通过as'命令完成:

 as ceshi.s -o ceshi.o

说明: 每一个'源文件'产生一个'目标'文件

3).o文件

目标文件:就是源代码'经过汇编后',但'未进行链接'的那些'中间'文件

说明: Linux下的'.o文件'就是'目标'文件

注意: 目标文件和可执行文件内容和格式'几乎'都一样,所以我们可以'广义地'将目标文件和可执行文化看成'一类型'文件,他们都是'按照ELF文件格式'存储的

4)查看.o文件内容

++++++++++++++++++'行'++++++++++++++++++

.text:           '代码'段(存放函数的二进制机器指令)

.data:           '数据'段(存已初始化的局部/全局静态变量、未初始化的全局静态变量)

.bss:            'bss'段(声明未初始化变量所占大小)

.rodata:         '只读'数据段(存放" "引住的只读字符串)

.comment:        '注释'信息段

.node.GUN-stack: '堆栈'提示段

++++++++++++++++++'列'++++++++++++++++++

Size:段的'长度'

File Off:段的'所在位置'(即距离文件头的'偏移'位置)

段的属性:
    
  --  CONTENTS:表示该段'在文件中存在'
    
  --  ALLOC:表示'只分配了大小',但没有存内容

5)补充

1)以'二进制格式'打开

vim -b ceshi.o

2)然后'用xxd'把文件转换成'十六进制'格式-->'覆盖原文件'

:%!xxd

'等价': xxd ceshi.o

vim修改二进制文件

++++++++++++++++深入'挖掘 .o文件'++++++++++++++++

objdump  -h   xxxx.o    打印'主要段'的信息

objdump  -x   xxxx.o    打印更多的'详细'信息

objdump  -s   xxx.o     将所有段的内容'以16进制方式'打印出来

objdump  -d   xxx.o     或者-S将所有包含指令的段反汇编

objdump  -t   xxx.o     查看'所有的符号'以及他们'所在段'
            
readelf  -h   xxx.o     查看.o文件的'文件头'详细信息
            
readelf  -S   xxx.o     显示.o文件中的'所有段',即查看'段表'
            
size xxx.o              查看.o文件中'各个段'所占大小
            
nm xxx.o                查看.o文件中'所有的符号'

④    链接

链接过程将'多个目标文件'以及'所需的库文件(.so等)'链接成最终的'可执行文件'(executable file)

1)尝试直接运行

2)分析原因

3)动态链接

备注: g默认是'动态'链接

效果: 将'.o系列的目标文件'和'系统库'和'自定义库'进行'链接'

实质: 将'ceshi.c'中所用到的'函数'等'位置(position)'信息链接进'ceshi.o'文件中形成'ceshi-dynamic';'运行时'根据'提示信息'从相应的'库'中查找,然后'执行'

补充: 这里没有指定'库',说明使用系统默认'库' --> ' /usr/lib64/'、'/usr/local/lib64'

4)静态链接

5)二者对比

'静态'链接后,'对应的二进制文件',既有'源文件'自身编译的二进制文件,'还有'对应'库'的代码(复制一份),所以'体积'比较大

优点: 不需要'依赖'其它环境,'Windows'一般采用该种方式

++++++++++++++'分割线'++++++++++++++

'动态'链接后,对应的二进制文件',既有'源文件'自身编译的二进制文件,还有'对应'库的'位置(position)信息-->体积较小',运行时才去'加载'

缺点: 对环境有依赖,与'操作系统'相关的类库'强耦合'

⑤    相关参考

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

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

编译过程使用的工具

大神的撰写

GCC全过程详解+剖析生成的.o文件

华为博客参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值