libctf, create, types: type and decl tags

These are a little more fiddly than previous kinds, because their
namespacing rules are odd: they have names (so presumably we want an API to
look them up by name), but the names are not unique (they don't need to be,
because they are not entities you can refer to from C), so many distinct
tags in the same TU can have the same name.  Type tags only refer to a type
ID: decl tags refer to a specific function parameter or structure member via
a zero-indexed "component index".

The name tables for these things are a hash of name to a set of type IDs;
rather different from all the other named entities in libctf.  As a
consequence, they can presently be looked up only using their own dedicated
functions, not using ctf_lookup_by_name et al.  (It's not clear if this
restriction could ever be lifted: ctf_lookup_by_name and friends return a
type ID, not a set of them.)

They are similar enough to each other that we can at least have one function
to look up both type and decl tags if you don't care about their
component_idx and only want a type ID: ctf_tag.  (And one to iterate over
them, ctf_tag_next).

(A caveat: because tags aren't widely used or generated yet, much of this is
more or less untested and/or supposition and will need testing later.)

New API, more or less the minimum needed because it's not entirely clear how
these things will be used:

+ctf_id_t ctf_tag (ctf_dict_t *, ctf_id_t tag);
+ctf_id_t ctf_decl_tag (ctf_dict_t *, ctf_id_t decl_tag,
+		       int64_t *component_idx);
+ctf_id_t ctf_tag_next (ctf_dict_t *, const char *tag, ctf_next_t **);
+ctf_id_t ctf_add_type_tag (ctf_dict_t *, uint32_t, ctf_id_t, const char *);
+ctf_id_t ctf_add_decl_type_tag (ctf_dict_t *, uint32_t, ctf_id_t, const char *);
+ctf_id_t ctf_add_decl_tag (ctf_dict_t *, uint32_t, ctf_id_t, const char *,
+			   int component_idx);
This commit is contained in:
Nick Alcock
2025-04-25 11:14:09 +01:00
parent 39cdb3e395
commit fb8917ac21
6 changed files with 369 additions and 8 deletions

View File

