+/* Compare the given input string and length against a table of known C storage
+ qualifier keywords. We just ignore these in ctf_lookup_by_name, below. To
+ do this quickly, we use a pre-computed Perfect Hash Function similar to the
+ technique originally described in the classic paper:
+
+ R.J. Cichelli, "Minimal Perfect Hash Functions Made Simple",
+ Communications of the ACM, Volume 23, Issue 1, January 1980, pp. 17-19.
+
+ For an input string S of length N, we use hash H = S[N - 1] + N - 105, which
+ for the current set of qualifiers yields a unique H in the range [0 .. 20].
+ The hash can be modified when the keyword set changes as necessary. We also
+ store the length of each keyword and check it prior to the final strcmp().
+
+ TODO: just use gperf. */
+
+static int
+isqualifier (const char *s, size_t len)
+{
+ static const struct qual
+ {
+ const char *q_name;
+ size_t q_len;
+ } qhash[] = {
+ {"static", 6}, {"", 0}, {"", 0}, {"", 0},
+ {"volatile", 8}, {"", 0}, {"", 0}, {"", 0}, {"", 0},
+ {"", 0}, {"auto", 4}, {"extern", 6}, {"", 0}, {"", 0},
+ {"", 0}, {"", 0}, {"const", 5}, {"register", 8},
+ {"", 0}, {"restrict", 8}, {"_Restrict", 9}
+ };
+
+ int h = s[len - 1] + (int) len - 105;
+ const struct qual *qp = &qhash[h];
+
+ return (h >= 0 && (size_t) h < sizeof (qhash) / sizeof (qhash[0])
+ && (size_t) len == qp->q_len &&
+ strncmp (qp->q_name, s, qp->q_len) == 0);
+}
+
+/* Attempt to convert the given C type name into the corresponding CTF type ID.
+ It is not possible to do complete and proper conversion of type names
+ without implementing a more full-fledged parser, which is necessary to
+ handle things like types that are function pointers to functions that
+ have arguments that are function pointers, and fun stuff like that.
+ Instead, this function implements a very simple conversion algorithm that
+ finds the things that we actually care about: structs, unions, enums,
+ integers, floats, typedefs, and pointers to any of these named types. */
+
+ctf_id_t
+ctf_lookup_by_name (ctf_file_t *fp, const char *name)
+{
+ static const char delimiters[] = " \t\n\r\v\f*";
+
+ const ctf_lookup_t *lp;
+ const char *p, *q, *end;
+ ctf_id_t type = 0;
+ ctf_id_t ntype, ptype;
+
+ if (name == NULL)
+ return (ctf_set_errno (fp, EINVAL));
+
+ for (p = name, end = name + strlen (name); *p != '\0'; p = q)
+ {
+ while (isspace (*p))
+ p++; /* Skip leading whitespace. */
+
+ if (p == end)
+ break;
+
+ if ((q = strpbrk (p + 1, delimiters)) == NULL)
+ q = end; /* Compare until end. */
+
+ if (*p == '*')
+ {
+ /* Find a pointer to type by looking in fp->ctf_ptrtab.
+ If we can't find a pointer to the given type, see if
+ we can compute a pointer to the type resulting from
+ resolving the type down to its base type and use
+ that instead. This helps with cases where the CTF
+ data includes "struct foo *" but not "foo_t *" and
+ the user tries to access "foo_t *" in the debugger.
+
+ TODO need to handle parent containers too. */
+
+ ntype = fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, type)];
+ if (ntype == 0)
+ {
+ ntype = ctf_type_resolve_unsliced (fp, type);
+ if (ntype == CTF_ERR
+ || (ntype =
+ fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, ntype)]) == 0)
+ {
+ (void) ctf_set_errno (fp, ECTF_NOTYPE);
+ goto err;
+ }
+ }
+
+ type = LCTF_INDEX_TO_TYPE (fp, ntype, (fp->ctf_flags & LCTF_CHILD));
+
+ q = p + 1;
+ continue;
+ }
+
+ if (isqualifier (p, (size_t) (q - p)))
+ continue; /* Skip qualifier keyword. */
+
+ for (lp = fp->ctf_lookups; lp->ctl_prefix != NULL; lp++)
+ {
+ /* TODO: This is not MT-safe. */
+ if ((lp->ctl_prefix[0] == '\0' ||
+ strncmp (p, lp->ctl_prefix, (size_t) (q - p)) == 0) &&
+ (size_t) (q - p) >= lp->ctl_len)
+ {
+ for (p += lp->ctl_len; isspace (*p); p++)
+ continue; /* Skip prefix and next whitespace. */
+
+ if ((q = strchr (p, '*')) == NULL)
+ q = end; /* Compare until end. */
+
+ while (isspace (q[-1]))
+ q--; /* Exclude trailing whitespace. */
+
+ /* Expand and/or allocate storage for a slice of the name, then
+ copy it in. */
+
+ if (fp->ctf_tmp_typeslicelen >= (size_t) (q - p) + 1)
+ {
+ memcpy (fp->ctf_tmp_typeslice, p, (size_t) (q - p));
+ fp->ctf_tmp_typeslice[(size_t) (q - p)] = '\0';
+ }
+ else
+ {
+ free (fp->ctf_tmp_typeslice);
+ fp->ctf_tmp_typeslice = strndup (p, (size_t) (q - p));
+ if (fp->ctf_tmp_typeslice == NULL)
+ {
+ (void) ctf_set_errno (fp, ENOMEM);
+ return CTF_ERR;
+ }
+ }
+
+ if ((type = ctf_hash_lookup_type (lp->ctl_hash, fp,
+ fp->ctf_tmp_typeslice)) == 0)
+ {
+ (void) ctf_set_errno (fp, ECTF_NOTYPE);
+ goto err;
+ }
+
+ break;
+ }
+ }
+
+ if (lp->ctl_prefix == NULL)
+ {
+ (void) ctf_set_errno (fp, ECTF_NOTYPE);
+ goto err;
+ }
+ }
+
+ if (*p != '\0' || type == 0)
+ return (ctf_set_errno (fp, ECTF_SYNTAX));
+
+ return type;
+
+err:
+ if (fp->ctf_parent != NULL
+ && (ptype = ctf_lookup_by_name (fp->ctf_parent, name)) != CTF_ERR)
+ return ptype;
+
+ return CTF_ERR;
+}
+
+typedef struct ctf_lookup_var_key
+{
+ ctf_file_t *clvk_fp;
+ const char *clvk_name;
+} ctf_lookup_var_key_t;
+
+/* A bsearch function for variable names. */
+
+static int
+ctf_lookup_var (const void *key_, const void *memb_)
+{
+ const ctf_lookup_var_key_t *key = key_;
+ const ctf_varent_t *memb = memb_;
+
+ return (strcmp (key->clvk_name, ctf_strptr (key->clvk_fp, memb->ctv_name)));
+}
+
+/* Given a variable name, return the type of the variable with that name. */
+
+ctf_id_t
+ctf_lookup_variable (ctf_file_t *fp, const char *name)
+{
+ ctf_varent_t *ent;
+ ctf_lookup_var_key_t key = { fp, name };
+
+ /* This array is sorted, so we can bsearch for it. */
+
+ ent = bsearch (&key, fp->ctf_vars, fp->ctf_nvars, sizeof (ctf_varent_t),
+ ctf_lookup_var);
+
+ if (ent == NULL)
+ {
+ if (fp->ctf_parent != NULL)
+ return ctf_lookup_variable (fp->ctf_parent, name);
+
+ return (ctf_set_errno (fp, ECTF_NOTYPEDAT));
+ }
+
+ return ent->ctv_type;
+}
+
+/* Given a symbol table index, return the name of that symbol from the secondary
+ string table, or the null string (never NULL). */
+const char *
+ctf_lookup_symbol_name (ctf_file_t *fp, unsigned long symidx)
+{
+ const ctf_sect_t *sp = &fp->ctf_symtab;
+ Elf64_Sym sym, *gsp;
+
+ if (sp->cts_data == NULL)
+ {
+ ctf_set_errno (fp, ECTF_NOSYMTAB);
+ return _CTF_NULLSTR;
+ }
+
+ if (symidx >= fp->ctf_nsyms)
+ {
+ ctf_set_errno (fp, EINVAL);
+ return _CTF_NULLSTR;
+ }
+
+ if (sp->cts_entsize == sizeof (Elf32_Sym))
+ {
+ const Elf32_Sym *symp = (Elf32_Sym *) sp->cts_data + symidx;
+ gsp = ctf_sym_to_elf64 (symp, &sym);
+ }
+ else
+ gsp = (Elf64_Sym *) sp->cts_data + symidx;
+
+ if (gsp->st_name < fp->ctf_str[CTF_STRTAB_1].cts_len)
+ return (const char *) fp->ctf_str[CTF_STRTAB_1].cts_strs + gsp->st_name;
+
+ return _CTF_NULLSTR;
+}
+
+/* Given a symbol table index, return the type of the data object described
+ by the corresponding entry in the symbol table. */
+
+ctf_id_t
+ctf_lookup_by_symbol (ctf_file_t *fp, unsigned long symidx)
+{
+ const ctf_sect_t *sp = &fp->ctf_symtab;
+ ctf_id_t type;
+
+ if (sp->cts_data == NULL)
+ return (ctf_set_errno (fp, ECTF_NOSYMTAB));
+
+ if (symidx >= fp->ctf_nsyms)
+ return (ctf_set_errno (fp, EINVAL));
+
+ if (sp->cts_entsize == sizeof (Elf32_Sym))
+ {
+ const Elf32_Sym *symp = (Elf32_Sym *) sp->cts_data + symidx;
+ if (ELF32_ST_TYPE (symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno (fp, ECTF_NOTDATA));
+ }
+ else
+ {
+ const Elf64_Sym *symp = (Elf64_Sym *) sp->cts_data + symidx;
+ if (ELF64_ST_TYPE (symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno (fp, ECTF_NOTDATA));
+ }
+
+ if (fp->ctf_sxlate[symidx] == -1u)
+ return (ctf_set_errno (fp, ECTF_NOTYPEDAT));
+
+ type = *(uint32_t *) ((uintptr_t) fp->ctf_buf + fp->ctf_sxlate[symidx]);
+ if (type == 0)
+ return (ctf_set_errno (fp, ECTF_NOTYPEDAT));
+
+ return type;
+}
+