动态库基本原理和使用方法,-fPIC选项的来龙去脉

   日期:2020-10-06     浏览:100    评论:0    
核心提示:使用gcc生成so文件时,一般要加一个-fPIC选项,这个选项是啥意思?本文介绍了PIC的来龙去脉,值得一读...

使用gcc生成so文件时,一般要加一个-fPIC选项,这个选项是啥意思?本文介绍了PIC的来龙去脉。

1 链接视图看动态共享库和静态库

如果我们在主程序main.c中使用到一个函数foobar(),对main.c进行编译,编译器还不知道foobar()函数的地址,在编译阶段生成的main.o中,foobar一个未解析的引用。

链接器将main.o链接成可执行文件时,必须确定main.o中所引用的foorbar()函数的性质。如果foobar()是一个定义在其他静态目标模块(静态库或者目标文件)中的函数,那么链接器会按照静态链接的规则将main.o中的foobar()函数地址引用重定位。

gcc -o main.out mian.c ./libfoo.a

如果foobar()是定义在某个动态共享库中的函数,那么链接器就会把foobar标记为一个动态链接的符号,foobar此时是一个对动态符号的引用。不对它进行重定位。

gcc -o main.out main.c ./libfoo.so

2 执行视图看动态库

动态库发展过程有三个阶段。

2.1 演进阶段1:静态共享库

静态共享库不是静态库,是指共享库在编译时已经确定了自己在进程虚拟地址空间的位置。操作系统在某个特定的地址划分出一些地址块,为那些已知的模块预留足够的空间。在链接视图中,静态共享库在进程的虚拟地址空间的地址已经被决定了,如果后续要升级静态共享库,还是需要重新编译,链接可执行ELF。

2.2 演进阶段2:装载时重定位

装载重定位是指在链接时,对所有绝对地址的引用不作重定位,推迟到装载时再完成。程序是按照整体进行装载,程序中指令和数据相对位置不会改变,程序的基地址可能每次装载时不一样。一旦模块装载地址确定,系统就可以对程序中所有的绝对地址进行重定位。

Linux GCC选项

gcc -shared -o libbook.so book.c

使用装载时重定位,动态链接模块被装载映射到虚拟地址空间后,指令会被修改,多个进程无法共享同一份指令代码。指令被重定位以后对每个应用程序来说是不同的。演进阶段2的装载时重定位,无法解决指令部分需要在多个进程之间共享的需求。也就失去了动态链接节省内存的优势。

2.3 演进阶段3:地址无关代码(Position Independent Code, PIC)

要在多个进程之间共享指令部分,需要将指令进行分类。按照是否跨模块分两类:模块内引用和模块外部引用; 按照引用方式分两类:指令引用和数据访问。这样一共有四种组合:模块内部的函数调用、跳转,模块内部的数据访问(比如模块中定义的全局变量、静态变量),模块间的函数调用、跳转,模块间的数据访问(比如其他模块中定义的全局变量)。

类型一 模块内部函数调用、跳转

模块内部的跳转、函数调用都可以使用相对地址调用,不需要重定位。

类型二  模块内部数据访问

模块内部指令与模块内部数据之间的相对位置是固定的,只需要相对于当前指令加上固定的偏移量就可以访问模块内部数据。

类型三 模块间的数据访问

模块间的数据访问目的地址要等到装载时才能确定,ELF文件的做法是在数据段里面建一个指向这些模块间全局变量的指针数组,也被称为全局偏移表(Global Offset Table, GOT),当代码需要引用模块间全局变量时,可以通过GOT中的表项间接的引用。由于GOT是存放在数据段中,所以动态库在装载时可以被修改,每个进程都有独立的副本,相互之间不受影响。在编译时可以确定GOT相对于当前指令的偏移,编译器决定GOT内的每一项(4个字节为一项,一个指针)对应于哪一个全局变量名称,也就GOT给出了需要重定位的全局变量有哪些,以及该全局变量相对于GOT的位置。动态链接器在装载模块时会查找每个变量所在地址,然后填充GOT中的各个项,确保GOT中每个指针所指向的地址是正确的。

类型四 模块间的函数调用、跳转

与类型三类似,也是通过GOT,在GOT中存放的是目标函数的地址,当模块需要调用目标函数时,可以通过GOT进行间接跳转。

使用GCC产生地址无关代码

gcc -fPIC -shared -o libbook.so book.c

注意:这里的-fPIC中的PIC是大写,也有小写的-fpic(产生的代码相对较小,而且较快),在有些平台使用小写的-fpic选项有一些限制,而大写的-fPIC没有这个问题。绝大多数情况使用-fPIC。

可以通过以下方法查看一个so是不是PIC,如果是PIC,以下命令会有输出,如果不是PIC,则不会有任何输出。

readelf -d libbook.so | grep TEXTREL

 

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服