-fPIC编译选项
-fPIC 作⽤于编译阶段,告诉编译器产⽣与位置⽆关代码(Position-Independent Code),则产⽣的代码中,没有绝对地址,全部使⽤相对地址,故⽽代码可以被加载器加载到内存的任意位置,都可以正确的执⾏。这正是共享库所要求的,共享库被加载时,在内存的位置不是固定的。
gcc -shared -fPIC -o 1.so 1.c
这⾥有⼀个-fPIC参数
PIC就是position independent code
PIC使.so⽂件的代码段变为真正意义上的共享
如果不加-fPIC,则加载.so⽂件的代码段时,代码段引⽤的数据对象需要重定位, 重定位会修改代码段的内容,这就造成每个使⽤这个.so⽂件代码段的进程在内核⾥都会⽣成这个.so⽂件代码段的copy.每个copy都不⼀样,取决于这个.so⽂件代码段和数据段内存映射的位置.
也就是
不加fPIC编译出来的so,是要再加载时根据加载到的位置再次重定位的.(因为它⾥⾯的代码并不是位置⽆关代码)
如果被多个应⽤程序共同使⽤,那么它们必须每个程序维护⼀份.so的代码副本了.(因为.so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)
我们总是⽤fPIC来⽣成so,也从来不⽤fPIC来⽣成.a;fPIC与动态链接可以说基本没有关系,libc.so⼀样可以不⽤fPIC编译,只是这样的so必须要在加载到⽤户程序的地址空间时重定向所有表⽬.
因此,不⽤fPIC编译so并不总是不好.
如果你满⾜以下4个需求/条件:
1.该库可能需要经常更新
2.该库需要⾮常⾼的效率(尤其是有很多全局量的使⽤时)
3.该库并不很⼤.
4.该库基本不需要被多个应⽤程序共享
如果⽤没有加这个参数的编译后的共享库,也可以使⽤的话,可能是两个原因:
1:gcc默认开启-fPIC选项
2:loader使你的代码位置⽆关
从GCC来看,shared应该是包含fPIC选项的,但似乎不是所以系统都⽀持,所以最好显式加上fPIC选项。参见如下:
`-shared'
Produce a shared object which can then be linked with other
objects to form an executable. Not all systems support this
option. For predictable results, you must also specify the same
t of options that were ud to generate code (`-fpic', `-fPIC',
or model suboptions) when you specify this option.(1)
-
fPIC 的使⽤,会⽣成 PIC 代码,.so 要求为 PIC,以达到动态链接的⽬的,否则,⽆法实现动态链接。
non-PIC 与 PIC 代码的区别主要在于 access global data, jump label 的不同。
⽐如⼀条 access global data 的指令,
non-PIC 的形势是:ld r3, var1
PIC 的形式则是:ld r3, var1-offt@GOT,意思是从 GOT 表的 index 为var1-offt 的地⽅处指⽰的地址处装载⼀个值,即var1-
offt@GOT处的4个 byte 其实就是 var1 的地址。这个地址只有在运⾏的时候才知道,是由 dynamic-loader(ld-linux.so) 填进去的。
再⽐如 jump label 指令
non-PIC 的形势是:jump printf ,意思是调⽤ printf。
PIC 的形式则是:jump printf-offt@GOT,
意思是跳到 GOT 表的 index 为 printf-offt 的地⽅处指⽰的地址去执⾏,这个地址处的代码摆放在 .plt ction,每个外部函数对应⼀段这样的代码,其功能是呼叫dynamic-loader(ld-linux.so) 来查找函数的地址(本例中是 printf),然后将其地址写到 GOT 表的
index 为 printf-offt 的地⽅,同时执⾏这个函数。这样,第2次呼叫 printf 的时候,就会直接跳到 printf 的地址,⽽不必再查找
了。
GOT 是 data ction, 是⼀个 table, 除专⽤的⼏个 entry,每个 entry 的内容可以再执⾏的时候修改;
PLT 是 text ction, 是⼀段⼀段的 code,执⾏中不需要修改。
每个 target 实现 PIC 的机制不同,但⼤同⼩异。⽐如 MIPS 没有 .plt, ⽽是叫 .stub,功能和 .plt ⼀样。
可见,动态链接执⾏很复杂,⽐静态链接执⾏时间长;但是,极⼤的节省了 size,PIC 和动态链接技术是计算机发展史上⾮常重要的⼀个⾥程碑。
gcc manul上⾯有说
-fpic If the GOT size for the linked executable exceeds a machine-specific maximum size, you get an error message from the
linker indicating that -fpic does not work; in that ca, recompile with -fPIC instead. (The maximums are 8k on the SPARC
and 32k on the m68k and RS/6000. The 386 has no such limit.)
-fPIC If supported for the target machine, emit position-independent code, suitable for dynamic linking and avoiding any limit on the size of the global offt table. This option makes a difference on the m68k, PowerPC and SPARC. Position-independent code requires special support, and therefore works only on certain machines.
关键在于GOT全局偏移量表⾥⾯的跳转项⼤⼩。
intel处理器应该是统⼀4字节,没有问题。
powerpc上由于汇编码或者机器码的特殊要求,所以跳转项分为短、长两种。
-fpic为了节约内存,在GOT⾥⾯预留了“短”长度。
⽽-fPIC则采⽤了更⼤的跳转项。