/* * 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_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); }
/* * Open the specified file descriptor and return a pointer to a CTF container. * The file can be either an ELF file or raw CTF file. The caller is * responsible for closing the file descriptor when it is no longer needed. */ ctf_file_t * ctf_fdopen(int fd, int *errp) { ctf_sect_t ctfsect, symsect, strsect; ctf_file_t *fp = NULL; struct stat64 st; ssize_t nbytes; union { ctf_preamble_t ctf; Elf32_Ehdr e32; GElf_Ehdr e64; } hdr; bzero(&ctfsect, sizeof (ctf_sect_t)); bzero(&symsect, sizeof (ctf_sect_t)); bzero(&strsect, sizeof (ctf_sect_t)); bzero(&hdr.ctf, sizeof (hdr)); if (fstat64(fd, &st) == -1) return (ctf_set_open_errno(errp, errno)); if ((nbytes = pread64(fd, &hdr.ctf, sizeof (hdr), 0)) <= 0) return (ctf_set_open_errno(errp, nbytes < 0? errno : ECTF_FMT)); /* * If we have read enough bytes to form a CTF header and the magic * string matches, attempt to interpret the file as raw CTF. */ if (nbytes >= (ssize_t) sizeof (ctf_preamble_t) && hdr.ctf.ctp_magic == CTF_MAGIC) { if (hdr.ctf.ctp_version > CTF_VERSION) return (ctf_set_open_errno(errp, ECTF_CTFVERS)); ctfsect.cts_data = mmap64(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (ctfsect.cts_data == MAP_FAILED) return (ctf_set_open_errno(errp, errno)); ctfsect.cts_name = _CTF_SECTION; ctfsect.cts_type = SHT_PROGBITS; ctfsect.cts_flags = SHF_ALLOC; ctfsect.cts_size = (size_t)st.st_size; ctfsect.cts_entsize = 1; ctfsect.cts_offset = 0; if ((fp = ctf_bufopen(&ctfsect, NULL, NULL, errp)) == NULL) ctf_sect_munmap(&ctfsect); return (fp); } /* * If we have read enough bytes to form an ELF header and the magic * string matches, attempt to interpret the file as an ELF file. We * do our own largefile ELF processing, and convert everything to * GElf structures so that clients can operate on any data model. */ if (nbytes >= (ssize_t) sizeof (Elf32_Ehdr) && bcmp(&hdr.e32.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0) { #ifdef _BIG_ENDIAN uchar_t order = ELFDATA2MSB; #else uchar_t order = ELFDATA2LSB; #endif GElf_Half i, n; GElf_Shdr *sp; void *strs_map; size_t strs_mapsz; char *strs; if (hdr.e32.e_ident[EI_DATA] != order) return (ctf_set_open_errno(errp, ECTF_ENDIAN)); if (hdr.e32.e_version != EV_CURRENT) return (ctf_set_open_errno(errp, ECTF_ELFVERS)); if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS64) { if (nbytes < (ssize_t) sizeof (GElf_Ehdr)) return (ctf_set_open_errno(errp, ECTF_FMT)); } else { Elf32_Ehdr e32 = hdr.e32; ehdr_to_gelf(&e32, &hdr.e64); } if (hdr.e64.e_shstrndx >= hdr.e64.e_shnum) return (ctf_set_open_errno(errp, ECTF_CORRUPT)); n = hdr.e64.e_shnum; nbytes = sizeof (GElf_Shdr) * n; if ((sp = malloc(nbytes)) == NULL) return (ctf_set_open_errno(errp, errno)); /* * Read in and convert to GElf the array of Shdr structures * from e_shoff so we can locate sections of interest. */ if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS32) { Elf32_Shdr *sp32; nbytes = sizeof (Elf32_Shdr) * n; if ((sp32 = malloc(nbytes)) == NULL || pread64(fd, sp32, nbytes, hdr.e64.e_shoff) != nbytes) { free(sp); return (ctf_set_open_errno(errp, errno)); } for (i = 0; i < n; i++) shdr_to_gelf(&sp32[i], &sp[i]); free(sp32); } else if (pread64(fd, sp, nbytes, hdr.e64.e_shoff) != nbytes) { free(sp); return (ctf_set_open_errno(errp, errno)); } /* * Now mmap the section header strings section so that we can * perform string comparison on the section names. */ strs_mapsz = sp[hdr.e64.e_shstrndx].sh_size + (sp[hdr.e64.e_shstrndx].sh_offset & ~_PAGEMASK); strs_map = mmap64(NULL, strs_mapsz, PROT_READ, MAP_PRIVATE, fd, sp[hdr.e64.e_shstrndx].sh_offset & _PAGEMASK); strs = (char *)strs_map + (sp[hdr.e64.e_shstrndx].sh_offset & ~_PAGEMASK); if (strs_map == MAP_FAILED) { free(sp); return (ctf_set_open_errno(errp, ECTF_MMAP)); } /* * Iterate over the section header array looking for the CTF * section and symbol table. The strtab is linked to symtab. */ for (i = 0; i < n; i++) { const GElf_Shdr *shp = &sp[i]; const GElf_Shdr *lhp = &sp[shp->sh_link]; if (shp->sh_link >= hdr.e64.e_shnum) continue; /* corrupt sh_link field */ if (shp->sh_name >= sp[hdr.e64.e_shstrndx].sh_size || lhp->sh_name >= sp[hdr.e64.e_shstrndx].sh_size) continue; /* corrupt sh_name field */ if (shp->sh_type == SHT_PROGBITS && strcmp(strs + shp->sh_name, _CTF_SECTION) == 0) { ctfsect.cts_name = strs + shp->sh_name; ctfsect.cts_type = shp->sh_type; ctfsect.cts_flags = shp->sh_flags; ctfsect.cts_size = shp->sh_size; ctfsect.cts_entsize = shp->sh_entsize; ctfsect.cts_offset = (off64_t)shp->sh_offset; } else if (shp->sh_type == SHT_SYMTAB) { symsect.cts_name = strs + shp->sh_name; symsect.cts_type = shp->sh_type; symsect.cts_flags = shp->sh_flags; symsect.cts_size = shp->sh_size; symsect.cts_entsize = shp->sh_entsize; symsect.cts_offset = (off64_t)shp->sh_offset; strsect.cts_name = strs + lhp->sh_name; strsect.cts_type = lhp->sh_type; strsect.cts_flags = lhp->sh_flags; strsect.cts_size = lhp->sh_size; strsect.cts_entsize = lhp->sh_entsize; strsect.cts_offset = (off64_t)lhp->sh_offset; } } free(sp); /* free section header array */ if (ctfsect.cts_type == SHT_NULL) { (void) munmap(strs_map, strs_mapsz); return (ctf_set_open_errno(errp, ECTF_NOCTFDATA)); } /* * Now mmap the CTF data, symtab, and strtab sections and * call ctf_bufopen() to do the rest of the work. */ if (ctf_sect_mmap(&ctfsect, fd) == MAP_FAILED) { (void) munmap(strs_map, strs_mapsz); return (ctf_set_open_errno(errp, ECTF_MMAP)); } if (symsect.cts_type != SHT_NULL && strsect.cts_type != SHT_NULL) { if (ctf_sect_mmap(&symsect, fd) == MAP_FAILED || ctf_sect_mmap(&strsect, fd) == MAP_FAILED) { (void) ctf_set_open_errno(errp, ECTF_MMAP); goto bad; /* unmap all and abort */ } fp = ctf_bufopen(&ctfsect, &symsect, &strsect, errp); } else fp = ctf_bufopen(&ctfsect, NULL, NULL, errp); bad: if (fp == NULL) { ctf_sect_munmap(&ctfsect); ctf_sect_munmap(&symsect); ctf_sect_munmap(&strsect); } else fp->ctf_flags |= LCTF_MMAP; (void) munmap(strs_map, strs_mapsz); return (fp); } return (ctf_set_open_errno(errp, ECTF_FMT)); }
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); }
/* * Dupliate a ctf_file_t and its underlying section information into a new * container. This works by copying the three ctf_sect_t's of the original * container if they exist and passing those into ctf_bufopen. To copy those, we * mmap anonymous memory with ctf_data_alloc and bcopy the data across. It's not * the cheapest thing, but it's what we've got. */ ctf_file_t * ctf_dup(ctf_file_t *ofp) { ctf_file_t *fp; ctf_sect_t ctfsect, symsect, strsect; ctf_sect_t *ctp, *symp, *strp; void *cbuf, *symbuf, *strbuf; int err; cbuf = symbuf = strbuf = NULL; /* * The ctfsect isn't allowed to not exist, but the symbol and string * section might not. We only need to copy the data of the section, not * the name, as ctf_bufopen will take care of that. */ bcopy(&ofp->ctf_data, &ctfsect, sizeof (ctf_sect_t)); cbuf = ctf_data_alloc(ctfsect.cts_size); if (cbuf == NULL) { (void) ctf_set_errno(ofp, ECTF_MMAP); return (NULL); } bcopy(ctfsect.cts_data, cbuf, ctfsect.cts_size); ctf_data_protect(cbuf, ctfsect.cts_size); ctfsect.cts_data = cbuf; ctfsect.cts_offset = 0; ctp = &ctfsect; if (ofp->ctf_symtab.cts_data != NULL) { bcopy(&ofp->ctf_symtab, &symsect, sizeof (ctf_sect_t)); symbuf = ctf_data_alloc(symsect.cts_size); if (symbuf == NULL) { (void) ctf_set_errno(ofp, ECTF_MMAP); goto err; } bcopy(symsect.cts_data, symbuf, symsect.cts_size); ctf_data_protect(symbuf, symsect.cts_size); symsect.cts_data = symbuf; symsect.cts_offset = 0; symp = &symsect; } else { symp = NULL; } if (ofp->ctf_strtab.cts_data != NULL) { bcopy(&ofp->ctf_strtab, &strsect, sizeof (ctf_sect_t)); strbuf = ctf_data_alloc(strsect.cts_size); if (strbuf == NULL) { (void) ctf_set_errno(ofp, ECTF_MMAP); goto err; } bcopy(strsect.cts_data, strbuf, strsect.cts_size); ctf_data_protect(strbuf, strsect.cts_size); strsect.cts_data = strbuf; strsect.cts_offset = 0; strp = &strsect; } else { strp = NULL; } fp = ctf_bufopen(ctp, symp, strp, &err); if (fp == NULL) { (void) ctf_set_errno(ofp, err); goto err; } fp->ctf_flags |= LCTF_MMAP; return (fp); err: ctf_data_free(cbuf, ctfsect.cts_size); if (symbuf != NULL) ctf_data_free(symbuf, symsect.cts_size); if (strbuf != NULL) ctf_data_free(strbuf, strsect.cts_size); return (NULL); }