C++开始前篇,深入编译链接(补充2)
在开始链接之前,我们先了解几个概念:
一》符号的概念。
我们知道,链接的最重要的是“对符号的重定位”,而且上面提到了符号表,那什么是符号呢,在链接中,我们将函数和变量统称为符号(Symbol)。函数名和变量名就是符号名(Symbol Name)。每一个目标文件都有一个相应的符号表(Symbol Table),这个表里记录了目标文件中所用到的所有符号。每个对应的符号有一个对应的值,叫做符号值(Symbol Value)。对于函数和变量来说,符号值就是它们的地址。 除了函数和变量,还存在其他几种符号。简单了解一下。
1,全局符号:定义在本目标文件中的全局符号
2,外部符号:在本目标文件中引用,却没定义在本目标文件,称为外部符号
3,段名:由编译器产生的符号
4,局部符号:局部变量
5,行号信息 对于链接来说,其中最重要就是全局符号与外部符号。
二》关于ELF符号表的结构:
a,这是段表

b,这是符号表

我们可以找到两个表中以下的关系
1,从符号表的num下标为6开始看,static_init,是我们在定义过初始化了的局部静态变量,绑定属性是LOCAL,在段表中下标为3的段中存储,即在.data段;接下来是 static_uninit,是未初始化的局部静态变量,在段表中下标为4的段中存储,即.bss段。
2,再就是 global_init,与static_init相同,都保存在了.data段。而global_uninit,按照我们的分析,它跟staticy_uninit一样都应该保存与.bss段,这里却是COM,为什么?原来,这跟不同的语言与不同的编译器实现有关,有些编译器会将全局的未初始化变量存放在目标文件.bss段,有些则不存放,只是预留一个未定义的全局变量符号,等到最终链接成可执行文件的时候再在.bss段分配空间。(有关于此处的COM符号,在后面谈到关于COMMON块时再深入了解。)
3,func函数与main函数都是全局可见的,因为是函数,所以类型是STT_FUNC。它们都存在与.text中。
4,printf,因为我们只是使用却没有定义它,所以它在此目标文件中不存在于任何段,所以是UND(未定义)。
三》,强符号与弱符号。
对于C/C++语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。
针对强弱符号的概念,链接器会按如下规则处理和选择被定义多次的全局符号:
规则1,不允许强符号被多次定义。否则报错。
规则2,如果一个符号在某个目标文件中是强符号,在其他文件中都是弱符号,那么选择强符号。
规则3,如果一个符号在所有目标文件中都是弱符号,那么选择其中占用空间最大的一个。
-------------------------------------------------------------------------------------------------------------------------------------
了解之后,现在是时候进入静态链接了。
我们知道,链接是将所有目标文件以及库文件按照某种方式合并起来,最后生成可执行文件。是这么简单吗?是或不是,我们都要好好深入一下。
我们写两个文件a.c和b.c
a.c

extern表明shared可在其他的文件中找。
b.c

编译之后得到a.o,b.o。链接之后得到ab
分别查看这三个文件的信息



。先看ab的SIZE,稍微算一下我们可以发现,.text段和.data段它们的SIZE关系都是ab(SIZE)=a.o(SIZE)+b.o(SIZE);因此可以得出这是按相似段合并的。
。再看VMA,这是Virtual Memory Address,即虚拟地址。LMA表示Load Memory Addre1ss,即加载地址.除了在某些嵌入式系统不同,一般两个值都相同。a.o 和b.o中VMA和LMA都是0,因为编译阶段并不分配内存地址。
。。这种合并方式一般采用一种叫做两步链接的方法,
。。。第一步,空间与地址分配。
问题:分配的是什么空间?
此处的空间有两个含义,一是输出在可执行文件中的空间,二是在装载后的虚拟地址中的虚拟地址空间。我们可以看到,ab文件中并没有.bss段,原来对于.bss这样的段来说,分配空间的意义只局限于虚拟地址空间。在我们着重考虑虚拟地址空间。
总结一下第一步都干了些啥事:
链接之前,目标文件中所有段的VMA都是0,链接时,链接器按照相似段合并的方法在一个整合了两个目标文件的.text和.data段,并且给这些段分配了虚拟空间,也就有了虚拟地址。有了段的地址之后,链接器便开始计算每个段中的符号地址,因各符号在各段中的相对位置都是固定的,因此根据偏移量,可以得到各个符号的正确的虚拟地址。
举个栗子:a.o中.text的虚拟地址是0x80488094,在.text段中main的偏移为X,则main的虚拟地址为0x80488094+X。
。。。第二步,符号解析与重定位。
完成了第一步空间与地址的分配后,链接器根据第一步收集到的信息,读取输入文件中段的数据,重定位信息并且进行符号解析和重定位、调整代码中的地址等。
OK,我们详细的来研究一下。
看一下a.o的反汇编代码:

程序的代码里面使用的都是虚拟地址,从上面反汇编结果可以看出 在目标文件a.o中,main的起始地址是0,因为还没有分配空间。等到空间分配完成后,各个函数才会确定自己在虚拟地址空间中的位置;


仔细观察这两行 ,,第11行 “c7 44 24 04” 是movl指令码, “00 00 00 00”这是shared的地址。
第20行 “E8” 是相对偏移调用指令call的指令码,“FC FF FF FF”是目的地址相对于下一条指令的偏移。
当源代码a.c被编译成目标文件时,编译器并不知道shared和swap的地址,所以编译器暂时把0当做shared的地址;关于FC FF FF FF 是被调用函数的相对于调用指令的下一条指令的偏移量,这也只是一个临时的假地址,是常量-4的补码形式。
现在我们再看一看链接之后的结果:

我们发现,从链接结果来看,
原11行

