mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-09 04:21:49 +08:00
a0486bac41
- Use of nonportable <endian.h> - Use of qsort_r - Use of zlib without appropriate magic to pull in the binutils zlib - Use of off64_t without checking (fixed by dropping the unused fields that need off64_t entirely) - signedness problems due to long being too short a type on 32-bit platforms: ctf_id_t is now 'unsigned long', and CTF_ERR must be used only for functions that return ctf_id_t - One lingering use of bzero() and of <sys/errno.h> All fixed, using code from gnulib where possible. Relatedly, set cts_size in a couple of places it was missed (string table and symbol table loading upon ctf_bfdopen()). binutils/ * objdump.c (make_ctfsect): Drop cts_type, cts_flags, and cts_offset. * readelf.c (shdr_to_ctf_sect): Likewise. include/ * ctf-api.h (ctf_sect_t): Drop cts_type, cts_flags, and cts_offset. (ctf_id_t): This is now an unsigned type. (CTF_ERR): Cast it to ctf_id_t. Note that it should only be used for ctf_id_t-returning functions. libctf/ * Makefile.am (ZLIB): New. (ZLIBINC): Likewise. (AM_CFLAGS): Use them. (libctf_a_LIBADD): New, for LIBOBJS. * configure.ac: Check for zlib, endian.h, and qsort_r. * ctf-endian.h: New, providing htole64 and le64toh. * swap.h: Code style fixes. (bswap_identity_64): New. * qsort_r.c: New, from gnulib (with one added #include). * ctf-decls.h: New, providing a conditional qsort_r declaration, and unconditional definitions of MIN and MAX. * ctf-impl.h: Use it. Do not use <sys/errno.h>. (ctf_set_errno): Now returns unsigned long. * ctf-util.c (ctf_set_errno): Adjust here too. * ctf-archive.c: Use ctf-endian.h. (ctf_arc_open_by_offset): Use memset, not bzero. Drop cts_type, cts_flags and cts_offset. (ctf_arc_write): Drop debugging dependent on the size of off_t. * ctf-create.c: Provide a definition of roundup if not defined. (ctf_create): Drop cts_type, cts_flags and cts_offset. (ctf_add_reftype): Do not check if type IDs are below zero. (ctf_add_slice): Likewise. (ctf_add_typedef): Likewise. (ctf_add_member_offset): Cast error-returning ssize_t's to size_t when known error-free. Drop CTF_ERR usage for functions returning int. (ctf_add_member_encoded): Drop CTF_ERR usage for functions returning int. (ctf_add_variable): Likewise. (enumcmp): Likewise. (enumadd): Likewise. (membcmp): Likewise. (ctf_add_type): Likewise. Cast error-returning ssize_t's to size_t when known error-free. * ctf-dump.c (ctf_is_slice): Drop CTF_ERR usage for functions returning int: use CTF_ERR for functions returning ctf_type_id. (ctf_dump_label): Likewise. (ctf_dump_objts): Likewise. * ctf-labels.c (ctf_label_topmost): Likewise. (ctf_label_iter): Likewise. (ctf_label_info): Likewise. * ctf-lookup.c (ctf_func_args): Likewise. * ctf-open.c (upgrade_types): Cast to size_t where appropriate. (ctf_bufopen): Likewise. Use zlib types as needed. * ctf-types.c (ctf_member_iter): Drop CTF_ERR usage for functions returning int. (ctf_enum_iter): Likewise. (ctf_type_size): Likewise. (ctf_type_align): Likewise. Cast to size_t where appropriate. (ctf_type_kind_unsliced): Likewise. (ctf_type_kind): Likewise. (ctf_type_encoding): Likewise. (ctf_member_info): Likewise. (ctf_array_info): Likewise. (ctf_enum_value): Likewise. (ctf_type_rvisit): Likewise. * ctf-open-bfd.c (ctf_bfdopen): Drop cts_type, cts_flags and cts_offset. (ctf_simple_open): Likewise. (ctf_bfdopen_ctfsect): Likewise. Set cts_size properly. * Makefile.in: Regenerate. * aclocal.m4: Likewise. * config.h: Likewise. * configure: Likewise.
428 lines
12 KiB
C
428 lines
12 KiB
C
/* Type lookup.
|
|
Copyright (C) 2019 Free Software Foundation, Inc.
|
|
|
|
This file is part of libctf.
|
|
|
|
libctf is free software; you can redistribute it and/or modify it under
|
|
the terms of the GNU General Public License as published by the Free
|
|
Software Foundation; either version 3, or (at your option) any later
|
|
version.
|
|
|
|
This program is distributed in the hope that it will be useful, but
|
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; see the file COPYING. If not see
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include <ctf-impl.h>
|
|
#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. */
|
|
|
|
const ctf_type_t *
|
|
ctf_lookup_by_id (ctf_file_t **fpp, ctf_id_t type)
|
|
{
|
|
ctf_file_t *fp = *fpp; /* Caller passes in starting CTF container. */
|
|
ctf_id_t idx;
|
|
|
|
if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, type)
|
|
&& (fp = fp->ctf_parent) == NULL)
|
|
{
|
|
(void) ctf_set_errno (*fpp, ECTF_NOPARENT);
|
|
return NULL;
|
|
}
|
|
|
|
idx = LCTF_TYPE_TO_INDEX (fp, type);
|
|
if (idx > 0 && (unsigned long) idx <= fp->ctf_typemax)
|
|
{
|
|
*fpp = fp; /* Function returns ending CTF container. */
|
|
return (LCTF_INDEX_TO_TYPEPTR (fp, idx));
|
|
}
|
|
|
|
/* If this container is writable, check for a dynamic type. */
|
|
|
|
if (fp->ctf_flags & LCTF_RDWR)
|
|
{
|
|
ctf_dtdef_t *dtd;
|
|
|
|
if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
|
|
{
|
|
*fpp = fp;
|
|
return &dtd->dtd_data;
|
|
}
|
|
}
|
|
(void) ctf_set_errno (*fpp, ECTF_BADID);
|
|
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) < 0)
|
|
return -1; /* 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;
|
|
}
|