Index: head/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c =================================================================== --- head/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c (revision 253660) +++ head/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c (revision 253661) @@ -1,1384 +1,1395 @@ /* * 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. */ static char *curfile; #define CTF_BUF_CHUNK_SIZE (64 * 1024) #define RES_BUF_CHUNK_SIZE (64 * 1024) +static int ntypes=0; /* The number of types. */ + 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 */ }; /* * Macros to reverse byte order */ #define BSWAP_8(x) ((x) & 0xff) #define BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8)) #define BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16)) #define SWAP_16(x) (x) = BSWAP_16(x) #define SWAP_32(x) (x) = BSWAP_32(x) static int target_requires_swap; /*PRINTFLIKE1*/ static void parseterminate(const 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); } static 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; } static 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); } static void ctf_buf_free(ctf_buf_t *b) { strtab_destroy(&b->ctb_strtab); free(b->ctb_base); free(b); } static uint_t ctf_buf_cur(ctf_buf_t *b) { return (b->ctb_ptr - b->ctb_base); } static void ctf_buf_write(ctf_buf_t *b, void const *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 const *)p + len; n -= len; } } static int write_label(void *arg1, void *arg2) { labelent_t *le = arg1; ctf_buf_t *b = arg2; ctf_lblent_t ctl; ctl.ctl_label = strtab_insert(&b->ctb_strtab, le->le_name); ctl.ctl_typeidx = le->le_idx; if (target_requires_swap) { SWAP_32(ctl.ctl_label); SWAP_32(ctl.ctl_typeidx); } 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)); if (target_requires_swap) { SWAP_16(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; if (target_requires_swap) { SWAP_16(fdata[0]); SWAP_16(fdata[1]); } ctf_buf_write(b, fdata, sizeof (fdata)); for (i = 0; i < idp->ii_nargs; i++) { id = idp->ii_args[i]->t_id; if (target_requires_swap) { SWAP_16(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); if (target_requires_swap) { SWAP_32(ctt->ctt_name); SWAP_16(ctt->ctt_info); SWAP_16(ctt->ctt_size); SWAP_32(ctt->ctt_lsizehi); SWAP_32(ctt->ctt_lsizelo); } ctf_buf_write(b, ctt, sizeof (*ctt)); } else { ctf_stype_t *cts = (ctf_stype_t *)ctt; cts->ctt_size = (ushort_t)size; if (target_requires_swap) { SWAP_32(cts->ctt_name); SWAP_16(cts->ctt_info); SWAP_16(cts->ctt_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; if (target_requires_swap) { SWAP_32(cts->ctt_name); SWAP_16(cts->ctt_info); SWAP_16(cts->ctt_size); } ctf_buf_write(b, cts, sizeof (*cts)); } static int write_type(void *arg1, void *arg2) { tdesc_t *tp = arg1; ctf_buf_t *b = arg2; 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); if (target_requires_swap) { SWAP_32(data); } 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; if (target_requires_swap) { SWAP_16(cta.cta_contents); SWAP_16(cta.cta_index); SWAP_32(cta.cta_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; if (target_requires_swap) { SWAP_32(ctm.ctm_name); SWAP_16(ctm.ctm_type); SWAP_16(ctm.ctm_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); if (target_requires_swap) { SWAP_32(ctlm.ctlm_name); SWAP_16(ctlm.ctlm_type); SWAP_32(ctlm.ctlm_offsethi); SWAP_32(ctlm.ctlm_offsetlo); } 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) { warning("enum %s has too many values: %d > %d\n", tdesc_name(tp), i, CTF_MAX_VLEN); 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 && i > 0; 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; if (target_requires_swap) { SWAP_32(cte.cte_name); SWAP_32(cte.cte_value); } ctf_buf_write(b, &cte, sizeof (cte)); i--; } 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 < (int) tp->t_fndef->fn_nargs; i++) { id = tp->t_fndef->fn_args[i]->t_id; if (target_requires_swap) { SWAP_16(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(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 = 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(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; target_requires_swap = do_compress & CTF_SWAP_BYTES; do_compress &= ~CTF_SWAP_BYTES; /* * 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, 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, 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); if (target_requires_swap) { SWAP_16(h.cth_preamble.ctp_magic); SWAP_32(h.cth_parlabel); SWAP_32(h.cth_parname); SWAP_32(h.cth_lbloff); SWAP_32(h.cth_objtoff); SWAP_32(h.cth_funcoff); SWAP_32(h.cth_typeoff); SWAP_32(h.cth_stroff); SWAP_32(h.cth_strlen); } /* * 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); } static 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) { void *v = (void *) dptr; ctf_type_t *ctt = v; 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 = NULL; ctf_lblent_t *ctl; void *v = (void *) buf; for (ctl = v; (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) { void *v = (void *) dptr; ushort_t id = *((ushort_t *)v); 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) { void *v = (void *) dptr; info = *((ushort_t *)v); 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; } v = (void *) dptr; retid = *((ushort_t *)v); 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) { v = (void *) dptr; ushort_t id = *((ushort_t *)v); 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); void *v = (void *) dptr; ctt = v; 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; v = (void *) dptr; data = *((uint_t *)v); 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; v = (void *) dptr; data = *((uint_t *)v); 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; v = (void *) dptr; cta = v; 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)) { v = (void *) dptr; ctf_member_t *ctm = v; 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; + if (ctm->ctm_type > ntypes) { + parseterminate("Invalid member type ctm_type=%d", + ctm->ctm_type); + } } } else { for (i = 0, mpp = &tdp->t_members; i < vlen; i++, mpp = &((*mpp)->ml_next)) { v = (void *) dptr; ctf_lmember_t *ctlm = v; 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; + if (ctlm->ctlm_type > ntypes) { + parseterminate("Invalid lmember type ctlm_type=%d", + ctlm->ctlm_type); + } } } *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)) { v = (void *) dptr; cte = v; 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]; v = (void *) (dptr + (sizeof (ushort_t) * (vlen - 1))); if (vlen > 0 && *(ushort_t *)v == 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++) { v = (void *) dptr; argid = *(ushort_t *)v; 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; + + ntypes = count_types(h, buf); /* 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 (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"); void *v = (void *) buf; h = v; 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: head/cddl/contrib/opensolaris/tools/ctf/cvt/ctftools.h =================================================================== --- head/cddl/contrib/opensolaris/tools/ctf/cvt/ctftools.h (revision 253660) +++ head/cddl/contrib/opensolaris/tools/ctf/cvt/ctftools.h (revision 253661) @@ -1,454 +1,454 @@ /* * 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. */ #ifndef _CTFTOOLS_H #define _CTFTOOLS_H #pragma ident "%Z%%M% %I% %E% SMI" /* * Functions and data structures used in the manipulation of stabs and CTF data */ #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #include "list.h" #include "hash.h" #ifndef DEBUG_LEVEL #define DEBUG_LEVEL 0 #endif #ifndef DEBUG_PARSE #define DEBUG_PARSE 0 #endif #ifndef DEBUG_STREAM #define DEBUG_STREAM stderr #endif #ifndef MAX #define MAX(a, b) ((a) < (b) ? (b) : (a)) #endif #ifndef MIN #define MIN(a, b) ((a) > (b) ? (b) : (a)) #endif #define TRUE 1 #define FALSE 0 #define CTF_ELF_SCN_NAME ".SUNW_ctf" #define CTF_LABEL_LASTIDX -1 #define CTF_DEFAULT_LABEL "*** No Label Provided ***" /* * Default hash sizes */ #define TDATA_LAYOUT_HASH_SIZE 8191 /* A tdesc hash based on layout */ #define TDATA_ID_HASH_SIZE 997 /* A tdesc hash based on type id */ #define IIDESC_HASH_SIZE 8191 /* Hash of iidesc's */ /* * The default function argument array size. We'll realloc the array larger * if we need to, but we want a default value that will allow us to avoid * reallocation in the common case. */ #define FUNCARG_DEF 5 extern const char *progname; extern int debug_level; extern int debug_parse; extern char *curhdr; /* * This is a partial copy of the stab.h that DevPro includes with their * compiler. */ typedef struct stab { uint32_t n_strx; uint8_t n_type; int8_t n_other; int16_t n_desc; uint32_t n_value; } stab_t; #define N_GSYM 0x20 /* global symbol: name,,0,type,0 */ #define N_FUN 0x24 /* procedure: name,,0,linenumber,0 */ #define N_STSYM 0x26 /* static symbol: name,,0,type,0 or section relative */ #define N_LCSYM 0x28 /* .lcomm symbol: name,,0,type,0 or section relative */ #define N_ROSYM 0x2c /* ro_data: name,,0,type,0 or section relative */ #define N_OPT 0x3c /* compiler options */ #define N_RSYM 0x40 /* register sym: name,,0,type,register */ #define N_SO 0x64 /* source file name: name,,0,0,0 */ #define N_LSYM 0x80 /* local sym: name,,0,type,offset */ #define N_SOL 0x84 /* #included file name: name,,0,0,0 */ #define N_PSYM 0xa0 /* parameter: name,,0,type,offset */ #define N_LBRAC 0xc0 /* left bracket: 0,,0,nesting level,function relative */ #define N_RBRAC 0xe0 /* right bracket: 0,,0,nesting level,func relative */ #define N_BINCL 0x82 /* header file: name,,0,0,0 */ #define N_EINCL 0xa2 /* end of include file */ /* * Nodes in the type tree * * Each node consists of a single tdesc_t, with one of several auxiliary * structures linked in via the `data' union. */ /* The type of tdesc_t node */ typedef enum stabtype { STABTYPE_FIRST, /* do not use */ INTRINSIC, POINTER, ARRAY, FUNCTION, STRUCT, UNION, ENUM, FORWARD, TYPEDEF, TYPEDEF_UNRES, VOLATILE, CONST, RESTRICT, STABTYPE_LAST /* do not use */ } stabtype_t; typedef struct tdesc tdesc_t; /* Auxiliary structure for array tdesc_t */ typedef struct ardef { tdesc_t *ad_contents; tdesc_t *ad_idxtype; uint_t ad_nelems; } ardef_t; /* Auxiliary structure for structure/union tdesc_t */ typedef struct mlist { int ml_offset; /* Offset from start of structure (in bits) */ - int ml_size; /* Member size (in bits) */ + uint_t ml_size; /* Member size (in bits) */ char *ml_name; /* Member name */ struct tdesc *ml_type; /* Member type */ struct mlist *ml_next; /* Next member */ } mlist_t; /* Auxiliary structure for enum tdesc_t */ typedef struct elist { char *el_name; int el_number; struct elist *el_next; } elist_t; /* Auxiliary structure for intrinsics (integers and reals) */ typedef enum { INTR_INT, INTR_REAL } intrtype_t; typedef struct intr { intrtype_t intr_type; int intr_signed; union { char _iformat; int _fformat; } _u; int intr_offset; int intr_nbits; } intr_t; #define intr_iformat _u._iformat #define intr_fformat _u._fformat typedef struct fnarg { char *fna_name; struct tdesc *fna_type; } fnarg_t; #define FN_F_GLOBAL 0x1 #define FN_F_VARARGS 0x2 typedef struct fndef { struct tdesc *fn_ret; uint_t fn_nargs; tdesc_t **fn_args; uint_t fn_vargs; } fndef_t; typedef int32_t tid_t; /* * The tdesc_t (Type DESCription) is the basic node type used in the stabs data * structure. Each data node gets a tdesc structure. Each node is linked into * a directed graph (think of it as a tree with multiple roots and multiple * leaves), with the root nodes at the top, and intrinsics at the bottom. The * root nodes, which are pointed to by iidesc nodes, correspond to the types, * globals, and statics defined by the stabs. */ struct tdesc { char *t_name; tdesc_t *t_next; /* Name hash next pointer */ tid_t t_id; tdesc_t *t_hash; /* ID hash next pointer */ stabtype_t t_type; int t_size; /* Size in bytes of object represented by this node */ union { intr_t *intr; /* int, real */ tdesc_t *tdesc; /* ptr, typedef, vol, const, restr */ ardef_t *ardef; /* array */ mlist_t *members; /* struct, union */ elist_t *emem; /* enum */ fndef_t *fndef; /* function - first is return type */ } t_data; int t_flags; int t_vgen; /* Visitation generation (see traverse.c) */ int t_emark; /* Equality mark (see equiv_cb() in merge.c) */ }; #define t_intr t_data.intr #define t_tdesc t_data.tdesc #define t_ardef t_data.ardef #define t_members t_data.members #define t_emem t_data.emem #define t_fndef t_data.fndef #define TDESC_F_ISROOT 0x1 /* Has an iidesc_t (see below) */ #define TDESC_F_GLOBAL 0x2 #define TDESC_F_RESOLVED 0x4 /* * iidesc_t (Interesting Item DESCription) nodes point to tdesc_t nodes that * correspond to "interesting" stabs. A stab is interesting if it defines a * global or static variable, a global or static function, or a data type. */ typedef enum iitype { II_NOT = 0, II_GFUN, /* Global function */ II_SFUN, /* Static function */ II_GVAR, /* Global variable */ II_SVAR, /* Static variable */ II_PSYM, /* Function argument */ II_SOU, /* Struct or union */ II_TYPE /* Type (typedef) */ } iitype_t; typedef struct iidesc { iitype_t ii_type; char *ii_name; tdesc_t *ii_dtype; char *ii_owner; /* File that defined this node */ int ii_flags; /* Function arguments (if any) */ int ii_nargs; tdesc_t **ii_args; int ii_vargs; /* Function uses varargs */ } iidesc_t; #define IIDESC_F_USED 0x1 /* Write this iidesc out */ /* * labelent_t nodes identify labels and corresponding type ranges associated * with them. The label in a given labelent_t is associated with types with * ids <= le_idx. */ typedef struct labelent { char *le_name; int le_idx; } labelent_t; /* * The tdata_t (Type DATA) structure contains or references all type data for * a given file or, during merging, several files. */ typedef struct tdata { int td_curemark; /* Equality mark (see merge.c) */ int td_curvgen; /* Visitation generation (see traverse.c) */ int td_nextid; /* The ID for the next tdesc_t created */ hash_t *td_iihash; /* The iidesc_t nodes for this file */ hash_t *td_layouthash; /* The tdesc nodes, hashed by structure */ hash_t *td_idhash; /* The tdesc nodes, hashed by type id */ list_t *td_fwdlist; /* All forward declaration tdesc nodes */ char *td_parlabel; /* Top label uniq'd against in parent */ char *td_parname; /* Basename of parent */ list_t *td_labels; /* Labels and their type ranges */ pthread_mutex_t td_mergelock; int td_ref; } tdata_t; /* * By design, the iidesc hash is heterogeneous. The CTF emitter, on the * other hand, needs to be able to access the elements of the list by type, * and in a specific sorted order. An iiburst holds these elements in that * order. (A burster is a machine that separates carbon-copy forms) */ typedef struct iiburst { int iib_nfuncs; int iib_curfunc; iidesc_t **iib_funcs; int iib_nobjts; int iib_curobjt; iidesc_t **iib_objts; list_t *iib_types; int iib_maxtypeid; tdata_t *iib_td; struct tdtrav_data *iib_tdtd; /* tdtrav_data_t */ } iiburst_t; typedef struct ctf_buf ctf_buf_t; typedef struct symit_data symit_data_t; /* fixup_tdescs.c */ void cvt_fixstabs(tdata_t *); void cvt_fixups(tdata_t *, size_t); /* ctf.c */ caddr_t ctf_gen(iiburst_t *, size_t *, int); tdata_t *ctf_load(char *, caddr_t, size_t, symit_data_t *, char *); /* iidesc.c */ iidesc_t *iidesc_new(char *); int iidesc_hash(int, void *); void iter_iidescs_by_name(tdata_t *, const char *, int (*)(void *, void *), void *); iidesc_t *iidesc_dup(iidesc_t *); iidesc_t *iidesc_dup_rename(iidesc_t *, char const *, char const *); void iidesc_add(hash_t *, iidesc_t *); void iidesc_free(void *, void *); int iidesc_count_type(void *, void *); void iidesc_stats(hash_t *); int iidesc_dump(iidesc_t *); /* input.c */ typedef enum source_types { SOURCE_NONE = 0, SOURCE_UNKNOWN = 1, SOURCE_C = 2, SOURCE_S = 4 } source_types_t; source_types_t built_source_types(Elf *, const char *); int count_files(char **, int); int read_ctf(char **, int, char *, int (*)(tdata_t *, char *, void *), void *, int); int read_ctf_save_cb(tdata_t *, char *, void *); symit_data_t *symit_new(Elf *, const char *); void symit_reset(symit_data_t *); char *symit_curfile(symit_data_t *); GElf_Sym *symit_next(symit_data_t *, int); char *symit_name(symit_data_t *); void symit_free(symit_data_t *); /* merge.c */ void merge_into_master(tdata_t *, tdata_t *, tdata_t *, int); /* output.c */ #define CTF_FUZZY_MATCH 0x1 /* match local symbols to global CTF */ #define CTF_USE_DYNSYM 0x2 /* use .dynsym not .symtab */ #define CTF_COMPRESS 0x4 /* compress CTF output */ #define CTF_KEEP_STABS 0x8 /* keep .stabs sections */ #define CTF_SWAP_BYTES 0x10 /* target byte order is different from host */ void write_ctf(tdata_t *, const char *, const char *, int); /* parse.c */ void parse_init(tdata_t *); void parse_finish(tdata_t *); int parse_stab(stab_t *, char *, iidesc_t **); tdesc_t *lookup(int); tdesc_t *lookupname(const char *); void check_hash(void); void resolve_typed_bitfields(void); /* stabs.c */ int stabs_read(tdata_t *, Elf *, char *); /* dwarf.c */ int dw_read(tdata_t *, Elf *, char *); const char *dw_tag2str(uint_t); /* tdata.c */ tdata_t *tdata_new(void); void tdata_free(tdata_t *); void tdata_build_hashes(tdata_t *td); const char *tdesc_name(tdesc_t *); int tdesc_idhash(int, void *); int tdesc_idcmp(void *, void *); int tdesc_namehash(int, void *); int tdesc_namecmp(void *, void *); int tdesc_layouthash(int, void *); int tdesc_layoutcmp(void *, void *); void tdesc_free(tdesc_t *); void tdata_label_add(tdata_t *, const char *, int); labelent_t *tdata_label_top(tdata_t *); int tdata_label_find(tdata_t *, char *); void tdata_label_free(tdata_t *); void tdata_merge(tdata_t *, tdata_t *); void tdata_label_newmax(tdata_t *, int); /* util.c */ int streq(const char *, const char *); int findelfsecidx(Elf *, const char *, const char *); size_t elf_ptrsz(Elf *); char *mktmpname(const char *, const char *); void terminate(const char *, ...); void aborterr(const char *, ...); void set_terminate_cleanup(void (*)(void)); void elfterminate(const char *, const char *, ...); void warning(const char *, ...); void vadebug(int, const char *, va_list); void debug(int, const char *, ...); void watch_dump(int); void watch_set(void *, int); #ifdef __cplusplus } #endif #endif /* _CTFTOOLS_H */ Index: head/cddl/contrib/opensolaris/tools/ctf/cvt/dwarf.c =================================================================== --- head/cddl/contrib/opensolaris/tools/ctf/cvt/dwarf.c (revision 253660) +++ head/cddl/contrib/opensolaris/tools/ctf/cvt/dwarf.c (revision 253661) @@ -1,1848 +1,1886 @@ /* * 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. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * 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 #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_Off 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; void *tdp; tmpl.t_id = tid; if (hash_find(dw->dw_tidhash, &tmpl, &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 int die_signed(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Signed *valp, int req) { *valp = 0; if (dwarf_attrval_signed(die, name, valp, &dw->dw_err) != DWARF_E_NONE) { if (req) terminate("die %llu: failed to get signed: %s\n", die_off(dw, die), dwarf_errmsg(&dw->dw_err)); return (0); } return (1); } static int die_unsigned(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Unsigned *valp, int req) { *valp = 0; if (dwarf_attrval_unsigned(die, name, valp, &dw->dw_err) != DWARF_E_NONE) { if (req) terminate("die %llu: failed to get unsigned: %s\n", die_off(dw, die), dwarf_errmsg(&dw->dw_err)); return (0); } return (1); } static int die_bool(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Bool *valp, int req) { *valp = 0; if (dwarf_attrval_flag(die, name, valp, &dw->dw_err) != DWARF_E_NONE) { if (req) terminate("die %llu: failed to get flag: %s\n", die_off(dw, die), dwarf_errmsg(&dw->dw_err)); return (0); } return (1); } static int die_string(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, char **strp, int req) { const char *str = NULL; if (dwarf_attrval_string(die, name, &str, &dw->dw_err) != DWARF_E_NONE || str == NULL) { if (req) terminate("die %llu: failed to get string: %s\n", die_off(dw, die), dwarf_errmsg(&dw->dw_err)); else *strp = NULL; return (0); } else *strp = xstrdup(str); return (1); } static Dwarf_Off die_attr_ref(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name) { Dwarf_Off off; if (dwarf_attrval_unsigned(die, name, &off, &dw->dw_err) != DWARF_E_NONE) { terminate("die %llu: failed to get ref: %s\n", die_off(dw, die), dwarf_errmsg(&dw->dw_err)); } 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 __unused) { Dwarf_Locdesc *loc = NULL; Dwarf_Signed locnum = 0; if (dwarf_locdesc(die, name, &loc, &locnum, &dw->dw_err) != DW_DLV_OK) return (0); 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; if (loc != NULL) if (dwarf_locdesc_free(loc, &dw->dw_err) != DW_DLV_OK) terminate("die %llu: cannot free location descriptor: %s\n", die_off(dw, die), dwarf_errmsg(&dw->dw_err)); 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 = NULL; 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 <%llx>: creating array\n", off, 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; + /* Check for bogus gcc DW_AT_byte_size attribute */ + if (uval == (unsigned)-1) { + printf("dwarf.c:%s() working around bogus -1 DW_AT_byte_size\n", + __func__); + uval = 0; + } + 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 <%llx>: array nelems %u size %u\n", off, off, tdp->t_ardef->ad_nelems, tdp->t_size); } /*ARGSUSED1*/ static int die_array_resolve(tdesc_t *tdp, tdesc_t **tdpp __unused, 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 __unused, void *private __unused) { 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); + /* Check for bogus gcc DW_AT_byte_size attribute */ + if (uval == (unsigned)-1) { + printf("dwarf.c:%s() working around bogus -1 DW_AT_byte_size\n", + __func__); + uval = 0; + } 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 __unused, 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_Unsigned sz, bitsz, bitoff, maxsz=0; 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 = NULL; ml->ml_type = die_lookup_pass1(dw, mem, DW_AT_type); + debug(3, "die_sou_create(): ml_type = %p t_id = %d\n", + ml->ml_type, ml->ml_type->t_id); 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)) { #if BYTE_ORDER == _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; + + /* Find the size of the largest member to work around a gcc + * bug. See GCC Bugzilla 35998. + */ + if (maxsz < ml->ml_size) + maxsz = ml->ml_size; + } while ((mem = die_sibling(dw, mem)) != NULL); + /* See if we got a bogus DW_AT_byte_size. GCC will sometimes + * emit this. + */ + if (sz == (unsigned)-1) { + printf("dwarf.c:%s() working around bogus -1 DW_AT_byte_size\n", + __func__); + tdp->t_size = maxsz / 8; /* maxsz is in bits, t_size is bytes */ + } + /* * 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 __unused, 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) { + mt->t_intr->intr_nbits != (int)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 __unused, void *private __unused) { 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 <%x>: failed to size member \"%s\" " "of type %s (%d <%x>)\n", typename, tdp->t_id, tdp->t_id, ml->ml_name, tdesc_name(ml->ml_type), ml->ml_type->t_id, 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 <%llx>: creating function pointer\n", off, 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) { 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 < (int) 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 const *base; char *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 }, { 0, 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 #if defined(sun) || enc == DW_ATE_SUN_imaginary_float #endif ) 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: #if defined(sun) case DW_ATE_SUN_imaginary_float: case DW_ATE_SUN_interval_float: #endif 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); + + /* Check for bogus gcc DW_AT_byte_size attribute */ + if (sz == (unsigned)-1) { + printf("dwarf.c:%s() working around bogus -1 DW_AT_byte_size\n", + __func__); + sz = 0; + } 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 <%llx>: creating %s type %d\n", off, off, typename, type); tdp->t_type = type; if ((attr = die_attr(dw, die, DW_AT_type, 0)) != NULL) { 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 __unused) { Dwarf_Die arg; Dwarf_Half tag; iidesc_t *ii; char *name; debug(3, "die %llu <%llx>: creating function definition\n", off, 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 *name1; 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 ((name1 = die_name(dw, arg)) == NULL) { terminate("die %llu: func arg %d has no name\n", off, ii->ii_nargs + 1); } if (strcmp(name1, "...") == 0) { free(name1); 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 __unused) { 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 __unused) { 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 __unused, tdesc_t *tdp __unused) { 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, 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 <%llx>: create_one\n", off, 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 ((int) 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); } /*ARGSUSED*/ int dw_read(tdata_t *td, Elf *elf, char *filename __unused) { Dwarf_Unsigned abboff, hdrlen, nxthdr; Dwarf_Half vers, addrsz; Dwarf_Die cu = 0; Dwarf_Die child = 0; 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, &dw.dw_dw, &dw.dw_err)) == DW_DLV_NO_ENTRY) { errno = ENOENT; return (-1); } 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("rc = %d %s\n", rc, dwarf_errmsg(&dw.dw_err)); if ((cu = die_sibling(&dw, NULL)) == NULL) terminate("file does not contain dwarf type data " "(try compiling with -g)\n"); 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); } if ((child = die_child(&dw, cu)) != NULL) 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: head/cddl/contrib/opensolaris/tools/ctf/cvt/st_parse.c =================================================================== --- head/cddl/contrib/opensolaris/tools/ctf/cvt/st_parse.c (revision 253660) +++ head/cddl/contrib/opensolaris/tools/ctf/cvt/st_parse.c (revision 253661) @@ -1,1210 +1,1210 @@ /* * 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 (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. */ /* * This file is a sewer. */ #include #include #include #include #include #include #include #include #include "ctftools.h" #include "memory.h" #include "list.h" #define HASH(NUM) ((int)(NUM & (BUCKETS - 1))) #define BUCKETS 128 #define TYPEPAIRMULT 10000 #define MAKETYPEID(file, num) ((file) * TYPEPAIRMULT + num) #define TYPEFILE(tid) ((tid) / TYPEPAIRMULT) #define TYPENUM(tid) ((tid) % TYPEPAIRMULT) #define expected(a, b, c) _expected(a, b, c, __LINE__) static int faketypenumber = 100000000; static tdesc_t *hash_table[BUCKETS]; static tdesc_t *name_table[BUCKETS]; static list_t *typedbitfldmems; static void reset(void); static jmp_buf resetbuf; static char *soudef(char *cp, stabtype_t type, tdesc_t **rtdp); static void enumdef(char *cp, tdesc_t **rtdp); static int compute_sum(const char *w); static char *number(char *cp, int *n); static char *name(char *cp, char **w); static char *id(char *cp, int *h); static char *whitesp(char *cp); static void addhash(tdesc_t *tdp, int num); static int tagadd(char *w, int h, tdesc_t *tdp); static char *tdefdecl(char *cp, int h, tdesc_t **rtdp); static char *intrinsic(char *cp, tdesc_t **rtdp); static char *arraydef(char *cp, tdesc_t **rtdp); int debug_parse = DEBUG_PARSE; /*PRINTFLIKE3*/ static void parse_debug(int level, char *cp, const char *fmt, ...) { va_list ap; char buf[1024]; char tmp[32]; int i; if (level > debug_level || !debug_parse) return; if (cp != NULL) { for (i = 0; i < 30; i++) { if (cp[i] == '\0') break; if (!iscntrl(cp[i])) tmp[i] = cp[i]; } tmp[i] = '\0'; (void) snprintf(buf, sizeof (buf), "%s [cp='%s']\n", fmt, tmp); } else { strcpy(buf, fmt); strcat(buf, "\n"); } va_start(ap, fmt); vadebug(level, buf, ap); va_end(ap); } /* Report unexpected syntax in stabs. */ static void _expected( const char *who, /* what function, or part thereof, is reporting */ const char *what, /* what was expected */ const char *where, /* where we were in the line of input */ int line) { fprintf(stderr, "%s, expecting \"%s\" at \"%s\"\n", who, what, where); fprintf(stderr, "code line: %d, file %s\n", line, (curhdr ? curhdr : "NO FILE")); reset(); } /*ARGSUSED*/ void parse_init(tdata_t *td __unused) { int i; for (i = 0; i < BUCKETS; i++) { hash_table[i] = NULL; name_table[i] = NULL; } if (typedbitfldmems != NULL) { list_free(typedbitfldmems, NULL, NULL); typedbitfldmems = NULL; } } void parse_finish(tdata_t *td) { td->td_nextid = ++faketypenumber; } static tdesc_t * unres_new(int tid) { tdesc_t *tdp; tdp = xcalloc(sizeof (*tdp)); tdp->t_type = TYPEDEF_UNRES; tdp->t_id = tid; return (tdp); } static char * read_tid(char *cp, tdesc_t **tdpp) { tdesc_t *tdp; int tid; cp = id(cp, &tid); assert(tid != 0); if (*cp == '=') { if (!(cp = tdefdecl(cp + 1, tid, &tdp))) return (NULL); if (tdp->t_id && tdp->t_id != tid) { tdesc_t *ntdp = xcalloc(sizeof (*ntdp)); ntdp->t_type = TYPEDEF; ntdp->t_tdesc = tdp; tdp = ntdp; } addhash(tdp, tid); } else if ((tdp = lookup(tid)) == NULL) tdp = unres_new(tid); *tdpp = tdp; return (cp); } static iitype_t parse_fun(char *cp, iidesc_t *ii) { iitype_t iitype = 0; tdesc_t *tdp; tdesc_t **args = NULL; int nargs = 0; int va = 0; /* * name:P prototype * name:F global function * name:f static function */ switch (*cp++) { case 'P': iitype = II_NOT; /* not interesting */ break; case 'F': iitype = II_GFUN; break; case 'f': iitype = II_SFUN; break; default: expected("parse_nfun", "[PfF]", cp - 1); } if (!(cp = read_tid(cp, &tdp))) return (-1); if (*cp) args = xmalloc(sizeof (tdesc_t *) * FUNCARG_DEF); while (*cp && *++cp) { if (*cp == '0') { va = 1; continue; } nargs++; if (nargs > FUNCARG_DEF) args = xrealloc(args, sizeof (tdesc_t *) * nargs); if (!(cp = read_tid(cp, &args[nargs - 1]))) return (-1); } ii->ii_type = iitype; ii->ii_dtype = tdp; ii->ii_nargs = nargs; ii->ii_args = args; ii->ii_vargs = va; return (iitype); } static iitype_t parse_sym(char *cp, iidesc_t *ii) { tdesc_t *tdp; iitype_t iitype = 0; /* * name:G global variable * name:S static variable */ switch (*cp++) { case 'G': iitype = II_GVAR; break; case 'S': iitype = II_SVAR; break; case 'p': iitype = II_PSYM; break; case '(': cp--; /*FALLTHROUGH*/ case 'r': case 'V': iitype = II_NOT; /* not interesting */ break; default: expected("parse_sym", "[GprSV(]", cp - 1); } if (!(cp = read_tid(cp, &tdp))) return (-1); ii->ii_type = iitype; ii->ii_dtype = tdp; return (iitype); } static iitype_t parse_type(char *cp, iidesc_t *ii) { tdesc_t *tdp, *ntdp; int tid; if (*cp++ != 't') expected("parse_type", "t (type)", cp - 1); cp = id(cp, &tid); if ((tdp = lookup(tid)) == NULL) { if (*cp++ != '=') expected("parse_type", "= (definition)", cp - 1); (void) tdefdecl(cp, tid, &tdp); if (tdp->t_id == tid) { assert(tdp->t_type != TYPEDEF); assert(!lookup(tdp->t_id)); if (!streq(tdp->t_name, ii->ii_name)) { ntdp = xcalloc(sizeof (*ntdp)); ntdp->t_name = xstrdup(ii->ii_name); ntdp->t_type = TYPEDEF; ntdp->t_tdesc = tdp; tdp->t_id = faketypenumber++; tdp = ntdp; } } else if (tdp->t_id == 0) { assert(tdp->t_type == FORWARD || tdp->t_type == INTRINSIC); if (tdp->t_name && !streq(tdp->t_name, ii->ii_name)) { ntdp = xcalloc(sizeof (*ntdp)); ntdp->t_name = xstrdup(ii->ii_name); ntdp->t_type = TYPEDEF; ntdp->t_tdesc = tdp; tdp->t_id = faketypenumber++; tdp = ntdp; } } else if (tdp->t_id != tid) { ntdp = xcalloc(sizeof (*ntdp)); ntdp->t_name = xstrdup(ii->ii_name); ntdp->t_type = TYPEDEF; ntdp->t_tdesc = tdp; tdp = ntdp; } if (tagadd(ii->ii_name, tid, tdp) < 0) return (-1); } ii->ii_type = II_TYPE; ii->ii_dtype = tdp; return (II_TYPE); } static iitype_t parse_sou(char *cp, iidesc_t *idp) { tdesc_t *rtdp; int tid; if (*cp++ != 'T') expected("parse_sou", "T (sou)", cp - 1); cp = id(cp, &tid); if (*cp++ != '=') expected("parse_sou", "= (definition)", cp - 1); parse_debug(1, NULL, "parse_sou: declaring '%s'", idp->ii_name ? idp->ii_name : "(anon)"); if ((rtdp = lookup(tid)) != NULL) { if (idp->ii_name != NULL) { if (rtdp->t_name != NULL && strcmp(rtdp->t_name, idp->ii_name) != 0) { tdesc_t *tdp; tdp = xcalloc(sizeof (*tdp)); tdp->t_name = xstrdup(idp->ii_name); tdp->t_type = TYPEDEF; tdp->t_tdesc = rtdp; addhash(tdp, tid); /* for *(x,y) types */ parse_debug(3, NULL, " %s defined as %s(%d)", idp->ii_name, tdesc_name(rtdp), tid); } else if (rtdp->t_name == NULL) { rtdp->t_name = xstrdup(idp->ii_name); addhash(rtdp, tid); } } } else { rtdp = xcalloc(sizeof (*rtdp)); rtdp->t_name = idp->ii_name ? xstrdup(idp->ii_name) : NULL; addhash(rtdp, tid); } switch (*cp++) { case 's': (void) soudef(cp, STRUCT, &rtdp); break; case 'u': (void) soudef(cp, UNION, &rtdp); break; case 'e': enumdef(cp, &rtdp); break; default: expected("parse_sou", "", cp - 1); break; } idp->ii_type = II_SOU; idp->ii_dtype = rtdp; return (II_SOU); } int parse_stab(stab_t *stab, char *cp, iidesc_t **iidescp) { iidesc_t *ii = NULL; iitype_t (*parse)(char *, iidesc_t *); int rc; /* * set up for reset() */ if (setjmp(resetbuf)) return (-1); cp = whitesp(cp); ii = iidesc_new(NULL); cp = name(cp, &ii->ii_name); switch (stab->n_type) { case N_FUN: parse = parse_fun; break; case N_LSYM: if (*cp == 't') parse = parse_type; else if (*cp == 'T') parse = parse_sou; else parse = parse_sym; break; case N_GSYM: case N_LCSYM: case N_PSYM: case N_ROSYM: case N_RSYM: case N_STSYM: parse = parse_sym; break; default: parse_debug(1, cp, "Unknown stab type %#x", stab->n_type); bzero(&resetbuf, sizeof (resetbuf)); return (-1); } rc = parse(cp, ii); bzero(&resetbuf, sizeof (resetbuf)); if (rc < 0 || ii->ii_type == II_NOT) { iidesc_free(ii, NULL); return (rc); } *iidescp = ii; return (1); } /* * Check if we have this node in the hash table already */ tdesc_t * lookup(int h) { int bucket = HASH(h); tdesc_t *tdp = hash_table[bucket]; while (tdp != NULL) { if (tdp->t_id == h) return (tdp); tdp = tdp->t_hash; } return (NULL); } static char * whitesp(char *cp) { char c; for (c = *cp++; isspace(c); c = *cp++) ; --cp; return (cp); } static char * name(char *cp, char **w) { char *new, *orig, c; int len; orig = cp; c = *cp++; if (c == ':') *w = NULL; else if (isalpha(c) || strchr("_.$#", c)) { for (c = *cp++; isalnum(c) || strchr(" _.$#", c); c = *cp++) ; if (c != ':') reset(); len = cp - orig; new = xmalloc(len); while (orig < cp - 1) *new++ = *orig++; *new = '\0'; *w = new - (len - 1); } else reset(); return (cp); } static char * number(char *cp, int *n) { char *next; *n = (int)strtol(cp, &next, 10); if (next == cp) expected("number", "", cp); return (next); } static char * id(char *cp, int *h) { int n1, n2; if (*cp == '(') { /* SunPro style */ cp++; cp = number(cp, &n1); if (*cp++ != ',') expected("id", ",", cp - 1); cp = number(cp, &n2); if (*cp++ != ')') expected("id", ")", cp - 1); *h = MAKETYPEID(n1, n2); } else if (isdigit(*cp)) { /* gcc style */ cp = number(cp, &n1); *h = n1; } else { expected("id", "(/0-9", cp); } return (cp); } static int tagadd(char *w, int h, tdesc_t *tdp) { tdesc_t *otdp; tdp->t_name = w; if (!(otdp = lookup(h))) addhash(tdp, h); else if (otdp != tdp) { warning("duplicate entry\n"); warning(" old: %s %d (%d,%d)\n", tdesc_name(otdp), otdp->t_type, TYPEFILE(otdp->t_id), TYPENUM(otdp->t_id)); warning(" new: %s %d (%d,%d)\n", tdesc_name(tdp), tdp->t_type, TYPEFILE(tdp->t_id), TYPENUM(tdp->t_id)); return (-1); } return (0); } static char * tdefdecl(char *cp, int h, tdesc_t **rtdp) { tdesc_t *ntdp; char *w; int c, h2; char type; parse_debug(3, cp, "tdefdecl h=%d", h); /* Type codes */ switch (type = *cp) { case 'b': /* integer */ case 'R': /* fp */ cp = intrinsic(cp, rtdp); break; case '(': /* equiv to another type */ cp = id(cp, &h2); ntdp = lookup(h2); if (ntdp != NULL && *cp == '=') { if (ntdp->t_type == FORWARD && *(cp + 1) == 'x') { /* * The 6.2 compiler, and possibly others, will * sometimes emit the same stab for a forward * declaration twice. That is, "(1,2)=xsfoo:" * will sometimes show up in two different * places. This is, of course, quite fun. We * want CTF to work in spite of the compiler, * so we'll let this one through. */ char *c2 = cp + 2; char *nm; if (!strchr("sue", *c2++)) { expected("tdefdecl/x-redefine", "[sue]", c2 - 1); } c2 = name(c2, &nm); if (strcmp(nm, ntdp->t_name) != 0) { terminate("Stabs error: Attempt to " "redefine type (%d,%d) as " "something else: %s\n", TYPEFILE(h2), TYPENUM(h2), c2 - 1); } free(nm); h2 = faketypenumber++; ntdp = NULL; } else { terminate("Stabs error: Attempting to " "redefine type (%d,%d)\n", TYPEFILE(h2), TYPENUM(h2)); } } if (ntdp == NULL) { /* if that type isn't defined yet */ if (*cp != '=') { /* record it as unresolved */ parse_debug(3, NULL, "tdefdecl unres type %d", h2); *rtdp = calloc(sizeof (**rtdp), 1); (*rtdp)->t_type = TYPEDEF_UNRES; (*rtdp)->t_id = h2; break; } else cp++; /* define a new type */ cp = tdefdecl(cp, h2, rtdp); if ((*rtdp)->t_id && (*rtdp)->t_id != h2) { ntdp = calloc(sizeof (*ntdp), 1); ntdp->t_type = TYPEDEF; ntdp->t_tdesc = *rtdp; *rtdp = ntdp; } addhash(*rtdp, h2); } else { /* that type is already defined */ if (ntdp->t_type != TYPEDEF || ntdp->t_name != NULL) { *rtdp = ntdp; } else { parse_debug(3, NULL, "No duplicate typedef anon for ref"); *rtdp = ntdp; } } break; case '*': ntdp = NULL; cp = tdefdecl(cp + 1, h, &ntdp); if (ntdp == NULL) expected("tdefdecl/*", "id", cp); if (!ntdp->t_id) ntdp->t_id = faketypenumber++; *rtdp = xcalloc(sizeof (**rtdp)); (*rtdp)->t_type = POINTER; (*rtdp)->t_size = 0; (*rtdp)->t_id = h; (*rtdp)->t_tdesc = ntdp; break; case 'f': cp = tdefdecl(cp + 1, h, &ntdp); *rtdp = xcalloc(sizeof (**rtdp)); (*rtdp)->t_type = FUNCTION; (*rtdp)->t_size = 0; (*rtdp)->t_id = h; (*rtdp)->t_fndef = xcalloc(sizeof (fndef_t)); /* * The 6.1 compiler will sometimes generate incorrect stabs for * function pointers (it'll get the return type wrong). This * causes merges to fail. We therefore treat function pointers * as if they all point to functions that return int. When * 4432549 is fixed, the lookupname() call below should be * replaced with `ntdp'. */ (*rtdp)->t_fndef->fn_ret = lookupname("int"); break; case 'a': case 'z': cp++; if (*cp++ != 'r') expected("tdefdecl/[az]", "r", cp - 1); *rtdp = xcalloc(sizeof (**rtdp)); (*rtdp)->t_type = ARRAY; (*rtdp)->t_id = h; cp = arraydef(cp, rtdp); break; case 'x': c = *++cp; if (c != 's' && c != 'u' && c != 'e') expected("tdefdecl/x", "[sue]", cp - 1); cp = name(cp + 1, &w); ntdp = xcalloc(sizeof (*ntdp)); ntdp->t_type = FORWARD; ntdp->t_name = w; /* * We explicitly don't set t_id here - the caller will do it. * The caller may want to use a real type ID, or they may * choose to make one up. */ *rtdp = ntdp; break; case 'B': /* volatile */ cp = tdefdecl(cp + 1, h, &ntdp); if (!ntdp->t_id) ntdp->t_id = faketypenumber++; *rtdp = xcalloc(sizeof (**rtdp)); (*rtdp)->t_type = VOLATILE; (*rtdp)->t_size = 0; (*rtdp)->t_tdesc = ntdp; (*rtdp)->t_id = h; break; case 'k': /* const */ cp = tdefdecl(cp + 1, h, &ntdp); if (!ntdp->t_id) ntdp->t_id = faketypenumber++; *rtdp = xcalloc(sizeof (**rtdp)); (*rtdp)->t_type = CONST; (*rtdp)->t_size = 0; (*rtdp)->t_tdesc = ntdp; (*rtdp)->t_id = h; break; case 'K': /* restricted */ cp = tdefdecl(cp + 1, h, &ntdp); if (!ntdp->t_id) ntdp->t_id = faketypenumber++; *rtdp = xcalloc(sizeof (**rtdp)); (*rtdp)->t_type = RESTRICT; (*rtdp)->t_size = 0; (*rtdp)->t_tdesc = ntdp; (*rtdp)->t_id = h; break; case 'u': case 's': cp++; *rtdp = xcalloc(sizeof (**rtdp)); (*rtdp)->t_name = NULL; cp = soudef(cp, (type == 'u') ? UNION : STRUCT, rtdp); break; default: expected("tdefdecl", "", cp); } return (cp); } static char * intrinsic(char *cp, tdesc_t **rtdp) { intr_t *intr = xcalloc(sizeof (intr_t)); tdesc_t *tdp; int width, fmt, i; switch (*cp++) { case 'b': intr->intr_type = INTR_INT; if (*cp == 's') intr->intr_signed = 1; else if (*cp != 'u') expected("intrinsic/b", "[su]", cp); cp++; if (strchr("cbv", *cp)) intr->intr_iformat = *cp++; cp = number(cp, &width); if (*cp++ != ';') expected("intrinsic/b", "; (post-width)", cp - 1); cp = number(cp, &intr->intr_offset); if (*cp++ != ';') expected("intrinsic/b", "; (post-offset)", cp - 1); cp = number(cp, &intr->intr_nbits); break; case 'R': intr->intr_type = INTR_REAL; for (fmt = 0, i = 0; isdigit(*(cp + i)); i++) fmt = fmt * 10 + (*(cp + i) - '0'); if (fmt < 1 || fmt > CTF_FP_MAX) expected("intrinsic/R", "number <= CTF_FP_MAX", cp); intr->intr_fformat = fmt; cp += i; if (*cp++ != ';') expected("intrinsic/R", ";", cp - 1); cp = number(cp, &width); intr->intr_nbits = width * 8; break; } tdp = xcalloc(sizeof (*tdp)); tdp->t_type = INTRINSIC; tdp->t_size = width; tdp->t_name = NULL; tdp->t_intr = intr; parse_debug(3, NULL, "intrinsic: size=%d", width); *rtdp = tdp; return (cp); } static tdesc_t * bitintrinsic(tdesc_t *template, int nbits) { tdesc_t *newtdp = xcalloc(sizeof (tdesc_t)); newtdp->t_name = xstrdup(template->t_name); newtdp->t_id = faketypenumber++; newtdp->t_type = INTRINSIC; newtdp->t_size = template->t_size; newtdp->t_intr = xmalloc(sizeof (intr_t)); bcopy(template->t_intr, newtdp->t_intr, sizeof (intr_t)); newtdp->t_intr->intr_nbits = nbits; return (newtdp); } static char * offsize(char *cp, mlist_t *mlp) { int offset, size; if (*cp == ',') cp++; cp = number(cp, &offset); if (*cp++ != ',') expected("offsize/2", ",", cp - 1); cp = number(cp, &size); if (*cp++ != ';') expected("offsize/3", ";", cp - 1); mlp->ml_offset = offset; mlp->ml_size = size; return (cp); } static tdesc_t * find_intrinsic(tdesc_t *tdp) { for (;;) { switch (tdp->t_type) { case TYPEDEF: case VOLATILE: case CONST: case RESTRICT: tdp = tdp->t_tdesc; break; default: return (tdp); } } } static char * soudef(char *cp, stabtype_t type, tdesc_t **rtdp) { mlist_t *mlp, **prev; char *w; int h; int size; tdesc_t *tdp, *itdp; cp = number(cp, &size); (*rtdp)->t_size = size; (*rtdp)->t_type = type; /* s or u */ /* * An '@' here indicates a bitmask follows. This is so the * compiler can pass information to debuggers about how structures * are passed in the v9 world. We don't need this information * so we skip over it. */ if (cp[0] == '@') { cp += 3; } parse_debug(3, cp, "soudef: %s size=%d", tdesc_name(*rtdp), (*rtdp)->t_size); prev = &((*rtdp)->t_members); /* now fill up the fields */ while ((*cp != '\0') && (*cp != ';')) { /* signifies end of fields */ mlp = xcalloc(sizeof (*mlp)); *prev = mlp; cp = name(cp, &w); mlp->ml_name = w; cp = id(cp, &h); /* * find the tdesc struct in the hash table for this type * and stick a ptr in here */ tdp = lookup(h); if (tdp == NULL) { /* not in hash list */ parse_debug(3, NULL, " defines %s (%d)", w, h); if (*cp++ != '=') { tdp = unres_new(h); parse_debug(3, NULL, " refers to %s (unresolved %d)", (w ? w : "anon"), h); } else { cp = tdefdecl(cp, h, &tdp); if (tdp->t_id && tdp->t_id != h) { tdesc_t *ntdp = xcalloc(sizeof (*ntdp)); ntdp->t_type = TYPEDEF; ntdp->t_tdesc = tdp; tdp = ntdp; } addhash(tdp, h); parse_debug(4, cp, " soudef now looking at "); cp++; } } else { parse_debug(3, NULL, " refers to %s (%d, %s)", w ? w : "anon", h, tdesc_name(tdp)); } cp = offsize(cp, mlp); itdp = find_intrinsic(tdp); if (itdp->t_type == INTRINSIC) { - if (mlp->ml_size != itdp->t_intr->intr_nbits) { + if ((int)mlp->ml_size != itdp->t_intr->intr_nbits) { parse_debug(4, cp, "making %d bit intrinsic " "from %s", mlp->ml_size, tdesc_name(itdp)); mlp->ml_type = bitintrinsic(itdp, mlp->ml_size); } else mlp->ml_type = tdp; } else if (itdp->t_type == TYPEDEF_UNRES) { list_add(&typedbitfldmems, mlp); mlp->ml_type = tdp; } else { mlp->ml_type = tdp; } /* cp is now pointing to next field */ prev = &mlp->ml_next; } return (cp); } static char * arraydef(char *cp, tdesc_t **rtdp) { int start, end, h; cp = id(cp, &h); if (*cp++ != ';') expected("arraydef/1", ";", cp - 1); (*rtdp)->t_ardef = xcalloc(sizeof (ardef_t)); (*rtdp)->t_ardef->ad_idxtype = lookup(h); cp = number(cp, &start); /* lower */ if (*cp++ != ';') expected("arraydef/2", ";", cp - 1); if (*cp == 'S') { /* * variable length array - treat as null dimensioned * * For VLA variables on sparc, SS12 generated stab entry * looks as follows: * .stabs "buf:(0,28)=zr(0,4);0;S-12;(0,1)", 0x80, 0, 0, -16 * Whereas SS12u1 generated stab entry looks like this: * .stabs "buf:(0,28)=zr(0,4);0;S0;(0,1)", 0x80, 0, 0, 0 * On x86, both versions generate the first type of entry. * We should be able to parse both. */ cp++; if (*cp == '-') cp++; cp = number(cp, &end); end = start; } else { /* * normal fixed-dimension array * Stab entry for this looks as follows : * .stabs "x:(0,28)=ar(0,4);0;9;(0,3)", 0x80, 0, 40, 0 */ cp = number(cp, &end); /* upper */ } if (*cp++ != ';') expected("arraydef/3", ";", cp - 1); (*rtdp)->t_ardef->ad_nelems = end - start + 1; cp = tdefdecl(cp, h, &((*rtdp)->t_ardef->ad_contents)); parse_debug(3, cp, "defined array idx type %d %d-%d next ", h, start, end); return (cp); } static void enumdef(char *cp, tdesc_t **rtdp) { elist_t *elp, **prev; char *w; (*rtdp)->t_type = ENUM; (*rtdp)->t_emem = NULL; prev = &((*rtdp)->t_emem); while (*cp != ';') { elp = xcalloc(sizeof (*elp)); elp->el_next = NULL; *prev = elp; cp = name(cp, &w); elp->el_name = w; cp = number(cp, &elp->el_number); parse_debug(3, NULL, "enum %s: %s=%d", tdesc_name(*rtdp), elp->el_name, elp->el_number); prev = &elp->el_next; if (*cp++ != ',') expected("enumdef", ",", cp - 1); } } static tdesc_t * lookup_name(tdesc_t **hash, const char *name1) { int bucket = compute_sum(name1); tdesc_t *tdp, *ttdp = NULL; for (tdp = hash[bucket]; tdp != NULL; tdp = tdp->t_next) { if (tdp->t_name != NULL && strcmp(tdp->t_name, name1) == 0) { if (tdp->t_type == STRUCT || tdp->t_type == UNION || tdp->t_type == ENUM || tdp->t_type == INTRINSIC) return (tdp); if (tdp->t_type == TYPEDEF) ttdp = tdp; } } return (ttdp); } tdesc_t * lookupname(const char *name1) { return (lookup_name(name_table, name1)); } /* * Add a node to the hash queues. */ static void addhash(tdesc_t *tdp, int num) { int hash = HASH(num); tdesc_t *ttdp; char added_num = 0, added_name = 0; /* * If it already exists in the hash table don't add it again * (but still check to see if the name should be hashed). */ ttdp = lookup(num); if (ttdp == NULL) { tdp->t_id = num; tdp->t_hash = hash_table[hash]; hash_table[hash] = tdp; added_num = 1; } if (tdp->t_name != NULL) { ttdp = lookupname(tdp->t_name); if (ttdp == NULL) { hash = compute_sum(tdp->t_name); tdp->t_next = name_table[hash]; name_table[hash] = tdp; added_name = 1; } } if (!added_num && !added_name) { terminate("stabs: broken hash\n"); } } static int compute_sum(const char *w) { char c; int sum; for (sum = 0; (c = *w) != '\0'; sum += c, w++) ; return (HASH(sum)); } static void reset(void) { longjmp(resetbuf, 1); } void check_hash(void) { tdesc_t *tdp; int i; printf("checking hash\n"); for (i = 0; i < BUCKETS; i++) { if (hash_table[i]) { for (tdp = hash_table[i]->t_hash; tdp && tdp != hash_table[i]; tdp = tdp->t_hash) continue; if (tdp) { terminate("cycle in hash bucket %d\n", i); return; } } if (name_table[i]) { for (tdp = name_table[i]->t_next; tdp && tdp != name_table[i]; tdp = tdp->t_next) continue; if (tdp) { terminate("cycle in name bucket %d\n", i); return; } } } printf("done\n"); } /*ARGSUSED1*/ static int resolve_typed_bitfields_cb(void *arg, void *private __unused) { mlist_t *ml = arg; tdesc_t *tdp = ml->ml_type; debug(3, "Resolving typed bitfields (member %s)\n", (ml->ml_name ? ml->ml_name : "(anon)")); while (tdp) { switch (tdp->t_type) { case INTRINSIC: - if (ml->ml_size != tdp->t_intr->intr_nbits) { + if ((int)ml->ml_size != tdp->t_intr->intr_nbits) { debug(3, "making %d bit intrinsic from %s", ml->ml_size, tdesc_name(tdp)); ml->ml_type = bitintrinsic(tdp, ml->ml_size); } else { debug(3, "using existing %d bit %s intrinsic", ml->ml_size, tdesc_name(tdp)); ml->ml_type = tdp; } return (1); case POINTER: case TYPEDEF: case VOLATILE: case CONST: case RESTRICT: tdp = tdp->t_tdesc; break; default: return (1); } } terminate("type chain for bitfield member %s has a NULL", ml->ml_name); /*NOTREACHED*/ return (0); } void resolve_typed_bitfields(void) { (void) list_iter(typedbitfldmems, resolve_typed_bitfields_cb, NULL); }