1代码

点击查看代码
**sum.cpp**
int gdata=10;
int sum(int a,int b){
return a+b;
} **main.cpp** extern int gdata;
int sum(int , int );
int data=20;
int main(){
int a =gdata;
int b=data;
int ret=sum(a,b);
return 0;
}

1:编译

需要关注的几个点

1: .o 文件的格式组成是什么样子?

2: .exe 文件的组成格式是什么样子?

3: "所有.o文件段的合并 符号表合并后,进行符号解析"

4: "符号的重定位(重定向)"

5: "符号表的输出"=> "符号"

6: 符号什么时候分配虚拟地址?

预编译

以#开头的命令

除#pragma lib -> 链接阶段处理

除#pragma link -> 链接阶段处理

编译

语法分析,语义分析,代码优化 gcc g++

编译后生成相应平台的 汇编代码 X86 和 AT&T

链接

链接所有 .o文件和 静态库文件

.o 文件 主要是由以下组成

elf 文件头

.text

.data

.bss

.symbal

.section table

....

符号表中,在自己文件中定义的,那就是符号定义,如果是引用外部的就 是 "UND"符号引用

符号表中的符号 都没有分配地址,如下图,所以编译过程中,符号是不分配虚拟地址的,是在链接的时候分配

经过了上面的 预编译-》编译-》汇编 各个阶段后 下面开始进入了 链接阶段

main.o 文件 sum.o 文件

上面我们看到 .o 文件是由各个段组成的,所以进入链接阶段的时候

第一步 将各个.o 文件 的各个段合并

main.o 文件的 .text段 与 sum.o 文件的 .text 段合并

main.o 文件的 .data段 与 sum.o 文件的 .data 段合并

main.o 文件的 .bss段 与 sum.o 文件的 .bss 段合并

main.o 文件的 符号表 与 sum.o 文件的符号表 段合并

第二步 非常重要的一点是 在main.o文件的符号表与sum.o文件的符号表段合并的时候,需要进行符号解析

什么是"符号解析"?

所有对符号的引用,都要找到该符号定义的地方 “符号的引用” 即符号为 UND形式, 要找到该符号定义的地方即要找到该符号是在.text 段中定义还是在.data段中定义,

例如:链接器发现main.o文件的sum函数和gdata是UND形式的,那么链接器会去其他文件中找到sum和gdata的定义,如果没找到,那么链接器报错"符号未定义",

如果链接器找到了多个,那么链接器也会报错 “符号重定义”,所以在整个工程中,全局的名字是不能重名的,否则会产生冲突.

符号解析成功以后 就开始回给所有的符号分配地址

第三步 "符号重定向"

在符号解析成功以后并且给所有的符号分配地址后,需要继续做 "符号重定向"

在我们指令编译汇编生成.O文件的时候,生成的指令中的符号的地址都是用0 代理,如下图

现在我们需要将给符号分配好的地址 将指令中的这些0地址重新修正

现在我看下链接后的情况

符号表情况

指令情况

所以现在我们知道 “符号是在什么时候分配地址”, 在链接第一阶段 符号解析成功后

可执行文件 a.out 和 .O文件的组成方式很像,但是还是有一点区别

在a.out 可执行文件中 增加了 “program headers” , a.out 文件中不是 所有的内容都会加载到内存中的,这个

"program headers"中指定了需要加载哪些到内存中

上图中的 有两个load 就是需要加载到内存中的.


下面我们再看看可执行程序加载到内存过程

