3172 lines
101 KiB
C
3172 lines
101 KiB
C
|
/* CTF type deduplication.
|
||
|
Copyright (C) 2019-2022 Free Software Foundation, Inc.
|
||
|
|
||
|
This file is part of libctf.
|
||
|
|
||
|
libctf is free software; you can redistribute it and/or modify it under
|
||
|
the terms of the GNU General Public License as published by the Free
|
||
|
Software Foundation; either version 3, or (at your option) any later
|
||
|
version.
|
||
|
|
||
|
This program is distributed in the hope that it will be useful, but
|
||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
See the GNU General Public License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU General Public License
|
||
|
along with this program; see the file COPYING. If not see
|
||
|
<http://www.gnu.org/licenses/>. */
|
||
|
|
||
|
#include <ctf-impl.h>
|
||
|
#include <string.h>
|
||
|
#include <errno.h>
|
||
|
#include <assert.h>
|
||
|
#include "hashtab.h"
|
||
|
|
||
|
/* (In the below, relevant functions are named in square brackets.) */
|
||
|
|
||
|
/* Type deduplication is a three-phase process:
|
||
|
|
||
|
[ctf_dedup, ctf_dedup_hash_type, ctf_dedup_rhash_type]
|
||
|
1) come up with unambiguous hash values for all types: no two types may have
|
||
|
the same hash value, and any given type should have only one hash value
|
||
|
(for optimal deduplication).
|
||
|
|
||
|
[ctf_dedup, ctf_dedup_detect_name_ambiguity,
|
||
|
ctf_dedup_conflictify_unshared, ctf_dedup_mark_conflicting_hash]
|
||
|
2) mark those distinct types with names that collide (and thus cannot be
|
||
|
declared simultaneously in the same translation unit) as conflicting, and
|
||
|
recursively mark all types that cite one of those types as conflicting as
|
||
|
well. Possibly mark all types cited in only one TU as conflicting, if
|
||
|
the CTF_LINK_SHARE_DUPLICATED link mode is active.
|
||
|
|
||
|
[ctf_dedup_emit, ctf_dedup_emit_struct_members, ctf_dedup_id_to_target]
|
||
|
3) emit all the types, one hash value at a time. Types not marked
|
||
|
conflicting are emitted once, into the shared dictionary: types marked
|
||
|
conflicting are emitted once per TU into a dictionary corresponding to
|
||
|
each TU in which they appear. Structs marked conflicting get at the very
|
||
|
least a forward emitted into the shared dict so that other dicts can cite
|
||
|
it if needed.
|
||
|
|
||
|
[id_to_packed_id]
|
||
|
This all works over an array of inputs (usually in the same order as the
|
||
|
inputs on the link line). We don't use the ctf_link_inputs hash directly
|
||
|
because it is convenient to be able to address specific input types as a
|
||
|
*global type ID* or 'GID', a pair of an array offset and a ctf_id_t. Since
|
||
|
both are already 32 bits or less or can easily be constrained to that range,
|
||
|
we can pack them both into a single 64-bit hash word for easy lookups, which
|
||
|
would be much more annoying to do with a ctf_dict_t * and a ctf_id_t. (On
|
||
|
32-bit platforms, we must do that anyway, since pointers, and thus hash keys
|
||
|
and values, are only 32 bits wide). We track which inputs are parents of
|
||
|
which other inputs so that we can correctly recognize that types we have
|
||
|
traversed in children may cite types in parents, and so that we can process
|
||
|
the parents first.)
|
||
|
|
||
|
Note that thanks to ld -r, the deduplicator can be fed its own output, so the
|
||
|
inputs may themselves have child dicts. Since we need to support this usage
|
||
|
anyway, we can use it in one other place. If the caller finds translation
|
||
|
units to be too small a unit ambiguous types, links can be 'cu-mapped', where
|
||
|
the caller provides a mapping of input TU names to output child dict names.
|
||
|
This mapping can fuse many child TUs into one potential child dict, so that
|
||
|
ambiguous types in any of those input TUs go into the same child dict.
|
||
|
When a many:1 cu-mapping is detected, the ctf_dedup machinery is called
|
||
|
repeatedly, once for every output name that has more than one input, to fuse
|
||
|
all the input TUs associated with a given output dict into one, and once again
|
||
|
as normal to deduplicate all those intermediate outputs (and any 1:1 inputs)
|
||
|
together. This has much higher memory usage than otherwise, because in the
|
||
|
intermediate state, all the output TUs are in memory at once and cannot be
|
||
|
lazily opened. It also has implications for the emission code: if types
|
||
|
appear ambiguously in multiple input TUs that are all mapped to the same
|
||
|
child dict, we cannot put them in children in the cu-mapping link phase
|
||
|
because this output is meant to *become* a child in the next link stage and
|
||
|
parent/child relationships are only one level deep: so instead, we just hide
|
||
|
all but one of the ambiguous types.
|
||
|
|
||
|
There are a few other subtleties here that make this more complex than it
|
||
|
seems. Let's go over the steps above in more detail.
|
||
|
|
||
|
1) HASHING.
|
||
|
|
||
|
[ctf_dedup_hash_type, ctf_dedup_rhash_type]
|
||
|
Hashing proceeds recursively, mixing in the properties of each input type
|
||
|
(including its name, if any), and then adding the hash values of every type
|
||
|
cited by that type. The result is stashed in the cd_type_hashes so other
|
||
|
phases can find the hash values of input types given their IDs, and so that
|
||
|
if we encounter this type again while hashing we can just return its hash
|
||
|
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.
|
||
|
|
||
|
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
|
||
|
data volume to deal with than later phases. Trace output is hidden behind
|
||
|
ENABLE_LIBCTF_HASH_DEBUGGING to prevent the sheer number of calls to
|
||
|
ctf_dprintf from slowing things down (tenfold slowdowns are observed purely
|
||
|
from the calls to ctf_dprintf(), even with debugging switched off), and keep
|
||
|
down the volume of output (hundreds of gigabytes of debug output are not
|
||
|
uncommon on larger links).
|
||
|
|
||
|
We have to do *something* about potential cycles in the type graph. We'd
|
||
|
like to avoid emitting forwards in the final output if possible, because
|
||
|
forwards aren't much use: they have no members. We are mostly saved from
|
||
|
needing to worry about this at emission time by ctf_add_struct*()
|
||
|
automatically replacing newly-created forwards when the real struct/union
|
||
|
comes along. So we only have to avoid getting stuck in cycles during the
|
||
|
hashing phase, while also not confusing types that cite members that are
|
||
|
structs with each other. It is easiest to solve this problem by noting two
|
||
|
things:
|
||
|
|
||
|
- all cycles in C depend on the presence of tagged structs/unions
|
||
|
- all tagged structs/unions have a unique name they can be disambiguated by
|
||
|
|
||
|
[ctf_dedup_is_stub]
|
||
|
This means that we can break all cycles by ceasing to hash in cited types at
|
||
|
every tagged struct/union and instead hashing in a stub consisting of the
|
||
|
struct/union's *decorated name*, which is the name preceded by "s " or "u "
|
||
|
depending on the namespace (cached in cd_decorated_names). Forwards are
|
||
|
decorated identically (so a forward to "struct foo" would be represented as
|
||
|
"s foo"): this means that a citation of a forward to a type and a citation of
|
||
|
a concrete definition of a type with the same name ends up getting the same
|
||
|
hash value.
|
||
|
|
||
|
Of course, it is quite possible to have two TUs with structs with the same
|
||
|
name and different definitions, but that's OK because when we scan for types
|
||
|
with ambiguous names we will identify these and mark them conflicting.
|
||
|
|
||
|
We populate one thing to help conflictedness marking. No unconflicted type
|
||
|
may cite a conflicted one, but this means that conflictedness marking must
|
||
|
walk from types to the types that cite them, which is the opposite of the
|
||
|
usual order. We can make this easier to do by constructing a *citers* graph
|
||
|
in cd_citers, which points from types to the types that cite them: because we
|
||
|
emit forwards corresponding to every conflicted struct/union, we don't need
|
||
|
to do this for citations of structs/unions by other types. This is very
|
||
|
convenient for us, because that's the only type we don't traverse
|
||
|
recursively: so we can construct the citers graph at the same time as we
|
||
|
hash, rather than needing to add an extra pass. (This graph is a dynhash of
|
||
|
*type hash values*, so it's small: in effect it is automatically
|
||
|
deduplicated.)
|
||
|
|
||
|
2) COLLISIONAL MARKING.
|
||
|
|
||
|
[ctf_dedup_detect_name_ambiguity, ctf_dedup_mark_conflicting_hash]
|
||
|
We identify types whose names collide during the hashing process, and count
|
||
|
the rough number of uses of each name (caching may throw it off a bit: this
|
||
|
doesn't need to be accurate). We then mark the less-frequently-cited types
|
||
|
with each names conflicting: the most-frequently-cited one goes into the
|
||
|
shared type dictionary, while all others are duplicated into per-TU
|
||
|
dictionaries, named after the input TU, that have the shared dictionary as a
|
||
|
parent. For structures and unions this is not quite good enough: we'd like
|
||
|
to have citations of forwards to ambiguously named structures and unions
|
||
|
*stay* as citations of forwards, so that the user can tell that the caller
|
||
|
didn't actually know which structure definition was meant: but if we put one
|
||
|
of those structures into the shared dictionary, it would supplant and replace
|
||
|
the forward, leaving no sign. So structures and unions do not take part in
|
||
|
this popularity contest: if their names are ambiguous, they are just
|
||
|
duplicated, and only a forward appears in the shared dict.
|
||
|
|
||
|
[ctf_dedup_propagate_conflictedness]
|
||
|
The process of marking types conflicted is itself recursive: we recursively
|
||
|
traverse the cd_citers graph populated in the hashing pass above and mark
|
||
|
everything that we encounter conflicted (without wasting time re-marking
|
||
|
anything that is already marked). This naturally terminates just where we
|
||
|
want it to (at types that are cited by no other types, and at structures and
|
||
|
unions) and suffices to ensure that types that cite conflicted types are
|
||
|
always marked conflicted.
|
||
|
|
||
|
[ctf_dedup_conflictify_unshared, ctf_dedup_multiple_input_dicts]
|
||
|
When linking in CTF_LINK_SHARE_DUPLICATED mode, we would like all types that
|
||
|
are used in only one TU to end up in a per-CU dict. The easiest way to do
|
||
|
that is to mark them conflicted. ctf_dedup_conflictify_unshared does this,
|
||
|
traversing the output mapping and using ctf_dedup_multiple_input_dicts to
|
||
|
check the number of input dicts each distinct type hash value came from:
|
||
|
types that only came from one get marked conflicted. One caveat here is that
|
||
|
we need to consider both structs and forwards to them: a struct that appears
|
||
|
in one TU and has a dozen citations to an opaque forward in other TUs should
|
||
|
*not* be considered to be used in only one TU, because users would find it
|
||
|
useful to be able to traverse into opaque structures of that sort: so we use
|
||
|
cd_struct_origin to check both structs/unions and the forwards corresponding
|
||
|
to them.
|
||
|
|
||
|
3) EMISSION.
|
||
|
|
||
|
[ctf_dedup_walk_output_mapping, ctf_dedup_rwalk_output_mapping,
|
||
|
ctf_dedup_rwalk_one_output_mapping]
|
||
|
Emission involves another walk of the entire output mapping, this time
|
||
|
traversing everything other than struct members, recursively. Types are
|
||
|
emitted from leaves to trunk, emitting all types a type cites before emitting
|
||
|
the type itself. We sort the output mapping before traversing it, for
|
||
|
reproducibility and also correctness: the input dicts may have parent/child
|
||
|
relationships, so we simply sort all types that first appear in parents
|
||
|
before all children, then sort types that first appear in dicts appearing
|
||
|
earlier on the linker command line before those that appear later, then sort
|
||
|
by input ctf_id_t. (This is where we use cd_output_first_gid, collected
|
||
|
above.)
|
||
|
|
||
|
The walking is done using a recursive traverser which arranges to not revisit
|
||
|
any type already visited and to call its callback once per input GID for
|
||
|
input GIDs corresponding to conflicted output types. The traverser only
|
||
|
finds input types and calls a callback for them as many times as the output
|
||
|
needs to appear: it doesn't try to figure out anything about where the output
|
||
|
might go. That's done by the callback based on whether the type is
|
||
|
marked conflicted or not.
|
||
|
|
||
|
[ctf_dedup_emit_type, ctf_dedup_id_to_target, ctf_dedup_synthesize_forward]
|
||
|
ctf_dedup_emit_type is the (sole) callback for ctf_dedup_walk_output_mapping.
|
||
|
Conflicted types have all necessary dictionaries created, and then we emit
|
||
|
the type into each dictionary in turn, working over each input CTF type
|
||
|
corresponding to each hash value and using ctf_dedup_id_to_target to map each
|
||
|
input ctf_id_t into the corresponding type in the output (dealing with input
|
||
|
ctf_id_t's with parents in the process by simply chasing to the parent dict
|
||
|
if the type we're looking up is in there). Emitting structures involves
|
||
|
simply noting that the members of this structure need emission later on:
|
||
|
because you cannot cite a single structure member from another type, we avoid
|
||
|
emitting the members at this stage to keep recursion depths down a bit.
|
||
|
|
||
|
At this point, if we have by some mischance decided that two different types
|
||
|
with child types that hash to different values have in fact got the same hash
|
||
|
value themselves and *not* marked it conflicting, the type walk will walk
|
||
|
only *one* of them and in all likelihood we'll find that we are trying to
|
||
|
emit a type into some child dictionary that references a type that was never
|
||
|
emitted into that dictionary and assertion-fail. This always indicates a bug
|
||
|
in the conflictedness marking machinery or the hashing code, or both.
|
||
|
|
||
|
ctf_dedup_id_to_target calls ctf_dedup_synthesize_forward to do one extra
|
||
|
thing, alluded to above: if this is a conflicted tagged structure or union,
|
||
|
and the target is the shared dict (i.e., the type we're being asked to emit
|
||
|
is not itself conflicted so can't just point straight at the conflicted
|
||
|
type), we instead synthesise a forward with the same name, emit it into the
|
||
|
shared dict, record it in cd_output_emission_conflicted_forwards so that we
|
||
|
don't re-emit it, and return it. This means that cycles that contain
|
||
|
conflicts do not cause the entire cycle to be replicated in every child: only
|
||
|
that piece of the cycle which takes you back as far as the closest tagged
|
||
|
struct/union needs to be replicated. This trick means that no part of the
|
||
|
deduplicator needs a cycle detector: every recursive walk can stop at tagged
|
||
|
structures.
|
||
|
|
||
|
[ctf_dedup_emit_struct_members]
|
||
|
The final stage of emission is to walk over all structures with members
|
||
|
that need emission and emit all of them. Every type has been emitted at
|
||
|
this stage, so emission cannot fail.
|
||
|
|
||
|
[ctf_dedup_populate_type_mappings, ctf_dedup_populate_type_mapping]
|
||
|
Finally, we update the input -> output type ID mappings used by the ctf-link
|
||
|
machinery to update all the other sections. This is surprisingly expensive
|
||
|
and may be replaced with a scheme which lets the ctf-link machinery extract
|
||
|
the needed info directly from the deduplicator. */
|
||
|
|
||
|
/* Possible future optimizations are flagged with 'optimization opportunity'
|
||
|
below. */
|
||
|
|
||
|
/* Global optimization opportunity: a GC pass, eliminating types with no direct
|
||
|
or indirect citations from the other sections in the dictionary. */
|
||
|
|
||
|
/* Internal flag values for ctf_dedup_hash_type. */
|
||
|
|
||
|
/* Child call: consider forwardable types equivalent to forwards or stubs below
|
||
|
this point. */
|
||
|
#define CTF_DEDUP_HASH_INTERNAL_CHILD 0x01
|
||
|
|
||
|
/* Transform references to single ctf_id_ts in passed-in inputs into a number
|
||
|
that will fit in a uint64_t. Needs rethinking if CTF_MAX_TYPE is boosted.
|
||
|
|
||
|
On 32-bit platforms, we pack things together differently: see the note
|
||
|
above. */
|
||
|
|
||
|
#if UINTPTR_MAX < UINT64_MAX
|
||
|
# define IDS_NEED_ALLOCATION 1
|
||
|
# define CTF_DEDUP_GID(fp, input, type) id_to_packed_id (fp, input, type)
|
||
|
# define CTF_DEDUP_GID_TO_INPUT(id) packed_id_to_input (id)
|
||
|
# define CTF_DEDUP_GID_TO_TYPE(id) packed_id_to_type (id)
|
||
|
#else
|
||
|
# define CTF_DEDUP_GID(fp, input, type) \
|
||
|
(void *) (((uint64_t) input) << 32 | (type))
|
||
|
# define CTF_DEDUP_GID_TO_INPUT(id) ((int) (((uint64_t) id) >> 32))
|
||
|
# define CTF_DEDUP_GID_TO_TYPE(id) (ctf_id_t) (((uint64_t) id) & ~(0xffffffff00000000ULL))
|
||
|
#endif
|
||
|
|
||
|
#ifdef IDS_NEED_ALLOCATION
|
||
|
|
||
|
/* This is the 32-bit path, which stores GIDs in a pool and returns a pointer
|
||
|
into the pool. It is notably less efficient than the 64-bit direct storage
|
||
|
approach, but with a smaller key, this is all we can do. */
|
||
|
|
||
|
static void *
|
||
|
id_to_packed_id (ctf_dict_t *fp, int input_num, ctf_id_t type)
|
||
|
{
|
||
|
const void *lookup;
|
||
|
ctf_type_id_key_t *dynkey = NULL;
|
||
|
ctf_type_id_key_t key = { input_num, type };
|
||
|
|
||
|
if (!ctf_dynhash_lookup_kv (fp->ctf_dedup.cd_id_to_dict_t,
|
||
|
&key, &lookup, NULL))
|
||
|
{
|
||
|
if ((dynkey = malloc (sizeof (ctf_type_id_key_t))) == NULL)
|
||
|
goto oom;
|
||
|
memcpy (dynkey, &key, sizeof (ctf_type_id_key_t));
|
||
|
|
||
|
if (ctf_dynhash_insert (fp->ctf_dedup.cd_id_to_dict_t, dynkey, NULL) < 0)
|
||
|
goto oom;
|
||
|
|
||
|
ctf_dynhash_lookup_kv (fp->ctf_dedup.cd_id_to_dict_t,
|
||
|
dynkey, &lookup, NULL);
|
||
|
}
|
||
|
/* We use a raw assert() here because there isn't really a way to get any sort
|
||
|
of error back from this routine without vastly complicating things for the
|
||
|
much more common case of !IDS_NEED_ALLOCATION. */
|
||
|
assert (lookup);
|
||
|
return (void *) lookup;
|
||
|
|
||
|
oom:
|
||
|
free (dynkey);
|
||
|
ctf_set_errno (fp, ENOMEM);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
packed_id_to_input (const void *id)
|
||
|
{
|
||
|
const ctf_type_id_key_t *key = (ctf_type_id_key_t *) id;
|
||
|
|
||
|
return key->ctii_input_num;
|
||
|
}
|
||
|
|
||
|
static ctf_id_t
|
||
|
packed_id_to_type (const void *id)
|
||
|
{
|
||
|
const ctf_type_id_key_t *key = (ctf_type_id_key_t *) id;
|
||
|
|
||
|
return key->ctii_type;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* Make an element in a dynhash-of-dynsets, or return it if already present. */
|
||
|
|
||
|
static ctf_dynset_t *
|
||
|
make_set_element (ctf_dynhash_t *set, const void *key)
|
||
|
{
|
||
|
ctf_dynset_t *element;
|
||
|
|
||
|
if ((element = ctf_dynhash_lookup (set, key)) == NULL)
|
||
|
{
|
||
|
if ((element = ctf_dynset_create (htab_hash_string,
|
||
|
htab_eq_string,
|
||
|
NULL)) == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if (ctf_dynhash_insert (set, (void *) key, element) < 0)
|
||
|
{
|
||
|
ctf_dynset_destroy (element);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return element;
|
||
|
}
|
||
|
|
||
|
/* Initialize the dedup atoms table. */
|
||
|
int
|
||
|
ctf_dedup_atoms_init (ctf_dict_t *fp)
|
||
|
{
|
||
|
if (fp->ctf_dedup_atoms)
|
||
|
return 0;
|
||
|
|
||
|
if (!fp->ctf_dedup_atoms_alloc)
|
||
|
{
|
||
|
if ((fp->ctf_dedup_atoms_alloc
|
||
|
= ctf_dynset_create (htab_hash_string, htab_eq_string,
|
||
|
free)) == NULL)
|
||
|
return ctf_set_errno (fp, ENOMEM);
|
||
|
}
|
||
|
fp->ctf_dedup_atoms = fp->ctf_dedup_atoms_alloc;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Intern things in the dedup atoms table. */
|
||
|
|
||
|
static const char *
|
||
|
intern (ctf_dict_t *fp, char *atom)
|
||
|
{
|
||
|
const void *foo;
|
||
|
|
||
|
if (atom == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
if (!ctf_dynset_exists (fp->ctf_dedup_atoms, atom, &foo))
|
||
|
{
|
||
|
if (ctf_dynset_insert (fp->ctf_dedup_atoms, atom) < 0)
|
||
|
{
|
||
|
ctf_set_errno (fp, ENOMEM);
|
||
|
return NULL;
|
||
|
}
|
||
|
foo = atom;
|
||
|
}
|
||
|
else
|
||
|
free (atom);
|
||
|
|
||
|
return (const char *) foo;
|
||
|
}
|
||
|
|
||
|
/* Add an indication of the namespace to a type name in a way that is not valid
|
||
|
for C identifiers. Used to maintain hashes of type names to other things
|
||
|
while allowing for the four C namespaces (normal, struct, union, enum).
|
||
|
Return a new dynamically-allocated string. */
|
||
|
static const char *
|
||
|
ctf_decorate_type_name (ctf_dict_t *fp, const char *name, int kind)
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
const char *ret;
|
||
|
const char *k;
|
||
|
char *p;
|
||
|
size_t i;
|
||
|
|
||
|
switch (kind)
|
||
|
{
|
||
|
case CTF_K_STRUCT:
|
||
|
k = "s ";
|
||
|
i = 0;
|
||
|
break;
|
||
|
case CTF_K_UNION:
|
||
|
k = "u ";
|
||
|
i = 1;
|
||
|
break;
|
||
|
case CTF_K_ENUM:
|
||
|
k = "e ";
|
||
|
i = 2;
|
||
|
break;
|
||
|
default:
|
||
|
k = "";
|
||
|
i = 3;
|
||
|
}
|
||
|
|
||
|
if ((ret = ctf_dynhash_lookup (d->cd_decorated_names[i], name)) == NULL)
|
||
|
{
|
||
|
char *str;
|
||
|
|
||
|
if ((str = malloc (strlen (name) + strlen (k) + 1)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
p = stpcpy (str, k);
|
||
|
strcpy (p, name);
|
||
|
ret = intern (fp, str);
|
||
|
if (!ret)
|
||
|
goto oom;
|
||
|
|
||
|
if (ctf_dynhash_cinsert (d->cd_decorated_names[i], name, ret) < 0)
|
||
|
goto oom;
|
||
|
}
|
||
|
|
||
|
return ret;
|
||
|
|
||
|
oom:
|
||
|
ctf_set_errno (fp, ENOMEM);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Hash a type, possibly debugging-dumping something about it as well. */
|
||
|
static inline void
|
||
|
ctf_dedup_sha1_add (ctf_sha1_t *sha1, const void *buf, size_t len,
|
||
|
const char *description _libctf_unused_,
|
||
|
unsigned long depth _libctf_unused_)
|
||
|
{
|
||
|
ctf_sha1_add (sha1, buf, len);
|
||
|
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_sha1_t tmp;
|
||
|
char tmp_hval[CTF_SHA1_SIZE];
|
||
|
tmp = *sha1;
|
||
|
ctf_sha1_fini (&tmp, tmp_hval);
|
||
|
ctf_dprintf ("%lu: after hash addition of %s: %s\n", depth, description,
|
||
|
tmp_hval);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
static const char *
|
||
|
ctf_dedup_hash_type (ctf_dict_t *fp, ctf_dict_t *input,
|
||
|
ctf_dict_t **inputs, uint32_t *parents,
|
||
|
int input_num, ctf_id_t type, int flags,
|
||
|
unsigned long depth,
|
||
|
int (*populate_fun) (ctf_dict_t *fp,
|
||
|
ctf_dict_t *input,
|
||
|
ctf_dict_t **inputs,
|
||
|
int input_num,
|
||
|
ctf_id_t type,
|
||
|
void *id,
|
||
|
const char *decorated_name,
|
||
|
const char *hash));
|
||
|
|
||
|
/* Determine whether this type is being hashed as a stub (in which case it is
|
||
|
unsafe to cache it). */
|
||
|
static int
|
||
|
ctf_dedup_is_stub (const char *name, int kind, int fwdkind, int flags)
|
||
|
{
|
||
|
/* We can cache all types unless we are recursing to children and are hashing
|
||
|
in a tagged struct, union or forward, all of which are replaced with their
|
||
|
decorated name as a stub and will have different hash values when hashed at
|
||
|
the top level. */
|
||
|
|
||
|
return ((flags & CTF_DEDUP_HASH_INTERNAL_CHILD) && name
|
||
|
&& (kind == CTF_K_STRUCT || kind == CTF_K_UNION
|
||
|
|| (kind == CTF_K_FORWARD && (fwdkind == CTF_K_STRUCT
|
||
|
|| fwdkind == CTF_K_UNION))));
|
||
|
}
|
||
|
|
||
|
/* Populate struct_origin if need be (not already populated, or populated with
|
||
|
a different origin), in which case it must go to -1, "shared".)
|
||
|
|
||
|
Only called for forwards or forwardable types with names, when the link mode
|
||
|
is CTF_LINK_SHARE_DUPLICATED. */
|
||
|
static int
|
||
|
ctf_dedup_record_origin (ctf_dict_t *fp, int input_num, const char *decorated,
|
||
|
void *id)
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
void *origin;
|
||
|
int populate_origin = 0;
|
||
|
|
||
|
if (ctf_dynhash_lookup_kv (d->cd_struct_origin, decorated, NULL, &origin))
|
||
|
{
|
||
|
if (CTF_DEDUP_GID_TO_INPUT (origin) != input_num
|
||
|
&& CTF_DEDUP_GID_TO_INPUT (origin) != -1)
|
||
|
{
|
||
|
populate_origin = 1;
|
||
|
origin = CTF_DEDUP_GID (fp, -1, -1);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
populate_origin = 1;
|
||
|
origin = id;
|
||
|
}
|
||
|
|
||
|
if (populate_origin)
|
||
|
if (ctf_dynhash_cinsert (d->cd_struct_origin, decorated, origin) < 0)
|
||
|
return ctf_set_errno (fp, errno);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Do the underlying hashing and recursion for ctf_dedup_hash_type (which it
|
||
|
calls, recursively). */
|
||
|
|
||
|
static const char *
|
||
|
ctf_dedup_rhash_type (ctf_dict_t *fp, ctf_dict_t *input, ctf_dict_t **inputs,
|
||
|
uint32_t *parents, int input_num, ctf_id_t type,
|
||
|
void *type_id, const ctf_type_t *tp, const char *name,
|
||
|
const char *decorated, int kind, int flags,
|
||
|
unsigned long depth,
|
||
|
int (*populate_fun) (ctf_dict_t *fp,
|
||
|
ctf_dict_t *input,
|
||
|
ctf_dict_t **inputs,
|
||
|
int input_num,
|
||
|
ctf_id_t type,
|
||
|
void *id,
|
||
|
const char *decorated_name,
|
||
|
const char *hash))
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
ctf_next_t *i = NULL;
|
||
|
ctf_sha1_t hash;
|
||
|
ctf_id_t child_type;
|
||
|
char hashbuf[CTF_SHA1_SIZE];
|
||
|
const char *hval = NULL;
|
||
|
const char *whaterr;
|
||
|
int err = 0;
|
||
|
|
||
|
const char *citer = NULL;
|
||
|
ctf_dynset_t *citers = NULL;
|
||
|
|
||
|
/* Add a citer to the citers set. */
|
||
|
#define ADD_CITER(citers, hval) \
|
||
|
do \
|
||
|
{ \
|
||
|
whaterr = N_("error updating citers"); \
|
||
|
if (!citers) \
|
||
|
if ((citers = ctf_dynset_create (htab_hash_string, \
|
||
|
htab_eq_string, \
|
||
|
NULL)) == NULL) \
|
||
|
goto oom; \
|
||
|
if (ctf_dynset_cinsert (citers, hval) < 0) \
|
||
|
goto oom; \
|
||
|
} \
|
||
|
while (0)
|
||
|
|
||
|
/* If this is a named struct or union or a forward to one, and this is a child
|
||
|
traversal, treat this type as if it were a forward -- do not recurse to
|
||
|
children, ignore all content not already hashed in, and hash in the
|
||
|
decorated name of the type instead. */
|
||
|
|
||
|
if (ctf_dedup_is_stub (name, kind, tp->ctt_type, flags))
|
||
|
{
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_dprintf ("Struct/union/forward citation: substituting forwarding "
|
||
|
"stub with decorated name %s\n", decorated);
|
||
|
|
||
|
#endif
|
||
|
ctf_sha1_init (&hash);
|
||
|
ctf_dedup_sha1_add (&hash, decorated, strlen (decorated) + 1,
|
||
|
"decorated struct/union/forward name", depth);
|
||
|
ctf_sha1_fini (&hash, hashbuf);
|
||
|
|
||
|
if ((hval = intern (fp, strdup (hashbuf))) == NULL)
|
||
|
{
|
||
|
ctf_err_warn (fp, 0, 0, _("%s (%i): out of memory during forwarding-"
|
||
|
"stub hashing for type with GID %p"),
|
||
|
ctf_link_input_name (input), input_num, type_id);
|
||
|
return NULL; /* errno is set for us. */
|
||
|
}
|
||
|
|
||
|
/* In share-duplicated link mode, make sure the origin of this type is
|
||
|
recorded, even if this is a type in a parent dict which will not be
|
||
|
directly traversed. */
|
||
|
if (d->cd_link_flags & CTF_LINK_SHARE_DUPLICATED
|
||
|
&& ctf_dedup_record_origin (fp, input_num, decorated, type_id) < 0)
|
||
|
return NULL; /* errno is set for us. */
|
||
|
|
||
|
return hval;
|
||
|
}
|
||
|
|
||
|
/* Now ensure that subsequent recursive calls (but *not* the top-level call)
|
||
|
get this treatment. */
|
||
|
flags |= CTF_DEDUP_HASH_INTERNAL_CHILD;
|
||
|
|
||
|
/* If this is a struct, union, or forward with a name, record the unique
|
||
|
originating input TU, if there is one. */
|
||
|
|
||
|
if (decorated && (ctf_forwardable_kind (kind) || kind != CTF_K_FORWARD))
|
||
|
if (d->cd_link_flags & CTF_LINK_SHARE_DUPLICATED
|
||
|
&& ctf_dedup_record_origin (fp, input_num, decorated, type_id) < 0)
|
||
|
return NULL; /* errno is set for us. */
|
||
|
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_dprintf ("%lu: hashing thing with ID %i/%lx (kind %i): %s.\n",
|
||
|
depth, input_num, type, kind, name ? name : "");
|
||
|
#endif
|
||
|
|
||
|
/* Some type kinds don't have names: the API provides no way to set the name,
|
||
|
so the type the deduplicator outputs will be nameless even if the input
|
||
|
somehow has a name, and the name should not be mixed into the hash. */
|
||
|
|
||
|
switch (kind)
|
||
|
{
|
||
|
case CTF_K_POINTER:
|
||
|
case CTF_K_ARRAY:
|
||
|
case CTF_K_FUNCTION:
|
||
|
case CTF_K_VOLATILE:
|
||
|
case CTF_K_CONST:
|
||
|
case CTF_K_RESTRICT:
|
||
|
case CTF_K_SLICE:
|
||
|
name = NULL;
|
||
|
}
|
||
|
|
||
|
/* Mix in invariant stuff, transforming the type kind if needed. Note that
|
||
|
the vlen is *not* hashed in: the actual variable-length info is hashed in
|
||
|
instead, piecewise. The vlen is not part of the type, only the
|
||
|
variable-length data is: identical types with distinct vlens are quite
|
||
|
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.*/
|
||
|
|
||
|
ctf_sha1_init (&hash);
|
||
|
if (name)
|
||
|
ctf_dedup_sha1_add (&hash, name, strlen (name) + 1, "name", depth);
|
||
|
ctf_dedup_sha1_add (&hash, &kind, sizeof (uint32_t), "kind", depth);
|
||
|
|
||
|
/* Hash content of this type. */
|
||
|
switch (kind)
|
||
|
{
|
||
|
case CTF_K_UNKNOWN:
|
||
|
/* No extra state. */
|
||
|
break;
|
||
|
case CTF_K_FORWARD:
|
||
|
|
||
|
/* Add the forwarded kind, stored in the ctt_type. */
|
||
|
ctf_dedup_sha1_add (&hash, &tp->ctt_type, sizeof (tp->ctt_type),
|
||
|
"forwarded kind", depth);
|
||
|
break;
|
||
|
case CTF_K_INTEGER:
|
||
|
case CTF_K_FLOAT:
|
||
|
{
|
||
|
ctf_encoding_t ep;
|
||
|
memset (&ep, 0, sizeof (ctf_encoding_t));
|
||
|
|
||
|
ctf_dedup_sha1_add (&hash, &tp->ctt_size, sizeof (uint32_t), "size",
|
||
|
depth);
|
||
|
if (ctf_type_encoding (input, type, &ep) < 0)
|
||
|
{
|
||
|
whaterr = N_("error getting encoding");
|
||
|
goto input_err;
|
||
|
}
|
||
|
ctf_dedup_sha1_add (&hash, &ep, sizeof (ctf_encoding_t), "encoding",
|
||
|
depth);
|
||
|
break;
|
||
|
}
|
||
|
/* Types that reference other types. */
|
||
|
case CTF_K_TYPEDEF:
|
||
|
case CTF_K_VOLATILE:
|
||
|
case CTF_K_CONST:
|
||
|
case CTF_K_RESTRICT:
|
||
|
case CTF_K_POINTER:
|
||
|
/* Hash the referenced type, if not already hashed, and mix it in. */
|
||
|
child_type = ctf_type_reference (input, type);
|
||
|
if ((hval = ctf_dedup_hash_type (fp, input, inputs, parents, input_num,
|
||
|
child_type, flags, depth,
|
||
|
populate_fun)) == NULL)
|
||
|
{
|
||
|
whaterr = N_("error doing referenced type hashing");
|
||
|
goto err;
|
||
|
}
|
||
|
ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "referenced type",
|
||
|
depth);
|
||
|
citer = hval;
|
||
|
|
||
|
break;
|
||
|
|
||
|
/* The slices of two types hash identically only if the type they overlay
|
||
|
also has the same encoding. This is not ideal, but in practice will work
|
||
|
well enough. We work directly rather than using the CTF API because
|
||
|
we do not want the slice's normal automatically-shine-through
|
||
|
semantics to kick in here. */
|
||
|
case CTF_K_SLICE:
|
||
|
{
|
||
|
const ctf_slice_t *slice;
|
||
|
const ctf_dtdef_t *dtd;
|
||
|
ssize_t size;
|
||
|
ssize_t increment;
|
||
|
|
||
|
child_type = ctf_type_reference (input, type);
|
||
|
ctf_get_ctt_size (input, tp, &size, &increment);
|
||
|
ctf_dedup_sha1_add (&hash, &size, sizeof (ssize_t), "size", depth);
|
||
|
|
||
|
if ((hval = ctf_dedup_hash_type (fp, input, inputs, parents, input_num,
|
||
|
child_type, flags, depth,
|
||
|
populate_fun)) == NULL)
|
||
|
{
|
||
|
whaterr = N_("error doing slice-referenced type hashing");
|
||
|
goto err;
|
||
|
}
|
||
|
ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "sliced type",
|
||
|
depth);
|
||
|
citer = hval;
|
||
|
|
||
|
if ((dtd = ctf_dynamic_type (input, type)) != NULL)
|
||
|
slice = (ctf_slice_t *) dtd->dtd_vlen;
|
||
|
else
|
||
|
slice = (ctf_slice_t *) ((uintptr_t) tp + increment);
|
||
|
|
||
|
ctf_dedup_sha1_add (&hash, &slice->cts_offset,
|
||
|
sizeof (slice->cts_offset), "slice offset", depth);
|
||
|
ctf_dedup_sha1_add (&hash, &slice->cts_bits,
|
||
|
sizeof (slice->cts_bits), "slice bits", depth);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CTF_K_ARRAY:
|
||
|
{
|
||
|
ctf_arinfo_t ar;
|
||
|
|
||
|
if (ctf_array_info (input, type, &ar) < 0)
|
||
|
{
|
||
|
whaterr = N_("error getting array info");
|
||
|
goto input_err;
|
||
|
}
|
||
|
|
||
|
if ((hval = ctf_dedup_hash_type (fp, input, inputs, parents, input_num,
|
||
|
ar.ctr_contents, flags, depth,
|
||
|
populate_fun)) == NULL)
|
||
|
{
|
||
|
whaterr = N_("error doing array contents type hashing");
|
||
|
goto err;
|
||
|
}
|
||
|
ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "array contents",
|
||
|
depth);
|
||
|
ADD_CITER (citers, hval);
|
||
|
|
||
|
if ((hval = ctf_dedup_hash_type (fp, input, inputs, parents, input_num,
|
||
|
ar.ctr_index, flags, depth,
|
||
|
populate_fun)) == NULL)
|
||
|
{
|
||
|
whaterr = N_("error doing array index type hashing");
|
||
|
goto err;
|
||
|
}
|
||
|
ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "array index",
|
||
|
depth);
|
||
|
ctf_dedup_sha1_add (&hash, &ar.ctr_nelems, sizeof (ar.ctr_nelems),
|
||
|
"element count", depth);
|
||
|
ADD_CITER (citers, hval);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case CTF_K_FUNCTION:
|
||
|
{
|
||
|
ctf_funcinfo_t fi;
|
||
|
ctf_id_t *args;
|
||
|
uint32_t j;
|
||
|
|
||
|
if (ctf_func_type_info (input, type, &fi) < 0)
|
||
|
{
|
||
|
whaterr = N_("error getting func type info");
|
||
|
goto input_err;
|
||
|
}
|
||
|
|
||
|
if ((hval = ctf_dedup_hash_type (fp, input, inputs, parents, input_num,
|
||
|
fi.ctc_return, flags, depth,
|
||
|
populate_fun)) == NULL)
|
||
|
{
|
||
|
whaterr = N_("error getting func return type");
|
||
|
goto err;
|
||
|
}
|
||
|
ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "func return",
|
||
|
depth);
|
||
|
ctf_dedup_sha1_add (&hash, &fi.ctc_argc, sizeof (fi.ctc_argc),
|
||
|
"func argc", depth);
|
||
|
ctf_dedup_sha1_add (&hash, &fi.ctc_flags, sizeof (fi.ctc_flags),
|
||
|
"func flags", depth);
|
||
|
ADD_CITER (citers, hval);
|
||
|
|
||
|
if ((args = calloc (fi.ctc_argc, sizeof (ctf_id_t))) == NULL)
|
||
|
{
|
||
|
err = ENOMEM;
|
||
|
whaterr = N_("error doing memory allocation");
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
if (ctf_func_type_args (input, type, fi.ctc_argc, args) < 0)
|
||
|
{
|
||
|
free (args);
|
||
|
whaterr = N_("error getting func arg type");
|
||
|
goto input_err;
|
||
|
}
|
||
|
for (j = 0; j < fi.ctc_argc; j++)
|
||
|
{
|
||
|
if ((hval = ctf_dedup_hash_type (fp, input, inputs, parents,
|
||
|
input_num, args[j], flags, depth,
|
||
|
populate_fun)) == NULL)
|
||
|
{
|
||
|
free (args);
|
||
|
whaterr = N_("error doing func arg type hashing");
|
||
|
goto err;
|
||
|
}
|
||
|
ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "func arg type",
|
||
|
depth);
|
||
|
ADD_CITER (citers, hval);
|
||
|
}
|
||
|
free (args);
|
||
|
break;
|
||
|
}
|
||
|
case CTF_K_ENUM:
|
||
|
{
|
||
|
int val;
|
||
|
const char *ename;
|
||
|
|
||
|
ctf_dedup_sha1_add (&hash, &tp->ctt_size, sizeof (uint32_t),
|
||
|
"enum size", depth);
|
||
|
while ((ename = ctf_enum_next (input, type, &i, &val)) != NULL)
|
||
|
{
|
||
|
ctf_dedup_sha1_add (&hash, ename, strlen (ename) + 1, "enumerator",
|
||
|
depth);
|
||
|
ctf_dedup_sha1_add (&hash, &val, sizeof (val), "enumerand", depth);
|
||
|
}
|
||
|
if (ctf_errno (input) != ECTF_NEXT_END)
|
||
|
{
|
||
|
whaterr = N_("error doing enum member iteration");
|
||
|
goto input_err;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
/* Top-level only. */
|
||
|
case CTF_K_STRUCT:
|
||
|
case CTF_K_UNION:
|
||
|
{
|
||
|
ssize_t offset;
|
||
|
const char *mname;
|
||
|
ctf_id_t membtype;
|
||
|
ssize_t size;
|
||
|
|
||
|
ctf_get_ctt_size (input, tp, &size, NULL);
|
||
|
ctf_dedup_sha1_add (&hash, &size, sizeof (ssize_t), "struct size",
|
||
|
depth);
|
||
|
|
||
|
while ((offset = ctf_member_next (input, type, &i, &mname, &membtype,
|
||
|
0)) >= 0)
|
||
|
{
|
||
|
if (mname == NULL)
|
||
|
mname = "";
|
||
|
ctf_dedup_sha1_add (&hash, mname, strlen (mname) + 1,
|
||
|
"member name", depth);
|
||
|
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_dprintf ("%lu: Traversing to member %s\n", depth, mname);
|
||
|
#endif
|
||
|
if ((hval = ctf_dedup_hash_type (fp, input, inputs, parents,
|
||
|
input_num, membtype, flags, depth,
|
||
|
populate_fun)) == NULL)
|
||
|
{
|
||
|
whaterr = N_("error doing struct/union member type hashing");
|
||
|
goto iterr;
|
||
|
}
|
||
|
|
||
|
ctf_dedup_sha1_add (&hash, hval, strlen (hval) + 1, "member hash",
|
||
|
depth);
|
||
|
ctf_dedup_sha1_add (&hash, &offset, sizeof (offset), "member offset",
|
||
|
depth);
|
||
|
ADD_CITER (citers, hval);
|
||
|
}
|
||
|
if (ctf_errno (input) != ECTF_NEXT_END)
|
||
|
{
|
||
|
whaterr = N_("error doing struct/union member iteration");
|
||
|
goto input_err;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
whaterr = N_("error: unknown type kind");
|
||
|
goto err;
|
||
|
}
|
||
|
ctf_sha1_fini (&hash, hashbuf);
|
||
|
|
||
|
if ((hval = intern (fp, strdup (hashbuf))) == NULL)
|
||
|
{
|
||
|
whaterr = N_("cannot intern hash");
|
||
|
goto oom;
|
||
|
}
|
||
|
|
||
|
/* Populate the citers for this type's subtypes, now the hash for the type
|
||
|
itself is known. */
|
||
|
whaterr = N_("error tracking citers");
|
||
|
|
||
|
if (citer)
|
||
|
{
|
||
|
ctf_dynset_t *citer_hashes;
|
||
|
|
||
|
if ((citer_hashes = make_set_element (d->cd_citers, citer)) == NULL)
|
||
|
goto oom;
|
||
|
if (ctf_dynset_cinsert (citer_hashes, hval) < 0)
|
||
|
goto oom;
|
||
|
}
|
||
|
else if (citers)
|
||
|
{
|
||
|
const void *k;
|
||
|
|
||
|
while ((err = ctf_dynset_cnext (citers, &i, &k)) == 0)
|
||
|
{
|
||
|
ctf_dynset_t *citer_hashes;
|
||
|
citer = (const char *) k;
|
||
|
|
||
|
if ((citer_hashes = make_set_element (d->cd_citers, citer)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
if (ctf_dynset_exists (citer_hashes, hval, NULL))
|
||
|
continue;
|
||
|
if (ctf_dynset_cinsert (citer_hashes, hval) < 0)
|
||
|
goto oom;
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
goto err;
|
||
|
ctf_dynset_destroy (citers);
|
||
|
}
|
||
|
|
||
|
return hval;
|
||
|
|
||
|
iterr:
|
||
|
ctf_next_destroy (i);
|
||
|
input_err:
|
||
|
err = ctf_errno (input);
|
||
|
err:
|
||
|
ctf_sha1_fini (&hash, NULL);
|
||
|
ctf_err_warn (fp, 0, err, _("%s (%i): %s: during type hashing for type %lx, "
|
||
|
"kind %i"), ctf_link_input_name (input),
|
||
|
input_num, gettext (whaterr), type, kind);
|
||
|
return NULL;
|
||
|
oom:
|
||
|
ctf_set_errno (fp, errno);
|
||
|
ctf_err_warn (fp, 0, 0, _("%s (%i): %s: during type hashing for type %lx, "
|
||
|
"kind %i"), ctf_link_input_name (input),
|
||
|
input_num, gettext (whaterr), type, kind);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* Hash a TYPE in the INPUT: FP is the eventual output, where the ctf_dedup
|
||
|
state is stored. INPUT_NUM is the number of this input in the set of inputs.
|
||
|
Record its hash in FP's cd_type_hashes once it is known. PARENTS is
|
||
|
described in the comment above ctf_dedup.
|
||
|
|
||
|
(The flags argument currently accepts only the flag
|
||
|
CTF_DEDUP_HASH_INTERNAL_CHILD, an implementation detail used to prevent
|
||
|
struct/union hashing in recursive traversals below the TYPE.)
|
||
|
|
||
|
We use the CTF API rather than direct access wherever possible, because types
|
||
|
that appear identical through the API should be considered identical, with
|
||
|
one exception: slices should only be considered identical to other slices,
|
||
|
not to the corresponding unsliced type.
|
||
|
|
||
|
The POPULATE_FUN is a mandatory hook that populates other mappings with each
|
||
|
type we see (excepting types that are recursively hashed as stubs). The
|
||
|
caller should not rely on the order of calls to this hook, though it will be
|
||
|
called at least once for every non-stub reference to every type.
|
||
|
|
||
|
Returns a hash value (an atom), or NULL on error. */
|
||
|
|
||
|
static const char *
|
||
|
ctf_dedup_hash_type (ctf_dict_t *fp, ctf_dict_t *input,
|
||
|
ctf_dict_t **inputs, uint32_t *parents,
|
||
|
int input_num, ctf_id_t type, int flags,
|
||
|
unsigned long depth,
|
||
|
int (*populate_fun) (ctf_dict_t *fp,
|
||
|
ctf_dict_t *input,
|
||
|
ctf_dict_t **inputs,
|
||
|
int input_num,
|
||
|
ctf_id_t type,
|
||
|
void *id,
|
||
|
const char *decorated_name,
|
||
|
const char *hash))
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
const ctf_type_t *tp;
|
||
|
void *type_id;
|
||
|
const char *hval = NULL;
|
||
|
const char *name;
|
||
|
const char *whaterr;
|
||
|
const char *decorated = NULL;
|
||
|
uint32_t kind, fwdkind;
|
||
|
|
||
|
depth++;
|
||
|
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_dprintf ("%lu: ctf_dedup_hash_type (%i, %lx, flags %x)\n", depth, input_num, type, flags);
|
||
|
#endif
|
||
|
|
||
|
/* The unimplemented type doesn't really exist, but must be noted in parent
|
||
|
hashes: so it gets a fixed, arbitrary hash. */
|
||
|
if (type == 0)
|
||
|
return "00000000000000000000";
|
||
|
|
||
|
/* Possible optimization: if the input type is in the parent type space, just
|
||
|
copy recursively-cited hashes from the parent's types into the output
|
||
|
mapping rather than rehashing them. */
|
||
|
|
||
|
type_id = CTF_DEDUP_GID (fp, input_num, type);
|
||
|
|
||
|
if ((tp = ctf_lookup_by_id (&input, type)) == NULL)
|
||
|
{
|
||
|
ctf_set_errno (fp, ctf_errno (input));
|
||
|
ctf_err_warn (fp, 0, 0, _("%s (%i): lookup failure for type %lx: "
|
||
|
"flags %x"), ctf_link_input_name (input),
|
||
|
input_num, type, flags);
|
||
|
return NULL; /* errno is set for us. */
|
||
|
}
|
||
|
|
||
|
kind = LCTF_INFO_KIND (input, tp->ctt_info);
|
||
|
name = ctf_strraw (input, tp->ctt_name);
|
||
|
|
||
|
if (tp->ctt_name == 0 || !name || name[0] == '\0')
|
||
|
name = NULL;
|
||
|
|
||
|
/* Decorate the name appropriately for the namespace it appears in: forwards
|
||
|
appear in the namespace of their referent. */
|
||
|
|
||
|
fwdkind = kind;
|
||
|
if (name)
|
||
|
{
|
||
|
if (kind == CTF_K_FORWARD)
|
||
|
fwdkind = tp->ctt_type;
|
||
|
|
||
|
if ((decorated = ctf_decorate_type_name (fp, name, fwdkind)) == NULL)
|
||
|
return NULL; /* errno is set for us. */
|
||
|
}
|
||
|
|
||
|
/* If not hashing a stub, we can rely on various sorts of caches.
|
||
|
|
||
|
Optimization opportunity: we may be able to avoid calling the populate_fun
|
||
|
sometimes here. */
|
||
|
|
||
|
if (!ctf_dedup_is_stub (name, kind, fwdkind, flags))
|
||
|
{
|
||
|
if ((hval = ctf_dynhash_lookup (d->cd_type_hashes, type_id)) != NULL)
|
||
|
{
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
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,
|
||
|
decorated, hval);
|
||
|
|
||
|
return hval;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* We have never seen this type before, and must figure out its hash and the
|
||
|
hashes of the types it cites.
|
||
|
|
||
|
Hash this type, and call ourselves recursively. (The hashing part is
|
||
|
optional, and is disabled if overidden_hval is set.) */
|
||
|
|
||
|
if ((hval = ctf_dedup_rhash_type (fp, input, inputs, parents, input_num,
|
||
|
type, type_id, tp, name, decorated,
|
||
|
kind, flags, depth, populate_fun)) == NULL)
|
||
|
return NULL; /* errno is set for us. */
|
||
|
|
||
|
/* The hash of this type is now known: record it unless caching is unsafe
|
||
|
because the hash value will change later. This will be the final storage
|
||
|
of this type's hash, so we call the population function on it. */
|
||
|
|
||
|
if (!ctf_dedup_is_stub (name, kind, fwdkind, flags))
|
||
|
{
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_dprintf ("Caching %lx, ID %p (%s), %s in final location\n", type,
|
||
|
type_id, name ? name : "", hval);
|
||
|
#endif
|
||
|
|
||
|
if (ctf_dynhash_cinsert (d->cd_type_hashes, type_id, hval) < 0)
|
||
|
{
|
||
|
whaterr = N_("error hash caching");
|
||
|
goto oom;
|
||
|
}
|
||
|
|
||
|
if (populate_fun (fp, input, inputs, input_num, type, type_id,
|
||
|
decorated, hval) < 0)
|
||
|
{
|
||
|
whaterr = N_("error calling population function");
|
||
|
goto err; /* errno is set for us. */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_dprintf ("%lu: Returning final hash for ID %i/%lx: %s\n", depth,
|
||
|
input_num, type, hval);
|
||
|
#endif
|
||
|
return hval;
|
||
|
|
||
|
oom:
|
||
|
ctf_set_errno (fp, errno);
|
||
|
err:
|
||
|
ctf_err_warn (fp, 0, 0, _("%s (%i): %s: during type hashing, "
|
||
|
"type %lx, kind %i"),
|
||
|
ctf_link_input_name (input), input_num,
|
||
|
gettext (whaterr), type, kind);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* 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. */
|
||
|
|
||
|
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,
|
||
|
const char *hval)
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
ctf_dynset_t *type_ids;
|
||
|
ctf_dynhash_t *name_counts;
|
||
|
long int count;
|
||
|
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_dprintf ("Hash %s, %s, into output mapping for %i/%lx @ %s\n",
|
||
|
hval, decorated_name ? decorated_name : "(unnamed)",
|
||
|
input_num, type, ctf_link_input_name (input));
|
||
|
|
||
|
const char *orig_hval;
|
||
|
|
||
|
/* Make sure we never map a single GID to multiple hash values. */
|
||
|
|
||
|
if ((orig_hval = ctf_dynhash_lookup (d->cd_output_mapping_guard, id)) != NULL)
|
||
|
{
|
||
|
/* We can rely on pointer identity here, since all hashes are
|
||
|
interned. */
|
||
|
if (!ctf_assert (fp, orig_hval == hval))
|
||
|
return -1;
|
||
|
}
|
||
|
else
|
||
|
if (ctf_dynhash_cinsert (d->cd_output_mapping_guard, id, hval) < 0)
|
||
|
return ctf_set_errno (fp, errno);
|
||
|
#endif
|
||
|
|
||
|
/* Record the type in the output mapping: if this is the first time this type
|
||
|
has been seen, also record it in the cd_output_first_gid. Because we
|
||
|
traverse types in TU order and we do not merge types after the hashing
|
||
|
phase, this will be the lowest TU this type ever appears in. */
|
||
|
|
||
|
if ((type_ids = ctf_dynhash_lookup (d->cd_output_mapping,
|
||
|
hval)) == NULL)
|
||
|
{
|
||
|
if (ctf_dynhash_cinsert (d->cd_output_first_gid, hval, id) < 0)
|
||
|
return ctf_set_errno (fp, errno);
|
||
|
|
||
|
if ((type_ids = ctf_dynset_create (htab_hash_pointer,
|
||
|
htab_eq_pointer,
|
||
|
NULL)) == NULL)
|
||
|
return ctf_set_errno (fp, errno);
|
||
|
if (ctf_dynhash_insert (d->cd_output_mapping, (void *) hval,
|
||
|
type_ids) < 0)
|
||
|
{
|
||
|
ctf_dynset_destroy (type_ids);
|
||
|
return ctf_set_errno (fp, errno);
|
||
|
}
|
||
|
}
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
{
|
||
|
/* Verify that all types with this hash are of the same kind, and that the
|
||
|
first TU a type was seen in never falls. */
|
||
|
|
||
|
int err;
|
||
|
const void *one_id;
|
||
|
ctf_next_t *i = NULL;
|
||
|
int orig_kind = ctf_type_kind_unsliced (input, type);
|
||
|
int orig_first_tu;
|
||
|
|
||
|
orig_first_tu = CTF_DEDUP_GID_TO_INPUT
|
||
|
(ctf_dynhash_lookup (d->cd_output_first_gid, hval));
|
||
|
if (!ctf_assert (fp, orig_first_tu <= CTF_DEDUP_GID_TO_INPUT (id)))
|
||
|
return -1;
|
||
|
|
||
|
while ((err = ctf_dynset_cnext (type_ids, &i, &one_id)) == 0)
|
||
|
{
|
||
|
ctf_dict_t *foo = inputs[CTF_DEDUP_GID_TO_INPUT (one_id)];
|
||
|
ctf_id_t bar = CTF_DEDUP_GID_TO_TYPE (one_id);
|
||
|
if (ctf_type_kind_unsliced (foo, bar) != orig_kind)
|
||
|
{
|
||
|
ctf_err_warn (fp, 1, 0, "added wrong kind to output mapping "
|
||
|
"for hash %s named %s: %p/%lx from %s is "
|
||
|
"kind %i, but newly-added %p/%lx from %s is "
|
||
|
"kind %i", hval,
|
||
|
decorated_name ? decorated_name : "(unnamed)",
|
||
|
(void *) foo, bar,
|
||
|
ctf_link_input_name (foo),
|
||
|
ctf_type_kind_unsliced (foo, bar),
|
||
|
(void *) input, type,
|
||
|
ctf_link_input_name (input), orig_kind);
|
||
|
if (!ctf_assert (fp, ctf_type_kind_unsliced (foo, bar)
|
||
|
== orig_kind))
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
return ctf_set_errno (fp, err);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* 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)
|
||
|
&& ctf_dynset_insert (type_ids, id) < 0)
|
||
|
return ctf_set_errno (fp, errno);
|
||
|
|
||
|
/* The rest only needs to happen for types with names. */
|
||
|
if (!decorated_name)
|
||
|
return 0;
|
||
|
|
||
|
/* Count the number of occurrences of the hash value for this GID. */
|
||
|
|
||
|
hval = ctf_dynhash_lookup (d->cd_type_hashes, id);
|
||
|
|
||
|
/* Mapping from name -> hash(hashval, count) not already present? */
|
||
|
if ((name_counts = ctf_dynhash_lookup (d->cd_name_counts,
|
||
|
decorated_name)) == NULL)
|
||
|
{
|
||
|
if ((name_counts = ctf_dynhash_create (ctf_hash_string,
|
||
|
ctf_hash_eq_string,
|
||
|
NULL, NULL)) == NULL)
|
||
|
return ctf_set_errno (fp, errno);
|
||
|
if (ctf_dynhash_cinsert (d->cd_name_counts, decorated_name,
|
||
|
name_counts) < 0)
|
||
|
{
|
||
|
ctf_dynhash_destroy (name_counts);
|
||
|
return ctf_set_errno (fp, errno);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* This will, conveniently, return NULL (i.e. 0) for a new entry. */
|
||
|
count = (long int) (uintptr_t) ctf_dynhash_lookup (name_counts, hval);
|
||
|
|
||
|
if (ctf_dynhash_cinsert (name_counts, hval,
|
||
|
(const void *) (uintptr_t) (count + 1)) < 0)
|
||
|
return ctf_set_errno (fp, errno);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Mark a single hash as corresponding to a conflicting type. Mark all types
|
||
|
that cite it as conflicting as well, terminating the recursive walk only when
|
||
|
types that are already conflicted or types do not cite other types are seen.
|
||
|
(Tagged structures and unions do not appear in the cd_citers graph, so the
|
||
|
walk also terminates there, since any reference to a conflicting structure is
|
||
|
just going to reference an unconflicting forward instead: see
|
||
|
ctf_dedup_maybe_synthesize_forward.) */
|
||
|
|
||
|
static int
|
||
|
ctf_dedup_mark_conflicting_hash (ctf_dict_t *fp, const char *hval)
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
ctf_next_t *i = NULL;
|
||
|
int err;
|
||
|
const void *k;
|
||
|
ctf_dynset_t *citers;
|
||
|
|
||
|
/* Mark conflicted if not already so marked. */
|
||
|
if (ctf_dynset_exists (d->cd_conflicting_types, hval, NULL))
|
||
|
return 0;
|
||
|
|
||
|
ctf_dprintf ("Marking %s as conflicted\n", hval);
|
||
|
|
||
|
if (ctf_dynset_cinsert (d->cd_conflicting_types, hval) < 0)
|
||
|
{
|
||
|
ctf_dprintf ("Out of memory marking %s as conflicted\n", hval);
|
||
|
ctf_set_errno (fp, errno);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* If any types cite this type, mark them conflicted too. */
|
||
|
if ((citers = ctf_dynhash_lookup (d->cd_citers, hval)) == NULL)
|
||
|
return 0;
|
||
|
|
||
|
while ((err = ctf_dynset_cnext (citers, &i, &k)) == 0)
|
||
|
{
|
||
|
const char *hv = (const char *) k;
|
||
|
|
||
|
if (ctf_dynset_exists (d->cd_conflicting_types, hv, NULL))
|
||
|
continue;
|
||
|
|
||
|
if (ctf_dedup_mark_conflicting_hash (fp, hv) < 0)
|
||
|
{
|
||
|
ctf_next_destroy (i);
|
||
|
return -1; /* errno is set for us. */
|
||
|
}
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
return ctf_set_errno (fp, err);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Look up a type kind from the output mapping, given a type hash value. */
|
||
|
static int
|
||
|
ctf_dedup_hash_kind (ctf_dict_t *fp, ctf_dict_t **inputs, const char *hash)
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
void *id;
|
||
|
ctf_dynset_t *type_ids;
|
||
|
|
||
|
/* Precondition: the output mapping is populated. */
|
||
|
if (!ctf_assert (fp, ctf_dynhash_elements (d->cd_output_mapping) > 0))
|
||
|
return -1;
|
||
|
|
||
|
/* Look up some GID from the output hash for this type. (They are all
|
||
|
identical, so we can pick any). Don't assert if someone calls this
|
||
|
function wrongly, but do assert if the output mapping knows about the hash,
|
||
|
but has nothing associated with it. */
|
||
|
|
||
|
type_ids = ctf_dynhash_lookup (d->cd_output_mapping, hash);
|
||
|
if (!type_ids)
|
||
|
{
|
||
|
ctf_dprintf ("Looked up type kind by nonexistent hash %s.\n", hash);
|
||
|
return ctf_set_errno (fp, ECTF_INTERNAL);
|
||
|
}
|
||
|
id = ctf_dynset_lookup_any (type_ids);
|
||
|
if (!ctf_assert (fp, id))
|
||
|
return -1;
|
||
|
|
||
|
return ctf_type_kind_unsliced (inputs[CTF_DEDUP_GID_TO_INPUT (id)],
|
||
|
CTF_DEDUP_GID_TO_TYPE (id));
|
||
|
}
|
||
|
|
||
|
/* Used to keep a count of types: i.e. distinct type hash values. */
|
||
|
typedef struct ctf_dedup_type_counter
|
||
|
{
|
||
|
ctf_dict_t *fp;
|
||
|
ctf_dict_t **inputs;
|
||
|
int num_non_forwards;
|
||
|
} ctf_dedup_type_counter_t;
|
||
|
|
||
|
/* Add to the type counter for one name entry from the cd_name_counts. */
|
||
|
static int
|
||
|
ctf_dedup_count_types (void *key_, void *value _libctf_unused_, void *arg_)
|
||
|
{
|
||
|
const char *hval = (const char *) key_;
|
||
|
int kind;
|
||
|
ctf_dedup_type_counter_t *arg = (ctf_dedup_type_counter_t *) arg_;
|
||
|
|
||
|
kind = ctf_dedup_hash_kind (arg->fp, arg->inputs, hval);
|
||
|
|
||
|
/* We rely on ctf_dedup_hash_kind setting the fp to -ECTF_INTERNAL on error to
|
||
|
smuggle errors out of here. */
|
||
|
|
||
|
if (kind != CTF_K_FORWARD)
|
||
|
{
|
||
|
arg->num_non_forwards++;
|
||
|
ctf_dprintf ("Counting hash %s: kind %i: num_non_forwards is %i\n",
|
||
|
hval, kind, arg->num_non_forwards);
|
||
|
}
|
||
|
|
||
|
/* We only need to know if there is more than one non-forward (an ambiguous
|
||
|
type): don't waste time iterating any more than needed to figure that
|
||
|
out. */
|
||
|
|
||
|
if (arg->num_non_forwards > 1)
|
||
|
return 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Detect name ambiguity and mark ambiguous names as conflicting, other than the
|
||
|
most common. */
|
||
|
static int
|
||
|
ctf_dedup_detect_name_ambiguity (ctf_dict_t *fp, ctf_dict_t **inputs)
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
ctf_next_t *i = NULL;
|
||
|
void *k;
|
||
|
void *v;
|
||
|
int err;
|
||
|
const char *whaterr;
|
||
|
|
||
|
/* Go through cd_name_counts for all CTF namespaces in turn. */
|
||
|
|
||
|
while ((err = ctf_dynhash_next (d->cd_name_counts, &i, &k, &v)) == 0)
|
||
|
{
|
||
|
const char *decorated = (const char *) k;
|
||
|
ctf_dynhash_t *name_counts = (ctf_dynhash_t *) v;
|
||
|
ctf_next_t *j = NULL;
|
||
|
|
||
|
/* If this is a forwardable kind or a forward (which we can tell without
|
||
|
consulting the type because its decorated name has a space as its
|
||
|
second character: see ctf_decorate_type_name), we are only interested
|
||
|
in whether this name has many hashes associated with it: any such name
|
||
|
is necessarily ambiguous, and types with that name are conflicting.
|
||
|
Once we know whether this is true, we can skip to the next name: so use
|
||
|
ctf_dynhash_iter_find for efficiency. */
|
||
|
|
||
|
if (decorated[0] != '\0' && decorated[1] == ' ')
|
||
|
{
|
||
|
ctf_dedup_type_counter_t counters = { fp, inputs, 0 };
|
||
|
ctf_dynhash_t *counts = (ctf_dynhash_t *) v;
|
||
|
|
||
|
ctf_dynhash_iter_find (counts, ctf_dedup_count_types, &counters);
|
||
|
|
||
|
/* Check for assertion failure and pass it up. */
|
||
|
if (ctf_errno (fp) == ECTF_INTERNAL)
|
||
|
goto assert_err;
|
||
|
|
||
|
if (counters.num_non_forwards > 1)
|
||
|
{
|
||
|
const void *hval_;
|
||
|
|
||
|
while ((err = ctf_dynhash_cnext (counts, &j, &hval_, NULL)) == 0)
|
||
|
{
|
||
|
const char *hval = (const char *) hval_;
|
||
|
ctf_dynset_t *type_ids;
|
||
|
void *id;
|
||
|
int kind;
|
||
|
|
||
|
/* Dig through the types in this hash to find the non-forwards
|
||
|
and mark them ambiguous. */
|
||
|
|
||
|
type_ids = ctf_dynhash_lookup (d->cd_output_mapping, hval);
|
||
|
|
||
|
/* Nonexistent? Must be a forward with no referent. */
|
||
|
if (!type_ids)
|
||
|
continue;
|
||
|
|
||
|
id = ctf_dynset_lookup_any (type_ids);
|
||
|
|
||
|
kind = ctf_type_kind (inputs[CTF_DEDUP_GID_TO_INPUT (id)],
|
||
|
CTF_DEDUP_GID_TO_TYPE (id));
|
||
|
|
||
|
if (kind != CTF_K_FORWARD)
|
||
|
{
|
||
|
ctf_dprintf ("Marking %p, with hash %s, conflicting: one "
|
||
|
"of many non-forward GIDs for %s\n", id,
|
||
|
hval, (char *) k);
|
||
|
ctf_dedup_mark_conflicting_hash (fp, hval);
|
||
|
}
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
{
|
||
|
whaterr = N_("error marking conflicting structs/unions");
|
||
|
goto iterr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* This is an ordinary type. Find the most common type with this
|
||
|
name, and mark it unconflicting: all others are conflicting. (We
|
||
|
cannot do this sort of popularity contest with forwardable types
|
||
|
because any forwards to that type would be immediately unified with
|
||
|
the most-popular type on insertion, and we want conflicting structs
|
||
|
et al to have all forwards left intact, so the user is notified
|
||
|
that this type is conflicting. TODO: improve this in future by
|
||
|
setting such forwards non-root-visible.) */
|
||
|
|
||
|
const void *key;
|
||
|
const void *count;
|
||
|
const char *hval;
|
||
|
long max_hcount = -1;
|
||
|
const char *max_hval = NULL;
|
||
|
|
||
|
if (ctf_dynhash_elements (name_counts) <= 1)
|
||
|
continue;
|
||
|
|
||
|
/* First find the most common. */
|
||
|
while ((err = ctf_dynhash_cnext (name_counts, &j, &key, &count)) == 0)
|
||
|
{
|
||
|
hval = (const char *) key;
|
||
|
if ((long int) (uintptr_t) count > max_hcount)
|
||
|
{
|
||
|
max_hcount = (long int) (uintptr_t) count;
|
||
|
max_hval = hval;
|
||
|
}
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
{
|
||
|
whaterr = N_("error finding commonest conflicting type");
|
||
|
goto iterr;
|
||
|
}
|
||
|
|
||
|
/* Mark all the others as conflicting. */
|
||
|
while ((err = ctf_dynhash_cnext (name_counts, &j, &key, NULL)) == 0)
|
||
|
{
|
||
|
hval = (const char *) key;
|
||
|
if (strcmp (max_hval, hval) == 0)
|
||
|
continue;
|
||
|
|
||
|
ctf_dprintf ("Marking %s, an uncommon hash for %s, conflicting\n",
|
||
|
hval, (const char *) k);
|
||
|
if (ctf_dedup_mark_conflicting_hash (fp, hval) < 0)
|
||
|
{
|
||
|
whaterr = N_("error marking hashes as conflicting");
|
||
|
goto err;
|
||
|
}
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
{
|
||
|
whaterr = N_("marking uncommon conflicting types");
|
||
|
goto iterr;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
{
|
||
|
whaterr = N_("scanning for ambiguous names");
|
||
|
goto iterr;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err:
|
||
|
ctf_next_destroy (i);
|
||
|
ctf_err_warn (fp, 0, 0, "%s", gettext (whaterr));
|
||
|
return -1; /* errno is set for us. */
|
||
|
|
||
|
iterr:
|
||
|
ctf_err_warn (fp, 0, err, _("iteration failed: %s"), gettext (whaterr));
|
||
|
return ctf_set_errno (fp, err);
|
||
|
|
||
|
assert_err:
|
||
|
ctf_next_destroy (i);
|
||
|
return -1; /* errno is set for us. */
|
||
|
}
|
||
|
|
||
|
/* Initialize the deduplication machinery. */
|
||
|
|
||
|
static int
|
||
|
ctf_dedup_init (ctf_dict_t *fp)
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
size_t i;
|
||
|
|
||
|
if (ctf_dedup_atoms_init (fp) < 0)
|
||
|
goto oom;
|
||
|
|
||
|
#if IDS_NEED_ALLOCATION
|
||
|
if ((d->cd_id_to_dict_t = ctf_dynhash_create (ctf_hash_type_id_key,
|
||
|
ctf_hash_eq_type_id_key,
|
||
|
free, NULL)) == NULL)
|
||
|
goto oom;
|
||
|
#endif
|
||
|
|
||
|
for (i = 0; i < 4; i++)
|
||
|
{
|
||
|
if ((d->cd_decorated_names[i] = ctf_dynhash_create (ctf_hash_string,
|
||
|
ctf_hash_eq_string,
|
||
|
NULL, NULL)) == NULL)
|
||
|
goto oom;
|
||
|
}
|
||
|
|
||
|
if ((d->cd_name_counts
|
||
|
= ctf_dynhash_create (ctf_hash_string,
|
||
|
ctf_hash_eq_string, NULL,
|
||
|
(ctf_hash_free_fun) ctf_dynhash_destroy)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
if ((d->cd_type_hashes
|
||
|
= ctf_dynhash_create (ctf_hash_integer,
|
||
|
ctf_hash_eq_integer,
|
||
|
NULL, NULL)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
if ((d->cd_struct_origin
|
||
|
= ctf_dynhash_create (ctf_hash_string,
|
||
|
ctf_hash_eq_string,
|
||
|
NULL, NULL)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
if ((d->cd_citers
|
||
|
= ctf_dynhash_create (ctf_hash_string,
|
||
|
ctf_hash_eq_string, NULL,
|
||
|
(ctf_hash_free_fun) ctf_dynset_destroy)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
if ((d->cd_output_mapping
|
||
|
= ctf_dynhash_create (ctf_hash_string,
|
||
|
ctf_hash_eq_string, NULL,
|
||
|
(ctf_hash_free_fun) ctf_dynset_destroy)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
if ((d->cd_output_first_gid
|
||
|
= 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,
|
||
|
ctf_hash_eq_integer, NULL, NULL)) == NULL)
|
||
|
goto oom;
|
||
|
#endif
|
||
|
|
||
|
if ((d->cd_input_nums
|
||
|
= ctf_dynhash_create (ctf_hash_integer,
|
||
|
ctf_hash_eq_integer,
|
||
|
NULL, NULL)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
if ((d->cd_emission_struct_members
|
||
|
= ctf_dynhash_create (ctf_hash_integer,
|
||
|
ctf_hash_eq_integer,
|
||
|
NULL, NULL)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
if ((d->cd_conflicting_types
|
||
|
= ctf_dynset_create (htab_hash_string,
|
||
|
htab_eq_string, NULL)) == NULL)
|
||
|
goto oom;
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
oom:
|
||
|
ctf_err_warn (fp, 0, ENOMEM, _("ctf_dedup_init: cannot initialize: "
|
||
|
"out of memory"));
|
||
|
return ctf_set_errno (fp, ENOMEM);
|
||
|
}
|
||
|
|
||
|
/* No ctf_dedup calls are allowed after this call other than starting a new
|
||
|
deduplication via ctf_dedup (not even ctf_dedup_type_mapping lookups). */
|
||
|
void
|
||
|
ctf_dedup_fini (ctf_dict_t *fp, ctf_dict_t **outputs, uint32_t noutputs)
|
||
|
{
|
||
|
ctf_dedup_t *d = &fp->ctf_dedup;
|
||
|
size_t i;
|
||
|
|
||
|
/* ctf_dedup_atoms is kept across links. */
|
||
|
#if IDS_NEED_ALLOCATION
|
||
|
ctf_dynhash_destroy (d->cd_id_to_dict_t);
|
||
|
#endif
|
||
|
for (i = 0; i < 4; i++)
|
||
|
ctf_dynhash_destroy (d->cd_decorated_names[i]);
|
||
|
ctf_dynhash_destroy (d->cd_name_counts);
|
||
|
ctf_dynhash_destroy (d->cd_type_hashes);
|
||
|
ctf_dynhash_destroy (d->cd_struct_origin);
|
||
|
ctf_dynhash_destroy (d->cd_citers);
|
||
|
ctf_dynhash_destroy (d->cd_output_mapping);
|
||
|
ctf_dynhash_destroy (d->cd_output_first_gid);
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_dynhash_destroy (d->cd_output_mapping_guard);
|
||
|
#endif
|
||
|
ctf_dynhash_destroy (d->cd_input_nums);
|
||
|
ctf_dynhash_destroy (d->cd_emission_struct_members);
|
||
|
ctf_dynset_destroy (d->cd_conflicting_types);
|
||
|
|
||
|
/* Free the per-output state. */
|
||
|
if (outputs)
|
||
|
{
|
||
|
for (i = 0; i < noutputs; i++)
|
||
|
{
|
||
|
ctf_dedup_t *od = &outputs[i]->ctf_dedup;
|
||
|
ctf_dynhash_destroy (od->cd_output_emission_hashes);
|
||
|
ctf_dynhash_destroy (od->cd_output_emission_conflicted_forwards);
|
||
|
ctf_dict_close (od->cd_output);
|
||
|
}
|
||
|
}
|
||
|
memset (d, 0, sizeof (ctf_dedup_t));
|
||
|
}
|
||
|
|
||
|
/* Return 1 if this type is cited by multiple input dictionaries. */
|
||
|
|
||
|
static int
|
||
|
ctf_dedup_multiple_input_dicts (ctf_dict_t *output, ctf_dict_t **inputs,
|
||
|
const char *hval)
|
||
|
{
|
||
|
ctf_dedup_t *d = &output->ctf_dedup;
|
||
|
ctf_dynset_t *type_ids;
|
||
|
ctf_next_t *i = NULL;
|
||
|
void *id;
|
||
|
ctf_dict_t *found = NULL, *relative_found = NULL;
|
||
|
const char *type_id;
|
||
|
ctf_dict_t *input_fp;
|
||
|
ctf_id_t input_id;
|
||
|
const char *name;
|
||
|
const char *decorated;
|
||
|
int fwdkind;
|
||
|
int multiple = 0;
|
||
|
int err;
|
||
|
|
||
|
type_ids = ctf_dynhash_lookup (d->cd_output_mapping, hval);
|
||
|
if (!ctf_assert (output, type_ids))
|
||
|
return -1;
|
||
|
|
||
|
/* Scan across the IDs until we find proof that two disjoint dictionaries
|
||
|
are referenced. Exit as soon as possible. Optimization opportunity, but
|
||
|
possibly not worth it, given that this is only executed in
|
||
|
CTF_LINK_SHARE_DUPLICATED mode. */
|
||
|
|
||
|
while ((err = ctf_dynset_next (type_ids, &i, &id)) == 0)
|
||
|
{
|
||
|
ctf_dict_t *fp = inputs[CTF_DEDUP_GID_TO_INPUT (id)];
|
||
|
|
||
|
if (fp == found || fp == relative_found)
|
||
|
continue;
|
||
|
|
||
|
if (!found)
|
||
|
{
|
||
|
found = fp;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!relative_found
|
||
|
&& (fp->ctf_parent == found || found->ctf_parent == fp))
|
||
|
{
|
||
|
relative_found = fp;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
multiple = 1;
|
||
|
ctf_next_destroy (i);
|
||
|
break;
|
||
|
}
|
||
|
if ((err != ECTF_NEXT_END) && (err != 0))
|
||
|
{
|
||
|
ctf_err_warn (output, 0, err, _("iteration error "
|
||
|
"propagating conflictedness"));
|
||
|
return ctf_set_errno (output, err);
|
||
|
}
|
||
|
|
||
|
if (multiple)
|
||
|
return multiple;
|
||
|
|
||
|
/* This type itself does not appear in multiple input dicts: how about another
|
||
|
related type with the same name (e.g. a forward if this is a struct,
|
||
|
etc). */
|
||
|
|
||
|
type_id = ctf_dynset_lookup_any (type_ids);
|
||
|
if (!ctf_assert (output, type_id))
|
||
|
return -1;
|
||
|
|
||
|
input_fp = inputs[CTF_DEDUP_GID_TO_INPUT (type_id)];
|
||
|
input_id = CTF_DEDUP_GID_TO_TYPE (type_id);
|
||
|
fwdkind = ctf_type_kind_forwarded (input_fp, input_id);
|
||
|
name = ctf_type_name_raw (input_fp, input_id);
|
||
|
|
||
|
if ((fwdkind == CTF_K_STRUCT || fwdkind == CTF_K_UNION)
|
||
|
&& name[0] != '\0')
|
||
|
{
|
||
|
const void *origin;
|
||
|
|
||
|
if ((decorated = ctf_decorate_type_name (output, name,
|
||
|
fwdkind)) == NULL)
|
||
|
return -1; /* errno is set for us. */
|
||
|
|
||
|
origin = ctf_dynhash_lookup (d->cd_struct_origin, decorated);
|
||
|
if ((origin != NULL) && (CTF_DEDUP_GID_TO_INPUT (origin) < 0))
|
||
|
multiple = 1;
|
||
|
}
|
||
|
|
||
|
return multiple;
|
||
|
}
|
||
|
|
||
|
/* Demote unconflicting types which reference only one input, or which reference
|
||
|
two inputs where one input is the parent of the other, into conflicting
|
||
|
types. Only used if the link mode is CTF_LINK_SHARE_DUPLICATED. */
|
||
|
|
||
|
static int
|
||
|
ctf_dedup_conflictify_unshared (ctf_dict_t *output, ctf_dict_t **inputs)
|
||
|
{
|
||
|
ctf_dedup_t *d = &output->ctf_dedup;
|
||
|
ctf_next_t *i = NULL;
|
||
|
int err;
|
||
|
const void *k;
|
||
|
ctf_dynset_t *to_mark = NULL;
|
||
|
|
||
|
if ((to_mark = ctf_dynset_create (htab_hash_string, htab_eq_string,
|
||
|
NULL)) == NULL)
|
||
|
goto err_no;
|
||
|
|
||
|
while ((err = ctf_dynhash_cnext (d->cd_output_mapping, &i, &k, NULL)) == 0)
|
||
|
{
|
||
|
const char *hval = (const char *) k;
|
||
|
int conflicting;
|
||
|
|
||
|
/* Types referenced by only one dict, with no type appearing under that
|
||
|
name elsewhere, are marked conflicting. */
|
||
|
|
||
|
conflicting = !ctf_dedup_multiple_input_dicts (output, inputs, hval);
|
||
|
|
||
|
if (conflicting < 0)
|
||
|
goto err; /* errno is set for us. */
|
||
|
|
||
|
if (conflicting)
|
||
|
if (ctf_dynset_cinsert (to_mark, hval) < 0)
|
||
|
goto err;
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
goto iterr;
|
||
|
|
||
|
while ((err = ctf_dynset_cnext (to_mark, &i, &k)) == 0)
|
||
|
{
|
||
|
const char *hval = (const char *) k;
|
||
|
|
||
|
if (ctf_dedup_mark_conflicting_hash (output, hval) < 0)
|
||
|
goto err;
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
goto iterr;
|
||
|
|
||
|
ctf_dynset_destroy (to_mark);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
err_no:
|
||
|
ctf_set_errno (output, errno);
|
||
|
err:
|
||
|
err = ctf_errno (output);
|
||
|
ctf_next_destroy (i);
|
||
|
iterr:
|
||
|
ctf_dynset_destroy (to_mark);
|
||
|
ctf_err_warn (output, 0, err, _("conflictifying unshared types"));
|
||
|
return ctf_set_errno (output, err);
|
||
|
}
|
||
|
|
||
|
/* The core deduplicator. Populate cd_output_mapping in the output ctf_dedup
|
||
|
with a mapping of all types that belong in this dictionary and where they
|
||
|
come from, and cd_conflicting_types with an indication of whether each type
|
||
|
is conflicted or not. OUTPUT is the top-level output: INPUTS is the array of
|
||
|
input dicts; NINPUTS is the size of that array; PARENTS is an NINPUTS-element
|
||
|
array with each element corresponding to a input which is a child dict set to
|
||
|
the number in the INPUTS array of that input's parent.
|
||
|
|
||
|
If CU_MAPPED is set, this is a first pass for a link with a non-empty CU
|
||
|
mapping: 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,
|
||
|
uint32_t *parents, int cu_mapped)
|
||
|
{
|
||
|
ctf_dedup_t *d = &output->ctf_dedup;
|
||
|
size_t i;
|
||
|
ctf_next_t *it = NULL;
|
||
|
|
||
|
if (ctf_dedup_init (output) < 0)
|
||
|
return -1; /* errno is set for us. */
|
||
|
|
||
|
for (i = 0; i < ninputs; i++)
|
||
|
{
|
||
|
ctf_dprintf ("Input %i: %s\n", (int) i, ctf_link_input_name (inputs[i]));
|
||
|
if (ctf_dynhash_insert (d->cd_input_nums, inputs[i],
|
||
|
(void *) (uintptr_t) i) < 0)
|
||
|
{
|
||
|
ctf_set_errno (output, errno);
|
||
|
ctf_err_warn (output, 0, errno, _("ctf_dedup: cannot initialize: %s\n"),
|
||
|
ctf_errmsg (errno));
|
||
|
goto err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 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!). */
|
||
|
d->cd_link_flags = output->ctf_link_flags;
|
||
|
if (cu_mapped)
|
||
|
d->cd_link_flags &= ~(CTF_LINK_SHARE_DUPLICATED);
|
||
|
|
||
|
/* Compute hash values for all types, recursively, treating child structures
|
||
|
and unions equivalent to forwards, and hashing in the name of the referent
|
||
|
of each such type into structures, unions, and non-opaque forwards.
|
||
|
Populate a mapping from decorated name (including an indication of
|
||
|
struct/union/enum namespace) to count of type hash values in
|
||
|
cd_name_counts, a mapping from and a mapping from hash values to input type
|
||
|
IDs in cd_output_mapping. */
|
||
|
|
||
|
ctf_dprintf ("Computing type hashes\n");
|
||
|
for (i = 0; i < ninputs; i++)
|
||
|
{
|
||
|
ctf_id_t id;
|
||
|
|
||
|
while ((id = ctf_type_next (inputs[i], &it, NULL, 1)) != CTF_ERR)
|
||
|
{
|
||
|
if (ctf_dedup_hash_type (output, inputs[i], inputs,
|
||
|
parents, i, id, 0, 0,
|
||
|
ctf_dedup_populate_mappings) == NULL)
|
||
|
goto err; /* errno is set for us. */
|
||
|
}
|
||
|
if (ctf_errno (inputs[i]) != ECTF_NEXT_END)
|
||
|
{
|
||
|
ctf_set_errno (output, ctf_errno (inputs[i]));
|
||
|
ctf_err_warn (output, 0, 0, _("iteration failure "
|
||
|
"computing type hashes"));
|
||
|
goto err;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* 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
|
||
|
conflicting in the output. */
|
||
|
|
||
|
ctf_dprintf ("Detecting type name ambiguity\n");
|
||
|
if (ctf_dedup_detect_name_ambiguity (output, inputs) < 0)
|
||
|
goto err; /* errno is set for us. */
|
||
|
|
||
|
/* If the link mode is CTF_LINK_SHARE_DUPLICATED, we change any unconflicting
|
||
|
types whose output mapping references only one input dict into a
|
||
|
conflicting type, so that they end up in the per-CU dictionaries. */
|
||
|
|
||
|
if (d->cd_link_flags & CTF_LINK_SHARE_DUPLICATED)
|
||
|
{
|
||
|
ctf_dprintf ("Conflictifying unshared types\n");
|
||
|
if (ctf_dedup_conflictify_unshared (output, inputs) < 0)
|
||
|
goto err; /* errno is set for us. */
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
err:
|
||
|
ctf_dedup_fini (output, NULL, 0);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
ctf_dedup_rwalk_output_mapping (ctf_dict_t *output, ctf_dict_t **inputs,
|
||
|
uint32_t ninputs, uint32_t *parents,
|
||
|
ctf_dynset_t *already_visited,
|
||
|
const char *hval,
|
||
|
int (*visit_fun) (const char *hval,
|
||
|
ctf_dict_t *output,
|
||
|
ctf_dict_t **inputs,
|
||
|
uint32_t ninputs,
|
||
|
uint32_t *parents,
|
||
|
int already_visited,
|
||
|
ctf_dict_t *input,
|
||
|
ctf_id_t type,
|
||
|
void *id,
|
||
|
int depth,
|
||
|
void *arg),
|
||
|
void *arg, unsigned long depth);
|
||
|
|
||
|
/* Like ctf_dedup_rwalk_output_mapping (which see), only takes a single target
|
||
|
type and visits it. */
|
||
|
static int
|
||
|
ctf_dedup_rwalk_one_output_mapping (ctf_dict_t *output,
|
||
|
ctf_dict_t **inputs, uint32_t ninputs,
|
||
|
uint32_t *parents,
|
||
|
ctf_dynset_t *already_visited,
|
||
|
int visited, void *type_id,
|
||
|
const char *hval,
|
||
|
int (*visit_fun) (const char *hval,
|
||
|
ctf_dict_t *output,
|
||
|
ctf_dict_t **inputs,
|
||
|
uint32_t ninputs,
|
||
|
uint32_t *parents,
|
||
|
int already_visited,
|
||
|
ctf_dict_t *input,
|
||
|
ctf_id_t type,
|
||
|
void *id,
|
||
|
int depth,
|
||
|
void *arg),
|
||
|
void *arg, unsigned long depth)
|
||
|
{
|
||
|
ctf_dedup_t *d = &output->ctf_dedup;
|
||
|
ctf_dict_t *fp;
|
||
|
int input_num;
|
||
|
ctf_id_t type;
|
||
|
int ret;
|
||
|
const char *whaterr;
|
||
|
|
||
|
input_num = CTF_DEDUP_GID_TO_INPUT (type_id);
|
||
|
fp = inputs[input_num];
|
||
|
type = CTF_DEDUP_GID_TO_TYPE (type_id);
|
||
|
|
||
|
ctf_dprintf ("%lu: Starting walk over type %s, %i/%lx (%p), from %s, "
|
||
|
"kind %i\n", depth, hval, input_num, type, (void *) fp,
|
||
|
ctf_link_input_name (fp), ctf_type_kind_unsliced (fp, type));
|
||
|
|
||
|
/* Get the single call we do if this type has already been visited out of the
|
||
|
way. */
|
||
|
if (visited)
|
||
|
return visit_fun (hval, output, inputs, ninputs, parents, visited, fp,
|
||
|
type, type_id, depth, arg);
|
||
|
|
||
|
/* This macro is really ugly, but the alternative is repeating this code many
|
||
|
times, which is worse. */
|
||
|
|
||
|
#define CTF_TYPE_WALK(type, errlabel, errmsg) \
|
||
|
do \
|
||
|
{ \
|
||
|
void *type_id; \
|
||
|
const char *hashval; \
|
||
|
int cited_type_input_num = input_num; \
|
||
|
\
|
||
|
if ((fp->ctf_flags & LCTF_CHILD) && (LCTF_TYPE_ISPARENT (fp, type))) \
|
||
|
cited_type_input_num = parents[input_num]; \
|
||
|
\
|
||
|
type_id = CTF_DEDUP_GID (output, cited_type_input_num, type); \
|
||
|
\
|
||
|
if (type == 0) \
|
||
|
{ \
|
||
|
ctf_dprintf ("Walking: unimplemented type\n"); \
|
||
|
break; \
|
||
|
} \
|
||
|
\
|
||
|
ctf_dprintf ("Looking up ID %i/%lx in type hashes\n", \
|
||
|
cited_type_input_num, type); \
|
||
|
hashval = ctf_dynhash_lookup (d->cd_type_hashes, type_id); \
|
||
|
if (!ctf_assert (output, hashval)) \
|
||
|
{ \
|
||
|
whaterr = N_("error looking up ID in type hashes"); \
|
||
|
goto errlabel; \
|
||
|
} \
|
||
|
ctf_dprintf ("ID %i/%lx has hash %s\n", cited_type_input_num, type, \
|
||
|
hashval); \
|
||
|
\
|
||
|
ret = ctf_dedup_rwalk_output_mapping (output, inputs, ninputs, parents, \
|
||
|
already_visited, hashval, \
|
||
|
visit_fun, arg, depth); \
|
||
|
if (ret < 0) \
|
||
|
{ \
|
||
|
whaterr = errmsg; \
|
||
|
goto errlabel; \
|
||
|
} \
|
||
|
} \
|
||
|
while (0)
|
||
|
|
||
|
switch (ctf_type_kind_unsliced (fp, type))
|
||
|
{
|
||
|
case CTF_K_UNKNOWN:
|
||
|
case CTF_K_FORWARD:
|
||
|
case CTF_K_INTEGER:
|
||
|
case CTF_K_FLOAT:
|
||
|
case CTF_K_ENUM:
|
||
|
/* No types referenced. */
|
||
|
break;
|
||
|
|
||
|
case CTF_K_TYPEDEF:
|
||
|
case CTF_K_VOLATILE:
|
||
|
case CTF_K_CONST:
|
||
|
case CTF_K_RESTRICT:
|
||
|
case CTF_K_POINTER:
|
||
|
case CTF_K_SLICE:
|
||
|
CTF_TYPE_WALK (ctf_type_reference (fp, type), err,
|
||
|
N_("error during referenced type walk"));
|
||
|
break;
|
||
|
|
||
|
case CTF_K_ARRAY:
|
||
|
{
|
||
|
ctf_arinfo_t ar;
|
||
|
|
||
|
if (ctf_array_info (fp, type, &ar) < 0)
|
||
|
{
|
||
|
whaterr = N_("error during array info lookup");
|
||
|
goto err_msg;
|
||
|
}
|
||
|
|
||
|
CTF_TYPE_WALK (ar.ctr_contents, err,
|
||
|
N_("error during array contents type walk"));
|
||
|
CTF_TYPE_WALK (ar.ctr_index, err,
|
||
|
N_("error during array index type walk"));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CTF_K_FUNCTION:
|
||
|
{
|
||
|
ctf_funcinfo_t fi;
|
||
|
ctf_id_t *args;
|
||
|
uint32_t j;
|
||
|
|
||
|
if (ctf_func_type_info (fp, type, &fi) < 0)
|
||
|
{
|
||
|
whaterr = N_("error during func type info lookup");
|
||
|
goto err_msg;
|
||
|
}
|
||
|
|
||
|
CTF_TYPE_WALK (fi.ctc_return, err,
|
||
|
N_("error during func return type walk"));
|
||
|
|
||
|
if ((args = calloc (fi.ctc_argc, sizeof (ctf_id_t))) == NULL)
|
||
|
{
|
||
|
whaterr = N_("error doing memory allocation");
|
||
|
goto err_msg;
|
||
|
}
|
||
|
|
||
|
if (ctf_func_type_args (fp, type, fi.ctc_argc, args) < 0)
|
||
|
{
|
||
|
whaterr = N_("error doing func arg type lookup");
|
||
|
free (args);
|
||
|
goto err_msg;
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < fi.ctc_argc; j++)
|
||
|
CTF_TYPE_WALK (args[j], err_free_args,
|
||
|
N_("error during Func arg type walk"));
|
||
|
free (args);
|
||
|
break;
|
||
|
|
||
|
err_free_args:
|
||
|
free (args);
|
||
|
goto err;
|
||
|
}
|
||
|
case CTF_K_STRUCT:
|
||
|
case CTF_K_UNION:
|
||
|
/* We do not recursively traverse the members of structures: they are
|
||
|
emitted later, in a separate pass. */
|
||
|
break;
|
||
|
default:
|
||
|
whaterr = N_("CTF dict corruption: unknown type kind");
|
||
|
goto err_msg;
|
||
|
}
|
||
|
|
||
|
return visit_fun (hval, output, inputs, ninputs, parents, visited, fp, type,
|
||
|
type_id, depth, arg);
|
||
|
|
||
|
err_msg:
|
||
|
ctf_set_errno (output, ctf_errno (fp));
|
||
|
ctf_err_warn (output, 0, 0, _("%s in input file %s at type ID %lx"),
|
||
|
gettext (whaterr), ctf_link_input_name (fp), type);
|
||
|
err:
|
||
|
return -1;
|
||
|
}
|
||
|
/* Recursively traverse the output mapping, and do something with each type
|
||
|
visited, from leaves to root. VISIT_FUN, called as recursion unwinds,
|
||
|
returns a negative error code or zero. Type hashes may be visited more than
|
||
|
once, but are not recursed through repeatedly: ALREADY_VISITED tracks whether
|
||
|
types have already been visited. */
|
||
|
static int
|
||
|
ctf_dedup_rwalk_output_mapping (ctf_dict_t *output, ctf_dict_t **inputs,
|
||
|
uint32_t ninputs, uint32_t *parents,
|
||
|
ctf_dynset_t *already_visited,
|
||
|
const char *hval,
|
||
|
int (*visit_fun) (const char *hval,
|
||
|
ctf_dict_t *output,
|
||
|
ctf_dict_t **inputs,
|
||
|
uint32_t ninputs,
|
||
|
uint32_t *parents,
|
||
|
int already_visited,
|
||
|
ctf_dict_t *input,
|
||
|
ctf_id_t type,
|
||
|
void *id,
|
||
|
int depth,
|
||
|
void *arg),
|
||
|
void *arg, unsigned long depth)
|
||
|
{
|
||
|
ctf_dedup_t *d = &output->ctf_dedup;
|
||
|
ctf_next_t *i = NULL;
|
||
|
int err;
|
||
|
int visited = 1;
|
||
|
ctf_dynset_t *type_ids;
|
||
|
void *id;
|
||
|
|
||
|
depth++;
|
||
|
|
||
|
type_ids = ctf_dynhash_lookup (d->cd_output_mapping, hval);
|
||
|
if (!type_ids)
|
||
|
{
|
||
|
ctf_err_warn (output, 0, ECTF_INTERNAL,
|
||
|
_("looked up type kind by nonexistent hash %s"), hval);
|
||
|
return ctf_set_errno (output, ECTF_INTERNAL);
|
||
|
}
|
||
|
|
||
|
/* Have we seen this type before? */
|
||
|
|
||
|
if (!ctf_dynset_exists (already_visited, hval, NULL))
|
||
|
{
|
||
|
/* Mark as already-visited immediately, to eliminate the possibility of
|
||
|
cycles: but remember we have not actually visited it yet for the
|
||
|
upcoming call to the visit_fun. (All our callers handle cycles
|
||
|
properly themselves, so we can just abort them aggressively as soon as
|
||
|
we find ourselves in one.) */
|
||
|
|
||
|
visited = 0;
|
||
|
if (ctf_dynset_cinsert (already_visited, hval) < 0)
|
||
|
{
|
||
|
ctf_err_warn (output, 0, ENOMEM,
|
||
|
_("out of memory tracking already-visited types"));
|
||
|
return ctf_set_errno (output, ENOMEM);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* If this type is marked conflicted, traverse members and call
|
||
|
ctf_dedup_rwalk_output_mapping_once on all the unique ones: otherwise, just
|
||
|
pick a random one and use it. */
|
||
|
|
||
|
if (!ctf_dynset_exists (d->cd_conflicting_types, hval, NULL))
|
||
|
{
|
||
|
id = ctf_dynset_lookup_any (type_ids);
|
||
|
if (!ctf_assert (output, id))
|
||
|
return -1;
|
||
|
|
||
|
return ctf_dedup_rwalk_one_output_mapping (output, inputs, ninputs,
|
||
|
parents, already_visited,
|
||
|
visited, id, hval, visit_fun,
|
||
|
arg, depth);
|
||
|
}
|
||
|
|
||
|
while ((err = ctf_dynset_next (type_ids, &i, &id)) == 0)
|
||
|
{
|
||
|
int ret;
|
||
|
|
||
|
ret = ctf_dedup_rwalk_one_output_mapping (output, inputs, ninputs,
|
||
|
parents, already_visited,
|
||
|
visited, id, hval,
|
||
|
visit_fun, arg, depth);
|
||
|
if (ret < 0)
|
||
|
{
|
||
|
ctf_next_destroy (i);
|
||
|
return ret; /* errno is set for us. */
|
||
|
}
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
{
|
||
|
ctf_err_warn (output, 0, err, _("cannot walk conflicted type"));
|
||
|
return ctf_set_errno (output, err);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
typedef struct ctf_sort_om_cb_arg
|
||
|
{
|
||
|
ctf_dict_t **inputs;
|
||
|
uint32_t ninputs;
|
||
|
ctf_dedup_t *d;
|
||
|
} ctf_sort_om_cb_arg_t;
|
||
|
|
||
|
/* Sort the output mapping into order: types first appearing in earlier inputs
|
||
|
first, parents preceding children: if types first appear in the same input,
|
||
|
sort those with earlier ctf_id_t's first. */
|
||
|
static int
|
||
|
sort_output_mapping (const ctf_next_hkv_t *one, const ctf_next_hkv_t *two,
|
||
|
void *arg_)
|
||
|
{
|
||
|
ctf_sort_om_cb_arg_t *arg = (ctf_sort_om_cb_arg_t *) arg_;
|
||
|
ctf_dedup_t *d = arg->d;
|
||
|
const char *one_hval = (const char *) one->hkv_key;
|
||
|
const char *two_hval = (const char *) two->hkv_key;
|
||
|
void *one_gid, *two_gid;
|
||
|
uint32_t one_ninput;
|
||
|
uint32_t two_ninput;
|
||
|
ctf_dict_t *one_fp;
|
||
|
ctf_dict_t *two_fp;
|
||
|
ctf_id_t one_type;
|
||
|
ctf_id_t two_type;
|
||
|
|
||
|
one_gid = ctf_dynhash_lookup (d->cd_output_first_gid, one_hval);
|
||
|
two_gid = ctf_dynhash_lookup (d->cd_output_first_gid, two_hval);
|
||
|
|
||
|
one_ninput = CTF_DEDUP_GID_TO_INPUT (one_gid);
|
||
|
two_ninput = CTF_DEDUP_GID_TO_INPUT (two_gid);
|
||
|
|
||
|
one_type = CTF_DEDUP_GID_TO_TYPE (one_gid);
|
||
|
two_type = CTF_DEDUP_GID_TO_TYPE (two_gid);
|
||
|
|
||
|
/* It's kind of hard to smuggle an assertion failure out of here. */
|
||
|
assert (one_ninput < arg->ninputs && two_ninput < arg->ninputs);
|
||
|
|
||
|
one_fp = arg->inputs[one_ninput];
|
||
|
two_fp = arg->inputs[two_ninput];
|
||
|
|
||
|
/* Parents before children. */
|
||
|
|
||
|
if (!(one_fp->ctf_flags & LCTF_CHILD)
|
||
|
&& (two_fp->ctf_flags & LCTF_CHILD))
|
||
|
return -1;
|
||
|
else if ((one_fp->ctf_flags & LCTF_CHILD)
|
||
|
&& !(two_fp->ctf_flags & LCTF_CHILD))
|
||
|
return 1;
|
||
|
|
||
|
/* ninput order, types appearing in earlier TUs first. */
|
||
|
|
||
|
if (one_ninput < two_ninput)
|
||
|
return -1;
|
||
|
else if (two_ninput < one_ninput)
|
||
|
return 1;
|
||
|
|
||
|
/* Same TU. Earliest ctf_id_t first. They cannot be the same. */
|
||
|
|
||
|
assert (one_type != two_type);
|
||
|
if (one_type < two_type)
|
||
|
return -1;
|
||
|
else
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* The public entry point to ctf_dedup_rwalk_output_mapping, above. */
|
||
|
static int
|
||
|
ctf_dedup_walk_output_mapping (ctf_dict_t *output, ctf_dict_t **inputs,
|
||
|
uint32_t ninputs, uint32_t *parents,
|
||
|
int (*visit_fun) (const char *hval,
|
||
|
ctf_dict_t *output,
|
||
|
ctf_dict_t **inputs,
|
||
|
uint32_t ninputs,
|
||
|
uint32_t *parents,
|
||
|
int already_visited,
|
||
|
ctf_dict_t *input,
|
||
|
ctf_id_t type,
|
||
|
void *id,
|
||
|
int depth,
|
||
|
void *arg),
|
||
|
void *arg)
|
||
|
{
|
||
|
ctf_dynset_t *already_visited;
|
||
|
ctf_next_t *i = NULL;
|
||
|
ctf_sort_om_cb_arg_t sort_arg;
|
||
|
int err;
|
||
|
void *k;
|
||
|
|
||
|
if ((already_visited = ctf_dynset_create (htab_hash_string,
|
||
|
htab_eq_string,
|
||
|
NULL)) == NULL)
|
||
|
return ctf_set_errno (output, ENOMEM);
|
||
|
|
||
|
sort_arg.inputs = inputs;
|
||
|
sort_arg.ninputs = ninputs;
|
||
|
sort_arg.d = &output->ctf_dedup;
|
||
|
|
||
|
while ((err = ctf_dynhash_next_sorted (output->ctf_dedup.cd_output_mapping,
|
||
|
&i, &k, NULL, sort_output_mapping,
|
||
|
&sort_arg)) == 0)
|
||
|
{
|
||
|
const char *hval = (const char *) k;
|
||
|
|
||
|
err = ctf_dedup_rwalk_output_mapping (output, inputs, ninputs, parents,
|
||
|
already_visited, hval, visit_fun,
|
||
|
arg, 0);
|
||
|
if (err < 0)
|
||
|
{
|
||
|
ctf_next_destroy (i);
|
||
|
goto err; /* errno is set for us. */
|
||
|
}
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
{
|
||
|
ctf_err_warn (output, 0, err, _("cannot recurse over output mapping"));
|
||
|
ctf_set_errno (output, err);
|
||
|
goto err;
|
||
|
}
|
||
|
ctf_dynset_destroy (already_visited);
|
||
|
|
||
|
return 0;
|
||
|
err:
|
||
|
ctf_dynset_destroy (already_visited);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* Possibly synthesise a synthetic forward in TARGET to subsitute for a
|
||
|
conflicted per-TU type ID in INPUT with hash HVAL. Return its CTF ID, or 0
|
||
|
if none was needed. */
|
||
|
static ctf_id_t
|
||
|
ctf_dedup_maybe_synthesize_forward (ctf_dict_t *output, ctf_dict_t *target,
|
||
|
ctf_dict_t *input, ctf_id_t id,
|
||
|
const char *hval)
|
||
|
{
|
||
|
ctf_dedup_t *od = &output->ctf_dedup;
|
||
|
ctf_dedup_t *td = &target->ctf_dedup;
|
||
|
int kind;
|
||
|
int fwdkind;
|
||
|
const char *name = ctf_type_name_raw (input, id);
|
||
|
const char *decorated;
|
||
|
void *v;
|
||
|
ctf_id_t emitted_forward;
|
||
|
|
||
|
if (!ctf_dynset_exists (od->cd_conflicting_types, hval, NULL)
|
||
|
|| target->ctf_flags & LCTF_CHILD
|
||
|
|| name[0] == '\0'
|
||
|
|| (((kind = ctf_type_kind_unsliced (input, id)) != CTF_K_STRUCT
|
||
|
&& kind != CTF_K_UNION && kind != CTF_K_FORWARD)))
|
||
|
return 0;
|
||
|
|
||
|
fwdkind = ctf_type_kind_forwarded (input, id);
|
||
|
|
||
|
ctf_dprintf ("Using synthetic forward for conflicted struct/union with "
|
||
|
"hval %s\n", hval);
|
||
|
|
||
|
if (!ctf_assert (output, name))
|
||
|
return CTF_ERR;
|
||
|
|
||
|
if ((decorated = ctf_decorate_type_name (output, name, fwdkind)) == NULL)
|
||
|
return CTF_ERR;
|
||
|
|
||
|
if (!ctf_dynhash_lookup_kv (td->cd_output_emission_conflicted_forwards,
|
||
|
decorated, NULL, &v))
|
||
|
{
|
||
|
if ((emitted_forward = ctf_add_forward (target, CTF_ADD_ROOT, name,
|
||
|
fwdkind)) == CTF_ERR)
|
||
|
{
|
||
|
ctf_set_errno (output, ctf_errno (target));
|
||
|
return CTF_ERR;
|
||
|
}
|
||
|
|
||
|
if (ctf_dynhash_cinsert (td->cd_output_emission_conflicted_forwards,
|
||
|
decorated, (void *) (uintptr_t)
|
||
|
emitted_forward) < 0)
|
||
|
{
|
||
|
ctf_set_errno (output, ENOMEM);
|
||
|
return CTF_ERR;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
emitted_forward = (ctf_id_t) (uintptr_t) v;
|
||
|
|
||
|
ctf_dprintf ("Cross-TU conflicted struct: passing back forward, %lx\n",
|
||
|
emitted_forward);
|
||
|
|
||
|
return emitted_forward;
|
||
|
}
|
||
|
|
||
|
/* Map a GID in some INPUT dict, in the form of an input number and a ctf_id_t,
|
||
|
into a GID in a target output dict. If it returns 0, this is the
|
||
|
unimplemented type, and the input type must have been 0. The OUTPUT dict is
|
||
|
assumed to be the parent of the TARGET, if it is not the TARGET itself.
|
||
|
|
||
|
Returns CTF_ERR on failure. Responds to an incoming CTF_ERR as an 'id' by
|
||
|
returning CTF_ERR, to simplify callers. Errors are always propagated to the
|
||
|
input, even if they relate to the target, for the same reason. (Target
|
||
|
errors are expected to be very rare.)
|
||
|
|
||
|
If the type in question is a citation of a conflicted type in a different TU,
|
||
|
emit a forward of the right type in its place (if not already emitted), and
|
||
|
record that forward in cd_output_emission_conflicted_forwards. This avoids
|
||
|
the need to replicate the entire type graph below this point in the current
|
||
|
TU (an appalling waste of space).
|
||
|
|
||
|
TODO: maybe replace forwards in the same TU with their referents? Might
|
||
|
make usability a bit better. */
|
||
|
|
||
|
static ctf_id_t
|
||
|
ctf_dedup_id_to_target (ctf_dict_t *output, ctf_dict_t *target,
|
||
|
ctf_dict_t **inputs, uint32_t ninputs,
|
||
|
uint32_t *parents, ctf_dict_t *input, int input_num,
|
||
|
ctf_id_t id)
|
||
|
{
|
||
|
ctf_dedup_t *od = &output->ctf_dedup;
|
||
|
ctf_dedup_t *td = &target->ctf_dedup;
|
||
|
ctf_dict_t *err_fp = input;
|
||
|
const char *hval;
|
||
|
void *target_id;
|
||
|
ctf_id_t emitted_forward;
|
||
|
|
||
|
/* The target type of an error is an error. */
|
||
|
if (id == CTF_ERR)
|
||
|
return CTF_ERR;
|
||
|
|
||
|
/* The unimplemented type's ID never changes. */
|
||
|
if (!id)
|
||
|
{
|
||
|
ctf_dprintf ("%i/%lx: unimplemented type\n", input_num, id);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
ctf_dprintf ("Mapping %i/%lx to target %p (%s)\n", input_num,
|
||
|
id, (void *) target, ctf_link_input_name (target));
|
||
|
|
||
|
/* If the input type is in the parent type space, and this is a child, reset
|
||
|
the input to the parent (which must already have been emitted, since
|
||
|
emission of parent dicts happens before children). */
|
||
|
if ((input->ctf_flags & LCTF_CHILD) && (LCTF_TYPE_ISPARENT (input, id)))
|
||
|
{
|
||
|
if (!ctf_assert (output, parents[input_num] <= ninputs))
|
||
|
return -1;
|
||
|
input = inputs[parents[input_num]];
|
||
|
input_num = parents[input_num];
|
||
|
}
|
||
|
|
||
|
hval = ctf_dynhash_lookup (od->cd_type_hashes,
|
||
|
CTF_DEDUP_GID (output, input_num, id));
|
||
|
|
||
|
if (!ctf_assert (output, hval && td->cd_output_emission_hashes))
|
||
|
return -1;
|
||
|
|
||
|
/* If this type is a conflicted tagged structure, union, or forward,
|
||
|
substitute a synthetic forward instead, emitting it if need be. Only do
|
||
|
this if the target is in the parent dict: if it's in the child dict, we can
|
||
|
just point straight at the thing itself. Of course, we might be looking in
|
||
|
the child dict right now and not find it and have to look in the parent, so
|
||
|
we have to do this check twice. */
|
||
|
|
||
|
emitted_forward = ctf_dedup_maybe_synthesize_forward (output, target,
|
||
|
input, id, hval);
|
||
|
switch (emitted_forward)
|
||
|
{
|
||
|
case 0: /* No forward needed. */
|
||
|
break;
|
||
|
case -1:
|
||
|
ctf_set_errno (err_fp, ctf_errno (output));
|
||
|
ctf_err_warn (err_fp, 0, 0, _("cannot add synthetic forward for type "
|
||
|
"%i/%lx"), input_num, id);
|
||
|
return -1;
|
||
|
default:
|
||
|
return emitted_forward;
|
||
|
}
|
||
|
|
||
|
ctf_dprintf ("Looking up %i/%lx, hash %s, in target\n", input_num, id, hval);
|
||
|
|
||
|
target_id = ctf_dynhash_lookup (td->cd_output_emission_hashes, hval);
|
||
|
if (!target_id)
|
||
|
{
|
||
|
/* Must be in the parent, so this must be a child, and they must not be
|
||
|
the same dict. */
|
||
|
ctf_dprintf ("Checking shared parent for target\n");
|
||
|
if (!ctf_assert (output, (target != output)
|
||
|
&& (target->ctf_flags & LCTF_CHILD)))
|
||
|
return -1;
|
||
|
|
||
|
target_id = ctf_dynhash_lookup (od->cd_output_emission_hashes, hval);
|
||
|
|
||
|
emitted_forward = ctf_dedup_maybe_synthesize_forward (output, output,
|
||
|
input, id, hval);
|
||
|
switch (emitted_forward)
|
||
|
{
|
||
|
case 0: /* No forward needed. */
|
||
|
break;
|
||
|
case -1:
|
||
|
ctf_err_warn (err_fp, 0, ctf_errno (output),
|
||
|
_("cannot add synthetic forward for type %i/%lx"),
|
||
|
input_num, id);
|
||
|
return ctf_set_errno (err_fp, ctf_errno (output));
|
||
|
default:
|
||
|
return emitted_forward;
|
||
|
}
|
||
|
}
|
||
|
if (!ctf_assert (output, target_id))
|
||
|
return -1;
|
||
|
return (ctf_id_t) (uintptr_t) target_id;
|
||
|
}
|
||
|
|
||
|
/* Emit a single deduplicated TYPE with the given HVAL, located in a given
|
||
|
INPUT, with the given (G)ID, into the shared OUTPUT or a
|
||
|
possibly-newly-created per-CU dict. All the types this type depends upon
|
||
|
have already been emitted. (This type itself may also have been emitted.)
|
||
|
|
||
|
If the ARG is 1, this is a CU-mapped deduplication round mapping many
|
||
|
ctf_dict_t's into precisely one: conflicting types should be marked
|
||
|
non-root-visible. If the ARG is 0, conflicting types go into per-CU
|
||
|
dictionaries stored in the input's ctf_dedup.cd_output: otherwise, everything
|
||
|
is emitted directly into the output. No struct/union members are emitted.
|
||
|
|
||
|
Optimization opportunity: trace the ancestry of non-root-visible types and
|
||
|
elide all that neither have a root-visible type somewhere towards their root,
|
||
|
nor have the type visible via any other route (the function info section,
|
||
|
data object section, backtrace section etc). */
|
||
|
|
||
|
static int
|
||
|
ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
|
||
|
uint32_t ninputs, uint32_t *parents, int already_visited,
|
||
|
ctf_dict_t *input, ctf_id_t type, void *id, int depth,
|
||
|
void *arg)
|
||
|
{
|
||
|
ctf_dedup_t *d = &output->ctf_dedup;
|
||
|
int kind = ctf_type_kind_unsliced (input, type);
|
||
|
const char *name;
|
||
|
ctf_dict_t *target = output;
|
||
|
ctf_dict_t *real_input;
|
||
|
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 = 1;
|
||
|
int is_conflicting;
|
||
|
|
||
|
ctf_next_t *i = NULL;
|
||
|
ctf_id_t new_type;
|
||
|
ctf_id_t ref;
|
||
|
ctf_id_t maybe_dup = 0;
|
||
|
ctf_encoding_t ep;
|
||
|
const char *errtype;
|
||
|
int emission_hashed = 0;
|
||
|
|
||
|
/* We don't want to re-emit something we've already emitted. */
|
||
|
|
||
|
if (already_visited)
|
||
|
return 0;
|
||
|
|
||
|
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. 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_dprintf ("%i: Type %s in %i/%lx is conflicted: "
|
||
|
"inserting into per-CU target.\n",
|
||
|
depth, hval, input_num, type);
|
||
|
|
||
|
if (input->ctf_dedup.cd_output)
|
||
|
target = input->ctf_dedup.cd_output;
|
||
|
else
|
||
|
{
|
||
|
int err;
|
||
|
|
||
|
if ((target = ctf_create (&err)) == NULL)
|
||
|
{
|
||
|
ctf_err_warn (output, 0, err,
|
||
|
_("cannot create per-CU CTF archive for CU %s"),
|
||
|
ctf_link_input_name (input));
|
||
|
return ctf_set_errno (output, err);
|
||
|
}
|
||
|
|
||
|
ctf_import_unref (target, output);
|
||
|
if (ctf_cuname (input) != NULL)
|
||
|
ctf_cuname_set (target, ctf_cuname (input));
|
||
|
else
|
||
|
ctf_cuname_set (target, "unnamed-CU");
|
||
|
ctf_parent_name_set (target, _CTF_SECTION);
|
||
|
|
||
|
input->ctf_dedup.cd_output = target;
|
||
|
}
|
||
|
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);
|
||
|
|
||
|
/* Hide conflicting types, if we were asked to: also hide if a type with this
|
||
|
name already exists and is not a forward. */
|
||
|
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,
|
||
|
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:
|
||
|
/* These are types that CTF cannot encode, marked as such by the
|
||
|
compiler. */
|
||
|
errtype = _("unknown type");
|
||
|
if ((new_type = ctf_add_unknown (target, isroot, name)) == CTF_ERR)
|
||
|
goto err_target;
|
||
|
break;
|
||
|
case CTF_K_FORWARD:
|
||
|
/* This will do nothing if the type to which this forwards already exists,
|
||
|
and will be replaced with such a type if it appears later. */
|
||
|
|
||
|
errtype = _("forward");
|
||
|
if ((new_type = ctf_add_forward (target, isroot, name,
|
||
|
ctf_type_kind_forwarded (input, type)))
|
||
|
== CTF_ERR)
|
||
|
goto err_target;
|
||
|
break;
|
||
|
|
||
|
case CTF_K_FLOAT:
|
||
|
case CTF_K_INTEGER:
|
||
|
errtype = _("float/int");
|
||
|
if (ctf_type_encoding (input, type, &ep) < 0)
|
||
|
goto err_input; /* errno is set for us. */
|
||
|
if ((new_type = ctf_add_encoded (target, isroot, name, &ep, kind))
|
||
|
== CTF_ERR)
|
||
|
goto err_target;
|
||
|
break;
|
||
|
|
||
|
case CTF_K_ENUM:
|
||
|
{
|
||
|
int val;
|
||
|
errtype = _("enum");
|
||
|
if ((new_type = ctf_add_enum (target, isroot, name)) == CTF_ERR)
|
||
|
goto err_input; /* errno is set for us. */
|
||
|
|
||
|
while ((name = ctf_enum_next (input, type, &i, &val)) != NULL)
|
||
|
{
|
||
|
if (ctf_add_enumerator (target, new_type, name, val) < 0)
|
||
|
{
|
||
|
ctf_err_warn (target, 0, ctf_errno (target),
|
||
|
_("%s (%i): cannot add enumeration value %s "
|
||
|
"from input type %lx"),
|
||
|
ctf_link_input_name (input), input_num, name,
|
||
|
type);
|
||
|
ctf_next_destroy (i);
|
||
|
return ctf_set_errno (output, ctf_errno (target));
|
||
|
}
|
||
|
}
|
||
|
if (ctf_errno (input) != ECTF_NEXT_END)
|
||
|
goto err_input;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CTF_K_TYPEDEF:
|
||
|
errtype = _("typedef");
|
||
|
|
||
|
ref = ctf_type_reference (input, type);
|
||
|
if ((ref = ctf_dedup_id_to_target (output, target, inputs, ninputs,
|
||
|
parents, input, input_num,
|
||
|
ref)) == CTF_ERR)
|
||
|
goto err_input; /* errno is set for us. */
|
||
|
|
||
|
if ((new_type = ctf_add_typedef (target, isroot, name, ref)) == CTF_ERR)
|
||
|
goto err_target; /* errno is set for us. */
|
||
|
break;
|
||
|
|
||
|
case CTF_K_VOLATILE:
|
||
|
case CTF_K_CONST:
|
||
|
case CTF_K_RESTRICT:
|
||
|
case CTF_K_POINTER:
|
||
|
errtype = _("pointer or cvr-qual");
|
||
|
|
||
|
ref = ctf_type_reference (input, type);
|
||
|
if ((ref = ctf_dedup_id_to_target (output, target, inputs, ninputs,
|
||
|
parents, input, input_num,
|
||
|
ref)) == CTF_ERR)
|
||
|
goto err_input; /* errno is set for us. */
|
||
|
|
||
|
if ((new_type = ctf_add_reftype (target, isroot, ref, kind)) == CTF_ERR)
|
||
|
goto err_target; /* errno is set for us. */
|
||
|
break;
|
||
|
|
||
|
case CTF_K_SLICE:
|
||
|
errtype = _("slice");
|
||
|
|
||
|
if (ctf_type_encoding (input, type, &ep) < 0)
|
||
|
goto err_input; /* errno is set for us. */
|
||
|
|
||
|
ref = ctf_type_reference (input, type);
|
||
|
if ((ref = ctf_dedup_id_to_target (output, target, inputs, ninputs,
|
||
|
parents, input, input_num,
|
||
|
ref)) == CTF_ERR)
|
||
|
goto err_input;
|
||
|
|
||
|
if ((new_type = ctf_add_slice (target, isroot, ref, &ep)) == CTF_ERR)
|
||
|
goto err_target;
|
||
|
break;
|
||
|
|
||
|
case CTF_K_ARRAY:
|
||
|
{
|
||
|
ctf_arinfo_t ar;
|
||
|
|
||
|
errtype = _("array info");
|
||
|
if (ctf_array_info (input, type, &ar) < 0)
|
||
|
goto err_input;
|
||
|
|
||
|
ar.ctr_contents = ctf_dedup_id_to_target (output, target, inputs,
|
||
|
ninputs, parents, input,
|
||
|
input_num, ar.ctr_contents);
|
||
|
ar.ctr_index = ctf_dedup_id_to_target (output, target, inputs, ninputs,
|
||
|
parents, input, input_num,
|
||
|
ar.ctr_index);
|
||
|
|
||
|
if (ar.ctr_contents == CTF_ERR || ar.ctr_index == CTF_ERR)
|
||
|
goto err_input;
|
||
|
|
||
|
if ((new_type = ctf_add_array (target, isroot, &ar)) == CTF_ERR)
|
||
|
goto err_target;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CTF_K_FUNCTION:
|
||
|
{
|
||
|
ctf_funcinfo_t fi;
|
||
|
ctf_id_t *args;
|
||
|
uint32_t j;
|
||
|
|
||
|
errtype = _("function");
|
||
|
if (ctf_func_type_info (input, type, &fi) < 0)
|
||
|
goto err_input;
|
||
|
|
||
|
fi.ctc_return = ctf_dedup_id_to_target (output, target, inputs, ninputs,
|
||
|
parents, input, input_num,
|
||
|
fi.ctc_return);
|
||
|
if (fi.ctc_return == CTF_ERR)
|
||
|
goto err_input;
|
||
|
|
||
|
if ((args = calloc (fi.ctc_argc, sizeof (ctf_id_t))) == NULL)
|
||
|
{
|
||
|
ctf_set_errno (input, ENOMEM);
|
||
|
goto err_input;
|
||
|
}
|
||
|
|
||
|
errtype = _("function args");
|
||
|
if (ctf_func_type_args (input, type, fi.ctc_argc, args) < 0)
|
||
|
{
|
||
|
free (args);
|
||
|
goto err_input;
|
||
|
}
|
||
|
|
||
|
for (j = 0; j < fi.ctc_argc; j++)
|
||
|
{
|
||
|
args[j] = ctf_dedup_id_to_target (output, target, inputs, ninputs,
|
||
|
parents, input, input_num,
|
||
|
args[j]);
|
||
|
if (args[j] == CTF_ERR)
|
||
|
goto err_input;
|
||
|
}
|
||
|
|
||
|
if ((new_type = ctf_add_function (target, isroot,
|
||
|
&fi, args)) == CTF_ERR)
|
||
|
{
|
||
|
free (args);
|
||
|
goto err_target;
|
||
|
}
|
||
|
free (args);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case CTF_K_STRUCT:
|
||
|
case CTF_K_UNION:
|
||
|
{
|
||
|
size_t size = ctf_type_size (input, type);
|
||
|
void *out_id;
|
||
|
/* Insert the structure itself, so other types can refer to it. */
|
||
|
|
||
|
errtype = _("structure/union");
|
||
|
if (kind == CTF_K_STRUCT)
|
||
|
new_type = ctf_add_struct_sized (target, isroot, name, size);
|
||
|
else
|
||
|
new_type = ctf_add_union_sized (target, isroot, name, size);
|
||
|
|
||
|
if (new_type == CTF_ERR)
|
||
|
goto err_target;
|
||
|
|
||
|
out_id = CTF_DEDUP_GID (output, output_num, new_type);
|
||
|
ctf_dprintf ("%i: Noting need to emit members of %p -> %p\n", depth,
|
||
|
id, out_id);
|
||
|
/* Record the need to emit the members of this structure later. */
|
||
|
if (ctf_dynhash_insert (d->cd_emission_struct_members, id, out_id) < 0)
|
||
|
{
|
||
|
ctf_set_errno (target, errno);
|
||
|
goto err_target;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
ctf_err_warn (output, 0, ECTF_CORRUPT, _("%s: unknown type kind for "
|
||
|
"input type %lx"),
|
||
|
ctf_link_input_name (input), type);
|
||
|
return ctf_set_errno (output, ECTF_CORRUPT);
|
||
|
}
|
||
|
|
||
|
if (!emission_hashed
|
||
|
&& new_type != 0
|
||
|
&& ctf_dynhash_cinsert (target->ctf_dedup.cd_output_emission_hashes,
|
||
|
hval, (void *) (uintptr_t) new_type) < 0)
|
||
|
{
|
||
|
ctf_err_warn (output, 0, ENOMEM, _("out of memory tracking deduplicated "
|
||
|
"global type IDs"));
|
||
|
return ctf_set_errno (output, ENOMEM);
|
||
|
}
|
||
|
|
||
|
if (!emission_hashed && new_type != 0)
|
||
|
ctf_dprintf ("%i: Inserted %s, %i/%lx -> %lx into emission hash for "
|
||
|
"target %p (%s)\n", depth, hval, input_num, type, new_type,
|
||
|
(void *) target, ctf_link_input_name (target));
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
oom_hash:
|
||
|
ctf_err_warn (output, 0, ENOMEM, _("out of memory creating emission-tracking "
|
||
|
"hashes"));
|
||
|
return ctf_set_errno (output, ENOMEM);
|
||
|
|
||
|
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);
|
||
|
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"),
|
||
|
ctf_link_input_name (input), input_num,
|
||
|
errtype, type);
|
||
|
return ctf_set_errno (output, ctf_errno (target));
|
||
|
}
|
||
|
|
||
|
/* Traverse the cd_emission_struct_members and emit the members of all
|
||
|
structures and unions. All other types are emitted and complete by this
|
||
|
point. */
|
||
|
|
||
|
static int
|
||
|
ctf_dedup_emit_struct_members (ctf_dict_t *output, ctf_dict_t **inputs,
|
||
|
uint32_t ninputs, uint32_t *parents)
|
||
|
{
|
||
|
ctf_dedup_t *d = &output->ctf_dedup;
|
||
|
ctf_next_t *i = NULL;
|
||
|
void *input_id, *target_id;
|
||
|
int err;
|
||
|
ctf_dict_t *err_fp, *input_fp;
|
||
|
int input_num;
|
||
|
ctf_id_t err_type;
|
||
|
|
||
|
while ((err = ctf_dynhash_next (d->cd_emission_struct_members, &i,
|
||
|
&input_id, &target_id)) == 0)
|
||
|
{
|
||
|
ctf_next_t *j = NULL;
|
||
|
ctf_dict_t *target;
|
||
|
uint32_t target_num;
|
||
|
ctf_id_t input_type, target_type;
|
||
|
ssize_t offset;
|
||
|
ctf_id_t membtype;
|
||
|
const char *name;
|
||
|
|
||
|
input_num = CTF_DEDUP_GID_TO_INPUT (input_id);
|
||
|
input_fp = inputs[input_num];
|
||
|
input_type = CTF_DEDUP_GID_TO_TYPE (input_id);
|
||
|
|
||
|
/* The output is either -1 (for the shared, parent output dict) or the
|
||
|
number of the corresponding input. */
|
||
|
target_num = CTF_DEDUP_GID_TO_INPUT (target_id);
|
||
|
if (target_num == (uint32_t) -1)
|
||
|
target = output;
|
||
|
else
|
||
|
{
|
||
|
target = inputs[target_num]->ctf_dedup.cd_output;
|
||
|
if (!ctf_assert (output, target))
|
||
|
{
|
||
|
err_fp = output;
|
||
|
err_type = input_type;
|
||
|
goto err_target;
|
||
|
}
|
||
|
}
|
||
|
target_type = CTF_DEDUP_GID_TO_TYPE (target_id);
|
||
|
|
||
|
while ((offset = ctf_member_next (input_fp, input_type, &j, &name,
|
||
|
&membtype, 0)) >= 0)
|
||
|
{
|
||
|
err_fp = target;
|
||
|
err_type = target_type;
|
||
|
if ((membtype = ctf_dedup_id_to_target (output, target, inputs,
|
||
|
ninputs, parents, input_fp,
|
||
|
input_num,
|
||
|
membtype)) == CTF_ERR)
|
||
|
{
|
||
|
ctf_next_destroy (j);
|
||
|
goto err_target;
|
||
|
}
|
||
|
|
||
|
if (name == NULL)
|
||
|
name = "";
|
||
|
#ifdef ENABLE_LIBCTF_HASH_DEBUGGING
|
||
|
ctf_dprintf ("Emitting %s, offset %zi\n", name, offset);
|
||
|
#endif
|
||
|
if (ctf_add_member_offset (target, target_type, name,
|
||
|
membtype, offset) < 0)
|
||
|
{
|
||
|
ctf_next_destroy (j);
|
||
|
goto err_target;
|
||
|
}
|
||
|
}
|
||
|
if (ctf_errno (input_fp) != ECTF_NEXT_END)
|
||
|
{
|
||
|
err = ctf_errno (input_fp);
|
||
|
ctf_next_destroy (i);
|
||
|
goto iterr;
|
||
|
}
|
||
|
}
|
||
|
if (err != ECTF_NEXT_END)
|
||
|
goto iterr;
|
||
|
|
||
|
return 0;
|
||
|
err_target:
|
||
|
ctf_next_destroy (i);
|
||
|
ctf_err_warn (output, 0, ctf_errno (err_fp),
|
||
|
_("%s (%i): error emitting members for structure type %lx"),
|
||
|
ctf_link_input_name (input_fp), input_num, err_type);
|
||
|
return ctf_set_errno (output, ctf_errno (err_fp));
|
||
|
iterr:
|
||
|
ctf_err_warn (output, 0, err, _("iteration failure emitting "
|
||
|
"structure members"));
|
||
|
return ctf_set_errno (output, err);
|
||
|
}
|
||
|
|
||
|
/* Emit deduplicated types into the outputs. The shared type repository is
|
||
|
OUTPUT, on which the ctf_dedup function must have already been called. The
|
||
|
PARENTS array contains the INPUTS index of the parent dict for every child
|
||
|
dict at the corresponding index in the INPUTS (for non-child dicts, the value
|
||
|
is undefined).
|
||
|
|
||
|
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. */
|
||
|
|
||
|
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)
|
||
|
{
|
||
|
size_t num_outputs = 1; /* Always at least one output: us. */
|
||
|
ctf_dict_t **outputs;
|
||
|
ctf_dict_t **walk;
|
||
|
size_t i;
|
||
|
|
||
|
ctf_dprintf ("Triggering emission.\n");
|
||
|
if (ctf_dedup_walk_output_mapping (output, inputs, ninputs, parents,
|
||
|
ctf_dedup_emit_type, &cu_mapped) < 0)
|
||
|
return NULL; /* errno is set for us. */
|
||
|
|
||
|
ctf_dprintf ("Populating struct members.\n");
|
||
|
if (ctf_dedup_emit_struct_members (output, inputs, ninputs, parents) < 0)
|
||
|
return NULL; /* errno is set for us. */
|
||
|
|
||
|
for (i = 0; i < ninputs; i++)
|
||
|
{
|
||
|
if (inputs[i]->ctf_dedup.cd_output)
|
||
|
num_outputs++;
|
||
|
}
|
||
|
|
||
|
if (!ctf_assert (output, !cu_mapped || (cu_mapped && num_outputs == 1)))
|
||
|
return NULL;
|
||
|
|
||
|
if ((outputs = calloc (num_outputs, sizeof (ctf_dict_t *))) == NULL)
|
||
|
{
|
||
|
ctf_err_warn (output, 0, ENOMEM,
|
||
|
_("out of memory allocating link outputs array"));
|
||
|
ctf_set_errno (output, ENOMEM);
|
||
|
return NULL;
|
||
|
}
|
||
|
*noutputs = num_outputs;
|
||
|
|
||
|
walk = outputs;
|
||
|
*walk = output;
|
||
|
output->ctf_refcnt++;
|
||
|
walk++;
|
||
|
|
||
|
for (i = 0; i < ninputs; i++)
|
||
|
{
|
||
|
if (inputs[i]->ctf_dedup.cd_output)
|
||
|
{
|
||
|
*walk = inputs[i]->ctf_dedup.cd_output;
|
||
|
inputs[i]->ctf_dedup.cd_output = NULL;
|
||
|
walk++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return outputs;
|
||
|
}
|
||
|
|
||
|
/* Determine what type SRC_FP / SRC_TYPE was emitted as in the FP, which
|
||
|
must be the shared dict or have it as a parent: return 0 if none. The SRC_FP
|
||
|
must be a past input to ctf_dedup. */
|
||
|
|
||
|
ctf_id_t
|
||
|
ctf_dedup_type_mapping (ctf_dict_t *fp, ctf_dict_t *src_fp, ctf_id_t src_type)
|
||
|
{
|
||
|
ctf_dict_t *output = NULL;
|
||
|
ctf_dedup_t *d;
|
||
|
int input_num;
|
||
|
void *num_ptr;
|
||
|
void *type_ptr;
|
||
|
int found;
|
||
|
const char *hval;
|
||
|
|
||
|
/* It is an error (an internal error in the caller, in ctf-link.c) to call
|
||
|
this with an FP that is not a per-CU output or shared output dict, or with
|
||
|
a SRC_FP that was not passed to ctf_dedup as an input; it is an internal
|
||
|
error in ctf-dedup for the type passed not to have been hashed, though if
|
||
|
the src_fp is a child dict and the type is not a child type, it will have
|
||
|
been hashed under the GID corresponding to the parent. */
|
||
|
|
||
|
if (fp->ctf_dedup.cd_type_hashes != NULL)
|
||
|
output = fp;
|
||
|
else if (fp->ctf_parent && fp->ctf_parent->ctf_dedup.cd_type_hashes != NULL)
|
||
|
output = fp->ctf_parent;
|
||
|
else
|
||
|
{
|
||
|
ctf_set_errno (fp, ECTF_INTERNAL);
|
||
|
ctf_err_warn (fp, 0, ECTF_INTERNAL,
|
||
|
_("dict %p passed to ctf_dedup_type_mapping is not a "
|
||
|
"deduplicated output"), (void *) fp);
|
||
|
return CTF_ERR;
|
||
|
}
|
||
|
|
||
|
if (src_fp->ctf_parent && ctf_type_isparent (src_fp, src_type))
|
||
|
src_fp = src_fp->ctf_parent;
|
||
|
|
||
|
d = &output->ctf_dedup;
|
||
|
|
||
|
found = ctf_dynhash_lookup_kv (d->cd_input_nums, src_fp, NULL, &num_ptr);
|
||
|
if (!ctf_assert (output, found != 0))
|
||
|
return CTF_ERR; /* errno is set for us. */
|
||
|
input_num = (uintptr_t) num_ptr;
|
||
|
|
||
|
hval = ctf_dynhash_lookup (d->cd_type_hashes,
|
||
|
CTF_DEDUP_GID (output, input_num, src_type));
|
||
|
|
||
|
if (!ctf_assert (output, hval != NULL))
|
||
|
return CTF_ERR; /* errno is set for us. */
|
||
|
|
||
|
/* The emission hashes may be unset if this dict was created after
|
||
|
deduplication to house variables or other things that would conflict if
|
||
|
stored in the shared dict. */
|
||
|
if (fp->ctf_dedup.cd_output_emission_hashes)
|
||
|
if (ctf_dynhash_lookup_kv (fp->ctf_dedup.cd_output_emission_hashes, hval,
|
||
|
NULL, &type_ptr))
|
||
|
return (ctf_id_t) (uintptr_t) type_ptr;
|
||
|
|
||
|
if (fp->ctf_parent)
|
||
|
{
|
||
|
ctf_dict_t *pfp = fp->ctf_parent;
|
||
|
if (pfp->ctf_dedup.cd_output_emission_hashes)
|
||
|
if (ctf_dynhash_lookup_kv (pfp->ctf_dedup.cd_output_emission_hashes,
|
||
|
hval, NULL, &type_ptr))
|
||
|
return (ctf_id_t) (uintptr_t) type_ptr;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|