aboutsummaryrefslogtreecommitdiff
path: root/libctf/ctf-create.c
diff options
context:
space:
mode:
authorNick Alcock <nick.alcock@oracle.com>2023-12-19 16:58:19 +0000
committerNick Alcock <nick.alcock@oracle.com>2024-04-19 16:14:46 +0100
commit8a60c93096326ef818dd72d0a44bd575a04cc55a (patch)
tree60c2d3df1bbfee0ad98cbb73bca635825ef8b9b6 /libctf/ctf-create.c
parent2ba5ec13b20a927666096dd7d6df22b845dcd475 (diff)
downloadbinutils-8a60c93096326ef818dd72d0a44bd575a04cc55a.zip
binutils-8a60c93096326ef818dd72d0a44bd575a04cc55a.tar.gz
binutils-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-create.c')
-rw-r--r--libctf/ctf-create.c184
1 files changed, 97 insertions, 87 deletions
diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c
index 240f3da..7aa244e 100644
--- a/libctf/ctf-create.c
+++ b/libctf/ctf-create.c
@@ -47,9 +47,13 @@ ctf_grow_ptrtab (ctf_dict_t *fp)
size_t new_ptrtab_len = fp->ctf_ptrtab_len;
/* We allocate one more ptrtab entry than we need, for the initial zero,
- plus one because the caller will probably allocate a new type. */
+ plus one because the caller will probably allocate a new type.
- if (fp->ctf_ptrtab == NULL)
+ Equally, if the ptrtab is small -- perhaps due to ctf_open of a small
+ dict -- boost it by quite a lot at first, so we don't need to keep
+ realloc()ing. */
+
+ if (fp->ctf_ptrtab == NULL || fp->ctf_ptrtab_len < 1024)
new_ptrtab_len = 1024;
else if ((fp->ctf_typemax + 2) > fp->ctf_ptrtab_len)
new_ptrtab_len = fp->ctf_ptrtab_len * 1.25;
@@ -104,29 +108,11 @@ ctf_create (int *errp)
{
static const ctf_header_t hdr = { .cth_preamble = { CTF_MAGIC, CTF_VERSION, 0 } };
- ctf_dynhash_t *dthash;
- ctf_dynhash_t *dvhash;
ctf_dynhash_t *structs = NULL, *unions = NULL, *enums = NULL, *names = NULL;
- ctf_dynhash_t *objthash = NULL, *funchash = NULL;
ctf_sect_t cts;
ctf_dict_t *fp;
libctf_init_debug();
- dthash = ctf_dynhash_create (ctf_hash_integer, ctf_hash_eq_integer,
- NULL, NULL);
- if (dthash == NULL)
- {
- ctf_set_open_errno (errp, EAGAIN);
- goto err;
- }
-
- dvhash = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
- NULL, NULL);
- if (dvhash == NULL)
- {
- ctf_set_open_errno (errp, EAGAIN);
- goto err_dt;
- }
structs = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
NULL, NULL);
@@ -136,14 +122,10 @@ ctf_create (int *errp)
NULL, NULL);
names = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
NULL, NULL);
- objthash = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
- free, NULL);
- funchash = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
- free, NULL);
if (!structs || !unions || !enums || !names)
{
ctf_set_open_errno (errp, EAGAIN);
- goto err_dv;
+ goto err;
}
cts.cts_name = _CTF_SECTION;
@@ -151,24 +133,26 @@ ctf_create (int *errp)
cts.cts_size = sizeof (hdr);
cts.cts_entsize = 1;
- if ((fp = ctf_bufopen_internal (&cts, NULL, NULL, NULL, 1, errp)) == NULL)
- goto err_dv;
+ if ((fp = ctf_bufopen_internal (&cts, NULL, NULL, NULL, errp)) == NULL)
+ goto err;
+ /* These hashes will have been initialized with a starting size of zero,
+ which is surely wrong. Use ones with slightly larger sizes. */
+ ctf_dynhash_destroy (fp->ctf_structs);
+ ctf_dynhash_destroy (fp->ctf_unions);
+ ctf_dynhash_destroy (fp->ctf_enums);
+ ctf_dynhash_destroy (fp->ctf_names);
fp->ctf_structs = structs;
fp->ctf_unions = unions;
fp->ctf_enums = enums;
fp->ctf_names = names;
- fp->ctf_objthash = objthash;
- fp->ctf_funchash = funchash;
- fp->ctf_dthash = dthash;
- fp->ctf_dvhash = dvhash;
fp->ctf_dtoldid = 0;
- fp->ctf_snapshots = 1;
fp->ctf_snapshot_lu = 0;
fp->ctf_flags |= LCTF_DIRTY;
+ /* Make sure the ptrtab starts out at a reasonable size. */
+
ctf_set_ctl_hashes (fp);
- ctf_setmodel (fp, CTF_MODEL_NATIVE);
if (ctf_grow_ptrtab (fp) < 0)
{
ctf_set_open_errno (errp, ctf_errno (fp));
@@ -178,17 +162,11 @@ ctf_create (int *errp)
return fp;
- err_dv:
+ err:
ctf_dynhash_destroy (structs);
ctf_dynhash_destroy (unions);
ctf_dynhash_destroy (enums);
ctf_dynhash_destroy (names);
- ctf_dynhash_destroy (objthash);
- ctf_dynhash_destroy (funchash);
- ctf_dynhash_destroy (dvhash);
- err_dt:
- ctf_dynhash_destroy (dthash);
- err:
return NULL;
}
@@ -196,9 +174,6 @@ ctf_create (int *errp)
int
ctf_update (ctf_dict_t *fp)
{
- if (!(fp->ctf_flags & LCTF_RDWR))
- return (ctf_set_errno (fp, ECTF_RDONLY));
-
fp->ctf_dtoldid = fp->ctf_typemax;
return 0;
}
@@ -310,9 +285,6 @@ ctf_dynamic_type (const ctf_dict_t *fp, ctf_id_t id)
{
ctf_id_t idx;
- if (!(fp->ctf_flags & LCTF_RDWR))
- return NULL;
-
if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, id))
fp = fp->ctf_parent;
@@ -323,6 +295,19 @@ ctf_dynamic_type (const ctf_dict_t *fp, ctf_id_t id)
return NULL;
}
+static int
+ctf_static_type (const ctf_dict_t *fp, ctf_id_t id)
+{
+ ctf_id_t idx;
+
+ if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, id))
+ fp = fp->ctf_parent;
+
+ idx = LCTF_TYPE_TO_INDEX(fp, id);
+
+ return ((unsigned long) idx <= fp->ctf_stypes);
+}
+
int
ctf_dvd_insert (ctf_dict_t *fp, ctf_dvdef_t *dvd)
{
@@ -385,7 +370,7 @@ ctf_rollback (ctf_dict_t *fp, ctf_snapshot_id_t id)
ctf_dtdef_t *dtd, *ntd;
ctf_dvdef_t *dvd, *nvd;
- if (!(fp->ctf_flags & LCTF_RDWR))
+ if (id.snapshot_id < fp->ctf_stypes)
return (ctf_set_errno (fp, ECTF_RDONLY));
if (fp->ctf_snapshot_lu >= id.snapshot_id)
@@ -449,15 +434,25 @@ ctf_add_generic (ctf_dict_t *fp, uint32_t flag, const char *name, int kind,
if (flag != CTF_ADD_NONROOT && flag != CTF_ADD_ROOT)
return (ctf_set_typed_errno (fp, EINVAL));
- if (!(fp->ctf_flags & LCTF_RDWR))
- return (ctf_set_typed_errno (fp, ECTF_RDONLY));
-
if (LCTF_INDEX_TO_TYPE (fp, fp->ctf_typemax, 1) >= CTF_MAX_TYPE)
return (ctf_set_typed_errno (fp, ECTF_FULL));
if (LCTF_INDEX_TO_TYPE (fp, fp->ctf_typemax, 1) == (CTF_MAX_PTYPE - 1))
return (ctf_set_typed_errno (fp, ECTF_FULL));
+ /* Prohibit addition of a root-visible type that is already present
+ in the non-dynamic portion. */
+
+ if (flag == CTF_ADD_ROOT && name != NULL && name[0] != '\0')
+ {
+ ctf_id_t existing;
+
+ if (((existing = ctf_dynhash_lookup_type (ctf_name_table (fp, kind),
+ name)) > 0)
+ && ctf_static_type (fp, existing))
+ return (ctf_set_typed_errno (fp, ECTF_RDONLY));
+ }
+
/* Make sure ptrtab always grows to be big enough for all types. */
if (ctf_grow_ptrtab (fp) < 0)
return CTF_ERR; /* errno is set for us. */
@@ -724,10 +719,9 @@ ctf_set_array (ctf_dict_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)
if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, type))
fp = fp->ctf_parent;
- if (!(ofp->ctf_flags & LCTF_RDWR))
- return (ctf_set_errno (ofp, ECTF_RDONLY));
-
- if (!(fp->ctf_flags & LCTF_RDWR))
+ /* You can only call ctf_set_array on a type you have added, not a
+ type that was read in via ctf_open(). */
+ if (type < fp->ctf_stypes)
return (ctf_set_errno (ofp, ECTF_RDONLY));
if (dtd == NULL
@@ -755,9 +749,6 @@ ctf_add_function (ctf_dict_t *fp, uint32_t flag,
size_t initial_vlen;
size_t i;
- if (!(fp->ctf_flags & LCTF_RDWR))
- return (ctf_set_typed_errno (fp, ECTF_RDONLY));
-
if (ctc == NULL || (ctc->ctc_flags & ~CTF_FUNC_VARARG) != 0
|| (ctc->ctc_argc != 0 && argv == NULL))
return (ctf_set_typed_errno (fp, EINVAL));
@@ -813,6 +804,10 @@ ctf_add_struct_sized (ctf_dict_t *fp, uint32_t flag, const char *name,
if (name != NULL)
type = ctf_lookup_by_rawname (fp, CTF_K_STRUCT, name);
+ /* Prohibit promotion if this type was ctf_open()ed. */
+ if (type > 0 && type < fp->ctf_stypes)
+ return (ctf_set_errno (fp, ECTF_RDONLY));
+
if (type != 0 && ctf_type_kind (fp, type) == CTF_K_FORWARD)
dtd = ctf_dtd_lookup (fp, type);
else if ((type = ctf_add_generic (fp, flag, name, CTF_K_STRUCT,
@@ -853,11 +848,15 @@ ctf_add_union_sized (ctf_dict_t *fp, uint32_t flag, const char *name,
if (name != NULL)
type = ctf_lookup_by_rawname (fp, CTF_K_UNION, name);
+ /* Prohibit promotion if this type was ctf_open()ed. */
+ if (type > 0 && type < fp->ctf_stypes)
+ return (ctf_set_errno (fp, ECTF_RDONLY));
+
if (type != 0 && ctf_type_kind (fp, type) == CTF_K_FORWARD)
dtd = ctf_dtd_lookup (fp, type);
else if ((type = ctf_add_generic (fp, flag, name, CTF_K_UNION,
initial_vlen, &dtd)) == CTF_ERR)
- return CTF_ERR; /* errno is set for us */
+ return CTF_ERR; /* errno is set for us. */
/* Forwards won't have any vlen yet. */
if (dtd->dtd_vlen_alloc == 0)
@@ -892,6 +891,10 @@ ctf_add_enum (ctf_dict_t *fp, uint32_t flag, const char *name)
if (name != NULL)
type = ctf_lookup_by_rawname (fp, CTF_K_ENUM, name);
+ /* Prohibit promotion if this type was ctf_open()ed. */
+ if (type > 0 && type < fp->ctf_stypes)
+ return (ctf_set_errno (fp, ECTF_RDONLY));
+
if (type != 0 && ctf_type_kind (fp, type) == CTF_K_FORWARD)
dtd = ctf_dtd_lookup (fp, type);
else if ((type = ctf_add_generic (fp, flag, name, CTF_K_ENUM,
@@ -953,8 +956,9 @@ ctf_add_forward (ctf_dict_t *fp, uint32_t flag, const char *name,
if (name == NULL || name[0] == '\0')
return (ctf_set_typed_errno (fp, ECTF_NONAME));
- /* If the type is already defined or exists as a forward tag, just
- return the ctf_id_t of the existing definition. */
+ /* If the type is already defined or exists as a forward tag, just return
+ the ctf_id_t of the existing definition. Since this changes nothing,
+ it's safe to do even on the read-only portion of the dict. */
type = ctf_lookup_by_rawname (fp, kind, name);
@@ -1066,10 +1070,7 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, enid))
fp = fp->ctf_parent;
- if (!(ofp->ctf_flags & LCTF_RDWR))
- return (ctf_set_errno (ofp, ECTF_RDONLY));
-
- if (!(fp->ctf_flags & LCTF_RDWR))
+ if (enid < fp->ctf_stypes)
return (ctf_set_errno (ofp, ECTF_RDONLY));
if (dtd == NULL)
@@ -1142,10 +1143,7 @@ ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
fp = fp->ctf_parent;
}
- if (!(ofp->ctf_flags & LCTF_RDWR))
- return (ctf_set_errno (ofp, ECTF_RDONLY));
-
- if (!(fp->ctf_flags & LCTF_RDWR))
+ if (souid < fp->ctf_stypes)
return (ctf_set_errno (ofp, ECTF_RDONLY));
if (dtd == NULL)
@@ -1332,18 +1330,15 @@ ctf_add_member (ctf_dict_t *fp, ctf_id_t souid, const char *name,
return ctf_add_member_offset (fp, souid, name, type, (unsigned long) - 1);
}
+/* Add a variable regardless of whether or not it is already present.
+
+ Internal use only. */
int
-ctf_add_variable (ctf_dict_t *fp, const char *name, ctf_id_t ref)
+ctf_add_variable_forced (ctf_dict_t *fp, const char *name, ctf_id_t ref)
{
ctf_dvdef_t *dvd;
ctf_dict_t *tmp = fp;
- if (!(fp->ctf_flags & LCTF_RDWR))
- return (ctf_set_errno (fp, ECTF_RDONLY));
-
- if (ctf_dvd_lookup (fp, name) != NULL)
- return (ctf_set_errno (fp, ECTF_DUPLICATE));
-
if (ctf_lookup_by_id (&tmp, ref) == NULL)
return -1; /* errno is set for us. */
@@ -1375,21 +1370,30 @@ ctf_add_variable (ctf_dict_t *fp, const char *name, ctf_id_t ref)
}
int
-ctf_add_funcobjt_sym (ctf_dict_t *fp, int is_function, const char *name, ctf_id_t id)
+ctf_add_variable (ctf_dict_t *fp, const char *name, ctf_id_t ref)
+{
+ if (ctf_lookup_variable_here (fp, name) != CTF_ERR)
+ return (ctf_set_errno (fp, ECTF_DUPLICATE));
+
+ if (ctf_errno (fp) != ECTF_NOTYPEDAT)
+ return -1; /* errno is set for us. */
+
+ return ctf_add_variable_forced (fp, name, ref);
+}
+
+/* Add a function or object symbol regardless of whether or not it is already
+ present (already existing symbols are silently overwritten).
+
+ Internal use only. */
+int
+ctf_add_funcobjt_sym_forced (ctf_dict_t *fp, int is_function, const char *name, ctf_id_t id)
{
ctf_dict_t *tmp = fp;
char *dupname;
ctf_dynhash_t *h = is_function ? fp->ctf_funchash : fp->ctf_objthash;
- if (!(fp->ctf_flags & LCTF_RDWR))
- return (ctf_set_errno (fp, ECTF_RDONLY));
-
- if (ctf_dynhash_lookup (fp->ctf_objthash, name) != NULL ||
- ctf_dynhash_lookup (fp->ctf_funchash, name) != NULL)
- return (ctf_set_errno (fp, ECTF_DUPLICATE));
-
if (ctf_lookup_by_id (&tmp, id) == NULL)
- return -1; /* errno is set for us. */
+ return -1; /* errno is set for us. */
if (is_function && ctf_type_kind (fp, id) != CTF_K_FUNCTION)
return (ctf_set_errno (fp, ECTF_NOTFUNC));
@@ -1406,6 +1410,15 @@ ctf_add_funcobjt_sym (ctf_dict_t *fp, int is_function, const char *name, ctf_id_
}
int
+ctf_add_funcobjt_sym (ctf_dict_t *fp, int is_function, const char *name, ctf_id_t id)
+{
+ if (ctf_lookup_by_sym_or_name (fp, 0, name, 0, is_function) != CTF_ERR)
+ return (ctf_set_errno (fp, ECTF_DUPLICATE));
+
+ return ctf_add_funcobjt_sym_forced (fp, is_function, name, id);
+}
+
+int
ctf_add_objt_sym (ctf_dict_t *fp, const char *name, ctf_id_t id)
{
return (ctf_add_funcobjt_sym (fp, 0, name, id));
@@ -1606,9 +1619,6 @@ ctf_add_type_internal (ctf_dict_t *dst_fp, ctf_dict_t *src_fp, ctf_id_t src_type
ctf_id_t orig_src_type = src_type;
- if (!(dst_fp->ctf_flags & LCTF_RDWR))
- return (ctf_set_typed_errno (dst_fp, ECTF_RDONLY));
-
if ((src_tp = ctf_lookup_by_id (&src_fp, src_type)) == NULL)
return (ctf_set_typed_errno (dst_fp, ctf_errno (src_fp)));