使用GCC编译一个.c文件影藏了哪些过程?

GCC四步详解

第一步:预处理(也叫预编译)

gcc -E  hello.c  -o hello.i

或者 cpp hello.c > hello.i     【cpp是预编译器】

将所有#define删除,并且展开所有的宏定义

处理所有的条件预编译指令,如#if #ifdef  #undef  #ifndef  #endif #elif

处理#include,将包含的文件插入到此处,这是一个递归的过程

删除所有注释   //   /* */

添加行号和文件名标识,以便于编译时产生的错误警告能显示行号

保留#pragma编译器指令

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

第三步:汇编
        gcc  -c  hello.s  -o  hello.o或者 as  hello.s -o  hello.o
        汇编是将第二步生成的汇编代码变成机器可执行的指令,每一个汇编语句几乎都对应一条机器指令

第四步:链接

链接动态库和静态库

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

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

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

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

可执行文件的概貌详解

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

深入挖掘 .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文件中所有的符号

使用命令gcc -c test.c编译下面这个test.c程序生成test.o文件,然后查看test.o文件结构

test.c

 
  1. /* this is a test code */

  2. /* test.c */

  3. int printf(const char *format, ...);

  4. int g_var2 = 10;

  5. int g_var2;

  6. void func(int i)

  7. {

  8. printf("%d\n",i);

  9. }

  10. int main(void)

  11. {

  12. static int static_var1 = 20;

  13. static int static_var2;

  14. int var3 = 1;

  15. int var4;

  16. func(static_var1 + static_var2 + var3 + var4);

  17. return var3;

  18. }

然后查看生成的test.o文件的结构
objdump -h test.o

行:
    .text  :代码段(存放函数的二进制机器指令)
    .data :数据段(存已初始化的局部/全局静态变量、未初始化的全局静态变量)
    .bss  :bss段(声明未初始化变量所占大小)
    .rodata :只读数据段(存放 " " 引住的只读字符串)
    .comment :注释信息段
    .node.GUN-stack :堆栈提示段
列:
    Size:段的长度
    File Off :段的所在位置(即距离文件头的偏移位置)
段的属性:
    CONTENTS:表示该段在文件中存在
    ALLOC :表示只分配了大小,但没有存内容

关于.bss段

我们说.bss段是存放未初始化的全局变量(静态与非静态)和局部静态变量的
所以我们程序中的g_var2和stactic_var2应该都在.bss段中被预留位置,所以
.bss段的size应该是8个字节,但是结果却是4个字节,怎么回事呢?
这就是不用的编译器实现不一样的原因了,有些编译器会将未初始化的全局非静态变量放在.bss段,有些则不放,只是预留一个未定义的全局变量符号,等到最终链接成可执行文件的时候再在.bss段分配空间。而我的编译器是没有将g_var2(全局未初始化的非静态变量)放在任何段
下面让我们真正的查看一下g_var2
首先,我们使用  readelf -S  test.o  查看段表(主要为了查看每个段的段号)

然后我们再使用 readelf -s  test.o看一下符号表(我们定义的变量名都是符号,包括函数名)

符号表里会显示这个符号所在的位置

我们看到static_var1和g_var1所在段的段号为3(3是.data段),static_var2所在段的段号为4(4是.bss段),而g_var2却没有被放入任何一个段,只是用COM标记了一下,那这个COM表示什么意思呢?COM标记的符号被称为弱符号,一个变量名是弱符号,则这个变量的大小在编译的时候不能被确定,而在链接之后才能确定该变量的大小。test.o文件在链接之后,g_var2会被放入.bss段(当然,也只是说明g_var2所需要的空间大小,并不会存放内容),而在程序运行的时候g_var2这样的变量才会真正去占用内存空间

强制将某变量或者某函数放入某个段

__attribute__((section(".data")))  int   g_var2;   //强制将g_var2放入.data段中

