diff options
Diffstat (limited to 'gcc/btfout.c')
-rw-r--r-- | gcc/btfout.c | 1133 |
1 files changed, 1133 insertions, 0 deletions
diff --git a/gcc/btfout.c b/gcc/btfout.c new file mode 100644 index 0000000..cdc6c63 --- /dev/null +++ b/gcc/btfout.c @@ -0,0 +1,1133 @@ +/* Output BTF format from GCC. + Copyright (C) 2021 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/>. */ + +/* This file contains routines to output the BPF Type Format (BTF). The BTF + debug format is very similar to CTF; as a result, the structure of this file + closely resembles that of ctfout.c, and the same CTF container objects are + used. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "target.h" +#include "memmodel.h" +#include "tm_p.h" +#include "output.h" +#include "dwarf2asm.h" +#include "debug.h" +#include "ctfc.h" +#include "diagnostic-core.h" +#include "cgraph.h" +#include "varasm.h" +#include "dwarf2out.h" /* For lookup_decl_die. */ + +static int btf_label_num; + +static GTY (()) section * btf_info_section; + +/* BTF debug info section. */ + +#ifndef BTF_INFO_SECTION_NAME +#define BTF_INFO_SECTION_NAME ".BTF" +#endif + +#define BTF_INFO_SECTION_FLAGS (SECTION_DEBUG) + +/* Maximum size (in bytes) for an artifically generated BTF label. */ + +#define MAX_BTF_LABEL_BYTES 40 + +static char btf_info_section_label[MAX_BTF_LABEL_BYTES]; + +#ifndef BTF_INFO_SECTION_LABEL +#define BTF_INFO_SECTION_LABEL "Lbtf" +#endif + +/* BTF encodes void as type id 0. */ + +#define BTF_VOID_TYPEID 0 +#define BTF_INIT_TYPEID 1 + +#define BTF_INVALID_TYPEID 0xFFFFFFFF + +/* Mapping of CTF variables to the IDs they will be assigned when they are + converted to BTF_KIND_VAR type records. Strictly accounts for the index + from the start of the variable type entries, does not include the number + of types emitted prior to the variable records. */ +static hash_map <ctf_dvdef_ref, unsigned int> *btf_var_ids; + +/* Mapping of type IDs from original CTF ID to BTF ID. Types do not map + 1-to-1 from CTF to BTF. To avoid polluting the CTF container when updating + type references-by-ID, we use this map instead. */ +static ctf_id_t * btf_id_map = NULL; + +/* Information for creating the BTF_KIND_DATASEC records. */ +typedef struct btf_datasec +{ + const char *name; /* Section name, e.g. ".bss". */ + uint32_t name_offset; /* Offset to name in string table. */ + vec<struct btf_var_secinfo> entries; /* Variable entries in this section. */ +} btf_datasec_t; + +/* One BTF_KIND_DATASEC record is created for each output data section which + will hold at least one variable. */ +static vec<btf_datasec_t> datasecs; + +/* Holes occur for types which are present in the CTF container, but are either + non-representable or redundant in BTF. */ +static vec<ctf_id_t> holes; + +/* CTF definition(s) of void. Only one definition of void should be generated. + We should not encounter more than one definition of void, but use a vector + to be safe. */ +static vec<ctf_id_t> voids; + +/* Functions in BTF have two separate type records - one for the prototype + (BTF_KIND_FUNC_PROTO), as well as a BTF_KIND_FUNC. CTF_K_FUNCTION types + map closely to BTF_KIND_FUNC_PROTO, but the BTF_KIND_FUNC records must be + created. This vector holds them. */ +static GTY (()) vec<ctf_dtdef_ref, va_gc> *funcs; + +/* The number of BTF variables added to the TU CTF container. */ +static unsigned int num_vars_added = 0; + +/* The number of BTF types added to the TU CTF container. */ +static unsigned int num_types_added = 0; + +/* The number of types synthesized for BTF that do not correspond to + CTF types. */ +static unsigned int num_types_created = 0; + +/* Map a CTF type kind to the corresponding BTF type kind. */ + +static uint32_t +get_btf_kind (uint32_t ctf_kind) +{ + /* N.B. the values encoding kinds are not in general the same for the + same kind between CTF and BTF. e.g. CTF_K_CONST != BTF_KIND_CONST. */ + switch (ctf_kind) + { + case CTF_K_INTEGER: return BTF_KIND_INT; + case CTF_K_FLOAT: return BTF_KIND_FLOAT; + case CTF_K_POINTER: return BTF_KIND_PTR; + case CTF_K_ARRAY: return BTF_KIND_ARRAY; + case CTF_K_FUNCTION: return BTF_KIND_FUNC_PROTO; + case CTF_K_STRUCT: return BTF_KIND_STRUCT; + case CTF_K_UNION: return BTF_KIND_UNION; + case CTF_K_ENUM: return BTF_KIND_ENUM; + case CTF_K_FORWARD: return BTF_KIND_FWD; + case CTF_K_TYPEDEF: return BTF_KIND_TYPEDEF; + case CTF_K_VOLATILE: return BTF_KIND_VOLATILE; + case CTF_K_CONST: return BTF_KIND_CONST; + case CTF_K_RESTRICT: return BTF_KIND_RESTRICT; + default:; + } + return BTF_KIND_UNKN; +} + +/* Allocate the btf_id_map, and initialize elements to BTF_INVALID_TYPEID. */ + +static void +init_btf_id_map (size_t len) +{ + btf_id_map = XNEWVEC (ctf_id_t, len); + + btf_id_map[0] = BTF_VOID_TYPEID; + for (size_t i = 1; i < len; i++) + btf_id_map[i] = BTF_INVALID_TYPEID; +} + +/* Return the BTF type ID of CTF type ID KEY, or BTF_INVALID_TYPEID if the CTF + type with ID KEY does not map to a BTF type. */ + +ctf_id_t +get_btf_id (ctf_id_t key) +{ + return btf_id_map[key]; +} + +/* Set the CTF type ID KEY to map to BTF type ID VAL. */ + +static inline void +set_btf_id (ctf_id_t key, ctf_id_t val) +{ + btf_id_map[key] = val; +} + +/* Return TRUE iff the given CTF type ID maps to a BTF type which will + be emitted. */ +static inline bool +btf_emit_id_p (ctf_id_t id) +{ + return ((btf_id_map[id] != BTF_VOID_TYPEID) + && (btf_id_map[id] <= BTF_MAX_TYPE)); +} + +/* Each BTF type can be followed additional, variable-length information + completing the description of the type. Calculate the number of bytes + of variable information required to encode a given type. */ + +static uint64_t +btf_calc_num_vbytes (ctf_dtdef_ref dtd) +{ + uint64_t vlen_bytes = 0; + + uint32_t kind = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info)); + uint32_t vlen = CTF_V2_INFO_VLEN (dtd->dtd_data.ctti_info); + + switch (kind) + { + case BTF_KIND_UNKN: + case BTF_KIND_PTR: + case BTF_KIND_FWD: + case BTF_KIND_TYPEDEF: + case BTF_KIND_VOLATILE: + case BTF_KIND_CONST: + case BTF_KIND_RESTRICT: + case BTF_KIND_FUNC: + /* These kinds have no vlen data. */ + break; + + case BTF_KIND_INT: + /* Size 0 integers represent redundant definitions of void that will + not be emitted. Don't allocate space for them. */ + if (dtd->dtd_data.ctti_size == 0) + break; + + vlen_bytes += sizeof (uint32_t); + break; + + case BTF_KIND_ARRAY: + vlen_bytes += sizeof (struct btf_array); + break; + + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + vlen_bytes += vlen * sizeof (struct btf_member); + break; + + case BTF_KIND_ENUM: + vlen_bytes += vlen * sizeof (struct btf_enum); + break; + + case BTF_KIND_FUNC_PROTO: + vlen_bytes += vlen * sizeof (struct btf_param); + break; + + case BTF_KIND_VAR: + vlen_bytes += sizeof (struct btf_var); + break; + + case BTF_KIND_DATASEC: + vlen_bytes += vlen * sizeof (struct btf_var_secinfo); + break; + + default: + break; + } + return vlen_bytes; +} + +/* Initialize BTF section (.BTF) for output. */ + +void +init_btf_sections (void) +{ + btf_info_section = get_section (BTF_INFO_SECTION_NAME, BTF_INFO_SECTION_FLAGS, + NULL); + + ASM_GENERATE_INTERNAL_LABEL (btf_info_section_label, + BTF_INFO_SECTION_LABEL, btf_label_num++); +} + +/* Push a BTF datasec variable entry INFO into the datasec named SECNAME, + creating the datasec if it does not already exist. */ + +static void +btf_datasec_push_entry (ctf_container_ref ctfc, const char *secname, + struct btf_var_secinfo info) +{ + if (secname == NULL) + return; + + for (size_t i = 0; i < datasecs.length (); i++) + if (strcmp (datasecs[i].name, secname) == 0) + { + datasecs[i].entries.safe_push (info); + return; + } + + /* If we don't already have a datasec record for secname, make one. */ + + uint32_t str_off; + ctf_add_string (ctfc, secname, &str_off, CTF_AUX_STRTAB); + if (strcmp (secname, "")) + ctfc->ctfc_aux_strlen += strlen (secname) + 1; + + btf_datasec_t ds; + ds.name = secname; + ds.name_offset = str_off; + + ds.entries.create (0); + ds.entries.safe_push (info); + + datasecs.safe_push (ds); + num_types_created++; +} + +/* Construct all BTF_KIND_DATASEC records for CTFC. One such record is created + for each non-empty data-containing section in the output. Each record is + followed by a variable number of entries describing the variables stored + in that section. */ + +static void +btf_collect_datasec (ctf_container_ref ctfc) +{ + /* See cgraph.h struct symtab_node, which varpool_node extends. */ + varpool_node *node; + FOR_EACH_VARIABLE (node) + { + dw_die_ref die = lookup_decl_die (node->decl); + if (die == NULL) + continue; + + ctf_dvdef_ref dvd = ctf_dvd_lookup (ctfc, die); + if (dvd == NULL) + continue; + + const char *section_name = node->get_section (); + + if (section_name == NULL) + { + switch (categorize_decl_for_section (node->decl, 0)) + { + case SECCAT_BSS: + section_name = ".bss"; + break; + case SECCAT_DATA: + section_name = ".data"; + break; + case SECCAT_RODATA: + section_name = ".rodata"; + break; + default: + continue; + } + } + + struct btf_var_secinfo info; + + info.type = 0; + unsigned int *var_id = btf_var_ids->get (dvd); + if (var_id) + /* +1 for the sentinel type not in the types map. */ + info.type = *var_id + num_types_added + 1; + else + continue; + + info.size = 0; + tree size = DECL_SIZE_UNIT (node->decl); + if (tree_fits_uhwi_p (size)) + info.size = tree_to_uhwi (size); + + /* Offset is left as 0 at compile time, to be filled in by loaders such + as libbpf. */ + info.offset = 0; + + btf_datasec_push_entry (ctfc, section_name, info); + } +} + +/* Return true if the type ID is that of a type which will not be emitted (for + example, if it is not representable in BTF). */ + +static bool +btf_removed_type_p (ctf_id_t id) +{ + return holes.contains (id); +} + +/* Adjust the given type ID to account for holes and duplicate definitions of + void. */ + +static ctf_id_t +btf_adjust_type_id (ctf_id_t id) +{ + size_t n; + ctf_id_t i = 0; + + /* Do not adjust invalid type markers. */ + if (id == BTF_INVALID_TYPEID) + return id; + + for (n = 0; n < voids.length (); n++) + if (id == voids[n]) + return BTF_VOID_TYPEID; + + for (n = 0; n < holes.length (); n++) + { + if (holes[n] < id) + i++; + else if (holes[n] == id) + return BTF_VOID_TYPEID; + } + + return id - i; +} + +/* Postprocessing callback routine for types. */ + +int +btf_dtd_postprocess_cb (ctf_dtdef_ref *slot, ctf_container_ref arg_ctfc) +{ + ctf_dtdef_ref ctftype = (ctf_dtdef_ref) * slot; + + size_t index = ctftype->dtd_type; + gcc_assert (index <= arg_ctfc->ctfc_types->elements ()); + + uint32_t ctf_kind, btf_kind; + + ctf_kind = CTF_V2_INFO_KIND (ctftype->dtd_data.ctti_info); + btf_kind = get_btf_kind (ctf_kind); + + if (btf_kind == BTF_KIND_UNKN) + /* This type is not representable in BTF. Create a hole. */ + holes.safe_push (ctftype->dtd_type); + + else if (btf_kind == BTF_KIND_INT && ctftype->dtd_data.ctti_size == 0) + { + /* This is a (redundant) definition of void. */ + voids.safe_push (ctftype->dtd_type); + holes.safe_push (ctftype->dtd_type); + } + + arg_ctfc->ctfc_types_list[index] = ctftype; + + return 1; +} + +/* Preprocessing callback routine for variables. */ + +int +btf_dvd_emit_preprocess_cb (ctf_dvdef_ref *slot, ctf_container_ref arg_ctfc) +{ + ctf_dvdef_ref var = (ctf_dvdef_ref) * slot; + + /* Do not add variables which refer to unsupported types. */ + if (btf_removed_type_p (var->dvd_type)) + return 1; + + arg_ctfc->ctfc_vars_list[num_vars_added] = var; + btf_var_ids->put (var, num_vars_added); + + num_vars_added++; + num_types_created++; + + return 1; +} + +/* Preprocessing callback routine for types. */ + +static void +btf_dtd_emit_preprocess_cb (ctf_container_ref ctfc, ctf_dtdef_ref dtd) +{ + if (!btf_emit_id_p (dtd->dtd_type)) + return; + + uint32_t btf_kind + = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info)); + + if (btf_kind == BTF_KIND_FUNC_PROTO) + { + /* Functions actually get two types: a BTF_KIND_FUNC_PROTO, and + also a BTF_KIND_FUNC. But the CTF container only allocates one + type per function, which matches closely with BTF_KIND_FUNC_PROTO. + For each such function, also allocate a BTF_KIND_FUNC entry. + These will be output later. */ + ctf_dtdef_ref func_dtd = ggc_cleared_alloc<ctf_dtdef_t> (); + func_dtd->dtd_data = dtd->dtd_data; + func_dtd->dtd_data.ctti_type = dtd->dtd_type; + + vec_safe_push (funcs, func_dtd); + num_types_created++; + + /* Only the BTF_KIND_FUNC type actually references the name. The + BTF_KIND_FUNC_PROTO is always anonymous. */ + dtd->dtd_data.ctti_name = 0; + } + + ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd); +} + +/* Preprocess the CTF information to prepare for BTF output. BTF is almost a + subset of CTF, with many small differences in encoding, and lacking support + for some types (notably floating point formats). + + During the preprocessing pass: + - Ascertain that the sorted list of types has been prepared. For the BTF + generation process, this is taken care of by the btf_init_postprocess (). + + - BTF_KIND_FUNC and BTF_KIND_DATASEC records are constructed. These types do + not have analogues in CTF (the analogous type to CTF_K_FUNCTION is + BTF_KIND_FUNC_PROTO), but can be relatively easily deduced from CTF + information. + + - Construct BTF_KIND_VAR records, representing variables. + + - Calculate the total size in bytes of variable-length information following + BTF type records. This is used for outputting the BTF header. + + After preprocessing, all BTF information is ready to be output: + - ctfc->ctfc_types_list holdstypes converted from CTF types. This does not + include KIND_VAR, KIND_FUNC, nor KIND_DATASEC types. These types have been + re-encoded to the appropriate representation in BTF. + - ctfc->ctfc_vars_list holds all variables which should be output. + Variables of unsupported types are not present in this list. + - Vector 'funcs' holds all BTF_KIND_FUNC types, one to match each + BTF_KIND_FUNC_PROTO. + - Vector 'datasecs' holds all BTF_KIND_DATASEC types. */ + +static void +btf_emit_preprocess (ctf_container_ref ctfc) +{ + size_t num_ctf_types = ctfc->ctfc_types->elements (); + size_t num_ctf_vars = ctfc->ctfc_vars->elements (); + size_t i; + + if (num_ctf_types) + { + gcc_assert (ctfc->ctfc_types_list); + /* Preprocess the types. */ + for (i = 1; i <= num_ctf_types; i++) + btf_dtd_emit_preprocess_cb (ctfc, ctfc->ctfc_types_list[i]); + } + + btf_var_ids = hash_map<ctf_dvdef_ref, unsigned int>::create_ggc (100); + + if (num_ctf_vars) + { + /* Allocate and construct the list of variables. While BTF variables are + not distinct from types (in that variables are simply types with + BTF_KIND_VAR), it is simpler to maintain a separate list of variables + and append them to the types list during output. */ + ctfc->ctfc_vars_list = ggc_vec_alloc<ctf_dvdef_ref>(num_ctf_vars); + ctfc->ctfc_vars->traverse<ctf_container_ref, btf_dvd_emit_preprocess_cb> + (ctfc); + + ctfc->ctfc_num_vlen_bytes += (num_vars_added * sizeof (struct btf_var)); + } + + btf_collect_datasec (ctfc); +} + +/* Return true iff DMD is a member description of a bit-field which can be + validly represented in BTF. */ + +static bool +btf_dmd_representable_bitfield_p (ctf_container_ref ctfc, ctf_dmdef_t *dmd) +{ + ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type]; + + if (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info) == CTF_K_SLICE) + { + unsigned short word_offset = ref_type->dtd_u.dtu_slice.cts_offset; + unsigned short bits = ref_type->dtd_u.dtu_slice.cts_bits; + uint64_t sou_offset = dmd->dmd_offset; + + if ((bits > 0xff) || ((sou_offset + word_offset) > 0xffffff)) + return false; + + return true; + } + + return false; +} + +/* BTF asm helper routines. */ + +/* Asm'out a BTF type. This routine is responsible for the bulk of the task + of converting CTF types to their BTF representation. */ + +static void +btf_asm_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd) +{ + uint32_t btf_kind, btf_kflag, btf_vlen, btf_size_type; + uint32_t ctf_info = dtd->dtd_data.ctti_info; + + btf_kind = get_btf_kind (CTF_V2_INFO_KIND (ctf_info)); + btf_size_type = dtd->dtd_data.ctti_type; + btf_vlen = CTF_V2_INFO_VLEN (ctf_info); + + /* By now any unrepresentable types have been removed. */ + gcc_assert (btf_kind != BTF_KIND_UNKN); + + /* Size 0 integers are redundant definitions of void. None should remain + in the types list by this point. */ + gcc_assert (btf_kind != BTF_KIND_INT || btf_size_type >= 1); + + /* Re-encode the ctti_info to BTF. */ + /* kflag is 1 for structs/unions with a bitfield member. + kflag is 1 for forwards to unions. + kflag is 0 in all other cases. */ + btf_kflag = 0; + + if (btf_kind == BTF_KIND_STRUCT || btf_kind == BTF_KIND_UNION) + { + /* If a struct/union has ANY bitfield members, set kflag=1. + Note that we must also change the encoding of every member to encode + both member bitfield size (stealing most-significant 8 bits) and bit + offset (LS 24 bits). This is done during preprocessing. */ + ctf_dmdef_t *dmd; + for (dmd = dtd->dtd_u.dtu_members; + dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd)) + { + /* Set kflag if this member is a representable bitfield. */ + if (btf_dmd_representable_bitfield_p (ctfc, dmd)) + btf_kflag = 1; + + /* Struct members that refer to unsupported types or bitfield formats + shall be skipped. These are marked during preprocessing. */ + else if (!btf_emit_id_p (dmd->dmd_type)) + btf_vlen -= 1; + } + } + + /* BTF forwards make use of KIND_FLAG to distinguish between forwards to + structs and forwards to unions. The dwarf2ctf conversion process stores + the kind of the forward in ctti_type, but for BTF this must be 0 for + forwards, with only the KIND_FLAG to distinguish. + At time of writing, BTF forwards to enums are unspecified. */ + if (btf_kind == BTF_KIND_FWD) + { + if (dtd->dtd_data.ctti_type == CTF_K_UNION) + btf_kflag = 1; + + btf_size_type = 0; + } + + dw2_asm_output_data (4, dtd->dtd_data.ctti_name, "btt_name"); + dw2_asm_output_data (4, BTF_TYPE_INFO (btf_kind, btf_kflag, btf_vlen), + "btt_info: kind=%u, kflag=%u, vlen=%u", + btf_kind, btf_kflag, btf_vlen); + switch (btf_kind) + { + case BTF_KIND_INT: + case BTF_KIND_FLOAT: + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + case BTF_KIND_ENUM: + case BTF_KIND_DATASEC: + dw2_asm_output_data (4, dtd->dtd_data.ctti_size, "btt_size: %uB", + dtd->dtd_data.ctti_size); + return; + default: + break; + } + + dw2_asm_output_data (4, get_btf_id (dtd->dtd_data.ctti_type), "btt_type"); +} + +/* Asm'out the variable information following a BTF_KIND_ARRAY. */ + +static void +btf_asm_array (ctf_dtdef_ref dtd) +{ + dw2_asm_output_data (4, get_btf_id (dtd->dtd_u.dtu_arr.ctr_contents), + "bta_contents"); + dw2_asm_output_data (4, get_btf_id (dtd->dtd_u.dtu_arr.ctr_index), + "bta_index"); + dw2_asm_output_data (4, dtd->dtd_u.dtu_arr.ctr_nelems, "bta_nelems"); +} + +/* Asm'out a BTF_KIND_VAR. */ + +static void +btf_asm_varent (ctf_dvdef_ref var) +{ + dw2_asm_output_data (4, var->dvd_name_offset, "btv_name"); + dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_VAR, 0, 0), "btv_info"); + dw2_asm_output_data (4, get_btf_id (var->dvd_type), "btv_type"); + dw2_asm_output_data (4, (var->dvd_visibility ? 1 : 0), "btv_linkage"); +} + +/* Asm'out a member description following a BTF_KIND_STRUCT or + BTF_KIND_UNION. */ + +static void +btf_asm_sou_member (ctf_container_ref ctfc, ctf_dmdef_t * dmd) +{ + ctf_dtdef_ref ref_type = ctfc->ctfc_types_list[dmd->dmd_type]; + + /* Re-encode bitfields to BTF representation. */ + if (CTF_V2_INFO_KIND (ref_type->dtd_data.ctti_info) == CTF_K_SLICE) + { + ctf_id_t base_type = ref_type->dtd_u.dtu_slice.cts_type; + unsigned short word_offset = ref_type->dtd_u.dtu_slice.cts_offset; + unsigned short bits = ref_type->dtd_u.dtu_slice.cts_bits; + uint64_t sou_offset = dmd->dmd_offset; + + /* Pack the bit offset and bitfield size together. */ + sou_offset += word_offset; + + /* If this bitfield cannot be represented, do not output anything. + The parent struct/union 'vlen' field has already been updated. */ + if ((bits > 0xff) || (sou_offset > 0xffffff)) + return; + + sou_offset &= 0x00ffffff; + sou_offset |= ((bits & 0xff) << 24); + + /* Refer to the base type of the slice. */ + dw2_asm_output_data (4, dmd->dmd_name_offset, "btm_name_off"); + dw2_asm_output_data (4, get_btf_id (base_type), "btm_type"); + dw2_asm_output_data (4, sou_offset, "btm_offset"); + } + else + { + dw2_asm_output_data (4, dmd->dmd_name_offset, "btm_name_off"); + dw2_asm_output_data (4, get_btf_id (dmd->dmd_type), "btm_type"); + dw2_asm_output_data (4, dmd->dmd_offset, "btm_offset"); + } +} + +/* Asm'out an enum constant following a BTF_KIND_ENUM. */ + +static void +btf_asm_enum_const (ctf_dmdef_t * dmd) +{ + dw2_asm_output_data (4, dmd->dmd_name_offset, "bte_name"); + dw2_asm_output_data (4, dmd->dmd_value, "bte_value"); +} + +/* Asm'out a function parameter description following a BTF_KIND_FUNC_PROTO. */ + +static void +btf_asm_func_arg (ctf_func_arg_t * farg, size_t stroffset) +{ + /* If the function arg does not have a name, refer to the null string at + the start of the string table. This ensures correct encoding for varargs + '...' arguments. */ + if ((farg->farg_name != NULL) && strcmp (farg->farg_name, "")) + dw2_asm_output_data (4, farg->farg_name_offset + stroffset, "farg_name"); + else + dw2_asm_output_data (4, 0, "farg_name"); + + dw2_asm_output_data (4, (btf_removed_type_p (farg->farg_type) + ? BTF_VOID_TYPEID + : get_btf_id (farg->farg_type)), + "farg_type"); +} + +/* Asm'out a BTF_KIND_FUNC type. */ + +static void +btf_asm_func_type (ctf_dtdef_ref dtd) +{ + dw2_asm_output_data (4, dtd->dtd_data.ctti_name, "btt_name"); + dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_FUNC, 0, 0), "btt_info"); + dw2_asm_output_data (4, get_btf_id (dtd->dtd_data.ctti_type), "btt_type"); +} + +/* Asm'out a variable entry following a BTF_KIND_DATASEC. */ + +static void +btf_asm_datasec_entry (struct btf_var_secinfo info) +{ + dw2_asm_output_data (4, info.type, "bts_type"); + dw2_asm_output_data (4, info.offset, "bts_offset"); + dw2_asm_output_data (4, info.size, "bts_size"); +} + +/* Asm'out a whole BTF_KIND_DATASEC, including its variable entries. */ + +static void +btf_asm_datasec_type (btf_datasec_t ds, size_t stroffset) +{ + dw2_asm_output_data (4, ds.name_offset + stroffset, "btt_name"); + dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_DATASEC, 0, + ds.entries.length ()), + "btt_info"); + /* Note: the "total section size in bytes" is emitted as 0 and patched by + loaders such as libbpf. */ + dw2_asm_output_data (4, 0, "btt_size"); + for (size_t i = 0; i < ds.entries.length (); i++) + btf_asm_datasec_entry (ds.entries[i]); +} + +/* Compute and output the header information for a .BTF section. */ + +static void +output_btf_header (ctf_container_ref ctfc) +{ + switch_to_section (btf_info_section); + ASM_OUTPUT_LABEL (asm_out_file, btf_info_section_label); + + /* BTF magic number, version, flags, and header length. */ + dw2_asm_output_data (2, BTF_MAGIC, "btf_magic"); + dw2_asm_output_data (1, BTF_VERSION, "btf_version"); + dw2_asm_output_data (1, 0, "btf_flags"); + dw2_asm_output_data (4, sizeof (struct btf_header), "btf_hdr_len"); + + uint32_t type_off = 0, type_len = 0; + uint32_t str_off = 0, str_len = 0; + uint32_t datasec_vlen_bytes = 0; + + if (!ctfc_is_empty_container (ctfc)) + { + for (size_t i = 0; i < datasecs.length (); i++) + { + datasec_vlen_bytes += ((datasecs[i].entries.length ()) + * sizeof (struct btf_var_secinfo)); + } + + /* Total length (bytes) of the types section. */ + type_len = (num_types_added * sizeof (struct btf_type)) + + (num_types_created * sizeof (struct btf_type)) + + datasec_vlen_bytes + + ctfc->ctfc_num_vlen_bytes; + + str_off = type_off + type_len; + + str_len = ctfc->ctfc_strtable.ctstab_len + + ctfc->ctfc_aux_strtable.ctstab_len; + } + + /* Offset of type section. */ + dw2_asm_output_data (4, type_off, "type_off"); + /* Length of type section in bytes. */ + dw2_asm_output_data (4, type_len, "type_len"); + /* Offset of string section. */ + dw2_asm_output_data (4, str_off, "str_off"); + /* Length of string section in bytes. */ + dw2_asm_output_data (4, str_len, "str_len"); +} + +/* Output all BTF_KIND_VARs in CTFC. */ + +static void +output_btf_vars (ctf_container_ref ctfc) +{ + size_t i; + size_t num_ctf_vars = num_vars_added; + if (num_ctf_vars) + { + for (i = 0; i < num_ctf_vars; i++) + btf_asm_varent (ctfc->ctfc_vars_list[i]); + } +} + +/* Output BTF string records. The BTF strings section is a concatenation + of the standard and auxilliary string tables in the ctf container. */ + +static void +output_btf_strs (ctf_container_ref ctfc) +{ + ctf_string_t * ctf_string = ctfc->ctfc_strtable.ctstab_head; + + while (ctf_string) + { + dw2_asm_output_nstring (ctf_string->cts_str, -1, "btf_string"); + ctf_string = ctf_string->cts_next; + } + + ctf_string = ctfc->ctfc_aux_strtable.ctstab_head; + while (ctf_string) + { + dw2_asm_output_nstring (ctf_string->cts_str, -1, "btf_aux_string"); + ctf_string = ctf_string->cts_next; + } +} + +/* Output all (representable) members of a BTF_KIND_STRUCT or + BTF_KIND_UNION type. */ + +static void +output_asm_btf_sou_fields (ctf_container_ref ctfc, ctf_dtdef_ref dtd) +{ + ctf_dmdef_t * dmd; + + for (dmd = dtd->dtd_u.dtu_members; + dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd)) + btf_asm_sou_member (ctfc, dmd); +} + +/* Output all enumerator constants following a BTF_KIND_ENUM. */ + +static void +output_asm_btf_enum_list (ctf_container_ref ARG_UNUSED (ctfc), + ctf_dtdef_ref dtd) +{ + ctf_dmdef_t * dmd; + + for (dmd = dtd->dtd_u.dtu_members; + dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd)) + btf_asm_enum_const (dmd); +} + +/* Output all function arguments following a BTF_KIND_FUNC_PROTO. */ + +static void +output_asm_btf_func_args_list (ctf_container_ref ctfc, + ctf_dtdef_ref dtd) +{ + size_t farg_name_offset = ctfc_get_strtab_len (ctfc, CTF_STRTAB); + ctf_func_arg_t * farg; + for (farg = dtd->dtd_u.dtu_argv; + farg != NULL; farg = (ctf_func_arg_t *) ctf_farg_list_next (farg)) + btf_asm_func_arg (farg, farg_name_offset); +} + +/* Output the variable portion of a BTF type record. The information depends + on the kind of the type. */ + +static void +output_asm_btf_vlen_bytes (ctf_container_ref ctfc, ctf_dtdef_ref dtd) +{ + uint32_t btf_kind, encoding; + + btf_kind = get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info)); + + if (btf_kind == BTF_KIND_UNKN) + return; + + switch (btf_kind) + { + case BTF_KIND_INT: + /* Redundant definitions of void may still be hanging around in the type + list as size 0 integers. Skip emitting them. */ + if (dtd->dtd_data.ctti_size < 1) + break; + + encoding = BTF_INT_DATA (dtd->dtd_u.dtu_enc.cte_format, + dtd->dtd_u.dtu_enc.cte_offset, + dtd->dtd_u.dtu_enc.cte_bits); + + dw2_asm_output_data (4, encoding, "bti_encoding"); + break; + + case BTF_KIND_ARRAY: + btf_asm_array (dtd); + break; + + case BTF_KIND_STRUCT: + case BTF_KIND_UNION: + output_asm_btf_sou_fields (ctfc, dtd); + break; + + case BTF_KIND_ENUM: + output_asm_btf_enum_list (ctfc, dtd); + break; + + case BTF_KIND_FUNC_PROTO: + output_asm_btf_func_args_list (ctfc, dtd); + break; + + case BTF_KIND_VAR: + /* BTF Variables are handled by output_btf_vars and btf_asm_varent. + There should be no BTF_KIND_VAR types at this point. */ + gcc_unreachable (); + + case BTF_KIND_DATASEC: + /* The BTF_KIND_DATASEC records are handled by output_btf_datasec_types + and btf_asm_datasec_type. There should be no BTF_KIND_DATASEC types + at this point. */ + gcc_unreachable (); + + default: + /* All other BTF type kinds have no variable length data. */ + break; + } +} + +/* Output a whole BTF type record for TYPE, including the fixed and variable + data portions. */ + +static void +output_asm_btf_type (ctf_container_ref ctfc, ctf_dtdef_ref type) +{ + if (btf_emit_id_p (type->dtd_type)) + { + btf_asm_type (ctfc, type); + output_asm_btf_vlen_bytes (ctfc, type); + } +} + +/* Output all BTF types in the container. This does not include synthesized + types: BTF_KIND_VAR, BTF_KIND_FUNC, nor BTF_KIND_DATASEC. */ + +static void +output_btf_types (ctf_container_ref ctfc) +{ + size_t i; + size_t num_types = ctfc->ctfc_types->elements (); + if (num_types) + { + for (i = 1; i <= num_types; i++) + output_asm_btf_type (ctfc, ctfc->ctfc_types_list[i]); + } +} + +/* Output all BTF_KIND_FUNC type records. */ + +static void +output_btf_func_types (void) +{ + for (size_t i = 0; i < vec_safe_length (funcs); i++) + btf_asm_func_type ((*funcs)[i]); +} + +/* Output all BTF_KIND_DATASEC records. */ + +static void +output_btf_datasec_types (ctf_container_ref ctfc) +{ + size_t name_offset = ctfc_get_strtab_len (ctfc, CTF_STRTAB); + + for (size_t i = 0; i < datasecs.length(); i++) + btf_asm_datasec_type (datasecs[i], name_offset); +} + +/* Postprocess the CTF debug data post initialization. + + During the postprocess pass: + + - Prepare the sorted list of BTF types. + + The sorted list of BTF types is, firstly, used for lookup (during the BTF + generation process) of CTF/BTF types given a typeID. + + Secondly, in the emitted BTF section, BTF Types need to be in the sorted + order of their type IDs. The BTF types section is viewed as an array, + with type IDs used to index into that array. It is essential that every + type be placed at the exact index corresponding to its ID, or else + references to that type from other types will no longer be correct. + + - References to void types are converted to reference BTF_VOID_TYPEID. In + CTF, a distinct type is used to encode void. + + - Bitfield struct/union members are converted to BTF encoding. CTF uses + slices to encode bitfields, but BTF does not have slices and encodes + bitfield information directly in the variable-length btf_member + descriptions following the struct or union type. + + - Unrepresentable types are removed. We cannot have any invalid BTF types + appearing in the output so they must be removed, and type ids of other + types and references adjust accordingly. This also involves ensuring that + BTF descriptions of struct members referring to unrepresentable types are + not emitted, as they would be nonsensical. + + - Adjust inner- and inter-type references-by-ID to account for removed + types, and construct the types list. */ + +void +btf_init_postprocess (void) +{ + ctf_container_ref tu_ctfc = ctf_get_tu_ctfc (); + + size_t i; + size_t num_ctf_types = tu_ctfc->ctfc_types->elements (); + + holes.create (0); + voids.create (0); + + num_types_added = 0; + num_types_created = 0; + + if (num_ctf_types) + { + init_btf_id_map (num_ctf_types + 1); + + /* Allocate the types list and traverse all types, placing each type + at the index according to its ID. Add 1 because type ID 0 always + represents VOID. */ + tu_ctfc->ctfc_types_list + = ggc_vec_alloc<ctf_dtdef_ref>(num_ctf_types + 1); + tu_ctfc->ctfc_types->traverse<ctf_container_ref, btf_dtd_postprocess_cb> + (tu_ctfc); + + /* Build mapping of CTF type ID -> BTF type ID, and count total number + of valid BTF types added. */ + for (i = 1; i <= num_ctf_types; i++) + { + ctf_dtdef_ref dtd = tu_ctfc->ctfc_types_list[i]; + ctf_id_t btfid = btf_adjust_type_id (dtd->dtd_type); + set_btf_id (dtd->dtd_type, btfid); + if (btfid < BTF_MAX_TYPE && (btfid != BTF_VOID_TYPEID)) + num_types_added ++; + } + } +} + +/* Process and output all BTF data. Entry point of btfout. */ + +void +btf_output (const char * filename) +{ + ctf_container_ref tu_ctfc = ctf_get_tu_ctfc (); + + init_btf_sections (); + + datasecs.create (0); + vec_alloc (funcs, 16); + + ctf_add_cuname (tu_ctfc, filename); + + btf_emit_preprocess (tu_ctfc); + + output_btf_header (tu_ctfc); + output_btf_types (tu_ctfc); + output_btf_vars (tu_ctfc); + output_btf_func_types (); + output_btf_datasec_types (tu_ctfc); + output_btf_strs (tu_ctfc); +} + +/* Reset all state for BTF generation so that we can rerun the compiler within + the same process. */ + +void +btf_finalize (void) +{ + btf_info_section = NULL; + + /* Clear preprocessing state. */ + num_vars_added = 0; + num_types_added = 0; + num_types_created = 0; + + holes.release (); + voids.release (); + for (size_t i = 0; i < datasecs.length (); i++) + datasecs[i].entries.release (); + datasecs.release (); + + funcs = NULL; + + free (btf_id_map); + btf_id_map = NULL; + + ggc_free (btf_var_ids); + btf_var_ids = NULL; + + ctf_container_ref tu_ctfc = ctf_get_tu_ctfc (); + ctfc_delete_container (tu_ctfc); + tu_ctfc = NULL; +} + +#include "gt-btfout.h" |