суббота, 15 апреля 2023 г.

custom attributes in gcc and dwarf

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

and then follows instructions

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):

so lets add function add_param_direction in dwarf2out.cc:
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;
}
It first checks if parameter has attribute param_in or param_out (but not both at the same time bcs this is senseless) and adds custom dwarf flag attribute via add_AT_flag call. Then we just need to call this function from gen_formal_parameter_die
 
Now we can add our custom attributes - this can be done via plugin but I preferred to patch c-family/c-attribs.cc:
 
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;
}

Function 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
 
All patches located here 

results

Lets define couple of new keywords:

#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

Комментариев нет:

Отправить комментарий