libctf: adapt core dictops for v4 and prefix types

The heart of libctf's reading code is the ctf_dictops_t and the functions it
provides for reading various things no matter what the CTF version in use:
these are called via LCTF_*() macros that translate into calls into the
dictops.

The introduction of prefix types in v4 requires changes here: in particular,
we want the ability to get the type kind of whatever ctf_type_t we are
looking at (the 'unprefixed' kind), as well as the ability to get the type
kind taking prefixes into account: and more generally we want the ability
to both look at a given prefix and look at the type as a whole.  So several
ctf_dictops_t entries are added for this (ctfo_get_prefixed_kind,
ctfo_get_prefixed_vlen).

This means API changes (no callers yet adjusted, it'll happen as we go),
because the existing macros were mostly called with e.g. a ctt_info value
and returned a type kind, while now we need to be called with the actual
ctf_type_t itself, so we can possibly walk beyond it to find the real type
record.  ctfo_get_vbytes needs adjusting for this.

We also add names to most of the ctf_type_t parameters, because suddenly we
can have up to three of them: one relating to the first entry in the type
record (which may be a prefix, usually called 'prefix'), one relating to the
true type record (which may be a suffix, so usually called 'suffix'), and
one possibly relating to some intermediate record if we have multiple
prefixes (usually called 'tp').

There is one horrible special case in here: the vlen of the new
CTF_K_FUNC_LINKAGE kind (equivalent to BTF_KIND_FUNC) is always zero: it
reuses the vlen field to encode the linkage (!).  BTF is rife with ugly
hacks like this.
This commit is contained in:
Nick Alcock
2025-04-24 13:50:38 +01:00
parent ab3ad58be9
commit 6a4a485c7b
2 changed files with 258 additions and 60 deletions

View File

@@ -133,11 +133,13 @@ typedef struct ctf_lookup
typedef struct ctf_dictops
{
uint32_t (*ctfo_get_kind) (uint32_t);
uint32_t (*ctfo_get_prefixed_kind) (const ctf_type_t *tp);
uint32_t (*ctfo_get_root) (uint32_t);
uint32_t (*ctfo_get_vlen) (uint32_t);
ssize_t (*ctfo_get_ctt_size) (const ctf_dict_t *, const ctf_type_t *,
uint32_t (*ctfo_get_prefixed_vlen) (const ctf_type_t *tp);
ssize_t (*ctfo_get_ctt_size) (const ctf_dict_t *, const ctf_type_t *tp,
ssize_t *, ssize_t *);
ssize_t (*ctfo_get_vbytes) (ctf_dict_t *, unsigned short, ssize_t, size_t);
ssize_t (*ctfo_get_vbytes) (ctf_dict_t *, const ctf_type_t *, ssize_t);
} ctf_dictops_t;
typedef struct ctf_list
@@ -593,11 +595,24 @@ extern ctf_id_t ctf_index_to_type (const ctf_dict_t *, uint32_t);
&(ctf_dtd_lookup (fp, ctf_index_to_type (fp, i))->dtd_data) : \
(ctf_type_t *)((uintptr_t)(fp)->ctf_buf + (fp)->ctf_txlate[(i)]))
#define LCTF_INFO_KIND(fp, info) ((fp)->ctf_dictops->ctfo_get_kind(info))
/* The non *INFO variants of these macros acquire the relevant info from the
suffixed type, if the type is prefixed. (Internally to libctf, all types
that may ever take a prefix are prefixed until they are written out, so that
nothing special needs to be done to handle them should their size later
expand past the limit where prefixing is needed.) */
#define LCTF_INFO_UNPREFIXED_KIND(fp, info) ((fp)->ctf_dictops->ctfo_get_kind(info))
#define LCTF_KIND(fp, tp) ((fp)->ctf_dictops->ctfo_get_prefixed_kind(tp))
#define LCTF_INFO_ISROOT(fp, info) ((fp)->ctf_dictops->ctfo_get_root(info))
#define LCTF_INFO_VLEN(fp, info) ((fp)->ctf_dictops->ctfo_get_vlen(info))
#define LCTF_VBYTES(fp, kind, size, vlen) \
((fp)->ctf_dictops->ctfo_get_vbytes(fp, kind, size, vlen))
#define LCTF_VLEN(fp, tp) ((fp)->ctf_dictops->ctfo_get_prefixed_vlen(tp))
#define LCTF_INFO_UNPREFIXED_VLEN(fp, info) ((fp)->ctf_dictops->ctfo_get_vlen(info))
#define LCTF_VBYTES(fp, tp, size) \
((fp)->ctf_dictops->ctfo_get_vbytes (fp, tp, size))
#define LCTF_IS_PREFIXED_KIND(kind) (kind == CTF_K_BIG || kind == CTF_K_CONFLICTING)
#define LCTF_IS_PREFIXED_INFO(info) \
((CTF_INFO_KIND ((info)) == CTF_K_BIG) \
|| (CTF_INFO_KIND ((info)) == CTF_K_CONFLICTING))
#define LCTF_CHILD 0x0001 /* CTF dict is a child. */
#define LCTF_LINKING 0x0002 /* CTF link is underway: respect ctf_link_flags. */
@@ -807,6 +822,9 @@ extern ctf_link_sym_t *ctf_elf32_to_link_sym (ctf_dict_t *fp, ctf_link_sym_t *ds
extern ctf_link_sym_t *ctf_elf64_to_link_sym (ctf_dict_t *fp, ctf_link_sym_t *dst,
const Elf64_Sym *src, uint32_t symidx);
ssize_t get_ctt_size_v2_unconverted (const ctf_dict_t *, const ctf_type_t *,
ssize_t *sizep, ssize_t *incrementp);
/* Variables, all underscore-prepended. */
extern const char _CTF_SECTION[]; /* name of CTF ELF section */

View File

@@ -43,10 +43,16 @@ get_kind_v1 (uint32_t info)
return (CTF_V1_INFO_KIND (info));
}
static uint32_t
get_prefixed_kind_v1 (const ctf_type_t *tp)
{
return (CTF_V1_INFO_KIND (tp->ctt_info));
}
static uint32_t
get_root_v1 (uint32_t info)
{
return (CTF_V1_INFO_ISROOT (info));
return !!(CTF_V1_INFO_ISROOT (info));
}
static uint32_t
@@ -55,16 +61,28 @@ get_vlen_v1 (uint32_t info)
return (CTF_V1_INFO_VLEN (info));
}
static uint32_t
get_prefixed_vlen_v1 (const ctf_type_t *tp)
{
return (CTF_V1_INFO_VLEN (tp->ctt_info));
}
static uint32_t
get_kind_v2 (uint32_t info)
{
return (CTF_V2_INFO_KIND (info));
}
static uint32_t
get_prefixed_kind_v2 (const ctf_type_t *tp)
{
return (CTF_V2_INFO_KIND (tp->ctt_info));
}
static uint32_t
get_root_v2 (uint32_t info)
{
return (CTF_V2_INFO_ISROOT (info));
return !!(CTF_V2_INFO_ISROOT (info));
}
static uint32_t
@@ -73,8 +91,68 @@ get_vlen_v2 (uint32_t info)
return (CTF_V2_INFO_VLEN (info));
}
static uint32_t
get_prefixed_vlen_v2 (const ctf_type_t *tp)
{
return (CTF_V2_INFO_VLEN (tp->ctt_info));
}
static uint32_t
get_kind_v4 (uint32_t info)
{
return (CTF_INFO_KIND (info));
}
static uint32_t
get_prefixed_kind_v4 (const ctf_type_t *tp)
{
/* Resolve away as many prefixes as exist. */
while (LCTF_IS_PREFIXED_INFO (tp->ctt_info))
tp++;
return CTF_INFO_KIND (tp->ctt_info);
}
static uint32_t
get_root_v4 (uint32_t info)
{
return (CTF_INFO_KIND (info) != CTF_K_CONFLICTING);
}
static uint32_t
get_vlen_v4 (uint32_t info)
{
return (CTF_INFO_VLEN (info));
}
static uint32_t
get_prefixed_vlen_v4 (const ctf_type_t *tp)
{
const ctf_type_t *suffix;
/* Resolve away non-BIG prefixes (which have no affect on vlen). */
while (LCTF_IS_PREFIXED_INFO (tp->ctt_info)
&& CTF_INFO_KIND (tp->ctt_info) != CTF_K_BIG)
tp++;
if (!LCTF_IS_PREFIXED_INFO (tp->ctt_info))
return (CTF_INFO_VLEN (tp->ctt_info));
suffix = tp + 1;
/* Special case: CTF_K_FUNC_LINKAGE reuses the vlen field for the linkage: its
vlen is always zero. */
if (CTF_INFO_KIND (suffix->ctt_info) == CTF_K_FUNC_LINKAGE)
return 0;
/* CTF_K_BIG. */
return (CTF_INFO_VLEN (tp->ctt_info) << 16 | CTF_INFO_VLEN (suffix->ctt_info));
}
static inline ssize_t
get_ctt_size_common (const ctf_dict_t *fp _libctf_unused_,
get_ctt_size_old (const ctf_dict_t *fp _libctf_unused_,
const ctf_type_t *tp _libctf_unused_,
ssize_t *sizep, ssize_t *incrementp, size_t lsize,
size_t csize, size_t ctf_type_size,
@@ -107,23 +185,23 @@ get_ctt_size_v1 (const ctf_dict_t *fp, const ctf_type_t *tp,
{
ctf_type_v1_t *t1p = (ctf_type_v1_t *) tp;
return (get_ctt_size_common (fp, tp, sizep, incrementp,
CTF_TYPE_LSIZE (t1p), t1p->ctt_size,
return (get_ctt_size_old (fp, tp, sizep, incrementp,
CTF_V3_TYPE_LSIZE (t1p), t1p->ctt_size,
sizeof (ctf_type_v1_t), sizeof (ctf_stype_v1_t),
CTF_LSIZE_SENT_V1));
}
/* Return the size that a v1 will be once it is converted to v2. */
static ssize_t
ssize_t
get_ctt_size_v2_unconverted (const ctf_dict_t *fp, const ctf_type_t *tp,
ssize_t *sizep, ssize_t *incrementp)
{
ctf_type_v1_t *t1p = (ctf_type_v1_t *) tp;
return (get_ctt_size_common (fp, tp, sizep, incrementp,
CTF_TYPE_LSIZE (t1p), t1p->ctt_size,
sizeof (ctf_type_t), sizeof (ctf_stype_t),
return (get_ctt_size_old (fp, tp, sizep, incrementp,
CTF_V3_TYPE_LSIZE (t1p), t1p->ctt_size,
sizeof (ctf_type_v2_t), sizeof (ctf_stype_v2_t),
CTF_LSIZE_SENT));
}
@@ -131,32 +209,67 @@ static ssize_t
get_ctt_size_v2 (const ctf_dict_t *fp, const ctf_type_t *tp,
ssize_t *sizep, ssize_t *incrementp)
{
return (get_ctt_size_common (fp, tp, sizep, incrementp,
CTF_TYPE_LSIZE (tp), tp->ctt_size,
sizeof (ctf_type_t), sizeof (ctf_stype_t),
ctf_type_v2_t *t2p = (ctf_type_v2_t *) tp;
return (get_ctt_size_old (fp, tp, sizep, incrementp,
CTF_V3_TYPE_LSIZE (t2p), t2p->ctt_size,
sizeof (ctf_type_v2_t), sizeof (ctf_stype_v2_t),
CTF_LSIZE_SENT));
}
static ssize_t
get_vbytes_common (ctf_dict_t *fp, unsigned short kind,
ssize_t size _libctf_unused_, size_t vlen)
get_ctt_size_v4 (const ctf_dict_t *fp _libctf_unused_, const ctf_type_t *tp,
ssize_t *sizep, ssize_t *incrementp)
{
ssize_t size = 0;
/* Figure out how many prefixes there are, and adjust the size appropriately
if we pass a BIG. */
if (incrementp)
*incrementp = 0;
while (LCTF_IS_PREFIXED_INFO (tp->ctt_info))
{
if (CTF_INFO_KIND (tp->ctt_info) == CTF_K_BIG)
size = ((ssize_t) tp->ctt_size) << 32;
if (incrementp)
*incrementp += sizeof (ctf_type_t);
tp++;
}
if (incrementp)
*incrementp += sizeof (ctf_type_t);
size |= tp->ctt_size;
if (sizep)
*sizep = size;
return size;
}
static ssize_t
get_vbytes_old (ctf_dict_t *fp, unsigned short kind, size_t vlen)
{
switch (kind)
{
case CTF_K_INTEGER:
case CTF_K_FLOAT:
case CTF_V3_K_INTEGER:
case CTF_V3_K_FLOAT:
return (sizeof (uint32_t));
case CTF_K_SLICE:
case CTF_V3_K_SLICE:
return (sizeof (ctf_slice_t));
case CTF_K_ENUM:
case CTF_V3_K_ENUM:
return (sizeof (ctf_enum_t) * vlen);
case CTF_K_FORWARD:
case CTF_K_UNKNOWN:
case CTF_K_POINTER:
case CTF_K_TYPEDEF:
case CTF_K_VOLATILE:
case CTF_K_CONST:
case CTF_K_RESTRICT:
case CTF_V3_K_FORWARD:
case CTF_V3_K_UNKNOWN:
case CTF_V3_K_POINTER:
case CTF_V3_K_TYPEDEF:
case CTF_V3_K_VOLATILE:
case CTF_V3_K_CONST:
case CTF_V3_K_RESTRICT:
return 0;
default:
ctf_set_errno (fp, ECTF_CORRUPT);
@@ -166,57 +279,124 @@ get_vbytes_common (ctf_dict_t *fp, unsigned short kind,
}
static ssize_t
get_vbytes_v1 (ctf_dict_t *fp, unsigned short kind, ssize_t size, size_t vlen)
get_vbytes_v1 (ctf_dict_t *fp, const ctf_type_t *tp, ssize_t size)
{
unsigned short kind = CTF_V1_INFO_KIND (tp->ctt_info);
size_t vlen = CTF_V1_INFO_VLEN (tp->ctt_info);
switch (kind)
{
case CTF_K_ARRAY:
case CTF_V3_K_ARRAY:
return (sizeof (ctf_array_v1_t));
case CTF_K_FUNCTION:
case CTF_V3_K_FUNCTION:
return (sizeof (unsigned short) * (vlen + (vlen & 1)));
case CTF_K_STRUCT:
case CTF_K_UNION:
case CTF_V3_K_STRUCT:
case CTF_V3_K_UNION:
if (size < CTF_LSTRUCT_THRESH_V1)
return (sizeof (ctf_member_v1_t) * vlen);
else
return (sizeof (ctf_lmember_v1_t) * vlen);
}
return (get_vbytes_common (fp, kind, size, vlen));
return (get_vbytes_old (fp, kind, vlen));
}
static ssize_t
get_vbytes_v2 (ctf_dict_t *fp, unsigned short kind, ssize_t size, size_t vlen)
get_vbytes_v2 (ctf_dict_t *fp, const ctf_type_t *tp, ssize_t size)
{
unsigned short kind = CTF_V2_INFO_KIND (tp->ctt_info);
size_t vlen = CTF_V2_INFO_VLEN (tp->ctt_info);
switch (kind)
{
case CTF_K_ARRAY:
case CTF_V3_K_ARRAY:
return (sizeof (ctf_array_t));
case CTF_K_FUNCTION:
case CTF_V3_K_FUNCTION:
return (sizeof (uint32_t) * (vlen + (vlen & 1)));
case CTF_K_STRUCT:
case CTF_K_UNION:
case CTF_V3_K_STRUCT:
case CTF_V3_K_UNION:
if (size < CTF_LSTRUCT_THRESH)
return (sizeof (ctf_member_t) * vlen);
return (sizeof (ctf_member_v2_t) * vlen);
else
return (sizeof (ctf_lmember_t) * vlen);
return (sizeof (ctf_lmember_v2_t) * vlen);
}
return (get_vbytes_common (fp, kind, size, vlen));
return (get_vbytes_old (fp, kind, vlen));
}
static ssize_t
get_vbytes_v4 (ctf_dict_t *fp, const ctf_type_t *tp,
ssize_t size _libctf_unused_)
{
unsigned short kind = LCTF_KIND (fp, tp);
ssize_t vlen = LCTF_VLEN (fp, tp);
switch (kind)
{
case CTF_K_INTEGER:
case CTF_K_FLOAT:
return (sizeof (uint32_t));
case CTF_K_SLICE:
return (sizeof (ctf_slice_t));
case CTF_K_ENUM:
return (sizeof (ctf_enum_t) * vlen);
case CTF_K_ENUM64:
return (sizeof (ctf_enum64_t) * vlen);
case CTF_K_ARRAY:
return (sizeof (ctf_array_t));
case CTF_K_STRUCT:
case CTF_K_UNION:
return (sizeof (ctf_member_t) * vlen);
case CTF_K_FUNCTION:
return (sizeof (ctf_param_t) * vlen);
case CTF_K_VAR:
return (sizeof (ctf_linkage_t));
case CTF_K_DATASEC:
return (sizeof (ctf_var_secinfo_t) * vlen);
case CTF_K_DECL_TAG:
return (sizeof (ctf_decl_tag_t));
case CTF_K_TYPE_TAG:
case CTF_K_FORWARD:
case CTF_K_UNKNOWN:
case CTF_K_POINTER:
case CTF_K_TYPEDEF:
case CTF_K_VOLATILE:
case CTF_K_CONST:
case CTF_K_RESTRICT:
case CTF_K_FUNC_LINKAGE:
case CTF_K_BTF_FLOAT:
return 0;
/* These should have been resolved away by LCTF_KIND.
If this somehow didn't work, fail. */
case CTF_K_BIG:
case CTF_K_CONFLICTING:
ctf_set_errno (fp, ECTF_INTERNAL);
ctf_err_warn (fp, 0, 0, _("internal error: prefixed kind seen in get_vbytes_v4: %x"), kind);
return -1;
default:
ctf_set_errno (fp, ECTF_CORRUPT);
ctf_err_warn (fp, 0, 0, _("detected invalid CTF kind: %x"), kind);
return -1;
}
}
static const ctf_dictops_t ctf_dictops[] = {
{NULL, NULL, NULL, NULL, NULL},
{NULL, NULL, NULL, NULL, NULL, NULL, NULL},
/* CTF_VERSION_1 */
{get_kind_v1, get_root_v1, get_vlen_v1, get_ctt_size_v1, get_vbytes_v1},
{get_kind_v1, get_prefixed_kind_v1, get_root_v1, get_vlen_v1,
get_prefixed_vlen_v1, get_ctt_size_v1, get_vbytes_v1},
/* CTF_VERSION_1_UPGRADED_3 */
{get_kind_v2, get_root_v2, get_vlen_v2, get_ctt_size_v2, get_vbytes_v2},
{get_kind_v2, get_prefixed_kind_v2, get_root_v2, get_vlen_v2,
get_prefixed_vlen_v2, get_ctt_size_v2, get_vbytes_v2},
/* CTF_VERSION_2 */
{get_kind_v2, get_root_v2, get_vlen_v2, get_ctt_size_v2, get_vbytes_v2},
{get_kind_v2, get_prefixed_kind_v2, get_root_v2, get_vlen_v2,
get_prefixed_vlen_v2, get_ctt_size_v2, get_vbytes_v2},
/* CTF_VERSION_3, identical to 2: only new type kinds */
{get_kind_v2, get_root_v2, get_vlen_v2, get_ctt_size_v2, get_vbytes_v2},
/* UPTODO: CTF_VERSION_4, identical to 3 at present (but not for long) */
{get_kind_v2, get_root_v2, get_vlen_v2, get_ctt_size_v2, get_vbytes_v2},
{get_kind_v2, get_prefixed_kind_v2, get_root_v2, get_vlen_v2,
get_prefixed_vlen_v2, get_ctt_size_v2, get_vbytes_v2},
/* CTF_VERSION_4, always BTF-compatible. */
{get_kind_v4, get_prefixed_kind_v4, get_root_v4, get_vlen_v4,
get_prefixed_vlen_v4, get_ctt_size_v4, get_vbytes_v4},
};
/* Initialize the symtab translation table as appropriate for its indexing