/* CTF linking.
- Copyright (C) 2019 Free Software Foundation, Inc.
+ Copyright (C) 2019-2022 Free Software Foundation, Inc.
This file is part of libctf.
#include <ctf-impl.h>
#include <string.h>
-/* Type tracking machinery. */
+#if defined (PIC)
+#pragma weak ctf_open
+#endif
-/* Record the correspondence between a source and ctf_add_type()-added
- destination type: both types are translated into parent type IDs if need be,
- so they relate to the actual container they are in. Outside controlled
- circumstances (like linking) it is probably not useful to do more than
- compare these pointers, since there is nothing stopping the user closing the
- source container whenever they want to.
+/* CTF linking consists of adding CTF archives full of content to be merged into
+ this one to the current file (which must be writable) by calling
+ ctf_link_add_ctf. Once this is done, a call to ctf_link will merge the type
+ tables together, generating new CTF files as needed, with this one as a
+ parent, to contain types from the inputs which conflict. ctf_link_add_strtab
+ takes a callback which provides string/offset pairs to be added to the
+ external symbol table and deduplicated from all CTF string tables in the
+ output link; ctf_link_shuffle_syms takes a callback which provides symtab
+ entries in ascending order, and shuffles the function and data sections to
+ match; and ctf_link_write emits a CTF file (if there are no conflicts
+ requiring per-compilation-unit sub-CTF files) or CTF archives (otherwise) and
+ returns it, suitable for addition in the .ctf section of the output. */
+
+/* Return the name of the compilation unit this CTF dict or its parent applies
+ to, or a non-null string otherwise: prefer the parent. Used in debugging
+ output. Sometimes used for outputs too. */
+const char *
+ctf_link_input_name (ctf_dict_t *fp)
+{
+ if (fp->ctf_parent && fp->ctf_parent->ctf_cuname)
+ return fp->ctf_parent->ctf_cuname;
+ else if (fp->ctf_cuname)
+ return fp->ctf_cuname;
+ else
+ return "(unnamed)";
+}
- Our OOM handling here is just to not do anything, because this is called deep
- enough in the call stack that doing anything useful is painfully difficult:
- the worst consequence if we do OOM is a bit of type duplication anyway. */
+/* Return the cuname of a dict, or the string "unnamed-CU" if none. */
-void
-ctf_add_type_mapping (ctf_file_t *src_fp, ctf_id_t src_type,
- ctf_file_t *dst_fp, ctf_id_t dst_type)
+static const char *
+ctf_unnamed_cuname (ctf_dict_t *fp)
{
- if (LCTF_TYPE_ISPARENT (src_fp, src_type) && src_fp->ctf_parent)
- src_fp = src_fp->ctf_parent;
+ const char *cuname = ctf_cuname (fp);
- src_type = LCTF_TYPE_TO_INDEX(src_fp, src_type);
+ if (!cuname)
+ cuname = "unnamed-CU";
- if (LCTF_TYPE_ISPARENT (dst_fp, dst_type) && dst_fp->ctf_parent)
- dst_fp = dst_fp->ctf_parent;
+ return cuname;
+}
- dst_type = LCTF_TYPE_TO_INDEX(dst_fp, dst_type);
+/* The linker inputs look like this. clin_fp is used for short-circuited
+ CU-mapped links that can entirely avoid the first link phase in some
+ situations in favour of just passing on the contained ctf_dict_t: it is
+ always the sole ctf_dict_t inside the corresponding clin_arc. If set, it
+ gets assigned directly to the final link inputs and freed from there, so it
+ never gets explicitly freed in the ctf_link_input. */
+typedef struct ctf_link_input
+{
+ const char *clin_filename;
+ ctf_archive_t *clin_arc;
+ ctf_dict_t *clin_fp;
+ int n;
+} ctf_link_input_t;
- /* This dynhash is a bit tricky: it has a multivalued (structural) key, so we
- need to use the sized-hash machinery to generate key hashing and equality
- functions. */
+static void
+ctf_link_input_close (void *input)
+{
+ ctf_link_input_t *i = (ctf_link_input_t *) input;
+ if (i->clin_arc)
+ ctf_arc_close (i->clin_arc);
+ free (i);
+}
- if (dst_fp->ctf_link_type_mapping == NULL)
- {
- ctf_hash_fun f = ctf_hash_type_mapping_key;
- ctf_hash_eq_fun e = ctf_hash_eq_type_mapping_key;
+/* Like ctf_link_add_ctf, below, but with no error-checking, so it can be called
+ in the middle of an ongoing link. */
+static int
+ctf_link_add_ctf_internal (ctf_dict_t *fp, ctf_archive_t *ctf,
+ ctf_dict_t *fp_input, const char *name)
+{
+ ctf_link_input_t *input = NULL;
+ char *dupname = NULL;
- if ((dst_fp->ctf_link_type_mapping = ctf_dynhash_create (f, e, free,
- NULL)) == NULL)
- return;
- }
+ if ((input = calloc (1, sizeof (ctf_link_input_t))) == NULL)
+ goto oom;
- ctf_link_type_mapping_key_t *key;
- key = calloc (1, sizeof (struct ctf_link_type_mapping_key));
- if (!key)
- return;
+ if ((dupname = strdup (name)) == NULL)
+ goto oom;
- key->cltm_fp = src_fp;
- key->cltm_idx = src_type;
+ input->clin_arc = ctf;
+ input->clin_fp = fp_input;
+ input->clin_filename = dupname;
+ input->n = ctf_dynhash_elements (fp->ctf_link_inputs);
- ctf_dynhash_insert (dst_fp->ctf_link_type_mapping, key,
- (void *) (uintptr_t) dst_type);
+ if (ctf_dynhash_insert (fp->ctf_link_inputs, dupname, input) < 0)
+ goto oom;
+
+ return 0;
+ oom:
+ free (input);
+ free (dupname);
+ return ctf_set_errno (fp, ENOMEM);
}
-/* Look up a type mapping: return 0 if none. The DST_FP is modified to point to
- the parent if need be. The ID returned is from the dst_fp's perspective. */
-ctf_id_t
-ctf_type_mapping (ctf_file_t *src_fp, ctf_id_t src_type, ctf_file_t **dst_fp)
-{
- ctf_link_type_mapping_key_t key;
- ctf_file_t *target_fp = *dst_fp;
- ctf_id_t dst_type = 0;
+/* Add a file, memory buffer, or unopened file (by name) to a link.
- if (LCTF_TYPE_ISPARENT (src_fp, src_type) && src_fp->ctf_parent)
- src_fp = src_fp->ctf_parent;
+ You can call this with:
- src_type = LCTF_TYPE_TO_INDEX(src_fp, src_type);
- key.cltm_fp = src_fp;
- key.cltm_idx = src_type;
+ CTF and NAME: link the passed ctf_archive_t, with the given NAME.
+ NAME alone: open NAME as a CTF file when needed.
+ BUF and NAME: open the BUF (of length N) as CTF, with the given NAME. (Not
+ yet implemented.)
- if (target_fp->ctf_link_type_mapping)
- dst_type = (uintptr_t) ctf_dynhash_lookup (target_fp->ctf_link_type_mapping,
- &key);
+ Passed in CTF args are owned by the dictionary and will be freed by it.
+ The BUF arg is *not* owned by the dictionary, and the user should not free
+ its referent until the link is done.
- if (dst_type != 0)
- {
- dst_type = LCTF_INDEX_TO_TYPE (target_fp, dst_type,
- target_fp->ctf_parent != NULL);
- *dst_fp = target_fp;
- return dst_type;
- }
+ The order of calls to this function influences the order of types in the
+ final link output, but otherwise is not important.
- if (target_fp->ctf_parent)
- target_fp = target_fp->ctf_parent;
- else
- return 0;
+ Private for now, but may in time become public once support for BUF is
+ implemented. */
- if (target_fp->ctf_link_type_mapping)
- dst_type = (uintptr_t) ctf_dynhash_lookup (target_fp->ctf_link_type_mapping,
- &key);
+static int
+ctf_link_add (ctf_dict_t *fp, ctf_archive_t *ctf, const char *name,
+ void *buf _libctf_unused_, size_t n _libctf_unused_)
+{
+ if (buf)
+ return (ctf_set_errno (fp, ECTF_NOTYET));
+
+ if (!((ctf && name && !buf)
+ || (name && !buf && !ctf)
+ || (buf && name && !ctf)))
+ return (ctf_set_errno (fp, EINVAL));
+
+ /* We can only lazily open files if libctf.so is in use rather than
+ libctf-nobfd.so. This is a little tricky: in shared libraries, we can use
+ a weak symbol so that -lctf -lctf-nobfd works, but in static libraries we
+ must distinguish between the two libraries explicitly. */
+
+#if defined (PIC)
+ if (!buf && !ctf && name && !ctf_open)
+ return (ctf_set_errno (fp, ECTF_NEEDSBFD));
+#elif NOBFD
+ if (!buf && !ctf && name)
+ return (ctf_set_errno (fp, ECTF_NEEDSBFD));
+#endif
- if (dst_type)
- dst_type = LCTF_INDEX_TO_TYPE (target_fp, dst_type,
- target_fp->ctf_parent != NULL);
+ if (fp->ctf_link_outputs)
+ return (ctf_set_errno (fp, ECTF_LINKADDEDLATE));
+ if (fp->ctf_link_inputs == NULL)
+ fp->ctf_link_inputs = ctf_dynhash_create (ctf_hash_string,
+ ctf_hash_eq_string, free,
+ ctf_link_input_close);
+
+ if (fp->ctf_link_inputs == NULL)
+ return (ctf_set_errno (fp, ENOMEM));
- *dst_fp = target_fp;
- return dst_type;
+ return ctf_link_add_ctf_internal (fp, ctf, NULL, name);
}
-/* Linker machinery.
+/* Add an opened CTF archive or unopened file (by name) to a link.
+ If CTF is NULL and NAME is non-null, an unopened file is meant:
+ otherwise, the specified archive is assumed to have the given NAME.
- CTF linking consists of adding CTF archives full of content to be merged into
- this one to the current file (which must be writable) by calling
- ctf_link_add_ctf(). Once this is done, a call to ctf_link() will merge the
- type tables together, generating new CTF files as needed, with this one as a
- parent, to contain types from the inputs which conflict.
- ctf_link_add_strtab() takes a callback which provides string/offset pairs to
- be added to the external symbol table and deduplicated from all CTF string
- tables in the output link; ctf_link_shuffle_syms() takes a callback which
- provides symtab entries in ascending order, and shuffles the function and
- data sections to match; and ctf_link_write() emits a CTF file (if there are
- no conflicts requiring per-compilation-unit sub-CTF files) or CTF archives
- (otherwise) and returns it, suitable for addition in the .ctf section of the
- output. */
+ Passed in CTF args are owned by the dictionary and will be freed by it.
-/* Add a file to a link. */
+ The order of calls to this function influences the order of types in the
+ final link output, but otherwise is not important. */
-static void ctf_arc_close_thunk (void *arc)
+int
+ctf_link_add_ctf (ctf_dict_t *fp, ctf_archive_t *ctf, const char *name)
{
- ctf_arc_close ((ctf_archive_t *) arc);
+ return ctf_link_add (fp, ctf, name, NULL, 0);
}
-static void ctf_file_close_thunk (void *file)
-{
- ctf_file_close ((ctf_file_t *) file);
-}
+/* Lazily open a CTF archive for linking, if not already open.
-int
-ctf_link_add_ctf (ctf_file_t *fp, ctf_archive_t *ctf, const char *name)
+ Returns the number of files contained within the opened archive (0 for none),
+ or -1 on error, as usual. */
+static ssize_t
+ctf_link_lazy_open (ctf_dict_t *fp, ctf_link_input_t *input)
{
- char *dupname = NULL;
+ size_t count;
+ int err;
- if (fp->ctf_link_outputs)
- return (ctf_set_errno (fp, ECTF_LINKADDEDLATE));
- if (fp->ctf_link_inputs == NULL)
- fp->ctf_link_inputs = ctf_dynhash_create (ctf_hash_string,
- ctf_hash_eq_string, free,
- ctf_arc_close_thunk);
+ if (input->clin_arc)
+ return ctf_archive_count (input->clin_arc);
- if (fp->ctf_link_inputs == NULL)
- goto oom;
+ if (input->clin_fp)
+ return 1;
- if ((dupname = strdup (name)) == NULL)
- goto oom;
+ /* See ctf_link_add_ctf. */
+#if defined (PIC) || !NOBFD
+ input->clin_arc = ctf_open (input->clin_filename, NULL, &err);
+#else
+ ctf_err_warn (fp, 0, ECTF_NEEDSBFD, _("cannot open %s lazily"),
+ input->clin_filename);
+ ctf_set_errno (fp, ECTF_NEEDSBFD);
+ return -1;
+#endif
- if (ctf_dynhash_insert (fp->ctf_link_inputs, dupname, ctf) < 0)
- goto oom;
+ /* Having no CTF sections is not an error. We just don't need to do
+ anything. */
- return 0;
- oom:
- free (fp->ctf_link_inputs);
- fp->ctf_link_inputs = NULL;
- free (dupname);
- return (ctf_set_errno (fp, ENOMEM));
+ if (!input->clin_arc)
+ {
+ if (err == ECTF_NOCTFDATA)
+ return 0;
+
+ ctf_err_warn (fp, 0, err, _("opening CTF %s failed"),
+ input->clin_filename);
+ ctf_set_errno (fp, err);
+ return -1;
+ }
+
+ if ((count = ctf_archive_count (input->clin_arc)) == 0)
+ ctf_arc_close (input->clin_arc);
+
+ return (ssize_t) count;
}
/* Return a per-CU output CTF dictionary suitable for the given CU, creating and
interning it if need be. */
-static ctf_file_t *
-ctf_create_per_cu (ctf_file_t *fp, const char *filename, const char *cuname)
+_libctf_nonnull_((1,2))
+static ctf_dict_t *
+ctf_create_per_cu (ctf_dict_t *fp, const char *cu_name)
{
- ctf_file_t *cu_fp;
+ ctf_dict_t *cu_fp;
+ const char *ctf_name = NULL;
char *dynname = NULL;
- if ((cu_fp = ctf_dynhash_lookup (fp->ctf_link_outputs, filename)) == NULL)
+ /* First, check the mapping table and translate the per-CU name we use
+ accordingly. */
+
+ if (fp->ctf_link_in_cu_mapping)
+ {
+ if ((ctf_name = ctf_dynhash_lookup (fp->ctf_link_in_cu_mapping,
+ cu_name)) == NULL)
+ ctf_name = cu_name;
+ }
+
+ if (ctf_name == NULL)
+ ctf_name = cu_name;
+
+ if ((cu_fp = ctf_dynhash_lookup (fp->ctf_link_outputs, ctf_name)) == NULL)
{
int err;
if ((cu_fp = ctf_create (&err)) == NULL)
{
- ctf_dprintf ("Cannot create per-CU CTF archive for CU %s from "
- "input file %s: %s\n", cuname, filename,
- ctf_errmsg (err));
+ ctf_err_warn (fp, 0, err, _("cannot create per-CU CTF archive for "
+ "input CU %s"), cu_name);
ctf_set_errno (fp, err);
return NULL;
}
- if ((dynname = strdup (filename)) == NULL)
+ if ((dynname = strdup (ctf_name)) == NULL)
goto oom;
if (ctf_dynhash_insert (fp->ctf_link_outputs, dynname, cu_fp) < 0)
goto oom;
- ctf_import (cu_fp, fp);
- ctf_cuname_set (cu_fp, cuname);
+ ctf_import_unref (cu_fp, fp);
+ ctf_cuname_set (cu_fp, cu_name);
ctf_parent_name_set (cu_fp, _CTF_SECTION);
}
return cu_fp;
- oom:
- free (dynname);
- ctf_file_close (cu_fp);
- ctf_set_errno (fp, ENOMEM);
- return NULL;
+ oom:
+ free (dynname);
+ ctf_dict_close (cu_fp);
+ ctf_set_errno (fp, ENOMEM);
+ return NULL;
+}
+
+/* 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;
+
+ 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);
+
+ 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, ctf_unnamed_cuname (in_fp))) == 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)
+{
+ 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_dict_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, err, _("iteration error in deduplicating link "
+ "input freeing"));
+ 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.
+
+ Also, if we are not omitting the variable section, integrate all symbols from
+ the symtypetabs into the variable section too. (Duplication with the
+ symtypetab section in the output will be eliminated at serialization time.) */
+
+static int
+ctf_link_deduplicating_variables (ctf_dict_t *fp, ctf_dict_t **inputs,
+ size_t ninputs, int cu_mapped)
+{
+ size_t i;
+
+ for (i = 0; i < ninputs; i++)
+ {
+ ctf_next_t *it = NULL;
+ ctf_id_t type;
+ const char *name;
+
+ /* First the variables on the inputs. */
+
+ while ((type = ctf_variable_next (inputs[i], &it, &name)) != CTF_ERR)
+ {
+ if (ctf_link_one_variable (fp, inputs[i], name, type, cu_mapped) < 0)
+ {
+ ctf_next_destroy (it);
+ return -1; /* errno is set for us. */
+ }
+ }
+ if (ctf_errno (inputs[i]) != ECTF_NEXT_END)
+ return ctf_set_errno (fp, ctf_errno (inputs[i]));
+
+ /* Next the symbols. We integrate data symbols even though the compiler
+ is currently doing the same, to allow the compiler to stop in
+ future. */
+
+ while ((type = ctf_symbol_next (inputs[i], &it, &name, 0)) != CTF_ERR)
+ {
+ if (ctf_link_one_variable (fp, inputs[i], name, type, 1) < 0)
+ {
+ ctf_next_destroy (it);
+ return -1; /* errno is set for us. */
+ }
+ }
+ if (ctf_errno (inputs[i]) != ECTF_NEXT_END)
+ return ctf_set_errno (fp, ctf_errno (inputs[i]));
+
+ /* Finally the function symbols. */
+
+ while ((type = ctf_symbol_next (inputs[i], &it, &name, 1)) != CTF_ERR)
+ {
+ if (ctf_link_one_variable (fp, inputs[i], name, type, 1) < 0)
+ {
+ ctf_next_destroy (it);
+ return -1; /* errno is set for us. */
+ }
+ }
+ if (ctf_errno (inputs[i]) != ECTF_NEXT_END)
+ return ctf_set_errno (fp, ctf_errno (inputs[i]));
+ }
+ return 0;
+}
+
+/* Check for symbol conflicts during linking. Three possibilities: already
+ exists, conflicting, or nonexistent. We don't have a dvd structure we can
+ use as a flag like check_variable does, so we use a tristate return
+ value instead: -1: conflicting; 1: nonexistent: 0: already exists. */
+
+static int
+check_sym (ctf_dict_t *fp, const char *name, ctf_id_t type, int functions)
+{
+ ctf_dynhash_t *thishash = functions ? fp->ctf_funchash : fp->ctf_objthash;
+ ctf_dynhash_t *thathash = functions ? fp->ctf_objthash : fp->ctf_funchash;
+ void *value;
+
+ /* Wrong type (function when object is wanted, etc). */
+ if (ctf_dynhash_lookup_kv (thathash, name, NULL, NULL))
+ return -1;
+
+ /* Not present at all yet. */
+ if (!ctf_dynhash_lookup_kv (thishash, name, NULL, &value))
+ return 1;
+
+ /* Already present. */
+ if ((ctf_id_t) (uintptr_t) value == type)
+ return 0;
+
+ /* Wrong type. */
+ return -1;
+}
+
+/* Do a deduplicating link of one symtypetab (function info or data object) in
+ one input dict. */
+
+static int
+ctf_link_deduplicating_one_symtypetab (ctf_dict_t *fp, ctf_dict_t *input,
+ int cu_mapped, int functions)
+{
+ ctf_next_t *it = NULL;
+ const char *name;
+ ctf_id_t type;
+
+ while ((type = ctf_symbol_next (input, &it, &name, functions)) != CTF_ERR)
+ {
+ ctf_id_t dst_type;
+ ctf_dict_t *per_cu_out_fp;
+ int sym;
+
+ /* Look in the parent first. */
+
+ if ((dst_type = ctf_dedup_type_mapping (fp, input, 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. */
+
+ sym = check_sym (fp, name, dst_type, functions);
+
+ /* Already present: next symbol. */
+ if (sym == 0)
+ continue;
+ /* Not present: add it. */
+ else if (sym > 0)
+ {
+ if (ctf_add_funcobjt_sym (fp, functions,
+ name, dst_type) < 0)
+ return -1; /* errno is set for us. */
+ continue;
+ }
+ }
+
+ /* Can't add to the parent due to a name clash (most unlikely), 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 ("Symbol %s in input file %s depends on a type %lx "
+ "hidden due to conflicts: skipped.\n", name,
+ ctf_unnamed_cuname (input), type);
+ continue;
+ }
+
+ if ((per_cu_out_fp = ctf_create_per_cu (fp, ctf_unnamed_cuname (input))) == 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,
+ input, type)) == CTF_ERR)
+ return -1; /* errno is set for us. */
+
+ if (dst_type == 0)
+ {
+ ctf_err_warn (fp, 1, 0,
+ _("type %lx for symbol %s in input file %s "
+ "not found: skipped"), type, name,
+ ctf_unnamed_cuname (input));
+ continue;
+ }
+ }
+
+ sym = check_sym (per_cu_out_fp, name, dst_type, functions);
+
+ /* Already present: next symbol. */
+ if (sym == 0)
+ continue;
+ /* Not present: add it. */
+ else if (sym > 0)
+ {
+ if (ctf_add_funcobjt_sym (per_cu_out_fp, functions,
+ name, dst_type) < 0)
+ return -1; /* errno is set for us. */
+ }
+ else
+ {
+ /* Perhaps this should be an assertion failure. */
+ ctf_err_warn (fp, 0, ECTF_DUPLICATE,
+ _("symbol %s in input file %s found conflicting "
+ "even when trying in per-CU dict."), name,
+ ctf_unnamed_cuname (input));
+ return (ctf_set_errno (fp, ECTF_DUPLICATE));
+ }
+ }
+ if (ctf_errno (input) != ECTF_NEXT_END)
+ {
+ ctf_set_errno (fp, ctf_errno (input));
+ ctf_err_warn (fp, 0, ctf_errno (input),
+ functions ? _("iterating over function symbols") :
+ _("iterating over data symbols"));
+ return -1;
+ }
+
+ return 0;
}
-typedef struct ctf_link_in_member_cb_arg
+/* Do a deduplicating link of the function info and data objects
+ in the inputs. */
+static int
+ctf_link_deduplicating_syms (ctf_dict_t *fp, ctf_dict_t **inputs,
+ size_t ninputs, int cu_mapped)
{
- ctf_file_t *out_fp;
- const char *file_name;
- ctf_file_t *in_fp;
- ctf_file_t *main_input_fp;
- const char *cu_name;
- char *arcname;
- int done_main_member;
- int share_mode;
- int in_input_cu_file;
-} ctf_link_in_member_cb_arg_t;
+ size_t i;
+
+ for (i = 0; i < ninputs; i++)
+ {
+ if (ctf_link_deduplicating_one_symtypetab (fp, inputs[i],
+ cu_mapped, 0) < 0)
+ return -1; /* errno is set for us. */
+
+ if (ctf_link_deduplicating_one_symtypetab (fp, inputs[i],
+ cu_mapped, 1) < 0)
+ return -1; /* errno is set for us. */
+ }
-/* Link one type into the link. We rely on ctf_add_type() to detect
- duplicates. This is not terribly reliable yet (unnmamed types will be
- mindlessly duplicated), but will improve shortly. */
+ return 0;
+}
+/* Do the per-CU part of a deduplicating link. */
static int
-ctf_link_one_type (ctf_id_t type, int isroot _libctf_unused_, void *arg_)
+ctf_link_deduplicating_per_cu (ctf_dict_t *fp)
{
- ctf_link_in_member_cb_arg_t *arg = (ctf_link_in_member_cb_arg_t *) arg_;
- ctf_file_t *per_cu_out_fp;
+ ctf_next_t *i = NULL;
int err;
+ void *out_cu;
+ void *in_cus;
- if (arg->share_mode != CTF_LINK_SHARE_UNCONFLICTED)
- {
- ctf_dprintf ("Share-duplicated mode not yet implemented.\n");
- return ctf_set_errno (arg->out_fp, ECTF_NOTYET);
- }
-
- /* Simply call ctf_add_type: if it reports a conflict and we're adding to the
- main CTF file, add to the per-CU archive member instead, creating it if
- necessary. If we got this type from a per-CU archive member, add it
- straight back to the corresponding member in the output. */
+ /* 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. */
- if (!arg->in_input_cu_file)
+ while ((err = ctf_dynhash_next (fp->ctf_link_out_cu_mapping, &i, &out_cu,
+ &in_cus)) == 0)
{
- if (ctf_add_type (arg->out_fp, arg->in_fp, type) != CTF_ERR)
- return 0;
-
- err = ctf_errno (arg->out_fp);
- if (err != ECTF_CONFLICT)
+ const char *out_name = (const char *) out_cu;
+ ctf_dynhash_t *in = (ctf_dynhash_t *) in_cus;
+ ctf_dict_t *out = NULL;
+ ctf_dict_t **inputs;
+ ctf_dict_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_dprintf ("Cannot link type %lx from archive member %s, input file %s "
- "into output link: %s\n", type, arg->arcname, arg->file_name,
- ctf_errmsg (err));
- return -1;
+ ctf_err_warn (fp, 0, EFBIG, _("too many inputs in deduplicating "
+ "link: %li"), (long int) ninputs);
+ ctf_set_errno (fp, EFBIG);
+ goto err_open_inputs;
}
- ctf_set_errno (arg->out_fp, 0);
- }
- if ((per_cu_out_fp = ctf_create_per_cu (arg->out_fp, arg->arcname,
- arg->cu_name)) == NULL)
- return -1; /* Errno is set for us. */
-
- if (ctf_add_type (per_cu_out_fp, arg->in_fp, type) != CTF_ERR)
- return 0;
+ /* 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, err, _("cannot open archive %s in "
+ "CU-mapped CTF link"),
+ only_input->clin_filename);
+ ctf_set_errno (fp, err);
+ goto err_open_inputs;
+ }
+ ctf_next_destroy (ai);
- err = ctf_errno (per_cu_out_fp);
- if (err == ECTF_CONFLICT)
- /* Conflicts are possible at this stage only if a non-ld user has combined
- multiple TUs into a single output dictionary. Even in this case we do not
- want to stop the link or propagate the error. */
- ctf_set_errno (arg->out_fp, 0);
+ 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, 0, _("cannot add intermediate files "
+ "to link"));
+ 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;
+ }
- return 0; /* As above: do not lose types. */
-}
+ /* 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. */
-/* Check if we can safely add a variable with the given type to this container. */
+ if ((inputs = ctf_link_deduplicating_open_inputs (fp, in, ninputs,
+ &parents)) == NULL)
+ {
+ ctf_next_destroy (i);
+ goto err_inputs;
+ }
-static int
-check_variable (const char *name, ctf_file_t *fp, ctf_id_t type,
- ctf_dvdef_t **out_dvd)
-{
- ctf_dvdef_t *dvd;
+ if ((out = ctf_create (&err)) == NULL)
+ {
+ ctf_err_warn (fp, 0, err, _("cannot create per-CU CTF archive "
+ "for %s"),
+ out_name);
+ ctf_set_errno (fp, err);
+ goto err_inputs;
+ }
- dvd = ctf_dynhash_lookup (fp->ctf_dvhash, name);
- *out_dvd = dvd;
- if (!dvd)
- return 1;
+ /* Share the atoms table to reduce memory usage. */
+ out->ctf_dedup_atoms = fp->ctf_dedup_atoms_alloc;
- if (dvd->dvd_type != type)
- {
- /* Variable here. Wrong type: cannot add. Just skip it, because there is
- no way to express this in CTF. (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);
- }
+ /* 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);
- return 0; /* Already exists. */
-}
+ if (ctf_dedup (out, inputs, ninputs, parents, 1) < 0)
+ {
+ ctf_set_errno (fp, ctf_errno (out));
+ ctf_err_warn (fp, 0, 0, _("CU-mapped deduplication failed for %s"),
+ out_name);
+ goto err_inputs;
+ }
-/* Link one variable in. */
+ if ((outputs = ctf_dedup_emit (out, inputs, ninputs, parents,
+ &noutputs, 1)) == NULL)
+ {
+ ctf_set_errno (fp, ctf_errno (out));
+ ctf_err_warn (fp, 0, 0, _("CU-mapped deduplicating link type emission "
+ "failed for %s"), out_name);
+ goto err_inputs;
+ }
+ if (!ctf_assert (fp, noutputs == 1))
+ {
+ size_t j;
+ for (j = 1; j < noutputs; j++)
+ ctf_dict_close (outputs[j]);
+ goto err_inputs_outputs;
+ }
-static int
-ctf_link_one_variable (const char *name, ctf_id_t type, void *arg_)
-{
- ctf_link_in_member_cb_arg_t *arg = (ctf_link_in_member_cb_arg_t *) arg_;
- ctf_file_t *per_cu_out_fp;
- ctf_id_t dst_type = 0;
- ctf_file_t *check_fp;
- ctf_dvdef_t *dvd;
+ if (!(fp->ctf_link_flags & CTF_LINK_OMIT_VARIABLES_SECTION)
+ && ctf_link_deduplicating_variables (out, inputs, ninputs, 1) < 0)
+ {
+ ctf_set_errno (fp, ctf_errno (out));
+ ctf_err_warn (fp, 0, 0, _("CU-mapped deduplicating link variable "
+ "emission failed for %s"), out_name);
+ goto err_inputs_outputs;
+ }
- /* In unconflicted link mode, if this type is mapped to a type in the parent
- container, 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. */
+ ctf_dedup_fini (out, outputs, noutputs);
- check_fp = arg->out_fp;
+ /* For now, we omit symbol section linking for CU-mapped links, until it
+ is clear how to unify the symbol table across such links. (Perhaps we
+ should emit an unconditionally indexed symtab, like the compiler
+ does.) */
- dst_type = ctf_type_mapping (arg->in_fp, type, &check_fp);
- if (dst_type != 0)
- {
- if (check_fp == arg->out_fp)
+ if (ctf_link_deduplicating_close_inputs (fp, in, inputs, ninputs) < 0)
{
- if (check_variable (name, check_fp, dst_type, &dvd))
- {
- /* No variable here: we can add it. */
- if (ctf_add_variable (check_fp, name, dst_type) < 0)
- return (ctf_set_errno (arg->out_fp, ctf_errno (check_fp)));
- return 0;
- }
-
- /* Already present? Nothing to do. */
- if (dvd && dvd->dvd_type == type)
- return 0;
+ free (inputs);
+ free (parents);
+ goto err_outputs;
}
- }
+ free (inputs);
+ free (parents);
- /* 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. */
+ /* 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);
- if ((per_cu_out_fp = ctf_create_per_cu (arg->out_fp, arg->arcname,
- arg->cu_name)) == NULL)
- return -1; /* Errno is set for us. */
+ /* 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 the type was not found, check for it in the child too. */
- if (dst_type == 0)
- {
- check_fp = per_cu_out_fp;
- dst_type = ctf_type_mapping (arg->in_fp, type, &check_fp);
+ 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 (dst_type == 0)
+ if (ctf_link_add_ctf_internal (fp, in_arc, NULL,
+ ctf_cuname (outputs[0])) < 0)
{
- ctf_dprintf ("Type %lx for variable %s in input file %s not "
- "found: skipped.\n", type, name, arg->file_name);
- /* Do not terminate the link: just skip the variable. */
- return 0;
+ ctf_err_warn (fp, 0, 0, _("cannot add intermediate files to link"));
+ goto err_outputs;
}
+
+ ctf_dict_close (out);
+ free (outputs);
+ continue;
+
+ err_inputs_outputs:
+ ctf_list_splice (&fp->ctf_errs_warnings, &outputs[0]->ctf_errs_warnings);
+ ctf_dict_close (outputs[0]);
+ free (outputs);
+ err_inputs:
+ ctf_link_deduplicating_close_inputs (fp, in, inputs, ninputs);
+ ctf_dict_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_dict_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, err, _("iteration error in CU-mapped deduplicating "
+ "link"));
+ return ctf_set_errno (fp, err);
}
- 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 (arg->out_fp, ctf_errno (per_cu_out_fp)));
return 0;
}
-/* Merge every type and variable in this archive member into the link, so we can
- relink things that have already had ld run on them. We use the archive
- member name, sans any leading '.ctf.', as the CU name for ambiguous types if
- there is one and it's not the default: otherwise, we use the name of the
- input file. */
-static int
-ctf_link_one_input_archive_member (ctf_file_t *in_fp, const char *name, void *arg_)
+/* Do a deduplicating link using the ctf-dedup machinery. */
+static void
+ctf_link_deduplicating (ctf_dict_t *fp)
{
- ctf_link_in_member_cb_arg_t *arg = (ctf_link_in_member_cb_arg_t *) arg_;
- int err = 0;
+ size_t i;
+ ctf_dict_t **inputs, **outputs = NULL;
+ ssize_t ninputs;
+ uint32_t noutputs;
+ uint32_t *parents;
- if (strcmp (name, _CTF_SECTION) == 0)
+ if (ctf_dedup_atoms_init (fp) < 0)
{
- /* This file is the default member of this archive, and has already been
- explicitly processed.
+ ctf_err_warn (fp, 0, 0, _("allocating CTF dedup atoms table"));
+ return; /* Errno is set for us. */
+ }
- In the default sharing mode of CTF_LINK_SHARE_UNCONFLICTED, it does no
- harm to rescan an existing shared repo again: all the types will just
- end up in the same place. But in CTF_LINK_SHARE_DUPLICATED mode, this
- causes the system to erroneously conclude that all types are duplicated
- and should be shared, even if they are not. */
+ if (fp->ctf_link_out_cu_mapping
+ && (ctf_link_deduplicating_per_cu (fp) < 0))
+ return; /* Errno is set for us. */
- if (arg->done_main_member)
- return 0;
- arg->arcname = strdup (".ctf.");
- if (arg->arcname)
- {
- char *new_name;
+ if ((ninputs = ctf_link_deduplicating_count_inputs (fp, NULL, NULL)) < 0)
+ return; /* Errno is set for us. */
- new_name = ctf_str_append (arg->arcname, arg->file_name);
- if (new_name)
- arg->arcname = new_name;
- else
- free (arg->arcname);
- }
- }
- else
- {
- arg->arcname = strdup (name);
+ if ((inputs = ctf_link_deduplicating_open_inputs (fp, NULL, ninputs,
+ &parents)) == NULL)
+ return; /* Errno is set for us. */
- /* Get ambiguous types from our parent. */
- ctf_import (in_fp, arg->main_input_fp);
- arg->in_input_cu_file = 1;
- }
+ if (ninputs == 1 && ctf_cuname (inputs[0]) != NULL)
+ ctf_cuname_set (fp, ctf_cuname (inputs[0]));
- if (!arg->arcname)
- return ctf_set_errno (in_fp, ENOMEM);
+ if (ctf_dedup (fp, inputs, ninputs, parents, 0) < 0)
+ {
+ ctf_err_warn (fp, 0, 0, _("deduplication failed for %s"),
+ ctf_link_input_name (fp));
+ goto err;
+ }
- arg->cu_name = name;
- if (strncmp (arg->cu_name, ".ctf.", strlen (".ctf.")) == 0)
- arg->cu_name += strlen (".ctf.");
- arg->in_fp = in_fp;
+ if ((outputs = ctf_dedup_emit (fp, inputs, ninputs, parents, &noutputs,
+ 0)) == NULL)
+ {
+ ctf_err_warn (fp, 0, 0, _("deduplicating link type emission failed "
+ "for %s"), ctf_link_input_name (fp));
+ goto err;
+ }
- if ((err = ctf_type_iter_all (in_fp, ctf_link_one_type, arg)) > -1)
- err = ctf_variable_iter (in_fp, ctf_link_one_variable, arg);
+ if (!ctf_assert (fp, outputs[0] == fp))
+ {
+ for (i = 1; i < noutputs; i++)
+ ctf_dict_close (outputs[i]);
+ goto err;
+ }
- arg->in_input_cu_file = 0;
- free (arg->arcname);
+ for (i = 0; i < noutputs; i++)
+ {
+ char *dynname;
- if (err < 0)
- return -1; /* Errno is set for us. */
+ /* We already have access to this one. Close the duplicate. */
+ if (i == 0)
+ {
+ ctf_dict_close (outputs[0]);
+ continue;
+ }
- return 0;
-}
+ if ((dynname = strdup (ctf_cuname (outputs[i]))) == NULL)
+ goto oom_one_output;
-/* Dump the unnecessary link type mapping after one input file is processed. */
-static void
-empty_link_type_mapping (void *key _libctf_unused_, void *value,
- void *arg _libctf_unused_)
-{
- ctf_file_t *fp = (ctf_file_t *) value;
+ if (ctf_dynhash_insert (fp->ctf_link_outputs, dynname, outputs[i]) < 0)
+ goto oom_one_output;
- if (fp->ctf_link_type_mapping)
- ctf_dynhash_empty (fp->ctf_link_type_mapping);
-}
+ continue;
-/* Link one input file's types into the output file. */
-static void
-ctf_link_one_input_archive (void *key, void *value, void *arg_)
-{
- const char *file_name = (const char *) key;
- ctf_archive_t *arc = (ctf_archive_t *) value;
- ctf_link_in_member_cb_arg_t *arg = (ctf_link_in_member_cb_arg_t *) arg_;
- int err;
+ oom_one_output:
+ ctf_set_errno (fp, ENOMEM);
+ ctf_err_warn (fp, 0, 0, _("out of memory allocating link outputs"));
+ free (dynname);
- arg->file_name = file_name;
- arg->done_main_member = 0;
- if ((arg->main_input_fp = ctf_arc_open_by_name (arc, NULL, &err)) == NULL)
- if (err != ECTF_ARNNAME)
- {
- ctf_dprintf ("Cannot open main archive member in input file %s in the "
- "link: skipping: %s.\n", arg->file_name,
- ctf_errmsg (err));
- return;
- }
+ for (; i < noutputs; i++)
+ ctf_dict_close (outputs[i]);
+ goto err;
+ }
- if (ctf_link_one_input_archive_member (arg->main_input_fp,
- _CTF_SECTION, arg) < 0)
+ if (!(fp->ctf_link_flags & CTF_LINK_OMIT_VARIABLES_SECTION)
+ && ctf_link_deduplicating_variables (fp, inputs, ninputs, 0) < 0)
{
- ctf_file_close (arg->main_input_fp);
- return;
+ ctf_err_warn (fp, 0, 0, _("deduplicating link variable emission failed for "
+ "%s"), ctf_link_input_name (fp));
+ goto err_clean_outputs;
}
- arg->done_main_member = 1;
- if (ctf_archive_iter (arc, ctf_link_one_input_archive_member, arg) < 0)
- ctf_dprintf ("Cannot traverse archive in input file %s: link "
- "cannot continue: %s.\n", arg->file_name,
- ctf_errmsg (ctf_errno (arg->out_fp)));
- else
+
+ if (ctf_link_deduplicating_syms (fp, inputs, ninputs, 0) < 0)
{
- /* The only error indication to the caller is the errno: so ensure that it
- is zero if there was no actual error from the caller. */
- ctf_set_errno (arg->out_fp, 0);
+ ctf_err_warn (fp, 0, 0, _("deduplicating link symbol emission failed for "
+ "%s"), ctf_link_input_name (fp));
+ goto err_clean_outputs;
}
- ctf_file_close (arg->main_input_fp);
- /* Discard the now-unnecessary mapping table data. */
- if (arg->out_fp->ctf_link_type_mapping)
- ctf_dynhash_empty (arg->out_fp->ctf_link_type_mapping);
- ctf_dynhash_iter (arg->out_fp->ctf_link_outputs, empty_link_type_mapping, NULL);
+ ctf_dedup_fini (fp, outputs, noutputs);
+
+ /* 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_dict_close (inputs[i]);
+ free (inputs);
+ free (parents);
+ free (outputs);
+ return;
+
+ err_clean_outputs:
+ for (i = 1; i < noutputs; i++)
+ {
+ ctf_dynhash_remove (fp->ctf_link_outputs, ctf_cuname (outputs[i]));
+ ctf_dict_close (outputs[i]);
+ }
+ goto err;
}
-/* Merge types and variable sections in all files added to the link
- together. */
+/* Merge types and variable sections in all dicts added to the link together.
+ All the added dicts are closed. */
int
-ctf_link (ctf_file_t *fp, int share_mode)
+ctf_link (ctf_dict_t *fp, int flags)
{
- ctf_link_in_member_cb_arg_t arg;
+ ctf_next_t *i = NULL;
+ int err;
- memset (&arg, 0, sizeof (struct ctf_link_in_member_cb_arg));
- arg.out_fp = fp;
- arg.share_mode = share_mode;
+ fp->ctf_link_flags = flags;
if (fp->ctf_link_inputs == NULL)
return 0; /* Nothing to do. */
if (fp->ctf_link_outputs == NULL)
fp->ctf_link_outputs = ctf_dynhash_create (ctf_hash_string,
ctf_hash_eq_string, free,
- ctf_file_close_thunk);
+ (ctf_hash_free_fun)
+ ctf_dict_close);
if (fp->ctf_link_outputs == NULL)
return ctf_set_errno (fp, ENOMEM);
- ctf_dynhash_iter (fp->ctf_link_inputs, ctf_link_one_input_archive,
- &arg);
+ /* Create empty CUs if requested. We do not currently claim that multiple
+ links in succession with CTF_LINK_EMPTY_CU_MAPPINGS set in some calls and
+ not set in others will do anything especially sensible. */
+
+ fp->ctf_flags |= LCTF_LINKING;
+ if (fp->ctf_link_out_cu_mapping && (flags & CTF_LINK_EMPTY_CU_MAPPINGS))
+ {
+ void *v;
+
+ while ((err = ctf_dynhash_next (fp->ctf_link_out_cu_mapping, &i, &v,
+ NULL)) == 0)
+ {
+ const char *to = (const char *) v;
+ if (ctf_create_per_cu (fp, to) == NULL)
+ {
+ fp->ctf_flags &= ~LCTF_LINKING;
+ ctf_next_destroy (i);
+ return -1; /* Errno is set for us. */
+ }
+ }
+ if (err != ECTF_NEXT_END)
+ {
+ fp->ctf_flags &= ~LCTF_LINKING;
+ ctf_err_warn (fp, 1, err, _("iteration error creating empty CUs"));
+ ctf_set_errno (fp, err);
+ return -1;
+ }
+ }
+
+ ctf_link_deduplicating (fp);
- if (ctf_errno (fp) != 0)
+ fp->ctf_flags &= ~LCTF_LINKING;
+ if ((ctf_errno (fp) != 0) && (ctf_errno (fp) != ECTF_NOCTFDATA))
return -1;
return 0;
}
ctf_link_intern_extern_string (void *key _libctf_unused_, void *value,
void *arg_)
{
- ctf_file_t *fp = (ctf_file_t *) value;
+ ctf_dict_t *fp = (ctf_dict_t *) value;
ctf_link_out_string_cb_arg_t *arg = (ctf_link_out_string_cb_arg_t *) arg_;
fp->ctf_flags |= LCTF_DIRTY;
- if (ctf_str_add_external (fp, arg->str, arg->offset) == NULL)
+ if (!ctf_str_add_external (fp, arg->str, arg->offset))
arg->err = ENOMEM;
}
/* Repeatedly call ADD_STRING to acquire strings from the external string table,
adding them to the atoms table for this CU and all subsidiary CUs.
- If ctf_link() is also called, it must be called first if you want the new CTF
- files ctf_link() can create to get their strings dedupped against the ELF
+ If ctf_link is also called, it must be called first if you want the new CTF
+ files ctf_link can create to get their strings dedupped against the ELF
strtab properly. */
int
-ctf_link_add_strtab (ctf_file_t *fp, ctf_link_strtab_string_f *add_string,
+ctf_link_add_strtab (ctf_dict_t *fp, ctf_link_strtab_string_f *add_string,
void *arg)
{
const char *str;
ctf_link_out_string_cb_arg_t iter_arg = { str, offset, 0 };
fp->ctf_flags |= LCTF_DIRTY;
- if (ctf_str_add_external (fp, str, offset) == NULL)
+ if (!ctf_str_add_external (fp, str, offset))
err = ENOMEM;
ctf_dynhash_iter (fp->ctf_link_outputs, ctf_link_intern_extern_string,
err = iter_arg.err;
}
+ if (err)
+ ctf_set_errno (fp, err);
+
return -err;
}
-/* Not yet implemented. */
+/* Inform the ctf-link machinery of a new symbol in the target symbol table
+ (which must be some symtab that is not usually stripped, and which
+ is in agreement with ctf_bfdopen_ctfsect). May be called either before or
+ after ctf_link_add_strtab. */
+int
+ctf_link_add_linker_symbol (ctf_dict_t *fp, ctf_link_sym_t *sym)
+{
+ ctf_in_flight_dynsym_t *cid;
+
+ /* Cheat a little: if there is already an ENOMEM error code recorded against
+ this dict, we shouldn't even try to add symbols because there will be no
+ memory to do so: probably we failed to add some previous symbol. This
+ makes out-of-memory exits 'sticky' across calls to this function, so the
+ caller doesn't need to worry about error conditions. */
+
+ if (ctf_errno (fp) == ENOMEM)
+ return -ENOMEM; /* errno is set for us. */
+
+ if (ctf_symtab_skippable (sym))
+ return 0;
+
+ if (sym->st_type != STT_OBJECT && sym->st_type != STT_FUNC)
+ return 0;
+
+ /* Add the symbol to the in-flight list. */
+
+ if ((cid = malloc (sizeof (ctf_in_flight_dynsym_t))) == NULL)
+ goto oom;
+
+ cid->cid_sym = *sym;
+ ctf_list_append (&fp->ctf_in_flight_dynsyms, cid);
+
+ return 0;
+
+ oom:
+ ctf_dynhash_destroy (fp->ctf_dynsyms);
+ fp->ctf_dynsyms = NULL;
+ ctf_set_errno (fp, ENOMEM);
+ return -ENOMEM;
+}
+
+/* Impose an ordering on symbols. The ordering takes effect immediately, but
+ since the ordering info does not include type IDs, lookups may return nothing
+ until such IDs are added by calls to ctf_add_*_sym. Must be called after
+ ctf_link_add_strtab and ctf_link_add_linker_symbol. */
int
-ctf_link_shuffle_syms (ctf_file_t *fp _libctf_unused_,
- ctf_link_iter_symbol_f *add_sym _libctf_unused_,
- void *arg _libctf_unused_)
+ctf_link_shuffle_syms (ctf_dict_t *fp)
{
+ ctf_in_flight_dynsym_t *did, *nid;
+ ctf_next_t *i = NULL;
+ int err = ENOMEM;
+ void *name_, *sym_;
+
+ if (!fp->ctf_dynsyms)
+ {
+ fp->ctf_dynsyms = ctf_dynhash_create (ctf_hash_string,
+ ctf_hash_eq_string,
+ NULL, free);
+ if (!fp->ctf_dynsyms)
+ {
+ ctf_set_errno (fp, ENOMEM);
+ return -ENOMEM;
+ }
+ }
+
+ /* Add all the symbols, excluding only those we already know are prohibited
+ from appearing in symtypetabs. */
+
+ for (did = ctf_list_next (&fp->ctf_in_flight_dynsyms); did != NULL; did = nid)
+ {
+ ctf_link_sym_t *new_sym;
+
+ nid = ctf_list_next (did);
+ ctf_list_delete (&fp->ctf_in_flight_dynsyms, did);
+
+ /* We might get a name or an external strtab offset. The strtab offset is
+ guaranteed resolvable at this point, so turn it into a string. */
+
+ if (did->cid_sym.st_name == NULL)
+ {
+ uint32_t off = CTF_SET_STID (did->cid_sym.st_nameidx, CTF_STRTAB_1);
+
+ did->cid_sym.st_name = ctf_strraw (fp, off);
+ did->cid_sym.st_nameidx_set = 0;
+ if (!ctf_assert (fp, did->cid_sym.st_name != NULL))
+ return -ECTF_INTERNAL; /* errno is set for us. */
+ }
+
+ /* The symbol might have turned out to be nameless, so we have to recheck
+ for skippability here. */
+ if (!ctf_symtab_skippable (&did->cid_sym))
+ {
+ ctf_dprintf ("symbol from linker: %s (%x)\n", did->cid_sym.st_name,
+ did->cid_sym.st_symidx);
+
+ if ((new_sym = malloc (sizeof (ctf_link_sym_t))) == NULL)
+ goto local_oom;
+
+ memcpy (new_sym, &did->cid_sym, sizeof (ctf_link_sym_t));
+ if (ctf_dynhash_cinsert (fp->ctf_dynsyms, new_sym->st_name, new_sym) < 0)
+ goto local_oom;
+
+ if (fp->ctf_dynsymmax < new_sym->st_symidx)
+ fp->ctf_dynsymmax = new_sym->st_symidx;
+ }
+
+ free (did);
+ continue;
+
+ local_oom:
+ free (did);
+ free (new_sym);
+ goto err;
+ }
+
+ /* If no symbols are reported, unwind what we have done and return. This
+ makes it a bit easier for the serializer to tell that no symbols have been
+ reported and that it should look elsewhere for reported symbols. */
+ if (!ctf_dynhash_elements (fp->ctf_dynsyms))
+ {
+ ctf_dprintf ("No symbols: not a final link.\n");
+ ctf_dynhash_destroy (fp->ctf_dynsyms);
+ fp->ctf_dynsyms = NULL;
+ return 0;
+ }
+
+ /* Construct a mapping from shndx to the symbol info. */
+ free (fp->ctf_dynsymidx);
+ if ((fp->ctf_dynsymidx = calloc (fp->ctf_dynsymmax + 1,
+ sizeof (ctf_link_sym_t *))) == NULL)
+ goto err;
+
+ while ((err = ctf_dynhash_next (fp->ctf_dynsyms, &i, &name_, &sym_)) == 0)
+ {
+ const char *name = (const char *) name;
+ ctf_link_sym_t *symp = (ctf_link_sym_t *) sym_;
+
+ if (!ctf_assert (fp, symp->st_symidx <= fp->ctf_dynsymmax))
+ {
+ ctf_next_destroy (i);
+ err = ctf_errno (fp);
+ goto err;
+ }
+ fp->ctf_dynsymidx[symp->st_symidx] = symp;
+ }
+ if (err != ECTF_NEXT_END)
+ {
+ ctf_err_warn (fp, 0, err, _("error iterating over shuffled symbols"));
+ goto err;
+ }
return 0;
+
+ err:
+ /* Leave the in-flight symbols around: they'll be freed at
+ dict close time regardless. */
+ ctf_dynhash_destroy (fp->ctf_dynsyms);
+ fp->ctf_dynsyms = NULL;
+ free (fp->ctf_dynsymidx);
+ fp->ctf_dynsymidx = NULL;
+ fp->ctf_dynsymmax = 0;
+ ctf_set_errno (fp, err);
+ return -err;
}
typedef struct ctf_name_list_accum_cb_arg
{
char **names;
- ctf_file_t *fp;
- ctf_file_t **files;
+ ctf_dict_t *fp;
+ ctf_dict_t **files;
size_t i;
+ char **dynames;
+ size_t ndynames;
} ctf_name_list_accum_cb_arg_t;
-/* Accumulate the names and a count of the names in the link output hash,
- and run ctf_update() on them to generate them. */
+/* Accumulate the names and a count of the names in the link output hash. */
static void
ctf_accumulate_archive_names (void *key, void *value, void *arg_)
{
const char *name = (const char *) key;
- ctf_file_t *fp = (ctf_file_t *) value;
+ ctf_dict_t *fp = (ctf_dict_t *) value;
char **names;
- ctf_file_t **files;
+ ctf_dict_t **files;
ctf_name_list_accum_cb_arg_t *arg = (ctf_name_list_accum_cb_arg_t *) arg_;
- int err;
-
- if ((err = ctf_update (fp)) < 0)
- {
- ctf_set_errno (arg->fp, ctf_errno (fp));
- return;
- }
if ((names = realloc (arg->names, sizeof (char *) * ++(arg->i))) == NULL)
{
return;
}
- if ((files = realloc (arg->files, sizeof (ctf_file_t *) * arg->i)) == NULL)
+ if ((files = realloc (arg->files, sizeof (ctf_dict_t *) * arg->i)) == NULL)
{
(arg->i)--;
ctf_set_errno (arg->fp, ENOMEM);
return;
}
+
+ /* Allow the caller to get in and modify the name at the last minute. If the
+ caller *does* modify the name, we have to stash away the new name the
+ caller returned so we can free it later on. (The original name is the key
+ of the ctf_link_outputs hash and is freed by the dynhash machinery.) */
+
+ if (fp->ctf_link_memb_name_changer)
+ {
+ char **dynames;
+ char *dyname;
+ void *nc_arg = fp->ctf_link_memb_name_changer_arg;
+
+ dyname = fp->ctf_link_memb_name_changer (fp, name, nc_arg);
+
+ if (dyname != NULL)
+ {
+ if ((dynames = realloc (arg->dynames,
+ sizeof (char *) * ++(arg->ndynames))) == NULL)
+ {
+ (arg->ndynames)--;
+ ctf_set_errno (arg->fp, ENOMEM);
+ return;
+ }
+ arg->dynames = dynames;
+ name = (const char *) dyname;
+ }
+ }
+
arg->names = names;
arg->names[(arg->i) - 1] = (char *) name;
arg->files = files;
arg->files[(arg->i) - 1] = fp;
}
+/* Change the name of the parent CTF section, if the name transformer has got to
+ it. */
+static void
+ctf_change_parent_name (void *key _libctf_unused_, void *value, void *arg)
+{
+ ctf_dict_t *fp = (ctf_dict_t *) value;
+ const char *name = (const char *) arg;
+
+ ctf_parent_name_set (fp, name);
+}
+
+/* Warn if we may suffer information loss because the CTF input files are too
+ old. Usually we provide complete backward compatibility, but compiler
+ changes etc which never hit a release may have a flag in the header that
+ simply prevents those changes from being used. */
+static void
+ctf_link_warn_outdated_inputs (ctf_dict_t *fp)
+{
+ ctf_next_t *i = NULL;
+ void *name_;
+ void *ifp_;
+ int err;
+
+ while ((err = ctf_dynhash_next (fp->ctf_link_inputs, &i, &name_, &ifp_)) == 0)
+ {
+ const char *name = (const char *) name_;
+ ctf_dict_t *ifp = (ctf_dict_t *) ifp_;
+
+ if (!(ifp->ctf_header->cth_flags & CTF_F_NEWFUNCINFO)
+ && (ifp->ctf_header->cth_varoff - ifp->ctf_header->cth_funcoff) > 0)
+ ctf_err_warn (ifp, 1, 0, _("linker input %s has CTF func info but uses "
+ "an old, unreleased func info format: "
+ "this func info section will be dropped."),
+ name);
+ }
+ if (err != ECTF_NEXT_END)
+ ctf_err_warn (fp, 0, err, _("error checking for outdated inputs"));
+}
+
/* Write out a CTF archive (if there are per-CU CTF files) or a CTF file
(otherwise) into a new dynamically-allocated string, and return it.
Members with sizes above THRESHOLD are compressed. */
unsigned char *
-ctf_link_write (ctf_file_t *fp, size_t *size, size_t threshold)
+ctf_link_write (ctf_dict_t *fp, size_t *size, size_t threshold)
{
ctf_name_list_accum_cb_arg_t arg;
char **names;
- ctf_file_t **files;
+ char *transformed_name = NULL;
+ ctf_dict_t **files;
FILE *f = NULL;
+ size_t i;
int err;
long fsize;
const char *errloc;
memset (&arg, 0, sizeof (ctf_name_list_accum_cb_arg_t));
arg.fp = fp;
+ fp->ctf_flags |= LCTF_LINKING;
- if (ctf_update (fp) < 0)
- {
- errloc = "CTF file construction";
- goto err;
- }
+ ctf_link_warn_outdated_inputs (fp);
if (fp->ctf_link_outputs)
{
}
}
- /* No extra outputs? Just write a simple ctf_file_t. */
+ /* No extra outputs? Just write a simple ctf_dict_t. */
if (arg.i == 0)
- return ctf_write_mem (fp, size, threshold);
+ {
+ unsigned char *ret = ctf_write_mem (fp, size, threshold);
+ fp->ctf_flags &= ~LCTF_LINKING;
+ return ret;
+ }
/* Writing an archive. Stick ourselves (the shared repository, parent of all
other archives) on the front of it with the default name. */
}
arg.names = names;
memmove (&(arg.names[1]), arg.names, sizeof (char *) * (arg.i));
+
arg.names[0] = (char *) _CTF_SECTION;
+ if (fp->ctf_link_memb_name_changer)
+ {
+ void *nc_arg = fp->ctf_link_memb_name_changer_arg;
+
+ transformed_name = fp->ctf_link_memb_name_changer (fp, _CTF_SECTION,
+ nc_arg);
+
+ if (transformed_name != NULL)
+ {
+ arg.names[0] = transformed_name;
+ ctf_dynhash_iter (fp->ctf_link_outputs, ctf_change_parent_name,
+ transformed_name);
+ }
+ }
+
+ /* Propagate the link flags to all the dicts in this link. */
+ for (i = 0; i < arg.i; i++)
+ {
+ arg.files[i]->ctf_link_flags = fp->ctf_link_flags;
+ arg.files[i]->ctf_flags |= LCTF_LINKING;
+ }
if ((files = realloc (arg.files,
- sizeof (struct ctf_file *) * (arg.i + 1))) == NULL)
+ sizeof (struct ctf_dict *) * (arg.i + 1))) == NULL)
{
- errloc = "ctf_file reallocation";
+ errloc = "ctf_dict reallocation";
goto err_no;
}
arg.files = files;
- memmove (&(arg.files[1]), arg.files, sizeof (ctf_file_t *) * (arg.i));
+ memmove (&(arg.files[1]), arg.files, sizeof (ctf_dict_t *) * (arg.i));
arg.files[0] = fp;
if ((f = tmpfile ()) == NULL)
*size = fsize;
free (arg.names);
free (arg.files);
+ free (transformed_name);
+ if (arg.ndynames)
+ {
+ size_t i;
+ for (i = 0; i < arg.ndynames; i++)
+ free (arg.dynames[i]);
+ free (arg.dynames);
+ }
+ fclose (f);
return buf;
err_no:
ctf_set_errno (fp, errno);
+
+ /* Turn off the is-linking flag on all the dicts in this link. */
+ for (i = 0; i < arg.i; i++)
+ arg.files[i]->ctf_flags &= ~LCTF_LINKING;
err:
free (buf);
if (f)
fclose (f);
free (arg.names);
free (arg.files);
- ctf_dprintf ("Cannot write archive in link: %s failure: %s\n", errloc,
- ctf_errmsg (ctf_errno (fp)));
+ free (transformed_name);
+ if (arg.ndynames)
+ {
+ size_t i;
+ for (i = 0; i < arg.ndynames; i++)
+ free (arg.dynames[i]);
+ free (arg.dynames);
+ }
+ ctf_err_warn (fp, 0, 0, _("cannot write archive in link: %s failure"),
+ errloc);
return NULL;
}