On Wed, Apr 23, 2025 at 7:46 PM Cary Coutant <ccout...@gmail.com> wrote:
>>
>> The first part of this is straightforward. The DWARF for Base will
>> contain a member for the vtable pointer, and that plus knowledge of
>> how the ABI lays out vtables allows the debugger to effectively do a
>> dynamic_cast<void*> to obtain a pointer to the most derived object.
>> From there the vtable address is compared against the ELF symbol table
>> to find the mangled name of the vtable symbol.
>
>
> This made me do a bit of research...
>
> The artificial member for the vtable pointer appears to be a DWARF extension 
> requested as far back as 2003 and implemented in 2009:
>
>    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=11208

That was an interesting read. I particularly enjoyed this bit in the
original issue, written in 2002.

"This one isn't really necessary.  The ABI specifies where the vtable
 pointer will be, and GDB is quite capable of using that knowledge to
 identify the runtime type."

And yet here we are 23 years later with examples where gdb et al fail
to correctly identify the runtime type :)

> But I can't find any relevant discussion on the DWARF mailing lists, until a 
> question arose about that very member in 2022:
>
>    https://dwarfstd.org/pipermail/dwarf-discuss/2022-February/002127.html
>
> It seems to me that, given the apparent need for this information in the 
> DWARF info, we should have addressed it in DWARF by now. I suspect the DWARF 
> committee's position was (or would have been) that the ABI tells you how to 
> find the vtable so it doesn't need to be explicitly recorded in the DWARF 
> info. But if both GCC and LLVM have decided it's useful enough (and there's 
> discussion about that point in the original PR that 11208 spun off from), 
> then we should discuss it. Otherwise, we risk having different toolchains 
> adopt different solutions. (GCC and LLVM appear to have avoided that through 
> careful consideration of what the other project was doing.) The argument in 
> PR 11208 is that it's /legal/ in DWARF to do this, so no new DWARF feature 
> was requested.
>
> The request in PR 11208 was for three things:
>
> > 1) I'd like to be able to locate the vtable pointer in the class
> >    structure so that the debugger knows that the hole in the apparent
> >    layout is not padding.
> >
> > 2) I'd like to know the type of the target of the vtable pointer, so
> >    that if the user asks to see it they see something sane.
> >
> > 3) I'd like to be able to find a specific virtual functions entry in
> >    the vtable, however I believe that this information will be best
> >    expressed as a property of the function, not directly of the class
> >    or vtable. DWARF3 has the DW_AT_vtable_elem_location attribute for
> >    precisely this information. gcc should generate that too.
> >
> >    Quoting the DWARF spec again :-
> >      An entry for a virtual function also has a
> >      DW_AT_vtable_elem_location attribute whose value contains a
> >      location description yielding the address of the slot for the
> >      function within the virtual function table for the enclosing
> >      class. The address of an object of the enclosing type is pushed
> >      onto the expression stack before the location description is
> >      evaluated.
>
> Point #1 is satisfied with an artificial member whose data_member_location is 
> the offset of the vtable pointer.

Right.

> I'm not clear how Point #2 was addressed.

I don't think it was.

> Point #3 was addressed via the vtable_elem_location attribute.
>
> Looking at the DWARF generated by GCC (and I'm guessing LLVM does the same), 
> I see vtable_elem_location attributes that look like this:
>
>     <1b8>   DW_AT_vtable_elem_location: 2 byte block: 10 0 (DW_OP_constu: 0)
>
> This is not correct DWARF! It's supposed to be a location description, and 
> this is merely a DWARF expression that evaluates to an offset relative to the 
> vtable pointer. The description of the attribute says that address of an 
> object of the enclosing type is pushed onto the expression stack, so there 
> really ought to be a DW_OP_deref to get the vtable pointer on the stack, 
> followed by the DW_OP_constu and DW_OP_add.
>
> Now if we compare this to DW_AT_data_member_location, we see that one valid 
> form for that attribute is an integer constant providing the offset of the 
> data member. But even there, if the attribute has a location expression, it 
> should compute an actual address, not just deliver the offset.
>
> It would seem an obvious and useful extension to DWARF to allow 
> DW_AT_vtable_elem_location to take a constant class form that provides the 
> offset relative to the start of the vtable, so an acceptable form of the 
> attribute might be:
>
>     <1b8>   DW_AT_vtable_elem_location: 0   # (using a constant class form)
>
> There's still the question of what do we do about the form GCC is already 
> emitting (and has been emitting since 2009)? Make it legal? Let it slide and 
> ask the compilers to fix it?

I spent my formative years in the world of web browsers where you
generally can't effectively push back on content generators so my gut
reaction is that reifying the existing behavior in the spec makes a
lot more sense than changing gcc/clang/etc.

> Getting back to the original request, do we need an issue to provide an 
> officially-blessed DWARF way to find the vtable pointer? The current approach 
> seems rather hacky, especially with respect to attaching a special meaning to 
> the DW_AT_name attribute. Perhaps a DW_AT_vtable_ptr_location attribute on 
> the class? And maybe a DW_OP_push_vtable_location operator?

I think the current approach is fine, tbh. In practice this isn't an issue.

>>
>> Then things begin to get hairy, the debugger demangles the mangled
>> name that exists in the ELF symbol table, chops off the "vtable for "
>> prefix on the demangled name, and searches for the type by name in the
>> DWARF. If it finds the type, it adjusts the type of the value and
>> prints it accordingly. But this text based matching doesn't always
>> work. There are no mangled names for types so the debugger's
>> demangling has to match the compiler's output character for character.
>
>
> Yes, things are really hairy here! It seems like you'd want some DWARF-based 
> RTTI that could be as simple as a DW_AT_vtable_location on each class? That 
> should let you map a vtable pointer onto the DIE for the class of the object 
> you have. (Although it may be expensive, since building that lookup table 
> would require parsing the whole DIE tree.) The LLVM discussion Michael 
> pointed to does seem relevant.

Something along those lines that says "if an instance of an object has
vtable pointer 0x12345678 then this DW_TAG_class_type is the concrete
type".

>>
>> Perhaps these are merely quality of implementation issues and belong
>> on the respective bug trackers, however, better representations are
>> possible. Rustc, for example, does not rely on the ELF symbol table
>> and demangled string matching. It emits a global variable in the DWARF
>> whose location is the address of the vtable. That variable has a
>> DW_AT_type pointing to a DW_TAG_class_type that describes the layout
>> of the vtable, and that type has a DW_AT_containing_type that points
>> to the type making use of that vtable.
>
>
> Is this an artificial global variable? How would that help vs. just recording 
> the address of the vtable as an attribute of the class?

It should be (https://github.com/rust-lang/rust/issues/125126).

Having it be a global is nice precisely because it doesn't require the
debugger to trawl the entire DIE tree up front, only the top level.

- Kyle

> -cary
>
-- 
Dwarf-discuss mailing list
Dwarf-discuss@lists.dwarfstd.org
https://lists.dwarfstd.org/mailman/listinfo/dwarf-discuss

Reply via email to