aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/ChangeLog18
-rw-r--r--include/ctf-api.h39
-rw-r--r--libctf/ChangeLog12
-rw-r--r--libctf/Makefile.am4
-rw-r--r--libctf/Makefile.in21
-rw-r--r--libctf/ctf-create.c4
-rw-r--r--libctf/ctf-error.c5
-rw-r--r--libctf/ctf-impl.h2
-rw-r--r--libctf/ctf-link.c528
-rw-r--r--libctf/ctf-open.c2
10 files changed, 619 insertions, 16 deletions
diff --git a/include/ChangeLog b/include/ChangeLog
index ddc5667..6980ec2 100644
--- a/include/ChangeLog
+++ b/include/ChangeLog
@@ -1,5 +1,23 @@
2019-07-13 Nick Alcock <nick.alcock@oracle.com>
+ * ctf-api.h (struct ctf_link_sym): New, a symbol in flight to the
+ libctf linking machinery.
+ (CTF_LINK_SHARE_UNCONFLICTED): New.
+ (CTF_LINK_SHARE_DUPLICATED): New.
+ (ECTF_LINKADDEDLATE): New, replacing ECTF_UNUSED.
+ (ECTF_NOTYET): New, a 'not yet implemented' message.
+ (ctf_link_add_ctf): New, add an input file's CTF to the link.
+ (ctf_link): New, merge the type and string sections.
+ (ctf_link_strtab_string_f): New, callback for feeding strtab info.
+ (ctf_link_iter_symbol_f): New, callback for feeding symtab info.
+ (ctf_link_add_strtab): New, tell the CTF linker about the ELF
+ strtab's strings.
+ (ctf_link_shuffle_syms): New, ask the CTF linker to shuffle its
+ symbols into symtab order.
+ (ctf_link_write): New, ask the CTF linker to write the CTF out.
+
+2019-07-13 Nick Alcock <nick.alcock@oracle.com>
+
* ctf-api.h (ctf_arc_write_fd): New.
(ctf_write_mem): Likewise.
(ctf_gzwrite): Spacing fix.
diff --git a/include/ctf-api.h b/include/ctf-api.h
index 2bee08b..e4c6f9f 100644
--- a/include/ctf-api.h
+++ b/include/ctf-api.h
@@ -65,6 +65,28 @@ typedef struct ctf_sect
size_t cts_entsize; /* Size of each section entry (symtab only). */
} ctf_sect_t;
+/* A minimal symbol extracted from a linker's internal symbol table
+ representation. */
+
+typedef struct ctf_link_sym
+{
+ /* The st_name will not be accessed outside the call to
+ ctf_link_shuffle_syms(). */
+
+ const char *st_name;
+ uint32_t st_shndx;
+ uint32_t st_type;
+ uint32_t st_value;
+} ctf_link_sym_t;
+
+/* Indication of how to share types when linking. */
+
+/* Share all types thare are not in conflict. The default. */
+#define CTF_LINK_SHARE_UNCONFLICTED 0x0
+
+/* Share only types that are used by multiple inputs. Not implemented yet. */
+#define CTF_LINK_SHARE_DUPLICATED 0x1
+
/* Symbolic names for CTF sections. */
typedef enum ctf_sect_names
@@ -145,7 +167,7 @@ enum
ECTF_NOSYMTAB, /* Symbol table data is not available. */
ECTF_NOPARENT, /* Parent CTF container is not available. */
ECTF_DMODEL, /* Data model mismatch. */
- ECTF_UNUSED, /* Unused error. */
+ ECTF_LINKADDEDLATE, /* File added to link too late. */
ECTF_ZALLOC, /* Failed to allocate (de)compression buffer. */
ECTF_DECOMPRESS, /* Failed to decompress CTF data. */
ECTF_STRTAB, /* String table for this string is missing. */
@@ -180,7 +202,8 @@ enum
ECTF_ARNNAME, /* Name not found in CTF archive. */
ECTF_SLICEOVERFLOW, /* Overflow of type bitness or offset in slice. */
ECTF_DUMPSECTUNKNOWN, /* Unknown section number in dump. */
- ECTF_DUMPSECTCHANGED /* Section changed in middle of dump. */
+ ECTF_DUMPSECTCHANGED, /* Section changed in middle of dump. */
+ ECTF_NOTYET /* Feature not yet implemented. */
};
/* The CTF data model is inferred to be the caller's data model or the data
@@ -385,6 +408,18 @@ extern int ctf_gzwrite (ctf_file_t *fp, gzFile fd);
extern int ctf_compress_write (ctf_file_t * fp, int fd);
extern unsigned char *ctf_write_mem (ctf_file_t *, size_t *, size_t threshold);
+extern int ctf_link_add_ctf (ctf_file_t *, ctf_archive_t *, const char *);
+extern int ctf_link (ctf_file_t *, int share_mode);
+typedef const char *ctf_link_strtab_string_f (uint32_t *offset, void *arg);
+extern int ctf_link_add_strtab (ctf_file_t *, ctf_link_strtab_string_f *,
+ void *);
+typedef ctf_link_sym_t *ctf_link_iter_symbol_f (ctf_link_sym_t *dest,
+ void *arg);
+extern int ctf_link_shuffle_syms (ctf_file_t *, ctf_link_iter_symbol_f *,
+ void *);
+extern unsigned char *ctf_link_write (ctf_file_t *, size_t *size,
+ size_t threshold);
+
extern void ctf_setdebug (int debug);
extern int ctf_getdebug (void);
diff --git a/libctf/ChangeLog b/libctf/ChangeLog
index c23e5ae..959a038 100644
--- a/libctf/ChangeLog
+++ b/libctf/ChangeLog
@@ -1,5 +1,17 @@
2019-07-13 Nick Alcock <nick.alcock@oracle.com>
+ * ctf-link.c: New file, linking of the string and type sections.
+ * Makefile.am (libctf_a_SOURCES): Add it.
+ * Makefile.in: Regenerate.
+
+ * ctf-impl.h (ctf_file_t): New fields ctf_link_inputs,
+ ctf_link_outputs.
+ * ctf-create.c (ctf_update): Update accordingly.
+ * ctf-open.c (ctf_file_close): Likewise.
+ * ctf-error.c (_ctf_errlist): Updated with new errors.
+
+2019-07-13 Nick Alcock <nick.alcock@oracle.com>
+
* ctf-dump.c (ctf_dump_funcs): Check the right error value.
2019-07-13 Nick Alcock <nick.alcock@oracle.com>
diff --git a/libctf/Makefile.am b/libctf/Makefile.am
index 43fc78a..a0a27b4 100644
--- a/libctf/Makefile.am
+++ b/libctf/Makefile.am
@@ -33,8 +33,8 @@ AM_CFLAGS = -std=gnu99 @ac_libctf_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANTIC@ @
noinst_LIBRARIES = libctf.a
libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c ctf-error.c \
- ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c ctf-open-bfd.c \
- ctf-string.c ctf-subr.c ctf-types.c ctf-util.c
+ ctf-hash.c ctf-labels.c ctf-link.c ctf-lookup.c ctf-open.c \
+ ctf-open-bfd.c ctf-string.c ctf-subr.c ctf-types.c ctf-util.c
if NEED_CTF_QSORT_R
libctf_a_SOURCES += ctf-qsort_r.c
endif
diff --git a/libctf/Makefile.in b/libctf/Makefile.in
index c898eb4..1d2efb9 100644
--- a/libctf/Makefile.in
+++ b/libctf/Makefile.in
@@ -131,16 +131,16 @@ am__v_AR_1 =
libctf_a_AR = $(AR) $(ARFLAGS)
libctf_a_LIBADD =
am__libctf_a_SOURCES_DIST = ctf-archive.c ctf-dump.c ctf-create.c \
- ctf-decl.c ctf-error.c ctf-hash.c ctf-labels.c ctf-lookup.c \
- ctf-open.c ctf-open-bfd.c ctf-string.c ctf-subr.c ctf-types.c \
- ctf-util.c ctf-qsort_r.c
+ ctf-decl.c ctf-error.c ctf-hash.c ctf-labels.c ctf-link.c \
+ ctf-lookup.c ctf-open.c ctf-open-bfd.c ctf-string.c ctf-subr.c \
+ ctf-types.c ctf-util.c ctf-qsort_r.c
@NEED_CTF_QSORT_R_TRUE@am__objects_1 = ctf-qsort_r.$(OBJEXT)
am_libctf_a_OBJECTS = ctf-archive.$(OBJEXT) ctf-dump.$(OBJEXT) \
ctf-create.$(OBJEXT) ctf-decl.$(OBJEXT) ctf-error.$(OBJEXT) \
- ctf-hash.$(OBJEXT) ctf-labels.$(OBJEXT) ctf-lookup.$(OBJEXT) \
- ctf-open.$(OBJEXT) ctf-open-bfd.$(OBJEXT) ctf-string.$(OBJEXT) \
- ctf-subr.$(OBJEXT) ctf-types.$(OBJEXT) ctf-util.$(OBJEXT) \
- $(am__objects_1)
+ ctf-hash.$(OBJEXT) ctf-labels.$(OBJEXT) ctf-link.$(OBJEXT) \
+ ctf-lookup.$(OBJEXT) ctf-open.$(OBJEXT) ctf-open-bfd.$(OBJEXT) \
+ ctf-string.$(OBJEXT) ctf-subr.$(OBJEXT) ctf-types.$(OBJEXT) \
+ ctf-util.$(OBJEXT) $(am__objects_1)
libctf_a_OBJECTS = $(am_libctf_a_OBJECTS)
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
@@ -331,9 +331,9 @@ AM_CPPFLAGS = -D_GNU_SOURCE -I$(top_srcdir) -I$(top_srcdir)/../include -I$(top_s
AM_CFLAGS = -std=gnu99 @ac_libctf_warn_cflags@ @warn@ @c_warn@ @WARN_PEDANTIC@ @WERROR@ $(ZLIBINC)
noinst_LIBRARIES = libctf.a
libctf_a_SOURCES = ctf-archive.c ctf-dump.c ctf-create.c ctf-decl.c \
- ctf-error.c ctf-hash.c ctf-labels.c ctf-lookup.c ctf-open.c \
- ctf-open-bfd.c ctf-string.c ctf-subr.c ctf-types.c ctf-util.c \
- $(am__append_1)
+ ctf-error.c ctf-hash.c ctf-labels.c ctf-link.c ctf-lookup.c \
+ ctf-open.c ctf-open-bfd.c ctf-string.c ctf-subr.c ctf-types.c \
+ ctf-util.c $(am__append_1)
all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -409,6 +409,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-error.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-hash.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-labels.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-link.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-lookup.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-open-bfd.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctf-open.Po@am__quote@
diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c
index d75de91..fc37d6a 100644
--- a/libctf/ctf-create.c
+++ b/libctf/ctf-create.c
@@ -470,6 +470,8 @@ ctf_update (ctf_file_t *fp)
nfp->ctf_dtoldid = fp->ctf_dtnextid - 1;
nfp->ctf_snapshots = fp->ctf_snapshots + 1;
nfp->ctf_specific = fp->ctf_specific;
+ nfp->ctf_link_inputs = fp->ctf_link_inputs;
+ nfp->ctf_link_outputs = fp->ctf_link_outputs;
nfp->ctf_syn_ext_strtab = fp->ctf_syn_ext_strtab;
nfp->ctf_snapshot_lu = fp->ctf_snapshots;
@@ -480,6 +482,8 @@ ctf_update (ctf_file_t *fp)
nfp->ctf_str_atoms = fp->ctf_str_atoms;
fp->ctf_str_atoms = NULL;
memset (&fp->ctf_dtdefs, 0, sizeof (ctf_list_t));
+ fp->ctf_link_inputs = NULL;
+ fp->ctf_link_outputs = NULL;
fp->ctf_syn_ext_strtab = NULL;
fp->ctf_dvhash = NULL;
diff --git a/libctf/ctf-error.c b/libctf/ctf-error.c
index 660a30a..7f6a4ce 100644
--- a/libctf/ctf-error.c
+++ b/libctf/ctf-error.c
@@ -33,7 +33,7 @@ static const char *const _ctf_errlist[] = {
"Symbol table information is not available", /* ECTF_NOSYMTAB */
"Type information is in parent and unavailable", /* ECTF_NOPARENT */
"Cannot import types with different data model", /* ECTF_DMODEL */
- "Unused error", /* ECTF_UNUSED */
+ "File added to link too late", /* ECTF_LINKADDEDLATE */
"Failed to allocate (de)compression buffer", /* ECTF_ZALLOC */
"Failed to decompress CTF data", /* ECTF_DECOMPRESS */
"External string table is not available", /* ECTF_STRTAB */
@@ -68,7 +68,8 @@ static const char *const _ctf_errlist[] = {
"Name not found in CTF archive", /* ECTF_ARNNAME */
"Overflow of type bitness or offset in slice", /* ECTF_SLICEOVERFLOW */
"Unknown section number in dump", /* ECTF_DUMPSECTUNKNOWN */
- "Section changed in middle of dump" /* ECTF_DUMPSECTCHANGED */
+ "Section changed in middle of dump", /* ECTF_DUMPSECTCHANGED */
+ "Feature not yet implemented" /* ECTF_NOTYET */
};
static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]);
diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h
index 03c48cf..9fb58f5 100644
--- a/libctf/ctf-impl.h
+++ b/libctf/ctf-impl.h
@@ -267,6 +267,8 @@ struct ctf_file
unsigned long ctf_snapshots; /* ctf_snapshot() plus ctf_update() count. */
unsigned long ctf_snapshot_lu; /* ctf_snapshot() call count at last update. */
ctf_archive_t *ctf_archive; /* Archive this ctf_file_t came from. */
+ ctf_dynhash_t *ctf_link_inputs; /* Inputs to this link. */
+ ctf_dynhash_t *ctf_link_outputs; /* Additional outputs from this link. */
char *ctf_tmp_typeslice; /* Storage for slicing up type names. */
size_t ctf_tmp_typeslicelen; /* Size of the typeslice. */
void *ctf_specific; /* Data for ctf_get/setspecific(). */
diff --git a/libctf/ctf-link.c b/libctf/ctf-link.c
new file mode 100644
index 0000000..8f18a49
--- /dev/null
+++ b/libctf/ctf-link.c
@@ -0,0 +1,528 @@
+/* CTF linking.
+ Copyright (C) 2019 Free Software Foundation, Inc.
+
+ This file is part of libctf.
+
+ libctf is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 3, or (at your option) any later
+ version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; see the file COPYING. If not see
+ <http://www.gnu.org/licenses/>. */
+
+#include <ctf-impl.h>
+#include <string.h>
+
+/* Linker machinery.
+
+ 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. */
+
+/* Add a file to a link. */
+
+static void ctf_arc_close_thunk (void *arc)
+{
+ ctf_arc_close ((ctf_archive_t *) arc);
+}
+
+static void ctf_file_close_thunk (void *file)
+{
+ ctf_file_close ((ctf_file_t *) file);
+}
+
+int
+ctf_link_add_ctf (ctf_file_t *fp, ctf_archive_t *ctf, const char *name)
+{
+ char *dupname = 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_arc_close_thunk);
+
+ if (fp->ctf_link_inputs == NULL)
+ goto oom;
+
+ if ((dupname = strdup (name)) == NULL)
+ goto oom;
+
+ if (ctf_dynhash_insert (fp->ctf_link_inputs, dupname, ctf) < 0)
+ goto oom;
+
+ return 0;
+ oom:
+ free (fp->ctf_link_inputs);
+ fp->ctf_link_inputs = NULL;
+ free (dupname);
+ return (ctf_set_errno (fp, ENOMEM));
+}
+
+typedef struct ctf_link_in_member_cb_arg
+{
+ 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;
+
+/* 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. */
+
+static int
+ctf_link_one_type (ctf_id_t type, int isroot _libctf_unused_, 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;
+ int err;
+
+ 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. */
+
+ if (!arg->in_input_cu_file)
+ {
+ 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)
+ {
+ 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_set_errno (arg->out_fp, 0);
+ }
+
+ if ((per_cu_out_fp = ctf_dynhash_lookup (arg->out_fp->ctf_link_outputs,
+ arg->arcname)) == NULL)
+ {
+ int err;
+
+ if ((per_cu_out_fp = ctf_create (&err)) == NULL)
+ {
+ ctf_dprintf ("Cannot create per-CU CTF archive for member %s: %s\n",
+ arg->arcname, ctf_errmsg (err));
+ ctf_set_errno (arg->out_fp, err);
+ return -1;
+ }
+
+ if (ctf_dynhash_insert (arg->out_fp->ctf_link_outputs, arg->arcname,
+ per_cu_out_fp) < 0)
+ {
+ ctf_set_errno (arg->out_fp, ENOMEM);
+ return -1;
+ }
+
+ ctf_import (per_cu_out_fp, arg->out_fp);
+ ctf_cuname_set (per_cu_out_fp, arg->cu_name);
+ }
+
+ if (ctf_add_type (per_cu_out_fp, arg->in_fp, type) != CTF_ERR)
+ return 0;
+
+ 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);
+
+ return 0; /* As above: do not lose types. */
+}
+
+/* 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_)
+{
+ ctf_link_in_member_cb_arg_t *arg = (ctf_link_in_member_cb_arg_t *) arg_;
+ int err = 0;
+
+ if (strcmp (name, _CTF_SECTION) == 0)
+ {
+ /* This file is the default member of this archive, and has already been
+ explicitly processed.
+
+ 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 (arg->done_main_member)
+ return 0;
+ arg->arcname = strdup (".ctf.");
+ if (arg->arcname)
+ {
+ char *new_name;
+
+ 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);
+
+ /* Get ambiguous types from our parent. */
+ ctf_import (in_fp, arg->main_input_fp);
+ arg->in_input_cu_file = 1;
+ }
+
+ if (!arg->arcname)
+ return ctf_set_errno (in_fp, ENOMEM);
+
+ arg->cu_name = name;
+ if (strncmp (arg->cu_name, ".ctf.", strlen (".ctf.")) == 0)
+ arg->cu_name += strlen (".ctf.");
+ arg->in_fp = in_fp;
+
+ err = ctf_type_iter_all (in_fp, ctf_link_one_type, arg);
+
+ arg->in_input_cu_file = 0;
+ free (arg->arcname);
+
+ if (err < 0)
+ return -1; /* Errno is set for us. */
+
+ return 0;
+}
+
+/* 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;
+
+ 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;
+ }
+
+ if (ctf_link_one_input_archive_member (arg->main_input_fp,
+ _CTF_SECTION, arg) < 0)
+ {
+ ctf_file_close (arg->main_input_fp);
+ return;
+ }
+ 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
+ {
+ /* 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_file_close (arg->main_input_fp);
+}
+
+/* Merge types and variable sections in all files added to the link
+ together. */
+int
+ctf_link (ctf_file_t *fp, int share_mode)
+{
+ ctf_link_in_member_cb_arg_t arg;
+
+ memset (&arg, 0, sizeof (struct ctf_link_in_member_cb_arg));
+ arg.out_fp = fp;
+ arg.share_mode = share_mode;
+
+ 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);
+
+ 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);
+
+ if (ctf_errno (fp) != 0)
+ return -1;
+ return 0;
+}
+
+typedef struct ctf_link_out_string_cb_arg
+{
+ const char *str;
+ uint32_t offset;
+ int err;
+} ctf_link_out_string_cb_arg_t;
+
+/* Intern a string in the string table of an output per-CU CTF file. */
+static void
+ctf_link_intern_extern_string (void *key _libctf_unused_, void *value,
+ void *arg_)
+{
+ ctf_file_t *fp = (ctf_file_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)
+ 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
+ strtab properly. */
+int
+ctf_link_add_strtab (ctf_file_t *fp, ctf_link_strtab_string_f *add_string,
+ void *arg)
+{
+ const char *str;
+ uint32_t offset;
+ int err = 0;
+
+ while ((str = add_string (&offset, arg)) != NULL)
+ {
+ 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)
+ err = ENOMEM;
+
+ ctf_dynhash_iter (fp->ctf_link_outputs, ctf_link_intern_extern_string,
+ &iter_arg);
+ if (iter_arg.err)
+ err = iter_arg.err;
+ }
+
+ return -err;
+}
+
+/* Not yet implemented. */
+int
+ctf_link_shuffle_syms (ctf_file_t *fp _libctf_unused_,
+ ctf_link_iter_symbol_f *add_sym _libctf_unused_,
+ void *arg _libctf_unused_)
+{
+ return 0;
+}
+
+typedef struct ctf_name_list_accum_cb_arg
+{
+ char **names;
+ ctf_file_t *fp;
+ ctf_file_t **files;
+ size_t i;
+} 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. */
+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;
+ char **names;
+ ctf_file_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)
+ {
+ (arg->i)--;
+ ctf_set_errno (arg->fp, ENOMEM);
+ return;
+ }
+
+ if ((files = realloc (arg->files, sizeof (ctf_file_t *) * arg->i)) == NULL)
+ {
+ (arg->i)--;
+ ctf_set_errno (arg->fp, ENOMEM);
+ return;
+ }
+ arg->names = names;
+ arg->names[(arg->i) - 1] = (char *) name;
+ arg->files = files;
+ arg->files[(arg->i) - 1] = fp;
+}
+
+/* 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_name_list_accum_cb_arg_t arg;
+ char **names;
+ ctf_file_t **files;
+ FILE *f = NULL;
+ int err;
+ long fsize;
+ const char *errloc;
+ unsigned char *buf = NULL;
+
+ memset (&arg, 0, sizeof (ctf_name_list_accum_cb_arg_t));
+ arg.fp = fp;
+
+ if (ctf_update (fp) < 0)
+ {
+ errloc = "CTF file construction";
+ goto err;
+ }
+
+ if (fp->ctf_link_outputs)
+ {
+ ctf_dynhash_iter (fp->ctf_link_outputs, ctf_accumulate_archive_names, &arg);
+ if (ctf_errno (fp) < 0)
+ {
+ errloc = "hash creation";
+ goto err;
+ }
+ }
+
+ /* No extra outputs? Just write a simple ctf_file_t. */
+ if (arg.i == 0)
+ return ctf_write_mem (fp, size, threshold);
+
+ /* Writing an archive. Stick ourselves (the shared repository, parent of all
+ other archives) on the front of it with the default name. */
+ if ((names = realloc (arg.names, sizeof (char *) * (arg.i + 1))) == NULL)
+ {
+ errloc = "name reallocation";
+ goto err_no;
+ }
+ arg.names = names;
+ memmove (&(arg.names[1]), arg.names, sizeof (char *) * (arg.i));
+ arg.names[0] = (char *) _CTF_SECTION;
+
+ if ((files = realloc (arg.files,
+ sizeof (struct ctf_file *) * (arg.i + 1))) == NULL)
+ {
+ errloc = "ctf_file reallocation";
+ goto err_no;
+ }
+ arg.files = files;
+ memmove (&(arg.files[1]), arg.files, sizeof (ctf_file_t *) * (arg.i));
+ arg.files[0] = fp;
+
+ if ((f = tmpfile ()) == NULL)
+ {
+ errloc = "tempfile creation";
+ goto err_no;
+ }
+
+ if ((err = ctf_arc_write_fd (fileno (f), arg.files, arg.i + 1,
+ (const char **) arg.names,
+ threshold)) < 0)
+ {
+ errloc = "archive writing";
+ ctf_set_errno (fp, err);
+ goto err;
+ }
+
+ if (fseek (f, 0, SEEK_END) < 0)
+ {
+ errloc = "seeking to end";
+ goto err_no;
+ }
+
+ if ((fsize = ftell (f)) < 0)
+ {
+ errloc = "filesize determination";
+ goto err_no;
+ }
+
+ if (fseek (f, 0, SEEK_SET) < 0)
+ {
+ errloc = "filepos resetting";
+ goto err_no;
+ }
+
+ if ((buf = malloc (fsize)) == NULL)
+ {
+ errloc = "CTF archive buffer allocation";
+ goto err_no;
+ }
+
+ while (!feof (f) && fread (buf, fsize, 1, f) == 0)
+ if (ferror (f))
+ {
+ errloc = "reading archive from temporary file";
+ goto err_no;
+ }
+
+ *size = fsize;
+ free (arg.names);
+ free (arg.files);
+ return buf;
+
+ err_no:
+ ctf_set_errno (fp, errno);
+ 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)));
+ return NULL;
+}
diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c
index f4179cb..3bc102a 100644
--- a/libctf/ctf-open.c
+++ b/libctf/ctf-open.c
@@ -1625,6 +1625,8 @@ ctf_file_close (ctf_file_t *fp)
ctf_free (fp->ctf_dynbase);
ctf_dynhash_destroy (fp->ctf_syn_ext_strtab);
+ ctf_dynhash_destroy (fp->ctf_link_inputs);
+ ctf_dynhash_destroy (fp->ctf_link_outputs);
ctf_free (fp->ctf_sxlate);
ctf_free (fp->ctf_txlate);