我正在编写一个相当大的C ++共享对象库,并遇到了一个小问题,使调试变得很痛苦:
如果我在头文件中定义一个函数/方法,并忘记为它创建一个存根(在开发期间),因为我构建为共享对象库而不是可执行文件,所以在编译时不会出现错误我忘记了实现这个功能。我发现错误的唯一方法是在运行时,最终链接到此库的应用程序会出现“未定义的符号”错误。
我正在寻找一种简单的方法来检查我是否在编译时拥有所需的所有符号,也许我可以添加到我的Makefile中。
我提出的一个解决方案是通过nm -C -U
运行已编译的库,以获取所有未定义引用的demangled列表。问题是这也提供了其他库中的所有引用的列表,例如GLibC,当最终的应用程序放在一起时,它们当然会与该库链接在一起。可以通过我的所有头文件使用nm
到grep
的输出,看看是否有任何相应的名称..但这看起来很疯狂。当然,这不是一个不常见的问题,有更好的解决方法吗?
答案 0 :(得分:82)
查看链接器选项-z defs
/ --no-undefined
。创建共享对象时,如果存在未解析的符号,则会导致链接失败。
如果您使用gcc来调用链接器,您将使用编译器-Wl
选项将选项传递给链接器:
gcc -shared ... -Wl,-z,defs
例如,请考虑以下文件:
#include <stdio.h>
void forgot_to_define(FILE *fp);
void doit(const char *filename)
{
FILE *fp = fopen(filename, "r");
if (fp != NULL)
{
forgot_to_define(fp);
fclose(fp);
}
}
现在,如果你将它构建到共享对象中,它将会成功:
> gcc -shared -fPIC -o libsilly.so silly.c && echo succeeded || echo failed
succeeded
但是如果您添加-z defs
,该链接将会失败并告诉您缺少的符号:
> gcc -shared -fPIC -o libsilly.so silly.c -Wl,-z,defs && echo succeeded || echo failed
/tmp/cccIwwbn.o: In function `doit':
silly.c:(.text+0x2c): undefined reference to `forgot_to_define'
collect2: ld returned 1 exit status
failed
答案 1 :(得分:12)
在Linux上(您似乎正在使用)ldd -r a.out
应该为您提供您正在寻找的答案。
更新:创建a.out
的一种简单方法,可以对其进行检查:
echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so
ldd -r ./a.out
答案 2 :(得分:8)
测试套件怎么样?您可以创建链接到所需符号的模拟可执行文件。如果链接失败,则表示您的库接口不完整。
答案 3 :(得分:3)
我曾经遇到过同样的问题。我正在用C ++开发一个组件模型,当然,组件应该在运行时动态加载。我想到了三个解决方案,即我应用的解决方案:
希望有所帮助。