1033 lines
29 KiB
C++
1033 lines
29 KiB
C++
/* Generate CTF types and objects from the GCC DWARF.
|
|
Copyright (C) 2021-2022 Free Software Foundation, Inc.
|
|
|
|
This file is part of GCC.
|
|
|
|
GCC is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
version.
|
|
|
|
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with GCC; see the file COPYING3. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include "config.h"
|
|
#include "system.h"
|
|
#include "coretypes.h"
|
|
#include "target.h"
|
|
#include "dwarf2out.h"
|
|
#include "dwarf2out.h"
|
|
|
|
#include "dwarf2ctf.h"
|
|
#include "ctfc.h"
|
|
|
|
/* Forward declarations for some routines defined in this file. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_type (ctf_container_ref, dw_die_ref);
|
|
|
|
/* All the DIE structures we handle come from the DWARF information
|
|
generated by GCC. However, there are three situations where we need
|
|
to create our own created DIE structures because GCC doesn't
|
|
provide them.
|
|
|
|
The DWARF spec suggests using a DIE with DW_TAG_unspecified_type
|
|
and name "void" in order to denote the void type. But GCC doesn't
|
|
follow this advice. Still we need a DIE to act as a key for void
|
|
types, we use ctf_void_die.
|
|
|
|
Also, if a subrange type corresponding to an array index does not
|
|
specify a type then we assume it is `int'.
|
|
|
|
Finally, for types unrepresentable in CTF, we need a DIE to anchor
|
|
them to a CTF type of kind unknown.
|
|
|
|
The variables below are initialized in ctf_debug_init and hold
|
|
references to the proper DIEs. */
|
|
|
|
static GTY (()) dw_die_ref ctf_void_die;
|
|
static GTY (()) dw_die_ref ctf_array_index_die;
|
|
static GTY (()) dw_die_ref ctf_unknown_die;
|
|
|
|
/* Some DIEs have a type description attribute, stored in a DW_AT_type
|
|
attribute. However, GCC generates no attribute to signify a `void'
|
|
type.
|
|
|
|
This can happen in many contexts (return type of a function,
|
|
pointed or qualified type, etc) so we use the `ctf_get_AT_type'
|
|
function below abstracts this. */
|
|
|
|
static dw_die_ref
|
|
ctf_get_AT_type (dw_die_ref die)
|
|
{
|
|
dw_die_ref type_die = get_AT_ref (die, DW_AT_type);
|
|
return (type_die ? type_die : ctf_void_die);
|
|
}
|
|
|
|
/* Some data member DIEs have location specified as a DWARF expression
|
|
(specifically in DWARF2). Luckily, the expression is a simple
|
|
DW_OP_plus_uconst with one operand set to zero.
|
|
|
|
Sometimes the data member location may also be negative. In yet some other
|
|
cases (specifically union data members), the data member location is
|
|
non-existent. Handle all these scenarios here to abstract this. */
|
|
|
|
static HOST_WIDE_INT
|
|
ctf_get_AT_data_member_location (dw_die_ref die)
|
|
{
|
|
HOST_WIDE_INT field_location = 0;
|
|
dw_attr_node * attr;
|
|
|
|
/* The field location (in bits) can be determined from
|
|
either a DW_AT_data_member_location attribute or a
|
|
DW_AT_data_bit_offset attribute. */
|
|
if (get_AT (die, DW_AT_data_bit_offset))
|
|
field_location = get_AT_unsigned (die, DW_AT_data_bit_offset);
|
|
else
|
|
{
|
|
attr = get_AT (die, DW_AT_data_member_location);
|
|
if (attr && AT_class (attr) == dw_val_class_loc)
|
|
{
|
|
dw_loc_descr_ref descr = AT_loc (attr);
|
|
/* Operand 2 must be zero; the structure is assumed to be on the
|
|
stack in DWARF2. */
|
|
gcc_assert (!descr->dw_loc_oprnd2.v.val_unsigned);
|
|
gcc_assert (descr->dw_loc_oprnd2.val_class
|
|
== dw_val_class_unsigned_const);
|
|
field_location = descr->dw_loc_oprnd1.v.val_unsigned * 8;
|
|
}
|
|
else
|
|
{
|
|
attr = get_AT (die, DW_AT_data_member_location);
|
|
if (attr && AT_class (attr) == dw_val_class_const)
|
|
field_location = AT_int (attr) * 8;
|
|
else
|
|
field_location = (get_AT_unsigned (die,
|
|
DW_AT_data_member_location)
|
|
* 8);
|
|
}
|
|
}
|
|
|
|
return field_location;
|
|
}
|
|
|
|
/* CTF Types' and CTF Variables' Location Information. CTF section does not
|
|
emit location information, this is provided for BTF CO-RE use-cases. These
|
|
functions fetch information from DWARF Die directly, as such the location
|
|
information is not buffered in the CTF container. */
|
|
|
|
const char *
|
|
ctf_get_die_loc_file (dw_die_ref die)
|
|
{
|
|
if (!die)
|
|
return NULL;
|
|
|
|
struct dwarf_file_data * file;
|
|
file = get_AT_file (die, DW_AT_decl_file);
|
|
if (!file)
|
|
return NULL;
|
|
|
|
return file->filename;
|
|
}
|
|
|
|
unsigned int
|
|
ctf_get_die_loc_line (dw_die_ref die)
|
|
{
|
|
if (!die)
|
|
return 0;
|
|
|
|
return get_AT_unsigned (die, DW_AT_decl_line);
|
|
}
|
|
|
|
unsigned int
|
|
ctf_get_die_loc_col (dw_die_ref die)
|
|
{
|
|
if (!die)
|
|
return 0;
|
|
|
|
return get_AT_unsigned (die, DW_AT_decl_column);
|
|
}
|
|
|
|
/* Generate CTF for the void type. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_void_type (ctf_container_ref ctfc)
|
|
{
|
|
ctf_encoding_t ctf_encoding = {0, 0, 0};
|
|
|
|
/* In CTF the void type is encoded as a 0-byte signed integer
|
|
type. */
|
|
|
|
ctf_encoding.cte_bits = 0;
|
|
ctf_encoding.cte_format = CTF_INT_SIGNED;
|
|
|
|
gcc_assert (ctf_void_die != NULL);
|
|
return ctf_add_integer (ctfc, CTF_ADD_ROOT, "void",
|
|
&ctf_encoding, ctf_void_die);
|
|
}
|
|
|
|
/* Generate CTF type of unknown kind. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_unknown_type (ctf_container_ref ctfc)
|
|
{
|
|
ctf_id_t unknown_type_id;
|
|
|
|
/* In CTF, the unknown type is encoded as a 0 byte sized type with kind
|
|
CTF_K_UNKNOWN. Create an encoding object merely to reuse the underlying
|
|
ctf_add_encoded interface; the CTF encoding object is not 'used' any more
|
|
than just the generation of size from. */
|
|
ctf_encoding_t ctf_encoding = {0, 0, 0};
|
|
|
|
gcc_assert (ctf_unknown_die != NULL);
|
|
/* Type de-duplication. */
|
|
if (!ctf_type_exists (ctfc, ctf_unknown_die, &unknown_type_id))
|
|
unknown_type_id = ctf_add_unknown (ctfc, CTF_ADD_ROOT, "unknown",
|
|
&ctf_encoding, ctf_unknown_die);
|
|
|
|
return unknown_type_id;
|
|
}
|
|
|
|
/* Sizes of entities can be given in bytes or bits. This function
|
|
abstracts this by returning the size in bits of the given entity.
|
|
If no DW_AT_byte_size nor DW_AT_bit_size are defined, this function
|
|
returns 0. */
|
|
|
|
static uint32_t
|
|
ctf_die_bitsize (dw_die_ref die)
|
|
{
|
|
dw_attr_node *attr_byte_size = get_AT (die, DW_AT_byte_size);
|
|
dw_attr_node *attr_bit_size = get_AT (die, DW_AT_bit_size);
|
|
|
|
if (attr_bit_size)
|
|
return AT_unsigned (attr_bit_size);
|
|
else if (attr_byte_size)
|
|
return (AT_unsigned (attr_byte_size) * 8);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
/* Generate CTF for base type (integer, boolean, real, fixed point and complex).
|
|
Important: the caller of this API must make sure that duplicate types are
|
|
not added. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_base_type (ctf_container_ref ctfc, dw_die_ref type)
|
|
{
|
|
ctf_id_t type_id = CTF_NULL_TYPEID;
|
|
|
|
ctf_encoding_t ctf_encoding = {0, 0, 0};
|
|
|
|
unsigned int encoding = get_AT_unsigned (type, DW_AT_encoding);
|
|
unsigned int bit_size = ctf_die_bitsize (type);
|
|
const char * name_string = get_AT_string (type, DW_AT_name);
|
|
|
|
switch (encoding)
|
|
{
|
|
case DW_ATE_void:
|
|
|
|
ctf_encoding.cte_format = CTF_INT_SIGNED;
|
|
ctf_encoding.cte_bits = 0;
|
|
|
|
gcc_assert (name_string);
|
|
type_id = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
|
|
&ctf_encoding, type);
|
|
|
|
break;
|
|
case DW_ATE_boolean:
|
|
|
|
ctf_encoding.cte_format = CTF_INT_BOOL;
|
|
ctf_encoding.cte_bits = bit_size;
|
|
|
|
gcc_assert (name_string);
|
|
type_id = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
|
|
&ctf_encoding, type);
|
|
break;
|
|
case DW_ATE_float:
|
|
{
|
|
unsigned int float_bit_size
|
|
= tree_to_uhwi (TYPE_SIZE (float_type_node));
|
|
unsigned int double_bit_size
|
|
= tree_to_uhwi (TYPE_SIZE (double_type_node));
|
|
unsigned int long_double_bit_size
|
|
= tree_to_uhwi (TYPE_SIZE (long_double_type_node));
|
|
|
|
if (bit_size == float_bit_size)
|
|
ctf_encoding.cte_format = CTF_FP_SINGLE;
|
|
else if (bit_size == double_bit_size)
|
|
ctf_encoding.cte_format = CTF_FP_DOUBLE;
|
|
else if (bit_size == long_double_bit_size)
|
|
ctf_encoding.cte_format = CTF_FP_LDOUBLE;
|
|
else
|
|
/* CTF does not have representation for other types. Skip them. */
|
|
break;
|
|
|
|
ctf_encoding.cte_bits = bit_size;
|
|
type_id = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
|
|
&ctf_encoding, type);
|
|
|
|
break;
|
|
}
|
|
case DW_ATE_signed_char:
|
|
/* FALLTHROUGH */
|
|
case DW_ATE_unsigned_char:
|
|
/* FALLTHROUGH */
|
|
case DW_ATE_signed:
|
|
/* FALLTHROUGH */
|
|
case DW_ATE_unsigned:
|
|
|
|
if (encoding == DW_ATE_signed_char
|
|
|| encoding == DW_ATE_unsigned_char)
|
|
ctf_encoding.cte_format |= CTF_INT_CHAR;
|
|
|
|
if (encoding == DW_ATE_signed
|
|
|| encoding == DW_ATE_signed_char)
|
|
ctf_encoding.cte_format |= CTF_INT_SIGNED;
|
|
|
|
ctf_encoding.cte_bits = bit_size;
|
|
type_id = ctf_add_integer (ctfc, CTF_ADD_ROOT, name_string,
|
|
&ctf_encoding, type);
|
|
break;
|
|
|
|
case DW_ATE_complex_float:
|
|
{
|
|
unsigned int float_bit_size
|
|
= tree_to_uhwi (TYPE_SIZE (float_type_node));
|
|
unsigned int double_bit_size
|
|
= tree_to_uhwi (TYPE_SIZE (double_type_node));
|
|
unsigned int long_double_bit_size
|
|
= tree_to_uhwi (TYPE_SIZE (long_double_type_node));
|
|
|
|
if (bit_size == float_bit_size * 2)
|
|
ctf_encoding.cte_format = CTF_FP_CPLX;
|
|
else if (bit_size == double_bit_size * 2)
|
|
ctf_encoding.cte_format = CTF_FP_DCPLX;
|
|
else if (bit_size == long_double_bit_size * 2)
|
|
ctf_encoding.cte_format = CTF_FP_LDCPLX;
|
|
else
|
|
/* CTF does not have representation for other types. Skip them. */
|
|
break;
|
|
|
|
ctf_encoding.cte_bits = bit_size;
|
|
type_id = ctf_add_float (ctfc, CTF_ADD_ROOT, name_string,
|
|
&ctf_encoding, type);
|
|
break;
|
|
}
|
|
default:
|
|
/* Ignore. */
|
|
break;
|
|
}
|
|
|
|
return type_id;
|
|
}
|
|
|
|
/* Generate CTF for a pointer type. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_pointer_type (ctf_container_ref ctfc, dw_die_ref ptr_type)
|
|
{
|
|
ctf_id_t type_id = CTF_NULL_TYPEID;
|
|
ctf_id_t ptr_type_id = CTF_NULL_TYPEID;
|
|
dw_die_ref pointed_type_die = ctf_get_AT_type (ptr_type);
|
|
|
|
type_id = gen_ctf_type (ctfc, pointed_type_die);
|
|
|
|
/* Type de-duplication.
|
|
Consult the ctfc_types hash again before adding the CTF pointer type
|
|
because there can be cases where a pointer type may have been added by
|
|
the gen_ctf_type call above. */
|
|
if (ctf_type_exists (ctfc, ptr_type, &ptr_type_id))
|
|
return ptr_type_id;
|
|
|
|
ptr_type_id = ctf_add_pointer (ctfc, CTF_ADD_ROOT, type_id, ptr_type);
|
|
return ptr_type_id;
|
|
}
|
|
|
|
/* Generate CTF for an array type. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_array_type (ctf_container_ref ctfc, dw_die_ref array_type)
|
|
{
|
|
dw_die_ref c;
|
|
ctf_id_t array_elems_type_id = CTF_NULL_TYPEID;
|
|
|
|
int vector_type_p = get_AT_flag (array_type, DW_AT_GNU_vector);
|
|
if (vector_type_p)
|
|
return array_elems_type_id;
|
|
|
|
dw_die_ref array_elems_type = ctf_get_AT_type (array_type);
|
|
|
|
/* First, register the type of the array elements if needed. */
|
|
array_elems_type_id = gen_ctf_type (ctfc, array_elems_type);
|
|
|
|
/* DWARF array types pretend C supports multi-dimensional arrays.
|
|
So for the type int[N][M], the array type DIE contains two
|
|
subrange_type children, the first with upper bound N-1 and the
|
|
second with upper bound M-1.
|
|
|
|
CTF, on the other hand, just encodes each array type in its own
|
|
array type CTF struct. Therefore we have to iterate on the
|
|
children and create all the needed types. */
|
|
|
|
c = dw_get_die_child (array_type);
|
|
gcc_assert (c);
|
|
do
|
|
{
|
|
ctf_arinfo_t arinfo;
|
|
dw_die_ref array_index_type;
|
|
uint32_t array_num_elements;
|
|
|
|
c = dw_get_die_sib (c);
|
|
|
|
if (dw_get_die_tag (c) == DW_TAG_subrange_type)
|
|
{
|
|
dw_attr_node *upper_bound_at;
|
|
|
|
array_index_type = ctf_get_AT_type (c);
|
|
|
|
/* When DW_AT_upper_bound is used to specify the size of an
|
|
array in DWARF, it is usually an unsigned constant
|
|
specifying the upper bound index of the array. However,
|
|
for unsized arrays, such as foo[] or bar[0],
|
|
DW_AT_upper_bound is a signed integer constant
|
|
instead. */
|
|
|
|
upper_bound_at = get_AT (c, DW_AT_upper_bound);
|
|
if (upper_bound_at
|
|
&& AT_class (upper_bound_at) == dw_val_class_unsigned_const)
|
|
/* This is the upper bound index. */
|
|
array_num_elements = get_AT_unsigned (c, DW_AT_upper_bound) + 1;
|
|
else if (get_AT (c, DW_AT_count))
|
|
array_num_elements = get_AT_unsigned (c, DW_AT_count);
|
|
else
|
|
{
|
|
/* This is a VLA of some kind. */
|
|
array_num_elements = 0;
|
|
}
|
|
}
|
|
else if (dw_get_die_tag (c) == DW_TAG_enumeration_type)
|
|
{
|
|
array_index_type = 0;
|
|
array_num_elements = 0;
|
|
/* XXX writeme. */
|
|
gcc_assert (1);
|
|
}
|
|
else
|
|
gcc_unreachable ();
|
|
|
|
/* Ok, mount and register the array type. Note how the array
|
|
type we register here is the type of the elements in
|
|
subsequent "dimensions", if there are any. */
|
|
|
|
arinfo.ctr_nelems = array_num_elements;
|
|
if (array_index_type)
|
|
arinfo.ctr_index = gen_ctf_type (ctfc, array_index_type);
|
|
else
|
|
arinfo.ctr_index = gen_ctf_type (ctfc, ctf_array_index_die);
|
|
|
|
arinfo.ctr_contents = array_elems_type_id;
|
|
if (!ctf_type_exists (ctfc, c, &array_elems_type_id))
|
|
array_elems_type_id = ctf_add_array (ctfc, CTF_ADD_ROOT, &arinfo,
|
|
c);
|
|
}
|
|
while (c != dw_get_die_child (array_type));
|
|
|
|
#if 0
|
|
/* Type de-duplication.
|
|
Consult the ctfc_types hash again before adding the CTF array type because
|
|
there can be cases where an array_type type may have been added by the
|
|
gen_ctf_type call above. */
|
|
if (!ctf_type_exists (ctfc, array_type, &type_id))
|
|
type_id = ctf_add_array (ctfc, CTF_ADD_ROOT, &arinfo, array_type);
|
|
#endif
|
|
|
|
return array_elems_type_id;
|
|
}
|
|
|
|
/* Generate CTF for a typedef. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_typedef (ctf_container_ref ctfc, dw_die_ref tdef)
|
|
{
|
|
ctf_id_t tdef_type_id, tid;
|
|
const char *tdef_name = get_AT_string (tdef, DW_AT_name);
|
|
dw_die_ref tdef_type = ctf_get_AT_type (tdef);
|
|
|
|
tid = gen_ctf_type (ctfc, tdef_type);
|
|
|
|
/* Type de-duplication.
|
|
This is necessary because the ctf for the typedef may have been already
|
|
added due to the gen_ctf_type call above. */
|
|
if (!ctf_type_exists (ctfc, tdef, &tdef_type_id))
|
|
{
|
|
tdef_type_id = ctf_add_typedef (ctfc, CTF_ADD_ROOT,
|
|
tdef_name,
|
|
tid,
|
|
tdef);
|
|
}
|
|
return tdef_type_id;
|
|
}
|
|
|
|
/* Generate CTF for a type modifier.
|
|
|
|
If the given DIE contains a valid C modifier (like _Atomic), which is not
|
|
supported by CTF, then this function skips the modifier die and continues
|
|
with the underlying type.
|
|
|
|
For all other cases, this function returns a CTF_NULL_TYPEID;
|
|
*/
|
|
|
|
static ctf_id_t
|
|
gen_ctf_modifier_type (ctf_container_ref ctfc, dw_die_ref modifier)
|
|
{
|
|
uint32_t kind = CTF_K_MAX;
|
|
ctf_id_t modifier_type_id, qual_type_id;
|
|
dw_die_ref qual_type = ctf_get_AT_type (modifier);
|
|
|
|
switch (dw_get_die_tag (modifier))
|
|
{
|
|
case DW_TAG_const_type: kind = CTF_K_CONST; break;
|
|
case DW_TAG_volatile_type: kind = CTF_K_VOLATILE; break;
|
|
case DW_TAG_restrict_type: kind = CTF_K_RESTRICT; break;
|
|
case DW_TAG_atomic_type: break;
|
|
default:
|
|
return CTF_NULL_TYPEID;
|
|
}
|
|
|
|
/* Register the type for which this modifier applies. */
|
|
qual_type_id = gen_ctf_type (ctfc, qual_type);
|
|
|
|
/* Skip generating a CTF modifier record for _Atomic as there is no
|
|
representation for it. */
|
|
if (dw_get_die_tag (modifier) == DW_TAG_atomic_type)
|
|
return qual_type_id;
|
|
|
|
gcc_assert (kind != CTF_K_MAX);
|
|
/* Now register the modifier itself. */
|
|
if (!ctf_type_exists (ctfc, modifier, &modifier_type_id))
|
|
modifier_type_id = ctf_add_reftype (ctfc, CTF_ADD_ROOT,
|
|
qual_type_id, kind,
|
|
modifier);
|
|
|
|
return modifier_type_id;
|
|
}
|
|
|
|
/* Generate CTF for a struct type. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_sou_type (ctf_container_ref ctfc, dw_die_ref sou, uint32_t kind)
|
|
{
|
|
uint32_t bit_size = ctf_die_bitsize (sou);
|
|
int declaration_p = get_AT_flag (sou, DW_AT_declaration);
|
|
const char *sou_name = get_AT_string (sou, DW_AT_name);
|
|
|
|
ctf_id_t sou_type_id;
|
|
|
|
/* An incomplete structure or union type is represented in DWARF by
|
|
a structure or union DIE that does not have a size attribute and
|
|
that has a DW_AT_declaration attribute. This corresponds to a
|
|
CTF forward type with kind CTF_K_STRUCT. */
|
|
if (bit_size == 0 && declaration_p)
|
|
return ctf_add_forward (ctfc, CTF_ADD_ROOT,
|
|
sou_name, kind, sou);
|
|
|
|
/* This is a complete struct or union type. Generate a CTF type for
|
|
it if it doesn't exist already. */
|
|
if (!ctf_type_exists (ctfc, sou, &sou_type_id))
|
|
sou_type_id = ctf_add_sou (ctfc, CTF_ADD_ROOT,
|
|
sou_name, kind, bit_size / 8,
|
|
sou);
|
|
|
|
/* Now process the struct members. */
|
|
{
|
|
dw_die_ref c;
|
|
|
|
c = dw_get_die_child (sou);
|
|
if (c)
|
|
do
|
|
{
|
|
const char *field_name;
|
|
dw_die_ref field_type;
|
|
HOST_WIDE_INT field_location;
|
|
ctf_id_t field_type_id;
|
|
|
|
c = dw_get_die_sib (c);
|
|
|
|
field_name = get_AT_string (c, DW_AT_name);
|
|
field_type = ctf_get_AT_type (c);
|
|
field_location = ctf_get_AT_data_member_location (c);
|
|
|
|
/* Generate the field type. */
|
|
field_type_id = gen_ctf_type (ctfc, field_type);
|
|
|
|
/* If this is a bit-field, then wrap the field type
|
|
generated above with a CTF slice. */
|
|
if (get_AT (c, DW_AT_bit_offset)
|
|
|| get_AT (c, DW_AT_data_bit_offset))
|
|
{
|
|
dw_attr_node *attr;
|
|
HOST_WIDE_INT bitpos = 0;
|
|
HOST_WIDE_INT bitsize = ctf_die_bitsize (c);
|
|
HOST_WIDE_INT bit_offset;
|
|
|
|
/* The bit offset is given in bits and it may be
|
|
negative. */
|
|
attr = get_AT (c, DW_AT_bit_offset);
|
|
if (attr)
|
|
{
|
|
if (AT_class (attr) == dw_val_class_unsigned_const)
|
|
bit_offset = AT_unsigned (attr);
|
|
else
|
|
bit_offset = AT_int (attr);
|
|
|
|
if (BYTES_BIG_ENDIAN)
|
|
bitpos = field_location + bit_offset;
|
|
else
|
|
{
|
|
HOST_WIDE_INT bit_size;
|
|
|
|
attr = get_AT (c, DW_AT_byte_size);
|
|
if (attr)
|
|
/* Explicit size given in bytes. */
|
|
bit_size = AT_unsigned (attr) * 8;
|
|
else
|
|
/* Infer the size from the member type. */
|
|
bit_size = ctf_die_bitsize (field_type);
|
|
|
|
bitpos = (field_location
|
|
+ bit_size
|
|
- bit_offset
|
|
- bitsize);
|
|
}
|
|
}
|
|
|
|
/* In DWARF5 a data_bit_offset attribute is given with
|
|
the offset of the data from the beginning of the
|
|
struct. Acknowledge it if present. */
|
|
attr = get_AT (c, DW_AT_data_bit_offset);
|
|
if (attr)
|
|
bitpos += AT_unsigned (attr);
|
|
|
|
field_type_id = ctf_add_slice (ctfc, CTF_ADD_NONROOT,
|
|
field_type_id,
|
|
bitpos - field_location,
|
|
bitsize,
|
|
c);
|
|
}
|
|
|
|
/* Add the field type to the struct or union type. */
|
|
ctf_add_member_offset (ctfc, sou,
|
|
field_name,
|
|
field_type_id,
|
|
field_location);
|
|
}
|
|
while (c != dw_get_die_child (sou));
|
|
}
|
|
|
|
return sou_type_id;
|
|
}
|
|
|
|
/* Generate CTF for a function type. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_function_type (ctf_container_ref ctfc, dw_die_ref function,
|
|
bool from_global_func)
|
|
{
|
|
const char *function_name = get_AT_string (function, DW_AT_name);
|
|
dw_die_ref return_type = ctf_get_AT_type (function);
|
|
|
|
ctf_funcinfo_t func_info;
|
|
uint32_t num_args = 0;
|
|
|
|
ctf_id_t return_type_id;
|
|
ctf_id_t function_type_id;
|
|
|
|
/* First, add the return type. */
|
|
return_type_id = gen_ctf_type (ctfc, return_type);
|
|
func_info.ctc_return = return_type_id;
|
|
|
|
/* Type de-duplication.
|
|
Consult the ctfc_types hash before adding the CTF function type. */
|
|
if (ctf_type_exists (ctfc, function, &function_type_id))
|
|
return function_type_id;
|
|
|
|
/* Do a first pass on the formals to determine the number of
|
|
arguments, and whether the function type gets a varargs. */
|
|
{
|
|
dw_die_ref c;
|
|
|
|
c = dw_get_die_child (function);
|
|
if (c)
|
|
do
|
|
{
|
|
c = dw_get_die_sib (c);
|
|
|
|
if (dw_get_die_tag (c) == DW_TAG_formal_parameter)
|
|
num_args += 1;
|
|
else if (dw_get_die_tag (c) == DW_TAG_unspecified_parameters)
|
|
{
|
|
func_info.ctc_flags |= CTF_FUNC_VARARG;
|
|
num_args += 1;
|
|
}
|
|
}
|
|
while (c != dw_get_die_child (function));
|
|
}
|
|
|
|
/* Note the number of typed arguments _includes_ the vararg. */
|
|
func_info.ctc_argc = num_args;
|
|
|
|
/* Type de-duplication has already been performed by now. */
|
|
function_type_id = ctf_add_function (ctfc, CTF_ADD_ROOT,
|
|
function_name,
|
|
(const ctf_funcinfo_t *)&func_info,
|
|
function,
|
|
from_global_func);
|
|
|
|
/* Second pass on formals: generate the CTF types corresponding to
|
|
them and add them as CTF function args. */
|
|
{
|
|
dw_die_ref c;
|
|
unsigned int i = 0;
|
|
const char *arg_name;
|
|
ctf_id_t arg_type;
|
|
|
|
c = dw_get_die_child (function);
|
|
if (c)
|
|
do
|
|
{
|
|
c = dw_get_die_sib (c);
|
|
|
|
if (dw_get_die_tag (c) == DW_TAG_unspecified_parameters)
|
|
{
|
|
gcc_assert (i == num_args - 1);
|
|
/* Add an argument with type 0 and no name. */
|
|
ctf_add_function_arg (ctfc, function, "", 0);
|
|
}
|
|
else if (dw_get_die_tag (c) == DW_TAG_formal_parameter)
|
|
{
|
|
i++;
|
|
arg_name = get_AT_string (c, DW_AT_name);
|
|
arg_type = gen_ctf_type (ctfc, ctf_get_AT_type (c));
|
|
/* Add the argument to the existing CTF function type. */
|
|
ctf_add_function_arg (ctfc, function, arg_name, arg_type);
|
|
}
|
|
else
|
|
/* This is a local variable. Ignore. */
|
|
continue;
|
|
}
|
|
while (c != dw_get_die_child (function));
|
|
}
|
|
|
|
return function_type_id;
|
|
}
|
|
|
|
/* Generate CTF for an enumeration type. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_enumeration_type (ctf_container_ref ctfc, dw_die_ref enumeration)
|
|
{
|
|
const char *enum_name = get_AT_string (enumeration, DW_AT_name);
|
|
unsigned int bit_size = ctf_die_bitsize (enumeration);
|
|
int declaration_p = get_AT_flag (enumeration, DW_AT_declaration);
|
|
|
|
ctf_id_t enumeration_type_id;
|
|
|
|
/* If this is an incomplete enum, generate a CTF forward for it and
|
|
be done. */
|
|
if (declaration_p)
|
|
{
|
|
gcc_assert (enum_name);
|
|
return ctf_add_forward (ctfc, CTF_ADD_ROOT, enum_name,
|
|
CTF_K_ENUM, enumeration);
|
|
}
|
|
|
|
/* If the size the enumerators is not specified then use the size of
|
|
the underlying type, which must be a base type. */
|
|
if (bit_size == 0)
|
|
{
|
|
dw_die_ref type = ctf_get_AT_type (enumeration);
|
|
bit_size = ctf_die_bitsize (type);
|
|
}
|
|
|
|
/* Generate a CTF type for the enumeration. */
|
|
enumeration_type_id = ctf_add_enum (ctfc, CTF_ADD_ROOT,
|
|
enum_name, bit_size / 8, enumeration);
|
|
|
|
/* Process the enumerators. */
|
|
{
|
|
dw_die_ref c;
|
|
|
|
c = dw_get_die_child (enumeration);
|
|
if (c)
|
|
do
|
|
{
|
|
const char *enumerator_name;
|
|
dw_attr_node *enumerator_value;
|
|
HOST_WIDE_INT value_wide_int;
|
|
|
|
c = dw_get_die_sib (c);
|
|
|
|
enumerator_name = get_AT_string (c, DW_AT_name);
|
|
enumerator_value = get_AT (c, DW_AT_const_value);
|
|
|
|
/* enumerator_value can be either a signed or an unsigned
|
|
constant value. */
|
|
if (AT_class (enumerator_value) == dw_val_class_unsigned_const
|
|
|| (AT_class (enumerator_value)
|
|
== dw_val_class_unsigned_const_implicit))
|
|
value_wide_int = AT_unsigned (enumerator_value);
|
|
else
|
|
value_wide_int = AT_int (enumerator_value);
|
|
|
|
ctf_add_enumerator (ctfc, enumeration_type_id,
|
|
enumerator_name, value_wide_int, enumeration);
|
|
}
|
|
while (c != dw_get_die_child (enumeration));
|
|
}
|
|
|
|
return enumeration_type_id;
|
|
}
|
|
|
|
/* Add a CTF variable record for the given input DWARF DIE. */
|
|
|
|
static void
|
|
gen_ctf_variable (ctf_container_ref ctfc, dw_die_ref die)
|
|
{
|
|
const char *var_name = get_AT_string (die, DW_AT_name);
|
|
dw_die_ref var_type = ctf_get_AT_type (die);
|
|
unsigned int external_vis = get_AT_flag (die, DW_AT_external);
|
|
ctf_id_t var_type_id;
|
|
|
|
/* Avoid duplicates. */
|
|
if (ctf_dvd_lookup (ctfc, die))
|
|
return;
|
|
|
|
/* Do not generate CTF variable records for non-defining incomplete
|
|
declarations. Such declarations can be known via the DWARF
|
|
DW_AT_specification attribute. */
|
|
if (ctf_dvd_ignore_lookup (ctfc, die))
|
|
return;
|
|
|
|
/* The value of the DW_AT_specification attribute, if present, is a
|
|
reference to the debugging information entry representing the
|
|
non-defining declaration. */
|
|
dw_die_ref decl = get_AT_ref (die, DW_AT_specification);
|
|
|
|
/* Add the type of the variable. */
|
|
var_type_id = gen_ctf_type (ctfc, var_type);
|
|
|
|
/* Generate the new CTF variable and update global counter. */
|
|
(void) ctf_add_variable (ctfc, var_name, var_type_id, die, external_vis,
|
|
decl);
|
|
/* Skip updating the number of global objects at this time. This is updated
|
|
later after pre-processing as some CTF variable records although
|
|
generated now, will not be emitted later. [PR105089]. */
|
|
}
|
|
|
|
/* Add a CTF function record for the given input DWARF DIE. */
|
|
|
|
static void
|
|
gen_ctf_function (ctf_container_ref ctfc, dw_die_ref die)
|
|
{
|
|
ctf_id_t function_type_id;
|
|
/* Type de-duplication.
|
|
Consult the ctfc_types hash before adding the CTF function type. */
|
|
if (ctf_type_exists (ctfc, die, &function_type_id))
|
|
return;
|
|
|
|
/* Add the type of the function and update the global functions
|
|
counter. Note that DWARF encodes function types in both
|
|
DW_TAG_subroutine_type and DW_TAG_subprogram in exactly the same
|
|
way. */
|
|
(void) gen_ctf_function_type (ctfc, die, true /* from_global_func */);
|
|
ctfc->ctfc_num_global_funcs += 1;
|
|
}
|
|
|
|
/* Add CTF type record(s) for the given input DWARF DIE and return its type id.
|
|
|
|
If there is already a CTF type corresponding to the given DIE, then
|
|
this function returns the type id of the existing type.
|
|
|
|
If the given DIE is not recognized as a type, then this function
|
|
returns CTF_NULL_TYPEID. */
|
|
|
|
static ctf_id_t
|
|
gen_ctf_type (ctf_container_ref ctfc, dw_die_ref die)
|
|
{
|
|
ctf_id_t type_id;
|
|
int unrecog_die = false;
|
|
|
|
if (ctf_type_exists (ctfc, die, &type_id))
|
|
return type_id;
|
|
|
|
switch (dw_get_die_tag (die))
|
|
{
|
|
case DW_TAG_base_type:
|
|
type_id = gen_ctf_base_type (ctfc, die);
|
|
break;
|
|
case DW_TAG_pointer_type:
|
|
type_id = gen_ctf_pointer_type (ctfc, die);
|
|
break;
|
|
case DW_TAG_typedef:
|
|
type_id = gen_ctf_typedef (ctfc, die);
|
|
break;
|
|
case DW_TAG_array_type:
|
|
type_id = gen_ctf_array_type (ctfc, die);
|
|
break;
|
|
case DW_TAG_structure_type:
|
|
type_id = gen_ctf_sou_type (ctfc, die, CTF_K_STRUCT);
|
|
break;
|
|
case DW_TAG_union_type:
|
|
type_id = gen_ctf_sou_type (ctfc, die, CTF_K_UNION);
|
|
break;
|
|
case DW_TAG_subroutine_type:
|
|
type_id = gen_ctf_function_type (ctfc, die,
|
|
false /* from_global_func */);
|
|
break;
|
|
case DW_TAG_enumeration_type:
|
|
type_id = gen_ctf_enumeration_type (ctfc, die);
|
|
break;
|
|
case DW_TAG_atomic_type:
|
|
/* FALLTHROUGH */
|
|
case DW_TAG_const_type:
|
|
/* FALLTHROUGH */
|
|
case DW_TAG_restrict_type:
|
|
/* FALLTHROUGH */
|
|
case DW_TAG_volatile_type:
|
|
type_id = gen_ctf_modifier_type (ctfc, die);
|
|
break;
|
|
case DW_TAG_unspecified_type:
|
|
{
|
|
const char *name = get_AT_string (die, DW_AT_name);
|
|
|
|
if (name && strcmp (name, "void") == 0)
|
|
type_id = gen_ctf_void_type (ctfc);
|
|
else
|
|
type_id = CTF_NULL_TYPEID;
|
|
|
|
break;
|
|
}
|
|
case DW_TAG_reference_type:
|
|
type_id = CTF_NULL_TYPEID;
|
|
break;
|
|
default:
|
|
/* Unrecognized DIE. */
|
|
unrecog_die = true;
|
|
type_id = CTF_NULL_TYPEID;
|
|
break;
|
|
}
|
|
|
|
/* For all types unrepresented in CTF, use an explicit CTF type of kind
|
|
CTF_K_UNKNOWN. */
|
|
if ((type_id == CTF_NULL_TYPEID) && (!unrecog_die))
|
|
type_id = gen_ctf_unknown_type (ctfc);
|
|
|
|
return type_id;
|
|
}
|
|
|
|
/* Prepare for output and write out the CTF debug information. */
|
|
|
|
static void
|
|
ctf_debug_finalize (const char *filename, bool btf)
|
|
{
|
|
if (btf)
|
|
{
|
|
btf_output (filename);
|
|
btf_finalize ();
|
|
}
|
|
|
|
else
|
|
{
|
|
/* Emit the collected CTF information. */
|
|
ctf_output (filename);
|
|
|
|
/* Reset the CTF state. */
|
|
ctf_finalize ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
ctf_do_die (dw_die_ref die)
|
|
{
|
|
ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
|
|
|
|
/* Note how we tell the caller to continue traversing children DIEs
|
|
if this DIE didn't result in CTF records being added. */
|
|
if (dw_get_die_tag (die) == DW_TAG_variable)
|
|
{
|
|
gen_ctf_variable (tu_ctfc, die);
|
|
return false;
|
|
}
|
|
else if (dw_get_die_tag (die) == DW_TAG_subprogram)
|
|
{
|
|
gen_ctf_function (tu_ctfc, die);
|
|
return false;
|
|
}
|
|
else
|
|
return gen_ctf_type (tu_ctfc, die) == CTF_NULL_TYPEID;
|
|
}
|
|
|
|
/* Initialize CTF subsystem for CTF debug info generation. */
|
|
|
|
void
|
|
ctf_debug_init (void)
|
|
{
|
|
/* First, initialize the CTF subsystem. */
|
|
ctf_init ();
|
|
|
|
/* Create a couple of DIE structures that we may need. */
|
|
ctf_void_die = new_die_raw (DW_TAG_unspecified_type);
|
|
add_name_attribute (ctf_void_die, "void");
|
|
ctf_array_index_die
|
|
= base_type_die (integer_type_node, 0 /* reverse */);
|
|
add_name_attribute (ctf_array_index_die, "int");
|
|
ctf_unknown_die = new_die_raw (DW_TAG_unspecified_type);
|
|
add_name_attribute (ctf_unknown_die, "unknown");
|
|
}
|
|
|
|
/* Preprocess the CTF debug information after initialization. */
|
|
|
|
void
|
|
ctf_debug_init_postprocess (bool btf)
|
|
{
|
|
/* Only BTF requires postprocessing right after init. */
|
|
if (btf)
|
|
btf_init_postprocess ();
|
|
}
|
|
|
|
/* Early finish CTF/BTF debug info. */
|
|
|
|
void
|
|
ctf_debug_early_finish (const char * filename)
|
|
{
|
|
/* Emit CTF debug info early always. */
|
|
if (ctf_debug_info_level > CTFINFO_LEVEL_NONE
|
|
/* Emit BTF debug info early if CO-RE relocations are not
|
|
required. */
|
|
|| (btf_debuginfo_p () && !btf_with_core_debuginfo_p ()))
|
|
ctf_debug_finalize (filename, btf_debuginfo_p ());
|
|
}
|
|
|
|
/* Finish CTF/BTF debug info emission. */
|
|
|
|
void
|
|
ctf_debug_finish (const char * filename)
|
|
{
|
|
/* Emit BTF debug info here when CO-RE relocations need to be generated.
|
|
BTF with CO-RE relocations needs to be generated when CO-RE is in effect
|
|
for the BPF target. */
|
|
if (btf_with_core_debuginfo_p ())
|
|
{
|
|
gcc_assert (btf_debuginfo_p ());
|
|
ctf_debug_finalize (filename, btf_debuginfo_p ());
|
|
}
|
|
}
|
|
|
|
#include "gt-dwarf2ctf.h"
|