never gets explicitly freed in the ctf_link_input. */
typedef struct ctf_link_input
{
- const char *clin_filename;
+ char *clin_filename;
ctf_archive_t *clin_arc;
ctf_dict_t *clin_fp;
int n;
ctf_link_input_t *i = (ctf_link_input_t *) input;
if (i->clin_arc)
ctf_arc_close (i->clin_arc);
+ free (i->clin_filename);
free (i);
}
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;
+ int existing = 0;
+ ctf_link_input_t *input;
+ char *filename, *keyname;
- if ((input = calloc (1, sizeof (ctf_link_input_t))) == NULL)
+ /* Existing: return it, or (if a different dict with the same name
+ is already there) make up a new unique name. Always use the actual name
+ for the filename, because that needs to be ctf_open()ed. */
+
+ if ((input = ctf_dynhash_lookup (fp->ctf_link_inputs, name)) != NULL)
+ {
+ if ((fp_input != NULL && (input->clin_fp == fp_input))
+ || (ctf != NULL && (input->clin_arc == ctf)))
+ return 0;
+ existing = 1;
+ }
+
+ if ((filename = strdup (name)) == NULL)
goto oom;
- if ((dupname = strdup (name)) == NULL)
+ if ((input = calloc (1, sizeof (ctf_link_input_t))) == NULL)
goto oom;
input->clin_arc = ctf;
input->clin_fp = fp_input;
- input->clin_filename = dupname;
+ input->clin_filename = filename;
input->n = ctf_dynhash_elements (fp->ctf_link_inputs);
- if (ctf_dynhash_insert (fp->ctf_link_inputs, dupname, input) < 0)
+ if (existing)
+ {
+ if (asprintf (&keyname, "%s#%li", name, (long int)
+ ctf_dynhash_elements (fp->ctf_link_inputs)) < 0)
+ goto oom;
+ }
+ else if ((keyname = strdup (name)) == NULL)
+ goto oom;
+
+ if (ctf_dynhash_insert (fp->ctf_link_inputs, keyname, input) < 0)
goto oom;
return 0;
oom:
free (input);
- free (dupname);
+ free (filename);
+ free (keyname);
return ctf_set_errno (fp, ENOMEM);
}
The order of calls to this function influences the order of types in the
final link output, but otherwise is not important.
+ Repeated additions of the same NAME have no effect; repeated additions of
+ different dicts with the same NAME add all the dicts with unique NAMEs
+ derived from NAME.
+
Private for now, but may in time become public once support for BUF is
implemented. */
return (ssize_t) count;
}
-/* Return a per-CU output CTF dictionary suitable for the given CU, creating and
- interning it if need be. */
+/* Find a non-clashing unique name for a per-CU output dict, to prevent distinct
+ members corresponding to inputs with identical cunames from overwriting each
+ other. The name should be something like NAME. */
+
+static char *
+ctf_new_per_cu_name (ctf_dict_t *fp, const char *name)
+{
+ char *dynname;
+ long int i = 0;
+
+ if ((dynname = strdup (name)) == NULL)
+ return NULL;
+
+ while ((ctf_dynhash_lookup (fp->ctf_link_outputs, dynname)) != NULL)
+ {
+ free (dynname);
+ if (asprintf (&dynname, "%s#%li", name, i++) < 0)
+ return NULL;
+ }
+
+ return dynname;
+}
+
+/* Return a per-CU output CTF dictionary suitable for the given INPUT or CU,
+ creating and interning it if need be. */
-_libctf_nonnull_((1,2))
static ctf_dict_t *
-ctf_create_per_cu (ctf_dict_t *fp, const char *cu_name)
+ctf_create_per_cu (ctf_dict_t *fp, ctf_dict_t *input, const char *cu_name)
{
ctf_dict_t *cu_fp;
const char *ctf_name = NULL;
char *dynname = NULL;
- /* First, check the mapping table and translate the per-CU name we use
+ /* Already has a per-CU mapping? Just return it. */
+
+ if (input && input->ctf_link_in_out)
+ return input->ctf_link_in_out;
+
+ /* Check the mapping table and translate the per-CU name we use
accordingly. */
+ if (cu_name == NULL)
+ cu_name = ctf_unnamed_cuname (input);
+
if (fp->ctf_link_in_cu_mapping)
{
if ((ctf_name = ctf_dynhash_lookup (fp->ctf_link_in_cu_mapping,
if (ctf_name == NULL)
ctf_name = cu_name;
- if ((cu_fp = ctf_dynhash_lookup (fp->ctf_link_outputs, ctf_name)) == NULL)
+ /* Look up the per-CU dict. If we don't know of one, or it is for
+ a different input CU which just happens to have the same name,
+ create a new one. */
+
+ if ((cu_fp = ctf_dynhash_lookup (fp->ctf_link_outputs, ctf_name)) == NULL
+ || cu_fp->ctf_link_in_out != fp)
{
int err;
return NULL;
}
- if ((dynname = strdup (ctf_name)) == NULL)
- goto oom;
- if (ctf_dynhash_insert (fp->ctf_link_outputs, dynname, cu_fp) < 0)
+ ctf_import_unref (cu_fp, fp);
+
+ if ((dynname = ctf_new_per_cu_name (fp, ctf_name)) == NULL)
goto oom;
- ctf_import_unref (cu_fp, fp);
ctf_cuname_set (cu_fp, cu_name);
+
ctf_parent_name_set (cu_fp, _CTF_SECTION);
+ cu_fp->ctf_link_in_out = fp;
+ fp->ctf_link_in_out = cu_fp;
+
+ if (ctf_dynhash_insert (fp->ctf_link_outputs, dynname, cu_fp) < 0)
+ goto oom;
}
return cu_fp;
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,
return 0;
}
- if ((per_cu_out_fp = ctf_create_per_cu (fp, ctf_unnamed_cuname (in_fp))) == NULL)
+ 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. */
continue;
}
- if ((per_cu_out_fp = ctf_create_per_cu (fp, ctf_unnamed_cuname (input))) == NULL)
+ if ((per_cu_out_fp = ctf_create_per_cu (fp, input, NULL)) == NULL)
return -1; /* errno is set for us. */
/* If the type was not found, check for it in the child too. */
return 0;
}
+/* Empty all the ctf_link_outputs. */
+static int
+ctf_link_empty_outputs (ctf_dict_t *fp)
+{
+ ctf_next_t *i = NULL;
+ void *v;
+ int err;
+
+ ctf_dynhash_empty (fp->ctf_link_outputs);
+
+ while ((err = ctf_dynhash_next (fp->ctf_link_inputs, &i, NULL, &v)) == 0)
+ {
+ ctf_dict_t *in = (ctf_dict_t *) v;
+ in->ctf_link_in_out = NULL;
+ }
+ if (err != ECTF_NEXT_END)
+ {
+ fp->ctf_flags &= ~LCTF_LINKING;
+ ctf_err_warn (fp, 1, err, _("iteration error removing old outputs"));
+ ctf_set_errno (fp, err);
+ return -1;
+ }
+ return 0;
+}
+
/* Do a deduplicating link using the ctf-dedup machinery. */
static void
ctf_link_deduplicating (ctf_dict_t *fp)
continue;
}
- if ((dynname = strdup (ctf_cuname (outputs[i]))) == NULL)
+ if ((dynname = ctf_new_per_cu_name (fp, ctf_cuname (outputs[i]))) == NULL)
goto oom_one_output;
if (ctf_dynhash_insert (fp->ctf_link_outputs, dynname, outputs[i]) < 0)
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]);
- }
+ ctf_link_empty_outputs (fp);
goto err;
}
/* Merge types and variable sections in all dicts added to the link together.
- All the added dicts are closed. */
+ The result of any previous link is discarded. */
int
ctf_link (ctf_dict_t *fp, int flags)
{
- ctf_next_t *i = NULL;
int err;
fp->ctf_link_flags = flags;
if (fp->ctf_link_inputs == NULL)
return 0; /* Nothing to do. */
- if (fp->ctf_link_outputs == NULL)
+ if (fp->ctf_link_outputs != NULL)
+ ctf_link_empty_outputs (fp);
+ else
fp->ctf_link_outputs = ctf_dynhash_create (ctf_hash_string,
ctf_hash_eq_string, free,
(ctf_hash_free_fun)
fp->ctf_flags |= LCTF_LINKING;
if (fp->ctf_link_out_cu_mapping && (flags & CTF_LINK_EMPTY_CU_MAPPINGS))
{
- void *v;
+ ctf_next_t *i = NULL;
+ void *k;
- while ((err = ctf_dynhash_next (fp->ctf_link_out_cu_mapping, &i, &v,
+ while ((err = ctf_dynhash_next (fp->ctf_link_out_cu_mapping, &i, &k,
NULL)) == 0)
{
- const char *to = (const char *) v;
- if (ctf_create_per_cu (fp, to) == NULL)
+ const char *to = (const char *) k;
+ if (ctf_create_per_cu (fp, NULL, to) == NULL)
{
fp->ctf_flags &= ~LCTF_LINKING;
ctf_next_destroy (i);
--- /dev/null
+# Copyright (C) 2021-2022 Free Software Foundation, Inc.
+#
+# This file is part of the GNU Binutils.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+# MA 02110-1301, USA.
+#
+
+load_file $srcdir/../../ld/testsuite/lib/ld-lib.exp
+
+global srcdir subdir OBJDUMP
+global testname
+global subsrcdir
+
+set subsrcdir "$srcdir/$subdir/"
+set testname "$dir/libctf-repeat-cu.exp"
+
+if ![is_elf_format] {
+ unsupported "CTF needs bfd changes to be emitted on non-ELF"
+ return 0
+}
+
+if {![check_ctf_available]} {
+ unsupported "no CTF format support in the compiler"
+ return 0
+}
+
+if {[info exists env(LC_ALL)]} {
+ set old_lc_all $env(LC_ALL)
+}
+set env(LC_ALL) "C"
+
+# Compile one SRC to OBJ and put it into ARCHIVE.
+proc one_lib_compile { src flags obj archive } {
+ global CC_FOR_TARGET CFLAGS_FOR_TARGET AR subsrcdir
+
+ if [is_remote host] {
+ set src [remote_download host [file join $subsrcdir $src]]
+ } else {
+ set src [file join $subsrcdir $src]
+ }
+
+ set comp_output [run_host_cmd "$CC_FOR_TARGET" "$CFLAGS_FOR_TARGET $flags -gctf -fPIC -c -o $obj $src"]
+ if { $comp_output != "" } {
+ return $comp_output
+ }
+
+ set ar_output [run_host_cmd "$AR" "rc $archive $obj"]
+ return $comp_output
+}
+
+# Compile one SRC to OBJ and put it into ARCHIVE: error-check the result.
+proc one_lib_compile_check { src flags obj archive } {
+ global testname
+
+ set comp_output [one_lib_compile $src $flags $obj $archive]
+
+ if { $comp_output != ""} {
+ send_log "compilation of $src with $flags failed with <$comp_output>"
+ perror "compilation of $src with $flags failed"
+ fail $testname
+ return 0
+ }
+ return 1
+}
+
+if { ! [one_lib_compile_check libctf-repeat-cu-lib.c "-DINT -DFUN=a" tmpdir/libctf-repeat-cu-lib.o tmpdir/a.a] } {
+ return 0
+}
+
+if { ! [one_lib_compile_check libctf-repeat-cu-lib.c "-DCHAR -DFUN=b" tmpdir/libctf-repeat-cu-lib.o tmpdir/b.a] } {
+ return 0
+}
+
+if { ! [one_lib_compile_check libctf-repeat-cu-lib.c "-DFUN=c" tmpdir/libctf-repeat-cu-lib.o tmpdir/c.a] } {
+ return 0
+}
+
+if [is_remote host] {
+ set src [remote_download host [file join $subsrcdir libctf-repeat-cu-main.c]]
+} else {
+ set src [file join $subsrcdir libctf-repeat-cu-main.c]
+}
+
+set comp_output [run_host_cmd "$CC_FOR_TARGET" "$CFLAGS_FOR_TARGET -gctf -fPIC -shared -o tmpdir/libctf-repeat-cu-main.so $src tmpdir/a.a tmpdir/b.a tmpdir/c.a"]
+if { $comp_output != "" } {
+ send_log "compilation of tmpdir/libctf-repeat-cu-main.so failed"
+ perror "compilation of tmpdir/libctf-repeat-cu-main.so failed"
+ fail $testname
+ return $comp_output
+}
+
+set comp_output [run_host_cmd "$OBJDUMP" "--ctf tmpdir/libctf-repeat-cu-main.so > tmpdir/dump.out"]
+
+if { [regexp_diff "tmpdir/dump.out" [file join $subsrcdir libctf-repeat-cu.d] ] } {
+ fail $testname
+ if { $verbose == 2 } then { verbose "output is [file_contents tmpdir/dump.out]" 2 }
+}
+
+pass $testname
+
+if {[info exists old_lc_all]} {
+ set env(LC_ALL) $old_lc_all
+} else {
+ unset env(LC_ALL)
+}