Search code examples
cgdbdwarf

GDB opaque type resolution strategy for C executables


I am wondering how GDB resolves opaque types for C executables for instance.

in a .so

// foo.h
struct bar;

// foo.c
#include "foo.h"
struct bar {
...
}

in my program

#include "foo.h"
struct bar *b; <-- GDB can print the structure.

Even when a second forward declaration is provided with the same name in a new CU also included by my program it seems that GDB is still able to find the correct definition.

I have looked at the dwarfdump for my program and for the .so and to me it seems that the variable DIE entry for b is unconnected with the structure definition of bar which is in a separate CU. There is a structure DIE associated with b but it has no member DIEs.

So for examples in the main program CU dwarf info I would see

< 2><0x00000148>    DW_TAG_variable
                    DW_AT_name                  b
                    DW_AT_decl_file             0x00000001
                    DW_AT_decl_line             0x00000011
                    DW_AT_decl_column           0x00000019
                    DW_AT_type                  <0x000000f2>
                    DW_AT_location              len 0x0003: 0x91b87f: 
                        DW_OP_fbreg -72

and when I chase the DW_AT_type (through a ptr type first) I get

< 1><0x000000db>  DW_TAG_structure_type
                  DW_AT_name                  bar
                  DW_AT_declaration           yes(1)

with no child DW_TAG_members.

However in the other CU there is the correct DW_TAG_structure type with DW_TAG_members. So the crux of the question is how does gdb go from the DW_TAG_variable to the correct DW_TAG_structure even if there are multiple possible definitions do to the same name being used in different CUs as an opaque type?

Thanks in advance.


Solution

  • GDB simply matches the types by name across all TUs. Consider the following example:

    // main.c
    struct bar;
    
    extern struct bar* fn1();
    extern struct bar* fn2();
    
    int main()
    {
      struct bar *b1 = fn1();
      struct bar *b2 = fn2();
      return 0;
    }
    
    // foo1.c
    #include <stdlib.h>
    
    struct bar {
      int x;
    };
    
    struct bar* fn1() {
      struct bar* b = malloc(sizeof(*b));
      b->x = 42;
      return b;
    }
    
    // foo2.c
    #include <stdlib.h>
    
    struct bar {
      double zzz;
    };
    
    struct bar* fn2() {
      struct bar* b = malloc(sizeof(*b));
      b->zzz = 1234.0;
      return b;
    }
    

    Compile this with gcc -g main.c foo1.c foo2.c and observe that GDB doesn't really know which struct bar is the correct one:

    gdb -q ./a.out
    (gdb) start
    Temporary breakpoint 1, main () at main.c:8
    8         struct bar *b1 = fn1();
    (gdb) n
    9         struct bar *b2 = fn2();
    (gdb) n
    10        return 0;
    (gdb) p *b1
    $1 = {zzz = 2.0750757125332355e-322}
    
    (gdb) x/d b1
    0x5555555592a0: 42
    
    (gdb) p *b2
    $2 = {zzz = 1234}