各种变量所在位置总结
    全局已初始化非静态变量、局部已初始化静态变量会被放入.data段
    全局未初始化静态变量会被放入.bss段
    全图未初始化非静态变量不会被放入任何一个段,只是用COM标记一下

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

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

    使用GCC编译一个.c文件影藏了哪些过程? GCC四步详解 第一步:预处理(也叫预编译)         gcc -E  hello.c  -o hello.i         或者 cpp hell ...

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

    基于上一篇的相关介绍,这里来实战一下: 上一篇见 https://blog.csdn.net/zwl1584671413/article/details/108146790 比如现在我这边生成了一个可 ...

  3. Linux平台Makefile文件的编写基础篇和GCC参数详解

    问:gcc中的-I.是什么意思....看到了有的是gcc -I. -I/usr/xxxxx..那个-I.是什么意思呢 最佳答案 答:-Ixxx 的意思是除了默认的头文件搜索路径(比如/usr/incl ...

  4. 浏览器解析html全过程详解

    前端文摘:深入解析浏览器的幕后工作原理 关于浏览器解析html全过程详解 输入URL到浏览器接收返回的数据的整个过程 TCP报文格式详解 IP报文格式详解 Linux IO模式及 select.pol ...

  5. WSO2安装使用的全过程详解

    WSO2安装使用的全过程详解 1. Wso2 Install 1.0 Port 1.1 Docker Install 1.2 Uninstall 2. Git Install 2.0 Port 2.1 ...

  6. 新手必看:访问url到加载全过程详解(看完不会我吃shi)

    新手必看:访问url到加载全过程详解(看完不会我吃shi) 1.放在前面:新手必须知道的那些概念 1.1 什么是IP.域名.主机名.url.服务器 1.2 http & https 1.3 O ...

  7. Apollo6.0代码Lattice算法详解——Part5: 生成横纵向轨迹

    Apollo6.0代码Lattice算法详解--Part5: 生成横纵向轨迹 0.前置知识 1.涉及主要函数 2.函数关系 3.部分函数代码详解 3.1 lattice_planner.cc中代码部分 ...

  8. 系统启动U盘制作全过程详解

    叙:之前自己的系统是家庭版的,在自己升级为专业版的时候出了问题,很生气想重装系统,但是苦于没有系统U盘,想自己制作一个系统U盘,但当时出差手里又没有带空白U盘,很尴尬~,最后自己自己在网上查了很久,试 ...

  9. 视频教程-javascript/jquery全过程详解-Java

    javascript/jquery全过程详解 资深大数据.java讲师,十年开发经验,曾经任职于北大青鸟.讯腾软件等多家知名教育机构,精通javaweb, 前端技术,J2EE技术体系,熟练使用Spri ...

最新文章

  1. YOLOv3和YOLOv4长篇核心综述(下)
  2. 基于IntelVt技术的Linux内核调试器 - 2
  3. MySQL 乐观锁与悲观锁
  4. python2.7输出语句_python2.7入门---模块(Module)
  5. DataGrid实现简单的行分组
  6. Firefox 新版本发布,Cookie 可真不好吃
  7. Java的一些基础小知识之JVM与GC (转)
  8. java 中异步消息通知,ActivityMQ的基本使用
  9. Project: cosmo ical4j
  10. 【NOIP2017提高A组冲刺11.2】救赎(数学期望)
  11. 最大公约数简便算法_三种求最大公约数的方法
  12. Android 更换App图标
  13. 【仪器常用操作方法】TDS1012示波器常用操作方法
  14. Exception in thread “main“ java.lang.ArrayStoreException解决方案(记录一下)
  15. Java写win10激活码_win10系统配置java环境 生成密钥
  16. linux鼠标箭头消失了,Ubuntu 16.04 鼠标光标消失的解决方法(右键可弹窗,可以点击)...
  17. Linux系统中CPU占用率过高问题原因分析
  18. DASAN(V5824G)大山 OLT HGU (4GE+2VOIP)配置指导
  19. vrrp mstp 配置实验(学习笔记)
  20. USB是如何诞生的?

热门文章

  1. JavaScript中split() 使用方法
  2. 一个可以设置中奖概率的抽奖程序[转]
  3. 封装运动框架多个属性
  4. 类型实现《程序员的第一年》--------------C#中System.Collections.Generic.SortedDictionary 的使用...
  5. oracle sqlcode 多条,SQL查询以连接Oracle中多个行的列值
  6. python网格搜索核函数_机器学习笔记——模型调参利器 GridSearchCV(网格搜索)参数的说明...
  7. 安徽师范大学计算机专业导师,安徽师范大学数学计算机科学学院导师介绍:罗永龙...
  8. MAC Android Studio| Error: The android gradle plugin version 3.0.0-alpha1 is too old
  9. 【控制】《复杂运动体系统的分布式协同控制与优化》-方浩老师-第7章-带有操作度及能量优化的分布式协同搬运控制
  10. 【Matlab 控制】仿真多智体一致性分析,附代码