| /* Generate CTF. |
| Copyright (C) 2019-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 "toplev.h" |
| #include "ctfc.h" |
| #include "diagnostic-core.h" |
| |
| /* A CTF container object - one per translation unit. */ |
| |
| ctf_container_ref tu_ctfc; |
| |
| ctf_container_ref |
| ctf_get_tu_ctfc (void) |
| { |
| return tu_ctfc; |
| } |
| |
| /* If the next ctf type id is still set to the init value, no ctf records to |
| report. */ |
| bool |
| ctfc_is_empty_container (ctf_container_ref ctfc) |
| { |
| return ((ctfc)->ctfc_nextid == CTF_INIT_TYPEID); |
| } |
| |
| /* Get the total number of CTF types in the container. */ |
| |
| unsigned int |
| ctfc_get_num_ctf_types (ctf_container_ref ctfc) |
| { |
| return ctfc->ctfc_types->elements (); |
| } |
| |
| /* Get the total number of CTF variables in the container. */ |
| |
| unsigned int ctfc_get_num_ctf_vars (ctf_container_ref ctfc) |
| { |
| return ctfc->ctfc_vars->elements (); |
| } |
| |
| /* Get reference to the CTF string table or the CTF auxilliary |
| string table. */ |
| |
| ctf_strtable_t * |
| ctfc_get_strtab (ctf_container_ref ctfc, int aux) |
| { |
| return aux ? &(ctfc)->ctfc_aux_strtable : &(ctfc->ctfc_strtable); |
| } |
| |
| /* Get the length of the specified string table of the CTF container. */ |
| |
| size_t |
| ctfc_get_strtab_len (ctf_container_ref ctfc, int aux) |
| { |
| ctf_strtable_t * strtab = ctfc_get_strtab (ctfc, aux); |
| return strtab->ctstab_len; |
| } |
| |
| /* Get the number of bytes to represent the variable length portion of all CTF |
| types in the CTF container. */ |
| |
| size_t ctfc_get_num_vlen_bytes (ctf_container_ref ctfc) |
| { |
| return ctfc->ctfc_num_vlen_bytes; |
| } |
| |
| /* Return which member of the union is used in CTFTYPE. Used for garbage |
| collection. */ |
| |
| enum ctf_dtu_d_union_enum |
| ctf_dtu_d_union_selector (ctf_dtdef_ref ctftype) |
| { |
| uint32_t kind = CTF_V2_INFO_KIND (ctftype->dtd_data.ctti_info); |
| switch (kind) |
| { |
| case CTF_K_UNKNOWN: |
| case CTF_K_INTEGER: |
| case CTF_K_FLOAT: |
| return CTF_DTU_D_ENCODING; |
| case CTF_K_STRUCT: |
| case CTF_K_UNION: |
| case CTF_K_ENUM: |
| return CTF_DTU_D_MEMBERS; |
| case CTF_K_ARRAY: |
| return CTF_DTU_D_ARRAY; |
| case CTF_K_FUNCTION: |
| return CTF_DTU_D_ARGUMENTS; |
| case CTF_K_SLICE: |
| return CTF_DTU_D_SLICE; |
| default: |
| /* The largest member as default. */ |
| return CTF_DTU_D_ARRAY; |
| } |
| } |
| |
| /* Insert CTF type into the CTF container. */ |
| |
| static void |
| ctf_dtd_insert (ctf_container_ref ctfc, ctf_dtdef_ref dtd) |
| { |
| bool existed = false; |
| ctf_dtdef_ref entry = dtd; |
| |
| ctf_dtdef_ref * item = ctfc->ctfc_types->find_slot (entry, INSERT); |
| if (*item == NULL) |
| *item = dtd; |
| else |
| existed = true; |
| /* Duplicate CTF type records not expected to be inserted. */ |
| gcc_assert (!existed); |
| } |
| |
| /* Lookup CTF type given a DWARF die for the type. */ |
| |
| ctf_dtdef_ref |
| ctf_dtd_lookup (const ctf_container_ref ctfc, const dw_die_ref type) |
| { |
| ctf_dtdef_t entry; |
| entry.dtd_key = type; |
| |
| ctf_dtdef_ref * slot = ctfc->ctfc_types->find_slot (&entry, NO_INSERT); |
| |
| if (slot) |
| return (ctf_dtdef_ref)*slot; |
| |
| return NULL; |
| } |
| |
| /* Insert CTF variable into the CTF container. */ |
| |
| static void |
| ctf_dvd_insert (ctf_container_ref ctfc, ctf_dvdef_ref dvd) |
| { |
| bool existed = false; |
| ctf_dvdef_ref entry = dvd; |
| |
| ctf_dvdef_ref * item = ctfc->ctfc_vars->find_slot (entry, INSERT); |
| if (*item == NULL) |
| *item = dvd; |
| else |
| existed = true; |
| /* Duplicate variable records not expected to be inserted. */ |
| gcc_assert (!existed); |
| } |
| |
| /* Lookup CTF variable given a DWARF die for the decl. */ |
| |
| ctf_dvdef_ref |
| ctf_dvd_lookup (const ctf_container_ref ctfc, dw_die_ref die) |
| { |
| ctf_dvdef_t entry; |
| entry.dvd_key = die; |
| |
| ctf_dvdef_ref * slot = ctfc->ctfc_vars->find_slot (&entry, NO_INSERT); |
| |
| if (slot) |
| return (ctf_dvdef_ref)*slot; |
| |
| return NULL; |
| } |
| |
| /* Insert a dummy CTF variable into the list of variables to be ignored. */ |
| |
| static void |
| ctf_dvd_ignore_insert (ctf_container_ref ctfc, ctf_dvdef_ref dvd) |
| { |
| bool existed = false; |
| ctf_dvdef_ref entry = dvd; |
| |
| ctf_dvdef_ref * item = ctfc->ctfc_ignore_vars->find_slot (entry, INSERT); |
| if (*item == NULL) |
| *item = dvd; |
| else |
| existed = true; |
| /* Duplicate variable records not expected to be inserted. */ |
| gcc_assert (!existed); |
| } |
| |
| /* Lookup the dummy CTF variable given the DWARF die for the non-defining |
| decl to be ignored. */ |
| |
| bool |
| ctf_dvd_ignore_lookup (const ctf_container_ref ctfc, dw_die_ref die) |
| { |
| ctf_dvdef_t entry; |
| entry.dvd_key = die; |
| |
| ctf_dvdef_ref * slot = ctfc->ctfc_ignore_vars->find_slot (&entry, NO_INSERT); |
| |
| if (slot) |
| return true; |
| |
| return false; |
| } |
| |
| /* Append member definition to the list. Member list is a singly-linked list |
| with list start pointing to the head. */ |
| |
| static void |
| ctf_dmd_list_append (ctf_dmdef_t ** dmd, ctf_dmdef_t * elem) |
| { |
| ctf_dmdef_t * tail = (dmd && *dmd) ? *dmd : NULL; |
| if (tail) |
| { |
| while (tail->dmd_next) |
| tail = tail->dmd_next; |
| |
| tail->dmd_next = elem; |
| } |
| else |
| *dmd = elem; |
| |
| elem->dmd_next = NULL; |
| } |
| |
| /* Append function argument to the list. Member list is a singly-linked list |
| with list start pointing to the head. */ |
| |
| static void |
| ctf_farg_list_append (ctf_func_arg_t ** farg, ctf_func_arg_t * elem) |
| { |
| ctf_func_arg_t * tail = (farg && *farg) ? *farg : NULL; |
| if (tail) |
| { |
| while (tail->farg_next) |
| tail = tail->farg_next; |
| |
| tail->farg_next = elem; |
| } |
| else |
| *farg = elem; |
| |
| elem->farg_next = NULL; |
| } |
| |
| /* Append str to the CTF string table. */ |
| |
| static void |
| ctfc_strtable_append_str (ctf_strtable_t * str_table, const char * str) |
| { |
| ctf_string_t * ctf_string = ggc_cleared_alloc<ctf_string_t> (); |
| /* Keep a reference to the input STR. */ |
| ctf_string->cts_str = str; |
| ctf_string->cts_next = NULL; |
| |
| if (!str_table->ctstab_head) |
| str_table->ctstab_head = ctf_string; |
| |
| /* Append to the end of the list. */ |
| if (str_table->ctstab_tail) |
| str_table->ctstab_tail->cts_next = ctf_string; |
| |
| str_table->ctstab_tail = ctf_string; |
| } |
| |
| /* Wrapper function to add str to the CTF string table. No de-duplication of |
| CTF strings is done by the compiler. */ |
| |
| static const char * |
| ctfc_strtable_add_str (ctf_strtable_t * str_table, const char * name, |
| uint32_t * name_offset) |
| { |
| size_t len; |
| char * ctf_string; |
| /* Return value is the offset to the string in the string table. */ |
| uint32_t str_offset = str_table->ctstab_len; |
| |
| /* Add empty string only once at the beginning of the string table. Also, do |
| not add null strings, return the offset to the empty string for them. */ |
| if ((!name || (name != NULL && !strcmp (name, ""))) && str_offset) |
| { |
| ctf_string = CONST_CAST (char *, str_table->ctstab_estr); |
| str_offset = 0; |
| } |
| else |
| { |
| gcc_assert (name); |
| /* Add null-terminated strings to the string table. */ |
| len = strlen (name) + 1; |
| ctf_string = CONST_CAST (char *, ggc_strdup (name)); |
| |
| ctfc_strtable_append_str (str_table, ctf_string); |
| /* Add string to the string table. Keep number of strings updated. */ |
| str_table->ctstab_num++; |
| /* Keep the number of bytes contained in the string table updated. */ |
| str_table->ctstab_len += len; |
| } |
| |
| *name_offset = str_offset; |
| |
| return (const char *) ctf_string; |
| |
| } |
| |
| /* Add string to the appropriate string table in the CTF container. */ |
| |
| const char * |
| ctf_add_string (ctf_container_ref ctfc, const char * name, |
| uint32_t * name_offset, int aux_str = CTF_STRTAB) |
| { |
| /* Get the CTF string table or the CTF auxilliary string table, |
| as applicable. */ |
| ctf_strtable_t *str_table = ctfc_get_strtab (ctfc, aux_str); |
| return ctfc_strtable_add_str (str_table, name, name_offset); |
| } |
| |
| /* Add the compilation unit (CU) name string to the the CTF string table. The |
| CU name has a prepended pwd string if it is a relative path. Also set the |
| CU name offset in the CTF container. */ |
| |
| void |
| ctf_add_cuname (ctf_container_ref ctfc, const char * filename) |
| { |
| char * cuname = NULL; |
| |
| /* (filename at this point of compilation cannot be null). */ |
| |
| if (!IS_DIR_SEPARATOR (filename[0])) |
| { |
| /* Filename is a relative path. */ |
| const char * cu_pwd = get_src_pwd (); |
| const int cu_pwd_len = strlen (cu_pwd); |
| |
| /* Add a DIR_SEPARATOR char before the filename. */ |
| const int len = cu_pwd_len + 2 + strlen (filename); |
| |
| cuname = (char *) ggc_alloc_atomic (len); |
| memset (cuname, 0, len); |
| |
| strcpy (cuname, cu_pwd); |
| cuname[cu_pwd_len] = DIR_SEPARATOR; |
| cuname[cu_pwd_len+1] = 0; |
| strcat (cuname, filename); |
| } |
| else |
| /* Filename is an absolute path. */ |
| cuname = CONST_CAST (char *, ggc_strdup (filename)); |
| |
| ctf_add_string (ctfc, cuname, &(ctfc->ctfc_cuname_offset)); |
| /* Add 1 as CTF strings in the CTF string table are null-terminated |
| strings. */ |
| ctfc->ctfc_strlen += strlen (cuname) + 1; |
| |
| /* Mark cuname for garbage collection. */ |
| cuname = NULL; |
| } |
| |
| /* Functions to create CTF types. |
| |
| These functions perform the task of adding CTF types to the CTF container. |
| No de-duplication is done by them; the onus is on the calling function to do |
| so. The caller must first do a lookup via ctf_dtd_lookup or |
| ctf_dvd_lookup, as applicable, to ascertain that the CTF type or the CTF |
| variable respectively does not already exist, and then add it. */ |
| |
| static ctf_id_t |
| ctf_add_generic (ctf_container_ref ctfc, uint32_t flag, const char * name, |
| ctf_dtdef_ref * rp, dw_die_ref die) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type; |
| |
| gcc_assert (flag == CTF_ADD_NONROOT || flag == CTF_ADD_ROOT); |
| |
| dtd = ggc_cleared_alloc<ctf_dtdef_t> (); |
| |
| type = ctfc->ctfc_nextid++; |
| gcc_assert (type < CTF_MAX_TYPE); /* CTF type ID overflow. */ |
| |
| /* Buffer the strings in the CTF string table. */ |
| dtd->dtd_name = ctf_add_string (ctfc, name, &(dtd->dtd_data.ctti_name)); |
| dtd->dtd_type = type; |
| dtd->dtd_key = die; |
| |
| if ((name != NULL) && strcmp (name, "")) |
| ctfc->ctfc_strlen += strlen (name) + 1; |
| |
| ctf_dtd_insert (ctfc, dtd); |
| |
| *rp = dtd; |
| return type; |
| } |
| |
| static ctf_id_t |
| ctf_add_encoded (ctf_container_ref ctfc, uint32_t flag, const char * name, |
| const ctf_encoding_t * ep, uint32_t kind, dw_die_ref die) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type; |
| |
| type = ctf_add_generic (ctfc, flag, name, &dtd, die); |
| |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, flag, 0); |
| |
| uint32_t roundup_nbytes = (ROUND_UP (ep->cte_bits, BITS_PER_UNIT) |
| / BITS_PER_UNIT); |
| |
| /* FIXME, stay close to what libctf does. But by getting next power of two, |
| aren't we conveying less precise information. E.g. floating point mode |
| XF has a size of 12 bytes. */ |
| dtd->dtd_data.ctti_size = roundup_nbytes ? (1 << ceil_log2 (roundup_nbytes)) |
| : roundup_nbytes; |
| dtd->dtd_u.dtu_enc = *ep; |
| |
| ctfc->ctfc_num_stypes++; |
| |
| return type; |
| } |
| |
| ctf_id_t |
| ctf_add_reftype (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref, |
| uint32_t kind, dw_die_ref die) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type; |
| |
| gcc_assert (ref <= CTF_MAX_TYPE); |
| |
| type = ctf_add_generic (ctfc, flag, NULL, &dtd, die); |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, flag, 0); |
| /* Caller of this API must guarantee that a CTF type with id = ref already |
| exists. This will also be validated for us at link-time. */ |
| dtd->dtd_data.ctti_type = (uint32_t) ref; |
| |
| ctfc->ctfc_num_stypes++; |
| |
| return type; |
| } |
| |
| ctf_id_t |
| ctf_add_forward (ctf_container_ref ctfc, uint32_t flag, const char * name, |
| uint32_t kind, dw_die_ref die) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type = 0; |
| |
| type = ctf_add_generic (ctfc, flag, name, &dtd, die); |
| |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FORWARD, flag, 0); |
| dtd->dtd_data.ctti_type = kind; |
| |
| ctfc->ctfc_num_stypes++; |
| |
| return type; |
| } |
| |
| ctf_id_t |
| ctf_add_typedef (ctf_container_ref ctfc, uint32_t flag, const char * name, |
| ctf_id_t ref, dw_die_ref die) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type; |
| |
| gcc_assert (ref <= CTF_MAX_TYPE); |
| /* Nameless Typedefs are not expected. */ |
| gcc_assert ((name != NULL) && strcmp (name, "")); |
| |
| type = ctf_add_generic (ctfc, flag, name, &dtd, die); |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_TYPEDEF, flag, 0); |
| /* Caller of this API must guarantee that a CTF type with id = ref already |
| exists. This will also be validated for us at link-time. */ |
| dtd->dtd_data.ctti_type = (uint32_t) ref; |
| |
| gcc_assert (dtd->dtd_type != dtd->dtd_data.ctti_type); |
| |
| ctfc->ctfc_num_stypes++; |
| |
| return type; |
| } |
| |
| ctf_id_t |
| ctf_add_slice (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref, |
| uint32_t bit_offset, uint32_t bit_size, dw_die_ref die) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type; |
| uint32_t roundup_nbytes; |
| |
| gcc_assert ((bit_size <= 255) && (bit_offset <= 255)); |
| |
| gcc_assert (ref <= CTF_MAX_TYPE); |
| |
| type = ctf_add_generic (ctfc, flag, NULL, &dtd, die); |
| |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_SLICE, flag, 0); |
| |
| roundup_nbytes = (ROUND_UP (bit_size, BITS_PER_UNIT) / BITS_PER_UNIT); |
| /* FIXME, stay close to what libctf does. But by getting next power of two, |
| aren't we conveying less precise information, especially for bitfields. |
| For example, cte_bits = 33, roundup_nbytes = 5, ctti_size = 8 in the |
| implementation below. */ |
| dtd->dtd_data.ctti_size = roundup_nbytes ? (1 << ceil_log2 (roundup_nbytes)) |
| : 0; |
| |
| /* Caller of this API must guarantee that a CTF type with id = ref already |
| exists. This will also be validated for us at link-time. */ |
| dtd->dtd_u.dtu_slice.cts_type = (uint32_t) ref; |
| dtd->dtd_u.dtu_slice.cts_bits = bit_size; |
| dtd->dtd_u.dtu_slice.cts_offset = bit_offset; |
| |
| ctfc->ctfc_num_stypes++; |
| |
| return type; |
| } |
| |
| ctf_id_t |
| ctf_add_float (ctf_container_ref ctfc, uint32_t flag, |
| const char * name, const ctf_encoding_t * ep, dw_die_ref die) |
| { |
| return (ctf_add_encoded (ctfc, flag, name, ep, CTF_K_FLOAT, die)); |
| } |
| |
| ctf_id_t |
| ctf_add_integer (ctf_container_ref ctfc, uint32_t flag, |
| const char * name, const ctf_encoding_t * ep, dw_die_ref die) |
| { |
| return (ctf_add_encoded (ctfc, flag, name, ep, CTF_K_INTEGER, die)); |
| } |
| |
| ctf_id_t |
| ctf_add_unknown (ctf_container_ref ctfc, uint32_t flag, |
| const char * name, const ctf_encoding_t * ep, dw_die_ref die) |
| { |
| return (ctf_add_encoded (ctfc, flag, name, ep, CTF_K_UNKNOWN, die)); |
| } |
| |
| ctf_id_t |
| ctf_add_pointer (ctf_container_ref ctfc, uint32_t flag, ctf_id_t ref, |
| dw_die_ref die) |
| { |
| return (ctf_add_reftype (ctfc, flag, ref, CTF_K_POINTER, die)); |
| } |
| |
| ctf_id_t |
| ctf_add_array (ctf_container_ref ctfc, uint32_t flag, const ctf_arinfo_t * arp, |
| dw_die_ref die) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type; |
| |
| gcc_assert (arp); |
| |
| /* Caller of this API must make sure CTF type for arp->ctr_contents and |
| arp->ctr_index are already added. This will also be validated for us at |
| link-time. */ |
| |
| type = ctf_add_generic (ctfc, flag, NULL, &dtd, die); |
| |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_ARRAY, flag, 0); |
| dtd->dtd_data.ctti_size = 0; |
| dtd->dtd_u.dtu_arr = *arp; |
| |
| ctfc->ctfc_num_stypes++; |
| |
| return type; |
| } |
| |
| ctf_id_t |
| ctf_add_enum (ctf_container_ref ctfc, uint32_t flag, const char * name, |
| HOST_WIDE_INT size, dw_die_ref die) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type; |
| |
| /* In the compiler, no need to handle the case of promoting forwards to |
| enums. This comment is simply to note a divergence from libctf. */ |
| |
| /* The compiler does, however, update any previously existing forward types |
| to non-root. CTF does not allow existence of two root types with the same |
| name. */ |
| ctf_dtdef_ref enum_fwd_type = ctf_dtd_lookup (ctfc, die); |
| if (enum_fwd_type) |
| { |
| enum_fwd_type->dtd_data.ctti_info |
| = CTF_TYPE_INFO (CTF_K_FORWARD, CTF_ADD_NONROOT, 0); |
| } |
| |
| type = ctf_add_generic (ctfc, flag, name, &dtd, die); |
| |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_ENUM, flag, 0); |
| |
| /* Size in bytes should always fit, of course. |
| TBD WARN - warn instead? */ |
| gcc_assert (size <= CTF_MAX_SIZE); |
| |
| dtd->dtd_data.ctti_size = size; |
| |
| ctfc->ctfc_num_stypes++; |
| |
| return type; |
| } |
| |
| int |
| ctf_add_enumerator (ctf_container_ref ctfc, ctf_id_t enid, const char * name, |
| HOST_WIDE_INT value, dw_die_ref die) |
| { |
| ctf_dmdef_t * dmd; |
| uint32_t kind, vlen, root; |
| |
| /* Callers of this API must make sure that CTF_K_ENUM with enid has been |
| addded. This will also be validated for us at link-time. */ |
| ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die); |
| gcc_assert (dtd); |
| gcc_assert (dtd->dtd_type == enid); |
| gcc_assert (name); |
| |
| kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info); |
| root = CTF_V2_INFO_ISROOT (dtd->dtd_data.ctti_info); |
| vlen = CTF_V2_INFO_VLEN (dtd->dtd_data.ctti_info); |
| |
| gcc_assert (kind == CTF_K_ENUM && vlen < CTF_MAX_VLEN); |
| |
| /* Enum value is of type HOST_WIDE_INT in the compiler, dmd_value is int32_t |
| on the other hand. Check bounds and skip adding this enum value if out of |
| bounds. */ |
| if ((value > INT_MAX) || (value < INT_MIN)) |
| { |
| /* FIXME - Note this TBD_CTF_REPRESENTATION_LIMIT. */ |
| return (1); |
| } |
| |
| dmd = ggc_cleared_alloc<ctf_dmdef_t> (); |
| |
| /* Buffer the strings in the CTF string table. */ |
| dmd->dmd_name = ctf_add_string (ctfc, name, &(dmd->dmd_name_offset)); |
| dmd->dmd_type = CTF_NULL_TYPEID; |
| dmd->dmd_offset = 0; |
| |
| dmd->dmd_value = value; |
| |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, root, vlen + 1); |
| ctf_dmd_list_append (&dtd->dtd_u.dtu_members, dmd); |
| |
| if ((name != NULL) && strcmp (name, "")) |
| ctfc->ctfc_strlen += strlen (name) + 1; |
| |
| return (0); |
| } |
| |
| int |
| ctf_add_member_offset (ctf_container_ref ctfc, dw_die_ref sou, |
| const char * name, ctf_id_t type, |
| uint64_t bit_offset) |
| { |
| ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, sou); |
| ctf_dmdef_t * dmd; |
| |
| uint32_t kind, vlen, root; |
| |
| /* The type of the member being added must already exist. */ |
| gcc_assert (dtd); |
| |
| kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info); |
| root = CTF_V2_INFO_ISROOT (dtd->dtd_data.ctti_info); |
| vlen = CTF_V2_INFO_VLEN (dtd->dtd_data.ctti_info); |
| |
| gcc_assert (kind == CTF_K_STRUCT || kind == CTF_K_UNION); |
| gcc_assert (vlen < CTF_MAX_VLEN); |
| |
| dmd = ggc_cleared_alloc<ctf_dmdef_t> (); |
| |
| /* Buffer the strings in the CTF string table. */ |
| dmd->dmd_name = ctf_add_string (ctfc, name, &(dmd->dmd_name_offset)); |
| dmd->dmd_type = type; |
| dmd->dmd_value = -1; |
| |
| if (kind == CTF_K_STRUCT && vlen != 0) |
| dmd->dmd_offset = bit_offset; |
| else |
| dmd->dmd_offset = 0; |
| |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, root, vlen + 1); |
| ctf_dmd_list_append (&dtd->dtd_u.dtu_members, dmd); |
| |
| if ((name != NULL) && strcmp (name, "")) |
| ctfc->ctfc_strlen += strlen (name) + 1; |
| |
| return 0; |
| } |
| |
| int |
| ctf_add_variable (ctf_container_ref ctfc, const char * name, ctf_id_t ref, |
| dw_die_ref die, unsigned int external_vis, |
| dw_die_ref die_var_decl) |
| { |
| ctf_dvdef_ref dvd, dvd_ignore; |
| |
| gcc_assert (name); |
| |
| if (name != NULL) |
| { |
| dvd = ggc_cleared_alloc<ctf_dvdef_t> (); |
| dvd->dvd_key = die; |
| /* Buffer the strings in the CTF string table. */ |
| dvd->dvd_name = ctf_add_string (ctfc, name, &(dvd->dvd_name_offset)); |
| dvd->dvd_visibility = external_vis; |
| dvd->dvd_type = ref; |
| |
| /* If DW_AT_specification attribute exists, keep track of it as this is |
| the non-defining declaration corresponding to the variable. We will |
| skip emitting CTF variable for such incomplete, non-defining |
| declarations. |
| There could be some non-defining declarations, however, for which a |
| defining declaration does not show up in the same CU. For such |
| cases, the compiler continues to emit CTF variable record as |
| usual. */ |
| if (die_var_decl) |
| { |
| dvd_ignore = ggc_cleared_alloc<ctf_dvdef_t> (); |
| dvd_ignore->dvd_key = die_var_decl; |
| /* It's alright to leave other fields as zero. No valid CTF |
| variable will be added for these DW_TAG_variable DIEs. */ |
| ctf_dvd_ignore_insert (ctfc, dvd_ignore); |
| } |
| |
| ctf_dvd_insert (ctfc, dvd); |
| |
| if (strcmp (name, "")) |
| ctfc->ctfc_strlen += strlen (name) + 1; |
| } |
| |
| return 0; |
| } |
| |
| int |
| ctf_add_function_arg (ctf_container_ref ctfc, dw_die_ref func, |
| const char * name, ctf_id_t type) |
| { |
| ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, func); |
| ctf_func_arg_t * farg; |
| uint32_t vlen; |
| |
| /* The function to which argument is being added must already exist. */ |
| gcc_assert (dtd); |
| /* The number of args must have been non-zero. */ |
| vlen = CTF_V2_INFO_VLEN (dtd->dtd_data.ctti_info); |
| gcc_assert (vlen); |
| |
| farg = ggc_cleared_alloc<ctf_func_arg_t> (); |
| |
| /* Buffer the strings in the auxilliary string table. CTF V3 format does not |
| require function argument names. Use auxilliary string table to keep |
| these strings to avoid unnecessary bloat in CTF section in CTF V3. */ |
| farg->farg_name = ctf_add_string (ctfc, name, &(farg->farg_name_offset), |
| CTF_AUX_STRTAB); |
| farg->farg_type = type; |
| |
| ctf_farg_list_append (&dtd->dtd_u.dtu_argv, farg); |
| |
| /* For aux_str, keep ctfc_aux_strlen updated for debugging. */ |
| if ((name != NULL) && strcmp (name, "")) |
| ctfc->ctfc_aux_strlen += strlen (name) + 1; |
| |
| return 0; |
| } |
| |
| ctf_id_t |
| ctf_add_function (ctf_container_ref ctfc, uint32_t flag, const char * name, |
| const ctf_funcinfo_t * ctc, dw_die_ref die, |
| bool from_global_func) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type; |
| uint32_t vlen; |
| |
| gcc_assert (ctc); |
| |
| vlen = ctc->ctc_argc; |
| gcc_assert (vlen <= CTF_MAX_VLEN); |
| |
| type = ctf_add_generic (ctfc, flag, name, &dtd, die); |
| |
| dtd->from_global_func = from_global_func; |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FUNCTION, flag, vlen); |
| /* Caller must make sure CTF types for ctc->ctc_return are already added. */ |
| dtd->dtd_data.ctti_type = (uint32_t) ctc->ctc_return; |
| /* Caller must make sure CTF types for function arguments are already added |
| via ctf_add_function_arg () API. */ |
| |
| ctfc->ctfc_num_stypes++; |
| |
| return type; |
| } |
| |
| ctf_id_t |
| ctf_add_sou (ctf_container_ref ctfc, uint32_t flag, const char * name, |
| uint32_t kind, size_t size, dw_die_ref die) |
| { |
| ctf_dtdef_ref dtd; |
| ctf_id_t type = 0; |
| |
| gcc_assert ((kind == CTF_K_STRUCT) || (kind == CTF_K_UNION)); |
| |
| /* In the compiler, no need to handle the case of promoting forwards to |
| structs. This comment is simply to note a divergence from libctf. */ |
| |
| /* The compiler does, however, update any previously existing forward types |
| to non-root. CTF does not allow existence of two root types with the same |
| name. */ |
| ctf_dtdef_ref sou_fwd_type = ctf_dtd_lookup (ctfc, die); |
| if (sou_fwd_type) |
| { |
| sou_fwd_type->dtd_data.ctti_info |
| = CTF_TYPE_INFO (CTF_K_FORWARD, CTF_ADD_NONROOT, 0); |
| } |
| |
| type = ctf_add_generic (ctfc, flag, name, &dtd, die); |
| |
| dtd->dtd_data.ctti_info = CTF_TYPE_INFO (kind, flag, 0); |
| |
| if (size > CTF_MAX_SIZE) |
| { |
| dtd->dtd_data.ctti_size = CTF_LSIZE_SENT; |
| dtd->dtd_data.ctti_lsizehi = CTF_SIZE_TO_LSIZE_HI (size); |
| dtd->dtd_data.ctti_lsizelo = CTF_SIZE_TO_LSIZE_LO (size); |
| ctfc->ctfc_num_types++; |
| } |
| else |
| { |
| dtd->dtd_data.ctti_size = (uint32_t) size; |
| ctfc->ctfc_num_stypes++; |
| } |
| |
| return type; |
| } |
| |
| /* Given a TREE_TYPE node, return the CTF type ID for that type. */ |
| |
| ctf_id_t |
| ctf_lookup_tree_type (ctf_container_ref ctfc, const tree type) |
| { |
| dw_die_ref die = lookup_type_die (type); |
| if (die == NULL) |
| return CTF_NULL_TYPEID; |
| |
| ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die); |
| if (dtd == NULL) |
| return CTF_NULL_TYPEID; |
| |
| return dtd->dtd_type; |
| } |
| |
| /* Check if CTF for TYPE has already been generated. Mainstay for |
| de-duplication. If CTF type already exists, returns TRUE and updates |
| the TYPE_ID for the caller. */ |
| |
| bool |
| ctf_type_exists (ctf_container_ref ctfc, dw_die_ref type, |
| ctf_id_t * type_id) |
| { |
| bool exists = false; |
| ctf_dtdef_ref ctf_type_seen = ctf_dtd_lookup (ctfc, type); |
| |
| if (ctf_type_seen) |
| { |
| exists = true; |
| /* CTF type for this type exists. */ |
| *type_id = ctf_type_seen->dtd_type; |
| } |
| |
| return exists; |
| } |
| |
| /* Location information for CTF Types and CTF Variables. CTF section does not |
| emit location information; at this time, location information is needed for |
| BTF CO-RE use-cases. */ |
| |
| int |
| ctfc_get_dtd_srcloc (ctf_dtdef_ref dtd, ctf_srcloc_ref loc) |
| { |
| loc->ctsloc_file = ctf_get_die_loc_file (dtd->dtd_key); |
| loc->ctsloc_line = ctf_get_die_loc_line (dtd->dtd_key); |
| loc->ctsloc_col = ctf_get_die_loc_col (dtd->dtd_key); |
| |
| if (loc->ctsloc_file == NULL) |
| return 1; |
| |
| return 0; |
| } |
| |
| int |
| ctfc_get_dvd_srcloc (ctf_dvdef_ref dvd, ctf_srcloc_ref loc) |
| { |
| loc->ctsloc_file = ctf_get_die_loc_file (dvd->dvd_key); |
| loc->ctsloc_line = ctf_get_die_loc_line (dvd->dvd_key); |
| loc->ctsloc_col = ctf_get_die_loc_col (dvd->dvd_key); |
| |
| if (loc->ctsloc_file == NULL) |
| return 1; |
| |
| return 0; |
| } |
| |
| /* CTF container setup and teardown routines. */ |
| |
| /* Initialize the CTF string table. |
| The first entry in the CTF string table (empty string) is added. */ |
| |
| static void |
| init_ctf_strtable (ctf_strtable_t * strtab) |
| { |
| strtab->ctstab_head = NULL; |
| strtab->ctstab_tail = NULL; |
| strtab->ctstab_num = 0; |
| strtab->ctstab_len = 0; |
| |
| /* The first entry in the CTF string table is an empty string. E.g., CTF |
| type records with no name (like CTF_K_CONST, CTF_K_VOLATILE etc) point to |
| this string. */ |
| uint32_t estr_offset = 0; |
| strtab->ctstab_estr = ctfc_strtable_add_str (strtab, "", &estr_offset); |
| } |
| |
| /* Initialize the string tables in the CTF container. */ |
| |
| static void |
| init_ctf_string_table (ctf_container_ref ctfc) |
| { |
| init_ctf_strtable (&ctfc->ctfc_strtable); |
| ctfc->ctfc_strlen++; |
| |
| init_ctf_strtable (&ctfc->ctfc_aux_strtable); |
| ctfc->ctfc_aux_strlen++; |
| } |
| |
| /* Allocate a new CTF container with the desired flags. */ |
| |
| static inline ctf_container_ref |
| new_ctf_container (void) |
| { |
| tu_ctfc = ggc_cleared_alloc<ctf_container_t> (); |
| tu_ctfc->ctfc_types |
| = hash_table<ctfc_dtd_hasher>::create_ggc (100); |
| tu_ctfc->ctfc_vars |
| = hash_table<ctfc_dvd_hasher>::create_ggc (100); |
| tu_ctfc->ctfc_ignore_vars |
| = hash_table<ctfc_dvd_hasher>::create_ggc (10); |
| |
| return tu_ctfc; |
| } |
| |
| /* Initialize a CTF container per translation unit. */ |
| |
| static void |
| init_ctf_container (void) |
| { |
| tu_ctfc = new_ctf_container (); |
| |
| tu_ctfc->ctfc_magic = CTF_MAGIC; |
| tu_ctfc->ctfc_version = CTF_VERSION; |
| tu_ctfc->ctfc_flags = CTF_F_NEWFUNCINFO; |
| tu_ctfc->ctfc_nextid = CTF_INIT_TYPEID; |
| |
| init_ctf_string_table (tu_ctfc); |
| } |
| |
| void |
| ctfc_delete_strtab (ctf_strtable_t * strtab) |
| { |
| ctf_string_t * str = NULL; |
| ctf_string_t * next_str = NULL; |
| |
| str = strtab->ctstab_head; |
| next_str = str; |
| while (next_str != NULL) |
| { |
| next_str = str->cts_next; |
| ggc_free (str); |
| str = next_str; |
| } |
| |
| strtab->ctstab_head = NULL; |
| strtab->ctstab_tail = NULL; |
| strtab->ctstab_estr = NULL; |
| } |
| |
| /* Delete the CTF container's resources. */ |
| |
| void |
| ctfc_delete_container (ctf_container_ref ctfc) |
| { |
| if (ctfc) |
| { |
| ctfc->ctfc_types->empty (); |
| ctfc->ctfc_types = NULL; |
| |
| ctfc->ctfc_vars->empty (); |
| ctfc->ctfc_types = NULL; |
| |
| ctfc->ctfc_ignore_vars->empty (); |
| ctfc->ctfc_ignore_vars = NULL; |
| |
| ctfc_delete_strtab (&ctfc->ctfc_strtable); |
| ctfc_delete_strtab (&ctfc->ctfc_aux_strtable); |
| if (ctfc->ctfc_vars_list) |
| { |
| ggc_free (ctfc->ctfc_vars_list); |
| ctfc->ctfc_vars_list = NULL; |
| } |
| if (ctfc->ctfc_types_list) |
| { |
| ggc_free (ctfc->ctfc_types_list); |
| ctfc->ctfc_types_list = NULL; |
| } |
| if (ctfc->ctfc_gfuncs_list) |
| { |
| ggc_free (ctfc->ctfc_gfuncs_list); |
| ctfc->ctfc_gfuncs_list = NULL; |
| } |
| if (ctfc->ctfc_gobjts_list) |
| { |
| ggc_free (ctfc->ctfc_gobjts_list); |
| ctfc->ctfc_gobjts_list = NULL; |
| } |
| |
| ctfc= NULL; |
| } |
| } |
| |
| /* CTF routines interfacing to the compiler. */ |
| |
| void |
| ctf_init (void) |
| { |
| init_ctf_container (); |
| } |