-fPIC, -fpic, -fpie, -fPIE

-fPIC与-fpic都是在编译时加入的选项,用于生成位置无关的代码(Position-Independent-Code)。这两个选项都是可以使代码在加载到内存时使用相对地址,所有对固定地址的访问都通过全局偏移表(GOT)来实现。-fPIC和-fpic最大的区别在于是否对GOT的大小有限制。-fPIC对GOT表大小无限制,所以如果在不确定的情况下,使用-fPIC是更好的选择。
-fPIE与-fpie是等价的。这个选项与-fPIC/-fpic大致相同,不同点在于:-fPIC用于生成动态库,-fPIE用与生成可执行文件。-fPIE用来生成位置无关的可执行代码。

 

PIC实现原理:

    (1)GOT:在动态库的数据段增加GOT(Global Offset Table),该表的每一项是符号到地址的绝对映射。由于代码段到数据段的偏移是固定的,因此可以在编译时确定代码段中的某个符号到GOT特定项之间的偏移。这样,代码段中的符号偏移就可以在编译时确定了,在加载时也无需修改代码段的内容,只需要填写位于数据段的GOT的所有项的符号的绝对地址就完成了。因为数据段本来就是进程间不共享,每个进程独立的一份,因此GOT的设计完全解决了以上两个问题,从而达到两个目的:1,代码段可以在多进程间共享;2,代码段是只读的。

    (2)PLT:PLT是 Program Linkage Table 的缩写,即程序链接表,PLT的出现是为了延时定位的目的。一个动态库中的函数往往要远多于全局变量,并且被调用的函数往往少于定义的函数。GOT中包含了该动态库中的所有的全局变量的映射,并且在连接器加载时解析所有的全局变量的地址。如果用同样的方式去处理函数调用符号,则开销会非常大。因此在代码段设计了一个PLT表,每一项其实是个代码段,用于执行如下逻辑:首次访问时,解析参数和向GOT填写函数地址,后续访问直接访问GOT中的函数地址。如此达到了延时定位的目的。

    因此,一个PIC的动态库中,对全局变量使用GOT来映射,对函数调用使用PLT+GOT来映射,从而达到共享库代码段复用,代码段安全访问的目的。而这些就是 PIC 的意义。

你可能感兴趣的:(Linux)