《程序员自我修养》阅读笔记-静态链接

静态链接分两步,(1)空间与地址分配,(2)符号解析与重定位。

1 空间与地址分配。空间域地址分配有两个含义,一个是输出的可执行文件的空间,一个是装载后的虚拟地址的空间。在这里我们指的是后者。在将多个目标文件(静态)链接成可执行文件的时候,链接器会将所有的代码段放在一起,会将所有的数据段放在一起。放在一起之后,其实每个符号的地址就确定了。比如说有两个目标文件。第一个目标文件中的代码段有一个符号,它的地址为X,即距离代码段的偏移为X。假设经过链接器合并之后,第一个目标文件的代码段的起始地址为A,那么在可执行文件中这个符号的地址为A+X。另外,在目标文件中,地址都是从0开始,但是在可执行文件中,地址不是从0开始。

2 符号解析与重定位。假设两个目标文件a.o,b.o,a.o里可能调用了b.o的函数或者使用了b.o中的变量,那么在a.o里,是不知道这些符号的具体地址的。但是在链接过程的“空间与地址分配”结束后,所有符号的地址就确定了,所谓的重定位就是说,要将那些还没有正确填入符号地址的地方都填上正确的符号地址。这里,就需要一种重定位表,链接器通过重定位表,才知道哪些地方需要重定位。前面说过,如果代码段里有需要重定位的地方,那么会有专门针对于代码段的重定位表(重定位表也是目标文件中的一个段,所以可能应该叫重定位段,我们这里都叫重定位表),比如.rel.text。当然,比如代码段中有多个地方需要重定位的时候,重定位表里就有很多项。其中每一项的数据结构包括两个部分:

(1)r_offset:重定位入口的偏移。这个指的是要修正的位置相对于段的偏移。

(2)r_info:这里面包括两部分,重定位入口的类型和符号。它的低八位表示重定位入口的类型(在静态链接中,一般是两种类型,R_386_32绝对寻址修正,R_386_PC32相对寻址修正),高24位表示重定位入口的符号在符号表中的下标。

那么现在就比较明显了,链接器可以从r_info的高24位知道它得符号的名字进而得到符号的地址,从r_offset中知道要修正哪里,从r_info的低八位中知道如何修正。链接器依次处理重定位表里每一项就可以确定所有的重定位信息。

下面说另外一个问题。在gcc中,支持这样一种机制:强符号与弱符号。先看代码。

1 //A.c
2 int x;
3
4 void f()
5 {
6    ++x;
7 }
//B.c
#include <stdio.h>

int x=100;

int main()
{
   f();
   printf("%d\n",x);
}

然后编译 gcc A.c B.c 得到a.out,运行./a.out最后输出了101 。这里A.c的x就是一个弱符号,B.c的x就是强符号。强符号的定义为初始化了的全局变量,弱符号为没有初始化的全局变量。在链接的时候,出现多个符号名相同的强符号的时候会链接错误,当有强符号有弱符号的时候弱符号会被覆盖。没有强符号有多个弱符号的时候最后链接器会保留占用内存最大的弱符号。所以在目标文件中,会将弱符号放在一个叫做COMMON的块(段)里,而不是放在BSS段里,因为不能确定它的大小(可能别的目标文件里的同一个弱符号占用的内存更大)。

时间: 2024-10-06 08:56:27

《程序员自我修养》阅读笔记-静态链接的相关文章

《程序员自我修养》阅读笔记-动态链接

1.动态链接的含义.动态链接就是将链接时的重定位推迟到加载时.相比于静态链接,动态链接的一个优点是可以节省内存.因为共享文件的代码可以共享.使用动态链接的时候,可执行文件和共享文件都会加载到内存.但是,如果很多可执行文件都使用了同一个动向文件的时候,共享文件的代码部分只需要装载一次,这样就达到了节省内存的目的.在这里,共享文件的数据部分在每个可执行文件中都要保存一份.所以,共享文件中跟自己的数据有关的代码就可能会变化,因为数据的地址不确定.一旦变化,就不能达到代码共享的目的了.所以,在这里,一般

《程序员自我修养》阅读笔记-目标文件里有什么

linux下的目标文件(.o文件)采用ELF格式.目标文件里采用段section的格式存储,比如代码段啊(存代码),数据段啊(初始化了的全局变量和局部静态变量),BSS段啊(未初始化的全局和局部静态变量),只读数据段啊(程序中用到的字符串)等.在所有的段外,有一个重要的结构叫做文件头.所以这里从文件头开始说. 1 文件头主要包含以下内容(没什么用的就不写了): (1)e_ident:这里面一个重要的内容就是魔数和机器的位数.魔数用来标识文件的类型,比如elf啊,a.out啊,MZ啊等.机器位数就

程序员修炼三部曲阅读笔记01

