首页 文章

轻松检查共享库中未解析的符号?

提问于
浏览
71

我正在编写一个相当大的C共享对象库,并遇到一个小问题,使调试变得痛苦:

如果我在头文件中定义一个函数/方法,并忘记为它创建一个存根(在开发期间),因为我构建为共享对象库而不是可执行文件,编译时没有出现错误告诉我我有忘记实现这个功能 . 我发现错误的唯一方法是在运行时,最终链接到此库的应用程序会出现“未定义的符号”错误 .

我正在寻找一种简单的方法来检查我是否在编译时拥有所需的所有符号,也许我可以添加到我的Makefile中 .

我提出的一个解决方案是通过 nm -C -U 运行已编译的库,以获取所有未定义引用的demangled列表 . 问题是这也提供了其他库中的所有引用的列表,例如GLibC,当最终的应用程序放在一起时,它们当然会与该库链接在一起 . 可以通过我的所有头文件使用 nmgrep 的输出,看看是否有任何名称对应..但这看起来很疯狂 . 当然,这不是一个罕见的问题,有一个更好的解决方法吗?

4 回答

  • 79

    查看链接器选项 -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
    
  • 11

    在Linux上(您似乎正在使用) ldd -r a.out 应该为您提供您正在寻找的答案 .

    更新:创建 a.out 的简单方法,以对其进行检查:

    echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so
     ldd -r ./a.out
    
  • 3

    那个测试套件怎么样?您可以创建链接到所需符号的模拟可执行文件 . 如果链接失败,则表示您的库接口不完整 .

  • 8

    我曾经遇到过同样的问题 . 我正在用C开发一个组件模型,当然,组件应该在运行时动态加载 . 我想到了三个解决方案:我应用的解决方案:

    • 花一些时间来定义一个能够静态编译的构建系统 . 你将失去一些时间来设计它,但它会节省你很多时间来捕捉这些烦人的运行时错误 .

    • 将您的函数分组到众所周知且易于理解的部分中,以便您可以组合函数/存根以确保每个相应的函数都有其存根 . 如果你花时间记录它,你可以编写一个脚本来检查定义(例如,通过它的doxygen注释)并检查相应的.cpp文件 .

    • 执行多个加载同一组库的测试可执行文件,并将RTLD_NOW标志指定为dlopen(如果您在* NIX下) . 他们将发出缺失符号的信号 .

    希望有所帮助 .

相关问题