动静态库编译及“-fpic 可重定位”








动静态库编译及“-fpic 可重定位”

编译动态库所用命令形式如下:

gcc (-fpic) -shared -o libmyfunction.so(目标库名称)  myfunction.c(源文件)

    — fpic 使输出的对象模块是按照可重定位地址方式生成的。 
    — shared指定把对应的源文件生成对应的动态链接库文件。

--------------------------------------------------------------------------------------------------------------

ar 命令的命令行格式如下: 
      ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files... 
参数archive定义库的名称, files是库文件中包含的目标文件的清单, 用空格分隔每个文件
比如创建一个静态库文件的命令如下::
      ar r libapue.a error.o errorlog.o lockreg.o

 

 

知识恶补:

What effect does '-fpic' option have on making a share lib ?

Question:

加上-fPIC参数后,编译后的文件和没有加这个参数的文件,有什么区别呢,在代码里面做了什么修改能增强它的可重定位性,或者说位置无关性呢? 而且,用没有加这个参数的编译后的共享库,也可以使用,它和加了参数后的使用起来又有什么区别呢?

邮件列表里面写的,好像position independent(位置无关)和relocate(可重定位)不是一个意思,是这样吗? 
我猜想: 
任何源代码编译后的二进制目标文件都是可重定位的,但是如果把它链接成可执行文件,那么如果这个目标文件在编译过程中不管有没有添加-fPIC,它都不存在可重定位的性质了。如果编译这些目标文件时候没有加-fPIC,然后链接成共享库,这时候这个库是没有位置无关性的,但是它具有可重定位的性质,所以照样可以被使用。 不知道我想的对不对。还有,如果我有一个静态库,可是我不想把它当静态库用,有什么办法把它转变成动态库呢(具有位置无关性的)。如果我的程序同时需要静态库和动态库,那么我就先链接静态库,然后再链接动态库,是这样吗? 

 

Answer:

不加fPIC编译出来的so,是要再加载时根据加载到的位置再次重定位的.(因为它里面的代码并不是位置无关代码)
如果被多个应用程序共同使用,那么它们必须每个程序维护一份so的代码副本了.(因为so被每个程序加载的位置都不同,显然这些重定位后的代码也不同,当然不能共享)这样就失去了共享库的好处,实际上和静态库的区别并不大,在运行时占用的内存是类似的,仅仅是二进制代码占的硬盘空间小一些.而且在加载时才重定位的开销也很大(这一点使得这种做法更加没有意义).

我们总是用fPIC来生成so,也从来不用fPIC来生成a.
fPIC与动态链接可以说基本没有关系,libc.so一样可以不用fPIC编译,只是这样的so必须要在加载到用户程序的地址空间时重定向所有表目.

因此,不用fPIC编译so并不总是不好.
如果你满足以下4个需求/条件:
1.该库可能需要经常更新
2.该库需要非常高的效率(尤其是有很多全局量的使用时)
3.该库并不很大.
4.该库基本不需要被多个应用程序共享

-------------------------------------------------------------------------------------------------------------------------------------------

&:

位置无关代码主要是在访问全局变量和全局函数的时候采用了位置无关的重定位方法,既依赖GOT和PLT来重定位. 
普通的重定位方法需要修改代码段,比如偏移地址0x100处需要重定位,loader就修改代码段的0x100处的内容,通过查找重定位信息得到具体的值.这种方法需要修改代码段的内容,对于动态连接库来说,其初衷是让多个进程共享代码段,若对其进行写操作,就会引起COW,从而失去共享。
-fPIC 选项告诉编绎器使用GOT和PLT的方法重定位,这两个都是数据段,因此避免了COW,真正实现了共享. 。如果不用-fPIC,动态连接库依然可以使用,但其重定位方法为一般方法,必然会引起COW.但也无所谓,除了性能在COW时稍微受些影响,其他也没啥啥. 
嗯,以上只是我个人的粗浅理解.还望指正。

#:
-fPIC 的使用,会生成 PIC 代码,.so 要求为 PIC,以达到动态链接的目的,否则,无法实现动态链接。 

non-PIC 与 PIC 代码的区别主要在于 access global data, jump label 的不同。 
比如一条 access global data 的指令, 
non-PIC 的形式是:ld r3, var1 
PIC 的形式则是:ld r3, ,意思是从 GOT 表的 index 为 var1-offset 的地方处 
指示的地址处装载一个值,即 处的4个 byte 其实就是 var1 的地址。这个地址只有在运行的时候才知道, 
是由 dynamic-loader(ld-linux.so) 填进去的。 

再比如 jump label 指令 
non-PIC 的形式是:jump printf ,意思是调用 printf。 
PIC 的形式则是:jump ,意思是跳到 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 和动态链接技术是计算机发展史上非常重要的一个 
里程碑。 

@:

我的是MIPS平台,有MMU的,编译so文件时 没加这个参数。 
$:

MIPS 的 ABI 在这一点上比较奇特。它 default 生成的代码就是 PIC 的,即使你不加 -fpic/-fPIC,你可以看一下在 GCC 手册里 mips 的 -mshared和-mno-shared 两个选项。这看上去不错,但是导致了在一些不需要PIC的地方效率比较低。就这几天,GCC的开发人员试图给 MIPS GCC 加入 non-PIC 的支持。一旦这个特性加入后,MIPS 有可能会和大多数平台保持一致,也就是 shared library 需要 -fpic/-fPIC。

#:

MIPS 控制 PIC 的 option 不是 -fpic/-fPIC, 而是 -mabicalls 。

MIPS 在链接一些 .o 和 .so 时,确实要求这些 .o 和 .so 都是 PIC 的,否则会报错。 
这是因为 MIPS 独特的 PIC 实现机制导致的,在 PIC 时,它会在每个 function 的开始处重新装载 GP 的值到 global pointer register,用来寻址本文件(每个 .so 或 .exe)的 GOT,但是 non-PIC 的代码不会重新装载 GP 的值到 global pointer register, 一旦从 .so 里回到 .exe 里,global pointer register 已经被破坏了,所以要求这些 .o 和 .so 都是 PIC。


摘自:http://hi.baidu.com/ownahope/item/042f900843148566d55a1111


你可能感兴趣的:(动静态库编译及“-fpic 可重定位”)