我正在构建一个动态库(C++),它导出许多符号。
这些导出的符号(显然)与我的库的 API 匹配。 但是,我的库还导出了大量附加符号(我的公共标头中根本没有提到这些符号)。
为了保持我的库的二进制接口较小(防止名称冲突;不破坏符号空间;防止库的使用者不小心使用可能在下一个版本中消失的符号),我可以使导出使用版本脚本显式符号,就像这样(实际上版本脚本要复杂得多;无论如何,它涉及 C++ 分解的符号和通配符):
{
global:
extern "C++" {
/* the public API uses the 'MyLib::' namespace */
MyLib::*;
};
/* g++ version mangling prefixes for 'typeinfo' */
_ZTI*; _ZTF*; _ZTS*;
/* g++ version mangling prefixes for 'vtable' */
_ZTT*; _ZTV*;
local:
*;
};
然后使用这些附加标志调用链接器
-fvisibility=hidden -Wl,--version-script=mylib.ver
酷。
现在,该库还附带了一个测试套件。 为了能够编写简洁的测试,单元测试使用不属于公共 API 的符号。
从库中剥离非公共符号将阻止单元测试(链接到库)找到这些符号。 因此测试套件变得无法使用。
解决此问题的一个明显方法是仅在单元测试中使用公共符号。但这将使单元测试变得更加复杂,并且测试结果更难以解释。
另一种可能的解决方案是不使用动态库进行单元测试,而是使用其他库(例如静态库;或没有版本脚本链接的库的变体)。 我不喜欢这个,因为我想测试实际的库,而不是一些替代品。
第三个解决方案(我最喜欢的)是推迟非公共符号的剥离,直到部署库:测试套件将使用本地库构建(包含所有符号),但在
make install
期间
(或其他一些明确的构建后步骤)不必要的符号被剥离。
不幸的是我不知道如何实现这一点。
strip
这样的工具会做一些不同的事情((GNU?)有一个
-N
标志应该删除符号,但它似乎不起作用,即使对于普通的 C 库(没有 C++ 符号)损坏)
有没有办法在构建后步骤中从库中删除符号(仅保留已知的集合)?是的。 (动态)符号名称位于
.dynstr
部分,如果您覆盖那里的名称(例如,用
_ZMyLib...
覆盖
_XMyLib
),动态链接器将无法解析这些符号。迭代
.dynsym
(动态)符号表中的所有符号并不难。
如何通过 C++ 符号修改和使用通配符来做到这一点?您必须对每个符号调用
__cxa_demangle,然后进行通配符匹配并决定是“zap”该符号还是保留它。