看了程序员的修炼三部曲,感触颇多. 这本书主要分为九章,第一章绪论的部分主要是讲述本身一些结构,以及本书对一般的人的作用.第二章将根据徳雷福斯模型(稍后介绍)将人分为新手到专家的五个阶段.你可以根据描述准确的定为自己所处的阶段.三四五章是将自我认识的部分,主要是正对大脑.第三章讲述的是认识你的大脑,会让你对大脑的工作模式有更好的认识,之后当然是实践的第四章-利用你的右脑.大脑也会范错误,需要我们调试,就是第五章的调试你的大脑.在对大脑有了深刻的认识之后当然是关注如何提高了.后面的几章就是关于提高

一个程序员的奋斗史 阅读笔记

感悟如下: 1.程序员这个职业在业务方面很少有似是而非的内容,是就是,不是就不是.模棱两可是要不得的,打肿脸充胖子更是大忌,不要动不动就写精通 2.优秀的程序员需要不停学习新技术,多看书,多找资料,并在实践在使用自己学到的技术,不吝惜知识的分享 3.环境很重要:大公司学习,只是有深度,小公司学习知识面广,能解决很多基本问题.但公司若想超越对手,则会选择专研度深的程序员.程序员的知识掌握能力应该是金字塔形的 4.英文的重要性,新技术新知识都是先有英文书籍资料(FUCK) 5.应届生提高技术水平是第

《程序员自我修养》阅读笔记-可执行文件装载与进程

1 装载方式:页映射.操作系统将物理内存划分成页(比如每个页大小16K),可执行文件也按照页划分.每次需要哪个页时就将其装载到物理内存中某个空闲页.若没有空闲页,可以采取一定策略将物理内存中某个页换出. 2 可执行文件装载的过程. (1)创建虚拟地址空间.分配页目录.这一步是建立虚拟空间到物理空间的映射关系. (2)读取可执行文件头,建立虚拟空间与可执行文件的映射关系.这个映射关系的作用是以后发生页错误的时候,操作系统能够从这个映射关系中知道缺页在可执行文件中的位置. (3)将cpu的指令寄存器

程序员修炼三部曲阅读笔记02

积累经验是学习和成长的关键—我们通过实践的方法学习,效果最好.然而,仅仅一开实践并不能保证成功,必须从实践中有所收获,但是面对一些常见的障碍,我们很难做到这点.你又无法强迫它,过度努力的尝试可能和不去尝试一样糟糕. 本章的开头是引用的马克吐温的话:“我们应该小心翼翼的从实践中获得智慧并且视可而止,否则我们就会线不慎坐在热炉子上的猫一样,它在也不会坐在热炉子上—这还好,但是它也不会坐在冷炉子上了”.好精辟的比喻啊,马克吐温不愧是幽默大师,猫的反应其实和人一样,过度的依赖实践,一旦实践中获得的知识是

C++程序员的自我修养–读书笔记

 1:注意不要反回指向栈内存的指针或引用,因为在函数返回时改内存已经被销毁了 2:C/C++没有办法知道指针所指的内存容量大小 当数组作为参数传递时,数组将退化成相同类型的指针 不要指望要指针参数去申请动态内存,因为函数会为产生一个临时变量指向参数的内存,当函数内分配内存时,将内存的地址赋给了临时参数,而没有给实参赋值,所有实参没有发生任何变化,应该修改的是指针所指的内容,而不是修改指针的指向,所有可以用指向指针的指针 3:重载和内联机制既可用于全局函数也可用于类的成员函数,const和vi

一、《程序员的自我修养》笔记-前言

引子:在linux上写了三年多的c了,平时遇到一些编译和链接的问题仍然很是头痛,感觉很无力,好基友推荐<程序员的自我修养>,趁着周末,速速围观. 先记录下作者在书中抛出来的问题 1.为啥程序是从main函数开始执行? 2.PE/ELF文件存的是啥? 3.如何写一个直接跑在未安装os裸机上的程序? 4.目标文件是啥?链接是啥? 5.链接为啥报错? 6.句柄到底是啥? 7.普通c/c++代码如何被编译成牧宝文件及程序在目标文件中如何存储? 8.目标文件如何被链接器链接到一起,并形成可执行文件? 9

程序员的自我修养 学习笔记(1)

本文源自在学习<程序员的自我修养>中的心得体会. 对于底层系统程序开发者来说,硬件平台可以抽象为三个主要部件,CPU.内存.I/O控制器. 早期的计算机没有复杂的图形功能,CPU和内存之间的频率差异不大,它们都是连接在同一个bus上面的.其他I/O设备,诸如显示设备.键盘.磁盘等速度比内存.CPU慢很多.为了IO设备与CPU.内存之间的协调通讯,一般每个IO设备商都有相应的IO控制器,早期的硬件结构图如下: 随着技术的进步,CPU的频率越来越高,内存跟不上CPU的速度,他们之间就需要一个转换机