root/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */
/*
 * Copyright (c) 2013, 2016 by Delphix. All rights reserved.
 * Copyright (c) 2018, Joyent, Inc.
 * Copyright 2025 Oxide Computer Company
 */

#include <mdb/mdb_ctf.h>
#include <mdb/mdb_ctf_impl.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_string.h>
#include <mdb/mdb.h>
#include <mdb/mdb_debug.h>

#include <libctf.h>
#include <string.h>
#include <limits.h>

typedef struct tnarg {
        mdb_tgt_t *tn_tgt;              /* target to use for lookup */
        const char *tn_name;            /* query string to lookup */
        ctf_file_t *tn_fp;              /* CTF container from match */
        ctf_id_t tn_id;                 /* CTF type ID from match */
} tnarg_t;

typedef struct type_iter {
        mdb_ctf_type_f *ti_cb;
        void *ti_arg;
        ctf_file_t *ti_fp;
} type_iter_t;

typedef struct member_iter {
        mdb_ctf_member_f *mi_cb;
        void *mi_arg;
        ctf_file_t *mi_fp;
        uint32_t mi_flags;
        ulong_t mi_off;
} member_iter_t;

typedef struct type_visit {
        mdb_ctf_visit_f *tv_cb;
        void            *tv_arg;
        ctf_file_t      *tv_fp;
        ulong_t         tv_base_offset; /* used when recursing from type_cb() */
        int             tv_base_depth;  /* used when recursing from type_cb() */
        int             tv_min_depth;
} type_visit_t;

typedef struct mbr_info {
        const char *mbr_member;
        ulong_t *mbr_offp;
        mdb_ctf_id_t *mbr_typep;
} mbr_info_t;

typedef struct synth_intrinsic {
        const char *syn_name;
        ctf_encoding_t syn_enc;
        uint_t syn_kind;
} synth_intrinsic_t;

typedef struct synth_typedef {
        const char *syt_src;
        const char *syt_targ;
} synth_typedef_t;

/*
 * As part of our support for synthetic types via ::typedef, we define a core
 * set of types.
 */
