Index: head/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c =================================================================== --- head/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c (revision 233406) +++ head/cddl/contrib/opensolaris/tools/ctf/cvt/ctf.c (revision 233407) @@ -1,1285 +1,1384 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Create and parse buffers containing CTF data. */ #include #include #include #include #include #include #include #include "ctf_headers.h" #include "ctftools.h" #include "strtab.h" #include "memory.h" /* * Name of the file currently being read, used to print error messages. We * assume that only one file will be read at a time, and thus make no attempt * to allow curfile to be used simultaneously by multiple threads. * * The value is only valid during a call to ctf_load. */ char *curfile; #define CTF_BUF_CHUNK_SIZE (64 * 1024) #define RES_BUF_CHUNK_SIZE (64 * 1024) struct ctf_buf { strtab_t ctb_strtab; /* string table */ caddr_t ctb_base; /* pointer to base of buffer */ caddr_t ctb_end; /* pointer to end of buffer */ caddr_t ctb_ptr; /* pointer to empty buffer space */ size_t ctb_size; /* size of buffer */ int nptent; /* number of processed types */ int ntholes; /* number of type holes */ }; +/* + * Macros to reverse byte order + */ +#define BSWAP_8(x) ((x) & 0xff) +#define BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8)) +#define BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16)) + +#define SWAP_16(x) (x) = BSWAP_16(x) +#define SWAP_32(x) (x) = BSWAP_32(x) + +static int target_requires_swap; + /*PRINTFLIKE1*/ static void parseterminate(const char *fmt, ...) { static char msgbuf[1024]; /* sigh */ va_list ap; va_start(ap, fmt); vsnprintf(msgbuf, sizeof (msgbuf), fmt, ap); va_end(ap); terminate("%s: %s\n", curfile, msgbuf); } static void ctf_buf_grow(ctf_buf_t *b) { off_t ptroff = b->ctb_ptr - b->ctb_base; b->ctb_size += CTF_BUF_CHUNK_SIZE; b->ctb_base = xrealloc(b->ctb_base, b->ctb_size); b->ctb_end = b->ctb_base + b->ctb_size; b->ctb_ptr = b->ctb_base + ptroff; } static ctf_buf_t * ctf_buf_new(void) { ctf_buf_t *b = xcalloc(sizeof (ctf_buf_t)); strtab_create(&b->ctb_strtab); ctf_buf_grow(b); return (b); } static void ctf_buf_free(ctf_buf_t *b) { strtab_destroy(&b->ctb_strtab); free(b->ctb_base); free(b); } static uint_t ctf_buf_cur(ctf_buf_t *b) { return (b->ctb_ptr - b->ctb_base); } static void ctf_buf_write(ctf_buf_t *b, void const *p, size_t n) { size_t len; while (n != 0) { if (b->ctb_ptr == b->ctb_end) ctf_buf_grow(b); len = MIN((size_t)(b->ctb_end - b->ctb_ptr), n); bcopy(p, b->ctb_ptr, len); b->ctb_ptr += len; p = (char const *)p + len; n -= len; } } static int write_label(void *arg1, void *arg2) { labelent_t *le = arg1; ctf_buf_t *b = arg2; ctf_lblent_t ctl; ctl.ctl_label = strtab_insert(&b->ctb_strtab, le->le_name); ctl.ctl_typeidx = le->le_idx; + if (target_requires_swap) { + SWAP_32(ctl.ctl_label); + SWAP_32(ctl.ctl_typeidx); + } + ctf_buf_write(b, &ctl, sizeof (ctl)); return (1); } static void write_objects(iidesc_t *idp, ctf_buf_t *b) { ushort_t id = (idp ? idp->ii_dtype->t_id : 0); ctf_buf_write(b, &id, sizeof (id)); + if (target_requires_swap) { + SWAP_16(id); + } + debug(3, "Wrote object %s (%d)\n", (idp ? idp->ii_name : "(null)"), id); } static void write_functions(iidesc_t *idp, ctf_buf_t *b) { ushort_t fdata[2]; ushort_t id; int nargs; int i; if (!idp) { fdata[0] = 0; ctf_buf_write(b, &fdata[0], sizeof (fdata[0])); debug(3, "Wrote function (null)\n"); return; } nargs = idp->ii_nargs + (idp->ii_vargs != 0); if (nargs > CTF_MAX_VLEN) { terminate("function %s has too many args: %d > %d\n", idp->ii_name, nargs, CTF_MAX_VLEN); } fdata[0] = CTF_TYPE_INFO(CTF_K_FUNCTION, 1, nargs); fdata[1] = idp->ii_dtype->t_id; + + if (target_requires_swap) { + SWAP_16(fdata[0]); + SWAP_16(fdata[1]); + } + ctf_buf_write(b, fdata, sizeof (fdata)); for (i = 0; i < idp->ii_nargs; i++) { id = idp->ii_args[i]->t_id; + + if (target_requires_swap) { + SWAP_16(id); + } + ctf_buf_write(b, &id, sizeof (id)); } if (idp->ii_vargs) { id = 0; ctf_buf_write(b, &id, sizeof (id)); } debug(3, "Wrote function %s (%d args)\n", idp->ii_name, nargs); } /* * Depending on the size of the type being described, either a ctf_stype_t (for * types with size < CTF_LSTRUCT_THRESH) or a ctf_type_t (all others) will be * written. We isolate the determination here so the rest of the writer code * doesn't need to care. */ static void write_sized_type_rec(ctf_buf_t *b, ctf_type_t *ctt, size_t size) { if (size > CTF_MAX_SIZE) { ctt->ctt_size = CTF_LSIZE_SENT; ctt->ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size); ctt->ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size); + if (target_requires_swap) { + SWAP_32(ctt->ctt_name); + SWAP_16(ctt->ctt_info); + SWAP_16(ctt->ctt_size); + SWAP_32(ctt->ctt_lsizehi); + SWAP_32(ctt->ctt_lsizelo); + } ctf_buf_write(b, ctt, sizeof (*ctt)); } else { ctf_stype_t *cts = (ctf_stype_t *)ctt; cts->ctt_size = (ushort_t)size; + + if (target_requires_swap) { + SWAP_32(cts->ctt_name); + SWAP_16(cts->ctt_info); + SWAP_16(cts->ctt_size); + } + ctf_buf_write(b, cts, sizeof (*cts)); } } static void write_unsized_type_rec(ctf_buf_t *b, ctf_type_t *ctt) { ctf_stype_t *cts = (ctf_stype_t *)ctt; + if (target_requires_swap) { + SWAP_32(cts->ctt_name); + SWAP_16(cts->ctt_info); + SWAP_16(cts->ctt_size); + } + ctf_buf_write(b, cts, sizeof (*cts)); } static int write_type(void *arg1, void *arg2) { tdesc_t *tp = arg1; ctf_buf_t *b = arg2; elist_t *ep; mlist_t *mp; intr_t *ip; size_t offset; uint_t encoding; uint_t data; int isroot = tp->t_flags & TDESC_F_ISROOT; int i; ctf_type_t ctt; ctf_array_t cta; ctf_member_t ctm; ctf_lmember_t ctlm; ctf_enum_t cte; ushort_t id; ctlm.ctlm_pad = 0; /* * There shouldn't be any holes in the type list (where a hole is * defined as two consecutive tdescs without consecutive ids), but * check for them just in case. If we do find holes, we need to make * fake entries to fill the holes, or we won't be able to reconstruct * the tree from the written data. */ if (++b->nptent < CTF_TYPE_TO_INDEX(tp->t_id)) { debug(2, "genctf: type hole from %d < x < %d\n", b->nptent - 1, CTF_TYPE_TO_INDEX(tp->t_id)); ctt.ctt_name = CTF_TYPE_NAME(CTF_STRTAB_0, 0); ctt.ctt_info = CTF_TYPE_INFO(0, 0, 0); while (b->nptent < CTF_TYPE_TO_INDEX(tp->t_id)) { write_sized_type_rec(b, &ctt, 0); b->nptent++; } } offset = strtab_insert(&b->ctb_strtab, tp->t_name); ctt.ctt_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset); switch (tp->t_type) { case INTRINSIC: ip = tp->t_intr; if (ip->intr_type == INTR_INT) ctt.ctt_info = CTF_TYPE_INFO(CTF_K_INTEGER, isroot, 1); else ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FLOAT, isroot, 1); write_sized_type_rec(b, &ctt, tp->t_size); encoding = 0; if (ip->intr_type == INTR_INT) { if (ip->intr_signed) encoding |= CTF_INT_SIGNED; if (ip->intr_iformat == 'c') encoding |= CTF_INT_CHAR; else if (ip->intr_iformat == 'b') encoding |= CTF_INT_BOOL; else if (ip->intr_iformat == 'v') encoding |= CTF_INT_VARARGS; } else encoding = ip->intr_fformat; data = CTF_INT_DATA(encoding, ip->intr_offset, ip->intr_nbits); + if (target_requires_swap) { + SWAP_32(data); + } ctf_buf_write(b, &data, sizeof (data)); break; case POINTER: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_POINTER, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; case ARRAY: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_ARRAY, isroot, 1); write_sized_type_rec(b, &ctt, tp->t_size); cta.cta_contents = tp->t_ardef->ad_contents->t_id; cta.cta_index = tp->t_ardef->ad_idxtype->t_id; cta.cta_nelems = tp->t_ardef->ad_nelems; + if (target_requires_swap) { + SWAP_16(cta.cta_contents); + SWAP_16(cta.cta_index); + SWAP_32(cta.cta_nelems); + } ctf_buf_write(b, &cta, sizeof (cta)); break; case STRUCT: case UNION: for (i = 0, mp = tp->t_members; mp != NULL; mp = mp->ml_next) i++; /* count up struct or union members */ if (i > CTF_MAX_VLEN) { terminate("sou %s has too many members: %d > %d\n", tdesc_name(tp), i, CTF_MAX_VLEN); } if (tp->t_type == STRUCT) ctt.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, isroot, i); else ctt.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, isroot, i); write_sized_type_rec(b, &ctt, tp->t_size); if (tp->t_size < CTF_LSTRUCT_THRESH) { for (mp = tp->t_members; mp != NULL; mp = mp->ml_next) { offset = strtab_insert(&b->ctb_strtab, mp->ml_name); ctm.ctm_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset); ctm.ctm_type = mp->ml_type->t_id; ctm.ctm_offset = mp->ml_offset; + if (target_requires_swap) { + SWAP_32(ctm.ctm_name); + SWAP_16(ctm.ctm_type); + SWAP_16(ctm.ctm_offset); + } ctf_buf_write(b, &ctm, sizeof (ctm)); } } else { for (mp = tp->t_members; mp != NULL; mp = mp->ml_next) { offset = strtab_insert(&b->ctb_strtab, mp->ml_name); ctlm.ctlm_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset); ctlm.ctlm_type = mp->ml_type->t_id; ctlm.ctlm_offsethi = CTF_OFFSET_TO_LMEMHI(mp->ml_offset); ctlm.ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO(mp->ml_offset); + + if (target_requires_swap) { + SWAP_32(ctlm.ctlm_name); + SWAP_16(ctlm.ctlm_type); + SWAP_32(ctlm.ctlm_offsethi); + SWAP_32(ctlm.ctlm_offsetlo); + } + ctf_buf_write(b, &ctlm, sizeof (ctlm)); } } break; case ENUM: for (i = 0, ep = tp->t_emem; ep != NULL; ep = ep->el_next) i++; /* count up enum members */ if (i > CTF_MAX_VLEN) { warning("enum %s has too many values: %d > %d\n", tdesc_name(tp), i, CTF_MAX_VLEN); i = CTF_MAX_VLEN; } ctt.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, isroot, i); write_sized_type_rec(b, &ctt, tp->t_size); for (ep = tp->t_emem; ep != NULL && i > 0; ep = ep->el_next) { offset = strtab_insert(&b->ctb_strtab, ep->el_name); cte.cte_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset); cte.cte_value = ep->el_number; + + if (target_requires_swap) { + SWAP_32(cte.cte_name); + SWAP_32(cte.cte_value); + } + ctf_buf_write(b, &cte, sizeof (cte)); i--; } break; case FORWARD: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FORWARD, isroot, 0); ctt.ctt_type = 0; write_unsized_type_rec(b, &ctt); break; case TYPEDEF: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_TYPEDEF, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; case VOLATILE: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_VOLATILE, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; case CONST: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_CONST, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; case FUNCTION: i = tp->t_fndef->fn_nargs + tp->t_fndef->fn_vargs; if (i > CTF_MAX_VLEN) { terminate("function %s has too many args: %d > %d\n", i, CTF_MAX_VLEN); } ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FUNCTION, isroot, i); ctt.ctt_type = tp->t_fndef->fn_ret->t_id; write_unsized_type_rec(b, &ctt); for (i = 0; i < (int) tp->t_fndef->fn_nargs; i++) { id = tp->t_fndef->fn_args[i]->t_id; + + if (target_requires_swap) { + SWAP_16(id); + } + ctf_buf_write(b, &id, sizeof (id)); } if (tp->t_fndef->fn_vargs) { id = 0; ctf_buf_write(b, &id, sizeof (id)); i++; } if (i & 1) { id = 0; ctf_buf_write(b, &id, sizeof (id)); } break; case RESTRICT: ctt.ctt_info = CTF_TYPE_INFO(CTF_K_RESTRICT, isroot, 0); ctt.ctt_type = tp->t_tdesc->t_id; write_unsized_type_rec(b, &ctt); break; default: warning("Can't write unknown type %d\n", tp->t_type); } debug(3, "Wrote type %d %s\n", tp->t_id, tdesc_name(tp)); return (1); } typedef struct resbuf { caddr_t rb_base; caddr_t rb_ptr; size_t rb_size; z_stream rb_zstr; } resbuf_t; static void rbzs_grow(resbuf_t *rb) { off_t ptroff = (caddr_t)rb->rb_zstr.next_out - rb->rb_base; rb->rb_size += RES_BUF_CHUNK_SIZE; rb->rb_base = xrealloc(rb->rb_base, rb->rb_size); rb->rb_ptr = rb->rb_base + ptroff; rb->rb_zstr.next_out = (Bytef *)(rb->rb_ptr); rb->rb_zstr.avail_out += RES_BUF_CHUNK_SIZE; } static void compress_start(resbuf_t *rb) { int rc; rb->rb_zstr.zalloc = (alloc_func)0; rb->rb_zstr.zfree = (free_func)0; rb->rb_zstr.opaque = (voidpf)0; if ((rc = deflateInit(&rb->rb_zstr, Z_BEST_COMPRESSION)) != Z_OK) parseterminate("zlib start failed: %s", zError(rc)); } static ssize_t compress_buffer(void *buf, size_t n, void *data) { resbuf_t *rb = (resbuf_t *)data; int rc; rb->rb_zstr.next_out = (Bytef *)rb->rb_ptr; rb->rb_zstr.avail_out = rb->rb_size - (rb->rb_ptr - rb->rb_base); rb->rb_zstr.next_in = buf; rb->rb_zstr.avail_in = n; while (rb->rb_zstr.avail_in) { if (rb->rb_zstr.avail_out == 0) rbzs_grow(rb); if ((rc = deflate(&rb->rb_zstr, Z_NO_FLUSH)) != Z_OK) parseterminate("zlib deflate failed: %s", zError(rc)); } rb->rb_ptr = (caddr_t)rb->rb_zstr.next_out; return (n); } static void compress_flush(resbuf_t *rb, int type) { int rc; for (;;) { if (rb->rb_zstr.avail_out == 0) rbzs_grow(rb); rc = deflate(&rb->rb_zstr, type); if ((type == Z_FULL_FLUSH && rc == Z_BUF_ERROR) || (type == Z_FINISH && rc == Z_STREAM_END)) break; else if (rc != Z_OK) parseterminate("zlib finish failed: %s", zError(rc)); } rb->rb_ptr = (caddr_t)rb->rb_zstr.next_out; } static void compress_end(resbuf_t *rb) { int rc; compress_flush(rb, Z_FINISH); if ((rc = deflateEnd(&rb->rb_zstr)) != Z_OK) parseterminate("zlib end failed: %s", zError(rc)); } /* * Pad the buffer to a power-of-2 boundary */ static void pad_buffer(ctf_buf_t *buf, int align) { uint_t cur = ctf_buf_cur(buf); ssize_t topad = (align - (cur % align)) % align; static const char pad[8] = { 0 }; while (topad > 0) { ctf_buf_write(buf, pad, (topad > 8 ? 8 : topad)); topad -= 8; } } static ssize_t bcopy_data(void *buf, size_t n, void *data) { caddr_t *posp = (caddr_t *)data; bcopy(buf, *posp, n); *posp += n; return (n); } static caddr_t write_buffer(ctf_header_t *h, ctf_buf_t *buf, size_t *resszp) { caddr_t outbuf; caddr_t bufpos; outbuf = xmalloc(sizeof (ctf_header_t) + (buf->ctb_ptr - buf->ctb_base) + buf->ctb_strtab.str_size); bufpos = outbuf; (void) bcopy_data(h, sizeof (ctf_header_t), &bufpos); (void) bcopy_data(buf->ctb_base, buf->ctb_ptr - buf->ctb_base, &bufpos); (void) strtab_write(&buf->ctb_strtab, bcopy_data, &bufpos); *resszp = bufpos - outbuf; return (outbuf); } /* * Create the compression buffer, and fill it with the CTF and string * table data. We flush the compression state between the two so the * dictionary used for the string tables won't be polluted with values * that made sense for the CTF data. */ static caddr_t write_compressed_buffer(ctf_header_t *h, ctf_buf_t *buf, size_t *resszp) { resbuf_t resbuf; resbuf.rb_size = RES_BUF_CHUNK_SIZE; resbuf.rb_base = xmalloc(resbuf.rb_size); bcopy(h, resbuf.rb_base, sizeof (ctf_header_t)); resbuf.rb_ptr = resbuf.rb_base + sizeof (ctf_header_t); compress_start(&resbuf); (void) compress_buffer(buf->ctb_base, buf->ctb_ptr - buf->ctb_base, &resbuf); compress_flush(&resbuf, Z_FULL_FLUSH); (void) strtab_write(&buf->ctb_strtab, compress_buffer, &resbuf); compress_end(&resbuf); *resszp = (resbuf.rb_ptr - resbuf.rb_base); return (resbuf.rb_base); } caddr_t ctf_gen(iiburst_t *iiburst, size_t *resszp, int do_compress) { ctf_buf_t *buf = ctf_buf_new(); ctf_header_t h; caddr_t outbuf; int i; + target_requires_swap = do_compress & CTF_SWAP_BYTES; + do_compress &= ~CTF_SWAP_BYTES; + /* * Prepare the header, and create the CTF output buffers. The data * object section and function section are both lists of 2-byte * integers; we pad these out to the next 4-byte boundary if needed. */ h.cth_magic = CTF_MAGIC; h.cth_version = CTF_VERSION; h.cth_flags = do_compress ? CTF_F_COMPRESS : 0; h.cth_parlabel = strtab_insert(&buf->ctb_strtab, iiburst->iib_td->td_parlabel); h.cth_parname = strtab_insert(&buf->ctb_strtab, iiburst->iib_td->td_parname); h.cth_lbloff = 0; (void) list_iter(iiburst->iib_td->td_labels, write_label, buf); pad_buffer(buf, 2); h.cth_objtoff = ctf_buf_cur(buf); for (i = 0; i < iiburst->iib_nobjts; i++) write_objects(iiburst->iib_objts[i], buf); pad_buffer(buf, 2); h.cth_funcoff = ctf_buf_cur(buf); for (i = 0; i < iiburst->iib_nfuncs; i++) write_functions(iiburst->iib_funcs[i], buf); pad_buffer(buf, 4); h.cth_typeoff = ctf_buf_cur(buf); (void) list_iter(iiburst->iib_types, write_type, buf); debug(2, "CTF wrote %d types\n", list_count(iiburst->iib_types)); h.cth_stroff = ctf_buf_cur(buf); h.cth_strlen = strtab_size(&buf->ctb_strtab); + + if (target_requires_swap) { + SWAP_16(h.cth_preamble.ctp_magic); + SWAP_32(h.cth_parlabel); + SWAP_32(h.cth_parname); + SWAP_32(h.cth_lbloff); + SWAP_32(h.cth_objtoff); + SWAP_32(h.cth_funcoff); + SWAP_32(h.cth_typeoff); + SWAP_32(h.cth_stroff); + SWAP_32(h.cth_strlen); + } /* * We only do compression for ctfmerge, as ctfconvert is only * supposed to be used on intermediary build objects. This is * significantly faster. */ if (do_compress) outbuf = write_compressed_buffer(&h, buf, resszp); else outbuf = write_buffer(&h, buf, resszp); ctf_buf_free(buf); return (outbuf); } static void get_ctt_size(ctf_type_t *ctt, size_t *sizep, size_t *incrementp) { if (ctt->ctt_size == CTF_LSIZE_SENT) { *sizep = (size_t)CTF_TYPE_LSIZE(ctt); *incrementp = sizeof (ctf_type_t); } else { *sizep = ctt->ctt_size; *incrementp = sizeof (ctf_stype_t); } } static int count_types(ctf_header_t *h, caddr_t data) { caddr_t dptr = data + h->cth_typeoff; int count = 0; dptr = data + h->cth_typeoff; while (dptr < data + h->cth_stroff) { void *v = (void *) dptr; ctf_type_t *ctt = v; size_t vlen = CTF_INFO_VLEN(ctt->ctt_info); size_t size, increment; get_ctt_size(ctt, &size, &increment); switch (CTF_INFO_KIND(ctt->ctt_info)) { case CTF_K_INTEGER: case CTF_K_FLOAT: dptr += 4; break; case CTF_K_POINTER: case CTF_K_FORWARD: case CTF_K_TYPEDEF: case CTF_K_VOLATILE: case CTF_K_CONST: case CTF_K_RESTRICT: case CTF_K_FUNCTION: dptr += sizeof (ushort_t) * (vlen + (vlen & 1)); break; case CTF_K_ARRAY: dptr += sizeof (ctf_array_t); break; case CTF_K_STRUCT: case CTF_K_UNION: if (size < CTF_LSTRUCT_THRESH) dptr += sizeof (ctf_member_t) * vlen; else dptr += sizeof (ctf_lmember_t) * vlen; break; case CTF_K_ENUM: dptr += sizeof (ctf_enum_t) * vlen; break; case CTF_K_UNKNOWN: break; default: parseterminate("Unknown CTF type %d (#%d) at %#x", CTF_INFO_KIND(ctt->ctt_info), count, dptr - data); } dptr += increment; count++; } debug(3, "CTF read %d types\n", count); return (count); } /* * Resurrect the labels stored in the CTF data, returning the index associated * with a label provided by the caller. There are several cases, outlined * below. Note that, given two labels, the one associated with the lesser type * index is considered to be older than the other. * * 1. matchlbl == NULL - return the index of the most recent label. * 2. matchlbl == "BASE" - return the index of the oldest label. * 3. matchlbl != NULL, but doesn't match any labels in the section - warn * the user, and proceed as if matchlbl == "BASE" (for safety). * 4. matchlbl != NULL, and matches one of the labels in the section - return * the type index associated with the label. */ static int resurrect_labels(ctf_header_t *h, tdata_t *td, caddr_t ctfdata, char *matchlbl) { caddr_t buf = ctfdata + h->cth_lbloff; caddr_t sbuf = ctfdata + h->cth_stroff; size_t bufsz = h->cth_objtoff - h->cth_lbloff; int lastidx = 0, baseidx = -1; char *baselabel = NULL; ctf_lblent_t *ctl; void *v = (void *) buf; for (ctl = v; (caddr_t)ctl < buf + bufsz; ctl++) { char *label = sbuf + ctl->ctl_label; lastidx = ctl->ctl_typeidx; debug(3, "Resurrected label %s type idx %d\n", label, lastidx); tdata_label_add(td, label, lastidx); if (baseidx == -1) { baseidx = lastidx; baselabel = label; if (matchlbl != NULL && streq(matchlbl, "BASE")) return (lastidx); } if (matchlbl != NULL && streq(label, matchlbl)) return (lastidx); } if (matchlbl != NULL) { /* User provided a label that didn't match */ warning("%s: Cannot find label `%s' - using base (%s)\n", curfile, matchlbl, (baselabel ? baselabel : "NONE")); tdata_label_free(td); tdata_label_add(td, baselabel, baseidx); return (baseidx); } return (lastidx); } static void resurrect_objects(ctf_header_t *h, tdata_t *td, tdesc_t **tdarr, int tdsize, caddr_t ctfdata, symit_data_t *si) { caddr_t buf = ctfdata + h->cth_objtoff; size_t bufsz = h->cth_funcoff - h->cth_objtoff; caddr_t dptr; symit_reset(si); for (dptr = buf; dptr < buf + bufsz; dptr += 2) { void *v = (void *) dptr; ushort_t id = *((ushort_t *)v); iidesc_t *ii; GElf_Sym *sym; if (!(sym = symit_next(si, STT_OBJECT)) && id != 0) { parseterminate( "Unexpected end of object symbols at %x of %x", dptr - buf, bufsz); } if (id == 0) { debug(3, "Skipping null object\n"); continue; } else if (id >= tdsize) { parseterminate("Reference to invalid type %d", id); } ii = iidesc_new(symit_name(si)); ii->ii_dtype = tdarr[id]; if (GELF_ST_BIND(sym->st_info) == STB_LOCAL) { ii->ii_type = II_SVAR; ii->ii_owner = xstrdup(symit_curfile(si)); } else ii->ii_type = II_GVAR; hash_add(td->td_iihash, ii); debug(3, "Resurrected %s object %s (%d) from %s\n", (ii->ii_type == II_GVAR ? "global" : "static"), ii->ii_name, id, (ii->ii_owner ? ii->ii_owner : "(none)")); } } static void resurrect_functions(ctf_header_t *h, tdata_t *td, tdesc_t **tdarr, int tdsize, caddr_t ctfdata, symit_data_t *si) { caddr_t buf = ctfdata + h->cth_funcoff; size_t bufsz = h->cth_typeoff - h->cth_funcoff; caddr_t dptr = buf; iidesc_t *ii; ushort_t info; ushort_t retid; GElf_Sym *sym; int i; symit_reset(si); while (dptr < buf + bufsz) { void *v = (void *) dptr; info = *((ushort_t *)v); dptr += 2; if (!(sym = symit_next(si, STT_FUNC)) && info != 0) parseterminate("Unexpected end of function symbols"); if (info == 0) { debug(3, "Skipping null function (%s)\n", symit_name(si)); continue; } v = (void *) dptr; retid = *((ushort_t *)v); dptr += 2; if (retid >= tdsize) parseterminate("Reference to invalid type %d", retid); ii = iidesc_new(symit_name(si)); ii->ii_dtype = tdarr[retid]; if (GELF_ST_BIND(sym->st_info) == STB_LOCAL) { ii->ii_type = II_SFUN; ii->ii_owner = xstrdup(symit_curfile(si)); } else ii->ii_type = II_GFUN; ii->ii_nargs = CTF_INFO_VLEN(info); if (ii->ii_nargs) ii->ii_args = xmalloc(sizeof (tdesc_t *) * ii->ii_nargs); for (i = 0; i < ii->ii_nargs; i++, dptr += 2) { v = (void *) dptr; ushort_t id = *((ushort_t *)v); if (id >= tdsize) parseterminate("Reference to invalid type %d", id); ii->ii_args[i] = tdarr[id]; } if (ii->ii_nargs && ii->ii_args[ii->ii_nargs - 1] == NULL) { ii->ii_nargs--; ii->ii_vargs = 1; } hash_add(td->td_iihash, ii); debug(3, "Resurrected %s function %s (%d, %d args)\n", (ii->ii_type == II_GFUN ? "global" : "static"), ii->ii_name, retid, ii->ii_nargs); } } static void resurrect_types(ctf_header_t *h, tdata_t *td, tdesc_t **tdarr, int tdsize, caddr_t ctfdata, int maxid) { caddr_t buf = ctfdata + h->cth_typeoff; size_t bufsz = h->cth_stroff - h->cth_typeoff; caddr_t sbuf = ctfdata + h->cth_stroff; caddr_t dptr = buf; tdesc_t *tdp; uint_t data; uint_t encoding; size_t size, increment; int tcnt; int iicnt = 0; tid_t tid, argid; int kind, vlen; int i; elist_t **epp; mlist_t **mpp; intr_t *ip; ctf_type_t *ctt; ctf_array_t *cta; ctf_enum_t *cte; /* * A maxid of zero indicates a request to resurrect all types, so reset * maxid to the maximum type id. */ if (maxid == 0) maxid = CTF_MAX_TYPE; for (dptr = buf, tcnt = 0, tid = 1; dptr < buf + bufsz; tcnt++, tid++) { if (tid > maxid) break; if (tid >= tdsize) parseterminate("Reference to invalid type %d", tid); void *v = (void *) dptr; ctt = v; get_ctt_size(ctt, &size, &increment); dptr += increment; tdp = tdarr[tid]; if (CTF_NAME_STID(ctt->ctt_name) != CTF_STRTAB_0) parseterminate( "Unable to cope with non-zero strtab id"); if (CTF_NAME_OFFSET(ctt->ctt_name) != 0) { tdp->t_name = xstrdup(sbuf + CTF_NAME_OFFSET(ctt->ctt_name)); } else tdp->t_name = NULL; kind = CTF_INFO_KIND(ctt->ctt_info); vlen = CTF_INFO_VLEN(ctt->ctt_info); switch (kind) { case CTF_K_INTEGER: tdp->t_type = INTRINSIC; tdp->t_size = size; v = (void *) dptr; data = *((uint_t *)v); dptr += sizeof (uint_t); encoding = CTF_INT_ENCODING(data); ip = xmalloc(sizeof (intr_t)); ip->intr_type = INTR_INT; ip->intr_signed = (encoding & CTF_INT_SIGNED) ? 1 : 0; if (encoding & CTF_INT_CHAR) ip->intr_iformat = 'c'; else if (encoding & CTF_INT_BOOL) ip->intr_iformat = 'b'; else if (encoding & CTF_INT_VARARGS) ip->intr_iformat = 'v'; else ip->intr_iformat = '\0'; ip->intr_offset = CTF_INT_OFFSET(data); ip->intr_nbits = CTF_INT_BITS(data); tdp->t_intr = ip; break; case CTF_K_FLOAT: tdp->t_type = INTRINSIC; tdp->t_size = size; v = (void *) dptr; data = *((uint_t *)v); dptr += sizeof (uint_t); ip = xcalloc(sizeof (intr_t)); ip->intr_type = INTR_REAL; ip->intr_fformat = CTF_FP_ENCODING(data); ip->intr_offset = CTF_FP_OFFSET(data); ip->intr_nbits = CTF_FP_BITS(data); tdp->t_intr = ip; break; case CTF_K_POINTER: tdp->t_type = POINTER; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_ARRAY: tdp->t_type = ARRAY; tdp->t_size = size; v = (void *) dptr; cta = v; dptr += sizeof (ctf_array_t); tdp->t_ardef = xmalloc(sizeof (ardef_t)); tdp->t_ardef->ad_contents = tdarr[cta->cta_contents]; tdp->t_ardef->ad_idxtype = tdarr[cta->cta_index]; tdp->t_ardef->ad_nelems = cta->cta_nelems; break; case CTF_K_STRUCT: case CTF_K_UNION: tdp->t_type = (kind == CTF_K_STRUCT ? STRUCT : UNION); tdp->t_size = size; if (size < CTF_LSTRUCT_THRESH) { for (i = 0, mpp = &tdp->t_members; i < vlen; i++, mpp = &((*mpp)->ml_next)) { v = (void *) dptr; ctf_member_t *ctm = v; dptr += sizeof (ctf_member_t); *mpp = xmalloc(sizeof (mlist_t)); (*mpp)->ml_name = xstrdup(sbuf + ctm->ctm_name); (*mpp)->ml_type = tdarr[ctm->ctm_type]; (*mpp)->ml_offset = ctm->ctm_offset; (*mpp)->ml_size = 0; } } 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; } } *mpp = NULL; break; case CTF_K_ENUM: tdp->t_type = ENUM; tdp->t_size = size; for (i = 0, epp = &tdp->t_emem; i < vlen; i++, epp = &((*epp)->el_next)) { v = (void *) dptr; cte = v; dptr += sizeof (ctf_enum_t); *epp = xmalloc(sizeof (elist_t)); (*epp)->el_name = xstrdup(sbuf + cte->cte_name); (*epp)->el_number = cte->cte_value; } *epp = NULL; break; case CTF_K_FORWARD: tdp->t_type = FORWARD; list_add(&td->td_fwdlist, tdp); break; case CTF_K_TYPEDEF: tdp->t_type = TYPEDEF; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_VOLATILE: tdp->t_type = VOLATILE; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_CONST: tdp->t_type = CONST; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_FUNCTION: tdp->t_type = FUNCTION; tdp->t_fndef = xcalloc(sizeof (fndef_t)); tdp->t_fndef->fn_ret = tdarr[ctt->ctt_type]; v = (void *) (dptr + (sizeof (ushort_t) * (vlen - 1))); if (vlen > 0 && *(ushort_t *)v == 0) tdp->t_fndef->fn_vargs = 1; tdp->t_fndef->fn_nargs = vlen - tdp->t_fndef->fn_vargs; tdp->t_fndef->fn_args = xcalloc(sizeof (tdesc_t) * vlen - tdp->t_fndef->fn_vargs); for (i = 0; i < vlen; i++) { v = (void *) dptr; argid = *(ushort_t *)v; dptr += sizeof (ushort_t); if (argid != 0) tdp->t_fndef->fn_args[i] = tdarr[argid]; } if (vlen & 1) dptr += sizeof (ushort_t); break; case CTF_K_RESTRICT: tdp->t_type = RESTRICT; tdp->t_tdesc = tdarr[ctt->ctt_type]; break; case CTF_K_UNKNOWN: break; default: warning("Can't parse unknown CTF type %d\n", kind); } if (CTF_INFO_ISROOT(ctt->ctt_info)) { iidesc_t *ii = iidesc_new(tdp->t_name); if (tdp->t_type == STRUCT || tdp->t_type == UNION || tdp->t_type == ENUM) ii->ii_type = II_SOU; else ii->ii_type = II_TYPE; ii->ii_dtype = tdp; hash_add(td->td_iihash, ii); iicnt++; } debug(3, "Resurrected %d %stype %s (%d)\n", tdp->t_type, (CTF_INFO_ISROOT(ctt->ctt_info) ? "root " : ""), tdesc_name(tdp), tdp->t_id); } debug(3, "Resurrected %d types (%d were roots)\n", tcnt, iicnt); } /* * For lack of other inspiration, we're going to take the boring route. We * count the number of types. This lets us malloc that many tdesc structs * before we start filling them in. This has the advantage of allowing us to * avoid a merge-esque remap step. */ static tdata_t * ctf_parse(ctf_header_t *h, caddr_t buf, symit_data_t *si, char *label) { tdata_t *td = tdata_new(); tdesc_t **tdarr; int ntypes = count_types(h, buf); int idx, i; /* shudder */ tdarr = xcalloc(sizeof (tdesc_t *) * (ntypes + 1)); tdarr[0] = NULL; for (i = 1; i <= ntypes; i++) { tdarr[i] = xcalloc(sizeof (tdesc_t)); tdarr[i]->t_id = i; } td->td_parlabel = xstrdup(buf + h->cth_stroff + h->cth_parlabel); /* we have the technology - we can rebuild them */ idx = resurrect_labels(h, td, buf, label); resurrect_objects(h, td, tdarr, ntypes + 1, buf, si); resurrect_functions(h, td, tdarr, ntypes + 1, buf, si); resurrect_types(h, td, tdarr, ntypes + 1, buf, idx); free(tdarr); td->td_nextid = ntypes + 1; return (td); } static size_t decompress_ctf(caddr_t cbuf, size_t cbufsz, caddr_t dbuf, size_t dbufsz) { z_stream zstr; int rc; zstr.zalloc = (alloc_func)0; zstr.zfree = (free_func)0; zstr.opaque = (voidpf)0; zstr.next_in = (Bytef *)cbuf; zstr.avail_in = cbufsz; zstr.next_out = (Bytef *)dbuf; zstr.avail_out = dbufsz; if ((rc = inflateInit(&zstr)) != Z_OK || (rc = inflate(&zstr, Z_NO_FLUSH)) != Z_STREAM_END || (rc = inflateEnd(&zstr)) != Z_OK) { warning("CTF decompress zlib error %s\n", zError(rc)); return (0); } debug(3, "reflated %lu bytes to %lu, pointer at %d\n", zstr.total_in, zstr.total_out, (caddr_t)zstr.next_in - cbuf); return (zstr.total_out); } /* * Reconstruct the type tree from a given buffer of CTF data. Only the types * up to the type associated with the provided label, inclusive, will be * reconstructed. If a NULL label is provided, all types will be reconstructed. * * This function won't work on files that have been uniquified. */ tdata_t * ctf_load(char *file, caddr_t buf, size_t bufsz, symit_data_t *si, char *label) { ctf_header_t *h; caddr_t ctfdata; size_t ctfdatasz; tdata_t *td; curfile = file; if (bufsz < sizeof (ctf_header_t)) parseterminate("Corrupt CTF - short header"); void *v = (void *) buf; h = v; buf += sizeof (ctf_header_t); bufsz -= sizeof (ctf_header_t); if (h->cth_magic != CTF_MAGIC) parseterminate("Corrupt CTF - bad magic 0x%x", h->cth_magic); if (h->cth_version != CTF_VERSION) parseterminate("Unknown CTF version %d", h->cth_version); ctfdatasz = h->cth_stroff + h->cth_strlen; if (h->cth_flags & CTF_F_COMPRESS) { size_t actual; ctfdata = xmalloc(ctfdatasz); if ((actual = decompress_ctf(buf, bufsz, ctfdata, ctfdatasz)) != ctfdatasz) { parseterminate("Corrupt CTF - short decompression " "(was %d, expecting %d)", actual, ctfdatasz); } } else { ctfdata = buf; ctfdatasz = bufsz; } td = ctf_parse(h, ctfdata, si, label); if (h->cth_flags & CTF_F_COMPRESS) free(ctfdata); curfile = NULL; return (td); } Index: head/cddl/contrib/opensolaris/tools/ctf/cvt/ctfmerge.c =================================================================== --- head/cddl/contrib/opensolaris/tools/ctf/cvt/ctfmerge.c (revision 233406) +++ head/cddl/contrib/opensolaris/tools/ctf/cvt/ctfmerge.c (revision 233407) @@ -1,1024 +1,1024 @@ /* * 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 #if defined(sun) #include #endif #include #include #include #include #if defined(sun) #include #endif #include #include #include #if defined(sun) #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); } #if defined(sun) 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 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 | keep_stabs); + 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); 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); } #if defined(sun) 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)) terminate("No room for additional types in master\n"); savetd->td_nextid = withfile ? reftd->td_nextid : CTF_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 | write_fuzzy_match | dynsym | keep_stabs); + 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); } Index: head/cddl/contrib/opensolaris/tools/ctf/cvt/ctftools.h =================================================================== --- head/cddl/contrib/opensolaris/tools/ctf/cvt/ctftools.h (revision 233406) +++ head/cddl/contrib/opensolaris/tools/ctf/cvt/ctftools.h (revision 233407) @@ -1,453 +1,454 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _CTFTOOLS_H #define _CTFTOOLS_H #pragma ident "%Z%%M% %I% %E% SMI" /* * Functions and data structures used in the manipulation of stabs and CTF data */ #include #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif #include "list.h" #include "hash.h" #ifndef DEBUG_LEVEL #define DEBUG_LEVEL 0 #endif #ifndef DEBUG_PARSE #define DEBUG_PARSE 0 #endif #ifndef DEBUG_STREAM #define DEBUG_STREAM stderr #endif #ifndef MAX #define MAX(a, b) ((a) < (b) ? (b) : (a)) #endif #ifndef MIN #define MIN(a, b) ((a) > (b) ? (b) : (a)) #endif #define TRUE 1 #define FALSE 0 #define CTF_ELF_SCN_NAME ".SUNW_ctf" #define CTF_LABEL_LASTIDX -1 #define CTF_DEFAULT_LABEL "*** No Label Provided ***" /* * Default hash sizes */ #define TDATA_LAYOUT_HASH_SIZE 8191 /* A tdesc hash based on layout */ #define TDATA_ID_HASH_SIZE 997 /* A tdesc hash based on type id */ #define IIDESC_HASH_SIZE 8191 /* Hash of iidesc's */ /* * The default function argument array size. We'll realloc the array larger * if we need to, but we want a default value that will allow us to avoid * reallocation in the common case. */ #define FUNCARG_DEF 5 extern const char *progname; extern int debug_level; extern int debug_parse; extern char *curhdr; /* * This is a partial copy of the stab.h that DevPro includes with their * compiler. */ typedef struct stab { uint32_t n_strx; uint8_t n_type; int8_t n_other; int16_t n_desc; uint32_t n_value; } stab_t; #define N_GSYM 0x20 /* global symbol: name,,0,type,0 */ #define N_FUN 0x24 /* procedure: name,,0,linenumber,0 */ #define N_STSYM 0x26 /* static symbol: name,,0,type,0 or section relative */ #define N_LCSYM 0x28 /* .lcomm symbol: name,,0,type,0 or section relative */ #define N_ROSYM 0x2c /* ro_data: name,,0,type,0 or section relative */ #define N_OPT 0x3c /* compiler options */ #define N_RSYM 0x40 /* register sym: name,,0,type,register */ #define N_SO 0x64 /* source file name: name,,0,0,0 */ #define N_LSYM 0x80 /* local sym: name,,0,type,offset */ #define N_SOL 0x84 /* #included file name: name,,0,0,0 */ #define N_PSYM 0xa0 /* parameter: name,,0,type,offset */ #define N_LBRAC 0xc0 /* left bracket: 0,,0,nesting level,function relative */ #define N_RBRAC 0xe0 /* right bracket: 0,,0,nesting level,func relative */ #define N_BINCL 0x82 /* header file: name,,0,0,0 */ #define N_EINCL 0xa2 /* end of include file */ /* * Nodes in the type tree * * Each node consists of a single tdesc_t, with one of several auxiliary * structures linked in via the `data' union. */ /* The type of tdesc_t node */ typedef enum stabtype { STABTYPE_FIRST, /* do not use */ INTRINSIC, POINTER, ARRAY, FUNCTION, STRUCT, UNION, ENUM, FORWARD, TYPEDEF, TYPEDEF_UNRES, VOLATILE, CONST, RESTRICT, STABTYPE_LAST /* do not use */ } stabtype_t; typedef struct tdesc tdesc_t; /* Auxiliary structure for array tdesc_t */ typedef struct ardef { tdesc_t *ad_contents; tdesc_t *ad_idxtype; uint_t ad_nelems; } ardef_t; /* Auxiliary structure for structure/union tdesc_t */ typedef struct mlist { int ml_offset; /* Offset from start of structure (in bits) */ int ml_size; /* Member size (in bits) */ char *ml_name; /* Member name */ struct tdesc *ml_type; /* Member type */ struct mlist *ml_next; /* Next member */ } mlist_t; /* Auxiliary structure for enum tdesc_t */ typedef struct elist { char *el_name; int el_number; struct elist *el_next; } elist_t; /* Auxiliary structure for intrinsics (integers and reals) */ typedef enum { INTR_INT, INTR_REAL } intrtype_t; typedef struct intr { intrtype_t intr_type; int intr_signed; union { char _iformat; int _fformat; } _u; int intr_offset; int intr_nbits; } intr_t; #define intr_iformat _u._iformat #define intr_fformat _u._fformat typedef struct fnarg { char *fna_name; struct tdesc *fna_type; } fnarg_t; #define FN_F_GLOBAL 0x1 #define FN_F_VARARGS 0x2 typedef struct fndef { struct tdesc *fn_ret; uint_t fn_nargs; tdesc_t **fn_args; uint_t fn_vargs; } fndef_t; typedef int32_t tid_t; /* * The tdesc_t (Type DESCription) is the basic node type used in the stabs data * structure. Each data node gets a tdesc structure. Each node is linked into * a directed graph (think of it as a tree with multiple roots and multiple * leaves), with the root nodes at the top, and intrinsics at the bottom. The * root nodes, which are pointed to by iidesc nodes, correspond to the types, * globals, and statics defined by the stabs. */ struct tdesc { char *t_name; tdesc_t *t_next; /* Name hash next pointer */ tid_t t_id; tdesc_t *t_hash; /* ID hash next pointer */ stabtype_t t_type; int t_size; /* Size in bytes of object represented by this node */ union { intr_t *intr; /* int, real */ tdesc_t *tdesc; /* ptr, typedef, vol, const, restr */ ardef_t *ardef; /* array */ mlist_t *members; /* struct, union */ elist_t *emem; /* enum */ fndef_t *fndef; /* function - first is return type */ } t_data; int t_flags; int t_vgen; /* Visitation generation (see traverse.c) */ int t_emark; /* Equality mark (see equiv_cb() in merge.c) */ }; #define t_intr t_data.intr #define t_tdesc t_data.tdesc #define t_ardef t_data.ardef #define t_members t_data.members #define t_emem t_data.emem #define t_fndef t_data.fndef #define TDESC_F_ISROOT 0x1 /* Has an iidesc_t (see below) */ #define TDESC_F_GLOBAL 0x2 #define TDESC_F_RESOLVED 0x4 /* * iidesc_t (Interesting Item DESCription) nodes point to tdesc_t nodes that * correspond to "interesting" stabs. A stab is interesting if it defines a * global or static variable, a global or static function, or a data type. */ typedef enum iitype { II_NOT = 0, II_GFUN, /* Global function */ II_SFUN, /* Static function */ II_GVAR, /* Global variable */ II_SVAR, /* Static variable */ II_PSYM, /* Function argument */ II_SOU, /* Struct or union */ II_TYPE /* Type (typedef) */ } iitype_t; typedef struct iidesc { iitype_t ii_type; char *ii_name; tdesc_t *ii_dtype; char *ii_owner; /* File that defined this node */ int ii_flags; /* Function arguments (if any) */ int ii_nargs; tdesc_t **ii_args; int ii_vargs; /* Function uses varargs */ } iidesc_t; #define IIDESC_F_USED 0x1 /* Write this iidesc out */ /* * labelent_t nodes identify labels and corresponding type ranges associated * with them. The label in a given labelent_t is associated with types with * ids <= le_idx. */ typedef struct labelent { char *le_name; int le_idx; } labelent_t; /* * The tdata_t (Type DATA) structure contains or references all type data for * a given file or, during merging, several files. */ typedef struct tdata { int td_curemark; /* Equality mark (see merge.c) */ int td_curvgen; /* Visitation generation (see traverse.c) */ int td_nextid; /* The ID for the next tdesc_t created */ hash_t *td_iihash; /* The iidesc_t nodes for this file */ hash_t *td_layouthash; /* The tdesc nodes, hashed by structure */ hash_t *td_idhash; /* The tdesc nodes, hashed by type id */ list_t *td_fwdlist; /* All forward declaration tdesc nodes */ char *td_parlabel; /* Top label uniq'd against in parent */ char *td_parname; /* Basename of parent */ list_t *td_labels; /* Labels and their type ranges */ pthread_mutex_t td_mergelock; int td_ref; } tdata_t; /* * By design, the iidesc hash is heterogeneous. The CTF emitter, on the * other hand, needs to be able to access the elements of the list by type, * and in a specific sorted order. An iiburst holds these elements in that * order. (A burster is a machine that separates carbon-copy forms) */ typedef struct iiburst { int iib_nfuncs; int iib_curfunc; iidesc_t **iib_funcs; int iib_nobjts; int iib_curobjt; iidesc_t **iib_objts; list_t *iib_types; int iib_maxtypeid; tdata_t *iib_td; struct tdtrav_data *iib_tdtd; /* tdtrav_data_t */ } iiburst_t; typedef struct ctf_buf ctf_buf_t; typedef struct symit_data symit_data_t; /* fixup_tdescs.c */ void cvt_fixstabs(tdata_t *); void cvt_fixups(tdata_t *, size_t); /* ctf.c */ caddr_t ctf_gen(iiburst_t *, size_t *, int); tdata_t *ctf_load(char *, caddr_t, size_t, symit_data_t *, char *); /* iidesc.c */ iidesc_t *iidesc_new(char *); int iidesc_hash(int, void *); void iter_iidescs_by_name(tdata_t *, const char *, int (*)(void *, void *), void *); iidesc_t *iidesc_dup(iidesc_t *); iidesc_t *iidesc_dup_rename(iidesc_t *, char const *, char const *); void iidesc_add(hash_t *, iidesc_t *); void iidesc_free(void *, void *); int iidesc_count_type(void *, void *); void iidesc_stats(hash_t *); int iidesc_dump(iidesc_t *); /* input.c */ typedef enum source_types { SOURCE_NONE = 0, SOURCE_UNKNOWN = 1, SOURCE_C = 2, SOURCE_S = 4 } source_types_t; source_types_t built_source_types(Elf *, const char *); int count_files(char **, int); int read_ctf(char **, int, char *, int (*)(tdata_t *, char *, void *), void *, int); int read_ctf_save_cb(tdata_t *, char *, void *); symit_data_t *symit_new(Elf *, const char *); void symit_reset(symit_data_t *); char *symit_curfile(symit_data_t *); GElf_Sym *symit_next(symit_data_t *, int); char *symit_name(symit_data_t *); void symit_free(symit_data_t *); /* merge.c */ void merge_into_master(tdata_t *, tdata_t *, tdata_t *, int); /* output.c */ #define CTF_FUZZY_MATCH 0x1 /* match local symbols to global CTF */ #define CTF_USE_DYNSYM 0x2 /* use .dynsym not .symtab */ #define CTF_COMPRESS 0x4 /* compress CTF output */ #define CTF_KEEP_STABS 0x8 /* keep .stabs sections */ +#define CTF_SWAP_BYTES 0x10 /* target byte order is different from host */ void write_ctf(tdata_t *, const char *, const char *, int); /* parse.c */ void parse_init(tdata_t *); void parse_finish(tdata_t *); int parse_stab(stab_t *, char *, iidesc_t **); tdesc_t *lookup(int); tdesc_t *lookupname(const char *); void check_hash(void); void resolve_typed_bitfields(void); /* stabs.c */ int stabs_read(tdata_t *, Elf *, char *); /* dwarf.c */ int dw_read(tdata_t *, Elf *, char *); const char *dw_tag2str(uint_t); /* tdata.c */ tdata_t *tdata_new(void); void tdata_free(tdata_t *); void tdata_build_hashes(tdata_t *td); const char *tdesc_name(tdesc_t *); int tdesc_idhash(int, void *); int tdesc_idcmp(void *, void *); int tdesc_namehash(int, void *); int tdesc_namecmp(void *, void *); int tdesc_layouthash(int, void *); int tdesc_layoutcmp(void *, void *); void tdesc_free(tdesc_t *); void tdata_label_add(tdata_t *, const char *, int); labelent_t *tdata_label_top(tdata_t *); int tdata_label_find(tdata_t *, char *); void tdata_label_free(tdata_t *); void tdata_merge(tdata_t *, tdata_t *); void tdata_label_newmax(tdata_t *, int); /* util.c */ int streq(const char *, const char *); int findelfsecidx(Elf *, const char *, const char *); size_t elf_ptrsz(Elf *); char *mktmpname(const char *, const char *); void terminate(const char *, ...); void aborterr(const char *, ...); void set_terminate_cleanup(void (*)(void)); void elfterminate(const char *, const char *, ...); void warning(const char *, ...); void vadebug(int, const char *, va_list); void debug(int, const char *, ...); void watch_dump(int); void watch_set(void *, int); #ifdef __cplusplus } #endif #endif /* _CTFTOOLS_H */ Index: head/cddl/contrib/opensolaris/tools/ctf/cvt/output.c =================================================================== --- head/cddl/contrib/opensolaris/tools/ctf/cvt/output.c (revision 233406) +++ head/cddl/contrib/opensolaris/tools/ctf/cvt/output.c (revision 233407) @@ -1,757 +1,775 @@ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Routines for preparing tdata trees for conversion into CTF data, and * for placing the resulting data into an output file. */ #include #include #include #include #include #include #include #include #include #include "ctftools.h" #include "list.h" #include "memory.h" #include "traverse.h" #include "symbol.h" typedef struct iidesc_match { int iim_fuzzy; iidesc_t *iim_ret; char *iim_name; char *iim_file; uchar_t iim_bind; } iidesc_match_t; static int burst_iitypes(void *data, void *arg) { iidesc_t *ii = data; iiburst_t *iiburst = arg; switch (ii->ii_type) { case II_GFUN: case II_SFUN: case II_GVAR: case II_SVAR: if (!(ii->ii_flags & IIDESC_F_USED)) return (0); break; default: break; } ii->ii_dtype->t_flags |= TDESC_F_ISROOT; (void) iitraverse_td(ii, iiburst->iib_tdtd); return (1); } /*ARGSUSED1*/ static int save_type_by_id(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private) { iiburst_t *iiburst = private; /* * Doing this on every node is horribly inefficient, but given that * we may be suppressing some types, we can't trust nextid in the * tdata_t. */ if (tdp->t_id > iiburst->iib_maxtypeid) iiburst->iib_maxtypeid = tdp->t_id; slist_add(&iiburst->iib_types, tdp, tdesc_idcmp); return (1); } static tdtrav_cb_f burst_types_cbs[] = { NULL, save_type_by_id, /* intrinsic */ save_type_by_id, /* pointer */ save_type_by_id, /* array */ save_type_by_id, /* function */ save_type_by_id, /* struct */ save_type_by_id, /* union */ save_type_by_id, /* enum */ save_type_by_id, /* forward */ save_type_by_id, /* typedef */ tdtrav_assert, /* typedef_unres */ save_type_by_id, /* volatile */ save_type_by_id, /* const */ save_type_by_id /* restrict */ }; static iiburst_t * iiburst_new(tdata_t *td, int max) { iiburst_t *iiburst = xcalloc(sizeof (iiburst_t)); iiburst->iib_td = td; iiburst->iib_funcs = xcalloc(sizeof (iidesc_t *) * max); iiburst->iib_nfuncs = 0; iiburst->iib_objts = xcalloc(sizeof (iidesc_t *) * max); iiburst->iib_nobjts = 0; return (iiburst); } static void iiburst_types(iiburst_t *iiburst) { tdtrav_data_t tdtd; tdtrav_init(&tdtd, &iiburst->iib_td->td_curvgen, NULL, burst_types_cbs, NULL, (void *)iiburst); iiburst->iib_tdtd = &tdtd; (void) hash_iter(iiburst->iib_td->td_iihash, burst_iitypes, iiburst); } static void iiburst_free(iiburst_t *iiburst) { free(iiburst->iib_funcs); free(iiburst->iib_objts); list_free(iiburst->iib_types, NULL, NULL); free(iiburst); } /* * See if this iidesc matches the ELF symbol data we pass in. * * A fuzzy match is where we have a local symbol matching the name of a * global type description. This is common when a mapfile is used for a * DSO, but we don't accept it by default. * * A weak fuzzy match is when a weak symbol was resolved and matched to * a global type description. */ static int matching_iidesc(void *arg1, void *arg2) { iidesc_t *iidesc = arg1; iidesc_match_t *match = arg2; if (streq(iidesc->ii_name, match->iim_name) == 0) return (0); switch (iidesc->ii_type) { case II_GFUN: case II_GVAR: if (match->iim_bind == STB_GLOBAL) { match->iim_ret = iidesc; return (-1); } else if (match->iim_fuzzy && match->iim_ret == NULL) { match->iim_ret = iidesc; /* continue to look for strong match */ return (0); } break; case II_SFUN: case II_SVAR: if (match->iim_bind == STB_LOCAL && match->iim_file != NULL && streq(iidesc->ii_owner, match->iim_file)) { match->iim_ret = iidesc; return (-1); } break; default: break; } return (0); } static iidesc_t * find_iidesc(tdata_t *td, iidesc_match_t *match) { match->iim_ret = NULL; iter_iidescs_by_name(td, match->iim_name, matching_iidesc, match); return (match->iim_ret); } /* * If we have a weak symbol, attempt to find the strong symbol it will * resolve to. Note: the code where this actually happens is in * sym_process() in cmd/sgs/libld/common/syms.c * * Finding the matching symbol is unfortunately not trivial. For a * symbol to be a candidate, it must: * * - have the same type (function, object) * - have the same value (address) * - have the same size * - not be another weak symbol * - belong to the same section (checked via section index) * * If such a candidate is global, then we assume we've found it. The * linker generates the symbol table such that the curfile might be * incorrect; this is OK for global symbols, since find_iidesc() doesn't * need to check for the source file for the symbol. * * We might have found a strong local symbol, where the curfile is * accurate and matches that of the weak symbol. We assume this is a * reasonable match. * * If we've got a local symbol with a non-matching curfile, there are * two possibilities. Either this is a completely different symbol, or * it's a once-global symbol that was scoped to local via a mapfile. In * the latter case, curfile is likely inaccurate since the linker does * not preserve the needed curfile in the order of the symbol table (see * the comments about locally scoped symbols in libld's update_osym()). * As we can't tell this case from the former one, we use this symbol * iff no other matching symbol is found. * * What we really need here is a SUNW section containing weak<->strong * mappings that we can consume. */ static int check_for_weak(GElf_Sym *weak, char const *weakfile, Elf_Data *data, int nent, Elf_Data *strdata, GElf_Sym *retsym, char **curfilep) { char *curfile = NULL; char *tmpfile1 = NULL; GElf_Sym tmpsym; int candidate = 0; int i; tmpsym.st_info = 0; tmpsym.st_name = 0; if (GELF_ST_BIND(weak->st_info) != STB_WEAK) return (0); for (i = 0; i < nent; i++) { GElf_Sym sym; uchar_t type; if (gelf_getsym(data, i, &sym) == NULL) continue; type = GELF_ST_TYPE(sym.st_info); if (type == STT_FILE) curfile = (char *)strdata->d_buf + sym.st_name; if (GELF_ST_TYPE(weak->st_info) != type || weak->st_value != sym.st_value) continue; if (weak->st_size != sym.st_size) continue; if (GELF_ST_BIND(sym.st_info) == STB_WEAK) continue; if (sym.st_shndx != weak->st_shndx) continue; if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && (curfile == NULL || weakfile == NULL || strcmp(curfile, weakfile) != 0)) { candidate = 1; tmpfile1 = curfile; tmpsym = sym; continue; } *curfilep = curfile; *retsym = sym; return (1); } if (candidate) { *curfilep = tmpfile1; *retsym = tmpsym; return (1); } return (0); } /* * When we've found the underlying symbol's type description * for a weak symbol, we need to copy it and rename it to match * the weak symbol. We also need to add it to the td so it's * handled along with the others later. */ static iidesc_t * copy_from_strong(tdata_t *td, GElf_Sym *sym, iidesc_t *strongdesc, const char *weakname, const char *weakfile) { iidesc_t *new = iidesc_dup_rename(strongdesc, weakname, weakfile); uchar_t type = GELF_ST_TYPE(sym->st_info); switch (type) { case STT_OBJECT: new->ii_type = II_GVAR; break; case STT_FUNC: new->ii_type = II_GFUN; break; } hash_add(td->td_iihash, new); return (new); } /* * Process the symbol table of the output file, associating each symbol * with a type description if possible, and sorting them into functions * and data, maintaining symbol table order. */ static iiburst_t * sort_iidescs(Elf *elf, const char *file, tdata_t *td, int fuzzymatch, int dynsym) { iiburst_t *iiburst; Elf_Scn *scn; GElf_Shdr shdr; Elf_Data *data, *strdata; int i, stidx; int nent; iidesc_match_t match; match.iim_fuzzy = fuzzymatch; match.iim_file = NULL; if ((stidx = findelfsecidx(elf, file, dynsym ? ".dynsym" : ".symtab")) < 0) terminate("%s: Can't open symbol table\n", file); scn = elf_getscn(elf, stidx); data = elf_getdata(scn, NULL); gelf_getshdr(scn, &shdr); nent = shdr.sh_size / shdr.sh_entsize; scn = elf_getscn(elf, shdr.sh_link); strdata = elf_getdata(scn, NULL); iiburst = iiburst_new(td, nent); for (i = 0; i < nent; i++) { GElf_Sym sym; iidesc_t **tolist; GElf_Sym ssym; iidesc_match_t smatch; int *curr; iidesc_t *iidesc; if (gelf_getsym(data, i, &sym) == NULL) elfterminate(file, "Couldn't read symbol %d", i); match.iim_name = (char *)strdata->d_buf + sym.st_name; match.iim_bind = GELF_ST_BIND(sym.st_info); switch (GELF_ST_TYPE(sym.st_info)) { case STT_FILE: match.iim_file = match.iim_name; continue; case STT_OBJECT: tolist = iiburst->iib_objts; curr = &iiburst->iib_nobjts; break; case STT_FUNC: tolist = iiburst->iib_funcs; curr = &iiburst->iib_nfuncs; break; default: continue; } if (ignore_symbol(&sym, match.iim_name)) continue; iidesc = find_iidesc(td, &match); if (iidesc != NULL) { tolist[*curr] = iidesc; iidesc->ii_flags |= IIDESC_F_USED; (*curr)++; continue; } if (!check_for_weak(&sym, match.iim_file, data, nent, strdata, &ssym, &smatch.iim_file)) { (*curr)++; continue; } smatch.iim_fuzzy = fuzzymatch; smatch.iim_name = (char *)strdata->d_buf + ssym.st_name; smatch.iim_bind = GELF_ST_BIND(ssym.st_info); debug(3, "Weak symbol %s resolved to %s\n", match.iim_name, smatch.iim_name); iidesc = find_iidesc(td, &smatch); if (iidesc != NULL) { tolist[*curr] = copy_from_strong(td, &sym, iidesc, match.iim_name, match.iim_file); tolist[*curr]->ii_flags |= IIDESC_F_USED; } (*curr)++; } /* * Stabs are generated for every function declared in a given C source * file. When converting an object file, we may encounter a stab that * has no symbol table entry because the optimizer has decided to omit * that item (for example, an unreferenced static function). We may * see iidescs that do not have an associated symtab entry, and so * we do not write records for those functions into the CTF data. * All others get marked as a root by this function. */ iiburst_types(iiburst); /* * By not adding some of the functions and/or objects, we may have * caused some types that were referenced solely by those * functions/objects to be suppressed. This could cause a label, * generated prior to the evisceration, to be incorrect. Find the * highest type index, and change the label indicies to be no higher * than this value. */ tdata_label_newmax(td, iiburst->iib_maxtypeid); return (iiburst); } static void write_file(Elf *src, const char *srcname, Elf *dst, const char *dstname, caddr_t ctfdata, size_t ctfsize, int flags) { GElf_Ehdr sehdr, dehdr; Elf_Scn *sscn, *dscn; Elf_Data *sdata, *ddata; GElf_Shdr shdr; GElf_Word symtab_type; int symtab_idx = -1; off_t new_offset = 0; off_t ctfnameoff = 0; int dynsym = (flags & CTF_USE_DYNSYM); int keep_stabs = (flags & CTF_KEEP_STABS); int *secxlate; int srcidx, dstidx; int curnmoff = 0; int changing = 0; int pad; int i; if (gelf_newehdr(dst, gelf_getclass(src)) == NULL) elfterminate(dstname, "Cannot copy ehdr to temp file"); gelf_getehdr(src, &sehdr); memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr)); gelf_update_ehdr(dst, &dehdr); symtab_type = dynsym ? SHT_DYNSYM : SHT_SYMTAB; /* * Neither the existing stab sections nor the SUNW_ctf sections (new or * existing) are SHF_ALLOC'd, so they won't be in areas referenced by * program headers. As such, we can just blindly copy the program * headers from the existing file to the new file. */ if (sehdr.e_phnum != 0) { (void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT); if (gelf_newphdr(dst, sehdr.e_phnum) == NULL) elfterminate(dstname, "Cannot make phdrs in temp file"); for (i = 0; i < sehdr.e_phnum; i++) { GElf_Phdr phdr; gelf_getphdr(src, i, &phdr); gelf_update_phdr(dst, i, &phdr); } } secxlate = xmalloc(sizeof (int) * sehdr.e_shnum); for (srcidx = dstidx = 0; srcidx < sehdr.e_shnum; srcidx++) { Elf_Scn *scn = elf_getscn(src, srcidx); GElf_Shdr shdr1; char *sname; gelf_getshdr(scn, &shdr1); sname = elf_strptr(src, sehdr.e_shstrndx, shdr1.sh_name); if (sname == NULL) { elfterminate(srcname, "Can't find string at %u", shdr1.sh_name); } if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) { secxlate[srcidx] = -1; } else if (!keep_stabs && (strncmp(sname, ".stab", 5) == 0 || strncmp(sname, ".debug", 6) == 0 || strncmp(sname, ".rel.debug", 10) == 0 || strncmp(sname, ".rela.debug", 11) == 0)) { secxlate[srcidx] = -1; } else if (dynsym && shdr1.sh_type == SHT_SYMTAB) { /* * If we're building CTF against the dynsym, * we'll rip out the symtab so debuggers aren't * confused. */ secxlate[srcidx] = -1; } else { secxlate[srcidx] = dstidx++; curnmoff += strlen(sname) + 1; } new_offset = (off_t)dehdr.e_phoff; } for (srcidx = 1; srcidx < sehdr.e_shnum; srcidx++) { char *sname; sscn = elf_getscn(src, srcidx); gelf_getshdr(sscn, &shdr); if (secxlate[srcidx] == -1) { changing = 1; continue; } dscn = elf_newscn(dst); /* * If this file has program headers, we need to explicitly lay * out sections. If none of the sections prior to this one have * been removed, then we can just use the existing location. If * one or more sections have been changed, then we need to * adjust this one to avoid holes. */ if (changing && sehdr.e_phnum != 0) { pad = new_offset % shdr.sh_addralign; if (pad) new_offset += shdr.sh_addralign - pad; shdr.sh_offset = new_offset; } shdr.sh_link = secxlate[shdr.sh_link]; if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA) shdr.sh_info = secxlate[shdr.sh_info]; sname = elf_strptr(src, sehdr.e_shstrndx, shdr.sh_name); if (sname == NULL) { elfterminate(srcname, "Can't find string at %u", shdr.sh_name); } #if !defined(sun) if (gelf_update_shdr(dscn, &shdr) == 0) elfterminate(dstname, "Cannot update sect %s", sname); #endif if ((sdata = elf_getdata(sscn, NULL)) == NULL) elfterminate(srcname, "Cannot get sect %s data", sname); if ((ddata = elf_newdata(dscn)) == NULL) elfterminate(dstname, "Can't make sect %s data", sname); #if defined(sun) bcopy(sdata, ddata, sizeof (Elf_Data)); #else /* * FreeBSD's Elf_Data has private fields which the * elf_* routines manage. Simply copying the * entire structure corrupts the data. So we need * to copy the public fields explictly. */ ddata->d_align = sdata->d_align; ddata->d_off = sdata->d_off; ddata->d_size = sdata->d_size; ddata->d_type = sdata->d_type; ddata->d_version = sdata->d_version; #endif if (srcidx == sehdr.e_shstrndx) { char seclen = strlen(CTF_ELF_SCN_NAME); ddata->d_buf = xmalloc(ddata->d_size + shdr.sh_size + seclen + 1); bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); strcpy((caddr_t)ddata->d_buf + shdr.sh_size, CTF_ELF_SCN_NAME); ctfnameoff = (off_t)shdr.sh_size; shdr.sh_size += seclen + 1; ddata->d_size += seclen + 1; if (sehdr.e_phnum != 0) changing = 1; } if (shdr.sh_type == symtab_type && shdr.sh_entsize != 0) { int nsym = shdr.sh_size / shdr.sh_entsize; symtab_idx = secxlate[srcidx]; ddata->d_buf = xmalloc(shdr.sh_size); bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); for (i = 0; i < nsym; i++) { GElf_Sym sym; short newscn; if (gelf_getsym(ddata, i, &sym) == NULL) printf("Could not get symbol %d\n",i); if (sym.st_shndx >= SHN_LORESERVE) continue; if ((newscn = secxlate[sym.st_shndx]) != sym.st_shndx) { sym.st_shndx = (newscn == -1 ? 1 : newscn); gelf_update_sym(ddata, i, &sym); } } } #if !defined(sun) if (ddata->d_buf == NULL && sdata->d_buf != NULL) { ddata->d_buf = xmalloc(shdr.sh_size); bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); } #endif if (gelf_update_shdr(dscn, &shdr) == 0) elfterminate(dstname, "Cannot update sect %s", sname); new_offset = (off_t)shdr.sh_offset; if (shdr.sh_type != SHT_NOBITS) new_offset += shdr.sh_size; } if (symtab_idx == -1) { terminate("%s: Cannot find %s section\n", srcname, dynsym ? "SHT_DYNSYM" : "SHT_SYMTAB"); } /* Add the ctf section */ dscn = elf_newscn(dst); gelf_getshdr(dscn, &shdr); shdr.sh_name = ctfnameoff; shdr.sh_type = SHT_PROGBITS; shdr.sh_size = ctfsize; shdr.sh_link = symtab_idx; shdr.sh_addralign = 4; if (changing && sehdr.e_phnum != 0) { pad = new_offset % shdr.sh_addralign; if (pad) new_offset += shdr.sh_addralign - pad; shdr.sh_offset = new_offset; new_offset += shdr.sh_size; } ddata = elf_newdata(dscn); ddata->d_buf = ctfdata; ddata->d_size = ctfsize; ddata->d_align = shdr.sh_addralign; ddata->d_off = 0; gelf_update_shdr(dscn, &shdr); /* update the section header location */ if (sehdr.e_phnum != 0) { size_t align = gelf_fsize(dst, ELF_T_ADDR, 1, EV_CURRENT); size_t r = new_offset % align; if (r) new_offset += align - r; dehdr.e_shoff = new_offset; } /* commit to disk */ dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx]; gelf_update_ehdr(dst, &dehdr); if (elf_update(dst, ELF_C_WRITE) < 0) elfterminate(dstname, "Cannot finalize temp file"); free(secxlate); } static caddr_t make_ctf_data(tdata_t *td, Elf *elf, const char *file, size_t *lenp, int flags) { iiburst_t *iiburst; caddr_t data; iiburst = sort_iidescs(elf, file, td, flags & CTF_FUZZY_MATCH, flags & CTF_USE_DYNSYM); - data = ctf_gen(iiburst, lenp, flags & CTF_COMPRESS); + data = ctf_gen(iiburst, lenp, flags & (CTF_COMPRESS | CTF_SWAP_BYTES)); iiburst_free(iiburst); return (data); } void write_ctf(tdata_t *td, const char *curname, const char *newname, int flags) { struct stat st; Elf *elf = NULL; Elf *telf = NULL; + GElf_Ehdr ehdr; caddr_t data; size_t len; int fd = -1; int tfd = -1; + int byteorder; (void) elf_version(EV_CURRENT); if ((fd = open(curname, O_RDONLY)) < 0 || fstat(fd, &st) < 0) terminate("%s: Cannot open for re-reading", curname); if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) elfterminate(curname, "Cannot re-read"); if ((tfd = open(newname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0) terminate("Cannot open temp file %s for writing", newname); if ((telf = elf_begin(tfd, ELF_C_WRITE, NULL)) == NULL) elfterminate(curname, "Cannot write"); + + if (gelf_getehdr(elf, &ehdr)) { +#if BYTE_ORDER == _BIG_ENDIAN + byteorder = ELFDATA2MSB; +#else + byteorder = ELFDATA2LSB; +#endif + /* + * If target and host has the same byte order + * clear byte swapping request + */ + if (ehdr.e_ident[EI_DATA] == byteorder) + flags &= ~CTF_SWAP_BYTES; + } + else + elfterminate(curname, "Failed to get EHDR"); data = make_ctf_data(td, elf, curname, &len, flags); write_file(elf, curname, telf, newname, data, len, flags); free(data); elf_end(telf); elf_end(elf); (void) close(fd); (void) close(tfd); }