libctf: open, types: ctf_import for BTF

ctf_import needs a bunch of fixes to work with pure BTF dicts -- and, for
that matter, importing newly-created parent dicts that have never been
written out, which may have a bunch of nonprovisional types (if types were
added to it before any imports were done) or may not (if at least one
ctf_import into it was done before any types were added).

So we adjust things so that the values that are checked against are the
nonprovisional-types values: the header revisions actually changed the name
of cth_parent_typemax to cth_parent_ntypes to make this clearer, so catch up
with that.  In the parent, we have to use ctf_idmax, not ctf_typemax.

One thing we must prohibit is that you cannot add a bunch of types to a
child and then import a parent into it: the type IDs will all be wrong
and the string offsets more so.  This was partly prohibited: prohibit it
entirely (excepting only that the not-actually-written-out void type
we might add to new BTF dicts does not influence this check).

Since BTF children don't have a cth_parent_ntypes or a cth_parent_strlen, we
cannot check this stuff, but just set them and hope.
This commit is contained in:
Nick Alcock
2025-04-25 17:59:31 +01:00
parent d5012389a4
commit 908a7e7167
2 changed files with 40 additions and 16 deletions

View File

@@ -2641,20 +2641,30 @@ ctf_import_internal (ctf_dict_t *fp, ctf_dict_t *pfp, int unreffed)
number of types. (Provisional types excepted: they go at the top of the
type ID space, and will not overlap any child types.) */
if (pfp->ctf_idmax != fp->ctf_header->cth_parent_typemax)
if (pfp->ctf_idmax != fp->ctf_header->cth_parent_ntypes)
{
if (fp->ctf_header->cth_parent_typemax != 0)
if (fp->ctf_header->cth_parent_ntypes != 0)
{
ctf_err_warn (fp, 0, ECTF_WRONGPARENT,
_("ctf_import: incorrect parent dict: %u types expected, %u found"),
fp->ctf_header->cth_parent_typemax, pfp->ctf_idmax);
fp->ctf_header->cth_parent_ntypes, pfp->ctf_idmax);
return (ctf_set_errno (fp, ECTF_WRONGPARENT));
}
else if (fp->ctf_header->cth_parent_typemax == 0)
else if (fp->ctf_opened_btf)
{
/* A pure BTF dict does not track the number of types in the parent:
just update and hope. */
fp->ctf_header->cth_parent_ntypes = pfp->ctf_idmax;
}
else if (fp->ctf_header->cth_parent_ntypes == 0)
{
/* If we are importing into a parent dict, the child dict had better
be empty. Set its starting type ID, which need not be zero: the
parent can already have types. */
parent can already have types. We assign typemax rather than
idmax because when this is a new dict we want the types to count
up from the number of types currently in the parent, not the number
in the parent when it was opened. */
if (fp->ctf_typemax != 0)
{
@@ -2663,17 +2673,23 @@ ctf_import_internal (ctf_dict_t *fp, ctf_dict_t *pfp, int unreffed)
fp->ctf_typemax);
return (ctf_set_errno (fp, EINVAL));
}
fp->ctf_header->cth_parent_typemax = pfp->ctf_typemax;
fp->ctf_header->cth_parent_ntypes = pfp->ctf_typemax;
}
}
/* We might in time be able to lift this restriction, but it is unlikely to be
something anyone would want to do, so let's not bother for now. */
/* No importing dicts with provisional strings in (except for the void one
added to all new dicts). We might in time be able to lift this
restriction, but it is unlikely to be something anyone would want to do, so
let's not bother for now. If we have a system-created void type, it might
have instantiated a new string. */
if (ctf_dynhash_elements (fp->ctf_prov_strtab) != 0)
if ((fp->ctf_void_type == NULL
&& ctf_dynhash_elements (fp->ctf_prov_strtab) != 0)
|| (fp->ctf_void_type != NULL
&& ctf_dynhash_elements (fp->ctf_prov_strtab) > 1))
{
ctf_err_warn (fp, 0, EINVAL,
_("ctf_import: child dict already has %zi bytes of strings, cannot import"),
_("ctf_import: child dict already has %zi strings, cannot import"),
ctf_dynhash_elements (fp->ctf_prov_strtab));
return (ctf_set_errno (fp, EINVAL));
}
@@ -2694,8 +2710,16 @@ ctf_import_internal (ctf_dict_t *fp, ctf_dict_t *pfp, int unreffed)
fp->ctf_parent_unreffed = unreffed;
fp->ctf_parent = pfp;
/* BTF dicts don't have any parent strlen in the header, but we need to know
it to dereference strings. */
if (fp->ctf_opened_btf)
fp->ctf_header->cth_parent_strlen = pfp->ctf_str[CTF_STRTAB_0].cts_len;
/* If this is a dict that hasn't previously allowed string lookups,
we can allow them now, and finish initialization. */
we can allow them now, and finish initialization. (This requires us to
figure out whether the buffer contains pure BTF or not: we can do that by
checking the CTF-specific magic number in the header we read in.) */
fp->ctf_flags |= LCTF_CHILD;
fp->ctf_flags &= ~LCTF_NO_STR;