aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Birrell <jb@FreeBSD.org>2008-04-25 09:07:28 +0000
committerJohn Birrell <jb@FreeBSD.org>2008-04-25 09:07:28 +0000
commit38c68f8a196ef330599c13b5caa9d0bc5c5bff0c (patch)
tree51ee53bee14223a7bbc5f1fdb6a3ecca0f7dcfd5
parentc53d354ffe3b3f603cf36c67c489eeeb881403b2 (diff)
downloadsrc-38c68f8a196ef330599c13b5caa9d0bc5c5bff0c.tar.gz
src-38c68f8a196ef330599c13b5caa9d0bc5c5bff0c.zip
Vendor import of the CTF (Compact C Type Format) code used by DTrace
from OpenSolaris.
Notes
Notes: svn path=/vendor-cddl/opensolaris/dist/cddl/contrib/opensolaris/; revision=178481
-rw-r--r--tools/ctf/common/ctf_headers.h72
-rw-r--r--tools/ctf/common/list.c228
-rw-r--r--tools/ctf/common/list.h58
-rw-r--r--tools/ctf/common/memory.c103
-rw-r--r--tools/ctf/common/memory.h52
-rw-r--r--tools/ctf/common/symbol.c62
-rw-r--r--tools/ctf/common/symbol.h44
-rw-r--r--tools/ctf/common/utils.c104
-rw-r--r--tools/ctf/common/utils.h53
-rw-r--r--tools/ctf/cvt/alist.c215
-rw-r--r--tools/ctf/cvt/alist.h57
-rw-r--r--tools/ctf/cvt/barrier.c92
-rw-r--r--tools/ctf/cvt/barrier.h62
-rw-r--r--tools/ctf/cvt/compare.c92
-rw-r--r--tools/ctf/cvt/ctf.c1263
-rw-r--r--tools/ctf/cvt/ctfconvert.c263
-rw-r--r--tools/ctf/cvt/ctfmerge.c1004
-rw-r--r--tools/ctf/cvt/ctfmerge.h89
-rw-r--r--tools/ctf/cvt/ctftools.h453
-rw-r--r--tools/ctf/cvt/dwarf.c1848
-rw-r--r--tools/ctf/cvt/fifo.c153
-rw-r--r--tools/ctf/cvt/fifo.h54
-rw-r--r--tools/ctf/cvt/fixup_tdescs.c279
-rw-r--r--tools/ctf/cvt/hash.c291
-rw-r--r--tools/ctf/cvt/hash.h59
-rw-r--r--tools/ctf/cvt/iidesc.c197
-rw-r--r--tools/ctf/cvt/input.c419
-rw-r--r--tools/ctf/cvt/merge.c1143
-rw-r--r--tools/ctf/cvt/output.c757
-rw-r--r--tools/ctf/cvt/st_parse.c1198
-rw-r--r--tools/ctf/cvt/stabs.c381
-rw-r--r--tools/ctf/cvt/stack.c112
-rw-r--r--tools/ctf/cvt/stack.h53
-rw-r--r--tools/ctf/cvt/strtab.c258
-rw-r--r--tools/ctf/cvt/strtab.h69
-rw-r--r--tools/ctf/cvt/tdata.c488
-rw-r--r--tools/ctf/cvt/traverse.c226
-rw-r--r--tools/ctf/cvt/traverse.h71
-rw-r--r--tools/ctf/cvt/util.c283
-rw-r--r--tools/ctf/dump/dump.c1028
40 files changed, 13733 insertions, 0 deletions
diff --git a/tools/ctf/common/ctf_headers.h b/tools/ctf/common/ctf_headers.h
new file mode 100644
index 000000000000..b00b8fd9a650
--- /dev/null
+++ b/tools/ctf/common/ctf_headers.h
@@ -0,0 +1,72 @@
+/*
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CTF_HEADERS_H
+#define _CTF_HEADERS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Because the ON tools are executed on the system where they are built,
+ * the tools need to include the headers installed on the build system,
+ * rather than those in the ON source tree. However, some of the headers
+ * required by the tools are part of the ON source tree, but not delivered
+ * as part of Solaris. These include the following:
+ *
+ * $(SRC)/lib/libctf/common/libctf.h
+ * $(SRC)/uts/common/sys/ctf_api.h
+ * $(SRC)/uts/common/sys/ctf.h
+ *
+ * These headers get installed in the proto area in the build environment
+ * under $(ROOT)/usr/include and $(ROOT)/usr/include/sys. Though these
+ * headers are not part of the release, in releases including and prior to
+ * Solaris 9, they did get installed on the build system via bfu. Therefore,
+ * we can not simply force the order of inclusion with -I/usr/include first
+ * in Makefile.ctf because we might actually get downlevel versions of the
+ * ctf headers. Depending on the order of the -I includes, we can also have
+ * a problem with mismatched headers when building the ctf tools with some
+ * headers getting pulled in from /usr/include and others from
+ * $(SRC)/uts/common/sys.
+ *
+ * To address the problem, we have done two things:
+ * 1) Created this header with a specific order of inclusion for the
+ * ctf headers. Because the <libctf.h> header includes <sys/ctf_api.h>
+ * which in turn includes <sys/ctf.h> we need to include these in
+ * reverse order to guarantee that we get the correct versions of
+ * the headers.
+ * 2) In $(SRC)/tools/ctf/Makefile.ctf, we order the -I includes such
+ * that we first search the directories where the ctf headers
+ * live, followed by /usr/include, followed by $(SRC)/uts/common.
+ * This last -I include is needed in order to prevent a build failure
+ * when <sys/ctf_api.h> is included via a nested #include rather than
+ * an explicit path #include.
+ */
+
+#include <uts/common/sys/ctf.h>
+#include <uts/common/sys/ctf_api.h>
+#include <lib/libctf/common/libctf.h>
+
+#endif /* _CTF_HEADERS_H */
diff --git a/tools/ctf/common/list.c b/tools/ctf/common/list.c
new file mode 100644
index 000000000000..4958f270a994
--- /dev/null
+++ b/tools/ctf/common/list.c
@@ -0,0 +1,228 @@
+/*
+ * 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 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating linked lists
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "list.h"
+#include "memory.h"
+
+struct list {
+ void *l_data;
+ struct list *l_next;
+};
+
+/* Add an element to a list */
+void
+list_add(list_t **list, void *data)
+{
+ list_t *le;
+
+ le = xmalloc(sizeof (list_t));
+ le->l_data = data;
+ le->l_next = *list;
+ *list = le;
+}
+
+/* Add an element to a sorted list */
+void
+slist_add(list_t **list, void *data, int (*cmp)(void *, void *))
+{
+ list_t **nextp;
+
+ for (nextp = list; *nextp; nextp = &((*nextp)->l_next)) {
+ if (cmp((*nextp)->l_data, data) > 0)
+ break;
+ }
+
+ list_add(nextp, data);
+}
+
+/*ARGSUSED2*/
+static int
+list_defcmp(void *d1, void *d2, void *private __unused)
+{
+ return (d1 != d2);
+}
+
+void *
+list_remove(list_t **list, void *data, int (*cmp)(void *, void *, void *),
+ void *private)
+{
+ list_t *le, **le2;
+ void *led;
+
+ if (!cmp)
+ cmp = list_defcmp;
+
+ for (le = *list, le2 = list; le; le2 = &le->l_next, le = le->l_next) {
+ if (cmp(le->l_data, data, private) == 0) {
+ *le2 = le->l_next;
+ led = le->l_data;
+ free(le);
+ return (led);
+ }
+ }
+
+ return (NULL);
+}
+
+void
+list_free(list_t *list, void (*datafree)(void *, void *), void *private)
+{
+ list_t *le;
+
+ while (list) {
+ le = list;
+ list = list->l_next;
+ if (le->l_data && datafree)
+ datafree(le->l_data, private);
+ free(le);
+ }
+}
+
+/*
+ * This iterator is specifically designed to tolerate the deletion of the
+ * node being iterated over.
+ */
+int
+list_iter(list_t *list, int (*func)(void *, void *), void *private)
+{
+ list_t *lnext;
+ int cumrc = 0;
+ int cbrc;
+
+ while (list) {
+ lnext = list->l_next;
+ if ((cbrc = func(list->l_data, private)) < 0)
+ return (cbrc);
+ cumrc += cbrc;
+ list = lnext;
+ }
+
+ return (cumrc);
+}
+
+/*ARGSUSED*/
+static int
+list_count_cb(void *data __unused, void *private __unused)
+{
+ return (1);
+}
+
+int
+list_count(list_t *list)
+{
+ return (list_iter(list, list_count_cb, NULL));
+}
+
+int
+list_empty(list_t *list)
+{
+ return (list == NULL);
+}
+
+void *
+list_find(list_t *list, void *tmpl, int (*cmp)(void *, void *))
+{
+ for (; list; list = list->l_next) {
+ if (cmp(list->l_data, tmpl) == 0)
+ return (list->l_data);
+ }
+
+ return (NULL);
+}
+
+void *
+list_first(list_t *list)
+{
+ return (list ? list->l_data : NULL);
+}
+
+void
+list_concat(list_t **list1, list_t *list2)
+{
+ list_t *l, *last;
+
+ for (l = *list1, last = NULL; l; last = l, l = l->l_next)
+ continue;
+
+ if (last == NULL)
+ *list1 = list2;
+ else
+ last->l_next = list2;
+}
+
+/*
+ * Merges two sorted lists. Equal nodes (as determined by cmp) are retained.
+ */
+void
+slist_merge(list_t **list1p, list_t *list2, int (*cmp)(void *, void *))
+{
+ list_t *list1, *next2;
+ list_t *last1 = NULL;
+
+ if (*list1p == NULL) {
+ *list1p = list2;
+ return;
+ }
+
+ list1 = *list1p;
+ while (list2 != NULL) {
+ if (cmp(list1->l_data, list2->l_data) > 0) {
+ next2 = list2->l_next;
+
+ if (last1 == NULL) {
+ /* Insert at beginning */
+ *list1p = last1 = list2;
+ list2->l_next = list1;
+ } else {
+ list2->l_next = list1;
+ last1->l_next = list2;
+ last1 = list2;
+ }
+
+ list2 = next2;
+ } else {
+
+ last1 = list1;
+ list1 = list1->l_next;
+
+ if (list1 == NULL) {
+ /* Add the rest to the end of list1 */
+ last1->l_next = list2;
+ list2 = NULL;
+ }
+ }
+ }
+}
diff --git a/tools/ctf/common/list.h b/tools/ctf/common/list.h
new file mode 100644
index 000000000000..2e41271d0ff0
--- /dev/null
+++ b/tools/ctf/common/list.h
@@ -0,0 +1,58 @@
+/*
+ * 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 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating linked lists
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct list list_t;
+
+void list_add(list_t **, void *);
+void slist_add(list_t **, void *, int (*)(void *, void *));
+void *list_remove(list_t **, void *, int (*)(void *, void *, void *), void *);
+void list_free(list_t *, void (*)(void *, void *), void *);
+void *list_find(list_t *, void *, int (*)(void *, void *));
+void *list_first(list_t *);
+int list_iter(list_t *, int (*)(void *, void *), void *);
+int list_count(list_t *);
+int list_empty(list_t *);
+void list_concat(list_t **, list_t *);
+void slist_merge(list_t **, list_t *, int (*)(void *, void *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIST_H */
diff --git a/tools/ctf/common/memory.c b/tools/ctf/common/memory.c
new file mode 100644
index 000000000000..e16044a8b672
--- /dev/null
+++ b/tools/ctf/common/memory.c
@@ -0,0 +1,103 @@
+/*
+ * 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 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for memory management
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "memory.h"
+
+static void
+memory_bailout(void)
+{
+ (void) fprintf(stderr, "Out of memory\n");
+ exit(1);
+}
+
+void *
+xmalloc(size_t size)
+{
+ void *mem;
+
+ if ((mem = malloc(size)) == NULL)
+ memory_bailout();
+
+ return (mem);
+}
+
+void *
+xcalloc(size_t size)
+{
+ void *mem;
+
+ mem = xmalloc(size);
+ bzero(mem, size);
+
+ return (mem);
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *newstr;
+
+ if ((newstr = strdup(str)) == NULL)
+ memory_bailout();
+
+ return (newstr);
+}
+
+char *
+xstrndup(char *str, size_t len)
+{
+ char *newstr;
+
+ if ((newstr = malloc(len + 1)) == NULL)
+ memory_bailout();
+
+ (void) strncpy(newstr, str, len);
+ newstr[len] = '\0';
+
+ return (newstr);
+}
+
+void *
+xrealloc(void *ptr, size_t size)
+{
+ void *mem;
+
+ if ((mem = realloc(ptr, size)) == NULL)
+ memory_bailout();
+
+ return (mem);
+}
diff --git a/tools/ctf/common/memory.h b/tools/ctf/common/memory.h
new file mode 100644
index 000000000000..88ca31bec65a
--- /dev/null
+++ b/tools/ctf/common/memory.h
@@ -0,0 +1,52 @@
+/*
+ * 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 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for memory management
+ */
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *xmalloc(size_t);
+void *xcalloc(size_t);
+char *xstrdup(const char *);
+char *xstrndup(char *, size_t);
+void *xrealloc(void *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MEMORY_H */
diff --git a/tools/ctf/common/symbol.c b/tools/ctf/common/symbol.c
new file mode 100644
index 000000000000..29796acb2c61
--- /dev/null
+++ b/tools/ctf/common/symbol.c
@@ -0,0 +1,62 @@
+/*
+ * 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 <sys/types.h>
+#include <string.h>
+
+#include "symbol.h"
+
+int
+ignore_symbol(GElf_Sym *sym, const char *name)
+{
+ uchar_t type = GELF_ST_TYPE(sym->st_info);
+
+ /*
+ * As an optimization, we do not output function or data object
+ * records for undefined or anonymous symbols.
+ */
+ if (sym->st_shndx == SHN_UNDEF || sym->st_name == 0)
+ return (1);
+
+ /*
+ * _START_ and _END_ are added to the symbol table by the
+ * linker, and will never have associated type information.
+ */
+ if (strcmp(name, "_START_") == 0 || strcmp(name, "_END_") == 0)
+ return (1);
+
+ /*
+ * Do not output records for absolute-valued object symbols
+ * that have value zero. The compiler insists on generating
+ * things like this for __fsr_init_value settings, etc.
+ */
+ if (type == STT_OBJECT && sym->st_shndx == SHN_ABS &&
+ sym->st_value == 0)
+ return (1);
+ return (0);
+}
diff --git a/tools/ctf/common/symbol.h b/tools/ctf/common/symbol.h
new file mode 100644
index 000000000000..92b5e892affb
--- /dev/null
+++ b/tools/ctf/common/symbol.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef _SYMBOL_H
+#define _SYMBOL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <gelf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ignore_symbol(GElf_Sym *sym, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYMBOL_H */
diff --git a/tools/ctf/common/utils.c b/tools/ctf/common/utils.c
new file mode 100644
index 000000000000..b9db1a870165
--- /dev/null
+++ b/tools/ctf/common/utils.c
@@ -0,0 +1,104 @@
+/*
+ * 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 (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "utils.h"
+
+/*LINTLIBRARY*/
+
+static const char *pname;
+
+#pragma init(getpname)
+const char *
+getpname(void)
+{
+ const char *p, *q;
+
+ if (pname != NULL)
+ return (pname);
+
+ if ((p = getexecname()) != NULL)
+ q = strrchr(p, '/');
+ else
+ q = NULL;
+
+ if (q == NULL)
+ pname = p;
+ else
+ pname = q + 1;
+
+ return (pname);
+}
+
+void
+vwarn(const char *format, va_list alist)
+{
+ int err = errno;
+
+ if (pname != NULL)
+ (void) fprintf(stderr, "%s: ", pname);
+
+ (void) vfprintf(stderr, format, alist);
+
+ if (strchr(format, '\n') == NULL)
+ (void) fprintf(stderr, ": %s\n", strerror(err));
+}
+
+/*PRINTFLIKE1*/
+void
+warn(const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ vwarn(format, alist);
+ va_end(alist);
+}
+
+void
+vdie(const char *format, va_list alist)
+{
+ vwarn(format, alist);
+ exit(E_ERROR);
+}
+
+/*PRINTFLIKE1*/
+void
+die(const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ vdie(format, alist);
+ va_end(alist);
+}
diff --git a/tools/ctf/common/utils.h b/tools/ctf/common/utils.h
new file mode 100644
index 000000000000..9b07361a53ab
--- /dev/null
+++ b/tools/ctf/common/utils.h
@@ -0,0 +1,53 @@
+/*
+ * 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 (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define E_SUCCESS 0 /* Exit status for success */
+#define E_ERROR 1 /* Exit status for error */
+#define E_USAGE 2 /* Exit status for usage error */
+
+extern void vwarn(const char *, va_list);
+extern void warn(const char *, ...);
+extern void vdie(const char *, va_list);
+extern void die(const char *, ...);
+
+extern const char *getpname(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UTILS_H */
diff --git a/tools/ctf/cvt/alist.c b/tools/ctf/cvt/alist.c
new file mode 100644
index 000000000000..8e776dc08a7f
--- /dev/null
+++ b/tools/ctf/cvt/alist.c
@@ -0,0 +1,215 @@
+/*
+ * 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 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Create, manage, and destroy association lists. alists are arrays with
+ * arbitrary index types, and are also commonly known as associative arrays.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "alist.h"
+#include "memory.h"
+#include "hash.h"
+
+#define ALIST_HASH_SIZE 997
+
+struct alist {
+ hash_t *al_elements;
+ void (*al_namefree)(void *);
+ void (*al_valfree)(void *);
+};
+
+typedef struct alist_el {
+ void *ale_name;
+ void *ale_value;
+} alist_el_t;
+
+static int
+alist_hash(int nbuckets, void *arg)
+{
+ alist_el_t *el = arg;
+ uintptr_t num = (uintptr_t)el->ale_name;
+
+ return (num % nbuckets);
+}
+
+static int
+alist_cmp(void *arg1, void *arg2)
+{
+ alist_el_t *el1 = arg1;
+ alist_el_t *el2 = arg2;
+ return ((uintptr_t)el1->ale_name != (uintptr_t)el2->ale_name);
+}
+
+alist_t *
+alist_xnew(int nbuckets, void (*namefree)(void *),
+ void (*valfree)(void *), int (*hashfn)(int, void *),
+ int (*cmpfn)(void *, void *))
+{
+ alist_t *alist;
+
+ alist = xcalloc(sizeof (alist_t));
+ alist->al_elements = hash_new(nbuckets, hashfn, cmpfn);
+ alist->al_namefree = namefree;
+ alist->al_valfree = valfree;
+
+ return (alist);
+}
+
+alist_t *
+alist_new(void (*namefree)(void *), void (*valfree)(void *))
+{
+ return (alist_xnew(ALIST_HASH_SIZE, namefree, valfree,
+ alist_hash, alist_cmp));
+}
+
+static void
+alist_free_cb(void *arg1, void *arg2)
+{
+ alist_el_t *el = arg1;
+ alist_t *alist = arg2;
+ if (alist->al_namefree)
+ alist->al_namefree(el->ale_name);
+ if (alist->al_valfree)
+ alist->al_valfree(el->ale_name);
+ free(el);
+}
+
+void
+alist_free(alist_t *alist)
+{
+ hash_free(alist->al_elements, alist_free_cb, alist);
+ free(alist);
+}
+
+void
+alist_add(alist_t *alist, void *name, void *value)
+{
+ alist_el_t *el;
+
+ el = xmalloc(sizeof (alist_el_t));
+ el->ale_name = name;
+ el->ale_value = value;
+ hash_add(alist->al_elements, el);
+}
+
+int
+alist_find(alist_t *alist, void *name, void **value)
+{
+ alist_el_t template, *retx;
+ void *ret;
+
+ template.ale_name = name;
+ if (!hash_find(alist->al_elements, &template, &ret))
+ return (0);
+
+ if (value) {
+ retx = ret;
+ *value = retx->ale_value;
+ }
+
+ return (1);
+}
+
+typedef struct alist_iter_data {
+ int (*aid_func)(void *, void *, void *);
+ void *aid_priv;
+} alist_iter_data_t;
+
+static int
+alist_iter_cb(void *arg1, void *arg2)
+{
+ alist_el_t *el = arg1;
+ alist_iter_data_t *aid = arg2;
+ return (aid->aid_func(el->ale_name, el->ale_value, aid->aid_priv));
+}
+
+int
+alist_iter(alist_t *alist, int (*func)(void *, void *, void *), void *private)
+{
+ alist_iter_data_t aid;
+
+ aid.aid_func = func;
+ aid.aid_priv = private;
+
+ return (hash_iter(alist->al_elements, alist_iter_cb, &aid));
+}
+
+/*
+ * Debugging support. Used to print the contents of an alist.
+ */
+
+void
+alist_stats(alist_t *alist, int verbose)
+{
+ printf("Alist statistics\n");
+ hash_stats(alist->al_elements, verbose);
+}
+
+static int alist_def_print_cb_key_int = 1;
+static int alist_def_print_cb_value_int = 1;
+
+static int
+alist_def_print_cb(void *key, void *value)
+{
+ printf("Key: ");
+ if (alist_def_print_cb_key_int == 1)
+ printf("%5lu ", (ulong_t)key);
+ else
+ printf("%s\n", (char *)key);
+
+ printf("Value: ");
+ if (alist_def_print_cb_value_int == 1)
+ printf("%5lu\n", (ulong_t)value);
+ else
+ printf("%s\n", (char *)key);
+
+ return (1);
+}
+
+static int
+alist_dump_cb(void *node, void *private)
+{
+ int (*printer)(void *, void *) = private;
+ alist_el_t *el = node;
+
+ printer(el->ale_name, el->ale_value);
+
+ return (1);
+}
+
+int
+alist_dump(alist_t *alist, int (*printer)(void *, void *))
+{
+ if (!printer)
+ printer = alist_def_print_cb;
+
+ return (hash_iter(alist->al_elements, alist_dump_cb, (void *)printer));
+}
diff --git a/tools/ctf/cvt/alist.h b/tools/ctf/cvt/alist.h
new file mode 100644
index 000000000000..629e0290e7ce
--- /dev/null
+++ b/tools/ctf/cvt/alist.h
@@ -0,0 +1,57 @@
+/*
+ * 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 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ASSOC_H
+#define _ASSOC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Create, manage, and destroy association lists. alists are arrays with
+ * arbitrary index types.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct alist alist_t;
+
+alist_t *alist_new(void (*)(void *), void (*)(void *));
+alist_t *alist_xnew(int, void (*)(void *), void (*)(void *),
+ int (*)(int, void *), int (*)(void *, void *));
+void alist_free(alist_t *);
+void alist_add(alist_t *, void *, void *);
+int alist_find(alist_t *, void *, void **);
+int alist_iter(alist_t *, int (*)(void *, void *, void *), void *);
+void alist_stats(alist_t *, int);
+int alist_dump(alist_t *, int (*)(void *, void *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ASSOC_H */
diff --git a/tools/ctf/cvt/barrier.c b/tools/ctf/cvt/barrier.c
new file mode 100644
index 000000000000..bc278b063de1
--- /dev/null
+++ b/tools/ctf/cvt/barrier.c
@@ -0,0 +1,92 @@
+/*
+ * 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 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file implements a barrier, a synchronization primitive designed to allow
+ * threads to wait for each other at given points. Barriers are initialized
+ * with a given number of threads, n, using barrier_init(). When a thread calls
+ * barrier_wait(), that thread blocks until n - 1 other threads reach the
+ * barrier_wait() call using the same barrier_t. When n threads have reached
+ * the barrier, they are all awakened and sent on their way. One of the threads
+ * returns from barrier_wait() with a return code of 1; the remaining threads
+ * get a return code of 0.
+ */
+
+#include <pthread.h>
+#if defined(sun)
+#include <synch.h>
+#endif
+#include <stdio.h>
+
+#include "barrier.h"
+
+void
+barrier_init(barrier_t *bar, int nthreads)
+{
+ pthread_mutex_init(&bar->bar_lock, NULL);
+#if defined(sun)
+ sema_init(&bar->bar_sem, 0, USYNC_THREAD, NULL);
+#else
+ sem_init(&bar->bar_sem, 0, 0);
+#endif
+
+ bar->bar_numin = 0;
+ bar->bar_nthr = nthreads;
+}
+
+int
+barrier_wait(barrier_t *bar)
+{
+ pthread_mutex_lock(&bar->bar_lock);
+
+ if (++bar->bar_numin < bar->bar_nthr) {
+ pthread_mutex_unlock(&bar->bar_lock);
+#if defined(sun)
+ sema_wait(&bar->bar_sem);
+#else
+ sem_wait(&bar->bar_sem);
+#endif
+
+ return (0);
+
+ } else {
+ int i;
+
+ /* reset for next use */
+ bar->bar_numin = 0;
+ for (i = 1; i < bar->bar_nthr; i++)
+#if defined(sun)
+ sema_post(&bar->bar_sem);
+#else
+ sem_post(&bar->bar_sem);
+#endif
+ pthread_mutex_unlock(&bar->bar_lock);
+
+ return (1);
+ }
+}
diff --git a/tools/ctf/cvt/barrier.h b/tools/ctf/cvt/barrier.h
new file mode 100644
index 000000000000..c7e6212a69c5
--- /dev/null
+++ b/tools/ctf/cvt/barrier.h
@@ -0,0 +1,62 @@
+/*
+ * 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 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _BARRIER_H
+#define _BARRIER_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * APIs for the barrier synchronization primitive.
+ */
+
+#if defined(sun)
+#include <synch.h>
+#else
+#include <semaphore.h>
+typedef sem_t sema_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct barrier {
+ pthread_mutex_t bar_lock; /* protects bar_numin */
+ int bar_numin; /* current number of waiters */
+
+ sema_t bar_sem; /* where everyone waits */
+ int bar_nthr; /* # of waiters to trigger release */
+} barrier_t;
+
+extern void barrier_init(barrier_t *, int);
+extern int barrier_wait(barrier_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BARRIER_H */
diff --git a/tools/ctf/cvt/compare.c b/tools/ctf/cvt/compare.c
new file mode 100644
index 000000000000..26037f8a537a
--- /dev/null
+++ b/tools/ctf/cvt/compare.c
@@ -0,0 +1,92 @@
+/*
+ * 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"
+
+/*
+ * This is a test program designed to catch mismerges and mistranslations from
+ * stabs to CTF.
+ *
+ * Given a file with stabs data and a file with CTF data, determine whether
+ * or not all of the data structures and objects described by the stabs data
+ * are present in the CTF data.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "ctftools.h"
+
+char *progname;
+int debug_level = DEBUG_LEVEL;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s ctf_file stab_file\n", progname);
+}
+
+int
+main(int argc, char **argv)
+{
+ tdata_t *ctftd, *stabrtd, *stabtd, *difftd;
+ char *ctfname, *stabname;
+ int new;
+
+ progname = argv[0];
+
+ if (argc != 3) {
+ usage();
+ exit(2);
+ }
+
+ ctfname = argv[1];
+ stabname = argv[2];
+
+ stabrtd = tdata_new();
+ stabtd = tdata_new();
+ difftd = tdata_new();
+
+ if (read_stabs(stabrtd, stabname, 0) != 0)
+ merge_into_master(stabrtd, stabtd, NULL, 1);
+ else if (read_ctf(&stabname, 1, NULL, read_ctf_save_cb, &stabtd, 0)
+ == 0)
+ terminate("%s doesn't have stabs or CTF\n", stabname);
+
+ if (read_ctf(&ctfname, 1, NULL, read_ctf_save_cb, &ctftd, 0) == 0)
+ terminate("%s doesn't contain CTF data\n", ctfname);
+
+ merge_into_master(stabtd, ctftd, difftd, 0);
+
+ if ((new = hash_count(difftd->td_iihash)) != 0) {
+ (void) hash_iter(difftd->td_iihash, (int (*)())iidesc_dump,
+ NULL);
+ terminate("%s grew by %d\n", stabname, new);
+ }
+
+ return (0);
+}
diff --git a/tools/ctf/cvt/ctf.c b/tools/ctf/cvt/ctf.c
new file mode 100644
index 000000000000..6a9d67aa3002
--- /dev/null
+++ b/tools/ctf/cvt/ctf.c
@@ -0,0 +1,1263 @@
+/*
+ * 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"
+
+/*
+ * Create and parse buffers containing CTF data.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <ctype.h>
+#include <zlib.h>
+#include <elf.h>
+
+#include "ctf_headers.h"
+#include "ctftools.h"
+#include "strtab.h"
+#include "memory.h"
+
+/*
+ * Name of the file currently being read, used to print error messages. We
+ * assume that only one file will be read at a time, and thus make no attempt
+ * to allow curfile to be used simultaneously by multiple threads.
+ *
+ * The value is only valid during a call to ctf_load.
+ */
+char *curfile;
+
+#define CTF_BUF_CHUNK_SIZE (64 * 1024)
+#define RES_BUF_CHUNK_SIZE (64 * 1024)
+
+struct ctf_buf {
+ strtab_t ctb_strtab; /* string table */
+ caddr_t ctb_base; /* pointer to base of buffer */
+ caddr_t ctb_end; /* pointer to end of buffer */
+ caddr_t ctb_ptr; /* pointer to empty buffer space */
+ size_t ctb_size; /* size of buffer */
+ int nptent; /* number of processed types */
+ int ntholes; /* number of type holes */
+};
+
+/*PRINTFLIKE1*/
+static void
+parseterminate(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;
+
+ ctf_buf_write(b, &ctl, sizeof (ctl));
+
+ return (1);
+}
+
+static void
+write_objects(iidesc_t *idp, ctf_buf_t *b)
+{
+ ushort_t id = (idp ? idp->ii_dtype->t_id : 0);
+
+ ctf_buf_write(b, &id, sizeof (id));
+
+ debug(3, "Wrote object %s (%d)\n", (idp ? idp->ii_name : "(null)"), id);
+}
+
+static void
+write_functions(iidesc_t *idp, ctf_buf_t *b)
+{
+ ushort_t fdata[2];
+ ushort_t id;
+ int nargs;
+ int i;
+
+ if (!idp) {
+ fdata[0] = 0;
+ ctf_buf_write(b, &fdata[0], sizeof (fdata[0]));
+
+ debug(3, "Wrote function (null)\n");
+ return;
+ }
+
+ nargs = idp->ii_nargs + (idp->ii_vargs != 0);
+ fdata[0] = CTF_TYPE_INFO(CTF_K_FUNCTION, 1, nargs);
+ fdata[1] = idp->ii_dtype->t_id;
+ ctf_buf_write(b, fdata, sizeof (fdata));
+
+ for (i = 0; i < idp->ii_nargs; i++) {
+ id = idp->ii_args[i]->t_id;
+ ctf_buf_write(b, &id, sizeof (id));
+ }
+
+ if (idp->ii_vargs) {
+ id = 0;
+ ctf_buf_write(b, &id, sizeof (id));
+ }
+
+ debug(3, "Wrote function %s (%d args)\n", idp->ii_name, nargs);
+}
+
+/*
+ * Depending on the size of the type being described, either a ctf_stype_t (for
+ * types with size < CTF_LSTRUCT_THRESH) or a ctf_type_t (all others) will be
+ * written. We isolate the determination here so the rest of the writer code
+ * doesn't need to care.
+ */
+static void
+write_sized_type_rec(ctf_buf_t *b, ctf_type_t *ctt, size_t size)
+{
+ if (size > CTF_MAX_SIZE) {
+ ctt->ctt_size = CTF_LSIZE_SENT;
+ ctt->ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size);
+ ctt->ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size);
+ ctf_buf_write(b, ctt, sizeof (*ctt));
+ } else {
+ ctf_stype_t *cts = (ctf_stype_t *)ctt;
+
+ cts->ctt_size = (ushort_t)size;
+ ctf_buf_write(b, cts, sizeof (*cts));
+ }
+}
+
+static void
+write_unsized_type_rec(ctf_buf_t *b, ctf_type_t *ctt)
+{
+ ctf_stype_t *cts = (ctf_stype_t *)ctt;
+
+ ctf_buf_write(b, cts, sizeof (*cts));
+}
+
+static int
+write_type(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);
+ ctf_buf_write(b, &data, sizeof (data));
+ break;
+
+ case POINTER:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_POINTER, isroot, 0);
+ ctt.ctt_type = tp->t_tdesc->t_id;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case ARRAY:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_ARRAY, isroot, 1);
+ write_sized_type_rec(b, &ctt, tp->t_size);
+
+ cta.cta_contents = tp->t_ardef->ad_contents->t_id;
+ cta.cta_index = tp->t_ardef->ad_idxtype->t_id;
+ cta.cta_nelems = tp->t_ardef->ad_nelems;
+ ctf_buf_write(b, &cta, sizeof (cta));
+ break;
+
+ case STRUCT:
+ case UNION:
+ for (i = 0, mp = tp->t_members; mp != NULL; mp = mp->ml_next)
+ i++; /* count up struct or union members */
+
+ if (tp->t_type == STRUCT)
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, isroot, i);
+ else
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, isroot, i);
+
+ write_sized_type_rec(b, &ctt, tp->t_size);
+
+ if (tp->t_size < CTF_LSTRUCT_THRESH) {
+ for (mp = tp->t_members; mp != NULL; mp = mp->ml_next) {
+ offset = strtab_insert(&b->ctb_strtab,
+ mp->ml_name);
+
+ ctm.ctm_name = CTF_TYPE_NAME(CTF_STRTAB_0,
+ offset);
+ ctm.ctm_type = mp->ml_type->t_id;
+ ctm.ctm_offset = mp->ml_offset;
+ ctf_buf_write(b, &ctm, sizeof (ctm));
+ }
+ } else {
+ for (mp = tp->t_members; mp != NULL; mp = mp->ml_next) {
+ offset = strtab_insert(&b->ctb_strtab,
+ mp->ml_name);
+
+ ctlm.ctlm_name = CTF_TYPE_NAME(CTF_STRTAB_0,
+ offset);
+ ctlm.ctlm_type = mp->ml_type->t_id;
+ ctlm.ctlm_offsethi =
+ CTF_OFFSET_TO_LMEMHI(mp->ml_offset);
+ ctlm.ctlm_offsetlo =
+ CTF_OFFSET_TO_LMEMLO(mp->ml_offset);
+ ctf_buf_write(b, &ctlm, sizeof (ctlm));
+ }
+ }
+ break;
+
+ case ENUM:
+ for (i = 0, ep = tp->t_emem; ep != NULL; ep = ep->el_next)
+ i++; /* count up enum members */
+
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, isroot, i);
+ write_sized_type_rec(b, &ctt, tp->t_size);
+
+ for (ep = tp->t_emem; ep != NULL; ep = ep->el_next) {
+ offset = strtab_insert(&b->ctb_strtab, ep->el_name);
+ cte.cte_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset);
+ cte.cte_value = ep->el_number;
+ ctf_buf_write(b, &cte, sizeof (cte));
+ }
+ break;
+
+ case FORWARD:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FORWARD, isroot, 0);
+ ctt.ctt_type = 0;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case TYPEDEF:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_TYPEDEF, isroot, 0);
+ ctt.ctt_type = tp->t_tdesc->t_id;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case VOLATILE:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_VOLATILE, isroot, 0);
+ ctt.ctt_type = tp->t_tdesc->t_id;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case CONST:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_CONST, isroot, 0);
+ ctt.ctt_type = tp->t_tdesc->t_id;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case FUNCTION:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FUNCTION, isroot,
+ tp->t_fndef->fn_nargs + tp->t_fndef->fn_vargs);
+ 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;
+ 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;
+
+ /*
+ * 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);
+
+ /*
+ * 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);
+}
diff --git a/tools/ctf/cvt/ctfconvert.c b/tools/ctf/cvt/ctfconvert.c
new file mode 100644
index 000000000000..efe6c279a0b7
--- /dev/null
+++ b/tools/ctf/cvt/ctfconvert.c
@@ -0,0 +1,263 @@
+/*
+ * 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"
+
+/*
+ * Given a file containing sections with stabs data, convert the stabs data to
+ * CTF data, and replace the stabs sections with a CTF section.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "ctftools.h"
+#include "memory.h"
+
+const char *progname;
+int debug_level = DEBUG_LEVEL;
+
+static char *infile = NULL;
+static const char *outfile = NULL;
+static int dynsym;
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "Usage: %s [-gis] -l label | -L labelenv [-o outfile] object_file\n"
+ "\n"
+ " Note: if -L labelenv is specified and labelenv is not set in\n"
+ " the environment, a default value is used.\n",
+ progname);
+}
+
+static void
+terminate_cleanup(void)
+{
+#if !defined(__FreeBSD__)
+ if (!outfile) {
+ fprintf(stderr, "Removing %s\n", infile);
+ unlink(infile);
+ }
+#endif
+}
+
+static void
+handle_sig(int sig)
+{
+ terminate("Caught signal %d - exiting\n", sig);
+}
+
+static int
+file_read(tdata_t *td, char *filename, int ignore_non_c)
+{
+ typedef int (*reader_f)(tdata_t *, Elf *, char *);
+ static reader_f readers[] = {
+ stabs_read,
+ dw_read,
+ NULL
+ };
+
+ source_types_t source_types;
+ Elf *elf;
+ int i, rc, fd;
+
+ if ((fd = open(filename, O_RDONLY)) < 0)
+ terminate("failed to open %s", filename);
+
+ (void) elf_version(EV_CURRENT);
+
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
+ close(fd);
+ terminate("failed to read %s: %s\n", filename,
+ elf_errmsg(-1));
+ }
+
+ source_types = built_source_types(elf, filename);
+
+ if ((source_types == SOURCE_NONE || (source_types & SOURCE_UNKNOWN)) &&
+ ignore_non_c) {
+ debug(1, "Ignoring file %s from unknown sources\n", filename);
+ exit(0);
+ }
+
+ for (i = 0; readers[i] != NULL; i++) {
+ if ((rc = readers[i](td, elf, filename)) == 0)
+ break;
+
+ assert(rc < 0 && errno == ENOENT);
+ }
+
+ if (readers[i] == NULL) {
+ /*
+ * None of the readers found compatible type data.
+ */
+
+ if (findelfsecidx(elf, filename, ".debug") >= 0) {
+ terminate("%s: DWARF version 1 is not supported\n",
+ filename);
+ }
+
+ if (!(source_types & SOURCE_C) && ignore_non_c) {
+ debug(1, "Ignoring file %s not built from C sources\n",
+ filename);
+ exit(0);
+ }
+
+ rc = 0;
+ } else {
+ rc = 1;
+ }
+
+ (void) elf_end(elf);
+ (void) close(fd);
+
+ return (rc);
+}
+
+int
+main(int argc, char **argv)
+{
+ tdata_t *filetd, *mstrtd;
+ const char *label = NULL;
+ int verbose = 0;
+ int ignore_non_c = 0;
+ int keep_stabs = 0;
+ int c;
+
+#if defined(sun)
+ sighold(SIGINT);
+ sighold(SIGQUIT);
+ sighold(SIGTERM);
+#endif
+
+ progname = basename(argv[0]);
+
+ if (getenv("CTFCONVERT_DEBUG_LEVEL"))
+ debug_level = atoi(getenv("CTFCONVERT_DEBUG_LEVEL"));
+ if (getenv("CTFCONVERT_DEBUG_PARSE"))
+ debug_parse = atoi(getenv("CTFCONVERT_DEBUG_PARSE"));
+
+ while ((c = getopt(argc, argv, ":l:L:o:givs")) != EOF) {
+ switch (c) {
+ case 'l':
+ label = optarg;
+ break;
+ case 'L':
+ if ((label = getenv(optarg)) == NULL)
+ label = CTF_DEFAULT_LABEL;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 's':
+ dynsym = CTF_USE_DYNSYM;
+ break;
+ case 'i':
+ ignore_non_c = 1;
+ break;
+ case 'g':
+ keep_stabs = CTF_KEEP_STABS;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ exit(2);
+ }
+ }
+
+ if (getenv("STRIPSTABS_KEEP_STABS") != NULL)
+ keep_stabs = CTF_KEEP_STABS;
+
+ if (argc - optind != 1 || label == NULL) {
+ usage();
+ exit(2);
+ }
+
+ infile = argv[optind];
+ if (access(infile, R_OK) != 0)
+ terminate("Can't access %s", infile);
+
+ /*
+ * Upon receipt of a signal, we want to clean up and exit. Our
+ * primary goal during cleanup is to restore the system to a state
+ * such that a subsequent make will eventually cause this command to
+ * be re-run. If we remove the input file (which we do if we get a
+ * signal and the user didn't specify a separate output file), make
+ * will need to rebuild the input file, and will then need to re-run
+ * ctfconvert, which is what we want.
+ */
+ set_terminate_cleanup(terminate_cleanup);
+
+#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
+
+ filetd = tdata_new();
+
+ if (!file_read(filetd, infile, ignore_non_c))
+ terminate("%s doesn't have type data to convert\n", infile);
+
+ if (verbose)
+ iidesc_stats(filetd->td_iihash);
+
+ mstrtd = tdata_new();
+ merge_into_master(filetd, mstrtd, NULL, 1);
+
+ tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX);
+
+ /*
+ * If the user supplied an output file that is different from the
+ * input file, write directly to the output file. Otherwise, write
+ * to a temporary file, and replace the input file when we're done.
+ */
+ if (outfile && strcmp(infile, outfile) != 0) {
+ write_ctf(mstrtd, infile, outfile, dynsym | keep_stabs);
+ } else {
+ char *tmpname = mktmpname(infile, ".ctf");
+ write_ctf(mstrtd, infile, tmpname, dynsym | keep_stabs);
+ if (rename(tmpname, infile) != 0)
+ terminate("Couldn't rename temp file %s", tmpname);
+ free(tmpname);
+ }
+
+ return (0);
+}
diff --git a/tools/ctf/cvt/ctfmerge.c b/tools/ctf/cvt/ctfmerge.c
new file mode 100644
index 000000000000..546dcdfc9386
--- /dev/null
+++ b/tools/ctf/cvt/ctfmerge.c
@@ -0,0 +1,1004 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <assert.h>
+#if defined(sun)
+#include <synch.h>
+#endif
+#include <signal.h>
+#include <libgen.h>
+#include <string.h>
+#include <errno.h>
+#if defined(sun)
+#include <alloca.h>
+#endif
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#if defined(sun)
+#include <sys/sysconf.h>
+#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, 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);
+ 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);
+
+ 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)
+{
+ pthread_t thrid;
+ 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(&thrid, 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 int
+strcompare(const void *p1, const void *p2)
+{
+ char *s1 = *((char **)p1);
+ char *s2 = *((char **)p2);
+
+ return (strcmp(s1, s2));
+}
+
+int
+main(int argc, char **argv)
+{
+ workqueue_t wq;
+ 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);
+
+ /*
+ * 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);
+ if (rename(tmpname, outfile) != 0)
+ terminate("Couldn't rename output temp file %s", tmpname);
+ free(tmpname);
+
+ return (0);
+}
diff --git a/tools/ctf/cvt/ctfmerge.h b/tools/ctf/cvt/ctfmerge.h
new file mode 100644
index 000000000000..38560eab6446
--- /dev/null
+++ b/tools/ctf/cvt/ctfmerge.h
@@ -0,0 +1,89 @@
+/*
+ * 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 2002-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CTFMERGE_H
+#define _CTFMERGE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Merging structures used in ctfmerge. See ctfmerge.c for locking semantics.
+ */
+
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ctftools.h"
+#include "barrier.h"
+#include "fifo.h"
+
+typedef struct wip {
+ pthread_mutex_t wip_lock;
+ pthread_cond_t wip_cv;
+ tdata_t *wip_td;
+ int wip_nmerged;
+ int wip_batchid;
+} wip_t;
+
+typedef struct workqueue {
+ int wq_next_batchid;
+
+ int wq_maxbatchsz;
+
+ wip_t *wq_wip;
+ int wq_nwipslots;
+ int wq_nthreads;
+ int wq_ithrottle;
+
+ pthread_mutex_t wq_queue_lock;
+ fifo_t *wq_queue;
+ pthread_cond_t wq_work_avail;
+ pthread_cond_t wq_work_removed;
+ int wq_ninqueue;
+ int wq_nextpownum;
+
+ pthread_mutex_t wq_donequeue_lock;
+ fifo_t *wq_donequeue;
+ int wq_lastdonebatch;
+ pthread_cond_t wq_done_cv;
+
+ pthread_cond_t wq_alldone_cv; /* protected by queue_lock */
+ int wq_alldone;
+
+ int wq_nomorefiles;
+
+ barrier_t wq_bar1;
+ barrier_t wq_bar2;
+} workqueue_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTFMERGE_H */
diff --git a/tools/ctf/cvt/ctftools.h b/tools/ctf/cvt/ctftools.h
new file mode 100644
index 000000000000..52bbf44e1690
--- /dev/null
+++ b/tools/ctf/cvt/ctftools.h
@@ -0,0 +1,453 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <pthread.h>
+
+#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 */
+
+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 */
diff --git a/tools/ctf/cvt/dwarf.c b/tools/ctf/cvt/dwarf.c
new file mode 100644
index 000000000000..19aeff2c7942
--- /dev/null
+++ b/tools/ctf/cvt/dwarf.c
@@ -0,0 +1,1848 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * DWARF to tdata conversion
+ *
+ * For the most part, conversion is straightforward, proceeding in two passes.
+ * On the first pass, we iterate through every die, creating new type nodes as
+ * necessary. Referenced tdesc_t's are created in an uninitialized state, thus
+ * allowing type reference pointers to be filled in. If the tdesc_t
+ * corresponding to a given die can be completely filled out (sizes and offsets
+ * calculated, and so forth) without using any referenced types, the tdesc_t is
+ * marked as resolved. Consider an array type. If the type corresponding to
+ * the array contents has not yet been processed, we will create a blank tdesc
+ * for the contents type (only the type ID will be filled in, relying upon the
+ * later portion of the first pass to encounter and complete the referenced
+ * type). We will then attempt to determine the size of the array. If the
+ * array has a byte size attribute, we will have completely characterized the
+ * array type, and will be able to mark it as resolved. The lack of a byte
+ * size attribute, on the other hand, will prevent us from fully resolving the
+ * type, as the size will only be calculable with reference to the contents
+ * type, which has not, as yet, been encountered. The array type will thus be
+ * left without the resolved flag, and the first pass will continue.
+ *
+ * When we begin the second pass, we will have created tdesc_t nodes for every
+ * type in the section. We will traverse the tree, from the iidescs down,
+ * processing each unresolved node. As the referenced nodes will have been
+ * populated, the array type used in our example above will be able to use the
+ * size of the referenced types (if available) to determine its own type. The
+ * traversal will be repeated until all types have been resolved or we have
+ * failed to make progress. When all tdescs have been resolved, the conversion
+ * is complete.
+ *
+ * There are, as always, a few special cases that are handled during the first
+ * and second passes:
+ *
+ * 1. Empty enums - GCC will occasionally emit an enum without any members.
+ * Later on in the file, it will emit the same enum type, though this time
+ * with the full complement of members. All references to the memberless
+ * enum need to be redirected to the full definition. During the first
+ * pass, each enum is entered in dm_enumhash, along with a pointer to its
+ * corresponding tdesc_t. If, during the second pass, we encounter a
+ * memberless enum, we use the hash to locate the full definition. All
+ * tdescs referencing the empty enum are then redirected.
+ *
+ * 2. Forward declarations - If the compiler sees a forward declaration for
+ * a structure, followed by the definition of that structure, it will emit
+ * DWARF data for both the forward declaration and the definition. We need
+ * to resolve the forward declarations when possible, by redirecting
+ * forward-referencing tdescs to the actual struct/union definitions. This
+ * redirection is done completely within the first pass. We begin by
+ * recording all forward declarations in dw_fwdhash. When we define a
+ * structure, we check to see if there have been any corresponding forward
+ * declarations. If so, we redirect the tdescs which referenced the forward
+ * declarations to the structure or union definition.
+ *
+ * XXX see if a post traverser will allow the elimination of repeated pass 2
+ * traversals.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <libelf.h>
+#include <libdwarf.h>
+#include <libgen.h>
+#include <dwarf.h>
+
+#include "ctf_headers.h"
+#include "ctftools.h"
+#include "memory.h"
+#include "list.h"
+#include "traverse.h"
+
+/* The version of DWARF which we support. */
+#define DWARF_VERSION 2
+
+/*
+ * We need to define a couple of our own intrinsics, to smooth out some of the
+ * differences between the GCC and DevPro DWARF emitters. See the referenced
+ * routines and the special cases in the file comment for more details.
+ *
+ * Type IDs are 32 bits wide. We're going to use the top of that field to
+ * indicate types that we've created ourselves.
+ */
+#define TID_FILEMAX 0x3fffffff /* highest tid from file */
+#define TID_VOID 0x40000001 /* see die_void() */
+#define TID_LONG 0x40000002 /* see die_array() */
+
+#define TID_MFGTID_BASE 0x40000003 /* first mfg'd tid */
+
+/*
+ * To reduce the staggering amount of error-handling code that would otherwise
+ * be required, the attribute-retrieval routines handle most of their own
+ * errors. If the following flag is supplied as the value of the `req'
+ * argument, they will also handle the absence of a requested attribute by
+ * terminating the program.
+ */
+#define DW_ATTR_REQ 1
+
+#define TDESC_HASH_BUCKETS 511
+
+typedef struct dwarf {
+ Dwarf_Debug dw_dw; /* for libdwarf */
+ Dwarf_Error dw_err; /* for libdwarf */
+ Dwarf_Off dw_maxoff; /* highest legal offset in this cu */
+ tdata_t *dw_td; /* root of the tdesc/iidesc tree */
+ hash_t *dw_tidhash; /* hash of tdescs by t_id */
+ hash_t *dw_fwdhash; /* hash of fwd decls by name */
+ hash_t *dw_enumhash; /* hash of memberless enums by name */
+ tdesc_t *dw_void; /* manufactured void type */
+ tdesc_t *dw_long; /* manufactured long type for arrays */
+ size_t dw_ptrsz; /* size of a pointer in this file */
+ tid_t dw_mfgtid_last; /* last mfg'd type ID used */
+ uint_t dw_nunres; /* count of unresolved types */
+ char *dw_cuname; /* name of compilation unit */
+} dwarf_t;
+
+static void die_create_one(dwarf_t *, Dwarf_Die);
+static void die_create(dwarf_t *, Dwarf_Die);
+
+static tid_t
+mfgtid_next(dwarf_t *dw)
+{
+ return (++dw->dw_mfgtid_last);
+}
+
+static void
+tdesc_add(dwarf_t *dw, tdesc_t *tdp)
+{
+ hash_add(dw->dw_tidhash, tdp);
+}
+
+static tdesc_t *
+tdesc_lookup(dwarf_t *dw, int tid)
+{
+ tdesc_t tmpl;
+ void *tdp;
+
+ tmpl.t_id = tid;
+
+ if (hash_find(dw->dw_tidhash, &tmpl, &tdp))
+ return (tdp);
+ else
+ return (NULL);
+}
+
+/*
+ * Resolve a tdesc down to a node which should have a size. Returns the size,
+ * zero if the size hasn't yet been determined.
+ */
+static size_t
+tdesc_size(tdesc_t *tdp)
+{
+ for (;;) {
+ switch (tdp->t_type) {
+ case INTRINSIC:
+ case POINTER:
+ case ARRAY:
+ case FUNCTION:
+ case STRUCT:
+ case UNION:
+ case ENUM:
+ return (tdp->t_size);
+
+ case FORWARD:
+ return (0);
+
+ case TYPEDEF:
+ case VOLATILE:
+ case CONST:
+ case RESTRICT:
+ tdp = tdp->t_tdesc;
+ continue;
+
+ case 0: /* not yet defined */
+ return (0);
+
+ default:
+ terminate("tdp %u: tdesc_size on unknown type %d\n",
+ tdp->t_id, tdp->t_type);
+ }
+ }
+}
+
+static size_t
+tdesc_bitsize(tdesc_t *tdp)
+{
+ for (;;) {
+ switch (tdp->t_type) {
+ case INTRINSIC:
+ return (tdp->t_intr->intr_nbits);
+
+ case ARRAY:
+ case FUNCTION:
+ case STRUCT:
+ case UNION:
+ case ENUM:
+ case POINTER:
+ return (tdp->t_size * NBBY);
+
+ case FORWARD:
+ return (0);
+
+ case TYPEDEF:
+ case VOLATILE:
+ case RESTRICT:
+ case CONST:
+ tdp = tdp->t_tdesc;
+ continue;
+
+ case 0: /* not yet defined */
+ return (0);
+
+ default:
+ terminate("tdp %u: tdesc_bitsize on unknown type %d\n",
+ tdp->t_id, tdp->t_type);
+ }
+ }
+}
+
+static tdesc_t *
+tdesc_basetype(tdesc_t *tdp)
+{
+ for (;;) {
+ switch (tdp->t_type) {
+ case TYPEDEF:
+ case VOLATILE:
+ case RESTRICT:
+ case CONST:
+ tdp = tdp->t_tdesc;
+ break;
+ case 0: /* not yet defined */
+ return (NULL);
+ default:
+ return (tdp);
+ }
+ }
+}
+
+static Dwarf_Off
+die_off(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Off off;
+
+ if (dwarf_dieoffset(die, &off, &dw->dw_err) == DW_DLV_OK)
+ return (off);
+
+ terminate("failed to get offset for die: %s\n",
+ dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (0);
+}
+
+static Dwarf_Die
+die_sibling(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Die sib;
+ int rc;
+
+ if ((rc = dwarf_siblingof(dw->dw_dw, die, &sib, &dw->dw_err)) ==
+ DW_DLV_OK)
+ return (sib);
+ else if (rc == DW_DLV_NO_ENTRY)
+ return (NULL);
+
+ terminate("die %llu: failed to find type sibling: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (NULL);
+}
+
+static Dwarf_Die
+die_child(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Die child;
+ int rc;
+
+ if ((rc = dwarf_child(die, &child, &dw->dw_err)) == DW_DLV_OK)
+ return (child);
+ else if (rc == DW_DLV_NO_ENTRY)
+ return (NULL);
+
+ terminate("die %llu: failed to find type child: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (NULL);
+}
+
+static Dwarf_Half
+die_tag(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Half tag;
+
+ if (dwarf_tag(die, &tag, &dw->dw_err) == DW_DLV_OK)
+ return (tag);
+
+ terminate("die %llu: failed to get tag for type: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (0);
+}
+
+static Dwarf_Attribute
+die_attr(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, int req)
+{
+ Dwarf_Attribute attr;
+ int rc;
+
+ if ((rc = dwarf_attr(die, name, &attr, &dw->dw_err)) == DW_DLV_OK) {
+ return (attr);
+ } else if (rc == DW_DLV_NO_ENTRY) {
+ if (req) {
+ terminate("die %llu: no attr 0x%x\n", die_off(dw, die),
+ name);
+ } else {
+ return (NULL);
+ }
+ }
+
+ terminate("die %llu: failed to get attribute for type: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (NULL);
+}
+
+static int
+die_signed(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Signed *valp,
+ int req)
+{
+ *valp = 0;
+ if (dwarf_attrval_signed(die, name, valp, &dw->dw_err) != DWARF_E_NONE) {
+ if (req)
+ terminate("die %llu: failed to get signed: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+die_unsigned(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Unsigned *valp,
+ int req)
+{
+ *valp = 0;
+ if (dwarf_attrval_unsigned(die, name, valp, &dw->dw_err) != DWARF_E_NONE) {
+ if (req)
+ terminate("die %llu: failed to get unsigned: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+die_bool(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Bool *valp, int req)
+{
+ *valp = 0;
+
+ if (dwarf_attrval_flag(die, name, valp, &dw->dw_err) != DWARF_E_NONE) {
+ if (req)
+ terminate("die %llu: failed to get flag: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+die_string(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, char **strp, int req)
+{
+ const char *str = NULL;
+
+ if (dwarf_attrval_string(die, name, &str, &dw->dw_err) != DWARF_E_NONE ||
+ str == NULL) {
+ if (req)
+ terminate("die %llu: failed to get string: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ else
+ *strp = NULL;
+ return (0);
+ } else
+ *strp = xstrdup(str);
+
+ return (1);
+}
+
+static Dwarf_Off
+die_attr_ref(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name)
+{
+ Dwarf_Off off;
+
+ if (dwarf_attrval_unsigned(die, name, &off, &dw->dw_err) != DWARF_E_NONE) {
+ terminate("die %llu: failed to get ref: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ }
+
+ return (off);
+}
+
+static char *
+die_name(dwarf_t *dw, Dwarf_Die die)
+{
+ char *str = NULL;
+
+ (void) die_string(dw, die, DW_AT_name, &str, 0);
+
+ return (str);
+}
+
+static int
+die_isdecl(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Bool val;
+
+ return (die_bool(dw, die, DW_AT_declaration, &val, 0) && val);
+}
+
+static int
+die_isglobal(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Signed vis;
+ Dwarf_Bool ext;
+
+ /*
+ * Some compilers (gcc) use DW_AT_external to indicate function
+ * visibility. Others (Sun) use DW_AT_visibility.
+ */
+ if (die_signed(dw, die, DW_AT_visibility, &vis, 0))
+ return (vis == DW_VIS_exported);
+ else
+ return (die_bool(dw, die, DW_AT_external, &ext, 0) && ext);
+}
+
+static tdesc_t *
+die_add(dwarf_t *dw, Dwarf_Off off)
+{
+ tdesc_t *tdp = xcalloc(sizeof (tdesc_t));
+
+ tdp->t_id = off;
+
+ tdesc_add(dw, tdp);
+
+ return (tdp);
+}
+
+static tdesc_t *
+die_lookup_pass1(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name)
+{
+ Dwarf_Off ref = die_attr_ref(dw, die, name);
+ tdesc_t *tdp;
+
+ if ((tdp = tdesc_lookup(dw, ref)) != NULL)
+ return (tdp);
+
+ return (die_add(dw, ref));
+}
+
+static int
+die_mem_offset(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Unsigned *valp, int req __unused)
+{
+ Dwarf_Locdesc *loc = NULL;
+ Dwarf_Signed locnum = 0;
+
+ if (dwarf_locdesc(die, name, &loc, &locnum, &dw->dw_err) != DW_DLV_OK)
+ return (0);
+
+ if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) {
+ terminate("die %llu: cannot parse member offset\n",
+ die_off(dw, die));
+ }
+
+ *valp = loc->ld_s->lr_number;
+
+ if (loc != NULL)
+ if (dwarf_locdesc_free(loc, &dw->dw_err) != DW_DLV_OK)
+ terminate("die %llu: cannot free location descriptor: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+
+ return (1);
+}
+
+static tdesc_t *
+tdesc_intr_common(dwarf_t *dw, int tid, const char *name, size_t sz)
+{
+ tdesc_t *tdp;
+ intr_t *intr;
+
+ intr = xcalloc(sizeof (intr_t));
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = 1;
+ intr->intr_nbits = sz * NBBY;
+
+ tdp = xcalloc(sizeof (tdesc_t));
+ tdp->t_name = xstrdup(name);
+ tdp->t_size = sz;
+ tdp->t_id = tid;
+ tdp->t_type = INTRINSIC;
+ tdp->t_intr = intr;
+ tdp->t_flags = TDESC_F_RESOLVED;
+
+ tdesc_add(dw, tdp);
+
+ return (tdp);
+}
+
+/*
+ * Manufacture a void type. Used for gcc-emitted stabs, where the lack of a
+ * type reference implies a reference to a void type. A void *, for example
+ * will be represented by a pointer die without a DW_AT_type. CTF requires
+ * that pointer nodes point to something, so we'll create a void for use as
+ * the target. Note that the DWARF data may already create a void type. Ours
+ * would then be a duplicate, but it'll be removed in the self-uniquification
+ * merge performed at the completion of DWARF->tdesc conversion.
+ */
+static tdesc_t *
+tdesc_intr_void(dwarf_t *dw)
+{
+ if (dw->dw_void == NULL)
+ dw->dw_void = tdesc_intr_common(dw, TID_VOID, "void", 0);
+
+ return (dw->dw_void);
+}
+
+static tdesc_t *
+tdesc_intr_long(dwarf_t *dw)
+{
+ if (dw->dw_long == NULL) {
+ dw->dw_long = tdesc_intr_common(dw, TID_LONG, "long",
+ dw->dw_ptrsz);
+ }
+
+ return (dw->dw_long);
+}
+
+/*
+ * Used for creating bitfield types. We create a copy of an existing intrinsic,
+ * adjusting the size of the copy to match what the caller requested. The
+ * caller can then use the copy as the type for a bitfield structure member.
+ */
+static tdesc_t *
+tdesc_intr_clone(dwarf_t *dw, tdesc_t *old, size_t bitsz)
+{
+ tdesc_t *new = xcalloc(sizeof (tdesc_t));
+
+ if (!(old->t_flags & TDESC_F_RESOLVED)) {
+ terminate("tdp %u: attempt to make a bit field from an "
+ "unresolved type\n", old->t_id);
+ }
+
+ new->t_name = xstrdup(old->t_name);
+ new->t_size = old->t_size;
+ new->t_id = mfgtid_next(dw);
+ new->t_type = INTRINSIC;
+ new->t_flags = TDESC_F_RESOLVED;
+
+ new->t_intr = xcalloc(sizeof (intr_t));
+ bcopy(old->t_intr, new->t_intr, sizeof (intr_t));
+ new->t_intr->intr_nbits = bitsz;
+
+ tdesc_add(dw, new);
+
+ return (new);
+}
+
+static void
+tdesc_array_create(dwarf_t *dw, Dwarf_Die dim, tdesc_t *arrtdp,
+ tdesc_t *dimtdp)
+{
+ Dwarf_Unsigned uval;
+ Dwarf_Signed sval;
+ tdesc_t *ctdp = NULL;
+ Dwarf_Die dim2;
+ ardef_t *ar;
+
+ if ((dim2 = die_sibling(dw, dim)) == NULL) {
+ ctdp = arrtdp;
+ } else if (die_tag(dw, dim2) == DW_TAG_subrange_type) {
+ ctdp = xcalloc(sizeof (tdesc_t));
+ ctdp->t_id = mfgtid_next(dw);
+ debug(3, "die %llu: creating new type %u for sub-dimension\n",
+ die_off(dw, dim2), ctdp->t_id);
+ tdesc_array_create(dw, dim2, arrtdp, ctdp);
+ } else {
+ terminate("die %llu: unexpected non-subrange node in array\n",
+ die_off(dw, dim2));
+ }
+
+ dimtdp->t_type = ARRAY;
+ dimtdp->t_ardef = ar = xcalloc(sizeof (ardef_t));
+
+ /*
+ * Array bounds can be signed or unsigned, but there are several kinds
+ * of signless forms (data1, data2, etc) that take their sign from the
+ * routine that is trying to interpret them. That is, data1 can be
+ * either signed or unsigned, depending on whether you use the signed or
+ * unsigned accessor function. GCC will use the signless forms to store
+ * unsigned values which have their high bit set, so we need to try to
+ * read them first as unsigned to get positive values. We could also
+ * try signed first, falling back to unsigned if we got a negative
+ * value.
+ */
+ if (die_unsigned(dw, dim, DW_AT_upper_bound, &uval, 0))
+ ar->ad_nelems = uval + 1;
+ else if (die_signed(dw, dim, DW_AT_upper_bound, &sval, 0))
+ ar->ad_nelems = sval + 1;
+ else
+ ar->ad_nelems = 0;
+
+ /*
+ * Different compilers use different index types. Force the type to be
+ * a common, known value (long).
+ */
+ ar->ad_idxtype = tdesc_intr_long(dw);
+ ar->ad_contents = ctdp;
+
+ if (ar->ad_contents->t_size != 0) {
+ dimtdp->t_size = ar->ad_contents->t_size * ar->ad_nelems;
+ dimtdp->t_flags |= TDESC_F_RESOLVED;
+ }
+}
+
+/*
+ * Create a tdesc from an array node. Some arrays will come with byte size
+ * attributes, and thus can be resolved immediately. Others don't, and will
+ * need to wait until the second pass for resolution.
+ */
+static void
+die_array_create(dwarf_t *dw, Dwarf_Die arr, Dwarf_Off off, tdesc_t *tdp)
+{
+ tdesc_t *arrtdp = die_lookup_pass1(dw, arr, DW_AT_type);
+ Dwarf_Unsigned uval;
+ Dwarf_Die dim;
+
+ debug(3, "die %llu <%llx>: creating array\n", off, off);
+
+ if ((dim = die_child(dw, arr)) == NULL ||
+ die_tag(dw, dim) != DW_TAG_subrange_type)
+ terminate("die %llu: failed to retrieve array bounds\n", off);
+
+ tdesc_array_create(dw, dim, arrtdp, tdp);
+
+ if (die_unsigned(dw, arr, DW_AT_byte_size, &uval, 0)) {
+ tdesc_t *dimtdp;
+ int flags;
+
+ tdp->t_size = uval;
+
+ /*
+ * Ensure that sub-dimensions have sizes too before marking
+ * as resolved.
+ */
+ flags = TDESC_F_RESOLVED;
+ for (dimtdp = tdp->t_ardef->ad_contents;
+ dimtdp->t_type == ARRAY;
+ dimtdp = dimtdp->t_ardef->ad_contents) {
+ if (!(dimtdp->t_flags & TDESC_F_RESOLVED)) {
+ flags = 0;
+ break;
+ }
+ }
+
+ tdp->t_flags |= flags;
+ }
+
+ debug(3, "die %llu <%llx>: array nelems %u size %u\n", off, off,
+ tdp->t_ardef->ad_nelems, tdp->t_size);
+}
+
+/*ARGSUSED1*/
+static int
+die_array_resolve(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private)
+{
+ dwarf_t *dw = private;
+ size_t sz;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ debug(3, "trying to resolve array %d (cont %d)\n", tdp->t_id,
+ tdp->t_ardef->ad_contents->t_id);
+
+ if ((sz = tdesc_size(tdp->t_ardef->ad_contents)) == 0) {
+ debug(3, "unable to resolve array %s (%d) contents %d\n",
+ tdesc_name(tdp), tdp->t_id,
+ tdp->t_ardef->ad_contents->t_id);
+
+ dw->dw_nunres++;
+ return (1);
+ }
+
+ tdp->t_size = sz * tdp->t_ardef->ad_nelems;
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ debug(3, "resolved array %d: %u bytes\n", tdp->t_id, tdp->t_size);
+
+ return (1);
+}
+
+/*ARGSUSED1*/
+static int
+die_array_failed(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private __unused)
+{
+ tdesc_t *cont = tdp->t_ardef->ad_contents;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ fprintf(stderr, "Array %d: failed to size contents type %s (%d)\n",
+ tdp->t_id, tdesc_name(cont), cont->t_id);
+
+ return (1);
+}
+
+/*
+ * Most enums (those with members) will be resolved during this first pass.
+ * Others - those without members (see the file comment) - won't be, and will
+ * need to wait until the second pass when they can be matched with their full
+ * definitions.
+ */
+static void
+die_enum_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ Dwarf_Die mem;
+ Dwarf_Unsigned uval;
+ Dwarf_Signed sval;
+
+ debug(3, "die %llu: creating enum\n", off);
+
+ tdp->t_type = ENUM;
+
+ (void) die_unsigned(dw, die, DW_AT_byte_size, &uval, DW_ATTR_REQ);
+ tdp->t_size = uval;
+
+ if ((mem = die_child(dw, die)) != NULL) {
+ elist_t **elastp = &tdp->t_emem;
+
+ do {
+ elist_t *el;
+
+ if (die_tag(dw, mem) != DW_TAG_enumerator) {
+ /* Nested type declaration */
+ die_create_one(dw, mem);
+ continue;
+ }
+
+ el = xcalloc(sizeof (elist_t));
+ el->el_name = die_name(dw, mem);
+
+ if (die_signed(dw, mem, DW_AT_const_value, &sval, 0)) {
+ el->el_number = sval;
+ } else if (die_unsigned(dw, mem, DW_AT_const_value,
+ &uval, 0)) {
+ el->el_number = uval;
+ } else {
+ terminate("die %llu: enum %llu: member without "
+ "value\n", off, die_off(dw, mem));
+ }
+
+ debug(3, "die %llu: enum %llu: created %s = %d\n", off,
+ die_off(dw, mem), el->el_name, el->el_number);
+
+ *elastp = el;
+ elastp = &el->el_next;
+
+ } while ((mem = die_sibling(dw, mem)) != NULL);
+
+ hash_add(dw->dw_enumhash, tdp);
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ if (tdp->t_name != NULL) {
+ iidesc_t *ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = II_SOU;
+ ii->ii_name = xstrdup(tdp->t_name);
+ ii->ii_dtype = tdp;
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+ }
+ }
+}
+
+static int
+die_enum_match(void *arg1, void *arg2)
+{
+ tdesc_t *tdp = arg1, **fullp = arg2;
+
+ if (tdp->t_emem != NULL) {
+ *fullp = tdp;
+ return (-1); /* stop the iteration */
+ }
+
+ return (0);
+}
+
+/*ARGSUSED1*/
+static int
+die_enum_resolve(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private)
+{
+ dwarf_t *dw = private;
+ tdesc_t *full = NULL;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ (void) hash_find_iter(dw->dw_enumhash, tdp, die_enum_match, &full);
+
+ /*
+ * The answer to this one won't change from iteration to iteration,
+ * so don't even try.
+ */
+ if (full == NULL) {
+ terminate("tdp %u: enum %s has no members\n", tdp->t_id,
+ tdesc_name(tdp));
+ }
+
+ debug(3, "tdp %u: enum %s redirected to %u\n", tdp->t_id,
+ tdesc_name(tdp), full->t_id);
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ return (1);
+}
+
+static int
+die_fwd_map(void *arg1, void *arg2)
+{
+ tdesc_t *fwd = arg1, *sou = arg2;
+
+ debug(3, "tdp %u: mapped forward %s to sou %u\n", fwd->t_id,
+ tdesc_name(fwd), sou->t_id);
+ fwd->t_tdesc = sou;
+
+ return (0);
+}
+
+/*
+ * Structures and unions will never be resolved during the first pass, as we
+ * won't be able to fully determine the member sizes. The second pass, which
+ * have access to sizing information, will be able to complete the resolution.
+ */
+static void
+die_sou_create(dwarf_t *dw, Dwarf_Die str, Dwarf_Off off, tdesc_t *tdp,
+ int type, const char *typename)
+{
+ Dwarf_Unsigned sz, bitsz, bitoff;
+ Dwarf_Die mem;
+ mlist_t *ml, **mlastp;
+ iidesc_t *ii;
+
+ tdp->t_type = (die_isdecl(dw, str) ? FORWARD : type);
+
+ debug(3, "die %llu: creating %s %s\n", off,
+ (tdp->t_type == FORWARD ? "forward decl" : typename),
+ tdesc_name(tdp));
+
+ if (tdp->t_type == FORWARD) {
+ hash_add(dw->dw_fwdhash, tdp);
+ return;
+ }
+
+ (void) hash_find_iter(dw->dw_fwdhash, tdp, die_fwd_map, tdp);
+
+ (void) die_unsigned(dw, str, DW_AT_byte_size, &sz, DW_ATTR_REQ);
+ tdp->t_size = sz;
+
+ /*
+ * GCC allows empty SOUs as an extension.
+ */
+ if ((mem = die_child(dw, str)) == NULL) {
+ goto out;
+ }
+
+ mlastp = &tdp->t_members;
+
+ do {
+ Dwarf_Off memoff = die_off(dw, mem);
+ Dwarf_Half tag = die_tag(dw, mem);
+ Dwarf_Unsigned mloff;
+
+ if (tag != DW_TAG_member) {
+ /* Nested type declaration */
+ die_create_one(dw, mem);
+ continue;
+ }
+
+ debug(3, "die %llu: mem %llu: creating member\n", off, memoff);
+
+ ml = xcalloc(sizeof (mlist_t));
+
+ /*
+ * This could be a GCC anon struct/union member, so we'll allow
+ * an empty name, even though nothing can really handle them
+ * properly. Note that some versions of GCC miss out debug
+ * info for anon structs, though recent versions are fixed (gcc
+ * bug 11816).
+ */
+ if ((ml->ml_name = die_name(dw, mem)) == NULL)
+ ml->ml_name = NULL;
+
+ ml->ml_type = die_lookup_pass1(dw, mem, DW_AT_type);
+
+ if (die_mem_offset(dw, mem, DW_AT_data_member_location,
+ &mloff, 0)) {
+ debug(3, "die %llu: got mloff %llx\n", off,
+ (u_longlong_t)mloff);
+ ml->ml_offset = mloff * 8;
+ }
+
+ if (die_unsigned(dw, mem, DW_AT_bit_size, &bitsz, 0))
+ ml->ml_size = bitsz;
+ else
+ ml->ml_size = tdesc_bitsize(ml->ml_type);
+
+ if (die_unsigned(dw, mem, DW_AT_bit_offset, &bitoff, 0)) {
+#if BYTE_ORDER == _BIG_ENDIAN
+ ml->ml_offset += bitoff;
+#else
+ ml->ml_offset += tdesc_bitsize(ml->ml_type) - bitoff -
+ ml->ml_size;
+#endif
+ }
+
+ debug(3, "die %llu: mem %llu: created \"%s\" (off %u sz %u)\n",
+ off, memoff, ml->ml_name, ml->ml_offset, ml->ml_size);
+
+ *mlastp = ml;
+ mlastp = &ml->ml_next;
+ } while ((mem = die_sibling(dw, mem)) != NULL);
+
+ /*
+ * GCC will attempt to eliminate unused types, thus decreasing the
+ * size of the emitted dwarf. That is, if you declare a foo_t in your
+ * header, include said header in your source file, and neglect to
+ * actually use (directly or indirectly) the foo_t in the source file,
+ * the foo_t won't make it into the emitted DWARF. So, at least, goes
+ * the theory.
+ *
+ * Occasionally, it'll emit the DW_TAG_structure_type for the foo_t,
+ * and then neglect to emit the members. Strangely, the loner struct
+ * tag will always be followed by a proper nested declaration of
+ * something else. This is clearly a bug, but we're not going to have
+ * time to get it fixed before this goo goes back, so we'll have to work
+ * around it. If we see a no-membered struct with a nested declaration
+ * (i.e. die_child of the struct tag won't be null), we'll ignore it.
+ * Being paranoid, we won't simply remove it from the hash. Instead,
+ * we'll decline to create an iidesc for it, thus ensuring that this
+ * type won't make it into the output file. To be safe, we'll also
+ * change the name.
+ */
+ if (tdp->t_members == NULL) {
+ const char *old = tdesc_name(tdp);
+ size_t newsz = 7 + strlen(old) + 1;
+ char *new = xmalloc(newsz);
+ (void) snprintf(new, newsz, "orphan %s", old);
+
+ debug(3, "die %llu: worked around %s %s\n", off, typename, old);
+
+ if (tdp->t_name != NULL)
+ free(tdp->t_name);
+ tdp->t_name = new;
+ return;
+ }
+
+out:
+ if (tdp->t_name != NULL) {
+ ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = II_SOU;
+ ii->ii_name = xstrdup(tdp->t_name);
+ ii->ii_dtype = tdp;
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+ }
+}
+
+static void
+die_struct_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_sou_create(dw, die, off, tdp, STRUCT, "struct");
+}
+
+static void
+die_union_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_sou_create(dw, die, off, tdp, UNION, "union");
+}
+
+/*ARGSUSED1*/
+static int
+die_sou_resolve(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private)
+{
+ dwarf_t *dw = private;
+ mlist_t *ml;
+ tdesc_t *mt;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ debug(3, "resolving sou %s\n", tdesc_name(tdp));
+
+ for (ml = tdp->t_members; ml != NULL; ml = ml->ml_next) {
+ if (ml->ml_size == 0) {
+ mt = tdesc_basetype(ml->ml_type);
+
+ if ((ml->ml_size = tdesc_bitsize(mt)) != 0)
+ continue;
+
+ /*
+ * For empty members, or GCC/C99 flexible array
+ * members, a size of 0 is correct.
+ */
+ if (mt->t_members == NULL)
+ continue;
+ if (mt->t_type == ARRAY && mt->t_ardef->ad_nelems == 0)
+ continue;
+
+ dw->dw_nunres++;
+ return (1);
+ }
+
+ if ((mt = tdesc_basetype(ml->ml_type)) == NULL) {
+ dw->dw_nunres++;
+ return (1);
+ }
+
+ if (ml->ml_size != 0 && mt->t_type == INTRINSIC &&
+ mt->t_intr->intr_nbits != ml->ml_size) {
+ /*
+ * This member is a bitfield, and needs to reference
+ * an intrinsic type with the same width. If the
+ * currently-referenced type isn't of the same width,
+ * we'll copy it, adjusting the width of the copy to
+ * the size we'd like.
+ */
+ debug(3, "tdp %u: creating bitfield for %d bits\n",
+ tdp->t_id, ml->ml_size);
+
+ ml->ml_type = tdesc_intr_clone(dw, mt, ml->ml_size);
+ }
+ }
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ return (1);
+}
+
+/*ARGSUSED1*/
+static int
+die_sou_failed(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private __unused)
+{
+ const char *typename = (tdp->t_type == STRUCT ? "struct" : "union");
+ mlist_t *ml;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ for (ml = tdp->t_members; ml != NULL; ml = ml->ml_next) {
+ if (ml->ml_size == 0) {
+ fprintf(stderr, "%s %d <%x>: failed to size member \"%s\" "
+ "of type %s (%d <%x>)\n", typename, tdp->t_id,
+ tdp->t_id,
+ ml->ml_name, tdesc_name(ml->ml_type),
+ ml->ml_type->t_id, ml->ml_type->t_id);
+ }
+ }
+
+ return (1);
+}
+
+static void
+die_funcptr_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Half tag;
+ Dwarf_Die arg;
+ fndef_t *fn;
+ int i;
+
+ debug(3, "die %llu <%llx>: creating function pointer\n", off, off);
+
+ /*
+ * We'll begin by processing any type definition nodes that may be
+ * lurking underneath this one.
+ */
+ for (arg = die_child(dw, die); arg != NULL;
+ arg = die_sibling(dw, arg)) {
+ if ((tag = die_tag(dw, arg)) != DW_TAG_formal_parameter &&
+ tag != DW_TAG_unspecified_parameters) {
+ /* Nested type declaration */
+ die_create_one(dw, arg);
+ }
+ }
+
+ if (die_isdecl(dw, die)) {
+ /*
+ * This is a prototype. We don't add prototypes to the
+ * tree, so we're going to drop the tdesc. Unfortunately,
+ * it has already been added to the tree. Nobody will reference
+ * it, though, and it will be leaked.
+ */
+ return;
+ }
+
+ fn = xcalloc(sizeof (fndef_t));
+
+ tdp->t_type = FUNCTION;
+
+ if ((attr = die_attr(dw, die, DW_AT_type, 0)) != NULL) {
+ fn->fn_ret = die_lookup_pass1(dw, die, DW_AT_type);
+ } else {
+ fn->fn_ret = tdesc_intr_void(dw);
+ }
+
+ /*
+ * Count the arguments to the function, then read them in.
+ */
+ for (fn->fn_nargs = 0, arg = die_child(dw, die); arg != NULL;
+ arg = die_sibling(dw, arg)) {
+ if ((tag = die_tag(dw, arg)) == DW_TAG_formal_parameter)
+ fn->fn_nargs++;
+ else if (tag == DW_TAG_unspecified_parameters &&
+ fn->fn_nargs > 0)
+ fn->fn_vargs = 1;
+ }
+
+ if (fn->fn_nargs != 0) {
+ debug(3, "die %llu: adding %d argument%s\n", off, fn->fn_nargs,
+ (fn->fn_nargs > 1 ? "s" : ""));
+
+ fn->fn_args = xcalloc(sizeof (tdesc_t *) * fn->fn_nargs);
+ for (i = 0, arg = die_child(dw, die);
+ arg != NULL && i < (int) fn->fn_nargs;
+ arg = die_sibling(dw, arg)) {
+ if (die_tag(dw, arg) != DW_TAG_formal_parameter)
+ continue;
+
+ fn->fn_args[i++] = die_lookup_pass1(dw, arg,
+ DW_AT_type);
+ }
+ }
+
+ tdp->t_fndef = fn;
+ tdp->t_flags |= TDESC_F_RESOLVED;
+}
+
+/*
+ * GCC and DevPro use different names for the base types. While the terms are
+ * the same, they are arranged in a different order. Some terms, such as int,
+ * are implied in one, and explicitly named in the other. Given a base type
+ * as input, this routine will return a common name, along with an intr_t
+ * that reflects said name.
+ */
+static intr_t *
+die_base_name_parse(const char *name, char **newp)
+{
+ char buf[100];
+ char const *base;
+ char *c;
+ int nlong = 0, nshort = 0, nchar = 0, nint = 0;
+ int sign = 1;
+ char fmt = '\0';
+ intr_t *intr;
+
+ if (strlen(name) > sizeof (buf) - 1)
+ terminate("base type name \"%s\" is too long\n", name);
+
+ strncpy(buf, name, sizeof (buf));
+
+ for (c = strtok(buf, " "); c != NULL; c = strtok(NULL, " ")) {
+ if (strcmp(c, "signed") == 0)
+ sign = 1;
+ else if (strcmp(c, "unsigned") == 0)
+ sign = 0;
+ else if (strcmp(c, "long") == 0)
+ nlong++;
+ else if (strcmp(c, "char") == 0) {
+ nchar++;
+ fmt = 'c';
+ } else if (strcmp(c, "short") == 0)
+ nshort++;
+ else if (strcmp(c, "int") == 0)
+ nint++;
+ else {
+ /*
+ * If we don't recognize any of the tokens, we'll tell
+ * the caller to fall back to the dwarf-provided
+ * encoding information.
+ */
+ return (NULL);
+ }
+ }
+
+ if (nchar > 1 || nshort > 1 || nint > 1 || nlong > 2)
+ return (NULL);
+
+ if (nchar > 0) {
+ if (nlong > 0 || nshort > 0 || nint > 0)
+ return (NULL);
+
+ base = "char";
+
+ } else if (nshort > 0) {
+ if (nlong > 0)
+ return (NULL);
+
+ base = "short";
+
+ } else if (nlong > 0) {
+ base = "long";
+
+ } else {
+ base = "int";
+ }
+
+ intr = xcalloc(sizeof (intr_t));
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = sign;
+ intr->intr_iformat = fmt;
+
+ snprintf(buf, sizeof (buf), "%s%s%s",
+ (sign ? "" : "unsigned "),
+ (nlong > 1 ? "long " : ""),
+ base);
+
+ *newp = xstrdup(buf);
+ return (intr);
+}
+
+typedef struct fp_size_map {
+ size_t fsm_typesz[2]; /* size of {32,64} type */
+ uint_t fsm_enc[3]; /* CTF_FP_* for {bare,cplx,imagry} type */
+} fp_size_map_t;
+
+static const fp_size_map_t fp_encodings[] = {
+ { { 4, 4 }, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } },
+ { { 8, 8 }, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } },
+#ifdef __sparc
+ { { 16, 16 }, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } },
+#else
+ { { 12, 16 }, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } },
+#endif
+ { { 0, 0 }, { 0, 0, 0 } }
+};
+
+static uint_t
+die_base_type2enc(dwarf_t *dw, Dwarf_Off off, Dwarf_Signed enc, size_t sz)
+{
+ const fp_size_map_t *map = fp_encodings;
+ uint_t szidx = dw->dw_ptrsz == sizeof (uint64_t);
+ uint_t mult = 1, col = 0;
+
+ if (enc == DW_ATE_complex_float) {
+ mult = 2;
+ col = 1;
+ } else if (enc == DW_ATE_imaginary_float
+#if defined(sun)
+ || enc == DW_ATE_SUN_imaginary_float
+#endif
+ )
+ col = 2;
+
+ while (map->fsm_typesz[szidx] != 0) {
+ if (map->fsm_typesz[szidx] * mult == sz)
+ return (map->fsm_enc[col]);
+ map++;
+ }
+
+ terminate("die %llu: unrecognized real type size %u\n", off, sz);
+ /*NOTREACHED*/
+ return (0);
+}
+
+static intr_t *
+die_base_from_dwarf(dwarf_t *dw, Dwarf_Die base, Dwarf_Off off, size_t sz)
+{
+ intr_t *intr = xcalloc(sizeof (intr_t));
+ Dwarf_Signed enc;
+
+ (void) die_signed(dw, base, DW_AT_encoding, &enc, DW_ATTR_REQ);
+
+ switch (enc) {
+ case DW_ATE_unsigned:
+ case DW_ATE_address:
+ intr->intr_type = INTR_INT;
+ break;
+ case DW_ATE_unsigned_char:
+ intr->intr_type = INTR_INT;
+ intr->intr_iformat = 'c';
+ break;
+ case DW_ATE_signed:
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = 1;
+ break;
+ case DW_ATE_signed_char:
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = 1;
+ intr->intr_iformat = 'c';
+ break;
+ case DW_ATE_boolean:
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = 1;
+ intr->intr_iformat = 'b';
+ break;
+ case DW_ATE_float:
+ case DW_ATE_complex_float:
+ case DW_ATE_imaginary_float:
+#if defined(sun)
+ case DW_ATE_SUN_imaginary_float:
+ case DW_ATE_SUN_interval_float:
+#endif
+ intr->intr_type = INTR_REAL;
+ intr->intr_signed = 1;
+ intr->intr_fformat = die_base_type2enc(dw, off, enc, sz);
+ break;
+ default:
+ terminate("die %llu: unknown base type encoding 0x%llx\n",
+ off, enc);
+ }
+
+ return (intr);
+}
+
+static void
+die_base_create(dwarf_t *dw, Dwarf_Die base, Dwarf_Off off, tdesc_t *tdp)
+{
+ Dwarf_Unsigned sz;
+ intr_t *intr;
+ char *new;
+
+ debug(3, "die %llu: creating base type\n", off);
+
+ /*
+ * The compilers have their own clever (internally inconsistent) ideas
+ * as to what base types should look like. Some times gcc will, for
+ * example, use DW_ATE_signed_char for char. Other times, however, it
+ * will use DW_ATE_signed. Needless to say, this causes some problems
+ * down the road, particularly with merging. We do, however, use the
+ * DWARF idea of type sizes, as this allows us to avoid caring about
+ * the data model.
+ */
+ (void) die_unsigned(dw, base, DW_AT_byte_size, &sz, DW_ATTR_REQ);
+
+ if (tdp->t_name == NULL)
+ terminate("die %llu: base type without name\n", off);
+
+ /* XXX make a name parser for float too */
+ if ((intr = die_base_name_parse(tdp->t_name, &new)) != NULL) {
+ /* Found it. We'll use the parsed version */
+ debug(3, "die %llu: name \"%s\" remapped to \"%s\"\n", off,
+ tdesc_name(tdp), new);
+
+ free(tdp->t_name);
+ tdp->t_name = new;
+ } else {
+ /*
+ * We didn't recognize the type, so we'll create an intr_t
+ * based on the DWARF data.
+ */
+ debug(3, "die %llu: using dwarf data for base \"%s\"\n", off,
+ tdesc_name(tdp));
+
+ intr = die_base_from_dwarf(dw, base, off, sz);
+ }
+
+ intr->intr_nbits = sz * 8;
+
+ tdp->t_type = INTRINSIC;
+ tdp->t_intr = intr;
+ tdp->t_size = sz;
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+}
+
+static void
+die_through_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp,
+ int type, const char *typename)
+{
+ Dwarf_Attribute attr;
+
+ debug(3, "die %llu <%llx>: creating %s type %d\n", off, off, typename, type);
+
+ tdp->t_type = type;
+
+ if ((attr = die_attr(dw, die, DW_AT_type, 0)) != NULL) {
+ tdp->t_tdesc = die_lookup_pass1(dw, die, DW_AT_type);
+ } else {
+ tdp->t_tdesc = tdesc_intr_void(dw);
+ }
+
+ if (type == POINTER)
+ tdp->t_size = dw->dw_ptrsz;
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ if (type == TYPEDEF) {
+ iidesc_t *ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = II_TYPE;
+ ii->ii_name = xstrdup(tdp->t_name);
+ ii->ii_dtype = tdp;
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+ }
+}
+
+static void
+die_typedef_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, TYPEDEF, "typedef");
+}
+
+static void
+die_const_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, CONST, "const");
+}
+
+static void
+die_pointer_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, POINTER, "pointer");
+}
+
+static void
+die_restrict_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, RESTRICT, "restrict");
+}
+
+static void
+die_volatile_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, VOLATILE, "volatile");
+}
+
+/*ARGSUSED3*/
+static void
+die_function_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp __unused)
+{
+ Dwarf_Die arg;
+ Dwarf_Half tag;
+ iidesc_t *ii;
+ char *name;
+
+ debug(3, "die %llu <%llx>: creating function definition\n", off, off);
+
+ /*
+ * We'll begin by processing any type definition nodes that may be
+ * lurking underneath this one.
+ */
+ for (arg = die_child(dw, die); arg != NULL;
+ arg = die_sibling(dw, arg)) {
+ if ((tag = die_tag(dw, arg)) != DW_TAG_formal_parameter &&
+ tag != DW_TAG_variable) {
+ /* Nested type declaration */
+ die_create_one(dw, arg);
+ }
+ }
+
+ if (die_isdecl(dw, die) || (name = die_name(dw, die)) == NULL) {
+ /*
+ * We process neither prototypes nor subprograms without
+ * names.
+ */
+ return;
+ }
+
+ ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = die_isglobal(dw, die) ? II_GFUN : II_SFUN;
+ ii->ii_name = name;
+ if (ii->ii_type == II_SFUN)
+ ii->ii_owner = xstrdup(dw->dw_cuname);
+
+ debug(3, "die %llu: function %s is %s\n", off, ii->ii_name,
+ (ii->ii_type == II_GFUN ? "global" : "static"));
+
+ if (die_attr(dw, die, DW_AT_type, 0) != NULL)
+ ii->ii_dtype = die_lookup_pass1(dw, die, DW_AT_type);
+ else
+ ii->ii_dtype = tdesc_intr_void(dw);
+
+ for (arg = die_child(dw, die); arg != NULL;
+ arg = die_sibling(dw, arg)) {
+ char *name1;
+
+ debug(3, "die %llu: looking at sub member at %llu\n",
+ off, die_off(dw, die));
+
+ if (die_tag(dw, arg) != DW_TAG_formal_parameter)
+ continue;
+
+ if ((name1 = die_name(dw, arg)) == NULL) {
+ terminate("die %llu: func arg %d has no name\n",
+ off, ii->ii_nargs + 1);
+ }
+
+ if (strcmp(name1, "...") == 0) {
+ free(name1);
+ ii->ii_vargs = 1;
+ continue;
+ }
+
+ ii->ii_nargs++;
+ }
+
+ if (ii->ii_nargs > 0) {
+ int i;
+
+ debug(3, "die %llu: function has %d argument%s\n", off,
+ ii->ii_nargs, (ii->ii_nargs == 1 ? "" : "s"));
+
+ ii->ii_args = xcalloc(sizeof (tdesc_t) * ii->ii_nargs);
+
+ for (arg = die_child(dw, die), i = 0;
+ arg != NULL && i < ii->ii_nargs;
+ arg = die_sibling(dw, arg)) {
+ if (die_tag(dw, arg) != DW_TAG_formal_parameter)
+ continue;
+
+ ii->ii_args[i++] = die_lookup_pass1(dw, arg,
+ DW_AT_type);
+ }
+ }
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+}
+
+/*ARGSUSED3*/
+static void
+die_variable_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp __unused)
+{
+ iidesc_t *ii;
+ char *name;
+
+ debug(3, "die %llu: creating object definition\n", off);
+
+ if (die_isdecl(dw, die) || (name = die_name(dw, die)) == NULL)
+ return; /* skip prototypes and nameless objects */
+
+ ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = die_isglobal(dw, die) ? II_GVAR : II_SVAR;
+ ii->ii_name = name;
+ ii->ii_dtype = die_lookup_pass1(dw, die, DW_AT_type);
+ if (ii->ii_type == II_SVAR)
+ ii->ii_owner = xstrdup(dw->dw_cuname);
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+}
+
+/*ARGSUSED2*/
+static int
+die_fwd_resolve(tdesc_t *fwd, tdesc_t **fwdp, void *private __unused)
+{
+ if (fwd->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ if (fwd->t_tdesc != NULL) {
+ debug(3, "tdp %u: unforwarded %s\n", fwd->t_id,
+ tdesc_name(fwd));
+ *fwdp = fwd->t_tdesc;
+ }
+
+ fwd->t_flags |= TDESC_F_RESOLVED;
+
+ return (1);
+}
+
+/*ARGSUSED*/
+static void
+die_lexblk_descend(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off __unused, tdesc_t *tdp __unused)
+{
+ Dwarf_Die child = die_child(dw, die);
+
+ if (child != NULL)
+ die_create(dw, child);
+}
+
+/*
+ * Used to map the die to a routine which can parse it, using the tag to do the
+ * mapping. While the processing of most tags entails the creation of a tdesc,
+ * there are a few which don't - primarily those which result in the creation of
+ * iidescs which refer to existing tdescs.
+ */
+
+#define DW_F_NOTDP 0x1 /* Don't create a tdesc for the creator */
+
+typedef struct die_creator {
+ Dwarf_Half dc_tag;
+ uint16_t dc_flags;
+ void (*dc_create)(dwarf_t *, Dwarf_Die, Dwarf_Off, tdesc_t *);
+} die_creator_t;
+
+static const die_creator_t die_creators[] = {
+ { DW_TAG_array_type, 0, die_array_create },
+ { DW_TAG_enumeration_type, 0, die_enum_create },
+ { DW_TAG_lexical_block, DW_F_NOTDP, die_lexblk_descend },
+ { DW_TAG_pointer_type, 0, die_pointer_create },
+ { DW_TAG_structure_type, 0, die_struct_create },
+ { DW_TAG_subroutine_type, 0, die_funcptr_create },
+ { DW_TAG_typedef, 0, die_typedef_create },
+ { DW_TAG_union_type, 0, die_union_create },
+ { DW_TAG_base_type, 0, die_base_create },
+ { DW_TAG_const_type, 0, die_const_create },
+ { DW_TAG_subprogram, DW_F_NOTDP, die_function_create },
+ { DW_TAG_variable, DW_F_NOTDP, die_variable_create },
+ { DW_TAG_volatile_type, 0, die_volatile_create },
+ { DW_TAG_restrict_type, 0, die_restrict_create },
+ { 0, 0, NULL }
+};
+
+static const die_creator_t *
+die_tag2ctor(Dwarf_Half tag)
+{
+ const die_creator_t *dc;
+
+ for (dc = die_creators; dc->dc_create != NULL; dc++) {
+ if (dc->dc_tag == tag)
+ return (dc);
+ }
+
+ return (NULL);
+}
+
+static void
+die_create_one(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Off off = die_off(dw, die);
+ const die_creator_t *dc;
+ Dwarf_Half tag;
+ tdesc_t *tdp;
+
+ debug(3, "die %llu <%llx>: create_one\n", off, off);
+
+ if (off > dw->dw_maxoff) {
+ terminate("illegal die offset %llu (max %llu)\n", off,
+ dw->dw_maxoff);
+ }
+
+ tag = die_tag(dw, die);
+
+ if ((dc = die_tag2ctor(tag)) == NULL) {
+ debug(2, "die %llu: ignoring tag type %x\n", off, tag);
+ return;
+ }
+
+ if ((tdp = tdesc_lookup(dw, off)) == NULL &&
+ !(dc->dc_flags & DW_F_NOTDP)) {
+ tdp = xcalloc(sizeof (tdesc_t));
+ tdp->t_id = off;
+ tdesc_add(dw, tdp);
+ }
+
+ if (tdp != NULL)
+ tdp->t_name = die_name(dw, die);
+
+ dc->dc_create(dw, die, off, tdp);
+}
+
+static void
+die_create(dwarf_t *dw, Dwarf_Die die)
+{
+ do {
+ die_create_one(dw, die);
+ } while ((die = die_sibling(dw, die)) != NULL);
+}
+
+static tdtrav_cb_f die_resolvers[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ die_array_resolve, /* array */
+ NULL, /* function */
+ die_sou_resolve, /* struct */
+ die_sou_resolve, /* union */
+ die_enum_resolve, /* enum */
+ die_fwd_resolve, /* forward */
+ NULL, /* typedef */
+ NULL, /* typedef unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL, /* restrict */
+};
+
+static tdtrav_cb_f die_fail_reporters[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ die_array_failed, /* array */
+ NULL, /* function */
+ die_sou_failed, /* struct */
+ die_sou_failed, /* union */
+ NULL, /* enum */
+ NULL, /* forward */
+ NULL, /* typedef */
+ NULL, /* typedef unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL, /* restrict */
+};
+
+static void
+die_resolve(dwarf_t *dw)
+{
+ int last = -1;
+ int pass = 0;
+
+ do {
+ pass++;
+ dw->dw_nunres = 0;
+
+ (void) iitraverse_hash(dw->dw_td->td_iihash,
+ &dw->dw_td->td_curvgen, NULL, NULL, die_resolvers, dw);
+
+ debug(3, "resolve: pass %d, %u left\n", pass, dw->dw_nunres);
+
+ if ((int) dw->dw_nunres == last) {
+ fprintf(stderr, "%s: failed to resolve the following "
+ "types:\n", progname);
+
+ (void) iitraverse_hash(dw->dw_td->td_iihash,
+ &dw->dw_td->td_curvgen, NULL, NULL,
+ die_fail_reporters, dw);
+
+ terminate("failed to resolve types\n");
+ }
+
+ last = dw->dw_nunres;
+
+ } while (dw->dw_nunres != 0);
+}
+
+/*ARGSUSED*/
+int
+dw_read(tdata_t *td, Elf *elf, char *filename __unused)
+{
+ Dwarf_Unsigned abboff, hdrlen, nxthdr;
+ Dwarf_Half vers, addrsz;
+ Dwarf_Die cu = 0;
+ Dwarf_Die child = 0;
+ dwarf_t dw;
+ char *prod = NULL;
+ int rc;
+
+ bzero(&dw, sizeof (dwarf_t));
+ dw.dw_td = td;
+ dw.dw_ptrsz = elf_ptrsz(elf);
+ dw.dw_mfgtid_last = TID_MFGTID_BASE;
+ dw.dw_tidhash = hash_new(TDESC_HASH_BUCKETS, tdesc_idhash, tdesc_idcmp);
+ dw.dw_fwdhash = hash_new(TDESC_HASH_BUCKETS, tdesc_namehash,
+ tdesc_namecmp);
+ dw.dw_enumhash = hash_new(TDESC_HASH_BUCKETS, tdesc_namehash,
+ tdesc_namecmp);
+
+ if ((rc = dwarf_elf_init(elf, DW_DLC_READ, &dw.dw_dw,
+ &dw.dw_err)) == DW_DLV_NO_ENTRY) {
+ errno = ENOENT;
+ return (-1);
+ } else if (rc != DW_DLV_OK) {
+ if (dwarf_errno(&dw.dw_err) == DW_DLE_DEBUG_INFO_NULL) {
+ /*
+ * There's no type data in the DWARF section, but
+ * libdwarf is too clever to handle that properly.
+ */
+ return (0);
+ }
+
+ terminate("failed to initialize DWARF: %s\n",
+ dwarf_errmsg(&dw.dw_err));
+ }
+
+ if ((rc = dwarf_next_cu_header(dw.dw_dw, &hdrlen, &vers, &abboff,
+ &addrsz, &nxthdr, &dw.dw_err)) != DW_DLV_OK)
+ terminate("rc = %d %s\n", rc, dwarf_errmsg(&dw.dw_err));
+
+ if ((cu = die_sibling(&dw, NULL)) == NULL)
+ terminate("file does not contain dwarf type data "
+ "(try compiling with -g)\n");
+
+ dw.dw_maxoff = nxthdr - 1;
+
+ if (dw.dw_maxoff > TID_FILEMAX)
+ terminate("file contains too many types\n");
+
+ debug(1, "DWARF version: %d\n", vers);
+ if (vers != DWARF_VERSION) {
+ terminate("file contains incompatible version %d DWARF code "
+ "(version 2 required)\n", vers);
+ }
+
+ if (die_string(&dw, cu, DW_AT_producer, &prod, 0)) {
+ debug(1, "DWARF emitter: %s\n", prod);
+ free(prod);
+ }
+
+ if ((dw.dw_cuname = die_name(&dw, cu)) != NULL) {
+ char *base = xstrdup(basename(dw.dw_cuname));
+ free(dw.dw_cuname);
+ dw.dw_cuname = base;
+
+ debug(1, "CU name: %s\n", dw.dw_cuname);
+ }
+
+ if ((child = die_child(&dw, cu)) != NULL)
+ die_create(&dw, child);
+
+ if ((rc = dwarf_next_cu_header(dw.dw_dw, &hdrlen, &vers, &abboff,
+ &addrsz, &nxthdr, &dw.dw_err)) != DW_DLV_NO_ENTRY)
+ terminate("multiple compilation units not supported\n");
+
+ (void) dwarf_finish(&dw.dw_dw, &dw.dw_err);
+
+ die_resolve(&dw);
+
+ cvt_fixups(td, dw.dw_ptrsz);
+
+ /* leak the dwarf_t */
+
+ return (0);
+}
diff --git a/tools/ctf/cvt/fifo.c b/tools/ctf/cvt/fifo.c
new file mode 100644
index 000000000000..fb9a11fccf56
--- /dev/null
+++ b/tools/ctf/cvt/fifo.c
@@ -0,0 +1,153 @@
+/*
+ * 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 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating a FIFO queue
+ */
+
+#include <stdlib.h>
+
+#include "fifo.h"
+#include "memory.h"
+
+typedef struct fifonode {
+ void *fn_data;
+ struct fifonode *fn_next;
+} fifonode_t;
+
+struct fifo {
+ fifonode_t *f_head;
+ fifonode_t *f_tail;
+};
+
+fifo_t *
+fifo_new(void)
+{
+ fifo_t *f;
+
+ f = xcalloc(sizeof (fifo_t));
+
+ return (f);
+}
+
+/* Add to the end of the fifo */
+void
+fifo_add(fifo_t *f, void *data)
+{
+ fifonode_t *fn = xmalloc(sizeof (fifonode_t));
+
+ fn->fn_data = data;
+ fn->fn_next = NULL;
+
+ if (f->f_tail == NULL)
+ f->f_head = f->f_tail = fn;
+ else {
+ f->f_tail->fn_next = fn;
+ f->f_tail = fn;
+ }
+}
+
+/* Remove from the front of the fifo */
+void *
+fifo_remove(fifo_t *f)
+{
+ fifonode_t *fn;
+ void *data;
+
+ if ((fn = f->f_head) == NULL)
+ return (NULL);
+
+ data = fn->fn_data;
+ if ((f->f_head = fn->fn_next) == NULL)
+ f->f_tail = NULL;
+
+ free(fn);
+
+ return (data);
+}
+
+/*ARGSUSED*/
+static void
+fifo_nullfree(void *arg)
+{
+ /* this function intentionally left blank */
+}
+
+/* Free an entire fifo */
+void
+fifo_free(fifo_t *f, void (*freefn)(void *))
+{
+ fifonode_t *fn = f->f_head;
+ fifonode_t *tmp;
+
+ if (freefn == NULL)
+ freefn = fifo_nullfree;
+
+ while (fn) {
+ (*freefn)(fn->fn_data);
+
+ tmp = fn;
+ fn = fn->fn_next;
+ free(tmp);
+ }
+
+ free(f);
+}
+
+int
+fifo_len(fifo_t *f)
+{
+ fifonode_t *fn;
+ int i;
+
+ for (i = 0, fn = f->f_head; fn; fn = fn->fn_next, i++);
+
+ return (i);
+}
+
+int
+fifo_empty(fifo_t *f)
+{
+ return (f->f_head == NULL);
+}
+
+int
+fifo_iter(fifo_t *f, int (*iter)(void *data, void *arg), void *arg)
+{
+ fifonode_t *fn;
+ int rc;
+ int ret = 0;
+
+ for (fn = f->f_head; fn; fn = fn->fn_next) {
+ if ((rc = iter(fn->fn_data, arg)) < 0)
+ return (-1);
+ ret += rc;
+ }
+
+ return (ret);
+}
diff --git a/tools/ctf/cvt/fifo.h b/tools/ctf/cvt/fifo.h
new file mode 100644
index 000000000000..e4b948abc418
--- /dev/null
+++ b/tools/ctf/cvt/fifo.h
@@ -0,0 +1,54 @@
+/*
+ * 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 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _FIFO_H
+#define _FIFO_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating a FIFO queue
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct fifo fifo_t;
+
+extern fifo_t *fifo_new(void);
+extern void fifo_add(fifo_t *, void *);
+extern void *fifo_remove(fifo_t *);
+extern void fifo_free(fifo_t *, void (*)(void *));
+extern int fifo_len(fifo_t *);
+extern int fifo_empty(fifo_t *);
+extern int fifo_iter(fifo_t *, int (*)(void *, void *), void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FIFO_H */
diff --git a/tools/ctf/cvt/fixup_tdescs.c b/tools/ctf/cvt/fixup_tdescs.c
new file mode 100644
index 000000000000..b239a62dc53b
--- /dev/null
+++ b/tools/ctf/cvt/fixup_tdescs.c
@@ -0,0 +1,279 @@
+/*
+ * 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"
+
+/*
+ * Workarounds for stabs generation bugs in the compiler and general needed
+ * fixups.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+
+#include "ctf_headers.h"
+#include "ctftools.h"
+#include "hash.h"
+#include "memory.h"
+
+/*
+ * Due to 4432619, the 6.1 compiler will sometimes incorrectly generate pointer
+ * stabs. Given a struct foo, and a corresponding typedef struct foo foo_t.
+ * In some cases, when faced with a pointer to a foo_t, the compiler will
+ * sometimes generate a stab that describes a pointer to a struct foo.
+ * Regardless of correctness, this breaks merges, as it occurs inconsistently
+ * by file. The following two routines know how to recognize and repair foo_t *
+ * and foo_t ** bugs in a specific set of cases. There is no general way to
+ * solve this problem without a fix to the compiler. In general, cases should
+ * only be added to these routines to fix merging problems in genunix.
+ */
+static void
+fix_ptrptr_to_struct(tdata_t *td)
+{
+ const char *strs[2] = { "as", "fdbuffer" };
+ const char *mems[2] = { "a_objectdir", "fd_shadow" };
+ const char *acts[2] = { "vnode", "page" };
+ const char *tgts[2] = { "vnode_t", "page_t" };
+ tdesc_t *str;
+ tdesc_t *act, *tgt;
+ tdesc_t *p1, *p2;
+ mlist_t *ml;
+ int i;
+
+ for (i = 0; i < (int) (sizeof (strs) / sizeof (strs[0])); i++) {
+ if (!(str = lookupname(strs[i])) || str->t_type != STRUCT)
+ continue;
+
+ for (ml = str->t_members; ml; ml = ml->ml_next) {
+ if (streq(ml->ml_name, mems[i]))
+ break;
+ }
+ if (!ml)
+ continue;
+
+ if (ml->ml_type->t_type != POINTER || ml->ml_type->t_name ||
+ ml->ml_type->t_tdesc->t_type != POINTER ||
+ ml->ml_type->t_tdesc->t_name)
+ continue;
+
+ act = ml->ml_type->t_tdesc->t_tdesc;
+ if (act->t_type != STRUCT || !streq(act->t_name, acts[i]))
+ continue;
+
+ if (!(tgt = lookupname(tgts[i])) || tgt->t_type != TYPEDEF)
+ continue;
+
+ /* We have an instance of the bug */
+ p2 = xcalloc(sizeof (*p2));
+ p2->t_type = POINTER;
+ p2->t_id = td->td_nextid++;
+ p2->t_tdesc = tgt;
+
+ p1 = xcalloc(sizeof (*p1));
+ p1->t_type = POINTER;
+ p1->t_id = td->td_nextid++;
+ p1->t_tdesc = p2;
+
+ ml->ml_type = p1;
+
+ debug(3, "Fixed %s->%s => ptrptr struct %s bug\n",
+ strs[i], mems[i], acts[i]);
+ }
+}
+
+static void
+fix_ptr_to_struct(tdata_t *td)
+{
+ const char *strs[2] = { "vmem", "id_space" };
+ const char *mems[2] = { NULL, "is_vmem" };
+ tdesc_t *ptr = NULL;
+ tdesc_t *str, *vmt;
+ mlist_t *ml;
+ int i;
+
+ if ((vmt = lookupname("vmem_t")) == NULL || vmt->t_type != TYPEDEF)
+ return;
+
+ for (i = 0; i < (int) (sizeof (strs) / sizeof (strs[0])); i++) {
+ if (!(str = lookupname(strs[i])) || str->t_type != STRUCT)
+ continue;
+
+ for (ml = str->t_members; ml; ml = ml->ml_next) {
+ if (mems[i] && !streq(ml->ml_name, mems[i]))
+ continue;
+
+ if (ml->ml_type->t_type != POINTER ||
+ ml->ml_type->t_name ||
+ (ml->ml_type->t_tdesc->t_type != STRUCT &&
+ ml->ml_type->t_tdesc->t_type != FORWARD) ||
+ !streq(ml->ml_type->t_tdesc->t_name, "vmem"))
+ continue;
+
+ debug(3, "Fixed %s->%s => ptr struct vmem bug\n",
+ strs[i], ml->ml_name);
+
+ if (!ptr) {
+ ptr = xcalloc(sizeof (*ptr));
+ ptr->t_type = POINTER;
+ ptr->t_id = td->td_nextid++;
+ ptr->t_tdesc = vmt;
+ }
+
+ ml->ml_type = ptr;
+ }
+ }
+}
+
+/*
+ * Fix stabs generation bugs. These routines must be run before the
+ * post-conversion merge
+ */
+void
+cvt_fixstabs(tdata_t *td)
+{
+ fix_ptrptr_to_struct(td);
+ fix_ptr_to_struct(td);
+}
+
+struct match {
+ tdesc_t *m_ret;
+ const char *m_name;
+};
+
+static int
+matching_iidesc(void *arg1, void *arg2)
+{
+ iidesc_t *iidesc = arg1;
+ struct match *match = arg2;
+ if (!streq(iidesc->ii_name, match->m_name))
+ return (0);
+
+ if (iidesc->ii_type != II_TYPE && iidesc->ii_type != II_SOU)
+ return (0);
+
+ match->m_ret = iidesc->ii_dtype;
+ return (-1);
+}
+
+static tdesc_t *
+lookup_tdesc(tdata_t *td, char const *name)
+{
+ struct match match = { NULL, name };
+ iter_iidescs_by_name(td, name, matching_iidesc, &match);
+ return (match.m_ret);
+}
+
+/*
+ * The cpu structure grows, with the addition of a machcpu member, if
+ * _MACHDEP is defined. This means that, for example, the cpu structure
+ * in unix is different from the cpu structure in genunix. As one might
+ * expect, this causes merges to fail. Since everyone indirectly contains
+ * a pointer to a CPU structure, the failed merges can cause massive amounts
+ * of duplication. In the case of unix uniquifying against genunix, upwards
+ * of 50% of the structures were unmerged due to this problem. We fix this
+ * by adding a cpu_m member. If machcpu hasn't been defined in our module,
+ * we make a forward node for it.
+ */
+static void
+fix_small_cpu_struct(tdata_t *td, size_t ptrsize)
+{
+ tdesc_t *cput, *cpu;
+ tdesc_t *machcpu;
+ mlist_t *ml, *lml;
+ mlist_t *cpum;
+ int foundcpucyc = 0;
+
+ /*
+ * We're going to take the circuitous route finding the cpu structure,
+ * because we want to make sure that we find the right one. It would
+ * be nice if we could verify the header name too. DWARF might not
+ * have the cpu_t, so we let this pass.
+ */
+ if ((cput = lookup_tdesc(td, "cpu_t")) != NULL) {
+ if (cput->t_type != TYPEDEF)
+ return;
+ cpu = cput->t_tdesc;
+ } else {
+ cpu = lookup_tdesc(td, "cpu");
+ }
+
+ if (cpu == NULL)
+ return;
+
+ if (!streq(cpu->t_name, "cpu") || cpu->t_type != STRUCT)
+ return;
+
+ for (ml = cpu->t_members, lml = NULL; ml;
+ lml = ml, ml = ml->ml_next) {
+ if (strcmp(ml->ml_name, "cpu_cyclic") == 0)
+ foundcpucyc = 1;
+ }
+
+ if (foundcpucyc == 0 || lml == NULL ||
+ strcmp(lml->ml_name, "cpu_m") == 0)
+ return;
+
+ /*
+ * We need to derive the right offset for the fake cpu_m member. To do
+ * that, we require a special unused member to be the last member
+ * before the 'cpu_m', that we encode knowledge of here. ABI alignment
+ * on all platforms is such that we only need to add a pointer-size
+ * number of bits to get the right offset for cpu_m. This would most
+ * likely break if gcc's -malign-double were ever used, but that option
+ * breaks the ABI anyway.
+ */
+ if (!streq(lml->ml_name, "cpu_m_pad") &&
+ getenv("CTFCONVERT_PERMISSIVE") == NULL) {
+ terminate("last cpu_t member before cpu_m is %s; "
+ "it must be cpu_m_pad.\n", lml->ml_name);
+ }
+
+ if ((machcpu = lookup_tdesc(td, "machcpu")) == NULL) {
+ machcpu = xcalloc(sizeof (*machcpu));
+ machcpu->t_name = xstrdup("machcpu");
+ machcpu->t_id = td->td_nextid++;
+ machcpu->t_type = FORWARD;
+ } else if (machcpu->t_type != STRUCT) {
+ return;
+ }
+
+ debug(3, "Adding cpu_m machcpu %s to cpu struct\n",
+ (machcpu->t_type == FORWARD ? "forward" : "struct"));
+
+ cpum = xmalloc(sizeof (*cpum));
+ cpum->ml_offset = lml->ml_offset + (ptrsize * NBBY);
+ cpum->ml_size = 0;
+ cpum->ml_name = xstrdup("cpu_m");
+ cpum->ml_type = machcpu;
+ cpum->ml_next = NULL;
+
+ lml->ml_next = cpum;
+}
+
+void
+cvt_fixups(tdata_t *td, size_t ptrsize)
+{
+ fix_small_cpu_struct(td, ptrsize);
+}
diff --git a/tools/ctf/cvt/hash.c b/tools/ctf/cvt/hash.c
new file mode 100644
index 000000000000..36ed45a30f0a
--- /dev/null
+++ b/tools/ctf/cvt/hash.c
@@ -0,0 +1,291 @@
+/*
+ * 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"
+
+/*
+ * Routines for manipulating hash tables
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "list.h"
+
+struct hash {
+ int h_nbuckets;
+ list_t **h_buckets;
+
+ int (*h_hashfn)(int, void *);
+ int (*h_cmp)(void *, void *);
+};
+
+struct hash_data {
+ hash_t *hd_hash;
+ int (*hd_fun)(void *, void *);
+ void *hd_key;
+ void *hd_private;
+
+ void *hd_ret;
+};
+
+static int
+hash_def_hash(int nbuckets, void *arg)
+{
+ uintptr_t data = (uintptr_t) arg;
+ return (data % nbuckets);
+}
+
+static int
+hash_def_cmp(void *d1, void *d2)
+{
+ return (d1 != d2);
+}
+
+
+int
+hash_name(int nbuckets, const char *name)
+{
+ const char *c;
+ ulong_t g;
+ int h = 0;
+
+ for (c = name; *c; c++) {
+ h = (h << 4) + *c;
+ if ((g = (h & 0xf0000000)) != 0) {
+ h ^= (g >> 24);
+ h ^= g;
+ }
+ }
+
+ return (h % nbuckets);
+}
+
+hash_t *
+hash_new(int nbuckets, int (*hashfn)(int, void *), int (*cmp)(void *, void *))
+{
+ hash_t *hash;
+
+ hash = xmalloc(sizeof (hash_t));
+ hash->h_buckets = xcalloc(sizeof (list_t *) * nbuckets);
+ hash->h_nbuckets = nbuckets;
+ hash->h_hashfn = hashfn ? hashfn : hash_def_hash;
+ hash->h_cmp = cmp ? cmp : hash_def_cmp;
+
+ return (hash);
+}
+
+void
+hash_add(hash_t *hash, void *key)
+{
+ int bucket = hash->h_hashfn(hash->h_nbuckets, key);
+
+ list_add(&hash->h_buckets[bucket], key);
+}
+
+static int
+hash_add_cb(void *node, void *private)
+{
+ hash_add((hash_t *)private, node);
+ return (0);
+}
+
+void
+hash_merge(hash_t *to, hash_t *from)
+{
+ (void) hash_iter(from, hash_add_cb, to);
+}
+
+static int
+hash_remove_cb(void *key1, void *key2, void *arg)
+{
+ hash_t *hash = arg;
+ return (hash->h_cmp(key1, key2));
+}
+
+void
+hash_remove(hash_t *hash, void *key)
+{
+ int bucket = hash->h_hashfn(hash->h_nbuckets, key);
+
+ (void) list_remove(&hash->h_buckets[bucket], key,
+ hash_remove_cb, hash);
+}
+
+int
+hash_match(hash_t *hash, void *key, int (*fun)(void *, void *),
+ void *private)
+{
+ int bucket = hash->h_hashfn(hash->h_nbuckets, key);
+
+ return (list_iter(hash->h_buckets[bucket], fun, private) < 0);
+}
+
+static int
+hash_find_list_cb(void *node, void *arg)
+{
+ struct hash_data *hd = arg;
+ int cbrc;
+ int rc = 0;
+
+ if (hd->hd_hash->h_cmp(hd->hd_key, node) == 0) {
+ if ((cbrc = hd->hd_fun(node, hd->hd_private)) < 0)
+ return (cbrc);
+ rc += cbrc;
+ }
+
+ return (rc);
+}
+
+int
+hash_find_iter(hash_t *hash, void *key, int (*fun)(void *, void *),
+ void *private)
+{
+ int bucket = hash->h_hashfn(hash->h_nbuckets, key);
+ struct hash_data hd;
+
+ hd.hd_hash = hash;
+ hd.hd_fun = fun;
+ hd.hd_key = key;
+ hd.hd_private = private;
+
+ return (list_iter(hash->h_buckets[bucket], hash_find_list_cb,
+ &hd));
+}
+
+/* stop on first match */
+static int
+hash_find_first_cb(void *node, void *arg)
+{
+ struct hash_data *hd = arg;
+ if (hd->hd_hash->h_cmp(hd->hd_key, node) == 0) {
+ hd->hd_ret = node;
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+hash_find(hash_t *hash, void *key, void **value)
+{
+ int ret;
+ struct hash_data hd;
+
+ hd.hd_hash = hash;
+ hd.hd_fun = hash_find_first_cb;
+ hd.hd_key = key;
+
+ ret = hash_match(hash, key, hash_find_first_cb, &hd);
+ if (ret && value)
+ *value = hd.hd_ret;
+
+ return (ret);
+}
+
+int
+hash_iter(hash_t *hash, int (*fun)(void *, void *), void *private)
+{
+ int cumrc = 0;
+ int cbrc;
+ int i;
+
+ for (i = 0; i < hash->h_nbuckets; i++) {
+ if (hash->h_buckets[i] != NULL) {
+ if ((cbrc = list_iter(hash->h_buckets[i], fun,
+ private)) < 0)
+ return (cbrc);
+ cumrc += cbrc;
+ }
+ }
+
+ return (cumrc);
+}
+
+int
+hash_count(hash_t *hash)
+{
+ int num, i;
+
+ for (num = 0, i = 0; i < hash->h_nbuckets; i++)
+ num += list_count(hash->h_buckets[i]);
+
+ return (num);
+}
+
+void
+hash_free(hash_t *hash, void (*datafree)(void *, void *), void *private)
+{
+ int i;
+
+ if (hash == NULL)
+ return;
+
+ for (i = 0; i < hash->h_nbuckets; i++)
+ list_free(hash->h_buckets[i], datafree, private);
+ free(hash->h_buckets);
+ free(hash);
+}
+
+void
+hash_stats(hash_t *hash, int verbose)
+{
+ int min = list_count(hash->h_buckets[0]);
+ int minidx = 0;
+ int max = min;
+ int maxidx = 0;
+ int tot = min;
+ int count;
+ int i;
+
+ if (min && verbose)
+ printf("%3d: %d\n", 0, min);
+ for (i = 1; i < hash->h_nbuckets; i++) {
+ count = list_count(hash->h_buckets[i]);
+ if (min > count) {
+ min = count;
+ minidx = i;
+ }
+ if (max < count) {
+ max = count;
+ maxidx = i;
+ }
+ if (count && verbose)
+ printf("%3d: %d\n", i, count);
+ tot += count;
+ }
+
+ printf("Hash statistics:\n");
+ printf(" Buckets: %d\n", hash->h_nbuckets);
+ printf(" Items : %d\n", tot);
+ printf(" Min/Max: %d in #%d, %d in #%d\n", min, minidx, max, maxidx);
+ printf(" Average: %5.2f\n", (float)tot / (float)hash->h_nbuckets);
+}
diff --git a/tools/ctf/cvt/hash.h b/tools/ctf/cvt/hash.h
new file mode 100644
index 000000000000..94d93d4e5e8a
--- /dev/null
+++ b/tools/ctf/cvt/hash.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef _HASH_H
+#define _HASH_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating hash tables
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct hash hash_t;
+
+hash_t *hash_new(int, int (*)(int, void *), int (*)(void *, void *));
+void hash_add(hash_t *, void *);
+void hash_merge(hash_t *, hash_t *);
+void hash_remove(hash_t *, void *);
+int hash_find(hash_t *, void *, void **);
+int hash_find_iter(hash_t *, void *, int (*)(void *, void *), void *);
+int hash_iter(hash_t *, int (*)(void *, void *), void *);
+int hash_match(hash_t *, void *, int (*)(void *, void *), void *);
+int hash_count(hash_t *);
+int hash_name(int, const char *);
+void hash_stats(hash_t *, int);
+void hash_free(hash_t *, void (*)(void *, void *), void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HASH_H */
diff --git a/tools/ctf/cvt/iidesc.c b/tools/ctf/cvt/iidesc.c
new file mode 100644
index 000000000000..fc1cefc9321b
--- /dev/null
+++ b/tools/ctf/cvt/iidesc.c
@@ -0,0 +1,197 @@
+/*
+ * 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 manipulating iidesc_t structures
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "ctftools.h"
+#include "memory.h"
+#include "list.h"
+#include "hash.h"
+
+typedef struct iidesc_find {
+ iidesc_t *iif_tgt;
+ iidesc_t *iif_ret;
+} iidesc_find_t;
+
+iidesc_t *
+iidesc_new(char *name)
+{
+ iidesc_t *ii;
+
+ ii = xcalloc(sizeof (iidesc_t));
+ if (name)
+ ii->ii_name = xstrdup(name);
+
+ return (ii);
+}
+
+int
+iidesc_hash(int nbuckets, void *arg)
+{
+ iidesc_t *ii = arg;
+ int h = 0;
+
+ if (ii->ii_name)
+ return (hash_name(nbuckets, ii->ii_name));
+
+ return (h);
+}
+
+static int
+iidesc_cmp(void *arg1, void *arg2)
+{
+ iidesc_t *src = arg1;
+ iidesc_find_t *find = arg2;
+ iidesc_t *tgt = find->iif_tgt;
+
+ if (src->ii_type != tgt->ii_type ||
+ !streq(src->ii_name, tgt->ii_name))
+ return (0);
+
+ find->iif_ret = src;
+
+ return (-1);
+}
+
+void
+iidesc_add(hash_t *hash, iidesc_t *new)
+{
+ iidesc_find_t find;
+
+ find.iif_tgt = new;
+ find.iif_ret = NULL;
+
+ (void) hash_match(hash, new, iidesc_cmp, &find);
+
+ if (find.iif_ret != NULL) {
+ iidesc_t *old = find.iif_ret;
+ iidesc_t tmp;
+ /* replacing existing one */
+ bcopy(old, &tmp, sizeof (tmp));
+ bcopy(new, old, sizeof (*old));
+ bcopy(&tmp, new, sizeof (*new));
+
+ iidesc_free(new, NULL);
+ return;
+ }
+
+ hash_add(hash, new);
+}
+
+void
+iter_iidescs_by_name(tdata_t *td, char const *name,
+ int (*func)(void *, void *), void *data)
+{
+ iidesc_t tmpdesc;
+ bzero(&tmpdesc, sizeof(tmpdesc));
+ tmpdesc.ii_name = xstrdup(name);
+ (void) hash_match(td->td_iihash, &tmpdesc, func, data);
+ free(tmpdesc.ii_name);
+}
+
+iidesc_t *
+iidesc_dup(iidesc_t *src)
+{
+ iidesc_t *tgt;
+
+ tgt = xmalloc(sizeof (iidesc_t));
+ bcopy(src, tgt, sizeof (iidesc_t));
+
+ tgt->ii_name = src->ii_name ? xstrdup(src->ii_name) : NULL;
+ tgt->ii_owner = src->ii_owner ? xstrdup(src->ii_owner) : NULL;
+
+ if (tgt->ii_nargs) {
+ tgt->ii_args = xmalloc(sizeof (tdesc_t *) * tgt->ii_nargs);
+ bcopy(src->ii_args, tgt->ii_args,
+ sizeof (tdesc_t *) * tgt->ii_nargs);
+ }
+
+ return (tgt);
+}
+
+iidesc_t *
+iidesc_dup_rename(iidesc_t *src, char const *name, char const *owner)
+{
+ iidesc_t *tgt = iidesc_dup(src);
+ free(tgt->ii_name);
+ free(tgt->ii_owner);
+
+ tgt->ii_name = name ? xstrdup(name) : NULL;
+ tgt->ii_owner = owner ? xstrdup(owner) : NULL;
+
+ return (tgt);
+}
+
+/*ARGSUSED*/
+void
+iidesc_free(void *arg, void *private __unused)
+{
+ iidesc_t *idp = arg;
+ if (idp->ii_name)
+ free(idp->ii_name);
+ if (idp->ii_nargs)
+ free(idp->ii_args);
+ if (idp->ii_owner)
+ free(idp->ii_owner);
+ free(idp);
+}
+
+int
+iidesc_dump(iidesc_t *ii)
+{
+ printf("type: %d name %s\n", ii->ii_type,
+ (ii->ii_name ? ii->ii_name : "(anon)"));
+
+ return (0);
+}
+
+int
+iidesc_count_type(void *data, void *private)
+{
+ iidesc_t *ii = data;
+ iitype_t match = (iitype_t)private;
+
+ return (ii->ii_type == match);
+}
+
+void
+iidesc_stats(hash_t *ii)
+{
+ printf("GFun: %5d SFun: %5d GVar: %5d SVar: %5d T %5d SOU: %5d\n",
+ hash_iter(ii, iidesc_count_type, (void *)II_GFUN),
+ hash_iter(ii, iidesc_count_type, (void *)II_SFUN),
+ hash_iter(ii, iidesc_count_type, (void *)II_GVAR),
+ hash_iter(ii, iidesc_count_type, (void *)II_SVAR),
+ hash_iter(ii, iidesc_count_type, (void *)II_TYPE),
+ hash_iter(ii, iidesc_count_type, (void *)II_SOU));
+}
diff --git a/tools/ctf/cvt/input.c b/tools/ctf/cvt/input.c
new file mode 100644
index 000000000000..67ebde702298
--- /dev/null
+++ b/tools/ctf/cvt/input.c
@@ -0,0 +1,419 @@
+/*
+ * 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 retrieving CTF data from a .SUNW_ctf ELF section
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <gelf.h>
+#include <strings.h>
+#include <sys/types.h>
+
+#include "ctftools.h"
+#include "memory.h"
+#include "symbol.h"
+
+typedef int read_cb_f(tdata_t *, char *, void *);
+
+/*
+ * Return the source types that the object was generated from.
+ */
+source_types_t
+built_source_types(Elf *elf, char const *file)
+{
+ source_types_t types = SOURCE_NONE;
+ symit_data_t *si;
+
+ if ((si = symit_new(elf, file)) == NULL)
+ return (SOURCE_NONE);
+
+ while (symit_next(si, STT_FILE) != NULL) {
+ char *name = symit_name(si);
+ size_t len = strlen(name);
+ if (len < 2 || name[len - 2] != '.') {
+ types |= SOURCE_UNKNOWN;
+ continue;
+ }
+
+ switch (name[len - 1]) {
+ case 'c':
+ types |= SOURCE_C;
+ break;
+ case 'h':
+ /* ignore */
+ break;
+ case 's':
+ case 'S':
+ types |= SOURCE_S;
+ break;
+ default:
+ types |= SOURCE_UNKNOWN;
+ }
+ }
+
+ symit_free(si);
+ return (types);
+}
+
+static int
+read_file(Elf *elf, char *file, char *label, read_cb_f *func, void *arg,
+ int require_ctf)
+{
+ Elf_Scn *ctfscn;
+ Elf_Data *ctfdata = NULL;
+ symit_data_t *si = NULL;
+ int ctfscnidx;
+ tdata_t *td;
+
+ if ((ctfscnidx = findelfsecidx(elf, file, ".SUNW_ctf")) < 0) {
+ if (require_ctf &&
+ (built_source_types(elf, file) & SOURCE_C)) {
+ terminate("Input file %s was partially built from "
+ "C sources, but no CTF data was present\n", file);
+ }
+ return (0);
+ }
+
+ if ((ctfscn = elf_getscn(elf, ctfscnidx)) == NULL ||
+ (ctfdata = elf_getdata(ctfscn, NULL)) == NULL)
+ elfterminate(file, "Cannot read CTF section");
+
+ /* Reconstruction of type tree */
+ if ((si = symit_new(elf, file)) == NULL) {
+ warning("%s has no symbol table - skipping", file);
+ return (0);
+ }
+
+ td = ctf_load(file, ctfdata->d_buf, ctfdata->d_size, si, label);
+ tdata_build_hashes(td);
+
+ symit_free(si);
+
+ if (td != NULL) {
+ if (func(td, file, arg) < 0)
+ return (-1);
+ else
+ return (1);
+ }
+ return (0);
+}
+
+static int
+read_archive(int fd, Elf *elf, char *file, char *label, read_cb_f *func,
+ void *arg, int require_ctf)
+{
+ Elf *melf;
+ Elf_Cmd cmd = ELF_C_READ;
+ Elf_Arhdr *arh;
+ int secnum = 1, found = 0;
+
+ while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
+ int rc = 0;
+
+ if ((arh = elf_getarhdr(melf)) == NULL) {
+ elfterminate(file, "Can't get archive header for "
+ "member %d", secnum);
+ }
+
+ /* skip special sections - their names begin with "/" */
+ if (*arh->ar_name != '/') {
+ size_t memlen = strlen(file) + 1 +
+ strlen(arh->ar_name) + 1 + 1;
+ char *memname = xmalloc(memlen);
+
+ snprintf(memname, memlen, "%s(%s)", file, arh->ar_name);
+
+ switch (elf_kind(melf)) {
+ case ELF_K_AR:
+ rc = read_archive(fd, melf, memname, label,
+ func, arg, require_ctf);
+ break;
+ case ELF_K_ELF:
+ rc = read_file(melf, memname, label,
+ func, arg, require_ctf);
+ break;
+ default:
+ terminate("%s: Unknown elf kind %d\n",
+ memname, elf_kind(melf));
+ }
+
+ free(memname);
+ }
+
+ cmd = elf_next(melf);
+ (void) elf_end(melf);
+ secnum++;
+
+ if (rc < 0)
+ return (rc);
+ else
+ found += rc;
+ }
+
+ return (found);
+}
+
+static int
+read_ctf_common(char *file, char *label, read_cb_f *func, void *arg,
+ int require_ctf)
+{
+ Elf *elf;
+ int found = 0;
+ int fd;
+
+ debug(3, "Reading %s (label %s)\n", file, (label ? label : "NONE"));
+
+ (void) elf_version(EV_CURRENT);
+
+ if ((fd = open(file, O_RDONLY)) < 0)
+ terminate("%s: Cannot open for reading", file);
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
+ elfterminate(file, "Cannot read");
+
+ switch (elf_kind(elf)) {
+ case ELF_K_AR:
+ found = read_archive(fd, elf, file, label,
+ func, arg, require_ctf);
+ break;
+
+ case ELF_K_ELF:
+ found = read_file(elf, file, label,
+ func, arg, require_ctf);
+ break;
+
+ default:
+ terminate("%s: Unknown elf kind %d\n", file, elf_kind(elf));
+ }
+
+ (void) elf_end(elf);
+ (void) close(fd);
+
+ return (found);
+}
+
+/*ARGSUSED*/
+int
+read_ctf_save_cb(tdata_t *td, char *name __unused, void *retp)
+{
+ tdata_t **tdp = retp;
+
+ *tdp = td;
+
+ return (1);
+}
+
+int
+read_ctf(char **files, int n, char *label, read_cb_f *func, void *private,
+ int require_ctf)
+{
+ int found;
+ int i, rc;
+
+ for (i = 0, found = 0; i < n; i++) {
+ if ((rc = read_ctf_common(files[i], label, func,
+ private, require_ctf)) < 0)
+ return (rc);
+ found += rc;
+ }
+
+ return (found);
+}
+
+static int
+count_archive(int fd, Elf *elf, char *file)
+{
+ Elf *melf;
+ Elf_Cmd cmd = ELF_C_READ;
+ Elf_Arhdr *arh;
+ int nfiles = 0, err = 0;
+
+ while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
+ if ((arh = elf_getarhdr(melf)) == NULL) {
+ warning("Can't process input archive %s\n",
+ file);
+ err++;
+ }
+
+ if (*arh->ar_name != '/')
+ nfiles++;
+
+ cmd = elf_next(melf);
+ (void) elf_end(melf);
+ }
+
+ if (err > 0)
+ return (-1);
+
+ return (nfiles);
+}
+
+int
+count_files(char **files, int n)
+{
+ int nfiles = 0, err = 0;
+ Elf *elf;
+ int fd, rc, i;
+
+ (void) elf_version(EV_CURRENT);
+
+ for (i = 0; i < n; i++) {
+ char *file = files[i];
+
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ warning("Can't read input file %s", file);
+ err++;
+ continue;
+ }
+
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
+ warning("Can't open input file %s: %s\n", file,
+ elf_errmsg(-1));
+ err++;
+ (void) close(fd);
+ continue;
+ }
+
+ switch (elf_kind(elf)) {
+ case ELF_K_AR:
+ if ((rc = count_archive(fd, elf, file)) < 0)
+ err++;
+ else
+ nfiles += rc;
+ break;
+ case ELF_K_ELF:
+ nfiles++;
+ break;
+ default:
+ warning("Input file %s is corrupt\n", file);
+ err++;
+ }
+
+ (void) elf_end(elf);
+ (void) close(fd);
+ }
+
+ if (err > 0)
+ return (-1);
+
+ debug(2, "Found %d files in %d input files\n", nfiles, n);
+
+ return (nfiles);
+}
+
+struct symit_data {
+ GElf_Shdr si_shdr;
+ Elf_Data *si_symd;
+ Elf_Data *si_strd;
+ GElf_Sym si_cursym;
+ char *si_curname;
+ char *si_curfile;
+ int si_nument;
+ int si_next;
+};
+
+symit_data_t *
+symit_new(Elf *elf, const char *file)
+{
+ symit_data_t *si;
+ Elf_Scn *scn;
+ int symtabidx;
+
+ if ((symtabidx = findelfsecidx(elf, file, ".symtab")) < 0)
+ return (NULL);
+
+ si = xcalloc(sizeof (symit_data_t));
+
+ if ((scn = elf_getscn(elf, symtabidx)) == NULL ||
+ gelf_getshdr(scn, &si->si_shdr) == NULL ||
+ (si->si_symd = elf_getdata(scn, NULL)) == NULL)
+ elfterminate(file, "Cannot read .symtab");
+
+ if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL ||
+ (si->si_strd = elf_getdata(scn, NULL)) == NULL)
+ elfterminate(file, "Cannot read strings for .symtab");
+
+ si->si_nument = si->si_shdr.sh_size / si->si_shdr.sh_entsize;
+
+ return (si);
+}
+
+void
+symit_free(symit_data_t *si)
+{
+ free(si);
+}
+
+void
+symit_reset(symit_data_t *si)
+{
+ si->si_next = 0;
+}
+
+char *
+symit_curfile(symit_data_t *si)
+{
+ return (si->si_curfile);
+}
+
+GElf_Sym *
+symit_next(symit_data_t *si, int type)
+{
+ GElf_Sym sym;
+ int check_sym = (type == STT_OBJECT || type == STT_FUNC);
+
+ for (; si->si_next < si->si_nument; si->si_next++) {
+ gelf_getsym(si->si_symd, si->si_next, &si->si_cursym);
+ gelf_getsym(si->si_symd, si->si_next, &sym);
+ si->si_curname = (caddr_t)si->si_strd->d_buf + sym.st_name;
+
+ if (GELF_ST_TYPE(sym.st_info) == STT_FILE)
+ si->si_curfile = si->si_curname;
+
+ if (GELF_ST_TYPE(sym.st_info) != type ||
+ sym.st_shndx == SHN_UNDEF)
+ continue;
+
+ if (check_sym && ignore_symbol(&sym, si->si_curname))
+ continue;
+
+ si->si_next++;
+
+ return (&si->si_cursym);
+ }
+
+ return (NULL);
+}
+
+char *
+symit_name(symit_data_t *si)
+{
+ return (si->si_curname);
+}
diff --git a/tools/ctf/cvt/merge.c b/tools/ctf/cvt/merge.c
new file mode 100644
index 000000000000..2ef37d460b36
--- /dev/null
+++ b/tools/ctf/cvt/merge.c
@@ -0,0 +1,1143 @@
+/*
+ * 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"
+
+/*
+ * This file contains routines that merge one tdata_t tree, called the child,
+ * into another, called the parent. Note that these names are used mainly for
+ * convenience and to represent the direction of the merge. They are not meant
+ * to imply any relationship between the tdata_t graphs prior to the merge.
+ *
+ * tdata_t structures contain two main elements - a hash of iidesc_t nodes, and
+ * a directed graph of tdesc_t nodes, pointed to by the iidesc_t nodes. Simply
+ * put, we merge the tdesc_t graphs, followed by the iidesc_t nodes, and then we
+ * clean up loose ends.
+ *
+ * The algorithm is as follows:
+ *
+ * 1. Mapping iidesc_t nodes
+ *
+ * For each child iidesc_t node, we first try to map its tdesc_t subgraph
+ * against the tdesc_t graph in the parent. For each node in the child subgraph
+ * that exists in the parent, a mapping between the two (between their type IDs)
+ * is established. For the child nodes that cannot be mapped onto existing
+ * parent nodes, a mapping is established between the child node ID and a
+ * newly-allocated ID that the node will use when it is re-created in the
+ * parent. These unmappable nodes are added to the md_tdtba (tdesc_t To Be
+ * Added) hash, which tracks nodes that need to be created in the parent.
+ *
+ * If all of the nodes in the subgraph for an iidesc_t in the child can be
+ * mapped to existing nodes in the parent, then we can try to map the child
+ * iidesc_t onto an iidesc_t in the parent. If we cannot find an equivalent
+ * iidesc_t, or if we were not able to completely map the tdesc_t subgraph(s),
+ * then we add this iidesc_t to the md_iitba (iidesc_t To Be Added) list. This
+ * list tracks iidesc_t nodes that are to be created in the parent.
+ *
+ * While visiting the tdesc_t nodes, we may discover a forward declaration (a
+ * FORWARD tdesc_t) in the parent that is resolved in the child. That is, there
+ * may be a structure or union definition in the child with the same name as the
+ * forward declaration in the parent. If we find such a node, we record an
+ * association in the md_fdida (Forward => Definition ID Association) list
+ * between the parent ID of the forward declaration and the ID that the
+ * definition will use when re-created in the parent.
+ *
+ * 2. Creating new tdesc_t nodes (the md_tdtba hash)
+ *
+ * We have now attempted to map all tdesc_t nodes from the child into the
+ * parent, and have, in md_tdtba, a hash of all tdesc_t nodes that need to be
+ * created (or, as we so wittily call it, conjured) in the parent. We iterate
+ * through this hash, creating the indicated tdesc_t nodes. For a given tdesc_t
+ * node, conjuring requires two steps - the copying of the common tdesc_t data
+ * (name, type, etc) from the child node, and the creation of links from the
+ * newly-created node to the parent equivalents of other tdesc_t nodes pointed
+ * to by node being conjured. Note that in some cases, the targets of these
+ * links will be on the md_tdtba hash themselves, and may not have been created
+ * yet. As such, we can't establish the links from these new nodes into the
+ * parent graph. We therefore conjure them with links to nodes in the *child*
+ * graph, and add pointers to the links to be created to the md_tdtbr (tdesc_t
+ * To Be Remapped) hash. For example, a POINTER tdesc_t that could not be
+ * resolved would have its &tdesc_t->t_tdesc added to md_tdtbr.
+ *
+ * 3. Creating new iidesc_t nodes (the md_iitba list)
+ *
+ * When we have completed step 2, all tdesc_t nodes have been created (or
+ * already existed) in the parent. Some of them may have incorrect links (the
+ * members of the md_tdtbr list), but they've all been created. As such, we can
+ * create all of the iidesc_t nodes, as we can attach the tdesc_t subgraph
+ * pointers correctly. We create each node, and attach the pointers to the
+ * appropriate parts of the parent tdesc_t graph.
+ *
+ * 4. Resolving newly-created tdesc_t node links (the md_tdtbr list)
+ *
+ * As in step 3, we rely on the fact that all of the tdesc_t nodes have been
+ * created. Each entry in the md_tdtbr list is a pointer to where a link into
+ * the parent will be established. As saved in the md_tdtbr list, these
+ * pointers point into the child tdesc_t subgraph. We can thus get the target
+ * type ID from the child, look at the ID mapping to determine the desired link
+ * target, and redirect the link accordingly.
+ *
+ * 5. Parent => child forward declaration resolution
+ *
+ * If entries were made in the md_fdida list in step 1, we have forward
+ * declarations in the parent that need to be resolved to their definitions
+ * re-created in step 2 from the child. Using the md_fdida list, we can locate
+ * the definition for the forward declaration, and we can redirect all inbound
+ * edges to the forward declaration node to the actual definition.
+ *
+ * A pox on the house of anyone who changes the algorithm without updating
+ * this comment.
+ */
+
+#include <stdio.h>
+#include <strings.h>
+#include <assert.h>
+#include <pthread.h>
+
+#include "ctf_headers.h"
+#include "ctftools.h"
+#include "list.h"
+#include "alist.h"
+#include "memory.h"
+#include "traverse.h"
+
+typedef struct equiv_data equiv_data_t;
+typedef struct merge_cb_data merge_cb_data_t;
+
+/*
+ * There are two traversals in this file, for equivalency and for tdesc_t
+ * re-creation, that do not fit into the tdtraverse() framework. We have our
+ * own traversal mechanism and ops vector here for those two cases.
+ */
+typedef struct tdesc_ops {
+ const char *name;
+ int (*equiv)(tdesc_t *, tdesc_t *, equiv_data_t *);
+ tdesc_t *(*conjure)(tdesc_t *, int, merge_cb_data_t *);
+} tdesc_ops_t;
+extern tdesc_ops_t tdesc_ops[];
+
+/*
+ * The workhorse structure of tdata_t merging. Holds all lists of nodes to be
+ * processed during various phases of the merge algorithm.
+ */
+struct merge_cb_data {
+ tdata_t *md_parent;
+ tdata_t *md_tgt;
+ alist_t *md_ta; /* Type Association */
+ alist_t *md_fdida; /* Forward -> Definition ID Association */
+ list_t **md_iitba; /* iidesc_t nodes To Be Added to the parent */
+ hash_t *md_tdtba; /* tdesc_t nodes To Be Added to the parent */
+ list_t **md_tdtbr; /* tdesc_t nodes To Be Remapped */
+ int md_flags;
+}; /* merge_cb_data_t */
+
+/*
+ * When we first create a tdata_t from stabs data, we will have duplicate nodes.
+ * Normal merges, however, assume that the child tdata_t is already self-unique,
+ * and for speed reasons do not attempt to self-uniquify. If this flag is set,
+ * the merge algorithm will self-uniquify by avoiding the insertion of
+ * duplicates in the md_tdtdba list.
+ */
+#define MCD_F_SELFUNIQUIFY 0x1
+
+/*
+ * When we merge the CTF data for the modules, we don't want it to contain any
+ * data that can be found in the reference module (usually genunix). If this
+ * flag is set, we're doing a merge between the fully merged tdata_t for this
+ * module and the tdata_t for the reference module, with the data unique to this
+ * module ending up in a third tdata_t. It is this third tdata_t that will end
+ * up in the .SUNW_ctf section for the module.
+ */
+#define MCD_F_REFMERGE 0x2
+
+/*
+ * Mapping of child type IDs to parent type IDs
+ */
+
+static void
+add_mapping(alist_t *ta, tid_t srcid, tid_t tgtid)
+{
+ debug(3, "Adding mapping %u <%x> => %u <%x>\n", srcid, srcid, tgtid, tgtid);
+
+ assert(!alist_find(ta, (void *)(uintptr_t)srcid, NULL));
+ assert(srcid != 0 && tgtid != 0);
+
+ alist_add(ta, (void *)(uintptr_t)srcid, (void *)(uintptr_t)tgtid);
+}
+
+static tid_t
+get_mapping(alist_t *ta, int srcid)
+{
+ void *ltgtid;
+
+ if (alist_find(ta, (void *)(uintptr_t)srcid, (void **)&ltgtid))
+ return ((uintptr_t)ltgtid);
+ else
+ return (0);
+}
+
+/*
+ * Determining equivalence of tdesc_t subgraphs
+ */
+
+struct equiv_data {
+ alist_t *ed_ta;
+ tdesc_t *ed_node;
+ tdesc_t *ed_tgt;
+
+ int ed_clear_mark;
+ int ed_cur_mark;
+ int ed_selfuniquify;
+}; /* equiv_data_t */
+
+static int equiv_node(tdesc_t *, tdesc_t *, equiv_data_t *);
+
+/*ARGSUSED2*/
+static int
+equiv_intrinsic(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed __unused)
+{
+ intr_t *si = stdp->t_intr;
+ intr_t *ti = ttdp->t_intr;
+
+ if (si->intr_type != ti->intr_type ||
+ si->intr_signed != ti->intr_signed ||
+ si->intr_offset != ti->intr_offset ||
+ si->intr_nbits != ti->intr_nbits)
+ return (0);
+
+ if (si->intr_type == INTR_INT &&
+ si->intr_iformat != ti->intr_iformat)
+ return (0);
+ else if (si->intr_type == INTR_REAL &&
+ si->intr_fformat != ti->intr_fformat)
+ return (0);
+
+ return (1);
+}
+
+static int
+equiv_plain(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed)
+{
+ return (equiv_node(stdp->t_tdesc, ttdp->t_tdesc, ed));
+}
+
+static int
+equiv_function(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed)
+{
+ fndef_t *fn1 = stdp->t_fndef, *fn2 = ttdp->t_fndef;
+ int i;
+
+ if (fn1->fn_nargs != fn2->fn_nargs ||
+ fn1->fn_vargs != fn2->fn_vargs)
+ return (0);
+
+ if (!equiv_node(fn1->fn_ret, fn2->fn_ret, ed))
+ return (0);
+
+ for (i = 0; i < (int) fn1->fn_nargs; i++) {
+ if (!equiv_node(fn1->fn_args[i], fn2->fn_args[i], ed))
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+equiv_array(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed)
+{
+ ardef_t *ar1 = stdp->t_ardef, *ar2 = ttdp->t_ardef;
+
+ if (!equiv_node(ar1->ad_contents, ar2->ad_contents, ed) ||
+ !equiv_node(ar1->ad_idxtype, ar2->ad_idxtype, ed))
+ return (0);
+
+ if (ar1->ad_nelems != ar2->ad_nelems)
+ return (0);
+
+ return (1);
+}
+
+static int
+equiv_su(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed)
+{
+ mlist_t *ml1 = stdp->t_members, *ml2 = ttdp->t_members;
+ mlist_t *olm1 = NULL;
+
+ while (ml1 && ml2) {
+ if (ml1->ml_offset != ml2->ml_offset ||
+ strcmp(ml1->ml_name, ml2->ml_name) != 0)
+ return (0);
+
+ /*
+ * Don't do the recursive equivalency checking more than
+ * we have to.
+ */
+ if (olm1 == NULL || olm1->ml_type->t_id != ml1->ml_type->t_id) {
+ if (ml1->ml_size != ml2->ml_size ||
+ !equiv_node(ml1->ml_type, ml2->ml_type, ed))
+ return (0);
+ }
+
+ olm1 = ml1;
+ ml1 = ml1->ml_next;
+ ml2 = ml2->ml_next;
+ }
+
+ if (ml1 || ml2)
+ return (0);
+
+ return (1);
+}
+
+/*ARGSUSED2*/
+static int
+equiv_enum(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed __unused)
+{
+ elist_t *el1 = stdp->t_emem;
+ elist_t *el2 = ttdp->t_emem;
+
+ while (el1 && el2) {
+ if (el1->el_number != el2->el_number ||
+ strcmp(el1->el_name, el2->el_name) != 0)
+ return (0);
+
+ el1 = el1->el_next;
+ el2 = el2->el_next;
+ }
+
+ if (el1 || el2)
+ return (0);
+
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+equiv_assert(tdesc_t *stdp __unused, tdesc_t *ttdp __unused, equiv_data_t *ed __unused)
+{
+ /* foul, evil, and very bad - this is a "shouldn't happen" */
+ assert(1 == 0);
+
+ return (0);
+}
+
+static int
+fwd_equiv(tdesc_t *ctdp, tdesc_t *mtdp)
+{
+ tdesc_t *defn = (ctdp->t_type == FORWARD ? mtdp : ctdp);
+
+ return (defn->t_type == STRUCT || defn->t_type == UNION);
+}
+
+static int
+equiv_node(tdesc_t *ctdp, tdesc_t *mtdp, equiv_data_t *ed)
+{
+ int (*equiv)(tdesc_t *, tdesc_t *, equiv_data_t *);
+ int mapping;
+
+ if (ctdp->t_emark > ed->ed_clear_mark ||
+ mtdp->t_emark > ed->ed_clear_mark)
+ return (ctdp->t_emark == mtdp->t_emark);
+
+ /*
+ * In normal (non-self-uniquify) mode, we don't want to do equivalency
+ * checking on a subgraph that has already been checked. If a mapping
+ * has already been established for a given child node, we can simply
+ * compare the mapping for the child node with the ID of the parent
+ * node. If we are in self-uniquify mode, then we're comparing two
+ * subgraphs within the child graph, and thus need to ignore any
+ * type mappings that have been created, as they are only valid into the
+ * parent.
+ */
+ if ((mapping = get_mapping(ed->ed_ta, ctdp->t_id)) > 0 &&
+ mapping == mtdp->t_id && !ed->ed_selfuniquify)
+ return (1);
+
+ if (!streq(ctdp->t_name, mtdp->t_name))
+ return (0);
+
+ if (ctdp->t_type != mtdp->t_type) {
+ if (ctdp->t_type == FORWARD || mtdp->t_type == FORWARD)
+ return (fwd_equiv(ctdp, mtdp));
+ else
+ return (0);
+ }
+
+ ctdp->t_emark = ed->ed_cur_mark;
+ mtdp->t_emark = ed->ed_cur_mark;
+ ed->ed_cur_mark++;
+
+ if ((equiv = tdesc_ops[ctdp->t_type].equiv) != NULL)
+ return (equiv(ctdp, mtdp, ed));
+
+ return (1);
+}
+
+/*
+ * We perform an equivalency check on two subgraphs by traversing through them
+ * in lockstep. If a given node is equivalent in both the parent and the child,
+ * we mark it in both subgraphs, using the t_emark field, with a monotonically
+ * increasing number. If, in the course of the traversal, we reach a node that
+ * we have visited and numbered during this equivalency check, we have a cycle.
+ * If the previously-visited nodes don't have the same emark, then the edges
+ * that brought us to these nodes are not equivalent, and so the check ends.
+ * If the emarks are the same, the edges are equivalent. We then backtrack and
+ * continue the traversal. If we have exhausted all edges in the subgraph, and
+ * have not found any inequivalent nodes, then the subgraphs are equivalent.
+ */
+static int
+equiv_cb(void *bucket, void *arg)
+{
+ equiv_data_t *ed = arg;
+ tdesc_t *mtdp = bucket;
+ tdesc_t *ctdp = ed->ed_node;
+
+ ed->ed_clear_mark = ed->ed_cur_mark + 1;
+ ed->ed_cur_mark = ed->ed_clear_mark + 1;
+
+ if (equiv_node(ctdp, mtdp, ed)) {
+ debug(3, "equiv_node matched %d <%x> %d <%x>\n",
+ ctdp->t_id, ctdp->t_id, mtdp->t_id, mtdp->t_id);
+ ed->ed_tgt = mtdp;
+ /* matched. stop looking */
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED1*/
+static int
+map_td_tree_pre(tdesc_t *ctdp, tdesc_t **ctdpp __unused, void *private)
+{
+ merge_cb_data_t *mcd = private;
+
+ if (get_mapping(mcd->md_ta, ctdp->t_id) > 0)
+ return (0);
+
+ return (1);
+}
+
+/*ARGSUSED1*/
+static int
+map_td_tree_post(tdesc_t *ctdp, tdesc_t **ctdpp __unused, void *private)
+{
+ merge_cb_data_t *mcd = private;
+ equiv_data_t ed;
+
+ ed.ed_ta = mcd->md_ta;
+ ed.ed_clear_mark = mcd->md_parent->td_curemark;
+ ed.ed_cur_mark = mcd->md_parent->td_curemark + 1;
+ ed.ed_node = ctdp;
+ ed.ed_selfuniquify = 0;
+
+ debug(3, "map_td_tree_post on %d <%x> %s\n", ctdp->t_id, ctdp->t_id,tdesc_name(ctdp));
+
+ if (hash_find_iter(mcd->md_parent->td_layouthash, ctdp,
+ equiv_cb, &ed) < 0) {
+ /* We found an equivalent node */
+ if (ed.ed_tgt->t_type == FORWARD && ctdp->t_type != FORWARD) {
+ int id = mcd->md_tgt->td_nextid++;
+
+ debug(3, "Creating new defn type %d <%x>\n", id, id);
+ add_mapping(mcd->md_ta, ctdp->t_id, id);
+ alist_add(mcd->md_fdida, (void *)(ulong_t)ed.ed_tgt,
+ (void *)(ulong_t)id);
+ hash_add(mcd->md_tdtba, ctdp);
+ } else
+ add_mapping(mcd->md_ta, ctdp->t_id, ed.ed_tgt->t_id);
+
+ } else if (debug_level > 1 && hash_iter(mcd->md_parent->td_idhash,
+ equiv_cb, &ed) < 0) {
+ /*
+ * We didn't find an equivalent node by looking through the
+ * layout hash, but we somehow found it by performing an
+ * exhaustive search through the entire graph. This usually
+ * means that the "name" hash function is broken.
+ */
+ aborterr("Second pass for %d (%s) == %d\n", ctdp->t_id,
+ tdesc_name(ctdp), ed.ed_tgt->t_id);
+ } else {
+ int id = mcd->md_tgt->td_nextid++;
+
+ debug(3, "Creating new type %d <%x>\n", id, id);
+ add_mapping(mcd->md_ta, ctdp->t_id, id);
+ hash_add(mcd->md_tdtba, ctdp);
+ }
+
+ mcd->md_parent->td_curemark = ed.ed_cur_mark + 1;
+
+ return (1);
+}
+
+/*ARGSUSED1*/
+static int
+map_td_tree_self_post(tdesc_t *ctdp, tdesc_t **ctdpp __unused, void *private)
+{
+ merge_cb_data_t *mcd = private;
+ equiv_data_t ed;
+
+ ed.ed_ta = mcd->md_ta;
+ ed.ed_clear_mark = mcd->md_parent->td_curemark;
+ ed.ed_cur_mark = mcd->md_parent->td_curemark + 1;
+ ed.ed_node = ctdp;
+ ed.ed_selfuniquify = 1;
+ ed.ed_tgt = NULL;
+
+ if (hash_find_iter(mcd->md_tdtba, ctdp, equiv_cb, &ed) < 0) {
+ debug(3, "Self check found %d <%x> in %d <%x>\n", ctdp->t_id,
+ ctdp->t_id, ed.ed_tgt->t_id, ed.ed_tgt->t_id);
+ add_mapping(mcd->md_ta, ctdp->t_id,
+ get_mapping(mcd->md_ta, ed.ed_tgt->t_id));
+ } else if (debug_level > 1 && hash_iter(mcd->md_tdtba,
+ equiv_cb, &ed) < 0) {
+ /*
+ * We didn't find an equivalent node using the quick way (going
+ * through the hash normally), but we did find it by iterating
+ * through the entire hash. This usually means that the hash
+ * function is broken.
+ */
+ aborterr("Self-unique second pass for %d <%x> (%s) == %d <%x>\n",
+ ctdp->t_id, ctdp->t_id, tdesc_name(ctdp), ed.ed_tgt->t_id,
+ ed.ed_tgt->t_id);
+ } else {
+ int id = mcd->md_tgt->td_nextid++;
+
+ debug(3, "Creating new type %d <%x>\n", id, id);
+ add_mapping(mcd->md_ta, ctdp->t_id, id);
+ hash_add(mcd->md_tdtba, ctdp);
+ }
+
+ mcd->md_parent->td_curemark = ed.ed_cur_mark + 1;
+
+ return (1);
+}
+
+static tdtrav_cb_f map_pre[] = {
+ NULL,
+ map_td_tree_pre, /* intrinsic */
+ map_td_tree_pre, /* pointer */
+ map_td_tree_pre, /* array */
+ map_td_tree_pre, /* function */
+ map_td_tree_pre, /* struct */
+ map_td_tree_pre, /* union */
+ map_td_tree_pre, /* enum */
+ map_td_tree_pre, /* forward */
+ map_td_tree_pre, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ map_td_tree_pre, /* volatile */
+ map_td_tree_pre, /* const */
+ map_td_tree_pre /* restrict */
+};
+
+static tdtrav_cb_f map_post[] = {
+ NULL,
+ map_td_tree_post, /* intrinsic */
+ map_td_tree_post, /* pointer */
+ map_td_tree_post, /* array */
+ map_td_tree_post, /* function */
+ map_td_tree_post, /* struct */
+ map_td_tree_post, /* union */
+ map_td_tree_post, /* enum */
+ map_td_tree_post, /* forward */
+ map_td_tree_post, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ map_td_tree_post, /* volatile */
+ map_td_tree_post, /* const */
+ map_td_tree_post /* restrict */
+};
+
+static tdtrav_cb_f map_self_post[] = {
+ NULL,
+ map_td_tree_self_post, /* intrinsic */
+ map_td_tree_self_post, /* pointer */
+ map_td_tree_self_post, /* array */
+ map_td_tree_self_post, /* function */
+ map_td_tree_self_post, /* struct */
+ map_td_tree_self_post, /* union */
+ map_td_tree_self_post, /* enum */
+ map_td_tree_self_post, /* forward */
+ map_td_tree_self_post, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ map_td_tree_self_post, /* volatile */
+ map_td_tree_self_post, /* const */
+ map_td_tree_self_post /* restrict */
+};
+
+/*
+ * Determining equivalence of iidesc_t nodes
+ */
+
+typedef struct iifind_data {
+ iidesc_t *iif_template;
+ alist_t *iif_ta;
+ int iif_newidx;
+ int iif_refmerge;
+} iifind_data_t;
+
+/*
+ * Check to see if this iidesc_t (node) - the current one on the list we're
+ * iterating through - matches the target one (iif->iif_template). Return -1
+ * if it matches, to stop the iteration.
+ */
+static int
+iidesc_match(void *data, void *arg)
+{
+ iidesc_t *node = data;
+ iifind_data_t *iif = arg;
+ int i;
+
+ if (node->ii_type != iif->iif_template->ii_type ||
+ !streq(node->ii_name, iif->iif_template->ii_name) ||
+ node->ii_dtype->t_id != iif->iif_newidx)
+ return (0);
+
+ if ((node->ii_type == II_SVAR || node->ii_type == II_SFUN) &&
+ !streq(node->ii_owner, iif->iif_template->ii_owner))
+ return (0);
+
+ if (node->ii_nargs != iif->iif_template->ii_nargs)
+ return (0);
+
+ for (i = 0; i < node->ii_nargs; i++) {
+ if (get_mapping(iif->iif_ta,
+ iif->iif_template->ii_args[i]->t_id) !=
+ node->ii_args[i]->t_id)
+ return (0);
+ }
+
+ if (iif->iif_refmerge) {
+ switch (iif->iif_template->ii_type) {
+ case II_GFUN:
+ case II_SFUN:
+ case II_GVAR:
+ case II_SVAR:
+ debug(3, "suppressing duping of %d %s from %s\n",
+ iif->iif_template->ii_type,
+ iif->iif_template->ii_name,
+ (iif->iif_template->ii_owner ?
+ iif->iif_template->ii_owner : "NULL"));
+ return (0);
+ case II_NOT:
+ case II_PSYM:
+ case II_SOU:
+ case II_TYPE:
+ break;
+ }
+ }
+
+ return (-1);
+}
+
+static int
+merge_type_cb(void *data, void *arg)
+{
+ iidesc_t *sii = data;
+ merge_cb_data_t *mcd = arg;
+ iifind_data_t iif;
+ tdtrav_cb_f *post;
+
+ post = (mcd->md_flags & MCD_F_SELFUNIQUIFY ? map_self_post : map_post);
+
+ /* Map the tdesc nodes */
+ (void) iitraverse(sii, &mcd->md_parent->td_curvgen, NULL, map_pre, post,
+ mcd);
+
+ /* Map the iidesc nodes */
+ iif.iif_template = sii;
+ iif.iif_ta = mcd->md_ta;
+ iif.iif_newidx = get_mapping(mcd->md_ta, sii->ii_dtype->t_id);
+ iif.iif_refmerge = (mcd->md_flags & MCD_F_REFMERGE);
+
+ if (hash_match(mcd->md_parent->td_iihash, sii, iidesc_match,
+ &iif) == 1)
+ /* successfully mapped */
+ return (1);
+
+ debug(3, "tba %s (%d)\n", (sii->ii_name ? sii->ii_name : "(anon)"),
+ sii->ii_type);
+
+ list_add(mcd->md_iitba, sii);
+
+ return (0);
+}
+
+static int
+remap_node(tdesc_t **tgtp, tdesc_t *oldtgt, int selftid, tdesc_t *newself,
+ merge_cb_data_t *mcd)
+{
+ tdesc_t *tgt = NULL;
+ tdesc_t template;
+ int oldid = oldtgt->t_id;
+
+ if (oldid == selftid) {
+ *tgtp = newself;
+ return (1);
+ }
+
+ if ((template.t_id = get_mapping(mcd->md_ta, oldid)) == 0)
+ aborterr("failed to get mapping for tid %d <%x>\n", oldid, oldid);
+
+ if (!hash_find(mcd->md_parent->td_idhash, (void *)&template,
+ (void *)&tgt) && (!(mcd->md_flags & MCD_F_REFMERGE) ||
+ !hash_find(mcd->md_tgt->td_idhash, (void *)&template,
+ (void *)&tgt))) {
+ debug(3, "Remap couldn't find %d <%x> (from %d <%x>)\n", template.t_id,
+ template.t_id, oldid, oldid);
+ *tgtp = oldtgt;
+ list_add(mcd->md_tdtbr, tgtp);
+ return (0);
+ }
+
+ *tgtp = tgt;
+ return (1);
+}
+
+static tdesc_t *
+conjure_template(tdesc_t *old, int newselfid)
+{
+ tdesc_t *new = xcalloc(sizeof (tdesc_t));
+
+ new->t_name = old->t_name ? xstrdup(old->t_name) : NULL;
+ new->t_type = old->t_type;
+ new->t_size = old->t_size;
+ new->t_id = newselfid;
+ new->t_flags = old->t_flags;
+
+ return (new);
+}
+
+/*ARGSUSED2*/
+static tdesc_t *
+conjure_intrinsic(tdesc_t *old, int newselfid, merge_cb_data_t *mcd __unused)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+
+ new->t_intr = xmalloc(sizeof (intr_t));
+ bcopy(old->t_intr, new->t_intr, sizeof (intr_t));
+
+ return (new);
+}
+
+static tdesc_t *
+conjure_plain(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+
+ (void) remap_node(&new->t_tdesc, old->t_tdesc, old->t_id, new, mcd);
+
+ return (new);
+}
+
+static tdesc_t *
+conjure_function(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+ fndef_t *nfn = xmalloc(sizeof (fndef_t));
+ fndef_t *ofn = old->t_fndef;
+ int i;
+
+ (void) remap_node(&nfn->fn_ret, ofn->fn_ret, old->t_id, new, mcd);
+
+ nfn->fn_nargs = ofn->fn_nargs;
+ nfn->fn_vargs = ofn->fn_vargs;
+
+ if (nfn->fn_nargs > 0)
+ nfn->fn_args = xcalloc(sizeof (tdesc_t *) * ofn->fn_nargs);
+
+ for (i = 0; i < (int) ofn->fn_nargs; i++) {
+ (void) remap_node(&nfn->fn_args[i], ofn->fn_args[i], old->t_id,
+ new, mcd);
+ }
+
+ new->t_fndef = nfn;
+
+ return (new);
+}
+
+static tdesc_t *
+conjure_array(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+ ardef_t *nar = xmalloc(sizeof (ardef_t));
+ ardef_t *oar = old->t_ardef;
+
+ (void) remap_node(&nar->ad_contents, oar->ad_contents, old->t_id, new,
+ mcd);
+ (void) remap_node(&nar->ad_idxtype, oar->ad_idxtype, old->t_id, new,
+ mcd);
+
+ nar->ad_nelems = oar->ad_nelems;
+
+ new->t_ardef = nar;
+
+ return (new);
+}
+
+static tdesc_t *
+conjure_su(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+ mlist_t *omem, **nmemp;
+
+ for (omem = old->t_members, nmemp = &new->t_members;
+ omem; omem = omem->ml_next, nmemp = &((*nmemp)->ml_next)) {
+ *nmemp = xmalloc(sizeof (mlist_t));
+ (*nmemp)->ml_offset = omem->ml_offset;
+ (*nmemp)->ml_size = omem->ml_size;
+ (*nmemp)->ml_name = xstrdup(omem->ml_name ? omem->ml_name : "empty omem->ml_name");
+ (void) remap_node(&((*nmemp)->ml_type), omem->ml_type,
+ old->t_id, new, mcd);
+ }
+ *nmemp = NULL;
+
+ return (new);
+}
+
+/*ARGSUSED2*/
+static tdesc_t *
+conjure_enum(tdesc_t *old, int newselfid, merge_cb_data_t *mcd __unused)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+ elist_t *oel, **nelp;
+
+ for (oel = old->t_emem, nelp = &new->t_emem;
+ oel; oel = oel->el_next, nelp = &((*nelp)->el_next)) {
+ *nelp = xmalloc(sizeof (elist_t));
+ (*nelp)->el_name = xstrdup(oel->el_name);
+ (*nelp)->el_number = oel->el_number;
+ }
+ *nelp = NULL;
+
+ return (new);
+}
+
+/*ARGSUSED2*/
+static tdesc_t *
+conjure_forward(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+
+ list_add(&mcd->md_tgt->td_fwdlist, new);
+
+ return (new);
+}
+
+/*ARGSUSED*/
+static tdesc_t *
+conjure_assert(tdesc_t *old __unused, int newselfid __unused, merge_cb_data_t *mcd __unused)
+{
+ assert(1 == 0);
+ return (NULL);
+}
+
+static iidesc_t *
+conjure_iidesc(iidesc_t *old, merge_cb_data_t *mcd)
+{
+ iidesc_t *new = iidesc_dup(old);
+ int i;
+
+ (void) remap_node(&new->ii_dtype, old->ii_dtype, -1, NULL, mcd);
+ for (i = 0; i < new->ii_nargs; i++) {
+ (void) remap_node(&new->ii_args[i], old->ii_args[i], -1, NULL,
+ mcd);
+ }
+
+ return (new);
+}
+
+static int
+fwd_redir(tdesc_t *fwd, tdesc_t **fwdp, void *private)
+{
+ alist_t *map = private;
+ void *defn;
+
+ if (!alist_find(map, (void *)fwd, (void **)&defn))
+ return (0);
+
+ debug(3, "Redirecting an edge to %s\n", tdesc_name(defn));
+
+ *fwdp = defn;
+
+ return (1);
+}
+
+static tdtrav_cb_f fwd_redir_cbs[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ NULL, /* array */
+ NULL, /* function */
+ NULL, /* struct */
+ NULL, /* union */
+ NULL, /* enum */
+ fwd_redir, /* forward */
+ NULL, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL /* restrict */
+};
+
+typedef struct redir_mstr_data {
+ tdata_t *rmd_tgt;
+ alist_t *rmd_map;
+} redir_mstr_data_t;
+
+static int
+redir_mstr_fwd_cb(void *name, void *value, void *arg)
+{
+ tdesc_t *fwd = name;
+ int defnid = (uintptr_t)value;
+ redir_mstr_data_t *rmd = arg;
+ tdesc_t template;
+ tdesc_t *defn;
+
+ template.t_id = defnid;
+
+ if (!hash_find(rmd->rmd_tgt->td_idhash, (void *)&template,
+ (void *)&defn)) {
+ aborterr("Couldn't unforward %d (%s)\n", defnid,
+ tdesc_name(defn));
+ }
+
+ debug(3, "Forward map: resolved %d to %s\n", defnid, tdesc_name(defn));
+
+ alist_add(rmd->rmd_map, (void *)fwd, (void *)defn);
+
+ return (1);
+}
+
+static void
+redir_mstr_fwds(merge_cb_data_t *mcd)
+{
+ redir_mstr_data_t rmd;
+ alist_t *map = alist_new(NULL, NULL);
+
+ rmd.rmd_tgt = mcd->md_tgt;
+ rmd.rmd_map = map;
+
+ if (alist_iter(mcd->md_fdida, redir_mstr_fwd_cb, &rmd)) {
+ (void) iitraverse_hash(mcd->md_tgt->td_iihash,
+ &mcd->md_tgt->td_curvgen, fwd_redir_cbs, NULL, NULL, map);
+ }
+
+ alist_free(map);
+}
+
+static int
+add_iitba_cb(void *data, void *private)
+{
+ merge_cb_data_t *mcd = private;
+ iidesc_t *tba = data;
+ iidesc_t *new;
+ iifind_data_t iif;
+ int newidx;
+
+ newidx = get_mapping(mcd->md_ta, tba->ii_dtype->t_id);
+ assert(newidx != -1);
+
+ (void) list_remove(mcd->md_iitba, data, NULL, NULL);
+
+ iif.iif_template = tba;
+ iif.iif_ta = mcd->md_ta;
+ iif.iif_newidx = newidx;
+ iif.iif_refmerge = (mcd->md_flags & MCD_F_REFMERGE);
+
+ if (hash_match(mcd->md_parent->td_iihash, tba, iidesc_match,
+ &iif) == 1) {
+ debug(3, "iidesc_t %s already exists\n",
+ (tba->ii_name ? tba->ii_name : "(anon)"));
+ return (1);
+ }
+
+ new = conjure_iidesc(tba, mcd);
+ hash_add(mcd->md_tgt->td_iihash, new);
+
+ return (1);
+}
+
+static int
+add_tdesc(tdesc_t *oldtdp, int newid, merge_cb_data_t *mcd)
+{
+ tdesc_t *newtdp;
+ tdesc_t template;
+
+ template.t_id = newid;
+ assert(hash_find(mcd->md_parent->td_idhash,
+ (void *)&template, NULL) == 0);
+
+ debug(3, "trying to conjure %d %s (%d, <%x>) as %d, <%x>\n",
+ oldtdp->t_type, tdesc_name(oldtdp), oldtdp->t_id,
+ oldtdp->t_id, newid, newid);
+
+ if ((newtdp = tdesc_ops[oldtdp->t_type].conjure(oldtdp, newid,
+ mcd)) == NULL)
+ /* couldn't map everything */
+ return (0);
+
+ debug(3, "succeeded\n");
+
+ hash_add(mcd->md_tgt->td_idhash, newtdp);
+ hash_add(mcd->md_tgt->td_layouthash, newtdp);
+
+ return (1);
+}
+
+static int
+add_tdtba_cb(void *data, void *arg)
+{
+ tdesc_t *tdp = data;
+ merge_cb_data_t *mcd = arg;
+ int newid;
+ int rc;
+
+ newid = get_mapping(mcd->md_ta, tdp->t_id);
+ assert(newid != -1);
+
+ if ((rc = add_tdesc(tdp, newid, mcd)))
+ hash_remove(mcd->md_tdtba, (void *)tdp);
+
+ return (rc);
+}
+
+static int
+add_tdtbr_cb(void *data, void *arg)
+{
+ tdesc_t **tdpp = data;
+ merge_cb_data_t *mcd = arg;
+
+ debug(3, "Remapping %s (%d)\n", tdesc_name(*tdpp), (*tdpp)->t_id);
+
+ if (!remap_node(tdpp, *tdpp, -1, NULL, mcd))
+ return (0);
+
+ (void) list_remove(mcd->md_tdtbr, (void *)tdpp, NULL, NULL);
+ return (1);
+}
+
+static void
+merge_types(hash_t *src, merge_cb_data_t *mcd)
+{
+ list_t *iitba = NULL;
+ list_t *tdtbr = NULL;
+ int iirc, tdrc;
+
+ mcd->md_iitba = &iitba;
+ mcd->md_tdtba = hash_new(TDATA_LAYOUT_HASH_SIZE, tdesc_layouthash,
+ tdesc_layoutcmp);
+ mcd->md_tdtbr = &tdtbr;
+
+ (void) hash_iter(src, merge_type_cb, mcd);
+
+ tdrc = hash_iter(mcd->md_tdtba, add_tdtba_cb, mcd);
+ debug(3, "add_tdtba_cb added %d items\n", tdrc);
+
+ iirc = list_iter(*mcd->md_iitba, add_iitba_cb, mcd);
+ debug(3, "add_iitba_cb added %d items\n", iirc);
+
+ assert(list_count(*mcd->md_iitba) == 0 &&
+ hash_count(mcd->md_tdtba) == 0);
+
+ tdrc = list_iter(*mcd->md_tdtbr, add_tdtbr_cb, mcd);
+ debug(3, "add_tdtbr_cb added %d items\n", tdrc);
+
+ if (list_count(*mcd->md_tdtbr) != 0)
+ aborterr("Couldn't remap all nodes\n");
+
+ /*
+ * We now have an alist of master forwards and the ids of the new master
+ * definitions for those forwards in mcd->md_fdida. By this point,
+ * we're guaranteed that all of the master definitions referenced in
+ * fdida have been added to the master tree. We now traverse through
+ * the master tree, redirecting all edges inbound to forwards that have
+ * definitions to those definitions.
+ */
+ if (mcd->md_parent == mcd->md_tgt) {
+ redir_mstr_fwds(mcd);
+ }
+}
+
+void
+merge_into_master(tdata_t *cur, tdata_t *mstr, tdata_t *tgt, int selfuniquify)
+{
+ merge_cb_data_t mcd;
+
+ cur->td_ref++;
+ mstr->td_ref++;
+ if (tgt)
+ tgt->td_ref++;
+
+ assert(cur->td_ref == 1 && mstr->td_ref == 1 &&
+ (tgt == NULL || tgt->td_ref == 1));
+
+ mcd.md_parent = mstr;
+ mcd.md_tgt = (tgt ? tgt : mstr);
+ mcd.md_ta = alist_new(NULL, NULL);
+ mcd.md_fdida = alist_new(NULL, NULL);
+ mcd.md_flags = 0;
+
+ if (selfuniquify)
+ mcd.md_flags |= MCD_F_SELFUNIQUIFY;
+ if (tgt)
+ mcd.md_flags |= MCD_F_REFMERGE;
+
+ mstr->td_curvgen = MAX(mstr->td_curvgen, cur->td_curvgen);
+ mstr->td_curemark = MAX(mstr->td_curemark, cur->td_curemark);
+
+ merge_types(cur->td_iihash, &mcd);
+
+ if (debug_level >= 3) {
+ debug(3, "Type association stats\n");
+ alist_stats(mcd.md_ta, 0);
+ debug(3, "Layout hash stats\n");
+ hash_stats(mcd.md_tgt->td_layouthash, 1);
+ }
+
+ alist_free(mcd.md_fdida);
+ alist_free(mcd.md_ta);
+
+ cur->td_ref--;
+ mstr->td_ref--;
+ if (tgt)
+ tgt->td_ref--;
+}
+
+tdesc_ops_t tdesc_ops[] = {
+ { "ERROR! BAD tdesc TYPE", NULL, NULL },
+ { "intrinsic", equiv_intrinsic, conjure_intrinsic },
+ { "pointer", equiv_plain, conjure_plain },
+ { "array", equiv_array, conjure_array },
+ { "function", equiv_function, conjure_function },
+ { "struct", equiv_su, conjure_su },
+ { "union", equiv_su, conjure_su },
+ { "enum", equiv_enum, conjure_enum },
+ { "forward", NULL, conjure_forward },
+ { "typedef", equiv_plain, conjure_plain },
+ { "typedef_unres", equiv_assert, conjure_assert },
+ { "volatile", equiv_plain, conjure_plain },
+ { "const", equiv_plain, conjure_plain },
+ { "restrict", equiv_plain, conjure_plain }
+};
diff --git a/tools/ctf/cvt/output.c b/tools/ctf/cvt/output.c
new file mode 100644
index 000000000000..ae8c5945c8ef
--- /dev/null
+++ b/tools/ctf/cvt/output.c
@@ -0,0 +1,757 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <unistd.h>
+
+#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) {
+ 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);
+
+ iiburst_free(iiburst);
+
+ return (data);
+}
+
+void
+write_ctf(tdata_t *td, const char *curname, const char *newname, int flags)
+{
+ struct stat st;
+ Elf *elf = NULL;
+ Elf *telf = NULL;
+ caddr_t data;
+ size_t len;
+ int fd = -1;
+ int tfd = -1;
+
+ (void) elf_version(EV_CURRENT);
+ if ((fd = open(curname, O_RDONLY)) < 0 || fstat(fd, &st) < 0)
+ terminate("%s: Cannot open for re-reading", curname);
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
+ elfterminate(curname, "Cannot re-read");
+
+ if ((tfd = open(newname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0)
+ terminate("Cannot open temp file %s for writing", newname);
+ if ((telf = elf_begin(tfd, ELF_C_WRITE, NULL)) == NULL)
+ elfterminate(curname, "Cannot write");
+
+ data = make_ctf_data(td, elf, curname, &len, flags);
+ write_file(elf, curname, telf, newname, data, len, flags);
+ free(data);
+
+ elf_end(telf);
+ elf_end(elf);
+ (void) close(fd);
+ (void) close(tfd);
+}
diff --git a/tools/ctf/cvt/st_parse.c b/tools/ctf/cvt/st_parse.c
new file mode 100644
index 000000000000..53643204c1c6
--- /dev/null
+++ b/tools/ctf/cvt/st_parse.c
@@ -0,0 +1,1198 @@
+/*
+ * 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"
+
+/*
+ * This file is a sewer.
+ */
+
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <strings.h>
+#include <setjmp.h>
+#include <ctype.h>
+#include <uts/common/sys/ctf.h>
+
+#include "ctftools.h"
+#include "memory.h"
+#include "list.h"
+
+#define HASH(NUM) ((int)(NUM & (BUCKETS - 1)))
+#define BUCKETS 128
+
+#define TYPEPAIRMULT 10000
+#define MAKETYPEID(file, num) ((file) * TYPEPAIRMULT + num)
+#define TYPEFILE(tid) ((tid) / TYPEPAIRMULT)
+#define TYPENUM(tid) ((tid) % TYPEPAIRMULT)
+
+#define expected(a, b, c) _expected(a, b, c, __LINE__)
+
+static int faketypenumber = 100000000;
+
+static tdesc_t *hash_table[BUCKETS];
+static tdesc_t *name_table[BUCKETS];
+
+list_t *typedbitfldmems;
+
+static void reset(void);
+static jmp_buf resetbuf;
+
+static char *soudef(char *cp, stabtype_t type, tdesc_t **rtdp);
+static void enumdef(char *cp, tdesc_t **rtdp);
+static int compute_sum(const char *w);
+
+static char *number(char *cp, int *n);
+static char *name(char *cp, char **w);
+static char *id(char *cp, int *h);
+static char *whitesp(char *cp);
+static void addhash(tdesc_t *tdp, int num);
+static int tagadd(char *w, int h, tdesc_t *tdp);
+static char *tdefdecl(char *cp, int h, tdesc_t **rtdp);
+static char *intrinsic(char *cp, tdesc_t **rtdp);
+static char *arraydef(char *cp, tdesc_t **rtdp);
+
+int debug_parse = DEBUG_PARSE;
+
+/*PRINTFLIKE3*/
+static void
+parse_debug(int level, char *cp, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+ char tmp[32];
+ int i;
+
+ if (level > debug_level || !debug_parse)
+ return;
+
+ if (cp != NULL) {
+ for (i = 0; i < 30; i++) {
+ if (cp[i] == '\0')
+ break;
+ if (!iscntrl(cp[i]))
+ tmp[i] = cp[i];
+ }
+ tmp[i] = '\0';
+ (void) snprintf(buf, sizeof (buf), "%s [cp='%s']\n", fmt, tmp);
+ } else {
+ strcpy(buf, fmt);
+ strcat(buf, "\n");
+ }
+
+ va_start(ap, fmt);
+ vadebug(level, buf, ap);
+ va_end(ap);
+}
+
+/* Report unexpected syntax in stabs. */
+static void
+_expected(
+ const char *who, /* what function, or part thereof, is reporting */
+ const char *what, /* what was expected */
+ const char *where, /* where we were in the line of input */
+ int line)
+{
+ fprintf(stderr, "%s, expecting \"%s\" at \"%s\"\n", who, what, where);
+ fprintf(stderr, "code line: %d, file %s\n", line,
+ (curhdr ? curhdr : "NO FILE"));
+ reset();
+}
+
+/*ARGSUSED*/
+void
+parse_init(tdata_t *td __unused)
+{
+ int i;
+
+ for (i = 0; i < BUCKETS; i++) {
+ hash_table[i] = NULL;
+ name_table[i] = NULL;
+ }
+
+ if (typedbitfldmems != NULL) {
+ list_free(typedbitfldmems, NULL, NULL);
+ typedbitfldmems = NULL;
+ }
+}
+
+void
+parse_finish(tdata_t *td)
+{
+ td->td_nextid = ++faketypenumber;
+}
+
+static tdesc_t *
+unres_new(int tid)
+{
+ tdesc_t *tdp;
+
+ tdp = xcalloc(sizeof (*tdp));
+ tdp->t_type = TYPEDEF_UNRES;
+ tdp->t_id = tid;
+
+ return (tdp);
+}
+
+static char *
+read_tid(char *cp, tdesc_t **tdpp)
+{
+ tdesc_t *tdp;
+ int tid;
+
+ cp = id(cp, &tid);
+
+ assert(tid != 0);
+
+ if (*cp == '=') {
+ if (!(cp = tdefdecl(cp + 1, tid, &tdp)))
+ return (NULL);
+ if (tdp->t_id && tdp->t_id != tid) {
+ tdesc_t *ntdp = xcalloc(sizeof (*ntdp));
+
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp = ntdp;
+ }
+ addhash(tdp, tid);
+ } else if ((tdp = lookup(tid)) == NULL)
+ tdp = unres_new(tid);
+
+ *tdpp = tdp;
+ return (cp);
+}
+
+static iitype_t
+parse_fun(char *cp, iidesc_t *ii)
+{
+ iitype_t iitype = 0;
+ tdesc_t *tdp;
+ tdesc_t **args = NULL;
+ int nargs = 0;
+ int va = 0;
+
+ /*
+ * name:P prototype
+ * name:F global function
+ * name:f static function
+ */
+ switch (*cp++) {
+ case 'P':
+ iitype = II_NOT; /* not interesting */
+ break;
+
+ case 'F':
+ iitype = II_GFUN;
+ break;
+
+ case 'f':
+ iitype = II_SFUN;
+ break;
+
+ default:
+ expected("parse_nfun", "[PfF]", cp - 1);
+ }
+
+ if (!(cp = read_tid(cp, &tdp)))
+ return (-1);
+
+ if (*cp)
+ args = xmalloc(sizeof (tdesc_t *) * FUNCARG_DEF);
+
+ while (*cp && *++cp) {
+ if (*cp == '0') {
+ va = 1;
+ continue;
+ }
+
+ nargs++;
+ if (nargs > FUNCARG_DEF)
+ args = xrealloc(args, sizeof (tdesc_t *) * nargs);
+ if (!(cp = read_tid(cp, &args[nargs - 1])))
+ return (-1);
+ }
+
+ ii->ii_type = iitype;
+ ii->ii_dtype = tdp;
+ ii->ii_nargs = nargs;
+ ii->ii_args = args;
+ ii->ii_vargs = va;
+
+ return (iitype);
+}
+
+static iitype_t
+parse_sym(char *cp, iidesc_t *ii)
+{
+ tdesc_t *tdp;
+ iitype_t iitype = 0;
+
+ /*
+ * name:G global variable
+ * name:S static variable
+ */
+ switch (*cp++) {
+ case 'G':
+ iitype = II_GVAR;
+ break;
+ case 'S':
+ iitype = II_SVAR;
+ break;
+ case 'p':
+ iitype = II_PSYM;
+ break;
+ case '(':
+ cp--;
+ /*FALLTHROUGH*/
+ case 'r':
+ case 'V':
+ iitype = II_NOT; /* not interesting */
+ break;
+ default:
+ expected("parse_sym", "[GprSV(]", cp - 1);
+ }
+
+ if (!(cp = read_tid(cp, &tdp)))
+ return (-1);
+
+ ii->ii_type = iitype;
+ ii->ii_dtype = tdp;
+
+ return (iitype);
+}
+
+static iitype_t
+parse_type(char *cp, iidesc_t *ii)
+{
+ tdesc_t *tdp, *ntdp;
+ int tid;
+
+ if (*cp++ != 't')
+ expected("parse_type", "t (type)", cp - 1);
+
+ cp = id(cp, &tid);
+ if ((tdp = lookup(tid)) == NULL) {
+ if (*cp++ != '=')
+ expected("parse_type", "= (definition)", cp - 1);
+
+ (void) tdefdecl(cp, tid, &tdp);
+
+ if (tdp->t_id == tid) {
+ assert(tdp->t_type != TYPEDEF);
+ assert(!lookup(tdp->t_id));
+
+ if (!streq(tdp->t_name, ii->ii_name)) {
+ ntdp = xcalloc(sizeof (*ntdp));
+ ntdp->t_name = xstrdup(ii->ii_name);
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp->t_id = faketypenumber++;
+ tdp = ntdp;
+ }
+ } else if (tdp->t_id == 0) {
+ assert(tdp->t_type == FORWARD ||
+ tdp->t_type == INTRINSIC);
+
+ if (tdp->t_name && !streq(tdp->t_name, ii->ii_name)) {
+ ntdp = xcalloc(sizeof (*ntdp));
+ ntdp->t_name = xstrdup(ii->ii_name);
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp->t_id = faketypenumber++;
+ tdp = ntdp;
+ }
+ } else if (tdp->t_id != tid) {
+ ntdp = xcalloc(sizeof (*ntdp));
+ ntdp->t_name = xstrdup(ii->ii_name);
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp = ntdp;
+ }
+
+ if (tagadd(ii->ii_name, tid, tdp) < 0)
+ return (-1);
+ }
+
+ ii->ii_type = II_TYPE;
+ ii->ii_dtype = tdp;
+ return (II_TYPE);
+}
+
+static iitype_t
+parse_sou(char *cp, iidesc_t *idp)
+{
+ tdesc_t *rtdp;
+ int tid;
+
+ if (*cp++ != 'T')
+ expected("parse_sou", "T (sou)", cp - 1);
+
+ cp = id(cp, &tid);
+ if (*cp++ != '=')
+ expected("parse_sou", "= (definition)", cp - 1);
+
+ parse_debug(1, NULL, "parse_sou: declaring '%s'", idp->ii_name ?
+ idp->ii_name : "(anon)");
+ if ((rtdp = lookup(tid)) != NULL) {
+ if (idp->ii_name != NULL) {
+ if (rtdp->t_name != NULL &&
+ strcmp(rtdp->t_name, idp->ii_name) != 0) {
+ tdesc_t *tdp;
+
+ tdp = xcalloc(sizeof (*tdp));
+ tdp->t_name = xstrdup(idp->ii_name);
+ tdp->t_type = TYPEDEF;
+ tdp->t_tdesc = rtdp;
+ addhash(tdp, tid); /* for *(x,y) types */
+ parse_debug(3, NULL, " %s defined as %s(%d)",
+ idp->ii_name, tdesc_name(rtdp), tid);
+ } else if (rtdp->t_name == NULL) {
+ rtdp->t_name = xstrdup(idp->ii_name);
+ addhash(rtdp, tid);
+ }
+ }
+ } else {
+ rtdp = xcalloc(sizeof (*rtdp));
+ rtdp->t_name = idp->ii_name ? xstrdup(idp->ii_name) : NULL;
+ addhash(rtdp, tid);
+ }
+
+ switch (*cp++) {
+ case 's':
+ (void) soudef(cp, STRUCT, &rtdp);
+ break;
+ case 'u':
+ (void) soudef(cp, UNION, &rtdp);
+ break;
+ case 'e':
+ enumdef(cp, &rtdp);
+ break;
+ default:
+ expected("parse_sou", "<tag type s/u/e>", cp - 1);
+ break;
+ }
+
+ idp->ii_type = II_SOU;
+ idp->ii_dtype = rtdp;
+ return (II_SOU);
+}
+
+int
+parse_stab(stab_t *stab, char *cp, iidesc_t **iidescp)
+{
+ iidesc_t *ii = NULL;
+ iitype_t (*parse)(char *, iidesc_t *);
+ int rc;
+
+ /*
+ * set up for reset()
+ */
+ if (setjmp(resetbuf))
+ return (-1);
+
+ cp = whitesp(cp);
+ ii = iidesc_new(NULL);
+ cp = name(cp, &ii->ii_name);
+
+ switch (stab->n_type) {
+ case N_FUN:
+ parse = parse_fun;
+ break;
+
+ case N_LSYM:
+ if (*cp == 't')
+ parse = parse_type;
+ else if (*cp == 'T')
+ parse = parse_sou;
+ else
+ parse = parse_sym;
+ break;
+
+ case N_GSYM:
+ case N_LCSYM:
+ case N_PSYM:
+ case N_ROSYM:
+ case N_RSYM:
+ case N_STSYM:
+ parse = parse_sym;
+ break;
+ default:
+ parse_debug(1, cp, "Unknown stab type %#x", stab->n_type);
+ bzero(&resetbuf, sizeof (resetbuf));
+ return (-1);
+ }
+
+ rc = parse(cp, ii);
+ bzero(&resetbuf, sizeof (resetbuf));
+
+ if (rc < 0 || ii->ii_type == II_NOT) {
+ iidesc_free(ii, NULL);
+ return (rc);
+ }
+
+ *iidescp = ii;
+
+ return (1);
+}
+
+/*
+ * Check if we have this node in the hash table already
+ */
+tdesc_t *
+lookup(int h)
+{
+ int bucket = HASH(h);
+ tdesc_t *tdp = hash_table[bucket];
+
+ while (tdp != NULL) {
+ if (tdp->t_id == h)
+ return (tdp);
+ tdp = tdp->t_hash;
+ }
+ return (NULL);
+}
+
+static char *
+whitesp(char *cp)
+{
+ char c;
+
+ for (c = *cp++; isspace(c); c = *cp++);
+ --cp;
+ return (cp);
+}
+
+static char *
+name(char *cp, char **w)
+{
+ char *new, *orig, c;
+ int len;
+
+ orig = cp;
+ c = *cp++;
+ if (c == ':')
+ *w = NULL;
+ else if (isalpha(c) || strchr("_.$", c)) {
+ for (c = *cp++; isalnum(c) || strchr(" _.$", c); c = *cp++)
+ ;
+ if (c != ':')
+ reset();
+ len = cp - orig;
+ new = xmalloc(len);
+ while (orig < cp - 1)
+ *new++ = *orig++;
+ *new = '\0';
+ *w = new - (len - 1);
+ } else
+ reset();
+
+ return (cp);
+}
+
+static char *
+number(char *cp, int *n)
+{
+ char *next;
+
+ *n = (int)strtol(cp, &next, 10);
+ if (next == cp)
+ expected("number", "<number>", cp);
+ return (next);
+}
+
+static char *
+id(char *cp, int *h)
+{
+ int n1, n2;
+
+ if (*cp == '(') { /* SunPro style */
+ cp++;
+ cp = number(cp, &n1);
+ if (*cp++ != ',')
+ expected("id", ",", cp - 1);
+ cp = number(cp, &n2);
+ if (*cp++ != ')')
+ expected("id", ")", cp - 1);
+ *h = MAKETYPEID(n1, n2);
+ } else if (isdigit(*cp)) { /* gcc style */
+ cp = number(cp, &n1);
+ *h = n1;
+ } else {
+ expected("id", "(/0-9", cp);
+ }
+ return (cp);
+}
+
+static int
+tagadd(char *w, int h, tdesc_t *tdp)
+{
+ tdesc_t *otdp;
+
+ tdp->t_name = w;
+ if (!(otdp = lookup(h)))
+ addhash(tdp, h);
+ else if (otdp != tdp) {
+ warning("duplicate entry\n");
+ warning(" old: %s %d (%d,%d)\n", tdesc_name(otdp),
+ otdp->t_type, TYPEFILE(otdp->t_id), TYPENUM(otdp->t_id));
+ warning(" new: %s %d (%d,%d)\n", tdesc_name(tdp),
+ tdp->t_type, TYPEFILE(tdp->t_id), TYPENUM(tdp->t_id));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static char *
+tdefdecl(char *cp, int h, tdesc_t **rtdp)
+{
+ tdesc_t *ntdp;
+ char *w;
+ int c, h2;
+ char type;
+
+ parse_debug(3, cp, "tdefdecl h=%d", h);
+
+ /* Type codes */
+ switch (type = *cp) {
+ case 'b': /* integer */
+ case 'R': /* fp */
+ cp = intrinsic(cp, rtdp);
+ break;
+ case '(': /* equiv to another type */
+ cp = id(cp, &h2);
+ ntdp = lookup(h2);
+
+ if (ntdp != NULL && *cp == '=') {
+ if (ntdp->t_type == FORWARD && *(cp + 1) == 'x') {
+ /*
+ * The 6.2 compiler, and possibly others, will
+ * sometimes emit the same stab for a forward
+ * declaration twice. That is, "(1,2)=xsfoo:"
+ * will sometimes show up in two different
+ * places. This is, of course, quite fun. We
+ * want CTF to work in spite of the compiler,
+ * so we'll let this one through.
+ */
+ char *c2 = cp + 2;
+ char *nm;
+
+ if (!strchr("sue", *c2++)) {
+ expected("tdefdecl/x-redefine", "[sue]",
+ c2 - 1);
+ }
+
+ c2 = name(c2, &nm);
+ if (strcmp(nm, ntdp->t_name) != 0) {
+ terminate("Stabs error: Attempt to "
+ "redefine type (%d,%d) as "
+ "something else: %s\n",
+ TYPEFILE(h2), TYPENUM(h2),
+ c2 - 1);
+ }
+ free(nm);
+
+ h2 = faketypenumber++;
+ ntdp = NULL;
+ } else {
+ terminate("Stabs error: Attempting to "
+ "redefine type (%d,%d)\n", TYPEFILE(h2),
+ TYPENUM(h2));
+ }
+ }
+
+ if (ntdp == NULL) { /* if that type isn't defined yet */
+ if (*cp != '=') {
+ /* record it as unresolved */
+ parse_debug(3, NULL, "tdefdecl unres type %d",
+ h2);
+ *rtdp = calloc(sizeof (**rtdp), 1);
+ (*rtdp)->t_type = TYPEDEF_UNRES;
+ (*rtdp)->t_id = h2;
+ break;
+ } else
+ cp++;
+
+ /* define a new type */
+ cp = tdefdecl(cp, h2, rtdp);
+ if ((*rtdp)->t_id && (*rtdp)->t_id != h2) {
+ ntdp = calloc(sizeof (*ntdp), 1);
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = *rtdp;
+ *rtdp = ntdp;
+ }
+
+ addhash(*rtdp, h2);
+
+ } else { /* that type is already defined */
+ if (ntdp->t_type != TYPEDEF || ntdp->t_name != NULL) {
+ *rtdp = ntdp;
+ } else {
+ parse_debug(3, NULL,
+ "No duplicate typedef anon for ref");
+ *rtdp = ntdp;
+ }
+ }
+ break;
+ case '*':
+ ntdp = NULL;
+ cp = tdefdecl(cp + 1, h, &ntdp);
+ if (ntdp == NULL)
+ expected("tdefdecl/*", "id", cp);
+
+ if (!ntdp->t_id)
+ ntdp->t_id = faketypenumber++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = POINTER;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_id = h;
+ (*rtdp)->t_tdesc = ntdp;
+ break;
+ case 'f':
+ cp = tdefdecl(cp + 1, h, &ntdp);
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = FUNCTION;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_id = h;
+ (*rtdp)->t_fndef = xcalloc(sizeof (fndef_t));
+ /*
+ * The 6.1 compiler will sometimes generate incorrect stabs for
+ * function pointers (it'll get the return type wrong). This
+ * causes merges to fail. We therefore treat function pointers
+ * as if they all point to functions that return int. When
+ * 4432549 is fixed, the lookupname() call below should be
+ * replaced with `ntdp'.
+ */
+ (*rtdp)->t_fndef->fn_ret = lookupname("int");
+ break;
+ case 'a':
+ case 'z':
+ cp++;
+ if (*cp++ != 'r')
+ expected("tdefdecl/[az]", "r", cp - 1);
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = ARRAY;
+ (*rtdp)->t_id = h;
+ cp = arraydef(cp, rtdp);
+ break;
+ case 'x':
+ c = *++cp;
+ if (c != 's' && c != 'u' && c != 'e')
+ expected("tdefdecl/x", "[sue]", cp - 1);
+ cp = name(cp + 1, &w);
+
+ ntdp = xcalloc(sizeof (*ntdp));
+ ntdp->t_type = FORWARD;
+ ntdp->t_name = w;
+ /*
+ * We explicitly don't set t_id here - the caller will do it.
+ * The caller may want to use a real type ID, or they may
+ * choose to make one up.
+ */
+
+ *rtdp = ntdp;
+ break;
+
+ case 'B': /* volatile */
+ cp = tdefdecl(cp + 1, h, &ntdp);
+
+ if (!ntdp->t_id)
+ ntdp->t_id = faketypenumber++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = VOLATILE;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_tdesc = ntdp;
+ (*rtdp)->t_id = h;
+ break;
+
+ case 'k': /* const */
+ cp = tdefdecl(cp + 1, h, &ntdp);
+
+ if (!ntdp->t_id)
+ ntdp->t_id = faketypenumber++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = CONST;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_tdesc = ntdp;
+ (*rtdp)->t_id = h;
+ break;
+
+ case 'K': /* restricted */
+ cp = tdefdecl(cp + 1, h, &ntdp);
+
+ if (!ntdp->t_id)
+ ntdp->t_id = faketypenumber++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = RESTRICT;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_tdesc = ntdp;
+ (*rtdp)->t_id = h;
+ break;
+
+ case 'u':
+ case 's':
+ cp++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_name = NULL;
+ cp = soudef(cp, (type == 'u') ? UNION : STRUCT, rtdp);
+ break;
+ default:
+ expected("tdefdecl", "<type code>", cp);
+ }
+ return (cp);
+}
+
+static char *
+intrinsic(char *cp, tdesc_t **rtdp)
+{
+ intr_t *intr = xcalloc(sizeof (intr_t));
+ tdesc_t *tdp;
+ int width, fmt, i;
+
+ switch (*cp++) {
+ case 'b':
+ intr->intr_type = INTR_INT;
+ if (*cp == 's')
+ intr->intr_signed = 1;
+ else if (*cp != 'u')
+ expected("intrinsic/b", "[su]", cp);
+ cp++;
+
+ if (strchr("cbv", *cp))
+ intr->intr_iformat = *cp++;
+
+ cp = number(cp, &width);
+ if (*cp++ != ';')
+ expected("intrinsic/b", "; (post-width)", cp - 1);
+
+ cp = number(cp, &intr->intr_offset);
+ if (*cp++ != ';')
+ expected("intrinsic/b", "; (post-offset)", cp - 1);
+
+ cp = number(cp, &intr->intr_nbits);
+ break;
+
+ case 'R':
+ intr->intr_type = INTR_REAL;
+ for (fmt = 0, i = 0; isdigit(*(cp + i)); i++)
+ fmt = fmt * 10 + (*(cp + i) - '0');
+
+ if (fmt < 1 || fmt > CTF_FP_MAX)
+ expected("intrinsic/R", "number <= CTF_FP_MAX", cp);
+
+ intr->intr_fformat = fmt;
+ cp += i;
+
+ if (*cp++ != ';')
+ expected("intrinsic/R", ";", cp - 1);
+ cp = number(cp, &width);
+
+ intr->intr_nbits = width * 8;
+ break;
+ }
+
+ tdp = xcalloc(sizeof (*tdp));
+ tdp->t_type = INTRINSIC;
+ tdp->t_size = width;
+ tdp->t_name = NULL;
+ tdp->t_intr = intr;
+ parse_debug(3, NULL, "intrinsic: size=%d", width);
+ *rtdp = tdp;
+
+ return (cp);
+}
+
+static tdesc_t *
+bitintrinsic(tdesc_t *template, int nbits)
+{
+ tdesc_t *newtdp = xcalloc(sizeof (tdesc_t));
+
+ newtdp->t_name = xstrdup(template->t_name);
+ newtdp->t_id = faketypenumber++;
+ newtdp->t_type = INTRINSIC;
+ newtdp->t_size = template->t_size;
+ newtdp->t_intr = xmalloc(sizeof (intr_t));
+ bcopy(template->t_intr, newtdp->t_intr, sizeof (intr_t));
+ newtdp->t_intr->intr_nbits = nbits;
+
+ return (newtdp);
+}
+
+static char *
+offsize(char *cp, mlist_t *mlp)
+{
+ int offset, size;
+
+ if (*cp == ',')
+ cp++;
+ cp = number(cp, &offset);
+ if (*cp++ != ',')
+ expected("offsize/2", ",", cp - 1);
+ cp = number(cp, &size);
+ if (*cp++ != ';')
+ expected("offsize/3", ";", cp - 1);
+ mlp->ml_offset = offset;
+ mlp->ml_size = size;
+ return (cp);
+}
+
+static tdesc_t *
+find_intrinsic(tdesc_t *tdp)
+{
+ for (;;) {
+ switch (tdp->t_type) {
+ case TYPEDEF:
+ case VOLATILE:
+ case CONST:
+ case RESTRICT:
+ tdp = tdp->t_tdesc;
+ break;
+
+ default:
+ return (tdp);
+ }
+ }
+}
+
+static char *
+soudef(char *cp, stabtype_t type, tdesc_t **rtdp)
+{
+ mlist_t *mlp, **prev;
+ char *w;
+ int h;
+ int size;
+ tdesc_t *tdp, *itdp;
+
+ cp = number(cp, &size);
+ (*rtdp)->t_size = size;
+ (*rtdp)->t_type = type; /* s or u */
+
+ /*
+ * An '@' here indicates a bitmask follows. This is so the
+ * compiler can pass information to debuggers about how structures
+ * are passed in the v9 world. We don't need this information
+ * so we skip over it.
+ */
+ if (cp[0] == '@') {
+ cp += 3;
+ }
+
+ parse_debug(3, cp, "soudef: %s size=%d", tdesc_name(*rtdp),
+ (*rtdp)->t_size);
+
+ prev = &((*rtdp)->t_members);
+ /* now fill up the fields */
+ while ((*cp != '\0') && (*cp != ';')) { /* signifies end of fields */
+ mlp = xcalloc(sizeof (*mlp));
+ *prev = mlp;
+ cp = name(cp, &w);
+ mlp->ml_name = w;
+ cp = id(cp, &h);
+ /*
+ * find the tdesc struct in the hash table for this type
+ * and stick a ptr in here
+ */
+ tdp = lookup(h);
+ if (tdp == NULL) { /* not in hash list */
+ parse_debug(3, NULL, " defines %s (%d)", w, h);
+ if (*cp++ != '=') {
+ tdp = unres_new(h);
+ parse_debug(3, NULL,
+ " refers to %s (unresolved %d)",
+ (w ? w : "anon"), h);
+ } else {
+ cp = tdefdecl(cp, h, &tdp);
+
+ if (tdp->t_id && tdp->t_id != h) {
+ tdesc_t *ntdp = xcalloc(sizeof (*ntdp));
+
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp = ntdp;
+ }
+
+ addhash(tdp, h);
+ parse_debug(4, cp,
+ " soudef now looking at ");
+ cp++;
+ }
+ } else {
+ parse_debug(3, NULL, " refers to %s (%d, %s)",
+ w ? w : "anon", h, tdesc_name(tdp));
+ }
+
+ cp = offsize(cp, mlp);
+
+ itdp = find_intrinsic(tdp);
+ if (itdp->t_type == INTRINSIC) {
+ if (mlp->ml_size != itdp->t_intr->intr_nbits) {
+ parse_debug(4, cp, "making %d bit intrinsic "
+ "from %s", mlp->ml_size, tdesc_name(itdp));
+ mlp->ml_type = bitintrinsic(itdp, mlp->ml_size);
+ } else
+ mlp->ml_type = tdp;
+ } else if (itdp->t_type == TYPEDEF_UNRES) {
+ list_add(&typedbitfldmems, mlp);
+ mlp->ml_type = tdp;
+ } else {
+ mlp->ml_type = tdp;
+ }
+
+ /* cp is now pointing to next field */
+ prev = &mlp->ml_next;
+ }
+ return (cp);
+}
+
+static char *
+arraydef(char *cp, tdesc_t **rtdp)
+{
+ int start, end, h;
+
+ cp = id(cp, &h);
+ if (*cp++ != ';')
+ expected("arraydef/1", ";", cp - 1);
+
+ (*rtdp)->t_ardef = xcalloc(sizeof (ardef_t));
+ (*rtdp)->t_ardef->ad_idxtype = lookup(h);
+
+ cp = number(cp, &start); /* lower */
+ if (*cp++ != ';')
+ expected("arraydef/2", ";", cp - 1);
+
+ if (*cp == 'S') {
+ /* variable length array - treat as null dimensioned */
+ cp++;
+ if (*cp++ != '-')
+ expected("arraydef/fpoff-sep", "-", cp - 1);
+ cp = number(cp, &end);
+ end = start;
+ } else {
+ /* normal fixed-dimension array */
+ cp = number(cp, &end); /* upper */
+ }
+
+ if (*cp++ != ';')
+ expected("arraydef/3", ";", cp - 1);
+ (*rtdp)->t_ardef->ad_nelems = end - start + 1;
+ cp = tdefdecl(cp, h, &((*rtdp)->t_ardef->ad_contents));
+
+ parse_debug(3, cp, "defined array idx type %d %d-%d next ",
+ h, start, end);
+
+ return (cp);
+}
+
+static void
+enumdef(char *cp, tdesc_t **rtdp)
+{
+ elist_t *elp, **prev;
+ char *w;
+
+ (*rtdp)->t_type = ENUM;
+ (*rtdp)->t_emem = NULL;
+
+ prev = &((*rtdp)->t_emem);
+ while (*cp != ';') {
+ elp = xcalloc(sizeof (*elp));
+ elp->el_next = NULL;
+ *prev = elp;
+ cp = name(cp, &w);
+ elp->el_name = w;
+ cp = number(cp, &elp->el_number);
+ parse_debug(3, NULL, "enum %s: %s=%d", tdesc_name(*rtdp),
+ elp->el_name, elp->el_number);
+ prev = &elp->el_next;
+ if (*cp++ != ',')
+ expected("enumdef", ",", cp - 1);
+ }
+}
+
+static tdesc_t *
+lookup_name(tdesc_t **hash, const char *name1)
+{
+ int bucket = compute_sum(name1);
+ tdesc_t *tdp, *ttdp = NULL;
+
+ for (tdp = hash[bucket]; tdp != NULL; tdp = tdp->t_next) {
+ if (tdp->t_name != NULL && strcmp(tdp->t_name, name1) == 0) {
+ if (tdp->t_type == STRUCT || tdp->t_type == UNION ||
+ tdp->t_type == ENUM || tdp->t_type == INTRINSIC)
+ return (tdp);
+ if (tdp->t_type == TYPEDEF)
+ ttdp = tdp;
+ }
+ }
+ return (ttdp);
+}
+
+tdesc_t *
+lookupname(const char *name1)
+{
+ return (lookup_name(name_table, name1));
+}
+
+/*
+ * Add a node to the hash queues.
+ */
+static void
+addhash(tdesc_t *tdp, int num)
+{
+ int hash = HASH(num);
+ tdesc_t *ttdp;
+ char added_num = 0, added_name = 0;
+
+ /*
+ * If it already exists in the hash table don't add it again
+ * (but still check to see if the name should be hashed).
+ */
+ ttdp = lookup(num);
+
+ if (ttdp == NULL) {
+ tdp->t_id = num;
+ tdp->t_hash = hash_table[hash];
+ hash_table[hash] = tdp;
+ added_num = 1;
+ }
+
+ if (tdp->t_name != NULL) {
+ ttdp = lookupname(tdp->t_name);
+ if (ttdp == NULL) {
+ hash = compute_sum(tdp->t_name);
+ tdp->t_next = name_table[hash];
+ name_table[hash] = tdp;
+ added_name = 1;
+ }
+ }
+ if (!added_num && !added_name) {
+ terminate("stabs: broken hash\n");
+ }
+}
+
+static int
+compute_sum(const char *w)
+{
+ char c;
+ int sum;
+
+ for (sum = 0; (c = *w) != '\0'; sum += c, w++)
+ ;
+ return (HASH(sum));
+}
+
+static void
+reset(void)
+{
+ longjmp(resetbuf, 1);
+}
+
+void
+check_hash(void)
+{
+ tdesc_t *tdp;
+ int i;
+
+ printf("checking hash\n");
+ for (i = 0; i < BUCKETS; i++) {
+ if (hash_table[i]) {
+ for (tdp = hash_table[i]->t_hash;
+ tdp && tdp != hash_table[i];
+ tdp = tdp->t_hash)
+ continue;
+ if (tdp) {
+ terminate("cycle in hash bucket %d\n", i);
+ return;
+ }
+ }
+
+ if (name_table[i]) {
+ for (tdp = name_table[i]->t_next;
+ tdp && tdp != name_table[i];
+ tdp = tdp->t_next)
+ continue;
+ if (tdp) {
+ terminate("cycle in name bucket %d\n", i);
+ return;
+ }
+ }
+ }
+ printf("done\n");
+}
+
+/*ARGSUSED1*/
+static int
+resolve_typed_bitfields_cb(void *arg, void *private __unused)
+{
+ mlist_t *ml = arg;
+ tdesc_t *tdp = ml->ml_type;
+
+ debug(3, "Resolving typed bitfields (member %s)\n",
+ (ml->ml_name ? ml->ml_name : "(anon)"));
+
+ while (tdp) {
+ switch (tdp->t_type) {
+ case INTRINSIC:
+ if (ml->ml_size != tdp->t_intr->intr_nbits) {
+ debug(3, "making %d bit intrinsic from %s",
+ ml->ml_size, tdesc_name(tdp));
+ ml->ml_type = bitintrinsic(tdp, ml->ml_size);
+ } else {
+ debug(3, "using existing %d bit %s intrinsic",
+ ml->ml_size, tdesc_name(tdp));
+ ml->ml_type = tdp;
+ }
+ return (1);
+
+ case POINTER:
+ case TYPEDEF:
+ case VOLATILE:
+ case CONST:
+ case RESTRICT:
+ tdp = tdp->t_tdesc;
+ break;
+
+ default:
+ return (1);
+ }
+ }
+
+ terminate("type chain for bitfield member %s has a NULL", ml->ml_name);
+ /*NOTREACHED*/
+ return (0);
+}
+
+void
+resolve_typed_bitfields(void)
+{
+ (void) list_iter(typedbitfldmems,
+ resolve_typed_bitfields_cb, NULL);
+}
diff --git a/tools/ctf/cvt/stabs.c b/tools/ctf/cvt/stabs.c
new file mode 100644
index 000000000000..c0c68b53e030
--- /dev/null
+++ b/tools/ctf/cvt/stabs.c
@@ -0,0 +1,381 @@
+/*
+ * 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 used to read stabs data from a file, and to build a tdata structure
+ * based on the interesting parts of that data.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+#include <string.h>
+#include <libgen.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include "ctftools.h"
+#include "list.h"
+#include "stack.h"
+#include "memory.h"
+#include "traverse.h"
+
+char *curhdr;
+
+/*
+ * The stabs generator will sometimes reference types before they've been
+ * defined. If this is the case, a TYPEDEF_UNRES tdesc will be generated.
+ * Note that this is different from a forward declaration, in which the
+ * stab is defined, but is defined as something that doesn't exist yet.
+ * When we have read all of the stabs from the file, we can go back and
+ * fix up all of the unresolved types. We should be able to fix all of them.
+ */
+/*ARGSUSED2*/
+static int
+resolve_tou_node(tdesc_t *node, tdesc_t **nodep, void *private __unused)
+{
+ tdesc_t *new;
+
+ debug(3, "Trying to resolve %s (%d)\n", tdesc_name(node), node->t_id);
+ new = lookup(node->t_id);
+
+ if (new == NULL) {
+ terminate("Couldn't resolve type %d\n", node->t_id);
+ }
+
+ debug(3, " Resolving to %d\n", new->t_id);
+
+ *nodep = new;
+
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+resolve_fwd_node(tdesc_t *node, tdesc_t **nodep, void *private __unused)
+{
+ tdesc_t *new = lookupname(node->t_name);
+
+ debug(3, "Trying to unforward %s (%d)\n", tdesc_name(node), node->t_id);
+
+ if (!new || (new->t_type != STRUCT && new->t_type != UNION))
+ return (0);
+
+ debug(3, " Unforwarded to %d\n", new->t_id);
+
+ *nodep = new;
+
+ return (1);
+}
+
+static tdtrav_cb_f resolve_cbs[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ NULL, /* array */
+ NULL, /* function */
+ NULL, /* struct */
+ NULL, /* union */
+ NULL, /* enum */
+ resolve_fwd_node, /* forward */
+ NULL, /* typedef */
+ resolve_tou_node, /* typedef unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL, /* restrict */
+};
+
+static void
+resolve_nodes(tdata_t *td)
+{
+ debug(2, "Resolving unresolved stabs\n");
+
+ (void) iitraverse_hash(td->td_iihash, &td->td_curvgen, resolve_cbs,
+ NULL, NULL, td);
+}
+
+static char *
+concat(char *s1, char *s2, int s2strip)
+{
+ int savelen = strlen(s2) - s2strip;
+ int newlen = (s1 ? strlen(s1) : 0) + savelen + 1;
+ char *out;
+
+ out = xrealloc(s1, newlen);
+ if (s1)
+ strncpy(out + strlen(out), s2, savelen);
+ else
+ strncpy(out, s2, savelen);
+
+ out[newlen - 1] = '\0';
+
+ return (out);
+}
+
+/*
+ * N_FUN stabs come with their arguments in promoted form. In order to get the
+ * actual arguments, we need to wait for the N_PSYM stabs that will come towards
+ * the end of the function. These routines free the arguments (fnarg_free) we
+ * got from the N_FUN stab and add (fnarg_add) the ones from the N_PSYM stabs.
+ */
+static void
+fnarg_add(iidesc_t *curfun, iidesc_t *arg)
+{
+ curfun->ii_nargs++;
+
+ if (curfun->ii_nargs == 1)
+ curfun->ii_args = xmalloc(sizeof (tdesc_t *) * FUNCARG_DEF);
+ else if (curfun->ii_nargs > FUNCARG_DEF) {
+ curfun->ii_args = xrealloc(curfun->ii_args,
+ sizeof (tdesc_t *) * curfun->ii_nargs);
+ }
+
+ curfun->ii_args[curfun->ii_nargs - 1] = arg->ii_dtype;
+ arg->ii_dtype = NULL;
+}
+
+static void
+fnarg_free(iidesc_t *ii)
+{
+ ii->ii_nargs = 0;
+ free(ii->ii_args);
+ ii->ii_args = NULL;
+}
+
+/*
+ * Read the stabs from the stab ELF section, and turn them into a tdesc tree,
+ * assembled under an iidesc list.
+ */
+int
+stabs_read(tdata_t *td, Elf *elf, char *file)
+{
+ Elf_Scn *scn;
+ Elf_Data *data;
+ stab_t *stab;
+ stk_t *file_stack;
+ iidesc_t *iidescp;
+ iidesc_t *curfun = NULL;
+ char curpath[MAXPATHLEN];
+ char *curfile = NULL;
+ char *str;
+ char *fstr = NULL, *ofstr = NULL;
+ int stabidx, stabstridx;
+ int nstabs, rc, i;
+ int scope = 0;
+
+ if (!((stabidx = findelfsecidx(elf, file, ".stab.excl")) >= 0 &&
+ (stabstridx = findelfsecidx(elf, file, ".stab.exclstr")) >= 0) &&
+ !((stabidx = findelfsecidx(elf, file, ".stab")) >= 0 &&
+ (stabstridx = findelfsecidx(elf, file, ".stabstr")) >= 0)) {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ file_stack = stack_new(free);
+
+ stack_push(file_stack, file);
+ curhdr = file;
+
+ debug(3, "Found stabs in %d, strings in %d\n", stabidx, stabstridx);
+
+ scn = elf_getscn(elf, stabidx);
+ data = elf_rawdata(scn, NULL);
+ nstabs = data->d_size / sizeof (stab_t);
+
+ parse_init(td);
+ for (i = 0; i < nstabs; i++) {
+ stab = &((stab_t *)data->d_buf)[i];
+
+ /* We don't want any local definitions */
+ if (stab->n_type == N_LBRAC) {
+ scope++;
+ debug(3, "stab %d: opening scope (%d)\n", i + 1, scope);
+ continue;
+ } else if (stab->n_type == N_RBRAC) {
+ scope--;
+ debug(3, "stab %d: closing scope (%d)\n", i + 1, scope);
+ continue;
+ } else if (stab->n_type == N_EINCL) {
+ /*
+ * There's a bug in the 5.2 (Taz) compilers that causes
+ * them to emit an extra N_EINCL if there's no actual
+ * text in the file being compiled. To work around this
+ * bug, we explicitly check to make sure we're not
+ * trying to pop a stack that only has the outer scope
+ * on it.
+ */
+ if (stack_level(file_stack) != 1) {
+ str = (char *)stack_pop(file_stack);
+ free(str);
+ curhdr = (char *)stack_peek(file_stack);
+ }
+ }
+
+ /* We only care about a subset of the stabs */
+ if (!(stab->n_type == N_FUN || stab->n_type == N_GSYM ||
+ stab->n_type == N_LCSYM || stab->n_type == N_LSYM ||
+ stab->n_type == N_PSYM || stab->n_type == N_ROSYM ||
+ stab->n_type == N_RSYM ||
+ stab->n_type == N_STSYM || stab->n_type == N_BINCL ||
+ stab->n_type == N_SO || stab->n_type == N_OPT))
+ continue;
+
+ if ((str = elf_strptr(elf, stabstridx,
+ (size_t)stab->n_strx)) == NULL) {
+ terminate("%s: Can't find string at %u for stab %d\n",
+ file, stab->n_strx, i);
+ }
+
+ if (stab->n_type == N_BINCL) {
+ curhdr = xstrdup(str);
+ stack_push(file_stack, curhdr);
+ continue;
+ } else if (stab->n_type == N_SO) {
+ if (str[strlen(str) - 1] != '/') {
+ strcpy(curpath, str);
+ curfile = basename(curpath);
+ }
+ continue;
+ } else if (stab->n_type == N_OPT) {
+ if (strcmp(str, "gcc2_compiled.") == 0) {
+ terminate("%s: GCC-generated stabs are "
+ "unsupported. Use DWARF instead.\n", file);
+ }
+ continue;
+ }
+
+ if (str[strlen(str) - 1] == '\\') {
+ int offset = 1;
+ /*
+ * There's a bug in the compilers that causes them to
+ * generate \ for continuations with just -g (this is
+ * ok), and \\ for continuations with -g -O (this is
+ * broken). This bug is "fixed" in the 6.2 compilers
+ * via the elimination of continuation stabs.
+ */
+ if (str[strlen(str) - 2] == '\\')
+ offset = 2;
+ fstr = concat(fstr, str, offset);
+ continue;
+ } else
+ fstr = concat(fstr, str, 0);
+
+ debug(3, "%4d: .stabs \"%s\", %#x, %d, %hd, %d (from %s)\n", i,
+ fstr, stab->n_type, 0, stab->n_desc,
+ stab->n_value, curhdr);
+
+ if (debug_level >= 3)
+ check_hash();
+
+ /*
+ * Sometimes the compiler stutters, and emits the same stab
+ * twice. This is bad for the parser, which will attempt to
+ * redefine the type IDs indicated in the stabs. This is
+ * compiler bug 4433511.
+ */
+ if (ofstr && strcmp(fstr, ofstr) == 0) {
+ debug(3, "Stutter stab\n");
+ free(fstr);
+ fstr = NULL;
+ continue;
+ }
+
+ if (ofstr)
+ free(ofstr);
+ ofstr = fstr;
+
+ iidescp = NULL;
+
+ if ((rc = parse_stab(stab, fstr, &iidescp)) < 0) {
+ terminate("%s: Couldn't parse stab \"%s\" "
+ "(source file %s)\n", file, str, curhdr);
+ }
+
+ if (rc == 0)
+ goto parse_loop_end;
+
+ /* Make sure the scope tracking is working correctly */
+ assert(stab->n_type != N_FUN || (iidescp->ii_type != II_GFUN &&
+ iidescp->ii_type != II_SFUN) || scope == 0);
+
+ /*
+ * The only things we care about that are in local scope are
+ * the N_PSYM stabs.
+ */
+ if (scope && stab->n_type != N_PSYM) {
+ if (iidescp)
+ iidesc_free(iidescp, NULL);
+ goto parse_loop_end;
+ }
+
+ switch (iidescp->ii_type) {
+ case II_SFUN:
+ iidescp->ii_owner = xstrdup(curfile);
+ /*FALLTHROUGH*/
+ case II_GFUN:
+ curfun = iidescp;
+ fnarg_free(iidescp);
+ iidesc_add(td->td_iihash, iidescp);
+ break;
+
+ case II_SVAR:
+ iidescp->ii_owner = xstrdup(curfile);
+ /*FALLTHROUGH*/
+ case II_GVAR:
+ case II_TYPE:
+ case II_SOU:
+ iidesc_add(td->td_iihash, iidescp);
+ break;
+
+ case II_PSYM:
+ fnarg_add(curfun, iidescp);
+ iidesc_free(iidescp, NULL);
+ break;
+ default:
+ aborterr("invalid ii_type %d for stab type %d",
+ iidescp->ii_type, stab->n_type);
+ }
+
+parse_loop_end:
+ fstr = NULL;
+ }
+
+ if (ofstr)
+ free(ofstr);
+
+ resolve_nodes(td);
+ resolve_typed_bitfields();
+ parse_finish(td);
+
+ cvt_fixstabs(td);
+ cvt_fixups(td, elf_ptrsz(elf));
+
+ return (0);
+}
diff --git a/tools/ctf/cvt/stack.c b/tools/ctf/cvt/stack.c
new file mode 100644
index 000000000000..7c36cd5ef149
--- /dev/null
+++ b/tools/ctf/cvt/stack.c
@@ -0,0 +1,112 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating stacks
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "stack.h"
+#include "memory.h"
+
+#define STACK_SEEDSIZE 5
+
+struct stk {
+ int st_nument;
+ int st_top;
+ void **st_data;
+
+ void (*st_free)(void *);
+};
+
+stk_t *
+stack_new(void (*freep)(void *))
+{
+ stk_t *sp;
+
+ sp = xmalloc(sizeof (stk_t));
+ sp->st_nument = STACK_SEEDSIZE;
+ sp->st_top = -1;
+ sp->st_data = xmalloc(sizeof (void *) * sp->st_nument);
+ sp->st_free = freep;
+
+ return (sp);
+}
+
+void
+stack_free(stk_t *sp)
+{
+ int i;
+
+ if (sp->st_free) {
+ for (i = 0; i <= sp->st_top; i++)
+ sp->st_free(sp->st_data[i]);
+ }
+ free(sp->st_data);
+ free(sp);
+}
+
+void *
+stack_pop(stk_t *sp)
+{
+ assert(sp->st_top >= 0);
+
+ return (sp->st_data[sp->st_top--]);
+}
+
+void *
+stack_peek(stk_t *sp)
+{
+ if (sp->st_top == -1)
+ return (NULL);
+
+ return (sp->st_data[sp->st_top]);
+}
+
+void
+stack_push(stk_t *sp, void *data)
+{
+ sp->st_top++;
+
+ if (sp->st_top == sp->st_nument) {
+ sp->st_nument += STACK_SEEDSIZE;
+ sp->st_data = xrealloc(sp->st_data,
+ sizeof (void *) * sp->st_nument);
+ }
+
+ sp->st_data[sp->st_top] = data;
+}
+
+int
+stack_level(stk_t *sp)
+{
+ return (sp->st_top + 1);
+}
diff --git a/tools/ctf/cvt/stack.h b/tools/ctf/cvt/stack.h
new file mode 100644
index 000000000000..7dca7cfb10d8
--- /dev/null
+++ b/tools/ctf/cvt/stack.h
@@ -0,0 +1,53 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _STACK_H
+#define _STACK_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating stacks
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct stk stk_t;
+
+stk_t *stack_new(void (*)(void *));
+void stack_free(stk_t *);
+void *stack_pop(stk_t *);
+void *stack_peek(stk_t *);
+void stack_push(stk_t *, void *);
+int stack_level(stk_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STACK_H */
diff --git a/tools/ctf/cvt/strtab.c b/tools/ctf/cvt/strtab.c
new file mode 100644
index 000000000000..d8b2bf0e8176
--- /dev/null
+++ b/tools/ctf/cvt/strtab.c
@@ -0,0 +1,258 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/sysmacros.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "strtab.h"
+#include "memory.h"
+
+#define STRTAB_HASHSZ 211 /* use a prime number of hash buckets */
+#define STRTAB_BUFSZ (64 * 1024) /* use 64K data buffers by default */
+
+static void
+strtab_grow(strtab_t *sp)
+{
+ sp->str_nbufs++;
+ sp->str_bufs = xrealloc(sp->str_bufs, sp->str_nbufs * sizeof (char *));
+ sp->str_ptr = xmalloc(sp->str_bufsz);
+ sp->str_bufs[sp->str_nbufs - 1] = sp->str_ptr;
+}
+
+void
+strtab_create(strtab_t *sp)
+{
+ sp->str_hash = xcalloc(STRTAB_HASHSZ * sizeof (strhash_t *));
+ sp->str_hashsz = STRTAB_HASHSZ;
+ sp->str_bufs = NULL;
+ sp->str_ptr = NULL;
+ sp->str_nbufs = 0;
+ sp->str_bufsz = STRTAB_BUFSZ;
+ sp->str_nstrs = 1;
+ sp->str_size = 1;
+
+ strtab_grow(sp);
+ *sp->str_ptr++ = '\0';
+}
+
+void
+strtab_destroy(strtab_t *sp)
+{
+ strhash_t *hp, *hq;
+ ulong_t i;
+
+ for (i = 0; i < sp->str_hashsz; i++) {
+ for (hp = sp->str_hash[i]; hp != NULL; hp = hq) {
+ hq = hp->str_next;
+ free(hp);
+ }
+ }
+
+ for (i = 0; i < sp->str_nbufs; i++)
+ free(sp->str_bufs[i]);
+
+ free(sp->str_hash);
+ free(sp->str_bufs);
+}
+
+static ulong_t
+strtab_hash(const char *key, size_t *len)
+{
+ ulong_t g, h = 0;
+ const char *p;
+ size_t n = 0;
+
+ for (p = key; *p != '\0'; p++, n++) {
+ h = (h << 4) + *p;
+
+ if ((g = (h & 0xf0000000)) != 0) {
+ h ^= (g >> 24);
+ h ^= g;
+ }
+ }
+
+ *len = n;
+ return (h);
+}
+
+static int
+strtab_compare(strtab_t *sp, strhash_t *hp, const char *str, size_t len)
+{
+ ulong_t b = hp->str_buf;
+ const char *buf = hp->str_data;
+ size_t resid, n;
+ int rv;
+
+ while (len != 0) {
+ if (buf == sp->str_bufs[b] + sp->str_bufsz)
+ buf = sp->str_bufs[++b];
+
+ resid = sp->str_bufs[b] + sp->str_bufsz - buf;
+ n = MIN(resid, len);
+
+ if ((rv = strncmp(buf, str, n)) != 0)
+ return (rv);
+
+ buf += n;
+ str += n;
+ len -= n;
+ }
+
+ return (0);
+}
+
+static void
+strtab_copyin(strtab_t *sp, const char *str, size_t len)
+{
+ ulong_t b = sp->str_nbufs - 1;
+ size_t resid, n;
+
+ while (len != 0) {
+ if (sp->str_ptr == sp->str_bufs[b] + sp->str_bufsz) {
+ strtab_grow(sp);
+ b++;
+ }
+
+ resid = sp->str_bufs[b] + sp->str_bufsz - sp->str_ptr;
+ n = MIN(resid, len);
+ bcopy(str, sp->str_ptr, n);
+
+ sp->str_ptr += n;
+ str += n;
+ len -= n;
+ }
+}
+
+size_t
+strtab_insert(strtab_t *sp, const char *str)
+{
+ strhash_t *hp;
+ size_t len;
+ ulong_t h;
+
+ if (str == NULL || str[0] == '\0')
+ return (0); /* we keep a \0 at offset 0 to simplify things */
+
+ h = strtab_hash(str, &len) % sp->str_hashsz;
+
+ /*
+ * If the string is already in our hash table, just return the offset
+ * of the existing string element and do not add a duplicate string.
+ */
+ for (hp = sp->str_hash[h]; hp != NULL; hp = hp->str_next) {
+ if (strtab_compare(sp, hp, str, len + 1) == 0)
+ return (hp->str_off);
+ }
+
+ /*
+ * Create a new hash bucket, initialize it, and insert it at the front
+ * of the hash chain for the appropriate bucket.
+ */
+ hp = xmalloc(sizeof (strhash_t));
+
+ hp->str_data = sp->str_ptr;
+ hp->str_buf = sp->str_nbufs - 1;
+ hp->str_off = sp->str_size;
+ hp->str_len = len;
+ hp->str_next = sp->str_hash[h];
+
+ sp->str_hash[h] = hp;
+
+ /*
+ * Now copy the string data into our buffer list, and then update
+ * the global counts of strings and bytes. Return str's byte offset.
+ */
+ strtab_copyin(sp, str, len + 1);
+ sp->str_nstrs++;
+ sp->str_size += len + 1;
+
+ return (hp->str_off);
+}
+
+size_t
+strtab_size(const strtab_t *sp)
+{
+ return (sp->str_size);
+}
+
+ssize_t
+strtab_write(const strtab_t *sp,
+ ssize_t (*func)(void *, size_t, void *), void *priv)
+{
+ ssize_t res, total = 0;
+ ulong_t i;
+ size_t n;
+
+ for (i = 0; i < sp->str_nbufs; i++, total += res) {
+ if (i == sp->str_nbufs - 1)
+ n = sp->str_ptr - sp->str_bufs[i];
+ else
+ n = sp->str_bufsz;
+
+ if ((res = func(sp->str_bufs[i], n, priv)) <= 0)
+ break;
+ }
+
+ if (total == 0 && sp->str_size != 0)
+ return (-1);
+
+ return (total);
+}
+
+void
+strtab_print(const strtab_t *sp)
+{
+ const strhash_t *hp;
+ ulong_t i;
+
+ for (i = 0; i < sp->str_hashsz; i++) {
+ for (hp = sp->str_hash[i]; hp != NULL; hp = hp->str_next) {
+ const char *buf = hp->str_data;
+ ulong_t b = hp->str_buf;
+ size_t resid, len, n;
+
+ (void) printf("[%lu] %lu \"", (ulong_t)hp->str_off, b);
+
+ for (len = hp->str_len; len != 0; len -= n) {
+ if (buf == sp->str_bufs[b] + sp->str_bufsz)
+ buf = sp->str_bufs[++b];
+
+ resid = sp->str_bufs[b] + sp->str_bufsz - buf;
+ n = MIN(resid, len);
+
+ (void) printf("%.*s", (int)n, buf);
+ buf += n;
+ }
+
+ (void) printf("\"\n");
+ }
+ }
+}
diff --git a/tools/ctf/cvt/strtab.h b/tools/ctf/cvt/strtab.h
new file mode 100644
index 000000000000..13c1e59cd916
--- /dev/null
+++ b/tools/ctf/cvt/strtab.h
@@ -0,0 +1,69 @@
+/*
+ * 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 (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _STRTAB_H
+#define _STRTAB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct strhash {
+ const char *str_data; /* pointer to actual string data */
+ ulong_t str_buf; /* index of string data buffer */
+ size_t str_off; /* offset in bytes of this string */
+ size_t str_len; /* length in bytes of this string */
+ struct strhash *str_next; /* next string in hash chain */
+} strhash_t;
+
+typedef struct strtab {
+ strhash_t **str_hash; /* array of hash buckets */
+ ulong_t str_hashsz; /* size of hash bucket array */
+ char **str_bufs; /* array of buffer pointers */
+ char *str_ptr; /* pointer to current buffer location */
+ ulong_t str_nbufs; /* size of buffer pointer array */
+ size_t str_bufsz; /* size of individual buffer */
+ ulong_t str_nstrs; /* total number of strings in strtab */
+ size_t str_size; /* total size of strings in bytes */
+} strtab_t;
+
+extern void strtab_create(strtab_t *);
+extern void strtab_destroy(strtab_t *);
+extern size_t strtab_insert(strtab_t *, const char *);
+extern size_t strtab_size(const strtab_t *);
+extern ssize_t strtab_write(const strtab_t *,
+ ssize_t (*)(void *, size_t, void *), void *);
+extern void strtab_print(const strtab_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STRTAB_H */
diff --git a/tools/ctf/cvt/tdata.c b/tools/ctf/cvt/tdata.c
new file mode 100644
index 000000000000..4a0cc79600e7
--- /dev/null
+++ b/tools/ctf/cvt/tdata.c
@@ -0,0 +1,488 @@
+/*
+ * 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 manipulating tdesc and tdata structures
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <pthread.h>
+
+#include "ctftools.h"
+#include "memory.h"
+#include "traverse.h"
+
+/*
+ * The layout hash is used during the equivalency checking. We have a node in
+ * the child graph that may be equivalent to a node in the parent graph. To
+ * find the corresponding node (if any) in the parent, we need a quick way to
+ * get to all nodes in the parent that look like the node in the child. Since a
+ * large number of nodes don't have names, we need to incorporate the layout of
+ * the node into the hash. If we don't, we'll end up with the vast majority of
+ * nodes in bucket zero, with one or two nodes in each of the remaining buckets.
+ *
+ * There are a couple of constraints, both of which concern forward
+ * declarations. Recall that a forward declaration tdesc is equivalent to a
+ * tdesc that actually defines the structure or union. As such, we cannot
+ * incorporate anything into the hash for a named struct or union node that
+ * couldn't be found by looking at the forward, and vice versa.
+ */
+int
+tdesc_layouthash(int nbuckets, void *node)
+{
+ tdesc_t *tdp = node;
+ char *name = NULL;
+ ulong_t h = 0;
+
+ if (tdp->t_name)
+ name = tdp->t_name;
+ else {
+ switch (tdp->t_type) {
+ case POINTER:
+ case TYPEDEF:
+ case VOLATILE:
+ case CONST:
+ case RESTRICT:
+ name = tdp->t_tdesc->t_name;
+ break;
+ case FUNCTION:
+ h = tdp->t_fndef->fn_nargs +
+ tdp->t_fndef->fn_vargs;
+ name = tdp->t_fndef->fn_ret->t_name;
+ break;
+ case ARRAY:
+ h = tdp->t_ardef->ad_nelems;
+ name = tdp->t_ardef->ad_contents->t_name;
+ break;
+ case STRUCT:
+ case UNION:
+ /*
+ * Unnamed structures, which cannot have forward
+ * declarations pointing to them. We can therefore
+ * incorporate the name of the first member into
+ * the hash value.
+ */
+ name = tdp->t_members->ml_name;
+ break;
+ case ENUM:
+ /* Use the first element in the hash value */
+ name = tdp->t_emem->el_name;
+ break;
+ default:
+ /*
+ * Intrinsics, forwards, and typedefs all have
+ * names.
+ */
+ warning("Unexpected unnamed %d tdesc (ID %d)\n",
+ tdp->t_type, tdp->t_id);
+ }
+ }
+
+ if (name)
+ return (hash_name(nbuckets, name));
+
+ return (h % nbuckets);
+}
+
+int
+tdesc_layoutcmp(void *arg1, void *arg2)
+{
+ tdesc_t *tdp1 = arg1, *tdp2 = arg2;
+
+ if (tdp1->t_name == NULL) {
+ if (tdp2->t_name == NULL)
+ return (0);
+ else
+ return (-1);
+ } else if (tdp2->t_name == NULL)
+ return (1);
+ else
+ return (strcmp(tdp1->t_name, tdp2->t_name));
+}
+
+int
+tdesc_idhash(int nbuckets, void *data)
+{
+ tdesc_t *tdp = data;
+
+ return (tdp->t_id % nbuckets);
+}
+
+int
+tdesc_idcmp(void *arg1, void *arg2)
+{
+ tdesc_t *tdp1 = arg1, *tdp2 = arg2;
+
+ if (tdp1->t_id == tdp2->t_id)
+ return (0);
+ else
+ return (tdp1->t_id > tdp2->t_id ? 1 : -1);
+}
+
+int
+tdesc_namehash(int nbuckets, void *data)
+{
+ tdesc_t *tdp = data;
+ ulong_t h, g;
+ char *c;
+
+ if (tdp->t_name == NULL)
+ return (0);
+
+ for (h = 0, c = tdp->t_name; *c; c++) {
+ h = (h << 4) + *c;
+ if ((g = (h & 0xf0000000)) != 0) {
+ h ^= (g >> 24);
+ h ^= g;
+ }
+ }
+
+ return (h % nbuckets);
+}
+
+int
+tdesc_namecmp(void *arg1, void *arg2)
+{
+ tdesc_t *tdp1 = arg1, *tdp2 = arg2;
+
+ return (!streq(tdp1->t_name, tdp2->t_name));
+}
+
+#if defined(sun)
+/*ARGSUSED1*/
+static int
+tdesc_print(void *data, void *private __unused)
+{
+ tdesc_t *tdp = data;
+
+ printf("%7d %s\n", tdp->t_id, tdesc_name(tdp));
+
+ return (1);
+}
+#endif
+
+static void
+free_intr(tdesc_t *tdp)
+{
+ free(tdp->t_intr);
+}
+
+static void
+free_ardef(tdesc_t *tdp)
+{
+ free(tdp->t_ardef);
+}
+
+static void
+free_mlist(tdesc_t *tdp)
+{
+ mlist_t *ml = tdp->t_members;
+ mlist_t *oml;
+
+ while (ml) {
+ oml = ml;
+ ml = ml->ml_next;
+
+ if (oml->ml_name)
+ free(oml->ml_name);
+ free(oml);
+ }
+}
+
+static void
+free_elist(tdesc_t *tdp)
+{
+ elist_t *el = tdp->t_emem;
+ elist_t *oel;
+
+ while (el) {
+ oel = el;
+ el = el->el_next;
+
+ if (oel->el_name)
+ free(oel->el_name);
+ free(oel);
+ }
+}
+
+static void (*free_cbs[])(tdesc_t *) = {
+ NULL,
+ free_intr,
+ NULL,
+ free_ardef,
+ NULL,
+ free_mlist,
+ free_mlist,
+ free_elist,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*ARGSUSED1*/
+static void
+tdesc_free_cb(void *arg, void *private __unused)
+{
+ tdesc_t *tdp = arg;
+ if (tdp->t_name)
+ free(tdp->t_name);
+ if (free_cbs[tdp->t_type])
+ free_cbs[tdp->t_type](tdp);
+ free(tdp);
+
+ return;
+}
+
+void
+tdesc_free(tdesc_t *tdp)
+{
+ tdesc_free_cb(tdp, NULL);
+}
+
+static int
+tdata_label_cmp(void *arg1, void *arg2)
+{
+ labelent_t *le1 = arg1;
+ labelent_t *le2 = arg2;
+ return (le1->le_idx - le2->le_idx);
+}
+
+void
+tdata_label_add(tdata_t *td, const char *label, int idx)
+{
+ labelent_t *le = xmalloc(sizeof (*le));
+
+ le->le_name = xstrdup(label);
+ le->le_idx = (idx == -1 ? td->td_nextid - 1 : idx);
+
+ slist_add(&td->td_labels, le, tdata_label_cmp);
+}
+
+static int
+tdata_label_top_cb(void *data, void *arg)
+{
+ labelent_t *le = data;
+ labelent_t **topp = arg;
+
+ *topp = le;
+
+ return (1);
+}
+
+labelent_t *
+tdata_label_top(tdata_t *td)
+{
+ labelent_t *top = NULL;
+
+ (void) list_iter(td->td_labels, tdata_label_top_cb, &top);
+
+ return (top);
+}
+
+static int
+tdata_label_find_cb(void *arg1, void *arg2)
+{
+ labelent_t *le = arg1;
+ labelent_t *tmpl = arg2;
+ return (streq(le->le_name, tmpl->le_name));
+}
+
+int
+tdata_label_find(tdata_t *td, char *label)
+{
+ labelent_t let;
+ labelent_t *ret;
+
+ if (streq(label, "BASE")) {
+ ret = (labelent_t *)list_first(td->td_labels);
+ return (ret ? ret->le_idx : -1);
+ }
+
+ let.le_name = label;
+
+ if (!(ret = (labelent_t *)list_find(td->td_labels, &let,
+ tdata_label_find_cb)))
+ return (-1);
+
+ return (ret->le_idx);
+}
+
+static int
+tdata_label_newmax_cb(void *data, void *arg)
+{
+ labelent_t *le = data;
+ int *newmaxp = arg;
+
+ if (le->le_idx > *newmaxp) {
+ le->le_idx = *newmaxp;
+ return (1);
+ }
+
+ return (0);
+}
+
+void
+tdata_label_newmax(tdata_t *td, int newmax)
+{
+ (void) list_iter(td->td_labels, tdata_label_newmax_cb, &newmax);
+}
+
+/*ARGSUSED1*/
+static void
+tdata_label_free_cb(void *arg, void *private __unused)
+{
+ labelent_t *le = arg;
+ if (le->le_name)
+ free(le->le_name);
+ free(le);
+}
+
+void
+tdata_label_free(tdata_t *td)
+{
+ list_free(td->td_labels, tdata_label_free_cb, NULL);
+ td->td_labels = NULL;
+}
+
+tdata_t *
+tdata_new(void)
+{
+ tdata_t *new = xcalloc(sizeof (tdata_t));
+
+ new->td_layouthash = hash_new(TDATA_LAYOUT_HASH_SIZE, tdesc_layouthash,
+ tdesc_layoutcmp);
+ new->td_idhash = hash_new(TDATA_ID_HASH_SIZE, tdesc_idhash,
+ tdesc_idcmp);
+ /*
+ * This is also traversed as a list, but amortized O(1)
+ * lookup massively impacts part of the merge phase, so
+ * we store the iidescs as a hash.
+ */
+ new->td_iihash = hash_new(IIDESC_HASH_SIZE, iidesc_hash, NULL);
+ new->td_nextid = 1;
+ new->td_curvgen = 1;
+
+ pthread_mutex_init(&new->td_mergelock, NULL);
+
+ return (new);
+}
+
+void
+tdata_free(tdata_t *td)
+{
+ hash_free(td->td_iihash, iidesc_free, NULL);
+ hash_free(td->td_layouthash, tdesc_free_cb, NULL);
+ hash_free(td->td_idhash, NULL, NULL);
+ list_free(td->td_fwdlist, NULL, NULL);
+
+ tdata_label_free(td);
+
+ free(td->td_parlabel);
+ free(td->td_parname);
+
+ pthread_mutex_destroy(&td->td_mergelock);
+
+ free(td);
+}
+
+/*ARGSUSED1*/
+static int
+build_hashes(tdesc_t *ctdp, tdesc_t **ctdpp __unused, void *private)
+{
+ tdata_t *td = private;
+
+ hash_add(td->td_idhash, ctdp);
+ hash_add(td->td_layouthash, ctdp);
+
+ return (1);
+}
+
+static tdtrav_cb_f build_hashes_cbs[] = {
+ NULL,
+ build_hashes, /* intrinsic */
+ build_hashes, /* pointer */
+ build_hashes, /* array */
+ build_hashes, /* function */
+ build_hashes, /* struct */
+ build_hashes, /* union */
+ build_hashes, /* enum */
+ build_hashes, /* forward */
+ build_hashes, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ build_hashes, /* volatile */
+ build_hashes, /* const */
+ build_hashes /* restrict */
+};
+
+static void
+tdata_build_hashes_common(tdata_t *td, hash_t *hash)
+{
+ (void) iitraverse_hash(hash, &td->td_curvgen, NULL, NULL,
+ build_hashes_cbs, td);
+}
+
+void
+tdata_build_hashes(tdata_t *td)
+{
+ tdata_build_hashes_common(td, td->td_iihash);
+}
+
+/* Merge td2 into td1. td2 is destroyed by the merge */
+void
+tdata_merge(tdata_t *td1, tdata_t *td2)
+{
+ td1->td_curemark = MAX(td1->td_curemark, td2->td_curemark);
+ td1->td_curvgen = MAX(td1->td_curvgen, td2->td_curvgen);
+ td1->td_nextid = MAX(td1->td_nextid, td2->td_nextid);
+
+ hash_merge(td1->td_iihash, td2->td_iihash);
+
+ /* Add td2's type tree to the hashes */
+ tdata_build_hashes_common(td1, td2->td_iihash);
+
+ list_concat(&td1->td_fwdlist, td2->td_fwdlist);
+ td2->td_fwdlist = NULL;
+
+ slist_merge(&td1->td_labels, td2->td_labels,
+ tdata_label_cmp);
+ td2->td_labels = NULL;
+
+ /* free the td2 hashes (data is now part of td1) */
+
+ hash_free(td2->td_layouthash, NULL, NULL);
+ td2->td_layouthash = NULL;
+
+ hash_free(td2->td_iihash, NULL, NULL);
+ td2->td_iihash = NULL;
+
+ tdata_free(td2);
+}
diff --git a/tools/ctf/cvt/traverse.c b/tools/ctf/cvt/traverse.c
new file mode 100644
index 000000000000..feb9908638af
--- /dev/null
+++ b/tools/ctf/cvt/traverse.c
@@ -0,0 +1,226 @@
+/*
+ * 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 used to traverse tdesc trees, invoking user-supplied callbacks
+ * as the tree is traversed.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+
+#include "ctftools.h"
+#include "traverse.h"
+#include "memory.h"
+
+int (*tddescenders[])(tdesc_t *, tdtrav_data_t *);
+tdtrav_cb_f tdnops[];
+
+void
+tdtrav_init(tdtrav_data_t *tdtd, int *vgenp, tdtrav_cb_f *firstops,
+ tdtrav_cb_f *preops, tdtrav_cb_f *postops, void *private)
+{
+ tdtd->vgen = ++(*vgenp);
+ tdtd->firstops = firstops ? firstops : tdnops;
+ tdtd->preops = preops ? preops : tdnops;
+ tdtd->postops = postops ? postops : tdnops;
+ tdtd->private = private;
+}
+
+static int
+tdtrav_plain(tdesc_t *this, tdtrav_data_t *tdtd)
+{
+ return (tdtraverse(this->t_tdesc, &this->t_tdesc, tdtd));
+}
+
+static int
+tdtrav_func(tdesc_t *this, tdtrav_data_t *tdtd)
+{
+ fndef_t *fn = this->t_fndef;
+ int i, rc;
+
+ if ((rc = tdtraverse(fn->fn_ret, &fn->fn_ret, tdtd)) < 0)
+ return (rc);
+
+ for (i = 0; i < (int) fn->fn_nargs; i++) {
+ if ((rc = tdtraverse(fn->fn_args[i], &fn->fn_args[i],
+ tdtd)) < 0)
+ return (rc);
+ }
+
+ return (0);
+}
+
+static int
+tdtrav_array(tdesc_t *this, tdtrav_data_t *tdtd)
+{
+ ardef_t *ardef = this->t_ardef;
+ int rc;
+
+ if ((rc = tdtraverse(ardef->ad_contents, &ardef->ad_contents,
+ tdtd)) < 0)
+ return (rc);
+
+ return (tdtraverse(ardef->ad_idxtype, &ardef->ad_idxtype, tdtd));
+}
+
+static int
+tdtrav_su(tdesc_t *this, tdtrav_data_t *tdtd)
+{
+ mlist_t *ml;
+ int rc = 0;
+
+ for (ml = this->t_members; ml; ml = ml->ml_next) {
+ if ((rc = tdtraverse(ml->ml_type, &ml->ml_type, tdtd)) < 0)
+ return (rc);
+ }
+
+ return (rc);
+}
+
+/*ARGSUSED*/
+int
+tdtrav_assert(tdesc_t *node __unused, tdesc_t **nodep __unused, void *private __unused)
+{
+ assert(1 == 0);
+
+ return (-1);
+}
+
+tdtrav_cb_f tdnops[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ NULL, /* array */
+ NULL, /* function */
+ NULL, /* struct */
+ NULL, /* union */
+ NULL, /* enum */
+ NULL, /* forward */
+ NULL, /* typedef */
+ NULL, /* typedef_unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL /* restrict */
+};
+
+int (*tddescenders[])(tdesc_t *, tdtrav_data_t *) = {
+ NULL,
+ NULL, /* intrinsic */
+ tdtrav_plain, /* pointer */
+ tdtrav_array, /* array */
+ tdtrav_func, /* function */
+ tdtrav_su, /* struct */
+ tdtrav_su, /* union */
+ NULL, /* enum */
+ NULL, /* forward */
+ tdtrav_plain, /* typedef */
+ NULL, /* typedef_unres */
+ tdtrav_plain, /* volatile */
+ tdtrav_plain, /* const */
+ tdtrav_plain /* restrict */
+};
+
+int
+tdtraverse(tdesc_t *this, tdesc_t **thisp, tdtrav_data_t *tdtd)
+{
+ tdtrav_cb_f travcb;
+ int (*descender)(tdesc_t *, tdtrav_data_t *);
+ int descend = 1;
+ int rc;
+
+ if ((travcb = tdtd->firstops[this->t_type]) != NULL) {
+ if ((rc = travcb(this, thisp, tdtd->private)) < 0)
+ return (rc);
+ else if (rc == 0)
+ descend = 0;
+ }
+
+ if (this->t_vgen == tdtd->vgen)
+ return (1);
+ this->t_vgen = tdtd->vgen;
+
+ if (descend && (travcb = tdtd->preops[this->t_type]) != NULL) {
+ if ((rc = travcb(this, thisp, tdtd->private)) < 0)
+ return (rc);
+ else if (rc == 0)
+ descend = 0;
+ }
+
+ if (descend) {
+ if ((descender = tddescenders[this->t_type]) != NULL &&
+ (rc = descender(this, tdtd)) < 0)
+ return (rc);
+
+ if ((travcb = tdtd->postops[this->t_type]) != NULL &&
+ (rc = travcb(this, thisp, tdtd->private)) < 0)
+ return (rc);
+ }
+
+ return (1);
+}
+
+int
+iitraverse_td(void *arg1, void *arg2)
+{
+ iidesc_t *ii = arg1;
+ tdtrav_data_t *tdtd = arg2;
+ int i, rc;
+
+ if ((rc = tdtraverse(ii->ii_dtype, &ii->ii_dtype, tdtd)) < 0)
+ return (rc);
+
+ for (i = 0; i < ii->ii_nargs; i++) {
+ if ((rc = tdtraverse(ii->ii_args[i], &ii->ii_args[i],
+ tdtd)) < 0)
+ return (rc);
+ }
+
+ return (1);
+}
+
+int
+iitraverse(iidesc_t *ii, int *vgenp, tdtrav_cb_f *firstops, tdtrav_cb_f *preops,
+ tdtrav_cb_f *postops, void *private)
+{
+ tdtrav_data_t tdtd;
+
+ tdtrav_init(&tdtd, vgenp, firstops, preops, postops, private);
+
+ return (iitraverse_td(ii, &tdtd));
+}
+
+int
+iitraverse_hash(hash_t *iihash, int *vgenp, tdtrav_cb_f *firstops,
+ tdtrav_cb_f *preops, tdtrav_cb_f *postops, void *private)
+{
+ tdtrav_data_t tdtd;
+
+ tdtrav_init(&tdtd, vgenp, firstops, preops, postops, private);
+
+ return (hash_iter(iihash, iitraverse_td, &tdtd));
+}
diff --git a/tools/ctf/cvt/traverse.h b/tools/ctf/cvt/traverse.h
new file mode 100644
index 000000000000..6a56370abc40
--- /dev/null
+++ b/tools/ctf/cvt/traverse.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef _TRAVERSE_H
+#define _TRAVERSE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines used to traverse tdesc trees, invoking user-supplied callbacks
+ * as the tree is traversed.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ctftools.h"
+
+typedef int (*tdtrav_cb_f)(tdesc_t *, tdesc_t **, void *);
+
+typedef struct tdtrav_data {
+ int vgen;
+
+ tdtrav_cb_f *firstops;
+ tdtrav_cb_f *preops;
+ tdtrav_cb_f *postops;
+
+ void *private;
+} tdtrav_data_t;
+
+void tdtrav_init(tdtrav_data_t *, int *, tdtrav_cb_f *, tdtrav_cb_f *,
+ tdtrav_cb_f *, void *);
+int tdtraverse(tdesc_t *, tdesc_t **, tdtrav_data_t *);
+
+int iitraverse(iidesc_t *, int *, tdtrav_cb_f *, tdtrav_cb_f *, tdtrav_cb_f *,
+ void *);
+int iitraverse_hash(hash_t *, int *, tdtrav_cb_f *, tdtrav_cb_f *,
+ tdtrav_cb_f *, void *);
+int iitraverse_td(void *, void *);
+
+int tdtrav_assert(tdesc_t *, tdesc_t **, void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TRAVERSE_H */
diff --git a/tools/ctf/cvt/util.c b/tools/ctf/cvt/util.c
new file mode 100644
index 000000000000..0f36fa02decf
--- /dev/null
+++ b/tools/ctf/cvt/util.c
@@ -0,0 +1,283 @@
+/*
+ * 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"
+
+/*
+ * Utility functions
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/param.h>
+
+#include "ctftools.h"
+#include "memory.h"
+
+static void (*terminate_cleanup)(void) = NULL;
+
+/* returns 1 if s1 == s2, 0 otherwise */
+int
+streq(const char *s1, const char *s2)
+{
+ if (s1 == NULL) {
+ if (s2 != NULL)
+ return (0);
+ } else if (s2 == NULL)
+ return (0);
+ else if (strcmp(s1, s2) != 0)
+ return (0);
+
+ return (1);
+}
+
+int
+findelfsecidx(Elf *elf, const char *file, const char *tofind)
+{
+ Elf_Scn *scn = NULL;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ elfterminate(file, "Couldn't read ehdr");
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ char *name;
+
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ elfterminate(file,
+ "Couldn't read header for section %d",
+ elf_ndxscn(scn));
+ }
+
+ if ((name = elf_strptr(elf, ehdr.e_shstrndx,
+ (size_t)shdr.sh_name)) == NULL) {
+ elfterminate(file,
+ "Couldn't get name for section %d",
+ elf_ndxscn(scn));
+ }
+
+ if (strcmp(name, tofind) == 0)
+ return (elf_ndxscn(scn));
+ }
+
+ return (-1);
+}
+
+size_t
+elf_ptrsz(Elf *elf)
+{
+ GElf_Ehdr ehdr;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ terminate("failed to read ELF header: %s\n",
+ elf_errmsg(-1));
+ }
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
+ return (4);
+ else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ return (8);
+ else
+ terminate("unknown ELF class %d\n", ehdr.e_ident[EI_CLASS]);
+
+ /*NOTREACHED*/
+ return (0);
+}
+
+/*PRINTFLIKE2*/
+static void
+whine(const char *type, const char *format, va_list ap)
+{
+ int error = errno;
+
+ fprintf(stderr, "%s: %s: ", type, progname);
+ vfprintf(stderr, format, ap);
+
+ if (format[strlen(format) - 1] != '\n')
+ fprintf(stderr, ": %s\n", strerror(error));
+}
+
+void
+set_terminate_cleanup(void (*cleanup)(void))
+{
+ terminate_cleanup = cleanup;
+}
+
+/*PRINTFLIKE1*/
+void
+terminate(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ whine("ERROR", format, ap);
+ va_end(ap);
+
+ if (terminate_cleanup)
+ terminate_cleanup();
+
+ if (getenv("CTF_ABORT_ON_TERMINATE") != NULL)
+ abort();
+#if defined(__FreeBSD__)
+/*
+ * For the time being just output the termination message, but don't
+ * return an exit status that would cause the build to fail. We need
+ * to get as much stuff built as possible before going back and
+ * figuring out what is wrong with certain files.
+ */
+ exit(0);
+#else
+ exit(1);
+#endif
+}
+
+/*PRINTFLIKE1*/
+void
+aborterr(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ whine("ERROR", format, ap);
+ va_end(ap);
+
+#if defined(sun)
+ abort();
+#else
+ exit(0);
+#endif
+}
+
+/*PRINTFLIKE1*/
+void
+warning(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ whine("WARNING", format, ap);
+ va_end(ap);
+
+ if (debug_level >= 3)
+ terminate("Termination due to warning\n");
+}
+
+/*PRINTFLIKE2*/
+void
+vadebug(int level, const char *format, va_list ap)
+{
+ if (level > debug_level)
+ return;
+
+ (void) fprintf(DEBUG_STREAM, "DEBUG: ");
+ (void) vfprintf(DEBUG_STREAM, format, ap);
+ fflush(DEBUG_STREAM);
+}
+
+/*PRINTFLIKE2*/
+void
+debug(int level, const char *format, ...)
+{
+ va_list ap;
+
+ if (level > debug_level)
+ return;
+
+ va_start(ap, format);
+ (void) vadebug(level, format, ap);
+ va_end(ap);
+}
+
+char *
+mktmpname(const char *origname, const char *suffix)
+{
+ char *newname;
+
+ newname = xmalloc(strlen(origname) + strlen(suffix) + 1);
+ (void) strcpy(newname, origname);
+ (void) strcat(newname, suffix);
+ return (newname);
+}
+
+/*PRINTFLIKE2*/
+void
+elfterminate(const char *file, const char *fmt, ...)
+{
+ static char msgbuf[BUFSIZ];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof (msgbuf), fmt, ap);
+ va_end(ap);
+
+ terminate("%s: %s: %s\n", file, msgbuf, elf_errmsg(-1));
+}
+
+const char *
+tdesc_name(tdesc_t *tdp)
+{
+ return (tdp->t_name == NULL ? "(anon)" : tdp->t_name);
+}
+
+char *watch_address = NULL;
+int watch_length = 0;
+
+void
+watch_set(void *addr, int len)
+{
+ watch_address = addr;
+ watch_length = len;
+}
+
+void
+watch_dump(int v)
+{
+ char *p = watch_address;
+ int i;
+
+ if (watch_address == NULL || watch_length == 0)
+ return;
+
+ printf("%d: watch %p len %d\n",v,watch_address,watch_length);
+ for (i = 0; i < watch_length; i++) {
+ if (*p >= 0x20 && *p < 0x7f) {
+ printf(" %c",*p++ & 0xff);
+ } else {
+ printf(" %02x",*p++ & 0xff);
+ }
+ }
+ printf("\n");
+
+}
+
+
diff --git a/tools/ctf/dump/dump.c b/tools/ctf/dump/dump.c
new file mode 100644
index 000000000000..740485ddff03
--- /dev/null
+++ b/tools/ctf/dump/dump.c
@@ -0,0 +1,1028 @@
+/*
+ * 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 <sys/types.h>
+#include <sys/sysmacros.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <strings.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <zlib.h>
+
+#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 */
+
+ /*
+ * 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);
+
+ 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++) {
+ 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++);
+ 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;
+
+ v = (void *) (cd->cd_ctfdata + hp->cth_typeoff);
+ const ushort_t *end = v;
+
+ 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);
+ 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) {
+ (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++);
+
+ if (n != 0) {
+ (void) printf("%u", *fp++);
+ for (i = 1; i < n; i++)
+ (void) printf(", %u", *fp++);
+ }
+
+ (void) printf(")\n");
+ } else
+ fp += n + 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;
+
+ ulong_t id;
+
+ 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");
+
+ id = 1;
+ if (hp->cth_parlabel || hp->cth_parname)
+ id += 1 << CTF_PARENT_SHIFT;
+
+ for (/* */; tp < end; id++) {
+ ulong_t i, n = CTF_INFO_VLEN(tp->ctt_info);
+ size_t size, increment, vlen = 0;
+ int kind = CTF_INFO_KIND(tp->ctt_info);
+
+ union {
+ const void *ptr;
+ ctf_array_t *ap;
+ const ctf_member_t *mp;
+ const ctf_lmember_t *lmp;
+ const ctf_enum_t *ep;
+ const ushort_t *argp;
+ } u;
+
+ 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;
+ }
+ u.ptr = (const char *)tp + increment;
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ if (flags != F_STATS) {
+ uint_t encoding = *((const uint_t *)u.ptr);
+
+ (void) printf("INTEGER %s encoding=%s offset=%u"
+ " bits=%u", ref_to_str(tp->ctt_name, hp,
+ cd), int_encoding_to_str(
+ CTF_INT_ENCODING(encoding)),
+ CTF_INT_OFFSET(encoding),
+ CTF_INT_BITS(encoding));
+ }
+ vlen = sizeof (uint_t);
+ break;
+
+ case CTF_K_FLOAT:
+ if (flags != F_STATS) {
+ uint_t encoding = *((const uint_t *)u.ptr);
+
+ (void) printf("FLOAT %s encoding=%s offset=%u "
+ "bits=%u", ref_to_str(tp->ctt_name, hp,
+ cd), fp_encoding_to_str(
+ CTF_FP_ENCODING(encoding)),
+ CTF_FP_OFFSET(encoding),
+ CTF_FP_BITS(encoding));
+ }
+ vlen = sizeof (uint_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);
+ }
+ break;
+
+ case CTF_K_ARRAY:
+ 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);
+ }
+ vlen = sizeof (ctf_array_t);
+ break;
+
+ 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);
+
+ if (n != 0) {
+ (void) printf("%u", *u.argp++);
+ for (i = 1; i < n; i++, u.argp++)
+ (void) printf(", %u", *u.argp);
+ }
+
+ (void) printf(")");
+ }
+
+ vlen = sizeof (ushort_t) * (n + (n & 1));
+ 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));
+ }
+ } 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);
+ }
+ }
+ }
+
+ vlen = n * (size >= CTF_LSTRUCT_THRESH ?
+ sizeof (ctf_lmember_t) : sizeof (ctf_member_t));
+ break;
+
+ case CTF_K_ENUM:
+ if (flags != F_STATS) {
+ (void) printf("ENUM %s\n",
+ ref_to_str(tp->ctt_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));
+ }
+ 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);
+ }
+ 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);
+ }
+ 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);
+ }
+ 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);
+ }
+ 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);
+ }
+
+ 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) {
+ v = (void *) cd.cd_ctfdata;
+ hp = v;
+ cd.cd_ctfdata = (caddr_t)cd.cd_ctfdata + sizeof (ctf_header_t);
+
+ if (cd.cd_ctflen < sizeof (ctf_header_t)) {
+ die("%s does not contain a v%d CTF header\n", filename,
+ CTF_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);
+}