diff --git a/include/ctf-api.h b/include/ctf-api.h index d3d8dbfacf5..dd719d11129 100644 --- a/include/ctf-api.h +++ b/include/ctf-api.h @@ -243,7 +243,8 @@ typedef struct ctf_snapshot_id _CTF_ITEM (ECTF_FLAGS, "CTF header contains flags unknown to libctf.") \ _CTF_ITEM (ECTF_NEEDSBFD, "This feature needs a libctf with BFD support.") \ _CTF_ITEM (ECTF_INCOMPLETE, "Type is not a complete type.") \ - _CTF_ITEM (ECTF_NONAME, "Type name must not be empty.") + _CTF_ITEM (ECTF_NONAME, "Type name must not be empty.") \ + _CTF_ITEM (ECTF_BADFLAG, "Invalid CTF dict flag specified.") #define ECTF_BASE 1000 /* Base value for libctf errnos. */ @@ -256,7 +257,7 @@ _CTF_ERRORS #undef _CTF_FIRST }; -#define ECTF_NERR (ECTF_NONAME - ECTF_BASE + 1) /* Count of CTF errors. */ +#define ECTF_NERR (ECTF_BADFLAG - ECTF_BASE + 1) /* Count of CTF errors. */ /* The CTF data model is inferred to be the caller's data model or the data model of the given object, unless ctf_setmodel is explicitly called. */ @@ -282,6 +283,12 @@ _CTF_ERRORS #define CTF_MN_RECURSE 0x1 /* Recurse into unnamed members. */ +/* Flags for ctf_dict_set_flag. */ + +/* If set, duplicate enumerators in a single dict fail with ECTF_DUPLICATE. */ + +#define CTF_STRICT_NO_DUP_ENUMERATORS 0x1 + /* These typedefs are used to define the signature for callback functions that can be used with the iteration and visit functions below. There is also a family of iteration functions that do not require callbacks. */ @@ -350,6 +357,11 @@ extern ctf_archive_t *ctf_open (const char *filename, const char *target, int *errp); extern void ctf_close (ctf_archive_t *); +/* Set or unset dict-wide boolean flags, and get the value of these flags. */ + +extern int ctf_dict_set_flag (ctf_dict_t *, uint64_t flag, int set); +extern int ctf_dict_get_flag (ctf_dict_t *, uint64_t flag); + /* Return the data, symbol, or string sections used by a given CTF dict. */ extern ctf_sect_t ctf_getdatasect (const ctf_dict_t *); extern ctf_sect_t ctf_getsymsect (const ctf_dict_t *); diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c index d6746030918..a7544955212 100644 --- a/libctf/ctf-create.c +++ b/libctf/ctf-create.c @@ -1071,7 +1071,13 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name, if the enum they are part of is a root-visible type. */ if (root == CTF_ADD_ROOT && ctf_dynhash_lookup (fp->ctf_names, name)) - return (ctf_set_errno (ofp, ECTF_DUPLICATE)); + { + if (fp->ctf_flags & LCTF_STRICT_NO_DUP_ENUMERATORS) + return (ctf_set_errno (ofp, ECTF_DUPLICATE)); + + if (ctf_track_enumerator (fp, enid, name) < 0) + return (ctf_set_errno (ofp, ctf_errno (fp))); + } if (kind != CTF_K_ENUM) return (ctf_set_errno (ofp, ECTF_NOTENUM)); @@ -1094,7 +1100,7 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name, non-root-visible types, since the duplicate detection above does the job for root-visible types just fine. */ - if (root == CTF_ADD_NONROOT) + if (root == CTF_ADD_NONROOT && (fp->ctf_flags & LCTF_STRICT_NO_DUP_ENUMERATORS)) { size_t i; @@ -1429,6 +1435,42 @@ ctf_add_func_sym (ctf_dict_t *fp, const char *name, ctf_id_t id) return (ctf_add_funcobjt_sym (fp, 1, name, id)); } +/* Add an enumeration constant observed in a given enum type as an identifier. + They appear as names that cite the enum type. + + Constants that appear in more than one enum, or which are already the names + of types, appear in ctf_conflicting_enums as well. + + This is done for all enumeration types at open time, and for newly-added ones + as well: if the strict-enum flag is turned on, this table must be kept up to + date with enums added in the interim. */ + +int +ctf_track_enumerator (ctf_dict_t *fp, ctf_id_t type, const char *cte_name) +{ + int err; + + if (ctf_dynhash_lookup_type (fp->ctf_names, cte_name) == 0) + { + uint32_t name = ctf_str_add (fp, cte_name); + + if (name == 0) + return -1; /* errno is set for us. */ + + err = ctf_dynhash_insert_type (fp, fp->ctf_names, type, name); + } + else + { + err = ctf_dynset_insert (fp->ctf_conflicting_enums, (void *) + cte_name); + if (err != 0) + ctf_set_errno (fp, err * -1); + } + if (err != 0) + return -1; /* errno is set for us. */ + return 0; +} + typedef struct ctf_bundle { ctf_dict_t *ctb_dict; /* CTF dict handle. */ diff --git a/libctf/ctf-dedup.c b/libctf/ctf-dedup.c index 4243bde16f7..be423c43b5c 100644 --- a/libctf/ctf-dedup.c +++ b/libctf/ctf-dedup.c @@ -2683,6 +2683,7 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, return ctf_set_errno (output, err); } + target->ctf_flags |= LCTF_STRICT_NO_DUP_ENUMERATORS; ctf_import_unref (target, output); if (ctf_cuname (input) != NULL) ctf_cuname_set (target, ctf_cuname (input)); diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index 0d88e639884..6373ecb7a79 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -602,7 +602,8 @@ struct ctf_next ((fp)->ctf_dictops->ctfo_get_vbytes(fp, kind, size, vlen)) #define LCTF_CHILD 0x0001 /* CTF dict is a child. */ -#define LCTF_LINKING 0x0002 /* CTF link is underway: respect ctf_link_flags. */ +#define LCTF_LINKING 0x0002 /* CTF link is underway: respect ctf_link_flags. */ +#define LCTF_STRICT_NO_DUP_ENUMERATORS 0x0004 /* Duplicate enums prohibited. */ 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); @@ -713,6 +714,8 @@ extern int ctf_add_variable_forced (ctf_dict_t *, const char *, ctf_id_t); extern int ctf_add_funcobjt_sym_forced (ctf_dict_t *, int is_function, const char *, ctf_id_t); +extern int ctf_track_enumerator (ctf_dict_t *, ctf_id_t, const char *); + extern int ctf_dedup_atoms_init (ctf_dict_t *); extern int ctf_dedup (ctf_dict_t *, ctf_dict_t **, uint32_t ninputs, int cu_mapped); diff --git a/libctf/ctf-link.c b/libctf/ctf-link.c index 3bfc36ed9e1..0d122e6d366 100644 --- a/libctf/ctf-link.c +++ b/libctf/ctf-link.c @@ -336,6 +336,8 @@ ctf_create_per_cu (ctf_dict_t *fp, ctf_dict_t *input, const char *cu_name) return NULL; } + /* The deduplicator is ready for strict enumerator value checking. */ + cu_fp->ctf_flags |= LCTF_STRICT_NO_DUP_ENUMERATORS; ctf_import_unref (cu_fp, fp); if ((dynname = ctf_new_per_cu_name (fp, ctf_name)) == NULL) @@ -1233,6 +1235,9 @@ ctf_link_deduplicating_per_cu (ctf_dict_t *fp) goto err_inputs; } + /* The deduplicator is ready for strict enumerator value checking. */ + out->ctf_flags |= LCTF_STRICT_NO_DUP_ENUMERATORS; + /* Share the atoms table to reduce memory usage. */ out->ctf_dedup_atoms = fp->ctf_dedup_atoms_alloc; @@ -1498,6 +1503,7 @@ int ctf_link (ctf_dict_t *fp, int flags) { int err; + int oldflags = fp->ctf_flags; fp->ctf_link_flags = flags; @@ -1515,9 +1521,9 @@ ctf_link (ctf_dict_t *fp, int flags) if (fp->ctf_link_outputs == NULL) return ctf_set_errno (fp, ENOMEM); - fp->ctf_flags |= LCTF_LINKING; + fp->ctf_flags |= LCTF_LINKING & LCTF_STRICT_NO_DUP_ENUMERATORS; ctf_link_deduplicating (fp); - fp->ctf_flags &= ~LCTF_LINKING; + fp->ctf_flags = oldflags; if ((ctf_errno (fp) != 0) && (ctf_errno (fp) != ECTF_NOCTFDATA)) return -1; @@ -1537,14 +1543,14 @@ ctf_link (ctf_dict_t *fp, int flags) const char *to = (const char *) k; if (ctf_create_per_cu (fp, NULL, to) == NULL) { - fp->ctf_flags &= ~LCTF_LINKING; + fp->ctf_flags = oldflags; ctf_next_destroy (i); return -1; /* Errno is set for us. */ } } if (err != ECTF_NEXT_END) { - fp->ctf_flags &= ~LCTF_LINKING; + fp->ctf_flags = oldflags; ctf_err_warn (fp, 1, err, _("iteration error creating empty CUs")); return ctf_set_errno (fp, err); } @@ -2040,9 +2046,14 @@ ctf_link_write (ctf_dict_t *fp, size_t *size, size_t threshold) goto err_no; } - /* Turn off the is-linking flag on all the dicts in this link. */ + /* Turn off the is-linking flag on all the dicts in this link: if the strict enum + checking flag is off on the parent, turn it off on all the children too. */ for (i = 0; i < arg.i; i++) - arg.files[i]->ctf_flags &= ~LCTF_LINKING; + { + arg.files[i]->ctf_flags &= ~LCTF_LINKING; + if (!(fp->ctf_flags & LCTF_STRICT_NO_DUP_ENUMERATORS)) + arg.files[i]->ctf_flags &= ~LCTF_STRICT_NO_DUP_ENUMERATORS; + } *size = fsize; free (arg.names); @@ -2061,9 +2072,13 @@ ctf_link_write (ctf_dict_t *fp, size_t *size, size_t threshold) err_no: ctf_set_errno (fp, errno); - /* Turn off the is-linking flag on all the dicts in this link. */ + /* Turn off the is-linking flag on all the dicts in this link, as above. */ for (i = 0; i < arg.i; i++) - arg.files[i]->ctf_flags &= ~LCTF_LINKING; + { + arg.files[i]->ctf_flags &= ~LCTF_LINKING; + if (!(fp->ctf_flags & LCTF_STRICT_NO_DUP_ENUMERATORS)) + arg.files[i]->ctf_flags &= ~LCTF_STRICT_NO_DUP_ENUMERATORS; + } err: free (buf); if (f) diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c index ab97cde3ebf..3f624987f90 100644 --- a/libctf/ctf-open.c +++ b/libctf/ctf-open.c @@ -1024,9 +1024,8 @@ init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth, ctf_dprintf ("%lu total types processed\n", fp->ctf_typemax); - /* In the third pass, we traverse the enums we spotted earlier and add all - the enumeration constants therein either to the types table (if no - type exists with that name) or to ctf_conflciting_enums (otherwise). + /* In the third pass, we traverse the enums we spotted earlier and track all + the enumeration constants to aid in future detection of duplicates. Doing this in a third pass is necessary to avoid the case where an enum appears with a constant FOO, then later a type named FOO appears, @@ -1040,36 +1039,12 @@ init_static_types_internal (ctf_dict_t *fp, ctf_header_t *cth, while ((cte_name = ctf_enum_next (fp, enum_id, &i_constants, NULL)) != NULL) { - /* Add all the enumeration constants as identifiers. They all appear - as types that cite the original enum. - - Constants that appear in more than one enum, or which are already - the names of types, appear in ctf_conflicting_enums as well. */ - - if (ctf_dynhash_lookup_type (fp->ctf_names, cte_name) == 0) + if (ctf_track_enumerator (fp, enum_id, cte_name) < 0) { - uint32_t name = ctf_str_add (fp, cte_name); - - if (name == 0) - goto enum_err; - - err = ctf_dynhash_insert_type (fp, fp->ctf_names, enum_id, name); + ctf_next_destroy (i_constants); + ctf_next_destroy (i); + return ctf_errno (fp); } - else - { - err = ctf_dynset_insert (fp->ctf_conflicting_enums, (void *) - cte_name); - - if (err != 0) - goto enum_err; - } - continue; - - enum_err: - ctf_set_errno (fp, err); - ctf_next_destroy (i_constants); - ctf_next_destroy (i); - return ctf_errno (fp); } if (ctf_errno (fp) != ECTF_NEXT_END) { diff --git a/libctf/ctf-subr.c b/libctf/ctf-subr.c index 6dbf3e7231a..cdcbe960bde 100644 --- a/libctf/ctf-subr.c +++ b/libctf/ctf-subr.c @@ -152,6 +152,43 @@ ctf_version (int version) return _libctf_version; } +/* Get and set CTF dict-wide flags. We are fairly strict about returning + errors here, to make it easier to determine programmatically which flags are + valid. */ + +int +ctf_dict_set_flag (ctf_dict_t *fp, uint64_t flag, int set) +{ + if (set < 0 || set > 1) + return (ctf_set_errno (fp, ECTF_BADFLAG)); + + switch (flag) + { + case CTF_STRICT_NO_DUP_ENUMERATORS: + if (set) + fp->ctf_flags |= LCTF_STRICT_NO_DUP_ENUMERATORS; + else + fp->ctf_flags &= ~LCTF_STRICT_NO_DUP_ENUMERATORS; + break; + default: + return (ctf_set_errno (fp, ECTF_BADFLAG)); + } + return 0; +} + +int +ctf_dict_get_flag (ctf_dict_t *fp, uint64_t flag) +{ + switch (flag) + { + case CTF_STRICT_NO_DUP_ENUMERATORS: + return (fp->ctf_flags & LCTF_STRICT_NO_DUP_ENUMERATORS) != 0; + default: + return (ctf_set_errno (fp, ECTF_BADFLAG)); + } + return 0; +} + void libctf_init_debug (void) { diff --git a/libctf/libctf.ver b/libctf/libctf.ver index e6c31ff37aa..1c85624cfc8 100644 --- a/libctf/libctf.ver +++ b/libctf/libctf.ver @@ -204,4 +204,6 @@ LIBCTF_1.3 { ctf_lookup_enumerator; ctf_lookup_enumerator_next; ctf_arc_lookup_enumerator_next; + ctf_dict_set_flag; + ctf_dict_get_flag; } LIBCTF_1.2; diff --git a/libctf/testsuite/libctf-lookup/enumerator-iteration.c b/libctf/testsuite/libctf-lookup/enumerator-iteration.c index 78cb5a3cc0d..9d26421c92c 100644 --- a/libctf/testsuite/libctf-lookup/enumerator-iteration.c +++ b/libctf/testsuite/libctf-lookup/enumerator-iteration.c @@ -56,19 +56,40 @@ main (int argc, char *argv[]) if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL) goto open_err; - /* Look for all instances of ENUMSAMPLE2_1, and add some new enums to all + /* Look for all instances of ENUMSAMPLE2_2, and add some new enums to all dicts found, to test dynamic enum iteration as well as static. Add two enums with a different name and constants to any that should already be there (one hidden), and one with the same constants, but hidden, to test ctf_lookup_enumerator_next()'s multiple-lookup functionality and - ctf_lookup_enumerator() in the presence of hidden types. */ + ctf_lookup_enumerator() in the presence of hidden types. + + This also tests that you can add to enums under iteration without causing + disaster. */ printf ("First iteration: addition of enums.\n"); while ((type = ctf_arc_lookup_enumerator_next (ctf, "IENUMSAMPLE2_2", &i, &val, &fp, &err)) != CTF_ERR) { char *foo; + int dynadd2_value; + int old_dynadd2_flag; + + /* Make sure that getting and setting a garbage flag, and setting one to a + garbage value, fails properly. */ + if (ctf_dict_set_flag (fp, CTF_STRICT_NO_DUP_ENUMERATORS, 666) >= 0 + || ctf_errno (fp) != ECTF_BADFLAG) + fprintf (stderr, "Invalid flag value setting did not fail as it ought to\n"); + + if (ctf_dict_set_flag (fp, 0, 1) >= 0 || ctf_errno (fp) != ECTF_BADFLAG) + fprintf (stderr, "Invalid flag setting did not fail as it ought to\n"); + + if (ctf_dict_get_flag (fp, 0) >= 0 || ctf_errno (fp) != ECTF_BADFLAG) + fprintf (stderr, "Invalid flag getting did not fail as it ought to\n"); + + /* Set it strict for now. */ + if (ctf_dict_set_flag (fp, CTF_STRICT_NO_DUP_ENUMERATORS, 1) < 0) + goto set_flag_err; printf ("IENUMSAMPLE2_2 in %s has value %li\n", foo = ctf_type_aname (fp, type), (long int) val); @@ -79,8 +100,10 @@ main (int argc, char *argv[]) if (ctf_add_enumerator (fp, type, "DYNADD", counter += 10) < 0) goto enumerator_add_err; + if (ctf_add_enumerator (fp, type, "DYNADD2", counter += 10) < 0) goto enumerator_add_err; + dynadd2_value = counter; /* Make sure that overlapping enumerator addition fails as it should. */ @@ -88,6 +111,32 @@ main (int argc, char *argv[]) || ctf_errno (fp) != ECTF_DUPLICATE) fprintf (stderr, "Duplicate enumerator addition did not fail as it ought to\n"); + /* Make sure that it still fails if you set an enum value to the value it + already has. */ + if (ctf_add_enumerator (fp, type, "DYNADD2", dynadd2_value) >= 0 + || ctf_errno (fp) != ECTF_DUPLICATE) + fprintf (stderr, "Duplicate enumerator addition did not fail as it ought to\n"); + + /* Flip the strict flag and try again. This time, it should succeed. */ + + if ((old_dynadd2_flag = ctf_dict_get_flag (fp, CTF_STRICT_NO_DUP_ENUMERATORS)) < 0) + goto get_flag_err; + + if (ctf_dict_set_flag (fp, CTF_STRICT_NO_DUP_ENUMERATORS, 0) < 0) + goto set_flag_err; + + if (ctf_add_enumerator (fp, type, "DYNADD2", dynadd2_value) < 0) + goto enumerator_add_err; + + /* Flip it again and try *again*. This time it should fail again. */ + + if (ctf_dict_set_flag (fp, CTF_STRICT_NO_DUP_ENUMERATORS, old_dynadd2_flag) < 0) + goto set_flag_err; + + if (ctf_add_enumerator (fp, type, "DYNADD2", dynadd2_value) >= 0 + || ctf_errno (fp) != ECTF_DUPLICATE) + fprintf (stderr, "Duplicate enumerator addition did not fail as it ought to\n"); + if ((type = ctf_add_enum (fp, CTF_ADD_NONROOT, "ie4_hidden")) == CTF_ERR) goto enum_add_err; @@ -104,12 +153,18 @@ main (int argc, char *argv[]) if (ctf_add_enumerator (fp, type, "DYNADD2", counter += 10) < 0) goto enumerator_add_err; - /* Look them up via ctf_lookup_enumerator. */ + /* Look them up via ctf_lookup_enumerator. DYNADD2 should fail because + it has duplicate enumerators. */ if (ctf_lookup_enumerator (fp, "DYNADD", &val) == CTF_ERR) goto enumerator_lookup_err; printf ("direct lookup: DYNADD value: %i\n", (int) val); + if ((err = ctf_lookup_enumerator (fp, "DYNADD2", &val)) >= 0 || + ctf_errno (fp) != ECTF_DUPLICATE) + fprintf (stderr, "Duplicate enumerator lookup did not fail as it ought to: %i, %s\n", + err, ctf_errmsg (ctf_errno (fp))); + if ((type = ctf_lookup_enumerator (fp, "DYNADD3", &val) != CTF_ERR) || ctf_errno (fp) != ECTF_NOENUMNAM) { @@ -164,4 +219,10 @@ main (int argc, char *argv[]) fprintf (stderr, "Cannot look up enumerator in dict \"%s\": %s\n", ctf_cuname (fp) ? ctf_cuname (fp) : "(null: parent)", ctf_errmsg (ctf_errno (fp))); return 1; + get_flag_err: + fprintf (stderr, "ctf_dict_get_flag failed: %s\n", ctf_errmsg (ctf_errno (fp))); + return 1; + set_flag_err: + fprintf (stderr, "ctf_dict_set_flag failed: %s\n", ctf_errmsg (ctf_errno (fp))); + return 1; }