diff options
Diffstat (limited to 'usr/src/common/ctf/ctf_create.c')
-rw-r--r-- | usr/src/common/ctf/ctf_create.c | 842 |
1 files changed, 748 insertions, 94 deletions
diff --git a/usr/src/common/ctf/ctf_create.c b/usr/src/common/ctf/ctf_create.c index 239d166f44..c0a4b15055 100644 --- a/usr/src/common/ctf/ctf_create.c +++ b/usr/src/common/ctf/ctf_create.c @@ -25,7 +25,7 @@ * Use is subject to license terms. */ /* - * Copyright (c) 2013, Joyent, Inc. All rights reserved. + * Copyright (c) 2015, Joyent, Inc. */ #include <sys/sysmacros.h> @@ -86,6 +86,48 @@ ctf_create(int *errp) return (fp); } +ctf_file_t * +ctf_fdcreate(int fd, int *errp) +{ + ctf_file_t *fp; + static const ctf_header_t hdr = { { CTF_MAGIC, CTF_VERSION, 0 } }; + + const ulong_t hashlen = 128; + ctf_dtdef_t **hash; + ctf_sect_t cts; + + if (fd == -1) + return (ctf_create(errp)); + + hash = ctf_alloc(hashlen * sizeof (ctf_dtdef_t *)); + + 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_fdcreate_int(fd, errp, &cts)) == 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) { @@ -236,14 +278,24 @@ int ctf_update(ctf_file_t *fp) { ctf_file_t ofp, *nfp; - ctf_header_t hdr; + ctf_header_t hdr, *bhdr; ctf_dtdef_t *dtd; - ctf_sect_t cts; + ctf_dsdef_t *dsd; + ctf_dldef_t *dld; + ctf_sect_t cts, *symp, *strp; uchar_t *s, *s0, *t; - size_t size; + ctf_lblent_t *label; + uint16_t *obj, *func; + size_t size, objsize, funcsize, labelsize, plen; void *buf; int err; + ulong_t i; + const char *plabel; + const char *sname; + + uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; + uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data; if (!(fp->ctf_flags & LCTF_RDWR)) return (ctf_set_errno(fp, ECTF_RDONLY)); @@ -261,8 +313,26 @@ ctf_update(ctf_file_t *fp) 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] */ + if (fp->ctf_flags & LCTF_CHILD) { + if (fp->ctf_parname == NULL) { + plen = 0; + hdr.cth_parname = 1; /* i.e. _CTF_STRTAB_TEMPLATE[1] */ + plabel = NULL; + } else { + plen = strlen(fp->ctf_parname) + 1; + plabel = ctf_label_topmost(fp->ctf_parent); + } + } else { + plabel = NULL; + plen = 0; + } + + /* + * Iterate over the labels that we have. + */ + for (labelsize = 0, dld = ctf_list_next(&fp->ctf_dldefs); + dld != NULL; dld = ctf_list_next(dld)) + labelsize += sizeof (ctf_lblent_t); /* * Iterate through the dynamic type definition list and compute the @@ -304,25 +374,121 @@ ctf_update(ctf_file_t *fp) } /* + * An entry for each object must exist in the data section. However, if + * the symbol is SHN_UNDEF, then it is skipped. For objects, the storage + * is just the size of the 2-byte id. For functions it's always 2 bytes, + * plus 2 bytes per argument and the return type. + */ + dsd = ctf_list_next(&fp->ctf_dsdefs); + for (objsize = 0, funcsize = 0, i = 0; i < fp->ctf_nsyms; i++) { + int type; + + if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { + const Elf32_Sym *symp = (Elf32_Sym *)symbase + i; + + type = ELF32_ST_TYPE(symp->st_info); + if (ctf_sym_valid(strbase, type, symp->st_shndx, + symp->st_value, symp->st_name) == B_FALSE) + continue; + } else { + const Elf64_Sym *symp = (Elf64_Sym *)symbase + i; + + type = ELF64_ST_TYPE(symp->st_info); + if (ctf_sym_valid(strbase, type, symp->st_shndx, + symp->st_value, symp->st_name) == B_FALSE) + continue; + } + + while (dsd != NULL && i > dsd->dsd_symidx) + dsd = ctf_list_next(dsd); + if (type == STT_OBJECT) { + objsize += sizeof (uint16_t); + } else { + /* Every function has a uint16_t info no matter what */ + if (dsd == NULL || i < dsd->dsd_symidx) { + funcsize += sizeof (uint16_t); + } else { + funcsize += sizeof (uint16_t) * + (dsd->dsd_nargs + 2); + } + } + } + + /* + * The objtoff and funcoffset must be 2-byte aligned. We're guaranteed + * that this is always true for the objtoff because labels are always 8 + * bytes large. Similarly, because objects are always two bytes of data, + * this will always be true for funcoff. + */ + hdr.cth_objtoff = hdr.cth_lbloff + labelsize; + hdr.cth_funcoff = hdr.cth_objtoff + objsize; + + /* + * The type offset must be 4 byte aligned. + */ + hdr.cth_typeoff = hdr.cth_funcoff + funcsize; + if (hdr.cth_typeoff & 3) + hdr.cth_typeoff += 4 - (hdr.cth_typeoff & 3); + ASSERT((hdr.cth_typeoff & 3) == 0); + + /* * 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; + hdr.cth_strlen = fp->ctf_dtstrlen + plen; size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen; + ctf_dprintf("lbloff: %u\nobjtoff: %u\nfuncoff: %u\n" + "typeoff: %u\nstroff: %u\nstrlen: %u\n", + hdr.cth_lbloff, hdr.cth_objtoff, hdr.cth_funcoff, + hdr.cth_typeoff, 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); + bhdr = buf; + label = (ctf_lblent_t *)((uintptr_t)buf + sizeof (ctf_header_t)); + t = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_typeoff; s = s0 = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_stroff; + obj = (uint16_t *)((uintptr_t)buf + sizeof (ctf_header_t) + + hdr.cth_objtoff); + func = (uint16_t *)((uintptr_t)buf + sizeof (ctf_header_t) + + hdr.cth_funcoff); bcopy(_CTF_STRTAB_TEMPLATE, s, sizeof (_CTF_STRTAB_TEMPLATE)); s += sizeof (_CTF_STRTAB_TEMPLATE); /* + * We have an actual parent name and we're a child container, therefore + * we should make sure to note our parent's name here. + */ + if (plen != 0) { + VERIFY(s + plen - s0 <= hdr.cth_strlen); + bcopy(fp->ctf_parname, s, plen); + bhdr->cth_parname = s - s0; + s += plen; + } + + /* + * First pass over the labels and copy them out. + */ + for (dld = ctf_list_next(&fp->ctf_dldefs); dld != NULL; + dld = ctf_list_next(dld), label++) { + size_t len = strlen(dld->dld_name) + 1; + + VERIFY(s + len - s0 <= hdr.cth_strlen); + bcopy(dld->dld_name, s, len); + label->ctl_typeidx = dld->dld_type; + label->ctl_label = s - s0; + s += len; + + if (plabel != NULL && strcmp(plabel, dld->dld_name) == 0) + bhdr->cth_parlabel = label->ctl_label; + } + + /* * We now take a final lap through the dynamic type definition list and * copy the appropriate type records and strings to the output buffer. */ @@ -339,6 +505,7 @@ ctf_update(ctf_file_t *fp) if (dtd->dtd_name != NULL) { dtd->dtd_data.ctt_name = (uint_t)(s - s0); len = strlen(dtd->dtd_name) + 1; + VERIFY(s + len - s0 <= hdr.cth_strlen); bcopy(dtd->dtd_name, s, len); s += len; } else @@ -411,6 +578,61 @@ ctf_update(ctf_file_t *fp) } /* + * Now we fill in our dynamic data and function sections. We use the + * same criteria as above, but also consult the dsd list. + */ + dsd = ctf_list_next(&fp->ctf_dsdefs); + for (i = 0; i < fp->ctf_nsyms; i++) { + int type; + if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { + const Elf32_Sym *symp = (Elf32_Sym *)symbase + i; + type = ELF32_ST_TYPE(symp->st_info); + + if (ctf_sym_valid(strbase, type, symp->st_shndx, + symp->st_value, symp->st_name) == B_FALSE) + continue; + } else { + const Elf64_Sym *symp = (Elf64_Sym *)symbase + i; + type = ELF64_ST_TYPE(symp->st_info); + if (ctf_sym_valid(strbase, type, symp->st_shndx, + symp->st_value, symp->st_name) == B_FALSE) + continue; + } + + while (dsd != NULL && i > dsd->dsd_symidx) { + dsd = ctf_list_next(dsd); + } + if (type == STT_OBJECT) { + if (dsd == NULL || i < dsd->dsd_symidx) { + *obj = 0; + } else { + *obj = dsd->dsd_tid; + } + obj++; + VERIFY((uintptr_t)obj <= (uintptr_t)func); + } else { + if (dsd == NULL || i < dsd->dsd_symidx) { + ushort_t data = CTF_TYPE_INFO(CTF_K_UNKNOWN, + 0, 0); + *func = data; + func++; + } else { + int j; + ushort_t data = CTF_TYPE_INFO(CTF_K_FUNCTION, 0, + dsd->dsd_nargs); + + *func = data; + func++; + *func = dsd->dsd_tid; + func++; + for (j = 0; j < dsd->dsd_nargs; j++) + func[j] = dsd->dsd_argc[j]; + func += dsd->dsd_nargs; + } + } + } + + /* * 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. */ @@ -423,7 +645,15 @@ ctf_update(ctf_file_t *fp) cts.cts_entsize = 1; cts.cts_offset = 0; - if ((nfp = ctf_bufopen(&cts, NULL, NULL, &err)) == NULL) { + if (fp->ctf_nsyms == 0) { + symp = NULL; + strp = NULL; + } else { + symp = &fp->ctf_symtab; + strp = &fp->ctf_strtab; + } + + if ((nfp = ctf_bufopen(&cts, symp, strp, &err)) == NULL) { ctf_data_free(buf, size); return (ctf_set_errno(fp, err)); } @@ -433,10 +663,11 @@ ctf_update(ctf_file_t *fp) 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_dsdefs = fp->ctf_dsdefs; + nfp->ctf_dldefs = fp->ctf_dldefs; nfp->ctf_dtstrlen = fp->ctf_dtstrlen; nfp->ctf_dtnextid = fp->ctf_dtnextid; nfp->ctf_dtoldid = fp->ctf_dtnextid - 1; @@ -445,6 +676,23 @@ ctf_update(ctf_file_t *fp) fp->ctf_dthash = NULL; fp->ctf_dthashlen = 0; bzero(&fp->ctf_dtdefs, sizeof (ctf_list_t)); + bzero(&fp->ctf_dsdefs, sizeof (ctf_list_t)); + bzero(&fp->ctf_dldefs, sizeof (ctf_list_t)); + + /* + * Because the various containers share the data sections, we don't want + * to have ctf_close free it all. However, the name of the section is in + * fact unique to the ctf_sect_t. Thus we save the names of the symbol + * and string sections around the bzero() and restore them afterwards, + * ensuring that we don't result in a memory leak. + */ + sname = fp->ctf_symtab.cts_name; + bzero(&fp->ctf_symtab, sizeof (ctf_sect_t)); + fp->ctf_symtab.cts_name = sname; + + sname = fp->ctf_strtab.cts_name; + bzero(&fp->ctf_strtab, sizeof (ctf_sect_t)); + fp->ctf_strtab.cts_name = sname; bcopy(fp, &ofp, sizeof (ctf_file_t)); bcopy(nfp, fp, sizeof (ctf_file_t)); @@ -563,6 +811,101 @@ ctf_dtd_lookup(ctf_file_t *fp, ctf_id_t type) return (dtd); } +ctf_dsdef_t * +ctf_dsd_lookup(ctf_file_t *fp, ulong_t idx) +{ + ctf_dsdef_t *dsd; + + for (dsd = ctf_list_next(&fp->ctf_dsdefs); dsd != NULL; + dsd = ctf_list_next(dsd)) { + if (dsd->dsd_symidx == idx) + return (dsd); + } + + return (NULL); +} + +/* + * We order the ctf_dsdef_t by symbol index to make things better for updates. + */ +void +ctf_dsd_insert(ctf_file_t *fp, ctf_dsdef_t *dsd) +{ + ctf_dsdef_t *i; + + for (i = ctf_list_next(&fp->ctf_dsdefs); i != NULL; + i = ctf_list_next(i)) { + if (i->dsd_symidx > dsd->dsd_symidx) + break; + } + + if (i == NULL) { + ctf_list_append(&fp->ctf_dsdefs, dsd); + return; + } + + ctf_list_insert_before(&fp->ctf_dsdefs, i, dsd); +} + +/* ARGSUSED */ +void +ctf_dsd_delete(ctf_file_t *fp, ctf_dsdef_t *dsd) +{ + if (dsd->dsd_nargs > 0) + ctf_free(dsd->dsd_argc, + sizeof (ctf_id_t) * dsd->dsd_nargs); + ctf_list_delete(&fp->ctf_dsdefs, dsd); + ctf_free(dsd, sizeof (ctf_dsdef_t)); +} + +ctf_dldef_t * +ctf_dld_lookup(ctf_file_t *fp, const char *name) +{ + ctf_dldef_t *dld; + + for (dld = ctf_list_next(&fp->ctf_dldefs); dld != NULL; + dld = ctf_list_next(dld)) { + if (strcmp(name, dld->dld_name) == 0) + return (dld); + } + + return (NULL); +} + +void +ctf_dld_insert(ctf_file_t *fp, ctf_dldef_t *dld, uint_t pos) +{ + ctf_dldef_t *l; + + if (pos == 0) { + ctf_list_prepend(&fp->ctf_dldefs, dld); + return; + } + + for (l = ctf_list_next(&fp->ctf_dldefs); pos != 0 && dld != NULL; + l = ctf_list_next(l), pos--) + ; + + if (l == NULL) + ctf_list_append(&fp->ctf_dldefs, dld); + else + ctf_list_insert_before(&fp->ctf_dsdefs, l, dld); +} + +void +ctf_dld_delete(ctf_file_t *fp, ctf_dldef_t *dld) +{ + ctf_list_delete(&fp->ctf_dldefs, dld); + + if (dld->dld_name != NULL) { + size_t len = strlen(dld->dld_name) + 1; + ctf_free(dld->dld_name, len); + fp->ctf_dtstrlen -= len; + } + + ctf_free(dld, sizeof (ctf_dldef_t)); +} + /* * 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 @@ -583,10 +926,10 @@ ctf_discard(ctf_file_t *fp) return (0); /* no update required */ for (dtd = ctf_list_prev(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) { + ntd = ctf_list_prev(dtd); if (dtd->dtd_type <= fp->ctf_dtoldid) continue; /* skip types that have been committed */ - ntd = ctf_list_prev(dtd); ctf_dtd_delete(fp, dtd); } @@ -637,26 +980,7 @@ ctf_add_generic(ctf_file_t *fp, uint_t flag, const char *name, ctf_dtdef_t **rp) 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_id_t ctf_add_encoded(ctf_file_t *fp, uint_t flag, const char *name, const ctf_encoding_t *ep, uint_t kind) { @@ -670,14 +994,22 @@ ctf_add_encoded(ctf_file_t *fp, uint_t flag, 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); + + /* + * If the type's size is not an even number of bytes, then we should + * round up the type size to the nearest byte. + */ + dtd->dtd_data.ctt_size = ep->cte_bits / NBBY; + if ((ep->cte_bits % NBBY) != 0) + dtd->dtd_data.ctt_size++; 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_id_t +ctf_add_reftype(ctf_file_t *fp, uint_t flag, + const char *name, ctf_id_t ref, uint_t kind) { ctf_dtdef_t *dtd; ctf_id_t type; @@ -685,7 +1017,7 @@ ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind) 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) + if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ ctf_ref_inc(fp, ref); @@ -711,9 +1043,9 @@ ctf_add_float(ctf_file_t *fp, uint_t flag, } ctf_id_t -ctf_add_pointer(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +ctf_add_pointer(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref) { - return (ctf_add_reftype(fp, flag, ref, CTF_K_POINTER)); + return (ctf_add_reftype(fp, flag, name, ref, CTF_K_POINTER)); } ctf_id_t @@ -728,13 +1060,18 @@ ctf_add_array(ctf_file_t *fp, uint_t flag, const ctf_arinfo_t *arp) fpd = fp; if (ctf_lookup_by_id(&fpd, arp->ctr_contents) == NULL && - ctf_dtd_lookup(fp, arp->ctr_contents) == NULL) + ctf_dtd_lookup(fp, arp->ctr_contents) == NULL) { + ctf_dprintf("bad contents for array: %ld\n", + arp->ctr_contents); return (ctf_set_errno(fp, ECTF_BADID)); + } fpd = fp; if (ctf_lookup_by_id(&fpd, arp->ctr_index) == NULL && - ctf_dtd_lookup(fp, arp->ctr_index) == NULL) + ctf_dtd_lookup(fp, arp->ctr_index) == NULL) { + ctf_dprintf("bad index for array: %ld\n", arp->ctr_index); return (ctf_set_errno(fp, ECTF_BADID)); + } if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ @@ -781,7 +1118,7 @@ ctf_set_array(ctf_file_t *fp, ctf_id_t type, const ctf_arinfo_t *arp) } ctf_id_t -ctf_add_function(ctf_file_t *fp, uint_t flag, +ctf_add_funcptr(ctf_file_t *fp, uint_t flag, const ctf_funcinfo_t *ctc, const ctf_id_t *argv) { ctf_dtdef_t *dtd; @@ -842,20 +1179,34 @@ 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; + ctf_dtdef_t *dtd = NULL; + ctf_id_t type = CTF_ERR; 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 */ + if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD) { + type = hep->h_type; + dtd = ctf_dtd_lookup(fp, type); + if (CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_FORWARD) + dtd = NULL; + } + if (dtd == NULL) { + type = ctf_add_generic(fp, flag, name, &dtd); + if (type == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + } + + VERIFY(type != CTF_ERR); dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, flag, 0); dtd->dtd_data.ctt_size = 0; + /* + * Always dirty in case we modified a forward. + */ + fp->ctf_flags |= LCTF_DIRTY; + return (type); } @@ -864,20 +1215,34 @@ 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; + ctf_dtdef_t *dtd = NULL; + ctf_id_t type = CTF_ERR; 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 */ + if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD) { + type = hep->h_type; + dtd = ctf_dtd_lookup(fp, type); + if (CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_FORWARD) + dtd = NULL; + } + if (dtd == NULL) { + type = ctf_add_generic(fp, flag, name, &dtd); + if (type == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + } + + VERIFY(type != CTF_ERR); dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, flag, 0); dtd->dtd_data.ctt_size = 0; + /* + * Always dirty in case we modified a forward. + */ + fp->ctf_flags |= LCTF_DIRTY; + return (type); } @@ -886,20 +1251,34 @@ 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; + ctf_dtdef_t *dtd = NULL; + ctf_id_t type = CTF_ERR; 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 */ + if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD) { + type = hep->h_type; + dtd = ctf_dtd_lookup(fp, type); + if (CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_FORWARD) + dtd = NULL; + } + if (dtd == NULL) { + type = ctf_add_generic(fp, flag, name, &dtd); + if (type == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + } + + VERIFY(type != CTF_ERR); dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, flag, 0); dtd->dtd_data.ctt_size = fp->ctf_dmodel->ctd_int; + /* + * Always dirty in case we modified a forward. + */ + fp->ctf_flags |= LCTF_DIRTY; + return (type); } @@ -965,21 +1344,21 @@ ctf_add_typedef(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref) } ctf_id_t -ctf_add_volatile(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +ctf_add_volatile(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref) { - return (ctf_add_reftype(fp, flag, ref, CTF_K_VOLATILE)); + return (ctf_add_reftype(fp, flag, name, ref, CTF_K_VOLATILE)); } ctf_id_t -ctf_add_const(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +ctf_add_const(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref) { - return (ctf_add_reftype(fp, flag, ref, CTF_K_CONST)); + return (ctf_add_reftype(fp, flag, name, ref, CTF_K_CONST)); } ctf_id_t -ctf_add_restrict(ctf_file_t *fp, uint_t flag, ctf_id_t ref) +ctf_add_restrict(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref) { - return (ctf_add_reftype(fp, flag, ref, CTF_K_RESTRICT)); + return (ctf_add_reftype(fp, flag, name, ref, CTF_K_RESTRICT)); } int @@ -1012,8 +1391,10 @@ ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value) for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members); dmd != NULL; dmd = ctf_list_next(dmd)) { - if (strcmp(dmd->dmd_name, name) == 0) + if (strcmp(dmd->dmd_name, name) == 0) { + ctf_dprintf("encountered duplicate member %s\n", name); return (ctf_set_errno(fp, ECTF_DUPMEMBER)); + } } if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL) @@ -1039,13 +1420,16 @@ ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value) } int -ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type) +ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type, + ulong_t offset) { ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, souid); ctf_dmdef_t *dmd; + ulong_t mbitsz; ssize_t msize, malign, ssize; uint_t kind, vlen, root; + int mkind; char *s = NULL; if (!(fp->ctf_flags & LCTF_RDWR)) @@ -1064,19 +1448,58 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type) if (vlen == CTF_MAX_VLEN) return (ctf_set_errno(fp, ECTF_DTFULL)); - if (name != NULL) { + /* + * Structures may have members which are anonymous. If they have two of + * these, then the duplicate member detection would find it due to the + * string of "", so we skip it. + */ + if (name != NULL && *name != '\0') { 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) + 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) + (malign = ctf_type_align(fp, type)) == CTF_ERR || + (mkind = ctf_type_kind(fp, type)) == CTF_ERR) return (CTF_ERR); /* errno is set for us */ + /* + * ctf_type_size returns sizes in bytes. However, for bitfields, that + * means that it may misrepresent and actually rounds it up to a power + * of two and store that in bytes. So instead we have to get the + * Integers encoding and rely on that. + */ + if (mkind == CTF_K_INTEGER) { + ctf_encoding_t e; + + if (ctf_type_encoding(fp, type, &e) == CTF_ERR) + return (CTF_ERR); /* errno is set for us */ + mbitsz = e.cte_bits; + } else if (mkind == CTF_K_FORWARD) { + /* + * This is a rather rare case. In general one cannot add a + * forward to a structure. However, the CTF tools traditionally + * tried to add a forward to the struct cpu as the last member. + * Therefore, if we find one here, we're going to verify the + * size and make sure it's zero. It's certainly odd, but that's + * life. + * + * Further, if it's not an absolute position being specified, + * then we refuse to add it. + */ + if (offset == ULONG_MAX) + return (ctf_set_errno(fp, EINVAL)); + VERIFY(msize == 0); + mbitsz = msize; + } else { + mbitsz = msize * 8; + } + if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL) return (ctf_set_errno(fp, EAGAIN)); @@ -1092,29 +1515,36 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type) 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; + size_t off; + + if (offset == ULONG_MAX) { + ctf_encoding_t linfo; + ssize_t lsize; + + off = lmd->dmd_offset; + 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 = offset; + ssize = (offset + mbitsz) / NBBY; + } } else { dmd->dmd_offset = 0; ssize = ctf_get_ctt_size(fp, &dtd->dtd_data, NULL, NULL); @@ -1380,7 +1810,7 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t 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); + dst_type = ctf_add_reftype(dst_fp, flag, NULL, src_type, kind); break; case CTF_K_ARRAY: @@ -1415,7 +1845,7 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type) if (ctc.ctc_return == CTF_ERR) return (CTF_ERR); /* errno is set for us */ - dst_type = ctf_add_function(dst_fp, flag, &ctc, NULL); + dst_type = ctf_add_funcptr(dst_fp, flag, &ctc, NULL); break; case CTF_K_STRUCT: @@ -1540,3 +1970,227 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type) return (dst_type); } + +int +ctf_add_function(ctf_file_t *fp, ulong_t idx, const ctf_funcinfo_t *fip, + const ctf_id_t *argc) +{ + int i; + ctf_dsdef_t *dsd; + ctf_file_t *afp; + uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + if (ctf_dsd_lookup(fp, idx) != NULL) + return (ctf_set_errno(fp, ECTF_CONFLICT)); + + if (symbase == NULL) + return (ctf_set_errno(fp, ECTF_STRTAB)); + + if (idx > fp->ctf_nsyms) + return (ctf_set_errno(fp, ECTF_NOTDATA)); + + if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { + const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx; + if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC) + return (ctf_set_errno(fp, ECTF_NOTFUNC)); + } else { + const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx; + if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC) + return (ctf_set_errno(fp, ECTF_NOTFUNC)); + } + + afp = fp; + if (ctf_lookup_by_id(&afp, fip->ctc_return) == NULL) + return (CTF_ERR); /* errno is set for us */ + + for (i = 0; i < fip->ctc_argc; i++) { + afp = fp; + if (ctf_lookup_by_id(&afp, argc[i]) == NULL) + return (CTF_ERR); /* errno is set for us */ + } + + dsd = ctf_alloc(sizeof (ctf_dsdef_t)); + if (dsd == NULL) + return (ctf_set_errno(fp, ENOMEM)); + dsd->dsd_nargs = fip->ctc_argc; + if (fip->ctc_flags & CTF_FUNC_VARARG) + dsd->dsd_nargs++; + if (dsd->dsd_nargs != 0) { + dsd->dsd_argc = ctf_alloc(sizeof (ctf_id_t) * dsd->dsd_nargs); + if (dsd->dsd_argc == NULL) { + ctf_free(dsd, sizeof (ctf_dsdef_t)); + return (ctf_set_errno(fp, ENOMEM)); + } + bcopy(argc, dsd->dsd_argc, sizeof (ctf_id_t) * fip->ctc_argc); + if (fip->ctc_flags & CTF_FUNC_VARARG) + dsd->dsd_argc[fip->ctc_argc] = 0; + } + dsd->dsd_symidx = idx; + dsd->dsd_tid = fip->ctc_return; + + ctf_dsd_insert(fp, dsd); + fp->ctf_flags |= LCTF_DIRTY; + + return (0); +} + +int +ctf_add_object(ctf_file_t *fp, ulong_t idx, ctf_id_t type) +{ + ctf_dsdef_t *dsd; + ctf_file_t *afp; + uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data; + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + if (ctf_dsd_lookup(fp, idx) != NULL) + return (ctf_set_errno(fp, ECTF_CONFLICT)); + + if (symbase == NULL) + return (ctf_set_errno(fp, ECTF_STRTAB)); + + if (idx > fp->ctf_nsyms) + return (ctf_set_errno(fp, ECTF_NOTDATA)); + + if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) { + const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx; + if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT) + return (ctf_set_errno(fp, ECTF_NOTDATA)); + } else { + const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx; + if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT) + return (ctf_set_errno(fp, ECTF_NOTDATA)); + } + + afp = fp; + if (ctf_lookup_by_id(&afp, type) == NULL) + return (CTF_ERR); /* errno is set for us */ + + dsd = ctf_alloc(sizeof (ctf_dsdef_t)); + if (dsd == NULL) + return (ctf_set_errno(fp, ENOMEM)); + dsd->dsd_symidx = idx; + dsd->dsd_tid = type; + dsd->dsd_argc = NULL; + + ctf_dsd_insert(fp, dsd); + fp->ctf_flags |= LCTF_DIRTY; + + return (0); +} + +void +ctf_dataptr(ctf_file_t *fp, const void **addrp, size_t *sizep) +{ + if (addrp != NULL) + *addrp = fp->ctf_base; + if (sizep != NULL) + *sizep = fp->ctf_size; +} + +int +ctf_add_label(ctf_file_t *fp, const char *name, ctf_id_t type, uint_t position) +{ + ctf_file_t *fpd; + ctf_dldef_t *dld; + + if (name == NULL) + return (ctf_set_errno(fp, EINVAL)); + + if (!(fp->ctf_flags & LCTF_RDWR)) + return (ctf_set_errno(fp, ECTF_RDONLY)); + + fpd = fp; + if (type != 0 && ctf_lookup_by_id(&fpd, type) == NULL) + return (CTF_ERR); /* errno is set for us */ + + if (type != 0 && (fp->ctf_flags & LCTF_CHILD) && + CTF_TYPE_ISPARENT(type)) + return (ctf_set_errno(fp, ECTF_NOPARENT)); + + if (ctf_dld_lookup(fp, name) != NULL) + return (ctf_set_errno(fp, ECTF_LABELEXISTS)); + + if ((dld = ctf_alloc(sizeof (ctf_dldef_t))) == NULL) + return (ctf_set_errno(fp, EAGAIN)); + + if ((dld->dld_name = ctf_strdup(name)) == NULL) { + ctf_free(dld, sizeof (ctf_dldef_t)); + return (ctf_set_errno(fp, EAGAIN)); + } + + dld->dld_type = type; + fp->ctf_dtstrlen += strlen(name) + 1; + ctf_dld_insert(fp, dld, position); + fp->ctf_flags |= LCTF_DIRTY; + + return (0); +} + +/* + * Update the size of a structure or union. Note that we don't allow this to + * shrink the size of a struct or union, only to increase it. This is useful for + * cases when you have a structure whose actual size is larger than the sum of + * its members due to padding for natural alignment. + */ +int +ctf_set_size(ctf_file_t *fp, ctf_id_t id, const ulong_t newsz) +{ + ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, id); + uint_t kind; + size_t oldsz; + + 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); + + if (kind != CTF_K_STRUCT && kind != CTF_K_UNION) + return (ctf_set_errno(fp, ECTF_NOTSOU)); + + if ((oldsz = dtd->dtd_data.ctt_size) == CTF_LSIZE_SENT) + oldsz = CTF_TYPE_LSIZE(&dtd->dtd_data); + + if (newsz < oldsz) + return (ctf_set_errno(fp, EINVAL)); + + if (newsz > CTF_MAX_SIZE) { + dtd->dtd_data.ctt_size = CTF_LSIZE_SENT; + dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(newsz); + dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(newsz); + } else { + dtd->dtd_data.ctt_size = (ushort_t)newsz; + } + + fp->ctf_flags |= LCTF_DIRTY; + return (0); +} + +int +ctf_set_root(ctf_file_t *fp, ctf_id_t id, const boolean_t vis) +{ + ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, id); + uint_t kind, vlen; + + 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); + vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info); + + dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, vis, vlen); + return (0); +} |