<三>从编译器角度理解C++代码编译和链接原理的更多相关文章

  1. Linux | GCC如何实现代码编译&&汇编&&链接过程

      正文: 每次我们程序员所写的 代码 是给程序员看的呢?还是给电脑看的?其实我们所写的代码只是我们程序员之间交流的一样特殊语言,电脑是看不懂的.那么我们如何实现人机交流呢?这就不得不请出我们我们今天 ...

  2. C++模板声明与实现分开--由此想到的编译,链接原理

    参考了以下两篇文章: C++编译链接原理简介  语言程序编译过程 2 问题来源:当模板文件的实现与声明分开在不同文件中时,链接时会提示找不到相应模板函数,如下 一,编译和链接的大概原理: 1,编译,遍 ...

  3. 从编译器角度理解C++中的引用和指针

    欲分析指针和引用,则要分析变量名和地址之间的关系(不管你理解还是不理解,无论你是从老师那里听到的,还是网上看到的,应该都知道两句话:1. 指针就是地址,2.引用就是给变量起个别名) 所以我们就要来分析 ...

  4. 多态原理探究-从C++编译器角度理解多态的实现原理

    理论知识: 当类中声明虚函数时,编译器会在类中生成一个虚函数表. 虚函数表是一个存储类成员函数指针的数据结构. 虚函数表是由编译器自动生成与维护的. virtual成员函数会被编译器放入虚函数表中. ...

  5. 小师妹学JVM之:深入理解JIT和编译优化-你看不懂系列

    目录 简介 JIT编译器 Tiered Compilation分层编译 OSR(On-Stack Replacement) Deoptimization 常见的编译优化举例 Inlining内联 Br ...

  6. 如何提升代码编译的速度 iOS

    前阵子有遇到代码编译速度慢的问题,特别是在swift和object-c混编的过程中问题很突显. 网上找到一篇蛮好的文章里面又一些解决方法 推荐一下 http://www.open-open.com/l ...

  7. Linux - Unix环境高级编程(第三版) 代码编译

    Unix环境高级编程(第三版) 代码编译 本文地址:http://blog.csdn.net/caroline_wendy 时间:2014.10.2 1. 下载代码:http://www.apuebo ...

  8. 编译器角度看C++复制构造函数

    [C++对象模型]复制构造函数的建构操作 关于复制构造函数的简单介绍,可以看我以前写过的一篇文章C++复制控制之复制构造函数该文章中介绍了复制构造函数的定义.调用时机.也对编译器合成的复制构造函数行为 ...

  9. 第6章 Python中的动态可执行方法 第6.1节 Python代码编译

    在介绍动态可执行方法前,本节先介绍一下Python代码编译有关的知识,因为部分内容和动态执行有些关联. 一.    Python解释器的功能 Python虽然是解释型语言,但Python代码也是可编译 ...

随机推荐

  1. DP の 百题大过关(5/100)

      动态规划自古以来是DALAO凌虐萌新的分水岭,但有些OIer认为并没有这么重要--会打暴力,大不了记忆化.但是其实,动态规划学得好不好,可以彰显出一个OIerOIer的基本素养--能否富有逻辑地思 ...

  2. odoo 14 Debug 调试

    1 # PDB的用法 2 # PDB是用来调试运行代码的. 3 # 如何启动PDB(启动之后你可以调用odoo任何模块中的方法) 4 # ./odoo-bin shell --log-level=de ...

  3. Java多线程开发系列之五:Springboot 中异步请求方法的使用

    Springboot 中异步线程的使用在过往的后台开发中,我们往往使用java自带的线程或线程池,来进行异步的调用.这对于效果来说没什么,甚至可以让开发人员对底层的状况更清晰,但是对于代码的易读性和可 ...

  4. Unity3D学习笔记12——渲染纹理

    目录 1. 概述 2. 详论 3. 问题 1. 概述 在文章<Unity3D学习笔记11--后处理>中论述了后处理是帧缓存(Framebuffer)技术实现之一:而另外一个帧缓存技术实现就 ...

  5. Shell第三章《for循环》

    Shell循环:for 语法结构: for 变量名 [ in 取值列表 ] do 循环体 done 需求:自动创建10个用户 #!/bin/bash read -p "请输入你要创建的用户名 ...

  6. 【JDBC】学习路径10-c3p0数据源的使用(JDBC完结)

    第一章:下载 c3p0官网:https://www.mchange.com/projects/c3p0/ 这个是SourceForge提供的下载地址:https://sourceforge.net/p ...

  7. Mysql8.0修改lower_case_table_names参数导致重启失败

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. GreatSQL是MySQL的国产分支版本,使用上与MySQL一致. 事件起因:在测试一个数据迁移工具时,源端oracle ...

  8. vue2和vue3的区别?

    vue2和vue3的主要区别在于以下几点: 1.生命周期函数钩子不同 2.数据双向绑定原理不同 3.定义变量和方法不同 4.指令和插槽的使用不同 5.API类型不同 6.是否支持碎片 7.父子组件之间 ...

  9. Android Notification使用

    一 Notification的类别 1.状态栏和抽屉式通知 //获取NotificationManager对象 val notificationManager = getSystemService(N ...

  10. KingbaseFlySync 版本升级

    关键字: KingbaseFlySync.Linux.x86_64.mips64el.aarch64.Java 拓扑图: 客户现场源端和目标端写在一个flysync.ini中,所以不单独把目标端拿出来 ...