From: Nick Alcock Date: Mon, 15 Jul 2024 20:11:40 +0000 (+0100) Subject: libctf: tear opening and serialization in two X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a14fb397b29b76786c05edb106f97d232607baba;p=thirdparty%2Fbinutils-gdb.git libctf: tear opening and serialization in two The next stage in sharing the strtab involves tearing two core parts of libctf into two pieces. Large parts of init_static_types, called at open time, involve traversing the types table and initializing the hashtabs used by the type name lookup functions and the enumerator conflicting checks. If the string table is partly located in the parent dict, this is obviously not going to work: so split out that code into a new init_static_types_names function (which also means moving the wrapper around init_static_types that was used to simplify the enumerator code into being a wrapper around init_static_types_names instead) and call that from init_static_types (for parent dicts, and < v4 dicts), and from ctf_import (for v4 dicts). At the same time as doing this we arrange to set LCTF_NO_STR (recently introduced) iff this is a v4 child dict with a nonzero cth_parent_strlen: this then blocks more or less everything that involves string operations until a ctf_import has actually imported the strtab it depends on. (No string oeprations that actually use this have been introduced yet, but since no string deduplication is happening yet either this is harmless.) For v4 dicts, at import time we also validate that the cth_parent_strlen has the same value as the parent's strlen (zero is also a valid value, indicating a non-shared strtab, as is commonplace in older dicts, dicts emitted by the compiler, parent dicts etc). This makes ctf_import more complex, so we simplify things again by dropping all the repeated code in the obscure used-only-by-ctf_link ctf_import_unref and turning both into wrappers around an internal function. We prohibit repeated ctf_imports (except of NULL or the same dict repeatedly), and set up some new fields which will be used later to prevent people from adding strings to parent dicts with pre-existing serialized strtabs once they have children imported into them (which would change their string length and corrupt all those strtabs). Serialization also needs to be torn in two. The problem here is that currently serialization does too much: it emits everything including the strtab, does things that depend on the strtab being finalized (notably variable table sorting), and then writes it out. Much of this emission itself involves strtab writes, so the strtab is not actually complete until halfway through ctf_serialize. But when deduplicating, we want to use machinery in ctf-link and ctf-dedup to deduplicate the strtab after it is complete, and only then write it out. We could do this via having ctf_serialize call some sort of horrible callback, but it seems much simpler to just cut ctf_serialize in two, and introduce a new ctf_preserialize which can optionally be called to do all this "everything but the strtab" work. (If it's not called, ctf_serialize calls it itself.) This means pulling some internal variables out of ctf_serialize into the ctf_dict_t, and slightly abusing LCTF_NO_STR to mean (in addition to its "no, you can't do much between opening a child dict and importing its parent" semantics), "no, you can't do much between calling ctf_preserialize and ctf_serialize". The requirements of both are not quite identical -- you definitely can do things that involve string lookups after ctf_preserialize -- but it serves to stop callers from accidentally adding more types after the types table has been written out, and that's good enough. ctf_preserialize isn't public API anyway. libctf/ * ctf-impl.h (struct ctf_dict) [ctf_serializing_buf]: New. [ctf_serializing_buf_size]: Likewise. [ctf_serializing_vars]: Likewise. [ctf_serializing_nvars]: Likewise. [ctf_max_children]: Likewise. (LCTF_PRESERIALIZED): New. (ctf_preserialize): New. (ctf_depreserialize): New. * ctf-open.c (init_static_types): Rename to... (init_static_types_names): ... this, wrapping a different function. (init_static_types_internal): Rename to... (init_static_types): ... this, and set LCTF_NO_STR if neecessary. Tear out the name-lookup guts into... (init_static_types_names_internal): ... this new function. Fix a few comment typos. (ctf_bufopen): Emphasise that you cannot rely on looking up strings at any point in ctf_bufopen any more. (ctf_dict_close): Free ctf_serializing_buf. (ctf_import): Turn into a wrapper, calling... (ctf_import_internal): ... this. Prohibit repeated ctf_imports of different parent dicts, or "unimporting" by setting it back to NULL again. Validate the parent we do import using cth_parent_strlen. Call init_static_types_names if the strtab is shared with the parent. (ctf_import_unref): Turn into a wrapper. * ctf-serialize.c (ctf_serialize): Split out everything before strtab serialization into... (ctf_preserialize): ... this new function. (ctf_depreserialize): New, undo preserialization on error. --- diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index ce0e186e08a..b8b2732739a 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -399,6 +399,10 @@ struct ctf_dict unsigned char *ctf_dynbase; /* Freeable CTF file pointer. */ unsigned char *ctf_buf; /* Uncompressed CTF data buffer. */ size_t ctf_size; /* Size of CTF header + uncompressed data. */ + unsigned char *ctf_serializing_buf; /* CTF buffer in mid-serialization. */ + size_t ctf_serializing_buf_size; /* Length of that buffer. */ + ctf_varent_t *ctf_serializing_vars; /* Unsorted vars in mid-serialization. */ + size_t ctf_serializing_nvars; /* Number of those vars. */ uint32_t *ctf_sxlate; /* Translation table for unindexed symtypetab entries. */ unsigned long ctf_nsyms; /* Number of entries in symtab xlate table. */ @@ -440,6 +444,7 @@ struct ctf_dict uint32_t ctf_parmax; /* Highest type ID of a parent type. */ uint32_t ctf_refcnt; /* Reference count (for parent links). */ uint32_t ctf_flags; /* Libctf flags (see below). */ + uint32_t ctf_max_children; /* Max number of child dicts. */ int ctf_errno; /* Error code for most recent error. */ int ctf_version; /* CTF data version. */ ctf_dynhash_t *ctf_dthash; /* Hash of dynamic type definitions. */ @@ -606,6 +611,7 @@ struct ctf_next #define LCTF_STRICT_NO_DUP_ENUMERATORS 0x0004 /* Duplicate enums prohibited. */ #define LCTF_NO_STR 0x0008 /* No string lookup possible yet. */ #define LCTF_NO_SERIALIZE 0x0010 /* Serialization of this dict prohibited. */ +#define LCTF_PRESERIALIZED 0x0020 /* Already serialized all but the strtab. */ extern ctf_dynhash_t *ctf_name_table (ctf_dict_t *, int); extern const ctf_type_t *ctf_lookup_by_id (ctf_dict_t **, ctf_id_t); @@ -753,6 +759,9 @@ extern void ctf_str_remove_ref (ctf_dict_t *, const char *, uint32_t *ref); extern void ctf_str_rollback (ctf_dict_t *, ctf_snapshot_id_t); extern const ctf_strs_writable_t *ctf_str_write_strtab (ctf_dict_t *); +extern int ctf_preserialize (ctf_dict_t *fp); +extern void ctf_depreserialize (ctf_dict_t *fp); + extern struct ctf_archive_internal * ctf_new_archive_internal (int is_archive, int unmap_on_close, struct ctf_archive *, ctf_dict_t *, diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c index 9423ad462bc..3ba78c89426 100644 --- a/libctf/ctf-open.c +++ b/libctf/ctf-open.c @@ -463,7 +463,7 @@ upgrade_types_v1 (ctf_dict_t *fp, ctf_header_t *cth) 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 @@ -701,8 +701,7 @@ upgrade_types (ctf_dict_t *fp, ctf_header_t *cth) } 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). @@ -718,22 +717,6 @@ init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth, 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; @@ -741,18 +724,12 @@ init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth, 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) { @@ -765,9 +742,12 @@ init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth, 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++) { @@ -844,28 +824,96 @@ init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth, 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++) { @@ -1734,8 +1782,9 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, 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; @@ -1997,6 +2046,7 @@ ctf_dict_close (ctf_dict_t *fp) free (fp->ctf_txlate); free (fp->ctf_ptrtab); free (fp->ctf_pptrtab); + free (fp->ctf_serializing_buf); free (fp->ctf_header); free (fp); @@ -2120,45 +2170,100 @@ ctf_cuname_set (ctf_dict_t *fp, const char *name) 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 @@ -2166,34 +2271,7 @@ ctf_import (ctf_dict_t *fp, ctf_dict_t *pfp) 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. */ diff --git a/libctf/ctf-serialize.c b/libctf/ctf-serialize.c index ebd61457ccb..19ab8abf63e 100644 --- a/libctf/ctf-serialize.c +++ b/libctf/ctf-serialize.c @@ -933,22 +933,16 @@ ctf_sort_var (const void *one_, const void *two_, void *arg_) /* 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; @@ -956,20 +950,13 @@ ctf_serialize (ctf_dict_t *fp, size_t *bufsiz) 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, @@ -977,10 +964,7 @@ ctf_serialize (ctf_dict_t *fp, size_t *bufsiz) 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 @@ -1010,17 +994,17 @@ ctf_serialize (ctf_dict_t *fp, size_t *bufsiz) 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 @@ -1032,7 +1016,7 @@ ctf_serialize (ctf_dict_t *fp, size_t *bufsiz) 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); @@ -1052,14 +1036,12 @@ ctf_serialize (ctf_dict_t *fp, size_t *bufsiz) 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; @@ -1072,18 +1054,21 @@ ctf_serialize (ctf_dict_t *fp, size_t *bufsiz) 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; @@ -1091,6 +1076,7 @@ ctf_serialize (ctf_dict_t *fp, size_t *bufsiz) 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); @@ -1103,6 +1089,65 @@ ctf_serialize (ctf_dict_t *fp, size_t *bufsiz) 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 @@ -1113,34 +1158,45 @@ ctf_serialize (ctf_dict_t *fp, size_t *bufsiz) 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. */ }