diff options
Diffstat (limited to 'libctf')
-rw-r--r-- | libctf/ctf-archive.c | 9 | ||||
-rw-r--r-- | libctf/ctf-create.c | 7 | ||||
-rw-r--r-- | libctf/ctf-dedup.c | 249 | ||||
-rw-r--r-- | libctf/ctf-hash.c | 29 | ||||
-rw-r--r-- | libctf/ctf-impl.h | 16 | ||||
-rw-r--r-- | libctf/ctf-link.c | 22 | ||||
-rw-r--r-- | libctf/ctf-open.c | 2 | ||||
-rw-r--r-- | libctf/ctf-types.c | 53 | ||||
-rw-r--r-- | libctf/doc/ctf-spec.texi | 2 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/add-to-opened.c | 15 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/struct-iteration-ctf.c | 7 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/struct-iteration.c | 18 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/struct-iteration.lk | 10 | ||||
-rw-r--r-- | libctf/testsuite/libctf-writable/ctf-compressed.c | 4 | ||||
-rw-r--r-- | libctf/testsuite/libctf-writable/ctf-nonroot-linking.c | 11 | ||||
-rw-r--r-- | libctf/testsuite/libctf-writable/slice-of-slice.c | 77 | ||||
-rw-r--r-- | libctf/testsuite/libctf-writable/slice-of-slice.lk | 1 |
17 files changed, 441 insertions, 91 deletions
diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c index dc01e2e..6c4595f 100644 --- a/libctf/ctf-archive.c +++ b/libctf/ctf-archive.c @@ -485,6 +485,10 @@ ctf_arc_open_internal (const char *filename, int *errp) is private.) */ arc->ctfa_magic = s.st_size; close (fd); + + if (errp) + *errp = 0; + return arc; err_unmap: @@ -588,6 +592,9 @@ ctf_dict_open_sections (const ctf_archive_t *arc, const char *name, int *errp) { + if (errp) + *errp = 0; + if (arc->ctfi_is_archive) { ctf_dict_t *ret; @@ -761,7 +768,7 @@ ctf_arc_import_parent (const ctf_archive_t *arc, ctf_dict_t *fp, int *errp) { if ((fp->ctf_flags & LCTF_CHILD) && fp->ctf_parname && !fp->ctf_parent) { - int err; + int err = 0; ctf_dict_t *parent = ctf_dict_open_cached ((ctf_archive_t *) arc, fp->ctf_parname, &err); if (errp) diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c index a4d8e7c..25dd44d 100644 --- a/libctf/ctf-create.c +++ b/libctf/ctf-create.c @@ -18,7 +18,6 @@ <http://www.gnu.org/licenses/>. */ #include <ctf-impl.h> -#include <sys/param.h> #include <string.h> #include <unistd.h> @@ -1045,7 +1044,7 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name, int value) { ctf_dict_t *ofp = fp; - ctf_dtdef_t *dtd = ctf_dtd_lookup (fp, enid); + ctf_dtdef_t *dtd; unsigned char *old_vlen; ctf_enum_t *en; @@ -1054,6 +1053,10 @@ ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name, if (name == NULL) return (ctf_set_errno (fp, EINVAL)); + if ((enid = ctf_type_resolve_unsliced (fp, enid)) == CTF_ERR) + return -1; /* errno is set for us. */ + + dtd = ctf_dtd_lookup (fp, enid); if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, enid)) fp = fp->ctf_parent; diff --git a/libctf/ctf-dedup.c b/libctf/ctf-dedup.c index 174f353..c7c2edd 100644 --- a/libctf/ctf-dedup.c +++ b/libctf/ctf-dedup.c @@ -96,9 +96,9 @@ value: it is also stashed in the *output mapping*, a mapping from hash value to the set of GIDs corresponding to that type in all inputs. We also keep track of the GID of the first appearance of the type in any input (in - cd_output_first_gid), and the GID of structs, unions, and forwards that only - appear in one TU (in cd_struct_origin). See below for where these things are - used. + cd_output_first_gid), the GID of structs, unions, and forwards that only + appear in one TU (in cd_struct_origin), and an indication of whether this + type is root-visible or not. See below for where these things are used. Everything in this phase is time-critical, because it is operating over non-deduplicated types and so may have hundreds or thousands of times the @@ -492,6 +492,7 @@ ctf_dedup_hash_type (ctf_dict_t *fp, ctf_dict_t *input, ctf_dict_t **inputs, int input_num, ctf_id_t type, + int isroot, void *id, const char *decorated_name, const char *hash)); @@ -560,6 +561,7 @@ ctf_dedup_rhash_type (ctf_dict_t *fp, ctf_dict_t *input, ctf_dict_t **inputs, ctf_dict_t **inputs, int input_num, ctf_id_t type, + int isroot, void *id, const char *decorated_name, const char *hash)) @@ -666,7 +668,11 @@ ctf_dedup_rhash_type (ctf_dict_t *fp, ctf_dict_t *input, ctf_dict_t **inputs, possible. Equally, we do not want to hash in the isroot flag: both the compiler and the deduplicator set the nonroot flag to indicate clashes with *other types in the same TU* with the same name: so two types can easily - have distinct nonroot flags, yet be exactly the same type.*/ + have distinct nonroot flags, yet be exactly the same type. This means we + can never use the non-root-visible flag from the input for anything, + because if there are several distinct values the one chosen is basically + random. We unify non-root-visible flags separately: see the uses of + cd_nonroot_consistency. */ ctf_sha1_init (&hash); if (name) @@ -1015,6 +1021,7 @@ ctf_dedup_hash_type (ctf_dict_t *fp, ctf_dict_t *input, ctf_dict_t **inputs, int input_num, ctf_id_t type, + int isroot, void *id, const char *decorated_name, const char *hash)) @@ -1027,6 +1034,7 @@ ctf_dedup_hash_type (ctf_dict_t *fp, ctf_dict_t *input, const char *whaterr; const char *decorated = NULL; uint32_t kind, fwdkind; + int isroot; depth++; @@ -1056,6 +1064,7 @@ ctf_dedup_hash_type (ctf_dict_t *fp, ctf_dict_t *input, kind = LCTF_INFO_KIND (input, tp->ctt_info); name = ctf_strraw (input, tp->ctt_name); + isroot = LCTF_INFO_ISROOT (input, tp->ctt_info); if (tp->ctt_name == 0 || !name || name[0] == '\0') name = NULL; @@ -1086,7 +1095,7 @@ ctf_dedup_hash_type (ctf_dict_t *fp, ctf_dict_t *input, ctf_dprintf ("%lu: Known hash for ID %i/%lx: %s\n", depth, input_num, type, hval); #endif - populate_fun (fp, input, inputs, input_num, type, type_id, + populate_fun (fp, input, inputs, input_num, type, isroot, type_id, decorated, hval); return hval; @@ -1121,7 +1130,7 @@ ctf_dedup_hash_type (ctf_dict_t *fp, ctf_dict_t *input, goto oom; } - if (populate_fun (fp, input, inputs, input_num, type, type_id, + if (populate_fun (fp, input, inputs, input_num, type, isroot, type_id, decorated, hval) < 0) { whaterr = N_("error calling population function"); @@ -1150,19 +1159,20 @@ ctf_dedup_count_name (ctf_dict_t *fp, const char *name, void *id); /* Populate a number of useful mappings not directly used by the hashing machinery: the output mapping, the cd_name_counts mapping from name -> hash - -> count of hashval deduplication state for a given hashed type, and the - cd_output_first_tu mapping. */ + -> count of hashval deduplication state for a given hashed type; the + cd_output_first_gid mapping; and the cd_nonroot_consistency mapping. */ static int ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_, ctf_dict_t **inputs _libctf_unused_, int input_num _libctf_unused_, - ctf_id_t type _libctf_unused_, void *id, - const char *decorated_name, + ctf_id_t type _libctf_unused_, int isroot, + void *id, const char *decorated_name, const char *hval) { ctf_dedup_t *d = &fp->ctf_dedup; ctf_dynset_t *type_ids; + void *root_visible; #ifdef ENABLE_LIBCTF_HASH_DEBUGGING ctf_dprintf ("Hash %s, %s, into output mapping for %i/%lx @ %s\n", @@ -1249,6 +1259,32 @@ ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_, } #endif + /* Track the consistency of the non-root flag for this type. + 0: all root-visible; 1: all non-root-visible; 2: inconsistent. */ + + if (!ctf_dynhash_lookup_kv (d->cd_nonroot_consistency, hval, NULL, + &root_visible)) + { + if (isroot) + root_visible = (void *) 0; + else + root_visible = (void *) 1; + + if (ctf_dynhash_cinsert (d->cd_nonroot_consistency, hval, root_visible) < 0) + return ctf_set_errno (fp, errno); + } + else + { + if (((uintptr_t) root_visible == 0 && !isroot) + || ((uintptr_t) root_visible == 1 && isroot)) + { + root_visible = (void *) 2; + + if (ctf_dynhash_cinsert (d->cd_nonroot_consistency, hval, root_visible) < 0) + return ctf_set_errno (fp, errno); + } + } + /* This function will be repeatedly called for the same types many times: don't waste time reinserting the same keys in that case. */ if (!ctf_dynset_exists (type_ids, id, NULL) @@ -1282,6 +1318,33 @@ ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_, return 0; } +/* Clean up things no longer needed after hashing is over. */ +static int +ctf_dedup_hash_type_fini (ctf_dict_t *fp) +{ + ctf_next_t *i = NULL; + int err; + void *hval, *root_visible; + + /* Clean up cd_nonroot_consistency. We only care now about types we are sure + are non-root-visible everywhere: root-visible types and types that are + sometimes root-visible and sometimes not are treated as root-visible. */ + + while ((err = ctf_dynhash_next (fp->ctf_dedup.cd_nonroot_consistency, &i, + &hval, &root_visible)) == 0) + { + if ((uintptr_t) root_visible != 1) + ctf_dynhash_next_remove (&i); + } + if (err != ECTF_NEXT_END) + { + ctf_err_warn (fp, 0, err, _("iteration failure cleaning up type hashes")); + return ctf_set_errno (fp, err); + } + + return 0; +} + static int ctf_dedup_count_name (ctf_dict_t *fp, const char *name, void *id) { @@ -1678,6 +1741,12 @@ ctf_dedup_init (ctf_dict_t *fp) NULL, NULL)) == NULL) goto oom; + if ((d->cd_nonroot_consistency + = ctf_dynhash_create (ctf_hash_string, + ctf_hash_eq_string, + NULL, NULL)) == NULL) + goto oom; + #ifdef ENABLE_LIBCTF_HASH_DEBUGGING if ((d->cd_output_mapping_guard = ctf_dynhash_create (ctf_hash_integer, @@ -1730,6 +1799,7 @@ ctf_dedup_fini (ctf_dict_t *fp, ctf_dict_t **outputs, uint32_t noutputs) ctf_dynhash_destroy (d->cd_citers); ctf_dynhash_destroy (d->cd_output_mapping); ctf_dynhash_destroy (d->cd_output_first_gid); + ctf_dynhash_destroy (d->cd_nonroot_consistency); #ifdef ENABLE_LIBCTF_HASH_DEBUGGING ctf_dynhash_destroy (d->cd_output_mapping_guard); #endif @@ -1912,15 +1982,15 @@ ctf_dedup_conflictify_unshared (ctf_dict_t *output, ctf_dict_t **inputs) OUTPUT is the top-level output: INPUTS is the array of input dicts; NINPUTS is the size of that array. - If CU_MAPPED is set, this is a first pass for a link with a non-empty CU - mapping: only one output will result. + If CU_MAPPING_PHASE is nonzero, this is a link with a non-empty CU mapping: + in phase 1, only one output will result. Only deduplicates: does not emit the types into the output. Call ctf_dedup_emit afterwards to do that. */ int ctf_dedup (ctf_dict_t *output, ctf_dict_t **inputs, uint32_t ninputs, - int cu_mapped) + int cu_mapping_phase) { ctf_dedup_t *d = &output->ctf_dedup; size_t i; @@ -1942,12 +2012,13 @@ ctf_dedup (ctf_dict_t *output, ctf_dict_t **inputs, uint32_t ninputs, } } - /* Some flags do not apply when CU-mapping: this is not a duplicated link, - because there is only one output and we really don't want to end up marking - all nonconflicting but appears-only-once types as conflicting (which in the - CU-mapped link means we'd mark them all as non-root-visible!). */ + /* Some flags do not apply in the first phase of CU-mapped links: this is not + a share-duplicated link, because there is only one output and we really + don't want to end up marking all nonconflicting but appears-only-once types + as conflicting. */ + d->cd_link_flags = output->ctf_link_flags; - if (cu_mapped) + if (cu_mapping_phase == 1) d->cd_link_flags &= ~(CTF_LINK_SHARE_DUPLICATED); /* Compute hash values for all types, recursively, treating child structures @@ -1979,6 +2050,10 @@ ctf_dedup (ctf_dict_t *output, ctf_dict_t **inputs, uint32_t ninputs, } } + /* Drop state no longer needed after hashing is over. */ + + ctf_dedup_hash_type_fini (output); + /* Go through the cd_name_counts name->hash->count mapping for all CTF namespaces: any name with many hashes associated with it at this stage is necessarily ambiguous. Mark all the hashes except the most common as @@ -2638,8 +2713,8 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, const ctf_type_t *tp; int input_num = CTF_DEDUP_GID_TO_INPUT (id); int output_num = (uint32_t) -1; /* 'shared' */ - int cu_mapped = *(int *)arg; - int isroot; + int cu_mapping_phase = *(int *)arg; + int isroot = 1; int is_conflicting; ctf_next_t *i = NULL; @@ -2663,7 +2738,7 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, ctf_link_outputs dict of the output that is its parent. */ is_conflicting = ctf_dynset_exists (d->cd_conflicting_types, hval, NULL); - if (is_conflicting && !cu_mapped) + if (is_conflicting && cu_mapping_phase != 1) { ctf_dprintf ("%i: Type %s in %i/%lx is conflicted: " "inserting into per-CU target.\n", @@ -2698,6 +2773,34 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, output_num = input_num; } + if (!target->ctf_dedup.cd_output_emission_hashes) + if ((target->ctf_dedup.cd_output_emission_hashes + = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string, + NULL, NULL)) == NULL) + goto oom_hash; + + if (!target->ctf_dedup.cd_output_emission_conflicted_forwards) + if ((target->ctf_dedup.cd_output_emission_conflicted_forwards + = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string, + NULL, NULL)) == NULL) + goto oom_hash; + + /* When cu-mapping mode is turned on, we merge types derived from multiple CUs + into one target dict: in phase 1, by merging them according to the mapping; + in phase 2, as a consequence of taking the merged results from phase 1. + Any given type appears only once in the type mapping, but in + ctf_dedup_rwalk_output_mapping we loop inserting conflicting types into a + child dict corresponding to every input dict they came from. This means + that if those dicts are mapped together, in phase 1 we can attempt to + insert them *multiple times* into the same dict, which then causes them to + be duplicated in phase 2 as well. Avoid this by making sure this hval + isn't already present in the emission hash in phase 1: if it is, we in + effect already visited this type, and can return as we did above. */ + + if (cu_mapping_phase == 1 + && ctf_dynhash_lookup (target->ctf_dedup.cd_output_emission_hashes, hval)) + return 0; + real_input = input; if ((tp = ctf_lookup_by_id (&real_input, type)) == NULL) { @@ -2708,35 +2811,64 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, } name = ctf_strraw (real_input, tp->ctt_name); - isroot = LCTF_INFO_ISROOT (real_input, tp->ctt_info); - /* Hide conflicting types, if we were asked to: also hide if a type with this - name already exists and is not a forward, or if this type is hidden on the - input. */ - if (cu_mapped && is_conflicting) - isroot = 0; - else if (name - && (maybe_dup = ctf_lookup_by_rawname (target, kind, name)) != 0) + /* cu_mapped links at phase 1 get absolutely *everything* marked non-root, + named or not. Such links, when we are merging multiple child CUs into one, + are the only point at which we can ever put conflicting and nonconflicting + instances of the same type into the same dict, and which one comes first is + arbitrary. Rather than having to figure out when we insert a type whether + another one is coming that might conflict with it without being so marked, + just mark everything as non-root: we'll disregard it in the next phase of + cu-mapped linking anyway. + + In phase 2 (the final dedup phase) of cu-mapped links, we have to deal with + the fallout of this, in that single inputs have 100% non-root types (so the + non-root bit isn't really meaningful) but some subset of them may be + genuinely clashing, conflicting, but already in child dicts (a thing that + is impossible in non-CU-mapped links, when child dicts correspond to single + CUs). + + So in phase 2, we hide conflicting types, if this type is conflicting and a + type with this name already exists in the target and is not a forward. + + Note that enums also get their enumerands checked, below. + + Otherwise, in "phase 0" (i.e. normal links), we can respect the non-root + flag the user passed in and simply propagate it directly to the output. + If the user provided a mix of root-visible and non-root-visible flags, + we treat it as non-root-visible: see ctf_dedup_hash_type_fini. */ + + switch (cu_mapping_phase) { - if (ctf_type_kind (target, maybe_dup) != CTF_K_FORWARD) + case 0: /* Normal link. Root-visibility explicitly tracked. */ + if (ctf_dynhash_lookup (d->cd_nonroot_consistency, hval)) isroot = 0; + break; + case 1: /* cu-mapped link. Never root-visible. */ + isroot = 0; + break; + case 2: /* Final phase of cu-mapped link. Non-root if already present. */ + if (is_conflicting && name + && ((maybe_dup = ctf_lookup_by_rawname (target, kind, name)) != 0)) + { + if (ctf_type_kind (target, maybe_dup) != CTF_K_FORWARD) + { + ctf_dprintf ("%s, kind %i, hval %s: conflicting type marked as " + "non-root because of pre-existing type %s/%lx, " + "kind %i.\n", name, kind, hval, ctf_cuname (target), + maybe_dup, ctf_type_kind (target, maybe_dup)); + isroot = 0; + } + } + break; + default: + if (!ctf_assert (output, cu_mapping_phase >= 0 && cu_mapping_phase <= 2)) + return -1; /* errno is set for us. */ } ctf_dprintf ("%i: Emitting type with hash %s (%s), into target %i/%p\n", depth, hval, name ? name : "", input_num, (void *) target); - if (!target->ctf_dedup.cd_output_emission_hashes) - if ((target->ctf_dedup.cd_output_emission_hashes - = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string, - NULL, NULL)) == NULL) - goto oom_hash; - - if (!target->ctf_dedup.cd_output_emission_conflicted_forwards) - if ((target->ctf_dedup.cd_output_emission_conflicted_forwards - = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string, - NULL, NULL)) == NULL) - goto oom_hash; - switch (kind) { case CTF_K_UNKNOWN: @@ -2771,6 +2903,28 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, { int val; errtype = _("enum"); + + /* Check enumerands for duplication and nonrootify if clashing: this is + an extension of the isroot check above. */ + + if (isroot && cu_mapping_phase == 2) + { + const char *enumerand; + while ((enumerand = ctf_enum_next (input, type, &i, &val)) != NULL) + { + if (is_conflicting && name + && ctf_dynhash_lookup (target->ctf_names, enumerand) != NULL) + { + ctf_dprintf ("%s, kind %i, hval %s: conflicting type marked " + "as non-root because of pre-existing enumerand " + "%s.\n", name, kind, hval, enumerand); + isroot = 0; + } + } + if (ctf_errno (input) != ECTF_NEXT_END) + goto err_input; + } + if ((new_type = ctf_add_enum (target, isroot, name)) == CTF_ERR) goto err_input; /* errno is set for us. */ @@ -3086,12 +3240,12 @@ ctf_dedup_emit_struct_members (ctf_dict_t *output, ctf_dict_t **inputs, Return an array of fps with content emitted into them (starting with OUTPUT, which is the parent of all others, then all the newly-generated outputs). - If CU_MAPPED is set, this is a first pass for a link with a non-empty CU - mapping: only one output will result. */ + If CU_MAPPING_PHASE is set to 1, this is a first pass for a link with a + non-empty CU mapping: only one output will result. */ ctf_dict_t ** ctf_dedup_emit (ctf_dict_t *output, ctf_dict_t **inputs, uint32_t ninputs, - uint32_t *parents, uint32_t *noutputs, int cu_mapped) + uint32_t *parents, uint32_t *noutputs, int cu_mapping_phase) { size_t num_outputs = 1; /* Always at least one output: us. */ ctf_dict_t **outputs; @@ -3100,7 +3254,7 @@ ctf_dedup_emit (ctf_dict_t *output, ctf_dict_t **inputs, uint32_t ninputs, ctf_dprintf ("Triggering emission.\n"); if (ctf_dedup_walk_output_mapping (output, inputs, ninputs, parents, - ctf_dedup_emit_type, &cu_mapped) < 0) + ctf_dedup_emit_type, &cu_mapping_phase) < 0) return NULL; /* errno is set for us. */ ctf_dprintf ("Populating struct members.\n"); @@ -3113,7 +3267,8 @@ ctf_dedup_emit (ctf_dict_t *output, ctf_dict_t **inputs, uint32_t ninputs, num_outputs++; } - if (!ctf_assert (output, !cu_mapped || (cu_mapped && num_outputs == 1))) + if (!ctf_assert (output, (cu_mapping_phase != 1 + || (cu_mapping_phase == 1 && num_outputs == 1)))) return NULL; if ((outputs = calloc (num_outputs, sizeof (ctf_dict_t *))) == NULL) diff --git a/libctf/ctf-hash.c b/libctf/ctf-hash.c index a11d11e..abd0140 100644 --- a/libctf/ctf-hash.c +++ b/libctf/ctf-hash.c @@ -406,8 +406,9 @@ ctf_dynhash_iter_remove (ctf_dynhash_t *hp, ctf_hash_iter_remove_f fun, /* Traverse a dynhash in arbitrary order, in _next iterator form. - Mutating the dynhash while iterating is not supported (just as it isn't for - htab_traverse). + Adding entries to the dynhash while iterating is not supported (just as it + isn't for htab_traverse). Deleting is fine (see ctf_dynhash_next_remove, + below). Note: unusually, this returns zero on success and a *positive* value on error, because it does not take an fp, taking an error pointer would be @@ -479,6 +480,30 @@ ctf_dynhash_next (ctf_dynhash_t *h, ctf_next_t **it, void **key, void **value) return ECTF_NEXT_END; } +/* Remove the entry most recently returned by ctf_dynhash_next. + + Returns ECTF_NEXT_END if this is impossible (already removed, iterator not + initialized, iterator off the end). */ + +int +ctf_dynhash_next_remove (ctf_next_t * const *it) +{ + ctf_next_t *i = *it; + + if ((void (*) (void)) ctf_dynhash_next != i->ctn_iter_fun) + return ECTF_NEXT_WRONGFUN; + + if (!i) + return ECTF_NEXT_END; + + if (i->ctn_n == 0) + return ECTF_NEXT_END; + + htab_clear_slot (i->cu.ctn_h->htab, i->u.ctn_hash_slot - 1); + + return 0; +} + int ctf_dynhash_sort_by_name (const ctf_next_hkv_t *one, const ctf_next_hkv_t *two, void *unused _libctf_unused_) diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index b40c867..3217f92 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -22,7 +22,6 @@ #include "config.h" #include <errno.h> -#include <sys/param.h> #include "ctf-decls.h" #include <ctf-api.h> #include "ctf-sha1.h" @@ -67,8 +66,13 @@ extern "C" macros glibc may introduce, which have names of the pattern __attribute_blah__. */ +#if defined (__clang__) #define _libctf_printflike_(string_index,first_to_check) \ __attribute__ ((__format__ (__printf__, (string_index), (first_to_check)))) +#else +#define _libctf_printflike_(string_index,first_to_check) \ + __attribute__ ((__format__ (__gnu_printf__, (string_index), (first_to_check)))) +#endif #define _libctf_unlikely_(x) __builtin_expect ((x), 0) #define _libctf_unused_ __attribute__ ((__unused__)) #define _libctf_malloc_ __attribute__((__malloc__)) @@ -332,6 +336,11 @@ typedef struct ctf_dedup hash. */ ctf_dynhash_t *cd_output_mapping_guard; + /* Maps from type hash values to an indication of their nonroot flag. 0 means + all root-visible; 1 means non-root-visible; 2 means a mixture. All values + other than 1 are deleted after hashing. */ + ctf_dynhash_t *cd_nonroot_consistency; + /* Maps the global type IDs of structures in input TUs whose members still need emission to the global type ID of the already-emitted target type (which has no members yet) in the appropriate target. Uniquely, the latter @@ -672,6 +681,7 @@ extern int ctf_dynhash_next (ctf_dynhash_t *, ctf_next_t **, extern int ctf_dynhash_next_sorted (ctf_dynhash_t *, ctf_next_t **, void **key, void **value, ctf_hash_sort_f, void *); +extern int ctf_dynhash_next_remove (ctf_next_t * const *); extern ctf_dynset_t *ctf_dynset_create (htab_hash, htab_eq, ctf_hash_free_fun); extern int ctf_dynset_insert (ctf_dynset_t *, void *); @@ -718,10 +728,10 @@ 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); + int cu_mapped_phase); extern ctf_dict_t **ctf_dedup_emit (ctf_dict_t *, ctf_dict_t **, uint32_t ninputs, uint32_t *parents, - uint32_t *noutputs, int cu_mapped); + uint32_t *noutputs, int cu_mapped_phase); extern void ctf_dedup_fini (ctf_dict_t *, ctf_dict_t **, uint32_t); extern ctf_id_t ctf_dedup_type_mapping (ctf_dict_t *fp, ctf_dict_t *src_fp, ctf_id_t src_type); diff --git a/libctf/ctf-link.c b/libctf/ctf-link.c index 2019c11..2d4401d 100644 --- a/libctf/ctf-link.c +++ b/libctf/ctf-link.c @@ -1385,6 +1385,7 @@ ctf_link_deduplicating (ctf_dict_t *fp) ssize_t ninputs; uint32_t noutputs; uint32_t *parents; + int cu_phase = 0; if (ctf_dedup_atoms_init (fp) < 0) { @@ -1392,9 +1393,20 @@ ctf_link_deduplicating (ctf_dict_t *fp) return; /* Errno is set for us. */ } - if (fp->ctf_link_out_cu_mapping - && (ctf_link_deduplicating_per_cu (fp) < 0)) - return; /* Errno is set for us. */ + /* Trigger a CU-mapped link if need be: one pass of dedups squashing inputs into + single child dicts corresponding to each CU mapping, and one pass that + treats those as if they are ordinary inputs and links them together. + + This latter pass does need to act very slightly differently from normal, so we + keep track of the "CU phase", with 0 being a normal link, 1 being the + squash-together phase, and 2 being the final act-as-if-it-were-normal pass. */ + + if (fp->ctf_link_out_cu_mapping) + { + if (ctf_link_deduplicating_per_cu (fp) < 0) + return; /* Errno is set for us. */ + cu_phase = 2; + } if ((ninputs = ctf_link_deduplicating_count_inputs (fp, NULL, NULL)) < 0) return; /* Errno is set for us. */ @@ -1406,7 +1418,7 @@ ctf_link_deduplicating (ctf_dict_t *fp) if (ninputs == 1 && ctf_cuname (inputs[0]) != NULL) ctf_cuname_set (fp, ctf_cuname (inputs[0])); - if (ctf_dedup (fp, inputs, ninputs, 0) < 0) + if (ctf_dedup (fp, inputs, ninputs, cu_phase) < 0) { ctf_err_warn (fp, 0, 0, _("deduplication failed for %s"), ctf_link_input_name (fp)); @@ -1414,7 +1426,7 @@ ctf_link_deduplicating (ctf_dict_t *fp) } if ((outputs = ctf_dedup_emit (fp, inputs, ninputs, parents, &noutputs, - 0)) == NULL) + cu_phase)) == NULL) { ctf_err_warn (fp, 0, 0, _("deduplicating link type emission failed " "for %s"), ctf_link_input_name (fp)); diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c index 449728f..8c08667 100644 --- a/libctf/ctf-open.c +++ b/libctf/ctf-open.c @@ -1426,6 +1426,8 @@ ctf_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, libctf_init_debug(); + ctf_set_open_errno (errp, 0); + if ((ctfsect == NULL) || ((symsect != NULL) && (strsect == NULL))) return (ctf_set_open_errno (errp, EINVAL)); diff --git a/libctf/ctf-types.c b/libctf/ctf-types.c index 45200b7..044a185 100644 --- a/libctf/ctf-types.c +++ b/libctf/ctf-types.c @@ -187,10 +187,21 @@ ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it, *membtype = memb.ctlm_type; offset = (unsigned long) CTF_LMEM_OFFSET (&memb); - if (membname[0] == 0 - && (ctf_type_kind (fp, memb.ctlm_type) == CTF_K_STRUCT - || ctf_type_kind (fp, memb.ctlm_type) == CTF_K_UNION)) - i->ctn_type = memb.ctlm_type; + if (membname[0] == 0) + { + ctf_id_t resolved; + + if ((resolved = ctf_type_resolve (fp, memb.ctlm_type)) == CTF_ERR) + { + if (ctf_errno (fp) != ECTF_NONREPRESENTABLE) + return -1; /* errno is set for us. */ + resolved = memb.ctlm_type; + } + + if (ctf_type_kind (fp, resolved) == CTF_K_STRUCT + || ctf_type_kind (fp, resolved) == CTF_K_UNION) + i->ctn_type = resolved; + } i->ctn_n++; /* The callers might want automatic recursive sub-struct traversal. */ @@ -594,21 +605,31 @@ ctf_type_resolve_unsliced (ctf_dict_t *fp, ctf_id_t type) { ctf_dict_t *ofp = fp; const ctf_type_t *tp; + ctf_id_t resolved_type; if ((type = ctf_type_resolve (fp, type)) == CTF_ERR) return CTF_ERR; if ((tp = ctf_lookup_by_id (&fp, type)) == NULL) return CTF_ERR; /* errno is set for us. */ + resolved_type = type; - if ((LCTF_INFO_KIND (fp, tp->ctt_info)) == CTF_K_SLICE) + do { - ctf_id_t ret; + type = resolved_type; - if ((ret = ctf_type_reference (fp, type)) == CTF_ERR) - return (ctf_set_typed_errno (ofp, ctf_errno (fp))); - return ret; + if ((LCTF_INFO_KIND (fp, tp->ctt_info)) == CTF_K_SLICE) + if ((type = ctf_type_reference (fp, type)) == CTF_ERR) + return (ctf_set_typed_errno (ofp, ctf_errno (fp))); + + if ((resolved_type = ctf_type_resolve (fp, type)) == CTF_ERR) + return CTF_ERR; + + if ((tp = ctf_lookup_by_id (&fp, resolved_type)) == NULL) + return CTF_ERR; /* errno is set for us. */ } + while (LCTF_INFO_KIND (fp, tp->ctt_info) == CTF_K_SLICE); + return type; } @@ -1396,16 +1417,24 @@ ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name, { ctf_lmember_t memb; const char *membname; + ctf_id_t resolved; if (ctf_struct_member (fp, &memb, tp, vlen, vbytes, i) < 0) return (ctf_set_errno (ofp, ctf_errno (fp))); membname = ctf_strptr (fp, memb.ctlm_name); + if ((resolved = ctf_type_resolve (fp, memb.ctlm_type)) == CTF_ERR) + { + if (ctf_errno (fp) != ECTF_NONREPRESENTABLE) + return (ctf_set_errno (ofp, ctf_errno (fp))); + resolved = memb.ctlm_type; + } + if (membname[0] == 0 - && (ctf_type_kind (fp, memb.ctlm_type) == CTF_K_STRUCT - || ctf_type_kind (fp, memb.ctlm_type) == CTF_K_UNION) - && (ctf_member_info (fp, memb.ctlm_type, name, mip) == 0)) + && (ctf_type_kind (fp, resolved) == CTF_K_STRUCT + || ctf_type_kind (fp, resolved) == CTF_K_UNION) + && (ctf_member_info (fp, resolved, name, mip) == 0)) { mip->ctm_offset += (unsigned long) CTF_LMEM_OFFSET (&memb); return 0; diff --git a/libctf/doc/ctf-spec.texi b/libctf/doc/ctf-spec.texi index a79ee5e..5b2b881 100644 --- a/libctf/doc/ctf-spec.texi +++ b/libctf/doc/ctf-spec.texi @@ -806,7 +806,7 @@ just very frequently @emph{referenced}.) Here's the set of kinds so far. Each kind has a @code{#define} associated with it, also given here. -@multitable {Kind} {@code{CTF_K_VOLATILE}} {Indicates a type that cannot be represented in CTF, or that} {@xref{Pointers typedefs and cvr-quals}} +@multitable {Kind} {@code{CTF_K_VOLATILE}} {Indicates a type that cannot be represented in CTF, or that} @headitem Kind @tab Macro @tab Purpose @item 0 @tab @code{CTF_K_UNKNOWN} diff --git a/libctf/testsuite/libctf-lookup/add-to-opened.c b/libctf/testsuite/libctf-lookup/add-to-opened.c index 96629af..700257e 100644 --- a/libctf/testsuite/libctf-lookup/add-to-opened.c +++ b/libctf/testsuite/libctf-lookup/add-to-opened.c @@ -15,7 +15,7 @@ main (int argc, char *argv[]) ctf_encoding_t en = { CTF_INT_SIGNED, 0, sizeof (int) }; unsigned char *ctf_written; size_t size; - int err; + int err = 666; if (argc != 2) { @@ -25,9 +25,18 @@ main (int argc, char *argv[]) if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL) goto open_err; + + /* The error int should be reset on success as well as on error. */ + if (err != 0) + goto err_err; + + err = 666; if ((fp = ctf_dict_open (ctf, NULL, &err)) == NULL) goto open_err; + if (err != 0) + goto err_err; + /* Check that various modifications to already-written types are prohibited. */ @@ -145,4 +154,8 @@ main (int argc, char *argv[]) open_err: fprintf (stderr, "%s: cannot open: %s\n", argv[0], ctf_errmsg (err)); return 1; + + err_err: + fprintf (stderr, "%s: open error not set to success on success\n", argv[0]); + return 1; } diff --git a/libctf/testsuite/libctf-lookup/struct-iteration-ctf.c b/libctf/testsuite/libctf-lookup/struct-iteration-ctf.c index 7df67ad..0a50d2c 100644 --- a/libctf/testsuite/libctf-lookup/struct-iteration-ctf.c +++ b/libctf/testsuite/libctf-lookup/struct-iteration-ctf.c @@ -6,12 +6,12 @@ struct foo_t size_t bar; const char *baz; struct foo_t *self; - union + const volatile union { double should_not_appear; char *nor_should_this; } named; - struct + volatile const struct { long unnamed_sub_member; union @@ -19,6 +19,9 @@ struct foo_t double one_more_level; long yes_really_one_more; }; + struct { + int and_finally; + }; }; struct {}; /* Empty ones */ union {}; diff --git a/libctf/testsuite/libctf-lookup/struct-iteration.c b/libctf/testsuite/libctf-lookup/struct-iteration.c index b240864..fd89ca7 100644 --- a/libctf/testsuite/libctf-lookup/struct-iteration.c +++ b/libctf/testsuite/libctf-lookup/struct-iteration.c @@ -51,11 +51,29 @@ main (int argc, char *argv[]) while ((offset = ctf_member_next (fp, type, &i, &name, &membtype, CTF_MN_RECURSE)) >= 0) { + ctf_membinfo_t memb; char *type_name = ctf_type_aname (fp, membtype); printf ("next test: %s, offset %zx, has type %lx/%s\n", name, offset, membtype, type_name); free (type_name); + + /* Check that we can get the same member via ctf_member_info too. */ + if (name[0] != '\0') + { + if (ctf_member_info (fp, type, name, &memb) != 0) + { + fprintf (stderr, "Cannot get member info for %s: %s\n", + name, ctf_errmsg (ctf_errno (fp))); + exit (1); + } + if (memb.ctm_offset != offset || memb.ctm_type != membtype) + { + fprintf (stderr, "ctf_member_info versus iteration comparison " + "failure: types %lx/%lx, offsets %zx/%lx\n", + membtype, memb.ctm_type, offset, memb.ctm_offset); + } + } } if (ctf_errno (fp) != ECTF_NEXT_END) goto nerr; diff --git a/libctf/testsuite/libctf-lookup/struct-iteration.lk b/libctf/testsuite/libctf-lookup/struct-iteration.lk index fd64454..aecffd4 100644 --- a/libctf/testsuite/libctf-lookup/struct-iteration.lk +++ b/libctf/testsuite/libctf-lookup/struct-iteration.lk @@ -4,8 +4,8 @@ iter test: foo, offset [0-9a-f]*, has type [0-9a-f]*/int iter test: bar, offset [0-9a-f]*, has type [0-9a-f]*/size_t iter test: baz, offset [0-9a-f]*, has type [0-9a-f]*/const char \* iter test: self, offset [0-9a-f]*, has type [0-9a-f]*/struct foo_t \* -iter test: named, offset [0-9a-f]*, has type [0-9a-f]*/union -iter test: , offset [0-9a-f]*, has type [0-9a-f]*/struct +iter test: named, offset [0-9a-f]*, has type [0-9a-f]*/volatile const union +iter test: , offset [0-9a-f]*, has type [0-9a-f]*/volatile const struct iter test: , offset [0-9a-f]*, has type [0-9a-f]*/struct iter test: , offset [0-9a-f]*, has type [0-9a-f]*/union iter test: after_the_end, offset [0-9a-f]*, has type [0-9a-f]*/int @@ -13,12 +13,14 @@ next test: foo, offset [0-9a-f]*, has type [0-9a-f]*/int next test: bar, offset [0-9a-f]*, has type [0-9a-f]*/size_t next test: baz, offset [0-9a-f]*, has type [0-9a-f]*/const char \* next test: self, offset [0-9a-f]*, has type [0-9a-f]*/struct foo_t \* -next test: named, offset [0-9a-f]*, has type [0-9a-f]*/union -next test: , offset [0-9a-f]*, has type [0-9a-f]*/struct +next test: named, offset [0-9a-f]*, has type [0-9a-f]*/volatile const union +next test: , offset [0-9a-f]*, has type [0-9a-f]*/volatile const struct next test: unnamed_sub_member, offset [0-9a-f]*, has type [0-9a-f]*/long int next test: , offset [0-9a-f]*, has type [0-9a-f]*/union next test: one_more_level, offset [0-9a-f]*, has type [0-9a-f]*/double next test: yes_really_one_more, offset [0-9a-f]*, has type [0-9a-f]*/long int next test: , offset [0-9a-f]*, has type [0-9a-f]*/struct +next test: and_finally, offset [0-9a-f]*, has type [0-9a-f]*/int +next test: , offset [0-9a-f]*, has type [0-9a-f]*/struct next test: , offset [0-9a-f]*, has type [0-9a-f]*/union next test: after_the_end, offset [0-9a-f]*, has type [0-9a-f]*/int diff --git a/libctf/testsuite/libctf-writable/ctf-compressed.c b/libctf/testsuite/libctf-writable/ctf-compressed.c index 4769cdb..646b603 100644 --- a/libctf/testsuite/libctf-writable/ctf-compressed.c +++ b/libctf/testsuite/libctf-writable/ctf-compressed.c @@ -119,10 +119,14 @@ main (int argc, char *argv[]) /* Dump the header of each archive member, and search for CTF_F_COMPRESS in the resulting dump. */ + err = 666; while ((dump_fp = ctf_archive_next (final_arc, &i, NULL, 0, &err)) != NULL) { char *dumpstr; + if (err != 0) + fprintf (stderr, "err not set to success on success\n"); + while ((dumpstr = ctf_dump (dump_fp, &dump_state, CTF_SECT_HEADER, NULL, NULL)) != NULL) { diff --git a/libctf/testsuite/libctf-writable/ctf-nonroot-linking.c b/libctf/testsuite/libctf-writable/ctf-nonroot-linking.c index 6edd189..67b85eb 100644 --- a/libctf/testsuite/libctf-writable/ctf-nonroot-linking.c +++ b/libctf/testsuite/libctf-writable/ctf-nonroot-linking.c @@ -24,16 +24,6 @@ main (int argc, char *argv[]) ctf_next_t *i = NULL; int err; - /* Linking does not currently work on mingw because of an unreliable tmpfile - implementation on that platform (see - https://github.com/msys2/MINGW-packages/issues/18878). Simply skip for - now. */ - -#ifdef __MINGW32__ - printf ("UNSUPPORTED: platform bug breaks ctf_link\n"); - return 0; -#else - if ((fp = ctf_create (&err)) == NULL) goto create_err; @@ -123,5 +113,4 @@ main (int argc, char *argv[]) link_err: fprintf (stderr, "Cannot link: %s\n", ctf_errmsg (ctf_errno (fp))); return 1; -#endif } diff --git a/libctf/testsuite/libctf-writable/slice-of-slice.c b/libctf/testsuite/libctf-writable/slice-of-slice.c new file mode 100644 index 0000000..9bacfe2 --- /dev/null +++ b/libctf/testsuite/libctf-writable/slice-of-slice.c @@ -0,0 +1,77 @@ +/* Make sure that slices of slices are properly resolved. If they're not, both + population and lookup will fail. */ + +#include <ctf-api.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int main (void) +{ + ctf_dict_t *fp; + ctf_id_t base; + ctf_id_t slice; + ctf_id_t slice2; + ctf_encoding_t long_encoding = { CTF_INT_SIGNED, 0, sizeof (long) }; + ctf_encoding_t foo; + int val; + int err; + + if ((fp = ctf_create (&err)) == NULL) + { + fprintf (stderr, "Cannot create: %s\n", ctf_errmsg (err)); + return 1; + } + + if ((base = ctf_add_enum_encoded (fp, CTF_ADD_ROOT, "enom", &long_encoding)) + == CTF_ERR) + goto err; + + if (ctf_add_enumerator (fp, base, "a", 1) < 0 || + ctf_add_enumerator (fp, base, "b", 0) < 0) + goto err; + + foo.cte_format = 0; + foo.cte_bits = 4; + foo.cte_offset = 4; + if ((slice = ctf_add_slice (fp, CTF_ADD_ROOT, base, &foo)) == CTF_ERR) + goto err; + + foo.cte_bits = 6; + foo.cte_offset = 2; + if ((slice2 = ctf_add_slice (fp, CTF_ADD_ROOT, slice, &foo)) == CTF_ERR) + goto err; + + if (ctf_add_variable (fp, "foo", slice) < 0) + goto err; + + if (ctf_enum_value (fp, slice, "a", &val) < 0) + { + fprintf (stderr, "Cannot look up value of sliced enum: %s\n", ctf_errmsg (ctf_errno (fp))); + return 1; + } + if (val != 1) + { + fprintf (stderr, "sliced enum value is wrong\n"); + return 1; + } + + if (ctf_enum_value (fp, slice2, "b", &val) < 0) + { + fprintf (stderr, "Cannot look up value of sliced sliced enum: %s\n", ctf_errmsg (ctf_errno (fp))); + return 1; + } + if (val != 0) + { + fprintf (stderr, "sliced sliced enum value is wrong\n"); + return 1; + } + + ctf_dict_close (fp); + fprintf (stderr, "All done.\n"); + return 0; + + err: + fprintf (stderr, "cannot populate: %s\n", ctf_errmsg (ctf_errno (fp))); + return 1; +} diff --git a/libctf/testsuite/libctf-writable/slice-of-slice.lk b/libctf/testsuite/libctf-writable/slice-of-slice.lk new file mode 100644 index 0000000..b944f73 --- /dev/null +++ b/libctf/testsuite/libctf-writable/slice-of-slice.lk @@ -0,0 +1 @@ +All done. |