关于连接的一些总结

库是一种可执行代码的二进制形式,能够被操做系统载入内存执行。库有两种:静态库(.a、.lib)和动态库(.so、.dll)。linux

a. 静态库 
  之因此称为【静态库】,是由于在连接阶段,会将汇编生成的目标文件.o与引用到的库一块儿连接打包到可执行文件中。所以对应的连接方式称为静态连接。首先,静态库对函数库的连接是放在编译时期完成的。其次,程序在运行时与函数库再无瓜葛,移植方便。静态库也有缺点以下图所示,形成空间内存浪费; bash

b. 动态库
  动态库在程序编译时并不会被链接到目标代码中,而是在程序运行是才被载入。不一样的应用程序若是调用相同的库,那么在内存里只须要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行时才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只须要更新动态库便可,增量更新。app

程序的编译过程

   假设如今有以下两个c程序文件,sum.c内容以下:函数

int sum(int *a, int n){
	int i, s = 0;
	for( i = 0; i < n ; i++){
		s += a[i];
	}
	return s;
}

  主程序main.c的内容以下:工具

int sum(int *a,int n);

int array[2]  = {1, 2};

int main(){
	int val = sum(array,2 );
	return val;
}

  在linux上执行以下语句:操作系统

gcc -Og -o prog main.c sum.c

  以main.c为例,程序首先调用C预处理器,将main.c翻译成一个Ascii码的中间件,mian.i,接下来将main.i翻译成一个汇编语言文件main.s,而后调用汇编器将main.s翻译可重定位的目标文件main.o,同理会生成相应的sum.o文件。最后,运行连接器程序ld,将main.o和sum.o以及一些必要的文件组合起来,建立一个可执行文件prog。.net

  在构造可执行文件的时候,连接器要完成两个任务:翻译

  一、符号解析,符号解析的目的是将每一个全局符号引用与符号定义关联起来,便可重定位的目标文件定义了符号,而可执行的目标文件引用符号,将二者的符号相互绑定,这里的符号对应一个函数、一个全局变量、或者一个静态变量。中间件

  二、重定位,编译器和汇编器生成从0开始的的代码和数据节,连接器将符号定义和内存的位置关联起来来重定位代码和数据节,而后修改全部这些对符号的引用,使得它们指向内存的位置。对象

一些基础知识

  目标文件有三种类型:

    可重定位的目标文件:包含二进制数据和代码,其形式能够在编译时与其余可重定位合并起来,建立一个可执行的目标文件

    可执行的目标文件:包含二进制和代码,能够直接复制到内存并执行

    共享目标文件:一类特殊的可重定位目标文件,能够在加载或者运行时被动态的加载到内存执行。

  一个典型的ELF(可执行、可连接格式)的可重定位目标文件包含几个重要的部分,以下图:

 

  .text 已编译程序的机器代码

  .data 已初始化的全局和静态C变量,局部C变量的值保存在栈中。

  .bss 未初始化的全局和静态C变量,以及全部被初始化为0的静态或全局变量。

  .sysmtab 一个符号表,存放程序中定义和引用的函数以及全局或静态变量,不包含局部变量的信息。

 

连接器实际上会处理三种不一样的符号,对应于代码中不一样写法的部分:
全局符号 Global symbols
在当前模块中定义,且能够被其余代码引用的符号,例如非静态 C 函数和非静态全局变量。
外部符号 External symbols 
一样是全局符号,可是是在其余模块(也就是其余的源代码)中定义的,可是能够在当前模块中引用。
本地符号 Local symbols
在当前模块中定义,只能被当前模块引用的符号,例如静态函数和静态全局变量。
注意,Local linker symbol 并非 local program variables

连接过程

第一步 符号解析 Symbol resolution 
  符号解析会将每一个符号引用恰好和一个符号定义联系起来。汇编器生成可重定位目标文件后,内部符号都已被正确地符号解析, 外部符号可能会引用了非本模块的符号定义,汇编器没法找到符号定义,所以没法解析。 汇编器把外部符号放入”符号表“.symtab,同时把如何解析该符号的方法放入”重定位表“。连接器只知道非静态的全局变量/函数,而对于局部变量一无所知,局部非静态变量和局部静态变量的区别:

局部非静态变量会保存在栈中
局部静态变量会保存在 .bss 或 .data 中

连接器符号解析时会用到符号表:

强符号与弱符号: 函数和初始化的全局变量叫强符号, 未初始化的全局变量叫弱符号。(extern int a是一个弱符号定义, int a 也是弱符号)

符号解析规则:

 1.(定义多个强符号) 当引用符号时,该符号的符号定义有不止一个强符号定义时,会出现符号重定义错误。
 2.(定义一个强符号和一个或者多个弱符号)当引用符号时, 该符号的符号定义有个强符号定义和一个和多个弱符号定义, 使用强符号定义。
 3.(定义多个弱符号)当引用符号时, 该符号的符号定义都是弱符号时, 选择任意一个定义。

第二步 重定位 Relocation 

就是把不一样可重定位对象文件拼成可执行对象文件,有三步,以下:

1.合并可重定位目标文件中相同的节。
2.重定位节和符号定义,修改符号表。为节和符号定义分配虚拟地址。修改符号表中符号定义的值为刚分配的虚拟地址。
3.重定位节中的符号引用,修改代码段和数据段符号引用。使用重定位表.rel.text .rel.data, 修改text,data中符号引用的地址。

例以下图,多个可重定位对象文件合并成可执行文件的过程所示:

 

对重定位的理解,在重定位,有两个步骤。

  首先重定位节和符号定义,连接器将不一样目标文件全部相同类型的节合并到同一个新的聚合节中,例如,来自全部输入模块的.data节会被合并到同一个节中。而后连接器将运行时内存的地址赋给新的聚合节,赋给输入模块中定义的那个节和其中定义的每一个符号。此时,程序中的每条指令和全局变量都有一个惟一的运行时内存了。

  其次是重定位符号的引用,修改数据节和代码节中符号的引用,使得他们指向正确的运行时内存地址。

 

处理目标文件的一些工具,以下:

AR :建立静态库,插入、删除、列出和提取成员。

NM :列出一个目标文件的符号表中定义的符号。

LDD :列出一个可执行程序文件在运行时所需的共享库。

 

 

收藏:不周山之读薄 CSAPP

符号及符号解析

参考:《深刻理解计算机系统》

相关文章
相关标签/搜索