X-Git-Url: https://repo.jachan.dev/binutils.git/blobdiff_plain/2e428e74401480c562a203ba6b6923ce8c7811fa..HEAD:/libctf/ctf-archive.c diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c index ac13d6dd5e..8366d7fd60 100644 --- a/libctf/ctf-archive.c +++ b/libctf/ctf-archive.c @@ -1,5 +1,5 @@ /* CTF archive files. - Copyright (C) 2019-2020 Free Software Foundation, Inc. + Copyright (C) 2019-2022 Free Software Foundation, Inc. This file is part of libctf. @@ -32,25 +32,31 @@ #include #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); + +/* 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 to the start of the file referenced by the passed-in - fd. The entries in CTF_FILES are referenced by name: the names are passed in - the names array, which must have CTF_FILES entries. + 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_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt, +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; @@ -66,14 +72,14 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt, struct ctf_archive_modent *modent; ctf_dprintf ("Writing CTF archive with %lu files\n", - (unsigned long) ctf_file_cnt); + (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 @@ -82,26 +88,26 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt, ctf_startoffs = headersz; if (lseek (fd, ctf_startoffs - 1, SEEK_SET) < 0) { - errmsg = "ctf_arc_write(): cannot extend file while writing: %s\n"; + 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\n"; + 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\n"; + 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 @@ -112,8 +118,8 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt, 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 @@ -122,35 +128,35 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt, 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 archive: %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 to archive: %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 archive: %s\n"; + errmsg = N_("ctf_arc_write(): cannot write CTF file to archive"); errno = off * -1; goto err_free; } @@ -163,7 +169,7 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt, 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); @@ -171,8 +177,8 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt, if ((nameoffs = lseek (fd, 0, SEEK_CUR)) < 0) { - errmsg = "ctf_arc_write(): Cannot get current file position " - "in archive: %s\n"; + errmsg = N_("ctf_arc_write(): cannot get current file position " + "in archive"); goto err_free; } archdr->ctfa_names = htole64 (nameoffs); @@ -182,7 +188,7 @@ ctf_arc_write_fd (int fd, ctf_file_t **ctf_files, size_t ctf_file_cnt, ssize_t len; if ((len = write (fd, np, namesz)) < 0) { - errmsg = "ctf_arc_write(): Cannot write name table to archive: %s\n"; + errmsg = N_("ctf_arc_write(): cannot write name table to archive"); goto err_free; } namesz -= len; @@ -201,19 +207,22 @@ err_free: err_unmap: arc_mmap_unmap (archdr, headersz, NULL); err: - ctf_dprintf (errmsg, 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_FILES are referenced by name: - the names are passed in the names array, which must have CTF_FILES entries. +/* 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_file_t ** ctf_files, size_t ctf_file_cnt, +ctf_arc_write (const char *file, ctf_dict_t **ctf_dicts, size_t ctf_dict_cnt, const char **names, size_t threshold) { int err; @@ -221,30 +230,23 @@ ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt, if ((fd = open (file, O_RDWR | O_CREAT | O_TRUNC | O_CLOEXEC, 0666)) < 0) { - ctf_dprintf ("ctf_arc_write(): cannot create %s: %s\n", file, - strerror (errno)); + 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_files, ctf_file_cnt, names, threshold); + err = ctf_arc_write_fd (fd, ctf_dicts, ctf_dict_cnt, names, threshold); if (err) - goto err; + goto err_close; if ((err = close (fd)) < 0) - { - ctf_dprintf ("ctf_arc_write(): Cannot close after writing to archive: " - "%s\n", strerror (errno)); - goto err_close; - } - - err: - close (fd); - if (err < 0) - unlink (file); - - return err; + 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); @@ -256,13 +258,13 @@ ctf_arc_write (const char *file, ctf_file_t ** ctf_files, size_t ctf_file_cnt, 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; @@ -342,12 +344,13 @@ search_modent_by_name (const void *key, const void *ent, void *arg) } /* Make a new struct ctf_archive_internal wrapper for a ctf_archive or a - ctf_file. Closes ARC and/or FP on error. Arrange to free the SYMSECT or - STRSECT, as needed, on close. */ + 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, struct ctf_archive *arc, - ctf_file_t *fp, const ctf_sect_t *symsect, +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) { @@ -356,25 +359,60 @@ ctf_new_archive_internal (int is_archive, struct ctf_archive *arc, if ((arci = calloc (1, sizeof (struct ctf_archive_internal))) == NULL) { if (is_archive) - ctf_arc_close_internal (arc); + { + if (unmap_on_close) + ctf_arc_close_internal (arc); + } else - ctf_file_close (fp); + ctf_dict_close (fp); return (ctf_set_open_errno (errp, errno)); } arci->ctfi_is_archive = is_archive; if (is_archive) arci->ctfi_archive = arc; else - arci->ctfi_file = fp; + 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). */ @@ -384,12 +422,18 @@ ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, { struct ctf_archive *arc = NULL; int is_archive; - ctf_file_t *fp = NULL; + ctf_dict_t *fp = NULL; if (ctfsect->cts_size > sizeof (uint64_t) && - ((*(uint64_t *) ctfsect->cts_data) == CTFA_MAGIC)) + (le64toh ((*(uint64_t *) ctfsect->cts_data)) == CTFA_MAGIC)) { - /* The archive is mmappable, so this operation is trivial. */ + /* 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; @@ -399,12 +443,11 @@ ctf_arc_bufopen (const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, is_archive = 0; if ((fp = ctf_bufopen (ctfsect, symsect, strsect, errp)) == NULL) { - ctf_dprintf ("ctf_internal_open(): cannot open CTF: %s\n", - ctf_errmsg (*errp)); + ctf_err_warn (NULL, 0, *errp, _("ctf_arc_bufopen(): cannot open CTF")); return NULL; } } - return ctf_new_archive_internal (is_archive, arc, fp, symsect, strsect, + return ctf_new_archive_internal (is_archive, 0, arc, fp, symsect, strsect, errp); } @@ -421,24 +464,24 @@ ctf_arc_open_internal (const char *filename, int *errp) 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; } @@ -457,8 +500,7 @@ err_close: 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; } @@ -481,24 +523,33 @@ ctf_arc_close (ctf_archive_t *arc) 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); + 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; @@ -506,13 +557,13 @@ ctf_arc_open_by_name_internal (const struct ctf_archive *arc, 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 = (const char *) arc + le64toh (arc->ctfa_names); - modent = bsearch_r (name, modent, le64toh (arc->ctfa_nfiles), + modent = bsearch_r (name, modent, le64toh (arc->ctfa_ndicts), sizeof (struct ctf_archive_modent), search_modent_by_name, (void *) search_nametbl); @@ -525,30 +576,35 @@ ctf_arc_open_by_name_internal (const struct ctf_archive *arc, 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; } @@ -558,19 +614,19 @@ ctf_arc_open_by_name_sections (const ctf_archive_t *arc, *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; @@ -580,21 +636,87 @@ ctf_arc_open_by_name (const ctf_archive_t *arc, const char *name, int *errp) 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_file_t at the given ctfa_ctfs-relative offset, or NULL if +/* 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; +} + +/* 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)); @@ -606,10 +728,277 @@ ctf_arc_open_by_offset (const struct ctf_archive *arc, 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 @@ -625,7 +1014,7 @@ ctf_archive_raw_iter_internal (const struct ctf_archive *arc, + 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; @@ -654,65 +1043,127 @@ ctf_archive_raw_iter (const ctf_archive_t *arc, 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; + } + + /* 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_; - return func (arc->ctfi_file, _CTF_SECTION, data); + f = ctf_dict_open_cached ((ctf_archive_t *) wrapper, name_, errp); + return f; } #ifdef HAVE_MMAP @@ -745,7 +1196,8 @@ static int arc_mmap_writeout (int fd _libctf_unused_, void *header, 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; @@ -757,7 +1209,8 @@ static int arc_mmap_unmap (void *header, size_t headersz, const char **errmsg) 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; @@ -801,8 +1254,8 @@ static int arc_mmap_writeout (int fd, void *header, size_t headersz, 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; } @@ -811,7 +1264,7 @@ static int arc_mmap_writeout (int fd, void *header, size_t headersz, 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)