首页 > 解决方案 > 在共享库中查找引用未定义符号的源文件

问题描述

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

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

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

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

标签: c++ubuntushared-libraries

解决方案


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

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的条目描述。它的链接名称由记录给出:<2>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 个命中(当然,因为只编译了一个源文件),告诉我们_ZN3bar9undefinedEakabar::undefined被引用foo.cpp,它是在 build-directory 中编译的/home/imk/develop/so/scrap


推荐阅读