Vendor import of DTrace support files from OpenSolaris.

This commit is contained in:
jb 2008-04-25 23:33:18 +00:00
parent ce71c4333f
commit cb8ce77727
26 changed files with 9898 additions and 0 deletions

View 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
}

View 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 */

View 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 */

View 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 */

File diff suppressed because it is too large Load Diff

View 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 */

View 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 */

View 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 */

View 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);
}

File diff suppressed because it is too large Load Diff

View 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);
}

View 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));
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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);
}

View 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;
}
}

View 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 */

View 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);
}

View 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);
}

View 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);
}

View 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));
}

View 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);
}

View 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 */

View 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 */

View 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 */

View 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 */