mirror of
https://github.com/autc04/Retro68.git
synced 2024-11-28 05:51:04 +00:00
1705 lines
44 KiB
C
1705 lines
44 KiB
C
/* Type handling functions.
|
|
Copyright (C) 2019-2022 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 <assert.h>
|
|
#include <string.h>
|
|
|
|
/* Determine whether a type is a parent or a child. */
|
|
|
|
int
|
|
ctf_type_isparent (ctf_dict_t *fp, ctf_id_t id)
|
|
{
|
|
return (LCTF_TYPE_ISPARENT (fp, id));
|
|
}
|
|
|
|
int
|
|
ctf_type_ischild (ctf_dict_t * fp, ctf_id_t id)
|
|
{
|
|
return (LCTF_TYPE_ISCHILD (fp, id));
|
|
}
|
|
|
|
/* Expand a structure element into the passed-in ctf_lmember_t. */
|
|
|
|
static int
|
|
ctf_struct_member (ctf_dict_t *fp, ctf_lmember_t *dst, const ctf_type_t *tp,
|
|
unsigned char *vlen, size_t vbytes, size_t n)
|
|
{
|
|
if (!ctf_assert (fp, n < LCTF_INFO_VLEN (fp, tp->ctt_info)))
|
|
return -1; /* errno is set for us. */
|
|
|
|
/* Already large. */
|
|
if (tp->ctt_size == CTF_LSIZE_SENT)
|
|
{
|
|
ctf_lmember_t *lmp = (ctf_lmember_t *) vlen;
|
|
|
|
if (!ctf_assert (fp, (n + 1) * sizeof (ctf_lmember_t) <= vbytes))
|
|
return -1; /* errno is set for us. */
|
|
|
|
memcpy (dst, &lmp[n], sizeof (ctf_lmember_t));
|
|
}
|
|
else
|
|
{
|
|
ctf_member_t *mp = (ctf_member_t *) vlen;
|
|
dst->ctlm_name = mp[n].ctm_name;
|
|
dst->ctlm_type = mp[n].ctm_type;
|
|
dst->ctlm_offsetlo = mp[n].ctm_offset;
|
|
dst->ctlm_offsethi = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Iterate over the members of a STRUCT or UNION. We pass the name, member
|
|
type, and offset of each member to the specified callback function. */
|
|
|
|
int
|
|
ctf_member_iter (ctf_dict_t *fp, ctf_id_t type, ctf_member_f *func, void *arg)
|
|
{
|
|
ctf_next_t *i = NULL;
|
|
ssize_t offset;
|
|
const char *name;
|
|
ctf_id_t membtype;
|
|
|
|
while ((offset = ctf_member_next (fp, type, &i, &name, &membtype, 0)) >= 0)
|
|
{
|
|
int rc;
|
|
if ((rc = func (name, membtype, offset, arg)) != 0)
|
|
{
|
|
ctf_next_destroy (i);
|
|
return rc;
|
|
}
|
|
}
|
|
if (ctf_errno (fp) != ECTF_NEXT_END)
|
|
return -1; /* errno is set for us. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Iterate over the members of a STRUCT or UNION, returning each member's
|
|
offset and optionally name and member type in turn. On end-of-iteration,
|
|
returns -1. If FLAGS is CTF_MN_RECURSE, recurse into unnamed members. */
|
|
|
|
ssize_t
|
|
ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
|
|
const char **name, ctf_id_t *membtype, int flags)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
uint32_t kind;
|
|
ssize_t offset;
|
|
uint32_t max_vlen;
|
|
ctf_next_t *i = *it;
|
|
|
|
if (!i)
|
|
{
|
|
const ctf_type_t *tp;
|
|
ctf_dtdef_t *dtd;
|
|
ssize_t size;
|
|
ssize_t increment;
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((i = ctf_next_create ()) == NULL)
|
|
return ctf_set_errno (ofp, ENOMEM);
|
|
i->cu.ctn_fp = ofp;
|
|
i->ctn_tp = tp;
|
|
|
|
ctf_get_ctt_size (fp, tp, &size, &increment);
|
|
kind = LCTF_INFO_KIND (fp, tp->ctt_info);
|
|
|
|
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
|
|
{
|
|
ctf_next_destroy (i);
|
|
return (ctf_set_errno (ofp, ECTF_NOTSOU));
|
|
}
|
|
|
|
if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
|
|
{
|
|
i->u.ctn_vlen = dtd->dtd_vlen;
|
|
i->ctn_size = dtd->dtd_vlen_alloc;
|
|
}
|
|
else
|
|
{
|
|
unsigned long vlen = LCTF_INFO_VLEN (fp, tp->ctt_info);
|
|
|
|
i->u.ctn_vlen = (unsigned char *) tp + increment;
|
|
i->ctn_size = LCTF_VBYTES (fp, kind, size, vlen);;
|
|
}
|
|
i->ctn_iter_fun = (void (*) (void)) ctf_member_next;
|
|
i->ctn_n = 0;
|
|
*it = i;
|
|
}
|
|
|
|
if ((void (*) (void)) ctf_member_next != i->ctn_iter_fun)
|
|
return (ctf_set_errno (ofp, ECTF_NEXT_WRONGFUN));
|
|
|
|
if (ofp != i->cu.ctn_fp)
|
|
return (ctf_set_errno (ofp, ECTF_NEXT_WRONGFP));
|
|
|
|
/* Resolve to the native dict of this type. */
|
|
if ((fp = ctf_get_dict (ofp, type)) == NULL)
|
|
return (ctf_set_errno (ofp, ECTF_NOPARENT));
|
|
|
|
max_vlen = LCTF_INFO_VLEN (fp, i->ctn_tp->ctt_info);
|
|
|
|
/* When we hit an unnamed struct/union member, we set ctn_type to indicate
|
|
that we are inside one, then return the unnamed member: on the next call,
|
|
we must skip over top-level member iteration in favour of iteration within
|
|
the sub-struct until it later turns out that that iteration has ended. */
|
|
|
|
retry:
|
|
if (!i->ctn_type)
|
|
{
|
|
ctf_lmember_t memb;
|
|
const char *membname;
|
|
|
|
if (i->ctn_n == max_vlen)
|
|
goto end_iter;
|
|
|
|
if (ctf_struct_member (fp, &memb, i->ctn_tp, i->u.ctn_vlen, i->ctn_size,
|
|
i->ctn_n) < 0)
|
|
return -1; /* errno is set for us. */
|
|
|
|
membname = ctf_strptr (fp, memb.ctlm_name);
|
|
|
|
if (name)
|
|
*name = membname;
|
|
if (membtype)
|
|
*membtype = memb.ctlm_type;
|
|
offset = (unsigned long) CTF_LMEM_OFFSET (&memb);
|
|
|
|
if (membname[0] == 0
|
|
&& (ctf_type_kind (fp, memb.ctlm_type) == CTF_K_STRUCT
|
|
|| ctf_type_kind (fp, memb.ctlm_type) == CTF_K_UNION))
|
|
i->ctn_type = memb.ctlm_type;
|
|
i->ctn_n++;
|
|
|
|
/* The callers might want automatic recursive sub-struct traversal. */
|
|
if (!(flags & CTF_MN_RECURSE))
|
|
i->ctn_type = 0;
|
|
|
|
/* Sub-struct traversal starting? Take note of the offset of this member,
|
|
for later boosting of sub-struct members' offsets. */
|
|
if (i->ctn_type)
|
|
i->ctn_increment = offset;
|
|
}
|
|
/* Traversing a sub-struct? Just return it, with the offset adjusted. */
|
|
else
|
|
{
|
|
ssize_t ret = ctf_member_next (fp, i->ctn_type, &i->ctn_next, name,
|
|
membtype, flags);
|
|
|
|
if (ret >= 0)
|
|
return ret + i->ctn_increment;
|
|
|
|
if (ctf_errno (fp) != ECTF_NEXT_END)
|
|
{
|
|
ctf_next_destroy (i);
|
|
*it = NULL;
|
|
i->ctn_type = 0;
|
|
return ret; /* errno is set for us. */
|
|
}
|
|
|
|
if (!ctf_assert (fp, (i->ctn_next == NULL)))
|
|
return -1; /* errno is set for us. */
|
|
|
|
i->ctn_type = 0;
|
|
/* This sub-struct has ended: on to the next real member. */
|
|
goto retry;
|
|
}
|
|
|
|
return offset;
|
|
|
|
end_iter:
|
|
ctf_next_destroy (i);
|
|
*it = NULL;
|
|
return ctf_set_errno (ofp, ECTF_NEXT_END);
|
|
}
|
|
|
|
/* Iterate over the members of an ENUM. We pass the string name and associated
|
|
integer value of each enum element to the specified callback function. */
|
|
|
|
int
|
|
ctf_enum_iter (ctf_dict_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg)
|
|
{
|
|
ctf_next_t *i = NULL;
|
|
const char *name;
|
|
int val;
|
|
|
|
while ((name = ctf_enum_next (fp, type, &i, &val)) != NULL)
|
|
{
|
|
int rc;
|
|
if ((rc = func (name, val, arg)) != 0)
|
|
{
|
|
ctf_next_destroy (i);
|
|
return rc;
|
|
}
|
|
}
|
|
if (ctf_errno (fp) != ECTF_NEXT_END)
|
|
return -1; /* errno is set for us. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Iterate over the members of an enum TYPE, returning each enumerand's NAME or
|
|
NULL at end of iteration or error, and optionally passing back the
|
|
enumerand's integer VALue. */
|
|
|
|
const char *
|
|
ctf_enum_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
|
|
int *val)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
uint32_t kind;
|
|
const char *name;
|
|
ctf_next_t *i = *it;
|
|
|
|
if (!i)
|
|
{
|
|
const ctf_type_t *tp;
|
|
ctf_dtdef_t *dtd;
|
|
|
|
if ((type = ctf_type_resolve_unsliced (fp, type)) == CTF_ERR)
|
|
return NULL; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return NULL; /* errno is set for us. */
|
|
|
|
if ((i = ctf_next_create ()) == NULL)
|
|
{
|
|
ctf_set_errno (ofp, ENOMEM);
|
|
return NULL;
|
|
}
|
|
i->cu.ctn_fp = ofp;
|
|
|
|
(void) ctf_get_ctt_size (fp, tp, NULL,
|
|
&i->ctn_increment);
|
|
kind = LCTF_INFO_KIND (fp, tp->ctt_info);
|
|
|
|
if (kind != CTF_K_ENUM)
|
|
{
|
|
ctf_next_destroy (i);
|
|
ctf_set_errno (ofp, ECTF_NOTENUM);
|
|
return NULL;
|
|
}
|
|
|
|
dtd = ctf_dynamic_type (fp, type);
|
|
i->ctn_iter_fun = (void (*) (void)) ctf_enum_next;
|
|
i->ctn_n = LCTF_INFO_VLEN (fp, tp->ctt_info);
|
|
|
|
if (dtd == NULL)
|
|
i->u.ctn_en = (const ctf_enum_t *) ((uintptr_t) tp +
|
|
i->ctn_increment);
|
|
else
|
|
i->u.ctn_en = (const ctf_enum_t *) dtd->dtd_vlen;
|
|
|
|
*it = i;
|
|
}
|
|
|
|
if ((void (*) (void)) ctf_enum_next != i->ctn_iter_fun)
|
|
{
|
|
ctf_set_errno (ofp, ECTF_NEXT_WRONGFUN);
|
|
return NULL;
|
|
}
|
|
|
|
if (ofp != i->cu.ctn_fp)
|
|
{
|
|
ctf_set_errno (ofp, ECTF_NEXT_WRONGFP);
|
|
return NULL;
|
|
}
|
|
|
|
/* Resolve to the native dict of this type. */
|
|
if ((fp = ctf_get_dict (ofp, type)) == NULL)
|
|
{
|
|
ctf_set_errno (ofp, ECTF_NOPARENT);
|
|
return NULL;
|
|
}
|
|
|
|
if (i->ctn_n == 0)
|
|
goto end_iter;
|
|
|
|
name = ctf_strptr (fp, i->u.ctn_en->cte_name);
|
|
if (val)
|
|
*val = i->u.ctn_en->cte_value;
|
|
i->u.ctn_en++;
|
|
i->ctn_n--;
|
|
|
|
return name;
|
|
|
|
end_iter:
|
|
ctf_next_destroy (i);
|
|
*it = NULL;
|
|
ctf_set_errno (ofp, ECTF_NEXT_END);
|
|
return NULL;
|
|
}
|
|
|
|
/* Iterate over every root (user-visible) type in the given CTF dict.
|
|
We pass the type ID of each type to the specified callback function.
|
|
|
|
Does not traverse parent types: you have to do that explicitly. This is by
|
|
design, to avoid traversing them more than once if traversing many children
|
|
of a single parent. */
|
|
|
|
int
|
|
ctf_type_iter (ctf_dict_t *fp, ctf_type_f *func, void *arg)
|
|
{
|
|
ctf_next_t *i = NULL;
|
|
ctf_id_t type;
|
|
|
|
while ((type = ctf_type_next (fp, &i, NULL, 0)) != CTF_ERR)
|
|
{
|
|
int rc;
|
|
if ((rc = func (type, arg)) != 0)
|
|
{
|
|
ctf_next_destroy (i);
|
|
return rc;
|
|
}
|
|
}
|
|
if (ctf_errno (fp) != ECTF_NEXT_END)
|
|
return -1; /* errno is set for us. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Iterate over every type in the given CTF dict, user-visible or not.
|
|
We pass the type ID of each type to the specified callback function.
|
|
|
|
Does not traverse parent types: you have to do that explicitly. This is by
|
|
design, to avoid traversing them more than once if traversing many children
|
|
of a single parent. */
|
|
|
|
int
|
|
ctf_type_iter_all (ctf_dict_t *fp, ctf_type_all_f *func, void *arg)
|
|
{
|
|
ctf_next_t *i = NULL;
|
|
ctf_id_t type;
|
|
int flag;
|
|
|
|
while ((type = ctf_type_next (fp, &i, &flag, 1)) != CTF_ERR)
|
|
{
|
|
int rc;
|
|
if ((rc = func (type, flag, arg)) != 0)
|
|
{
|
|
ctf_next_destroy (i);
|
|
return rc;
|
|
}
|
|
}
|
|
if (ctf_errno (fp) != ECTF_NEXT_END)
|
|
return -1; /* errno is set for us. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Iterate over every type in the given CTF dict, optionally including
|
|
non-user-visible types, returning each type ID and hidden flag in turn.
|
|
Returns CTF_ERR on end of iteration or error.
|
|
|
|
Does not traverse parent types: you have to do that explicitly. This is by
|
|
design, to avoid traversing them more than once if traversing many children
|
|
of a single parent. */
|
|
|
|
ctf_id_t
|
|
ctf_type_next (ctf_dict_t *fp, ctf_next_t **it, int *flag, int want_hidden)
|
|
{
|
|
ctf_next_t *i = *it;
|
|
|
|
if (!i)
|
|
{
|
|
if ((i = ctf_next_create ()) == NULL)
|
|
return ctf_set_errno (fp, ENOMEM);
|
|
|
|
i->cu.ctn_fp = fp;
|
|
i->ctn_type = 1;
|
|
i->ctn_iter_fun = (void (*) (void)) ctf_type_next;
|
|
*it = i;
|
|
}
|
|
|
|
if ((void (*) (void)) ctf_type_next != i->ctn_iter_fun)
|
|
return (ctf_set_errno (fp, ECTF_NEXT_WRONGFUN));
|
|
|
|
if (fp != i->cu.ctn_fp)
|
|
return (ctf_set_errno (fp, ECTF_NEXT_WRONGFP));
|
|
|
|
while (i->ctn_type <= fp->ctf_typemax)
|
|
{
|
|
const ctf_type_t *tp = LCTF_INDEX_TO_TYPEPTR (fp, i->ctn_type);
|
|
|
|
if ((!want_hidden) && (!LCTF_INFO_ISROOT (fp, tp->ctt_info)))
|
|
{
|
|
i->ctn_type++;
|
|
continue;
|
|
}
|
|
|
|
if (flag)
|
|
*flag = LCTF_INFO_ISROOT (fp, tp->ctt_info);
|
|
return LCTF_INDEX_TO_TYPE (fp, i->ctn_type++, fp->ctf_flags & LCTF_CHILD);
|
|
}
|
|
ctf_next_destroy (i);
|
|
*it = NULL;
|
|
return ctf_set_errno (fp, ECTF_NEXT_END);
|
|
}
|
|
|
|
/* Iterate over every variable in the given CTF dict, in arbitrary order.
|
|
We pass the name of each variable to the specified callback function. */
|
|
|
|
int
|
|
ctf_variable_iter (ctf_dict_t *fp, ctf_variable_f *func, void *arg)
|
|
{
|
|
ctf_next_t *i = NULL;
|
|
ctf_id_t type;
|
|
const char *name;
|
|
|
|
while ((type = ctf_variable_next (fp, &i, &name)) != CTF_ERR)
|
|
{
|
|
int rc;
|
|
if ((rc = func (name, type, arg)) != 0)
|
|
{
|
|
ctf_next_destroy (i);
|
|
return rc;
|
|
}
|
|
}
|
|
if (ctf_errno (fp) != ECTF_NEXT_END)
|
|
return -1; /* errno is set for us. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Iterate over every variable in the given CTF dict, in arbitrary order,
|
|
returning the name and type of each variable in turn. The name argument is
|
|
not optional. Returns CTF_ERR on end of iteration or error. */
|
|
|
|
ctf_id_t
|
|
ctf_variable_next (ctf_dict_t *fp, ctf_next_t **it, const char **name)
|
|
{
|
|
ctf_next_t *i = *it;
|
|
|
|
if ((fp->ctf_flags & LCTF_CHILD) && (fp->ctf_parent == NULL))
|
|
return (ctf_set_errno (fp, ECTF_NOPARENT));
|
|
|
|
if (!i)
|
|
{
|
|
if ((i = ctf_next_create ()) == NULL)
|
|
return ctf_set_errno (fp, ENOMEM);
|
|
|
|
i->cu.ctn_fp = fp;
|
|
i->ctn_iter_fun = (void (*) (void)) ctf_variable_next;
|
|
if (fp->ctf_flags & LCTF_RDWR)
|
|
i->u.ctn_dvd = ctf_list_next (&fp->ctf_dvdefs);
|
|
*it = i;
|
|
}
|
|
|
|
if ((void (*) (void)) ctf_variable_next != i->ctn_iter_fun)
|
|
return (ctf_set_errno (fp, ECTF_NEXT_WRONGFUN));
|
|
|
|
if (fp != i->cu.ctn_fp)
|
|
return (ctf_set_errno (fp, ECTF_NEXT_WRONGFP));
|
|
|
|
if (!(fp->ctf_flags & LCTF_RDWR))
|
|
{
|
|
if (i->ctn_n >= fp->ctf_nvars)
|
|
goto end_iter;
|
|
|
|
*name = ctf_strptr (fp, fp->ctf_vars[i->ctn_n].ctv_name);
|
|
return fp->ctf_vars[i->ctn_n++].ctv_type;
|
|
}
|
|
else
|
|
{
|
|
ctf_id_t id;
|
|
|
|
if (i->u.ctn_dvd == NULL)
|
|
goto end_iter;
|
|
|
|
*name = i->u.ctn_dvd->dvd_name;
|
|
id = i->u.ctn_dvd->dvd_type;
|
|
i->u.ctn_dvd = ctf_list_next (i->u.ctn_dvd);
|
|
return id;
|
|
}
|
|
|
|
end_iter:
|
|
ctf_next_destroy (i);
|
|
*it = NULL;
|
|
return ctf_set_errno (fp, ECTF_NEXT_END);
|
|
}
|
|
|
|
/* Follow a given type through the graph for TYPEDEF, VOLATILE, CONST, and
|
|
RESTRICT nodes until we reach a "base" type node. This is useful when
|
|
we want to follow a type ID to a node that has members or a size. To guard
|
|
against infinite loops, we implement simplified cycle detection and check
|
|
each link against itself, the previous node, and the topmost node.
|
|
|
|
Does not drill down through slices to their contained type.
|
|
|
|
Callers of this function must not presume that a type it returns must have a
|
|
valid ctt_size: forwards do not, and must be separately handled. */
|
|
|
|
ctf_id_t
|
|
ctf_type_resolve (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
ctf_id_t prev = type, otype = type;
|
|
ctf_dict_t *ofp = fp;
|
|
const ctf_type_t *tp;
|
|
|
|
if (type == 0)
|
|
return (ctf_set_errno (ofp, ECTF_NONREPRESENTABLE));
|
|
|
|
while ((tp = ctf_lookup_by_id (&fp, type)) != NULL)
|
|
{
|
|
switch (LCTF_INFO_KIND (fp, tp->ctt_info))
|
|
{
|
|
case CTF_K_TYPEDEF:
|
|
case CTF_K_VOLATILE:
|
|
case CTF_K_CONST:
|
|
case CTF_K_RESTRICT:
|
|
if (tp->ctt_type == type || tp->ctt_type == otype
|
|
|| tp->ctt_type == prev)
|
|
{
|
|
ctf_err_warn (ofp, 0, ECTF_CORRUPT, _("type %lx cycle detected"),
|
|
otype);
|
|
return (ctf_set_errno (ofp, ECTF_CORRUPT));
|
|
}
|
|
prev = type;
|
|
type = tp->ctt_type;
|
|
break;
|
|
case CTF_K_UNKNOWN:
|
|
return (ctf_set_errno (ofp, ECTF_NONREPRESENTABLE));
|
|
default:
|
|
return type;
|
|
}
|
|
if (type == 0)
|
|
return (ctf_set_errno (ofp, ECTF_NONREPRESENTABLE));
|
|
}
|
|
|
|
return CTF_ERR; /* errno is set for us. */
|
|
}
|
|
|
|
/* Like ctf_type_resolve(), but traverse down through slices to their contained
|
|
type. */
|
|
|
|
ctf_id_t
|
|
ctf_type_resolve_unsliced (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
const ctf_type_t *tp;
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
|
|
return -1;
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return CTF_ERR; /* errno is set for us. */
|
|
|
|
if ((LCTF_INFO_KIND (fp, tp->ctt_info)) == CTF_K_SLICE)
|
|
return ctf_type_reference (fp, type);
|
|
return type;
|
|
}
|
|
|
|
/* Return the native dict of a given type: if called on a child and the
|
|
type is in the parent, return the parent. Needed if you plan to access
|
|
the type directly, without using the API. */
|
|
ctf_dict_t *
|
|
ctf_get_dict (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
if ((fp->ctf_flags & LCTF_CHILD) && LCTF_TYPE_ISPARENT (fp, type))
|
|
return fp->ctf_parent;
|
|
|
|
return fp;
|
|
}
|
|
|
|
/* Look up a name in the given name table, in the appropriate hash given the
|
|
kind of the identifier. The name is a raw, undecorated identifier. */
|
|
|
|
ctf_id_t ctf_lookup_by_rawname (ctf_dict_t *fp, int kind, const char *name)
|
|
{
|
|
return ctf_lookup_by_rawhash (fp, ctf_name_table (fp, kind), name);
|
|
}
|
|
|
|
/* Look up a name in the given name table, in the appropriate hash given the
|
|
readability state of the dictionary. The name is a raw, undecorated
|
|
identifier. */
|
|
|
|
ctf_id_t ctf_lookup_by_rawhash (ctf_dict_t *fp, ctf_names_t *np, const char *name)
|
|
{
|
|
ctf_id_t id;
|
|
|
|
if (fp->ctf_flags & LCTF_RDWR)
|
|
id = (ctf_id_t) (uintptr_t) ctf_dynhash_lookup (np->ctn_writable, name);
|
|
else
|
|
id = ctf_hash_lookup_type (np->ctn_readonly, fp, name);
|
|
return id;
|
|
}
|
|
|
|
/* Lookup the given type ID and return its name as a new dynamically-allocated
|
|
string. */
|
|
|
|
char *
|
|
ctf_type_aname (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
ctf_decl_t cd;
|
|
ctf_decl_node_t *cdp;
|
|
ctf_decl_prec_t prec, lp, rp;
|
|
int ptr, arr;
|
|
uint32_t k;
|
|
char *buf;
|
|
|
|
if (fp == NULL && type == CTF_ERR)
|
|
return NULL; /* Simplify caller code by permitting CTF_ERR. */
|
|
|
|
ctf_decl_init (&cd);
|
|
ctf_decl_push (&cd, fp, type);
|
|
|
|
if (cd.cd_err != 0)
|
|
{
|
|
ctf_decl_fini (&cd);
|
|
ctf_set_errno (fp, cd.cd_err);
|
|
return NULL;
|
|
}
|
|
|
|
/* If the type graph's order conflicts with lexical precedence order
|
|
for pointers or arrays, then we need to surround the declarations at
|
|
the corresponding lexical precedence with parentheses. This can
|
|
result in either a parenthesized pointer (*) as in int (*)() or
|
|
int (*)[], or in a parenthesized pointer and array as in int (*[])(). */
|
|
|
|
ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER;
|
|
arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY;
|
|
|
|
rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1;
|
|
lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1;
|
|
|
|
k = CTF_K_POINTER; /* Avoid leading whitespace (see below). */
|
|
|
|
for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++)
|
|
{
|
|
for (cdp = ctf_list_next (&cd.cd_nodes[prec]);
|
|
cdp != NULL; cdp = ctf_list_next (cdp))
|
|
{
|
|
ctf_dict_t *rfp = fp;
|
|
const ctf_type_t *tp = ctf_lookup_by_id (&rfp, cdp->cd_type);
|
|
const char *name = ctf_strptr (rfp, tp->ctt_name);
|
|
|
|
if (k != CTF_K_POINTER && k != CTF_K_ARRAY)
|
|
ctf_decl_sprintf (&cd, " ");
|
|
|
|
if (lp == prec)
|
|
{
|
|
ctf_decl_sprintf (&cd, "(");
|
|
lp = -1;
|
|
}
|
|
|
|
switch (cdp->cd_kind)
|
|
{
|
|
case CTF_K_INTEGER:
|
|
case CTF_K_FLOAT:
|
|
case CTF_K_TYPEDEF:
|
|
/* Integers, floats, and typedefs must always be named types. */
|
|
|
|
if (name[0] == '\0')
|
|
{
|
|
ctf_set_errno (fp, ECTF_CORRUPT);
|
|
ctf_decl_fini (&cd);
|
|
return NULL;
|
|
}
|
|
|
|
ctf_decl_sprintf (&cd, "%s", name);
|
|
break;
|
|
case CTF_K_POINTER:
|
|
ctf_decl_sprintf (&cd, "*");
|
|
break;
|
|
case CTF_K_ARRAY:
|
|
ctf_decl_sprintf (&cd, "[%u]", cdp->cd_n);
|
|
break;
|
|
case CTF_K_FUNCTION:
|
|
{
|
|
size_t i;
|
|
ctf_funcinfo_t fi;
|
|
ctf_id_t *argv = NULL;
|
|
|
|
if (ctf_func_type_info (rfp, cdp->cd_type, &fi) < 0)
|
|
goto err; /* errno is set for us. */
|
|
|
|
if ((argv = calloc (fi.ctc_argc, sizeof (ctf_id_t *))) == NULL)
|
|
{
|
|
ctf_set_errno (rfp, errno);
|
|
goto err;
|
|
}
|
|
|
|
if (ctf_func_type_args (rfp, cdp->cd_type,
|
|
fi.ctc_argc, argv) < 0)
|
|
goto err; /* errno is set for us. */
|
|
|
|
ctf_decl_sprintf (&cd, "(*) (");
|
|
for (i = 0; i < fi.ctc_argc; i++)
|
|
{
|
|
char *arg = ctf_type_aname (rfp, argv[i]);
|
|
|
|
if (arg == NULL)
|
|
goto err; /* errno is set for us. */
|
|
ctf_decl_sprintf (&cd, "%s", arg);
|
|
free (arg);
|
|
|
|
if ((i < fi.ctc_argc - 1)
|
|
|| (fi.ctc_flags & CTF_FUNC_VARARG))
|
|
ctf_decl_sprintf (&cd, ", ");
|
|
}
|
|
|
|
if (fi.ctc_flags & CTF_FUNC_VARARG)
|
|
ctf_decl_sprintf (&cd, "...");
|
|
ctf_decl_sprintf (&cd, ")");
|
|
|
|
free (argv);
|
|
break;
|
|
|
|
err:
|
|
free (argv);
|
|
ctf_decl_fini (&cd);
|
|
return NULL;
|
|
}
|
|
break;
|
|
case CTF_K_STRUCT:
|
|
ctf_decl_sprintf (&cd, "struct %s", name);
|
|
break;
|
|
case CTF_K_UNION:
|
|
ctf_decl_sprintf (&cd, "union %s", name);
|
|
break;
|
|
case CTF_K_ENUM:
|
|
ctf_decl_sprintf (&cd, "enum %s", name);
|
|
break;
|
|
case CTF_K_FORWARD:
|
|
{
|
|
switch (ctf_type_kind_forwarded (fp, cdp->cd_type))
|
|
{
|
|
case CTF_K_STRUCT:
|
|
ctf_decl_sprintf (&cd, "struct %s", name);
|
|
break;
|
|
case CTF_K_UNION:
|
|
ctf_decl_sprintf (&cd, "union %s", name);
|
|
break;
|
|
case CTF_K_ENUM:
|
|
ctf_decl_sprintf (&cd, "enum %s", name);
|
|
break;
|
|
default:
|
|
ctf_set_errno (fp, ECTF_CORRUPT);
|
|
ctf_decl_fini (&cd);
|
|
return NULL;
|
|
}
|
|
break;
|
|
}
|
|
case CTF_K_VOLATILE:
|
|
ctf_decl_sprintf (&cd, "volatile");
|
|
break;
|
|
case CTF_K_CONST:
|
|
ctf_decl_sprintf (&cd, "const");
|
|
break;
|
|
case CTF_K_RESTRICT:
|
|
ctf_decl_sprintf (&cd, "restrict");
|
|
break;
|
|
case CTF_K_UNKNOWN:
|
|
if (name[0] == '\0')
|
|
ctf_decl_sprintf (&cd, _("(nonrepresentable type)"));
|
|
else
|
|
ctf_decl_sprintf (&cd, _("(nonrepresentable type %s)"),
|
|
name);
|
|
break;
|
|
}
|
|
|
|
k = cdp->cd_kind;
|
|
}
|
|
|
|
if (rp == prec)
|
|
ctf_decl_sprintf (&cd, ")");
|
|
}
|
|
|
|
if (cd.cd_enomem)
|
|
(void) ctf_set_errno (fp, ENOMEM);
|
|
|
|
buf = ctf_decl_buf (&cd);
|
|
|
|
ctf_decl_fini (&cd);
|
|
return buf;
|
|
}
|
|
|
|
/* Lookup the given type ID and print a string name for it into buf. Return
|
|
the actual number of bytes (not including \0) needed to format the name. */
|
|
|
|
ssize_t
|
|
ctf_type_lname (ctf_dict_t *fp, ctf_id_t type, char *buf, size_t len)
|
|
{
|
|
char *str = ctf_type_aname (fp, type);
|
|
size_t slen;
|
|
|
|
if (str == NULL)
|
|
return CTF_ERR; /* errno is set for us. */
|
|
|
|
slen = strlen (str);
|
|
snprintf (buf, len, "%s", str);
|
|
free (str);
|
|
|
|
if (slen >= len)
|
|
(void) ctf_set_errno (fp, ECTF_NAMELEN);
|
|
|
|
return slen;
|
|
}
|
|
|
|
/* Lookup the given type ID and print a string name for it into buf. If buf
|
|
is too small, return NULL: the ECTF_NAMELEN error is set on 'fp' for us. */
|
|
|
|
char *
|
|
ctf_type_name (ctf_dict_t *fp, ctf_id_t type, char *buf, size_t len)
|
|
{
|
|
ssize_t rv = ctf_type_lname (fp, type, buf, len);
|
|
return (rv >= 0 && (size_t) rv < len ? buf : NULL);
|
|
}
|
|
|
|
/* Lookup the given type ID and return its raw, unadorned, undecorated name.
|
|
The name will live as long as its ctf_dict_t does.
|
|
|
|
The only decoration is that a NULL return always means an error: nameless
|
|
types return a null string. */
|
|
|
|
const char *
|
|
ctf_type_name_raw (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
const ctf_type_t *tp;
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return NULL; /* errno is set for us. */
|
|
|
|
if (tp->ctt_name == 0)
|
|
return "";
|
|
|
|
return ctf_strraw (fp, tp->ctt_name);
|
|
}
|
|
|
|
/* Lookup the given type ID and return its raw, unadorned, undecorated name as a
|
|
new dynamically-allocated string. */
|
|
|
|
char *
|
|
ctf_type_aname_raw (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
const char *name = ctf_type_name_raw (fp, type);
|
|
|
|
if (name != NULL)
|
|
return strdup (name);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Resolve the type down to a base type node, and then return the size
|
|
of the type storage in bytes. */
|
|
|
|
ssize_t
|
|
ctf_type_size (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
const ctf_type_t *tp;
|
|
ssize_t size;
|
|
ctf_arinfo_t ar;
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
switch (LCTF_INFO_KIND (fp, tp->ctt_info))
|
|
{
|
|
case CTF_K_POINTER:
|
|
return fp->ctf_dmodel->ctd_pointer;
|
|
|
|
case CTF_K_FUNCTION:
|
|
return 0; /* Function size is only known by symtab. */
|
|
|
|
case CTF_K_ENUM:
|
|
return fp->ctf_dmodel->ctd_int;
|
|
|
|
case CTF_K_ARRAY:
|
|
/* ctf_add_array() does not directly encode the element size, but
|
|
requires the user to multiply to determine the element size.
|
|
|
|
If ctf_get_ctt_size() returns nonzero, then use the recorded
|
|
size instead. */
|
|
|
|
if ((size = ctf_get_ctt_size (fp, tp, NULL, NULL)) > 0)
|
|
return size;
|
|
|
|
if (ctf_array_info (ofp, type, &ar) < 0
|
|
|| (size = ctf_type_size (ofp, ar.ctr_contents)) < 0)
|
|
return -1; /* errno is set for us. */
|
|
|
|
return size * ar.ctr_nelems;
|
|
|
|
case CTF_K_FORWARD:
|
|
/* Forwards do not have a meaningful size. */
|
|
return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
|
|
|
|
default: /* including slices of enums, etc */
|
|
return (ctf_get_ctt_size (fp, tp, NULL, NULL));
|
|
}
|
|
}
|
|
|
|
/* Resolve the type down to a base type node, and then return the alignment
|
|
needed for the type storage in bytes.
|
|
|
|
XXX may need arch-dependent attention. */
|
|
|
|
ssize_t
|
|
ctf_type_align (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
const ctf_type_t *tp;
|
|
ctf_dict_t *ofp = fp;
|
|
int kind;
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
kind = LCTF_INFO_KIND (fp, tp->ctt_info);
|
|
switch (kind)
|
|
{
|
|
case CTF_K_POINTER:
|
|
case CTF_K_FUNCTION:
|
|
return fp->ctf_dmodel->ctd_pointer;
|
|
|
|
case CTF_K_ARRAY:
|
|
{
|
|
ctf_arinfo_t r;
|
|
if (ctf_array_info (ofp, type, &r) < 0)
|
|
return -1; /* errno is set for us. */
|
|
return (ctf_type_align (ofp, r.ctr_contents));
|
|
}
|
|
|
|
case CTF_K_STRUCT:
|
|
case CTF_K_UNION:
|
|
{
|
|
size_t align = 0;
|
|
ctf_dtdef_t *dtd;
|
|
unsigned char *vlen;
|
|
uint32_t i = 0, n = LCTF_INFO_VLEN (fp, tp->ctt_info);
|
|
ssize_t size, increment, vbytes;
|
|
|
|
ctf_get_ctt_size (fp, tp, &size, &increment);
|
|
|
|
if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
|
|
{
|
|
vlen = dtd->dtd_vlen;
|
|
vbytes = dtd->dtd_vlen_alloc;
|
|
}
|
|
else
|
|
{
|
|
vlen = (unsigned char *) tp + increment;
|
|
vbytes = LCTF_VBYTES (fp, kind, size, n);
|
|
}
|
|
|
|
if (kind == CTF_K_STRUCT)
|
|
n = MIN (n, 1); /* Only use first member for structs. */
|
|
|
|
for (; n != 0; n--, i++)
|
|
{
|
|
ctf_lmember_t memb;
|
|
|
|
if (ctf_struct_member (fp, &memb, tp, vlen, vbytes, i) < 0)
|
|
return -1; /* errno is set for us. */
|
|
|
|
ssize_t am = ctf_type_align (ofp, memb.ctlm_type);
|
|
align = MAX (align, (size_t) am);
|
|
}
|
|
return align;
|
|
}
|
|
|
|
case CTF_K_ENUM:
|
|
return fp->ctf_dmodel->ctd_int;
|
|
|
|
case CTF_K_FORWARD:
|
|
/* Forwards do not have a meaningful alignment. */
|
|
return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
|
|
|
|
default: /* including slices of enums, etc */
|
|
return (ctf_get_ctt_size (fp, tp, NULL, NULL));
|
|
}
|
|
}
|
|
|
|
/* Return the kind (CTF_K_* constant) for the specified type ID. */
|
|
|
|
int
|
|
ctf_type_kind_unsliced (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
const ctf_type_t *tp;
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
return (LCTF_INFO_KIND (fp, tp->ctt_info));
|
|
}
|
|
|
|
/* Return the kind (CTF_K_* constant) for the specified type ID.
|
|
Slices are considered to be of the same kind as the type sliced. */
|
|
|
|
int
|
|
ctf_type_kind (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
int kind;
|
|
|
|
if ((kind = ctf_type_kind_unsliced (fp, type)) < 0)
|
|
return -1;
|
|
|
|
if (kind == CTF_K_SLICE)
|
|
{
|
|
if ((type = ctf_type_reference (fp, type)) == CTF_ERR)
|
|
return -1;
|
|
kind = ctf_type_kind_unsliced (fp, type);
|
|
}
|
|
|
|
return kind;
|
|
}
|
|
|
|
/* Return the kind of this type, except, for forwards, return the kind of thing
|
|
this is a forward to. */
|
|
int
|
|
ctf_type_kind_forwarded (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
int kind;
|
|
const ctf_type_t *tp;
|
|
|
|
if ((kind = ctf_type_kind (fp, type)) < 0)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if (kind != CTF_K_FORWARD)
|
|
return kind;
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
return tp->ctt_type;
|
|
}
|
|
|
|
/* If the type is one that directly references another type (such as POINTER),
|
|
then return the ID of the type to which it refers. */
|
|
|
|
ctf_id_t
|
|
ctf_type_reference (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
const ctf_type_t *tp;
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return CTF_ERR; /* errno is set for us. */
|
|
|
|
switch (LCTF_INFO_KIND (fp, tp->ctt_info))
|
|
{
|
|
case CTF_K_POINTER:
|
|
case CTF_K_TYPEDEF:
|
|
case CTF_K_VOLATILE:
|
|
case CTF_K_CONST:
|
|
case CTF_K_RESTRICT:
|
|
return tp->ctt_type;
|
|
/* Slices store their type in an unusual place. */
|
|
case CTF_K_SLICE:
|
|
{
|
|
ctf_dtdef_t *dtd;
|
|
const ctf_slice_t *sp;
|
|
|
|
if ((dtd = ctf_dynamic_type (ofp, type)) == NULL)
|
|
{
|
|
ssize_t increment;
|
|
|
|
(void) ctf_get_ctt_size (fp, tp, NULL, &increment);
|
|
sp = (const ctf_slice_t *) ((uintptr_t) tp + increment);
|
|
}
|
|
else
|
|
sp = (const ctf_slice_t *) dtd->dtd_vlen;
|
|
|
|
return sp->cts_type;
|
|
}
|
|
default:
|
|
return (ctf_set_errno (ofp, ECTF_NOTREF));
|
|
}
|
|
}
|
|
|
|
/* 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 accesses "foo_t *" in the debugger.
|
|
|
|
XXX what about parent dicts? */
|
|
|
|
ctf_id_t
|
|
ctf_type_pointer (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
ctf_id_t ntype;
|
|
|
|
if (ctf_lookup_by_id (&fp, type) == NULL)
|
|
return CTF_ERR; /* errno is set for us. */
|
|
|
|
if ((ntype = fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, type)]) != 0)
|
|
return (LCTF_INDEX_TO_TYPE (fp, ntype, (fp->ctf_flags & LCTF_CHILD)));
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
|
|
return (ctf_set_errno (ofp, ECTF_NOTYPE));
|
|
|
|
if (ctf_lookup_by_id (&fp, type) == NULL)
|
|
return (ctf_set_errno (ofp, ECTF_NOTYPE));
|
|
|
|
if ((ntype = fp->ctf_ptrtab[LCTF_TYPE_TO_INDEX (fp, type)]) != 0)
|
|
return (LCTF_INDEX_TO_TYPE (fp, ntype, (fp->ctf_flags & LCTF_CHILD)));
|
|
|
|
return (ctf_set_errno (ofp, ECTF_NOTYPE));
|
|
}
|
|
|
|
/* Return the encoding for the specified INTEGER, FLOAT, or ENUM. */
|
|
|
|
int
|
|
ctf_type_encoding (ctf_dict_t *fp, ctf_id_t type, ctf_encoding_t *ep)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
ctf_dtdef_t *dtd;
|
|
const ctf_type_t *tp;
|
|
ssize_t increment;
|
|
const unsigned char *vlen;
|
|
uint32_t data;
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((dtd = ctf_dynamic_type (ofp, type)) != NULL)
|
|
vlen = dtd->dtd_vlen;
|
|
else
|
|
{
|
|
ctf_get_ctt_size (fp, tp, NULL, &increment);
|
|
vlen = (const unsigned char *) ((uintptr_t) tp + increment);
|
|
}
|
|
|
|
switch (LCTF_INFO_KIND (fp, tp->ctt_info))
|
|
{
|
|
case CTF_K_INTEGER:
|
|
data = *(const uint32_t *) vlen;
|
|
ep->cte_format = CTF_INT_ENCODING (data);
|
|
ep->cte_offset = CTF_INT_OFFSET (data);
|
|
ep->cte_bits = CTF_INT_BITS (data);
|
|
break;
|
|
case CTF_K_FLOAT:
|
|
data = *(const uint32_t *) vlen;
|
|
ep->cte_format = CTF_FP_ENCODING (data);
|
|
ep->cte_offset = CTF_FP_OFFSET (data);
|
|
ep->cte_bits = CTF_FP_BITS (data);
|
|
break;
|
|
case CTF_K_ENUM:
|
|
/* v3 only: we must guess at the underlying integral format. */
|
|
ep->cte_format = CTF_INT_SIGNED;
|
|
ep->cte_offset = 0;
|
|
ep->cte_bits = 0;
|
|
break;
|
|
case CTF_K_SLICE:
|
|
{
|
|
const ctf_slice_t *slice;
|
|
ctf_encoding_t underlying_en;
|
|
ctf_id_t underlying;
|
|
|
|
slice = (ctf_slice_t *) vlen;
|
|
underlying = ctf_type_resolve (fp, slice->cts_type);
|
|
if (ctf_type_encoding (fp, underlying, &underlying_en) < 0)
|
|
return -1; /* errno is set for us. */
|
|
|
|
ep->cte_format = underlying_en.cte_format;
|
|
ep->cte_offset = slice->cts_offset;
|
|
ep->cte_bits = slice->cts_bits;
|
|
break;
|
|
}
|
|
default:
|
|
return (ctf_set_errno (ofp, ECTF_NOTINTFP));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
ctf_type_cmp (ctf_dict_t *lfp, ctf_id_t ltype, ctf_dict_t *rfp,
|
|
ctf_id_t rtype)
|
|
{
|
|
int rval;
|
|
|
|
if (ltype < rtype)
|
|
rval = -1;
|
|
else if (ltype > rtype)
|
|
rval = 1;
|
|
else
|
|
rval = 0;
|
|
|
|
if (lfp == rfp)
|
|
return rval;
|
|
|
|
if (LCTF_TYPE_ISPARENT (lfp, ltype) && lfp->ctf_parent != NULL)
|
|
lfp = lfp->ctf_parent;
|
|
|
|
if (LCTF_TYPE_ISPARENT (rfp, rtype) && rfp->ctf_parent != NULL)
|
|
rfp = rfp->ctf_parent;
|
|
|
|
if (lfp < rfp)
|
|
return -1;
|
|
|
|
if (lfp > rfp)
|
|
return 1;
|
|
|
|
return rval;
|
|
}
|
|
|
|
/* Return a boolean value indicating if two types are compatible. This function
|
|
returns true if the two types are the same, or if they (or their ultimate
|
|
base type) have the same encoding properties, or (for structs / unions /
|
|
enums / forward declarations) if they have the same name and (for structs /
|
|
unions) member count. */
|
|
|
|
int
|
|
ctf_type_compat (ctf_dict_t *lfp, ctf_id_t ltype,
|
|
ctf_dict_t *rfp, ctf_id_t rtype)
|
|
{
|
|
const ctf_type_t *ltp, *rtp;
|
|
ctf_encoding_t le, re;
|
|
ctf_arinfo_t la, ra;
|
|
uint32_t lkind, rkind;
|
|
int same_names = 0;
|
|
|
|
if (ctf_type_cmp (lfp, ltype, rfp, rtype) == 0)
|
|
return 1;
|
|
|
|
ltype = ctf_type_resolve (lfp, ltype);
|
|
lkind = ctf_type_kind (lfp, ltype);
|
|
|
|
rtype = ctf_type_resolve (rfp, rtype);
|
|
rkind = ctf_type_kind (rfp, rtype);
|
|
|
|
ltp = ctf_lookup_by_id (&lfp, ltype);
|
|
rtp = ctf_lookup_by_id (&rfp, rtype);
|
|
|
|
if (ltp != NULL && rtp != NULL)
|
|
same_names = (strcmp (ctf_strptr (lfp, ltp->ctt_name),
|
|
ctf_strptr (rfp, rtp->ctt_name)) == 0);
|
|
|
|
if (((lkind == CTF_K_ENUM) && (rkind == CTF_K_INTEGER)) ||
|
|
((rkind == CTF_K_ENUM) && (lkind == CTF_K_INTEGER)))
|
|
return 1;
|
|
|
|
if (lkind != rkind)
|
|
return 0;
|
|
|
|
switch (lkind)
|
|
{
|
|
case CTF_K_INTEGER:
|
|
case CTF_K_FLOAT:
|
|
memset (&le, 0, sizeof (le));
|
|
memset (&re, 0, sizeof (re));
|
|
return (ctf_type_encoding (lfp, ltype, &le) == 0
|
|
&& ctf_type_encoding (rfp, rtype, &re) == 0
|
|
&& memcmp (&le, &re, sizeof (ctf_encoding_t)) == 0);
|
|
case CTF_K_POINTER:
|
|
return (ctf_type_compat (lfp, ctf_type_reference (lfp, ltype),
|
|
rfp, ctf_type_reference (rfp, rtype)));
|
|
case CTF_K_ARRAY:
|
|
return (ctf_array_info (lfp, ltype, &la) == 0
|
|
&& ctf_array_info (rfp, rtype, &ra) == 0
|
|
&& la.ctr_nelems == ra.ctr_nelems
|
|
&& ctf_type_compat (lfp, la.ctr_contents, rfp, ra.ctr_contents)
|
|
&& ctf_type_compat (lfp, la.ctr_index, rfp, ra.ctr_index));
|
|
case CTF_K_STRUCT:
|
|
case CTF_K_UNION:
|
|
return (same_names && (ctf_type_size (lfp, ltype)
|
|
== ctf_type_size (rfp, rtype)));
|
|
case CTF_K_ENUM:
|
|
{
|
|
int lencoded, rencoded;
|
|
lencoded = ctf_type_encoding (lfp, ltype, &le);
|
|
rencoded = ctf_type_encoding (rfp, rtype, &re);
|
|
|
|
if ((lencoded != rencoded) ||
|
|
((lencoded == 0) && memcmp (&le, &re, sizeof (ctf_encoding_t)) != 0))
|
|
return 0;
|
|
}
|
|
/* FALLTHRU */
|
|
case CTF_K_FORWARD:
|
|
return same_names; /* No other checks required for these type kinds. */
|
|
default:
|
|
return 0; /* Should not get here since we did a resolve. */
|
|
}
|
|
}
|
|
|
|
/* Return the number of members in a STRUCT or UNION, or the number of
|
|
enumerators in an ENUM. The count does not include unnamed sub-members. */
|
|
|
|
int
|
|
ctf_member_count (ctf_dict_t *fp, ctf_id_t type)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
const ctf_type_t *tp;
|
|
uint32_t kind;
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
kind = LCTF_INFO_KIND (fp, tp->ctt_info);
|
|
|
|
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION && kind != CTF_K_ENUM)
|
|
return (ctf_set_errno (ofp, ECTF_NOTSUE));
|
|
|
|
return LCTF_INFO_VLEN (fp, tp->ctt_info);
|
|
}
|
|
|
|
/* Return the type and offset for a given member of a STRUCT or UNION. */
|
|
|
|
int
|
|
ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
|
|
ctf_membinfo_t *mip)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
const ctf_type_t *tp;
|
|
ctf_dtdef_t *dtd;
|
|
unsigned char *vlen;
|
|
ssize_t size, increment, vbytes;
|
|
uint32_t kind, n, i = 0;
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
ctf_get_ctt_size (fp, tp, &size, &increment);
|
|
kind = LCTF_INFO_KIND (fp, tp->ctt_info);
|
|
|
|
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
|
|
return (ctf_set_errno (ofp, ECTF_NOTSOU));
|
|
|
|
n = LCTF_INFO_VLEN (fp, tp->ctt_info);
|
|
if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
|
|
{
|
|
vlen = dtd->dtd_vlen;
|
|
vbytes = dtd->dtd_vlen_alloc;
|
|
}
|
|
else
|
|
{
|
|
vlen = (unsigned char *) tp + increment;
|
|
vbytes = LCTF_VBYTES (fp, kind, size, n);
|
|
}
|
|
|
|
for (; n != 0; n--, i++)
|
|
{
|
|
ctf_lmember_t memb;
|
|
const char *membname;
|
|
|
|
if (ctf_struct_member (fp, &memb, tp, vlen, vbytes, i) < 0)
|
|
return -1; /* errno is set for us. */
|
|
|
|
membname = ctf_strptr (fp, memb.ctlm_name);
|
|
|
|
if (membname[0] == 0
|
|
&& (ctf_type_kind (fp, memb.ctlm_type) == CTF_K_STRUCT
|
|
|| ctf_type_kind (fp, memb.ctlm_type) == CTF_K_UNION)
|
|
&& (ctf_member_info (fp, memb.ctlm_type, name, mip) == 0))
|
|
return 0;
|
|
|
|
if (strcmp (membname, name) == 0)
|
|
{
|
|
mip->ctm_type = memb.ctlm_type;
|
|
mip->ctm_offset = (unsigned long) CTF_LMEM_OFFSET (&memb);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return (ctf_set_errno (ofp, ECTF_NOMEMBNAM));
|
|
}
|
|
|
|
/* Return the array type, index, and size information for the specified ARRAY. */
|
|
|
|
int
|
|
ctf_array_info (ctf_dict_t *fp, ctf_id_t type, ctf_arinfo_t *arp)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
const ctf_type_t *tp;
|
|
const ctf_array_t *ap;
|
|
const ctf_dtdef_t *dtd;
|
|
ssize_t increment;
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ARRAY)
|
|
return (ctf_set_errno (ofp, ECTF_NOTARRAY));
|
|
|
|
if ((dtd = ctf_dynamic_type (ofp, type)) != NULL)
|
|
ap = (const ctf_array_t *) dtd->dtd_vlen;
|
|
else
|
|
{
|
|
ctf_get_ctt_size (fp, tp, NULL, &increment);
|
|
ap = (const ctf_array_t *) ((uintptr_t) tp + increment);
|
|
}
|
|
arp->ctr_contents = ap->cta_contents;
|
|
arp->ctr_index = ap->cta_index;
|
|
arp->ctr_nelems = ap->cta_nelems;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Convert the specified value to the corresponding enum tag name, if a
|
|
matching name can be found. Otherwise NULL is returned. */
|
|
|
|
const char *
|
|
ctf_enum_name (ctf_dict_t *fp, ctf_id_t type, int value)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
const ctf_type_t *tp;
|
|
const ctf_enum_t *ep;
|
|
const ctf_dtdef_t *dtd;
|
|
ssize_t increment;
|
|
uint32_t n;
|
|
|
|
if ((type = ctf_type_resolve_unsliced (fp, type)) == CTF_ERR)
|
|
return NULL; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return NULL; /* errno is set for us. */
|
|
|
|
if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ENUM)
|
|
{
|
|
ctf_set_errno (ofp, ECTF_NOTENUM);
|
|
return NULL;
|
|
}
|
|
|
|
ctf_get_ctt_size (fp, tp, NULL, &increment);
|
|
|
|
if ((dtd = ctf_dynamic_type (ofp, type)) == NULL)
|
|
ep = (const ctf_enum_t *) ((uintptr_t) tp + increment);
|
|
else
|
|
ep = (const ctf_enum_t *) dtd->dtd_vlen;
|
|
|
|
for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, ep++)
|
|
{
|
|
if (ep->cte_value == value)
|
|
return (ctf_strptr (fp, ep->cte_name));
|
|
}
|
|
|
|
ctf_set_errno (ofp, ECTF_NOENUMNAM);
|
|
return NULL;
|
|
}
|
|
|
|
/* Convert the specified enum tag name to the corresponding value, if a
|
|
matching name can be found. Otherwise CTF_ERR is returned. */
|
|
|
|
int
|
|
ctf_enum_value (ctf_dict_t *fp, ctf_id_t type, const char *name, int *valp)
|
|
{
|
|
ctf_dict_t *ofp = fp;
|
|
const ctf_type_t *tp;
|
|
const ctf_enum_t *ep;
|
|
const ctf_dtdef_t *dtd;
|
|
ssize_t increment;
|
|
uint32_t n;
|
|
|
|
if ((type = ctf_type_resolve_unsliced (fp, type)) == CTF_ERR)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ENUM)
|
|
{
|
|
(void) ctf_set_errno (ofp, ECTF_NOTENUM);
|
|
return -1;
|
|
}
|
|
|
|
ctf_get_ctt_size (fp, tp, NULL, &increment);
|
|
|
|
if ((dtd = ctf_dynamic_type (ofp, type)) == NULL)
|
|
ep = (const ctf_enum_t *) ((uintptr_t) tp + increment);
|
|
else
|
|
ep = (const ctf_enum_t *) dtd->dtd_vlen;
|
|
|
|
for (n = LCTF_INFO_VLEN (fp, tp->ctt_info); n != 0; n--, ep++)
|
|
{
|
|
if (strcmp (ctf_strptr (fp, ep->cte_name), name) == 0)
|
|
{
|
|
if (valp != NULL)
|
|
*valp = ep->cte_value;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
ctf_set_errno (ofp, ECTF_NOENUMNAM);
|
|
return -1;
|
|
}
|
|
|
|
/* Given a type ID relating to a function type, return info on return types and
|
|
arg counts for that function. */
|
|
|
|
int
|
|
ctf_func_type_info (ctf_dict_t *fp, ctf_id_t type, ctf_funcinfo_t *fip)
|
|
{
|
|
const ctf_type_t *tp;
|
|
uint32_t kind;
|
|
const uint32_t *args;
|
|
const ctf_dtdef_t *dtd;
|
|
ssize_t size, increment;
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
(void) ctf_get_ctt_size (fp, tp, &size, &increment);
|
|
kind = LCTF_INFO_KIND (fp, tp->ctt_info);
|
|
|
|
if (kind != CTF_K_FUNCTION)
|
|
return (ctf_set_errno (fp, ECTF_NOTFUNC));
|
|
|
|
fip->ctc_return = tp->ctt_type;
|
|
fip->ctc_flags = 0;
|
|
fip->ctc_argc = LCTF_INFO_VLEN (fp, tp->ctt_info);
|
|
|
|
if ((dtd = ctf_dynamic_type (fp, type)) == NULL)
|
|
args = (uint32_t *) ((uintptr_t) tp + increment);
|
|
else
|
|
args = (uint32_t *) dtd->dtd_vlen;
|
|
|
|
if (fip->ctc_argc != 0 && args[fip->ctc_argc - 1] == 0)
|
|
{
|
|
fip->ctc_flags |= CTF_FUNC_VARARG;
|
|
fip->ctc_argc--;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Given a type ID relating to a function type, return the arguments for the
|
|
function. */
|
|
|
|
int
|
|
ctf_func_type_args (ctf_dict_t *fp, ctf_id_t type, uint32_t argc, ctf_id_t *argv)
|
|
{
|
|
const ctf_type_t *tp;
|
|
const uint32_t *args;
|
|
const ctf_dtdef_t *dtd;
|
|
ssize_t size, increment;
|
|
ctf_funcinfo_t f;
|
|
|
|
if (ctf_func_type_info (fp, type, &f) < 0)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
(void) ctf_get_ctt_size (fp, tp, &size, &increment);
|
|
|
|
if ((dtd = ctf_dynamic_type (fp, type)) == NULL)
|
|
args = (uint32_t *) ((uintptr_t) tp + increment);
|
|
else
|
|
args = (uint32_t *) dtd->dtd_vlen;
|
|
|
|
for (argc = MIN (argc, f.ctc_argc); argc != 0; argc--)
|
|
*argv++ = *args++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Recursively visit the members of any type. This function is used as the
|
|
engine for ctf_type_visit, below. We resolve the input type, recursively
|
|
invoke ourself for each type member if the type is a struct or union, and
|
|
then invoke the callback function on the current type. If any callback
|
|
returns non-zero, we abort and percolate the error code back up to the top. */
|
|
|
|
static int
|
|
ctf_type_rvisit (ctf_dict_t *fp, ctf_id_t type, ctf_visit_f *func,
|
|
void *arg, const char *name, unsigned long offset, int depth)
|
|
{
|
|
ctf_id_t otype = type;
|
|
const ctf_type_t *tp;
|
|
const ctf_dtdef_t *dtd;
|
|
unsigned char *vlen;
|
|
ssize_t size, increment, vbytes;
|
|
uint32_t kind, n, i = 0;
|
|
int nonrepresentable = 0;
|
|
int rc;
|
|
|
|
if ((type = ctf_type_resolve (fp, type)) == CTF_ERR) {
|
|
if (ctf_errno (fp) != ECTF_NONREPRESENTABLE)
|
|
return -1; /* errno is set for us. */
|
|
else
|
|
nonrepresentable = 1;
|
|
}
|
|
|
|
if (!nonrepresentable)
|
|
if ((tp = ctf_lookup_by_id (&fp, type)) == NULL)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((rc = func (name, otype, offset, depth, arg)) != 0)
|
|
return rc;
|
|
|
|
if (!nonrepresentable)
|
|
kind = LCTF_INFO_KIND (fp, tp->ctt_info);
|
|
|
|
if (nonrepresentable || (kind != CTF_K_STRUCT && kind != CTF_K_UNION))
|
|
return 0;
|
|
|
|
ctf_get_ctt_size (fp, tp, &size, &increment);
|
|
|
|
n = LCTF_INFO_VLEN (fp, tp->ctt_info);
|
|
if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
|
|
{
|
|
vlen = dtd->dtd_vlen;
|
|
vbytes = dtd->dtd_vlen_alloc;
|
|
}
|
|
else
|
|
{
|
|
vlen = (unsigned char *) tp + increment;
|
|
vbytes = LCTF_VBYTES (fp, kind, size, n);
|
|
}
|
|
|
|
for (; n != 0; n--, i++)
|
|
{
|
|
ctf_lmember_t memb;
|
|
|
|
if (ctf_struct_member (fp, &memb, tp, vlen, vbytes, i) < 0)
|
|
return -1; /* errno is set for us. */
|
|
|
|
if ((rc = ctf_type_rvisit (fp, memb.ctlm_type,
|
|
func, arg, ctf_strptr (fp, memb.ctlm_name),
|
|
offset + (unsigned long) CTF_LMEM_OFFSET (&memb),
|
|
depth + 1)) != 0)
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Recursively visit the members of any type. We pass the name, member
|
|
type, and offset of each member to the specified callback function. */
|
|
int
|
|
ctf_type_visit (ctf_dict_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg)
|
|
{
|
|
return (ctf_type_rvisit (fp, type, func, arg, "", 0, 0));
|
|
}
|