tbuf = (ctf_type_v1_t *) (fp->ctf_buf + cth->cth_typeoff);
tend = (ctf_type_v1_t *) (fp->ctf_buf + cth->cth_stroff);
- /* Much like init_static_types(), this is a two-pass process.
+ /* This is a two-pass process.
First, figure out the new type-section size needed. (It is possible,
in theory, for it to be less than the old size, but this is very
}
static int
-init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth,
- ctf_dynset_t *all_enums);
+init_static_types_names (ctf_dict_t *fp, ctf_header_t *cth);
/* Populate statically-defined types (those loaded from a saved buffer).
static int
init_static_types (ctf_dict_t *fp, ctf_header_t *cth)
-{
- ctf_dynset_t *all_enums;
- int err;
-
- if ((all_enums = ctf_dynset_create (htab_hash_pointer, htab_eq_pointer,
- NULL)) == NULL)
- return ENOMEM;
-
- err = init_static_types_internal (fp, cth, all_enums);
- ctf_dynset_destroy (all_enums);
- return err;
-}
-
-static int
-init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth,
- ctf_dynset_t *all_enums)
{
const ctf_type_t *tbuf;
const ctf_type_t *tend;
unsigned long pop[CTF_K_MAX + 1] = { 0 };
int pop_enumerators = 0;
const ctf_type_t *tp;
- uint32_t id;
- uint32_t *xp;
unsigned long typemax = 0;
- ctf_next_t *i = NULL;
- void *k;
/* We determine whether the dict is a child or a parent based on the value of
cth_parname. */
int child = cth->cth_parname != 0;
- int nlstructs = 0, nlunions = 0;
- int err;
if (fp->ctf_version < CTF_VERSION_4)
{
tend = (ctf_type_t *) (fp->ctf_buf + cth->cth_stroff);
/* We make two passes through the entire type section, and one third pass
- through part of it. In this first pass, we count the number of each type
- and type-like identifier (like enumerators) and the total number of
- types. */
+ through part of it: but only the first is guaranteed to happen at this
+ stage. The second and third passes require working string lookup, so in
+ child dicts can only happen at ctf_import time.
+
+ In this first pass, we count the number of each type and type-like
+ identifier (like enumerators) and the total number of types. */
for (tp = tbuf; tp < tend; typemax++)
{
because later-added types will call grow_ptrtab() automatically, as
needed. */
- fp->ctf_txlate = malloc (sizeof (uint32_t) * (typemax + 1));
+ fp->ctf_txlate = calloc (typemax + 1, sizeof (uint32_t));
+ fp->ctf_ptrtab = calloc (typemax + 1, sizeof (uint32_t));
fp->ctf_ptrtab_len = typemax + 1;
- fp->ctf_ptrtab = malloc (sizeof (uint32_t) * fp->ctf_ptrtab_len);
fp->ctf_stypes = typemax;
+ fp->ctf_typemax = typemax;
if (fp->ctf_txlate == NULL || fp->ctf_ptrtab == NULL)
return ENOMEM; /* Memory allocation failed. */
+ /* UPTODO: BTF is like v4 here: no string lookups in children, which blocks
+ almost all operations until after ctf_import. */
+ if (child && cth->cth_parent_strlen != 0)
+ {
+ fp->ctf_flags |= LCTF_NO_STR;
+ return 0;
+ }
+
+ ctf_dprintf ("%lu types initialized (other than names)\n", fp->ctf_typemax);
+
+ return init_static_types_names (fp, cth);
+}
+
+static int
+init_static_types_names_internal (ctf_dict_t *fp, ctf_header_t *cth,
+ ctf_dynset_t *all_enums);
+
+/* A wrapper to simplify memory allocation. */
+
+static int
+init_static_types_names (ctf_dict_t *fp, ctf_header_t *cth)
+{
+ ctf_dynset_t *all_enums;
+ int err;
+
+ if ((all_enums = ctf_dynset_create (htab_hash_pointer, htab_eq_pointer,
+ NULL)) == NULL)
+ return ENOMEM;
+
+ err = init_static_types_names_internal (fp, cth, all_enums);
+ ctf_dynset_destroy (all_enums);
+ return err;
+}
+
+/* Initialize the parts of the CTF dict whose initialization depends on name
+ lookup. This happens at open time except for child dicts, when (for CTFv4+
+ dicts) it happens at ctf_import time instead, because before then the strtab
+ is truncated at the start.
+
+ As a function largely called at open time, this function does not reliably
+ set the ctf_errno, but instead *returns* the error code. */
+
+static int
+init_static_types_names_internal (ctf_dict_t *fp, ctf_header_t *cth,
+ ctf_dynset_t *all_enums)
+{
+ const ctf_type_t *tbuf;
+ const ctf_type_t *tend;
+
+ const ctf_type_t *tp;
+ uint32_t id;
+ uint32_t *xp;
+
+ unsigned long typemax = fp->ctf_typemax;
+
+ ctf_next_t *i = NULL;
+ void *k;
+ int err;
+
+ int child = cth->cth_parname != 0;
+ int nlstructs = 0, nlunions = 0;
+
+ tbuf = (ctf_type_t *) (fp->ctf_buf + cth->cth_typeoff);
+ tend = (ctf_type_t *) (fp->ctf_buf + cth->cth_stroff);
+
+ assert (!(fp->ctf_flags & LCTF_NO_STR));
+
xp = fp->ctf_txlate;
*xp++ = 0; /* Type id 0 is used as a sentinel value. */
- memset (fp->ctf_txlate, 0, sizeof (uint32_t) * (typemax + 1));
- memset (fp->ctf_ptrtab, 0, sizeof (uint32_t) * (typemax + 1));
-
- /* In the second pass through the types, we fill in each entry of the
- type and pointer tables and add names to the appropriate hashes.
+ /* In this second pass through the types, we fill in each entry of the type
+ and pointer tables and add names to the appropriate hashes.
(Not all names are added in this pass, only type names. See below.)
- Bump ctf_typemax as we go, but keep it one higher than normal, so that
- the type being read in is considered a valid type and it is at least
- barely possible to run simple lookups on it. */
+ Reset ctf_typemax and bump it as we go, but keep it one higher than normal,
+ so that the type being read in is considered a valid type and it is at
+ least barely possible to run simple lookups on it: but higher types are
+ not, since their names are not yet known. (It is kept at its standard
+ value before this function is called so that at least some type-related
+ operations work. */
for (id = 1, fp->ctf_typemax = 1, tp = tbuf; tp < tend; xp++, id++, fp->ctf_typemax++)
{
ctf_set_version (fp, hp, hp->cth_version);
- /* Temporary assignment, just enough to be able to initialize
- the atoms table. */
+ /* Temporary assignment, just enough to be able to initialize the atoms table.
+ This does not guarantee that we can look up strings: in v4, child dicts
+ cannot reliably look up strings until after ctf_import. */
fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *) fp->ctf_buf
+ hp->cth_stroff;
free (fp->ctf_txlate);
free (fp->ctf_ptrtab);
free (fp->ctf_pptrtab);
+ free (fp->ctf_serializing_buf);
free (fp->ctf_header);
free (fp);
return 0;
}
-/* Import the types from the specified parent dict by storing a pointer to it in
- ctf_parent and incrementing its reference count. Only one parent is allowed:
- if a parent already exists, it is replaced by the new parent. The pptrtab
- is wiped, and will be refreshed by the next ctf_lookup_by_name call. */
-int
-ctf_import (ctf_dict_t *fp, ctf_dict_t *pfp)
+static int
+ctf_import_internal (ctf_dict_t *fp, ctf_dict_t *pfp, int unreffed)
{
- if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0))
+ int err;
+ int no_strings = fp->ctf_flags & LCTF_NO_STR;
+ int old_flags = fp->ctf_flags;
+ ctf_dict_t *old_parent = fp->ctf_parent;
+ const char *old_parname = fp->ctf_parname;
+ int old_unreffed = fp->ctf_parent_unreffed;
+
+ if (pfp == NULL || pfp == fp->ctf_parent)
+ return 0;
+
+ if (fp == NULL || fp == pfp || pfp->ctf_refcnt == 0)
return (ctf_set_errno (fp, EINVAL));
- if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel)
+ if (pfp->ctf_dmodel != fp->ctf_dmodel)
return (ctf_set_errno (fp, ECTF_DMODEL));
- if (fp->ctf_parent && !fp->ctf_parent_unreffed)
- ctf_dict_close (fp->ctf_parent);
- fp->ctf_parent = NULL;
+ if (fp->ctf_parent && fp->ctf_header->cth_parent_strlen != 0)
+ return (ctf_set_errno (fp, ECTF_HASPARENT));
+ if (fp->ctf_header->cth_parent_strlen != 0 &&
+ pfp->ctf_header->cth_strlen != fp->ctf_header->cth_parent_strlen)
+ {
+ ctf_err_warn (fp, 0, ECTF_WRONGPARENT,
+ _("ctf_import: incorrect parent dict: %u bytes of strings expected, %u found"),
+ fp->ctf_header->cth_parent_strlen, pfp->ctf_header->cth_strlen);
+ return (ctf_set_errno (fp, ECTF_WRONGPARENT));
+ }
+
+ fp->ctf_parent = NULL;
free (fp->ctf_pptrtab);
fp->ctf_pptrtab = NULL;
fp->ctf_pptrtab_len = 0;
fp->ctf_pptrtab_typemax = 0;
- if (pfp != NULL)
+ if (fp->ctf_parname == NULL)
+ if ((err = ctf_parent_name_set (fp, "PARENT")) < 0)
+ return err; /* errno is set for us. */
+
+ if (!unreffed)
+ pfp->ctf_refcnt++;
+
+ fp->ctf_parent_unreffed = unreffed;
+ fp->ctf_parent = pfp;
+
+ /* If this is a dict that hasn't previously allowed string lookups,
+ we can allow them now, and finish initialization. */
+
+ fp->ctf_flags |= LCTF_CHILD;
+ fp->ctf_flags &= ~LCTF_NO_STR;
+
+ if (no_strings && (err = init_static_types_names (fp, fp->ctf_header)) < 0)
{
- int err;
+ /* Undo everything other than cache flushing. */
- if (fp->ctf_parname == NULL)
- if ((err = ctf_parent_name_set (fp, "PARENT")) < 0)
- return err;
+ fp->ctf_flags = old_flags;
+ fp->ctf_parent_unreffed = old_unreffed;
+ fp->ctf_parent = old_parent;
+ fp->ctf_parname = old_parname;
- fp->ctf_flags |= LCTF_CHILD;
- pfp->ctf_refcnt++;
- fp->ctf_parent_unreffed = 0;
+ if (fp->ctf_parent_unreffed)
+ old_parent->ctf_refcnt++;
+
+ return (ctf_set_errno (fp, err)); /* errno is set for us. */
}
- fp->ctf_parent = pfp;
+ /* Failure can now no longer happen, so we can close the old parent (which may
+ deallocate it and is not easily reversible). */
+
+ if (old_parent && !old_unreffed)
+ ctf_dict_close (old_parent);
+
+ fp->ctf_parent->ctf_max_children++;
return 0;
}
+/* Import the types from the specified parent dict by storing a pointer to it in
+ ctf_parent and incrementing its reference count. You can only call this
+ function once on serialized dicts: the parent cannot be replaced once set.
+ (You can call it on unserialized dicts as often as you like.)
+
+ The pptrtab is wiped, and will be refreshed by the next ctf_lookup_by_name
+ call.
+
+ You can call this with a parent of NULL as many times as you like (but
+ it doesn't do much). */
+int
+ctf_import (ctf_dict_t *fp, ctf_dict_t *pfp)
+{
+ return ctf_import_internal (fp, pfp, 0);
+}
+
/* Like ctf_import, but does not increment the refcount on the imported parent
or close it at any point: as a result it can go away at any time and the
caller must do all freeing itself. Used internally to avoid refcount
int
ctf_import_unref (ctf_dict_t *fp, ctf_dict_t *pfp)
{
- if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0))
- return (ctf_set_errno (fp, EINVAL));
-
- if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel)
- return (ctf_set_errno (fp, ECTF_DMODEL));
-
- if (fp->ctf_parent && !fp->ctf_parent_unreffed)
- ctf_dict_close (fp->ctf_parent);
- fp->ctf_parent = NULL;
-
- free (fp->ctf_pptrtab);
- fp->ctf_pptrtab = NULL;
- fp->ctf_pptrtab_len = 0;
- fp->ctf_pptrtab_typemax = 0;
- if (pfp != NULL)
- {
- int err;
-
- if (fp->ctf_parname == NULL)
- if ((err = ctf_parent_name_set (fp, "PARENT")) < 0)
- return err;
-
- fp->ctf_flags |= LCTF_CHILD;
- fp->ctf_parent_unreffed = 1;
- }
-
- fp->ctf_parent = pfp;
- return 0;
+ return ctf_import_internal (fp, pfp, 1);
}
/* Set the data model constant for the CTF dict. */
/* Overall serialization. */
-/* Emit a new CTF dict which is a serialized copy of this one: also reify
- the string table and update all offsets in the current dict suitably.
- (This simplifies ctf-string.c a little, at the cost of storing a second
- copy of the strtab if this dict was originally read in via ctf_open.)
-
- Other aspects of the existing dict are unchanged, although some
- static entries may be duplicated in the dynamic state (which should
- have no effect on visible operation). */
+/* Do all aspects of serialization up to strtab writeout and variable table
+ sorting. The resulting dict will have the LCTF_PRESERIALIZED flag on and
+ must not be modified in any way before serialization. (This is not enforced,
+ as this feature is internal-only, employed by the linker machinery.) */
-static unsigned char *
-ctf_serialize (ctf_dict_t *fp, size_t *bufsiz)
+int
+ctf_preserialize (ctf_dict_t *fp)
{
ctf_header_t hdr, *hdrp;
ctf_dvdef_t *dvd;
- ctf_varent_t *dvarents;
- const ctf_strs_writable_t *strtab;
int sym_functions = 0;
unsigned char *t;
size_t buf_size, type_size, objt_size, func_size;
size_t funcidx_size, objtidx_size;
size_t nvars;
- unsigned char *buf = NULL, *newbuf;
+ unsigned char *buf = NULL;
emit_symtypetab_state_t symstate;
memset (&symstate, 0, sizeof (emit_symtypetab_state_t));
- /* Stop unstable file formats (subject to change) getting out into the
- wild. */
-#if CTF_VERSION != CTF_STABLE_VERSION
- if (!getenv ("I_KNOW_LIBCTF_IS_UNSTABLE"))
- {
- ctf_set_errno (fp, ECTF_UNSTABLE);
- return NULL;
- }
-#endif
+ if (fp->ctf_flags & LCTF_NO_STR)
+ return (ctf_set_errno (fp, ECTF_NOPARENT));
/* Prohibit reserialization of dicts for which we have dynamic state inherited
from the upgrade process which we cannot record in the dict. Right now,
offset to v2 and higher, and nowhere to record this in CTFv4. */
if (fp->ctf_flags & LCTF_NO_SERIALIZE)
- {
- ctf_set_errno (fp, ECTF_CTFVERS_NO_SERIALIZE);
- return NULL;
- }
+ return (ctf_set_errno (fp, ECTF_CTFVERS_NO_SERIALIZE));
/* Fill in an initial CTF header. The type section begins at a 4-byte aligned
boundary past the CTF header itself (at relative offset zero). The flag
sym_functions)) != CTF_ERR)
if ((ctf_add_funcobjt_sym_forced (fp, sym_functions, sym_name, sym)) < 0)
if (ctf_errno (fp) != ECTF_DUPLICATE)
- return NULL; /* errno is set for us. */
+ return -1; /* errno is set for us. */
if (ctf_errno (fp) != ECTF_NEXT_END)
- return NULL; /* errno is set for us. */
+ return -1; /* errno is set for us. */
} while (sym_functions++ < 1);
/* Figure out how big the symtypetabs are now. */
if (ctf_symtypetab_sect_sizes (fp, &symstate, &hdr, &objt_size, &func_size,
&objtidx_size, &funcidx_size) < 0)
- return NULL; /* errno is set for us. */
+ return -1; /* errno is set for us. */
/* Propagate all vars into the dynamic state, so we can put them back later.
Variables already in the dynamic state, likely due to repeated
if (name != NULL && !ctf_dvd_lookup (fp, name))
if (ctf_add_variable_forced (fp, name, fp->ctf_vars[i].ctv_type) < 0)
- return NULL; /* errno is set for us. */
+ return -1; /* errno is set for us. */
}
for (nvars = 0, dvd = ctf_list_next (&fp->ctf_dvdefs);
hdr.cth_typeoff = hdr.cth_varoff + (nvars * sizeof (ctf_varent_t));
hdr.cth_stroff = hdr.cth_typeoff + type_size;
hdr.cth_strlen = 0;
+ hdr.cth_parent_strlen = 0;
buf_size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen;
if ((buf = malloc (buf_size)) == NULL)
- {
- ctf_set_errno (fp, EAGAIN);
- return NULL;
- }
+ return (ctf_set_errno (fp, EAGAIN));
memcpy (buf, &hdr, sizeof (ctf_header_t));
t = (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_objtoff;
if (ctf_emit_symtypetab_sects (fp, &symstate, &t, objt_size, func_size,
objtidx_size, funcidx_size) < 0)
- goto err;
+ {
+ free (buf);
+ return -1; /* errno is set for us. */
+ }
assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_varoff);
/* Work over the variable list, translating everything into ctf_varent_t's and
prepping the string table. */
- dvarents = (ctf_varent_t *) t;
+ fp->ctf_serializing_vars = (ctf_varent_t *) t;
for (i = 0, dvd = ctf_list_next (&fp->ctf_dvdefs); dvd != NULL;
dvd = ctf_list_next (dvd), i++)
{
- ctf_varent_t *var = &dvarents[i];
+ ctf_varent_t *var = &fp->ctf_serializing_vars[i];
ctf_str_add_ref (fp, dvd->dvd_name, &var->ctv_name);
var->ctv_type = (uint32_t) dvd->dvd_type;
assert (i == nvars);
t += sizeof (ctf_varent_t) * nvars;
+ fp->ctf_serializing_nvars = nvars;
assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_typeoff);
assert (t == (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_stroff);
+ fp->ctf_serializing_buf = buf;
+ fp->ctf_serializing_buf_size = buf_size;
+
+ /* Prohibit additions and the like from this point on. */
+ fp->ctf_flags |= LCTF_NO_STR;
+
+ return 0;
+}
+
+/* Undo preserialization (called on error). */
+void
+ctf_depreserialize (ctf_dict_t *fp)
+{
+ fp->ctf_flags &= ~LCTF_NO_STR;
+ free (fp->ctf_serializing_buf);
+ fp->ctf_serializing_buf = NULL;
+ fp->ctf_serializing_vars = NULL;
+ fp->ctf_serializing_buf_size = 0;
+ fp->ctf_serializing_nvars = 0;
+}
+
+/* Emit a new CTF dict which is a serialized copy of this one: also reify
+ the string table and update all offsets in the current dict suitably.
+ (This simplifies ctf-string.c a little, at the cost of storing a second
+ copy of the strtab if this dict was originally read in via ctf_open.)
+
+ Other aspects of the existing dict are unchanged, although some
+ static entries may be duplicated in the dynamic state (which should
+ have no effect on visible operation). */
+
+static unsigned char *
+ctf_serialize (ctf_dict_t *fp, size_t *bufsiz)
+{
+ const ctf_strs_writable_t *strtab;
+ unsigned char *buf, *newbuf;
+ ctf_header_t *hdrp;
+
+ /* Stop unstable file formats (subject to change) getting out into the
+ wild. */
+#if CTF_VERSION != CTF_STABLE_VERSION
+ if (!getenv ("I_KNOW_LIBCTF_IS_UNSTABLE"))
+ {
+ ctf_depreserialize (fp);
+ ctf_set_errno (fp, ECTF_UNSTABLE);
+ return NULL;
+ }
+#endif
+
+ /* Preserialize, if we need to. */
+
+ if (!fp->ctf_serializing_buf)
+ if (ctf_preserialize (fp) < 0)
+ return NULL; /* errno is set for us. */
+
+ /* UPTODO: prevent writing of BTF dicts when upgrading from CTFv3. */
+
+ /* Allow string lookup again, now we need it to sort the vartab. */
+ fp->ctf_flags &= ~LCTF_NO_STR;
+
/* Construct the final string table and fill out all the string refs with the
final offsets. At link time, before the strtab can be constructed, child
dicts also need their cth_parent_strlen header field updated to match the
if ((fp->ctf_flags & LCTF_LINKING) && fp->ctf_parent)
fp->ctf_header->cth_parent_strlen = fp->ctf_parent->ctf_str[CTF_STRTAB_0].cts_len;
+ hdrp = (ctf_header_t *) fp->ctf_serializing_buf;
+
strtab = ctf_str_write_strtab (fp);
if (strtab == NULL)
- goto oom;
+ goto err;
/* Now the string table is constructed, we can sort the buffer of
ctf_varent_t's. */
ctf_sort_var_arg_cb_t sort_var_arg = { fp, (ctf_strs_t *) strtab };
- ctf_qsort_r (dvarents, nvars, sizeof (ctf_varent_t), ctf_sort_var,
- &sort_var_arg);
+ ctf_qsort_r (fp->ctf_serializing_vars, fp->ctf_serializing_nvars,
+ sizeof (ctf_varent_t), ctf_sort_var, &sort_var_arg);
- if ((newbuf = realloc (buf, buf_size + strtab->cts_len)) == NULL)
+ if ((newbuf = realloc (fp->ctf_serializing_buf, fp->ctf_serializing_buf_size
+ + strtab->cts_len)) == NULL)
goto oom;
- buf = newbuf;
- memcpy (buf + buf_size, strtab->cts_strs, strtab->cts_len);
- hdrp = (ctf_header_t *) buf;
+ fp->ctf_serializing_buf = newbuf;
+ memcpy (fp->ctf_serializing_buf + fp->ctf_serializing_buf_size, strtab->cts_strs,
+ strtab->cts_len);
+ hdrp = (ctf_header_t *) fp->ctf_serializing_buf;
hdrp->cth_strlen = strtab->cts_len;
- buf_size += hdrp->cth_strlen;
- *bufsiz = buf_size;
hdrp->cth_parent_strlen = fp->ctf_header->cth_parent_strlen;
+ fp->ctf_serializing_buf_size += hdrp->cth_strlen;
+ *bufsiz = fp->ctf_serializing_buf_size;
+
+ buf = fp->ctf_serializing_buf;
+
+ fp->ctf_serializing_buf = NULL;
+ fp->ctf_serializing_vars = NULL;
+ fp->ctf_serializing_buf_size = 0;
+ fp->ctf_serializing_nvars = 0;
return buf;
oom:
ctf_set_errno (fp, EAGAIN);
err:
- free (buf);
+ ctf_depreserialize (fp);
return NULL; /* errno is set for us. */
}