static const synth_intrinsic_t synth_builtins32[] = {
{ "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER },
{ "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
{ "unsigned", { 0, 0, 32 }, CTF_K_INTEGER },
{ "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
{ "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER },
{ "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
{ "long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
{ "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
{ "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
{ "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER },
{ "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
{ "signed long", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
{ "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
{ "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
{ "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER },
{ "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER },
{ "unsigned long", { 0, 0, 32 }, CTF_K_INTEGER },
{ "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER },
{ "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER },
{ "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT },
{ "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT },
{ "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT },
{ "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT },
{ "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT },
{ "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT },
{ "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT },
{ "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT },
{ "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT },
{ NULL, { 0, 0, 0}, 0 }
};

static const synth_intrinsic_t synth_builtins64[] = {
{ "void", { CTF_INT_SIGNED, 0, 0 }, CTF_K_INTEGER },
{ "signed", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
{ "unsigned", { 0, 0, 32 }, CTF_K_INTEGER },
{ "char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
{ "short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER },
{ "int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
{ "long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
{ "long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
{ "signed char", { CTF_INT_SIGNED | CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
{ "signed short", { CTF_INT_SIGNED, 0, 16 }, CTF_K_INTEGER },
{ "signed int", { CTF_INT_SIGNED, 0, 32 }, CTF_K_INTEGER },
{ "signed long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
{ "signed long long", { CTF_INT_SIGNED, 0, 64 }, CTF_K_INTEGER },
{ "unsigned char", { CTF_INT_CHAR, 0, 8 }, CTF_K_INTEGER },
{ "unsigned short", { 0, 0, 16 }, CTF_K_INTEGER },
{ "unsigned int", { 0, 0, 32 }, CTF_K_INTEGER },
{ "unsigned long", { 0, 0, 64 }, CTF_K_INTEGER },
{ "unsigned long long", { 0, 0, 64 }, CTF_K_INTEGER },
{ "_Bool", { CTF_INT_BOOL, 0, 8 }, CTF_K_INTEGER },
{ "float", { CTF_FP_SINGLE, 0, 32 }, CTF_K_FLOAT },
{ "double", { CTF_FP_DOUBLE, 0, 64 }, CTF_K_FLOAT },
{ "long double", { CTF_FP_LDOUBLE, 0, 128 }, CTF_K_FLOAT },
{ "float imaginary", { CTF_FP_IMAGRY, 0, 32 }, CTF_K_FLOAT },
{ "double imaginary", { CTF_FP_DIMAGRY, 0, 64 }, CTF_K_FLOAT },
{ "long double imaginary", { CTF_FP_LDIMAGRY, 0, 128 }, CTF_K_FLOAT },
{ "float complex", { CTF_FP_CPLX, 0, 64 }, CTF_K_FLOAT },
{ "double complex", { CTF_FP_DCPLX, 0, 128 }, CTF_K_FLOAT },
{ "long double complex", { CTF_FP_LDCPLX, 0, 256 }, CTF_K_FLOAT },
{ NULL, { 0, 0, 0 }, 0 }
};

static const synth_typedef_t synth_typedefs32[] = {
{ "char", "int8_t" },
{ "short", "int16_t" },
{ "int", "int32_t" },
{ "long long", "int64_t" },
{ "int", "intptr_t" },
{ "unsigned char", "uint8_t" },
{ "unsigned short", "uint16_t" },
{ "unsigned", "uint32_t" },
{ "unsigned long long", "uint64_t" },
{ "unsigned char", "uchar_t" },
{ "unsigned short", "ushort_t" },
{ "unsigned", "uint_t" },
{ "unsigned long", "ulong_t" },
{ "unsigned long long", "u_longlong_t" },
{ "int", "ptrdiff_t" },
{ "unsigned", "uintptr_t" },
{ NULL, NULL }
};

static const synth_typedef_t synth_typedefs64[] = {
{ "char", "int8_t" },
{ "short", "int16_t" },
{ "int", "int32_t" },
{ "long", "int64_t" },
{ "long", "intptr_t" },
{ "unsigned char", "uint8_t" },
{ "unsigned short", "uint16_t" },
{ "unsigned", "uint32_t" },
{ "unsigned long", "uint64_t" },
{ "unsigned char", "uchar_t" },
{ "unsigned short", "ushort_t" },
{ "unsigned", "uint_t" },
{ "unsigned long", "ulong_t" },
{ "unsigned long long", "u_longlong_t" },
{ "long", "ptrdiff_t" },
{ "unsigned long", "uintptr_t" },
{ NULL, NULL }
};

static void
set_ctf_id(mdb_ctf_id_t *p, ctf_file_t *fp, ctf_id_t id)
{
        mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;

        mcip->mci_fp = fp;
        mcip->mci_id = id;
}

/*
 * Callback function for mdb_tgt_object_iter used from name_to_type, below,
 * to search the CTF namespace of each object file for a particular name.
 */
/*ARGSUSED*/
static int
obj_lookup(void *data, const mdb_map_t *mp, const char *name)
{
        tnarg_t *tnp = data;
        ctf_file_t *fp;
        ctf_id_t id;

        if ((fp = mdb_tgt_name_to_ctf(tnp->tn_tgt, name)) != NULL &&
            (id = ctf_lookup_by_name(fp, tnp->tn_name)) != CTF_ERR) {
                tnp->tn_fp = fp;
                tnp->tn_id = id;

                /*
                 * We may have found a forward declaration.  If we did, we'll
                 * note the ID and file pointer, but we'll keep searching in
                 * an attempt to find the real thing.  If we found something
                 * real (i.e. not a forward), we stop the iteration.
                 */
                return (ctf_type_kind(fp, id) == CTF_K_FORWARD ? 0 : -1);
        }

        return (0);
}

/*
 * Convert a string type name with an optional leading object specifier into
 * the corresponding CTF file container and type ID.  If an error occurs, we
 * print an appropriate message and return NULL.
 */
static ctf_file_t *
name_to_type(mdb_tgt_t *t, const char *cname, ctf_id_t *idp)
{
        const char *object = MDB_TGT_OBJ_EXEC;
        ctf_file_t *fp = NULL;
        ctf_id_t id = CTF_ERR;
        tnarg_t arg;
        char *p, *s;
        char buf[MDB_SYM_NAMLEN];
        char *name = &buf[0];

        (void) mdb_snprintf(buf, sizeof (buf), "%s", cname);

        if ((p = strrsplit(name, '`')) != NULL) {
                /*
                 * We need to shuffle things around a little to support
                 * type names of the form "struct module`name".
                 */
                if ((s = strsplit(name, ' ')) != NULL) {
                        bcopy(cname + (s - name), name, (p - s) - 1);
                        name[(p - s) - 1] = '\0';
                        bcopy(cname, name + (p - s), s - name);
                        p = name + (p - s);
                }
                if (*name != '\0')
                        object = name;
                name = p;
        }

        /*
         * Attempt to look up the name in the primary object file.  If this
         * fails and the name was unscoped, search all remaining object files.
         * Finally, search the synthetic types.
         */
        if (((fp = mdb_tgt_name_to_ctf(t, object)) == NULL ||
            (id = ctf_lookup_by_name(fp, name)) == CTF_ERR ||
            ctf_type_kind(fp, id) == CTF_K_FORWARD) &&
            object == MDB_TGT_OBJ_EXEC) {

                arg.tn_tgt = t;
                arg.tn_name = name;
                arg.tn_fp = NULL;
                arg.tn_id = CTF_ERR;

                (void) mdb_tgt_object_iter(t, obj_lookup, &arg);

                if (arg.tn_id != CTF_ERR) {
                        fp = arg.tn_fp;
                        id = arg.tn_id;
                } else if (mdb.m_synth != NULL) {
                        if ((id = ctf_lookup_by_name(mdb.m_synth,
                            name)) != CTF_ERR)
                                fp = mdb.m_synth;
                }
        }

        if (fp == NULL)
                return (NULL); /* errno is set for us */

        if (id == CTF_ERR) {
                (void) set_errno(ctf_to_errno(ctf_errno(fp)));
                return (NULL);
        }

        *idp = id;
        return (fp);
}

/*
 * Check to see if there is ctf data in the given object. This is useful
 * so that we don't enter some loop where every call to lookup fails.
 */
int
mdb_ctf_enabled_by_object(const char *object)
{
        mdb_tgt_t *t = mdb.m_target;

        return (mdb_tgt_name_to_ctf(t, object) != NULL);
}

int
mdb_ctf_lookup_by_name(const char *name, mdb_ctf_id_t *p)
{
        ctf_file_t *fp = NULL;
        mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;
        mdb_tgt_t *t = mdb.m_target;

        if (mcip == NULL)
                return (set_errno(EINVAL));

        if ((fp = name_to_type(t, name, &mcip->mci_id)) == NULL) {
                mdb_ctf_type_invalidate(p);
                return (-1); /* errno is set for us */
        }

        mcip->mci_fp = fp;

        return (0);
}

int
mdb_ctf_lookup_by_symbol(const GElf_Sym *symp, const mdb_syminfo_t *sip,
    mdb_ctf_id_t *p)
{
        ctf_file_t *fp = NULL;
        mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;
        mdb_tgt_t *t = mdb.m_target;

        if (mcip == NULL)
                return (set_errno(EINVAL));

        if (symp == NULL || sip == NULL) {
                mdb_ctf_type_invalidate(p);
                return (set_errno(EINVAL));
        }

        if ((fp = mdb_tgt_addr_to_ctf(t, symp->st_value)) == NULL) {
                mdb_ctf_type_invalidate(p);
                return (-1); /* errno is set for us */
        }

        if ((mcip->mci_id = ctf_lookup_by_symbol(fp, sip->sym_id)) == CTF_ERR) {
                mdb_ctf_type_invalidate(p);
                return (set_errno(ctf_to_errno(ctf_errno(fp))));
        }

        mcip->mci_fp = fp;

        return (0);
}

int
mdb_ctf_lookup_by_addr(uintptr_t addr, mdb_ctf_id_t *p)
{
        GElf_Sym sym;
        mdb_syminfo_t si;
        char name[MDB_SYM_NAMLEN];
        const mdb_map_t *mp;
        mdb_tgt_t *t = mdb.m_target;
        const char *obj, *c;

        if (p == NULL)
                return (set_errno(EINVAL));

        if (mdb_tgt_lookup_by_addr(t, addr, MDB_TGT_SYM_EXACT, name,
            sizeof (name), NULL, NULL) == -1) {
                mdb_ctf_type_invalidate(p);
                return (-1); /* errno is set for us */
        }

        if ((c = strrsplit(name, '`')) != NULL) {
                obj = name;
        } else {
                if ((mp = mdb_tgt_addr_to_map(t, addr)) == NULL) {
                        mdb_ctf_type_invalidate(p);
                        return (-1); /* errno is set for us */
                }

                obj = mp->map_name;
                c = name;
        }

        if (mdb_tgt_lookup_by_name(t, obj, c, &sym, &si) == -1) {
                mdb_ctf_type_invalidate(p);
                return (-1); /* errno is set for us */
        }

        return (mdb_ctf_lookup_by_symbol(&sym, &si, p));
}

int
mdb_ctf_module_lookup(const char *name, mdb_ctf_id_t *p)
{
        ctf_file_t *fp;
        ctf_id_t id;
        mdb_module_t *mod;

        if ((mod = mdb_get_module()) == NULL)
                return (set_errno(EMDB_CTX));

        if ((fp = mod->mod_ctfp) == NULL)
                return (set_errno(EMDB_NOCTF));

        if ((id = ctf_lookup_by_name(fp, name)) == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(fp))));

        set_ctf_id(p, fp, id);

        return (0);
}

/*ARGSUSED*/
int
mdb_ctf_func_info(const GElf_Sym *symp, const mdb_syminfo_t *sip,
    mdb_ctf_funcinfo_t *mfp)
{
        ctf_file_t *fp = NULL;
        ctf_funcinfo_t f;
        mdb_tgt_t *t = mdb.m_target;
        char name[MDB_SYM_NAMLEN];
        const mdb_map_t *mp;
        mdb_syminfo_t si;
        int err;

        if (symp == NULL || mfp == NULL)
                return (set_errno(EINVAL));

        /*
         * In case the input symbol came from a merged or private symbol table,
         * re-lookup the address as a symbol, and then perform a fully scoped
         * lookup of that symbol name to get the mdb_syminfo_t for its CTF.
         */
        if ((fp = mdb_tgt_addr_to_ctf(t, symp->st_value)) == NULL ||
            (mp = mdb_tgt_addr_to_map(t, symp->st_value)) == NULL ||
            mdb_tgt_lookup_by_addr(t, symp->st_value, MDB_TGT_SYM_FUZZY,
            name, sizeof (name), NULL, NULL) != 0)
                return (-1); /* errno is set for us */

        if (strchr(name, '`') != NULL)
                err = mdb_tgt_lookup_by_scope(t, name, NULL, &si);
        else
                err = mdb_tgt_lookup_by_name(t, mp->map_name, name, NULL, &si);

        if (err != 0)
                return (-1); /* errno is set for us */

        if (ctf_func_info(fp, si.sym_id, &f) == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(fp))));

        set_ctf_id(&mfp->mtf_return, fp, f.ctc_return);
        mfp->mtf_argc = f.ctc_argc;
        mfp->mtf_flags = f.ctc_flags;
        mfp->mtf_symidx = si.sym_id;

        return (0);
}

int
mdb_ctf_func_args(const mdb_ctf_funcinfo_t *funcp, uint_t len,
    mdb_ctf_id_t *argv)
{
        ctf_file_t *fp;
        ctf_id_t cargv[32];
        int i;

        if (len > (sizeof (cargv) / sizeof (cargv[0])))
                return (set_errno(EINVAL));

        if (funcp == NULL || argv == NULL)
                return (set_errno(EINVAL));

        fp = mdb_ctf_type_file(funcp->mtf_return);

        if (ctf_func_args(fp, funcp->mtf_symidx, len, cargv) == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(fp))));

        for (i = MIN(len, funcp->mtf_argc) - 1; i >= 0; i--) {
                set_ctf_id(&argv[i], fp, cargv[i]);
        }

        return (0);
}

void
mdb_ctf_type_invalidate(mdb_ctf_id_t *idp)
{
        set_ctf_id(idp, NULL, CTF_ERR);
}

int
mdb_ctf_type_valid(mdb_ctf_id_t id)
{
        return (((mdb_ctf_impl_t *)&id)->mci_id != CTF_ERR);
}

int
mdb_ctf_type_cmp(mdb_ctf_id_t aid, mdb_ctf_id_t bid)
{
        mdb_ctf_impl_t *aidp = (mdb_ctf_impl_t *)&aid;
        mdb_ctf_impl_t *bidp = (mdb_ctf_impl_t *)&bid;

        return (ctf_type_cmp(aidp->mci_fp, aidp->mci_id,
            bidp->mci_fp, bidp->mci_id));
}

int
mdb_ctf_type_resolve(mdb_ctf_id_t mid, mdb_ctf_id_t *outp)
{
        ctf_id_t id;
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&mid;

        if ((id = ctf_type_resolve(idp->mci_fp, idp->mci_id)) == CTF_ERR) {
                if (outp)
                        mdb_ctf_type_invalidate(outp);
                return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
        }

        if (ctf_type_kind(idp->mci_fp, id) == CTF_K_FORWARD) {
                char name[MDB_SYM_NAMLEN];
                mdb_ctf_id_t lookup_id;

                if (ctf_type_name(idp->mci_fp, id, name, sizeof (name)) !=
                    NULL &&
                    mdb_ctf_lookup_by_name(name, &lookup_id) == 0 &&
                    outp != NULL) {
                        *outp = lookup_id;
                        return (0);
                }
        }

        if (outp != NULL)
                set_ctf_id(outp, idp->mci_fp, id);

        return (0);
}

char *
mdb_ctf_type_name(mdb_ctf_id_t id, char *buf, size_t len)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
        char *ret;

        if (!mdb_ctf_type_valid(id)) {
                (void) set_errno(EINVAL);
                return (NULL);
        }

        ret = ctf_type_name(idp->mci_fp, idp->mci_id, buf, len);
        if (ret == NULL)
                (void) set_errno(ctf_to_errno(ctf_errno(idp->mci_fp)));

        return (ret);
}

ssize_t
mdb_ctf_type_lname(mdb_ctf_id_t id, char *buf, size_t len)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
        char *ret;

        if (!mdb_ctf_type_valid(id))
                return (set_errno(EINVAL));

        return (ctf_type_lname(idp->mci_fp, idp->mci_id, buf, len));
}

ssize_t
mdb_ctf_type_size(mdb_ctf_id_t id)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
        ssize_t ret;

        /* resolve the type in case there's a forward declaration */
        if ((ret = mdb_ctf_type_resolve(id, &id)) != 0)
                return (ret);

        if ((ret = ctf_type_size(idp->mci_fp, idp->mci_id)) == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));

        return (ret);
}

int
mdb_ctf_type_kind(mdb_ctf_id_t id)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
        int ret;

        if ((ret = ctf_type_kind(idp->mci_fp, idp->mci_id)) == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));

        return (ret);
}

int
mdb_ctf_type_reference(mdb_ctf_id_t mid, mdb_ctf_id_t *outp)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&mid;
        ctf_id_t id;

        if ((id = ctf_type_reference(idp->mci_fp, idp->mci_id)) == CTF_ERR) {
                if (outp)
                        mdb_ctf_type_invalidate(outp);
                return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));
        }

        if (outp != NULL)
                set_ctf_id(outp, idp->mci_fp, id);

        return (0);
}


int
mdb_ctf_type_encoding(mdb_ctf_id_t id, ctf_encoding_t *ep)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;

        if (ctf_type_encoding(idp->mci_fp, idp->mci_id, ep) == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));

        return (0);
}

/*
 * callback proxy for mdb_ctf_type_visit
 */
static int
type_cb(const char *name, ctf_id_t type, ulong_t off, int depth, void *arg)
{
        type_visit_t *tvp = arg;
        mdb_ctf_id_t id;
        mdb_ctf_id_t base;
        mdb_ctf_impl_t *basep = (mdb_ctf_impl_t *)&base;

        int ret;

        if (depth < tvp->tv_min_depth)
                return (0);

        off += tvp->tv_base_offset;
        depth += tvp->tv_base_depth;

        set_ctf_id(&id, tvp->tv_fp, type);

        (void) mdb_ctf_type_resolve(id, &base);
        if ((ret = tvp->tv_cb(name, id, base, off, depth, tvp->tv_arg)) != 0)
                return (ret);

        /*
         * If the type resolves to a type in a different file, we must have
         * followed a forward declaration.  We need to recurse into the
         * new type.
         */
        if (basep->mci_fp != tvp->tv_fp && mdb_ctf_type_valid(base)) {
                type_visit_t tv;

                tv.tv_cb = tvp->tv_cb;
                tv.tv_arg = tvp->tv_arg;
                tv.tv_fp = basep->mci_fp;

                tv.tv_base_offset = off;
                tv.tv_base_depth = depth;
                tv.tv_min_depth = 1;    /* depth = 0 has already been done */

                ret = ctf_type_visit(basep->mci_fp, basep->mci_id,
                    type_cb, &tv);
        }
        return (ret);
}

int
mdb_ctf_type_visit(mdb_ctf_id_t id, mdb_ctf_visit_f *func, void *arg)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
        type_visit_t tv;
        int ret;

        tv.tv_cb = func;
        tv.tv_arg = arg;
        tv.tv_fp = idp->mci_fp;
        tv.tv_base_offset = 0;
        tv.tv_base_depth = 0;
        tv.tv_min_depth = 0;

        ret = ctf_type_visit(idp->mci_fp, idp->mci_id, type_cb, &tv);

        if (ret == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));

        return (ret);
}

int
mdb_ctf_array_info(mdb_ctf_id_t id, mdb_ctf_arinfo_t *arp)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
        ctf_arinfo_t car;

        if (ctf_array_info(idp->mci_fp, idp->mci_id, &car) == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));

        set_ctf_id(&arp->mta_contents, idp->mci_fp, car.ctr_contents);
        set_ctf_id(&arp->mta_index, idp->mci_fp, car.ctr_index);

        arp->mta_nelems = car.ctr_nelems;

        return (0);
}

const char *
mdb_ctf_enum_name(mdb_ctf_id_t id, int value)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
        const char *ret;

        /* resolve the type in case there's a forward declaration */
        if (mdb_ctf_type_resolve(id, &id) != 0)
                return (NULL);

        if ((ret = ctf_enum_name(idp->mci_fp, idp->mci_id, value)) == NULL)
                (void) set_errno(ctf_to_errno(ctf_errno(idp->mci_fp)));

        return (ret);
}

/*
 * callback proxy for mdb_ctf_member_iter
 */
static int
member_iter_cb(const char *name, ctf_id_t type, ulong_t off, void *data)
{
        int ret, kind;
        member_iter_t *mip = data;
        mdb_ctf_id_t id;

        set_ctf_id(&id, mip->mi_fp, type);

        ret = mip->mi_cb(name, id, mip->mi_off + off, mip->mi_arg);
        if (ret != 0) {
                return (ret);
        }

        /*
         * If we have an anonymous struct or union and we've been asked to
         * process it, recurse through it. We need to modify the offset that
         * we're at as we progress through this so that way the caller sees this
         * at the correct memory location.
         */
        kind = ctf_type_kind(mip->mi_fp, type);
        if ((kind == CTF_K_STRUCT || kind == CTF_K_UNION) && *name == '\0' &&
            (mip->mi_flags & MDB_CTF_F_ITER_ANON) != 0) {
                mip->mi_off += off;
                ret = ctf_member_iter(mip->mi_fp, type, member_iter_cb, mip);
                mip->mi_off -= off;
        }

        return (ret);
}

int
mdb_ctf_member_iter(mdb_ctf_id_t id, mdb_ctf_member_f *cb, void *data,
    uint32_t flags)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
        member_iter_t mi;
        int ret;

        if ((flags & ~MDB_CTF_F_ITER_ANON) != 0) {
                return (set_errno(EINVAL));
        }

        /* resolve the type in case there's a forward declaration */
        if ((ret = mdb_ctf_type_resolve(id, &id)) != 0)
                return (ret);

        mi.mi_cb = cb;
        mi.mi_arg = data;
        mi.mi_fp = idp->mci_fp;
        mi.mi_flags = flags;
        mi.mi_off = 0;

        ret = ctf_member_iter(idp->mci_fp, idp->mci_id, member_iter_cb, &mi);

        if (ret == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(idp->mci_fp))));

        return (ret);
}

int
mdb_ctf_enum_iter(mdb_ctf_id_t id, mdb_ctf_enum_f *cb, void *data)
{
        mdb_ctf_impl_t *idp = (mdb_ctf_impl_t *)&id;
        int ret;

        /* resolve the type in case there's a forward declaration */
        if ((ret = mdb_ctf_type_resolve(id, &id)) != 0)
                return (ret);

        return (ctf_enum_iter(idp->mci_fp, idp->mci_id, cb, data));
}

/*
 * callback proxy for mdb_ctf_type_iter
 */
/* ARGSUSED */
static int
type_iter_cb(ctf_id_t type, boolean_t root, void *data)
{
        type_iter_t *tip = data;
        mdb_ctf_id_t id;

        set_ctf_id(&id, tip->ti_fp, type);

        return (tip->ti_cb(id, tip->ti_arg));
}

int
mdb_ctf_type_iter(const char *object, mdb_ctf_type_f *cb, void *data)
{
        ctf_file_t *fp;
        mdb_tgt_t *t = mdb.m_target;
        int ret;
        type_iter_t ti;

        if (object == MDB_CTF_SYNTHETIC_ITER)
                fp = mdb.m_synth;
        else
                fp = mdb_tgt_name_to_ctf(t, object);

        if (fp == NULL)
                return (-1);

        ti.ti_cb = cb;
        ti.ti_arg = data;
        ti.ti_fp = fp;

        if ((ret = ctf_type_iter(fp, B_FALSE, type_iter_cb, &ti)) == CTF_ERR)
                return (set_errno(ctf_to_errno(ctf_errno(fp))));

        return (ret);
}

/* utility functions */

ctf_id_t
mdb_ctf_type_id(mdb_ctf_id_t id)
{
        return (((mdb_ctf_impl_t *)&id)->mci_id);
}

ctf_file_t *
mdb_ctf_type_file(mdb_ctf_id_t id)
{
        return (((mdb_ctf_impl_t *)&id)->mci_fp);
}

static int
member_info_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data)
{
        mbr_info_t *mbrp = data;

        if (strcmp(name, mbrp->mbr_member) == 0) {
                if (mbrp->mbr_offp != NULL)
                        *(mbrp->mbr_offp) = off;
                if (mbrp->mbr_typep != NULL)
                        *(mbrp->mbr_typep) = id;

                return (1);
        }

        return (0);
}

int
mdb_ctf_member_info(mdb_ctf_id_t id, const char *member, ulong_t *offp,
    mdb_ctf_id_t *typep)
{
        mbr_info_t mbr;
        int rc;

        mbr.mbr_member = member;
        mbr.mbr_offp = offp;
        mbr.mbr_typep = typep;

        rc = mdb_ctf_member_iter(id, member_info_cb, &mbr, MDB_CTF_F_ITER_ANON);

        /* couldn't get member list */
        if (rc == -1)
                return (-1); /* errno is set for us */

        /* not a member */
        if (rc == 0)
                return (set_errno(EMDB_CTFNOMEMB));

        return (0);
}

/*
 * Returns offset in _bits_ in *retp.
 */
int
mdb_ctf_offsetof(mdb_ctf_id_t id, const char *member, ulong_t *retp)
{
        return (mdb_ctf_member_info(id, member, retp, NULL));
}

/*
 * Returns offset in _bytes_, or -1 on failure.
 */
int
mdb_ctf_offsetof_by_name(const char *type, const char *member)
{
        mdb_ctf_id_t id;
        ulong_t off;

        if (mdb_ctf_lookup_by_name(type, &id) == -1) {
                mdb_warn("couldn't find type %s", type);
                return (-1);
        }

        if (mdb_ctf_offsetof(id, member, &off) == -1) {
                mdb_warn("couldn't find member %s of type %s", member, type);
                return (-1);
        }
        if (off % 8 != 0) {
                mdb_warn("member %s of type %s is an unsupported bitfield\n",
                    member, type);
                return (-1);
        }
        off /= 8;

        return (off);
}

ssize_t
mdb_ctf_sizeof_by_name(const char *type)
{
        mdb_ctf_id_t id;
        ssize_t size;

        if (mdb_ctf_lookup_by_name(type, &id) == -1) {
                mdb_warn("couldn't find type %s", type);
                return (-1);
        }

        if ((size = mdb_ctf_type_size(id)) == -1) {
                mdb_warn("couldn't determine type size of %s", type);
                return (-1);
        }

        return (size);
}

/*ARGSUSED*/
static int
num_members_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data)
{
        int *count = data;
        *count = *count + 1;
        return (0);
}

int
mdb_ctf_num_members(mdb_ctf_id_t id)
{
        int count = 0;

        if (mdb_ctf_member_iter(id, num_members_cb, &count, 0) != 0)
                return (-1); /* errno is set for us */

        return (count);
}

typedef struct mbr_contains {
        char **mbc_bufp;
        size_t *mbc_lenp;
        ulong_t *mbc_offp;
        mdb_ctf_id_t *mbc_idp;
        ssize_t mbc_total;
} mbr_contains_t;

static int
offset_to_name_cb(const char *name, mdb_ctf_id_t id, ulong_t off, void *data)
{
        mbr_contains_t *mbc = data;
        ulong_t size;
        ctf_encoding_t e;
        size_t n;

        if (*mbc->mbc_offp < off)
                return (0);

        if (mdb_ctf_type_encoding(id, &e) == -1)
                size = mdb_ctf_type_size(id) * NBBY;
        else
                size = e.cte_bits;

        if (off + size <= *mbc->mbc_offp)
                return (0);

        if (*name == '\0' && (mdb_ctf_type_kind(id) == CTF_K_STRUCT ||
            mdb_ctf_type_kind(id) == CTF_K_UNION)) {
                n = mdb_snprintf(*mbc->mbc_bufp, *mbc->mbc_lenp, "<anon>");
        } else {
                n = mdb_snprintf(*mbc->mbc_bufp, *mbc->mbc_lenp, "%s", name);
        }
        mbc->mbc_total += n;
        if (n > *mbc->mbc_lenp)
                n = *mbc->mbc_lenp;

        *mbc->mbc_lenp -= n;
        *mbc->mbc_bufp += n;

        *mbc->mbc_offp -= off;
        *mbc->mbc_idp = id;

        return (1);
}

ssize_t
mdb_ctf_offset_to_name(mdb_ctf_id_t id, ulong_t off, char *buf, size_t len,
    int dot, mdb_ctf_id_t *midp, ulong_t *moffp)
{
        size_t size;
        size_t n;
        mbr_contains_t mbc;

        if (!mdb_ctf_type_valid(id))
                return (set_errno(EINVAL));

        /*
         * Quick sanity check to make sure the given offset is within
         * this scope of this type.
         */
        if (mdb_ctf_type_size(id) * NBBY <= off)
                return (set_errno(EINVAL));

        mbc.mbc_bufp = &buf;
        mbc.mbc_lenp = &len;
        mbc.mbc_offp = &off;
        mbc.mbc_idp = &id;
        mbc.mbc_total = 0;

        *buf = '\0';

        for (;;) {
                /*
                 * Check for an exact match.
                 */
                if (off == 0)
                        break;

                (void) mdb_ctf_type_resolve(id, &id);

                /*
                 * Find the member that contains this offset.
                 */
                switch (mdb_ctf_type_kind(id)) {
                case CTF_K_ARRAY: {
                        mdb_ctf_arinfo_t ar;
                        uint_t index;

                        (void) mdb_ctf_array_info(id, &ar);
                        size = mdb_ctf_type_size(ar.mta_contents) * NBBY;
                        index = off / size;

                        id = ar.mta_contents;
                        off %= size;

                        n = mdb_snprintf(buf, len, "[%u]", index);
                        mbc.mbc_total += n;
                        if (n > len)
                                n = len;

                        buf += n;
                        len -= n;
                        break;
                }

                case CTF_K_STRUCT: {
                        int ret;

                        /*
                         * Find the member that contains this offset
                         * and continue.
                         */

                        if (dot) {
                                mbc.mbc_total++;
                                if (len != 0) {
                                        *buf++ = '.';
                                        *buf = '\0';
                                        len--;
                                }
                        }

                        ret = mdb_ctf_member_iter(id, offset_to_name_cb, &mbc,
                            0);
                        if (ret == -1)
                                return (-1); /* errno is set for us */

                        /*
                         * If we did not find a member containing this offset
                         * (due to holes in the structure), return EINVAL.
                         */
                        if (ret == 0)
                                return (set_errno(EINVAL));

                        break;
                }

                case CTF_K_UNION:
                        /*
                         * Treat unions like atomic entities since we can't
                         * do more than guess which member of the union
                         * might be the intended one.
                         */
                        goto done;

                case CTF_K_INTEGER:
                case CTF_K_FLOAT:
                case CTF_K_POINTER:
                case CTF_K_ENUM:
                        goto done;

                default:
                        return (set_errno(EINVAL));
                }

                dot = 1;
        }
done:
        if (midp != NULL)
                *midp = id;
        if (moffp != NULL)
                *moffp = off;

        return (mbc.mbc_total);
}

static void
mdb_ctf_warn(uint_t flags, const char *format, ...)
{
        va_list alist;

        if (flags & MDB_CTF_VREAD_QUIET)
                return;

        va_start(alist, format);
        vwarn(format, alist);
        va_end(alist);
}

/*
 * Check if two types are structurally the same rather than logically
 * the same. That is to say that two types are equal if they have the
 * same logical structure rather than having the same ids in CTF-land.
 */
static int type_equals(mdb_ctf_id_t, mdb_ctf_id_t);

static int
type_equals_cb(const char *name, mdb_ctf_id_t amem, ulong_t aoff, void *data)
{
        mdb_ctf_id_t b = *(mdb_ctf_id_t *)data;
        ulong_t boff;
        mdb_ctf_id_t bmem;

        /*
         * Look up the corresponding member in the other composite type.
         */
        if (mdb_ctf_member_info(b, name, &boff, &bmem) != 0)
                return (1);

        /*
         * We don't allow members to be shuffled around.
         */
        if (aoff != boff)
                return (1);

        return (type_equals(amem, bmem) ? 0 : 1);
}

static int
type_equals(mdb_ctf_id_t a, mdb_ctf_id_t b)
{
        size_t asz, bsz;
        int akind, bkind;
        mdb_ctf_arinfo_t aar, bar;

        /*
         * Resolve both types down to their fundamental types, and make
         * sure their sizes and kinds match.
         */
        if (mdb_ctf_type_resolve(a, &a) != 0 ||
            mdb_ctf_type_resolve(b, &b) != 0 ||
            (asz = mdb_ctf_type_size(a)) == -1UL ||
            (bsz = mdb_ctf_type_size(b)) == -1UL ||
            (akind = mdb_ctf_type_kind(a)) == -1 ||
            (bkind = mdb_ctf_type_kind(b)) == -1 ||
            asz != bsz || akind != bkind) {
                return (0);
        }

        switch (akind) {
        case CTF_K_INTEGER:
        case CTF_K_FLOAT:
        case CTF_K_POINTER:
                /*
                 * For pointers we could be a little stricter and require
                 * both pointers to reference types which look vaguely
                 * similar (for example, we could insist that the two types
                 * have the same name). However, all we really care about
                 * here is that the structure of the two types are the same,
                 * and, in that regard, one pointer is as good as another.
                 */
                return (1);

        case CTF_K_UNION:
        case CTF_K_STRUCT:
                /*
                 * The test for the number of members is only strictly
                 * necessary for unions since we'll find other problems with
                 * structs. However, the extra check will do no harm.
                 */
                return (mdb_ctf_num_members(a) == mdb_ctf_num_members(b) &&
                    mdb_ctf_member_iter(a, type_equals_cb, &b, 0) == 0);

        case CTF_K_ARRAY:
                return (mdb_ctf_array_info(a, &aar) == 0 &&
                    mdb_ctf_array_info(b, &bar) == 0 &&
                    aar.mta_nelems == bar.mta_nelems &&
                    type_equals(aar.mta_index, bar.mta_index) &&
                    type_equals(aar.mta_contents, bar.mta_contents));
        }

        return (0);
}


typedef struct member {
        char            *m_modbuf;
        char            *m_tgtbuf;
        const char      *m_tgtname;
        mdb_ctf_id_t    m_tgtid;
        uint_t          m_flags;
} member_t;

static int vread_helper(mdb_ctf_id_t, char *, mdb_ctf_id_t, char *,
    const char *, uint_t);

static int
member_cb(const char *name, mdb_ctf_id_t modmid, ulong_t modoff, void *data)
{
        member_t *mp = data;
        char *modbuf = mp->m_modbuf;
        mdb_ctf_id_t tgtmid;
        char *tgtbuf = mp->m_tgtbuf;
        ulong_t tgtoff;
        char tgtname[128];

        (void) mdb_snprintf(tgtname, sizeof (tgtname),
            "member %s of type %s", name, mp->m_tgtname);

        if (mdb_ctf_member_info(mp->m_tgtid, name, &tgtoff, &tgtmid) != 0) {
                if (mp->m_flags & MDB_CTF_VREAD_IGNORE_ABSENT)
                        return (0);
                mdb_ctf_warn(mp->m_flags, "could not find %s\n", tgtname);
                return (set_errno(EMDB_CTFNOMEMB));
        }

        return (vread_helper(modmid, modbuf + modoff / NBBY,
            tgtmid, tgtbuf + tgtoff / NBBY, tgtname, mp->m_flags));
}

typedef struct enum_value {
        int             *ev_modbuf;
        const char      *ev_name;
} enum_value_t;

static int
enum_cb(const char *name, int value, void *data)
{
        enum_value_t *ev = data;

        if (strcmp(name, ev->ev_name) == 0) {
                *ev->ev_modbuf = value;
                return (1);
        }
        return (0);
}

static int
vread_helper(mdb_ctf_id_t modid, char *modbuf,
    mdb_ctf_id_t tgtid, char *tgtbuf, const char *tgtname, uint_t flags)
{
        size_t modsz, tgtsz;
        int modkind, tgtkind, mod_members;
        member_t mbr;
        enum_value_t ev;
        int ret;
        mdb_ctf_arinfo_t tar, mar;
        int i;
        char typename[128];
        char mdbtypename[128];
        ctf_encoding_t tgt_encoding, mod_encoding;
        boolean_t signed_int = B_FALSE;

        if (mdb_ctf_type_name(tgtid, typename, sizeof (typename)) == NULL) {
                (void) mdb_snprintf(typename, sizeof (typename),
                    "#%ul", mdb_ctf_type_id(tgtid));
        }
        if (mdb_ctf_type_name(modid,
            mdbtypename, sizeof (mdbtypename)) == NULL) {
                (void) mdb_snprintf(mdbtypename, sizeof (mdbtypename),
                    "#%ul", mdb_ctf_type_id(modid));
        }

        if (tgtname == NULL)
                tgtname = "";

        /*
         * Resolve the types to their canonical form.
         */
        (void) mdb_ctf_type_resolve(modid, &modid);
        (void) mdb_ctf_type_resolve(tgtid, &tgtid);

        if ((modkind = mdb_ctf_type_kind(modid)) == -1) {
                mdb_ctf_warn(flags,
                    "couldn't determine type kind of mdb module type %s\n",
                    mdbtypename);
                return (-1); /* errno is set for us */
        }
        if ((tgtkind = mdb_ctf_type_kind(tgtid)) == -1) {
                mdb_ctf_warn(flags,
                    "couldn't determine type kind of %s\n", typename);
                return (-1); /* errno is set for us */
        }

        if ((modsz = mdb_ctf_type_size(modid)) == -1UL) {
                mdb_ctf_warn(flags, "couldn't determine type size of "
                    "mdb module type %s\n", mdbtypename);
                return (-1); /* errno is set for us */
        }
        if ((tgtsz = mdb_ctf_type_size(tgtid)) == -1UL) {
                mdb_ctf_warn(flags, "couldn't determine size of %s (%s)\n",
                    typename, tgtname);
                return (-1); /* errno is set for us */
        }

        if (tgtkind == CTF_K_POINTER && modkind == CTF_K_INTEGER &&
            strcmp(mdbtypename, "uintptr_t") == 0) {
                /* allow them to convert a pointer to a uintptr_t */
                ASSERT(modsz == tgtsz);
        } else if (tgtkind != modkind) {
                mdb_ctf_warn(flags, "unexpected kind for type %s (%s)\n",
                    typename, tgtname);
                return (set_errno(EMDB_INCOMPAT));
        }

        switch (tgtkind) {
        case CTF_K_INTEGER:
        case CTF_K_FLOAT:
                /*
                 * Must determine if the target and module types have the same
                 * encoding before we can copy them.
                 */
                if (mdb_ctf_type_encoding(tgtid, &tgt_encoding) != 0) {
                        mdb_ctf_warn(flags,
                            "couldn't determine encoding of type %s (%s)\n",
                            typename, tgtname);
                        return (-1); /* errno is set for us */
                }
                if (mdb_ctf_type_encoding(modid, &mod_encoding) != 0) {
                        mdb_ctf_warn(flags, "couldn't determine encoding of "
                            "mdb module type %s\n", mdbtypename);
                        return (-1); /* errno is set for us */
                }

                if (modkind == CTF_K_INTEGER) {
                        if ((tgt_encoding.cte_format & CTF_INT_SIGNED) !=
                            (mod_encoding.cte_format & CTF_INT_SIGNED)) {
                                mdb_ctf_warn(flags,
                                    "signedness mismatch between type "
                                    "%s (%s) and mdb module type %s\n",
                                    typename, tgtname, mdbtypename);
                                return (set_errno(EMDB_INCOMPAT));
                        }
                        signed_int =
                            ((tgt_encoding.cte_format & CTF_INT_SIGNED) != 0);
                } else if (tgt_encoding.cte_format != mod_encoding.cte_format) {
                        mdb_ctf_warn(flags,
                            "encoding mismatch (%#x != %#x) between type "
                            "%s (%s) and mdb module type %s\n",
                            tgt_encoding.cte_format, mod_encoding.cte_format,
                            typename, tgtname, mdbtypename);
                        return (set_errno(EMDB_INCOMPAT));
                }
                /* FALLTHROUGH */
        case CTF_K_POINTER:
                /*
                 * If the sizes don't match we need to be tricky to make
                 * sure that the caller gets the correct data.
                 */
                if (modsz < tgtsz) {
                        mdb_ctf_warn(flags, "size of type %s (%s) is too "
                            "large for mdb module type %s\n",
                            typename, tgtname, mdbtypename);
                        return (set_errno(EMDB_INCOMPAT));
                } else if (modsz > tgtsz) {
                        /* BEGIN CSTYLED */
                        /*
                         * Fill modbuf with 1's for sign extension if target
                         * buf is a signed integer and its value is negative.
                         *
                         *   S = sign bit (in most-significant byte)
                         *
                         *      BIG ENDIAN DATA
                         *    +--------+--------+--------+--------+
                         *    |S       |        |        |        |
                         *    +--------+--------+--------+--------+
                         *     0        1  ...            sz-1     sz
                         *
                         *      LITTLE ENDIAN DATA
                         *    +--------+--------+--------+--------+
                         *    |        |        |        |S       |
                         *    +--------+--------+--------+--------+
                         *     0        1  ...            sz-1     sz
                         */
                        /* END CSTYLED */
#ifdef _BIG_ENDIAN
                        if (signed_int && (tgtbuf[0] & 0x80) != 0)
#else
                        if (signed_int && (tgtbuf[tgtsz - 1] & 0x80) != 0)
#endif
                                (void) memset(modbuf, 0xFF, modsz);
                        else
                                bzero(modbuf, modsz);
#ifdef _BIG_ENDIAN
                        bcopy(tgtbuf, modbuf + modsz - tgtsz, tgtsz);
#else
                        bcopy(tgtbuf, modbuf, tgtsz);
#endif
                } else {
                        bcopy(tgtbuf, modbuf, modsz);
                }

                return (0);

        case CTF_K_ENUM:
                if (modsz != tgtsz || modsz != sizeof (int)) {
                        mdb_ctf_warn(flags, "unexpected size of type %s (%s)\n",
                            typename, tgtname);
                        return (set_errno(EMDB_INCOMPAT));
                }

                /*
                 * Default to the same value as in the target.
                 */
                bcopy(tgtbuf, modbuf, sizeof (int));

                /* LINTED */
                i = *(int *)tgtbuf;

                /* LINTED */
                ev.ev_modbuf = (int *)modbuf;
                ev.ev_name = mdb_ctf_enum_name(tgtid, i);
                if (ev.ev_name == NULL) {
                        mdb_ctf_warn(flags,
                            "unexpected value %u of enum type %s (%s)\n",
                            i, typename, tgtname);
                        return (set_errno(EMDB_INCOMPAT));
                }

                ret = mdb_ctf_enum_iter(modid, enum_cb, &ev);
                if (ret == 0) {
                        /* value not found */
                        mdb_ctf_warn(flags,
                            "unexpected value %s (%u) of enum type %s (%s)\n",
                            ev.ev_name, i, typename, tgtname);
                        return (set_errno(EMDB_INCOMPAT));
                } else if (ret == 1) {
                        /* value found */
                        return (0);
                } else if (ret == -1) {
                        mdb_ctf_warn(flags, "could not iterate enum %s (%s)\n",
                            typename, tgtname);
                }
                return (ret);

        case CTF_K_STRUCT:
                mbr.m_modbuf = modbuf;
                mbr.m_tgtbuf = tgtbuf;
                mbr.m_tgtid = tgtid;
                mbr.m_flags = flags;
                mbr.m_tgtname = typename;

                return (mdb_ctf_member_iter(modid, member_cb, &mbr, 0));

        case CTF_K_UNION:
                mbr.m_modbuf = modbuf;
                mbr.m_tgtbuf = tgtbuf;
                mbr.m_tgtid = tgtid;
                mbr.m_flags = flags;
                mbr.m_tgtname = typename;

                /*
                 * Not all target union members need to be present in the
                 * mdb type. If there is only a single union member in the
                 * mdb type, its actual type does not need to match with
                 * its target's type. On the other hand, if more than one
                 * union members are specified in the mdb type, their types
                 * must match with the types of their relevant union members
                 * of the target union.
                 */
                mod_members = mdb_ctf_num_members(modid);
                if (mod_members == 1) {
                        return (mdb_ctf_member_iter(modid, member_cb, &mbr, 0));
                } else if (mod_members > 1) {
                        if (mdb_ctf_member_iter(modid, type_equals_cb,
                            &tgtid, 0)) {
                                mdb_ctf_warn(flags,
                                    "inexact match for union %s (%s)\n",
                                    typename, tgtname);
                                return (set_errno(EMDB_INCOMPAT));
                        }

                        /*
                         * From the check above we know that the members
                         * which are present in the mdb type are equal to
                         * the types in the target. Thus, the member_cb
                         * callback below will not move anything around and
                         * it is equivalent to:
                         *
                         * bcopy(tgtbuf, modbuf, MAX(module member's sizes))
                         */
                        return (mdb_ctf_member_iter(modid, member_cb, &mbr, 0));
                } else {
                        /*
                         * We either got 0 or -1. In any case that number
                         * should be returned right away. For the error
                         * case of -1, errno has been set for us.
                         */
                        return (mod_members);
                }

        case CTF_K_ARRAY:
                if (mdb_ctf_array_info(tgtid, &tar) != 0) {
                        mdb_ctf_warn(flags,
                            "couldn't get array info for %s (%s)\n",
                            typename, tgtname);
                        return (-1); /* errno is set for us */
                }
                if (mdb_ctf_array_info(modid, &mar) != 0) {
                        mdb_ctf_warn(flags,
                            "couldn't get array info for mdb module type %s\n",
                            mdbtypename);
                        return (-1); /* errno is set for us */
                }

                if (tar.mta_nelems != mar.mta_nelems) {
                        mdb_ctf_warn(flags,
                            "unexpected array size (%u) for type %s (%s)\n",
                            tar.mta_nelems, typename, tgtname);
                        return (set_errno(EMDB_INCOMPAT));
                }

                if ((modsz = mdb_ctf_type_size(mar.mta_contents)) == -1UL) {
                        mdb_ctf_warn(flags, "couldn't determine type size of "
                            "mdb module type %s\n", mdbtypename);
                        return (-1); /* errno is set for us */
                }
                if ((tgtsz = mdb_ctf_type_size(tar.mta_contents)) == -1UL) {
                        mdb_ctf_warn(flags,
                            "couldn't determine size of %s (%s)\n",
                            typename, tgtname);
                        return (-1); /* errno is set for us */
                }

                for (i = 0; i < tar.mta_nelems; i++) {
                        ret = vread_helper(mar.mta_contents, modbuf + i * modsz,
                            tar.mta_contents, tgtbuf + i * tgtsz,
                            tgtname, flags);

                        if (ret != 0)
                                return (ret);
                }

                return (0);
        }

        mdb_ctf_warn(flags, "unsupported kind %d for type %s (%s)\n",
            modkind, typename, tgtname);
        return (set_errno(EMDB_INCOMPAT));
}

/*
 * Like mdb_vread(), mdb_ctf_vread() is used to read from the target's
 * virtual address space.  However, mdb_ctf_vread() can be used to safely
 * read a complex type (e.g. a struct) from the target, even if MDB was compiled
 * against a different definition of that type (e.g. when debugging a crash
 * dump from an older release).
 *
 * Callers can achieve this by defining their own type which corresponds to the
 * type in the target, but contains only the members that the caller requires.
 * Using the CTF type information embedded in the target, mdb_ctf_vread will
 * find the required members in the target and fill in the caller's structure.
 * The members are located by name, and their types are verified to be
 * compatible.
 *
 * By convention, the caller will declare a type with the name "mdb_<type>",
 * where <type> is the name of the type in the target (e.g. mdb_zio_t).  This
 * type will contain the members that the caller is interested in.  For example:
 *
 * typedef struct mdb_zio {
 *         enum zio_type io_type;
 *         uintptr_t io_waiter;
 *         struct {
 *                 struct {
 *                         uintptr_t list_next;
 *                 } list_head;
 *         } io_parent_list;
 *         int io_error;
 * } mdb_zio_t;
 *
 * mdb_zio_t zio;
 * error = mdb_ctf_vread(&zio, "zio_t", "mdb_zio_t", zio_target_addr, 0);
 *
 * If a given MDB module has different dcmds or walkers that need to read
 * different members from the same struct, then different "mdb_" types
 * should be declared for each caller.  By convention, these types should
 * be named "mdb_<dcmd or walker>_<type>", e.g. mdb_findstack_kthread_t
 * for ::findstack.  If the MDB module is compiled from several source files,
 * one must be especially careful to not define different types with the
 * same name in different source files, because the compiler can not detect
 * this error.
 *
 * Enums will also be translated by name, so the mdb module will receive
 * the enum value it expects even if the target has renumbered the enum.
 * Warning: it will therefore only work with enums are only used to store
 * legitimate enum values (not several values or-ed together).
 *
 * Flags values:
 *
 * MDB_CTF_VREAD_QUIET: keep quiet about failures
 * MDB_CTF_VREAD_IGNORE_ABSENT: ignore any member that couldn't be found in the
 * target struct; be careful not to use an uninitialized result.
 */
int
mdb_ctf_vread(void *modbuf, const char *target_typename,
    const char *mdb_typename, uintptr_t addr, uint_t flags)
{
        ctf_file_t *mfp;
        ctf_id_t mid;
        void *tgtbuf;
        size_t size;
        mdb_ctf_id_t tgtid;
        mdb_ctf_id_t modid;
        mdb_module_t *mod;
        int ret;

        if ((mod = mdb_get_module()) == NULL) {
                mdb_ctf_warn(flags, "could not get MDB module");
                return (set_errno(EMDB_NOCTF));
        }

        if ((mfp = mod->mod_ctfp) == NULL) {
                mdb_ctf_warn(flags, "no ctf data found for mdb module %s\n",
                    mod->mod_name);
                return (set_errno(EMDB_NOCTF));
        }

        if ((mid = ctf_lookup_by_name(mfp, mdb_typename)) == CTF_ERR) {
                mdb_ctf_warn(flags, "couldn't find ctf data for "
                    "type %s in mdb module %s\n",
                    mdb_typename, mod->mod_name);
                return (set_errno(ctf_to_errno(ctf_errno(mfp))));
        }

        set_ctf_id(&modid, mfp, mid);

        if (mdb_ctf_lookup_by_name(target_typename, &tgtid) != 0) {
                mdb_ctf_warn(flags,
                    "couldn't find type %s in target's ctf data\n",
                    target_typename);
                return (set_errno(EMDB_NOCTF));
        }

        /*
         * Read the data out of the target's address space.
         */
        if ((size = mdb_ctf_type_size(tgtid)) == -1UL) {
                mdb_ctf_warn(flags, "couldn't determine size of type %s\n",
                    target_typename);
                return (-1); /* errno is set for us */
        }

        tgtbuf = mdb_alloc(size, UM_SLEEP);

        if (mdb_vread(tgtbuf, size, addr) < 0) {
                mdb_ctf_warn(flags, "couldn't read %s from %p\n",
                    target_typename, addr);
                mdb_free(tgtbuf, size);
                return (-1); /* errno is set for us */
        }

        ret = vread_helper(modid, modbuf, tgtid, tgtbuf, NULL, flags);

        mdb_free(tgtbuf, size);

        return (ret);
}

/*
 * Note: mdb_ctf_readsym() doesn't take separate parameters for the name
 * of the target's type vs the mdb module's type.  Use with complicated
 * types (e.g. structs) may result in unnecessary failure if a member of
 * the struct has been changed in the target, but is not actually needed
 * by the mdb module.  Use mdb_lookup_by_name() + mdb_ctf_vread() to
 * avoid this problem.
 */
int
mdb_ctf_readsym(void *buf, const char *typename, const char *name, uint_t flags)
{
        GElf_Sym sym;

        if (mdb_lookup_by_obj(MDB_TGT_OBJ_EVERY, name, &sym) != 0) {
                mdb_ctf_warn(flags, "couldn't find symbol %s\n", name);
                return (-1); /* errno is set for us */
        }

        return (mdb_ctf_vread(buf, typename, typename, sym.st_value, flags));
}

ctf_file_t *
mdb_ctf_bufopen(const void *ctf_va, size_t ctf_size, const void *sym_va,
    Shdr *symhdr, const void *str_va, Shdr *strhdr, int *errp)
{
        ctf_sect_t ctdata, symtab, strtab;

        ctdata.cts_name = ".SUNW_ctf";
        ctdata.cts_type = SHT_PROGBITS;
        ctdata.cts_flags = 0;
        ctdata.cts_data = ctf_va;
        ctdata.cts_size = ctf_size;
        ctdata.cts_entsize = 1;
        ctdata.cts_offset = 0;

        symtab.cts_name = ".symtab";
        symtab.cts_type = symhdr->sh_type;
        symtab.cts_flags = symhdr->sh_flags;
        symtab.cts_data = sym_va;
        symtab.cts_size = symhdr->sh_size;
        symtab.cts_entsize = symhdr->sh_entsize;
        symtab.cts_offset = symhdr->sh_offset;

        strtab.cts_name = ".strtab";
        strtab.cts_type = strhdr->sh_type;
        strtab.cts_flags = strhdr->sh_flags;
        strtab.cts_data = str_va;
        strtab.cts_size = strhdr->sh_size;
        strtab.cts_entsize = strhdr->sh_entsize;
        strtab.cts_offset = strhdr->sh_offset;

        return (ctf_bufopen(&ctdata, &symtab, &strtab, errp));
}

int
mdb_ctf_synthetics_init(void)
{
        int err;

        if ((mdb.m_synth = ctf_create(&err)) == NULL)
                return (set_errno(ctf_to_errno(err)));

        return (0);
}

void
mdb_ctf_synthetics_fini(void)
{
        if (mdb.m_synth == NULL)
                return;

        ctf_close(mdb.m_synth);
        mdb.m_synth = NULL;
}

int
mdb_ctf_synthetics_create_base(int kind)
{
        const synth_intrinsic_t *synp;
        const synth_typedef_t *sytp;
        int err;
        ctf_id_t id;
        ctf_file_t *cp = mdb.m_synth;

        if (mdb.m_synth == NULL) {
                mdb_printf("synthetic types disabled: ctf create failed\n");
                return (1);
        }

        switch (kind) {
        case SYNTHETIC_ILP32:
                synp = synth_builtins32;
                sytp = synth_typedefs32;
                break;
        case SYNTHETIC_LP64:
                synp = synth_builtins64;
                sytp = synth_typedefs64;
                break;
        default:
                mdb_dprintf(MDB_DBG_CTF, "invalid type of intrinsic: %d\n",
                    kind);
                return (1);
        }

        err = 0;
        for (; synp->syn_name != NULL; synp++) {
                if (synp->syn_kind == CTF_K_INTEGER) {
                        err = ctf_add_integer(cp, CTF_ADD_ROOT, synp->syn_name,
                            &synp->syn_enc);
                } else {
                        err = ctf_add_float(cp, CTF_ADD_ROOT, synp->syn_name,
                            &synp->syn_enc);
                }

                if (err == CTF_ERR) {
                        mdb_dprintf(MDB_DBG_CTF, "couldn't add synthetic "
                            "type: %s\n", synp->syn_name);
                        goto discard;
                }
        }

        if (ctf_update(cp) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types\n");
                goto discard;
        }

        for (; sytp->syt_src != NULL; sytp++) {
                id = ctf_lookup_by_name(cp, sytp->syt_src);
                if (id == CTF_ERR) {
                        mdb_dprintf(MDB_DBG_CTF, "cailed to lookup %s: %s\n",
                            sytp->syt_src, ctf_errmsg(ctf_errno(cp)));
                        goto discard;
                }
                if (ctf_add_typedef(cp, CTF_ADD_ROOT, sytp->syt_targ, id) ==
                    CTF_ERR) {
                        mdb_dprintf(MDB_DBG_CTF, "couldn't add typedef %s "
                            "%s: %s\n", sytp->syt_targ, sytp->syt_src,
                            ctf_errmsg(ctf_errno(cp)));
                        goto discard;
                }
        }

        if (ctf_update(cp) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types\n");
                goto discard;
        }

        return (0);

discard:
        err = set_errno(ctf_to_errno(ctf_errno(cp)));
        (void) ctf_discard(cp);
        return (err);
}

int
mdb_ctf_synthetics_reset(void)
{
        mdb_ctf_synthetics_fini();
        return (mdb_ctf_synthetics_init());
}

int
mdb_ctf_add_typedef(const char *name, const mdb_ctf_id_t *p, mdb_ctf_id_t *new)
{
        ctf_id_t rid;
        mdb_ctf_id_t tid;
        mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;

        if (mdb.m_synth == NULL) {
                mdb_printf("synthetic types disabled: ctf create failed\n");
                return (1);
        }

        if (mdb_ctf_lookup_by_name(name, &tid) == 0) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add type %s: a type "
                    "with that name already exists\n", name);
                return (set_errno(EEXIST));
        }

        rid = ctf_add_type(mdb.m_synth, mcip->mci_fp, mcip->mci_id);
        if (rid == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add reference type: %s\n",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }
        rid = ctf_add_typedef(mdb.m_synth, CTF_ADD_ROOT, name, rid);
        if (rid == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add typedef: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (new != NULL)
                set_ctf_id(new, mdb.m_synth, rid);

        return (0);
}

int
mdb_ctf_add_struct(const char *name, mdb_ctf_id_t *rid)
{
        mdb_ctf_id_t tid;
        ctf_id_t id;

        if (mdb.m_synth == NULL) {
                mdb_printf("synthetic types disabled: ctf create failed\n");
                return (1);
        }

        if (name != NULL && mdb_ctf_lookup_by_name(name, &tid) == 0) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add type %s: a type "
                    "with that name already exists\n", name);
                return (set_errno(EEXIST));
        }

        if ((id = ctf_add_struct(mdb.m_synth, CTF_ADD_ROOT, name)) ==
            CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add struct: %s\n",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (rid != NULL)
                set_ctf_id(rid, mdb.m_synth, id);

        return (0);
}

int
mdb_ctf_add_union(const char *name, mdb_ctf_id_t *rid)
{
        mdb_ctf_id_t tid;
        ctf_id_t id;

        if (mdb.m_synth == NULL) {
                mdb_printf("synthetic types disabled: ctf create failed\n");
                return (1);
        }

        if (name != NULL && mdb_ctf_lookup_by_name(name, &tid) == 0) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add type %s: a type "
                    "with that name already exists\n", name);
                return (set_errno(EEXIST));
        }

        if ((id = ctf_add_union(mdb.m_synth, CTF_ADD_ROOT, name)) ==
            CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add union: %s\n",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (rid != NULL)
                set_ctf_id(rid, mdb.m_synth, id);

        return (0);
}

int
mdb_ctf_add_member(const mdb_ctf_id_t *p, const char *name,
    const mdb_ctf_id_t *mtype, mdb_ctf_id_t *rid)
{
        ctf_id_t id, mtid;
        mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;
        mdb_ctf_impl_t *mcim = (mdb_ctf_impl_t *)mtype;

        if (mdb.m_synth == NULL) {
                mdb_printf("synthetic types disabled: ctf create failed\n");
                return (DCMD_ERR);
        }

        if (mcip->mci_fp != mdb.m_synth) {
                mdb_dprintf(MDB_DBG_CTF, "requested to add member to a type "
                    "that wasn't created from a synthetic\n");
                return (set_errno(EINVAL));
        }

        mtid = ctf_add_type(mdb.m_synth, mcim->mci_fp, mcim->mci_id);
        if (mtid == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add member type: %s\n",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        id = ctf_add_member(mdb.m_synth, mcip->mci_id, name, mtid, ULONG_MAX);
        if (id == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add member %s: %s\n",
                    name, ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (rid != NULL)
                set_ctf_id(rid, mdb.m_synth, id);

        return (0);
}

int
mdb_ctf_add_array(const mdb_ctf_arinfo_t *marp, mdb_ctf_id_t *rid)
{
        mdb_ctf_impl_t *mcip;
        ctf_arinfo_t car;
        ctf_id_t id;

        if (mdb.m_synth == NULL) {
                mdb_printf("synthetic types disabled: ctf create failed\n");
                return (1);
        }

        car.ctr_nelems = marp->mta_nelems;

        mcip = (mdb_ctf_impl_t *)&marp->mta_contents;
        id = ctf_add_type(mdb.m_synth, mcip->mci_fp, mcip->mci_id);
        if (id == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add member type: %s\n",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }
        car.ctr_contents = id;

        mcip = (mdb_ctf_impl_t *)&marp->mta_index;
        id = ctf_add_type(mdb.m_synth, mcip->mci_fp, mcip->mci_id);
        if (id == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add member type: %s\n",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }
        car.ctr_index = id;

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        id = ctf_add_array(mdb.m_synth, CTF_ADD_ROOT, &car);
        if (id == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (rid != NULL)
                set_ctf_id(rid, mdb.m_synth, id);

        return (0);
}

int
mdb_ctf_add_pointer(const mdb_ctf_id_t *p, mdb_ctf_id_t *rid)
{
        ctf_id_t id;
        mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)p;

        if (mdb.m_synth == NULL) {
                mdb_printf("synthetic types disabled: ctf create failed\n");
                return (1);
        }

        id = ctf_add_type(mdb.m_synth, mcip->mci_fp, mcip->mci_id);
        if (id == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add pointer type: %s\n",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }


        id = ctf_add_pointer(mdb.m_synth, CTF_ADD_ROOT, NULL, id);
        if (id == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add pointer: %s\n",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }


        if (rid != NULL)
                set_ctf_id(rid, mdb.m_synth, id);

        return (0);
}

int
mdb_ctf_type_delete(const mdb_ctf_id_t *id)
{
        int ret;

        mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)id;

        if (mcip->mci_fp != mdb.m_synth) {
                mdb_warn("bad ctf_file_t, expected synth container\n");
                return (1);
        }

        ret = ctf_delete_type(mcip->mci_fp, mcip->mci_id);
        if (ret != 0) {
                mdb_dprintf(MDB_DBG_CTF, "failed to delete synthetic type: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        if (ctf_update(mdb.m_synth) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types: %s",
                    ctf_errmsg(ctf_errno(mdb.m_synth)));
                return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
        }

        return (0);
}

/* ARGSUSED */
static int
mdb_ctf_synthetics_file_cb(mdb_ctf_id_t id, void *arg)
{
        ctf_file_t *targ = arg;
        mdb_ctf_impl_t *mcip = (mdb_ctf_impl_t *)&id;

        if (ctf_add_type(targ, mcip->mci_fp, mcip->mci_id) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to add type %d: %s\n",
                    mcip->mci_id, ctf_errmsg(ctf_errno(mcip->mci_fp)));
                return (set_errno(ctf_to_errno(ctf_errno(mcip->mci_fp))));
        }

        return (0);
}

int
mdb_ctf_synthetics_from_file(const char *file)
{
        ctf_file_t *fp, *syn = mdb.m_synth;
        int ret;
        type_iter_t ti;

        if (syn == NULL) {
                mdb_warn("synthetic types disabled: ctf create failed\n");
                return (1);
        }

        if ((fp = mdb_ctf_open(file, &ret)) == NULL) {
                mdb_warn("failed to parse ctf data in %s: %s\n", file,
                    ctf_errmsg(ret));
                return (1);
        }

        ret = DCMD_OK;
        ti.ti_fp = fp;
        ti.ti_arg = syn;
        ti.ti_cb = mdb_ctf_synthetics_file_cb;
        if (ctf_type_iter(fp, B_FALSE, type_iter_cb, &ti) == CTF_ERR) {
                ret = set_errno(ctf_to_errno(ctf_errno(fp)));
                mdb_warn("failed to add types");
                goto cleanup;
        }

        if (ctf_update(syn) == CTF_ERR) {
                mdb_dprintf(MDB_DBG_CTF, "failed to update synthetic types\n");
                ret = set_errno(ctf_to_errno(ctf_errno(fp)));
        }

cleanup:
        ctf_close(fp);
        if (ret != 0)
                (void) ctf_discard(syn);
        return (ret);
}

int
mdb_ctf_synthetics_to_file(const char *file)
{
        int err;
        ctf_file_t *fp = mdb.m_synth;

        if (fp == NULL) {
                mdb_warn("synthetic types are disabled, not writing "
                    "anything\n");
                return (DCMD_ERR);
        }

        err = mdb_ctf_write(file, fp);
        if (err != 0) {
                if (err == CTF_ERR)
                        (void) set_errno(ctf_to_errno(ctf_errno(fp)));
                else
                        (void) set_errno(err);
                err = DCMD_ERR;
        } else {
                err = DCMD_OK;
        }

        return (err);
}

static int
cmd_typelist_type(mdb_ctf_id_t id, void *arg)
{
        char buf[1024];

        if (mdb_ctf_type_name(id, buf, sizeof (buf)) != NULL) {
                mdb_printf("%s\n", buf);
        }
        return (0);
}

static int
cmd_typelist_module(void *data, const mdb_map_t *mp, const char *name)
{
        (void) mdb_ctf_type_iter(name, cmd_typelist_type, data);
        return (0);
}

int
cmd_typelist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
        if ((flags & DCMD_ADDRSPEC) != 0) {
                return (DCMD_USAGE);
        }

        (void) mdb_tgt_object_iter(mdb.m_target, cmd_typelist_module, NULL);
        (void) mdb_ctf_type_iter(MDB_CTF_SYNTHETIC_ITER, cmd_typelist_type,
            NULL);

        return (DCMD_OK);
}