-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.该库基本不需要被多个应⽤程序共享
如果⽤没有加这个参数的编译后的共享库,也可以使⽤的话,可能是两个原因:
offset指令是什么意思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
set of options that were used 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-offset@GOT,意思是从 GOT 表的 index 为var1-offset 的地⽅处指⽰的地址处装载⼀个值,即var1-
offset@GOT处的4个 byte 其实就是 var1 的地址。这个地址只有在运⾏的时候才知道,是由 dynamic-loader(ld-linux.so) 填进去的。
再⽐如 jump label 指令
non-PIC 的形势是:jump printf ,意思是调⽤ printf。
PIC 的形式则是:jump printf-offset@GOT,
意思是跳到 GOT 表的 index 为 printf-offset 的地⽅处指⽰的地址去执⾏,这个地址处的代码摆放在 .plt section,每个外部函数对应⼀段这样的代码,其功能是呼叫dynamic-loader(ld-linux.so) 来查函数的地址(本例中是 printf),然后将其地址写到 GOT 表的
index 为 printf-offset 的地⽅,同时执⾏这个函数。这样,第2次呼叫 printf 的时候,就会直接跳到 printf 的地址,⽽不必再查
了。
GOT 是 data section, 是⼀个 table, 除专⽤的⼏个 entry,每个 entry 的内容可以再执⾏的时候修改;
PLT 是 text section, 是⼀段⼀段的 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 case, recompile with -fPIC instead. (These 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 offset 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则采⽤了更⼤的跳转项。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论