diff --git a/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c b/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c index 5cd2de9f43ea..b3b4c1f7168f 100644 --- a/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c +++ b/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c @@ -1,1382 +1,1491 @@ /* * 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) 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 */ + uint_t nptent; /* number of processed types */ }; /* * 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); + uint_t id = (idp ? idp->ii_dtype->t_id : 0); if (target_requires_swap) { - SWAP_16(id); + SWAP_32(id); } ctf_buf_write(b, &id, sizeof (id)); debug(3, "Wrote object %s (%d)\n", (idp ? idp->ii_name : "(null)"), id); } static void write_functions(iidesc_t *idp, ctf_buf_t *b) { - ushort_t fdata[2]; - ushort_t id; + uint_t fdata[2]; + uint_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) { + if (nargs > CTF_V3_MAX_VLEN) { terminate("function %s has too many args: %d > %d\n", - idp->ii_name, nargs, CTF_MAX_VLEN); + idp->ii_name, nargs, CTF_V3_MAX_VLEN); } - fdata[0] = CTF_TYPE_INFO(CTF_K_FUNCTION, 1, nargs); + fdata[0] = CTF_V3_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]); + SWAP_32(fdata[0]); + SWAP_32(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); + SWAP_32(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) +write_sized_type_rec(ctf_buf_t *b, struct ctf_type_v3 *ctt, size_t size) { - if (size > CTF_MAX_SIZE) { - ctt->ctt_size = CTF_LSIZE_SENT; + if (size > CTF_V3_MAX_SIZE) { + ctt->ctt_size = CTF_V3_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_info); + SWAP_32(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; + struct ctf_stype_v3 *cts = (struct ctf_stype_v3 *)ctt; - cts->ctt_size = (ushort_t)size; + cts->ctt_size = size; if (target_requires_swap) { SWAP_32(cts->ctt_name); - SWAP_16(cts->ctt_info); - SWAP_16(cts->ctt_size); + SWAP_32(cts->ctt_info); + SWAP_32(cts->ctt_size); } ctf_buf_write(b, cts, sizeof (*cts)); } } static void -write_unsized_type_rec(ctf_buf_t *b, ctf_type_t *ctt) +write_unsized_type_rec(ctf_buf_t *b, struct ctf_type_v3 *ctt) { - ctf_stype_t *cts = (ctf_stype_t *)ctt; + struct ctf_stype_v3 *cts = (struct ctf_stype_v3 *)ctt; if (target_requires_swap) { SWAP_32(cts->ctt_name); - SWAP_16(cts->ctt_info); - SWAP_16(cts->ctt_size); + SWAP_32(cts->ctt_info); + SWAP_32(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; + struct ctf_type_v3 ctt; + struct ctf_array_v3 cta; + struct ctf_member_v3 ctm; + struct ctf_lmember_v3 ctlm; + struct ctf_enum cte; + uint_t id; /* * 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)) { + if (++b->nptent < CTF_V3_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)); + b->nptent - 1, CTF_V3_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)) { + ctt.ctt_info = CTF_V3_TYPE_INFO(0, 0, 0); + while (b->nptent < CTF_V3_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, + ctt.ctt_info = CTF_V3_TYPE_INFO(CTF_K_INTEGER, isroot, 1); else - ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FLOAT, isroot, 1); + ctt.ctt_info = CTF_V3_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_info = CTF_V3_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); + ctt.ctt_info = CTF_V3_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_contents); + SWAP_32(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) { + if (i > CTF_V3_MAX_VLEN) { terminate("sou %s has too many members: %d > %d\n", - tdesc_name(tp), i, CTF_MAX_VLEN); + tdesc_name(tp), i, CTF_V3_MAX_VLEN); } if (tp->t_type == STRUCT) - ctt.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, isroot, i); + ctt.ctt_info = CTF_V3_TYPE_INFO(CTF_K_STRUCT, isroot, i); else - ctt.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, isroot, i); + ctt.ctt_info = CTF_V3_TYPE_INFO(CTF_K_UNION, isroot, i); write_sized_type_rec(b, &ctt, tp->t_size); - if (tp->t_size < CTF_LSTRUCT_THRESH) { + if (tp->t_size < CTF_V3_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); + SWAP_32(ctm.ctm_type); + SWAP_32(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_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) { - i = CTF_MAX_VLEN; + if (i > CTF_V3_MAX_VLEN) { + i = CTF_V3_MAX_VLEN; } - ctt.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, isroot, i); + ctt.ctt_info = CTF_V3_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_info = CTF_V3_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_info = CTF_V3_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_info = CTF_V3_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_info = CTF_V3_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) { + if (i > CTF_V3_MAX_VLEN) { terminate("function %s has too many args: %d > %d\n", - tdesc_name(tp), i, CTF_MAX_VLEN); + tdesc_name(tp), i, CTF_V3_MAX_VLEN); } - ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FUNCTION, isroot, i); + ctt.ctt_info = CTF_V3_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); + SWAP_32(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_info = CTF_V3_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_version = CTF_VERSION_3; 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) +get_ctt_info(ctf_header_t *h, void *v, uint_t *kind, uint_t *vlen, int *isroot) +{ + if (h->cth_version == CTF_VERSION_2) { + struct ctf_type_v2 *ctt = v; + + *kind = CTF_V2_INFO_KIND(ctt->ctt_info); + *vlen = CTF_V2_INFO_VLEN(ctt->ctt_info); + *isroot = CTF_V2_INFO_ISROOT(ctt->ctt_info); + } else { + struct ctf_type_v3 *ctt = v; + + *kind = CTF_V3_INFO_KIND(ctt->ctt_info); + *vlen = CTF_V3_INFO_VLEN(ctt->ctt_info); + *isroot = CTF_V3_INFO_ISROOT(ctt->ctt_info); + } +} + +static void +get_ctt_size(ctf_header_t *h, void *v, 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); + if (h->cth_version == CTF_VERSION_2) { + struct ctf_type_v2 *ctt = v; + + if (ctt->ctt_size == CTF_V2_LSIZE_SENT) { + *sizep = (size_t)CTF_TYPE_LSIZE(ctt); + *incrementp = sizeof (struct ctf_type_v2); + } else { + *sizep = ctt->ctt_size; + *incrementp = sizeof (struct ctf_stype_v2); + } } else { - *sizep = ctt->ctt_size; - *incrementp = sizeof (ctf_stype_t); + struct ctf_type_v3 *ctt = v; + + if (ctt->ctt_size == CTF_V3_LSIZE_SENT) { + *sizep = (size_t)CTF_TYPE_LSIZE(ctt); + *incrementp = sizeof (struct ctf_type_v3); + } else { + *sizep = ctt->ctt_size; + *incrementp = sizeof (struct ctf_stype_v3); + } } } static int count_types(ctf_header_t *h, caddr_t data) { caddr_t dptr = data + h->cth_typeoff; + uint_t version = h->cth_version; + size_t idwidth; int count = 0; + idwidth = version == CTF_VERSION_2 ? 2 : 4; 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; + uint_t vlen, kind; + int isroot; - get_ctt_size(ctt, &size, &increment); + get_ctt_info(h, v, &kind, &vlen, &isroot); + get_ctt_size(h, v, &size, &increment); - switch (CTF_INFO_KIND(ctt->ctt_info)) { + switch (kind) { 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)); + dptr += idwidth * vlen; break; case CTF_K_ARRAY: - dptr += sizeof (ctf_array_t); + if (version == CTF_VERSION_2) + dptr += sizeof (struct ctf_array_v2); + else + dptr += sizeof (struct ctf_array_v3); 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; + if (version == CTF_VERSION_2) { + if (size < CTF_V2_LSTRUCT_THRESH) + dptr += sizeof (struct ctf_member_v2) * + vlen; + else + dptr += sizeof (struct ctf_lmember_v2) * + vlen; + } else { + if (size < CTF_V3_LSTRUCT_THRESH) + dptr += sizeof (struct ctf_member_v3) * + vlen; + else + dptr += sizeof (struct ctf_lmember_v3) * + 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); + kind, 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; + size_t idwidth; + + idwidth = h->cth_version == CTF_VERSION_2 ? 2 : 4; symit_reset(si); - for (dptr = buf; dptr < buf + bufsz; dptr += 2) { - void *v = (void *) dptr; - ushort_t id = *((ushort_t *)v); + for (dptr = buf; dptr < buf + bufsz; dptr += idwidth) { + uint32_t id = 0; + + memcpy(&id, (void *) dptr, idwidth); 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) { + } else if (id >= (uint_t)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; + size_t idwidth; caddr_t dptr = buf; iidesc_t *ii; - ushort_t info; - ushort_t retid; GElf_Sym *sym; int i; + idwidth = h->cth_version == CTF_VERSION_2 ? 2 : 4; + symit_reset(si); while (dptr < buf + bufsz) { - void *v = (void *) dptr; - info = *((ushort_t *)v); - dptr += 2; + uint32_t id, info, retid; + + info = 0; + memcpy(&info, (void *) dptr, idwidth); + dptr += idwidth; 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; + retid = 0; + memcpy(&retid, (void *) dptr, idwidth); + dptr += idwidth; - if (retid >= tdsize) + if (retid >= (uint_t)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 (h->cth_version == CTF_VERSION_2) + ii->ii_nargs = CTF_V2_INFO_VLEN(info); + else + ii->ii_nargs = CTF_V3_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) + for (i = 0; i < ii->ii_nargs; i++, dptr += idwidth) { + id = 0; + memcpy(&id, (void *) dptr, idwidth); + if (id >= (uint_t)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; + size_t idwidth, size, increment; int tcnt; int iicnt = 0; tid_t tid, argid; - int kind, vlen; - int i; + int isroot, kind, vlen; + int i, version; elist_t **epp; mlist_t **mpp; intr_t *ip; - ctf_type_t *ctt; - ctf_array_t *cta; - ctf_enum_t *cte; + version = h->cth_version; + idwidth = version == CTF_VERSION_2 ? 2 : 4; /* * 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; + if (maxid == 0) { + maxid = version == CTF_VERSION_2 ? + CTF_V2_MAX_TYPE : CTF_V3_MAX_TYPE; + } for (dptr = buf, tcnt = 0, tid = 1; dptr < buf + bufsz; tcnt++, tid++) { + ctf_enum_t *cte; + uint_t name, type; + void *v; + if (tid > maxid) break; if (tid >= tdsize) parseterminate("Reference to invalid type %d", tid); - void *v = (void *) dptr; - ctt = v; + get_ctt_info(h, dptr, &kind, &vlen, &isroot); + get_ctt_size(h, dptr, &size, &increment); + if (version == CTF_VERSION_2) { + struct ctf_type_v2 *ctt = (void *) dptr; - get_ctt_size(ctt, &size, &increment); + name = ctt->ctt_name; + type = ctt->ctt_type; + } else { + struct ctf_type_v3 *ctt = (void *) dptr; + + name = ctt->ctt_name; + type = ctt->ctt_type; + } dptr += increment; tdp = tdarr[tid]; - if (CTF_NAME_STID(ctt->ctt_name) != CTF_STRTAB_0) + if (CTF_NAME_STID(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)); + if (CTF_NAME_OFFSET(name) != 0) { + tdp->t_name = xstrdup(sbuf + CTF_NAME_OFFSET(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]; + tdp->t_tdesc = tdarr[type]; break; - case CTF_K_ARRAY: + case CTF_K_ARRAY: { + uint_t contents, index, nelems; + tdp->t_type = ARRAY; tdp->t_size = size; - v = (void *) dptr; - cta = v; - dptr += sizeof (ctf_array_t); + if (version == CTF_VERSION_2) { + struct ctf_array_v2 *cta = (void *) dptr; + contents = cta->cta_contents; + index = cta->cta_index; + nelems = cta->cta_nelems; + dptr += sizeof (*cta); + } else { + struct ctf_array_v3 *cta = (void *) dptr; + contents = cta->cta_contents; + index = cta->cta_index; + nelems = cta->cta_nelems; + dptr += sizeof (*cta); + } 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; + tdp->t_ardef->ad_contents = tdarr[contents]; + tdp->t_ardef->ad_idxtype = tdarr[index]; + tdp->t_ardef->ad_nelems = nelems; break; + } case CTF_K_STRUCT: - case CTF_K_UNION: + 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 (version == CTF_VERSION_2) { + if (size < CTF_V2_LSTRUCT_THRESH) { + for (i = 0, mpp = &tdp->t_members; i < vlen; + i++, mpp = &((*mpp)->ml_next)) { + v = (void *) dptr; + struct ctf_member_v2 *ctm = v; + dptr += sizeof (struct ctf_member_v2); + + *mpp = xmalloc(sizeof (mlist_t)); + (*mpp)->ml_name = xstrdup(sbuf + + ctm->ctm_name); + (*mpp)->ml_type = tdarr[ctm->ctm_type]; + (*mpp)->ml_offset = ctm->ctm_offset; + (*mpp)->ml_size = 0; + } + } else { + for (i = 0, mpp = &tdp->t_members; i < vlen; + i++, mpp = &((*mpp)->ml_next)) { + v = (void *) dptr; + struct ctf_lmember_v2 *ctlm = v; + dptr += sizeof (struct ctf_lmember_v2); + + *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; + } } } 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 (size < CTF_V3_LSTRUCT_THRESH) { + for (i = 0, mpp = &tdp->t_members; i < vlen; + i++, mpp = &((*mpp)->ml_next)) { + v = (void *) dptr; + struct ctf_member_v3 *ctm = v; + dptr += sizeof (struct ctf_member_v3); + + *mpp = xmalloc(sizeof (mlist_t)); + (*mpp)->ml_name = xstrdup(sbuf + + ctm->ctm_name); + (*mpp)->ml_type = tdarr[ctm->ctm_type]; + (*mpp)->ml_offset = ctm->ctm_offset; + (*mpp)->ml_size = 0; + } + } else { + for (i = 0, mpp = &tdp->t_members; i < vlen; + i++, mpp = &((*mpp)->ml_next)) { + v = (void *) dptr; + struct ctf_lmember_v3 *ctlm = v; + dptr += sizeof (struct ctf_lmember_v3); + + *mpp = xmalloc(sizeof (mlist_t)); + (*mpp)->ml_name = xstrdup(sbuf + + ctlm->ctlm_name); + (*mpp)->ml_type = + tdarr[ctlm->ctlm_type]; + (*mpp)->ml_offset = + (int)CTF_LMEM_OFFSET(ctlm); + (*mpp)->ml_size = 0; + } } } *mpp = NULL; break; + } case CTF_K_ENUM: tdp->t_type = ENUM; tdp->t_size = size; for (i = 0, epp = &tdp->t_emem; i < vlen; i++, epp = &((*epp)->el_next)) { 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]; + tdp->t_tdesc = tdarr[type]; break; case CTF_K_VOLATILE: tdp->t_type = VOLATILE; - tdp->t_tdesc = tdarr[ctt->ctt_type]; + tdp->t_tdesc = tdarr[type]; break; case CTF_K_CONST: tdp->t_type = CONST; - tdp->t_tdesc = tdarr[ctt->ctt_type]; + tdp->t_tdesc = tdarr[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]; + tdp->t_fndef->fn_ret = tdarr[type]; - v = (void *) (dptr + (sizeof (ushort_t) * (vlen - 1))); - if (vlen > 0 && *(ushort_t *)v == 0) + v = (void *) (dptr + (idwidth * (vlen - 1))); + if (vlen > 0 && *(uint_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); + memcpy(&argid, v, idwidth); + dptr += idwidth; if (argid != 0) tdp->t_fndef->fn_args[i] = tdarr[argid]; } - if (vlen & 1) - dptr += sizeof (ushort_t); + dptr = roundup2(dptr, 4); break; case CTF_K_RESTRICT: tdp->t_type = RESTRICT; - tdp->t_tdesc = tdarr[ctt->ctt_type]; + tdp->t_tdesc = tdarr[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)) { + if (isroot) { 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); + (isroot ? "root " : ""), tdesc_name(tdp), tdp->t_id); } debug(3, "Resurrected %d types (%d were roots)\n", tcnt, iicnt); } /* * For lack of other inspiration, we're going to take the boring route. We * count the number of types. This lets us malloc that many tdesc structs * before we start filling them in. This has the advantage of allowing us to * avoid a merge-esque remap step. */ static tdata_t * ctf_parse(ctf_header_t *h, caddr_t buf, symit_data_t *si, char *label) { tdata_t *td = tdata_new(); tdesc_t **tdarr; int ntypes = count_types(h, buf); int idx, i; /* shudder */ tdarr = xcalloc(sizeof (tdesc_t *) * (ntypes + 1)); tdarr[0] = NULL; for (i = 1; i <= ntypes; i++) { tdarr[i] = xcalloc(sizeof (tdesc_t)); tdarr[i]->t_id = i; } td->td_parlabel = xstrdup(buf + h->cth_stroff + h->cth_parlabel); /* we have the technology - we can rebuild them */ idx = resurrect_labels(h, td, buf, label); resurrect_objects(h, td, tdarr, ntypes + 1, buf, si); resurrect_functions(h, td, tdarr, ntypes + 1, buf, si); resurrect_types(h, td, tdarr, ntypes + 1, buf, idx); free(tdarr); td->td_nextid = ntypes + 1; return (td); } static size_t decompress_ctf(caddr_t cbuf, size_t cbufsz, caddr_t dbuf, size_t dbufsz) { z_stream zstr; int rc; zstr.zalloc = (alloc_func)0; zstr.zfree = (free_func)0; zstr.opaque = (voidpf)0; zstr.next_in = (Bytef *)cbuf; zstr.avail_in = cbufsz; zstr.next_out = (Bytef *)dbuf; zstr.avail_out = dbufsz; if ((rc = inflateInit(&zstr)) != Z_OK || (rc = inflate(&zstr, Z_NO_FLUSH)) != Z_STREAM_END || (rc = inflateEnd(&zstr)) != Z_OK) { warning("CTF decompress zlib error %s\n", zError(rc)); return (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) + if (h->cth_version != CTF_VERSION_2 && h->cth_version != CTF_VERSION_3) 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); } diff --git a/cddl/contrib/opensolaris/tools/ctf/cvt/ctfmerge.c b/cddl/contrib/opensolaris/tools/ctf/cvt/ctfmerge.c index ddb5f388ca98..161927cf0663 100644 --- a/cddl/contrib/opensolaris/tools/ctf/cvt/ctfmerge.c +++ b/cddl/contrib/opensolaris/tools/ctf/cvt/ctfmerge.c @@ -1,1025 +1,1025 @@ /* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Given several files containing CTF data, merge and uniquify that data into * a single CTF section in an output file. * * Merges can proceed independently. As such, we perform the merges in parallel * using a worker thread model. A given glob of CTF data (either all of the CTF * data from a single input file, or the result of one or more merges) can only * be involved in a single merge at any given time, so the process decreases in * parallelism, especially towards the end, as more and more files are * consolidated, finally resulting in a single merge of two large CTF graphs. * Unfortunately, the last merge is also the slowest, as the two graphs being * merged are each the product of merges of half of the input files. * * The algorithm consists of two phases, described in detail below. The first * phase entails the merging of CTF data in groups of eight. The second phase * takes the results of Phase I, and merges them two at a time. This disparity * is due to an observation that the merge time increases at least quadratically * with the size of the CTF data being merged. As such, merges of CTF graphs * newly read from input files are much faster than merges of CTF graphs that * are themselves the results of prior merges. * * A further complication is the need to ensure the repeatability of CTF merges. * That is, a merge should produce the same output every time, given the same * input. In both phases, this consistency requirement is met by imposing an * ordering on the merge process, thus ensuring that a given set of input files * are merged in the same order every time. * * Phase I * * The main thread reads the input files one by one, transforming the CTF * data they contain into tdata structures. When a given file has been read * and parsed, it is placed on the work queue for retrieval by worker threads. * * Central to Phase I is the Work In Progress (wip) array, which is used to * merge batches of files in a predictable order. Files are read by the main * thread, and are merged into wip array elements in round-robin order. When * the number of files merged into a given array slot equals the batch size, * the merged CTF graph in that array is added to the done slot in order by * array slot. * * For example, consider a case where we have five input files, a batch size * of two, a wip array size of two, and two worker threads (T1 and T2). * * 1. The wip array elements are assigned initial batch numbers 0 and 1. * 2. T1 reads an input file from the input queue (wq_queue). This is the * first input file, so it is placed into wip[0]. The second file is * similarly read and placed into wip[1]. The wip array slots now contain * one file each (wip_nmerged == 1). * 3. T1 reads the third input file, which it merges into wip[0]. The * number of files in wip[0] is equal to the batch size. * 4. T2 reads the fourth input file, which it merges into wip[1]. wip[1] * is now full too. * 5. T2 attempts to place the contents of wip[1] on the done queue * (wq_done_queue), but it can't, since the batch ID for wip[1] is 1. * Batch 0 needs to be on the done queue before batch 1 can be added, so * T2 blocks on wip[1]'s cv. * 6. T1 attempts to place the contents of wip[0] on the done queue, and * succeeds, updating wq_lastdonebatch to 0. It clears wip[0], and sets * its batch ID to 2. T1 then signals wip[1]'s cv to awaken T2. * 7. T2 wakes up, notices that wq_lastdonebatch is 0, which means that * batch 1 can now be added. It adds wip[1] to the done queue, clears * wip[1], and sets its batch ID to 3. It signals wip[0]'s cv, and * restarts. * * The above process continues until all input files have been consumed. At * this point, a pair of barriers are used to allow a single thread to move * any partial batches from the wip array to the done array in batch ID order. * When this is complete, wq_done_queue is moved to wq_queue, and Phase II * begins. * * Locking Semantics (Phase I) * * The input queue (wq_queue) and the done queue (wq_done_queue) are * protected by separate mutexes - wq_queue_lock and wq_done_queue. wip * array slots are protected by their own mutexes, which must be grabbed * before releasing the input queue lock. The wip array lock is dropped * when the thread restarts the loop. If the array slot was full, the * array lock will be held while the slot contents are added to the done * queue. The done queue lock is used to protect the wip slot cv's. * * The pow number is protected by the queue lock. The master batch ID * and last completed batch (wq_lastdonebatch) counters are protected *in * Phase I* by the done queue lock. * * Phase II * * When Phase II begins, the queue consists of the merged batches from the * first phase. Assume we have five batches: * * Q: a b c d e * * Using the same batch ID mechanism we used in Phase I, but without the wip * array, worker threads remove two entries at a time from the beginning of * the queue. These two entries are merged, and are added back to the tail * of the queue, as follows: * * Q: a b c d e # start * Q: c d e ab # a, b removed, merged, added to end * Q: e ab cd # c, d removed, merged, added to end * Q: cd eab # e, ab removed, merged, added to end * Q: cdeab # cd, eab removed, merged, added to end * * When one entry remains on the queue, with no merges outstanding, Phase II * finishes. We pre-determine the stopping point by pre-calculating the * number of nodes that will appear on the list. In the example above, the * number (wq_ninqueue) is 9. When ninqueue is 1, we conclude Phase II by * signaling the main thread via wq_done_cv. * * Locking Semantics (Phase II) * * The queue (wq_queue), ninqueue, and the master batch ID and last * completed batch counters are protected by wq_queue_lock. The done * queue and corresponding lock are unused in Phase II as is the wip array. * * Uniquification * * We want the CTF data that goes into a given module to be as small as * possible. For example, we don't want it to contain any type data that may * be present in another common module. As such, after creating the master * tdata_t for a given module, we can, if requested by the user, uniquify it * against the tdata_t from another module (genunix in the case of the SunOS * kernel). We perform a merge between the tdata_t for this module and the * tdata_t from genunix. Nodes found in this module that are not present in * genunix are added to a third tdata_t - the uniquified tdata_t. * * Additive Merges * * In some cases, for example if we are issuing a new version of a common * module in a patch, we need to make sure that the CTF data already present * in that module does not change. Changes to this data would void the CTF * data in any module that uniquified against the common module. To preserve * the existing data, we can perform what is known as an additive merge. In * this case, a final uniquification is performed against the CTF data in the * previous version of the module. The result will be the placement of new * and changed data after the existing data, thus preserving the existing type * ID space. * * Saving the result * * When the merges are complete, the resulting tdata_t is placed into the * output file, replacing the .SUNW_ctf section (if any) already in that file. * * The person who changes the merging thread code in this file without updating * this comment will not live to see the stock hit five. */ #include #include #include #include #include #ifdef illumos #include #endif #include #include #include #include #ifdef illumos #include #endif #include #include #include #ifdef illumos #include #endif #include "ctf_headers.h" #include "ctftools.h" #include "ctfmerge.h" #include "traverse.h" #include "memory.h" #include "fifo.h" #include "barrier.h" #pragma init(bigheap) #define MERGE_PHASE1_BATCH_SIZE 8 #define MERGE_PHASE1_MAX_SLOTS 5 #define MERGE_INPUT_THROTTLE_LEN 10 const char *progname; static char *outfile = NULL; static char *tmpname = NULL; static int dynsym; int debug_level = DEBUG_LEVEL; static size_t maxpgsize = 0x400000; void usage(void) { (void) fprintf(stderr, "Usage: %s [-fgstv] -l label | -L labelenv -o outfile file ...\n" " %s [-fgstv] -l label | -L labelenv -o outfile -d uniqfile\n" " %*s [-g] [-D uniqlabel] file ...\n" " %s [-fgstv] -l label | -L labelenv -o outfile -w withfile " "file ...\n" " %s [-g] -c srcfile destfile\n" "\n" " Note: if -L labelenv is specified and labelenv is not set in\n" " the environment, a default value is used.\n", progname, progname, (int)strlen(progname), " ", progname, progname); } #ifdef illumos static void bigheap(void) { size_t big, *size; int sizes; struct memcntl_mha mha; /* * First, get the available pagesizes. */ if ((sizes = getpagesizes(NULL, 0)) == -1) return; if (sizes == 1 || (size = alloca(sizeof (size_t) * sizes)) == NULL) return; if (getpagesizes(size, sizes) == -1) return; while (size[sizes - 1] > maxpgsize) sizes--; /* set big to the largest allowed page size */ big = size[sizes - 1]; if (big & (big - 1)) { /* * The largest page size is not a power of two for some * inexplicable reason; return. */ return; } /* * Now, align our break to the largest page size. */ if (brk((void *)((((uintptr_t)sbrk(0) - 1) & ~(big - 1)) + big)) != 0) return; /* * set the preferred page size for the heap */ mha.mha_cmd = MHA_MAPSIZE_BSSBRK; mha.mha_flags = 0; mha.mha_pagesize = big; (void) memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mha, 0, 0); } #endif /* illumos */ static void finalize_phase_one(workqueue_t *wq) { int startslot, i; /* * wip slots are cleared out only when maxbatchsz td's have been merged * into them. We're not guaranteed that the number of files we're * merging is a multiple of maxbatchsz, so there will be some partial * groups in the wip array. Move them to the done queue in batch ID * order, starting with the slot containing the next batch that would * have been placed on the done queue, followed by the others. * One thread will be doing this while the others wait at the barrier * back in worker_thread(), so we don't need to worry about pesky things * like locks. */ for (startslot = -1, i = 0; i < wq->wq_nwipslots; i++) { if (wq->wq_wip[i].wip_batchid == wq->wq_lastdonebatch + 1) { startslot = i; break; } } assert(startslot != -1); for (i = startslot; i < startslot + wq->wq_nwipslots; i++) { int slotnum = i % wq->wq_nwipslots; wip_t *wipslot = &wq->wq_wip[slotnum]; if (wipslot->wip_td != NULL) { debug(2, "clearing slot %d (%d) (saving %d)\n", slotnum, i, wipslot->wip_nmerged); } else debug(2, "clearing slot %d (%d)\n", slotnum, i); if (wipslot->wip_td != NULL) { fifo_add(wq->wq_donequeue, wipslot->wip_td); wq->wq_wip[slotnum].wip_td = NULL; } } wq->wq_lastdonebatch = wq->wq_next_batchid++; debug(2, "phase one done: donequeue has %d items\n", fifo_len(wq->wq_donequeue)); } static void init_phase_two(workqueue_t *wq) { int num; /* * We're going to continually merge the first two entries on the queue, * placing the result on the end, until there's nothing left to merge. * At that point, everything will have been merged into one. The * initial value of ninqueue needs to be equal to the total number of * entries that will show up on the queue, both at the start of the * phase and as generated by merges during the phase. */ wq->wq_ninqueue = num = fifo_len(wq->wq_donequeue); while (num != 1) { wq->wq_ninqueue += num / 2; num = num / 2 + num % 2; } /* * Move the done queue to the work queue. We won't be using the done * queue in phase 2. */ assert(fifo_len(wq->wq_queue) == 0); fifo_free(wq->wq_queue, NULL); wq->wq_queue = wq->wq_donequeue; } static void wip_save_work(workqueue_t *wq, wip_t *slot, int slotnum) { pthread_mutex_lock(&wq->wq_donequeue_lock); while (wq->wq_lastdonebatch + 1 < slot->wip_batchid) pthread_cond_wait(&slot->wip_cv, &wq->wq_donequeue_lock); assert(wq->wq_lastdonebatch + 1 == slot->wip_batchid); fifo_add(wq->wq_donequeue, slot->wip_td); wq->wq_lastdonebatch++; pthread_cond_signal(&wq->wq_wip[(slotnum + 1) % wq->wq_nwipslots].wip_cv); /* reset the slot for next use */ slot->wip_td = NULL; slot->wip_batchid = wq->wq_next_batchid++; pthread_mutex_unlock(&wq->wq_donequeue_lock); } static void wip_add_work(wip_t *slot, tdata_t *pow) { if (slot->wip_td == NULL) { slot->wip_td = pow; slot->wip_nmerged = 1; } else { debug(2, "%d: merging %p into %p\n", pthread_self(), (void *)pow, (void *)slot->wip_td); merge_into_master(pow, slot->wip_td, NULL, 0); tdata_free(pow); slot->wip_nmerged++; } } static void worker_runphase1(workqueue_t *wq) { wip_t *wipslot; tdata_t *pow; int wipslotnum, pownum; for (;;) { pthread_mutex_lock(&wq->wq_queue_lock); while (fifo_empty(wq->wq_queue)) { if (wq->wq_nomorefiles == 1) { pthread_cond_broadcast(&wq->wq_work_avail); pthread_mutex_unlock(&wq->wq_queue_lock); /* on to phase 2 ... */ return; } pthread_cond_wait(&wq->wq_work_avail, &wq->wq_queue_lock); } /* there's work to be done! */ pow = fifo_remove(wq->wq_queue); pownum = wq->wq_nextpownum++; pthread_cond_broadcast(&wq->wq_work_removed); assert(pow != NULL); /* merge it into the right slot */ wipslotnum = pownum % wq->wq_nwipslots; wipslot = &wq->wq_wip[wipslotnum]; pthread_mutex_lock(&wipslot->wip_lock); pthread_mutex_unlock(&wq->wq_queue_lock); wip_add_work(wipslot, pow); if (wipslot->wip_nmerged == wq->wq_maxbatchsz) wip_save_work(wq, wipslot, wipslotnum); pthread_mutex_unlock(&wipslot->wip_lock); } } static void worker_runphase2(workqueue_t *wq) { tdata_t *pow1, *pow2; int batchid; for (;;) { pthread_mutex_lock(&wq->wq_queue_lock); if (wq->wq_ninqueue == 1) { pthread_cond_broadcast(&wq->wq_work_avail); pthread_mutex_unlock(&wq->wq_queue_lock); debug(2, "%d: entering p2 completion barrier\n", pthread_self()); if (barrier_wait(&wq->wq_bar1)) { pthread_mutex_lock(&wq->wq_queue_lock); wq->wq_alldone = 1; pthread_cond_signal(&wq->wq_alldone_cv); pthread_mutex_unlock(&wq->wq_queue_lock); } return; } if (fifo_len(wq->wq_queue) < 2) { pthread_cond_wait(&wq->wq_work_avail, &wq->wq_queue_lock); pthread_mutex_unlock(&wq->wq_queue_lock); continue; } /* there's work to be done! */ pow1 = fifo_remove(wq->wq_queue); pow2 = fifo_remove(wq->wq_queue); wq->wq_ninqueue -= 2; batchid = wq->wq_next_batchid++; pthread_mutex_unlock(&wq->wq_queue_lock); debug(2, "%d: merging %p into %p\n", pthread_self(), (void *)pow1, (void *)pow2); merge_into_master(pow1, pow2, NULL, 0); tdata_free(pow1); /* * merging is complete. place at the tail of the queue in * proper order. */ pthread_mutex_lock(&wq->wq_queue_lock); while (wq->wq_lastdonebatch + 1 != batchid) { pthread_cond_wait(&wq->wq_done_cv, &wq->wq_queue_lock); } wq->wq_lastdonebatch = batchid; fifo_add(wq->wq_queue, pow2); debug(2, "%d: added %p to queue, len now %d, ninqueue %d\n", pthread_self(), (void *)pow2, fifo_len(wq->wq_queue), wq->wq_ninqueue); pthread_cond_broadcast(&wq->wq_done_cv); pthread_cond_signal(&wq->wq_work_avail); pthread_mutex_unlock(&wq->wq_queue_lock); } } /* * Main loop for worker threads. */ static void worker_thread(workqueue_t *wq) { worker_runphase1(wq); debug(2, "%d: entering first barrier\n", pthread_self()); if (barrier_wait(&wq->wq_bar1)) { debug(2, "%d: doing work in first barrier\n", pthread_self()); finalize_phase_one(wq); init_phase_two(wq); debug(2, "%d: ninqueue is %d, %d on queue\n", pthread_self(), wq->wq_ninqueue, fifo_len(wq->wq_queue)); } debug(2, "%d: entering second barrier\n", pthread_self()); (void) barrier_wait(&wq->wq_bar2); debug(2, "%d: phase 1 complete\n", pthread_self()); worker_runphase2(wq); } /* * Pass a tdata_t tree, built from an input file, off to the work queue for * consumption by worker threads. */ static int merge_ctf_cb(tdata_t *td, char *name, void *arg) { workqueue_t *wq = arg; debug(3, "Adding tdata %p for processing\n", (void *)td); pthread_mutex_lock(&wq->wq_queue_lock); while (fifo_len(wq->wq_queue) > wq->wq_ithrottle) { debug(2, "Throttling input (len = %d, throttle = %d)\n", fifo_len(wq->wq_queue), wq->wq_ithrottle); pthread_cond_wait(&wq->wq_work_removed, &wq->wq_queue_lock); } fifo_add(wq->wq_queue, td); debug(1, "Thread %d announcing %s\n", pthread_self(), name); pthread_cond_broadcast(&wq->wq_work_avail); pthread_mutex_unlock(&wq->wq_queue_lock); return (1); } /* * This program is intended to be invoked from a Makefile, as part of the build. * As such, in the event of a failure or user-initiated interrupt (^C), we need * to ensure that a subsequent re-make will cause ctfmerge to be executed again. * Unfortunately, ctfmerge will usually be invoked directly after (and as part * of the same Makefile rule as) a link, and will operate on the linked file * in place. If we merely exit upon receipt of a SIGINT, a subsequent make * will notice that the *linked* file is newer than the object files, and thus * will not reinvoke ctfmerge. The only way to ensure that a subsequent make * reinvokes ctfmerge, is to remove the file to which we are adding CTF * data (confusingly named the output file). This means that the link will need * to happen again, but links are generally fast, and we can't allow the merge * to be skipped. * * Another possibility would be to block SIGINT entirely - to always run to * completion. The run time of ctfmerge can, however, be measured in minutes * in some cases, so this is not a valid option. */ static void handle_sig(int sig) { terminate("Caught signal %d - exiting\n", sig); } static void terminate_cleanup(void) { int dounlink = getenv("CTFMERGE_TERMINATE_NO_UNLINK") ? 0 : 1; if (tmpname != NULL && dounlink) unlink(tmpname); if (outfile == NULL) return; #if !defined(__FreeBSD__) if (dounlink) { fprintf(stderr, "Removing %s\n", outfile); unlink(outfile); } #endif } static void copy_ctf_data(char *srcfile, char *destfile, int keep_stabs) { tdata_t *srctd; if (read_ctf(&srcfile, 1, NULL, read_ctf_save_cb, &srctd, 1) == 0) terminate("No CTF data found in source file %s\n", srcfile); tmpname = mktmpname(destfile, ".ctf"); write_ctf(srctd, destfile, tmpname, CTF_COMPRESS | CTF_SWAP_BYTES | keep_stabs); if (rename(tmpname, destfile) != 0) { terminate("Couldn't rename temp file %s to %s", tmpname, destfile); } free(tmpname); tdata_free(srctd); } static void wq_init(workqueue_t *wq, int nfiles) { int throttle, nslots, i; if (getenv("CTFMERGE_MAX_SLOTS")) nslots = atoi(getenv("CTFMERGE_MAX_SLOTS")); else nslots = MERGE_PHASE1_MAX_SLOTS; if (getenv("CTFMERGE_PHASE1_BATCH_SIZE")) wq->wq_maxbatchsz = atoi(getenv("CTFMERGE_PHASE1_BATCH_SIZE")); else wq->wq_maxbatchsz = MERGE_PHASE1_BATCH_SIZE; nslots = MIN(nslots, (nfiles + wq->wq_maxbatchsz - 1) / wq->wq_maxbatchsz); wq->wq_wip = xcalloc(sizeof (wip_t) * nslots); wq->wq_nwipslots = nslots; wq->wq_nthreads = MIN(sysconf(_SC_NPROCESSORS_ONLN) * 3 / 2, nslots); wq->wq_thread = xmalloc(sizeof (pthread_t) * wq->wq_nthreads); if (getenv("CTFMERGE_INPUT_THROTTLE")) throttle = atoi(getenv("CTFMERGE_INPUT_THROTTLE")); else throttle = MERGE_INPUT_THROTTLE_LEN; wq->wq_ithrottle = throttle * wq->wq_nthreads; debug(1, "Using %d slots, %d threads\n", wq->wq_nwipslots, wq->wq_nthreads); wq->wq_next_batchid = 0; for (i = 0; i < nslots; i++) { pthread_mutex_init(&wq->wq_wip[i].wip_lock, NULL); pthread_cond_init(&wq->wq_wip[i].wip_cv, NULL); wq->wq_wip[i].wip_batchid = wq->wq_next_batchid++; } pthread_mutex_init(&wq->wq_queue_lock, NULL); wq->wq_queue = fifo_new(); pthread_cond_init(&wq->wq_work_avail, NULL); pthread_cond_init(&wq->wq_work_removed, NULL); wq->wq_ninqueue = nfiles; wq->wq_nextpownum = 0; pthread_mutex_init(&wq->wq_donequeue_lock, NULL); wq->wq_donequeue = fifo_new(); wq->wq_lastdonebatch = -1; pthread_cond_init(&wq->wq_done_cv, NULL); pthread_cond_init(&wq->wq_alldone_cv, NULL); wq->wq_alldone = 0; barrier_init(&wq->wq_bar1, wq->wq_nthreads); barrier_init(&wq->wq_bar2, wq->wq_nthreads); wq->wq_nomorefiles = 0; } static void start_threads(workqueue_t *wq) { sigset_t sets; int i; sigemptyset(&sets); sigaddset(&sets, SIGINT); sigaddset(&sets, SIGQUIT); sigaddset(&sets, SIGTERM); pthread_sigmask(SIG_BLOCK, &sets, NULL); for (i = 0; i < wq->wq_nthreads; i++) { pthread_create(&wq->wq_thread[i], NULL, (void *(*)(void *))worker_thread, wq); } #ifdef illumos sigset(SIGINT, handle_sig); sigset(SIGQUIT, handle_sig); sigset(SIGTERM, handle_sig); #else signal(SIGINT, handle_sig); signal(SIGQUIT, handle_sig); signal(SIGTERM, handle_sig); #endif pthread_sigmask(SIG_UNBLOCK, &sets, NULL); } static void join_threads(workqueue_t *wq) { int i; for (i = 0; i < wq->wq_nthreads; i++) { pthread_join(wq->wq_thread[i], NULL); } } static int strcompare(const void *p1, const void *p2) { char *s1 = *((char **)p1); char *s2 = *((char **)p2); return (strcmp(s1, s2)); } /* * Core work queue structure; passed to worker threads on thread creation * as the main point of coordination. Allocate as a static structure; we * could have put this into a local variable in main, but passing a pointer * into your stack to another thread is fragile at best and leads to some * hard-to-debug failure modes. */ static workqueue_t wq; int main(int argc, char **argv) { tdata_t *mstrtd, *savetd; char *uniqfile = NULL, *uniqlabel = NULL; char *withfile = NULL; char *label = NULL; char **ifiles, **tifiles; int verbose = 0, docopy = 0; int write_fuzzy_match = 0; int keep_stabs = 0; int require_ctf = 0; int nifiles, nielems; int c, i, idx, tidx, err; progname = basename(argv[0]); if (getenv("CTFMERGE_DEBUG_LEVEL")) debug_level = atoi(getenv("CTFMERGE_DEBUG_LEVEL")); err = 0; while ((c = getopt(argc, argv, ":cd:D:fgl:L:o:tvw:s")) != EOF) { switch (c) { case 'c': docopy = 1; break; case 'd': /* Uniquify against `uniqfile' */ uniqfile = optarg; break; case 'D': /* Uniquify against label `uniqlabel' in `uniqfile' */ uniqlabel = optarg; break; case 'f': write_fuzzy_match = CTF_FUZZY_MATCH; break; case 'g': keep_stabs = CTF_KEEP_STABS; break; case 'l': /* Label merged types with `label' */ label = optarg; break; case 'L': /* Label merged types with getenv(`label`) */ if ((label = getenv(optarg)) == NULL) label = CTF_DEFAULT_LABEL; break; case 'o': /* Place merged types in CTF section in `outfile' */ outfile = optarg; break; case 't': /* Insist *all* object files built from C have CTF */ require_ctf = 1; break; case 'v': /* More debugging information */ verbose = 1; break; case 'w': /* Additive merge with data from `withfile' */ withfile = optarg; break; case 's': /* use the dynsym rather than the symtab */ dynsym = CTF_USE_DYNSYM; break; default: usage(); exit(2); } } /* Validate arguments */ if (docopy) { if (uniqfile != NULL || uniqlabel != NULL || label != NULL || outfile != NULL || withfile != NULL || dynsym != 0) err++; if (argc - optind != 2) err++; } else { if (uniqfile != NULL && withfile != NULL) err++; if (uniqlabel != NULL && uniqfile == NULL) err++; if (outfile == NULL || label == NULL) err++; if (argc - optind == 0) err++; } if (err) { usage(); exit(2); } if (getenv("STRIPSTABS_KEEP_STABS") != NULL) keep_stabs = CTF_KEEP_STABS; if (uniqfile && access(uniqfile, R_OK) != 0) { warning("Uniquification file %s couldn't be opened and " "will be ignored.\n", uniqfile); uniqfile = NULL; } if (withfile && access(withfile, R_OK) != 0) { warning("With file %s couldn't be opened and will be " "ignored.\n", withfile); withfile = NULL; } if (outfile && access(outfile, R_OK|W_OK) != 0) terminate("Cannot open output file %s for r/w", outfile); /* * This is ugly, but we don't want to have to have a separate tool * (yet) just for copying an ELF section with our specific requirements, * so we shoe-horn a copier into ctfmerge. */ if (docopy) { copy_ctf_data(argv[optind], argv[optind + 1], keep_stabs); exit(0); } set_terminate_cleanup(terminate_cleanup); /* Sort the input files and strip out duplicates */ nifiles = argc - optind; ifiles = xmalloc(sizeof (char *) * nifiles); tifiles = xmalloc(sizeof (char *) * nifiles); for (i = 0; i < nifiles; i++) tifiles[i] = argv[optind + i]; qsort(tifiles, nifiles, sizeof (char *), (int (*)())strcompare); ifiles[0] = tifiles[0]; for (idx = 0, tidx = 1; tidx < nifiles; tidx++) { if (strcmp(ifiles[idx], tifiles[tidx]) != 0) ifiles[++idx] = tifiles[tidx]; } nifiles = idx + 1; /* Make sure they all exist */ if ((nielems = count_files(ifiles, nifiles)) < 0) terminate("Some input files were inaccessible\n"); /* Prepare for the merge */ wq_init(&wq, nielems); start_threads(&wq); /* * Start the merge * * We're reading everything from each of the object files, so we * don't need to specify labels. */ if (read_ctf(ifiles, nifiles, NULL, merge_ctf_cb, &wq, require_ctf) == 0) { /* * If we're verifying that C files have CTF, it's safe to * assume that in this case, we're building only from assembly * inputs. */ if (require_ctf) exit(0); terminate("No ctf sections found to merge\n"); } pthread_mutex_lock(&wq.wq_queue_lock); wq.wq_nomorefiles = 1; pthread_cond_broadcast(&wq.wq_work_avail); pthread_mutex_unlock(&wq.wq_queue_lock); pthread_mutex_lock(&wq.wq_queue_lock); while (wq.wq_alldone == 0) pthread_cond_wait(&wq.wq_alldone_cv, &wq.wq_queue_lock); pthread_mutex_unlock(&wq.wq_queue_lock); join_threads(&wq); /* * All requested files have been merged, with the resulting tree in * mstrtd. savetd is the tree that will be placed into the output file. * * Regardless of whether we're doing a normal uniquification or an * additive merge, we need a type tree that has been uniquified * against uniqfile or withfile, as appropriate. * * If we're doing a uniquification, we stuff the resulting tree into * outfile. Otherwise, we add the tree to the tree already in withfile. */ assert(fifo_len(wq.wq_queue) == 1); mstrtd = fifo_remove(wq.wq_queue); if (verbose || debug_level) { debug(2, "Statistics for td %p\n", (void *)mstrtd); iidesc_stats(mstrtd->td_iihash); } if (uniqfile != NULL || withfile != NULL) { char *reffile, *reflabel = NULL; tdata_t *reftd; if (uniqfile != NULL) { reffile = uniqfile; reflabel = uniqlabel; } else reffile = withfile; if (read_ctf(&reffile, 1, reflabel, read_ctf_save_cb, &reftd, require_ctf) == 0) { terminate("No CTF data found in reference file %s\n", reffile); } savetd = tdata_new(); - if (CTF_TYPE_ISCHILD(reftd->td_nextid)) + if (CTF_V3_TYPE_ISCHILD(reftd->td_nextid)) terminate("No room for additional types in master\n"); savetd->td_nextid = withfile ? reftd->td_nextid : - CTF_INDEX_TO_TYPE(1, TRUE); + CTF_V3_INDEX_TO_TYPE(1, TRUE); merge_into_master(mstrtd, reftd, savetd, 0); tdata_label_add(savetd, label, CTF_LABEL_LASTIDX); if (withfile) { /* * savetd holds the new data to be added to the withfile */ tdata_t *withtd = reftd; tdata_merge(withtd, savetd); savetd = withtd; } else { char uniqname[MAXPATHLEN]; labelent_t *parle; parle = tdata_label_top(reftd); savetd->td_parlabel = xstrdup(parle->le_name); strncpy(uniqname, reffile, sizeof (uniqname)); uniqname[MAXPATHLEN - 1] = '\0'; savetd->td_parname = xstrdup(basename(uniqname)); } } else { /* * No post processing. Write the merged tree as-is into the * output file. */ tdata_label_free(mstrtd); tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX); savetd = mstrtd; } tmpname = mktmpname(outfile, ".ctf"); write_ctf(savetd, outfile, tmpname, CTF_COMPRESS | CTF_SWAP_BYTES | write_fuzzy_match | dynsym | keep_stabs); if (rename(tmpname, outfile) != 0) terminate("Couldn't rename output temp file %s", tmpname); free(tmpname); return (0); } diff --git a/cddl/contrib/opensolaris/tools/ctf/dump/dump.c b/cddl/contrib/opensolaris/tools/ctf/dump/dump.c index 740485ddff03..6da2c2cf8b26 100644 --- a/cddl/contrib/opensolaris/tools/ctf/dump/dump.c +++ b/cddl/contrib/opensolaris/tools/ctf/dump/dump.c @@ -1,1028 +1,1120 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include "ctf_headers.h" #include "utils.h" #include "symbol.h" #define WARN(x) { warn(x); return (E_ERROR); } /* * Flags that indicate what data is to be displayed. An explicit `all' value is * provided to allow the code to distinguish between a request for everything * (currently requested by invoking ctfdump without flags) and individual * requests for all of the types of data (an invocation with all flags). In the * former case, we want to be able to implicitly adjust the definition of `all' * based on the CTF version of the file being dumped. For example, if a v2 file * is being dumped, `all' includes F_LABEL - a request to dump the label * section. If a v1 file is being dumped, `all' does not include F_LABEL, * because v1 CTF doesn't support labels. We need to be able to distinguish * between `ctfdump foo', which has an implicit request for labels if `foo' * supports them, and `ctfdump -l foo', which has an explicity request. In the * latter case, we exit with an error if `foo' is a v1 CTF file. */ static enum { F_DATA = 0x01, /* show data object section */ F_FUNC = 0x02, /* show function section */ F_HDR = 0x04, /* show header */ F_STR = 0x08, /* show string table */ F_TYPES = 0x10, /* show type section */ F_STATS = 0x20, /* show statistics */ F_LABEL = 0x40, /* show label section */ F_ALL = 0x80, /* explicit request for `all' */ F_ALLMSK = 0xff /* show all sections and statistics */ } flags = 0; static struct { ulong_t s_ndata; /* total number of data objects */ ulong_t s_nfunc; /* total number of functions */ ulong_t s_nargs; /* total number of function arguments */ ulong_t s_argmax; /* longest argument list */ ulong_t s_ntypes; /* total number of types */ ulong_t s_types[16]; /* number of types by kind */ ulong_t s_nsmem; /* total number of struct members */ ulong_t s_nsbytes; /* total size of all structs */ ulong_t s_smmax; /* largest struct in terms of members */ ulong_t s_sbmax; /* largest struct in terms of bytes */ ulong_t s_numem; /* total number of union members */ ulong_t s_nubytes; /* total size of all unions */ ulong_t s_ummax; /* largest union in terms of members */ ulong_t s_ubmax; /* largest union in terms of bytes */ ulong_t s_nemem; /* total number of enum members */ ulong_t s_emmax; /* largest enum in terms of members */ ulong_t s_nstr; /* total number of strings */ size_t s_strlen; /* total length of all strings */ size_t s_strmax; /* longest string length */ } stats; typedef struct ctf_data { caddr_t cd_ctfdata; /* Pointer to the CTF data */ size_t cd_ctflen; /* Length of CTF data */ + size_t cd_idwidth; /* Size of a type ID, in bytes */ + /* * cd_symdata will be non-NULL if the CTF data is being retrieved from * an ELF file with a symbol table. cd_strdata and cd_nsyms should be * used only if cd_symdata is non-NULL. */ Elf_Data *cd_symdata; /* Symbol table */ Elf_Data *cd_strdata; /* Symbol table strings */ int cd_nsyms; /* Number of symbol table entries */ } ctf_data_t; static const char * ref_to_str(uint_t name, const ctf_header_t *hp, const ctf_data_t *cd) { size_t offset = CTF_NAME_OFFSET(name); const char *s = cd->cd_ctfdata + hp->cth_stroff + offset; if (CTF_NAME_STID(name) != CTF_STRTAB_0) return ("<< ??? - name in external strtab >>"); if (offset >= hp->cth_strlen) return ("<< ??? - name exceeds strlab len >>"); if (hp->cth_stroff + offset >= cd->cd_ctflen) return ("<< ??? - file truncated >>"); if (s[0] == '\0') return ("(anon)"); return (s); } static const char * int_encoding_to_str(uint_t encoding) { static char buf[32]; if (encoding == 0 || (encoding & ~(CTF_INT_SIGNED | CTF_INT_CHAR | CTF_INT_BOOL | CTF_INT_VARARGS)) != 0) (void) snprintf(buf, sizeof (buf), " 0x%x", encoding); else { buf[0] = '\0'; if (encoding & CTF_INT_SIGNED) (void) strcat(buf, " SIGNED"); if (encoding & CTF_INT_CHAR) (void) strcat(buf, " CHAR"); if (encoding & CTF_INT_BOOL) (void) strcat(buf, " BOOL"); if (encoding & CTF_INT_VARARGS) (void) strcat(buf, " VARARGS"); } return (buf + 1); } static const char * fp_encoding_to_str(uint_t encoding) { static const char *const encs[] = { NULL, "SINGLE", "DOUBLE", "COMPLEX", "DCOMPLEX", "LDCOMPLEX", "LDOUBLE", "INTERVAL", "DINTERVAL", "LDINTERVAL", "IMAGINARY", "DIMAGINARY", "LDIMAGINARY" }; static char buf[16]; if (encoding < 1 || encoding >= (sizeof (encs) / sizeof (char *))) { (void) snprintf(buf, sizeof (buf), "%u", encoding); return (buf); } return (encs[encoding]); } static void print_line(const char *s) { static const char line[] = "----------------------------------------" "----------------------------------------"; (void) printf("\n%s%.*s\n\n", s, (int)(78 - strlen(s)), line); } static int print_header(const ctf_header_t *hp, const ctf_data_t *cd) { print_line("- CTF Header "); (void) printf(" cth_magic = 0x%04x\n", hp->cth_magic); (void) printf(" cth_version = %u\n", hp->cth_version); (void) printf(" cth_flags = 0x%02x\n", hp->cth_flags); (void) printf(" cth_parlabel = %s\n", ref_to_str(hp->cth_parlabel, hp, cd)); (void) printf(" cth_parname = %s\n", ref_to_str(hp->cth_parname, hp, cd)); (void) printf(" cth_lbloff = %u\n", hp->cth_lbloff); (void) printf(" cth_objtoff = %u\n", hp->cth_objtoff); (void) printf(" cth_funcoff = %u\n", hp->cth_funcoff); (void) printf(" cth_typeoff = %u\n", hp->cth_typeoff); (void) printf(" cth_stroff = %u\n", hp->cth_stroff); (void) printf(" cth_strlen = %u\n", hp->cth_strlen); return (E_SUCCESS); } static int print_labeltable(const ctf_header_t *hp, const ctf_data_t *cd) { void *v = (void *) (cd->cd_ctfdata + hp->cth_lbloff); const ctf_lblent_t *ctl = v; ulong_t i, n = (hp->cth_objtoff - hp->cth_lbloff) / sizeof (*ctl); print_line("- Label Table "); if (hp->cth_lbloff & 3) WARN("cth_lbloff is not aligned properly\n"); if (hp->cth_lbloff >= cd->cd_ctflen) WARN("file is truncated or cth_lbloff is corrupt\n"); if (hp->cth_objtoff >= cd->cd_ctflen) WARN("file is truncated or cth_objtoff is corrupt\n"); if (hp->cth_lbloff > hp->cth_objtoff) WARN("file is corrupt -- cth_lbloff > cth_objtoff\n"); for (i = 0; i < n; i++, ctl++) { (void) printf(" %5u %s\n", ctl->ctl_typeidx, ref_to_str(ctl->ctl_label, hp, cd)); } return (E_SUCCESS); } /* * Given the current symbol index (-1 to start at the beginning of the symbol * table) and the type of symbol to match, this function returns the index of * the next matching symbol (if any), and places the name of that symbol in * *namep. If no symbol is found, -1 is returned. */ static int next_sym(const ctf_data_t *cd, const int symidx, const uchar_t matchtype, char **namep) { int i; for (i = symidx + 1; i < cd->cd_nsyms; i++) { GElf_Sym sym; char *name; int type; if (gelf_getsym(cd->cd_symdata, i, &sym) == 0) return (-1); name = (char *)cd->cd_strdata->d_buf + sym.st_name; type = GELF_ST_TYPE(sym.st_info); /* * Skip various types of symbol table entries. */ if (type != matchtype || ignore_symbol(&sym, name)) continue; /* Found one */ *namep = name; return (i); } return (-1); } static int read_data(const ctf_header_t *hp, const ctf_data_t *cd) { - void *v = (void *) (cd->cd_ctfdata + hp->cth_objtoff); - const ushort_t *idp = v; - ulong_t n = (hp->cth_funcoff - hp->cth_objtoff) / sizeof (ushort_t); + const char *v = (void *) (cd->cd_ctfdata + hp->cth_objtoff); + ulong_t n = (hp->cth_funcoff - hp->cth_objtoff) / cd->cd_idwidth; if (flags != F_STATS) print_line("- Data Objects "); if (hp->cth_objtoff & 1) WARN("cth_objtoff is not aligned properly\n"); if (hp->cth_objtoff >= cd->cd_ctflen) WARN("file is truncated or cth_objtoff is corrupt\n"); if (hp->cth_funcoff >= cd->cd_ctflen) WARN("file is truncated or cth_funcoff is corrupt\n"); if (hp->cth_objtoff > hp->cth_funcoff) WARN("file is corrupt -- cth_objtoff > cth_funcoff\n"); if (flags != F_STATS) { int symidx, len, i; char *name = NULL; for (symidx = -1, i = 0; i < (int) n; i++) { + uint32_t id = 0; int nextsym; if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, symidx, STT_OBJECT, &name)) < 0) name = NULL; else symidx = nextsym; - len = printf(" [%u] %u", i, *idp++); + memcpy(&id, v, cd->cd_idwidth); + v += cd->cd_idwidth; + len = printf(" [%u] %u", i, id); if (name != NULL) (void) printf("%*s%s (%u)", (15 - len), "", name, symidx); (void) putchar('\n'); } } stats.s_ndata = n; return (E_SUCCESS); } static int read_funcs(const ctf_header_t *hp, const ctf_data_t *cd) { - void *v = (void *) (cd->cd_ctfdata + hp->cth_funcoff); - const ushort_t *fp = v; + const char *v = (void *) (cd->cd_ctfdata + hp->cth_funcoff); + uint_t f = 0, info; - v = (void *) (cd->cd_ctfdata + hp->cth_typeoff); - const ushort_t *end = v; + const char *end = (void *) (cd->cd_ctfdata + hp->cth_typeoff); ulong_t id; int symidx; if (flags != F_STATS) print_line("- Functions "); if (hp->cth_funcoff & 1) WARN("cth_funcoff is not aligned properly\n"); if (hp->cth_funcoff >= cd->cd_ctflen) WARN("file is truncated or cth_funcoff is corrupt\n"); if (hp->cth_typeoff >= cd->cd_ctflen) WARN("file is truncated or cth_typeoff is corrupt\n"); if (hp->cth_funcoff > hp->cth_typeoff) WARN("file is corrupt -- cth_funcoff > cth_typeoff\n"); - for (symidx = -1, id = 0; fp < end; id++) { - ushort_t info = *fp++; - ushort_t kind = CTF_INFO_KIND(info); - ushort_t n = CTF_INFO_VLEN(info); + for (symidx = -1, id = 0; v < end; id++) { + info = 0; + memcpy(&info, v, cd->cd_idwidth); + v += cd->cd_idwidth; + ushort_t kind = hp->cth_version == CTF_VERSION_2 ? + CTF_V2_INFO_KIND(info) : CTF_V3_INFO_KIND(info); + ushort_t n = hp->cth_version == CTF_VERSION_2 ? + CTF_V2_INFO_VLEN(info) : CTF_V3_INFO_VLEN(info); ushort_t i; int nextsym; char *name; if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, symidx, STT_FUNC, &name)) < 0) name = NULL; else symidx = nextsym; if (kind == CTF_K_UNKNOWN && n == 0) continue; /* skip padding */ if (kind != CTF_K_FUNCTION) { (void) printf(" [%lu] unexpected kind -- %u\n", id, kind); return (E_ERROR); } - if (fp + n > end) { + if (v + n * cd->cd_idwidth > end) { (void) printf(" [%lu] vlen %u extends past section " "boundary\n", id, n); return (E_ERROR); } if (flags != F_STATS) { (void) printf(" [%lu] FUNC ", id); if (name != NULL) (void) printf("(%s) ", name); - (void) printf("returns: %u args: (", *fp++); + memcpy(&f, v, cd->cd_idwidth); + v += cd->cd_idwidth; + (void) printf("returns: %u args: (", f); if (n != 0) { - (void) printf("%u", *fp++); - for (i = 1; i < n; i++) - (void) printf(", %u", *fp++); + memcpy(&f, v, cd->cd_idwidth); + v += cd->cd_idwidth; + (void) printf("%u", f); + for (i = 1; i < n; i++) { + memcpy(&f, v, cd->cd_idwidth); + v += cd->cd_idwidth; + (void) printf(", %u", f); + } } (void) printf(")\n"); } else - fp += n + 1; /* skip to next function definition */ + v += n * cd->cd_idwidth + 1; /* skip to next function definition */ stats.s_nfunc++; stats.s_nargs += n; stats.s_argmax = MAX(stats.s_argmax, n); } return (E_SUCCESS); } static int read_types(const ctf_header_t *hp, const ctf_data_t *cd) { - void *v = (void *) (cd->cd_ctfdata + hp->cth_typeoff); - const ctf_type_t *tp = v; - - v = (void *) (cd->cd_ctfdata + hp->cth_stroff); - const ctf_type_t *end = v; - + const char *v = (void *) (cd->cd_ctfdata + hp->cth_typeoff); + const char *end = (void *) (cd->cd_ctfdata + hp->cth_stroff); ulong_t id; + uint_t version; if (flags != F_STATS) print_line("- Types "); if (hp->cth_typeoff & 3) WARN("cth_typeoff is not aligned properly\n"); if (hp->cth_typeoff >= cd->cd_ctflen) WARN("file is truncated or cth_typeoff is corrupt\n"); if (hp->cth_stroff >= cd->cd_ctflen) WARN("file is truncated or cth_stroff is corrupt\n"); if (hp->cth_typeoff > hp->cth_stroff) WARN("file is corrupt -- cth_typeoff > cth_stroff\n"); + version = hp->cth_version; + id = 1; if (hp->cth_parlabel || hp->cth_parname) - id += 1 << CTF_PARENT_SHIFT; + id += 1ul << (hp->cth_version == CTF_VERSION_2 ? + CTF_V2_PARENT_SHIFT : CTF_V3_PARENT_SHIFT); - for (/* */; tp < end; id++) { - ulong_t i, n = CTF_INFO_VLEN(tp->ctt_info); + for (/* */; v < end; id++) { + struct ctf_type_v2 t2; + struct ctf_type_v3 t3; + ulong_t i, n; size_t size, increment, vlen = 0; - int kind = CTF_INFO_KIND(tp->ctt_info); + uint_t isroot, name, type; + int kind; + + if (version == CTF_VERSION_2) { + memcpy(&t2, v, sizeof(t2)); + name = t2.ctt_name; + n = CTF_V2_INFO_VLEN(t2.ctt_info); + isroot = CTF_V2_INFO_ISROOT(t2.ctt_info); + kind = CTF_V2_INFO_KIND(t2.ctt_info); + type = t2.ctt_type; + + if (t2.ctt_size == CTF_V2_LSIZE_SENT) { + increment = sizeof (struct ctf_type_v2); + size = (size_t)CTF_TYPE_LSIZE(&t2); + } else { + increment = sizeof (struct ctf_stype_v2); + size = t2.ctt_size; + } + } else { + memcpy(&t3, v, sizeof(t3)); + name = t3.ctt_name; + n = CTF_V3_INFO_VLEN(t3.ctt_info); + isroot = CTF_V3_INFO_ISROOT(t3.ctt_info); + kind = CTF_V3_INFO_KIND(t3.ctt_info); + type = t3.ctt_type; + + if (t3.ctt_size == CTF_V3_LSIZE_SENT) { + increment = sizeof (struct ctf_type_v3); + size = (size_t)CTF_TYPE_LSIZE(&t3); + } else { + increment = sizeof (struct ctf_stype_v3); + size = t3.ctt_size; + } + } union { - const void *ptr; - ctf_array_t *ap; - const ctf_member_t *mp; - const ctf_lmember_t *lmp; + const char *ptr; + struct ctf_array_v2 *ap2; + struct ctf_array_v3 *ap3; + const struct ctf_member_v2 *mp2; + const struct ctf_member_v3 *mp3; + const struct ctf_lmember_v2 *lmp2; + const struct ctf_lmember_v3 *lmp3; const ctf_enum_t *ep; - const ushort_t *argp; } u; + u.ptr = v + increment; + if (flags != F_STATS) { (void) printf(" %c%lu%c ", - "[<"[CTF_INFO_ISROOT(tp->ctt_info)], id, - "]>"[CTF_INFO_ISROOT(tp->ctt_info)]); - } - - if (tp->ctt_size == CTF_LSIZE_SENT) { - increment = sizeof (ctf_type_t); - size = (size_t)CTF_TYPE_LSIZE(tp); - } else { - increment = sizeof (ctf_stype_t); - size = tp->ctt_size; + "[<"[isroot], id, "]>"[isroot]); } - u.ptr = (const char *)tp + increment; switch (kind) { case CTF_K_INTEGER: if (flags != F_STATS) { - uint_t encoding = *((const uint_t *)u.ptr); + uint_t encoding = + *((const uint_t *)(const void *)u.ptr); (void) printf("INTEGER %s encoding=%s offset=%u" - " bits=%u", ref_to_str(tp->ctt_name, hp, - cd), int_encoding_to_str( + " bits=%u", ref_to_str(name, hp, cd), + int_encoding_to_str( CTF_INT_ENCODING(encoding)), CTF_INT_OFFSET(encoding), CTF_INT_BITS(encoding)); } - vlen = sizeof (uint_t); + vlen = sizeof (uint32_t); break; case CTF_K_FLOAT: if (flags != F_STATS) { - uint_t encoding = *((const uint_t *)u.ptr); + uint_t encoding = + *((const uint_t *)(const void *)u.ptr); (void) printf("FLOAT %s encoding=%s offset=%u " - "bits=%u", ref_to_str(tp->ctt_name, hp, - cd), fp_encoding_to_str( + "bits=%u", ref_to_str(name, hp, cd), + fp_encoding_to_str( CTF_FP_ENCODING(encoding)), CTF_FP_OFFSET(encoding), CTF_FP_BITS(encoding)); } - vlen = sizeof (uint_t); + vlen = sizeof (uint32_t); break; case CTF_K_POINTER: if (flags != F_STATS) { (void) printf("POINTER %s refers to %u", - ref_to_str(tp->ctt_name, hp, cd), - tp->ctt_type); + ref_to_str(name, hp, cd), type); } break; - case CTF_K_ARRAY: + case CTF_K_ARRAY: { + uint_t contents, index, nelems; + + if (version == CTF_VERSION_2) { + contents = u.ap2->cta_contents; + index = u.ap2->cta_index; + nelems = u.ap2->cta_nelems; + } else { + contents = u.ap3->cta_contents; + index = u.ap3->cta_index; + nelems = u.ap3->cta_nelems; + } if (flags != F_STATS) { (void) printf("ARRAY %s content: %u index: %u " - "nelems: %u\n", ref_to_str(tp->ctt_name, - hp, cd), u.ap->cta_contents, - u.ap->cta_index, u.ap->cta_nelems); + "nelems: %u\n", ref_to_str(name, hp, cd), + contents, index, nelems); } - vlen = sizeof (ctf_array_t); + if (version == 2) + vlen = sizeof (struct ctf_array_v2); + else + vlen = sizeof (struct ctf_array_v3); break; + } + + case CTF_K_FUNCTION: { + uint_t arg = 0; - case CTF_K_FUNCTION: if (flags != F_STATS) { (void) printf("FUNCTION %s returns: %u args: (", - ref_to_str(tp->ctt_name, hp, cd), - tp->ctt_type); + ref_to_str(name, hp, cd), type); if (n != 0) { - (void) printf("%u", *u.argp++); - for (i = 1; i < n; i++, u.argp++) - (void) printf(", %u", *u.argp); + memcpy(&arg, u.ptr, cd->cd_idwidth); + u.ptr += cd->cd_idwidth; + (void) printf("%u", arg); + for (i = 1; i < n; + i++, u.ptr += cd->cd_idwidth) { + memcpy(&arg, u.ptr, + cd->cd_idwidth); + (void) printf(", %u", arg); + } } (void) printf(")"); } - vlen = sizeof (ushort_t) * (n + (n & 1)); + vlen = roundup2(cd->cd_idwidth * n, 4); break; + } case CTF_K_STRUCT: case CTF_K_UNION: if (kind == CTF_K_STRUCT) { stats.s_nsmem += n; stats.s_smmax = MAX(stats.s_smmax, n); stats.s_nsbytes += size; stats.s_sbmax = MAX(stats.s_sbmax, size); if (flags != F_STATS) (void) printf("STRUCT"); } else { stats.s_numem += n; stats.s_ummax = MAX(stats.s_ummax, n); stats.s_nubytes += size; stats.s_ubmax = MAX(stats.s_ubmax, size); if (flags != F_STATS) (void) printf("UNION"); } if (flags != F_STATS) { (void) printf(" %s (%zd bytes)\n", - ref_to_str(tp->ctt_name, hp, cd), size); - - if (size >= CTF_LSTRUCT_THRESH) { - for (i = 0; i < n; i++, u.lmp++) { - (void) printf( - "\t%s type=%u off=%llu\n", - ref_to_str(u.lmp->ctlm_name, - hp, cd), u.lmp->ctlm_type, - (unsigned long long) - CTF_LMEM_OFFSET(u.lmp)); + ref_to_str(name, hp, cd), size); + + if (version == CTF_VERSION_2) { + if (size >= CTF_V2_LSTRUCT_THRESH) { + for (i = 0; i < n; i++, u.lmp2++) { + (void) printf( + "\t%s type=%u off=%llu\n", + ref_to_str(u.lmp2->ctlm_name, + hp, cd), u.lmp2->ctlm_type, + (unsigned long long) + CTF_LMEM_OFFSET(u.lmp2)); + } + } else { + for (i = 0; i < n; i++, u.mp2++) { + (void) printf( + "\t%s type=%u off=%u\n", + ref_to_str(u.mp2->ctm_name, + hp, cd), u.mp2->ctm_type, + u.mp2->ctm_offset); + } } } else { - for (i = 0; i < n; i++, u.mp++) { - (void) printf( - "\t%s type=%u off=%u\n", - ref_to_str(u.mp->ctm_name, - hp, cd), u.mp->ctm_type, - u.mp->ctm_offset); + if (size >= CTF_V3_LSTRUCT_THRESH) { + for (i = 0; i < n; i++, u.lmp3++) { + (void) printf( + "\t%s type=%u off=%llu\n", + ref_to_str(u.lmp3->ctlm_name, + hp, cd), u.lmp3->ctlm_type, + (unsigned long long) + CTF_LMEM_OFFSET(u.lmp3)); + } + } else { + for (i = 0; i < n; i++, u.mp3++) { + (void) printf( + "\t%s type=%u off=%u\n", + ref_to_str(u.mp3->ctm_name, + hp, cd), u.mp3->ctm_type, + u.mp3->ctm_offset); + } } } } - vlen = n * (size >= CTF_LSTRUCT_THRESH ? - sizeof (ctf_lmember_t) : sizeof (ctf_member_t)); + if (version == CTF_VERSION_2) { + vlen = n * (size >= CTF_V2_LSTRUCT_THRESH ? + sizeof (struct ctf_lmember_v2) : + sizeof (struct ctf_member_v2)); + } else { + vlen = n * (size >= CTF_V3_LSTRUCT_THRESH ? + sizeof (struct ctf_lmember_v3) : + sizeof (struct ctf_member_v3)); + } break; case CTF_K_ENUM: if (flags != F_STATS) { (void) printf("ENUM %s\n", - ref_to_str(tp->ctt_name, hp, cd)); + ref_to_str(name, hp, cd)); for (i = 0; i < n; i++, u.ep++) { (void) printf("\t%s = %d\n", ref_to_str(u.ep->cte_name, hp, cd), u.ep->cte_value); } } stats.s_nemem += n; stats.s_emmax = MAX(stats.s_emmax, n); vlen = sizeof (ctf_enum_t) * n; break; case CTF_K_FORWARD: if (flags != F_STATS) { (void) printf("FORWARD %s", - ref_to_str(tp->ctt_name, hp, cd)); + ref_to_str(name, hp, cd)); } break; case CTF_K_TYPEDEF: if (flags != F_STATS) { (void) printf("TYPEDEF %s refers to %u", - ref_to_str(tp->ctt_name, hp, cd), - tp->ctt_type); + ref_to_str(name, hp, cd), type); } break; case CTF_K_VOLATILE: if (flags != F_STATS) { (void) printf("VOLATILE %s refers to %u", - ref_to_str(tp->ctt_name, hp, cd), - tp->ctt_type); + ref_to_str(name, hp, cd), type); } break; case CTF_K_CONST: if (flags != F_STATS) { (void) printf("CONST %s refers to %u", - ref_to_str(tp->ctt_name, hp, cd), - tp->ctt_type); + ref_to_str(name, hp, cd), type); } break; case CTF_K_RESTRICT: if (flags != F_STATS) { (void) printf("RESTRICT %s refers to %u", - ref_to_str(tp->ctt_name, hp, cd), - tp->ctt_type); + ref_to_str(name, hp, cd), type); } break; case CTF_K_UNKNOWN: break; /* hole in type id space */ default: (void) printf("unexpected kind %u\n", kind); return (E_ERROR); } if (flags != F_STATS) (void) printf("\n"); stats.s_ntypes++; stats.s_types[kind]++; - tp = (ctf_type_t *)((uintptr_t)tp + increment + vlen); + v += increment + vlen; } return (E_SUCCESS); } static int read_strtab(const ctf_header_t *hp, const ctf_data_t *cd) { size_t n, off, len = hp->cth_strlen; const char *s = cd->cd_ctfdata + hp->cth_stroff; if (flags != F_STATS) print_line("- String Table "); if (hp->cth_stroff >= cd->cd_ctflen) WARN("file is truncated or cth_stroff is corrupt\n"); if (hp->cth_stroff + hp->cth_strlen > cd->cd_ctflen) WARN("file is truncated or cth_strlen is corrupt\n"); for (off = 0; len != 0; off += n) { if (flags != F_STATS) { (void) printf(" [%lu] %s\n", (ulong_t)off, s[0] == '\0' ? "\\0" : s); } n = strlen(s) + 1; len -= n; s += n; stats.s_nstr++; stats.s_strlen += n; stats.s_strmax = MAX(stats.s_strmax, n); } return (E_SUCCESS); } static void long_stat(const char *name, ulong_t value) { (void) printf(" %-36s= %lu\n", name, value); } static void fp_stat(const char *name, float value) { (void) printf(" %-36s= %.2f\n", name, value); } static int print_stats(void) { print_line("- CTF Statistics "); long_stat("total number of data objects", stats.s_ndata); (void) printf("\n"); long_stat("total number of functions", stats.s_nfunc); long_stat("total number of function arguments", stats.s_nargs); long_stat("maximum argument list length", stats.s_argmax); if (stats.s_nfunc != 0) { fp_stat("average argument list length", (float)stats.s_nargs / (float)stats.s_nfunc); } (void) printf("\n"); long_stat("total number of types", stats.s_ntypes); long_stat("total number of integers", stats.s_types[CTF_K_INTEGER]); long_stat("total number of floats", stats.s_types[CTF_K_FLOAT]); long_stat("total number of pointers", stats.s_types[CTF_K_POINTER]); long_stat("total number of arrays", stats.s_types[CTF_K_ARRAY]); long_stat("total number of func types", stats.s_types[CTF_K_FUNCTION]); long_stat("total number of structs", stats.s_types[CTF_K_STRUCT]); long_stat("total number of unions", stats.s_types[CTF_K_UNION]); long_stat("total number of enums", stats.s_types[CTF_K_ENUM]); long_stat("total number of forward tags", stats.s_types[CTF_K_FORWARD]); long_stat("total number of typedefs", stats.s_types[CTF_K_TYPEDEF]); long_stat("total number of volatile types", stats.s_types[CTF_K_VOLATILE]); long_stat("total number of const types", stats.s_types[CTF_K_CONST]); long_stat("total number of restrict types", stats.s_types[CTF_K_RESTRICT]); long_stat("total number of unknowns (holes)", stats.s_types[CTF_K_UNKNOWN]); (void) printf("\n"); long_stat("total number of struct members", stats.s_nsmem); long_stat("maximum number of struct members", stats.s_smmax); long_stat("total size of all structs", stats.s_nsbytes); long_stat("maximum size of a struct", stats.s_sbmax); if (stats.s_types[CTF_K_STRUCT] != 0) { fp_stat("average number of struct members", (float)stats.s_nsmem / (float)stats.s_types[CTF_K_STRUCT]); fp_stat("average size of a struct", (float)stats.s_nsbytes / (float)stats.s_types[CTF_K_STRUCT]); } (void) printf("\n"); long_stat("total number of union members", stats.s_numem); long_stat("maximum number of union members", stats.s_ummax); long_stat("total size of all unions", stats.s_nubytes); long_stat("maximum size of a union", stats.s_ubmax); if (stats.s_types[CTF_K_UNION] != 0) { fp_stat("average number of union members", (float)stats.s_numem / (float)stats.s_types[CTF_K_UNION]); fp_stat("average size of a union", (float)stats.s_nubytes / (float)stats.s_types[CTF_K_UNION]); } (void) printf("\n"); long_stat("total number of enum members", stats.s_nemem); long_stat("maximum number of enum members", stats.s_emmax); if (stats.s_types[CTF_K_ENUM] != 0) { fp_stat("average number of enum members", (float)stats.s_nemem / (float)stats.s_types[CTF_K_ENUM]); } (void) printf("\n"); long_stat("total number of unique strings", stats.s_nstr); long_stat("bytes of string data", stats.s_strlen); long_stat("maximum string length", stats.s_strmax); if (stats.s_nstr != 0) { fp_stat("average string length", (float)stats.s_strlen / (float)stats.s_nstr); } (void) printf("\n"); return (E_SUCCESS); } static int print_usage(FILE *fp, int verbose) { (void) fprintf(fp, "Usage: %s [-dfhlsSt] [-u file] file\n", getpname()); if (verbose) { (void) fprintf(fp, "\t-d dump data object section\n" "\t-f dump function section\n" "\t-h dump file header\n" "\t-l dump label table\n" "\t-s dump string table\n" "\t-S dump statistics\n" "\t-t dump type section\n" "\t-u save uncompressed CTF to a file\n"); } return (E_USAGE); } static Elf_Scn * findelfscn(Elf *elf, GElf_Ehdr *ehdr, const char *secname) { GElf_Shdr shdr; Elf_Scn *scn; char *name; for (scn = NULL; (scn = elf_nextscn(elf, scn)) != NULL; ) { if (gelf_getshdr(scn, &shdr) != NULL && (name = elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)) != NULL && strcmp(name, secname) == 0) return (scn); } return (NULL); } int main(int argc, char *argv[]) { const char *filename = NULL; const char *ufile = NULL; int error = 0; int c, fd, ufd; ctf_data_t cd; const ctf_preamble_t *pp; ctf_header_t *hp = NULL; Elf *elf; GElf_Ehdr ehdr; (void) elf_version(EV_CURRENT); for (opterr = 0; optind < argc; optind++) { while ((c = getopt(argc, argv, "dfhlsStu:")) != (int)EOF) { switch (c) { case 'd': flags |= F_DATA; break; case 'f': flags |= F_FUNC; break; case 'h': flags |= F_HDR; break; case 'l': flags |= F_LABEL; break; case 's': flags |= F_STR; break; case 'S': flags |= F_STATS; break; case 't': flags |= F_TYPES; break; case 'u': ufile = optarg; break; default: if (optopt == '?') return (print_usage(stdout, 1)); warn("illegal option -- %c\n", optopt); return (print_usage(stderr, 0)); } } if (optind < argc) { if (filename != NULL) return (print_usage(stderr, 0)); filename = argv[optind]; } } if (filename == NULL) return (print_usage(stderr, 0)); if (flags == 0 && ufile == NULL) flags = F_ALLMSK; if ((fd = open(filename, O_RDONLY)) == -1) die("failed to open %s", filename); if ((elf = elf_begin(fd, ELF_C_READ, NULL)) != NULL && gelf_getehdr(elf, &ehdr) != NULL) { Elf_Data *dp = NULL; Elf_Scn *ctfscn = findelfscn(elf, &ehdr, ".SUNW_ctf"); Elf_Scn *symscn; GElf_Shdr ctfshdr; if (ctfscn == NULL || (dp = elf_getdata(ctfscn, NULL)) == NULL) die("%s does not contain .SUNW_ctf data\n", filename); cd.cd_ctfdata = dp->d_buf; cd.cd_ctflen = dp->d_size; /* * If the sh_link field of the CTF section header is non-zero * it indicates which section contains the symbol table that * should be used. We default to the .symtab section if sh_link * is zero or if there's an error reading the section header. */ if (gelf_getshdr(ctfscn, &ctfshdr) != NULL && ctfshdr.sh_link != 0) { symscn = elf_getscn(elf, ctfshdr.sh_link); } else { symscn = findelfscn(elf, &ehdr, ".symtab"); } /* If we found a symbol table, find the corresponding strings */ if (symscn != NULL) { GElf_Shdr shdr; Elf_Scn *symstrscn; if (gelf_getshdr(symscn, &shdr) != NULL) { symstrscn = elf_getscn(elf, shdr.sh_link); cd.cd_nsyms = shdr.sh_size / shdr.sh_entsize; cd.cd_symdata = elf_getdata(symscn, NULL); cd.cd_strdata = elf_getdata(symstrscn, NULL); } } } else { struct stat st; if (fstat(fd, &st) == -1) die("failed to fstat %s", filename); cd.cd_ctflen = st.st_size; cd.cd_ctfdata = mmap(NULL, cd.cd_ctflen, PROT_READ, MAP_PRIVATE, fd, 0); if (cd.cd_ctfdata == MAP_FAILED) die("failed to mmap %s", filename); } /* * Get a pointer to the CTF data buffer and interpret the first portion * as a ctf_header_t. Validate the magic number and size. */ if (cd.cd_ctflen < sizeof (ctf_preamble_t)) die("%s does not contain a CTF preamble\n", filename); void *v = (void *) cd.cd_ctfdata; pp = v; if (pp->ctp_magic != CTF_MAGIC) die("%s does not appear to contain CTF data\n", filename); - if (pp->ctp_version == CTF_VERSION) { + if (pp->ctp_version >= CTF_VERSION_2) { v = (void *) cd.cd_ctfdata; hp = v; cd.cd_ctfdata = (caddr_t)cd.cd_ctfdata + sizeof (ctf_header_t); + cd.cd_idwidth = pp->ctp_version == CTF_VERSION_2 ? 2 : 4; + if (cd.cd_ctflen < sizeof (ctf_header_t)) { die("%s does not contain a v%d CTF header\n", filename, - CTF_VERSION); + pp->ctp_version); } } else { die("%s contains unsupported CTF version %d\n", filename, pp->ctp_version); } /* * If the data buffer is compressed, then malloc a buffer large enough * to hold the decompressed data, and use zlib to decompress it. */ if (hp->cth_flags & CTF_F_COMPRESS) { z_stream zstr; void *buf; int rc; if ((buf = malloc(hp->cth_stroff + hp->cth_strlen)) == NULL) die("failed to allocate decompression buffer"); bzero(&zstr, sizeof (z_stream)); zstr.next_in = (void *)cd.cd_ctfdata; zstr.avail_in = cd.cd_ctflen; zstr.next_out = buf; zstr.avail_out = hp->cth_stroff + hp->cth_strlen; if ((rc = inflateInit(&zstr)) != Z_OK) die("failed to initialize zlib: %s\n", zError(rc)); if ((rc = inflate(&zstr, Z_FINISH)) != Z_STREAM_END) die("failed to decompress CTF data: %s\n", zError(rc)); if ((rc = inflateEnd(&zstr)) != Z_OK) die("failed to finish decompression: %s\n", zError(rc)); if (zstr.total_out != hp->cth_stroff + hp->cth_strlen) die("CTF data is corrupt -- short decompression\n"); cd.cd_ctfdata = buf; cd.cd_ctflen = hp->cth_stroff + hp->cth_strlen; } if (flags & F_HDR) error |= print_header(hp, &cd); if (flags & (F_LABEL)) error |= print_labeltable(hp, &cd); if (flags & (F_DATA | F_STATS)) error |= read_data(hp, &cd); if (flags & (F_FUNC | F_STATS)) error |= read_funcs(hp, &cd); if (flags & (F_TYPES | F_STATS)) error |= read_types(hp, &cd); if (flags & (F_STR | F_STATS)) error |= read_strtab(hp, &cd); if (flags & F_STATS) error |= print_stats(); /* * If the -u option is specified, write the uncompressed CTF data to a * raw CTF file. CTF data can already be extracted compressed by * applying elfdump -w -N .SUNW_ctf to an ELF file, so we don't bother. */ if (ufile != NULL) { ctf_header_t h; bcopy(hp, &h, sizeof (h)); h.cth_flags &= ~CTF_F_COMPRESS; if ((ufd = open(ufile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 || write(ufd, &h, sizeof (h)) != sizeof (h) || write(ufd, cd.cd_ctfdata, cd.cd_ctflen) != (int) cd.cd_ctflen) { warn("failed to write CTF data to '%s'", ufile); error |= E_ERROR; } (void) close(ufd); } if (elf != NULL) (void) elf_end(elf); (void) close(fd); return (error); } diff --git a/sys/sys/ctf.h b/sys/sys/ctf.h index 4072e318320c..1a724c842fef 100644 --- a/sys/sys/ctf.h +++ b/sys/sys/ctf.h @@ -1,301 +1,301 @@ /* $OpenBSD: ctf.h,v 1.5 2017/08/13 14:56:05 nayden Exp $ */ /*- * SPDX-License-Identifier: ISC * * Copyright (c) 2016 Martin Pieuchot * Copyright (c) 2022 The FreeBSD Foundation * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _SYS_CTF_H_ #define _SYS_CTF_H_ #include /* * CTF ``Compact ANSI-C Type Format'' ABI header file. * * See the ctf(5) manual page for a detailed description of the format. */ typedef struct ctf_preamble { __uint16_t ctp_magic; __uint8_t ctp_version; __uint8_t ctp_flags; } ctf_preamble_t; typedef struct ctf_header { struct ctf_preamble cth_preamble; #define cth_magic cth_preamble.ctp_magic #define cth_version cth_preamble.ctp_version #define cth_flags cth_preamble.ctp_flags __uint32_t cth_parlabel; __uint32_t cth_parname; __uint32_t cth_lbloff; __uint32_t cth_objtoff; __uint32_t cth_funcoff; __uint32_t cth_typeoff; __uint32_t cth_stroff; __uint32_t cth_strlen; } ctf_header_t; #define CTF_F_COMPRESS (1 << 0) /* zlib compression */ typedef struct ctf_lblent { __uint32_t ctl_label; __uint32_t ctl_typeidx; } ctf_lblent_t; struct ctf_stype_v2 { __uint32_t ctt_name; __uint16_t ctt_info; union { __uint16_t _size; __uint16_t _type; } _u; }; struct ctf_stype_v3 { __uint32_t ctt_name; __uint32_t ctt_info; union { __uint32_t _size; __uint32_t _type; } _u; }; struct ctf_type_v2 { __uint32_t ctt_name; __uint16_t ctt_info; union { __uint16_t _size; __uint16_t _type; } _u; __uint32_t ctt_lsizehi; __uint32_t ctt_lsizelo; }; struct ctf_type_v3 { __uint32_t ctt_name; __uint32_t ctt_info; union { __uint32_t _size; __uint32_t _type; } _u; __uint32_t ctt_lsizehi; __uint32_t ctt_lsizelo; }; #define ctt_size _u._size #define ctt_type _u._type struct ctf_array_v2 { __uint16_t cta_contents; __uint16_t cta_index; __uint32_t cta_nelems; }; struct ctf_array_v3 { __uint32_t cta_contents; __uint32_t cta_index; __uint32_t cta_nelems; }; struct ctf_member_v2 { __uint32_t ctm_name; __uint16_t ctm_type; __uint16_t ctm_offset; }; struct ctf_member_v3 { __uint32_t ctm_name; __uint32_t ctm_type; __uint32_t ctm_offset; }; struct ctf_lmember_v2 { __uint32_t ctlm_name; __uint16_t ctlm_type; __uint16_t ctlm_pad; __uint32_t ctlm_offsethi; __uint32_t ctlm_offsetlo; }; struct ctf_lmember_v3 { __uint32_t ctlm_name; __uint32_t ctlm_type; __uint32_t ctlm_offsethi; __uint32_t ctlm_offsetlo; }; #define CTF_V2_LSTRUCT_THRESH (1 << 13) #define CTF_V3_LSTRUCT_THRESH (1 << 29) typedef struct ctf_enum { __uint32_t cte_name; __int32_t cte_value; } ctf_enum_t; #define CTF_MAGIC 0xcff1 -#define CTF_VERSION CTF_VERSION_2 +#define CTF_VERSION CTF_VERSION_3 #define CTF_VERSION_3 3 #define CTF_VERSION_2 2 #define CTF_VERSION_1 1 #define CTF_MAX_NAME 0x7fffffff #define CTF_V2_MAX_VLEN 0x03ff #define CTF_V2_MAX_SIZE 0xfffe #define CTF_V2_LSIZE_SENT (CTF_V2_MAX_SIZE + 1) /* sentinel for cts vs ctt */ #define CTF_V3_MAX_VLEN 0x00ffffff #define CTF_V3_MAX_SIZE 0xfffffffeu #define CTF_V3_LSIZE_SENT (CTF_V3_MAX_SIZE + 1) #define CTF_V2_PARENT_SHIFT 15 #define CTF_V2_MAX_TYPE 0xffff #define CTF_V2_TYPE_ISPARENT(id) ((id) < 0x8000) #define CTF_V2_TYPE_ISCHILD(id) ((id) > 0x7fff) #define CTF_V2_TYPE_TO_INDEX(type) ((type) & 0x7fff) #define CTF_V2_INDEX_TO_TYPE(type, ischild) \ (((type) & 0x7fff) | ((ischild) != 0 ? 0x8000 : 0)) #define CTF_V2_TYPE_INFO(kind, isroot, vlen) \ (((kind) << 11) | ((isroot) != 0 ? (1 << 10) : 0) | \ ((vlen) & CTF_V2_MAX_VLEN)) #define CTF_V3_PARENT_SHIFT 31 #define CTF_V3_MAX_TYPE 0xfffffffeu #define CTF_V3_TYPE_ISPARENT(id) ((__uint32_t)(id) < 0x80000000u) #define CTF_V3_TYPE_ISCHILD(id) ((__uint32_t)(id) > 0x7fffffffu) #define CTF_V3_TYPE_TO_INDEX(type) ((type) & 0x7fffffffu) #define CTF_V3_INDEX_TO_TYPE(type, ischild) \ (((type) & 0x7fffffffu) | ((ischild) != 0 ? 0x80000000u : 0)) #define CTF_V3_TYPE_INFO(kind, isroot, vlen) \ (((kind) << 26) | ((isroot) != 0 ? (1 << 25) : 0) | \ ((vlen) & CTF_V3_MAX_VLEN)) #define CTF_STRTAB_0 0 #define CTF_STRTAB_1 1 #define CTF_TYPE_NAME(t, o) (((t) << 31) | ((o) & ((1u << 31) - 1))) /* * Info macro. */ #define CTF_V2_INFO_VLEN(i) ((i) & CTF_V2_MAX_VLEN) #define CTF_V2_INFO_ISROOT(i) (((i) & 0x0400) >> 10) #define CTF_V2_INFO_KIND(i) (((i) & 0xf800) >> 11) #define CTF_V3_INFO_VLEN(i) ((i) & CTF_V3_MAX_VLEN) #define CTF_V3_INFO_ISROOT(i) (((i) & 0x02000000) >> 25) #define CTF_V3_INFO_KIND(i) (((i) & 0xfc000000) >> 26) #define CTF_K_UNKNOWN 0 #define CTF_K_INTEGER 1 #define CTF_K_FLOAT 2 #define CTF_K_POINTER 3 #define CTF_K_ARRAY 4 #define CTF_K_FUNCTION 5 #define CTF_K_STRUCT 6 #define CTF_K_UNION 7 #define CTF_K_ENUM 8 #define CTF_K_FORWARD 9 #define CTF_K_TYPEDEF 10 #define CTF_K_VOLATILE 11 #define CTF_K_CONST 12 #define CTF_K_RESTRICT 13 #define CTF_K_MAX 63 /* * Integer/Float Encoding macro. */ #define _CTF_ENCODING(e) (((e) & 0xff000000) >> 24) #define _CTF_OFFSET(e) (((e) & 0x00ff0000) >> 16) #define _CTF_BITS(e) (((e) & 0x0000ffff)) #define _CTF_DATA(encoding, offset, bits) \ (((encoding) << 24) | ((offset) << 16) | (bits)) #define CTF_INT_ENCODING(e) _CTF_ENCODING(e) #define CTF_INT_SIGNED (1 << 0) #define CTF_INT_CHAR (1 << 1) #define CTF_INT_BOOL (1 << 2) #define CTF_INT_VARARGS (1 << 3) #define CTF_INT_OFFSET(e) _CTF_OFFSET(e) #define CTF_INT_BITS(e) _CTF_BITS(e) #define CTF_INT_DATA(e, o, b) _CTF_DATA(e, o, b) #define CTF_FP_ENCODING(e) _CTF_ENCODING(e) #define CTF_FP_SINGLE 1 #define CTF_FP_DOUBLE 2 #define CTF_FP_CPLX 3 #define CTF_FP_DCPLX 4 #define CTF_FP_LDCPLX 5 #define CTF_FP_LDOUBLE 6 #define CTF_FP_INTRVL 7 #define CTF_FP_DINTRVL 8 #define CTF_FP_LDINTRVL 9 #define CTF_FP_IMAGRY 10 #define CTF_FP_DIMAGRY 11 #define CTF_FP_LDIMAGRY 12 #define CTF_FP_OFFSET(e) _CTF_OFFSET(e) #define CTF_FP_BITS(e) _CTF_BITS(e) #define CTF_FP_DATA(e, o, b) _CTF_DATA(e, o, b) /* * Name reference macro. */ #define CTF_NAME_STID(n) ((n) >> 31) #define CTF_NAME_OFFSET(n) ((n) & CTF_MAX_NAME) /* * Type macro. */ #define CTF_SIZE_TO_LSIZE_HI(s) ((uint32_t)((uint64_t)(s) >> 32)) #define CTF_SIZE_TO_LSIZE_LO(s) ((uint32_t)(s)) #define CTF_TYPE_LSIZE(t) \ (((uint64_t)(t)->ctt_lsizehi) << 32 | (t)->ctt_lsizelo) /* * Member macro. */ #define CTF_LMEM_OFFSET(m) \ (((__uint64_t)(m)->ctlm_offsethi) << 32 | (m)->ctlm_offsetlo) #define CTF_OFFSET_TO_LMEMHI(off) ((__uint32_t)((__uint64_t)(off) >> 32)) #define CTF_OFFSET_TO_LMEMLO(off) ((__uint32_t)(off)) /* * Compatibility for pre-v3 code. */ typedef struct ctf_array_v2 ctf_array_t; typedef struct ctf_member_v2 ctf_member_t; typedef struct ctf_lmember_v2 ctf_lmember_t; typedef struct ctf_type_v2 ctf_type_t; typedef struct ctf_stype_v2 ctf_stype_t; #define CTF_INFO_KIND CTF_V2_INFO_KIND #define CTF_INFO_VLEN CTF_V2_INFO_VLEN #define CTF_INFO_ISROOT CTF_V2_INFO_ISROOT #define CTF_TYPE_INFO CTF_V2_TYPE_INFO #define CTF_TYPE_ISPARENT CTF_V2_TYPE_ISPARENT #define CTF_TYPE_ISCHILD CTF_V2_TYPE_ISCHILD #define CTF_TYPE_TO_INDEX CTF_V2_TYPE_TO_INDEX #define CTF_INDEX_TO_TYPE CTF_V2_INDEX_TO_TYPE #define CTF_LSIZE_SENT CTF_V2_LSIZE_SENT #define CTF_LSTRUCT_THRESH CTF_V2_LSTRUCT_THRESH #define CTF_MAX_SIZE CTF_V2_MAX_SIZE #define CTF_MAX_TYPE CTF_V2_MAX_TYPE #define CTF_MAX_VLEN CTF_V2_MAX_VLEN #endif /* _SYS_CTF_H_ */