Re: [RFC] While processing a struct die, store the method's address in its fn_field

Siva Chandra
2014-11-25T15:00:05Z

On Mon, Nov 24, 2014 at 12:22 PM, Doug Evans <dje@google.com> wrote:
> Adding new fields to types or symbols is a big deal.
> I'd like to understand why things are failing better.
> For normal functions gdb gets the start address
> from BLOCK_START (SYMBOL_BLOCK_VALUE (sym)).

I will try, but tl;dr.

Consider a simple class definition like this which has a method which
is not inlined:

class A
{
public:
int method (int a);
};

int
A::method (int a)
{
return a * a;
}

The DWARF for the class comes out like this:

< 1><0x0000002d> DW_TAG_class_type
DW_AT_name "A"
DW_AT_byte_size 0x00000001
DW_AT_decl_file 0x00000001 /tmp
DW_AT_decl_line 0x00000001
DW_AT_sibling <0x00000057>
< 2><0x00000037> DW_TAG_subprogram
DW_AT_external yes(1)
DW_AT_name "method"
DW_AT_decl_file 0x00000001 /tmp
DW_AT_decl_line 0x00000004
DW_AT_linkage_name "_ZN1A6methodEi"
DW_AT_type <0x00000057>
DW_AT_accessibility DW_ACCESS_public
DW_AT_declaration yes(1)
DW_AT_object_pointer <0x0000004b>
< 3><0x0000004b> DW_TAG_formal_parameter
DW_AT_type <0x0000005e>
DW_AT_artificial yes(1)
< 3><0x00000050> DW_TAG_formal_parameter
DW_AT_type <0x00000057>

Notice that there is a linkage name for the method named "method". So,
one can demangle the linkage name and get to the symbol. If this
fails, one can use the linkage name to get to the minsym. The issue
with lambdas is that their operator() methods do not have a linkage
name. Not exactly true: they have a linkage name but the DWARF doesn't
specify it. Consider this example:

$> cat lambda.cc

int
main ()
{
auto lambda = [] (int j) { return j + 113; };
return lambda (-113);
}

$> g++ -g -std=c++11 lambda.cc -o lambda

The DWARF for the lambda struct come out like this:

< 3><0x00000070> DW_TAG_structure_type
DW_AT_name "<lambda(int)>"
DW_AT_byte_size 0x00000001
DW_AT_decl_file 0x00000001 /tmp
DW_AT_decl_line 0x00000004
< 4><0x00000078> DW_TAG_subprogram
DW_AT_name "<lambda>"
DW_AT_artificial yes(1)
DW_AT_declaration yes(1)
DW_AT_object_pointer <0x00000085>
DW_AT_sibling <0x0000009c>
< 5><0x00000085> DW_TAG_formal_parameter
DW_AT_type <0x0000008a>
DW_AT_artificial yes(1)
< 5><0x0000008a> DW_TAG_pointer_type
DW_AT_byte_size 0x00000008
DW_AT_type <0x00000070>
< 5><0x00000090> DW_TAG_formal_parameter
DW_AT_type <0x00000095>
< 5><0x00000095> DW_TAG_rvalue_reference_type
DW_AT_byte_size 0x00000008
DW_AT_type <0x00000070>
< 4><0x0000009c> DW_TAG_subprogram
DW_AT_name "<lambda>"
DW_AT_artificial yes(1)
DW_AT_declaration yes(1)
DW_AT_object_pointer <0x000000a9>
DW_AT_sibling <0x000000bf>
< 5><0x000000a9> DW_TAG_formal_parameter
DW_AT_type <0x0000008a>
DW_AT_artificial yes(1)
< 5><0x000000ae> DW_TAG_formal_parameter
DW_AT_type <0x000000b3>
< 5><0x000000b3> DW_TAG_reference_type
DW_AT_byte_size 0x00000008
DW_AT_type <0x000000b9>
< 5><0x000000b9> DW_TAG_const_type
DW_AT_type <0x00000070>
< 4><0x000000bf> DW_TAG_subprogram
DW_AT_name "<lambda>"
DW_AT_artificial yes(1)
DW_AT_declaration yes(1)
<Unknown AT value 0x211a> yes(1)
DW_AT_object_pointer <0x000000cc>
DW_AT_sibling <0x000000d2>
< 5><0x000000cc> DW_TAG_formal_parameter
DW_AT_type <0x0000008a>
DW_AT_artificial yes(1)
< 4><0x000000d2> DW_TAG_subprogram
DW_AT_name "~<lambda>"
DW_AT_artificial yes(1)
DW_AT_declaration yes(1)
DW_AT_object_pointer <0x000000df>
DW_AT_sibling <0x000000ea>
< 5><0x000000df> DW_TAG_formal_parameter
DW_AT_type <0x0000008a>
DW_AT_artificial yes(1)
< 5><0x000000e4> DW_TAG_formal_parameter
DW_AT_type <0x0000002d>
DW_AT_artificial yes(1)
< 4><0x000000ea> DW_TAG_subprogram
DW_AT_name "operator()"
DW_AT_type <0x0000002d>
DW_AT_artificial yes(1)
DW_AT_low_pc 0x00400566
DW_AT_high_pc <offset-from-lowpc>19
DW_AT_frame_base len 0x0001:
9c: DW_OP_call_frame_cfa
DW_AT_object_pointer <0x0000010f>
DW_AT_GNU_all_call_sites yes(1)
< 5><0x00000109> DW_TAG_pointer_type
DW_AT_byte_size 0x00000008
DW_AT_type <0x000000b9>
< 5><0x0000010f> DW_TAG_formal_parameter
DW_AT_name "__closure"
DW_AT_type <0x0000011b>
DW_AT_artificial yes(1)
DW_AT_location len 0x0002:
9168: DW_OP_fbreg -24
< 5><0x0000011b> DW_TAG_const_type
DW_AT_type <0x00000109>
< 5><0x00000120> DW_TAG_formal_parameter
DW_AT_name "j"
DW_AT_decl_file 0x00000001 /tmp
DW_AT_decl_line 0x00000004
DW_AT_type <0x0000002d>
DW_AT_location len 0x0002:
9164: DW_OP_fbreg -28

Notice that the operator() method has no linkage name specified in the
DWARF. But, it has a DW_AT_low_pc. Lets grep the binary for this low
pc value:

$> readelf -a lambda | grep 400566
41: 0000000000400566 19 FUNC LOCAL DEFAULT 12 _ZZ4mainENKUliE_clEi

So then, there is an ELF symbol for the operator() method! Lets
demangle the name:

(gdb) maintenance demangle _ZZ4mainENKUliE_clEi
main::{lambda(int)#1}::operator()(int) const

This name is not the same as the name of the lambda structure given by
DWARF! If I use clang to compile, the demangled ELF symbol for the
operator() turns out to be "main::$_0::operator()(int) const", which
seems to indicate that gcc and clang have their own way of
differentiating the different lambdas that can occur in a CU. Hence,
GDB should probably not even attempt to generate a name, mangle it and
lookup for a symbol with that name. However, low_pc is immediately
available in the DWARF generated by GCC, so why not use it? [Clang
actually does not put out the low pc value for the subprogram die
under a structure die, but it puts out a separate (not under a
structure die) subprogram die for the operator() method with the low
pc value.]

Thanks,
Siva Chandra