Lets check if we can add our own attributes (if Google can afford it, then why is it forbidden to mere mortals?). For example I want to have in gcc and dwarf flag about functions/methods parameters direction - is some param IN or OUT. I chose the value of this dwarf attribure 0x28ff
It`s pretty obviously that we can add our own custom attribute in gcc - they even have example how to do this. But what about dwarf producer? Long story short - seems that you cannot do it from plugin. The only dwarf related pass for plugins is pass_dwarf2_frame. So we need to patch gcc. But before this we need to
build gcc from sources
At moment of writing latest stable version of gcc was 12.0 so run
git clone --branch releases/gcc-12 https://github.com/gcc-mirror/gcc.git
patch gcc
Lets see how gcc produces dwarf output. All symbol table formatters implement gcc_debug_hooks and currently gcc has 3 (btw there are patches for mingw to produce PDB, so in theory you could have vmlinux.pdb):
- dwarf2out.cc - this is our target
- godump.c
- vmsdbgout.c
bool add_param_direction(tree decl, dw_die_ref parm_die)
{
bool pa1 = lookup_attribute ("param_in", DECL_ATTRIBUTES (decl));
bool pa2 = lookup_attribute ("param_out", DECL_ATTRIBUTES (decl));
if ( !(pa1 ^ pa2) )
return false;
unsigned char pa_value = 0;
// seems that you can`t have flag with value 1 - see gcc_assert at line 9599
if ( pa1 )
pa_value = 2;
if ( pa2 )
pa_value = 3;
add_AT_flag(parm_die, (dwarf_attribute)0x28ff, pa_value);
return true;
}
tree handle_param_in_attribute (tree *node, tree name, tree ARG_UNUSED (args),
int ARG_UNUSED(flags), bool *no_add_attrs)
{
if ( !DECL_P (*node) )
{
warning (OPT_Wattributes, "%qE attribute can apply to params declarations only", name);
*no_add_attrs = true;
return NULL_TREE;
}
tree decl = *node;
if (TREE_CODE (decl) != PARM_DECL)
{
warning (OPT_Wattributes, "%qE attribute can apply to params only", name);
*no_add_attrs = true;
} else {
// check presense of param_out
if ( lookup_attribute ("param_out", DECL_ATTRIBUTES (decl)) )
{
warning (OPT_Wattributes, "%qE attribute useless when param_out was used", name);
*no_add_attrs = true;
DECL_ATTRIBUTES (decl) = remove_attribute("param_out", DECL_ATTRIBUTES (decl));
}
}
return NULL_TREE;
}
handle_param_in_attribute
checks that this attribute linked with function/method parameter. Then it checks that the same parameter don`t have attribute param_out - in this case it just removes both results
#define IN __attribute__((param_in))
#define OUT __attribute__((param_out))
and mark some arguments as IN or OUT - for example for method bool PlainRender::dump_type(uint64_t key, OUT std::string &res, named *n, int level)
After rebuilding of debug version with our patched gcc we can see in objdump output something like this
<1><10d8ac>: Abbrev Number: 18 (DW_TAG_subprogram)
<10d8ad> DW_AT_specification: <0x103579>
<10d8b1> DW_AT_object_pointer: <0x10d8cc>
<10d8b5> DW_AT_low_pc : 0x4301a6
<10d8bd> DW_AT_high_pc : 0x4317e7
<10d8c5> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
<10d8c7> DW_AT_GNU_all_tail_call_sites: 1
<10d8c8> DW_AT_sibling : <0x10db29>
<2><10d8cc>: Abbrev Number: 10 (DW_TAG_formal_parameter)
<10d8cd> DW_AT_name : (indirect string, offset: 0x107d3): this
<10d8d1> DW_AT_type : <0x1035e5>
<10d8d5> DW_AT_artificial : 1
<10d8d6> DW_AT_location : 3 byte block: 91 98 7c (DW_OP_fbreg: -488)
<2><10d8da>: Abbrev Number: 45 (DW_TAG_formal_parameter)
<10d8db> DW_AT_name : key
<10d8df> DW_AT_decl_file : 1
<10d8e0> DW_AT_decl_line : 123
<10d8e1> DW_AT_decl_column : 38
<10d8e2> DW_AT_type : <0xfb4be>
<10d8e6> DW_AT_location : 3 byte block: 91 90 7c (DW_OP_fbreg: -496)
<2><10d8ea>: Abbrev Number: 234 (DW_TAG_formal_parameter)
<10d8ec> Unknown AT value: 28ff: 3
<10d8ed> DW_AT_name : res