libctf: dedup: preserve non-root flag across normal links

The previous commits dropped preservation of the non-root flag in ctf_link
and arranged to use it somewhat differently to track conflicting types in
cu-mapped CUs when doing cu-mapped links.  This was necessary to prevent
entirely spuriously hidden types from appearing on the output of such links.

Bring it (and the test for it) back.  The problem with the previous design
was that it implicitly assumed that the non-root flag it saw on the input
was always meant to be preserved (when in the final phase of cu-mapped links
it merely means that conflicting types were found in intermediate links),
and also that it could figure out what the non-root flag on the input was by
sucking in the non-root flag of the input type corresponding to an output in
the output mapping (which maps type hashes to a corresponding type on some
input).

This method of getting properties of the input type *does* work *if* that
property was one of those hashed by the ctf_dedup_hash_type process.  In
that case, every type with a given hash will have the same value for all
hashed-in properties, so it doesn't matter which one is consulted (the
output mapping points at an arbitrary one of those input types).  But the
non-root flag is explicitly *not* hashed in: as a comment in
ctf_dedup_rhash_type notes, being non-root is not a property of a type, and
two types (one non-root, one not) can perfectly well be the same type even
though one is visible and one isn't.  So just copying the non-root flag from
the output mapping's idea of the input type will copy in a value that is not
stabilized by the hash, so is more-or-less random!

So we cannot do that.  We have to do something else, which means we have to
decide what to do if two identical types with different nonroot flag values
pop up.  The most sensible thing to do is probably to say that if all
instances of a type are non-root-visible, the linked output should also be
non-root-visible: any root-visible types in that set, and the output type is
root-visible again.

We implement this with a new cd_nonroot_consistency dynhash, which maps type
hashes to the value 0 ("all instances root-visible"), 1 ("all instances
non-root-visible") or 2 ("inconsistent").  After hashing is over, we save a
bit of memory by deleting everything from this hashtab that doesn't have a
value of 1 ("non-root-visible"), then use this to decide whether to emit any
given type as non-root-visible or not.

However... that's not quite enough.  In cu-mapped links, we want to
disregard this whole thing because we just hide everything -- but in phase
2, when we take the smushed-together CUs resulting from phase 1 and
deduplicate them against each other, we want to do what the previous commits
implemented and ignore the non-root flag entirely, instead falling back to
preventing clashes by hiding anything that would be considered conflicting.
We extend the existing cu_mapped parameter to various bits of ctf_dedup so
that it is now tristate: 0 means a normal link, 1 means the smush-it-
together phase of cu-mapped links, and 2 means the final phase of cu-mapped
links.  We do the hide-conflicting stuff only in phase 2, meaning that
normal links by GNU ld can always respect the value of the nonroot flag put
on types in the input.

(One extra thing added as part of this: you can now efficiently delete the
last value returned by ctf_dynhash_next() by calling
ctf_dynhash_next_remove.)

We bring back the ctf-nonroot-linking test with one tweak: linking now works
on mingw as long as you're using the ucrt libc, so re-enable it for better
test coverage on that platform.

libctf/
	PR libctf/33047
	* ctf-hash.c (ctf_dynhash_next_remove): New.
	* ctf-impl.h (struct ctf_dedup) [cd_nonroot_consistency]: New.
	* ctf-link.c (ctf_link_deduplicating):  Differentiate between
	cu-mapped and non-cu-mapped links, even in the final phase.
	* ctf-dedup.c (ctf_dedup_hash_type): Callback prototype addition.
	Get the non-root flag and pass it down.
	(ctf_dedup_rhash_type): Callback prototype addition. Document
	restrictions on use of the nonroot flag.
	(ctf_dedup_populate_mappings): Populate cd_nonroot_consistency.
	(ctf_dedup_hash_type_fini): New function: delete now-unnecessary
	values from cd_nonroot_consistency.
	(ctf_dedup_init): Initialize it.
	(ctf_dedup_fini): Destroy it.
	(ctf_dedup): cu_mapping is now cu_mapping_phase.  Call
	ctf_dedup_hash_type_fini.
	(ctf_dedup_emit_type): Use cu_mapping_phase and
	cd_nonroot_consistency to propagate the non-root flag into outputs
	for normal links, and to do name-based conflict checking only for
	phase 2 of cu-mapped links.
	(ctf_dedup_emit): cu_mapping is now cu_mapping_phase.  Adjust
	assertion accordingly.
	* testsuite/libctf-writable/ctf-nonroot-linking.c: Bring back.
	* testsuite/libctf-writable/ctf-nonroot-linking.lk: Likewise.
This commit is contained in:
Nick Alcock
2025-05-30 22:12:37 +01:00
parent 002957be18
commit cedf6f8702
6 changed files with 322 additions and 56 deletions

View File

@@ -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,7 +2713,7 @@ 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 cu_mapping_phase = *(int *)arg;
int isroot = 1;
int is_conflicting;
@@ -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",
@@ -2709,33 +2784,58 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
name = ctf_strraw (real_input, tp->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;
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",
@@ -2788,17 +2888,22 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
int val;
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;
}
}
if (ctf_errno (input) != ECTF_NEXT_END)
goto err_input;
@@ -3119,12 +3224,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;
@@ -3133,7 +3238,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");
@@ -3146,7 +3251,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)

View File

@@ -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_)

View File

@@ -331,6 +331,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
@@ -671,6 +676,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 *);
@@ -717,10 +723,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);

View File

@@ -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));

View File

@@ -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 <ctf-api.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}

View File

@@ -0,0 +1 @@
Non-root type found.