/* CTF archive files.
- Copyright (C) 2019 Free Software Foundation, Inc.
+ Copyright (C) 2019-2021 Free Software Foundation, Inc.
This file is part of libctf.
#include <sys/mman.h>
#endif
-static off_t arc_write_one_ctf (ctf_file_t * f, int fd, size_t threshold);
-static ctf_file_t *ctf_arc_open_by_offset (const struct ctf_archive *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect,
- size_t offset, int *errp);
+static off_t arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold);
+static ctf_dict_t *ctf_dict_open_by_offset (const struct ctf_archive *arc,
+ const ctf_sect_t *symsect,
+ const ctf_sect_t *strsect,
+ size_t offset, int little_endian,
+ int *errp);
static int sort_modent_by_name (const void *one, const void *two, void *n);
static void *arc_mmap_header (int fd, size_t headersz);
static void *arc_mmap_file (int fd, size_t size);
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);
-/* bsearch() internal state. */
-static __thread char *search_nametbl;
+/* Flag to indicate "symbol not present" in ctf_archive_internal.ctfi_symdicts
+ and ctfi_symnamedicts. Never initialized. */
+static ctf_dict_t enosym;
-/* Write out a CTF archive. The entries in CTF_FILES are referenced by name:
- the names are passed in the names array, which must have CTF_FILES entries.
+/* 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
+ the names array, which must have CTF_DICTS entries.
Returns 0 on success, or an errno, or an ECTF_* value. */
int
-ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt,
- const char **names, size_t threshold)
+ctf_arc_write_fd (int fd, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt,
+ const char **names, size_t threshold)
{
const char *errmsg;
struct ctf_archive *archdr;
- int fd;
size_t i;
char dummy = 0;
size_t headersz;
off_t nameoffs;
struct ctf_archive_modent *modent;
- ctf_dprintf ("Writing archive %s with %lu files\n", file,
- (unsigned long) ctf_file_cnt);
-
- if ((fd = open (file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) < 0)
- {
- errmsg = "ctf_arc_write(): cannot create %s: %s\n";
- goto err;
- }
+ ctf_dprintf ("Writing CTF archive with %lu files\n",
+ (unsigned long) ctf_dict_cnt);
/* Figure out the size of the mmap()ed header, including the
ctf_archive_modent array. We assume that all of this needs no
padding: a likely assumption, given that it's all made up of
uint64_t's. */
headersz = sizeof (struct ctf_archive)
- + (ctf_file_cnt * sizeof (uint64_t) * 2);
+ + (ctf_dict_cnt * sizeof (uint64_t) * 2);
ctf_dprintf ("headersz is %lu\n", (unsigned long) headersz);
/* From now on we work in two pieces: an mmap()ed region from zero up to the
ctf_startoffs = headersz;
if (lseek (fd, ctf_startoffs - 1, SEEK_SET) < 0)
{
- errmsg = "ctf_arc_write(): cannot extend file while writing %s: %s\n";
- goto err_close;
+ errmsg = N_("ctf_arc_write(): cannot extend file while writing");
+ goto err;
}
if (write (fd, &dummy, 1) < 0)
{
- errmsg = "ctf_arc_write(): cannot extend file while writing %s: %s\n";
- goto err_close;
+ errmsg = N_("ctf_arc_write(): cannot extend file while writing");
+ goto err;
}
if ((archdr = arc_mmap_header (fd, headersz)) == NULL)
{
- errmsg = "ctf_arc_write(): Cannot mmap() %s: %s\n";
- goto err_close;
+ errmsg = N_("ctf_arc_write(): cannot mmap");
+ goto err;
}
/* Fill in everything we can, which is everything other than the name
table offset. */
archdr->ctfa_magic = htole64 (CTFA_MAGIC);
- archdr->ctfa_nfiles = htole64 (ctf_file_cnt);
+ archdr->ctfa_ndicts = htole64 (ctf_dict_cnt);
archdr->ctfa_ctfs = htole64 (ctf_startoffs);
/* We could validate that all CTF files have the same data model, but
this case, but we must be sure not to dereference uninitialized
memory.) */
- if (ctf_file_cnt > 0)
- archdr->ctfa_model = htole64 (ctf_getmodel (ctf_files[0]));
+ if (ctf_dict_cnt > 0)
+ archdr->ctfa_model = htole64 (ctf_getmodel (ctf_dicts[0]));
/* Now write out the CTFs: ctf_archive_modent array via the mapping,
ctfs via write(). The names themselves have not been written yet: we
The name table is not sorted. */
- for (i = 0, namesz = 0; i < le64toh (archdr->ctfa_nfiles); i++)
+ for (i = 0, namesz = 0; i < le64toh (archdr->ctfa_ndicts); i++)
namesz += strlen (names[i]) + 1;
nametbl = malloc (namesz);
if (nametbl == NULL)
{
- errmsg = "Error writing named CTF to %s: %s\n";
+ errmsg = N_("ctf_arc_write(): error writing named CTF to archive");
goto err_unmap;
}
for (i = 0, namesz = 0,
modent = (ctf_archive_modent_t *) ((char *) archdr
+ sizeof (struct ctf_archive));
- i < le64toh (archdr->ctfa_nfiles); i++)
+ i < le64toh (archdr->ctfa_ndicts); i++)
{
off_t off;
strcpy (&nametbl[namesz], names[i]);
- off = arc_write_one_ctf (ctf_files[i], fd, threshold);
+ off = arc_write_one_ctf (ctf_dicts[i], fd, threshold);
if ((off < 0) && (off > -ECTF_BASE))
{
- errmsg = "ctf_arc_write(): Cannot determine file "
- "position while writing %s: %s";
+ errmsg = N_("ctf_arc_write(): cannot determine file "
+ "position while writing to archive");
goto err_free;
}
if (off < 0)
{
- errmsg = "ctf_arc_write(): Cannot write CTF file to %s: %s\n";
+ errmsg = N_("ctf_arc_write(): cannot write CTF file to archive");
errno = off * -1;
goto err_free;
}
ctf_qsort_r ((ctf_archive_modent_t *) ((char *) archdr
+ sizeof (struct ctf_archive)),
- le64toh (archdr->ctfa_nfiles),
+ le64toh (archdr->ctfa_ndicts),
sizeof (struct ctf_archive_modent), sort_modent_by_name,
nametbl);
if ((nameoffs = lseek (fd, 0, SEEK_CUR)) < 0)
{
- errmsg = "ctf_arc_write(): Cannot get current file position "
- "in %s: %s\n";
+ errmsg = N_("ctf_arc_write(): cannot get current file position "
+ "in archive");
goto err_free;
}
archdr->ctfa_names = htole64 (nameoffs);
ssize_t len;
if ((len = write (fd, np, namesz)) < 0)
{
- errmsg = "ctf_arc_write(): Cannot write name table in %s: %s\n";
+ errmsg = N_("ctf_arc_write(): cannot write name table to archive");
goto err_free;
}
namesz -= len;
if (arc_mmap_writeout (fd, archdr, headersz, &errmsg) < 0)
goto err_unmap;
if (arc_mmap_unmap (archdr, headersz, &errmsg) < 0)
- goto err_unlink;
- if (close (fd) < 0)
- {
- errmsg = "ctf_arc_write(): Cannot close after writing to %s: %s\n";
- goto err_unlink;
- }
-
+ goto err;
return 0;
err_free:
free (nametbl);
err_unmap:
arc_mmap_unmap (archdr, headersz, NULL);
-err_close:
- close (fd);
-err_unlink:
- unlink (file);
err:
- ctf_dprintf (errmsg, file, errno < ECTF_BASE ? strerror (errno) :
- ctf_errmsg (errno));
+ /* We report errors into the first file in the archive, if any: if this is a
+ zero-file archive, put it in the open-errors stream for lack of anywhere
+ else for it to go. */
+ ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno, "%s",
+ gettext (errmsg));
return errno;
}
+/* Write out a CTF archive. The entries in CTF_DICTS are referenced by name:
+ the names are passed in the names array, which must have CTF_DICTS entries.
+
+ If the filename is NULL, create a temporary file and return a pointer to it.
+
+ Returns 0 on success, or an errno, or an ECTF_* value. */
+int
+ctf_arc_write (const char *file, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt,
+ const char **names, size_t threshold)
+{
+ int err;
+ int fd;
+
+ if ((fd = open (file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) < 0)
+ {
+ ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno,
+ _("ctf_arc_write(): cannot create %s"), file);
+ return errno;
+ }
+
+ err = ctf_arc_write_fd (fd, ctf_dicts, ctf_dict_cnt, names, threshold);
+ if (err)
+ goto err_close;
+
+ if ((err = close (fd)) < 0)
+ ctf_err_warn (ctf_dict_cnt > 0 ? ctf_dicts[0] : NULL, 0, errno,
+ _("ctf_arc_write(): cannot close after writing to archive"));
+ goto err;
+
+ err_close:
+ (void) close (fd);
+ err:
+ if (err < 0)
+ unlink (file);
+
+ return err;
+}
+
/* Write one CTF file out. Return the file position of the written file (or
rather, of the file-size uint64_t that precedes it): negative return is a
negative errno or ctf_errno value. On error, the file position may no longer
be at the end of the file. */
static off_t
-arc_write_one_ctf (ctf_file_t * f, int fd, size_t threshold)
+arc_write_one_ctf (ctf_dict_t * f, int fd, size_t threshold)
{
off_t off, end_off;
uint64_t ctfsz = 0;
char *ctfszp;
size_t ctfsz_len;
- int (*writefn) (ctf_file_t * fp, int fd);
+ int (*writefn) (ctf_dict_t * fp, int fd);
+
+ if (ctf_serialize (f) < 0)
+ return f->ctf_errno * -1;
if ((off = lseek (fd, 0, SEEK_CUR)) < 0)
return errno * -1;
&nametbl[le64toh (b->name_offset)]);
}
-/* bsearch() function to search for a given name in the sorted array of struct
+/* bsearch_r() function to search for a given name in the sorted array of struct
ctf_archive_modents. */
static int
-search_modent_by_name (const void *key, const void *ent)
+search_modent_by_name (const void *key, const void *ent, void *arg)
{
const char *k = key;
const struct ctf_archive_modent *v = ent;
+ const char *search_nametbl = arg;
return strcmp (k, &search_nametbl[le64toh (v->name_offset)]);
}
-/* A trivial wrapper: open a CTF archive, from data in a buffer (which the
- caller must preserve until ctf_arc_close() time). Returns the archive, or
- NULL and an error in *err (if not NULL). */
-struct ctf_archive *
-ctf_arc_bufopen (const void *buf, size_t size _libctf_unused_, int *errp)
+/* Make a new struct ctf_archive_internal wrapper for a ctf_archive or a
+ ctf_dict. Closes ARC and/or FP on error. Arrange to free the SYMSECT or
+ STRSECT, as needed, on close. Possibly do not unmap on close. */
+
+struct ctf_archive_internal *
+ctf_new_archive_internal (int is_archive, int unmap_on_close,
+ struct ctf_archive *arc,
+ ctf_dict_t *fp, const ctf_sect_t *symsect,
+ const ctf_sect_t *strsect,
+ int *errp)
{
- struct ctf_archive *arc = (struct ctf_archive *) buf;
+ struct ctf_archive_internal *arci;
- if (le64toh (arc->ctfa_magic) != CTFA_MAGIC)
+ if ((arci = calloc (1, sizeof (struct ctf_archive_internal))) == NULL)
{
- if (errp)
- *errp = ECTF_FMT;
- return NULL;
+ if (is_archive)
+ {
+ if (unmap_on_close)
+ ctf_arc_close_internal (arc);
+ }
+ else
+ ctf_dict_close (fp);
+ return (ctf_set_open_errno (errp, errno));
}
- return arc;
+ arci->ctfi_is_archive = is_archive;
+ if (is_archive)
+ arci->ctfi_archive = arc;
+ else
+ arci->ctfi_dict = fp;
+ if (symsect)
+ memcpy (&arci->ctfi_symsect, symsect, sizeof (struct ctf_sect));
+ if (strsect)
+ memcpy (&arci->ctfi_strsect, strsect, sizeof (struct ctf_sect));
+ arci->ctfi_free_symsect = 0;
+ arci->ctfi_free_strsect = 0;
+ arci->ctfi_unmap_on_close = unmap_on_close;
+ arci->ctfi_symsect_little_endian = -1;
+
+ return arci;
+}
+
+/* Set the symbol-table endianness of an archive (defaulting the symtab
+ endianness of all ctf_file_t's opened from that archive). */
+void
+ctf_arc_symsect_endianness (ctf_archive_t *arc, int little_endian)
+{
+ arc->ctfi_symsect_little_endian = !!little_endian;
+ if (!arc->ctfi_is_archive)
+ ctf_symsect_endianness (arc->ctfi_dict, arc->ctfi_symsect_little_endian);
+}
+
+/* Get the CTF preamble from data in a buffer, which may be either an archive or
+ a CTF dict. If multiple dicts are present in an archive, the preamble comes
+ from an arbitrary dict. The preamble is a pointer into the ctfsect passed
+ in. */
+
+const ctf_preamble_t *
+ctf_arc_bufpreamble (const ctf_sect_t *ctfsect)
+{
+ if (ctfsect->cts_size > sizeof (uint64_t) &&
+ (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
+ {
+ struct ctf_archive *arc = (struct ctf_archive *) ctfsect->cts_data;
+ return (const ctf_preamble_t *) ((char *) arc + le64toh (arc->ctfa_ctfs)
+ + sizeof (uint64_t));
+ }
+ else
+ return (const ctf_preamble_t *) ctfsect->cts_data;
+}
+
+/* Open a CTF archive or dictionary from data in a buffer (which the caller must
+ preserve until ctf_arc_close() time). Returns the archive, or NULL and an
+ error in *err (if not NULL). */
+ctf_archive_t *
+ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
+ const ctf_sect_t *strsect, int *errp)
+{
+ struct ctf_archive *arc = NULL;
+ int is_archive;
+ ctf_dict_t *fp = NULL;
+
+ if (ctfsect->cts_size > sizeof (uint64_t) &&
+ (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC))
+ {
+ /* The archive is mmappable, so this operation is trivial.
+
+ This buffer is nonmodifiable, so the trick involving mmapping only part
+ of it and storing the length in the magic number is not applicable: so
+ record this fact in the archive-wrapper header. (We cannot record it
+ in the archive, because the archive may very well be a read-only
+ mapping.) */
+
+ is_archive = 1;
+ arc = (struct ctf_archive *) ctfsect->cts_data;
+ }
+ else
+ {
+ is_archive = 0;
+ if ((fp = ctf_bufopen (ctfsect, symsect, strsect, errp)) == NULL)
+ {
+ ctf_err_warn (NULL, 0, *errp, _("ctf_arc_bufopen(): cannot open CTF"));
+ return NULL;
+ }
+ }
+ return ctf_new_archive_internal (is_archive, 0, arc, fp, symsect, strsect,
+ errp);
}
/* Open a CTF archive. Returns the archive, or NULL and an error in *err (if
libctf_init_debug();
if ((fd = open (filename, O_RDONLY)) < 0)
{
- errmsg = "ctf_arc_open(): cannot open %s: %s\n";
+ errmsg = N_("ctf_arc_open(): cannot open %s");
goto err;
}
if (fstat (fd, &s) < 0)
{
- errmsg = "ctf_arc_open(): cannot stat %s: %s\n";
+ errmsg = N_("ctf_arc_open(): cannot stat %s");
goto err_close;
}
if ((arc = arc_mmap_file (fd, s.st_size)) == NULL)
{
- errmsg = "ctf_arc_open(): Cannot read in %s: %s\n";
+ errmsg = N_("ctf_arc_open(): cannot read in %s");
goto err_close;
}
if (le64toh (arc->ctfa_magic) != CTFA_MAGIC)
{
- errmsg = "ctf_arc_open(): Invalid magic number";
+ errmsg = N_("ctf_arc_open(): %s: invalid magic number");
errno = ECTF_FMT;
goto err_unmap;
}
err:
if (errp)
*errp = errno;
- ctf_dprintf (errmsg, filename, errno < ECTF_BASE ? strerror (errno) :
- ctf_errmsg (errno));
+ ctf_err_warn (NULL, 0, errno, gettext (errmsg), filename);
return NULL;
}
return;
if (arc->ctfi_is_archive)
- ctf_arc_close_internal (arc->ctfi_archive);
+ {
+ if (arc->ctfi_unmap_on_close)
+ ctf_arc_close_internal (arc->ctfi_archive);
+ }
else
- ctf_file_close (arc->ctfi_file);
- free ((void *) arc->ctfi_symsect.cts_data);
- free ((void *) arc->ctfi_strsect.cts_data);
+ ctf_dict_close (arc->ctfi_dict);
+ free (arc->ctfi_symdicts);
+ free (arc->ctfi_symnamedicts);
+ ctf_dynhash_destroy (arc->ctfi_dicts);
+ if (arc->ctfi_free_symsect)
+ free ((void *) arc->ctfi_symsect.cts_data);
+ if (arc->ctfi_free_strsect)
+ free ((void *) arc->ctfi_strsect.cts_data);
free (arc->ctfi_data);
+ if (arc->ctfi_bfd_close)
+ arc->ctfi_bfd_close (arc);
free (arc);
}
-/* Return the ctf_file_t with the given name, or NULL if none, setting 'err' if
+/* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
non-NULL. A name of NULL means to open the default file. */
-static ctf_file_t *
-ctf_arc_open_by_name_internal (const struct ctf_archive *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect,
- const char *name, int *errp)
+static ctf_dict_t *
+ctf_dict_open_internal (const struct ctf_archive *arc,
+ const ctf_sect_t *symsect,
+ const ctf_sect_t *strsect,
+ const char *name, int little_endian,
+ int *errp)
{
struct ctf_archive_modent *modent;
+ const char *search_nametbl;
if (name == NULL)
name = _CTF_SECTION; /* The default name. */
- ctf_dprintf ("ctf_arc_open_by_name(%s): opening\n", name);
+ ctf_dprintf ("ctf_dict_open_internal(%s): opening\n", name);
modent = (ctf_archive_modent_t *) ((char *) arc
+ sizeof (struct ctf_archive));
- search_nametbl = (char *) arc + le64toh (arc->ctfa_names);
- modent = bsearch (name, modent, le64toh (arc->ctfa_nfiles),
- sizeof (struct ctf_archive_modent),
- search_modent_by_name);
+ search_nametbl = (const char *) arc + le64toh (arc->ctfa_names);
+ modent = bsearch_r (name, modent, le64toh (arc->ctfa_ndicts),
+ sizeof (struct ctf_archive_modent),
+ search_modent_by_name, (void *) search_nametbl);
/* This is actually a common case and normal operation: no error
debug output. */
return NULL;
}
- return ctf_arc_open_by_offset (arc, symsect, strsect,
- le64toh (modent->ctf_offset), errp);
+ return ctf_dict_open_by_offset (arc, symsect, strsect,
+ le64toh (modent->ctf_offset),
+ little_endian, errp);
}
-/* Return the ctf_file_t with the given name, or NULL if none, setting 'err' if
+/* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
non-NULL. A name of NULL means to open the default file.
Use the specified string and symbol table sections.
Public entry point. */
-ctf_file_t *
-ctf_arc_open_by_name_sections (const ctf_archive_t *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect,
- const char *name,
- int *errp)
+ctf_dict_t *
+ctf_dict_open_sections (const ctf_archive_t *arc,
+ const ctf_sect_t *symsect,
+ const ctf_sect_t *strsect,
+ const char *name,
+ int *errp)
{
if (arc->ctfi_is_archive)
{
- ctf_file_t *ret;
- ret = ctf_arc_open_by_name_internal (arc->ctfi_archive, symsect, strsect,
- name, errp);
+ ctf_dict_t *ret;
+ ret = ctf_dict_open_internal (arc->ctfi_archive, symsect, strsect,
+ name, arc->ctfi_symsect_little_endian,
+ 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;
}
*errp = ECTF_ARNNAME;
return NULL;
}
- arc->ctfi_file->ctf_archive = (ctf_archive_t *) arc;
+ arc->ctfi_dict->ctf_archive = (ctf_archive_t *) arc;
- /* Bump the refcount so that the user can ctf_file_close() it. */
- arc->ctfi_file->ctf_refcnt++;
- return arc->ctfi_file;
+ /* Bump the refcount so that the user can ctf_dict_close() it. */
+ arc->ctfi_dict->ctf_refcnt++;
+ return arc->ctfi_dict;
}
-/* Return the ctf_file_t with the given name, or NULL if none, setting 'err' if
+/* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if
non-NULL. A name of NULL means to open the default file.
Public entry point. */
-ctf_file_t *
-ctf_arc_open_by_name (const ctf_archive_t *arc, const char *name, int *errp)
+ctf_dict_t *
+ctf_dict_open (const ctf_archive_t *arc, const char *name, int *errp)
{
const ctf_sect_t *symsect = &arc->ctfi_symsect;
const ctf_sect_t *strsect = &arc->ctfi_strsect;
if (strsect->cts_name == NULL)
strsect = NULL;
- return ctf_arc_open_by_name_sections (arc, symsect, strsect, name, errp);
+ 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. If this is the first cached dict, designate it the
+ crossdict_cache. */
+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++;
+
+ if (arc->ctfi_crossdict_cache == NULL)
+ arc->ctfi_crossdict_cache = fp;
+
+ return fp;
+
+ oom:
+ ctf_dict_close (fp);
+ free (dupname);
+ if (errp)
+ *errp = ENOMEM;
+ return NULL;
}
-/* Return the ctf_file_t at the given ctfa_ctfs-relative offset, or NULL if
+/* 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_symnamedicts);
+ ctf_dynhash_destroy (wrapper->ctfi_dicts);
+ wrapper->ctfi_symdicts = NULL;
+ wrapper->ctfi_symnamedicts = NULL;
+ wrapper->ctfi_dicts = NULL;
+ wrapper->ctfi_crossdict_cache = NULL;
+}
+
+/* Return the ctf_dict_t at the given ctfa_ctfs-relative offset, or NULL if
none, setting 'err' if non-NULL. */
-static ctf_file_t *
-ctf_arc_open_by_offset (const struct ctf_archive *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect, size_t offset,
- int *errp)
+static ctf_dict_t *
+ctf_dict_open_by_offset (const struct ctf_archive *arc,
+ const ctf_sect_t *symsect,
+ const ctf_sect_t *strsect, size_t offset,
+ int little_endian, int *errp)
{
ctf_sect_t ctfsect;
- ctf_file_t *fp;
+ ctf_dict_t *fp;
- ctf_dprintf ("ctf_arc_open_by_offset(%lu): opening\n", (unsigned long) offset);
+ ctf_dprintf ("ctf_dict_open_by_offset(%lu): opening\n", (unsigned long) offset);
memset (&ctfsect, 0, sizeof (ctf_sect_t));
ctfsect.cts_data = (void *) ((char *) arc + offset + sizeof (uint64_t));
fp = ctf_bufopen (&ctfsect, symsect, strsect, errp);
if (fp)
- ctf_setmodel (fp, le64toh (arc->ctfa_model));
+ {
+ ctf_setmodel (fp, le64toh (arc->ctfa_model));
+ if (little_endian >= 0)
+ ctf_symsect_endianness (fp, little_endian);
+ }
return fp;
}
+/* Backward compatibility. */
+ctf_dict_t *
+ctf_arc_open_by_name (const ctf_archive_t *arc, const char *name,
+ int *errp)
+{
+ return ctf_dict_open (arc, name, errp);
+}
+
+ctf_dict_t *
+ctf_arc_open_by_name_sections (const ctf_archive_t *arc,
+ const ctf_sect_t *symsect,
+ const ctf_sect_t *strsect,
+ const char *name,
+ int *errp)
+{
+ 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)
+{
+ if (!wrapper->ctfi_is_archive)
+ return 1;
+
+ return wrapper->ctfi_archive->ctfa_ndicts;
+}
+
+/* Look up a symbol in an archive by name or index (if the name is set, a lookup
+ by name is done). 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 is 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). */
+
+static ctf_dict_t *
+ctf_arc_lookup_sym_or_name (ctf_archive_t *wrapper, unsigned long symidx,
+ const char *symname, ctf_id_t *typep, int *errp)
+{
+ ctf_dict_t *fp;
+ void *fpkey;
+ ctf_id_t type;
+
+ /* The usual non-archive-transparent-wrapper special case. */
+ if (!wrapper->ctfi_is_archive)
+ {
+ if (!symname)
+ {
+ if ((type = ctf_lookup_by_symbol (wrapper->ctfi_dict, symidx)) == CTF_ERR)
+ {
+ if (errp)
+ *errp = ctf_errno (wrapper->ctfi_dict);
+ return NULL;
+ }
+ }
+ else
+ {
+ if ((type = ctf_lookup_by_symbol_name (wrapper->ctfi_dict,
+ symname)) == 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 symbol indexes, if not already done. We
+ cache the originating dictionary of all symbols. The dict links are weak,
+ to the dictionaries cached in ctfi_dicts: their refcnts are *not* bumped.
+ We also cache similar mappings for symbol names: these are ordinary
+ dynhashes, with weak links to dicts. */
+
+ 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;
+ }
+ }
+ if (!wrapper->ctfi_symnamedicts)
+ {
+ if ((wrapper->ctfi_symnamedicts = ctf_dynhash_create (ctf_hash_string,
+ ctf_hash_eq_string,
+ free, NULL)) == NULL)
+ {
+ if (errp)
+ *errp = ENOMEM;
+ return NULL;
+ }
+ }
+
+ /* Perhaps the dict in which we found a previous lookup is cached. If it's
+ supposed to be cached but we don't find it, pretend it was always not
+ found: this should never happen, but shouldn't be allowed to cause trouble
+ if it does. */
+
+ if ((symname && ctf_dynhash_lookup_kv (wrapper->ctfi_symnamedicts,
+ symname, NULL, &fpkey))
+ || (!symname && wrapper->ctfi_symdicts[symidx] != NULL))
+ {
+ if (symname)
+ fp = (ctf_dict_t *) fpkey;
+ else
+ fp = wrapper->ctfi_symdicts[symidx];
+
+ if (fp == &enosym)
+ goto no_sym;
+
+ if (symname)
+ {
+ if ((type = ctf_lookup_by_symbol_name (fp, symname)) == CTF_ERR)
+ goto cache_no_sym;
+ }
+ else
+ {
+ if ((type = ctf_lookup_by_symbol (fp, symidx)) == CTF_ERR)
+ goto cache_no_sym;
+ }
+
+ if (typep)
+ *typep = type;
+ fp->ctf_refcnt++;
+ return fp;
+ }
+
+ /* 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 (!symname)
+ {
+ if ((type = ctf_lookup_by_symbol (fp, symidx)) != CTF_ERR)
+ wrapper->ctfi_symdicts[symidx] = fp;
+ }
+ else
+ {
+ if ((type = ctf_lookup_by_symbol_name (fp, symname)) != CTF_ERR)
+ {
+ char *tmp;
+ /* No error checking, as above. */
+ if ((tmp = strdup (symname)) != NULL)
+ ctf_dynhash_insert (wrapper->ctfi_symnamedicts, tmp, fp);
+ }
+ }
+
+ if (type != CTF_ERR)
+ {
+ if (typep)
+ *typep = type;
+ ctf_next_destroy (i);
+ return fp;
+ }
+ if (ctf_errno (fp) != ECTF_NOTYPEDAT)
+ {
+ if (errp)
+ *errp = ctf_errno (fp);
+ ctf_next_destroy (i);
+ return NULL; /* errno is set for us. */
+ }
+ 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;
+
+ cache_no_sym:
+ if (!symname)
+ wrapper->ctfi_symdicts[symidx] = &enosym;
+ else
+ {
+ char *tmp;
+
+ /* No error checking: if caching fails, there is only a slight performance
+ impact. */
+ if ((tmp = strdup (symname)) != NULL)
+ if (ctf_dynhash_insert (wrapper->ctfi_symnamedicts, tmp, &enosym) < 0)
+ free (tmp);
+ }
+
+ no_sym:
+ if (errp)
+ *errp = ECTF_NOTYPEDAT;
+ if (typep)
+ *typep = CTF_ERR;
+ return NULL;
+}
+
+/* The public API for looking up a symbol by index. */
+ctf_dict_t *
+ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx,
+ ctf_id_t *typep, int *errp)
+{
+ return ctf_arc_lookup_sym_or_name (wrapper, symidx, NULL, typep, errp);
+}
+
+/* The public API for looking up a symbol by name. */
+
+ctf_dict_t *
+ctf_arc_lookup_symbol_name (ctf_archive_t *wrapper, const char *symname,
+ ctf_id_t *typep, int *errp)
+{
+ return ctf_arc_lookup_sym_or_name (wrapper, 0, symname, typep, errp);
+}
+
/* 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
+ sizeof (struct ctf_archive));
nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
- for (i = 0; i < le64toh (arc->ctfa_nfiles); i++)
+ for (i = 0; i < le64toh (arc->ctfa_ndicts); i++)
{
const char *name;
char *fp;
return -EINVAL; /* Not supported. */
}
-/* Iterate over all CTF files in an archive. We pass all CTF files in turn to
- the specified callback function. */
-static int
-ctf_archive_iter_internal (const ctf_archive_t *wrapper,
- const struct ctf_archive *arc,
- const ctf_sect_t *symsect,
- const ctf_sect_t *strsect,
- ctf_archive_member_f *func, void *data)
+/* Iterate over all CTF files in an archive: public entry point. We pass all
+ CTF files in turn to the specified callback function. */
+int
+ctf_archive_iter (const ctf_archive_t *arc, ctf_archive_member_f *func,
+ void *data)
{
- int rc;
- size_t i;
- ctf_file_t *f;
- struct ctf_archive_modent *modent;
- const char *nametbl;
-
- modent = (ctf_archive_modent_t *) ((char *) arc
- + sizeof (struct ctf_archive));
- nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
+ ctf_next_t *i = NULL;
+ ctf_dict_t *fp;
+ const char *name;
+ int err;
- for (i = 0; i < le64toh (arc->ctfa_nfiles); i++)
+ while ((fp = ctf_archive_next (arc, &i, &name, 0, &err)) != NULL)
{
- const char *name;
+ int rc;
- name = &nametbl[le64toh (modent[i].name_offset)];
- if ((f = ctf_arc_open_by_name_internal (arc, symsect, strsect,
- name, &rc)) == NULL)
- return rc;
-
- f->ctf_archive = (ctf_archive_t *) wrapper;
- if ((rc = func (f, name, data)) != 0)
+ if ((rc = func (fp, name, data)) != 0)
{
- ctf_file_close (f);
+ ctf_dict_close (fp);
+ ctf_next_destroy (i);
return rc;
}
-
- ctf_file_close (f);
+ ctf_dict_close (fp);
}
return 0;
}
-/* Iterate over all CTF files in an archive: public entry point. We pass all
- CTF files in turn to the specified callback function. */
-int
-ctf_archive_iter (const ctf_archive_t *arc, ctf_archive_member_f *func,
- void *data)
+/* Iterate over all CTF files in an archive, returning each dict in turn as a
+ ctf_dict_t, and NULL on error or end of iteration. It is the caller's
+ responsibility to close it. Parent dicts may be skipped.
+
+ 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. */
+
+ctf_dict_t *
+ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **name,
+ int skip_parent, int *errp)
{
- const ctf_sect_t *symsect = &arc->ctfi_symsect;
- const ctf_sect_t *strsect = &arc->ctfi_strsect;
+ ctf_dict_t *f;
+ ctf_next_t *i = *it;
+ struct ctf_archive *arc;
+ struct ctf_archive_modent *modent;
+ const char *nametbl;
+ const char *name_;
- if (symsect->cts_name == NULL)
- symsect = NULL;
- if (strsect->cts_name == NULL)
- strsect = NULL;
+ if (!i)
+ {
+ if ((i = ctf_next_create()) == NULL)
+ {
+ if (errp)
+ *errp = ENOMEM;
+ return NULL;
+ }
+ i->cu.ctn_arc = wrapper;
+ i->ctn_iter_fun = (void (*) (void)) ctf_archive_next;
+ *it = i;
+ }
- if (arc->ctfi_is_archive)
- return ctf_archive_iter_internal (arc, arc->ctfi_archive, symsect, strsect,
- func, data);
+ if ((void (*) (void)) ctf_archive_next != i->ctn_iter_fun)
+ {
+ if (errp)
+ *errp = ECTF_NEXT_WRONGFUN;
+ return NULL;
+ }
+
+ if (wrapper != i->cu.ctn_arc)
+ {
+ if (errp)
+ *errp = ECTF_NEXT_WRONGFP;
+ return NULL;
+ }
- return func (arc->ctfi_file, _CTF_SECTION, data);
+ /* Iteration is made a bit more complex by the need to handle ctf_dict_t's
+ transparently wrapped in a single-member archive. These are parents: if
+ skip_parent is on, they are skipped and the iterator terminates
+ immediately. */
+
+ if (!wrapper->ctfi_is_archive && i->ctn_n == 0)
+ {
+ i->ctn_n++;
+ if (!skip_parent)
+ {
+ wrapper->ctfi_dict->ctf_refcnt++;
+ if (name)
+ *name = _CTF_SECTION;
+ return wrapper->ctfi_dict;
+ }
+ }
+
+ arc = wrapper->ctfi_archive;
+
+ /* The loop keeps going when skip_parent is on as long as the member we find
+ is the parent (i.e. at most two iterations, but possibly an early return if
+ *all* we have is a parent). */
+
+ do
+ {
+ if ((!wrapper->ctfi_is_archive) || (i->ctn_n >= le64toh (arc->ctfa_ndicts)))
+ {
+ ctf_next_destroy (i);
+ *it = NULL;
+ if (errp)
+ *errp = ECTF_NEXT_END;
+ return NULL;
+ }
+
+ modent = (ctf_archive_modent_t *) ((char *) arc
+ + sizeof (struct ctf_archive));
+ nametbl = (((const char *) arc) + le64toh (arc->ctfa_names));
+
+ name_ = &nametbl[le64toh (modent[i->ctn_n].name_offset)];
+ i->ctn_n++;
+ }
+ while (skip_parent && strcmp (name_, _CTF_SECTION) == 0);
+
+ if (name)
+ *name = name_;
+
+ f = ctf_dict_open_cached ((ctf_archive_t *) wrapper, name_, errp);
+ return f;
}
#ifdef HAVE_MMAP
if (msync (header, headersz, MS_ASYNC) < 0)
{
if (errmsg)
- *errmsg = "arc_mmap_writeout(): Cannot sync after writing to %s: %s\n";
+ *errmsg = N_("arc_mmap_writeout(): cannot sync after writing "
+ "to %s: %s");
return -1;
}
return 0;
if (munmap (header, headersz) < 0)
{
if (errmsg)
- *errmsg = "arc_mmap_munmap(): Cannot unmap after writing to %s: %s\n";
+ *errmsg = N_("arc_mmap_munmap(): cannot unmap after writing "
+ "to %s: %s");
return -1;
}
return 0;
}
#else
/* Map the header in. Only used on new, empty files. */
-static void *arc_mmap_header (int fd, size_t headersz)
+static void *arc_mmap_header (int fd _libctf_unused_, size_t headersz)
{
void *hdr;
if ((hdr = malloc (headersz)) == NULL)
if ((lseek (fd, 0, SEEK_SET)) < 0)
{
if (errmsg)
- *errmsg = "arc_mmap_writeout(): Cannot seek while writing header to "
- "%s: %s\n";
+ *errmsg = N_("arc_mmap_writeout(): cannot seek while writing header to "
+ "%s: %s");
return -1;
}
if ((len = write (fd, data, count)) < 0)
{
if (errmsg)
- *errmsg = "arc_mmap_writeout(): Cannot write header to %s: %s\n";
+ *errmsg = N_("arc_mmap_writeout(): cannot write header to %s: %s");
return len;
}
if (len == EINTR)