在开始链接之前,我们先了解几个概念:

一》符号的概念。

  我们知道,链接的最重要的是“对符号的重定位”,而且上面提到了符号表,那什么是符号呢,在链接中,我们将函数和变量统称为符号(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)的更多相关文章

  1. C++开始前篇,深入编译链接(3)

    一,COMMON块 什么是COMMON块,这是一种机制,早期的Fortran没有动态分配空间的机制,程序员必须事先声明它所需要的临时使用空间的大小.Fortran把这种空间叫做COMMON块,当不同的 ...

  2. C++开始前篇,深入编译链接

    C++开始,为什么要写这个东西,因为按照课堂进度的话,现在的C++已经学到模板以及重载了,有时却仍然因为一些小问题无法解答,原因是忘记了开始时学到的知识,深知不能像猴子掰棒子一样,掰一个扔一个,因此, ...

  3. linux 编译,链接和加载

    1.   序 最近在折腾各种.so,碰到了一些问题,一开始对于很多错误也没有头绪,茫然不知所措.索性化了一天多时间将<<程序员的自我修养—链接.装载与库>>中部分内容略读了一遍 ...

  4. 【原创】构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下前篇)—简单的优化措施

    原文:[原创]构建高性能ASP.NET站点 第六章-性能瓶颈诊断与初步调优(下前篇)-简单的优化措施 构建高性能ASP.NET站点 第六章—性能瓶颈诊断与初步调优(下前篇)—简单的优化措施 前言:本篇 ...

  5. webpack2 前篇

    webpack2 前篇 #webpack 前两天用了一天半时间琢磨了下webpack2,想起去年这时候,面对webpack1那样恶心的文档,前前后后搞了好几次才摸索清楚,那真是吐了. 划重点 其实we ...

  6. 【C编程基础】C编译链接命令gccc

    1.gcc安装 rpm -qa|grep gcc ==>检查gcc是否安装 gcc -v ==>检查gcc版本 yum -y install gcc ==>安装gcc  2.基本语法 ...

  7. G++编译链接的那些事

    语言 CPP 前言   虽然 VSCodeC++ 编辑器非常受大家的欢迎,无论是大佬还是小白都说对其爱不释手...   我...用了一段时间后发现实在是麻烦,配置往往花费我大量时间.可以说真的是吃力不 ...

  8. 【原创】Linux下编译链接中常见问题总结

    前言 一直以来对Linux下编译链接产生的问题没有好好重视起来,出现问题就度娘一下,很多时候的确是在搜索帮助下解决了BUG,但由于对原因不求甚解,没有细细研究,结果总是在遇到在BUG时弄得手忙脚乱得. ...

  9. 关于tcc、tlink的编译链接机制的研究

    1.学习过程 在c:\下建立文件夹c,并将编译器tcc.exe.连接器tlink.exe.相关文件c0s.obj.cs.lib.emu.lib.maths.lib放入文件夹中. 要搭建一个简单的C语言 ...

  10. ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇

    原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇 第三章 为控件添加事件 好了,我们之前以前开发一个控件.而且也添加了属性,开发也很规范,但是那个控件还差最后一点:添加事件. 系列 ...

随机推荐

  1. win7搭建双系统ubuntu

    参考链接:http://www.linuxidc.com/Linux/2014-10/108430.htm 0.下载EasyBCD软件和ubuntu镜像1.在win7,右键我的电脑,磁盘管理,压缩卷, ...

  2. 简洁的div翻转案例

    <!DOCTYPE html> <html > <head> <title>test</title> <meta http-equiv ...

  3. Django基础,Day2 - 编写urls,views,models

    编写views views:作为MVC中的C,接收用户的输入,调用数据库Model层和业务逻辑Model层,处理后将处理结果渲染到V层中去. polls/views.py: from django.h ...

  4. 微信jsApI及微信分享对应在手机浏览器的调用总结。

    摘录自别人的博客: 第一篇:微信内置浏览器的JsAPI(WeixinJSBridge续) 之前有写过几篇关于微信内置浏览器(WebView)中特有的Javascript API(Javascript ...

  5. 2016百度之星 资格赛ABCDE

    看题:http://bestcoder.hdu.edu.cn/contests/contest_show.php?cid=690 交题:http://acm.hdu.edu.cn/search.php ...

  6. AngularJS学习第一课

    AngularJS2与AngularJS1有很大的不同. AngularJS2支持ECMAScript6,为了提升性能,支持移动化开发(安卓.ios平台),因此升级为AngularJS 2,Angul ...

  7. Android 只开启一个Activity实例

    在一个Activity中,多次调用startActivity()来启动另一个Activity,要想只生成一个Activity实例,方法有两种. 方法一:设置起动模式 一个Activity有四种启动模式 ...

  8. Jquery制作--美化下拉框

    平常我们用的原生select下拉框,大部分样式没办法修改,导致在不同的浏览器里面会跟设计图的风格大相径庭.所以为了能让它美化起来,就用JQ模拟了一个下拉框,可以随意定义样式.原生的下拉框也保留在div ...

  9. VMware ubuntu中执行python文件的操作小结

  10. LINQ 函数的实战演练测试

    1.首先定义一个图书类.专门存放图书的属性信息. 代码如下:   //Book.cs using System; namespace LinqTest { public class Book { pu ...