Vendor import of DTrace support files from OpenSolaris.
This commit is contained in:
parent
ce71c4333f
commit
cb8ce77727
87
cddl/contrib/opensolaris/cmd/mdb/tools/common/die.c
Normal file
87
cddl/contrib/opensolaris/cmd/mdb/tools/common/die.c
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <elf.h>
|
||||
|
||||
#include <util.h>
|
||||
|
||||
void
|
||||
die(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int err = errno;
|
||||
#if !defined(sun)
|
||||
const char *progname = getprogname();
|
||||
#endif
|
||||
|
||||
(void) fprintf(stderr, "%s: ", progname);
|
||||
|
||||
va_start(ap, format);
|
||||
/* LINTED - variable format specifier */
|
||||
(void) vfprintf(stderr, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (format[strlen(format) - 1] != '\n')
|
||||
(void) fprintf(stderr, ": %s\n", strerror(err));
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
exit(0);
|
||||
#else
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
elfdie(char *format, ...)
|
||||
{
|
||||
va_list ap;
|
||||
#if !defined(sun)
|
||||
const char *progname = getprogname();
|
||||
#endif
|
||||
|
||||
(void) fprintf(stderr, "%s: ", progname);
|
||||
|
||||
va_start(ap, format);
|
||||
/* LINTED - variable format specifier */
|
||||
(void) vfprintf(stderr, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (format[strlen(format) - 1] != '\n')
|
||||
(void) fprintf(stderr, ": %s\n", elf_errmsg(elf_errno()));
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
exit(0);
|
||||
#else
|
||||
exit(1);
|
||||
#endif
|
||||
}
|
51
cddl/contrib/opensolaris/cmd/mdb/tools/common/util.h
Normal file
51
cddl/contrib/opensolaris/cmd/mdb/tools/common/util.h
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _UTIL_H
|
||||
#define _UTIL_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <libelf.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int findelfsecidx(Elf *, char *);
|
||||
|
||||
extern void die(char *, ...);
|
||||
extern void elfdie(char *, ...);
|
||||
|
||||
#if defined(sun)
|
||||
extern const char *progname;
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _UTIL_H */
|
126
cddl/contrib/opensolaris/cmd/sgs/include/_string_table.h
Normal file
126
cddl/contrib/opensolaris/cmd/sgs/include/_string_table.h
Normal file
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef __STRING_TABLE_DOT_H
|
||||
#define __STRING_TABLE_DOT_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/avl.h>
|
||||
#include <string_table.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A string is represented in a string table using two values: length, and
|
||||
* value. Grouping all the strings of a given length together allows for
|
||||
* efficient matching of tail strings, as each input string value is hashed.
|
||||
* Each string table uses a 2-level AVL tree of AVL trees to represent this
|
||||
* organization.
|
||||
*
|
||||
* The outer (main) AVL tree contains LenNode structures. The search key for
|
||||
* nodes on this main tree is the string length. Each such node represents
|
||||
* all strings of a given length, and all strings of that length are found
|
||||
* within.
|
||||
*
|
||||
* The strings within each LenNode are maintained using a secondary AVL tree
|
||||
* of StrNode structures. The search key for this inner tree is the string
|
||||
* itself. The strings are maintained in lexical order.
|
||||
*/
|
||||
typedef struct {
|
||||
avl_node_t sn_avlnode; /* AVL book-keeping */
|
||||
const char *sn_str; /* string */
|
||||
size_t sn_refcnt; /* reference count */
|
||||
} StrNode;
|
||||
|
||||
typedef struct {
|
||||
avl_node_t ln_avlnode; /* AVL book-keeping */
|
||||
avl_tree_t *ln_strtree; /* AVL tree of associated strings */
|
||||
size_t ln_strlen; /* length of associated strings */
|
||||
} LenNode;
|
||||
|
||||
/*
|
||||
* Define a master string data item. Other strings may be suffixes of this
|
||||
* string. The final string table will consist of the master string values,
|
||||
* laid end to end, with the other strings referencing their tails.
|
||||
*/
|
||||
typedef struct str_master Str_master;
|
||||
|
||||
struct str_master {
|
||||
const char *sm_str; /* pointer to master string */
|
||||
Str_master *sm_next; /* used for tracking master strings */
|
||||
size_t sm_strlen; /* length of master string */
|
||||
uint_t sm_hashval; /* hashval of master string */
|
||||
size_t sm_stroff; /* offset into destination strtab */
|
||||
};
|
||||
|
||||
/*
|
||||
* Define a hash data item. This item represents an individual string that has
|
||||
* been input into the String hash table. The string may either be a suffix of
|
||||
* another string, or a master string.
|
||||
*/
|
||||
typedef struct str_hash Str_hash;
|
||||
|
||||
struct str_hash {
|
||||
size_t hi_strlen; /* string length */
|
||||
size_t hi_refcnt; /* number of references to str */
|
||||
uint_t hi_hashval; /* hash for string */
|
||||
Str_master *hi_mstr; /* pointer to master string */
|
||||
Str_hash *hi_next; /* next entry in hash bucket */
|
||||
};
|
||||
|
||||
/*
|
||||
* Controlling data structure for a String Table.
|
||||
*/
|
||||
struct str_tbl {
|
||||
avl_tree_t *st_lentree; /* AVL tree of string lengths */
|
||||
char *st_strbuf; /* string buffer */
|
||||
Str_hash **st_hashbcks; /* hash buckets */
|
||||
Str_master *st_mstrlist; /* list of all master strings */
|
||||
size_t st_fullstrsize; /* uncompressed table size */
|
||||
size_t st_nextoff; /* next available string */
|
||||
size_t st_strsize; /* compressed size */
|
||||
size_t st_strcnt; /* number of strings */
|
||||
uint_t st_hbckcnt; /* number of buckets in */
|
||||
/* hashlist */
|
||||
uint_t st_flags;
|
||||
};
|
||||
|
||||
#define FLG_STTAB_COMPRESS 0x01 /* compressed string table */
|
||||
#define FLG_STTAB_COOKED 0x02 /* offset has been assigned */
|
||||
|
||||
/*
|
||||
* Starting value for use with string hashing functions inside of string_table.c
|
||||
*/
|
||||
#define HASHSEED 5381
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __STRING_TABLE_DOT_H */
|
280
cddl/contrib/opensolaris/cmd/sgs/include/alist.h
Normal file
280
cddl/contrib/opensolaris/cmd/sgs/include/alist.h
Normal file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*
|
||||
* Define an Alist, a list maintained as a reallocable array, and a for() loop
|
||||
* macro to generalize its traversal. Note that the array can be reallocated
|
||||
* as it is being traversed, thus the offset of each element is recomputed from
|
||||
* the start of the structure.
|
||||
*/
|
||||
|
||||
#ifndef _ALIST_H
|
||||
#define _ALIST_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined(sun)
|
||||
#include <sys/machelf.h>
|
||||
#else
|
||||
#include <sys/elf.h>
|
||||
#endif
|
||||
|
||||
/*
|
||||
* An Alist implements array lists. The functionality is similar to
|
||||
* that of a linked list. However, an Alist is represented by a single
|
||||
* contigious allocation of memory. The head of the memory is a header
|
||||
* that contains control information for the list. Following the header
|
||||
* is an array used to hold the user data. In the type definitions that
|
||||
* follow, we define these as an array with a single element, but when
|
||||
* we allocate the memory, we actually allocate the amount of memory needed.
|
||||
*
|
||||
* There are two "flavors" of array list:
|
||||
*
|
||||
* Alist - Contain arbitrary data, usually structs.
|
||||
* APlist - Contain pointers to data allocated elsewhere.
|
||||
*
|
||||
* This differentiation is useful, because pointer lists are heavily
|
||||
* used, and support a slightly different set of operations that are
|
||||
* unique to their purpose.
|
||||
*
|
||||
* Array lists are initially represented by a NULL pointer. The memory
|
||||
* for the list is only allocated if an item is inserted. This is very
|
||||
* efficient for data structures that may or may not be needed for a
|
||||
* given linker operation --- you only pay for what you use. In addition:
|
||||
*
|
||||
* - Array lists grow as needed (memory is reallocated as necessary)
|
||||
* - Data is kept contiguously (no unused holes in between elements)
|
||||
* at the beginning of the data area. This locality has
|
||||
* good cache behavior, as access to adjacent items are
|
||||
* highly likely to be in the same page of memory.
|
||||
* - Insert/Delete operations at the end of the list are very
|
||||
* efficient. However, insert/delete operations elsewhere
|
||||
* will cause a relatively expensive overlapped memory
|
||||
* copy of the data following the insert/delete location.
|
||||
* - As with any generic memory alloctor (i.e. malloc()/free()),
|
||||
* array lists are not type safe for the data they contain.
|
||||
* Data is managed as (void *) pointers to data of a given
|
||||
* length, so the Alist module cannot prevent the caller from
|
||||
* inserting/extracting the wrong type of data. The caller
|
||||
* must guard against this.
|
||||
* - To free an array list, simply call the standard free() function
|
||||
* on the list pointer.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Aliste is used to represent list indexes, offsets, and sizes.
|
||||
*/
|
||||
typedef size_t Aliste;
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Alist is used to hold non-pointer items --- usually structs:
|
||||
* - There must be an even number of Aliste fields before the
|
||||
* al_data field. This ensures that al_data will have
|
||||
* an alignment of 8, no matter whether sizeof(Aliste)
|
||||
* is 4 or 8. That means that al_data will have sufficient
|
||||
* alignment for any use, just like memory allocated via
|
||||
* malloc().
|
||||
* - al_nitems and al_next are redundant, in that they are
|
||||
* directly related:
|
||||
* al_next = al_nitems * al_size
|
||||
* We do this to make ALIST_TRAVERSE_BYOFFSET maximally
|
||||
* efficient. This doesn't waste space, because of the
|
||||
* requirement to have an even # of Alist fields (above).
|
||||
*
|
||||
* Note that Alists allow the data to be referenced by 0 based array
|
||||
* index, or by their byte offset from the start of the Alist memory
|
||||
* allocation. The index form is preferred for most use, as it is simpler.
|
||||
* However, by-offset access is used by rtld link maps, and this ability
|
||||
* is convenient in that case.
|
||||
*/
|
||||
typedef struct {
|
||||
Aliste al_arritems; /* # of items in al_data allocation */
|
||||
Aliste al_nitems; /* # items (index of next avail item) */
|
||||
Aliste al_next; /* offset of next available al_data[] */
|
||||
Aliste al_size; /* size of each al_data[] item */
|
||||
void *al_data[1]; /* data (can grow) */
|
||||
} Alist;
|
||||
|
||||
/*
|
||||
* APlist is a variant of Alist that contains pointers. There are several
|
||||
* benefits to this special type:
|
||||
* - API is simpler
|
||||
* - Pointers are used directly, instead of requiring a
|
||||
* pointer-to-pointer double indirection.
|
||||
* - The implementation is slightly more efficient.
|
||||
* - Operations that make particular sense for pointers
|
||||
* can be supported without confusing the API for the
|
||||
* regular Alists.
|
||||
*/
|
||||
typedef struct {
|
||||
Aliste apl_arritems; /* # of items in apl_data allocation */
|
||||
Aliste apl_nitems; /* # items (index of next avail item) */
|
||||
void *apl_data[1]; /* data area: (arrcnt * size) bytes */
|
||||
} APlist;
|
||||
|
||||
|
||||
/*
|
||||
* The ALIST_OFF_DATA and APLIST_OFF_DATA macros give the byte offset
|
||||
* from the start of an array list to the first byte of the data area
|
||||
* used to hold user data. The same trick used by the standard offsetof()
|
||||
* macro is used.
|
||||
*/
|
||||
#define ALIST_OFF_DATA ((size_t)(((Alist *)0)->al_data))
|
||||
#define APLIST_OFF_DATA ((size_t)(((APlist *)0)->apl_data))
|
||||
|
||||
|
||||
/*
|
||||
* The TRAVERSE macros are intended to be used within a for(), and
|
||||
* cause the resulting loop to iterate over each item in the loop,
|
||||
* in order.
|
||||
* ALIST_TRAVERSE: Traverse over the items in an Alist,
|
||||
* using the zero based item array index to refer to
|
||||
* each item.
|
||||
* ALIST_TRAVERSE_BY_OFFSET: Traverse over the items in an
|
||||
* Alist using the byte offset from the head of the
|
||||
* Alist pointer to refer to each item. It should be noted
|
||||
* that the first such offset is given by ALIST_OFF_DATA,
|
||||
* and as such, there will never be a 0 offset. Some code
|
||||
* uses this fact to treat 0 as a reserved value with
|
||||
* special meaning.
|
||||
*
|
||||
* By-offset access is convenient for some parts of
|
||||
* rtld, where a value of 0 is used to indicate an
|
||||
* uninitialized link map control.
|
||||
*
|
||||
* APLIST_TRAVERSE: Traverse over the pointers in an APlist, using
|
||||
* the zero based item array index to refer to each pointer.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Within the loop:
|
||||
*
|
||||
* LIST - Pointer to Alist structure for list
|
||||
* IDX - The current item index
|
||||
* OFF - The current item offset
|
||||
* DATA - Pointer to item
|
||||
*/
|
||||
#define ALIST_TRAVERSE(LIST, IDX, DATA) \
|
||||
(IDX) = 0, \
|
||||
((LIST) != NULL) && ((DATA) = (void *)(LIST)->al_data); \
|
||||
\
|
||||
((LIST) != NULL) && ((IDX) < (LIST)->al_nitems); \
|
||||
\
|
||||
(IDX)++, \
|
||||
(DATA) = (void *) (((LIST)->al_size * (IDX)) + (char *)(LIST)->al_data)
|
||||
|
||||
#define ALIST_TRAVERSE_BY_OFFSET(LIST, OFF, DATA) \
|
||||
(((LIST) != NULL) && ((OFF) = ALIST_OFF_DATA) && \
|
||||
(((DATA) = (void *)((char *)(LIST) + (OFF))))); \
|
||||
\
|
||||
(((LIST) != NULL) && ((OFF) < (LIST)->al_next)); \
|
||||
\
|
||||
(((OFF) += ((LIST)->al_size)), \
|
||||
((DATA) = (void *)((char *)(LIST) + (OFF))))
|
||||
|
||||
/*
|
||||
* Within the loop:
|
||||
*
|
||||
* LIST - Pointer to APlist structure for list
|
||||
* IDX - The current item index
|
||||
* PTR - item value
|
||||
*
|
||||
* Note that this macro is designed to ensure that PTR retains the
|
||||
* value of the final pointer in the list after exiting the for loop,
|
||||
* and to avoid dereferencing an out of range address. This is done by
|
||||
* doing the dereference in the middle expression, using the comma
|
||||
* operator to ensure that a NULL pointer won't stop the loop.
|
||||
*/
|
||||
#define APLIST_TRAVERSE(LIST, IDX, PTR) \
|
||||
(IDX) = 0; \
|
||||
\
|
||||
((LIST) != NULL) && ((IDX) < (LIST)->apl_nitems) && \
|
||||
(((PTR) = ((LIST)->apl_data)[IDX]), 1); \
|
||||
\
|
||||
(IDX)++
|
||||
|
||||
|
||||
/*
|
||||
* Possible values returned by aplist_test()
|
||||
*/
|
||||
typedef enum {
|
||||
ALE_ALLOCFAIL = 0, /* Memory allocation error */
|
||||
ALE_EXISTS = 1, /* alist entry already exists */
|
||||
ALE_NOTFND = 2, /* item not found and insert not required */
|
||||
ALE_CREATE = 3 /* alist entry created */
|
||||
} aplist_test_t;
|
||||
|
||||
|
||||
/*
|
||||
* Access to an Alist item by index or offset. This is needed because the
|
||||
* size of an item in an Alist is not known by the C compiler, and we
|
||||
* have to do the indexing arithmetic explicitly.
|
||||
*
|
||||
* For an APlist, index the apl_data field directly --- No macro is needed.
|
||||
*/
|
||||
#define alist_item(_lp, _idx) \
|
||||
((void *)(ALIST_OFF_DATA + ((_idx) * (_lp)->al_size) + (char *)(_lp)))
|
||||
#define alist_item_by_offset(_lp, _off) \
|
||||
((void *)((_off) + (char *)(_lp)))
|
||||
|
||||
/*
|
||||
* # of items currently found in a list. These macros handle the case
|
||||
* where the list has not been allocated yet.
|
||||
*/
|
||||
#define alist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->al_nitems)
|
||||
#define aplist_nitems(_lp) (((_lp) == NULL) ? 0 : (_lp)->apl_nitems)
|
||||
|
||||
|
||||
extern void *alist_append(Alist **, const void *, size_t, Aliste);
|
||||
extern void alist_delete(Alist *, Aliste *);
|
||||
extern void alist_delete_by_offset(Alist *, Aliste *);
|
||||
extern void *alist_insert(Alist **, const void *, size_t,
|
||||
Aliste, Aliste);
|
||||
extern void *alist_insert_by_offset(Alist **, const void *, size_t,
|
||||
Aliste, Aliste);
|
||||
extern void alist_reset(Alist *);
|
||||
|
||||
|
||||
extern void *aplist_append(APlist **, const void *, Aliste);
|
||||
extern void aplist_delete(APlist *, Aliste *);
|
||||
extern int aplist_delete_value(APlist *, const void *);
|
||||
extern void *aplist_insert(APlist **, const void *,
|
||||
Aliste, Aliste idx);
|
||||
extern void aplist_reset(APlist *);
|
||||
extern aplist_test_t aplist_test(APlist **, const void *, Aliste);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _ALIST_H */
|
1017
cddl/contrib/opensolaris/cmd/sgs/include/debug.h
Normal file
1017
cddl/contrib/opensolaris/cmd/sgs/include/debug.h
Normal file
File diff suppressed because it is too large
Load Diff
296
cddl/contrib/opensolaris/cmd/sgs/include/sgs.h
Normal file
296
cddl/contrib/opensolaris/cmd/sgs/include/sgs.h
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 1988 AT&T
|
||||
* All Rights Reserved
|
||||
*
|
||||
*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*
|
||||
* Global include file for all sgs.
|
||||
*/
|
||||
|
||||
#ifndef _SGS_H
|
||||
#define _SGS_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* <assert.h> keys off of NDEBUG */
|
||||
#ifdef DEBUG
|
||||
#undef NDEBUG
|
||||
#else
|
||||
#define NDEBUG
|
||||
#endif
|
||||
|
||||
#ifndef _ASM
|
||||
#include <sys/types.h>
|
||||
#if defined(sun)
|
||||
#include <sys/machelf.h>
|
||||
#else
|
||||
#include <elf.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <libelf.h>
|
||||
#include <assert.h>
|
||||
#include <alist.h>
|
||||
#endif /* _ASM */
|
||||
|
||||
/*
|
||||
* Software identification.
|
||||
*/
|
||||
#define SGS ""
|
||||
#define SGU_PKG "Software Generation Utilities"
|
||||
#define SGU_REL "(SGU) Solaris-ELF (4.0)"
|
||||
|
||||
|
||||
#ifndef _ASM
|
||||
|
||||
/*
|
||||
* link_ver_string[] contains a version string for use by the link-editor
|
||||
* and all other linker components. It is found in libconv, and is
|
||||
* generated by sgs/libconv/common/bld_vernote.ksh. That script produces
|
||||
* libconv/{plat}/vernote.s, which is in turn assembled/linked into
|
||||
* libconv.
|
||||
*/
|
||||
extern const char link_ver_string[];
|
||||
/*
|
||||
* Macro to round to next double word boundary.
|
||||
*/
|
||||
#define S_DROUND(x) (((x) + sizeof (double) - 1) & ~(sizeof (double) - 1))
|
||||
|
||||
/*
|
||||
* General align and round macros.
|
||||
*/
|
||||
#define S_ALIGN(x, a) ((x) & ~(((a) ? (a) : 1) - 1))
|
||||
#define S_ROUND(x, a) ((x) + (((a) ? (a) : 1) - 1) & ~(((a) ? (a) : 1) - 1))
|
||||
|
||||
/*
|
||||
* Bit manipulation macros; generic bit mask and is `v' in the range
|
||||
* supportable in `n' bits?
|
||||
*/
|
||||
#define S_MASK(n) ((1 << (n)) -1)
|
||||
#define S_INRANGE(v, n) (((-(1 << (n)) - 1) < (v)) && ((v) < (1 << (n))))
|
||||
|
||||
|
||||
/*
|
||||
* Yet another definition of the OFFSETOF macro, used with the AVL routines.
|
||||
*/
|
||||
#define SGSOFFSETOF(s, m) ((size_t)(&(((s *)0)->m)))
|
||||
|
||||
/*
|
||||
* When casting between integer and pointer types, gcc will complain
|
||||
* if the integer type used is not large enough to hold the pointer
|
||||
* value without loss. Although a dubious practice in general, this
|
||||
* is sometimes done by design. In those cases, the general solution
|
||||
* is to introduce an intermediate cast to widen the integer value. The
|
||||
* CAST_PTRINT macro does this, and its use documents the fact that
|
||||
* the programmer is doing that sort of cast.
|
||||
*/
|
||||
#ifdef __GNUC__
|
||||
#define CAST_PTRINT(cast, value) ((cast)(uintptr_t)value)
|
||||
#else
|
||||
#define CAST_PTRINT(cast, value) ((cast)value)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* General typedefs.
|
||||
*/
|
||||
typedef enum {
|
||||
FALSE = 0,
|
||||
TRUE = 1
|
||||
} Boolean;
|
||||
|
||||
/*
|
||||
* Types of errors (used by eprintf()), together with a generic error return
|
||||
* value.
|
||||
*/
|
||||
typedef enum {
|
||||
ERR_NONE,
|
||||
ERR_WARNING,
|
||||
ERR_FATAL,
|
||||
ERR_ELF,
|
||||
ERR_NUM /* Must be last */
|
||||
} Error;
|
||||
|
||||
#if defined(_LP64) && !defined(_ELF64)
|
||||
#define S_ERROR (~(uint_t)0)
|
||||
#else
|
||||
#define S_ERROR (~(uintptr_t)0)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* LIST_TRAVERSE() is used as the only "argument" of a "for" loop to
|
||||
* traverse a linked list. The node pointer `node' is set to each node in
|
||||
* turn and the corresponding data pointer is copied to `data'. The macro
|
||||
* is used as in
|
||||
* for (LIST_TRAVERSE(List *list, Listnode *node, void *data)) {
|
||||
* process(data);
|
||||
* }
|
||||
*/
|
||||
#define LIST_TRAVERSE(L, N, D) \
|
||||
(void) (((N) = (L)->head) != NULL && ((D) = (N)->data) != NULL); \
|
||||
(N) != NULL; \
|
||||
(void) (((N) = (N)->next) != NULL && ((D) = (N)->data) != NULL)
|
||||
|
||||
typedef struct listnode Listnode;
|
||||
typedef struct list List;
|
||||
|
||||
struct listnode { /* a node on a linked list */
|
||||
void *data; /* the data item */
|
||||
Listnode *next; /* the next element */
|
||||
};
|
||||
|
||||
struct list { /* a linked list */
|
||||
Listnode *head; /* the first element */
|
||||
Listnode *tail; /* the last element */
|
||||
};
|
||||
|
||||
|
||||
#ifdef _SYSCALL32
|
||||
typedef struct listnode32 Listnode32;
|
||||
typedef struct list32 List32;
|
||||
|
||||
struct listnode32 { /* a node on a linked list */
|
||||
Elf32_Addr data; /* the data item */
|
||||
Elf32_Addr next; /* the next element */
|
||||
};
|
||||
|
||||
struct list32 { /* a linked list */
|
||||
Elf32_Addr head; /* the first element */
|
||||
Elf32_Addr tail; /* the last element */
|
||||
};
|
||||
#endif /* _SYSCALL32 */
|
||||
|
||||
|
||||
/*
|
||||
* Structure to maintain rejected files elf information. Files that are not
|
||||
* applicable to the present link-edit are rejected and a search for an
|
||||
* appropriate file may be resumed. The first rejected files information is
|
||||
* retained so that a better error diagnostic can be given should an appropriate
|
||||
* file not be located.
|
||||
*/
|
||||
typedef struct {
|
||||
ushort_t rej_type; /* SGS_REJ_ value */
|
||||
ushort_t rej_flag; /* additional information */
|
||||
uint_t rej_info; /* numeric and string information */
|
||||
const char *rej_str; /* associated with error */
|
||||
const char *rej_name; /* object name - expanded library */
|
||||
/* name and archive members */
|
||||
} Rej_desc;
|
||||
|
||||
#define SGS_REJ_NONE 0
|
||||
#define SGS_REJ_MACH 1 /* wrong ELF machine type */
|
||||
#define SGS_REJ_CLASS 2 /* wrong ELF class (32-bit/64-bit) */
|
||||
#define SGS_REJ_DATA 3 /* wrong ELF data format (MSG/LSB) */
|
||||
#define SGS_REJ_TYPE 4 /* bad ELF type */
|
||||
#define SGS_REJ_BADFLAG 5 /* bad ELF flags value */
|
||||
#define SGS_REJ_MISFLAG 6 /* mismatched ELF flags value */
|
||||
#define SGS_REJ_VERSION 7 /* mismatched ELF/lib version */
|
||||
#define SGS_REJ_HAL 8 /* HAL R1 extensions required */
|
||||
#define SGS_REJ_US3 9 /* Sun UltraSPARC III extensions */
|
||||
/* required */
|
||||
#define SGS_REJ_STR 10 /* generic error - info is a string */
|
||||
#define SGS_REJ_UNKFILE 11 /* unknown file type */
|
||||
#define SGS_REJ_HWCAP_1 12 /* hardware capabilities mismatch */
|
||||
|
||||
/*
|
||||
* For those source files used both inside and outside of the
|
||||
* libld source base (tools/common/string_table.c) we can
|
||||
* automatically switch between the allocation models
|
||||
* based off of the 'cc -DUSE_LIBLD_MALLOC' flag.
|
||||
*/
|
||||
#ifdef USE_LIBLD_MALLOC
|
||||
#define calloc(x, a) libld_malloc(((size_t)x) * ((size_t)a))
|
||||
#define free libld_free
|
||||
#define malloc libld_malloc
|
||||
#define realloc libld_realloc
|
||||
|
||||
#define libld_calloc(x, a) libld_malloc(((size_t)x) * ((size_t)a))
|
||||
extern void libld_free(void *);
|
||||
extern void *libld_malloc(size_t);
|
||||
extern void *libld_realloc(void *, size_t);
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Data structures (defined in libld.h).
|
||||
*/
|
||||
typedef struct ent_desc Ent_desc;
|
||||
typedef struct group_desc Group_desc;
|
||||
typedef struct ifl_desc Ifl_desc;
|
||||
typedef struct is_desc Is_desc;
|
||||
typedef struct isa_desc Isa_desc;
|
||||
typedef struct isa_opt Isa_opt;
|
||||
typedef struct mv_desc Mv_desc;
|
||||
typedef struct ofl_desc Ofl_desc;
|
||||
typedef struct os_desc Os_desc;
|
||||
typedef struct rel_cache Rel_cache;
|
||||
typedef struct sdf_desc Sdf_desc;
|
||||
typedef struct sdv_desc Sdv_desc;
|
||||
typedef struct sg_desc Sg_desc;
|
||||
typedef struct sort_desc Sort_desc;
|
||||
typedef struct sec_order Sec_order;
|
||||
typedef struct sym_desc Sym_desc;
|
||||
typedef struct sym_aux Sym_aux;
|
||||
typedef struct sym_avlnode Sym_avlnode;
|
||||
typedef struct uts_desc Uts_desc;
|
||||
typedef struct ver_desc Ver_desc;
|
||||
typedef struct ver_index Ver_index;
|
||||
typedef struct audit_desc Audit_desc;
|
||||
typedef struct audit_info Audit_info;
|
||||
typedef struct audit_list Audit_list;
|
||||
|
||||
/*
|
||||
* Data structures defined in machrel.h.
|
||||
*/
|
||||
typedef struct rel_desc Rel_desc;
|
||||
|
||||
/*
|
||||
* Data structures defined in rtld.h.
|
||||
*/
|
||||
typedef struct lm_list Lm_list;
|
||||
#ifdef _SYSCALL32
|
||||
typedef struct lm_list32 Lm_list32;
|
||||
#endif /* _SYSCALL32 */
|
||||
|
||||
/*
|
||||
* For the various utilities that include sgs.h
|
||||
*/
|
||||
extern int assfail(const char *, const char *, int);
|
||||
extern void eprintf(Lm_list *, Error, const char *, ...);
|
||||
extern char *sgs_demangle(char *);
|
||||
extern uint_t sgs_str_hash(const char *);
|
||||
extern uint_t findprime(uint_t);
|
||||
|
||||
#endif /* _ASM */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _SGS_H */
|
63
cddl/contrib/opensolaris/cmd/sgs/include/string_table.h
Normal file
63
cddl/contrib/opensolaris/cmd/sgs/include/string_table.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _STRING_TABLE_DOT_H
|
||||
#define _STRING_TABLE_DOT_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Exported, opaque string table handle.
|
||||
*/
|
||||
typedef struct str_tbl Str_tbl;
|
||||
|
||||
/*
|
||||
* Exported string table functions.
|
||||
*/
|
||||
extern int st_delstring(Str_tbl *, const char *);
|
||||
extern void st_destroy(Str_tbl *);
|
||||
extern size_t st_getstrtab_sz(Str_tbl *);
|
||||
extern const char *st_getstrbuf(Str_tbl *);
|
||||
extern int st_insert(Str_tbl *, const char *);
|
||||
extern Str_tbl *st_new(uint_t);
|
||||
extern int st_setstrbuf(Str_tbl *, char *, size_t);
|
||||
extern int st_setstring(Str_tbl *, const char *, size_t *);
|
||||
|
||||
/*
|
||||
* Exported flags values for st_new().
|
||||
*/
|
||||
#define FLG_STNEW_COMPRESS 0x01 /* compressed string table */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _STRING_TABLE_DOT_H */
|
62
cddl/contrib/opensolaris/cmd/sgs/messages/sgs.ident
Normal file
62
cddl/contrib/opensolaris/cmd/sgs/messages/sgs.ident
Normal file
@ -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 */
|
56
cddl/contrib/opensolaris/cmd/sgs/tools/common/findprime.c
Normal file
56
cddl/contrib/opensolaris/cmd/sgs/tools/common/findprime.c
Normal file
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sgs.h>
|
||||
|
||||
/*
|
||||
* function that will find a prime'ish number. Usefull for
|
||||
* hashbuckets and related things.
|
||||
*/
|
||||
uint_t
|
||||
findprime(uint_t count)
|
||||
{
|
||||
uint_t h, f;
|
||||
|
||||
if (count <= 3)
|
||||
return (3);
|
||||
|
||||
|
||||
/*
|
||||
* Check to see if divisible by two, if so
|
||||
* increment.
|
||||
*/
|
||||
if ((count & 0x1) == 0)
|
||||
count++;
|
||||
|
||||
for (h = count, f = 2; f * f <= h; f++)
|
||||
if ((h % f) == 0)
|
||||
h += f = 1;
|
||||
return (h);
|
||||
}
|
1210
cddl/contrib/opensolaris/cmd/sgs/tools/common/sgsmsg.c
Normal file
1210
cddl/contrib/opensolaris/cmd/sgs/tools/common/sgsmsg.c
Normal file
File diff suppressed because it is too large
Load Diff
685
cddl/contrib/opensolaris/cmd/sgs/tools/common/string_table.c
Normal file
685
cddl/contrib/opensolaris/cmd/sgs/tools/common/string_table.c
Normal file
@ -0,0 +1,685 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <_string_table.h>
|
||||
#include <strings.h>
|
||||
#include <sgs.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
* This file provides the interfaces to build a Str_tbl suitable for use by
|
||||
* either the sgsmsg message system, or a standard ELF string table (SHT_STRTAB)
|
||||
* as created by ld(1).
|
||||
*
|
||||
* There are two modes which can be used when constructing a string table:
|
||||
*
|
||||
* st_new(0)
|
||||
* standard string table - no compression. This is the
|
||||
* traditional, fast method.
|
||||
*
|
||||
* st_new(FLG_STTAB_COMPRESS)
|
||||
* builds a compressed string table which both eliminates
|
||||
* duplicate strings, and permits strings with common suffixes
|
||||
* (atexit vs. exit) to overlap in the table. This provides space
|
||||
* savings for many string tables. Although more work than the
|
||||
* traditional method, the algorithms used are designed to scale
|
||||
* and keep any overhead at a minimum.
|
||||
*
|
||||
* These string tables are built with a common interface in a two-pass manner.
|
||||
* The first pass finds all of the strings required for the string-table and
|
||||
* calculates the size required for the final string table.
|
||||
*
|
||||
* The second pass allocates the string table, populates the strings into the
|
||||
* table and returns the offsets the strings have been assigned.
|
||||
*
|
||||
* The calling sequence to build and populate a string table is:
|
||||
*
|
||||
* st_new(); // initialize strtab
|
||||
*
|
||||
* st_insert(st1); // first pass of strings ...
|
||||
* // calculates size required for
|
||||
* // string table
|
||||
*
|
||||
* st_delstring(st?); // remove string previously
|
||||
* // inserted
|
||||
* st_insert(stN);
|
||||
*
|
||||
* st_getstrtab_sz(); // freezes strtab and computes
|
||||
* // size of table.
|
||||
*
|
||||
* st_setstrbuf(); // associates a final destination
|
||||
* // for the string table
|
||||
*
|
||||
* st_setstring(st1); // populate the string table
|
||||
* ... // offsets are based off of second
|
||||
* // pass through the string table
|
||||
* st_setstring(stN);
|
||||
*
|
||||
* st_destroy(); // tear down string table
|
||||
* // structures.
|
||||
*
|
||||
* String Suffix Compression Algorithm:
|
||||
*
|
||||
* Here's a quick high level overview of the Suffix String
|
||||
* compression algorithm used. First - the heart of the algorithm
|
||||
* is a Hash table list which represents a dictionary of all unique
|
||||
* strings inserted into the string table. The hash function for
|
||||
* this table is a standard string hash except that the hash starts
|
||||
* at the last character in the string (&str[n - 1]) and works towards
|
||||
* the first character in the function (&str[0]). As we compute the
|
||||
* HASH value for a given string, we also compute the hash values
|
||||
* for all of the possible suffix strings for that string.
|
||||
*
|
||||
* As we compute the hash - at each character see if the current
|
||||
* suffix string for that hash is already present in the table. If
|
||||
* it is, and the string is a master string. Then change that
|
||||
* string to a suffix string of the new string being inserted.
|
||||
*
|
||||
* When the final hash value is found (hash for str[0...n]), check
|
||||
* to see if it is in the hash table - if so increment the reference
|
||||
* count for the string. If it is not yet in the table, insert a
|
||||
* new hash table entry for a master string.
|
||||
*
|
||||
* The above method will find all suffixes of a given string given
|
||||
* that the strings are inserted from shortest to longest. That is
|
||||
* why this is a two phase method, we first collect all of the
|
||||
* strings and store them based off of their length in an AVL tree.
|
||||
* Once all of the strings have been submitted we then start the
|
||||
* hash table build by traversing the AVL tree in order and
|
||||
* inserting the strings from shortest to longest as described
|
||||
* above.
|
||||
*/
|
||||
|
||||
/* LINTLIBRARY */
|
||||
|
||||
static int
|
||||
avl_len_compare(const void *n1, const void *n2)
|
||||
{
|
||||
size_t len1, len2;
|
||||
|
||||
len1 = ((LenNode *)n1)->ln_strlen;
|
||||
len2 = ((LenNode *)n2)->ln_strlen;
|
||||
|
||||
if (len1 == len2)
|
||||
return (0);
|
||||
if (len2 < len1)
|
||||
return (1);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
static int
|
||||
avl_str_compare(const void *n1, const void *n2)
|
||||
{
|
||||
const char *str1, *str2;
|
||||
int rc;
|
||||
|
||||
str1 = ((StrNode *)n1)->sn_str;
|
||||
str2 = ((StrNode *)n2)->sn_str;
|
||||
|
||||
rc = strcmp(str1, str2);
|
||||
if (rc > 0)
|
||||
return (1);
|
||||
if (rc < 0)
|
||||
return (-1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return an initialized Str_tbl - returns NULL on failure.
|
||||
*
|
||||
* flags:
|
||||
* FLG_STTAB_COMPRESS - build a compressed string table
|
||||
*/
|
||||
Str_tbl *
|
||||
st_new(uint_t flags)
|
||||
{
|
||||
Str_tbl *stp;
|
||||
|
||||
if ((stp = calloc(sizeof (Str_tbl), 1)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* Start with a leading '\0' - it's tradition.
|
||||
*/
|
||||
stp->st_strsize = stp->st_fullstrsize = stp->st_nextoff = 1;
|
||||
|
||||
/*
|
||||
* Do we compress this string table?
|
||||
*/
|
||||
stp->st_flags = flags;
|
||||
if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
|
||||
return (stp);
|
||||
|
||||
if ((stp->st_lentree = calloc(sizeof (avl_tree_t), 1)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
avl_create(stp->st_lentree, &avl_len_compare, sizeof (LenNode),
|
||||
SGSOFFSETOF(LenNode, ln_avlnode));
|
||||
|
||||
return (stp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a new string into the Str_tbl. There are two AVL trees used.
|
||||
*
|
||||
* . The first LenNode AVL tree maintains a tree of nodes based on string
|
||||
* sizes.
|
||||
* . Each LenNode maintains a StrNode AVL tree for each string. Large
|
||||
* applications have been known to contribute thousands of strings of
|
||||
* the same size. Should strings need to be removed (-z ignore), then
|
||||
* the string AVL tree makes this removal efficient and scalable.
|
||||
*/
|
||||
int
|
||||
st_insert(Str_tbl *stp, const char *str)
|
||||
{
|
||||
size_t len;
|
||||
StrNode *snp, sn = { 0 };
|
||||
LenNode *lnp, ln = { 0 };
|
||||
avl_index_t where;
|
||||
|
||||
/*
|
||||
* String table can't have been cooked
|
||||
*/
|
||||
assert((stp->st_flags & FLG_STTAB_COOKED) == 0);
|
||||
|
||||
/*
|
||||
* Null strings always point to the head of the string
|
||||
* table - no reason to keep searching.
|
||||
*/
|
||||
if ((len = strlen(str)) == 0)
|
||||
return (0);
|
||||
|
||||
stp->st_fullstrsize += len + 1;
|
||||
stp->st_strcnt++;
|
||||
|
||||
if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* From the controlling string table, determine which LenNode AVL node
|
||||
* provides for this string length. If the node doesn't exist, insert
|
||||
* a new node to represent this string length.
|
||||
*/
|
||||
ln.ln_strlen = len;
|
||||
if ((lnp = avl_find(stp->st_lentree, &ln, &where)) == NULL) {
|
||||
if ((lnp = calloc(sizeof (LenNode), 1)) == NULL)
|
||||
return (-1);
|
||||
lnp->ln_strlen = len;
|
||||
avl_insert(stp->st_lentree, lnp, where);
|
||||
|
||||
if ((lnp->ln_strtree = calloc(sizeof (avl_tree_t), 1)) == NULL)
|
||||
return (0);
|
||||
|
||||
avl_create(lnp->ln_strtree, &avl_str_compare, sizeof (StrNode),
|
||||
SGSOFFSETOF(StrNode, sn_avlnode));
|
||||
}
|
||||
|
||||
/*
|
||||
* From the string length AVL node determine whether a StrNode AVL node
|
||||
* provides this string. If the node doesn't exist, insert a new node
|
||||
* to represent this string.
|
||||
*/
|
||||
sn.sn_str = str;
|
||||
if ((snp = avl_find(lnp->ln_strtree, &sn, &where)) == NULL) {
|
||||
if ((snp = calloc(sizeof (StrNode), 1)) == NULL)
|
||||
return (-1);
|
||||
snp->sn_str = str;
|
||||
avl_insert(lnp->ln_strtree, snp, where);
|
||||
}
|
||||
snp->sn_refcnt++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a previously inserted string from the Str_tbl.
|
||||
*/
|
||||
int
|
||||
st_delstring(Str_tbl *stp, const char *str)
|
||||
{
|
||||
size_t len;
|
||||
LenNode *lnp, ln = { 0 };
|
||||
StrNode *snp, sn = { 0 };
|
||||
|
||||
/*
|
||||
* String table can't have been cooked
|
||||
*/
|
||||
assert((stp->st_flags & FLG_STTAB_COOKED) == 0);
|
||||
|
||||
len = strlen(str);
|
||||
stp->st_fullstrsize -= len + 1;
|
||||
|
||||
if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Determine which LenNode AVL node provides for this string length.
|
||||
*/
|
||||
ln.ln_strlen = len;
|
||||
if ((lnp = avl_find(stp->st_lentree, &ln, 0)) != NULL) {
|
||||
sn.sn_str = str;
|
||||
if ((snp = avl_find(lnp->ln_strtree, &sn, 0)) != NULL) {
|
||||
/*
|
||||
* Reduce the reference count, and if zero remove the
|
||||
* node.
|
||||
*/
|
||||
if (--snp->sn_refcnt == 0)
|
||||
avl_remove(lnp->ln_strtree, snp);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* No strings of this length, or no string itself - someone goofed.
|
||||
*/
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tear down a String_Table structure.
|
||||
*/
|
||||
void
|
||||
st_destroy(Str_tbl *stp)
|
||||
{
|
||||
Str_hash *sthash, *psthash;
|
||||
Str_master *mstr, *pmstr;
|
||||
uint_t i;
|
||||
|
||||
/*
|
||||
* cleanup the master strings
|
||||
*/
|
||||
for (mstr = stp->st_mstrlist, pmstr = 0; mstr;
|
||||
mstr = mstr->sm_next) {
|
||||
if (pmstr)
|
||||
free(pmstr);
|
||||
pmstr = mstr;
|
||||
}
|
||||
if (pmstr)
|
||||
free(pmstr);
|
||||
|
||||
if (stp->st_hashbcks) {
|
||||
for (i = 0; i < stp->st_hbckcnt; i++) {
|
||||
for (sthash = stp->st_hashbcks[i], psthash = 0;
|
||||
sthash; sthash = sthash->hi_next) {
|
||||
if (psthash)
|
||||
free(psthash);
|
||||
psthash = sthash;
|
||||
}
|
||||
if (psthash)
|
||||
free(psthash);
|
||||
}
|
||||
free(stp->st_hashbcks);
|
||||
}
|
||||
free(stp);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* For a given string - copy it into the buffer associated with
|
||||
* the string table - and return the offset it has been assigned.
|
||||
*
|
||||
* If a value of '-1' is returned - the string was not found in
|
||||
* the Str_tbl.
|
||||
*/
|
||||
int
|
||||
st_setstring(Str_tbl *stp, const char *str, size_t *stoff)
|
||||
{
|
||||
size_t stlen;
|
||||
uint_t hashval;
|
||||
Str_hash *sthash;
|
||||
Str_master *mstr;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* String table *must* have been previously cooked
|
||||
*/
|
||||
assert(stp->st_strbuf);
|
||||
|
||||
assert(stp->st_flags & FLG_STTAB_COOKED);
|
||||
stlen = strlen(str);
|
||||
/*
|
||||
* Null string always points to head of string table
|
||||
*/
|
||||
if (stlen == 0) {
|
||||
*stoff = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
|
||||
size_t _stoff;
|
||||
|
||||
stlen++; /* count for trailing '\0' */
|
||||
_stoff = stp->st_nextoff;
|
||||
/*
|
||||
* Have we overflowed our assigned buffer?
|
||||
*/
|
||||
if ((_stoff + stlen) > stp->st_fullstrsize)
|
||||
return (-1);
|
||||
memcpy(stp->st_strbuf + _stoff, str, stlen);
|
||||
*stoff = _stoff;
|
||||
stp->st_nextoff += stlen;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate reverse hash for string.
|
||||
*/
|
||||
hashval = HASHSEED;
|
||||
for (i = stlen; i >= 0; i--) {
|
||||
hashval = ((hashval << 5) + hashval) +
|
||||
str[i]; /* h = ((h * 33) + c) */
|
||||
}
|
||||
|
||||
for (sthash = stp->st_hashbcks[hashval % stp->st_hbckcnt]; sthash;
|
||||
sthash = sthash->hi_next) {
|
||||
const char *hstr;
|
||||
|
||||
if (sthash->hi_hashval != hashval)
|
||||
continue;
|
||||
|
||||
hstr = &sthash->hi_mstr->sm_str[sthash->hi_mstr->sm_strlen -
|
||||
sthash->hi_strlen];
|
||||
if (strcmp(str, hstr) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Did we find the string?
|
||||
*/
|
||||
if (sthash == 0)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
* Has this string been copied into the string table?
|
||||
*/
|
||||
mstr = sthash->hi_mstr;
|
||||
if (mstr->sm_stroff == 0) {
|
||||
size_t mstrlen = mstr->sm_strlen + 1;
|
||||
|
||||
mstr->sm_stroff = stp->st_nextoff;
|
||||
|
||||
/*
|
||||
* Have we overflowed our assigned buffer?
|
||||
*/
|
||||
if ((mstr->sm_stroff + mstrlen) > stp->st_fullstrsize)
|
||||
return (-1);
|
||||
|
||||
(void) memcpy(stp->st_strbuf + mstr->sm_stroff,
|
||||
mstr->sm_str, mstrlen);
|
||||
stp->st_nextoff += mstrlen;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate offset of (sub)string.
|
||||
*/
|
||||
*stoff = mstr->sm_stroff + mstr->sm_strlen - sthash->hi_strlen;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
st_hash_insert(Str_tbl *stp, const char *str, size_t len)
|
||||
{
|
||||
int i;
|
||||
uint_t hashval = HASHSEED;
|
||||
uint_t bckcnt = stp->st_hbckcnt;
|
||||
Str_hash **hashbcks = stp->st_hashbcks;
|
||||
Str_hash *sthash;
|
||||
Str_master *mstr = 0;
|
||||
|
||||
/*
|
||||
* We use a classic 'Bernstein k=33' hash function. But
|
||||
* instead of hashing from the start of the string to the
|
||||
* end, we do it in reverse.
|
||||
*
|
||||
* This way - we are essentially building all of the
|
||||
* suffix hashvalues as we go. We can check to see if
|
||||
* any suffixes already exist in the tree as we generate
|
||||
* the hash.
|
||||
*/
|
||||
for (i = len; i >= 0; i--) {
|
||||
hashval = ((hashval << 5) + hashval) +
|
||||
str[i]; /* h = ((h * 33) + c) */
|
||||
|
||||
for (sthash = hashbcks[hashval % bckcnt];
|
||||
sthash; sthash = sthash->hi_next) {
|
||||
const char *hstr;
|
||||
Str_master *_mstr;
|
||||
|
||||
if (sthash->hi_hashval != hashval)
|
||||
continue;
|
||||
|
||||
_mstr = sthash->hi_mstr;
|
||||
hstr = &_mstr->sm_str[_mstr->sm_strlen -
|
||||
sthash->hi_strlen];
|
||||
|
||||
if (strcmp(&str[i], hstr))
|
||||
continue;
|
||||
|
||||
if (i == 0) {
|
||||
/*
|
||||
* Entry already in table, increment refcnt and
|
||||
* get out.
|
||||
*/
|
||||
sthash->hi_refcnt++;
|
||||
return (0);
|
||||
} else {
|
||||
/*
|
||||
* If this 'suffix' is presently a 'master
|
||||
* string, then take over it's record.
|
||||
*/
|
||||
if (sthash->hi_strlen == _mstr->sm_strlen) {
|
||||
/*
|
||||
* we should only do this once.
|
||||
*/
|
||||
assert(mstr == 0);
|
||||
mstr = _mstr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Do we need a new master string, or can we take over
|
||||
* one we already found in the table?
|
||||
*/
|
||||
if (mstr == 0) {
|
||||
/*
|
||||
* allocate a new master string
|
||||
*/
|
||||
if ((mstr = calloc(sizeof (Str_hash), 1)) == 0)
|
||||
return (-1);
|
||||
mstr->sm_next = stp->st_mstrlist;
|
||||
stp->st_mstrlist = mstr;
|
||||
stp->st_strsize += len + 1;
|
||||
} else {
|
||||
/*
|
||||
* We are taking over a existing master string, the string size
|
||||
* only increments by the difference between the current string
|
||||
* and the previous master.
|
||||
*/
|
||||
assert(len > mstr->sm_strlen);
|
||||
stp->st_strsize += len - mstr->sm_strlen;
|
||||
}
|
||||
|
||||
if ((sthash = calloc(sizeof (Str_hash), 1)) == 0)
|
||||
return (-1);
|
||||
|
||||
mstr->sm_hashval = sthash->hi_hashval = hashval;
|
||||
mstr->sm_strlen = sthash->hi_strlen = len;
|
||||
mstr->sm_str = str;
|
||||
sthash->hi_refcnt = 1;
|
||||
sthash->hi_mstr = mstr;
|
||||
|
||||
/*
|
||||
* Insert string element into head of hash list
|
||||
*/
|
||||
hashval = hashval % bckcnt;
|
||||
sthash->hi_next = hashbcks[hashval];
|
||||
hashbcks[hashval] = sthash;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return amount of space required for the string table.
|
||||
*/
|
||||
size_t
|
||||
st_getstrtab_sz(Str_tbl *stp)
|
||||
{
|
||||
assert(stp->st_fullstrsize > 0);
|
||||
|
||||
if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
|
||||
stp->st_flags |= FLG_STTAB_COOKED;
|
||||
return (stp->st_fullstrsize);
|
||||
}
|
||||
|
||||
if ((stp->st_flags & FLG_STTAB_COOKED) == 0) {
|
||||
LenNode *lnp;
|
||||
void *cookie;
|
||||
|
||||
stp->st_flags |= FLG_STTAB_COOKED;
|
||||
/*
|
||||
* allocate a hash table about the size of # of
|
||||
* strings input.
|
||||
*/
|
||||
stp->st_hbckcnt = findprime(stp->st_strcnt);
|
||||
if ((stp->st_hashbcks =
|
||||
calloc(sizeof (Str_hash), stp->st_hbckcnt)) == NULL)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* We now walk all of the strings in the list, from shortest to
|
||||
* longest, and insert them into the hashtable.
|
||||
*/
|
||||
if ((lnp = avl_first(stp->st_lentree)) == NULL) {
|
||||
/*
|
||||
* Is it possible we have an empty string table, if so,
|
||||
* the table still contains '\0', so return the size.
|
||||
*/
|
||||
if (avl_numnodes(stp->st_lentree) == 0) {
|
||||
assert(stp->st_strsize == 1);
|
||||
return (stp->st_strsize);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
while (lnp) {
|
||||
StrNode *snp;
|
||||
|
||||
/*
|
||||
* Walk the string lists and insert them into the hash
|
||||
* list. Once a string is inserted we no longer need
|
||||
* it's entry, so the string can be freed.
|
||||
*/
|
||||
for (snp = avl_first(lnp->ln_strtree); snp;
|
||||
snp = AVL_NEXT(lnp->ln_strtree, snp)) {
|
||||
if (st_hash_insert(stp, snp->sn_str,
|
||||
lnp->ln_strlen) == -1)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that the strings have been copied, walk the
|
||||
* StrNode tree and free all the AVL nodes. Note,
|
||||
* avl_destroy_nodes() beats avl_remove() as the
|
||||
* latter balances the nodes as they are removed.
|
||||
* We just want to tear the whole thing down fast.
|
||||
*/
|
||||
cookie = NULL;
|
||||
while ((snp = avl_destroy_nodes(lnp->ln_strtree,
|
||||
&cookie)) != NULL)
|
||||
free(snp);
|
||||
avl_destroy(lnp->ln_strtree);
|
||||
free(lnp->ln_strtree);
|
||||
lnp->ln_strtree = NULL;
|
||||
|
||||
/*
|
||||
* Move on to the next LenNode.
|
||||
*/
|
||||
lnp = AVL_NEXT(stp->st_lentree, lnp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Now that all of the strings have been freed, walk the
|
||||
* LenNode tree and free all of the AVL nodes. Note,
|
||||
* avl_destroy_nodes() beats avl_remove() as the latter
|
||||
* balances the nodes as they are removed. We just want to
|
||||
* tear the whole thing down fast.
|
||||
*/
|
||||
cookie = NULL;
|
||||
while ((lnp = avl_destroy_nodes(stp->st_lentree,
|
||||
&cookie)) != NULL)
|
||||
free(lnp);
|
||||
avl_destroy(stp->st_lentree);
|
||||
free(stp->st_lentree);
|
||||
stp->st_lentree = 0;
|
||||
}
|
||||
|
||||
assert(stp->st_strsize > 0);
|
||||
assert(stp->st_fullstrsize >= stp->st_strsize);
|
||||
|
||||
return (stp->st_strsize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Associate a buffer with a string table.
|
||||
*/
|
||||
const char *
|
||||
st_getstrbuf(Str_tbl *stp)
|
||||
{
|
||||
return (stp->st_strbuf);
|
||||
}
|
||||
|
||||
int
|
||||
st_setstrbuf(Str_tbl *stp, char *stbuf, size_t bufsize)
|
||||
{
|
||||
assert(stp->st_flags & FLG_STTAB_COOKED);
|
||||
|
||||
if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
|
||||
if (bufsize < stp->st_fullstrsize)
|
||||
return (-1);
|
||||
} else {
|
||||
if (bufsize < stp->st_strsize)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
stp->st_strbuf = stbuf;
|
||||
#ifdef DEBUG
|
||||
/*
|
||||
* for debug builds - start with a stringtable filled in
|
||||
* with '0xff'. This makes it very easy to find wholes
|
||||
* which we failed to fill in - in the strtab.
|
||||
*/
|
||||
memset(stbuf, 0xff, bufsize);
|
||||
stbuf[0] = '\0';
|
||||
#else
|
||||
memset(stbuf, 0x0, bufsize);
|
||||
#endif
|
||||
return (0);
|
||||
}
|
969
cddl/contrib/opensolaris/common/avl/avl.c
Normal file
969
cddl/contrib/opensolaris/common/avl/avl.c
Normal file
@ -0,0 +1,969 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
|
||||
/*
|
||||
* AVL - generic AVL tree implementation for kernel use
|
||||
*
|
||||
* A complete description of AVL trees can be found in many CS textbooks.
|
||||
*
|
||||
* Here is a very brief overview. An AVL tree is a binary search tree that is
|
||||
* almost perfectly balanced. By "almost" perfectly balanced, we mean that at
|
||||
* any given node, the left and right subtrees are allowed to differ in height
|
||||
* by at most 1 level.
|
||||
*
|
||||
* This relaxation from a perfectly balanced binary tree allows doing
|
||||
* insertion and deletion relatively efficiently. Searching the tree is
|
||||
* still a fast operation, roughly O(log(N)).
|
||||
*
|
||||
* The key to insertion and deletion is a set of tree maniuplations called
|
||||
* rotations, which bring unbalanced subtrees back into the semi-balanced state.
|
||||
*
|
||||
* This implementation of AVL trees has the following peculiarities:
|
||||
*
|
||||
* - The AVL specific data structures are physically embedded as fields
|
||||
* in the "using" data structures. To maintain generality the code
|
||||
* must constantly translate between "avl_node_t *" and containing
|
||||
* data structure "void *"s by adding/subracting the avl_offset.
|
||||
*
|
||||
* - Since the AVL data is always embedded in other structures, there is
|
||||
* no locking or memory allocation in the AVL routines. This must be
|
||||
* provided for by the enclosing data structure's semantics. Typically,
|
||||
* avl_insert()/_add()/_remove()/avl_insert_here() require some kind of
|
||||
* exclusive write lock. Other operations require a read lock.
|
||||
*
|
||||
* - The implementation uses iteration instead of explicit recursion,
|
||||
* since it is intended to run on limited size kernel stacks. Since
|
||||
* there is no recursion stack present to move "up" in the tree,
|
||||
* there is an explicit "parent" link in the avl_node_t.
|
||||
*
|
||||
* - The left/right children pointers of a node are in an array.
|
||||
* In the code, variables (instead of constants) are used to represent
|
||||
* left and right indices. The implementation is written as if it only
|
||||
* dealt with left handed manipulations. By changing the value assigned
|
||||
* to "left", the code also works for right handed trees. The
|
||||
* following variables/terms are frequently used:
|
||||
*
|
||||
* int left; // 0 when dealing with left children,
|
||||
* // 1 for dealing with right children
|
||||
*
|
||||
* int left_heavy; // -1 when left subtree is taller at some node,
|
||||
* // +1 when right subtree is taller
|
||||
*
|
||||
* int right; // will be the opposite of left (0 or 1)
|
||||
* int right_heavy;// will be the opposite of left_heavy (-1 or 1)
|
||||
*
|
||||
* int direction; // 0 for "<" (ie. left child); 1 for ">" (right)
|
||||
*
|
||||
* Though it is a little more confusing to read the code, the approach
|
||||
* allows using half as much code (and hence cache footprint) for tree
|
||||
* manipulations and eliminates many conditional branches.
|
||||
*
|
||||
* - The avl_index_t is an opaque "cookie" used to find nodes at or
|
||||
* adjacent to where a new value would be inserted in the tree. The value
|
||||
* is a modified "avl_node_t *". The bottom bit (normally 0 for a
|
||||
* pointer) is set to indicate if that the new node has a value greater
|
||||
* than the value of the indicated "avl_node_t *".
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/debug.h>
|
||||
#include <sys/avl.h>
|
||||
#include <sys/cmn_err.h>
|
||||
|
||||
/*
|
||||
* Small arrays to translate between balance (or diff) values and child indeces.
|
||||
*
|
||||
* Code that deals with binary tree data structures will randomly use
|
||||
* left and right children when examining a tree. C "if()" statements
|
||||
* which evaluate randomly suffer from very poor hardware branch prediction.
|
||||
* In this code we avoid some of the branch mispredictions by using the
|
||||
* following translation arrays. They replace random branches with an
|
||||
* additional memory reference. Since the translation arrays are both very
|
||||
* small the data should remain efficiently in cache.
|
||||
*/
|
||||
static const int avl_child2balance[2] = {-1, 1};
|
||||
static const int avl_balance2child[] = {0, 0, 1};
|
||||
|
||||
|
||||
/*
|
||||
* Walk from one node to the previous valued node (ie. an infix walk
|
||||
* towards the left). At any given node we do one of 2 things:
|
||||
*
|
||||
* - If there is a left child, go to it, then to it's rightmost descendant.
|
||||
*
|
||||
* - otherwise we return thru parent nodes until we've come from a right child.
|
||||
*
|
||||
* Return Value:
|
||||
* NULL - if at the end of the nodes
|
||||
* otherwise next node
|
||||
*/
|
||||
void *
|
||||
avl_walk(avl_tree_t *tree, void *oldnode, int left)
|
||||
{
|
||||
size_t off = tree->avl_offset;
|
||||
avl_node_t *node = AVL_DATA2NODE(oldnode, off);
|
||||
int right = 1 - left;
|
||||
int was_child;
|
||||
|
||||
|
||||
/*
|
||||
* nowhere to walk to if tree is empty
|
||||
*/
|
||||
if (node == NULL)
|
||||
return (NULL);
|
||||
|
||||
/*
|
||||
* Visit the previous valued node. There are two possibilities:
|
||||
*
|
||||
* If this node has a left child, go down one left, then all
|
||||
* the way right.
|
||||
*/
|
||||
if (node->avl_child[left] != NULL) {
|
||||
for (node = node->avl_child[left];
|
||||
node->avl_child[right] != NULL;
|
||||
node = node->avl_child[right])
|
||||
;
|
||||
/*
|
||||
* Otherwise, return thru left children as far as we can.
|
||||
*/
|
||||
} else {
|
||||
for (;;) {
|
||||
was_child = AVL_XCHILD(node);
|
||||
node = AVL_XPARENT(node);
|
||||
if (node == NULL)
|
||||
return (NULL);
|
||||
if (was_child == right)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (AVL_NODE2DATA(node, off));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the lowest valued node in a tree or NULL.
|
||||
* (leftmost child from root of tree)
|
||||
*/
|
||||
void *
|
||||
avl_first(avl_tree_t *tree)
|
||||
{
|
||||
avl_node_t *node;
|
||||
avl_node_t *prev = NULL;
|
||||
size_t off = tree->avl_offset;
|
||||
|
||||
for (node = tree->avl_root; node != NULL; node = node->avl_child[0])
|
||||
prev = node;
|
||||
|
||||
if (prev != NULL)
|
||||
return (AVL_NODE2DATA(prev, off));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the highest valued node in a tree or NULL.
|
||||
* (rightmost child from root of tree)
|
||||
*/
|
||||
void *
|
||||
avl_last(avl_tree_t *tree)
|
||||
{
|
||||
avl_node_t *node;
|
||||
avl_node_t *prev = NULL;
|
||||
size_t off = tree->avl_offset;
|
||||
|
||||
for (node = tree->avl_root; node != NULL; node = node->avl_child[1])
|
||||
prev = node;
|
||||
|
||||
if (prev != NULL)
|
||||
return (AVL_NODE2DATA(prev, off));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Access the node immediately before or after an insertion point.
|
||||
*
|
||||
* "avl_index_t" is a (avl_node_t *) with the bottom bit indicating a child
|
||||
*
|
||||
* Return value:
|
||||
* NULL: no node in the given direction
|
||||
* "void *" of the found tree node
|
||||
*/
|
||||
void *
|
||||
avl_nearest(avl_tree_t *tree, avl_index_t where, int direction)
|
||||
{
|
||||
int child = AVL_INDEX2CHILD(where);
|
||||
avl_node_t *node = AVL_INDEX2NODE(where);
|
||||
void *data;
|
||||
size_t off = tree->avl_offset;
|
||||
|
||||
if (node == NULL) {
|
||||
ASSERT(tree->avl_root == NULL);
|
||||
return (NULL);
|
||||
}
|
||||
data = AVL_NODE2DATA(node, off);
|
||||
if (child != direction)
|
||||
return (data);
|
||||
|
||||
return (avl_walk(tree, data, direction));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Search for the node which contains "value". The algorithm is a
|
||||
* simple binary tree search.
|
||||
*
|
||||
* return value:
|
||||
* NULL: the value is not in the AVL tree
|
||||
* *where (if not NULL) is set to indicate the insertion point
|
||||
* "void *" of the found tree node
|
||||
*/
|
||||
void *
|
||||
avl_find(avl_tree_t *tree, void *value, avl_index_t *where)
|
||||
{
|
||||
avl_node_t *node;
|
||||
avl_node_t *prev = NULL;
|
||||
int child = 0;
|
||||
int diff;
|
||||
size_t off = tree->avl_offset;
|
||||
|
||||
for (node = tree->avl_root; node != NULL;
|
||||
node = node->avl_child[child]) {
|
||||
|
||||
prev = node;
|
||||
|
||||
diff = tree->avl_compar(value, AVL_NODE2DATA(node, off));
|
||||
ASSERT(-1 <= diff && diff <= 1);
|
||||
if (diff == 0) {
|
||||
#ifdef DEBUG
|
||||
if (where != NULL)
|
||||
*where = 0;
|
||||
#endif
|
||||
return (AVL_NODE2DATA(node, off));
|
||||
}
|
||||
child = avl_balance2child[1 + diff];
|
||||
|
||||
}
|
||||
|
||||
if (where != NULL)
|
||||
*where = AVL_MKINDEX(prev, child);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Perform a rotation to restore balance at the subtree given by depth.
|
||||
*
|
||||
* This routine is used by both insertion and deletion. The return value
|
||||
* indicates:
|
||||
* 0 : subtree did not change height
|
||||
* !0 : subtree was reduced in height
|
||||
*
|
||||
* The code is written as if handling left rotations, right rotations are
|
||||
* symmetric and handled by swapping values of variables right/left[_heavy]
|
||||
*
|
||||
* On input balance is the "new" balance at "node". This value is either
|
||||
* -2 or +2.
|
||||
*/
|
||||
static int
|
||||
avl_rotation(avl_tree_t *tree, avl_node_t *node, int balance)
|
||||
{
|
||||
int left = !(balance < 0); /* when balance = -2, left will be 0 */
|
||||
int right = 1 - left;
|
||||
int left_heavy = balance >> 1;
|
||||
int right_heavy = -left_heavy;
|
||||
avl_node_t *parent = AVL_XPARENT(node);
|
||||
avl_node_t *child = node->avl_child[left];
|
||||
avl_node_t *cright;
|
||||
avl_node_t *gchild;
|
||||
avl_node_t *gright;
|
||||
avl_node_t *gleft;
|
||||
int which_child = AVL_XCHILD(node);
|
||||
int child_bal = AVL_XBALANCE(child);
|
||||
|
||||
/* BEGIN CSTYLED */
|
||||
/*
|
||||
* case 1 : node is overly left heavy, the left child is balanced or
|
||||
* also left heavy. This requires the following rotation.
|
||||
*
|
||||
* (node bal:-2)
|
||||
* / \
|
||||
* / \
|
||||
* (child bal:0 or -1)
|
||||
* / \
|
||||
* / \
|
||||
* cright
|
||||
*
|
||||
* becomes:
|
||||
*
|
||||
* (child bal:1 or 0)
|
||||
* / \
|
||||
* / \
|
||||
* (node bal:-1 or 0)
|
||||
* / \
|
||||
* / \
|
||||
* cright
|
||||
*
|
||||
* we detect this situation by noting that child's balance is not
|
||||
* right_heavy.
|
||||
*/
|
||||
/* END CSTYLED */
|
||||
if (child_bal != right_heavy) {
|
||||
|
||||
/*
|
||||
* compute new balance of nodes
|
||||
*
|
||||
* If child used to be left heavy (now balanced) we reduced
|
||||
* the height of this sub-tree -- used in "return...;" below
|
||||
*/
|
||||
child_bal += right_heavy; /* adjust towards right */
|
||||
|
||||
/*
|
||||
* move "cright" to be node's left child
|
||||
*/
|
||||
cright = child->avl_child[right];
|
||||
node->avl_child[left] = cright;
|
||||
if (cright != NULL) {
|
||||
AVL_SETPARENT(cright, node);
|
||||
AVL_SETCHILD(cright, left);
|
||||
}
|
||||
|
||||
/*
|
||||
* move node to be child's right child
|
||||
*/
|
||||
child->avl_child[right] = node;
|
||||
AVL_SETBALANCE(node, -child_bal);
|
||||
AVL_SETCHILD(node, right);
|
||||
AVL_SETPARENT(node, child);
|
||||
|
||||
/*
|
||||
* update the pointer into this subtree
|
||||
*/
|
||||
AVL_SETBALANCE(child, child_bal);
|
||||
AVL_SETCHILD(child, which_child);
|
||||
AVL_SETPARENT(child, parent);
|
||||
if (parent != NULL)
|
||||
parent->avl_child[which_child] = child;
|
||||
else
|
||||
tree->avl_root = child;
|
||||
|
||||
return (child_bal == 0);
|
||||
}
|
||||
|
||||
/* BEGIN CSTYLED */
|
||||
/*
|
||||
* case 2 : When node is left heavy, but child is right heavy we use
|
||||
* a different rotation.
|
||||
*
|
||||
* (node b:-2)
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* (child b:+1)
|
||||
* / \
|
||||
* / \
|
||||
* (gchild b: != 0)
|
||||
* / \
|
||||
* / \
|
||||
* gleft gright
|
||||
*
|
||||
* becomes:
|
||||
*
|
||||
* (gchild b:0)
|
||||
* / \
|
||||
* / \
|
||||
* / \
|
||||
* (child b:?) (node b:?)
|
||||
* / \ / \
|
||||
* / \ / \
|
||||
* gleft gright
|
||||
*
|
||||
* computing the new balances is more complicated. As an example:
|
||||
* if gchild was right_heavy, then child is now left heavy
|
||||
* else it is balanced
|
||||
*/
|
||||
/* END CSTYLED */
|
||||
gchild = child->avl_child[right];
|
||||
gleft = gchild->avl_child[left];
|
||||
gright = gchild->avl_child[right];
|
||||
|
||||
/*
|
||||
* move gright to left child of node and
|
||||
*
|
||||
* move gleft to right child of node
|
||||
*/
|
||||
node->avl_child[left] = gright;
|
||||
if (gright != NULL) {
|
||||
AVL_SETPARENT(gright, node);
|
||||
AVL_SETCHILD(gright, left);
|
||||
}
|
||||
|
||||
child->avl_child[right] = gleft;
|
||||
if (gleft != NULL) {
|
||||
AVL_SETPARENT(gleft, child);
|
||||
AVL_SETCHILD(gleft, right);
|
||||
}
|
||||
|
||||
/*
|
||||
* move child to left child of gchild and
|
||||
*
|
||||
* move node to right child of gchild and
|
||||
*
|
||||
* fixup parent of all this to point to gchild
|
||||
*/
|
||||
balance = AVL_XBALANCE(gchild);
|
||||
gchild->avl_child[left] = child;
|
||||
AVL_SETBALANCE(child, (balance == right_heavy ? left_heavy : 0));
|
||||
AVL_SETPARENT(child, gchild);
|
||||
AVL_SETCHILD(child, left);
|
||||
|
||||
gchild->avl_child[right] = node;
|
||||
AVL_SETBALANCE(node, (balance == left_heavy ? right_heavy : 0));
|
||||
AVL_SETPARENT(node, gchild);
|
||||
AVL_SETCHILD(node, right);
|
||||
|
||||
AVL_SETBALANCE(gchild, 0);
|
||||
AVL_SETPARENT(gchild, parent);
|
||||
AVL_SETCHILD(gchild, which_child);
|
||||
if (parent != NULL)
|
||||
parent->avl_child[which_child] = gchild;
|
||||
else
|
||||
tree->avl_root = gchild;
|
||||
|
||||
return (1); /* the new tree is always shorter */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Insert a new node into an AVL tree at the specified (from avl_find()) place.
|
||||
*
|
||||
* Newly inserted nodes are always leaf nodes in the tree, since avl_find()
|
||||
* searches out to the leaf positions. The avl_index_t indicates the node
|
||||
* which will be the parent of the new node.
|
||||
*
|
||||
* After the node is inserted, a single rotation further up the tree may
|
||||
* be necessary to maintain an acceptable AVL balance.
|
||||
*/
|
||||
void
|
||||
avl_insert(avl_tree_t *tree, void *new_data, avl_index_t where)
|
||||
{
|
||||
avl_node_t *node;
|
||||
avl_node_t *parent = AVL_INDEX2NODE(where);
|
||||
int old_balance;
|
||||
int new_balance;
|
||||
int which_child = AVL_INDEX2CHILD(where);
|
||||
size_t off = tree->avl_offset;
|
||||
|
||||
ASSERT(tree);
|
||||
#ifdef _LP64
|
||||
ASSERT(((uintptr_t)new_data & 0x7) == 0);
|
||||
#endif
|
||||
|
||||
node = AVL_DATA2NODE(new_data, off);
|
||||
|
||||
/*
|
||||
* First, add the node to the tree at the indicated position.
|
||||
*/
|
||||
++tree->avl_numnodes;
|
||||
|
||||
node->avl_child[0] = NULL;
|
||||
node->avl_child[1] = NULL;
|
||||
|
||||
AVL_SETCHILD(node, which_child);
|
||||
AVL_SETBALANCE(node, 0);
|
||||
AVL_SETPARENT(node, parent);
|
||||
if (parent != NULL) {
|
||||
ASSERT(parent->avl_child[which_child] == NULL);
|
||||
parent->avl_child[which_child] = node;
|
||||
} else {
|
||||
ASSERT(tree->avl_root == NULL);
|
||||
tree->avl_root = node;
|
||||
}
|
||||
/*
|
||||
* Now, back up the tree modifying the balance of all nodes above the
|
||||
* insertion point. If we get to a highly unbalanced ancestor, we
|
||||
* need to do a rotation. If we back out of the tree we are done.
|
||||
* If we brought any subtree into perfect balance (0), we are also done.
|
||||
*/
|
||||
for (;;) {
|
||||
node = parent;
|
||||
if (node == NULL)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Compute the new balance
|
||||
*/
|
||||
old_balance = AVL_XBALANCE(node);
|
||||
new_balance = old_balance + avl_child2balance[which_child];
|
||||
|
||||
/*
|
||||
* If we introduced equal balance, then we are done immediately
|
||||
*/
|
||||
if (new_balance == 0) {
|
||||
AVL_SETBALANCE(node, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If both old and new are not zero we went
|
||||
* from -1 to -2 balance, do a rotation.
|
||||
*/
|
||||
if (old_balance != 0)
|
||||
break;
|
||||
|
||||
AVL_SETBALANCE(node, new_balance);
|
||||
parent = AVL_XPARENT(node);
|
||||
which_child = AVL_XCHILD(node);
|
||||
}
|
||||
|
||||
/*
|
||||
* perform a rotation to fix the tree and return
|
||||
*/
|
||||
(void) avl_rotation(tree, node, new_balance);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert "new_data" in "tree" in the given "direction" either after or
|
||||
* before (AVL_AFTER, AVL_BEFORE) the data "here".
|
||||
*
|
||||
* Insertions can only be done at empty leaf points in the tree, therefore
|
||||
* if the given child of the node is already present we move to either
|
||||
* the AVL_PREV or AVL_NEXT and reverse the insertion direction. Since
|
||||
* every other node in the tree is a leaf, this always works.
|
||||
*
|
||||
* To help developers using this interface, we assert that the new node
|
||||
* is correctly ordered at every step of the way in DEBUG kernels.
|
||||
*/
|
||||
void
|
||||
avl_insert_here(
|
||||
avl_tree_t *tree,
|
||||
void *new_data,
|
||||
void *here,
|
||||
int direction)
|
||||
{
|
||||
avl_node_t *node;
|
||||
int child = direction; /* rely on AVL_BEFORE == 0, AVL_AFTER == 1 */
|
||||
#ifdef DEBUG
|
||||
int diff;
|
||||
#endif
|
||||
|
||||
ASSERT(tree != NULL);
|
||||
ASSERT(new_data != NULL);
|
||||
ASSERT(here != NULL);
|
||||
ASSERT(direction == AVL_BEFORE || direction == AVL_AFTER);
|
||||
|
||||
/*
|
||||
* If corresponding child of node is not NULL, go to the neighboring
|
||||
* node and reverse the insertion direction.
|
||||
*/
|
||||
node = AVL_DATA2NODE(here, tree->avl_offset);
|
||||
|
||||
#ifdef DEBUG
|
||||
diff = tree->avl_compar(new_data, here);
|
||||
ASSERT(-1 <= diff && diff <= 1);
|
||||
ASSERT(diff != 0);
|
||||
ASSERT(diff > 0 ? child == 1 : child == 0);
|
||||
#endif
|
||||
|
||||
if (node->avl_child[child] != NULL) {
|
||||
node = node->avl_child[child];
|
||||
child = 1 - child;
|
||||
while (node->avl_child[child] != NULL) {
|
||||
#ifdef DEBUG
|
||||
diff = tree->avl_compar(new_data,
|
||||
AVL_NODE2DATA(node, tree->avl_offset));
|
||||
ASSERT(-1 <= diff && diff <= 1);
|
||||
ASSERT(diff != 0);
|
||||
ASSERT(diff > 0 ? child == 1 : child == 0);
|
||||
#endif
|
||||
node = node->avl_child[child];
|
||||
}
|
||||
#ifdef DEBUG
|
||||
diff = tree->avl_compar(new_data,
|
||||
AVL_NODE2DATA(node, tree->avl_offset));
|
||||
ASSERT(-1 <= diff && diff <= 1);
|
||||
ASSERT(diff != 0);
|
||||
ASSERT(diff > 0 ? child == 1 : child == 0);
|
||||
#endif
|
||||
}
|
||||
ASSERT(node->avl_child[child] == NULL);
|
||||
|
||||
avl_insert(tree, new_data, AVL_MKINDEX(node, child));
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a new node to an AVL tree.
|
||||
*/
|
||||
void
|
||||
avl_add(avl_tree_t *tree, void *new_node)
|
||||
{
|
||||
avl_index_t where;
|
||||
|
||||
/*
|
||||
* This is unfortunate. We want to call panic() here, even for
|
||||
* non-DEBUG kernels. In userland, however, we can't depend on anything
|
||||
* in libc or else the rtld build process gets confused. So, all we can
|
||||
* do in userland is resort to a normal ASSERT().
|
||||
*/
|
||||
if (avl_find(tree, new_node, &where) != NULL)
|
||||
#ifdef _KERNEL
|
||||
panic("avl_find() succeeded inside avl_add()");
|
||||
#else
|
||||
ASSERT(0);
|
||||
#endif
|
||||
avl_insert(tree, new_node, where);
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a node from the AVL tree. Deletion is similar to insertion, but
|
||||
* with 2 complications.
|
||||
*
|
||||
* First, we may be deleting an interior node. Consider the following subtree:
|
||||
*
|
||||
* d c c
|
||||
* / \ / \ / \
|
||||
* b e b e b e
|
||||
* / \ / \ /
|
||||
* a c a a
|
||||
*
|
||||
* When we are deleting node (d), we find and bring up an adjacent valued leaf
|
||||
* node, say (c), to take the interior node's place. In the code this is
|
||||
* handled by temporarily swapping (d) and (c) in the tree and then using
|
||||
* common code to delete (d) from the leaf position.
|
||||
*
|
||||
* Secondly, an interior deletion from a deep tree may require more than one
|
||||
* rotation to fix the balance. This is handled by moving up the tree through
|
||||
* parents and applying rotations as needed. The return value from
|
||||
* avl_rotation() is used to detect when a subtree did not change overall
|
||||
* height due to a rotation.
|
||||
*/
|
||||
void
|
||||
avl_remove(avl_tree_t *tree, void *data)
|
||||
{
|
||||
avl_node_t *delete;
|
||||
avl_node_t *parent;
|
||||
avl_node_t *node;
|
||||
avl_node_t tmp;
|
||||
int old_balance;
|
||||
int new_balance;
|
||||
int left;
|
||||
int right;
|
||||
int which_child;
|
||||
size_t off = tree->avl_offset;
|
||||
|
||||
ASSERT(tree);
|
||||
|
||||
delete = AVL_DATA2NODE(data, off);
|
||||
|
||||
/*
|
||||
* Deletion is easiest with a node that has at most 1 child.
|
||||
* We swap a node with 2 children with a sequentially valued
|
||||
* neighbor node. That node will have at most 1 child. Note this
|
||||
* has no effect on the ordering of the remaining nodes.
|
||||
*
|
||||
* As an optimization, we choose the greater neighbor if the tree
|
||||
* is right heavy, otherwise the left neighbor. This reduces the
|
||||
* number of rotations needed.
|
||||
*/
|
||||
if (delete->avl_child[0] != NULL && delete->avl_child[1] != NULL) {
|
||||
|
||||
/*
|
||||
* choose node to swap from whichever side is taller
|
||||
*/
|
||||
old_balance = AVL_XBALANCE(delete);
|
||||
left = avl_balance2child[old_balance + 1];
|
||||
right = 1 - left;
|
||||
|
||||
/*
|
||||
* get to the previous value'd node
|
||||
* (down 1 left, as far as possible right)
|
||||
*/
|
||||
for (node = delete->avl_child[left];
|
||||
node->avl_child[right] != NULL;
|
||||
node = node->avl_child[right])
|
||||
;
|
||||
|
||||
/*
|
||||
* create a temp placeholder for 'node'
|
||||
* move 'node' to delete's spot in the tree
|
||||
*/
|
||||
tmp = *node;
|
||||
|
||||
*node = *delete;
|
||||
if (node->avl_child[left] == node)
|
||||
node->avl_child[left] = &tmp;
|
||||
|
||||
parent = AVL_XPARENT(node);
|
||||
if (parent != NULL)
|
||||
parent->avl_child[AVL_XCHILD(node)] = node;
|
||||
else
|
||||
tree->avl_root = node;
|
||||
AVL_SETPARENT(node->avl_child[left], node);
|
||||
AVL_SETPARENT(node->avl_child[right], node);
|
||||
|
||||
/*
|
||||
* Put tmp where node used to be (just temporary).
|
||||
* It always has a parent and at most 1 child.
|
||||
*/
|
||||
delete = &tmp;
|
||||
parent = AVL_XPARENT(delete);
|
||||
parent->avl_child[AVL_XCHILD(delete)] = delete;
|
||||
which_child = (delete->avl_child[1] != 0);
|
||||
if (delete->avl_child[which_child] != NULL)
|
||||
AVL_SETPARENT(delete->avl_child[which_child], delete);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Here we know "delete" is at least partially a leaf node. It can
|
||||
* be easily removed from the tree.
|
||||
*/
|
||||
ASSERT(tree->avl_numnodes > 0);
|
||||
--tree->avl_numnodes;
|
||||
parent = AVL_XPARENT(delete);
|
||||
which_child = AVL_XCHILD(delete);
|
||||
if (delete->avl_child[0] != NULL)
|
||||
node = delete->avl_child[0];
|
||||
else
|
||||
node = delete->avl_child[1];
|
||||
|
||||
/*
|
||||
* Connect parent directly to node (leaving out delete).
|
||||
*/
|
||||
if (node != NULL) {
|
||||
AVL_SETPARENT(node, parent);
|
||||
AVL_SETCHILD(node, which_child);
|
||||
}
|
||||
if (parent == NULL) {
|
||||
tree->avl_root = node;
|
||||
return;
|
||||
}
|
||||
parent->avl_child[which_child] = node;
|
||||
|
||||
|
||||
/*
|
||||
* Since the subtree is now shorter, begin adjusting parent balances
|
||||
* and performing any needed rotations.
|
||||
*/
|
||||
do {
|
||||
|
||||
/*
|
||||
* Move up the tree and adjust the balance
|
||||
*
|
||||
* Capture the parent and which_child values for the next
|
||||
* iteration before any rotations occur.
|
||||
*/
|
||||
node = parent;
|
||||
old_balance = AVL_XBALANCE(node);
|
||||
new_balance = old_balance - avl_child2balance[which_child];
|
||||
parent = AVL_XPARENT(node);
|
||||
which_child = AVL_XCHILD(node);
|
||||
|
||||
/*
|
||||
* If a node was in perfect balance but isn't anymore then
|
||||
* we can stop, since the height didn't change above this point
|
||||
* due to a deletion.
|
||||
*/
|
||||
if (old_balance == 0) {
|
||||
AVL_SETBALANCE(node, new_balance);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the new balance is zero, we don't need to rotate
|
||||
* else
|
||||
* need a rotation to fix the balance.
|
||||
* If the rotation doesn't change the height
|
||||
* of the sub-tree we have finished adjusting.
|
||||
*/
|
||||
if (new_balance == 0)
|
||||
AVL_SETBALANCE(node, new_balance);
|
||||
else if (!avl_rotation(tree, node, new_balance))
|
||||
break;
|
||||
} while (parent != NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* initialize a new AVL tree
|
||||
*/
|
||||
void
|
||||
avl_create(avl_tree_t *tree, int (*compar) (const void *, const void *),
|
||||
size_t size, size_t offset)
|
||||
{
|
||||
ASSERT(tree);
|
||||
ASSERT(compar);
|
||||
ASSERT(size > 0);
|
||||
ASSERT(size >= offset + sizeof (avl_node_t));
|
||||
#ifdef _LP64
|
||||
ASSERT((offset & 0x7) == 0);
|
||||
#endif
|
||||
|
||||
tree->avl_compar = compar;
|
||||
tree->avl_root = NULL;
|
||||
tree->avl_numnodes = 0;
|
||||
tree->avl_size = size;
|
||||
tree->avl_offset = offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete a tree.
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
void
|
||||
avl_destroy(avl_tree_t *tree)
|
||||
{
|
||||
ASSERT(tree);
|
||||
ASSERT(tree->avl_numnodes == 0);
|
||||
ASSERT(tree->avl_root == NULL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the number of nodes in an AVL tree.
|
||||
*/
|
||||
ulong_t
|
||||
avl_numnodes(avl_tree_t *tree)
|
||||
{
|
||||
ASSERT(tree);
|
||||
return (tree->avl_numnodes);
|
||||
}
|
||||
|
||||
|
||||
#define CHILDBIT (1L)
|
||||
|
||||
/*
|
||||
* Post-order tree walk used to visit all tree nodes and destroy the tree
|
||||
* in post order. This is used for destroying a tree w/o paying any cost
|
||||
* for rebalancing it.
|
||||
*
|
||||
* example:
|
||||
*
|
||||
* void *cookie = NULL;
|
||||
* my_data_t *node;
|
||||
*
|
||||
* while ((node = avl_destroy_nodes(tree, &cookie)) != NULL)
|
||||
* free(node);
|
||||
* avl_destroy(tree);
|
||||
*
|
||||
* The cookie is really an avl_node_t to the current node's parent and
|
||||
* an indication of which child you looked at last.
|
||||
*
|
||||
* On input, a cookie value of CHILDBIT indicates the tree is done.
|
||||
*/
|
||||
void *
|
||||
avl_destroy_nodes(avl_tree_t *tree, void **cookie)
|
||||
{
|
||||
avl_node_t *node;
|
||||
avl_node_t *parent;
|
||||
int child;
|
||||
void *first;
|
||||
size_t off = tree->avl_offset;
|
||||
|
||||
/*
|
||||
* Initial calls go to the first node or it's right descendant.
|
||||
*/
|
||||
if (*cookie == NULL) {
|
||||
first = avl_first(tree);
|
||||
|
||||
/*
|
||||
* deal with an empty tree
|
||||
*/
|
||||
if (first == NULL) {
|
||||
*cookie = (void *)CHILDBIT;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
node = AVL_DATA2NODE(first, off);
|
||||
parent = AVL_XPARENT(node);
|
||||
goto check_right_side;
|
||||
}
|
||||
|
||||
/*
|
||||
* If there is no parent to return to we are done.
|
||||
*/
|
||||
parent = (avl_node_t *)((uintptr_t)(*cookie) & ~CHILDBIT);
|
||||
if (parent == NULL) {
|
||||
if (tree->avl_root != NULL) {
|
||||
ASSERT(tree->avl_numnodes == 1);
|
||||
tree->avl_root = NULL;
|
||||
tree->avl_numnodes = 0;
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove the child pointer we just visited from the parent and tree.
|
||||
*/
|
||||
child = (uintptr_t)(*cookie) & CHILDBIT;
|
||||
parent->avl_child[child] = NULL;
|
||||
ASSERT(tree->avl_numnodes > 1);
|
||||
--tree->avl_numnodes;
|
||||
|
||||
/*
|
||||
* If we just did a right child or there isn't one, go up to parent.
|
||||
*/
|
||||
if (child == 1 || parent->avl_child[1] == NULL) {
|
||||
node = parent;
|
||||
parent = AVL_XPARENT(parent);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do parent's right child, then leftmost descendent.
|
||||
*/
|
||||
node = parent->avl_child[1];
|
||||
while (node->avl_child[0] != NULL) {
|
||||
parent = node;
|
||||
node = node->avl_child[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* If here, we moved to a left child. It may have one
|
||||
* child on the right (when balance == +1).
|
||||
*/
|
||||
check_right_side:
|
||||
if (node->avl_child[1] != NULL) {
|
||||
ASSERT(AVL_XBALANCE(node) == 1);
|
||||
parent = node;
|
||||
node = node->avl_child[1];
|
||||
ASSERT(node->avl_child[0] == NULL &&
|
||||
node->avl_child[1] == NULL);
|
||||
} else {
|
||||
ASSERT(AVL_XBALANCE(node) <= 0);
|
||||
}
|
||||
|
||||
done:
|
||||
if (parent == NULL) {
|
||||
*cookie = (void *)CHILDBIT;
|
||||
ASSERT(node == tree->avl_root);
|
||||
} else {
|
||||
*cookie = (void *)((uintptr_t)parent | AVL_XCHILD(node));
|
||||
}
|
||||
|
||||
return (AVL_NODE2DATA(node, off));
|
||||
}
|
1366
cddl/contrib/opensolaris/common/ctf/ctf_create.c
Normal file
1366
cddl/contrib/opensolaris/common/ctf/ctf_create.c
Normal file
File diff suppressed because it is too large
Load Diff
184
cddl/contrib/opensolaris/common/ctf/ctf_decl.c
Normal file
184
cddl/contrib/opensolaris/common/ctf/ctf_decl.c
Normal file
@ -0,0 +1,184 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
/*
|
||||
* CTF Declaration Stack
|
||||
*
|
||||
* In order to implement ctf_type_name(), we must convert a type graph back
|
||||
* into a C type declaration. Unfortunately, a type graph represents a storage
|
||||
* class ordering of the type whereas a type declaration must obey the C rules
|
||||
* for operator precedence, and the two orderings are frequently in conflict.
|
||||
* For example, consider these CTF type graphs and their C declarations:
|
||||
*
|
||||
* CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER : int (*)()
|
||||
* CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER : int (*)[]
|
||||
*
|
||||
* In each case, parentheses are used to raise operator * to higher lexical
|
||||
* precedence, so the string form of the C declaration cannot be constructed by
|
||||
* walking the type graph links and forming the string from left to right.
|
||||
*
|
||||
* The functions in this file build a set of stacks from the type graph nodes
|
||||
* corresponding to the C operator precedence levels in the appropriate order.
|
||||
* The code in ctf_type_name() can then iterate over the levels and nodes in
|
||||
* lexical precedence order and construct the final C declaration string.
|
||||
*/
|
||||
|
||||
#include <ctf_impl.h>
|
||||
|
||||
void
|
||||
ctf_decl_init(ctf_decl_t *cd, char *buf, size_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
bzero(cd, sizeof (ctf_decl_t));
|
||||
|
||||
for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++)
|
||||
cd->cd_order[i] = CTF_PREC_BASE - 1;
|
||||
|
||||
cd->cd_qualp = CTF_PREC_BASE;
|
||||
cd->cd_ordp = CTF_PREC_BASE;
|
||||
|
||||
cd->cd_buf = buf;
|
||||
cd->cd_ptr = buf;
|
||||
cd->cd_end = buf + len;
|
||||
}
|
||||
|
||||
void
|
||||
ctf_decl_fini(ctf_decl_t *cd)
|
||||
{
|
||||
ctf_decl_node_t *cdp, *ndp;
|
||||
int i;
|
||||
|
||||
for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) {
|
||||
for (cdp = ctf_list_next(&cd->cd_nodes[i]);
|
||||
cdp != NULL; cdp = ndp) {
|
||||
ndp = ctf_list_next(cdp);
|
||||
ctf_free(cdp, sizeof (ctf_decl_node_t));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ctf_decl_push(ctf_decl_t *cd, ctf_file_t *fp, ctf_id_t type)
|
||||
{
|
||||
ctf_decl_node_t *cdp;
|
||||
ctf_decl_prec_t prec;
|
||||
uint_t kind, n = 1;
|
||||
int is_qual = 0;
|
||||
|
||||
const ctf_type_t *tp;
|
||||
ctf_arinfo_t ar;
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) {
|
||||
cd->cd_err = fp->ctf_errno;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (kind = LCTF_INFO_KIND(fp, tp->ctt_info)) {
|
||||
case CTF_K_ARRAY:
|
||||
(void) ctf_array_info(fp, type, &ar);
|
||||
ctf_decl_push(cd, fp, ar.ctr_contents);
|
||||
n = ar.ctr_nelems;
|
||||
prec = CTF_PREC_ARRAY;
|
||||
break;
|
||||
|
||||
case CTF_K_TYPEDEF:
|
||||
if (ctf_strptr(fp, tp->ctt_name)[0] == '\0') {
|
||||
ctf_decl_push(cd, fp, tp->ctt_type);
|
||||
return;
|
||||
}
|
||||
prec = CTF_PREC_BASE;
|
||||
break;
|
||||
|
||||
case CTF_K_FUNCTION:
|
||||
ctf_decl_push(cd, fp, tp->ctt_type);
|
||||
prec = CTF_PREC_FUNCTION;
|
||||
break;
|
||||
|
||||
case CTF_K_POINTER:
|
||||
ctf_decl_push(cd, fp, tp->ctt_type);
|
||||
prec = CTF_PREC_POINTER;
|
||||
break;
|
||||
|
||||
case CTF_K_VOLATILE:
|
||||
case CTF_K_CONST:
|
||||
case CTF_K_RESTRICT:
|
||||
ctf_decl_push(cd, fp, tp->ctt_type);
|
||||
prec = cd->cd_qualp;
|
||||
is_qual++;
|
||||
break;
|
||||
|
||||
default:
|
||||
prec = CTF_PREC_BASE;
|
||||
}
|
||||
|
||||
if ((cdp = ctf_alloc(sizeof (ctf_decl_node_t))) == NULL) {
|
||||
cd->cd_err = EAGAIN;
|
||||
return;
|
||||
}
|
||||
|
||||
cdp->cd_type = type;
|
||||
cdp->cd_kind = kind;
|
||||
cdp->cd_n = n;
|
||||
|
||||
if (ctf_list_next(&cd->cd_nodes[prec]) == NULL)
|
||||
cd->cd_order[prec] = cd->cd_ordp++;
|
||||
|
||||
/*
|
||||
* Reset cd_qualp to the highest precedence level that we've seen so
|
||||
* far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER).
|
||||
*/
|
||||
if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY)
|
||||
cd->cd_qualp = prec;
|
||||
|
||||
/*
|
||||
* C array declarators are ordered inside out so prepend them. Also by
|
||||
* convention qualifiers of base types precede the type specifier (e.g.
|
||||
* const int vs. int const) even though the two forms are equivalent.
|
||||
*/
|
||||
if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE))
|
||||
ctf_list_prepend(&cd->cd_nodes[prec], cdp);
|
||||
else
|
||||
ctf_list_append(&cd->cd_nodes[prec], cdp);
|
||||
}
|
||||
|
||||
/*PRINTFLIKE2*/
|
||||
void
|
||||
ctf_decl_sprintf(ctf_decl_t *cd, const char *format, ...)
|
||||
{
|
||||
size_t len = (size_t)(cd->cd_end - cd->cd_ptr);
|
||||
va_list ap;
|
||||
size_t n;
|
||||
|
||||
va_start(ap, format);
|
||||
n = vsnprintf(cd->cd_ptr, len, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
cd->cd_ptr += MIN(n, len);
|
||||
cd->cd_len += n;
|
||||
}
|
97
cddl/contrib/opensolaris/common/ctf/ctf_error.c
Normal file
97
cddl/contrib/opensolaris/common/ctf/ctf_error.c
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <ctf_impl.h>
|
||||
|
||||
static const char *const _ctf_errlist[] = {
|
||||
"File is not in CTF or ELF format", /* ECTF_FMT */
|
||||
"File uses more recent ELF version than libctf", /* ECTF_ELFVERS */
|
||||
"File uses more recent CTF version than libctf", /* ECTF_CTFVERS */
|
||||
"File is a different endian-ness than libctf", /* ECTF_ENDIAN */
|
||||
"Symbol table uses invalid entry size", /* ECTF_SYMTAB */
|
||||
"Symbol table data buffer is not valid", /* ECTF_SYMBAD */
|
||||
"String table data buffer is not valid", /* ECTF_STRBAD */
|
||||
"File data structure corruption detected", /* ECTF_CORRUPT */
|
||||
"File does not contain CTF data", /* ECTF_NOCTFDATA */
|
||||
"Buffer does not contain CTF data", /* ECTF_NOCTFBUF */
|
||||
"Symbol table information is not available", /* ECTF_NOSYMTAB */
|
||||
"Type information is in parent and unavailable", /* ECTF_NOPARENT */
|
||||
"Cannot import types with different data model", /* ECTF_DMODEL */
|
||||
"Failed to mmap a needed data section", /* ECTF_MMAP */
|
||||
"Decompression package SUNWzlib not installed", /* ECTF_ZMISSING */
|
||||
"Failed to initialize decompression library", /* ECTF_ZINIT */
|
||||
"Failed to allocate decompression buffer", /* ECTF_ZALLOC */
|
||||
"Failed to decompress CTF data", /* ECTF_DECOMPRESS */
|
||||
"External string table is not available", /* ECTF_STRTAB */
|
||||
"String name offset is corrupt", /* ECTF_BADNAME */
|
||||
"Invalid type identifier", /* ECTF_BADID */
|
||||
"Type is not a struct or union", /* ECTF_NOTSOU */
|
||||
"Type is not an enum", /* ECTF_NOTENUM */
|
||||
"Type is not a struct, union, or enum", /* ECTF_NOTSUE */
|
||||
"Type is not an integer or float", /* ECTF_NOTINTFP */
|
||||
"Type is not an array", /* ECTF_NOTARRAY */
|
||||
"Type does not reference another type", /* ECTF_NOTREF */
|
||||
"Input buffer is too small for type name", /* ECTF_NAMELEN */
|
||||
"No type information available for that name", /* ECTF_NOTYPE */
|
||||
"Syntax error in type name", /* ECTF_SYNTAX */
|
||||
"Symbol table entry is not a function", /* ECTF_NOTFUNC */
|
||||
"No function information available for symbol", /* ECTF_NOFUNCDAT */
|
||||
"Symbol table entry is not a data object", /* ECTF_NOTDATA */
|
||||
"No type information available for symbol", /* ECTF_NOTYPEDAT */
|
||||
"No label information available for that name", /* ECTF_NOLABEL */
|
||||
"File does not contain any labels", /* ECTF_NOLABELDATA */
|
||||
"Feature not supported", /* ECTF_NOTSUP */
|
||||
"Invalid enum element name", /* ECTF_NOENUMNAM */
|
||||
"Invalid member name", /* ECTF_NOMEMBNAM */
|
||||
"CTF container is read-only", /* ECTF_RDONLY */
|
||||
"Limit on number of dynamic type members reached", /* ECTF_DTFULL */
|
||||
"Limit on number of dynamic types reached", /* ECTF_FULL */
|
||||
"Duplicate member name definition", /* ECTF_DUPMEMBER */
|
||||
"Conflicting type is already defined", /* ECTF_CONFLICT */
|
||||
};
|
||||
|
||||
static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]);
|
||||
|
||||
const char *
|
||||
ctf_errmsg(int error)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
if (error >= ECTF_BASE && (error - ECTF_BASE) < _ctf_nerr)
|
||||
str = _ctf_errlist[error - ECTF_BASE];
|
||||
else
|
||||
str = ctf_strerror(error);
|
||||
|
||||
return (str ? str : "Unknown error");
|
||||
}
|
||||
|
||||
int
|
||||
ctf_errno(ctf_file_t *fp)
|
||||
{
|
||||
return (fp->ctf_errno);
|
||||
}
|
178
cddl/contrib/opensolaris/common/ctf/ctf_hash.c
Normal file
178
cddl/contrib/opensolaris/common/ctf/ctf_hash.c
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <ctf_impl.h>
|
||||
|
||||
static const ushort_t _CTF_EMPTY[1] = { 0 };
|
||||
|
||||
int
|
||||
ctf_hash_create(ctf_hash_t *hp, ulong_t nelems)
|
||||
{
|
||||
if (nelems > USHRT_MAX)
|
||||
return (EOVERFLOW);
|
||||
|
||||
/*
|
||||
* If the hash table is going to be empty, don't bother allocating any
|
||||
* memory and make the only bucket point to a zero so lookups fail.
|
||||
*/
|
||||
if (nelems == 0) {
|
||||
bzero(hp, sizeof (ctf_hash_t));
|
||||
hp->h_buckets = (ushort_t *)_CTF_EMPTY;
|
||||
hp->h_nbuckets = 1;
|
||||
return (0);
|
||||
}
|
||||
|
||||
hp->h_nbuckets = 211; /* use a prime number of hash buckets */
|
||||
hp->h_nelems = nelems + 1; /* we use index zero as a sentinel */
|
||||
hp->h_free = 1; /* first free element is index 1 */
|
||||
|
||||
hp->h_buckets = ctf_alloc(sizeof (ushort_t) * hp->h_nbuckets);
|
||||
hp->h_chains = ctf_alloc(sizeof (ctf_helem_t) * hp->h_nelems);
|
||||
|
||||
if (hp->h_buckets == NULL || hp->h_chains == NULL) {
|
||||
ctf_hash_destroy(hp);
|
||||
return (EAGAIN);
|
||||
}
|
||||
|
||||
bzero(hp->h_buckets, sizeof (ushort_t) * hp->h_nbuckets);
|
||||
bzero(hp->h_chains, sizeof (ctf_helem_t) * hp->h_nelems);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
uint_t
|
||||
ctf_hash_size(const ctf_hash_t *hp)
|
||||
{
|
||||
return (hp->h_nelems ? hp->h_nelems - 1 : 0);
|
||||
}
|
||||
|
||||
static ulong_t
|
||||
ctf_hash_compute(const char *key, size_t len)
|
||||
{
|
||||
ulong_t g, h = 0;
|
||||
const char *p, *q = key + len;
|
||||
size_t n = 0;
|
||||
|
||||
for (p = key; p < q; p++, n++) {
|
||||
h = (h << 4) + *p;
|
||||
|
||||
if ((g = (h & 0xf0000000)) != 0) {
|
||||
h ^= (g >> 24);
|
||||
h ^= g;
|
||||
}
|
||||
}
|
||||
|
||||
return (h);
|
||||
}
|
||||
|
||||
int
|
||||
ctf_hash_insert(ctf_hash_t *hp, ctf_file_t *fp, ushort_t type, uint_t name)
|
||||
{
|
||||
ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID(name)];
|
||||
const char *str = ctsp->cts_strs + CTF_NAME_OFFSET(name);
|
||||
ctf_helem_t *hep = &hp->h_chains[hp->h_free];
|
||||
ulong_t h;
|
||||
|
||||
if (type == 0)
|
||||
return (EINVAL);
|
||||
|
||||
if (hp->h_free >= hp->h_nelems)
|
||||
return (EOVERFLOW);
|
||||
|
||||
if (ctsp->cts_strs == NULL)
|
||||
return (ECTF_STRTAB);
|
||||
|
||||
if (ctsp->cts_len <= CTF_NAME_OFFSET(name))
|
||||
return (ECTF_BADNAME);
|
||||
|
||||
if (str[0] == '\0')
|
||||
return (0); /* just ignore empty strings on behalf of caller */
|
||||
|
||||
hep->h_name = name;
|
||||
hep->h_type = type;
|
||||
h = ctf_hash_compute(str, strlen(str)) % hp->h_nbuckets;
|
||||
hep->h_next = hp->h_buckets[h];
|
||||
hp->h_buckets[h] = hp->h_free++;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wrapper for ctf_hash_lookup/ctf_hash_insert: if the key is already in the
|
||||
* hash, override the previous definition with this new official definition.
|
||||
* If the key is not present, then call ctf_hash_insert() and hash it in.
|
||||
*/
|
||||
int
|
||||
ctf_hash_define(ctf_hash_t *hp, ctf_file_t *fp, ushort_t type, uint_t name)
|
||||
{
|
||||
const char *str = ctf_strptr(fp, name);
|
||||
ctf_helem_t *hep = ctf_hash_lookup(hp, fp, str, strlen(str));
|
||||
|
||||
if (hep == NULL)
|
||||
return (ctf_hash_insert(hp, fp, type, name));
|
||||
|
||||
hep->h_type = type;
|
||||
return (0);
|
||||
}
|
||||
|
||||
ctf_helem_t *
|
||||
ctf_hash_lookup(ctf_hash_t *hp, ctf_file_t *fp, const char *key, size_t len)
|
||||
{
|
||||
ctf_helem_t *hep;
|
||||
ctf_strs_t *ctsp;
|
||||
const char *str;
|
||||
ushort_t i;
|
||||
|
||||
ulong_t h = ctf_hash_compute(key, len) % hp->h_nbuckets;
|
||||
|
||||
for (i = hp->h_buckets[h]; i != 0; i = hep->h_next) {
|
||||
hep = &hp->h_chains[i];
|
||||
ctsp = &fp->ctf_str[CTF_NAME_STID(hep->h_name)];
|
||||
str = ctsp->cts_strs + CTF_NAME_OFFSET(hep->h_name);
|
||||
|
||||
if (strncmp(key, str, len) == 0 && str[len] == '\0')
|
||||
return (hep);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
ctf_hash_destroy(ctf_hash_t *hp)
|
||||
{
|
||||
if (hp->h_buckets != NULL && hp->h_nbuckets != 1) {
|
||||
ctf_free(hp->h_buckets, sizeof (ushort_t) * hp->h_nbuckets);
|
||||
hp->h_buckets = NULL;
|
||||
}
|
||||
|
||||
if (hp->h_chains != NULL) {
|
||||
ctf_free(hp->h_chains, sizeof (ctf_helem_t) * hp->h_nelems);
|
||||
hp->h_chains = NULL;
|
||||
}
|
||||
}
|
336
cddl/contrib/opensolaris/common/ctf/ctf_impl.h
Normal file
336
cddl/contrib/opensolaris/common/ctf/ctf_impl.h
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#ifndef _CTF_IMPL_H
|
||||
#define _CTF_IMPL_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/errno.h>
|
||||
#include <sys/sysmacros.h>
|
||||
#include <sys/ctf_api.h>
|
||||
|
||||
#ifdef _KERNEL
|
||||
|
||||
#include <sys/systm.h>
|
||||
#include <sys/cmn_err.h>
|
||||
#include <sys/varargs.h>
|
||||
|
||||
#define isspace(c) \
|
||||
((c) == ' ' || (c) == '\t' || (c) == '\n' || \
|
||||
(c) == '\r' || (c) == '\f' || (c) == '\v')
|
||||
|
||||
#define MAP_FAILED ((void *)-1)
|
||||
|
||||
#else /* _KERNEL */
|
||||
|
||||
#include <strings.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#endif /* _KERNEL */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct ctf_helem {
|
||||
uint_t h_name; /* reference to name in string table */
|
||||
ushort_t h_type; /* corresponding type ID number */
|
||||
ushort_t h_next; /* index of next element in hash chain */
|
||||
} ctf_helem_t;
|
||||
|
||||
typedef struct ctf_hash {
|
||||
ushort_t *h_buckets; /* hash bucket array (chain indices) */
|
||||
ctf_helem_t *h_chains; /* hash chains buffer */
|
||||
ushort_t h_nbuckets; /* number of elements in bucket array */
|
||||
ushort_t h_nelems; /* number of elements in hash table */
|
||||
uint_t h_free; /* index of next free hash element */
|
||||
} ctf_hash_t;
|
||||
|
||||
typedef struct ctf_strs {
|
||||
const char *cts_strs; /* base address of string table */
|
||||
size_t cts_len; /* size of string table in bytes */
|
||||
} ctf_strs_t;
|
||||
|
||||
typedef struct ctf_dmodel {
|
||||
const char *ctd_name; /* data model name */
|
||||
int ctd_code; /* data model code */
|
||||
size_t ctd_pointer; /* size of void * in bytes */
|
||||
size_t ctd_char; /* size of char in bytes */
|
||||
size_t ctd_short; /* size of short in bytes */
|
||||
size_t ctd_int; /* size of int in bytes */
|
||||
size_t ctd_long; /* size of long in bytes */
|
||||
} ctf_dmodel_t;
|
||||
|
||||
typedef struct ctf_lookup {
|
||||
const char *ctl_prefix; /* string prefix for this lookup */
|
||||
size_t ctl_len; /* length of prefix string in bytes */
|
||||
ctf_hash_t *ctl_hash; /* pointer to hash table for lookup */
|
||||
} ctf_lookup_t;
|
||||
|
||||
typedef struct ctf_fileops {
|
||||
ushort_t (*ctfo_get_kind)(ushort_t);
|
||||
ushort_t (*ctfo_get_root)(ushort_t);
|
||||
ushort_t (*ctfo_get_vlen)(ushort_t);
|
||||
} ctf_fileops_t;
|
||||
|
||||
typedef struct ctf_list {
|
||||
struct ctf_list *l_prev; /* previous pointer or tail pointer */
|
||||
struct ctf_list *l_next; /* next pointer or head pointer */
|
||||
} ctf_list_t;
|
||||
|
||||
typedef enum {
|
||||
CTF_PREC_BASE,
|
||||
CTF_PREC_POINTER,
|
||||
CTF_PREC_ARRAY,
|
||||
CTF_PREC_FUNCTION,
|
||||
CTF_PREC_MAX
|
||||
} ctf_decl_prec_t;
|
||||
|
||||
typedef struct ctf_decl_node {
|
||||
ctf_list_t cd_list; /* linked list pointers */
|
||||
ctf_id_t cd_type; /* type identifier */
|
||||
uint_t cd_kind; /* type kind */
|
||||
uint_t cd_n; /* type dimension if array */
|
||||
} ctf_decl_node_t;
|
||||
|
||||
typedef struct ctf_decl {
|
||||
ctf_list_t cd_nodes[CTF_PREC_MAX]; /* declaration node stacks */
|
||||
int cd_order[CTF_PREC_MAX]; /* storage order of decls */
|
||||
ctf_decl_prec_t cd_qualp; /* qualifier precision */
|
||||
ctf_decl_prec_t cd_ordp; /* ordered precision */
|
||||
char *cd_buf; /* buffer for output */
|
||||
char *cd_ptr; /* buffer location */
|
||||
char *cd_end; /* buffer limit */
|
||||
size_t cd_len; /* buffer space required */
|
||||
int cd_err; /* saved error value */
|
||||
} ctf_decl_t;
|
||||
|
||||
typedef struct ctf_dmdef {
|
||||
ctf_list_t dmd_list; /* list forward/back pointers */
|
||||
char *dmd_name; /* name of this member */
|
||||
ctf_id_t dmd_type; /* type of this member (for sou) */
|
||||
ulong_t dmd_offset; /* offset of this member in bits (for sou) */
|
||||
int dmd_value; /* value of this member (for enum) */
|
||||
} ctf_dmdef_t;
|
||||
|
||||
typedef struct ctf_dtdef {
|
||||
ctf_list_t dtd_list; /* list forward/back pointers */
|
||||
struct ctf_dtdef *dtd_hash; /* hash chain pointer for ctf_dthash */
|
||||
char *dtd_name; /* name associated with definition (if any) */
|
||||
ctf_id_t dtd_type; /* type identifier for this definition */
|
||||
ctf_type_t dtd_data; /* type node (see <sys/ctf.h>) */
|
||||
union {
|
||||
ctf_list_t dtu_members; /* struct, union, or enum */
|
||||
ctf_arinfo_t dtu_arr; /* array */
|
||||
ctf_encoding_t dtu_enc; /* integer or float */
|
||||
ctf_id_t *dtu_argv; /* function */
|
||||
} dtd_u;
|
||||
} ctf_dtdef_t;
|
||||
|
||||
typedef struct ctf_bundle {
|
||||
ctf_file_t *ctb_file; /* CTF container handle */
|
||||
ctf_id_t ctb_type; /* CTF type identifier */
|
||||
ctf_dtdef_t *ctb_dtd; /* CTF dynamic type definition (if any) */
|
||||
} ctf_bundle_t;
|
||||
|
||||
/*
|
||||
* The ctf_file is the structure used to represent a CTF container to library
|
||||
* clients, who see it only as an opaque pointer. Modifications can therefore
|
||||
* be made freely to this structure without regard to client versioning. The
|
||||
* ctf_file_t typedef appears in <sys/ctf_api.h> and declares a forward tag.
|
||||
*
|
||||
* NOTE: ctf_update() requires that everything inside of ctf_file either be an
|
||||
* immediate value, a pointer to dynamically allocated data *outside* of the
|
||||
* ctf_file itself, or a pointer to statically allocated data. If you add a
|
||||
* pointer to ctf_file that points to something within the ctf_file itself,
|
||||
* you must make corresponding changes to ctf_update().
|
||||
*/
|
||||
struct ctf_file {
|
||||
const ctf_fileops_t *ctf_fileops; /* version-specific file operations */
|
||||
ctf_sect_t ctf_data; /* CTF data from object file */
|
||||
ctf_sect_t ctf_symtab; /* symbol table from object file */
|
||||
ctf_sect_t ctf_strtab; /* string table from object file */
|
||||
ctf_hash_t ctf_structs; /* hash table of struct types */
|
||||
ctf_hash_t ctf_unions; /* hash table of union types */
|
||||
ctf_hash_t ctf_enums; /* hash table of enum types */
|
||||
ctf_hash_t ctf_names; /* hash table of remaining type names */
|
||||
ctf_lookup_t ctf_lookups[5]; /* pointers to hashes for name lookup */
|
||||
ctf_strs_t ctf_str[2]; /* array of string table base and bounds */
|
||||
const uchar_t *ctf_base; /* base of CTF header + uncompressed buffer */
|
||||
const uchar_t *ctf_buf; /* uncompressed CTF data buffer */
|
||||
size_t ctf_size; /* size of CTF header + uncompressed data */
|
||||
uint_t *ctf_sxlate; /* translation table for symtab entries */
|
||||
ulong_t ctf_nsyms; /* number of entries in symtab xlate table */
|
||||
uint_t *ctf_txlate; /* translation table for type IDs */
|
||||
ushort_t *ctf_ptrtab; /* translation table for pointer-to lookups */
|
||||
ulong_t ctf_typemax; /* maximum valid type ID number */
|
||||
const ctf_dmodel_t *ctf_dmodel; /* data model pointer (see above) */
|
||||
struct ctf_file *ctf_parent; /* parent CTF container (if any) */
|
||||
const char *ctf_parlabel; /* label in parent container (if any) */
|
||||
const char *ctf_parname; /* basename of parent (if any) */
|
||||
uint_t ctf_refcnt; /* reference count (for parent links) */
|
||||
uint_t ctf_flags; /* libctf flags (see below) */
|
||||
int ctf_errno; /* error code for most recent error */
|
||||
int ctf_version; /* CTF data version */
|
||||
ctf_dtdef_t **ctf_dthash; /* hash of dynamic type definitions */
|
||||
ulong_t ctf_dthashlen; /* size of dynamic type hash bucket array */
|
||||
ctf_list_t ctf_dtdefs; /* list of dynamic type definitions */
|
||||
size_t ctf_dtstrlen; /* total length of dynamic type strings */
|
||||
ulong_t ctf_dtnextid; /* next dynamic type id to assign */
|
||||
ulong_t ctf_dtoldid; /* oldest id that has been committed */
|
||||
void *ctf_specific; /* data for ctf_get/setspecific */
|
||||
};
|
||||
|
||||
#define LCTF_INDEX_TO_TYPEPTR(fp, i) \
|
||||
((ctf_type_t *)((uintptr_t)(fp)->ctf_buf + (fp)->ctf_txlate[(i)]))
|
||||
|
||||
#define LCTF_INFO_KIND(fp, info) ((fp)->ctf_fileops->ctfo_get_kind(info))
|
||||
#define LCTF_INFO_ROOT(fp, info) ((fp)->ctf_fileops->ctfo_get_root(info))
|
||||
#define LCTF_INFO_VLEN(fp, info) ((fp)->ctf_fileops->ctfo_get_vlen(info))
|
||||
|
||||
#define LCTF_MMAP 0x0001 /* libctf should munmap buffers on close */
|
||||
#define LCTF_CHILD 0x0002 /* CTF container is a child */
|
||||
#define LCTF_RDWR 0x0004 /* CTF container is writable */
|
||||
#define LCTF_DIRTY 0x0008 /* CTF container has been modified */
|
||||
|
||||
#define ECTF_BASE 1000 /* base value for libctf errnos */
|
||||
|
||||
enum {
|
||||
ECTF_FMT = ECTF_BASE, /* file is not in CTF or ELF format */
|
||||
ECTF_ELFVERS, /* ELF version is more recent than libctf */
|
||||
ECTF_CTFVERS, /* CTF version is more recent than libctf */
|
||||
ECTF_ENDIAN, /* data is different endian-ness than lib */
|
||||
ECTF_SYMTAB, /* symbol table uses invalid entry size */
|
||||
ECTF_SYMBAD, /* symbol table data buffer invalid */
|
||||
ECTF_STRBAD, /* string table data buffer invalid */
|
||||
ECTF_CORRUPT, /* file data corruption detected */
|
||||
ECTF_NOCTFDATA, /* ELF file does not contain CTF data */
|
||||
ECTF_NOCTFBUF, /* buffer does not contain CTF data */
|
||||
ECTF_NOSYMTAB, /* symbol table data is not available */
|
||||
ECTF_NOPARENT, /* parent CTF container is not available */
|
||||
ECTF_DMODEL, /* data model mismatch */
|
||||
ECTF_MMAP, /* failed to mmap a data section */
|
||||
ECTF_ZMISSING, /* decompression library not installed */
|
||||
ECTF_ZINIT, /* failed to initialize decompression library */
|
||||
ECTF_ZALLOC, /* failed to allocate decompression buffer */
|
||||
ECTF_DECOMPRESS, /* failed to decompress CTF data */
|
||||
ECTF_STRTAB, /* string table for this string is missing */
|
||||
ECTF_BADNAME, /* string offset is corrupt w.r.t. strtab */
|
||||
ECTF_BADID, /* invalid type ID number */
|
||||
ECTF_NOTSOU, /* type is not a struct or union */
|
||||
ECTF_NOTENUM, /* type is not an enum */
|
||||
ECTF_NOTSUE, /* type is not a struct, union, or enum */
|
||||
ECTF_NOTINTFP, /* type is not an integer or float */
|
||||
ECTF_NOTARRAY, /* type is not an array */
|
||||
ECTF_NOTREF, /* type does not reference another type */
|
||||
ECTF_NAMELEN, /* buffer is too small to hold type name */
|
||||
ECTF_NOTYPE, /* no type found corresponding to name */
|
||||
ECTF_SYNTAX, /* syntax error in type name */
|
||||
ECTF_NOTFUNC, /* symtab entry does not refer to a function */
|
||||
ECTF_NOFUNCDAT, /* no func info available for function */
|
||||
ECTF_NOTDATA, /* symtab entry does not refer to a data obj */
|
||||
ECTF_NOTYPEDAT, /* no type info available for object */
|
||||
ECTF_NOLABEL, /* no label found corresponding to name */
|
||||
ECTF_NOLABELDATA, /* file does not contain any labels */
|
||||
ECTF_NOTSUP, /* feature not supported */
|
||||
ECTF_NOENUMNAM, /* enum element name not found */
|
||||
ECTF_NOMEMBNAM, /* member name not found */
|
||||
ECTF_RDONLY, /* CTF container is read-only */
|
||||
ECTF_DTFULL, /* CTF type is full (no more members allowed) */
|
||||
ECTF_FULL, /* CTF container is full */
|
||||
ECTF_DUPMEMBER, /* duplicate member name definition */
|
||||
ECTF_CONFLICT /* conflicting type definition present */
|
||||
};
|
||||
|
||||
extern ssize_t ctf_get_ctt_size(const ctf_file_t *, const ctf_type_t *,
|
||||
ssize_t *, ssize_t *);
|
||||
|
||||
extern const ctf_type_t *ctf_lookup_by_id(ctf_file_t **, ctf_id_t);
|
||||
|
||||
extern int ctf_hash_create(ctf_hash_t *, ulong_t);
|
||||
extern int ctf_hash_insert(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t);
|
||||
extern int ctf_hash_define(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t);
|
||||
extern ctf_helem_t *ctf_hash_lookup(ctf_hash_t *, ctf_file_t *,
|
||||
const char *, size_t);
|
||||
extern uint_t ctf_hash_size(const ctf_hash_t *);
|
||||
extern void ctf_hash_destroy(ctf_hash_t *);
|
||||
|
||||
#define ctf_list_prev(elem) ((void *)(((ctf_list_t *)(elem))->l_prev))
|
||||
#define ctf_list_next(elem) ((void *)(((ctf_list_t *)(elem))->l_next))
|
||||
|
||||
extern void ctf_list_append(ctf_list_t *, void *);
|
||||
extern void ctf_list_prepend(ctf_list_t *, void *);
|
||||
extern void ctf_list_delete(ctf_list_t *, void *);
|
||||
|
||||
extern void ctf_dtd_insert(ctf_file_t *, ctf_dtdef_t *);
|
||||
extern void ctf_dtd_delete(ctf_file_t *, ctf_dtdef_t *);
|
||||
extern ctf_dtdef_t *ctf_dtd_lookup(ctf_file_t *, ctf_id_t);
|
||||
|
||||
extern void ctf_decl_init(ctf_decl_t *, char *, size_t);
|
||||
extern void ctf_decl_fini(ctf_decl_t *);
|
||||
extern void ctf_decl_push(ctf_decl_t *, ctf_file_t *, ctf_id_t);
|
||||
extern void ctf_decl_sprintf(ctf_decl_t *, const char *, ...);
|
||||
|
||||
extern const char *ctf_strraw(ctf_file_t *, uint_t);
|
||||
extern const char *ctf_strptr(ctf_file_t *, uint_t);
|
||||
|
||||
extern ctf_file_t *ctf_set_open_errno(int *, int);
|
||||
extern long ctf_set_errno(ctf_file_t *, int);
|
||||
|
||||
extern const void *ctf_sect_mmap(ctf_sect_t *, int);
|
||||
extern void ctf_sect_munmap(const ctf_sect_t *);
|
||||
|
||||
extern void *ctf_data_alloc(size_t);
|
||||
extern void ctf_data_free(void *, size_t);
|
||||
extern void ctf_data_protect(void *, size_t);
|
||||
|
||||
extern void *ctf_alloc(size_t);
|
||||
extern void ctf_free(void *, size_t);
|
||||
|
||||
extern char *ctf_strdup(const char *);
|
||||
extern const char *ctf_strerror(int);
|
||||
extern void ctf_dprintf(const char *, ...);
|
||||
|
||||
extern void *ctf_zopen(int *);
|
||||
|
||||
extern const char _CTF_SECTION[]; /* name of CTF ELF section */
|
||||
extern const char _CTF_NULLSTR[]; /* empty string */
|
||||
|
||||
extern int _libctf_version; /* library client version */
|
||||
extern int _libctf_debug; /* debugging messages enabled */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _CTF_IMPL_H */
|
153
cddl/contrib/opensolaris/common/ctf/ctf_labels.c
Normal file
153
cddl/contrib/opensolaris/common/ctf/ctf_labels.c
Normal file
@ -0,0 +1,153 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <ctf_impl.h>
|
||||
|
||||
static int
|
||||
extract_label_info(ctf_file_t *fp, const ctf_lblent_t **ctl, uint_t *num_labels)
|
||||
{
|
||||
const ctf_header_t *h;
|
||||
|
||||
/*
|
||||
* Labels are only supported in V2 or later
|
||||
*/
|
||||
if (fp->ctf_version < CTF_VERSION_2)
|
||||
return (ctf_set_errno(fp, ECTF_NOTSUP));
|
||||
|
||||
h = (const ctf_header_t *)fp->ctf_data.cts_data;
|
||||
|
||||
/* LINTED - pointer alignment */
|
||||
*ctl = (const ctf_lblent_t *)(fp->ctf_buf + h->cth_lbloff);
|
||||
*num_labels = (h->cth_objtoff - h->cth_lbloff) / sizeof (ctf_lblent_t);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the topmost label, or NULL if any errors are encountered
|
||||
*/
|
||||
const char *
|
||||
ctf_label_topmost(ctf_file_t *fp)
|
||||
{
|
||||
const ctf_lblent_t *ctlp;
|
||||
const char *s;
|
||||
uint_t num_labels;
|
||||
|
||||
if (extract_label_info(fp, &ctlp, &num_labels) == CTF_ERR)
|
||||
return (NULL); /* errno is set */
|
||||
|
||||
if (num_labels == 0) {
|
||||
(void) ctf_set_errno(fp, ECTF_NOLABELDATA);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((s = ctf_strraw(fp, (ctlp + num_labels - 1)->ctl_label)) == NULL)
|
||||
(void) ctf_set_errno(fp, ECTF_CORRUPT);
|
||||
|
||||
return (s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all labels. We pass the label string and the lblinfo_t struct
|
||||
* to the specified callback function.
|
||||
*/
|
||||
int
|
||||
ctf_label_iter(ctf_file_t *fp, ctf_label_f *func, void *arg)
|
||||
{
|
||||
const ctf_lblent_t *ctlp;
|
||||
uint_t i, num_labels;
|
||||
ctf_lblinfo_t linfo;
|
||||
const char *lname;
|
||||
int rc;
|
||||
|
||||
if (extract_label_info(fp, &ctlp, &num_labels) == CTF_ERR)
|
||||
return (CTF_ERR); /* errno is set */
|
||||
|
||||
if (num_labels == 0)
|
||||
return (ctf_set_errno(fp, ECTF_NOLABELDATA));
|
||||
|
||||
for (i = 0; i < num_labels; i++, ctlp++) {
|
||||
if ((lname = ctf_strraw(fp, ctlp->ctl_label)) == NULL) {
|
||||
ctf_dprintf("failed to decode label %u with "
|
||||
"typeidx %u\n", ctlp->ctl_label, ctlp->ctl_typeidx);
|
||||
return (ctf_set_errno(fp, ECTF_CORRUPT));
|
||||
}
|
||||
|
||||
linfo.ctb_typeidx = ctlp->ctl_typeidx;
|
||||
if ((rc = func(lname, &linfo, arg)) != 0)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
typedef struct linfo_cb_arg {
|
||||
const char *lca_name; /* Label we want to retrieve info for */
|
||||
ctf_lblinfo_t *lca_info; /* Where to store the info about the label */
|
||||
} linfo_cb_arg_t;
|
||||
|
||||
static int
|
||||
label_info_cb(const char *lname, const ctf_lblinfo_t *linfo, void *arg)
|
||||
{
|
||||
/*
|
||||
* If lname matches the label we are looking for, copy the
|
||||
* lblinfo_t struct for the caller.
|
||||
*/
|
||||
if (strcmp(lname, ((linfo_cb_arg_t *)arg)->lca_name) == 0) {
|
||||
/*
|
||||
* Allow caller not to allocate storage to test if label exists
|
||||
*/
|
||||
if (((linfo_cb_arg_t *)arg)->lca_info != NULL)
|
||||
bcopy(linfo, ((linfo_cb_arg_t *)arg)->lca_info,
|
||||
sizeof (ctf_lblinfo_t));
|
||||
return (1); /* Indicate we found a match */
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve information about the label with name "lname"
|
||||
*/
|
||||
int
|
||||
ctf_label_info(ctf_file_t *fp, const char *lname, ctf_lblinfo_t *linfo)
|
||||
{
|
||||
linfo_cb_arg_t cb_arg;
|
||||
int rc;
|
||||
|
||||
cb_arg.lca_name = lname;
|
||||
cb_arg.lca_info = linfo;
|
||||
|
||||
if ((rc = ctf_label_iter(fp, label_info_cb, &cb_arg)) == CTF_ERR)
|
||||
return (rc);
|
||||
|
||||
if (rc != 1)
|
||||
return (ctf_set_errno(fp, ECTF_NOLABEL));
|
||||
|
||||
return (0);
|
||||
}
|
313
cddl/contrib/opensolaris/common/ctf/ctf_lookup.c
Normal file
313
cddl/contrib/opensolaris/common/ctf/ctf_lookup.c
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/sysmacros.h>
|
||||
#include <ctf_impl.h>
|
||||
|
||||
/*
|
||||
* Compare the given input string and length against a table of known C storage
|
||||
* qualifier keywords. We just ignore these in ctf_lookup_by_name, below. To
|
||||
* do this quickly, we use a pre-computed Perfect Hash Function similar to the
|
||||
* technique originally described in the classic paper:
|
||||
*
|
||||
* R.J. Cichelli, "Minimal Perfect Hash Functions Made Simple",
|
||||
* Communications of the ACM, Volume 23, Issue 1, January 1980, pp. 17-19.
|
||||
*
|
||||
* For an input string S of length N, we use hash H = S[N - 1] + N - 105, which
|
||||
* for the current set of qualifiers yields a unique H in the range [0 .. 20].
|
||||
* The hash can be modified when the keyword set changes as necessary. We also
|
||||
* store the length of each keyword and check it prior to the final strcmp().
|
||||
*/
|
||||
static int
|
||||
isqualifier(const char *s, size_t len)
|
||||
{
|
||||
static const struct qual {
|
||||
const char *q_name;
|
||||
size_t q_len;
|
||||
} qhash[] = {
|
||||
{ "static", 6 }, { "", 0 }, { "", 0 }, { "", 0 },
|
||||
{ "volatile", 8 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 },
|
||||
{ "", 0 }, { "auto", 4 }, { "extern", 6 }, { "", 0 }, { "", 0 },
|
||||
{ "", 0 }, { "", 0 }, { "const", 5 }, { "register", 8 },
|
||||
{ "", 0 }, { "restrict", 8 }, { "_Restrict", 9 }
|
||||
};
|
||||
|
||||
int h = s[len - 1] + (int)len - 105;
|
||||
const struct qual *qp = &qhash[h];
|
||||
|
||||
return (h >= 0 && h < sizeof (qhash) / sizeof (qhash[0]) &&
|
||||
len == qp->q_len && strncmp(qp->q_name, s, qp->q_len) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to convert the given C type name into the corresponding CTF type ID.
|
||||
* It is not possible to do complete and proper conversion of type names
|
||||
* without implementing a more full-fledged parser, which is necessary to
|
||||
* handle things like types that are function pointers to functions that
|
||||
* have arguments that are function pointers, and fun stuff like that.
|
||||
* Instead, this function implements a very simple conversion algorithm that
|
||||
* finds the things that we actually care about: structs, unions, enums,
|
||||
* integers, floats, typedefs, and pointers to any of these named types.
|
||||
*/
|
||||
ctf_id_t
|
||||
ctf_lookup_by_name(ctf_file_t *fp, const char *name)
|
||||
{
|
||||
static const char delimiters[] = " \t\n\r\v\f*";
|
||||
|
||||
const ctf_lookup_t *lp;
|
||||
const ctf_helem_t *hp;
|
||||
const char *p, *q, *end;
|
||||
ctf_id_t type = 0;
|
||||
ctf_id_t ntype, ptype;
|
||||
|
||||
if (name == NULL)
|
||||
return (ctf_set_errno(fp, EINVAL));
|
||||
|
||||
for (p = name, end = name + strlen(name); *p != '\0'; p = q) {
|
||||
while (isspace(*p))
|
||||
p++; /* skip leading ws */
|
||||
|
||||
if (p == end)
|
||||
break;
|
||||
|
||||
if ((q = strpbrk(p + 1, delimiters)) == NULL)
|
||||
q = end; /* compare until end */
|
||||
|
||||
if (*p == '*') {
|
||||
/*
|
||||
* Find a pointer to type by looking in fp->ctf_ptrtab.
|
||||
* If we can't find a pointer to the given type, see if
|
||||
* we can compute a pointer to the type resulting from
|
||||
* resolving the type down to its base type and use
|
||||
* that instead. This helps with cases where the CTF
|
||||
* data includes "struct foo *" but not "foo_t *" and
|
||||
* the user tries to access "foo_t *" in the debugger.
|
||||
*/
|
||||
ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)];
|
||||
if (ntype == 0) {
|
||||
ntype = ctf_type_resolve(fp, type);
|
||||
if (ntype == CTF_ERR || (ntype = fp->ctf_ptrtab[
|
||||
CTF_TYPE_TO_INDEX(ntype)]) == 0) {
|
||||
(void) ctf_set_errno(fp, ECTF_NOTYPE);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
type = CTF_INDEX_TO_TYPE(ntype,
|
||||
(fp->ctf_flags & LCTF_CHILD));
|
||||
|
||||
q = p + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isqualifier(p, (size_t)(q - p)))
|
||||
continue; /* skip qualifier keyword */
|
||||
|
||||
for (lp = fp->ctf_lookups; lp->ctl_prefix != NULL; lp++) {
|
||||
if (lp->ctl_prefix[0] == '\0' ||
|
||||
strncmp(p, lp->ctl_prefix, (size_t)(q - p)) == 0) {
|
||||
for (p += lp->ctl_len; isspace(*p); p++)
|
||||
continue; /* skip prefix and next ws */
|
||||
|
||||
if ((q = strchr(p, '*')) == NULL)
|
||||
q = end; /* compare until end */
|
||||
|
||||
while (isspace(q[-1]))
|
||||
q--; /* exclude trailing ws */
|
||||
|
||||
if ((hp = ctf_hash_lookup(lp->ctl_hash, fp, p,
|
||||
(size_t)(q - p))) == NULL) {
|
||||
(void) ctf_set_errno(fp, ECTF_NOTYPE);
|
||||
goto err;
|
||||
}
|
||||
|
||||
type = hp->h_type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lp->ctl_prefix == NULL) {
|
||||
(void) ctf_set_errno(fp, ECTF_NOTYPE);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
if (*p != '\0' || type == 0)
|
||||
return (ctf_set_errno(fp, ECTF_SYNTAX));
|
||||
|
||||
return (type);
|
||||
|
||||
err:
|
||||
if (fp->ctf_parent != NULL &&
|
||||
(ptype = ctf_lookup_by_name(fp->ctf_parent, name)) != CTF_ERR)
|
||||
return (ptype);
|
||||
|
||||
return (CTF_ERR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a symbol table index, return the type of the data object described
|
||||
* by the corresponding entry in the symbol table.
|
||||
*/
|
||||
ctf_id_t
|
||||
ctf_lookup_by_symbol(ctf_file_t *fp, ulong_t symidx)
|
||||
{
|
||||
const ctf_sect_t *sp = &fp->ctf_symtab;
|
||||
ctf_id_t type;
|
||||
|
||||
if (sp->cts_data == NULL)
|
||||
return (ctf_set_errno(fp, ECTF_NOSYMTAB));
|
||||
|
||||
if (symidx >= fp->ctf_nsyms)
|
||||
return (ctf_set_errno(fp, EINVAL));
|
||||
|
||||
if (sp->cts_entsize == sizeof (Elf32_Sym)) {
|
||||
const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx;
|
||||
if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT)
|
||||
return (ctf_set_errno(fp, ECTF_NOTDATA));
|
||||
} else {
|
||||
const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx;
|
||||
if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT)
|
||||
return (ctf_set_errno(fp, ECTF_NOTDATA));
|
||||
}
|
||||
|
||||
if (fp->ctf_sxlate[symidx] == -1u)
|
||||
return (ctf_set_errno(fp, ECTF_NOTYPEDAT));
|
||||
|
||||
type = *(ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]);
|
||||
if (type == 0)
|
||||
return (ctf_set_errno(fp, ECTF_NOTYPEDAT));
|
||||
|
||||
return (type);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the pointer to the internal CTF type data corresponding to the
|
||||
* given type ID. If the ID is invalid, the function returns NULL.
|
||||
* This function is not exported outside of the library.
|
||||
*/
|
||||
const ctf_type_t *
|
||||
ctf_lookup_by_id(ctf_file_t **fpp, ctf_id_t type)
|
||||
{
|
||||
ctf_file_t *fp = *fpp; /* caller passes in starting CTF container */
|
||||
|
||||
if ((fp->ctf_flags & LCTF_CHILD) && CTF_TYPE_ISPARENT(type) &&
|
||||
(fp = fp->ctf_parent) == NULL) {
|
||||
(void) ctf_set_errno(*fpp, ECTF_NOPARENT);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
type = CTF_TYPE_TO_INDEX(type);
|
||||
if (type > 0 && type <= fp->ctf_typemax) {
|
||||
*fpp = fp; /* function returns ending CTF container */
|
||||
return (LCTF_INDEX_TO_TYPEPTR(fp, type));
|
||||
}
|
||||
|
||||
(void) ctf_set_errno(fp, ECTF_BADID);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a symbol table index, return the info for the function described
|
||||
* by the corresponding entry in the symbol table.
|
||||
*/
|
||||
int
|
||||
ctf_func_info(ctf_file_t *fp, ulong_t symidx, ctf_funcinfo_t *fip)
|
||||
{
|
||||
const ctf_sect_t *sp = &fp->ctf_symtab;
|
||||
const ushort_t *dp;
|
||||
ushort_t info, kind, n;
|
||||
|
||||
if (sp->cts_data == NULL)
|
||||
return (ctf_set_errno(fp, ECTF_NOSYMTAB));
|
||||
|
||||
if (symidx >= fp->ctf_nsyms)
|
||||
return (ctf_set_errno(fp, EINVAL));
|
||||
|
||||
if (sp->cts_entsize == sizeof (Elf32_Sym)) {
|
||||
const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx;
|
||||
if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
|
||||
return (ctf_set_errno(fp, ECTF_NOTFUNC));
|
||||
} else {
|
||||
const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx;
|
||||
if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
|
||||
return (ctf_set_errno(fp, ECTF_NOTFUNC));
|
||||
}
|
||||
|
||||
if (fp->ctf_sxlate[symidx] == -1u)
|
||||
return (ctf_set_errno(fp, ECTF_NOFUNCDAT));
|
||||
|
||||
dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]);
|
||||
|
||||
info = *dp++;
|
||||
kind = LCTF_INFO_KIND(fp, info);
|
||||
n = LCTF_INFO_VLEN(fp, info);
|
||||
|
||||
if (kind == CTF_K_UNKNOWN && n == 0)
|
||||
return (ctf_set_errno(fp, ECTF_NOFUNCDAT));
|
||||
|
||||
if (kind != CTF_K_FUNCTION)
|
||||
return (ctf_set_errno(fp, ECTF_CORRUPT));
|
||||
|
||||
fip->ctc_return = *dp++;
|
||||
fip->ctc_argc = n;
|
||||
fip->ctc_flags = 0;
|
||||
|
||||
if (n != 0 && dp[n - 1] == 0) {
|
||||
fip->ctc_flags |= CTF_FUNC_VARARG;
|
||||
fip->ctc_argc--;
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a symbol table index, return the arguments for the function described
|
||||
* by the corresponding entry in the symbol table.
|
||||
*/
|
||||
int
|
||||
ctf_func_args(ctf_file_t *fp, ulong_t symidx, uint_t argc, ctf_id_t *argv)
|
||||
{
|
||||
const ushort_t *dp;
|
||||
ctf_funcinfo_t f;
|
||||
|
||||
if (ctf_func_info(fp, symidx, &f) == CTF_ERR)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
/*
|
||||
* The argument data is two ushort_t's past the translation table
|
||||
* offset: one for the function info, and one for the return type.
|
||||
*/
|
||||
dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]) + 2;
|
||||
|
||||
for (argc = MIN(argc, f.ctc_argc); argc != 0; argc--)
|
||||
*argv++ = *dp++;
|
||||
|
||||
return (0);
|
||||
}
|
954
cddl/contrib/opensolaris/common/ctf/ctf_open.c
Normal file
954
cddl/contrib/opensolaris/common/ctf/ctf_open.c
Normal file
@ -0,0 +1,954 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <ctf_impl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/zmod.h>
|
||||
|
||||
static const ctf_dmodel_t _libctf_models[] = {
|
||||
{ "ILP32", CTF_MODEL_ILP32, 4, 1, 2, 4, 4 },
|
||||
{ "LP64", CTF_MODEL_LP64, 8, 1, 2, 4, 8 },
|
||||
{ NULL, 0, 0, 0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
const char _CTF_SECTION[] = ".SUNW_ctf";
|
||||
const char _CTF_NULLSTR[] = "";
|
||||
|
||||
int _libctf_version = CTF_VERSION; /* library client version */
|
||||
int _libctf_debug = 0; /* debugging messages enabled */
|
||||
|
||||
static ushort_t
|
||||
get_kind_v1(ushort_t info)
|
||||
{
|
||||
return (CTF_INFO_KIND_V1(info));
|
||||
}
|
||||
|
||||
static ushort_t
|
||||
get_kind_v2(ushort_t info)
|
||||
{
|
||||
return (CTF_INFO_KIND(info));
|
||||
}
|
||||
|
||||
static ushort_t
|
||||
get_root_v1(ushort_t info)
|
||||
{
|
||||
return (CTF_INFO_ISROOT_V1(info));
|
||||
}
|
||||
|
||||
static ushort_t
|
||||
get_root_v2(ushort_t info)
|
||||
{
|
||||
return (CTF_INFO_ISROOT(info));
|
||||
}
|
||||
|
||||
static ushort_t
|
||||
get_vlen_v1(ushort_t info)
|
||||
{
|
||||
return (CTF_INFO_VLEN_V1(info));
|
||||
}
|
||||
|
||||
static ushort_t
|
||||
get_vlen_v2(ushort_t info)
|
||||
{
|
||||
return (CTF_INFO_VLEN(info));
|
||||
}
|
||||
|
||||
static const ctf_fileops_t ctf_fileops[] = {
|
||||
{ NULL, NULL },
|
||||
{ get_kind_v1, get_root_v1, get_vlen_v1 },
|
||||
{ get_kind_v2, get_root_v2, get_vlen_v2 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Convert a 32-bit ELF symbol into GElf (Elf64) and return a pointer to it.
|
||||
*/
|
||||
static Elf64_Sym *
|
||||
sym_to_gelf(const Elf32_Sym *src, Elf64_Sym *dst)
|
||||
{
|
||||
dst->st_name = src->st_name;
|
||||
dst->st_value = src->st_value;
|
||||
dst->st_size = src->st_size;
|
||||
dst->st_info = src->st_info;
|
||||
dst->st_other = src->st_other;
|
||||
dst->st_shndx = src->st_shndx;
|
||||
|
||||
return (dst);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the symtab translation table by filling each entry with the
|
||||
* offset of the CTF type or function data corresponding to each STT_FUNC or
|
||||
* STT_OBJECT entry in the symbol table.
|
||||
*/
|
||||
static int
|
||||
init_symtab(ctf_file_t *fp, const ctf_header_t *hp,
|
||||
const ctf_sect_t *sp, const ctf_sect_t *strp)
|
||||
{
|
||||
const uchar_t *symp = sp->cts_data;
|
||||
uint_t *xp = fp->ctf_sxlate;
|
||||
uint_t *xend = xp + fp->ctf_nsyms;
|
||||
|
||||
uint_t objtoff = hp->cth_objtoff;
|
||||
uint_t funcoff = hp->cth_funcoff;
|
||||
|
||||
ushort_t info, vlen;
|
||||
Elf64_Sym sym, *gsp;
|
||||
const char *name;
|
||||
|
||||
/*
|
||||
* The CTF data object and function type sections are ordered to match
|
||||
* the relative order of the respective symbol types in the symtab.
|
||||
* If no type information is available for a symbol table entry, a
|
||||
* pad is inserted in the CTF section. As a further optimization,
|
||||
* anonymous or undefined symbols are omitted from the CTF data.
|
||||
*/
|
||||
for (; xp < xend; xp++, symp += sp->cts_entsize) {
|
||||
if (sp->cts_entsize == sizeof (Elf32_Sym))
|
||||
gsp = sym_to_gelf((Elf32_Sym *)(uintptr_t)symp, &sym);
|
||||
else
|
||||
gsp = (Elf64_Sym *)(uintptr_t)symp;
|
||||
|
||||
if (gsp->st_name < strp->cts_size)
|
||||
name = (const char *)strp->cts_data + gsp->st_name;
|
||||
else
|
||||
name = _CTF_NULLSTR;
|
||||
|
||||
if (gsp->st_name == 0 || gsp->st_shndx == SHN_UNDEF ||
|
||||
strcmp(name, "_START_") == 0 ||
|
||||
strcmp(name, "_END_") == 0) {
|
||||
*xp = -1u;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (ELF64_ST_TYPE(gsp->st_info)) {
|
||||
case STT_OBJECT:
|
||||
if (objtoff >= hp->cth_funcoff ||
|
||||
(gsp->st_shndx == SHN_ABS && gsp->st_value == 0)) {
|
||||
*xp = -1u;
|
||||
break;
|
||||
}
|
||||
|
||||
*xp = objtoff;
|
||||
objtoff += sizeof (ushort_t);
|
||||
break;
|
||||
|
||||
case STT_FUNC:
|
||||
if (funcoff >= hp->cth_typeoff) {
|
||||
*xp = -1u;
|
||||
break;
|
||||
}
|
||||
|
||||
*xp = funcoff;
|
||||
|
||||
info = *(ushort_t *)((uintptr_t)fp->ctf_buf + funcoff);
|
||||
vlen = LCTF_INFO_VLEN(fp, info);
|
||||
|
||||
/*
|
||||
* If we encounter a zero pad at the end, just skip it.
|
||||
* Otherwise skip over the function and its return type
|
||||
* (+2) and the argument list (vlen).
|
||||
*/
|
||||
if (LCTF_INFO_KIND(fp, info) == CTF_K_UNKNOWN &&
|
||||
vlen == 0)
|
||||
funcoff += sizeof (ushort_t); /* skip pad */
|
||||
else
|
||||
funcoff += sizeof (ushort_t) * (vlen + 2);
|
||||
break;
|
||||
|
||||
default:
|
||||
*xp = -1u;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctf_dprintf("loaded %lu symtab entries\n", fp->ctf_nsyms);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the type ID translation table with the byte offset of each type,
|
||||
* and initialize the hash tables of each named type.
|
||||
*/
|
||||
static int
|
||||
init_types(ctf_file_t *fp, const ctf_header_t *cth)
|
||||
{
|
||||
/* LINTED - pointer alignment */
|
||||
const ctf_type_t *tbuf = (ctf_type_t *)(fp->ctf_buf + cth->cth_typeoff);
|
||||
/* LINTED - pointer alignment */
|
||||
const ctf_type_t *tend = (ctf_type_t *)(fp->ctf_buf + cth->cth_stroff);
|
||||
|
||||
ulong_t pop[CTF_K_MAX + 1] = { 0 };
|
||||
const ctf_type_t *tp;
|
||||
ctf_hash_t *hp;
|
||||
ushort_t id, dst;
|
||||
uint_t *xp;
|
||||
|
||||
/*
|
||||
* We initially determine whether the container is a child or a parent
|
||||
* based on the value of cth_parname. To support containers that pre-
|
||||
* date cth_parname, we also scan the types themselves for references
|
||||
* to values in the range reserved for child types in our first pass.
|
||||
*/
|
||||
int child = cth->cth_parname != 0;
|
||||
int nlstructs = 0, nlunions = 0;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* We make two passes through the entire type section. In this first
|
||||
* pass, we count the number of each type and the total number of types.
|
||||
*/
|
||||
for (tp = tbuf; tp < tend; fp->ctf_typemax++) {
|
||||
ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info);
|
||||
ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info);
|
||||
ssize_t size, increment;
|
||||
|
||||
size_t vbytes;
|
||||
uint_t n;
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
|
||||
|
||||
switch (kind) {
|
||||
case CTF_K_INTEGER:
|
||||
case CTF_K_FLOAT:
|
||||
vbytes = sizeof (uint_t);
|
||||
break;
|
||||
case CTF_K_ARRAY:
|
||||
vbytes = sizeof (ctf_array_t);
|
||||
break;
|
||||
case CTF_K_FUNCTION:
|
||||
vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
|
||||
break;
|
||||
case CTF_K_STRUCT:
|
||||
case CTF_K_UNION:
|
||||
if (fp->ctf_version == CTF_VERSION_1 ||
|
||||
size < CTF_LSTRUCT_THRESH) {
|
||||
ctf_member_t *mp = (ctf_member_t *)
|
||||
((uintptr_t)tp + increment);
|
||||
|
||||
vbytes = sizeof (ctf_member_t) * vlen;
|
||||
for (n = vlen; n != 0; n--, mp++)
|
||||
child |= CTF_TYPE_ISCHILD(mp->ctm_type);
|
||||
} else {
|
||||
ctf_lmember_t *lmp = (ctf_lmember_t *)
|
||||
((uintptr_t)tp + increment);
|
||||
|
||||
vbytes = sizeof (ctf_lmember_t) * vlen;
|
||||
for (n = vlen; n != 0; n--, lmp++)
|
||||
child |=
|
||||
CTF_TYPE_ISCHILD(lmp->ctlm_type);
|
||||
}
|
||||
break;
|
||||
case CTF_K_ENUM:
|
||||
vbytes = sizeof (ctf_enum_t) * vlen;
|
||||
break;
|
||||
case CTF_K_FORWARD:
|
||||
/*
|
||||
* For forward declarations, ctt_type is the CTF_K_*
|
||||
* kind for the tag, so bump that population count too.
|
||||
* If ctt_type is unknown, treat the tag as a struct.
|
||||
*/
|
||||
if (tp->ctt_type == CTF_K_UNKNOWN ||
|
||||
tp->ctt_type >= CTF_K_MAX)
|
||||
pop[CTF_K_STRUCT]++;
|
||||
else
|
||||
pop[tp->ctt_type]++;
|
||||
/*FALLTHRU*/
|
||||
case CTF_K_UNKNOWN:
|
||||
vbytes = 0;
|
||||
break;
|
||||
case CTF_K_POINTER:
|
||||
case CTF_K_TYPEDEF:
|
||||
case CTF_K_VOLATILE:
|
||||
case CTF_K_CONST:
|
||||
case CTF_K_RESTRICT:
|
||||
child |= CTF_TYPE_ISCHILD(tp->ctt_type);
|
||||
vbytes = 0;
|
||||
break;
|
||||
default:
|
||||
ctf_dprintf("detected invalid CTF kind -- %u\n", kind);
|
||||
return (ECTF_CORRUPT);
|
||||
}
|
||||
tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
|
||||
pop[kind]++;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we detected a reference to a child type ID, then we know this
|
||||
* container is a child and may have a parent's types imported later.
|
||||
*/
|
||||
if (child) {
|
||||
ctf_dprintf("CTF container %p is a child\n", (void *)fp);
|
||||
fp->ctf_flags |= LCTF_CHILD;
|
||||
} else
|
||||
ctf_dprintf("CTF container %p is a parent\n", (void *)fp);
|
||||
|
||||
/*
|
||||
* Now that we've counted up the number of each type, we can allocate
|
||||
* the hash tables, type translation table, and pointer table.
|
||||
*/
|
||||
if ((err = ctf_hash_create(&fp->ctf_structs, pop[CTF_K_STRUCT])) != 0)
|
||||
return (err);
|
||||
|
||||
if ((err = ctf_hash_create(&fp->ctf_unions, pop[CTF_K_UNION])) != 0)
|
||||
return (err);
|
||||
|
||||
if ((err = ctf_hash_create(&fp->ctf_enums, pop[CTF_K_ENUM])) != 0)
|
||||
return (err);
|
||||
|
||||
if ((err = ctf_hash_create(&fp->ctf_names,
|
||||
pop[CTF_K_INTEGER] + pop[CTF_K_FLOAT] + pop[CTF_K_FUNCTION] +
|
||||
pop[CTF_K_TYPEDEF] + pop[CTF_K_POINTER] + pop[CTF_K_VOLATILE] +
|
||||
pop[CTF_K_CONST] + pop[CTF_K_RESTRICT])) != 0)
|
||||
return (err);
|
||||
|
||||
fp->ctf_txlate = ctf_alloc(sizeof (uint_t) * (fp->ctf_typemax + 1));
|
||||
fp->ctf_ptrtab = ctf_alloc(sizeof (ushort_t) * (fp->ctf_typemax + 1));
|
||||
|
||||
if (fp->ctf_txlate == NULL || fp->ctf_ptrtab == NULL)
|
||||
return (EAGAIN); /* memory allocation failed */
|
||||
|
||||
xp = fp->ctf_txlate;
|
||||
*xp++ = 0; /* type id 0 is used as a sentinel value */
|
||||
|
||||
bzero(fp->ctf_txlate, sizeof (uint_t) * (fp->ctf_typemax + 1));
|
||||
bzero(fp->ctf_ptrtab, sizeof (ushort_t) * (fp->ctf_typemax + 1));
|
||||
|
||||
/*
|
||||
* In the second pass through the types, we fill in each entry of the
|
||||
* type and pointer tables and add names to the appropriate hashes.
|
||||
*/
|
||||
for (id = 1, tp = tbuf; tp < tend; xp++, id++) {
|
||||
ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info);
|
||||
ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info);
|
||||
ssize_t size, increment;
|
||||
|
||||
const char *name;
|
||||
size_t vbytes;
|
||||
ctf_helem_t *hep;
|
||||
ctf_encoding_t cte;
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
|
||||
name = ctf_strptr(fp, tp->ctt_name);
|
||||
|
||||
switch (kind) {
|
||||
case CTF_K_INTEGER:
|
||||
case CTF_K_FLOAT:
|
||||
/*
|
||||
* Only insert a new integer base type definition if
|
||||
* this type name has not been defined yet. We re-use
|
||||
* the names with different encodings for bit-fields.
|
||||
*/
|
||||
if ((hep = ctf_hash_lookup(&fp->ctf_names, fp,
|
||||
name, strlen(name))) == NULL) {
|
||||
err = ctf_hash_insert(&fp->ctf_names, fp,
|
||||
CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
|
||||
if (err != 0 && err != ECTF_STRTAB)
|
||||
return (err);
|
||||
} else if (ctf_type_encoding(fp, hep->h_type,
|
||||
&cte) == 0 && cte.cte_bits == 0) {
|
||||
/*
|
||||
* Work-around SOS8 stabs bug: replace existing
|
||||
* intrinsic w/ same name if it was zero bits.
|
||||
*/
|
||||
hep->h_type = CTF_INDEX_TO_TYPE(id, child);
|
||||
}
|
||||
vbytes = sizeof (uint_t);
|
||||
break;
|
||||
|
||||
case CTF_K_ARRAY:
|
||||
vbytes = sizeof (ctf_array_t);
|
||||
break;
|
||||
|
||||
case CTF_K_FUNCTION:
|
||||
err = ctf_hash_insert(&fp->ctf_names, fp,
|
||||
CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
|
||||
if (err != 0 && err != ECTF_STRTAB)
|
||||
return (err);
|
||||
vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
|
||||
break;
|
||||
|
||||
case CTF_K_STRUCT:
|
||||
err = ctf_hash_define(&fp->ctf_structs, fp,
|
||||
CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
|
||||
|
||||
if (err != 0 && err != ECTF_STRTAB)
|
||||
return (err);
|
||||
|
||||
if (fp->ctf_version == CTF_VERSION_1 ||
|
||||
size < CTF_LSTRUCT_THRESH)
|
||||
vbytes = sizeof (ctf_member_t) * vlen;
|
||||
else {
|
||||
vbytes = sizeof (ctf_lmember_t) * vlen;
|
||||
nlstructs++;
|
||||
}
|
||||
break;
|
||||
|
||||
case CTF_K_UNION:
|
||||
err = ctf_hash_define(&fp->ctf_unions, fp,
|
||||
CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
|
||||
|
||||
if (err != 0 && err != ECTF_STRTAB)
|
||||
return (err);
|
||||
|
||||
if (fp->ctf_version == CTF_VERSION_1 ||
|
||||
size < CTF_LSTRUCT_THRESH)
|
||||
vbytes = sizeof (ctf_member_t) * vlen;
|
||||
else {
|
||||
vbytes = sizeof (ctf_lmember_t) * vlen;
|
||||
nlunions++;
|
||||
}
|
||||
break;
|
||||
|
||||
case CTF_K_ENUM:
|
||||
err = ctf_hash_define(&fp->ctf_enums, fp,
|
||||
CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
|
||||
|
||||
if (err != 0 && err != ECTF_STRTAB)
|
||||
return (err);
|
||||
|
||||
vbytes = sizeof (ctf_enum_t) * vlen;
|
||||
break;
|
||||
|
||||
case CTF_K_TYPEDEF:
|
||||
err = ctf_hash_insert(&fp->ctf_names, fp,
|
||||
CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
|
||||
if (err != 0 && err != ECTF_STRTAB)
|
||||
return (err);
|
||||
vbytes = 0;
|
||||
break;
|
||||
|
||||
case CTF_K_FORWARD:
|
||||
/*
|
||||
* Only insert forward tags into the given hash if the
|
||||
* type or tag name is not already present.
|
||||
*/
|
||||
switch (tp->ctt_type) {
|
||||
case CTF_K_STRUCT:
|
||||
hp = &fp->ctf_structs;
|
||||
break;
|
||||
case CTF_K_UNION:
|
||||
hp = &fp->ctf_unions;
|
||||
break;
|
||||
case CTF_K_ENUM:
|
||||
hp = &fp->ctf_enums;
|
||||
break;
|
||||
default:
|
||||
hp = &fp->ctf_structs;
|
||||
}
|
||||
|
||||
if (ctf_hash_lookup(hp, fp,
|
||||
name, strlen(name)) == NULL) {
|
||||
err = ctf_hash_insert(hp, fp,
|
||||
CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
|
||||
if (err != 0 && err != ECTF_STRTAB)
|
||||
return (err);
|
||||
}
|
||||
vbytes = 0;
|
||||
break;
|
||||
|
||||
case CTF_K_POINTER:
|
||||
/*
|
||||
* If the type referenced by the pointer is in this CTF
|
||||
* container, then store the index of the pointer type
|
||||
* in fp->ctf_ptrtab[ index of referenced type ].
|
||||
*/
|
||||
if (CTF_TYPE_ISCHILD(tp->ctt_type) == child &&
|
||||
CTF_TYPE_TO_INDEX(tp->ctt_type) <= fp->ctf_typemax)
|
||||
fp->ctf_ptrtab[
|
||||
CTF_TYPE_TO_INDEX(tp->ctt_type)] = id;
|
||||
/*FALLTHRU*/
|
||||
|
||||
case CTF_K_VOLATILE:
|
||||
case CTF_K_CONST:
|
||||
case CTF_K_RESTRICT:
|
||||
err = ctf_hash_insert(&fp->ctf_names, fp,
|
||||
CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
|
||||
if (err != 0 && err != ECTF_STRTAB)
|
||||
return (err);
|
||||
/*FALLTHRU*/
|
||||
|
||||
default:
|
||||
vbytes = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
*xp = (uint_t)((uintptr_t)tp - (uintptr_t)fp->ctf_buf);
|
||||
tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
|
||||
}
|
||||
|
||||
ctf_dprintf("%lu total types processed\n", fp->ctf_typemax);
|
||||
ctf_dprintf("%u enum names hashed\n", ctf_hash_size(&fp->ctf_enums));
|
||||
ctf_dprintf("%u struct names hashed (%d long)\n",
|
||||
ctf_hash_size(&fp->ctf_structs), nlstructs);
|
||||
ctf_dprintf("%u union names hashed (%d long)\n",
|
||||
ctf_hash_size(&fp->ctf_unions), nlunions);
|
||||
ctf_dprintf("%u base type names hashed\n",
|
||||
ctf_hash_size(&fp->ctf_names));
|
||||
|
||||
/*
|
||||
* Make an additional pass through the pointer table to find pointers
|
||||
* that point to anonymous typedef nodes. If we find one, modify the
|
||||
* pointer table so that the pointer is also known to point to the
|
||||
* node that is referenced by the anonymous typedef node.
|
||||
*/
|
||||
for (id = 1; id <= fp->ctf_typemax; id++) {
|
||||
if ((dst = fp->ctf_ptrtab[id]) != 0) {
|
||||
tp = LCTF_INDEX_TO_TYPEPTR(fp, id);
|
||||
|
||||
if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_TYPEDEF &&
|
||||
strcmp(ctf_strptr(fp, tp->ctt_name), "") == 0 &&
|
||||
CTF_TYPE_ISCHILD(tp->ctt_type) == child &&
|
||||
CTF_TYPE_TO_INDEX(tp->ctt_type) <= fp->ctf_typemax)
|
||||
fp->ctf_ptrtab[
|
||||
CTF_TYPE_TO_INDEX(tp->ctt_type)] = dst;
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode the specified CTF buffer and optional symbol table and create a new
|
||||
* CTF container representing the symbolic debugging information. This code
|
||||
* can be used directly by the debugger, or it can be used as the engine for
|
||||
* ctf_fdopen() or ctf_open(), below.
|
||||
*/
|
||||
ctf_file_t *
|
||||
ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
|
||||
const ctf_sect_t *strsect, int *errp)
|
||||
{
|
||||
const ctf_preamble_t *pp;
|
||||
ctf_header_t hp;
|
||||
ctf_file_t *fp;
|
||||
void *buf, *base;
|
||||
size_t size, hdrsz;
|
||||
int err;
|
||||
|
||||
if (ctfsect == NULL || ((symsect == NULL) != (strsect == NULL)))
|
||||
return (ctf_set_open_errno(errp, EINVAL));
|
||||
|
||||
if (symsect != NULL && symsect->cts_entsize != sizeof (Elf32_Sym) &&
|
||||
symsect->cts_entsize != sizeof (Elf64_Sym))
|
||||
return (ctf_set_open_errno(errp, ECTF_SYMTAB));
|
||||
|
||||
if (symsect != NULL && symsect->cts_data == NULL)
|
||||
return (ctf_set_open_errno(errp, ECTF_SYMBAD));
|
||||
|
||||
if (strsect != NULL && strsect->cts_data == NULL)
|
||||
return (ctf_set_open_errno(errp, ECTF_STRBAD));
|
||||
|
||||
if (ctfsect->cts_size < sizeof (ctf_preamble_t))
|
||||
return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
|
||||
|
||||
pp = (const ctf_preamble_t *)ctfsect->cts_data;
|
||||
|
||||
ctf_dprintf("ctf_bufopen: magic=0x%x version=%u\n",
|
||||
pp->ctp_magic, pp->ctp_version);
|
||||
|
||||
/*
|
||||
* Validate each part of the CTF header (either V1 or V2).
|
||||
* First, we validate the preamble (common to all versions). At that
|
||||
* point, we know specific header version, and can validate the
|
||||
* version-specific parts including section offsets and alignments.
|
||||
*/
|
||||
if (pp->ctp_magic != CTF_MAGIC)
|
||||
return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
|
||||
|
||||
if (pp->ctp_version == CTF_VERSION_2) {
|
||||
if (ctfsect->cts_size < sizeof (ctf_header_t))
|
||||
return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
|
||||
|
||||
bcopy(ctfsect->cts_data, &hp, sizeof (hp));
|
||||
hdrsz = sizeof (ctf_header_t);
|
||||
|
||||
} else if (pp->ctp_version == CTF_VERSION_1) {
|
||||
const ctf_header_v1_t *h1p =
|
||||
(const ctf_header_v1_t *)ctfsect->cts_data;
|
||||
|
||||
if (ctfsect->cts_size < sizeof (ctf_header_v1_t))
|
||||
return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
|
||||
|
||||
bzero(&hp, sizeof (hp));
|
||||
hp.cth_preamble = h1p->cth_preamble;
|
||||
hp.cth_objtoff = h1p->cth_objtoff;
|
||||
hp.cth_funcoff = h1p->cth_funcoff;
|
||||
hp.cth_typeoff = h1p->cth_typeoff;
|
||||
hp.cth_stroff = h1p->cth_stroff;
|
||||
hp.cth_strlen = h1p->cth_strlen;
|
||||
|
||||
hdrsz = sizeof (ctf_header_v1_t);
|
||||
} else
|
||||
return (ctf_set_open_errno(errp, ECTF_CTFVERS));
|
||||
|
||||
size = hp.cth_stroff + hp.cth_strlen;
|
||||
|
||||
ctf_dprintf("ctf_bufopen: uncompressed size=%lu\n", (ulong_t)size);
|
||||
|
||||
if (hp.cth_lbloff > size || hp.cth_objtoff > size ||
|
||||
hp.cth_funcoff > size || hp.cth_typeoff > size ||
|
||||
hp.cth_stroff > size)
|
||||
return (ctf_set_open_errno(errp, ECTF_CORRUPT));
|
||||
|
||||
if (hp.cth_lbloff > hp.cth_objtoff ||
|
||||
hp.cth_objtoff > hp.cth_funcoff ||
|
||||
hp.cth_funcoff > hp.cth_typeoff ||
|
||||
hp.cth_typeoff > hp.cth_stroff)
|
||||
return (ctf_set_open_errno(errp, ECTF_CORRUPT));
|
||||
|
||||
if ((hp.cth_lbloff & 3) || (hp.cth_objtoff & 1) ||
|
||||
(hp.cth_funcoff & 1) || (hp.cth_typeoff & 3))
|
||||
return (ctf_set_open_errno(errp, ECTF_CORRUPT));
|
||||
|
||||
/*
|
||||
* Once everything is determined to be valid, attempt to decompress
|
||||
* the CTF data buffer if it is compressed. Otherwise we just put
|
||||
* the data section's buffer pointer into ctf_buf, below.
|
||||
*/
|
||||
if (hp.cth_flags & CTF_F_COMPRESS) {
|
||||
size_t srclen, dstlen;
|
||||
const void *src;
|
||||
int rc = Z_OK;
|
||||
|
||||
if (ctf_zopen(errp) == NULL)
|
||||
return (NULL); /* errp is set for us */
|
||||
|
||||
if ((base = ctf_data_alloc(size + hdrsz)) == MAP_FAILED)
|
||||
return (ctf_set_open_errno(errp, ECTF_ZALLOC));
|
||||
|
||||
bcopy(ctfsect->cts_data, base, hdrsz);
|
||||
((ctf_preamble_t *)base)->ctp_flags &= ~CTF_F_COMPRESS;
|
||||
buf = (uchar_t *)base + hdrsz;
|
||||
|
||||
src = (uchar_t *)ctfsect->cts_data + hdrsz;
|
||||
srclen = ctfsect->cts_size - hdrsz;
|
||||
dstlen = size;
|
||||
|
||||
if ((rc = z_uncompress(buf, &dstlen, src, srclen)) != Z_OK) {
|
||||
ctf_dprintf("zlib inflate err: %s\n", z_strerror(rc));
|
||||
ctf_data_free(base, size + hdrsz);
|
||||
return (ctf_set_open_errno(errp, ECTF_DECOMPRESS));
|
||||
}
|
||||
|
||||
if (dstlen != size) {
|
||||
ctf_dprintf("zlib inflate short -- got %lu of %lu "
|
||||
"bytes\n", (ulong_t)dstlen, (ulong_t)size);
|
||||
ctf_data_free(base, size + hdrsz);
|
||||
return (ctf_set_open_errno(errp, ECTF_CORRUPT));
|
||||
}
|
||||
|
||||
ctf_data_protect(base, size + hdrsz);
|
||||
|
||||
} else {
|
||||
base = (void *)ctfsect->cts_data;
|
||||
buf = (uchar_t *)base + hdrsz;
|
||||
}
|
||||
|
||||
/*
|
||||
* Once we have uncompressed and validated the CTF data buffer, we can
|
||||
* proceed with allocating a ctf_file_t and initializing it.
|
||||
*/
|
||||
if ((fp = ctf_alloc(sizeof (ctf_file_t))) == NULL)
|
||||
return (ctf_set_open_errno(errp, EAGAIN));
|
||||
|
||||
bzero(fp, sizeof (ctf_file_t));
|
||||
fp->ctf_version = hp.cth_version;
|
||||
fp->ctf_fileops = &ctf_fileops[hp.cth_version];
|
||||
bcopy(ctfsect, &fp->ctf_data, sizeof (ctf_sect_t));
|
||||
|
||||
if (symsect != NULL) {
|
||||
bcopy(symsect, &fp->ctf_symtab, sizeof (ctf_sect_t));
|
||||
bcopy(strsect, &fp->ctf_strtab, sizeof (ctf_sect_t));
|
||||
}
|
||||
|
||||
if (fp->ctf_data.cts_name != NULL)
|
||||
fp->ctf_data.cts_name = ctf_strdup(fp->ctf_data.cts_name);
|
||||
if (fp->ctf_symtab.cts_name != NULL)
|
||||
fp->ctf_symtab.cts_name = ctf_strdup(fp->ctf_symtab.cts_name);
|
||||
if (fp->ctf_strtab.cts_name != NULL)
|
||||
fp->ctf_strtab.cts_name = ctf_strdup(fp->ctf_strtab.cts_name);
|
||||
|
||||
if (fp->ctf_data.cts_name == NULL)
|
||||
fp->ctf_data.cts_name = _CTF_NULLSTR;
|
||||
if (fp->ctf_symtab.cts_name == NULL)
|
||||
fp->ctf_symtab.cts_name = _CTF_NULLSTR;
|
||||
if (fp->ctf_strtab.cts_name == NULL)
|
||||
fp->ctf_strtab.cts_name = _CTF_NULLSTR;
|
||||
|
||||
fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *)buf + hp.cth_stroff;
|
||||
fp->ctf_str[CTF_STRTAB_0].cts_len = hp.cth_strlen;
|
||||
|
||||
if (strsect != NULL) {
|
||||
fp->ctf_str[CTF_STRTAB_1].cts_strs = strsect->cts_data;
|
||||
fp->ctf_str[CTF_STRTAB_1].cts_len = strsect->cts_size;
|
||||
}
|
||||
|
||||
fp->ctf_base = base;
|
||||
fp->ctf_buf = buf;
|
||||
fp->ctf_size = size + hdrsz;
|
||||
|
||||
/*
|
||||
* If we have a parent container name and label, store the relocated
|
||||
* string pointers in the CTF container for easy access later.
|
||||
*/
|
||||
if (hp.cth_parlabel != 0)
|
||||
fp->ctf_parlabel = ctf_strptr(fp, hp.cth_parlabel);
|
||||
if (hp.cth_parname != 0)
|
||||
fp->ctf_parname = ctf_strptr(fp, hp.cth_parname);
|
||||
|
||||
ctf_dprintf("ctf_bufopen: parent name %s (label %s)\n",
|
||||
fp->ctf_parname ? fp->ctf_parname : "<NULL>",
|
||||
fp->ctf_parlabel ? fp->ctf_parlabel : "<NULL>");
|
||||
|
||||
/*
|
||||
* If we have a symbol table section, allocate and initialize
|
||||
* the symtab translation table, pointed to by ctf_sxlate.
|
||||
*/
|
||||
if (symsect != NULL) {
|
||||
fp->ctf_nsyms = symsect->cts_size / symsect->cts_entsize;
|
||||
fp->ctf_sxlate = ctf_alloc(fp->ctf_nsyms * sizeof (uint_t));
|
||||
|
||||
if (fp->ctf_sxlate == NULL) {
|
||||
(void) ctf_set_open_errno(errp, EAGAIN);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if ((err = init_symtab(fp, &hp, symsect, strsect)) != 0) {
|
||||
(void) ctf_set_open_errno(errp, err);
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
|
||||
if ((err = init_types(fp, &hp)) != 0) {
|
||||
(void) ctf_set_open_errno(errp, err);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize the ctf_lookup_by_name top-level dictionary. We keep an
|
||||
* array of type name prefixes and the corresponding ctf_hash to use.
|
||||
* NOTE: This code must be kept in sync with the code in ctf_update().
|
||||
*/
|
||||
fp->ctf_lookups[0].ctl_prefix = "struct";
|
||||
fp->ctf_lookups[0].ctl_len = strlen(fp->ctf_lookups[0].ctl_prefix);
|
||||
fp->ctf_lookups[0].ctl_hash = &fp->ctf_structs;
|
||||
fp->ctf_lookups[1].ctl_prefix = "union";
|
||||
fp->ctf_lookups[1].ctl_len = strlen(fp->ctf_lookups[1].ctl_prefix);
|
||||
fp->ctf_lookups[1].ctl_hash = &fp->ctf_unions;
|
||||
fp->ctf_lookups[2].ctl_prefix = "enum";
|
||||
fp->ctf_lookups[2].ctl_len = strlen(fp->ctf_lookups[2].ctl_prefix);
|
||||
fp->ctf_lookups[2].ctl_hash = &fp->ctf_enums;
|
||||
fp->ctf_lookups[3].ctl_prefix = _CTF_NULLSTR;
|
||||
fp->ctf_lookups[3].ctl_len = strlen(fp->ctf_lookups[3].ctl_prefix);
|
||||
fp->ctf_lookups[3].ctl_hash = &fp->ctf_names;
|
||||
fp->ctf_lookups[4].ctl_prefix = NULL;
|
||||
fp->ctf_lookups[4].ctl_len = 0;
|
||||
fp->ctf_lookups[4].ctl_hash = NULL;
|
||||
|
||||
if (symsect != NULL) {
|
||||
if (symsect->cts_entsize == sizeof (Elf64_Sym))
|
||||
(void) ctf_setmodel(fp, CTF_MODEL_LP64);
|
||||
else
|
||||
(void) ctf_setmodel(fp, CTF_MODEL_ILP32);
|
||||
} else
|
||||
(void) ctf_setmodel(fp, CTF_MODEL_NATIVE);
|
||||
|
||||
fp->ctf_refcnt = 1;
|
||||
return (fp);
|
||||
|
||||
bad:
|
||||
ctf_close(fp);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Close the specified CTF container and free associated data structures. Note
|
||||
* that ctf_close() is a reference counted operation: if the specified file is
|
||||
* the parent of other active containers, its reference count will be greater
|
||||
* than one and it will be freed later when no active children exist.
|
||||
*/
|
||||
void
|
||||
ctf_close(ctf_file_t *fp)
|
||||
{
|
||||
ctf_dtdef_t *dtd, *ntd;
|
||||
|
||||
if (fp == NULL)
|
||||
return; /* allow ctf_close(NULL) to simplify caller code */
|
||||
|
||||
ctf_dprintf("ctf_close(%p) refcnt=%u\n", (void *)fp, fp->ctf_refcnt);
|
||||
|
||||
if (fp->ctf_refcnt > 1) {
|
||||
fp->ctf_refcnt--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fp->ctf_parent != NULL)
|
||||
ctf_close(fp->ctf_parent);
|
||||
|
||||
for (dtd = ctf_list_next(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) {
|
||||
ntd = ctf_list_next(dtd);
|
||||
ctf_dtd_delete(fp, dtd);
|
||||
}
|
||||
|
||||
ctf_free(fp->ctf_dthash, fp->ctf_dthashlen * sizeof (ctf_dtdef_t *));
|
||||
|
||||
if (fp->ctf_flags & LCTF_MMAP) {
|
||||
if (fp->ctf_data.cts_data != NULL)
|
||||
ctf_sect_munmap(&fp->ctf_data);
|
||||
if (fp->ctf_symtab.cts_data != NULL)
|
||||
ctf_sect_munmap(&fp->ctf_symtab);
|
||||
if (fp->ctf_strtab.cts_data != NULL)
|
||||
ctf_sect_munmap(&fp->ctf_strtab);
|
||||
}
|
||||
|
||||
if (fp->ctf_data.cts_name != _CTF_NULLSTR &&
|
||||
fp->ctf_data.cts_name != NULL) {
|
||||
ctf_free((char *)fp->ctf_data.cts_name,
|
||||
strlen(fp->ctf_data.cts_name) + 1);
|
||||
}
|
||||
|
||||
if (fp->ctf_symtab.cts_name != _CTF_NULLSTR &&
|
||||
fp->ctf_symtab.cts_name != NULL) {
|
||||
ctf_free((char *)fp->ctf_symtab.cts_name,
|
||||
strlen(fp->ctf_symtab.cts_name) + 1);
|
||||
}
|
||||
|
||||
if (fp->ctf_strtab.cts_name != _CTF_NULLSTR &&
|
||||
fp->ctf_strtab.cts_name != NULL) {
|
||||
ctf_free((char *)fp->ctf_strtab.cts_name,
|
||||
strlen(fp->ctf_strtab.cts_name) + 1);
|
||||
}
|
||||
|
||||
if (fp->ctf_base != fp->ctf_data.cts_data && fp->ctf_base != NULL)
|
||||
ctf_data_free((void *)fp->ctf_base, fp->ctf_size);
|
||||
|
||||
if (fp->ctf_sxlate != NULL)
|
||||
ctf_free(fp->ctf_sxlate, sizeof (uint_t) * fp->ctf_nsyms);
|
||||
|
||||
if (fp->ctf_txlate != NULL) {
|
||||
ctf_free(fp->ctf_txlate,
|
||||
sizeof (uint_t) * (fp->ctf_typemax + 1));
|
||||
}
|
||||
|
||||
if (fp->ctf_ptrtab != NULL) {
|
||||
ctf_free(fp->ctf_ptrtab,
|
||||
sizeof (ushort_t) * (fp->ctf_typemax + 1));
|
||||
}
|
||||
|
||||
ctf_hash_destroy(&fp->ctf_structs);
|
||||
ctf_hash_destroy(&fp->ctf_unions);
|
||||
ctf_hash_destroy(&fp->ctf_enums);
|
||||
ctf_hash_destroy(&fp->ctf_names);
|
||||
|
||||
ctf_free(fp, sizeof (ctf_file_t));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the CTF handle for the parent CTF container, if one exists.
|
||||
* Otherwise return NULL to indicate this container has no imported parent.
|
||||
*/
|
||||
ctf_file_t *
|
||||
ctf_parent_file(ctf_file_t *fp)
|
||||
{
|
||||
return (fp->ctf_parent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the name of the parent CTF container, if one exists. Otherwise
|
||||
* return NULL to indicate this container is a root container.
|
||||
*/
|
||||
const char *
|
||||
ctf_parent_name(ctf_file_t *fp)
|
||||
{
|
||||
return (fp->ctf_parname);
|
||||
}
|
||||
|
||||
/*
|
||||
* Import the types from the specified parent container by storing a pointer
|
||||
* to it in ctf_parent and incrementing its reference count. Only one parent
|
||||
* is allowed: if a parent already exists, it is replaced by the new parent.
|
||||
*/
|
||||
int
|
||||
ctf_import(ctf_file_t *fp, ctf_file_t *pfp)
|
||||
{
|
||||
if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0))
|
||||
return (ctf_set_errno(fp, EINVAL));
|
||||
|
||||
if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel)
|
||||
return (ctf_set_errno(fp, ECTF_DMODEL));
|
||||
|
||||
if (fp->ctf_parent != NULL)
|
||||
ctf_close(fp->ctf_parent);
|
||||
|
||||
if (pfp != NULL) {
|
||||
fp->ctf_flags |= LCTF_CHILD;
|
||||
pfp->ctf_refcnt++;
|
||||
}
|
||||
|
||||
fp->ctf_parent = pfp;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the data model constant for the CTF container.
|
||||
*/
|
||||
int
|
||||
ctf_setmodel(ctf_file_t *fp, int model)
|
||||
{
|
||||
const ctf_dmodel_t *dp;
|
||||
|
||||
for (dp = _libctf_models; dp->ctd_name != NULL; dp++) {
|
||||
if (dp->ctd_code == model) {
|
||||
fp->ctf_dmodel = dp;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
return (ctf_set_errno(fp, EINVAL));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the data model constant for the CTF container.
|
||||
*/
|
||||
int
|
||||
ctf_getmodel(ctf_file_t *fp)
|
||||
{
|
||||
return (fp->ctf_dmodel->ctd_code);
|
||||
}
|
||||
|
||||
void
|
||||
ctf_setspecific(ctf_file_t *fp, void *data)
|
||||
{
|
||||
fp->ctf_specific = data;
|
||||
}
|
||||
|
||||
void *
|
||||
ctf_getspecific(ctf_file_t *fp)
|
||||
{
|
||||
return (fp->ctf_specific);
|
||||
}
|
845
cddl/contrib/opensolaris/common/ctf/ctf_types.c
Normal file
845
cddl/contrib/opensolaris/common/ctf/ctf_types.c
Normal file
@ -0,0 +1,845 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <ctf_impl.h>
|
||||
|
||||
ssize_t
|
||||
ctf_get_ctt_size(const ctf_file_t *fp, const ctf_type_t *tp, ssize_t *sizep,
|
||||
ssize_t *incrementp)
|
||||
{
|
||||
ssize_t size, increment;
|
||||
|
||||
if (fp->ctf_version > CTF_VERSION_1 &&
|
||||
tp->ctt_size == CTF_LSIZE_SENT) {
|
||||
size = CTF_TYPE_LSIZE(tp);
|
||||
increment = sizeof (ctf_type_t);
|
||||
} else {
|
||||
size = tp->ctt_size;
|
||||
increment = sizeof (ctf_stype_t);
|
||||
}
|
||||
|
||||
if (sizep)
|
||||
*sizep = size;
|
||||
if (incrementp)
|
||||
*incrementp = increment;
|
||||
|
||||
return (size);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over the members of a STRUCT or UNION. We pass the name, member
|
||||
* type, and offset of each member to the specified callback function.
|
||||
*/
|
||||
int
|
||||
ctf_member_iter(ctf_file_t *fp, ctf_id_t type, ctf_member_f *func, void *arg)
|
||||
{
|
||||
ctf_file_t *ofp = fp;
|
||||
const ctf_type_t *tp;
|
||||
ssize_t size, increment;
|
||||
uint_t kind, n;
|
||||
int rc;
|
||||
|
||||
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
|
||||
kind = LCTF_INFO_KIND(fp, tp->ctt_info);
|
||||
|
||||
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
|
||||
return (ctf_set_errno(ofp, ECTF_NOTSOU));
|
||||
|
||||
if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
|
||||
const ctf_member_t *mp = (const ctf_member_t *)
|
||||
((uintptr_t)tp + increment);
|
||||
|
||||
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
|
||||
const char *name = ctf_strptr(fp, mp->ctm_name);
|
||||
if ((rc = func(name, mp->ctm_type, mp->ctm_offset,
|
||||
arg)) != 0)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
} else {
|
||||
const ctf_lmember_t *lmp = (const ctf_lmember_t *)
|
||||
((uintptr_t)tp + increment);
|
||||
|
||||
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
|
||||
const char *name = ctf_strptr(fp, lmp->ctlm_name);
|
||||
if ((rc = func(name, lmp->ctlm_type,
|
||||
(ulong_t)CTF_LMEM_OFFSET(lmp), arg)) != 0)
|
||||
return (rc);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over the members of an ENUM. We pass the string name and associated
|
||||
* integer value of each enum element to the specified callback function.
|
||||
*/
|
||||
int
|
||||
ctf_enum_iter(ctf_file_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg)
|
||||
{
|
||||
ctf_file_t *ofp = fp;
|
||||
const ctf_type_t *tp;
|
||||
const ctf_enum_t *ep;
|
||||
ssize_t increment;
|
||||
uint_t n;
|
||||
int rc;
|
||||
|
||||
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM)
|
||||
return (ctf_set_errno(ofp, ECTF_NOTENUM));
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, NULL, &increment);
|
||||
|
||||
ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
|
||||
|
||||
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
|
||||
const char *name = ctf_strptr(fp, ep->cte_name);
|
||||
if ((rc = func(name, ep->cte_value, arg)) != 0)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over every root (user-visible) type in the given CTF container.
|
||||
* We pass the type ID of each type to the specified callback function.
|
||||
*/
|
||||
int
|
||||
ctf_type_iter(ctf_file_t *fp, ctf_type_f *func, void *arg)
|
||||
{
|
||||
ctf_id_t id, max = fp->ctf_typemax;
|
||||
int rc, child = (fp->ctf_flags & LCTF_CHILD);
|
||||
|
||||
for (id = 1; id <= max; id++) {
|
||||
const ctf_type_t *tp = LCTF_INDEX_TO_TYPEPTR(fp, id);
|
||||
if (CTF_INFO_ISROOT(tp->ctt_info) &&
|
||||
(rc = func(CTF_INDEX_TO_TYPE(id, child), arg)) != 0)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Follow a given type through the graph for TYPEDEF, VOLATILE, CONST, and
|
||||
* RESTRICT nodes until we reach a "base" type node. This is useful when
|
||||
* we want to follow a type ID to a node that has members or a size. To guard
|
||||
* against infinite loops, we implement simplified cycle detection and check
|
||||
* each link against itself, the previous node, and the topmost node.
|
||||
*/
|
||||
ctf_id_t
|
||||
ctf_type_resolve(ctf_file_t *fp, ctf_id_t type)
|
||||
{
|
||||
ctf_id_t prev = type, otype = type;
|
||||
ctf_file_t *ofp = fp;
|
||||
const ctf_type_t *tp;
|
||||
|
||||
while ((tp = ctf_lookup_by_id(&fp, type)) != NULL) {
|
||||
switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
|
||||
case CTF_K_TYPEDEF:
|
||||
case CTF_K_VOLATILE:
|
||||
case CTF_K_CONST:
|
||||
case CTF_K_RESTRICT:
|
||||
if (tp->ctt_type == type || tp->ctt_type == otype ||
|
||||
tp->ctt_type == prev) {
|
||||
ctf_dprintf("type %ld cycle detected\n", otype);
|
||||
return (ctf_set_errno(ofp, ECTF_CORRUPT));
|
||||
}
|
||||
prev = type;
|
||||
type = tp->ctt_type;
|
||||
break;
|
||||
default:
|
||||
return (type);
|
||||
}
|
||||
}
|
||||
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup the given type ID and print a string name for it into buf. Return
|
||||
* the actual number of bytes (not including \0) needed to format the name.
|
||||
*/
|
||||
ssize_t
|
||||
ctf_type_lname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
|
||||
{
|
||||
ctf_decl_t cd;
|
||||
ctf_decl_node_t *cdp;
|
||||
ctf_decl_prec_t prec, lp, rp;
|
||||
int ptr, arr;
|
||||
uint_t k;
|
||||
|
||||
if (fp == NULL && type == CTF_ERR)
|
||||
return (-1); /* simplify caller code by permitting CTF_ERR */
|
||||
|
||||
ctf_decl_init(&cd, buf, len);
|
||||
ctf_decl_push(&cd, fp, type);
|
||||
|
||||
if (cd.cd_err != 0) {
|
||||
ctf_decl_fini(&cd);
|
||||
return (ctf_set_errno(fp, cd.cd_err));
|
||||
}
|
||||
|
||||
/*
|
||||
* If the type graph's order conflicts with lexical precedence order
|
||||
* for pointers or arrays, then we need to surround the declarations at
|
||||
* the corresponding lexical precedence with parentheses. This can
|
||||
* result in either a parenthesized pointer (*) as in int (*)() or
|
||||
* int (*)[], or in a parenthesized pointer and array as in int (*[])().
|
||||
*/
|
||||
ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER;
|
||||
arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY;
|
||||
|
||||
rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1;
|
||||
lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1;
|
||||
|
||||
k = CTF_K_POINTER; /* avoid leading whitespace (see below) */
|
||||
|
||||
for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) {
|
||||
for (cdp = ctf_list_next(&cd.cd_nodes[prec]);
|
||||
cdp != NULL; cdp = ctf_list_next(cdp)) {
|
||||
|
||||
ctf_file_t *rfp = fp;
|
||||
const ctf_type_t *tp =
|
||||
ctf_lookup_by_id(&rfp, cdp->cd_type);
|
||||
const char *name = ctf_strptr(rfp, tp->ctt_name);
|
||||
|
||||
if (k != CTF_K_POINTER && k != CTF_K_ARRAY)
|
||||
ctf_decl_sprintf(&cd, " ");
|
||||
|
||||
if (lp == prec) {
|
||||
ctf_decl_sprintf(&cd, "(");
|
||||
lp = -1;
|
||||
}
|
||||
|
||||
switch (cdp->cd_kind) {
|
||||
case CTF_K_INTEGER:
|
||||
case CTF_K_FLOAT:
|
||||
case CTF_K_TYPEDEF:
|
||||
ctf_decl_sprintf(&cd, "%s", name);
|
||||
break;
|
||||
case CTF_K_POINTER:
|
||||
ctf_decl_sprintf(&cd, "*");
|
||||
break;
|
||||
case CTF_K_ARRAY:
|
||||
ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n);
|
||||
break;
|
||||
case CTF_K_FUNCTION:
|
||||
ctf_decl_sprintf(&cd, "()");
|
||||
break;
|
||||
case CTF_K_STRUCT:
|
||||
case CTF_K_FORWARD:
|
||||
ctf_decl_sprintf(&cd, "struct %s", name);
|
||||
break;
|
||||
case CTF_K_UNION:
|
||||
ctf_decl_sprintf(&cd, "union %s", name);
|
||||
break;
|
||||
case CTF_K_ENUM:
|
||||
ctf_decl_sprintf(&cd, "enum %s", name);
|
||||
break;
|
||||
case CTF_K_VOLATILE:
|
||||
ctf_decl_sprintf(&cd, "volatile");
|
||||
break;
|
||||
case CTF_K_CONST:
|
||||
ctf_decl_sprintf(&cd, "const");
|
||||
break;
|
||||
case CTF_K_RESTRICT:
|
||||
ctf_decl_sprintf(&cd, "restrict");
|
||||
break;
|
||||
}
|
||||
|
||||
k = cdp->cd_kind;
|
||||
}
|
||||
|
||||
if (rp == prec)
|
||||
ctf_decl_sprintf(&cd, ")");
|
||||
}
|
||||
|
||||
if (cd.cd_len >= len)
|
||||
(void) ctf_set_errno(fp, ECTF_NAMELEN);
|
||||
|
||||
ctf_decl_fini(&cd);
|
||||
return (cd.cd_len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lookup the given type ID and print a string name for it into buf. If buf
|
||||
* is too small, return NULL: the ECTF_NAMELEN error is set on 'fp' for us.
|
||||
*/
|
||||
char *
|
||||
ctf_type_name(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
|
||||
{
|
||||
ssize_t rv = ctf_type_lname(fp, type, buf, len);
|
||||
return (rv >= 0 && rv < len ? buf : NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve the type down to a base type node, and then return the size
|
||||
* of the type storage in bytes.
|
||||
*/
|
||||
ssize_t
|
||||
ctf_type_size(ctf_file_t *fp, ctf_id_t type)
|
||||
{
|
||||
const ctf_type_t *tp;
|
||||
ssize_t size;
|
||||
ctf_arinfo_t ar;
|
||||
|
||||
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
|
||||
return (-1); /* errno is set for us */
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (-1); /* errno is set for us */
|
||||
|
||||
switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
|
||||
case CTF_K_POINTER:
|
||||
return (fp->ctf_dmodel->ctd_pointer);
|
||||
|
||||
case CTF_K_FUNCTION:
|
||||
return (0); /* function size is only known by symtab */
|
||||
|
||||
case CTF_K_ENUM:
|
||||
return (fp->ctf_dmodel->ctd_int);
|
||||
|
||||
case CTF_K_ARRAY:
|
||||
/*
|
||||
* Array size is not directly returned by stabs data. Instead,
|
||||
* it defines the element type and requires the user to perform
|
||||
* the multiplication. If ctf_get_ctt_size() returns zero, the
|
||||
* current version of ctfconvert does not compute member sizes
|
||||
* and we compute the size here on its behalf.
|
||||
*/
|
||||
if ((size = ctf_get_ctt_size(fp, tp, NULL, NULL)) > 0)
|
||||
return (size);
|
||||
|
||||
if (ctf_array_info(fp, type, &ar) == CTF_ERR ||
|
||||
(size = ctf_type_size(fp, ar.ctr_contents)) == CTF_ERR)
|
||||
return (-1); /* errno is set for us */
|
||||
|
||||
return (size * ar.ctr_nelems);
|
||||
|
||||
default:
|
||||
return (ctf_get_ctt_size(fp, tp, NULL, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolve the type down to a base type node, and then return the alignment
|
||||
* needed for the type storage in bytes.
|
||||
*/
|
||||
ssize_t
|
||||
ctf_type_align(ctf_file_t *fp, ctf_id_t type)
|
||||
{
|
||||
const ctf_type_t *tp;
|
||||
ctf_arinfo_t r;
|
||||
|
||||
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
|
||||
return (-1); /* errno is set for us */
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (-1); /* errno is set for us */
|
||||
|
||||
switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
|
||||
case CTF_K_POINTER:
|
||||
case CTF_K_FUNCTION:
|
||||
return (fp->ctf_dmodel->ctd_pointer);
|
||||
|
||||
case CTF_K_ARRAY:
|
||||
if (ctf_array_info(fp, type, &r) == CTF_ERR)
|
||||
return (-1); /* errno is set for us */
|
||||
return (ctf_type_align(fp, r.ctr_contents));
|
||||
|
||||
case CTF_K_STRUCT:
|
||||
case CTF_K_UNION: {
|
||||
uint_t n = LCTF_INFO_VLEN(fp, tp->ctt_info);
|
||||
ssize_t size, increment;
|
||||
size_t align = 0;
|
||||
const void *vmp;
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
|
||||
vmp = (uchar_t *)tp + increment;
|
||||
|
||||
if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_STRUCT)
|
||||
n = MIN(n, 1); /* only use first member for structs */
|
||||
|
||||
if (fp->ctf_version == CTF_VERSION_1 ||
|
||||
size < CTF_LSTRUCT_THRESH) {
|
||||
const ctf_member_t *mp = vmp;
|
||||
for (; n != 0; n--, mp++) {
|
||||
ssize_t am = ctf_type_align(fp, mp->ctm_type);
|
||||
align = MAX(align, am);
|
||||
}
|
||||
} else {
|
||||
const ctf_lmember_t *lmp = vmp;
|
||||
for (; n != 0; n--, lmp++) {
|
||||
ssize_t am = ctf_type_align(fp, lmp->ctlm_type);
|
||||
align = MAX(align, am);
|
||||
}
|
||||
}
|
||||
|
||||
return (align);
|
||||
}
|
||||
|
||||
case CTF_K_ENUM:
|
||||
return (fp->ctf_dmodel->ctd_int);
|
||||
|
||||
default:
|
||||
return (ctf_get_ctt_size(fp, tp, NULL, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the kind (CTF_K_* constant) for the specified type ID.
|
||||
*/
|
||||
int
|
||||
ctf_type_kind(ctf_file_t *fp, ctf_id_t type)
|
||||
{
|
||||
const ctf_type_t *tp;
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
return (LCTF_INFO_KIND(fp, tp->ctt_info));
|
||||
}
|
||||
|
||||
/*
|
||||
* If the type is one that directly references another type (such as POINTER),
|
||||
* then return the ID of the type to which it refers.
|
||||
*/
|
||||
ctf_id_t
|
||||
ctf_type_reference(ctf_file_t *fp, ctf_id_t type)
|
||||
{
|
||||
ctf_file_t *ofp = fp;
|
||||
const ctf_type_t *tp;
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
|
||||
case CTF_K_POINTER:
|
||||
case CTF_K_TYPEDEF:
|
||||
case CTF_K_VOLATILE:
|
||||
case CTF_K_CONST:
|
||||
case CTF_K_RESTRICT:
|
||||
return (tp->ctt_type);
|
||||
default:
|
||||
return (ctf_set_errno(ofp, ECTF_NOTREF));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Find a pointer to type by looking in fp->ctf_ptrtab. If we can't find a
|
||||
* pointer to the given type, see if we can compute a pointer to the type
|
||||
* resulting from resolving the type down to its base type and use that
|
||||
* instead. This helps with cases where the CTF data includes "struct foo *"
|
||||
* but not "foo_t *" and the user accesses "foo_t *" in the debugger.
|
||||
*/
|
||||
ctf_id_t
|
||||
ctf_type_pointer(ctf_file_t *fp, ctf_id_t type)
|
||||
{
|
||||
ctf_file_t *ofp = fp;
|
||||
ctf_id_t ntype;
|
||||
|
||||
if (ctf_lookup_by_id(&fp, type) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if ((ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]) != 0)
|
||||
return (CTF_INDEX_TO_TYPE(ntype, (fp->ctf_flags & LCTF_CHILD)));
|
||||
|
||||
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
|
||||
return (ctf_set_errno(ofp, ECTF_NOTYPE));
|
||||
|
||||
if (ctf_lookup_by_id(&fp, type) == NULL)
|
||||
return (ctf_set_errno(ofp, ECTF_NOTYPE));
|
||||
|
||||
if ((ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]) != 0)
|
||||
return (CTF_INDEX_TO_TYPE(ntype, (fp->ctf_flags & LCTF_CHILD)));
|
||||
|
||||
return (ctf_set_errno(ofp, ECTF_NOTYPE));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the encoding for the specified INTEGER or FLOAT.
|
||||
*/
|
||||
int
|
||||
ctf_type_encoding(ctf_file_t *fp, ctf_id_t type, ctf_encoding_t *ep)
|
||||
{
|
||||
ctf_file_t *ofp = fp;
|
||||
const ctf_type_t *tp;
|
||||
ssize_t increment;
|
||||
uint_t data;
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, NULL, &increment);
|
||||
|
||||
switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
|
||||
case CTF_K_INTEGER:
|
||||
data = *(const uint_t *)((uintptr_t)tp + increment);
|
||||
ep->cte_format = CTF_INT_ENCODING(data);
|
||||
ep->cte_offset = CTF_INT_OFFSET(data);
|
||||
ep->cte_bits = CTF_INT_BITS(data);
|
||||
break;
|
||||
case CTF_K_FLOAT:
|
||||
data = *(const uint_t *)((uintptr_t)tp + increment);
|
||||
ep->cte_format = CTF_FP_ENCODING(data);
|
||||
ep->cte_offset = CTF_FP_OFFSET(data);
|
||||
ep->cte_bits = CTF_FP_BITS(data);
|
||||
break;
|
||||
default:
|
||||
return (ctf_set_errno(ofp, ECTF_NOTINTFP));
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ctf_type_cmp(ctf_file_t *lfp, ctf_id_t ltype, ctf_file_t *rfp, ctf_id_t rtype)
|
||||
{
|
||||
int rval;
|
||||
|
||||
if (ltype < rtype)
|
||||
rval = -1;
|
||||
else if (ltype > rtype)
|
||||
rval = 1;
|
||||
else
|
||||
rval = 0;
|
||||
|
||||
if (lfp == rfp)
|
||||
return (rval);
|
||||
|
||||
if (CTF_TYPE_ISPARENT(ltype) && lfp->ctf_parent != NULL)
|
||||
lfp = lfp->ctf_parent;
|
||||
|
||||
if (CTF_TYPE_ISPARENT(rtype) && rfp->ctf_parent != NULL)
|
||||
rfp = rfp->ctf_parent;
|
||||
|
||||
if (lfp < rfp)
|
||||
return (-1);
|
||||
|
||||
if (lfp > rfp)
|
||||
return (1);
|
||||
|
||||
return (rval);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a boolean value indicating if two types are compatible integers or
|
||||
* floating-pointer values. This function returns true if the two types are
|
||||
* the same, or if they have the same ASCII name and encoding properties.
|
||||
* This function could be extended to test for compatibility for other kinds.
|
||||
*/
|
||||
int
|
||||
ctf_type_compat(ctf_file_t *lfp, ctf_id_t ltype,
|
||||
ctf_file_t *rfp, ctf_id_t rtype)
|
||||
{
|
||||
const ctf_type_t *ltp, *rtp;
|
||||
ctf_encoding_t le, re;
|
||||
ctf_arinfo_t la, ra;
|
||||
uint_t lkind, rkind;
|
||||
|
||||
if (ctf_type_cmp(lfp, ltype, rfp, rtype) == 0)
|
||||
return (1);
|
||||
|
||||
ltype = ctf_type_resolve(lfp, ltype);
|
||||
lkind = ctf_type_kind(lfp, ltype);
|
||||
|
||||
rtype = ctf_type_resolve(rfp, rtype);
|
||||
rkind = ctf_type_kind(rfp, rtype);
|
||||
|
||||
if (lkind != rkind ||
|
||||
(ltp = ctf_lookup_by_id(&lfp, ltype)) == NULL ||
|
||||
(rtp = ctf_lookup_by_id(&rfp, rtype)) == NULL ||
|
||||
strcmp(ctf_strptr(lfp, ltp->ctt_name),
|
||||
ctf_strptr(rfp, rtp->ctt_name)) != 0)
|
||||
return (0);
|
||||
|
||||
switch (lkind) {
|
||||
case CTF_K_INTEGER:
|
||||
case CTF_K_FLOAT:
|
||||
return (ctf_type_encoding(lfp, ltype, &le) == 0 &&
|
||||
ctf_type_encoding(rfp, rtype, &re) == 0 &&
|
||||
bcmp(&le, &re, sizeof (ctf_encoding_t)) == 0);
|
||||
case CTF_K_POINTER:
|
||||
return (ctf_type_compat(lfp, ctf_type_reference(lfp, ltype),
|
||||
rfp, ctf_type_reference(rfp, rtype)));
|
||||
case CTF_K_ARRAY:
|
||||
return (ctf_array_info(lfp, ltype, &la) == 0 &&
|
||||
ctf_array_info(rfp, rtype, &ra) == 0 &&
|
||||
la.ctr_nelems == ra.ctr_nelems && ctf_type_compat(
|
||||
lfp, la.ctr_contents, rfp, ra.ctr_contents) &&
|
||||
ctf_type_compat(lfp, la.ctr_index, rfp, ra.ctr_index));
|
||||
case CTF_K_STRUCT:
|
||||
case CTF_K_UNION:
|
||||
return (ctf_type_size(lfp, ltype) == ctf_type_size(rfp, rtype));
|
||||
case CTF_K_ENUM:
|
||||
case CTF_K_FORWARD:
|
||||
return (1); /* no other checks required for these type kinds */
|
||||
default:
|
||||
return (0); /* should not get here since we did a resolve */
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the type and offset for a given member of a STRUCT or UNION.
|
||||
*/
|
||||
int
|
||||
ctf_member_info(ctf_file_t *fp, ctf_id_t type, const char *name,
|
||||
ctf_membinfo_t *mip)
|
||||
{
|
||||
ctf_file_t *ofp = fp;
|
||||
const ctf_type_t *tp;
|
||||
ssize_t size, increment;
|
||||
uint_t kind, n;
|
||||
|
||||
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
|
||||
kind = LCTF_INFO_KIND(fp, tp->ctt_info);
|
||||
|
||||
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
|
||||
return (ctf_set_errno(ofp, ECTF_NOTSOU));
|
||||
|
||||
if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
|
||||
const ctf_member_t *mp = (const ctf_member_t *)
|
||||
((uintptr_t)tp + increment);
|
||||
|
||||
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
|
||||
if (strcmp(ctf_strptr(fp, mp->ctm_name), name) == 0) {
|
||||
mip->ctm_type = mp->ctm_type;
|
||||
mip->ctm_offset = mp->ctm_offset;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const ctf_lmember_t *lmp = (const ctf_lmember_t *)
|
||||
((uintptr_t)tp + increment);
|
||||
|
||||
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
|
||||
if (strcmp(ctf_strptr(fp, lmp->ctlm_name), name) == 0) {
|
||||
mip->ctm_type = lmp->ctlm_type;
|
||||
mip->ctm_offset = (ulong_t)CTF_LMEM_OFFSET(lmp);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (ctf_set_errno(ofp, ECTF_NOMEMBNAM));
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the array type, index, and size information for the specified ARRAY.
|
||||
*/
|
||||
int
|
||||
ctf_array_info(ctf_file_t *fp, ctf_id_t type, ctf_arinfo_t *arp)
|
||||
{
|
||||
ctf_file_t *ofp = fp;
|
||||
const ctf_type_t *tp;
|
||||
const ctf_array_t *ap;
|
||||
ssize_t increment;
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ARRAY)
|
||||
return (ctf_set_errno(ofp, ECTF_NOTARRAY));
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, NULL, &increment);
|
||||
|
||||
ap = (const ctf_array_t *)((uintptr_t)tp + increment);
|
||||
arp->ctr_contents = ap->cta_contents;
|
||||
arp->ctr_index = ap->cta_index;
|
||||
arp->ctr_nelems = ap->cta_nelems;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the specified value to the corresponding enum member name, if a
|
||||
* matching name can be found. Otherwise NULL is returned.
|
||||
*/
|
||||
const char *
|
||||
ctf_enum_name(ctf_file_t *fp, ctf_id_t type, int value)
|
||||
{
|
||||
ctf_file_t *ofp = fp;
|
||||
const ctf_type_t *tp;
|
||||
const ctf_enum_t *ep;
|
||||
ssize_t increment;
|
||||
uint_t n;
|
||||
|
||||
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
|
||||
return (NULL); /* errno is set for us */
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (NULL); /* errno is set for us */
|
||||
|
||||
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) {
|
||||
(void) ctf_set_errno(ofp, ECTF_NOTENUM);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, NULL, &increment);
|
||||
|
||||
ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
|
||||
|
||||
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
|
||||
if (ep->cte_value == value)
|
||||
return (ctf_strptr(fp, ep->cte_name));
|
||||
}
|
||||
|
||||
(void) ctf_set_errno(ofp, ECTF_NOENUMNAM);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert the specified enum tag name to the corresponding value, if a
|
||||
* matching name can be found. Otherwise CTF_ERR is returned.
|
||||
*/
|
||||
int
|
||||
ctf_enum_value(ctf_file_t *fp, ctf_id_t type, const char *name, int *valp)
|
||||
{
|
||||
ctf_file_t *ofp = fp;
|
||||
const ctf_type_t *tp;
|
||||
const ctf_enum_t *ep;
|
||||
ssize_t size, increment;
|
||||
uint_t n;
|
||||
|
||||
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) {
|
||||
(void) ctf_set_errno(ofp, ECTF_NOTENUM);
|
||||
return (CTF_ERR);
|
||||
}
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
|
||||
|
||||
ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
|
||||
|
||||
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
|
||||
if (strcmp(ctf_strptr(fp, ep->cte_name), name) == 0) {
|
||||
if (valp != NULL)
|
||||
*valp = ep->cte_value;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
(void) ctf_set_errno(ofp, ECTF_NOENUMNAM);
|
||||
return (CTF_ERR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively visit the members of any type. This function is used as the
|
||||
* engine for ctf_type_visit, below. We resolve the input type, recursively
|
||||
* invoke ourself for each type member if the type is a struct or union, and
|
||||
* then invoke the callback function on the current type. If any callback
|
||||
* returns non-zero, we abort and percolate the error code back up to the top.
|
||||
*/
|
||||
static int
|
||||
ctf_type_rvisit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg,
|
||||
const char *name, ulong_t offset, int depth)
|
||||
{
|
||||
ctf_id_t otype = type;
|
||||
const ctf_type_t *tp;
|
||||
ssize_t size, increment;
|
||||
uint_t kind, n;
|
||||
int rc;
|
||||
|
||||
if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
|
||||
return (CTF_ERR); /* errno is set for us */
|
||||
|
||||
if ((rc = func(name, otype, offset, depth, arg)) != 0)
|
||||
return (rc);
|
||||
|
||||
kind = LCTF_INFO_KIND(fp, tp->ctt_info);
|
||||
|
||||
if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
|
||||
return (0);
|
||||
|
||||
(void) ctf_get_ctt_size(fp, tp, &size, &increment);
|
||||
|
||||
if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
|
||||
const ctf_member_t *mp = (const ctf_member_t *)
|
||||
((uintptr_t)tp + increment);
|
||||
|
||||
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
|
||||
if ((rc = ctf_type_rvisit(fp, mp->ctm_type,
|
||||
func, arg, ctf_strptr(fp, mp->ctm_name),
|
||||
offset + mp->ctm_offset, depth + 1)) != 0)
|
||||
return (rc);
|
||||
}
|
||||
|
||||
} else {
|
||||
const ctf_lmember_t *lmp = (const ctf_lmember_t *)
|
||||
((uintptr_t)tp + increment);
|
||||
|
||||
for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
|
||||
if ((rc = ctf_type_rvisit(fp, lmp->ctlm_type,
|
||||
func, arg, ctf_strptr(fp, lmp->ctlm_name),
|
||||
offset + (ulong_t)CTF_LMEM_OFFSET(lmp),
|
||||
depth + 1)) != 0)
|
||||
return (rc);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recursively visit the members of any type. We pass the name, member
|
||||
* type, and offset of each member to the specified callback function.
|
||||
*/
|
||||
int
|
||||
ctf_type_visit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg)
|
||||
{
|
||||
return (ctf_type_rvisit(fp, type, func, arg, "", 0, 0));
|
||||
}
|
152
cddl/contrib/opensolaris/common/ctf/ctf_util.c
Normal file
152
cddl/contrib/opensolaris/common/ctf/ctf_util.c
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <ctf_impl.h>
|
||||
|
||||
/*
|
||||
* Simple doubly-linked list append routine. This implementation assumes that
|
||||
* each list element contains an embedded ctf_list_t as the first member.
|
||||
* An additional ctf_list_t is used to store the head (l_next) and tail
|
||||
* (l_prev) pointers. The current head and tail list elements have their
|
||||
* previous and next pointers set to NULL, respectively.
|
||||
*/
|
||||
void
|
||||
ctf_list_append(ctf_list_t *lp, void *new)
|
||||
{
|
||||
ctf_list_t *p = lp->l_prev; /* p = tail list element */
|
||||
ctf_list_t *q = new; /* q = new list element */
|
||||
|
||||
lp->l_prev = q;
|
||||
q->l_prev = p;
|
||||
q->l_next = NULL;
|
||||
|
||||
if (p != NULL)
|
||||
p->l_next = q;
|
||||
else
|
||||
lp->l_next = q;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepend the specified existing element to the given ctf_list_t. The
|
||||
* existing pointer should be pointing at a struct with embedded ctf_list_t.
|
||||
*/
|
||||
void
|
||||
ctf_list_prepend(ctf_list_t *lp, void *new)
|
||||
{
|
||||
ctf_list_t *p = new; /* p = new list element */
|
||||
ctf_list_t *q = lp->l_next; /* q = head list element */
|
||||
|
||||
lp->l_next = p;
|
||||
p->l_prev = NULL;
|
||||
p->l_next = q;
|
||||
|
||||
if (q != NULL)
|
||||
q->l_prev = p;
|
||||
else
|
||||
lp->l_prev = p;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete the specified existing element from the given ctf_list_t. The
|
||||
* existing pointer should be pointing at a struct with embedded ctf_list_t.
|
||||
*/
|
||||
void
|
||||
ctf_list_delete(ctf_list_t *lp, void *existing)
|
||||
{
|
||||
ctf_list_t *p = existing;
|
||||
|
||||
if (p->l_prev != NULL)
|
||||
p->l_prev->l_next = p->l_next;
|
||||
else
|
||||
lp->l_next = p->l_next;
|
||||
|
||||
if (p->l_next != NULL)
|
||||
p->l_next->l_prev = p->l_prev;
|
||||
else
|
||||
lp->l_prev = p->l_prev;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an encoded CTF string name into a pointer to a C string by looking
|
||||
* up the appropriate string table buffer and then adding the offset.
|
||||
*/
|
||||
const char *
|
||||
ctf_strraw(ctf_file_t *fp, uint_t name)
|
||||
{
|
||||
ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID(name)];
|
||||
|
||||
if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET(name) < ctsp->cts_len)
|
||||
return (ctsp->cts_strs + CTF_NAME_OFFSET(name));
|
||||
|
||||
/* string table not loaded or corrupt offset */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
const char *
|
||||
ctf_strptr(ctf_file_t *fp, uint_t name)
|
||||
{
|
||||
const char *s = ctf_strraw(fp, name);
|
||||
return (s != NULL ? s : "(?)");
|
||||
}
|
||||
|
||||
/*
|
||||
* Same strdup(3C), but use ctf_alloc() to do the memory allocation.
|
||||
*/
|
||||
char *
|
||||
ctf_strdup(const char *s1)
|
||||
{
|
||||
char *s2 = ctf_alloc(strlen(s1) + 1);
|
||||
|
||||
if (s2 != NULL)
|
||||
(void) strcpy(s2, s1);
|
||||
|
||||
return (s2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the specified error code into errp if it is non-NULL, and then
|
||||
* return NULL for the benefit of the caller.
|
||||
*/
|
||||
ctf_file_t *
|
||||
ctf_set_open_errno(int *errp, int error)
|
||||
{
|
||||
if (errp != NULL)
|
||||
*errp = error;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the specified error code into the CTF container, and then return
|
||||
* CTF_ERR for the benefit of the caller.
|
||||
*/
|
||||
long
|
||||
ctf_set_errno(ctf_file_t *fp, int err)
|
||||
{
|
||||
fp->ctf_errno = err;
|
||||
return (CTF_ERR);
|
||||
}
|
54
cddl/contrib/opensolaris/head/nlist.h
Normal file
54
cddl/contrib/opensolaris/head/nlist.h
Normal file
@ -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 */
|
55
cddl/contrib/opensolaris/head/note.h
Normal file
55
cddl/contrib/opensolaris/head/note.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright (c) 1994 by Sun Microsystems, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* note.h: interface for annotating source with info for tools
|
||||
*
|
||||
* NOTE is the default interface, but if the identifier NOTE is in use for
|
||||
* some other purpose, you may prepare a similar header file using your own
|
||||
* identifier, mapping that identifier to _NOTE. Also, exported header
|
||||
* files should *not* use NOTE, since the name may already be in use in
|
||||
* a program's namespace. Rather, exported header files should include
|
||||
* sys/note.h directly and use _NOTE. For consistency, all kernel source
|
||||
* should use _NOTE.
|
||||
*/
|
||||
|
||||
#ifndef _NOTE_H
|
||||
#define _NOTE_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI"
|
||||
|
||||
#include <sys/note.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NOTE _NOTE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _NOTE_H */
|
79
cddl/contrib/opensolaris/head/storclass.h
Normal file
79
cddl/contrib/opensolaris/head/storclass.h
Normal file
@ -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 */
|
230
cddl/contrib/opensolaris/head/syms.h
Normal file
230
cddl/contrib/opensolaris/head/syms.h
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License, Version 1.0 only
|
||||
* (the "License"). You may not use this file except in compliance
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/* Copyright (c) 1988 AT&T */
|
||||
/* All Rights Reserved */
|
||||
|
||||
|
||||
#ifndef _SYMS_H
|
||||
#define _SYMS_H
|
||||
|
||||
#pragma ident "%Z%%M% %I% %E% SMI" /* SVr4.0 2.8 */
|
||||
|
||||
/* Storage Classes are defined in storclass.h */
|
||||
#include <storclass.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Number of characters in a symbol name */
|
||||
#define SYMNMLEN 8
|
||||
/* Number of characters in a file name */
|
||||
#define FILNMLEN 14
|
||||
/* Number of array dimensions in auxiliary entry */
|
||||
#define DIMNUM 4
|
||||
|
||||
struct syment
|
||||
{
|
||||
union
|
||||
{
|
||||
char _n_name[SYMNMLEN]; /* old COFF version */
|
||||
struct
|
||||
{
|
||||
long _n_zeroes; /* new == 0 */
|
||||
long _n_offset; /* offset into string table */
|
||||
} _n_n;
|
||||
char *_n_nptr[2]; /* allows for overlaying */
|
||||
} _n;
|
||||
unsigned long n_value; /* value of symbol */
|
||||
short n_scnum; /* section number */
|
||||
unsigned short n_type; /* type and derived type */
|
||||
char n_sclass; /* storage class */
|
||||
char n_numaux; /* number of aux. entries */
|
||||
};
|
||||
|
||||
#define n_name _n._n_name
|
||||
#define n_nptr _n._n_nptr[1]
|
||||
#define n_zeroes _n._n_n._n_zeroes
|
||||
#define n_offset _n._n_n._n_offset
|
||||
|
||||
/*
|
||||
* Relocatable symbols have a section number of the
|
||||
* section in which they are defined. Otherwise, section
|
||||
* numbers have the following meanings:
|
||||
*/
|
||||
/* undefined symbol */
|
||||
#define N_UNDEF 0
|
||||
/* value of symbol is absolute */
|
||||
#define N_ABS -1
|
||||
/* special debugging symbol -- value of symbol is meaningless */
|
||||
#define N_DEBUG -2
|
||||
/* indicates symbol needs transfer vector (preload) */
|
||||
#define N_TV (unsigned short)-3
|
||||
|
||||
/* indicates symbol needs transfer vector (postload) */
|
||||
|
||||
#define P_TV (unsigned short)-4
|
||||
|
||||
/*
|
||||
* The fundamental type of a symbol packed into the low
|
||||
* 4 bits of the word.
|
||||
*/
|
||||
|
||||
#define _EF ".ef"
|
||||
|
||||
#define T_NULL 0
|
||||
#define T_ARG 1 /* function argument (only used by compiler) */
|
||||
#define T_CHAR 2 /* character */
|
||||
#define T_SHORT 3 /* short integer */
|
||||
#define T_INT 4 /* integer */
|
||||
#define T_LONG 5 /* long integer */
|
||||
#define T_FLOAT 6 /* floating point */
|
||||
#define T_DOUBLE 7 /* double word */
|
||||
#define T_STRUCT 8 /* structure */
|
||||
#define T_UNION 9 /* union */
|
||||
#define T_ENUM 10 /* enumeration */
|
||||
#define T_MOE 11 /* member of enumeration */
|
||||
#define T_UCHAR 12 /* unsigned character */
|
||||
#define T_USHORT 13 /* unsigned short */
|
||||
#define T_UINT 14 /* unsigned integer */
|
||||
#define T_ULONG 15 /* unsigned long */
|
||||
|
||||
/*
|
||||
* derived types are:
|
||||
*/
|
||||
|
||||
#define DT_NON 0 /* no derived type */
|
||||
#define DT_PTR 1 /* pointer */
|
||||
#define DT_FCN 2 /* function */
|
||||
#define DT_ARY 3 /* array */
|
||||
|
||||
/*
|
||||
* type packing constants
|
||||
*/
|
||||
|
||||
#define N_BTMASK 017
|
||||
#define N_TMASK 060
|
||||
#define N_TMASK1 0300
|
||||
#define N_TMASK2 0360
|
||||
#define N_BTSHFT 4
|
||||
#define N_TSHIFT 2
|
||||
|
||||
/*
|
||||
* MACROS
|
||||
*/
|
||||
|
||||
/* Basic Type of x */
|
||||
|
||||
#define BTYPE(x) ((x) & N_BTMASK)
|
||||
|
||||
/* Is x a pointer ? */
|
||||
|
||||
#define ISPTR(x) (((x) & N_TMASK) == (DT_PTR << N_BTSHFT))
|
||||
|
||||
/* Is x a function ? */
|
||||
|
||||
#define ISFCN(x) (((x) & N_TMASK) == (DT_FCN << N_BTSHFT))
|
||||
|
||||
/* Is x an array ? */
|
||||
|
||||
#define ISARY(x) (((x) & N_TMASK) == (DT_ARY << N_BTSHFT))
|
||||
|
||||
/* Is x a structure, union, or enumeration TAG? */
|
||||
|
||||
#define ISTAG(x) ((x) == C_STRTAG || (x) == C_UNTAG || (x) == C_ENTAG)
|
||||
|
||||
#define INCREF(x) ((((x)&~N_BTMASK)<<N_TSHIFT)|(DT_PTR<<N_BTSHFT)|(x&N_BTMASK))
|
||||
|
||||
#define DECREF(x) ((((x)>>N_TSHIFT)&~N_BTMASK)|((x)&N_BTMASK))
|
||||
|
||||
/*
|
||||
* AUXILIARY ENTRY FORMAT
|
||||
*/
|
||||
|
||||
union auxent
|
||||
{
|
||||
struct
|
||||
{
|
||||
long x_tagndx; /* str, un, or enum tag indx */
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
unsigned short x_lnno; /* declaration line */
|
||||
/* number */
|
||||
unsigned short x_size; /* str, union, array */
|
||||
/* size */
|
||||
} x_lnsz;
|
||||
long x_fsize; /* size of function */
|
||||
} x_misc;
|
||||
union
|
||||
{
|
||||
struct /* if ISFCN, tag, or .bb */
|
||||
{
|
||||
long x_lnnoptr; /* ptr to fcn line # */
|
||||
long x_endndx; /* entry ndx past */
|
||||
/* block end */
|
||||
} x_fcn;
|
||||
struct /* if ISARY, up to 4 dimen. */
|
||||
{
|
||||
unsigned short x_dimen[DIMNUM];
|
||||
} x_ary;
|
||||
} x_fcnary;
|
||||
unsigned short x_tvndx; /* tv index */
|
||||
} x_sym;
|
||||
struct
|
||||
{
|
||||
char x_fname[FILNMLEN];
|
||||
} x_file;
|
||||
struct
|
||||
{
|
||||
long x_scnlen; /* section length */
|
||||
unsigned short x_nreloc; /* number of reloc entries */
|
||||
unsigned short x_nlinno; /* number of line numbers */
|
||||
} x_scn;
|
||||
|
||||
struct
|
||||
{
|
||||
long x_tvfill; /* tv fill value */
|
||||
unsigned short x_tvlen; /* length of .tv */
|
||||
unsigned short x_tvran[2]; /* tv range */
|
||||
} x_tv; /* info about .tv section (in auxent of symbol .tv)) */
|
||||
};
|
||||
|
||||
#define SYMENT struct syment
|
||||
#define SYMESZ 18 /* sizeof(SYMENT) */
|
||||
|
||||
#define AUXENT union auxent
|
||||
#define AUXESZ 18 /* sizeof(AUXENT) */
|
||||
|
||||
/* Defines for "special" symbols */
|
||||
|
||||
#define _ETEXT "etext"
|
||||
#define _EDATA "edata"
|
||||
#define _END "end"
|
||||
#define _START "_start"
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _SYMS_H */
|
Loading…
Reference in New Issue
Block a user