diff --git a/libctf/ctf-dedup.c b/libctf/ctf-dedup.c index d5c69a02656..4a4420dfcae 100644 --- a/libctf/ctf-dedup.c +++ b/libctf/ctf-dedup.c @@ -102,9 +102,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 @@ -586,6 +586,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)); @@ -654,6 +655,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)) @@ -781,9 +783,13 @@ 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. For the same - reason, type prefixes are not hashed: one of them indicates only - nonrootness, and the other is merely an implementation detail. */ + 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. For the same reason, type prefixes are not hashed: + one of them indicates only nonrootness, and the other is merely an + implementation detail: neither is preserved across dedups. */ ctf_sha1_init (&hash); if (name) @@ -1384,6 +1390,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)) @@ -1396,6 +1403,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++; @@ -1425,6 +1433,7 @@ ctf_dedup_hash_type (ctf_dict_t *fp, ctf_dict_t *input, kind = ctf_type_kind_tp (input, tp); name = ctf_strraw (input, suffix->ctt_name); + isroot = LCTF_INFO_ISROOT (input, tp->ctt_info); if (suffix->ctt_name == 0 || !name || name[0] == '\0') name = NULL; @@ -1455,7 +1464,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; @@ -1489,7 +1498,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"); @@ -1518,19 +1527,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; int kind; #ifdef ENABLE_LIBCTF_HASH_DEBUGGING @@ -1654,6 +1664,32 @@ ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_, } } + /* 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) @@ -1687,6 +1723,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) { @@ -2198,6 +2261,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, @@ -2259,6 +2328,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 @@ -2442,15 +2512,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; @@ -2472,12 +2542,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 @@ -2540,6 +2611,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 @@ -3260,7 +3335,8 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, const ctf_type_t *tp, *suffix; int input_num = CTF_DEDUP_GID_TO_INPUT (id); int output_num = (uint32_t) -1; /* 'shared' */ - int cu_mapped = *(int *)arg; + int cu_mapping_phase = *(int *)arg; + int parent_cu_mapped = 0; int isroot = 1; int is_conflicting; int mark_type_conflicting = 0; @@ -3281,15 +3357,17 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, ctf_dprintf ("%i: Emitting type with hash %s from %s: determining target\n", depth, hval, ctf_link_input_name (input)); - /* Conflicting types go into a per-CU output dictionary, unless this is a - CU-mapped run or the input CU name is empty. The import is not refcounted, - since it goes into the ctf_link_outputs dict of the output that is its - parent. */ + /* Conflicting types go into a per-CU output dictionary, unless this is the + final phase of a CU-mapped run and the input CU name is empty. The import + is not refcounted, since it goes into the 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 - && (ctf_cuname (input) == NULL || - strcmp (ctf_cuname (input), "") != 0)) + if (cu_mapping_phase == 2 && ctf_cuname (input) != NULL + && strcmp (ctf_cuname (input), "") == 0) + parent_cu_mapped = 1; + + if (is_conflicting && cu_mapping_phase != 1 && !parent_cu_mapped) { ctf_dprintf ("%i: Type %s in %i/%lx is conflicted: " "inserting into per-CU target.\n", @@ -3335,36 +3413,76 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, name = ctf_strraw (real_input, suffix->ctt_name); - /* Hide conflicting types, if this type is conflicting and a type with this - name already exists in the target and is not a forward. We don't want to - hide them if they are the only type of that name in a per-CU child, as is - usually the case, but in cu-mapped links and in the final phase after - cu-mapping distinct conflicting types from different CUs can end up in the - same cu-mapped target. Hide all but one in this case: it doesn't matter - which, all the conflicting types necessarily lost the popularity contest - anyway. - - cu_mapped links get absolutely *everything* marked non-root, named or not. - cu-mapped links, when we are merging multiple child CUs into one, are the - only point at which we can ever put conflicting and nonconflicting + /* 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. - Note that enums also get their enumerands checked, below. */ + 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). - if (cu_mapped) - isroot = 0; - else if (is_conflicting && name - && ((maybe_dup = ctf_lookup_by_rawname (target, kind, name)) != 0)) + 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; mark_type_conflicting = 1; } + break; + case 1: /* cu-mapped link. Never root-visible, no explicit conflict + marking. */ + isroot = 0; + break; + case 2: /* Final phase of cu-mapped link. */ + if (is_conflicting && name) + { + /* Non-root if conflicting and the target is cu-mapped into the parent + dict (which must necessarily contain the non-conflicting type as + well). */ + if (parent_cu_mapped) + { + isroot = 0; + mark_type_conflicting = 1; + } + else + /* Non-root if already present (multiple conflicting types mapped + into the same child dict). */ + if ((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; + mark_type_conflicting = 1; + } + } + } + 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", @@ -3420,17 +3538,23 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, ctf_encoding_t en; errtype = _("enum"); - /* Check enumerands for duplication: this is an extension of the isroot - check above. */ + /* Check enumerands for duplication and nonrootify if clashing: this is + an extension of the isroot check above. */ - if (isroot) + 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) - isroot = 0; + { + 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; + mark_type_conflicting = 1; + } } if (ctf_errno (input) != ECTF_NEXT_END) goto err_input; @@ -3822,15 +3946,15 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs, err_input: ctf_err_warn (output, 0, ctf_errno (input), _("%s (%i): while emitting deduplicated %s, error getting " - "input type %lx"), ctf_link_input_name (input), - input_num, errtype, type); + "input type %lx (named %s)"), ctf_link_input_name (input), + input_num, errtype, type, name ? name : "(unnamed)"); return ctf_set_errno (output, ctf_errno (input)); err_target: ctf_err_warn (output, 0, ctf_errno (target), _("%s (%i): while emitting deduplicated %s, error emitting " - "target type from input type %lx"), + "target type from input type %lx (named %s)"), ctf_link_input_name (input), input_num, - errtype, type); + errtype, type, name ? name : "(unnamed)"); return ctf_set_errno (output, ctf_errno (target)); } @@ -4063,12 +4187,12 @@ ctf_dedup_emit_decl_tags (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; @@ -4077,7 +4201,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"); @@ -4094,7 +4218,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 7cde73f7816..ac67fb85ce5 100644 --- a/libctf/ctf-hash.c +++ b/libctf/ctf-hash.c @@ -514,8 +514,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 @@ -587,6 +588,30 @@ ctf_dynhash_next (const ctf_dynhash_t *h, ctf_next_t **it, void **key, void **va 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 09b3f29d06b..2abd0e39f12 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -344,6 +344,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 @@ -740,6 +745,7 @@ extern int ctf_dynhash_next (const ctf_dynhash_t *, ctf_next_t **, extern int ctf_dynhash_next_sorted (const 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 *); @@ -784,10 +790,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 int ctf_dedup_strings (ctf_dict_t *fp); 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, diff --git a/libctf/ctf-link.c b/libctf/ctf-link.c index 479d9789bd9..7bfb3daf88f 100644 --- a/libctf/ctf-link.c +++ b/libctf/ctf-link.c @@ -1258,6 +1258,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) { @@ -1265,9 +1266,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. */ @@ -1279,7 +1291,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)); @@ -1287,7 +1299,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/testsuite/libctf-writable/ctf-nonroot-linking.c b/libctf/testsuite/libctf-writable/ctf-nonroot-linking.c new file mode 100644 index 00000000000..67b85eb1bdd --- /dev/null +++ b/libctf/testsuite/libctf-writable/ctf-nonroot-linking.c @@ -0,0 +1,116 @@ +/* Make sure linking a non-root-visible type emits a non-root-visible + type, rather than silently promoting it to root-visible. Do it by dumping, + thus also testing the {non-root sigils} you get when dumping + non-root-visible types. */ + +#include +#include +#include +#include + +int +main (int argc, char *argv[]) +{ + ctf_dict_t *in1; + ctf_dict_t *fp; + ctf_dict_t *dump_fp; + ctf_archive_t *arc1; + ctf_archive_t *final_arc; + ctf_sect_t s; + ctf_encoding_t encoding = { CTF_INT_SIGNED, 0, sizeof (char) }; + unsigned char *buf1, *buf2; + size_t buf1_sz, buf2_sz; + ctf_dump_state_t *dump_state = NULL; + ctf_next_t *i = NULL; + int err; + + if ((fp = ctf_create (&err)) == NULL) + goto create_err; + + if ((in1 = ctf_create (&err)) == NULL) + goto create_err; + + /* A non-root addition. */ + + if ((ctf_add_integer (in1, CTF_ADD_NONROOT, "foo", &encoding)) == CTF_ERR) + { + fprintf (stderr, "Cannot add: %s\n", ctf_errmsg (ctf_errno (in1))); + return 1; + } + + /* Write it out and read it back in, to turn it into an archive. + This would be unnecessary if ctf_link_add() were public :( */ + if ((buf1 = ctf_write_mem (in1, &buf1_sz, -1)) == NULL) + { + fprintf (stderr, "Cannot serialize: %s\n", ctf_errmsg (ctf_errno (in1))); + return 1; + } + + s.cts_name = "foo"; + s.cts_data = (void *) buf1; + s.cts_size = buf1_sz; + s.cts_entsize = 64; /* Unimportant. */ + + if ((arc1 = ctf_arc_bufopen (&s, NULL, NULL, &err)) == NULL) + goto open_err; + + ctf_dict_close (in1); + + /* Link! Even a one-file link does deduplication. */ + + if (ctf_link_add_ctf (fp, arc1, "a") < 0) + goto link_err; + + if (ctf_link (fp, 0) < 0) + goto link_err; + + /* Write it out. We need a new buf here, because the archive is still + using the other buf. */ + + if ((buf2 = ctf_link_write (fp, &buf2_sz, 4096)) == NULL) + goto link_err; + + /* Read it back in. */ + + s.cts_data = (void *) buf2; + s.cts_size = buf2_sz; + + if ((final_arc = ctf_arc_bufopen (&s, NULL, NULL, &err)) == NULL) + goto open_err; + + /* Dump the types, and search for the {sigils of non-rootedness}. */ + while ((dump_fp = ctf_archive_next (final_arc, &i, NULL, 0, &err)) != NULL) + { + char *dumpstr; + + while ((dumpstr = ctf_dump (dump_fp, &dump_state, CTF_SECT_TYPE, + NULL, NULL)) != NULL) + { + if (strchr (dumpstr, '{') != NULL && strchr (dumpstr, '}') != NULL) + printf ("Non-root type found.\n"); + free (dumpstr); + } + ctf_dict_close (dump_fp); + } + if (err != ECTF_NEXT_END) + { + fprintf (stderr, "Archive iteration error: %s\n", ctf_errmsg (err)); + return 1; + } + + ctf_arc_close (final_arc); + free (buf1); + free (buf2); + ctf_dict_close (fp); + return 0; + + create_err: + fprintf (stderr, "Cannot create: %s\n", ctf_errmsg (err)); + return 1; + open_err: + fprintf (stderr, "Cannot open: %s\n", ctf_errmsg (err)); + return 1; + link_err: + fprintf (stderr, "Cannot link: %s\n", ctf_errmsg (ctf_errno (fp))); + return 1; +} diff --git a/libctf/testsuite/libctf-writable/ctf-nonroot-linking.lk b/libctf/testsuite/libctf-writable/ctf-nonroot-linking.lk new file mode 100644 index 00000000000..e3edda05c5c --- /dev/null +++ b/libctf/testsuite/libctf-writable/ctf-nonroot-linking.lk @@ -0,0 +1 @@ +Non-root type found.