Index: vendor/illumos/dist/tools/ctf/cvt/ctf.c =================================================================== --- vendor/illumos/dist/tools/ctf/cvt/ctf.c (revision 279821) +++ vendor/illumos/dist/tools/ctf/cvt/ctf.c (revision 279822) @@ -1,1282 +1,1282 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Create and parse buffers containing CTF data. */ #include #include #include #include #include #include #include #include "ctf_headers.h" #include "ctftools.h" #include "strtab.h" #include "memory.h" /* * Name of the file currently being read, used to print error messages. We * assume that only one file will be read at a time, and thus make no attempt * to allow curfile to be used simultaneously by multiple threads. * * The value is only valid during a call to ctf_load. */ char *curfile; #define CTF_BUF_CHUNK_SIZE (64 * 1024) #define RES_BUF_CHUNK_SIZE (64 * 1024) struct ctf_buf { strtab_t ctb_strtab; /* string table */ caddr_t ctb_base; /* pointer to base of buffer */ caddr_t ctb_end; /* pointer to end of buffer */ caddr_t ctb_ptr; /* pointer to empty buffer space */ size_t ctb_size; /* size of buffer */ int nptent; /* number of processed types */ int ntholes; /* number of type holes */ }; /*PRINTFLIKE1*/ static void parseterminate(char *fmt, ...) { static char msgbuf[1024]; /* sigh */ va_list ap; va_start(ap, fmt); vsnprintf(msgbuf, sizeof (msgbuf), fmt, ap); va_end(ap); terminate("%s: %s\n", curfile, msgbuf); } void ctf_buf_grow(ctf_buf_t *b) { off_t ptroff = b->ctb_ptr - b->ctb_base; b->ctb_size += CTF_BUF_CHUNK_SIZE; b->ctb_base = xrealloc(b->ctb_base, b->ctb_size); b->ctb_end = b->ctb_base + b->ctb_size; b->ctb_ptr = b->ctb_base + ptroff; } ctf_buf_t * ctf_buf_new(void) { ctf_buf_t *b = xcalloc(sizeof (ctf_buf_t)); strtab_create(&b->ctb_strtab); ctf_buf_grow(b); return (b); } void ctf_buf_free(ctf_buf_t *b) { strtab_destroy(&b->ctb_strtab); free(b->ctb_base); free(b); } uint_t ctf_buf_cur(ctf_buf_t *b) { return (b->ctb_ptr - b->ctb_base); } void ctf_buf_write(ctf_buf_t *b, const void *p, size_t n) { size_t len; while (n != 0) { if (b->ctb_ptr == b->ctb_end) ctf_buf_grow(b); len = MIN((size_t)(b->ctb_end - b->ctb_ptr), n); bcopy(p, b->ctb_ptr, len); b->ctb_ptr += len; p = (char *)p + len; n -= len; } } static int write_label(labelent_t *le, ctf_buf_t *b) { ctf_lblent_t ctl; ctl.ctl_label = strtab_insert(&b->ctb_strtab, le->le_name); ctl.ctl_typeidx = le->le_idx; ctf_buf_write(b, &ctl, sizeof (ctl)); return (1); } static void write_objects(iidesc_t *idp, ctf_buf_t *b) { ushort_t id = (idp ? idp->ii_dtype->t_id : 0); ctf_buf_write(b, &id, sizeof (id)); debug(3, "Wrote object %s (%d)\n", (idp ? idp->ii_name : "(null)"), id); } static void write_functions(iidesc_t *idp, ctf_buf_t *b) { ushort_t fdata[2]; ushort_t id; int nargs; int i; if (!idp) { fdata[0] = 0; ctf_buf_write(b, &fdata[0], sizeof (fdata[0])); debug(3, "Wrote function (null)\n"); return; } nargs = idp->ii_nargs + (idp->ii_vargs != 0); if (nargs > CTF_MAX_VLEN) { terminate("function %s has too many args: %d > %d\n", idp->ii_name, nargs, CTF_MAX_VLEN); } fdata[0] = CTF_TYPE_INFO(CTF_K_FUNCTION, 1, nargs); fdata[1] = idp->ii_dtype->t_id; ctf_buf_write(b, fdata, sizeof (fdata)); for (i = 0; i < idp->ii_nargs; i++) { id = idp->ii_args[i]->t_id; ctf_buf_write(b, &id, sizeof (id)); } if (idp->ii_vargs) { id = 0; ctf_buf_write(b, &id, sizeof (id)); } debug(3, "Wrote function %s (%d args)\n", idp->ii_name, nargs); } /* * Depending on the size of the type being described, either a ctf_stype_t (for * types with size < CTF_LSTRUCT_THRESH) or a ctf_type_t (all others) will be * written. We isolate the determination here so the rest of the writer code * doesn't need to care. */ static void write_sized_type_rec(ctf_buf_t *b, ctf_type_t *ctt, size_t size) { if (size > CTF_MAX_SIZE) { ctt->ctt_size = CTF_LSIZE_SENT; ctt->ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size); ctt->ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size); ctf_buf_write(b, ctt, sizeof (*ctt)); } else { ctf_stype_t *cts = (ctf_stype_t *)ctt; cts->ctt_size = (ushort_t)size; ctf_buf_write(b, cts, sizeof (*cts)); } } static void write_unsized_type_rec(ctf_buf_t *b, ctf_type_t *ctt) { ctf_stype_t *cts = (ctf_stype_t *)ctt; ctf_buf_write(b, cts, sizeof (*cts)); } static int write_type(tdesc_t *tp, ctf_buf_t *b) { elist_t *ep; mlist_t *mp; intr_t *ip; size_t offset; uint_t encoding; uint_t data; int isroot = tp->t_flags & TDESC_F_ISROOT; int i; ctf_type_t ctt; ctf_array_t cta; ctf_member_t ctm; ctf_lmember_t ctlm; ctf_enum_t cte; ushort_t id; ctlm.ctlm_pad = 0; /* * There shouldn't be any holes in the type list (where a hole is * defined as two consecutive tdescs without consecutive ids), but * check for them just in case. If we do find holes, we need to make * fake entries to fill the holes, or we won't be able to reconstruct * the tree from the written data. */ if (++b->nptent < CTF_TYPE_TO_INDEX(tp->t_id)) { debug(2, "genctf: type hole from %d < x < %d\n", b->nptent - 1, CTF_TYPE_TO_INDEX(tp->t_id)); ctt.ctt_name = CTF_TYPE_NAME(CTF_STRTAB_0, 0); ctt.ctt_info = CTF_TYPE_INFO(0, 0, 0); while (b->nptent < CTF_TYPE_TO_INDEX(tp->t_id)) { write_sized_type_rec(b, &ctt, 0); b->nptent++; } } offset = strtab_insert(&b->ctb_strtab, tp->t_name); ctt.ctt_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset); switch (tp->t_type) { case INTRINSIC: ip = tp->t_intr; if (ip->intr_type == INTR_INT) ctt.ctt_info = CTF_TYPE_INFO(CTF_K_INTEGER, isroot, 1); else ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FLOAT, isroot, 1); write_sized_type_rec(b, &ctt, tp->t_size); encoding = 0; if (ip->intr_type == INTR_INT) { if (ip->intr_signed) encoding |= CTF_INT_SIGNED; if (ip->intr_iformat == 'c') encoding |= CTF_INT_CHAR; else if (ip->intr_iformat == 'b') encoding |= CTF_INT_BOOL; else if (ip->intr_iformat == 'v') encoding |= CTF_INT_VARARGS; } else encoding = ip->intr_fformat; data = CTF_INT_DATA(encoding, ip->intr_offset, ip->intr_nbits); ctf_buf_write(b, &data, sizeof (data)); break; case POINTER: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_POINTER, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; case ARRAY: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_ARRAY, isroot, 1); write_sized_type_rec(b, &ctt, tp->t_size); cta.cta_contents = tp->t_ardef->ad_contents->t_id; cta.cta_index = tp->t_ardef->ad_idxtype->t_id; cta.cta_nelems = tp->t_ardef->ad_nelems; ctf_buf_write(b, &cta, sizeof (cta)); break; case STRUCT: case UNION: for (i = 0, mp = tp->t_members; mp != NULL; mp = mp->ml_next) i++; /* count up struct or union members */ if (i > CTF_MAX_VLEN) { terminate("sou %s has too many members: %d > %d\n", tdesc_name(tp), i, CTF_MAX_VLEN); } if (tp->t_type == STRUCT) ctt.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, isroot, i); else ctt.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, isroot, i); write_sized_type_rec(b, &ctt, tp->t_size); if (tp->t_size < CTF_LSTRUCT_THRESH) { for (mp = tp->t_members; mp != NULL; mp = mp->ml_next) { offset = strtab_insert(&b->ctb_strtab, mp->ml_name); ctm.ctm_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset); ctm.ctm_type = mp->ml_type->t_id; ctm.ctm_offset = mp->ml_offset; ctf_buf_write(b, &ctm, sizeof (ctm)); } } else { for (mp = tp->t_members; mp != NULL; mp = mp->ml_next) { offset = strtab_insert(&b->ctb_strtab, mp->ml_name); ctlm.ctlm_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset); ctlm.ctlm_type = mp->ml_type->t_id; ctlm.ctlm_offsethi = CTF_OFFSET_TO_LMEMHI(mp->ml_offset); ctlm.ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO(mp->ml_offset); ctf_buf_write(b, &ctlm, sizeof (ctlm)); } } break; case ENUM: for (i = 0, ep = tp->t_emem; ep != NULL; ep = ep->el_next) i++; /* count up enum members */ if (i > CTF_MAX_VLEN) { terminate("enum %s has too many values: %d > %d\n", tdesc_name(tp), i, CTF_MAX_VLEN); } ctt.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, isroot, i); write_sized_type_rec(b, &ctt, tp->t_size); for (ep = tp->t_emem; ep != NULL; ep = ep->el_next) { offset = strtab_insert(&b->ctb_strtab, ep->el_name); cte.cte_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset); cte.cte_value = ep->el_number; ctf_buf_write(b, &cte, sizeof (cte)); } break; case FORWARD: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FORWARD, isroot, 0); ctt.ctt_type = 0; write_unsized_type_rec(b, &ctt); break; case TYPEDEF: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_TYPEDEF, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; case VOLATILE: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_VOLATILE, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; case CONST: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_CONST, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; case FUNCTION: i = tp->t_fndef->fn_nargs + tp->t_fndef->fn_vargs; if (i > CTF_MAX_VLEN) { terminate("function %s has too many args: %d > %d\n", i, CTF_MAX_VLEN); } ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FUNCTION, isroot, i); ctt.ctt_type = tp->t_fndef->fn_ret->t_id; write_unsized_type_rec(b, &ctt); for (i = 0; i < tp->t_fndef->fn_nargs; i++) { id = tp->t_fndef->fn_args[i]->t_id; ctf_buf_write(b, &id, sizeof (id)); } if (tp->t_fndef->fn_vargs) { id = 0; ctf_buf_write(b, &id, sizeof (id)); i++; } if (i & 1) { id = 0; ctf_buf_write(b, &id, sizeof (id)); } break; case RESTRICT: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_RESTRICT, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; default: warning("Can't write unknown type %d\n", tp->t_type); } debug(3, "Wrote type %d %s\n", tp->t_id, tdesc_name(tp)); return (1); } typedef struct resbuf { caddr_t rb_base; caddr_t rb_ptr; size_t rb_size; z_stream rb_zstr; } resbuf_t; static void rbzs_grow(resbuf_t *rb) { off_t ptroff = (caddr_t)rb->rb_zstr.next_out - rb->rb_base; rb->rb_size += RES_BUF_CHUNK_SIZE; rb->rb_base = xrealloc(rb->rb_base, rb->rb_size); rb->rb_ptr = rb->rb_base + ptroff; rb->rb_zstr.next_out = (Bytef *)(rb->rb_ptr); rb->rb_zstr.avail_out += RES_BUF_CHUNK_SIZE; } static void compress_start(resbuf_t *rb) { int rc; rb->rb_zstr.zalloc = (alloc_func)0; rb->rb_zstr.zfree = (free_func)0; rb->rb_zstr.opaque = (voidpf)0; if ((rc = deflateInit(&rb->rb_zstr, Z_BEST_COMPRESSION)) != Z_OK) parseterminate("zlib start failed: %s", zError(rc)); } static ssize_t compress_buffer(const void *buf, size_t n, void *data) { resbuf_t *rb = (resbuf_t *)data; int rc; rb->rb_zstr.next_out = (Bytef *)rb->rb_ptr; rb->rb_zstr.avail_out = rb->rb_size - (rb->rb_ptr - rb->rb_base); rb->rb_zstr.next_in = (Bytef *)buf; rb->rb_zstr.avail_in = n; while (rb->rb_zstr.avail_in) { if (rb->rb_zstr.avail_out == 0) rbzs_grow(rb); if ((rc = deflate(&rb->rb_zstr, Z_NO_FLUSH)) != Z_OK) parseterminate("zlib deflate failed: %s", zError(rc)); } rb->rb_ptr = (caddr_t)rb->rb_zstr.next_out; return (n); } static void compress_flush(resbuf_t *rb, int type) { int rc; for (;;) { if (rb->rb_zstr.avail_out == 0) rbzs_grow(rb); rc = deflate(&rb->rb_zstr, type); if ((type == Z_FULL_FLUSH && rc == Z_BUF_ERROR) || (type == Z_FINISH && rc == Z_STREAM_END)) break; else if (rc != Z_OK) parseterminate("zlib finish failed: %s", zError(rc)); } rb->rb_ptr = (caddr_t)rb->rb_zstr.next_out; } static void compress_end(resbuf_t *rb) { int rc; compress_flush(rb, Z_FINISH); if ((rc = deflateEnd(&rb->rb_zstr)) != Z_OK) parseterminate("zlib end failed: %s", zError(rc)); } /* * Pad the buffer to a power-of-2 boundary */ static void pad_buffer(ctf_buf_t *buf, int align) { uint_t cur = ctf_buf_cur(buf); ssize_t topad = (align - (cur % align)) % align; static const char pad[8] = { 0 }; while (topad > 0) { ctf_buf_write(buf, pad, (topad > 8 ? 8 : topad)); topad -= 8; } } static ssize_t bcopy_data(const void *buf, size_t n, void *data) { caddr_t *posp = (caddr_t *)data; bcopy(buf, *posp, n); *posp += n; return (n); } static caddr_t write_buffer(ctf_header_t *h, ctf_buf_t *buf, size_t *resszp) { caddr_t outbuf; caddr_t bufpos; outbuf = xmalloc(sizeof (ctf_header_t) + (buf->ctb_ptr - buf->ctb_base) + buf->ctb_strtab.str_size); bufpos = outbuf; (void) bcopy_data(h, sizeof (ctf_header_t), &bufpos); (void) bcopy_data(buf->ctb_base, buf->ctb_ptr - buf->ctb_base, &bufpos); (void) strtab_write(&buf->ctb_strtab, bcopy_data, &bufpos); *resszp = bufpos - outbuf; return (outbuf); } /* * Create the compression buffer, and fill it with the CTF and string * table data. We flush the compression state between the two so the * dictionary used for the string tables won't be polluted with values * that made sense for the CTF data. */ static caddr_t write_compressed_buffer(ctf_header_t *h, ctf_buf_t *buf, size_t *resszp) { resbuf_t resbuf; resbuf.rb_size = RES_BUF_CHUNK_SIZE; resbuf.rb_base = xmalloc(resbuf.rb_size); bcopy(h, resbuf.rb_base, sizeof (ctf_header_t)); resbuf.rb_ptr = resbuf.rb_base + sizeof (ctf_header_t); compress_start(&resbuf); (void) compress_buffer(buf->ctb_base, buf->ctb_ptr - buf->ctb_base, &resbuf); compress_flush(&resbuf, Z_FULL_FLUSH); (void) strtab_write(&buf->ctb_strtab, compress_buffer, &resbuf); compress_end(&resbuf); *resszp = (resbuf.rb_ptr - resbuf.rb_base); return (resbuf.rb_base); } caddr_t ctf_gen(iiburst_t *iiburst, size_t *resszp, int do_compress) { ctf_buf_t *buf = ctf_buf_new(); ctf_header_t h; caddr_t outbuf; int i; /* * Prepare the header, and create the CTF output buffers. The data * object section and function section are both lists of 2-byte * integers; we pad these out to the next 4-byte boundary if needed. */ h.cth_magic = CTF_MAGIC; h.cth_version = CTF_VERSION; h.cth_flags = do_compress ? CTF_F_COMPRESS : 0; h.cth_parlabel = strtab_insert(&buf->ctb_strtab, iiburst->iib_td->td_parlabel); h.cth_parname = strtab_insert(&buf->ctb_strtab, iiburst->iib_td->td_parname); h.cth_lbloff = 0; (void) list_iter(iiburst->iib_td->td_labels, (int (*)())write_label, buf); pad_buffer(buf, 2); h.cth_objtoff = ctf_buf_cur(buf); for (i = 0; i < iiburst->iib_nobjts; i++) write_objects(iiburst->iib_objts[i], buf); pad_buffer(buf, 2); h.cth_funcoff = ctf_buf_cur(buf); for (i = 0; i < iiburst->iib_nfuncs; i++) write_functions(iiburst->iib_funcs[i], buf); pad_buffer(buf, 4); h.cth_typeoff = ctf_buf_cur(buf); (void) list_iter(iiburst->iib_types, (int (*)())write_type, buf); debug(2, "CTF wrote %d types\n", list_count(iiburst->iib_types)); h.cth_stroff = ctf_buf_cur(buf); h.cth_strlen = strtab_size(&buf->ctb_strtab); /* * We only do compression for ctfmerge, as ctfconvert is only * supposed to be used on intermediary build objects. This is * significantly faster. */ if (do_compress) outbuf = write_compressed_buffer(&h, buf, resszp); else outbuf = write_buffer(&h, buf, resszp); ctf_buf_free(buf); return (outbuf); } void get_ctt_size(ctf_type_t *ctt, size_t *sizep, size_t *incrementp) { if (ctt->ctt_size == CTF_LSIZE_SENT) { *sizep = (size_t)CTF_TYPE_LSIZE(ctt); *incrementp = sizeof (ctf_type_t); } else { *sizep = ctt->ctt_size; *incrementp = sizeof (ctf_stype_t); } } static int count_types(ctf_header_t *h, caddr_t data) { caddr_t dptr = data + h->cth_typeoff; int count = 0; dptr = data + h->cth_typeoff; while (dptr < data + h->cth_stroff) { /* LINTED - pointer alignment */ ctf_type_t *ctt = (ctf_type_t *)dptr; size_t vlen = CTF_INFO_VLEN(ctt->ctt_info); size_t size, increment; get_ctt_size(ctt, &size, &increment); switch (CTF_INFO_KIND(ctt->ctt_info)) { case CTF_K_INTEGER: case CTF_K_FLOAT: dptr += 4; break; case CTF_K_POINTER: case CTF_K_FORWARD: case CTF_K_TYPEDEF: case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: case CTF_K_FUNCTION: dptr += sizeof (ushort_t) * (vlen + (vlen & 1)); break; case CTF_K_ARRAY: dptr += sizeof (ctf_array_t); break; case CTF_K_STRUCT: case CTF_K_UNION: if (size < CTF_LSTRUCT_THRESH) dptr += sizeof (ctf_member_t) * vlen; else dptr += sizeof (ctf_lmember_t) * vlen; break; case CTF_K_ENUM: dptr += sizeof (ctf_enum_t) * vlen; break; case CTF_K_UNKNOWN: break; default: parseterminate("Unknown CTF type %d (#%d) at %#x", CTF_INFO_KIND(ctt->ctt_info), count, dptr - data); } dptr += increment; count++; } debug(3, "CTF read %d types\n", count); return (count); } /* * Resurrect the labels stored in the CTF data, returning the index associated * with a label provided by the caller. There are several cases, outlined * below. Note that, given two labels, the one associated with the lesser type * index is considered to be older than the other. * * 1. matchlbl == NULL - return the index of the most recent label. * 2. matchlbl == "BASE" - return the index of the oldest label. * 3. matchlbl != NULL, but doesn't match any labels in the section - warn * the user, and proceed as if matchlbl == "BASE" (for safety). * 4. matchlbl != NULL, and matches one of the labels in the section - return * the type index associated with the label. */ static int resurrect_labels(ctf_header_t *h, tdata_t *td, caddr_t ctfdata, char *matchlbl) { caddr_t buf = ctfdata + h->cth_lbloff; caddr_t sbuf = ctfdata + h->cth_stroff; size_t bufsz = h->cth_objtoff - h->cth_lbloff; int lastidx = 0, baseidx = -1; char *baselabel; ctf_lblent_t *ctl; /* LINTED - pointer alignment */ for (ctl = (ctf_lblent_t *)buf; (caddr_t)ctl < buf + bufsz; ctl++) { char *label = sbuf + ctl->ctl_label; lastidx = ctl->ctl_typeidx; debug(3, "Resurrected label %s type idx %d\n", label, lastidx); tdata_label_add(td, label, lastidx); if (baseidx == -1) { baseidx = lastidx; baselabel = label; if (matchlbl != NULL && streq(matchlbl, "BASE")) return (lastidx); } if (matchlbl != NULL && streq(label, matchlbl)) return (lastidx); } if (matchlbl != NULL) { /* User provided a label that didn't match */ warning("%s: Cannot find label `%s' - using base (%s)\n", curfile, matchlbl, (baselabel ? baselabel : "NONE")); tdata_label_free(td); tdata_label_add(td, baselabel, baseidx); return (baseidx); } return (lastidx); } static void resurrect_objects(ctf_header_t *h, tdata_t *td, tdesc_t **tdarr, int tdsize, caddr_t ctfdata, symit_data_t *si) { caddr_t buf = ctfdata + h->cth_objtoff; size_t bufsz = h->cth_funcoff - h->cth_objtoff; caddr_t dptr; symit_reset(si); for (dptr = buf; dptr < buf + bufsz; dptr += 2) { /* LINTED - pointer alignment */ ushort_t id = *((ushort_t *)dptr); iidesc_t *ii; GElf_Sym *sym; if (!(sym = symit_next(si, STT_OBJECT)) && id != 0) { parseterminate( "Unexpected end of object symbols at %x of %x", dptr - buf, bufsz); } if (id == 0) { debug(3, "Skipping null object\n"); continue; } else if (id >= tdsize) { parseterminate("Reference to invalid type %d", id); } ii = iidesc_new(symit_name(si)); ii->ii_dtype = tdarr[id]; if (GELF_ST_BIND(sym->st_info) == STB_LOCAL) { ii->ii_type = II_SVAR; ii->ii_owner = xstrdup(symit_curfile(si)); } else ii->ii_type = II_GVAR; hash_add(td->td_iihash, ii); debug(3, "Resurrected %s object %s (%d) from %s\n", (ii->ii_type == II_GVAR ? "global" : "static"), ii->ii_name, id, (ii->ii_owner ? ii->ii_owner : "(none)")); } } static void resurrect_functions(ctf_header_t *h, tdata_t *td, tdesc_t **tdarr, int tdsize, caddr_t ctfdata, symit_data_t *si) { caddr_t buf = ctfdata + h->cth_funcoff; size_t bufsz = h->cth_typeoff - h->cth_funcoff; caddr_t dptr = buf; iidesc_t *ii; ushort_t info; ushort_t retid; GElf_Sym *sym; int i; symit_reset(si); while (dptr < buf + bufsz) { /* LINTED - pointer alignment */ info = *((ushort_t *)dptr); dptr += 2; if (!(sym = symit_next(si, STT_FUNC)) && info != 0) parseterminate("Unexpected end of function symbols"); if (info == 0) { debug(3, "Skipping null function (%s)\n", symit_name(si)); continue; } /* LINTED - pointer alignment */ retid = *((ushort_t *)dptr); dptr += 2; if (retid >= tdsize) parseterminate("Reference to invalid type %d", retid); ii = iidesc_new(symit_name(si)); ii->ii_dtype = tdarr[retid]; if (GELF_ST_BIND(sym->st_info) == STB_LOCAL) { ii->ii_type = II_SFUN; ii->ii_owner = xstrdup(symit_curfile(si)); } else ii->ii_type = II_GFUN; ii->ii_nargs = CTF_INFO_VLEN(info); if (ii->ii_nargs) ii->ii_args = xmalloc(sizeof (tdesc_t *) * ii->ii_nargs); for (i = 0; i < ii->ii_nargs; i++, dptr += 2) { /* LINTED - pointer alignment */ ushort_t id = *((ushort_t *)dptr); if (id >= tdsize) parseterminate("Reference to invalid type %d", id); ii->ii_args[i] = tdarr[id]; } if (ii->ii_nargs && ii->ii_args[ii->ii_nargs - 1] == NULL) { ii->ii_nargs--; ii->ii_vargs = 1; } hash_add(td->td_iihash, ii); debug(3, "Resurrected %s function %s (%d, %d args)\n", (ii->ii_type == II_GFUN ? "global" : "static"), ii->ii_name, retid, ii->ii_nargs); } } static void resurrect_types(ctf_header_t *h, tdata_t *td, tdesc_t **tdarr, int tdsize, caddr_t ctfdata, int maxid) { caddr_t buf = ctfdata + h->cth_typeoff; size_t bufsz = h->cth_stroff - h->cth_typeoff; caddr_t sbuf = ctfdata + h->cth_stroff; caddr_t dptr = buf; tdesc_t *tdp; uint_t data; uint_t encoding; size_t size, increment; int tcnt; int iicnt = 0; tid_t tid, argid; int kind, vlen; int i; elist_t **epp; mlist_t **mpp; intr_t *ip; ctf_type_t *ctt; ctf_array_t *cta; ctf_enum_t *cte; /* * A maxid of zero indicates a request to resurrect all types, so reset * maxid to the maximum type id. */ if (maxid == 0) maxid = CTF_MAX_TYPE; for (dptr = buf, tcnt = 0, tid = 1; dptr < buf + bufsz; tcnt++, tid++) { if (tid > maxid) break; if (tid >= tdsize) parseterminate("Reference to invalid type %d", tid); /* LINTED - pointer alignment */ ctt = (ctf_type_t *)dptr; get_ctt_size(ctt, &size, &increment); dptr += increment; tdp = tdarr[tid]; if (CTF_NAME_STID(ctt->ctt_name) != CTF_STRTAB_0) parseterminate( "Unable to cope with non-zero strtab id"); if (CTF_NAME_OFFSET(ctt->ctt_name) != 0) { tdp->t_name = xstrdup(sbuf + CTF_NAME_OFFSET(ctt->ctt_name)); } else tdp->t_name = NULL; kind = CTF_INFO_KIND(ctt->ctt_info); vlen = CTF_INFO_VLEN(ctt->ctt_info); switch (kind) { case CTF_K_INTEGER: tdp->t_type = INTRINSIC; tdp->t_size = size; /* LINTED - pointer alignment */ data = *((uint_t *)dptr); dptr += sizeof (uint_t); encoding = CTF_INT_ENCODING(data); ip = xmalloc(sizeof (intr_t)); ip->intr_type = INTR_INT; ip->intr_signed = (encoding & CTF_INT_SIGNED) ? 1 : 0; if (encoding & CTF_INT_CHAR) ip->intr_iformat = 'c'; else if (encoding & CTF_INT_BOOL) ip->intr_iformat = 'b'; else if (encoding & CTF_INT_VARARGS) ip->intr_iformat = 'v'; else ip->intr_iformat = '\0'; ip->intr_offset = CTF_INT_OFFSET(data); ip->intr_nbits = CTF_INT_BITS(data); tdp->t_intr = ip; break; case CTF_K_FLOAT: tdp->t_type = INTRINSIC; tdp->t_size = size; /* LINTED - pointer alignment */ data = *((uint_t *)dptr); dptr += sizeof (uint_t); ip = xcalloc(sizeof (intr_t)); ip->intr_type = INTR_REAL; ip->intr_fformat = CTF_FP_ENCODING(data); ip->intr_offset = CTF_FP_OFFSET(data); ip->intr_nbits = CTF_FP_BITS(data); tdp->t_intr = ip; break; case CTF_K_POINTER: tdp->t_type = POINTER; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_ARRAY: tdp->t_type = ARRAY; tdp->t_size = size; /* LINTED - pointer alignment */ cta = (ctf_array_t *)dptr; dptr += sizeof (ctf_array_t); tdp->t_ardef = xmalloc(sizeof (ardef_t)); tdp->t_ardef->ad_contents = tdarr[cta->cta_contents]; tdp->t_ardef->ad_idxtype = tdarr[cta->cta_index]; tdp->t_ardef->ad_nelems = cta->cta_nelems; break; case CTF_K_STRUCT: case CTF_K_UNION: tdp->t_type = (kind == CTF_K_STRUCT ? STRUCT : UNION); tdp->t_size = size; if (size < CTF_LSTRUCT_THRESH) { for (i = 0, mpp = &tdp->t_members; i < vlen; i++, mpp = &((*mpp)->ml_next)) { /* LINTED - pointer alignment */ ctf_member_t *ctm = (ctf_member_t *) dptr; dptr += sizeof (ctf_member_t); *mpp = xmalloc(sizeof (mlist_t)); (*mpp)->ml_name = xstrdup(sbuf + ctm->ctm_name); (*mpp)->ml_type = tdarr[ctm->ctm_type]; (*mpp)->ml_offset = ctm->ctm_offset; (*mpp)->ml_size = 0; } } else { for (i = 0, mpp = &tdp->t_members; i < vlen; i++, mpp = &((*mpp)->ml_next)) { /* LINTED - pointer alignment */ ctf_lmember_t *ctlm = (ctf_lmember_t *) dptr; dptr += sizeof (ctf_lmember_t); *mpp = xmalloc(sizeof (mlist_t)); (*mpp)->ml_name = xstrdup(sbuf + ctlm->ctlm_name); (*mpp)->ml_type = tdarr[ctlm->ctlm_type]; (*mpp)->ml_offset = (int)CTF_LMEM_OFFSET(ctlm); (*mpp)->ml_size = 0; } } *mpp = NULL; break; case CTF_K_ENUM: tdp->t_type = ENUM; tdp->t_size = size; for (i = 0, epp = &tdp->t_emem; i < vlen; i++, epp = &((*epp)->el_next)) { /* LINTED - pointer alignment */ cte = (ctf_enum_t *)dptr; dptr += sizeof (ctf_enum_t); *epp = xmalloc(sizeof (elist_t)); (*epp)->el_name = xstrdup(sbuf + cte->cte_name); (*epp)->el_number = cte->cte_value; } *epp = NULL; break; case CTF_K_FORWARD: tdp->t_type = FORWARD; list_add(&td->td_fwdlist, tdp); break; case CTF_K_TYPEDEF: tdp->t_type = TYPEDEF; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_VOLATILE: tdp->t_type = VOLATILE; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_CONST: tdp->t_type = CONST; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_FUNCTION: tdp->t_type = FUNCTION; tdp->t_fndef = xcalloc(sizeof (fndef_t)); tdp->t_fndef->fn_ret = tdarr[ctt->ctt_type]; /* LINTED - pointer alignment */ if (vlen > 0 && *(ushort_t *)(dptr + (sizeof (ushort_t) * (vlen - 1))) == 0) tdp->t_fndef->fn_vargs = 1; tdp->t_fndef->fn_nargs = vlen - tdp->t_fndef->fn_vargs; tdp->t_fndef->fn_args = xcalloc(sizeof (tdesc_t) * vlen - tdp->t_fndef->fn_vargs); for (i = 0; i < vlen; i++) { /* LINTED - pointer alignment */ argid = *(ushort_t *)dptr; dptr += sizeof (ushort_t); if (argid != 0) tdp->t_fndef->fn_args[i] = tdarr[argid]; } if (vlen & 1) dptr += sizeof (ushort_t); break; case CTF_K_RESTRICT: tdp->t_type = RESTRICT; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_UNKNOWN: break; default: warning("Can't parse unknown CTF type %d\n", kind); } if (CTF_INFO_ISROOT(ctt->ctt_info)) { iidesc_t *ii = iidesc_new(tdp->t_name); if (tdp->t_type == STRUCT || tdp->t_type == UNION || tdp->t_type == ENUM) ii->ii_type = II_SOU; else ii->ii_type = II_TYPE; ii->ii_dtype = tdp; hash_add(td->td_iihash, ii); iicnt++; } debug(3, "Resurrected %d %stype %s (%d)\n", tdp->t_type, (CTF_INFO_ISROOT(ctt->ctt_info) ? "root " : ""), tdesc_name(tdp), tdp->t_id); } debug(3, "Resurrected %d types (%d were roots)\n", tcnt, iicnt); } /* * For lack of other inspiration, we're going to take the boring route. We * count the number of types. This lets us malloc that many tdesc structs * before we start filling them in. This has the advantage of allowing us to * avoid a merge-esque remap step. */ static tdata_t * ctf_parse(ctf_header_t *h, caddr_t buf, symit_data_t *si, char *label) { tdata_t *td = tdata_new(); tdesc_t **tdarr; int ntypes = count_types(h, buf); int idx, i; /* shudder */ tdarr = xcalloc(sizeof (tdesc_t *) * (ntypes + 1)); tdarr[0] = NULL; for (i = 1; i <= ntypes; i++) { tdarr[i] = xcalloc(sizeof (tdesc_t)); tdarr[i]->t_id = i; } td->td_parlabel = xstrdup(buf + h->cth_stroff + h->cth_parlabel); /* we have the technology - we can rebuild them */ idx = resurrect_labels(h, td, buf, label); resurrect_objects(h, td, tdarr, ntypes + 1, buf, si); resurrect_functions(h, td, tdarr, ntypes + 1, buf, si); resurrect_types(h, td, tdarr, ntypes + 1, buf, idx); free(tdarr); td->td_nextid = ntypes + 1; return (td); } static size_t decompress_ctf(caddr_t cbuf, size_t cbufsz, caddr_t dbuf, size_t dbufsz) { z_stream zstr; int rc; zstr.zalloc = (alloc_func)0; zstr.zfree = (free_func)0; zstr.opaque = (voidpf)0; zstr.next_in = (Bytef *)cbuf; zstr.avail_in = cbufsz; zstr.next_out = (Bytef *)dbuf; zstr.avail_out = dbufsz; if ((rc = inflateInit(&zstr)) != Z_OK || (rc = inflate(&zstr, Z_NO_FLUSH)) != Z_STREAM_END || (rc = inflateEnd(&zstr)) != Z_OK) { warning("CTF decompress zlib error %s\n", zError(rc)); - return (NULL); + return (0); } debug(3, "reflated %lu bytes to %lu, pointer at %d\n", zstr.total_in, zstr.total_out, (caddr_t)zstr.next_in - cbuf); return (zstr.total_out); } /* * Reconstruct the type tree from a given buffer of CTF data. Only the types * up to the type associated with the provided label, inclusive, will be * reconstructed. If a NULL label is provided, all types will be reconstructed. * * This function won't work on files that have been uniquified. */ tdata_t * ctf_load(char *file, caddr_t buf, size_t bufsz, symit_data_t *si, char *label) { ctf_header_t *h; caddr_t ctfdata; size_t ctfdatasz; tdata_t *td; curfile = file; if (bufsz < sizeof (ctf_header_t)) parseterminate("Corrupt CTF - short header"); /* LINTED - pointer alignment */ h = (ctf_header_t *)buf; buf += sizeof (ctf_header_t); bufsz -= sizeof (ctf_header_t); if (h->cth_magic != CTF_MAGIC) parseterminate("Corrupt CTF - bad magic 0x%x", h->cth_magic); if (h->cth_version != CTF_VERSION) parseterminate("Unknown CTF version %d", h->cth_version); ctfdatasz = h->cth_stroff + h->cth_strlen; if (h->cth_flags & CTF_F_COMPRESS) { size_t actual; ctfdata = xmalloc(ctfdatasz); if ((actual = decompress_ctf(buf, bufsz, ctfdata, ctfdatasz)) != ctfdatasz) { parseterminate("Corrupt CTF - short decompression " "(was %d, expecting %d)", actual, ctfdatasz); } } else { ctfdata = buf; ctfdatasz = bufsz; } td = ctf_parse(h, ctfdata, si, label); if (h->cth_flags & CTF_F_COMPRESS) free(ctfdata); curfile = NULL; return (td); } Index: vendor/illumos/dist/tools/ctf/cvt/dwarf.c =================================================================== --- vendor/illumos/dist/tools/ctf/cvt/dwarf.c (revision 279821) +++ vendor/illumos/dist/tools/ctf/cvt/dwarf.c (revision 279822) @@ -1,1993 +1,1993 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2012 Jason King. All rights reserved. * Use is subject to license terms. */ /* * DWARF to tdata conversion * * For the most part, conversion is straightforward, proceeding in two passes. * On the first pass, we iterate through every die, creating new type nodes as * necessary. Referenced tdesc_t's are created in an uninitialized state, thus * allowing type reference pointers to be filled in. If the tdesc_t * corresponding to a given die can be completely filled out (sizes and offsets * calculated, and so forth) without using any referenced types, the tdesc_t is * marked as resolved. Consider an array type. If the type corresponding to * the array contents has not yet been processed, we will create a blank tdesc * for the contents type (only the type ID will be filled in, relying upon the * later portion of the first pass to encounter and complete the referenced * type). We will then attempt to determine the size of the array. If the * array has a byte size attribute, we will have completely characterized the * array type, and will be able to mark it as resolved. The lack of a byte * size attribute, on the other hand, will prevent us from fully resolving the * type, as the size will only be calculable with reference to the contents * type, which has not, as yet, been encountered. The array type will thus be * left without the resolved flag, and the first pass will continue. * * When we begin the second pass, we will have created tdesc_t nodes for every * type in the section. We will traverse the tree, from the iidescs down, * processing each unresolved node. As the referenced nodes will have been * populated, the array type used in our example above will be able to use the * size of the referenced types (if available) to determine its own type. The * traversal will be repeated until all types have been resolved or we have * failed to make progress. When all tdescs have been resolved, the conversion * is complete. * * There are, as always, a few special cases that are handled during the first * and second passes: * * 1. Empty enums - GCC will occasionally emit an enum without any members. * Later on in the file, it will emit the same enum type, though this time * with the full complement of members. All references to the memberless * enum need to be redirected to the full definition. During the first * pass, each enum is entered in dm_enumhash, along with a pointer to its * corresponding tdesc_t. If, during the second pass, we encounter a * memberless enum, we use the hash to locate the full definition. All * tdescs referencing the empty enum are then redirected. * * 2. Forward declarations - If the compiler sees a forward declaration for * a structure, followed by the definition of that structure, it will emit * DWARF data for both the forward declaration and the definition. We need * to resolve the forward declarations when possible, by redirecting * forward-referencing tdescs to the actual struct/union definitions. This * redirection is done completely within the first pass. We begin by * recording all forward declarations in dw_fwdhash. When we define a * structure, we check to see if there have been any corresponding forward * declarations. If so, we redirect the tdescs which referenced the forward * declarations to the structure or union definition. * * XXX see if a post traverser will allow the elimination of repeated pass 2 * traversals. */ #include #include #include #include #include #include #include #include #include "ctf_headers.h" #include "ctftools.h" #include "memory.h" #include "list.h" #include "traverse.h" /* The version of DWARF which we support. */ #define DWARF_VERSION 2 /* * We need to define a couple of our own intrinsics, to smooth out some of the * differences between the GCC and DevPro DWARF emitters. See the referenced * routines and the special cases in the file comment for more details. * * Type IDs are 32 bits wide. We're going to use the top of that field to * indicate types that we've created ourselves. */ #define TID_FILEMAX 0x3fffffff /* highest tid from file */ #define TID_VOID 0x40000001 /* see die_void() */ #define TID_LONG 0x40000002 /* see die_array() */ #define TID_MFGTID_BASE 0x40000003 /* first mfg'd tid */ /* * To reduce the staggering amount of error-handling code that would otherwise * be required, the attribute-retrieval routines handle most of their own * errors. If the following flag is supplied as the value of the `req' * argument, they will also handle the absence of a requested attribute by * terminating the program. */ #define DW_ATTR_REQ 1 #define TDESC_HASH_BUCKETS 511 typedef struct dwarf { Dwarf_Debug dw_dw; /* for libdwarf */ Dwarf_Error dw_err; /* for libdwarf */ Dwarf_Unsigned dw_maxoff; /* highest legal offset in this cu */ tdata_t *dw_td; /* root of the tdesc/iidesc tree */ hash_t *dw_tidhash; /* hash of tdescs by t_id */ hash_t *dw_fwdhash; /* hash of fwd decls by name */ hash_t *dw_enumhash; /* hash of memberless enums by name */ tdesc_t *dw_void; /* manufactured void type */ tdesc_t *dw_long; /* manufactured long type for arrays */ size_t dw_ptrsz; /* size of a pointer in this file */ tid_t dw_mfgtid_last; /* last mfg'd type ID used */ uint_t dw_nunres; /* count of unresolved types */ char *dw_cuname; /* name of compilation unit */ } dwarf_t; static void die_create_one(dwarf_t *, Dwarf_Die); static void die_create(dwarf_t *, Dwarf_Die); static tid_t mfgtid_next(dwarf_t *dw) { return (++dw->dw_mfgtid_last); } static void tdesc_add(dwarf_t *dw, tdesc_t *tdp) { hash_add(dw->dw_tidhash, tdp); } static tdesc_t * tdesc_lookup(dwarf_t *dw, int tid) { tdesc_t tmpl, *tdp; tmpl.t_id = tid; if (hash_find(dw->dw_tidhash, &tmpl, (void **)&tdp)) return (tdp); else return (NULL); } /* * Resolve a tdesc down to a node which should have a size. Returns the size, * zero if the size hasn't yet been determined. */ static size_t tdesc_size(tdesc_t *tdp) { for (;;) { switch (tdp->t_type) { case INTRINSIC: case POINTER: case ARRAY: case FUNCTION: case STRUCT: case UNION: case ENUM: return (tdp->t_size); case FORWARD: return (0); case TYPEDEF: case VOLATILE: case CONST: case RESTRICT: tdp = tdp->t_tdesc; continue; case 0: /* not yet defined */ return (0); default: terminate("tdp %u: tdesc_size on unknown type %d\n", tdp->t_id, tdp->t_type); } } } static size_t tdesc_bitsize(tdesc_t *tdp) { for (;;) { switch (tdp->t_type) { case INTRINSIC: return (tdp->t_intr->intr_nbits); case ARRAY: case FUNCTION: case STRUCT: case UNION: case ENUM: case POINTER: return (tdp->t_size * NBBY); case FORWARD: return (0); case TYPEDEF: case VOLATILE: case RESTRICT: case CONST: tdp = tdp->t_tdesc; continue; case 0: /* not yet defined */ return (0); default: terminate("tdp %u: tdesc_bitsize on unknown type %d\n", tdp->t_id, tdp->t_type); } } } static tdesc_t * tdesc_basetype(tdesc_t *tdp) { for (;;) { switch (tdp->t_type) { case TYPEDEF: case VOLATILE: case RESTRICT: case CONST: tdp = tdp->t_tdesc; break; case 0: /* not yet defined */ return (NULL); default: return (tdp); } } } static Dwarf_Off die_off(dwarf_t *dw, Dwarf_Die die) { Dwarf_Off off; if (dwarf_dieoffset(die, &off, &dw->dw_err) == DW_DLV_OK) return (off); terminate("failed to get offset for die: %s\n", dwarf_errmsg(dw->dw_err)); /*NOTREACHED*/ return (0); } static Dwarf_Die die_sibling(dwarf_t *dw, Dwarf_Die die) { Dwarf_Die sib; int rc; if ((rc = dwarf_siblingof(dw->dw_dw, die, &sib, &dw->dw_err)) == DW_DLV_OK) return (sib); else if (rc == DW_DLV_NO_ENTRY) return (NULL); terminate("die %llu: failed to find type sibling: %s\n", die_off(dw, die), dwarf_errmsg(dw->dw_err)); /*NOTREACHED*/ return (NULL); } static Dwarf_Die die_child(dwarf_t *dw, Dwarf_Die die) { Dwarf_Die child; int rc; if ((rc = dwarf_child(die, &child, &dw->dw_err)) == DW_DLV_OK) return (child); else if (rc == DW_DLV_NO_ENTRY) return (NULL); terminate("die %llu: failed to find type child: %s\n", die_off(dw, die), dwarf_errmsg(dw->dw_err)); /*NOTREACHED*/ return (NULL); } static Dwarf_Half die_tag(dwarf_t *dw, Dwarf_Die die) { Dwarf_Half tag; if (dwarf_tag(die, &tag, &dw->dw_err) == DW_DLV_OK) return (tag); terminate("die %llu: failed to get tag for type: %s\n", die_off(dw, die), dwarf_errmsg(dw->dw_err)); /*NOTREACHED*/ return (0); } static Dwarf_Attribute die_attr(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, int req) { Dwarf_Attribute attr; int rc; if ((rc = dwarf_attr(die, name, &attr, &dw->dw_err)) == DW_DLV_OK) { return (attr); } else if (rc == DW_DLV_NO_ENTRY) { if (req) { terminate("die %llu: no attr 0x%x\n", die_off(dw, die), name); } else { return (NULL); } } terminate("die %llu: failed to get attribute for type: %s\n", die_off(dw, die), dwarf_errmsg(dw->dw_err)); /*NOTREACHED*/ return (NULL); } static Dwarf_Half die_attr_form(dwarf_t *dw, Dwarf_Attribute attr) { Dwarf_Half form; if (dwarf_whatform(attr, &form, &dw->dw_err) == DW_DLV_OK) return (form); terminate("failed to get attribute form for type: %s\n", dwarf_errmsg(dw->dw_err)); /*NOTREACHED*/ return (0); } /* * the following functions lookup the value of an attribute in a DIE: * * die_signed * die_unsigned * die_bool * die_string * * They all take the same parameters (with the exception of valp which is * a pointer to the type of the attribute we are looking up): * * dw - the dwarf object to look in * die - the DIE we're interested in * name - the name of the attribute to lookup * valp - pointer to where the value of the attribute is placed * req - if the value is required (0 / non-zero) * * If the attribute is not found, one of the following happens: * - program terminates (req is non-zero) * - function returns 0 * * If the value is found, and in a form (class) we can handle, the function * returns 1. * * Currently, we can only handle attribute values that are stored as * constants (immediate value). If an attribute has a form we cannot * handle (for example VLAs may store the dimensions of the array * as a DWARF expression that can compute it at runtime by reading * values off the stack or other locations in memory), it is treated * the same as if the attribute does not exist. */ static int die_signed(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Signed *valp, int req) { Dwarf_Attribute attr; Dwarf_Signed val; if ((attr = die_attr(dw, die, name, req)) == NULL) return (0); /* die_attr will terminate for us if necessary */ if (dwarf_formsdata(attr, &val, &dw->dw_err) != DW_DLV_OK) { if (req == 0) return (0); terminate("die %llu: failed to get signed (form 0x%x)\n", die_off(dw, die), die_attr_form(dw, attr)); } dwarf_dealloc(dw->dw_dw, attr, DW_DLA_ATTR); *valp = val; return (1); } static int die_unsigned(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Unsigned *valp, int req) { Dwarf_Attribute attr; Dwarf_Unsigned val; if ((attr = die_attr(dw, die, name, req)) == NULL) return (0); /* die_attr will terminate for us if necessary */ if (dwarf_formudata(attr, &val, &dw->dw_err) != DW_DLV_OK) { if (req == 0) return (0); terminate("die %llu: failed to get unsigned (form 0x%x)\n", die_off(dw, die), die_attr_form(dw, attr)); } dwarf_dealloc(dw->dw_dw, attr, DW_DLA_ATTR); *valp = val; return (1); } static int die_bool(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Bool *valp, int req) { Dwarf_Attribute attr; Dwarf_Bool val; if ((attr = die_attr(dw, die, name, req)) == NULL) return (0); /* die_attr will terminate for us if necessary */ if (dwarf_formflag(attr, &val, &dw->dw_err) != DW_DLV_OK) { if (req == 0) return (0); terminate("die %llu: failed to get bool (form 0x%x)\n", die_off(dw, die), die_attr_form(dw, attr)); } dwarf_dealloc(dw->dw_dw, attr, DW_DLA_ATTR); *valp = val; return (1); } static int die_string(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, char **strp, int req) { Dwarf_Attribute attr; char *str; if ((attr = die_attr(dw, die, name, req)) == NULL) return (0); /* die_attr will terminate for us if necessary */ if (dwarf_formstring(attr, &str, &dw->dw_err) != DW_DLV_OK) { if (req == 0) return (0); terminate("die %llu: failed to get string (form 0x%x)\n", die_off(dw, die), die_attr_form(dw, attr)); } *strp = xstrdup(str); dwarf_dealloc(dw->dw_dw, str, DW_DLA_STRING); return (1); } static Dwarf_Off die_attr_ref(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name) { Dwarf_Attribute attr; Dwarf_Off off; attr = die_attr(dw, die, name, DW_ATTR_REQ); if (dwarf_formref(attr, &off, &dw->dw_err) != DW_DLV_OK) { terminate("die %llu: failed to get ref (form 0x%x)\n", die_off(dw, die), die_attr_form(dw, attr)); } dwarf_dealloc(dw->dw_dw, attr, DW_DLA_ATTR); return (off); } static char * die_name(dwarf_t *dw, Dwarf_Die die) { char *str = NULL; (void) die_string(dw, die, DW_AT_name, &str, 0); return (str); } static int die_isdecl(dwarf_t *dw, Dwarf_Die die) { Dwarf_Bool val; return (die_bool(dw, die, DW_AT_declaration, &val, 0) && val); } static int die_isglobal(dwarf_t *dw, Dwarf_Die die) { Dwarf_Signed vis; Dwarf_Bool ext; /* * Some compilers (gcc) use DW_AT_external to indicate function * visibility. Others (Sun) use DW_AT_visibility. */ if (die_signed(dw, die, DW_AT_visibility, &vis, 0)) return (vis == DW_VIS_exported); else return (die_bool(dw, die, DW_AT_external, &ext, 0) && ext); } static tdesc_t * die_add(dwarf_t *dw, Dwarf_Off off) { tdesc_t *tdp = xcalloc(sizeof (tdesc_t)); tdp->t_id = off; tdesc_add(dw, tdp); return (tdp); } static tdesc_t * die_lookup_pass1(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name) { Dwarf_Off ref = die_attr_ref(dw, die, name); tdesc_t *tdp; if ((tdp = tdesc_lookup(dw, ref)) != NULL) return (tdp); return (die_add(dw, ref)); } static int die_mem_offset(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Unsigned *valp, int req) { Dwarf_Attribute attr; Dwarf_Locdesc *loc; Dwarf_Signed locnum; if ((attr = die_attr(dw, die, name, req)) == NULL) return (0); /* die_attr will terminate for us if necessary */ if (dwarf_loclist(attr, &loc, &locnum, &dw->dw_err) != DW_DLV_OK) { terminate("die %llu: failed to get mem offset location list\n", die_off(dw, die)); } dwarf_dealloc(dw->dw_dw, attr, DW_DLA_ATTR); if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) { terminate("die %llu: cannot parse member offset\n", die_off(dw, die)); } *valp = loc->ld_s->lr_number; dwarf_dealloc(dw->dw_dw, loc->ld_s, DW_DLA_LOC_BLOCK); dwarf_dealloc(dw->dw_dw, loc, DW_DLA_LOCDESC); return (1); } static tdesc_t * tdesc_intr_common(dwarf_t *dw, int tid, const char *name, size_t sz) { tdesc_t *tdp; intr_t *intr; intr = xcalloc(sizeof (intr_t)); intr->intr_type = INTR_INT; intr->intr_signed = 1; intr->intr_nbits = sz * NBBY; tdp = xcalloc(sizeof (tdesc_t)); tdp->t_name = xstrdup(name); tdp->t_size = sz; tdp->t_id = tid; tdp->t_type = INTRINSIC; tdp->t_intr = intr; tdp->t_flags = TDESC_F_RESOLVED; tdesc_add(dw, tdp); return (tdp); } /* * Manufacture a void type. Used for gcc-emitted stabs, where the lack of a * type reference implies a reference to a void type. A void *, for example * will be represented by a pointer die without a DW_AT_type. CTF requires * that pointer nodes point to something, so we'll create a void for use as * the target. Note that the DWARF data may already create a void type. Ours * would then be a duplicate, but it'll be removed in the self-uniquification * merge performed at the completion of DWARF->tdesc conversion. */ static tdesc_t * tdesc_intr_void(dwarf_t *dw) { if (dw->dw_void == NULL) dw->dw_void = tdesc_intr_common(dw, TID_VOID, "void", 0); return (dw->dw_void); } static tdesc_t * tdesc_intr_long(dwarf_t *dw) { if (dw->dw_long == NULL) { dw->dw_long = tdesc_intr_common(dw, TID_LONG, "long", dw->dw_ptrsz); } return (dw->dw_long); } /* * Used for creating bitfield types. We create a copy of an existing intrinsic, * adjusting the size of the copy to match what the caller requested. The * caller can then use the copy as the type for a bitfield structure member. */ static tdesc_t * tdesc_intr_clone(dwarf_t *dw, tdesc_t *old, size_t bitsz) { tdesc_t *new = xcalloc(sizeof (tdesc_t)); if (!(old->t_flags & TDESC_F_RESOLVED)) { terminate("tdp %u: attempt to make a bit field from an " "unresolved type\n", old->t_id); } new->t_name = xstrdup(old->t_name); new->t_size = old->t_size; new->t_id = mfgtid_next(dw); new->t_type = INTRINSIC; new->t_flags = TDESC_F_RESOLVED; new->t_intr = xcalloc(sizeof (intr_t)); bcopy(old->t_intr, new->t_intr, sizeof (intr_t)); new->t_intr->intr_nbits = bitsz; tdesc_add(dw, new); return (new); } static void tdesc_array_create(dwarf_t *dw, Dwarf_Die dim, tdesc_t *arrtdp, tdesc_t *dimtdp) { Dwarf_Unsigned uval; Dwarf_Signed sval; tdesc_t *ctdp; Dwarf_Die dim2; ardef_t *ar; if ((dim2 = die_sibling(dw, dim)) == NULL) { ctdp = arrtdp; } else if (die_tag(dw, dim2) == DW_TAG_subrange_type) { ctdp = xcalloc(sizeof (tdesc_t)); ctdp->t_id = mfgtid_next(dw); debug(3, "die %llu: creating new type %u for sub-dimension\n", die_off(dw, dim2), ctdp->t_id); tdesc_array_create(dw, dim2, arrtdp, ctdp); } else { terminate("die %llu: unexpected non-subrange node in array\n", die_off(dw, dim2)); } dimtdp->t_type = ARRAY; dimtdp->t_ardef = ar = xcalloc(sizeof (ardef_t)); /* * Array bounds can be signed or unsigned, but there are several kinds * of signless forms (data1, data2, etc) that take their sign from the * routine that is trying to interpret them. That is, data1 can be * either signed or unsigned, depending on whether you use the signed or * unsigned accessor function. GCC will use the signless forms to store * unsigned values which have their high bit set, so we need to try to * read them first as unsigned to get positive values. We could also * try signed first, falling back to unsigned if we got a negative * value. */ if (die_unsigned(dw, dim, DW_AT_upper_bound, &uval, 0)) ar->ad_nelems = uval + 1; else if (die_signed(dw, dim, DW_AT_upper_bound, &sval, 0)) ar->ad_nelems = sval + 1; else ar->ad_nelems = 0; /* * Different compilers use different index types. Force the type to be * a common, known value (long). */ ar->ad_idxtype = tdesc_intr_long(dw); ar->ad_contents = ctdp; if (ar->ad_contents->t_size != 0) { dimtdp->t_size = ar->ad_contents->t_size * ar->ad_nelems; dimtdp->t_flags |= TDESC_F_RESOLVED; } } /* * Create a tdesc from an array node. Some arrays will come with byte size * attributes, and thus can be resolved immediately. Others don't, and will * need to wait until the second pass for resolution. */ static void die_array_create(dwarf_t *dw, Dwarf_Die arr, Dwarf_Off off, tdesc_t *tdp) { tdesc_t *arrtdp = die_lookup_pass1(dw, arr, DW_AT_type); Dwarf_Unsigned uval; Dwarf_Die dim; debug(3, "die %llu: creating array\n", off); if ((dim = die_child(dw, arr)) == NULL || die_tag(dw, dim) != DW_TAG_subrange_type) terminate("die %llu: failed to retrieve array bounds\n", off); tdesc_array_create(dw, dim, arrtdp, tdp); if (die_unsigned(dw, arr, DW_AT_byte_size, &uval, 0)) { tdesc_t *dimtdp; int flags; tdp->t_size = uval; /* * Ensure that sub-dimensions have sizes too before marking * as resolved. */ flags = TDESC_F_RESOLVED; for (dimtdp = tdp->t_ardef->ad_contents; dimtdp->t_type == ARRAY; dimtdp = dimtdp->t_ardef->ad_contents) { if (!(dimtdp->t_flags & TDESC_F_RESOLVED)) { flags = 0; break; } } tdp->t_flags |= flags; } debug(3, "die %llu: array nelems %u size %u\n", off, tdp->t_ardef->ad_nelems, tdp->t_size); } /*ARGSUSED1*/ static int die_array_resolve(tdesc_t *tdp, tdesc_t **tdpp, void *private) { dwarf_t *dw = private; size_t sz; if (tdp->t_flags & TDESC_F_RESOLVED) return (1); debug(3, "trying to resolve array %d (cont %d)\n", tdp->t_id, tdp->t_ardef->ad_contents->t_id); if ((sz = tdesc_size(tdp->t_ardef->ad_contents)) == 0) { debug(3, "unable to resolve array %s (%d) contents %d\n", tdesc_name(tdp), tdp->t_id, tdp->t_ardef->ad_contents->t_id); dw->dw_nunres++; return (1); } tdp->t_size = sz * tdp->t_ardef->ad_nelems; tdp->t_flags |= TDESC_F_RESOLVED; debug(3, "resolved array %d: %u bytes\n", tdp->t_id, tdp->t_size); return (1); } /*ARGSUSED1*/ static int die_array_failed(tdesc_t *tdp, tdesc_t **tdpp, void *private) { tdesc_t *cont = tdp->t_ardef->ad_contents; if (tdp->t_flags & TDESC_F_RESOLVED) return (1); fprintf(stderr, "Array %d: failed to size contents type %s (%d)\n", tdp->t_id, tdesc_name(cont), cont->t_id); return (1); } /* * Most enums (those with members) will be resolved during this first pass. * Others - those without members (see the file comment) - won't be, and will * need to wait until the second pass when they can be matched with their full * definitions. */ static void die_enum_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { Dwarf_Die mem; Dwarf_Unsigned uval; Dwarf_Signed sval; debug(3, "die %llu: creating enum\n", off); tdp->t_type = ENUM; (void) die_unsigned(dw, die, DW_AT_byte_size, &uval, DW_ATTR_REQ); tdp->t_size = uval; if ((mem = die_child(dw, die)) != NULL) { elist_t **elastp = &tdp->t_emem; do { elist_t *el; if (die_tag(dw, mem) != DW_TAG_enumerator) { /* Nested type declaration */ die_create_one(dw, mem); continue; } el = xcalloc(sizeof (elist_t)); el->el_name = die_name(dw, mem); if (die_signed(dw, mem, DW_AT_const_value, &sval, 0)) { el->el_number = sval; } else if (die_unsigned(dw, mem, DW_AT_const_value, &uval, 0)) { el->el_number = uval; } else { terminate("die %llu: enum %llu: member without " "value\n", off, die_off(dw, mem)); } debug(3, "die %llu: enum %llu: created %s = %d\n", off, die_off(dw, mem), el->el_name, el->el_number); *elastp = el; elastp = &el->el_next; } while ((mem = die_sibling(dw, mem)) != NULL); hash_add(dw->dw_enumhash, tdp); tdp->t_flags |= TDESC_F_RESOLVED; if (tdp->t_name != NULL) { iidesc_t *ii = xcalloc(sizeof (iidesc_t)); ii->ii_type = II_SOU; ii->ii_name = xstrdup(tdp->t_name); ii->ii_dtype = tdp; iidesc_add(dw->dw_td->td_iihash, ii); } } } static int die_enum_match(void *arg1, void *arg2) { tdesc_t *tdp = arg1, **fullp = arg2; if (tdp->t_emem != NULL) { *fullp = tdp; return (-1); /* stop the iteration */ } return (0); } /*ARGSUSED1*/ static int die_enum_resolve(tdesc_t *tdp, tdesc_t **tdpp, void *private) { dwarf_t *dw = private; tdesc_t *full = NULL; if (tdp->t_flags & TDESC_F_RESOLVED) return (1); (void) hash_find_iter(dw->dw_enumhash, tdp, die_enum_match, &full); /* * The answer to this one won't change from iteration to iteration, * so don't even try. */ if (full == NULL) { terminate("tdp %u: enum %s has no members\n", tdp->t_id, tdesc_name(tdp)); } debug(3, "tdp %u: enum %s redirected to %u\n", tdp->t_id, tdesc_name(tdp), full->t_id); tdp->t_flags |= TDESC_F_RESOLVED; return (1); } static int die_fwd_map(void *arg1, void *arg2) { tdesc_t *fwd = arg1, *sou = arg2; debug(3, "tdp %u: mapped forward %s to sou %u\n", fwd->t_id, tdesc_name(fwd), sou->t_id); fwd->t_tdesc = sou; return (0); } /* * Structures and unions will never be resolved during the first pass, as we * won't be able to fully determine the member sizes. The second pass, which * have access to sizing information, will be able to complete the resolution. */ static void die_sou_create(dwarf_t *dw, Dwarf_Die str, Dwarf_Off off, tdesc_t *tdp, int type, const char *typename) { Dwarf_Unsigned sz, bitsz, bitoff; Dwarf_Die mem; mlist_t *ml, **mlastp; iidesc_t *ii; tdp->t_type = (die_isdecl(dw, str) ? FORWARD : type); debug(3, "die %llu: creating %s %s\n", off, (tdp->t_type == FORWARD ? "forward decl" : typename), tdesc_name(tdp)); if (tdp->t_type == FORWARD) { hash_add(dw->dw_fwdhash, tdp); return; } (void) hash_find_iter(dw->dw_fwdhash, tdp, die_fwd_map, tdp); (void) die_unsigned(dw, str, DW_AT_byte_size, &sz, DW_ATTR_REQ); tdp->t_size = sz; /* * GCC allows empty SOUs as an extension. */ if ((mem = die_child(dw, str)) == NULL) goto out; mlastp = &tdp->t_members; do { Dwarf_Off memoff = die_off(dw, mem); Dwarf_Half tag = die_tag(dw, mem); Dwarf_Unsigned mloff; if (tag != DW_TAG_member) { /* Nested type declaration */ die_create_one(dw, mem); continue; } debug(3, "die %llu: mem %llu: creating member\n", off, memoff); ml = xcalloc(sizeof (mlist_t)); /* * This could be a GCC anon struct/union member, so we'll allow * an empty name, even though nothing can really handle them * properly. Note that some versions of GCC miss out debug * info for anon structs, though recent versions are fixed (gcc * bug 11816). */ if ((ml->ml_name = die_name(dw, mem)) == NULL) ml->ml_name = ""; ml->ml_type = die_lookup_pass1(dw, mem, DW_AT_type); if (die_mem_offset(dw, mem, DW_AT_data_member_location, &mloff, 0)) { debug(3, "die %llu: got mloff %llx\n", off, (u_longlong_t)mloff); ml->ml_offset = mloff * 8; } if (die_unsigned(dw, mem, DW_AT_bit_size, &bitsz, 0)) ml->ml_size = bitsz; else ml->ml_size = tdesc_bitsize(ml->ml_type); if (die_unsigned(dw, mem, DW_AT_bit_offset, &bitoff, 0)) { #ifdef _BIG_ENDIAN ml->ml_offset += bitoff; #else ml->ml_offset += tdesc_bitsize(ml->ml_type) - bitoff - ml->ml_size; #endif } debug(3, "die %llu: mem %llu: created \"%s\" (off %u sz %u)\n", off, memoff, ml->ml_name, ml->ml_offset, ml->ml_size); *mlastp = ml; mlastp = &ml->ml_next; } while ((mem = die_sibling(dw, mem)) != NULL); /* * GCC will attempt to eliminate unused types, thus decreasing the * size of the emitted dwarf. That is, if you declare a foo_t in your * header, include said header in your source file, and neglect to * actually use (directly or indirectly) the foo_t in the source file, * the foo_t won't make it into the emitted DWARF. So, at least, goes * the theory. * * Occasionally, it'll emit the DW_TAG_structure_type for the foo_t, * and then neglect to emit the members. Strangely, the loner struct * tag will always be followed by a proper nested declaration of * something else. This is clearly a bug, but we're not going to have * time to get it fixed before this goo goes back, so we'll have to work * around it. If we see a no-membered struct with a nested declaration * (i.e. die_child of the struct tag won't be null), we'll ignore it. * Being paranoid, we won't simply remove it from the hash. Instead, * we'll decline to create an iidesc for it, thus ensuring that this * type won't make it into the output file. To be safe, we'll also * change the name. */ if (tdp->t_members == NULL) { const char *old = tdesc_name(tdp); size_t newsz = 7 + strlen(old) + 1; char *new = xmalloc(newsz); (void) snprintf(new, newsz, "orphan %s", old); debug(3, "die %llu: worked around %s %s\n", off, typename, old); if (tdp->t_name != NULL) free(tdp->t_name); tdp->t_name = new; return; } out: if (tdp->t_name != NULL) { ii = xcalloc(sizeof (iidesc_t)); ii->ii_type = II_SOU; ii->ii_name = xstrdup(tdp->t_name); ii->ii_dtype = tdp; iidesc_add(dw->dw_td->td_iihash, ii); } } static void die_struct_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { die_sou_create(dw, die, off, tdp, STRUCT, "struct"); } static void die_union_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { die_sou_create(dw, die, off, tdp, UNION, "union"); } /*ARGSUSED1*/ static int die_sou_resolve(tdesc_t *tdp, tdesc_t **tdpp, void *private) { dwarf_t *dw = private; mlist_t *ml; tdesc_t *mt; if (tdp->t_flags & TDESC_F_RESOLVED) return (1); debug(3, "resolving sou %s\n", tdesc_name(tdp)); for (ml = tdp->t_members; ml != NULL; ml = ml->ml_next) { if (ml->ml_size == 0) { mt = tdesc_basetype(ml->ml_type); if ((ml->ml_size = tdesc_bitsize(mt)) != 0) continue; /* * For empty members, or GCC/C99 flexible array * members, a size of 0 is correct. */ if (mt->t_members == NULL) continue; if (mt->t_type == ARRAY && mt->t_ardef->ad_nelems == 0) continue; dw->dw_nunres++; return (1); } if ((mt = tdesc_basetype(ml->ml_type)) == NULL) { dw->dw_nunres++; return (1); } if (ml->ml_size != 0 && mt->t_type == INTRINSIC && mt->t_intr->intr_nbits != ml->ml_size) { /* * This member is a bitfield, and needs to reference * an intrinsic type with the same width. If the * currently-referenced type isn't of the same width, * we'll copy it, adjusting the width of the copy to * the size we'd like. */ debug(3, "tdp %u: creating bitfield for %d bits\n", tdp->t_id, ml->ml_size); ml->ml_type = tdesc_intr_clone(dw, mt, ml->ml_size); } } tdp->t_flags |= TDESC_F_RESOLVED; return (1); } /*ARGSUSED1*/ static int die_sou_failed(tdesc_t *tdp, tdesc_t **tdpp, void *private) { const char *typename = (tdp->t_type == STRUCT ? "struct" : "union"); mlist_t *ml; if (tdp->t_flags & TDESC_F_RESOLVED) return (1); for (ml = tdp->t_members; ml != NULL; ml = ml->ml_next) { if (ml->ml_size == 0) { fprintf(stderr, "%s %d: failed to size member \"%s\" " "of type %s (%d)\n", typename, tdp->t_id, ml->ml_name, tdesc_name(ml->ml_type), ml->ml_type->t_id); } } return (1); } static void die_funcptr_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { Dwarf_Attribute attr; Dwarf_Half tag; Dwarf_Die arg; fndef_t *fn; int i; debug(3, "die %llu: creating function pointer\n", off); /* * We'll begin by processing any type definition nodes that may be * lurking underneath this one. */ for (arg = die_child(dw, die); arg != NULL; arg = die_sibling(dw, arg)) { if ((tag = die_tag(dw, arg)) != DW_TAG_formal_parameter && tag != DW_TAG_unspecified_parameters) { /* Nested type declaration */ die_create_one(dw, arg); } } if (die_isdecl(dw, die)) { /* * This is a prototype. We don't add prototypes to the * tree, so we're going to drop the tdesc. Unfortunately, * it has already been added to the tree. Nobody will reference * it, though, and it will be leaked. */ return; } fn = xcalloc(sizeof (fndef_t)); tdp->t_type = FUNCTION; if ((attr = die_attr(dw, die, DW_AT_type, 0)) != NULL) { dwarf_dealloc(dw->dw_dw, attr, DW_DLA_ATTR); fn->fn_ret = die_lookup_pass1(dw, die, DW_AT_type); } else { fn->fn_ret = tdesc_intr_void(dw); } /* * Count the arguments to the function, then read them in. */ for (fn->fn_nargs = 0, arg = die_child(dw, die); arg != NULL; arg = die_sibling(dw, arg)) { if ((tag = die_tag(dw, arg)) == DW_TAG_formal_parameter) fn->fn_nargs++; else if (tag == DW_TAG_unspecified_parameters && fn->fn_nargs > 0) fn->fn_vargs = 1; } if (fn->fn_nargs != 0) { debug(3, "die %llu: adding %d argument%s\n", off, fn->fn_nargs, (fn->fn_nargs > 1 ? "s" : "")); fn->fn_args = xcalloc(sizeof (tdesc_t *) * fn->fn_nargs); for (i = 0, arg = die_child(dw, die); arg != NULL && i < fn->fn_nargs; arg = die_sibling(dw, arg)) { if (die_tag(dw, arg) != DW_TAG_formal_parameter) continue; fn->fn_args[i++] = die_lookup_pass1(dw, arg, DW_AT_type); } } tdp->t_fndef = fn; tdp->t_flags |= TDESC_F_RESOLVED; } /* * GCC and DevPro use different names for the base types. While the terms are * the same, they are arranged in a different order. Some terms, such as int, * are implied in one, and explicitly named in the other. Given a base type * as input, this routine will return a common name, along with an intr_t * that reflects said name. */ static intr_t * die_base_name_parse(const char *name, char **newp) { char buf[100]; char *base, *c; int nlong = 0, nshort = 0, nchar = 0, nint = 0; int sign = 1; char fmt = '\0'; intr_t *intr; if (strlen(name) > sizeof (buf) - 1) terminate("base type name \"%s\" is too long\n", name); strncpy(buf, name, sizeof (buf)); for (c = strtok(buf, " "); c != NULL; c = strtok(NULL, " ")) { if (strcmp(c, "signed") == 0) sign = 1; else if (strcmp(c, "unsigned") == 0) sign = 0; else if (strcmp(c, "long") == 0) nlong++; else if (strcmp(c, "char") == 0) { nchar++; fmt = 'c'; } else if (strcmp(c, "short") == 0) nshort++; else if (strcmp(c, "int") == 0) nint++; else { /* * If we don't recognize any of the tokens, we'll tell * the caller to fall back to the dwarf-provided * encoding information. */ return (NULL); } } if (nchar > 1 || nshort > 1 || nint > 1 || nlong > 2) return (NULL); if (nchar > 0) { if (nlong > 0 || nshort > 0 || nint > 0) return (NULL); base = "char"; } else if (nshort > 0) { if (nlong > 0) return (NULL); base = "short"; } else if (nlong > 0) { base = "long"; } else { base = "int"; } intr = xcalloc(sizeof (intr_t)); intr->intr_type = INTR_INT; intr->intr_signed = sign; intr->intr_iformat = fmt; snprintf(buf, sizeof (buf), "%s%s%s", (sign ? "" : "unsigned "), (nlong > 1 ? "long " : ""), base); *newp = xstrdup(buf); return (intr); } typedef struct fp_size_map { size_t fsm_typesz[2]; /* size of {32,64} type */ uint_t fsm_enc[3]; /* CTF_FP_* for {bare,cplx,imagry} type */ } fp_size_map_t; static const fp_size_map_t fp_encodings[] = { { { 4, 4 }, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } }, { { 8, 8 }, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } }, #ifdef __sparc { { 16, 16 }, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } }, #else { { 12, 16 }, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } }, #endif { { 0, 0 } } }; static uint_t die_base_type2enc(dwarf_t *dw, Dwarf_Off off, Dwarf_Signed enc, size_t sz) { const fp_size_map_t *map = fp_encodings; uint_t szidx = dw->dw_ptrsz == sizeof (uint64_t); uint_t mult = 1, col = 0; if (enc == DW_ATE_complex_float) { mult = 2; col = 1; } else if (enc == DW_ATE_imaginary_float || enc == DW_ATE_SUN_imaginary_float) col = 2; while (map->fsm_typesz[szidx] != 0) { if (map->fsm_typesz[szidx] * mult == sz) return (map->fsm_enc[col]); map++; } terminate("die %llu: unrecognized real type size %u\n", off, sz); /*NOTREACHED*/ return (0); } static intr_t * die_base_from_dwarf(dwarf_t *dw, Dwarf_Die base, Dwarf_Off off, size_t sz) { intr_t *intr = xcalloc(sizeof (intr_t)); Dwarf_Signed enc; (void) die_signed(dw, base, DW_AT_encoding, &enc, DW_ATTR_REQ); switch (enc) { case DW_ATE_unsigned: case DW_ATE_address: intr->intr_type = INTR_INT; break; case DW_ATE_unsigned_char: intr->intr_type = INTR_INT; intr->intr_iformat = 'c'; break; case DW_ATE_signed: intr->intr_type = INTR_INT; intr->intr_signed = 1; break; case DW_ATE_signed_char: intr->intr_type = INTR_INT; intr->intr_signed = 1; intr->intr_iformat = 'c'; break; case DW_ATE_boolean: intr->intr_type = INTR_INT; intr->intr_signed = 1; intr->intr_iformat = 'b'; break; case DW_ATE_float: case DW_ATE_complex_float: case DW_ATE_imaginary_float: case DW_ATE_SUN_imaginary_float: case DW_ATE_SUN_interval_float: intr->intr_type = INTR_REAL; intr->intr_signed = 1; intr->intr_fformat = die_base_type2enc(dw, off, enc, sz); break; default: terminate("die %llu: unknown base type encoding 0x%llx\n", off, enc); } return (intr); } static void die_base_create(dwarf_t *dw, Dwarf_Die base, Dwarf_Off off, tdesc_t *tdp) { Dwarf_Unsigned sz; intr_t *intr; char *new; debug(3, "die %llu: creating base type\n", off); /* * The compilers have their own clever (internally inconsistent) ideas * as to what base types should look like. Some times gcc will, for * example, use DW_ATE_signed_char for char. Other times, however, it * will use DW_ATE_signed. Needless to say, this causes some problems * down the road, particularly with merging. We do, however, use the * DWARF idea of type sizes, as this allows us to avoid caring about * the data model. */ (void) die_unsigned(dw, base, DW_AT_byte_size, &sz, DW_ATTR_REQ); if (tdp->t_name == NULL) terminate("die %llu: base type without name\n", off); /* XXX make a name parser for float too */ if ((intr = die_base_name_parse(tdp->t_name, &new)) != NULL) { /* Found it. We'll use the parsed version */ debug(3, "die %llu: name \"%s\" remapped to \"%s\"\n", off, tdesc_name(tdp), new); free(tdp->t_name); tdp->t_name = new; } else { /* * We didn't recognize the type, so we'll create an intr_t * based on the DWARF data. */ debug(3, "die %llu: using dwarf data for base \"%s\"\n", off, tdesc_name(tdp)); intr = die_base_from_dwarf(dw, base, off, sz); } intr->intr_nbits = sz * 8; tdp->t_type = INTRINSIC; tdp->t_intr = intr; tdp->t_size = sz; tdp->t_flags |= TDESC_F_RESOLVED; } static void die_through_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp, int type, const char *typename) { Dwarf_Attribute attr; debug(3, "die %llu: creating %s\n", off, typename); tdp->t_type = type; if ((attr = die_attr(dw, die, DW_AT_type, 0)) != NULL) { dwarf_dealloc(dw->dw_dw, attr, DW_DLA_ATTR); tdp->t_tdesc = die_lookup_pass1(dw, die, DW_AT_type); } else { tdp->t_tdesc = tdesc_intr_void(dw); } if (type == POINTER) tdp->t_size = dw->dw_ptrsz; tdp->t_flags |= TDESC_F_RESOLVED; if (type == TYPEDEF) { iidesc_t *ii = xcalloc(sizeof (iidesc_t)); ii->ii_type = II_TYPE; ii->ii_name = xstrdup(tdp->t_name); ii->ii_dtype = tdp; iidesc_add(dw->dw_td->td_iihash, ii); } } static void die_typedef_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { die_through_create(dw, die, off, tdp, TYPEDEF, "typedef"); } static void die_const_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { die_through_create(dw, die, off, tdp, CONST, "const"); } static void die_pointer_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { die_through_create(dw, die, off, tdp, POINTER, "pointer"); } static void die_restrict_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { die_through_create(dw, die, off, tdp, RESTRICT, "restrict"); } static void die_volatile_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { die_through_create(dw, die, off, tdp, VOLATILE, "volatile"); } /*ARGSUSED3*/ static void die_function_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { Dwarf_Die arg; Dwarf_Half tag; iidesc_t *ii; char *name; debug(3, "die %llu: creating function definition\n", off); /* * We'll begin by processing any type definition nodes that may be * lurking underneath this one. */ for (arg = die_child(dw, die); arg != NULL; arg = die_sibling(dw, arg)) { if ((tag = die_tag(dw, arg)) != DW_TAG_formal_parameter && tag != DW_TAG_variable) { /* Nested type declaration */ die_create_one(dw, arg); } } if (die_isdecl(dw, die) || (name = die_name(dw, die)) == NULL) { /* * We process neither prototypes nor subprograms without * names. */ return; } ii = xcalloc(sizeof (iidesc_t)); ii->ii_type = die_isglobal(dw, die) ? II_GFUN : II_SFUN; ii->ii_name = name; if (ii->ii_type == II_SFUN) ii->ii_owner = xstrdup(dw->dw_cuname); debug(3, "die %llu: function %s is %s\n", off, ii->ii_name, (ii->ii_type == II_GFUN ? "global" : "static")); if (die_attr(dw, die, DW_AT_type, 0) != NULL) ii->ii_dtype = die_lookup_pass1(dw, die, DW_AT_type); else ii->ii_dtype = tdesc_intr_void(dw); for (arg = die_child(dw, die); arg != NULL; arg = die_sibling(dw, arg)) { char *name; debug(3, "die %llu: looking at sub member at %llu\n", off, die_off(dw, die)); if (die_tag(dw, arg) != DW_TAG_formal_parameter) continue; if ((name = die_name(dw, arg)) == NULL) { terminate("die %llu: func arg %d has no name\n", off, ii->ii_nargs + 1); } if (strcmp(name, "...") == 0) { free(name); ii->ii_vargs = 1; continue; } ii->ii_nargs++; } if (ii->ii_nargs > 0) { int i; debug(3, "die %llu: function has %d argument%s\n", off, ii->ii_nargs, (ii->ii_nargs == 1 ? "" : "s")); ii->ii_args = xcalloc(sizeof (tdesc_t) * ii->ii_nargs); for (arg = die_child(dw, die), i = 0; arg != NULL && i < ii->ii_nargs; arg = die_sibling(dw, arg)) { if (die_tag(dw, arg) != DW_TAG_formal_parameter) continue; ii->ii_args[i++] = die_lookup_pass1(dw, arg, DW_AT_type); } } iidesc_add(dw->dw_td->td_iihash, ii); } /*ARGSUSED3*/ static void die_variable_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { iidesc_t *ii; char *name; debug(3, "die %llu: creating object definition\n", off); if (die_isdecl(dw, die) || (name = die_name(dw, die)) == NULL) return; /* skip prototypes and nameless objects */ ii = xcalloc(sizeof (iidesc_t)); ii->ii_type = die_isglobal(dw, die) ? II_GVAR : II_SVAR; ii->ii_name = name; ii->ii_dtype = die_lookup_pass1(dw, die, DW_AT_type); if (ii->ii_type == II_SVAR) ii->ii_owner = xstrdup(dw->dw_cuname); iidesc_add(dw->dw_td->td_iihash, ii); } /*ARGSUSED2*/ static int die_fwd_resolve(tdesc_t *fwd, tdesc_t **fwdp, void *private) { if (fwd->t_flags & TDESC_F_RESOLVED) return (1); if (fwd->t_tdesc != NULL) { debug(3, "tdp %u: unforwarded %s\n", fwd->t_id, tdesc_name(fwd)); *fwdp = fwd->t_tdesc; } fwd->t_flags |= TDESC_F_RESOLVED; return (1); } /*ARGSUSED*/ static void die_lexblk_descend(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp) { Dwarf_Die child = die_child(dw, die); if (child != NULL) die_create(dw, child); } /* * Used to map the die to a routine which can parse it, using the tag to do the * mapping. While the processing of most tags entails the creation of a tdesc, * there are a few which don't - primarily those which result in the creation of * iidescs which refer to existing tdescs. */ #define DW_F_NOTDP 0x1 /* Don't create a tdesc for the creator */ typedef struct die_creator { Dwarf_Half dc_tag; uint16_t dc_flags; void (*dc_create)(dwarf_t *, Dwarf_Die, Dwarf_Off, tdesc_t *); } die_creator_t; static const die_creator_t die_creators[] = { { DW_TAG_array_type, 0, die_array_create }, { DW_TAG_enumeration_type, 0, die_enum_create }, { DW_TAG_lexical_block, DW_F_NOTDP, die_lexblk_descend }, { DW_TAG_pointer_type, 0, die_pointer_create }, { DW_TAG_structure_type, 0, die_struct_create }, { DW_TAG_subroutine_type, 0, die_funcptr_create }, { DW_TAG_typedef, 0, die_typedef_create }, { DW_TAG_union_type, 0, die_union_create }, { DW_TAG_base_type, 0, die_base_create }, { DW_TAG_const_type, 0, die_const_create }, { DW_TAG_subprogram, DW_F_NOTDP, die_function_create }, { DW_TAG_variable, DW_F_NOTDP, die_variable_create }, { DW_TAG_volatile_type, 0, die_volatile_create }, { DW_TAG_restrict_type, 0, die_restrict_create }, - { 0, NULL } + { 0, 0, NULL } }; static const die_creator_t * die_tag2ctor(Dwarf_Half tag) { const die_creator_t *dc; for (dc = die_creators; dc->dc_create != NULL; dc++) { if (dc->dc_tag == tag) return (dc); } return (NULL); } static void die_create_one(dwarf_t *dw, Dwarf_Die die) { Dwarf_Off off = die_off(dw, die); const die_creator_t *dc; Dwarf_Half tag; tdesc_t *tdp; debug(3, "die %llu: create_one\n", off); if (off > dw->dw_maxoff) { terminate("illegal die offset %llu (max %llu)\n", off, dw->dw_maxoff); } tag = die_tag(dw, die); if ((dc = die_tag2ctor(tag)) == NULL) { debug(2, "die %llu: ignoring tag type %x\n", off, tag); return; } if ((tdp = tdesc_lookup(dw, off)) == NULL && !(dc->dc_flags & DW_F_NOTDP)) { tdp = xcalloc(sizeof (tdesc_t)); tdp->t_id = off; tdesc_add(dw, tdp); } if (tdp != NULL) tdp->t_name = die_name(dw, die); dc->dc_create(dw, die, off, tdp); } static void die_create(dwarf_t *dw, Dwarf_Die die) { do { die_create_one(dw, die); } while ((die = die_sibling(dw, die)) != NULL); } static tdtrav_cb_f die_resolvers[] = { NULL, NULL, /* intrinsic */ NULL, /* pointer */ die_array_resolve, /* array */ NULL, /* function */ die_sou_resolve, /* struct */ die_sou_resolve, /* union */ die_enum_resolve, /* enum */ die_fwd_resolve, /* forward */ NULL, /* typedef */ NULL, /* typedef unres */ NULL, /* volatile */ NULL, /* const */ NULL, /* restrict */ }; static tdtrav_cb_f die_fail_reporters[] = { NULL, NULL, /* intrinsic */ NULL, /* pointer */ die_array_failed, /* array */ NULL, /* function */ die_sou_failed, /* struct */ die_sou_failed, /* union */ NULL, /* enum */ NULL, /* forward */ NULL, /* typedef */ NULL, /* typedef unres */ NULL, /* volatile */ NULL, /* const */ NULL, /* restrict */ }; static void die_resolve(dwarf_t *dw) { int last = -1; int pass = 0; do { pass++; dw->dw_nunres = 0; (void) iitraverse_hash(dw->dw_td->td_iihash, &dw->dw_td->td_curvgen, NULL, NULL, die_resolvers, dw); debug(3, "resolve: pass %d, %u left\n", pass, dw->dw_nunres); if (dw->dw_nunres == last) { fprintf(stderr, "%s: failed to resolve the following " "types:\n", progname); (void) iitraverse_hash(dw->dw_td->td_iihash, &dw->dw_td->td_curvgen, NULL, NULL, die_fail_reporters, dw); terminate("failed to resolve types\n"); } last = dw->dw_nunres; } while (dw->dw_nunres != 0); } /* * Any object containing a function or object symbol at any scope should also * contain DWARF data. */ static boolean_t should_have_dwarf(Elf *elf) { Elf_Scn *scn = NULL; Elf_Data *data = NULL; GElf_Shdr shdr; GElf_Sym sym; uint32_t symdx = 0; size_t nsyms = 0; boolean_t found = B_FALSE; while ((scn = elf_nextscn(elf, scn)) != NULL) { gelf_getshdr(scn, &shdr); if (shdr.sh_type == SHT_SYMTAB) { found = B_TRUE; break; } } if (!found) terminate("cannot convert stripped objects\n"); data = elf_getdata(scn, NULL); nsyms = shdr.sh_size / shdr.sh_entsize; for (symdx = 0; symdx < nsyms; symdx++) { gelf_getsym(data, symdx, &sym); if ((GELF_ST_TYPE(sym.st_info) == STT_FUNC) || (GELF_ST_TYPE(sym.st_info) == STT_TLS) || (GELF_ST_TYPE(sym.st_info) == STT_OBJECT)) { char *name; name = elf_strptr(elf, shdr.sh_link, sym.st_name); /* Studio emits these local symbols regardless */ if ((strcmp(name, "Bbss.bss") != 0) && (strcmp(name, "Ttbss.bss") != 0) && (strcmp(name, "Ddata.data") != 0) && (strcmp(name, "Ttdata.data") != 0) && (strcmp(name, "Drodata.rodata") != 0)) return (B_TRUE); } } return (B_FALSE); } /*ARGSUSED*/ int dw_read(tdata_t *td, Elf *elf, const char *filename) { Dwarf_Unsigned abboff, hdrlen, nxthdr; Dwarf_Half vers, addrsz; Dwarf_Die cu, child; dwarf_t dw; char *prod = NULL; int rc; bzero(&dw, sizeof (dwarf_t)); dw.dw_td = td; dw.dw_ptrsz = elf_ptrsz(elf); dw.dw_mfgtid_last = TID_MFGTID_BASE; dw.dw_tidhash = hash_new(TDESC_HASH_BUCKETS, tdesc_idhash, tdesc_idcmp); dw.dw_fwdhash = hash_new(TDESC_HASH_BUCKETS, tdesc_namehash, tdesc_namecmp); dw.dw_enumhash = hash_new(TDESC_HASH_BUCKETS, tdesc_namehash, tdesc_namecmp); if ((rc = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dw.dw_dw, &dw.dw_err)) == DW_DLV_NO_ENTRY) { if (should_have_dwarf(elf)) { errno = ENOENT; return (-1); } else { return (0); } } else if (rc != DW_DLV_OK) { if (dwarf_errno(dw.dw_err) == DW_DLE_DEBUG_INFO_NULL) { /* * There's no type data in the DWARF section, but * libdwarf is too clever to handle that properly. */ return (0); } terminate("failed to initialize DWARF: %s\n", dwarf_errmsg(dw.dw_err)); } if ((rc = dwarf_next_cu_header(dw.dw_dw, &hdrlen, &vers, &abboff, &addrsz, &nxthdr, &dw.dw_err)) != DW_DLV_OK) terminate("file does not contain valid DWARF data: %s\n", dwarf_errmsg(dw.dw_err)); /* * Some compilers emit no DWARF for empty files, others emit an empty * compilation unit. */ if ((cu = die_sibling(&dw, NULL)) == NULL || ((child = die_child(&dw, cu)) == NULL) && should_have_dwarf(elf)) { terminate("file does not contain dwarf type data " "(try compiling with -g)\n"); } else if (child == NULL) { return (0); } dw.dw_maxoff = nxthdr - 1; if (dw.dw_maxoff > TID_FILEMAX) terminate("file contains too many types\n"); debug(1, "DWARF version: %d\n", vers); if (vers != DWARF_VERSION) { terminate("file contains incompatible version %d DWARF code " "(version 2 required)\n", vers); } if (die_string(&dw, cu, DW_AT_producer, &prod, 0)) { debug(1, "DWARF emitter: %s\n", prod); free(prod); } if ((dw.dw_cuname = die_name(&dw, cu)) != NULL) { char *base = xstrdup(basename(dw.dw_cuname)); free(dw.dw_cuname); dw.dw_cuname = base; debug(1, "CU name: %s\n", dw.dw_cuname); } die_create(&dw, child); if ((rc = dwarf_next_cu_header(dw.dw_dw, &hdrlen, &vers, &abboff, &addrsz, &nxthdr, &dw.dw_err)) != DW_DLV_NO_ENTRY) terminate("multiple compilation units not supported\n"); (void) dwarf_finish(dw.dw_dw, &dw.dw_err); die_resolve(&dw); cvt_fixups(td, dw.dw_ptrsz); /* leak the dwarf_t */ return (0); } Index: vendor/illumos/dist/tools/ctf/cvt/output.c =================================================================== --- vendor/illumos/dist/tools/ctf/cvt/output.c (revision 279821) +++ vendor/illumos/dist/tools/ctf/cvt/output.c (revision 279822) @@ -1,722 +1,720 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Routines for preparing tdata trees for conversion into CTF data, and * for placing the resulting data into an output file. */ #include #include #include #include #include #include #include #include #include #include "ctftools.h" #include "list.h" #include "memory.h" #include "traverse.h" #include "symbol.h" typedef struct iidesc_match { int iim_fuzzy; iidesc_t *iim_ret; char *iim_name; char *iim_file; uchar_t iim_bind; } iidesc_match_t; static int burst_iitypes(void *data, void *arg) { iidesc_t *ii = data; iiburst_t *iiburst = arg; switch (ii->ii_type) { case II_GFUN: case II_SFUN: case II_GVAR: case II_SVAR: if (!(ii->ii_flags & IIDESC_F_USED)) return (0); break; default: break; } ii->ii_dtype->t_flags |= TDESC_F_ISROOT; (void) iitraverse_td(ii, iiburst->iib_tdtd); return (1); } /*ARGSUSED1*/ static int save_type_by_id(tdesc_t *tdp, tdesc_t **tdpp, void *private) { iiburst_t *iiburst = private; /* * Doing this on every node is horribly inefficient, but given that * we may be suppressing some types, we can't trust nextid in the * tdata_t. */ if (tdp->t_id > iiburst->iib_maxtypeid) iiburst->iib_maxtypeid = tdp->t_id; slist_add(&iiburst->iib_types, tdp, tdesc_idcmp); return (1); } static tdtrav_cb_f burst_types_cbs[] = { NULL, save_type_by_id, /* intrinsic */ save_type_by_id, /* pointer */ save_type_by_id, /* array */ save_type_by_id, /* function */ save_type_by_id, /* struct */ save_type_by_id, /* union */ save_type_by_id, /* enum */ save_type_by_id, /* forward */ save_type_by_id, /* typedef */ tdtrav_assert, /* typedef_unres */ save_type_by_id, /* volatile */ save_type_by_id, /* const */ save_type_by_id /* restrict */ }; static iiburst_t * iiburst_new(tdata_t *td, int max) { iiburst_t *iiburst = xcalloc(sizeof (iiburst_t)); iiburst->iib_td = td; iiburst->iib_funcs = xcalloc(sizeof (iidesc_t *) * max); iiburst->iib_nfuncs = 0; iiburst->iib_objts = xcalloc(sizeof (iidesc_t *) * max); iiburst->iib_nobjts = 0; return (iiburst); } static void iiburst_types(iiburst_t *iiburst) { tdtrav_data_t tdtd; tdtrav_init(&tdtd, &iiburst->iib_td->td_curvgen, NULL, burst_types_cbs, NULL, (void *)iiburst); iiburst->iib_tdtd = &tdtd; (void) hash_iter(iiburst->iib_td->td_iihash, burst_iitypes, iiburst); } static void iiburst_free(iiburst_t *iiburst) { free(iiburst->iib_funcs); free(iiburst->iib_objts); list_free(iiburst->iib_types, NULL, NULL); free(iiburst); } /* * See if this iidesc matches the ELF symbol data we pass in. * * A fuzzy match is where we have a local symbol matching the name of a * global type description. This is common when a mapfile is used for a * DSO, but we don't accept it by default. * * A weak fuzzy match is when a weak symbol was resolved and matched to * a global type description. */ static int matching_iidesc(iidesc_t *iidesc, iidesc_match_t *match) { if (streq(iidesc->ii_name, match->iim_name) == 0) return (0); switch (iidesc->ii_type) { case II_GFUN: case II_GVAR: if (match->iim_bind == STB_GLOBAL) { match->iim_ret = iidesc; return (-1); } else if (match->iim_fuzzy && match->iim_ret == NULL) { match->iim_ret = iidesc; /* continue to look for strong match */ return (0); } break; case II_SFUN: case II_SVAR: if (match->iim_bind == STB_LOCAL && match->iim_file != NULL && streq(iidesc->ii_owner, match->iim_file)) { match->iim_ret = iidesc; return (-1); } break; } return (0); } static iidesc_t * find_iidesc(tdata_t *td, iidesc_match_t *match) { match->iim_ret = NULL; iter_iidescs_by_name(td, match->iim_name, (int (*)())matching_iidesc, match); return (match->iim_ret); } /* * If we have a weak symbol, attempt to find the strong symbol it will * resolve to. Note: the code where this actually happens is in * sym_process() in cmd/sgs/libld/common/syms.c * * Finding the matching symbol is unfortunately not trivial. For a * symbol to be a candidate, it must: * * - have the same type (function, object) * - have the same value (address) * - have the same size * - not be another weak symbol * - belong to the same section (checked via section index) * * If such a candidate is global, then we assume we've found it. The * linker generates the symbol table such that the curfile might be * incorrect; this is OK for global symbols, since find_iidesc() doesn't * need to check for the source file for the symbol. * * We might have found a strong local symbol, where the curfile is * accurate and matches that of the weak symbol. We assume this is a * reasonable match. * * If we've got a local symbol with a non-matching curfile, there are * two possibilities. Either this is a completely different symbol, or * it's a once-global symbol that was scoped to local via a mapfile. In * the latter case, curfile is likely inaccurate since the linker does * not preserve the needed curfile in the order of the symbol table (see * the comments about locally scoped symbols in libld's update_osym()). * As we can't tell this case from the former one, we use this symbol * iff no other matching symbol is found. * * What we really need here is a SUNW section containing weak<->strong * mappings that we can consume. */ static int check_for_weak(GElf_Sym *weak, char const *weakfile, Elf_Data *data, int nent, Elf_Data *strdata, GElf_Sym *retsym, char **curfilep) { char *curfile = NULL; char *tmpfile; GElf_Sym tmpsym; int candidate = 0; int i; if (GELF_ST_BIND(weak->st_info) != STB_WEAK) return (0); for (i = 0; i < nent; i++) { GElf_Sym sym; uchar_t type; if (gelf_getsym(data, i, &sym) == NULL) continue; type = GELF_ST_TYPE(sym.st_info); if (type == STT_FILE) curfile = (char *)strdata->d_buf + sym.st_name; if (GELF_ST_TYPE(weak->st_info) != type || weak->st_value != sym.st_value) continue; if (weak->st_size != sym.st_size) continue; if (GELF_ST_BIND(sym.st_info) == STB_WEAK) continue; if (sym.st_shndx != weak->st_shndx) continue; if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && (curfile == NULL || weakfile == NULL || strcmp(curfile, weakfile) != 0)) { candidate = 1; tmpfile = curfile; tmpsym = sym; continue; } *curfilep = curfile; *retsym = sym; return (1); } if (candidate) { *curfilep = tmpfile; *retsym = tmpsym; return (1); } return (0); } /* * When we've found the underlying symbol's type description * for a weak symbol, we need to copy it and rename it to match * the weak symbol. We also need to add it to the td so it's * handled along with the others later. */ static iidesc_t * copy_from_strong(tdata_t *td, GElf_Sym *sym, iidesc_t *strongdesc, const char *weakname, const char *weakfile) { iidesc_t *new = iidesc_dup_rename(strongdesc, weakname, weakfile); uchar_t type = GELF_ST_TYPE(sym->st_info); switch (type) { case STT_OBJECT: new->ii_type = II_GVAR; break; case STT_FUNC: new->ii_type = II_GFUN; break; } hash_add(td->td_iihash, new); return (new); } /* * Process the symbol table of the output file, associating each symbol * with a type description if possible, and sorting them into functions * and data, maintaining symbol table order. */ static iiburst_t * sort_iidescs(Elf *elf, const char *file, tdata_t *td, int fuzzymatch, int dynsym) { iiburst_t *iiburst; Elf_Scn *scn; GElf_Shdr shdr; Elf_Data *data, *strdata; int i, stidx; int nent; iidesc_match_t match; match.iim_fuzzy = fuzzymatch; match.iim_file = NULL; if ((stidx = findelfsecidx(elf, file, dynsym ? ".dynsym" : ".symtab")) < 0) terminate("%s: Can't open symbol table\n", file); scn = elf_getscn(elf, stidx); data = elf_getdata(scn, NULL); gelf_getshdr(scn, &shdr); nent = shdr.sh_size / shdr.sh_entsize; scn = elf_getscn(elf, shdr.sh_link); strdata = elf_getdata(scn, NULL); iiburst = iiburst_new(td, nent); for (i = 0; i < nent; i++) { GElf_Sym sym; iidesc_t **tolist; GElf_Sym ssym; iidesc_match_t smatch; int *curr; iidesc_t *iidesc; if (gelf_getsym(data, i, &sym) == NULL) elfterminate(file, "Couldn't read symbol %d", i); match.iim_name = (char *)strdata->d_buf + sym.st_name; match.iim_bind = GELF_ST_BIND(sym.st_info); switch (GELF_ST_TYPE(sym.st_info)) { case STT_FILE: match.iim_file = match.iim_name; continue; case STT_OBJECT: tolist = iiburst->iib_objts; curr = &iiburst->iib_nobjts; break; case STT_FUNC: tolist = iiburst->iib_funcs; curr = &iiburst->iib_nfuncs; break; default: continue; } if (ignore_symbol(&sym, match.iim_name)) continue; iidesc = find_iidesc(td, &match); if (iidesc != NULL) { tolist[*curr] = iidesc; iidesc->ii_flags |= IIDESC_F_USED; (*curr)++; continue; } if (!check_for_weak(&sym, match.iim_file, data, nent, strdata, &ssym, &smatch.iim_file)) { (*curr)++; continue; } smatch.iim_fuzzy = fuzzymatch; smatch.iim_name = (char *)strdata->d_buf + ssym.st_name; smatch.iim_bind = GELF_ST_BIND(ssym.st_info); debug(3, "Weak symbol %s resolved to %s\n", match.iim_name, smatch.iim_name); iidesc = find_iidesc(td, &smatch); if (iidesc != NULL) { tolist[*curr] = copy_from_strong(td, &sym, iidesc, match.iim_name, match.iim_file); tolist[*curr]->ii_flags |= IIDESC_F_USED; } (*curr)++; } /* * Stabs are generated for every function declared in a given C source * file. When converting an object file, we may encounter a stab that * has no symbol table entry because the optimizer has decided to omit * that item (for example, an unreferenced static function). We may * see iidescs that do not have an associated symtab entry, and so * we do not write records for those functions into the CTF data. * All others get marked as a root by this function. */ iiburst_types(iiburst); /* * By not adding some of the functions and/or objects, we may have * caused some types that were referenced solely by those * functions/objects to be suppressed. This could cause a label, * generated prior to the evisceration, to be incorrect. Find the * highest type index, and change the label indicies to be no higher * than this value. */ tdata_label_newmax(td, iiburst->iib_maxtypeid); return (iiburst); } static void write_file(Elf *src, const char *srcname, Elf *dst, const char *dstname, caddr_t ctfdata, size_t ctfsize, int flags) { GElf_Ehdr sehdr, dehdr; Elf_Scn *sscn, *dscn; Elf_Data *sdata, *ddata; GElf_Shdr shdr; GElf_Word symtab_type; int symtab_idx = -1; off_t new_offset = 0; off_t ctfnameoff = 0; int dynsym = (flags & CTF_USE_DYNSYM); int keep_stabs = (flags & CTF_KEEP_STABS); int *secxlate; int srcidx, dstidx; int curnmoff = 0; int changing = 0; int pad; int i; - if (gelf_newehdr(dst, gelf_getclass(src)) == NULL) + if (gelf_newehdr(dst, gelf_getclass(src)) == 0) elfterminate(dstname, "Cannot copy ehdr to temp file"); gelf_getehdr(src, &sehdr); memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr)); gelf_update_ehdr(dst, &dehdr); symtab_type = dynsym ? SHT_DYNSYM : SHT_SYMTAB; /* * Neither the existing stab 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 (sehdr.e_phnum != 0) { (void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT); - if (gelf_newphdr(dst, sehdr.e_phnum) == NULL) + if (gelf_newphdr(dst, sehdr.e_phnum) == 0) elfterminate(dstname, "Cannot make phdrs in temp file"); for (i = 0; i < sehdr.e_phnum; i++) { GElf_Phdr phdr; gelf_getphdr(src, i, &phdr); gelf_update_phdr(dst, i, &phdr); } } secxlate = xmalloc(sizeof (int) * sehdr.e_shnum); for (srcidx = dstidx = 0; srcidx < sehdr.e_shnum; srcidx++) { Elf_Scn *scn = elf_getscn(src, srcidx); GElf_Shdr shdr; char *sname; gelf_getshdr(scn, &shdr); sname = elf_strptr(src, sehdr.e_shstrndx, shdr.sh_name); if (sname == NULL) { elfterminate(srcname, "Can't find string at %u", shdr.sh_name); } if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) { secxlate[srcidx] = -1; } else if (!keep_stabs && (strncmp(sname, ".stab", 5) == 0 || strncmp(sname, ".debug", 6) == 0 || strncmp(sname, ".rel.debug", 10) == 0 || strncmp(sname, ".rela.debug", 11) == 0)) { secxlate[srcidx] = -1; } else if (dynsym && shdr.sh_type == SHT_SYMTAB) { /* * If we're building CTF against the dynsym, * we'll rip out the symtab so debuggers aren't * confused. */ secxlate[srcidx] = -1; } else { secxlate[srcidx] = dstidx++; curnmoff += strlen(sname) + 1; } new_offset = (off_t)dehdr.e_phoff; } for (srcidx = 1; srcidx < sehdr.e_shnum; srcidx++) { char *sname; sscn = elf_getscn(src, srcidx); gelf_getshdr(sscn, &shdr); if (secxlate[srcidx] == -1) { changing = 1; continue; } dscn = elf_newscn(dst); /* * 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 && sehdr.e_phnum != 0) { pad = new_offset % shdr.sh_addralign; if (pad) 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, sehdr.e_shstrndx, shdr.sh_name); if (sname == NULL) { elfterminate(srcname, "Can't find string at %u", shdr.sh_name); } if ((sdata = elf_getdata(sscn, NULL)) == NULL) elfterminate(srcname, "Cannot get sect %s data", sname); if ((ddata = elf_newdata(dscn)) == NULL) elfterminate(dstname, "Can't make sect %s data", sname); bcopy(sdata, ddata, sizeof (Elf_Data)); if (srcidx == sehdr.e_shstrndx) { char seclen = strlen(CTF_ELF_SCN_NAME); ddata->d_buf = xmalloc(ddata->d_size + shdr.sh_size + seclen + 1); bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); 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 (sehdr.e_phnum != 0) changing = 1; } if (shdr.sh_type == symtab_type && shdr.sh_entsize != 0) { int nsym = shdr.sh_size / shdr.sh_entsize; symtab_idx = secxlate[srcidx]; ddata->d_buf = xmalloc(shdr.sh_size); 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); gelf_update_sym(ddata, i, &sym); } } } - if (gelf_update_shdr(dscn, &shdr) == NULL) + if (gelf_update_shdr(dscn, &shdr) == 0) elfterminate(dstname, "Cannot update sect %s", sname); new_offset = (off_t)shdr.sh_offset; if (shdr.sh_type != SHT_NOBITS) new_offset += shdr.sh_size; } if (symtab_idx == -1) { terminate("%s: Cannot find %s section\n", srcname, dynsym ? "SHT_DYNSYM" : "SHT_SYMTAB"); } /* Add the ctf section */ dscn = elf_newscn(dst); gelf_getshdr(dscn, &shdr); shdr.sh_name = ctfnameoff; shdr.sh_type = SHT_PROGBITS; shdr.sh_size = ctfsize; shdr.sh_link = symtab_idx; shdr.sh_addralign = 4; if (changing && sehdr.e_phnum != 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; } ddata = elf_newdata(dscn); ddata->d_buf = ctfdata; ddata->d_size = ctfsize; ddata->d_align = shdr.sh_addralign; gelf_update_shdr(dscn, &shdr); /* update the section header location */ if (sehdr.e_phnum != 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 */ dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx]; gelf_update_ehdr(dst, &dehdr); if (elf_update(dst, ELF_C_WRITE) < 0) elfterminate(dstname, "Cannot finalize temp file"); free(secxlate); } static caddr_t make_ctf_data(tdata_t *td, Elf *elf, const char *file, size_t *lenp, int flags) { iiburst_t *iiburst; caddr_t data; iiburst = sort_iidescs(elf, file, td, flags & CTF_FUZZY_MATCH, flags & CTF_USE_DYNSYM); data = ctf_gen(iiburst, lenp, flags & CTF_COMPRESS); iiburst_free(iiburst); return (data); } void write_ctf(tdata_t *td, const char *curname, const char *newname, int flags) { struct stat st; Elf *elf = NULL; Elf *telf = NULL; caddr_t data; size_t len; int fd = -1; int tfd = -1; (void) elf_version(EV_CURRENT); if ((fd = open(curname, O_RDONLY)) < 0 || fstat(fd, &st) < 0) terminate("%s: Cannot open for re-reading", curname); if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) elfterminate(curname, "Cannot re-read"); if ((tfd = open(newname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) terminate("Cannot open temp file %s for writing", newname); if ((telf = elf_begin(tfd, ELF_C_WRITE, NULL)) == NULL) elfterminate(curname, "Cannot write"); data = make_ctf_data(td, elf, curname, &len, flags); write_file(elf, curname, telf, newname, data, len, flags); free(data); elf_end(telf); elf_end(elf); (void) close(fd); (void) close(tfd); }