Find the source files referencing undefined symbol in shared library

Viewed 1435

I have a shared library (ELF format, I suppose) built from C++ source code. The library is built in debug mode.

Given an undefined symbol of this library I'd like to determine the source file(-s) (or an object file(-s)) it came from.

How can it be done? (I suppose it is quite possible for debug version of the library.)

Recursive grep is not an option because I'm interested only in the source files the library consist of. The undefined symbol may come from an external header file, so greping the source code of the library itself will not find anything.

1 Answers

Your shared library, built with debug info, references an undefined external variable, like the example I'm just going to build:

foo.cpp

 namespace bar {
     extern int undefined;
 };

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

I'm putting the undefined symbol in a namespace just to get a case where it's name-mangled to the linker, since you're talking about C++.

Compile and link, with debug info:

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

Here it is in the library's symbol table, raw:

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

and demangled:

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

Now if we dump the debug-info we see this:

$ 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

in which our symbol _ZN3bar9undefinedE is described by entry <2> in the first (and only) compilation unit that was compiled for libfoo.so. It's linkage name is given by the record:

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

So, to get the name of the source file(s) in which bar::undefined is referenced, we want to:-

Extract from the debug info all blocks of lines like:

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

Then out of them, extract all blocks like:

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

Then out of those blocks, print the last two lines. Here is one way - very likely not the most expert way - of doing it:

$ 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

We get 1 hit (of course, since only one source file was compiled), telling us that _ZN3bar9undefinedE, a.k.a bar::undefined, is referenced in foo.cpp, which was compiled in build-directory /home/imk/develop/so/scrap.

Related