机关网络 网站 建设,wordpress创业,建设咨询网站,提高网站的访问速度文章目录创建动态库设计动态库ABI兼容动态符号的可见性示例控制符号可见性通过-fvisibility通过strip工具删除指定符号创建动态库
在Linux中创建动态库通过如下过程完成#xff1a; gcc -fPIC -c first.c second.c gcc -shared first.o second.o -o libdynamiclib.so 按照Li…
文章目录创建动态库设计动态库ABI兼容动态符号的可见性示例控制符号可见性通过-fvisibility通过strip工具删除指定符号创建动态库
在Linux中创建动态库通过如下过程完成 gcc -fPIC -c first.c second.c gcc -shared first.o second.o -o libdynamiclib.so 按照Linux的惯例动态以lib作为前缀并以.so作为文件扩展名。 需要两个必要的选项
fPIC告诉编译器使用位置无关代码技术。
fPIC本质上是用于辅助加载器加载程序可以使多个进程可以无缝映射到已加载动态库的内存映射中。 有一点还需要注意的是如果静态库是链接到动态库中那么就在编译静态库时就必须使用**-fPIC**选项编译。否则链接器会报错。
shared告诉编译器生成动态库。
设计动态库
动态库本质上是一种代码复用的技术动态库提供功能接口给客户程序而使客户程序不必关心内部实现细节。 对动态库的需要特别注意ABI兼容和接口的可见效性。
ABI兼容
ABI的全称是Application Binary Interfance应用程序二进制接口。 ABI与API相同的概念但是两者的侧重不一样。API是指代码层面的接口在代码层面客户程序关注所依赖库提供的接口定义返回值等特征。而ABI是指客户程序和所依赖的库被编译成二进制文件后在链接器加载器层面对应的符号是否匹配。 其实站在开发者的角度认为API与ABI是应该等价的代码里的接口匹配难道编译后二进制文件中的接口对应的符号还不匹配吗是的对C来说是要需要关注这些问题。
ABI兼容也是C饱受诟病的问题它是C的复杂性造成并且因为各个编译器厂商没有统一ABI标准而加剧。同一套代码使用不同的编译器而导致无法链接到客户程序。 最被广泛认识的造成C ABI兼容的问题是符号修饰。 C引入的命名空间类私有公有等一些面向对象的语法。在编码层面这些机制很好的保证了模块的划分及命名冲突。但是在编译层面也引入了符号命名的复杂性因为在变成二进制符号时可没这些概念但是编译器也需要保证符号唯一所以会有一套符号命令规则。 这套规则并没有标准化各个厂家间甚至同一厂家不同的版本间也可能不兼容。 gcc中有个比较典型的ABI兼容情况也是我们比较容易遇到的从GCC 5.1开始为了兼容C 11标准对std::string和 std::list的命令空间进行了更改相应的符号也变化了。从std::string变为了std::__cx11:string从std::list变为了std::__cx11::string。 所以如果依赖库所使用的编译器是gcc5.1之前的版本而客户程序使用的是gcc5.1版本那么在链接时std::string和std::list的因为两个版本的编译器生成的符号不一样而会导致链接失败。有两个解决方法
都用同一版本的gcc编译。使用_GLIBCXX_USE_CXX11_ABI宏
加入编译选项 -D_GLIBCXX_USE_CXX11_ABI1/0
所以容易遇到ABI兼容问题的场景
客户程序与所依赖的库使用的是不同的编译器编译
比如在Linux下客户程序用的gcc而所依赖的库使用clang编译。
客户程序与所依赖的库使用同一编译器的不同版本
比如我们上面所举的GCC的例子。
当然我们也并不需要掌握ABI兼容问题的细节只要我们做到以下几点就可以避免绝大多数ABI兼容问题
客户程序与所依赖的库尽量用同一个编译器同一版本编译以C语言风格设计接口
在C语言中并没有符号修饰对调用约定内存布局编译器也做到了统一。 通过extern C让编译器生成不带修辞的符号名。
#ifdef __cplusplus
extern C
{int Function(int x,int y);
}
#endif //__cplusplus接口中的数据类型也使用C语言的类型。比如将std::string替换为char[]。如果是自定义类则改为结构体。在C中是兼容C语言的类型内存布局的。在接口实现时可以使用C实现。
动态符号的可见性
动态库应该只向客户程序暴露所需求的接口。所以需要控制接口对外的可见性。 **在Linux中所有符号默认都是外部可见的**可以通过以下几种方法来控制Linux下符号的可见性
-fvisibilityhidden
在编译动态库时加入该编译选项那么所有的动态符号都置为对外不可见任何尝试链接该动态库的客户程序将无法访问这些符号。
__attribute__((visibility(default | hidden)))
通过在函数前面使用编译属性修饰可以指示链接器运行或禁止对外提供该符号。
strip工具
直接通过strip工具抹除动态库中的某个符号。
示例
如下是两个简单的示例代码文件:
void function1(void);
void function2(void);
void function3(void);#include stdio.h
#include sharedLib.h
void function1(void) {printf(function1\n);
}void function2(void) {printf(function2\n);
}void function3(void) {printf(function3\n);
}#include sharedLib.h
int main() {function1();function2();function3();
}gcc -fPIC -shared sharedLib.c -o libsharedLib.so 生成动态libsharedLib.so
通过nm libsharedLib.so查看它的符号如下
00000000000006f5 T function1
0000000000000707 T function2
0000000000000719 T function3符号的类型都是T类型表示位于代码区的符号对外可见。
链接动态库生成test可执行程序 gcc main.c -L. -lsharedLib -o test 在运行test程序时需要设置LD_LIBRARY_PATH环境变量用于指定libsharedLib.so的路径。使加载器能找到它。
控制符号可见性
通过-fvisibility
在编译时添加-fvisibility编译选项让所有符号默认不可见 -fvisibilityhidden -fvisibility-inlines-hidden 再通过该编译选项将libsharedLib.so的接口都改为不可见。 gcc -fPIC -shared sharedLib.c -fvisibilityhidden -fvisibility-inlines-hidden -o libsharedLib.so
通过nm libsharedLib.so查看符号如下
0000000000000675 t function1
0000000000000687 t function2
0000000000000699 t function3符号类型变为t表示为内部符号对外不可见。 那么链接libsharedLib.so库时就会报错如下
gcc main.c -L. -lsharedLib -o testmain.c:(.text0x5)对‘function1’未定义的引用
main.c:(.text0xa)对‘function2’未定义的引用
main.c:(.text0xf)对‘function3’未定义的引用通过__attribute__((visibility(default)))修饰需要导出的符号
将符号改为不可见同时通过改属性设置某个方法为可见。设置function1为可见。 #include stdio.h
#include sharedLib.h
#define FUNC_EXPORT __attribute__((visibility(default)))
void FUNC_EXPORT function1(void) {printf(function1\n);
}void function2(void) {printf(function2\n);
}void function3(void) {printf(function3\n);
}将function1设置为了可见重新编译。 gcc sharedLib.c -fPIC -shared -fvisibilityhidden -fvisibility-inlines-hidden -o libsharedLib.so 通过nm libsharedLib.so查看其符号如下:
0000000000000675 T function1
0000000000000687 t function2
0000000000000699 t function3function1变为了可见其它两个都是不可见。
通过strip工具删除指定符号
strip --strip-symbol 符号名直接通过strip命令简单粗暴的删除动态库中的符号。当然它也可以删除静态库中的符号。 strip --strip-symbol function1 libsharedLib.so删除libsharedLib.so中的符号function1。