/* * Write the uncompressed CTF data stream to the specified file descriptor. * This is useful for saving the results of dynamic CTF containers. */ int ctf_write(ctf_file_t *fp, int fd) { const uchar_t *buf = fp->ctf_base; ssize_t resid = fp->ctf_size; ssize_t len; while (resid != 0) { if ((len = write(fd, buf, resid)) <= 0) return (ctf_set_errno(fp, errno)); resid -= len; buf += len; } return (0); }
/* * 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] == -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); }
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); }
/* * 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); }
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); }
/* * 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); }
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); }
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) 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); }
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 = clp2(P2ROUNDUP(ep->cte_bits, NBBY) / NBBY); dtd->dtd_u.dtu_enc = *ep; return (type); }
/* * 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)); } }
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; ctf_file_t *fpd; fpd = fp; if (ref == CTF_ERR || (ctf_lookup_by_id(&fpd, ref) == NULL && ctf_dtd_lookup(fp, ref) == 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(CTF_K_TYPEDEF, flag, 0); dtd->dtd_data.ctt_type = (ushort_t)ref; ctf_ref_inc(fp, ref); 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); }
/* * 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); }
/* * 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. Note that to work properly * with our reference counting schemes, we must delete the dynamic list in * reverse. */ int ctf_discard(ctf_file_t *fp) { ctf_dtdef_t *dtd, *ntd; 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_prev(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { ntd = ctf_list_prev(dtd); if (CTF_TYPE_TO_INDEX(dtd->dtd_type) <= fp->ctf_dtoldid) continue; /* skip types that have been committed */ ctf_dtd_delete(fp, dtd); } fp->ctf_dtnextid = fp->ctf_dtoldid + 1; fp->ctf_flags &= ~LCTF_DIRTY; return (0); }
/* * 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); }
static int ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags) { GElf_Ehdr sehdr, dehdr; Elf_Scn *sscn, *dscn; Elf_Data *sdata, *ddata; GElf_Shdr shdr; int symtab_idx = -1; off_t new_offset = 0; off_t ctfnameoff = 0; int compress = (flags & CTF_ELFWRITE_F_COMPRESS); int *secxlate = NULL; int srcidx, dstidx, pad, i; int curnmoff = 0; int changing = 0; int ret; size_t nshdr, nphdr, strndx; void *strdatabuf = NULL, *symdatabuf = NULL; size_t strdatasz = 0, symdatasz = 0; void *cdata = NULL; size_t elfsize, asize; if ((flags & ~(CTF_ELFWRITE_F_COMPRESS)) != 0) { ret = ctf_set_errno(fp, EINVAL); goto out; } if (gelf_newehdr(dst, gelf_getclass(src)) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (gelf_getehdr(src, &sehdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } (void) memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr)); if (gelf_update_ehdr(dst, &dehdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* * Use libelf to get the number of sections and the string section to * deal with ELF files that may have a large number of sections. We just * always use this to make our live easier. */ if (elf_getphdrnum(src, &nphdr) != 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (elf_getshdrnum(src, &nshdr) != 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (elf_getshdrstrndx(src, &strndx) != 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* * Neither the existing debug sections nor the SUNW_ctf sections (new or * existing) are SHF_ALLOC'd, so they won't be in areas referenced by * program headers. As such, we can just blindly copy the program * headers from the existing file to the new file. */ if (nphdr != 0) { (void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT); if (gelf_newphdr(dst, nphdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } for (i = 0; i < nphdr; i++) { GElf_Phdr phdr; if (gelf_getphdr(src, i, &phdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (gelf_update_phdr(dst, i, &phdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } } } secxlate = ctf_alloc(sizeof (int) * nshdr); for (srcidx = dstidx = 0; srcidx < nshdr; srcidx++) { Elf_Scn *scn = elf_getscn(src, srcidx); GElf_Shdr shdr; char *sname; if (gelf_getshdr(scn, &shdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } sname = elf_strptr(src, strndx, shdr.sh_name); if (sname == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) { secxlate[srcidx] = -1; } else { secxlate[srcidx] = dstidx++; curnmoff += strlen(sname) + 1; } new_offset = (off_t)dehdr.e_phoff; } for (srcidx = 1; srcidx < nshdr; srcidx++) { char *sname; sscn = elf_getscn(src, srcidx); if (gelf_getshdr(sscn, &shdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (secxlate[srcidx] == -1) { changing = 1; continue; } dscn = elf_newscn(dst); if (dscn == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* * If this file has program headers, we need to explicitly lay * out sections. If none of the sections prior to this one have * been removed, then we can just use the existing location. If * one or more sections have been changed, then we need to * adjust this one to avoid holes. */ if (changing && nphdr != 0) { pad = new_offset % shdr.sh_addralign; if (pad != 0) new_offset += shdr.sh_addralign - pad; shdr.sh_offset = new_offset; } shdr.sh_link = secxlate[shdr.sh_link]; if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA) shdr.sh_info = secxlate[shdr.sh_info]; sname = elf_strptr(src, strndx, shdr.sh_name); if (sname == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if ((sdata = elf_getdata(sscn, NULL)) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if ((ddata = elf_newdata(dscn)) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } bcopy(sdata, ddata, sizeof (Elf_Data)); if (srcidx == strndx) { char seclen = strlen(CTF_ELF_SCN_NAME); strdatasz = ddata->d_size + shdr.sh_size + seclen + 1; ddata->d_buf = strdatabuf = ctf_alloc(strdatasz); if (ddata->d_buf == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); (void) strcpy((caddr_t)ddata->d_buf + shdr.sh_size, CTF_ELF_SCN_NAME); ctfnameoff = (off_t)shdr.sh_size; shdr.sh_size += seclen + 1; ddata->d_size += seclen + 1; if (nphdr != 0) changing = 1; } if (shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) { int nsym = shdr.sh_size / shdr.sh_entsize; symtab_idx = secxlate[srcidx]; symdatasz = shdr.sh_size; ddata->d_buf = symdatabuf = ctf_alloc(symdatasz); if (ddata->d_buf == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } (void) bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); for (i = 0; i < nsym; i++) { GElf_Sym sym; short newscn; (void) gelf_getsym(ddata, i, &sym); if (sym.st_shndx >= SHN_LORESERVE) continue; if ((newscn = secxlate[sym.st_shndx]) != sym.st_shndx) { sym.st_shndx = (newscn == -1 ? 1 : newscn); if (gelf_update_sym(ddata, i, &sym) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } } } } if (gelf_update_shdr(dscn, &shdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } new_offset = (off_t)shdr.sh_offset; if (shdr.sh_type != SHT_NOBITS) new_offset += shdr.sh_size; } if (symtab_idx == -1) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* Add the ctf section */ if ((dscn = elf_newscn(dst)) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (gelf_getshdr(dscn, &shdr) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } shdr.sh_name = ctfnameoff; shdr.sh_type = SHT_PROGBITS; shdr.sh_size = fp->ctf_size; shdr.sh_link = symtab_idx; shdr.sh_addralign = 4; if (changing && nphdr != 0) { pad = new_offset % shdr.sh_addralign; if (pad) new_offset += shdr.sh_addralign - pad; shdr.sh_offset = new_offset; new_offset += shdr.sh_size; } if ((ddata = elf_newdata(dscn)) == NULL) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (compress != 0) { int err; if (ctf_zopen(&err) == NULL) { ret = ctf_set_errno(fp, err); goto out; } if ((err = ctf_compress(fp, &cdata, &asize, &elfsize)) != 0) { ret = ctf_set_errno(fp, err); goto out; } ddata->d_buf = cdata; ddata->d_size = elfsize; } else { ddata->d_buf = (void *)fp->ctf_base; ddata->d_size = fp->ctf_size; } ddata->d_align = shdr.sh_addralign; if (gelf_update_shdr(dscn, &shdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } /* update the section header location */ if (nphdr != 0) { size_t align = gelf_fsize(dst, ELF_T_ADDR, 1, EV_CURRENT); size_t r = new_offset % align; if (r) new_offset += align - r; dehdr.e_shoff = new_offset; } /* commit to disk */ if (sehdr.e_shstrndx == SHN_XINDEX) dehdr.e_shstrndx = SHN_XINDEX; else dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx]; if (gelf_update_ehdr(dst, &dehdr) == 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } if (elf_update(dst, ELF_C_WRITE) < 0) { ret = ctf_set_errno(fp, ECTF_ELF); goto out; } ret = 0; out: if (strdatabuf != NULL) ctf_free(strdatabuf, strdatasz); if (symdatabuf != NULL) ctf_free(symdatabuf, symdatasz); if (cdata != NULL) ctf_data_free(cdata, fp->ctf_size); if (secxlate != NULL) ctf_free(secxlate, sizeof (int) * nshdr); return (ret); }
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; ctf_ref_inc(fp, type); fp->ctf_flags |= LCTF_DIRTY; return (0); }
/* * 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); }
/* * 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 == src_fp) return (src_type); 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) { if (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)); else dst_type = CTF_ERR; } /* * 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. * * In the case of integer and floating point types, we match using the * type encoding as well - else we may incorrectly return a bitfield * type, for instance. */ if (dst_type == CTF_ERR && name[0] != '\0') { for (dtd = ctf_list_prev(&dst_fp->ctf_dtdefs); dtd != NULL && CTF_TYPE_TO_INDEX(dtd->dtd_type) > dst_fp->ctf_dtoldid; dtd = ctf_list_prev(dtd)) { if (CTF_INFO_KIND(dtd->dtd_data.ctt_info) != kind || dtd->dtd_name == NULL || strcmp(dtd->dtd_name, name) != 0) continue; if (kind == CTF_K_INTEGER || kind == CTF_K_FLOAT) { if (ctf_type_encoding(src_fp, src_type, &src_en) != 0) continue; if (bcmp(&src_en, &dtd->dtd_u.dtu_enc, sizeof (ctf_encoding_t)) != 0) continue; } 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 */ /* * Now that we know that we can't fail, we go through and bump * all the reference counts on the member types. */ for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members); dmd != NULL; dmd = ctf_list_next(dmd)) ctf_ref_inc(dst_fp, dmd->dmd_type); 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); }
/* * 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); }
/* * 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((unsigned char)*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' || strncmp(p, lp->ctl_prefix, (size_t)(q - p)) == 0) { for (p += lp->ctl_len; isspace((unsigned char)*p); p++) continue; /* skip prefix and next ws */ if ((q = strchr(p, '*')) == NULL) q = end; /* compare until end */ while (isspace((unsigned char)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); }