mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
1828 lines
54 KiB
C++
1828 lines
54 KiB
C++
/* Output BTF format from GCC.
|
|
Copyright (C) 2021-2026 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.cc, 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 "stringpool.h" /* For lookup_attribute. */
|
|
#include "attribs.h" /* For lookup_attribute. */
|
|
#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
|
|
|
|
#define BTF_INVALID_TYPEID 0xFFFFFFFF
|
|
|
|
/* Internal representation of an entry in a BTF_KIND_DATASEC record. */
|
|
struct btf_datasec_entry
|
|
{
|
|
union {
|
|
ctf_dvdef_ref dvd; /* Reference to the underlying variable represented. */
|
|
ctf_dtdef_ref dtd; /* Reference to the underlying type represented. */
|
|
};
|
|
bool is_var; /* True iff this entry represents a variable. */
|
|
uint32_t size; /* Size of variable or function, in bytes.
|
|
For functions, always zero at compile time. */
|
|
};
|
|
|
|
/* Internal representation of a BTF_KIND_DATASEC record. */
|
|
typedef struct btf_datasec
|
|
{
|
|
ctf_id_t id; /* BTF type ID of this record. */
|
|
const char *name; /* Section name, e.g. ".bss". */
|
|
uint32_t name_offset; /* Offset to name in string table. */
|
|
vec<struct btf_datasec_entry> entries; /* 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;
|
|
|
|
/* 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;
|
|
|
|
/* Maps BTF_KIND_FUNC_PROTO to the BTF_KIND_FUNC record for it. Used when
|
|
creating DATASEC entries. */
|
|
static GTY (()) hash_map<ctf_dtdef_ref, ctf_dtdef_ref> *func_map;
|
|
|
|
/* Highest BTF ID assigned to any regular type translated from CTF.
|
|
Does not include BTF_KIND_{VAR,FUNC,DATASEC} types. */
|
|
static ctf_id_t max_translated_id = 0;
|
|
|
|
/* Name strings for BTF kinds.
|
|
Note: the indices here must match the type defines in btf.h. */
|
|
static const char *const btf_kind_names[] =
|
|
{
|
|
"UNKN", "INT", "PTR", "ARRAY", "STRUCT", "UNION", "ENUM", "FWD",
|
|
"TYPEDEF", "VOLATILE", "CONST", "RESTRICT", "FUNC", "FUNC_PROTO",
|
|
"VAR", "DATASEC", "FLOAT", "DECL_TAG", "TYPE_TAG", "ENUM64"
|
|
};
|
|
|
|
/* Return a name string for the given BTF_KIND. */
|
|
|
|
static const char *
|
|
btf_kind_name (uint32_t btf_kind)
|
|
{
|
|
return btf_kind_names[btf_kind];
|
|
}
|
|
|
|
/* 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;
|
|
case CTF_K_DECL_TAG: return BTF_KIND_DECL_TAG;
|
|
case CTF_K_TYPE_TAG: return BTF_KIND_TYPE_TAG;
|
|
default:;
|
|
}
|
|
return BTF_KIND_UNKN;
|
|
}
|
|
|
|
/* Convenience wrapper around get_btf_kind for the common case. */
|
|
|
|
uint32_t
|
|
btf_dtd_kind (ctf_dtdef_ref dtd)
|
|
{
|
|
if (!dtd)
|
|
return BTF_KIND_UNKN;
|
|
return get_btf_kind (CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info));
|
|
}
|
|
|
|
/* Some BTF types, like BTF_KIND_FUNC_PROTO, are anonymous. The machinery
|
|
in btfout to emit BTF, may reset dtd_data->ctti_name, but does not update
|
|
the name in the ctf_dtdef_ref type object (deliberate choice). This
|
|
interface helps abstract out that state of affairs, while giving access to
|
|
the name of the type as intended. */
|
|
|
|
static const char *
|
|
get_btf_type_name (ctf_dtdef_ref dtd)
|
|
{
|
|
const char *anon = "";
|
|
return (dtd->dtd_data.ctti_name) ? dtd->dtd_name : anon;
|
|
}
|
|
|
|
static bool
|
|
btf_emit_type_p (ctf_dtdef_ref dtd)
|
|
{
|
|
uint32_t kind = btf_dtd_kind (dtd);
|
|
|
|
if (kind == BTF_KIND_UNKN)
|
|
/* This type is not representable in BTF. */
|
|
return false;
|
|
|
|
if (kind == BTF_KIND_INT && dtd->dtd_data.ctti_size == 0)
|
|
/* This is a (redundant) definition of void. */
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* Return true if DTD is a forward-declared enum. The BTF representation
|
|
of forward declared enums is not formally defined. */
|
|
|
|
static bool
|
|
btf_fwd_to_enum_p (ctf_dtdef_ref dtd)
|
|
{
|
|
uint32_t kind = btf_dtd_kind (dtd);
|
|
return (kind == BTF_KIND_FWD && dtd->dtd_data.ctti_type == CTF_K_ENUM);
|
|
}
|
|
|
|
/* 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 = btf_dtd_kind (dtd);
|
|
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:
|
|
case BTF_KIND_TYPE_TAG:
|
|
/* 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 += (dtd->dtd_data.ctti_size > 4)
|
|
? vlen * sizeof (struct btf_enum64)
|
|
: 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;
|
|
|
|
case BTF_KIND_DECL_TAG:
|
|
vlen_bytes += sizeof (struct btf_decl_tag);
|
|
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++);
|
|
}
|
|
|
|
/* Return the section name, as of interest to btf_collect_datasec, for the
|
|
given symtab node. Note that this deliberately returns NULL for objects
|
|
which do not go in a section btf_collect_datasec cares about. */
|
|
static const char *
|
|
get_section_name (symtab_node *node)
|
|
{
|
|
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:;
|
|
}
|
|
}
|
|
|
|
return section_name;
|
|
}
|
|
|
|
/* 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_dmdef_t *dmd)
|
|
{
|
|
ctf_dtdef_ref ref_type = dmd->dmd_type;
|
|
if (!ref_type)
|
|
return false;
|
|
|
|
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 reference to another BTF type. */
|
|
|
|
static void
|
|
btf_asm_type_ref (const char *prefix, ctf_dtdef_ref dtd)
|
|
{
|
|
if (!dtd || !btf_emit_type_p (dtd))
|
|
dw2_asm_output_data (4, BTF_VOID_TYPEID, "%s: void", prefix);
|
|
else
|
|
{
|
|
uint32_t kind = btf_dtd_kind (dtd);
|
|
if (btf_fwd_to_enum_p (dtd))
|
|
kind = BTF_KIND_ENUM;
|
|
else if (kind == BTF_KIND_FUNC_PROTO && dtd->dtd_type > max_translated_id)
|
|
kind = BTF_KIND_FUNC;
|
|
|
|
dw2_asm_output_data (4, dtd->dtd_type, "%s: (BTF_KIND_%s '%s')",
|
|
prefix, btf_kind_name (kind),
|
|
get_btf_type_name (dtd));
|
|
}
|
|
}
|
|
|
|
/* 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_dtdef_ref dtd)
|
|
{
|
|
uint32_t btf_kind, btf_kflag, btf_vlen, btf_size;
|
|
uint32_t ctf_info = dtd->dtd_data.ctti_info;
|
|
|
|
btf_kind = btf_dtd_kind (dtd);
|
|
btf_size = dtd->dtd_data.ctti_size;
|
|
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 >= 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. */
|
|
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 (dmd))
|
|
{
|
|
btf_kflag = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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.
|
|
Forwards to enum types are special-cased below. */
|
|
else if (btf_kind == BTF_KIND_FWD)
|
|
{
|
|
if (dtd->dtd_data.ctti_type == CTF_K_UNION)
|
|
btf_kflag = 1;
|
|
|
|
/* PR debug/111735. Encode foward-declared enums as BTF_KIND_ENUM
|
|
with vlen=0. A representation for these is not formally defined;
|
|
this is the de-facto standard used by other tools like clang
|
|
and pahole. */
|
|
else if (dtd->dtd_data.ctti_type == CTF_K_ENUM)
|
|
{
|
|
btf_kind = BTF_KIND_ENUM;
|
|
btf_vlen = 0;
|
|
}
|
|
|
|
btf_size = 0;
|
|
}
|
|
|
|
else if (btf_kind == BTF_KIND_ENUM)
|
|
{
|
|
btf_kflag = dtd->dtd_enum_unsigned
|
|
? BTF_KF_ENUM_UNSIGNED
|
|
: BTF_KF_ENUM_SIGNED;
|
|
if (dtd->dtd_data.ctti_size == 0x8)
|
|
btf_kind = BTF_KIND_ENUM64;
|
|
}
|
|
|
|
/* PR debug/112656. BTF_KIND_FUNC_PROTO is always anonymous. */
|
|
else if (btf_kind == BTF_KIND_FUNC_PROTO)
|
|
dtd->dtd_data.ctti_name = 0;
|
|
|
|
dw2_asm_output_data (4, dtd->dtd_data.ctti_name,
|
|
"TYPE %" PRIu64 " BTF_KIND_%s '%s'",
|
|
dtd->dtd_type, btf_kind_name (btf_kind),
|
|
get_btf_type_name (dtd));
|
|
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:
|
|
case BTF_KIND_ENUM64:
|
|
dw2_asm_output_data (4, btf_size, "btt_size: %uB", btf_size);
|
|
return;
|
|
case BTF_KIND_ARRAY:
|
|
case BTF_KIND_FWD:
|
|
/* These types do not encode any information in the size/type field
|
|
and should write 0. */
|
|
dw2_asm_output_data (4, 0, "(unused)");
|
|
return;
|
|
case BTF_KIND_DECL_TAG:
|
|
{
|
|
if (dtd->ref_type)
|
|
break;
|
|
else if (dtd->dtd_u.dtu_tag.ref_var)
|
|
{
|
|
/* ref_type is NULL for decl tag attached to a variable. */
|
|
ctf_dvdef_ref dvd = dtd->dtd_u.dtu_tag.ref_var;
|
|
dw2_asm_output_data (4, dvd->dvd_id,
|
|
"btt_type: (BTF_KIND_VAR '%s')",
|
|
dvd->dvd_name);
|
|
return;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
btf_asm_type_ref ("btt_type", dtd->ref_type);
|
|
}
|
|
|
|
/* Asm'out the variable information following a BTF_KIND_ARRAY. */
|
|
|
|
static void
|
|
btf_asm_array (ctf_arinfo_t arr)
|
|
{
|
|
btf_asm_type_ref ("bta_elem_type", arr.ctr_contents);
|
|
btf_asm_type_ref ("bta_index_type", arr.ctr_index);
|
|
dw2_asm_output_data (4, 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,
|
|
"TYPE %" PRIu64 " BTF_KIND_VAR '%s'",
|
|
var->dvd_id, var->dvd_name);
|
|
dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_VAR, 0, 0), "btv_info");
|
|
btf_asm_type_ref ("btv_type", var->dvd_type);
|
|
dw2_asm_output_data (4, var->dvd_visibility, "btv_linkage");
|
|
}
|
|
|
|
/* Asm'out a member description following a BTF_KIND_STRUCT or
|
|
BTF_KIND_UNION. */
|
|
|
|
static void
|
|
btf_asm_sou_member (ctf_dmdef_t * dmd, unsigned int idx)
|
|
{
|
|
ctf_dtdef_ref base_type = dmd->dmd_type;
|
|
uint64_t sou_offset = dmd->dmd_offset;
|
|
|
|
dw2_asm_output_data (4, dmd->dmd_name_offset,
|
|
"MEMBER '%s' idx=%u",
|
|
dmd->dmd_name, idx);
|
|
|
|
if (base_type
|
|
&& CTF_V2_INFO_KIND (base_type->dtd_data.ctti_info) == CTF_K_SLICE)
|
|
{
|
|
if (btf_dmd_representable_bitfield_p (dmd))
|
|
{
|
|
unsigned short word_offset = base_type->dtd_u.dtu_slice.cts_offset;
|
|
unsigned short bits = base_type->dtd_u.dtu_slice.cts_bits;
|
|
|
|
/* Pack the bit offset and bitfield size together. */
|
|
sou_offset += word_offset;
|
|
sou_offset &= 0x00ffffff;
|
|
sou_offset |= ((bits & 0xff) << 24);
|
|
|
|
/* Refer to the base type of the slice. */
|
|
base_type = base_type->dtd_u.dtu_slice.cts_type;
|
|
}
|
|
else
|
|
{
|
|
/* Bitfield cannot be represented in BTF. Emit the member as having
|
|
'void' type. */
|
|
base_type = NULL;
|
|
}
|
|
}
|
|
|
|
btf_asm_type_ref ("btm_type", base_type);
|
|
dw2_asm_output_data (4, sou_offset, "btm_offset");
|
|
}
|
|
|
|
/* Asm'out an enum constant following a BTF_KIND_ENUM{,64}. */
|
|
|
|
static void
|
|
btf_asm_enum_const (unsigned int size, ctf_dmdef_t * dmd, unsigned int idx)
|
|
{
|
|
dw2_asm_output_data (4, dmd->dmd_name_offset, "ENUM_CONST '%s' idx=%u",
|
|
dmd->dmd_name, idx);
|
|
if (size <= 4)
|
|
dw2_asm_output_data (size < 4 ? 4 : size, dmd->dmd_value, "bte_value");
|
|
else
|
|
{
|
|
dw2_asm_output_data (4, dmd->dmd_value & 0xffffffff, "bte_value_lo32");
|
|
dw2_asm_output_data (4, (dmd->dmd_value >> 32) & 0xffffffff, "bte_value_hi32");
|
|
}
|
|
}
|
|
|
|
/* 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 '%s'", farg->farg_name);
|
|
else
|
|
dw2_asm_output_data (4, 0, "farg_name ''");
|
|
|
|
btf_asm_type_ref ("farg_type", farg->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,
|
|
"TYPE %" PRIu64 " BTF_KIND_FUNC '%s'",
|
|
dtd->dtd_type, get_btf_type_name (dtd));
|
|
dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_FUNC, 0, dtd->linkage),
|
|
"btt_info: kind=%u, kflag=%u, linkage=%u",
|
|
BTF_KIND_FUNC, 0, dtd->linkage);
|
|
btf_asm_type_ref ("btt_type", dtd->ref_type);
|
|
}
|
|
|
|
/* Asm'out a variable entry following a BTF_KIND_DATASEC. */
|
|
|
|
static void
|
|
btf_asm_datasec_entry (struct btf_datasec_entry entry)
|
|
{
|
|
const char *symbol_name = NULL;
|
|
if (entry.is_var)
|
|
{
|
|
symbol_name = entry.dvd->dvd_name;
|
|
dw2_asm_output_data (4, entry.dvd->dvd_id,
|
|
"bts_type: (BTF_KIND_VAR '%s')", symbol_name);
|
|
}
|
|
else
|
|
{
|
|
symbol_name = entry.dtd->dtd_name;
|
|
btf_asm_type_ref ("bts_type", entry.dtd);
|
|
}
|
|
|
|
if (!btf_with_core_debuginfo_p () || symbol_name == NULL)
|
|
dw2_asm_output_data (4, 0, "bts_offset");
|
|
else
|
|
dw2_asm_output_offset (4, symbol_name, NULL, "bts_offset");
|
|
|
|
dw2_asm_output_data (4, entry.size, "bts_size");
|
|
}
|
|
|
|
/* Asm'out a whole BTF_KIND_DATASEC, including its variable entries. */
|
|
|
|
static void
|
|
btf_asm_datasec_type (btf_datasec_t ds)
|
|
{
|
|
dw2_asm_output_data (4, ds.name_offset,
|
|
"TYPE %" PRIu64 " BTF_KIND_DATASEC '%s'",
|
|
ds.id, ds.name);
|
|
dw2_asm_output_data (4, BTF_TYPE_INFO (BTF_KIND_DATASEC, 0,
|
|
ds.entries.length ()),
|
|
"btt_info: n_entries=%u", ds.entries.length ());
|
|
/* 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;
|
|
|
|
if (!ctfc_is_empty_container (ctfc))
|
|
{
|
|
/* Total length (bytes) of the types section. */
|
|
type_len = ctfc->ctfc_num_types * sizeof (struct btf_type)
|
|
+ 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: ntypes=%u, vlen=%u",
|
|
(uint32_t) ctfc->ctfc_num_types,
|
|
(uint32_t) ctfc->ctfc_num_vlen_bytes);
|
|
/* 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 = ctfc->ctfc_vars_list_count;
|
|
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;
|
|
static int str_pos = 0;
|
|
|
|
while (ctf_string)
|
|
{
|
|
dw2_asm_output_nstring (ctf_string->cts_str, -1,
|
|
"btf_string, str_pos = 0x%x", str_pos);
|
|
str_pos += strlen(ctf_string->cts_str) + 1;
|
|
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, str_pos = 0x%x", str_pos);
|
|
str_pos += strlen(ctf_string->cts_str) + 1;
|
|
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_dtdef_ref dtd)
|
|
{
|
|
ctf_dmdef_t * dmd;
|
|
|
|
unsigned idx = 0;
|
|
for (dmd = dtd->dtd_u.dtu_members;
|
|
dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
|
|
{
|
|
btf_asm_sou_member (dmd, idx);
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
/* Output all enumerator constants following a BTF_KIND_ENUM{,64}. */
|
|
|
|
static void
|
|
output_asm_btf_enum_list (ctf_dtdef_ref dtd)
|
|
{
|
|
ctf_dmdef_t * dmd;
|
|
|
|
unsigned idx = 0;
|
|
for (dmd = dtd->dtd_u.dtu_members;
|
|
dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
|
|
{
|
|
btf_asm_enum_const (dtd->dtd_data.ctti_size, dmd, idx);
|
|
idx++;
|
|
}
|
|
}
|
|
|
|
/* 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 = btf_dtd_kind (dtd);
|
|
|
|
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;
|
|
|
|
/* In BTF the CHAR `encoding' seems to not be used, so clear it here. */
|
|
dtd->dtd_u.dtu_enc.cte_format &= ~BTF_INT_CHAR;
|
|
|
|
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->dtd_u.dtu_arr);
|
|
break;
|
|
|
|
case BTF_KIND_STRUCT:
|
|
case BTF_KIND_UNION:
|
|
output_asm_btf_sou_fields (dtd);
|
|
break;
|
|
|
|
case BTF_KIND_ENUM:
|
|
output_asm_btf_enum_list (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 ();
|
|
|
|
case BTF_KIND_DECL_TAG:
|
|
dw2_asm_output_data (4, dtd->dtd_u.dtu_tag.component_idx,
|
|
"component_idx=%d",
|
|
dtd->dtd_u.dtu_tag.component_idx);
|
|
break;
|
|
|
|
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_type_p (type))
|
|
{
|
|
btf_asm_type (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;
|
|
if (debug_prune_btf)
|
|
num_types = max_translated_id;
|
|
else
|
|
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)
|
|
{
|
|
ctf_dtdef_ref ref;
|
|
unsigned i;
|
|
FOR_EACH_VEC_ELT (*funcs, i, ref)
|
|
btf_asm_func_type (ref);
|
|
}
|
|
|
|
static void
|
|
output_btf_tags (ctf_container_ref ctfc)
|
|
{
|
|
/* If pruning, tags which are not pruned have already been added to
|
|
the used list and output by output_btf_types. */
|
|
if (debug_prune_btf)
|
|
return;
|
|
|
|
ctf_dtdef_ref dtd;
|
|
unsigned i;
|
|
FOR_EACH_VEC_ELT (*ctfc->ctfc_tags, i, dtd)
|
|
output_asm_btf_type (ctfc, dtd);
|
|
}
|
|
|
|
/* Output all BTF_KIND_DATASEC records. */
|
|
|
|
static void
|
|
output_btf_datasec_types (void)
|
|
{
|
|
for (size_t i = 0; i < datasecs.length (); i++)
|
|
btf_asm_datasec_type (datasecs[i]);
|
|
}
|
|
|
|
/* Write out all BTF debug info. */
|
|
|
|
void
|
|
btf_output (ctf_container_ref ctfc)
|
|
{
|
|
output_btf_header (ctfc);
|
|
output_btf_types (ctfc);
|
|
output_btf_vars (ctfc);
|
|
output_btf_func_types ();
|
|
output_btf_tags (ctfc);
|
|
output_btf_datasec_types ();
|
|
output_btf_strs (ctfc);
|
|
}
|
|
|
|
/* Workaround for 'const void' variables. These variables are sometimes used
|
|
in eBPF programs to address kernel symbols. DWARF does not generate const
|
|
qualifier on void type, so we would incorrectly emit these variables
|
|
without the const qualifier. Find any such variables, and update them to
|
|
refer to a new 'const' modifier type for void. */
|
|
|
|
static void
|
|
btf_add_const_void (ctf_container_ref ctfc)
|
|
{
|
|
ctf_dtdef_ref constvoid_dtd = NULL;
|
|
varpool_node *var;
|
|
FOR_EACH_VARIABLE (var)
|
|
{
|
|
if (!var->decl)
|
|
continue;
|
|
|
|
tree type = TREE_TYPE (var->decl);
|
|
if (type && VOID_TYPE_P (type) && TYPE_READONLY (type))
|
|
{
|
|
dw_die_ref die = lookup_decl_die (var->decl);
|
|
if (die == NULL)
|
|
continue;
|
|
|
|
ctf_dvdef_ref dvd = ctf_dvd_lookup (ctfc, die);
|
|
if (dvd == NULL)
|
|
continue;
|
|
|
|
/* Create the 'const' modifier type for void. */
|
|
if (constvoid_dtd == NULL)
|
|
constvoid_dtd = ctf_add_reftype (ctfc, CTF_ADD_ROOT,
|
|
dvd->dvd_type, CTF_K_CONST, NULL);
|
|
dvd->dvd_type = constvoid_dtd;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Functions actually get two type records: 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,
|
|
construct a BTF_KIND_FUNC entry. This is done early, because we want FUNC
|
|
records even for functions which are later inlined by optimizations. */
|
|
|
|
static void
|
|
btf_add_func_records (ctf_container_ref ctfc)
|
|
{
|
|
cgraph_node *func;
|
|
FOR_EACH_FUNCTION (func)
|
|
{
|
|
dw_die_ref die = lookup_decl_die (func->decl);
|
|
if (die == NULL)
|
|
continue;
|
|
|
|
ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
|
|
if (dtd == NULL)
|
|
continue;
|
|
|
|
/* Do not add FUNC records for kernel helpers. */
|
|
if (DECL_EXTERNAL (func->decl)
|
|
&& (lookup_attribute ("kernel_helper",
|
|
DECL_ATTRIBUTES (func->decl))) != NULL_TREE)
|
|
continue;
|
|
|
|
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;
|
|
func_dtd->ref_type = dtd;
|
|
func_dtd->linkage = dtd->linkage;
|
|
func_dtd->dtd_name = dtd->dtd_name;
|
|
/* Type ID will be assigned just before output. */
|
|
|
|
/* Only the BTF_KIND_FUNC type actually references the name.
|
|
The BTF_KIND_FUNC_PROTO is always anonymous. */
|
|
dtd->dtd_data.ctti_name = 0;
|
|
|
|
/* Mark 'extern' funcs. */
|
|
if (DECL_EXTERNAL (func->decl))
|
|
func_dtd->linkage = BTF_FUNC_EXTERN;
|
|
|
|
/* Buffer newly created FUNC records. We cannot simply insert them
|
|
into the types map, because types are keyed by their DWARF DIE,
|
|
and we have no unique DIE to use as a key since the FUNC_PROTOs
|
|
are already present in the map. */
|
|
vec_safe_push (funcs, func_dtd);
|
|
func_map->put (dtd, func_dtd);
|
|
}
|
|
}
|
|
|
|
/* The set of types used directly in the source program, and any types manually
|
|
marked as used. This is the set of types which will be emitted when
|
|
flag_prune_btf is set. */
|
|
static GTY (()) hash_set<ctf_dtdef_ref> *btf_used_types;
|
|
|
|
/* Fixup used to avoid unnecessary pointer chasing for types. A fixup is
|
|
created when a structure or union member is a pointer to another struct
|
|
or union type. In such cases, avoid emitting full type information for
|
|
the pointee struct or union type (which may be quite large), unless that
|
|
type is used directly elsewhere. */
|
|
struct btf_fixup
|
|
{
|
|
ctf_dtdef_ref pointer_dtd; /* Type node to which the fixup is applied. */
|
|
ctf_dtdef_ref pointee_dtd; /* Original type node referred to by pointer_dtd.
|
|
If this concrete type is not otherwise used,
|
|
then a forward is created. */
|
|
};
|
|
|
|
/* Stores fixups while processing types. */
|
|
static vec<struct btf_fixup> fixups;
|
|
|
|
/* For fixups where the underlying type is not used in the end, a BTF_KIND_FWD
|
|
is created and emitted. This vector stores them. */
|
|
static GTY (()) vec<ctf_dtdef_ref, va_gc> *forwards;
|
|
|
|
/* Implementation of btf_add_used_type.
|
|
Recursively add type DTD and any types it references to the used set.
|
|
Return a type that should be used for references to DTD - usually DTD itself,
|
|
but may be NULL if DTD corresponds to a type which will not be emitted.
|
|
CHECK_PTR is true if one of the predecessors in recursive calls is a struct
|
|
or union member. SEEN_PTR is true if CHECK_PTR is true AND one of the
|
|
predecessors was a pointer type. These two flags are used to avoid chasing
|
|
pointers to struct/union only used from pointer members. For such types, we
|
|
will emit a forward instead of the full type information, unless
|
|
CREATE_FIXUPS is false. */
|
|
|
|
static ctf_dtdef_ref
|
|
btf_add_used_type_1 (ctf_container_ref ctfc, ctf_dtdef_ref dtd,
|
|
bool check_ptr, bool seen_ptr, bool create_fixups)
|
|
{
|
|
if (dtd == NULL)
|
|
return NULL;
|
|
|
|
uint32_t ctf_kind = CTF_V2_INFO_KIND (dtd->dtd_data.ctti_info);
|
|
uint32_t kind = get_btf_kind (ctf_kind);
|
|
|
|
/* Check whether the type has already been added. */
|
|
if (btf_used_types->contains (dtd))
|
|
{
|
|
/* It's possible the type was already added as a fixup, but that we now
|
|
have a concrete use of it. */
|
|
switch (kind)
|
|
{
|
|
case BTF_KIND_PTR:
|
|
case BTF_KIND_TYPEDEF:
|
|
case BTF_KIND_CONST:
|
|
case BTF_KIND_VOLATILE:
|
|
case BTF_KIND_RESTRICT:
|
|
if (check_ptr)
|
|
/* Type was previously added as a fixup, and that's OK. */
|
|
return dtd;
|
|
else
|
|
{
|
|
/* The type was previously added as a fixup, but now we have
|
|
a concrete use of it. Remove the fixup. */
|
|
for (size_t i = 0; i < fixups.length (); i++)
|
|
if (fixups[i].pointer_dtd == dtd)
|
|
fixups.unordered_remove (i);
|
|
|
|
/* Add the concrete base type. */
|
|
dtd->ref_type = btf_add_used_type_1 (ctfc, dtd->ref_type,
|
|
check_ptr, seen_ptr,
|
|
create_fixups);
|
|
return dtd;
|
|
}
|
|
default:
|
|
return dtd;
|
|
}
|
|
}
|
|
|
|
if (ctf_kind == CTF_K_SLICE)
|
|
{
|
|
/* Bitfield. Add the underlying type to the used set, but leave
|
|
the reference to the bitfield. The slice type won't be emitted,
|
|
but we need the information in it when writing out the bitfield
|
|
encoding. */
|
|
btf_add_used_type_1 (ctfc, dtd->dtd_u.dtu_slice.cts_type,
|
|
check_ptr, seen_ptr, create_fixups);
|
|
return dtd;
|
|
}
|
|
|
|
/* Skip redundant definitions of void and types with no BTF encoding. */
|
|
if ((kind == BTF_KIND_INT && dtd->dtd_data.ctti_size == 0)
|
|
|| (kind == BTF_KIND_UNKN))
|
|
return NULL;
|
|
|
|
/* Add the type itself, and assign its id.
|
|
Do this before recursing to handle things like linked list structures. */
|
|
gcc_assert (ctfc->ctfc_nextid <= BTF_MAX_TYPE);
|
|
dtd->dtd_type = ctfc->ctfc_nextid++;
|
|
btf_used_types->add (dtd);
|
|
ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name), CTF_STRTAB);
|
|
ctfc->ctfc_num_types++;
|
|
ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
|
|
|
|
/* Recursively add types referenced by this type. */
|
|
switch (kind)
|
|
{
|
|
case BTF_KIND_INT:
|
|
case BTF_KIND_FLOAT:
|
|
case BTF_KIND_FWD:
|
|
case BTF_KIND_DECL_TAG:
|
|
/* Leaf kinds which do not refer to any other types.
|
|
BTF_KIND_DECL_TAG is a special case: we treat it as though it does not
|
|
refer to any other types, since we only want the DECL_TAG to be added
|
|
if the type to which it refers has already been added. */
|
|
break;
|
|
|
|
case BTF_KIND_FUNC:
|
|
case BTF_KIND_VAR:
|
|
/* Root kinds; no type we are visiting may refer to these. */
|
|
gcc_unreachable ();
|
|
|
|
case BTF_KIND_PTR:
|
|
case BTF_KIND_TYPEDEF:
|
|
case BTF_KIND_CONST:
|
|
case BTF_KIND_VOLATILE:
|
|
case BTF_KIND_RESTRICT:
|
|
case BTF_KIND_TYPE_TAG:
|
|
{
|
|
/* These type kinds refer to exactly one other type. */
|
|
if (check_ptr && !seen_ptr)
|
|
seen_ptr = (kind == BTF_KIND_PTR);
|
|
|
|
/* Try to avoid chasing pointers to struct/union types if the
|
|
underlying type isn't used. */
|
|
if (check_ptr && seen_ptr && create_fixups
|
|
&& kind != BTF_KIND_TYPEDEF)
|
|
{
|
|
ctf_dtdef_ref ref = dtd->ref_type;
|
|
uint32_t ref_kind = btf_dtd_kind (ref);
|
|
|
|
if ((ref_kind == BTF_KIND_STRUCT || ref_kind == BTF_KIND_UNION)
|
|
&& !btf_used_types->contains (ref))
|
|
{
|
|
struct btf_fixup fixup;
|
|
fixup.pointer_dtd = dtd;
|
|
fixup.pointee_dtd = ref;
|
|
fixups.safe_push (fixup);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Add the type to which this type refers. */
|
|
dtd->ref_type = btf_add_used_type_1 (ctfc, dtd->ref_type, check_ptr,
|
|
seen_ptr, create_fixups);
|
|
break;
|
|
}
|
|
case BTF_KIND_ARRAY:
|
|
{
|
|
/* Add element and index types. */
|
|
ctf_arinfo_t *arr = &(dtd->dtd_u.dtu_arr);
|
|
arr->ctr_contents = btf_add_used_type_1 (ctfc, arr->ctr_contents,
|
|
false, false, create_fixups);
|
|
arr->ctr_index = btf_add_used_type_1 (ctfc, arr->ctr_index, false,
|
|
false, create_fixups);
|
|
break;
|
|
}
|
|
case BTF_KIND_STRUCT:
|
|
case BTF_KIND_UNION:
|
|
case BTF_KIND_ENUM:
|
|
case BTF_KIND_ENUM64:
|
|
{
|
|
/* Add members. */
|
|
ctf_dmdef_t *dmd;
|
|
for (dmd = dtd->dtd_u.dtu_members;
|
|
dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
|
|
{
|
|
/* Add member type for struct/union members. For enums, only the
|
|
enumerator names are needed. */
|
|
if (kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION)
|
|
dmd->dmd_type = btf_add_used_type_1 (ctfc, dmd->dmd_type, true,
|
|
false, create_fixups);
|
|
ctf_add_string (ctfc, dmd->dmd_name, &(dmd->dmd_name_offset),
|
|
CTF_STRTAB);
|
|
}
|
|
break;
|
|
}
|
|
case BTF_KIND_FUNC_PROTO:
|
|
{
|
|
/* Add return type. */
|
|
dtd->ref_type = btf_add_used_type_1 (ctfc, dtd->ref_type, false, false,
|
|
create_fixups);
|
|
|
|
/* Add arg types. */
|
|
ctf_func_arg_t * farg;
|
|
for (farg = dtd->dtd_u.dtu_argv;
|
|
farg != NULL; farg = (ctf_func_arg_t *) ctf_farg_list_next (farg))
|
|
{
|
|
farg->farg_type = btf_add_used_type_1 (ctfc, farg->farg_type,
|
|
false, false,
|
|
create_fixups);
|
|
/* Note: argument names are stored in the auxilliary string table,
|
|
since CTF does not include arg names. That table has not been
|
|
cleared, so no need to re-add argument names here. */
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
return dtd;
|
|
}
|
|
|
|
/* Recursively add type DTD and any types it references to the used set.
|
|
Return a type that should be used for references to DTD - usually DTD itself,
|
|
but may be NULL if DTD corresponds to a type which will not be emitted. */
|
|
|
|
static ctf_dtdef_ref
|
|
btf_add_used_type (ctf_container_ref ctfc, ctf_dtdef_ref dtd)
|
|
{
|
|
return btf_add_used_type_1 (ctfc, dtd, false, false, true);
|
|
}
|
|
|
|
/* Initial entry point of BTF generation, called at early_finish () after
|
|
CTF information has possibly been output. Translate all CTF information
|
|
to BTF, and do any processing that must be done early, such as creating
|
|
BTF_KIND_FUNC records. */
|
|
|
|
void
|
|
btf_early_finish (void)
|
|
{
|
|
ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
|
|
|
|
vec_alloc (funcs, 16);
|
|
func_map = hash_map<ctf_dtdef_ref, ctf_dtdef_ref>::create_ggc (16);
|
|
|
|
/* Note: from this point on, destructive changes are made to the TU CTFC to
|
|
translate CTF to BTF. If CTF debug info has also been requested, it must
|
|
be emitted before starting the translation to BTF. */
|
|
btf_add_const_void (tu_ctfc);
|
|
btf_add_func_records (tu_ctfc);
|
|
|
|
/* These fields are reset to count BTF types etc. */
|
|
tu_ctfc->ctfc_num_types = 0;
|
|
tu_ctfc->ctfc_num_vlen_bytes = 0;
|
|
tu_ctfc->ctfc_vars_list_count = 0;
|
|
|
|
if (debug_prune_btf)
|
|
{
|
|
btf_used_types
|
|
= hash_set<ctf_dtdef_ref>::create_ggc (tu_ctfc->ctfc_types->elements ());
|
|
tu_ctfc->ctfc_nextid = 1;
|
|
fixups.create (1);
|
|
|
|
/* Empty the string table, which was already populated with strings for
|
|
all types translated from DWARF. We may only need a very small subset
|
|
of these strings; those will be re-added below. */
|
|
ctfc_delete_strtab (&tu_ctfc->ctfc_strtable);
|
|
init_ctf_strtable (&tu_ctfc->ctfc_strtable);
|
|
tu_ctfc->ctfc_strlen++;
|
|
}
|
|
}
|
|
|
|
/* Push a BTF datasec entry ENTRY into the datasec named SECNAME,
|
|
creating the datasec record if it does not already exist. */
|
|
|
|
static void
|
|
btf_datasec_push_entry (ctf_container_ref ctfc, const char *secname,
|
|
struct btf_datasec_entry entry)
|
|
{
|
|
if (secname == NULL)
|
|
return;
|
|
|
|
/* If we already have a datasec record for the appropriate section,
|
|
append the new entry to it. */
|
|
for (size_t i = 0; i < datasecs.length (); i++)
|
|
if (strcmp (datasecs[i].name, secname) == 0)
|
|
{
|
|
datasecs[i].entries.safe_push (entry);
|
|
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;
|
|
|
|
/* Note: ID will be assigned just before output. */
|
|
btf_datasec_t ds;
|
|
ds.name = secname;
|
|
ds.name_offset = str_off;
|
|
|
|
/* Insert the entry into the new datasec record. */
|
|
ds.entries.create (1);
|
|
ds.entries.quick_push (entry);
|
|
|
|
/* Insert the datasec record itself. */
|
|
datasecs.safe_push (ds);
|
|
}
|
|
|
|
/* Create a datasec entry for a function, and insert it into the datasec
|
|
record for the appropriate section. Create the record if it does not
|
|
yet exist. */
|
|
|
|
static void
|
|
btf_datasec_add_func (ctf_container_ref ctfc, cgraph_node *func,
|
|
ctf_dtdef_ref func_dtd)
|
|
{
|
|
const char *section_name = get_section_name (func);
|
|
|
|
/* Note: get_section_name () returns NULL for functions in text
|
|
section. This is intentional, since we do not want to generate
|
|
DATASEC entries for them. */
|
|
if (section_name == NULL)
|
|
return;
|
|
|
|
struct btf_datasec_entry entry;
|
|
gcc_assert (func_dtd);
|
|
entry.dtd = func_dtd;
|
|
entry.is_var = false;
|
|
|
|
/* Size is left as zero at compile time, to be filled in by loaders
|
|
such as libbpf. */
|
|
entry.size = 0;
|
|
|
|
btf_datasec_push_entry (ctfc, section_name, entry);
|
|
}
|
|
|
|
/* Create a datasec entry for a variable, and insert it into the datasec
|
|
record for the appropriate section. Create the record if it does not
|
|
yet exist. */
|
|
|
|
static void
|
|
btf_datasec_add_var (ctf_container_ref ctfc, varpool_node *var,
|
|
ctf_dvdef_ref dvd)
|
|
{
|
|
/* PR112849: avoid assuming a section for extern decls without
|
|
an explicit section, which would result in incorrectly
|
|
emitting a BTF_KIND_DATASEC entry for them. */
|
|
if (DECL_EXTERNAL (var->decl) && var->get_section () == NULL)
|
|
return;
|
|
|
|
const char *section_name = get_section_name (var);
|
|
if (section_name == NULL)
|
|
return;
|
|
|
|
gcc_assert (dvd);
|
|
struct btf_datasec_entry entry;
|
|
entry.dvd = dvd;
|
|
entry.is_var = true;
|
|
entry.size = 0;
|
|
|
|
tree size = DECL_SIZE_UNIT (var->decl);
|
|
if (tree_fits_uhwi_p (size))
|
|
entry.size = tree_to_uhwi (size);
|
|
else if (VOID_TYPE_P (TREE_TYPE (var->decl)))
|
|
entry.size = 1;
|
|
|
|
btf_datasec_push_entry (ctfc, section_name, entry);
|
|
}
|
|
|
|
/* Add datasec entries for functions to CTFC. */
|
|
|
|
static void
|
|
btf_add_func_datasec_entries (ctf_container_ref ctfc)
|
|
{
|
|
/* We need to create FUNC records at early_finish, so that we have them
|
|
even for functions which are later inlined by optimization passes.
|
|
But on the other hand, we do not want datasec entries for such functions,
|
|
so only create the datasec entries for them late. This loop will not
|
|
hit functions which have already been inlined. */
|
|
cgraph_node *func;
|
|
FOR_EACH_FUNCTION (func)
|
|
{
|
|
dw_die_ref die = lookup_decl_die (func->decl);
|
|
if (die == NULL)
|
|
continue;
|
|
|
|
ctf_dtdef_ref dtd = ctf_dtd_lookup (ctfc, die);
|
|
if (dtd == NULL)
|
|
continue;
|
|
|
|
ctf_dtdef_ref *pdtd = func_map->get (dtd);
|
|
if (pdtd && DECL_EXTERNAL (func->decl))
|
|
btf_datasec_add_func (ctfc, func, *pdtd);
|
|
}
|
|
}
|
|
|
|
/* Helper function used to determine whether or not a BTF_KIND_VAR record
|
|
for the variable VAR shall be emitted. */
|
|
|
|
static bool
|
|
btf_emit_variable_p (ctf_container_ref ctfc, varpool_node *var,
|
|
ctf_dvdef_ref *pdvd)
|
|
{
|
|
dw_die_ref die = lookup_decl_die (var->decl);
|
|
if (die == NULL)
|
|
return false;
|
|
|
|
ctf_dvdef_ref dvd = ctf_dvd_lookup (ctfc, die);
|
|
if (dvd == NULL)
|
|
return false;
|
|
|
|
/* If this is an extern variable declaration with a defining declaration
|
|
later, skip it so that only the defining declaration is emitted.
|
|
This is the same case, fix and reasoning as in CTF; see PR105089. */
|
|
if (ctf_dvd_ignore_lookup (ctfc, dvd->dvd_key))
|
|
return false;
|
|
|
|
/* Skip variables with unrepresentable types. */
|
|
if (!btf_emit_type_p (dvd->dvd_type))
|
|
return false;
|
|
|
|
*pdvd = dvd;
|
|
return true;
|
|
}
|
|
|
|
/* Add BTF_KIND_VAR records for variables. */
|
|
|
|
static void
|
|
btf_add_vars (ctf_container_ref ctfc)
|
|
{
|
|
size_t num_ctf_vars = ctfc->ctfc_vars->elements ();
|
|
|
|
ctfc->ctfc_vars_list = ggc_vec_alloc<ctf_dvdef_ref>(num_ctf_vars);
|
|
|
|
varpool_node *var;
|
|
ctf_dvdef_ref dvd;
|
|
FOR_EACH_VARIABLE (var)
|
|
{
|
|
if (!btf_emit_variable_p (ctfc, var, &dvd))
|
|
continue;
|
|
|
|
/* Mark 'extern' variables. */
|
|
if (DECL_EXTERNAL (var->decl))
|
|
dvd->dvd_visibility = BTF_VAR_GLOBAL_EXTERN;
|
|
|
|
/* Add the variable to the vars list. */
|
|
ctfc->ctfc_vars_list[ctfc->ctfc_vars_list_count++] = dvd;
|
|
|
|
/* Add a BTF_KIND_DATASEC entry for the variable. */
|
|
btf_datasec_add_var (ctfc, var, dvd);
|
|
|
|
const char *section = var->get_section ();
|
|
if (section && (strcmp (section, ".maps") == 0) && debug_prune_btf)
|
|
{
|
|
/* The .maps section has special meaning in BTF: it is used for BPF
|
|
map definitions. These definitions should be structs. We must
|
|
collect pointee types used in map members as though they are used
|
|
directly, effectively ignoring (from the pruning perspective) that
|
|
they are struct members. */
|
|
ctf_dtdef_ref dtd = dvd->dvd_type;
|
|
uint32_t kind = btf_dtd_kind (dvd->dvd_type);
|
|
if (kind == BTF_KIND_STRUCT)
|
|
{
|
|
ctf_dmdef_t *dmd;
|
|
for (dmd = dtd->dtd_u.dtu_members;
|
|
dmd != NULL; dmd = (ctf_dmdef_t *) ctf_dmd_list_next (dmd))
|
|
btf_add_used_type (ctfc, dmd->dmd_type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Callback used by btf_assign_type_ids to insert types into their initial
|
|
positions in the type list. */
|
|
|
|
static int
|
|
btf_type_list_cb (ctf_dtdef_ref *slot, ctf_container_ref ctfc)
|
|
{
|
|
ctf_dtdef_ref dtd = *slot;
|
|
ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
|
|
return 1;
|
|
}
|
|
|
|
/* Construct the initial type list and assign BTF IDs for all types translated
|
|
from CTF. */
|
|
|
|
static void
|
|
btf_collect_translated_types (ctf_container_ref ctfc)
|
|
{
|
|
size_t num_ctf_types = ctfc->ctfc_types->elements ();
|
|
|
|
/* First, place each type at its CTF-assigned index in the list.
|
|
The '+1' here and below is to account for the implicit void type with
|
|
ID 0. There is no real type at index 0 in the list. */
|
|
ctfc->ctfc_types_list = ggc_vec_alloc<ctf_dtdef_ref>(num_ctf_types + 1);
|
|
ctfc->ctfc_types->traverse<ctf_container_ref, btf_type_list_cb> (ctfc);
|
|
|
|
/* Now, pass through the list and adjust IDs to account for types which will
|
|
not be emitted. This results in each type that will be emitted in BTF
|
|
being assigned an appropriate ID. Note that types which will not be
|
|
emitted remain in the list; they are skipped at output time. */
|
|
unsigned int skip = 0;
|
|
for (size_t i = 1; i <= num_ctf_types; i++)
|
|
{
|
|
ctf_dtdef_ref dtd = ctfc->ctfc_types_list[i];
|
|
if (!btf_emit_type_p (dtd))
|
|
{
|
|
dtd->dtd_type = BTF_INVALID_TYPEID;
|
|
skip += 1;
|
|
continue;
|
|
}
|
|
|
|
dtd->dtd_type -= skip;
|
|
ctfc->ctfc_num_types++;
|
|
ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
|
|
}
|
|
|
|
max_translated_id = ctfc->ctfc_num_types;
|
|
ctfc->ctfc_nextid = ctfc->ctfc_num_types + 1;
|
|
}
|
|
|
|
/* Assign BTF IDs for FUNC records and account for their size. */
|
|
|
|
static void
|
|
btf_assign_func_ids (ctf_container_ref ctfc)
|
|
{
|
|
ctf_dtdef_ref dtd;
|
|
unsigned int i;
|
|
FOR_EACH_VEC_ELT (*funcs, i, dtd)
|
|
{
|
|
dtd->dtd_type = ctfc->ctfc_nextid++;
|
|
ctfc->ctfc_num_types++;
|
|
}
|
|
}
|
|
|
|
/* Assign BTF IDs for variables and account for their size. */
|
|
|
|
static void
|
|
btf_assign_var_ids (ctf_container_ref ctfc)
|
|
{
|
|
for (size_t i = 0; i < ctfc->ctfc_vars_list_count; i++)
|
|
{
|
|
ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[i];
|
|
ctf_id_t id = ctfc->ctfc_nextid++;
|
|
gcc_assert (id <= BTF_MAX_TYPE);
|
|
dvd->dvd_id = id;
|
|
|
|
ctfc->ctfc_num_types++;
|
|
ctfc->ctfc_num_vlen_bytes += sizeof (struct btf_var);
|
|
}
|
|
}
|
|
|
|
/* Assign BTF IDs for type and decl tags and account for their size. */
|
|
|
|
static void
|
|
btf_assign_tag_ids (ctf_container_ref ctfc)
|
|
{
|
|
size_t num_tags = vec_safe_length (ctfc->ctfc_tags);
|
|
if (num_tags == 0)
|
|
return;
|
|
|
|
unsigned int i;
|
|
ctf_dtdef_ref dtd;
|
|
FOR_EACH_VEC_ELT (*ctfc->ctfc_tags, i, dtd)
|
|
{
|
|
/* Assign BTF id. */
|
|
ctf_id_t id = ctfc->ctfc_nextid++;
|
|
gcc_assert (id <= BTF_MAX_TYPE);
|
|
dtd->dtd_type = id;
|
|
|
|
/* Tags on functions will have a ref_type pointing to the
|
|
FUNC_PROTO, we want them to point the FUNC record instead. */
|
|
ctf_dtdef_ref *pdtd = NULL;
|
|
if (dtd->ref_type && (pdtd = func_map->get (dtd->ref_type)) != NULL)
|
|
dtd->ref_type = *pdtd;
|
|
|
|
/* Strings for tags are stored in the auxiliary strtab, which is
|
|
concatenated after the regular strtab. ctti_name only accounts
|
|
for offset in the auxiliary strtab until this point. */
|
|
dtd->dtd_data.ctti_name += ctfc_get_strtab_len (ctfc, CTF_STRTAB);
|
|
ctfc->ctfc_num_types++;
|
|
ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (dtd);
|
|
}
|
|
}
|
|
|
|
/* Assign BTF IDs for datasec records and account for their size. */
|
|
|
|
static void
|
|
btf_assign_datasec_ids (ctf_container_ref ctfc)
|
|
{
|
|
for (size_t i = 0; i < datasecs.length (); i++)
|
|
{
|
|
datasecs[i].id = ctfc->ctfc_nextid++;
|
|
datasecs[i].name_offset += ctfc_get_strtab_len (ctfc, CTF_STRTAB);
|
|
ctfc->ctfc_num_types++;
|
|
ctfc->ctfc_num_vlen_bytes += (datasecs[i].entries.length ()
|
|
* sizeof (struct btf_var_secinfo));
|
|
}
|
|
}
|
|
|
|
|
|
/* Manually mark that type T is used to ensure it will not be pruned.
|
|
Used by the BPF backend when generating BPF CO-RE to mark types used
|
|
in CO-RE relocations. */
|
|
|
|
void
|
|
btf_mark_type_used (tree t)
|
|
{
|
|
/* If we are not going to prune anyway, this is a no-op. */
|
|
if (!debug_prune_btf)
|
|
return;
|
|
|
|
gcc_assert (TYPE_P (t));
|
|
ctf_container_ref ctfc = ctf_get_tu_ctfc ();
|
|
ctf_dtdef_ref dtd = ctf_lookup_tree_type (ctfc, t);
|
|
|
|
if (!dtd)
|
|
return;
|
|
|
|
btf_add_used_type (ctfc, dtd);
|
|
}
|
|
|
|
/* Callback used for assembling the only-used-types list. Note that this is
|
|
the same as btf_type_list_cb above, but the hash_set traverse requires a
|
|
different function signature. */
|
|
|
|
static bool
|
|
btf_used_type_list_cb (const ctf_dtdef_ref& dtd, ctf_container_ref ctfc)
|
|
{
|
|
ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
|
|
return true;
|
|
}
|
|
|
|
/* Collect the set of types reachable from global variables and functions.
|
|
This is the minimal set of types, used when generating pruned BTF. */
|
|
|
|
static void
|
|
btf_collect_pruned_types (ctf_container_ref ctfc)
|
|
{
|
|
vec_alloc (forwards, 1);
|
|
|
|
/* Add types used from functions. */
|
|
ctf_dtdef_ref dtd;
|
|
size_t i;
|
|
FOR_EACH_VEC_ELT (*funcs, i, dtd)
|
|
{
|
|
btf_add_used_type (ctfc, dtd->ref_type);
|
|
ctf_add_string (ctfc, dtd->dtd_name, &(dtd->dtd_data.ctti_name),
|
|
CTF_STRTAB);
|
|
}
|
|
|
|
/* Add types used from global variables. */
|
|
for (i = 0; i < ctfc->ctfc_vars_list_count; i++)
|
|
{
|
|
ctf_dvdef_ref dvd = ctfc->ctfc_vars_list[i];
|
|
btf_add_used_type (ctfc, dvd->dvd_type);
|
|
ctf_add_string (ctfc, dvd->dvd_name, &(dvd->dvd_name_offset), CTF_STRTAB);
|
|
}
|
|
|
|
/* Used type tags will be added by recursive btf_add_used_type calls above.
|
|
For decl tags, scan the list and only add those decl tags whose referent
|
|
types are marked as used. We may have pruned a struct type with members
|
|
annotated by a decl tag. */
|
|
FOR_EACH_VEC_ELT (*ctfc->ctfc_tags, i, dtd)
|
|
{
|
|
/* Only add decl tags whose referent types have not been pruned.
|
|
Variables are never pruned, so decl tags on variables are always
|
|
used. */
|
|
if (btf_dtd_kind (dtd) == BTF_KIND_DECL_TAG
|
|
&& ((dtd->ref_type && btf_used_types->contains (dtd->ref_type))
|
|
|| (dtd->dtd_u.dtu_tag.ref_var)))
|
|
btf_add_used_type (ctfc, dtd);
|
|
|
|
/* Tags on functions or function args will have a ref_type pointing to the
|
|
FUNC_PROTO, we want them to point the FUNC record instead. */
|
|
ctf_dtdef_ref *pdtd = NULL;
|
|
if (dtd->ref_type
|
|
&& btf_used_types->contains (dtd->ref_type)
|
|
&& (pdtd = func_map->get (dtd->ref_type)) != NULL)
|
|
dtd->ref_type = *pdtd;
|
|
}
|
|
|
|
/* Process fixups. If the base type was never added, create a forward for it
|
|
and adjust the reference to point to that. If it was added, then nothing
|
|
needs to change. */
|
|
for (i = 0; i < fixups.length (); i++)
|
|
{
|
|
struct btf_fixup *fx = &fixups[i];
|
|
if (!btf_used_types->contains (fx->pointee_dtd))
|
|
{
|
|
/* The underlying type is not used. Create a forward. */
|
|
ctf_dtdef_ref fwd = ggc_cleared_alloc<ctf_dtdef_t> ();
|
|
ctf_id_t id = ctfc->ctfc_nextid++;
|
|
gcc_assert (id <= BTF_MAX_TYPE);
|
|
|
|
bool union_p = (btf_dtd_kind (fx->pointee_dtd) == BTF_KIND_UNION);
|
|
|
|
fwd->dtd_name = fx->pointee_dtd->dtd_name;
|
|
fwd->dtd_data.ctti_info = CTF_TYPE_INFO (CTF_K_FORWARD, union_p, 0);
|
|
fwd->dtd_type = id;
|
|
ctfc->ctfc_num_types++;
|
|
ctfc->ctfc_num_vlen_bytes += btf_calc_num_vbytes (fwd);
|
|
ctf_add_string (ctfc, fwd->dtd_name, &(fwd->dtd_data.ctti_name),
|
|
CTF_STRTAB);
|
|
|
|
/* Update the pointer to point to the forward. */
|
|
fx->pointer_dtd->ref_type = fwd;
|
|
vec_safe_push (forwards, fwd);
|
|
}
|
|
}
|
|
|
|
/* Construct the resulting pruned type list. */
|
|
ctfc->ctfc_types_list
|
|
= ggc_vec_alloc<ctf_dtdef_ref> (btf_used_types->elements () + 1
|
|
+ vec_safe_length (forwards));
|
|
|
|
btf_used_types->traverse<ctf_container_ref, btf_used_type_list_cb> (ctfc);
|
|
|
|
/* Insert the newly created forwards into the regular types list too. */
|
|
FOR_EACH_VEC_ELT (*forwards, i, dtd)
|
|
ctfc->ctfc_types_list[dtd->dtd_type] = dtd;
|
|
|
|
max_translated_id = btf_used_types->elements () + vec_safe_length (forwards);
|
|
}
|
|
|
|
/* Late entry point for BTF generation, called from dwarf2out_finish ().
|
|
Complete and emit BTF information. */
|
|
|
|
void
|
|
btf_finish (void)
|
|
{
|
|
ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
|
|
init_btf_sections ();
|
|
|
|
datasecs.create (0);
|
|
|
|
btf_add_vars (tu_ctfc);
|
|
if (debug_prune_btf)
|
|
{
|
|
/* Collect pruned set of BTF types and prepare for emission.
|
|
This includes only types directly used in file-scope variables and
|
|
function return/argument types. */
|
|
btf_collect_pruned_types (tu_ctfc);
|
|
}
|
|
else
|
|
{
|
|
/* Collect all BTF types and prepare for emission.
|
|
This includes all types translated from DWARF. */
|
|
btf_collect_translated_types (tu_ctfc);
|
|
}
|
|
btf_add_func_datasec_entries (tu_ctfc);
|
|
|
|
btf_assign_var_ids (tu_ctfc);
|
|
btf_assign_func_ids (tu_ctfc);
|
|
|
|
/* Both decl and type tags may be pruned if the types/decls to which they
|
|
refer are pruned. This is handled in btf_collect_pruned_types, and
|
|
through that process they have also been assigned ids already. */
|
|
if (!debug_prune_btf)
|
|
btf_assign_tag_ids (tu_ctfc);
|
|
|
|
btf_assign_datasec_ids (tu_ctfc);
|
|
|
|
/* Finally, write out the complete .BTF section. */
|
|
btf_output (tu_ctfc);
|
|
|
|
/* If compiling for BPF with CO-RE info, we cannot deallocate until after the
|
|
contents of the .BTF.ext section are finalized, which happens very late in
|
|
BPF backend. Therefore, the deallocation (i.e. btf_finalize ()) is delayed
|
|
until TARGET_ASM_FILE_END for BPF CO-RE. */
|
|
if (!btf_with_core_debuginfo_p ())
|
|
btf_finalize ();
|
|
}
|
|
|
|
/* 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;
|
|
max_translated_id = 0;
|
|
|
|
for (size_t i = 0; i < datasecs.length (); i++)
|
|
datasecs[i].entries.release ();
|
|
datasecs.release ();
|
|
|
|
funcs = NULL;
|
|
if (func_map)
|
|
{
|
|
func_map->empty ();
|
|
func_map = NULL;
|
|
}
|
|
|
|
if (debug_prune_btf)
|
|
{
|
|
if (btf_used_types)
|
|
{
|
|
btf_used_types->empty ();
|
|
btf_used_types = NULL;
|
|
}
|
|
|
|
fixups.release ();
|
|
forwards = NULL;
|
|
}
|
|
|
|
ctf_container_ref tu_ctfc = ctf_get_tu_ctfc ();
|
|
ctfc_delete_container (tu_ctfc);
|
|
tu_ctfc = NULL;
|
|
}
|
|
|
|
/* Traversal function for all BTF_KIND_FUNC type records. */
|
|
|
|
bool
|
|
traverse_btf_func_types (funcs_traverse_callback callback, void *data)
|
|
{
|
|
ctf_dtdef_ref ref;
|
|
unsigned i;
|
|
FOR_EACH_VEC_ELT (*funcs, i, ref)
|
|
{
|
|
bool stop = callback (ref, data);
|
|
if (stop == true)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#include "gt-btfout.h"
|