From 38c68f8a196ef330599c13b5caa9d0bc5c5bff0c Mon Sep 17 00:00:00 2001 From: John Birrell Date: Fri, 25 Apr 2008 09:07:28 +0000 Subject: [PATCH] Vendor import of the CTF (Compact C Type Format) code used by DTrace from OpenSolaris. --- tools/ctf/common/ctf_headers.h | 72 ++ tools/ctf/common/list.c | 228 ++++ tools/ctf/common/list.h | 58 + tools/ctf/common/memory.c | 103 ++ tools/ctf/common/memory.h | 52 + tools/ctf/common/symbol.c | 62 ++ tools/ctf/common/symbol.h | 44 + tools/ctf/common/utils.c | 104 ++ tools/ctf/common/utils.h | 53 + tools/ctf/cvt/alist.c | 215 ++++ tools/ctf/cvt/alist.h | 57 + tools/ctf/cvt/barrier.c | 92 ++ tools/ctf/cvt/barrier.h | 62 ++ tools/ctf/cvt/compare.c | 92 ++ tools/ctf/cvt/ctf.c | 1263 ++++++++++++++++++++++ tools/ctf/cvt/ctfconvert.c | 263 +++++ tools/ctf/cvt/ctfmerge.c | 1004 +++++++++++++++++ tools/ctf/cvt/ctfmerge.h | 89 ++ tools/ctf/cvt/ctftools.h | 453 ++++++++ tools/ctf/cvt/dwarf.c | 1848 ++++++++++++++++++++++++++++++++ tools/ctf/cvt/fifo.c | 153 +++ tools/ctf/cvt/fifo.h | 54 + tools/ctf/cvt/fixup_tdescs.c | 279 +++++ tools/ctf/cvt/hash.c | 291 +++++ tools/ctf/cvt/hash.h | 59 + tools/ctf/cvt/iidesc.c | 197 ++++ tools/ctf/cvt/input.c | 419 ++++++++ tools/ctf/cvt/merge.c | 1143 ++++++++++++++++++++ tools/ctf/cvt/output.c | 757 +++++++++++++ tools/ctf/cvt/st_parse.c | 1198 +++++++++++++++++++++ tools/ctf/cvt/stabs.c | 381 +++++++ tools/ctf/cvt/stack.c | 112 ++ tools/ctf/cvt/stack.h | 53 + tools/ctf/cvt/strtab.c | 258 +++++ tools/ctf/cvt/strtab.h | 69 ++ tools/ctf/cvt/tdata.c | 488 +++++++++ tools/ctf/cvt/traverse.c | 226 ++++ tools/ctf/cvt/traverse.h | 71 ++ tools/ctf/cvt/util.c | 283 +++++ tools/ctf/dump/dump.c | 1028 ++++++++++++++++++ 40 files changed, 13733 insertions(+) create mode 100644 tools/ctf/common/ctf_headers.h create mode 100644 tools/ctf/common/list.c create mode 100644 tools/ctf/common/list.h create mode 100644 tools/ctf/common/memory.c create mode 100644 tools/ctf/common/memory.h create mode 100644 tools/ctf/common/symbol.c create mode 100644 tools/ctf/common/symbol.h create mode 100644 tools/ctf/common/utils.c create mode 100644 tools/ctf/common/utils.h create mode 100644 tools/ctf/cvt/alist.c create mode 100644 tools/ctf/cvt/alist.h create mode 100644 tools/ctf/cvt/barrier.c create mode 100644 tools/ctf/cvt/barrier.h create mode 100644 tools/ctf/cvt/compare.c create mode 100644 tools/ctf/cvt/ctf.c create mode 100644 tools/ctf/cvt/ctfconvert.c create mode 100644 tools/ctf/cvt/ctfmerge.c create mode 100644 tools/ctf/cvt/ctfmerge.h create mode 100644 tools/ctf/cvt/ctftools.h create mode 100644 tools/ctf/cvt/dwarf.c create mode 100644 tools/ctf/cvt/fifo.c create mode 100644 tools/ctf/cvt/fifo.h create mode 100644 tools/ctf/cvt/fixup_tdescs.c create mode 100644 tools/ctf/cvt/hash.c create mode 100644 tools/ctf/cvt/hash.h create mode 100644 tools/ctf/cvt/iidesc.c create mode 100644 tools/ctf/cvt/input.c create mode 100644 tools/ctf/cvt/merge.c create mode 100644 tools/ctf/cvt/output.c create mode 100644 tools/ctf/cvt/st_parse.c create mode 100644 tools/ctf/cvt/stabs.c create mode 100644 tools/ctf/cvt/stack.c create mode 100644 tools/ctf/cvt/stack.h create mode 100644 tools/ctf/cvt/strtab.c create mode 100644 tools/ctf/cvt/strtab.h create mode 100644 tools/ctf/cvt/tdata.c create mode 100644 tools/ctf/cvt/traverse.c create mode 100644 tools/ctf/cvt/traverse.h create mode 100644 tools/ctf/cvt/util.c create mode 100644 tools/ctf/dump/dump.c 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 header includes + * which in turn includes 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 is included via a nested #include rather than + * an explicit path #include. + */ + +#include +#include +#include + +#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 +#include +#include + +#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 +#include +#include +#include +#include +#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 + +#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 +#include + +#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 + +#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 +#include +#include +#include +#include + +#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 + +#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 +#include + +#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 +#if defined(sun) +#include +#endif +#include + +#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 +#else +#include +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 +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#include "ctf_headers.h" +#include "ctftools.h" +#include "strtab.h" +#include "memory.h" + +/* + * Name of the file currently being read, used to print error messages. We + * assume that only one file will be read at a time, and thus make no attempt + * to allow curfile to be used simultaneously by multiple threads. + * + * The value is only valid during a call to ctf_load. + */ +char *curfile; + +#define CTF_BUF_CHUNK_SIZE (64 * 1024) +#define RES_BUF_CHUNK_SIZE (64 * 1024) + +struct ctf_buf { + strtab_t ctb_strtab; /* string table */ + caddr_t ctb_base; /* pointer to base of buffer */ + caddr_t ctb_end; /* pointer to end of buffer */ + caddr_t ctb_ptr; /* pointer to empty buffer space */ + size_t ctb_size; /* size of buffer */ + int nptent; /* number of processed types */ + int ntholes; /* number of type holes */ +}; + +/*PRINTFLIKE1*/ +static void +parseterminate(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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#if defined(sun) +#include +#endif +#include +#include +#include +#include +#if defined(sun) +#include +#endif +#include +#include +#include +#if defined(sun) +#include +#endif + +#include "ctf_headers.h" +#include "ctftools.h" +#include "ctfmerge.h" +#include "traverse.h" +#include "memory.h" +#include "fifo.h" +#include "barrier.h" + +#pragma init(bigheap) + +#define MERGE_PHASE1_BATCH_SIZE 8 +#define MERGE_PHASE1_MAX_SLOTS 5 +#define MERGE_INPUT_THROTTLE_LEN 10 + +const char *progname; +static char *outfile = NULL; +static char *tmpname = NULL; +static int dynsym; +int debug_level = DEBUG_LEVEL; +static size_t maxpgsize = 0x400000; + + +void +usage(void) +{ + (void) fprintf(stderr, + "Usage: %s [-fgstv] -l label | -L labelenv -o outfile file ...\n" + " %s [-fgstv] -l label | -L labelenv -o outfile -d uniqfile\n" + " %*s [-g] [-D uniqlabel] file ...\n" + " %s [-fgstv] -l label | -L labelenv -o outfile -w withfile " + "file ...\n" + " %s [-g] -c srcfile destfile\n" + "\n" + " Note: if -L labelenv is specified and labelenv is not set in\n" + " the environment, a default value is used.\n", + progname, progname, 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 + +#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 +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "list.h" +#include "hash.h" + +#ifndef DEBUG_LEVEL +#define DEBUG_LEVEL 0 +#endif +#ifndef DEBUG_PARSE +#define DEBUG_PARSE 0 +#endif + +#ifndef DEBUG_STREAM +#define DEBUG_STREAM stderr +#endif + +#ifndef MAX +#define MAX(a, b) ((a) < (b) ? (b) : (a)) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) > (b) ? (b) : (a)) +#endif + +#define TRUE 1 +#define FALSE 0 + +#define CTF_ELF_SCN_NAME ".SUNW_ctf" + +#define CTF_LABEL_LASTIDX -1 + +#define CTF_DEFAULT_LABEL "*** No Label Provided ***" + +/* + * Default hash sizes + */ +#define TDATA_LAYOUT_HASH_SIZE 8191 /* A tdesc hash based on layout */ +#define TDATA_ID_HASH_SIZE 997 /* A tdesc hash based on type id */ +#define IIDESC_HASH_SIZE 8191 /* Hash of iidesc's */ + +/* + * The default function argument array size. We'll realloc the array larger + * if we need to, but we want a default value that will allow us to avoid + * reallocation in the common case. + */ +#define FUNCARG_DEF 5 + +extern const char *progname; +extern int debug_level; +extern int debug_parse; +extern char *curhdr; + +/* + * This is a partial copy of the stab.h that DevPro includes with their + * compiler. + */ +typedef struct stab { + uint32_t n_strx; + uint8_t n_type; + int8_t n_other; + int16_t n_desc; + uint32_t n_value; +} stab_t; + +#define N_GSYM 0x20 /* global symbol: name,,0,type,0 */ +#define N_FUN 0x24 /* procedure: name,,0,linenumber,0 */ +#define N_STSYM 0x26 /* static symbol: name,,0,type,0 or section relative */ +#define N_LCSYM 0x28 /* .lcomm symbol: name,,0,type,0 or section relative */ +#define N_ROSYM 0x2c /* ro_data: name,,0,type,0 or section relative */ +#define N_OPT 0x3c /* compiler options */ +#define N_RSYM 0x40 /* register sym: name,,0,type,register */ +#define N_SO 0x64 /* source file name: name,,0,0,0 */ +#define N_LSYM 0x80 /* local sym: name,,0,type,offset */ +#define N_SOL 0x84 /* #included file name: name,,0,0,0 */ +#define N_PSYM 0xa0 /* parameter: name,,0,type,offset */ +#define N_LBRAC 0xc0 /* left bracket: 0,,0,nesting level,function relative */ +#define N_RBRAC 0xe0 /* right bracket: 0,,0,nesting level,func relative */ +#define N_BINCL 0x82 /* header file: name,,0,0,0 */ +#define N_EINCL 0xa2 /* end of include file */ + +/* + * Nodes in the type tree + * + * Each node consists of a single tdesc_t, with one of several auxiliary + * structures linked in via the `data' union. + */ + +/* The type of tdesc_t node */ +typedef enum stabtype { + STABTYPE_FIRST, /* do not use */ + INTRINSIC, + POINTER, + ARRAY, + FUNCTION, + STRUCT, + UNION, + ENUM, + FORWARD, + TYPEDEF, + TYPEDEF_UNRES, + VOLATILE, + CONST, + RESTRICT, + STABTYPE_LAST /* do not use */ +} stabtype_t; + +typedef struct tdesc tdesc_t; + +/* Auxiliary structure for array tdesc_t */ +typedef struct ardef { + tdesc_t *ad_contents; + tdesc_t *ad_idxtype; + uint_t ad_nelems; +} ardef_t; + +/* Auxiliary structure for structure/union tdesc_t */ +typedef struct mlist { + int ml_offset; /* Offset from start of structure (in bits) */ + int ml_size; /* Member size (in bits) */ + char *ml_name; /* Member name */ + struct tdesc *ml_type; /* Member type */ + struct mlist *ml_next; /* Next member */ +} mlist_t; + +/* Auxiliary structure for enum tdesc_t */ +typedef struct elist { + char *el_name; + int el_number; + struct elist *el_next; +} elist_t; + +/* Auxiliary structure for intrinsics (integers and reals) */ +typedef enum { + INTR_INT, + INTR_REAL +} intrtype_t; + +typedef struct intr { + intrtype_t intr_type; + int intr_signed; + union { + char _iformat; + int _fformat; + } _u; + int intr_offset; + int intr_nbits; +} intr_t; + +#define intr_iformat _u._iformat +#define intr_fformat _u._fformat + +typedef struct fnarg { + char *fna_name; + struct tdesc *fna_type; +} fnarg_t; + +#define FN_F_GLOBAL 0x1 +#define FN_F_VARARGS 0x2 + +typedef struct fndef { + struct tdesc *fn_ret; + uint_t fn_nargs; + tdesc_t **fn_args; + uint_t fn_vargs; +} fndef_t; + +typedef int32_t tid_t; + +/* + * The tdesc_t (Type DESCription) is the basic node type used in the stabs data + * structure. Each data node gets a tdesc structure. Each node is linked into + * a directed graph (think of it as a tree with multiple roots and multiple + * leaves), with the root nodes at the top, and intrinsics at the bottom. The + * root nodes, which are pointed to by iidesc nodes, correspond to the types, + * globals, and statics defined by the stabs. + */ +struct tdesc { + char *t_name; + tdesc_t *t_next; /* Name hash next pointer */ + + tid_t t_id; + tdesc_t *t_hash; /* ID hash next pointer */ + + stabtype_t t_type; + int t_size; /* Size in bytes of object represented by this node */ + + union { + intr_t *intr; /* int, real */ + tdesc_t *tdesc; /* ptr, typedef, vol, const, restr */ + ardef_t *ardef; /* array */ + mlist_t *members; /* struct, union */ + elist_t *emem; /* enum */ + fndef_t *fndef; /* function - first is return type */ + } t_data; + + int t_flags; + int t_vgen; /* Visitation generation (see traverse.c) */ + int t_emark; /* Equality mark (see equiv_cb() in merge.c) */ +}; + +#define t_intr t_data.intr +#define t_tdesc t_data.tdesc +#define t_ardef t_data.ardef +#define t_members t_data.members +#define t_emem t_data.emem +#define t_fndef t_data.fndef + +#define TDESC_F_ISROOT 0x1 /* Has an iidesc_t (see below) */ +#define TDESC_F_GLOBAL 0x2 +#define TDESC_F_RESOLVED 0x4 + +/* + * iidesc_t (Interesting Item DESCription) nodes point to tdesc_t nodes that + * correspond to "interesting" stabs. A stab is interesting if it defines a + * global or static variable, a global or static function, or a data type. + */ +typedef enum iitype { + II_NOT = 0, + II_GFUN, /* Global function */ + II_SFUN, /* Static function */ + II_GVAR, /* Global variable */ + II_SVAR, /* Static variable */ + II_PSYM, /* Function argument */ + II_SOU, /* Struct or union */ + II_TYPE /* Type (typedef) */ +} iitype_t; + +typedef struct iidesc { + iitype_t ii_type; + char *ii_name; + tdesc_t *ii_dtype; + char *ii_owner; /* File that defined this node */ + int ii_flags; + + /* Function arguments (if any) */ + int ii_nargs; + tdesc_t **ii_args; + int ii_vargs; /* Function uses varargs */ +} iidesc_t; + +#define IIDESC_F_USED 0x1 /* Write this iidesc out */ + +/* + * labelent_t nodes identify labels and corresponding type ranges associated + * with them. The label in a given labelent_t is associated with types with + * ids <= le_idx. + */ +typedef struct labelent { + char *le_name; + int le_idx; +} labelent_t; + +/* + * The tdata_t (Type DATA) structure contains or references all type data for + * a given file or, during merging, several files. + */ +typedef struct tdata { + int td_curemark; /* Equality mark (see merge.c) */ + int td_curvgen; /* Visitation generation (see traverse.c) */ + int td_nextid; /* The ID for the next tdesc_t created */ + hash_t *td_iihash; /* The iidesc_t nodes for this file */ + + hash_t *td_layouthash; /* The tdesc nodes, hashed by structure */ + hash_t *td_idhash; /* The tdesc nodes, hashed by type id */ + list_t *td_fwdlist; /* All forward declaration tdesc nodes */ + + char *td_parlabel; /* Top label uniq'd against in parent */ + char *td_parname; /* Basename of parent */ + list_t *td_labels; /* Labels and their type ranges */ + + pthread_mutex_t td_mergelock; + + int td_ref; +} tdata_t; + +/* + * By design, the iidesc hash is heterogeneous. The CTF emitter, on the + * other hand, needs to be able to access the elements of the list by type, + * and in a specific sorted order. An iiburst holds these elements in that + * order. (A burster is a machine that separates carbon-copy forms) + */ +typedef struct iiburst { + int iib_nfuncs; + int iib_curfunc; + iidesc_t **iib_funcs; + + int iib_nobjts; + int iib_curobjt; + iidesc_t **iib_objts; + + list_t *iib_types; + int iib_maxtypeid; + + tdata_t *iib_td; + struct tdtrav_data *iib_tdtd; /* tdtrav_data_t */ +} iiburst_t; + +typedef struct ctf_buf ctf_buf_t; + +typedef struct symit_data symit_data_t; + +/* fixup_tdescs.c */ +void cvt_fixstabs(tdata_t *); +void cvt_fixups(tdata_t *, size_t); + +/* ctf.c */ +caddr_t ctf_gen(iiburst_t *, size_t *, int); +tdata_t *ctf_load(char *, caddr_t, size_t, symit_data_t *, char *); + +/* iidesc.c */ +iidesc_t *iidesc_new(char *); +int iidesc_hash(int, void *); +void iter_iidescs_by_name(tdata_t *, const char *, + int (*)(void *, void *), void *); +iidesc_t *iidesc_dup(iidesc_t *); +iidesc_t *iidesc_dup_rename(iidesc_t *, char const *, char const *); +void iidesc_add(hash_t *, iidesc_t *); +void iidesc_free(void *, void *); +int iidesc_count_type(void *, void *); +void iidesc_stats(hash_t *); +int iidesc_dump(iidesc_t *); + +/* input.c */ +typedef enum source_types { + SOURCE_NONE = 0, + SOURCE_UNKNOWN = 1, + SOURCE_C = 2, + SOURCE_S = 4 +} source_types_t; + +source_types_t built_source_types(Elf *, const char *); +int count_files(char **, int); +int read_ctf(char **, int, char *, int (*)(tdata_t *, char *, void *), + void *, int); +int read_ctf_save_cb(tdata_t *, char *, void *); +symit_data_t *symit_new(Elf *, const char *); +void symit_reset(symit_data_t *); +char *symit_curfile(symit_data_t *); +GElf_Sym *symit_next(symit_data_t *, int); +char *symit_name(symit_data_t *); +void symit_free(symit_data_t *); + +/* merge.c */ +void merge_into_master(tdata_t *, tdata_t *, tdata_t *, int); + +/* output.c */ +#define CTF_FUZZY_MATCH 0x1 /* match local symbols to global CTF */ +#define CTF_USE_DYNSYM 0x2 /* use .dynsym not .symtab */ +#define CTF_COMPRESS 0x4 /* compress CTF output */ +#define CTF_KEEP_STABS 0x8 /* keep .stabs sections */ + +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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctf_headers.h" +#include "ctftools.h" +#include "memory.h" +#include "list.h" +#include "traverse.h" + +/* The version of DWARF which we support. */ +#define DWARF_VERSION 2 + +/* + * We need to define a couple of our own intrinsics, to smooth out some of the + * differences between the GCC and DevPro DWARF emitters. See the referenced + * routines and the special cases in the file comment for more details. + * + * Type IDs are 32 bits wide. We're going to use the top of that field to + * indicate types that we've created ourselves. + */ +#define TID_FILEMAX 0x3fffffff /* highest tid from file */ +#define TID_VOID 0x40000001 /* see die_void() */ +#define TID_LONG 0x40000002 /* see die_array() */ + +#define TID_MFGTID_BASE 0x40000003 /* first mfg'd tid */ + +/* + * To reduce the staggering amount of error-handling code that would otherwise + * be required, the attribute-retrieval routines handle most of their own + * errors. If the following flag is supplied as the value of the `req' + * argument, they will also handle the absence of a requested attribute by + * terminating the program. + */ +#define DW_ATTR_REQ 1 + +#define TDESC_HASH_BUCKETS 511 + +typedef struct dwarf { + Dwarf_Debug dw_dw; /* for libdwarf */ + Dwarf_Error dw_err; /* for libdwarf */ + Dwarf_Off dw_maxoff; /* highest legal offset in this cu */ + tdata_t *dw_td; /* root of the tdesc/iidesc tree */ + hash_t *dw_tidhash; /* hash of tdescs by t_id */ + hash_t *dw_fwdhash; /* hash of fwd decls by name */ + hash_t *dw_enumhash; /* hash of memberless enums by name */ + tdesc_t *dw_void; /* manufactured void type */ + tdesc_t *dw_long; /* manufactured long type for arrays */ + size_t dw_ptrsz; /* size of a pointer in this file */ + tid_t dw_mfgtid_last; /* last mfg'd type ID used */ + uint_t dw_nunres; /* count of unresolved types */ + char *dw_cuname; /* name of compilation unit */ +} dwarf_t; + +static void die_create_one(dwarf_t *, Dwarf_Die); +static void die_create(dwarf_t *, Dwarf_Die); + +static tid_t +mfgtid_next(dwarf_t *dw) +{ + return (++dw->dw_mfgtid_last); +} + +static void +tdesc_add(dwarf_t *dw, tdesc_t *tdp) +{ + hash_add(dw->dw_tidhash, tdp); +} + +static tdesc_t * +tdesc_lookup(dwarf_t *dw, int tid) +{ + tdesc_t tmpl; + void *tdp; + + tmpl.t_id = tid; + + if (hash_find(dw->dw_tidhash, &tmpl, &tdp)) + return (tdp); + else + return (NULL); +} + +/* + * Resolve a tdesc down to a node which should have a size. Returns the size, + * zero if the size hasn't yet been determined. + */ +static size_t +tdesc_size(tdesc_t *tdp) +{ + for (;;) { + switch (tdp->t_type) { + case INTRINSIC: + case POINTER: + case ARRAY: + case FUNCTION: + case STRUCT: + case UNION: + case ENUM: + return (tdp->t_size); + + case FORWARD: + return (0); + + case TYPEDEF: + case VOLATILE: + case CONST: + case RESTRICT: + tdp = tdp->t_tdesc; + continue; + + case 0: /* not yet defined */ + return (0); + + default: + terminate("tdp %u: tdesc_size on unknown type %d\n", + tdp->t_id, tdp->t_type); + } + } +} + +static size_t +tdesc_bitsize(tdesc_t *tdp) +{ + for (;;) { + switch (tdp->t_type) { + case INTRINSIC: + return (tdp->t_intr->intr_nbits); + + case ARRAY: + case FUNCTION: + case STRUCT: + case UNION: + case ENUM: + case POINTER: + return (tdp->t_size * NBBY); + + case FORWARD: + return (0); + + case TYPEDEF: + case VOLATILE: + case RESTRICT: + case CONST: + tdp = tdp->t_tdesc; + continue; + + case 0: /* not yet defined */ + return (0); + + default: + terminate("tdp %u: tdesc_bitsize on unknown type %d\n", + tdp->t_id, tdp->t_type); + } + } +} + +static tdesc_t * +tdesc_basetype(tdesc_t *tdp) +{ + for (;;) { + switch (tdp->t_type) { + case TYPEDEF: + case VOLATILE: + case RESTRICT: + case CONST: + tdp = tdp->t_tdesc; + break; + case 0: /* not yet defined */ + return (NULL); + default: + return (tdp); + } + } +} + +static Dwarf_Off +die_off(dwarf_t *dw, Dwarf_Die die) +{ + Dwarf_Off off; + + if (dwarf_dieoffset(die, &off, &dw->dw_err) == DW_DLV_OK) + return (off); + + terminate("failed to get offset for die: %s\n", + dwarf_errmsg(&dw->dw_err)); + /*NOTREACHED*/ + return (0); +} + +static Dwarf_Die +die_sibling(dwarf_t *dw, Dwarf_Die die) +{ + Dwarf_Die sib; + int rc; + + if ((rc = dwarf_siblingof(dw->dw_dw, die, &sib, &dw->dw_err)) == + DW_DLV_OK) + return (sib); + else if (rc == DW_DLV_NO_ENTRY) + return (NULL); + + terminate("die %llu: failed to find type sibling: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + /*NOTREACHED*/ + return (NULL); +} + +static Dwarf_Die +die_child(dwarf_t *dw, Dwarf_Die die) +{ + Dwarf_Die child; + int rc; + + if ((rc = dwarf_child(die, &child, &dw->dw_err)) == DW_DLV_OK) + return (child); + else if (rc == DW_DLV_NO_ENTRY) + return (NULL); + + terminate("die %llu: failed to find type child: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + /*NOTREACHED*/ + return (NULL); +} + +static Dwarf_Half +die_tag(dwarf_t *dw, Dwarf_Die die) +{ + Dwarf_Half tag; + + if (dwarf_tag(die, &tag, &dw->dw_err) == DW_DLV_OK) + return (tag); + + terminate("die %llu: failed to get tag for type: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + /*NOTREACHED*/ + return (0); +} + +static Dwarf_Attribute +die_attr(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, int req) +{ + Dwarf_Attribute attr; + int rc; + + if ((rc = dwarf_attr(die, name, &attr, &dw->dw_err)) == DW_DLV_OK) { + return (attr); + } else if (rc == DW_DLV_NO_ENTRY) { + if (req) { + terminate("die %llu: no attr 0x%x\n", die_off(dw, die), + name); + } else { + return (NULL); + } + } + + terminate("die %llu: failed to get attribute for type: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + /*NOTREACHED*/ + return (NULL); +} + +static int +die_signed(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Signed *valp, + int req) +{ + *valp = 0; + if (dwarf_attrval_signed(die, name, valp, &dw->dw_err) != DWARF_E_NONE) { + if (req) + terminate("die %llu: failed to get signed: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + return (0); + } + + return (1); +} + +static int +die_unsigned(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Unsigned *valp, + int req) +{ + *valp = 0; + if (dwarf_attrval_unsigned(die, name, valp, &dw->dw_err) != DWARF_E_NONE) { + if (req) + terminate("die %llu: failed to get unsigned: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + return (0); + } + + return (1); +} + +static int +die_bool(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Bool *valp, int req) +{ + *valp = 0; + + if (dwarf_attrval_flag(die, name, valp, &dw->dw_err) != DWARF_E_NONE) { + if (req) + terminate("die %llu: failed to get flag: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + return (0); + } + + return (1); +} + +static int +die_string(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, char **strp, int req) +{ + const char *str = NULL; + + if (dwarf_attrval_string(die, name, &str, &dw->dw_err) != DWARF_E_NONE || + str == NULL) { + if (req) + terminate("die %llu: failed to get string: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + else + *strp = NULL; + return (0); + } else + *strp = xstrdup(str); + + return (1); +} + +static Dwarf_Off +die_attr_ref(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name) +{ + Dwarf_Off off; + + if (dwarf_attrval_unsigned(die, name, &off, &dw->dw_err) != DWARF_E_NONE) { + terminate("die %llu: failed to get ref: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + } + + return (off); +} + +static char * +die_name(dwarf_t *dw, Dwarf_Die die) +{ + char *str = NULL; + + (void) die_string(dw, die, DW_AT_name, &str, 0); + + return (str); +} + +static int +die_isdecl(dwarf_t *dw, Dwarf_Die die) +{ + Dwarf_Bool val; + + return (die_bool(dw, die, DW_AT_declaration, &val, 0) && val); +} + +static int +die_isglobal(dwarf_t *dw, Dwarf_Die die) +{ + Dwarf_Signed vis; + Dwarf_Bool ext; + + /* + * Some compilers (gcc) use DW_AT_external to indicate function + * visibility. Others (Sun) use DW_AT_visibility. + */ + if (die_signed(dw, die, DW_AT_visibility, &vis, 0)) + return (vis == DW_VIS_exported); + else + return (die_bool(dw, die, DW_AT_external, &ext, 0) && ext); +} + +static tdesc_t * +die_add(dwarf_t *dw, Dwarf_Off off) +{ + tdesc_t *tdp = xcalloc(sizeof (tdesc_t)); + + tdp->t_id = off; + + tdesc_add(dw, tdp); + + return (tdp); +} + +static tdesc_t * +die_lookup_pass1(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name) +{ + Dwarf_Off ref = die_attr_ref(dw, die, name); + tdesc_t *tdp; + + if ((tdp = tdesc_lookup(dw, ref)) != NULL) + return (tdp); + + return (die_add(dw, ref)); +} + +static int +die_mem_offset(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, + Dwarf_Unsigned *valp, int req __unused) +{ + Dwarf_Locdesc *loc = NULL; + Dwarf_Signed locnum = 0; + + if (dwarf_locdesc(die, name, &loc, &locnum, &dw->dw_err) != DW_DLV_OK) + return (0); + + if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) { + terminate("die %llu: cannot parse member offset\n", + die_off(dw, die)); + } + + *valp = loc->ld_s->lr_number; + + if (loc != NULL) + if (dwarf_locdesc_free(loc, &dw->dw_err) != DW_DLV_OK) + terminate("die %llu: cannot free location descriptor: %s\n", + die_off(dw, die), dwarf_errmsg(&dw->dw_err)); + + return (1); +} + +static tdesc_t * +tdesc_intr_common(dwarf_t *dw, int tid, const char *name, size_t sz) +{ + tdesc_t *tdp; + intr_t *intr; + + intr = xcalloc(sizeof (intr_t)); + intr->intr_type = INTR_INT; + intr->intr_signed = 1; + intr->intr_nbits = sz * NBBY; + + tdp = xcalloc(sizeof (tdesc_t)); + tdp->t_name = xstrdup(name); + tdp->t_size = sz; + tdp->t_id = tid; + tdp->t_type = INTRINSIC; + tdp->t_intr = intr; + tdp->t_flags = TDESC_F_RESOLVED; + + tdesc_add(dw, tdp); + + return (tdp); +} + +/* + * Manufacture a void type. Used for gcc-emitted stabs, where the lack of a + * type reference implies a reference to a void type. A void *, for example + * will be represented by a pointer die without a DW_AT_type. CTF requires + * that pointer nodes point to something, so we'll create a void for use as + * the target. Note that the DWARF data may already create a void type. Ours + * would then be a duplicate, but it'll be removed in the self-uniquification + * merge performed at the completion of DWARF->tdesc conversion. + */ +static tdesc_t * +tdesc_intr_void(dwarf_t *dw) +{ + if (dw->dw_void == NULL) + dw->dw_void = tdesc_intr_common(dw, TID_VOID, "void", 0); + + return (dw->dw_void); +} + +static tdesc_t * +tdesc_intr_long(dwarf_t *dw) +{ + if (dw->dw_long == NULL) { + dw->dw_long = tdesc_intr_common(dw, TID_LONG, "long", + dw->dw_ptrsz); + } + + return (dw->dw_long); +} + +/* + * Used for creating bitfield types. We create a copy of an existing intrinsic, + * adjusting the size of the copy to match what the caller requested. The + * caller can then use the copy as the type for a bitfield structure member. + */ +static tdesc_t * +tdesc_intr_clone(dwarf_t *dw, tdesc_t *old, size_t bitsz) +{ + tdesc_t *new = xcalloc(sizeof (tdesc_t)); + + if (!(old->t_flags & TDESC_F_RESOLVED)) { + terminate("tdp %u: attempt to make a bit field from an " + "unresolved type\n", old->t_id); + } + + new->t_name = xstrdup(old->t_name); + new->t_size = old->t_size; + new->t_id = mfgtid_next(dw); + new->t_type = INTRINSIC; + new->t_flags = TDESC_F_RESOLVED; + + new->t_intr = xcalloc(sizeof (intr_t)); + bcopy(old->t_intr, new->t_intr, sizeof (intr_t)); + new->t_intr->intr_nbits = bitsz; + + tdesc_add(dw, new); + + return (new); +} + +static void +tdesc_array_create(dwarf_t *dw, Dwarf_Die dim, tdesc_t *arrtdp, + tdesc_t *dimtdp) +{ + Dwarf_Unsigned uval; + Dwarf_Signed sval; + tdesc_t *ctdp = NULL; + Dwarf_Die dim2; + ardef_t *ar; + + if ((dim2 = die_sibling(dw, dim)) == NULL) { + ctdp = arrtdp; + } else if (die_tag(dw, dim2) == DW_TAG_subrange_type) { + ctdp = xcalloc(sizeof (tdesc_t)); + ctdp->t_id = mfgtid_next(dw); + debug(3, "die %llu: creating new type %u for sub-dimension\n", + die_off(dw, dim2), ctdp->t_id); + tdesc_array_create(dw, dim2, arrtdp, ctdp); + } else { + terminate("die %llu: unexpected non-subrange node in array\n", + die_off(dw, dim2)); + } + + dimtdp->t_type = ARRAY; + dimtdp->t_ardef = ar = xcalloc(sizeof (ardef_t)); + + /* + * Array bounds can be signed or unsigned, but there are several kinds + * of signless forms (data1, data2, etc) that take their sign from the + * routine that is trying to interpret them. That is, data1 can be + * either signed or unsigned, depending on whether you use the signed or + * unsigned accessor function. GCC will use the signless forms to store + * unsigned values which have their high bit set, so we need to try to + * read them first as unsigned to get positive values. We could also + * try signed first, falling back to unsigned if we got a negative + * value. + */ + if (die_unsigned(dw, dim, DW_AT_upper_bound, &uval, 0)) + ar->ad_nelems = uval + 1; + else if (die_signed(dw, dim, DW_AT_upper_bound, &sval, 0)) + ar->ad_nelems = sval + 1; + else + ar->ad_nelems = 0; + + /* + * Different compilers use different index types. Force the type to be + * a common, known value (long). + */ + ar->ad_idxtype = tdesc_intr_long(dw); + ar->ad_contents = ctdp; + + if (ar->ad_contents->t_size != 0) { + dimtdp->t_size = ar->ad_contents->t_size * ar->ad_nelems; + dimtdp->t_flags |= TDESC_F_RESOLVED; + } +} + +/* + * Create a tdesc from an array node. Some arrays will come with byte size + * attributes, and thus can be resolved immediately. Others don't, and will + * need to wait until the second pass for resolution. + */ +static void +die_array_create(dwarf_t *dw, Dwarf_Die arr, Dwarf_Off off, tdesc_t *tdp) +{ + tdesc_t *arrtdp = die_lookup_pass1(dw, arr, DW_AT_type); + Dwarf_Unsigned uval; + Dwarf_Die dim; + + debug(3, "die %llu <%llx>: creating array\n", off, off); + + if ((dim = die_child(dw, arr)) == NULL || + die_tag(dw, dim) != DW_TAG_subrange_type) + terminate("die %llu: failed to retrieve array bounds\n", off); + + tdesc_array_create(dw, dim, arrtdp, tdp); + + if (die_unsigned(dw, arr, DW_AT_byte_size, &uval, 0)) { + tdesc_t *dimtdp; + int flags; + + 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 + +#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 +#include + +#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 +#include +#include +#include +#include + +#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 +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 **)<gtid)) + 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ctftools.h" +#include "list.h" +#include "memory.h" +#include "traverse.h" +#include "symbol.h" + +typedef struct iidesc_match { + int iim_fuzzy; + iidesc_t *iim_ret; + char *iim_name; + char *iim_file; + uchar_t iim_bind; +} iidesc_match_t; + +static int +burst_iitypes(void *data, void *arg) +{ + iidesc_t *ii = data; + iiburst_t *iiburst = arg; + + switch (ii->ii_type) { + case II_GFUN: + case II_SFUN: + case II_GVAR: + case II_SVAR: + if (!(ii->ii_flags & IIDESC_F_USED)) + return (0); + break; + default: + break; + } + + ii->ii_dtype->t_flags |= TDESC_F_ISROOT; + (void) iitraverse_td(ii, iiburst->iib_tdtd); + return (1); +} + +/*ARGSUSED1*/ +static int +save_type_by_id(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private) +{ + iiburst_t *iiburst = private; + + /* + * Doing this on every node is horribly inefficient, but given that + * we may be suppressing some types, we can't trust nextid in the + * tdata_t. + */ + if (tdp->t_id > iiburst->iib_maxtypeid) + iiburst->iib_maxtypeid = tdp->t_id; + + slist_add(&iiburst->iib_types, tdp, tdesc_idcmp); + + return (1); +} + +static tdtrav_cb_f burst_types_cbs[] = { + NULL, + save_type_by_id, /* intrinsic */ + save_type_by_id, /* pointer */ + save_type_by_id, /* array */ + save_type_by_id, /* function */ + save_type_by_id, /* struct */ + save_type_by_id, /* union */ + save_type_by_id, /* enum */ + save_type_by_id, /* forward */ + save_type_by_id, /* typedef */ + tdtrav_assert, /* typedef_unres */ + save_type_by_id, /* volatile */ + save_type_by_id, /* const */ + save_type_by_id /* restrict */ +}; + + +static iiburst_t * +iiburst_new(tdata_t *td, int max) +{ + iiburst_t *iiburst = xcalloc(sizeof (iiburst_t)); + iiburst->iib_td = td; + iiburst->iib_funcs = xcalloc(sizeof (iidesc_t *) * max); + iiburst->iib_nfuncs = 0; + iiburst->iib_objts = xcalloc(sizeof (iidesc_t *) * max); + iiburst->iib_nobjts = 0; + return (iiburst); +} + +static void +iiburst_types(iiburst_t *iiburst) +{ + tdtrav_data_t tdtd; + + tdtrav_init(&tdtd, &iiburst->iib_td->td_curvgen, NULL, burst_types_cbs, + NULL, (void *)iiburst); + + iiburst->iib_tdtd = &tdtd; + + (void) hash_iter(iiburst->iib_td->td_iihash, burst_iitypes, iiburst); +} + +static void +iiburst_free(iiburst_t *iiburst) +{ + free(iiburst->iib_funcs); + free(iiburst->iib_objts); + list_free(iiburst->iib_types, NULL, NULL); + free(iiburst); +} + +/* + * See if this iidesc matches the ELF symbol data we pass in. + * + * A fuzzy match is where we have a local symbol matching the name of a + * global type description. This is common when a mapfile is used for a + * DSO, but we don't accept it by default. + * + * A weak fuzzy match is when a weak symbol was resolved and matched to + * a global type description. + */ +static int +matching_iidesc(void *arg1, void *arg2) +{ + iidesc_t *iidesc = arg1; + iidesc_match_t *match = arg2; + if (streq(iidesc->ii_name, match->iim_name) == 0) + return (0); + + switch (iidesc->ii_type) { + case II_GFUN: + case II_GVAR: + if (match->iim_bind == STB_GLOBAL) { + match->iim_ret = iidesc; + return (-1); + } else if (match->iim_fuzzy && match->iim_ret == NULL) { + match->iim_ret = iidesc; + /* continue to look for strong match */ + return (0); + } + break; + case II_SFUN: + case II_SVAR: + if (match->iim_bind == STB_LOCAL && + match->iim_file != NULL && + streq(iidesc->ii_owner, match->iim_file)) { + match->iim_ret = iidesc; + return (-1); + } + break; + default: + break; + } + return (0); +} + +static iidesc_t * +find_iidesc(tdata_t *td, iidesc_match_t *match) +{ + match->iim_ret = NULL; + iter_iidescs_by_name(td, match->iim_name, + matching_iidesc, match); + return (match->iim_ret); +} + +/* + * If we have a weak symbol, attempt to find the strong symbol it will + * resolve to. Note: the code where this actually happens is in + * sym_process() in cmd/sgs/libld/common/syms.c + * + * Finding the matching symbol is unfortunately not trivial. For a + * symbol to be a candidate, it must: + * + * - have the same type (function, object) + * - have the same value (address) + * - have the same size + * - not be another weak symbol + * - belong to the same section (checked via section index) + * + * If such a candidate is global, then we assume we've found it. The + * linker generates the symbol table such that the curfile might be + * incorrect; this is OK for global symbols, since find_iidesc() doesn't + * need to check for the source file for the symbol. + * + * We might have found a strong local symbol, where the curfile is + * accurate and matches that of the weak symbol. We assume this is a + * reasonable match. + * + * If we've got a local symbol with a non-matching curfile, there are + * two possibilities. Either this is a completely different symbol, or + * it's a once-global symbol that was scoped to local via a mapfile. In + * the latter case, curfile is likely inaccurate since the linker does + * not preserve the needed curfile in the order of the symbol table (see + * the comments about locally scoped symbols in libld's update_osym()). + * As we can't tell this case from the former one, we use this symbol + * iff no other matching symbol is found. + * + * What we really need here is a SUNW section containing weak<->strong + * mappings that we can consume. + */ +static int +check_for_weak(GElf_Sym *weak, char const *weakfile, + Elf_Data *data, int nent, Elf_Data *strdata, + GElf_Sym *retsym, char **curfilep) +{ + char *curfile = NULL; + char *tmpfile1 = NULL; + GElf_Sym tmpsym; + int candidate = 0; + int i; + tmpsym.st_info = 0; + tmpsym.st_name = 0; + + if (GELF_ST_BIND(weak->st_info) != STB_WEAK) + return (0); + + for (i = 0; i < nent; i++) { + GElf_Sym sym; + uchar_t type; + + if (gelf_getsym(data, i, &sym) == NULL) + continue; + + type = GELF_ST_TYPE(sym.st_info); + + if (type == STT_FILE) + curfile = (char *)strdata->d_buf + sym.st_name; + + if (GELF_ST_TYPE(weak->st_info) != type || + weak->st_value != sym.st_value) + continue; + + if (weak->st_size != sym.st_size) + continue; + + if (GELF_ST_BIND(sym.st_info) == STB_WEAK) + continue; + + if (sym.st_shndx != weak->st_shndx) + continue; + + if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && + (curfile == NULL || weakfile == NULL || + strcmp(curfile, weakfile) != 0)) { + candidate = 1; + tmpfile1 = curfile; + tmpsym = sym; + continue; + } + + *curfilep = curfile; + *retsym = sym; + return (1); + } + + if (candidate) { + *curfilep = tmpfile1; + *retsym = tmpsym; + return (1); + } + + return (0); +} + +/* + * When we've found the underlying symbol's type description + * for a weak symbol, we need to copy it and rename it to match + * the weak symbol. We also need to add it to the td so it's + * handled along with the others later. + */ +static iidesc_t * +copy_from_strong(tdata_t *td, GElf_Sym *sym, iidesc_t *strongdesc, + const char *weakname, const char *weakfile) +{ + iidesc_t *new = iidesc_dup_rename(strongdesc, weakname, weakfile); + uchar_t type = GELF_ST_TYPE(sym->st_info); + + switch (type) { + case STT_OBJECT: + new->ii_type = II_GVAR; + break; + case STT_FUNC: + new->ii_type = II_GFUN; + break; + } + + hash_add(td->td_iihash, new); + + return (new); +} + +/* + * Process the symbol table of the output file, associating each symbol + * with a type description if possible, and sorting them into functions + * and data, maintaining symbol table order. + */ +static iiburst_t * +sort_iidescs(Elf *elf, const char *file, tdata_t *td, int fuzzymatch, + int dynsym) +{ + iiburst_t *iiburst; + Elf_Scn *scn; + GElf_Shdr shdr; + Elf_Data *data, *strdata; + int i, stidx; + int nent; + iidesc_match_t match; + + match.iim_fuzzy = fuzzymatch; + match.iim_file = NULL; + + if ((stidx = findelfsecidx(elf, file, + dynsym ? ".dynsym" : ".symtab")) < 0) + terminate("%s: Can't open symbol table\n", file); + scn = elf_getscn(elf, stidx); + data = elf_getdata(scn, NULL); + gelf_getshdr(scn, &shdr); + nent = shdr.sh_size / shdr.sh_entsize; + + scn = elf_getscn(elf, shdr.sh_link); + strdata = elf_getdata(scn, NULL); + + iiburst = iiburst_new(td, nent); + + for (i = 0; i < nent; i++) { + GElf_Sym sym; + iidesc_t **tolist; + GElf_Sym ssym; + iidesc_match_t smatch; + int *curr; + iidesc_t *iidesc; + + if (gelf_getsym(data, i, &sym) == NULL) + elfterminate(file, "Couldn't read symbol %d", i); + + match.iim_name = (char *)strdata->d_buf + sym.st_name; + match.iim_bind = GELF_ST_BIND(sym.st_info); + + switch (GELF_ST_TYPE(sym.st_info)) { + case STT_FILE: + match.iim_file = match.iim_name; + continue; + case STT_OBJECT: + tolist = iiburst->iib_objts; + curr = &iiburst->iib_nobjts; + break; + case STT_FUNC: + tolist = iiburst->iib_funcs; + curr = &iiburst->iib_nfuncs; + break; + default: + continue; + } + + if (ignore_symbol(&sym, match.iim_name)) + continue; + + iidesc = find_iidesc(td, &match); + + if (iidesc != NULL) { + tolist[*curr] = iidesc; + iidesc->ii_flags |= IIDESC_F_USED; + (*curr)++; + continue; + } + + if (!check_for_weak(&sym, match.iim_file, data, nent, strdata, + &ssym, &smatch.iim_file)) { + (*curr)++; + continue; + } + + smatch.iim_fuzzy = fuzzymatch; + smatch.iim_name = (char *)strdata->d_buf + ssym.st_name; + smatch.iim_bind = GELF_ST_BIND(ssym.st_info); + + debug(3, "Weak symbol %s resolved to %s\n", match.iim_name, + smatch.iim_name); + + iidesc = find_iidesc(td, &smatch); + + if (iidesc != NULL) { + tolist[*curr] = copy_from_strong(td, &sym, + iidesc, match.iim_name, match.iim_file); + tolist[*curr]->ii_flags |= IIDESC_F_USED; + } + + (*curr)++; + } + + /* + * Stabs are generated for every function declared in a given C source + * file. When converting an object file, we may encounter a stab that + * has no symbol table entry because the optimizer has decided to omit + * that item (for example, an unreferenced static function). We may + * see iidescs that do not have an associated symtab entry, and so + * we do not write records for those functions into the CTF data. + * All others get marked as a root by this function. + */ + iiburst_types(iiburst); + + /* + * By not adding some of the functions and/or objects, we may have + * caused some types that were referenced solely by those + * functions/objects to be suppressed. This could cause a label, + * generated prior to the evisceration, to be incorrect. Find the + * highest type index, and change the label indicies to be no higher + * than this value. + */ + tdata_label_newmax(td, iiburst->iib_maxtypeid); + + return (iiburst); +} + +static void +write_file(Elf *src, const char *srcname, Elf *dst, const char *dstname, + caddr_t ctfdata, size_t ctfsize, int flags) +{ + GElf_Ehdr sehdr, dehdr; + Elf_Scn *sscn, *dscn; + Elf_Data *sdata, *ddata; + GElf_Shdr shdr; + GElf_Word symtab_type; + int symtab_idx = -1; + off_t new_offset = 0; + off_t ctfnameoff = 0; + int dynsym = (flags & CTF_USE_DYNSYM); + int keep_stabs = (flags & CTF_KEEP_STABS); + int *secxlate; + int srcidx, dstidx; + int curnmoff = 0; + int changing = 0; + int pad; + int i; + + if (gelf_newehdr(dst, gelf_getclass(src)) == NULL) + elfterminate(dstname, "Cannot copy ehdr to temp file"); + gelf_getehdr(src, &sehdr); + memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr)); + gelf_update_ehdr(dst, &dehdr); + + symtab_type = dynsym ? SHT_DYNSYM : SHT_SYMTAB; + + /* + * Neither the existing stab sections nor the SUNW_ctf sections (new or + * existing) are SHF_ALLOC'd, so they won't be in areas referenced by + * program headers. As such, we can just blindly copy the program + * headers from the existing file to the new file. + */ + if (sehdr.e_phnum != 0) { + (void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT); + if (gelf_newphdr(dst, sehdr.e_phnum) == NULL) + elfterminate(dstname, "Cannot make phdrs in temp file"); + + for (i = 0; i < sehdr.e_phnum; i++) { + GElf_Phdr phdr; + + gelf_getphdr(src, i, &phdr); + gelf_update_phdr(dst, i, &phdr); + } + } + + secxlate = xmalloc(sizeof (int) * sehdr.e_shnum); + for (srcidx = dstidx = 0; srcidx < sehdr.e_shnum; srcidx++) { + Elf_Scn *scn = elf_getscn(src, srcidx); + GElf_Shdr shdr1; + char *sname; + + gelf_getshdr(scn, &shdr1); + sname = elf_strptr(src, sehdr.e_shstrndx, shdr1.sh_name); + if (sname == NULL) { + elfterminate(srcname, "Can't find string at %u", + shdr1.sh_name); + } + + if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) { + secxlate[srcidx] = -1; + } else if (!keep_stabs && + (strncmp(sname, ".stab", 5) == 0 || + strncmp(sname, ".debug", 6) == 0 || + strncmp(sname, ".rel.debug", 10) == 0 || + strncmp(sname, ".rela.debug", 11) == 0)) { + secxlate[srcidx] = -1; + } else if (dynsym && shdr1.sh_type == SHT_SYMTAB) { + /* + * If we're building CTF against the dynsym, + * we'll rip out the symtab so debuggers aren't + * confused. + */ + secxlate[srcidx] = -1; + } else { + secxlate[srcidx] = dstidx++; + curnmoff += strlen(sname) + 1; + } + + new_offset = (off_t)dehdr.e_phoff; + } + + for (srcidx = 1; srcidx < sehdr.e_shnum; srcidx++) { + char *sname; + + sscn = elf_getscn(src, srcidx); + gelf_getshdr(sscn, &shdr); + + if (secxlate[srcidx] == -1) { + changing = 1; + continue; + } + + dscn = elf_newscn(dst); + + /* + * If this file has program headers, we need to explicitly lay + * out sections. If none of the sections prior to this one have + * been removed, then we can just use the existing location. If + * one or more sections have been changed, then we need to + * adjust this one to avoid holes. + */ + if (changing && sehdr.e_phnum != 0) { + pad = new_offset % shdr.sh_addralign; + + if (pad) + new_offset += shdr.sh_addralign - pad; + shdr.sh_offset = new_offset; + } + + shdr.sh_link = secxlate[shdr.sh_link]; + + if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA) + shdr.sh_info = secxlate[shdr.sh_info]; + + sname = elf_strptr(src, sehdr.e_shstrndx, shdr.sh_name); + if (sname == NULL) { + elfterminate(srcname, "Can't find string at %u", + shdr.sh_name); + } + +#if !defined(sun) + if (gelf_update_shdr(dscn, &shdr) == 0) + elfterminate(dstname, "Cannot update sect %s", sname); +#endif + + if ((sdata = elf_getdata(sscn, NULL)) == NULL) + elfterminate(srcname, "Cannot get sect %s data", sname); + if ((ddata = elf_newdata(dscn)) == NULL) + elfterminate(dstname, "Can't make sect %s data", sname); +#if defined(sun) + bcopy(sdata, ddata, sizeof (Elf_Data)); +#else + /* + * FreeBSD's Elf_Data has private fields which the + * elf_* routines manage. Simply copying the + * entire structure corrupts the data. So we need + * to copy the public fields explictly. + */ + ddata->d_align = sdata->d_align; + ddata->d_off = sdata->d_off; + ddata->d_size = sdata->d_size; + ddata->d_type = sdata->d_type; + ddata->d_version = sdata->d_version; +#endif + + if (srcidx == sehdr.e_shstrndx) { + char seclen = strlen(CTF_ELF_SCN_NAME); + + ddata->d_buf = xmalloc(ddata->d_size + shdr.sh_size + + seclen + 1); + bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); + strcpy((caddr_t)ddata->d_buf + shdr.sh_size, + CTF_ELF_SCN_NAME); + ctfnameoff = (off_t)shdr.sh_size; + shdr.sh_size += seclen + 1; + ddata->d_size += seclen + 1; + + if (sehdr.e_phnum != 0) + changing = 1; + } + + if (shdr.sh_type == symtab_type && shdr.sh_entsize != 0) { + int nsym = shdr.sh_size / shdr.sh_entsize; + + symtab_idx = secxlate[srcidx]; + + ddata->d_buf = xmalloc(shdr.sh_size); + bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size); + + for (i = 0; i < nsym; i++) { + GElf_Sym sym; + short newscn; + + if (gelf_getsym(ddata, i, &sym) == NULL) + printf("Could not get symbol %d\n",i); + + if (sym.st_shndx >= SHN_LORESERVE) + continue; + + if ((newscn = secxlate[sym.st_shndx]) != + sym.st_shndx) { + sym.st_shndx = + (newscn == -1 ? 1 : newscn); + + gelf_update_sym(ddata, i, &sym); + } + } + } + +#if !defined(sun) + if (ddata->d_buf == NULL) { + 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 +#include +#include +#include +#include +#include +#include +#include + +#include "ctftools.h" +#include "memory.h" +#include "list.h" + +#define HASH(NUM) ((int)(NUM & (BUCKETS - 1))) +#define BUCKETS 128 + +#define TYPEPAIRMULT 10000 +#define MAKETYPEID(file, num) ((file) * TYPEPAIRMULT + num) +#define TYPEFILE(tid) ((tid) / TYPEPAIRMULT) +#define TYPENUM(tid) ((tid) % TYPEPAIRMULT) + +#define expected(a, b, c) _expected(a, b, c, __LINE__) + +static int faketypenumber = 100000000; + +static tdesc_t *hash_table[BUCKETS]; +static tdesc_t *name_table[BUCKETS]; + +list_t *typedbitfldmems; + +static void reset(void); +static jmp_buf resetbuf; + +static char *soudef(char *cp, stabtype_t type, tdesc_t **rtdp); +static void enumdef(char *cp, tdesc_t **rtdp); +static int compute_sum(const char *w); + +static char *number(char *cp, int *n); +static char *name(char *cp, char **w); +static char *id(char *cp, int *h); +static char *whitesp(char *cp); +static void addhash(tdesc_t *tdp, int num); +static int tagadd(char *w, int h, tdesc_t *tdp); +static char *tdefdecl(char *cp, int h, tdesc_t **rtdp); +static char *intrinsic(char *cp, tdesc_t **rtdp); +static char *arraydef(char *cp, tdesc_t **rtdp); + +int debug_parse = DEBUG_PARSE; + +/*PRINTFLIKE3*/ +static void +parse_debug(int level, char *cp, const char *fmt, ...) +{ + va_list ap; + char buf[1024]; + char tmp[32]; + int i; + + if (level > debug_level || !debug_parse) + return; + + if (cp != NULL) { + for (i = 0; i < 30; i++) { + if (cp[i] == '\0') + break; + if (!iscntrl(cp[i])) + tmp[i] = cp[i]; + } + tmp[i] = '\0'; + (void) snprintf(buf, sizeof (buf), "%s [cp='%s']\n", fmt, tmp); + } else { + strcpy(buf, fmt); + strcat(buf, "\n"); + } + + va_start(ap, fmt); + vadebug(level, buf, ap); + va_end(ap); +} + +/* Report unexpected syntax in stabs. */ +static void +_expected( + const char *who, /* what function, or part thereof, is reporting */ + const char *what, /* what was expected */ + const char *where, /* where we were in the line of input */ + int line) +{ + fprintf(stderr, "%s, expecting \"%s\" at \"%s\"\n", who, what, where); + fprintf(stderr, "code line: %d, file %s\n", line, + (curhdr ? curhdr : "NO FILE")); + reset(); +} + +/*ARGSUSED*/ +void +parse_init(tdata_t *td __unused) +{ + int i; + + for (i = 0; i < BUCKETS; i++) { + hash_table[i] = NULL; + name_table[i] = NULL; + } + + if (typedbitfldmems != NULL) { + list_free(typedbitfldmems, NULL, NULL); + typedbitfldmems = NULL; + } +} + +void +parse_finish(tdata_t *td) +{ + td->td_nextid = ++faketypenumber; +} + +static tdesc_t * +unres_new(int tid) +{ + tdesc_t *tdp; + + tdp = xcalloc(sizeof (*tdp)); + tdp->t_type = TYPEDEF_UNRES; + tdp->t_id = tid; + + return (tdp); +} + +static char * +read_tid(char *cp, tdesc_t **tdpp) +{ + tdesc_t *tdp; + int tid; + + cp = id(cp, &tid); + + assert(tid != 0); + + if (*cp == '=') { + if (!(cp = tdefdecl(cp + 1, tid, &tdp))) + return (NULL); + if (tdp->t_id && tdp->t_id != tid) { + tdesc_t *ntdp = xcalloc(sizeof (*ntdp)); + + ntdp->t_type = TYPEDEF; + ntdp->t_tdesc = tdp; + tdp = ntdp; + } + addhash(tdp, tid); + } else if ((tdp = lookup(tid)) == NULL) + tdp = unres_new(tid); + + *tdpp = tdp; + return (cp); +} + +static iitype_t +parse_fun(char *cp, iidesc_t *ii) +{ + iitype_t iitype = 0; + tdesc_t *tdp; + tdesc_t **args = NULL; + int nargs = 0; + int va = 0; + + /* + * name:P prototype + * name:F global function + * name:f static function + */ + switch (*cp++) { + case 'P': + iitype = II_NOT; /* not interesting */ + break; + + case 'F': + iitype = II_GFUN; + break; + + case 'f': + iitype = II_SFUN; + break; + + default: + expected("parse_nfun", "[PfF]", cp - 1); + } + + if (!(cp = read_tid(cp, &tdp))) + return (-1); + + if (*cp) + args = xmalloc(sizeof (tdesc_t *) * FUNCARG_DEF); + + while (*cp && *++cp) { + if (*cp == '0') { + va = 1; + continue; + } + + nargs++; + if (nargs > FUNCARG_DEF) + args = xrealloc(args, sizeof (tdesc_t *) * nargs); + if (!(cp = read_tid(cp, &args[nargs - 1]))) + return (-1); + } + + ii->ii_type = iitype; + ii->ii_dtype = tdp; + ii->ii_nargs = nargs; + ii->ii_args = args; + ii->ii_vargs = va; + + return (iitype); +} + +static iitype_t +parse_sym(char *cp, iidesc_t *ii) +{ + tdesc_t *tdp; + iitype_t iitype = 0; + + /* + * name:G global variable + * name:S static variable + */ + switch (*cp++) { + case 'G': + iitype = II_GVAR; + break; + case 'S': + iitype = II_SVAR; + break; + case 'p': + iitype = II_PSYM; + break; + case '(': + cp--; + /*FALLTHROUGH*/ + case 'r': + case 'V': + iitype = II_NOT; /* not interesting */ + break; + default: + expected("parse_sym", "[GprSV(]", cp - 1); + } + + if (!(cp = read_tid(cp, &tdp))) + return (-1); + + ii->ii_type = iitype; + ii->ii_dtype = tdp; + + return (iitype); +} + +static iitype_t +parse_type(char *cp, iidesc_t *ii) +{ + tdesc_t *tdp, *ntdp; + int tid; + + if (*cp++ != 't') + expected("parse_type", "t (type)", cp - 1); + + cp = id(cp, &tid); + if ((tdp = lookup(tid)) == NULL) { + if (*cp++ != '=') + expected("parse_type", "= (definition)", cp - 1); + + (void) tdefdecl(cp, tid, &tdp); + + if (tdp->t_id == tid) { + assert(tdp->t_type != TYPEDEF); + assert(!lookup(tdp->t_id)); + + if (!streq(tdp->t_name, ii->ii_name)) { + ntdp = xcalloc(sizeof (*ntdp)); + ntdp->t_name = xstrdup(ii->ii_name); + ntdp->t_type = TYPEDEF; + ntdp->t_tdesc = tdp; + tdp->t_id = faketypenumber++; + tdp = ntdp; + } + } else if (tdp->t_id == 0) { + assert(tdp->t_type == FORWARD || + tdp->t_type == INTRINSIC); + + if (tdp->t_name && !streq(tdp->t_name, ii->ii_name)) { + ntdp = xcalloc(sizeof (*ntdp)); + ntdp->t_name = xstrdup(ii->ii_name); + ntdp->t_type = TYPEDEF; + ntdp->t_tdesc = tdp; + tdp->t_id = faketypenumber++; + tdp = ntdp; + } + } else if (tdp->t_id != tid) { + ntdp = xcalloc(sizeof (*ntdp)); + ntdp->t_name = xstrdup(ii->ii_name); + ntdp->t_type = TYPEDEF; + ntdp->t_tdesc = tdp; + tdp = ntdp; + } + + if (tagadd(ii->ii_name, tid, tdp) < 0) + return (-1); + } + + ii->ii_type = II_TYPE; + ii->ii_dtype = tdp; + return (II_TYPE); +} + +static iitype_t +parse_sou(char *cp, iidesc_t *idp) +{ + tdesc_t *rtdp; + int tid; + + if (*cp++ != 'T') + expected("parse_sou", "T (sou)", cp - 1); + + cp = id(cp, &tid); + if (*cp++ != '=') + expected("parse_sou", "= (definition)", cp - 1); + + parse_debug(1, NULL, "parse_sou: declaring '%s'", idp->ii_name ? + idp->ii_name : "(anon)"); + if ((rtdp = lookup(tid)) != NULL) { + if (idp->ii_name != NULL) { + if (rtdp->t_name != NULL && + strcmp(rtdp->t_name, idp->ii_name) != 0) { + tdesc_t *tdp; + + tdp = xcalloc(sizeof (*tdp)); + tdp->t_name = xstrdup(idp->ii_name); + tdp->t_type = TYPEDEF; + tdp->t_tdesc = rtdp; + addhash(tdp, tid); /* for *(x,y) types */ + parse_debug(3, NULL, " %s defined as %s(%d)", + idp->ii_name, tdesc_name(rtdp), tid); + } else if (rtdp->t_name == NULL) { + rtdp->t_name = xstrdup(idp->ii_name); + addhash(rtdp, tid); + } + } + } else { + rtdp = xcalloc(sizeof (*rtdp)); + rtdp->t_name = idp->ii_name ? xstrdup(idp->ii_name) : NULL; + addhash(rtdp, tid); + } + + switch (*cp++) { + case 's': + (void) soudef(cp, STRUCT, &rtdp); + break; + case 'u': + (void) soudef(cp, UNION, &rtdp); + break; + case 'e': + enumdef(cp, &rtdp); + break; + default: + expected("parse_sou", "", cp - 1); + break; + } + + idp->ii_type = II_SOU; + idp->ii_dtype = rtdp; + return (II_SOU); +} + +int +parse_stab(stab_t *stab, char *cp, iidesc_t **iidescp) +{ + iidesc_t *ii = NULL; + iitype_t (*parse)(char *, iidesc_t *); + int rc; + + /* + * set up for reset() + */ + if (setjmp(resetbuf)) + return (-1); + + cp = whitesp(cp); + ii = iidesc_new(NULL); + cp = name(cp, &ii->ii_name); + + switch (stab->n_type) { + case N_FUN: + parse = parse_fun; + break; + + case N_LSYM: + if (*cp == 't') + parse = parse_type; + else if (*cp == 'T') + parse = parse_sou; + else + parse = parse_sym; + break; + + case N_GSYM: + case N_LCSYM: + case N_PSYM: + case N_ROSYM: + case N_RSYM: + case N_STSYM: + parse = parse_sym; + break; + default: + parse_debug(1, cp, "Unknown stab type %#x", stab->n_type); + bzero(&resetbuf, sizeof (resetbuf)); + return (-1); + } + + rc = parse(cp, ii); + bzero(&resetbuf, sizeof (resetbuf)); + + if (rc < 0 || ii->ii_type == II_NOT) { + iidesc_free(ii, NULL); + return (rc); + } + + *iidescp = ii; + + return (1); +} + +/* + * Check if we have this node in the hash table already + */ +tdesc_t * +lookup(int h) +{ + int bucket = HASH(h); + tdesc_t *tdp = hash_table[bucket]; + + while (tdp != NULL) { + if (tdp->t_id == h) + return (tdp); + tdp = tdp->t_hash; + } + return (NULL); +} + +static char * +whitesp(char *cp) +{ + char c; + + for (c = *cp++; isspace(c); c = *cp++); + --cp; + return (cp); +} + +static char * +name(char *cp, char **w) +{ + char *new, *orig, c; + int len; + + orig = cp; + c = *cp++; + if (c == ':') + *w = NULL; + else if (isalpha(c) || strchr("_.$", c)) { + for (c = *cp++; isalnum(c) || strchr(" _.$", c); c = *cp++) + ; + if (c != ':') + reset(); + len = cp - orig; + new = xmalloc(len); + while (orig < cp - 1) + *new++ = *orig++; + *new = '\0'; + *w = new - (len - 1); + } else + reset(); + + return (cp); +} + +static char * +number(char *cp, int *n) +{ + char *next; + + *n = (int)strtol(cp, &next, 10); + if (next == cp) + expected("number", "", cp); + return (next); +} + +static char * +id(char *cp, int *h) +{ + int n1, n2; + + if (*cp == '(') { /* SunPro style */ + cp++; + cp = number(cp, &n1); + if (*cp++ != ',') + expected("id", ",", cp - 1); + cp = number(cp, &n2); + if (*cp++ != ')') + expected("id", ")", cp - 1); + *h = MAKETYPEID(n1, n2); + } else if (isdigit(*cp)) { /* gcc style */ + cp = number(cp, &n1); + *h = n1; + } else { + expected("id", "(/0-9", cp); + } + return (cp); +} + +static int +tagadd(char *w, int h, tdesc_t *tdp) +{ + tdesc_t *otdp; + + tdp->t_name = w; + if (!(otdp = lookup(h))) + addhash(tdp, h); + else if (otdp != tdp) { + warning("duplicate entry\n"); + warning(" old: %s %d (%d,%d)\n", tdesc_name(otdp), + otdp->t_type, TYPEFILE(otdp->t_id), TYPENUM(otdp->t_id)); + warning(" new: %s %d (%d,%d)\n", tdesc_name(tdp), + tdp->t_type, TYPEFILE(tdp->t_id), TYPENUM(tdp->t_id)); + return (-1); + } + + return (0); +} + +static char * +tdefdecl(char *cp, int h, tdesc_t **rtdp) +{ + tdesc_t *ntdp; + char *w; + int c, h2; + char type; + + parse_debug(3, cp, "tdefdecl h=%d", h); + + /* Type codes */ + switch (type = *cp) { + case 'b': /* integer */ + case 'R': /* fp */ + cp = intrinsic(cp, rtdp); + break; + case '(': /* equiv to another type */ + cp = id(cp, &h2); + ntdp = lookup(h2); + + if (ntdp != NULL && *cp == '=') { + if (ntdp->t_type == FORWARD && *(cp + 1) == 'x') { + /* + * The 6.2 compiler, and possibly others, will + * sometimes emit the same stab for a forward + * declaration twice. That is, "(1,2)=xsfoo:" + * will sometimes show up in two different + * places. This is, of course, quite fun. We + * want CTF to work in spite of the compiler, + * so we'll let this one through. + */ + char *c2 = cp + 2; + char *nm; + + if (!strchr("sue", *c2++)) { + expected("tdefdecl/x-redefine", "[sue]", + c2 - 1); + } + + c2 = name(c2, &nm); + if (strcmp(nm, ntdp->t_name) != 0) { + terminate("Stabs error: Attempt to " + "redefine type (%d,%d) as " + "something else: %s\n", + TYPEFILE(h2), TYPENUM(h2), + c2 - 1); + } + free(nm); + + h2 = faketypenumber++; + ntdp = NULL; + } else { + terminate("Stabs error: Attempting to " + "redefine type (%d,%d)\n", TYPEFILE(h2), + TYPENUM(h2)); + } + } + + if (ntdp == NULL) { /* if that type isn't defined yet */ + if (*cp != '=') { + /* record it as unresolved */ + parse_debug(3, NULL, "tdefdecl unres type %d", + h2); + *rtdp = calloc(sizeof (**rtdp), 1); + (*rtdp)->t_type = TYPEDEF_UNRES; + (*rtdp)->t_id = h2; + break; + } else + cp++; + + /* define a new type */ + cp = tdefdecl(cp, h2, rtdp); + if ((*rtdp)->t_id && (*rtdp)->t_id != h2) { + ntdp = calloc(sizeof (*ntdp), 1); + ntdp->t_type = TYPEDEF; + ntdp->t_tdesc = *rtdp; + *rtdp = ntdp; + } + + addhash(*rtdp, h2); + + } else { /* that type is already defined */ + if (ntdp->t_type != TYPEDEF || ntdp->t_name != NULL) { + *rtdp = ntdp; + } else { + parse_debug(3, NULL, + "No duplicate typedef anon for ref"); + *rtdp = ntdp; + } + } + break; + case '*': + ntdp = NULL; + cp = tdefdecl(cp + 1, h, &ntdp); + if (ntdp == NULL) + expected("tdefdecl/*", "id", cp); + + if (!ntdp->t_id) + ntdp->t_id = faketypenumber++; + + *rtdp = xcalloc(sizeof (**rtdp)); + (*rtdp)->t_type = POINTER; + (*rtdp)->t_size = 0; + (*rtdp)->t_id = h; + (*rtdp)->t_tdesc = ntdp; + break; + case 'f': + cp = tdefdecl(cp + 1, h, &ntdp); + *rtdp = xcalloc(sizeof (**rtdp)); + (*rtdp)->t_type = FUNCTION; + (*rtdp)->t_size = 0; + (*rtdp)->t_id = h; + (*rtdp)->t_fndef = xcalloc(sizeof (fndef_t)); + /* + * The 6.1 compiler will sometimes generate incorrect stabs for + * function pointers (it'll get the return type wrong). This + * causes merges to fail. We therefore treat function pointers + * as if they all point to functions that return int. When + * 4432549 is fixed, the lookupname() call below should be + * replaced with `ntdp'. + */ + (*rtdp)->t_fndef->fn_ret = lookupname("int"); + break; + case 'a': + case 'z': + cp++; + if (*cp++ != 'r') + expected("tdefdecl/[az]", "r", cp - 1); + *rtdp = xcalloc(sizeof (**rtdp)); + (*rtdp)->t_type = ARRAY; + (*rtdp)->t_id = h; + cp = arraydef(cp, rtdp); + break; + case 'x': + c = *++cp; + if (c != 's' && c != 'u' && c != 'e') + expected("tdefdecl/x", "[sue]", cp - 1); + cp = name(cp + 1, &w); + + ntdp = xcalloc(sizeof (*ntdp)); + ntdp->t_type = FORWARD; + ntdp->t_name = w; + /* + * We explicitly don't set t_id here - the caller will do it. + * The caller may want to use a real type ID, or they may + * choose to make one up. + */ + + *rtdp = ntdp; + break; + + case 'B': /* volatile */ + cp = tdefdecl(cp + 1, h, &ntdp); + + if (!ntdp->t_id) + ntdp->t_id = faketypenumber++; + + *rtdp = xcalloc(sizeof (**rtdp)); + (*rtdp)->t_type = VOLATILE; + (*rtdp)->t_size = 0; + (*rtdp)->t_tdesc = ntdp; + (*rtdp)->t_id = h; + break; + + case 'k': /* const */ + cp = tdefdecl(cp + 1, h, &ntdp); + + if (!ntdp->t_id) + ntdp->t_id = faketypenumber++; + + *rtdp = xcalloc(sizeof (**rtdp)); + (*rtdp)->t_type = CONST; + (*rtdp)->t_size = 0; + (*rtdp)->t_tdesc = ntdp; + (*rtdp)->t_id = h; + break; + + case 'K': /* restricted */ + cp = tdefdecl(cp + 1, h, &ntdp); + + if (!ntdp->t_id) + ntdp->t_id = faketypenumber++; + + *rtdp = xcalloc(sizeof (**rtdp)); + (*rtdp)->t_type = RESTRICT; + (*rtdp)->t_size = 0; + (*rtdp)->t_tdesc = ntdp; + (*rtdp)->t_id = h; + break; + + case 'u': + case 's': + cp++; + + *rtdp = xcalloc(sizeof (**rtdp)); + (*rtdp)->t_name = NULL; + cp = soudef(cp, (type == 'u') ? UNION : STRUCT, rtdp); + break; + default: + expected("tdefdecl", "", cp); + } + return (cp); +} + +static char * +intrinsic(char *cp, tdesc_t **rtdp) +{ + intr_t *intr = xcalloc(sizeof (intr_t)); + tdesc_t *tdp; + int width, fmt, i; + + switch (*cp++) { + case 'b': + intr->intr_type = INTR_INT; + if (*cp == 's') + intr->intr_signed = 1; + else if (*cp != 'u') + expected("intrinsic/b", "[su]", cp); + cp++; + + if (strchr("cbv", *cp)) + intr->intr_iformat = *cp++; + + cp = number(cp, &width); + if (*cp++ != ';') + expected("intrinsic/b", "; (post-width)", cp - 1); + + cp = number(cp, &intr->intr_offset); + if (*cp++ != ';') + expected("intrinsic/b", "; (post-offset)", cp - 1); + + cp = number(cp, &intr->intr_nbits); + break; + + case 'R': + intr->intr_type = INTR_REAL; + for (fmt = 0, i = 0; isdigit(*(cp + i)); i++) + fmt = fmt * 10 + (*(cp + i) - '0'); + + if (fmt < 1 || fmt > CTF_FP_MAX) + expected("intrinsic/R", "number <= CTF_FP_MAX", cp); + + intr->intr_fformat = fmt; + cp += i; + + if (*cp++ != ';') + expected("intrinsic/R", ";", cp - 1); + cp = number(cp, &width); + + intr->intr_nbits = width * 8; + break; + } + + tdp = xcalloc(sizeof (*tdp)); + tdp->t_type = INTRINSIC; + tdp->t_size = width; + tdp->t_name = NULL; + tdp->t_intr = intr; + parse_debug(3, NULL, "intrinsic: size=%d", width); + *rtdp = tdp; + + return (cp); +} + +static tdesc_t * +bitintrinsic(tdesc_t *template, int nbits) +{ + tdesc_t *newtdp = xcalloc(sizeof (tdesc_t)); + + newtdp->t_name = xstrdup(template->t_name); + newtdp->t_id = faketypenumber++; + newtdp->t_type = INTRINSIC; + newtdp->t_size = template->t_size; + newtdp->t_intr = xmalloc(sizeof (intr_t)); + bcopy(template->t_intr, newtdp->t_intr, sizeof (intr_t)); + newtdp->t_intr->intr_nbits = nbits; + + return (newtdp); +} + +static char * +offsize(char *cp, mlist_t *mlp) +{ + int offset, size; + + if (*cp == ',') + cp++; + cp = number(cp, &offset); + if (*cp++ != ',') + expected("offsize/2", ",", cp - 1); + cp = number(cp, &size); + if (*cp++ != ';') + expected("offsize/3", ";", cp - 1); + mlp->ml_offset = offset; + mlp->ml_size = size; + return (cp); +} + +static tdesc_t * +find_intrinsic(tdesc_t *tdp) +{ + for (;;) { + switch (tdp->t_type) { + case TYPEDEF: + case VOLATILE: + case CONST: + case RESTRICT: + tdp = tdp->t_tdesc; + break; + + default: + return (tdp); + } + } +} + +static char * +soudef(char *cp, stabtype_t type, tdesc_t **rtdp) +{ + mlist_t *mlp, **prev; + char *w; + int h; + int size; + tdesc_t *tdp, *itdp; + + cp = number(cp, &size); + (*rtdp)->t_size = size; + (*rtdp)->t_type = type; /* s or u */ + + /* + * An '@' here indicates a bitmask follows. This is so the + * compiler can pass information to debuggers about how structures + * are passed in the v9 world. We don't need this information + * so we skip over it. + */ + if (cp[0] == '@') { + cp += 3; + } + + parse_debug(3, cp, "soudef: %s size=%d", tdesc_name(*rtdp), + (*rtdp)->t_size); + + prev = &((*rtdp)->t_members); + /* now fill up the fields */ + while ((*cp != '\0') && (*cp != ';')) { /* signifies end of fields */ + mlp = xcalloc(sizeof (*mlp)); + *prev = mlp; + cp = name(cp, &w); + mlp->ml_name = w; + cp = id(cp, &h); + /* + * find the tdesc struct in the hash table for this type + * and stick a ptr in here + */ + tdp = lookup(h); + if (tdp == NULL) { /* not in hash list */ + parse_debug(3, NULL, " defines %s (%d)", w, h); + if (*cp++ != '=') { + tdp = unres_new(h); + parse_debug(3, NULL, + " refers to %s (unresolved %d)", + (w ? w : "anon"), h); + } else { + cp = tdefdecl(cp, h, &tdp); + + if (tdp->t_id && tdp->t_id != h) { + tdesc_t *ntdp = xcalloc(sizeof (*ntdp)); + + ntdp->t_type = TYPEDEF; + ntdp->t_tdesc = tdp; + tdp = ntdp; + } + + addhash(tdp, h); + parse_debug(4, cp, + " soudef now looking at "); + cp++; + } + } else { + parse_debug(3, NULL, " refers to %s (%d, %s)", + w ? w : "anon", h, tdesc_name(tdp)); + } + + cp = offsize(cp, mlp); + + itdp = find_intrinsic(tdp); + if (itdp->t_type == INTRINSIC) { + if (mlp->ml_size != itdp->t_intr->intr_nbits) { + 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include + +#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 +#include +#include +#include +#include + +#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 + +#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 +#include +#include +#include + +#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 +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "ctf_headers.h" +#include "utils.h" +#include "symbol.h" + +#define WARN(x) { warn(x); return (E_ERROR); } + +/* + * Flags that indicate what data is to be displayed. An explicit `all' value is + * provided to allow the code to distinguish between a request for everything + * (currently requested by invoking ctfdump without flags) and individual + * requests for all of the types of data (an invocation with all flags). In the + * former case, we want to be able to implicitly adjust the definition of `all' + * based on the CTF version of the file being dumped. For example, if a v2 file + * is being dumped, `all' includes F_LABEL - a request to dump the label + * section. If a v1 file is being dumped, `all' does not include F_LABEL, + * because v1 CTF doesn't support labels. We need to be able to distinguish + * between `ctfdump foo', which has an implicit request for labels if `foo' + * supports them, and `ctfdump -l foo', which has an explicity request. In the + * latter case, we exit with an error if `foo' is a v1 CTF file. + */ +static enum { + F_DATA = 0x01, /* show data object section */ + F_FUNC = 0x02, /* show function section */ + F_HDR = 0x04, /* show header */ + F_STR = 0x08, /* show string table */ + F_TYPES = 0x10, /* show type section */ + F_STATS = 0x20, /* show statistics */ + F_LABEL = 0x40, /* show label section */ + F_ALL = 0x80, /* explicit request for `all' */ + F_ALLMSK = 0xff /* show all sections and statistics */ +} flags = 0; + +static struct { + ulong_t s_ndata; /* total number of data objects */ + ulong_t s_nfunc; /* total number of functions */ + ulong_t s_nargs; /* total number of function arguments */ + ulong_t s_argmax; /* longest argument list */ + ulong_t s_ntypes; /* total number of types */ + ulong_t s_types[16]; /* number of types by kind */ + ulong_t s_nsmem; /* total number of struct members */ + ulong_t s_nsbytes; /* total size of all structs */ + ulong_t s_smmax; /* largest struct in terms of members */ + ulong_t s_sbmax; /* largest struct in terms of bytes */ + ulong_t s_numem; /* total number of union members */ + ulong_t s_nubytes; /* total size of all unions */ + ulong_t s_ummax; /* largest union in terms of members */ + ulong_t s_ubmax; /* largest union in terms of bytes */ + ulong_t s_nemem; /* total number of enum members */ + ulong_t s_emmax; /* largest enum in terms of members */ + ulong_t s_nstr; /* total number of strings */ + size_t s_strlen; /* total length of all strings */ + size_t s_strmax; /* longest string length */ +} stats; + +typedef struct ctf_data { + caddr_t cd_ctfdata; /* Pointer to the CTF data */ + size_t cd_ctflen; /* Length of CTF data */ + + /* + * 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); +}