diff options
Diffstat (limited to 'src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf')
11 files changed, 4645 insertions, 0 deletions
diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/Makefile.kup b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/Makefile.kup diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_create.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_create.c new file mode 100644 index 00000000..4518f596 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_create.c @@ -0,0 +1,1370 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/sysmacros.h> +#include <sys/param.h> +#include <sys/mman.h> +#else /* VBOX */ +#endif /* VBOX */ +#include <ctf_impl.h> + +/* + * This static string is used as the template for initially populating a + * dynamic container's string table. We always store \0 in the first byte, + * and we use the generic string "PARENT" to mark this container's parent + * if one is associated with the container using ctf_import(). + */ +static const char _CTF_STRTAB_TEMPLATE[] = "\0PARENT"; + +/* + * To create an empty CTF container, we just declare a zeroed header and call + * ctf_bufopen() on it. If ctf_bufopen succeeds, we mark the new container r/w + * and initialize the dynamic members. We set dtstrlen to 1 to reserve the + * first byte of the string table for a \0 byte, and we start assigning type + * IDs at 1 because type ID 0 is used as a sentinel. + */ +ctf_file_t * +ctf_create(int *errp) +{ + static const ctf_header_t hdr = { { CTF_MAGIC, CTF_VERSION, 0 } }; + + const ulong_t hashlen = 128; + ctf_dtdef_t **hash = ctf_alloc(hashlen * sizeof (ctf_dtdef_t *)); + ctf_sect_t cts; + ctf_file_t *fp; + + if (hash == NULL) + return (ctf_set_open_errno(errp, EAGAIN)); + + cts.cts_name = _CTF_SECTION; + cts.cts_type = SHT_PROGBITS; + cts.cts_flags = 0; + cts.cts_data = &hdr; + cts.cts_size = sizeof (hdr); + cts.cts_entsize = 1; + cts.cts_offset = 0; + + if ((fp = ctf_bufopen(&cts, NULL, NULL, errp)) == NULL) { + ctf_free(hash, hashlen * sizeof (ctf_dtdef_t *)); + return (NULL); + } + + fp->ctf_flags |= LCTF_RDWR; + fp->ctf_dthashlen = hashlen; + bzero(hash, hashlen * sizeof (ctf_dtdef_t *)); + fp->ctf_dthash = hash; + fp->ctf_dtstrlen = sizeof (_CTF_STRTAB_TEMPLATE); + fp->ctf_dtnextid = 1; + fp->ctf_dtoldid = 0; + + return (fp); +} + +static uchar_t * +ctf_copy_smembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t) +{ + ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members); + ctf_member_t ctm; + + for (; dmd != NULL; dmd = ctf_list_next(dmd)) { + if (dmd->dmd_name) { + ctm.ctm_name = soff; + soff += VBDTCAST(uint_t)strlen(dmd->dmd_name) + 1; + } else + ctm.ctm_name = 0; + + ctm.ctm_type = (ushort_t)dmd->dmd_type; + ctm.ctm_offset = (ushort_t)dmd->dmd_offset; + + bcopy(&ctm, t, sizeof (ctm)); + t += sizeof (ctm); + } + + return (t); +} + +static uchar_t * +ctf_copy_lmembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t) +{ + ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members); + ctf_lmember_t ctlm; + + for (; dmd != NULL; dmd = ctf_list_next(dmd)) { + if (dmd->dmd_name) { + ctlm.ctlm_name = soff; + soff += VBDTCAST(uint_t)strlen(dmd->dmd_name) + 1; + } else + ctlm.ctlm_name = 0; + + ctlm.ctlm_type = (ushort_t)dmd->dmd_type; + ctlm.ctlm_pad = 0; + ctlm.ctlm_offsethi = CTF_OFFSET_TO_LMEMHI(dmd->dmd_offset); + ctlm.ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO(dmd->dmd_offset); + + bcopy(&ctlm, t, sizeof (ctlm)); + t += sizeof (ctlm); + } + + return (t); +} + +static uchar_t * +ctf_copy_emembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t) +{ + ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members); + ctf_enum_t cte; + + for (; dmd != NULL; dmd = ctf_list_next(dmd)) { + cte.cte_name = soff; + cte.cte_value = dmd->dmd_value; + soff += VBDTCAST(uint_t)strlen(dmd->dmd_name) + 1; + bcopy(&cte, t, sizeof (cte)); + t += sizeof (cte); + } + + return (t); +} + +static uchar_t * +ctf_copy_membnames(ctf_dtdef_t *dtd, uchar_t *s) +{ + ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members); + size_t len; + + for (; dmd != NULL; dmd = ctf_list_next(dmd)) { + if (dmd->dmd_name == NULL) + continue; /* skip anonymous members */ + len = strlen(dmd->dmd_name) + 1; + bcopy(dmd->dmd_name, s, len); + s += len; + } + + return (s); +} + +/* + * If the specified CTF container is writable and has been modified, reload + * this container with the updated type definitions. In order to make this + * code and the rest of libctf as simple as possible, we perform updates by + * taking the dynamic type definitions and creating an in-memory CTF file + * containing the definitions, and then call ctf_bufopen() on it. This not + * only leverages ctf_bufopen(), but also avoids having to bifurcate the rest + * of the library code with different lookup paths for static and dynamic + * type definitions. We are therefore optimizing greatly for lookup over + * update, which we assume will be an uncommon operation. We perform one + * extra trick here for the benefit of callers and to keep our code simple: + * ctf_bufopen() will return a new ctf_file_t, but we want to keep the fp + * constant for the caller, so after ctf_bufopen() returns, we use bcopy to + * swap the interior of the old and new ctf_file_t's, and then free the old. + */ +int +ctf_update(ctf_file_t *fp) +{ + ctf_file_t ofp, *nfp; + ctf_header_t hdr; + ctf_dtdef_t *dtd; + ctf_sect_t cts; + + uchar_t *s, *s0, *t; + size_t size; + void *buf; + int err; + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + if (!(fp->ctf_flags & LCTF_DIRTY)) + return (0); /* no update required */ + + /* + * Fill in an initial CTF header. We will leave the label, object, + * and function sections empty and only output a header, type section, + * and string table. The type section begins at a 4-byte aligned + * boundary past the CTF header itself (at relative offset zero). + */ + bzero(&hdr, sizeof (hdr)); + hdr.cth_magic = CTF_MAGIC; + hdr.cth_version = CTF_VERSION; + + if (fp->ctf_flags & LCTF_CHILD) + hdr.cth_parname = 1; /* i.e. _CTF_STRTAB_TEMPLATE[1] */ + + /* + * Iterate through the dynamic type definition list and compute the + * size of the CTF type section we will need to generate. + */ + for (size = 0, dtd = ctf_list_next(&fp->ctf_dtdefs); + dtd != NULL; dtd = ctf_list_next(dtd)) { + + uint_t kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info); + uint_t vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info); + + if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT) + size += sizeof (ctf_stype_t); + else + size += sizeof (ctf_type_t); + + switch (kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + size += sizeof (uint_t); + break; + case CTF_K_ARRAY: + size += sizeof (ctf_array_t); + break; + case CTF_K_FUNCTION: + size += sizeof (ushort_t) * (vlen + (vlen & 1)); + break; + case CTF_K_STRUCT: + case CTF_K_UNION: + if (dtd->dtd_data.ctt_size < CTF_LSTRUCT_THRESH) + size += sizeof (ctf_member_t) * vlen; + else + size += sizeof (ctf_lmember_t) * vlen; + break; + case CTF_K_ENUM: + size += sizeof (ctf_enum_t) * vlen; + break; + } + } + + /* + * Fill in the string table offset and size, compute the size of the + * entire CTF buffer we need, and then allocate a new buffer and + * bcopy the finished header to the start of the buffer. + */ + hdr.cth_stroff = hdr.cth_typeoff + VBDTCAST(uint_t)size; + hdr.cth_strlen = VBDTCAST(uint_t)fp->ctf_dtstrlen; + size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen; + + if ((buf = ctf_data_alloc(size)) == MAP_FAILED) + return (ctf_set_errno(fp, EAGAIN)); + + bcopy(&hdr, buf, sizeof (ctf_header_t)); + t = (uchar_t *)buf + sizeof (ctf_header_t); + s = s0 = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_stroff; + + bcopy(_CTF_STRTAB_TEMPLATE, s, sizeof (_CTF_STRTAB_TEMPLATE)); + s += sizeof (_CTF_STRTAB_TEMPLATE); + + /* + * We now take a final lap through the dynamic type definition list and + * copy the appropriate type records and strings to the output buffer. + */ + for (dtd = ctf_list_next(&fp->ctf_dtdefs); + dtd != NULL; dtd = ctf_list_next(dtd)) { + + uint_t kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info); + uint_t vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info); + + ctf_array_t cta; + uint_t encoding; + size_t len; + + if (dtd->dtd_name != NULL) { + dtd->dtd_data.ctt_name = (uint_t)(s - s0); + len = strlen(dtd->dtd_name) + 1; + bcopy(dtd->dtd_name, s, len); + s += len; + } else + dtd->dtd_data.ctt_name = 0; + + if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT) + len = sizeof (ctf_stype_t); + else + len = sizeof (ctf_type_t); + + bcopy(&dtd->dtd_data, t, len); + t += len; + + switch (kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + if (kind == CTF_K_INTEGER) { + encoding = CTF_INT_DATA( + dtd->dtd_u.dtu_enc.cte_format, + dtd->dtd_u.dtu_enc.cte_offset, + dtd->dtd_u.dtu_enc.cte_bits); + } else { + encoding = CTF_FP_DATA( + dtd->dtd_u.dtu_enc.cte_format, + dtd->dtd_u.dtu_enc.cte_offset, + dtd->dtd_u.dtu_enc.cte_bits); + } + bcopy(&encoding, t, sizeof (encoding)); + t += sizeof (encoding); + break; + + case CTF_K_ARRAY: + cta.cta_contents = (ushort_t) + dtd->dtd_u.dtu_arr.ctr_contents; + cta.cta_index = (ushort_t) + dtd->dtd_u.dtu_arr.ctr_index; + cta.cta_nelems = dtd->dtd_u.dtu_arr.ctr_nelems; + bcopy(&cta, t, sizeof (cta)); + t += sizeof (cta); + break; + + case CTF_K_FUNCTION: { + ushort_t *argv = (ushort_t *)(uintptr_t)t; + uint_t argc; + + for (argc = 0; argc < vlen; argc++) + *argv++ = (ushort_t)dtd->dtd_u.dtu_argv[argc]; + + if (vlen & 1) + *argv++ = 0; /* pad to 4-byte boundary */ + + t = (uchar_t *)argv; + break; + } + + case CTF_K_STRUCT: + case CTF_K_UNION: + if (dtd->dtd_data.ctt_size < CTF_LSTRUCT_THRESH) + t = ctf_copy_smembers(dtd, (uint_t)(s - s0), t); + else + t = ctf_copy_lmembers(dtd, (uint_t)(s - s0), t); + s = ctf_copy_membnames(dtd, s); + break; + + case CTF_K_ENUM: + t = ctf_copy_emembers(dtd, (uint_t)(s - s0), t); + s = ctf_copy_membnames(dtd, s); + break; + } + } + + /* + * Finally, we are ready to ctf_bufopen() the new container. If this + * is successful, we then switch nfp and fp and free the old container. + */ + ctf_data_protect(buf, size); + cts.cts_name = _CTF_SECTION; + cts.cts_type = SHT_PROGBITS; + cts.cts_flags = 0; + cts.cts_data = buf; + cts.cts_size = size; + cts.cts_entsize = 1; + cts.cts_offset = 0; + + if ((nfp = ctf_bufopen(&cts, NULL, NULL, &err)) == NULL) { + ctf_data_free(buf, size); + return (ctf_set_errno(fp, err)); + } + + (void) ctf_setmodel(nfp, ctf_getmodel(fp)); + (void) ctf_import(nfp, fp->ctf_parent); + + nfp->ctf_refcnt = fp->ctf_refcnt; + nfp->ctf_flags |= fp->ctf_flags & ~LCTF_DIRTY; + nfp->ctf_data.cts_data = NULL; /* force ctf_data_free() on close */ + nfp->ctf_dthash = fp->ctf_dthash; + nfp->ctf_dthashlen = fp->ctf_dthashlen; + nfp->ctf_dtdefs = fp->ctf_dtdefs; + nfp->ctf_dtstrlen = fp->ctf_dtstrlen; + nfp->ctf_dtnextid = fp->ctf_dtnextid; + nfp->ctf_dtoldid = fp->ctf_dtnextid - 1; + nfp->ctf_specific = fp->ctf_specific; + + fp->ctf_dthash = NULL; + fp->ctf_dthashlen = 0; + bzero(&fp->ctf_dtdefs, sizeof (ctf_list_t)); + + bcopy(fp, &ofp, sizeof (ctf_file_t)); + bcopy(nfp, fp, sizeof (ctf_file_t)); + bcopy(&ofp, nfp, sizeof (ctf_file_t)); + + /* + * Initialize the ctf_lookup_by_name top-level dictionary. We keep an + * array of type name prefixes and the corresponding ctf_hash to use. + * NOTE: This code must be kept in sync with the code in ctf_bufopen(). + */ + fp->ctf_lookups[0].ctl_hash = &fp->ctf_structs; + fp->ctf_lookups[1].ctl_hash = &fp->ctf_unions; + fp->ctf_lookups[2].ctl_hash = &fp->ctf_enums; + fp->ctf_lookups[3].ctl_hash = &fp->ctf_names; + + nfp->ctf_refcnt = 1; /* force nfp to be freed */ + ctf_close(nfp); + + return (0); +} + +void +ctf_dtd_insert(ctf_file_t *fp, ctf_dtdef_t *dtd) +{ + ulong_t h = dtd->dtd_type & (fp->ctf_dthashlen - 1); + + dtd->dtd_hash = fp->ctf_dthash[h]; + fp->ctf_dthash[h] = dtd; + ctf_list_append(&fp->ctf_dtdefs, dtd); +} + +void +ctf_dtd_delete(ctf_file_t *fp, ctf_dtdef_t *dtd) +{ + ulong_t h = dtd->dtd_type & (fp->ctf_dthashlen - 1); + ctf_dtdef_t *p, **q = &fp->ctf_dthash[h]; + ctf_dmdef_t *dmd, *nmd; + size_t len; + + for (p = *q; p != NULL; p = p->dtd_hash) { + if (p != dtd) + q = &p->dtd_hash; + else + break; + } + + if (p != NULL) + *q = p->dtd_hash; + + switch (CTF_INFO_KIND(dtd->dtd_data.ctt_info)) { + case CTF_K_STRUCT: + case CTF_K_UNION: + case CTF_K_ENUM: + for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members); + dmd != NULL; dmd = nmd) { + if (dmd->dmd_name != NULL) { + len = strlen(dmd->dmd_name) + 1; + ctf_free(dmd->dmd_name, len); + fp->ctf_dtstrlen -= len; + } + nmd = ctf_list_next(dmd); + ctf_free(dmd, sizeof (ctf_dmdef_t)); + } + break; + case CTF_K_FUNCTION: + ctf_free(dtd->dtd_u.dtu_argv, sizeof (ctf_id_t) * + CTF_INFO_VLEN(dtd->dtd_data.ctt_info)); + break; + } + + if (dtd->dtd_name) { + len = strlen(dtd->dtd_name) + 1; + ctf_free(dtd->dtd_name, len); + fp->ctf_dtstrlen -= len; + } + + ctf_list_delete(&fp->ctf_dtdefs, dtd); + ctf_free(dtd, sizeof (ctf_dtdef_t)); +} + +ctf_dtdef_t * +ctf_dtd_lookup(ctf_file_t *fp, ctf_id_t type) +{ + ulong_t h = type & (fp->ctf_dthashlen - 1); + ctf_dtdef_t *dtd; + + if (fp->ctf_dthash == NULL) + return (NULL); + + for (dtd = fp->ctf_dthash[h]; dtd != NULL; dtd = dtd->dtd_hash) { + if (dtd->dtd_type == type) + break; + } + + return (dtd); +} + +/* + * Discard all of the dynamic type definitions that have been added to the + * container since the last call to ctf_update(). We locate such types by + * scanning the list and deleting elements that have type IDs greater than + * ctf_dtoldid, which is set by ctf_update(), above. + */ +int +ctf_discard(ctf_file_t *fp) +{ + ctf_dtdef_t *dtd, *ntd VBDTMSC(NULL); + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + if (!(fp->ctf_flags & LCTF_DIRTY)) + return (0); /* no update required */ + + for (dtd = ctf_list_next(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { + if (dtd->dtd_type <= (intptr_t /*vbox*/)fp->ctf_dtoldid) + continue; /* skip types that have been committed */ + + ntd = ctf_list_next(dtd); + ctf_dtd_delete(fp, dtd); + } + + fp->ctf_dtnextid = fp->ctf_dtoldid + 1; + fp->ctf_flags &= ~LCTF_DIRTY; + + return (0); +} + +static ctf_id_t +ctf_add_generic(ctf_file_t *fp, uint_t flag, const char *name, ctf_dtdef_t **rp) +{ + ctf_dtdef_t *dtd; + ctf_id_t type; + char *s = NULL; + + if (flag != CTF_ADD_NONROOT && flag != CTF_ADD_ROOT) + return (ctf_set_errno(fp, EINVAL)); + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + if (CTF_INDEX_TO_TYPE(fp->ctf_dtnextid, 1) > CTF_MAX_TYPE) + return (ctf_set_errno(fp, ECTF_FULL)); + + if ((dtd = ctf_alloc(sizeof (ctf_dtdef_t))) == NULL) + return (ctf_set_errno(fp, EAGAIN)); + + if (name != NULL && (s = ctf_strdup(name)) == NULL) { + ctf_free(dtd, sizeof (ctf_dtdef_t)); + return (ctf_set_errno(fp, EAGAIN)); + } + + type = fp->ctf_dtnextid++; + type = CTF_INDEX_TO_TYPE(type, (fp->ctf_flags & LCTF_CHILD)); + + bzero(dtd, sizeof (ctf_dtdef_t)); + dtd->dtd_name = s; + dtd->dtd_type = type; + + if (s != NULL) + fp->ctf_dtstrlen += strlen(s) + 1; + + ctf_dtd_insert(fp, dtd); + fp->ctf_flags |= LCTF_DIRTY; + + *rp = dtd; + return (type); +} + +/* + * When encoding integer sizes, we want to convert a byte count in the range + * 1-8 to the closest power of 2 (e.g. 3->4, 5->8, etc). The clp2() function + * is a clever implementation from "Hacker's Delight" by Henry Warren, Jr. + */ +static size_t +clp2(size_t x) +{ + x--; + + x |= (x >> 1); + x |= (x >> 2); + x |= (x >> 4); + x |= (x >> 8); + x |= (x >> 16); + + return (x + 1); +} + +static ctf_id_t +ctf_add_encoded(ctf_file_t *fp, uint_t flag, + const char *name, const ctf_encoding_t *ep, uint_t kind) +{ + ctf_dtdef_t *dtd; + ctf_id_t type; + + if (ep == NULL) + return (ctf_set_errno(fp, EINVAL)); + + if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, 0); + dtd->dtd_data.ctt_size = VBDTCAST(ushort_t)clp2(P2ROUNDUP(ep->cte_bits, NBBY) / NBBY); + dtd->dtd_u.dtu_enc = *ep; + + return (type); +} + +static ctf_id_t +ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind) +{ + ctf_dtdef_t *dtd; + ctf_id_t type; + + if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE) + return (ctf_set_errno(fp, EINVAL)); + + if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, 0); + dtd->dtd_data.ctt_type = (ushort_t)ref; + + return (type); +} + +ctf_id_t +ctf_add_integer(ctf_file_t *fp, uint_t flag, + const char *name, const ctf_encoding_t *ep) +{ + return (ctf_add_encoded(fp, flag, name, ep, CTF_K_INTEGER)); +} + +ctf_id_t +ctf_add_float(ctf_file_t *fp, uint_t flag, + const char *name, const ctf_encoding_t *ep) +{ + return (ctf_add_encoded(fp, flag, name, ep, CTF_K_FLOAT)); +} + +ctf_id_t +ctf_add_pointer(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +{ + return (ctf_add_reftype(fp, flag, ref, CTF_K_POINTER)); +} + +ctf_id_t +ctf_add_array(ctf_file_t *fp, uint_t flag, const ctf_arinfo_t *arp) +{ + ctf_dtdef_t *dtd; + ctf_id_t type; + + if (arp == NULL) + return (ctf_set_errno(fp, EINVAL)); + + if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_ARRAY, flag, 0); + dtd->dtd_data.ctt_size = 0; + dtd->dtd_u.dtu_arr = *arp; + + return (type); +} + +int +ctf_set_array(ctf_file_t *fp, ctf_id_t type, const ctf_arinfo_t *arp) +{ + ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, type); + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + if (dtd == NULL || CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_ARRAY) + return (ctf_set_errno(fp, ECTF_BADID)); + + fp->ctf_flags |= LCTF_DIRTY; + dtd->dtd_u.dtu_arr = *arp; + + return (0); +} + +ctf_id_t +ctf_add_function(ctf_file_t *fp, uint_t flag, + const ctf_funcinfo_t *ctc, const ctf_id_t *argv) +{ + ctf_dtdef_t *dtd; + ctf_id_t type; + uint_t vlen; + ctf_id_t *vdat = NULL; + + if (ctc == NULL || (ctc->ctc_flags & ~CTF_FUNC_VARARG) != 0 || + (ctc->ctc_argc != 0 && argv == NULL)) + return (ctf_set_errno(fp, EINVAL)); + + vlen = ctc->ctc_argc; + if (ctc->ctc_flags & CTF_FUNC_VARARG) + vlen++; /* add trailing zero to indicate varargs (see below) */ + + if (vlen > CTF_MAX_VLEN) + return (ctf_set_errno(fp, EOVERFLOW)); + + if (vlen != 0 && (vdat = ctf_alloc(sizeof (ctf_id_t) * vlen)) == NULL) + return (ctf_set_errno(fp, EAGAIN)); + + if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR) { + ctf_free(vdat, sizeof (ctf_id_t) * vlen); + return (CTF_ERR); /* errno is set for us */ + } + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_FUNCTION, flag, vlen); + dtd->dtd_data.ctt_type = (ushort_t)ctc->ctc_return; + + bcopy(argv, vdat, sizeof (ctf_id_t) * ctc->ctc_argc); + if (ctc->ctc_flags & CTF_FUNC_VARARG) + vdat[vlen - 1] = 0; /* add trailing zero to indicate varargs */ + dtd->dtd_u.dtu_argv = vdat; + + return (type); +} + +ctf_id_t +ctf_add_struct(ctf_file_t *fp, uint_t flag, const char *name) +{ + ctf_hash_t *hp = &fp->ctf_structs; + ctf_helem_t *hep = NULL; + ctf_dtdef_t *dtd; + ctf_id_t type; + + if (name != NULL) + hep = ctf_hash_lookup(hp, fp, name, strlen(name)); + + if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD) + dtd = ctf_dtd_lookup(fp, type = hep->h_type); + else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, flag, 0); + dtd->dtd_data.ctt_size = 0; + + return (type); +} + +ctf_id_t +ctf_add_union(ctf_file_t *fp, uint_t flag, const char *name) +{ + ctf_hash_t *hp = &fp->ctf_unions; + ctf_helem_t *hep = NULL; + ctf_dtdef_t *dtd; + ctf_id_t type; + + if (name != NULL) + hep = ctf_hash_lookup(hp, fp, name, strlen(name)); + + if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD) + dtd = ctf_dtd_lookup(fp, type = hep->h_type); + else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, flag, 0); + dtd->dtd_data.ctt_size = 0; + + return (type); +} + +ctf_id_t +ctf_add_enum(ctf_file_t *fp, uint_t flag, const char *name) +{ + ctf_hash_t *hp = &fp->ctf_enums; + ctf_helem_t *hep = NULL; + ctf_dtdef_t *dtd; + ctf_id_t type; + + if (name != NULL) + hep = ctf_hash_lookup(hp, fp, name, strlen(name)); + + if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD) + dtd = ctf_dtd_lookup(fp, type = hep->h_type); + else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, flag, 0); + dtd->dtd_data.ctt_size = VBDTCAST(ushort_t)fp->ctf_dmodel->ctd_int; + + return (type); +} + +ctf_id_t +ctf_add_forward(ctf_file_t *fp, uint_t flag, const char *name, uint_t kind) +{ + ctf_hash_t *hp; + ctf_helem_t *hep; + ctf_dtdef_t *dtd; + ctf_id_t type; + + switch (kind) { + case CTF_K_STRUCT: + hp = &fp->ctf_structs; + break; + case CTF_K_UNION: + hp = &fp->ctf_unions; + break; + case CTF_K_ENUM: + hp = &fp->ctf_enums; + break; + default: + return (ctf_set_errno(fp, ECTF_NOTSUE)); + } + + /* + * If the type is already defined or exists as a forward tag, just + * return the ctf_id_t of the existing definition. + */ + if (name != NULL && (hep = ctf_hash_lookup(hp, + fp, name, strlen(name))) != NULL) + return (hep->h_type); + + if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_FORWARD, flag, 0); + dtd->dtd_data.ctt_type = kind; + + return (type); +} + +ctf_id_t +ctf_add_typedef(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref) +{ + ctf_dtdef_t *dtd; + ctf_id_t type; + + if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE) + return (ctf_set_errno(fp, EINVAL)); + + if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_TYPEDEF, flag, 0); + dtd->dtd_data.ctt_type = (ushort_t)ref; + + return (type); +} + +ctf_id_t +ctf_add_volatile(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +{ + return (ctf_add_reftype(fp, flag, ref, CTF_K_VOLATILE)); +} + +ctf_id_t +ctf_add_const(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +{ + return (ctf_add_reftype(fp, flag, ref, CTF_K_CONST)); +} + +ctf_id_t +ctf_add_restrict(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +{ + return (ctf_add_reftype(fp, flag, ref, CTF_K_RESTRICT)); +} + +int +ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value) +{ + ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, enid); + ctf_dmdef_t *dmd; + + uint_t kind, vlen, root; + char *s; + + if (name == NULL) + return (ctf_set_errno(fp, EINVAL)); + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + if (dtd == NULL) + return (ctf_set_errno(fp, ECTF_BADID)); + + kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info); + root = CTF_INFO_ISROOT(dtd->dtd_data.ctt_info); + vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info); + + if (kind != CTF_K_ENUM) + return (ctf_set_errno(fp, ECTF_NOTENUM)); + + if (vlen == CTF_MAX_VLEN) + return (ctf_set_errno(fp, ECTF_DTFULL)); + + for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members); + dmd != NULL; dmd = ctf_list_next(dmd)) { + if (strcmp(dmd->dmd_name, name) == 0) + return (ctf_set_errno(fp, ECTF_DUPMEMBER)); + } + + if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL) + return (ctf_set_errno(fp, EAGAIN)); + + if ((s = ctf_strdup(name)) == NULL) { + ctf_free(dmd, sizeof (ctf_dmdef_t)); + return (ctf_set_errno(fp, EAGAIN)); + } + + dmd->dmd_name = s; + dmd->dmd_type = CTF_ERR; + dmd->dmd_offset = 0; + dmd->dmd_value = value; + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, root, vlen + 1); + ctf_list_append(&dtd->dtd_u.dtu_members, dmd); + + fp->ctf_dtstrlen += strlen(s) + 1; + fp->ctf_flags |= LCTF_DIRTY; + + return (0); +} + +int +ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type) +{ + ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, souid); + ctf_dmdef_t *dmd; + + ssize_t msize, malign, ssize; + uint_t kind, vlen, root; + char *s = NULL; + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + if (dtd == NULL) + return (ctf_set_errno(fp, ECTF_BADID)); + + kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info); + root = CTF_INFO_ISROOT(dtd->dtd_data.ctt_info); + vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info); + + if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) + return (ctf_set_errno(fp, ECTF_NOTSOU)); + + if (vlen == CTF_MAX_VLEN) + return (ctf_set_errno(fp, ECTF_DTFULL)); + + if (name != NULL) { + for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members); + dmd != NULL; dmd = ctf_list_next(dmd)) { + if (dmd->dmd_name != NULL && + strcmp(dmd->dmd_name, name) == 0) + return (ctf_set_errno(fp, ECTF_DUPMEMBER)); + } + } + + if ((msize = ctf_type_size(fp, type)) == CTF_ERR || + (malign = ctf_type_align(fp, type)) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL) + return (ctf_set_errno(fp, EAGAIN)); + + if (name != NULL && (s = ctf_strdup(name)) == NULL) { + ctf_free(dmd, sizeof (ctf_dmdef_t)); + return (ctf_set_errno(fp, EAGAIN)); + } + + dmd->dmd_name = s; + dmd->dmd_type = type; + dmd->dmd_value = -1; + + if (kind == CTF_K_STRUCT && vlen != 0) { + ctf_dmdef_t *lmd = ctf_list_prev(&dtd->dtd_u.dtu_members); + ctf_id_t ltype = ctf_type_resolve(fp, lmd->dmd_type); + size_t off = lmd->dmd_offset; + + ctf_encoding_t linfo; + ssize_t lsize; + + if (ctf_type_encoding(fp, ltype, &linfo) != CTF_ERR) + off += linfo.cte_bits; + else if ((lsize = ctf_type_size(fp, ltype)) != CTF_ERR) + off += lsize * NBBY; + + /* + * Round up the offset of the end of the last member to the + * next byte boundary, convert 'off' to bytes, and then round + * it up again to the next multiple of the alignment required + * by the new member. Finally, convert back to bits and store + * the result in dmd_offset. Technically we could do more + * efficient packing if the new member is a bit-field, but + * we're the "compiler" and ANSI says we can do as we choose. + */ + off = roundup(off, NBBY) / NBBY; + off = roundup(off, MAX(malign, 1)); + dmd->dmd_offset = off * NBBY; + ssize = off + msize; + } else { + dmd->dmd_offset = 0; + ssize = ctf_get_ctt_size(fp, &dtd->dtd_data, NULL, NULL); + ssize = MAX(ssize, msize); + } + + if (ssize > CTF_MAX_SIZE) { + dtd->dtd_data.ctt_size = CTF_LSIZE_SENT; + dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(ssize); + dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(ssize); + } else + dtd->dtd_data.ctt_size = (ushort_t)ssize; + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, root, vlen + 1); + ctf_list_append(&dtd->dtd_u.dtu_members, dmd); + + if (s != NULL) + fp->ctf_dtstrlen += strlen(s) + 1; + + fp->ctf_flags |= LCTF_DIRTY; + return (0); +} + +static int +enumcmp(const char *name, int value, void *arg) +{ + ctf_bundle_t *ctb = arg; + int bvalue; + + return (ctf_enum_value(ctb->ctb_file, ctb->ctb_type, + name, &bvalue) == CTF_ERR || value != bvalue); +} + +static int +enumadd(const char *name, int value, void *arg) +{ + ctf_bundle_t *ctb = arg; + + return (ctf_add_enumerator(ctb->ctb_file, ctb->ctb_type, + name, value) == CTF_ERR); +} + +/*ARGSUSED*/ +static int +membcmp(const char *name, ctf_id_t type, ulong_t offset, void *arg) +{ + ctf_bundle_t *ctb = arg; + ctf_membinfo_t ctm; + RT_NOREF1(type); + + return (ctf_member_info(ctb->ctb_file, ctb->ctb_type, + name, &ctm) == CTF_ERR || ctm.ctm_offset != offset); +} + +static int +membadd(const char *name, ctf_id_t type, ulong_t offset, void *arg) +{ + ctf_bundle_t *ctb = arg; + ctf_dmdef_t *dmd; + char *s = NULL; + + if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL) + return (ctf_set_errno(ctb->ctb_file, EAGAIN)); + + if (name != NULL && (s = ctf_strdup(name)) == NULL) { + ctf_free(dmd, sizeof (ctf_dmdef_t)); + return (ctf_set_errno(ctb->ctb_file, EAGAIN)); + } + + /* + * For now, dmd_type is copied as the src_fp's type; it is reset to an + * equivalent dst_fp type by a final loop in ctf_add_type(), below. + */ + dmd->dmd_name = s; + dmd->dmd_type = type; + dmd->dmd_offset = offset; + dmd->dmd_value = -1; + + ctf_list_append(&ctb->ctb_dtd->dtd_u.dtu_members, dmd); + + if (s != NULL) + ctb->ctb_file->ctf_dtstrlen += strlen(s) + 1; + + ctb->ctb_file->ctf_flags |= LCTF_DIRTY; + return (0); +} + +/* + * The ctf_add_type routine is used to copy a type from a source CTF container + * to a dynamic destination container. This routine operates recursively by + * following the source type's links and embedded member types. If the + * destination container already contains a named type which has the same + * attributes, then we succeed and return this type but no changes occur. + */ +ctf_id_t +ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type) +{ + ctf_id_t dst_type = CTF_ERR; + uint_t dst_kind = CTF_K_UNKNOWN; + + const ctf_type_t *tp; + const char *name; + uint_t kind, flag, vlen; + + ctf_bundle_t src, dst; + ctf_encoding_t src_en, dst_en; + ctf_arinfo_t src_ar, dst_ar; + + ctf_dtdef_t *dtd; + ctf_funcinfo_t ctc; + ssize_t size; + + ctf_hash_t *hp; + ctf_helem_t *hep; + + if (!(dst_fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(dst_fp, ECTF_RDONLY)); + + if ((tp = ctf_lookup_by_id(&src_fp, src_type)) == NULL) + return (ctf_set_errno(dst_fp, ctf_errno(src_fp))); + + name = ctf_strptr(src_fp, tp->ctt_name); + kind = LCTF_INFO_KIND(src_fp, tp->ctt_info); + flag = LCTF_INFO_ROOT(src_fp, tp->ctt_info); + vlen = LCTF_INFO_VLEN(src_fp, tp->ctt_info); + + switch (kind) { + case CTF_K_STRUCT: + hp = &dst_fp->ctf_structs; + break; + case CTF_K_UNION: + hp = &dst_fp->ctf_unions; + break; + case CTF_K_ENUM: + hp = &dst_fp->ctf_enums; + break; + default: + hp = &dst_fp->ctf_names; + break; + } + + /* + * If the source type has a name and is a root type (visible at the + * top-level scope), lookup the name in the destination container and + * verify that it is of the same kind before we do anything else. + */ + if ((flag & CTF_ADD_ROOT) && name[0] != '\0' && + (hep = ctf_hash_lookup(hp, dst_fp, name, strlen(name))) != NULL) { + dst_type = (ctf_id_t)hep->h_type; + dst_kind = ctf_type_kind(dst_fp, dst_type); + } + + /* + * If an identically named dst_type exists, fail with ECTF_CONFLICT + * unless dst_type is a forward declaration and src_type is a struct, + * union, or enum (i.e. the definition of the previous forward decl). + */ + if (dst_type != CTF_ERR && dst_kind != kind && ( + dst_kind != CTF_K_FORWARD || (kind != CTF_K_ENUM && + kind != CTF_K_STRUCT && kind != CTF_K_UNION))) + return (ctf_set_errno(dst_fp, ECTF_CONFLICT)); + + /* + * If the non-empty name was not found in the appropriate hash, search + * the list of pending dynamic definitions that are not yet committed. + * If a matching name and kind are found, assume this is the type that + * we are looking for. This is necessary to permit ctf_add_type() to + * operate recursively on entities such as a struct that contains a + * pointer member that refers to the same struct type. + */ + if (dst_type == CTF_ERR && name[0] != '\0') { + for (dtd = ctf_list_prev(&dst_fp->ctf_dtdefs); dtd != NULL && + dtd->dtd_type > (intptr_t /*vbox*/)dst_fp->ctf_dtoldid; + dtd = ctf_list_prev(dtd)) { + if ((uint_t)CTF_INFO_KIND(dtd->dtd_data.ctt_info) == kind && + dtd->dtd_name != NULL && + strcmp(dtd->dtd_name, name) == 0) + return (dtd->dtd_type); + } + } + + src.ctb_file = src_fp; + src.ctb_type = src_type; + src.ctb_dtd = NULL; + + dst.ctb_file = dst_fp; + dst.ctb_type = dst_type; + dst.ctb_dtd = NULL; + + /* + * Now perform kind-specific processing. If dst_type is CTF_ERR, then + * we add a new type with the same properties as src_type to dst_fp. + * If dst_type is not CTF_ERR, then we verify that dst_type has the + * same attributes as src_type. We recurse for embedded references. + */ + switch (kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + if (ctf_type_encoding(src_fp, src_type, &src_en) != 0) + return (ctf_set_errno(dst_fp, ctf_errno(src_fp))); + + if (dst_type != CTF_ERR) { + if (ctf_type_encoding(dst_fp, dst_type, &dst_en) != 0) + return (CTF_ERR); /* errno is set for us */ + + if (bcmp(&src_en, &dst_en, sizeof (ctf_encoding_t))) + return (ctf_set_errno(dst_fp, ECTF_CONFLICT)); + + } else if (kind == CTF_K_INTEGER) { + dst_type = ctf_add_integer(dst_fp, flag, name, &src_en); + } else + dst_type = ctf_add_float(dst_fp, flag, name, &src_en); + break; + + case CTF_K_POINTER: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + src_type = ctf_type_reference(src_fp, src_type); + src_type = ctf_add_type(dst_fp, src_fp, src_type); + + if (src_type == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dst_type = ctf_add_reftype(dst_fp, flag, src_type, kind); + break; + + case CTF_K_ARRAY: + if (ctf_array_info(src_fp, src_type, &src_ar) == CTF_ERR) + return (ctf_set_errno(dst_fp, ctf_errno(src_fp))); + + src_ar.ctr_contents = + ctf_add_type(dst_fp, src_fp, src_ar.ctr_contents); + src_ar.ctr_index = + ctf_add_type(dst_fp, src_fp, src_ar.ctr_index); + src_ar.ctr_nelems = src_ar.ctr_nelems; + + if (src_ar.ctr_contents == CTF_ERR || + src_ar.ctr_index == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + if (dst_type != CTF_ERR) { + if (ctf_array_info(dst_fp, dst_type, &dst_ar) != 0) + return (CTF_ERR); /* errno is set for us */ + + if (bcmp(&src_ar, &dst_ar, sizeof (ctf_arinfo_t))) + return (ctf_set_errno(dst_fp, ECTF_CONFLICT)); + } else + dst_type = ctf_add_array(dst_fp, flag, &src_ar); + break; + + case CTF_K_FUNCTION: + ctc.ctc_return = ctf_add_type(dst_fp, src_fp, tp->ctt_type); + ctc.ctc_argc = 0; + ctc.ctc_flags = 0; + + if (ctc.ctc_return == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dst_type = ctf_add_function(dst_fp, flag, &ctc, NULL); + break; + + case CTF_K_STRUCT: + case CTF_K_UNION: { + ctf_dmdef_t *dmd; + int errs = 0; + + /* + * Technically to match a struct or union we need to check both + * ways (src members vs. dst, dst members vs. src) but we make + * this more optimal by only checking src vs. dst and comparing + * the total size of the structure (which we must do anyway) + * which covers the possibility of dst members not in src. + * This optimization can be defeated for unions, but is so + * pathological as to render it irrelevant for our purposes. + */ + if (dst_type != CTF_ERR && dst_kind != CTF_K_FORWARD) { + if (ctf_type_size(src_fp, src_type) != + ctf_type_size(dst_fp, dst_type)) + return (ctf_set_errno(dst_fp, ECTF_CONFLICT)); + + if (ctf_member_iter(src_fp, src_type, membcmp, &dst)) + return (ctf_set_errno(dst_fp, ECTF_CONFLICT)); + + break; + } + + /* + * Unlike the other cases, copying structs and unions is done + * manually so as to avoid repeated lookups in ctf_add_member + * and to ensure the exact same member offsets as in src_type. + */ + dst_type = ctf_add_generic(dst_fp, flag, name, &dtd); + if (dst_type == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + dst.ctb_type = dst_type; + dst.ctb_dtd = dtd; + + if (ctf_member_iter(src_fp, src_type, membadd, &dst) != 0) + errs++; /* increment errs and fail at bottom of case */ + + if ((size = ctf_type_size(src_fp, src_type)) > CTF_MAX_SIZE) { + dtd->dtd_data.ctt_size = CTF_LSIZE_SENT; + dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size); + dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size); + } else + dtd->dtd_data.ctt_size = (ushort_t)size; + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, vlen); + + /* + * Make a final pass through the members changing each dmd_type + * (a src_fp type) to an equivalent type in dst_fp. We pass + * through all members, leaving any that fail set to CTF_ERR. + */ + for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members); + dmd != NULL; dmd = ctf_list_next(dmd)) { + if ((dmd->dmd_type = ctf_add_type(dst_fp, src_fp, + dmd->dmd_type)) == CTF_ERR) + errs++; + } + + if (errs) + return (CTF_ERR); /* errno is set for us */ + break; + } + + case CTF_K_ENUM: + if (dst_type != CTF_ERR && dst_kind != CTF_K_FORWARD) { + if (ctf_enum_iter(src_fp, src_type, enumcmp, &dst) || + ctf_enum_iter(dst_fp, dst_type, enumcmp, &src)) + return (ctf_set_errno(dst_fp, ECTF_CONFLICT)); + } else { + dst_type = ctf_add_enum(dst_fp, flag, name); + if ((dst.ctb_type = dst_type) == CTF_ERR || + ctf_enum_iter(src_fp, src_type, enumadd, &dst)) + return (CTF_ERR); /* errno is set for us */ + } + break; + + case CTF_K_FORWARD: + if (dst_type == CTF_ERR) { + dst_type = ctf_add_forward(dst_fp, + flag, name, CTF_K_STRUCT); /* assume STRUCT */ + } + break; + + case CTF_K_TYPEDEF: + src_type = ctf_type_reference(src_fp, src_type); + src_type = ctf_add_type(dst_fp, src_fp, src_type); + + if (src_type == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + /* + * If dst_type is not CTF_ERR at this point, we should check if + * ctf_type_reference(dst_fp, dst_type) != src_type and if so + * fail with ECTF_CONFLICT. However, this causes problems with + * <sys/types.h> typedefs that vary based on things like if + * _ILP32x then pid_t is int otherwise long. We therefore omit + * this check and assume that if the identically named typedef + * already exists in dst_fp, it is correct or equivalent. + */ + if (dst_type == CTF_ERR) { + dst_type = ctf_add_typedef(dst_fp, flag, + name, src_type); + } + break; + + default: + return (ctf_set_errno(dst_fp, ECTF_CORRUPT)); + } + + return (dst_type); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_decl.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_decl.c new file mode 100644 index 00000000..f544d8f6 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_decl.c @@ -0,0 +1,190 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +/* + * CTF Declaration Stack + * + * In order to implement ctf_type_name(), we must convert a type graph back + * into a C type declaration. Unfortunately, a type graph represents a storage + * class ordering of the type whereas a type declaration must obey the C rules + * for operator precedence, and the two orderings are frequently in conflict. + * For example, consider these CTF type graphs and their C declarations: + * + * CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER : int (*)() + * CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER : int (*)[] + * + * In each case, parentheses are used to raise operator * to higher lexical + * precedence, so the string form of the C declaration cannot be constructed by + * walking the type graph links and forming the string from left to right. + * + * The functions in this file build a set of stacks from the type graph nodes + * corresponding to the C operator precedence levels in the appropriate order. + * The code in ctf_type_name() can then iterate over the levels and nodes in + * lexical precedence order and construct the final C declaration string. + */ + +#include <ctf_impl.h> + +void +ctf_decl_init(ctf_decl_t *cd, char *buf, size_t len) +{ + int i; + + bzero(cd, sizeof (ctf_decl_t)); + + for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) + cd->cd_order[i] = CTF_PREC_BASE - 1; + + cd->cd_qualp = CTF_PREC_BASE; + cd->cd_ordp = CTF_PREC_BASE; + + cd->cd_buf = buf; + cd->cd_ptr = buf; + cd->cd_end = buf + len; +} + +void +ctf_decl_fini(ctf_decl_t *cd) +{ + ctf_decl_node_t *cdp, *ndp; + int i; + + for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) { + for (cdp = ctf_list_next(&cd->cd_nodes[i]); + cdp != NULL; cdp = ndp) { + ndp = ctf_list_next(cdp); + ctf_free(cdp, sizeof (ctf_decl_node_t)); + } + } +} + +void +ctf_decl_push(ctf_decl_t *cd, ctf_file_t *fp, ctf_id_t type) +{ + ctf_decl_node_t *cdp; + ctf_decl_prec_t prec; + uint_t kind, n = 1; + int is_qual = 0; + + const ctf_type_t *tp; + ctf_arinfo_t ar; + + if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) { + cd->cd_err = fp->ctf_errno; + return; + } + + switch (kind = LCTF_INFO_KIND(fp, tp->ctt_info)) { + case CTF_K_ARRAY: + (void) ctf_array_info(fp, type, &ar); + ctf_decl_push(cd, fp, ar.ctr_contents); + n = ar.ctr_nelems; + prec = CTF_PREC_ARRAY; + break; + + case CTF_K_TYPEDEF: + if (ctf_strptr(fp, tp->ctt_name)[0] == '\0') { + ctf_decl_push(cd, fp, tp->ctt_type); + return; + } + prec = CTF_PREC_BASE; + break; + + case CTF_K_FUNCTION: + ctf_decl_push(cd, fp, tp->ctt_type); + prec = CTF_PREC_FUNCTION; + break; + + case CTF_K_POINTER: + ctf_decl_push(cd, fp, tp->ctt_type); + prec = CTF_PREC_POINTER; + break; + + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + ctf_decl_push(cd, fp, tp->ctt_type); + prec = cd->cd_qualp; + is_qual++; + break; + + default: + prec = CTF_PREC_BASE; + } + + if ((cdp = ctf_alloc(sizeof (ctf_decl_node_t))) == NULL) { + cd->cd_err = EAGAIN; + return; + } + + cdp->cd_type = type; + cdp->cd_kind = kind; + cdp->cd_n = n; + + if (ctf_list_next(&cd->cd_nodes[prec]) == NULL) + cd->cd_order[prec] = cd->cd_ordp++; + + /* + * Reset cd_qualp to the highest precedence level that we've seen so + * far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER). + */ + if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY) + cd->cd_qualp = prec; + + /* + * C array declarators are ordered inside out so prepend them. Also by + * convention qualifiers of base types precede the type specifier (e.g. + * const int vs. int const) even though the two forms are equivalent. + */ + if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE)) + ctf_list_prepend(&cd->cd_nodes[prec], cdp); + else + ctf_list_append(&cd->cd_nodes[prec], cdp); +} + +/*PRINTFLIKE2*/ +void +ctf_decl_sprintf(ctf_decl_t *cd, const char *format, ...) +{ + size_t len = (size_t)(cd->cd_end - cd->cd_ptr); + va_list ap; + size_t n; + + va_start(ap, format); +#ifndef VBOX + n = vsnprintf(cd->cd_ptr, len, format, ap); +#else + n = RTStrPrintfV(cd->cd_ptr, len, format, ap); +#endif + va_end(ap); + + cd->cd_ptr += MIN(n, len); + cd->cd_len += n; +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_error.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_error.c new file mode 100644 index 00000000..414cb075 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_error.c @@ -0,0 +1,99 @@ +/* + * 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 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#include <ctf_impl.h> + +static const char *const _ctf_errlist[] = { + "File is not in CTF or ELF format", /* ECTF_FMT */ + "File uses more recent ELF version than libctf", /* ECTF_ELFVERS */ + "File uses more recent CTF version than libctf", /* ECTF_CTFVERS */ + "File is a different endian-ness than libctf", /* ECTF_ENDIAN */ + "Symbol table uses invalid entry size", /* ECTF_SYMTAB */ + "Symbol table data buffer is not valid", /* ECTF_SYMBAD */ + "String table data buffer is not valid", /* ECTF_STRBAD */ + "File data structure corruption detected", /* ECTF_CORRUPT */ + "File does not contain CTF data", /* ECTF_NOCTFDATA */ + "Buffer does not contain CTF data", /* ECTF_NOCTFBUF */ + "Symbol table information is not available", /* ECTF_NOSYMTAB */ + "Type information is in parent and unavailable", /* ECTF_NOPARENT */ + "Cannot import types with different data model", /* ECTF_DMODEL */ + "Failed to mmap a needed data section", /* ECTF_MMAP */ + "Decompression package SUNWzlib not installed", /* ECTF_ZMISSING */ + "Failed to initialize decompression library", /* ECTF_ZINIT */ + "Failed to allocate decompression buffer", /* ECTF_ZALLOC */ + "Failed to decompress CTF data", /* ECTF_DECOMPRESS */ + "External string table is not available", /* ECTF_STRTAB */ + "String name offset is corrupt", /* ECTF_BADNAME */ + "Invalid type identifier", /* ECTF_BADID */ + "Type is not a struct or union", /* ECTF_NOTSOU */ + "Type is not an enum", /* ECTF_NOTENUM */ + "Type is not a struct, union, or enum", /* ECTF_NOTSUE */ + "Type is not an integer or float", /* ECTF_NOTINTFP */ + "Type is not an array", /* ECTF_NOTARRAY */ + "Type does not reference another type", /* ECTF_NOTREF */ + "Input buffer is too small for type name", /* ECTF_NAMELEN */ + "No type information available for that name", /* ECTF_NOTYPE */ + "Syntax error in type name", /* ECTF_SYNTAX */ + "Symbol table entry is not a function", /* ECTF_NOTFUNC */ + "No function information available for symbol", /* ECTF_NOFUNCDAT */ + "Symbol table entry is not a data object", /* ECTF_NOTDATA */ + "No type information available for symbol", /* ECTF_NOTYPEDAT */ + "No label information available for that name", /* ECTF_NOLABEL */ + "File does not contain any labels", /* ECTF_NOLABELDATA */ + "Feature not supported", /* ECTF_NOTSUP */ + "Invalid enum element name", /* ECTF_NOENUMNAM */ + "Invalid member name", /* ECTF_NOMEMBNAM */ + "CTF container is read-only", /* ECTF_RDONLY */ + "Limit on number of dynamic type members reached", /* ECTF_DTFULL */ + "Limit on number of dynamic types reached", /* ECTF_FULL */ + "Duplicate member name definition", /* ECTF_DUPMEMBER */ + "Conflicting type is already defined", /* ECTF_CONFLICT */ +}; + +static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]); + +const char * +ctf_errmsg(int error) +{ + const char *str; + + if (error >= ECTF_BASE && (error - ECTF_BASE) < _ctf_nerr) + str = _ctf_errlist[error - ECTF_BASE]; + else + str = ctf_strerror(error); + + return (str ? str : "Unknown error"); +} + +int +ctf_errno(ctf_file_t *fp) +{ + return (fp->ctf_errno); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_hash.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_hash.c new file mode 100644 index 00000000..e2f390c1 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_hash.c @@ -0,0 +1,180 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#include <ctf_impl.h> + +static const ushort_t _CTF_EMPTY[1] = { 0 }; + +int +ctf_hash_create(ctf_hash_t *hp, ulong_t nelems) +{ + if (nelems > USHRT_MAX) + return (EOVERFLOW); + + /* + * If the hash table is going to be empty, don't bother allocating any + * memory and make the only bucket point to a zero so lookups fail. + */ + if (nelems == 0) { + bzero(hp, sizeof (ctf_hash_t)); + hp->h_buckets = (ushort_t *)_CTF_EMPTY; + hp->h_nbuckets = 1; + return (0); + } + + hp->h_nbuckets = 211; /* use a prime number of hash buckets */ + hp->h_nelems = nelems + 1; /* we use index zero as a sentinel */ + hp->h_free = 1; /* first free element is index 1 */ + + hp->h_buckets = ctf_alloc(sizeof (ushort_t) * hp->h_nbuckets); + hp->h_chains = ctf_alloc(sizeof (ctf_helem_t) * hp->h_nelems); + + if (hp->h_buckets == NULL || hp->h_chains == NULL) { + ctf_hash_destroy(hp); + return (EAGAIN); + } + + bzero(hp->h_buckets, sizeof (ushort_t) * hp->h_nbuckets); + bzero(hp->h_chains, sizeof (ctf_helem_t) * hp->h_nelems); + + return (0); +} + +uint_t +ctf_hash_size(const ctf_hash_t *hp) +{ + return (hp->h_nelems ? hp->h_nelems - 1 : 0); +} + +static ulong_t +ctf_hash_compute(const char *key, size_t len) +{ + ulong_t g, h = 0; + const char *p, *q = key + len; + size_t n = 0; + + for (p = key; p < q; p++, n++) { + h = (h << 4) + *p; + + if ((g = (h & 0xf0000000)) != 0) { + h ^= (g >> 24); + h ^= g; + } + } + + return (h); +} + +int +ctf_hash_insert(ctf_hash_t *hp, ctf_file_t *fp, ushort_t type, uint_t name) +{ + ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID(name)]; + const char *str = ctsp->cts_strs + CTF_NAME_OFFSET(name); + ctf_helem_t *hep = &hp->h_chains[hp->h_free]; + ulong_t h; + + if (type == 0) + return (EINVAL); + + if (hp->h_free >= hp->h_nelems) + return (EOVERFLOW); + + if (ctsp->cts_strs == NULL) + return (ECTF_STRTAB); + + if (ctsp->cts_len <= CTF_NAME_OFFSET(name)) + return (ECTF_BADNAME); + + if (str[0] == '\0') + return (0); /* just ignore empty strings on behalf of caller */ + + hep->h_name = name; + hep->h_type = type; + h = ctf_hash_compute(str, strlen(str)) % hp->h_nbuckets; + hep->h_next = hp->h_buckets[h]; + hp->h_buckets[h] = hp->h_free++; + + return (0); +} + +/* + * Wrapper for ctf_hash_lookup/ctf_hash_insert: if the key is already in the + * hash, override the previous definition with this new official definition. + * If the key is not present, then call ctf_hash_insert() and hash it in. + */ +int +ctf_hash_define(ctf_hash_t *hp, ctf_file_t *fp, ushort_t type, uint_t name) +{ + const char *str = ctf_strptr(fp, name); + ctf_helem_t *hep = ctf_hash_lookup(hp, fp, str, strlen(str)); + + if (hep == NULL) + return (ctf_hash_insert(hp, fp, type, name)); + + hep->h_type = type; + return (0); +} + +ctf_helem_t * +ctf_hash_lookup(ctf_hash_t *hp, ctf_file_t *fp, const char *key, size_t len) +{ + ctf_helem_t *hep; + ctf_strs_t *ctsp; + const char *str; + ushort_t i; + + ulong_t h = ctf_hash_compute(key, len) % hp->h_nbuckets; + + for (i = hp->h_buckets[h]; i != 0; i = hep->h_next) { + hep = &hp->h_chains[i]; + ctsp = &fp->ctf_str[CTF_NAME_STID(hep->h_name)]; + str = ctsp->cts_strs + CTF_NAME_OFFSET(hep->h_name); + + if (strncmp(key, str, len) == 0 && str[len] == '\0') + return (hep); + } + + return (NULL); +} + +void +ctf_hash_destroy(ctf_hash_t *hp) +{ + if (hp->h_buckets != NULL && hp->h_nbuckets != 1) { + ctf_free(hp->h_buckets, sizeof (ushort_t) * hp->h_nbuckets); + hp->h_buckets = NULL; + } + + if (hp->h_chains != NULL) { + ctf_free(hp->h_chains, sizeof (ctf_helem_t) * hp->h_nelems); + hp->h_chains = NULL; + } +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_impl.h b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_impl.h new file mode 100644 index 00000000..b8dcc6db --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_impl.h @@ -0,0 +1,353 @@ +/* + * 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. + */ + +#ifndef _CTF_IMPL_H +#define _CTF_IMPL_H + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/types.h> +#include <sys/errno.h> +#include <sys/sysmacros.h> +#else /* VBOX */ +# include <iprt/formats/elf32.h> +# include <iprt/formats/elf64.h> +# include <errno.h> +# include "VBoxDTraceLibCWrappers.h" +#endif /* VBOX */ +#include <sys/ctf_api.h> + +#ifdef _KERNEL + +#ifndef VBOX +#include <sys/systm.h> +#include <sys/cmn_err.h> +#include <sys/varargs.h> +#endif + +#define isspace(c) \ + ((c) == ' ' || (c) == '\t' || (c) == '\n' || \ + (c) == '\r' || (c) == '\f' || (c) == '\v') + +#define MAP_FAILED ((void *)-1) + +#else /* _KERNEL */ + +#ifndef VBOX +#include <strings.h> +#include <stdlib.h> +#include <stdarg.h> +#include <stdio.h> +#include <limits.h> +#include <ctype.h> +#endif + +#endif /* _KERNEL */ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct ctf_helem { + uint_t h_name; /* reference to name in string table */ + ushort_t h_type; /* corresponding type ID number */ + ushort_t h_next; /* index of next element in hash chain */ +} ctf_helem_t; + +typedef struct ctf_hash { + ushort_t *h_buckets; /* hash bucket array (chain indices) */ + ctf_helem_t *h_chains; /* hash chains buffer */ + ushort_t h_nbuckets; /* number of elements in bucket array */ + ushort_t h_nelems; /* number of elements in hash table */ + uint_t h_free; /* index of next free hash element */ +} ctf_hash_t; + +typedef struct ctf_strs { + const char *cts_strs; /* base address of string table */ + size_t cts_len; /* size of string table in bytes */ +} ctf_strs_t; + +typedef struct ctf_dmodel { + const char *ctd_name; /* data model name */ + int ctd_code; /* data model code */ + size_t ctd_pointer; /* size of void * in bytes */ + size_t ctd_char; /* size of char in bytes */ + size_t ctd_short; /* size of short in bytes */ + size_t ctd_int; /* size of int in bytes */ + size_t ctd_long; /* size of long in bytes */ +} ctf_dmodel_t; + +typedef struct ctf_lookup { + const char *ctl_prefix; /* string prefix for this lookup */ + size_t ctl_len; /* length of prefix string in bytes */ + ctf_hash_t *ctl_hash; /* pointer to hash table for lookup */ +} ctf_lookup_t; + +typedef struct ctf_fileops { + ushort_t (*ctfo_get_kind)(ushort_t); + ushort_t (*ctfo_get_root)(ushort_t); + ushort_t (*ctfo_get_vlen)(ushort_t); +} ctf_fileops_t; + +typedef struct ctf_list { + struct ctf_list *l_prev; /* previous pointer or tail pointer */ + struct ctf_list *l_next; /* next pointer or head pointer */ +} ctf_list_t; + +typedef enum { + CTF_PREC_BASE, + CTF_PREC_POINTER, + CTF_PREC_ARRAY, + CTF_PREC_FUNCTION, + CTF_PREC_MAX +} ctf_decl_prec_t; + +typedef struct ctf_decl_node { + ctf_list_t cd_list; /* linked list pointers */ + ctf_id_t cd_type; /* type identifier */ + uint_t cd_kind; /* type kind */ + uint_t cd_n; /* type dimension if array */ +} ctf_decl_node_t; + +typedef struct ctf_decl { + ctf_list_t cd_nodes[CTF_PREC_MAX]; /* declaration node stacks */ + int cd_order[CTF_PREC_MAX]; /* storage order of decls */ + ctf_decl_prec_t cd_qualp; /* qualifier precision */ + ctf_decl_prec_t cd_ordp; /* ordered precision */ + char *cd_buf; /* buffer for output */ + char *cd_ptr; /* buffer location */ + char *cd_end; /* buffer limit */ + size_t cd_len; /* buffer space required */ + int cd_err; /* saved error value */ +} ctf_decl_t; + +typedef struct ctf_dmdef { + ctf_list_t dmd_list; /* list forward/back pointers */ + char *dmd_name; /* name of this member */ + ctf_id_t dmd_type; /* type of this member (for sou) */ + ulong_t dmd_offset; /* offset of this member in bits (for sou) */ + int dmd_value; /* value of this member (for enum) */ +} ctf_dmdef_t; + +typedef struct ctf_dtdef { + ctf_list_t dtd_list; /* list forward/back pointers */ + struct ctf_dtdef *dtd_hash; /* hash chain pointer for ctf_dthash */ + char *dtd_name; /* name associated with definition (if any) */ + ctf_id_t dtd_type; /* type identifier for this definition */ + ctf_type_t dtd_data; /* type node (see <sys/ctf.h>) */ + union { + ctf_list_t dtu_members; /* struct, union, or enum */ + ctf_arinfo_t dtu_arr; /* array */ + ctf_encoding_t dtu_enc; /* integer or float */ + ctf_id_t *dtu_argv; /* function */ + } dtd_u; +} ctf_dtdef_t; + +typedef struct ctf_bundle { + ctf_file_t *ctb_file; /* CTF container handle */ + ctf_id_t ctb_type; /* CTF type identifier */ + ctf_dtdef_t *ctb_dtd; /* CTF dynamic type definition (if any) */ +} ctf_bundle_t; + +/* + * The ctf_file is the structure used to represent a CTF container to library + * clients, who see it only as an opaque pointer. Modifications can therefore + * be made freely to this structure without regard to client versioning. The + * ctf_file_t typedef appears in <sys/ctf_api.h> and declares a forward tag. + * + * NOTE: ctf_update() requires that everything inside of ctf_file either be an + * immediate value, a pointer to dynamically allocated data *outside* of the + * ctf_file itself, or a pointer to statically allocated data. If you add a + * pointer to ctf_file that points to something within the ctf_file itself, + * you must make corresponding changes to ctf_update(). + */ +struct ctf_file { + const ctf_fileops_t *ctf_fileops; /* version-specific file operations */ + ctf_sect_t ctf_data; /* CTF data from object file */ + ctf_sect_t ctf_symtab; /* symbol table from object file */ + ctf_sect_t ctf_strtab; /* string table from object file */ + ctf_hash_t ctf_structs; /* hash table of struct types */ + ctf_hash_t ctf_unions; /* hash table of union types */ + ctf_hash_t ctf_enums; /* hash table of enum types */ + ctf_hash_t ctf_names; /* hash table of remaining type names */ + ctf_lookup_t ctf_lookups[5]; /* pointers to hashes for name lookup */ + ctf_strs_t ctf_str[2]; /* array of string table base and bounds */ + const uchar_t *ctf_base; /* base of CTF header + uncompressed buffer */ + const uchar_t *ctf_buf; /* uncompressed CTF data buffer */ + size_t ctf_size; /* size of CTF header + uncompressed data */ + uint_t *ctf_sxlate; /* translation table for symtab entries */ + ulong_t ctf_nsyms; /* number of entries in symtab xlate table */ + uint_t *ctf_txlate; /* translation table for type IDs */ + ushort_t *ctf_ptrtab; /* translation table for pointer-to lookups */ + ulong_t ctf_typemax; /* maximum valid type ID number */ + const ctf_dmodel_t *ctf_dmodel; /* data model pointer (see above) */ + struct ctf_file *ctf_parent; /* parent CTF container (if any) */ + const char *ctf_parlabel; /* label in parent container (if any) */ + const char *ctf_parname; /* basename of parent (if any) */ + uint_t ctf_refcnt; /* reference count (for parent links) */ + uint_t ctf_flags; /* libctf flags (see below) */ + int ctf_errno; /* error code for most recent error */ + int ctf_version; /* CTF data version */ + ctf_dtdef_t **ctf_dthash; /* hash of dynamic type definitions */ + ulong_t ctf_dthashlen; /* size of dynamic type hash bucket array */ + ctf_list_t ctf_dtdefs; /* list of dynamic type definitions */ + size_t ctf_dtstrlen; /* total length of dynamic type strings */ + ulong_t ctf_dtnextid; /* next dynamic type id to assign */ + ulong_t ctf_dtoldid; /* oldest id that has been committed */ + void *ctf_specific; /* data for ctf_get/setspecific */ +}; + +#define LCTF_INDEX_TO_TYPEPTR(fp, i) \ + ((ctf_type_t *)((uintptr_t)(fp)->ctf_buf + (fp)->ctf_txlate[(i)])) + +#define LCTF_INFO_KIND(fp, info) ((fp)->ctf_fileops->ctfo_get_kind(info)) +#define LCTF_INFO_ROOT(fp, info) ((fp)->ctf_fileops->ctfo_get_root(info)) +#define LCTF_INFO_VLEN(fp, info) ((fp)->ctf_fileops->ctfo_get_vlen(info)) + +#define LCTF_MMAP 0x0001 /* libctf should munmap buffers on close */ +#define LCTF_CHILD 0x0002 /* CTF container is a child */ +#define LCTF_RDWR 0x0004 /* CTF container is writable */ +#define LCTF_DIRTY 0x0008 /* CTF container has been modified */ + +#define ECTF_BASE 1000 /* base value for libctf errnos */ + +enum { + ECTF_FMT = ECTF_BASE, /* file is not in CTF or ELF format */ + ECTF_ELFVERS, /* ELF version is more recent than libctf */ + ECTF_CTFVERS, /* CTF version is more recent than libctf */ + ECTF_ENDIAN, /* data is different endian-ness than lib */ + ECTF_SYMTAB, /* symbol table uses invalid entry size */ + ECTF_SYMBAD, /* symbol table data buffer invalid */ + ECTF_STRBAD, /* string table data buffer invalid */ + ECTF_CORRUPT, /* file data corruption detected */ + ECTF_NOCTFDATA, /* ELF file does not contain CTF data */ + ECTF_NOCTFBUF, /* buffer does not contain CTF data */ + ECTF_NOSYMTAB, /* symbol table data is not available */ + ECTF_NOPARENT, /* parent CTF container is not available */ + ECTF_DMODEL, /* data model mismatch */ + ECTF_MMAP, /* failed to mmap a data section */ + ECTF_ZMISSING, /* decompression library not installed */ + ECTF_ZINIT, /* failed to initialize decompression library */ + ECTF_ZALLOC, /* failed to allocate decompression buffer */ + ECTF_DECOMPRESS, /* failed to decompress CTF data */ + ECTF_STRTAB, /* string table for this string is missing */ + ECTF_BADNAME, /* string offset is corrupt w.r.t. strtab */ + ECTF_BADID, /* invalid type ID number */ + ECTF_NOTSOU, /* type is not a struct or union */ + ECTF_NOTENUM, /* type is not an enum */ + ECTF_NOTSUE, /* type is not a struct, union, or enum */ + ECTF_NOTINTFP, /* type is not an integer or float */ + ECTF_NOTARRAY, /* type is not an array */ + ECTF_NOTREF, /* type does not reference another type */ + ECTF_NAMELEN, /* buffer is too small to hold type name */ + ECTF_NOTYPE, /* no type found corresponding to name */ + ECTF_SYNTAX, /* syntax error in type name */ + ECTF_NOTFUNC, /* symtab entry does not refer to a function */ + ECTF_NOFUNCDAT, /* no func info available for function */ + ECTF_NOTDATA, /* symtab entry does not refer to a data obj */ + ECTF_NOTYPEDAT, /* no type info available for object */ + ECTF_NOLABEL, /* no label found corresponding to name */ + ECTF_NOLABELDATA, /* file does not contain any labels */ + ECTF_NOTSUP, /* feature not supported */ + ECTF_NOENUMNAM, /* enum element name not found */ + ECTF_NOMEMBNAM, /* member name not found */ + ECTF_RDONLY, /* CTF container is read-only */ + ECTF_DTFULL, /* CTF type is full (no more members allowed) */ + ECTF_FULL, /* CTF container is full */ + ECTF_DUPMEMBER, /* duplicate member name definition */ + ECTF_CONFLICT /* conflicting type definition present */ +}; + +extern ssize_t ctf_get_ctt_size(const ctf_file_t *, const ctf_type_t *, + ssize_t *, ssize_t *); + +extern const ctf_type_t *ctf_lookup_by_id(ctf_file_t **, ctf_id_t); + +extern int ctf_hash_create(ctf_hash_t *, ulong_t); +extern int ctf_hash_insert(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t); +extern int ctf_hash_define(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t); +extern ctf_helem_t *ctf_hash_lookup(ctf_hash_t *, ctf_file_t *, + const char *, size_t); +extern uint_t ctf_hash_size(const ctf_hash_t *); +extern void ctf_hash_destroy(ctf_hash_t *); + +#define ctf_list_prev(elem) ((void *)(((ctf_list_t *)(elem))->l_prev)) +#define ctf_list_next(elem) ((void *)(((ctf_list_t *)(elem))->l_next)) + +extern void ctf_list_append(ctf_list_t *, void *); +extern void ctf_list_prepend(ctf_list_t *, void *); +extern void ctf_list_delete(ctf_list_t *, void *); + +extern void ctf_dtd_insert(ctf_file_t *, ctf_dtdef_t *); +extern void ctf_dtd_delete(ctf_file_t *, ctf_dtdef_t *); +extern ctf_dtdef_t *ctf_dtd_lookup(ctf_file_t *, ctf_id_t); + +extern void ctf_decl_init(ctf_decl_t *, char *, size_t); +extern void ctf_decl_fini(ctf_decl_t *); +extern void ctf_decl_push(ctf_decl_t *, ctf_file_t *, ctf_id_t); +extern void ctf_decl_sprintf(ctf_decl_t *, const char *, ...); + +extern const char *ctf_strraw(ctf_file_t *, uint_t); +extern const char *ctf_strptr(ctf_file_t *, uint_t); + +extern ctf_file_t *ctf_set_open_errno(int *, int); +extern long ctf_set_errno(ctf_file_t *, int); + +extern const void *ctf_sect_mmap(ctf_sect_t *, int); +extern void ctf_sect_munmap(const ctf_sect_t *); + +extern void *ctf_data_alloc(size_t); +extern void ctf_data_free(void *, size_t); +extern void ctf_data_protect(void *, size_t); + +extern void *ctf_alloc(size_t); +extern void ctf_free(void *, size_t); + +extern char *ctf_strdup(const char *); +extern const char *ctf_strerror(int); +extern void ctf_dprintf(const char *, ...); + +extern void *ctf_zopen(int *); + +extern const char _CTF_SECTION[]; /* name of CTF ELF section */ +extern const char _CTF_NULLSTR[]; /* empty string */ + +extern int _libctf_version; /* library client version */ +extern int _libctf_debug; /* debugging messages enabled */ + +#ifdef VBOX +# ifndef MAP_FAILED /* returned by ctf_data_alloc on failure. */ +# define MAP_FAILED ((void *)~(uintptr_t)0) +# endif +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _CTF_IMPL_H */ diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_labels.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_labels.c new file mode 100644 index 00000000..798353dc --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_labels.c @@ -0,0 +1,158 @@ +/* + * 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 2002-2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#include <ctf_impl.h> + +static int +extract_label_info(ctf_file_t *fp, const ctf_lblent_t **ctl, uint_t *num_labels) +{ + const ctf_header_t *h; + + /* + * Labels are only supported in V2 or later + */ + if (fp->ctf_version < CTF_VERSION_2) { + *ctl = NULL; /* Shup up, GCC! */ + *num_labels = 0; + return (ctf_set_errno(fp, ECTF_NOTSUP)); + } + + h = (const ctf_header_t *)fp->ctf_data.cts_data; + + /* LINTED - pointer alignment */ + *ctl = (const ctf_lblent_t *)(fp->ctf_buf + h->cth_lbloff); + *num_labels = (h->cth_objtoff - h->cth_lbloff) / sizeof (ctf_lblent_t); + + return (0); +} + +/* + * Returns the topmost label, or NULL if any errors are encountered + */ +const char * +ctf_label_topmost(ctf_file_t *fp) +{ + const ctf_lblent_t *ctlp; + const char *s; + uint_t num_labels; + + if (extract_label_info(fp, &ctlp, &num_labels) == CTF_ERR) + return (NULL); /* errno is set */ + + if (num_labels == 0) { + (void) ctf_set_errno(fp, ECTF_NOLABELDATA); + return (NULL); + } + + if ((s = ctf_strraw(fp, (ctlp + num_labels - 1)->ctl_label)) == NULL) + (void) ctf_set_errno(fp, ECTF_CORRUPT); + + return (s); +} + +/* + * Iterate over all labels. We pass the label string and the lblinfo_t struct + * to the specified callback function. + */ +int +ctf_label_iter(ctf_file_t *fp, ctf_label_f *func, void *arg) +{ + const ctf_lblent_t *ctlp; + uint_t i, num_labels; + ctf_lblinfo_t linfo; + const char *lname; + int rc; + + if (extract_label_info(fp, &ctlp, &num_labels) == CTF_ERR) + return (CTF_ERR); /* errno is set */ + + if (num_labels == 0) + return (ctf_set_errno(fp, ECTF_NOLABELDATA)); + + for (i = 0; i < num_labels; i++, ctlp++) { + if ((lname = ctf_strraw(fp, ctlp->ctl_label)) == NULL) { + ctf_dprintf("failed to decode label %u with " + "typeidx %u\n", ctlp->ctl_label, ctlp->ctl_typeidx); + return (ctf_set_errno(fp, ECTF_CORRUPT)); + } + + linfo.ctb_typeidx = ctlp->ctl_typeidx; + if ((rc = func(lname, &linfo, arg)) != 0) + return (rc); + } + + return (0); +} + +typedef struct linfo_cb_arg { + const char *lca_name; /* Label we want to retrieve info for */ + ctf_lblinfo_t *lca_info; /* Where to store the info about the label */ +} linfo_cb_arg_t; + +static int +label_info_cb(const char *lname, const ctf_lblinfo_t *linfo, void *arg) +{ + /* + * If lname matches the label we are looking for, copy the + * lblinfo_t struct for the caller. + */ + if (strcmp(lname, ((linfo_cb_arg_t *)arg)->lca_name) == 0) { + /* + * Allow caller not to allocate storage to test if label exists + */ + if (((linfo_cb_arg_t *)arg)->lca_info != NULL) + bcopy(linfo, ((linfo_cb_arg_t *)arg)->lca_info, + sizeof (ctf_lblinfo_t)); + return (1); /* Indicate we found a match */ + } + + return (0); +} + +/* + * Retrieve information about the label with name "lname" + */ +int +ctf_label_info(ctf_file_t *fp, const char *lname, ctf_lblinfo_t *linfo) +{ + linfo_cb_arg_t cb_arg; + int rc; + + cb_arg.lca_name = lname; + cb_arg.lca_info = linfo; + + if ((rc = ctf_label_iter(fp, label_info_cb, &cb_arg)) == CTF_ERR) + return (rc); + + if (rc != 1) + return (ctf_set_errno(fp, ECTF_NOLABEL)); + + return (0); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_lookup.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_lookup.c new file mode 100644 index 00000000..c49d6162 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_lookup.c @@ -0,0 +1,322 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/sysmacros.h> +#else /* VBOX */ +# include <ctype.h> +#endif /* VBOX */ +#include <ctf_impl.h> + +/* + * Compare the given input string and length against a table of known C storage + * qualifier keywords. We just ignore these in ctf_lookup_by_name, below. To + * do this quickly, we use a pre-computed Perfect Hash Function similar to the + * technique originally described in the classic paper: + * + * R.J. Cichelli, "Minimal Perfect Hash Functions Made Simple", + * Communications of the ACM, Volume 23, Issue 1, January 1980, pp. 17-19. + * + * For an input string S of length N, we use hash H = S[N - 1] + N - 105, which + * for the current set of qualifiers yields a unique H in the range [0 .. 20]. + * The hash can be modified when the keyword set changes as necessary. We also + * store the length of each keyword and check it prior to the final strcmp(). + */ +static int +isqualifier(const char *s, size_t len) +{ + static const struct qual { + const char *q_name; + size_t q_len; + } qhash[] = { + { "static", 6 }, { "", 0 }, { "", 0 }, { "", 0 }, + { "volatile", 8 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 }, + { "", 0 }, { "auto", 4 }, { "extern", 6 }, { "", 0 }, { "", 0 }, + { "", 0 }, { "", 0 }, { "const", 5 }, { "register", 8 }, + { "", 0 }, { "restrict", 8 }, { "_Restrict", 9 } + }; + + int h = s[len - 1] + (int)len - 105; + const struct qual *qp = &qhash[h]; + + return (h >= 0 && h < sizeof (qhash) / sizeof (qhash[0]) && + len == qp->q_len && strncmp(qp->q_name, s, qp->q_len) == 0); +} + +/* + * Attempt to convert the given C type name into the corresponding CTF type ID. + * It is not possible to do complete and proper conversion of type names + * without implementing a more full-fledged parser, which is necessary to + * handle things like types that are function pointers to functions that + * have arguments that are function pointers, and fun stuff like that. + * Instead, this function implements a very simple conversion algorithm that + * finds the things that we actually care about: structs, unions, enums, + * integers, floats, typedefs, and pointers to any of these named types. + */ +ctf_id_t +ctf_lookup_by_name(ctf_file_t *fp, const char *name) +{ + static const char delimiters[] = " \t\n\r\v\f*"; + + const ctf_lookup_t *lp; + const ctf_helem_t *hp; + const char *p, *q, *end; + ctf_id_t type = 0; + ctf_id_t ntype, ptype; + + if (name == NULL) + return (ctf_set_errno(fp, EINVAL)); + + for (p = name, end = name + strlen(name); *p != '\0'; p = q) { + while (isspace(*p)) + p++; /* skip leading ws */ + + if (p == end) + break; + + if ((q = strpbrk(p + 1, delimiters)) == NULL) + q = end; /* compare until end */ + + if (*p == '*') { + /* + * Find a pointer to type by looking in fp->ctf_ptrtab. + * If we can't find a pointer to the given type, see if + * we can compute a pointer to the type resulting from + * resolving the type down to its base type and use + * that instead. This helps with cases where the CTF + * data includes "struct foo *" but not "foo_t *" and + * the user tries to access "foo_t *" in the debugger. + */ + ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]; + if (ntype == 0) { + ntype = ctf_type_resolve(fp, type); + if (ntype == CTF_ERR || (ntype = fp->ctf_ptrtab[ + CTF_TYPE_TO_INDEX(ntype)]) == 0) { + (void) ctf_set_errno(fp, ECTF_NOTYPE); + goto err; + } + } + + type = CTF_INDEX_TO_TYPE(ntype, + (fp->ctf_flags & LCTF_CHILD)); + + q = p + 1; + continue; + } + + if (isqualifier(p, (size_t)(q - p))) + continue; /* skip qualifier keyword */ + + for (lp = fp->ctf_lookups; lp->ctl_prefix != NULL; lp++) { + if (lp->ctl_prefix[0] == '\0' || +#ifdef VBOX + ( strncmp(p, lp->ctl_prefix, (size_t)(q - p)) == 0 + && lp->ctl_len <= (size_t)(q - p)) /* don't push 'p' to far and crash */ ) { +#else + strncmp(p, lp->ctl_prefix, (size_t)(q - p)) == 0) { +#endif + for (p += lp->ctl_len; isspace(*p); p++) + continue; /* skip prefix and next ws */ + + if ((q = strchr(p, '*')) == NULL) + q = end; /* compare until end */ + + while (isspace(q[-1])) + q--; /* exclude trailing ws */ + + if ((hp = ctf_hash_lookup(lp->ctl_hash, fp, p, + (size_t)(q - p))) == NULL) { + (void) ctf_set_errno(fp, ECTF_NOTYPE); + goto err; + } + + type = hp->h_type; + break; + } + } + + if (lp->ctl_prefix == NULL) { + (void) ctf_set_errno(fp, ECTF_NOTYPE); + goto err; + } + } + + if (*p != '\0' || type == 0) + return (ctf_set_errno(fp, ECTF_SYNTAX)); + + return (type); + +err: + if (fp->ctf_parent != NULL && + (ptype = ctf_lookup_by_name(fp->ctf_parent, name)) != CTF_ERR) + return (ptype); + + return (CTF_ERR); +} + +/* + * Given a symbol table index, return the type of the data object described + * by the corresponding entry in the symbol table. + */ +ctf_id_t +ctf_lookup_by_symbol(ctf_file_t *fp, ulong_t symidx) +{ + const ctf_sect_t *sp = &fp->ctf_symtab; + ctf_id_t type; + + if (sp->cts_data == NULL) + return (ctf_set_errno(fp, ECTF_NOSYMTAB)); + + if (symidx >= fp->ctf_nsyms) + return (ctf_set_errno(fp, EINVAL)); + + if (sp->cts_entsize == sizeof (Elf32_Sym)) { + const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx; + if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT) + return (ctf_set_errno(fp, ECTF_NOTDATA)); + } else { + const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx; + if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT) + return (ctf_set_errno(fp, ECTF_NOTDATA)); + } + + if (fp->ctf_sxlate[symidx] == ~0u /*VBOX: -1u*/) + return (ctf_set_errno(fp, ECTF_NOTYPEDAT)); + + type = *(ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]); + if (type == 0) + return (ctf_set_errno(fp, ECTF_NOTYPEDAT)); + + return (type); +} + +/* + * Return the pointer to the internal CTF type data corresponding to the + * given type ID. If the ID is invalid, the function returns NULL. + * This function is not exported outside of the library. + */ +const ctf_type_t * +ctf_lookup_by_id(ctf_file_t **fpp, ctf_id_t type) +{ + ctf_file_t *fp = *fpp; /* caller passes in starting CTF container */ + + if ((fp->ctf_flags & LCTF_CHILD) && CTF_TYPE_ISPARENT(type) && + (fp = fp->ctf_parent) == NULL) { + (void) ctf_set_errno(*fpp, ECTF_NOPARENT); + return (NULL); + } + + type = CTF_TYPE_TO_INDEX(type); + if (type > 0 && type <= (intptr_t/*vbox*/)fp->ctf_typemax) { + *fpp = fp; /* function returns ending CTF container */ + return (LCTF_INDEX_TO_TYPEPTR(fp, type)); + } + + (void) ctf_set_errno(fp, ECTF_BADID); + return (NULL); +} + +/* + * Given a symbol table index, return the info for the function described + * by the corresponding entry in the symbol table. + */ +int +ctf_func_info(ctf_file_t *fp, ulong_t symidx, ctf_funcinfo_t *fip) +{ + const ctf_sect_t *sp = &fp->ctf_symtab; + const ushort_t *dp; + ushort_t info, kind, n; + + if (sp->cts_data == NULL) + return (ctf_set_errno(fp, ECTF_NOSYMTAB)); + + if (symidx >= fp->ctf_nsyms) + return (ctf_set_errno(fp, EINVAL)); + + if (sp->cts_entsize == sizeof (Elf32_Sym)) { + const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx; + if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC) + return (ctf_set_errno(fp, ECTF_NOTFUNC)); + } else { + const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx; + if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC) + return (ctf_set_errno(fp, ECTF_NOTFUNC)); + } + + if (fp->ctf_sxlate[symidx] == ~0u /*VBOX: -1u*/) + return (ctf_set_errno(fp, ECTF_NOFUNCDAT)); + + dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]); + + info = *dp++; + kind = LCTF_INFO_KIND(fp, info); + n = LCTF_INFO_VLEN(fp, info); + + if (kind == CTF_K_UNKNOWN && n == 0) + return (ctf_set_errno(fp, ECTF_NOFUNCDAT)); + + if (kind != CTF_K_FUNCTION) + return (ctf_set_errno(fp, ECTF_CORRUPT)); + + fip->ctc_return = *dp++; + fip->ctc_argc = n; + fip->ctc_flags = 0; + + if (n != 0 && dp[n - 1] == 0) { + fip->ctc_flags |= CTF_FUNC_VARARG; + fip->ctc_argc--; + } + + return (0); +} + +/* + * Given a symbol table index, return the arguments for the function described + * by the corresponding entry in the symbol table. + */ +int +ctf_func_args(ctf_file_t *fp, ulong_t symidx, uint_t argc, ctf_id_t *argv) +{ + const ushort_t *dp; + ctf_funcinfo_t f; + + if (ctf_func_info(fp, symidx, &f) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + + /* + * The argument data is two ushort_t's past the translation table + * offset: one for the function info, and one for the return type. + */ + dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]) + 2; + + for (argc = MIN(argc, f.ctc_argc); argc != 0; argc--) + *argv++ = *dp++; + + return (0); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_open.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_open.c new file mode 100644 index 00000000..f86a0cfd --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_open.c @@ -0,0 +1,972 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#ifndef VBOX +#include <ctf_impl.h> +#include <sys/mman.h> +#include <sys/zmod.h> +#else /* VBOX */ +# define CTF_OLD_VERSIONS +# include <ctf_impl.h> +# include <zlib.h> +# define z_compress compress +# define z_uncompress uncompress +# define z_strerror zError +#endif /* VBOX */ + +static const ctf_dmodel_t _libctf_models[] = { + { "ILP32", CTF_MODEL_ILP32, 4, 1, 2, 4, 4 }, + { "LP64", CTF_MODEL_LP64, 8, 1, 2, 4, 8 }, + { NULL, 0, 0, 0, 0, 0, 0 } +}; + +const char _CTF_SECTION[] = ".SUNW_ctf"; +const char _CTF_NULLSTR[] = ""; + +int _libctf_version = CTF_VERSION; /* library client version */ +int _libctf_debug = 0; /* debugging messages enabled */ + +static ushort_t +get_kind_v1(ushort_t info) +{ + return (CTF_INFO_KIND_V1(info)); +} + +static ushort_t +get_kind_v2(ushort_t info) +{ + return (CTF_INFO_KIND(info)); +} + +static ushort_t +get_root_v1(ushort_t info) +{ + return (CTF_INFO_ISROOT_V1(info)); +} + +static ushort_t +get_root_v2(ushort_t info) +{ + return (CTF_INFO_ISROOT(info)); +} + +static ushort_t +get_vlen_v1(ushort_t info) +{ + return (CTF_INFO_VLEN_V1(info)); +} + +static ushort_t +get_vlen_v2(ushort_t info) +{ + return (CTF_INFO_VLEN(info)); +} + +static const ctf_fileops_t ctf_fileops[] = { + { NULL, NULL }, + { get_kind_v1, get_root_v1, get_vlen_v1 }, + { get_kind_v2, get_root_v2, get_vlen_v2 }, +}; + +/* + * Convert a 32-bit ELF symbol into GElf (Elf64) and return a pointer to it. + */ +static Elf64_Sym * +sym_to_gelf(const Elf32_Sym *src, Elf64_Sym *dst) +{ + dst->st_name = src->st_name; + dst->st_value = src->st_value; + dst->st_size = src->st_size; + dst->st_info = src->st_info; + dst->st_other = src->st_other; + dst->st_shndx = src->st_shndx; + + return (dst); +} + +/* + * Initialize the symtab translation table by filling each entry with the + * offset of the CTF type or function data corresponding to each STT_FUNC or + * STT_OBJECT entry in the symbol table. + */ +static int +init_symtab(ctf_file_t *fp, const ctf_header_t *hp, + const ctf_sect_t *sp, const ctf_sect_t *strp) +{ + const uchar_t *symp = sp->cts_data; + uint_t *xp = fp->ctf_sxlate; + uint_t *xend = xp + fp->ctf_nsyms; + + uint_t objtoff = hp->cth_objtoff; + uint_t funcoff = hp->cth_funcoff; + + ushort_t info, vlen; + Elf64_Sym sym, *gsp; + const char *name; + + /* + * The CTF data object and function type sections are ordered to match + * the relative order of the respective symbol types in the symtab. + * If no type information is available for a symbol table entry, a + * pad is inserted in the CTF section. As a further optimization, + * anonymous or undefined symbols are omitted from the CTF data. + */ + for (; xp < xend; xp++, symp += sp->cts_entsize) { + if (sp->cts_entsize == sizeof (Elf32_Sym)) + gsp = sym_to_gelf((Elf32_Sym *)(uintptr_t)symp, &sym); + else + gsp = (Elf64_Sym *)(uintptr_t)symp; + + if (gsp->st_name < strp->cts_size) + name = (const char *)strp->cts_data + gsp->st_name; + else + name = _CTF_NULLSTR; + + if (gsp->st_name == 0 || gsp->st_shndx == SHN_UNDEF || + strcmp(name, "_START_") == 0 || + strcmp(name, "_END_") == 0) { + *xp = ~0u /*VBOX: -1u*/; + continue; + } + + switch (ELF64_ST_TYPE(gsp->st_info)) { + case STT_OBJECT: + if (objtoff >= hp->cth_funcoff || + (gsp->st_shndx == SHN_ABS && gsp->st_value == 0)) { + *xp = ~0u /*VBOX: -1u*/; + break; + } + + *xp = objtoff; + objtoff += sizeof (ushort_t); + break; + + case STT_FUNC: + if (funcoff >= hp->cth_typeoff) { + *xp = ~0u /*VBOX: -1u*/; + break; + } + + *xp = funcoff; + + info = *(ushort_t *)((uintptr_t)fp->ctf_buf + funcoff); + vlen = LCTF_INFO_VLEN(fp, info); + + /* + * If we encounter a zero pad at the end, just skip it. + * Otherwise skip over the function and its return type + * (+2) and the argument list (vlen). + */ + if (LCTF_INFO_KIND(fp, info) == CTF_K_UNKNOWN && + vlen == 0) + funcoff += sizeof (ushort_t); /* skip pad */ + else + funcoff += sizeof (ushort_t) * (vlen + 2); + break; + + default: + *xp = ~0u /*VBOX: -1u*/; + break; + } + } + + ctf_dprintf("loaded %lu symtab entries\n", fp->ctf_nsyms); + return (0); +} + +/* + * Initialize the type ID translation table with the byte offset of each type, + * and initialize the hash tables of each named type. + */ +static int +init_types(ctf_file_t *fp, const ctf_header_t *cth) +{ + /* LINTED - pointer alignment */ + const ctf_type_t *tbuf = (ctf_type_t *)(fp->ctf_buf + cth->cth_typeoff); + /* LINTED - pointer alignment */ + const ctf_type_t *tend = (ctf_type_t *)(fp->ctf_buf + cth->cth_stroff); + + ulong_t pop[CTF_K_MAX + 1] = { 0 }; + const ctf_type_t *tp; + ctf_hash_t *hp; + ushort_t id, dst; + uint_t *xp; + + /* + * We initially determine whether the container is a child or a parent + * based on the value of cth_parname. To support containers that pre- + * date cth_parname, we also scan the types themselves for references + * to values in the range reserved for child types in our first pass. + */ + int child = cth->cth_parname != 0; + int nlstructs = 0, nlunions = 0; + int err; + + /* + * We make two passes through the entire type section. In this first + * pass, we count the number of each type and the total number of types. + */ + for (tp = tbuf; tp < tend; fp->ctf_typemax++) { + ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info); + ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info); + ssize_t size, increment; + + size_t vbytes; + uint_t n; + + (void) ctf_get_ctt_size(fp, tp, &size, &increment); + + switch (kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + vbytes = sizeof (uint_t); + break; + case CTF_K_ARRAY: + vbytes = sizeof (ctf_array_t); + break; + case CTF_K_FUNCTION: + vbytes = sizeof (ushort_t) * (vlen + (vlen & 1)); + break; + case CTF_K_STRUCT: + case CTF_K_UNION: + if (fp->ctf_version == CTF_VERSION_1 || + size < CTF_LSTRUCT_THRESH) { + ctf_member_t *mp = (ctf_member_t *) + ((uintptr_t)tp + increment); + + vbytes = sizeof (ctf_member_t) * vlen; + for (n = vlen; n != 0; n--, mp++) + child |= CTF_TYPE_ISCHILD(mp->ctm_type); + } else { + ctf_lmember_t *lmp = (ctf_lmember_t *) + ((uintptr_t)tp + increment); + + vbytes = sizeof (ctf_lmember_t) * vlen; + for (n = vlen; n != 0; n--, lmp++) + child |= + CTF_TYPE_ISCHILD(lmp->ctlm_type); + } + break; + case CTF_K_ENUM: + vbytes = sizeof (ctf_enum_t) * vlen; + break; + case CTF_K_FORWARD: + /* + * For forward declarations, ctt_type is the CTF_K_* + * kind for the tag, so bump that population count too. + * If ctt_type is unknown, treat the tag as a struct. + */ + if (tp->ctt_type == CTF_K_UNKNOWN || + tp->ctt_type >= CTF_K_MAX) + pop[CTF_K_STRUCT]++; + else + pop[tp->ctt_type]++; + RT_FALL_THRU(); + case CTF_K_UNKNOWN: + vbytes = 0; + break; + case CTF_K_POINTER: + case CTF_K_TYPEDEF: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + child |= CTF_TYPE_ISCHILD(tp->ctt_type); + vbytes = 0; + break; + default: + ctf_dprintf("detected invalid CTF kind -- %u\n", kind); + return (ECTF_CORRUPT); + } + tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); + pop[kind]++; + } + + /* + * If we detected a reference to a child type ID, then we know this + * container is a child and may have a parent's types imported later. + */ + if (child) { + ctf_dprintf("CTF container %p is a child\n", (void *)fp); + fp->ctf_flags |= LCTF_CHILD; + } else + ctf_dprintf("CTF container %p is a parent\n", (void *)fp); + + /* + * Now that we've counted up the number of each type, we can allocate + * the hash tables, type translation table, and pointer table. + */ + if ((err = ctf_hash_create(&fp->ctf_structs, pop[CTF_K_STRUCT])) != 0) + return (err); + + if ((err = ctf_hash_create(&fp->ctf_unions, pop[CTF_K_UNION])) != 0) + return (err); + + if ((err = ctf_hash_create(&fp->ctf_enums, pop[CTF_K_ENUM])) != 0) + return (err); + + if ((err = ctf_hash_create(&fp->ctf_names, + pop[CTF_K_INTEGER] + pop[CTF_K_FLOAT] + pop[CTF_K_FUNCTION] + + pop[CTF_K_TYPEDEF] + pop[CTF_K_POINTER] + pop[CTF_K_VOLATILE] + + pop[CTF_K_CONST] + pop[CTF_K_RESTRICT])) != 0) + return (err); + + fp->ctf_txlate = ctf_alloc(sizeof (uint_t) * (fp->ctf_typemax + 1)); + fp->ctf_ptrtab = ctf_alloc(sizeof (ushort_t) * (fp->ctf_typemax + 1)); + + if (fp->ctf_txlate == NULL || fp->ctf_ptrtab == NULL) + return (EAGAIN); /* memory allocation failed */ + + xp = fp->ctf_txlate; + *xp++ = 0; /* type id 0 is used as a sentinel value */ + + bzero(fp->ctf_txlate, sizeof (uint_t) * (fp->ctf_typemax + 1)); + bzero(fp->ctf_ptrtab, sizeof (ushort_t) * (fp->ctf_typemax + 1)); + + /* + * In the second pass through the types, we fill in each entry of the + * type and pointer tables and add names to the appropriate hashes. + */ + for (id = 1, tp = tbuf; tp < tend; xp++, id++) { + ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info); + ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info); + ssize_t size, increment; + + const char *name; + size_t vbytes; + ctf_helem_t *hep; + ctf_encoding_t cte; + + (void) ctf_get_ctt_size(fp, tp, &size, &increment); + name = ctf_strptr(fp, tp->ctt_name); + + switch (kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + /* + * Only insert a new integer base type definition if + * this type name has not been defined yet. We re-use + * the names with different encodings for bit-fields. + */ + if ((hep = ctf_hash_lookup(&fp->ctf_names, fp, + name, strlen(name))) == NULL) { + err = ctf_hash_insert(&fp->ctf_names, fp, + CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); + if (err != 0 && err != ECTF_STRTAB) + return (err); + } else if (ctf_type_encoding(fp, hep->h_type, + &cte) == 0 && cte.cte_bits == 0) { + /* + * Work-around SOS8 stabs bug: replace existing + * intrinsic w/ same name if it was zero bits. + */ + hep->h_type = CTF_INDEX_TO_TYPE(id, child); + } + vbytes = sizeof (uint_t); + break; + + case CTF_K_ARRAY: + vbytes = sizeof (ctf_array_t); + break; + + case CTF_K_FUNCTION: + err = ctf_hash_insert(&fp->ctf_names, fp, + CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); + if (err != 0 && err != ECTF_STRTAB) + return (err); + vbytes = sizeof (ushort_t) * (vlen + (vlen & 1)); + break; + + case CTF_K_STRUCT: + err = ctf_hash_define(&fp->ctf_structs, fp, + CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); + + if (err != 0 && err != ECTF_STRTAB) + return (err); + + if (fp->ctf_version == CTF_VERSION_1 || + size < CTF_LSTRUCT_THRESH) + vbytes = sizeof (ctf_member_t) * vlen; + else { + vbytes = sizeof (ctf_lmember_t) * vlen; + nlstructs++; + } + break; + + case CTF_K_UNION: + err = ctf_hash_define(&fp->ctf_unions, fp, + CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); + + if (err != 0 && err != ECTF_STRTAB) + return (err); + + if (fp->ctf_version == CTF_VERSION_1 || + size < CTF_LSTRUCT_THRESH) + vbytes = sizeof (ctf_member_t) * vlen; + else { + vbytes = sizeof (ctf_lmember_t) * vlen; + nlunions++; + } + break; + + case CTF_K_ENUM: + err = ctf_hash_define(&fp->ctf_enums, fp, + CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); + + if (err != 0 && err != ECTF_STRTAB) + return (err); + + vbytes = sizeof (ctf_enum_t) * vlen; + break; + + case CTF_K_TYPEDEF: + err = ctf_hash_insert(&fp->ctf_names, fp, + CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); + if (err != 0 && err != ECTF_STRTAB) + return (err); + vbytes = 0; + break; + + case CTF_K_FORWARD: + /* + * Only insert forward tags into the given hash if the + * type or tag name is not already present. + */ + switch (tp->ctt_type) { + case CTF_K_STRUCT: + hp = &fp->ctf_structs; + break; + case CTF_K_UNION: + hp = &fp->ctf_unions; + break; + case CTF_K_ENUM: + hp = &fp->ctf_enums; + break; + default: + hp = &fp->ctf_structs; + } + + if (ctf_hash_lookup(hp, fp, + name, strlen(name)) == NULL) { + err = ctf_hash_insert(hp, fp, + CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); + if (err != 0 && err != ECTF_STRTAB) + return (err); + } + vbytes = 0; + break; + + case CTF_K_POINTER: + /* + * If the type referenced by the pointer is in this CTF + * container, then store the index of the pointer type + * in fp->ctf_ptrtab[ index of referenced type ]. + */ + if (CTF_TYPE_ISCHILD(tp->ctt_type) == child && + CTF_TYPE_TO_INDEX(tp->ctt_type) <= (intptr_t/*vbox*/)fp->ctf_typemax) + fp->ctf_ptrtab[ + CTF_TYPE_TO_INDEX(tp->ctt_type)] = id; + RT_FALL_THRU(); + + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + err = ctf_hash_insert(&fp->ctf_names, fp, + CTF_INDEX_TO_TYPE(id, child), tp->ctt_name); + if (err != 0 && err != ECTF_STRTAB) + return (err); + RT_FALL_THRU(); + + default: + vbytes = 0; + break; + } + + *xp = (uint_t)((uintptr_t)tp - (uintptr_t)fp->ctf_buf); + tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); + } + + ctf_dprintf("%lu total types processed\n", fp->ctf_typemax); + ctf_dprintf("%u enum names hashed\n", ctf_hash_size(&fp->ctf_enums)); + ctf_dprintf("%u struct names hashed (%d long)\n", + ctf_hash_size(&fp->ctf_structs), nlstructs); + ctf_dprintf("%u union names hashed (%d long)\n", + ctf_hash_size(&fp->ctf_unions), nlunions); + ctf_dprintf("%u base type names hashed\n", + ctf_hash_size(&fp->ctf_names)); + + /* + * Make an additional pass through the pointer table to find pointers + * that point to anonymous typedef nodes. If we find one, modify the + * pointer table so that the pointer is also known to point to the + * node that is referenced by the anonymous typedef node. + */ + for (id = 1; id <= fp->ctf_typemax; id++) { + if ((dst = fp->ctf_ptrtab[id]) != 0) { + tp = LCTF_INDEX_TO_TYPEPTR(fp, id); + + if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_TYPEDEF && + strcmp(ctf_strptr(fp, tp->ctt_name), "") == 0 && + CTF_TYPE_ISCHILD(tp->ctt_type) == child && + CTF_TYPE_TO_INDEX(tp->ctt_type) <= (intptr_t /*vbox*/)fp->ctf_typemax) + fp->ctf_ptrtab[ + CTF_TYPE_TO_INDEX(tp->ctt_type)] = dst; + } + } + + return (0); +} + +/* + * Decode the specified CTF buffer and optional symbol table and create a new + * CTF container representing the symbolic debugging information. This code + * can be used directly by the debugger, or it can be used as the engine for + * ctf_fdopen() or ctf_open(), below. + */ +ctf_file_t * +ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect, + const ctf_sect_t *strsect, int *errp) +{ + const ctf_preamble_t *pp; + ctf_header_t hp; + ctf_file_t *fp; + void *buf, *base; + size_t size, hdrsz; + int err; + + if (ctfsect == NULL || ((symsect == NULL) != (strsect == NULL))) + return (ctf_set_open_errno(errp, EINVAL)); + + if (symsect != NULL && symsect->cts_entsize != sizeof (Elf32_Sym) && + symsect->cts_entsize != sizeof (Elf64_Sym)) + return (ctf_set_open_errno(errp, ECTF_SYMTAB)); + + if (symsect != NULL && symsect->cts_data == NULL) + return (ctf_set_open_errno(errp, ECTF_SYMBAD)); + + if (strsect != NULL && strsect->cts_data == NULL) + return (ctf_set_open_errno(errp, ECTF_STRBAD)); + + if (ctfsect->cts_size < sizeof (ctf_preamble_t)) + return (ctf_set_open_errno(errp, ECTF_NOCTFBUF)); + + pp = (const ctf_preamble_t *)ctfsect->cts_data; + + ctf_dprintf("ctf_bufopen: magic=0x%x version=%u\n", + pp->ctp_magic, pp->ctp_version); + + /* + * Validate each part of the CTF header (either V1 or V2). + * First, we validate the preamble (common to all versions). At that + * point, we know specific header version, and can validate the + * version-specific parts including section offsets and alignments. + */ + if (pp->ctp_magic != CTF_MAGIC) + return (ctf_set_open_errno(errp, ECTF_NOCTFBUF)); + + if (pp->ctp_version == CTF_VERSION_2) { + if (ctfsect->cts_size < sizeof (ctf_header_t)) + return (ctf_set_open_errno(errp, ECTF_NOCTFBUF)); + + bcopy(ctfsect->cts_data, &hp, sizeof (hp)); + hdrsz = sizeof (ctf_header_t); + + } else if (pp->ctp_version == CTF_VERSION_1) { + const ctf_header_v1_t *h1p = + (const ctf_header_v1_t *)ctfsect->cts_data; + + if (ctfsect->cts_size < sizeof (ctf_header_v1_t)) + return (ctf_set_open_errno(errp, ECTF_NOCTFBUF)); + + bzero(&hp, sizeof (hp)); + hp.cth_preamble = h1p->cth_preamble; + hp.cth_objtoff = h1p->cth_objtoff; + hp.cth_funcoff = h1p->cth_funcoff; + hp.cth_typeoff = h1p->cth_typeoff; + hp.cth_stroff = h1p->cth_stroff; + hp.cth_strlen = h1p->cth_strlen; + + hdrsz = sizeof (ctf_header_v1_t); + } else + return (ctf_set_open_errno(errp, ECTF_CTFVERS)); + + size = hp.cth_stroff + hp.cth_strlen; + + ctf_dprintf("ctf_bufopen: uncompressed size=%lu\n", (ulong_t)size); + + if (hp.cth_lbloff > size || hp.cth_objtoff > size || + hp.cth_funcoff > size || hp.cth_typeoff > size || + hp.cth_stroff > size) + return (ctf_set_open_errno(errp, ECTF_CORRUPT)); + + if (hp.cth_lbloff > hp.cth_objtoff || + hp.cth_objtoff > hp.cth_funcoff || + hp.cth_funcoff > hp.cth_typeoff || + hp.cth_typeoff > hp.cth_stroff) + return (ctf_set_open_errno(errp, ECTF_CORRUPT)); + + if ((hp.cth_lbloff & 3) || (hp.cth_objtoff & 1) || + (hp.cth_funcoff & 1) || (hp.cth_typeoff & 3)) + return (ctf_set_open_errno(errp, ECTF_CORRUPT)); + + /* + * Once everything is determined to be valid, attempt to decompress + * the CTF data buffer if it is compressed. Otherwise we just put + * the data section's buffer pointer into ctf_buf, below. + */ + if (hp.cth_flags & CTF_F_COMPRESS) { +#ifndef VBOX + size_t srclen, dstlen; +#else + uLong srclen; + uLong dstlen; +#endif + const void *src; + int rc = Z_OK; + +#ifndef VBOX + if (ctf_zopen(errp) == NULL) + return (NULL); /* errp is set for us */ +#endif + + if ((base = ctf_data_alloc(size + hdrsz)) == MAP_FAILED) + return (ctf_set_open_errno(errp, ECTF_ZALLOC)); + + bcopy(ctfsect->cts_data, base, hdrsz); + ((ctf_preamble_t *)base)->ctp_flags &= ~CTF_F_COMPRESS; + buf = (uchar_t *)base + hdrsz; + + src = (uchar_t *)ctfsect->cts_data + hdrsz; + srclen = VBDTCAST(uLong)(ctfsect->cts_size - hdrsz); + dstlen = VBDTCAST(uLong)size; + + if ((rc = z_uncompress(buf, &dstlen, src, srclen)) != Z_OK) { + ctf_dprintf("zlib inflate err: %s\n", z_strerror(rc)); + ctf_data_free(base, size + hdrsz); + return (ctf_set_open_errno(errp, ECTF_DECOMPRESS)); + } + + if (dstlen != size) { + ctf_dprintf("zlib inflate short -- got %lu of %lu " + "bytes\n", (ulong_t)dstlen, (ulong_t)size); + ctf_data_free(base, size + hdrsz); + return (ctf_set_open_errno(errp, ECTF_CORRUPT)); + } + + ctf_data_protect(base, size + hdrsz); + + } else { + base = (void *)ctfsect->cts_data; + buf = (uchar_t *)base + hdrsz; + } + + /* + * Once we have uncompressed and validated the CTF data buffer, we can + * proceed with allocating a ctf_file_t and initializing it. + */ + if ((fp = ctf_alloc(sizeof (ctf_file_t))) == NULL) + return (ctf_set_open_errno(errp, EAGAIN)); + + bzero(fp, sizeof (ctf_file_t)); + fp->ctf_version = hp.cth_version; + fp->ctf_fileops = &ctf_fileops[hp.cth_version]; + bcopy(ctfsect, &fp->ctf_data, sizeof (ctf_sect_t)); + + if (symsect != NULL) { + bcopy(symsect, &fp->ctf_symtab, sizeof (ctf_sect_t)); + bcopy(strsect, &fp->ctf_strtab, sizeof (ctf_sect_t)); + } + + if (fp->ctf_data.cts_name != NULL) + fp->ctf_data.cts_name = ctf_strdup(fp->ctf_data.cts_name); + if (fp->ctf_symtab.cts_name != NULL) + fp->ctf_symtab.cts_name = ctf_strdup(fp->ctf_symtab.cts_name); + if (fp->ctf_strtab.cts_name != NULL) + fp->ctf_strtab.cts_name = ctf_strdup(fp->ctf_strtab.cts_name); + + if (fp->ctf_data.cts_name == NULL) + fp->ctf_data.cts_name = _CTF_NULLSTR; + if (fp->ctf_symtab.cts_name == NULL) + fp->ctf_symtab.cts_name = _CTF_NULLSTR; + if (fp->ctf_strtab.cts_name == NULL) + fp->ctf_strtab.cts_name = _CTF_NULLSTR; + + fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *)buf + hp.cth_stroff; + fp->ctf_str[CTF_STRTAB_0].cts_len = hp.cth_strlen; + + if (strsect != NULL) { + fp->ctf_str[CTF_STRTAB_1].cts_strs = strsect->cts_data; + fp->ctf_str[CTF_STRTAB_1].cts_len = strsect->cts_size; + } + + fp->ctf_base = base; + fp->ctf_buf = buf; + fp->ctf_size = size + hdrsz; + + /* + * If we have a parent container name and label, store the relocated + * string pointers in the CTF container for easy access later. + */ + if (hp.cth_parlabel != 0) + fp->ctf_parlabel = ctf_strptr(fp, hp.cth_parlabel); + if (hp.cth_parname != 0) + fp->ctf_parname = ctf_strptr(fp, hp.cth_parname); + + ctf_dprintf("ctf_bufopen: parent name %s (label %s)\n", + fp->ctf_parname ? fp->ctf_parname : "<NULL>", + fp->ctf_parlabel ? fp->ctf_parlabel : "<NULL>"); + + /* + * If we have a symbol table section, allocate and initialize + * the symtab translation table, pointed to by ctf_sxlate. + */ + if (symsect != NULL) { + fp->ctf_nsyms = symsect->cts_size / symsect->cts_entsize; + fp->ctf_sxlate = ctf_alloc(fp->ctf_nsyms * sizeof (uint_t)); + + if (fp->ctf_sxlate == NULL) { + (void) ctf_set_open_errno(errp, EAGAIN); + goto bad; + } + + if ((err = init_symtab(fp, &hp, symsect, strsect)) != 0) { + (void) ctf_set_open_errno(errp, err); + goto bad; + } + } + + if ((err = init_types(fp, &hp)) != 0) { + (void) ctf_set_open_errno(errp, err); + goto bad; + } + + /* + * Initialize the ctf_lookup_by_name top-level dictionary. We keep an + * array of type name prefixes and the corresponding ctf_hash to use. + * NOTE: This code must be kept in sync with the code in ctf_update(). + */ + fp->ctf_lookups[0].ctl_prefix = "struct"; + fp->ctf_lookups[0].ctl_len = strlen(fp->ctf_lookups[0].ctl_prefix); + fp->ctf_lookups[0].ctl_hash = &fp->ctf_structs; + fp->ctf_lookups[1].ctl_prefix = "union"; + fp->ctf_lookups[1].ctl_len = strlen(fp->ctf_lookups[1].ctl_prefix); + fp->ctf_lookups[1].ctl_hash = &fp->ctf_unions; + fp->ctf_lookups[2].ctl_prefix = "enum"; + fp->ctf_lookups[2].ctl_len = strlen(fp->ctf_lookups[2].ctl_prefix); + fp->ctf_lookups[2].ctl_hash = &fp->ctf_enums; + fp->ctf_lookups[3].ctl_prefix = _CTF_NULLSTR; + fp->ctf_lookups[3].ctl_len = strlen(fp->ctf_lookups[3].ctl_prefix); + fp->ctf_lookups[3].ctl_hash = &fp->ctf_names; + fp->ctf_lookups[4].ctl_prefix = NULL; + fp->ctf_lookups[4].ctl_len = 0; + fp->ctf_lookups[4].ctl_hash = NULL; + + if (symsect != NULL) { + if (symsect->cts_entsize == sizeof (Elf64_Sym)) + (void) ctf_setmodel(fp, CTF_MODEL_LP64); + else + (void) ctf_setmodel(fp, CTF_MODEL_ILP32); + } else + (void) ctf_setmodel(fp, CTF_MODEL_NATIVE); + + fp->ctf_refcnt = 1; + return (fp); + +bad: + ctf_close(fp); + return (NULL); +} + +/* + * Close the specified CTF container and free associated data structures. Note + * that ctf_close() is a reference counted operation: if the specified file is + * the parent of other active containers, its reference count will be greater + * than one and it will be freed later when no active children exist. + */ +void +ctf_close(ctf_file_t *fp) +{ + ctf_dtdef_t *dtd, *ntd; + + if (fp == NULL) + return; /* allow ctf_close(NULL) to simplify caller code */ + + ctf_dprintf("ctf_close(%p) refcnt=%u\n", (void *)fp, fp->ctf_refcnt); + + if (fp->ctf_refcnt > 1) { + fp->ctf_refcnt--; + return; + } + + if (fp->ctf_parent != NULL) + ctf_close(fp->ctf_parent); + + for (dtd = ctf_list_next(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { + ntd = ctf_list_next(dtd); + ctf_dtd_delete(fp, dtd); + } + + ctf_free(fp->ctf_dthash, fp->ctf_dthashlen * sizeof (ctf_dtdef_t *)); + + if (fp->ctf_flags & LCTF_MMAP) { + if (fp->ctf_data.cts_data != NULL) + ctf_sect_munmap(&fp->ctf_data); + if (fp->ctf_symtab.cts_data != NULL) + ctf_sect_munmap(&fp->ctf_symtab); + if (fp->ctf_strtab.cts_data != NULL) + ctf_sect_munmap(&fp->ctf_strtab); + } + + if (fp->ctf_data.cts_name != _CTF_NULLSTR && + fp->ctf_data.cts_name != NULL) { + ctf_free((char *)fp->ctf_data.cts_name, + strlen(fp->ctf_data.cts_name) + 1); + } + + if (fp->ctf_symtab.cts_name != _CTF_NULLSTR && + fp->ctf_symtab.cts_name != NULL) { + ctf_free((char *)fp->ctf_symtab.cts_name, + strlen(fp->ctf_symtab.cts_name) + 1); + } + + if (fp->ctf_strtab.cts_name != _CTF_NULLSTR && + fp->ctf_strtab.cts_name != NULL) { + ctf_free((char *)fp->ctf_strtab.cts_name, + strlen(fp->ctf_strtab.cts_name) + 1); + } + + if (fp->ctf_base != fp->ctf_data.cts_data && fp->ctf_base != NULL) + ctf_data_free((void *)fp->ctf_base, fp->ctf_size); + + if (fp->ctf_sxlate != NULL) + ctf_free(fp->ctf_sxlate, sizeof (uint_t) * fp->ctf_nsyms); + + if (fp->ctf_txlate != NULL) { + ctf_free(fp->ctf_txlate, + sizeof (uint_t) * (fp->ctf_typemax + 1)); + } + + if (fp->ctf_ptrtab != NULL) { + ctf_free(fp->ctf_ptrtab, + sizeof (ushort_t) * (fp->ctf_typemax + 1)); + } + + ctf_hash_destroy(&fp->ctf_structs); + ctf_hash_destroy(&fp->ctf_unions); + ctf_hash_destroy(&fp->ctf_enums); + ctf_hash_destroy(&fp->ctf_names); + + ctf_free(fp, sizeof (ctf_file_t)); +} + +/* + * Return the CTF handle for the parent CTF container, if one exists. + * Otherwise return NULL to indicate this container has no imported parent. + */ +ctf_file_t * +ctf_parent_file(ctf_file_t *fp) +{ + return (fp->ctf_parent); +} + +/* + * Return the name of the parent CTF container, if one exists. Otherwise + * return NULL to indicate this container is a root container. + */ +const char * +ctf_parent_name(ctf_file_t *fp) +{ + return (fp->ctf_parname); +} + +/* + * Import the types from the specified parent container by storing a pointer + * to it in ctf_parent and incrementing its reference count. Only one parent + * is allowed: if a parent already exists, it is replaced by the new parent. + */ +int +ctf_import(ctf_file_t *fp, ctf_file_t *pfp) +{ + if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0)) + return (ctf_set_errno(fp, EINVAL)); + + if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel) + return (ctf_set_errno(fp, ECTF_DMODEL)); + + if (fp->ctf_parent != NULL) + ctf_close(fp->ctf_parent); + + if (pfp != NULL) { + fp->ctf_flags |= LCTF_CHILD; + pfp->ctf_refcnt++; + } + + fp->ctf_parent = pfp; + return (0); +} + +/* + * Set the data model constant for the CTF container. + */ +int +ctf_setmodel(ctf_file_t *fp, int model) +{ + const ctf_dmodel_t *dp; + + for (dp = _libctf_models; dp->ctd_name != NULL; dp++) { + if (dp->ctd_code == model) { + fp->ctf_dmodel = dp; + return (0); + } + } + + return (ctf_set_errno(fp, EINVAL)); +} + +/* + * Return the data model constant for the CTF container. + */ +int +ctf_getmodel(ctf_file_t *fp) +{ + return (fp->ctf_dmodel->ctd_code); +} + +void +ctf_setspecific(ctf_file_t *fp, void *data) +{ + fp->ctf_specific = data; +} + +void * +ctf_getspecific(ctf_file_t *fp) +{ + return (fp->ctf_specific); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_types.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_types.c new file mode 100644 index 00000000..7375c067 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_types.c @@ -0,0 +1,847 @@ +/* + * 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. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#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 && VBDTCAST(size_t)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(VBDTCAST(ssize_t)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(VBDTCAST(ssize_t)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)); +} diff --git a/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_util.c b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_util.c new file mode 100644 index 00000000..c13c1d64 --- /dev/null +++ b/src/VBox/ExtPacks/VBoxDTrace/onnv/common/ctf/ctf_util.c @@ -0,0 +1,154 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef VBOX +#pragma ident "%Z%%M% %I% %E% SMI" +#endif + +#include <ctf_impl.h> + +/* + * Simple doubly-linked list append routine. This implementation assumes that + * each list element contains an embedded ctf_list_t as the first member. + * An additional ctf_list_t is used to store the head (l_next) and tail + * (l_prev) pointers. The current head and tail list elements have their + * previous and next pointers set to NULL, respectively. + */ +void +ctf_list_append(ctf_list_t *lp, void *new) +{ + ctf_list_t *p = lp->l_prev; /* p = tail list element */ + ctf_list_t *q = new; /* q = new list element */ + + lp->l_prev = q; + q->l_prev = p; + q->l_next = NULL; + + if (p != NULL) + p->l_next = q; + else + lp->l_next = q; +} + +/* + * Prepend the specified existing element to the given ctf_list_t. The + * existing pointer should be pointing at a struct with embedded ctf_list_t. + */ +void +ctf_list_prepend(ctf_list_t *lp, void *new) +{ + ctf_list_t *p = new; /* p = new list element */ + ctf_list_t *q = lp->l_next; /* q = head list element */ + + lp->l_next = p; + p->l_prev = NULL; + p->l_next = q; + + if (q != NULL) + q->l_prev = p; + else + lp->l_prev = p; +} + +/* + * Delete the specified existing element from the given ctf_list_t. The + * existing pointer should be pointing at a struct with embedded ctf_list_t. + */ +void +ctf_list_delete(ctf_list_t *lp, void *existing) +{ + ctf_list_t *p = existing; + + if (p->l_prev != NULL) + p->l_prev->l_next = p->l_next; + else + lp->l_next = p->l_next; + + if (p->l_next != NULL) + p->l_next->l_prev = p->l_prev; + else + lp->l_prev = p->l_prev; +} + +/* + * Convert an encoded CTF string name into a pointer to a C string by looking + * up the appropriate string table buffer and then adding the offset. + */ +const char * +ctf_strraw(ctf_file_t *fp, uint_t name) +{ + ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID(name)]; + + if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET(name) < ctsp->cts_len) + return (ctsp->cts_strs + CTF_NAME_OFFSET(name)); + + /* string table not loaded or corrupt offset */ + return (NULL); +} + +const char * +ctf_strptr(ctf_file_t *fp, uint_t name) +{ + const char *s = ctf_strraw(fp, name); + return (s != NULL ? s : "(?)"); +} + +/* + * Same strdup(3C), but use ctf_alloc() to do the memory allocation. + */ +char * +ctf_strdup(const char *s1) +{ + char *s2 = ctf_alloc(strlen(s1) + 1); + + if (s2 != NULL) + (void) strcpy(s2, s1); + + return (s2); +} + +/* + * Store the specified error code into errp if it is non-NULL, and then + * return NULL for the benefit of the caller. + */ +ctf_file_t * +ctf_set_open_errno(int *errp, int error) +{ + if (errp != NULL) + *errp = error; + return (NULL); +} + +/* + * Store the specified error code into the CTF container, and then return + * CTF_ERR for the benefit of the caller. + */ +long +ctf_set_errno(ctf_file_t *fp, int err) +{ + fp->ctf_errno = err; + return (CTF_ERR); +} |