原20行

它们的地址都被重写了。原20行,call指令的下一条是leave 它的地址是 80480b9 所以相对于leave指令偏移量为0x00000003 的地址为8048b9+3=8048bc。刚好是swap的地址!
说到这儿,其实重定位的过程就是个符号解析的过程,在链接器扫描完所有的输入目标文件之后,所有这些未定义的符号都应该能在全局符号表中找到,否则链接器就会报符号未定义错误。(symbol UND).
OK ,就到这儿,下一节,再来学习上次说到的COMMON块、静态库链接相关的知识。
C++开始前篇,深入编译链接(补充2)的更多相关文章
- C++开始前篇,深入编译链接(3)
一,COMMON块 什么是COMMON块,这是一种机制,早期的Fortran没有动态分配空间的机制,程序员必须事先声明它所需要的临时使用空间的大小.Fortran把这种空间叫做COMMON块,当不同的 ...
- C++开始前篇,深入编译链接
C++开始,为什么要写这个东西,因为按照课堂进度的话,现在的C++已经学到模板以及重载了,有时却仍然因为一些小问题无法解答,原因是忘记了开始时学到的知识,深知不能像猴子掰棒子一样,掰一个扔一个,因此, ...
- linux 编译,链接和加载
1. 序 最近在折腾各种.so,碰到了一些问题,一开始对于很多错误也没有头绪,茫然不知所措.索性化了一天多时间将<<程序员的自我修养—链接.装载与库>>中部分内容略读了一遍 ...
- 【原创】构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下前篇)—简单的优化措施
原文:[原创]构建高性能ASP.NET站点 第六章-性能瓶颈诊断与初步调优(下前篇)-简单的优化措施 构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下前篇)—简单的优化措施 前言:本篇 ...
- webpack2 前篇
webpack2 前篇 #webpack 前两天用了一天半时间琢磨了下webpack2,想起去年这时候,面对webpack1那样恶心的文档,前前后后搞了好几次才摸索清楚,那真是吐了. 划重点 其实we ...
- 【C编程基础】C编译链接命令gccc
1.gcc安装 rpm -qa|grep gcc ==>检查gcc是否安装 gcc -v ==>检查gcc版本 yum -y install gcc ==>安装gcc 2.基本语法 ...
- G++编译链接的那些事
语言 CPP 前言 虽然 VSCodeC++ 编辑器非常受大家的欢迎,无论是大佬还是小白都说对其爱不释手... 我...用了一段时间后发现实在是麻烦,配置往往花费我大量时间.可以说真的是吃力不 ...
- 【原创】Linux下编译链接中常见问题总结
前言 一直以来对Linux下编译链接产生的问题没有好好重视起来,出现问题就度娘一下,很多时候的确是在搜索帮助下解决了BUG,但由于对原因不求甚解,没有细细研究,结果总是在遇到在BUG时弄得手忙脚乱得. ...
- 关于tcc、tlink的编译链接机制的研究
1.学习过程 在c:\下建立文件夹c,并将编译器tcc.exe.连接器tlink.exe.相关文件c0s.obj.cs.lib.emu.lib.maths.lib放入文件夹中. 要搭建一个简单的C语言 ...
- ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇
原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇 第三章 为控件添加事件 好了,我们之前以前开发一个控件.而且也添加了属性,开发也很规范,但是那个控件还差最后一点:添加事件. 系列 ...
随机推荐
- Bzoj1305 [CQOI2009]dance跳舞
Time Limit: 5 Sec Memory Limit: 162 MBSubmit: 2925 Solved: 1221 Description 一次舞会有n个男孩和n个女孩.每首曲子开始时 ...
- 二叉排序树(BST)创建,删除,查找操作
binary search tree,中文翻译为二叉搜索树.二叉查找树或者二叉排序树.简称为BST 一:二叉搜索树的定义 他的定义与树的定义是类似的,也是一个递归的定义: 1.要么是一棵空树 2.如果 ...
- win10 右键菜单添加Git Hash Here
1.通过在"运行"中输入'regedit',打开注册表. 2.找到[HKEY_CLASSES_ROOT\Directory\Background]. 3.在[Background] ...
- STL的使用
Vector:不定长数组 Vector是C++里的不定长数组,相比传统数组vector主要更灵活,便于节省空间,邻接表的实现等.而且它在STL中时间效率也很高效:几乎与数组不相上下. #include ...
- 微信学习总结 02 ngrok 部署本机代码,使外网可以访问
一 什么是ngrok ngrok is a reverse proxy that creates a secure tunnel from a public endpoint to a locally ...
- 关于ACM的总结
看了不少大神的退役帖,今天终于要本弱装一波逼祭奠一下我关于ACM的回忆. 从大二上开始接触到大三下结束,接近两年的时间,对于大神们来说两年的确算不上时间,然而对于本弱来说就是大学的一半时光.大一的懵懂 ...
- C内嵌汇编-格式
C内嵌汇编-格式: __asm__(汇编语句部分:输出部分:输入部分破坏描述部分);C内嵌汇编以关键字"__asm__"或"asm"开始, 下辖四个部分, 各部 ...
- Emotion Debt 2017/1/6
原文 We can't move beyond the past until our emotional debts are paid. Many people today live under th ...
- C# 数据批量插入到数据库SqlBulkCopy(源数据类型:List<T> Or DataTable)
/*_____________________ List<T>类型数据 To Sql_______________________________*/ /// <summary& ...
- RabbitMQ学习系列(五): RPC 远程过程调用
前面讲过一些RabbitMQ的安装和用法,也说了说RabbitMQ在一般的业务场景下如何使用.不知道的可以看我前面的博客,http://www.cnblogs.com/zhangweizhong/ca ...