LINUX下动态库调用静态库的方法
LINUX下动态库调用静态库的方法
有这样一种情形,在创建一个动态库的同时,可能会调用一个静态库,这个静态库可能是你自己写的,也可能是第三方的。比如有下面五个文件,生成一个静态库,一个动态库,一个执行文件:
/// static.h
void static_print();
///static.cpp
#include <iostream>
#include "static.h"
void static_print() {
std::cout<<"This is static_print function"<<std::endl;
}
// shared.h
void shared_print();
// shared.cpp
#include <iostream>
#include "shared.h"
#include "static.h"
void shared_print() {
std::cout<<"This is shared_print function";
static_print();
}
test.cpp
#include "share.h"
int main()
{
shared_print();
return 0;
}
怎么办呢?方法有两种:
1、 动态库是动态库,静态库是静态库,各自编译自己的,然后在最终使用的可执行文件上再动态编译加载。按上面的例子来说明:
静态库的.o文件不用-fPIC生成. 生成动态库时不加表态库.
生成应用程序时加载动态库和静态库.
g++ -c static.cpp // 生成static.o
ar -r libstatic.a static.o // 生成静态库libstatic.a
g++ -c -fPIC shared.cpp // 生成shared.o
g++ -shared shared.o -o libshared.so // 生成动态库libshared.so 注: -shared是g++的选项,与shared.o无关. 这时如果加-lstatic. error:relocation R_X86_64_32 against `a local symbol' can not be used when making a shared object; recompile with –fPIC
(这里测试没有出现这个问题,特意测试了一下)
g++ test.cpp -L. -lshared -lstatic -o test.exe // link libshared.so 到test.exe中
2、 把静态库直接打到动态库中去。
静态库的.o文件也用-fPIC生成. 生成动态库时把静态库加入.
生成应用程序时只加载动态库
g++ -c -fPIC static.cpp // 生成static.o
ar -r libstatic.a static.o // 生成静态库libstatic.a
g++ -c -fPIC shared.cpp // 生成shared.o
g++ -shared shared.o –L. -lstatic -o libshared.so // 生成动态库libshared.so 注: -shared是g++的选项,与shared.o无关. -lstatic选项把libstatic.a的函数加入动态库中.
g++ test.cpp –L. -lshared -o test.exe // link libshared.so 到test.exe中.
这个例子是从网上找来的,非常感谢。
至于哪种方式好,个人还是觉得看你的实际应用情况,仁者见仁,智者见智。
gcc编译参数-fPIC的一些问题
(2012-07-26 15:41:08)-fPIC 作用于编译阶段,告诉编译器产生与位置无关代码(Position-Independent Code),
不加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'
-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
-fPIC
关键在于GOT全局偏移量表里面的跳转项大小。
intel处理器应该是统一4字节,没有问题。
powerpc上由于汇编
更多推荐
所有评论(0)