【问题标题】:Find the source files referencing undefined symbol in shared library在共享库中查找引用未定义符号的源文件
【发布时间】:2018-11-06 11:58:34
【问题描述】:

我有一个由 C++ 源代码构建的共享库(我想是 ELF 格式)。该库是在调试模式下构建的。

鉴于此库的未定义符号,我想确定它来自的源文件(-s)(或目标文件(-s))。

怎么做? (我想调试版本的库是很有可能的。)

递归 grep 不是一个选项,因为我只对库包含的源文件感兴趣。未定义符号可能来自外部头文件,因此对库本身的源代码进行 greing 将找不到任何东西。

【问题讨论】:

  • 递归 grep 遍历源代码。
  • 您能否详细说明您的答案?为此,我想到了使用 ldd 的 readelf 之类的东西。
  • @stark 感谢您的评论,但源本身可能不包含符号 - 符号可能来自外部头文件。
  • 头文件不会导致该错误。只有使用符号才会。
  • @stark 不,可能没有直接使用该符号 - 例如,在整个源代码中都使用了宏。在预处理过程中,该宏将被替换为符号。

标签: c++ ubuntu shared-libraries


【解决方案1】:

使用调试信息构建的共享库引用了未定义的外部 变量,就像我要构建的示例一样:

foo.cpp

 namespace bar {
     extern int undefined;
 };

 int foo()
 {
     return bar::undefined;
 }

我将未定义的符号放在命名空间中只是为了了解它所在的情况 由于您在谈论 C++,因此对链接器进行了名称修改。

编译和链接,带有调试信息:

 $ g++ -shared -g -fPIC -o libfoo.so foo.cpp

这是在库的符号表中,原始的:

 $ nm --undefined-only libfoo.so | grep undefined
                  U _ZN3bar9undefinedE

并解构:

 $ nm -C --undefined-only libfoo.so | grep undefined
                  U bar::undefined

现在,如果我们转储调试信息,我们会看到:

$ readelf --debug-dump=info libfoo.so
Contents of the .debug_info section:

  Compilation Unit @ offset 0x0:
   Length:        0x6d (32-bit)
   Version:       4
   Abbrev Offset: 0x0
   Pointer Size:  8
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (indirect string, offset: 0x0): GNU C++14 7.3.0 -mtune=generic -march=x86-64 -g -fPIC -fstack-protector-strong
    <10>   DW_AT_language    : 4    (C++)
    <11>   DW_AT_name        : (indirect string, offset: 0x8f): foo.cpp
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x74): /home/imk/develop/so/scrap
    <19>   DW_AT_low_pc      : 0x5ba
    <21>   DW_AT_high_pc     : 0xf
    <29>   DW_AT_stmt_list   : 0x0
 <1><2d>: Abbrev Number: 2 (DW_TAG_namespace)
    <2e>   DW_AT_name        : bar
    <32>   DW_AT_decl_file   : 1
    <33>   DW_AT_decl_line   : 1
    <34>   DW_AT_sibling     : <0x48>
 <2><38>: Abbrev Number: 3 (DW_TAG_variable)
    <39>   DW_AT_name        : (indirect string, offset: 0x6a): undefined
    <3d>   DW_AT_decl_file   : 1
    <3e>   DW_AT_decl_line   : 2
    <3f>   DW_AT_linkage_name: (indirect string, offset: 0x57): _ZN3bar9undefinedE
    <43>   DW_AT_type        : <0x48>
    <47>   DW_AT_external    : 1
    <47>   DW_AT_declaration : 1
 <2><47>: Abbrev Number: 0
 <1><48>: Abbrev Number: 4 (DW_TAG_base_type)
    <49>   DW_AT_byte_size   : 4
    <4a>   DW_AT_encoding    : 5    (signed)
    <4b>   DW_AT_name        : int
 <1><4f>: Abbrev Number: 5 (DW_TAG_subprogram)
    <50>   DW_AT_external    : 1
    <50>   DW_AT_name        : foo
    <54>   DW_AT_decl_file   : 1
    <55>   DW_AT_decl_line   : 5
    <56>   DW_AT_linkage_name: (indirect string, offset: 0x4f): _Z3foov
    <5a>   DW_AT_type        : <0x48>
    <5e>   DW_AT_low_pc      : 0x5ba
    <66>   DW_AT_high_pc     : 0xf
    <6e>   DW_AT_frame_base  : 1 byte block: 9c     (DW_OP_call_frame_cfa)
    <70>   DW_AT_GNU_all_call_sites: 1
 <1><70>: Abbrev Number: 0

其中我们的符号_ZN3bar9undefinedE由条目&lt;2&gt;描述 第一个(也是唯一一个)为libfoo.so 编译的编译单元。它是 链接名称由记录给出:

<3f>   DW_AT_linkage_name: (indirect string, offset: 0x57): _ZN3bar9undefinedE

所以,要获取 bar::undefined 所在的源文件的名称 引用,我们想要:-

从调试信息中提取所有行块,例如:

 ...Compilation Unit...
 ...
 ...
 ..._ZN3bar9undefinedE...

然后从它们中提取所有块,例如:

 ...DW_TAG_compile_unit...
 ...
 ...DW_AT_comp_dir...

然后在 那些 块中,打印最后两行。这是一种方法 - 很可能 不是最专业的方法 - 这样做:

$ readelf --debug-dump=info libfoo.so | awk '/Compilation Unit/, /_ZN3bar9undefinedE/' | awk '/DW_TAG_compile_unit/,/DW_AT_comp_dir/' | grep -B1 'DW_AT_comp_dir' 
    <11>   DW_AT_name        : (indirect string, offset: 0x8f): foo.cpp
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x74): /home/imk/develop/so/scrap

我们得到 1 次命中(当然,因为只编译了一个源文件),告诉我们 _ZN3bar9undefinedE, 又名bar::undefined,在foo.cpp 中引用,在构建目录/home/imk/develop/so/scrap 中编译。

【讨论】:

  • 感谢您的回答!但由于某种原因,该符号在 readelf --debug-dump=info 输出中不存在。虽然它存在于 nm --undefined-only 输出中。
  • 看起来库可能不包含它所依赖的完整 DWARF 调试信息。或者可能我的awking在你的情况下滑倒了。 awker 的路径上散落着香蕉皮。如果不建立您的图书馆,将无法提供更多帮助。
  • 我已经搜索了整个 readelf --debug-dump=info 输出 - 它不包含符号。看起来缺少调试信息信息。
猜你喜欢
  • 1970-01-01
  • 2011-02-28
  • 2016-10-16
  • 2017-08-02
  • 2011-07-19
  • 2018-04-14
  • 2021-08-20
  • 1970-01-01
  • 2021-11-29
相关资源
最近更新 更多