关于共享库的那些事儿
零散的知识点 不断添加
关于 -fPIC
-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 AArch64, m68k, PowerPC and SPARC.
Position-independent code requires special support, and therefore works only on certain
machines.
When this flag is set, the macros "__pic__" and "__PIC__" are defined to 2.
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个需求/条件:
- 该库可能需要经常更新
- 该库需要非常高的效率(尤其是有很多全局量的使用时)
- 该库并不很大.
- 该库基本不需要被多个应用程序共享
不加fPIC编译的 so文件的优点是加载速度比较快。
如果用没有加这个参数的编译后的共享库,也可以使用的话,可能是两个原因:
- gcc默认开启-fPIC选项
- loader使你的代码位置无关
从GCC来看,shared应该是包含fPIC选项的,但似乎不是所以系统都支持,所以最好显式加上fPIC选项
-fpic为了节约内存,在GOT里面预留了“短”长度。
而-fPIC则采用了更大的跳转项
-- 老徐 gcc编译参数-fPIC的一些问题 http://blog.sina.com.cn/s/blog_54f82cc201011op1.html
关于链接, 注意顺序
gcc main.c -L ./ -lcom # 可以编译
gcc -L ./ -lcom main.c # 不能编译
关于寻找库
程序在编译链接时,编译器是按照如下顺序来查找动态链接库(共享库)和静态链接库的:
- gcc会先按照-Ldir -Bprefix选项指定的路径查找
- 再找gcc的环境变量GCC_EXEC_PREFIX
- 再找gcc的环境变量LIBRARY_PATH
- 然后查找GCC安装的目录(可以通过gcc -print-search-dirs查询)
- 然后查找默认路径/lib
- 然后查找默认路径/usr/lib
- 最后查找默认路径/usr/local/lib
- 在同一个目录下,如果有相同文件名的库(只是后缀不同),那么默认链接的是动态链接库,可以用-static选项显示的指定链接静态库。
程序运行时动态库的搜索路径搜索的先后顺序是:
- 环境变量LD_LIBRARY_PATH指定的动态库搜索路径;
- 编译目标代码时指定的动态库搜索路径(指的是用-wl,rpath或-R选项而不是-L);
example: gcc -Wl,-rpath,/home/arc/test,-rpath,/lib/,-rpath,/usr/lib/,-rpath,/usr/local/lib test.c - 配置文件/etc/ld.so.conf中指定的动态库搜索路径;
- 默认的动态库搜索路径/lib;
- 默认的动态库搜索路径/usr/lib。
在上述1、2、3指定动态库搜索路径时,都可指定多个动态库搜索路径,其搜索的先后顺序是按指定路径的先后顺序搜索的
在这里补充说明下:gcc的-Wl,rpath选项可以设置动态库所在路径,也就是编译生成的该程序在运行时将到-Wl,rpath所指定的路径下去寻找动态库,如果没找到则到其它地方去找,并且这个路径会直接写在elf文件(就是生成的可执行文件)中,这样可以免去设置LD_LIBRARY_PATH。注意,gcc参数设定时-Wl,rpath,/path/to/lib, 中间不能有空格。
下面对编译时库的查找与运行时库的查找做一个简单的比较:
- 编译时查找的是静态库或动态库,而运行时,查找的只是动态库。
- 编译时可以用-L指定查找路径,或者用环境变量LIBRARY_PATH,而运行时可以用-Wl,rpath或-R选项,或者修改/etc/ld.so.conf文件或者设置环境变量LD_LIBRARY_PATH.
- 编译时用的链接器是ld,而运行时用的链接器是/lib/ld-linux.so.2.
- 编译时与运行时都会查找默认路径:/lib /usr/lib
- 编译时还有一个默认路径:/usr/local/lib,而运行时不会默认找查该路径。
-- seamus 程序编译运行时头文件或动态链接库的查找 https://blog.csdn.net/dlutxie/article/details/6776936