static int arc_mmap_writeout (int fd, void *header, size_t headersz,
const char **errmsg);
static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg);
+static void ctf_arc_import_parent (const ctf_archive_t *arc, ctf_dict_t *fp);
+
+/* Flag to indicate "symbol not present" in
+ ctf_archive_internal.ctfi_symdicts. Never initialized. */
+static ctf_dict_t enosym;
/* Write out a CTF archive to the start of the file referenced by the passed-in
fd. The entries in CTF_DICTS are referenced by name: the names are passed in
}
else
ctf_dict_close (arc->ctfi_dict);
+ free (arc->ctfi_syms);
+ free (arc->ctfi_symdicts);
+ ctf_dynhash_destroy (arc->ctfi_dicts);
if (arc->ctfi_free_symsect)
free ((void *) arc->ctfi_symsect.cts_data);
if (arc->ctfi_free_strsect)
ret = ctf_dict_open_internal (arc->ctfi_archive, symsect, strsect,
name, errp);
if (ret)
- ret->ctf_archive = (ctf_archive_t *) arc;
+ {
+ ret->ctf_archive = (ctf_archive_t *) arc;
+ ctf_arc_import_parent (arc, ret);
+ }
return ret;
}
return ctf_dict_open_sections (arc, symsect, strsect, name, errp);
}
+static void
+ctf_cached_dict_close (void *fp)
+{
+ ctf_dict_close ((ctf_dict_t *) fp);
+}
+
+/* Return the ctf_dict_t with the given name and cache it in the
+ archive's ctfi_dicts. */
+static ctf_dict_t *
+ctf_dict_open_cached (ctf_archive_t *arc, const char *name, int *errp)
+{
+ ctf_dict_t *fp;
+ char *dupname;
+
+ /* Just return from the cache if possible. */
+ if (arc->ctfi_dicts
+ && ((fp = ctf_dynhash_lookup (arc->ctfi_dicts, name)) != NULL))
+ {
+ fp->ctf_refcnt++;
+ return fp;
+ }
+
+ /* Not yet cached: open it. */
+ fp = ctf_dict_open (arc, name, errp);
+ dupname = strdup (name);
+
+ if (!fp || !dupname)
+ goto oom;
+
+ if (arc->ctfi_dicts == NULL)
+ if ((arc->ctfi_dicts
+ = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
+ free, ctf_cached_dict_close)) == NULL)
+ goto oom;
+
+ if (ctf_dynhash_insert (arc->ctfi_dicts, dupname, fp) < 0)
+ goto oom;
+ fp->ctf_refcnt++;
+
+ return fp;
+
+ oom:
+ ctf_dict_close (fp);
+ free (dupname);
+ if (errp)
+ *errp = ENOMEM;
+ return NULL;
+}
+
+/* Flush any caches the CTF archive may have open. */
+void
+ctf_arc_flush_caches (ctf_archive_t *wrapper)
+{
+ free (wrapper->ctfi_symdicts);
+ free (wrapper->ctfi_syms);
+ ctf_dynhash_destroy (wrapper->ctfi_dicts);
+ wrapper->ctfi_symdicts = NULL;
+ wrapper->ctfi_syms = NULL;
+ wrapper->ctfi_dicts = NULL;
+}
+
/* Return the ctf_dict_t at the given ctfa_ctfs-relative offset, or NULL if
none, setting 'err' if non-NULL. */
static ctf_dict_t *
return ctf_dict_open_sections (arc, symsect, strsect, name, errp);
}
+/* Import the parent into a ctf archive, if this is a child, the parent is not
+ already set, and a suitable archive member exists. No error is raised if
+ this is not possible: this is just a best-effort helper operation to give
+ people useful dicts to start with. */
+static void
+ctf_arc_import_parent (const ctf_archive_t *arc, ctf_dict_t *fp)
+{
+ if ((fp->ctf_flags & LCTF_CHILD) && fp->ctf_parname && !fp->ctf_parent)
+ {
+ ctf_dict_t *parent = ctf_dict_open_cached ((ctf_archive_t *) arc,
+ fp->ctf_parname, NULL);
+ if (parent)
+ {
+ ctf_import (fp, parent);
+ ctf_dict_close (parent);
+ }
+ }
+}
+
/* Return the number of members in an archive. */
size_t
ctf_archive_count (const ctf_archive_t *wrapper)
return wrapper->ctfi_archive->ctfa_ndicts;
}
+/* Look up a symbol in an archive. Return the dict in the archive that the
+ symbol is found in, and (optionally) the ctf_id_t of the symbol in that dict
+ (so you don't have to look it up yourself). The dict and mapping are both
+ cached, so repeated lookups are nearly free.
+
+ As usual, you should ctf_dict_close() the returned dict once you are done
+ with it.
+
+ Returns NULL on error, and an error in errp (if set). */
+
+ctf_dict_t *
+ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx,
+ ctf_id_t *typep, int *errp)
+{
+ ctf_dict_t *fp;
+ ctf_id_t type;
+
+ /* The usual non-archive-transparent-wrapper special case. */
+ if (!wrapper->ctfi_is_archive)
+ {
+ if ((type = ctf_lookup_by_symbol (wrapper->ctfi_dict, symidx)) == CTF_ERR)
+ {
+ if (errp)
+ *errp = ctf_errno (wrapper->ctfi_dict);
+ return NULL;
+ }
+ if (typep)
+ *typep = type;
+ wrapper->ctfi_dict->ctf_refcnt++;
+ return wrapper->ctfi_dict;
+ }
+
+ if (wrapper->ctfi_symsect.cts_name == NULL
+ || wrapper->ctfi_symsect.cts_data == NULL
+ || wrapper->ctfi_symsect.cts_size == 0
+ || wrapper->ctfi_symsect.cts_entsize == 0)
+ {
+ if (errp)
+ *errp = ECTF_NOSYMTAB;
+ return NULL;
+ }
+
+ /* Make enough space for all possible symbols, if not already done.
+ We cache both the ctf_id_t and the originating dictionary of all symbols.
+ The dict links are weak, to the dictionaries cached in ctfi_dicts: their
+ refcnts are *not* bumped. */
+
+ if (!wrapper->ctfi_syms)
+ {
+ if ((wrapper->ctfi_syms = calloc (wrapper->ctfi_symsect.cts_size
+ / wrapper->ctfi_symsect.cts_entsize,
+ sizeof (ctf_id_t))) == NULL)
+ {
+ if (errp)
+ *errp = ENOMEM;
+ return NULL;
+ }
+ }
+ if (!wrapper->ctfi_symdicts)
+ {
+ if ((wrapper->ctfi_symdicts = calloc (wrapper->ctfi_symsect.cts_size
+ / wrapper->ctfi_symsect.cts_entsize,
+ sizeof (ctf_dict_t *))) == NULL)
+ {
+ if (errp)
+ *errp = ENOMEM;
+ return NULL;
+ }
+ }
+
+ /* Perhaps it's cached. */
+ if (wrapper->ctfi_symdicts[symidx] != NULL)
+ {
+ if (wrapper->ctfi_symdicts[symidx] == &enosym)
+ {
+ if (errp)
+ *errp = ECTF_NOTYPEDAT;
+ if (typep)
+ *typep = CTF_ERR;
+ return NULL;
+ }
+
+ if (typep)
+ *typep = wrapper->ctfi_syms[symidx];
+ wrapper->ctfi_symdicts[symidx]->ctf_refcnt++;
+ return wrapper->ctfi_symdicts[symidx];
+ }
+
+ /* Not cached: find it and cache it. We must track open errors ourselves even
+ if our caller doesn't, to be able to distinguish no-error end-of-iteration
+ from open errors. */
+
+ int local_err;
+ int *local_errp;
+ ctf_next_t *i = NULL;
+ const char *name;
+
+ if (errp)
+ local_errp = errp;
+ else
+ local_errp = &local_err;
+
+ while ((fp = ctf_archive_next (wrapper, &i, &name, 0, local_errp)) != NULL)
+ {
+ if ((type = ctf_lookup_by_symbol (fp, symidx)) != CTF_ERR)
+ {
+ wrapper->ctfi_syms[symidx] = type;
+ wrapper->ctfi_symdicts[symidx] = fp;
+ ctf_next_destroy (i);
+
+ if (typep)
+ *typep = type;
+ return fp;
+ }
+ ctf_dict_close (fp);
+ }
+ if (*local_errp != ECTF_NEXT_END)
+ {
+ ctf_next_destroy (i);
+ return NULL;
+ }
+ /* Don't leak end-of-iteration to the caller. */
+ *local_errp = 0;
+
+ wrapper->ctfi_symdicts[symidx] = &enosym;
+
+ if (errp)
+ *errp = ECTF_NOTYPEDAT;
+ if (typep)
+ *typep = CTF_ERR;
+ return NULL;
+}
+
/* Raw iteration over all CTF files in an archive. We pass the raw data for all
CTF files in turn to the specified callback function. */
static int
return rc;
f->ctf_archive = (ctf_archive_t *) wrapper;
+ ctf_arc_import_parent (wrapper, f);
if ((rc = func (f, name, data)) != 0)
{
ctf_dict_close (f);
whether they are skipped or not, the caller must ctf_import the parent if
need be.
+ The archive member is cached for rapid return on future calls.
+
We identify parents by name rather than by flag value: for now, with the
linker only emitting parents named _CTF_SECTION, this works well enough. */
is the parent (i.e. at most two iterations, but possibly an early return if
*all* we have is a parent). */
- const ctf_sect_t *symsect;
- const ctf_sect_t *strsect;
-
do
{
if ((!wrapper->ctfi_is_archive) || (i->ctn_n >= le64toh (arc->ctfa_ndicts)))
return NULL;
}
- symsect = &wrapper->ctfi_symsect;
- strsect = &wrapper->ctfi_strsect;
-
- if (symsect->cts_name == NULL)
- symsect = NULL;
- if (strsect->cts_name == NULL)
- strsect = NULL;
-
modent = (ctf_archive_modent_t *) ((char *) arc
+ sizeof (struct ctf_archive));
nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
if (name)
*name = name_;
- f = ctf_dict_open_internal (arc, symsect, strsect, name_, errp);
- f->ctf_archive = (ctf_archive_t *) wrapper;
+ f = ctf_dict_open_cached ((ctf_archive_t *) wrapper, name_, errp);
return f;
}