diff options
Diffstat (limited to 'cddl/contrib/opensolaris/common/ctf/ctf_types.c')
-rw-r--r-- | cddl/contrib/opensolaris/common/ctf/ctf_types.c | 845 |
1 files changed, 845 insertions, 0 deletions
diff --git a/cddl/contrib/opensolaris/common/ctf/ctf_types.c b/cddl/contrib/opensolaris/common/ctf/ctf_types.c new file mode 100644 index 000000000000..290c518ae72b --- /dev/null +++ b/cddl/contrib/opensolaris/common/ctf/ctf_types.c @@ -0,0 +1,845 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <ctf_impl.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); +} + +/* + * 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_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); /* errno is set for us */ + + if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) + return (CTF_ERR); /* 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_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); +} + +/* + * 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_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); /* errno is set for us */ + + 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_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); +} + +/* + * Iterate over every root (user-visible) type in the given CTF container. + * We pass the type ID of each type to the specified callback function. + */ +int +ctf_type_iter(ctf_file_t *fp, 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 (CTF_INFO_ISROOT(tp->ctt_info) && + (rc = func(CTF_INDEX_TO_TYPE(id, child), arg)) != 0) + return (rc); + } + + return (0); +} + +/* + * 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. + */ +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); /* errno is set for us */ +} + +/* + * 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_file_t *fp, ctf_id_t type, char *buf, size_t len) +{ + 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); /* simplify caller code by permitting CTF_ERR */ + + 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)); + } + + /* + * 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_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: + case CTF_K_FLOAT: + case CTF_K_TYPEDEF: + 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: + ctf_decl_sprintf(&cd, "()"); + break; + case CTF_K_STRUCT: + case CTF_K_FORWARD: + 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_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) + ctf_decl_sprintf(&cd, ")"); + } + + if (cd.cd_len >= len) + (void) ctf_set_errno(fp, ECTF_NAMELEN); + + ctf_decl_fini(&cd); + return (cd.cd_len); +} + +/* + * 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_file_t *fp, ctf_id_t type, char *buf, size_t len) +{ + ssize_t rv = ctf_type_lname(fp, type, buf, len); + return (rv >= 0 && rv < len ? buf : 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_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); /* 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: + /* + * Array size is not directly returned by stabs data. Instead, + * it defines the element type and requires the user to perform + * the multiplication. If ctf_get_ctt_size() returns zero, the + * current version of ctfconvert does not compute member sizes + * and we compute the size here on its behalf. + */ + 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); /* errno is set for us */ + + return (size * ar.ctr_nelems); + + default: + 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. + */ +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); /* 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: + 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); /* errno is set for us */ + 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); /* only use first member for structs */ + + 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: + return (fp->ctf_dmodel->ctd_int); + + default: + return (ctf_get_ctt_size(fp, tp, NULL, NULL)); + } +} + +/* + * Return the kind (CTF_K_* constant) for the specified type ID. + */ +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); /* errno is set for us */ + + return (LCTF_INFO_KIND(fp, tp->ctt_info)); +} + +/* + * 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_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); /* 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); + 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. + */ +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); /* errno is set for us */ + + 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)); +} + +/* + * Return the encoding for the specified INTEGER or FLOAT. + */ +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); /* errno is set for us */ + + (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); +} + +/* + * Return a boolean value indicating if two types are compatible integers or + * floating-pointer values. This function returns true if the two types are + * the same, or if they have the same ASCII name and encoding properties. + * This function could be extended to test for compatibility for other kinds. + */ +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); /* no other checks required for these type kinds */ + default: + return (0); /* should not get here since we did a resolve */ + } +} + +/* + * Return the type and offset for a given member of a STRUCT or UNION. + */ +int +ctf_member_info(ctf_file_t *fp, ctf_id_t type, const char *name, + ctf_membinfo_t *mip) +{ + ctf_file_t *ofp = fp; + const ctf_type_t *tp; + ssize_t size, increment; + uint_t kind, n; + + if ((type = ctf_type_resolve(fp, type)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) + return (CTF_ERR); /* 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_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++) { + if (strcmp(ctf_strptr(fp, mp->ctm_name), name) == 0) { + mip->ctm_type = mp->ctm_type; + mip->ctm_offset = mp->ctm_offset; + return (0); + } + } + } 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 (strcmp(ctf_strptr(fp, lmp->ctlm_name), name) == 0) { + mip->ctm_type = lmp->ctlm_type; + mip->ctm_offset = (ulong_t)CTF_LMEM_OFFSET(lmp); + 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_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); /* errno is set for us */ + + 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); +} + +/* + * Convert the specified value to the corresponding enum member name, if a + * matching name can be found. Otherwise NULL is returned. + */ +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); /* 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) { + (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); +} + +/* + * 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_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); /* errno is set for us */ + + 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_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); +} + +/* + * 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_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); /* errno is set for us */ + + if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) + return (CTF_ERR); /* errno is set for us */ + + 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); +} + +/* + * 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_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg) +{ + return (ctf_type_rvisit(fp, type, func, arg, "", 0, 0)); +} |