From 2de84d2572206157cf33d1fb75463eeb42ae8e42 Mon Sep 17 00:00:00 2001 From: John Birrell Date: Fri, 25 Apr 2008 23:33:18 +0000 Subject: [PATCH] Vendor import of DTrace support files from OpenSolaris. --- cmd/mdb/tools/common/die.c | 87 ++ cmd/mdb/tools/common/util.h | 51 + cmd/sgs/include/_string_table.h | 126 +++ cmd/sgs/include/alist.h | 280 ++++++ cmd/sgs/include/debug.h | 1017 ++++++++++++++++++++ cmd/sgs/include/sgs.h | 296 ++++++ cmd/sgs/include/string_table.h | 63 ++ cmd/sgs/messages/sgs.ident | 62 ++ cmd/sgs/tools/common/findprime.c | 56 ++ cmd/sgs/tools/common/sgsmsg.c | 1210 ++++++++++++++++++++++++ cmd/sgs/tools/common/string_table.c | 685 ++++++++++++++ common/avl/avl.c | 969 +++++++++++++++++++ common/ctf/ctf_create.c | 1366 +++++++++++++++++++++++++++ common/ctf/ctf_decl.c | 184 ++++ common/ctf/ctf_error.c | 97 ++ common/ctf/ctf_hash.c | 178 ++++ common/ctf/ctf_impl.h | 336 +++++++ common/ctf/ctf_labels.c | 153 +++ common/ctf/ctf_lookup.c | 313 ++++++ common/ctf/ctf_open.c | 954 +++++++++++++++++++ common/ctf/ctf_types.c | 845 +++++++++++++++++ common/ctf/ctf_util.c | 152 +++ head/nlist.h | 54 ++ head/note.h | 55 ++ head/storclass.h | 79 ++ head/syms.h | 230 +++++ 26 files changed, 9898 insertions(+) create mode 100644 cmd/mdb/tools/common/die.c create mode 100644 cmd/mdb/tools/common/util.h create mode 100644 cmd/sgs/include/_string_table.h create mode 100644 cmd/sgs/include/alist.h create mode 100644 cmd/sgs/include/debug.h create mode 100644 cmd/sgs/include/sgs.h create mode 100644 cmd/sgs/include/string_table.h create mode 100644 cmd/sgs/messages/sgs.ident create mode 100644 cmd/sgs/tools/common/findprime.c create mode 100644 cmd/sgs/tools/common/sgsmsg.c create mode 100644 cmd/sgs/tools/common/string_table.c create mode 100644 common/avl/avl.c create mode 100644 common/ctf/ctf_create.c create mode 100644 common/ctf/ctf_decl.c create mode 100644 common/ctf/ctf_error.c create mode 100644 common/ctf/ctf_hash.c create mode 100644 common/ctf/ctf_impl.h create mode 100644 common/ctf/ctf_labels.c create mode 100644 common/ctf/ctf_lookup.c create mode 100644 common/ctf/ctf_open.c create mode 100644 common/ctf/ctf_types.c create mode 100644 common/ctf/ctf_util.c create mode 100644 head/nlist.h create mode 100644 head/note.h create mode 100644 head/storclass.h create mode 100644 head/syms.h 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 +#include +#include +#include +#include +#include + +#include + +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 + +#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 +#include +#include + +#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 +#if defined(sun) +#include +#else +#include +#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 +#include +#include +#include + +#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 + +/* keys off of NDEBUG */ +#ifdef DEBUG +#undef NDEBUG +#else +#define NDEBUG +#endif + +#ifndef _ASM +#include +#if defined(sun) +#include +#else +#include +#endif +#include +#include +#include +#include +#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 + +#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 +#include + +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#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' \n", + sthash->hi_refcnt, sthash->hi_mstr->sm_str); + } else { + (void) printf(" [%ld]: '%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 ' 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 +#include +#include + +/* + * 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 +#include +#include +#include +#include + +/* + * 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 +#include +#include +#include + +/* + * 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 + * 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 + +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 + +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 + +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 +#include +#include +#include + +#ifdef _KERNEL + +#include +#include +#include + +#define isspace(c) \ + ((c) == ' ' || (c) == '\t' || (c) == '\n' || \ + (c) == '\r' || (c) == '\f' || (c) == '\v') + +#define MAP_FAILED ((void *)-1) + +#else /* _KERNEL */ + +#include +#include +#include +#include +#include +#include + +#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 ) */ + 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 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 + +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 +#include + +/* + * 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 +#include +#include + +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 : "", + fp->ctf_parlabel ? fp->ctf_parlabel : ""); + + /* + * 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 + +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 + +/* + * 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 + +#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 + +#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)&~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 */