Compare commits

...

5 Commits

Author SHA1 Message Date
Nick Alcock
947cb41467 libctf: use __attribute__((__gnu_printf__)) where appropriate
We don't use any GNU-specific printf args, but this prevents warnings about
%z, observed on MinGW even though every libc anyone is likely to use there
supports %z perfectly well, and we're not stopping using it just because
MinGW complains.  Doing this means we stand more chance of seeing *actual*
problems on such platforms without them being drowned in noise.

We turn this off on clang, which doesn't support __gnu_printf__.

Suggested by Eli Zaretskii.

libctf/
	PR libctf/31863
	* ctf-impl.h (_libctf_printflike_): Use __gnu_printf__.
2025-06-03 18:24:29 +01:00
Nick Alcock
86e13049aa libctf, dedup: reclaim space wasted by duplicate hidden types
In normal deduplicating links, we insert every type (identified by its
unique hash) precisely once.  But conflicting types appear in multiple
dicts, so for those, we loop, inserting them into every target dict
in turn (each corresponding to an input dict that type appears in).
But in cu-mapped links, some of those dicts may have been merged into
one: now that we are hiding duplicate conflicting types more
aggressively in such links, we are getting duplicate identical hidden
types turning up in large numbers.

Fix this by eliminating them in cu-mapping phase 1 (the phase in which this
merging takes place), by checking to see if a type with this hash has
already been inserted in this dict and skipping it if so.  This is
redundant and a waste of time in other cu-mapping phases and in normal
links, but in cu-mapped links it saves a few tens to hundreds of kilobytes
in kernel-sized links.

libctf/
	PR libctf/33047
	* ctf-dedup.c (ctf_dedup_emit_type): Check for already-emitted
	types in cu-mapping phase 1.
2025-06-03 18:24:28 +01:00
Nick Alcock
e248167f27 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.

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
such 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 time even though one is
visible and one isn't.

So we cannot use that technique.  We have to use 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.
2025-06-03 18:22:23 +01:00
Nick Alcock
84470967ed libctf: dedup: improve hiding of conflicting types in the same dict
If types are conflicting, they are usually moved into separate child dicts
-- but not always.  If they are added to the same dict by the cu-mapping
mechanism (as used e.g. for multi-TU kernel modules), we can easily end
up adding multiple conflicting types with the same name to the same dict.

The mechanism used for turning on the non-root-visible flag in order to do
this had a kludge attached which always hid types with the same name,
whether or not they were conflicting.  This is unnecessary and can hide
types that should not be hidden, as well as hiding bugs.  Remove it, and
replace it with two different approaches:

 - for everything but cu-mapped links (the in-memory first phase of a link
   with ctf_link_add_cu_mapping in force), check for duplicate names if
   types are conflicting, and mark them as hidden if the names are found.
   This will never happen in normal links (in an upcoming commit we will
   suppress doing even this much in such cases).

 - for cu-mapped links, the only case that merges multiple distinct target
   dicts into one, we apply a big hammer and simply hide everything!  The
   non-root flag will be ignored in the next link phase anyway (which dedups
   the cu-mapped pieces against each other), and this way we can be sure
   that merging multiple types cannot incur name clashes at this stage.

The result seems to work: the only annoyance is that when enums with
conflicting enumerators are found in a single cu-mapped child (so, really
multiple merged children), you may end up with every instance of that enum
being hidden for reasons of conflictingness.  I don't see a real way to
avoid that.

libctf/
	PR libctf/33047
	* ctf-dedup.c (ctf_dedup_emit_type): Only consider non
	conflicting types.  Improve type hiding in the presence of clashing
	enumerators.  Hide everything when doing a cu-mapped link: they will
	be unhidden by the next link pass if nonconflicting.
2025-06-03 12:03:39 +01:00
Nick Alcock
265f5b0ddf Revert "libctf: fix linking of non-root-visible types"
This reverts commit 87b2f67310.

It is based on a misconception, that hidden types in the deduplicator
input should always be hidden in the output.  For cu-mapped links,
and final links following cu-mapped links, this is not true: we want
to hide inputs if they were conflicting on the output and no more.

We will reintroduce the testcase once a better fix is found.

libctf/
	PR libctf/33047
	* ctf-dedup.c (ctf_dedup_emit_type): Don't respect the nonroot flag.
	* testsuite/libctf-writable/ctf-nonroot-linking.c: Removed.
	* testsuite/libctf-writable/ctf-nonroot-linking.lk: Removed.
2025-06-02 16:50:29 +01:00
5 changed files with 265 additions and 73 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,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,33 +2773,6 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
output_num = input_num;
}
real_input = input;
if ((tp = ctf_lookup_by_id (&real_input, type)) == NULL)
{
ctf_err_warn (output, 0, ctf_errno (input),
_("%s: lookup failure for type %lx"),
ctf_link_input_name (real_input), type);
return ctf_set_errno (output, ctf_errno (input));
}
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)
{
if (ctf_type_kind (target, maybe_dup) != CTF_K_FORWARD)
isroot = 0;
}
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,
@@ -2737,6 +2785,90 @@ ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
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)
{
ctf_err_warn (output, 0, ctf_errno (input),
_("%s: lookup failure for type %lx"),
ctf_link_input_name (real_input), type);
return ctf_set_errno (output, ctf_errno (input));
}
name = ctf_strraw (real_input, tp->ctt_name);
/* 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)
{
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);
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)

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

@@ -66,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__))
@@ -331,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
@@ -671,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 *);
@@ -717,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);

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

@@ -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
}