aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Birrell <jb@FreeBSD.org>2008-04-25 23:33:18 +0000
committerJohn Birrell <jb@FreeBSD.org>2008-04-25 23:33:18 +0000
commit2de84d2572206157cf33d1fb75463eeb42ae8e42 (patch)
treebae97b75e11b423edef1cbad51ea9836bf6e379e
parent38c68f8a196ef330599c13b5caa9d0bc5c5bff0c (diff)
downloadsrc-2de84d2572206157cf33d1fb75463eeb42ae8e42.tar.gz
src-2de84d2572206157cf33d1fb75463eeb42ae8e42.zip
Vendor import of DTrace support files from OpenSolaris.
Notes
Notes: svn path=/vendor-cddl/opensolaris/dist/cddl/contrib/opensolaris/; revision=178525
-rw-r--r--cmd/mdb/tools/common/die.c87
-rw-r--r--cmd/mdb/tools/common/util.h51
-rw-r--r--cmd/sgs/include/_string_table.h126
-rw-r--r--cmd/sgs/include/alist.h280
-rw-r--r--cmd/sgs/include/debug.h1017
-rw-r--r--cmd/sgs/include/sgs.h296
-rw-r--r--cmd/sgs/include/string_table.h63
-rw-r--r--cmd/sgs/messages/sgs.ident62
-rw-r--r--cmd/sgs/tools/common/findprime.c56
-rw-r--r--cmd/sgs/tools/common/sgsmsg.c1210
-rw-r--r--cmd/sgs/tools/common/string_table.c685
-rw-r--r--common/avl/avl.c969
-rw-r--r--common/ctf/ctf_create.c1366
-rw-r--r--common/ctf/ctf_decl.c184
-rw-r--r--common/ctf/ctf_error.c97
-rw-r--r--common/ctf/ctf_hash.c178
-rw-r--r--common/ctf/ctf_impl.h336
-rw-r--r--common/ctf/ctf_labels.c153
-rw-r--r--common/ctf/ctf_lookup.c313
-rw-r--r--common/ctf/ctf_open.c954
-rw-r--r--common/ctf/ctf_types.c845
-rw-r--r--common/ctf/ctf_util.c152
-rw-r--r--head/nlist.h54
-rw-r--r--head/note.h55
-rw-r--r--head/storclass.h79
-rw-r--r--head/syms.h230
26 files changed, 9898 insertions, 0 deletions
diff --git a/cmd/mdb/tools/common/die.c b/cmd/mdb/tools/common/die.c
new file mode 100644
index 000000000000..602969e1abb3
--- /dev/null
+++ b/cmd/mdb/tools/common/die.c
@@ -0,0 +1,87 @@
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <elf.h>
+
+#include <util.h>
+
+void
+die(char *format, ...)
+{
+ va_list ap;
+ int err = errno;
+#if !defined(sun)
+ const char *progname = getprogname();
+#endif
+
+ (void) fprintf(stderr, "%s: ", progname);
+
+ va_start(ap, format);
+ /* LINTED - variable format specifier */
+ (void) vfprintf(stderr, format, ap);
+ va_end(ap);
+
+ if (format[strlen(format) - 1] != '\n')
+ (void) fprintf(stderr, ": %s\n", strerror(err));
+
+#if defined(__FreeBSD__)
+ exit(0);
+#else
+ exit(1);
+#endif
+}
+
+void
+elfdie(char *format, ...)
+{
+ va_list ap;
+#if !defined(sun)
+ const char *progname = getprogname();
+#endif
+
+ (void) fprintf(stderr, "%s: ", progname);
+
+ va_start(ap, format);
+ /* LINTED - variable format specifier */
+ (void) vfprintf(stderr, format, ap);
+ va_end(ap);
+
+ if (format[strlen(format) - 1] != '\n')
+ (void) fprintf(stderr, ": %s\n", elf_errmsg(elf_errno()));
+
+#if defined(__FreeBSD__)
+ exit(0);
+#else
+ exit(1);
+#endif
+}
diff --git a/cmd/mdb/tools/common/util.h b/cmd/mdb/tools/common/util.h
new file mode 100644
index 000000000000..a0932ada0d57
--- /dev/null
+++ b/cmd/mdb/tools/common/util.h
@@ -0,0 +1,51 @@
+/*
+ * 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 _UTIL_H
+#define _UTIL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <libelf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int findelfsecidx(Elf *, char *);
+
+extern void die(char *, ...);
+extern void elfdie(char *, ...);
+
+#if defined(sun)
+extern const char *progname;
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UTIL_H */
diff --git a/cmd/sgs/include/_string_table.h b/cmd/sgs/include/_string_table.h
new file mode 100644
index 000000000000..2d2963f9dab7
--- /dev/null
+++ b/cmd/sgs/include/_string_table.h
@@ -0,0 +1,126 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef __STRING_TABLE_DOT_H
+#define __STRING_TABLE_DOT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/avl.h>
+#include <string_table.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * A string is represented in a string table using two values: length, and
+ * value. Grouping all the strings of a given length together allows for
+ * efficient matching of tail strings, as each input string value is hashed.
+ * Each string table uses a 2-level AVL tree of AVL trees to represent this
+ * organization.
+ *
+ * The outer (main) AVL tree contains LenNode structures. The search key for
+ * nodes on this main tree is the string length. Each such node represents
+ * all strings of a given length, and all strings of that length are found
+ * within.
+ *
+ * The strings within each LenNode are maintained using a secondary AVL tree
+ * of StrNode structures. The search key for this inner tree is the string
+ * itself. The strings are maintained in lexical order.
+ */
+typedef struct {
+ avl_node_t sn_avlnode; /* AVL book-keeping */
+ const char *sn_str; /* string */
+ size_t sn_refcnt; /* reference count */
+} StrNode;
+
+typedef struct {
+ avl_node_t ln_avlnode; /* AVL book-keeping */
+ avl_tree_t *ln_strtree; /* AVL tree of associated strings */
+ size_t ln_strlen; /* length of associated strings */
+} LenNode;
+
+/*
+ * Define a master string data item. Other strings may be suffixes of this
+ * string. The final string table will consist of the master string values,
+ * laid end to end, with the other strings referencing their tails.
+ */
+typedef struct str_master Str_master;
+
+struct str_master {
+ const char *sm_str; /* pointer to master string */
+ Str_master *sm_next; /* used for tracking master strings */
+ size_t sm_strlen; /* length of master string */
+ uint_t sm_hashval; /* hashval of master string */
+ size_t sm_stroff; /* offset into destination strtab */
+};
+
+/*
+ * Define a hash data item. This item represents an individual string that has
+ * been input into the String hash table. The string may either be a suffix of
+ * another string, or a master string.
+ */
+typedef struct str_hash Str_hash;
+
+struct str_hash {
+ size_t hi_strlen; /* string length */
+ size_t hi_refcnt; /* number of references to str */
+ uint_t hi_hashval; /* hash for string */
+ Str_master *hi_mstr; /* pointer to master string */
+ Str_hash *hi_next; /* next entry in hash bucket */
+};
+
+/*
+ * Controlling data structure for a String Table.
+ */
+struct str_tbl {
+ avl_tree_t *st_lentree; /* AVL tree of string lengths */
+ char *st_strbuf; /* string buffer */
+ Str_hash **st_hashbcks; /* hash buckets */
+ Str_master *st_mstrlist; /* list of all master strings */
+ size_t st_fullstrsize; /* uncompressed table size */
+ size_t st_nextoff; /* next available string */
+ size_t st_strsize; /* compressed size */
+ size_t st_strcnt; /* number of strings */
+ uint_t st_hbckcnt; /* number of buckets in */
+ /* hashlist */
+ uint_t st_flags;
+};
+
+#define FLG_STTAB_COMPRESS 0x01 /* compressed string table */
+#define FLG_STTAB_COOKED 0x02 /* offset has been assigned */
+
+/*
+ * Starting value for use with string hashing functions inside of string_table.c
+ */
+#define HASHSEED 5381
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __STRING_TABLE_DOT_H */
diff --git a/cmd/sgs/include/alist.h b/cmd/sgs/include/alist.h
new file mode 100644
index 000000000000..c27160bd35cc
--- /dev/null
+++ b/cmd/sgs/include/alist.h
@@ -0,0 +1,280 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Define an Alist, a list maintained as a reallocable array, and a for() loop
+ * macro to generalize its traversal. Note that the array can be reallocated
+ * as it is being traversed, thus the offset of each element is recomputed from
+ * the start of the structure.
+ */
+
+#ifndef _ALIST_H
+#define _ALIST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#if defined(sun)
+#include <sys/machelf.h>
+#else
+#include <sys/elf.h>
+#endif
+
+/*
+ * An Alist implements array lists. The functionality is similar to
+ * that of a linked list. However, an Alist is represented by a single
+ * contigious allocation of memory. The head of the memory is a header
+ * that contains control information for the list. Following the header
+ * is an array used to hold the user data. In the type definitions that
+ * follow, we define these as an array with a single element, but when
+ * we allocate the memory, we actually allocate the amount of memory needed.
+ *
+ * There are two "flavors" of array list:
+ *
+ * Alist - Contain arbitrary data, usually structs.
+ * APlist - Contain pointers to data allocated elsewhere.
+ *
+ * This differentiation is useful, because pointer lists are heavily
+ * used, and support a slightly different set of operations that are
+ * unique to their purpose.
+ *
+ * Array lists are initially represented by a NULL pointer. The memory
+ * for the list is only allocated if an item is inserted. This is very
+ * efficient for data structures that may or may not be needed for a
+ * given linker operation --- you only pay for what you use. In addition:
+ *
+ * - Array lists grow as needed (memory is reallocated as necessary)
+ * - Data is kept contiguously (no unused holes in between elements)
+ * at the beginning of the data area. This locality has
+ * good cache behavior, as access to adjacent items are
+ * highly likely to be in the same page of memory.
+ * - Insert/Delete operations at the end of the list are very
+ * efficient. However, insert/delete operations elsewhere
+ * will cause a relatively expensive overlapped memory
+ * copy of the data following the insert/delete location.
+ * - As with any generic memory alloctor (i.e. malloc()/free()),
+ * array lists are not type safe for the data they contain.
+ * Data is managed as (void *) pointers to data of a given
+ * length, so the Alist module cannot prevent the caller from
+ * inserting/extracting the wrong type of data. The caller
+ * must guard against this.
+ * - To free an array list, simply call the standard free() function
+ * on the list pointer.
+ */
+
+
+
+/*
+ * Aliste is used to represent list indexes, offsets, and sizes.
+ */
+typedef size_t Aliste;
+
+
+
+/*
+ * Alist is used to hold non-pointer items --- usually structs:
+ * - There must be an even number of Aliste fields before the
+ * al_data field. This ensures that al_data will have
+ * an alignment of 8, no matter whether sizeof(Aliste)
+ * is 4 or 8. That means that al_data will have sufficient
+ * alignment for any use, just like memory allocated via
+ * malloc().
+ * - al_nitems and al_next are redundant, in that they are
+ * directly related:
+ * al_next = al_nitems * al_size
+ * We do this to make ALIST_TRAVERSE_BYOFFSET maximally
+ * efficient. This doesn't waste space, because of the
+ * requirement to have an even # of Alist fields (above).
+ *
+ * Note that Alists allow the data to be referenced by 0 based array
+ * index, or by their byte offset from the start of the Alist memory
+ * allocation. The index form is preferred for most use, as it is simpler.
+ * However, by-offset access is used by rtld link maps, and this ability
+ * is convenient in that case.
+ */
+typedef struct {
+ Aliste al_arritems; /* # of items in al_data allocation */
+ Aliste al_nitems; /* # items (index of next avail item) */
+ Aliste al_next; /* offset of next available al_data[] */
+ Aliste al_size; /* size of each al_data[] item */
+ void *al_data[1]; /* data (can grow) */
+} Alist;
+
+/*
+ * APlist is a variant of Alist that contains pointers. There are several
+ * benefits to this special type:
+ * - API is simpler
+ * - Pointers are used directly, instead of requiring a
+ * pointer-to-pointer double indirection.
+ * - The implementation is slightly more efficient.
+ * - Operations that make particular sense for pointers
+ * can be supported without confusing the API for the
+ * regular Alists.
+ */
+typedef struct {
+ Aliste apl_arritems; /* # of items in apl_data allocation */
+ Aliste apl_nitems; /* # items (index of next avail item) */
+ void *apl_data[1]; /* data area: (arrcnt * size) bytes */
+} APlist;
+
+
+/*
+ * The ALIST_OFF_DATA and APLIST_OFF_DATA macros give the byte offset
+ * from the start of an array list to the first byte of the data area
+ * used to hold user data. The same trick used by the standard offsetof()
+ * macro is used.
+ */
+#define ALIST_OFF_DATA ((size_t)(((Alist *)0)->al_data))
+#define APLIST_OFF_DATA ((size_t)(((APlist *)0)->apl_data))
+
+
+/*
+ * The TRAVERSE macros are intended to be used within a for(), and
+ * cause the resulting loop to iterate over each item in the loop,
+ * in order.
+ * ALIST_TRAVERSE: Traverse over the items in an Alist,
+ * using the zero based item array index to refer to
+ * each item.
+ * ALIST_TRAVERSE_BY_OFFSET: Traverse over the items in an
+ * Alist using the byte offset from the head of the
+ * Alist pointer to refer to each item. It should be noted
+ * that the first such offset is given by ALIST_OFF_DATA,
+ * and as such, there will never be a 0 offset. Some code
+ * uses this fact to treat 0 as a reserved value with
+ * special meaning.
+ *
+ * By-offset access is convenient for some parts of
+ * rtld, where a value of 0 is used to indicate an
+ * uninitialized link map control.
+ *
+ * APLIST_TRAVERSE: Traverse over the pointers in an APlist, using
+ * the zero based item array index to refer to each pointer.
+ */
+
+/*
+ * Within the loop:
+ *
+ * LIST - Pointer to Alist structure for list
+ * IDX - The current item index
+ * OFF - The current item offset
+ * DATA - Pointer to item
+ */
+#define ALIST_TRAVERSE(LIST, IDX, DATA) \
+ (IDX) = 0, \
+ ((LIST) != NULL) && ((DATA) = (void *)(LIST)->al_data); \
+ \
+ ((LIST) != NULL) && ((IDX) < (LIST)->al_nitems); \
+ \
+ (IDX)++, \
+ (DATA) = (void *) (((LIST)->al_size * (IDX)) + (char *)(LIST)->al_data)
+
+#define ALIST_TRAVERSE_BY_OFFSET(LIST, OFF, DATA) \
+ (((LIST) != NULL) && ((OFF) = ALIST_OFF_DATA) && \
+ (((DATA) = (void *)((char *)(LIST) + (OFF))))); \
+ \
+ (((LIST) != NULL) && ((OFF) < (LIST)->al_next)); \
+ \
+ (((OFF) += ((LIST)->al_size)), \
+ ((DATA) = (void *)((char *)(LIST) + (OFF))))
+
+/*
+ * Within the loop:
+ *
+ * LIST - Pointer to APlist structure for list
+ * IDX - The current item index
+ * PTR - item value
+ *
+ * Note that this macro is designed to ensure that PTR retains the
+ * value of the final pointer in the list after exiting the for loop,
+ * and to avoid dereferencing an out of range address. This is done by
+ * doing the dereference in the middle expression, using the comma
+ * operator to ensure that a NULL pointer won't stop the loop.
+ */
+#define APLIST_TRAVERSE(LIST, IDX, PTR) \
+ (IDX) = 0; \
+ \
+ ((LIST) != NULL) && ((IDX) < (LIST)->apl_nitems) && \
+ (((PTR) = ((LIST)->apl_data)[IDX]), 1); \
+ \
+ (IDX)++
+
+
+/*
+ * Possible values returned by aplist_test()
+ */
+typedef enum {
+ ALE_ALLOCFAIL = 0, /* Memory allocation error */
+ ALE_EXISTS = 1, /* alist entry already exists */
+ ALE_NOTFND = 2, /* item not found and insert not required */
+ ALE_CREATE = 3 /* alist entry created */
+} aplist_test_t;
+
+
+/*
+ * Access to an Alist item by index or offset. This is needed because the
+ * size of an item in an Alist is not known by the C compiler, and we
+ * have to do the indexing arithmetic explicitly.
+ *
+ * For an APlist, index the apl_data field directly --- No macro is needed.
+ */
+#define alist_item(_lp, _idx) \
+ ((void *)(ALIST_OFF_DATA + ((_idx) * (_lp)->al_size) + (char *)(_lp)))
+#define alist_item_by_offset(_lp, _off) \
+ ((void *)((_off) + (char *)(_lp)))
+
+/*
+ * # of items currently found in a list. These macros handle the case
+ * where the list has not been allocated yet.
+ */
+#define alist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->al_nitems)
+#define aplist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->apl_nitems)
+
+
+extern void *alist_append(Alist **, const void *, size_t, Aliste);
+extern void alist_delete(Alist *, Aliste *);
+extern void alist_delete_by_offset(Alist *, Aliste *);
+extern void *alist_insert(Alist **, const void *, size_t,
+ Aliste, Aliste);
+extern void *alist_insert_by_offset(Alist **, const void *, size_t,
+ Aliste, Aliste);
+extern void alist_reset(Alist *);
+
+
+extern void *aplist_append(APlist **, const void *, Aliste);
+extern void aplist_delete(APlist *, Aliste *);
+extern int aplist_delete_value(APlist *, const void *);
+extern void *aplist_insert(APlist **, const void *,
+ Aliste, Aliste idx);
+extern void aplist_reset(APlist *);
+extern aplist_test_t aplist_test(APlist **, const void *, Aliste);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ALIST_H */
diff --git a/cmd/sgs/include/debug.h b/cmd/sgs/include/debug.h
new file mode 100644
index 000000000000..0a42f8dd9e9b
--- /dev/null
+++ b/cmd/sgs/include/debug.h
@@ -0,0 +1,1017 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Global include file for lddbg debugging.
+ *
+ * ld(1) and ld.so.1(1) carry out all diagnostic debugging calls via lazy
+ * loading the library liblddbg.so. Thus debugging is always enabled. The
+ * utility elfdump(1) is explicitly dependent upon this library. There are two
+ * categories of routines defined in this library:
+ *
+ * o Debugging routines that have specific linker knowledge, and test the
+ * class of debugging allowable before proceeding, start with the `Dbg_'
+ * prefix.
+ *
+ * o Lower level routines that provide generic ELF structure interpretation
+ * start with the `Elf_' prefix. These latter routines are the only
+ * routines used by the elfdump(1) utility.
+ */
+#include <sgs.h>
+#include <libld.h>
+#include <rtld.h>
+#include <gelf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Define Dbg_*() interface flags. These flags direct the debugging routine to
+ * generate different diagnostics, thus the strings themselves are maintained
+ * in the debugging library.
+ */
+#define DBG_SUP_ENVIRON 1
+#define DBG_SUP_CMDLINE 2
+#define DBG_SUP_DEFAULT 3
+
+#define DBG_CONF_IGNORE 1 /* configuration processing errors */
+#define DBG_CONF_VERSION 2
+#define DBG_CONF_PRCFAIL 3
+#define DBG_CONF_CORRUPT 4
+#define DBG_CONF_ABIMISMATCH 5
+
+#define DBG_ORDER_INFO_RANGE 1 /* sh_link out of range */
+#define DBG_ORDER_INFO_ORDER 2 /* sh_info also ordered */
+#define DBG_ORDER_LINK_OUTRANGE 3 /* sh_link out of range */
+#define DBG_ORDER_FLAGS 4 /* sh_flags do not match */
+#define DBG_ORDER_CYCLIC 5 /* sh_link cyclic */
+#define DBG_ORDER_LINK_ERROR 6 /* sh_link (one) has an error */
+
+#define DBG_INIT_SORT 1 /* calling init from sorted order */
+#define DBG_INIT_PEND 2 /* calling pending init */
+#define DBG_INIT_DYN 3 /* dynamically triggered init */
+#define DBG_INIT_DONE 4 /* init completed */
+
+#define DBG_DLSYM_DEF 0
+#define DBG_DLSYM_NEXT 1
+#define DBG_DLSYM_DEFAULT 2
+#define DBG_DLSYM_SELF 3
+#define DBG_DLSYM_PROBE 4
+#define DBG_DLSYM_SINGLETON 5
+
+#define DBG_DLCLOSE_NULL 0
+#define DBG_DLCLOSE_IGNORE 1
+#define DBG_DLCLOSE_RESCAN 2
+
+#define DBG_WAIT_INIT 1
+#define DBG_WAIT_FINI 2
+#define DBG_WAIT_SYMBOL 3
+
+#define DBG_SYM_REDUCE_GLOBAL 1 /* reporting global symbols to local */
+#define DBG_SYM_REDUCE_RETAIN 2 /* reporting non reduced local syms */
+
+/*
+ * Group handle operations - passed to Dbg_file_hdl_title(). Indicate why
+ * handle dependencies are being manipulated.
+ */
+#define DBG_HDL_CREATE 0 /* handle creation */
+#define DBG_HDL_ADD 1 /* addition to existing handle */
+#define DBG_HDL_DELETE 2 /* deletion from a handle */
+#define DBG_HDL_ORPHAN 3 /* handle being moved to orphan list */
+#define DBG_HDL_REINST 4 /* handle reinstated from orphan list */
+
+/*
+ * Group handle dependency operations - passed to Dbg_file_hdl_action().
+ * Identify the dependencies that are associated with a handle.
+ */
+#define DBG_DEP_ADD 0 /* dependency added */
+#define DBG_DEP_UPDATE 1 /* dependency updated */
+#define DBG_DEP_DELETE 2 /* dependency deleted */
+#define DBG_DEP_REMOVE 3 /* dependency removed from handle */
+#define DBG_DEP_REMAIN 4 /* dependency must remain on handle */
+#define DBG_DEP_ORPHAN 5 /* dependency must remain an orphan */
+#define DBG_DEP_REINST 6 /* dependency reinstated from orphan */
+
+/*
+ * Binding information, indicating the result of a symbol binding. Can also
+ * indicate the reference as being EXTERN or PARENT. Binding information is
+ * used to augment diagnostic binding information (which in turn can be used by
+ * lari(1)), and to enable ldd(1) -p processing.
+ */
+#define DBG_BINFO_FOUND 0x0001 /* information regarding binding */
+#define DBG_BINFO_DIRECT 0x0002 /* bound directly */
+#define DBG_BINFO_COPYREF 0x0004 /* bound to copy relocated reference */
+#define DBG_BINFO_FILTEE 0x0008 /* bound to filtee */
+#define DBG_BINFO_INTERPOSE 0x0010 /* bound to an identified interposer */
+#define DBG_BINFO_PLTADDR 0x0020 /* bound to executables undefined plt */
+#define DBG_BINFO_MSK 0x0fff
+
+#define DBG_BINFO_REF_EXTERN 0x1000 /* reference to EXTERN */
+#define DBG_BINFO_REF_PARENT 0x2000 /* reference to PARENT */
+#define DBG_BINFO_REF_MSK 0xf000
+
+
+#define DBG_CAP_INITIAL 0
+#define DBG_CAP_IGNORE 1
+#define DBG_CAP_OLD 2
+#define DBG_CAP_NEW 3
+#define DBG_CAP_RESOLVED 4
+
+#define DBG_REL_START 1
+#define DBG_REL_FINISH 2
+#define DBG_REL_NONE 3
+
+#define DBG_NL_STD 0 /* newline controllers - standard and */
+#define DBG_NL_FRC 2 /* forced. */
+
+#define DBG_BNDREJ_NODIR 0 /* bind rejected, direct to nodirect */
+#define DBG_BNDREJ_SINGLE 1 /* bind rejected, singleton without */
+ /* default search model */
+#define DBG_BNDREJ_NUM DBG_BNDREJ_SINGLE
+
+/*
+ * Define a debug descriptor, and a user macro that inspects the descriptor as
+ * a means of triggering a class of diagnostic output.
+ */
+typedef struct {
+ uint_t d_class; /* debugging classes */
+ uint_t d_extra; /* extra information for classes */
+ APlist *d_list; /* associated strings */
+} Dbg_desc;
+
+extern Dbg_desc *dbg_desc;
+
+#define DBG_ENABLED (dbg_desc->d_class)
+#define DBG_CALL(func) if (DBG_ENABLED) func
+
+/*
+ * Most debugging tokens are interpreted within liblddbg, and thus any flags
+ * within d_class are only meaningful to this library. The following flags
+ * extend the d_class diagnostic, and are maintained in d_extra. These flags
+ * may be interpreted by the debugging library itself or from the callers
+ * dbg_print() routine.
+ */
+#define DBG_E_DETAIL 0x0001 /* add detail to a class */
+#define DBG_E_LONG 0x0002 /* use long names (ie. no truncation) */
+
+#define DBG_E_STDNL 0x0010 /* standard newline indicator */
+
+#define DBG_E_SNAME 0x0100 /* prepend simple name (ld only) */
+#define DBG_E_FNAME 0x0200 /* prepend full name (ld only) */
+#define DBG_E_CLASS 0x0400 /* prepend ELF class (ld only) */
+#define DBG_E_LMID 0x0800 /* prepend link-map id (ld.so.1 only) */
+#define DBG_E_DEMANGLE 0x1000 /* demangle symbol names */
+
+#define DBG_NOTDETAIL() !(dbg_desc->d_extra & DBG_E_DETAIL)
+#define DBG_NOTLONG() !(dbg_desc->d_extra & DBG_E_LONG)
+
+#define DBG_ISSNAME() (dbg_desc->d_extra & DBG_E_SNAME)
+#define DBG_ISFNAME() (dbg_desc->d_extra & DBG_E_FNAME)
+#define DBG_ISCLASS() (dbg_desc->d_extra & DBG_E_CLASS)
+#define DBG_ISLMID() (dbg_desc->d_extra & DBG_E_LMID)
+#define DBG_ISDEMANGLE() \
+ (dbg_desc->d_extra & DBG_E_DEMANGLE)
+
+/*
+ * Print routine, this must be supplied by the application. The initial
+ * argument may provide a link-map list to associate with the format statement
+ * that follows.
+ */
+/* PRINTFLIKE2 */
+extern void dbg_print(Lm_list *, const char *, ...);
+
+extern uintptr_t Dbg_setup(const char *, Dbg_desc *);
+
+/*
+ * Establish ELF32 and ELF64 class Dbg_*() interfaces.
+ */
+#if defined(_ELF64)
+
+#define Dbg_demangle_name Dbg64_demangle_name
+
+#define Dbg_bind_global Dbg64_bind_global
+#define Dbg_bind_plt_summary Dbg64_bind_plt_summary
+#define Dbg_bind_pltpad_from Dbg64_bind_pltpad_from
+#define Dbg_bind_pltpad_to Dbg64_bind_pltpad_to
+#define Dbg_bind_reject Dbg64_bind_reject
+#define Dbg_bind_weak Dbg64_bind_weak
+
+#define Dbg_cap_val_hw1 Dbg64_cap_val_hw1
+#define Dbg_cap_hw_candidate Dbg64_cap_hw_candidate
+#define Dbg_cap_hw_filter Dbg64_cap_hw_filter
+#define Dbg_cap_mapfile Dbg64_cap_mapfile
+#define Dbg_cap_sec_entry Dbg64_cap_sec_entry
+#define Dbg_cap_sec_title Dbg64_cap_sec_title
+
+#define Dbg_ent_entry Dbg64_ent_entry
+#define Dbg_ent_print Dbg64_ent_print
+
+#define Dbg_file_analyze Dbg64_file_analyze
+#define Dbg_file_aout Dbg64_file_aout
+#define Dbg_file_ar Dbg64_file_ar
+#define Dbg_file_ar_rescan Dbg64_file_ar_rescan
+#define Dbg_file_bind_entry Dbg64_file_bind_entry
+#define Dbg_file_bindings Dbg64_file_bindings
+#define Dbg_file_cleanup Dbg64_file_cleanup
+#define Dbg_file_cntl Dbg64_file_cntl
+#define Dbg_file_config_dis Dbg64_file_config_dis
+#define Dbg_file_config_obj Dbg64_file_config_obj
+#define Dbg_file_del_rescan Dbg64_file_del_rescan
+#define Dbg_file_delete Dbg64_file_delete
+#define Dbg_file_dlclose Dbg64_file_dlclose
+#define Dbg_file_dldump Dbg64_file_dldump
+#define Dbg_file_dlopen Dbg64_file_dlopen
+#define Dbg_file_elf Dbg64_file_elf
+#define Dbg_file_filtee Dbg64_file_filtee
+#define Dbg_file_filter Dbg64_file_filter
+#define Dbg_file_fixname Dbg64_file_fixname
+#define Dbg_file_generic Dbg64_file_generic
+#define Dbg_file_hdl_action Dbg64_file_hdl_action
+#define Dbg_file_hdl_collect Dbg64_file_hdl_collect
+#define Dbg_file_hdl_title Dbg64_file_hdl_title
+#define Dbg_file_lazyload Dbg64_file_lazyload
+#define Dbg_file_ldso Dbg64_file_ldso
+#define Dbg_file_mode_promote Dbg64_file_mode_promote
+#define Dbg_file_modified Dbg64_file_modified
+#define Dbg_file_needed Dbg64_file_needed
+#define Dbg_file_output Dbg64_file_output
+#define Dbg_file_preload Dbg64_file_preload
+#define Dbg_file_prot Dbg64_file_prot
+#define Dbg_file_rejected Dbg64_file_rejected
+#define Dbg_file_reuse Dbg64_file_reuse
+#define Dbg_file_skip Dbg64_file_skip
+
+#define Dbg_got_display Dbg64_got_display
+
+#define Dbg_libs_audit Dbg64_libs_audit
+#define Dbg_libs_find Dbg64_libs_find
+#define Dbg_libs_found Dbg64_libs_found
+#define Dbg_libs_ignore Dbg64_libs_ignore
+#define Dbg_libs_init Dbg64_libs_init
+#define Dbg_libs_l Dbg64_libs_l
+#define Dbg_libs_path Dbg64_libs_path
+#define Dbg_libs_req Dbg64_libs_req
+#define Dbg_libs_update Dbg64_libs_update
+#define Dbg_libs_yp Dbg64_libs_yp
+#define Dbg_libs_ylu Dbg64_libs_ylu
+
+#define Dbg_map_dash Dbg64_map_dash
+#define Dbg_map_ent Dbg64_map_ent
+#define Dbg_map_parse Dbg64_map_parse
+#define Dbg_map_pipe Dbg64_map_pipe
+#define Dbg_map_seg Dbg64_map_seg
+#define Dbg_map_set_atsign Dbg64_map_set_atsign
+#define Dbg_map_set_equal Dbg64_map_set_equal
+#define Dbg_map_size_new Dbg64_map_size_new
+#define Dbg_map_size_old Dbg64_map_size_old
+#define Dbg_map_sort_fini Dbg64_map_sort_fini
+#define Dbg_map_sort_orig Dbg64_map_sort_orig
+#define Dbg_map_symbol Dbg64_map_symbol
+#define Dbg_map_version Dbg64_map_version
+
+#define Dbg_move_adjexpandreloc Dbg64_move_adjexpandreloc
+#define Dbg_move_adjmovereloc Dbg64_move_adjmovereloc
+#define Dbg_move_data Dbg64_move_data
+#define Dbg_move_entry1 Dbg64_move_entry1
+#define Dbg_move_entry2 Dbg64_move_entry2
+#define Dbg_move_expand Dbg64_move_expand
+#define Dbg_move_input Dbg64_move_input
+#define Dbg_move_outmove Dbg64_move_outmove
+#define Dbg_move_outsctadj Dbg64_move_outsctadj
+#define Dbg_move_parexpn Dbg64_move_parexpn
+
+#define Dbg_reloc_apply_reg Dbg64_reloc_apply_reg
+#define Dbg_reloc_apply_val Dbg64_reloc_apply_val
+#define Dbg_reloc_ars_entry Dbg64_reloc_ars_entry
+#define Dbg_reloc_copy Dbg64_reloc_copy
+#define Dbg_reloc_discard Dbg64_reloc_discard
+#define Dbg_reloc_doact Dbg64_reloc_doact
+#define Dbg_reloc_doact_title Dbg64_reloc_doact_title
+#define Dbg_reloc_dooutrel Dbg64_reloc_dooutrel
+#define Dbg_reloc_entry Dbg64_reloc_entry
+#define Dbg_reloc_error Dbg64_reloc_error
+#define Dbg_reloc_generate Dbg64_reloc_generate
+#define Dbg_reloc_in Dbg64_reloc_in
+#define Dbg_reloc_ors_entry Dbg64_reloc_ors_entry
+#define Dbg_reloc_out Dbg64_reloc_out
+#define Dbg_reloc_proc Dbg64_reloc_proc
+#define Dbg_reloc_run Dbg64_reloc_run
+#define Dbg_reloc_transition Dbg64_reloc_transition
+#define Dbg_reloc_sloppycomdat Dbg64_reloc_sloppycomdat
+
+#define Dbg_sec_added Dbg64_sec_added
+#define Dbg_sec_created Dbg64_sec_created
+#define Dbg_sec_discarded Dbg64_sec_discarded
+#define Dbg_sec_genstr_compress Dbg64_sec_genstr_compress
+#define Dbg_sec_group Dbg64_sec_group
+#define Dbg_sec_in Dbg64_sec_in
+#define Dbg_sec_order_error Dbg64_sec_order_error
+#define Dbg_sec_order_list Dbg64_sec_order_list
+#define Dbg_sec_strtab Dbg64_sec_strtab
+#define Dbg_sec_unsup_strmerge Dbg64_sec_unsup_strmerge
+
+#define Dbg_seg_desc_entry Dbg64_seg_desc_entry
+#define Dbg_seg_entry Dbg64_seg_entry
+#define Dbg_seg_list Dbg64_seg_list
+#define Dbg_seg_os Dbg64_seg_os
+#define Dbg_seg_title Dbg64_seg_title
+
+#define Dbg_shdr_modified Dbg64_shdr_modified
+
+#define Dbg_statistics_ar Dbg64_statistics_ar
+#define Dbg_statistics_ld Dbg64_statistics_ld
+
+#define Dbg_support_action Dbg64_support_action
+#define Dbg_support_load Dbg64_support_load
+#define Dbg_support_req Dbg64_support_req
+
+#define Dbg_syminfo_entry Dbg64_syminfo_entry
+#define Dbg_syminfo_title Dbg64_syminfo_title
+
+#define Dbg_syms_ar_checking Dbg64_syms_ar_checking
+#define Dbg_syms_ar_entry Dbg64_syms_ar_entry
+#define Dbg_syms_ar_resolve Dbg64_syms_ar_resolve
+#define Dbg_syms_ar_title Dbg64_syms_ar_title
+#define Dbg_syms_created Dbg64_syms_created
+#define Dbg_syms_discarded Dbg64_syms_discarded
+#define Dbg_syms_dlsym Dbg64_syms_dlsym
+#define Dbg_syms_dup_sort_addr Dbg64_syms_dup_sort_addr
+#define Dbg_syms_entered Dbg64_syms_entered
+#define Dbg_syms_entry Dbg64_syms_entry
+#define Dbg_syms_global Dbg64_syms_global
+#define Dbg_syms_ignore Dbg64_syms_ignore
+#define Dbg_syms_ignore_gnuver Dbg64_syms_ignore_gnuver
+#define Dbg_syms_lazy_rescan Dbg64_syms_lazy_rescan
+#define Dbg_syms_lookup Dbg64_syms_lookup
+#define Dbg_syms_new Dbg64_syms_new
+#define Dbg_syms_old Dbg64_syms_old
+#define Dbg_syms_process Dbg64_syms_process
+#define Dbg_syms_reduce Dbg64_syms_reduce
+#define Dbg_syms_reloc Dbg64_syms_reloc
+#define Dbg_syms_resolved Dbg64_syms_resolved
+#define Dbg_syms_resolving Dbg64_syms_resolving
+#define Dbg_syms_sec_entry Dbg64_syms_sec_entry
+#define Dbg_syms_sec_title Dbg64_syms_sec_title
+#define Dbg_syms_spec_title Dbg64_syms_spec_title
+#define Dbg_syms_updated Dbg64_syms_updated
+#define Dbg_syms_up_title Dbg64_syms_up_title
+
+#define Dbg_util_broadcast Dbg64_util_broadcast
+#define Dbg_util_call_array Dbg64_util_call_array
+#define Dbg_util_call_fini Dbg64_util_call_fini
+#define Dbg_util_call_init Dbg64_util_call_init
+#define Dbg_util_call_main Dbg64_util_call_main
+#define Dbg_util_collect Dbg64_util_collect
+#define Dbg_util_dbnotify Dbg64_util_dbnotify
+#define Dbg_util_edge_in Dbg64_util_edge_in
+#define Dbg_util_edge_out Dbg64_util_edge_out
+#define Dbg_util_intoolate Dbg64_util_intoolate
+#define Dbg_util_lcinterface Dbg64_util_lcinterface
+#define Dbg_util_nl Dbg64_util_nl
+#define Dbg_util_no_init Dbg64_util_no_init
+#define Dbg_util_scc_entry Dbg64_util_scc_entry
+#define Dbg_util_scc_title Dbg64_util_scc_title
+#define Dbg_util_str Dbg64_util_str
+#define Dbg_util_wait Dbg64_util_wait
+
+#define Dbg_unused_file Dbg64_unused_file
+#define Dbg_unused_lcinterface Dbg64_unused_lcinterface
+#define Dbg_unused_path Dbg64_unused_path
+#define Dbg_unused_sec Dbg64_unused_sec
+#define Dbg_unused_unref Dbg64_unused_unref
+
+#define Dbg_ver_avail_entry Dbg64_ver_avail_entry
+#define Dbg_ver_avail_title Dbg64_ver_avail_title
+#define Dbg_ver_def_title Dbg64_ver_def_title
+#define Dbg_ver_desc_entry Dbg64_ver_desc_entry
+#define Dbg_ver_need_entry Dbg64_ver_need_entry
+#define Dbg_ver_need_title Dbg64_ver_need_title
+#define Dbg_ver_nointerface Dbg64_ver_nointerface
+#define Dbg_ver_symbol Dbg64_ver_symbol
+
+#else
+
+#define Dbg_demangle_name Dbg32_demangle_name
+
+#define Dbg_bind_global Dbg32_bind_global
+#define Dbg_bind_plt_summary Dbg32_bind_plt_summary
+#define Dbg_bind_reject Dbg32_bind_reject
+#define Dbg_bind_weak Dbg32_bind_weak
+
+#define Dbg_cap_val_hw1 Dbg32_cap_val_hw1
+#define Dbg_cap_hw_candidate Dbg32_cap_hw_candidate
+#define Dbg_cap_hw_filter Dbg32_cap_hw_filter
+#define Dbg_cap_mapfile Dbg32_cap_mapfile
+#define Dbg_cap_sec_entry Dbg32_cap_sec_entry
+#define Dbg_cap_sec_title Dbg32_cap_sec_title
+
+#define Dbg_ent_entry Dbg32_ent_entry
+#define Dbg_ent_print Dbg32_ent_print
+
+#define Dbg_file_analyze Dbg32_file_analyze
+#define Dbg_file_aout Dbg32_file_aout
+#define Dbg_file_ar Dbg32_file_ar
+#define Dbg_file_ar_rescan Dbg32_file_ar_rescan
+#define Dbg_file_bind_entry Dbg32_file_bind_entry
+#define Dbg_file_bindings Dbg32_file_bindings
+#define Dbg_file_cleanup Dbg32_file_cleanup
+#define Dbg_file_cntl Dbg32_file_cntl
+#define Dbg_file_config_dis Dbg32_file_config_dis
+#define Dbg_file_config_obj Dbg32_file_config_obj
+#define Dbg_file_del_rescan Dbg32_file_del_rescan
+#define Dbg_file_delete Dbg32_file_delete
+#define Dbg_file_dlclose Dbg32_file_dlclose
+#define Dbg_file_dldump Dbg32_file_dldump
+#define Dbg_file_dlopen Dbg32_file_dlopen
+#define Dbg_file_elf Dbg32_file_elf
+#define Dbg_file_filtee Dbg32_file_filtee
+#define Dbg_file_filter Dbg32_file_filter
+#define Dbg_file_fixname Dbg32_file_fixname
+#define Dbg_file_generic Dbg32_file_generic
+#define Dbg_file_hdl_action Dbg32_file_hdl_action
+#define Dbg_file_hdl_collect Dbg32_file_hdl_collect
+#define Dbg_file_hdl_title Dbg32_file_hdl_title
+#define Dbg_file_lazyload Dbg32_file_lazyload
+#define Dbg_file_ldso Dbg32_file_ldso
+#define Dbg_file_mode_promote Dbg32_file_mode_promote
+#define Dbg_file_modified Dbg32_file_modified
+#define Dbg_file_needed Dbg32_file_needed
+#define Dbg_file_output Dbg32_file_output
+#define Dbg_file_preload Dbg32_file_preload
+#define Dbg_file_prot Dbg32_file_prot
+#define Dbg_file_rejected Dbg32_file_rejected
+#define Dbg_file_reuse Dbg32_file_reuse
+#define Dbg_file_skip Dbg32_file_skip
+
+#define Dbg_got_display Dbg32_got_display
+
+#define Dbg_libs_audit Dbg32_libs_audit
+#define Dbg_libs_find Dbg32_libs_find
+#define Dbg_libs_found Dbg32_libs_found
+#define Dbg_libs_ignore Dbg32_libs_ignore
+#define Dbg_libs_init Dbg32_libs_init
+#define Dbg_libs_l Dbg32_libs_l
+#define Dbg_libs_path Dbg32_libs_path
+#define Dbg_libs_req Dbg32_libs_req
+#define Dbg_libs_update Dbg32_libs_update
+#define Dbg_libs_yp Dbg32_libs_yp
+#define Dbg_libs_ylu Dbg32_libs_ylu
+
+#define Dbg_map_dash Dbg32_map_dash
+#define Dbg_map_ent Dbg32_map_ent
+#define Dbg_map_parse Dbg32_map_parse
+#define Dbg_map_pipe Dbg32_map_pipe
+#define Dbg_map_seg Dbg32_map_seg
+#define Dbg_map_set_atsign Dbg32_map_set_atsign
+#define Dbg_map_set_equal Dbg32_map_set_equal
+#define Dbg_map_size_new Dbg32_map_size_new
+#define Dbg_map_size_old Dbg32_map_size_old
+#define Dbg_map_sort_fini Dbg32_map_sort_fini
+#define Dbg_map_sort_orig Dbg32_map_sort_orig
+#define Dbg_map_symbol Dbg32_map_symbol
+#define Dbg_map_version Dbg32_map_version
+
+#define Dbg_move_adjexpandreloc Dbg32_move_adjexpandreloc
+#define Dbg_move_adjmovereloc Dbg32_move_adjmovereloc
+#define Dbg_move_data Dbg32_move_data
+#define Dbg_move_entry1 Dbg32_move_entry1
+#define Dbg_move_entry2 Dbg32_move_entry2
+#define Dbg_move_expand Dbg32_move_expand
+#define Dbg_move_input Dbg32_move_input
+#define Dbg_move_outmove Dbg32_move_outmove
+#define Dbg_move_outsctadj Dbg32_move_outsctadj
+#define Dbg_move_parexpn Dbg32_move_parexpn
+
+#define Dbg_reloc_apply_reg Dbg32_reloc_apply_reg
+#define Dbg_reloc_apply_val Dbg32_reloc_apply_val
+#define Dbg_reloc_ars_entry Dbg32_reloc_ars_entry
+#define Dbg_reloc_copy Dbg32_reloc_copy
+#define Dbg_reloc_discard Dbg32_reloc_discard
+#define Dbg_reloc_doact Dbg32_reloc_doact
+#define Dbg_reloc_doact_title Dbg32_reloc_doact_title
+#define Dbg_reloc_dooutrel Dbg32_reloc_dooutrel
+#define Dbg_reloc_entry Dbg32_reloc_entry
+#define Dbg_reloc_error Dbg32_reloc_error
+#define Dbg_reloc_generate Dbg32_reloc_generate
+#define Dbg_reloc_in Dbg32_reloc_in
+#define Dbg_reloc_ors_entry Dbg32_reloc_ors_entry
+#define Dbg_reloc_out Dbg32_reloc_out
+#define Dbg_reloc_proc Dbg32_reloc_proc
+#define Dbg_reloc_run Dbg32_reloc_run
+#define Dbg_reloc_transition Dbg32_reloc_transition
+#define Dbg_reloc_sloppycomdat Dbg32_reloc_sloppycomdat
+
+#define Dbg_sec_added Dbg32_sec_added
+#define Dbg_sec_created Dbg32_sec_created
+#define Dbg_sec_discarded Dbg32_sec_discarded
+#define Dbg_sec_genstr_compress Dbg32_sec_genstr_compress
+#define Dbg_sec_group Dbg32_sec_group
+#define Dbg_sec_in Dbg32_sec_in
+#define Dbg_sec_order_error Dbg32_sec_order_error
+#define Dbg_sec_order_list Dbg32_sec_order_list
+#define Dbg_sec_strtab Dbg32_sec_strtab
+#define Dbg_sec_unsup_strmerge Dbg32_sec_unsup_strmerge
+
+#define Dbg_seg_desc_entry Dbg32_seg_desc_entry
+#define Dbg_seg_entry Dbg32_seg_entry
+#define Dbg_seg_list Dbg32_seg_list
+#define Dbg_seg_os Dbg32_seg_os
+#define Dbg_seg_title Dbg32_seg_title
+
+#define Dbg_shdr_modified Dbg32_shdr_modified
+
+#define Dbg_statistics_ar Dbg32_statistics_ar
+#define Dbg_statistics_ld Dbg32_statistics_ld
+
+#define Dbg_support_action Dbg32_support_action
+#define Dbg_support_load Dbg32_support_load
+#define Dbg_support_req Dbg32_support_req
+
+#define Dbg_syminfo_entry Dbg32_syminfo_entry
+#define Dbg_syminfo_title Dbg32_syminfo_title
+
+#define Dbg_syms_ar_checking Dbg32_syms_ar_checking
+#define Dbg_syms_ar_entry Dbg32_syms_ar_entry
+#define Dbg_syms_ar_resolve Dbg32_syms_ar_resolve
+#define Dbg_syms_ar_title Dbg32_syms_ar_title
+#define Dbg_syms_created Dbg32_syms_created
+#define Dbg_syms_discarded Dbg32_syms_discarded
+#define Dbg_syms_dlsym Dbg32_syms_dlsym
+#define Dbg_syms_dup_sort_addr Dbg32_syms_dup_sort_addr
+#define Dbg_syms_entered Dbg32_syms_entered
+#define Dbg_syms_entry Dbg32_syms_entry
+#define Dbg_syms_global Dbg32_syms_global
+#define Dbg_syms_ignore Dbg32_syms_ignore
+#define Dbg_syms_ignore_gnuver Dbg32_syms_ignore_gnuver
+#define Dbg_syms_lazy_rescan Dbg32_syms_lazy_rescan
+#define Dbg_syms_lookup Dbg32_syms_lookup
+#define Dbg_syms_lookup_aout Dbg32_syms_lookup_aout
+#define Dbg_syms_new Dbg32_syms_new
+#define Dbg_syms_old Dbg32_syms_old
+#define Dbg_syms_process Dbg32_syms_process
+#define Dbg_syms_reduce Dbg32_syms_reduce
+#define Dbg_syms_reloc Dbg32_syms_reloc
+#define Dbg_syms_resolved Dbg32_syms_resolved
+#define Dbg_syms_resolving Dbg32_syms_resolving
+#define Dbg_syms_sec_entry Dbg32_syms_sec_entry
+#define Dbg_syms_sec_title Dbg32_syms_sec_title
+#define Dbg_syms_spec_title Dbg32_syms_spec_title
+#define Dbg_syms_updated Dbg32_syms_updated
+#define Dbg_syms_up_title Dbg32_syms_up_title
+
+#define Dbg_util_broadcast Dbg32_util_broadcast
+#define Dbg_util_call_array Dbg32_util_call_array
+#define Dbg_util_call_fini Dbg32_util_call_fini
+#define Dbg_util_call_init Dbg32_util_call_init
+#define Dbg_util_call_main Dbg32_util_call_main
+#define Dbg_util_collect Dbg32_util_collect
+#define Dbg_util_dbnotify Dbg32_util_dbnotify
+#define Dbg_util_edge_in Dbg32_util_edge_in
+#define Dbg_util_edge_out Dbg32_util_edge_out
+#define Dbg_util_intoolate Dbg32_util_intoolate
+#define Dbg_util_lcinterface Dbg32_util_lcinterface
+#define Dbg_util_nl Dbg32_util_nl
+#define Dbg_util_no_init Dbg32_util_no_init
+#define Dbg_util_scc_entry Dbg32_util_scc_entry
+#define Dbg_util_scc_title Dbg32_util_scc_title
+#define Dbg_util_str Dbg32_util_str
+#define Dbg_util_wait Dbg32_util_wait
+
+#define Dbg_unused_file Dbg32_unused_file
+#define Dbg_unused_lcinterface Dbg32_unused_lcinterface
+#define Dbg_unused_path Dbg32_unused_path
+#define Dbg_unused_sec Dbg32_unused_sec
+#define Dbg_unused_unref Dbg32_unused_unref
+
+#define Dbg_ver_avail_entry Dbg32_ver_avail_entry
+#define Dbg_ver_avail_title Dbg32_ver_avail_title
+#define Dbg_ver_def_title Dbg32_ver_def_title
+#define Dbg_ver_desc_entry Dbg32_ver_desc_entry
+#define Dbg_ver_need_entry Dbg32_ver_need_entry
+#define Dbg_ver_need_title Dbg32_ver_need_title
+#define Dbg_ver_nointerface Dbg32_ver_nointerface
+#define Dbg_ver_symbol Dbg32_ver_symbol
+
+#endif
+
+/*
+ * External Dbg_*() interface routines.
+ */
+extern void Dbg_args_files(Lm_list *, int, char *);
+extern void Dbg_args_flags(Lm_list *, int, int);
+extern void Dbg_audit_ignore(Rt_map *);
+extern void Dbg_audit_interface(Lm_list *, const char *, const char *);
+extern void Dbg_audit_lib(Lm_list *, const char *);
+extern void Dbg_audit_object(Lm_list *, const char *, const char *);
+extern void Dbg_audit_symval(Lm_list *, const char *, const char *,
+ const char *, Addr, Addr);
+extern void Dbg_audit_skip(Lm_list *, const char *, const char *);
+extern void Dbg_audit_terminate(Lm_list *, const char *);
+extern void Dbg_audit_version(Lm_list *, const char *, ulong_t);
+
+extern void Dbg_bind_global(Rt_map *, Addr, Off, Xword, Pltbindtype,
+ Rt_map *, Addr, Off, const char *, uint_t);
+extern void Dbg_bind_plt_summary(Lm_list *, Half, Word, Word, Word, Word,
+ Word, Word);
+#if defined(_ELF64)
+extern void Dbg_bind_pltpad_from(Rt_map *, Addr, const char *);
+extern void Dbg_bind_pltpad_to(Rt_map *, Addr, const char *, const char *);
+#endif
+extern void Dbg_bind_reject(Rt_map *, Rt_map *, const char *, int);
+extern void Dbg_bind_weak(Rt_map *, Addr, Addr, const char *);
+
+extern void Dbg_cap_hw_candidate(Lm_list *, const char *);
+extern void Dbg_cap_hw_filter(Lm_list *, const char *, Rt_map *);
+extern void Dbg_cap_mapfile(Lm_list *, Xword, Xword, Half);
+extern void Dbg_cap_sec_entry(Lm_list *, uint_t, Xword, Xword, Half);
+extern void Dbg_cap_sec_title(Ofl_desc *);
+extern void Dbg_cap_val_hw1(Lm_list *, Xword, Half);
+
+extern const char *
+ Dbg_demangle_name(const char *);
+
+extern void Dbg_ent_entry(Lm_list *, Half, Ent_desc *);
+extern void Dbg_ent_print(Lm_list *, Half, List *, Boolean);
+
+extern void Dbg_file_analyze(Rt_map *);
+extern void Dbg_file_aout(Lm_list *, const char *, ulong_t, ulong_t,
+ ulong_t, const char *, Aliste);
+extern void Dbg_file_ar(Lm_list *, const char *, int);
+extern void Dbg_file_ar_rescan(Lm_list *);
+extern void Dbg_file_bind_entry(Lm_list *, Bnd_desc *);
+extern void Dbg_file_bindings(Rt_map *, int);
+extern void Dbg_file_cleanup(Lm_list *, const char *, Aliste);
+extern void Dbg_file_cntl(Lm_list *, Aliste, Aliste);
+extern void Dbg_file_config_dis(Lm_list *, const char *, int);
+extern void Dbg_file_config_obj(Lm_list *, const char *, const char *,
+ const char *);
+extern void Dbg_file_del_rescan(Lm_list *);
+extern void Dbg_file_delete(Rt_map *);
+extern void Dbg_file_dlclose(Lm_list *, const char *, int);
+extern void Dbg_file_dldump(Rt_map *, const char *, int);
+extern void Dbg_file_dlopen(Rt_map *, const char *, int *, int);
+extern void Dbg_file_elf(Lm_list *, const char *, ulong_t, ulong_t,
+ ulong_t, ulong_t, const char *, Aliste);
+extern void Dbg_file_filtee(Lm_list *, const char *, const char *, int);
+extern void Dbg_file_filter(Lm_list *, const char *, const char *, int);
+extern void Dbg_file_fixname(Lm_list *, const char *, const char *);
+extern void Dbg_file_generic(Lm_list *, Ifl_desc *);
+extern void Dbg_file_hdl_action(Grp_hdl *, Rt_map *, int, uint_t);
+extern void Dbg_file_hdl_collect(Grp_hdl *, const char *);
+extern void Dbg_file_hdl_title(int);
+extern void Dbg_file_lazyload(Rt_map *, const char *, const char *);
+extern void Dbg_file_ldso(Rt_map *, char **, auxv_t *, const char *,
+ Aliste);
+extern void Dbg_file_mode_promote(Rt_map *, int);
+extern void Dbg_file_modified(Lm_list *, const char *, const char *,
+ const char *, int, int, Elf *, Elf *);
+extern void Dbg_file_needed(Rt_map *, const char *);
+extern void Dbg_file_output(Ofl_desc *);
+extern void Dbg_file_preload(Lm_list *, const char *);
+extern void Dbg_file_prot(Rt_map *, int);
+extern void Dbg_file_rejected(Lm_list *, Rej_desc *, Half mach);
+extern void Dbg_file_reuse(Lm_list *, const char *, const char *);
+extern void Dbg_file_skip(Lm_list *, const char *, const char *);
+
+extern void Dbg_got_display(Ofl_desc *, Off, int, Word, size_t);
+
+extern void Dbg_libs_audit(Lm_list *, const char *, const char *);
+extern void Dbg_libs_find(Lm_list *, const char *);
+extern void Dbg_libs_found(Lm_list *, const char *, int);
+extern void Dbg_libs_ignore(Lm_list *, const char *);
+extern void Dbg_libs_init(Lm_list *, List *, List *);
+extern void Dbg_libs_l(Lm_list *, const char *, const char *);
+extern void Dbg_libs_path(Lm_list *, const char *, uint_t, const char *);
+extern void Dbg_libs_req(Lm_list *, const char *, const char *,
+ const char *);
+extern void Dbg_libs_update(Lm_list *, List *, List *);
+extern void Dbg_libs_yp(Lm_list *, const char *);
+extern void Dbg_libs_ylu(Lm_list *, const char *, const char *, int);
+
+extern void Dbg_map_dash(Lm_list *, const char *, Sdf_desc *);
+extern void Dbg_map_ent(Lm_list *, Boolean, Ent_desc *, Ofl_desc *);
+extern void Dbg_map_parse(Lm_list *, const char *);
+extern void Dbg_map_pipe(Lm_list *, Sg_desc *, const char *, const Word);
+extern void Dbg_map_seg(Ofl_desc *, int, Sg_desc *);
+extern void Dbg_map_set_atsign(Boolean);
+extern void Dbg_map_set_equal(Boolean);
+extern void Dbg_map_size_new(Lm_list *, const char *);
+extern void Dbg_map_size_old(Ofl_desc *, Sym_desc *);
+extern void Dbg_map_sort_fini(Lm_list *, Sg_desc *);
+extern void Dbg_map_sort_orig(Lm_list *, Sg_desc *);
+extern void Dbg_map_symbol(Ofl_desc *, Sym_desc *);
+extern void Dbg_map_version(Lm_list *, const char *, const char *, int);
+
+extern void Dbg_move_adjexpandreloc(Lm_list *, Xword, const char *);
+extern void Dbg_move_adjmovereloc(Lm_list *, Xword, Xword, const char *);
+extern void Dbg_move_data(Rt_map *);
+extern void Dbg_move_entry1(Lm_list *, int, Move *, Sym_desc *);
+extern void Dbg_move_entry2(Lm_list *, Move *, Word, const char *);
+extern void Dbg_move_expand(Lm_list *, Move *, Addr);
+extern void Dbg_move_input(Lm_list *, const char *);
+extern void Dbg_move_outmove(Lm_list *, const char *);
+extern void Dbg_move_outsctadj(Lm_list *, Sym_desc *);
+extern void Dbg_move_parexpn(Lm_list *, const char *, const char *);
+
+extern void Dbg_reloc_apply_reg(Lm_list *, int, Half, Xword, Xword);
+extern void Dbg_reloc_apply_val(Lm_list *, int, Xword, Xword);
+extern void Dbg_reloc_ars_entry(Lm_list *, int, Word, Half, Rel_desc *);
+extern void Dbg_reloc_copy(Rt_map *, Rt_map *, const char *, int);
+extern void Dbg_reloc_discard(Lm_list *, Half, Rel_desc *);
+extern void Dbg_reloc_doact(Lm_list *, int, Half, Word, Word, Xword, Xword,
+ const char *, Os_desc *);
+extern void Dbg_reloc_doact_title(Lm_list *);
+extern void Dbg_reloc_dooutrel(Lm_list *, Word);
+extern void Dbg_reloc_entry(Lm_list *, const char *, Half, Word, void *,
+ const char *, const char *, const char *);
+extern void Dbg_reloc_error(Lm_list *, int, Half, Word, void *,
+ const char *);
+extern void Dbg_reloc_generate(Lm_list *, Os_desc *, Word);
+extern void Dbg_reloc_in(Lm_list *, int, Half, Word, void *, const char *,
+ const char *);
+extern void Dbg_reloc_ors_entry(Lm_list *, int, Word, Half, Rel_desc *);
+extern void Dbg_reloc_out(Ofl_desc *, int, Word, void *, const char *,
+ const char *);
+extern void Dbg_reloc_proc(Lm_list *, Os_desc *, Is_desc *, Is_desc *);
+extern void Dbg_reloc_run(Rt_map *, uint_t, int, int);
+extern void Dbg_reloc_transition(Lm_list *, Half, Word, Rel_desc *);
+extern void Dbg_reloc_sloppycomdat(Lm_list *, const char *, Sym_desc *);
+
+extern void Dbg_sec_added(Lm_list *, Os_desc *, Sg_desc *);
+extern void Dbg_sec_created(Lm_list *, Os_desc *, Sg_desc *);
+extern void Dbg_sec_discarded(Lm_list *, Is_desc *, Is_desc *);
+extern void Dbg_sec_genstr_compress(Lm_list *, const char *,
+ Xword, Xword);
+extern void Dbg_sec_group(Lm_list *, Is_desc *, Group_desc *);
+extern void Dbg_sec_in(Lm_list *, Is_desc *);
+extern void Dbg_sec_order_error(Lm_list *, Ifl_desc *, Word, int);
+extern void Dbg_sec_order_list(Ofl_desc *, int);
+extern void Dbg_sec_strtab(Lm_list *, Os_desc *, Str_tbl *);
+extern void Dbg_sec_unsup_strmerge(Lm_list *, Is_desc *);
+
+extern void Dbg_seg_desc_entry(Lm_list *, Half, int, Sg_desc *);
+extern void Dbg_seg_entry(Ofl_desc *, int, Sg_desc *);
+extern void Dbg_seg_list(Lm_list *, Half, List *);
+extern void Dbg_seg_os(Ofl_desc *, Os_desc *, int);
+extern void Dbg_seg_title(Lm_list *);
+
+extern void Dbg_shdr_modified(Lm_list *, const char *, Half, Shdr *, Shdr *,
+ const char *);
+
+extern void Dbg_statistics_ar(Ofl_desc *);
+extern void Dbg_statistics_ld(Ofl_desc *);
+
+extern void Dbg_support_action(Lm_list *, const char *, const char *,
+ Support_ndx, const char *);
+extern void Dbg_support_load(Lm_list *, const char *, const char *);
+extern void Dbg_support_req(Lm_list *, const char *, int);
+
+extern void Dbg_syminfo_entry(Lm_list *, Word, Syminfo *, Sym *,
+ const char *, Dyn *);
+extern void Dbg_syminfo_title(Lm_list *);
+
+extern void Dbg_syms_ar_checking(Lm_list *, Xword, Elf_Arsym *,
+ const char *);
+extern void Dbg_syms_ar_entry(Lm_list *, Xword, Elf_Arsym *);
+extern void Dbg_syms_ar_resolve(Lm_list *, Xword, Elf_Arsym *,
+ const char *, int);
+extern void Dbg_syms_ar_title(Lm_list *, const char *, int);
+extern void Dbg_syms_created(Lm_list *, const char *);
+extern void Dbg_syms_discarded(Lm_list *, Sym_desc *);
+extern void Dbg_syms_dlsym(Rt_map *, const char *, int *, const char *,
+ int);
+extern void Dbg_syms_dup_sort_addr(Lm_list *, const char *, const char *,
+ const char *, Addr);
+extern void Dbg_syms_entered(Ofl_desc *, Sym *, Sym_desc *);
+extern void Dbg_syms_entry(Lm_list *, Word, Sym_desc *);
+extern void Dbg_syms_global(Lm_list *, Word, const char *);
+extern void Dbg_syms_ignore(Ofl_desc *, Sym_desc *);
+extern void Dbg_syms_ignore_gnuver(Rt_map *, const char *, Word, Versym);
+extern void Dbg_syms_lazy_rescan(Lm_list *, const char *);
+extern void Dbg_syms_lookup(Rt_map *, const char *, const char *);
+#if !(defined(_ELF64))
+extern void Dbg_syms_lookup_aout(Lm_list *, const char *);
+#endif
+extern void Dbg_syms_new(Ofl_desc *, Sym *, Sym_desc *);
+extern void Dbg_syms_old(Ofl_desc *, Sym_desc *);
+extern void Dbg_syms_process(Lm_list *, Ifl_desc *);
+extern void Dbg_syms_reduce(Ofl_desc *, int, Sym_desc *, int,
+ const char *);
+extern void Dbg_syms_reloc(Ofl_desc *, Sym_desc *);
+extern void Dbg_syms_resolved(Ofl_desc *, Sym_desc *);
+extern void Dbg_syms_resolving(Ofl_desc *, Word, const char *, int, int,
+ Sym *, Sym *, Sym_desc *, Ifl_desc *);
+extern void Dbg_syms_sec_entry(Lm_list *, Word, Sg_desc *, Os_desc *);
+extern void Dbg_syms_sec_title(Lm_list *);
+extern void Dbg_syms_spec_title(Lm_list *);
+extern void Dbg_syms_updated(Ofl_desc *, Sym_desc *, const char *);
+extern void Dbg_syms_up_title(Lm_list *);
+
+extern void Dbg_tls_modactivity(Lm_list *, void *, uint_t);
+extern void Dbg_tls_static_block(Lm_list *, void *, ulong_t, ulong_t);
+extern void Dbg_tls_static_resv(Rt_map *, ulong_t, ulong_t);
+
+extern void Dbg_util_broadcast(Rt_map *);
+extern void Dbg_util_call_array(Rt_map *, void *, int, Word);
+extern void Dbg_util_call_fini(Rt_map *);
+extern void Dbg_util_call_init(Rt_map *, int);
+extern void Dbg_util_call_main(Rt_map *);
+extern void Dbg_util_collect(Rt_map *, int, int);
+extern void Dbg_util_dbnotify(Lm_list *, rd_event_e, r_state_e);
+extern void Dbg_util_edge_in(Lm_list *, Rt_map *, uint_t, Rt_map *,
+ int, int);
+extern void Dbg_util_edge_out(Rt_map *, Rt_map *);
+extern void Dbg_util_intoolate(Rt_map *);
+extern void Dbg_util_lcinterface(Rt_map *, int, char *);
+extern void Dbg_util_nl(Lm_list *, int);
+extern void Dbg_util_no_init(Rt_map *);
+extern void Dbg_util_str(Lm_list *, const char *);
+extern void Dbg_util_scc_entry(Rt_map *, uint_t);
+extern void Dbg_util_scc_title(Lm_list *, int);
+extern void Dbg_util_wait(Rt_map *, Rt_map *, int);
+
+extern void Dbg_unused_file(Lm_list *, const char *, int, uint_t);
+extern void Dbg_unused_lcinterface(Rt_map *, Rt_map *, int);
+extern void Dbg_unused_path(Lm_list *, const char *, uint_t, uint_t,
+ const char *);
+extern void Dbg_unused_sec(Lm_list *, Is_desc *);
+extern void Dbg_unused_unref(Rt_map *, const char *);
+
+extern void Dbg_ver_avail_entry(Lm_list *, Ver_index *, const char *);
+extern void Dbg_ver_avail_title(Lm_list *, const char *);
+extern void Dbg_ver_def_title(Lm_list *, const char *);
+extern void Dbg_ver_desc_entry(Lm_list *, Ver_desc *);
+extern void Dbg_ver_need_entry(Lm_list *, Half, const char *,
+ const char *);
+extern void Dbg_ver_need_title(Lm_list *, const char *);
+extern void Dbg_ver_nointerface(Lm_list *, const char *);
+extern void Dbg_ver_symbol(Lm_list *, const char *);
+
+/*
+ * Define Elf_*() interface flags.
+ */
+#define ELF_DBG_ELFDUMP 1
+#define ELF_DBG_RTLD 2
+#define ELF_DBG_LD 3
+
+/*
+ * Define generic Elf_*() interfaces.
+ */
+extern void Elf_syminfo_entry(Lm_list *, Word, Syminfo *, const char *,
+ const char *);
+extern void Elf_syminfo_title(Lm_list *);
+
+/*
+ * Establish ELF32 and ELF64 class Elf_*() interfaces.
+ */
+#if defined(_ELF64)
+
+#define Elf_cap_entry Elf64_cap_entry
+#define Elf_cap_title Elf64_cap_title
+
+#define Elf_demangle_name Elf64_demangle_name
+#define Elf_dyn_entry Elf64_dyn_entry
+#define Elf_dyn_null_entry Elf64_dyn_null_entry
+#define Elf_dyn_title Elf64_dyn_title
+
+#define Elf_ehdr Elf64_ehdr
+
+#define Elf_got_entry Elf64_got_entry
+#define Elf_got_title Elf64_got_title
+
+#define Elf_reloc_apply_reg Elf64_reloc_apply_reg
+#define Elf_reloc_apply_val Elf64_reloc_apply_val
+#define Elf_reloc_entry_1 Elf64_reloc_entry_1
+#define Elf_reloc_entry_2 Elf64_reloc_entry_2
+#define Elf_reloc_title Elf64_reloc_title
+
+#define Elf_phdr Elf64_phdr
+
+#define Elf_shdr Elf64_shdr
+
+#define Elf_syms_table_entry Elf64_syms_table_entry
+#define Elf_syms_table_title Elf64_syms_table_title
+
+#define Elf_ver_def_title Elf64_ver_def_title
+#define Elf_ver_line_1 Elf64_ver_line_1
+#define Elf_ver_line_2 Elf64_ver_line_2
+#define Elf_ver_line_3 Elf64_ver_line_3
+#define Elf_ver_line_4 Elf64_ver_line_4
+#define Elf_ver_line_5 Elf64_ver_line_5
+#define Elf_ver_need_title Elf64_ver_need_title
+
+#else
+
+#define Elf_cap_entry Elf32_cap_entry
+#define Elf_cap_title Elf32_cap_title
+
+#define Elf_demangle_name Elf32_demangle_name
+#define Elf_dyn_entry Elf32_dyn_entry
+#define Elf_dyn_null_entry Elf32_dyn_null_entry
+#define Elf_dyn_title Elf32_dyn_title
+
+#define Elf_ehdr Elf32_ehdr
+
+#define Elf_got_entry Elf32_got_entry
+#define Elf_got_title Elf32_got_title
+
+#define Elf_reloc_apply_reg Elf32_reloc_apply_reg
+#define Elf_reloc_apply_val Elf32_reloc_apply_val
+#define Elf_reloc_entry_1 Elf32_reloc_entry_1
+#define Elf_reloc_entry_2 Elf32_reloc_entry_2
+#define Elf_reloc_title Elf32_reloc_title
+
+#define Elf_phdr Elf32_phdr
+
+#define Elf_shdr Elf32_shdr
+
+#define Elf_syms_table_entry Elf32_syms_table_entry
+#define Elf_syms_table_title Elf32_syms_table_title
+
+#define Elf_ver_def_title Elf32_ver_def_title
+#define Elf_ver_line_1 Elf32_ver_line_1
+#define Elf_ver_line_2 Elf32_ver_line_2
+#define Elf_ver_line_3 Elf32_ver_line_3
+#define Elf_ver_line_4 Elf32_ver_line_4
+#define Elf_ver_line_5 Elf32_ver_line_5
+#define Elf_ver_need_title Elf32_ver_need_title
+
+#endif
+
+extern void Elf_cap_entry(Lm_list *, Cap *, int, Half);
+extern void Elf_cap_title(Lm_list *);
+
+extern const char \
+ *Elf_demangle_name(const char *);
+extern void Elf_dyn_entry(Lm_list *, Dyn *, int, const char *, Half);
+extern void Elf_dyn_null_entry(Lm_list *, Dyn *, int, int);
+extern void Elf_dyn_title(Lm_list *);
+
+extern void Elf_ehdr(Lm_list *, Ehdr *, Shdr *);
+
+extern void Elf_got_entry(Lm_list *, Sword, Addr, Xword, Half,
+ uchar_t, uchar_t, Word, void *, const char *);
+extern void Elf_got_title(Lm_list *);
+
+extern void Elf_phdr(Lm_list *, Half, Phdr *);
+
+extern void Elf_reloc_apply_val(Lm_list *, int, Xword, Xword);
+extern void Elf_reloc_apply_reg(Lm_list *, int, Half, Xword, Xword);
+extern void Elf_reloc_entry_1(Lm_list *, int, const char *, Half, Word,
+ void *, const char *, const char *, const char *);
+extern void Elf_reloc_entry_2(Lm_list *, int, const char *, Word,
+ const char *, Off, Sxword, const char *, const char *,
+ const char *);
+extern void Elf_reloc_title(Lm_list *, int, Word);
+
+extern void Elf_shdr(Lm_list *, Half, Shdr *);
+
+extern void Elf_syms_table_entry(Lm_list *, int, const char *, Half, Sym *,
+ Versym, int, const char *, const char *);
+extern void Elf_syms_table_title(Lm_list *, int);
+
+extern void Elf_ver_def_title(Lm_list *);
+extern void Elf_ver_line_1(Lm_list *, const char *, const char *,
+ const char *, const char *);
+extern void Elf_ver_line_2(Lm_list *, const char *, const char *);
+extern void Elf_ver_line_3(Lm_list *, const char *, const char *,
+ const char *);
+extern void Elf_ver_line_4(Lm_list *, const char *);
+extern void Elf_ver_line_5(Lm_list *, const char *, const char *);
+extern void Elf_ver_need_title(Lm_list *, int);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DEBUG_H */
diff --git a/cmd/sgs/include/sgs.h b/cmd/sgs/include/sgs.h
new file mode 100644
index 000000000000..34669cf60b3a
--- /dev/null
+++ b/cmd/sgs/include/sgs.h
@@ -0,0 +1,296 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright (c) 1988 AT&T
+ * All Rights Reserved
+ *
+ *
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Global include file for all sgs.
+ */
+
+#ifndef _SGS_H
+#define _SGS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* <assert.h> keys off of NDEBUG */
+#ifdef DEBUG
+#undef NDEBUG
+#else
+#define NDEBUG
+#endif
+
+#ifndef _ASM
+#include <sys/types.h>
+#if defined(sun)
+#include <sys/machelf.h>
+#else
+#include <elf.h>
+#endif
+#include <stdlib.h>
+#include <libelf.h>
+#include <assert.h>
+#include <alist.h>
+#endif /* _ASM */
+
+/*
+ * Software identification.
+ */
+#define SGS ""
+#define SGU_PKG "Software Generation Utilities"
+#define SGU_REL "(SGU) Solaris-ELF (4.0)"
+
+
+#ifndef _ASM
+
+/*
+ * link_ver_string[] contains a version string for use by the link-editor
+ * and all other linker components. It is found in libconv, and is
+ * generated by sgs/libconv/common/bld_vernote.ksh. That script produces
+ * libconv/{plat}/vernote.s, which is in turn assembled/linked into
+ * libconv.
+ */
+extern const char link_ver_string[];
+/*
+ * Macro to round to next double word boundary.
+ */
+#define S_DROUND(x) (((x) + sizeof (double) - 1) & ~(sizeof (double) - 1))
+
+/*
+ * General align and round macros.
+ */
+#define S_ALIGN(x, a) ((x) & ~(((a) ? (a) : 1) - 1))
+#define S_ROUND(x, a) ((x) + (((a) ? (a) : 1) - 1) & ~(((a) ? (a) : 1) - 1))
+
+/*
+ * Bit manipulation macros; generic bit mask and is `v' in the range
+ * supportable in `n' bits?
+ */
+#define S_MASK(n) ((1 << (n)) -1)
+#define S_INRANGE(v, n) (((-(1 << (n)) - 1) < (v)) && ((v) < (1 << (n))))
+
+
+/*
+ * Yet another definition of the OFFSETOF macro, used with the AVL routines.
+ */
+#define SGSOFFSETOF(s, m) ((size_t)(&(((s *)0)->m)))
+
+/*
+ * When casting between integer and pointer types, gcc will complain
+ * if the integer type used is not large enough to hold the pointer
+ * value without loss. Although a dubious practice in general, this
+ * is sometimes done by design. In those cases, the general solution
+ * is to introduce an intermediate cast to widen the integer value. The
+ * CAST_PTRINT macro does this, and its use documents the fact that
+ * the programmer is doing that sort of cast.
+ */
+#ifdef __GNUC__
+#define CAST_PTRINT(cast, value) ((cast)(uintptr_t)value)
+#else
+#define CAST_PTRINT(cast, value) ((cast)value)
+#endif
+
+/*
+ * General typedefs.
+ */
+typedef enum {
+ FALSE = 0,
+ TRUE = 1
+} Boolean;
+
+/*
+ * Types of errors (used by eprintf()), together with a generic error return
+ * value.
+ */
+typedef enum {
+ ERR_NONE,
+ ERR_WARNING,
+ ERR_FATAL,
+ ERR_ELF,
+ ERR_NUM /* Must be last */
+} Error;
+
+#if defined(_LP64) && !defined(_ELF64)
+#define S_ERROR (~(uint_t)0)
+#else
+#define S_ERROR (~(uintptr_t)0)
+#endif
+
+/*
+ * LIST_TRAVERSE() is used as the only "argument" of a "for" loop to
+ * traverse a linked list. The node pointer `node' is set to each node in
+ * turn and the corresponding data pointer is copied to `data'. The macro
+ * is used as in
+ * for (LIST_TRAVERSE(List *list, Listnode *node, void *data)) {
+ * process(data);
+ * }
+ */
+#define LIST_TRAVERSE(L, N, D) \
+ (void) (((N) = (L)->head) != NULL && ((D) = (N)->data) != NULL); \
+ (N) != NULL; \
+ (void) (((N) = (N)->next) != NULL && ((D) = (N)->data) != NULL)
+
+typedef struct listnode Listnode;
+typedef struct list List;
+
+struct listnode { /* a node on a linked list */
+ void *data; /* the data item */
+ Listnode *next; /* the next element */
+};
+
+struct list { /* a linked list */
+ Listnode *head; /* the first element */
+ Listnode *tail; /* the last element */
+};
+
+
+#ifdef _SYSCALL32
+typedef struct listnode32 Listnode32;
+typedef struct list32 List32;
+
+struct listnode32 { /* a node on a linked list */
+ Elf32_Addr data; /* the data item */
+ Elf32_Addr next; /* the next element */
+};
+
+struct list32 { /* a linked list */
+ Elf32_Addr head; /* the first element */
+ Elf32_Addr tail; /* the last element */
+};
+#endif /* _SYSCALL32 */
+
+
+/*
+ * Structure to maintain rejected files elf information. Files that are not
+ * applicable to the present link-edit are rejected and a search for an
+ * appropriate file may be resumed. The first rejected files information is
+ * retained so that a better error diagnostic can be given should an appropriate
+ * file not be located.
+ */
+typedef struct {
+ ushort_t rej_type; /* SGS_REJ_ value */
+ ushort_t rej_flag; /* additional information */
+ uint_t rej_info; /* numeric and string information */
+ const char *rej_str; /* associated with error */
+ const char *rej_name; /* object name - expanded library */
+ /* name and archive members */
+} Rej_desc;
+
+#define SGS_REJ_NONE 0
+#define SGS_REJ_MACH 1 /* wrong ELF machine type */
+#define SGS_REJ_CLASS 2 /* wrong ELF class (32-bit/64-bit) */
+#define SGS_REJ_DATA 3 /* wrong ELF data format (MSG/LSB) */
+#define SGS_REJ_TYPE 4 /* bad ELF type */
+#define SGS_REJ_BADFLAG 5 /* bad ELF flags value */
+#define SGS_REJ_MISFLAG 6 /* mismatched ELF flags value */
+#define SGS_REJ_VERSION 7 /* mismatched ELF/lib version */
+#define SGS_REJ_HAL 8 /* HAL R1 extensions required */
+#define SGS_REJ_US3 9 /* Sun UltraSPARC III extensions */
+ /* required */
+#define SGS_REJ_STR 10 /* generic error - info is a string */
+#define SGS_REJ_UNKFILE 11 /* unknown file type */
+#define SGS_REJ_HWCAP_1 12 /* hardware capabilities mismatch */
+
+/*
+ * For those source files used both inside and outside of the
+ * libld source base (tools/common/string_table.c) we can
+ * automatically switch between the allocation models
+ * based off of the 'cc -DUSE_LIBLD_MALLOC' flag.
+ */
+#ifdef USE_LIBLD_MALLOC
+#define calloc(x, a) libld_malloc(((size_t)x) * ((size_t)a))
+#define free libld_free
+#define malloc libld_malloc
+#define realloc libld_realloc
+
+#define libld_calloc(x, a) libld_malloc(((size_t)x) * ((size_t)a))
+extern void libld_free(void *);
+extern void *libld_malloc(size_t);
+extern void *libld_realloc(void *, size_t);
+#endif
+
+
+/*
+ * Data structures (defined in libld.h).
+ */
+typedef struct ent_desc Ent_desc;
+typedef struct group_desc Group_desc;
+typedef struct ifl_desc Ifl_desc;
+typedef struct is_desc Is_desc;
+typedef struct isa_desc Isa_desc;
+typedef struct isa_opt Isa_opt;
+typedef struct mv_desc Mv_desc;
+typedef struct ofl_desc Ofl_desc;
+typedef struct os_desc Os_desc;
+typedef struct rel_cache Rel_cache;
+typedef struct sdf_desc Sdf_desc;
+typedef struct sdv_desc Sdv_desc;
+typedef struct sg_desc Sg_desc;
+typedef struct sort_desc Sort_desc;
+typedef struct sec_order Sec_order;
+typedef struct sym_desc Sym_desc;
+typedef struct sym_aux Sym_aux;
+typedef struct sym_avlnode Sym_avlnode;
+typedef struct uts_desc Uts_desc;
+typedef struct ver_desc Ver_desc;
+typedef struct ver_index Ver_index;
+typedef struct audit_desc Audit_desc;
+typedef struct audit_info Audit_info;
+typedef struct audit_list Audit_list;
+
+/*
+ * Data structures defined in machrel.h.
+ */
+typedef struct rel_desc Rel_desc;
+
+/*
+ * Data structures defined in rtld.h.
+ */
+typedef struct lm_list Lm_list;
+#ifdef _SYSCALL32
+typedef struct lm_list32 Lm_list32;
+#endif /* _SYSCALL32 */
+
+/*
+ * For the various utilities that include sgs.h
+ */
+extern int assfail(const char *, const char *, int);
+extern void eprintf(Lm_list *, Error, const char *, ...);
+extern char *sgs_demangle(char *);
+extern uint_t sgs_str_hash(const char *);
+extern uint_t findprime(uint_t);
+
+#endif /* _ASM */
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* _SGS_H */
diff --git a/cmd/sgs/include/string_table.h b/cmd/sgs/include/string_table.h
new file mode 100644
index 000000000000..e42f81779f17
--- /dev/null
+++ b/cmd/sgs/include/string_table.h
@@ -0,0 +1,63 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _STRING_TABLE_DOT_H
+#define _STRING_TABLE_DOT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Exported, opaque string table handle.
+ */
+typedef struct str_tbl Str_tbl;
+
+/*
+ * Exported string table functions.
+ */
+extern int st_delstring(Str_tbl *, const char *);
+extern void st_destroy(Str_tbl *);
+extern size_t st_getstrtab_sz(Str_tbl *);
+extern const char *st_getstrbuf(Str_tbl *);
+extern int st_insert(Str_tbl *, const char *);
+extern Str_tbl *st_new(uint_t);
+extern int st_setstrbuf(Str_tbl *, char *, size_t);
+extern int st_setstring(Str_tbl *, const char *, size_t *);
+
+/*
+ * Exported flags values for st_new().
+ */
+#define FLG_STNEW_COMPRESS 0x01 /* compressed string table */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STRING_TABLE_DOT_H */
diff --git a/cmd/sgs/messages/sgs.ident b/cmd/sgs/messages/sgs.ident
new file mode 100644
index 000000000000..6afbf5ff5e26
--- /dev/null
+++ b/cmd/sgs/messages/sgs.ident
@@ -0,0 +1,62 @@
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# 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
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+#
+# Global message identifiers for the sgs utilities. This information is read
+# by sgsmsg(1l) using the -i option.
+# Each utilities message file references one of the `MSGID' identifiers. Its
+# associated numeric setid is used when creating catgets(3c) messages, the
+# string domain is used when creating gettext(3i) messages.
+#
+
+mesgid setid domain
+
+MSG_ID_RTLD 1 SUNW_OST_SGS /* sgs/rtld */
+MSG_ID_LIBRTLD 2 SUNW_OST_SGS /* sgs/librtld */
+MSG_ID_LIBLD 3 SUNW_OST_SGS /* sgs/libld */
+MSG_ID_LIBLDDBG 4 SUNW_OST_SGS /* sgs/liblddbg */
+MSG_ID_LIBLDSTAB 5 SUNW_OST_SGS /* sgs/libldstab */
+MSG_ID_LIBRTLD_DB 6 SUNW_OST_SGS /* sgs/librtld_db */
+MSG_ID_LIBPROF 7 SUNW_OST_SGS /* sgs/libprof */
+MSG_ID_LIBCRLE 8 SUNW_OST_SGS /* sgs/libcrle */
+
+MSG_ID_LIBELF 10 SUNW_OST_SGS /* sgs/libelf */
+
+MSG_ID_LD 20 SUNW_OST_SGS /* sgs/ld */
+MSG_ID_LDD 21 SUNW_OST_SGS /* sgs/ldd */
+MSG_ID_PVS 22 SUNW_OST_SGS /* sgs/pvs */
+MSG_ID_CRLE 23 SUNW_OST_SGS /* sgs/crle */
+MSG_ID_ELFDUMP 24 SUNW_OST_SGS /* sgs/elfdump */
+MSG_ID_MOE 25 SUNW_OST_SGS /* sgs/moe */
+MSG_ID_ELFEDIT 26 SUNW_OST_SGS /* sgs/elfedit */
+MSG_ID_ELFEDIT_CAP 27 SUNW_OST_SGS /* cap: */
+MSG_ID_ELFEDIT_DYN 27 SUNW_OST_SGS /* dyn: */
+MSG_ID_ELFEDIT_EHDR 27 SUNW_OST_SGS /* ehdr: */
+MSG_ID_ELFEDIT_PHDR 27 SUNW_OST_SGS /* phdr: */
+MSG_ID_ELFEDIT_SHDR 27 SUNW_OST_SGS /* shdr: */
+MSG_ID_ELFEDIT_STR 27 SUNW_OST_SGS /* str: */
+MSG_ID_ELFEDIT_SYM 27 SUNW_OST_SGS /* sym: */
+MSG_ID_ELFEDIT_SYMINFO 27 SUNW_OST_SGS /* syminfo: */
+MSG_ID_ELFWRAP 28 SUNW_OST_SGS /* sgs/elfwrap */
diff --git a/cmd/sgs/tools/common/findprime.c b/cmd/sgs/tools/common/findprime.c
new file mode 100644
index 000000000000..299fa21362c7
--- /dev/null
+++ b/cmd/sgs/tools/common/findprime.c
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sgs.h>
+
+/*
+ * function that will find a prime'ish number. Usefull for
+ * hashbuckets and related things.
+ */
+uint_t
+findprime(uint_t count)
+{
+ uint_t h, f;
+
+ if (count <= 3)
+ return (3);
+
+
+ /*
+ * Check to see if divisible by two, if so
+ * increment.
+ */
+ if ((count & 0x1) == 0)
+ count++;
+
+ for (h = count, f = 2; f * f <= h; f++)
+ if ((h % f) == 0)
+ h += f = 1;
+ return (h);
+}
diff --git a/cmd/sgs/tools/common/sgsmsg.c b/cmd/sgs/tools/common/sgsmsg.c
new file mode 100644
index 000000000000..9b2e37b507d6
--- /dev/null
+++ b/cmd/sgs/tools/common/sgsmsg.c
@@ -0,0 +1,1210 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * sgsmsg generates several message files from an input template file. Messages
+ * are constructed for use with gettext(3i) - the default - or catgets(3c). The
+ * files generate are:
+ *
+ * msg.h a header file containing definitions for each message. The -h
+ * option triggers the creation of these definitions and specifies
+ * the name to use.
+ *
+ * msg.c a data array of message strings. The msg.h definitions are
+ * offsets into this array. The -d option triggers the creation of
+ * these definitions and specifies the name to use.
+ *
+ * messages a message file suitable for catgets(3c) or gettext(3i) use. The
+ * -m option triggers this output and specifies the filename to be
+ * used.
+ *
+ * The template file is processed based on the first character of each line:
+ *
+ * # or $ entries are copied (as is) to the message file (messages).
+ *
+ * @ token(s) entries are translated. Two translations are possible dependent
+ * on whether one or more tokens are supplied:
+ *
+ * A single token is interpreted as one of two reserved message
+ * output indicators, or a message identifier. The reserved output
+ * indicator _START_ enables output to the message file - Note that
+ * the occurance of any other @ token will also enable message
+ * output. The reserved output indicator _END_ disables output to
+ * the message file. The use of these two indicators provides for
+ * only those message strings that require translation to be output
+ * to the message file.
+ *
+ * Besides the reserved output indicators, a single token is taken
+ * to be a message identifier which will be subsituted for a
+ * `setid' for catgets(3c) output, or a `domain' name for
+ * gettext(3i) output. This value is determine by substituting the
+ * token for the associated definition found in the message
+ * identifier file (specified with the -i option).
+ *
+ * Multiple tokens are taken to be a message definition followed by
+ * the associated message string. The message string is copied to
+ * the data array being built in msg.c. The index into this array
+ * becomes the `message' identifier created in the msg.h file.
+ */
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/param.h>
+
+#include <sgs.h>
+#include <_string_table.h>
+
+/*
+ * Define any error message strings.
+ */
+static const char
+ * Errmsg_malt = "sgsmsg: file %s: line %d: malformed input "
+ "at line\n",
+ * Errmsg_nmem = "sgsmsg: memory allocation failed: %s\n",
+ * Errmsg_opne = "sgsmsg: file %s: open failed: %s\n",
+ * Errmsg_wrte = "sgsmsg: file %s: write failed: %s\n",
+ * Errmsg_read = "sgsmsg: file %s: read failed %s\n",
+ * Errmsg_stnw = "sgsmsg: st_new(): failed: %s\n",
+ * Errmsg_stin = "sgsmsg: Str_tbl insert failed: %s\n",
+ * Errmsg_mnfn = "sgsmsg: message not found in Str_tbl: %s\n",
+ * Errmsg_use = "usage: sgsmsg [-clv] [-d mesgdata] [-h mesgdefs] "
+ "[-m messages] [-n name] [-i mesgident] file ...\n";
+
+/*
+ * Define all output filenames and associated descriptors.
+ */
+static FILE *fddefs, *fddata, *fdmsgs, *fdmids, *fddesc;
+static char *fldefs, *fldata, *flmsgs, *flmids, *fldesc;
+static FILE *fdlint;
+static char fllint[MAXPATHLEN];
+
+static uint_t vflag; /* verbose flag */
+static Str_tbl *stp; /* string table */
+
+/*
+ * Define any default strings.
+ */
+static const char
+ *nmlint = "/tmp/sgsmsg.lint",
+ *interface = "sgs_msg",
+ *start = "_START_",
+ *end = "_END_";
+
+/*
+ * Define any default flags and data items.
+ */
+static int cflag = 0, lflag = 0, prtmsgs = 0, line, ptr = 1, msgid = 0;
+static char *mesgid = 0, *setid = 0, *domain = 0;
+
+typedef struct msg_string {
+ char *ms_defn;
+ char *ms_message;
+ struct msg_string *ms_next;
+} msg_string;
+
+static msg_string *msg_head;
+static msg_string *msg_tail;
+
+/*
+ * message_append() is responsible for both inserting strings into
+ * the master Str_tbl as well as maintaining a list of the
+ * DEFINITIONS associated with each string.
+ *
+ * The list of strings is traversed at the end once the full
+ * Str_tbl has been constructed - and string offsets can be
+ * assigned.
+ */
+static void
+message_append(const char *defn, const char *message)
+{
+ msg_string *msg;
+ if ((msg = calloc(sizeof (msg_string), 1)) == 0) {
+ (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * Initialize the string table.
+ */
+ if ((stp == 0) && ((stp = st_new(FLG_STNEW_COMPRESS)) == NULL)) {
+ (void) fprintf(stderr, Errmsg_stnw, strerror(errno));
+ exit(1);
+ }
+
+
+ if ((msg->ms_defn = strdup(defn)) == 0) {
+ (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
+ exit(1);
+ }
+ if ((msg->ms_message = strdup(message)) == 0) {
+ (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
+ exit(1);
+ }
+
+ if (st_insert(stp, msg->ms_message) == -1) {
+ (void) fprintf(stderr, Errmsg_stin,
+ message);
+ exit(1);
+ }
+
+ if (msg_head == 0) {
+ msg_head = msg_tail = msg;
+ return;
+ }
+ msg_tail->ms_next = msg;
+ msg_tail = msg;
+}
+
+/*
+ * Initialize a setid value. Given a setid definition determine its numeric
+ * value from the specified message identifier file (specified with the -i
+ * option). Return a pointer to the numeric string.
+ */
+static int
+getmesgid(char *id)
+{
+ char *buffer, *token, *_mesgid = 0, *_setid = 0, *_domain = 0;
+
+ /*
+ * If we're being asked to interpret a message id but the user didn't
+ * provide the required message identifier file (-i option) we're in
+ * trouble.
+ */
+ if (flmids == 0) {
+ (void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: "
+ "unable to process mesgid\n\t"
+ "no message identifier file specified "
+ "(see -i option)\n", fldesc, line, id);
+ return (1);
+ }
+
+ if ((buffer = malloc(LINE_MAX)) == 0) {
+ (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
+ return (1);
+ }
+
+ /*
+ * Read the message identifier file and locate the required mesgid.
+ */
+ rewind(fdmids);
+ while (fgets(buffer, LINE_MAX, fdmids) != NULL) {
+ if ((token = strstr(buffer, id)) == NULL)
+ continue;
+
+ /*
+ * Establish individual strings for the mesgid, setid and domain
+ * values.
+ */
+ _mesgid = token;
+ while (!(isspace(*token)))
+ token++;
+ *token++ = 0;
+
+ while (isspace(*token))
+ token++;
+ _setid = token;
+ while (!(isspace(*token)))
+ token++;
+ *token++ = 0;
+
+ while (isspace(*token))
+ token++;
+ _domain = token;
+ while (!(isspace(*token)))
+ token++;
+ *token = 0;
+ break;
+ }
+
+ /*
+ * Did we find a match?
+ */
+ if ((_mesgid == 0) || (_setid == 0) || (_domain == 0)) {
+ (void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: "
+ "unable to process mesgid\n\t"
+ "identifier does not exist in file %s\n",
+ fldesc, line, id, flmids);
+ return (1);
+ }
+
+ /*
+ * Have we been here before?
+ */
+ if (mesgid) {
+ if (cflag == 1) {
+ /*
+ * If we're being asked to process more than one mesgid
+ * warn the user that only one mesgid can be used for
+ * the catgets(3c) call.
+ */
+ (void) fprintf(stderr, "sgsmsg: file %s: line %d: "
+ "setid %s: warning: multiple mesgids "
+ "encountered\n\t"
+ "last setting used in messaging code\n",
+ fldesc, line, id);
+ }
+ }
+
+ mesgid = _mesgid;
+ setid = _setid;
+ domain = _domain;
+
+ /*
+ * Generate the message file output (insure output flag is enabled).
+ */
+ if (prtmsgs != -1)
+ prtmsgs = 1;
+ if (fdmsgs && (prtmsgs == 1)) {
+ if (cflag == 1) {
+ if (fprintf(fdmsgs, "$quote \"\n$set %s\n",
+ setid) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, flmsgs,
+ strerror(errno));
+ return (1);
+ }
+ } else {
+ if (fprintf(fdmsgs, "domain\t\"%s\"\n", domain) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, flmsgs,
+ strerror(errno));
+ return (1);
+ }
+ }
+ }
+
+ /*
+ * For catgets(3c) output generate a setid definition in the message
+ * definition file.
+ */
+ if (fddefs && (cflag == 1) &&
+ (fprintf(fddefs, "#define\t%s\t%s\n\n", mesgid, setid) < 0)) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Dump contents of String Table to standard out
+ */
+static void
+dump_stringtab(Str_tbl *stp)
+{
+ uint_t cnt;
+
+ if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
+ (void) printf("string table full size: %ld: uncompressed\n",
+ stp->st_fullstrsize);
+ return;
+ }
+
+ (void) printf("string table full size: %ld compressed down to: %ld\n\n",
+ stp->st_fullstrsize, stp->st_strsize);
+ (void) printf("string table compression information [%d buckets]:\n",
+ stp->st_hbckcnt);
+
+ for (cnt = 0; cnt < stp->st_hbckcnt; cnt++) {
+ Str_hash *sthash = stp->st_hashbcks[cnt];
+
+ if (sthash == 0)
+ continue;
+
+ (void) printf(" bucket: [%d]\n", cnt);
+
+ while (sthash) {
+ size_t stroff = sthash->hi_mstr->sm_strlen -
+ sthash->hi_strlen;
+
+ if (stroff == 0) {
+ (void) printf(" [%ld]: '%s' <master>\n",
+ sthash->hi_refcnt, sthash->hi_mstr->sm_str);
+ } else {
+ (void) printf(" [%ld]: '%s' <suffix of: "
+ "'%s'>\n", sthash->hi_refcnt,
+ &sthash->hi_mstr->sm_str[stroff],
+ sthash->hi_mstr->sm_str);
+ }
+ sthash = sthash->hi_next;
+ }
+ }
+}
+
+/*
+ * Initialize the message definition header file stream.
+ */
+static int
+init_defs(void)
+{
+ static char guard[FILENAME_MAX + 6];
+ char *optr;
+ const char *iptr, *_ptr;
+
+ /*
+ * Establish a header guard name using the files basename.
+ */
+ for (iptr = 0, _ptr = fldefs; _ptr && (*_ptr != '\0'); _ptr++) {
+ if (*_ptr == '/')
+ iptr = _ptr + 1;
+ }
+ if (iptr == 0)
+ iptr = fldefs;
+
+ optr = guard;
+ for (*optr++ = '_'; iptr && (*iptr != '\0'); iptr++, optr++) {
+ if (*iptr == '.') {
+ *optr++ = '_';
+ *optr++ = 'D';
+ *optr++ = 'O';
+ *optr++ = 'T';
+ *optr = '_';
+ } else
+ *optr = toupper(*iptr);
+ }
+
+ if (fprintf(fddefs, "#ifndef\t%s\n#define\t%s\n\n", guard, guard) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ if (fprintf(fddefs, "#ifndef\t__lint\n\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ /*
+ * add "typedef int Msg;"
+ */
+ if (fprintf(fddefs, "typedef int\tMsg;\n\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ /*
+ * If the associated data array is global define a prototype.
+ * Define a macro to access the array elements.
+ */
+ if (lflag == 0) {
+ if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n",
+ interface) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs,
+ strerror(errno));
+ return (1);
+ }
+ }
+ if (fprintf(fddefs, "#define\tMSG_ORIG(x)\t&__%s[x]\n\n",
+ interface) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ /*
+ * Generate a prototype to access the associated data array.
+ */
+ if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
+ interface) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+ if (fprintf(fddefs, "#define\tMSG_INTL(x)\t_%s(x)\n\n",
+ interface) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * Finish the message definition header file.
+ */
+static int
+fini_defs(void)
+{
+ if (fprintf(fddefs, "\n#else\t/* __lint */\n\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ /*
+ * When __lint is defined, Msg is a char *. This allows lint to
+ * check our format strings against it's arguments.
+ */
+ if (fprintf(fddefs, "\ntypedef char *\tMsg;\n\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
+ interface) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ if (lflag == 0) {
+ if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n",
+ interface) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs,
+ strerror(errno));
+ return (1);
+ }
+ }
+
+ if (fprintf(fddefs,
+ "#define MSG_ORIG(x)\tx\n#define MSG_INTL(x)\tx\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ /*
+ * Copy the temporary lint defs file into the new header.
+ */
+ if (fdlint) {
+ long size;
+ char *buf;
+
+ size = ftell(fdlint);
+ (void) rewind(fdlint);
+
+ if ((buf = malloc(size)) == 0) {
+ (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
+ return (1);
+ }
+ if (fread(buf, size, 1, fdlint) == 0) {
+ (void) fprintf(stderr, Errmsg_read, fllint,
+ strerror(errno));
+ return (1);
+ }
+ if (fwrite(buf, size, 1, fddefs) == 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs,
+ strerror(errno));
+ return (1);
+ }
+ (void) free(buf);
+ }
+
+ if (fprintf(fddefs, "\n#endif\t/* __lint */\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ if (fprintf(fddefs, "\n#endif\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * The entire messaging file has been scanned - and all strings have been
+ * inserted into the string_table. We can now walk the message queue
+ * and create the '#define <DEFN>' for each string - with the strings
+ * assigned offset into the string_table.
+ */
+static int
+output_defs(void)
+{
+ msg_string *msg;
+ size_t stbufsize;
+ char *stbuf;
+
+ stbufsize = st_getstrtab_sz(stp);
+ if ((stbuf = malloc(stbufsize)) == 0) {
+ (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
+ exit(1);
+ }
+ (void) st_setstrbuf(stp, stbuf, stbufsize);
+ for (msg = msg_head; msg; msg = msg->ms_next) {
+ size_t stoff;
+ if ((st_setstring(stp, msg->ms_message, &stoff)) == -1) {
+ (void) fprintf(stderr, Errmsg_mnfn, msg->ms_message);
+ return (1);
+ }
+ if (fprintf(fddefs, "\n#define\t%s\t%ld\n",
+ msg->ms_defn, stoff) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ fldefs, strerror(errno));
+ return (1);
+ }
+ if (fddefs && fprintf(fddefs, "#define\t%s_SIZE\t%d\n",
+ msg->ms_defn, strlen(msg->ms_message)) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ fldefs, strerror(errno));
+ return (1);
+ }
+ }
+ return (0);
+}
+
+
+/*
+ * Finish off the data structure definition.
+ */
+static int
+output_data(void)
+{
+ size_t stbufsize;
+ size_t ndx;
+ size_t column = 1;
+ const char *stbuf;
+ const char *fmtstr;
+
+ stbufsize = st_getstrtab_sz(stp);
+ stbuf = st_getstrbuf(stp);
+
+ assert(stbuf);
+
+ /*
+ * Determine from the local flag whether the data declaration should
+ * be static.
+ */
+ if (lflag)
+ fmtstr = (const char *)"static const";
+ else
+ fmtstr = (const char *)"const";
+
+ if (fprintf(fddata, "\n%s char __%s[%ld] = { ",
+ fmtstr, interface, stbufsize) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno));
+ return (1);
+ }
+
+ for (ndx = 0; ndx < (stbufsize - 1); ndx++) {
+ if (column == 1) {
+ if (fddata && fprintf(fddata,
+ "\n/* %4ld */ 0x%.2x,", ndx,
+ (unsigned char)stbuf[ndx]) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ fldata, strerror(errno));
+ return (1);
+ }
+ } else {
+ if (fddata && fprintf(fddata, " 0x%.2x,",
+ (unsigned char)stbuf[ndx]) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ fldata, strerror(errno));
+ return (1);
+ }
+ }
+
+ if (column++ == 10)
+ column = 1;
+ }
+
+ if (column == 1)
+ fmtstr = "\n\t0x%.2x };\n";
+ else
+ fmtstr = " 0x%.2x };\n";
+
+ if (fprintf(fddata, fmtstr, (unsigned char)stbuf[stbufsize - 1]) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno));
+ return (1);
+ }
+
+ return (0);
+}
+
+static int
+file()
+{
+ char buffer[LINE_MAX], * token;
+ uint_t bufsize;
+ char *token_buffer;
+ int escape = 0;
+
+ if ((token_buffer = malloc(LINE_MAX)) == 0) {
+ (void) fprintf(stderr, Errmsg_nmem, strerror(errno));
+ return (1);
+ }
+ bufsize = LINE_MAX;
+
+ line = 1;
+
+ while ((token = fgets(buffer, LINE_MAX, fddesc)) != NULL) {
+ char defn[PATH_MAX], * _defn, * str;
+ int len;
+
+ switch (*token) {
+ case '#':
+ case '$':
+ if (escape) {
+ (void) fprintf(stderr, Errmsg_malt, fldesc,
+ line);
+ return (1);
+ }
+
+ /*
+ * If a msgid has been output a msgstr must follow
+ * before we digest the new token. A msgid is only set
+ * if fdmsgs is in use.
+ */
+ if (msgid) {
+ msgid = 0;
+ if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ flmsgs, strerror(errno));
+ return (1);
+ }
+ }
+
+ /*
+ * Pass lines directly through to the output message
+ * file.
+ */
+ if (fdmsgs && (prtmsgs == 1)) {
+ char comment;
+
+ if (cflag == 0)
+ comment = '#';
+ else
+ comment = '$';
+
+ if (fprintf(fdmsgs, "%c%s", comment,
+ ++token) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ flmsgs, strerror(errno));
+ return (1);
+ }
+ }
+ break;
+
+ case '@':
+ if (escape) {
+ (void) fprintf(stderr, Errmsg_malt, fldesc,
+ line);
+ return (1);
+ }
+
+ /*
+ * If a msgid has been output a msgstr must follow
+ * before we digest the new token.
+ */
+ if (msgid) {
+ msgid = 0;
+ if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ flmsgs, strerror(errno));
+ return (1);
+ }
+ }
+
+ /*
+ * Determine whether we have one or more tokens.
+ */
+ token++;
+ while (isspace(*token)) /* rid any whitespace */
+ token++;
+ _defn = token; /* definition start */
+ while (!(isspace(*token)))
+ token++;
+ *token++ = 0;
+
+ while (isspace(*token)) /* rid any whitespace */
+ token++;
+
+ /*
+ * Determine whether the single token is one of the
+ * reserved message output delimiters otherwise
+ * translate it as a message identifier.
+ */
+ if (*token == 0) {
+ if (strcmp(_defn, start) == 0)
+ prtmsgs = 1;
+ else if (strcmp(_defn, end) == 0)
+ prtmsgs = -1;
+ else if (getmesgid(_defn) == 1)
+ return (1);
+ break;
+ }
+
+ /*
+ * Multiple tokens are translated by taking the first
+ * token as the message definition, and the rest of the
+ * line as the message itself. A message line ending
+ * with an escape ('\') is expected to be continued on
+ * the next line.
+ */
+ if (prtmsgs != -1)
+ prtmsgs = 1;
+ if (fdmsgs && (prtmsgs == 1)) {
+ /*
+ * For catgets(3c) make sure a message
+ * identifier has been established (this is
+ * normally a domain for gettext(3i), but for
+ * sgsmsg use this could be argued as being
+ * redundent). Also make sure that the message
+ * definitions haven't exceeeded the maximum
+ * value allowed by gencat(1) before generating
+ * any message file entries.
+ */
+ if (cflag == 1) {
+ if (setid == 0) {
+ (void) fprintf(stderr, "file "
+ "%s: no message identifier "
+ "has been established\n",
+ fldesc);
+ return (1);
+ }
+ if (ptr > NL_MSGMAX) {
+ (void) fprintf(stderr, "file "
+ "%s: message definition "
+ "(%d) exceeds allowable "
+ "limit (NL_MSGMAX)\n",
+ fldesc, ptr);
+ return (1);
+ }
+ }
+
+ /*
+ * For catgets(3c) write the definition and the
+ * message string to the message file. For
+ * gettext(3i) write the message string as a
+ * mesgid - indicate a mesgid has been output
+ * so that a msgstr can follow.
+ */
+ if (cflag == 1) {
+ if (fprintf(fdmsgs, "%d\t%s", ptr,
+ token) < 0) {
+ (void) fprintf(stderr,
+ Errmsg_wrte, flmsgs,
+ strerror(errno));
+ return (1);
+ }
+ } else {
+ if (fprintf(fdmsgs, "msgid\t\"") < 0) {
+ (void) fprintf(stderr,
+ Errmsg_wrte, flmsgs,
+ strerror(errno));
+ return (1);
+ }
+ msgid = 1;
+ }
+ }
+
+ /*
+ * The message itself is a quoted string as this makes
+ * embedding spaces at the start (or the end) of the
+ * string very easy.
+ */
+ if (*token != '"') {
+ (void) fprintf(stderr, Errmsg_malt, fldesc,
+ line);
+ return (1);
+ }
+
+ (void) strcpy(defn, _defn);
+
+ /*
+ * Write the tag to the lint definitions.
+ */
+ if (fdlint) {
+ if (fprintf(fdlint, "\n#define\t%s\t",
+ _defn) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ fllint, strerror(errno));
+ return (1);
+ }
+ }
+
+ len = 0;
+
+ /*
+ * Write each character of the message string to the
+ * data array. Translate any escaped characters - use
+ * the same specially recognized characters as defined
+ * by gencat(1).
+ */
+message:
+ if (*token == '"') {
+ if (fdlint &&
+ (fprintf(fdlint, "%c", *token) < 0)) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ fllint, strerror(errno));
+ return (1);
+ }
+ token++;
+ }
+ while (*token) {
+ char _token;
+
+ if ((*token == '\\') && (escape == 0)) {
+ escape = 1;
+ if (fdlint && (*(token + 1) != '\n') &&
+ fprintf(fdlint, "%c", *token) < 0) {
+ (void) fprintf(stderr,
+ Errmsg_wrte, fllint,
+ strerror(errno));
+ return (1);
+ }
+ token++;
+ continue;
+ }
+ if (escape) {
+ if (*token == 'n')
+ _token = '\n';
+ else if (*token == 't')
+ _token = '\t';
+ else if (*token == 'v')
+ _token = '\v';
+ else if (*token == 'b')
+ _token = '\b';
+ else if (*token == 'f')
+ _token = '\f';
+ else if (*token == '\\')
+ _token = '\\';
+ else if (*token == '"')
+ _token = '"';
+ else if (*token == '\n')
+ break;
+ else
+ _token = *token;
+
+ if (fdmsgs && (prtmsgs == 1) &&
+ (fprintf(fdmsgs, "\\") < 0)) {
+ (void) fprintf(stderr,
+ Errmsg_wrte, flmsgs,
+ strerror(errno));
+ return (1);
+ }
+ } else {
+ /*
+ * If this is the trailing quote then
+ * thats the last of the message string.
+ * Eat up any remaining white space and
+ * unless an escape character is found
+ * terminate the data string with a 0.
+ */
+ /* BEGIN CSTYLED */
+ if (*token == '"') {
+ if (fdlint && (fprintf(fdlint,
+ "%c", *token) < 0)) {
+ (void) fprintf(stderr,
+ Errmsg_wrte, fllint,
+ strerror(errno));
+ return (1);
+ }
+
+ if (fdmsgs && (prtmsgs == 1) &&
+ (fprintf(fdmsgs, "%c",
+ *token) < 0)) {
+ (void) fprintf(stderr,
+ Errmsg_wrte, flmsgs,
+ strerror(errno));
+ return (1);
+ }
+
+ while (*++token) {
+ if (*token == '\n')
+ break;
+ }
+ _token = '\0';
+ } else
+ _token = *token;
+ /* END CSTYLED */
+ }
+
+ if (fdmsgs && (prtmsgs == 1) &&
+ (fprintf(fdmsgs, "%c", *token) < 0)) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ flmsgs, strerror(errno));
+ return (1);
+ }
+
+ if (fdlint && fprintf(fdlint,
+ "%c", *token) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ fllint, strerror(errno));
+ return (1);
+ }
+
+ if (len >= bufsize) {
+ bufsize += LINE_MAX;
+ if ((token_buffer = realloc(
+ token_buffer, bufsize)) == 0) {
+ (void) fprintf(stderr,
+ Errmsg_nmem,
+ strerror(errno));
+ return (1);
+ }
+ }
+ token_buffer[len] = _token;
+ ptr++, token++, len++;
+ escape = 0;
+
+ if (_token == '\0')
+ break;
+ }
+
+ /*
+ * After the complete message string has been processed
+ * (including its continuation beyond one line), create
+ * a string size definition.
+ */
+ if (escape == 0) {
+ const char *form = "#define\t%s_SIZE\t%d\n";
+
+ token_buffer[len] = '\0';
+
+ message_append(defn, token_buffer);
+
+ if (fdlint && fprintf(fdlint, form, defn,
+ (len - 1)) < 0) {
+ (void) fprintf(stderr, Errmsg_wrte,
+ fllint, strerror(errno));
+ return (1);
+ }
+ }
+ break;
+
+ default:
+ /*
+ * Empty lines are passed through to the message file.
+ */
+ while (isspace(*token))
+ token++;
+
+ if (*token == 0) {
+ if (msgid || (fdmsgs && (prtmsgs == 1))) {
+ /*
+ * If a msgid has been output a msgstr
+ * must follow before we digest the new
+ * token.
+ */
+ if (msgid) {
+ msgid = 0;
+ str = "msgstr\t\"\"\n\n";
+ } else
+ str = "\n";
+
+ if (fprintf(fdmsgs, str) < 0) {
+ (void) fprintf(stderr,
+ Errmsg_wrte, flmsgs,
+ strerror(errno));
+ return (1);
+ }
+ }
+ break;
+ }
+
+ /*
+ * If an escape is in effect then any tokens are taken
+ * to be message continuations.
+ */
+ if (escape) {
+ escape = 0;
+ goto message;
+ }
+
+ (void) fprintf(stderr, "file %s: line %d: invalid "
+ "input does not start with #, $ or @\n", fldesc,
+ line);
+ return (1);
+ }
+ line++;
+ }
+
+ free(token_buffer);
+
+ return (0);
+}
+
+int
+main(int argc, char ** argv)
+{
+ opterr = 0;
+ while ((line = getopt(argc, argv, "cd:h:lm:n:i:v")) != EOF) {
+ switch (line) {
+ case 'c': /* catgets instead of gettext */
+ cflag = 1;
+ break;
+ case 'd': /* new message data filename */
+ fldata = optarg; /* (msg.c is default) */
+ break;
+ case 'h': /* new message defs filename */
+ fldefs = optarg; /* (msg.h is default) */
+ break;
+ case 'i': /* input message ids from */
+ flmids = optarg; /* from this file */
+ break;
+ case 'l': /* define message data arrays */
+ lflag = 1; /* to be local (static) */
+ break;
+ case 'm': /* generate message database */
+ flmsgs = optarg; /* to this file */
+ break;
+ case 'n': /* new data array and func */
+ interface = optarg; /* name (msg is default) */
+ break;
+ case 'v':
+ vflag = 1; /* set verbose flag */
+ break;
+ case '?':
+ (void) fprintf(stderr, Errmsg_use, argv[0]);
+ exit(1);
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Validate the we have been given at least one input file.
+ */
+ if ((argc - optind) < 1) {
+ (void) fprintf(stderr, Errmsg_use);
+ exit(1);
+ }
+
+ /*
+ * Open all the required output files.
+ */
+ if (fldefs) {
+ if ((fddefs = fopen(fldefs, "w+")) == NULL) {
+ (void) fprintf(stderr, Errmsg_opne, fldefs,
+ strerror(errno));
+ return (1);
+ }
+ }
+ if (fldata) {
+ if (fldefs && (strcmp(fldefs, fldata) == 0))
+ fddata = fddefs;
+ else if ((fddata = fopen(fldata, "w+")) == NULL) {
+ (void) fprintf(stderr, Errmsg_opne, fldata,
+ strerror(errno));
+ return (1);
+ }
+ }
+ if (fddefs && fddata) {
+ (void) sprintf(fllint, "%s.%d", nmlint, (int)getpid());
+ if ((fdlint = fopen(fllint, "w+")) == NULL) {
+ (void) fprintf(stderr, Errmsg_opne, fllint,
+ strerror(errno));
+ return (1);
+ }
+ }
+ if (flmsgs) {
+ if ((fdmsgs = fopen(flmsgs, "w+")) == NULL) {
+ (void) fprintf(stderr, Errmsg_opne, flmsgs,
+ strerror(errno));
+ return (1);
+ }
+ }
+ if (flmids) {
+ if ((fdmids = fopen(flmids, "r")) == NULL) {
+ (void) fprintf(stderr, Errmsg_opne, flmids,
+ strerror(errno));
+ return (1);
+ }
+ }
+
+
+ /*
+ * Initialize the message definition and message data streams.
+ */
+ if (fddefs) {
+ if (init_defs())
+ return (1);
+ }
+
+ /*
+ * Read the input message file, and for each line process accordingly.
+ */
+ for (; optind < argc; optind++) {
+ int err;
+
+ fldesc = argv[optind];
+
+ if ((fddesc = fopen(fldesc, "r")) == NULL) {
+ (void) fprintf(stderr, Errmsg_opne, fldesc,
+ strerror(errno));
+ return (1);
+ }
+ err = file();
+ (void) fclose(fddesc);
+
+ if (err != 0)
+ return (1);
+ }
+
+ /*
+ * If a msgid has been output a msgstr must follow before we end the
+ * file.
+ */
+ if (msgid) {
+ msgid = 0;
+ if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
+ (void) fprintf(stderr, Errmsg_wrte, flmsgs,
+ strerror(errno));
+ return (1);
+ }
+ }
+
+ if (fdmids)
+ (void) fclose(fdmids);
+ if (fdmsgs)
+ (void) fclose(fdmsgs);
+
+ if (fddefs) {
+ if (output_defs())
+ return (1);
+ }
+
+ /*
+ * Finish off any generated data and header file.
+ */
+ if (fldata) {
+ if (output_data())
+ return (1);
+ }
+ if (fddefs) {
+ if (fini_defs())
+ return (1);
+ }
+
+ if (vflag)
+ dump_stringtab(stp);
+
+ /*
+ * Close up everything and go home.
+ */
+ if (fddata)
+ (void) fclose(fddata);
+ if (fddefs && (fddefs != fddata))
+ (void) fclose(fddefs);
+ if (fddefs && fddata) {
+ (void) fclose(fdlint);
+ (void) unlink(fllint);
+ }
+
+ if (stp)
+ st_destroy(stp);
+
+ return (0);
+}
diff --git a/cmd/sgs/tools/common/string_table.c b/cmd/sgs/tools/common/string_table.c
new file mode 100644
index 000000000000..e174acaf0419
--- /dev/null
+++ b/cmd/sgs/tools/common/string_table.c
@@ -0,0 +1,685 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <_string_table.h>
+#include <strings.h>
+#include <sgs.h>
+#include <stdio.h>
+
+/*
+ * This file provides the interfaces to build a Str_tbl suitable for use by
+ * either the sgsmsg message system, or a standard ELF string table (SHT_STRTAB)
+ * as created by ld(1).
+ *
+ * There are two modes which can be used when constructing a string table:
+ *
+ * st_new(0)
+ * standard string table - no compression. This is the
+ * traditional, fast method.
+ *
+ * st_new(FLG_STTAB_COMPRESS)
+ * builds a compressed string table which both eliminates
+ * duplicate strings, and permits strings with common suffixes
+ * (atexit vs. exit) to overlap in the table. This provides space
+ * savings for many string tables. Although more work than the
+ * traditional method, the algorithms used are designed to scale
+ * and keep any overhead at a minimum.
+ *
+ * These string tables are built with a common interface in a two-pass manner.
+ * The first pass finds all of the strings required for the string-table and
+ * calculates the size required for the final string table.
+ *
+ * The second pass allocates the string table, populates the strings into the
+ * table and returns the offsets the strings have been assigned.
+ *
+ * The calling sequence to build and populate a string table is:
+ *
+ * st_new(); // initialize strtab
+ *
+ * st_insert(st1); // first pass of strings ...
+ * // calculates size required for
+ * // string table
+ *
+ * st_delstring(st?); // remove string previously
+ * // inserted
+ * st_insert(stN);
+ *
+ * st_getstrtab_sz(); // freezes strtab and computes
+ * // size of table.
+ *
+ * st_setstrbuf(); // associates a final destination
+ * // for the string table
+ *
+ * st_setstring(st1); // populate the string table
+ * ... // offsets are based off of second
+ * // pass through the string table
+ * st_setstring(stN);
+ *
+ * st_destroy(); // tear down string table
+ * // structures.
+ *
+ * String Suffix Compression Algorithm:
+ *
+ * Here's a quick high level overview of the Suffix String
+ * compression algorithm used. First - the heart of the algorithm
+ * is a Hash table list which represents a dictionary of all unique
+ * strings inserted into the string table. The hash function for
+ * this table is a standard string hash except that the hash starts
+ * at the last character in the string (&str[n - 1]) and works towards
+ * the first character in the function (&str[0]). As we compute the
+ * HASH value for a given string, we also compute the hash values
+ * for all of the possible suffix strings for that string.
+ *
+ * As we compute the hash - at each character see if the current
+ * suffix string for that hash is already present in the table. If
+ * it is, and the string is a master string. Then change that
+ * string to a suffix string of the new string being inserted.
+ *
+ * When the final hash value is found (hash for str[0...n]), check
+ * to see if it is in the hash table - if so increment the reference
+ * count for the string. If it is not yet in the table, insert a
+ * new hash table entry for a master string.
+ *
+ * The above method will find all suffixes of a given string given
+ * that the strings are inserted from shortest to longest. That is
+ * why this is a two phase method, we first collect all of the
+ * strings and store them based off of their length in an AVL tree.
+ * Once all of the strings have been submitted we then start the
+ * hash table build by traversing the AVL tree in order and
+ * inserting the strings from shortest to longest as described
+ * above.
+ */
+
+/* LINTLIBRARY */
+
+static int
+avl_len_compare(const void *n1, const void *n2)
+{
+ size_t len1, len2;
+
+ len1 = ((LenNode *)n1)->ln_strlen;
+ len2 = ((LenNode *)n2)->ln_strlen;
+
+ if (len1 == len2)
+ return (0);
+ if (len2 < len1)
+ return (1);
+ return (-1);
+}
+
+static int
+avl_str_compare(const void *n1, const void *n2)
+{
+ const char *str1, *str2;
+ int rc;
+
+ str1 = ((StrNode *)n1)->sn_str;
+ str2 = ((StrNode *)n2)->sn_str;
+
+ rc = strcmp(str1, str2);
+ if (rc > 0)
+ return (1);
+ if (rc < 0)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Return an initialized Str_tbl - returns NULL on failure.
+ *
+ * flags:
+ * FLG_STTAB_COMPRESS - build a compressed string table
+ */
+Str_tbl *
+st_new(uint_t flags)
+{
+ Str_tbl *stp;
+
+ if ((stp = calloc(sizeof (Str_tbl), 1)) == NULL)
+ return (NULL);
+
+ /*
+ * Start with a leading '\0' - it's tradition.
+ */
+ stp->st_strsize = stp->st_fullstrsize = stp->st_nextoff = 1;
+
+ /*
+ * Do we compress this string table?
+ */
+ stp->st_flags = flags;
+ if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
+ return (stp);
+
+ if ((stp->st_lentree = calloc(sizeof (avl_tree_t), 1)) == NULL)
+ return (NULL);
+
+ avl_create(stp->st_lentree, &avl_len_compare, sizeof (LenNode),
+ SGSOFFSETOF(LenNode, ln_avlnode));
+
+ return (stp);
+}
+
+/*
+ * Insert a new string into the Str_tbl. There are two AVL trees used.
+ *
+ * . The first LenNode AVL tree maintains a tree of nodes based on string
+ * sizes.
+ * . Each LenNode maintains a StrNode AVL tree for each string. Large
+ * applications have been known to contribute thousands of strings of
+ * the same size. Should strings need to be removed (-z ignore), then
+ * the string AVL tree makes this removal efficient and scalable.
+ */
+int
+st_insert(Str_tbl *stp, const char *str)
+{
+ size_t len;
+ StrNode *snp, sn = { 0 };
+ LenNode *lnp, ln = { 0 };
+ avl_index_t where;
+
+ /*
+ * String table can't have been cooked
+ */
+ assert((stp->st_flags & FLG_STTAB_COOKED) == 0);
+
+ /*
+ * Null strings always point to the head of the string
+ * table - no reason to keep searching.
+ */
+ if ((len = strlen(str)) == 0)
+ return (0);
+
+ stp->st_fullstrsize += len + 1;
+ stp->st_strcnt++;
+
+ if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
+ return (0);
+
+ /*
+ * From the controlling string table, determine which LenNode AVL node
+ * provides for this string length. If the node doesn't exist, insert
+ * a new node to represent this string length.
+ */
+ ln.ln_strlen = len;
+ if ((lnp = avl_find(stp->st_lentree, &ln, &where)) == NULL) {
+ if ((lnp = calloc(sizeof (LenNode), 1)) == NULL)
+ return (-1);
+ lnp->ln_strlen = len;
+ avl_insert(stp->st_lentree, lnp, where);
+
+ if ((lnp->ln_strtree = calloc(sizeof (avl_tree_t), 1)) == NULL)
+ return (0);
+
+ avl_create(lnp->ln_strtree, &avl_str_compare, sizeof (StrNode),
+ SGSOFFSETOF(StrNode, sn_avlnode));
+ }
+
+ /*
+ * From the string length AVL node determine whether a StrNode AVL node
+ * provides this string. If the node doesn't exist, insert a new node
+ * to represent this string.
+ */
+ sn.sn_str = str;
+ if ((snp = avl_find(lnp->ln_strtree, &sn, &where)) == NULL) {
+ if ((snp = calloc(sizeof (StrNode), 1)) == NULL)
+ return (-1);
+ snp->sn_str = str;
+ avl_insert(lnp->ln_strtree, snp, where);
+ }
+ snp->sn_refcnt++;
+
+ return (0);
+}
+
+/*
+ * Remove a previously inserted string from the Str_tbl.
+ */
+int
+st_delstring(Str_tbl *stp, const char *str)
+{
+ size_t len;
+ LenNode *lnp, ln = { 0 };
+ StrNode *snp, sn = { 0 };
+
+ /*
+ * String table can't have been cooked
+ */
+ assert((stp->st_flags & FLG_STTAB_COOKED) == 0);
+
+ len = strlen(str);
+ stp->st_fullstrsize -= len + 1;
+
+ if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
+ return (0);
+
+ /*
+ * Determine which LenNode AVL node provides for this string length.
+ */
+ ln.ln_strlen = len;
+ if ((lnp = avl_find(stp->st_lentree, &ln, 0)) != NULL) {
+ sn.sn_str = str;
+ if ((snp = avl_find(lnp->ln_strtree, &sn, 0)) != NULL) {
+ /*
+ * Reduce the reference count, and if zero remove the
+ * node.
+ */
+ if (--snp->sn_refcnt == 0)
+ avl_remove(lnp->ln_strtree, snp);
+ return (0);
+ }
+ }
+
+ /*
+ * No strings of this length, or no string itself - someone goofed.
+ */
+ return (-1);
+}
+
+/*
+ * Tear down a String_Table structure.
+ */
+void
+st_destroy(Str_tbl *stp)
+{
+ Str_hash *sthash, *psthash;
+ Str_master *mstr, *pmstr;
+ uint_t i;
+
+ /*
+ * cleanup the master strings
+ */
+ for (mstr = stp->st_mstrlist, pmstr = 0; mstr;
+ mstr = mstr->sm_next) {
+ if (pmstr)
+ free(pmstr);
+ pmstr = mstr;
+ }
+ if (pmstr)
+ free(pmstr);
+
+ if (stp->st_hashbcks) {
+ for (i = 0; i < stp->st_hbckcnt; i++) {
+ for (sthash = stp->st_hashbcks[i], psthash = 0;
+ sthash; sthash = sthash->hi_next) {
+ if (psthash)
+ free(psthash);
+ psthash = sthash;
+ }
+ if (psthash)
+ free(psthash);
+ }
+ free(stp->st_hashbcks);
+ }
+ free(stp);
+}
+
+
+/*
+ * For a given string - copy it into the buffer associated with
+ * the string table - and return the offset it has been assigned.
+ *
+ * If a value of '-1' is returned - the string was not found in
+ * the Str_tbl.
+ */
+int
+st_setstring(Str_tbl *stp, const char *str, size_t *stoff)
+{
+ size_t stlen;
+ uint_t hashval;
+ Str_hash *sthash;
+ Str_master *mstr;
+ int i;
+
+ /*
+ * String table *must* have been previously cooked
+ */
+ assert(stp->st_strbuf);
+
+ assert(stp->st_flags & FLG_STTAB_COOKED);
+ stlen = strlen(str);
+ /*
+ * Null string always points to head of string table
+ */
+ if (stlen == 0) {
+ *stoff = 0;
+ return (0);
+ }
+
+ if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
+ size_t _stoff;
+
+ stlen++; /* count for trailing '\0' */
+ _stoff = stp->st_nextoff;
+ /*
+ * Have we overflowed our assigned buffer?
+ */
+ if ((_stoff + stlen) > stp->st_fullstrsize)
+ return (-1);
+ memcpy(stp->st_strbuf + _stoff, str, stlen);
+ *stoff = _stoff;
+ stp->st_nextoff += stlen;
+ return (0);
+ }
+
+ /*
+ * Calculate reverse hash for string.
+ */
+ hashval = HASHSEED;
+ for (i = stlen; i >= 0; i--) {
+ hashval = ((hashval << 5) + hashval) +
+ str[i]; /* h = ((h * 33) + c) */
+ }
+
+ for (sthash = stp->st_hashbcks[hashval % stp->st_hbckcnt]; sthash;
+ sthash = sthash->hi_next) {
+ const char *hstr;
+
+ if (sthash->hi_hashval != hashval)
+ continue;
+
+ hstr = &sthash->hi_mstr->sm_str[sthash->hi_mstr->sm_strlen -
+ sthash->hi_strlen];
+ if (strcmp(str, hstr) == 0)
+ break;
+ }
+
+ /*
+ * Did we find the string?
+ */
+ if (sthash == 0)
+ return (-1);
+
+ /*
+ * Has this string been copied into the string table?
+ */
+ mstr = sthash->hi_mstr;
+ if (mstr->sm_stroff == 0) {
+ size_t mstrlen = mstr->sm_strlen + 1;
+
+ mstr->sm_stroff = stp->st_nextoff;
+
+ /*
+ * Have we overflowed our assigned buffer?
+ */
+ if ((mstr->sm_stroff + mstrlen) > stp->st_fullstrsize)
+ return (-1);
+
+ (void) memcpy(stp->st_strbuf + mstr->sm_stroff,
+ mstr->sm_str, mstrlen);
+ stp->st_nextoff += mstrlen;
+ }
+
+ /*
+ * Calculate offset of (sub)string.
+ */
+ *stoff = mstr->sm_stroff + mstr->sm_strlen - sthash->hi_strlen;
+
+ return (0);
+}
+
+
+static int
+st_hash_insert(Str_tbl *stp, const char *str, size_t len)
+{
+ int i;
+ uint_t hashval = HASHSEED;
+ uint_t bckcnt = stp->st_hbckcnt;
+ Str_hash **hashbcks = stp->st_hashbcks;
+ Str_hash *sthash;
+ Str_master *mstr = 0;
+
+ /*
+ * We use a classic 'Bernstein k=33' hash function. But
+ * instead of hashing from the start of the string to the
+ * end, we do it in reverse.
+ *
+ * This way - we are essentially building all of the
+ * suffix hashvalues as we go. We can check to see if
+ * any suffixes already exist in the tree as we generate
+ * the hash.
+ */
+ for (i = len; i >= 0; i--) {
+ hashval = ((hashval << 5) + hashval) +
+ str[i]; /* h = ((h * 33) + c) */
+
+ for (sthash = hashbcks[hashval % bckcnt];
+ sthash; sthash = sthash->hi_next) {
+ const char *hstr;
+ Str_master *_mstr;
+
+ if (sthash->hi_hashval != hashval)
+ continue;
+
+ _mstr = sthash->hi_mstr;
+ hstr = &_mstr->sm_str[_mstr->sm_strlen -
+ sthash->hi_strlen];
+
+ if (strcmp(&str[i], hstr))
+ continue;
+
+ if (i == 0) {
+ /*
+ * Entry already in table, increment refcnt and
+ * get out.
+ */
+ sthash->hi_refcnt++;
+ return (0);
+ } else {
+ /*
+ * If this 'suffix' is presently a 'master
+ * string, then take over it's record.
+ */
+ if (sthash->hi_strlen == _mstr->sm_strlen) {
+ /*
+ * we should only do this once.
+ */
+ assert(mstr == 0);
+ mstr = _mstr;
+ }
+ }
+ }
+ }
+
+ /*
+ * Do we need a new master string, or can we take over
+ * one we already found in the table?
+ */
+ if (mstr == 0) {
+ /*
+ * allocate a new master string
+ */
+ if ((mstr = calloc(sizeof (Str_hash), 1)) == 0)
+ return (-1);
+ mstr->sm_next = stp->st_mstrlist;
+ stp->st_mstrlist = mstr;
+ stp->st_strsize += len + 1;
+ } else {
+ /*
+ * We are taking over a existing master string, the string size
+ * only increments by the difference between the current string
+ * and the previous master.
+ */
+ assert(len > mstr->sm_strlen);
+ stp->st_strsize += len - mstr->sm_strlen;
+ }
+
+ if ((sthash = calloc(sizeof (Str_hash), 1)) == 0)
+ return (-1);
+
+ mstr->sm_hashval = sthash->hi_hashval = hashval;
+ mstr->sm_strlen = sthash->hi_strlen = len;
+ mstr->sm_str = str;
+ sthash->hi_refcnt = 1;
+ sthash->hi_mstr = mstr;
+
+ /*
+ * Insert string element into head of hash list
+ */
+ hashval = hashval % bckcnt;
+ sthash->hi_next = hashbcks[hashval];
+ hashbcks[hashval] = sthash;
+ return (0);
+}
+
+/*
+ * Return amount of space required for the string table.
+ */
+size_t
+st_getstrtab_sz(Str_tbl *stp)
+{
+ assert(stp->st_fullstrsize > 0);
+
+ if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
+ stp->st_flags |= FLG_STTAB_COOKED;
+ return (stp->st_fullstrsize);
+ }
+
+ if ((stp->st_flags & FLG_STTAB_COOKED) == 0) {
+ LenNode *lnp;
+ void *cookie;
+
+ stp->st_flags |= FLG_STTAB_COOKED;
+ /*
+ * allocate a hash table about the size of # of
+ * strings input.
+ */
+ stp->st_hbckcnt = findprime(stp->st_strcnt);
+ if ((stp->st_hashbcks =
+ calloc(sizeof (Str_hash), stp->st_hbckcnt)) == NULL)
+ return (0);
+
+ /*
+ * We now walk all of the strings in the list, from shortest to
+ * longest, and insert them into the hashtable.
+ */
+ if ((lnp = avl_first(stp->st_lentree)) == NULL) {
+ /*
+ * Is it possible we have an empty string table, if so,
+ * the table still contains '\0', so return the size.
+ */
+ if (avl_numnodes(stp->st_lentree) == 0) {
+ assert(stp->st_strsize == 1);
+ return (stp->st_strsize);
+ }
+ return (0);
+ }
+
+ while (lnp) {
+ StrNode *snp;
+
+ /*
+ * Walk the string lists and insert them into the hash
+ * list. Once a string is inserted we no longer need
+ * it's entry, so the string can be freed.
+ */
+ for (snp = avl_first(lnp->ln_strtree); snp;
+ snp = AVL_NEXT(lnp->ln_strtree, snp)) {
+ if (st_hash_insert(stp, snp->sn_str,
+ lnp->ln_strlen) == -1)
+ return (0);
+ }
+
+ /*
+ * Now that the strings have been copied, walk the
+ * StrNode tree and free all the AVL nodes. Note,
+ * avl_destroy_nodes() beats avl_remove() as the
+ * latter balances the nodes as they are removed.
+ * We just want to tear the whole thing down fast.
+ */
+ cookie = NULL;
+ while ((snp = avl_destroy_nodes(lnp->ln_strtree,
+ &cookie)) != NULL)
+ free(snp);
+ avl_destroy(lnp->ln_strtree);
+ free(lnp->ln_strtree);
+ lnp->ln_strtree = NULL;
+
+ /*
+ * Move on to the next LenNode.
+ */
+ lnp = AVL_NEXT(stp->st_lentree, lnp);
+ }
+
+ /*
+ * Now that all of the strings have been freed, walk the
+ * LenNode tree and free all of the AVL nodes. Note,
+ * avl_destroy_nodes() beats avl_remove() as the latter
+ * balances the nodes as they are removed. We just want to
+ * tear the whole thing down fast.
+ */
+ cookie = NULL;
+ while ((lnp = avl_destroy_nodes(stp->st_lentree,
+ &cookie)) != NULL)
+ free(lnp);
+ avl_destroy(stp->st_lentree);
+ free(stp->st_lentree);
+ stp->st_lentree = 0;
+ }
+
+ assert(stp->st_strsize > 0);
+ assert(stp->st_fullstrsize >= stp->st_strsize);
+
+ return (stp->st_strsize);
+}
+
+/*
+ * Associate a buffer with a string table.
+ */
+const char *
+st_getstrbuf(Str_tbl *stp)
+{
+ return (stp->st_strbuf);
+}
+
+int
+st_setstrbuf(Str_tbl *stp, char *stbuf, size_t bufsize)
+{
+ assert(stp->st_flags & FLG_STTAB_COOKED);
+
+ if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
+ if (bufsize < stp->st_fullstrsize)
+ return (-1);
+ } else {
+ if (bufsize < stp->st_strsize)
+ return (-1);
+ }
+
+ stp->st_strbuf = stbuf;
+#ifdef DEBUG
+ /*
+ * for debug builds - start with a stringtable filled in
+ * with '0xff'. This makes it very easy to find wholes
+ * which we failed to fill in - in the strtab.
+ */
+ memset(stbuf, 0xff, bufsize);
+ stbuf[0] = '\0';
+#else
+ memset(stbuf, 0x0, bufsize);
+#endif
+ return (0);
+}
diff --git a/common/avl/avl.c b/common/avl/avl.c
new file mode 100644
index 000000000000..7403e813014c
--- /dev/null
+++ b/common/avl/avl.c
@@ -0,0 +1,969 @@
+/*
+ * 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"
+
+
+/*
+ * AVL - generic AVL tree implementation for kernel use
+ *
+ * A complete description of AVL trees can be found in many CS textbooks.
+ *
+ * Here is a very brief overview. An AVL tree is a binary search tree that is
+ * almost perfectly balanced. By "almost" perfectly balanced, we mean that at
+ * any given node, the left and right subtrees are allowed to differ in height
+ * by at most 1 level.
+ *
+ * This relaxation from a perfectly balanced binary tree allows doing
+ * insertion and deletion relatively efficiently. Searching the tree is
+ * still a fast operation, roughly O(log(N)).
+ *
+ * The key to insertion and deletion is a set of tree maniuplations called
+ * rotations, which bring unbalanced subtrees back into the semi-balanced state.
+ *
+ * This implementation of AVL trees has the following peculiarities:
+ *
+ * - The AVL specific data structures are physically embedded as fields
+ * in the "using" data structures. To maintain generality the code
+ * must constantly translate between "avl_node_t *" and containing
+ * data structure "void *"s by adding/subracting the avl_offset.
+ *
+ * - Since the AVL data is always embedded in other structures, there is
+ * no locking or memory allocation in the AVL routines. This must be
+ * provided for by the enclosing data structure's semantics. Typically,
+ * avl_insert()/_add()/_remove()/avl_insert_here() require some kind of
+ * exclusive write lock. Other operations require a read lock.
+ *
+ * - The implementation uses iteration instead of explicit recursion,
+ * since it is intended to run on limited size kernel stacks. Since
+ * there is no recursion stack present to move "up" in the tree,
+ * there is an explicit "parent" link in the avl_node_t.
+ *
+ * - The left/right children pointers of a node are in an array.
+ * In the code, variables (instead of constants) are used to represent
+ * left and right indices. The implementation is written as if it only
+ * dealt with left handed manipulations. By changing the value assigned
+ * to "left", the code also works for right handed trees. The
+ * following variables/terms are frequently used:
+ *
+ * int left; // 0 when dealing with left children,
+ * // 1 for dealing with right children
+ *
+ * int left_heavy; // -1 when left subtree is taller at some node,
+ * // +1 when right subtree is taller
+ *
+ * int right; // will be the opposite of left (0 or 1)
+ * int right_heavy;// will be the opposite of left_heavy (-1 or 1)
+ *
+ * int direction; // 0 for "<" (ie. left child); 1 for ">" (right)
+ *
+ * Though it is a little more confusing to read the code, the approach
+ * allows using half as much code (and hence cache footprint) for tree
+ * manipulations and eliminates many conditional branches.
+ *
+ * - The avl_index_t is an opaque "cookie" used to find nodes at or
+ * adjacent to where a new value would be inserted in the tree. The value
+ * is a modified "avl_node_t *". The bottom bit (normally 0 for a
+ * pointer) is set to indicate if that the new node has a value greater
+ * than the value of the indicated "avl_node_t *".
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/debug.h>
+#include <sys/avl.h>
+#include <sys/cmn_err.h>
+
+/*
+ * Small arrays to translate between balance (or diff) values and child indeces.
+ *
+ * Code that deals with binary tree data structures will randomly use
+ * left and right children when examining a tree. C "if()" statements
+ * which evaluate randomly suffer from very poor hardware branch prediction.
+ * In this code we avoid some of the branch mispredictions by using the
+ * following translation arrays. They replace random branches with an
+ * additional memory reference. Since the translation arrays are both very
+ * small the data should remain efficiently in cache.
+ */
+static const int avl_child2balance[2] = {-1, 1};
+static const int avl_balance2child[] = {0, 0, 1};
+
+
+/*
+ * Walk from one node to the previous valued node (ie. an infix walk
+ * towards the left). At any given node we do one of 2 things:
+ *
+ * - If there is a left child, go to it, then to it's rightmost descendant.
+ *
+ * - otherwise we return thru parent nodes until we've come from a right child.
+ *
+ * Return Value:
+ * NULL - if at the end of the nodes
+ * otherwise next node
+ */
+void *
+avl_walk(avl_tree_t *tree, void *oldnode, int left)
+{
+ size_t off = tree->avl_offset;
+ avl_node_t *node = AVL_DATA2NODE(oldnode, off);
+ int right = 1 - left;
+ int was_child;
+
+
+ /*
+ * nowhere to walk to if tree is empty
+ */
+ if (node == NULL)
+ return (NULL);
+
+ /*
+ * Visit the previous valued node. There are two possibilities:
+ *
+ * If this node has a left child, go down one left, then all
+ * the way right.
+ */
+ if (node->avl_child[left] != NULL) {
+ for (node = node->avl_child[left];
+ node->avl_child[right] != NULL;
+ node = node->avl_child[right])
+ ;
+ /*
+ * Otherwise, return thru left children as far as we can.
+ */
+ } else {
+ for (;;) {
+ was_child = AVL_XCHILD(node);
+ node = AVL_XPARENT(node);
+ if (node == NULL)
+ return (NULL);
+ if (was_child == right)
+ break;
+ }
+ }
+
+ return (AVL_NODE2DATA(node, off));
+}
+
+/*
+ * Return the lowest valued node in a tree or NULL.
+ * (leftmost child from root of tree)
+ */
+void *
+avl_first(avl_tree_t *tree)
+{
+ avl_node_t *node;
+ avl_node_t *prev = NULL;
+ size_t off = tree->avl_offset;
+
+ for (node = tree->avl_root; node != NULL; node = node->avl_child[0])
+ prev = node;
+
+ if (prev != NULL)
+ return (AVL_NODE2DATA(prev, off));
+ return (NULL);
+}
+
+/*
+ * Return the highest valued node in a tree or NULL.
+ * (rightmost child from root of tree)
+ */
+void *
+avl_last(avl_tree_t *tree)
+{
+ avl_node_t *node;
+ avl_node_t *prev = NULL;
+ size_t off = tree->avl_offset;
+
+ for (node = tree->avl_root; node != NULL; node = node->avl_child[1])
+ prev = node;
+
+ if (prev != NULL)
+ return (AVL_NODE2DATA(prev, off));
+ return (NULL);
+}
+
+/*
+ * Access the node immediately before or after an insertion point.
+ *
+ * "avl_index_t" is a (avl_node_t *) with the bottom bit indicating a child
+ *
+ * Return value:
+ * NULL: no node in the given direction
+ * "void *" of the found tree node
+ */
+void *
+avl_nearest(avl_tree_t *tree, avl_index_t where, int direction)
+{
+ int child = AVL_INDEX2CHILD(where);
+ avl_node_t *node = AVL_INDEX2NODE(where);
+ void *data;
+ size_t off = tree->avl_offset;
+
+ if (node == NULL) {
+ ASSERT(tree->avl_root == NULL);
+ return (NULL);
+ }
+ data = AVL_NODE2DATA(node, off);
+ if (child != direction)
+ return (data);
+
+ return (avl_walk(tree, data, direction));
+}
+
+
+/*
+ * Search for the node which contains "value". The algorithm is a
+ * simple binary tree search.
+ *
+ * return value:
+ * NULL: the value is not in the AVL tree
+ * *where (if not NULL) is set to indicate the insertion point
+ * "void *" of the found tree node
+ */
+void *
+avl_find(avl_tree_t *tree, void *value, avl_index_t *where)
+{
+ avl_node_t *node;
+ avl_node_t *prev = NULL;
+ int child = 0;
+ int diff;
+ size_t off = tree->avl_offset;
+
+ for (node = tree->avl_root; node != NULL;
+ node = node->avl_child[child]) {
+
+ prev = node;
+
+ diff = tree->avl_compar(value, AVL_NODE2DATA(node, off));
+ ASSERT(-1 <= diff && diff <= 1);
+ if (diff == 0) {
+#ifdef DEBUG
+ if (where != NULL)
+ *where = 0;
+#endif
+ return (AVL_NODE2DATA(node, off));
+ }
+ child = avl_balance2child[1 + diff];
+
+ }
+
+ if (where != NULL)
+ *where = AVL_MKINDEX(prev, child);
+
+ return (NULL);
+}
+
+
+/*
+ * Perform a rotation to restore balance at the subtree given by depth.
+ *
+ * This routine is used by both insertion and deletion. The return value
+ * indicates:
+ * 0 : subtree did not change height
+ * !0 : subtree was reduced in height
+ *
+ * The code is written as if handling left rotations, right rotations are
+ * symmetric and handled by swapping values of variables right/left[_heavy]
+ *
+ * On input balance is the "new" balance at "node". This value is either
+ * -2 or +2.
+ */
+static int
+avl_rotation(avl_tree_t *tree, avl_node_t *node, int balance)
+{
+ int left = !(balance < 0); /* when balance = -2, left will be 0 */
+ int right = 1 - left;
+ int left_heavy = balance >> 1;
+ int right_heavy = -left_heavy;
+ avl_node_t *parent = AVL_XPARENT(node);
+ avl_node_t *child = node->avl_child[left];
+ avl_node_t *cright;
+ avl_node_t *gchild;
+ avl_node_t *gright;
+ avl_node_t *gleft;
+ int which_child = AVL_XCHILD(node);
+ int child_bal = AVL_XBALANCE(child);
+
+ /* BEGIN CSTYLED */
+ /*
+ * case 1 : node is overly left heavy, the left child is balanced or
+ * also left heavy. This requires the following rotation.
+ *
+ * (node bal:-2)
+ * / \
+ * / \
+ * (child bal:0 or -1)
+ * / \
+ * / \
+ * cright
+ *
+ * becomes:
+ *
+ * (child bal:1 or 0)
+ * / \
+ * / \
+ * (node bal:-1 or 0)
+ * / \
+ * / \
+ * cright
+ *
+ * we detect this situation by noting that child's balance is not
+ * right_heavy.
+ */
+ /* END CSTYLED */
+ if (child_bal != right_heavy) {
+
+ /*
+ * compute new balance of nodes
+ *
+ * If child used to be left heavy (now balanced) we reduced
+ * the height of this sub-tree -- used in "return...;" below
+ */
+ child_bal += right_heavy; /* adjust towards right */
+
+ /*
+ * move "cright" to be node's left child
+ */
+ cright = child->avl_child[right];
+ node->avl_child[left] = cright;
+ if (cright != NULL) {
+ AVL_SETPARENT(cright, node);
+ AVL_SETCHILD(cright, left);
+ }
+
+ /*
+ * move node to be child's right child
+ */
+ child->avl_child[right] = node;
+ AVL_SETBALANCE(node, -child_bal);
+ AVL_SETCHILD(node, right);
+ AVL_SETPARENT(node, child);
+
+ /*
+ * update the pointer into this subtree
+ */
+ AVL_SETBALANCE(child, child_bal);
+ AVL_SETCHILD(child, which_child);
+ AVL_SETPARENT(child, parent);
+ if (parent != NULL)
+ parent->avl_child[which_child] = child;
+ else
+ tree->avl_root = child;
+
+ return (child_bal == 0);
+ }
+
+ /* BEGIN CSTYLED */
+ /*
+ * case 2 : When node is left heavy, but child is right heavy we use
+ * a different rotation.
+ *
+ * (node b:-2)
+ * / \
+ * / \
+ * / \
+ * (child b:+1)
+ * / \
+ * / \
+ * (gchild b: != 0)
+ * / \
+ * / \
+ * gleft gright
+ *
+ * becomes:
+ *
+ * (gchild b:0)
+ * / \
+ * / \
+ * / \
+ * (child b:?) (node b:?)
+ * / \ / \
+ * / \ / \
+ * gleft gright
+ *
+ * computing the new balances is more complicated. As an example:
+ * if gchild was right_heavy, then child is now left heavy
+ * else it is balanced
+ */
+ /* END CSTYLED */
+ gchild = child->avl_child[right];
+ gleft = gchild->avl_child[left];
+ gright = gchild->avl_child[right];
+
+ /*
+ * move gright to left child of node and
+ *
+ * move gleft to right child of node
+ */
+ node->avl_child[left] = gright;
+ if (gright != NULL) {
+ AVL_SETPARENT(gright, node);
+ AVL_SETCHILD(gright, left);
+ }
+
+ child->avl_child[right] = gleft;
+ if (gleft != NULL) {
+ AVL_SETPARENT(gleft, child);
+ AVL_SETCHILD(gleft, right);
+ }
+
+ /*
+ * move child to left child of gchild and
+ *
+ * move node to right child of gchild and
+ *
+ * fixup parent of all this to point to gchild
+ */
+ balance = AVL_XBALANCE(gchild);
+ gchild->avl_child[left] = child;
+ AVL_SETBALANCE(child, (balance == right_heavy ? left_heavy : 0));
+ AVL_SETPARENT(child, gchild);
+ AVL_SETCHILD(child, left);
+
+ gchild->avl_child[right] = node;
+ AVL_SETBALANCE(node, (balance == left_heavy ? right_heavy : 0));
+ AVL_SETPARENT(node, gchild);
+ AVL_SETCHILD(node, right);
+
+ AVL_SETBALANCE(gchild, 0);
+ AVL_SETPARENT(gchild, parent);
+ AVL_SETCHILD(gchild, which_child);
+ if (parent != NULL)
+ parent->avl_child[which_child] = gchild;
+ else
+ tree->avl_root = gchild;
+
+ return (1); /* the new tree is always shorter */
+}
+
+
+/*
+ * Insert a new node into an AVL tree at the specified (from avl_find()) place.
+ *
+ * Newly inserted nodes are always leaf nodes in the tree, since avl_find()
+ * searches out to the leaf positions. The avl_index_t indicates the node
+ * which will be the parent of the new node.
+ *
+ * After the node is inserted, a single rotation further up the tree may
+ * be necessary to maintain an acceptable AVL balance.
+ */
+void
+avl_insert(avl_tree_t *tree, void *new_data, avl_index_t where)
+{
+ avl_node_t *node;
+ avl_node_t *parent = AVL_INDEX2NODE(where);
+ int old_balance;
+ int new_balance;
+ int which_child = AVL_INDEX2CHILD(where);
+ size_t off = tree->avl_offset;
+
+ ASSERT(tree);
+#ifdef _LP64
+ ASSERT(((uintptr_t)new_data & 0x7) == 0);
+#endif
+
+ node = AVL_DATA2NODE(new_data, off);
+
+ /*
+ * First, add the node to the tree at the indicated position.
+ */
+ ++tree->avl_numnodes;
+
+ node->avl_child[0] = NULL;
+ node->avl_child[1] = NULL;
+
+ AVL_SETCHILD(node, which_child);
+ AVL_SETBALANCE(node, 0);
+ AVL_SETPARENT(node, parent);
+ if (parent != NULL) {
+ ASSERT(parent->avl_child[which_child] == NULL);
+ parent->avl_child[which_child] = node;
+ } else {
+ ASSERT(tree->avl_root == NULL);
+ tree->avl_root = node;
+ }
+ /*
+ * Now, back up the tree modifying the balance of all nodes above the
+ * insertion point. If we get to a highly unbalanced ancestor, we
+ * need to do a rotation. If we back out of the tree we are done.
+ * If we brought any subtree into perfect balance (0), we are also done.
+ */
+ for (;;) {
+ node = parent;
+ if (node == NULL)
+ return;
+
+ /*
+ * Compute the new balance
+ */
+ old_balance = AVL_XBALANCE(node);
+ new_balance = old_balance + avl_child2balance[which_child];
+
+ /*
+ * If we introduced equal balance, then we are done immediately
+ */
+ if (new_balance == 0) {
+ AVL_SETBALANCE(node, 0);
+ return;
+ }
+
+ /*
+ * If both old and new are not zero we went
+ * from -1 to -2 balance, do a rotation.
+ */
+ if (old_balance != 0)
+ break;
+
+ AVL_SETBALANCE(node, new_balance);
+ parent = AVL_XPARENT(node);
+ which_child = AVL_XCHILD(node);
+ }
+
+ /*
+ * perform a rotation to fix the tree and return
+ */
+ (void) avl_rotation(tree, node, new_balance);
+}
+
+/*
+ * Insert "new_data" in "tree" in the given "direction" either after or
+ * before (AVL_AFTER, AVL_BEFORE) the data "here".
+ *
+ * Insertions can only be done at empty leaf points in the tree, therefore
+ * if the given child of the node is already present we move to either
+ * the AVL_PREV or AVL_NEXT and reverse the insertion direction. Since
+ * every other node in the tree is a leaf, this always works.
+ *
+ * To help developers using this interface, we assert that the new node
+ * is correctly ordered at every step of the way in DEBUG kernels.
+ */
+void
+avl_insert_here(
+ avl_tree_t *tree,
+ void *new_data,
+ void *here,
+ int direction)
+{
+ avl_node_t *node;
+ int child = direction; /* rely on AVL_BEFORE == 0, AVL_AFTER == 1 */
+#ifdef DEBUG
+ int diff;
+#endif
+
+ ASSERT(tree != NULL);
+ ASSERT(new_data != NULL);
+ ASSERT(here != NULL);
+ ASSERT(direction == AVL_BEFORE || direction == AVL_AFTER);
+
+ /*
+ * If corresponding child of node is not NULL, go to the neighboring
+ * node and reverse the insertion direction.
+ */
+ node = AVL_DATA2NODE(here, tree->avl_offset);
+
+#ifdef DEBUG
+ diff = tree->avl_compar(new_data, here);
+ ASSERT(-1 <= diff && diff <= 1);
+ ASSERT(diff != 0);
+ ASSERT(diff > 0 ? child == 1 : child == 0);
+#endif
+
+ if (node->avl_child[child] != NULL) {
+ node = node->avl_child[child];
+ child = 1 - child;
+ while (node->avl_child[child] != NULL) {
+#ifdef DEBUG
+ diff = tree->avl_compar(new_data,
+ AVL_NODE2DATA(node, tree->avl_offset));
+ ASSERT(-1 <= diff && diff <= 1);
+ ASSERT(diff != 0);
+ ASSERT(diff > 0 ? child == 1 : child == 0);
+#endif
+ node = node->avl_child[child];
+ }
+#ifdef DEBUG
+ diff = tree->avl_compar(new_data,
+ AVL_NODE2DATA(node, tree->avl_offset));
+ ASSERT(-1 <= diff && diff <= 1);
+ ASSERT(diff != 0);
+ ASSERT(diff > 0 ? child == 1 : child == 0);
+#endif
+ }
+ ASSERT(node->avl_child[child] == NULL);
+
+ avl_insert(tree, new_data, AVL_MKINDEX(node, child));
+}
+
+/*
+ * Add a new node to an AVL tree.
+ */
+void
+avl_add(avl_tree_t *tree, void *new_node)
+{
+ avl_index_t where;
+
+ /*
+ * This is unfortunate. We want to call panic() here, even for
+ * non-DEBUG kernels. In userland, however, we can't depend on anything
+ * in libc or else the rtld build process gets confused. So, all we can
+ * do in userland is resort to a normal ASSERT().
+ */
+ if (avl_find(tree, new_node, &where) != NULL)
+#ifdef _KERNEL
+ panic("avl_find() succeeded inside avl_add()");
+#else
+ ASSERT(0);
+#endif
+ avl_insert(tree, new_node, where);
+}
+
+/*
+ * Delete a node from the AVL tree. Deletion is similar to insertion, but
+ * with 2 complications.
+ *
+ * First, we may be deleting an interior node. Consider the following subtree:
+ *
+ * d c c
+ * / \ / \ / \
+ * b e b e b e
+ * / \ / \ /
+ * a c a a
+ *
+ * When we are deleting node (d), we find and bring up an adjacent valued leaf
+ * node, say (c), to take the interior node's place. In the code this is
+ * handled by temporarily swapping (d) and (c) in the tree and then using
+ * common code to delete (d) from the leaf position.
+ *
+ * Secondly, an interior deletion from a deep tree may require more than one
+ * rotation to fix the balance. This is handled by moving up the tree through
+ * parents and applying rotations as needed. The return value from
+ * avl_rotation() is used to detect when a subtree did not change overall
+ * height due to a rotation.
+ */
+void
+avl_remove(avl_tree_t *tree, void *data)
+{
+ avl_node_t *delete;
+ avl_node_t *parent;
+ avl_node_t *node;
+ avl_node_t tmp;
+ int old_balance;
+ int new_balance;
+ int left;
+ int right;
+ int which_child;
+ size_t off = tree->avl_offset;
+
+ ASSERT(tree);
+
+ delete = AVL_DATA2NODE(data, off);
+
+ /*
+ * Deletion is easiest with a node that has at most 1 child.
+ * We swap a node with 2 children with a sequentially valued
+ * neighbor node. That node will have at most 1 child. Note this
+ * has no effect on the ordering of the remaining nodes.
+ *
+ * As an optimization, we choose the greater neighbor if the tree
+ * is right heavy, otherwise the left neighbor. This reduces the
+ * number of rotations needed.
+ */
+ if (delete->avl_child[0] != NULL && delete->avl_child[1] != NULL) {
+
+ /*
+ * choose node to swap from whichever side is taller
+ */
+ old_balance = AVL_XBALANCE(delete);
+ left = avl_balance2child[old_balance + 1];
+ right = 1 - left;
+
+ /*
+ * get to the previous value'd node
+ * (down 1 left, as far as possible right)
+ */
+ for (node = delete->avl_child[left];
+ node->avl_child[right] != NULL;
+ node = node->avl_child[right])
+ ;
+
+ /*
+ * create a temp placeholder for 'node'
+ * move 'node' to delete's spot in the tree
+ */
+ tmp = *node;
+
+ *node = *delete;
+ if (node->avl_child[left] == node)
+ node->avl_child[left] = &tmp;
+
+ parent = AVL_XPARENT(node);
+ if (parent != NULL)
+ parent->avl_child[AVL_XCHILD(node)] = node;
+ else
+ tree->avl_root = node;
+ AVL_SETPARENT(node->avl_child[left], node);
+ AVL_SETPARENT(node->avl_child[right], node);
+
+ /*
+ * Put tmp where node used to be (just temporary).
+ * It always has a parent and at most 1 child.
+ */
+ delete = &tmp;
+ parent = AVL_XPARENT(delete);
+ parent->avl_child[AVL_XCHILD(delete)] = delete;
+ which_child = (delete->avl_child[1] != 0);
+ if (delete->avl_child[which_child] != NULL)
+ AVL_SETPARENT(delete->avl_child[which_child], delete);
+ }
+
+
+ /*
+ * Here we know "delete" is at least partially a leaf node. It can
+ * be easily removed from the tree.
+ */
+ ASSERT(tree->avl_numnodes > 0);
+ --tree->avl_numnodes;
+ parent = AVL_XPARENT(delete);
+ which_child = AVL_XCHILD(delete);
+ if (delete->avl_child[0] != NULL)
+ node = delete->avl_child[0];
+ else
+ node = delete->avl_child[1];
+
+ /*
+ * Connect parent directly to node (leaving out delete).
+ */
+ if (node != NULL) {
+ AVL_SETPARENT(node, parent);
+ AVL_SETCHILD(node, which_child);
+ }
+ if (parent == NULL) {
+ tree->avl_root = node;
+ return;
+ }
+ parent->avl_child[which_child] = node;
+
+
+ /*
+ * Since the subtree is now shorter, begin adjusting parent balances
+ * and performing any needed rotations.
+ */
+ do {
+
+ /*
+ * Move up the tree and adjust the balance
+ *
+ * Capture the parent and which_child values for the next
+ * iteration before any rotations occur.
+ */
+ node = parent;
+ old_balance = AVL_XBALANCE(node);
+ new_balance = old_balance - avl_child2balance[which_child];
+ parent = AVL_XPARENT(node);
+ which_child = AVL_XCHILD(node);
+
+ /*
+ * If a node was in perfect balance but isn't anymore then
+ * we can stop, since the height didn't change above this point
+ * due to a deletion.
+ */
+ if (old_balance == 0) {
+ AVL_SETBALANCE(node, new_balance);
+ break;
+ }
+
+ /*
+ * If the new balance is zero, we don't need to rotate
+ * else
+ * need a rotation to fix the balance.
+ * If the rotation doesn't change the height
+ * of the sub-tree we have finished adjusting.
+ */
+ if (new_balance == 0)
+ AVL_SETBALANCE(node, new_balance);
+ else if (!avl_rotation(tree, node, new_balance))
+ break;
+ } while (parent != NULL);
+}
+
+/*
+ * initialize a new AVL tree
+ */
+void
+avl_create(avl_tree_t *tree, int (*compar) (const void *, const void *),
+ size_t size, size_t offset)
+{
+ ASSERT(tree);
+ ASSERT(compar);
+ ASSERT(size > 0);
+ ASSERT(size >= offset + sizeof (avl_node_t));
+#ifdef _LP64
+ ASSERT((offset & 0x7) == 0);
+#endif
+
+ tree->avl_compar = compar;
+ tree->avl_root = NULL;
+ tree->avl_numnodes = 0;
+ tree->avl_size = size;
+ tree->avl_offset = offset;
+}
+
+/*
+ * Delete a tree.
+ */
+/* ARGSUSED */
+void
+avl_destroy(avl_tree_t *tree)
+{
+ ASSERT(tree);
+ ASSERT(tree->avl_numnodes == 0);
+ ASSERT(tree->avl_root == NULL);
+}
+
+
+/*
+ * Return the number of nodes in an AVL tree.
+ */
+ulong_t
+avl_numnodes(avl_tree_t *tree)
+{
+ ASSERT(tree);
+ return (tree->avl_numnodes);
+}
+
+
+#define CHILDBIT (1L)
+
+/*
+ * Post-order tree walk used to visit all tree nodes and destroy the tree
+ * in post order. This is used for destroying a tree w/o paying any cost
+ * for rebalancing it.
+ *
+ * example:
+ *
+ * void *cookie = NULL;
+ * my_data_t *node;
+ *
+ * while ((node = avl_destroy_nodes(tree, &cookie)) != NULL)
+ * free(node);
+ * avl_destroy(tree);
+ *
+ * The cookie is really an avl_node_t to the current node's parent and
+ * an indication of which child you looked at last.
+ *
+ * On input, a cookie value of CHILDBIT indicates the tree is done.
+ */
+void *
+avl_destroy_nodes(avl_tree_t *tree, void **cookie)
+{
+ avl_node_t *node;
+ avl_node_t *parent;
+ int child;
+ void *first;
+ size_t off = tree->avl_offset;
+
+ /*
+ * Initial calls go to the first node or it's right descendant.
+ */
+ if (*cookie == NULL) {
+ first = avl_first(tree);
+
+ /*
+ * deal with an empty tree
+ */
+ if (first == NULL) {
+ *cookie = (void *)CHILDBIT;
+ return (NULL);
+ }
+
+ node = AVL_DATA2NODE(first, off);
+ parent = AVL_XPARENT(node);
+ goto check_right_side;
+ }
+
+ /*
+ * If there is no parent to return to we are done.
+ */
+ parent = (avl_node_t *)((uintptr_t)(*cookie) & ~CHILDBIT);
+ if (parent == NULL) {
+ if (tree->avl_root != NULL) {
+ ASSERT(tree->avl_numnodes == 1);
+ tree->avl_root = NULL;
+ tree->avl_numnodes = 0;
+ }
+ return (NULL);
+ }
+
+ /*
+ * Remove the child pointer we just visited from the parent and tree.
+ */
+ child = (uintptr_t)(*cookie) & CHILDBIT;
+ parent->avl_child[child] = NULL;
+ ASSERT(tree->avl_numnodes > 1);
+ --tree->avl_numnodes;
+
+ /*
+ * If we just did a right child or there isn't one, go up to parent.
+ */
+ if (child == 1 || parent->avl_child[1] == NULL) {
+ node = parent;
+ parent = AVL_XPARENT(parent);
+ goto done;
+ }
+
+ /*
+ * Do parent's right child, then leftmost descendent.
+ */
+ node = parent->avl_child[1];
+ while (node->avl_child[0] != NULL) {
+ parent = node;
+ node = node->avl_child[0];
+ }
+
+ /*
+ * If here, we moved to a left child. It may have one
+ * child on the right (when balance == +1).
+ */
+check_right_side:
+ if (node->avl_child[1] != NULL) {
+ ASSERT(AVL_XBALANCE(node) == 1);
+ parent = node;
+ node = node->avl_child[1];
+ ASSERT(node->avl_child[0] == NULL &&
+ node->avl_child[1] == NULL);
+ } else {
+ ASSERT(AVL_XBALANCE(node) <= 0);
+ }
+
+done:
+ if (parent == NULL) {
+ *cookie = (void *)CHILDBIT;
+ ASSERT(node == tree->avl_root);
+ } else {
+ *cookie = (void *)((uintptr_t)parent | AVL_XCHILD(node));
+ }
+
+ return (AVL_NODE2DATA(node, off));
+}
diff --git a/common/ctf/ctf_create.c b/common/ctf/ctf_create.c
new file mode 100644
index 000000000000..a4f3df34e8cf
--- /dev/null
+++ b/common/ctf/ctf_create.c
@@ -0,0 +1,1366 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/sysmacros.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <ctf_impl.h>
+
+/*
+ * This static string is used as the template for initially populating a
+ * dynamic container's string table. We always store \0 in the first byte,
+ * and we use the generic string "PARENT" to mark this container's parent
+ * if one is associated with the container using ctf_import().
+ */
+static const char _CTF_STRTAB_TEMPLATE[] = "\0PARENT";
+
+/*
+ * To create an empty CTF container, we just declare a zeroed header and call
+ * ctf_bufopen() on it. If ctf_bufopen succeeds, we mark the new container r/w
+ * and initialize the dynamic members. We set dtstrlen to 1 to reserve the
+ * first byte of the string table for a \0 byte, and we start assigning type
+ * IDs at 1 because type ID 0 is used as a sentinel.
+ */
+ctf_file_t *
+ctf_create(int *errp)
+{
+ static const ctf_header_t hdr = { { CTF_MAGIC, CTF_VERSION, 0 } };
+
+ const ulong_t hashlen = 128;
+ ctf_dtdef_t **hash = ctf_alloc(hashlen * sizeof (ctf_dtdef_t *));
+ ctf_sect_t cts;
+ ctf_file_t *fp;
+
+ if (hash == NULL)
+ return (ctf_set_open_errno(errp, EAGAIN));
+
+ cts.cts_name = _CTF_SECTION;
+ cts.cts_type = SHT_PROGBITS;
+ cts.cts_flags = 0;
+ cts.cts_data = &hdr;
+ cts.cts_size = sizeof (hdr);
+ cts.cts_entsize = 1;
+ cts.cts_offset = 0;
+
+ if ((fp = ctf_bufopen(&cts, NULL, NULL, errp)) == NULL) {
+ ctf_free(hash, hashlen * sizeof (ctf_dtdef_t *));
+ return (NULL);
+ }
+
+ fp->ctf_flags |= LCTF_RDWR;
+ fp->ctf_dthashlen = hashlen;
+ bzero(hash, hashlen * sizeof (ctf_dtdef_t *));
+ fp->ctf_dthash = hash;
+ fp->ctf_dtstrlen = sizeof (_CTF_STRTAB_TEMPLATE);
+ fp->ctf_dtnextid = 1;
+ fp->ctf_dtoldid = 0;
+
+ return (fp);
+}
+
+static uchar_t *
+ctf_copy_smembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t)
+{
+ ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ ctf_member_t ctm;
+
+ for (; dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (dmd->dmd_name) {
+ ctm.ctm_name = soff;
+ soff += strlen(dmd->dmd_name) + 1;
+ } else
+ ctm.ctm_name = 0;
+
+ ctm.ctm_type = (ushort_t)dmd->dmd_type;
+ ctm.ctm_offset = (ushort_t)dmd->dmd_offset;
+
+ bcopy(&ctm, t, sizeof (ctm));
+ t += sizeof (ctm);
+ }
+
+ return (t);
+}
+
+static uchar_t *
+ctf_copy_lmembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t)
+{
+ ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ ctf_lmember_t ctlm;
+
+ for (; dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (dmd->dmd_name) {
+ ctlm.ctlm_name = soff;
+ soff += strlen(dmd->dmd_name) + 1;
+ } else
+ ctlm.ctlm_name = 0;
+
+ ctlm.ctlm_type = (ushort_t)dmd->dmd_type;
+ ctlm.ctlm_pad = 0;
+ ctlm.ctlm_offsethi = CTF_OFFSET_TO_LMEMHI(dmd->dmd_offset);
+ ctlm.ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO(dmd->dmd_offset);
+
+ bcopy(&ctlm, t, sizeof (ctlm));
+ t += sizeof (ctlm);
+ }
+
+ return (t);
+}
+
+static uchar_t *
+ctf_copy_emembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t)
+{
+ ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ ctf_enum_t cte;
+
+ for (; dmd != NULL; dmd = ctf_list_next(dmd)) {
+ cte.cte_name = soff;
+ cte.cte_value = dmd->dmd_value;
+ soff += strlen(dmd->dmd_name) + 1;
+ bcopy(&cte, t, sizeof (cte));
+ t += sizeof (cte);
+ }
+
+ return (t);
+}
+
+static uchar_t *
+ctf_copy_membnames(ctf_dtdef_t *dtd, uchar_t *s)
+{
+ ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ size_t len;
+
+ for (; dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (dmd->dmd_name == NULL)
+ continue; /* skip anonymous members */
+ len = strlen(dmd->dmd_name) + 1;
+ bcopy(dmd->dmd_name, s, len);
+ s += len;
+ }
+
+ return (s);
+}
+
+/*
+ * If the specified CTF container is writable and has been modified, reload
+ * this container with the updated type definitions. In order to make this
+ * code and the rest of libctf as simple as possible, we perform updates by
+ * taking the dynamic type definitions and creating an in-memory CTF file
+ * containing the definitions, and then call ctf_bufopen() on it. This not
+ * only leverages ctf_bufopen(), but also avoids having to bifurcate the rest
+ * of the library code with different lookup paths for static and dynamic
+ * type definitions. We are therefore optimizing greatly for lookup over
+ * update, which we assume will be an uncommon operation. We perform one
+ * extra trick here for the benefit of callers and to keep our code simple:
+ * ctf_bufopen() will return a new ctf_file_t, but we want to keep the fp
+ * constant for the caller, so after ctf_bufopen() returns, we use bcopy to
+ * swap the interior of the old and new ctf_file_t's, and then free the old.
+ */
+int
+ctf_update(ctf_file_t *fp)
+{
+ ctf_file_t ofp, *nfp;
+ ctf_header_t hdr;
+ ctf_dtdef_t *dtd;
+ ctf_sect_t cts;
+
+ uchar_t *s, *s0, *t;
+ size_t size;
+ void *buf;
+ int err;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (!(fp->ctf_flags & LCTF_DIRTY))
+ return (0); /* no update required */
+
+ /*
+ * Fill in an initial CTF header. We will leave the label, object,
+ * and function sections empty and only output a header, type section,
+ * and string table. The type section begins at a 4-byte aligned
+ * boundary past the CTF header itself (at relative offset zero).
+ */
+ bzero(&hdr, sizeof (hdr));
+ hdr.cth_magic = CTF_MAGIC;
+ hdr.cth_version = CTF_VERSION;
+
+ if (fp->ctf_flags & LCTF_CHILD)
+ hdr.cth_parname = 1; /* i.e. _CTF_STRTAB_TEMPLATE[1] */
+
+ /*
+ * Iterate through the dynamic type definition list and compute the
+ * size of the CTF type section we will need to generate.
+ */
+ for (size = 0, dtd = ctf_list_next(&fp->ctf_dtdefs);
+ dtd != NULL; dtd = ctf_list_next(dtd)) {
+
+ uint_t kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ uint_t vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info);
+
+ if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT)
+ size += sizeof (ctf_stype_t);
+ else
+ size += sizeof (ctf_type_t);
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ size += sizeof (uint_t);
+ break;
+ case CTF_K_ARRAY:
+ size += sizeof (ctf_array_t);
+ break;
+ case CTF_K_FUNCTION:
+ size += sizeof (ushort_t) * (vlen + (vlen & 1));
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (dtd->dtd_data.ctt_size < CTF_LSTRUCT_THRESH)
+ size += sizeof (ctf_member_t) * vlen;
+ else
+ size += sizeof (ctf_lmember_t) * vlen;
+ break;
+ case CTF_K_ENUM:
+ size += sizeof (ctf_enum_t) * vlen;
+ break;
+ }
+ }
+
+ /*
+ * Fill in the string table offset and size, compute the size of the
+ * entire CTF buffer we need, and then allocate a new buffer and
+ * bcopy the finished header to the start of the buffer.
+ */
+ hdr.cth_stroff = hdr.cth_typeoff + size;
+ hdr.cth_strlen = fp->ctf_dtstrlen;
+ size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen;
+
+ if ((buf = ctf_data_alloc(size)) == MAP_FAILED)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ bcopy(&hdr, buf, sizeof (ctf_header_t));
+ t = (uchar_t *)buf + sizeof (ctf_header_t);
+ s = s0 = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_stroff;
+
+ bcopy(_CTF_STRTAB_TEMPLATE, s, sizeof (_CTF_STRTAB_TEMPLATE));
+ s += sizeof (_CTF_STRTAB_TEMPLATE);
+
+ /*
+ * We now take a final lap through the dynamic type definition list and
+ * copy the appropriate type records and strings to the output buffer.
+ */
+ for (dtd = ctf_list_next(&fp->ctf_dtdefs);
+ dtd != NULL; dtd = ctf_list_next(dtd)) {
+
+ uint_t kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ uint_t vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info);
+
+ ctf_array_t cta;
+ uint_t encoding;
+ size_t len;
+
+ if (dtd->dtd_name != NULL) {
+ dtd->dtd_data.ctt_name = (uint_t)(s - s0);
+ len = strlen(dtd->dtd_name) + 1;
+ bcopy(dtd->dtd_name, s, len);
+ s += len;
+ } else
+ dtd->dtd_data.ctt_name = 0;
+
+ if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT)
+ len = sizeof (ctf_stype_t);
+ else
+ len = sizeof (ctf_type_t);
+
+ bcopy(&dtd->dtd_data, t, len);
+ t += len;
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ if (kind == CTF_K_INTEGER) {
+ encoding = CTF_INT_DATA(
+ dtd->dtd_u.dtu_enc.cte_format,
+ dtd->dtd_u.dtu_enc.cte_offset,
+ dtd->dtd_u.dtu_enc.cte_bits);
+ } else {
+ encoding = CTF_FP_DATA(
+ dtd->dtd_u.dtu_enc.cte_format,
+ dtd->dtd_u.dtu_enc.cte_offset,
+ dtd->dtd_u.dtu_enc.cte_bits);
+ }
+ bcopy(&encoding, t, sizeof (encoding));
+ t += sizeof (encoding);
+ break;
+
+ case CTF_K_ARRAY:
+ cta.cta_contents = (ushort_t)
+ dtd->dtd_u.dtu_arr.ctr_contents;
+ cta.cta_index = (ushort_t)
+ dtd->dtd_u.dtu_arr.ctr_index;
+ cta.cta_nelems = dtd->dtd_u.dtu_arr.ctr_nelems;
+ bcopy(&cta, t, sizeof (cta));
+ t += sizeof (cta);
+ break;
+
+ case CTF_K_FUNCTION: {
+ ushort_t *argv = (ushort_t *)(uintptr_t)t;
+ uint_t argc;
+
+ for (argc = 0; argc < vlen; argc++)
+ *argv++ = (ushort_t)dtd->dtd_u.dtu_argv[argc];
+
+ if (vlen & 1)
+ *argv++ = 0; /* pad to 4-byte boundary */
+
+ t = (uchar_t *)argv;
+ break;
+ }
+
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (dtd->dtd_data.ctt_size < CTF_LSTRUCT_THRESH)
+ t = ctf_copy_smembers(dtd, (uint_t)(s - s0), t);
+ else
+ t = ctf_copy_lmembers(dtd, (uint_t)(s - s0), t);
+ s = ctf_copy_membnames(dtd, s);
+ break;
+
+ case CTF_K_ENUM:
+ t = ctf_copy_emembers(dtd, (uint_t)(s - s0), t);
+ s = ctf_copy_membnames(dtd, s);
+ break;
+ }
+ }
+
+ /*
+ * Finally, we are ready to ctf_bufopen() the new container. If this
+ * is successful, we then switch nfp and fp and free the old container.
+ */
+ ctf_data_protect(buf, size);
+ cts.cts_name = _CTF_SECTION;
+ cts.cts_type = SHT_PROGBITS;
+ cts.cts_flags = 0;
+ cts.cts_data = buf;
+ cts.cts_size = size;
+ cts.cts_entsize = 1;
+ cts.cts_offset = 0;
+
+ if ((nfp = ctf_bufopen(&cts, NULL, NULL, &err)) == NULL) {
+ ctf_data_free(buf, size);
+ return (ctf_set_errno(fp, err));
+ }
+
+ (void) ctf_setmodel(nfp, ctf_getmodel(fp));
+ (void) ctf_import(nfp, fp->ctf_parent);
+
+ nfp->ctf_refcnt = fp->ctf_refcnt;
+ nfp->ctf_flags |= fp->ctf_flags & ~LCTF_DIRTY;
+ nfp->ctf_data.cts_data = NULL; /* force ctf_data_free() on close */
+ nfp->ctf_dthash = fp->ctf_dthash;
+ nfp->ctf_dthashlen = fp->ctf_dthashlen;
+ nfp->ctf_dtdefs = fp->ctf_dtdefs;
+ nfp->ctf_dtstrlen = fp->ctf_dtstrlen;
+ nfp->ctf_dtnextid = fp->ctf_dtnextid;
+ nfp->ctf_dtoldid = fp->ctf_dtnextid - 1;
+ nfp->ctf_specific = fp->ctf_specific;
+
+ fp->ctf_dthash = NULL;
+ fp->ctf_dthashlen = 0;
+ bzero(&fp->ctf_dtdefs, sizeof (ctf_list_t));
+
+ bcopy(fp, &ofp, sizeof (ctf_file_t));
+ bcopy(nfp, fp, sizeof (ctf_file_t));
+ bcopy(&ofp, nfp, sizeof (ctf_file_t));
+
+ /*
+ * Initialize the ctf_lookup_by_name top-level dictionary. We keep an
+ * array of type name prefixes and the corresponding ctf_hash to use.
+ * NOTE: This code must be kept in sync with the code in ctf_bufopen().
+ */
+ fp->ctf_lookups[0].ctl_hash = &fp->ctf_structs;
+ fp->ctf_lookups[1].ctl_hash = &fp->ctf_unions;
+ fp->ctf_lookups[2].ctl_hash = &fp->ctf_enums;
+ fp->ctf_lookups[3].ctl_hash = &fp->ctf_names;
+
+ nfp->ctf_refcnt = 1; /* force nfp to be freed */
+ ctf_close(nfp);
+
+ return (0);
+}
+
+void
+ctf_dtd_insert(ctf_file_t *fp, ctf_dtdef_t *dtd)
+{
+ ulong_t h = dtd->dtd_type & (fp->ctf_dthashlen - 1);
+
+ dtd->dtd_hash = fp->ctf_dthash[h];
+ fp->ctf_dthash[h] = dtd;
+ ctf_list_append(&fp->ctf_dtdefs, dtd);
+}
+
+void
+ctf_dtd_delete(ctf_file_t *fp, ctf_dtdef_t *dtd)
+{
+ ulong_t h = dtd->dtd_type & (fp->ctf_dthashlen - 1);
+ ctf_dtdef_t *p, **q = &fp->ctf_dthash[h];
+ ctf_dmdef_t *dmd, *nmd;
+ size_t len;
+
+ for (p = *q; p != NULL; p = p->dtd_hash) {
+ if (p != dtd)
+ q = &p->dtd_hash;
+ else
+ break;
+ }
+
+ if (p != NULL)
+ *q = p->dtd_hash;
+
+ switch (CTF_INFO_KIND(dtd->dtd_data.ctt_info)) {
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ case CTF_K_ENUM:
+ for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ dmd != NULL; dmd = nmd) {
+ if (dmd->dmd_name != NULL) {
+ len = strlen(dmd->dmd_name) + 1;
+ ctf_free(dmd->dmd_name, len);
+ fp->ctf_dtstrlen -= len;
+ }
+ nmd = ctf_list_next(dmd);
+ ctf_free(dmd, sizeof (ctf_dmdef_t));
+ }
+ break;
+ case CTF_K_FUNCTION:
+ ctf_free(dtd->dtd_u.dtu_argv, sizeof (ctf_id_t) *
+ CTF_INFO_VLEN(dtd->dtd_data.ctt_info));
+ break;
+ }
+
+ if (dtd->dtd_name) {
+ len = strlen(dtd->dtd_name) + 1;
+ ctf_free(dtd->dtd_name, len);
+ fp->ctf_dtstrlen -= len;
+ }
+
+ ctf_list_delete(&fp->ctf_dtdefs, dtd);
+ ctf_free(dtd, sizeof (ctf_dtdef_t));
+}
+
+ctf_dtdef_t *
+ctf_dtd_lookup(ctf_file_t *fp, ctf_id_t type)
+{
+ ulong_t h = type & (fp->ctf_dthashlen - 1);
+ ctf_dtdef_t *dtd;
+
+ if (fp->ctf_dthash == NULL)
+ return (NULL);
+
+ for (dtd = fp->ctf_dthash[h]; dtd != NULL; dtd = dtd->dtd_hash) {
+ if (dtd->dtd_type == type)
+ break;
+ }
+
+ return (dtd);
+}
+
+/*
+ * Discard all of the dynamic type definitions that have been added to the
+ * container since the last call to ctf_update(). We locate such types by
+ * scanning the list and deleting elements that have type IDs greater than
+ * ctf_dtoldid, which is set by ctf_update(), above.
+ */
+int
+ctf_discard(ctf_file_t *fp)
+{
+ ctf_dtdef_t *dtd, *ntd;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (!(fp->ctf_flags & LCTF_DIRTY))
+ return (0); /* no update required */
+
+ for (dtd = ctf_list_next(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) {
+ if (dtd->dtd_type <= fp->ctf_dtoldid)
+ continue; /* skip types that have been committed */
+
+ ntd = ctf_list_next(dtd);
+ ctf_dtd_delete(fp, dtd);
+ }
+
+ fp->ctf_dtnextid = fp->ctf_dtoldid + 1;
+ fp->ctf_flags &= ~LCTF_DIRTY;
+
+ return (0);
+}
+
+static ctf_id_t
+ctf_add_generic(ctf_file_t *fp, uint_t flag, const char *name, ctf_dtdef_t **rp)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+ char *s = NULL;
+
+ if (flag != CTF_ADD_NONROOT && flag != CTF_ADD_ROOT)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (CTF_INDEX_TO_TYPE(fp->ctf_dtnextid, 1) > CTF_MAX_TYPE)
+ return (ctf_set_errno(fp, ECTF_FULL));
+
+ if ((dtd = ctf_alloc(sizeof (ctf_dtdef_t))) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if (name != NULL && (s = ctf_strdup(name)) == NULL) {
+ ctf_free(dtd, sizeof (ctf_dtdef_t));
+ return (ctf_set_errno(fp, EAGAIN));
+ }
+
+ type = fp->ctf_dtnextid++;
+ type = CTF_INDEX_TO_TYPE(type, (fp->ctf_flags & LCTF_CHILD));
+
+ bzero(dtd, sizeof (ctf_dtdef_t));
+ dtd->dtd_name = s;
+ dtd->dtd_type = type;
+
+ if (s != NULL)
+ fp->ctf_dtstrlen += strlen(s) + 1;
+
+ ctf_dtd_insert(fp, dtd);
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ *rp = dtd;
+ return (type);
+}
+
+/*
+ * When encoding integer sizes, we want to convert a byte count in the range
+ * 1-8 to the closest power of 2 (e.g. 3->4, 5->8, etc). The clp2() function
+ * is a clever implementation from "Hacker's Delight" by Henry Warren, Jr.
+ */
+static size_t
+clp2(size_t x)
+{
+ x--;
+
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+
+ return (x + 1);
+}
+
+static ctf_id_t
+ctf_add_encoded(ctf_file_t *fp, uint_t flag,
+ const char *name, const ctf_encoding_t *ep, uint_t kind)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (ep == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, 0);
+ dtd->dtd_data.ctt_size = clp2(P2ROUNDUP(ep->cte_bits, NBBY) / NBBY);
+ dtd->dtd_u.dtu_enc = *ep;
+
+ return (type);
+}
+
+static ctf_id_t
+ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, 0);
+ dtd->dtd_data.ctt_type = (ushort_t)ref;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_integer(ctf_file_t *fp, uint_t flag,
+ const char *name, const ctf_encoding_t *ep)
+{
+ return (ctf_add_encoded(fp, flag, name, ep, CTF_K_INTEGER));
+}
+
+ctf_id_t
+ctf_add_float(ctf_file_t *fp, uint_t flag,
+ const char *name, const ctf_encoding_t *ep)
+{
+ return (ctf_add_encoded(fp, flag, name, ep, CTF_K_FLOAT));
+}
+
+ctf_id_t
+ctf_add_pointer(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+{
+ return (ctf_add_reftype(fp, flag, ref, CTF_K_POINTER));
+}
+
+ctf_id_t
+ctf_add_array(ctf_file_t *fp, uint_t flag, const ctf_arinfo_t *arp)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (arp == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_ARRAY, flag, 0);
+ dtd->dtd_data.ctt_size = 0;
+ dtd->dtd_u.dtu_arr = *arp;
+
+ return (type);
+}
+
+int
+ctf_set_array(ctf_file_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)
+{
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, type);
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (dtd == NULL || CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_ARRAY)
+ return (ctf_set_errno(fp, ECTF_BADID));
+
+ fp->ctf_flags |= LCTF_DIRTY;
+ dtd->dtd_u.dtu_arr = *arp;
+
+ return (0);
+}
+
+ctf_id_t
+ctf_add_function(ctf_file_t *fp, uint_t flag,
+ const ctf_funcinfo_t *ctc, const ctf_id_t *argv)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+ uint_t vlen;
+ ctf_id_t *vdat = NULL;
+
+ if (ctc == NULL || (ctc->ctc_flags & ~CTF_FUNC_VARARG) != 0 ||
+ (ctc->ctc_argc != 0 && argv == NULL))
+ return (ctf_set_errno(fp, EINVAL));
+
+ vlen = ctc->ctc_argc;
+ if (ctc->ctc_flags & CTF_FUNC_VARARG)
+ vlen++; /* add trailing zero to indicate varargs (see below) */
+
+ if (vlen > CTF_MAX_VLEN)
+ return (ctf_set_errno(fp, EOVERFLOW));
+
+ if (vlen != 0 && (vdat = ctf_alloc(sizeof (ctf_id_t) * vlen)) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR) {
+ ctf_free(vdat, sizeof (ctf_id_t) * vlen);
+ return (CTF_ERR); /* errno is set for us */
+ }
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_FUNCTION, flag, vlen);
+ dtd->dtd_data.ctt_type = (ushort_t)ctc->ctc_return;
+
+ bcopy(argv, vdat, sizeof (ctf_id_t) * ctc->ctc_argc);
+ if (ctc->ctc_flags & CTF_FUNC_VARARG)
+ vdat[vlen - 1] = 0; /* add trailing zero to indicate varargs */
+ dtd->dtd_u.dtu_argv = vdat;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_struct(ctf_file_t *fp, uint_t flag, const char *name)
+{
+ ctf_hash_t *hp = &fp->ctf_structs;
+ ctf_helem_t *hep = NULL;
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (name != NULL)
+ hep = ctf_hash_lookup(hp, fp, name, strlen(name));
+
+ if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD)
+ dtd = ctf_dtd_lookup(fp, type = hep->h_type);
+ else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, flag, 0);
+ dtd->dtd_data.ctt_size = 0;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_union(ctf_file_t *fp, uint_t flag, const char *name)
+{
+ ctf_hash_t *hp = &fp->ctf_unions;
+ ctf_helem_t *hep = NULL;
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (name != NULL)
+ hep = ctf_hash_lookup(hp, fp, name, strlen(name));
+
+ if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD)
+ dtd = ctf_dtd_lookup(fp, type = hep->h_type);
+ else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, flag, 0);
+ dtd->dtd_data.ctt_size = 0;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_enum(ctf_file_t *fp, uint_t flag, const char *name)
+{
+ ctf_hash_t *hp = &fp->ctf_enums;
+ ctf_helem_t *hep = NULL;
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (name != NULL)
+ hep = ctf_hash_lookup(hp, fp, name, strlen(name));
+
+ if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD)
+ dtd = ctf_dtd_lookup(fp, type = hep->h_type);
+ else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, flag, 0);
+ dtd->dtd_data.ctt_size = fp->ctf_dmodel->ctd_int;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_forward(ctf_file_t *fp, uint_t flag, const char *name, uint_t kind)
+{
+ ctf_hash_t *hp;
+ ctf_helem_t *hep;
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ switch (kind) {
+ case CTF_K_STRUCT:
+ hp = &fp->ctf_structs;
+ break;
+ case CTF_K_UNION:
+ hp = &fp->ctf_unions;
+ break;
+ case CTF_K_ENUM:
+ hp = &fp->ctf_enums;
+ break;
+ default:
+ return (ctf_set_errno(fp, ECTF_NOTSUE));
+ }
+
+ /*
+ * If the type is already defined or exists as a forward tag, just
+ * return the ctf_id_t of the existing definition.
+ */
+ if (name != NULL && (hep = ctf_hash_lookup(hp,
+ fp, name, strlen(name))) != NULL)
+ return (hep->h_type);
+
+ if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_FORWARD, flag, 0);
+ dtd->dtd_data.ctt_type = kind;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_typedef(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_TYPEDEF, flag, 0);
+ dtd->dtd_data.ctt_type = (ushort_t)ref;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_volatile(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+{
+ return (ctf_add_reftype(fp, flag, ref, CTF_K_VOLATILE));
+}
+
+ctf_id_t
+ctf_add_const(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+{
+ return (ctf_add_reftype(fp, flag, ref, CTF_K_CONST));
+}
+
+ctf_id_t
+ctf_add_restrict(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+{
+ return (ctf_add_reftype(fp, flag, ref, CTF_K_RESTRICT));
+}
+
+int
+ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value)
+{
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, enid);
+ ctf_dmdef_t *dmd;
+
+ uint_t kind, vlen, root;
+ char *s;
+
+ if (name == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (dtd == NULL)
+ return (ctf_set_errno(fp, ECTF_BADID));
+
+ kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ root = CTF_INFO_ISROOT(dtd->dtd_data.ctt_info);
+ vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info);
+
+ if (kind != CTF_K_ENUM)
+ return (ctf_set_errno(fp, ECTF_NOTENUM));
+
+ if (vlen == CTF_MAX_VLEN)
+ return (ctf_set_errno(fp, ECTF_DTFULL));
+
+ for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (strcmp(dmd->dmd_name, name) == 0)
+ return (ctf_set_errno(fp, ECTF_DUPMEMBER));
+ }
+
+ if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if ((s = ctf_strdup(name)) == NULL) {
+ ctf_free(dmd, sizeof (ctf_dmdef_t));
+ return (ctf_set_errno(fp, EAGAIN));
+ }
+
+ dmd->dmd_name = s;
+ dmd->dmd_type = CTF_ERR;
+ dmd->dmd_offset = 0;
+ dmd->dmd_value = value;
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, root, vlen + 1);
+ ctf_list_append(&dtd->dtd_u.dtu_members, dmd);
+
+ fp->ctf_dtstrlen += strlen(s) + 1;
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ return (0);
+}
+
+int
+ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)
+{
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, souid);
+ ctf_dmdef_t *dmd;
+
+ ssize_t msize, malign, ssize;
+ uint_t kind, vlen, root;
+ char *s = NULL;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (dtd == NULL)
+ return (ctf_set_errno(fp, ECTF_BADID));
+
+ kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ root = CTF_INFO_ISROOT(dtd->dtd_data.ctt_info);
+ vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info);
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (ctf_set_errno(fp, ECTF_NOTSOU));
+
+ if (vlen == CTF_MAX_VLEN)
+ return (ctf_set_errno(fp, ECTF_DTFULL));
+
+ if (name != NULL) {
+ for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (dmd->dmd_name != NULL &&
+ strcmp(dmd->dmd_name, name) == 0)
+ return (ctf_set_errno(fp, ECTF_DUPMEMBER));
+ }
+ }
+
+ if ((msize = ctf_type_size(fp, type)) == CTF_ERR ||
+ (malign = ctf_type_align(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if (name != NULL && (s = ctf_strdup(name)) == NULL) {
+ ctf_free(dmd, sizeof (ctf_dmdef_t));
+ return (ctf_set_errno(fp, EAGAIN));
+ }
+
+ dmd->dmd_name = s;
+ dmd->dmd_type = type;
+ dmd->dmd_value = -1;
+
+ if (kind == CTF_K_STRUCT && vlen != 0) {
+ ctf_dmdef_t *lmd = ctf_list_prev(&dtd->dtd_u.dtu_members);
+ ctf_id_t ltype = ctf_type_resolve(fp, lmd->dmd_type);
+ size_t off = lmd->dmd_offset;
+
+ ctf_encoding_t linfo;
+ ssize_t lsize;
+
+ if (ctf_type_encoding(fp, ltype, &linfo) != CTF_ERR)
+ off += linfo.cte_bits;
+ else if ((lsize = ctf_type_size(fp, ltype)) != CTF_ERR)
+ off += lsize * NBBY;
+
+ /*
+ * Round up the offset of the end of the last member to the
+ * next byte boundary, convert 'off' to bytes, and then round
+ * it up again to the next multiple of the alignment required
+ * by the new member. Finally, convert back to bits and store
+ * the result in dmd_offset. Technically we could do more
+ * efficient packing if the new member is a bit-field, but
+ * we're the "compiler" and ANSI says we can do as we choose.
+ */
+ off = roundup(off, NBBY) / NBBY;
+ off = roundup(off, MAX(malign, 1));
+ dmd->dmd_offset = off * NBBY;
+ ssize = off + msize;
+ } else {
+ dmd->dmd_offset = 0;
+ ssize = ctf_get_ctt_size(fp, &dtd->dtd_data, NULL, NULL);
+ ssize = MAX(ssize, msize);
+ }
+
+ if (ssize > CTF_MAX_SIZE) {
+ dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
+ dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(ssize);
+ dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(ssize);
+ } else
+ dtd->dtd_data.ctt_size = (ushort_t)ssize;
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, root, vlen + 1);
+ ctf_list_append(&dtd->dtd_u.dtu_members, dmd);
+
+ if (s != NULL)
+ fp->ctf_dtstrlen += strlen(s) + 1;
+
+ fp->ctf_flags |= LCTF_DIRTY;
+ return (0);
+}
+
+static int
+enumcmp(const char *name, int value, void *arg)
+{
+ ctf_bundle_t *ctb = arg;
+ int bvalue;
+
+ return (ctf_enum_value(ctb->ctb_file, ctb->ctb_type,
+ name, &bvalue) == CTF_ERR || value != bvalue);
+}
+
+static int
+enumadd(const char *name, int value, void *arg)
+{
+ ctf_bundle_t *ctb = arg;
+
+ return (ctf_add_enumerator(ctb->ctb_file, ctb->ctb_type,
+ name, value) == CTF_ERR);
+}
+
+/*ARGSUSED*/
+static int
+membcmp(const char *name, ctf_id_t type, ulong_t offset, void *arg)
+{
+ ctf_bundle_t *ctb = arg;
+ ctf_membinfo_t ctm;
+
+ return (ctf_member_info(ctb->ctb_file, ctb->ctb_type,
+ name, &ctm) == CTF_ERR || ctm.ctm_offset != offset);
+}
+
+static int
+membadd(const char *name, ctf_id_t type, ulong_t offset, void *arg)
+{
+ ctf_bundle_t *ctb = arg;
+ ctf_dmdef_t *dmd;
+ char *s = NULL;
+
+ if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL)
+ return (ctf_set_errno(ctb->ctb_file, EAGAIN));
+
+ if (name != NULL && (s = ctf_strdup(name)) == NULL) {
+ ctf_free(dmd, sizeof (ctf_dmdef_t));
+ return (ctf_set_errno(ctb->ctb_file, EAGAIN));
+ }
+
+ /*
+ * For now, dmd_type is copied as the src_fp's type; it is reset to an
+ * equivalent dst_fp type by a final loop in ctf_add_type(), below.
+ */
+ dmd->dmd_name = s;
+ dmd->dmd_type = type;
+ dmd->dmd_offset = offset;
+ dmd->dmd_value = -1;
+
+ ctf_list_append(&ctb->ctb_dtd->dtd_u.dtu_members, dmd);
+
+ if (s != NULL)
+ ctb->ctb_file->ctf_dtstrlen += strlen(s) + 1;
+
+ ctb->ctb_file->ctf_flags |= LCTF_DIRTY;
+ return (0);
+}
+
+/*
+ * The ctf_add_type routine is used to copy a type from a source CTF container
+ * to a dynamic destination container. This routine operates recursively by
+ * following the source type's links and embedded member types. If the
+ * destination container already contains a named type which has the same
+ * attributes, then we succeed and return this type but no changes occur.
+ */
+ctf_id_t
+ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)
+{
+ ctf_id_t dst_type = CTF_ERR;
+ uint_t dst_kind = CTF_K_UNKNOWN;
+
+ const ctf_type_t *tp;
+ const char *name;
+ uint_t kind, flag, vlen;
+
+ ctf_bundle_t src, dst;
+ ctf_encoding_t src_en, dst_en;
+ ctf_arinfo_t src_ar, dst_ar;
+
+ ctf_dtdef_t *dtd;
+ ctf_funcinfo_t ctc;
+ ssize_t size;
+
+ ctf_hash_t *hp;
+ ctf_helem_t *hep;
+
+ if (!(dst_fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(dst_fp, ECTF_RDONLY));
+
+ if ((tp = ctf_lookup_by_id(&src_fp, src_type)) == NULL)
+ return (ctf_set_errno(dst_fp, ctf_errno(src_fp)));
+
+ name = ctf_strptr(src_fp, tp->ctt_name);
+ kind = LCTF_INFO_KIND(src_fp, tp->ctt_info);
+ flag = LCTF_INFO_ROOT(src_fp, tp->ctt_info);
+ vlen = LCTF_INFO_VLEN(src_fp, tp->ctt_info);
+
+ switch (kind) {
+ case CTF_K_STRUCT:
+ hp = &dst_fp->ctf_structs;
+ break;
+ case CTF_K_UNION:
+ hp = &dst_fp->ctf_unions;
+ break;
+ case CTF_K_ENUM:
+ hp = &dst_fp->ctf_enums;
+ break;
+ default:
+ hp = &dst_fp->ctf_names;
+ break;
+ }
+
+ /*
+ * If the source type has a name and is a root type (visible at the
+ * top-level scope), lookup the name in the destination container and
+ * verify that it is of the same kind before we do anything else.
+ */
+ if ((flag & CTF_ADD_ROOT) && name[0] != '\0' &&
+ (hep = ctf_hash_lookup(hp, dst_fp, name, strlen(name))) != NULL) {
+ dst_type = (ctf_id_t)hep->h_type;
+ dst_kind = ctf_type_kind(dst_fp, dst_type);
+ }
+
+ /*
+ * If an identically named dst_type exists, fail with ECTF_CONFLICT
+ * unless dst_type is a forward declaration and src_type is a struct,
+ * union, or enum (i.e. the definition of the previous forward decl).
+ */
+ if (dst_type != CTF_ERR && dst_kind != kind && (
+ dst_kind != CTF_K_FORWARD || (kind != CTF_K_ENUM &&
+ kind != CTF_K_STRUCT && kind != CTF_K_UNION)))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+
+ /*
+ * If the non-empty name was not found in the appropriate hash, search
+ * the list of pending dynamic definitions that are not yet committed.
+ * If a matching name and kind are found, assume this is the type that
+ * we are looking for. This is necessary to permit ctf_add_type() to
+ * operate recursively on entities such as a struct that contains a
+ * pointer member that refers to the same struct type.
+ */
+ if (dst_type == CTF_ERR && name[0] != '\0') {
+ for (dtd = ctf_list_prev(&dst_fp->ctf_dtdefs); dtd != NULL &&
+ dtd->dtd_type > dst_fp->ctf_dtoldid;
+ dtd = ctf_list_prev(dtd)) {
+ if (CTF_INFO_KIND(dtd->dtd_data.ctt_info) == kind &&
+ dtd->dtd_name != NULL &&
+ strcmp(dtd->dtd_name, name) == 0)
+ return (dtd->dtd_type);
+ }
+ }
+
+ src.ctb_file = src_fp;
+ src.ctb_type = src_type;
+ src.ctb_dtd = NULL;
+
+ dst.ctb_file = dst_fp;
+ dst.ctb_type = dst_type;
+ dst.ctb_dtd = NULL;
+
+ /*
+ * Now perform kind-specific processing. If dst_type is CTF_ERR, then
+ * we add a new type with the same properties as src_type to dst_fp.
+ * If dst_type is not CTF_ERR, then we verify that dst_type has the
+ * same attributes as src_type. We recurse for embedded references.
+ */
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ if (ctf_type_encoding(src_fp, src_type, &src_en) != 0)
+ return (ctf_set_errno(dst_fp, ctf_errno(src_fp)));
+
+ if (dst_type != CTF_ERR) {
+ if (ctf_type_encoding(dst_fp, dst_type, &dst_en) != 0)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (bcmp(&src_en, &dst_en, sizeof (ctf_encoding_t)))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+
+ } else if (kind == CTF_K_INTEGER) {
+ dst_type = ctf_add_integer(dst_fp, flag, name, &src_en);
+ } else
+ dst_type = ctf_add_float(dst_fp, flag, name, &src_en);
+ break;
+
+ case CTF_K_POINTER:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ src_type = ctf_type_reference(src_fp, src_type);
+ src_type = ctf_add_type(dst_fp, src_fp, src_type);
+
+ if (src_type == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dst_type = ctf_add_reftype(dst_fp, flag, src_type, kind);
+ break;
+
+ case CTF_K_ARRAY:
+ if (ctf_array_info(src_fp, src_type, &src_ar) == CTF_ERR)
+ return (ctf_set_errno(dst_fp, ctf_errno(src_fp)));
+
+ src_ar.ctr_contents =
+ ctf_add_type(dst_fp, src_fp, src_ar.ctr_contents);
+ src_ar.ctr_index =
+ ctf_add_type(dst_fp, src_fp, src_ar.ctr_index);
+ src_ar.ctr_nelems = src_ar.ctr_nelems;
+
+ if (src_ar.ctr_contents == CTF_ERR ||
+ src_ar.ctr_index == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (dst_type != CTF_ERR) {
+ if (ctf_array_info(dst_fp, dst_type, &dst_ar) != 0)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (bcmp(&src_ar, &dst_ar, sizeof (ctf_arinfo_t)))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+ } else
+ dst_type = ctf_add_array(dst_fp, flag, &src_ar);
+ break;
+
+ case CTF_K_FUNCTION:
+ ctc.ctc_return = ctf_add_type(dst_fp, src_fp, tp->ctt_type);
+ ctc.ctc_argc = 0;
+ ctc.ctc_flags = 0;
+
+ if (ctc.ctc_return == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dst_type = ctf_add_function(dst_fp, flag, &ctc, NULL);
+ break;
+
+ case CTF_K_STRUCT:
+ case CTF_K_UNION: {
+ ctf_dmdef_t *dmd;
+ int errs = 0;
+
+ /*
+ * Technically to match a struct or union we need to check both
+ * ways (src members vs. dst, dst members vs. src) but we make
+ * this more optimal by only checking src vs. dst and comparing
+ * the total size of the structure (which we must do anyway)
+ * which covers the possibility of dst members not in src.
+ * This optimization can be defeated for unions, but is so
+ * pathological as to render it irrelevant for our purposes.
+ */
+ if (dst_type != CTF_ERR && dst_kind != CTF_K_FORWARD) {
+ if (ctf_type_size(src_fp, src_type) !=
+ ctf_type_size(dst_fp, dst_type))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+
+ if (ctf_member_iter(src_fp, src_type, membcmp, &dst))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+
+ break;
+ }
+
+ /*
+ * Unlike the other cases, copying structs and unions is done
+ * manually so as to avoid repeated lookups in ctf_add_member
+ * and to ensure the exact same member offsets as in src_type.
+ */
+ dst_type = ctf_add_generic(dst_fp, flag, name, &dtd);
+ if (dst_type == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dst.ctb_type = dst_type;
+ dst.ctb_dtd = dtd;
+
+ if (ctf_member_iter(src_fp, src_type, membadd, &dst) != 0)
+ errs++; /* increment errs and fail at bottom of case */
+
+ if ((size = ctf_type_size(src_fp, src_type)) > CTF_MAX_SIZE) {
+ dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
+ dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size);
+ dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size);
+ } else
+ dtd->dtd_data.ctt_size = (ushort_t)size;
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, vlen);
+
+ /*
+ * Make a final pass through the members changing each dmd_type
+ * (a src_fp type) to an equivalent type in dst_fp. We pass
+ * through all members, leaving any that fail set to CTF_ERR.
+ */
+ for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if ((dmd->dmd_type = ctf_add_type(dst_fp, src_fp,
+ dmd->dmd_type)) == CTF_ERR)
+ errs++;
+ }
+
+ if (errs)
+ return (CTF_ERR); /* errno is set for us */
+ break;
+ }
+
+ case CTF_K_ENUM:
+ if (dst_type != CTF_ERR && dst_kind != CTF_K_FORWARD) {
+ if (ctf_enum_iter(src_fp, src_type, enumcmp, &dst) ||
+ ctf_enum_iter(dst_fp, dst_type, enumcmp, &src))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+ } else {
+ dst_type = ctf_add_enum(dst_fp, flag, name);
+ if ((dst.ctb_type = dst_type) == CTF_ERR ||
+ ctf_enum_iter(src_fp, src_type, enumadd, &dst))
+ return (CTF_ERR); /* errno is set for us */
+ }
+ break;
+
+ case CTF_K_FORWARD:
+ if (dst_type == CTF_ERR) {
+ dst_type = ctf_add_forward(dst_fp,
+ flag, name, CTF_K_STRUCT); /* assume STRUCT */
+ }
+ break;
+
+ case CTF_K_TYPEDEF:
+ src_type = ctf_type_reference(src_fp, src_type);
+ src_type = ctf_add_type(dst_fp, src_fp, src_type);
+
+ if (src_type == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ /*
+ * If dst_type is not CTF_ERR at this point, we should check if
+ * ctf_type_reference(dst_fp, dst_type) != src_type and if so
+ * fail with ECTF_CONFLICT. However, this causes problems with
+ * <sys/types.h> typedefs that vary based on things like if
+ * _ILP32x then pid_t is int otherwise long. We therefore omit
+ * this check and assume that if the identically named typedef
+ * already exists in dst_fp, it is correct or equivalent.
+ */
+ if (dst_type == CTF_ERR) {
+ dst_type = ctf_add_typedef(dst_fp, flag,
+ name, src_type);
+ }
+ break;
+
+ default:
+ return (ctf_set_errno(dst_fp, ECTF_CORRUPT));
+ }
+
+ return (dst_type);
+}
diff --git a/common/ctf/ctf_decl.c b/common/ctf/ctf_decl.c
new file mode 100644
index 000000000000..6bf57001570f
--- /dev/null
+++ b/common/ctf/ctf_decl.c
@@ -0,0 +1,184 @@
+/*
+ * 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"
+
+/*
+ * CTF Declaration Stack
+ *
+ * In order to implement ctf_type_name(), we must convert a type graph back
+ * into a C type declaration. Unfortunately, a type graph represents a storage
+ * class ordering of the type whereas a type declaration must obey the C rules
+ * for operator precedence, and the two orderings are frequently in conflict.
+ * For example, consider these CTF type graphs and their C declarations:
+ *
+ * CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER : int (*)()
+ * CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER : int (*)[]
+ *
+ * In each case, parentheses are used to raise operator * to higher lexical
+ * precedence, so the string form of the C declaration cannot be constructed by
+ * walking the type graph links and forming the string from left to right.
+ *
+ * The functions in this file build a set of stacks from the type graph nodes
+ * corresponding to the C operator precedence levels in the appropriate order.
+ * The code in ctf_type_name() can then iterate over the levels and nodes in
+ * lexical precedence order and construct the final C declaration string.
+ */
+
+#include <ctf_impl.h>
+
+void
+ctf_decl_init(ctf_decl_t *cd, char *buf, size_t len)
+{
+ int i;
+
+ bzero(cd, sizeof (ctf_decl_t));
+
+ for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++)
+ cd->cd_order[i] = CTF_PREC_BASE - 1;
+
+ cd->cd_qualp = CTF_PREC_BASE;
+ cd->cd_ordp = CTF_PREC_BASE;
+
+ cd->cd_buf = buf;
+ cd->cd_ptr = buf;
+ cd->cd_end = buf + len;
+}
+
+void
+ctf_decl_fini(ctf_decl_t *cd)
+{
+ ctf_decl_node_t *cdp, *ndp;
+ int i;
+
+ for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) {
+ for (cdp = ctf_list_next(&cd->cd_nodes[i]);
+ cdp != NULL; cdp = ndp) {
+ ndp = ctf_list_next(cdp);
+ ctf_free(cdp, sizeof (ctf_decl_node_t));
+ }
+ }
+}
+
+void
+ctf_decl_push(ctf_decl_t *cd, ctf_file_t *fp, ctf_id_t type)
+{
+ ctf_decl_node_t *cdp;
+ ctf_decl_prec_t prec;
+ uint_t kind, n = 1;
+ int is_qual = 0;
+
+ const ctf_type_t *tp;
+ ctf_arinfo_t ar;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) {
+ cd->cd_err = fp->ctf_errno;
+ return;
+ }
+
+ switch (kind = LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_ARRAY:
+ (void) ctf_array_info(fp, type, &ar);
+ ctf_decl_push(cd, fp, ar.ctr_contents);
+ n = ar.ctr_nelems;
+ prec = CTF_PREC_ARRAY;
+ break;
+
+ case CTF_K_TYPEDEF:
+ if (ctf_strptr(fp, tp->ctt_name)[0] == '\0') {
+ ctf_decl_push(cd, fp, tp->ctt_type);
+ return;
+ }
+ prec = CTF_PREC_BASE;
+ break;
+
+ case CTF_K_FUNCTION:
+ ctf_decl_push(cd, fp, tp->ctt_type);
+ prec = CTF_PREC_FUNCTION;
+ break;
+
+ case CTF_K_POINTER:
+ ctf_decl_push(cd, fp, tp->ctt_type);
+ prec = CTF_PREC_POINTER;
+ break;
+
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ ctf_decl_push(cd, fp, tp->ctt_type);
+ prec = cd->cd_qualp;
+ is_qual++;
+ break;
+
+ default:
+ prec = CTF_PREC_BASE;
+ }
+
+ if ((cdp = ctf_alloc(sizeof (ctf_decl_node_t))) == NULL) {
+ cd->cd_err = EAGAIN;
+ return;
+ }
+
+ cdp->cd_type = type;
+ cdp->cd_kind = kind;
+ cdp->cd_n = n;
+
+ if (ctf_list_next(&cd->cd_nodes[prec]) == NULL)
+ cd->cd_order[prec] = cd->cd_ordp++;
+
+ /*
+ * Reset cd_qualp to the highest precedence level that we've seen so
+ * far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER).
+ */
+ if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY)
+ cd->cd_qualp = prec;
+
+ /*
+ * C array declarators are ordered inside out so prepend them. Also by
+ * convention qualifiers of base types precede the type specifier (e.g.
+ * const int vs. int const) even though the two forms are equivalent.
+ */
+ if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE))
+ ctf_list_prepend(&cd->cd_nodes[prec], cdp);
+ else
+ ctf_list_append(&cd->cd_nodes[prec], cdp);
+}
+
+/*PRINTFLIKE2*/
+void
+ctf_decl_sprintf(ctf_decl_t *cd, const char *format, ...)
+{
+ size_t len = (size_t)(cd->cd_end - cd->cd_ptr);
+ va_list ap;
+ size_t n;
+
+ va_start(ap, format);
+ n = vsnprintf(cd->cd_ptr, len, format, ap);
+ va_end(ap);
+
+ cd->cd_ptr += MIN(n, len);
+ cd->cd_len += n;
+}
diff --git a/common/ctf/ctf_error.c b/common/ctf/ctf_error.c
new file mode 100644
index 000000000000..888c6c848b3f
--- /dev/null
+++ b/common/ctf/ctf_error.c
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctf_impl.h>
+
+static const char *const _ctf_errlist[] = {
+ "File is not in CTF or ELF format", /* ECTF_FMT */
+ "File uses more recent ELF version than libctf", /* ECTF_ELFVERS */
+ "File uses more recent CTF version than libctf", /* ECTF_CTFVERS */
+ "File is a different endian-ness than libctf", /* ECTF_ENDIAN */
+ "Symbol table uses invalid entry size", /* ECTF_SYMTAB */
+ "Symbol table data buffer is not valid", /* ECTF_SYMBAD */
+ "String table data buffer is not valid", /* ECTF_STRBAD */
+ "File data structure corruption detected", /* ECTF_CORRUPT */
+ "File does not contain CTF data", /* ECTF_NOCTFDATA */
+ "Buffer does not contain CTF data", /* ECTF_NOCTFBUF */
+ "Symbol table information is not available", /* ECTF_NOSYMTAB */
+ "Type information is in parent and unavailable", /* ECTF_NOPARENT */
+ "Cannot import types with different data model", /* ECTF_DMODEL */
+ "Failed to mmap a needed data section", /* ECTF_MMAP */
+ "Decompression package SUNWzlib not installed", /* ECTF_ZMISSING */
+ "Failed to initialize decompression library", /* ECTF_ZINIT */
+ "Failed to allocate decompression buffer", /* ECTF_ZALLOC */
+ "Failed to decompress CTF data", /* ECTF_DECOMPRESS */
+ "External string table is not available", /* ECTF_STRTAB */
+ "String name offset is corrupt", /* ECTF_BADNAME */
+ "Invalid type identifier", /* ECTF_BADID */
+ "Type is not a struct or union", /* ECTF_NOTSOU */
+ "Type is not an enum", /* ECTF_NOTENUM */
+ "Type is not a struct, union, or enum", /* ECTF_NOTSUE */
+ "Type is not an integer or float", /* ECTF_NOTINTFP */
+ "Type is not an array", /* ECTF_NOTARRAY */
+ "Type does not reference another type", /* ECTF_NOTREF */
+ "Input buffer is too small for type name", /* ECTF_NAMELEN */
+ "No type information available for that name", /* ECTF_NOTYPE */
+ "Syntax error in type name", /* ECTF_SYNTAX */
+ "Symbol table entry is not a function", /* ECTF_NOTFUNC */
+ "No function information available for symbol", /* ECTF_NOFUNCDAT */
+ "Symbol table entry is not a data object", /* ECTF_NOTDATA */
+ "No type information available for symbol", /* ECTF_NOTYPEDAT */
+ "No label information available for that name", /* ECTF_NOLABEL */
+ "File does not contain any labels", /* ECTF_NOLABELDATA */
+ "Feature not supported", /* ECTF_NOTSUP */
+ "Invalid enum element name", /* ECTF_NOENUMNAM */
+ "Invalid member name", /* ECTF_NOMEMBNAM */
+ "CTF container is read-only", /* ECTF_RDONLY */
+ "Limit on number of dynamic type members reached", /* ECTF_DTFULL */
+ "Limit on number of dynamic types reached", /* ECTF_FULL */
+ "Duplicate member name definition", /* ECTF_DUPMEMBER */
+ "Conflicting type is already defined", /* ECTF_CONFLICT */
+};
+
+static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]);
+
+const char *
+ctf_errmsg(int error)
+{
+ const char *str;
+
+ if (error >= ECTF_BASE && (error - ECTF_BASE) < _ctf_nerr)
+ str = _ctf_errlist[error - ECTF_BASE];
+ else
+ str = ctf_strerror(error);
+
+ return (str ? str : "Unknown error");
+}
+
+int
+ctf_errno(ctf_file_t *fp)
+{
+ return (fp->ctf_errno);
+}
diff --git a/common/ctf/ctf_hash.c b/common/ctf/ctf_hash.c
new file mode 100644
index 000000000000..b10a7618f66e
--- /dev/null
+++ b/common/ctf/ctf_hash.c
@@ -0,0 +1,178 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctf_impl.h>
+
+static const ushort_t _CTF_EMPTY[1] = { 0 };
+
+int
+ctf_hash_create(ctf_hash_t *hp, ulong_t nelems)
+{
+ if (nelems > USHRT_MAX)
+ return (EOVERFLOW);
+
+ /*
+ * If the hash table is going to be empty, don't bother allocating any
+ * memory and make the only bucket point to a zero so lookups fail.
+ */
+ if (nelems == 0) {
+ bzero(hp, sizeof (ctf_hash_t));
+ hp->h_buckets = (ushort_t *)_CTF_EMPTY;
+ hp->h_nbuckets = 1;
+ return (0);
+ }
+
+ hp->h_nbuckets = 211; /* use a prime number of hash buckets */
+ hp->h_nelems = nelems + 1; /* we use index zero as a sentinel */
+ hp->h_free = 1; /* first free element is index 1 */
+
+ hp->h_buckets = ctf_alloc(sizeof (ushort_t) * hp->h_nbuckets);
+ hp->h_chains = ctf_alloc(sizeof (ctf_helem_t) * hp->h_nelems);
+
+ if (hp->h_buckets == NULL || hp->h_chains == NULL) {
+ ctf_hash_destroy(hp);
+ return (EAGAIN);
+ }
+
+ bzero(hp->h_buckets, sizeof (ushort_t) * hp->h_nbuckets);
+ bzero(hp->h_chains, sizeof (ctf_helem_t) * hp->h_nelems);
+
+ return (0);
+}
+
+uint_t
+ctf_hash_size(const ctf_hash_t *hp)
+{
+ return (hp->h_nelems ? hp->h_nelems - 1 : 0);
+}
+
+static ulong_t
+ctf_hash_compute(const char *key, size_t len)
+{
+ ulong_t g, h = 0;
+ const char *p, *q = key + len;
+ size_t n = 0;
+
+ for (p = key; p < q; p++, n++) {
+ h = (h << 4) + *p;
+
+ if ((g = (h & 0xf0000000)) != 0) {
+ h ^= (g >> 24);
+ h ^= g;
+ }
+ }
+
+ return (h);
+}
+
+int
+ctf_hash_insert(ctf_hash_t *hp, ctf_file_t *fp, ushort_t type, uint_t name)
+{
+ ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID(name)];
+ const char *str = ctsp->cts_strs + CTF_NAME_OFFSET(name);
+ ctf_helem_t *hep = &hp->h_chains[hp->h_free];
+ ulong_t h;
+
+ if (type == 0)
+ return (EINVAL);
+
+ if (hp->h_free >= hp->h_nelems)
+ return (EOVERFLOW);
+
+ if (ctsp->cts_strs == NULL)
+ return (ECTF_STRTAB);
+
+ if (ctsp->cts_len <= CTF_NAME_OFFSET(name))
+ return (ECTF_BADNAME);
+
+ if (str[0] == '\0')
+ return (0); /* just ignore empty strings on behalf of caller */
+
+ hep->h_name = name;
+ hep->h_type = type;
+ h = ctf_hash_compute(str, strlen(str)) % hp->h_nbuckets;
+ hep->h_next = hp->h_buckets[h];
+ hp->h_buckets[h] = hp->h_free++;
+
+ return (0);
+}
+
+/*
+ * Wrapper for ctf_hash_lookup/ctf_hash_insert: if the key is already in the
+ * hash, override the previous definition with this new official definition.
+ * If the key is not present, then call ctf_hash_insert() and hash it in.
+ */
+int
+ctf_hash_define(ctf_hash_t *hp, ctf_file_t *fp, ushort_t type, uint_t name)
+{
+ const char *str = ctf_strptr(fp, name);
+ ctf_helem_t *hep = ctf_hash_lookup(hp, fp, str, strlen(str));
+
+ if (hep == NULL)
+ return (ctf_hash_insert(hp, fp, type, name));
+
+ hep->h_type = type;
+ return (0);
+}
+
+ctf_helem_t *
+ctf_hash_lookup(ctf_hash_t *hp, ctf_file_t *fp, const char *key, size_t len)
+{
+ ctf_helem_t *hep;
+ ctf_strs_t *ctsp;
+ const char *str;
+ ushort_t i;
+
+ ulong_t h = ctf_hash_compute(key, len) % hp->h_nbuckets;
+
+ for (i = hp->h_buckets[h]; i != 0; i = hep->h_next) {
+ hep = &hp->h_chains[i];
+ ctsp = &fp->ctf_str[CTF_NAME_STID(hep->h_name)];
+ str = ctsp->cts_strs + CTF_NAME_OFFSET(hep->h_name);
+
+ if (strncmp(key, str, len) == 0 && str[len] == '\0')
+ return (hep);
+ }
+
+ return (NULL);
+}
+
+void
+ctf_hash_destroy(ctf_hash_t *hp)
+{
+ if (hp->h_buckets != NULL && hp->h_nbuckets != 1) {
+ ctf_free(hp->h_buckets, sizeof (ushort_t) * hp->h_nbuckets);
+ hp->h_buckets = NULL;
+ }
+
+ if (hp->h_chains != NULL) {
+ ctf_free(hp->h_chains, sizeof (ctf_helem_t) * hp->h_nelems);
+ hp->h_chains = NULL;
+ }
+}
diff --git a/common/ctf/ctf_impl.h b/common/ctf/ctf_impl.h
new file mode 100644
index 000000000000..99990806a32e
--- /dev/null
+++ b/common/ctf/ctf_impl.h
@@ -0,0 +1,336 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CTF_IMPL_H
+#define _CTF_IMPL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/sysmacros.h>
+#include <sys/ctf_api.h>
+
+#ifdef _KERNEL
+
+#include <sys/systm.h>
+#include <sys/cmn_err.h>
+#include <sys/varargs.h>
+
+#define isspace(c) \
+ ((c) == ' ' || (c) == '\t' || (c) == '\n' || \
+ (c) == '\r' || (c) == '\f' || (c) == '\v')
+
+#define MAP_FAILED ((void *)-1)
+
+#else /* _KERNEL */
+
+#include <strings.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <limits.h>
+#include <ctype.h>
+
+#endif /* _KERNEL */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ctf_helem {
+ uint_t h_name; /* reference to name in string table */
+ ushort_t h_type; /* corresponding type ID number */
+ ushort_t h_next; /* index of next element in hash chain */
+} ctf_helem_t;
+
+typedef struct ctf_hash {
+ ushort_t *h_buckets; /* hash bucket array (chain indices) */
+ ctf_helem_t *h_chains; /* hash chains buffer */
+ ushort_t h_nbuckets; /* number of elements in bucket array */
+ ushort_t h_nelems; /* number of elements in hash table */
+ uint_t h_free; /* index of next free hash element */
+} ctf_hash_t;
+
+typedef struct ctf_strs {
+ const char *cts_strs; /* base address of string table */
+ size_t cts_len; /* size of string table in bytes */
+} ctf_strs_t;
+
+typedef struct ctf_dmodel {
+ const char *ctd_name; /* data model name */
+ int ctd_code; /* data model code */
+ size_t ctd_pointer; /* size of void * in bytes */
+ size_t ctd_char; /* size of char in bytes */
+ size_t ctd_short; /* size of short in bytes */
+ size_t ctd_int; /* size of int in bytes */
+ size_t ctd_long; /* size of long in bytes */
+} ctf_dmodel_t;
+
+typedef struct ctf_lookup {
+ const char *ctl_prefix; /* string prefix for this lookup */
+ size_t ctl_len; /* length of prefix string in bytes */
+ ctf_hash_t *ctl_hash; /* pointer to hash table for lookup */
+} ctf_lookup_t;
+
+typedef struct ctf_fileops {
+ ushort_t (*ctfo_get_kind)(ushort_t);
+ ushort_t (*ctfo_get_root)(ushort_t);
+ ushort_t (*ctfo_get_vlen)(ushort_t);
+} ctf_fileops_t;
+
+typedef struct ctf_list {
+ struct ctf_list *l_prev; /* previous pointer or tail pointer */
+ struct ctf_list *l_next; /* next pointer or head pointer */
+} ctf_list_t;
+
+typedef enum {
+ CTF_PREC_BASE,
+ CTF_PREC_POINTER,
+ CTF_PREC_ARRAY,
+ CTF_PREC_FUNCTION,
+ CTF_PREC_MAX
+} ctf_decl_prec_t;
+
+typedef struct ctf_decl_node {
+ ctf_list_t cd_list; /* linked list pointers */
+ ctf_id_t cd_type; /* type identifier */
+ uint_t cd_kind; /* type kind */
+ uint_t cd_n; /* type dimension if array */
+} ctf_decl_node_t;
+
+typedef struct ctf_decl {
+ ctf_list_t cd_nodes[CTF_PREC_MAX]; /* declaration node stacks */
+ int cd_order[CTF_PREC_MAX]; /* storage order of decls */
+ ctf_decl_prec_t cd_qualp; /* qualifier precision */
+ ctf_decl_prec_t cd_ordp; /* ordered precision */
+ char *cd_buf; /* buffer for output */
+ char *cd_ptr; /* buffer location */
+ char *cd_end; /* buffer limit */
+ size_t cd_len; /* buffer space required */
+ int cd_err; /* saved error value */
+} ctf_decl_t;
+
+typedef struct ctf_dmdef {
+ ctf_list_t dmd_list; /* list forward/back pointers */
+ char *dmd_name; /* name of this member */
+ ctf_id_t dmd_type; /* type of this member (for sou) */
+ ulong_t dmd_offset; /* offset of this member in bits (for sou) */
+ int dmd_value; /* value of this member (for enum) */
+} ctf_dmdef_t;
+
+typedef struct ctf_dtdef {
+ ctf_list_t dtd_list; /* list forward/back pointers */
+ struct ctf_dtdef *dtd_hash; /* hash chain pointer for ctf_dthash */
+ char *dtd_name; /* name associated with definition (if any) */
+ ctf_id_t dtd_type; /* type identifier for this definition */
+ ctf_type_t dtd_data; /* type node (see <sys/ctf.h>) */
+ union {
+ ctf_list_t dtu_members; /* struct, union, or enum */
+ ctf_arinfo_t dtu_arr; /* array */
+ ctf_encoding_t dtu_enc; /* integer or float */
+ ctf_id_t *dtu_argv; /* function */
+ } dtd_u;
+} ctf_dtdef_t;
+
+typedef struct ctf_bundle {
+ ctf_file_t *ctb_file; /* CTF container handle */
+ ctf_id_t ctb_type; /* CTF type identifier */
+ ctf_dtdef_t *ctb_dtd; /* CTF dynamic type definition (if any) */
+} ctf_bundle_t;
+
+/*
+ * The ctf_file is the structure used to represent a CTF container to library
+ * clients, who see it only as an opaque pointer. Modifications can therefore
+ * be made freely to this structure without regard to client versioning. The
+ * ctf_file_t typedef appears in <sys/ctf_api.h> and declares a forward tag.
+ *
+ * NOTE: ctf_update() requires that everything inside of ctf_file either be an
+ * immediate value, a pointer to dynamically allocated data *outside* of the
+ * ctf_file itself, or a pointer to statically allocated data. If you add a
+ * pointer to ctf_file that points to something within the ctf_file itself,
+ * you must make corresponding changes to ctf_update().
+ */
+struct ctf_file {
+ const ctf_fileops_t *ctf_fileops; /* version-specific file operations */
+ ctf_sect_t ctf_data; /* CTF data from object file */
+ ctf_sect_t ctf_symtab; /* symbol table from object file */
+ ctf_sect_t ctf_strtab; /* string table from object file */
+ ctf_hash_t ctf_structs; /* hash table of struct types */
+ ctf_hash_t ctf_unions; /* hash table of union types */
+ ctf_hash_t ctf_enums; /* hash table of enum types */
+ ctf_hash_t ctf_names; /* hash table of remaining type names */
+ ctf_lookup_t ctf_lookups[5]; /* pointers to hashes for name lookup */
+ ctf_strs_t ctf_str[2]; /* array of string table base and bounds */
+ const uchar_t *ctf_base; /* base of CTF header + uncompressed buffer */
+ const uchar_t *ctf_buf; /* uncompressed CTF data buffer */
+ size_t ctf_size; /* size of CTF header + uncompressed data */
+ uint_t *ctf_sxlate; /* translation table for symtab entries */
+ ulong_t ctf_nsyms; /* number of entries in symtab xlate table */
+ uint_t *ctf_txlate; /* translation table for type IDs */
+ ushort_t *ctf_ptrtab; /* translation table for pointer-to lookups */
+ ulong_t ctf_typemax; /* maximum valid type ID number */
+ const ctf_dmodel_t *ctf_dmodel; /* data model pointer (see above) */
+ struct ctf_file *ctf_parent; /* parent CTF container (if any) */
+ const char *ctf_parlabel; /* label in parent container (if any) */
+ const char *ctf_parname; /* basename of parent (if any) */
+ uint_t ctf_refcnt; /* reference count (for parent links) */
+ uint_t ctf_flags; /* libctf flags (see below) */
+ int ctf_errno; /* error code for most recent error */
+ int ctf_version; /* CTF data version */
+ ctf_dtdef_t **ctf_dthash; /* hash of dynamic type definitions */
+ ulong_t ctf_dthashlen; /* size of dynamic type hash bucket array */
+ ctf_list_t ctf_dtdefs; /* list of dynamic type definitions */
+ size_t ctf_dtstrlen; /* total length of dynamic type strings */
+ ulong_t ctf_dtnextid; /* next dynamic type id to assign */
+ ulong_t ctf_dtoldid; /* oldest id that has been committed */
+ void *ctf_specific; /* data for ctf_get/setspecific */
+};
+
+#define LCTF_INDEX_TO_TYPEPTR(fp, i) \
+ ((ctf_type_t *)((uintptr_t)(fp)->ctf_buf + (fp)->ctf_txlate[(i)]))
+
+#define LCTF_INFO_KIND(fp, info) ((fp)->ctf_fileops->ctfo_get_kind(info))
+#define LCTF_INFO_ROOT(fp, info) ((fp)->ctf_fileops->ctfo_get_root(info))
+#define LCTF_INFO_VLEN(fp, info) ((fp)->ctf_fileops->ctfo_get_vlen(info))
+
+#define LCTF_MMAP 0x0001 /* libctf should munmap buffers on close */
+#define LCTF_CHILD 0x0002 /* CTF container is a child */
+#define LCTF_RDWR 0x0004 /* CTF container is writable */
+#define LCTF_DIRTY 0x0008 /* CTF container has been modified */
+
+#define ECTF_BASE 1000 /* base value for libctf errnos */
+
+enum {
+ ECTF_FMT = ECTF_BASE, /* file is not in CTF or ELF format */
+ ECTF_ELFVERS, /* ELF version is more recent than libctf */
+ ECTF_CTFVERS, /* CTF version is more recent than libctf */
+ ECTF_ENDIAN, /* data is different endian-ness than lib */
+ ECTF_SYMTAB, /* symbol table uses invalid entry size */
+ ECTF_SYMBAD, /* symbol table data buffer invalid */
+ ECTF_STRBAD, /* string table data buffer invalid */
+ ECTF_CORRUPT, /* file data corruption detected */
+ ECTF_NOCTFDATA, /* ELF file does not contain CTF data */
+ ECTF_NOCTFBUF, /* buffer does not contain CTF data */
+ ECTF_NOSYMTAB, /* symbol table data is not available */
+ ECTF_NOPARENT, /* parent CTF container is not available */
+ ECTF_DMODEL, /* data model mismatch */
+ ECTF_MMAP, /* failed to mmap a data section */
+ ECTF_ZMISSING, /* decompression library not installed */
+ ECTF_ZINIT, /* failed to initialize decompression library */
+ ECTF_ZALLOC, /* failed to allocate decompression buffer */
+ ECTF_DECOMPRESS, /* failed to decompress CTF data */
+ ECTF_STRTAB, /* string table for this string is missing */
+ ECTF_BADNAME, /* string offset is corrupt w.r.t. strtab */
+ ECTF_BADID, /* invalid type ID number */
+ ECTF_NOTSOU, /* type is not a struct or union */
+ ECTF_NOTENUM, /* type is not an enum */
+ ECTF_NOTSUE, /* type is not a struct, union, or enum */
+ ECTF_NOTINTFP, /* type is not an integer or float */
+ ECTF_NOTARRAY, /* type is not an array */
+ ECTF_NOTREF, /* type does not reference another type */
+ ECTF_NAMELEN, /* buffer is too small to hold type name */
+ ECTF_NOTYPE, /* no type found corresponding to name */
+ ECTF_SYNTAX, /* syntax error in type name */
+ ECTF_NOTFUNC, /* symtab entry does not refer to a function */
+ ECTF_NOFUNCDAT, /* no func info available for function */
+ ECTF_NOTDATA, /* symtab entry does not refer to a data obj */
+ ECTF_NOTYPEDAT, /* no type info available for object */
+ ECTF_NOLABEL, /* no label found corresponding to name */
+ ECTF_NOLABELDATA, /* file does not contain any labels */
+ ECTF_NOTSUP, /* feature not supported */
+ ECTF_NOENUMNAM, /* enum element name not found */
+ ECTF_NOMEMBNAM, /* member name not found */
+ ECTF_RDONLY, /* CTF container is read-only */
+ ECTF_DTFULL, /* CTF type is full (no more members allowed) */
+ ECTF_FULL, /* CTF container is full */
+ ECTF_DUPMEMBER, /* duplicate member name definition */
+ ECTF_CONFLICT /* conflicting type definition present */
+};
+
+extern ssize_t ctf_get_ctt_size(const ctf_file_t *, const ctf_type_t *,
+ ssize_t *, ssize_t *);
+
+extern const ctf_type_t *ctf_lookup_by_id(ctf_file_t **, ctf_id_t);
+
+extern int ctf_hash_create(ctf_hash_t *, ulong_t);
+extern int ctf_hash_insert(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t);
+extern int ctf_hash_define(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t);
+extern ctf_helem_t *ctf_hash_lookup(ctf_hash_t *, ctf_file_t *,
+ const char *, size_t);
+extern uint_t ctf_hash_size(const ctf_hash_t *);
+extern void ctf_hash_destroy(ctf_hash_t *);
+
+#define ctf_list_prev(elem) ((void *)(((ctf_list_t *)(elem))->l_prev))
+#define ctf_list_next(elem) ((void *)(((ctf_list_t *)(elem))->l_next))
+
+extern void ctf_list_append(ctf_list_t *, void *);
+extern void ctf_list_prepend(ctf_list_t *, void *);
+extern void ctf_list_delete(ctf_list_t *, void *);
+
+extern void ctf_dtd_insert(ctf_file_t *, ctf_dtdef_t *);
+extern void ctf_dtd_delete(ctf_file_t *, ctf_dtdef_t *);
+extern ctf_dtdef_t *ctf_dtd_lookup(ctf_file_t *, ctf_id_t);
+
+extern void ctf_decl_init(ctf_decl_t *, char *, size_t);
+extern void ctf_decl_fini(ctf_decl_t *);
+extern void ctf_decl_push(ctf_decl_t *, ctf_file_t *, ctf_id_t);
+extern void ctf_decl_sprintf(ctf_decl_t *, const char *, ...);
+
+extern const char *ctf_strraw(ctf_file_t *, uint_t);
+extern const char *ctf_strptr(ctf_file_t *, uint_t);
+
+extern ctf_file_t *ctf_set_open_errno(int *, int);
+extern long ctf_set_errno(ctf_file_t *, int);
+
+extern const void *ctf_sect_mmap(ctf_sect_t *, int);
+extern void ctf_sect_munmap(const ctf_sect_t *);
+
+extern void *ctf_data_alloc(size_t);
+extern void ctf_data_free(void *, size_t);
+extern void ctf_data_protect(void *, size_t);
+
+extern void *ctf_alloc(size_t);
+extern void ctf_free(void *, size_t);
+
+extern char *ctf_strdup(const char *);
+extern const char *ctf_strerror(int);
+extern void ctf_dprintf(const char *, ...);
+
+extern void *ctf_zopen(int *);
+
+extern const char _CTF_SECTION[]; /* name of CTF ELF section */
+extern const char _CTF_NULLSTR[]; /* empty string */
+
+extern int _libctf_version; /* library client version */
+extern int _libctf_debug; /* debugging messages enabled */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTF_IMPL_H */
diff --git a/common/ctf/ctf_labels.c b/common/ctf/ctf_labels.c
new file mode 100644
index 000000000000..ddcb1d330202
--- /dev/null
+++ b/common/ctf/ctf_labels.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-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctf_impl.h>
+
+static int
+extract_label_info(ctf_file_t *fp, const ctf_lblent_t **ctl, uint_t *num_labels)
+{
+ const ctf_header_t *h;
+
+ /*
+ * Labels are only supported in V2 or later
+ */
+ if (fp->ctf_version < CTF_VERSION_2)
+ return (ctf_set_errno(fp, ECTF_NOTSUP));
+
+ h = (const ctf_header_t *)fp->ctf_data.cts_data;
+
+ /* LINTED - pointer alignment */
+ *ctl = (const ctf_lblent_t *)(fp->ctf_buf + h->cth_lbloff);
+ *num_labels = (h->cth_objtoff - h->cth_lbloff) / sizeof (ctf_lblent_t);
+
+ return (0);
+}
+
+/*
+ * Returns the topmost label, or NULL if any errors are encountered
+ */
+const char *
+ctf_label_topmost(ctf_file_t *fp)
+{
+ const ctf_lblent_t *ctlp;
+ const char *s;
+ uint_t num_labels;
+
+ if (extract_label_info(fp, &ctlp, &num_labels) == CTF_ERR)
+ return (NULL); /* errno is set */
+
+ if (num_labels == 0) {
+ (void) ctf_set_errno(fp, ECTF_NOLABELDATA);
+ return (NULL);
+ }
+
+ if ((s = ctf_strraw(fp, (ctlp + num_labels - 1)->ctl_label)) == NULL)
+ (void) ctf_set_errno(fp, ECTF_CORRUPT);
+
+ return (s);
+}
+
+/*
+ * Iterate over all labels. We pass the label string and the lblinfo_t struct
+ * to the specified callback function.
+ */
+int
+ctf_label_iter(ctf_file_t *fp, ctf_label_f *func, void *arg)
+{
+ const ctf_lblent_t *ctlp;
+ uint_t i, num_labels;
+ ctf_lblinfo_t linfo;
+ const char *lname;
+ int rc;
+
+ if (extract_label_info(fp, &ctlp, &num_labels) == CTF_ERR)
+ return (CTF_ERR); /* errno is set */
+
+ if (num_labels == 0)
+ return (ctf_set_errno(fp, ECTF_NOLABELDATA));
+
+ for (i = 0; i < num_labels; i++, ctlp++) {
+ if ((lname = ctf_strraw(fp, ctlp->ctl_label)) == NULL) {
+ ctf_dprintf("failed to decode label %u with "
+ "typeidx %u\n", ctlp->ctl_label, ctlp->ctl_typeidx);
+ return (ctf_set_errno(fp, ECTF_CORRUPT));
+ }
+
+ linfo.ctb_typeidx = ctlp->ctl_typeidx;
+ if ((rc = func(lname, &linfo, arg)) != 0)
+ return (rc);
+ }
+
+ return (0);
+}
+
+typedef struct linfo_cb_arg {
+ const char *lca_name; /* Label we want to retrieve info for */
+ ctf_lblinfo_t *lca_info; /* Where to store the info about the label */
+} linfo_cb_arg_t;
+
+static int
+label_info_cb(const char *lname, const ctf_lblinfo_t *linfo, void *arg)
+{
+ /*
+ * If lname matches the label we are looking for, copy the
+ * lblinfo_t struct for the caller.
+ */
+ if (strcmp(lname, ((linfo_cb_arg_t *)arg)->lca_name) == 0) {
+ /*
+ * Allow caller not to allocate storage to test if label exists
+ */
+ if (((linfo_cb_arg_t *)arg)->lca_info != NULL)
+ bcopy(linfo, ((linfo_cb_arg_t *)arg)->lca_info,
+ sizeof (ctf_lblinfo_t));
+ return (1); /* Indicate we found a match */
+ }
+
+ return (0);
+}
+
+/*
+ * Retrieve information about the label with name "lname"
+ */
+int
+ctf_label_info(ctf_file_t *fp, const char *lname, ctf_lblinfo_t *linfo)
+{
+ linfo_cb_arg_t cb_arg;
+ int rc;
+
+ cb_arg.lca_name = lname;
+ cb_arg.lca_info = linfo;
+
+ if ((rc = ctf_label_iter(fp, label_info_cb, &cb_arg)) == CTF_ERR)
+ return (rc);
+
+ if (rc != 1)
+ return (ctf_set_errno(fp, ECTF_NOLABEL));
+
+ return (0);
+}
diff --git a/common/ctf/ctf_lookup.c b/common/ctf/ctf_lookup.c
new file mode 100644
index 000000000000..f8fa72435591
--- /dev/null
+++ b/common/ctf/ctf_lookup.c
@@ -0,0 +1,313 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/sysmacros.h>
+#include <ctf_impl.h>
+
+/*
+ * Compare the given input string and length against a table of known C storage
+ * qualifier keywords. We just ignore these in ctf_lookup_by_name, below. To
+ * do this quickly, we use a pre-computed Perfect Hash Function similar to the
+ * technique originally described in the classic paper:
+ *
+ * R.J. Cichelli, "Minimal Perfect Hash Functions Made Simple",
+ * Communications of the ACM, Volume 23, Issue 1, January 1980, pp. 17-19.
+ *
+ * For an input string S of length N, we use hash H = S[N - 1] + N - 105, which
+ * for the current set of qualifiers yields a unique H in the range [0 .. 20].
+ * The hash can be modified when the keyword set changes as necessary. We also
+ * store the length of each keyword and check it prior to the final strcmp().
+ */
+static int
+isqualifier(const char *s, size_t len)
+{
+ static const struct qual {
+ const char *q_name;
+ size_t q_len;
+ } qhash[] = {
+ { "static", 6 }, { "", 0 }, { "", 0 }, { "", 0 },
+ { "volatile", 8 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 },
+ { "", 0 }, { "auto", 4 }, { "extern", 6 }, { "", 0 }, { "", 0 },
+ { "", 0 }, { "", 0 }, { "const", 5 }, { "register", 8 },
+ { "", 0 }, { "restrict", 8 }, { "_Restrict", 9 }
+ };
+
+ int h = s[len - 1] + (int)len - 105;
+ const struct qual *qp = &qhash[h];
+
+ return (h >= 0 && h < sizeof (qhash) / sizeof (qhash[0]) &&
+ len == qp->q_len && strncmp(qp->q_name, s, qp->q_len) == 0);
+}
+
+/*
+ * Attempt to convert the given C type name into the corresponding CTF type ID.
+ * It is not possible to do complete and proper conversion of type names
+ * without implementing a more full-fledged parser, which is necessary to
+ * handle things like types that are function pointers to functions that
+ * have arguments that are function pointers, and fun stuff like that.
+ * Instead, this function implements a very simple conversion algorithm that
+ * finds the things that we actually care about: structs, unions, enums,
+ * integers, floats, typedefs, and pointers to any of these named types.
+ */
+ctf_id_t
+ctf_lookup_by_name(ctf_file_t *fp, const char *name)
+{
+ static const char delimiters[] = " \t\n\r\v\f*";
+
+ const ctf_lookup_t *lp;
+ const ctf_helem_t *hp;
+ const char *p, *q, *end;
+ ctf_id_t type = 0;
+ ctf_id_t ntype, ptype;
+
+ if (name == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ for (p = name, end = name + strlen(name); *p != '\0'; p = q) {
+ while (isspace(*p))
+ p++; /* skip leading ws */
+
+ if (p == end)
+ break;
+
+ if ((q = strpbrk(p + 1, delimiters)) == NULL)
+ q = end; /* compare until end */
+
+ if (*p == '*') {
+ /*
+ * Find a pointer to type by looking in fp->ctf_ptrtab.
+ * If we can't find a pointer to the given type, see if
+ * we can compute a pointer to the type resulting from
+ * resolving the type down to its base type and use
+ * that instead. This helps with cases where the CTF
+ * data includes "struct foo *" but not "foo_t *" and
+ * the user tries to access "foo_t *" in the debugger.
+ */
+ ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)];
+ if (ntype == 0) {
+ ntype = ctf_type_resolve(fp, type);
+ if (ntype == CTF_ERR || (ntype = fp->ctf_ptrtab[
+ CTF_TYPE_TO_INDEX(ntype)]) == 0) {
+ (void) ctf_set_errno(fp, ECTF_NOTYPE);
+ goto err;
+ }
+ }
+
+ type = CTF_INDEX_TO_TYPE(ntype,
+ (fp->ctf_flags & LCTF_CHILD));
+
+ q = p + 1;
+ continue;
+ }
+
+ if (isqualifier(p, (size_t)(q - p)))
+ continue; /* skip qualifier keyword */
+
+ for (lp = fp->ctf_lookups; lp->ctl_prefix != NULL; lp++) {
+ if (lp->ctl_prefix[0] == '\0' ||
+ strncmp(p, lp->ctl_prefix, (size_t)(q - p)) == 0) {
+ for (p += lp->ctl_len; isspace(*p); p++)
+ continue; /* skip prefix and next ws */
+
+ if ((q = strchr(p, '*')) == NULL)
+ q = end; /* compare until end */
+
+ while (isspace(q[-1]))
+ q--; /* exclude trailing ws */
+
+ if ((hp = ctf_hash_lookup(lp->ctl_hash, fp, p,
+ (size_t)(q - p))) == NULL) {
+ (void) ctf_set_errno(fp, ECTF_NOTYPE);
+ goto err;
+ }
+
+ type = hp->h_type;
+ break;
+ }
+ }
+
+ if (lp->ctl_prefix == NULL) {
+ (void) ctf_set_errno(fp, ECTF_NOTYPE);
+ goto err;
+ }
+ }
+
+ if (*p != '\0' || type == 0)
+ return (ctf_set_errno(fp, ECTF_SYNTAX));
+
+ return (type);
+
+err:
+ if (fp->ctf_parent != NULL &&
+ (ptype = ctf_lookup_by_name(fp->ctf_parent, name)) != CTF_ERR)
+ return (ptype);
+
+ return (CTF_ERR);
+}
+
+/*
+ * Given a symbol table index, return the type of the data object described
+ * by the corresponding entry in the symbol table.
+ */
+ctf_id_t
+ctf_lookup_by_symbol(ctf_file_t *fp, ulong_t symidx)
+{
+ const ctf_sect_t *sp = &fp->ctf_symtab;
+ ctf_id_t type;
+
+ if (sp->cts_data == NULL)
+ return (ctf_set_errno(fp, ECTF_NOSYMTAB));
+
+ if (symidx >= fp->ctf_nsyms)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (sp->cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+ }
+
+ if (fp->ctf_sxlate[symidx] == -1u)
+ return (ctf_set_errno(fp, ECTF_NOTYPEDAT));
+
+ type = *(ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]);
+ if (type == 0)
+ return (ctf_set_errno(fp, ECTF_NOTYPEDAT));
+
+ return (type);
+}
+
+/*
+ * Return the pointer to the internal CTF type data corresponding to the
+ * given type ID. If the ID is invalid, the function returns NULL.
+ * This function is not exported outside of the library.
+ */
+const ctf_type_t *
+ctf_lookup_by_id(ctf_file_t **fpp, ctf_id_t type)
+{
+ ctf_file_t *fp = *fpp; /* caller passes in starting CTF container */
+
+ if ((fp->ctf_flags & LCTF_CHILD) && CTF_TYPE_ISPARENT(type) &&
+ (fp = fp->ctf_parent) == NULL) {
+ (void) ctf_set_errno(*fpp, ECTF_NOPARENT);
+ return (NULL);
+ }
+
+ type = CTF_TYPE_TO_INDEX(type);
+ if (type > 0 && type <= fp->ctf_typemax) {
+ *fpp = fp; /* function returns ending CTF container */
+ return (LCTF_INDEX_TO_TYPEPTR(fp, type));
+ }
+
+ (void) ctf_set_errno(fp, ECTF_BADID);
+ return (NULL);
+}
+
+/*
+ * Given a symbol table index, return the info for the function described
+ * by the corresponding entry in the symbol table.
+ */
+int
+ctf_func_info(ctf_file_t *fp, ulong_t symidx, ctf_funcinfo_t *fip)
+{
+ const ctf_sect_t *sp = &fp->ctf_symtab;
+ const ushort_t *dp;
+ ushort_t info, kind, n;
+
+ if (sp->cts_data == NULL)
+ return (ctf_set_errno(fp, ECTF_NOSYMTAB));
+
+ if (symidx >= fp->ctf_nsyms)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (sp->cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
+ return (ctf_set_errno(fp, ECTF_NOTFUNC));
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
+ return (ctf_set_errno(fp, ECTF_NOTFUNC));
+ }
+
+ if (fp->ctf_sxlate[symidx] == -1u)
+ return (ctf_set_errno(fp, ECTF_NOFUNCDAT));
+
+ dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]);
+
+ info = *dp++;
+ kind = LCTF_INFO_KIND(fp, info);
+ n = LCTF_INFO_VLEN(fp, info);
+
+ if (kind == CTF_K_UNKNOWN && n == 0)
+ return (ctf_set_errno(fp, ECTF_NOFUNCDAT));
+
+ if (kind != CTF_K_FUNCTION)
+ return (ctf_set_errno(fp, ECTF_CORRUPT));
+
+ fip->ctc_return = *dp++;
+ fip->ctc_argc = n;
+ fip->ctc_flags = 0;
+
+ if (n != 0 && dp[n - 1] == 0) {
+ fip->ctc_flags |= CTF_FUNC_VARARG;
+ fip->ctc_argc--;
+ }
+
+ return (0);
+}
+
+/*
+ * Given a symbol table index, return the arguments for the function described
+ * by the corresponding entry in the symbol table.
+ */
+int
+ctf_func_args(ctf_file_t *fp, ulong_t symidx, uint_t argc, ctf_id_t *argv)
+{
+ const ushort_t *dp;
+ ctf_funcinfo_t f;
+
+ if (ctf_func_info(fp, symidx, &f) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ /*
+ * The argument data is two ushort_t's past the translation table
+ * offset: one for the function info, and one for the return type.
+ */
+ dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]) + 2;
+
+ for (argc = MIN(argc, f.ctc_argc); argc != 0; argc--)
+ *argv++ = *dp++;
+
+ return (0);
+}
diff --git a/common/ctf/ctf_open.c b/common/ctf/ctf_open.c
new file mode 100644
index 000000000000..e49a4cb32934
--- /dev/null
+++ b/common/ctf/ctf_open.c
@@ -0,0 +1,954 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctf_impl.h>
+#include <sys/mman.h>
+#include <sys/zmod.h>
+
+static const ctf_dmodel_t _libctf_models[] = {
+ { "ILP32", CTF_MODEL_ILP32, 4, 1, 2, 4, 4 },
+ { "LP64", CTF_MODEL_LP64, 8, 1, 2, 4, 8 },
+ { NULL, 0, 0, 0, 0, 0, 0 }
+};
+
+const char _CTF_SECTION[] = ".SUNW_ctf";
+const char _CTF_NULLSTR[] = "";
+
+int _libctf_version = CTF_VERSION; /* library client version */
+int _libctf_debug = 0; /* debugging messages enabled */
+
+static ushort_t
+get_kind_v1(ushort_t info)
+{
+ return (CTF_INFO_KIND_V1(info));
+}
+
+static ushort_t
+get_kind_v2(ushort_t info)
+{
+ return (CTF_INFO_KIND(info));
+}
+
+static ushort_t
+get_root_v1(ushort_t info)
+{
+ return (CTF_INFO_ISROOT_V1(info));
+}
+
+static ushort_t
+get_root_v2(ushort_t info)
+{
+ return (CTF_INFO_ISROOT(info));
+}
+
+static ushort_t
+get_vlen_v1(ushort_t info)
+{
+ return (CTF_INFO_VLEN_V1(info));
+}
+
+static ushort_t
+get_vlen_v2(ushort_t info)
+{
+ return (CTF_INFO_VLEN(info));
+}
+
+static const ctf_fileops_t ctf_fileops[] = {
+ { NULL, NULL },
+ { get_kind_v1, get_root_v1, get_vlen_v1 },
+ { get_kind_v2, get_root_v2, get_vlen_v2 },
+};
+
+/*
+ * Convert a 32-bit ELF symbol into GElf (Elf64) and return a pointer to it.
+ */
+static Elf64_Sym *
+sym_to_gelf(const Elf32_Sym *src, Elf64_Sym *dst)
+{
+ dst->st_name = src->st_name;
+ dst->st_value = src->st_value;
+ dst->st_size = src->st_size;
+ dst->st_info = src->st_info;
+ dst->st_other = src->st_other;
+ dst->st_shndx = src->st_shndx;
+
+ return (dst);
+}
+
+/*
+ * Initialize the symtab translation table by filling each entry with the
+ * offset of the CTF type or function data corresponding to each STT_FUNC or
+ * STT_OBJECT entry in the symbol table.
+ */
+static int
+init_symtab(ctf_file_t *fp, const ctf_header_t *hp,
+ const ctf_sect_t *sp, const ctf_sect_t *strp)
+{
+ const uchar_t *symp = sp->cts_data;
+ uint_t *xp = fp->ctf_sxlate;
+ uint_t *xend = xp + fp->ctf_nsyms;
+
+ uint_t objtoff = hp->cth_objtoff;
+ uint_t funcoff = hp->cth_funcoff;
+
+ ushort_t info, vlen;
+ Elf64_Sym sym, *gsp;
+ const char *name;
+
+ /*
+ * The CTF data object and function type sections are ordered to match
+ * the relative order of the respective symbol types in the symtab.
+ * If no type information is available for a symbol table entry, a
+ * pad is inserted in the CTF section. As a further optimization,
+ * anonymous or undefined symbols are omitted from the CTF data.
+ */
+ for (; xp < xend; xp++, symp += sp->cts_entsize) {
+ if (sp->cts_entsize == sizeof (Elf32_Sym))
+ gsp = sym_to_gelf((Elf32_Sym *)(uintptr_t)symp, &sym);
+ else
+ gsp = (Elf64_Sym *)(uintptr_t)symp;
+
+ if (gsp->st_name < strp->cts_size)
+ name = (const char *)strp->cts_data + gsp->st_name;
+ else
+ name = _CTF_NULLSTR;
+
+ if (gsp->st_name == 0 || gsp->st_shndx == SHN_UNDEF ||
+ strcmp(name, "_START_") == 0 ||
+ strcmp(name, "_END_") == 0) {
+ *xp = -1u;
+ continue;
+ }
+
+ switch (ELF64_ST_TYPE(gsp->st_info)) {
+ case STT_OBJECT:
+ if (objtoff >= hp->cth_funcoff ||
+ (gsp->st_shndx == SHN_ABS && gsp->st_value == 0)) {
+ *xp = -1u;
+ break;
+ }
+
+ *xp = objtoff;
+ objtoff += sizeof (ushort_t);
+ break;
+
+ case STT_FUNC:
+ if (funcoff >= hp->cth_typeoff) {
+ *xp = -1u;
+ break;
+ }
+
+ *xp = funcoff;
+
+ info = *(ushort_t *)((uintptr_t)fp->ctf_buf + funcoff);
+ vlen = LCTF_INFO_VLEN(fp, info);
+
+ /*
+ * If we encounter a zero pad at the end, just skip it.
+ * Otherwise skip over the function and its return type
+ * (+2) and the argument list (vlen).
+ */
+ if (LCTF_INFO_KIND(fp, info) == CTF_K_UNKNOWN &&
+ vlen == 0)
+ funcoff += sizeof (ushort_t); /* skip pad */
+ else
+ funcoff += sizeof (ushort_t) * (vlen + 2);
+ break;
+
+ default:
+ *xp = -1u;
+ break;
+ }
+ }
+
+ ctf_dprintf("loaded %lu symtab entries\n", fp->ctf_nsyms);
+ return (0);
+}
+
+/*
+ * Initialize the type ID translation table with the byte offset of each type,
+ * and initialize the hash tables of each named type.
+ */
+static int
+init_types(ctf_file_t *fp, const ctf_header_t *cth)
+{
+ /* LINTED - pointer alignment */
+ const ctf_type_t *tbuf = (ctf_type_t *)(fp->ctf_buf + cth->cth_typeoff);
+ /* LINTED - pointer alignment */
+ const ctf_type_t *tend = (ctf_type_t *)(fp->ctf_buf + cth->cth_stroff);
+
+ ulong_t pop[CTF_K_MAX + 1] = { 0 };
+ const ctf_type_t *tp;
+ ctf_hash_t *hp;
+ ushort_t id, dst;
+ uint_t *xp;
+
+ /*
+ * We initially determine whether the container is a child or a parent
+ * based on the value of cth_parname. To support containers that pre-
+ * date cth_parname, we also scan the types themselves for references
+ * to values in the range reserved for child types in our first pass.
+ */
+ int child = cth->cth_parname != 0;
+ int nlstructs = 0, nlunions = 0;
+ int err;
+
+ /*
+ * We make two passes through the entire type section. In this first
+ * pass, we count the number of each type and the total number of types.
+ */
+ for (tp = tbuf; tp < tend; fp->ctf_typemax++) {
+ ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+ ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info);
+ ssize_t size, increment;
+
+ size_t vbytes;
+ uint_t n;
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ vbytes = sizeof (uint_t);
+ break;
+ case CTF_K_ARRAY:
+ vbytes = sizeof (ctf_array_t);
+ break;
+ case CTF_K_FUNCTION:
+ vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (fp->ctf_version == CTF_VERSION_1 ||
+ size < CTF_LSTRUCT_THRESH) {
+ ctf_member_t *mp = (ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ vbytes = sizeof (ctf_member_t) * vlen;
+ for (n = vlen; n != 0; n--, mp++)
+ child |= CTF_TYPE_ISCHILD(mp->ctm_type);
+ } else {
+ ctf_lmember_t *lmp = (ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ vbytes = sizeof (ctf_lmember_t) * vlen;
+ for (n = vlen; n != 0; n--, lmp++)
+ child |=
+ CTF_TYPE_ISCHILD(lmp->ctlm_type);
+ }
+ break;
+ case CTF_K_ENUM:
+ vbytes = sizeof (ctf_enum_t) * vlen;
+ break;
+ case CTF_K_FORWARD:
+ /*
+ * For forward declarations, ctt_type is the CTF_K_*
+ * kind for the tag, so bump that population count too.
+ * If ctt_type is unknown, treat the tag as a struct.
+ */
+ if (tp->ctt_type == CTF_K_UNKNOWN ||
+ tp->ctt_type >= CTF_K_MAX)
+ pop[CTF_K_STRUCT]++;
+ else
+ pop[tp->ctt_type]++;
+ /*FALLTHRU*/
+ case CTF_K_UNKNOWN:
+ vbytes = 0;
+ break;
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ child |= CTF_TYPE_ISCHILD(tp->ctt_type);
+ vbytes = 0;
+ break;
+ default:
+ ctf_dprintf("detected invalid CTF kind -- %u\n", kind);
+ return (ECTF_CORRUPT);
+ }
+ tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
+ pop[kind]++;
+ }
+
+ /*
+ * If we detected a reference to a child type ID, then we know this
+ * container is a child and may have a parent's types imported later.
+ */
+ if (child) {
+ ctf_dprintf("CTF container %p is a child\n", (void *)fp);
+ fp->ctf_flags |= LCTF_CHILD;
+ } else
+ ctf_dprintf("CTF container %p is a parent\n", (void *)fp);
+
+ /*
+ * Now that we've counted up the number of each type, we can allocate
+ * the hash tables, type translation table, and pointer table.
+ */
+ if ((err = ctf_hash_create(&fp->ctf_structs, pop[CTF_K_STRUCT])) != 0)
+ return (err);
+
+ if ((err = ctf_hash_create(&fp->ctf_unions, pop[CTF_K_UNION])) != 0)
+ return (err);
+
+ if ((err = ctf_hash_create(&fp->ctf_enums, pop[CTF_K_ENUM])) != 0)
+ return (err);
+
+ if ((err = ctf_hash_create(&fp->ctf_names,
+ pop[CTF_K_INTEGER] + pop[CTF_K_FLOAT] + pop[CTF_K_FUNCTION] +
+ pop[CTF_K_TYPEDEF] + pop[CTF_K_POINTER] + pop[CTF_K_VOLATILE] +
+ pop[CTF_K_CONST] + pop[CTF_K_RESTRICT])) != 0)
+ return (err);
+
+ fp->ctf_txlate = ctf_alloc(sizeof (uint_t) * (fp->ctf_typemax + 1));
+ fp->ctf_ptrtab = ctf_alloc(sizeof (ushort_t) * (fp->ctf_typemax + 1));
+
+ if (fp->ctf_txlate == NULL || fp->ctf_ptrtab == NULL)
+ return (EAGAIN); /* memory allocation failed */
+
+ xp = fp->ctf_txlate;
+ *xp++ = 0; /* type id 0 is used as a sentinel value */
+
+ bzero(fp->ctf_txlate, sizeof (uint_t) * (fp->ctf_typemax + 1));
+ bzero(fp->ctf_ptrtab, sizeof (ushort_t) * (fp->ctf_typemax + 1));
+
+ /*
+ * In the second pass through the types, we fill in each entry of the
+ * type and pointer tables and add names to the appropriate hashes.
+ */
+ for (id = 1, tp = tbuf; tp < tend; xp++, id++) {
+ ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+ ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info);
+ ssize_t size, increment;
+
+ const char *name;
+ size_t vbytes;
+ ctf_helem_t *hep;
+ ctf_encoding_t cte;
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+ name = ctf_strptr(fp, tp->ctt_name);
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ /*
+ * Only insert a new integer base type definition if
+ * this type name has not been defined yet. We re-use
+ * the names with different encodings for bit-fields.
+ */
+ if ((hep = ctf_hash_lookup(&fp->ctf_names, fp,
+ name, strlen(name))) == NULL) {
+ err = ctf_hash_insert(&fp->ctf_names, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ } else if (ctf_type_encoding(fp, hep->h_type,
+ &cte) == 0 && cte.cte_bits == 0) {
+ /*
+ * Work-around SOS8 stabs bug: replace existing
+ * intrinsic w/ same name if it was zero bits.
+ */
+ hep->h_type = CTF_INDEX_TO_TYPE(id, child);
+ }
+ vbytes = sizeof (uint_t);
+ break;
+
+ case CTF_K_ARRAY:
+ vbytes = sizeof (ctf_array_t);
+ break;
+
+ case CTF_K_FUNCTION:
+ err = ctf_hash_insert(&fp->ctf_names, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
+ break;
+
+ case CTF_K_STRUCT:
+ err = ctf_hash_define(&fp->ctf_structs, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+
+ if (fp->ctf_version == CTF_VERSION_1 ||
+ size < CTF_LSTRUCT_THRESH)
+ vbytes = sizeof (ctf_member_t) * vlen;
+ else {
+ vbytes = sizeof (ctf_lmember_t) * vlen;
+ nlstructs++;
+ }
+ break;
+
+ case CTF_K_UNION:
+ err = ctf_hash_define(&fp->ctf_unions, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+
+ if (fp->ctf_version == CTF_VERSION_1 ||
+ size < CTF_LSTRUCT_THRESH)
+ vbytes = sizeof (ctf_member_t) * vlen;
+ else {
+ vbytes = sizeof (ctf_lmember_t) * vlen;
+ nlunions++;
+ }
+ break;
+
+ case CTF_K_ENUM:
+ err = ctf_hash_define(&fp->ctf_enums, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+
+ vbytes = sizeof (ctf_enum_t) * vlen;
+ break;
+
+ case CTF_K_TYPEDEF:
+ err = ctf_hash_insert(&fp->ctf_names, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ vbytes = 0;
+ break;
+
+ case CTF_K_FORWARD:
+ /*
+ * Only insert forward tags into the given hash if the
+ * type or tag name is not already present.
+ */
+ switch (tp->ctt_type) {
+ case CTF_K_STRUCT:
+ hp = &fp->ctf_structs;
+ break;
+ case CTF_K_UNION:
+ hp = &fp->ctf_unions;
+ break;
+ case CTF_K_ENUM:
+ hp = &fp->ctf_enums;
+ break;
+ default:
+ hp = &fp->ctf_structs;
+ }
+
+ if (ctf_hash_lookup(hp, fp,
+ name, strlen(name)) == NULL) {
+ err = ctf_hash_insert(hp, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ }
+ vbytes = 0;
+ break;
+
+ case CTF_K_POINTER:
+ /*
+ * If the type referenced by the pointer is in this CTF
+ * container, then store the index of the pointer type
+ * in fp->ctf_ptrtab[ index of referenced type ].
+ */
+ if (CTF_TYPE_ISCHILD(tp->ctt_type) == child &&
+ CTF_TYPE_TO_INDEX(tp->ctt_type) <= fp->ctf_typemax)
+ fp->ctf_ptrtab[
+ CTF_TYPE_TO_INDEX(tp->ctt_type)] = id;
+ /*FALLTHRU*/
+
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ err = ctf_hash_insert(&fp->ctf_names, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ /*FALLTHRU*/
+
+ default:
+ vbytes = 0;
+ break;
+ }
+
+ *xp = (uint_t)((uintptr_t)tp - (uintptr_t)fp->ctf_buf);
+ tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
+ }
+
+ ctf_dprintf("%lu total types processed\n", fp->ctf_typemax);
+ ctf_dprintf("%u enum names hashed\n", ctf_hash_size(&fp->ctf_enums));
+ ctf_dprintf("%u struct names hashed (%d long)\n",
+ ctf_hash_size(&fp->ctf_structs), nlstructs);
+ ctf_dprintf("%u union names hashed (%d long)\n",
+ ctf_hash_size(&fp->ctf_unions), nlunions);
+ ctf_dprintf("%u base type names hashed\n",
+ ctf_hash_size(&fp->ctf_names));
+
+ /*
+ * Make an additional pass through the pointer table to find pointers
+ * that point to anonymous typedef nodes. If we find one, modify the
+ * pointer table so that the pointer is also known to point to the
+ * node that is referenced by the anonymous typedef node.
+ */
+ for (id = 1; id <= fp->ctf_typemax; id++) {
+ if ((dst = fp->ctf_ptrtab[id]) != 0) {
+ tp = LCTF_INDEX_TO_TYPEPTR(fp, id);
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_TYPEDEF &&
+ strcmp(ctf_strptr(fp, tp->ctt_name), "") == 0 &&
+ CTF_TYPE_ISCHILD(tp->ctt_type) == child &&
+ CTF_TYPE_TO_INDEX(tp->ctt_type) <= fp->ctf_typemax)
+ fp->ctf_ptrtab[
+ CTF_TYPE_TO_INDEX(tp->ctt_type)] = dst;
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Decode the specified CTF buffer and optional symbol table and create a new
+ * CTF container representing the symbolic debugging information. This code
+ * can be used directly by the debugger, or it can be used as the engine for
+ * ctf_fdopen() or ctf_open(), below.
+ */
+ctf_file_t *
+ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
+ const ctf_sect_t *strsect, int *errp)
+{
+ const ctf_preamble_t *pp;
+ ctf_header_t hp;
+ ctf_file_t *fp;
+ void *buf, *base;
+ size_t size, hdrsz;
+ int err;
+
+ if (ctfsect == NULL || ((symsect == NULL) != (strsect == NULL)))
+ return (ctf_set_open_errno(errp, EINVAL));
+
+ if (symsect != NULL && symsect->cts_entsize != sizeof (Elf32_Sym) &&
+ symsect->cts_entsize != sizeof (Elf64_Sym))
+ return (ctf_set_open_errno(errp, ECTF_SYMTAB));
+
+ if (symsect != NULL && symsect->cts_data == NULL)
+ return (ctf_set_open_errno(errp, ECTF_SYMBAD));
+
+ if (strsect != NULL && strsect->cts_data == NULL)
+ return (ctf_set_open_errno(errp, ECTF_STRBAD));
+
+ if (ctfsect->cts_size < sizeof (ctf_preamble_t))
+ return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
+
+ pp = (const ctf_preamble_t *)ctfsect->cts_data;
+
+ ctf_dprintf("ctf_bufopen: magic=0x%x version=%u\n",
+ pp->ctp_magic, pp->ctp_version);
+
+ /*
+ * Validate each part of the CTF header (either V1 or V2).
+ * First, we validate the preamble (common to all versions). At that
+ * point, we know specific header version, and can validate the
+ * version-specific parts including section offsets and alignments.
+ */
+ if (pp->ctp_magic != CTF_MAGIC)
+ return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
+
+ if (pp->ctp_version == CTF_VERSION_2) {
+ if (ctfsect->cts_size < sizeof (ctf_header_t))
+ return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
+
+ bcopy(ctfsect->cts_data, &hp, sizeof (hp));
+ hdrsz = sizeof (ctf_header_t);
+
+ } else if (pp->ctp_version == CTF_VERSION_1) {
+ const ctf_header_v1_t *h1p =
+ (const ctf_header_v1_t *)ctfsect->cts_data;
+
+ if (ctfsect->cts_size < sizeof (ctf_header_v1_t))
+ return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
+
+ bzero(&hp, sizeof (hp));
+ hp.cth_preamble = h1p->cth_preamble;
+ hp.cth_objtoff = h1p->cth_objtoff;
+ hp.cth_funcoff = h1p->cth_funcoff;
+ hp.cth_typeoff = h1p->cth_typeoff;
+ hp.cth_stroff = h1p->cth_stroff;
+ hp.cth_strlen = h1p->cth_strlen;
+
+ hdrsz = sizeof (ctf_header_v1_t);
+ } else
+ return (ctf_set_open_errno(errp, ECTF_CTFVERS));
+
+ size = hp.cth_stroff + hp.cth_strlen;
+
+ ctf_dprintf("ctf_bufopen: uncompressed size=%lu\n", (ulong_t)size);
+
+ if (hp.cth_lbloff > size || hp.cth_objtoff > size ||
+ hp.cth_funcoff > size || hp.cth_typeoff > size ||
+ hp.cth_stroff > size)
+ return (ctf_set_open_errno(errp, ECTF_CORRUPT));
+
+ if (hp.cth_lbloff > hp.cth_objtoff ||
+ hp.cth_objtoff > hp.cth_funcoff ||
+ hp.cth_funcoff > hp.cth_typeoff ||
+ hp.cth_typeoff > hp.cth_stroff)
+ return (ctf_set_open_errno(errp, ECTF_CORRUPT));
+
+ if ((hp.cth_lbloff & 3) || (hp.cth_objtoff & 1) ||
+ (hp.cth_funcoff & 1) || (hp.cth_typeoff & 3))
+ return (ctf_set_open_errno(errp, ECTF_CORRUPT));
+
+ /*
+ * Once everything is determined to be valid, attempt to decompress
+ * the CTF data buffer if it is compressed. Otherwise we just put
+ * the data section's buffer pointer into ctf_buf, below.
+ */
+ if (hp.cth_flags & CTF_F_COMPRESS) {
+ size_t srclen, dstlen;
+ const void *src;
+ int rc = Z_OK;
+
+ if (ctf_zopen(errp) == NULL)
+ return (NULL); /* errp is set for us */
+
+ if ((base = ctf_data_alloc(size + hdrsz)) == MAP_FAILED)
+ return (ctf_set_open_errno(errp, ECTF_ZALLOC));
+
+ bcopy(ctfsect->cts_data, base, hdrsz);
+ ((ctf_preamble_t *)base)->ctp_flags &= ~CTF_F_COMPRESS;
+ buf = (uchar_t *)base + hdrsz;
+
+ src = (uchar_t *)ctfsect->cts_data + hdrsz;
+ srclen = ctfsect->cts_size - hdrsz;
+ dstlen = size;
+
+ if ((rc = z_uncompress(buf, &dstlen, src, srclen)) != Z_OK) {
+ ctf_dprintf("zlib inflate err: %s\n", z_strerror(rc));
+ ctf_data_free(base, size + hdrsz);
+ return (ctf_set_open_errno(errp, ECTF_DECOMPRESS));
+ }
+
+ if (dstlen != size) {
+ ctf_dprintf("zlib inflate short -- got %lu of %lu "
+ "bytes\n", (ulong_t)dstlen, (ulong_t)size);
+ ctf_data_free(base, size + hdrsz);
+ return (ctf_set_open_errno(errp, ECTF_CORRUPT));
+ }
+
+ ctf_data_protect(base, size + hdrsz);
+
+ } else {
+ base = (void *)ctfsect->cts_data;
+ buf = (uchar_t *)base + hdrsz;
+ }
+
+ /*
+ * Once we have uncompressed and validated the CTF data buffer, we can
+ * proceed with allocating a ctf_file_t and initializing it.
+ */
+ if ((fp = ctf_alloc(sizeof (ctf_file_t))) == NULL)
+ return (ctf_set_open_errno(errp, EAGAIN));
+
+ bzero(fp, sizeof (ctf_file_t));
+ fp->ctf_version = hp.cth_version;
+ fp->ctf_fileops = &ctf_fileops[hp.cth_version];
+ bcopy(ctfsect, &fp->ctf_data, sizeof (ctf_sect_t));
+
+ if (symsect != NULL) {
+ bcopy(symsect, &fp->ctf_symtab, sizeof (ctf_sect_t));
+ bcopy(strsect, &fp->ctf_strtab, sizeof (ctf_sect_t));
+ }
+
+ if (fp->ctf_data.cts_name != NULL)
+ fp->ctf_data.cts_name = ctf_strdup(fp->ctf_data.cts_name);
+ if (fp->ctf_symtab.cts_name != NULL)
+ fp->ctf_symtab.cts_name = ctf_strdup(fp->ctf_symtab.cts_name);
+ if (fp->ctf_strtab.cts_name != NULL)
+ fp->ctf_strtab.cts_name = ctf_strdup(fp->ctf_strtab.cts_name);
+
+ if (fp->ctf_data.cts_name == NULL)
+ fp->ctf_data.cts_name = _CTF_NULLSTR;
+ if (fp->ctf_symtab.cts_name == NULL)
+ fp->ctf_symtab.cts_name = _CTF_NULLSTR;
+ if (fp->ctf_strtab.cts_name == NULL)
+ fp->ctf_strtab.cts_name = _CTF_NULLSTR;
+
+ fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *)buf + hp.cth_stroff;
+ fp->ctf_str[CTF_STRTAB_0].cts_len = hp.cth_strlen;
+
+ if (strsect != NULL) {
+ fp->ctf_str[CTF_STRTAB_1].cts_strs = strsect->cts_data;
+ fp->ctf_str[CTF_STRTAB_1].cts_len = strsect->cts_size;
+ }
+
+ fp->ctf_base = base;
+ fp->ctf_buf = buf;
+ fp->ctf_size = size + hdrsz;
+
+ /*
+ * If we have a parent container name and label, store the relocated
+ * string pointers in the CTF container for easy access later.
+ */
+ if (hp.cth_parlabel != 0)
+ fp->ctf_parlabel = ctf_strptr(fp, hp.cth_parlabel);
+ if (hp.cth_parname != 0)
+ fp->ctf_parname = ctf_strptr(fp, hp.cth_parname);
+
+ ctf_dprintf("ctf_bufopen: parent name %s (label %s)\n",
+ fp->ctf_parname ? fp->ctf_parname : "<NULL>",
+ fp->ctf_parlabel ? fp->ctf_parlabel : "<NULL>");
+
+ /*
+ * If we have a symbol table section, allocate and initialize
+ * the symtab translation table, pointed to by ctf_sxlate.
+ */
+ if (symsect != NULL) {
+ fp->ctf_nsyms = symsect->cts_size / symsect->cts_entsize;
+ fp->ctf_sxlate = ctf_alloc(fp->ctf_nsyms * sizeof (uint_t));
+
+ if (fp->ctf_sxlate == NULL) {
+ (void) ctf_set_open_errno(errp, EAGAIN);
+ goto bad;
+ }
+
+ if ((err = init_symtab(fp, &hp, symsect, strsect)) != 0) {
+ (void) ctf_set_open_errno(errp, err);
+ goto bad;
+ }
+ }
+
+ if ((err = init_types(fp, &hp)) != 0) {
+ (void) ctf_set_open_errno(errp, err);
+ goto bad;
+ }
+
+ /*
+ * Initialize the ctf_lookup_by_name top-level dictionary. We keep an
+ * array of type name prefixes and the corresponding ctf_hash to use.
+ * NOTE: This code must be kept in sync with the code in ctf_update().
+ */
+ fp->ctf_lookups[0].ctl_prefix = "struct";
+ fp->ctf_lookups[0].ctl_len = strlen(fp->ctf_lookups[0].ctl_prefix);
+ fp->ctf_lookups[0].ctl_hash = &fp->ctf_structs;
+ fp->ctf_lookups[1].ctl_prefix = "union";
+ fp->ctf_lookups[1].ctl_len = strlen(fp->ctf_lookups[1].ctl_prefix);
+ fp->ctf_lookups[1].ctl_hash = &fp->ctf_unions;
+ fp->ctf_lookups[2].ctl_prefix = "enum";
+ fp->ctf_lookups[2].ctl_len = strlen(fp->ctf_lookups[2].ctl_prefix);
+ fp->ctf_lookups[2].ctl_hash = &fp->ctf_enums;
+ fp->ctf_lookups[3].ctl_prefix = _CTF_NULLSTR;
+ fp->ctf_lookups[3].ctl_len = strlen(fp->ctf_lookups[3].ctl_prefix);
+ fp->ctf_lookups[3].ctl_hash = &fp->ctf_names;
+ fp->ctf_lookups[4].ctl_prefix = NULL;
+ fp->ctf_lookups[4].ctl_len = 0;
+ fp->ctf_lookups[4].ctl_hash = NULL;
+
+ if (symsect != NULL) {
+ if (symsect->cts_entsize == sizeof (Elf64_Sym))
+ (void) ctf_setmodel(fp, CTF_MODEL_LP64);
+ else
+ (void) ctf_setmodel(fp, CTF_MODEL_ILP32);
+ } else
+ (void) ctf_setmodel(fp, CTF_MODEL_NATIVE);
+
+ fp->ctf_refcnt = 1;
+ return (fp);
+
+bad:
+ ctf_close(fp);
+ return (NULL);
+}
+
+/*
+ * Close the specified CTF container and free associated data structures. Note
+ * that ctf_close() is a reference counted operation: if the specified file is
+ * the parent of other active containers, its reference count will be greater
+ * than one and it will be freed later when no active children exist.
+ */
+void
+ctf_close(ctf_file_t *fp)
+{
+ ctf_dtdef_t *dtd, *ntd;
+
+ if (fp == NULL)
+ return; /* allow ctf_close(NULL) to simplify caller code */
+
+ ctf_dprintf("ctf_close(%p) refcnt=%u\n", (void *)fp, fp->ctf_refcnt);
+
+ if (fp->ctf_refcnt > 1) {
+ fp->ctf_refcnt--;
+ return;
+ }
+
+ if (fp->ctf_parent != NULL)
+ ctf_close(fp->ctf_parent);
+
+ for (dtd = ctf_list_next(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) {
+ ntd = ctf_list_next(dtd);
+ ctf_dtd_delete(fp, dtd);
+ }
+
+ ctf_free(fp->ctf_dthash, fp->ctf_dthashlen * sizeof (ctf_dtdef_t *));
+
+ if (fp->ctf_flags & LCTF_MMAP) {
+ if (fp->ctf_data.cts_data != NULL)
+ ctf_sect_munmap(&fp->ctf_data);
+ if (fp->ctf_symtab.cts_data != NULL)
+ ctf_sect_munmap(&fp->ctf_symtab);
+ if (fp->ctf_strtab.cts_data != NULL)
+ ctf_sect_munmap(&fp->ctf_strtab);
+ }
+
+ if (fp->ctf_data.cts_name != _CTF_NULLSTR &&
+ fp->ctf_data.cts_name != NULL) {
+ ctf_free((char *)fp->ctf_data.cts_name,
+ strlen(fp->ctf_data.cts_name) + 1);
+ }
+
+ if (fp->ctf_symtab.cts_name != _CTF_NULLSTR &&
+ fp->ctf_symtab.cts_name != NULL) {
+ ctf_free((char *)fp->ctf_symtab.cts_name,
+ strlen(fp->ctf_symtab.cts_name) + 1);
+ }
+
+ if (fp->ctf_strtab.cts_name != _CTF_NULLSTR &&
+ fp->ctf_strtab.cts_name != NULL) {
+ ctf_free((char *)fp->ctf_strtab.cts_name,
+ strlen(fp->ctf_strtab.cts_name) + 1);
+ }
+
+ if (fp->ctf_base != fp->ctf_data.cts_data && fp->ctf_base != NULL)
+ ctf_data_free((void *)fp->ctf_base, fp->ctf_size);
+
+ if (fp->ctf_sxlate != NULL)
+ ctf_free(fp->ctf_sxlate, sizeof (uint_t) * fp->ctf_nsyms);
+
+ if (fp->ctf_txlate != NULL) {
+ ctf_free(fp->ctf_txlate,
+ sizeof (uint_t) * (fp->ctf_typemax + 1));
+ }
+
+ if (fp->ctf_ptrtab != NULL) {
+ ctf_free(fp->ctf_ptrtab,
+ sizeof (ushort_t) * (fp->ctf_typemax + 1));
+ }
+
+ ctf_hash_destroy(&fp->ctf_structs);
+ ctf_hash_destroy(&fp->ctf_unions);
+ ctf_hash_destroy(&fp->ctf_enums);
+ ctf_hash_destroy(&fp->ctf_names);
+
+ ctf_free(fp, sizeof (ctf_file_t));
+}
+
+/*
+ * Return the CTF handle for the parent CTF container, if one exists.
+ * Otherwise return NULL to indicate this container has no imported parent.
+ */
+ctf_file_t *
+ctf_parent_file(ctf_file_t *fp)
+{
+ return (fp->ctf_parent);
+}
+
+/*
+ * Return the name of the parent CTF container, if one exists. Otherwise
+ * return NULL to indicate this container is a root container.
+ */
+const char *
+ctf_parent_name(ctf_file_t *fp)
+{
+ return (fp->ctf_parname);
+}
+
+/*
+ * Import the types from the specified parent container by storing a pointer
+ * to it in ctf_parent and incrementing its reference count. Only one parent
+ * is allowed: if a parent already exists, it is replaced by the new parent.
+ */
+int
+ctf_import(ctf_file_t *fp, ctf_file_t *pfp)
+{
+ if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0))
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel)
+ return (ctf_set_errno(fp, ECTF_DMODEL));
+
+ if (fp->ctf_parent != NULL)
+ ctf_close(fp->ctf_parent);
+
+ if (pfp != NULL) {
+ fp->ctf_flags |= LCTF_CHILD;
+ pfp->ctf_refcnt++;
+ }
+
+ fp->ctf_parent = pfp;
+ return (0);
+}
+
+/*
+ * Set the data model constant for the CTF container.
+ */
+int
+ctf_setmodel(ctf_file_t *fp, int model)
+{
+ const ctf_dmodel_t *dp;
+
+ for (dp = _libctf_models; dp->ctd_name != NULL; dp++) {
+ if (dp->ctd_code == model) {
+ fp->ctf_dmodel = dp;
+ return (0);
+ }
+ }
+
+ return (ctf_set_errno(fp, EINVAL));
+}
+
+/*
+ * Return the data model constant for the CTF container.
+ */
+int
+ctf_getmodel(ctf_file_t *fp)
+{
+ return (fp->ctf_dmodel->ctd_code);
+}
+
+void
+ctf_setspecific(ctf_file_t *fp, void *data)
+{
+ fp->ctf_specific = data;
+}
+
+void *
+ctf_getspecific(ctf_file_t *fp)
+{
+ return (fp->ctf_specific);
+}
diff --git a/common/ctf/ctf_types.c b/common/ctf/ctf_types.c
new file mode 100644
index 000000000000..290c518ae72b
--- /dev/null
+++ b/common/ctf/ctf_types.c
@@ -0,0 +1,845 @@
+/*
+ * 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 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <ctf_impl.h>
+
+ssize_t
+ctf_get_ctt_size(const ctf_file_t *fp, const ctf_type_t *tp, ssize_t *sizep,
+ ssize_t *incrementp)
+{
+ ssize_t size, increment;
+
+ if (fp->ctf_version > CTF_VERSION_1 &&
+ tp->ctt_size == CTF_LSIZE_SENT) {
+ size = CTF_TYPE_LSIZE(tp);
+ increment = sizeof (ctf_type_t);
+ } else {
+ size = tp->ctt_size;
+ increment = sizeof (ctf_stype_t);
+ }
+
+ if (sizep)
+ *sizep = size;
+ if (incrementp)
+ *incrementp = increment;
+
+ return (size);
+}
+
+/*
+ * Iterate over the members of a STRUCT or UNION. We pass the name, member
+ * type, and offset of each member to the specified callback function.
+ */
+int
+ctf_member_iter(ctf_file_t *fp, ctf_id_t type, ctf_member_f *func, void *arg)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ ssize_t size, increment;
+ uint_t kind, n;
+ int rc;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+ kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (ctf_set_errno(ofp, ECTF_NOTSOU));
+
+ if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
+ const ctf_member_t *mp = (const ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
+ const char *name = ctf_strptr(fp, mp->ctm_name);
+ if ((rc = func(name, mp->ctm_type, mp->ctm_offset,
+ arg)) != 0)
+ return (rc);
+ }
+
+ } else {
+ const ctf_lmember_t *lmp = (const ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
+ const char *name = ctf_strptr(fp, lmp->ctlm_name);
+ if ((rc = func(name, lmp->ctlm_type,
+ (ulong_t)CTF_LMEM_OFFSET(lmp), arg)) != 0)
+ return (rc);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Iterate over the members of an ENUM. We pass the string name and associated
+ * integer value of each enum element to the specified callback function.
+ */
+int
+ctf_enum_iter(ctf_file_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ctf_enum_t *ep;
+ ssize_t increment;
+ uint_t n;
+ int rc;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM)
+ return (ctf_set_errno(ofp, ECTF_NOTENUM));
+
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+
+ ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
+ const char *name = ctf_strptr(fp, ep->cte_name);
+ if ((rc = func(name, ep->cte_value, arg)) != 0)
+ return (rc);
+ }
+
+ return (0);
+}
+
+/*
+ * Iterate over every root (user-visible) type in the given CTF container.
+ * We pass the type ID of each type to the specified callback function.
+ */
+int
+ctf_type_iter(ctf_file_t *fp, ctf_type_f *func, void *arg)
+{
+ ctf_id_t id, max = fp->ctf_typemax;
+ int rc, child = (fp->ctf_flags & LCTF_CHILD);
+
+ for (id = 1; id <= max; id++) {
+ const ctf_type_t *tp = LCTF_INDEX_TO_TYPEPTR(fp, id);
+ if (CTF_INFO_ISROOT(tp->ctt_info) &&
+ (rc = func(CTF_INDEX_TO_TYPE(id, child), arg)) != 0)
+ return (rc);
+ }
+
+ return (0);
+}
+
+/*
+ * Follow a given type through the graph for TYPEDEF, VOLATILE, CONST, and
+ * RESTRICT nodes until we reach a "base" type node. This is useful when
+ * we want to follow a type ID to a node that has members or a size. To guard
+ * against infinite loops, we implement simplified cycle detection and check
+ * each link against itself, the previous node, and the topmost node.
+ */
+ctf_id_t
+ctf_type_resolve(ctf_file_t *fp, ctf_id_t type)
+{
+ ctf_id_t prev = type, otype = type;
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+
+ while ((tp = ctf_lookup_by_id(&fp, type)) != NULL) {
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ if (tp->ctt_type == type || tp->ctt_type == otype ||
+ tp->ctt_type == prev) {
+ ctf_dprintf("type %ld cycle detected\n", otype);
+ return (ctf_set_errno(ofp, ECTF_CORRUPT));
+ }
+ prev = type;
+ type = tp->ctt_type;
+ break;
+ default:
+ return (type);
+ }
+ }
+
+ return (CTF_ERR); /* errno is set for us */
+}
+
+/*
+ * Lookup the given type ID and print a string name for it into buf. Return
+ * the actual number of bytes (not including \0) needed to format the name.
+ */
+ssize_t
+ctf_type_lname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
+{
+ ctf_decl_t cd;
+ ctf_decl_node_t *cdp;
+ ctf_decl_prec_t prec, lp, rp;
+ int ptr, arr;
+ uint_t k;
+
+ if (fp == NULL && type == CTF_ERR)
+ return (-1); /* simplify caller code by permitting CTF_ERR */
+
+ ctf_decl_init(&cd, buf, len);
+ ctf_decl_push(&cd, fp, type);
+
+ if (cd.cd_err != 0) {
+ ctf_decl_fini(&cd);
+ return (ctf_set_errno(fp, cd.cd_err));
+ }
+
+ /*
+ * If the type graph's order conflicts with lexical precedence order
+ * for pointers or arrays, then we need to surround the declarations at
+ * the corresponding lexical precedence with parentheses. This can
+ * result in either a parenthesized pointer (*) as in int (*)() or
+ * int (*)[], or in a parenthesized pointer and array as in int (*[])().
+ */
+ ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER;
+ arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY;
+
+ rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1;
+ lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1;
+
+ k = CTF_K_POINTER; /* avoid leading whitespace (see below) */
+
+ for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) {
+ for (cdp = ctf_list_next(&cd.cd_nodes[prec]);
+ cdp != NULL; cdp = ctf_list_next(cdp)) {
+
+ ctf_file_t *rfp = fp;
+ const ctf_type_t *tp =
+ ctf_lookup_by_id(&rfp, cdp->cd_type);
+ const char *name = ctf_strptr(rfp, tp->ctt_name);
+
+ if (k != CTF_K_POINTER && k != CTF_K_ARRAY)
+ ctf_decl_sprintf(&cd, " ");
+
+ if (lp == prec) {
+ ctf_decl_sprintf(&cd, "(");
+ lp = -1;
+ }
+
+ switch (cdp->cd_kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ case CTF_K_TYPEDEF:
+ ctf_decl_sprintf(&cd, "%s", name);
+ break;
+ case CTF_K_POINTER:
+ ctf_decl_sprintf(&cd, "*");
+ break;
+ case CTF_K_ARRAY:
+ ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n);
+ break;
+ case CTF_K_FUNCTION:
+ ctf_decl_sprintf(&cd, "()");
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_FORWARD:
+ ctf_decl_sprintf(&cd, "struct %s", name);
+ break;
+ case CTF_K_UNION:
+ ctf_decl_sprintf(&cd, "union %s", name);
+ break;
+ case CTF_K_ENUM:
+ ctf_decl_sprintf(&cd, "enum %s", name);
+ break;
+ case CTF_K_VOLATILE:
+ ctf_decl_sprintf(&cd, "volatile");
+ break;
+ case CTF_K_CONST:
+ ctf_decl_sprintf(&cd, "const");
+ break;
+ case CTF_K_RESTRICT:
+ ctf_decl_sprintf(&cd, "restrict");
+ break;
+ }
+
+ k = cdp->cd_kind;
+ }
+
+ if (rp == prec)
+ ctf_decl_sprintf(&cd, ")");
+ }
+
+ if (cd.cd_len >= len)
+ (void) ctf_set_errno(fp, ECTF_NAMELEN);
+
+ ctf_decl_fini(&cd);
+ return (cd.cd_len);
+}
+
+/*
+ * Lookup the given type ID and print a string name for it into buf. If buf
+ * is too small, return NULL: the ECTF_NAMELEN error is set on 'fp' for us.
+ */
+char *
+ctf_type_name(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
+{
+ ssize_t rv = ctf_type_lname(fp, type, buf, len);
+ return (rv >= 0 && rv < len ? buf : NULL);
+}
+
+/*
+ * Resolve the type down to a base type node, and then return the size
+ * of the type storage in bytes.
+ */
+ssize_t
+ctf_type_size(ctf_file_t *fp, ctf_id_t type)
+{
+ const ctf_type_t *tp;
+ ssize_t size;
+ ctf_arinfo_t ar;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (-1); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (-1); /* errno is set for us */
+
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_POINTER:
+ return (fp->ctf_dmodel->ctd_pointer);
+
+ case CTF_K_FUNCTION:
+ return (0); /* function size is only known by symtab */
+
+ case CTF_K_ENUM:
+ return (fp->ctf_dmodel->ctd_int);
+
+ case CTF_K_ARRAY:
+ /*
+ * Array size is not directly returned by stabs data. Instead,
+ * it defines the element type and requires the user to perform
+ * the multiplication. If ctf_get_ctt_size() returns zero, the
+ * current version of ctfconvert does not compute member sizes
+ * and we compute the size here on its behalf.
+ */
+ if ((size = ctf_get_ctt_size(fp, tp, NULL, NULL)) > 0)
+ return (size);
+
+ if (ctf_array_info(fp, type, &ar) == CTF_ERR ||
+ (size = ctf_type_size(fp, ar.ctr_contents)) == CTF_ERR)
+ return (-1); /* errno is set for us */
+
+ return (size * ar.ctr_nelems);
+
+ default:
+ return (ctf_get_ctt_size(fp, tp, NULL, NULL));
+ }
+}
+
+/*
+ * Resolve the type down to a base type node, and then return the alignment
+ * needed for the type storage in bytes.
+ */
+ssize_t
+ctf_type_align(ctf_file_t *fp, ctf_id_t type)
+{
+ const ctf_type_t *tp;
+ ctf_arinfo_t r;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (-1); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (-1); /* errno is set for us */
+
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_POINTER:
+ case CTF_K_FUNCTION:
+ return (fp->ctf_dmodel->ctd_pointer);
+
+ case CTF_K_ARRAY:
+ if (ctf_array_info(fp, type, &r) == CTF_ERR)
+ return (-1); /* errno is set for us */
+ return (ctf_type_align(fp, r.ctr_contents));
+
+ case CTF_K_STRUCT:
+ case CTF_K_UNION: {
+ uint_t n = LCTF_INFO_VLEN(fp, tp->ctt_info);
+ ssize_t size, increment;
+ size_t align = 0;
+ const void *vmp;
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+ vmp = (uchar_t *)tp + increment;
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_STRUCT)
+ n = MIN(n, 1); /* only use first member for structs */
+
+ if (fp->ctf_version == CTF_VERSION_1 ||
+ size < CTF_LSTRUCT_THRESH) {
+ const ctf_member_t *mp = vmp;
+ for (; n != 0; n--, mp++) {
+ ssize_t am = ctf_type_align(fp, mp->ctm_type);
+ align = MAX(align, am);
+ }
+ } else {
+ const ctf_lmember_t *lmp = vmp;
+ for (; n != 0; n--, lmp++) {
+ ssize_t am = ctf_type_align(fp, lmp->ctlm_type);
+ align = MAX(align, am);
+ }
+ }
+
+ return (align);
+ }
+
+ case CTF_K_ENUM:
+ return (fp->ctf_dmodel->ctd_int);
+
+ default:
+ return (ctf_get_ctt_size(fp, tp, NULL, NULL));
+ }
+}
+
+/*
+ * Return the kind (CTF_K_* constant) for the specified type ID.
+ */
+int
+ctf_type_kind(ctf_file_t *fp, ctf_id_t type)
+{
+ const ctf_type_t *tp;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ return (LCTF_INFO_KIND(fp, tp->ctt_info));
+}
+
+/*
+ * If the type is one that directly references another type (such as POINTER),
+ * then return the ID of the type to which it refers.
+ */
+ctf_id_t
+ctf_type_reference(ctf_file_t *fp, ctf_id_t type)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ return (tp->ctt_type);
+ default:
+ return (ctf_set_errno(ofp, ECTF_NOTREF));
+ }
+}
+
+/*
+ * Find a pointer to type by looking in fp->ctf_ptrtab. If we can't find a
+ * pointer to the given type, see if we can compute a pointer to the type
+ * resulting from resolving the type down to its base type and use that
+ * instead. This helps with cases where the CTF data includes "struct foo *"
+ * but not "foo_t *" and the user accesses "foo_t *" in the debugger.
+ */
+ctf_id_t
+ctf_type_pointer(ctf_file_t *fp, ctf_id_t type)
+{
+ ctf_file_t *ofp = fp;
+ ctf_id_t ntype;
+
+ if (ctf_lookup_by_id(&fp, type) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]) != 0)
+ return (CTF_INDEX_TO_TYPE(ntype, (fp->ctf_flags & LCTF_CHILD)));
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (ctf_set_errno(ofp, ECTF_NOTYPE));
+
+ if (ctf_lookup_by_id(&fp, type) == NULL)
+ return (ctf_set_errno(ofp, ECTF_NOTYPE));
+
+ if ((ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]) != 0)
+ return (CTF_INDEX_TO_TYPE(ntype, (fp->ctf_flags & LCTF_CHILD)));
+
+ return (ctf_set_errno(ofp, ECTF_NOTYPE));
+}
+
+/*
+ * Return the encoding for the specified INTEGER or FLOAT.
+ */
+int
+ctf_type_encoding(ctf_file_t *fp, ctf_id_t type, ctf_encoding_t *ep)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ ssize_t increment;
+ uint_t data;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_INTEGER:
+ data = *(const uint_t *)((uintptr_t)tp + increment);
+ ep->cte_format = CTF_INT_ENCODING(data);
+ ep->cte_offset = CTF_INT_OFFSET(data);
+ ep->cte_bits = CTF_INT_BITS(data);
+ break;
+ case CTF_K_FLOAT:
+ data = *(const uint_t *)((uintptr_t)tp + increment);
+ ep->cte_format = CTF_FP_ENCODING(data);
+ ep->cte_offset = CTF_FP_OFFSET(data);
+ ep->cte_bits = CTF_FP_BITS(data);
+ break;
+ default:
+ return (ctf_set_errno(ofp, ECTF_NOTINTFP));
+ }
+
+ return (0);
+}
+
+int
+ctf_type_cmp(ctf_file_t *lfp, ctf_id_t ltype, ctf_file_t *rfp, ctf_id_t rtype)
+{
+ int rval;
+
+ if (ltype < rtype)
+ rval = -1;
+ else if (ltype > rtype)
+ rval = 1;
+ else
+ rval = 0;
+
+ if (lfp == rfp)
+ return (rval);
+
+ if (CTF_TYPE_ISPARENT(ltype) && lfp->ctf_parent != NULL)
+ lfp = lfp->ctf_parent;
+
+ if (CTF_TYPE_ISPARENT(rtype) && rfp->ctf_parent != NULL)
+ rfp = rfp->ctf_parent;
+
+ if (lfp < rfp)
+ return (-1);
+
+ if (lfp > rfp)
+ return (1);
+
+ return (rval);
+}
+
+/*
+ * Return a boolean value indicating if two types are compatible integers or
+ * floating-pointer values. This function returns true if the two types are
+ * the same, or if they have the same ASCII name and encoding properties.
+ * This function could be extended to test for compatibility for other kinds.
+ */
+int
+ctf_type_compat(ctf_file_t *lfp, ctf_id_t ltype,
+ ctf_file_t *rfp, ctf_id_t rtype)
+{
+ const ctf_type_t *ltp, *rtp;
+ ctf_encoding_t le, re;
+ ctf_arinfo_t la, ra;
+ uint_t lkind, rkind;
+
+ if (ctf_type_cmp(lfp, ltype, rfp, rtype) == 0)
+ return (1);
+
+ ltype = ctf_type_resolve(lfp, ltype);
+ lkind = ctf_type_kind(lfp, ltype);
+
+ rtype = ctf_type_resolve(rfp, rtype);
+ rkind = ctf_type_kind(rfp, rtype);
+
+ if (lkind != rkind ||
+ (ltp = ctf_lookup_by_id(&lfp, ltype)) == NULL ||
+ (rtp = ctf_lookup_by_id(&rfp, rtype)) == NULL ||
+ strcmp(ctf_strptr(lfp, ltp->ctt_name),
+ ctf_strptr(rfp, rtp->ctt_name)) != 0)
+ return (0);
+
+ switch (lkind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ return (ctf_type_encoding(lfp, ltype, &le) == 0 &&
+ ctf_type_encoding(rfp, rtype, &re) == 0 &&
+ bcmp(&le, &re, sizeof (ctf_encoding_t)) == 0);
+ case CTF_K_POINTER:
+ return (ctf_type_compat(lfp, ctf_type_reference(lfp, ltype),
+ rfp, ctf_type_reference(rfp, rtype)));
+ case CTF_K_ARRAY:
+ return (ctf_array_info(lfp, ltype, &la) == 0 &&
+ ctf_array_info(rfp, rtype, &ra) == 0 &&
+ la.ctr_nelems == ra.ctr_nelems && ctf_type_compat(
+ lfp, la.ctr_contents, rfp, ra.ctr_contents) &&
+ ctf_type_compat(lfp, la.ctr_index, rfp, ra.ctr_index));
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ return (ctf_type_size(lfp, ltype) == ctf_type_size(rfp, rtype));
+ case CTF_K_ENUM:
+ case CTF_K_FORWARD:
+ return (1); /* no other checks required for these type kinds */
+ default:
+ return (0); /* should not get here since we did a resolve */
+ }
+}
+
+/*
+ * Return the type and offset for a given member of a STRUCT or UNION.
+ */
+int
+ctf_member_info(ctf_file_t *fp, ctf_id_t type, const char *name,
+ ctf_membinfo_t *mip)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ ssize_t size, increment;
+ uint_t kind, n;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+ kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (ctf_set_errno(ofp, ECTF_NOTSOU));
+
+ if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
+ const ctf_member_t *mp = (const ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
+ if (strcmp(ctf_strptr(fp, mp->ctm_name), name) == 0) {
+ mip->ctm_type = mp->ctm_type;
+ mip->ctm_offset = mp->ctm_offset;
+ return (0);
+ }
+ }
+ } else {
+ const ctf_lmember_t *lmp = (const ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
+ if (strcmp(ctf_strptr(fp, lmp->ctlm_name), name) == 0) {
+ mip->ctm_type = lmp->ctlm_type;
+ mip->ctm_offset = (ulong_t)CTF_LMEM_OFFSET(lmp);
+ return (0);
+ }
+ }
+ }
+
+ return (ctf_set_errno(ofp, ECTF_NOMEMBNAM));
+}
+
+/*
+ * Return the array type, index, and size information for the specified ARRAY.
+ */
+int
+ctf_array_info(ctf_file_t *fp, ctf_id_t type, ctf_arinfo_t *arp)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ctf_array_t *ap;
+ ssize_t increment;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ARRAY)
+ return (ctf_set_errno(ofp, ECTF_NOTARRAY));
+
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+
+ ap = (const ctf_array_t *)((uintptr_t)tp + increment);
+ arp->ctr_contents = ap->cta_contents;
+ arp->ctr_index = ap->cta_index;
+ arp->ctr_nelems = ap->cta_nelems;
+
+ return (0);
+}
+
+/*
+ * Convert the specified value to the corresponding enum member name, if a
+ * matching name can be found. Otherwise NULL is returned.
+ */
+const char *
+ctf_enum_name(ctf_file_t *fp, ctf_id_t type, int value)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ctf_enum_t *ep;
+ ssize_t increment;
+ uint_t n;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (NULL); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (NULL); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) {
+ (void) ctf_set_errno(ofp, ECTF_NOTENUM);
+ return (NULL);
+ }
+
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+
+ ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
+ if (ep->cte_value == value)
+ return (ctf_strptr(fp, ep->cte_name));
+ }
+
+ (void) ctf_set_errno(ofp, ECTF_NOENUMNAM);
+ return (NULL);
+}
+
+/*
+ * Convert the specified enum tag name to the corresponding value, if a
+ * matching name can be found. Otherwise CTF_ERR is returned.
+ */
+int
+ctf_enum_value(ctf_file_t *fp, ctf_id_t type, const char *name, int *valp)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ctf_enum_t *ep;
+ ssize_t size, increment;
+ uint_t n;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) {
+ (void) ctf_set_errno(ofp, ECTF_NOTENUM);
+ return (CTF_ERR);
+ }
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+
+ ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
+ if (strcmp(ctf_strptr(fp, ep->cte_name), name) == 0) {
+ if (valp != NULL)
+ *valp = ep->cte_value;
+ return (0);
+ }
+ }
+
+ (void) ctf_set_errno(ofp, ECTF_NOENUMNAM);
+ return (CTF_ERR);
+}
+
+/*
+ * Recursively visit the members of any type. This function is used as the
+ * engine for ctf_type_visit, below. We resolve the input type, recursively
+ * invoke ourself for each type member if the type is a struct or union, and
+ * then invoke the callback function on the current type. If any callback
+ * returns non-zero, we abort and percolate the error code back up to the top.
+ */
+static int
+ctf_type_rvisit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg,
+ const char *name, ulong_t offset, int depth)
+{
+ ctf_id_t otype = type;
+ const ctf_type_t *tp;
+ ssize_t size, increment;
+ uint_t kind, n;
+ int rc;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((rc = func(name, otype, offset, depth, arg)) != 0)
+ return (rc);
+
+ kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (0);
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+
+ if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
+ const ctf_member_t *mp = (const ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
+ if ((rc = ctf_type_rvisit(fp, mp->ctm_type,
+ func, arg, ctf_strptr(fp, mp->ctm_name),
+ offset + mp->ctm_offset, depth + 1)) != 0)
+ return (rc);
+ }
+
+ } else {
+ const ctf_lmember_t *lmp = (const ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
+ if ((rc = ctf_type_rvisit(fp, lmp->ctlm_type,
+ func, arg, ctf_strptr(fp, lmp->ctlm_name),
+ offset + (ulong_t)CTF_LMEM_OFFSET(lmp),
+ depth + 1)) != 0)
+ return (rc);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Recursively visit the members of any type. We pass the name, member
+ * type, and offset of each member to the specified callback function.
+ */
+int
+ctf_type_visit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg)
+{
+ return (ctf_type_rvisit(fp, type, func, arg, "", 0, 0));
+}
diff --git a/common/ctf/ctf_util.c b/common/ctf/ctf_util.c
new file mode 100644
index 000000000000..740d403e8c52
--- /dev/null
+++ b/common/ctf/ctf_util.c
@@ -0,0 +1,152 @@
+/*
+ * 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"
+
+#include <ctf_impl.h>
+
+/*
+ * Simple doubly-linked list append routine. This implementation assumes that
+ * each list element contains an embedded ctf_list_t as the first member.
+ * An additional ctf_list_t is used to store the head (l_next) and tail
+ * (l_prev) pointers. The current head and tail list elements have their
+ * previous and next pointers set to NULL, respectively.
+ */
+void
+ctf_list_append(ctf_list_t *lp, void *new)
+{
+ ctf_list_t *p = lp->l_prev; /* p = tail list element */
+ ctf_list_t *q = new; /* q = new list element */
+
+ lp->l_prev = q;
+ q->l_prev = p;
+ q->l_next = NULL;
+
+ if (p != NULL)
+ p->l_next = q;
+ else
+ lp->l_next = q;
+}
+
+/*
+ * Prepend the specified existing element to the given ctf_list_t. The
+ * existing pointer should be pointing at a struct with embedded ctf_list_t.
+ */
+void
+ctf_list_prepend(ctf_list_t *lp, void *new)
+{
+ ctf_list_t *p = new; /* p = new list element */
+ ctf_list_t *q = lp->l_next; /* q = head list element */
+
+ lp->l_next = p;
+ p->l_prev = NULL;
+ p->l_next = q;
+
+ if (q != NULL)
+ q->l_prev = p;
+ else
+ lp->l_prev = p;
+}
+
+/*
+ * Delete the specified existing element from the given ctf_list_t. The
+ * existing pointer should be pointing at a struct with embedded ctf_list_t.
+ */
+void
+ctf_list_delete(ctf_list_t *lp, void *existing)
+{
+ ctf_list_t *p = existing;
+
+ if (p->l_prev != NULL)
+ p->l_prev->l_next = p->l_next;
+ else
+ lp->l_next = p->l_next;
+
+ if (p->l_next != NULL)
+ p->l_next->l_prev = p->l_prev;
+ else
+ lp->l_prev = p->l_prev;
+}
+
+/*
+ * Convert an encoded CTF string name into a pointer to a C string by looking
+ * up the appropriate string table buffer and then adding the offset.
+ */
+const char *
+ctf_strraw(ctf_file_t *fp, uint_t name)
+{
+ ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID(name)];
+
+ if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET(name) < ctsp->cts_len)
+ return (ctsp->cts_strs + CTF_NAME_OFFSET(name));
+
+ /* string table not loaded or corrupt offset */
+ return (NULL);
+}
+
+const char *
+ctf_strptr(ctf_file_t *fp, uint_t name)
+{
+ const char *s = ctf_strraw(fp, name);
+ return (s != NULL ? s : "(?)");
+}
+
+/*
+ * Same strdup(3C), but use ctf_alloc() to do the memory allocation.
+ */
+char *
+ctf_strdup(const char *s1)
+{
+ char *s2 = ctf_alloc(strlen(s1) + 1);
+
+ if (s2 != NULL)
+ (void) strcpy(s2, s1);
+
+ return (s2);
+}
+
+/*
+ * Store the specified error code into errp if it is non-NULL, and then
+ * return NULL for the benefit of the caller.
+ */
+ctf_file_t *
+ctf_set_open_errno(int *errp, int error)
+{
+ if (errp != NULL)
+ *errp = error;
+ return (NULL);
+}
+
+/*
+ * Store the specified error code into the CTF container, and then return
+ * CTF_ERR for the benefit of the caller.
+ */
+long
+ctf_set_errno(ctf_file_t *fp, int err)
+{
+ fp->ctf_errno = err;
+ return (CTF_ERR);
+}
diff --git a/head/nlist.h b/head/nlist.h
new file mode 100644
index 000000000000..ea1bd203bb31
--- /dev/null
+++ b/head/nlist.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 (c) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#ifndef _NLIST_H
+#define _NLIST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.8.2.4 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct nlist {
+ char *n_name; /* symbol name */
+ long n_value; /* value of symbol */
+ short n_scnum; /* section number */
+ unsigned short n_type; /* type and derived type */
+ char n_sclass; /* storage class */
+ char n_numaux; /* number of aux. entries */
+};
+
+#if defined(__STDC__)
+extern int nlist(const char *, struct nlist *);
+#else /* __STDC__ */
+extern int nlist();
+#endif /* __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NLIST_H */
diff --git a/head/note.h b/head/note.h
new file mode 100644
index 000000000000..6c73867a0275
--- /dev/null
+++ b/head/note.h
@@ -0,0 +1,55 @@
+/*
+ * 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) 1994 by Sun Microsystems, Inc.
+ */
+
+/*
+ * note.h: interface for annotating source with info for tools
+ *
+ * NOTE is the default interface, but if the identifier NOTE is in use for
+ * some other purpose, you may prepare a similar header file using your own
+ * identifier, mapping that identifier to _NOTE. Also, exported header
+ * files should *not* use NOTE, since the name may already be in use in
+ * a program's namespace. Rather, exported header files should include
+ * sys/note.h directly and use _NOTE. For consistency, all kernel source
+ * should use _NOTE.
+ */
+
+#ifndef _NOTE_H
+#define _NOTE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/note.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NOTE _NOTE
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NOTE_H */
diff --git a/head/storclass.h b/head/storclass.h
new file mode 100644
index 000000000000..3cbfb8e48fbf
--- /dev/null
+++ b/head/storclass.h
@@ -0,0 +1,79 @@
+/*
+ * 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) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#ifndef _STORCLASS_H
+#define _STORCLASS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 1.6 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * STORAGE CLASSES
+ */
+
+#define C_EFCN -1 /* physical end of function */
+#define C_NULL 0
+#define C_AUTO 1 /* automatic variable */
+#define C_EXT 2 /* external symbol */
+#define C_STAT 3 /* static */
+#define C_REG 4 /* register variable */
+#define C_EXTDEF 5 /* external definition */
+#define C_LABEL 6 /* label */
+#define C_ULABEL 7 /* undefined label */
+#define C_MOS 8 /* member of structure */
+#define C_ARG 9 /* function argument */
+#define C_STRTAG 10 /* structure tag */
+#define C_MOU 11 /* member of union */
+#define C_UNTAG 12 /* union tag */
+#define C_TPDEF 13 /* type definition */
+#define C_USTATIC 14 /* undefined static */
+#define C_ENTAG 15 /* enumeration tag */
+#define C_MOE 16 /* member of enumeration */
+#define C_REGPARM 17 /* register parameter */
+#define C_FIELD 18 /* bit field */
+#define C_BLOCK 100 /* ".bb" or ".eb" */
+#define C_FCN 101 /* ".bf" or ".ef" */
+#define C_EOS 102 /* end of structure */
+#define C_FILE 103 /* file name */
+
+/*
+ * The following storage class is a "dummy" used only by STS
+ * for line number entries reformatted as symbol table entries
+ */
+
+#define C_LINE 104
+#define C_ALIAS 105 /* duplicate tag */
+#define C_HIDDEN 106 /* special storage class for external */
+ /* symbols in dmert public libraries */
+#define C_SHADOW 107 /* shadow symbol */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STORCLASS_H */
diff --git a/head/syms.h b/head/syms.h
new file mode 100644
index 000000000000..d18dda2ff4a5
--- /dev/null
+++ b/head/syms.h
@@ -0,0 +1,230 @@
+/*
+ * 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) 1988 AT&T */
+/* All Rights Reserved */
+
+
+#ifndef _SYMS_H
+#define _SYMS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 2.8 */
+
+/* Storage Classes are defined in storclass.h */
+#include <storclass.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Number of characters in a symbol name */
+#define SYMNMLEN 8
+/* Number of characters in a file name */
+#define FILNMLEN 14
+/* Number of array dimensions in auxiliary entry */
+#define DIMNUM 4
+
+struct syment
+{
+ union
+ {
+ char _n_name[SYMNMLEN]; /* old COFF version */
+ struct
+ {
+ long _n_zeroes; /* new == 0 */
+ long _n_offset; /* offset into string table */
+ } _n_n;
+ char *_n_nptr[2]; /* allows for overlaying */
+ } _n;
+ unsigned long n_value; /* value of symbol */
+ short n_scnum; /* section number */
+ unsigned short n_type; /* type and derived type */
+ char n_sclass; /* storage class */
+ char n_numaux; /* number of aux. entries */
+};
+
+#define n_name _n._n_name
+#define n_nptr _n._n_nptr[1]
+#define n_zeroes _n._n_n._n_zeroes
+#define n_offset _n._n_n._n_offset
+
+/*
+ * Relocatable symbols have a section number of the
+ * section in which they are defined. Otherwise, section
+ * numbers have the following meanings:
+ */
+ /* undefined symbol */
+#define N_UNDEF 0
+ /* value of symbol is absolute */
+#define N_ABS -1
+ /* special debugging symbol -- value of symbol is meaningless */
+#define N_DEBUG -2
+ /* indicates symbol needs transfer vector (preload) */
+#define N_TV (unsigned short)-3
+
+ /* indicates symbol needs transfer vector (postload) */
+
+#define P_TV (unsigned short)-4
+
+/*
+ * The fundamental type of a symbol packed into the low
+ * 4 bits of the word.
+ */
+
+#define _EF ".ef"
+
+#define T_NULL 0
+#define T_ARG 1 /* function argument (only used by compiler) */
+#define T_CHAR 2 /* character */
+#define T_SHORT 3 /* short integer */
+#define T_INT 4 /* integer */
+#define T_LONG 5 /* long integer */
+#define T_FLOAT 6 /* floating point */
+#define T_DOUBLE 7 /* double word */
+#define T_STRUCT 8 /* structure */
+#define T_UNION 9 /* union */
+#define T_ENUM 10 /* enumeration */
+#define T_MOE 11 /* member of enumeration */
+#define T_UCHAR 12 /* unsigned character */
+#define T_USHORT 13 /* unsigned short */
+#define T_UINT 14 /* unsigned integer */
+#define T_ULONG 15 /* unsigned long */
+
+/*
+ * derived types are:
+ */
+
+#define DT_NON 0 /* no derived type */
+#define DT_PTR 1 /* pointer */
+#define DT_FCN 2 /* function */
+#define DT_ARY 3 /* array */
+
+/*
+ * type packing constants
+ */
+
+#define N_BTMASK 017
+#define N_TMASK 060
+#define N_TMASK1 0300
+#define N_TMASK2 0360
+#define N_BTSHFT 4
+#define N_TSHIFT 2
+
+/*
+ * MACROS
+ */
+
+ /* Basic Type of x */
+
+#define BTYPE(x) ((x) & N_BTMASK)
+
+ /* Is x a pointer ? */
+
+#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
+
+ /* Is x a function ? */
+
+#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
+
+ /* Is x an array ? */
+
+#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
+
+ /* Is x a structure, union, or enumeration TAG? */
+
+#define ISTAG(x) ((x) == C_STRTAG || (x) == C_UNTAG || (x) == C_ENTAG)
+
+#define INCREF(x) ((((x)&~N_BTMASK)<<N_TSHIFT)|(DT_PTR<<N_BTSHFT)|(x&N_BTMASK))
+
+#define DECREF(x) ((((x)>>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK))
+
+/*
+ * AUXILIARY ENTRY FORMAT
+ */
+
+union auxent
+{
+ struct
+ {
+ long x_tagndx; /* str, un, or enum tag indx */
+ union
+ {
+ struct
+ {
+ unsigned short x_lnno; /* declaration line */
+ /* number */
+ unsigned short x_size; /* str, union, array */
+ /* size */
+ } x_lnsz;
+ long x_fsize; /* size of function */
+ } x_misc;
+ union
+ {
+ struct /* if ISFCN, tag, or .bb */
+ {
+ long x_lnnoptr; /* ptr to fcn line # */
+ long x_endndx; /* entry ndx past */
+ /* block end */
+ } x_fcn;
+ struct /* if ISARY, up to 4 dimen. */
+ {
+ unsigned short x_dimen[DIMNUM];
+ } x_ary;
+ } x_fcnary;
+ unsigned short x_tvndx; /* tv index */
+ } x_sym;
+ struct
+ {
+ char x_fname[FILNMLEN];
+ } x_file;
+ struct
+ {
+ long x_scnlen; /* section length */
+ unsigned short x_nreloc; /* number of reloc entries */
+ unsigned short x_nlinno; /* number of line numbers */
+ } x_scn;
+
+ struct
+ {
+ long x_tvfill; /* tv fill value */
+ unsigned short x_tvlen; /* length of .tv */
+ unsigned short x_tvran[2]; /* tv range */
+ } x_tv; /* info about .tv section (in auxent of symbol .tv)) */
+};
+
+#define SYMENT struct syment
+#define SYMESZ 18 /* sizeof(SYMENT) */
+
+#define AUXENT union auxent
+#define AUXESZ 18 /* sizeof(AUXENT) */
+
+/* Defines for "special" symbols */
+
+#define _ETEXT "etext"
+#define _EDATA "edata"
+#define _END "end"
+#define _START "_start"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYMS_H */