代码之家  ›  专栏  ›  技术社区  ›  David Claridge

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

  •  72
  • David Claridge  · 技术社区  · 15 年前

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

    如果我在一个头文件中定义了一个函数/方法,并且忘记为它创建一个存根(在开发过程中),因为我构建的是一个共享对象库而不是一个可执行文件,那么在编译时不会出现错误,告诉我我忘记了实现该函数。我发现问题的唯一方法是在运行时,当针对这个库的应用程序链接最终因“未定义的符号”错误而失效时。

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

    我提出的一个解决方案是运行编译后的库 nm -C -U 获取所有未定义引用的散列表。问题是,这也会列出其他库(如glibc)中的所有引用,当然,当最终应用程序放在一起时,这些引用将与此库一起链接。可以使用 nm grep 通过我所有的头文件,查看是否有任何对应的名称。但这似乎很疯狂。当然,这不是一个不寻常的问题,有更好的解决方法吗?

    4 回复  |  直到 9 年前
        1
  •  80
  •   Jonathon Reinhart    9 年前

    查看链接器选项 -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
    
        2
  •  11
  •   Employed Russian    15 年前

    在Linux上(您似乎正在使用) ldd -r a.out 应该给你准确的答案。

    更新:创建 a.out 检查依据:

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

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

        4
  •  3
  •   Diego Sevilla    15 年前

    我也有过同样的问题。我在C++中开发了一个组件模型,当然,组件应该在运行时动态加载。我想到了三个解决方案,那就是我应用的方案:

    1. 花些时间定义一个能够静态编译的构建系统。您将损失一些时间来设计它,但它将节省您许多时间来捕获这些恼人的运行时错误。
    2. 将您的函数分组到已知和理解良好的部分中,这样您就可以对函数/存根进行分组,以确保每个对应的函数都有其存根。如果您花时间很好地记录它,那么您可以编写一个脚本来检查定义(例如,通过它的doxygen注释),并检查相应的.cpp文件。
    3. 执行几个加载同一组库的测试可执行文件,并将rtld_now标志指定为dlopen(如果您在*nix下)。它们将发出缺失符号的信号。

    希望有帮助。