summaryrefslogtreecommitdiff
path: root/libctf/ctf-link.c
diff options
context:
space:
mode:
Diffstat (limited to 'libctf/ctf-link.c')
-rw-r--r--libctf/ctf-link.c660
1 files changed, 658 insertions, 2 deletions
diff --git a/libctf/ctf-link.c b/libctf/ctf-link.c
index 043d6a2f530..f269fe7ac0e 100644
--- a/libctf/ctf-link.c
+++ b/libctf/ctf-link.c
@@ -819,6 +819,659 @@ ctf_link_one_input_archive (void *key, void *value, void *arg_)
ctf_link_close_one_input_archive (key, value, NULL);
}
+typedef struct link_sort_inputs_cb_arg
+{
+ int is_cu_mapped;
+ ctf_file_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_file_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, "Iteration error counting deduplicating CTF link "
+ "inputs: %s", ctf_errmsg (err));
+ 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_file_t **
+ctf_link_deduplicating_open_inputs (ctf_file_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_file_t **dedup_inputs = NULL;
+ ctf_file_t **walk;
+ uint32_t *parents_ = NULL;
+ int err;
+
+ if (cu_names)
+ inputs = cu_names;
+
+ if ((dedup_inputs = calloc (ninputs, sizeof (ctf_file_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_file_t *one_fp;
+ ctf_file_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_arc_open_by_name (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, "Error in deduplicating CTF link input allocation: %s",
+ ctf_errmsg (ctf_errno (fp)));
+ 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_file_t *fp, ctf_dynhash_t *cu_names,
+ ctf_file_t **inputs, ssize_t ninputs)
+{
+ ctf_next_t *it = NULL;
+ void *name;
+ int err;
+ ssize_t i;
+
+ /* This is the inverse of ctf_link_deduplicating_open_inputs: so first, close
+ all the individual input dicts, opened by the archive iterator. */
+ for (i = 0; i < ninputs; i++)
+ ctf_file_close (inputs[i]);
+
+ /* Now close the archives they are part of. */
+ if (cu_names)
+ {
+ while ((err = ctf_dynhash_next (cu_names, &it, &name, NULL)) == 0)
+ {
+ /* Remove the input from the linker inputs, if it exists, which also
+ closes it. */
+
+ ctf_dynhash_remove (fp->ctf_link_inputs, (const char *) name);
+ }
+ if (err != ECTF_NEXT_END)
+ {
+ ctf_err_warn (fp, 0, "Iteration error in deduplicating link input "
+ "freeing: %s", ctf_errmsg (err));
+ ctf_set_errno (fp, err);
+ }
+ }
+ else
+ ctf_dynhash_empty (fp->ctf_link_inputs);
+
+ return 0;
+}
+
+/* Do a deduplicating link of all variables in the inputs. */
+static int
+ctf_link_deduplicating_variables (ctf_file_t *fp, ctf_file_t **inputs,
+ size_t ninputs, int cu_mapped)
+{
+ ctf_link_in_member_cb_arg_t arg;
+ size_t i;
+
+ arg.cu_mapped = cu_mapped;
+ arg.out_fp = fp;
+ arg.in_input_cu_file = 0;
+
+ for (i = 0; i < ninputs; i++)
+ {
+ arg.in_fp = inputs[i];
+ if (ctf_cuname (inputs[i]) != NULL)
+ arg.in_file_name = ctf_cuname (inputs[i]);
+ else
+ arg.in_file_name = "unnamed-CU";
+ arg.cu_name = arg.in_file_name;
+ if (ctf_variable_iter (arg.in_fp, ctf_link_one_variable, &arg) < 0)
+ return ctf_set_errno (fp, ctf_errno (arg.in_fp));
+
+ /* Outputs > 0 are per-CU. */
+ arg.in_input_cu_file = 1;
+ }
+ return 0;
+}
+
+/* Do the per-CU part of a deduplicating link. */
+static int
+ctf_link_deduplicating_per_cu (ctf_file_t *fp)
+{
+ ctf_next_t *i = NULL;
+ int err;
+ void *out_cu;
+ void *in_cus;
+
+ /* Links with a per-CU mapping in force get a first pass of deduplication,
+ dedupping the inputs for a given CU mapping into the output for that
+ mapping. The outputs from this process get fed back into the final pass
+ that is carried out even for non-CU links. */
+
+ while ((err = ctf_dynhash_next (fp->ctf_link_out_cu_mapping, &i, &out_cu,
+ &in_cus)) == 0)
+ {
+ const char *out_name = (const char *) out_cu;
+ ctf_dynhash_t *in = (ctf_dynhash_t *) in_cus;
+ ctf_file_t *out = NULL;
+ ctf_file_t **inputs;
+ ctf_file_t **outputs;
+ ctf_archive_t *in_arc;
+ ssize_t ninputs;
+ ctf_link_input_t *only_input;
+ uint32_t noutputs;
+ uint32_t *parents;
+
+ if ((ninputs = ctf_link_deduplicating_count_inputs (fp, in,
+ &only_input)) == -1)
+ goto err_open_inputs;
+
+ /* CU mapping with no inputs? Skip. */
+ if (ninputs == 0)
+ continue;
+
+ if (labs ((long int) ninputs) > 0xfffffffe)
+ {
+ ctf_err_warn (fp, 0, "Too many inputs in deduplicating link: %li",
+ (long int) ninputs);
+ goto err_open_inputs;
+ }
+
+ /* Short-circuit: a cu-mapped link with only one input archive with
+ unconflicting contents is a do-nothing, and we can just leave the input
+ in place: we do have to change the cuname, though, so we unwrap it,
+ change the cuname, then stuff it back in the linker input again, via
+ the clin_fp short-circuit member. ctf_link_deduplicating_open_inputs
+ will spot this member and jam it straight into the next link phase,
+ ignoring the corresponding archive. */
+ if (only_input && ninputs == 1)
+ {
+ ctf_next_t *ai = NULL;
+ int err;
+
+ /* We can abuse an archive iterator to get the only member cheaply, no
+ matter what its name. */
+ only_input->clin_fp = ctf_archive_next (only_input->clin_arc,
+ &ai, NULL, 0, &err);
+ if (!only_input->clin_fp)
+ {
+ ctf_err_warn (fp, 0, "Cannot open archive %s in CU-mapped CTF "
+ "link: %s", only_input->clin_filename,
+ ctf_errmsg (err));
+ ctf_set_errno (fp, err);
+ goto err_open_inputs;
+ }
+ ctf_next_destroy (ai);
+
+ if (strcmp (only_input->clin_filename, out_name) != 0)
+ {
+ /* Renaming. We need to add a new input, then null out the
+ clin_arc and clin_fp of the old one to stop it being
+ auto-closed on removal. The new input needs its cuname changed
+ to out_name, which is doable only because the cuname is a
+ dynamic property which can be changed even in readonly
+ dicts. */
+
+ ctf_cuname_set (only_input->clin_fp, out_name);
+ if (ctf_link_add_ctf_internal (fp, only_input->clin_arc,
+ only_input->clin_fp,
+ out_name) < 0)
+ {
+ ctf_err_warn (fp, 0, "Cannot add intermediate files "
+ "to link: %s", ctf_errmsg (ctf_errno (fp)));
+ goto err_open_inputs;
+ }
+ only_input->clin_arc = NULL;
+ only_input->clin_fp = NULL;
+ ctf_dynhash_remove (fp->ctf_link_inputs,
+ only_input->clin_filename);
+ }
+ continue;
+ }
+
+ /* This is a real CU many-to-one mapping: we must dedup the inputs into
+ a new output to be used in the final link phase. */
+
+ if ((inputs = ctf_link_deduplicating_open_inputs (fp, in, ninputs,
+ &parents)) == NULL)
+ {
+ ctf_next_destroy (i);
+ goto err_inputs;
+ }
+
+ if ((out = ctf_create (&err)) == NULL)
+ {
+ ctf_err_warn (fp, 0, "Cannot create per-CU CTF archive for %s: %s",
+ out_name, ctf_errmsg (err));
+ ctf_set_errno (fp, err);
+ goto err_inputs;
+ }
+
+ /* Share the atoms table to reduce memory usage. */
+ out->ctf_dedup_atoms = fp->ctf_dedup_atoms_alloc;
+
+ /* No ctf_imports at this stage: this per-CU dictionary has no parents.
+ Parent/child deduplication happens in the link's final pass. However,
+ the cuname *is* important, as it is propagated into the final
+ dictionary. */
+ ctf_cuname_set (out, out_name);
+
+ if (ctf_dedup (out, inputs, ninputs, parents, 1) < 0)
+ {
+ ctf_err_warn (fp, 0, "CU-mapped deduplication failed for %s: %s",
+ out_name, ctf_errmsg (ctf_errno (out)));
+ goto err_inputs;
+ }
+
+ if ((outputs = ctf_dedup_emit (out, inputs, ninputs, parents,
+ &noutputs, 1)) == NULL)
+ {
+ ctf_err_warn (fp, 0, "CU-mapped deduplicating link type emission "
+ "failed for %s: %s", out_name,
+ ctf_errmsg (ctf_errno (out)));
+ goto err_inputs;
+ }
+ if (!ctf_assert (fp, noutputs == 1))
+ goto err_inputs_outputs;
+
+ if (!(fp->ctf_link_flags & CTF_LINK_OMIT_VARIABLES_SECTION)
+ && ctf_link_deduplicating_variables (out, inputs, ninputs, 1) < 0)
+ {
+ ctf_err_warn (fp, 0, "CU-mapped deduplicating link variable "
+ "emission failed for %s: %s", out_name,
+ ctf_errmsg (ctf_errno (out)));
+ goto err_inputs_outputs;
+ }
+
+ if (ctf_link_deduplicating_close_inputs (fp, in, inputs, ninputs) < 0)
+ {
+ free (inputs);
+ free (parents);
+ goto err_outputs;
+ }
+ free (inputs);
+ free (parents);
+
+ /* Splice any errors or warnings created during this link back into the
+ dict that the caller knows about. */
+ ctf_list_splice (&fp->ctf_errs_warnings, &outputs[0]->ctf_errs_warnings);
+
+ /* This output now becomes an input to the next link phase, with a name
+ equal to the CU name. We have to wrap it in an archive wrapper
+ first. */
+
+ if ((in_arc = ctf_new_archive_internal (0, 0, NULL, outputs[0], NULL,
+ NULL, &err)) == NULL)
+ {
+ ctf_set_errno (fp, err);
+ goto err_outputs;
+ }
+
+ if (ctf_link_add_ctf_internal (fp, in_arc, NULL,
+ ctf_cuname (outputs[0])) < 0)
+ {
+ ctf_err_warn (fp, 0, "Cannot add intermediate files to link: %s",
+ ctf_errmsg (ctf_errno (fp)));
+ goto err_outputs;
+ }
+
+ ctf_file_close (out);
+ free (outputs);
+ continue;
+
+ err_inputs_outputs:
+ ctf_list_splice (&fp->ctf_errs_warnings, &outputs[0]->ctf_errs_warnings);
+ ctf_file_close (outputs[0]);
+ free (outputs);
+ err_inputs:
+ ctf_link_deduplicating_close_inputs (fp, in, inputs, ninputs);
+ ctf_file_close (out);
+ free (inputs);
+ free (parents);
+ err_open_inputs:
+ ctf_next_destroy (i);
+ return -1;
+
+ err_outputs:
+ ctf_list_splice (&fp->ctf_errs_warnings, &outputs[0]->ctf_errs_warnings);
+ ctf_file_close (outputs[0]);
+ free (outputs);
+ ctf_next_destroy (i);
+ return -1; /* Errno is set for us. */
+ }
+ if (err != ECTF_NEXT_END)
+ {
+ ctf_err_warn (fp, 0, "Iteration error in CU-mapped deduplicating "
+ "link: %s", ctf_errmsg (err));
+ return ctf_set_errno (fp, err);
+ }
+
+ return 0;
+}
+
+/* Do a deduplicating link using the ctf-dedup machinery. */
+static void
+ctf_link_deduplicating (ctf_file_t *fp)
+{
+ size_t i;
+ ctf_file_t **inputs, **outputs = NULL;
+ ssize_t ninputs;
+ uint32_t noutputs;
+ uint32_t *parents;
+
+ if (ctf_dedup_atoms_init (fp) < 0)
+ {
+ ctf_err_warn (fp, 0, "%s allocating CTF dedup atoms table",
+ ctf_errmsg (ctf_errno (fp)));
+ return; /* Errno is set for us. */
+ }
+
+ if (fp->ctf_link_out_cu_mapping
+ && (ctf_link_deduplicating_per_cu (fp) < 0))
+ return; /* Errno is set for us. */
+
+ if ((ninputs = ctf_link_deduplicating_count_inputs (fp, NULL, NULL)) < 0)
+ return; /* Errno is set for us. */
+
+ if ((inputs = ctf_link_deduplicating_open_inputs (fp, NULL, ninputs,
+ &parents)) == NULL)
+ return; /* Errno is set for us. */
+
+ if (ninputs == 1 && ctf_cuname (inputs[0]) != NULL)
+ ctf_cuname_set (fp, ctf_cuname (inputs[0]));
+
+ if (ctf_dedup (fp, inputs, ninputs, parents, 0) < 0)
+ {
+ ctf_err_warn (fp, 0, "Deduplication failed for %s: %s",
+ ctf_link_input_name (fp), ctf_errmsg (ctf_errno (fp)));
+ goto err;
+ }
+
+ if ((outputs = ctf_dedup_emit (fp, inputs, ninputs, parents, &noutputs,
+ 0)) == NULL)
+ {
+ ctf_err_warn (fp, 0, "Deduplicating link type emission failed "
+ "for %s: %s", ctf_link_input_name (fp),
+ ctf_errmsg (ctf_errno (fp)));
+ goto err;
+ }
+
+ if (!ctf_assert (fp, outputs[0] == fp))
+ goto err;
+
+ for (i = 0; i < noutputs; i++)
+ {
+ char *dynname;
+
+ /* We already have access to this one. Close the duplicate. */
+ if (i == 0)
+ {
+ ctf_file_close (outputs[0]);
+ continue;
+ }
+
+ if ((dynname = strdup (ctf_cuname (outputs[i]))) == NULL)
+ goto oom_one_output;
+
+ if (ctf_dynhash_insert (fp->ctf_link_outputs, dynname, outputs[i]) < 0)
+ goto oom_one_output;
+
+ continue;
+
+ oom_one_output:
+ ctf_err_warn (fp, 0, "Out of memory allocating link outputs");
+ ctf_set_errno (fp, ENOMEM);
+ free (dynname);
+
+ for (; i < noutputs; i++)
+ ctf_file_close (outputs[i]);
+ goto err;
+ }
+
+ if (!(fp->ctf_link_flags & CTF_LINK_OMIT_VARIABLES_SECTION)
+ && ctf_link_deduplicating_variables (fp, inputs, ninputs, 0) < 0)
+ {
+ ctf_err_warn (fp, 0, "Deduplicating link variable emission failed for "
+ "%s: %s", ctf_link_input_name (fp),
+ ctf_errmsg (ctf_errno (fp)));
+ for (i = 1; i < noutputs; i++)
+ ctf_file_close (outputs[i]);
+ goto err;
+ }
+
+ /* Now close all the inputs, including per-CU intermediates. */
+
+ if (ctf_link_deduplicating_close_inputs (fp, NULL, inputs, ninputs) < 0)
+ return; /* errno is set for us. */
+
+ ninputs = 0; /* Prevent double-close. */
+ ctf_set_errno (fp, 0);
+
+ /* Fall through. */
+
+ err:
+ for (i = 0; i < (size_t) ninputs; i++)
+ ctf_file_close (inputs[i]);
+ free (inputs);
+ free (parents);
+ free (outputs);
+ return;
+}
+
/* Merge types and variable sections in all files added to the link
together. All the added files are closed. */
int
@@ -871,8 +1524,11 @@ ctf_link (ctf_file_t *fp, int flags)
}
}
- ctf_dynhash_iter (fp->ctf_link_inputs, ctf_link_one_input_archive,
- &arg);
+ if ((flags & CTF_LINK_NONDEDUP) || (getenv ("LD_NO_CTF_DEDUP")))
+ ctf_dynhash_iter (fp->ctf_link_inputs, ctf_link_one_input_archive,
+ &arg);
+ else
+ ctf_link_deduplicating (fp);
/* Discard the now-unnecessary mapping table data from all the outputs. */
if (fp->ctf_link_type_mapping)