月度归档:2015年04月

与位置无关的PIC

摘自 深入理解计算机系统
共享库的一个主要目的是允许多个正在运行的进程共享存储器中的相同库代码,因为可以节约宝贵的存储器资源,那么多个进程是如何共享一个程序的copy。
方法一 给每个共享库分配一个事先预备的专用的地址空间,加载器将共享库加载至这个空间中,这种方法会产生以下缺点:
1 即使程序不被使用,也会占用了一个存储器空间, 会造成空间上的浪费。
2 如果库被修改后变大,那么还需要另外寻找适合存储库的空间,以前的空间被浪费掉。
3 如果系统拥有成千上万个库,那么系统的存储器会被这么库占用很多,造成很大的空间浪费。

方法二 编译库代码,使在任何地址都可以加载和执行这些代码,这种代码就叫做PIC(position-independent code PIC)。


PIC数据引用:
无论在存储器中的任何地方加载一个目标模块/共享目标模块,数据段都会被分配在代码段后面,估代码段的任何指令和数据段的任何变量之间都是一个运行时常量的距离。利用这一点,编译器在数据段开始的位置创建一个全局偏移量表(GOT)。(GOT)包含每个被这个目标模块引用的全局数据目标的表目。编译器还为GOT每个表目生成一个重定位记录。在加载时,动态链接器会重定位GOT中的每个表目,使得它包含正确的绝对地址。每个引用全局数据的目标模块都有一张自己的GOT。
下面代码为GOT将变量修改为绝对地址(引用地址)
call L1
L1: popl %ebx;
addl $VAROFF , %ebx
movl (%ebx) , %eax
movl (%eax) , %eax

PIC 函数调用:
call L1
L1: popl %ebx;
addl $PROCOFF ,%ebx
call *(%ebx)

链接共享库的应用

当一个应用程序加载前,动态链接器需要先加载动态链接库,有些应用程序不经过事先编译,就可以动态的加载动态共享库。
例一:为软件更新提供了便利的条件
将程序制作为共享库的形式,当有新的版本生成时,只需将新的软件覆盖以前的,下次程序启动时,自动获取了新版的应用,采用此方式更新,因为涉及更新面少,所以系统bug少,减少程序的更新难度

例二: 高性能web服务器
web服务器可以生成动态内容,例 个性化页面,动态显示内容,动态执行相关功能,以前的web服务器是通过创建子进程的方式,来生成不同的内容,随着动态链接共享库概念的提出,目前是将每个功能封装为一个共享库,当有一个来自远方的请求时,此时服务器动态的加载和链接相应的函数,并将其缓存至服务器中,当有很多类似的请求时候,可以降低系统的开销。

Linux c下如何动态调用链接共享库
#include < dlfcn.h>
void *dlopen(const char * filename,inf flag) //加载共享库 filename 成功则为指向句柄的指针,出错返回null
flag : RTLD_NOW 立即解析对外部符号的引用
RTLD_LAZY 推迟符号解析的引用

void*dlsym(void *handle,char *symbol) 成功则指向返回符号的指针 出错返回null
void *dlclose(void *handle) 卸载共享库
void char *dlerror(void) 如果上述函数执行失败,则这个函数会返回错误信息,否则返回null

例:
#include < stdio.h>
#include < dlfcn.h>
int x[2] ={1,2};
int y[2] ={3,4};
int z[2];
int main(){
void *handle;
void (*a)(int * ,int *,int * ,int);
char *err;
handle = dlopen(“/**.so”,ETLD_LAZY);
if(!handle){
prtinf(“加载共享库出错%s!\n”,dlerror());
exit(1);
}
addvec = dlsym(handle,”a”); //动态加载方法 “a”
if(deferrer() !=NULL){
prtinf(“加载共享库方法出错%s!\n”,dlerror());
exit(1);
}

a(x,y,z,2); //执行动态库中的方法
if(dlclose(handle) <0){ prtinf("共享库卸载出错%s!\n",dlerror()); exit(1); } return 0; } 动态jia za

动态链接共享库

在以前的文章中,我们发现软件需要使用大量的静态库中的函数,
一、共享库产生的原由:
1 在链接器生成可执行目标文件时,已经将库文件加载至可执行文件中,那么当静态库更新时,调用静态库的可执行目标文件中的静态库中的函数不会更新为最新的版本。
2 如果在程序中大量的调用静态库,例 常用的scanf printf 这些系统标准输入输出库,这些函数会被大量的调用,如果每次链接都会将这些文件包含进系统中,那么将会使程序的变大,增加不必要的空间浪费。
基于以上两点,就产生了一个新的事物 动态链接共享库。
———————————————–
二、共享库简介:
共享库是一个目标模块,在运行时,可以加载到任意地址的存储器上,并在存储器和一个程序链接,这就是动态链接。动态链接就由一个叫动态链接器的程序来执行而产生的效果。在linux中动态链接共享库通常以.so后缀结尾,window下.dll就是动态链接库,在一个运行环境下,一个动态链接库只能存在于一个.so或者.dll文件中。如果存在两个或以上的文件,那么将会导致所有的动态链接库加载失败,因为.so .dll文件需要被动态加载其中的代码和数据,所以动态库对于动态链接器必须唯一。