编译c++ so注意点
每一个 c++ 程序编译完成都会依赖 libstdc++,众所周知,与 glibc 不同的是,libstdc++ 与 gcc 的版本是绑定的。这个特性所带来的麻烦其中之一是,使用高版本 gcc 编译出来的程序,直接放到低版本 gcc 的环境中是无法运行的,需要把对应的 libstdc++.so 同时拷贝过去才能正常运行。
什么是菱形依赖?
一个菱形依赖的典型案例:A库依赖C1,B库依赖了C2,这两个C的版本是无法兼容的,造成用户程序无法同时链接A库和B库。
解决菱形依赖的总体思想
菱形依赖导致用户无法接入 so 的根本原因在于符号冲突,造成符号冲突的原因是用户在编译自己的程序时,从我们的
so 和用户自己的程序或依赖库中,存在相同符号的不同实现。因此我们的目标就是让用户从我们的 so 中找不到存在冲突的符号。为了达到这个目的,我们需要采用如下两个步骤:
1. 将所有的依赖打入一个so中
如果我们的so动态依赖了其它的动态库,而用户如果依赖了这个库的其它版本,那么这会不可避免的产生菱形依赖。因此在第一步,我们需要尽量将我们的所有依赖库,以静态依赖的方式打到一个so里面,不把我们所以依赖的字库直接暴露给用户。
方法是在链接这个so的时候,把所有需要的.a 都链接上。
因此这个so几乎需要包含我们依赖的全部,由于gcc在编译so时并没有要求所有的符号都要包含在里面,因此我们需要手动check这个so 还有哪些未定义的符号,方法是使用命令:ld **.so。
2. 只导出相关符号
gcc 在编译 so 时,默认所有符号都会导出。gcc 在链接时提供一个叫 version-script 选项指定一个文件,可以在这个文件中指定哪些符号是需要导出的。
由于 C++ 的接口通常相当复杂,并且符号难以捉摸,强烈建议大家 so 的 API 接口使用 C 来实现,让用户的接入更加干净。
静态链接 libstdc++
由于按默认方式编译出的 so 会依赖当前 gcc 版本的 libstdc++,因此我们可以在链接这个 so 时,使用 '-Bstatic -lstdc++ -Bdynamic' 选项把 stdc++ 静态编入到这个 so 里面。事实证明将 gcc-4.9.2 的 stdc++ 打入 so 中后是可以在 7u 上正常运行的。
总体步骤
1. 使用 C 接口封装 API;2. 编写 version-script,仅包含需要暴露的 API;
3. 编译 so 时静态链接所有的依赖库;
4. 链接选项增加 '-Bstatic -lstdc++ -Bdynamic' 将当前版本的 libstdc++ 包含进 so.