+/* Add a mapping directing that the CU named FROM should have its
+ conflicting/non-duplicate types (depending on link mode) go into a dict
+ named TO. Many FROMs can share a TO.
+
+ We forcibly add a dict named TO in every case, even though it may well
+ wind up empty, because clients that use this facility usually expect to find
+ every TO dict present, even if empty, and malfunction otherwise. */
+
+int
+ctf_link_add_cu_mapping (ctf_dict_t *fp, const char *from, const char *to)
+{
+ int err;
+ char *f = NULL, *t = NULL;
+ ctf_dynhash_t *one_out;
+
+ /* Mappings cannot be set up if per-CU output dicts already exist. */
+ if (fp->ctf_link_outputs && ctf_dynhash_elements (fp->ctf_link_outputs) != 0)
+ return (ctf_set_errno (fp, ECTF_LINKADDEDLATE));
+
+ if (fp->ctf_link_in_cu_mapping == NULL)
+ fp->ctf_link_in_cu_mapping = ctf_dynhash_create (ctf_hash_string,
+ ctf_hash_eq_string, free,
+ free);
+ if (fp->ctf_link_in_cu_mapping == NULL)
+ goto oom;
+
+ if (fp->ctf_link_out_cu_mapping == NULL)
+ fp->ctf_link_out_cu_mapping = ctf_dynhash_create (ctf_hash_string,
+ ctf_hash_eq_string, free,
+ (ctf_hash_free_fun)
+ ctf_dynhash_destroy);
+ if (fp->ctf_link_out_cu_mapping == NULL)
+ goto oom;
+
+ f = strdup (from);
+ t = strdup (to);
+ if (!f || !t)
+ goto oom;
+
+ /* Track both in a list from FROM to TO and in a list from TO to a list of
+ FROM. The former is used to create TUs with the mapped-to name at need:
+ the latter is used in deduplicating links to pull in all input CUs
+ corresponding to a single output CU. */
+
+ if ((err = ctf_dynhash_insert (fp->ctf_link_in_cu_mapping, f, t)) < 0)
+ {
+ ctf_set_errno (fp, err);
+ goto oom_noerrno;
+ }
+
+ /* f and t are now owned by the in_cu_mapping: reallocate them. */
+ f = strdup (from);
+ t = strdup (to);
+ if (!f || !t)
+ goto oom;
+
+ if ((one_out = ctf_dynhash_lookup (fp->ctf_link_out_cu_mapping, t)) == NULL)
+ {
+ if ((one_out = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
+ free, NULL)) == NULL)
+ goto oom;
+ if ((err = ctf_dynhash_insert (fp->ctf_link_out_cu_mapping,
+ t, one_out)) < 0)
+ {
+ ctf_dynhash_destroy (one_out);
+ ctf_set_errno (fp, err);
+ goto oom_noerrno;
+ }
+ }
+ else
+ {
+ free (t);
+ t = NULL;
+ }
+
+ if (ctf_dynhash_insert (one_out, f, NULL) < 0)
+ {
+ ctf_set_errno (fp, err);
+ goto oom_noerrno;
+ }
+
+ return 0;
+
+ oom:
+ ctf_set_errno (fp, errno);
+ oom_noerrno:
+ free (f);
+ free (t);
+ return -1;
+}
+
+/* Set a function which is called to transform the names of archive members.
+ This is useful for applying regular transformations to many names, where
+ ctf_link_add_cu_mapping applies arbitrarily irregular changes to single
+ names. The member name changer is applied at ctf_link_write time, so it
+ cannot conflate multiple CUs into one the way ctf_link_add_cu_mapping can.
+ The changer function accepts a name and should return a new
+ dynamically-allocated name, or NULL if the name should be left unchanged. */
+void
+ctf_link_set_memb_name_changer (ctf_dict_t *fp,
+ ctf_link_memb_name_changer_f *changer,
+ void *arg)
+{
+ fp->ctf_link_memb_name_changer = changer;
+ fp->ctf_link_memb_name_changer_arg = arg;
+}
+
+/* Set a function which is used to filter out unwanted variables from the link. */
+int
+ctf_link_set_variable_filter (ctf_dict_t *fp, ctf_link_variable_filter_f *filter,
+ void *arg)
+{
+ fp->ctf_link_variable_filter = filter;
+ fp->ctf_link_variable_filter_arg = arg;
+ return 0;
+}
+
+/* Check if we can safely add a variable with the given type to this dict. */
+
+static int
+check_variable (const char *name, ctf_dict_t *fp, ctf_id_t type,
+ ctf_dvdef_t **out_dvd)
+{
+ ctf_dvdef_t *dvd;
+
+ dvd = ctf_dynhash_lookup (fp->ctf_dvhash, name);
+ *out_dvd = dvd;
+ if (!dvd)
+ return 1;
+
+ if (dvd->dvd_type != type)
+ {
+ /* Variable here. Wrong type: cannot add. Just skip it, because there is
+ no way to express this in CTF. Don't even warn: this case is too
+ common. (This might be the parent, in which case we'll try adding in
+ the child first, and only then give up.) */
+ ctf_dprintf ("Inexpressible duplicate variable %s skipped.\n", name);
+ }
+
+ return 0; /* Already exists. */
+}
+
+/* Link one variable named NAME of type TYPE found in IN_FP into FP. */
+
+static int
+ctf_link_one_variable (ctf_dict_t *fp, ctf_dict_t *in_fp, const char *name,
+ ctf_id_t type, int cu_mapped)
+{
+ ctf_dict_t *per_cu_out_fp;
+ ctf_id_t dst_type = 0;
+ ctf_dvdef_t *dvd;
+
+ /* See if this variable is filtered out. */
+
+ if (fp->ctf_link_variable_filter)
+ {
+ void *farg = fp->ctf_link_variable_filter_arg;
+ if (fp->ctf_link_variable_filter (in_fp, name, type, farg))
+ return 0;
+ }
+
+ /* If this type is mapped to a type in the parent dict, we want to try to add
+ to that first: if it reports a duplicate, or if the type is in a child
+ already, add straight to the child. */
+
+ if ((dst_type = ctf_dedup_type_mapping (fp, in_fp, type)) == CTF_ERR)
+ return -1; /* errno is set for us. */
+
+ if (dst_type != 0)
+ {
+ if (!ctf_assert (fp, ctf_type_isparent (fp, dst_type)))
+ return -1; /* errno is set for us. */
+
+ if (check_variable (name, fp, dst_type, &dvd))
+ {
+ /* No variable here: we can add it. */
+ if (ctf_add_variable (fp, name, dst_type) < 0)
+ return -1; /* errno is set for us. */
+ return 0;
+ }
+
+ /* Already present? Nothing to do. */
+ if (dvd && dvd->dvd_type == dst_type)
+ return 0;
+ }
+
+ /* Can't add to the parent due to a name clash, or because it references a
+ type only present in the child. Try adding to the child, creating if need
+ be. If we can't do that, skip it. Don't add to a child if we're doing a
+ CU-mapped link, since that has only one output. */
+
+ if (cu_mapped)
+ {
+ ctf_dprintf ("Variable %s in input file %s depends on a type %lx hidden "
+ "due to conflicts: skipped.\n", name,
+ ctf_unnamed_cuname (in_fp), type);
+ return 0;
+ }
+
+ if ((per_cu_out_fp = ctf_create_per_cu (fp, in_fp, NULL)) == NULL)
+ return -1; /* errno is set for us. */
+
+ /* If the type was not found, check for it in the child too. */
+ if (dst_type == 0)
+ {
+ if ((dst_type = ctf_dedup_type_mapping (per_cu_out_fp,
+ in_fp, type)) == CTF_ERR)
+ return -1; /* errno is set for us. */
+
+ if (dst_type == 0)
+ {
+ ctf_err_warn (fp, 1, 0, _("type %lx for variable %s in input file %s "
+ "not found: skipped"), type, name,
+ ctf_unnamed_cuname (in_fp));
+ /* Do not terminate the link: just skip the variable. */
+ return 0;
+ }
+ }
+
+ if (check_variable (name, per_cu_out_fp, dst_type, &dvd))
+ if (ctf_add_variable (per_cu_out_fp, name, dst_type) < 0)
+ return (ctf_set_errno (fp, ctf_errno (per_cu_out_fp)));
+ return 0;
+}
+
+typedef struct link_sort_inputs_cb_arg
+{
+ int is_cu_mapped;
+ ctf_dict_t *fp;
+} link_sort_inputs_cb_arg_t;
+
+/* Sort the inputs by N (the link order). For CU-mapped links, this is a
+ mapping of input to output name, not a mapping of input name to input
+ ctf_link_input_t: compensate accordingly. */
+static int
+ctf_link_sort_inputs (const ctf_next_hkv_t *one, const ctf_next_hkv_t *two,
+ void *arg)
+{
+ ctf_link_input_t *input_1;
+ ctf_link_input_t *input_2;
+ link_sort_inputs_cb_arg_t *cu_mapped = (link_sort_inputs_cb_arg_t *) arg;
+
+ if (!cu_mapped || !cu_mapped->is_cu_mapped)
+ {
+ input_1 = (ctf_link_input_t *) one->hkv_value;
+ input_2 = (ctf_link_input_t *) two->hkv_value;
+ }
+ else
+ {
+ const char *name_1 = (const char *) one->hkv_key;
+ const char *name_2 = (const char *) two->hkv_key;
+
+ input_1 = ctf_dynhash_lookup (cu_mapped->fp->ctf_link_inputs, name_1);
+ input_2 = ctf_dynhash_lookup (cu_mapped->fp->ctf_link_inputs, name_2);
+
+ /* There is no guarantee that CU-mappings actually have corresponding
+ inputs: the relative ordering in that case is unimportant. */
+ if (!input_1)
+ return -1;
+ if (!input_2)
+ return 1;
+ }
+
+ if (input_1->n < input_2->n)
+ return -1;
+ else if (input_1->n > input_2->n)
+ return 1;
+ else
+ return 0;
+}
+
+/* Count the number of input dicts in the ctf_link_inputs, or that subset of the
+ ctf_link_inputs given by CU_NAMES if set. Return the number of input dicts,
+ and optionally the name and ctf_link_input_t of the single input archive if
+ only one exists (no matter how many dicts it contains). */
+static ssize_t
+ctf_link_deduplicating_count_inputs (ctf_dict_t *fp, ctf_dynhash_t *cu_names,
+ ctf_link_input_t **only_one_input)
+{
+ ctf_dynhash_t *inputs = fp->ctf_link_inputs;
+ ctf_next_t *i = NULL;
+ void *name, *input;
+ ctf_link_input_t *one_input = NULL;
+ const char *one_name = NULL;
+ ssize_t count = 0, narcs = 0;
+ int err;
+
+ if (cu_names)
+ inputs = cu_names;
+
+ while ((err = ctf_dynhash_next (inputs, &i, &name, &input)) == 0)
+ {
+ ssize_t one_count;
+
+ one_name = (const char *) name;
+ /* If we are processing CU names, get the real input. */
+ if (cu_names)
+ one_input = ctf_dynhash_lookup (fp->ctf_link_inputs, one_name);
+ else
+ one_input = (ctf_link_input_t *) input;
+
+ if (!one_input)
+ continue;
+
+ one_count = ctf_link_lazy_open (fp, one_input);
+
+ if (one_count < 0)
+ {
+ ctf_next_destroy (i);
+ return -1; /* errno is set for us. */
+ }
+
+ count += one_count;
+ narcs++;
+ }
+ if (err != ECTF_NEXT_END)
+ {
+ ctf_err_warn (fp, 0, err, _("iteration error counting deduplicating "
+ "CTF link inputs"));
+ ctf_set_errno (fp, err);
+ return -1;
+ }
+
+ if (!count)
+ return 0;
+
+ if (narcs == 1)
+ {
+ if (only_one_input)
+ *only_one_input = one_input;
+ }
+ else if (only_one_input)
+ *only_one_input = NULL;
+
+ return count;
+}
+
+/* Allocate and populate an inputs array big enough for a given set of inputs:
+ either a specific set of CU names (those from that set found in the
+ ctf_link_inputs), or the entire ctf_link_inputs (if cu_names is not set).
+ The number of inputs (from ctf_link_deduplicating_count_inputs, above) is
+ passed in NINPUTS: an array of uint32_t containing parent pointers
+ (corresponding to those members of the inputs that have parents) is allocated
+ and returned in PARENTS.
+
+ The inputs are *archives*, not files: the archive can have multiple members
+ if it is the result of a previous incremental link. We want to add every one
+ in turn, including the shared parent. (The dedup machinery knows that a type
+ used by a single dictionary and its parent should not be shared in
+ CTF_LINK_SHARE_DUPLICATED mode.)
+
+ If no inputs exist that correspond to these CUs, return NULL with the errno
+ set to ECTF_NOCTFDATA. */
+static ctf_dict_t **
+ctf_link_deduplicating_open_inputs (ctf_dict_t *fp, ctf_dynhash_t *cu_names,
+ ssize_t ninputs, uint32_t **parents)
+{
+ ctf_dynhash_t *inputs = fp->ctf_link_inputs;
+ ctf_next_t *i = NULL;
+ void *name, *input;
+ link_sort_inputs_cb_arg_t sort_arg;
+ ctf_dict_t **dedup_inputs = NULL;
+ ctf_dict_t **walk;
+ uint32_t *parents_ = NULL;
+ int err;
+
+ if (cu_names)
+ inputs = cu_names;
+
+ if ((dedup_inputs = calloc (ninputs, sizeof (ctf_dict_t *))) == NULL)
+ goto oom;
+
+ if ((parents_ = calloc (ninputs, sizeof (uint32_t))) == NULL)
+ goto oom;
+
+ walk = dedup_inputs;
+
+ /* Counting done: push every input into the array, in the order they were
+ passed to ctf_link_add_ctf (and ultimately ld). */
+
+ sort_arg.is_cu_mapped = (cu_names != NULL);
+ sort_arg.fp = fp;
+
+ while ((err = ctf_dynhash_next_sorted (inputs, &i, &name, &input,
+ ctf_link_sort_inputs, &sort_arg)) == 0)
+ {
+ const char *one_name = (const char *) name;
+ ctf_link_input_t *one_input;
+ ctf_dict_t *one_fp;
+ ctf_dict_t *parent_fp = NULL;
+ uint32_t parent_i;
+ ctf_next_t *j = NULL;
+
+ /* If we are processing CU names, get the real input. All the inputs
+ will have been opened, if they contained any CTF at all. */
+ if (cu_names)
+ one_input = ctf_dynhash_lookup (fp->ctf_link_inputs, one_name);
+ else
+ one_input = (ctf_link_input_t *) input;
+
+ if (!one_input || (!one_input->clin_arc && !one_input->clin_fp))
+ continue;
+
+ /* Short-circuit: if clin_fp is set, just use it. */
+ if (one_input->clin_fp)
+ {
+ parents_[walk - dedup_inputs] = walk - dedup_inputs;
+ *walk = one_input->clin_fp;
+ walk++;
+ continue;
+ }
+
+ /* Get and insert the parent archive (if any), if this archive has
+ multiple members. We assume, as elsewhere, that the parent is named
+ _CTF_SECTION. */
+
+ if ((parent_fp = ctf_dict_open (one_input->clin_arc, _CTF_SECTION,
+ &err)) == NULL)
+ {
+ if (err != ECTF_NOMEMBNAM)
+ {
+ ctf_next_destroy (i);
+ ctf_set_errno (fp, err);
+ goto err;
+ }
+ }
+ else
+ {
+ *walk = parent_fp;
+ parent_i = walk - dedup_inputs;
+ walk++;
+ }
+
+ /* We disregard the input archive name: either it is the parent (which we
+ already have), or we want to put everything into one TU sharing the
+ cuname anyway (if this is a CU-mapped link), or this is the final phase
+ of a relink with CU-mapping off (i.e. ld -r) in which case the cuname
+ is correctly set regardless. */
+ while ((one_fp = ctf_archive_next (one_input->clin_arc, &j, NULL,
+ 1, &err)) != NULL)
+ {
+ if (one_fp->ctf_flags & LCTF_CHILD)
+ {
+ /* The contents of the parents array for elements not
+ corresponding to children is undefined. If there is no parent
+ (itself a sign of a likely linker bug or corrupt input), we set
+ it to itself. */
+
+ ctf_import (one_fp, parent_fp);
+ if (parent_fp)
+ parents_[walk - dedup_inputs] = parent_i;
+ else
+ parents_[walk - dedup_inputs] = walk - dedup_inputs;
+ }
+ *walk = one_fp;
+ walk++;
+ }
+ if (err != ECTF_NEXT_END)
+ {
+ ctf_next_destroy (i);
+ goto iterr;
+ }
+ }
+ if (err != ECTF_NEXT_END)
+ goto iterr;
+
+ *parents = parents_;
+
+ return dedup_inputs;
+
+ oom:
+ err = ENOMEM;
+
+ iterr:
+ ctf_set_errno (fp, err);
+
+ err:
+ free (dedup_inputs);
+ free (parents_);
+ ctf_err_warn (fp, 0, 0, _("error in deduplicating CTF link "
+ "input allocation"));
+ return NULL;
+}
+
+/* Close INPUTS that have already been linked, first the passed array, and then
+ that subset of the ctf_link_inputs archives they came from cited by the
+ CU_NAMES. If CU_NAMES is not specified, close all the ctf_link_inputs in one
+ go, leaving it empty. */
+static int
+ctf_link_deduplicating_close_inputs (ctf_dict_t *fp, ctf_dynhash_t *cu_names,
+ ctf_dict_t **inputs, ssize_t ninputs)