#include <ctf_impl.h>
#include <sys/debug.h>
ssize_t
ctf_get_ctt_size(const ctf_file_t *fp, const ctf_type_t *tp, ssize_t *sizep,
ssize_t *incrementp)
{
ssize_t size, increment;
if (fp->ctf_version > CTF_VERSION_1 &&
tp->ctt_size == CTF_LSIZE_SENT) {
size = CTF_TYPE_LSIZE(tp);
increment = sizeof (ctf_type_t);
} else {
size = tp->ctt_size;
increment = sizeof (ctf_stype_t);
}
if (sizep)
*sizep = size;
if (incrementp)
*incrementp = increment;
return (size);
}
void
ctf_set_ctt_size(ctf_type_t *tp, ssize_t size)
{
if (size > CTF_MAX_SIZE) {
tp->ctt_size = CTF_LSIZE_SENT;
tp->ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size);
tp->ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size);
} else {
tp->ctt_size = (ushort_t)size;
}
}
int
ctf_member_iter(ctf_file_t *fp, ctf_id_t type, ctf_member_f *func, void *arg)
{
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
ssize_t size, increment;
uint_t kind, n;
int rc;
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
return (CTF_ERR);
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
(void) 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));
if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
const ctf_member_t *mp = (const ctf_member_t *)
((uintptr_t)tp + increment);
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
const char *name = ctf_strptr(fp, mp->ctm_name);
if ((rc = func(name, mp->ctm_type, mp->ctm_offset,
arg)) != 0)
return (rc);
}
} else {
const ctf_lmember_t *lmp = (const ctf_lmember_t *)
((uintptr_t)tp + increment);
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
const char *name = ctf_strptr(fp, lmp->ctlm_name);
if ((rc = func(name, lmp->ctlm_type,
(ulong_t)CTF_LMEM_OFFSET(lmp), arg)) != 0)
return (rc);
}
}
return (0);
}
int
ctf_enum_iter(ctf_file_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg)
{
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
const ctf_enum_t *ep;
ssize_t increment;
uint_t n;
int rc;
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
return (CTF_ERR);
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM)
return (ctf_set_errno(ofp, ECTF_NOTENUM));
(void) ctf_get_ctt_size(fp, tp, NULL, &increment);
ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
const char *name = ctf_strptr(fp, ep->cte_name);
if ((rc = func(name, ep->cte_value, arg)) != 0)
return (rc);
}
return (0);
}
int
ctf_type_iter(ctf_file_t *fp, boolean_t nonroot, ctf_type_f *func, void *arg)
{
ctf_id_t id, max = fp->ctf_typemax;
int rc, child = (fp->ctf_flags & LCTF_CHILD);
for (id = 1; id <= max; id++) {
const ctf_type_t *tp = LCTF_INDEX_TO_TYPEPTR(fp, id);
if ((nonroot || CTF_INFO_ISROOT(tp->ctt_info)) &&
(rc = func(CTF_INDEX_TO_TYPE(id, child),
CTF_INFO_ISROOT(tp->ctt_info), arg)) != 0)
return (rc);
}
return (0);
}
ctf_id_t
ctf_type_resolve(ctf_file_t *fp, ctf_id_t type)
{
ctf_id_t prev = type, otype = type;
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
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_dprintf("type %ld cycle detected\n", otype);
return (ctf_set_errno(ofp, ECTF_CORRUPT));
}
prev = type;
type = tp->ctt_type;
break;
default:
return (type);
}
}
return (CTF_ERR);
}
static const char *
ctf_format_int(ctf_decl_t *cd, const char *vname, const char *qname,
const char *name)
{
const char *c;
if (vname == NULL) {
if (qname != NULL)
ctf_decl_sprintf(cd, "%s`%s", qname, name);
else
ctf_decl_sprintf(cd, "%s", name);
return (NULL);
}
if ((c = strchr(name, ':')) == NULL) {
ctf_decl_sprintf(cd, "%s", name);
return (vname);
}
ctf_decl_sprintf(cd, "%.*s %s%s", c - name, name, vname, c);
return (NULL);
}
static void
ctf_format_func(ctf_file_t *fp, ctf_decl_t *cd,
const char *vname, ctf_id_t id, int want_func_args)
{
ctf_funcinfo_t fi;
ctf_id_t args[20];
ctf_decl_sprintf(cd, "%s(", vname == NULL ? "" : vname);
if (!want_func_args)
goto out;
if (ctf_func_info_by_id(fp, id, &fi) != 0)
goto out;
if (fi.ctc_argc > ARRAY_SIZE(args))
fi.ctc_argc = ARRAY_SIZE(args);
if (fi.ctc_argc == 0) {
ctf_decl_sprintf(cd, "void");
goto out;
}
if (ctf_func_args_by_id(fp, id, fi.ctc_argc, args) != 0)
goto out;
for (size_t i = 0; i < fi.ctc_argc; i++) {
char aname[512];
if (ctf_type_name(fp, args[i], aname, sizeof (aname)) == NULL)
(void) strlcpy(aname, "unknown_t", sizeof (aname));
ctf_decl_sprintf(cd, "%s%s", aname,
i + 1 == fi.ctc_argc ? "" : ", ");
}
if (fi.ctc_flags & CTF_FUNC_VARARG)
ctf_decl_sprintf(cd, "%s...", fi.ctc_argc == 0 ? "" : ", ");
out:
ctf_decl_sprintf(cd, ")");
}
static ssize_t
ctf_type_qlname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
const char *vname, const char *qname)
{
int want_func_args = (vname != NULL);
ctf_decl_t cd;
ctf_decl_node_t *cdp;
ctf_decl_prec_t prec, lp, rp;
int ptr, arr;
uint_t k;
if (fp == NULL && type == CTF_ERR)
return (-1);
ctf_decl_init(&cd, buf, len);
ctf_decl_push(&cd, fp, type);
if (cd.cd_err != 0) {
ctf_decl_fini(&cd);
return (ctf_set_errno(fp, cd.cd_err));
}
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;
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_file_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:
vname = ctf_format_int(&cd, vname, qname, name);
break;
case CTF_K_FLOAT:
case CTF_K_TYPEDEF:
if (qname != NULL)
ctf_decl_sprintf(&cd, "%s`", qname);
ctf_decl_sprintf(&cd, "%s", name);
break;
case CTF_K_POINTER:
ctf_decl_sprintf(&cd, "*");
break;
case CTF_K_ARRAY:
ctf_decl_sprintf(&cd, "%s[%u]",
vname != NULL ? vname : "", cdp->cd_n);
vname = NULL;
break;
case CTF_K_FUNCTION:
ctf_format_func(fp, &cd, vname,
cdp->cd_type, want_func_args);
vname = NULL;
break;
case CTF_K_FORWARD:
switch (tp->ctt_type) {
case CTF_K_UNION:
ctf_decl_sprintf(&cd, "union ");
break;
case CTF_K_ENUM:
ctf_decl_sprintf(&cd, "enum ");
break;
case CTF_K_STRUCT:
default:
ctf_decl_sprintf(&cd, "struct ");
break;
}
if (qname != NULL)
ctf_decl_sprintf(&cd, "%s`", qname);
ctf_decl_sprintf(&cd, "%s", name);
break;
case CTF_K_STRUCT:
ctf_decl_sprintf(&cd, "struct ");
if (qname != NULL)
ctf_decl_sprintf(&cd, "%s`", qname);
ctf_decl_sprintf(&cd, "%s", name);
break;
case CTF_K_UNION:
ctf_decl_sprintf(&cd, "union ");
if (qname != NULL)
ctf_decl_sprintf(&cd, "%s`", qname);
ctf_decl_sprintf(&cd, "%s", name);
break;
case CTF_K_ENUM:
ctf_decl_sprintf(&cd, "enum ");
if (qname != NULL)
ctf_decl_sprintf(&cd, "%s`", qname);
ctf_decl_sprintf(&cd, "%s", name);
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;
}
k = cdp->cd_kind;
}
if (rp == prec) {
if (vname != NULL && prec < CTF_PREC_FUNCTION) {
cdp = ctf_list_next(
&cd.cd_nodes[CTF_PREC_FUNCTION]);
if (cdp != NULL) {
ctf_decl_sprintf(&cd, "%s", vname);
vname = NULL;
}
}
ctf_decl_sprintf(&cd, ")");
}
}
if (vname != NULL)
ctf_decl_sprintf(&cd, " %s", vname);
if (cd.cd_len >= len)
(void) ctf_set_errno(fp, ECTF_NAMELEN);
ctf_decl_fini(&cd);
return (cd.cd_len);
}
ssize_t
ctf_type_lname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
{
return (ctf_type_qlname(fp, type, buf, len, NULL, NULL));
}
char *
ctf_type_name(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
{
ssize_t rv = ctf_type_qlname(fp, type, buf, len, NULL, NULL);
return (rv >= 0 && rv < len ? buf : NULL);
}
char *
ctf_type_qname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
const char *qname)
{
ssize_t rv = ctf_type_qlname(fp, type, buf, len, NULL, qname);
return (rv >= 0 && rv < len ? buf : NULL);
}
char *
ctf_type_cname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len,
const char *cname)
{
ssize_t rv = ctf_type_qlname(fp, type, buf, len, cname, NULL);
return (rv >= 0 && rv < len ? buf : NULL);
}
ssize_t
ctf_type_size(ctf_file_t *fp, ctf_id_t type)
{
const ctf_type_t *tp;
ssize_t size;
ctf_arinfo_t ar;
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
return (-1);
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (-1);
switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
case CTF_K_POINTER:
return (fp->ctf_dmodel->ctd_pointer);
case CTF_K_FUNCTION:
return (0);
case CTF_K_FORWARD:
return (0);
case CTF_K_ENUM:
return (ctf_get_ctt_size(fp, tp, NULL, NULL));
case CTF_K_ARRAY:
if ((size = ctf_get_ctt_size(fp, tp, NULL, NULL)) > 0)
return (size);
if (ctf_array_info(fp, type, &ar) == CTF_ERR ||
(size = ctf_type_size(fp, ar.ctr_contents)) == CTF_ERR)
return (-1);
return (size * ar.ctr_nelems);
case CTF_K_STRUCT:
case CTF_K_UNION:
size = ctf_get_ctt_size(fp, tp, NULL, NULL);
if (size == 0) {
ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, type);
if (dtd != NULL)
return (dtd->dtd_data.ctt_size);
}
return (size);
default:
return (ctf_get_ctt_size(fp, tp, NULL, NULL));
}
}
ssize_t
ctf_type_align(ctf_file_t *fp, ctf_id_t type)
{
const ctf_type_t *tp;
ctf_arinfo_t r;
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
return (-1);
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (-1);
switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
case CTF_K_POINTER:
case CTF_K_FUNCTION:
return (fp->ctf_dmodel->ctd_pointer);
case CTF_K_ARRAY:
if (ctf_array_info(fp, type, &r) == CTF_ERR)
return (-1);
return (ctf_type_align(fp, r.ctr_contents));
case CTF_K_STRUCT:
case CTF_K_UNION: {
uint_t n = LCTF_INFO_VLEN(fp, tp->ctt_info);
ssize_t size, increment;
size_t align = 0;
const void *vmp;
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
vmp = (uchar_t *)tp + increment;
if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_STRUCT)
n = MIN(n, 1);
if (fp->ctf_version == CTF_VERSION_1 ||
size < CTF_LSTRUCT_THRESH) {
const ctf_member_t *mp = vmp;
for (; n != 0; n--, mp++) {
ssize_t am = ctf_type_align(fp, mp->ctm_type);
align = MAX(align, am);
}
} else {
const ctf_lmember_t *lmp = vmp;
for (; n != 0; n--, lmp++) {
ssize_t am = ctf_type_align(fp, lmp->ctlm_type);
align = MAX(align, am);
}
}
return (align);
}
case CTF_K_ENUM:
default:
return (ctf_get_ctt_size(fp, tp, NULL, NULL));
}
}
int
ctf_type_kind(ctf_file_t *fp, ctf_id_t type)
{
const ctf_type_t *tp;
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
return (LCTF_INFO_KIND(fp, tp->ctt_info));
}
ctf_id_t
ctf_type_reference(ctf_file_t *fp, ctf_id_t type)
{
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
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);
default:
return (ctf_set_errno(ofp, ECTF_NOTREF));
}
}
ctf_id_t
ctf_type_pointer(ctf_file_t *fp, ctf_id_t type)
{
ctf_file_t *ofp = fp;
ctf_id_t ntype;
if (ctf_lookup_by_id(&fp, type) == NULL)
return (CTF_ERR);
if ((ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]) != 0)
return (CTF_INDEX_TO_TYPE(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[CTF_TYPE_TO_INDEX(type)]) != 0)
return (CTF_INDEX_TO_TYPE(ntype, (fp->ctf_flags & LCTF_CHILD)));
return (ctf_set_errno(ofp, ECTF_NOTYPE));
}
int
ctf_type_encoding(ctf_file_t *fp, ctf_id_t type, ctf_encoding_t *ep)
{
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
ssize_t increment;
uint_t data;
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
(void) ctf_get_ctt_size(fp, tp, NULL, &increment);
switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
case CTF_K_INTEGER:
data = *(const uint_t *)((uintptr_t)tp + increment);
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 uint_t *)((uintptr_t)tp + increment);
ep->cte_format = CTF_FP_ENCODING(data);
ep->cte_offset = CTF_FP_OFFSET(data);
ep->cte_bits = CTF_FP_BITS(data);
break;
default:
return (ctf_set_errno(ofp, ECTF_NOTINTFP));
}
return (0);
}
int
ctf_type_cmp(ctf_file_t *lfp, ctf_id_t ltype, ctf_file_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 (CTF_TYPE_ISPARENT(ltype) && lfp->ctf_parent != NULL)
lfp = lfp->ctf_parent;
if (CTF_TYPE_ISPARENT(rtype) && rfp->ctf_parent != NULL)
rfp = rfp->ctf_parent;
if (lfp < rfp)
return (-1);
if (lfp > rfp)
return (1);
return (rval);
}
int
ctf_type_compat(ctf_file_t *lfp, ctf_id_t ltype,
ctf_file_t *rfp, ctf_id_t rtype)
{
const ctf_type_t *ltp, *rtp;
ctf_encoding_t le, re;
ctf_arinfo_t la, ra;
uint_t lkind, rkind;
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);
if (lkind != rkind ||
(ltp = ctf_lookup_by_id(&lfp, ltype)) == NULL ||
(rtp = ctf_lookup_by_id(&rfp, rtype)) == NULL ||
strcmp(ctf_strptr(lfp, ltp->ctt_name),
ctf_strptr(rfp, rtp->ctt_name)) != 0)
return (0);
switch (lkind) {
case CTF_K_INTEGER:
case CTF_K_FLOAT:
return (ctf_type_encoding(lfp, ltype, &le) == 0 &&
ctf_type_encoding(rfp, rtype, &re) == 0 &&
bcmp(&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 (ctf_type_size(lfp, ltype) == ctf_type_size(rfp, rtype));
case CTF_K_ENUM:
case CTF_K_FORWARD:
return (1);
default:
return (0);
}
}
typedef struct {
ctf_file_t *cms_fp;
const ctf_type_t *cms_tp;
ulong_t cms_curoff;
} ctf_member_stack_t;
static void
ctf_member_info_push(ctf_member_stack_t *stack, size_t *depthp, size_t max,
const ctf_member_stack_t *cur, ushort_t mtype, const char *mname,
ulong_t moff)
{
uint_t kind;
ctf_member_stack_t *cms;
if (*depthp == max)
return;
if (*mname != '\0')
return;
cms = &stack[*depthp];
cms->cms_fp = cur->cms_fp;
cms->cms_tp = ctf_lookup_by_id(&cms->cms_fp, mtype);
if (cms->cms_tp == NULL)
return;
kind = LCTF_INFO_KIND(cms->cms_fp, cms->cms_tp->ctt_info);
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
return;
cms->cms_curoff = cur->cms_curoff + moff;
*depthp = *depthp + 1;
}
int
ctf_member_info(ctf_file_t *ifp, ctf_id_t type, const char *name,
ctf_membinfo_t *mip)
{
uint_t kind;
ctf_member_stack_t stack[128];
size_t depth = 0;
if ((type = ctf_type_resolve(ifp, type)) == CTF_ERR)
return (CTF_ERR);
stack[depth].cms_fp = ifp;
stack[depth].cms_curoff = 0;
stack[depth].cms_tp = ctf_lookup_by_id(&stack[depth].cms_fp, type);
if (stack[depth].cms_tp == NULL)
return (CTF_ERR);
kind = LCTF_INFO_KIND(stack[depth].cms_fp,
stack[depth].cms_tp->ctt_info);
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
return (ctf_set_errno(ifp, ECTF_NOTSOU));
depth++;
while (depth != 0) {
ssize_t size, increment;
ctf_member_stack_t cms;
depth--;
cms = stack[depth];
(void) ctf_get_ctt_size(cms.cms_fp, cms.cms_tp, &size,
&increment);
if (cms.cms_fp->ctf_version == CTF_VERSION_1 ||
size < CTF_LSTRUCT_THRESH) {
const ctf_member_t *mp = (const ctf_member_t *)
((uintptr_t)cms.cms_tp + increment);
for (uint_t n = LCTF_INFO_VLEN(cms.cms_fp,
cms.cms_tp->ctt_info); n != 0; n--, mp++) {
const char *mname = ctf_strptr(cms.cms_fp,
mp->ctm_name);
if (strcmp(mname, name) == 0) {
mip->ctm_type = mp->ctm_type;
mip->ctm_offset = mp->ctm_offset +
cms.cms_curoff;
return (0);
}
ctf_member_info_push(stack, &depth,
ARRAY_SIZE(stack), &cms, mp->ctm_type,
mname, mp->ctm_offset);
}
} else {
const ctf_lmember_t *lmp = (const ctf_lmember_t *)
((uintptr_t)cms.cms_tp + increment);
for (uint_t n = LCTF_INFO_VLEN(cms.cms_fp,
cms.cms_tp->ctt_info); n != 0; n--, lmp++) {
const char *mname = ctf_strptr(cms.cms_fp,
lmp->ctlm_name);
ulong_t off = (ulong_t)CTF_LMEM_OFFSET(lmp);
if (strcmp(mname, name) == 0) {
mip->ctm_type = lmp->ctlm_type;
mip->ctm_offset = cms.cms_curoff + off;
return (0);
}
ctf_member_info_push(stack, &depth,
ARRAY_SIZE(stack), &cms, lmp->ctlm_type,
mname, off);
}
}
}
return (ctf_set_errno(ifp, ECTF_NOMEMBNAM));
}
int
ctf_array_info(ctf_file_t *fp, ctf_id_t type, ctf_arinfo_t *arp)
{
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
const ctf_array_t *ap;
ssize_t increment;
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ARRAY)
return (ctf_set_errno(ofp, ECTF_NOTARRAY));
(void) 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);
}
const char *
ctf_enum_name(ctf_file_t *fp, ctf_id_t type, int value)
{
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
const ctf_enum_t *ep;
ssize_t increment;
uint_t n;
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
return (NULL);
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (NULL);
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) {
(void) ctf_set_errno(ofp, ECTF_NOTENUM);
return (NULL);
}
(void) ctf_get_ctt_size(fp, tp, NULL, &increment);
ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
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));
}
(void) ctf_set_errno(ofp, ECTF_NOENUMNAM);
return (NULL);
}
int
ctf_enum_value(ctf_file_t *fp, ctf_id_t type, const char *name, int *valp)
{
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
const ctf_enum_t *ep;
ssize_t size, increment;
uint_t n;
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
return (CTF_ERR);
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) {
(void) ctf_set_errno(ofp, ECTF_NOTENUM);
return (CTF_ERR);
}
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
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);
}
}
(void) ctf_set_errno(ofp, ECTF_NOENUMNAM);
return (CTF_ERR);
}
static int
ctf_type_rvisit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg,
const char *name, ulong_t offset, int depth)
{
ctf_id_t otype = type;
const ctf_type_t *tp;
ssize_t size, increment;
uint_t kind, n;
int rc;
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
return (CTF_ERR);
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
if ((rc = func(name, otype, offset, depth, arg)) != 0)
return (rc);
kind = LCTF_INFO_KIND(fp, tp->ctt_info);
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
return (0);
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
const ctf_member_t *mp = (const ctf_member_t *)
((uintptr_t)tp + increment);
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
if ((rc = ctf_type_rvisit(fp, mp->ctm_type,
func, arg, ctf_strptr(fp, mp->ctm_name),
offset + mp->ctm_offset, depth + 1)) != 0)
return (rc);
}
} else {
const ctf_lmember_t *lmp = (const ctf_lmember_t *)
((uintptr_t)tp + increment);
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
if ((rc = ctf_type_rvisit(fp, lmp->ctlm_type,
func, arg, ctf_strptr(fp, lmp->ctlm_name),
offset + (ulong_t)CTF_LMEM_OFFSET(lmp),
depth + 1)) != 0)
return (rc);
}
}
return (0);
}
int
ctf_type_visit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg)
{
return (ctf_type_rvisit(fp, type, func, arg, "", 0, 0));
}
int
ctf_func_info_by_id(ctf_file_t *fp, ctf_id_t type, ctf_funcinfo_t *fip)
{
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
const ushort_t *dp;
int nargs;
ssize_t increment;
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_FUNCTION)
return (ctf_set_errno(ofp, ECTF_NOTFUNC));
fip->ctc_return = tp->ctt_type;
nargs = LCTF_INFO_VLEN(fp, tp->ctt_info);
fip->ctc_argc = nargs;
fip->ctc_flags = 0;
if (nargs != 0) {
(void) ctf_get_ctt_size(fp, tp, NULL, &increment);
dp = (ushort_t *)((uintptr_t)fp->ctf_buf +
fp->ctf_txlate[CTF_TYPE_TO_INDEX(type)] + increment);
if (dp[nargs - 1] == 0) {
fip->ctc_flags |= CTF_FUNC_VARARG;
fip->ctc_argc--;
}
}
return (0);
}
int
ctf_func_args_by_id(ctf_file_t *fp, ctf_id_t type, uint_t argc, ctf_id_t *argv)
{
ctf_file_t *ofp = fp;
const ctf_type_t *tp;
const ushort_t *dp;
int nargs;
ssize_t increment;
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
return (CTF_ERR);
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_FUNCTION)
return (ctf_set_errno(ofp, ECTF_NOTFUNC));
nargs = LCTF_INFO_VLEN(fp, tp->ctt_info);
(void) ctf_get_ctt_size(fp, tp, NULL, &increment);
dp = (ushort_t *)((uintptr_t)fp->ctf_buf +
fp->ctf_txlate[CTF_TYPE_TO_INDEX(type)] +
increment);
if (nargs != 0 && dp[nargs - 1] == 0)
nargs--;
for (nargs = MIN(argc, nargs); nargs != 0; nargs--)
*argv++ = *dp++;
return (0);
}
int
ctf_object_iter(ctf_file_t *fp, ctf_object_f *func, void *arg)
{
int i, ret;
ctf_id_t id;
uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
if (fp->ctf_symtab.cts_data == NULL)
return (ctf_set_errno(fp, ECTF_NOSYMTAB));
for (i = 0; i < fp->ctf_nsyms; i++) {
char *name;
if (fp->ctf_sxlate[i] == -1u)
continue;
id = *(ushort_t *)((uintptr_t)fp->ctf_buf +
fp->ctf_sxlate[i]);
if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT)
continue;
if (fp->ctf_strtab.cts_data != NULL &&
symp->st_name != 0)
name = (char *)(strbase + symp->st_name);
else
name = NULL;
} else {
const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT)
continue;
if (fp->ctf_strtab.cts_data != NULL &&
symp->st_name != 0)
name = (char *)(strbase + symp->st_name);
else
name = NULL;
}
if ((ret = func(name, id, i, arg)) != 0)
return (ret);
}
return (0);
}
int
ctf_function_iter(ctf_file_t *fp, ctf_function_f *func, void *arg)
{
int i, ret;
uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
if (fp->ctf_symtab.cts_data == NULL)
return (ctf_set_errno(fp, ECTF_NOSYMTAB));
for (i = 0; i < fp->ctf_nsyms; i++) {
char *name;
ushort_t info, *dp;
ctf_funcinfo_t fi;
if (fp->ctf_sxlate[i] == -1u)
continue;
dp = (ushort_t *)((uintptr_t)fp->ctf_buf +
fp->ctf_sxlate[i]);
info = *dp;
if (info == 0)
continue;
if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
continue;
if (fp->ctf_strtab.cts_data != NULL)
name = (char *)(strbase + symp->st_name);
else
name = NULL;
} else {
const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
continue;
if (fp->ctf_strtab.cts_data != NULL)
name = (char *)(strbase + symp->st_name);
else
name = NULL;
}
if (LCTF_INFO_KIND(fp, info) != CTF_K_FUNCTION)
continue;
dp++;
fi.ctc_return = *dp;
dp++;
fi.ctc_argc = LCTF_INFO_VLEN(fp, info);
fi.ctc_flags = 0;
if (fi.ctc_argc != 0 && dp[fi.ctc_argc - 1] == 0) {
fi.ctc_flags |= CTF_FUNC_VARARG;
fi.ctc_argc--;
}
if ((ret = func(name, i, &fi, arg)) != 0)
return (ret);
}
return (0);
}
char *
ctf_symbol_name(ctf_file_t *fp, ulong_t idx, char *buf, size_t len)
{
const char *name;
uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
if (fp->ctf_symtab.cts_data == NULL) {
(void) ctf_set_errno(fp, ECTF_NOSYMTAB);
return (NULL);
}
if (fp->ctf_strtab.cts_data == NULL) {
(void) ctf_set_errno(fp, ECTF_STRTAB);
return (NULL);
}
if (idx > fp->ctf_nsyms) {
(void) ctf_set_errno(fp, ECTF_NOTDATA);
return (NULL);
}
if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx;
if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT &&
ELF32_ST_TYPE(symp->st_info) != STT_FUNC) {
(void) ctf_set_errno(fp, ECTF_NOTDATA);
return (NULL);
}
if (symp->st_name == 0) {
(void) ctf_set_errno(fp, ENOENT);
return (NULL);
}
name = (const char *)(strbase + symp->st_name);
} else {
const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx;
if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC &&
ELF64_ST_TYPE(symp->st_info) != STT_OBJECT) {
(void) ctf_set_errno(fp, ECTF_NOTDATA);
return (NULL);
}
if (symp->st_name == 0) {
(void) ctf_set_errno(fp, ENOENT);
return (NULL);
}
name = (const char *)(strbase + symp->st_name);
}
(void) strlcpy(buf, name, len);
return (buf);
}
int
ctf_string_iter(ctf_file_t *fp, ctf_string_f *func, void *arg)
{
int rc;
const char *strp = fp->ctf_str[CTF_STRTAB_0].cts_strs;
size_t strl = fp->ctf_str[CTF_STRTAB_0].cts_len;
while (strl > 0) {
size_t len;
if ((rc = func(strp, arg)) != 0)
return (rc);
len = strlen(strp) + 1;
strl -= len;
strp += len;
}
return (0);
}
const char *
ctf_kind_name(ctf_file_t *fp, int kind)
{
switch (kind) {
case CTF_K_INTEGER:
return ("integer");
case CTF_K_FLOAT:
return ("float");
case CTF_K_POINTER:
return ("pointer");
case CTF_K_ARRAY:
return ("array");
case CTF_K_FUNCTION:
return ("function");
case CTF_K_STRUCT:
return ("struct");
case CTF_K_UNION:
return ("union");
case CTF_K_ENUM:
return ("enum");
case CTF_K_FORWARD:
return ("forward");
case CTF_K_TYPEDEF:
return ("typedef");
case CTF_K_VOLATILE:
return ("volatile");
case CTF_K_CONST:
return ("const");
case CTF_K_RESTRICT:
return ("restrict");
case CTF_K_UNKNOWN:
default:
return ("unknown");
}
}
ctf_id_t
ctf_max_id(ctf_file_t *fp)
{
int child = (fp->ctf_flags & LCTF_CHILD);
return (fp->ctf_typemax + (child ? CTF_CHILD_START : 0));
}
ulong_t
ctf_nr_syms(ctf_file_t *fp)
{
return (fp->ctf_nsyms);
}