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

Doug Evans
2014-11-25T22:10:04Z

On Tue, Nov 25, 2014 at 7:00 AM, Siva Chandra <sivachandra@google.com> wrote:
> 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?

For functions/methods gdb already uses lowpc as the start address.
[IOW it doesn't use the linkage name, though even recently
I'd forgotten this and thought otherwise.
Also, GDB doesn't take a demangled name, mangle it,
and then try to look that up in the ELF symbol table.]

If I do "mt print symbols foo.sym", then I see this:

Symtab for file lambda.cc
Compilation directory is /home/dje/src/play
Read from object file /home/dje/ruffies-copy/src/play/lambda.x64 (0x288bf00)
Language: c++

Line table:

line 6 at 0x4006a6
line 6 at 0x4006b1
line 5 at 0x4006b9
line 7 at 0x4006c1
line 8 at 0x4006d3
line 0 at 0x4006d5

Blockvector:

block #000, object at 0x277ee90, 1 syms/buckets in 0x4006a6..0x4006d5
int main(); block object 0x277edc0, 0x4006b9..0x4006d5 section .text
block #001, object at 0x277ee20 under 0x277ee90, 1 syms/buckets in
0x4006a6..0x4006d5
typedef int int;
block #002, object at 0x277ec80 under 0x277ed50, 3
syms/buckets in 0x4006a6..0x4006b9, function
<lambda(int)>::operator()(int) const
int <lambda(int)>::operator()(int) const; block object
0x277ec80, 0x4006a6..0x4006b9
const struct <lambda(int)> {
} * const __closure; computed at runtime
int j; computed at runtime
block #003, object at 0x277edc0 under 0x277ee20, 0 syms/buckets in
0x4006b9..0x4006d5, function main()
block #004, object at 0x277ed50 under 0x277edc0, 1 syms/buckets
in 0x4006c1..0x4006d3
struct <lambda(int)> {
} lambda; computed at runtime
struct <lambda(int)> {
};

Note that there is a block for "function
<<lambda(int)>::operator()(int) const>" with a start address of
0x4006a6.

(gdb) ! nm lambda.x64 | grep ZZ4
00000000004006a6 t _ZZ4mainENKUliE_clEi

So I think(!) the existing mechanism of using
BLOCK_START (SYMBOL_BLOCK_VALUE (sym))
should work here, we just need to make sure
the various pieces are glued together.
But we don't AFAICT need a new field to record low_pc,
it is already recorded.

> [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.]

DWARF can be hard to read at times.
Note that GCC can do this too. E.g.,
It will output a declaration in the proper context
(e.g., inside a class definition for a method) without DW_AT_low_pc
and then it will output another DIE at the top level with DW_AT_low_pc
and with a DW_AT_specification referring back to previous DIE in its context.