diff options
-rw-r--r-- | include/ChangeLog | 5 | ||||
-rw-r--r-- | include/ctf-api.h | 4 | ||||
-rw-r--r-- | libctf/ChangeLog | 51 | ||||
-rw-r--r-- | libctf/ctf-archive.c | 180 | ||||
-rw-r--r-- | libctf/ctf-impl.h | 6 | ||||
-rw-r--r-- | libctf/ctf-lookup.c | 232 | ||||
-rw-r--r-- | libctf/ctf-open.c | 1 | ||||
-rw-r--r-- | libctf/libctf.ver | 6 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/conflicting-type-syms-a.c | 5 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/conflicting-type-syms-b.c | 5 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/conflicting-type-syms.c | 99 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/conflicting-type-syms.lk | 7 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/enum-symbol-obj.lk | 5 | ||||
-rw-r--r-- | libctf/testsuite/libctf-lookup/enum-symbol.c | 115 | ||||
-rw-r--r-- | libctf/testsuite/libctf-writable/symtypetab-nonlinker-writeout.c | 34 |
15 files changed, 576 insertions, 179 deletions
diff --git a/include/ChangeLog b/include/ChangeLog index a24c920..71f0beb 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,8 @@ +2021-02-17 Nick Alcock <nick.alcock@oracle.com> + + * ctf-api.h (ctf_arc_lookup_symbol_name): New. + (ctf_lookup_by_symbol_name): Likewise. + 2021-02-19 Nelson Chu <nelson.chu@sifive.com> PR 27158 diff --git a/include/ctf-api.h b/include/ctf-api.h index 6dd43aa..ce764df 100644 --- a/include/ctf-api.h +++ b/include/ctf-api.h @@ -338,6 +338,9 @@ extern void ctf_arc_close (ctf_archive_t *); extern ctf_dict_t *ctf_arc_lookup_symbol (ctf_archive_t *, unsigned long symidx, ctf_id_t *, int *errp); +extern ctf_dict_t *ctf_arc_lookup_symbol_name (ctf_archive_t *, + const char *name, + ctf_id_t *, int *errp); extern void ctf_arc_flush_caches (ctf_archive_t *); extern ctf_dict_t *ctf_dict_open (const ctf_archive_t *, const char *, int *); @@ -388,6 +391,7 @@ extern int ctf_func_type_args (ctf_dict_t *, ctf_id_t, uint32_t, ctf_id_t *); extern ctf_id_t ctf_lookup_by_name (ctf_dict_t *, const char *); extern ctf_id_t ctf_lookup_by_symbol (ctf_dict_t *, unsigned long); +extern ctf_id_t ctf_lookup_by_symbol_name (ctf_dict_t *, const char *); extern ctf_id_t ctf_symbol_next (ctf_dict_t *, ctf_next_t **, const char **name, int functions); extern ctf_id_t ctf_lookup_variable (ctf_dict_t *, const char *); diff --git a/libctf/ChangeLog b/libctf/ChangeLog index 7a69476..acf3c46 100644 --- a/libctf/ChangeLog +++ b/libctf/ChangeLog @@ -1,3 +1,54 @@ +2021-02-17 Nick Alcock <nick.alcock@oracle.com> + + * ctf-impl.h (ctf_dict_t) <ctf_symhash>: New. + <ctf_symhash_latest>: Likewise. + (struct ctf_archive_internal) <ctfi_crossdict_cache>: New. + <ctfi_symnamedicts>: New. + <ctfi_syms>: Remove. + (ctf_lookup_symbol_name): Remove. + * ctf-lookup.c (ctf_lookup_symbol_name): Propagate errors from + parent properly. Make static. + (ctf_lookup_symbol_idx): New, linear search for the symbol name, + cached in the crossdict cache's ctf_symhash (if available), or + this dict's (otherwise). + (ctf_try_lookup_indexed): Allow the symname to be passed in. + (ctf_lookup_by_symbol): Turn into a wrapper around... + (ctf_lookup_by_sym_or_name): ... this, supporting name lookup too, + using ctf_lookup_symbol_idx in non-writable dicts. Special-case + name lookup in dynamic dicts without reported symbols, which have + no symtab or dynsymidx but where name lookup should still work. + (ctf_lookup_by_symbol_name): New, another wrapper. + * ctf-archive.c (enosym): Note that this is present in + ctfi_symnamedicts too. + (ctf_arc_close): Adjust for removal of ctfi_syms. Free the + ctfi_symnamedicts. + (ctf_arc_flush_caches): Likewise. + (ctf_dict_open_cached): Memoize the first cached dict in the + crossdict cache. + (ctf_arc_lookup_symbol): Turn into a wrapper around... + (ctf_arc_lookup_sym_or_name): ... this. No longer cache + ctf_id_t lookups: just call ctf_lookup_by_symbol as needed (but + still cache the dicts those lookups succeed in). Add + lookup-by-name support, with dicts of successful lookups cached in + ctfi_symnamedicts. Refactor the caching code a bit. + (ctf_arc_lookup_symbol_name): New, another wrapper. + * ctf-open.c (ctf_dict_close): Free the ctf_symhash. + * libctf.ver (LIBCTF_1.2): New version. Add + ctf_lookup_by_symbol_name, ctf_arc_lookup_symbol_name. + * testsuite/libctf-lookup/enum-symbol.c (main): Use + ctf_arc_lookup_symbol rather than looking up the name ourselves. + Fish it out repeatedly, to make sure that symbol caching isn't + broken. + (symidx_64): Remove. + (symidx_32): Remove. + * testsuite/libctf-lookup/enum-symbol-obj.lk: Test symbol lookup + in an unlinked object file (indexed symtypetab sections only). + * testsuite/libctf-writable/symtypetab-nonlinker-writeout.c + (try_maybe_reporting): Check symbol types via + ctf_lookup_by_symbol_name as well as ctf_symbol_next. + * testsuite/libctf-lookup/conflicting-type-syms.*: New test of + lookups in a multi-dict archive. + 2021-02-20 Alan Modra <amodra@gmail.com> * testsuite/config/default.exp (ld_L_opt): Define. diff --git a/libctf/ctf-archive.c b/libctf/ctf-archive.c index 193fc4d..6d9c75c 100644 --- a/libctf/ctf-archive.c +++ b/libctf/ctf-archive.c @@ -46,8 +46,8 @@ static int arc_mmap_writeout (int fd, void *header, size_t headersz, 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. */ +/* 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 @@ -529,8 +529,8 @@ ctf_arc_close (ctf_archive_t *arc) } else ctf_dict_close (arc->ctfi_dict); - free (arc->ctfi_syms); 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); @@ -645,8 +645,9 @@ 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. */ +/* 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) { @@ -678,6 +679,9 @@ ctf_dict_open_cached (ctf_archive_t *arc, const char *name, int *errp) goto oom; fp->ctf_refcnt++; + if (arc->ctfi_crossdict_cache == NULL) + arc->ctfi_crossdict_cache = fp; + return fp; oom: @@ -693,11 +697,12 @@ void ctf_arc_flush_caches (ctf_archive_t *wrapper) { free (wrapper->ctfi_symdicts); - free (wrapper->ctfi_syms); + free (wrapper->ctfi_symnamedicts); ctf_dynhash_destroy (wrapper->ctfi_dicts); wrapper->ctfi_symdicts = NULL; - wrapper->ctfi_syms = 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 @@ -778,31 +783,46 @@ 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. +/* 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). */ -ctf_dict_t * -ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx, - ctf_id_t *typep, int *errp) +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 ((type = ctf_lookup_by_symbol (wrapper->ctfi_dict, symidx)) == CTF_ERR) + if (!symname) { - if (errp) - *errp = ctf_errno (wrapper->ctfi_dict); - return NULL; + 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; @@ -820,27 +840,28 @@ ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx, 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. */ + /* 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_syms) + if (!wrapper->ctfi_symdicts) { - if ((wrapper->ctfi_syms = calloc (wrapper->ctfi_symsect.cts_size - / wrapper->ctfi_symsect.cts_entsize, - sizeof (ctf_id_t))) == NULL) + 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_symdicts) + if (!wrapper->ctfi_symnamedicts) { - if ((wrapper->ctfi_symdicts = calloc (wrapper->ctfi_symsect.cts_size - / wrapper->ctfi_symsect.cts_entsize, - sizeof (ctf_dict_t *))) == NULL) + if ((wrapper->ctfi_symnamedicts = ctf_dynhash_create (ctf_hash_string, + ctf_hash_eq_string, + free, NULL)) == NULL) { if (errp) *errp = ENOMEM; @@ -848,22 +869,38 @@ ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx, } } - /* Perhaps it's cached. */ - if (wrapper->ctfi_symdicts[symidx] != 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 (wrapper->ctfi_symdicts[symidx] == &enosym) + if (symname) + fp = (ctf_dict_t *) fpkey; + else + fp = wrapper->ctfi_symdicts[symidx]; + + if (fp == &enosym) + goto no_sym; + + if (symname) { - if (errp) - *errp = ECTF_NOTYPEDAT; - if (typep) - *typep = CTF_ERR; - return NULL; + 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 = wrapper->ctfi_syms[symidx]; - wrapper->ctfi_symdicts[symidx]->ctf_refcnt++; - return wrapper->ctfi_symdicts[symidx]; + *typep = type; + fp->ctf_refcnt++; + return fp; } /* Not cached: find it and cache it. We must track open errors ourselves even @@ -882,16 +919,36 @@ ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx, while ((fp = ctf_archive_next (wrapper, &i, &name, 0, local_errp)) != NULL) { - if ((type = ctf_lookup_by_symbol (fp, symidx)) != CTF_ERR) + if (!symname) { - wrapper->ctfi_syms[symidx] = type; - wrapper->ctfi_symdicts[symidx] = fp; - ctf_next_destroy (i); + 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) @@ -899,11 +956,25 @@ ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx, ctf_next_destroy (i); return NULL; } + /* Don't leak end-of-iteration to the caller. */ *local_errp = 0; - wrapper->ctfi_symdicts[symidx] = &enosym; + 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) @@ -911,6 +982,23 @@ ctf_arc_lookup_symbol (ctf_archive_t *wrapper, unsigned long symidx, 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 diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h index 0a508eb..a6e1da5 100644 --- a/libctf/ctf-impl.h +++ b/libctf/ctf-impl.h @@ -382,6 +382,8 @@ struct ctf_dict ctf_sect_t ctf_symtab; /* Symbol table from object file. */ ctf_sect_t ctf_strtab; /* String table from object file. */ int ctf_symsect_little_endian; /* Endianness of the ctf_symtab. */ + ctf_dynhash_t *ctf_symhash; /* (partial) hash, symsect name -> idx. */ + size_t ctf_symhash_latest; /* Amount of symsect scanned so far. */ ctf_dynhash_t *ctf_prov_strtab; /* Maps provisional-strtab offsets to names. */ ctf_dynhash_t *ctf_syn_ext_strtab; /* Maps ext-strtab offsets to names. */ @@ -508,8 +510,9 @@ struct ctf_archive_internal ctf_dict_t *ctfi_dict; struct ctf_archive *ctfi_archive; ctf_dynhash_t *ctfi_dicts; /* Dicts we have opened and cached. */ + ctf_dict_t *ctfi_crossdict_cache; /* Cross-dict caching. */ ctf_dict_t **ctfi_symdicts; /* Array of index -> ctf_dict_t *. */ - ctf_id_t *ctfi_syms; /* Array of index -> ctf_id_t. */ + ctf_dynhash_t *ctfi_symnamedicts; /* Hash of name -> ctf_dict_t *. */ ctf_sect_t ctfi_symsect; int ctfi_symsect_little_endian; /* -1 for unknown / do not set. */ ctf_sect_t ctfi_strsect; @@ -784,7 +787,6 @@ extern ctf_link_sym_t *ctf_elf32_to_link_sym (ctf_dict_t *fp, ctf_link_sym_t *ds const Elf32_Sym *src, uint32_t symidx); extern ctf_link_sym_t *ctf_elf64_to_link_sym (ctf_dict_t *fp, ctf_link_sym_t *dst, const Elf64_Sym *src, uint32_t symidx); -extern const char *ctf_lookup_symbol_name (ctf_dict_t *fp, unsigned long symidx); /* Variables, all underscore-prepended. */ diff --git a/libctf/ctf-lookup.c b/libctf/ctf-lookup.c index 72f6a2a..6e17e5f 100644 --- a/libctf/ctf-lookup.c +++ b/libctf/ctf-lookup.c @@ -459,7 +459,7 @@ ctf_symidx_sort (ctf_dict_t *fp, uint32_t *idx, size_t *nidx, /* Given a symbol index, return the name of that symbol from the table provided by ctf_link_shuffle_syms, or failing that from the secondary string table, or the null string. */ -const char * +static const char * ctf_lookup_symbol_name (ctf_dict_t *fp, unsigned long symidx) { const ctf_sect_t *sp = &fp->ctf_symtab; @@ -512,7 +512,13 @@ ctf_lookup_symbol_name (ctf_dict_t *fp, unsigned long symidx) try_parent: if (fp->ctf_parent) - return ctf_lookup_symbol_name (fp->ctf_parent, symidx); + { + const char *ret; + ret = ctf_lookup_symbol_name (fp->ctf_parent, symidx); + if (ret == NULL) + ctf_set_errno (fp, ctf_errno (fp->ctf_parent)); + return ret; + } else { ctf_set_errno (fp, err); @@ -520,6 +526,116 @@ ctf_lookup_symbol_name (ctf_dict_t *fp, unsigned long symidx) } } +/* Given a symbol name, return the index of that symbol, or -1 on error or if + not found. */ +static unsigned long +ctf_lookup_symbol_idx (ctf_dict_t *fp, const char *symname) +{ + const ctf_sect_t *sp = &fp->ctf_symtab; + ctf_link_sym_t sym; + void *known_idx; + int err; + ctf_dict_t *cache = fp; + + if (fp->ctf_dynsyms) + { + err = EINVAL; + + ctf_link_sym_t *symp; + + if ((symp = ctf_dynhash_lookup (fp->ctf_dynsyms, symname)) == NULL) + goto try_parent; + + return symp->st_symidx; + } + + err = ECTF_NOSYMTAB; + if (sp->cts_data == NULL) + goto try_parent; + + /* First, try a hash lookup to see if we have already spotted this symbol + during a past iteration: create the hash first if need be. The lifespan + of the strings is equal to the lifespan of the cts_data, so we don't + need to strdup them. If this dict was opened as part of an archive, + and this archive has designed a crossdict_cache to cache results that + are the same across all dicts in an archive, use it. */ + + if (fp->ctf_archive && fp->ctf_archive->ctfi_crossdict_cache) + cache = fp->ctf_archive->ctfi_crossdict_cache; + + if (!cache->ctf_symhash) + if ((cache->ctf_symhash = ctf_dynhash_create (ctf_hash_string, + ctf_hash_eq_string, + NULL, NULL)) == NULL) + goto oom; + + if (ctf_dynhash_lookup_kv (cache->ctf_symhash, symname, NULL, &known_idx)) + return (unsigned long) (uintptr_t) known_idx; + + /* Hash lookup unsuccessful: linear search, populating the hashtab for later + lookups as we go. */ + + for (; cache->ctf_symhash_latest < sp->cts_size / sp->cts_entsize; + cache->ctf_symhash_latest++) + { + switch (sp->cts_entsize) + { + case sizeof (Elf64_Sym): + { + Elf64_Sym *symp = (Elf64_Sym *) sp->cts_data; + ctf_elf64_to_link_sym (fp, &sym, &symp[cache->ctf_symhash_latest], + cache->ctf_symhash_latest); + if (!ctf_dynhash_lookup_kv (cache->ctf_symhash, sym.st_name, + NULL, NULL)) + if (ctf_dynhash_cinsert (cache->ctf_symhash, sym.st_name, + (const void *) (uintptr_t) + cache->ctf_symhash_latest) < 0) + goto oom; + if (strcmp (sym.st_name, symname) == 0) + return cache->ctf_symhash_latest++; + } + break; + case sizeof (Elf32_Sym): + { + Elf32_Sym *symp = (Elf32_Sym *) sp->cts_data; + ctf_elf32_to_link_sym (fp, &sym, &symp[cache->ctf_symhash_latest], + cache->ctf_symhash_latest); + if (!ctf_dynhash_lookup_kv (cache->ctf_symhash, sym.st_name, + NULL, NULL)) + if (ctf_dynhash_cinsert (cache->ctf_symhash, sym.st_name, + (const void *) (uintptr_t) + cache->ctf_symhash_latest) < 0) + goto oom; + if (strcmp (sym.st_name, symname) == 0) + return cache->ctf_symhash_latest++; + } + break; + default: + ctf_set_errno (fp, ECTF_SYMTAB); + return (unsigned long) -1; + } + } + + /* Searched everything, still not found. */ + + return (unsigned long) -1; + + try_parent: + if (fp->ctf_parent) + return ctf_lookup_symbol_idx (fp->ctf_parent, symname); + else + { + ctf_set_errno (fp, err); + return (unsigned long) -1; + } +oom: + ctf_set_errno (fp, ENOMEM); + ctf_err_warn (fp, 0, ENOMEM, _("cannot allocate memory for symbol " + "lookup hashtab")); + return (unsigned long) -1; + +} + /* Iterate over all symbols with types: if FUNC, function symbols, otherwise, data symbols. The name argument is not optional. The return order is arbitrary, though is likely to be in symbol index or name order. You can @@ -664,20 +780,24 @@ ctf_lookup_idx_name (const void *key_, const void *idx_) return (strcmp (key->clik_name, ctf_strptr (key->clik_fp, key->clik_names[*idx]))); } -/* Given a symbol number, look up that symbol in the function or object - index table (which must exist). Return 0 if not found there (or pad). */ +/* Given a symbol name or (failing that) number, look up that symbol in the + function or object index table (which must exist). Return 0 if not found + there (or pad). */ static ctf_id_t -ctf_try_lookup_indexed (ctf_dict_t *fp, unsigned long symidx, int is_function) +ctf_try_lookup_indexed (ctf_dict_t *fp, unsigned long symidx, + const char *symname, int is_function) { - const char *symname = ctf_lookup_symbol_name (fp, symidx); struct ctf_header *hp = fp->ctf_header; uint32_t *symtypetab; uint32_t *names; uint32_t *sxlate; size_t nidx; - ctf_dprintf ("Looking up type of object with symtab idx %lx (%s) in " + if (symname == NULL) + symname = ctf_lookup_symbol_name (fp, symidx); + + ctf_dprintf ("Looking up type of object with symtab idx %lx or name %s in " "indexed symtypetab\n", symidx, symname); if (symname[0] == '\0') @@ -745,13 +865,15 @@ ctf_try_lookup_indexed (ctf_dict_t *fp, unsigned long symidx, int is_function) return symtypetab[*idx]; } -/* Given a symbol table index, return the type of the function or data object - described by the corresponding entry in the symbol table. We can only return - symbols in read-only dicts and in dicts for which ctf_link_shuffle_syms has - been called to assign symbol indexes to symbol names. */ +/* Given a symbol name or (if NULL) symbol index, return the type of the + function or data object described by the corresponding entry in the symbol + table. We can only return symbols in read-only dicts and in dicts for which + ctf_link_shuffle_syms has been called to assign symbol indexes to symbol + names. */ -ctf_id_t -ctf_lookup_by_symbol (ctf_dict_t *fp, unsigned long symidx) +static ctf_id_t +ctf_lookup_by_sym_or_name (ctf_dict_t *fp, unsigned long symidx, + const char *symname) { const ctf_sect_t *sp = &fp->ctf_symtab; ctf_id_t type = 0; @@ -762,38 +884,62 @@ ctf_lookup_by_symbol (ctf_dict_t *fp, unsigned long symidx) { const ctf_link_sym_t *sym; - ctf_dprintf ("Looking up type of object with symtab idx %lx in " - "writable dict symtypetab\n", symidx); + if (symname) + ctf_dprintf ("Looking up type of object with symname %s in " + "writable dict symtypetab\n", symname); + else + ctf_dprintf ("Looking up type of object with symtab idx %lx in " + "writable dict symtypetab\n", symidx); /* The dict must be dynamic. */ if (!ctf_assert (fp, fp->ctf_flags & LCTF_RDWR)) return CTF_ERR; - err = EINVAL; - if (symidx > fp->ctf_dynsymmax) - goto try_parent; + /* No name? Need to look it up. */ + if (!symname) + { + err = EINVAL; + if (symidx > fp->ctf_dynsymmax) + goto try_parent; - sym = fp->ctf_dynsymidx[symidx]; - err = ECTF_NOTYPEDAT; - if (!sym || (sym->st_shndx != STT_OBJECT && sym->st_shndx != STT_FUNC)) - goto try_parent; + sym = fp->ctf_dynsymidx[symidx]; + err = ECTF_NOTYPEDAT; + if (!sym || (sym->st_shndx != STT_OBJECT && sym->st_shndx != STT_FUNC)) + goto try_parent; - if (!ctf_assert (fp, !sym->st_nameidx_set)) - return CTF_ERR; + if (!ctf_assert (fp, !sym->st_nameidx_set)) + return CTF_ERR; + symname = sym->st_name; + } if (fp->ctf_objthash == NULL || ((type = (ctf_id_t) (uintptr_t) - ctf_dynhash_lookup (fp->ctf_objthash, sym->st_name)) == 0)) + ctf_dynhash_lookup (fp->ctf_objthash, symname)) == 0)) { if (fp->ctf_funchash == NULL || ((type = (ctf_id_t) (uintptr_t) - ctf_dynhash_lookup (fp->ctf_funchash, sym->st_name)) == 0)) + ctf_dynhash_lookup (fp->ctf_funchash, symname)) == 0)) goto try_parent; } return type; } + /* Lookup by name in a dynamic dict: just do it directly. */ + if (symname && fp->ctf_flags & LCTF_RDWR) + { + if (fp->ctf_objthash == NULL + || ((type = (ctf_id_t) (uintptr_t) + ctf_dynhash_lookup (fp->ctf_objthash, symname)) == 0)) + { + if (fp->ctf_funchash == NULL + || ((type = (ctf_id_t) (uintptr_t) + ctf_dynhash_lookup (fp->ctf_funchash, symname)) == 0)) + goto try_parent; + } + return type; + } + err = ECTF_NOSYMTAB; if (sp->cts_data == NULL) goto try_parent; @@ -801,17 +947,17 @@ ctf_lookup_by_symbol (ctf_dict_t *fp, unsigned long symidx) /* This covers both out-of-range lookups and a dynamic dict which hasn't been shuffled yet. */ err = EINVAL; - if (symidx >= fp->ctf_nsyms) + if (symname == NULL && symidx >= fp->ctf_nsyms) goto try_parent; if (fp->ctf_objtidx_names) { - if ((type = ctf_try_lookup_indexed (fp, symidx, 0)) == CTF_ERR) + if ((type = ctf_try_lookup_indexed (fp, symidx, symname, 0)) == CTF_ERR) return CTF_ERR; /* errno is set for us. */ } if (type == 0 && fp->ctf_funcidx_names) { - if ((type = ctf_try_lookup_indexed (fp, symidx, 1)) == CTF_ERR) + if ((type = ctf_try_lookup_indexed (fp, symidx, symname, 1)) == CTF_ERR) return CTF_ERR; /* errno is set for us. */ } if (type != 0) @@ -825,6 +971,10 @@ ctf_lookup_by_symbol (ctf_dict_t *fp, unsigned long symidx) ctf_dprintf ("Looking up object type %lx in 1:1 dict symtypetab\n", symidx); + if (symname != NULL) + if ((symidx = ctf_lookup_symbol_idx (fp, symname)) == (unsigned long) -1) + goto try_parent; + if (fp->ctf_sxlate[symidx] == -1u) goto try_parent; @@ -836,11 +986,33 @@ ctf_lookup_by_symbol (ctf_dict_t *fp, unsigned long symidx) return type; try_parent: if (fp->ctf_parent) - return ctf_lookup_by_symbol (fp->ctf_parent, symidx); + { + ctf_id_t ret = ctf_lookup_by_sym_or_name (fp->ctf_parent, symidx, + symname); + if (ret == CTF_ERR) + ctf_set_errno (fp, ctf_errno (fp->ctf_parent)); + return ret; + } else return (ctf_set_errno (fp, err)); } +/* Given a symbol table index, return the type of the function or data object + described by the corresponding entry in the symbol table. */ +ctf_id_t +ctf_lookup_by_symbol (ctf_dict_t *fp, unsigned long symidx) +{ + return ctf_lookup_by_sym_or_name (fp, symidx, NULL); +} + +/* Given a symbol name, return the type of the function or data object described + by the corresponding entry in the symbol table. */ +ctf_id_t +ctf_lookup_by_symbol_name (ctf_dict_t *fp, const char *symname) +{ + return ctf_lookup_by_sym_or_name (fp, 0, symname); +} + /* Given a symbol table index, return the info for the function described by the corresponding entry in the symbol table, which may be a function symbol or may be a data symbol that happens to be a function pointer. */ diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c index 67d9f84..c2d9a33 100644 --- a/libctf/ctf-open.c +++ b/libctf/ctf-open.c @@ -1737,6 +1737,7 @@ ctf_dict_close (ctf_dict_t *fp) } ctf_dynhash_destroy (fp->ctf_dvhash); + ctf_dynhash_destroy (fp->ctf_symhash); free (fp->ctf_funcidx_sxlate); free (fp->ctf_objtidx_sxlate); ctf_dynhash_destroy (fp->ctf_objthash); diff --git a/libctf/libctf.ver b/libctf/libctf.ver index 2137c5b..8c362f3 100644 --- a/libctf/libctf.ver +++ b/libctf/libctf.ver @@ -196,3 +196,9 @@ LIBCTF_1.1 { ctf_symsect_endianness; ctf_arc_symsect_endianness; } LIBCTF_1.0; + +LIBCTF_1.2 { + global: + ctf_lookup_by_symbol_name; + ctf_arc_lookup_symbol_name; +} LIBCTF_1.1; diff --git a/libctf/testsuite/libctf-lookup/conflicting-type-syms-a.c b/libctf/testsuite/libctf-lookup/conflicting-type-syms-a.c new file mode 100644 index 0000000..6541487 --- /dev/null +++ b/libctf/testsuite/libctf-lookup/conflicting-type-syms-a.c @@ -0,0 +1,5 @@ +typedef long a_t; +typedef long b_t; + +a_t *a; +b_t ignore2; diff --git a/libctf/testsuite/libctf-lookup/conflicting-type-syms-b.c b/libctf/testsuite/libctf-lookup/conflicting-type-syms-b.c new file mode 100644 index 0000000..e458021 --- /dev/null +++ b/libctf/testsuite/libctf-lookup/conflicting-type-syms-b.c @@ -0,0 +1,5 @@ +typedef long a_t; +typedef long b_t; + +a_t b; +b_t ignore1; diff --git a/libctf/testsuite/libctf-lookup/conflicting-type-syms.c b/libctf/testsuite/libctf-lookup/conflicting-type-syms.c new file mode 100644 index 0000000..ffe6983 --- /dev/null +++ b/libctf/testsuite/libctf-lookup/conflicting-type-syms.c @@ -0,0 +1,99 @@ +#include "config.h" +#include <ctf-api.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int +main (int argc, char *argv[]) +{ + ctf_archive_t *ctf; + ctf_dict_t *a_fp, *ignore1_fp, *b_fp, *ignore2_fp, *tmp_fp; + int err; + ctf_id_t a, b, ignore1, ignore2, tmp; + char *foo; + ctf_next_t *i = NULL; + const char *name; + int val; + + if (argc != 2) + { + fprintf (stderr, "Syntax: %s PROGRAM\n", argv[0]); + exit(1); + } + + if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL) + goto open_err; + + /* Fish out each symbol in turn: also try to fish out a nonexistent one. */ + + if ((a_fp = ctf_arc_lookup_symbol_name (ctf, "a", &a, &err)) == NULL) + goto sym_err; + printf ("Type of a is %s\n", foo = ctf_type_aname (a_fp, a)); + + if ((b_fp = ctf_arc_lookup_symbol_name (ctf, "b", &b, &err)) == NULL) + goto sym_err; + printf ("Type of b is %s\n", foo = ctf_type_aname (b_fp, b)); + + if ((ignore1_fp = ctf_arc_lookup_symbol_name (ctf, "ignore1", &ignore1, &err)) == NULL) + goto sym_err; + printf ("Type of ignore1 is %s\n", foo = ctf_type_aname (ignore1_fp, ignore1)); + + if ((ignore2_fp = ctf_arc_lookup_symbol_name (ctf, "ignore2", &ignore2, &err)) == NULL) + goto sym_err; + printf ("Type of ignore2 is %s\n", foo = ctf_type_aname (ignore2_fp, ignore1)); + + /* Try a call in just-get-the-dict mode and make sure it doesn't fail. */ + if ((tmp_fp = ctf_arc_lookup_symbol_name (ctf, "ignore2", NULL, &err)) == NULL) + goto sym_err; + ctf_dict_close (tmp_fp); + + /* Make sure failures fail. */ + if ((ctf_arc_lookup_symbol_name (ctf, "nonexistent", NULL, &err) != NULL) + || err != ECTF_NOTYPEDAT) + goto nosym_err; + + /* Fish them out again to check the caching layer. */ + if (((tmp_fp = ctf_arc_lookup_symbol_name (ctf, "a", &tmp, &err)) != a_fp) + || (tmp != a)) + goto sym_cache_err; + ctf_dict_close (tmp_fp); + + if (((tmp_fp = ctf_arc_lookup_symbol_name (ctf, "b", &tmp, &err)) != b_fp) + || (tmp != b)) + goto sym_cache_err; + ctf_dict_close (tmp_fp); + + if (((tmp_fp = ctf_arc_lookup_symbol_name (ctf, "ignore1", &tmp, &err)) != ignore1_fp) + || (tmp != ignore1)) + goto sym_cache_err; + ctf_dict_close (tmp_fp); + + if (((tmp_fp = ctf_arc_lookup_symbol_name (ctf, "ignore2", &tmp, &err)) != ignore2_fp) + || (tmp != ignore2)) + goto sym_cache_err; + ctf_dict_close (tmp_fp); + + ctf_dict_close (a_fp); + ctf_dict_close (b_fp); + ctf_dict_close (ignore1_fp); + ctf_dict_close (ignore2_fp); + ctf_close (ctf); + + return 0; + + open_err: + fprintf (stderr, "%s: cannot open: %s\n", argv[0], ctf_errmsg (err)); + return 1; + sym_err: + fprintf (stderr, "%s: Symbol lookup error: %s\n", argv[0], ctf_errmsg (err)); + return 1; + nosym_err: + fprintf (stderr, "%s: Nonexistent symbol lookup unexpected error: %s\n", argv[0], + ctf_errmsg (err)); + return 1; + sym_cache_err: + fprintf (stderr, "%s: Symbol re-lookup error (caching bug): %s\n", argv[0], + ctf_errmsg (err)); + return 1; +} diff --git a/libctf/testsuite/libctf-lookup/conflicting-type-syms.lk b/libctf/testsuite/libctf-lookup/conflicting-type-syms.lk new file mode 100644 index 0000000..09b41df --- /dev/null +++ b/libctf/testsuite/libctf-lookup/conflicting-type-syms.lk @@ -0,0 +1,7 @@ +# lookup: conflicting-type-syms.c +# source: conflicting-type-syms-a.c +# source: conflicting-type-syms-b.c +Type of a is a_t \* +Type of b is a_t +Type of ignore1 is b_t +Type of ignore2 is b_t diff --git a/libctf/testsuite/libctf-lookup/enum-symbol-obj.lk b/libctf/testsuite/libctf-lookup/enum-symbol-obj.lk new file mode 100644 index 0000000..548d67b --- /dev/null +++ b/libctf/testsuite/libctf-lookup/enum-symbol-obj.lk @@ -0,0 +1,5 @@ +# lookup: enum-symbol.c +# source: enum-symbol-ctf.c +red1 has value 0 +green1 has value 1 +blue1 has value 2 diff --git a/libctf/testsuite/libctf-lookup/enum-symbol.c b/libctf/testsuite/libctf-lookup/enum-symbol.c index 4f63b61..c67478f 100644 --- a/libctf/testsuite/libctf-lookup/enum-symbol.c +++ b/libctf/testsuite/libctf-lookup/enum-symbol.c @@ -1,71 +1,19 @@ #include "config.h" -#include <bfd.h> -#include <elf.h> #include <ctf-api.h> -#include <swap.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -ssize_t symidx_64 (ctf_sect_t *s, ctf_sect_t *strsect, int little_endian, const char *name) -{ - const char *strs = (const char *) strsect->cts_data; - size_t i; - Elf64_Sym *sym = (Elf64_Sym *) s->cts_data; - for (i = 0; i < s->cts_size / s->cts_entsize; i++, sym++) - { - Elf64_Word nameoff = sym->st_name; -#ifdef WORDS_BIGENDIAN - if (little_endian) - swap_thing (nameoff); -#else - if (!little_endian) - swap_thing (nameoff); -#endif - if (strcmp (strs + nameoff, name) == 0) - return i; - } - return -1; -} - -ssize_t symidx_32 (ctf_sect_t *s, ctf_sect_t *strsect, int little_endian, const char *name) -{ - const char *strs = (const char *) strsect->cts_data; - size_t i; - Elf32_Sym *sym = (Elf32_Sym *) s->cts_data; - for (i = 0; i < s->cts_size / s->cts_entsize; i++, sym++) - { - Elf32_Word nameoff = sym->st_name; -#ifdef WORDS_BIGENDIAN - if (little_endian) - swap_thing (nameoff); -#else - if (!little_endian) - swap_thing (nameoff); -#endif - if (strcmp (strs + nameoff, name) == 0) - return i; - } - return -1; -} - int main (int argc, char *argv[]) { - ctf_dict_t *fp; - bfd *abfd; ctf_archive_t *ctf; - ctf_sect_t symsect; - ctf_sect_t strsect; - ssize_t symidx; + ctf_dict_t *fp, *tmp_fp; int err; - ctf_id_t type; + ctf_id_t type, tmp; ctf_next_t *i = NULL; const char *name; int val; - int little_endian; - - ssize_t (*get_sym) (ctf_sect_t *s, ctf_sect_t *strsect, int little_endian, const char *name); if (argc != 2) { @@ -73,53 +21,12 @@ main (int argc, char *argv[]) exit(1); } - /* Figure out the endianness of the symtab(s). */ - if ((abfd = bfd_openr (argv[1], NULL)) == NULL - || !bfd_check_format (abfd, bfd_object)) - goto bfd_open_err; - little_endian = bfd_little_endian (abfd); - bfd_close_all_done (abfd); - if ((ctf = ctf_open (argv[1], NULL, &err)) == NULL) goto open_err; - if ((fp = ctf_dict_open (ctf, NULL, &err)) == NULL) - goto open_err; - - symsect = ctf_getsymsect (fp); - strsect = ctf_getstrsect (fp); - if (symsect.cts_data == NULL - || strsect.cts_data == NULL) - { - fprintf (stderr, "%s: no symtab or strtab\n", argv[0]); - return 1; - } - - ctf_dict_close (fp); - - if (symsect.cts_entsize != sizeof (Elf64_Sym) && - symsect.cts_entsize != sizeof (Elf32_Sym)) - { - fprintf (stderr, "%s: unknown symsize: %lx\n", argv[0], - symsect.cts_size); - return 1; - } - - switch (symsect.cts_entsize) - { - case sizeof (Elf64_Sym): get_sym = symidx_64; break; - case sizeof (Elf32_Sym): get_sym = symidx_32; break; - } - - if ((symidx = get_sym (&symsect, &strsect, little_endian, "primary1")) < 0) - { - fprintf (stderr, "%s: symbol not found: primary1\n", argv[0]); - return 1; - } - - /* Fish it out, then fish out all its enumerand/value pairs. */ + /* Fish out the enumerator, then fish out all its enumerand/value pairs. */ - if ((fp = ctf_arc_lookup_symbol (ctf, symidx, &type, &err)) == NULL) + if ((fp = ctf_arc_lookup_symbol_name (ctf, "primary1", &type, &err)) == NULL) goto sym_err; while ((name = ctf_enum_next (fp, type, &i, &val)) != NULL) @@ -129,21 +36,27 @@ main (int argc, char *argv[]) if (ctf_errno (fp) != ECTF_NEXT_END) goto nerr; + /* Fish it out again to check the caching layer. */ + if (((tmp_fp = ctf_arc_lookup_symbol_name (ctf, "primary1", &tmp, &err)) != fp) + || (tmp != type)) + goto sym_cache_err; + + ctf_dict_close (tmp_fp); ctf_dict_close (fp); ctf_close (ctf); return 0; - bfd_open_err: - fprintf (stderr, "%s: cannot open: %s\n", argv[0], bfd_errmsg (bfd_get_error ())); - return 1; - open_err: fprintf (stderr, "%s: cannot open: %s\n", argv[0], ctf_errmsg (err)); return 1; sym_err: fprintf (stderr, "%s: Symbol lookup error: %s\n", argv[0], ctf_errmsg (err)); return 1; + sym_cache_err: + fprintf (stderr, "%s: Symbol re-lookup error (caching bug): %s\n", argv[0], + ctf_errmsg (err)); + return 1; err: fprintf (stderr, "Lookup failed: %s\n", ctf_errmsg (ctf_errno (fp))); return 1; diff --git a/libctf/testsuite/libctf-writable/symtypetab-nonlinker-writeout.c b/libctf/testsuite/libctf-writable/symtypetab-nonlinker-writeout.c index d339963..98144de 100644 --- a/libctf/testsuite/libctf-writable/symtypetab-nonlinker-writeout.c +++ b/libctf/testsuite/libctf-writable/symtypetab-nonlinker-writeout.c @@ -98,6 +98,21 @@ try_maybe_reporting (int report) if (ctf_errno (fp) != ECTF_NEXT_END) goto iter_err; + /* Look up all the symbols by name and make sure that works. */ + + if (ctf_lookup_by_symbol_name (fp, "data_a") != base2) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "data_b") != base3) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "data_c") != base) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "func_a") != func2) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "func_b") != func3) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "func_c") != func) + goto lookup_syms_err; + /* Possibly report some but not all of the symbols, as if we are a linker (no real program would do this without using the ctf_link APIs, but it's not *prohibited*, just useless, and if they do we don't want things to @@ -121,6 +136,21 @@ try_maybe_reporting (int report) if (report_sym (fp, &sym, "func_c", 4, 2) < 0 || report_sym (fp, &sym, "func_a", 5, 2) < 0) goto report_err; + + /* Look up all the symbols by name now we have reported symbols. */ + + if (ctf_lookup_by_symbol_name (fp, "data_a") != base2) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "data_b") != base3) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "data_c") != base) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "func_a") != func2) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "func_b") != func3) + goto lookup_syms_err; + if (ctf_lookup_by_symbol_name (fp, "func_c") != func) + goto lookup_syms_err; } /* Write out, to memory. */ @@ -203,6 +233,10 @@ try_maybe_reporting (int report) expected_overshoot_err: fprintf (stderr, "Too many symbols in post-writeout comparison\n"); exit (1); + lookup_syms_err: + fprintf (stderr, "Explicit lookup of symbols by name failed: %s\n", + ctf_errmsg (ctf_errno (fp))); + exit (1); expected_compar_err: fprintf (stderr, "Non-dynamic iteration comparison failure: %s " "(type %lx): expected %s (type %lx)\n", symname, symtype, |