X-Git-Url: https://repo.jachan.dev/binutils.git/blobdiff_plain/ae41200ba807f4342ae7ea5334a29cd6f519b02c..HEAD:/libctf/ctf-archive.c diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c index 72cdef91ac..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. @@ -36,13 +36,19 @@ 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 *errp); + 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_DICTS are referenced by name: the names are passed in @@ -373,10 +379,40 @@ ctf_new_archive_internal (int is_archive, int unmap_on_close, 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). */ @@ -493,6 +529,9 @@ ctf_arc_close (ctf_archive_t *arc) } else 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) @@ -509,7 +548,8 @@ 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 *errp) + const char *name, int little_endian, + int *errp) { struct ctf_archive_modent *modent; const char *search_nametbl; @@ -537,7 +577,8 @@ ctf_dict_open_internal (const struct ctf_archive *arc, } return ctf_dict_open_by_offset (arc, symsect, strsect, - le64toh (modent->ctf_offset), errp); + le64toh (modent->ctf_offset), + little_endian, errp); } /* Return the ctf_dict_t with the given name, or NULL if none, setting 'err' if @@ -557,9 +598,13 @@ ctf_dict_open_sections (const ctf_archive_t *arc, { ctf_dict_t *ret; ret = ctf_dict_open_internal (arc->ctfi_archive, symsect, strsect, - name, errp); + 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; } @@ -594,13 +639,79 @@ ctf_dict_open (const ctf_archive_t *arc, const char *name, int *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; +} + +/* 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_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 *errp) + int little_endian, int *errp) { ctf_sect_t ctfsect; ctf_dict_t *fp; @@ -617,7 +728,11 @@ ctf_dict_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; } @@ -639,6 +754,25 @@ ctf_arc_open_by_name_sections (const ctf_archive_t *arc, 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) @@ -649,6 +783,222 @@ ctf_archive_count (const ctf_archive_t *wrapper) 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 @@ -693,72 +1043,37 @@ 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_dict_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_ndicts); i++) + while ((fp = ctf_archive_next (arc, &i, &name, 0, &err)) != NULL) { - const char *name; - - name = &nametbl[le64toh (modent[i].name_offset)]; - if ((f = ctf_dict_open_internal (arc, symsect, strsect, - name, &rc)) == NULL) - return rc; + int rc; - f->ctf_archive = (ctf_archive_t *) wrapper; - if ((rc = func (f, name, data)) != 0) + if ((rc = func (fp, name, data)) != 0) { - ctf_dict_close (f); + ctf_dict_close (fp); + ctf_next_destroy (i); return rc; } - - ctf_dict_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) -{ - const ctf_sect_t *symsect = &arc->ctfi_symsect; - const ctf_sect_t *strsect = &arc->ctfi_strsect; - - if (symsect->cts_name == NULL) - symsect = NULL; - if (strsect->cts_name == NULL) - strsect = NULL; - - if (arc->ctfi_is_archive) - return ctf_archive_iter_internal (arc, arc->ctfi_archive, symsect, strsect, - func, data); - - return func (arc->ctfi_dict, _CTF_SECTION, 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. Regardless of - whether they are skipped or not, the caller must ctf_import the parent if - need be. + 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. */ @@ -812,6 +1127,8 @@ ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **na if (!skip_parent) { wrapper->ctfi_dict->ctf_refcnt++; + if (name) + *name = _CTF_SECTION; return wrapper->ctfi_dict; } } @@ -822,9 +1139,6 @@ ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **na 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))) @@ -836,27 +1150,19 @@ ctf_archive_next (const ctf_archive_t *wrapper, ctf_next_t **it, const char **na 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)); name_ = &nametbl[le64toh (modent[i->ctn_n].name_offset)]; i->ctn_n++; - } while (skip_parent && strcmp (name_, _CTF_SECTION) == 0); + } + while (skip_parent && strcmp (name_, _CTF_SECTION) == 0); 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; }