@@ -853,6 +853,45 @@ ctf_datasec_var_next (ctf_dict_t *fp, ctf_id_t datasec, ctf_next_t **it,
return (ctf_set_typed_errno (fp, ECTF_NEXT_END));
}
/* Iterate over all tags with the given TAG, returning the ID of each tag. */
ctf_id_t
ctf_tag_next (ctf_dict_t *fp, const char *tag, ctf_next_t **it)
{
ctf_next_t *i = *it;
int err;
void *type;
if (!i)
{
if ((i = ctf_next_create ()) == NULL)
return (ctf_set_typed_errno (fp, ENOMEM));
i->cu.ctn_fp = fp;
i->ctn_iter_fun = (void (*) (void)) ctf_tag_next;
i->cu.ctn_s = ctf_dynhash_lookup (fp->ctf_tags, tag);
*it = i;
}
if ((void (*) (void)) ctf_tag_next != i->ctn_iter_fun)
return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFUN));
if (fp != i->cu.ctn_fp)
return (ctf_set_typed_errno (fp, ECTF_NEXT_WRONGFP));
err = ctf_dynset_next (i->cu.ctn_s, &i->ctn_next, &type);
if (err != 0 && err != ECTF_NEXT_END)
return ctf_set_typed_errno (fp, err);
if (err == 0)
return (ctf_id_t) (uintptr_t) type;
ctf_next_destroy (i);
*it = NULL;
return (ctf_set_typed_errno (fp, ECTF_NEXT_END));
}
/* Follow a given type through the graph for TYPEDEF, VOLATILE, CONST, and
RESTRICT nodes until we reach a "base" type node. This is useful when
we want to follow a type ID to a node that has members or a size. To guard
@@ -1149,6 +1188,14 @@ ctf_type_aname (ctf_dict_t *fp, ctf_id_t type)
case CTF_K_ENUM64:
ctf_decl_sprintf (&cd, "enum %s", name);
break;
case CTF_K_TYPE_TAG:
ctf_decl_sprintf (&cd, "%s", name);
break;
/* UPTODO: decl tags... I guess we print them when we print the
associated variable, somehow? For now, just this... */
case CTF_K_DECL_TAG:
ctf_decl_sprintf (&cd, "btf_decl_tag (\"%s\")", name);
break;
case CTF_K_FUNC_LINKAGE:
case CTF_K_VAR:
{
@@ -1603,6 +1650,8 @@ ctf_type_reference (ctf_dict_t *fp, ctf_id_t type)
case CTF_K_VOLATILE:
case CTF_K_CONST:
case CTF_K_RESTRICT:
case CTF_K_TYPE_TAG:
case CTF_K_DECL_TAG:
case CTF_K_FUNCTION:
case CTF_K_FUNC_LINKAGE:
case CTF_K_VAR:
@@ -1630,6 +1679,119 @@ ctf_type_reference (ctf_dict_t *fp, ctf_id_t type)
}
}
/* Return the component ID and declaration to which a decl tag is attached. */
ctf_id_t
ctf_decl_tag (ctf_dict_t *fp, ctf_id_t decl_tag, int64_t *component_idx)
{
ctf_dict_t *ofp = fp;
const ctf_type_t *tp, *suffix;
unsigned char *vlen;
ctf_decl_tag_t *cdt;
if ((tp = ctf_lookup_by_id (&fp, decl_tag, &suffix)) == NULL)
return CTF_ERR; /* errno is set for us. */
if (LCTF_KIND (fp, tp) != CTF_K_DECL_TAG)
return (ctf_set_typed_errno (ofp, ECTF_NOTDECLTAG));
vlen = ctf_vlen (fp, decl_tag, tp, NULL);
cdt = (ctf_decl_tag_t *) vlen;
*component_idx = cdt->cdt_component_idx;
return suffix->ctt_type;
}
/* Return the type ID of the type to which a given type tag is attached, or of
the type of the declaration to which a decl tag is attached (so a decl tag on
a function parameter would return the type ID of the parameter's type). */
ctf_id_t
ctf_tag (ctf_dict_t *fp, ctf_id_t tag)
{
int kind = ctf_type_kind (fp, tag);
int64_t component_idx;
ctf_id_t ref;
if (kind != CTF_K_TYPE_TAG && kind != CTF_K_DECL_TAG)
return (ctf_set_typed_errno (fp, ECTF_NOTTAG));
if ((ref = ctf_type_reference (fp, tag)) == CTF_ERR)
return CTF_ERR; /* errno is set for us. */
if (kind == CTF_K_TYPE_TAG)
return ref;
if (ctf_decl_tag (fp, tag, &component_idx) == CTF_ERR)
return CTF_ERR; /* errno is set for us. */
if (component_idx == -1)
return ref;
/* See ctf_add_tag. */
switch (ctf_type_kind (fp, ref))
{
case CTF_K_STRUCT:
case CTF_K_UNION:
{
ctf_next_t *i = NULL;
int64_t j = 0;
ctf_id_t type;
while (ctf_member_next (fp, ref, &i, NULL, &type, NULL, 0) >= 0)
{
if (j++ == component_idx)
{
ctf_next_destroy (i);
return type;
}
}
if (ctf_errno (fp) != ECTF_NEXT_END)
{
ctf_next_destroy (i);
return CTF_ERR; /* errno is set for us. */
}
}
break;
case CTF_K_FUNC_LINKAGE:
case CTF_K_FUNCTION:
{
ctf_funcinfo_t fi;
ctf_id_t *args;
ctf_id_t argtype;
if (ctf_func_type_info (fp, ref, &fi) < 0)
return CTF_ERR; /* errno is set for us. */
if (component_idx + 1 > (ssize_t) fi.ctc_argc)
break;
if ((args = malloc ((component_idx + 1) * sizeof (ctf_id_t))) == NULL)
return (ctf_set_typed_errno (fp, ENOMEM));
if (ctf_func_type_args (fp, ref, component_idx + 1, args))
{
free (args);
return CTF_ERR; /* errno is set for us. */
}
argtype = args[component_idx];
free (args);
return argtype;
}
default:
return CTF_ERR; /* errno is set for us. */
}
ctf_err_warn (fp, 0, ECTF_NOTREF, _("decl tag %lx refers to type %lx, "
"component %" PRIi64 ", which does not exist"),
tag, (long) ref, component_idx);
return (ctf_set_typed_errno (fp, ECTF_NOTREF));
}
/* Find a pointer to type by looking in fp->ctf_ptrtab and fp->ctf_pptrtab. If
we can't find a pointer to the given type, see if we can compute a pointer to
the type resulting from resolving the type down to its base type and use that