diff options
-rw-r--r-- | include/ChangeLog | 18 | ||||
-rw-r--r-- | include/ctf-api.h | 39 | ||||
-rw-r--r-- | libctf/ChangeLog | 12 | ||||
-rw-r--r-- | libctf/Makefile.am | 4 | ||||
-rw-r--r-- | libctf/Makefile.in | 21 | ||||
-rw-r--r-- | libctf/ctf-create.c | 4 | ||||
-rw-r--r-- | libctf/ctf-error.c | 5 | ||||
-rw-r--r-- | libctf/ctf-impl.h | 2 | ||||
-rw-r--r-- | libctf/ctf-link.c | 528 | ||||
-rw-r--r-- | libctf/ctf-open.c | 2 |
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); |