diff options
author | Nick Alcock <nick.alcock@oracle.com> | 2023-12-19 16:58:19 +0000 |
---|---|---|
committer | Nick Alcock <nick.alcock@oracle.com> | 2024-04-19 16:14:46 +0100 |
commit | 8a60c93096326ef818dd72d0a44bd575a04cc55a (patch) | |
tree | 60c2d3df1bbfee0ad98cbb73bca635825ef8b9b6 /libctf/ctf-open.c | |
parent | 2ba5ec13b20a927666096dd7d6df22b845dcd475 (diff) | |
download | gdb-8a60c93096326ef818dd72d0a44bd575a04cc55a.zip gdb-8a60c93096326ef818dd72d0a44bd575a04cc55a.tar.gz gdb-8a60c93096326ef818dd72d0a44bd575a04cc55a.tar.bz2 |
libctf: support addition of types to dicts read via ctf_open()
libctf has long declared deserialized dictionaries (out of files or ELF
sections or memory buffers or whatever) to be read-only: back in the
furthest prehistory this was not the case, in that you could add a
few sorts of type to such dicts, but attempting to do so often caused
horrible memory corruption, so I banned the lot.
But it turns out real consumers want it (notably DTrace, which
synthesises pointers to types that don't have them and adds them to the
ctf_open()ed dicts if it needs them). Let's bring it back again, but
without the memory corruption and without the massive code duplication
required in days of yore to distinguish between static and dynamic
types: the representation of both types has been identical for a few
years, with the only difference being that types as a whole are stored in
a big buffer for types read in via ctf_open and per-type hashtables for
newly-added types.
So we discard the internally-visible concept of "readonly dictionaries"
in favour of declaring the *range of types* that were already present
when the dict was read in to be read-only: you can't modify them (say,
by adding members to them if they're structs, or calling ctf_set_array
on them), but you can add more types and point to them. (The API
remains the same, with calls sometimes returning ECTF_RDONLY, but now
they do so less often.)
This is a fairly invasive change, mostly because code written since the
ban was introduced didn't take the possibility of a static/dynamic split
into account. Some of these irregularities were hard to define as
anything but bugs.
Notably:
- The symbol handling was assuming that symbols only needed to be
looked for in dynamic hashtabs or static linker-laid-out indexed/
nonindexed layouts, but now we want to check both in case people
added more symbols to a dict they opened.
- The code that handles type additions wasn't checking to see if types
with the same name existed *at all* (so you could do
ctf_add_typedef (fp, "foo", bar) repeatedly without error). This
seems reasonable for types you just added, but we probably *do* want
to ban addition of types with names that override names we already
used in the ctf_open()ed portion, since that would probably corrupt
existing type relationships. (Doing things this way also avoids
causing new errors for any existing code that was doing this sort of
thing.)
- ctf_lookup_variable entirely failed to work for variables just added
by ctf_add_variable: you had to write the dict out and read it back
in again before they appeared.
- The symbol handling remembered what symbols you looked up but didn't
remember their types, so you could look up an object symbol and then
find it popping up when you asked for function symbols, which seems
less than ideal. Since we had to rejig things enough to be able to
distinguish function and object symbols internally anyway (in order
to give suitable errors if you try to add a symbol with a name that
already existed in the ctf_open()ed dict), this bug suddenly became
more visible and was easily fixed.
We do not (yet) support writing out dicts that have been previously read
in via ctf_open() or other deserializer (you can look things up in them,
but not write them out a second time). This never worked, so there is
no incompatibility; if it is needed at a later date, the serializer is a
little bit closer to having it work now (the only table we don't deal
with is the types table, and that's because the upcoming CTFv4 changes
are likely to make major changes to the way that table is represented
internally, so adding more code that depends on its current form seems
like a bad idea).
There is a new testcase that tests much of this, in particular that
modification of existing types is still banned and that you can add new
ones and chase them without error.
libctf/
* ctf-impl.h (struct ctf_dict.ctf_symhash): Split into...
(ctf_dict.ctf_symhash_func): ... this and...
(ctf_dict.ctf_symhash_objt): ... this.
(ctf_dict.ctf_stypes): New, counts static types.
(LCTF_INDEX_TO_TYPEPTR): Use it instead of CTF_RDWR.
(LCTF_RDWR): Deleted.
(LCTF_DIRTY): Renumbered.
(LCTF_LINKING): Likewise.
(ctf_lookup_variable_here): New.
(ctf_lookup_by_sym_or_name): Likewise.
(ctf_symbol_next_static): Likewise.
(ctf_add_variable_forced): Likewise.
(ctf_add_funcobjt_sym_forced): Likewise.
(ctf_simple_open_internal): Adjust.
(ctf_bufopen_internal): Likewise.
* ctf-create.c (ctf_grow_ptrtab): Adjust a lot to start with.
(ctf_create): Migrate a bunch of initializations into bufopen.
Force recreation of name tables. Do not forcibly override the
model, let ctf_bufopen do it.
(ctf_static_type): New.
(ctf_update): Drop LCTF_RDWR check.
(ctf_dynamic_type): Likewise.
(ctf_add_function): Likewise.
(ctf_add_type_internal): Likewise.
(ctf_rollback): Check ctf_stypes, not LCTF_RDWR.
(ctf_set_array): Likewise.
(ctf_add_struct_sized): Likewise.
(ctf_add_union_sized): Likewise.
(ctf_add_enum): Likewise.
(ctf_add_enumerator): Likewise (only on the target dict).
(ctf_add_member_offset): Likewise.
(ctf_add_generic): Drop LCTF_RDWR check. Ban addition of types
with colliding names.
(ctf_add_forward): Note safety under the new rules.
(ctf_add_variable): Split all but the existence check into...
(ctf_add_variable_forced): ... this new function.
(ctf_add_funcobjt_sym): Likewise...
(ctf_add_funcobjt_sym_forced): ... for this new function.
* ctf-link.c (ctf_link_add_linker_symbol): Ban calling on dicts
with any stypes.
(ctf_link_add_strtab): Likewise.
(ctf_link_shuffle_syms): Likewise.
(ctf_link_intern_extern_string): Note pre-existing prohibition.
* ctf-lookup.c (ctf_lookup_by_id): Drop LCTF_RDWR check.
(ctf_lookup_variable): Split out looking in a dict but not
its parent into...
(ctf_lookup_variable_here): ... this new function.
(ctf_lookup_symbol_idx): Track whether looking up a function or
object: cache them separately.
(ctf_symbol_next): Split out looking in non-dynamic symtypetab
entries to...
(ctf_symbol_next_static): ... this new function. Don't get confused
by the simultaneous presence of static and dynamic symtypetab entries.
(ctf_try_lookup_indexed): Don't waste time looking up symbols by
index before there can be any idea how symbols are numbered.
(ctf_lookup_by_sym_or_name): Distinguish between function and
data object lookups. Drop LCTF_RDWR.
(ctf_lookup_by_symbol): Adjust.
(ctf_lookup_by_symbol_name): Likewise.
* ctf-open.c (init_types): Rename to...
(init_static_types): ... this. Drop LCTF_RDWR. Populate ctf_stypes.
(ctf_simple_open): Drop writable arg.
(ctf_simple_open_internal): Likewise.
(ctf_bufopen): Likewise.
(ctf_bufopen_internal): Populate fields only used for writable dicts.
Drop LCTF_RDWR.
(ctf_dict_close): Cater for symhash cache split.
* ctf-serialize.c (ctf_serialize): Use ctf_stypes, not LCTF_RDWR.
* ctf-types.c (ctf_variable_next): Drop LCTF_RDWR.
* testsuite/libctf-lookup/add-to-opened*: New test.
Diffstat (limited to 'libctf/ctf-open.c')
-rw-r--r-- | libctf/ctf-open.c | 66 |
1 files changed, 41 insertions, 25 deletions
diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c index 87b0f74..f80bf54 100644 --- a/libctf/ctf-open.c +++ b/libctf/ctf-open.c @@ -670,13 +670,15 @@ upgrade_types (ctf_dict_t *fp, ctf_header_t *cth) return 0; } -/* Initialize the type ID translation table with the byte offset of each type, +/* Populate statically-defined types (those loaded from a saved buffer). + + Initialize the type ID translation table with the byte offset of each type, and initialize the hash tables of each named type. Upgrade the type table to the latest supported representation in the process, if needed, and if this recension of libctf supports upgrading. */ static int -init_types (ctf_dict_t *fp, ctf_header_t *cth) +init_static_types (ctf_dict_t *fp, ctf_header_t *cth) { const ctf_type_t *tbuf; const ctf_type_t *tend; @@ -694,8 +696,6 @@ init_types (ctf_dict_t *fp, ctf_header_t *cth) int nlstructs = 0, nlunions = 0; int err; - assert (!(fp->ctf_flags & LCTF_RDWR)); - if (_libctf_unlikely_ (fp->ctf_version == CTF_VERSION_1)) { int err; @@ -770,9 +770,16 @@ init_types (ctf_dict_t *fp, ctf_header_t *cth) ctf_hash_eq_string, NULL, NULL)) == NULL) return ENOMEM; + /* The ptrtab and txlate can be appropriately sized for precisely this set + of types: the txlate because it is only used to look up static types, + so dynamic types added later will never go through it, and the ptrtab + because later-added types will call grow_ptrtab() automatically, as + needed. */ + fp->ctf_txlate = malloc (sizeof (uint32_t) * (typemax + 1)); fp->ctf_ptrtab_len = typemax + 1; fp->ctf_ptrtab = malloc (sizeof (uint32_t) * fp->ctf_ptrtab_len); + fp->ctf_stypes = typemax; if (fp->ctf_txlate == NULL || fp->ctf_ptrtab == NULL) return ENOMEM; /* Memory allocation failed. */ @@ -1283,7 +1290,7 @@ ctf_dict_t *ctf_simple_open (const char *ctfsect, size_t ctfsect_size, { return ctf_simple_open_internal (ctfsect, ctfsect_size, symsect, symsect_size, symsect_entsize, strsect, strsect_size, NULL, - 0, errp); + errp); } /* Open a CTF file, mocking up a suitable ctf_sect and overriding the external @@ -1293,8 +1300,7 @@ ctf_dict_t *ctf_simple_open_internal (const char *ctfsect, size_t ctfsect_size, const char *symsect, size_t symsect_size, size_t symsect_entsize, const char *strsect, size_t strsect_size, - ctf_dynhash_t *syn_strtab, int writable, - int *errp) + ctf_dynhash_t *syn_strtab, int *errp) { ctf_sect_t skeleton; @@ -1332,7 +1338,7 @@ ctf_dict_t *ctf_simple_open_internal (const char *ctfsect, size_t ctfsect_size, } return ctf_bufopen_internal (ctfsectp, symsectp, strsectp, syn_strtab, - writable, errp); + errp); } /* Decode the specified CTF buffer and optional symbol table, and create a new @@ -1344,7 +1350,7 @@ ctf_dict_t * ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, const ctf_sect_t *strsect, int *errp) { - return ctf_bufopen_internal (ctfsect, symsect, strsect, NULL, 0, errp); + return ctf_bufopen_internal (ctfsect, symsect, strsect, NULL, errp); } /* Like ctf_bufopen, but overriding the external strtab with a synthetic one. */ @@ -1352,7 +1358,7 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, ctf_dict_t * ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, const ctf_sect_t *strsect, ctf_dynhash_t *syn_strtab, - int writable, int *errp) + int *errp) { const ctf_preamble_t *pp; size_t hdrsz = sizeof (ctf_header_t); @@ -1441,9 +1447,6 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, memset (fp, 0, sizeof (ctf_dict_t)); - if (writable) - fp->ctf_flags |= LCTF_RDWR; - if ((fp->ctf_header = malloc (sizeof (struct ctf_header))) == NULL) { free (fp); @@ -1526,7 +1529,7 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, section's buffer pointer into ctf_buf, below. */ /* Note: if this is a v1 buffer, it will be reallocated and expanded by - init_types(). */ + init_static_types(). */ if (hp->cth_flags & CTF_F_COMPRESS) { @@ -1607,7 +1610,7 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, proceed with initializing the ctf_dict_t we allocated above. Nothing that depends on buf or base should be set directly in this function - before the init_types() call, because it may be reallocated during + before the init_static_types() call, because it may be reallocated during transparent upgrade if this recension of libctf is so configured: see ctf_set_base(). */ @@ -1660,6 +1663,26 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, } fp->ctf_syn_ext_strtab = syn_strtab; + /* Dynamic state, for dynamic addition to this dict after loading. */ + + fp->ctf_dthash = ctf_dynhash_create (ctf_hash_integer, ctf_hash_eq_integer, + NULL, NULL); + fp->ctf_dvhash = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string, + NULL, NULL); + fp->ctf_snapshots = 1; + + fp->ctf_objthash = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string, + free, NULL); + fp->ctf_funchash = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string, + free, NULL); + + if (!fp->ctf_dthash || !fp->ctf_dvhash || !fp->ctf_snapshots || + !fp->ctf_objthash || !fp->ctf_funchash) + { + err = ENOMEM; + goto bad; + } + if (foreign_endian && (err = ctf_flip (fp, hp, fp->ctf_buf, 0)) != 0) { @@ -1673,15 +1696,7 @@ ctf_bufopen_internal (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, ctf_set_base (fp, hp, fp->ctf_base); - /* No need to do anything else for dynamic dicts: they do not support symbol - lookups, and the type table is maintained in the dthashes. */ - if (fp->ctf_flags & LCTF_RDWR) - { - fp->ctf_refcnt = 1; - return fp; - } - - if ((err = init_types (fp, hp)) != 0) + if ((err = init_static_types (fp, hp)) != 0) goto bad; /* Allocate and initialize the symtab translation table, pointed to by @@ -1800,7 +1815,8 @@ ctf_dict_close (ctf_dict_t *fp) } ctf_dynhash_destroy (fp->ctf_dvhash); - ctf_dynhash_destroy (fp->ctf_symhash); + ctf_dynhash_destroy (fp->ctf_symhash_func); + ctf_dynhash_destroy (fp->ctf_symhash_objt); free (fp->ctf_funcidx_sxlate); free (fp->ctf_objtidx_sxlate); ctf_dynhash_destroy (fp->ctf_objthash); |