libctf: lookups by name and symbol
These functions allow you to look up types given a name in a simple subset of C declarator syntax (no function pointers), to look up the types of variables given a name, and to look up the types of data objects and the type signatures of functions given symbol table offsets. (Despite its name, one function in this commit, ctf_lookup_symbol_name(), is for the internal use of libctf only, and does not appear in any public header files.) libctf/ * ctf-lookup.c (isqualifier): New. (ctf_lookup_by_name): Likewise. (struct ctf_lookup_var_key): Likewise. (ctf_lookup_var): Likewise. (ctf_lookup_variable): Likewise. (ctf_lookup_symbol_name): Likewise. (ctf_lookup_by_symbol): Likewise. (ctf_func_info): Likewise. (ctf_func_args): Likewise. include/ * ctf-api.h (ctf_func_info): New. (ctf_func_args): Likewise. (ctf_lookup_by_symbol): Likewise. (ctf_lookup_by_symbol): Likewise. (ctf_lookup_variable): Likewise.
This commit is contained in:
parent
316afdb130
commit
b437bfe0f4
5 changed files with 392 additions and 0 deletions
|
@ -21,6 +21,290 @@
|
|||
#include <elf.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Return the pointer to the internal CTF type data corresponding to the
|
||||
given type ID. If the ID is invalid, the function returns NULL.
|
||||
This function is not exported outside of the library. */
|
||||
|
@ -61,3 +345,83 @@ ctf_lookup_by_id (ctf_file_t **fpp, ctf_id_t type)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Given a symbol table index, return the info for the function described
|
||||
by the corresponding entry in the symbol table. */
|
||||
|
||||
int
|
||||
ctf_func_info (ctf_file_t *fp, unsigned long symidx, ctf_funcinfo_t *fip)
|
||||
{
|
||||
const ctf_sect_t *sp = &fp->ctf_symtab;
|
||||
const uint32_t *dp;
|
||||
uint32_t info, kind, n;
|
||||
|
||||
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_FUNC)
|
||||
return (ctf_set_errno (fp, ECTF_NOTFUNC));
|
||||
}
|
||||
else
|
||||
{
|
||||
const Elf64_Sym *symp = (Elf64_Sym *) sp->cts_data + symidx;
|
||||
if (ELF64_ST_TYPE (symp->st_info) != STT_FUNC)
|
||||
return (ctf_set_errno (fp, ECTF_NOTFUNC));
|
||||
}
|
||||
|
||||
if (fp->ctf_sxlate[symidx] == -1u)
|
||||
return (ctf_set_errno (fp, ECTF_NOFUNCDAT));
|
||||
|
||||
dp = (uint32_t *) ((uintptr_t) fp->ctf_buf + fp->ctf_sxlate[symidx]);
|
||||
|
||||
info = *dp++;
|
||||
kind = LCTF_INFO_KIND (fp, info);
|
||||
n = LCTF_INFO_VLEN (fp, info);
|
||||
|
||||
if (kind == CTF_K_UNKNOWN && n == 0)
|
||||
return (ctf_set_errno (fp, ECTF_NOFUNCDAT));
|
||||
|
||||
if (kind != CTF_K_FUNCTION)
|
||||
return (ctf_set_errno (fp, ECTF_CORRUPT));
|
||||
|
||||
fip->ctc_return = *dp++;
|
||||
fip->ctc_argc = n;
|
||||
fip->ctc_flags = 0;
|
||||
|
||||
if (n != 0 && dp[n - 1] == 0)
|
||||
{
|
||||
fip->ctc_flags |= CTF_FUNC_VARARG;
|
||||
fip->ctc_argc--;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Given a symbol table index, return the arguments for the function described
|
||||
by the corresponding entry in the symbol table. */
|
||||
|
||||
int
|
||||
ctf_func_args (ctf_file_t * fp, unsigned long symidx, uint32_t argc,
|
||||
ctf_id_t * argv)
|
||||
{
|
||||
const uint32_t *dp;
|
||||
ctf_funcinfo_t f;
|
||||
|
||||
if (ctf_func_info (fp, symidx, &f) == CTF_ERR)
|
||||
return CTF_ERR; /* errno is set for us. */
|
||||
|
||||
/* The argument data is two uint32_t's past the translation table
|
||||
offset: one for the function info, and one for the return type. */
|
||||
|
||||
dp = (uint32_t *) ((uintptr_t) fp->ctf_buf + fp->ctf_sxlate[symidx]) + 2;
|
||||
|
||||
for (argc = MIN (argc, f.ctc_argc); argc != 0; argc--)
|
||||
*argv++ = *dp++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue