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.
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}