| /* Generate CTF types and objects from the GCC DWARF. |
| Copyright (C) 2021-2024 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; |
| } |
| |
| /* Recursively generate CTF for array dimensions starting at DIE C (of type |
| DW_TAG_subrange_type) until DIE LAST (of type DW_TAG_subrange_type) is |
| reached. ARRAY_ELEMS_TYPE_ID is base type for the array. */ |
| |
| static ctf_id_t |
| gen_ctf_subrange_type (ctf_container_ref ctfc, ctf_id_t array_elems_type_id, |
| dw_die_ref c, dw_die_ref last) |
| { |
| ctf_arinfo_t arinfo; |
| ctf_id_t array_node_type_id = CTF_NULL_TYPEID; |
| |
| dw_attr_node *upper_bound_at; |
| dw_die_ref array_index_type; |
| uint32_t array_num_elements; |
| |
| if (dw_get_die_tag (c) == DW_TAG_subrange_type) |
| { |
| /* 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 |
| 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; |
| |
| array_index_type = ctf_get_AT_type (c); |
| arinfo.ctr_index = gen_ctf_type (ctfc, array_index_type); |
| |
| if (c == last) |
| arinfo.ctr_contents = array_elems_type_id; |
| else |
| arinfo.ctr_contents = gen_ctf_subrange_type (ctfc, array_elems_type_id, |
| dw_get_die_sib (c), last); |
| |
| if (!ctf_type_exists (ctfc, c, &array_node_type_id)) |
| array_node_type_id = ctf_add_array (ctfc, CTF_ADD_ROOT, &arinfo, c); |
| |
| return array_node_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 first, last, array_elems_type; |
| ctf_id_t array_elems_type_id = CTF_NULL_TYPEID; |
| ctf_id_t array_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; |
| |
| /* Find the first and last array dimension DIEs. */ |
| last = dw_get_die_child (array_type); |
| first = dw_get_die_sib (last); |
| |
| /* Type de-duplication. |
| Consult the ctfc_types before adding CTF type for the first dimension. */ |
| if (!ctf_type_exists (ctfc, first, &array_type_id)) |
| { |
| 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); |
| |
| array_type_id = gen_ctf_subrange_type (ctfc, array_elems_type_id, first, |
| last); |
| } |
| |
| return array_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); |
| |
| /* This is not precisely a TBD_CTF_REPRESENTATION_LIMIT, but |
| surely something to look at for the next format version bump |
| for CTF. */ |
| if (bitsize <= 255 && (bitpos - field_location) <= 255) |
| field_type_id = ctf_add_slice (ctfc, CTF_ADD_NONROOT, |
| field_type_id, |
| bitpos - field_location, |
| bitsize, c); |
| else |
| field_type_id = gen_ctf_unknown_type (ctfc); |
| } |
| |
| /* 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; |
| int linkage = get_AT_flag (function, DW_AT_external); |
| |
| 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, |
| linkage); |
| |
| /* 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); |
| unsigned int signedness = get_AT_unsigned (enumeration, DW_AT_encoding); |
| 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, |
| (signedness == DW_ATE_unsigned), |
| 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 when compiling BPF applciations gets deallocated by the |
| BPF target in bpf_file_end. */ |
| if (btf_debuginfo_p () && !btf_with_core_debuginfo_p ()) |
| 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_debuginfo_p () && btf_with_core_debuginfo_p ()) |
| ctf_debug_finalize (filename, btf_debuginfo_p ()); |
| } |
| |
| #include "gt-dwarf2ctf.h" |