/* * 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 (struct nlist) && symsect->cts_entsize != sizeof (struct nlist_64)) 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) { size_t srclen, dstlen; const void *src; int rc = Z_OK; if (ctf_zopen(errp) == NULL) return (NULL); /* errp is set for us */ 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 = ctfsect->cts_size - hdrsz; dstlen = 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 (struct nlist_64)) (void) ctf_setmodel(fp, CTF_MODEL_LP64); else if (symsect->cts_entsize == sizeof (struct nlist)) (void) ctf_setmodel(fp, CTF_MODEL_ILP32); else 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); }
ctf_file_t * dt_module_getctf(dtrace_hdl_t *dtp, dt_module_t *dmp) { const char *parent; dt_module_t *pmp; ctf_file_t *pfp; int model; if (dmp->dm_ctfp != NULL || dt_module_load(dtp, dmp) != 0) return (dmp->dm_ctfp); if (dmp->dm_ops == &dt_modops_64) model = CTF_MODEL_LP64; else model = CTF_MODEL_ILP32; /* * If the data model of the module does not match our program data * model, then do not permit CTF from this module to be opened and * returned to the compiler. If we support mixed data models in the * future for combined kernel/user tracing, this can be removed. */ if (dtp->dt_conf.dtc_ctfmodel != model) { (void) dt_set_errno(dtp, EDT_DATAMODEL); return (NULL); } if (dmp->dm_ctdata.cts_size == 0) { (void) dt_set_errno(dtp, EDT_NOCTF); return (NULL); } dmp->dm_ctfp = ctf_bufopen(&dmp->dm_ctdata, &dmp->dm_symtab, &dmp->dm_strtab, &dtp->dt_ctferr); if (dmp->dm_ctfp == NULL) { (void) dt_set_errno(dtp, EDT_CTF); return (NULL); } (void) ctf_setmodel(dmp->dm_ctfp, model); ctf_setspecific(dmp->dm_ctfp, dmp); if ((parent = ctf_parent_name(dmp->dm_ctfp)) != NULL) { if ((pmp = dt_module_create(dtp, parent)) == NULL || (pfp = dt_module_getctf(dtp, pmp)) == NULL) { if (pmp == NULL) (void) dt_set_errno(dtp, EDT_NOMEM); goto err; } if (ctf_import(dmp->dm_ctfp, pfp) == CTF_ERR) { dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp); (void) dt_set_errno(dtp, EDT_CTF); goto err; } } dt_dprintf("loaded CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); return (dmp->dm_ctfp); err: ctf_close(dmp->dm_ctfp); dmp->dm_ctfp = NULL; return (NULL); }
/* * 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. * * Note that the lists of dynamic types stays around and the resulting container * is still writeable. Furthermore, the reference counts that are on the dtd's * are still valid. */ 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 + size; hdr.cth_strlen = 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); }
ctf_file_t * dt_module_getctf(dtrace_hdl_t *dtp, dt_module_t *dmp) { const char *parent; dt_module_t *pmp; ctf_file_t *pfp; int model; if (dmp->dm_ctfp != NULL || dt_module_load(dtp, dmp) != 0) return (dmp->dm_ctfp); if (dmp->dm_ops == &dt_modops_macho_64) model = CTF_MODEL_LP64; else if (dmp->dm_ops == &dt_modops_macho_32) model = CTF_MODEL_ILP32; else if (dmp->dm_ops == &dt_modops_64) model = CTF_MODEL_LP64; else model = CTF_MODEL_ILP32; if (dmp->dm_ctdata.cts_size == 0) { (void) dt_set_errno(dtp, EDT_NOCTF); return (NULL); } dmp->dm_ctfp = ctf_bufopen(&dmp->dm_ctdata, &dmp->dm_symtab, &dmp->dm_strtab, &dtp->dt_ctferr); if (dmp->dm_ctfp == NULL) { (void) dt_set_errno(dtp, EDT_CTF); return (NULL); } (void) ctf_setmodel(dmp->dm_ctfp, model); ctf_setspecific(dmp->dm_ctfp, dmp); if ((parent = ctf_parent_name(dmp->dm_ctfp)) != NULL) { if ((pmp = dt_module_create(dtp, parent)) == NULL || (pfp = dt_module_getctf(dtp, pmp)) == NULL) { if (pmp == NULL) (void) dt_set_errno(dtp, EDT_NOMEM); goto err; } if (ctf_import(dmp->dm_ctfp, pfp) == CTF_ERR) { dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp); (void) dt_set_errno(dtp, EDT_CTF); goto err; } } dt_dprintf("loaded CTF container for %s (%p)\n", dmp->dm_name, (void *)dmp->dm_ctfp); return (dmp->dm_ctfp); err: ctf_close(dmp->dm_ctfp); dmp->dm_ctfp = NULL; return (NULL); }