summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWez Furlong <wez@netevil.org>2011-04-03 14:37:41 -0400
committerWez Furlong <wez@netevil.org>2011-04-03 14:37:41 -0400
commit253acb864d82ab0406d6a6bcecd09e502c64b140 (patch)
tree115e5562e80acc02e7329053f258cf1c0e3e1e38
downloadctf-253acb864d82ab0406d6a6bcecd09e502c64b140.tar.gz
Pull in the CTF sources from the FreeBSD 8.2 tree; arrange for them
to build under autoconf.
-rw-r--r--.hgignore30
-rw-r--r--Makefile.am58
-rw-r--r--OPENSOLARIS.LICENSE384
-rw-r--r--README.md12
-rwxr-xr-xautogen.sh13
-rw-r--r--common/ctf_headers.h72
-rw-r--r--common/list.c228
-rw-r--r--common/list.h58
-rw-r--r--common/memory.c103
-rw-r--r--common/memory.h52
-rw-r--r--common/symbol.c61
-rw-r--r--common/symbol.h44
-rw-r--r--common/utils.c107
-rw-r--r--common/utils.h53
-rw-r--r--configure.ac55
-rw-r--r--cvt/alist.c214
-rw-r--r--cvt/alist.h57
-rw-r--r--cvt/barrier.c94
-rw-r--r--cvt/barrier.h62
-rw-r--r--cvt/compare.c93
-rw-r--r--cvt/ctf.c1277
-rw-r--r--cvt/ctfconvert.c254
-rw-r--r--cvt/ctfmerge.c1011
-rw-r--r--cvt/ctfmerge.h90
-rw-r--r--cvt/ctftools.h446
-rw-r--r--cvt/dwarf.c1838
-rw-r--r--cvt/fifo.c153
-rw-r--r--cvt/fifo.h54
-rw-r--r--cvt/fixup_tdescs.c276
-rw-r--r--cvt/hash.c286
-rw-r--r--cvt/hash.h59
-rw-r--r--cvt/iidesc.c194
-rw-r--r--cvt/input.c412
-rw-r--r--cvt/merge.c1139
-rw-r--r--cvt/output.c748
-rw-r--r--cvt/st_parse.c1205
-rw-r--r--cvt/stabs.c371
-rw-r--r--cvt/stack.c112
-rw-r--r--cvt/stack.h53
-rw-r--r--cvt/strtab.c253
-rw-r--r--cvt/strtab.h69
-rw-r--r--cvt/tdata.c483
-rw-r--r--cvt/traverse.c224
-rw-r--r--cvt/traverse.h71
-rw-r--r--cvt/util.c273
-rw-r--r--dump/dump.c1015
-rw-r--r--libctf/ctf_create.c1363
-rw-r--r--libctf/ctf_decl.c184
-rw-r--r--libctf/ctf_error.c97
-rw-r--r--libctf/ctf_hash.c178
-rw-r--r--libctf/ctf_impl.h385
-rw-r--r--libctf/ctf_labels.c153
-rw-r--r--libctf/ctf_lib.c450
-rw-r--r--libctf/ctf_lookup.c312
-rw-r--r--libctf/ctf_open.c952
-rw-r--r--libctf/ctf_subr.c81
-rw-r--r--libctf/ctf_types.c845
-rw-r--r--libctf/ctf_util.c152
-rw-r--r--man/ctfconvert.185
-rw-r--r--man/ctfdump.183
-rw-r--r--man/ctfmerge.1120
-rw-r--r--pctf/ctf.h360
-rw-r--r--pctf/ctf_api.h244
-rw-r--r--pctf/libctf.h60
-rw-r--r--pctf/sysmacros.h143
65 files changed, 20463 insertions, 0 deletions
diff --git a/.hgignore b/.hgignore
new file mode 100644
index 0000000..e1ead4d
--- /dev/null
+++ b/.hgignore
@@ -0,0 +1,30 @@
+syntax: glob
+*.o
+*.lo
+*.la
+*.dylib
+autom4te.cache
+.libs
+.deps
+Makefile
+Makefile.in
+aclocal.m4
+compile
+config.guess
+config.log
+config.status
+config.sub
+configure
+depcomp
+pctf_config.h
+pctf_config.h.in
+pctf_config.h.in~
+install-sh
+libtool
+ltmain.sh
+missing
+stamp-h1
+.dirstamp
+ctfmerge
+ctfconvert
+ctfdump
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..cad2bf8
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,58 @@
+AUTOMAKE_OPTIONS = subdir-objects
+bin_PROGRAMS = ctfconvert ctfmerge ctfdump
+CFLAGS += -I$(top_srcdir)
+lib_LTLIBRARIES = libpctf.la
+noinst_LTLIBRARIES = libpctfcommon.la libcvt.la
+man1_MANS = man/ctfconvert.1 man/ctfmerge.1 man/ctfdump.1
+include_HEADERS = pctf/libctf.h pctf/ctf_api.h pctf/ctf.h
+
+libpctfcommon_la_SOURCES = \
+ common/list.c \
+ common/memory.c \
+ common/symbol.c \
+ common/utils.c
+
+libpctf_la_SOURCES = \
+ libctf/ctf_create.c \
+ libctf/ctf_decl.c \
+ libctf/ctf_error.c \
+ libctf/ctf_hash.c \
+ libctf/ctf_labels.c \
+ libctf/ctf_lib.c \
+ libctf/ctf_lookup.c \
+ libctf/ctf_open.c \
+ libctf/ctf_subr.c \
+ libctf/ctf_types.c \
+ libctf/ctf_util.c
+
+ctfdump_SOURCES = dump/dump.c
+ctfdump_LDADD = libpctfcommon.la
+
+libcvt_la_SOURCES = \
+ cvt/alist.c \
+ cvt/barrier.c \
+ cvt/compare.c \
+ cvt/ctf.c \
+ cvt/dwarf.c \
+ cvt/fifo.c \
+ cvt/fixup_tdescs.c \
+ cvt/hash.c \
+ cvt/iidesc.c \
+ cvt/input.c \
+ cvt/merge.c \
+ cvt/output.c \
+ cvt/st_parse.c \
+ cvt/stabs.c \
+ cvt/stack.c \
+ cvt/strtab.c \
+ cvt/tdata.c \
+ cvt/traverse.c \
+ cvt/util.c
+
+ctfconvert_SOURCES = cvt/ctfconvert.c
+ctfconvert_LDADD = libcvt.la libpctfcommon.la
+
+ctfmerge_SOURCES = cvt/ctfmerge.c
+ctfmerge_LDADD = libcvt.la libpctfcommon.la
+
+
diff --git a/OPENSOLARIS.LICENSE b/OPENSOLARIS.LICENSE
new file mode 100644
index 0000000..da23621
--- /dev/null
+++ b/OPENSOLARIS.LICENSE
@@ -0,0 +1,384 @@
+Unless otherwise noted, all files in this distribution are released
+under the Common Development and Distribution License (CDDL).
+Exceptions are noted within the associated source files.
+
+--------------------------------------------------------------------
+
+
+COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0
+
+1. Definitions.
+
+ 1.1. "Contributor" means each individual or entity that creates
+ or contributes to the creation of Modifications.
+
+ 1.2. "Contributor Version" means the combination of the Original
+ Software, prior Modifications used by a Contributor (if any),
+ and the Modifications made by that particular Contributor.
+
+ 1.3. "Covered Software" means (a) the Original Software, or (b)
+ Modifications, or (c) the combination of files containing
+ Original Software with files containing Modifications, in
+ each case including portions thereof.
+
+ 1.4. "Executable" means the Covered Software in any form other
+ than Source Code.
+
+ 1.5. "Initial Developer" means the individual or entity that first
+ makes Original Software available under this License.
+
+ 1.6. "Larger Work" means a work which combines Covered Software or
+ portions thereof with code not governed by the terms of this
+ License.
+
+ 1.7. "License" means this document.
+
+ 1.8. "Licensable" means having the right to grant, to the maximum
+ extent possible, whether at the time of the initial grant or
+ subsequently acquired, any and all of the rights conveyed
+ herein.
+
+ 1.9. "Modifications" means the Source Code and Executable form of
+ any of the following:
+
+ A. Any file that results from an addition to, deletion from or
+ modification of the contents of a file containing Original
+ Software or previous Modifications;
+
+ B. Any new file that contains any part of the Original
+ Software or previous Modifications; or
+
+ C. Any new file that is contributed or otherwise made
+ available under the terms of this License.
+
+ 1.10. "Original Software" means the Source Code and Executable
+ form of computer software code that is originally released
+ under this License.
+
+ 1.11. "Patent Claims" means any patent claim(s), now owned or
+ hereafter acquired, including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by
+ grantor.
+
+ 1.12. "Source Code" means (a) the common form of computer software
+ code in which modifications are made and (b) associated
+ documentation included in or with such code.
+
+ 1.13. "You" (or "Your") means an individual or a legal entity
+ exercising rights under, and complying with all of the terms
+ of, this License. For legal entities, "You" includes any
+ entity which controls, is controlled by, or is under common
+ control with You. For purposes of this definition,
+ "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by
+ contract or otherwise, or (b) ownership of more than fifty
+ percent (50%) of the outstanding shares or beneficial
+ ownership of such entity.
+
+2. License Grants.
+
+ 2.1. The Initial Developer Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and
+ subject to third party intellectual property claims, the Initial
+ Developer hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Initial Developer, to use,
+ reproduce, modify, display, perform, sublicense and
+ distribute the Original Software (or portions thereof),
+ with or without Modifications, and/or as part of a Larger
+ Work; and
+
+ (b) under Patent Claims infringed by the making, using or
+ selling of Original Software, to make, have made, use,
+ practice, sell, and offer for sale, and/or otherwise
+ dispose of the Original Software (or portions thereof).
+
+ (c) The licenses granted in Sections 2.1(a) and (b) are
+ effective on the date Initial Developer first distributes
+ or otherwise makes the Original Software available to a
+ third party under the terms of this License.
+
+ (d) Notwithstanding Section 2.1(b) above, no patent license is
+ granted: (1) for code that You delete from the Original
+ Software, or (2) for infringements caused by: (i) the
+ modification of the Original Software, or (ii) the
+ combination of the Original Software with other software
+ or devices.
+
+ 2.2. Contributor Grant.
+
+ Conditioned upon Your compliance with Section 3.1 below and
+ subject to third party intellectual property claims, each
+ Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ (a) under intellectual property rights (other than patent or
+ trademark) Licensable by Contributor to use, reproduce,
+ modify, display, perform, sublicense and distribute the
+ Modifications created by such Contributor (or portions
+ thereof), either on an unmodified basis, with other
+ Modifications, as Covered Software and/or as part of a
+ Larger Work; and
+
+ (b) under Patent Claims infringed by the making, using, or
+ selling of Modifications made by that Contributor either
+ alone and/or in combination with its Contributor Version
+ (or portions of such combination), to make, use, sell,
+ offer for sale, have made, and/or otherwise dispose of:
+ (1) Modifications made by that Contributor (or portions
+ thereof); and (2) the combination of Modifications made by
+ that Contributor with its Contributor Version (or portions
+ of such combination).
+
+ (c) The licenses granted in Sections 2.2(a) and 2.2(b) are
+ effective on the date Contributor first distributes or
+ otherwise makes the Modifications available to a third
+ party.
+
+ (d) Notwithstanding Section 2.2(b) above, no patent license is
+ granted: (1) for any code that Contributor has deleted
+ from the Contributor Version; (2) for infringements caused
+ by: (i) third party modifications of Contributor Version,
+ or (ii) the combination of Modifications made by that
+ Contributor with other software (except as part of the
+ Contributor Version) or other devices; or (3) under Patent
+ Claims infringed by Covered Software in the absence of
+ Modifications made by that Contributor.
+
+3. Distribution Obligations.
+
+ 3.1. Availability of Source Code.
+
+ Any Covered Software that You distribute or otherwise make
+ available in Executable form must also be made available in Source
+ Code form and that Source Code form must be distributed only under
+ the terms of this License. You must include a copy of this
+ License with every copy of the Source Code form of the Covered
+ Software You distribute or otherwise make available. You must
+ inform recipients of any such Covered Software in Executable form
+ as to how they can obtain such Covered Software in Source Code
+ form in a reasonable manner on or through a medium customarily
+ used for software exchange.
+
+ 3.2. Modifications.
+
+ The Modifications that You create or to which You contribute are
+ governed by the terms of this License. You represent that You
+ believe Your Modifications are Your original creation(s) and/or
+ You have sufficient rights to grant the rights conveyed by this
+ License.
+
+ 3.3. Required Notices.
+
+ You must include a notice in each of Your Modifications that
+ identifies You as the Contributor of the Modification. You may
+ not remove or alter any copyright, patent or trademark notices
+ contained within the Covered Software, or any notices of licensing
+ or any descriptive text giving attribution to any Contributor or
+ the Initial Developer.
+
+ 3.4. Application of Additional Terms.
+
+ You may not offer or impose any terms on any Covered Software in
+ Source Code form that alters or restricts the applicable version
+ of this License or the recipients' rights hereunder. You may
+ choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of
+ Covered Software. However, you may do so only on Your own behalf,
+ and not on behalf of the Initial Developer or any Contributor.
+ You must make it absolutely clear that any such warranty, support,
+ indemnity or liability obligation is offered by You alone, and You
+ hereby agree to indemnify the Initial Developer and every
+ Contributor for any liability incurred by the Initial Developer or
+ such Contributor as a result of warranty, support, indemnity or
+ liability terms You offer.
+
+ 3.5. Distribution of Executable Versions.
+
+ You may distribute the Executable form of the Covered Software
+ under the terms of this License or under the terms of a license of
+ Your choice, which may contain terms different from this License,
+ provided that You are in compliance with the terms of this License
+ and that the license for the Executable form does not attempt to
+ limit or alter the recipient's rights in the Source Code form from
+ the rights set forth in this License. If You distribute the
+ Covered Software in Executable form under a different license, You
+ must make it absolutely clear that any terms which differ from
+ this License are offered by You alone, not by the Initial
+ Developer or Contributor. You hereby agree to indemnify the
+ Initial Developer and every Contributor for any liability incurred
+ by the Initial Developer or such Contributor as a result of any
+ such terms You offer.
+
+ 3.6. Larger Works.
+
+ You may create a Larger Work by combining Covered Software with
+ other code not governed by the terms of this License and
+ distribute the Larger Work as a single product. In such a case,
+ You must make sure the requirements of this License are fulfilled
+ for the Covered Software.
+
+4. Versions of the License.
+
+ 4.1. New Versions.
+
+ Sun Microsystems, Inc. is the initial license steward and may
+ publish revised and/or new versions of this License from time to
+ time. Each version will be given a distinguishing version number.
+ Except as provided in Section 4.3, no one other than the license
+ steward has the right to modify this License.
+
+ 4.2. Effect of New Versions.
+
+ You may always continue to use, distribute or otherwise make the
+ Covered Software available under the terms of the version of the
+ License under which You originally received the Covered Software.
+ If the Initial Developer includes a notice in the Original
+ Software prohibiting it from being distributed or otherwise made
+ available under any subsequent version of the License, You must
+ distribute and make the Covered Software available under the terms
+ of the version of the License under which You originally received
+ the Covered Software. Otherwise, You may also choose to use,
+ distribute or otherwise make the Covered Software available under
+ the terms of any subsequent version of the License published by
+ the license steward.
+
+ 4.3. Modified Versions.
+
+ When You are an Initial Developer and You want to create a new
+ license for Your Original Software, You may create and use a
+ modified version of this License if You: (a) rename the license
+ and remove any references to the name of the license steward
+ (except to note that the license differs from this License); and
+ (b) otherwise make it clear that the license contains terms which
+ differ from this License.
+
+5. DISCLAIMER OF WARRANTY.
+
+ COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS"
+ BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
+ INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED
+ SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR
+ PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND
+ PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY
+ COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE
+ INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY
+ NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF
+ WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF
+ ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS
+ DISCLAIMER.
+
+6. TERMINATION.
+
+ 6.1. This License and the rights granted hereunder will terminate
+ automatically if You fail to comply with terms herein and fail to
+ cure such breach within 30 days of becoming aware of the breach.
+ Provisions which, by their nature, must remain in effect beyond
+ the termination of this License shall survive.
+
+ 6.2. If You assert a patent infringement claim (excluding
+ declaratory judgment actions) against Initial Developer or a
+ Contributor (the Initial Developer or Contributor against whom You
+ assert such claim is referred to as "Participant") alleging that
+ the Participant Software (meaning the Contributor Version where
+ the Participant is a Contributor or the Original Software where
+ the Participant is the Initial Developer) directly or indirectly
+ infringes any patent, then any and all rights granted directly or
+ indirectly to You by such Participant, the Initial Developer (if
+ the Initial Developer is not the Participant) and all Contributors
+ under Sections 2.1 and/or 2.2 of this License shall, upon 60 days
+ notice from Participant terminate prospectively and automatically
+ at the expiration of such 60 day notice period, unless if within
+ such 60 day period You withdraw Your claim with respect to the
+ Participant Software against such Participant either unilaterally
+ or pursuant to a written agreement with Participant.
+
+ 6.3. In the event of termination under Sections 6.1 or 6.2 above,
+ all end user licenses that have been validly granted by You or any
+ distributor hereunder prior to termination (excluding licenses
+ granted to You by any distributor) shall survive termination.
+
+7. LIMITATION OF LIABILITY.
+
+ UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT
+ (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE
+ INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF
+ COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE
+ LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR
+ CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT
+ LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK
+ STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER
+ COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN
+ INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF
+ LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL
+ INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT
+ APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO
+ NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR
+ CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT
+ APPLY TO YOU.
+
+8. U.S. GOVERNMENT END USERS.
+
+ The Covered Software is a "commercial item," as that term is
+ defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial
+ computer software" (as that term is defined at 48
+ C.F.R. 252.227-7014(a)(1)) and "commercial computer software
+ documentation" as such terms are used in 48 C.F.R. 12.212
+ (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48
+ C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all
+ U.S. Government End Users acquire Covered Software with only those
+ rights set forth herein. This U.S. Government Rights clause is in
+ lieu of, and supersedes, any other FAR, DFAR, or other clause or
+ provision that addresses Government rights in computer software
+ under this License.
+
+9. MISCELLANEOUS.
+
+ This License represents the complete agreement concerning subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. This License shall be governed
+ by the law of the jurisdiction specified in a notice contained
+ within the Original Software (except to the extent applicable law,
+ if any, provides otherwise), excluding such jurisdiction's
+ conflict-of-law provisions. Any litigation relating to this
+ License shall be subject to the jurisdiction of the courts located
+ in the jurisdiction and venue specified in a notice contained
+ within the Original Software, with the losing party responsible
+ for costs, including, without limitation, court costs and
+ reasonable attorneys' fees and expenses. The application of the
+ United Nations Convention on Contracts for the International Sale
+ of Goods is expressly excluded. Any law or regulation which
+ provides that the language of a contract shall be construed
+ against the drafter shall not apply to this License. You agree
+ that You alone are responsible for compliance with the United
+ States export administration regulations (and the export control
+ laws and regulation of any other countries) when You use,
+ distribute or otherwise make available any Covered Software.
+
+10. RESPONSIBILITY FOR CLAIMS.
+
+ As between Initial Developer and the Contributors, each party is
+ responsible for claims and damages arising, directly or
+ indirectly, out of its utilization of rights under this License
+ and You agree to work with Initial Developer and Contributors to
+ distribute such responsibility on an equitable basis. Nothing
+ herein is intended or shall be deemed to constitute any admission
+ of liability.
+
+--------------------------------------------------------------------
+
+NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND
+DISTRIBUTION LICENSE (CDDL)
+
+For Covered Software in this distribution, this License shall
+be governed by the laws of the State of California (excluding
+conflict-of-law provisions).
+
+Any litigation relating to this License shall be subject to the
+jurisdiction of the Federal Courts of the Northern District of
+California and the state courts of the State of California, with
+venue lying in Santa Clara County, California.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..90667dd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,12 @@
+Portable CTF
+------------
+
+This distribution contains the Compact ANSI-C Type Format source code,
+re-arranged for portability and re-use.
+
+It does not use the system CTF facilities, as those are explicitly labelled
+as unstable and subject to change.
+
+Instead, it provides those same APIs as supported functions, but these are
+namespaced as pctf (header file and library name; function names are unchanged).
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..545e1c0
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,13 @@
+#!/bin/sh -x
+# vim:ts=2:sw=2:et:
+
+if test `uname` = "Darwin" ; then
+ glibtoolize --automake
+else
+ libtoolize --automake
+fi
+autoheader
+aclocal
+automake --add-missing --foreign
+autoconf
+
diff --git a/common/ctf_headers.h b/common/ctf_headers.h
new file mode 100644
index 0000000..b00b8fd
--- /dev/null
+++ b/common/ctf_headers.h
@@ -0,0 +1,72 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CTF_HEADERS_H
+#define _CTF_HEADERS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Because the ON tools are executed on the system where they are built,
+ * the tools need to include the headers installed on the build system,
+ * rather than those in the ON source tree. However, some of the headers
+ * required by the tools are part of the ON source tree, but not delivered
+ * as part of Solaris. These include the following:
+ *
+ * $(SRC)/lib/libctf/common/libctf.h
+ * $(SRC)/uts/common/sys/ctf_api.h
+ * $(SRC)/uts/common/sys/ctf.h
+ *
+ * These headers get installed in the proto area in the build environment
+ * under $(ROOT)/usr/include and $(ROOT)/usr/include/sys. Though these
+ * headers are not part of the release, in releases including and prior to
+ * Solaris 9, they did get installed on the build system via bfu. Therefore,
+ * we can not simply force the order of inclusion with -I/usr/include first
+ * in Makefile.ctf because we might actually get downlevel versions of the
+ * ctf headers. Depending on the order of the -I includes, we can also have
+ * a problem with mismatched headers when building the ctf tools with some
+ * headers getting pulled in from /usr/include and others from
+ * $(SRC)/uts/common/sys.
+ *
+ * To address the problem, we have done two things:
+ * 1) Created this header with a specific order of inclusion for the
+ * ctf headers. Because the <libctf.h> header includes <sys/ctf_api.h>
+ * which in turn includes <sys/ctf.h> we need to include these in
+ * reverse order to guarantee that we get the correct versions of
+ * the headers.
+ * 2) In $(SRC)/tools/ctf/Makefile.ctf, we order the -I includes such
+ * that we first search the directories where the ctf headers
+ * live, followed by /usr/include, followed by $(SRC)/uts/common.
+ * This last -I include is needed in order to prevent a build failure
+ * when <sys/ctf_api.h> is included via a nested #include rather than
+ * an explicit path #include.
+ */
+
+#include <uts/common/sys/ctf.h>
+#include <uts/common/sys/ctf_api.h>
+#include <lib/libctf/common/libctf.h>
+
+#endif /* _CTF_HEADERS_H */
diff --git a/common/list.c b/common/list.c
new file mode 100644
index 0000000..4958f27
--- /dev/null
+++ b/common/list.c
@@ -0,0 +1,228 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating linked lists
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "list.h"
+#include "memory.h"
+
+struct list {
+ void *l_data;
+ struct list *l_next;
+};
+
+/* Add an element to a list */
+void
+list_add(list_t **list, void *data)
+{
+ list_t *le;
+
+ le = xmalloc(sizeof (list_t));
+ le->l_data = data;
+ le->l_next = *list;
+ *list = le;
+}
+
+/* Add an element to a sorted list */
+void
+slist_add(list_t **list, void *data, int (*cmp)(void *, void *))
+{
+ list_t **nextp;
+
+ for (nextp = list; *nextp; nextp = &((*nextp)->l_next)) {
+ if (cmp((*nextp)->l_data, data) > 0)
+ break;
+ }
+
+ list_add(nextp, data);
+}
+
+/*ARGSUSED2*/
+static int
+list_defcmp(void *d1, void *d2, void *private __unused)
+{
+ return (d1 != d2);
+}
+
+void *
+list_remove(list_t **list, void *data, int (*cmp)(void *, void *, void *),
+ void *private)
+{
+ list_t *le, **le2;
+ void *led;
+
+ if (!cmp)
+ cmp = list_defcmp;
+
+ for (le = *list, le2 = list; le; le2 = &le->l_next, le = le->l_next) {
+ if (cmp(le->l_data, data, private) == 0) {
+ *le2 = le->l_next;
+ led = le->l_data;
+ free(le);
+ return (led);
+ }
+ }
+
+ return (NULL);
+}
+
+void
+list_free(list_t *list, void (*datafree)(void *, void *), void *private)
+{
+ list_t *le;
+
+ while (list) {
+ le = list;
+ list = list->l_next;
+ if (le->l_data && datafree)
+ datafree(le->l_data, private);
+ free(le);
+ }
+}
+
+/*
+ * This iterator is specifically designed to tolerate the deletion of the
+ * node being iterated over.
+ */
+int
+list_iter(list_t *list, int (*func)(void *, void *), void *private)
+{
+ list_t *lnext;
+ int cumrc = 0;
+ int cbrc;
+
+ while (list) {
+ lnext = list->l_next;
+ if ((cbrc = func(list->l_data, private)) < 0)
+ return (cbrc);
+ cumrc += cbrc;
+ list = lnext;
+ }
+
+ return (cumrc);
+}
+
+/*ARGSUSED*/
+static int
+list_count_cb(void *data __unused, void *private __unused)
+{
+ return (1);
+}
+
+int
+list_count(list_t *list)
+{
+ return (list_iter(list, list_count_cb, NULL));
+}
+
+int
+list_empty(list_t *list)
+{
+ return (list == NULL);
+}
+
+void *
+list_find(list_t *list, void *tmpl, int (*cmp)(void *, void *))
+{
+ for (; list; list = list->l_next) {
+ if (cmp(list->l_data, tmpl) == 0)
+ return (list->l_data);
+ }
+
+ return (NULL);
+}
+
+void *
+list_first(list_t *list)
+{
+ return (list ? list->l_data : NULL);
+}
+
+void
+list_concat(list_t **list1, list_t *list2)
+{
+ list_t *l, *last;
+
+ for (l = *list1, last = NULL; l; last = l, l = l->l_next)
+ continue;
+
+ if (last == NULL)
+ *list1 = list2;
+ else
+ last->l_next = list2;
+}
+
+/*
+ * Merges two sorted lists. Equal nodes (as determined by cmp) are retained.
+ */
+void
+slist_merge(list_t **list1p, list_t *list2, int (*cmp)(void *, void *))
+{
+ list_t *list1, *next2;
+ list_t *last1 = NULL;
+
+ if (*list1p == NULL) {
+ *list1p = list2;
+ return;
+ }
+
+ list1 = *list1p;
+ while (list2 != NULL) {
+ if (cmp(list1->l_data, list2->l_data) > 0) {
+ next2 = list2->l_next;
+
+ if (last1 == NULL) {
+ /* Insert at beginning */
+ *list1p = last1 = list2;
+ list2->l_next = list1;
+ } else {
+ list2->l_next = list1;
+ last1->l_next = list2;
+ last1 = list2;
+ }
+
+ list2 = next2;
+ } else {
+
+ last1 = list1;
+ list1 = list1->l_next;
+
+ if (list1 == NULL) {
+ /* Add the rest to the end of list1 */
+ last1->l_next = list2;
+ list2 = NULL;
+ }
+ }
+ }
+}
diff --git a/common/list.h b/common/list.h
new file mode 100644
index 0000000..2e41271
--- /dev/null
+++ b/common/list.h
@@ -0,0 +1,58 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _LIST_H
+#define _LIST_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating linked lists
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct list list_t;
+
+void list_add(list_t **, void *);
+void slist_add(list_t **, void *, int (*)(void *, void *));
+void *list_remove(list_t **, void *, int (*)(void *, void *, void *), void *);
+void list_free(list_t *, void (*)(void *, void *), void *);
+void *list_find(list_t *, void *, int (*)(void *, void *));
+void *list_first(list_t *);
+int list_iter(list_t *, int (*)(void *, void *), void *);
+int list_count(list_t *);
+int list_empty(list_t *);
+void list_concat(list_t **, list_t *);
+void slist_merge(list_t **, list_t *, int (*)(void *, void *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIST_H */
diff --git a/common/memory.c b/common/memory.c
new file mode 100644
index 0000000..e16044a
--- /dev/null
+++ b/common/memory.c
@@ -0,0 +1,103 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for memory management
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "memory.h"
+
+static void
+memory_bailout(void)
+{
+ (void) fprintf(stderr, "Out of memory\n");
+ exit(1);
+}
+
+void *
+xmalloc(size_t size)
+{
+ void *mem;
+
+ if ((mem = malloc(size)) == NULL)
+ memory_bailout();
+
+ return (mem);
+}
+
+void *
+xcalloc(size_t size)
+{
+ void *mem;
+
+ mem = xmalloc(size);
+ bzero(mem, size);
+
+ return (mem);
+}
+
+char *
+xstrdup(const char *str)
+{
+ char *newstr;
+
+ if ((newstr = strdup(str)) == NULL)
+ memory_bailout();
+
+ return (newstr);
+}
+
+char *
+xstrndup(char *str, size_t len)
+{
+ char *newstr;
+
+ if ((newstr = malloc(len + 1)) == NULL)
+ memory_bailout();
+
+ (void) strncpy(newstr, str, len);
+ newstr[len] = '\0';
+
+ return (newstr);
+}
+
+void *
+xrealloc(void *ptr, size_t size)
+{
+ void *mem;
+
+ if ((mem = realloc(ptr, size)) == NULL)
+ memory_bailout();
+
+ return (mem);
+}
diff --git a/common/memory.h b/common/memory.h
new file mode 100644
index 0000000..88ca31b
--- /dev/null
+++ b/common/memory.h
@@ -0,0 +1,52 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _MEMORY_H
+#define _MEMORY_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for memory management
+ */
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void *xmalloc(size_t);
+void *xcalloc(size_t);
+char *xstrdup(const char *);
+char *xstrndup(char *, size_t);
+void *xrealloc(void *, size_t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MEMORY_H */
diff --git a/common/symbol.c b/common/symbol.c
new file mode 100644
index 0000000..f8313e6
--- /dev/null
+++ b/common/symbol.c
@@ -0,0 +1,61 @@
+/*
+ * 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 "libctf/ctf_impl.h"
+
+#include "symbol.h"
+
+int
+ignore_symbol(GElf_Sym *sym, const char *name)
+{
+ uchar_t type = GELF_ST_TYPE(sym->st_info);
+
+ /*
+ * As an optimization, we do not output function or data object
+ * records for undefined or anonymous symbols.
+ */
+ if (sym->st_shndx == SHN_UNDEF || sym->st_name == 0)
+ return (1);
+
+ /*
+ * _START_ and _END_ are added to the symbol table by the
+ * linker, and will never have associated type information.
+ */
+ if (strcmp(name, "_START_") == 0 || strcmp(name, "_END_") == 0)
+ return (1);
+
+ /*
+ * Do not output records for absolute-valued object symbols
+ * that have value zero. The compiler insists on generating
+ * things like this for __fsr_init_value settings, etc.
+ */
+ if (type == STT_OBJECT && sym->st_shndx == SHN_ABS &&
+ sym->st_value == 0)
+ return (1);
+ return (0);
+}
diff --git a/common/symbol.h b/common/symbol.h
new file mode 100644
index 0000000..92b5e89
--- /dev/null
+++ b/common/symbol.h
@@ -0,0 +1,44 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _SYMBOL_H
+#define _SYMBOL_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <gelf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int ignore_symbol(GElf_Sym *sym, const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _SYMBOL_H */
diff --git a/common/utils.c b/common/utils.c
new file mode 100644
index 0000000..49dc25e
--- /dev/null
+++ b/common/utils.c
@@ -0,0 +1,107 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libctf/ctf_impl.h"
+#include "common/utils.h"
+
+/*LINTLIBRARY*/
+
+static const char *pname;
+
+#pragma init(getpname)
+const char *
+getpname(void)
+{
+ const char *p, *q;
+
+ if (pname != NULL)
+ return (pname);
+
+#if HAVE_GETEXECNAME
+ p = getexecname();
+#elif HAVE_GETPROGNAME
+ p = getprogname();
+#else
+# error dont know how to find my executable name
+#endif
+
+ if (p != NULL)
+ q = strrchr(p, '/');
+ else
+ q = NULL;
+
+ if (q == NULL)
+ pname = p;
+ else
+ pname = q + 1;
+
+ return (pname);
+}
+
+void
+vwarn(const char *format, va_list alist)
+{
+ int err = errno;
+
+ if (pname != NULL)
+ (void) fprintf(stderr, "%s: ", pname);
+
+ (void) vfprintf(stderr, format, alist);
+
+ if (strchr(format, '\n') == NULL)
+ (void) fprintf(stderr, ": %s\n", strerror(err));
+}
+
+/*PRINTFLIKE1*/
+void
+warn(const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ vwarn(format, alist);
+ va_end(alist);
+}
+
+void
+vdie(const char *format, va_list alist)
+{
+ vwarn(format, alist);
+ exit(E_ERROR);
+}
+
+/*PRINTFLIKE1*/
+void
+die(const char *format, ...)
+{
+ va_list alist;
+
+ va_start(alist, format);
+ vdie(format, alist);
+ va_end(alist);
+}
diff --git a/common/utils.h b/common/utils.h
new file mode 100644
index 0000000..9b07361
--- /dev/null
+++ b/common/utils.h
@@ -0,0 +1,53 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1998-2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _UTILS_H
+#define _UTILS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <stdarg.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define E_SUCCESS 0 /* Exit status for success */
+#define E_ERROR 1 /* Exit status for error */
+#define E_USAGE 2 /* Exit status for usage error */
+
+extern void vwarn(const char *, va_list);
+extern void warn(const char *, ...);
+extern void vdie(const char *, va_list);
+extern void die(const char *, ...);
+
+extern const char *getpname(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _UTILS_H */
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..62d3e55
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,55 @@
+# vim:ts=2:sw=2:et:
+AC_INIT(pctf,1.1,,pctf)
+AM_INIT_AUTOMAKE([foreign])
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_C_INLINE
+AC_C_BIGENDIAN
+AC_PROG_LIBTOOL
+
+AC_CHECK_SIZEOF(void*)
+
+AC_CHECK_TYPE(ulong_t, unsigned long)
+AC_CHECK_TYPE(u_longlong_t, unsigned long long)
+AC_CHECK_TYPE(ushort_t, unsigned short)
+AC_CHECK_TYPE(uint_t, unsigned int)
+AC_CHECK_TYPE(off64_t, off_t)
+AC_CHECK_TYPE(uchar_t, unsigned char)
+
+AC_CHECK_FUNCS([ \
+getexecname \
+getprogname \
+])
+
+AC_CHECK_LIB(z, uncompress)
+AC_CHECK_LIB(elf, gelf_getshdr)
+AC_CHECK_LIB(dwarf, dwarf_elf_init)
+AC_CHECK_LIB(pthread, pthread_create)
+
+AC_CHECK_HEADERS([ \
+ctype.h \
+dwarf.h \
+errno.h \
+fcntl.h \
+gelf.h \
+libdwarf.h \
+libelf.h \
+libgen.h \
+limits.h \
+pthread.h \
+stdint.h \
+string.h \
+strings.h \
+sys/elf.h \
+sys/errno.h \
+sys/mman.h \
+sys/stat.h \
+sys/sysmacros.h \
+unistd.h \
+zlib.h \
+])
+
+AC_CONFIG_HEADERS(pctf_config.h)
+AC_CONFIG_FILES(Makefile)
+AC_OUTPUT
+
diff --git a/cvt/alist.c b/cvt/alist.c
new file mode 100644
index 0000000..db5cc80
--- /dev/null
+++ b/cvt/alist.c
@@ -0,0 +1,214 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Create, manage, and destroy association lists. alists are arrays with
+ * arbitrary index types, and are also commonly known as associative arrays.
+ */
+
+#include "libctf/ctf_impl.h"
+
+#include "alist.h"
+#include "memory.h"
+#include "hash.h"
+
+#define ALIST_HASH_SIZE 997
+
+struct alist {
+ hash_t *al_elements;
+ void (*al_namefree)(void *);
+ void (*al_valfree)(void *);
+};
+
+typedef struct alist_el {
+ void *ale_name;
+ void *ale_value;
+} alist_el_t;
+
+static int
+alist_hash(int nbuckets, void *arg)
+{
+ alist_el_t *el = arg;
+ uintptr_t num = (uintptr_t)el->ale_name;
+
+ return (num % nbuckets);
+}
+
+static int
+alist_cmp(void *arg1, void *arg2)
+{
+ alist_el_t *el1 = arg1;
+ alist_el_t *el2 = arg2;
+ return ((uintptr_t)el1->ale_name != (uintptr_t)el2->ale_name);
+}
+
+alist_t *
+alist_xnew(int nbuckets, void (*namefree)(void *),
+ void (*valfree)(void *), int (*hashfn)(int, void *),
+ int (*cmpfn)(void *, void *))
+{
+ alist_t *alist;
+
+ alist = xcalloc(sizeof (alist_t));
+ alist->al_elements = hash_new(nbuckets, hashfn, cmpfn);
+ alist->al_namefree = namefree;
+ alist->al_valfree = valfree;
+
+ return (alist);
+}
+
+alist_t *
+alist_new(void (*namefree)(void *), void (*valfree)(void *))
+{
+ return (alist_xnew(ALIST_HASH_SIZE, namefree, valfree,
+ alist_hash, alist_cmp));
+}
+
+static void
+alist_free_cb(void *arg1, void *arg2)
+{
+ alist_el_t *el = arg1;
+ alist_t *alist = arg2;
+ if (alist->al_namefree)
+ alist->al_namefree(el->ale_name);
+ if (alist->al_valfree)
+ alist->al_valfree(el->ale_name);
+ free(el);
+}
+
+void
+alist_free(alist_t *alist)
+{
+ hash_free(alist->al_elements, alist_free_cb, alist);
+ free(alist);
+}
+
+void
+alist_add(alist_t *alist, void *name, void *value)
+{
+ alist_el_t *el;
+
+ el = xmalloc(sizeof (alist_el_t));
+ el->ale_name = name;
+ el->ale_value = value;
+ hash_add(alist->al_elements, el);
+}
+
+int
+alist_find(alist_t *alist, void *name, void **value)
+{
+ alist_el_t template, *retx;
+ void *ret;
+
+ template.ale_name = name;
+ if (!hash_find(alist->al_elements, &template, &ret))
+ return (0);
+
+ if (value) {
+ retx = ret;
+ *value = retx->ale_value;
+ }
+
+ return (1);
+}
+
+typedef struct alist_iter_data {
+ int (*aid_func)(void *, void *, void *);
+ void *aid_priv;
+} alist_iter_data_t;
+
+static int
+alist_iter_cb(void *arg1, void *arg2)
+{
+ alist_el_t *el = arg1;
+ alist_iter_data_t *aid = arg2;
+ return (aid->aid_func(el->ale_name, el->ale_value, aid->aid_priv));
+}
+
+int
+alist_iter(alist_t *alist, int (*func)(void *, void *, void *), void *private)
+{
+ alist_iter_data_t aid;
+
+ aid.aid_func = func;
+ aid.aid_priv = private;
+
+ return (hash_iter(alist->al_elements, alist_iter_cb, &aid));
+}
+
+/*
+ * Debugging support. Used to print the contents of an alist.
+ */
+
+void
+alist_stats(alist_t *alist, int verbose)
+{
+ printf("Alist statistics\n");
+ hash_stats(alist->al_elements, verbose);
+}
+
+static int alist_def_print_cb_key_int = 1;
+static int alist_def_print_cb_value_int = 1;
+
+static int
+alist_def_print_cb(void *key, void *value)
+{
+ printf("Key: ");
+ if (alist_def_print_cb_key_int == 1)
+ printf("%5lu ", (ulong_t)key);
+ else
+ printf("%s\n", (char *)key);
+
+ printf("Value: ");
+ if (alist_def_print_cb_value_int == 1)
+ printf("%5lu\n", (ulong_t)value);
+ else
+ printf("%s\n", (char *)key);
+
+ return (1);
+}
+
+static int
+alist_dump_cb(void *node, void *private)
+{
+ int (*printer)(void *, void *) = private;
+ alist_el_t *el = node;
+
+ printer(el->ale_name, el->ale_value);
+
+ return (1);
+}
+
+int
+alist_dump(alist_t *alist, int (*printer)(void *, void *))
+{
+ if (!printer)
+ printer = alist_def_print_cb;
+
+ return (hash_iter(alist->al_elements, alist_dump_cb, (void *)printer));
+}
diff --git a/cvt/alist.h b/cvt/alist.h
new file mode 100644
index 0000000..629e029
--- /dev/null
+++ b/cvt/alist.h
@@ -0,0 +1,57 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _ASSOC_H
+#define _ASSOC_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Create, manage, and destroy association lists. alists are arrays with
+ * arbitrary index types.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct alist alist_t;
+
+alist_t *alist_new(void (*)(void *), void (*)(void *));
+alist_t *alist_xnew(int, void (*)(void *), void (*)(void *),
+ int (*)(int, void *), int (*)(void *, void *));
+void alist_free(alist_t *);
+void alist_add(alist_t *, void *, void *);
+int alist_find(alist_t *, void *, void **);
+int alist_iter(alist_t *, int (*)(void *, void *, void *), void *);
+void alist_stats(alist_t *, int);
+int alist_dump(alist_t *, int (*)(void *, void *));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ASSOC_H */
diff --git a/cvt/barrier.c b/cvt/barrier.c
new file mode 100644
index 0000000..1afce4b
--- /dev/null
+++ b/cvt/barrier.c
@@ -0,0 +1,94 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libctf/ctf_impl.h"
+
+/*
+ * This file implements a barrier, a synchronization primitive designed to allow
+ * threads to wait for each other at given points. Barriers are initialized
+ * with a given number of threads, n, using barrier_init(). When a thread calls
+ * barrier_wait(), that thread blocks until n - 1 other threads reach the
+ * barrier_wait() call using the same barrier_t. When n threads have reached
+ * the barrier, they are all awakened and sent on their way. One of the threads
+ * returns from barrier_wait() with a return code of 1; the remaining threads
+ * get a return code of 0.
+ */
+
+#include <pthread.h>
+#if defined(sun)
+#include <synch.h>
+#endif
+#include <stdio.h>
+
+#include "barrier.h"
+
+void
+barrier_init(barrier_t *bar, int nthreads)
+{
+ pthread_mutex_init(&bar->bar_lock, NULL);
+#if defined(sun)
+ sema_init(&bar->bar_sem, 0, USYNC_THREAD, NULL);
+#else
+ sem_init(&bar->bar_sem, 0, 0);
+#endif
+
+ bar->bar_numin = 0;
+ bar->bar_nthr = nthreads;
+}
+
+int
+barrier_wait(barrier_t *bar)
+{
+ pthread_mutex_lock(&bar->bar_lock);
+
+ if (++bar->bar_numin < bar->bar_nthr) {
+ pthread_mutex_unlock(&bar->bar_lock);
+#if defined(sun)
+ sema_wait(&bar->bar_sem);
+#else
+ sem_wait(&bar->bar_sem);
+#endif
+
+ return (0);
+
+ } else {
+ int i;
+
+ /* reset for next use */
+ bar->bar_numin = 0;
+ for (i = 1; i < bar->bar_nthr; i++)
+#if defined(sun)
+ sema_post(&bar->bar_sem);
+#else
+ sem_post(&bar->bar_sem);
+#endif
+ pthread_mutex_unlock(&bar->bar_lock);
+
+ return (1);
+ }
+}
diff --git a/cvt/barrier.h b/cvt/barrier.h
new file mode 100644
index 0000000..c7e6212
--- /dev/null
+++ b/cvt/barrier.h
@@ -0,0 +1,62 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _BARRIER_H
+#define _BARRIER_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * APIs for the barrier synchronization primitive.
+ */
+
+#if defined(sun)
+#include <synch.h>
+#else
+#include <semaphore.h>
+typedef sem_t sema_t;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct barrier {
+ pthread_mutex_t bar_lock; /* protects bar_numin */
+ int bar_numin; /* current number of waiters */
+
+ sema_t bar_sem; /* where everyone waits */
+ int bar_nthr; /* # of waiters to trigger release */
+} barrier_t;
+
+extern void barrier_init(barrier_t *, int);
+extern int barrier_wait(barrier_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BARRIER_H */
diff --git a/cvt/compare.c b/cvt/compare.c
new file mode 100644
index 0000000..bcc1f3a
--- /dev/null
+++ b/cvt/compare.c
@@ -0,0 +1,93 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This is a test program designed to catch mismerges and mistranslations from
+ * stabs to CTF.
+ *
+ * Given a file with stabs data and a file with CTF data, determine whether
+ * or not all of the data structures and objects described by the stabs data
+ * are present in the CTF data.
+ */
+
+#include "libctf/ctf_impl.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "ctftools.h"
+
+const char *progname;
+int debug_level = DEBUG_LEVEL;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s ctf_file stab_file\n", progname);
+}
+
+int
+main(int argc, char **argv)
+{
+ tdata_t *ctftd, *stabrtd, *stabtd, *difftd;
+ char *ctfname, *stabname;
+ int new;
+
+ progname = argv[0];
+
+ if (argc != 3) {
+ usage();
+ exit(2);
+ }
+
+ ctfname = argv[1];
+ stabname = argv[2];
+
+ stabrtd = tdata_new();
+ stabtd = tdata_new();
+ difftd = tdata_new();
+
+ if (read_stabs(stabrtd, stabname, 0) != 0)
+ merge_into_master(stabrtd, stabtd, NULL, 1);
+ else if (read_ctf(&stabname, 1, NULL, read_ctf_save_cb, &stabtd, 0)
+ == 0)
+ terminate("%s doesn't have stabs or CTF\n", stabname);
+
+ if (read_ctf(&ctfname, 1, NULL, read_ctf_save_cb, &ctftd, 0) == 0)
+ terminate("%s doesn't contain CTF data\n", ctfname);
+
+ merge_into_master(stabtd, ctftd, difftd, 0);
+
+ if ((new = hash_count(difftd->td_iihash)) != 0) {
+ (void) hash_iter(difftd->td_iihash, (int (*)())iidesc_dump,
+ NULL);
+ terminate("%s grew by %d\n", stabname, new);
+ }
+
+ return (0);
+}
diff --git a/cvt/ctf.c b/cvt/ctf.c
new file mode 100644
index 0000000..c81b98e
--- /dev/null
+++ b/cvt/ctf.c
@@ -0,0 +1,1277 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Create and parse buffers containing CTF data.
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "strtab.h"
+#include "memory.h"
+
+/*
+ * Name of the file currently being read, used to print error messages. We
+ * assume that only one file will be read at a time, and thus make no attempt
+ * to allow curfile to be used simultaneously by multiple threads.
+ *
+ * The value is only valid during a call to ctf_load.
+ */
+char *curfile;
+
+#define CTF_BUF_CHUNK_SIZE (64 * 1024)
+#define RES_BUF_CHUNK_SIZE (64 * 1024)
+
+struct ctf_buf {
+ strtab_t ctb_strtab; /* string table */
+ caddr_t ctb_base; /* pointer to base of buffer */
+ caddr_t ctb_end; /* pointer to end of buffer */
+ caddr_t ctb_ptr; /* pointer to empty buffer space */
+ size_t ctb_size; /* size of buffer */
+ int nptent; /* number of processed types */
+ int ntholes; /* number of type holes */
+};
+
+/*PRINTFLIKE1*/
+static void
+parseterminate(const char *fmt, ...)
+{
+ static char msgbuf[1024]; /* sigh */
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof (msgbuf), fmt, ap);
+ va_end(ap);
+
+ terminate("%s: %s\n", curfile, msgbuf);
+}
+
+static void
+ctf_buf_grow(ctf_buf_t *b)
+{
+ off_t ptroff = b->ctb_ptr - b->ctb_base;
+
+ b->ctb_size += CTF_BUF_CHUNK_SIZE;
+ b->ctb_base = xrealloc(b->ctb_base, b->ctb_size);
+ b->ctb_end = b->ctb_base + b->ctb_size;
+ b->ctb_ptr = b->ctb_base + ptroff;
+}
+
+static ctf_buf_t *
+ctf_buf_new(void)
+{
+ ctf_buf_t *b = xcalloc(sizeof (ctf_buf_t));
+
+ strtab_create(&b->ctb_strtab);
+ ctf_buf_grow(b);
+
+ return (b);
+}
+
+static void
+ctf_buf_free(ctf_buf_t *b)
+{
+ strtab_destroy(&b->ctb_strtab);
+ free(b->ctb_base);
+ free(b);
+}
+
+static uint_t
+ctf_buf_cur(ctf_buf_t *b)
+{
+ return (b->ctb_ptr - b->ctb_base);
+}
+
+static void
+ctf_buf_write(ctf_buf_t *b, void const *p, size_t n)
+{
+ size_t len;
+
+ while (n != 0) {
+ if (b->ctb_ptr == b->ctb_end)
+ ctf_buf_grow(b);
+
+ len = MIN((size_t)(b->ctb_end - b->ctb_ptr), n);
+ bcopy(p, b->ctb_ptr, len);
+ b->ctb_ptr += len;
+
+ p = (char const *)p + len;
+ n -= len;
+ }
+}
+
+static int
+write_label(void *arg1, void *arg2)
+{
+ labelent_t *le = arg1;
+ ctf_buf_t *b = arg2;
+ ctf_lblent_t ctl;
+
+ ctl.ctl_label = strtab_insert(&b->ctb_strtab, le->le_name);
+ ctl.ctl_typeidx = le->le_idx;
+
+ ctf_buf_write(b, &ctl, sizeof (ctl));
+
+ return (1);
+}
+
+static void
+write_objects(iidesc_t *idp, ctf_buf_t *b)
+{
+ ushort_t id = (idp ? idp->ii_dtype->t_id : 0);
+
+ ctf_buf_write(b, &id, sizeof (id));
+
+ debug(3, "Wrote object %s (%d)\n", (idp ? idp->ii_name : "(null)"), id);
+}
+
+static void
+write_functions(iidesc_t *idp, ctf_buf_t *b)
+{
+ ushort_t fdata[2];
+ ushort_t id;
+ int nargs;
+ int i;
+
+ if (!idp) {
+ fdata[0] = 0;
+ ctf_buf_write(b, &fdata[0], sizeof (fdata[0]));
+
+ debug(3, "Wrote function (null)\n");
+ return;
+ }
+
+ nargs = idp->ii_nargs + (idp->ii_vargs != 0);
+
+ if (nargs > CTF_MAX_VLEN) {
+ terminate("function %s has too many args: %d > %d\n",
+ idp->ii_name, nargs, CTF_MAX_VLEN);
+ }
+
+ fdata[0] = CTF_TYPE_INFO(CTF_K_FUNCTION, 1, nargs);
+ fdata[1] = idp->ii_dtype->t_id;
+ ctf_buf_write(b, fdata, sizeof (fdata));
+
+ for (i = 0; i < idp->ii_nargs; i++) {
+ id = idp->ii_args[i]->t_id;
+ ctf_buf_write(b, &id, sizeof (id));
+ }
+
+ if (idp->ii_vargs) {
+ id = 0;
+ ctf_buf_write(b, &id, sizeof (id));
+ }
+
+ debug(3, "Wrote function %s (%d args)\n", idp->ii_name, nargs);
+}
+
+/*
+ * Depending on the size of the type being described, either a ctf_stype_t (for
+ * types with size < CTF_LSTRUCT_THRESH) or a ctf_type_t (all others) will be
+ * written. We isolate the determination here so the rest of the writer code
+ * doesn't need to care.
+ */
+static void
+write_sized_type_rec(ctf_buf_t *b, ctf_type_t *ctt, size_t size)
+{
+ if (size > CTF_MAX_SIZE) {
+ ctt->ctt_size = CTF_LSIZE_SENT;
+ ctt->ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size);
+ ctt->ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size);
+ ctf_buf_write(b, ctt, sizeof (*ctt));
+ } else {
+ ctf_stype_t *cts = (ctf_stype_t *)ctt;
+
+ cts->ctt_size = (ushort_t)size;
+ ctf_buf_write(b, cts, sizeof (*cts));
+ }
+}
+
+static void
+write_unsized_type_rec(ctf_buf_t *b, ctf_type_t *ctt)
+{
+ ctf_stype_t *cts = (ctf_stype_t *)ctt;
+
+ ctf_buf_write(b, cts, sizeof (*cts));
+}
+
+static int
+write_type(void *arg1, void *arg2)
+{
+ tdesc_t *tp = arg1;
+ ctf_buf_t *b = arg2;
+ elist_t *ep;
+ mlist_t *mp;
+ intr_t *ip;
+
+ size_t offset;
+ uint_t encoding;
+ uint_t data;
+ int isroot = tp->t_flags & TDESC_F_ISROOT;
+ int i;
+
+ ctf_type_t ctt;
+ ctf_array_t cta;
+ ctf_member_t ctm;
+ ctf_lmember_t ctlm;
+ ctf_enum_t cte;
+ ushort_t id;
+
+ ctlm.ctlm_pad = 0;
+
+ /*
+ * There shouldn't be any holes in the type list (where a hole is
+ * defined as two consecutive tdescs without consecutive ids), but
+ * check for them just in case. If we do find holes, we need to make
+ * fake entries to fill the holes, or we won't be able to reconstruct
+ * the tree from the written data.
+ */
+ if (++b->nptent < CTF_TYPE_TO_INDEX(tp->t_id)) {
+ debug(2, "genctf: type hole from %d < x < %d\n",
+ b->nptent - 1, CTF_TYPE_TO_INDEX(tp->t_id));
+
+ ctt.ctt_name = CTF_TYPE_NAME(CTF_STRTAB_0, 0);
+ ctt.ctt_info = CTF_TYPE_INFO(0, 0, 0);
+ while (b->nptent < CTF_TYPE_TO_INDEX(tp->t_id)) {
+ write_sized_type_rec(b, &ctt, 0);
+ b->nptent++;
+ }
+ }
+
+ offset = strtab_insert(&b->ctb_strtab, tp->t_name);
+ ctt.ctt_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset);
+
+ switch (tp->t_type) {
+ case INTRINSIC:
+ ip = tp->t_intr;
+ if (ip->intr_type == INTR_INT)
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_INTEGER,
+ isroot, 1);
+ else
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FLOAT, isroot, 1);
+ write_sized_type_rec(b, &ctt, tp->t_size);
+
+ encoding = 0;
+
+ if (ip->intr_type == INTR_INT) {
+ if (ip->intr_signed)
+ encoding |= CTF_INT_SIGNED;
+ if (ip->intr_iformat == 'c')
+ encoding |= CTF_INT_CHAR;
+ else if (ip->intr_iformat == 'b')
+ encoding |= CTF_INT_BOOL;
+ else if (ip->intr_iformat == 'v')
+ encoding |= CTF_INT_VARARGS;
+ } else
+ encoding = ip->intr_fformat;
+
+ data = CTF_INT_DATA(encoding, ip->intr_offset, ip->intr_nbits);
+ ctf_buf_write(b, &data, sizeof (data));
+ break;
+
+ case POINTER:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_POINTER, isroot, 0);
+ ctt.ctt_type = tp->t_tdesc->t_id;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case ARRAY:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_ARRAY, isroot, 1);
+ write_sized_type_rec(b, &ctt, tp->t_size);
+
+ cta.cta_contents = tp->t_ardef->ad_contents->t_id;
+ cta.cta_index = tp->t_ardef->ad_idxtype->t_id;
+ cta.cta_nelems = tp->t_ardef->ad_nelems;
+ ctf_buf_write(b, &cta, sizeof (cta));
+ break;
+
+ case STRUCT:
+ case UNION:
+ for (i = 0, mp = tp->t_members; mp != NULL; mp = mp->ml_next)
+ i++; /* count up struct or union members */
+
+ if (i > CTF_MAX_VLEN) {
+ terminate("sou %s has too many members: %d > %d\n",
+ tdesc_name(tp), i, CTF_MAX_VLEN);
+ }
+
+ if (tp->t_type == STRUCT)
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, isroot, i);
+ else
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, isroot, i);
+
+ write_sized_type_rec(b, &ctt, tp->t_size);
+
+ if (tp->t_size < CTF_LSTRUCT_THRESH) {
+ for (mp = tp->t_members; mp != NULL; mp = mp->ml_next) {
+ offset = strtab_insert(&b->ctb_strtab,
+ mp->ml_name);
+
+ ctm.ctm_name = CTF_TYPE_NAME(CTF_STRTAB_0,
+ offset);
+ ctm.ctm_type = mp->ml_type->t_id;
+ ctm.ctm_offset = mp->ml_offset;
+ ctf_buf_write(b, &ctm, sizeof (ctm));
+ }
+ } else {
+ for (mp = tp->t_members; mp != NULL; mp = mp->ml_next) {
+ offset = strtab_insert(&b->ctb_strtab,
+ mp->ml_name);
+
+ ctlm.ctlm_name = CTF_TYPE_NAME(CTF_STRTAB_0,
+ offset);
+ ctlm.ctlm_type = mp->ml_type->t_id;
+ ctlm.ctlm_offsethi =
+ CTF_OFFSET_TO_LMEMHI(mp->ml_offset);
+ ctlm.ctlm_offsetlo =
+ CTF_OFFSET_TO_LMEMLO(mp->ml_offset);
+ ctf_buf_write(b, &ctlm, sizeof (ctlm));
+ }
+ }
+ break;
+
+ case ENUM:
+ for (i = 0, ep = tp->t_emem; ep != NULL; ep = ep->el_next)
+ i++; /* count up enum members */
+
+ if (i > CTF_MAX_VLEN) {
+ warning("enum %s has too many values: %d > %d\n",
+ tdesc_name(tp), i, CTF_MAX_VLEN);
+ i = CTF_MAX_VLEN;
+ }
+
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, isroot, i);
+ write_sized_type_rec(b, &ctt, tp->t_size);
+
+ for (ep = tp->t_emem; ep != NULL && i > 0; ep = ep->el_next) {
+ offset = strtab_insert(&b->ctb_strtab, ep->el_name);
+ cte.cte_name = CTF_TYPE_NAME(CTF_STRTAB_0, offset);
+ cte.cte_value = ep->el_number;
+ ctf_buf_write(b, &cte, sizeof (cte));
+ i--;
+ }
+ break;
+
+ case FORWARD:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FORWARD, isroot, 0);
+ ctt.ctt_type = 0;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case TYPEDEF:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_TYPEDEF, isroot, 0);
+ ctt.ctt_type = tp->t_tdesc->t_id;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case VOLATILE:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_VOLATILE, isroot, 0);
+ ctt.ctt_type = tp->t_tdesc->t_id;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case CONST:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_CONST, isroot, 0);
+ ctt.ctt_type = tp->t_tdesc->t_id;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ case FUNCTION:
+ i = tp->t_fndef->fn_nargs + tp->t_fndef->fn_vargs;
+
+ if (i > CTF_MAX_VLEN) {
+ terminate("function %s has too many args: %d > %d\n",
+ i, CTF_MAX_VLEN);
+ }
+
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_FUNCTION, isroot, i);
+ ctt.ctt_type = tp->t_fndef->fn_ret->t_id;
+ write_unsized_type_rec(b, &ctt);
+
+ for (i = 0; i < (int) tp->t_fndef->fn_nargs; i++) {
+ id = tp->t_fndef->fn_args[i]->t_id;
+ ctf_buf_write(b, &id, sizeof (id));
+ }
+
+ if (tp->t_fndef->fn_vargs) {
+ id = 0;
+ ctf_buf_write(b, &id, sizeof (id));
+ i++;
+ }
+
+ if (i & 1) {
+ id = 0;
+ ctf_buf_write(b, &id, sizeof (id));
+ }
+ break;
+
+ case RESTRICT:
+ ctt.ctt_info = CTF_TYPE_INFO(CTF_K_RESTRICT, isroot, 0);
+ ctt.ctt_type = tp->t_tdesc->t_id;
+ write_unsized_type_rec(b, &ctt);
+ break;
+
+ default:
+ warning("Can't write unknown type %d\n", tp->t_type);
+ }
+
+ debug(3, "Wrote type %d %s\n", tp->t_id, tdesc_name(tp));
+
+ return (1);
+}
+
+typedef struct resbuf {
+ caddr_t rb_base;
+ caddr_t rb_ptr;
+ size_t rb_size;
+ z_stream rb_zstr;
+} resbuf_t;
+
+static void
+rbzs_grow(resbuf_t *rb)
+{
+ off_t ptroff = (caddr_t)rb->rb_zstr.next_out - rb->rb_base;
+
+ rb->rb_size += RES_BUF_CHUNK_SIZE;
+ rb->rb_base = xrealloc(rb->rb_base, rb->rb_size);
+ rb->rb_ptr = rb->rb_base + ptroff;
+ rb->rb_zstr.next_out = (Bytef *)(rb->rb_ptr);
+ rb->rb_zstr.avail_out += RES_BUF_CHUNK_SIZE;
+}
+
+static void
+compress_start(resbuf_t *rb)
+{
+ int rc;
+
+ rb->rb_zstr.zalloc = (alloc_func)0;
+ rb->rb_zstr.zfree = (free_func)0;
+ rb->rb_zstr.opaque = (voidpf)0;
+
+ if ((rc = deflateInit(&rb->rb_zstr, Z_BEST_COMPRESSION)) != Z_OK)
+ parseterminate("zlib start failed: %s", zError(rc));
+}
+
+static ssize_t
+compress_buffer(void *buf, size_t n, void *data)
+{
+ resbuf_t *rb = (resbuf_t *)data;
+ int rc;
+
+ rb->rb_zstr.next_out = (Bytef *)rb->rb_ptr;
+ rb->rb_zstr.avail_out = rb->rb_size - (rb->rb_ptr - rb->rb_base);
+ rb->rb_zstr.next_in = buf;
+ rb->rb_zstr.avail_in = n;
+
+ while (rb->rb_zstr.avail_in) {
+ if (rb->rb_zstr.avail_out == 0)
+ rbzs_grow(rb);
+
+ if ((rc = deflate(&rb->rb_zstr, Z_NO_FLUSH)) != Z_OK)
+ parseterminate("zlib deflate failed: %s", zError(rc));
+ }
+ rb->rb_ptr = (caddr_t)rb->rb_zstr.next_out;
+
+ return (n);
+}
+
+static void
+compress_flush(resbuf_t *rb, int type)
+{
+ int rc;
+
+ for (;;) {
+ if (rb->rb_zstr.avail_out == 0)
+ rbzs_grow(rb);
+
+ rc = deflate(&rb->rb_zstr, type);
+ if ((type == Z_FULL_FLUSH && rc == Z_BUF_ERROR) ||
+ (type == Z_FINISH && rc == Z_STREAM_END))
+ break;
+ else if (rc != Z_OK)
+ parseterminate("zlib finish failed: %s", zError(rc));
+ }
+ rb->rb_ptr = (caddr_t)rb->rb_zstr.next_out;
+}
+
+static void
+compress_end(resbuf_t *rb)
+{
+ int rc;
+
+ compress_flush(rb, Z_FINISH);
+
+ if ((rc = deflateEnd(&rb->rb_zstr)) != Z_OK)
+ parseterminate("zlib end failed: %s", zError(rc));
+}
+
+/*
+ * Pad the buffer to a power-of-2 boundary
+ */
+static void
+pad_buffer(ctf_buf_t *buf, int align)
+{
+ uint_t cur = ctf_buf_cur(buf);
+ ssize_t topad = (align - (cur % align)) % align;
+ static const char pad[8] = { 0 };
+
+ while (topad > 0) {
+ ctf_buf_write(buf, pad, (topad > 8 ? 8 : topad));
+ topad -= 8;
+ }
+}
+
+static ssize_t
+bcopy_data(void *buf, size_t n, void *data)
+{
+ caddr_t *posp = (caddr_t *)data;
+ bcopy(buf, *posp, n);
+ *posp += n;
+ return (n);
+}
+
+static caddr_t
+write_buffer(ctf_header_t *h, ctf_buf_t *buf, size_t *resszp)
+{
+ caddr_t outbuf;
+ caddr_t bufpos;
+
+ outbuf = xmalloc(sizeof (ctf_header_t) + (buf->ctb_ptr - buf->ctb_base)
+ + buf->ctb_strtab.str_size);
+
+ bufpos = outbuf;
+ (void) bcopy_data(h, sizeof (ctf_header_t), &bufpos);
+ (void) bcopy_data(buf->ctb_base, buf->ctb_ptr - buf->ctb_base,
+ &bufpos);
+ (void) strtab_write(&buf->ctb_strtab, bcopy_data, &bufpos);
+ *resszp = bufpos - outbuf;
+ return (outbuf);
+}
+
+/*
+ * Create the compression buffer, and fill it with the CTF and string
+ * table data. We flush the compression state between the two so the
+ * dictionary used for the string tables won't be polluted with values
+ * that made sense for the CTF data.
+ */
+static caddr_t
+write_compressed_buffer(ctf_header_t *h, ctf_buf_t *buf, size_t *resszp)
+{
+ resbuf_t resbuf;
+ resbuf.rb_size = RES_BUF_CHUNK_SIZE;
+ resbuf.rb_base = xmalloc(resbuf.rb_size);
+ bcopy(h, resbuf.rb_base, sizeof (ctf_header_t));
+ resbuf.rb_ptr = resbuf.rb_base + sizeof (ctf_header_t);
+
+ compress_start(&resbuf);
+ (void) compress_buffer(buf->ctb_base, buf->ctb_ptr - buf->ctb_base,
+ &resbuf);
+ compress_flush(&resbuf, Z_FULL_FLUSH);
+ (void) strtab_write(&buf->ctb_strtab, compress_buffer, &resbuf);
+ compress_end(&resbuf);
+
+ *resszp = (resbuf.rb_ptr - resbuf.rb_base);
+ return (resbuf.rb_base);
+}
+
+caddr_t
+ctf_gen(iiburst_t *iiburst, size_t *resszp, int do_compress)
+{
+ ctf_buf_t *buf = ctf_buf_new();
+ ctf_header_t h;
+ caddr_t outbuf;
+
+ int i;
+
+ /*
+ * Prepare the header, and create the CTF output buffers. The data
+ * object section and function section are both lists of 2-byte
+ * integers; we pad these out to the next 4-byte boundary if needed.
+ */
+ h.cth_magic = CTF_MAGIC;
+ h.cth_version = CTF_VERSION;
+ h.cth_flags = do_compress ? CTF_F_COMPRESS : 0;
+ h.cth_parlabel = strtab_insert(&buf->ctb_strtab,
+ iiburst->iib_td->td_parlabel);
+ h.cth_parname = strtab_insert(&buf->ctb_strtab,
+ iiburst->iib_td->td_parname);
+
+ h.cth_lbloff = 0;
+ (void) list_iter(iiburst->iib_td->td_labels, write_label,
+ buf);
+
+ pad_buffer(buf, 2);
+ h.cth_objtoff = ctf_buf_cur(buf);
+ for (i = 0; i < iiburst->iib_nobjts; i++)
+ write_objects(iiburst->iib_objts[i], buf);
+
+ pad_buffer(buf, 2);
+ h.cth_funcoff = ctf_buf_cur(buf);
+ for (i = 0; i < iiburst->iib_nfuncs; i++)
+ write_functions(iiburst->iib_funcs[i], buf);
+
+ pad_buffer(buf, 4);
+ h.cth_typeoff = ctf_buf_cur(buf);
+ (void) list_iter(iiburst->iib_types, write_type, buf);
+
+ debug(2, "CTF wrote %d types\n", list_count(iiburst->iib_types));
+
+ h.cth_stroff = ctf_buf_cur(buf);
+ h.cth_strlen = strtab_size(&buf->ctb_strtab);
+
+ /*
+ * We only do compression for ctfmerge, as ctfconvert is only
+ * supposed to be used on intermediary build objects. This is
+ * significantly faster.
+ */
+ if (do_compress)
+ outbuf = write_compressed_buffer(&h, buf, resszp);
+ else
+ outbuf = write_buffer(&h, buf, resszp);
+
+ ctf_buf_free(buf);
+ return (outbuf);
+}
+
+static void
+get_ctt_size(ctf_type_t *ctt, size_t *sizep, size_t *incrementp)
+{
+ if (ctt->ctt_size == CTF_LSIZE_SENT) {
+ *sizep = (size_t)CTF_TYPE_LSIZE(ctt);
+ *incrementp = sizeof (ctf_type_t);
+ } else {
+ *sizep = ctt->ctt_size;
+ *incrementp = sizeof (ctf_stype_t);
+ }
+}
+
+static int
+count_types(ctf_header_t *h, caddr_t data)
+{
+ caddr_t dptr = data + h->cth_typeoff;
+ int count = 0;
+
+ dptr = data + h->cth_typeoff;
+ while (dptr < data + h->cth_stroff) {
+ void *v = (void *) dptr;
+ ctf_type_t *ctt = v;
+ size_t vlen = CTF_INFO_VLEN(ctt->ctt_info);
+ size_t size, increment;
+
+ get_ctt_size(ctt, &size, &increment);
+
+ switch (CTF_INFO_KIND(ctt->ctt_info)) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ dptr += 4;
+ break;
+ case CTF_K_POINTER:
+ case CTF_K_FORWARD:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ case CTF_K_FUNCTION:
+ dptr += sizeof (ushort_t) * (vlen + (vlen & 1));
+ break;
+ case CTF_K_ARRAY:
+ dptr += sizeof (ctf_array_t);
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (size < CTF_LSTRUCT_THRESH)
+ dptr += sizeof (ctf_member_t) * vlen;
+ else
+ dptr += sizeof (ctf_lmember_t) * vlen;
+ break;
+ case CTF_K_ENUM:
+ dptr += sizeof (ctf_enum_t) * vlen;
+ break;
+ case CTF_K_UNKNOWN:
+ break;
+ default:
+ parseterminate("Unknown CTF type %d (#%d) at %#x",
+ CTF_INFO_KIND(ctt->ctt_info), count, dptr - data);
+ }
+
+ dptr += increment;
+ count++;
+ }
+
+ debug(3, "CTF read %d types\n", count);
+
+ return (count);
+}
+
+/*
+ * Resurrect the labels stored in the CTF data, returning the index associated
+ * with a label provided by the caller. There are several cases, outlined
+ * below. Note that, given two labels, the one associated with the lesser type
+ * index is considered to be older than the other.
+ *
+ * 1. matchlbl == NULL - return the index of the most recent label.
+ * 2. matchlbl == "BASE" - return the index of the oldest label.
+ * 3. matchlbl != NULL, but doesn't match any labels in the section - warn
+ * the user, and proceed as if matchlbl == "BASE" (for safety).
+ * 4. matchlbl != NULL, and matches one of the labels in the section - return
+ * the type index associated with the label.
+ */
+static int
+resurrect_labels(ctf_header_t *h, tdata_t *td, caddr_t ctfdata, char *matchlbl)
+{
+ caddr_t buf = ctfdata + h->cth_lbloff;
+ caddr_t sbuf = ctfdata + h->cth_stroff;
+ size_t bufsz = h->cth_objtoff - h->cth_lbloff;
+ int lastidx = 0, baseidx = -1;
+ char *baselabel = NULL;
+ ctf_lblent_t *ctl;
+ void *v = (void *) buf;
+
+ for (ctl = v; (caddr_t)ctl < buf + bufsz; ctl++) {
+ char *label = sbuf + ctl->ctl_label;
+
+ lastidx = ctl->ctl_typeidx;
+
+ debug(3, "Resurrected label %s type idx %d\n", label, lastidx);
+
+ tdata_label_add(td, label, lastidx);
+
+ if (baseidx == -1) {
+ baseidx = lastidx;
+ baselabel = label;
+ if (matchlbl != NULL && streq(matchlbl, "BASE"))
+ return (lastidx);
+ }
+
+ if (matchlbl != NULL && streq(label, matchlbl))
+ return (lastidx);
+ }
+
+ if (matchlbl != NULL) {
+ /* User provided a label that didn't match */
+ warning("%s: Cannot find label `%s' - using base (%s)\n",
+ curfile, matchlbl, (baselabel ? baselabel : "NONE"));
+
+ tdata_label_free(td);
+ tdata_label_add(td, baselabel, baseidx);
+
+ return (baseidx);
+ }
+
+ return (lastidx);
+}
+
+static void
+resurrect_objects(ctf_header_t *h, tdata_t *td, tdesc_t **tdarr, int tdsize,
+ caddr_t ctfdata, symit_data_t *si)
+{
+ caddr_t buf = ctfdata + h->cth_objtoff;
+ size_t bufsz = h->cth_funcoff - h->cth_objtoff;
+ caddr_t dptr;
+
+ symit_reset(si);
+ for (dptr = buf; dptr < buf + bufsz; dptr += 2) {
+ void *v = (void *) dptr;
+ ushort_t id = *((ushort_t *)v);
+ iidesc_t *ii;
+ GElf_Sym *sym;
+
+ if (!(sym = symit_next(si, STT_OBJECT)) && id != 0) {
+ parseterminate(
+ "Unexpected end of object symbols at %x of %x",
+ dptr - buf, bufsz);
+ }
+
+ if (id == 0) {
+ debug(3, "Skipping null object\n");
+ continue;
+ } else if (id >= tdsize) {
+ parseterminate("Reference to invalid type %d", id);
+ }
+
+ ii = iidesc_new(symit_name(si));
+ ii->ii_dtype = tdarr[id];
+ if (GELF_ST_BIND(sym->st_info) == STB_LOCAL) {
+ ii->ii_type = II_SVAR;
+ ii->ii_owner = xstrdup(symit_curfile(si));
+ } else
+ ii->ii_type = II_GVAR;
+ hash_add(td->td_iihash, ii);
+
+ debug(3, "Resurrected %s object %s (%d) from %s\n",
+ (ii->ii_type == II_GVAR ? "global" : "static"),
+ ii->ii_name, id, (ii->ii_owner ? ii->ii_owner : "(none)"));
+ }
+}
+
+static void
+resurrect_functions(ctf_header_t *h, tdata_t *td, tdesc_t **tdarr, int tdsize,
+ caddr_t ctfdata, symit_data_t *si)
+{
+ caddr_t buf = ctfdata + h->cth_funcoff;
+ size_t bufsz = h->cth_typeoff - h->cth_funcoff;
+ caddr_t dptr = buf;
+ iidesc_t *ii;
+ ushort_t info;
+ ushort_t retid;
+ GElf_Sym *sym;
+ int i;
+
+ symit_reset(si);
+ while (dptr < buf + bufsz) {
+ void *v = (void *) dptr;
+ info = *((ushort_t *)v);
+ dptr += 2;
+
+ if (!(sym = symit_next(si, STT_FUNC)) && info != 0)
+ parseterminate("Unexpected end of function symbols");
+
+ if (info == 0) {
+ debug(3, "Skipping null function (%s)\n",
+ symit_name(si));
+ continue;
+ }
+
+ v = (void *) dptr;
+ retid = *((ushort_t *)v);
+ dptr += 2;
+
+ if (retid >= tdsize)
+ parseterminate("Reference to invalid type %d", retid);
+
+ ii = iidesc_new(symit_name(si));
+ ii->ii_dtype = tdarr[retid];
+ if (GELF_ST_BIND(sym->st_info) == STB_LOCAL) {
+ ii->ii_type = II_SFUN;
+ ii->ii_owner = xstrdup(symit_curfile(si));
+ } else
+ ii->ii_type = II_GFUN;
+ ii->ii_nargs = CTF_INFO_VLEN(info);
+ if (ii->ii_nargs)
+ ii->ii_args =
+ xmalloc(sizeof (tdesc_t *) * ii->ii_nargs);
+
+ for (i = 0; i < ii->ii_nargs; i++, dptr += 2) {
+ v = (void *) dptr;
+ ushort_t id = *((ushort_t *)v);
+ if (id >= tdsize)
+ parseterminate("Reference to invalid type %d",
+ id);
+ ii->ii_args[i] = tdarr[id];
+ }
+
+ if (ii->ii_nargs && ii->ii_args[ii->ii_nargs - 1] == NULL) {
+ ii->ii_nargs--;
+ ii->ii_vargs = 1;
+ }
+
+ hash_add(td->td_iihash, ii);
+
+ debug(3, "Resurrected %s function %s (%d, %d args)\n",
+ (ii->ii_type == II_GFUN ? "global" : "static"),
+ ii->ii_name, retid, ii->ii_nargs);
+ }
+}
+
+static void
+resurrect_types(ctf_header_t *h, tdata_t *td, tdesc_t **tdarr, int tdsize,
+ caddr_t ctfdata, int maxid)
+{
+ caddr_t buf = ctfdata + h->cth_typeoff;
+ size_t bufsz = h->cth_stroff - h->cth_typeoff;
+ caddr_t sbuf = ctfdata + h->cth_stroff;
+ caddr_t dptr = buf;
+ tdesc_t *tdp;
+ uint_t data;
+ uint_t encoding;
+ size_t size, increment;
+ int tcnt;
+ int iicnt = 0;
+ tid_t tid, argid;
+ int kind, vlen;
+ int i;
+
+ elist_t **epp;
+ mlist_t **mpp;
+ intr_t *ip;
+
+ ctf_type_t *ctt;
+ ctf_array_t *cta;
+ ctf_enum_t *cte;
+
+ /*
+ * A maxid of zero indicates a request to resurrect all types, so reset
+ * maxid to the maximum type id.
+ */
+ if (maxid == 0)
+ maxid = CTF_MAX_TYPE;
+
+ for (dptr = buf, tcnt = 0, tid = 1; dptr < buf + bufsz; tcnt++, tid++) {
+ if (tid > maxid)
+ break;
+
+ if (tid >= tdsize)
+ parseterminate("Reference to invalid type %d", tid);
+
+ void *v = (void *) dptr;
+ ctt = v;
+
+ get_ctt_size(ctt, &size, &increment);
+ dptr += increment;
+
+ tdp = tdarr[tid];
+
+ if (CTF_NAME_STID(ctt->ctt_name) != CTF_STRTAB_0)
+ parseterminate(
+ "Unable to cope with non-zero strtab id");
+ if (CTF_NAME_OFFSET(ctt->ctt_name) != 0) {
+ tdp->t_name =
+ xstrdup(sbuf + CTF_NAME_OFFSET(ctt->ctt_name));
+ } else
+ tdp->t_name = NULL;
+
+ kind = CTF_INFO_KIND(ctt->ctt_info);
+ vlen = CTF_INFO_VLEN(ctt->ctt_info);
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ tdp->t_type = INTRINSIC;
+ tdp->t_size = size;
+
+ v = (void *) dptr;
+ data = *((uint_t *)v);
+ dptr += sizeof (uint_t);
+ encoding = CTF_INT_ENCODING(data);
+
+ ip = xmalloc(sizeof (intr_t));
+ ip->intr_type = INTR_INT;
+ ip->intr_signed = (encoding & CTF_INT_SIGNED) ? 1 : 0;
+
+ if (encoding & CTF_INT_CHAR)
+ ip->intr_iformat = 'c';
+ else if (encoding & CTF_INT_BOOL)
+ ip->intr_iformat = 'b';
+ else if (encoding & CTF_INT_VARARGS)
+ ip->intr_iformat = 'v';
+ else
+ ip->intr_iformat = '\0';
+
+ ip->intr_offset = CTF_INT_OFFSET(data);
+ ip->intr_nbits = CTF_INT_BITS(data);
+ tdp->t_intr = ip;
+ break;
+
+ case CTF_K_FLOAT:
+ tdp->t_type = INTRINSIC;
+ tdp->t_size = size;
+
+ v = (void *) dptr;
+ data = *((uint_t *)v);
+ dptr += sizeof (uint_t);
+
+ ip = xcalloc(sizeof (intr_t));
+ ip->intr_type = INTR_REAL;
+ ip->intr_fformat = CTF_FP_ENCODING(data);
+ ip->intr_offset = CTF_FP_OFFSET(data);
+ ip->intr_nbits = CTF_FP_BITS(data);
+ tdp->t_intr = ip;
+ break;
+
+ case CTF_K_POINTER:
+ tdp->t_type = POINTER;
+ tdp->t_tdesc = tdarr[ctt->ctt_type];
+ break;
+
+ case CTF_K_ARRAY:
+ tdp->t_type = ARRAY;
+ tdp->t_size = size;
+
+ v = (void *) dptr;
+ cta = v;
+ dptr += sizeof (ctf_array_t);
+
+ tdp->t_ardef = xmalloc(sizeof (ardef_t));
+ tdp->t_ardef->ad_contents = tdarr[cta->cta_contents];
+ tdp->t_ardef->ad_idxtype = tdarr[cta->cta_index];
+ tdp->t_ardef->ad_nelems = cta->cta_nelems;
+ break;
+
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ tdp->t_type = (kind == CTF_K_STRUCT ? STRUCT : UNION);
+ tdp->t_size = size;
+
+ if (size < CTF_LSTRUCT_THRESH) {
+ for (i = 0, mpp = &tdp->t_members; i < vlen;
+ i++, mpp = &((*mpp)->ml_next)) {
+ v = (void *) dptr;
+ ctf_member_t *ctm = v;
+ dptr += sizeof (ctf_member_t);
+
+ *mpp = xmalloc(sizeof (mlist_t));
+ (*mpp)->ml_name = xstrdup(sbuf +
+ ctm->ctm_name);
+ (*mpp)->ml_type = tdarr[ctm->ctm_type];
+ (*mpp)->ml_offset = ctm->ctm_offset;
+ (*mpp)->ml_size = 0;
+ }
+ } else {
+ for (i = 0, mpp = &tdp->t_members; i < vlen;
+ i++, mpp = &((*mpp)->ml_next)) {
+ v = (void *) dptr;
+ ctf_lmember_t *ctlm = v;
+ dptr += sizeof (ctf_lmember_t);
+
+ *mpp = xmalloc(sizeof (mlist_t));
+ (*mpp)->ml_name = xstrdup(sbuf +
+ ctlm->ctlm_name);
+ (*mpp)->ml_type =
+ tdarr[ctlm->ctlm_type];
+ (*mpp)->ml_offset =
+ (int)CTF_LMEM_OFFSET(ctlm);
+ (*mpp)->ml_size = 0;
+ }
+ }
+
+ *mpp = NULL;
+ break;
+
+ case CTF_K_ENUM:
+ tdp->t_type = ENUM;
+ tdp->t_size = size;
+
+ for (i = 0, epp = &tdp->t_emem; i < vlen;
+ i++, epp = &((*epp)->el_next)) {
+ v = (void *) dptr;
+ cte = v;
+ dptr += sizeof (ctf_enum_t);
+
+ *epp = xmalloc(sizeof (elist_t));
+ (*epp)->el_name = xstrdup(sbuf + cte->cte_name);
+ (*epp)->el_number = cte->cte_value;
+ }
+ *epp = NULL;
+ break;
+
+ case CTF_K_FORWARD:
+ tdp->t_type = FORWARD;
+ list_add(&td->td_fwdlist, tdp);
+ break;
+
+ case CTF_K_TYPEDEF:
+ tdp->t_type = TYPEDEF;
+ tdp->t_tdesc = tdarr[ctt->ctt_type];
+ break;
+
+ case CTF_K_VOLATILE:
+ tdp->t_type = VOLATILE;
+ tdp->t_tdesc = tdarr[ctt->ctt_type];
+ break;
+
+ case CTF_K_CONST:
+ tdp->t_type = CONST;
+ tdp->t_tdesc = tdarr[ctt->ctt_type];
+ break;
+
+ case CTF_K_FUNCTION:
+ tdp->t_type = FUNCTION;
+ tdp->t_fndef = xcalloc(sizeof (fndef_t));
+ tdp->t_fndef->fn_ret = tdarr[ctt->ctt_type];
+
+ v = (void *) (dptr + (sizeof (ushort_t) * (vlen - 1)));
+ if (vlen > 0 && *(ushort_t *)v == 0)
+ tdp->t_fndef->fn_vargs = 1;
+
+ tdp->t_fndef->fn_nargs = vlen - tdp->t_fndef->fn_vargs;
+ tdp->t_fndef->fn_args = xcalloc(sizeof (tdesc_t) *
+ vlen - tdp->t_fndef->fn_vargs);
+
+ for (i = 0; i < vlen; i++) {
+ v = (void *) dptr;
+ argid = *(ushort_t *)v;
+ dptr += sizeof (ushort_t);
+
+ if (argid != 0)
+ tdp->t_fndef->fn_args[i] = tdarr[argid];
+ }
+
+ if (vlen & 1)
+ dptr += sizeof (ushort_t);
+ break;
+
+ case CTF_K_RESTRICT:
+ tdp->t_type = RESTRICT;
+ tdp->t_tdesc = tdarr[ctt->ctt_type];
+ break;
+
+ case CTF_K_UNKNOWN:
+ break;
+
+ default:
+ warning("Can't parse unknown CTF type %d\n", kind);
+ }
+
+ if (CTF_INFO_ISROOT(ctt->ctt_info)) {
+ iidesc_t *ii = iidesc_new(tdp->t_name);
+ if (tdp->t_type == STRUCT || tdp->t_type == UNION ||
+ tdp->t_type == ENUM)
+ ii->ii_type = II_SOU;
+ else
+ ii->ii_type = II_TYPE;
+ ii->ii_dtype = tdp;
+ hash_add(td->td_iihash, ii);
+
+ iicnt++;
+ }
+
+ debug(3, "Resurrected %d %stype %s (%d)\n", tdp->t_type,
+ (CTF_INFO_ISROOT(ctt->ctt_info) ? "root " : ""),
+ tdesc_name(tdp), tdp->t_id);
+ }
+
+ debug(3, "Resurrected %d types (%d were roots)\n", tcnt, iicnt);
+}
+
+/*
+ * For lack of other inspiration, we're going to take the boring route. We
+ * count the number of types. This lets us malloc that many tdesc structs
+ * before we start filling them in. This has the advantage of allowing us to
+ * avoid a merge-esque remap step.
+ */
+static tdata_t *
+ctf_parse(ctf_header_t *h, caddr_t buf, symit_data_t *si, char *label)
+{
+ tdata_t *td = tdata_new();
+ tdesc_t **tdarr;
+ int ntypes = count_types(h, buf);
+ int idx, i;
+
+ /* shudder */
+ tdarr = xcalloc(sizeof (tdesc_t *) * (ntypes + 1));
+ tdarr[0] = NULL;
+ for (i = 1; i <= ntypes; i++) {
+ tdarr[i] = xcalloc(sizeof (tdesc_t));
+ tdarr[i]->t_id = i;
+ }
+
+ td->td_parlabel = xstrdup(buf + h->cth_stroff + h->cth_parlabel);
+
+ /* we have the technology - we can rebuild them */
+ idx = resurrect_labels(h, td, buf, label);
+
+ resurrect_objects(h, td, tdarr, ntypes + 1, buf, si);
+ resurrect_functions(h, td, tdarr, ntypes + 1, buf, si);
+ resurrect_types(h, td, tdarr, ntypes + 1, buf, idx);
+
+ free(tdarr);
+
+ td->td_nextid = ntypes + 1;
+
+ return (td);
+}
+
+static size_t
+decompress_ctf(caddr_t cbuf, size_t cbufsz, caddr_t dbuf, size_t dbufsz)
+{
+ z_stream zstr;
+ int rc;
+
+ zstr.zalloc = (alloc_func)0;
+ zstr.zfree = (free_func)0;
+ zstr.opaque = (voidpf)0;
+
+ zstr.next_in = (Bytef *)cbuf;
+ zstr.avail_in = cbufsz;
+ zstr.next_out = (Bytef *)dbuf;
+ zstr.avail_out = dbufsz;
+
+ if ((rc = inflateInit(&zstr)) != Z_OK ||
+ (rc = inflate(&zstr, Z_NO_FLUSH)) != Z_STREAM_END ||
+ (rc = inflateEnd(&zstr)) != Z_OK) {
+ warning("CTF decompress zlib error %s\n", zError(rc));
+ return (0);
+ }
+
+ debug(3, "reflated %lu bytes to %lu, pointer at %d\n",
+ zstr.total_in, zstr.total_out, (caddr_t)zstr.next_in - cbuf);
+
+ return (zstr.total_out);
+}
+
+/*
+ * Reconstruct the type tree from a given buffer of CTF data. Only the types
+ * up to the type associated with the provided label, inclusive, will be
+ * reconstructed. If a NULL label is provided, all types will be reconstructed.
+ *
+ * This function won't work on files that have been uniquified.
+ */
+tdata_t *
+ctf_load(char *file, caddr_t buf, size_t bufsz, symit_data_t *si, char *label)
+{
+ ctf_header_t *h;
+ caddr_t ctfdata;
+ size_t ctfdatasz;
+ tdata_t *td;
+
+ curfile = file;
+
+ if (bufsz < sizeof (ctf_header_t))
+ parseterminate("Corrupt CTF - short header");
+
+ void *v = (void *) buf;
+ h = v;
+ buf += sizeof (ctf_header_t);
+ bufsz -= sizeof (ctf_header_t);
+
+ if (h->cth_magic != CTF_MAGIC)
+ parseterminate("Corrupt CTF - bad magic 0x%x", h->cth_magic);
+
+ if (h->cth_version != CTF_VERSION)
+ parseterminate("Unknown CTF version %d", h->cth_version);
+
+ ctfdatasz = h->cth_stroff + h->cth_strlen;
+ if (h->cth_flags & CTF_F_COMPRESS) {
+ size_t actual;
+
+ ctfdata = xmalloc(ctfdatasz);
+ if ((actual = decompress_ctf(buf, bufsz, ctfdata, ctfdatasz)) !=
+ ctfdatasz) {
+ parseterminate("Corrupt CTF - short decompression "
+ "(was %d, expecting %d)", actual, ctfdatasz);
+ }
+ } else {
+ ctfdata = buf;
+ ctfdatasz = bufsz;
+ }
+
+ td = ctf_parse(h, ctfdata, si, label);
+
+ if (h->cth_flags & CTF_F_COMPRESS)
+ free(ctfdata);
+
+ curfile = NULL;
+
+ return (td);
+}
diff --git a/cvt/ctfconvert.c b/cvt/ctfconvert.c
new file mode 100644
index 0000000..4f0b785
--- /dev/null
+++ b/cvt/ctfconvert.c
@@ -0,0 +1,254 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Given a file containing sections with stabs data, convert the stabs data to
+ * CTF data, and replace the stabs sections with a CTF section.
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "memory.h"
+
+const char *progname;
+int debug_level = DEBUG_LEVEL;
+
+static char *infile = NULL;
+static const char *outfile = NULL;
+static int dynsym;
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "Usage: %s [-gis] -l label | -L labelenv [-o outfile] object_file\n"
+ "\n"
+ " Note: if -L labelenv is specified and labelenv is not set in\n"
+ " the environment, a default value is used.\n",
+ progname);
+}
+
+static void
+terminate_cleanup(void)
+{
+#if !defined(__FreeBSD__)
+ if (!outfile) {
+ fprintf(stderr, "Removing %s\n", infile);
+ unlink(infile);
+ }
+#endif
+}
+
+static void
+handle_sig(int sig)
+{
+ terminate("Caught signal %d - exiting\n", sig);
+}
+
+static int
+file_read(tdata_t *td, char *filename, int ignore_non_c)
+{
+ typedef int (*reader_f)(tdata_t *, Elf *, char *);
+ static reader_f readers[] = {
+ stabs_read,
+ dw_read,
+ NULL
+ };
+
+ source_types_t source_types;
+ Elf *elf;
+ int i, rc, fd;
+
+ if ((fd = open(filename, O_RDONLY)) < 0)
+ terminate("failed to open %s", filename);
+
+ (void) elf_version(EV_CURRENT);
+
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
+ close(fd);
+ terminate("failed to read %s: %s\n", filename,
+ elf_errmsg(-1));
+ }
+
+ source_types = built_source_types(elf, filename);
+
+ if ((source_types == SOURCE_NONE || (source_types & SOURCE_UNKNOWN)) &&
+ ignore_non_c) {
+ debug(1, "Ignoring file %s from unknown sources\n", filename);
+ exit(0);
+ }
+
+ for (i = 0; readers[i] != NULL; i++) {
+ if ((rc = readers[i](td, elf, filename)) == 0)
+ break;
+
+ assert(rc < 0 && errno == ENOENT);
+ }
+
+ if (readers[i] == NULL) {
+ /*
+ * None of the readers found compatible type data.
+ */
+
+ if (findelfsecidx(elf, filename, ".debug") >= 0) {
+ terminate("%s: DWARF version 1 is not supported\n",
+ filename);
+ }
+
+ if (!(source_types & SOURCE_C) && ignore_non_c) {
+ debug(1, "Ignoring file %s not built from C sources\n",
+ filename);
+ exit(0);
+ }
+
+ rc = 0;
+ } else {
+ rc = 1;
+ }
+
+ (void) elf_end(elf);
+ (void) close(fd);
+
+ return (rc);
+}
+
+int
+main(int argc, char **argv)
+{
+ tdata_t *filetd, *mstrtd;
+ const char *label = NULL;
+ int verbose = 0;
+ int ignore_non_c = 0;
+ int keep_stabs = 0;
+ int c;
+
+#if defined(sun)
+ sighold(SIGINT);
+ sighold(SIGQUIT);
+ sighold(SIGTERM);
+#endif
+
+ progname = basename(argv[0]);
+
+ if (getenv("CTFCONVERT_DEBUG_LEVEL"))
+ debug_level = atoi(getenv("CTFCONVERT_DEBUG_LEVEL"));
+ if (getenv("CTFCONVERT_DEBUG_PARSE"))
+ debug_parse = atoi(getenv("CTFCONVERT_DEBUG_PARSE"));
+
+ while ((c = getopt(argc, argv, ":l:L:o:givs")) != EOF) {
+ switch (c) {
+ case 'l':
+ label = optarg;
+ break;
+ case 'L':
+ if ((label = getenv(optarg)) == NULL)
+ label = CTF_DEFAULT_LABEL;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 's':
+ dynsym = CTF_USE_DYNSYM;
+ break;
+ case 'i':
+ ignore_non_c = 1;
+ break;
+ case 'g':
+ keep_stabs = CTF_KEEP_STABS;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ exit(2);
+ }
+ }
+
+ if (getenv("STRIPSTABS_KEEP_STABS") != NULL)
+ keep_stabs = CTF_KEEP_STABS;
+
+ if (argc - optind != 1 || label == NULL) {
+ usage();
+ exit(2);
+ }
+
+ infile = argv[optind];
+ if (access(infile, R_OK) != 0)
+ terminate("Can't access %s", infile);
+
+ /*
+ * Upon receipt of a signal, we want to clean up and exit. Our
+ * primary goal during cleanup is to restore the system to a state
+ * such that a subsequent make will eventually cause this command to
+ * be re-run. If we remove the input file (which we do if we get a
+ * signal and the user didn't specify a separate output file), make
+ * will need to rebuild the input file, and will then need to re-run
+ * ctfconvert, which is what we want.
+ */
+ set_terminate_cleanup(terminate_cleanup);
+
+#if defined(sun)
+ sigset(SIGINT, handle_sig);
+ sigset(SIGQUIT, handle_sig);
+ sigset(SIGTERM, handle_sig);
+#else
+ signal(SIGINT, handle_sig);
+ signal(SIGQUIT, handle_sig);
+ signal(SIGTERM, handle_sig);
+#endif
+
+ filetd = tdata_new();
+
+ if (!file_read(filetd, infile, ignore_non_c))
+ terminate("%s doesn't have type data to convert\n", infile);
+
+ if (verbose)
+ iidesc_stats(filetd->td_iihash);
+
+ mstrtd = tdata_new();
+ merge_into_master(filetd, mstrtd, NULL, 1);
+
+ tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX);
+
+ /*
+ * If the user supplied an output file that is different from the
+ * input file, write directly to the output file. Otherwise, write
+ * to a temporary file, and replace the input file when we're done.
+ */
+ if (outfile && strcmp(infile, outfile) != 0) {
+ write_ctf(mstrtd, infile, outfile, dynsym | keep_stabs);
+ } else {
+ char *tmpname = mktmpname(infile, ".ctf");
+ write_ctf(mstrtd, infile, tmpname, dynsym | keep_stabs);
+ if (rename(tmpname, infile) != 0)
+ terminate("Couldn't rename temp file %s", tmpname);
+ free(tmpname);
+ }
+
+ return (0);
+}
diff --git a/cvt/ctfmerge.c b/cvt/ctfmerge.c
new file mode 100644
index 0000000..f9d7237
--- /dev/null
+++ b/cvt/ctfmerge.c
@@ -0,0 +1,1011 @@
+/*
+ * 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"
+
+/*
+ * Given several files containing CTF data, merge and uniquify that data into
+ * a single CTF section in an output file.
+ *
+ * Merges can proceed independently. As such, we perform the merges in parallel
+ * using a worker thread model. A given glob of CTF data (either all of the CTF
+ * data from a single input file, or the result of one or more merges) can only
+ * be involved in a single merge at any given time, so the process decreases in
+ * parallelism, especially towards the end, as more and more files are
+ * consolidated, finally resulting in a single merge of two large CTF graphs.
+ * Unfortunately, the last merge is also the slowest, as the two graphs being
+ * merged are each the product of merges of half of the input files.
+ *
+ * The algorithm consists of two phases, described in detail below. The first
+ * phase entails the merging of CTF data in groups of eight. The second phase
+ * takes the results of Phase I, and merges them two at a time. This disparity
+ * is due to an observation that the merge time increases at least quadratically
+ * with the size of the CTF data being merged. As such, merges of CTF graphs
+ * newly read from input files are much faster than merges of CTF graphs that
+ * are themselves the results of prior merges.
+ *
+ * A further complication is the need to ensure the repeatability of CTF merges.
+ * That is, a merge should produce the same output every time, given the same
+ * input. In both phases, this consistency requirement is met by imposing an
+ * ordering on the merge process, thus ensuring that a given set of input files
+ * are merged in the same order every time.
+ *
+ * Phase I
+ *
+ * The main thread reads the input files one by one, transforming the CTF
+ * data they contain into tdata structures. When a given file has been read
+ * and parsed, it is placed on the work queue for retrieval by worker threads.
+ *
+ * Central to Phase I is the Work In Progress (wip) array, which is used to
+ * merge batches of files in a predictable order. Files are read by the main
+ * thread, and are merged into wip array elements in round-robin order. When
+ * the number of files merged into a given array slot equals the batch size,
+ * the merged CTF graph in that array is added to the done slot in order by
+ * array slot.
+ *
+ * For example, consider a case where we have five input files, a batch size
+ * of two, a wip array size of two, and two worker threads (T1 and T2).
+ *
+ * 1. The wip array elements are assigned initial batch numbers 0 and 1.
+ * 2. T1 reads an input file from the input queue (wq_queue). This is the
+ * first input file, so it is placed into wip[0]. The second file is
+ * similarly read and placed into wip[1]. The wip array slots now contain
+ * one file each (wip_nmerged == 1).
+ * 3. T1 reads the third input file, which it merges into wip[0]. The
+ * number of files in wip[0] is equal to the batch size.
+ * 4. T2 reads the fourth input file, which it merges into wip[1]. wip[1]
+ * is now full too.
+ * 5. T2 attempts to place the contents of wip[1] on the done queue
+ * (wq_done_queue), but it can't, since the batch ID for wip[1] is 1.
+ * Batch 0 needs to be on the done queue before batch 1 can be added, so
+ * T2 blocks on wip[1]'s cv.
+ * 6. T1 attempts to place the contents of wip[0] on the done queue, and
+ * succeeds, updating wq_lastdonebatch to 0. It clears wip[0], and sets
+ * its batch ID to 2. T1 then signals wip[1]'s cv to awaken T2.
+ * 7. T2 wakes up, notices that wq_lastdonebatch is 0, which means that
+ * batch 1 can now be added. It adds wip[1] to the done queue, clears
+ * wip[1], and sets its batch ID to 3. It signals wip[0]'s cv, and
+ * restarts.
+ *
+ * The above process continues until all input files have been consumed. At
+ * this point, a pair of barriers are used to allow a single thread to move
+ * any partial batches from the wip array to the done array in batch ID order.
+ * When this is complete, wq_done_queue is moved to wq_queue, and Phase II
+ * begins.
+ *
+ * Locking Semantics (Phase I)
+ *
+ * The input queue (wq_queue) and the done queue (wq_done_queue) are
+ * protected by separate mutexes - wq_queue_lock and wq_done_queue. wip
+ * array slots are protected by their own mutexes, which must be grabbed
+ * before releasing the input queue lock. The wip array lock is dropped
+ * when the thread restarts the loop. If the array slot was full, the
+ * array lock will be held while the slot contents are added to the done
+ * queue. The done queue lock is used to protect the wip slot cv's.
+ *
+ * The pow number is protected by the queue lock. The master batch ID
+ * and last completed batch (wq_lastdonebatch) counters are protected *in
+ * Phase I* by the done queue lock.
+ *
+ * Phase II
+ *
+ * When Phase II begins, the queue consists of the merged batches from the
+ * first phase. Assume we have five batches:
+ *
+ * Q: a b c d e
+ *
+ * Using the same batch ID mechanism we used in Phase I, but without the wip
+ * array, worker threads remove two entries at a time from the beginning of
+ * the queue. These two entries are merged, and are added back to the tail
+ * of the queue, as follows:
+ *
+ * Q: a b c d e # start
+ * Q: c d e ab # a, b removed, merged, added to end
+ * Q: e ab cd # c, d removed, merged, added to end
+ * Q: cd eab # e, ab removed, merged, added to end
+ * Q: cdeab # cd, eab removed, merged, added to end
+ *
+ * When one entry remains on the queue, with no merges outstanding, Phase II
+ * finishes. We pre-determine the stopping point by pre-calculating the
+ * number of nodes that will appear on the list. In the example above, the
+ * number (wq_ninqueue) is 9. When ninqueue is 1, we conclude Phase II by
+ * signaling the main thread via wq_done_cv.
+ *
+ * Locking Semantics (Phase II)
+ *
+ * The queue (wq_queue), ninqueue, and the master batch ID and last
+ * completed batch counters are protected by wq_queue_lock. The done
+ * queue and corresponding lock are unused in Phase II as is the wip array.
+ *
+ * Uniquification
+ *
+ * We want the CTF data that goes into a given module to be as small as
+ * possible. For example, we don't want it to contain any type data that may
+ * be present in another common module. As such, after creating the master
+ * tdata_t for a given module, we can, if requested by the user, uniquify it
+ * against the tdata_t from another module (genunix in the case of the SunOS
+ * kernel). We perform a merge between the tdata_t for this module and the
+ * tdata_t from genunix. Nodes found in this module that are not present in
+ * genunix are added to a third tdata_t - the uniquified tdata_t.
+ *
+ * Additive Merges
+ *
+ * In some cases, for example if we are issuing a new version of a common
+ * module in a patch, we need to make sure that the CTF data already present
+ * in that module does not change. Changes to this data would void the CTF
+ * data in any module that uniquified against the common module. To preserve
+ * the existing data, we can perform what is known as an additive merge. In
+ * this case, a final uniquification is performed against the CTF data in the
+ * previous version of the module. The result will be the placement of new
+ * and changed data after the existing data, thus preserving the existing type
+ * ID space.
+ *
+ * Saving the result
+ *
+ * When the merges are complete, the resulting tdata_t is placed into the
+ * output file, replacing the .SUNW_ctf section (if any) already in that file.
+ *
+ * The person who changes the merging thread code in this file without updating
+ * this comment will not live to see the stock hit five.
+ */
+
+#include "libctf/ctf_impl.h"
+#if defined(sun)
+#include <synch.h>
+#endif
+#if defined(sun)
+#include <alloca.h>
+#endif
+#if defined(sun)
+#include <sys/sysconf.h>
+#endif
+#include "ctftools.h"
+#include "ctfmerge.h"
+#include "traverse.h"
+#include "memory.h"
+#include "fifo.h"
+#include "barrier.h"
+
+#pragma init(bigheap)
+
+#define MERGE_PHASE1_BATCH_SIZE 8
+#define MERGE_PHASE1_MAX_SLOTS 5
+#define MERGE_INPUT_THROTTLE_LEN 10
+
+const char *progname;
+static char *outfile = NULL;
+static char *tmpname = NULL;
+static int dynsym;
+int debug_level = DEBUG_LEVEL;
+static size_t maxpgsize = 0x400000;
+
+
+void
+usage(void)
+{
+ (void) fprintf(stderr,
+ "Usage: %s [-fgstv] -l label | -L labelenv -o outfile file ...\n"
+ " %s [-fgstv] -l label | -L labelenv -o outfile -d uniqfile\n"
+ " %*s [-g] [-D uniqlabel] file ...\n"
+ " %s [-fgstv] -l label | -L labelenv -o outfile -w withfile "
+ "file ...\n"
+ " %s [-g] -c srcfile destfile\n"
+ "\n"
+ " Note: if -L labelenv is specified and labelenv is not set in\n"
+ " the environment, a default value is used.\n",
+ progname, progname, strlen(progname), " ",
+ progname, progname);
+}
+
+#if defined(sun)
+static void
+bigheap(void)
+{
+ size_t big, *size;
+ int sizes;
+ struct memcntl_mha mha;
+
+ /*
+ * First, get the available pagesizes.
+ */
+ if ((sizes = getpagesizes(NULL, 0)) == -1)
+ return;
+
+ if (sizes == 1 || (size = alloca(sizeof (size_t) * sizes)) == NULL)
+ return;
+
+ if (getpagesizes(size, sizes) == -1)
+ return;
+
+ while (size[sizes - 1] > maxpgsize)
+ sizes--;
+
+ /* set big to the largest allowed page size */
+ big = size[sizes - 1];
+ if (big & (big - 1)) {
+ /*
+ * The largest page size is not a power of two for some
+ * inexplicable reason; return.
+ */
+ return;
+ }
+
+ /*
+ * Now, align our break to the largest page size.
+ */
+ if (brk((void *)((((uintptr_t)sbrk(0) - 1) & ~(big - 1)) + big)) != 0)
+ return;
+
+ /*
+ * set the preferred page size for the heap
+ */
+ mha.mha_cmd = MHA_MAPSIZE_BSSBRK;
+ mha.mha_flags = 0;
+ mha.mha_pagesize = big;
+
+ (void) memcntl(NULL, 0, MC_HAT_ADVISE, (caddr_t)&mha, 0, 0);
+}
+#endif
+
+static void
+finalize_phase_one(workqueue_t *wq)
+{
+ int startslot, i;
+
+ /*
+ * wip slots are cleared out only when maxbatchsz td's have been merged
+ * into them. We're not guaranteed that the number of files we're
+ * merging is a multiple of maxbatchsz, so there will be some partial
+ * groups in the wip array. Move them to the done queue in batch ID
+ * order, starting with the slot containing the next batch that would
+ * have been placed on the done queue, followed by the others.
+ * One thread will be doing this while the others wait at the barrier
+ * back in worker_thread(), so we don't need to worry about pesky things
+ * like locks.
+ */
+
+ for (startslot = -1, i = 0; i < wq->wq_nwipslots; i++) {
+ if (wq->wq_wip[i].wip_batchid == wq->wq_lastdonebatch + 1) {
+ startslot = i;
+ break;
+ }
+ }
+
+ assert(startslot != -1);
+
+ for (i = startslot; i < startslot + wq->wq_nwipslots; i++) {
+ int slotnum = i % wq->wq_nwipslots;
+ wip_t *wipslot = &wq->wq_wip[slotnum];
+
+ if (wipslot->wip_td != NULL) {
+ debug(2, "clearing slot %d (%d) (saving %d)\n",
+ slotnum, i, wipslot->wip_nmerged);
+ } else
+ debug(2, "clearing slot %d (%d)\n", slotnum, i);
+
+ if (wipslot->wip_td != NULL) {
+ fifo_add(wq->wq_donequeue, wipslot->wip_td);
+ wq->wq_wip[slotnum].wip_td = NULL;
+ }
+ }
+
+ wq->wq_lastdonebatch = wq->wq_next_batchid++;
+
+ debug(2, "phase one done: donequeue has %d items\n",
+ fifo_len(wq->wq_donequeue));
+}
+
+static void
+init_phase_two(workqueue_t *wq)
+{
+ int num;
+
+ /*
+ * We're going to continually merge the first two entries on the queue,
+ * placing the result on the end, until there's nothing left to merge.
+ * At that point, everything will have been merged into one. The
+ * initial value of ninqueue needs to be equal to the total number of
+ * entries that will show up on the queue, both at the start of the
+ * phase and as generated by merges during the phase.
+ */
+ wq->wq_ninqueue = num = fifo_len(wq->wq_donequeue);
+ while (num != 1) {
+ wq->wq_ninqueue += num / 2;
+ num = num / 2 + num % 2;
+ }
+
+ /*
+ * Move the done queue to the work queue. We won't be using the done
+ * queue in phase 2.
+ */
+ assert(fifo_len(wq->wq_queue) == 0);
+ fifo_free(wq->wq_queue, NULL);
+ wq->wq_queue = wq->wq_donequeue;
+}
+
+static void
+wip_save_work(workqueue_t *wq, wip_t *slot, int slotnum)
+{
+ pthread_mutex_lock(&wq->wq_donequeue_lock);
+
+ while (wq->wq_lastdonebatch + 1 < slot->wip_batchid)
+ pthread_cond_wait(&slot->wip_cv, &wq->wq_donequeue_lock);
+ assert(wq->wq_lastdonebatch + 1 == slot->wip_batchid);
+
+ fifo_add(wq->wq_donequeue, slot->wip_td);
+ wq->wq_lastdonebatch++;
+ pthread_cond_signal(&wq->wq_wip[(slotnum + 1) %
+ wq->wq_nwipslots].wip_cv);
+
+ /* reset the slot for next use */
+ slot->wip_td = NULL;
+ slot->wip_batchid = wq->wq_next_batchid++;
+
+ pthread_mutex_unlock(&wq->wq_donequeue_lock);
+}
+
+static void
+wip_add_work(wip_t *slot, tdata_t *pow)
+{
+ if (slot->wip_td == NULL) {
+ slot->wip_td = pow;
+ slot->wip_nmerged = 1;
+ } else {
+ debug(2, "%d: merging %p into %p\n", pthread_self(),
+ (void *)pow, (void *)slot->wip_td);
+
+ merge_into_master(pow, slot->wip_td, NULL, 0);
+ tdata_free(pow);
+
+ slot->wip_nmerged++;
+ }
+}
+
+static void
+worker_runphase1(workqueue_t *wq)
+{
+ wip_t *wipslot;
+ tdata_t *pow;
+ int wipslotnum, pownum;
+
+ for (;;) {
+ pthread_mutex_lock(&wq->wq_queue_lock);
+
+ while (fifo_empty(wq->wq_queue)) {
+ if (wq->wq_nomorefiles == 1) {
+ pthread_cond_broadcast(&wq->wq_work_avail);
+ pthread_mutex_unlock(&wq->wq_queue_lock);
+
+ /* on to phase 2 ... */
+ return;
+ }
+
+ pthread_cond_wait(&wq->wq_work_avail,
+ &wq->wq_queue_lock);
+ }
+
+ /* there's work to be done! */
+ pow = fifo_remove(wq->wq_queue);
+ pownum = wq->wq_nextpownum++;
+ pthread_cond_broadcast(&wq->wq_work_removed);
+
+ assert(pow != NULL);
+
+ /* merge it into the right slot */
+ wipslotnum = pownum % wq->wq_nwipslots;
+ wipslot = &wq->wq_wip[wipslotnum];
+
+ pthread_mutex_lock(&wipslot->wip_lock);
+
+ pthread_mutex_unlock(&wq->wq_queue_lock);
+
+ wip_add_work(wipslot, pow);
+
+ if (wipslot->wip_nmerged == wq->wq_maxbatchsz)
+ wip_save_work(wq, wipslot, wipslotnum);
+
+ pthread_mutex_unlock(&wipslot->wip_lock);
+ }
+}
+
+static void
+worker_runphase2(workqueue_t *wq)
+{
+ tdata_t *pow1, *pow2;
+ int batchid;
+
+ for (;;) {
+ pthread_mutex_lock(&wq->wq_queue_lock);
+
+ if (wq->wq_ninqueue == 1) {
+ pthread_cond_broadcast(&wq->wq_work_avail);
+ pthread_mutex_unlock(&wq->wq_queue_lock);
+
+ debug(2, "%d: entering p2 completion barrier\n",
+ pthread_self());
+ if (barrier_wait(&wq->wq_bar1)) {
+ pthread_mutex_lock(&wq->wq_queue_lock);
+ wq->wq_alldone = 1;
+ pthread_cond_signal(&wq->wq_alldone_cv);
+ pthread_mutex_unlock(&wq->wq_queue_lock);
+ }
+
+ return;
+ }
+
+ if (fifo_len(wq->wq_queue) < 2) {
+ pthread_cond_wait(&wq->wq_work_avail,
+ &wq->wq_queue_lock);
+ pthread_mutex_unlock(&wq->wq_queue_lock);
+ continue;
+ }
+
+ /* there's work to be done! */
+ pow1 = fifo_remove(wq->wq_queue);
+ pow2 = fifo_remove(wq->wq_queue);
+ wq->wq_ninqueue -= 2;
+
+ batchid = wq->wq_next_batchid++;
+
+ pthread_mutex_unlock(&wq->wq_queue_lock);
+
+ debug(2, "%d: merging %p into %p\n", pthread_self(),
+ (void *)pow1, (void *)pow2);
+ merge_into_master(pow1, pow2, NULL, 0);
+ tdata_free(pow1);
+
+ /*
+ * merging is complete. place at the tail of the queue in
+ * proper order.
+ */
+ pthread_mutex_lock(&wq->wq_queue_lock);
+ while (wq->wq_lastdonebatch + 1 != batchid) {
+ pthread_cond_wait(&wq->wq_done_cv,
+ &wq->wq_queue_lock);
+ }
+
+ wq->wq_lastdonebatch = batchid;
+
+ fifo_add(wq->wq_queue, pow2);
+ debug(2, "%d: added %p to queue, len now %d, ninqueue %d\n",
+ pthread_self(), (void *)pow2, fifo_len(wq->wq_queue),
+ wq->wq_ninqueue);
+ pthread_cond_broadcast(&wq->wq_done_cv);
+ pthread_cond_signal(&wq->wq_work_avail);
+ pthread_mutex_unlock(&wq->wq_queue_lock);
+ }
+}
+
+/*
+ * Main loop for worker threads.
+ */
+static void
+worker_thread(workqueue_t *wq)
+{
+ worker_runphase1(wq);
+
+ debug(2, "%d: entering first barrier\n", pthread_self());
+
+ if (barrier_wait(&wq->wq_bar1)) {
+
+ debug(2, "%d: doing work in first barrier\n", pthread_self());
+
+ finalize_phase_one(wq);
+
+ init_phase_two(wq);
+
+ debug(2, "%d: ninqueue is %d, %d on queue\n", pthread_self(),
+ wq->wq_ninqueue, fifo_len(wq->wq_queue));
+ }
+
+ debug(2, "%d: entering second barrier\n", pthread_self());
+
+ (void) barrier_wait(&wq->wq_bar2);
+
+ debug(2, "%d: phase 1 complete\n", pthread_self());
+
+ worker_runphase2(wq);
+}
+
+/*
+ * Pass a tdata_t tree, built from an input file, off to the work queue for
+ * consumption by worker threads.
+ */
+static int
+merge_ctf_cb(tdata_t *td, char *name, void *arg)
+{
+ workqueue_t *wq = arg;
+
+ debug(3, "Adding tdata %p for processing\n", (void *)td);
+
+ pthread_mutex_lock(&wq->wq_queue_lock);
+ while (fifo_len(wq->wq_queue) > wq->wq_ithrottle) {
+ debug(2, "Throttling input (len = %d, throttle = %d)\n",
+ fifo_len(wq->wq_queue), wq->wq_ithrottle);
+ pthread_cond_wait(&wq->wq_work_removed, &wq->wq_queue_lock);
+ }
+
+ fifo_add(wq->wq_queue, td);
+ debug(1, "Thread %d announcing %s\n", pthread_self(), name);
+ pthread_cond_broadcast(&wq->wq_work_avail);
+ pthread_mutex_unlock(&wq->wq_queue_lock);
+
+ return (1);
+}
+
+/*
+ * This program is intended to be invoked from a Makefile, as part of the build.
+ * As such, in the event of a failure or user-initiated interrupt (^C), we need
+ * to ensure that a subsequent re-make will cause ctfmerge to be executed again.
+ * Unfortunately, ctfmerge will usually be invoked directly after (and as part
+ * of the same Makefile rule as) a link, and will operate on the linked file
+ * in place. If we merely exit upon receipt of a SIGINT, a subsequent make
+ * will notice that the *linked* file is newer than the object files, and thus
+ * will not reinvoke ctfmerge. The only way to ensure that a subsequent make
+ * reinvokes ctfmerge, is to remove the file to which we are adding CTF
+ * data (confusingly named the output file). This means that the link will need
+ * to happen again, but links are generally fast, and we can't allow the merge
+ * to be skipped.
+ *
+ * Another possibility would be to block SIGINT entirely - to always run to
+ * completion. The run time of ctfmerge can, however, be measured in minutes
+ * in some cases, so this is not a valid option.
+ */
+static void
+handle_sig(int sig)
+{
+ terminate("Caught signal %d - exiting\n", sig);
+}
+
+static void
+terminate_cleanup(void)
+{
+ int dounlink = getenv("CTFMERGE_TERMINATE_NO_UNLINK") ? 0 : 1;
+
+ if (tmpname != NULL && dounlink)
+ unlink(tmpname);
+
+ if (outfile == NULL)
+ return;
+
+#if !defined(__FreeBSD__)
+ if (dounlink) {
+ fprintf(stderr, "Removing %s\n", outfile);
+ unlink(outfile);
+ }
+#endif
+}
+
+static void
+copy_ctf_data(char *srcfile, char *destfile, int keep_stabs)
+{
+ tdata_t *srctd;
+
+ if (read_ctf(&srcfile, 1, NULL, read_ctf_save_cb, &srctd, 1) == 0)
+ terminate("No CTF data found in source file %s\n", srcfile);
+
+ tmpname = mktmpname(destfile, ".ctf");
+ write_ctf(srctd, destfile, tmpname, CTF_COMPRESS | keep_stabs);
+ if (rename(tmpname, destfile) != 0) {
+ terminate("Couldn't rename temp file %s to %s", tmpname,
+ destfile);
+ }
+ free(tmpname);
+ tdata_free(srctd);
+}
+
+static void
+wq_init(workqueue_t *wq, int nfiles)
+{
+ int throttle, nslots, i;
+
+ if (getenv("CTFMERGE_MAX_SLOTS"))
+ nslots = atoi(getenv("CTFMERGE_MAX_SLOTS"));
+ else
+ nslots = MERGE_PHASE1_MAX_SLOTS;
+
+ if (getenv("CTFMERGE_PHASE1_BATCH_SIZE"))
+ wq->wq_maxbatchsz = atoi(getenv("CTFMERGE_PHASE1_BATCH_SIZE"));
+ else
+ wq->wq_maxbatchsz = MERGE_PHASE1_BATCH_SIZE;
+
+ nslots = MIN(nslots, (nfiles + wq->wq_maxbatchsz - 1) /
+ wq->wq_maxbatchsz);
+
+ wq->wq_wip = xcalloc(sizeof (wip_t) * nslots);
+ wq->wq_nwipslots = nslots;
+ wq->wq_nthreads = MIN(sysconf(_SC_NPROCESSORS_ONLN) * 3 / 2, nslots);
+ wq->wq_thread = xmalloc(sizeof (pthread_t) * wq->wq_nthreads);
+
+ if (getenv("CTFMERGE_INPUT_THROTTLE"))
+ throttle = atoi(getenv("CTFMERGE_INPUT_THROTTLE"));
+ else
+ throttle = MERGE_INPUT_THROTTLE_LEN;
+ wq->wq_ithrottle = throttle * wq->wq_nthreads;
+
+ debug(1, "Using %d slots, %d threads\n", wq->wq_nwipslots,
+ wq->wq_nthreads);
+
+ wq->wq_next_batchid = 0;
+
+ for (i = 0; i < nslots; i++) {
+ pthread_mutex_init(&wq->wq_wip[i].wip_lock, NULL);
+ wq->wq_wip[i].wip_batchid = wq->wq_next_batchid++;
+ }
+
+ pthread_mutex_init(&wq->wq_queue_lock, NULL);
+ wq->wq_queue = fifo_new();
+ pthread_cond_init(&wq->wq_work_avail, NULL);
+ pthread_cond_init(&wq->wq_work_removed, NULL);
+ wq->wq_ninqueue = nfiles;
+ wq->wq_nextpownum = 0;
+
+ pthread_mutex_init(&wq->wq_donequeue_lock, NULL);
+ wq->wq_donequeue = fifo_new();
+ wq->wq_lastdonebatch = -1;
+
+ pthread_cond_init(&wq->wq_done_cv, NULL);
+
+ pthread_cond_init(&wq->wq_alldone_cv, NULL);
+ wq->wq_alldone = 0;
+
+ barrier_init(&wq->wq_bar1, wq->wq_nthreads);
+ barrier_init(&wq->wq_bar2, wq->wq_nthreads);
+
+ wq->wq_nomorefiles = 0;
+}
+
+static void
+start_threads(workqueue_t *wq)
+{
+ sigset_t sets;
+ int i;
+
+ sigemptyset(&sets);
+ sigaddset(&sets, SIGINT);
+ sigaddset(&sets, SIGQUIT);
+ sigaddset(&sets, SIGTERM);
+ pthread_sigmask(SIG_BLOCK, &sets, NULL);
+
+ for (i = 0; i < wq->wq_nthreads; i++) {
+ pthread_create(&wq->wq_thread[i], NULL,
+ (void *(*)(void *))worker_thread, wq);
+ }
+
+#if defined(sun)
+ sigset(SIGINT, handle_sig);
+ sigset(SIGQUIT, handle_sig);
+ sigset(SIGTERM, handle_sig);
+#else
+ signal(SIGINT, handle_sig);
+ signal(SIGQUIT, handle_sig);
+ signal(SIGTERM, handle_sig);
+#endif
+ pthread_sigmask(SIG_UNBLOCK, &sets, NULL);
+}
+
+static void
+join_threads(workqueue_t *wq)
+{
+ int i;
+
+ for (i = 0; i < wq->wq_nthreads; i++) {
+ pthread_join(wq->wq_thread[i], NULL);
+ }
+}
+
+static int
+strcompare(const void *p1, const void *p2)
+{
+ char *s1 = *((char **)p1);
+ char *s2 = *((char **)p2);
+
+ return (strcmp(s1, s2));
+}
+
+/*
+ * Core work queue structure; passed to worker threads on thread creation
+ * as the main point of coordination. Allocate as a static structure; we
+ * could have put this into a local variable in main, but passing a pointer
+ * into your stack to another thread is fragile at best and leads to some
+ * hard-to-debug failure modes.
+ */
+static workqueue_t wq;
+
+int
+main(int argc, char **argv)
+{
+ tdata_t *mstrtd, *savetd;
+ char *uniqfile = NULL, *uniqlabel = NULL;
+ char *withfile = NULL;
+ char *label = NULL;
+ char **ifiles, **tifiles;
+ int verbose = 0, docopy = 0;
+ int write_fuzzy_match = 0;
+ int keep_stabs = 0;
+ int require_ctf = 0;
+ int nifiles, nielems;
+ int c, i, idx, tidx, err;
+
+ progname = basename(argv[0]);
+
+ if (getenv("CTFMERGE_DEBUG_LEVEL"))
+ debug_level = atoi(getenv("CTFMERGE_DEBUG_LEVEL"));
+
+ err = 0;
+ while ((c = getopt(argc, argv, ":cd:D:fgl:L:o:tvw:s")) != EOF) {
+ switch (c) {
+ case 'c':
+ docopy = 1;
+ break;
+ case 'd':
+ /* Uniquify against `uniqfile' */
+ uniqfile = optarg;
+ break;
+ case 'D':
+ /* Uniquify against label `uniqlabel' in `uniqfile' */
+ uniqlabel = optarg;
+ break;
+ case 'f':
+ write_fuzzy_match = CTF_FUZZY_MATCH;
+ break;
+ case 'g':
+ keep_stabs = CTF_KEEP_STABS;
+ break;
+ case 'l':
+ /* Label merged types with `label' */
+ label = optarg;
+ break;
+ case 'L':
+ /* Label merged types with getenv(`label`) */
+ if ((label = getenv(optarg)) == NULL)
+ label = CTF_DEFAULT_LABEL;
+ break;
+ case 'o':
+ /* Place merged types in CTF section in `outfile' */
+ outfile = optarg;
+ break;
+ case 't':
+ /* Insist *all* object files built from C have CTF */
+ require_ctf = 1;
+ break;
+ case 'v':
+ /* More debugging information */
+ verbose = 1;
+ break;
+ case 'w':
+ /* Additive merge with data from `withfile' */
+ withfile = optarg;
+ break;
+ case 's':
+ /* use the dynsym rather than the symtab */
+ dynsym = CTF_USE_DYNSYM;
+ break;
+ default:
+ usage();
+ exit(2);
+ }
+ }
+
+ /* Validate arguments */
+ if (docopy) {
+ if (uniqfile != NULL || uniqlabel != NULL || label != NULL ||
+ outfile != NULL || withfile != NULL || dynsym != 0)
+ err++;
+
+ if (argc - optind != 2)
+ err++;
+ } else {
+ if (uniqfile != NULL && withfile != NULL)
+ err++;
+
+ if (uniqlabel != NULL && uniqfile == NULL)
+ err++;
+
+ if (outfile == NULL || label == NULL)
+ err++;
+
+ if (argc - optind == 0)
+ err++;
+ }
+
+ if (err) {
+ usage();
+ exit(2);
+ }
+
+ if (getenv("STRIPSTABS_KEEP_STABS") != NULL)
+ keep_stabs = CTF_KEEP_STABS;
+
+ if (uniqfile && access(uniqfile, R_OK) != 0) {
+ warning("Uniquification file %s couldn't be opened and "
+ "will be ignored.\n", uniqfile);
+ uniqfile = NULL;
+ }
+ if (withfile && access(withfile, R_OK) != 0) {
+ warning("With file %s couldn't be opened and will be "
+ "ignored.\n", withfile);
+ withfile = NULL;
+ }
+ if (outfile && access(outfile, R_OK|W_OK) != 0)
+ terminate("Cannot open output file %s for r/w", outfile);
+
+ /*
+ * This is ugly, but we don't want to have to have a separate tool
+ * (yet) just for copying an ELF section with our specific requirements,
+ * so we shoe-horn a copier into ctfmerge.
+ */
+ if (docopy) {
+ copy_ctf_data(argv[optind], argv[optind + 1], keep_stabs);
+
+ exit(0);
+ }
+
+ set_terminate_cleanup(terminate_cleanup);
+
+ /* Sort the input files and strip out duplicates */
+ nifiles = argc - optind;
+ ifiles = xmalloc(sizeof (char *) * nifiles);
+ tifiles = xmalloc(sizeof (char *) * nifiles);
+
+ for (i = 0; i < nifiles; i++)
+ tifiles[i] = argv[optind + i];
+ qsort(tifiles, nifiles, sizeof (char *), (int (*)())strcompare);
+
+ ifiles[0] = tifiles[0];
+ for (idx = 0, tidx = 1; tidx < nifiles; tidx++) {
+ if (strcmp(ifiles[idx], tifiles[tidx]) != 0)
+ ifiles[++idx] = tifiles[tidx];
+ }
+ nifiles = idx + 1;
+
+ /* Make sure they all exist */
+ if ((nielems = count_files(ifiles, nifiles)) < 0)
+ terminate("Some input files were inaccessible\n");
+
+ /* Prepare for the merge */
+ wq_init(&wq, nielems);
+
+ start_threads(&wq);
+
+ /*
+ * Start the merge
+ *
+ * We're reading everything from each of the object files, so we
+ * don't need to specify labels.
+ */
+ if (read_ctf(ifiles, nifiles, NULL, merge_ctf_cb,
+ &wq, require_ctf) == 0) {
+ /*
+ * If we're verifying that C files have CTF, it's safe to
+ * assume that in this case, we're building only from assembly
+ * inputs.
+ */
+ if (require_ctf)
+ exit(0);
+ terminate("No ctf sections found to merge\n");
+ }
+
+ pthread_mutex_lock(&wq.wq_queue_lock);
+ wq.wq_nomorefiles = 1;
+ pthread_cond_broadcast(&wq.wq_work_avail);
+ pthread_mutex_unlock(&wq.wq_queue_lock);
+
+ pthread_mutex_lock(&wq.wq_queue_lock);
+ while (wq.wq_alldone == 0)
+ pthread_cond_wait(&wq.wq_alldone_cv, &wq.wq_queue_lock);
+ pthread_mutex_unlock(&wq.wq_queue_lock);
+
+ join_threads(&wq);
+
+ /*
+ * All requested files have been merged, with the resulting tree in
+ * mstrtd. savetd is the tree that will be placed into the output file.
+ *
+ * Regardless of whether we're doing a normal uniquification or an
+ * additive merge, we need a type tree that has been uniquified
+ * against uniqfile or withfile, as appropriate.
+ *
+ * If we're doing a uniquification, we stuff the resulting tree into
+ * outfile. Otherwise, we add the tree to the tree already in withfile.
+ */
+ assert(fifo_len(wq.wq_queue) == 1);
+ mstrtd = fifo_remove(wq.wq_queue);
+
+ if (verbose || debug_level) {
+ debug(2, "Statistics for td %p\n", (void *)mstrtd);
+
+ iidesc_stats(mstrtd->td_iihash);
+ }
+
+ if (uniqfile != NULL || withfile != NULL) {
+ char *reffile, *reflabel = NULL;
+ tdata_t *reftd;
+
+ if (uniqfile != NULL) {
+ reffile = uniqfile;
+ reflabel = uniqlabel;
+ } else
+ reffile = withfile;
+
+ if (read_ctf(&reffile, 1, reflabel, read_ctf_save_cb,
+ &reftd, require_ctf) == 0) {
+ terminate("No CTF data found in reference file %s\n",
+ reffile);
+ }
+
+ savetd = tdata_new();
+
+ if (CTF_TYPE_ISCHILD(reftd->td_nextid))
+ terminate("No room for additional types in master\n");
+
+ savetd->td_nextid = withfile ? reftd->td_nextid :
+ CTF_INDEX_TO_TYPE(1, TRUE);
+ merge_into_master(mstrtd, reftd, savetd, 0);
+
+ tdata_label_add(savetd, label, CTF_LABEL_LASTIDX);
+
+ if (withfile) {
+ /*
+ * savetd holds the new data to be added to the withfile
+ */
+ tdata_t *withtd = reftd;
+
+ tdata_merge(withtd, savetd);
+
+ savetd = withtd;
+ } else {
+ char uniqname[MAXPATHLEN];
+ labelent_t *parle;
+
+ parle = tdata_label_top(reftd);
+
+ savetd->td_parlabel = xstrdup(parle->le_name);
+
+ strncpy(uniqname, reffile, sizeof (uniqname));
+ uniqname[MAXPATHLEN - 1] = '\0';
+ savetd->td_parname = xstrdup(basename(uniqname));
+ }
+
+ } else {
+ /*
+ * No post processing. Write the merged tree as-is into the
+ * output file.
+ */
+ tdata_label_free(mstrtd);
+ tdata_label_add(mstrtd, label, CTF_LABEL_LASTIDX);
+
+ savetd = mstrtd;
+ }
+
+ tmpname = mktmpname(outfile, ".ctf");
+ write_ctf(savetd, outfile, tmpname,
+ CTF_COMPRESS | write_fuzzy_match | dynsym | keep_stabs);
+ if (rename(tmpname, outfile) != 0)
+ terminate("Couldn't rename output temp file %s", tmpname);
+ free(tmpname);
+
+ return (0);
+}
diff --git a/cvt/ctfmerge.h b/cvt/ctfmerge.h
new file mode 100644
index 0000000..ce40803
--- /dev/null
+++ b/cvt/ctfmerge.h
@@ -0,0 +1,90 @@
+/*
+ * 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 _CTFMERGE_H
+#define _CTFMERGE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Merging structures used in ctfmerge. See ctfmerge.c for locking semantics.
+ */
+
+#include <pthread.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ctftools.h"
+#include "barrier.h"
+#include "fifo.h"
+
+typedef struct wip {
+ pthread_mutex_t wip_lock;
+ pthread_cond_t wip_cv;
+ tdata_t *wip_td;
+ int wip_nmerged;
+ int wip_batchid;
+} wip_t;
+
+typedef struct workqueue {
+ int wq_next_batchid;
+
+ int wq_maxbatchsz;
+
+ wip_t *wq_wip;
+ int wq_nwipslots;
+ int wq_nthreads;
+ int wq_ithrottle;
+
+ pthread_mutex_t wq_queue_lock;
+ fifo_t *wq_queue;
+ pthread_cond_t wq_work_avail;
+ pthread_cond_t wq_work_removed;
+ int wq_ninqueue;
+ int wq_nextpownum;
+
+ pthread_mutex_t wq_donequeue_lock;
+ fifo_t *wq_donequeue;
+ int wq_lastdonebatch;
+ pthread_cond_t wq_done_cv;
+
+ pthread_cond_t wq_alldone_cv; /* protected by queue_lock */
+ int wq_alldone;
+
+ int wq_nomorefiles;
+
+ pthread_t *wq_thread;
+
+ barrier_t wq_bar1;
+ barrier_t wq_bar2;
+} workqueue_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTFMERGE_H */
diff --git a/cvt/ctftools.h b/cvt/ctftools.h
new file mode 100644
index 0000000..48958a2
--- /dev/null
+++ b/cvt/ctftools.h
@@ -0,0 +1,446 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _CTFTOOLS_H
+#define _CTFTOOLS_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Functions and data structures used in the manipulation of stabs and CTF data
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "common/list.h"
+#include "hash.h"
+
+#ifndef DEBUG_LEVEL
+#define DEBUG_LEVEL 0
+#endif
+#ifndef DEBUG_PARSE
+#define DEBUG_PARSE 0
+#endif
+
+#ifndef DEBUG_STREAM
+#define DEBUG_STREAM stderr
+#endif
+
+#ifndef MAX
+#define MAX(a, b) ((a) < (b) ? (b) : (a))
+#endif
+
+#ifndef MIN
+#define MIN(a, b) ((a) > (b) ? (b) : (a))
+#endif
+
+#define TRUE 1
+#define FALSE 0
+
+#define CTF_ELF_SCN_NAME ".SUNW_ctf"
+
+#define CTF_LABEL_LASTIDX -1
+
+#define CTF_DEFAULT_LABEL "*** No Label Provided ***"
+
+/*
+ * Default hash sizes
+ */
+#define TDATA_LAYOUT_HASH_SIZE 8191 /* A tdesc hash based on layout */
+#define TDATA_ID_HASH_SIZE 997 /* A tdesc hash based on type id */
+#define IIDESC_HASH_SIZE 8191 /* Hash of iidesc's */
+
+/*
+ * The default function argument array size. We'll realloc the array larger
+ * if we need to, but we want a default value that will allow us to avoid
+ * reallocation in the common case.
+ */
+#define FUNCARG_DEF 5
+
+extern const char *progname;
+extern int debug_level;
+extern int debug_parse;
+extern char *curhdr;
+
+/*
+ * This is a partial copy of the stab.h that DevPro includes with their
+ * compiler.
+ */
+typedef struct stab {
+ uint32_t n_strx;
+ uint8_t n_type;
+ int8_t n_other;
+ int16_t n_desc;
+ uint32_t n_value;
+} stab_t;
+
+#define N_GSYM 0x20 /* global symbol: name,,0,type,0 */
+#define N_FUN 0x24 /* procedure: name,,0,linenumber,0 */
+#define N_STSYM 0x26 /* static symbol: name,,0,type,0 or section relative */
+#define N_LCSYM 0x28 /* .lcomm symbol: name,,0,type,0 or section relative */
+#define N_ROSYM 0x2c /* ro_data: name,,0,type,0 or section relative */
+#define N_OPT 0x3c /* compiler options */
+#define N_RSYM 0x40 /* register sym: name,,0,type,register */
+#define N_SO 0x64 /* source file name: name,,0,0,0 */
+#define N_LSYM 0x80 /* local sym: name,,0,type,offset */
+#define N_SOL 0x84 /* #included file name: name,,0,0,0 */
+#define N_PSYM 0xa0 /* parameter: name,,0,type,offset */
+#define N_LBRAC 0xc0 /* left bracket: 0,,0,nesting level,function relative */
+#define N_RBRAC 0xe0 /* right bracket: 0,,0,nesting level,func relative */
+#define N_BINCL 0x82 /* header file: name,,0,0,0 */
+#define N_EINCL 0xa2 /* end of include file */
+
+/*
+ * Nodes in the type tree
+ *
+ * Each node consists of a single tdesc_t, with one of several auxiliary
+ * structures linked in via the `data' union.
+ */
+
+/* The type of tdesc_t node */
+typedef enum stabtype {
+ STABTYPE_FIRST, /* do not use */
+ INTRINSIC,
+ POINTER,
+ ARRAY,
+ FUNCTION,
+ STRUCT,
+ UNION,
+ ENUM,
+ FORWARD,
+ TYPEDEF,
+ TYPEDEF_UNRES,
+ VOLATILE,
+ CONST,
+ RESTRICT,
+ STABTYPE_LAST /* do not use */
+} stabtype_t;
+
+typedef struct tdesc tdesc_t;
+
+/* Auxiliary structure for array tdesc_t */
+typedef struct ardef {
+ tdesc_t *ad_contents;
+ tdesc_t *ad_idxtype;
+ uint_t ad_nelems;
+} ardef_t;
+
+/* Auxiliary structure for structure/union tdesc_t */
+typedef struct mlist {
+ int ml_offset; /* Offset from start of structure (in bits) */
+ int ml_size; /* Member size (in bits) */
+ char *ml_name; /* Member name */
+ struct tdesc *ml_type; /* Member type */
+ struct mlist *ml_next; /* Next member */
+} mlist_t;
+
+/* Auxiliary structure for enum tdesc_t */
+typedef struct elist {
+ char *el_name;
+ int el_number;
+ struct elist *el_next;
+} elist_t;
+
+/* Auxiliary structure for intrinsics (integers and reals) */
+typedef enum {
+ INTR_INT,
+ INTR_REAL
+} intrtype_t;
+
+typedef struct intr {
+ intrtype_t intr_type;
+ int intr_signed;
+ union {
+ char _iformat;
+ int _fformat;
+ } _u;
+ int intr_offset;
+ int intr_nbits;
+} intr_t;
+
+#define intr_iformat _u._iformat
+#define intr_fformat _u._fformat
+
+typedef struct fnarg {
+ char *fna_name;
+ struct tdesc *fna_type;
+} fnarg_t;
+
+#define FN_F_GLOBAL 0x1
+#define FN_F_VARARGS 0x2
+
+typedef struct fndef {
+ struct tdesc *fn_ret;
+ uint_t fn_nargs;
+ tdesc_t **fn_args;
+ uint_t fn_vargs;
+} fndef_t;
+
+typedef int32_t tid_t;
+
+/*
+ * The tdesc_t (Type DESCription) is the basic node type used in the stabs data
+ * structure. Each data node gets a tdesc structure. Each node is linked into
+ * a directed graph (think of it as a tree with multiple roots and multiple
+ * leaves), with the root nodes at the top, and intrinsics at the bottom. The
+ * root nodes, which are pointed to by iidesc nodes, correspond to the types,
+ * globals, and statics defined by the stabs.
+ */
+struct tdesc {
+ char *t_name;
+ tdesc_t *t_next; /* Name hash next pointer */
+
+ tid_t t_id;
+ tdesc_t *t_hash; /* ID hash next pointer */
+
+ stabtype_t t_type;
+ int t_size; /* Size in bytes of object represented by this node */
+
+ union {
+ intr_t *intr; /* int, real */
+ tdesc_t *tdesc; /* ptr, typedef, vol, const, restr */
+ ardef_t *ardef; /* array */
+ mlist_t *members; /* struct, union */
+ elist_t *emem; /* enum */
+ fndef_t *fndef; /* function - first is return type */
+ } t_data;
+
+ int t_flags;
+ int t_vgen; /* Visitation generation (see traverse.c) */
+ int t_emark; /* Equality mark (see equiv_cb() in merge.c) */
+};
+
+#define t_intr t_data.intr
+#define t_tdesc t_data.tdesc
+#define t_ardef t_data.ardef
+#define t_members t_data.members
+#define t_emem t_data.emem
+#define t_fndef t_data.fndef
+
+#define TDESC_F_ISROOT 0x1 /* Has an iidesc_t (see below) */
+#define TDESC_F_GLOBAL 0x2
+#define TDESC_F_RESOLVED 0x4
+
+/*
+ * iidesc_t (Interesting Item DESCription) nodes point to tdesc_t nodes that
+ * correspond to "interesting" stabs. A stab is interesting if it defines a
+ * global or static variable, a global or static function, or a data type.
+ */
+typedef enum iitype {
+ II_NOT = 0,
+ II_GFUN, /* Global function */
+ II_SFUN, /* Static function */
+ II_GVAR, /* Global variable */
+ II_SVAR, /* Static variable */
+ II_PSYM, /* Function argument */
+ II_SOU, /* Struct or union */
+ II_TYPE /* Type (typedef) */
+} iitype_t;
+
+typedef struct iidesc {
+ iitype_t ii_type;
+ char *ii_name;
+ tdesc_t *ii_dtype;
+ char *ii_owner; /* File that defined this node */
+ int ii_flags;
+
+ /* Function arguments (if any) */
+ int ii_nargs;
+ tdesc_t **ii_args;
+ int ii_vargs; /* Function uses varargs */
+} iidesc_t;
+
+#define IIDESC_F_USED 0x1 /* Write this iidesc out */
+
+/*
+ * labelent_t nodes identify labels and corresponding type ranges associated
+ * with them. The label in a given labelent_t is associated with types with
+ * ids <= le_idx.
+ */
+typedef struct labelent {
+ char *le_name;
+ int le_idx;
+} labelent_t;
+
+/*
+ * The tdata_t (Type DATA) structure contains or references all type data for
+ * a given file or, during merging, several files.
+ */
+typedef struct tdata {
+ int td_curemark; /* Equality mark (see merge.c) */
+ int td_curvgen; /* Visitation generation (see traverse.c) */
+ int td_nextid; /* The ID for the next tdesc_t created */
+ hash_t *td_iihash; /* The iidesc_t nodes for this file */
+
+ hash_t *td_layouthash; /* The tdesc nodes, hashed by structure */
+ hash_t *td_idhash; /* The tdesc nodes, hashed by type id */
+ list_t *td_fwdlist; /* All forward declaration tdesc nodes */
+
+ char *td_parlabel; /* Top label uniq'd against in parent */
+ char *td_parname; /* Basename of parent */
+ list_t *td_labels; /* Labels and their type ranges */
+
+ pthread_mutex_t td_mergelock;
+
+ int td_ref;
+} tdata_t;
+
+/*
+ * By design, the iidesc hash is heterogeneous. The CTF emitter, on the
+ * other hand, needs to be able to access the elements of the list by type,
+ * and in a specific sorted order. An iiburst holds these elements in that
+ * order. (A burster is a machine that separates carbon-copy forms)
+ */
+typedef struct iiburst {
+ int iib_nfuncs;
+ int iib_curfunc;
+ iidesc_t **iib_funcs;
+
+ int iib_nobjts;
+ int iib_curobjt;
+ iidesc_t **iib_objts;
+
+ list_t *iib_types;
+ int iib_maxtypeid;
+
+ tdata_t *iib_td;
+ struct tdtrav_data *iib_tdtd; /* tdtrav_data_t */
+} iiburst_t;
+
+typedef struct ctf_buf ctf_buf_t;
+
+typedef struct symit_data symit_data_t;
+
+/* fixup_tdescs.c */
+void cvt_fixstabs(tdata_t *);
+void cvt_fixups(tdata_t *, size_t);
+
+/* ctf.c */
+caddr_t ctf_gen(iiburst_t *, size_t *, int);
+tdata_t *ctf_load(char *, caddr_t, size_t, symit_data_t *, char *);
+
+/* iidesc.c */
+iidesc_t *iidesc_new(char *);
+int iidesc_hash(int, void *);
+void iter_iidescs_by_name(tdata_t *, const char *,
+ int (*)(void *, void *), void *);
+iidesc_t *iidesc_dup(iidesc_t *);
+iidesc_t *iidesc_dup_rename(iidesc_t *, char const *, char const *);
+void iidesc_add(hash_t *, iidesc_t *);
+void iidesc_free(void *, void *);
+int iidesc_count_type(void *, void *);
+void iidesc_stats(hash_t *);
+int iidesc_dump(iidesc_t *);
+
+/* input.c */
+typedef enum source_types {
+ SOURCE_NONE = 0,
+ SOURCE_UNKNOWN = 1,
+ SOURCE_C = 2,
+ SOURCE_S = 4
+} source_types_t;
+
+source_types_t built_source_types(Elf *, const char *);
+int count_files(char **, int);
+int read_ctf(char **, int, char *, int (*)(tdata_t *, char *, void *),
+ void *, int);
+int read_ctf_save_cb(tdata_t *, char *, void *);
+symit_data_t *symit_new(Elf *, const char *);
+void symit_reset(symit_data_t *);
+char *symit_curfile(symit_data_t *);
+GElf_Sym *symit_next(symit_data_t *, int);
+char *symit_name(symit_data_t *);
+void symit_free(symit_data_t *);
+
+/* merge.c */
+void merge_into_master(tdata_t *, tdata_t *, tdata_t *, int);
+
+/* output.c */
+#define CTF_FUZZY_MATCH 0x1 /* match local symbols to global CTF */
+#define CTF_USE_DYNSYM 0x2 /* use .dynsym not .symtab */
+#define CTF_COMPRESS 0x4 /* compress CTF output */
+#define CTF_KEEP_STABS 0x8 /* keep .stabs sections */
+
+void write_ctf(tdata_t *, const char *, const char *, int);
+
+/* parse.c */
+void parse_init(tdata_t *);
+void parse_finish(tdata_t *);
+int parse_stab(stab_t *, char *, iidesc_t **);
+tdesc_t *lookup(int);
+tdesc_t *lookupname(const char *);
+void check_hash(void);
+void resolve_typed_bitfields(void);
+
+/* stabs.c */
+int stabs_read(tdata_t *, Elf *, char *);
+
+/* dwarf.c */
+int dw_read(tdata_t *, Elf *, char *);
+const char *dw_tag2str(uint_t);
+
+/* tdata.c */
+tdata_t *tdata_new(void);
+void tdata_free(tdata_t *);
+void tdata_build_hashes(tdata_t *td);
+const char *tdesc_name(tdesc_t *);
+int tdesc_idhash(int, void *);
+int tdesc_idcmp(void *, void *);
+int tdesc_namehash(int, void *);
+int tdesc_namecmp(void *, void *);
+int tdesc_layouthash(int, void *);
+int tdesc_layoutcmp(void *, void *);
+void tdesc_free(tdesc_t *);
+void tdata_label_add(tdata_t *, const char *, int);
+labelent_t *tdata_label_top(tdata_t *);
+int tdata_label_find(tdata_t *, char *);
+void tdata_label_free(tdata_t *);
+void tdata_merge(tdata_t *, tdata_t *);
+void tdata_label_newmax(tdata_t *, int);
+
+/* util.c */
+int streq(const char *, const char *);
+int findelfsecidx(Elf *, const char *, const char *);
+size_t elf_ptrsz(Elf *);
+char *mktmpname(const char *, const char *);
+void terminate(const char *, ...);
+void aborterr(const char *, ...);
+void set_terminate_cleanup(void (*)(void));
+void elfterminate(const char *, const char *, ...);
+void warning(const char *, ...);
+void vadebug(int, const char *, va_list);
+void debug(int, const char *, ...);
+
+
+void watch_dump(int);
+void watch_set(void *, int);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTFTOOLS_H */
diff --git a/cvt/dwarf.c b/cvt/dwarf.c
new file mode 100644
index 0000000..3df3b1a
--- /dev/null
+++ b/cvt/dwarf.c
@@ -0,0 +1,1838 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * DWARF to tdata conversion
+ *
+ * For the most part, conversion is straightforward, proceeding in two passes.
+ * On the first pass, we iterate through every die, creating new type nodes as
+ * necessary. Referenced tdesc_t's are created in an uninitialized state, thus
+ * allowing type reference pointers to be filled in. If the tdesc_t
+ * corresponding to a given die can be completely filled out (sizes and offsets
+ * calculated, and so forth) without using any referenced types, the tdesc_t is
+ * marked as resolved. Consider an array type. If the type corresponding to
+ * the array contents has not yet been processed, we will create a blank tdesc
+ * for the contents type (only the type ID will be filled in, relying upon the
+ * later portion of the first pass to encounter and complete the referenced
+ * type). We will then attempt to determine the size of the array. If the
+ * array has a byte size attribute, we will have completely characterized the
+ * array type, and will be able to mark it as resolved. The lack of a byte
+ * size attribute, on the other hand, will prevent us from fully resolving the
+ * type, as the size will only be calculable with reference to the contents
+ * type, which has not, as yet, been encountered. The array type will thus be
+ * left without the resolved flag, and the first pass will continue.
+ *
+ * When we begin the second pass, we will have created tdesc_t nodes for every
+ * type in the section. We will traverse the tree, from the iidescs down,
+ * processing each unresolved node. As the referenced nodes will have been
+ * populated, the array type used in our example above will be able to use the
+ * size of the referenced types (if available) to determine its own type. The
+ * traversal will be repeated until all types have been resolved or we have
+ * failed to make progress. When all tdescs have been resolved, the conversion
+ * is complete.
+ *
+ * There are, as always, a few special cases that are handled during the first
+ * and second passes:
+ *
+ * 1. Empty enums - GCC will occasionally emit an enum without any members.
+ * Later on in the file, it will emit the same enum type, though this time
+ * with the full complement of members. All references to the memberless
+ * enum need to be redirected to the full definition. During the first
+ * pass, each enum is entered in dm_enumhash, along with a pointer to its
+ * corresponding tdesc_t. If, during the second pass, we encounter a
+ * memberless enum, we use the hash to locate the full definition. All
+ * tdescs referencing the empty enum are then redirected.
+ *
+ * 2. Forward declarations - If the compiler sees a forward declaration for
+ * a structure, followed by the definition of that structure, it will emit
+ * DWARF data for both the forward declaration and the definition. We need
+ * to resolve the forward declarations when possible, by redirecting
+ * forward-referencing tdescs to the actual struct/union definitions. This
+ * redirection is done completely within the first pass. We begin by
+ * recording all forward declarations in dw_fwdhash. When we define a
+ * structure, we check to see if there have been any corresponding forward
+ * declarations. If so, we redirect the tdescs which referenced the forward
+ * declarations to the structure or union definition.
+ *
+ * XXX see if a post traverser will allow the elimination of repeated pass 2
+ * traversals.
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "memory.h"
+#include "common/list.h"
+#include "traverse.h"
+
+/* The version of DWARF which we support. */
+#define DWARF_VERSION 2
+
+/*
+ * We need to define a couple of our own intrinsics, to smooth out some of the
+ * differences between the GCC and DevPro DWARF emitters. See the referenced
+ * routines and the special cases in the file comment for more details.
+ *
+ * Type IDs are 32 bits wide. We're going to use the top of that field to
+ * indicate types that we've created ourselves.
+ */
+#define TID_FILEMAX 0x3fffffff /* highest tid from file */
+#define TID_VOID 0x40000001 /* see die_void() */
+#define TID_LONG 0x40000002 /* see die_array() */
+
+#define TID_MFGTID_BASE 0x40000003 /* first mfg'd tid */
+
+/*
+ * To reduce the staggering amount of error-handling code that would otherwise
+ * be required, the attribute-retrieval routines handle most of their own
+ * errors. If the following flag is supplied as the value of the `req'
+ * argument, they will also handle the absence of a requested attribute by
+ * terminating the program.
+ */
+#define DW_ATTR_REQ 1
+
+#define TDESC_HASH_BUCKETS 511
+
+typedef struct dwarf {
+ Dwarf_Debug dw_dw; /* for libdwarf */
+ Dwarf_Error dw_err; /* for libdwarf */
+ Dwarf_Off dw_maxoff; /* highest legal offset in this cu */
+ tdata_t *dw_td; /* root of the tdesc/iidesc tree */
+ hash_t *dw_tidhash; /* hash of tdescs by t_id */
+ hash_t *dw_fwdhash; /* hash of fwd decls by name */
+ hash_t *dw_enumhash; /* hash of memberless enums by name */
+ tdesc_t *dw_void; /* manufactured void type */
+ tdesc_t *dw_long; /* manufactured long type for arrays */
+ size_t dw_ptrsz; /* size of a pointer in this file */
+ tid_t dw_mfgtid_last; /* last mfg'd type ID used */
+ uint_t dw_nunres; /* count of unresolved types */
+ char *dw_cuname; /* name of compilation unit */
+} dwarf_t;
+
+static void die_create_one(dwarf_t *, Dwarf_Die);
+static void die_create(dwarf_t *, Dwarf_Die);
+
+static tid_t
+mfgtid_next(dwarf_t *dw)
+{
+ return (++dw->dw_mfgtid_last);
+}
+
+static void
+tdesc_add(dwarf_t *dw, tdesc_t *tdp)
+{
+ hash_add(dw->dw_tidhash, tdp);
+}
+
+static tdesc_t *
+tdesc_lookup(dwarf_t *dw, int tid)
+{
+ tdesc_t tmpl;
+ void *tdp;
+
+ tmpl.t_id = tid;
+
+ if (hash_find(dw->dw_tidhash, &tmpl, &tdp))
+ return (tdp);
+ else
+ return (NULL);
+}
+
+/*
+ * Resolve a tdesc down to a node which should have a size. Returns the size,
+ * zero if the size hasn't yet been determined.
+ */
+static size_t
+tdesc_size(tdesc_t *tdp)
+{
+ for (;;) {
+ switch (tdp->t_type) {
+ case INTRINSIC:
+ case POINTER:
+ case ARRAY:
+ case FUNCTION:
+ case STRUCT:
+ case UNION:
+ case ENUM:
+ return (tdp->t_size);
+
+ case FORWARD:
+ return (0);
+
+ case TYPEDEF:
+ case VOLATILE:
+ case CONST:
+ case RESTRICT:
+ tdp = tdp->t_tdesc;
+ continue;
+
+ case 0: /* not yet defined */
+ return (0);
+
+ default:
+ terminate("tdp %u: tdesc_size on unknown type %d\n",
+ tdp->t_id, tdp->t_type);
+ }
+ }
+}
+
+static size_t
+tdesc_bitsize(tdesc_t *tdp)
+{
+ for (;;) {
+ switch (tdp->t_type) {
+ case INTRINSIC:
+ return (tdp->t_intr->intr_nbits);
+
+ case ARRAY:
+ case FUNCTION:
+ case STRUCT:
+ case UNION:
+ case ENUM:
+ case POINTER:
+ return (tdp->t_size * NBBY);
+
+ case FORWARD:
+ return (0);
+
+ case TYPEDEF:
+ case VOLATILE:
+ case RESTRICT:
+ case CONST:
+ tdp = tdp->t_tdesc;
+ continue;
+
+ case 0: /* not yet defined */
+ return (0);
+
+ default:
+ terminate("tdp %u: tdesc_bitsize on unknown type %d\n",
+ tdp->t_id, tdp->t_type);
+ }
+ }
+}
+
+static tdesc_t *
+tdesc_basetype(tdesc_t *tdp)
+{
+ for (;;) {
+ switch (tdp->t_type) {
+ case TYPEDEF:
+ case VOLATILE:
+ case RESTRICT:
+ case CONST:
+ tdp = tdp->t_tdesc;
+ break;
+ case 0: /* not yet defined */
+ return (NULL);
+ default:
+ return (tdp);
+ }
+ }
+}
+
+static Dwarf_Off
+die_off(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Off off;
+
+ if (dwarf_dieoffset(die, &off, &dw->dw_err) == DW_DLV_OK)
+ return (off);
+
+ terminate("failed to get offset for die: %s\n",
+ dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (0);
+}
+
+static Dwarf_Die
+die_sibling(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Die sib;
+ int rc;
+
+ if ((rc = dwarf_siblingof(dw->dw_dw, die, &sib, &dw->dw_err)) ==
+ DW_DLV_OK)
+ return (sib);
+ else if (rc == DW_DLV_NO_ENTRY)
+ return (NULL);
+
+ terminate("die %llu: failed to find type sibling: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (NULL);
+}
+
+static Dwarf_Die
+die_child(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Die child;
+ int rc;
+
+ if ((rc = dwarf_child(die, &child, &dw->dw_err)) == DW_DLV_OK)
+ return (child);
+ else if (rc == DW_DLV_NO_ENTRY)
+ return (NULL);
+
+ terminate("die %llu: failed to find type child: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (NULL);
+}
+
+static Dwarf_Half
+die_tag(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Half tag;
+
+ if (dwarf_tag(die, &tag, &dw->dw_err) == DW_DLV_OK)
+ return (tag);
+
+ terminate("die %llu: failed to get tag for type: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (0);
+}
+
+static Dwarf_Attribute
+die_attr(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, int req)
+{
+ Dwarf_Attribute attr;
+ int rc;
+
+ if ((rc = dwarf_attr(die, name, &attr, &dw->dw_err)) == DW_DLV_OK) {
+ return (attr);
+ } else if (rc == DW_DLV_NO_ENTRY) {
+ if (req) {
+ terminate("die %llu: no attr 0x%x\n", die_off(dw, die),
+ name);
+ } else {
+ return (NULL);
+ }
+ }
+
+ terminate("die %llu: failed to get attribute for type: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ /*NOTREACHED*/
+ return (NULL);
+}
+
+static int
+die_signed(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Signed *valp,
+ int req)
+{
+ *valp = 0;
+ if (dwarf_attrval_signed(die, name, valp, &dw->dw_err) != DWARF_E_NONE) {
+ if (req)
+ terminate("die %llu: failed to get signed: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+die_unsigned(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Unsigned *valp,
+ int req)
+{
+ *valp = 0;
+ if (dwarf_attrval_unsigned(die, name, valp, &dw->dw_err) != DWARF_E_NONE) {
+ if (req)
+ terminate("die %llu: failed to get unsigned: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+die_bool(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, Dwarf_Bool *valp, int req)
+{
+ *valp = 0;
+
+ if (dwarf_attrval_flag(die, name, valp, &dw->dw_err) != DWARF_E_NONE) {
+ if (req)
+ terminate("die %llu: failed to get flag: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+die_string(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name, char **strp, int req)
+{
+ const char *str = NULL;
+
+ if (dwarf_attrval_string(die, name, &str, &dw->dw_err) != DWARF_E_NONE ||
+ str == NULL) {
+ if (req)
+ terminate("die %llu: failed to get string: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ else
+ *strp = NULL;
+ return (0);
+ } else
+ *strp = xstrdup(str);
+
+ return (1);
+}
+
+static Dwarf_Off
+die_attr_ref(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name)
+{
+ Dwarf_Off off;
+
+ if (dwarf_attrval_unsigned(die, name, &off, &dw->dw_err) != DWARF_E_NONE) {
+ terminate("die %llu: failed to get ref: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+ }
+
+ return (off);
+}
+
+static char *
+die_name(dwarf_t *dw, Dwarf_Die die)
+{
+ char *str = NULL;
+
+ (void) die_string(dw, die, DW_AT_name, &str, 0);
+
+ return (str);
+}
+
+static int
+die_isdecl(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Bool val;
+
+ return (die_bool(dw, die, DW_AT_declaration, &val, 0) && val);
+}
+
+static int
+die_isglobal(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Signed vis;
+ Dwarf_Bool ext;
+
+ /*
+ * Some compilers (gcc) use DW_AT_external to indicate function
+ * visibility. Others (Sun) use DW_AT_visibility.
+ */
+ if (die_signed(dw, die, DW_AT_visibility, &vis, 0))
+ return (vis == DW_VIS_exported);
+ else
+ return (die_bool(dw, die, DW_AT_external, &ext, 0) && ext);
+}
+
+static tdesc_t *
+die_add(dwarf_t *dw, Dwarf_Off off)
+{
+ tdesc_t *tdp = xcalloc(sizeof (tdesc_t));
+
+ tdp->t_id = off;
+
+ tdesc_add(dw, tdp);
+
+ return (tdp);
+}
+
+static tdesc_t *
+die_lookup_pass1(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name)
+{
+ Dwarf_Off ref = die_attr_ref(dw, die, name);
+ tdesc_t *tdp;
+
+ if ((tdp = tdesc_lookup(dw, ref)) != NULL)
+ return (tdp);
+
+ return (die_add(dw, ref));
+}
+
+static int
+die_mem_offset(dwarf_t *dw, Dwarf_Die die, Dwarf_Half name,
+ Dwarf_Unsigned *valp, int req __unused)
+{
+ Dwarf_Locdesc *loc = NULL;
+ Dwarf_Signed locnum = 0;
+
+ if (dwarf_locdesc(die, name, &loc, &locnum, &dw->dw_err) != DW_DLV_OK)
+ return (0);
+
+ if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) {
+ terminate("die %llu: cannot parse member offset\n",
+ die_off(dw, die));
+ }
+
+ *valp = loc->ld_s->lr_number;
+
+ if (loc != NULL)
+ if (dwarf_locdesc_free(loc, &dw->dw_err) != DW_DLV_OK)
+ terminate("die %llu: cannot free location descriptor: %s\n",
+ die_off(dw, die), dwarf_errmsg(&dw->dw_err));
+
+ return (1);
+}
+
+static tdesc_t *
+tdesc_intr_common(dwarf_t *dw, int tid, const char *name, size_t sz)
+{
+ tdesc_t *tdp;
+ intr_t *intr;
+
+ intr = xcalloc(sizeof (intr_t));
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = 1;
+ intr->intr_nbits = sz * NBBY;
+
+ tdp = xcalloc(sizeof (tdesc_t));
+ tdp->t_name = xstrdup(name);
+ tdp->t_size = sz;
+ tdp->t_id = tid;
+ tdp->t_type = INTRINSIC;
+ tdp->t_intr = intr;
+ tdp->t_flags = TDESC_F_RESOLVED;
+
+ tdesc_add(dw, tdp);
+
+ return (tdp);
+}
+
+/*
+ * Manufacture a void type. Used for gcc-emitted stabs, where the lack of a
+ * type reference implies a reference to a void type. A void *, for example
+ * will be represented by a pointer die without a DW_AT_type. CTF requires
+ * that pointer nodes point to something, so we'll create a void for use as
+ * the target. Note that the DWARF data may already create a void type. Ours
+ * would then be a duplicate, but it'll be removed in the self-uniquification
+ * merge performed at the completion of DWARF->tdesc conversion.
+ */
+static tdesc_t *
+tdesc_intr_void(dwarf_t *dw)
+{
+ if (dw->dw_void == NULL)
+ dw->dw_void = tdesc_intr_common(dw, TID_VOID, "void", 0);
+
+ return (dw->dw_void);
+}
+
+static tdesc_t *
+tdesc_intr_long(dwarf_t *dw)
+{
+ if (dw->dw_long == NULL) {
+ dw->dw_long = tdesc_intr_common(dw, TID_LONG, "long",
+ dw->dw_ptrsz);
+ }
+
+ return (dw->dw_long);
+}
+
+/*
+ * Used for creating bitfield types. We create a copy of an existing intrinsic,
+ * adjusting the size of the copy to match what the caller requested. The
+ * caller can then use the copy as the type for a bitfield structure member.
+ */
+static tdesc_t *
+tdesc_intr_clone(dwarf_t *dw, tdesc_t *old, size_t bitsz)
+{
+ tdesc_t *new = xcalloc(sizeof (tdesc_t));
+
+ if (!(old->t_flags & TDESC_F_RESOLVED)) {
+ terminate("tdp %u: attempt to make a bit field from an "
+ "unresolved type\n", old->t_id);
+ }
+
+ new->t_name = xstrdup(old->t_name);
+ new->t_size = old->t_size;
+ new->t_id = mfgtid_next(dw);
+ new->t_type = INTRINSIC;
+ new->t_flags = TDESC_F_RESOLVED;
+
+ new->t_intr = xcalloc(sizeof (intr_t));
+ bcopy(old->t_intr, new->t_intr, sizeof (intr_t));
+ new->t_intr->intr_nbits = bitsz;
+
+ tdesc_add(dw, new);
+
+ return (new);
+}
+
+static void
+tdesc_array_create(dwarf_t *dw, Dwarf_Die dim, tdesc_t *arrtdp,
+ tdesc_t *dimtdp)
+{
+ Dwarf_Unsigned uval;
+ Dwarf_Signed sval;
+ tdesc_t *ctdp = NULL;
+ Dwarf_Die dim2;
+ ardef_t *ar;
+
+ if ((dim2 = die_sibling(dw, dim)) == NULL) {
+ ctdp = arrtdp;
+ } else if (die_tag(dw, dim2) == DW_TAG_subrange_type) {
+ ctdp = xcalloc(sizeof (tdesc_t));
+ ctdp->t_id = mfgtid_next(dw);
+ debug(3, "die %llu: creating new type %u for sub-dimension\n",
+ die_off(dw, dim2), ctdp->t_id);
+ tdesc_array_create(dw, dim2, arrtdp, ctdp);
+ } else {
+ terminate("die %llu: unexpected non-subrange node in array\n",
+ die_off(dw, dim2));
+ }
+
+ dimtdp->t_type = ARRAY;
+ dimtdp->t_ardef = ar = xcalloc(sizeof (ardef_t));
+
+ /*
+ * Array bounds can be signed or unsigned, but there are several kinds
+ * of signless forms (data1, data2, etc) that take their sign from the
+ * routine that is trying to interpret them. That is, data1 can be
+ * either signed or unsigned, depending on whether you use the signed or
+ * unsigned accessor function. GCC will use the signless forms to store
+ * unsigned values which have their high bit set, so we need to try to
+ * read them first as unsigned to get positive values. We could also
+ * try signed first, falling back to unsigned if we got a negative
+ * value.
+ */
+ if (die_unsigned(dw, dim, DW_AT_upper_bound, &uval, 0))
+ ar->ad_nelems = uval + 1;
+ else if (die_signed(dw, dim, DW_AT_upper_bound, &sval, 0))
+ ar->ad_nelems = sval + 1;
+ else
+ ar->ad_nelems = 0;
+
+ /*
+ * Different compilers use different index types. Force the type to be
+ * a common, known value (long).
+ */
+ ar->ad_idxtype = tdesc_intr_long(dw);
+ ar->ad_contents = ctdp;
+
+ if (ar->ad_contents->t_size != 0) {
+ dimtdp->t_size = ar->ad_contents->t_size * ar->ad_nelems;
+ dimtdp->t_flags |= TDESC_F_RESOLVED;
+ }
+}
+
+/*
+ * Create a tdesc from an array node. Some arrays will come with byte size
+ * attributes, and thus can be resolved immediately. Others don't, and will
+ * need to wait until the second pass for resolution.
+ */
+static void
+die_array_create(dwarf_t *dw, Dwarf_Die arr, Dwarf_Off off, tdesc_t *tdp)
+{
+ tdesc_t *arrtdp = die_lookup_pass1(dw, arr, DW_AT_type);
+ Dwarf_Unsigned uval;
+ Dwarf_Die dim;
+
+ debug(3, "die %llu <%llx>: creating array\n", off, off);
+
+ if ((dim = die_child(dw, arr)) == NULL ||
+ die_tag(dw, dim) != DW_TAG_subrange_type)
+ terminate("die %llu: failed to retrieve array bounds\n", off);
+
+ tdesc_array_create(dw, dim, arrtdp, tdp);
+
+ if (die_unsigned(dw, arr, DW_AT_byte_size, &uval, 0)) {
+ tdesc_t *dimtdp;
+ int flags;
+
+ tdp->t_size = uval;
+
+ /*
+ * Ensure that sub-dimensions have sizes too before marking
+ * as resolved.
+ */
+ flags = TDESC_F_RESOLVED;
+ for (dimtdp = tdp->t_ardef->ad_contents;
+ dimtdp->t_type == ARRAY;
+ dimtdp = dimtdp->t_ardef->ad_contents) {
+ if (!(dimtdp->t_flags & TDESC_F_RESOLVED)) {
+ flags = 0;
+ break;
+ }
+ }
+
+ tdp->t_flags |= flags;
+ }
+
+ debug(3, "die %llu <%llx>: array nelems %u size %u\n", off, off,
+ tdp->t_ardef->ad_nelems, tdp->t_size);
+}
+
+/*ARGSUSED1*/
+static int
+die_array_resolve(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private)
+{
+ dwarf_t *dw = private;
+ size_t sz;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ debug(3, "trying to resolve array %d (cont %d)\n", tdp->t_id,
+ tdp->t_ardef->ad_contents->t_id);
+
+ if ((sz = tdesc_size(tdp->t_ardef->ad_contents)) == 0) {
+ debug(3, "unable to resolve array %s (%d) contents %d\n",
+ tdesc_name(tdp), tdp->t_id,
+ tdp->t_ardef->ad_contents->t_id);
+
+ dw->dw_nunres++;
+ return (1);
+ }
+
+ tdp->t_size = sz * tdp->t_ardef->ad_nelems;
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ debug(3, "resolved array %d: %u bytes\n", tdp->t_id, tdp->t_size);
+
+ return (1);
+}
+
+/*ARGSUSED1*/
+static int
+die_array_failed(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private __unused)
+{
+ tdesc_t *cont = tdp->t_ardef->ad_contents;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ fprintf(stderr, "Array %d: failed to size contents type %s (%d)\n",
+ tdp->t_id, tdesc_name(cont), cont->t_id);
+
+ return (1);
+}
+
+/*
+ * Most enums (those with members) will be resolved during this first pass.
+ * Others - those without members (see the file comment) - won't be, and will
+ * need to wait until the second pass when they can be matched with their full
+ * definitions.
+ */
+static void
+die_enum_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ Dwarf_Die mem;
+ Dwarf_Unsigned uval;
+ Dwarf_Signed sval;
+
+ debug(3, "die %llu: creating enum\n", off);
+
+ tdp->t_type = ENUM;
+
+ (void) die_unsigned(dw, die, DW_AT_byte_size, &uval, DW_ATTR_REQ);
+ tdp->t_size = uval;
+
+ if ((mem = die_child(dw, die)) != NULL) {
+ elist_t **elastp = &tdp->t_emem;
+
+ do {
+ elist_t *el;
+
+ if (die_tag(dw, mem) != DW_TAG_enumerator) {
+ /* Nested type declaration */
+ die_create_one(dw, mem);
+ continue;
+ }
+
+ el = xcalloc(sizeof (elist_t));
+ el->el_name = die_name(dw, mem);
+
+ if (die_signed(dw, mem, DW_AT_const_value, &sval, 0)) {
+ el->el_number = sval;
+ } else if (die_unsigned(dw, mem, DW_AT_const_value,
+ &uval, 0)) {
+ el->el_number = uval;
+ } else {
+ terminate("die %llu: enum %llu: member without "
+ "value\n", off, die_off(dw, mem));
+ }
+
+ debug(3, "die %llu: enum %llu: created %s = %d\n", off,
+ die_off(dw, mem), el->el_name, el->el_number);
+
+ *elastp = el;
+ elastp = &el->el_next;
+
+ } while ((mem = die_sibling(dw, mem)) != NULL);
+
+ hash_add(dw->dw_enumhash, tdp);
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ if (tdp->t_name != NULL) {
+ iidesc_t *ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = II_SOU;
+ ii->ii_name = xstrdup(tdp->t_name);
+ ii->ii_dtype = tdp;
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+ }
+ }
+}
+
+static int
+die_enum_match(void *arg1, void *arg2)
+{
+ tdesc_t *tdp = arg1, **fullp = arg2;
+
+ if (tdp->t_emem != NULL) {
+ *fullp = tdp;
+ return (-1); /* stop the iteration */
+ }
+
+ return (0);
+}
+
+/*ARGSUSED1*/
+static int
+die_enum_resolve(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private)
+{
+ dwarf_t *dw = private;
+ tdesc_t *full = NULL;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ (void) hash_find_iter(dw->dw_enumhash, tdp, die_enum_match, &full);
+
+ /*
+ * The answer to this one won't change from iteration to iteration,
+ * so don't even try.
+ */
+ if (full == NULL) {
+ terminate("tdp %u: enum %s has no members\n", tdp->t_id,
+ tdesc_name(tdp));
+ }
+
+ debug(3, "tdp %u: enum %s redirected to %u\n", tdp->t_id,
+ tdesc_name(tdp), full->t_id);
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ return (1);
+}
+
+static int
+die_fwd_map(void *arg1, void *arg2)
+{
+ tdesc_t *fwd = arg1, *sou = arg2;
+
+ debug(3, "tdp %u: mapped forward %s to sou %u\n", fwd->t_id,
+ tdesc_name(fwd), sou->t_id);
+ fwd->t_tdesc = sou;
+
+ return (0);
+}
+
+/*
+ * Structures and unions will never be resolved during the first pass, as we
+ * won't be able to fully determine the member sizes. The second pass, which
+ * have access to sizing information, will be able to complete the resolution.
+ */
+static void
+die_sou_create(dwarf_t *dw, Dwarf_Die str, Dwarf_Off off, tdesc_t *tdp,
+ int type, const char *typename)
+{
+ Dwarf_Unsigned sz, bitsz, bitoff;
+ Dwarf_Die mem;
+ mlist_t *ml, **mlastp;
+ iidesc_t *ii;
+
+ tdp->t_type = (die_isdecl(dw, str) ? FORWARD : type);
+
+ debug(3, "die %llu: creating %s %s\n", off,
+ (tdp->t_type == FORWARD ? "forward decl" : typename),
+ tdesc_name(tdp));
+
+ if (tdp->t_type == FORWARD) {
+ hash_add(dw->dw_fwdhash, tdp);
+ return;
+ }
+
+ (void) hash_find_iter(dw->dw_fwdhash, tdp, die_fwd_map, tdp);
+
+ (void) die_unsigned(dw, str, DW_AT_byte_size, &sz, DW_ATTR_REQ);
+ tdp->t_size = sz;
+
+ /*
+ * GCC allows empty SOUs as an extension.
+ */
+ if ((mem = die_child(dw, str)) == NULL) {
+ goto out;
+ }
+
+ mlastp = &tdp->t_members;
+
+ do {
+ Dwarf_Off memoff = die_off(dw, mem);
+ Dwarf_Half tag = die_tag(dw, mem);
+ Dwarf_Unsigned mloff;
+
+ if (tag != DW_TAG_member) {
+ /* Nested type declaration */
+ die_create_one(dw, mem);
+ continue;
+ }
+
+ debug(3, "die %llu: mem %llu: creating member\n", off, memoff);
+
+ ml = xcalloc(sizeof (mlist_t));
+
+ /*
+ * This could be a GCC anon struct/union member, so we'll allow
+ * an empty name, even though nothing can really handle them
+ * properly. Note that some versions of GCC miss out debug
+ * info for anon structs, though recent versions are fixed (gcc
+ * bug 11816).
+ */
+ if ((ml->ml_name = die_name(dw, mem)) == NULL)
+ ml->ml_name = NULL;
+
+ ml->ml_type = die_lookup_pass1(dw, mem, DW_AT_type);
+
+ if (die_mem_offset(dw, mem, DW_AT_data_member_location,
+ &mloff, 0)) {
+ debug(3, "die %llu: got mloff %llx\n", off,
+ (u_longlong_t)mloff);
+ ml->ml_offset = mloff * 8;
+ }
+
+ if (die_unsigned(dw, mem, DW_AT_bit_size, &bitsz, 0))
+ ml->ml_size = bitsz;
+ else
+ ml->ml_size = tdesc_bitsize(ml->ml_type);
+
+ if (die_unsigned(dw, mem, DW_AT_bit_offset, &bitoff, 0)) {
+#if BYTE_ORDER == _BIG_ENDIAN
+ ml->ml_offset += bitoff;
+#else
+ ml->ml_offset += tdesc_bitsize(ml->ml_type) - bitoff -
+ ml->ml_size;
+#endif
+ }
+
+ debug(3, "die %llu: mem %llu: created \"%s\" (off %u sz %u)\n",
+ off, memoff, ml->ml_name, ml->ml_offset, ml->ml_size);
+
+ *mlastp = ml;
+ mlastp = &ml->ml_next;
+ } while ((mem = die_sibling(dw, mem)) != NULL);
+
+ /*
+ * GCC will attempt to eliminate unused types, thus decreasing the
+ * size of the emitted dwarf. That is, if you declare a foo_t in your
+ * header, include said header in your source file, and neglect to
+ * actually use (directly or indirectly) the foo_t in the source file,
+ * the foo_t won't make it into the emitted DWARF. So, at least, goes
+ * the theory.
+ *
+ * Occasionally, it'll emit the DW_TAG_structure_type for the foo_t,
+ * and then neglect to emit the members. Strangely, the loner struct
+ * tag will always be followed by a proper nested declaration of
+ * something else. This is clearly a bug, but we're not going to have
+ * time to get it fixed before this goo goes back, so we'll have to work
+ * around it. If we see a no-membered struct with a nested declaration
+ * (i.e. die_child of the struct tag won't be null), we'll ignore it.
+ * Being paranoid, we won't simply remove it from the hash. Instead,
+ * we'll decline to create an iidesc for it, thus ensuring that this
+ * type won't make it into the output file. To be safe, we'll also
+ * change the name.
+ */
+ if (tdp->t_members == NULL) {
+ const char *old = tdesc_name(tdp);
+ size_t newsz = 7 + strlen(old) + 1;
+ char *new = xmalloc(newsz);
+ (void) snprintf(new, newsz, "orphan %s", old);
+
+ debug(3, "die %llu: worked around %s %s\n", off, typename, old);
+
+ if (tdp->t_name != NULL)
+ free(tdp->t_name);
+ tdp->t_name = new;
+ return;
+ }
+
+out:
+ if (tdp->t_name != NULL) {
+ ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = II_SOU;
+ ii->ii_name = xstrdup(tdp->t_name);
+ ii->ii_dtype = tdp;
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+ }
+}
+
+static void
+die_struct_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_sou_create(dw, die, off, tdp, STRUCT, "struct");
+}
+
+static void
+die_union_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_sou_create(dw, die, off, tdp, UNION, "union");
+}
+
+/*ARGSUSED1*/
+static int
+die_sou_resolve(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private)
+{
+ dwarf_t *dw = private;
+ mlist_t *ml;
+ tdesc_t *mt;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ debug(3, "resolving sou %s\n", tdesc_name(tdp));
+
+ for (ml = tdp->t_members; ml != NULL; ml = ml->ml_next) {
+ if (ml->ml_size == 0) {
+ mt = tdesc_basetype(ml->ml_type);
+
+ if ((ml->ml_size = tdesc_bitsize(mt)) != 0)
+ continue;
+
+ /*
+ * For empty members, or GCC/C99 flexible array
+ * members, a size of 0 is correct.
+ */
+ if (mt->t_members == NULL)
+ continue;
+ if (mt->t_type == ARRAY && mt->t_ardef->ad_nelems == 0)
+ continue;
+
+ dw->dw_nunres++;
+ return (1);
+ }
+
+ if ((mt = tdesc_basetype(ml->ml_type)) == NULL) {
+ dw->dw_nunres++;
+ return (1);
+ }
+
+ if (ml->ml_size != 0 && mt->t_type == INTRINSIC &&
+ mt->t_intr->intr_nbits != ml->ml_size) {
+ /*
+ * This member is a bitfield, and needs to reference
+ * an intrinsic type with the same width. If the
+ * currently-referenced type isn't of the same width,
+ * we'll copy it, adjusting the width of the copy to
+ * the size we'd like.
+ */
+ debug(3, "tdp %u: creating bitfield for %d bits\n",
+ tdp->t_id, ml->ml_size);
+
+ ml->ml_type = tdesc_intr_clone(dw, mt, ml->ml_size);
+ }
+ }
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ return (1);
+}
+
+/*ARGSUSED1*/
+static int
+die_sou_failed(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private __unused)
+{
+ const char *typename = (tdp->t_type == STRUCT ? "struct" : "union");
+ mlist_t *ml;
+
+ if (tdp->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ for (ml = tdp->t_members; ml != NULL; ml = ml->ml_next) {
+ if (ml->ml_size == 0) {
+ fprintf(stderr, "%s %d <%x>: failed to size member \"%s\" "
+ "of type %s (%d <%x>)\n", typename, tdp->t_id,
+ tdp->t_id,
+ ml->ml_name, tdesc_name(ml->ml_type),
+ ml->ml_type->t_id, ml->ml_type->t_id);
+ }
+ }
+
+ return (1);
+}
+
+static void
+die_funcptr_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ Dwarf_Attribute attr;
+ Dwarf_Half tag;
+ Dwarf_Die arg;
+ fndef_t *fn;
+ int i;
+
+ debug(3, "die %llu <%llx>: creating function pointer\n", off, off);
+
+ /*
+ * We'll begin by processing any type definition nodes that may be
+ * lurking underneath this one.
+ */
+ for (arg = die_child(dw, die); arg != NULL;
+ arg = die_sibling(dw, arg)) {
+ if ((tag = die_tag(dw, arg)) != DW_TAG_formal_parameter &&
+ tag != DW_TAG_unspecified_parameters) {
+ /* Nested type declaration */
+ die_create_one(dw, arg);
+ }
+ }
+
+ if (die_isdecl(dw, die)) {
+ /*
+ * This is a prototype. We don't add prototypes to the
+ * tree, so we're going to drop the tdesc. Unfortunately,
+ * it has already been added to the tree. Nobody will reference
+ * it, though, and it will be leaked.
+ */
+ return;
+ }
+
+ fn = xcalloc(sizeof (fndef_t));
+
+ tdp->t_type = FUNCTION;
+
+ if ((attr = die_attr(dw, die, DW_AT_type, 0)) != NULL) {
+ fn->fn_ret = die_lookup_pass1(dw, die, DW_AT_type);
+ } else {
+ fn->fn_ret = tdesc_intr_void(dw);
+ }
+
+ /*
+ * Count the arguments to the function, then read them in.
+ */
+ for (fn->fn_nargs = 0, arg = die_child(dw, die); arg != NULL;
+ arg = die_sibling(dw, arg)) {
+ if ((tag = die_tag(dw, arg)) == DW_TAG_formal_parameter)
+ fn->fn_nargs++;
+ else if (tag == DW_TAG_unspecified_parameters &&
+ fn->fn_nargs > 0)
+ fn->fn_vargs = 1;
+ }
+
+ if (fn->fn_nargs != 0) {
+ debug(3, "die %llu: adding %d argument%s\n", off, fn->fn_nargs,
+ (fn->fn_nargs > 1 ? "s" : ""));
+
+ fn->fn_args = xcalloc(sizeof (tdesc_t *) * fn->fn_nargs);
+ for (i = 0, arg = die_child(dw, die);
+ arg != NULL && i < (int) fn->fn_nargs;
+ arg = die_sibling(dw, arg)) {
+ if (die_tag(dw, arg) != DW_TAG_formal_parameter)
+ continue;
+
+ fn->fn_args[i++] = die_lookup_pass1(dw, arg,
+ DW_AT_type);
+ }
+ }
+
+ tdp->t_fndef = fn;
+ tdp->t_flags |= TDESC_F_RESOLVED;
+}
+
+/*
+ * GCC and DevPro use different names for the base types. While the terms are
+ * the same, they are arranged in a different order. Some terms, such as int,
+ * are implied in one, and explicitly named in the other. Given a base type
+ * as input, this routine will return a common name, along with an intr_t
+ * that reflects said name.
+ */
+static intr_t *
+die_base_name_parse(const char *name, char **newp)
+{
+ char buf[100];
+ char const *base;
+ char *c;
+ int nlong = 0, nshort = 0, nchar = 0, nint = 0;
+ int sign = 1;
+ char fmt = '\0';
+ intr_t *intr;
+
+ if (strlen(name) > sizeof (buf) - 1)
+ terminate("base type name \"%s\" is too long\n", name);
+
+ strncpy(buf, name, sizeof (buf));
+
+ for (c = strtok(buf, " "); c != NULL; c = strtok(NULL, " ")) {
+ if (strcmp(c, "signed") == 0)
+ sign = 1;
+ else if (strcmp(c, "unsigned") == 0)
+ sign = 0;
+ else if (strcmp(c, "long") == 0)
+ nlong++;
+ else if (strcmp(c, "char") == 0) {
+ nchar++;
+ fmt = 'c';
+ } else if (strcmp(c, "short") == 0)
+ nshort++;
+ else if (strcmp(c, "int") == 0)
+ nint++;
+ else {
+ /*
+ * If we don't recognize any of the tokens, we'll tell
+ * the caller to fall back to the dwarf-provided
+ * encoding information.
+ */
+ return (NULL);
+ }
+ }
+
+ if (nchar > 1 || nshort > 1 || nint > 1 || nlong > 2)
+ return (NULL);
+
+ if (nchar > 0) {
+ if (nlong > 0 || nshort > 0 || nint > 0)
+ return (NULL);
+
+ base = "char";
+
+ } else if (nshort > 0) {
+ if (nlong > 0)
+ return (NULL);
+
+ base = "short";
+
+ } else if (nlong > 0) {
+ base = "long";
+
+ } else {
+ base = "int";
+ }
+
+ intr = xcalloc(sizeof (intr_t));
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = sign;
+ intr->intr_iformat = fmt;
+
+ snprintf(buf, sizeof (buf), "%s%s%s",
+ (sign ? "" : "unsigned "),
+ (nlong > 1 ? "long " : ""),
+ base);
+
+ *newp = xstrdup(buf);
+ return (intr);
+}
+
+typedef struct fp_size_map {
+ size_t fsm_typesz[2]; /* size of {32,64} type */
+ uint_t fsm_enc[3]; /* CTF_FP_* for {bare,cplx,imagry} type */
+} fp_size_map_t;
+
+static const fp_size_map_t fp_encodings[] = {
+ { { 4, 4 }, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } },
+ { { 8, 8 }, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } },
+#ifdef __sparc
+ { { 16, 16 }, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } },
+#else
+ { { 12, 16 }, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } },
+#endif
+ { { 0, 0 }, { 0, 0, 0 } }
+};
+
+static uint_t
+die_base_type2enc(dwarf_t *dw, Dwarf_Off off, Dwarf_Signed enc, size_t sz)
+{
+ const fp_size_map_t *map = fp_encodings;
+ uint_t szidx = dw->dw_ptrsz == sizeof (uint64_t);
+ uint_t mult = 1, col = 0;
+
+ if (enc == DW_ATE_complex_float) {
+ mult = 2;
+ col = 1;
+ } else if (enc == DW_ATE_imaginary_float
+#if defined(sun)
+ || enc == DW_ATE_SUN_imaginary_float
+#endif
+ )
+ col = 2;
+
+ while (map->fsm_typesz[szidx] != 0) {
+ if (map->fsm_typesz[szidx] * mult == sz)
+ return (map->fsm_enc[col]);
+ map++;
+ }
+
+ terminate("die %llu: unrecognized real type size %u\n", off, sz);
+ /*NOTREACHED*/
+ return (0);
+}
+
+static intr_t *
+die_base_from_dwarf(dwarf_t *dw, Dwarf_Die base, Dwarf_Off off, size_t sz)
+{
+ intr_t *intr = xcalloc(sizeof (intr_t));
+ Dwarf_Signed enc;
+
+ (void) die_signed(dw, base, DW_AT_encoding, &enc, DW_ATTR_REQ);
+
+ switch (enc) {
+ case DW_ATE_unsigned:
+ case DW_ATE_address:
+ intr->intr_type = INTR_INT;
+ break;
+ case DW_ATE_unsigned_char:
+ intr->intr_type = INTR_INT;
+ intr->intr_iformat = 'c';
+ break;
+ case DW_ATE_signed:
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = 1;
+ break;
+ case DW_ATE_signed_char:
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = 1;
+ intr->intr_iformat = 'c';
+ break;
+ case DW_ATE_boolean:
+ intr->intr_type = INTR_INT;
+ intr->intr_signed = 1;
+ intr->intr_iformat = 'b';
+ break;
+ case DW_ATE_float:
+ case DW_ATE_complex_float:
+ case DW_ATE_imaginary_float:
+#if defined(sun)
+ case DW_ATE_SUN_imaginary_float:
+ case DW_ATE_SUN_interval_float:
+#endif
+ intr->intr_type = INTR_REAL;
+ intr->intr_signed = 1;
+ intr->intr_fformat = die_base_type2enc(dw, off, enc, sz);
+ break;
+ default:
+ terminate("die %llu: unknown base type encoding 0x%llx\n",
+ off, enc);
+ }
+
+ return (intr);
+}
+
+static void
+die_base_create(dwarf_t *dw, Dwarf_Die base, Dwarf_Off off, tdesc_t *tdp)
+{
+ Dwarf_Unsigned sz;
+ intr_t *intr;
+ char *new;
+
+ debug(3, "die %llu: creating base type\n", off);
+
+ /*
+ * The compilers have their own clever (internally inconsistent) ideas
+ * as to what base types should look like. Some times gcc will, for
+ * example, use DW_ATE_signed_char for char. Other times, however, it
+ * will use DW_ATE_signed. Needless to say, this causes some problems
+ * down the road, particularly with merging. We do, however, use the
+ * DWARF idea of type sizes, as this allows us to avoid caring about
+ * the data model.
+ */
+ (void) die_unsigned(dw, base, DW_AT_byte_size, &sz, DW_ATTR_REQ);
+
+ if (tdp->t_name == NULL)
+ terminate("die %llu: base type without name\n", off);
+
+ /* XXX make a name parser for float too */
+ if ((intr = die_base_name_parse(tdp->t_name, &new)) != NULL) {
+ /* Found it. We'll use the parsed version */
+ debug(3, "die %llu: name \"%s\" remapped to \"%s\"\n", off,
+ tdesc_name(tdp), new);
+
+ free(tdp->t_name);
+ tdp->t_name = new;
+ } else {
+ /*
+ * We didn't recognize the type, so we'll create an intr_t
+ * based on the DWARF data.
+ */
+ debug(3, "die %llu: using dwarf data for base \"%s\"\n", off,
+ tdesc_name(tdp));
+
+ intr = die_base_from_dwarf(dw, base, off, sz);
+ }
+
+ intr->intr_nbits = sz * 8;
+
+ tdp->t_type = INTRINSIC;
+ tdp->t_intr = intr;
+ tdp->t_size = sz;
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+}
+
+static void
+die_through_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp,
+ int type, const char *typename)
+{
+ Dwarf_Attribute attr;
+
+ debug(3, "die %llu <%llx>: creating %s type %d\n", off, off, typename, type);
+
+ tdp->t_type = type;
+
+ if ((attr = die_attr(dw, die, DW_AT_type, 0)) != NULL) {
+ tdp->t_tdesc = die_lookup_pass1(dw, die, DW_AT_type);
+ } else {
+ tdp->t_tdesc = tdesc_intr_void(dw);
+ }
+
+ if (type == POINTER)
+ tdp->t_size = dw->dw_ptrsz;
+
+ tdp->t_flags |= TDESC_F_RESOLVED;
+
+ if (type == TYPEDEF) {
+ iidesc_t *ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = II_TYPE;
+ ii->ii_name = xstrdup(tdp->t_name);
+ ii->ii_dtype = tdp;
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+ }
+}
+
+static void
+die_typedef_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, TYPEDEF, "typedef");
+}
+
+static void
+die_const_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, CONST, "const");
+}
+
+static void
+die_pointer_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, POINTER, "pointer");
+}
+
+static void
+die_restrict_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, RESTRICT, "restrict");
+}
+
+static void
+die_volatile_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp)
+{
+ die_through_create(dw, die, off, tdp, VOLATILE, "volatile");
+}
+
+/*ARGSUSED3*/
+static void
+die_function_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp __unused)
+{
+ Dwarf_Die arg;
+ Dwarf_Half tag;
+ iidesc_t *ii;
+ char *name;
+
+ debug(3, "die %llu <%llx>: creating function definition\n", off, off);
+
+ /*
+ * We'll begin by processing any type definition nodes that may be
+ * lurking underneath this one.
+ */
+ for (arg = die_child(dw, die); arg != NULL;
+ arg = die_sibling(dw, arg)) {
+ if ((tag = die_tag(dw, arg)) != DW_TAG_formal_parameter &&
+ tag != DW_TAG_variable) {
+ /* Nested type declaration */
+ die_create_one(dw, arg);
+ }
+ }
+
+ if (die_isdecl(dw, die) || (name = die_name(dw, die)) == NULL) {
+ /*
+ * We process neither prototypes nor subprograms without
+ * names.
+ */
+ return;
+ }
+
+ ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = die_isglobal(dw, die) ? II_GFUN : II_SFUN;
+ ii->ii_name = name;
+ if (ii->ii_type == II_SFUN)
+ ii->ii_owner = xstrdup(dw->dw_cuname);
+
+ debug(3, "die %llu: function %s is %s\n", off, ii->ii_name,
+ (ii->ii_type == II_GFUN ? "global" : "static"));
+
+ if (die_attr(dw, die, DW_AT_type, 0) != NULL)
+ ii->ii_dtype = die_lookup_pass1(dw, die, DW_AT_type);
+ else
+ ii->ii_dtype = tdesc_intr_void(dw);
+
+ for (arg = die_child(dw, die); arg != NULL;
+ arg = die_sibling(dw, arg)) {
+ char *name1;
+
+ debug(3, "die %llu: looking at sub member at %llu\n",
+ off, die_off(dw, die));
+
+ if (die_tag(dw, arg) != DW_TAG_formal_parameter)
+ continue;
+
+ if ((name1 = die_name(dw, arg)) == NULL) {
+ terminate("die %llu: func arg %d has no name\n",
+ off, ii->ii_nargs + 1);
+ }
+
+ if (strcmp(name1, "...") == 0) {
+ free(name1);
+ ii->ii_vargs = 1;
+ continue;
+ }
+
+ ii->ii_nargs++;
+ }
+
+ if (ii->ii_nargs > 0) {
+ int i;
+
+ debug(3, "die %llu: function has %d argument%s\n", off,
+ ii->ii_nargs, (ii->ii_nargs == 1 ? "" : "s"));
+
+ ii->ii_args = xcalloc(sizeof (tdesc_t) * ii->ii_nargs);
+
+ for (arg = die_child(dw, die), i = 0;
+ arg != NULL && i < ii->ii_nargs;
+ arg = die_sibling(dw, arg)) {
+ if (die_tag(dw, arg) != DW_TAG_formal_parameter)
+ continue;
+
+ ii->ii_args[i++] = die_lookup_pass1(dw, arg,
+ DW_AT_type);
+ }
+ }
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+}
+
+/*ARGSUSED3*/
+static void
+die_variable_create(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off, tdesc_t *tdp __unused)
+{
+ iidesc_t *ii;
+ char *name;
+
+ debug(3, "die %llu: creating object definition\n", off);
+
+ if (die_isdecl(dw, die) || (name = die_name(dw, die)) == NULL)
+ return; /* skip prototypes and nameless objects */
+
+ ii = xcalloc(sizeof (iidesc_t));
+ ii->ii_type = die_isglobal(dw, die) ? II_GVAR : II_SVAR;
+ ii->ii_name = name;
+ ii->ii_dtype = die_lookup_pass1(dw, die, DW_AT_type);
+ if (ii->ii_type == II_SVAR)
+ ii->ii_owner = xstrdup(dw->dw_cuname);
+
+ iidesc_add(dw->dw_td->td_iihash, ii);
+}
+
+/*ARGSUSED2*/
+static int
+die_fwd_resolve(tdesc_t *fwd, tdesc_t **fwdp, void *private __unused)
+{
+ if (fwd->t_flags & TDESC_F_RESOLVED)
+ return (1);
+
+ if (fwd->t_tdesc != NULL) {
+ debug(3, "tdp %u: unforwarded %s\n", fwd->t_id,
+ tdesc_name(fwd));
+ *fwdp = fwd->t_tdesc;
+ }
+
+ fwd->t_flags |= TDESC_F_RESOLVED;
+
+ return (1);
+}
+
+/*ARGSUSED*/
+static void
+die_lexblk_descend(dwarf_t *dw, Dwarf_Die die, Dwarf_Off off __unused, tdesc_t *tdp __unused)
+{
+ Dwarf_Die child = die_child(dw, die);
+
+ if (child != NULL)
+ die_create(dw, child);
+}
+
+/*
+ * Used to map the die to a routine which can parse it, using the tag to do the
+ * mapping. While the processing of most tags entails the creation of a tdesc,
+ * there are a few which don't - primarily those which result in the creation of
+ * iidescs which refer to existing tdescs.
+ */
+
+#define DW_F_NOTDP 0x1 /* Don't create a tdesc for the creator */
+
+typedef struct die_creator {
+ Dwarf_Half dc_tag;
+ uint16_t dc_flags;
+ void (*dc_create)(dwarf_t *, Dwarf_Die, Dwarf_Off, tdesc_t *);
+} die_creator_t;
+
+static const die_creator_t die_creators[] = {
+ { DW_TAG_array_type, 0, die_array_create },
+ { DW_TAG_enumeration_type, 0, die_enum_create },
+ { DW_TAG_lexical_block, DW_F_NOTDP, die_lexblk_descend },
+ { DW_TAG_pointer_type, 0, die_pointer_create },
+ { DW_TAG_structure_type, 0, die_struct_create },
+ { DW_TAG_subroutine_type, 0, die_funcptr_create },
+ { DW_TAG_typedef, 0, die_typedef_create },
+ { DW_TAG_union_type, 0, die_union_create },
+ { DW_TAG_base_type, 0, die_base_create },
+ { DW_TAG_const_type, 0, die_const_create },
+ { DW_TAG_subprogram, DW_F_NOTDP, die_function_create },
+ { DW_TAG_variable, DW_F_NOTDP, die_variable_create },
+ { DW_TAG_volatile_type, 0, die_volatile_create },
+ { DW_TAG_restrict_type, 0, die_restrict_create },
+ { 0, 0, NULL }
+};
+
+static const die_creator_t *
+die_tag2ctor(Dwarf_Half tag)
+{
+ const die_creator_t *dc;
+
+ for (dc = die_creators; dc->dc_create != NULL; dc++) {
+ if (dc->dc_tag == tag)
+ return (dc);
+ }
+
+ return (NULL);
+}
+
+static void
+die_create_one(dwarf_t *dw, Dwarf_Die die)
+{
+ Dwarf_Off off = die_off(dw, die);
+ const die_creator_t *dc;
+ Dwarf_Half tag;
+ tdesc_t *tdp;
+
+ debug(3, "die %llu <%llx>: create_one\n", off, off);
+
+ if (off > dw->dw_maxoff) {
+ terminate("illegal die offset %llu (max %llu)\n", off,
+ dw->dw_maxoff);
+ }
+
+ tag = die_tag(dw, die);
+
+ if ((dc = die_tag2ctor(tag)) == NULL) {
+ debug(2, "die %llu: ignoring tag type %x\n", off, tag);
+ return;
+ }
+
+ if ((tdp = tdesc_lookup(dw, off)) == NULL &&
+ !(dc->dc_flags & DW_F_NOTDP)) {
+ tdp = xcalloc(sizeof (tdesc_t));
+ tdp->t_id = off;
+ tdesc_add(dw, tdp);
+ }
+
+ if (tdp != NULL)
+ tdp->t_name = die_name(dw, die);
+
+ dc->dc_create(dw, die, off, tdp);
+}
+
+static void
+die_create(dwarf_t *dw, Dwarf_Die die)
+{
+ do {
+ die_create_one(dw, die);
+ } while ((die = die_sibling(dw, die)) != NULL);
+}
+
+static tdtrav_cb_f die_resolvers[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ die_array_resolve, /* array */
+ NULL, /* function */
+ die_sou_resolve, /* struct */
+ die_sou_resolve, /* union */
+ die_enum_resolve, /* enum */
+ die_fwd_resolve, /* forward */
+ NULL, /* typedef */
+ NULL, /* typedef unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL, /* restrict */
+};
+
+static tdtrav_cb_f die_fail_reporters[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ die_array_failed, /* array */
+ NULL, /* function */
+ die_sou_failed, /* struct */
+ die_sou_failed, /* union */
+ NULL, /* enum */
+ NULL, /* forward */
+ NULL, /* typedef */
+ NULL, /* typedef unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL, /* restrict */
+};
+
+static void
+die_resolve(dwarf_t *dw)
+{
+ int last = -1;
+ int pass = 0;
+
+ do {
+ pass++;
+ dw->dw_nunres = 0;
+
+ (void) iitraverse_hash(dw->dw_td->td_iihash,
+ &dw->dw_td->td_curvgen, NULL, NULL, die_resolvers, dw);
+
+ debug(3, "resolve: pass %d, %u left\n", pass, dw->dw_nunres);
+
+ if ((int) dw->dw_nunres == last) {
+ fprintf(stderr, "%s: failed to resolve the following "
+ "types:\n", progname);
+
+ (void) iitraverse_hash(dw->dw_td->td_iihash,
+ &dw->dw_td->td_curvgen, NULL, NULL,
+ die_fail_reporters, dw);
+
+ terminate("failed to resolve types\n");
+ }
+
+ last = dw->dw_nunres;
+
+ } while (dw->dw_nunres != 0);
+}
+
+/*ARGSUSED*/
+int
+dw_read(tdata_t *td, Elf *elf, char *filename __unused)
+{
+ Dwarf_Unsigned abboff, hdrlen, nxthdr;
+ Dwarf_Half vers, addrsz;
+ Dwarf_Die cu = 0;
+ Dwarf_Die child = 0;
+ dwarf_t dw;
+ char *prod = NULL;
+ int rc;
+
+ bzero(&dw, sizeof (dwarf_t));
+ dw.dw_td = td;
+ dw.dw_ptrsz = elf_ptrsz(elf);
+ dw.dw_mfgtid_last = TID_MFGTID_BASE;
+ dw.dw_tidhash = hash_new(TDESC_HASH_BUCKETS, tdesc_idhash, tdesc_idcmp);
+ dw.dw_fwdhash = hash_new(TDESC_HASH_BUCKETS, tdesc_namehash,
+ tdesc_namecmp);
+ dw.dw_enumhash = hash_new(TDESC_HASH_BUCKETS, tdesc_namehash,
+ tdesc_namecmp);
+
+ if ((rc = dwarf_elf_init(elf, DW_DLC_READ, &dw.dw_dw,
+ &dw.dw_err)) == DW_DLV_NO_ENTRY) {
+ errno = ENOENT;
+ return (-1);
+ } else if (rc != DW_DLV_OK) {
+ if (dwarf_errno(&dw.dw_err) == DW_DLE_DEBUG_INFO_NULL) {
+ /*
+ * There's no type data in the DWARF section, but
+ * libdwarf is too clever to handle that properly.
+ */
+ return (0);
+ }
+
+ terminate("failed to initialize DWARF: %s\n",
+ dwarf_errmsg(&dw.dw_err));
+ }
+
+ if ((rc = dwarf_next_cu_header(dw.dw_dw, &hdrlen, &vers, &abboff,
+ &addrsz, &nxthdr, &dw.dw_err)) != DW_DLV_OK)
+ terminate("rc = %d %s\n", rc, dwarf_errmsg(&dw.dw_err));
+
+ if ((cu = die_sibling(&dw, NULL)) == NULL)
+ terminate("file does not contain dwarf type data "
+ "(try compiling with -g)\n");
+
+ dw.dw_maxoff = nxthdr - 1;
+
+ if (dw.dw_maxoff > TID_FILEMAX)
+ terminate("file contains too many types\n");
+
+ debug(1, "DWARF version: %d\n", vers);
+ if (vers != DWARF_VERSION) {
+ terminate("file contains incompatible version %d DWARF code "
+ "(version 2 required)\n", vers);
+ }
+
+ if (die_string(&dw, cu, DW_AT_producer, &prod, 0)) {
+ debug(1, "DWARF emitter: %s\n", prod);
+ free(prod);
+ }
+
+ if ((dw.dw_cuname = die_name(&dw, cu)) != NULL) {
+ char *base = xstrdup(basename(dw.dw_cuname));
+ free(dw.dw_cuname);
+ dw.dw_cuname = base;
+
+ debug(1, "CU name: %s\n", dw.dw_cuname);
+ }
+
+ if ((child = die_child(&dw, cu)) != NULL)
+ die_create(&dw, child);
+
+ if ((rc = dwarf_next_cu_header(dw.dw_dw, &hdrlen, &vers, &abboff,
+ &addrsz, &nxthdr, &dw.dw_err)) != DW_DLV_NO_ENTRY)
+ terminate("multiple compilation units not supported\n");
+
+ (void) dwarf_finish(&dw.dw_dw, &dw.dw_err);
+
+ die_resolve(&dw);
+
+ cvt_fixups(td, dw.dw_ptrsz);
+
+ /* leak the dwarf_t */
+
+ return (0);
+}
diff --git a/cvt/fifo.c b/cvt/fifo.c
new file mode 100644
index 0000000..fb9a11f
--- /dev/null
+++ b/cvt/fifo.c
@@ -0,0 +1,153 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating a FIFO queue
+ */
+
+#include <stdlib.h>
+
+#include "fifo.h"
+#include "memory.h"
+
+typedef struct fifonode {
+ void *fn_data;
+ struct fifonode *fn_next;
+} fifonode_t;
+
+struct fifo {
+ fifonode_t *f_head;
+ fifonode_t *f_tail;
+};
+
+fifo_t *
+fifo_new(void)
+{
+ fifo_t *f;
+
+ f = xcalloc(sizeof (fifo_t));
+
+ return (f);
+}
+
+/* Add to the end of the fifo */
+void
+fifo_add(fifo_t *f, void *data)
+{
+ fifonode_t *fn = xmalloc(sizeof (fifonode_t));
+
+ fn->fn_data = data;
+ fn->fn_next = NULL;
+
+ if (f->f_tail == NULL)
+ f->f_head = f->f_tail = fn;
+ else {
+ f->f_tail->fn_next = fn;
+ f->f_tail = fn;
+ }
+}
+
+/* Remove from the front of the fifo */
+void *
+fifo_remove(fifo_t *f)
+{
+ fifonode_t *fn;
+ void *data;
+
+ if ((fn = f->f_head) == NULL)
+ return (NULL);
+
+ data = fn->fn_data;
+ if ((f->f_head = fn->fn_next) == NULL)
+ f->f_tail = NULL;
+
+ free(fn);
+
+ return (data);
+}
+
+/*ARGSUSED*/
+static void
+fifo_nullfree(void *arg)
+{
+ /* this function intentionally left blank */
+}
+
+/* Free an entire fifo */
+void
+fifo_free(fifo_t *f, void (*freefn)(void *))
+{
+ fifonode_t *fn = f->f_head;
+ fifonode_t *tmp;
+
+ if (freefn == NULL)
+ freefn = fifo_nullfree;
+
+ while (fn) {
+ (*freefn)(fn->fn_data);
+
+ tmp = fn;
+ fn = fn->fn_next;
+ free(tmp);
+ }
+
+ free(f);
+}
+
+int
+fifo_len(fifo_t *f)
+{
+ fifonode_t *fn;
+ int i;
+
+ for (i = 0, fn = f->f_head; fn; fn = fn->fn_next, i++);
+
+ return (i);
+}
+
+int
+fifo_empty(fifo_t *f)
+{
+ return (f->f_head == NULL);
+}
+
+int
+fifo_iter(fifo_t *f, int (*iter)(void *data, void *arg), void *arg)
+{
+ fifonode_t *fn;
+ int rc;
+ int ret = 0;
+
+ for (fn = f->f_head; fn; fn = fn->fn_next) {
+ if ((rc = iter(fn->fn_data, arg)) < 0)
+ return (-1);
+ ret += rc;
+ }
+
+ return (ret);
+}
diff --git a/cvt/fifo.h b/cvt/fifo.h
new file mode 100644
index 0000000..e4b948a
--- /dev/null
+++ b/cvt/fifo.h
@@ -0,0 +1,54 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _FIFO_H
+#define _FIFO_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating a FIFO queue
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct fifo fifo_t;
+
+extern fifo_t *fifo_new(void);
+extern void fifo_add(fifo_t *, void *);
+extern void *fifo_remove(fifo_t *);
+extern void fifo_free(fifo_t *, void (*)(void *));
+extern int fifo_len(fifo_t *);
+extern int fifo_empty(fifo_t *);
+extern int fifo_iter(fifo_t *, int (*)(void *, void *), void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FIFO_H */
diff --git a/cvt/fixup_tdescs.c b/cvt/fixup_tdescs.c
new file mode 100644
index 0000000..55a54f4
--- /dev/null
+++ b/cvt/fixup_tdescs.c
@@ -0,0 +1,276 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Workarounds for stabs generation bugs in the compiler and general needed
+ * fixups.
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "hash.h"
+#include "memory.h"
+
+/*
+ * Due to 4432619, the 6.1 compiler will sometimes incorrectly generate pointer
+ * stabs. Given a struct foo, and a corresponding typedef struct foo foo_t.
+ * In some cases, when faced with a pointer to a foo_t, the compiler will
+ * sometimes generate a stab that describes a pointer to a struct foo.
+ * Regardless of correctness, this breaks merges, as it occurs inconsistently
+ * by file. The following two routines know how to recognize and repair foo_t *
+ * and foo_t ** bugs in a specific set of cases. There is no general way to
+ * solve this problem without a fix to the compiler. In general, cases should
+ * only be added to these routines to fix merging problems in genunix.
+ */
+static void
+fix_ptrptr_to_struct(tdata_t *td)
+{
+ const char *strs[2] = { "as", "fdbuffer" };
+ const char *mems[2] = { "a_objectdir", "fd_shadow" };
+ const char *acts[2] = { "vnode", "page" };
+ const char *tgts[2] = { "vnode_t", "page_t" };
+ tdesc_t *str;
+ tdesc_t *act, *tgt;
+ tdesc_t *p1, *p2;
+ mlist_t *ml;
+ int i;
+
+ for (i = 0; i < (int) (sizeof (strs) / sizeof (strs[0])); i++) {
+ if (!(str = lookupname(strs[i])) || str->t_type != STRUCT)
+ continue;
+
+ for (ml = str->t_members; ml; ml = ml->ml_next) {
+ if (streq(ml->ml_name, mems[i]))
+ break;
+ }
+ if (!ml)
+ continue;
+
+ if (ml->ml_type->t_type != POINTER || ml->ml_type->t_name ||
+ ml->ml_type->t_tdesc->t_type != POINTER ||
+ ml->ml_type->t_tdesc->t_name)
+ continue;
+
+ act = ml->ml_type->t_tdesc->t_tdesc;
+ if (act->t_type != STRUCT || !streq(act->t_name, acts[i]))
+ continue;
+
+ if (!(tgt = lookupname(tgts[i])) || tgt->t_type != TYPEDEF)
+ continue;
+
+ /* We have an instance of the bug */
+ p2 = xcalloc(sizeof (*p2));
+ p2->t_type = POINTER;
+ p2->t_id = td->td_nextid++;
+ p2->t_tdesc = tgt;
+
+ p1 = xcalloc(sizeof (*p1));
+ p1->t_type = POINTER;
+ p1->t_id = td->td_nextid++;
+ p1->t_tdesc = p2;
+
+ ml->ml_type = p1;
+
+ debug(3, "Fixed %s->%s => ptrptr struct %s bug\n",
+ strs[i], mems[i], acts[i]);
+ }
+}
+
+static void
+fix_ptr_to_struct(tdata_t *td)
+{
+ const char *strs[2] = { "vmem", "id_space" };
+ const char *mems[2] = { NULL, "is_vmem" };
+ tdesc_t *ptr = NULL;
+ tdesc_t *str, *vmt;
+ mlist_t *ml;
+ int i;
+
+ if ((vmt = lookupname("vmem_t")) == NULL || vmt->t_type != TYPEDEF)
+ return;
+
+ for (i = 0; i < (int) (sizeof (strs) / sizeof (strs[0])); i++) {
+ if (!(str = lookupname(strs[i])) || str->t_type != STRUCT)
+ continue;
+
+ for (ml = str->t_members; ml; ml = ml->ml_next) {
+ if (mems[i] && !streq(ml->ml_name, mems[i]))
+ continue;
+
+ if (ml->ml_type->t_type != POINTER ||
+ ml->ml_type->t_name ||
+ (ml->ml_type->t_tdesc->t_type != STRUCT &&
+ ml->ml_type->t_tdesc->t_type != FORWARD) ||
+ !streq(ml->ml_type->t_tdesc->t_name, "vmem"))
+ continue;
+
+ debug(3, "Fixed %s->%s => ptr struct vmem bug\n",
+ strs[i], ml->ml_name);
+
+ if (!ptr) {
+ ptr = xcalloc(sizeof (*ptr));
+ ptr->t_type = POINTER;
+ ptr->t_id = td->td_nextid++;
+ ptr->t_tdesc = vmt;
+ }
+
+ ml->ml_type = ptr;
+ }
+ }
+}
+
+/*
+ * Fix stabs generation bugs. These routines must be run before the
+ * post-conversion merge
+ */
+void
+cvt_fixstabs(tdata_t *td)
+{
+ fix_ptrptr_to_struct(td);
+ fix_ptr_to_struct(td);
+}
+
+struct match {
+ tdesc_t *m_ret;
+ const char *m_name;
+};
+
+static int
+matching_iidesc(void *arg1, void *arg2)
+{
+ iidesc_t *iidesc = arg1;
+ struct match *match = arg2;
+ if (!streq(iidesc->ii_name, match->m_name))
+ return (0);
+
+ if (iidesc->ii_type != II_TYPE && iidesc->ii_type != II_SOU)
+ return (0);
+
+ match->m_ret = iidesc->ii_dtype;
+ return (-1);
+}
+
+static tdesc_t *
+lookup_tdesc(tdata_t *td, char const *name)
+{
+ struct match match = { NULL, name };
+ iter_iidescs_by_name(td, name, matching_iidesc, &match);
+ return (match.m_ret);
+}
+
+/*
+ * The cpu structure grows, with the addition of a machcpu member, if
+ * _MACHDEP is defined. This means that, for example, the cpu structure
+ * in unix is different from the cpu structure in genunix. As one might
+ * expect, this causes merges to fail. Since everyone indirectly contains
+ * a pointer to a CPU structure, the failed merges can cause massive amounts
+ * of duplication. In the case of unix uniquifying against genunix, upwards
+ * of 50% of the structures were unmerged due to this problem. We fix this
+ * by adding a cpu_m member. If machcpu hasn't been defined in our module,
+ * we make a forward node for it.
+ */
+static void
+fix_small_cpu_struct(tdata_t *td, size_t ptrsize)
+{
+ tdesc_t *cput, *cpu;
+ tdesc_t *machcpu;
+ mlist_t *ml, *lml;
+ mlist_t *cpum;
+ int foundcpucyc = 0;
+
+ /*
+ * We're going to take the circuitous route finding the cpu structure,
+ * because we want to make sure that we find the right one. It would
+ * be nice if we could verify the header name too. DWARF might not
+ * have the cpu_t, so we let this pass.
+ */
+ if ((cput = lookup_tdesc(td, "cpu_t")) != NULL) {
+ if (cput->t_type != TYPEDEF)
+ return;
+ cpu = cput->t_tdesc;
+ } else {
+ cpu = lookup_tdesc(td, "cpu");
+ }
+
+ if (cpu == NULL)
+ return;
+
+ if (!streq(cpu->t_name, "cpu") || cpu->t_type != STRUCT)
+ return;
+
+ for (ml = cpu->t_members, lml = NULL; ml;
+ lml = ml, ml = ml->ml_next) {
+ if (strcmp(ml->ml_name, "cpu_cyclic") == 0)
+ foundcpucyc = 1;
+ }
+
+ if (foundcpucyc == 0 || lml == NULL ||
+ strcmp(lml->ml_name, "cpu_m") == 0)
+ return;
+
+ /*
+ * We need to derive the right offset for the fake cpu_m member. To do
+ * that, we require a special unused member to be the last member
+ * before the 'cpu_m', that we encode knowledge of here. ABI alignment
+ * on all platforms is such that we only need to add a pointer-size
+ * number of bits to get the right offset for cpu_m. This would most
+ * likely break if gcc's -malign-double were ever used, but that option
+ * breaks the ABI anyway.
+ */
+ if (!streq(lml->ml_name, "cpu_m_pad") &&
+ getenv("CTFCONVERT_PERMISSIVE") == NULL) {
+ terminate("last cpu_t member before cpu_m is %s; "
+ "it must be cpu_m_pad.\n", lml->ml_name);
+ }
+
+ if ((machcpu = lookup_tdesc(td, "machcpu")) == NULL) {
+ machcpu = xcalloc(sizeof (*machcpu));
+ machcpu->t_name = xstrdup("machcpu");
+ machcpu->t_id = td->td_nextid++;
+ machcpu->t_type = FORWARD;
+ } else if (machcpu->t_type != STRUCT) {
+ return;
+ }
+
+ debug(3, "Adding cpu_m machcpu %s to cpu struct\n",
+ (machcpu->t_type == FORWARD ? "forward" : "struct"));
+
+ cpum = xmalloc(sizeof (*cpum));
+ cpum->ml_offset = lml->ml_offset + (ptrsize * NBBY);
+ cpum->ml_size = 0;
+ cpum->ml_name = xstrdup("cpu_m");
+ cpum->ml_type = machcpu;
+ cpum->ml_next = NULL;
+
+ lml->ml_next = cpum;
+}
+
+void
+cvt_fixups(tdata_t *td, size_t ptrsize)
+{
+ fix_small_cpu_struct(td, ptrsize);
+}
diff --git a/cvt/hash.c b/cvt/hash.c
new file mode 100644
index 0000000..21a2a36
--- /dev/null
+++ b/cvt/hash.c
@@ -0,0 +1,286 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating hash tables
+ */
+
+#include "libctf/ctf_impl.h"
+#include "hash.h"
+#include "memory.h"
+#include "common/list.h"
+
+struct hash {
+ int h_nbuckets;
+ list_t **h_buckets;
+
+ int (*h_hashfn)(int, void *);
+ int (*h_cmp)(void *, void *);
+};
+
+struct hash_data {
+ hash_t *hd_hash;
+ int (*hd_fun)(void *, void *);
+ void *hd_key;
+ void *hd_private;
+
+ void *hd_ret;
+};
+
+static int
+hash_def_hash(int nbuckets, void *arg)
+{
+ uintptr_t data = (uintptr_t) arg;
+ return (data % nbuckets);
+}
+
+static int
+hash_def_cmp(void *d1, void *d2)
+{
+ return (d1 != d2);
+}
+
+
+int
+hash_name(int nbuckets, const char *name)
+{
+ const char *c;
+ ulong_t g;
+ int h = 0;
+
+ for (c = name; *c; c++) {
+ h = (h << 4) + *c;
+ if ((g = (h & 0xf0000000)) != 0) {
+ h ^= (g >> 24);
+ h ^= g;
+ }
+ }
+
+ return (h % nbuckets);
+}
+
+hash_t *
+hash_new(int nbuckets, int (*hashfn)(int, void *), int (*cmp)(void *, void *))
+{
+ hash_t *hash;
+
+ hash = xmalloc(sizeof (hash_t));
+ hash->h_buckets = xcalloc(sizeof (list_t *) * nbuckets);
+ hash->h_nbuckets = nbuckets;
+ hash->h_hashfn = hashfn ? hashfn : hash_def_hash;
+ hash->h_cmp = cmp ? cmp : hash_def_cmp;
+
+ return (hash);
+}
+
+void
+hash_add(hash_t *hash, void *key)
+{
+ int bucket = hash->h_hashfn(hash->h_nbuckets, key);
+
+ list_add(&hash->h_buckets[bucket], key);
+}
+
+static int
+hash_add_cb(void *node, void *private)
+{
+ hash_add((hash_t *)private, node);
+ return (0);
+}
+
+void
+hash_merge(hash_t *to, hash_t *from)
+{
+ (void) hash_iter(from, hash_add_cb, to);
+}
+
+static int
+hash_remove_cb(void *key1, void *key2, void *arg)
+{
+ hash_t *hash = arg;
+ return (hash->h_cmp(key1, key2));
+}
+
+void
+hash_remove(hash_t *hash, void *key)
+{
+ int bucket = hash->h_hashfn(hash->h_nbuckets, key);
+
+ (void) list_remove(&hash->h_buckets[bucket], key,
+ hash_remove_cb, hash);
+}
+
+int
+hash_match(hash_t *hash, void *key, int (*fun)(void *, void *),
+ void *private)
+{
+ int bucket = hash->h_hashfn(hash->h_nbuckets, key);
+
+ return (list_iter(hash->h_buckets[bucket], fun, private) < 0);
+}
+
+static int
+hash_find_list_cb(void *node, void *arg)
+{
+ struct hash_data *hd = arg;
+ int cbrc;
+ int rc = 0;
+
+ if (hd->hd_hash->h_cmp(hd->hd_key, node) == 0) {
+ if ((cbrc = hd->hd_fun(node, hd->hd_private)) < 0)
+ return (cbrc);
+ rc += cbrc;
+ }
+
+ return (rc);
+}
+
+int
+hash_find_iter(hash_t *hash, void *key, int (*fun)(void *, void *),
+ void *private)
+{
+ int bucket = hash->h_hashfn(hash->h_nbuckets, key);
+ struct hash_data hd;
+
+ hd.hd_hash = hash;
+ hd.hd_fun = fun;
+ hd.hd_key = key;
+ hd.hd_private = private;
+
+ return (list_iter(hash->h_buckets[bucket], hash_find_list_cb,
+ &hd));
+}
+
+/* stop on first match */
+static int
+hash_find_first_cb(void *node, void *arg)
+{
+ struct hash_data *hd = arg;
+ if (hd->hd_hash->h_cmp(hd->hd_key, node) == 0) {
+ hd->hd_ret = node;
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+hash_find(hash_t *hash, void *key, void **value)
+{
+ int ret;
+ struct hash_data hd;
+
+ hd.hd_hash = hash;
+ hd.hd_fun = hash_find_first_cb;
+ hd.hd_key = key;
+
+ ret = hash_match(hash, key, hash_find_first_cb, &hd);
+ if (ret && value)
+ *value = hd.hd_ret;
+
+ return (ret);
+}
+
+int
+hash_iter(hash_t *hash, int (*fun)(void *, void *), void *private)
+{
+ int cumrc = 0;
+ int cbrc;
+ int i;
+
+ for (i = 0; i < hash->h_nbuckets; i++) {
+ if (hash->h_buckets[i] != NULL) {
+ if ((cbrc = list_iter(hash->h_buckets[i], fun,
+ private)) < 0)
+ return (cbrc);
+ cumrc += cbrc;
+ }
+ }
+
+ return (cumrc);
+}
+
+int
+hash_count(hash_t *hash)
+{
+ int num, i;
+
+ for (num = 0, i = 0; i < hash->h_nbuckets; i++)
+ num += list_count(hash->h_buckets[i]);
+
+ return (num);
+}
+
+void
+hash_free(hash_t *hash, void (*datafree)(void *, void *), void *private)
+{
+ int i;
+
+ if (hash == NULL)
+ return;
+
+ for (i = 0; i < hash->h_nbuckets; i++)
+ list_free(hash->h_buckets[i], datafree, private);
+ free(hash->h_buckets);
+ free(hash);
+}
+
+void
+hash_stats(hash_t *hash, int verbose)
+{
+ int min = list_count(hash->h_buckets[0]);
+ int minidx = 0;
+ int max = min;
+ int maxidx = 0;
+ int tot = min;
+ int count;
+ int i;
+
+ if (min && verbose)
+ printf("%3d: %d\n", 0, min);
+ for (i = 1; i < hash->h_nbuckets; i++) {
+ count = list_count(hash->h_buckets[i]);
+ if (min > count) {
+ min = count;
+ minidx = i;
+ }
+ if (max < count) {
+ max = count;
+ maxidx = i;
+ }
+ if (count && verbose)
+ printf("%3d: %d\n", i, count);
+ tot += count;
+ }
+
+ printf("Hash statistics:\n");
+ printf(" Buckets: %d\n", hash->h_nbuckets);
+ printf(" Items : %d\n", tot);
+ printf(" Min/Max: %d in #%d, %d in #%d\n", min, minidx, max, maxidx);
+ printf(" Average: %5.2f\n", (float)tot / (float)hash->h_nbuckets);
+}
diff --git a/cvt/hash.h b/cvt/hash.h
new file mode 100644
index 0000000..94d93d4
--- /dev/null
+++ b/cvt/hash.h
@@ -0,0 +1,59 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _HASH_H
+#define _HASH_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating hash tables
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct hash hash_t;
+
+hash_t *hash_new(int, int (*)(int, void *), int (*)(void *, void *));
+void hash_add(hash_t *, void *);
+void hash_merge(hash_t *, hash_t *);
+void hash_remove(hash_t *, void *);
+int hash_find(hash_t *, void *, void **);
+int hash_find_iter(hash_t *, void *, int (*)(void *, void *), void *);
+int hash_iter(hash_t *, int (*)(void *, void *), void *);
+int hash_match(hash_t *, void *, int (*)(void *, void *), void *);
+int hash_count(hash_t *);
+int hash_name(int, const char *);
+void hash_stats(hash_t *, int);
+void hash_free(hash_t *, void (*)(void *, void *), void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _HASH_H */
diff --git a/cvt/iidesc.c b/cvt/iidesc.c
new file mode 100644
index 0000000..75f986c
--- /dev/null
+++ b/cvt/iidesc.c
@@ -0,0 +1,194 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating iidesc_t structures
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "memory.h"
+#include "common/list.h"
+#include "hash.h"
+
+typedef struct iidesc_find {
+ iidesc_t *iif_tgt;
+ iidesc_t *iif_ret;
+} iidesc_find_t;
+
+iidesc_t *
+iidesc_new(char *name)
+{
+ iidesc_t *ii;
+
+ ii = xcalloc(sizeof (iidesc_t));
+ if (name)
+ ii->ii_name = xstrdup(name);
+
+ return (ii);
+}
+
+int
+iidesc_hash(int nbuckets, void *arg)
+{
+ iidesc_t *ii = arg;
+ int h = 0;
+
+ if (ii->ii_name)
+ return (hash_name(nbuckets, ii->ii_name));
+
+ return (h);
+}
+
+static int
+iidesc_cmp(void *arg1, void *arg2)
+{
+ iidesc_t *src = arg1;
+ iidesc_find_t *find = arg2;
+ iidesc_t *tgt = find->iif_tgt;
+
+ if (src->ii_type != tgt->ii_type ||
+ !streq(src->ii_name, tgt->ii_name))
+ return (0);
+
+ find->iif_ret = src;
+
+ return (-1);
+}
+
+void
+iidesc_add(hash_t *hash, iidesc_t *new)
+{
+ iidesc_find_t find;
+
+ find.iif_tgt = new;
+ find.iif_ret = NULL;
+
+ (void) hash_match(hash, new, iidesc_cmp, &find);
+
+ if (find.iif_ret != NULL) {
+ iidesc_t *old = find.iif_ret;
+ iidesc_t tmp;
+ /* replacing existing one */
+ bcopy(old, &tmp, sizeof (tmp));
+ bcopy(new, old, sizeof (*old));
+ bcopy(&tmp, new, sizeof (*new));
+
+ iidesc_free(new, NULL);
+ return;
+ }
+
+ hash_add(hash, new);
+}
+
+void
+iter_iidescs_by_name(tdata_t *td, char const *name,
+ int (*func)(void *, void *), void *data)
+{
+ iidesc_t tmpdesc;
+ bzero(&tmpdesc, sizeof(tmpdesc));
+ tmpdesc.ii_name = xstrdup(name);
+ (void) hash_match(td->td_iihash, &tmpdesc, func, data);
+ free(tmpdesc.ii_name);
+}
+
+iidesc_t *
+iidesc_dup(iidesc_t *src)
+{
+ iidesc_t *tgt;
+
+ tgt = xmalloc(sizeof (iidesc_t));
+ bcopy(src, tgt, sizeof (iidesc_t));
+
+ tgt->ii_name = src->ii_name ? xstrdup(src->ii_name) : NULL;
+ tgt->ii_owner = src->ii_owner ? xstrdup(src->ii_owner) : NULL;
+
+ if (tgt->ii_nargs) {
+ tgt->ii_args = xmalloc(sizeof (tdesc_t *) * tgt->ii_nargs);
+ bcopy(src->ii_args, tgt->ii_args,
+ sizeof (tdesc_t *) * tgt->ii_nargs);
+ }
+
+ return (tgt);
+}
+
+iidesc_t *
+iidesc_dup_rename(iidesc_t *src, char const *name, char const *owner)
+{
+ iidesc_t *tgt = iidesc_dup(src);
+ free(tgt->ii_name);
+ free(tgt->ii_owner);
+
+ tgt->ii_name = name ? xstrdup(name) : NULL;
+ tgt->ii_owner = owner ? xstrdup(owner) : NULL;
+
+ return (tgt);
+}
+
+/*ARGSUSED*/
+void
+iidesc_free(void *arg, void *private __unused)
+{
+ iidesc_t *idp = arg;
+ if (idp->ii_name)
+ free(idp->ii_name);
+ if (idp->ii_nargs)
+ free(idp->ii_args);
+ if (idp->ii_owner)
+ free(idp->ii_owner);
+ free(idp);
+}
+
+int
+iidesc_dump(iidesc_t *ii)
+{
+ printf("type: %d name %s\n", ii->ii_type,
+ (ii->ii_name ? ii->ii_name : "(anon)"));
+
+ return (0);
+}
+
+int
+iidesc_count_type(void *data, void *private)
+{
+ iidesc_t *ii = data;
+ iitype_t match = (iitype_t)private;
+
+ return (ii->ii_type == match);
+}
+
+void
+iidesc_stats(hash_t *ii)
+{
+ printf("GFun: %5d SFun: %5d GVar: %5d SVar: %5d T %5d SOU: %5d\n",
+ hash_iter(ii, iidesc_count_type, (void *)II_GFUN),
+ hash_iter(ii, iidesc_count_type, (void *)II_SFUN),
+ hash_iter(ii, iidesc_count_type, (void *)II_GVAR),
+ hash_iter(ii, iidesc_count_type, (void *)II_SVAR),
+ hash_iter(ii, iidesc_count_type, (void *)II_TYPE),
+ hash_iter(ii, iidesc_count_type, (void *)II_SOU));
+}
diff --git a/cvt/input.c b/cvt/input.c
new file mode 100644
index 0000000..900cf85
--- /dev/null
+++ b/cvt/input.c
@@ -0,0 +1,412 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for retrieving CTF data from a .SUNW_ctf ELF section
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "memory.h"
+#include "common/symbol.h"
+
+typedef int read_cb_f(tdata_t *, char *, void *);
+
+/*
+ * Return the source types that the object was generated from.
+ */
+source_types_t
+built_source_types(Elf *elf, char const *file)
+{
+ source_types_t types = SOURCE_NONE;
+ symit_data_t *si;
+
+ if ((si = symit_new(elf, file)) == NULL)
+ return (SOURCE_NONE);
+
+ while (symit_next(si, STT_FILE) != NULL) {
+ char *name = symit_name(si);
+ size_t len = strlen(name);
+ if (len < 2 || name[len - 2] != '.') {
+ types |= SOURCE_UNKNOWN;
+ continue;
+ }
+
+ switch (name[len - 1]) {
+ case 'c':
+ types |= SOURCE_C;
+ break;
+ case 'h':
+ /* ignore */
+ break;
+ case 's':
+ case 'S':
+ types |= SOURCE_S;
+ break;
+ default:
+ types |= SOURCE_UNKNOWN;
+ }
+ }
+
+ symit_free(si);
+ return (types);
+}
+
+static int
+read_file(Elf *elf, char *file, char *label, read_cb_f *func, void *arg,
+ int require_ctf)
+{
+ Elf_Scn *ctfscn;
+ Elf_Data *ctfdata = NULL;
+ symit_data_t *si = NULL;
+ int ctfscnidx;
+ tdata_t *td;
+
+ if ((ctfscnidx = findelfsecidx(elf, file, ".SUNW_ctf")) < 0) {
+ if (require_ctf &&
+ (built_source_types(elf, file) & SOURCE_C)) {
+ terminate("Input file %s was partially built from "
+ "C sources, but no CTF data was present\n", file);
+ }
+ return (0);
+ }
+
+ if ((ctfscn = elf_getscn(elf, ctfscnidx)) == NULL ||
+ (ctfdata = elf_getdata(ctfscn, NULL)) == NULL)
+ elfterminate(file, "Cannot read CTF section");
+
+ /* Reconstruction of type tree */
+ if ((si = symit_new(elf, file)) == NULL) {
+ warning("%s has no symbol table - skipping", file);
+ return (0);
+ }
+
+ td = ctf_load(file, ctfdata->d_buf, ctfdata->d_size, si, label);
+ tdata_build_hashes(td);
+
+ symit_free(si);
+
+ if (td != NULL) {
+ if (func(td, file, arg) < 0)
+ return (-1);
+ else
+ return (1);
+ }
+ return (0);
+}
+
+static int
+read_archive(int fd, Elf *elf, char *file, char *label, read_cb_f *func,
+ void *arg, int require_ctf)
+{
+ Elf *melf;
+ Elf_Cmd cmd = ELF_C_READ;
+ Elf_Arhdr *arh;
+ int secnum = 1, found = 0;
+
+ while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
+ int rc = 0;
+
+ if ((arh = elf_getarhdr(melf)) == NULL) {
+ elfterminate(file, "Can't get archive header for "
+ "member %d", secnum);
+ }
+
+ /* skip special sections - their names begin with "/" */
+ if (*arh->ar_name != '/') {
+ size_t memlen = strlen(file) + 1 +
+ strlen(arh->ar_name) + 1 + 1;
+ char *memname = xmalloc(memlen);
+
+ snprintf(memname, memlen, "%s(%s)", file, arh->ar_name);
+
+ switch (elf_kind(melf)) {
+ case ELF_K_AR:
+ rc = read_archive(fd, melf, memname, label,
+ func, arg, require_ctf);
+ break;
+ case ELF_K_ELF:
+ rc = read_file(melf, memname, label,
+ func, arg, require_ctf);
+ break;
+ default:
+ terminate("%s: Unknown elf kind %d\n",
+ memname, elf_kind(melf));
+ }
+
+ free(memname);
+ }
+
+ cmd = elf_next(melf);
+ (void) elf_end(melf);
+ secnum++;
+
+ if (rc < 0)
+ return (rc);
+ else
+ found += rc;
+ }
+
+ return (found);
+}
+
+static int
+read_ctf_common(char *file, char *label, read_cb_f *func, void *arg,
+ int require_ctf)
+{
+ Elf *elf;
+ int found = 0;
+ int fd;
+
+ debug(3, "Reading %s (label %s)\n", file, (label ? label : "NONE"));
+
+ (void) elf_version(EV_CURRENT);
+
+ if ((fd = open(file, O_RDONLY)) < 0)
+ terminate("%s: Cannot open for reading", file);
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
+ elfterminate(file, "Cannot read");
+
+ switch (elf_kind(elf)) {
+ case ELF_K_AR:
+ found = read_archive(fd, elf, file, label,
+ func, arg, require_ctf);
+ break;
+
+ case ELF_K_ELF:
+ found = read_file(elf, file, label,
+ func, arg, require_ctf);
+ break;
+
+ default:
+ terminate("%s: Unknown elf kind %d\n", file, elf_kind(elf));
+ }
+
+ (void) elf_end(elf);
+ (void) close(fd);
+
+ return (found);
+}
+
+/*ARGSUSED*/
+int
+read_ctf_save_cb(tdata_t *td, char *name __unused, void *retp)
+{
+ tdata_t **tdp = retp;
+
+ *tdp = td;
+
+ return (1);
+}
+
+int
+read_ctf(char **files, int n, char *label, read_cb_f *func, void *private,
+ int require_ctf)
+{
+ int found;
+ int i, rc;
+
+ for (i = 0, found = 0; i < n; i++) {
+ if ((rc = read_ctf_common(files[i], label, func,
+ private, require_ctf)) < 0)
+ return (rc);
+ found += rc;
+ }
+
+ return (found);
+}
+
+static int
+count_archive(int fd, Elf *elf, char *file)
+{
+ Elf *melf;
+ Elf_Cmd cmd = ELF_C_READ;
+ Elf_Arhdr *arh;
+ int nfiles = 0, err = 0;
+
+ while ((melf = elf_begin(fd, cmd, elf)) != NULL) {
+ if ((arh = elf_getarhdr(melf)) == NULL) {
+ warning("Can't process input archive %s\n",
+ file);
+ err++;
+ }
+
+ if (*arh->ar_name != '/')
+ nfiles++;
+
+ cmd = elf_next(melf);
+ (void) elf_end(melf);
+ }
+
+ if (err > 0)
+ return (-1);
+
+ return (nfiles);
+}
+
+int
+count_files(char **files, int n)
+{
+ int nfiles = 0, err = 0;
+ Elf *elf;
+ int fd, rc, i;
+
+ (void) elf_version(EV_CURRENT);
+
+ for (i = 0; i < n; i++) {
+ char *file = files[i];
+
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ warning("Can't read input file %s", file);
+ err++;
+ continue;
+ }
+
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
+ warning("Can't open input file %s: %s\n", file,
+ elf_errmsg(-1));
+ err++;
+ (void) close(fd);
+ continue;
+ }
+
+ switch (elf_kind(elf)) {
+ case ELF_K_AR:
+ if ((rc = count_archive(fd, elf, file)) < 0)
+ err++;
+ else
+ nfiles += rc;
+ break;
+ case ELF_K_ELF:
+ nfiles++;
+ break;
+ default:
+ warning("Input file %s is corrupt\n", file);
+ err++;
+ }
+
+ (void) elf_end(elf);
+ (void) close(fd);
+ }
+
+ if (err > 0)
+ return (-1);
+
+ debug(2, "Found %d files in %d input files\n", nfiles, n);
+
+ return (nfiles);
+}
+
+struct symit_data {
+ GElf_Shdr si_shdr;
+ Elf_Data *si_symd;
+ Elf_Data *si_strd;
+ GElf_Sym si_cursym;
+ char *si_curname;
+ char *si_curfile;
+ int si_nument;
+ int si_next;
+};
+
+symit_data_t *
+symit_new(Elf *elf, const char *file)
+{
+ symit_data_t *si;
+ Elf_Scn *scn;
+ int symtabidx;
+
+ if ((symtabidx = findelfsecidx(elf, file, ".symtab")) < 0)
+ return (NULL);
+
+ si = xcalloc(sizeof (symit_data_t));
+
+ if ((scn = elf_getscn(elf, symtabidx)) == NULL ||
+ gelf_getshdr(scn, &si->si_shdr) == NULL ||
+ (si->si_symd = elf_getdata(scn, NULL)) == NULL)
+ elfterminate(file, "Cannot read .symtab");
+
+ if ((scn = elf_getscn(elf, si->si_shdr.sh_link)) == NULL ||
+ (si->si_strd = elf_getdata(scn, NULL)) == NULL)
+ elfterminate(file, "Cannot read strings for .symtab");
+
+ si->si_nument = si->si_shdr.sh_size / si->si_shdr.sh_entsize;
+
+ return (si);
+}
+
+void
+symit_free(symit_data_t *si)
+{
+ free(si);
+}
+
+void
+symit_reset(symit_data_t *si)
+{
+ si->si_next = 0;
+}
+
+char *
+symit_curfile(symit_data_t *si)
+{
+ return (si->si_curfile);
+}
+
+GElf_Sym *
+symit_next(symit_data_t *si, int type)
+{
+ GElf_Sym sym;
+ int check_sym = (type == STT_OBJECT || type == STT_FUNC);
+
+ for (; si->si_next < si->si_nument; si->si_next++) {
+ gelf_getsym(si->si_symd, si->si_next, &si->si_cursym);
+ gelf_getsym(si->si_symd, si->si_next, &sym);
+ si->si_curname = (caddr_t)si->si_strd->d_buf + sym.st_name;
+
+ if (GELF_ST_TYPE(sym.st_info) == STT_FILE)
+ si->si_curfile = si->si_curname;
+
+ if (GELF_ST_TYPE(sym.st_info) != type ||
+ sym.st_shndx == SHN_UNDEF)
+ continue;
+
+ if (check_sym && ignore_symbol(&sym, si->si_curname))
+ continue;
+
+ si->si_next++;
+
+ return (&si->si_cursym);
+ }
+
+ return (NULL);
+}
+
+char *
+symit_name(symit_data_t *si)
+{
+ return (si->si_curname);
+}
diff --git a/cvt/merge.c b/cvt/merge.c
new file mode 100644
index 0000000..b841fdf
--- /dev/null
+++ b/cvt/merge.c
@@ -0,0 +1,1139 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * This file contains routines that merge one tdata_t tree, called the child,
+ * into another, called the parent. Note that these names are used mainly for
+ * convenience and to represent the direction of the merge. They are not meant
+ * to imply any relationship between the tdata_t graphs prior to the merge.
+ *
+ * tdata_t structures contain two main elements - a hash of iidesc_t nodes, and
+ * a directed graph of tdesc_t nodes, pointed to by the iidesc_t nodes. Simply
+ * put, we merge the tdesc_t graphs, followed by the iidesc_t nodes, and then we
+ * clean up loose ends.
+ *
+ * The algorithm is as follows:
+ *
+ * 1. Mapping iidesc_t nodes
+ *
+ * For each child iidesc_t node, we first try to map its tdesc_t subgraph
+ * against the tdesc_t graph in the parent. For each node in the child subgraph
+ * that exists in the parent, a mapping between the two (between their type IDs)
+ * is established. For the child nodes that cannot be mapped onto existing
+ * parent nodes, a mapping is established between the child node ID and a
+ * newly-allocated ID that the node will use when it is re-created in the
+ * parent. These unmappable nodes are added to the md_tdtba (tdesc_t To Be
+ * Added) hash, which tracks nodes that need to be created in the parent.
+ *
+ * If all of the nodes in the subgraph for an iidesc_t in the child can be
+ * mapped to existing nodes in the parent, then we can try to map the child
+ * iidesc_t onto an iidesc_t in the parent. If we cannot find an equivalent
+ * iidesc_t, or if we were not able to completely map the tdesc_t subgraph(s),
+ * then we add this iidesc_t to the md_iitba (iidesc_t To Be Added) list. This
+ * list tracks iidesc_t nodes that are to be created in the parent.
+ *
+ * While visiting the tdesc_t nodes, we may discover a forward declaration (a
+ * FORWARD tdesc_t) in the parent that is resolved in the child. That is, there
+ * may be a structure or union definition in the child with the same name as the
+ * forward declaration in the parent. If we find such a node, we record an
+ * association in the md_fdida (Forward => Definition ID Association) list
+ * between the parent ID of the forward declaration and the ID that the
+ * definition will use when re-created in the parent.
+ *
+ * 2. Creating new tdesc_t nodes (the md_tdtba hash)
+ *
+ * We have now attempted to map all tdesc_t nodes from the child into the
+ * parent, and have, in md_tdtba, a hash of all tdesc_t nodes that need to be
+ * created (or, as we so wittily call it, conjured) in the parent. We iterate
+ * through this hash, creating the indicated tdesc_t nodes. For a given tdesc_t
+ * node, conjuring requires two steps - the copying of the common tdesc_t data
+ * (name, type, etc) from the child node, and the creation of links from the
+ * newly-created node to the parent equivalents of other tdesc_t nodes pointed
+ * to by node being conjured. Note that in some cases, the targets of these
+ * links will be on the md_tdtba hash themselves, and may not have been created
+ * yet. As such, we can't establish the links from these new nodes into the
+ * parent graph. We therefore conjure them with links to nodes in the *child*
+ * graph, and add pointers to the links to be created to the md_tdtbr (tdesc_t
+ * To Be Remapped) hash. For example, a POINTER tdesc_t that could not be
+ * resolved would have its &tdesc_t->t_tdesc added to md_tdtbr.
+ *
+ * 3. Creating new iidesc_t nodes (the md_iitba list)
+ *
+ * When we have completed step 2, all tdesc_t nodes have been created (or
+ * already existed) in the parent. Some of them may have incorrect links (the
+ * members of the md_tdtbr list), but they've all been created. As such, we can
+ * create all of the iidesc_t nodes, as we can attach the tdesc_t subgraph
+ * pointers correctly. We create each node, and attach the pointers to the
+ * appropriate parts of the parent tdesc_t graph.
+ *
+ * 4. Resolving newly-created tdesc_t node links (the md_tdtbr list)
+ *
+ * As in step 3, we rely on the fact that all of the tdesc_t nodes have been
+ * created. Each entry in the md_tdtbr list is a pointer to where a link into
+ * the parent will be established. As saved in the md_tdtbr list, these
+ * pointers point into the child tdesc_t subgraph. We can thus get the target
+ * type ID from the child, look at the ID mapping to determine the desired link
+ * target, and redirect the link accordingly.
+ *
+ * 5. Parent => child forward declaration resolution
+ *
+ * If entries were made in the md_fdida list in step 1, we have forward
+ * declarations in the parent that need to be resolved to their definitions
+ * re-created in step 2 from the child. Using the md_fdida list, we can locate
+ * the definition for the forward declaration, and we can redirect all inbound
+ * edges to the forward declaration node to the actual definition.
+ *
+ * A pox on the house of anyone who changes the algorithm without updating
+ * this comment.
+ */
+
+#include "libctf/ctf_impl.h"
+#include <assert.h>
+#include "ctftools.h"
+#include "common/list.h"
+#include "alist.h"
+#include "memory.h"
+#include "traverse.h"
+
+typedef struct equiv_data equiv_data_t;
+typedef struct merge_cb_data merge_cb_data_t;
+
+/*
+ * There are two traversals in this file, for equivalency and for tdesc_t
+ * re-creation, that do not fit into the tdtraverse() framework. We have our
+ * own traversal mechanism and ops vector here for those two cases.
+ */
+typedef struct tdesc_ops {
+ const char *name;
+ int (*equiv)(tdesc_t *, tdesc_t *, equiv_data_t *);
+ tdesc_t *(*conjure)(tdesc_t *, int, merge_cb_data_t *);
+} tdesc_ops_t;
+extern tdesc_ops_t tdesc_ops[];
+
+/*
+ * The workhorse structure of tdata_t merging. Holds all lists of nodes to be
+ * processed during various phases of the merge algorithm.
+ */
+struct merge_cb_data {
+ tdata_t *md_parent;
+ tdata_t *md_tgt;
+ alist_t *md_ta; /* Type Association */
+ alist_t *md_fdida; /* Forward -> Definition ID Association */
+ list_t **md_iitba; /* iidesc_t nodes To Be Added to the parent */
+ hash_t *md_tdtba; /* tdesc_t nodes To Be Added to the parent */
+ list_t **md_tdtbr; /* tdesc_t nodes To Be Remapped */
+ int md_flags;
+}; /* merge_cb_data_t */
+
+/*
+ * When we first create a tdata_t from stabs data, we will have duplicate nodes.
+ * Normal merges, however, assume that the child tdata_t is already self-unique,
+ * and for speed reasons do not attempt to self-uniquify. If this flag is set,
+ * the merge algorithm will self-uniquify by avoiding the insertion of
+ * duplicates in the md_tdtdba list.
+ */
+#define MCD_F_SELFUNIQUIFY 0x1
+
+/*
+ * When we merge the CTF data for the modules, we don't want it to contain any
+ * data that can be found in the reference module (usually genunix). If this
+ * flag is set, we're doing a merge between the fully merged tdata_t for this
+ * module and the tdata_t for the reference module, with the data unique to this
+ * module ending up in a third tdata_t. It is this third tdata_t that will end
+ * up in the .SUNW_ctf section for the module.
+ */
+#define MCD_F_REFMERGE 0x2
+
+/*
+ * Mapping of child type IDs to parent type IDs
+ */
+
+static void
+add_mapping(alist_t *ta, tid_t srcid, tid_t tgtid)
+{
+ debug(3, "Adding mapping %u <%x> => %u <%x>\n", srcid, srcid, tgtid, tgtid);
+
+ assert(!alist_find(ta, (void *)(uintptr_t)srcid, NULL));
+ assert(srcid != 0 && tgtid != 0);
+
+ alist_add(ta, (void *)(uintptr_t)srcid, (void *)(uintptr_t)tgtid);
+}
+
+static tid_t
+get_mapping(alist_t *ta, int srcid)
+{
+ void *ltgtid;
+
+ if (alist_find(ta, (void *)(uintptr_t)srcid, (void **)&ltgtid))
+ return ((uintptr_t)ltgtid);
+ else
+ return (0);
+}
+
+/*
+ * Determining equivalence of tdesc_t subgraphs
+ */
+
+struct equiv_data {
+ alist_t *ed_ta;
+ tdesc_t *ed_node;
+ tdesc_t *ed_tgt;
+
+ int ed_clear_mark;
+ int ed_cur_mark;
+ int ed_selfuniquify;
+}; /* equiv_data_t */
+
+static int equiv_node(tdesc_t *, tdesc_t *, equiv_data_t *);
+
+/*ARGSUSED2*/
+static int
+equiv_intrinsic(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed __unused)
+{
+ intr_t *si = stdp->t_intr;
+ intr_t *ti = ttdp->t_intr;
+
+ if (si->intr_type != ti->intr_type ||
+ si->intr_signed != ti->intr_signed ||
+ si->intr_offset != ti->intr_offset ||
+ si->intr_nbits != ti->intr_nbits)
+ return (0);
+
+ if (si->intr_type == INTR_INT &&
+ si->intr_iformat != ti->intr_iformat)
+ return (0);
+ else if (si->intr_type == INTR_REAL &&
+ si->intr_fformat != ti->intr_fformat)
+ return (0);
+
+ return (1);
+}
+
+static int
+equiv_plain(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed)
+{
+ return (equiv_node(stdp->t_tdesc, ttdp->t_tdesc, ed));
+}
+
+static int
+equiv_function(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed)
+{
+ fndef_t *fn1 = stdp->t_fndef, *fn2 = ttdp->t_fndef;
+ int i;
+
+ if (fn1->fn_nargs != fn2->fn_nargs ||
+ fn1->fn_vargs != fn2->fn_vargs)
+ return (0);
+
+ if (!equiv_node(fn1->fn_ret, fn2->fn_ret, ed))
+ return (0);
+
+ for (i = 0; i < (int) fn1->fn_nargs; i++) {
+ if (!equiv_node(fn1->fn_args[i], fn2->fn_args[i], ed))
+ return (0);
+ }
+
+ return (1);
+}
+
+static int
+equiv_array(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed)
+{
+ ardef_t *ar1 = stdp->t_ardef, *ar2 = ttdp->t_ardef;
+
+ if (!equiv_node(ar1->ad_contents, ar2->ad_contents, ed) ||
+ !equiv_node(ar1->ad_idxtype, ar2->ad_idxtype, ed))
+ return (0);
+
+ if (ar1->ad_nelems != ar2->ad_nelems)
+ return (0);
+
+ return (1);
+}
+
+static int
+equiv_su(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed)
+{
+ mlist_t *ml1 = stdp->t_members, *ml2 = ttdp->t_members;
+ mlist_t *olm1 = NULL;
+
+ while (ml1 && ml2) {
+ if (ml1->ml_offset != ml2->ml_offset ||
+ strcmp(ml1->ml_name, ml2->ml_name) != 0)
+ return (0);
+
+ /*
+ * Don't do the recursive equivalency checking more than
+ * we have to.
+ */
+ if (olm1 == NULL || olm1->ml_type->t_id != ml1->ml_type->t_id) {
+ if (ml1->ml_size != ml2->ml_size ||
+ !equiv_node(ml1->ml_type, ml2->ml_type, ed))
+ return (0);
+ }
+
+ olm1 = ml1;
+ ml1 = ml1->ml_next;
+ ml2 = ml2->ml_next;
+ }
+
+ if (ml1 || ml2)
+ return (0);
+
+ return (1);
+}
+
+/*ARGSUSED2*/
+static int
+equiv_enum(tdesc_t *stdp, tdesc_t *ttdp, equiv_data_t *ed __unused)
+{
+ elist_t *el1 = stdp->t_emem;
+ elist_t *el2 = ttdp->t_emem;
+
+ while (el1 && el2) {
+ if (el1->el_number != el2->el_number ||
+ strcmp(el1->el_name, el2->el_name) != 0)
+ return (0);
+
+ el1 = el1->el_next;
+ el2 = el2->el_next;
+ }
+
+ if (el1 || el2)
+ return (0);
+
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+equiv_assert(tdesc_t *stdp __unused, tdesc_t *ttdp __unused, equiv_data_t *ed __unused)
+{
+ /* foul, evil, and very bad - this is a "shouldn't happen" */
+ assert(1 == 0);
+
+ return (0);
+}
+
+static int
+fwd_equiv(tdesc_t *ctdp, tdesc_t *mtdp)
+{
+ tdesc_t *defn = (ctdp->t_type == FORWARD ? mtdp : ctdp);
+
+ return (defn->t_type == STRUCT || defn->t_type == UNION);
+}
+
+static int
+equiv_node(tdesc_t *ctdp, tdesc_t *mtdp, equiv_data_t *ed)
+{
+ int (*equiv)(tdesc_t *, tdesc_t *, equiv_data_t *);
+ int mapping;
+
+ if (ctdp->t_emark > ed->ed_clear_mark ||
+ mtdp->t_emark > ed->ed_clear_mark)
+ return (ctdp->t_emark == mtdp->t_emark);
+
+ /*
+ * In normal (non-self-uniquify) mode, we don't want to do equivalency
+ * checking on a subgraph that has already been checked. If a mapping
+ * has already been established for a given child node, we can simply
+ * compare the mapping for the child node with the ID of the parent
+ * node. If we are in self-uniquify mode, then we're comparing two
+ * subgraphs within the child graph, and thus need to ignore any
+ * type mappings that have been created, as they are only valid into the
+ * parent.
+ */
+ if ((mapping = get_mapping(ed->ed_ta, ctdp->t_id)) > 0 &&
+ mapping == mtdp->t_id && !ed->ed_selfuniquify)
+ return (1);
+
+ if (!streq(ctdp->t_name, mtdp->t_name))
+ return (0);
+
+ if (ctdp->t_type != mtdp->t_type) {
+ if (ctdp->t_type == FORWARD || mtdp->t_type == FORWARD)
+ return (fwd_equiv(ctdp, mtdp));
+ else
+ return (0);
+ }
+
+ ctdp->t_emark = ed->ed_cur_mark;
+ mtdp->t_emark = ed->ed_cur_mark;
+ ed->ed_cur_mark++;
+
+ if ((equiv = tdesc_ops[ctdp->t_type].equiv) != NULL)
+ return (equiv(ctdp, mtdp, ed));
+
+ return (1);
+}
+
+/*
+ * We perform an equivalency check on two subgraphs by traversing through them
+ * in lockstep. If a given node is equivalent in both the parent and the child,
+ * we mark it in both subgraphs, using the t_emark field, with a monotonically
+ * increasing number. If, in the course of the traversal, we reach a node that
+ * we have visited and numbered during this equivalency check, we have a cycle.
+ * If the previously-visited nodes don't have the same emark, then the edges
+ * that brought us to these nodes are not equivalent, and so the check ends.
+ * If the emarks are the same, the edges are equivalent. We then backtrack and
+ * continue the traversal. If we have exhausted all edges in the subgraph, and
+ * have not found any inequivalent nodes, then the subgraphs are equivalent.
+ */
+static int
+equiv_cb(void *bucket, void *arg)
+{
+ equiv_data_t *ed = arg;
+ tdesc_t *mtdp = bucket;
+ tdesc_t *ctdp = ed->ed_node;
+
+ ed->ed_clear_mark = ed->ed_cur_mark + 1;
+ ed->ed_cur_mark = ed->ed_clear_mark + 1;
+
+ if (equiv_node(ctdp, mtdp, ed)) {
+ debug(3, "equiv_node matched %d <%x> %d <%x>\n",
+ ctdp->t_id, ctdp->t_id, mtdp->t_id, mtdp->t_id);
+ ed->ed_tgt = mtdp;
+ /* matched. stop looking */
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*ARGSUSED1*/
+static int
+map_td_tree_pre(tdesc_t *ctdp, tdesc_t **ctdpp __unused, void *private)
+{
+ merge_cb_data_t *mcd = private;
+
+ if (get_mapping(mcd->md_ta, ctdp->t_id) > 0)
+ return (0);
+
+ return (1);
+}
+
+/*ARGSUSED1*/
+static int
+map_td_tree_post(tdesc_t *ctdp, tdesc_t **ctdpp __unused, void *private)
+{
+ merge_cb_data_t *mcd = private;
+ equiv_data_t ed;
+
+ ed.ed_ta = mcd->md_ta;
+ ed.ed_clear_mark = mcd->md_parent->td_curemark;
+ ed.ed_cur_mark = mcd->md_parent->td_curemark + 1;
+ ed.ed_node = ctdp;
+ ed.ed_selfuniquify = 0;
+
+ debug(3, "map_td_tree_post on %d <%x> %s\n", ctdp->t_id, ctdp->t_id,tdesc_name(ctdp));
+
+ if (hash_find_iter(mcd->md_parent->td_layouthash, ctdp,
+ equiv_cb, &ed) < 0) {
+ /* We found an equivalent node */
+ if (ed.ed_tgt->t_type == FORWARD && ctdp->t_type != FORWARD) {
+ int id = mcd->md_tgt->td_nextid++;
+
+ debug(3, "Creating new defn type %d <%x>\n", id, id);
+ add_mapping(mcd->md_ta, ctdp->t_id, id);
+ alist_add(mcd->md_fdida, (void *)(ulong_t)ed.ed_tgt,
+ (void *)(ulong_t)id);
+ hash_add(mcd->md_tdtba, ctdp);
+ } else
+ add_mapping(mcd->md_ta, ctdp->t_id, ed.ed_tgt->t_id);
+
+ } else if (debug_level > 1 && hash_iter(mcd->md_parent->td_idhash,
+ equiv_cb, &ed) < 0) {
+ /*
+ * We didn't find an equivalent node by looking through the
+ * layout hash, but we somehow found it by performing an
+ * exhaustive search through the entire graph. This usually
+ * means that the "name" hash function is broken.
+ */
+ aborterr("Second pass for %d (%s) == %d\n", ctdp->t_id,
+ tdesc_name(ctdp), ed.ed_tgt->t_id);
+ } else {
+ int id = mcd->md_tgt->td_nextid++;
+
+ debug(3, "Creating new type %d <%x>\n", id, id);
+ add_mapping(mcd->md_ta, ctdp->t_id, id);
+ hash_add(mcd->md_tdtba, ctdp);
+ }
+
+ mcd->md_parent->td_curemark = ed.ed_cur_mark + 1;
+
+ return (1);
+}
+
+/*ARGSUSED1*/
+static int
+map_td_tree_self_post(tdesc_t *ctdp, tdesc_t **ctdpp __unused, void *private)
+{
+ merge_cb_data_t *mcd = private;
+ equiv_data_t ed;
+
+ ed.ed_ta = mcd->md_ta;
+ ed.ed_clear_mark = mcd->md_parent->td_curemark;
+ ed.ed_cur_mark = mcd->md_parent->td_curemark + 1;
+ ed.ed_node = ctdp;
+ ed.ed_selfuniquify = 1;
+ ed.ed_tgt = NULL;
+
+ if (hash_find_iter(mcd->md_tdtba, ctdp, equiv_cb, &ed) < 0) {
+ debug(3, "Self check found %d <%x> in %d <%x>\n", ctdp->t_id,
+ ctdp->t_id, ed.ed_tgt->t_id, ed.ed_tgt->t_id);
+ add_mapping(mcd->md_ta, ctdp->t_id,
+ get_mapping(mcd->md_ta, ed.ed_tgt->t_id));
+ } else if (debug_level > 1 && hash_iter(mcd->md_tdtba,
+ equiv_cb, &ed) < 0) {
+ /*
+ * We didn't find an equivalent node using the quick way (going
+ * through the hash normally), but we did find it by iterating
+ * through the entire hash. This usually means that the hash
+ * function is broken.
+ */
+ aborterr("Self-unique second pass for %d <%x> (%s) == %d <%x>\n",
+ ctdp->t_id, ctdp->t_id, tdesc_name(ctdp), ed.ed_tgt->t_id,
+ ed.ed_tgt->t_id);
+ } else {
+ int id = mcd->md_tgt->td_nextid++;
+
+ debug(3, "Creating new type %d <%x>\n", id, id);
+ add_mapping(mcd->md_ta, ctdp->t_id, id);
+ hash_add(mcd->md_tdtba, ctdp);
+ }
+
+ mcd->md_parent->td_curemark = ed.ed_cur_mark + 1;
+
+ return (1);
+}
+
+static tdtrav_cb_f map_pre[] = {
+ NULL,
+ map_td_tree_pre, /* intrinsic */
+ map_td_tree_pre, /* pointer */
+ map_td_tree_pre, /* array */
+ map_td_tree_pre, /* function */
+ map_td_tree_pre, /* struct */
+ map_td_tree_pre, /* union */
+ map_td_tree_pre, /* enum */
+ map_td_tree_pre, /* forward */
+ map_td_tree_pre, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ map_td_tree_pre, /* volatile */
+ map_td_tree_pre, /* const */
+ map_td_tree_pre /* restrict */
+};
+
+static tdtrav_cb_f map_post[] = {
+ NULL,
+ map_td_tree_post, /* intrinsic */
+ map_td_tree_post, /* pointer */
+ map_td_tree_post, /* array */
+ map_td_tree_post, /* function */
+ map_td_tree_post, /* struct */
+ map_td_tree_post, /* union */
+ map_td_tree_post, /* enum */
+ map_td_tree_post, /* forward */
+ map_td_tree_post, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ map_td_tree_post, /* volatile */
+ map_td_tree_post, /* const */
+ map_td_tree_post /* restrict */
+};
+
+static tdtrav_cb_f map_self_post[] = {
+ NULL,
+ map_td_tree_self_post, /* intrinsic */
+ map_td_tree_self_post, /* pointer */
+ map_td_tree_self_post, /* array */
+ map_td_tree_self_post, /* function */
+ map_td_tree_self_post, /* struct */
+ map_td_tree_self_post, /* union */
+ map_td_tree_self_post, /* enum */
+ map_td_tree_self_post, /* forward */
+ map_td_tree_self_post, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ map_td_tree_self_post, /* volatile */
+ map_td_tree_self_post, /* const */
+ map_td_tree_self_post /* restrict */
+};
+
+/*
+ * Determining equivalence of iidesc_t nodes
+ */
+
+typedef struct iifind_data {
+ iidesc_t *iif_template;
+ alist_t *iif_ta;
+ int iif_newidx;
+ int iif_refmerge;
+} iifind_data_t;
+
+/*
+ * Check to see if this iidesc_t (node) - the current one on the list we're
+ * iterating through - matches the target one (iif->iif_template). Return -1
+ * if it matches, to stop the iteration.
+ */
+static int
+iidesc_match(void *data, void *arg)
+{
+ iidesc_t *node = data;
+ iifind_data_t *iif = arg;
+ int i;
+
+ if (node->ii_type != iif->iif_template->ii_type ||
+ !streq(node->ii_name, iif->iif_template->ii_name) ||
+ node->ii_dtype->t_id != iif->iif_newidx)
+ return (0);
+
+ if ((node->ii_type == II_SVAR || node->ii_type == II_SFUN) &&
+ !streq(node->ii_owner, iif->iif_template->ii_owner))
+ return (0);
+
+ if (node->ii_nargs != iif->iif_template->ii_nargs)
+ return (0);
+
+ for (i = 0; i < node->ii_nargs; i++) {
+ if (get_mapping(iif->iif_ta,
+ iif->iif_template->ii_args[i]->t_id) !=
+ node->ii_args[i]->t_id)
+ return (0);
+ }
+
+ if (iif->iif_refmerge) {
+ switch (iif->iif_template->ii_type) {
+ case II_GFUN:
+ case II_SFUN:
+ case II_GVAR:
+ case II_SVAR:
+ debug(3, "suppressing duping of %d %s from %s\n",
+ iif->iif_template->ii_type,
+ iif->iif_template->ii_name,
+ (iif->iif_template->ii_owner ?
+ iif->iif_template->ii_owner : "NULL"));
+ return (0);
+ case II_NOT:
+ case II_PSYM:
+ case II_SOU:
+ case II_TYPE:
+ break;
+ }
+ }
+
+ return (-1);
+}
+
+static int
+merge_type_cb(void *data, void *arg)
+{
+ iidesc_t *sii = data;
+ merge_cb_data_t *mcd = arg;
+ iifind_data_t iif;
+ tdtrav_cb_f *post;
+
+ post = (mcd->md_flags & MCD_F_SELFUNIQUIFY ? map_self_post : map_post);
+
+ /* Map the tdesc nodes */
+ (void) iitraverse(sii, &mcd->md_parent->td_curvgen, NULL, map_pre, post,
+ mcd);
+
+ /* Map the iidesc nodes */
+ iif.iif_template = sii;
+ iif.iif_ta = mcd->md_ta;
+ iif.iif_newidx = get_mapping(mcd->md_ta, sii->ii_dtype->t_id);
+ iif.iif_refmerge = (mcd->md_flags & MCD_F_REFMERGE);
+
+ if (hash_match(mcd->md_parent->td_iihash, sii, iidesc_match,
+ &iif) == 1)
+ /* successfully mapped */
+ return (1);
+
+ debug(3, "tba %s (%d)\n", (sii->ii_name ? sii->ii_name : "(anon)"),
+ sii->ii_type);
+
+ list_add(mcd->md_iitba, sii);
+
+ return (0);
+}
+
+static int
+remap_node(tdesc_t **tgtp, tdesc_t *oldtgt, int selftid, tdesc_t *newself,
+ merge_cb_data_t *mcd)
+{
+ tdesc_t *tgt = NULL;
+ tdesc_t template;
+ int oldid = oldtgt->t_id;
+
+ if (oldid == selftid) {
+ *tgtp = newself;
+ return (1);
+ }
+
+ if ((template.t_id = get_mapping(mcd->md_ta, oldid)) == 0)
+ aborterr("failed to get mapping for tid %d <%x>\n", oldid, oldid);
+
+ if (!hash_find(mcd->md_parent->td_idhash, (void *)&template,
+ (void *)&tgt) && (!(mcd->md_flags & MCD_F_REFMERGE) ||
+ !hash_find(mcd->md_tgt->td_idhash, (void *)&template,
+ (void *)&tgt))) {
+ debug(3, "Remap couldn't find %d <%x> (from %d <%x>)\n", template.t_id,
+ template.t_id, oldid, oldid);
+ *tgtp = oldtgt;
+ list_add(mcd->md_tdtbr, tgtp);
+ return (0);
+ }
+
+ *tgtp = tgt;
+ return (1);
+}
+
+static tdesc_t *
+conjure_template(tdesc_t *old, int newselfid)
+{
+ tdesc_t *new = xcalloc(sizeof (tdesc_t));
+
+ new->t_name = old->t_name ? xstrdup(old->t_name) : NULL;
+ new->t_type = old->t_type;
+ new->t_size = old->t_size;
+ new->t_id = newselfid;
+ new->t_flags = old->t_flags;
+
+ return (new);
+}
+
+/*ARGSUSED2*/
+static tdesc_t *
+conjure_intrinsic(tdesc_t *old, int newselfid, merge_cb_data_t *mcd __unused)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+
+ new->t_intr = xmalloc(sizeof (intr_t));
+ bcopy(old->t_intr, new->t_intr, sizeof (intr_t));
+
+ return (new);
+}
+
+static tdesc_t *
+conjure_plain(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+
+ (void) remap_node(&new->t_tdesc, old->t_tdesc, old->t_id, new, mcd);
+
+ return (new);
+}
+
+static tdesc_t *
+conjure_function(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+ fndef_t *nfn = xmalloc(sizeof (fndef_t));
+ fndef_t *ofn = old->t_fndef;
+ int i;
+
+ (void) remap_node(&nfn->fn_ret, ofn->fn_ret, old->t_id, new, mcd);
+
+ nfn->fn_nargs = ofn->fn_nargs;
+ nfn->fn_vargs = ofn->fn_vargs;
+
+ if (nfn->fn_nargs > 0)
+ nfn->fn_args = xcalloc(sizeof (tdesc_t *) * ofn->fn_nargs);
+
+ for (i = 0; i < (int) ofn->fn_nargs; i++) {
+ (void) remap_node(&nfn->fn_args[i], ofn->fn_args[i], old->t_id,
+ new, mcd);
+ }
+
+ new->t_fndef = nfn;
+
+ return (new);
+}
+
+static tdesc_t *
+conjure_array(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+ ardef_t *nar = xmalloc(sizeof (ardef_t));
+ ardef_t *oar = old->t_ardef;
+
+ (void) remap_node(&nar->ad_contents, oar->ad_contents, old->t_id, new,
+ mcd);
+ (void) remap_node(&nar->ad_idxtype, oar->ad_idxtype, old->t_id, new,
+ mcd);
+
+ nar->ad_nelems = oar->ad_nelems;
+
+ new->t_ardef = nar;
+
+ return (new);
+}
+
+static tdesc_t *
+conjure_su(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+ mlist_t *omem, **nmemp;
+
+ for (omem = old->t_members, nmemp = &new->t_members;
+ omem; omem = omem->ml_next, nmemp = &((*nmemp)->ml_next)) {
+ *nmemp = xmalloc(sizeof (mlist_t));
+ (*nmemp)->ml_offset = omem->ml_offset;
+ (*nmemp)->ml_size = omem->ml_size;
+ (*nmemp)->ml_name = xstrdup(omem->ml_name ? omem->ml_name : "empty omem->ml_name");
+ (void) remap_node(&((*nmemp)->ml_type), omem->ml_type,
+ old->t_id, new, mcd);
+ }
+ *nmemp = NULL;
+
+ return (new);
+}
+
+/*ARGSUSED2*/
+static tdesc_t *
+conjure_enum(tdesc_t *old, int newselfid, merge_cb_data_t *mcd __unused)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+ elist_t *oel, **nelp;
+
+ for (oel = old->t_emem, nelp = &new->t_emem;
+ oel; oel = oel->el_next, nelp = &((*nelp)->el_next)) {
+ *nelp = xmalloc(sizeof (elist_t));
+ (*nelp)->el_name = xstrdup(oel->el_name);
+ (*nelp)->el_number = oel->el_number;
+ }
+ *nelp = NULL;
+
+ return (new);
+}
+
+/*ARGSUSED2*/
+static tdesc_t *
+conjure_forward(tdesc_t *old, int newselfid, merge_cb_data_t *mcd)
+{
+ tdesc_t *new = conjure_template(old, newselfid);
+
+ list_add(&mcd->md_tgt->td_fwdlist, new);
+
+ return (new);
+}
+
+/*ARGSUSED*/
+static tdesc_t *
+conjure_assert(tdesc_t *old __unused, int newselfid __unused, merge_cb_data_t *mcd __unused)
+{
+ assert(1 == 0);
+ return (NULL);
+}
+
+static iidesc_t *
+conjure_iidesc(iidesc_t *old, merge_cb_data_t *mcd)
+{
+ iidesc_t *new = iidesc_dup(old);
+ int i;
+
+ (void) remap_node(&new->ii_dtype, old->ii_dtype, -1, NULL, mcd);
+ for (i = 0; i < new->ii_nargs; i++) {
+ (void) remap_node(&new->ii_args[i], old->ii_args[i], -1, NULL,
+ mcd);
+ }
+
+ return (new);
+}
+
+static int
+fwd_redir(tdesc_t *fwd, tdesc_t **fwdp, void *private)
+{
+ alist_t *map = private;
+ void *defn;
+
+ if (!alist_find(map, (void *)fwd, (void **)&defn))
+ return (0);
+
+ debug(3, "Redirecting an edge to %s\n", tdesc_name(defn));
+
+ *fwdp = defn;
+
+ return (1);
+}
+
+static tdtrav_cb_f fwd_redir_cbs[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ NULL, /* array */
+ NULL, /* function */
+ NULL, /* struct */
+ NULL, /* union */
+ NULL, /* enum */
+ fwd_redir, /* forward */
+ NULL, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL /* restrict */
+};
+
+typedef struct redir_mstr_data {
+ tdata_t *rmd_tgt;
+ alist_t *rmd_map;
+} redir_mstr_data_t;
+
+static int
+redir_mstr_fwd_cb(void *name, void *value, void *arg)
+{
+ tdesc_t *fwd = name;
+ int defnid = (uintptr_t)value;
+ redir_mstr_data_t *rmd = arg;
+ tdesc_t template;
+ tdesc_t *defn;
+
+ template.t_id = defnid;
+
+ if (!hash_find(rmd->rmd_tgt->td_idhash, (void *)&template,
+ (void *)&defn)) {
+ aborterr("Couldn't unforward %d (%s)\n", defnid,
+ tdesc_name(defn));
+ }
+
+ debug(3, "Forward map: resolved %d to %s\n", defnid, tdesc_name(defn));
+
+ alist_add(rmd->rmd_map, (void *)fwd, (void *)defn);
+
+ return (1);
+}
+
+static void
+redir_mstr_fwds(merge_cb_data_t *mcd)
+{
+ redir_mstr_data_t rmd;
+ alist_t *map = alist_new(NULL, NULL);
+
+ rmd.rmd_tgt = mcd->md_tgt;
+ rmd.rmd_map = map;
+
+ if (alist_iter(mcd->md_fdida, redir_mstr_fwd_cb, &rmd)) {
+ (void) iitraverse_hash(mcd->md_tgt->td_iihash,
+ &mcd->md_tgt->td_curvgen, fwd_redir_cbs, NULL, NULL, map);
+ }
+
+ alist_free(map);
+}
+
+static int
+add_iitba_cb(void *data, void *private)
+{
+ merge_cb_data_t *mcd = private;
+ iidesc_t *tba = data;
+ iidesc_t *new;
+ iifind_data_t iif;
+ int newidx;
+
+ newidx = get_mapping(mcd->md_ta, tba->ii_dtype->t_id);
+ assert(newidx != -1);
+
+ (void) list_remove(mcd->md_iitba, data, NULL, NULL);
+
+ iif.iif_template = tba;
+ iif.iif_ta = mcd->md_ta;
+ iif.iif_newidx = newidx;
+ iif.iif_refmerge = (mcd->md_flags & MCD_F_REFMERGE);
+
+ if (hash_match(mcd->md_parent->td_iihash, tba, iidesc_match,
+ &iif) == 1) {
+ debug(3, "iidesc_t %s already exists\n",
+ (tba->ii_name ? tba->ii_name : "(anon)"));
+ return (1);
+ }
+
+ new = conjure_iidesc(tba, mcd);
+ hash_add(mcd->md_tgt->td_iihash, new);
+
+ return (1);
+}
+
+static int
+add_tdesc(tdesc_t *oldtdp, int newid, merge_cb_data_t *mcd)
+{
+ tdesc_t *newtdp;
+ tdesc_t template;
+
+ template.t_id = newid;
+ assert(hash_find(mcd->md_parent->td_idhash,
+ (void *)&template, NULL) == 0);
+
+ debug(3, "trying to conjure %d %s (%d, <%x>) as %d, <%x>\n",
+ oldtdp->t_type, tdesc_name(oldtdp), oldtdp->t_id,
+ oldtdp->t_id, newid, newid);
+
+ if ((newtdp = tdesc_ops[oldtdp->t_type].conjure(oldtdp, newid,
+ mcd)) == NULL)
+ /* couldn't map everything */
+ return (0);
+
+ debug(3, "succeeded\n");
+
+ hash_add(mcd->md_tgt->td_idhash, newtdp);
+ hash_add(mcd->md_tgt->td_layouthash, newtdp);
+
+ return (1);
+}
+
+static int
+add_tdtba_cb(void *data, void *arg)
+{
+ tdesc_t *tdp = data;
+ merge_cb_data_t *mcd = arg;
+ int newid;
+ int rc;
+
+ newid = get_mapping(mcd->md_ta, tdp->t_id);
+ assert(newid != -1);
+
+ if ((rc = add_tdesc(tdp, newid, mcd)))
+ hash_remove(mcd->md_tdtba, (void *)tdp);
+
+ return (rc);
+}
+
+static int
+add_tdtbr_cb(void *data, void *arg)
+{
+ tdesc_t **tdpp = data;
+ merge_cb_data_t *mcd = arg;
+
+ debug(3, "Remapping %s (%d)\n", tdesc_name(*tdpp), (*tdpp)->t_id);
+
+ if (!remap_node(tdpp, *tdpp, -1, NULL, mcd))
+ return (0);
+
+ (void) list_remove(mcd->md_tdtbr, (void *)tdpp, NULL, NULL);
+ return (1);
+}
+
+static void
+merge_types(hash_t *src, merge_cb_data_t *mcd)
+{
+ list_t *iitba = NULL;
+ list_t *tdtbr = NULL;
+ int iirc, tdrc;
+
+ mcd->md_iitba = &iitba;
+ mcd->md_tdtba = hash_new(TDATA_LAYOUT_HASH_SIZE, tdesc_layouthash,
+ tdesc_layoutcmp);
+ mcd->md_tdtbr = &tdtbr;
+
+ (void) hash_iter(src, merge_type_cb, mcd);
+
+ tdrc = hash_iter(mcd->md_tdtba, add_tdtba_cb, mcd);
+ debug(3, "add_tdtba_cb added %d items\n", tdrc);
+
+ iirc = list_iter(*mcd->md_iitba, add_iitba_cb, mcd);
+ debug(3, "add_iitba_cb added %d items\n", iirc);
+
+ assert(list_count(*mcd->md_iitba) == 0 &&
+ hash_count(mcd->md_tdtba) == 0);
+
+ tdrc = list_iter(*mcd->md_tdtbr, add_tdtbr_cb, mcd);
+ debug(3, "add_tdtbr_cb added %d items\n", tdrc);
+
+ if (list_count(*mcd->md_tdtbr) != 0)
+ aborterr("Couldn't remap all nodes\n");
+
+ /*
+ * We now have an alist of master forwards and the ids of the new master
+ * definitions for those forwards in mcd->md_fdida. By this point,
+ * we're guaranteed that all of the master definitions referenced in
+ * fdida have been added to the master tree. We now traverse through
+ * the master tree, redirecting all edges inbound to forwards that have
+ * definitions to those definitions.
+ */
+ if (mcd->md_parent == mcd->md_tgt) {
+ redir_mstr_fwds(mcd);
+ }
+}
+
+void
+merge_into_master(tdata_t *cur, tdata_t *mstr, tdata_t *tgt, int selfuniquify)
+{
+ merge_cb_data_t mcd;
+
+ cur->td_ref++;
+ mstr->td_ref++;
+ if (tgt)
+ tgt->td_ref++;
+
+ assert(cur->td_ref == 1 && mstr->td_ref == 1 &&
+ (tgt == NULL || tgt->td_ref == 1));
+
+ mcd.md_parent = mstr;
+ mcd.md_tgt = (tgt ? tgt : mstr);
+ mcd.md_ta = alist_new(NULL, NULL);
+ mcd.md_fdida = alist_new(NULL, NULL);
+ mcd.md_flags = 0;
+
+ if (selfuniquify)
+ mcd.md_flags |= MCD_F_SELFUNIQUIFY;
+ if (tgt)
+ mcd.md_flags |= MCD_F_REFMERGE;
+
+ mstr->td_curvgen = MAX(mstr->td_curvgen, cur->td_curvgen);
+ mstr->td_curemark = MAX(mstr->td_curemark, cur->td_curemark);
+
+ merge_types(cur->td_iihash, &mcd);
+
+ if (debug_level >= 3) {
+ debug(3, "Type association stats\n");
+ alist_stats(mcd.md_ta, 0);
+ debug(3, "Layout hash stats\n");
+ hash_stats(mcd.md_tgt->td_layouthash, 1);
+ }
+
+ alist_free(mcd.md_fdida);
+ alist_free(mcd.md_ta);
+
+ cur->td_ref--;
+ mstr->td_ref--;
+ if (tgt)
+ tgt->td_ref--;
+}
+
+tdesc_ops_t tdesc_ops[] = {
+ { "ERROR! BAD tdesc TYPE", NULL, NULL },
+ { "intrinsic", equiv_intrinsic, conjure_intrinsic },
+ { "pointer", equiv_plain, conjure_plain },
+ { "array", equiv_array, conjure_array },
+ { "function", equiv_function, conjure_function },
+ { "struct", equiv_su, conjure_su },
+ { "union", equiv_su, conjure_su },
+ { "enum", equiv_enum, conjure_enum },
+ { "forward", NULL, conjure_forward },
+ { "typedef", equiv_plain, conjure_plain },
+ { "typedef_unres", equiv_assert, conjure_assert },
+ { "volatile", equiv_plain, conjure_plain },
+ { "const", equiv_plain, conjure_plain },
+ { "restrict", equiv_plain, conjure_plain }
+};
diff --git a/cvt/output.c b/cvt/output.c
new file mode 100644
index 0000000..6546351
--- /dev/null
+++ b/cvt/output.c
@@ -0,0 +1,748 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for preparing tdata trees for conversion into CTF data, and
+ * for placing the resulting data into an output file.
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "common/list.h"
+#include "memory.h"
+#include "traverse.h"
+#include "common/symbol.h"
+
+typedef struct iidesc_match {
+ int iim_fuzzy;
+ iidesc_t *iim_ret;
+ char *iim_name;
+ char *iim_file;
+ uchar_t iim_bind;
+} iidesc_match_t;
+
+static int
+burst_iitypes(void *data, void *arg)
+{
+ iidesc_t *ii = data;
+ iiburst_t *iiburst = arg;
+
+ switch (ii->ii_type) {
+ case II_GFUN:
+ case II_SFUN:
+ case II_GVAR:
+ case II_SVAR:
+ if (!(ii->ii_flags & IIDESC_F_USED))
+ return (0);
+ break;
+ default:
+ break;
+ }
+
+ ii->ii_dtype->t_flags |= TDESC_F_ISROOT;
+ (void) iitraverse_td(ii, iiburst->iib_tdtd);
+ return (1);
+}
+
+/*ARGSUSED1*/
+static int
+save_type_by_id(tdesc_t *tdp, tdesc_t **tdpp __unused, void *private)
+{
+ iiburst_t *iiburst = private;
+
+ /*
+ * Doing this on every node is horribly inefficient, but given that
+ * we may be suppressing some types, we can't trust nextid in the
+ * tdata_t.
+ */
+ if (tdp->t_id > iiburst->iib_maxtypeid)
+ iiburst->iib_maxtypeid = tdp->t_id;
+
+ slist_add(&iiburst->iib_types, tdp, tdesc_idcmp);
+
+ return (1);
+}
+
+static tdtrav_cb_f burst_types_cbs[] = {
+ NULL,
+ save_type_by_id, /* intrinsic */
+ save_type_by_id, /* pointer */
+ save_type_by_id, /* array */
+ save_type_by_id, /* function */
+ save_type_by_id, /* struct */
+ save_type_by_id, /* union */
+ save_type_by_id, /* enum */
+ save_type_by_id, /* forward */
+ save_type_by_id, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ save_type_by_id, /* volatile */
+ save_type_by_id, /* const */
+ save_type_by_id /* restrict */
+};
+
+
+static iiburst_t *
+iiburst_new(tdata_t *td, int max)
+{
+ iiburst_t *iiburst = xcalloc(sizeof (iiburst_t));
+ iiburst->iib_td = td;
+ iiburst->iib_funcs = xcalloc(sizeof (iidesc_t *) * max);
+ iiburst->iib_nfuncs = 0;
+ iiburst->iib_objts = xcalloc(sizeof (iidesc_t *) * max);
+ iiburst->iib_nobjts = 0;
+ return (iiburst);
+}
+
+static void
+iiburst_types(iiburst_t *iiburst)
+{
+ tdtrav_data_t tdtd;
+
+ tdtrav_init(&tdtd, &iiburst->iib_td->td_curvgen, NULL, burst_types_cbs,
+ NULL, (void *)iiburst);
+
+ iiburst->iib_tdtd = &tdtd;
+
+ (void) hash_iter(iiburst->iib_td->td_iihash, burst_iitypes, iiburst);
+}
+
+static void
+iiburst_free(iiburst_t *iiburst)
+{
+ free(iiburst->iib_funcs);
+ free(iiburst->iib_objts);
+ list_free(iiburst->iib_types, NULL, NULL);
+ free(iiburst);
+}
+
+/*
+ * See if this iidesc matches the ELF symbol data we pass in.
+ *
+ * A fuzzy match is where we have a local symbol matching the name of a
+ * global type description. This is common when a mapfile is used for a
+ * DSO, but we don't accept it by default.
+ *
+ * A weak fuzzy match is when a weak symbol was resolved and matched to
+ * a global type description.
+ */
+static int
+matching_iidesc(void *arg1, void *arg2)
+{
+ iidesc_t *iidesc = arg1;
+ iidesc_match_t *match = arg2;
+ if (streq(iidesc->ii_name, match->iim_name) == 0)
+ return (0);
+
+ switch (iidesc->ii_type) {
+ case II_GFUN:
+ case II_GVAR:
+ if (match->iim_bind == STB_GLOBAL) {
+ match->iim_ret = iidesc;
+ return (-1);
+ } else if (match->iim_fuzzy && match->iim_ret == NULL) {
+ match->iim_ret = iidesc;
+ /* continue to look for strong match */
+ return (0);
+ }
+ break;
+ case II_SFUN:
+ case II_SVAR:
+ if (match->iim_bind == STB_LOCAL &&
+ match->iim_file != NULL &&
+ streq(iidesc->ii_owner, match->iim_file)) {
+ match->iim_ret = iidesc;
+ return (-1);
+ }
+ break;
+ default:
+ break;
+ }
+ return (0);
+}
+
+static iidesc_t *
+find_iidesc(tdata_t *td, iidesc_match_t *match)
+{
+ match->iim_ret = NULL;
+ iter_iidescs_by_name(td, match->iim_name,
+ matching_iidesc, match);
+ return (match->iim_ret);
+}
+
+/*
+ * If we have a weak symbol, attempt to find the strong symbol it will
+ * resolve to. Note: the code where this actually happens is in
+ * sym_process() in cmd/sgs/libld/common/syms.c
+ *
+ * Finding the matching symbol is unfortunately not trivial. For a
+ * symbol to be a candidate, it must:
+ *
+ * - have the same type (function, object)
+ * - have the same value (address)
+ * - have the same size
+ * - not be another weak symbol
+ * - belong to the same section (checked via section index)
+ *
+ * If such a candidate is global, then we assume we've found it. The
+ * linker generates the symbol table such that the curfile might be
+ * incorrect; this is OK for global symbols, since find_iidesc() doesn't
+ * need to check for the source file for the symbol.
+ *
+ * We might have found a strong local symbol, where the curfile is
+ * accurate and matches that of the weak symbol. We assume this is a
+ * reasonable match.
+ *
+ * If we've got a local symbol with a non-matching curfile, there are
+ * two possibilities. Either this is a completely different symbol, or
+ * it's a once-global symbol that was scoped to local via a mapfile. In
+ * the latter case, curfile is likely inaccurate since the linker does
+ * not preserve the needed curfile in the order of the symbol table (see
+ * the comments about locally scoped symbols in libld's update_osym()).
+ * As we can't tell this case from the former one, we use this symbol
+ * iff no other matching symbol is found.
+ *
+ * What we really need here is a SUNW section containing weak<->strong
+ * mappings that we can consume.
+ */
+static int
+check_for_weak(GElf_Sym *weak, char const *weakfile,
+ Elf_Data *data, int nent, Elf_Data *strdata,
+ GElf_Sym *retsym, char **curfilep)
+{
+ char *curfile = NULL;
+ char *tmpfile1 = NULL;
+ GElf_Sym tmpsym;
+ int candidate = 0;
+ int i;
+ tmpsym.st_info = 0;
+ tmpsym.st_name = 0;
+
+ if (GELF_ST_BIND(weak->st_info) != STB_WEAK)
+ return (0);
+
+ for (i = 0; i < nent; i++) {
+ GElf_Sym sym;
+ uchar_t type;
+
+ if (gelf_getsym(data, i, &sym) == NULL)
+ continue;
+
+ type = GELF_ST_TYPE(sym.st_info);
+
+ if (type == STT_FILE)
+ curfile = (char *)strdata->d_buf + sym.st_name;
+
+ if (GELF_ST_TYPE(weak->st_info) != type ||
+ weak->st_value != sym.st_value)
+ continue;
+
+ if (weak->st_size != sym.st_size)
+ continue;
+
+ if (GELF_ST_BIND(sym.st_info) == STB_WEAK)
+ continue;
+
+ if (sym.st_shndx != weak->st_shndx)
+ continue;
+
+ if (GELF_ST_BIND(sym.st_info) == STB_LOCAL &&
+ (curfile == NULL || weakfile == NULL ||
+ strcmp(curfile, weakfile) != 0)) {
+ candidate = 1;
+ tmpfile1 = curfile;
+ tmpsym = sym;
+ continue;
+ }
+
+ *curfilep = curfile;
+ *retsym = sym;
+ return (1);
+ }
+
+ if (candidate) {
+ *curfilep = tmpfile1;
+ *retsym = tmpsym;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * When we've found the underlying symbol's type description
+ * for a weak symbol, we need to copy it and rename it to match
+ * the weak symbol. We also need to add it to the td so it's
+ * handled along with the others later.
+ */
+static iidesc_t *
+copy_from_strong(tdata_t *td, GElf_Sym *sym, iidesc_t *strongdesc,
+ const char *weakname, const char *weakfile)
+{
+ iidesc_t *new = iidesc_dup_rename(strongdesc, weakname, weakfile);
+ uchar_t type = GELF_ST_TYPE(sym->st_info);
+
+ switch (type) {
+ case STT_OBJECT:
+ new->ii_type = II_GVAR;
+ break;
+ case STT_FUNC:
+ new->ii_type = II_GFUN;
+ break;
+ }
+
+ hash_add(td->td_iihash, new);
+
+ return (new);
+}
+
+/*
+ * Process the symbol table of the output file, associating each symbol
+ * with a type description if possible, and sorting them into functions
+ * and data, maintaining symbol table order.
+ */
+static iiburst_t *
+sort_iidescs(Elf *elf, const char *file, tdata_t *td, int fuzzymatch,
+ int dynsym)
+{
+ iiburst_t *iiburst;
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+ Elf_Data *data, *strdata;
+ int i, stidx;
+ int nent;
+ iidesc_match_t match;
+
+ match.iim_fuzzy = fuzzymatch;
+ match.iim_file = NULL;
+
+ if ((stidx = findelfsecidx(elf, file,
+ dynsym ? ".dynsym" : ".symtab")) < 0)
+ terminate("%s: Can't open symbol table\n", file);
+ scn = elf_getscn(elf, stidx);
+ data = elf_getdata(scn, NULL);
+ gelf_getshdr(scn, &shdr);
+ nent = shdr.sh_size / shdr.sh_entsize;
+
+ scn = elf_getscn(elf, shdr.sh_link);
+ strdata = elf_getdata(scn, NULL);
+
+ iiburst = iiburst_new(td, nent);
+
+ for (i = 0; i < nent; i++) {
+ GElf_Sym sym;
+ iidesc_t **tolist;
+ GElf_Sym ssym;
+ iidesc_match_t smatch;
+ int *curr;
+ iidesc_t *iidesc;
+
+ if (gelf_getsym(data, i, &sym) == NULL)
+ elfterminate(file, "Couldn't read symbol %d", i);
+
+ match.iim_name = (char *)strdata->d_buf + sym.st_name;
+ match.iim_bind = GELF_ST_BIND(sym.st_info);
+
+ switch (GELF_ST_TYPE(sym.st_info)) {
+ case STT_FILE:
+ match.iim_file = match.iim_name;
+ continue;
+ case STT_OBJECT:
+ tolist = iiburst->iib_objts;
+ curr = &iiburst->iib_nobjts;
+ break;
+ case STT_FUNC:
+ tolist = iiburst->iib_funcs;
+ curr = &iiburst->iib_nfuncs;
+ break;
+ default:
+ continue;
+ }
+
+ if (ignore_symbol(&sym, match.iim_name))
+ continue;
+
+ iidesc = find_iidesc(td, &match);
+
+ if (iidesc != NULL) {
+ tolist[*curr] = iidesc;
+ iidesc->ii_flags |= IIDESC_F_USED;
+ (*curr)++;
+ continue;
+ }
+
+ if (!check_for_weak(&sym, match.iim_file, data, nent, strdata,
+ &ssym, &smatch.iim_file)) {
+ (*curr)++;
+ continue;
+ }
+
+ smatch.iim_fuzzy = fuzzymatch;
+ smatch.iim_name = (char *)strdata->d_buf + ssym.st_name;
+ smatch.iim_bind = GELF_ST_BIND(ssym.st_info);
+
+ debug(3, "Weak symbol %s resolved to %s\n", match.iim_name,
+ smatch.iim_name);
+
+ iidesc = find_iidesc(td, &smatch);
+
+ if (iidesc != NULL) {
+ tolist[*curr] = copy_from_strong(td, &sym,
+ iidesc, match.iim_name, match.iim_file);
+ tolist[*curr]->ii_flags |= IIDESC_F_USED;
+ }
+
+ (*curr)++;
+ }
+
+ /*
+ * Stabs are generated for every function declared in a given C source
+ * file. When converting an object file, we may encounter a stab that
+ * has no symbol table entry because the optimizer has decided to omit
+ * that item (for example, an unreferenced static function). We may
+ * see iidescs that do not have an associated symtab entry, and so
+ * we do not write records for those functions into the CTF data.
+ * All others get marked as a root by this function.
+ */
+ iiburst_types(iiburst);
+
+ /*
+ * By not adding some of the functions and/or objects, we may have
+ * caused some types that were referenced solely by those
+ * functions/objects to be suppressed. This could cause a label,
+ * generated prior to the evisceration, to be incorrect. Find the
+ * highest type index, and change the label indicies to be no higher
+ * than this value.
+ */
+ tdata_label_newmax(td, iiburst->iib_maxtypeid);
+
+ return (iiburst);
+}
+
+static void
+write_file(Elf *src, const char *srcname, Elf *dst, const char *dstname,
+ caddr_t ctfdata, size_t ctfsize, int flags)
+{
+ GElf_Ehdr sehdr, dehdr;
+ Elf_Scn *sscn, *dscn;
+ Elf_Data *sdata, *ddata;
+ GElf_Shdr shdr;
+ GElf_Word symtab_type;
+ int symtab_idx = -1;
+ off_t new_offset = 0;
+ off_t ctfnameoff = 0;
+ int dynsym = (flags & CTF_USE_DYNSYM);
+ int keep_stabs = (flags & CTF_KEEP_STABS);
+ int *secxlate;
+ int srcidx, dstidx;
+ int curnmoff = 0;
+ int changing = 0;
+ int pad;
+ int i;
+
+ if (gelf_newehdr(dst, gelf_getclass(src)) == NULL)
+ elfterminate(dstname, "Cannot copy ehdr to temp file");
+ gelf_getehdr(src, &sehdr);
+ memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr));
+ gelf_update_ehdr(dst, &dehdr);
+
+ symtab_type = dynsym ? SHT_DYNSYM : SHT_SYMTAB;
+
+ /*
+ * Neither the existing stab sections nor the SUNW_ctf sections (new or
+ * existing) are SHF_ALLOC'd, so they won't be in areas referenced by
+ * program headers. As such, we can just blindly copy the program
+ * headers from the existing file to the new file.
+ */
+ if (sehdr.e_phnum != 0) {
+ (void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT);
+ if (gelf_newphdr(dst, sehdr.e_phnum) == NULL)
+ elfterminate(dstname, "Cannot make phdrs in temp file");
+
+ for (i = 0; i < sehdr.e_phnum; i++) {
+ GElf_Phdr phdr;
+
+ gelf_getphdr(src, i, &phdr);
+ gelf_update_phdr(dst, i, &phdr);
+ }
+ }
+
+ secxlate = xmalloc(sizeof (int) * sehdr.e_shnum);
+ for (srcidx = dstidx = 0; srcidx < sehdr.e_shnum; srcidx++) {
+ Elf_Scn *scn = elf_getscn(src, srcidx);
+ GElf_Shdr shdr1;
+ char *sname;
+
+ gelf_getshdr(scn, &shdr1);
+ sname = elf_strptr(src, sehdr.e_shstrndx, shdr1.sh_name);
+ if (sname == NULL) {
+ elfterminate(srcname, "Can't find string at %u",
+ shdr1.sh_name);
+ }
+
+ if (strcmp(sname, CTF_ELF_SCN_NAME) == 0) {
+ secxlate[srcidx] = -1;
+ } else if (!keep_stabs &&
+ (strncmp(sname, ".stab", 5) == 0 ||
+ strncmp(sname, ".debug", 6) == 0 ||
+ strncmp(sname, ".rel.debug", 10) == 0 ||
+ strncmp(sname, ".rela.debug", 11) == 0)) {
+ secxlate[srcidx] = -1;
+ } else if (dynsym && shdr1.sh_type == SHT_SYMTAB) {
+ /*
+ * If we're building CTF against the dynsym,
+ * we'll rip out the symtab so debuggers aren't
+ * confused.
+ */
+ secxlate[srcidx] = -1;
+ } else {
+ secxlate[srcidx] = dstidx++;
+ curnmoff += strlen(sname) + 1;
+ }
+
+ new_offset = (off_t)dehdr.e_phoff;
+ }
+
+ for (srcidx = 1; srcidx < sehdr.e_shnum; srcidx++) {
+ char *sname;
+
+ sscn = elf_getscn(src, srcidx);
+ gelf_getshdr(sscn, &shdr);
+
+ if (secxlate[srcidx] == -1) {
+ changing = 1;
+ continue;
+ }
+
+ dscn = elf_newscn(dst);
+
+ /*
+ * If this file has program headers, we need to explicitly lay
+ * out sections. If none of the sections prior to this one have
+ * been removed, then we can just use the existing location. If
+ * one or more sections have been changed, then we need to
+ * adjust this one to avoid holes.
+ */
+ if (changing && sehdr.e_phnum != 0) {
+ pad = new_offset % shdr.sh_addralign;
+
+ if (pad)
+ new_offset += shdr.sh_addralign - pad;
+ shdr.sh_offset = new_offset;
+ }
+
+ shdr.sh_link = secxlate[shdr.sh_link];
+
+ if (shdr.sh_type == SHT_REL || shdr.sh_type == SHT_RELA)
+ shdr.sh_info = secxlate[shdr.sh_info];
+
+ sname = elf_strptr(src, sehdr.e_shstrndx, shdr.sh_name);
+ if (sname == NULL) {
+ elfterminate(srcname, "Can't find string at %u",
+ shdr.sh_name);
+ }
+
+#if !defined(sun)
+ if (gelf_update_shdr(dscn, &shdr) == 0)
+ elfterminate(dstname, "Cannot update sect %s", sname);
+#endif
+
+ if ((sdata = elf_getdata(sscn, NULL)) == NULL)
+ elfterminate(srcname, "Cannot get sect %s data", sname);
+ if ((ddata = elf_newdata(dscn)) == NULL)
+ elfterminate(dstname, "Can't make sect %s data", sname);
+#if defined(sun)
+ bcopy(sdata, ddata, sizeof (Elf_Data));
+#else
+ /*
+ * FreeBSD's Elf_Data has private fields which the
+ * elf_* routines manage. Simply copying the
+ * entire structure corrupts the data. So we need
+ * to copy the public fields explictly.
+ */
+ ddata->d_align = sdata->d_align;
+ ddata->d_off = sdata->d_off;
+ ddata->d_size = sdata->d_size;
+ ddata->d_type = sdata->d_type;
+ ddata->d_version = sdata->d_version;
+#endif
+
+ if (srcidx == sehdr.e_shstrndx) {
+ char seclen = strlen(CTF_ELF_SCN_NAME);
+
+ ddata->d_buf = xmalloc(ddata->d_size + shdr.sh_size +
+ seclen + 1);
+ bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
+ strcpy((caddr_t)ddata->d_buf + shdr.sh_size,
+ CTF_ELF_SCN_NAME);
+ ctfnameoff = (off_t)shdr.sh_size;
+ shdr.sh_size += seclen + 1;
+ ddata->d_size += seclen + 1;
+
+ if (sehdr.e_phnum != 0)
+ changing = 1;
+ }
+
+ if (shdr.sh_type == symtab_type && shdr.sh_entsize != 0) {
+ int nsym = shdr.sh_size / shdr.sh_entsize;
+
+ symtab_idx = secxlate[srcidx];
+
+ ddata->d_buf = xmalloc(shdr.sh_size);
+ bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
+
+ for (i = 0; i < nsym; i++) {
+ GElf_Sym sym;
+ short newscn;
+
+ if (gelf_getsym(ddata, i, &sym) == NULL)
+ printf("Could not get symbol %d\n",i);
+
+ if (sym.st_shndx >= SHN_LORESERVE)
+ continue;
+
+ if ((newscn = secxlate[sym.st_shndx]) !=
+ sym.st_shndx) {
+ sym.st_shndx =
+ (newscn == -1 ? 1 : newscn);
+
+ gelf_update_sym(ddata, i, &sym);
+ }
+ }
+ }
+
+#if !defined(sun)
+ if (ddata->d_buf == NULL && sdata->d_buf != NULL) {
+ ddata->d_buf = xmalloc(shdr.sh_size);
+ bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
+ }
+#endif
+
+ if (gelf_update_shdr(dscn, &shdr) == 0)
+ elfterminate(dstname, "Cannot update sect %s", sname);
+
+ new_offset = (off_t)shdr.sh_offset;
+ if (shdr.sh_type != SHT_NOBITS)
+ new_offset += shdr.sh_size;
+ }
+
+ if (symtab_idx == -1) {
+ terminate("%s: Cannot find %s section\n", srcname,
+ dynsym ? "SHT_DYNSYM" : "SHT_SYMTAB");
+ }
+
+ /* Add the ctf section */
+ dscn = elf_newscn(dst);
+ gelf_getshdr(dscn, &shdr);
+ shdr.sh_name = ctfnameoff;
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_size = ctfsize;
+ shdr.sh_link = symtab_idx;
+ shdr.sh_addralign = 4;
+ if (changing && sehdr.e_phnum != 0) {
+ pad = new_offset % shdr.sh_addralign;
+
+ if (pad)
+ new_offset += shdr.sh_addralign - pad;
+
+ shdr.sh_offset = new_offset;
+ new_offset += shdr.sh_size;
+ }
+
+ ddata = elf_newdata(dscn);
+ ddata->d_buf = ctfdata;
+ ddata->d_size = ctfsize;
+ ddata->d_align = shdr.sh_addralign;
+ ddata->d_off = 0;
+
+ gelf_update_shdr(dscn, &shdr);
+
+ /* update the section header location */
+ if (sehdr.e_phnum != 0) {
+ size_t align = gelf_fsize(dst, ELF_T_ADDR, 1, EV_CURRENT);
+ size_t r = new_offset % align;
+
+ if (r)
+ new_offset += align - r;
+
+ dehdr.e_shoff = new_offset;
+ }
+
+ /* commit to disk */
+ dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx];
+ gelf_update_ehdr(dst, &dehdr);
+ if (elf_update(dst, ELF_C_WRITE) < 0)
+ elfterminate(dstname, "Cannot finalize temp file");
+
+ free(secxlate);
+}
+
+static caddr_t
+make_ctf_data(tdata_t *td, Elf *elf, const char *file, size_t *lenp, int flags)
+{
+ iiburst_t *iiburst;
+ caddr_t data;
+
+ iiburst = sort_iidescs(elf, file, td, flags & CTF_FUZZY_MATCH,
+ flags & CTF_USE_DYNSYM);
+ data = ctf_gen(iiburst, lenp, flags & CTF_COMPRESS);
+
+ iiburst_free(iiburst);
+
+ return (data);
+}
+
+void
+write_ctf(tdata_t *td, const char *curname, const char *newname, int flags)
+{
+ struct stat st;
+ Elf *elf = NULL;
+ Elf *telf = NULL;
+ caddr_t data;
+ size_t len;
+ int fd = -1;
+ int tfd = -1;
+
+ (void) elf_version(EV_CURRENT);
+ if ((fd = open(curname, O_RDONLY)) < 0 || fstat(fd, &st) < 0)
+ terminate("%s: Cannot open for re-reading", curname);
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
+ elfterminate(curname, "Cannot re-read");
+
+ if ((tfd = open(newname, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0)
+ terminate("Cannot open temp file %s for writing", newname);
+ if ((telf = elf_begin(tfd, ELF_C_WRITE, NULL)) == NULL)
+ elfterminate(curname, "Cannot write");
+
+ data = make_ctf_data(td, elf, curname, &len, flags);
+ write_file(elf, curname, telf, newname, data, len, flags);
+ free(data);
+
+ elf_end(telf);
+ elf_end(elf);
+ (void) close(fd);
+ (void) close(tfd);
+}
diff --git a/cvt/st_parse.c b/cvt/st_parse.c
new file mode 100644
index 0000000..602ae23
--- /dev/null
+++ b/cvt/st_parse.c
@@ -0,0 +1,1205 @@
+/*
+ * 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) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/*
+ * This file is a sewer.
+ */
+
+#include "libctf/ctf_impl.h"
+#include <assert.h>
+#include <setjmp.h>
+#include "pctf/ctf.h"
+#include "ctftools.h"
+#include "memory.h"
+#include "common/list.h"
+
+#define HASH(NUM) ((int)(NUM & (BUCKETS - 1)))
+#define BUCKETS 128
+
+#define TYPEPAIRMULT 10000
+#define MAKETYPEID(file, num) ((file) * TYPEPAIRMULT + num)
+#define TYPEFILE(tid) ((tid) / TYPEPAIRMULT)
+#define TYPENUM(tid) ((tid) % TYPEPAIRMULT)
+
+#define expected(a, b, c) _expected(a, b, c, __LINE__)
+
+static int faketypenumber = 100000000;
+
+static tdesc_t *hash_table[BUCKETS];
+static tdesc_t *name_table[BUCKETS];
+
+list_t *typedbitfldmems;
+
+static void reset(void);
+static jmp_buf resetbuf;
+
+static char *soudef(char *cp, stabtype_t type, tdesc_t **rtdp);
+static void enumdef(char *cp, tdesc_t **rtdp);
+static int compute_sum(const char *w);
+
+static char *number(char *cp, int *n);
+static char *name(char *cp, char **w);
+static char *id(char *cp, int *h);
+static char *whitesp(char *cp);
+static void addhash(tdesc_t *tdp, int num);
+static int tagadd(char *w, int h, tdesc_t *tdp);
+static char *tdefdecl(char *cp, int h, tdesc_t **rtdp);
+static char *intrinsic(char *cp, tdesc_t **rtdp);
+static char *arraydef(char *cp, tdesc_t **rtdp);
+
+int debug_parse = DEBUG_PARSE;
+
+/*PRINTFLIKE3*/
+static void
+parse_debug(int level, char *cp, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+ char tmp[32];
+ int i;
+
+ if (level > debug_level || !debug_parse)
+ return;
+
+ if (cp != NULL) {
+ for (i = 0; i < 30; i++) {
+ if (cp[i] == '\0')
+ break;
+ if (!iscntrl(cp[i]))
+ tmp[i] = cp[i];
+ }
+ tmp[i] = '\0';
+ (void) snprintf(buf, sizeof (buf), "%s [cp='%s']\n", fmt, tmp);
+ } else {
+ strcpy(buf, fmt);
+ strcat(buf, "\n");
+ }
+
+ va_start(ap, fmt);
+ vadebug(level, buf, ap);
+ va_end(ap);
+}
+
+/* Report unexpected syntax in stabs. */
+static void
+_expected(
+ const char *who, /* what function, or part thereof, is reporting */
+ const char *what, /* what was expected */
+ const char *where, /* where we were in the line of input */
+ int line)
+{
+ fprintf(stderr, "%s, expecting \"%s\" at \"%s\"\n", who, what, where);
+ fprintf(stderr, "code line: %d, file %s\n", line,
+ (curhdr ? curhdr : "NO FILE"));
+ reset();
+}
+
+/*ARGSUSED*/
+void
+parse_init(tdata_t *td __unused)
+{
+ int i;
+
+ for (i = 0; i < BUCKETS; i++) {
+ hash_table[i] = NULL;
+ name_table[i] = NULL;
+ }
+
+ if (typedbitfldmems != NULL) {
+ list_free(typedbitfldmems, NULL, NULL);
+ typedbitfldmems = NULL;
+ }
+}
+
+void
+parse_finish(tdata_t *td)
+{
+ td->td_nextid = ++faketypenumber;
+}
+
+static tdesc_t *
+unres_new(int tid)
+{
+ tdesc_t *tdp;
+
+ tdp = xcalloc(sizeof (*tdp));
+ tdp->t_type = TYPEDEF_UNRES;
+ tdp->t_id = tid;
+
+ return (tdp);
+}
+
+static char *
+read_tid(char *cp, tdesc_t **tdpp)
+{
+ tdesc_t *tdp;
+ int tid;
+
+ cp = id(cp, &tid);
+
+ assert(tid != 0);
+
+ if (*cp == '=') {
+ if (!(cp = tdefdecl(cp + 1, tid, &tdp)))
+ return (NULL);
+ if (tdp->t_id && tdp->t_id != tid) {
+ tdesc_t *ntdp = xcalloc(sizeof (*ntdp));
+
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp = ntdp;
+ }
+ addhash(tdp, tid);
+ } else if ((tdp = lookup(tid)) == NULL)
+ tdp = unres_new(tid);
+
+ *tdpp = tdp;
+ return (cp);
+}
+
+static iitype_t
+parse_fun(char *cp, iidesc_t *ii)
+{
+ iitype_t iitype = 0;
+ tdesc_t *tdp;
+ tdesc_t **args = NULL;
+ int nargs = 0;
+ int va = 0;
+
+ /*
+ * name:P prototype
+ * name:F global function
+ * name:f static function
+ */
+ switch (*cp++) {
+ case 'P':
+ iitype = II_NOT; /* not interesting */
+ break;
+
+ case 'F':
+ iitype = II_GFUN;
+ break;
+
+ case 'f':
+ iitype = II_SFUN;
+ break;
+
+ default:
+ expected("parse_nfun", "[PfF]", cp - 1);
+ }
+
+ if (!(cp = read_tid(cp, &tdp)))
+ return (-1);
+
+ if (*cp)
+ args = xmalloc(sizeof (tdesc_t *) * FUNCARG_DEF);
+
+ while (*cp && *++cp) {
+ if (*cp == '0') {
+ va = 1;
+ continue;
+ }
+
+ nargs++;
+ if (nargs > FUNCARG_DEF)
+ args = xrealloc(args, sizeof (tdesc_t *) * nargs);
+ if (!(cp = read_tid(cp, &args[nargs - 1])))
+ return (-1);
+ }
+
+ ii->ii_type = iitype;
+ ii->ii_dtype = tdp;
+ ii->ii_nargs = nargs;
+ ii->ii_args = args;
+ ii->ii_vargs = va;
+
+ return (iitype);
+}
+
+static iitype_t
+parse_sym(char *cp, iidesc_t *ii)
+{
+ tdesc_t *tdp;
+ iitype_t iitype = 0;
+
+ /*
+ * name:G global variable
+ * name:S static variable
+ */
+ switch (*cp++) {
+ case 'G':
+ iitype = II_GVAR;
+ break;
+ case 'S':
+ iitype = II_SVAR;
+ break;
+ case 'p':
+ iitype = II_PSYM;
+ break;
+ case '(':
+ cp--;
+ /*FALLTHROUGH*/
+ case 'r':
+ case 'V':
+ iitype = II_NOT; /* not interesting */
+ break;
+ default:
+ expected("parse_sym", "[GprSV(]", cp - 1);
+ }
+
+ if (!(cp = read_tid(cp, &tdp)))
+ return (-1);
+
+ ii->ii_type = iitype;
+ ii->ii_dtype = tdp;
+
+ return (iitype);
+}
+
+static iitype_t
+parse_type(char *cp, iidesc_t *ii)
+{
+ tdesc_t *tdp, *ntdp;
+ int tid;
+
+ if (*cp++ != 't')
+ expected("parse_type", "t (type)", cp - 1);
+
+ cp = id(cp, &tid);
+ if ((tdp = lookup(tid)) == NULL) {
+ if (*cp++ != '=')
+ expected("parse_type", "= (definition)", cp - 1);
+
+ (void) tdefdecl(cp, tid, &tdp);
+
+ if (tdp->t_id == tid) {
+ assert(tdp->t_type != TYPEDEF);
+ assert(!lookup(tdp->t_id));
+
+ if (!streq(tdp->t_name, ii->ii_name)) {
+ ntdp = xcalloc(sizeof (*ntdp));
+ ntdp->t_name = xstrdup(ii->ii_name);
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp->t_id = faketypenumber++;
+ tdp = ntdp;
+ }
+ } else if (tdp->t_id == 0) {
+ assert(tdp->t_type == FORWARD ||
+ tdp->t_type == INTRINSIC);
+
+ if (tdp->t_name && !streq(tdp->t_name, ii->ii_name)) {
+ ntdp = xcalloc(sizeof (*ntdp));
+ ntdp->t_name = xstrdup(ii->ii_name);
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp->t_id = faketypenumber++;
+ tdp = ntdp;
+ }
+ } else if (tdp->t_id != tid) {
+ ntdp = xcalloc(sizeof (*ntdp));
+ ntdp->t_name = xstrdup(ii->ii_name);
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp = ntdp;
+ }
+
+ if (tagadd(ii->ii_name, tid, tdp) < 0)
+ return (-1);
+ }
+
+ ii->ii_type = II_TYPE;
+ ii->ii_dtype = tdp;
+ return (II_TYPE);
+}
+
+static iitype_t
+parse_sou(char *cp, iidesc_t *idp)
+{
+ tdesc_t *rtdp;
+ int tid;
+
+ if (*cp++ != 'T')
+ expected("parse_sou", "T (sou)", cp - 1);
+
+ cp = id(cp, &tid);
+ if (*cp++ != '=')
+ expected("parse_sou", "= (definition)", cp - 1);
+
+ parse_debug(1, NULL, "parse_sou: declaring '%s'", idp->ii_name ?
+ idp->ii_name : "(anon)");
+ if ((rtdp = lookup(tid)) != NULL) {
+ if (idp->ii_name != NULL) {
+ if (rtdp->t_name != NULL &&
+ strcmp(rtdp->t_name, idp->ii_name) != 0) {
+ tdesc_t *tdp;
+
+ tdp = xcalloc(sizeof (*tdp));
+ tdp->t_name = xstrdup(idp->ii_name);
+ tdp->t_type = TYPEDEF;
+ tdp->t_tdesc = rtdp;
+ addhash(tdp, tid); /* for *(x,y) types */
+ parse_debug(3, NULL, " %s defined as %s(%d)",
+ idp->ii_name, tdesc_name(rtdp), tid);
+ } else if (rtdp->t_name == NULL) {
+ rtdp->t_name = xstrdup(idp->ii_name);
+ addhash(rtdp, tid);
+ }
+ }
+ } else {
+ rtdp = xcalloc(sizeof (*rtdp));
+ rtdp->t_name = idp->ii_name ? xstrdup(idp->ii_name) : NULL;
+ addhash(rtdp, tid);
+ }
+
+ switch (*cp++) {
+ case 's':
+ (void) soudef(cp, STRUCT, &rtdp);
+ break;
+ case 'u':
+ (void) soudef(cp, UNION, &rtdp);
+ break;
+ case 'e':
+ enumdef(cp, &rtdp);
+ break;
+ default:
+ expected("parse_sou", "<tag type s/u/e>", cp - 1);
+ break;
+ }
+
+ idp->ii_type = II_SOU;
+ idp->ii_dtype = rtdp;
+ return (II_SOU);
+}
+
+int
+parse_stab(stab_t *stab, char *cp, iidesc_t **iidescp)
+{
+ iidesc_t *ii = NULL;
+ iitype_t (*parse)(char *, iidesc_t *);
+ int rc;
+
+ /*
+ * set up for reset()
+ */
+ if (setjmp(resetbuf))
+ return (-1);
+
+ cp = whitesp(cp);
+ ii = iidesc_new(NULL);
+ cp = name(cp, &ii->ii_name);
+
+ switch (stab->n_type) {
+ case N_FUN:
+ parse = parse_fun;
+ break;
+
+ case N_LSYM:
+ if (*cp == 't')
+ parse = parse_type;
+ else if (*cp == 'T')
+ parse = parse_sou;
+ else
+ parse = parse_sym;
+ break;
+
+ case N_GSYM:
+ case N_LCSYM:
+ case N_PSYM:
+ case N_ROSYM:
+ case N_RSYM:
+ case N_STSYM:
+ parse = parse_sym;
+ break;
+ default:
+ parse_debug(1, cp, "Unknown stab type %#x", stab->n_type);
+ bzero(&resetbuf, sizeof (resetbuf));
+ return (-1);
+ }
+
+ rc = parse(cp, ii);
+ bzero(&resetbuf, sizeof (resetbuf));
+
+ if (rc < 0 || ii->ii_type == II_NOT) {
+ iidesc_free(ii, NULL);
+ return (rc);
+ }
+
+ *iidescp = ii;
+
+ return (1);
+}
+
+/*
+ * Check if we have this node in the hash table already
+ */
+tdesc_t *
+lookup(int h)
+{
+ int bucket = HASH(h);
+ tdesc_t *tdp = hash_table[bucket];
+
+ while (tdp != NULL) {
+ if (tdp->t_id == h)
+ return (tdp);
+ tdp = tdp->t_hash;
+ }
+ return (NULL);
+}
+
+static char *
+whitesp(char *cp)
+{
+ char c;
+
+ for (c = *cp++; isspace(c); c = *cp++)
+ ;
+ --cp;
+ return (cp);
+}
+
+static char *
+name(char *cp, char **w)
+{
+ char *new, *orig, c;
+ int len;
+
+ orig = cp;
+ c = *cp++;
+ if (c == ':')
+ *w = NULL;
+ else if (isalpha(c) || strchr("_.$#", c)) {
+ for (c = *cp++; isalnum(c) || strchr(" _.$#", c); c = *cp++)
+ ;
+ if (c != ':')
+ reset();
+ len = cp - orig;
+ new = xmalloc(len);
+ while (orig < cp - 1)
+ *new++ = *orig++;
+ *new = '\0';
+ *w = new - (len - 1);
+ } else
+ reset();
+
+ return (cp);
+}
+
+static char *
+number(char *cp, int *n)
+{
+ char *next;
+
+ *n = (int)strtol(cp, &next, 10);
+ if (next == cp)
+ expected("number", "<number>", cp);
+ return (next);
+}
+
+static char *
+id(char *cp, int *h)
+{
+ int n1, n2;
+
+ if (*cp == '(') { /* SunPro style */
+ cp++;
+ cp = number(cp, &n1);
+ if (*cp++ != ',')
+ expected("id", ",", cp - 1);
+ cp = number(cp, &n2);
+ if (*cp++ != ')')
+ expected("id", ")", cp - 1);
+ *h = MAKETYPEID(n1, n2);
+ } else if (isdigit(*cp)) { /* gcc style */
+ cp = number(cp, &n1);
+ *h = n1;
+ } else {
+ expected("id", "(/0-9", cp);
+ }
+ return (cp);
+}
+
+static int
+tagadd(char *w, int h, tdesc_t *tdp)
+{
+ tdesc_t *otdp;
+
+ tdp->t_name = w;
+ if (!(otdp = lookup(h)))
+ addhash(tdp, h);
+ else if (otdp != tdp) {
+ warning("duplicate entry\n");
+ warning(" old: %s %d (%d,%d)\n", tdesc_name(otdp),
+ otdp->t_type, TYPEFILE(otdp->t_id), TYPENUM(otdp->t_id));
+ warning(" new: %s %d (%d,%d)\n", tdesc_name(tdp),
+ tdp->t_type, TYPEFILE(tdp->t_id), TYPENUM(tdp->t_id));
+ return (-1);
+ }
+
+ return (0);
+}
+
+static char *
+tdefdecl(char *cp, int h, tdesc_t **rtdp)
+{
+ tdesc_t *ntdp;
+ char *w;
+ int c, h2;
+ char type;
+
+ parse_debug(3, cp, "tdefdecl h=%d", h);
+
+ /* Type codes */
+ switch (type = *cp) {
+ case 'b': /* integer */
+ case 'R': /* fp */
+ cp = intrinsic(cp, rtdp);
+ break;
+ case '(': /* equiv to another type */
+ cp = id(cp, &h2);
+ ntdp = lookup(h2);
+
+ if (ntdp != NULL && *cp == '=') {
+ if (ntdp->t_type == FORWARD && *(cp + 1) == 'x') {
+ /*
+ * The 6.2 compiler, and possibly others, will
+ * sometimes emit the same stab for a forward
+ * declaration twice. That is, "(1,2)=xsfoo:"
+ * will sometimes show up in two different
+ * places. This is, of course, quite fun. We
+ * want CTF to work in spite of the compiler,
+ * so we'll let this one through.
+ */
+ char *c2 = cp + 2;
+ char *nm;
+
+ if (!strchr("sue", *c2++)) {
+ expected("tdefdecl/x-redefine", "[sue]",
+ c2 - 1);
+ }
+
+ c2 = name(c2, &nm);
+ if (strcmp(nm, ntdp->t_name) != 0) {
+ terminate("Stabs error: Attempt to "
+ "redefine type (%d,%d) as "
+ "something else: %s\n",
+ TYPEFILE(h2), TYPENUM(h2),
+ c2 - 1);
+ }
+ free(nm);
+
+ h2 = faketypenumber++;
+ ntdp = NULL;
+ } else {
+ terminate("Stabs error: Attempting to "
+ "redefine type (%d,%d)\n", TYPEFILE(h2),
+ TYPENUM(h2));
+ }
+ }
+
+ if (ntdp == NULL) { /* if that type isn't defined yet */
+ if (*cp != '=') {
+ /* record it as unresolved */
+ parse_debug(3, NULL, "tdefdecl unres type %d",
+ h2);
+ *rtdp = calloc(sizeof (**rtdp), 1);
+ (*rtdp)->t_type = TYPEDEF_UNRES;
+ (*rtdp)->t_id = h2;
+ break;
+ } else
+ cp++;
+
+ /* define a new type */
+ cp = tdefdecl(cp, h2, rtdp);
+ if ((*rtdp)->t_id && (*rtdp)->t_id != h2) {
+ ntdp = calloc(sizeof (*ntdp), 1);
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = *rtdp;
+ *rtdp = ntdp;
+ }
+
+ addhash(*rtdp, h2);
+
+ } else { /* that type is already defined */
+ if (ntdp->t_type != TYPEDEF || ntdp->t_name != NULL) {
+ *rtdp = ntdp;
+ } else {
+ parse_debug(3, NULL,
+ "No duplicate typedef anon for ref");
+ *rtdp = ntdp;
+ }
+ }
+ break;
+ case '*':
+ ntdp = NULL;
+ cp = tdefdecl(cp + 1, h, &ntdp);
+ if (ntdp == NULL)
+ expected("tdefdecl/*", "id", cp);
+
+ if (!ntdp->t_id)
+ ntdp->t_id = faketypenumber++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = POINTER;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_id = h;
+ (*rtdp)->t_tdesc = ntdp;
+ break;
+ case 'f':
+ cp = tdefdecl(cp + 1, h, &ntdp);
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = FUNCTION;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_id = h;
+ (*rtdp)->t_fndef = xcalloc(sizeof (fndef_t));
+ /*
+ * The 6.1 compiler will sometimes generate incorrect stabs for
+ * function pointers (it'll get the return type wrong). This
+ * causes merges to fail. We therefore treat function pointers
+ * as if they all point to functions that return int. When
+ * 4432549 is fixed, the lookupname() call below should be
+ * replaced with `ntdp'.
+ */
+ (*rtdp)->t_fndef->fn_ret = lookupname("int");
+ break;
+ case 'a':
+ case 'z':
+ cp++;
+ if (*cp++ != 'r')
+ expected("tdefdecl/[az]", "r", cp - 1);
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = ARRAY;
+ (*rtdp)->t_id = h;
+ cp = arraydef(cp, rtdp);
+ break;
+ case 'x':
+ c = *++cp;
+ if (c != 's' && c != 'u' && c != 'e')
+ expected("tdefdecl/x", "[sue]", cp - 1);
+ cp = name(cp + 1, &w);
+
+ ntdp = xcalloc(sizeof (*ntdp));
+ ntdp->t_type = FORWARD;
+ ntdp->t_name = w;
+ /*
+ * We explicitly don't set t_id here - the caller will do it.
+ * The caller may want to use a real type ID, or they may
+ * choose to make one up.
+ */
+
+ *rtdp = ntdp;
+ break;
+
+ case 'B': /* volatile */
+ cp = tdefdecl(cp + 1, h, &ntdp);
+
+ if (!ntdp->t_id)
+ ntdp->t_id = faketypenumber++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = VOLATILE;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_tdesc = ntdp;
+ (*rtdp)->t_id = h;
+ break;
+
+ case 'k': /* const */
+ cp = tdefdecl(cp + 1, h, &ntdp);
+
+ if (!ntdp->t_id)
+ ntdp->t_id = faketypenumber++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = CONST;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_tdesc = ntdp;
+ (*rtdp)->t_id = h;
+ break;
+
+ case 'K': /* restricted */
+ cp = tdefdecl(cp + 1, h, &ntdp);
+
+ if (!ntdp->t_id)
+ ntdp->t_id = faketypenumber++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_type = RESTRICT;
+ (*rtdp)->t_size = 0;
+ (*rtdp)->t_tdesc = ntdp;
+ (*rtdp)->t_id = h;
+ break;
+
+ case 'u':
+ case 's':
+ cp++;
+
+ *rtdp = xcalloc(sizeof (**rtdp));
+ (*rtdp)->t_name = NULL;
+ cp = soudef(cp, (type == 'u') ? UNION : STRUCT, rtdp);
+ break;
+ default:
+ expected("tdefdecl", "<type code>", cp);
+ }
+ return (cp);
+}
+
+static char *
+intrinsic(char *cp, tdesc_t **rtdp)
+{
+ intr_t *intr = xcalloc(sizeof (intr_t));
+ tdesc_t *tdp;
+ int width, fmt, i;
+
+ switch (*cp++) {
+ case 'b':
+ intr->intr_type = INTR_INT;
+ if (*cp == 's')
+ intr->intr_signed = 1;
+ else if (*cp != 'u')
+ expected("intrinsic/b", "[su]", cp);
+ cp++;
+
+ if (strchr("cbv", *cp))
+ intr->intr_iformat = *cp++;
+
+ cp = number(cp, &width);
+ if (*cp++ != ';')
+ expected("intrinsic/b", "; (post-width)", cp - 1);
+
+ cp = number(cp, &intr->intr_offset);
+ if (*cp++ != ';')
+ expected("intrinsic/b", "; (post-offset)", cp - 1);
+
+ cp = number(cp, &intr->intr_nbits);
+ break;
+
+ case 'R':
+ intr->intr_type = INTR_REAL;
+ for (fmt = 0, i = 0; isdigit(*(cp + i)); i++)
+ fmt = fmt * 10 + (*(cp + i) - '0');
+
+ if (fmt < 1 || fmt > CTF_FP_MAX)
+ expected("intrinsic/R", "number <= CTF_FP_MAX", cp);
+
+ intr->intr_fformat = fmt;
+ cp += i;
+
+ if (*cp++ != ';')
+ expected("intrinsic/R", ";", cp - 1);
+ cp = number(cp, &width);
+
+ intr->intr_nbits = width * 8;
+ break;
+ }
+
+ tdp = xcalloc(sizeof (*tdp));
+ tdp->t_type = INTRINSIC;
+ tdp->t_size = width;
+ tdp->t_name = NULL;
+ tdp->t_intr = intr;
+ parse_debug(3, NULL, "intrinsic: size=%d", width);
+ *rtdp = tdp;
+
+ return (cp);
+}
+
+static tdesc_t *
+bitintrinsic(tdesc_t *template, int nbits)
+{
+ tdesc_t *newtdp = xcalloc(sizeof (tdesc_t));
+
+ newtdp->t_name = xstrdup(template->t_name);
+ newtdp->t_id = faketypenumber++;
+ newtdp->t_type = INTRINSIC;
+ newtdp->t_size = template->t_size;
+ newtdp->t_intr = xmalloc(sizeof (intr_t));
+ bcopy(template->t_intr, newtdp->t_intr, sizeof (intr_t));
+ newtdp->t_intr->intr_nbits = nbits;
+
+ return (newtdp);
+}
+
+static char *
+offsize(char *cp, mlist_t *mlp)
+{
+ int offset, size;
+
+ if (*cp == ',')
+ cp++;
+ cp = number(cp, &offset);
+ if (*cp++ != ',')
+ expected("offsize/2", ",", cp - 1);
+ cp = number(cp, &size);
+ if (*cp++ != ';')
+ expected("offsize/3", ";", cp - 1);
+ mlp->ml_offset = offset;
+ mlp->ml_size = size;
+ return (cp);
+}
+
+static tdesc_t *
+find_intrinsic(tdesc_t *tdp)
+{
+ for (;;) {
+ switch (tdp->t_type) {
+ case TYPEDEF:
+ case VOLATILE:
+ case CONST:
+ case RESTRICT:
+ tdp = tdp->t_tdesc;
+ break;
+
+ default:
+ return (tdp);
+ }
+ }
+}
+
+static char *
+soudef(char *cp, stabtype_t type, tdesc_t **rtdp)
+{
+ mlist_t *mlp, **prev;
+ char *w;
+ int h;
+ int size;
+ tdesc_t *tdp, *itdp;
+
+ cp = number(cp, &size);
+ (*rtdp)->t_size = size;
+ (*rtdp)->t_type = type; /* s or u */
+
+ /*
+ * An '@' here indicates a bitmask follows. This is so the
+ * compiler can pass information to debuggers about how structures
+ * are passed in the v9 world. We don't need this information
+ * so we skip over it.
+ */
+ if (cp[0] == '@') {
+ cp += 3;
+ }
+
+ parse_debug(3, cp, "soudef: %s size=%d", tdesc_name(*rtdp),
+ (*rtdp)->t_size);
+
+ prev = &((*rtdp)->t_members);
+ /* now fill up the fields */
+ while ((*cp != '\0') && (*cp != ';')) { /* signifies end of fields */
+ mlp = xcalloc(sizeof (*mlp));
+ *prev = mlp;
+ cp = name(cp, &w);
+ mlp->ml_name = w;
+ cp = id(cp, &h);
+ /*
+ * find the tdesc struct in the hash table for this type
+ * and stick a ptr in here
+ */
+ tdp = lookup(h);
+ if (tdp == NULL) { /* not in hash list */
+ parse_debug(3, NULL, " defines %s (%d)", w, h);
+ if (*cp++ != '=') {
+ tdp = unres_new(h);
+ parse_debug(3, NULL,
+ " refers to %s (unresolved %d)",
+ (w ? w : "anon"), h);
+ } else {
+ cp = tdefdecl(cp, h, &tdp);
+
+ if (tdp->t_id && tdp->t_id != h) {
+ tdesc_t *ntdp = xcalloc(sizeof (*ntdp));
+
+ ntdp->t_type = TYPEDEF;
+ ntdp->t_tdesc = tdp;
+ tdp = ntdp;
+ }
+
+ addhash(tdp, h);
+ parse_debug(4, cp,
+ " soudef now looking at ");
+ cp++;
+ }
+ } else {
+ parse_debug(3, NULL, " refers to %s (%d, %s)",
+ w ? w : "anon", h, tdesc_name(tdp));
+ }
+
+ cp = offsize(cp, mlp);
+
+ itdp = find_intrinsic(tdp);
+ if (itdp->t_type == INTRINSIC) {
+ if (mlp->ml_size != itdp->t_intr->intr_nbits) {
+ parse_debug(4, cp, "making %d bit intrinsic "
+ "from %s", mlp->ml_size, tdesc_name(itdp));
+ mlp->ml_type = bitintrinsic(itdp, mlp->ml_size);
+ } else
+ mlp->ml_type = tdp;
+ } else if (itdp->t_type == TYPEDEF_UNRES) {
+ list_add(&typedbitfldmems, mlp);
+ mlp->ml_type = tdp;
+ } else {
+ mlp->ml_type = tdp;
+ }
+
+ /* cp is now pointing to next field */
+ prev = &mlp->ml_next;
+ }
+ return (cp);
+}
+
+static char *
+arraydef(char *cp, tdesc_t **rtdp)
+{
+ int start, end, h;
+
+ cp = id(cp, &h);
+ if (*cp++ != ';')
+ expected("arraydef/1", ";", cp - 1);
+
+ (*rtdp)->t_ardef = xcalloc(sizeof (ardef_t));
+ (*rtdp)->t_ardef->ad_idxtype = lookup(h);
+
+ cp = number(cp, &start); /* lower */
+ if (*cp++ != ';')
+ expected("arraydef/2", ";", cp - 1);
+
+ if (*cp == 'S') {
+ /*
+ * variable length array - treat as null dimensioned
+ *
+ * For VLA variables on sparc, SS12 generated stab entry
+ * looks as follows:
+ * .stabs "buf:(0,28)=zr(0,4);0;S-12;(0,1)", 0x80, 0, 0, -16
+ * Whereas SS12u1 generated stab entry looks like this:
+ * .stabs "buf:(0,28)=zr(0,4);0;S0;(0,1)", 0x80, 0, 0, 0
+ * On x86, both versions generate the first type of entry.
+ * We should be able to parse both.
+ */
+ cp++;
+ if (*cp == '-')
+ cp++;
+ cp = number(cp, &end);
+ end = start;
+ } else {
+ /*
+ * normal fixed-dimension array
+ * Stab entry for this looks as follows :
+ * .stabs "x:(0,28)=ar(0,4);0;9;(0,3)", 0x80, 0, 40, 0
+ */
+ cp = number(cp, &end); /* upper */
+ }
+
+ if (*cp++ != ';')
+ expected("arraydef/3", ";", cp - 1);
+ (*rtdp)->t_ardef->ad_nelems = end - start + 1;
+ cp = tdefdecl(cp, h, &((*rtdp)->t_ardef->ad_contents));
+
+ parse_debug(3, cp, "defined array idx type %d %d-%d next ",
+ h, start, end);
+
+ return (cp);
+}
+
+static void
+enumdef(char *cp, tdesc_t **rtdp)
+{
+ elist_t *elp, **prev;
+ char *w;
+
+ (*rtdp)->t_type = ENUM;
+ (*rtdp)->t_emem = NULL;
+
+ prev = &((*rtdp)->t_emem);
+ while (*cp != ';') {
+ elp = xcalloc(sizeof (*elp));
+ elp->el_next = NULL;
+ *prev = elp;
+ cp = name(cp, &w);
+ elp->el_name = w;
+ cp = number(cp, &elp->el_number);
+ parse_debug(3, NULL, "enum %s: %s=%d", tdesc_name(*rtdp),
+ elp->el_name, elp->el_number);
+ prev = &elp->el_next;
+ if (*cp++ != ',')
+ expected("enumdef", ",", cp - 1);
+ }
+}
+
+static tdesc_t *
+lookup_name(tdesc_t **hash, const char *name1)
+{
+ int bucket = compute_sum(name1);
+ tdesc_t *tdp, *ttdp = NULL;
+
+ for (tdp = hash[bucket]; tdp != NULL; tdp = tdp->t_next) {
+ if (tdp->t_name != NULL && strcmp(tdp->t_name, name1) == 0) {
+ if (tdp->t_type == STRUCT || tdp->t_type == UNION ||
+ tdp->t_type == ENUM || tdp->t_type == INTRINSIC)
+ return (tdp);
+ if (tdp->t_type == TYPEDEF)
+ ttdp = tdp;
+ }
+ }
+ return (ttdp);
+}
+
+tdesc_t *
+lookupname(const char *name1)
+{
+ return (lookup_name(name_table, name1));
+}
+
+/*
+ * Add a node to the hash queues.
+ */
+static void
+addhash(tdesc_t *tdp, int num)
+{
+ int hash = HASH(num);
+ tdesc_t *ttdp;
+ char added_num = 0, added_name = 0;
+
+ /*
+ * If it already exists in the hash table don't add it again
+ * (but still check to see if the name should be hashed).
+ */
+ ttdp = lookup(num);
+
+ if (ttdp == NULL) {
+ tdp->t_id = num;
+ tdp->t_hash = hash_table[hash];
+ hash_table[hash] = tdp;
+ added_num = 1;
+ }
+
+ if (tdp->t_name != NULL) {
+ ttdp = lookupname(tdp->t_name);
+ if (ttdp == NULL) {
+ hash = compute_sum(tdp->t_name);
+ tdp->t_next = name_table[hash];
+ name_table[hash] = tdp;
+ added_name = 1;
+ }
+ }
+ if (!added_num && !added_name) {
+ terminate("stabs: broken hash\n");
+ }
+}
+
+static int
+compute_sum(const char *w)
+{
+ char c;
+ int sum;
+
+ for (sum = 0; (c = *w) != '\0'; sum += c, w++)
+ ;
+ return (HASH(sum));
+}
+
+static void
+reset(void)
+{
+ longjmp(resetbuf, 1);
+}
+
+void
+check_hash(void)
+{
+ tdesc_t *tdp;
+ int i;
+
+ printf("checking hash\n");
+ for (i = 0; i < BUCKETS; i++) {
+ if (hash_table[i]) {
+ for (tdp = hash_table[i]->t_hash;
+ tdp && tdp != hash_table[i];
+ tdp = tdp->t_hash)
+ continue;
+ if (tdp) {
+ terminate("cycle in hash bucket %d\n", i);
+ return;
+ }
+ }
+
+ if (name_table[i]) {
+ for (tdp = name_table[i]->t_next;
+ tdp && tdp != name_table[i];
+ tdp = tdp->t_next)
+ continue;
+ if (tdp) {
+ terminate("cycle in name bucket %d\n", i);
+ return;
+ }
+ }
+ }
+ printf("done\n");
+}
+
+/*ARGSUSED1*/
+static int
+resolve_typed_bitfields_cb(void *arg, void *private __unused)
+{
+ mlist_t *ml = arg;
+ tdesc_t *tdp = ml->ml_type;
+
+ debug(3, "Resolving typed bitfields (member %s)\n",
+ (ml->ml_name ? ml->ml_name : "(anon)"));
+
+ while (tdp) {
+ switch (tdp->t_type) {
+ case INTRINSIC:
+ if (ml->ml_size != tdp->t_intr->intr_nbits) {
+ debug(3, "making %d bit intrinsic from %s",
+ ml->ml_size, tdesc_name(tdp));
+ ml->ml_type = bitintrinsic(tdp, ml->ml_size);
+ } else {
+ debug(3, "using existing %d bit %s intrinsic",
+ ml->ml_size, tdesc_name(tdp));
+ ml->ml_type = tdp;
+ }
+ return (1);
+
+ case POINTER:
+ case TYPEDEF:
+ case VOLATILE:
+ case CONST:
+ case RESTRICT:
+ tdp = tdp->t_tdesc;
+ break;
+
+ default:
+ return (1);
+ }
+ }
+
+ terminate("type chain for bitfield member %s has a NULL", ml->ml_name);
+ /*NOTREACHED*/
+ return (0);
+}
+
+void
+resolve_typed_bitfields(void)
+{
+ (void) list_iter(typedbitfldmems,
+ resolve_typed_bitfields_cb, NULL);
+}
diff --git a/cvt/stabs.c b/cvt/stabs.c
new file mode 100644
index 0000000..86a106f
--- /dev/null
+++ b/cvt/stabs.c
@@ -0,0 +1,371 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines used to read stabs data from a file, and to build a tdata structure
+ * based on the interesting parts of that data.
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "common/list.h"
+#include "stack.h"
+#include "memory.h"
+#include "traverse.h"
+
+char *curhdr;
+
+/*
+ * The stabs generator will sometimes reference types before they've been
+ * defined. If this is the case, a TYPEDEF_UNRES tdesc will be generated.
+ * Note that this is different from a forward declaration, in which the
+ * stab is defined, but is defined as something that doesn't exist yet.
+ * When we have read all of the stabs from the file, we can go back and
+ * fix up all of the unresolved types. We should be able to fix all of them.
+ */
+/*ARGSUSED2*/
+static int
+resolve_tou_node(tdesc_t *node, tdesc_t **nodep, void *private __unused)
+{
+ tdesc_t *new;
+
+ debug(3, "Trying to resolve %s (%d)\n", tdesc_name(node), node->t_id);
+ new = lookup(node->t_id);
+
+ if (new == NULL) {
+ terminate("Couldn't resolve type %d\n", node->t_id);
+ }
+
+ debug(3, " Resolving to %d\n", new->t_id);
+
+ *nodep = new;
+
+ return (1);
+}
+
+/*ARGSUSED*/
+static int
+resolve_fwd_node(tdesc_t *node, tdesc_t **nodep, void *private __unused)
+{
+ tdesc_t *new = lookupname(node->t_name);
+
+ debug(3, "Trying to unforward %s (%d)\n", tdesc_name(node), node->t_id);
+
+ if (!new || (new->t_type != STRUCT && new->t_type != UNION))
+ return (0);
+
+ debug(3, " Unforwarded to %d\n", new->t_id);
+
+ *nodep = new;
+
+ return (1);
+}
+
+static tdtrav_cb_f resolve_cbs[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ NULL, /* array */
+ NULL, /* function */
+ NULL, /* struct */
+ NULL, /* union */
+ NULL, /* enum */
+ resolve_fwd_node, /* forward */
+ NULL, /* typedef */
+ resolve_tou_node, /* typedef unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL, /* restrict */
+};
+
+static void
+resolve_nodes(tdata_t *td)
+{
+ debug(2, "Resolving unresolved stabs\n");
+
+ (void) iitraverse_hash(td->td_iihash, &td->td_curvgen, resolve_cbs,
+ NULL, NULL, td);
+}
+
+static char *
+concat(char *s1, char *s2, int s2strip)
+{
+ int savelen = strlen(s2) - s2strip;
+ int newlen = (s1 ? strlen(s1) : 0) + savelen + 1;
+ char *out;
+
+ out = xrealloc(s1, newlen);
+ if (s1)
+ strncpy(out + strlen(out), s2, savelen);
+ else
+ strncpy(out, s2, savelen);
+
+ out[newlen - 1] = '\0';
+
+ return (out);
+}
+
+/*
+ * N_FUN stabs come with their arguments in promoted form. In order to get the
+ * actual arguments, we need to wait for the N_PSYM stabs that will come towards
+ * the end of the function. These routines free the arguments (fnarg_free) we
+ * got from the N_FUN stab and add (fnarg_add) the ones from the N_PSYM stabs.
+ */
+static void
+fnarg_add(iidesc_t *curfun, iidesc_t *arg)
+{
+ curfun->ii_nargs++;
+
+ if (curfun->ii_nargs == 1)
+ curfun->ii_args = xmalloc(sizeof (tdesc_t *) * FUNCARG_DEF);
+ else if (curfun->ii_nargs > FUNCARG_DEF) {
+ curfun->ii_args = xrealloc(curfun->ii_args,
+ sizeof (tdesc_t *) * curfun->ii_nargs);
+ }
+
+ curfun->ii_args[curfun->ii_nargs - 1] = arg->ii_dtype;
+ arg->ii_dtype = NULL;
+}
+
+static void
+fnarg_free(iidesc_t *ii)
+{
+ ii->ii_nargs = 0;
+ free(ii->ii_args);
+ ii->ii_args = NULL;
+}
+
+/*
+ * Read the stabs from the stab ELF section, and turn them into a tdesc tree,
+ * assembled under an iidesc list.
+ */
+int
+stabs_read(tdata_t *td, Elf *elf, char *file)
+{
+ Elf_Scn *scn;
+ Elf_Data *data;
+ stab_t *stab;
+ stk_t *file_stack;
+ iidesc_t *iidescp;
+ iidesc_t *curfun = NULL;
+ char curpath[MAXPATHLEN];
+ char *curfile = NULL;
+ char *str;
+ char *fstr = NULL, *ofstr = NULL;
+ int stabidx, stabstridx;
+ int nstabs, rc, i;
+ int scope = 0;
+
+ if (!((stabidx = findelfsecidx(elf, file, ".stab.excl")) >= 0 &&
+ (stabstridx = findelfsecidx(elf, file, ".stab.exclstr")) >= 0) &&
+ !((stabidx = findelfsecidx(elf, file, ".stab")) >= 0 &&
+ (stabstridx = findelfsecidx(elf, file, ".stabstr")) >= 0)) {
+ errno = ENOENT;
+ return (-1);
+ }
+
+ file_stack = stack_new(free);
+
+ stack_push(file_stack, file);
+ curhdr = file;
+
+ debug(3, "Found stabs in %d, strings in %d\n", stabidx, stabstridx);
+
+ scn = elf_getscn(elf, stabidx);
+ data = elf_rawdata(scn, NULL);
+ nstabs = data->d_size / sizeof (stab_t);
+
+ parse_init(td);
+ for (i = 0; i < nstabs; i++) {
+ stab = &((stab_t *)data->d_buf)[i];
+
+ /* We don't want any local definitions */
+ if (stab->n_type == N_LBRAC) {
+ scope++;
+ debug(3, "stab %d: opening scope (%d)\n", i + 1, scope);
+ continue;
+ } else if (stab->n_type == N_RBRAC) {
+ scope--;
+ debug(3, "stab %d: closing scope (%d)\n", i + 1, scope);
+ continue;
+ } else if (stab->n_type == N_EINCL) {
+ /*
+ * There's a bug in the 5.2 (Taz) compilers that causes
+ * them to emit an extra N_EINCL if there's no actual
+ * text in the file being compiled. To work around this
+ * bug, we explicitly check to make sure we're not
+ * trying to pop a stack that only has the outer scope
+ * on it.
+ */
+ if (stack_level(file_stack) != 1) {
+ str = (char *)stack_pop(file_stack);
+ free(str);
+ curhdr = (char *)stack_peek(file_stack);
+ }
+ }
+
+ /* We only care about a subset of the stabs */
+ if (!(stab->n_type == N_FUN || stab->n_type == N_GSYM ||
+ stab->n_type == N_LCSYM || stab->n_type == N_LSYM ||
+ stab->n_type == N_PSYM || stab->n_type == N_ROSYM ||
+ stab->n_type == N_RSYM ||
+ stab->n_type == N_STSYM || stab->n_type == N_BINCL ||
+ stab->n_type == N_SO || stab->n_type == N_OPT))
+ continue;
+
+ if ((str = elf_strptr(elf, stabstridx,
+ (size_t)stab->n_strx)) == NULL) {
+ terminate("%s: Can't find string at %u for stab %d\n",
+ file, stab->n_strx, i);
+ }
+
+ if (stab->n_type == N_BINCL) {
+ curhdr = xstrdup(str);
+ stack_push(file_stack, curhdr);
+ continue;
+ } else if (stab->n_type == N_SO) {
+ if (str[strlen(str) - 1] != '/') {
+ strcpy(curpath, str);
+ curfile = basename(curpath);
+ }
+ continue;
+ } else if (stab->n_type == N_OPT) {
+ if (strcmp(str, "gcc2_compiled.") == 0) {
+ terminate("%s: GCC-generated stabs are "
+ "unsupported. Use DWARF instead.\n", file);
+ }
+ continue;
+ }
+
+ if (str[strlen(str) - 1] == '\\') {
+ int offset = 1;
+ /*
+ * There's a bug in the compilers that causes them to
+ * generate \ for continuations with just -g (this is
+ * ok), and \\ for continuations with -g -O (this is
+ * broken). This bug is "fixed" in the 6.2 compilers
+ * via the elimination of continuation stabs.
+ */
+ if (str[strlen(str) - 2] == '\\')
+ offset = 2;
+ fstr = concat(fstr, str, offset);
+ continue;
+ } else
+ fstr = concat(fstr, str, 0);
+
+ debug(3, "%4d: .stabs \"%s\", %#x, %d, %hd, %d (from %s)\n", i,
+ fstr, stab->n_type, 0, stab->n_desc,
+ stab->n_value, curhdr);
+
+ if (debug_level >= 3)
+ check_hash();
+
+ /*
+ * Sometimes the compiler stutters, and emits the same stab
+ * twice. This is bad for the parser, which will attempt to
+ * redefine the type IDs indicated in the stabs. This is
+ * compiler bug 4433511.
+ */
+ if (ofstr && strcmp(fstr, ofstr) == 0) {
+ debug(3, "Stutter stab\n");
+ free(fstr);
+ fstr = NULL;
+ continue;
+ }
+
+ if (ofstr)
+ free(ofstr);
+ ofstr = fstr;
+
+ iidescp = NULL;
+
+ if ((rc = parse_stab(stab, fstr, &iidescp)) < 0) {
+ terminate("%s: Couldn't parse stab \"%s\" "
+ "(source file %s)\n", file, str, curhdr);
+ }
+
+ if (rc == 0)
+ goto parse_loop_end;
+
+ /* Make sure the scope tracking is working correctly */
+ assert(stab->n_type != N_FUN || (iidescp->ii_type != II_GFUN &&
+ iidescp->ii_type != II_SFUN) || scope == 0);
+
+ /*
+ * The only things we care about that are in local scope are
+ * the N_PSYM stabs.
+ */
+ if (scope && stab->n_type != N_PSYM) {
+ if (iidescp)
+ iidesc_free(iidescp, NULL);
+ goto parse_loop_end;
+ }
+
+ switch (iidescp->ii_type) {
+ case II_SFUN:
+ iidescp->ii_owner = xstrdup(curfile);
+ /*FALLTHROUGH*/
+ case II_GFUN:
+ curfun = iidescp;
+ fnarg_free(iidescp);
+ iidesc_add(td->td_iihash, iidescp);
+ break;
+
+ case II_SVAR:
+ iidescp->ii_owner = xstrdup(curfile);
+ /*FALLTHROUGH*/
+ case II_GVAR:
+ case II_TYPE:
+ case II_SOU:
+ iidesc_add(td->td_iihash, iidescp);
+ break;
+
+ case II_PSYM:
+ fnarg_add(curfun, iidescp);
+ iidesc_free(iidescp, NULL);
+ break;
+ default:
+ aborterr("invalid ii_type %d for stab type %d",
+ iidescp->ii_type, stab->n_type);
+ }
+
+parse_loop_end:
+ fstr = NULL;
+ }
+
+ if (ofstr)
+ free(ofstr);
+
+ resolve_nodes(td);
+ resolve_typed_bitfields();
+ parse_finish(td);
+
+ cvt_fixstabs(td);
+ cvt_fixups(td, elf_ptrsz(elf));
+
+ return (0);
+}
diff --git a/cvt/stack.c b/cvt/stack.c
new file mode 100644
index 0000000..7c36cd5
--- /dev/null
+++ b/cvt/stack.c
@@ -0,0 +1,112 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating stacks
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "stack.h"
+#include "memory.h"
+
+#define STACK_SEEDSIZE 5
+
+struct stk {
+ int st_nument;
+ int st_top;
+ void **st_data;
+
+ void (*st_free)(void *);
+};
+
+stk_t *
+stack_new(void (*freep)(void *))
+{
+ stk_t *sp;
+
+ sp = xmalloc(sizeof (stk_t));
+ sp->st_nument = STACK_SEEDSIZE;
+ sp->st_top = -1;
+ sp->st_data = xmalloc(sizeof (void *) * sp->st_nument);
+ sp->st_free = freep;
+
+ return (sp);
+}
+
+void
+stack_free(stk_t *sp)
+{
+ int i;
+
+ if (sp->st_free) {
+ for (i = 0; i <= sp->st_top; i++)
+ sp->st_free(sp->st_data[i]);
+ }
+ free(sp->st_data);
+ free(sp);
+}
+
+void *
+stack_pop(stk_t *sp)
+{
+ assert(sp->st_top >= 0);
+
+ return (sp->st_data[sp->st_top--]);
+}
+
+void *
+stack_peek(stk_t *sp)
+{
+ if (sp->st_top == -1)
+ return (NULL);
+
+ return (sp->st_data[sp->st_top]);
+}
+
+void
+stack_push(stk_t *sp, void *data)
+{
+ sp->st_top++;
+
+ if (sp->st_top == sp->st_nument) {
+ sp->st_nument += STACK_SEEDSIZE;
+ sp->st_data = xrealloc(sp->st_data,
+ sizeof (void *) * sp->st_nument);
+ }
+
+ sp->st_data[sp->st_top] = data;
+}
+
+int
+stack_level(stk_t *sp)
+{
+ return (sp->st_top + 1);
+}
diff --git a/cvt/stack.h b/cvt/stack.h
new file mode 100644
index 0000000..7dca7cf
--- /dev/null
+++ b/cvt/stack.h
@@ -0,0 +1,53 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _STACK_H
+#define _STACK_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines for manipulating stacks
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct stk stk_t;
+
+stk_t *stack_new(void (*)(void *));
+void stack_free(stk_t *);
+void *stack_pop(stk_t *);
+void *stack_peek(stk_t *);
+void stack_push(stk_t *, void *);
+int stack_level(stk_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STACK_H */
diff --git a/cvt/strtab.c b/cvt/strtab.c
new file mode 100644
index 0000000..5a8ebc6
--- /dev/null
+++ b/cvt/strtab.c
@@ -0,0 +1,253 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "libctf/ctf_impl.h"
+#include "strtab.h"
+#include "memory.h"
+
+#define STRTAB_HASHSZ 211 /* use a prime number of hash buckets */
+#define STRTAB_BUFSZ (64 * 1024) /* use 64K data buffers by default */
+
+static void
+strtab_grow(strtab_t *sp)
+{
+ sp->str_nbufs++;
+ sp->str_bufs = xrealloc(sp->str_bufs, sp->str_nbufs * sizeof (char *));
+ sp->str_ptr = xmalloc(sp->str_bufsz);
+ sp->str_bufs[sp->str_nbufs - 1] = sp->str_ptr;
+}
+
+void
+strtab_create(strtab_t *sp)
+{
+ sp->str_hash = xcalloc(STRTAB_HASHSZ * sizeof (strhash_t *));
+ sp->str_hashsz = STRTAB_HASHSZ;
+ sp->str_bufs = NULL;
+ sp->str_ptr = NULL;
+ sp->str_nbufs = 0;
+ sp->str_bufsz = STRTAB_BUFSZ;
+ sp->str_nstrs = 1;
+ sp->str_size = 1;
+
+ strtab_grow(sp);
+ *sp->str_ptr++ = '\0';
+}
+
+void
+strtab_destroy(strtab_t *sp)
+{
+ strhash_t *hp, *hq;
+ ulong_t i;
+
+ for (i = 0; i < sp->str_hashsz; i++) {
+ for (hp = sp->str_hash[i]; hp != NULL; hp = hq) {
+ hq = hp->str_next;
+ free(hp);
+ }
+ }
+
+ for (i = 0; i < sp->str_nbufs; i++)
+ free(sp->str_bufs[i]);
+
+ free(sp->str_hash);
+ free(sp->str_bufs);
+}
+
+static ulong_t
+strtab_hash(const char *key, size_t *len)
+{
+ ulong_t g, h = 0;
+ const char *p;
+ size_t n = 0;
+
+ for (p = key; *p != '\0'; p++, n++) {
+ h = (h << 4) + *p;
+
+ if ((g = (h & 0xf0000000)) != 0) {
+ h ^= (g >> 24);
+ h ^= g;
+ }
+ }
+
+ *len = n;
+ return (h);
+}
+
+static int
+strtab_compare(strtab_t *sp, strhash_t *hp, const char *str, size_t len)
+{
+ ulong_t b = hp->str_buf;
+ const char *buf = hp->str_data;
+ size_t resid, n;
+ int rv;
+
+ while (len != 0) {
+ if (buf == sp->str_bufs[b] + sp->str_bufsz)
+ buf = sp->str_bufs[++b];
+
+ resid = sp->str_bufs[b] + sp->str_bufsz - buf;
+ n = MIN(resid, len);
+
+ if ((rv = strncmp(buf, str, n)) != 0)
+ return (rv);
+
+ buf += n;
+ str += n;
+ len -= n;
+ }
+
+ return (0);
+}
+
+static void
+strtab_copyin(strtab_t *sp, const char *str, size_t len)
+{
+ ulong_t b = sp->str_nbufs - 1;
+ size_t resid, n;
+
+ while (len != 0) {
+ if (sp->str_ptr == sp->str_bufs[b] + sp->str_bufsz) {
+ strtab_grow(sp);
+ b++;
+ }
+
+ resid = sp->str_bufs[b] + sp->str_bufsz - sp->str_ptr;
+ n = MIN(resid, len);
+ bcopy(str, sp->str_ptr, n);
+
+ sp->str_ptr += n;
+ str += n;
+ len -= n;
+ }
+}
+
+size_t
+strtab_insert(strtab_t *sp, const char *str)
+{
+ strhash_t *hp;
+ size_t len;
+ ulong_t h;
+
+ if (str == NULL || str[0] == '\0')
+ return (0); /* we keep a \0 at offset 0 to simplify things */
+
+ h = strtab_hash(str, &len) % sp->str_hashsz;
+
+ /*
+ * If the string is already in our hash table, just return the offset
+ * of the existing string element and do not add a duplicate string.
+ */
+ for (hp = sp->str_hash[h]; hp != NULL; hp = hp->str_next) {
+ if (strtab_compare(sp, hp, str, len + 1) == 0)
+ return (hp->str_off);
+ }
+
+ /*
+ * Create a new hash bucket, initialize it, and insert it at the front
+ * of the hash chain for the appropriate bucket.
+ */
+ hp = xmalloc(sizeof (strhash_t));
+
+ hp->str_data = sp->str_ptr;
+ hp->str_buf = sp->str_nbufs - 1;
+ hp->str_off = sp->str_size;
+ hp->str_len = len;
+ hp->str_next = sp->str_hash[h];
+
+ sp->str_hash[h] = hp;
+
+ /*
+ * Now copy the string data into our buffer list, and then update
+ * the global counts of strings and bytes. Return str's byte offset.
+ */
+ strtab_copyin(sp, str, len + 1);
+ sp->str_nstrs++;
+ sp->str_size += len + 1;
+
+ return (hp->str_off);
+}
+
+size_t
+strtab_size(const strtab_t *sp)
+{
+ return (sp->str_size);
+}
+
+ssize_t
+strtab_write(const strtab_t *sp,
+ ssize_t (*func)(void *, size_t, void *), void *priv)
+{
+ ssize_t res, total = 0;
+ ulong_t i;
+ size_t n;
+
+ for (i = 0; i < sp->str_nbufs; i++, total += res) {
+ if (i == sp->str_nbufs - 1)
+ n = sp->str_ptr - sp->str_bufs[i];
+ else
+ n = sp->str_bufsz;
+
+ if ((res = func(sp->str_bufs[i], n, priv)) <= 0)
+ break;
+ }
+
+ if (total == 0 && sp->str_size != 0)
+ return (-1);
+
+ return (total);
+}
+
+void
+strtab_print(const strtab_t *sp)
+{
+ const strhash_t *hp;
+ ulong_t i;
+
+ for (i = 0; i < sp->str_hashsz; i++) {
+ for (hp = sp->str_hash[i]; hp != NULL; hp = hp->str_next) {
+ const char *buf = hp->str_data;
+ ulong_t b = hp->str_buf;
+ size_t resid, len, n;
+
+ (void) printf("[%lu] %lu \"", (ulong_t)hp->str_off, b);
+
+ for (len = hp->str_len; len != 0; len -= n) {
+ if (buf == sp->str_bufs[b] + sp->str_bufsz)
+ buf = sp->str_bufs[++b];
+
+ resid = sp->str_bufs[b] + sp->str_bufsz - buf;
+ n = MIN(resid, len);
+
+ (void) printf("%.*s", (int)n, buf);
+ buf += n;
+ }
+
+ (void) printf("\"\n");
+ }
+ }
+}
diff --git a/cvt/strtab.h b/cvt/strtab.h
new file mode 100644
index 0000000..13c1e59
--- /dev/null
+++ b/cvt/strtab.h
@@ -0,0 +1,69 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 2001 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _STRTAB_H
+#define _STRTAB_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct strhash {
+ const char *str_data; /* pointer to actual string data */
+ ulong_t str_buf; /* index of string data buffer */
+ size_t str_off; /* offset in bytes of this string */
+ size_t str_len; /* length in bytes of this string */
+ struct strhash *str_next; /* next string in hash chain */
+} strhash_t;
+
+typedef struct strtab {
+ strhash_t **str_hash; /* array of hash buckets */
+ ulong_t str_hashsz; /* size of hash bucket array */
+ char **str_bufs; /* array of buffer pointers */
+ char *str_ptr; /* pointer to current buffer location */
+ ulong_t str_nbufs; /* size of buffer pointer array */
+ size_t str_bufsz; /* size of individual buffer */
+ ulong_t str_nstrs; /* total number of strings in strtab */
+ size_t str_size; /* total size of strings in bytes */
+} strtab_t;
+
+extern void strtab_create(strtab_t *);
+extern void strtab_destroy(strtab_t *);
+extern size_t strtab_insert(strtab_t *, const char *);
+extern size_t strtab_size(const strtab_t *);
+extern ssize_t strtab_write(const strtab_t *,
+ ssize_t (*)(void *, size_t, void *), void *);
+extern void strtab_print(const strtab_t *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STRTAB_H */
diff --git a/cvt/tdata.c b/cvt/tdata.c
new file mode 100644
index 0000000..524cf04
--- /dev/null
+++ b/cvt/tdata.c
@@ -0,0 +1,483 @@
+/*
+ * 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 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * Routines for manipulating tdesc and tdata structures
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "memory.h"
+#include "traverse.h"
+
+/*
+ * The layout hash is used during the equivalency checking. We have a node in
+ * the child graph that may be equivalent to a node in the parent graph. To
+ * find the corresponding node (if any) in the parent, we need a quick way to
+ * get to all nodes in the parent that look like the node in the child. Since a
+ * large number of nodes don't have names, we need to incorporate the layout of
+ * the node into the hash. If we don't, we'll end up with the vast majority of
+ * nodes in bucket zero, with one or two nodes in each of the remaining buckets.
+ *
+ * There are a couple of constraints, both of which concern forward
+ * declarations. Recall that a forward declaration tdesc is equivalent to a
+ * tdesc that actually defines the structure or union. As such, we cannot
+ * incorporate anything into the hash for a named struct or union node that
+ * couldn't be found by looking at the forward, and vice versa.
+ */
+int
+tdesc_layouthash(int nbuckets, void *node)
+{
+ tdesc_t *tdp = node;
+ char *name = NULL;
+ ulong_t h = 0;
+
+ if (tdp->t_name)
+ name = tdp->t_name;
+ else {
+ switch (tdp->t_type) {
+ case POINTER:
+ case TYPEDEF:
+ case VOLATILE:
+ case CONST:
+ case RESTRICT:
+ name = tdp->t_tdesc->t_name;
+ break;
+ case FUNCTION:
+ h = tdp->t_fndef->fn_nargs +
+ tdp->t_fndef->fn_vargs;
+ name = tdp->t_fndef->fn_ret->t_name;
+ break;
+ case ARRAY:
+ h = tdp->t_ardef->ad_nelems;
+ name = tdp->t_ardef->ad_contents->t_name;
+ break;
+ case STRUCT:
+ case UNION:
+ /*
+ * Unnamed structures, which cannot have forward
+ * declarations pointing to them. We can therefore
+ * incorporate the name of the first member into
+ * the hash value, assuming there are any.
+ */
+ if (tdp->t_members != NULL)
+ name = tdp->t_members->ml_name;
+ break;
+ case ENUM:
+ /* Use the first element in the hash value */
+ name = tdp->t_emem->el_name;
+ break;
+ default:
+ /*
+ * Intrinsics, forwards, and typedefs all have
+ * names.
+ */
+ warning("Unexpected unnamed %d tdesc (ID %d)\n",
+ tdp->t_type, tdp->t_id);
+ }
+ }
+
+ if (name)
+ return (hash_name(nbuckets, name));
+
+ return (h % nbuckets);
+}
+
+int
+tdesc_layoutcmp(void *arg1, void *arg2)
+{
+ tdesc_t *tdp1 = arg1, *tdp2 = arg2;
+
+ if (tdp1->t_name == NULL) {
+ if (tdp2->t_name == NULL)
+ return (0);
+ else
+ return (-1);
+ } else if (tdp2->t_name == NULL)
+ return (1);
+ else
+ return (strcmp(tdp1->t_name, tdp2->t_name));
+}
+
+int
+tdesc_idhash(int nbuckets, void *data)
+{
+ tdesc_t *tdp = data;
+
+ return (tdp->t_id % nbuckets);
+}
+
+int
+tdesc_idcmp(void *arg1, void *arg2)
+{
+ tdesc_t *tdp1 = arg1, *tdp2 = arg2;
+
+ if (tdp1->t_id == tdp2->t_id)
+ return (0);
+ else
+ return (tdp1->t_id > tdp2->t_id ? 1 : -1);
+}
+
+int
+tdesc_namehash(int nbuckets, void *data)
+{
+ tdesc_t *tdp = data;
+ ulong_t h, g;
+ char *c;
+
+ if (tdp->t_name == NULL)
+ return (0);
+
+ for (h = 0, c = tdp->t_name; *c; c++) {
+ h = (h << 4) + *c;
+ if ((g = (h & 0xf0000000)) != 0) {
+ h ^= (g >> 24);
+ h ^= g;
+ }
+ }
+
+ return (h % nbuckets);
+}
+
+int
+tdesc_namecmp(void *arg1, void *arg2)
+{
+ tdesc_t *tdp1 = arg1, *tdp2 = arg2;
+
+ return (!streq(tdp1->t_name, tdp2->t_name));
+}
+
+#if defined(sun)
+/*ARGSUSED1*/
+static int
+tdesc_print(void *data, void *private __unused)
+{
+ tdesc_t *tdp = data;
+
+ printf("%7d %s\n", tdp->t_id, tdesc_name(tdp));
+
+ return (1);
+}
+#endif
+
+static void
+free_intr(tdesc_t *tdp)
+{
+ free(tdp->t_intr);
+}
+
+static void
+free_ardef(tdesc_t *tdp)
+{
+ free(tdp->t_ardef);
+}
+
+static void
+free_mlist(tdesc_t *tdp)
+{
+ mlist_t *ml = tdp->t_members;
+ mlist_t *oml;
+
+ while (ml) {
+ oml = ml;
+ ml = ml->ml_next;
+
+ if (oml->ml_name)
+ free(oml->ml_name);
+ free(oml);
+ }
+}
+
+static void
+free_elist(tdesc_t *tdp)
+{
+ elist_t *el = tdp->t_emem;
+ elist_t *oel;
+
+ while (el) {
+ oel = el;
+ el = el->el_next;
+
+ if (oel->el_name)
+ free(oel->el_name);
+ free(oel);
+ }
+}
+
+static void (*free_cbs[])(tdesc_t *) = {
+ NULL,
+ free_intr,
+ NULL,
+ free_ardef,
+ NULL,
+ free_mlist,
+ free_mlist,
+ free_elist,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*ARGSUSED1*/
+static void
+tdesc_free_cb(void *arg, void *private __unused)
+{
+ tdesc_t *tdp = arg;
+ if (tdp->t_name)
+ free(tdp->t_name);
+ if (free_cbs[tdp->t_type])
+ free_cbs[tdp->t_type](tdp);
+ free(tdp);
+
+ return;
+}
+
+void
+tdesc_free(tdesc_t *tdp)
+{
+ tdesc_free_cb(tdp, NULL);
+}
+
+static int
+tdata_label_cmp(void *arg1, void *arg2)
+{
+ labelent_t *le1 = arg1;
+ labelent_t *le2 = arg2;
+ return (le1->le_idx - le2->le_idx);
+}
+
+void
+tdata_label_add(tdata_t *td, const char *label, int idx)
+{
+ labelent_t *le = xmalloc(sizeof (*le));
+
+ le->le_name = xstrdup(label);
+ le->le_idx = (idx == -1 ? td->td_nextid - 1 : idx);
+
+ slist_add(&td->td_labels, le, tdata_label_cmp);
+}
+
+static int
+tdata_label_top_cb(void *data, void *arg)
+{
+ labelent_t *le = data;
+ labelent_t **topp = arg;
+
+ *topp = le;
+
+ return (1);
+}
+
+labelent_t *
+tdata_label_top(tdata_t *td)
+{
+ labelent_t *top = NULL;
+
+ (void) list_iter(td->td_labels, tdata_label_top_cb, &top);
+
+ return (top);
+}
+
+static int
+tdata_label_find_cb(void *arg1, void *arg2)
+{
+ labelent_t *le = arg1;
+ labelent_t *tmpl = arg2;
+ return (streq(le->le_name, tmpl->le_name));
+}
+
+int
+tdata_label_find(tdata_t *td, char *label)
+{
+ labelent_t let;
+ labelent_t *ret;
+
+ if (streq(label, "BASE")) {
+ ret = (labelent_t *)list_first(td->td_labels);
+ return (ret ? ret->le_idx : -1);
+ }
+
+ let.le_name = label;
+
+ if (!(ret = (labelent_t *)list_find(td->td_labels, &let,
+ tdata_label_find_cb)))
+ return (-1);
+
+ return (ret->le_idx);
+}
+
+static int
+tdata_label_newmax_cb(void *data, void *arg)
+{
+ labelent_t *le = data;
+ int *newmaxp = arg;
+
+ if (le->le_idx > *newmaxp) {
+ le->le_idx = *newmaxp;
+ return (1);
+ }
+
+ return (0);
+}
+
+void
+tdata_label_newmax(tdata_t *td, int newmax)
+{
+ (void) list_iter(td->td_labels, tdata_label_newmax_cb, &newmax);
+}
+
+/*ARGSUSED1*/
+static void
+tdata_label_free_cb(void *arg, void *private __unused)
+{
+ labelent_t *le = arg;
+ if (le->le_name)
+ free(le->le_name);
+ free(le);
+}
+
+void
+tdata_label_free(tdata_t *td)
+{
+ list_free(td->td_labels, tdata_label_free_cb, NULL);
+ td->td_labels = NULL;
+}
+
+tdata_t *
+tdata_new(void)
+{
+ tdata_t *new = xcalloc(sizeof (tdata_t));
+
+ new->td_layouthash = hash_new(TDATA_LAYOUT_HASH_SIZE, tdesc_layouthash,
+ tdesc_layoutcmp);
+ new->td_idhash = hash_new(TDATA_ID_HASH_SIZE, tdesc_idhash,
+ tdesc_idcmp);
+ /*
+ * This is also traversed as a list, but amortized O(1)
+ * lookup massively impacts part of the merge phase, so
+ * we store the iidescs as a hash.
+ */
+ new->td_iihash = hash_new(IIDESC_HASH_SIZE, iidesc_hash, NULL);
+ new->td_nextid = 1;
+ new->td_curvgen = 1;
+
+ pthread_mutex_init(&new->td_mergelock, NULL);
+
+ return (new);
+}
+
+void
+tdata_free(tdata_t *td)
+{
+ hash_free(td->td_iihash, iidesc_free, NULL);
+ hash_free(td->td_layouthash, tdesc_free_cb, NULL);
+ hash_free(td->td_idhash, NULL, NULL);
+ list_free(td->td_fwdlist, NULL, NULL);
+
+ tdata_label_free(td);
+
+ free(td->td_parlabel);
+ free(td->td_parname);
+
+ pthread_mutex_destroy(&td->td_mergelock);
+
+ free(td);
+}
+
+/*ARGSUSED1*/
+static int
+build_hashes(tdesc_t *ctdp, tdesc_t **ctdpp __unused, void *private)
+{
+ tdata_t *td = private;
+
+ hash_add(td->td_idhash, ctdp);
+ hash_add(td->td_layouthash, ctdp);
+
+ return (1);
+}
+
+static tdtrav_cb_f build_hashes_cbs[] = {
+ NULL,
+ build_hashes, /* intrinsic */
+ build_hashes, /* pointer */
+ build_hashes, /* array */
+ build_hashes, /* function */
+ build_hashes, /* struct */
+ build_hashes, /* union */
+ build_hashes, /* enum */
+ build_hashes, /* forward */
+ build_hashes, /* typedef */
+ tdtrav_assert, /* typedef_unres */
+ build_hashes, /* volatile */
+ build_hashes, /* const */
+ build_hashes /* restrict */
+};
+
+static void
+tdata_build_hashes_common(tdata_t *td, hash_t *hash)
+{
+ (void) iitraverse_hash(hash, &td->td_curvgen, NULL, NULL,
+ build_hashes_cbs, td);
+}
+
+void
+tdata_build_hashes(tdata_t *td)
+{
+ tdata_build_hashes_common(td, td->td_iihash);
+}
+
+/* Merge td2 into td1. td2 is destroyed by the merge */
+void
+tdata_merge(tdata_t *td1, tdata_t *td2)
+{
+ td1->td_curemark = MAX(td1->td_curemark, td2->td_curemark);
+ td1->td_curvgen = MAX(td1->td_curvgen, td2->td_curvgen);
+ td1->td_nextid = MAX(td1->td_nextid, td2->td_nextid);
+
+ hash_merge(td1->td_iihash, td2->td_iihash);
+
+ /* Add td2's type tree to the hashes */
+ tdata_build_hashes_common(td1, td2->td_iihash);
+
+ list_concat(&td1->td_fwdlist, td2->td_fwdlist);
+ td2->td_fwdlist = NULL;
+
+ slist_merge(&td1->td_labels, td2->td_labels,
+ tdata_label_cmp);
+ td2->td_labels = NULL;
+
+ /* free the td2 hashes (data is now part of td1) */
+
+ hash_free(td2->td_layouthash, NULL, NULL);
+ td2->td_layouthash = NULL;
+
+ hash_free(td2->td_iihash, NULL, NULL);
+ td2->td_iihash = NULL;
+
+ tdata_free(td2);
+}
diff --git a/cvt/traverse.c b/cvt/traverse.c
new file mode 100644
index 0000000..febd0b3
--- /dev/null
+++ b/cvt/traverse.c
@@ -0,0 +1,224 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines used to traverse tdesc trees, invoking user-supplied callbacks
+ * as the tree is traversed.
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "traverse.h"
+#include "memory.h"
+
+int (*tddescenders[])(tdesc_t *, tdtrav_data_t *);
+tdtrav_cb_f tdnops[];
+
+void
+tdtrav_init(tdtrav_data_t *tdtd, int *vgenp, tdtrav_cb_f *firstops,
+ tdtrav_cb_f *preops, tdtrav_cb_f *postops, void *private)
+{
+ tdtd->vgen = ++(*vgenp);
+ tdtd->firstops = firstops ? firstops : tdnops;
+ tdtd->preops = preops ? preops : tdnops;
+ tdtd->postops = postops ? postops : tdnops;
+ tdtd->private = private;
+}
+
+static int
+tdtrav_plain(tdesc_t *this, tdtrav_data_t *tdtd)
+{
+ return (tdtraverse(this->t_tdesc, &this->t_tdesc, tdtd));
+}
+
+static int
+tdtrav_func(tdesc_t *this, tdtrav_data_t *tdtd)
+{
+ fndef_t *fn = this->t_fndef;
+ int i, rc;
+
+ if ((rc = tdtraverse(fn->fn_ret, &fn->fn_ret, tdtd)) < 0)
+ return (rc);
+
+ for (i = 0; i < (int) fn->fn_nargs; i++) {
+ if ((rc = tdtraverse(fn->fn_args[i], &fn->fn_args[i],
+ tdtd)) < 0)
+ return (rc);
+ }
+
+ return (0);
+}
+
+static int
+tdtrav_array(tdesc_t *this, tdtrav_data_t *tdtd)
+{
+ ardef_t *ardef = this->t_ardef;
+ int rc;
+
+ if ((rc = tdtraverse(ardef->ad_contents, &ardef->ad_contents,
+ tdtd)) < 0)
+ return (rc);
+
+ return (tdtraverse(ardef->ad_idxtype, &ardef->ad_idxtype, tdtd));
+}
+
+static int
+tdtrav_su(tdesc_t *this, tdtrav_data_t *tdtd)
+{
+ mlist_t *ml;
+ int rc = 0;
+
+ for (ml = this->t_members; ml; ml = ml->ml_next) {
+ if ((rc = tdtraverse(ml->ml_type, &ml->ml_type, tdtd)) < 0)
+ return (rc);
+ }
+
+ return (rc);
+}
+
+/*ARGSUSED*/
+int
+tdtrav_assert(tdesc_t *node __unused, tdesc_t **nodep __unused, void *private __unused)
+{
+ assert(1 == 0);
+
+ return (-1);
+}
+
+tdtrav_cb_f tdnops[] = {
+ NULL,
+ NULL, /* intrinsic */
+ NULL, /* pointer */
+ NULL, /* array */
+ NULL, /* function */
+ NULL, /* struct */
+ NULL, /* union */
+ NULL, /* enum */
+ NULL, /* forward */
+ NULL, /* typedef */
+ NULL, /* typedef_unres */
+ NULL, /* volatile */
+ NULL, /* const */
+ NULL /* restrict */
+};
+
+int (*tddescenders[])(tdesc_t *, tdtrav_data_t *) = {
+ NULL,
+ NULL, /* intrinsic */
+ tdtrav_plain, /* pointer */
+ tdtrav_array, /* array */
+ tdtrav_func, /* function */
+ tdtrav_su, /* struct */
+ tdtrav_su, /* union */
+ NULL, /* enum */
+ NULL, /* forward */
+ tdtrav_plain, /* typedef */
+ NULL, /* typedef_unres */
+ tdtrav_plain, /* volatile */
+ tdtrav_plain, /* const */
+ tdtrav_plain /* restrict */
+};
+
+int
+tdtraverse(tdesc_t *this, tdesc_t **thisp, tdtrav_data_t *tdtd)
+{
+ tdtrav_cb_f travcb;
+ int (*descender)(tdesc_t *, tdtrav_data_t *);
+ int descend = 1;
+ int rc;
+
+ if ((travcb = tdtd->firstops[this->t_type]) != NULL) {
+ if ((rc = travcb(this, thisp, tdtd->private)) < 0)
+ return (rc);
+ else if (rc == 0)
+ descend = 0;
+ }
+
+ if (this->t_vgen == tdtd->vgen)
+ return (1);
+ this->t_vgen = tdtd->vgen;
+
+ if (descend && (travcb = tdtd->preops[this->t_type]) != NULL) {
+ if ((rc = travcb(this, thisp, tdtd->private)) < 0)
+ return (rc);
+ else if (rc == 0)
+ descend = 0;
+ }
+
+ if (descend) {
+ if ((descender = tddescenders[this->t_type]) != NULL &&
+ (rc = descender(this, tdtd)) < 0)
+ return (rc);
+
+ if ((travcb = tdtd->postops[this->t_type]) != NULL &&
+ (rc = travcb(this, thisp, tdtd->private)) < 0)
+ return (rc);
+ }
+
+ return (1);
+}
+
+int
+iitraverse_td(void *arg1, void *arg2)
+{
+ iidesc_t *ii = arg1;
+ tdtrav_data_t *tdtd = arg2;
+ int i, rc;
+
+ if ((rc = tdtraverse(ii->ii_dtype, &ii->ii_dtype, tdtd)) < 0)
+ return (rc);
+
+ for (i = 0; i < ii->ii_nargs; i++) {
+ if ((rc = tdtraverse(ii->ii_args[i], &ii->ii_args[i],
+ tdtd)) < 0)
+ return (rc);
+ }
+
+ return (1);
+}
+
+int
+iitraverse(iidesc_t *ii, int *vgenp, tdtrav_cb_f *firstops, tdtrav_cb_f *preops,
+ tdtrav_cb_f *postops, void *private)
+{
+ tdtrav_data_t tdtd;
+
+ tdtrav_init(&tdtd, vgenp, firstops, preops, postops, private);
+
+ return (iitraverse_td(ii, &tdtd));
+}
+
+int
+iitraverse_hash(hash_t *iihash, int *vgenp, tdtrav_cb_f *firstops,
+ tdtrav_cb_f *preops, tdtrav_cb_f *postops, void *private)
+{
+ tdtrav_data_t tdtd;
+
+ tdtrav_init(&tdtd, vgenp, firstops, preops, postops, private);
+
+ return (hash_iter(iihash, iitraverse_td, &tdtd));
+}
diff --git a/cvt/traverse.h b/cvt/traverse.h
new file mode 100644
index 0000000..6a56370
--- /dev/null
+++ b/cvt/traverse.h
@@ -0,0 +1,71 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _TRAVERSE_H
+#define _TRAVERSE_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Routines used to traverse tdesc trees, invoking user-supplied callbacks
+ * as the tree is traversed.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ctftools.h"
+
+typedef int (*tdtrav_cb_f)(tdesc_t *, tdesc_t **, void *);
+
+typedef struct tdtrav_data {
+ int vgen;
+
+ tdtrav_cb_f *firstops;
+ tdtrav_cb_f *preops;
+ tdtrav_cb_f *postops;
+
+ void *private;
+} tdtrav_data_t;
+
+void tdtrav_init(tdtrav_data_t *, int *, tdtrav_cb_f *, tdtrav_cb_f *,
+ tdtrav_cb_f *, void *);
+int tdtraverse(tdesc_t *, tdesc_t **, tdtrav_data_t *);
+
+int iitraverse(iidesc_t *, int *, tdtrav_cb_f *, tdtrav_cb_f *, tdtrav_cb_f *,
+ void *);
+int iitraverse_hash(hash_t *, int *, tdtrav_cb_f *, tdtrav_cb_f *,
+ tdtrav_cb_f *, void *);
+int iitraverse_td(void *, void *);
+
+int tdtrav_assert(tdesc_t *, tdesc_t **, void *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TRAVERSE_H */
diff --git a/cvt/util.c b/cvt/util.c
new file mode 100644
index 0000000..1364fbc
--- /dev/null
+++ b/cvt/util.c
@@ -0,0 +1,273 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Utility functions
+ */
+
+#include "libctf/ctf_impl.h"
+#include "ctftools.h"
+#include "memory.h"
+
+static void (*terminate_cleanup)(void) = NULL;
+
+/* returns 1 if s1 == s2, 0 otherwise */
+int
+streq(const char *s1, const char *s2)
+{
+ if (s1 == NULL) {
+ if (s2 != NULL)
+ return (0);
+ } else if (s2 == NULL)
+ return (0);
+ else if (strcmp(s1, s2) != 0)
+ return (0);
+
+ return (1);
+}
+
+int
+findelfsecidx(Elf *elf, const char *file, const char *tofind)
+{
+ Elf_Scn *scn = NULL;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ elfterminate(file, "Couldn't read ehdr");
+
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ char *name;
+
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ elfterminate(file,
+ "Couldn't read header for section %d",
+ elf_ndxscn(scn));
+ }
+
+ if ((name = elf_strptr(elf, ehdr.e_shstrndx,
+ (size_t)shdr.sh_name)) == NULL) {
+ elfterminate(file,
+ "Couldn't get name for section %d",
+ elf_ndxscn(scn));
+ }
+
+ if (strcmp(name, tofind) == 0)
+ return (elf_ndxscn(scn));
+ }
+
+ return (-1);
+}
+
+size_t
+elf_ptrsz(Elf *elf)
+{
+ GElf_Ehdr ehdr;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ terminate("failed to read ELF header: %s\n",
+ elf_errmsg(-1));
+ }
+
+ if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
+ return (4);
+ else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
+ return (8);
+ else
+ terminate("unknown ELF class %d\n", ehdr.e_ident[EI_CLASS]);
+
+ /*NOTREACHED*/
+ return (0);
+}
+
+/*PRINTFLIKE2*/
+static void
+whine(const char *type, const char *format, va_list ap)
+{
+ int error = errno;
+
+ fprintf(stderr, "%s: %s: ", type, progname);
+ vfprintf(stderr, format, ap);
+
+ if (format[strlen(format) - 1] != '\n')
+ fprintf(stderr, ": %s\n", strerror(error));
+}
+
+void
+set_terminate_cleanup(void (*cleanup)(void))
+{
+ terminate_cleanup = cleanup;
+}
+
+/*PRINTFLIKE1*/
+void
+terminate(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ whine("ERROR", format, ap);
+ va_end(ap);
+
+ if (terminate_cleanup)
+ terminate_cleanup();
+
+ if (getenv("CTF_ABORT_ON_TERMINATE") != NULL)
+ abort();
+#if defined(__FreeBSD__)
+/*
+ * For the time being just output the termination message, but don't
+ * return an exit status that would cause the build to fail. We need
+ * to get as much stuff built as possible before going back and
+ * figuring out what is wrong with certain files.
+ */
+ exit(0);
+#else
+ exit(1);
+#endif
+}
+
+/*PRINTFLIKE1*/
+void
+aborterr(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ whine("ERROR", format, ap);
+ va_end(ap);
+
+#if defined(sun)
+ abort();
+#else
+ exit(0);
+#endif
+}
+
+/*PRINTFLIKE1*/
+void
+warning(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ whine("WARNING", format, ap);
+ va_end(ap);
+
+ if (debug_level >= 3)
+ terminate("Termination due to warning\n");
+}
+
+/*PRINTFLIKE2*/
+void
+vadebug(int level, const char *format, va_list ap)
+{
+ if (level > debug_level)
+ return;
+
+ (void) fprintf(DEBUG_STREAM, "DEBUG: ");
+ (void) vfprintf(DEBUG_STREAM, format, ap);
+ fflush(DEBUG_STREAM);
+}
+
+/*PRINTFLIKE2*/
+void
+debug(int level, const char *format, ...)
+{
+ va_list ap;
+
+ if (level > debug_level)
+ return;
+
+ va_start(ap, format);
+ (void) vadebug(level, format, ap);
+ va_end(ap);
+}
+
+char *
+mktmpname(const char *origname, const char *suffix)
+{
+ char *newname;
+
+ newname = xmalloc(strlen(origname) + strlen(suffix) + 1);
+ (void) strcpy(newname, origname);
+ (void) strcat(newname, suffix);
+ return (newname);
+}
+
+/*PRINTFLIKE2*/
+void
+elfterminate(const char *file, const char *fmt, ...)
+{
+ static char msgbuf[BUFSIZ];
+ va_list ap;
+
+ va_start(ap, fmt);
+ vsnprintf(msgbuf, sizeof (msgbuf), fmt, ap);
+ va_end(ap);
+
+ terminate("%s: %s: %s\n", file, msgbuf, elf_errmsg(-1));
+}
+
+const char *
+tdesc_name(tdesc_t *tdp)
+{
+ return (tdp->t_name == NULL ? "(anon)" : tdp->t_name);
+}
+
+char *watch_address = NULL;
+int watch_length = 0;
+
+void
+watch_set(void *addr, int len)
+{
+ watch_address = addr;
+ watch_length = len;
+}
+
+void
+watch_dump(int v)
+{
+ char *p = watch_address;
+ int i;
+
+ if (watch_address == NULL || watch_length == 0)
+ return;
+
+ printf("%d: watch %p len %d\n",v,watch_address,watch_length);
+ for (i = 0; i < watch_length; i++) {
+ if (*p >= 0x20 && *p < 0x7f) {
+ printf(" %c",*p++ & 0xff);
+ } else {
+ printf(" %02x",*p++ & 0xff);
+ }
+ }
+ printf("\n");
+
+}
+
+
diff --git a/dump/dump.c b/dump/dump.c
new file mode 100644
index 0000000..2a94482
--- /dev/null
+++ b/dump/dump.c
@@ -0,0 +1,1015 @@
+/*
+ * 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 "libctf/ctf_impl.h"
+#include "common/utils.h"
+#include "common/symbol.h"
+
+#define WARN(x) { warn(x); return (E_ERROR); }
+
+/*
+ * Flags that indicate what data is to be displayed. An explicit `all' value is
+ * provided to allow the code to distinguish between a request for everything
+ * (currently requested by invoking ctfdump without flags) and individual
+ * requests for all of the types of data (an invocation with all flags). In the
+ * former case, we want to be able to implicitly adjust the definition of `all'
+ * based on the CTF version of the file being dumped. For example, if a v2 file
+ * is being dumped, `all' includes F_LABEL - a request to dump the label
+ * section. If a v1 file is being dumped, `all' does not include F_LABEL,
+ * because v1 CTF doesn't support labels. We need to be able to distinguish
+ * between `ctfdump foo', which has an implicit request for labels if `foo'
+ * supports them, and `ctfdump -l foo', which has an explicity request. In the
+ * latter case, we exit with an error if `foo' is a v1 CTF file.
+ */
+static enum {
+ F_DATA = 0x01, /* show data object section */
+ F_FUNC = 0x02, /* show function section */
+ F_HDR = 0x04, /* show header */
+ F_STR = 0x08, /* show string table */
+ F_TYPES = 0x10, /* show type section */
+ F_STATS = 0x20, /* show statistics */
+ F_LABEL = 0x40, /* show label section */
+ F_ALL = 0x80, /* explicit request for `all' */
+ F_ALLMSK = 0xff /* show all sections and statistics */
+} flags = 0;
+
+static struct {
+ ulong_t s_ndata; /* total number of data objects */
+ ulong_t s_nfunc; /* total number of functions */
+ ulong_t s_nargs; /* total number of function arguments */
+ ulong_t s_argmax; /* longest argument list */
+ ulong_t s_ntypes; /* total number of types */
+ ulong_t s_types[16]; /* number of types by kind */
+ ulong_t s_nsmem; /* total number of struct members */
+ ulong_t s_nsbytes; /* total size of all structs */
+ ulong_t s_smmax; /* largest struct in terms of members */
+ ulong_t s_sbmax; /* largest struct in terms of bytes */
+ ulong_t s_numem; /* total number of union members */
+ ulong_t s_nubytes; /* total size of all unions */
+ ulong_t s_ummax; /* largest union in terms of members */
+ ulong_t s_ubmax; /* largest union in terms of bytes */
+ ulong_t s_nemem; /* total number of enum members */
+ ulong_t s_emmax; /* largest enum in terms of members */
+ ulong_t s_nstr; /* total number of strings */
+ size_t s_strlen; /* total length of all strings */
+ size_t s_strmax; /* longest string length */
+} stats;
+
+typedef struct ctf_data {
+ caddr_t cd_ctfdata; /* Pointer to the CTF data */
+ size_t cd_ctflen; /* Length of CTF data */
+
+ /*
+ * cd_symdata will be non-NULL if the CTF data is being retrieved from
+ * an ELF file with a symbol table. cd_strdata and cd_nsyms should be
+ * used only if cd_symdata is non-NULL.
+ */
+ Elf_Data *cd_symdata; /* Symbol table */
+ Elf_Data *cd_strdata; /* Symbol table strings */
+ int cd_nsyms; /* Number of symbol table entries */
+} ctf_data_t;
+
+static const char *
+ref_to_str(uint_t name, const ctf_header_t *hp, const ctf_data_t *cd)
+{
+ size_t offset = CTF_NAME_OFFSET(name);
+ const char *s = cd->cd_ctfdata + hp->cth_stroff + offset;
+
+ if (CTF_NAME_STID(name) != CTF_STRTAB_0)
+ return ("<< ??? - name in external strtab >>");
+
+ if (offset >= hp->cth_strlen)
+ return ("<< ??? - name exceeds strlab len >>");
+
+ if (hp->cth_stroff + offset >= cd->cd_ctflen)
+ return ("<< ??? - file truncated >>");
+
+ if (s[0] == '\0')
+ return ("(anon)");
+
+ return (s);
+}
+
+static const char *
+int_encoding_to_str(uint_t encoding)
+{
+ static char buf[32];
+
+ if (encoding == 0 || (encoding & ~(CTF_INT_SIGNED | CTF_INT_CHAR |
+ CTF_INT_BOOL | CTF_INT_VARARGS)) != 0)
+ (void) snprintf(buf, sizeof (buf), " 0x%x", encoding);
+ else {
+ buf[0] = '\0';
+ if (encoding & CTF_INT_SIGNED)
+ (void) strcat(buf, " SIGNED");
+ if (encoding & CTF_INT_CHAR)
+ (void) strcat(buf, " CHAR");
+ if (encoding & CTF_INT_BOOL)
+ (void) strcat(buf, " BOOL");
+ if (encoding & CTF_INT_VARARGS)
+ (void) strcat(buf, " VARARGS");
+ }
+
+ return (buf + 1);
+}
+
+static const char *
+fp_encoding_to_str(uint_t encoding)
+{
+ static const char *const encs[] = {
+ NULL, "SINGLE", "DOUBLE", "COMPLEX", "DCOMPLEX", "LDCOMPLEX",
+ "LDOUBLE", "INTERVAL", "DINTERVAL", "LDINTERVAL", "IMAGINARY",
+ "DIMAGINARY", "LDIMAGINARY"
+ };
+
+ static char buf[16];
+
+ if (encoding < 1 || encoding >= (sizeof (encs) / sizeof (char *))) {
+ (void) snprintf(buf, sizeof (buf), "%u", encoding);
+ return (buf);
+ }
+
+ return (encs[encoding]);
+}
+
+static void
+print_line(const char *s)
+{
+ static const char line[] = "----------------------------------------"
+ "----------------------------------------";
+ (void) printf("\n%s%.*s\n\n", s, (int)(78 - strlen(s)), line);
+}
+
+static int
+print_header(const ctf_header_t *hp, const ctf_data_t *cd)
+{
+ print_line("- CTF Header ");
+
+ (void) printf(" cth_magic = 0x%04x\n", hp->cth_magic);
+ (void) printf(" cth_version = %u\n", hp->cth_version);
+ (void) printf(" cth_flags = 0x%02x\n", hp->cth_flags);
+ (void) printf(" cth_parlabel = %s\n",
+ ref_to_str(hp->cth_parlabel, hp, cd));
+ (void) printf(" cth_parname = %s\n",
+ ref_to_str(hp->cth_parname, hp, cd));
+ (void) printf(" cth_lbloff = %u\n", hp->cth_lbloff);
+ (void) printf(" cth_objtoff = %u\n", hp->cth_objtoff);
+ (void) printf(" cth_funcoff = %u\n", hp->cth_funcoff);
+ (void) printf(" cth_typeoff = %u\n", hp->cth_typeoff);
+ (void) printf(" cth_stroff = %u\n", hp->cth_stroff);
+ (void) printf(" cth_strlen = %u\n", hp->cth_strlen);
+
+ return (E_SUCCESS);
+}
+
+static int
+print_labeltable(const ctf_header_t *hp, const ctf_data_t *cd)
+{
+ void *v = (void *) (cd->cd_ctfdata + hp->cth_lbloff);
+ const ctf_lblent_t *ctl = v;
+ ulong_t i, n = (hp->cth_objtoff - hp->cth_lbloff) / sizeof (*ctl);
+
+ print_line("- Label Table ");
+
+ if (hp->cth_lbloff & 3)
+ WARN("cth_lbloff is not aligned properly\n");
+ if (hp->cth_lbloff >= cd->cd_ctflen)
+ WARN("file is truncated or cth_lbloff is corrupt\n");
+ if (hp->cth_objtoff >= cd->cd_ctflen)
+ WARN("file is truncated or cth_objtoff is corrupt\n");
+ if (hp->cth_lbloff > hp->cth_objtoff)
+ WARN("file is corrupt -- cth_lbloff > cth_objtoff\n");
+
+ for (i = 0; i < n; i++, ctl++) {
+ (void) printf(" %5u %s\n", ctl->ctl_typeidx,
+ ref_to_str(ctl->ctl_label, hp, cd));
+ }
+
+ return (E_SUCCESS);
+}
+
+/*
+ * Given the current symbol index (-1 to start at the beginning of the symbol
+ * table) and the type of symbol to match, this function returns the index of
+ * the next matching symbol (if any), and places the name of that symbol in
+ * *namep. If no symbol is found, -1 is returned.
+ */
+static int
+next_sym(const ctf_data_t *cd, const int symidx, const uchar_t matchtype,
+ char **namep)
+{
+ int i;
+
+ for (i = symidx + 1; i < cd->cd_nsyms; i++) {
+ GElf_Sym sym;
+ char *name;
+ int type;
+
+ if (gelf_getsym(cd->cd_symdata, i, &sym) == 0)
+ return (-1);
+
+ name = (char *)cd->cd_strdata->d_buf + sym.st_name;
+ type = GELF_ST_TYPE(sym.st_info);
+
+ /*
+ * Skip various types of symbol table entries.
+ */
+ if (type != matchtype || ignore_symbol(&sym, name))
+ continue;
+
+ /* Found one */
+ *namep = name;
+ return (i);
+ }
+
+ return (-1);
+}
+
+static int
+read_data(const ctf_header_t *hp, const ctf_data_t *cd)
+{
+ void *v = (void *) (cd->cd_ctfdata + hp->cth_objtoff);
+ const ushort_t *idp = v;
+ ulong_t n = (hp->cth_funcoff - hp->cth_objtoff) / sizeof (ushort_t);
+
+ if (flags != F_STATS)
+ print_line("- Data Objects ");
+
+ if (hp->cth_objtoff & 1)
+ WARN("cth_objtoff is not aligned properly\n");
+ if (hp->cth_objtoff >= cd->cd_ctflen)
+ WARN("file is truncated or cth_objtoff is corrupt\n");
+ if (hp->cth_funcoff >= cd->cd_ctflen)
+ WARN("file is truncated or cth_funcoff is corrupt\n");
+ if (hp->cth_objtoff > hp->cth_funcoff)
+ WARN("file is corrupt -- cth_objtoff > cth_funcoff\n");
+
+ if (flags != F_STATS) {
+ int symidx, len, i;
+ char *name = NULL;
+
+ for (symidx = -1, i = 0; i < (int) n; i++) {
+ int nextsym;
+
+ if (cd->cd_symdata == NULL || (nextsym = next_sym(cd,
+ symidx, STT_OBJECT, &name)) < 0)
+ name = NULL;
+ else
+ symidx = nextsym;
+
+ len = printf(" [%u] %u", i, *idp++);
+ if (name != NULL)
+ (void) printf("%*s%s (%u)", (15 - len), "",
+ name, symidx);
+ (void) putchar('\n');
+ }
+ }
+
+ stats.s_ndata = n;
+ return (E_SUCCESS);
+}
+
+static int
+read_funcs(const ctf_header_t *hp, const ctf_data_t *cd)
+{
+ void *v = (void *) (cd->cd_ctfdata + hp->cth_funcoff);
+ const ushort_t *fp = v;
+
+ v = (void *) (cd->cd_ctfdata + hp->cth_typeoff);
+ const ushort_t *end = v;
+
+ ulong_t id;
+ int symidx;
+
+ if (flags != F_STATS)
+ print_line("- Functions ");
+
+ if (hp->cth_funcoff & 1)
+ WARN("cth_funcoff is not aligned properly\n");
+ if (hp->cth_funcoff >= cd->cd_ctflen)
+ WARN("file is truncated or cth_funcoff is corrupt\n");
+ if (hp->cth_typeoff >= cd->cd_ctflen)
+ WARN("file is truncated or cth_typeoff is corrupt\n");
+ if (hp->cth_funcoff > hp->cth_typeoff)
+ WARN("file is corrupt -- cth_funcoff > cth_typeoff\n");
+
+ for (symidx = -1, id = 0; fp < end; id++) {
+ ushort_t info = *fp++;
+ ushort_t kind = CTF_INFO_KIND(info);
+ ushort_t n = CTF_INFO_VLEN(info);
+ ushort_t i;
+ int nextsym;
+ char *name;
+
+ if (cd->cd_symdata == NULL || (nextsym = next_sym(cd, symidx,
+ STT_FUNC, &name)) < 0)
+ name = NULL;
+ else
+ symidx = nextsym;
+
+ if (kind == CTF_K_UNKNOWN && n == 0)
+ continue; /* skip padding */
+
+ if (kind != CTF_K_FUNCTION) {
+ (void) printf(" [%lu] unexpected kind -- %u\n",
+ id, kind);
+ return (E_ERROR);
+ }
+
+ if (fp + n > end) {
+ (void) printf(" [%lu] vlen %u extends past section "
+ "boundary\n", id, n);
+ return (E_ERROR);
+ }
+
+ if (flags != F_STATS) {
+ (void) printf(" [%lu] FUNC ", id);
+ if (name != NULL)
+ (void) printf("(%s) ", name);
+ (void) printf("returns: %u args: (", *fp++);
+
+ if (n != 0) {
+ (void) printf("%u", *fp++);
+ for (i = 1; i < n; i++)
+ (void) printf(", %u", *fp++);
+ }
+
+ (void) printf(")\n");
+ } else
+ fp += n + 1; /* skip to next function definition */
+
+ stats.s_nfunc++;
+ stats.s_nargs += n;
+ stats.s_argmax = MAX(stats.s_argmax, n);
+ }
+
+ return (E_SUCCESS);
+}
+
+static int
+read_types(const ctf_header_t *hp, const ctf_data_t *cd)
+{
+ void *v = (void *) (cd->cd_ctfdata + hp->cth_typeoff);
+ const ctf_type_t *tp = v;
+
+ v = (void *) (cd->cd_ctfdata + hp->cth_stroff);
+ const ctf_type_t *end = v;
+
+ ulong_t id;
+
+ if (flags != F_STATS)
+ print_line("- Types ");
+
+ if (hp->cth_typeoff & 3)
+ WARN("cth_typeoff is not aligned properly\n");
+ if (hp->cth_typeoff >= cd->cd_ctflen)
+ WARN("file is truncated or cth_typeoff is corrupt\n");
+ if (hp->cth_stroff >= cd->cd_ctflen)
+ WARN("file is truncated or cth_stroff is corrupt\n");
+ if (hp->cth_typeoff > hp->cth_stroff)
+ WARN("file is corrupt -- cth_typeoff > cth_stroff\n");
+
+ id = 1;
+ if (hp->cth_parlabel || hp->cth_parname)
+ id += 1 << CTF_PARENT_SHIFT;
+
+ for (/* */; tp < end; id++) {
+ ulong_t i, n = CTF_INFO_VLEN(tp->ctt_info);
+ size_t size, increment, vlen = 0;
+ int kind = CTF_INFO_KIND(tp->ctt_info);
+
+ union {
+ const void *ptr;
+ ctf_array_t *ap;
+ const ctf_member_t *mp;
+ const ctf_lmember_t *lmp;
+ const ctf_enum_t *ep;
+ const ushort_t *argp;
+ } u;
+
+ if (flags != F_STATS) {
+ (void) printf(" %c%lu%c ",
+ "[<"[CTF_INFO_ISROOT(tp->ctt_info)], id,
+ "]>"[CTF_INFO_ISROOT(tp->ctt_info)]);
+ }
+
+ if (tp->ctt_size == CTF_LSIZE_SENT) {
+ increment = sizeof (ctf_type_t);
+ size = (size_t)CTF_TYPE_LSIZE(tp);
+ } else {
+ increment = sizeof (ctf_stype_t);
+ size = tp->ctt_size;
+ }
+ u.ptr = (const char *)tp + increment;
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ if (flags != F_STATS) {
+ uint_t encoding = *((const uint_t *)u.ptr);
+
+ (void) printf("INTEGER %s encoding=%s offset=%u"
+ " bits=%u", ref_to_str(tp->ctt_name, hp,
+ cd), int_encoding_to_str(
+ CTF_INT_ENCODING(encoding)),
+ CTF_INT_OFFSET(encoding),
+ CTF_INT_BITS(encoding));
+ }
+ vlen = sizeof (uint_t);
+ break;
+
+ case CTF_K_FLOAT:
+ if (flags != F_STATS) {
+ uint_t encoding = *((const uint_t *)u.ptr);
+
+ (void) printf("FLOAT %s encoding=%s offset=%u "
+ "bits=%u", ref_to_str(tp->ctt_name, hp,
+ cd), fp_encoding_to_str(
+ CTF_FP_ENCODING(encoding)),
+ CTF_FP_OFFSET(encoding),
+ CTF_FP_BITS(encoding));
+ }
+ vlen = sizeof (uint_t);
+ break;
+
+ case CTF_K_POINTER:
+ if (flags != F_STATS) {
+ (void) printf("POINTER %s refers to %u",
+ ref_to_str(tp->ctt_name, hp, cd),
+ tp->ctt_type);
+ }
+ break;
+
+ case CTF_K_ARRAY:
+ if (flags != F_STATS) {
+ (void) printf("ARRAY %s content: %u index: %u "
+ "nelems: %u\n", ref_to_str(tp->ctt_name,
+ hp, cd), u.ap->cta_contents,
+ u.ap->cta_index, u.ap->cta_nelems);
+ }
+ vlen = sizeof (ctf_array_t);
+ break;
+
+ case CTF_K_FUNCTION:
+ if (flags != F_STATS) {
+ (void) printf("FUNCTION %s returns: %u args: (",
+ ref_to_str(tp->ctt_name, hp, cd),
+ tp->ctt_type);
+
+ if (n != 0) {
+ (void) printf("%u", *u.argp++);
+ for (i = 1; i < n; i++, u.argp++)
+ (void) printf(", %u", *u.argp);
+ }
+
+ (void) printf(")");
+ }
+
+ vlen = sizeof (ushort_t) * (n + (n & 1));
+ break;
+
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (kind == CTF_K_STRUCT) {
+ stats.s_nsmem += n;
+ stats.s_smmax = MAX(stats.s_smmax, n);
+ stats.s_nsbytes += size;
+ stats.s_sbmax = MAX(stats.s_sbmax, size);
+
+ if (flags != F_STATS)
+ (void) printf("STRUCT");
+ } else {
+ stats.s_numem += n;
+ stats.s_ummax = MAX(stats.s_ummax, n);
+ stats.s_nubytes += size;
+ stats.s_ubmax = MAX(stats.s_ubmax, size);
+
+ if (flags != F_STATS)
+ (void) printf("UNION");
+ }
+
+ if (flags != F_STATS) {
+ (void) printf(" %s (%zd bytes)\n",
+ ref_to_str(tp->ctt_name, hp, cd), size);
+
+ if (size >= CTF_LSTRUCT_THRESH) {
+ for (i = 0; i < n; i++, u.lmp++) {
+ (void) printf(
+ "\t%s type=%u off=%llu\n",
+ ref_to_str(u.lmp->ctlm_name,
+ hp, cd), u.lmp->ctlm_type,
+ (unsigned long long)
+ CTF_LMEM_OFFSET(u.lmp));
+ }
+ } else {
+ for (i = 0; i < n; i++, u.mp++) {
+ (void) printf(
+ "\t%s type=%u off=%u\n",
+ ref_to_str(u.mp->ctm_name,
+ hp, cd), u.mp->ctm_type,
+ u.mp->ctm_offset);
+ }
+ }
+ }
+
+ vlen = n * (size >= CTF_LSTRUCT_THRESH ?
+ sizeof (ctf_lmember_t) : sizeof (ctf_member_t));
+ break;
+
+ case CTF_K_ENUM:
+ if (flags != F_STATS) {
+ (void) printf("ENUM %s\n",
+ ref_to_str(tp->ctt_name, hp, cd));
+
+ for (i = 0; i < n; i++, u.ep++) {
+ (void) printf("\t%s = %d\n",
+ ref_to_str(u.ep->cte_name, hp, cd),
+ u.ep->cte_value);
+ }
+ }
+
+ stats.s_nemem += n;
+ stats.s_emmax = MAX(stats.s_emmax, n);
+
+ vlen = sizeof (ctf_enum_t) * n;
+ break;
+
+ case CTF_K_FORWARD:
+ if (flags != F_STATS) {
+ (void) printf("FORWARD %s",
+ ref_to_str(tp->ctt_name, hp, cd));
+ }
+ break;
+
+ case CTF_K_TYPEDEF:
+ if (flags != F_STATS) {
+ (void) printf("TYPEDEF %s refers to %u",
+ ref_to_str(tp->ctt_name, hp, cd),
+ tp->ctt_type);
+ }
+ break;
+
+ case CTF_K_VOLATILE:
+ if (flags != F_STATS) {
+ (void) printf("VOLATILE %s refers to %u",
+ ref_to_str(tp->ctt_name, hp, cd),
+ tp->ctt_type);
+ }
+ break;
+
+ case CTF_K_CONST:
+ if (flags != F_STATS) {
+ (void) printf("CONST %s refers to %u",
+ ref_to_str(tp->ctt_name, hp, cd),
+ tp->ctt_type);
+ }
+ break;
+
+ case CTF_K_RESTRICT:
+ if (flags != F_STATS) {
+ (void) printf("RESTRICT %s refers to %u",
+ ref_to_str(tp->ctt_name, hp, cd),
+ tp->ctt_type);
+ }
+ break;
+
+ case CTF_K_UNKNOWN:
+ break; /* hole in type id space */
+
+ default:
+ (void) printf("unexpected kind %u\n", kind);
+ return (E_ERROR);
+ }
+
+ if (flags != F_STATS)
+ (void) printf("\n");
+
+ stats.s_ntypes++;
+ stats.s_types[kind]++;
+
+ tp = (ctf_type_t *)((uintptr_t)tp + increment + vlen);
+ }
+
+ return (E_SUCCESS);
+}
+
+static int
+read_strtab(const ctf_header_t *hp, const ctf_data_t *cd)
+{
+ size_t n, off, len = hp->cth_strlen;
+ const char *s = cd->cd_ctfdata + hp->cth_stroff;
+
+ if (flags != F_STATS)
+ print_line("- String Table ");
+
+ if (hp->cth_stroff >= cd->cd_ctflen)
+ WARN("file is truncated or cth_stroff is corrupt\n");
+ if (hp->cth_stroff + hp->cth_strlen > cd->cd_ctflen)
+ WARN("file is truncated or cth_strlen is corrupt\n");
+
+ for (off = 0; len != 0; off += n) {
+ if (flags != F_STATS) {
+ (void) printf(" [%lu] %s\n", (ulong_t)off,
+ s[0] == '\0' ? "\\0" : s);
+ }
+ n = strlen(s) + 1;
+ len -= n;
+ s += n;
+
+ stats.s_nstr++;
+ stats.s_strlen += n;
+ stats.s_strmax = MAX(stats.s_strmax, n);
+ }
+
+ return (E_SUCCESS);
+}
+
+static void
+long_stat(const char *name, ulong_t value)
+{
+ (void) printf(" %-36s= %lu\n", name, value);
+}
+
+static void
+fp_stat(const char *name, float value)
+{
+ (void) printf(" %-36s= %.2f\n", name, value);
+}
+
+static int
+print_stats(void)
+{
+ print_line("- CTF Statistics ");
+
+ long_stat("total number of data objects", stats.s_ndata);
+ (void) printf("\n");
+
+ long_stat("total number of functions", stats.s_nfunc);
+ long_stat("total number of function arguments", stats.s_nargs);
+ long_stat("maximum argument list length", stats.s_argmax);
+
+ if (stats.s_nfunc != 0) {
+ fp_stat("average argument list length",
+ (float)stats.s_nargs / (float)stats.s_nfunc);
+ }
+
+ (void) printf("\n");
+
+ long_stat("total number of types", stats.s_ntypes);
+ long_stat("total number of integers", stats.s_types[CTF_K_INTEGER]);
+ long_stat("total number of floats", stats.s_types[CTF_K_FLOAT]);
+ long_stat("total number of pointers", stats.s_types[CTF_K_POINTER]);
+ long_stat("total number of arrays", stats.s_types[CTF_K_ARRAY]);
+ long_stat("total number of func types", stats.s_types[CTF_K_FUNCTION]);
+ long_stat("total number of structs", stats.s_types[CTF_K_STRUCT]);
+ long_stat("total number of unions", stats.s_types[CTF_K_UNION]);
+ long_stat("total number of enums", stats.s_types[CTF_K_ENUM]);
+ long_stat("total number of forward tags", stats.s_types[CTF_K_FORWARD]);
+ long_stat("total number of typedefs", stats.s_types[CTF_K_TYPEDEF]);
+ long_stat("total number of volatile types",
+ stats.s_types[CTF_K_VOLATILE]);
+ long_stat("total number of const types", stats.s_types[CTF_K_CONST]);
+ long_stat("total number of restrict types",
+ stats.s_types[CTF_K_RESTRICT]);
+ long_stat("total number of unknowns (holes)",
+ stats.s_types[CTF_K_UNKNOWN]);
+
+ (void) printf("\n");
+
+ long_stat("total number of struct members", stats.s_nsmem);
+ long_stat("maximum number of struct members", stats.s_smmax);
+ long_stat("total size of all structs", stats.s_nsbytes);
+ long_stat("maximum size of a struct", stats.s_sbmax);
+
+ if (stats.s_types[CTF_K_STRUCT] != 0) {
+ fp_stat("average number of struct members",
+ (float)stats.s_nsmem / (float)stats.s_types[CTF_K_STRUCT]);
+ fp_stat("average size of a struct", (float)stats.s_nsbytes /
+ (float)stats.s_types[CTF_K_STRUCT]);
+ }
+
+ (void) printf("\n");
+
+ long_stat("total number of union members", stats.s_numem);
+ long_stat("maximum number of union members", stats.s_ummax);
+ long_stat("total size of all unions", stats.s_nubytes);
+ long_stat("maximum size of a union", stats.s_ubmax);
+
+ if (stats.s_types[CTF_K_UNION] != 0) {
+ fp_stat("average number of union members",
+ (float)stats.s_numem / (float)stats.s_types[CTF_K_UNION]);
+ fp_stat("average size of a union", (float)stats.s_nubytes /
+ (float)stats.s_types[CTF_K_UNION]);
+ }
+
+ (void) printf("\n");
+
+ long_stat("total number of enum members", stats.s_nemem);
+ long_stat("maximum number of enum members", stats.s_emmax);
+
+ if (stats.s_types[CTF_K_ENUM] != 0) {
+ fp_stat("average number of enum members",
+ (float)stats.s_nemem / (float)stats.s_types[CTF_K_ENUM]);
+ }
+
+ (void) printf("\n");
+
+ long_stat("total number of unique strings", stats.s_nstr);
+ long_stat("bytes of string data", stats.s_strlen);
+ long_stat("maximum string length", stats.s_strmax);
+
+ if (stats.s_nstr != 0) {
+ fp_stat("average string length",
+ (float)stats.s_strlen / (float)stats.s_nstr);
+ }
+
+ (void) printf("\n");
+ return (E_SUCCESS);
+}
+
+static int
+print_usage(FILE *fp, int verbose)
+{
+ (void) fprintf(fp, "Usage: %s [-dfhlsSt] [-u file] file\n", getpname());
+
+ if (verbose) {
+ (void) fprintf(fp,
+ "\t-d dump data object section\n"
+ "\t-f dump function section\n"
+ "\t-h dump file header\n"
+ "\t-l dump label table\n"
+ "\t-s dump string table\n"
+ "\t-S dump statistics\n"
+ "\t-t dump type section\n"
+ "\t-u save uncompressed CTF to a file\n");
+ }
+
+ return (E_USAGE);
+}
+
+static Elf_Scn *
+findelfscn(Elf *elf, GElf_Ehdr *ehdr, const char *secname)
+{
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ char *name;
+
+ for (scn = NULL; (scn = elf_nextscn(elf, scn)) != NULL; ) {
+ if (gelf_getshdr(scn, &shdr) != NULL && (name =
+ elf_strptr(elf, ehdr->e_shstrndx, shdr.sh_name)) != NULL &&
+ strcmp(name, secname) == 0)
+ return (scn);
+ }
+
+ return (NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *filename = NULL;
+ const char *ufile = NULL;
+ int error = 0;
+ int c, fd, ufd;
+
+ ctf_data_t cd;
+ const ctf_preamble_t *pp;
+ ctf_header_t *hp = NULL;
+ Elf *elf;
+ GElf_Ehdr ehdr;
+
+ (void) elf_version(EV_CURRENT);
+
+ for (opterr = 0; optind < argc; optind++) {
+ while ((c = getopt(argc, argv, "dfhlsStu:")) != (int)EOF) {
+ switch (c) {
+ case 'd':
+ flags |= F_DATA;
+ break;
+ case 'f':
+ flags |= F_FUNC;
+ break;
+ case 'h':
+ flags |= F_HDR;
+ break;
+ case 'l':
+ flags |= F_LABEL;
+ break;
+ case 's':
+ flags |= F_STR;
+ break;
+ case 'S':
+ flags |= F_STATS;
+ break;
+ case 't':
+ flags |= F_TYPES;
+ break;
+ case 'u':
+ ufile = optarg;
+ break;
+ default:
+ if (optopt == '?')
+ return (print_usage(stdout, 1));
+ warn("illegal option -- %c\n", optopt);
+ return (print_usage(stderr, 0));
+ }
+ }
+
+ if (optind < argc) {
+ if (filename != NULL)
+ return (print_usage(stderr, 0));
+ filename = argv[optind];
+ }
+ }
+
+ if (filename == NULL)
+ return (print_usage(stderr, 0));
+
+ if (flags == 0 && ufile == NULL)
+ flags = F_ALLMSK;
+
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ die("failed to open %s", filename);
+
+ if ((elf = elf_begin(fd, ELF_C_READ, NULL)) != NULL &&
+ gelf_getehdr(elf, &ehdr) != NULL) {
+
+ Elf_Data *dp = NULL;
+ Elf_Scn *ctfscn = findelfscn(elf, &ehdr, ".SUNW_ctf");
+ Elf_Scn *symscn;
+ GElf_Shdr ctfshdr;
+
+ if (ctfscn == NULL || (dp = elf_getdata(ctfscn, NULL)) == NULL)
+ die("%s does not contain .SUNW_ctf data\n", filename);
+
+ cd.cd_ctfdata = dp->d_buf;
+ cd.cd_ctflen = dp->d_size;
+
+ /*
+ * If the sh_link field of the CTF section header is non-zero
+ * it indicates which section contains the symbol table that
+ * should be used. We default to the .symtab section if sh_link
+ * is zero or if there's an error reading the section header.
+ */
+ if (gelf_getshdr(ctfscn, &ctfshdr) != NULL &&
+ ctfshdr.sh_link != 0) {
+ symscn = elf_getscn(elf, ctfshdr.sh_link);
+ } else {
+ symscn = findelfscn(elf, &ehdr, ".symtab");
+ }
+
+ /* If we found a symbol table, find the corresponding strings */
+ if (symscn != NULL) {
+ GElf_Shdr shdr;
+ Elf_Scn *symstrscn;
+
+ if (gelf_getshdr(symscn, &shdr) != NULL) {
+ symstrscn = elf_getscn(elf, shdr.sh_link);
+
+ cd.cd_nsyms = shdr.sh_size / shdr.sh_entsize;
+ cd.cd_symdata = elf_getdata(symscn, NULL);
+ cd.cd_strdata = elf_getdata(symstrscn, NULL);
+ }
+ }
+ } else {
+ struct stat st;
+
+ if (fstat(fd, &st) == -1)
+ die("failed to fstat %s", filename);
+
+ cd.cd_ctflen = st.st_size;
+ cd.cd_ctfdata = mmap(NULL, cd.cd_ctflen, PROT_READ,
+ MAP_PRIVATE, fd, 0);
+ if (cd.cd_ctfdata == MAP_FAILED)
+ die("failed to mmap %s", filename);
+ }
+
+ /*
+ * Get a pointer to the CTF data buffer and interpret the first portion
+ * as a ctf_header_t. Validate the magic number and size.
+ */
+
+ if (cd.cd_ctflen < sizeof (ctf_preamble_t))
+ die("%s does not contain a CTF preamble\n", filename);
+
+ void *v = (void *) cd.cd_ctfdata;
+ pp = v;
+
+ if (pp->ctp_magic != CTF_MAGIC)
+ die("%s does not appear to contain CTF data\n", filename);
+
+ if (pp->ctp_version == CTF_VERSION) {
+ v = (void *) cd.cd_ctfdata;
+ hp = v;
+ cd.cd_ctfdata = (caddr_t)cd.cd_ctfdata + sizeof (ctf_header_t);
+
+ if (cd.cd_ctflen < sizeof (ctf_header_t)) {
+ die("%s does not contain a v%d CTF header\n", filename,
+ CTF_VERSION);
+ }
+
+ } else {
+ die("%s contains unsupported CTF version %d\n", filename,
+ pp->ctp_version);
+ }
+
+ /*
+ * If the data buffer is compressed, then malloc a buffer large enough
+ * to hold the decompressed data, and use zlib to decompress it.
+ */
+ if (hp->cth_flags & CTF_F_COMPRESS) {
+ z_stream zstr;
+ void *buf;
+ int rc;
+
+ if ((buf = malloc(hp->cth_stroff + hp->cth_strlen)) == NULL)
+ die("failed to allocate decompression buffer");
+
+ bzero(&zstr, sizeof (z_stream));
+ zstr.next_in = (void *)cd.cd_ctfdata;
+ zstr.avail_in = cd.cd_ctflen;
+ zstr.next_out = buf;
+ zstr.avail_out = hp->cth_stroff + hp->cth_strlen;
+
+ if ((rc = inflateInit(&zstr)) != Z_OK)
+ die("failed to initialize zlib: %s\n", zError(rc));
+
+ if ((rc = inflate(&zstr, Z_FINISH)) != Z_STREAM_END)
+ die("failed to decompress CTF data: %s\n", zError(rc));
+
+ if ((rc = inflateEnd(&zstr)) != Z_OK)
+ die("failed to finish decompression: %s\n", zError(rc));
+
+ if (zstr.total_out != hp->cth_stroff + hp->cth_strlen)
+ die("CTF data is corrupt -- short decompression\n");
+
+ cd.cd_ctfdata = buf;
+ cd.cd_ctflen = hp->cth_stroff + hp->cth_strlen;
+ }
+
+ if (flags & F_HDR)
+ error |= print_header(hp, &cd);
+ if (flags & (F_LABEL))
+ error |= print_labeltable(hp, &cd);
+ if (flags & (F_DATA | F_STATS))
+ error |= read_data(hp, &cd);
+ if (flags & (F_FUNC | F_STATS))
+ error |= read_funcs(hp, &cd);
+ if (flags & (F_TYPES | F_STATS))
+ error |= read_types(hp, &cd);
+ if (flags & (F_STR | F_STATS))
+ error |= read_strtab(hp, &cd);
+ if (flags & F_STATS)
+ error |= print_stats();
+
+ /*
+ * If the -u option is specified, write the uncompressed CTF data to a
+ * raw CTF file. CTF data can already be extracted compressed by
+ * applying elfdump -w -N .SUNW_ctf to an ELF file, so we don't bother.
+ */
+ if (ufile != NULL) {
+ ctf_header_t h;
+
+ bcopy(hp, &h, sizeof (h));
+ h.cth_flags &= ~CTF_F_COMPRESS;
+
+ if ((ufd = open(ufile, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0 ||
+ write(ufd, &h, sizeof (h)) != sizeof (h) ||
+ write(ufd, cd.cd_ctfdata, cd.cd_ctflen) != (int) cd.cd_ctflen) {
+ warn("failed to write CTF data to '%s'", ufile);
+ error |= E_ERROR;
+ }
+
+ (void) close(ufd);
+ }
+
+ if (elf != NULL)
+ (void) elf_end(elf);
+
+ (void) close(fd);
+ return (error);
+}
diff --git a/libctf/ctf_create.c b/libctf/ctf_create.c
new file mode 100644
index 0000000..df9376f
--- /dev/null
+++ b/libctf/ctf_create.c
@@ -0,0 +1,1363 @@
+/*
+ * 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"
+
+/*
+ * This static string is used as the template for initially populating a
+ * dynamic container's string table. We always store \0 in the first byte,
+ * and we use the generic string "PARENT" to mark this container's parent
+ * if one is associated with the container using ctf_import().
+ */
+static const char _CTF_STRTAB_TEMPLATE[] = "\0PARENT";
+
+/*
+ * To create an empty CTF container, we just declare a zeroed header and call
+ * ctf_bufopen() on it. If ctf_bufopen succeeds, we mark the new container r/w
+ * and initialize the dynamic members. We set dtstrlen to 1 to reserve the
+ * first byte of the string table for a \0 byte, and we start assigning type
+ * IDs at 1 because type ID 0 is used as a sentinel.
+ */
+ctf_file_t *
+ctf_create(int *errp)
+{
+ static const ctf_header_t hdr = { { CTF_MAGIC, CTF_VERSION, 0 } };
+
+ const ulong_t hashlen = 128;
+ ctf_dtdef_t **hash = ctf_alloc(hashlen * sizeof (ctf_dtdef_t *));
+ ctf_sect_t cts;
+ ctf_file_t *fp;
+
+ if (hash == NULL)
+ return (ctf_set_open_errno(errp, EAGAIN));
+
+ cts.cts_name = _CTF_SECTION;
+ cts.cts_type = SHT_PROGBITS;
+ cts.cts_flags = 0;
+ cts.cts_data = &hdr;
+ cts.cts_size = sizeof (hdr);
+ cts.cts_entsize = 1;
+ cts.cts_offset = 0;
+
+ if ((fp = ctf_bufopen(&cts, NULL, NULL, errp)) == NULL) {
+ ctf_free(hash, hashlen * sizeof (ctf_dtdef_t *));
+ return (NULL);
+ }
+
+ fp->ctf_flags |= LCTF_RDWR;
+ fp->ctf_dthashlen = hashlen;
+ bzero(hash, hashlen * sizeof (ctf_dtdef_t *));
+ fp->ctf_dthash = hash;
+ fp->ctf_dtstrlen = sizeof (_CTF_STRTAB_TEMPLATE);
+ fp->ctf_dtnextid = 1;
+ fp->ctf_dtoldid = 0;
+
+ return (fp);
+}
+
+static uchar_t *
+ctf_copy_smembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t)
+{
+ ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ ctf_member_t ctm;
+
+ for (; dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (dmd->dmd_name) {
+ ctm.ctm_name = soff;
+ soff += strlen(dmd->dmd_name) + 1;
+ } else
+ ctm.ctm_name = 0;
+
+ ctm.ctm_type = (ushort_t)dmd->dmd_type;
+ ctm.ctm_offset = (ushort_t)dmd->dmd_offset;
+
+ bcopy(&ctm, t, sizeof (ctm));
+ t += sizeof (ctm);
+ }
+
+ return (t);
+}
+
+static uchar_t *
+ctf_copy_lmembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t)
+{
+ ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ ctf_lmember_t ctlm;
+
+ for (; dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (dmd->dmd_name) {
+ ctlm.ctlm_name = soff;
+ soff += strlen(dmd->dmd_name) + 1;
+ } else
+ ctlm.ctlm_name = 0;
+
+ ctlm.ctlm_type = (ushort_t)dmd->dmd_type;
+ ctlm.ctlm_pad = 0;
+ ctlm.ctlm_offsethi = CTF_OFFSET_TO_LMEMHI(dmd->dmd_offset);
+ ctlm.ctlm_offsetlo = CTF_OFFSET_TO_LMEMLO(dmd->dmd_offset);
+
+ bcopy(&ctlm, t, sizeof (ctlm));
+ t += sizeof (ctlm);
+ }
+
+ return (t);
+}
+
+static uchar_t *
+ctf_copy_emembers(ctf_dtdef_t *dtd, uint_t soff, uchar_t *t)
+{
+ ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ ctf_enum_t cte;
+
+ for (; dmd != NULL; dmd = ctf_list_next(dmd)) {
+ cte.cte_name = soff;
+ cte.cte_value = dmd->dmd_value;
+ soff += strlen(dmd->dmd_name) + 1;
+ bcopy(&cte, t, sizeof (cte));
+ t += sizeof (cte);
+ }
+
+ return (t);
+}
+
+static uchar_t *
+ctf_copy_membnames(ctf_dtdef_t *dtd, uchar_t *s)
+{
+ ctf_dmdef_t *dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ size_t len;
+
+ for (; dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (dmd->dmd_name == NULL)
+ continue; /* skip anonymous members */
+ len = strlen(dmd->dmd_name) + 1;
+ bcopy(dmd->dmd_name, s, len);
+ s += len;
+ }
+
+ return (s);
+}
+
+/*
+ * If the specified CTF container is writable and has been modified, reload
+ * this container with the updated type definitions. In order to make this
+ * code and the rest of libctf as simple as possible, we perform updates by
+ * taking the dynamic type definitions and creating an in-memory CTF file
+ * containing the definitions, and then call ctf_bufopen() on it. This not
+ * only leverages ctf_bufopen(), but also avoids having to bifurcate the rest
+ * of the library code with different lookup paths for static and dynamic
+ * type definitions. We are therefore optimizing greatly for lookup over
+ * update, which we assume will be an uncommon operation. We perform one
+ * extra trick here for the benefit of callers and to keep our code simple:
+ * ctf_bufopen() will return a new ctf_file_t, but we want to keep the fp
+ * constant for the caller, so after ctf_bufopen() returns, we use bcopy to
+ * swap the interior of the old and new ctf_file_t's, and then free the old.
+ */
+int
+ctf_update(ctf_file_t *fp)
+{
+ ctf_file_t ofp, *nfp;
+ ctf_header_t hdr;
+ ctf_dtdef_t *dtd;
+ ctf_sect_t cts;
+
+ uchar_t *s, *s0, *t;
+ size_t size;
+ void *buf;
+ int err;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (!(fp->ctf_flags & LCTF_DIRTY))
+ return (0); /* no update required */
+
+ /*
+ * Fill in an initial CTF header. We will leave the label, object,
+ * and function sections empty and only output a header, type section,
+ * and string table. The type section begins at a 4-byte aligned
+ * boundary past the CTF header itself (at relative offset zero).
+ */
+ bzero(&hdr, sizeof (hdr));
+ hdr.cth_magic = CTF_MAGIC;
+ hdr.cth_version = CTF_VERSION;
+
+ if (fp->ctf_flags & LCTF_CHILD)
+ hdr.cth_parname = 1; /* i.e. _CTF_STRTAB_TEMPLATE[1] */
+
+ /*
+ * Iterate through the dynamic type definition list and compute the
+ * size of the CTF type section we will need to generate.
+ */
+ for (size = 0, dtd = ctf_list_next(&fp->ctf_dtdefs);
+ dtd != NULL; dtd = ctf_list_next(dtd)) {
+
+ uint_t kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ uint_t vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info);
+
+ if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT)
+ size += sizeof (ctf_stype_t);
+ else
+ size += sizeof (ctf_type_t);
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ size += sizeof (uint_t);
+ break;
+ case CTF_K_ARRAY:
+ size += sizeof (ctf_array_t);
+ break;
+ case CTF_K_FUNCTION:
+ size += sizeof (ushort_t) * (vlen + (vlen & 1));
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (dtd->dtd_data.ctt_size < CTF_LSTRUCT_THRESH)
+ size += sizeof (ctf_member_t) * vlen;
+ else
+ size += sizeof (ctf_lmember_t) * vlen;
+ break;
+ case CTF_K_ENUM:
+ size += sizeof (ctf_enum_t) * vlen;
+ break;
+ }
+ }
+
+ /*
+ * Fill in the string table offset and size, compute the size of the
+ * entire CTF buffer we need, and then allocate a new buffer and
+ * bcopy the finished header to the start of the buffer.
+ */
+ hdr.cth_stroff = hdr.cth_typeoff + size;
+ hdr.cth_strlen = fp->ctf_dtstrlen;
+ size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen;
+
+ if ((buf = ctf_data_alloc(size)) == MAP_FAILED)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ bcopy(&hdr, buf, sizeof (ctf_header_t));
+ t = (uchar_t *)buf + sizeof (ctf_header_t);
+ s = s0 = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_stroff;
+
+ bcopy(_CTF_STRTAB_TEMPLATE, s, sizeof (_CTF_STRTAB_TEMPLATE));
+ s += sizeof (_CTF_STRTAB_TEMPLATE);
+
+ /*
+ * We now take a final lap through the dynamic type definition list and
+ * copy the appropriate type records and strings to the output buffer.
+ */
+ for (dtd = ctf_list_next(&fp->ctf_dtdefs);
+ dtd != NULL; dtd = ctf_list_next(dtd)) {
+
+ uint_t kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ uint_t vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info);
+
+ ctf_array_t cta;
+ uint_t encoding;
+ size_t len;
+
+ if (dtd->dtd_name != NULL) {
+ dtd->dtd_data.ctt_name = (uint_t)(s - s0);
+ len = strlen(dtd->dtd_name) + 1;
+ bcopy(dtd->dtd_name, s, len);
+ s += len;
+ } else
+ dtd->dtd_data.ctt_name = 0;
+
+ if (dtd->dtd_data.ctt_size != CTF_LSIZE_SENT)
+ len = sizeof (ctf_stype_t);
+ else
+ len = sizeof (ctf_type_t);
+
+ bcopy(&dtd->dtd_data, t, len);
+ t += len;
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ if (kind == CTF_K_INTEGER) {
+ encoding = CTF_INT_DATA(
+ dtd->dtd_u.dtu_enc.cte_format,
+ dtd->dtd_u.dtu_enc.cte_offset,
+ dtd->dtd_u.dtu_enc.cte_bits);
+ } else {
+ encoding = CTF_FP_DATA(
+ dtd->dtd_u.dtu_enc.cte_format,
+ dtd->dtd_u.dtu_enc.cte_offset,
+ dtd->dtd_u.dtu_enc.cte_bits);
+ }
+ bcopy(&encoding, t, sizeof (encoding));
+ t += sizeof (encoding);
+ break;
+
+ case CTF_K_ARRAY:
+ cta.cta_contents = (ushort_t)
+ dtd->dtd_u.dtu_arr.ctr_contents;
+ cta.cta_index = (ushort_t)
+ dtd->dtd_u.dtu_arr.ctr_index;
+ cta.cta_nelems = dtd->dtd_u.dtu_arr.ctr_nelems;
+ bcopy(&cta, t, sizeof (cta));
+ t += sizeof (cta);
+ break;
+
+ case CTF_K_FUNCTION: {
+ ushort_t *argv = (ushort_t *)(uintptr_t)t;
+ uint_t argc;
+
+ for (argc = 0; argc < vlen; argc++)
+ *argv++ = (ushort_t)dtd->dtd_u.dtu_argv[argc];
+
+ if (vlen & 1)
+ *argv++ = 0; /* pad to 4-byte boundary */
+
+ t = (uchar_t *)argv;
+ break;
+ }
+
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (dtd->dtd_data.ctt_size < CTF_LSTRUCT_THRESH)
+ t = ctf_copy_smembers(dtd, (uint_t)(s - s0), t);
+ else
+ t = ctf_copy_lmembers(dtd, (uint_t)(s - s0), t);
+ s = ctf_copy_membnames(dtd, s);
+ break;
+
+ case CTF_K_ENUM:
+ t = ctf_copy_emembers(dtd, (uint_t)(s - s0), t);
+ s = ctf_copy_membnames(dtd, s);
+ break;
+ }
+ }
+
+ /*
+ * Finally, we are ready to ctf_bufopen() the new container. If this
+ * is successful, we then switch nfp and fp and free the old container.
+ */
+ ctf_data_protect(buf, size);
+ cts.cts_name = _CTF_SECTION;
+ cts.cts_type = SHT_PROGBITS;
+ cts.cts_flags = 0;
+ cts.cts_data = buf;
+ cts.cts_size = size;
+ cts.cts_entsize = 1;
+ cts.cts_offset = 0;
+
+ if ((nfp = ctf_bufopen(&cts, NULL, NULL, &err)) == NULL) {
+ ctf_data_free(buf, size);
+ return (ctf_set_errno(fp, err));
+ }
+
+ (void) ctf_setmodel(nfp, ctf_getmodel(fp));
+ (void) ctf_import(nfp, fp->ctf_parent);
+
+ nfp->ctf_refcnt = fp->ctf_refcnt;
+ nfp->ctf_flags |= fp->ctf_flags & ~LCTF_DIRTY;
+ nfp->ctf_data.cts_data = NULL; /* force ctf_data_free() on close */
+ nfp->ctf_dthash = fp->ctf_dthash;
+ nfp->ctf_dthashlen = fp->ctf_dthashlen;
+ nfp->ctf_dtdefs = fp->ctf_dtdefs;
+ nfp->ctf_dtstrlen = fp->ctf_dtstrlen;
+ nfp->ctf_dtnextid = fp->ctf_dtnextid;
+ nfp->ctf_dtoldid = fp->ctf_dtnextid - 1;
+ nfp->ctf_specific = fp->ctf_specific;
+
+ fp->ctf_dthash = NULL;
+ fp->ctf_dthashlen = 0;
+ bzero(&fp->ctf_dtdefs, sizeof (ctf_list_t));
+
+ bcopy(fp, &ofp, sizeof (ctf_file_t));
+ bcopy(nfp, fp, sizeof (ctf_file_t));
+ bcopy(&ofp, nfp, sizeof (ctf_file_t));
+
+ /*
+ * Initialize the ctf_lookup_by_name top-level dictionary. We keep an
+ * array of type name prefixes and the corresponding ctf_hash to use.
+ * NOTE: This code must be kept in sync with the code in ctf_bufopen().
+ */
+ fp->ctf_lookups[0].ctl_hash = &fp->ctf_structs;
+ fp->ctf_lookups[1].ctl_hash = &fp->ctf_unions;
+ fp->ctf_lookups[2].ctl_hash = &fp->ctf_enums;
+ fp->ctf_lookups[3].ctl_hash = &fp->ctf_names;
+
+ nfp->ctf_refcnt = 1; /* force nfp to be freed */
+ ctf_close(nfp);
+
+ return (0);
+}
+
+void
+ctf_dtd_insert(ctf_file_t *fp, ctf_dtdef_t *dtd)
+{
+ ulong_t h = dtd->dtd_type & (fp->ctf_dthashlen - 1);
+
+ dtd->dtd_hash = fp->ctf_dthash[h];
+ fp->ctf_dthash[h] = dtd;
+ ctf_list_append(&fp->ctf_dtdefs, dtd);
+}
+
+void
+ctf_dtd_delete(ctf_file_t *fp, ctf_dtdef_t *dtd)
+{
+ ulong_t h = dtd->dtd_type & (fp->ctf_dthashlen - 1);
+ ctf_dtdef_t *p, **q = &fp->ctf_dthash[h];
+ ctf_dmdef_t *dmd, *nmd;
+ size_t len;
+
+ for (p = *q; p != NULL; p = p->dtd_hash) {
+ if (p != dtd)
+ q = &p->dtd_hash;
+ else
+ break;
+ }
+
+ if (p != NULL)
+ *q = p->dtd_hash;
+
+ switch (CTF_INFO_KIND(dtd->dtd_data.ctt_info)) {
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ case CTF_K_ENUM:
+ for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ dmd != NULL; dmd = nmd) {
+ if (dmd->dmd_name != NULL) {
+ len = strlen(dmd->dmd_name) + 1;
+ ctf_free(dmd->dmd_name, len);
+ fp->ctf_dtstrlen -= len;
+ }
+ nmd = ctf_list_next(dmd);
+ ctf_free(dmd, sizeof (ctf_dmdef_t));
+ }
+ break;
+ case CTF_K_FUNCTION:
+ ctf_free(dtd->dtd_u.dtu_argv, sizeof (ctf_id_t) *
+ CTF_INFO_VLEN(dtd->dtd_data.ctt_info));
+ break;
+ }
+
+ if (dtd->dtd_name) {
+ len = strlen(dtd->dtd_name) + 1;
+ ctf_free(dtd->dtd_name, len);
+ fp->ctf_dtstrlen -= len;
+ }
+
+ ctf_list_delete(&fp->ctf_dtdefs, dtd);
+ ctf_free(dtd, sizeof (ctf_dtdef_t));
+}
+
+ctf_dtdef_t *
+ctf_dtd_lookup(ctf_file_t *fp, ctf_id_t type)
+{
+ ulong_t h = type & (fp->ctf_dthashlen - 1);
+ ctf_dtdef_t *dtd;
+
+ if (fp->ctf_dthash == NULL)
+ return (NULL);
+
+ for (dtd = fp->ctf_dthash[h]; dtd != NULL; dtd = dtd->dtd_hash) {
+ if (dtd->dtd_type == type)
+ break;
+ }
+
+ return (dtd);
+}
+
+/*
+ * Discard all of the dynamic type definitions that have been added to the
+ * container since the last call to ctf_update(). We locate such types by
+ * scanning the list and deleting elements that have type IDs greater than
+ * ctf_dtoldid, which is set by ctf_update(), above.
+ */
+int
+ctf_discard(ctf_file_t *fp)
+{
+ ctf_dtdef_t *dtd, *ntd;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (!(fp->ctf_flags & LCTF_DIRTY))
+ return (0); /* no update required */
+
+ for (dtd = ctf_list_next(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) {
+ if (dtd->dtd_type <= fp->ctf_dtoldid)
+ continue; /* skip types that have been committed */
+
+ ntd = ctf_list_next(dtd);
+ ctf_dtd_delete(fp, dtd);
+ }
+
+ fp->ctf_dtnextid = fp->ctf_dtoldid + 1;
+ fp->ctf_flags &= ~LCTF_DIRTY;
+
+ return (0);
+}
+
+static ctf_id_t
+ctf_add_generic(ctf_file_t *fp, uint_t flag, const char *name, ctf_dtdef_t **rp)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+ char *s = NULL;
+
+ if (flag != CTF_ADD_NONROOT && flag != CTF_ADD_ROOT)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (CTF_INDEX_TO_TYPE(fp->ctf_dtnextid, 1) > CTF_MAX_TYPE)
+ return (ctf_set_errno(fp, ECTF_FULL));
+
+ if ((dtd = ctf_alloc(sizeof (ctf_dtdef_t))) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if (name != NULL && (s = ctf_strdup(name)) == NULL) {
+ ctf_free(dtd, sizeof (ctf_dtdef_t));
+ return (ctf_set_errno(fp, EAGAIN));
+ }
+
+ type = fp->ctf_dtnextid++;
+ type = CTF_INDEX_TO_TYPE(type, (fp->ctf_flags & LCTF_CHILD));
+
+ bzero(dtd, sizeof (ctf_dtdef_t));
+ dtd->dtd_name = s;
+ dtd->dtd_type = type;
+
+ if (s != NULL)
+ fp->ctf_dtstrlen += strlen(s) + 1;
+
+ ctf_dtd_insert(fp, dtd);
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ *rp = dtd;
+ return (type);
+}
+
+/*
+ * When encoding integer sizes, we want to convert a byte count in the range
+ * 1-8 to the closest power of 2 (e.g. 3->4, 5->8, etc). The clp2() function
+ * is a clever implementation from "Hacker's Delight" by Henry Warren, Jr.
+ */
+static size_t
+clp2(size_t x)
+{
+ x--;
+
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+
+ return (x + 1);
+}
+
+static ctf_id_t
+ctf_add_encoded(ctf_file_t *fp, uint_t flag,
+ const char *name, const ctf_encoding_t *ep, uint_t kind)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (ep == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, 0);
+ dtd->dtd_data.ctt_size = clp2(P2ROUNDUP(ep->cte_bits, NBBY) / NBBY);
+ dtd->dtd_u.dtu_enc = *ep;
+
+ return (type);
+}
+
+static ctf_id_t
+ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, 0);
+ dtd->dtd_data.ctt_type = (ushort_t)ref;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_integer(ctf_file_t *fp, uint_t flag,
+ const char *name, const ctf_encoding_t *ep)
+{
+ return (ctf_add_encoded(fp, flag, name, ep, CTF_K_INTEGER));
+}
+
+ctf_id_t
+ctf_add_float(ctf_file_t *fp, uint_t flag,
+ const char *name, const ctf_encoding_t *ep)
+{
+ return (ctf_add_encoded(fp, flag, name, ep, CTF_K_FLOAT));
+}
+
+ctf_id_t
+ctf_add_pointer(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+{
+ return (ctf_add_reftype(fp, flag, ref, CTF_K_POINTER));
+}
+
+ctf_id_t
+ctf_add_array(ctf_file_t *fp, uint_t flag, const ctf_arinfo_t *arp)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (arp == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_ARRAY, flag, 0);
+ dtd->dtd_data.ctt_size = 0;
+ dtd->dtd_u.dtu_arr = *arp;
+
+ return (type);
+}
+
+int
+ctf_set_array(ctf_file_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)
+{
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, type);
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (dtd == NULL || CTF_INFO_KIND(dtd->dtd_data.ctt_info) != CTF_K_ARRAY)
+ return (ctf_set_errno(fp, ECTF_BADID));
+
+ fp->ctf_flags |= LCTF_DIRTY;
+ dtd->dtd_u.dtu_arr = *arp;
+
+ return (0);
+}
+
+ctf_id_t
+ctf_add_function(ctf_file_t *fp, uint_t flag,
+ const ctf_funcinfo_t *ctc, const ctf_id_t *argv)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+ uint_t vlen;
+ ctf_id_t *vdat = NULL;
+
+ if (ctc == NULL || (ctc->ctc_flags & ~CTF_FUNC_VARARG) != 0 ||
+ (ctc->ctc_argc != 0 && argv == NULL))
+ return (ctf_set_errno(fp, EINVAL));
+
+ vlen = ctc->ctc_argc;
+ if (ctc->ctc_flags & CTF_FUNC_VARARG)
+ vlen++; /* add trailing zero to indicate varargs (see below) */
+
+ if (vlen > CTF_MAX_VLEN)
+ return (ctf_set_errno(fp, EOVERFLOW));
+
+ if (vlen != 0 && (vdat = ctf_alloc(sizeof (ctf_id_t) * vlen)) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if ((type = ctf_add_generic(fp, flag, NULL, &dtd)) == CTF_ERR) {
+ ctf_free(vdat, sizeof (ctf_id_t) * vlen);
+ return (CTF_ERR); /* errno is set for us */
+ }
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_FUNCTION, flag, vlen);
+ dtd->dtd_data.ctt_type = (ushort_t)ctc->ctc_return;
+
+ bcopy(argv, vdat, sizeof (ctf_id_t) * ctc->ctc_argc);
+ if (ctc->ctc_flags & CTF_FUNC_VARARG)
+ vdat[vlen - 1] = 0; /* add trailing zero to indicate varargs */
+ dtd->dtd_u.dtu_argv = vdat;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_struct(ctf_file_t *fp, uint_t flag, const char *name)
+{
+ ctf_hash_t *hp = &fp->ctf_structs;
+ ctf_helem_t *hep = NULL;
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (name != NULL)
+ hep = ctf_hash_lookup(hp, fp, name, strlen(name));
+
+ if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD)
+ dtd = ctf_dtd_lookup(fp, type = hep->h_type);
+ else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_STRUCT, flag, 0);
+ dtd->dtd_data.ctt_size = 0;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_union(ctf_file_t *fp, uint_t flag, const char *name)
+{
+ ctf_hash_t *hp = &fp->ctf_unions;
+ ctf_helem_t *hep = NULL;
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (name != NULL)
+ hep = ctf_hash_lookup(hp, fp, name, strlen(name));
+
+ if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD)
+ dtd = ctf_dtd_lookup(fp, type = hep->h_type);
+ else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_UNION, flag, 0);
+ dtd->dtd_data.ctt_size = 0;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_enum(ctf_file_t *fp, uint_t flag, const char *name)
+{
+ ctf_hash_t *hp = &fp->ctf_enums;
+ ctf_helem_t *hep = NULL;
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (name != NULL)
+ hep = ctf_hash_lookup(hp, fp, name, strlen(name));
+
+ if (hep != NULL && ctf_type_kind(fp, hep->h_type) == CTF_K_FORWARD)
+ dtd = ctf_dtd_lookup(fp, type = hep->h_type);
+ else if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_ENUM, flag, 0);
+ dtd->dtd_data.ctt_size = fp->ctf_dmodel->ctd_int;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_forward(ctf_file_t *fp, uint_t flag, const char *name, uint_t kind)
+{
+ ctf_hash_t *hp;
+ ctf_helem_t *hep;
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ switch (kind) {
+ case CTF_K_STRUCT:
+ hp = &fp->ctf_structs;
+ break;
+ case CTF_K_UNION:
+ hp = &fp->ctf_unions;
+ break;
+ case CTF_K_ENUM:
+ hp = &fp->ctf_enums;
+ break;
+ default:
+ return (ctf_set_errno(fp, ECTF_NOTSUE));
+ }
+
+ /*
+ * If the type is already defined or exists as a forward tag, just
+ * return the ctf_id_t of the existing definition.
+ */
+ if (name != NULL && (hep = ctf_hash_lookup(hp,
+ fp, name, strlen(name))) != NULL)
+ return (hep->h_type);
+
+ if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_FORWARD, flag, 0);
+ dtd->dtd_data.ctt_type = kind;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_typedef(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
+{
+ ctf_dtdef_t *dtd;
+ ctf_id_t type;
+
+ if (ref == CTF_ERR || ref < 0 || ref > CTF_MAX_TYPE)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(CTF_K_TYPEDEF, flag, 0);
+ dtd->dtd_data.ctt_type = (ushort_t)ref;
+
+ return (type);
+}
+
+ctf_id_t
+ctf_add_volatile(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+{
+ return (ctf_add_reftype(fp, flag, ref, CTF_K_VOLATILE));
+}
+
+ctf_id_t
+ctf_add_const(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+{
+ return (ctf_add_reftype(fp, flag, ref, CTF_K_CONST));
+}
+
+ctf_id_t
+ctf_add_restrict(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+{
+ return (ctf_add_reftype(fp, flag, ref, CTF_K_RESTRICT));
+}
+
+int
+ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value)
+{
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, enid);
+ ctf_dmdef_t *dmd;
+
+ uint_t kind, vlen, root;
+ char *s;
+
+ if (name == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (dtd == NULL)
+ return (ctf_set_errno(fp, ECTF_BADID));
+
+ kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ root = CTF_INFO_ISROOT(dtd->dtd_data.ctt_info);
+ vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info);
+
+ if (kind != CTF_K_ENUM)
+ return (ctf_set_errno(fp, ECTF_NOTENUM));
+
+ if (vlen == CTF_MAX_VLEN)
+ return (ctf_set_errno(fp, ECTF_DTFULL));
+
+ for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (strcmp(dmd->dmd_name, name) == 0)
+ return (ctf_set_errno(fp, ECTF_DUPMEMBER));
+ }
+
+ if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if ((s = ctf_strdup(name)) == NULL) {
+ ctf_free(dmd, sizeof (ctf_dmdef_t));
+ return (ctf_set_errno(fp, EAGAIN));
+ }
+
+ dmd->dmd_name = s;
+ dmd->dmd_type = CTF_ERR;
+ dmd->dmd_offset = 0;
+ dmd->dmd_value = value;
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, root, vlen + 1);
+ ctf_list_append(&dtd->dtd_u.dtu_members, dmd);
+
+ fp->ctf_dtstrlen += strlen(s) + 1;
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ return (0);
+}
+
+int
+ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)
+{
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, souid);
+ ctf_dmdef_t *dmd;
+
+ ssize_t msize, malign, ssize;
+ uint_t kind, vlen, root;
+ char *s = NULL;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (dtd == NULL)
+ return (ctf_set_errno(fp, ECTF_BADID));
+
+ kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info);
+ root = CTF_INFO_ISROOT(dtd->dtd_data.ctt_info);
+ vlen = CTF_INFO_VLEN(dtd->dtd_data.ctt_info);
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (ctf_set_errno(fp, ECTF_NOTSOU));
+
+ if (vlen == CTF_MAX_VLEN)
+ return (ctf_set_errno(fp, ECTF_DTFULL));
+
+ if (name != NULL) {
+ for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if (dmd->dmd_name != NULL &&
+ strcmp(dmd->dmd_name, name) == 0)
+ return (ctf_set_errno(fp, ECTF_DUPMEMBER));
+ }
+ }
+
+ if ((msize = ctf_type_size(fp, type)) == CTF_ERR ||
+ (malign = ctf_type_align(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if (name != NULL && (s = ctf_strdup(name)) == NULL) {
+ ctf_free(dmd, sizeof (ctf_dmdef_t));
+ return (ctf_set_errno(fp, EAGAIN));
+ }
+
+ dmd->dmd_name = s;
+ dmd->dmd_type = type;
+ dmd->dmd_value = -1;
+
+ if (kind == CTF_K_STRUCT && vlen != 0) {
+ ctf_dmdef_t *lmd = ctf_list_prev(&dtd->dtd_u.dtu_members);
+ ctf_id_t ltype = ctf_type_resolve(fp, lmd->dmd_type);
+ size_t off = lmd->dmd_offset;
+
+ ctf_encoding_t linfo;
+ ssize_t lsize;
+
+ if (ctf_type_encoding(fp, ltype, &linfo) != CTF_ERR)
+ off += linfo.cte_bits;
+ else if ((lsize = ctf_type_size(fp, ltype)) != CTF_ERR)
+ off += lsize * NBBY;
+
+ /*
+ * Round up the offset of the end of the last member to the
+ * next byte boundary, convert 'off' to bytes, and then round
+ * it up again to the next multiple of the alignment required
+ * by the new member. Finally, convert back to bits and store
+ * the result in dmd_offset. Technically we could do more
+ * efficient packing if the new member is a bit-field, but
+ * we're the "compiler" and ANSI says we can do as we choose.
+ */
+ off = roundup(off, NBBY) / NBBY;
+ off = roundup(off, MAX(malign, 1));
+ dmd->dmd_offset = off * NBBY;
+ ssize = off + msize;
+ } else {
+ dmd->dmd_offset = 0;
+ ssize = ctf_get_ctt_size(fp, &dtd->dtd_data, NULL, NULL);
+ ssize = MAX(ssize, msize);
+ }
+
+ if (ssize > CTF_MAX_SIZE) {
+ dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
+ dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(ssize);
+ dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(ssize);
+ } else
+ dtd->dtd_data.ctt_size = (ushort_t)ssize;
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, root, vlen + 1);
+ ctf_list_append(&dtd->dtd_u.dtu_members, dmd);
+
+ if (s != NULL)
+ fp->ctf_dtstrlen += strlen(s) + 1;
+
+ fp->ctf_flags |= LCTF_DIRTY;
+ return (0);
+}
+
+static int
+enumcmp(const char *name, int value, void *arg)
+{
+ ctf_bundle_t *ctb = arg;
+ int bvalue;
+
+ return (ctf_enum_value(ctb->ctb_file, ctb->ctb_type,
+ name, &bvalue) == CTF_ERR || value != bvalue);
+}
+
+static int
+enumadd(const char *name, int value, void *arg)
+{
+ ctf_bundle_t *ctb = arg;
+
+ return (ctf_add_enumerator(ctb->ctb_file, ctb->ctb_type,
+ name, value) == CTF_ERR);
+}
+
+/*ARGSUSED*/
+static int
+membcmp(const char *name, ctf_id_t type, ulong_t offset, void *arg)
+{
+ ctf_bundle_t *ctb = arg;
+ ctf_membinfo_t ctm;
+
+ return (ctf_member_info(ctb->ctb_file, ctb->ctb_type,
+ name, &ctm) == CTF_ERR || ctm.ctm_offset != offset);
+}
+
+static int
+membadd(const char *name, ctf_id_t type, ulong_t offset, void *arg)
+{
+ ctf_bundle_t *ctb = arg;
+ ctf_dmdef_t *dmd;
+ char *s = NULL;
+
+ if ((dmd = ctf_alloc(sizeof (ctf_dmdef_t))) == NULL)
+ return (ctf_set_errno(ctb->ctb_file, EAGAIN));
+
+ if (name != NULL && (s = ctf_strdup(name)) == NULL) {
+ ctf_free(dmd, sizeof (ctf_dmdef_t));
+ return (ctf_set_errno(ctb->ctb_file, EAGAIN));
+ }
+
+ /*
+ * For now, dmd_type is copied as the src_fp's type; it is reset to an
+ * equivalent dst_fp type by a final loop in ctf_add_type(), below.
+ */
+ dmd->dmd_name = s;
+ dmd->dmd_type = type;
+ dmd->dmd_offset = offset;
+ dmd->dmd_value = -1;
+
+ ctf_list_append(&ctb->ctb_dtd->dtd_u.dtu_members, dmd);
+
+ if (s != NULL)
+ ctb->ctb_file->ctf_dtstrlen += strlen(s) + 1;
+
+ ctb->ctb_file->ctf_flags |= LCTF_DIRTY;
+ return (0);
+}
+
+/*
+ * The ctf_add_type routine is used to copy a type from a source CTF container
+ * to a dynamic destination container. This routine operates recursively by
+ * following the source type's links and embedded member types. If the
+ * destination container already contains a named type which has the same
+ * attributes, then we succeed and return this type but no changes occur.
+ */
+ctf_id_t
+ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)
+{
+ ctf_id_t dst_type = CTF_ERR;
+ uint_t dst_kind = CTF_K_UNKNOWN;
+
+ const ctf_type_t *tp;
+ const char *name;
+ uint_t kind, flag, vlen;
+
+ ctf_bundle_t src, dst;
+ ctf_encoding_t src_en, dst_en;
+ ctf_arinfo_t src_ar, dst_ar;
+
+ ctf_dtdef_t *dtd;
+ ctf_funcinfo_t ctc;
+ ssize_t size;
+
+ ctf_hash_t *hp;
+ ctf_helem_t *hep;
+
+ if (!(dst_fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(dst_fp, ECTF_RDONLY));
+
+ if ((tp = ctf_lookup_by_id(&src_fp, src_type)) == NULL)
+ return (ctf_set_errno(dst_fp, ctf_errno(src_fp)));
+
+ name = ctf_strptr(src_fp, tp->ctt_name);
+ kind = LCTF_INFO_KIND(src_fp, tp->ctt_info);
+ flag = LCTF_INFO_ROOT(src_fp, tp->ctt_info);
+ vlen = LCTF_INFO_VLEN(src_fp, tp->ctt_info);
+
+ switch (kind) {
+ case CTF_K_STRUCT:
+ hp = &dst_fp->ctf_structs;
+ break;
+ case CTF_K_UNION:
+ hp = &dst_fp->ctf_unions;
+ break;
+ case CTF_K_ENUM:
+ hp = &dst_fp->ctf_enums;
+ break;
+ default:
+ hp = &dst_fp->ctf_names;
+ break;
+ }
+
+ /*
+ * If the source type has a name and is a root type (visible at the
+ * top-level scope), lookup the name in the destination container and
+ * verify that it is of the same kind before we do anything else.
+ */
+ if ((flag & CTF_ADD_ROOT) && name[0] != '\0' &&
+ (hep = ctf_hash_lookup(hp, dst_fp, name, strlen(name))) != NULL) {
+ dst_type = (ctf_id_t)hep->h_type;
+ dst_kind = ctf_type_kind(dst_fp, dst_type);
+ }
+
+ /*
+ * If an identically named dst_type exists, fail with ECTF_CONFLICT
+ * unless dst_type is a forward declaration and src_type is a struct,
+ * union, or enum (i.e. the definition of the previous forward decl).
+ */
+ if (dst_type != CTF_ERR && dst_kind != kind && (
+ dst_kind != CTF_K_FORWARD || (kind != CTF_K_ENUM &&
+ kind != CTF_K_STRUCT && kind != CTF_K_UNION)))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+
+ /*
+ * If the non-empty name was not found in the appropriate hash, search
+ * the list of pending dynamic definitions that are not yet committed.
+ * If a matching name and kind are found, assume this is the type that
+ * we are looking for. This is necessary to permit ctf_add_type() to
+ * operate recursively on entities such as a struct that contains a
+ * pointer member that refers to the same struct type.
+ */
+ if (dst_type == CTF_ERR && name[0] != '\0') {
+ for (dtd = ctf_list_prev(&dst_fp->ctf_dtdefs); dtd != NULL &&
+ dtd->dtd_type > dst_fp->ctf_dtoldid;
+ dtd = ctf_list_prev(dtd)) {
+ if (CTF_INFO_KIND(dtd->dtd_data.ctt_info) == kind &&
+ dtd->dtd_name != NULL &&
+ strcmp(dtd->dtd_name, name) == 0)
+ return (dtd->dtd_type);
+ }
+ }
+
+ src.ctb_file = src_fp;
+ src.ctb_type = src_type;
+ src.ctb_dtd = NULL;
+
+ dst.ctb_file = dst_fp;
+ dst.ctb_type = dst_type;
+ dst.ctb_dtd = NULL;
+
+ /*
+ * Now perform kind-specific processing. If dst_type is CTF_ERR, then
+ * we add a new type with the same properties as src_type to dst_fp.
+ * If dst_type is not CTF_ERR, then we verify that dst_type has the
+ * same attributes as src_type. We recurse for embedded references.
+ */
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ if (ctf_type_encoding(src_fp, src_type, &src_en) != 0)
+ return (ctf_set_errno(dst_fp, ctf_errno(src_fp)));
+
+ if (dst_type != CTF_ERR) {
+ if (ctf_type_encoding(dst_fp, dst_type, &dst_en) != 0)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (bcmp(&src_en, &dst_en, sizeof (ctf_encoding_t)))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+
+ } else if (kind == CTF_K_INTEGER) {
+ dst_type = ctf_add_integer(dst_fp, flag, name, &src_en);
+ } else
+ dst_type = ctf_add_float(dst_fp, flag, name, &src_en);
+ break;
+
+ case CTF_K_POINTER:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ src_type = ctf_type_reference(src_fp, src_type);
+ src_type = ctf_add_type(dst_fp, src_fp, src_type);
+
+ if (src_type == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dst_type = ctf_add_reftype(dst_fp, flag, src_type, kind);
+ break;
+
+ case CTF_K_ARRAY:
+ if (ctf_array_info(src_fp, src_type, &src_ar) == CTF_ERR)
+ return (ctf_set_errno(dst_fp, ctf_errno(src_fp)));
+
+ src_ar.ctr_contents =
+ ctf_add_type(dst_fp, src_fp, src_ar.ctr_contents);
+ src_ar.ctr_index =
+ ctf_add_type(dst_fp, src_fp, src_ar.ctr_index);
+ src_ar.ctr_nelems = src_ar.ctr_nelems;
+
+ if (src_ar.ctr_contents == CTF_ERR ||
+ src_ar.ctr_index == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (dst_type != CTF_ERR) {
+ if (ctf_array_info(dst_fp, dst_type, &dst_ar) != 0)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (bcmp(&src_ar, &dst_ar, sizeof (ctf_arinfo_t)))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+ } else
+ dst_type = ctf_add_array(dst_fp, flag, &src_ar);
+ break;
+
+ case CTF_K_FUNCTION:
+ ctc.ctc_return = ctf_add_type(dst_fp, src_fp, tp->ctt_type);
+ ctc.ctc_argc = 0;
+ ctc.ctc_flags = 0;
+
+ if (ctc.ctc_return == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dst_type = ctf_add_function(dst_fp, flag, &ctc, NULL);
+ break;
+
+ case CTF_K_STRUCT:
+ case CTF_K_UNION: {
+ ctf_dmdef_t *dmd;
+ int errs = 0;
+
+ /*
+ * Technically to match a struct or union we need to check both
+ * ways (src members vs. dst, dst members vs. src) but we make
+ * this more optimal by only checking src vs. dst and comparing
+ * the total size of the structure (which we must do anyway)
+ * which covers the possibility of dst members not in src.
+ * This optimization can be defeated for unions, but is so
+ * pathological as to render it irrelevant for our purposes.
+ */
+ if (dst_type != CTF_ERR && dst_kind != CTF_K_FORWARD) {
+ if (ctf_type_size(src_fp, src_type) !=
+ ctf_type_size(dst_fp, dst_type))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+
+ if (ctf_member_iter(src_fp, src_type, membcmp, &dst))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+
+ break;
+ }
+
+ /*
+ * Unlike the other cases, copying structs and unions is done
+ * manually so as to avoid repeated lookups in ctf_add_member
+ * and to ensure the exact same member offsets as in src_type.
+ */
+ dst_type = ctf_add_generic(dst_fp, flag, name, &dtd);
+ if (dst_type == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ dst.ctb_type = dst_type;
+ dst.ctb_dtd = dtd;
+
+ if (ctf_member_iter(src_fp, src_type, membadd, &dst) != 0)
+ errs++; /* increment errs and fail at bottom of case */
+
+ if ((size = ctf_type_size(src_fp, src_type)) > CTF_MAX_SIZE) {
+ dtd->dtd_data.ctt_size = CTF_LSIZE_SENT;
+ dtd->dtd_data.ctt_lsizehi = CTF_SIZE_TO_LSIZE_HI(size);
+ dtd->dtd_data.ctt_lsizelo = CTF_SIZE_TO_LSIZE_LO(size);
+ } else
+ dtd->dtd_data.ctt_size = (ushort_t)size;
+
+ dtd->dtd_data.ctt_info = CTF_TYPE_INFO(kind, flag, vlen);
+
+ /*
+ * Make a final pass through the members changing each dmd_type
+ * (a src_fp type) to an equivalent type in dst_fp. We pass
+ * through all members, leaving any that fail set to CTF_ERR.
+ */
+ for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
+ dmd != NULL; dmd = ctf_list_next(dmd)) {
+ if ((dmd->dmd_type = ctf_add_type(dst_fp, src_fp,
+ dmd->dmd_type)) == CTF_ERR)
+ errs++;
+ }
+
+ if (errs)
+ return (CTF_ERR); /* errno is set for us */
+ break;
+ }
+
+ case CTF_K_ENUM:
+ if (dst_type != CTF_ERR && dst_kind != CTF_K_FORWARD) {
+ if (ctf_enum_iter(src_fp, src_type, enumcmp, &dst) ||
+ ctf_enum_iter(dst_fp, dst_type, enumcmp, &src))
+ return (ctf_set_errno(dst_fp, ECTF_CONFLICT));
+ } else {
+ dst_type = ctf_add_enum(dst_fp, flag, name);
+ if ((dst.ctb_type = dst_type) == CTF_ERR ||
+ ctf_enum_iter(src_fp, src_type, enumadd, &dst))
+ return (CTF_ERR); /* errno is set for us */
+ }
+ break;
+
+ case CTF_K_FORWARD:
+ if (dst_type == CTF_ERR) {
+ dst_type = ctf_add_forward(dst_fp,
+ flag, name, CTF_K_STRUCT); /* assume STRUCT */
+ }
+ break;
+
+ case CTF_K_TYPEDEF:
+ src_type = ctf_type_reference(src_fp, src_type);
+ src_type = ctf_add_type(dst_fp, src_fp, src_type);
+
+ if (src_type == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ /*
+ * If dst_type is not CTF_ERR at this point, we should check if
+ * ctf_type_reference(dst_fp, dst_type) != src_type and if so
+ * fail with ECTF_CONFLICT. However, this causes problems with
+ * <sys/types.h> typedefs that vary based on things like if
+ * _ILP32x then pid_t is int otherwise long. We therefore omit
+ * this check and assume that if the identically named typedef
+ * already exists in dst_fp, it is correct or equivalent.
+ */
+ if (dst_type == CTF_ERR) {
+ dst_type = ctf_add_typedef(dst_fp, flag,
+ name, src_type);
+ }
+ break;
+
+ default:
+ return (ctf_set_errno(dst_fp, ECTF_CORRUPT));
+ }
+
+ return (dst_type);
+}
diff --git a/libctf/ctf_decl.c b/libctf/ctf_decl.c
new file mode 100644
index 0000000..63a6f5c
--- /dev/null
+++ b/libctf/ctf_decl.c
@@ -0,0 +1,184 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * CTF Declaration Stack
+ *
+ * In order to implement ctf_type_name(), we must convert a type graph back
+ * into a C type declaration. Unfortunately, a type graph represents a storage
+ * class ordering of the type whereas a type declaration must obey the C rules
+ * for operator precedence, and the two orderings are frequently in conflict.
+ * For example, consider these CTF type graphs and their C declarations:
+ *
+ * CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER : int (*)()
+ * CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER : int (*)[]
+ *
+ * In each case, parentheses are used to raise operator * to higher lexical
+ * precedence, so the string form of the C declaration cannot be constructed by
+ * walking the type graph links and forming the string from left to right.
+ *
+ * The functions in this file build a set of stacks from the type graph nodes
+ * corresponding to the C operator precedence levels in the appropriate order.
+ * The code in ctf_type_name() can then iterate over the levels and nodes in
+ * lexical precedence order and construct the final C declaration string.
+ */
+
+#include "ctf_impl.h"
+
+void
+ctf_decl_init(ctf_decl_t *cd, char *buf, size_t len)
+{
+ int i;
+
+ bzero(cd, sizeof (ctf_decl_t));
+
+ for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++)
+ cd->cd_order[i] = CTF_PREC_BASE - 1;
+
+ cd->cd_qualp = CTF_PREC_BASE;
+ cd->cd_ordp = CTF_PREC_BASE;
+
+ cd->cd_buf = buf;
+ cd->cd_ptr = buf;
+ cd->cd_end = buf + len;
+}
+
+void
+ctf_decl_fini(ctf_decl_t *cd)
+{
+ ctf_decl_node_t *cdp, *ndp;
+ int i;
+
+ for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) {
+ for (cdp = ctf_list_next(&cd->cd_nodes[i]);
+ cdp != NULL; cdp = ndp) {
+ ndp = ctf_list_next(cdp);
+ ctf_free(cdp, sizeof (ctf_decl_node_t));
+ }
+ }
+}
+
+void
+ctf_decl_push(ctf_decl_t *cd, ctf_file_t *fp, ctf_id_t type)
+{
+ ctf_decl_node_t *cdp;
+ ctf_decl_prec_t prec;
+ uint_t kind, n = 1;
+ int is_qual = 0;
+
+ const ctf_type_t *tp;
+ ctf_arinfo_t ar;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL) {
+ cd->cd_err = fp->ctf_errno;
+ return;
+ }
+
+ switch (kind = LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_ARRAY:
+ (void) ctf_array_info(fp, type, &ar);
+ ctf_decl_push(cd, fp, ar.ctr_contents);
+ n = ar.ctr_nelems;
+ prec = CTF_PREC_ARRAY;
+ break;
+
+ case CTF_K_TYPEDEF:
+ if (ctf_strptr(fp, tp->ctt_name)[0] == '\0') {
+ ctf_decl_push(cd, fp, tp->ctt_type);
+ return;
+ }
+ prec = CTF_PREC_BASE;
+ break;
+
+ case CTF_K_FUNCTION:
+ ctf_decl_push(cd, fp, tp->ctt_type);
+ prec = CTF_PREC_FUNCTION;
+ break;
+
+ case CTF_K_POINTER:
+ ctf_decl_push(cd, fp, tp->ctt_type);
+ prec = CTF_PREC_POINTER;
+ break;
+
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ ctf_decl_push(cd, fp, tp->ctt_type);
+ prec = cd->cd_qualp;
+ is_qual++;
+ break;
+
+ default:
+ prec = CTF_PREC_BASE;
+ }
+
+ if ((cdp = ctf_alloc(sizeof (ctf_decl_node_t))) == NULL) {
+ cd->cd_err = EAGAIN;
+ return;
+ }
+
+ cdp->cd_type = type;
+ cdp->cd_kind = kind;
+ cdp->cd_n = n;
+
+ if (ctf_list_next(&cd->cd_nodes[prec]) == NULL)
+ cd->cd_order[prec] = cd->cd_ordp++;
+
+ /*
+ * Reset cd_qualp to the highest precedence level that we've seen so
+ * far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER).
+ */
+ if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY)
+ cd->cd_qualp = prec;
+
+ /*
+ * C array declarators are ordered inside out so prepend them. Also by
+ * convention qualifiers of base types precede the type specifier (e.g.
+ * const int vs. int const) even though the two forms are equivalent.
+ */
+ if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE))
+ ctf_list_prepend(&cd->cd_nodes[prec], cdp);
+ else
+ ctf_list_append(&cd->cd_nodes[prec], cdp);
+}
+
+/*PRINTFLIKE2*/
+void
+ctf_decl_sprintf(ctf_decl_t *cd, const char *format, ...)
+{
+ size_t len = (size_t)(cd->cd_end - cd->cd_ptr);
+ va_list ap;
+ size_t n;
+
+ va_start(ap, format);
+ n = vsnprintf(cd->cd_ptr, len, format, ap);
+ va_end(ap);
+
+ cd->cd_ptr += MIN(n, len);
+ cd->cd_len += n;
+}
diff --git a/libctf/ctf_error.c b/libctf/ctf_error.c
new file mode 100644
index 0000000..0270339
--- /dev/null
+++ b/libctf/ctf_error.c
@@ -0,0 +1,97 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ctf_impl.h"
+
+static const char *const _ctf_errlist[] = {
+ "File is not in CTF or ELF format", /* ECTF_FMT */
+ "File uses more recent ELF version than libctf", /* ECTF_ELFVERS */
+ "File uses more recent CTF version than libctf", /* ECTF_CTFVERS */
+ "File is a different endian-ness than libctf", /* ECTF_ENDIAN */
+ "Symbol table uses invalid entry size", /* ECTF_SYMTAB */
+ "Symbol table data buffer is not valid", /* ECTF_SYMBAD */
+ "String table data buffer is not valid", /* ECTF_STRBAD */
+ "File data structure corruption detected", /* ECTF_CORRUPT */
+ "File does not contain CTF data", /* ECTF_NOCTFDATA */
+ "Buffer does not contain CTF data", /* ECTF_NOCTFBUF */
+ "Symbol table information is not available", /* ECTF_NOSYMTAB */
+ "Type information is in parent and unavailable", /* ECTF_NOPARENT */
+ "Cannot import types with different data model", /* ECTF_DMODEL */
+ "Failed to mmap a needed data section", /* ECTF_MMAP */
+ "Decompression package SUNWzlib not installed", /* ECTF_ZMISSING */
+ "Failed to initialize decompression library", /* ECTF_ZINIT */
+ "Failed to allocate decompression buffer", /* ECTF_ZALLOC */
+ "Failed to decompress CTF data", /* ECTF_DECOMPRESS */
+ "External string table is not available", /* ECTF_STRTAB */
+ "String name offset is corrupt", /* ECTF_BADNAME */
+ "Invalid type identifier", /* ECTF_BADID */
+ "Type is not a struct or union", /* ECTF_NOTSOU */
+ "Type is not an enum", /* ECTF_NOTENUM */
+ "Type is not a struct, union, or enum", /* ECTF_NOTSUE */
+ "Type is not an integer or float", /* ECTF_NOTINTFP */
+ "Type is not an array", /* ECTF_NOTARRAY */
+ "Type does not reference another type", /* ECTF_NOTREF */
+ "Input buffer is too small for type name", /* ECTF_NAMELEN */
+ "No type information available for that name", /* ECTF_NOTYPE */
+ "Syntax error in type name", /* ECTF_SYNTAX */
+ "Symbol table entry is not a function", /* ECTF_NOTFUNC */
+ "No function information available for symbol", /* ECTF_NOFUNCDAT */
+ "Symbol table entry is not a data object", /* ECTF_NOTDATA */
+ "No type information available for symbol", /* ECTF_NOTYPEDAT */
+ "No label information available for that name", /* ECTF_NOLABEL */
+ "File does not contain any labels", /* ECTF_NOLABELDATA */
+ "Feature not supported", /* ECTF_NOTSUP */
+ "Invalid enum element name", /* ECTF_NOENUMNAM */
+ "Invalid member name", /* ECTF_NOMEMBNAM */
+ "CTF container is read-only", /* ECTF_RDONLY */
+ "Limit on number of dynamic type members reached", /* ECTF_DTFULL */
+ "Limit on number of dynamic types reached", /* ECTF_FULL */
+ "Duplicate member name definition", /* ECTF_DUPMEMBER */
+ "Conflicting type is already defined", /* ECTF_CONFLICT */
+};
+
+static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]);
+
+const char *
+ctf_errmsg(int error)
+{
+ const char *str;
+
+ if (error >= ECTF_BASE && (error - ECTF_BASE) < _ctf_nerr)
+ str = _ctf_errlist[error - ECTF_BASE];
+ else
+ str = ctf_strerror(error);
+
+ return (str ? str : "Unknown error");
+}
+
+int
+ctf_errno(ctf_file_t *fp)
+{
+ return (fp->ctf_errno);
+}
diff --git a/libctf/ctf_hash.c b/libctf/ctf_hash.c
new file mode 100644
index 0000000..dfef067
--- /dev/null
+++ b/libctf/ctf_hash.c
@@ -0,0 +1,178 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ctf_impl.h"
+
+static const ushort_t _CTF_EMPTY[1] = { 0 };
+
+int
+ctf_hash_create(ctf_hash_t *hp, ulong_t nelems)
+{
+ if (nelems > USHRT_MAX)
+ return (EOVERFLOW);
+
+ /*
+ * If the hash table is going to be empty, don't bother allocating any
+ * memory and make the only bucket point to a zero so lookups fail.
+ */
+ if (nelems == 0) {
+ bzero(hp, sizeof (ctf_hash_t));
+ hp->h_buckets = (ushort_t *)_CTF_EMPTY;
+ hp->h_nbuckets = 1;
+ return (0);
+ }
+
+ hp->h_nbuckets = 211; /* use a prime number of hash buckets */
+ hp->h_nelems = nelems + 1; /* we use index zero as a sentinel */
+ hp->h_free = 1; /* first free element is index 1 */
+
+ hp->h_buckets = ctf_alloc(sizeof (ushort_t) * hp->h_nbuckets);
+ hp->h_chains = ctf_alloc(sizeof (ctf_helem_t) * hp->h_nelems);
+
+ if (hp->h_buckets == NULL || hp->h_chains == NULL) {
+ ctf_hash_destroy(hp);
+ return (EAGAIN);
+ }
+
+ bzero(hp->h_buckets, sizeof (ushort_t) * hp->h_nbuckets);
+ bzero(hp->h_chains, sizeof (ctf_helem_t) * hp->h_nelems);
+
+ return (0);
+}
+
+uint_t
+ctf_hash_size(const ctf_hash_t *hp)
+{
+ return (hp->h_nelems ? hp->h_nelems - 1 : 0);
+}
+
+static ulong_t
+ctf_hash_compute(const char *key, size_t len)
+{
+ ulong_t g, h = 0;
+ const char *p, *q = key + len;
+ size_t n = 0;
+
+ for (p = key; p < q; p++, n++) {
+ h = (h << 4) + *p;
+
+ if ((g = (h & 0xf0000000)) != 0) {
+ h ^= (g >> 24);
+ h ^= g;
+ }
+ }
+
+ return (h);
+}
+
+int
+ctf_hash_insert(ctf_hash_t *hp, ctf_file_t *fp, ushort_t type, uint_t name)
+{
+ ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID(name)];
+ const char *str = ctsp->cts_strs + CTF_NAME_OFFSET(name);
+ ctf_helem_t *hep = &hp->h_chains[hp->h_free];
+ ulong_t h;
+
+ if (type == 0)
+ return (EINVAL);
+
+ if (hp->h_free >= hp->h_nelems)
+ return (EOVERFLOW);
+
+ if (ctsp->cts_strs == NULL)
+ return (ECTF_STRTAB);
+
+ if (ctsp->cts_len <= CTF_NAME_OFFSET(name))
+ return (ECTF_BADNAME);
+
+ if (str[0] == '\0')
+ return (0); /* just ignore empty strings on behalf of caller */
+
+ hep->h_name = name;
+ hep->h_type = type;
+ h = ctf_hash_compute(str, strlen(str)) % hp->h_nbuckets;
+ hep->h_next = hp->h_buckets[h];
+ hp->h_buckets[h] = hp->h_free++;
+
+ return (0);
+}
+
+/*
+ * Wrapper for ctf_hash_lookup/ctf_hash_insert: if the key is already in the
+ * hash, override the previous definition with this new official definition.
+ * If the key is not present, then call ctf_hash_insert() and hash it in.
+ */
+int
+ctf_hash_define(ctf_hash_t *hp, ctf_file_t *fp, ushort_t type, uint_t name)
+{
+ const char *str = ctf_strptr(fp, name);
+ ctf_helem_t *hep = ctf_hash_lookup(hp, fp, str, strlen(str));
+
+ if (hep == NULL)
+ return (ctf_hash_insert(hp, fp, type, name));
+
+ hep->h_type = type;
+ return (0);
+}
+
+ctf_helem_t *
+ctf_hash_lookup(ctf_hash_t *hp, ctf_file_t *fp, const char *key, size_t len)
+{
+ ctf_helem_t *hep;
+ ctf_strs_t *ctsp;
+ const char *str;
+ ushort_t i;
+
+ ulong_t h = ctf_hash_compute(key, len) % hp->h_nbuckets;
+
+ for (i = hp->h_buckets[h]; i != 0; i = hep->h_next) {
+ hep = &hp->h_chains[i];
+ ctsp = &fp->ctf_str[CTF_NAME_STID(hep->h_name)];
+ str = ctsp->cts_strs + CTF_NAME_OFFSET(hep->h_name);
+
+ if (strncmp(key, str, len) == 0 && str[len] == '\0')
+ return (hep);
+ }
+
+ return (NULL);
+}
+
+void
+ctf_hash_destroy(ctf_hash_t *hp)
+{
+ if (hp->h_buckets != NULL && hp->h_nbuckets != 1) {
+ ctf_free(hp->h_buckets, sizeof (ushort_t) * hp->h_nbuckets);
+ hp->h_buckets = NULL;
+ }
+
+ if (hp->h_chains != NULL) {
+ ctf_free(hp->h_chains, sizeof (ctf_helem_t) * hp->h_nelems);
+ hp->h_chains = NULL;
+ }
+}
diff --git a/libctf/ctf_impl.h b/libctf/ctf_impl.h
new file mode 100644
index 0000000..f0f0d55
--- /dev/null
+++ b/libctf/ctf_impl.h
@@ -0,0 +1,385 @@
+/*
+ * 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 "pctf_config.h"
+
+#define CTF_OLD_VERSIONS
+
+#include <sys/types.h>
+#if HAVE_ERRNO_H
+# include <errno.h>
+#endif
+#if HAVE_SYS_ERRNO_H
+# include <sys/errno.h>
+#endif
+#if HAVE_SYS_SYSMACROS_H
+# include <sys/sysmacros.h>
+#else
+# include "pctf/sysmacros.h"
+#endif
+#if HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+#ifndef MAP_FAILED
+# define MAP_FAILED (void*)-1
+#endif
+#include "pctf/ctf_api.h"
+
+#if HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#if HAVE_STRING_H
+# include <string.h>
+#endif
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#if HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#if HAVE_CTYPE_H
+# include <ctype.h>
+#endif
+#if HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#if HAVE_FCNTL_H
+# include <fcntl.h>
+#endif
+#if HAVE_ZLIB_H
+# include <zlib.h>
+#endif
+#if HAVE_GELF_H
+# include <gelf.h>
+#endif
+#if HAVE_LIBELF_H
+# include <libelf.h>
+#endif
+#if HAVE_PTHREAD_H
+# include <pthread.h>
+#endif
+#if HAVE_SYS_ELF_H
+# include <sys/elf.h>
+#endif
+#if HAVE_LIBDWARF_H
+# include <libdwarf.h>
+#endif
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#endif
+#if HAVE_DWARF_H
+# include <dwarf.h>
+#endif
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+#include <assert.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct ctf_helem {
+ uint_t h_name; /* reference to name in string table */
+ ushort_t h_type; /* corresponding type ID number */
+ ushort_t h_next; /* index of next element in hash chain */
+} ctf_helem_t;
+
+typedef struct ctf_hash {
+ ushort_t *h_buckets; /* hash bucket array (chain indices) */
+ ctf_helem_t *h_chains; /* hash chains buffer */
+ ushort_t h_nbuckets; /* number of elements in bucket array */
+ ushort_t h_nelems; /* number of elements in hash table */
+ uint_t h_free; /* index of next free hash element */
+} ctf_hash_t;
+
+typedef struct ctf_strs {
+ const char *cts_strs; /* base address of string table */
+ size_t cts_len; /* size of string table in bytes */
+} ctf_strs_t;
+
+typedef struct ctf_dmodel {
+ const char *ctd_name; /* data model name */
+ int ctd_code; /* data model code */
+ size_t ctd_pointer; /* size of void * in bytes */
+ size_t ctd_char; /* size of char in bytes */
+ size_t ctd_short; /* size of short in bytes */
+ size_t ctd_int; /* size of int in bytes */
+ size_t ctd_long; /* size of long in bytes */
+} ctf_dmodel_t;
+
+typedef struct ctf_lookup {
+ const char *ctl_prefix; /* string prefix for this lookup */
+ size_t ctl_len; /* length of prefix string in bytes */
+ ctf_hash_t *ctl_hash; /* pointer to hash table for lookup */
+} ctf_lookup_t;
+
+typedef struct ctf_fileops {
+ ushort_t (*ctfo_get_kind)(ushort_t);
+ ushort_t (*ctfo_get_root)(ushort_t);
+ ushort_t (*ctfo_get_vlen)(ushort_t);
+} ctf_fileops_t;
+
+typedef struct ctf_list {
+ struct ctf_list *l_prev; /* previous pointer or tail pointer */
+ struct ctf_list *l_next; /* next pointer or head pointer */
+} ctf_list_t;
+
+typedef enum {
+ CTF_PREC_BASE,
+ CTF_PREC_POINTER,
+ CTF_PREC_ARRAY,
+ CTF_PREC_FUNCTION,
+ CTF_PREC_MAX
+} ctf_decl_prec_t;
+
+typedef struct ctf_decl_node {
+ ctf_list_t cd_list; /* linked list pointers */
+ ctf_id_t cd_type; /* type identifier */
+ uint_t cd_kind; /* type kind */
+ uint_t cd_n; /* type dimension if array */
+} ctf_decl_node_t;
+
+typedef struct ctf_decl {
+ ctf_list_t cd_nodes[CTF_PREC_MAX]; /* declaration node stacks */
+ int cd_order[CTF_PREC_MAX]; /* storage order of decls */
+ ctf_decl_prec_t cd_qualp; /* qualifier precision */
+ ctf_decl_prec_t cd_ordp; /* ordered precision */
+ char *cd_buf; /* buffer for output */
+ char *cd_ptr; /* buffer location */
+ char *cd_end; /* buffer limit */
+ size_t cd_len; /* buffer space required */
+ int cd_err; /* saved error value */
+} ctf_decl_t;
+
+typedef struct ctf_dmdef {
+ ctf_list_t dmd_list; /* list forward/back pointers */
+ char *dmd_name; /* name of this member */
+ ctf_id_t dmd_type; /* type of this member (for sou) */
+ ulong_t dmd_offset; /* offset of this member in bits (for sou) */
+ int dmd_value; /* value of this member (for enum) */
+} ctf_dmdef_t;
+
+typedef struct ctf_dtdef {
+ ctf_list_t dtd_list; /* list forward/back pointers */
+ struct ctf_dtdef *dtd_hash; /* hash chain pointer for ctf_dthash */
+ char *dtd_name; /* name associated with definition (if any) */
+ ctf_id_t dtd_type; /* type identifier for this definition */
+ ctf_type_t dtd_data; /* type node (see <sys/ctf.h>) */
+ union {
+ ctf_list_t dtu_members; /* struct, union, or enum */
+ ctf_arinfo_t dtu_arr; /* array */
+ ctf_encoding_t dtu_enc; /* integer or float */
+ ctf_id_t *dtu_argv; /* function */
+ } dtd_u;
+} ctf_dtdef_t;
+
+typedef struct ctf_bundle {
+ ctf_file_t *ctb_file; /* CTF container handle */
+ ctf_id_t ctb_type; /* CTF type identifier */
+ ctf_dtdef_t *ctb_dtd; /* CTF dynamic type definition (if any) */
+} ctf_bundle_t;
+
+/*
+ * The ctf_file is the structure used to represent a CTF container to library
+ * clients, who see it only as an opaque pointer. Modifications can therefore
+ * be made freely to this structure without regard to client versioning. The
+ * ctf_file_t typedef appears in <sys/ctf_api.h> and declares a forward tag.
+ *
+ * NOTE: ctf_update() requires that everything inside of ctf_file either be an
+ * immediate value, a pointer to dynamically allocated data *outside* of the
+ * ctf_file itself, or a pointer to statically allocated data. If you add a
+ * pointer to ctf_file that points to something within the ctf_file itself,
+ * you must make corresponding changes to ctf_update().
+ */
+struct ctf_file {
+ const ctf_fileops_t *ctf_fileops; /* version-specific file operations */
+ ctf_sect_t ctf_data; /* CTF data from object file */
+ ctf_sect_t ctf_symtab; /* symbol table from object file */
+ ctf_sect_t ctf_strtab; /* string table from object file */
+ ctf_hash_t ctf_structs; /* hash table of struct types */
+ ctf_hash_t ctf_unions; /* hash table of union types */
+ ctf_hash_t ctf_enums; /* hash table of enum types */
+ ctf_hash_t ctf_names; /* hash table of remaining type names */
+ ctf_lookup_t ctf_lookups[5]; /* pointers to hashes for name lookup */
+ ctf_strs_t ctf_str[2]; /* array of string table base and bounds */
+ const uchar_t *ctf_base; /* base of CTF header + uncompressed buffer */
+ const uchar_t *ctf_buf; /* uncompressed CTF data buffer */
+ size_t ctf_size; /* size of CTF header + uncompressed data */
+ uint_t *ctf_sxlate; /* translation table for symtab entries */
+ ulong_t ctf_nsyms; /* number of entries in symtab xlate table */
+ uint_t *ctf_txlate; /* translation table for type IDs */
+ ushort_t *ctf_ptrtab; /* translation table for pointer-to lookups */
+ ulong_t ctf_typemax; /* maximum valid type ID number */
+ const ctf_dmodel_t *ctf_dmodel; /* data model pointer (see above) */
+ struct ctf_file *ctf_parent; /* parent CTF container (if any) */
+ const char *ctf_parlabel; /* label in parent container (if any) */
+ const char *ctf_parname; /* basename of parent (if any) */
+ uint_t ctf_refcnt; /* reference count (for parent links) */
+ uint_t ctf_flags; /* libctf flags (see below) */
+ int ctf_errno; /* error code for most recent error */
+ int ctf_version; /* CTF data version */
+ ctf_dtdef_t **ctf_dthash; /* hash of dynamic type definitions */
+ ulong_t ctf_dthashlen; /* size of dynamic type hash bucket array */
+ ctf_list_t ctf_dtdefs; /* list of dynamic type definitions */
+ size_t ctf_dtstrlen; /* total length of dynamic type strings */
+ ulong_t ctf_dtnextid; /* next dynamic type id to assign */
+ ulong_t ctf_dtoldid; /* oldest id that has been committed */
+ void *ctf_specific; /* data for ctf_get/setspecific */
+};
+
+#define LCTF_INDEX_TO_TYPEPTR(fp, i) \
+ ((ctf_type_t *)((uintptr_t)(fp)->ctf_buf + (fp)->ctf_txlate[(i)]))
+
+#define LCTF_INFO_KIND(fp, info) ((fp)->ctf_fileops->ctfo_get_kind(info))
+#define LCTF_INFO_ROOT(fp, info) ((fp)->ctf_fileops->ctfo_get_root(info))
+#define LCTF_INFO_VLEN(fp, info) ((fp)->ctf_fileops->ctfo_get_vlen(info))
+
+#define LCTF_MMAP 0x0001 /* libctf should munmap buffers on close */
+#define LCTF_CHILD 0x0002 /* CTF container is a child */
+#define LCTF_RDWR 0x0004 /* CTF container is writable */
+#define LCTF_DIRTY 0x0008 /* CTF container has been modified */
+
+#define ECTF_BASE 1000 /* base value for libctf errnos */
+
+enum {
+ ECTF_FMT = ECTF_BASE, /* file is not in CTF or ELF format */
+ ECTF_ELFVERS, /* ELF version is more recent than libctf */
+ ECTF_CTFVERS, /* CTF version is more recent than libctf */
+ ECTF_ENDIAN, /* data is different endian-ness than lib */
+ ECTF_SYMTAB, /* symbol table uses invalid entry size */
+ ECTF_SYMBAD, /* symbol table data buffer invalid */
+ ECTF_STRBAD, /* string table data buffer invalid */
+ ECTF_CORRUPT, /* file data corruption detected */
+ ECTF_NOCTFDATA, /* ELF file does not contain CTF data */
+ ECTF_NOCTFBUF, /* buffer does not contain CTF data */
+ ECTF_NOSYMTAB, /* symbol table data is not available */
+ ECTF_NOPARENT, /* parent CTF container is not available */
+ ECTF_DMODEL, /* data model mismatch */
+ ECTF_MMAP, /* failed to mmap a data section */
+ ECTF_ZMISSING, /* decompression library not installed */
+ ECTF_ZINIT, /* failed to initialize decompression library */
+ ECTF_ZALLOC, /* failed to allocate decompression buffer */
+ ECTF_DECOMPRESS, /* failed to decompress CTF data */
+ ECTF_STRTAB, /* string table for this string is missing */
+ ECTF_BADNAME, /* string offset is corrupt w.r.t. strtab */
+ ECTF_BADID, /* invalid type ID number */
+ ECTF_NOTSOU, /* type is not a struct or union */
+ ECTF_NOTENUM, /* type is not an enum */
+ ECTF_NOTSUE, /* type is not a struct, union, or enum */
+ ECTF_NOTINTFP, /* type is not an integer or float */
+ ECTF_NOTARRAY, /* type is not an array */
+ ECTF_NOTREF, /* type does not reference another type */
+ ECTF_NAMELEN, /* buffer is too small to hold type name */
+ ECTF_NOTYPE, /* no type found corresponding to name */
+ ECTF_SYNTAX, /* syntax error in type name */
+ ECTF_NOTFUNC, /* symtab entry does not refer to a function */
+ ECTF_NOFUNCDAT, /* no func info available for function */
+ ECTF_NOTDATA, /* symtab entry does not refer to a data obj */
+ ECTF_NOTYPEDAT, /* no type info available for object */
+ ECTF_NOLABEL, /* no label found corresponding to name */
+ ECTF_NOLABELDATA, /* file does not contain any labels */
+ ECTF_NOTSUP, /* feature not supported */
+ ECTF_NOENUMNAM, /* enum element name not found */
+ ECTF_NOMEMBNAM, /* member name not found */
+ ECTF_RDONLY, /* CTF container is read-only */
+ ECTF_DTFULL, /* CTF type is full (no more members allowed) */
+ ECTF_FULL, /* CTF container is full */
+ ECTF_DUPMEMBER, /* duplicate member name definition */
+ ECTF_CONFLICT /* conflicting type definition present */
+};
+
+extern ssize_t ctf_get_ctt_size(const ctf_file_t *, const ctf_type_t *,
+ ssize_t *, ssize_t *);
+
+extern const ctf_type_t *ctf_lookup_by_id(ctf_file_t **, ctf_id_t);
+
+extern int ctf_hash_create(ctf_hash_t *, ulong_t);
+extern int ctf_hash_insert(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t);
+extern int ctf_hash_define(ctf_hash_t *, ctf_file_t *, ushort_t, uint_t);
+extern ctf_helem_t *ctf_hash_lookup(ctf_hash_t *, ctf_file_t *,
+ const char *, size_t);
+extern uint_t ctf_hash_size(const ctf_hash_t *);
+extern void ctf_hash_destroy(ctf_hash_t *);
+
+#define ctf_list_prev(elem) ((void *)(((ctf_list_t *)(elem))->l_prev))
+#define ctf_list_next(elem) ((void *)(((ctf_list_t *)(elem))->l_next))
+
+extern void ctf_list_append(ctf_list_t *, void *);
+extern void ctf_list_prepend(ctf_list_t *, void *);
+extern void ctf_list_delete(ctf_list_t *, void *);
+
+extern void ctf_dtd_insert(ctf_file_t *, ctf_dtdef_t *);
+extern void ctf_dtd_delete(ctf_file_t *, ctf_dtdef_t *);
+extern ctf_dtdef_t *ctf_dtd_lookup(ctf_file_t *, ctf_id_t);
+
+extern void ctf_decl_init(ctf_decl_t *, char *, size_t);
+extern void ctf_decl_fini(ctf_decl_t *);
+extern void ctf_decl_push(ctf_decl_t *, ctf_file_t *, ctf_id_t);
+extern void ctf_decl_sprintf(ctf_decl_t *, const char *, ...);
+
+extern const char *ctf_strraw(ctf_file_t *, uint_t);
+extern const char *ctf_strptr(ctf_file_t *, uint_t);
+
+extern ctf_file_t *ctf_set_open_errno(int *, int);
+extern long ctf_set_errno(ctf_file_t *, int);
+
+extern const void *ctf_sect_mmap(ctf_sect_t *, int);
+extern void ctf_sect_munmap(const ctf_sect_t *);
+
+extern void *ctf_data_alloc(size_t);
+extern void ctf_data_free(void *, size_t);
+extern void ctf_data_protect(void *, size_t);
+
+extern void *ctf_alloc(size_t);
+extern void ctf_free(void *, size_t);
+
+extern char *ctf_strdup(const char *);
+extern const char *ctf_strerror(int);
+extern void ctf_dprintf(const char *, ...);
+
+extern void *ctf_zopen(int *);
+
+extern const char _CTF_SECTION[]; /* name of CTF ELF section */
+extern const char _CTF_NULLSTR[]; /* empty string */
+
+extern int _libctf_version; /* library client version */
+extern int _libctf_debug; /* debugging messages enabled */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTF_IMPL_H */
diff --git a/libctf/ctf_labels.c b/libctf/ctf_labels.c
new file mode 100644
index 0000000..7bc9451
--- /dev/null
+++ b/libctf/ctf_labels.c
@@ -0,0 +1,153 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2002-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ctf_impl.h"
+
+static int
+extract_label_info(ctf_file_t *fp, const ctf_lblent_t **ctl, uint_t *num_labels)
+{
+ const ctf_header_t *h;
+
+ /*
+ * Labels are only supported in V2 or later
+ */
+ if (fp->ctf_version < CTF_VERSION_2)
+ return (ctf_set_errno(fp, ECTF_NOTSUP));
+
+ h = (const ctf_header_t *)fp->ctf_data.cts_data;
+
+ /* LINTED - pointer alignment */
+ *ctl = (const ctf_lblent_t *)(fp->ctf_buf + h->cth_lbloff);
+ *num_labels = (h->cth_objtoff - h->cth_lbloff) / sizeof (ctf_lblent_t);
+
+ return (0);
+}
+
+/*
+ * Returns the topmost label, or NULL if any errors are encountered
+ */
+const char *
+ctf_label_topmost(ctf_file_t *fp)
+{
+ const ctf_lblent_t *ctlp;
+ const char *s;
+ uint_t num_labels;
+
+ if (extract_label_info(fp, &ctlp, &num_labels) == CTF_ERR)
+ return (NULL); /* errno is set */
+
+ if (num_labels == 0) {
+ (void) ctf_set_errno(fp, ECTF_NOLABELDATA);
+ return (NULL);
+ }
+
+ if ((s = ctf_strraw(fp, (ctlp + num_labels - 1)->ctl_label)) == NULL)
+ (void) ctf_set_errno(fp, ECTF_CORRUPT);
+
+ return (s);
+}
+
+/*
+ * Iterate over all labels. We pass the label string and the lblinfo_t struct
+ * to the specified callback function.
+ */
+int
+ctf_label_iter(ctf_file_t *fp, ctf_label_f *func, void *arg)
+{
+ const ctf_lblent_t *ctlp;
+ uint_t i, num_labels;
+ ctf_lblinfo_t linfo;
+ const char *lname;
+ int rc;
+
+ if (extract_label_info(fp, &ctlp, &num_labels) == CTF_ERR)
+ return (CTF_ERR); /* errno is set */
+
+ if (num_labels == 0)
+ return (ctf_set_errno(fp, ECTF_NOLABELDATA));
+
+ for (i = 0; i < num_labels; i++, ctlp++) {
+ if ((lname = ctf_strraw(fp, ctlp->ctl_label)) == NULL) {
+ ctf_dprintf("failed to decode label %u with "
+ "typeidx %u\n", ctlp->ctl_label, ctlp->ctl_typeidx);
+ return (ctf_set_errno(fp, ECTF_CORRUPT));
+ }
+
+ linfo.ctb_typeidx = ctlp->ctl_typeidx;
+ if ((rc = func(lname, &linfo, arg)) != 0)
+ return (rc);
+ }
+
+ return (0);
+}
+
+typedef struct linfo_cb_arg {
+ const char *lca_name; /* Label we want to retrieve info for */
+ ctf_lblinfo_t *lca_info; /* Where to store the info about the label */
+} linfo_cb_arg_t;
+
+static int
+label_info_cb(const char *lname, const ctf_lblinfo_t *linfo, void *arg)
+{
+ /*
+ * If lname matches the label we are looking for, copy the
+ * lblinfo_t struct for the caller.
+ */
+ if (strcmp(lname, ((linfo_cb_arg_t *)arg)->lca_name) == 0) {
+ /*
+ * Allow caller not to allocate storage to test if label exists
+ */
+ if (((linfo_cb_arg_t *)arg)->lca_info != NULL)
+ bcopy(linfo, ((linfo_cb_arg_t *)arg)->lca_info,
+ sizeof (ctf_lblinfo_t));
+ return (1); /* Indicate we found a match */
+ }
+
+ return (0);
+}
+
+/*
+ * Retrieve information about the label with name "lname"
+ */
+int
+ctf_label_info(ctf_file_t *fp, const char *lname, ctf_lblinfo_t *linfo)
+{
+ linfo_cb_arg_t cb_arg;
+ int rc;
+
+ cb_arg.lca_name = lname;
+ cb_arg.lca_info = linfo;
+
+ if ((rc = ctf_label_iter(fp, label_info_cb, &cb_arg)) == CTF_ERR)
+ return (rc);
+
+ if (rc != 1)
+ return (ctf_set_errno(fp, ECTF_NOLABEL));
+
+ return (0);
+}
diff --git a/libctf/ctf_lib.c b/libctf/ctf_lib.c
new file mode 100644
index 0000000..571f2da
--- /dev/null
+++ b/libctf/ctf_lib.c
@@ -0,0 +1,450 @@
+/*
+ * 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 struct {
+ int (*z_uncompress)(uchar_t *, ulong_t *, const uchar_t *, ulong_t);
+ const char *(*z_error)(int);
+ void *z_dlp;
+} zlib;
+
+static size_t _PAGESIZE;
+static size_t _PAGEMASK;
+
+#if defined(sun)
+#pragma init(_libctf_init)
+#else
+void _libctf_init(void) __attribute__ ((constructor));
+#endif
+void
+_libctf_init(void)
+{
+ _libctf_debug = getenv("LIBCTF_DEBUG") != NULL;
+
+ _PAGESIZE = getpagesize();
+ _PAGEMASK = ~(_PAGESIZE - 1);
+}
+
+/*
+ * Attempt to dlopen the decompression library and locate the symbols of
+ * interest that we will need to call. This information in cached so
+ * that multiple calls to ctf_bufopen() do not need to reopen the library.
+ */
+void *
+ctf_zopen(int *errp)
+{
+ zlib.z_uncompress = uncompress;
+ zlib.z_error = zError;
+
+ /* Dummy return variable as 'no error' */
+ zlib.z_dlp = (void *) (uintptr_t) 1;
+
+ return (zlib.z_dlp);
+}
+
+/*
+ * The ctf_bufopen() routine calls these subroutines, defined by <sys/zmod.h>,
+ * which we then patch through to the functions in the decompression library.
+ */
+int
+z_uncompress(void *dst, size_t *dstlen, const void *src, size_t srclen)
+{
+ return (zlib.z_uncompress(dst, (ulong_t *)dstlen, src, srclen));
+}
+
+const char *
+z_strerror(int err)
+{
+ return (zlib.z_error(err));
+}
+
+/*
+ * Convert a 32-bit ELF file header into GElf.
+ */
+static void
+ehdr_to_gelf(const Elf32_Ehdr *src, GElf_Ehdr *dst)
+{
+ bcopy(src->e_ident, dst->e_ident, EI_NIDENT);
+ dst->e_type = src->e_type;
+ dst->e_machine = src->e_machine;
+ dst->e_version = src->e_version;
+ dst->e_entry = (Elf64_Addr)src->e_entry;
+ dst->e_phoff = (Elf64_Off)src->e_phoff;
+ dst->e_shoff = (Elf64_Off)src->e_shoff;
+ dst->e_flags = src->e_flags;
+ dst->e_ehsize = src->e_ehsize;
+ dst->e_phentsize = src->e_phentsize;
+ dst->e_phnum = src->e_phnum;
+ dst->e_shentsize = src->e_shentsize;
+ dst->e_shnum = src->e_shnum;
+ dst->e_shstrndx = src->e_shstrndx;
+}
+
+/*
+ * Convert a 32-bit ELF section header into GElf.
+ */
+static void
+shdr_to_gelf(const Elf32_Shdr *src, GElf_Shdr *dst)
+{
+ dst->sh_name = src->sh_name;
+ dst->sh_type = src->sh_type;
+ dst->sh_flags = src->sh_flags;
+ dst->sh_addr = src->sh_addr;
+ dst->sh_offset = src->sh_offset;
+ dst->sh_size = src->sh_size;
+ dst->sh_link = src->sh_link;
+ dst->sh_info = src->sh_info;
+ dst->sh_addralign = src->sh_addralign;
+ dst->sh_entsize = src->sh_entsize;
+}
+
+/*
+ * In order to mmap a section from the ELF file, we must round down sh_offset
+ * to the previous page boundary, and mmap the surrounding page. We store
+ * the pointer to the start of the actual section data back into sp->cts_data.
+ */
+const void *
+ctf_sect_mmap(ctf_sect_t *sp, int fd)
+{
+ size_t pageoff = sp->cts_offset & ~_PAGEMASK;
+
+ caddr_t base = mmap64(NULL, sp->cts_size + pageoff, PROT_READ,
+ MAP_PRIVATE, fd, sp->cts_offset & _PAGEMASK);
+
+ if (base != MAP_FAILED)
+ sp->cts_data = base + pageoff;
+
+ return (base);
+}
+
+/*
+ * Since sp->cts_data has the adjusted offset, we have to again round down
+ * to get the actual mmap address and round up to get the size.
+ */
+void
+ctf_sect_munmap(const ctf_sect_t *sp)
+{
+ uintptr_t addr = (uintptr_t)sp->cts_data;
+ uintptr_t pageoff = addr & ~_PAGEMASK;
+
+ (void) munmap((void *)(addr - pageoff), sp->cts_size + pageoff);
+}
+
+/*
+ * Open the specified file descriptor and return a pointer to a CTF container.
+ * The file can be either an ELF file or raw CTF file. The caller is
+ * responsible for closing the file descriptor when it is no longer needed.
+ */
+ctf_file_t *
+ctf_fdopen(int fd, int *errp)
+{
+ ctf_sect_t ctfsect, symsect, strsect;
+ ctf_file_t *fp = NULL;
+
+ struct stat st;
+ ssize_t nbytes;
+
+ union {
+ ctf_preamble_t ctf;
+ Elf32_Ehdr e32;
+ GElf_Ehdr e64;
+ } hdr;
+
+ bzero(&ctfsect, sizeof (ctf_sect_t));
+ bzero(&symsect, sizeof (ctf_sect_t));
+ bzero(&strsect, sizeof (ctf_sect_t));
+ bzero(&hdr.ctf, sizeof (hdr));
+
+ if (fstat64(fd, &st) == -1)
+ return (ctf_set_open_errno(errp, errno));
+
+ if ((nbytes = pread64(fd, &hdr.ctf, sizeof (hdr), 0)) <= 0)
+ return (ctf_set_open_errno(errp, nbytes < 0? errno : ECTF_FMT));
+
+ /*
+ * If we have read enough bytes to form a CTF header and the magic
+ * string matches, attempt to interpret the file as raw CTF.
+ */
+ if (nbytes >= (ssize_t) sizeof (ctf_preamble_t) &&
+ hdr.ctf.ctp_magic == CTF_MAGIC) {
+ if (hdr.ctf.ctp_version > CTF_VERSION)
+ return (ctf_set_open_errno(errp, ECTF_CTFVERS));
+
+ ctfsect.cts_data = mmap64(NULL, st.st_size, PROT_READ,
+ MAP_PRIVATE, fd, 0);
+
+ if (ctfsect.cts_data == MAP_FAILED)
+ return (ctf_set_open_errno(errp, errno));
+
+ ctfsect.cts_name = _CTF_SECTION;
+ ctfsect.cts_type = SHT_PROGBITS;
+ ctfsect.cts_flags = SHF_ALLOC;
+ ctfsect.cts_size = (size_t)st.st_size;
+ ctfsect.cts_entsize = 1;
+ ctfsect.cts_offset = 0;
+
+ if ((fp = ctf_bufopen(&ctfsect, NULL, NULL, errp)) == NULL)
+ ctf_sect_munmap(&ctfsect);
+
+ return (fp);
+ }
+
+ /*
+ * If we have read enough bytes to form an ELF header and the magic
+ * string matches, attempt to interpret the file as an ELF file. We
+ * do our own largefile ELF processing, and convert everything to
+ * GElf structures so that clients can operate on any data model.
+ */
+ if (nbytes >= (ssize_t) sizeof (Elf32_Ehdr) &&
+ bcmp(&hdr.e32.e_ident[EI_MAG0], ELFMAG, SELFMAG) == 0) {
+#ifdef _BIG_ENDIAN
+ uchar_t order = ELFDATA2MSB;
+#else
+ uchar_t order = ELFDATA2LSB;
+#endif
+ GElf_Half i, n;
+ GElf_Shdr *sp;
+
+ void *strs_map;
+ size_t strs_mapsz;
+ char *strs;
+
+ if (hdr.e32.e_ident[EI_DATA] != order)
+ return (ctf_set_open_errno(errp, ECTF_ENDIAN));
+ if (hdr.e32.e_version != EV_CURRENT)
+ return (ctf_set_open_errno(errp, ECTF_ELFVERS));
+
+ if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS64) {
+ if (nbytes < (ssize_t) sizeof (GElf_Ehdr))
+ return (ctf_set_open_errno(errp, ECTF_FMT));
+ } else {
+ Elf32_Ehdr e32 = hdr.e32;
+ ehdr_to_gelf(&e32, &hdr.e64);
+ }
+
+ if (hdr.e64.e_shstrndx >= hdr.e64.e_shnum)
+ return (ctf_set_open_errno(errp, ECTF_CORRUPT));
+
+ n = hdr.e64.e_shnum;
+ nbytes = sizeof (GElf_Shdr) * n;
+
+ if ((sp = malloc(nbytes)) == NULL)
+ return (ctf_set_open_errno(errp, errno));
+
+ /*
+ * Read in and convert to GElf the array of Shdr structures
+ * from e_shoff so we can locate sections of interest.
+ */
+ if (hdr.e32.e_ident[EI_CLASS] == ELFCLASS32) {
+ Elf32_Shdr *sp32;
+
+ nbytes = sizeof (Elf32_Shdr) * n;
+
+ if ((sp32 = malloc(nbytes)) == NULL || pread64(fd,
+ sp32, nbytes, hdr.e64.e_shoff) != nbytes) {
+ free(sp);
+ return (ctf_set_open_errno(errp, errno));
+ }
+
+ for (i = 0; i < n; i++)
+ shdr_to_gelf(&sp32[i], &sp[i]);
+
+ free(sp32);
+
+ } else if (pread64(fd, sp, nbytes, hdr.e64.e_shoff) != nbytes) {
+ free(sp);
+ return (ctf_set_open_errno(errp, errno));
+ }
+
+ /*
+ * Now mmap the section header strings section so that we can
+ * perform string comparison on the section names.
+ */
+ strs_mapsz = sp[hdr.e64.e_shstrndx].sh_size +
+ (sp[hdr.e64.e_shstrndx].sh_offset & ~_PAGEMASK);
+
+ strs_map = mmap64(NULL, strs_mapsz, PROT_READ, MAP_PRIVATE,
+ fd, sp[hdr.e64.e_shstrndx].sh_offset & _PAGEMASK);
+
+ strs = (char *)strs_map +
+ (sp[hdr.e64.e_shstrndx].sh_offset & ~_PAGEMASK);
+
+ if (strs_map == MAP_FAILED) {
+ free(sp);
+ return (ctf_set_open_errno(errp, ECTF_MMAP));
+ }
+
+ /*
+ * Iterate over the section header array looking for the CTF
+ * section and symbol table. The strtab is linked to symtab.
+ */
+ for (i = 0; i < n; i++) {
+ const GElf_Shdr *shp = &sp[i];
+ const GElf_Shdr *lhp = &sp[shp->sh_link];
+
+ if (shp->sh_link >= hdr.e64.e_shnum)
+ continue; /* corrupt sh_link field */
+
+ if (shp->sh_name >= sp[hdr.e64.e_shstrndx].sh_size ||
+ lhp->sh_name >= sp[hdr.e64.e_shstrndx].sh_size)
+ continue; /* corrupt sh_name field */
+
+ if (shp->sh_type == SHT_PROGBITS &&
+ strcmp(strs + shp->sh_name, _CTF_SECTION) == 0) {
+ ctfsect.cts_name = strs + shp->sh_name;
+ ctfsect.cts_type = shp->sh_type;
+ ctfsect.cts_flags = shp->sh_flags;
+ ctfsect.cts_size = shp->sh_size;
+ ctfsect.cts_entsize = shp->sh_entsize;
+ ctfsect.cts_offset = (off64_t)shp->sh_offset;
+
+ } else if (shp->sh_type == SHT_SYMTAB) {
+ symsect.cts_name = strs + shp->sh_name;
+ symsect.cts_type = shp->sh_type;
+ symsect.cts_flags = shp->sh_flags;
+ symsect.cts_size = shp->sh_size;
+ symsect.cts_entsize = shp->sh_entsize;
+ symsect.cts_offset = (off64_t)shp->sh_offset;
+
+ strsect.cts_name = strs + lhp->sh_name;
+ strsect.cts_type = lhp->sh_type;
+ strsect.cts_flags = lhp->sh_flags;
+ strsect.cts_size = lhp->sh_size;
+ strsect.cts_entsize = lhp->sh_entsize;
+ strsect.cts_offset = (off64_t)lhp->sh_offset;
+ }
+ }
+
+ free(sp); /* free section header array */
+
+ if (ctfsect.cts_type == SHT_NULL) {
+ (void) munmap(strs_map, strs_mapsz);
+ return (ctf_set_open_errno(errp, ECTF_NOCTFDATA));
+ }
+
+ /*
+ * Now mmap the CTF data, symtab, and strtab sections and
+ * call ctf_bufopen() to do the rest of the work.
+ */
+ if (ctf_sect_mmap(&ctfsect, fd) == MAP_FAILED) {
+ (void) munmap(strs_map, strs_mapsz);
+ return (ctf_set_open_errno(errp, ECTF_MMAP));
+ }
+
+ if (symsect.cts_type != SHT_NULL &&
+ strsect.cts_type != SHT_NULL) {
+ if (ctf_sect_mmap(&symsect, fd) == MAP_FAILED ||
+ ctf_sect_mmap(&strsect, fd) == MAP_FAILED) {
+ (void) ctf_set_open_errno(errp, ECTF_MMAP);
+ goto bad; /* unmap all and abort */
+ }
+ fp = ctf_bufopen(&ctfsect, &symsect, &strsect, errp);
+ } else
+ fp = ctf_bufopen(&ctfsect, NULL, NULL, errp);
+bad:
+ if (fp == NULL) {
+ ctf_sect_munmap(&ctfsect);
+ ctf_sect_munmap(&symsect);
+ ctf_sect_munmap(&strsect);
+ } else
+ fp->ctf_flags |= LCTF_MMAP;
+
+ (void) munmap(strs_map, strs_mapsz);
+ return (fp);
+ }
+
+ return (ctf_set_open_errno(errp, ECTF_FMT));
+}
+
+/*
+ * Open the specified file and return a pointer to a CTF container. The file
+ * can be either an ELF file or raw CTF file. This is just a convenient
+ * wrapper around ctf_fdopen() for callers.
+ */
+ctf_file_t *
+ctf_open(const char *filename, int *errp)
+{
+ ctf_file_t *fp;
+ int fd;
+
+ if ((fd = open64(filename, O_RDONLY)) == -1) {
+ if (errp != NULL)
+ *errp = errno;
+ return (NULL);
+ }
+
+ fp = ctf_fdopen(fd, errp);
+ (void) close(fd);
+ return (fp);
+}
+
+/*
+ * Write the uncompressed CTF data stream to the specified file descriptor.
+ * This is useful for saving the results of dynamic CTF containers.
+ */
+int
+ctf_write(ctf_file_t *fp, int fd)
+{
+ const uchar_t *buf = fp->ctf_base;
+ ssize_t resid = fp->ctf_size;
+ ssize_t len;
+
+ while (resid != 0) {
+ if ((len = write(fd, buf, resid)) <= 0)
+ return (ctf_set_errno(fp, errno));
+ resid -= len;
+ buf += len;
+ }
+
+ return (0);
+}
+
+/*
+ * Set the CTF library client version to the specified version. If version is
+ * zero, we just return the default library version number.
+ */
+int
+ctf_version(int version)
+{
+ if (version < 0) {
+ errno = EINVAL;
+ return (-1);
+ }
+
+ if (version > 0) {
+ if (version > CTF_VERSION) {
+ errno = ENOTSUP;
+ return (-1);
+ }
+ ctf_dprintf("ctf_version: client using version %d\n", version);
+ _libctf_version = version;
+ }
+
+ return (_libctf_version);
+}
diff --git a/libctf/ctf_lookup.c b/libctf/ctf_lookup.c
new file mode 100644
index 0000000..284279f
--- /dev/null
+++ b/libctf/ctf_lookup.c
@@ -0,0 +1,312 @@
+/*
+ * 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"
+
+/*
+ * Compare the given input string and length against a table of known C storage
+ * qualifier keywords. We just ignore these in ctf_lookup_by_name, below. To
+ * do this quickly, we use a pre-computed Perfect Hash Function similar to the
+ * technique originally described in the classic paper:
+ *
+ * R.J. Cichelli, "Minimal Perfect Hash Functions Made Simple",
+ * Communications of the ACM, Volume 23, Issue 1, January 1980, pp. 17-19.
+ *
+ * For an input string S of length N, we use hash H = S[N - 1] + N - 105, which
+ * for the current set of qualifiers yields a unique H in the range [0 .. 20].
+ * The hash can be modified when the keyword set changes as necessary. We also
+ * store the length of each keyword and check it prior to the final strcmp().
+ */
+static int
+isqualifier(const char *s, size_t len)
+{
+ static const struct qual {
+ const char *q_name;
+ size_t q_len;
+ } qhash[] = {
+ { "static", 6 }, { "", 0 }, { "", 0 }, { "", 0 },
+ { "volatile", 8 }, { "", 0 }, { "", 0 }, { "", 0 }, { "", 0 },
+ { "", 0 }, { "auto", 4 }, { "extern", 6 }, { "", 0 }, { "", 0 },
+ { "", 0 }, { "", 0 }, { "const", 5 }, { "register", 8 },
+ { "", 0 }, { "restrict", 8 }, { "_Restrict", 9 }
+ };
+
+ int h = s[len - 1] + (int)len - 105;
+ const struct qual *qp = &qhash[h];
+
+ return (h >= 0 && h < sizeof (qhash) / sizeof (qhash[0]) &&
+ len == qp->q_len && strncmp(qp->q_name, s, qp->q_len) == 0);
+}
+
+/*
+ * Attempt to convert the given C type name into the corresponding CTF type ID.
+ * It is not possible to do complete and proper conversion of type names
+ * without implementing a more full-fledged parser, which is necessary to
+ * handle things like types that are function pointers to functions that
+ * have arguments that are function pointers, and fun stuff like that.
+ * Instead, this function implements a very simple conversion algorithm that
+ * finds the things that we actually care about: structs, unions, enums,
+ * integers, floats, typedefs, and pointers to any of these named types.
+ */
+ctf_id_t
+ctf_lookup_by_name(ctf_file_t *fp, const char *name)
+{
+ static const char delimiters[] = " \t\n\r\v\f*";
+
+ const ctf_lookup_t *lp;
+ const ctf_helem_t *hp;
+ const char *p, *q, *end;
+ ctf_id_t type = 0;
+ ctf_id_t ntype, ptype;
+
+ if (name == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ for (p = name, end = name + strlen(name); *p != '\0'; p = q) {
+ while (isspace(*p))
+ p++; /* skip leading ws */
+
+ if (p == end)
+ break;
+
+ if ((q = strpbrk(p + 1, delimiters)) == NULL)
+ q = end; /* compare until end */
+
+ if (*p == '*') {
+ /*
+ * Find a pointer to type by looking in fp->ctf_ptrtab.
+ * If we can't find a pointer to the given type, see if
+ * we can compute a pointer to the type resulting from
+ * resolving the type down to its base type and use
+ * that instead. This helps with cases where the CTF
+ * data includes "struct foo *" but not "foo_t *" and
+ * the user tries to access "foo_t *" in the debugger.
+ */
+ ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)];
+ if (ntype == 0) {
+ ntype = ctf_type_resolve(fp, type);
+ if (ntype == CTF_ERR || (ntype = fp->ctf_ptrtab[
+ CTF_TYPE_TO_INDEX(ntype)]) == 0) {
+ (void) ctf_set_errno(fp, ECTF_NOTYPE);
+ goto err;
+ }
+ }
+
+ type = CTF_INDEX_TO_TYPE(ntype,
+ (fp->ctf_flags & LCTF_CHILD));
+
+ q = p + 1;
+ continue;
+ }
+
+ if (isqualifier(p, (size_t)(q - p)))
+ continue; /* skip qualifier keyword */
+
+ for (lp = fp->ctf_lookups; lp->ctl_prefix != NULL; lp++) {
+ if (lp->ctl_prefix[0] == '\0' ||
+ strncmp(p, lp->ctl_prefix, (size_t)(q - p)) == 0) {
+ for (p += lp->ctl_len; isspace(*p); p++)
+ continue; /* skip prefix and next ws */
+
+ if ((q = strchr(p, '*')) == NULL)
+ q = end; /* compare until end */
+
+ while (isspace(q[-1]))
+ q--; /* exclude trailing ws */
+
+ if ((hp = ctf_hash_lookup(lp->ctl_hash, fp, p,
+ (size_t)(q - p))) == NULL) {
+ (void) ctf_set_errno(fp, ECTF_NOTYPE);
+ goto err;
+ }
+
+ type = hp->h_type;
+ break;
+ }
+ }
+
+ if (lp->ctl_prefix == NULL) {
+ (void) ctf_set_errno(fp, ECTF_NOTYPE);
+ goto err;
+ }
+ }
+
+ if (*p != '\0' || type == 0)
+ return (ctf_set_errno(fp, ECTF_SYNTAX));
+
+ return (type);
+
+err:
+ if (fp->ctf_parent != NULL &&
+ (ptype = ctf_lookup_by_name(fp->ctf_parent, name)) != CTF_ERR)
+ return (ptype);
+
+ return (CTF_ERR);
+}
+
+/*
+ * Given a symbol table index, return the type of the data object described
+ * by the corresponding entry in the symbol table.
+ */
+ctf_id_t
+ctf_lookup_by_symbol(ctf_file_t *fp, ulong_t symidx)
+{
+ const ctf_sect_t *sp = &fp->ctf_symtab;
+ ctf_id_t type;
+
+ if (sp->cts_data == NULL)
+ return (ctf_set_errno(fp, ECTF_NOSYMTAB));
+
+ if (symidx >= fp->ctf_nsyms)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (sp->cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+ }
+
+ if (fp->ctf_sxlate[symidx] == -1u)
+ return (ctf_set_errno(fp, ECTF_NOTYPEDAT));
+
+ type = *(ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]);
+ if (type == 0)
+ return (ctf_set_errno(fp, ECTF_NOTYPEDAT));
+
+ return (type);
+}
+
+/*
+ * Return the pointer to the internal CTF type data corresponding to the
+ * given type ID. If the ID is invalid, the function returns NULL.
+ * This function is not exported outside of the library.
+ */
+const ctf_type_t *
+ctf_lookup_by_id(ctf_file_t **fpp, ctf_id_t type)
+{
+ ctf_file_t *fp = *fpp; /* caller passes in starting CTF container */
+
+ if ((fp->ctf_flags & LCTF_CHILD) && CTF_TYPE_ISPARENT(type) &&
+ (fp = fp->ctf_parent) == NULL) {
+ (void) ctf_set_errno(*fpp, ECTF_NOPARENT);
+ return (NULL);
+ }
+
+ type = CTF_TYPE_TO_INDEX(type);
+ if (type > 0 && type <= fp->ctf_typemax) {
+ *fpp = fp; /* function returns ending CTF container */
+ return (LCTF_INDEX_TO_TYPEPTR(fp, type));
+ }
+
+ (void) ctf_set_errno(fp, ECTF_BADID);
+ return (NULL);
+}
+
+/*
+ * Given a symbol table index, return the info for the function described
+ * by the corresponding entry in the symbol table.
+ */
+int
+ctf_func_info(ctf_file_t *fp, ulong_t symidx, ctf_funcinfo_t *fip)
+{
+ const ctf_sect_t *sp = &fp->ctf_symtab;
+ const ushort_t *dp;
+ ushort_t info, kind, n;
+
+ if (sp->cts_data == NULL)
+ return (ctf_set_errno(fp, ECTF_NOSYMTAB));
+
+ if (symidx >= fp->ctf_nsyms)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (sp->cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)sp->cts_data + symidx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
+ return (ctf_set_errno(fp, ECTF_NOTFUNC));
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)sp->cts_data + symidx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
+ return (ctf_set_errno(fp, ECTF_NOTFUNC));
+ }
+
+ if (fp->ctf_sxlate[symidx] == -1u)
+ return (ctf_set_errno(fp, ECTF_NOFUNCDAT));
+
+ dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]);
+
+ info = *dp++;
+ kind = LCTF_INFO_KIND(fp, info);
+ n = LCTF_INFO_VLEN(fp, info);
+
+ if (kind == CTF_K_UNKNOWN && n == 0)
+ return (ctf_set_errno(fp, ECTF_NOFUNCDAT));
+
+ if (kind != CTF_K_FUNCTION)
+ return (ctf_set_errno(fp, ECTF_CORRUPT));
+
+ fip->ctc_return = *dp++;
+ fip->ctc_argc = n;
+ fip->ctc_flags = 0;
+
+ if (n != 0 && dp[n - 1] == 0) {
+ fip->ctc_flags |= CTF_FUNC_VARARG;
+ fip->ctc_argc--;
+ }
+
+ return (0);
+}
+
+/*
+ * Given a symbol table index, return the arguments for the function described
+ * by the corresponding entry in the symbol table.
+ */
+int
+ctf_func_args(ctf_file_t *fp, ulong_t symidx, uint_t argc, ctf_id_t *argv)
+{
+ const ushort_t *dp;
+ ctf_funcinfo_t f;
+
+ if (ctf_func_info(fp, symidx, &f) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ /*
+ * The argument data is two ushort_t's past the translation table
+ * offset: one for the function info, and one for the return type.
+ */
+ dp = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[symidx]) + 2;
+
+ for (argc = MIN(argc, f.ctc_argc); argc != 0; argc--)
+ *argv++ = *dp++;
+
+ return (0);
+}
diff --git a/libctf/ctf_open.c b/libctf/ctf_open.c
new file mode 100644
index 0000000..129a084
--- /dev/null
+++ b/libctf/ctf_open.c
@@ -0,0 +1,952 @@
+/*
+ * 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 ctf_dmodel_t _libctf_models[] = {
+ { "ILP32", CTF_MODEL_ILP32, 4, 1, 2, 4, 4 },
+ { "LP64", CTF_MODEL_LP64, 8, 1, 2, 4, 8 },
+ { NULL, 0, 0, 0, 0, 0, 0 }
+};
+
+const char _CTF_SECTION[] = ".SUNW_ctf";
+const char _CTF_NULLSTR[] = "";
+
+int _libctf_version = CTF_VERSION; /* library client version */
+int _libctf_debug = 0; /* debugging messages enabled */
+
+static ushort_t
+get_kind_v1(ushort_t info)
+{
+ return (CTF_INFO_KIND_V1(info));
+}
+
+static ushort_t
+get_kind_v2(ushort_t info)
+{
+ return (CTF_INFO_KIND(info));
+}
+
+static ushort_t
+get_root_v1(ushort_t info)
+{
+ return (CTF_INFO_ISROOT_V1(info));
+}
+
+static ushort_t
+get_root_v2(ushort_t info)
+{
+ return (CTF_INFO_ISROOT(info));
+}
+
+static ushort_t
+get_vlen_v1(ushort_t info)
+{
+ return (CTF_INFO_VLEN_V1(info));
+}
+
+static ushort_t
+get_vlen_v2(ushort_t info)
+{
+ return (CTF_INFO_VLEN(info));
+}
+
+static const ctf_fileops_t ctf_fileops[] = {
+ { NULL, NULL },
+ { get_kind_v1, get_root_v1, get_vlen_v1 },
+ { get_kind_v2, get_root_v2, get_vlen_v2 },
+};
+
+/*
+ * Convert a 32-bit ELF symbol into GElf (Elf64) and return a pointer to it.
+ */
+static Elf64_Sym *
+sym_to_gelf(const Elf32_Sym *src, Elf64_Sym *dst)
+{
+ dst->st_name = src->st_name;
+ dst->st_value = src->st_value;
+ dst->st_size = src->st_size;
+ dst->st_info = src->st_info;
+ dst->st_other = src->st_other;
+ dst->st_shndx = src->st_shndx;
+
+ return (dst);
+}
+
+/*
+ * Initialize the symtab translation table by filling each entry with the
+ * offset of the CTF type or function data corresponding to each STT_FUNC or
+ * STT_OBJECT entry in the symbol table.
+ */
+static int
+init_symtab(ctf_file_t *fp, const ctf_header_t *hp,
+ const ctf_sect_t *sp, const ctf_sect_t *strp)
+{
+ const uchar_t *symp = sp->cts_data;
+ uint_t *xp = fp->ctf_sxlate;
+ uint_t *xend = xp + fp->ctf_nsyms;
+
+ uint_t objtoff = hp->cth_objtoff;
+ uint_t funcoff = hp->cth_funcoff;
+
+ ushort_t info, vlen;
+ Elf64_Sym sym, *gsp;
+ const char *name;
+
+ /*
+ * The CTF data object and function type sections are ordered to match
+ * the relative order of the respective symbol types in the symtab.
+ * If no type information is available for a symbol table entry, a
+ * pad is inserted in the CTF section. As a further optimization,
+ * anonymous or undefined symbols are omitted from the CTF data.
+ */
+ for (; xp < xend; xp++, symp += sp->cts_entsize) {
+ if (sp->cts_entsize == sizeof (Elf32_Sym))
+ gsp = sym_to_gelf((Elf32_Sym *)(uintptr_t)symp, &sym);
+ else
+ gsp = (Elf64_Sym *)(uintptr_t)symp;
+
+ if (gsp->st_name < strp->cts_size)
+ name = (const char *)strp->cts_data + gsp->st_name;
+ else
+ name = _CTF_NULLSTR;
+
+ if (gsp->st_name == 0 || gsp->st_shndx == SHN_UNDEF ||
+ strcmp(name, "_START_") == 0 ||
+ strcmp(name, "_END_") == 0) {
+ *xp = -1u;
+ continue;
+ }
+
+ switch (ELF64_ST_TYPE(gsp->st_info)) {
+ case STT_OBJECT:
+ if (objtoff >= hp->cth_funcoff ||
+ (gsp->st_shndx == SHN_ABS && gsp->st_value == 0)) {
+ *xp = -1u;
+ break;
+ }
+
+ *xp = objtoff;
+ objtoff += sizeof (ushort_t);
+ break;
+
+ case STT_FUNC:
+ if (funcoff >= hp->cth_typeoff) {
+ *xp = -1u;
+ break;
+ }
+
+ *xp = funcoff;
+
+ info = *(ushort_t *)((uintptr_t)fp->ctf_buf + funcoff);
+ vlen = LCTF_INFO_VLEN(fp, info);
+
+ /*
+ * If we encounter a zero pad at the end, just skip it.
+ * Otherwise skip over the function and its return type
+ * (+2) and the argument list (vlen).
+ */
+ if (LCTF_INFO_KIND(fp, info) == CTF_K_UNKNOWN &&
+ vlen == 0)
+ funcoff += sizeof (ushort_t); /* skip pad */
+ else
+ funcoff += sizeof (ushort_t) * (vlen + 2);
+ break;
+
+ default:
+ *xp = -1u;
+ break;
+ }
+ }
+
+ ctf_dprintf("loaded %lu symtab entries\n", fp->ctf_nsyms);
+ return (0);
+}
+
+/*
+ * Initialize the type ID translation table with the byte offset of each type,
+ * and initialize the hash tables of each named type.
+ */
+static int
+init_types(ctf_file_t *fp, const ctf_header_t *cth)
+{
+ /* LINTED - pointer alignment */
+ const ctf_type_t *tbuf = (ctf_type_t *)(fp->ctf_buf + cth->cth_typeoff);
+ /* LINTED - pointer alignment */
+ const ctf_type_t *tend = (ctf_type_t *)(fp->ctf_buf + cth->cth_stroff);
+
+ ulong_t pop[CTF_K_MAX + 1] = { 0 };
+ const ctf_type_t *tp;
+ ctf_hash_t *hp;
+ ushort_t id, dst;
+ uint_t *xp;
+
+ /*
+ * We initially determine whether the container is a child or a parent
+ * based on the value of cth_parname. To support containers that pre-
+ * date cth_parname, we also scan the types themselves for references
+ * to values in the range reserved for child types in our first pass.
+ */
+ int child = cth->cth_parname != 0;
+ int nlstructs = 0, nlunions = 0;
+ int err;
+
+ /*
+ * We make two passes through the entire type section. In this first
+ * pass, we count the number of each type and the total number of types.
+ */
+ for (tp = tbuf; tp < tend; fp->ctf_typemax++) {
+ ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+ ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info);
+ ssize_t size, increment;
+
+ size_t vbytes;
+ uint_t n;
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ vbytes = sizeof (uint_t);
+ break;
+ case CTF_K_ARRAY:
+ vbytes = sizeof (ctf_array_t);
+ break;
+ case CTF_K_FUNCTION:
+ vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ if (fp->ctf_version == CTF_VERSION_1 ||
+ size < CTF_LSTRUCT_THRESH) {
+ ctf_member_t *mp = (ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ vbytes = sizeof (ctf_member_t) * vlen;
+ for (n = vlen; n != 0; n--, mp++)
+ child |= CTF_TYPE_ISCHILD(mp->ctm_type);
+ } else {
+ ctf_lmember_t *lmp = (ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ vbytes = sizeof (ctf_lmember_t) * vlen;
+ for (n = vlen; n != 0; n--, lmp++)
+ child |=
+ CTF_TYPE_ISCHILD(lmp->ctlm_type);
+ }
+ break;
+ case CTF_K_ENUM:
+ vbytes = sizeof (ctf_enum_t) * vlen;
+ break;
+ case CTF_K_FORWARD:
+ /*
+ * For forward declarations, ctt_type is the CTF_K_*
+ * kind for the tag, so bump that population count too.
+ * If ctt_type is unknown, treat the tag as a struct.
+ */
+ if (tp->ctt_type == CTF_K_UNKNOWN ||
+ tp->ctt_type >= CTF_K_MAX)
+ pop[CTF_K_STRUCT]++;
+ else
+ pop[tp->ctt_type]++;
+ /*FALLTHRU*/
+ case CTF_K_UNKNOWN:
+ vbytes = 0;
+ break;
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ child |= CTF_TYPE_ISCHILD(tp->ctt_type);
+ vbytes = 0;
+ break;
+ default:
+ ctf_dprintf("detected invalid CTF kind -- %u\n", kind);
+ return (ECTF_CORRUPT);
+ }
+ tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
+ pop[kind]++;
+ }
+
+ /*
+ * If we detected a reference to a child type ID, then we know this
+ * container is a child and may have a parent's types imported later.
+ */
+ if (child) {
+ ctf_dprintf("CTF container %p is a child\n", (void *)fp);
+ fp->ctf_flags |= LCTF_CHILD;
+ } else
+ ctf_dprintf("CTF container %p is a parent\n", (void *)fp);
+
+ /*
+ * Now that we've counted up the number of each type, we can allocate
+ * the hash tables, type translation table, and pointer table.
+ */
+ if ((err = ctf_hash_create(&fp->ctf_structs, pop[CTF_K_STRUCT])) != 0)
+ return (err);
+
+ if ((err = ctf_hash_create(&fp->ctf_unions, pop[CTF_K_UNION])) != 0)
+ return (err);
+
+ if ((err = ctf_hash_create(&fp->ctf_enums, pop[CTF_K_ENUM])) != 0)
+ return (err);
+
+ if ((err = ctf_hash_create(&fp->ctf_names,
+ pop[CTF_K_INTEGER] + pop[CTF_K_FLOAT] + pop[CTF_K_FUNCTION] +
+ pop[CTF_K_TYPEDEF] + pop[CTF_K_POINTER] + pop[CTF_K_VOLATILE] +
+ pop[CTF_K_CONST] + pop[CTF_K_RESTRICT])) != 0)
+ return (err);
+
+ fp->ctf_txlate = ctf_alloc(sizeof (uint_t) * (fp->ctf_typemax + 1));
+ fp->ctf_ptrtab = ctf_alloc(sizeof (ushort_t) * (fp->ctf_typemax + 1));
+
+ if (fp->ctf_txlate == NULL || fp->ctf_ptrtab == NULL)
+ return (EAGAIN); /* memory allocation failed */
+
+ xp = fp->ctf_txlate;
+ *xp++ = 0; /* type id 0 is used as a sentinel value */
+
+ bzero(fp->ctf_txlate, sizeof (uint_t) * (fp->ctf_typemax + 1));
+ bzero(fp->ctf_ptrtab, sizeof (ushort_t) * (fp->ctf_typemax + 1));
+
+ /*
+ * In the second pass through the types, we fill in each entry of the
+ * type and pointer tables and add names to the appropriate hashes.
+ */
+ for (id = 1, tp = tbuf; tp < tend; xp++, id++) {
+ ushort_t kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+ ulong_t vlen = LCTF_INFO_VLEN(fp, tp->ctt_info);
+ ssize_t size, increment;
+
+ const char *name;
+ size_t vbytes;
+ ctf_helem_t *hep;
+ ctf_encoding_t cte;
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+ name = ctf_strptr(fp, tp->ctt_name);
+
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ /*
+ * Only insert a new integer base type definition if
+ * this type name has not been defined yet. We re-use
+ * the names with different encodings for bit-fields.
+ */
+ if ((hep = ctf_hash_lookup(&fp->ctf_names, fp,
+ name, strlen(name))) == NULL) {
+ err = ctf_hash_insert(&fp->ctf_names, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ } else if (ctf_type_encoding(fp, hep->h_type,
+ &cte) == 0 && cte.cte_bits == 0) {
+ /*
+ * Work-around SOS8 stabs bug: replace existing
+ * intrinsic w/ same name if it was zero bits.
+ */
+ hep->h_type = CTF_INDEX_TO_TYPE(id, child);
+ }
+ vbytes = sizeof (uint_t);
+ break;
+
+ case CTF_K_ARRAY:
+ vbytes = sizeof (ctf_array_t);
+ break;
+
+ case CTF_K_FUNCTION:
+ err = ctf_hash_insert(&fp->ctf_names, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ vbytes = sizeof (ushort_t) * (vlen + (vlen & 1));
+ break;
+
+ case CTF_K_STRUCT:
+ err = ctf_hash_define(&fp->ctf_structs, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+
+ if (fp->ctf_version == CTF_VERSION_1 ||
+ size < CTF_LSTRUCT_THRESH)
+ vbytes = sizeof (ctf_member_t) * vlen;
+ else {
+ vbytes = sizeof (ctf_lmember_t) * vlen;
+ nlstructs++;
+ }
+ break;
+
+ case CTF_K_UNION:
+ err = ctf_hash_define(&fp->ctf_unions, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+
+ if (fp->ctf_version == CTF_VERSION_1 ||
+ size < CTF_LSTRUCT_THRESH)
+ vbytes = sizeof (ctf_member_t) * vlen;
+ else {
+ vbytes = sizeof (ctf_lmember_t) * vlen;
+ nlunions++;
+ }
+ break;
+
+ case CTF_K_ENUM:
+ err = ctf_hash_define(&fp->ctf_enums, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+
+ vbytes = sizeof (ctf_enum_t) * vlen;
+ break;
+
+ case CTF_K_TYPEDEF:
+ err = ctf_hash_insert(&fp->ctf_names, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ vbytes = 0;
+ break;
+
+ case CTF_K_FORWARD:
+ /*
+ * Only insert forward tags into the given hash if the
+ * type or tag name is not already present.
+ */
+ switch (tp->ctt_type) {
+ case CTF_K_STRUCT:
+ hp = &fp->ctf_structs;
+ break;
+ case CTF_K_UNION:
+ hp = &fp->ctf_unions;
+ break;
+ case CTF_K_ENUM:
+ hp = &fp->ctf_enums;
+ break;
+ default:
+ hp = &fp->ctf_structs;
+ }
+
+ if (ctf_hash_lookup(hp, fp,
+ name, strlen(name)) == NULL) {
+ err = ctf_hash_insert(hp, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ }
+ vbytes = 0;
+ break;
+
+ case CTF_K_POINTER:
+ /*
+ * If the type referenced by the pointer is in this CTF
+ * container, then store the index of the pointer type
+ * in fp->ctf_ptrtab[ index of referenced type ].
+ */
+ if (CTF_TYPE_ISCHILD(tp->ctt_type) == child &&
+ CTF_TYPE_TO_INDEX(tp->ctt_type) <= fp->ctf_typemax)
+ fp->ctf_ptrtab[
+ CTF_TYPE_TO_INDEX(tp->ctt_type)] = id;
+ /*FALLTHRU*/
+
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ err = ctf_hash_insert(&fp->ctf_names, fp,
+ CTF_INDEX_TO_TYPE(id, child), tp->ctt_name);
+ if (err != 0 && err != ECTF_STRTAB)
+ return (err);
+ /*FALLTHRU*/
+
+ default:
+ vbytes = 0;
+ break;
+ }
+
+ *xp = (uint_t)((uintptr_t)tp - (uintptr_t)fp->ctf_buf);
+ tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes);
+ }
+
+ ctf_dprintf("%lu total types processed\n", fp->ctf_typemax);
+ ctf_dprintf("%u enum names hashed\n", ctf_hash_size(&fp->ctf_enums));
+ ctf_dprintf("%u struct names hashed (%d long)\n",
+ ctf_hash_size(&fp->ctf_structs), nlstructs);
+ ctf_dprintf("%u union names hashed (%d long)\n",
+ ctf_hash_size(&fp->ctf_unions), nlunions);
+ ctf_dprintf("%u base type names hashed\n",
+ ctf_hash_size(&fp->ctf_names));
+
+ /*
+ * Make an additional pass through the pointer table to find pointers
+ * that point to anonymous typedef nodes. If we find one, modify the
+ * pointer table so that the pointer is also known to point to the
+ * node that is referenced by the anonymous typedef node.
+ */
+ for (id = 1; id <= fp->ctf_typemax; id++) {
+ if ((dst = fp->ctf_ptrtab[id]) != 0) {
+ tp = LCTF_INDEX_TO_TYPEPTR(fp, id);
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_TYPEDEF &&
+ strcmp(ctf_strptr(fp, tp->ctt_name), "") == 0 &&
+ CTF_TYPE_ISCHILD(tp->ctt_type) == child &&
+ CTF_TYPE_TO_INDEX(tp->ctt_type) <= fp->ctf_typemax)
+ fp->ctf_ptrtab[
+ CTF_TYPE_TO_INDEX(tp->ctt_type)] = dst;
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Decode the specified CTF buffer and optional symbol table and create a new
+ * CTF container representing the symbolic debugging information. This code
+ * can be used directly by the debugger, or it can be used as the engine for
+ * ctf_fdopen() or ctf_open(), below.
+ */
+ctf_file_t *
+ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
+ const ctf_sect_t *strsect, int *errp)
+{
+ const ctf_preamble_t *pp;
+ ctf_header_t hp;
+ ctf_file_t *fp;
+ void *buf, *base;
+ size_t size, hdrsz;
+ int err;
+
+ if (ctfsect == NULL || ((symsect == NULL) != (strsect == NULL)))
+ return (ctf_set_open_errno(errp, EINVAL));
+
+ if (symsect != NULL && symsect->cts_entsize != sizeof (Elf32_Sym) &&
+ symsect->cts_entsize != sizeof (Elf64_Sym))
+ return (ctf_set_open_errno(errp, ECTF_SYMTAB));
+
+ if (symsect != NULL && symsect->cts_data == NULL)
+ return (ctf_set_open_errno(errp, ECTF_SYMBAD));
+
+ if (strsect != NULL && strsect->cts_data == NULL)
+ return (ctf_set_open_errno(errp, ECTF_STRBAD));
+
+ if (ctfsect->cts_size < sizeof (ctf_preamble_t))
+ return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
+
+ pp = (const ctf_preamble_t *)ctfsect->cts_data;
+
+ ctf_dprintf("ctf_bufopen: magic=0x%x version=%u\n",
+ pp->ctp_magic, pp->ctp_version);
+
+ /*
+ * Validate each part of the CTF header (either V1 or V2).
+ * First, we validate the preamble (common to all versions). At that
+ * point, we know specific header version, and can validate the
+ * version-specific parts including section offsets and alignments.
+ */
+ if (pp->ctp_magic != CTF_MAGIC)
+ return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
+
+ if (pp->ctp_version == CTF_VERSION_2) {
+ if (ctfsect->cts_size < sizeof (ctf_header_t))
+ return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
+
+ bcopy(ctfsect->cts_data, &hp, sizeof (hp));
+ hdrsz = sizeof (ctf_header_t);
+
+ } else if (pp->ctp_version == CTF_VERSION_1) {
+ const ctf_header_v1_t *h1p =
+ (const ctf_header_v1_t *)ctfsect->cts_data;
+
+ if (ctfsect->cts_size < sizeof (ctf_header_v1_t))
+ return (ctf_set_open_errno(errp, ECTF_NOCTFBUF));
+
+ bzero(&hp, sizeof (hp));
+ hp.cth_preamble = h1p->cth_preamble;
+ hp.cth_objtoff = h1p->cth_objtoff;
+ hp.cth_funcoff = h1p->cth_funcoff;
+ hp.cth_typeoff = h1p->cth_typeoff;
+ hp.cth_stroff = h1p->cth_stroff;
+ hp.cth_strlen = h1p->cth_strlen;
+
+ hdrsz = sizeof (ctf_header_v1_t);
+ } else
+ return (ctf_set_open_errno(errp, ECTF_CTFVERS));
+
+ size = hp.cth_stroff + hp.cth_strlen;
+
+ ctf_dprintf("ctf_bufopen: uncompressed size=%lu\n", (ulong_t)size);
+
+ if (hp.cth_lbloff > size || hp.cth_objtoff > size ||
+ hp.cth_funcoff > size || hp.cth_typeoff > size ||
+ hp.cth_stroff > size)
+ return (ctf_set_open_errno(errp, ECTF_CORRUPT));
+
+ if (hp.cth_lbloff > hp.cth_objtoff ||
+ hp.cth_objtoff > hp.cth_funcoff ||
+ hp.cth_funcoff > hp.cth_typeoff ||
+ hp.cth_typeoff > hp.cth_stroff)
+ return (ctf_set_open_errno(errp, ECTF_CORRUPT));
+
+ if ((hp.cth_lbloff & 3) || (hp.cth_objtoff & 1) ||
+ (hp.cth_funcoff & 1) || (hp.cth_typeoff & 3))
+ return (ctf_set_open_errno(errp, ECTF_CORRUPT));
+
+ /*
+ * Once everything is determined to be valid, attempt to decompress
+ * the CTF data buffer if it is compressed. Otherwise we just put
+ * the data section's buffer pointer into ctf_buf, below.
+ */
+ if (hp.cth_flags & CTF_F_COMPRESS) {
+ size_t srclen, dstlen;
+ const void *src;
+ int rc = Z_OK;
+
+ if (ctf_zopen(errp) == NULL)
+ return (NULL); /* errp is set for us */
+
+ if ((base = ctf_data_alloc(size + hdrsz)) == MAP_FAILED)
+ return (ctf_set_open_errno(errp, ECTF_ZALLOC));
+
+ bcopy(ctfsect->cts_data, base, hdrsz);
+ ((ctf_preamble_t *)base)->ctp_flags &= ~CTF_F_COMPRESS;
+ buf = (uchar_t *)base + hdrsz;
+
+ src = (uchar_t *)ctfsect->cts_data + hdrsz;
+ srclen = ctfsect->cts_size - hdrsz;
+ dstlen = size;
+
+ if ((rc = z_uncompress(buf, &dstlen, src, srclen)) != Z_OK) {
+ ctf_dprintf("zlib inflate err: %s\n", z_strerror(rc));
+ ctf_data_free(base, size + hdrsz);
+ return (ctf_set_open_errno(errp, ECTF_DECOMPRESS));
+ }
+
+ if (dstlen != size) {
+ ctf_dprintf("zlib inflate short -- got %lu of %lu "
+ "bytes\n", (ulong_t)dstlen, (ulong_t)size);
+ ctf_data_free(base, size + hdrsz);
+ return (ctf_set_open_errno(errp, ECTF_CORRUPT));
+ }
+
+ ctf_data_protect(base, size + hdrsz);
+
+ } else {
+ base = (void *)ctfsect->cts_data;
+ buf = (uchar_t *)base + hdrsz;
+ }
+
+ /*
+ * Once we have uncompressed and validated the CTF data buffer, we can
+ * proceed with allocating a ctf_file_t and initializing it.
+ */
+ if ((fp = ctf_alloc(sizeof (ctf_file_t))) == NULL)
+ return (ctf_set_open_errno(errp, EAGAIN));
+
+ bzero(fp, sizeof (ctf_file_t));
+ fp->ctf_version = hp.cth_version;
+ fp->ctf_fileops = &ctf_fileops[hp.cth_version];
+ bcopy(ctfsect, &fp->ctf_data, sizeof (ctf_sect_t));
+
+ if (symsect != NULL) {
+ bcopy(symsect, &fp->ctf_symtab, sizeof (ctf_sect_t));
+ bcopy(strsect, &fp->ctf_strtab, sizeof (ctf_sect_t));
+ }
+
+ if (fp->ctf_data.cts_name != NULL)
+ fp->ctf_data.cts_name = ctf_strdup(fp->ctf_data.cts_name);
+ if (fp->ctf_symtab.cts_name != NULL)
+ fp->ctf_symtab.cts_name = ctf_strdup(fp->ctf_symtab.cts_name);
+ if (fp->ctf_strtab.cts_name != NULL)
+ fp->ctf_strtab.cts_name = ctf_strdup(fp->ctf_strtab.cts_name);
+
+ if (fp->ctf_data.cts_name == NULL)
+ fp->ctf_data.cts_name = _CTF_NULLSTR;
+ if (fp->ctf_symtab.cts_name == NULL)
+ fp->ctf_symtab.cts_name = _CTF_NULLSTR;
+ if (fp->ctf_strtab.cts_name == NULL)
+ fp->ctf_strtab.cts_name = _CTF_NULLSTR;
+
+ fp->ctf_str[CTF_STRTAB_0].cts_strs = (const char *)buf + hp.cth_stroff;
+ fp->ctf_str[CTF_STRTAB_0].cts_len = hp.cth_strlen;
+
+ if (strsect != NULL) {
+ fp->ctf_str[CTF_STRTAB_1].cts_strs = strsect->cts_data;
+ fp->ctf_str[CTF_STRTAB_1].cts_len = strsect->cts_size;
+ }
+
+ fp->ctf_base = base;
+ fp->ctf_buf = buf;
+ fp->ctf_size = size + hdrsz;
+
+ /*
+ * If we have a parent container name and label, store the relocated
+ * string pointers in the CTF container for easy access later.
+ */
+ if (hp.cth_parlabel != 0)
+ fp->ctf_parlabel = ctf_strptr(fp, hp.cth_parlabel);
+ if (hp.cth_parname != 0)
+ fp->ctf_parname = ctf_strptr(fp, hp.cth_parname);
+
+ ctf_dprintf("ctf_bufopen: parent name %s (label %s)\n",
+ fp->ctf_parname ? fp->ctf_parname : "<NULL>",
+ fp->ctf_parlabel ? fp->ctf_parlabel : "<NULL>");
+
+ /*
+ * If we have a symbol table section, allocate and initialize
+ * the symtab translation table, pointed to by ctf_sxlate.
+ */
+ if (symsect != NULL) {
+ fp->ctf_nsyms = symsect->cts_size / symsect->cts_entsize;
+ fp->ctf_sxlate = ctf_alloc(fp->ctf_nsyms * sizeof (uint_t));
+
+ if (fp->ctf_sxlate == NULL) {
+ (void) ctf_set_open_errno(errp, EAGAIN);
+ goto bad;
+ }
+
+ if ((err = init_symtab(fp, &hp, symsect, strsect)) != 0) {
+ (void) ctf_set_open_errno(errp, err);
+ goto bad;
+ }
+ }
+
+ if ((err = init_types(fp, &hp)) != 0) {
+ (void) ctf_set_open_errno(errp, err);
+ goto bad;
+ }
+
+ /*
+ * Initialize the ctf_lookup_by_name top-level dictionary. We keep an
+ * array of type name prefixes and the corresponding ctf_hash to use.
+ * NOTE: This code must be kept in sync with the code in ctf_update().
+ */
+ fp->ctf_lookups[0].ctl_prefix = "struct";
+ fp->ctf_lookups[0].ctl_len = strlen(fp->ctf_lookups[0].ctl_prefix);
+ fp->ctf_lookups[0].ctl_hash = &fp->ctf_structs;
+ fp->ctf_lookups[1].ctl_prefix = "union";
+ fp->ctf_lookups[1].ctl_len = strlen(fp->ctf_lookups[1].ctl_prefix);
+ fp->ctf_lookups[1].ctl_hash = &fp->ctf_unions;
+ fp->ctf_lookups[2].ctl_prefix = "enum";
+ fp->ctf_lookups[2].ctl_len = strlen(fp->ctf_lookups[2].ctl_prefix);
+ fp->ctf_lookups[2].ctl_hash = &fp->ctf_enums;
+ fp->ctf_lookups[3].ctl_prefix = _CTF_NULLSTR;
+ fp->ctf_lookups[3].ctl_len = strlen(fp->ctf_lookups[3].ctl_prefix);
+ fp->ctf_lookups[3].ctl_hash = &fp->ctf_names;
+ fp->ctf_lookups[4].ctl_prefix = NULL;
+ fp->ctf_lookups[4].ctl_len = 0;
+ fp->ctf_lookups[4].ctl_hash = NULL;
+
+ if (symsect != NULL) {
+ if (symsect->cts_entsize == sizeof (Elf64_Sym))
+ (void) ctf_setmodel(fp, CTF_MODEL_LP64);
+ else
+ (void) ctf_setmodel(fp, CTF_MODEL_ILP32);
+ } else
+ (void) ctf_setmodel(fp, CTF_MODEL_NATIVE);
+
+ fp->ctf_refcnt = 1;
+ return (fp);
+
+bad:
+ ctf_close(fp);
+ return (NULL);
+}
+
+/*
+ * Close the specified CTF container and free associated data structures. Note
+ * that ctf_close() is a reference counted operation: if the specified file is
+ * the parent of other active containers, its reference count will be greater
+ * than one and it will be freed later when no active children exist.
+ */
+void
+ctf_close(ctf_file_t *fp)
+{
+ ctf_dtdef_t *dtd, *ntd;
+
+ if (fp == NULL)
+ return; /* allow ctf_close(NULL) to simplify caller code */
+
+ ctf_dprintf("ctf_close(%p) refcnt=%u\n", (void *)fp, fp->ctf_refcnt);
+
+ if (fp->ctf_refcnt > 1) {
+ fp->ctf_refcnt--;
+ return;
+ }
+
+ if (fp->ctf_parent != NULL)
+ ctf_close(fp->ctf_parent);
+
+ for (dtd = ctf_list_next(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) {
+ ntd = ctf_list_next(dtd);
+ ctf_dtd_delete(fp, dtd);
+ }
+
+ ctf_free(fp->ctf_dthash, fp->ctf_dthashlen * sizeof (ctf_dtdef_t *));
+
+ if (fp->ctf_flags & LCTF_MMAP) {
+ if (fp->ctf_data.cts_data != NULL)
+ ctf_sect_munmap(&fp->ctf_data);
+ if (fp->ctf_symtab.cts_data != NULL)
+ ctf_sect_munmap(&fp->ctf_symtab);
+ if (fp->ctf_strtab.cts_data != NULL)
+ ctf_sect_munmap(&fp->ctf_strtab);
+ }
+
+ if (fp->ctf_data.cts_name != _CTF_NULLSTR &&
+ fp->ctf_data.cts_name != NULL) {
+ ctf_free((char *)fp->ctf_data.cts_name,
+ strlen(fp->ctf_data.cts_name) + 1);
+ }
+
+ if (fp->ctf_symtab.cts_name != _CTF_NULLSTR &&
+ fp->ctf_symtab.cts_name != NULL) {
+ ctf_free((char *)fp->ctf_symtab.cts_name,
+ strlen(fp->ctf_symtab.cts_name) + 1);
+ }
+
+ if (fp->ctf_strtab.cts_name != _CTF_NULLSTR &&
+ fp->ctf_strtab.cts_name != NULL) {
+ ctf_free((char *)fp->ctf_strtab.cts_name,
+ strlen(fp->ctf_strtab.cts_name) + 1);
+ }
+
+ if (fp->ctf_base != fp->ctf_data.cts_data && fp->ctf_base != NULL)
+ ctf_data_free((void *)fp->ctf_base, fp->ctf_size);
+
+ if (fp->ctf_sxlate != NULL)
+ ctf_free(fp->ctf_sxlate, sizeof (uint_t) * fp->ctf_nsyms);
+
+ if (fp->ctf_txlate != NULL) {
+ ctf_free(fp->ctf_txlate,
+ sizeof (uint_t) * (fp->ctf_typemax + 1));
+ }
+
+ if (fp->ctf_ptrtab != NULL) {
+ ctf_free(fp->ctf_ptrtab,
+ sizeof (ushort_t) * (fp->ctf_typemax + 1));
+ }
+
+ ctf_hash_destroy(&fp->ctf_structs);
+ ctf_hash_destroy(&fp->ctf_unions);
+ ctf_hash_destroy(&fp->ctf_enums);
+ ctf_hash_destroy(&fp->ctf_names);
+
+ ctf_free(fp, sizeof (ctf_file_t));
+}
+
+/*
+ * Return the CTF handle for the parent CTF container, if one exists.
+ * Otherwise return NULL to indicate this container has no imported parent.
+ */
+ctf_file_t *
+ctf_parent_file(ctf_file_t *fp)
+{
+ return (fp->ctf_parent);
+}
+
+/*
+ * Return the name of the parent CTF container, if one exists. Otherwise
+ * return NULL to indicate this container is a root container.
+ */
+const char *
+ctf_parent_name(ctf_file_t *fp)
+{
+ return (fp->ctf_parname);
+}
+
+/*
+ * Import the types from the specified parent container by storing a pointer
+ * to it in ctf_parent and incrementing its reference count. Only one parent
+ * is allowed: if a parent already exists, it is replaced by the new parent.
+ */
+int
+ctf_import(ctf_file_t *fp, ctf_file_t *pfp)
+{
+ if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0))
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel)
+ return (ctf_set_errno(fp, ECTF_DMODEL));
+
+ if (fp->ctf_parent != NULL)
+ ctf_close(fp->ctf_parent);
+
+ if (pfp != NULL) {
+ fp->ctf_flags |= LCTF_CHILD;
+ pfp->ctf_refcnt++;
+ }
+
+ fp->ctf_parent = pfp;
+ return (0);
+}
+
+/*
+ * Set the data model constant for the CTF container.
+ */
+int
+ctf_setmodel(ctf_file_t *fp, int model)
+{
+ const ctf_dmodel_t *dp;
+
+ for (dp = _libctf_models; dp->ctd_name != NULL; dp++) {
+ if (dp->ctd_code == model) {
+ fp->ctf_dmodel = dp;
+ return (0);
+ }
+ }
+
+ return (ctf_set_errno(fp, EINVAL));
+}
+
+/*
+ * Return the data model constant for the CTF container.
+ */
+int
+ctf_getmodel(ctf_file_t *fp)
+{
+ return (fp->ctf_dmodel->ctd_code);
+}
+
+void
+ctf_setspecific(ctf_file_t *fp, void *data)
+{
+ fp->ctf_specific = data;
+}
+
+void *
+ctf_getspecific(ctf_file_t *fp)
+{
+ return (fp->ctf_specific);
+}
diff --git a/libctf/ctf_subr.c b/libctf/ctf_subr.c
new file mode 100644
index 0000000..5f574c0
--- /dev/null
+++ b/libctf/ctf_subr.c
@@ -0,0 +1,81 @@
+/*
+ * 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"
+
+void *
+ctf_data_alloc(size_t size)
+{
+ return (mmap(NULL, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANON, -1, 0));
+}
+
+void
+ctf_data_free(void *buf, size_t size)
+{
+ (void) munmap(buf, size);
+}
+
+void
+ctf_data_protect(void *buf, size_t size)
+{
+ (void) mprotect(buf, size, PROT_READ);
+}
+
+void *
+ctf_alloc(size_t size)
+{
+ return (malloc(size));
+}
+
+/*ARGSUSED*/
+void
+ctf_free(void *buf, __unused size_t size)
+{
+ free(buf);
+}
+
+const char *
+ctf_strerror(int err)
+{
+ return ((const char *) strerror(err));
+}
+
+/*PRINTFLIKE1*/
+void
+ctf_dprintf(const char *format, ...)
+{
+ if (_libctf_debug) {
+ va_list alist;
+
+ va_start(alist, format);
+ (void) fputs("libctf DEBUG: ", stderr);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+ }
+}
diff --git a/libctf/ctf_types.c b/libctf/ctf_types.c
new file mode 100644
index 0000000..94983bf
--- /dev/null
+++ b/libctf/ctf_types.c
@@ -0,0 +1,845 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ctf_impl.h"
+
+ssize_t
+ctf_get_ctt_size(const ctf_file_t *fp, const ctf_type_t *tp, ssize_t *sizep,
+ ssize_t *incrementp)
+{
+ ssize_t size, increment;
+
+ if (fp->ctf_version > CTF_VERSION_1 &&
+ tp->ctt_size == CTF_LSIZE_SENT) {
+ size = CTF_TYPE_LSIZE(tp);
+ increment = sizeof (ctf_type_t);
+ } else {
+ size = tp->ctt_size;
+ increment = sizeof (ctf_stype_t);
+ }
+
+ if (sizep)
+ *sizep = size;
+ if (incrementp)
+ *incrementp = increment;
+
+ return (size);
+}
+
+/*
+ * Iterate over the members of a STRUCT or UNION. We pass the name, member
+ * type, and offset of each member to the specified callback function.
+ */
+int
+ctf_member_iter(ctf_file_t *fp, ctf_id_t type, ctf_member_f *func, void *arg)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ ssize_t size, increment;
+ uint_t kind, n;
+ int rc;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+ kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (ctf_set_errno(ofp, ECTF_NOTSOU));
+
+ if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
+ const ctf_member_t *mp = (const ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
+ const char *name = ctf_strptr(fp, mp->ctm_name);
+ if ((rc = func(name, mp->ctm_type, mp->ctm_offset,
+ arg)) != 0)
+ return (rc);
+ }
+
+ } else {
+ const ctf_lmember_t *lmp = (const ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
+ const char *name = ctf_strptr(fp, lmp->ctlm_name);
+ if ((rc = func(name, lmp->ctlm_type,
+ (ulong_t)CTF_LMEM_OFFSET(lmp), arg)) != 0)
+ return (rc);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Iterate over the members of an ENUM. We pass the string name and associated
+ * integer value of each enum element to the specified callback function.
+ */
+int
+ctf_enum_iter(ctf_file_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ctf_enum_t *ep;
+ ssize_t increment;
+ uint_t n;
+ int rc;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM)
+ return (ctf_set_errno(ofp, ECTF_NOTENUM));
+
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+
+ ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
+ const char *name = ctf_strptr(fp, ep->cte_name);
+ if ((rc = func(name, ep->cte_value, arg)) != 0)
+ return (rc);
+ }
+
+ return (0);
+}
+
+/*
+ * Iterate over every root (user-visible) type in the given CTF container.
+ * We pass the type ID of each type to the specified callback function.
+ */
+int
+ctf_type_iter(ctf_file_t *fp, ctf_type_f *func, void *arg)
+{
+ ctf_id_t id, max = fp->ctf_typemax;
+ int rc, child = (fp->ctf_flags & LCTF_CHILD);
+
+ for (id = 1; id <= max; id++) {
+ const ctf_type_t *tp = LCTF_INDEX_TO_TYPEPTR(fp, id);
+ if (CTF_INFO_ISROOT(tp->ctt_info) &&
+ (rc = func(CTF_INDEX_TO_TYPE(id, child), arg)) != 0)
+ return (rc);
+ }
+
+ return (0);
+}
+
+/*
+ * Follow a given type through the graph for TYPEDEF, VOLATILE, CONST, and
+ * RESTRICT nodes until we reach a "base" type node. This is useful when
+ * we want to follow a type ID to a node that has members or a size. To guard
+ * against infinite loops, we implement simplified cycle detection and check
+ * each link against itself, the previous node, and the topmost node.
+ */
+ctf_id_t
+ctf_type_resolve(ctf_file_t *fp, ctf_id_t type)
+{
+ ctf_id_t prev = type, otype = type;
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+
+ while ((tp = ctf_lookup_by_id(&fp, type)) != NULL) {
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ if (tp->ctt_type == type || tp->ctt_type == otype ||
+ tp->ctt_type == prev) {
+ ctf_dprintf("type %ld cycle detected\n", otype);
+ return (ctf_set_errno(ofp, ECTF_CORRUPT));
+ }
+ prev = type;
+ type = tp->ctt_type;
+ break;
+ default:
+ return (type);
+ }
+ }
+
+ return (CTF_ERR); /* errno is set for us */
+}
+
+/*
+ * Lookup the given type ID and print a string name for it into buf. Return
+ * the actual number of bytes (not including \0) needed to format the name.
+ */
+ssize_t
+ctf_type_lname(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
+{
+ ctf_decl_t cd;
+ ctf_decl_node_t *cdp;
+ ctf_decl_prec_t prec, lp, rp;
+ int ptr, arr;
+ uint_t k;
+
+ if (fp == NULL && type == CTF_ERR)
+ return (-1); /* simplify caller code by permitting CTF_ERR */
+
+ ctf_decl_init(&cd, buf, len);
+ ctf_decl_push(&cd, fp, type);
+
+ if (cd.cd_err != 0) {
+ ctf_decl_fini(&cd);
+ return (ctf_set_errno(fp, cd.cd_err));
+ }
+
+ /*
+ * If the type graph's order conflicts with lexical precedence order
+ * for pointers or arrays, then we need to surround the declarations at
+ * the corresponding lexical precedence with parentheses. This can
+ * result in either a parenthesized pointer (*) as in int (*)() or
+ * int (*)[], or in a parenthesized pointer and array as in int (*[])().
+ */
+ ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER;
+ arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY;
+
+ rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1;
+ lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1;
+
+ k = CTF_K_POINTER; /* avoid leading whitespace (see below) */
+
+ for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) {
+ for (cdp = ctf_list_next(&cd.cd_nodes[prec]);
+ cdp != NULL; cdp = ctf_list_next(cdp)) {
+
+ ctf_file_t *rfp = fp;
+ const ctf_type_t *tp =
+ ctf_lookup_by_id(&rfp, cdp->cd_type);
+ const char *name = ctf_strptr(rfp, tp->ctt_name);
+
+ if (k != CTF_K_POINTER && k != CTF_K_ARRAY)
+ ctf_decl_sprintf(&cd, " ");
+
+ if (lp == prec) {
+ ctf_decl_sprintf(&cd, "(");
+ lp = -1;
+ }
+
+ switch (cdp->cd_kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ case CTF_K_TYPEDEF:
+ ctf_decl_sprintf(&cd, "%s", name);
+ break;
+ case CTF_K_POINTER:
+ ctf_decl_sprintf(&cd, "*");
+ break;
+ case CTF_K_ARRAY:
+ ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n);
+ break;
+ case CTF_K_FUNCTION:
+ ctf_decl_sprintf(&cd, "()");
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_FORWARD:
+ ctf_decl_sprintf(&cd, "struct %s", name);
+ break;
+ case CTF_K_UNION:
+ ctf_decl_sprintf(&cd, "union %s", name);
+ break;
+ case CTF_K_ENUM:
+ ctf_decl_sprintf(&cd, "enum %s", name);
+ break;
+ case CTF_K_VOLATILE:
+ ctf_decl_sprintf(&cd, "volatile");
+ break;
+ case CTF_K_CONST:
+ ctf_decl_sprintf(&cd, "const");
+ break;
+ case CTF_K_RESTRICT:
+ ctf_decl_sprintf(&cd, "restrict");
+ break;
+ }
+
+ k = cdp->cd_kind;
+ }
+
+ if (rp == prec)
+ ctf_decl_sprintf(&cd, ")");
+ }
+
+ if (cd.cd_len >= len)
+ (void) ctf_set_errno(fp, ECTF_NAMELEN);
+
+ ctf_decl_fini(&cd);
+ return (cd.cd_len);
+}
+
+/*
+ * Lookup the given type ID and print a string name for it into buf. If buf
+ * is too small, return NULL: the ECTF_NAMELEN error is set on 'fp' for us.
+ */
+char *
+ctf_type_name(ctf_file_t *fp, ctf_id_t type, char *buf, size_t len)
+{
+ ssize_t rv = ctf_type_lname(fp, type, buf, len);
+ return (rv >= 0 && rv < len ? buf : NULL);
+}
+
+/*
+ * Resolve the type down to a base type node, and then return the size
+ * of the type storage in bytes.
+ */
+ssize_t
+ctf_type_size(ctf_file_t *fp, ctf_id_t type)
+{
+ const ctf_type_t *tp;
+ ssize_t size;
+ ctf_arinfo_t ar;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (-1); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (-1); /* errno is set for us */
+
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_POINTER:
+ return (fp->ctf_dmodel->ctd_pointer);
+
+ case CTF_K_FUNCTION:
+ return (0); /* function size is only known by symtab */
+
+ case CTF_K_ENUM:
+ return (fp->ctf_dmodel->ctd_int);
+
+ case CTF_K_ARRAY:
+ /*
+ * Array size is not directly returned by stabs data. Instead,
+ * it defines the element type and requires the user to perform
+ * the multiplication. If ctf_get_ctt_size() returns zero, the
+ * current version of ctfconvert does not compute member sizes
+ * and we compute the size here on its behalf.
+ */
+ if ((size = ctf_get_ctt_size(fp, tp, NULL, NULL)) > 0)
+ return (size);
+
+ if (ctf_array_info(fp, type, &ar) == CTF_ERR ||
+ (size = ctf_type_size(fp, ar.ctr_contents)) == CTF_ERR)
+ return (-1); /* errno is set for us */
+
+ return (size * ar.ctr_nelems);
+
+ default:
+ return (ctf_get_ctt_size(fp, tp, NULL, NULL));
+ }
+}
+
+/*
+ * Resolve the type down to a base type node, and then return the alignment
+ * needed for the type storage in bytes.
+ */
+ssize_t
+ctf_type_align(ctf_file_t *fp, ctf_id_t type)
+{
+ const ctf_type_t *tp;
+ ctf_arinfo_t r;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (-1); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (-1); /* errno is set for us */
+
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_POINTER:
+ case CTF_K_FUNCTION:
+ return (fp->ctf_dmodel->ctd_pointer);
+
+ case CTF_K_ARRAY:
+ if (ctf_array_info(fp, type, &r) == CTF_ERR)
+ return (-1); /* errno is set for us */
+ return (ctf_type_align(fp, r.ctr_contents));
+
+ case CTF_K_STRUCT:
+ case CTF_K_UNION: {
+ uint_t n = LCTF_INFO_VLEN(fp, tp->ctt_info);
+ ssize_t size, increment;
+ size_t align = 0;
+ const void *vmp;
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+ vmp = (uchar_t *)tp + increment;
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) == CTF_K_STRUCT)
+ n = MIN(n, 1); /* only use first member for structs */
+
+ if (fp->ctf_version == CTF_VERSION_1 ||
+ size < CTF_LSTRUCT_THRESH) {
+ const ctf_member_t *mp = vmp;
+ for (; n != 0; n--, mp++) {
+ ssize_t am = ctf_type_align(fp, mp->ctm_type);
+ align = MAX(align, am);
+ }
+ } else {
+ const ctf_lmember_t *lmp = vmp;
+ for (; n != 0; n--, lmp++) {
+ ssize_t am = ctf_type_align(fp, lmp->ctlm_type);
+ align = MAX(align, am);
+ }
+ }
+
+ return (align);
+ }
+
+ case CTF_K_ENUM:
+ return (fp->ctf_dmodel->ctd_int);
+
+ default:
+ return (ctf_get_ctt_size(fp, tp, NULL, NULL));
+ }
+}
+
+/*
+ * Return the kind (CTF_K_* constant) for the specified type ID.
+ */
+int
+ctf_type_kind(ctf_file_t *fp, ctf_id_t type)
+{
+ const ctf_type_t *tp;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ return (LCTF_INFO_KIND(fp, tp->ctt_info));
+}
+
+/*
+ * If the type is one that directly references another type (such as POINTER),
+ * then return the ID of the type to which it refers.
+ */
+ctf_id_t
+ctf_type_reference(ctf_file_t *fp, ctf_id_t type)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_POINTER:
+ case CTF_K_TYPEDEF:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ return (tp->ctt_type);
+ default:
+ return (ctf_set_errno(ofp, ECTF_NOTREF));
+ }
+}
+
+/*
+ * Find a pointer to type by looking in fp->ctf_ptrtab. If we can't find a
+ * pointer to the given type, see if we can compute a pointer to the type
+ * resulting from resolving the type down to its base type and use that
+ * instead. This helps with cases where the CTF data includes "struct foo *"
+ * but not "foo_t *" and the user accesses "foo_t *" in the debugger.
+ */
+ctf_id_t
+ctf_type_pointer(ctf_file_t *fp, ctf_id_t type)
+{
+ ctf_file_t *ofp = fp;
+ ctf_id_t ntype;
+
+ if (ctf_lookup_by_id(&fp, type) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]) != 0)
+ return (CTF_INDEX_TO_TYPE(ntype, (fp->ctf_flags & LCTF_CHILD)));
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (ctf_set_errno(ofp, ECTF_NOTYPE));
+
+ if (ctf_lookup_by_id(&fp, type) == NULL)
+ return (ctf_set_errno(ofp, ECTF_NOTYPE));
+
+ if ((ntype = fp->ctf_ptrtab[CTF_TYPE_TO_INDEX(type)]) != 0)
+ return (CTF_INDEX_TO_TYPE(ntype, (fp->ctf_flags & LCTF_CHILD)));
+
+ return (ctf_set_errno(ofp, ECTF_NOTYPE));
+}
+
+/*
+ * Return the encoding for the specified INTEGER or FLOAT.
+ */
+int
+ctf_type_encoding(ctf_file_t *fp, ctf_id_t type, ctf_encoding_t *ep)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ ssize_t increment;
+ uint_t data;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+
+ switch (LCTF_INFO_KIND(fp, tp->ctt_info)) {
+ case CTF_K_INTEGER:
+ data = *(const uint_t *)((uintptr_t)tp + increment);
+ ep->cte_format = CTF_INT_ENCODING(data);
+ ep->cte_offset = CTF_INT_OFFSET(data);
+ ep->cte_bits = CTF_INT_BITS(data);
+ break;
+ case CTF_K_FLOAT:
+ data = *(const uint_t *)((uintptr_t)tp + increment);
+ ep->cte_format = CTF_FP_ENCODING(data);
+ ep->cte_offset = CTF_FP_OFFSET(data);
+ ep->cte_bits = CTF_FP_BITS(data);
+ break;
+ default:
+ return (ctf_set_errno(ofp, ECTF_NOTINTFP));
+ }
+
+ return (0);
+}
+
+int
+ctf_type_cmp(ctf_file_t *lfp, ctf_id_t ltype, ctf_file_t *rfp, ctf_id_t rtype)
+{
+ int rval;
+
+ if (ltype < rtype)
+ rval = -1;
+ else if (ltype > rtype)
+ rval = 1;
+ else
+ rval = 0;
+
+ if (lfp == rfp)
+ return (rval);
+
+ if (CTF_TYPE_ISPARENT(ltype) && lfp->ctf_parent != NULL)
+ lfp = lfp->ctf_parent;
+
+ if (CTF_TYPE_ISPARENT(rtype) && rfp->ctf_parent != NULL)
+ rfp = rfp->ctf_parent;
+
+ if (lfp < rfp)
+ return (-1);
+
+ if (lfp > rfp)
+ return (1);
+
+ return (rval);
+}
+
+/*
+ * Return a boolean value indicating if two types are compatible integers or
+ * floating-pointer values. This function returns true if the two types are
+ * the same, or if they have the same ASCII name and encoding properties.
+ * This function could be extended to test for compatibility for other kinds.
+ */
+int
+ctf_type_compat(ctf_file_t *lfp, ctf_id_t ltype,
+ ctf_file_t *rfp, ctf_id_t rtype)
+{
+ const ctf_type_t *ltp, *rtp;
+ ctf_encoding_t le, re;
+ ctf_arinfo_t la, ra;
+ uint_t lkind, rkind;
+
+ if (ctf_type_cmp(lfp, ltype, rfp, rtype) == 0)
+ return (1);
+
+ ltype = ctf_type_resolve(lfp, ltype);
+ lkind = ctf_type_kind(lfp, ltype);
+
+ rtype = ctf_type_resolve(rfp, rtype);
+ rkind = ctf_type_kind(rfp, rtype);
+
+ if (lkind != rkind ||
+ (ltp = ctf_lookup_by_id(&lfp, ltype)) == NULL ||
+ (rtp = ctf_lookup_by_id(&rfp, rtype)) == NULL ||
+ strcmp(ctf_strptr(lfp, ltp->ctt_name),
+ ctf_strptr(rfp, rtp->ctt_name)) != 0)
+ return (0);
+
+ switch (lkind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ return (ctf_type_encoding(lfp, ltype, &le) == 0 &&
+ ctf_type_encoding(rfp, rtype, &re) == 0 &&
+ bcmp(&le, &re, sizeof (ctf_encoding_t)) == 0);
+ case CTF_K_POINTER:
+ return (ctf_type_compat(lfp, ctf_type_reference(lfp, ltype),
+ rfp, ctf_type_reference(rfp, rtype)));
+ case CTF_K_ARRAY:
+ return (ctf_array_info(lfp, ltype, &la) == 0 &&
+ ctf_array_info(rfp, rtype, &ra) == 0 &&
+ la.ctr_nelems == ra.ctr_nelems && ctf_type_compat(
+ lfp, la.ctr_contents, rfp, ra.ctr_contents) &&
+ ctf_type_compat(lfp, la.ctr_index, rfp, ra.ctr_index));
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ return (ctf_type_size(lfp, ltype) == ctf_type_size(rfp, rtype));
+ case CTF_K_ENUM:
+ case CTF_K_FORWARD:
+ return (1); /* no other checks required for these type kinds */
+ default:
+ return (0); /* should not get here since we did a resolve */
+ }
+}
+
+/*
+ * Return the type and offset for a given member of a STRUCT or UNION.
+ */
+int
+ctf_member_info(ctf_file_t *fp, ctf_id_t type, const char *name,
+ ctf_membinfo_t *mip)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ ssize_t size, increment;
+ uint_t kind, n;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+ kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (ctf_set_errno(ofp, ECTF_NOTSOU));
+
+ if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
+ const ctf_member_t *mp = (const ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
+ if (strcmp(ctf_strptr(fp, mp->ctm_name), name) == 0) {
+ mip->ctm_type = mp->ctm_type;
+ mip->ctm_offset = mp->ctm_offset;
+ return (0);
+ }
+ }
+ } else {
+ const ctf_lmember_t *lmp = (const ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
+ if (strcmp(ctf_strptr(fp, lmp->ctlm_name), name) == 0) {
+ mip->ctm_type = lmp->ctlm_type;
+ mip->ctm_offset = (ulong_t)CTF_LMEM_OFFSET(lmp);
+ return (0);
+ }
+ }
+ }
+
+ return (ctf_set_errno(ofp, ECTF_NOMEMBNAM));
+}
+
+/*
+ * Return the array type, index, and size information for the specified ARRAY.
+ */
+int
+ctf_array_info(ctf_file_t *fp, ctf_id_t type, ctf_arinfo_t *arp)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ctf_array_t *ap;
+ ssize_t increment;
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ARRAY)
+ return (ctf_set_errno(ofp, ECTF_NOTARRAY));
+
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+
+ ap = (const ctf_array_t *)((uintptr_t)tp + increment);
+ arp->ctr_contents = ap->cta_contents;
+ arp->ctr_index = ap->cta_index;
+ arp->ctr_nelems = ap->cta_nelems;
+
+ return (0);
+}
+
+/*
+ * Convert the specified value to the corresponding enum member name, if a
+ * matching name can be found. Otherwise NULL is returned.
+ */
+const char *
+ctf_enum_name(ctf_file_t *fp, ctf_id_t type, int value)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ctf_enum_t *ep;
+ ssize_t increment;
+ uint_t n;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (NULL); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (NULL); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) {
+ (void) ctf_set_errno(ofp, ECTF_NOTENUM);
+ return (NULL);
+ }
+
+ (void) ctf_get_ctt_size(fp, tp, NULL, &increment);
+
+ ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
+ if (ep->cte_value == value)
+ return (ctf_strptr(fp, ep->cte_name));
+ }
+
+ (void) ctf_set_errno(ofp, ECTF_NOENUMNAM);
+ return (NULL);
+}
+
+/*
+ * Convert the specified enum tag name to the corresponding value, if a
+ * matching name can be found. Otherwise CTF_ERR is returned.
+ */
+int
+ctf_enum_value(ctf_file_t *fp, ctf_id_t type, const char *name, int *valp)
+{
+ ctf_file_t *ofp = fp;
+ const ctf_type_t *tp;
+ const ctf_enum_t *ep;
+ ssize_t size, increment;
+ uint_t n;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (LCTF_INFO_KIND(fp, tp->ctt_info) != CTF_K_ENUM) {
+ (void) ctf_set_errno(ofp, ECTF_NOTENUM);
+ return (CTF_ERR);
+ }
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+
+ ep = (const ctf_enum_t *)((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, ep++) {
+ if (strcmp(ctf_strptr(fp, ep->cte_name), name) == 0) {
+ if (valp != NULL)
+ *valp = ep->cte_value;
+ return (0);
+ }
+ }
+
+ (void) ctf_set_errno(ofp, ECTF_NOENUMNAM);
+ return (CTF_ERR);
+}
+
+/*
+ * Recursively visit the members of any type. This function is used as the
+ * engine for ctf_type_visit, below. We resolve the input type, recursively
+ * invoke ourself for each type member if the type is a struct or union, and
+ * then invoke the callback function on the current type. If any callback
+ * returns non-zero, we abort and percolate the error code back up to the top.
+ */
+static int
+ctf_type_rvisit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg,
+ const char *name, ulong_t offset, int depth)
+{
+ ctf_id_t otype = type;
+ const ctf_type_t *tp;
+ ssize_t size, increment;
+ uint_t kind, n;
+ int rc;
+
+ if ((type = ctf_type_resolve(fp, type)) == CTF_ERR)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((tp = ctf_lookup_by_id(&fp, type)) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if ((rc = func(name, otype, offset, depth, arg)) != 0)
+ return (rc);
+
+ kind = LCTF_INFO_KIND(fp, tp->ctt_info);
+
+ if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
+ return (0);
+
+ (void) ctf_get_ctt_size(fp, tp, &size, &increment);
+
+ if (fp->ctf_version == CTF_VERSION_1 || size < CTF_LSTRUCT_THRESH) {
+ const ctf_member_t *mp = (const ctf_member_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, mp++) {
+ if ((rc = ctf_type_rvisit(fp, mp->ctm_type,
+ func, arg, ctf_strptr(fp, mp->ctm_name),
+ offset + mp->ctm_offset, depth + 1)) != 0)
+ return (rc);
+ }
+
+ } else {
+ const ctf_lmember_t *lmp = (const ctf_lmember_t *)
+ ((uintptr_t)tp + increment);
+
+ for (n = LCTF_INFO_VLEN(fp, tp->ctt_info); n != 0; n--, lmp++) {
+ if ((rc = ctf_type_rvisit(fp, lmp->ctlm_type,
+ func, arg, ctf_strptr(fp, lmp->ctlm_name),
+ offset + (ulong_t)CTF_LMEM_OFFSET(lmp),
+ depth + 1)) != 0)
+ return (rc);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Recursively visit the members of any type. We pass the name, member
+ * type, and offset of each member to the specified callback function.
+ */
+int
+ctf_type_visit(ctf_file_t *fp, ctf_id_t type, ctf_visit_f *func, void *arg)
+{
+ return (ctf_type_rvisit(fp, type, func, arg, "", 0, 0));
+}
diff --git a/libctf/ctf_util.c b/libctf/ctf_util.c
new file mode 100644
index 0000000..96d8c1d
--- /dev/null
+++ b/libctf/ctf_util.c
@@ -0,0 +1,152 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "ctf_impl.h"
+
+/*
+ * Simple doubly-linked list append routine. This implementation assumes that
+ * each list element contains an embedded ctf_list_t as the first member.
+ * An additional ctf_list_t is used to store the head (l_next) and tail
+ * (l_prev) pointers. The current head and tail list elements have their
+ * previous and next pointers set to NULL, respectively.
+ */
+void
+ctf_list_append(ctf_list_t *lp, void *new)
+{
+ ctf_list_t *p = lp->l_prev; /* p = tail list element */
+ ctf_list_t *q = new; /* q = new list element */
+
+ lp->l_prev = q;
+ q->l_prev = p;
+ q->l_next = NULL;
+
+ if (p != NULL)
+ p->l_next = q;
+ else
+ lp->l_next = q;
+}
+
+/*
+ * Prepend the specified existing element to the given ctf_list_t. The
+ * existing pointer should be pointing at a struct with embedded ctf_list_t.
+ */
+void
+ctf_list_prepend(ctf_list_t *lp, void *new)
+{
+ ctf_list_t *p = new; /* p = new list element */
+ ctf_list_t *q = lp->l_next; /* q = head list element */
+
+ lp->l_next = p;
+ p->l_prev = NULL;
+ p->l_next = q;
+
+ if (q != NULL)
+ q->l_prev = p;
+ else
+ lp->l_prev = p;
+}
+
+/*
+ * Delete the specified existing element from the given ctf_list_t. The
+ * existing pointer should be pointing at a struct with embedded ctf_list_t.
+ */
+void
+ctf_list_delete(ctf_list_t *lp, void *existing)
+{
+ ctf_list_t *p = existing;
+
+ if (p->l_prev != NULL)
+ p->l_prev->l_next = p->l_next;
+ else
+ lp->l_next = p->l_next;
+
+ if (p->l_next != NULL)
+ p->l_next->l_prev = p->l_prev;
+ else
+ lp->l_prev = p->l_prev;
+}
+
+/*
+ * Convert an encoded CTF string name into a pointer to a C string by looking
+ * up the appropriate string table buffer and then adding the offset.
+ */
+const char *
+ctf_strraw(ctf_file_t *fp, uint_t name)
+{
+ ctf_strs_t *ctsp = &fp->ctf_str[CTF_NAME_STID(name)];
+
+ if (ctsp->cts_strs != NULL && CTF_NAME_OFFSET(name) < ctsp->cts_len)
+ return (ctsp->cts_strs + CTF_NAME_OFFSET(name));
+
+ /* string table not loaded or corrupt offset */
+ return (NULL);
+}
+
+const char *
+ctf_strptr(ctf_file_t *fp, uint_t name)
+{
+ const char *s = ctf_strraw(fp, name);
+ return (s != NULL ? s : "(?)");
+}
+
+/*
+ * Same strdup(3C), but use ctf_alloc() to do the memory allocation.
+ */
+char *
+ctf_strdup(const char *s1)
+{
+ char *s2 = ctf_alloc(strlen(s1) + 1);
+
+ if (s2 != NULL)
+ (void) strcpy(s2, s1);
+
+ return (s2);
+}
+
+/*
+ * Store the specified error code into errp if it is non-NULL, and then
+ * return NULL for the benefit of the caller.
+ */
+ctf_file_t *
+ctf_set_open_errno(int *errp, int error)
+{
+ if (errp != NULL)
+ *errp = error;
+ return (NULL);
+}
+
+/*
+ * Store the specified error code into the CTF container, and then return
+ * CTF_ERR for the benefit of the caller.
+ */
+long
+ctf_set_errno(ctf_file_t *fp, int err)
+{
+ fp->ctf_errno = err;
+ return (CTF_ERR);
+}
diff --git a/man/ctfconvert.1 b/man/ctfconvert.1
new file mode 100644
index 0000000..0681a47
--- /dev/null
+++ b/man/ctfconvert.1
@@ -0,0 +1,85 @@
+.\"
+.\" Copyright (c) 2010 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" This software was developed by Rui Paulo under sponsorship from the
+.\" FreeBSD Foundation.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/cddl/usr.bin/ctfconvert/ctfconvert.1,v 1.1.2.2.2.1 2010/12/21 17:09:25 kensmith Exp $
+.\"
+.Dd July 7, 2010
+.Dt CTFCONVERT 1
+.Os
+.Sh NAME
+.Nm ctfconvert
+.Nd convert debug data to CTF data
+.Sh SYNOPSIS
+.Nm
+.Op Fl gis
+.Fl l Ar label
+.Fl L Ar labelenv
+.Op Fl o Ar outfile
+object_file
+.Sh DESCRIPTION
+The
+.Nm
+utility converts debug information from a binary file to CTF data and replaces
+the debug section of that file with a CTF section called SUNW_ctf.
+This new section is added to the input file, unless the -o
+option is present.
+You can also opt to keep the original debugging section with the
+-g option.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl l Ar label
+Sets the label as
+.Ar label .
+.It Fl L Ar labelenv
+Instructs
+.Nm
+to read the label from the environment variable
+.Ar labelenv .
+.It Fl g
+Don't delete the original debugging section.
+.It Fl i
+Ignore object files built from other languages than C.
+.It Fl s
+Use the .dynsym ELF section instead of the .symtab ELF section.
+.It Fl o Ar outfile
+Write the output to file in
+.Ar outfile .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr ctfmerge 1 ,
+.Xr ctfdump 1
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+The CTF utilities came from OpenSolaris.
diff --git a/man/ctfdump.1 b/man/ctfdump.1
new file mode 100644
index 0000000..c13f618
--- /dev/null
+++ b/man/ctfdump.1
@@ -0,0 +1,83 @@
+.\"
+.\" Copyright (c) 2010 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" This software was developed by Rui Paulo under sponsorship from the
+.\" FreeBSD Foundation.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/cddl/usr.bin/ctfdump/ctfdump.1,v 1.1.2.2.2.1 2010/12/21 17:09:25 kensmith Exp $
+.\"
+.Dd July 7, 2010
+.Dt CTFDUMP 1
+.Os
+.Sh NAME
+.Nm ctfdump
+.Nd dump the SUNW_ctf section of an ELF file
+.Sh SYNOPSIS
+.Nm
+.Op Fl dfhlsSt
+.Fl u Ar file
+file
+.Sh DESCRIPTION
+The
+.Nm
+utility dumps the contents of the CTF data section (SUNW_ctf) present in
+an ELF binary file.
+This section was previously created with
+.Xr ctfconvert 1
+or
+.Xr ctfmerge 1 .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Show the data object section.
+.It Fl f
+Show the function section.
+.It Fl h
+Show the header.
+.It Fl l
+Show the label section.
+.It Fl s
+Show the string table.
+.It Fl S
+Show statistics.
+.It Fl t
+Show the type section.
+.It Fl u Ar ufile
+Write the uncompressed CTF data to a raw CTF file called
+.Ar ufile .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr ctfconvert 1 ,
+.Xr ctfmerge 1
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+The CTF utilities came from OpenSolaris.
diff --git a/man/ctfmerge.1 b/man/ctfmerge.1
new file mode 100644
index 0000000..67030ce
--- /dev/null
+++ b/man/ctfmerge.1
@@ -0,0 +1,120 @@
+.\"
+.\" Copyright (c) 2010 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" This software was developed by Rui Paulo under sponsorship from the
+.\" FreeBSD Foundation.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD: src/cddl/usr.bin/ctfmerge/ctfmerge.1,v 1.1.2.2.2.1 2010/12/21 17:09:25 kensmith Exp $
+.\"
+.Dd July 7, 2010
+.Dt CTFMERGE 1
+.Os
+.Sh NAME
+.Nm ctfmerge
+.Nd merge several CTF data sections into one
+.Sh SYNOPSIS
+.Nm
+.Op Fl fgstv
+.Fl l Ar label
+.Fl L Ar labelenv
+.Fl o Ar outfile
+file ...
+.Nm
+.Op Fl fgstv
+.Fl l Ar label
+.Fl L Ar labelenv
+.Fl o Ar outfile
+.Fl d Ar uniqfile
+.Op Fl g
+.Op Fl D Ar uniqlabel
+file ...
+.Nm
+.Op Fl fgstv
+.Fl l Ar label
+.Fl L Ar labelenv
+.Fl o Ar outfile
+.Fl w Ar withfile
+file ...
+.Nm
+.Op Fl g
+.Fl c Ar srcfile
+.Ar destfile
+.Sh DESCRIPTION
+The
+.Nm
+utility merges several CTF data sections from several files into one
+output file, unifying common data.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl f
+Match global symbols to global CTF data.
+.It Fl g
+Don't delete the original debugging sections.
+.It Fl s
+Use the .dynsym ELF section instead of the .symtab ELF section.
+.It Fl t
+Make sure that all object files have a CTF section.
+.It Fl v
+Enable verbose mode.
+.It Fl l Ar label
+Sets the label as
+.Ar label .
+.It Fl L Ar labelenv
+Instructs
+.Nm
+to read the label from the environment variable
+.Ar labelenv .
+.It Fl o Ar outfile
+Use
+.Ar outfile
+to store the merged CTF data.
+.It Fl d Ar uniqfile
+Uniquify against
+.Ar uniqfile .
+.It Fl d Ar uniqlabel
+Uniquify against label
+.Ar uniqlabel
+.It Fl w Ar withfile
+Additive merge with
+.Ar withfile .
+.It Fl c Ar srcfile Ar destfile
+Copy CTF data from
+.Ar srcfile
+into
+.Ar destfile .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr ctfconvert 1 ,
+.Xr ctfdump 1
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+The CTF utilities came from OpenSolaris.
diff --git a/pctf/ctf.h b/pctf/ctf.h
new file mode 100644
index 0000000..2d1987b
--- /dev/null
+++ b/pctf/ctf.h
@@ -0,0 +1,360 @@
+/*
+ * 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 _CTF_H
+#define _CTF_H
+
+#if defined(sun)
+#pragma ident "%Z%%M% %I% %E% SMI"
+#endif
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * CTF - Compact ANSI-C Type Format
+ *
+ * This file format can be used to compactly represent the information needed
+ * by a debugger to interpret the ANSI-C types used by a given program.
+ * Traditionally, this kind of information is generated by the compiler when
+ * invoked with the -g flag and is stored in "stabs" strings or in the more
+ * modern DWARF format. CTF provides a representation of only the information
+ * that is relevant to debugging a complex, optimized C program such as the
+ * operating system kernel in a form that is significantly more compact than
+ * the equivalent stabs or DWARF representation. The format is data-model
+ * independent, so consumers do not need different code depending on whether
+ * they are 32-bit or 64-bit programs. CTF assumes that a standard ELF symbol
+ * table is available for use in the debugger, and uses the structure and data
+ * of the symbol table to avoid storing redundant information. The CTF data
+ * may be compressed on disk or in memory, indicated by a bit in the header.
+ * CTF may be interpreted in a raw disk file, or it may be stored in an ELF
+ * section, typically named .SUNW_ctf. Data structures are aligned so that
+ * a raw CTF file or CTF ELF section may be manipulated using mmap(2).
+ *
+ * The CTF file or section itself has the following structure:
+ *
+ * +--------+--------+---------+----------+-------+--------+
+ * | file | type | data | function | data | string |
+ * | header | labels | objects | info | types | table |
+ * +--------+--------+---------+----------+-------+--------+
+ *
+ * The file header stores a magic number and version information, encoding
+ * flags, and the byte offset of each of the sections relative to the end of the
+ * header itself. If the CTF data has been uniquified against another set of
+ * CTF data, a reference to that data also appears in the the header. This
+ * reference is the name of the label corresponding to the types uniquified
+ * against.
+ *
+ * Following the header is a list of labels, used to group the types included in
+ * the data types section. Each label is accompanied by a type ID i. A given
+ * label refers to the group of types whose IDs are in the range [0, i].
+ *
+ * Data object and function records are stored in the same order as they appear
+ * in the corresponding symbol table, except that symbols marked SHN_UNDEF are
+ * not stored and symbols that have no type data are padded out with zeroes.
+ * For each data object, the type ID (a small integer) is recorded. For each
+ * function, the type ID of the return type and argument types is recorded.
+ *
+ * The data types section is a list of variable size records that represent each
+ * type, in order by their ID. The types themselves form a directed graph,
+ * where each node may contain one or more outgoing edges to other type nodes,
+ * denoted by their ID.
+ *
+ * Strings are recorded as a string table ID (0 or 1) and a byte offset into the
+ * string table. String table 0 is the internal CTF string table. String table
+ * 1 is the external string table, which is the string table associated with the
+ * ELF symbol table for this object. CTF does not record any strings that are
+ * already in the symbol table, and the CTF string table does not contain any
+ * duplicated strings.
+ *
+ * If the CTF data has been merged with another parent CTF object, some outgoing
+ * edges may refer to type nodes that exist in another CTF object. The debugger
+ * and libctf library are responsible for connecting the appropriate objects
+ * together so that the full set of types can be explored and manipulated.
+ */
+
+#define CTF_MAX_TYPE 0xffff /* max type identifier value */
+#define CTF_MAX_NAME 0x7fffffff /* max offset into a string table */
+#define CTF_MAX_VLEN 0x3ff /* max struct, union, enum members or args */
+#define CTF_MAX_INTOFF 0xff /* max offset of intrinsic value in bits */
+#define CTF_MAX_INTBITS 0xffff /* max size of an intrinsic in bits */
+
+/* See ctf_type_t */
+#define CTF_MAX_SIZE 0xfffe /* max size of a type in bytes */
+#define CTF_LSIZE_SENT 0xffff /* sentinel for ctt_size */
+#define CTF_MAX_LSIZE UINT64_MAX
+
+typedef struct ctf_preamble {
+ ushort_t ctp_magic; /* magic number (CTF_MAGIC) */
+ uchar_t ctp_version; /* data format version number (CTF_VERSION) */
+ uchar_t ctp_flags; /* flags (see below) */
+} ctf_preamble_t;
+
+typedef struct ctf_header {
+ ctf_preamble_t cth_preamble;
+ uint_t cth_parlabel; /* ref to name of parent lbl uniq'd against */
+ uint_t cth_parname; /* ref to basename of parent */
+ uint_t cth_lbloff; /* offset of label section */
+ uint_t cth_objtoff; /* offset of object section */
+ uint_t cth_funcoff; /* offset of function section */
+ uint_t cth_typeoff; /* offset of type section */
+ uint_t cth_stroff; /* offset of string section */
+ uint_t cth_strlen; /* length of string section in bytes */
+} ctf_header_t;
+
+#define cth_magic cth_preamble.ctp_magic
+#define cth_version cth_preamble.ctp_version
+#define cth_flags cth_preamble.ctp_flags
+
+#ifdef CTF_OLD_VERSIONS
+
+typedef struct ctf_header_v1 {
+ ctf_preamble_t cth_preamble;
+ uint_t cth_objtoff;
+ uint_t cth_funcoff;
+ uint_t cth_typeoff;
+ uint_t cth_stroff;
+ uint_t cth_strlen;
+} ctf_header_v1_t;
+
+#endif /* CTF_OLD_VERSIONS */
+
+#define CTF_MAGIC 0xcff1 /* magic number identifying header */
+
+/* data format version number */
+#define CTF_VERSION_1 1
+#define CTF_VERSION_2 2
+#define CTF_VERSION CTF_VERSION_2 /* current version */
+
+#define CTF_F_COMPRESS 0x1 /* data buffer is compressed */
+
+typedef struct ctf_lblent {
+ uint_t ctl_label; /* ref to name of label */
+ uint_t ctl_typeidx; /* last type associated with this label */
+} ctf_lblent_t;
+
+typedef struct ctf_stype {
+ uint_t ctt_name; /* reference to name in string table */
+ ushort_t ctt_info; /* encoded kind, variant length (see below) */
+ union {
+ ushort_t _size; /* size of entire type in bytes */
+ ushort_t _type; /* reference to another type */
+ } _u;
+} ctf_stype_t;
+
+/*
+ * type sizes, measured in bytes, come in two flavors. 99% of them fit within
+ * (USHRT_MAX - 1), and thus can be stored in the ctt_size member of a
+ * ctf_stype_t. The maximum value for these sizes is CTF_MAX_SIZE. The sizes
+ * larger than CTF_MAX_SIZE must be stored in the ctt_lsize member of a
+ * ctf_type_t. Use of this member is indicated by the presence of
+ * CTF_LSIZE_SENT in ctt_size.
+ */
+typedef struct ctf_type {
+ uint_t ctt_name; /* reference to name in string table */
+ ushort_t ctt_info; /* encoded kind, variant length (see below) */
+ union {
+ ushort_t _size; /* always CTF_LSIZE_SENT */
+ ushort_t _type; /* do not use */
+ } _u;
+ uint_t ctt_lsizehi; /* high 32 bits of type size in bytes */
+ uint_t ctt_lsizelo; /* low 32 bits of type size in bytes */
+} ctf_type_t;
+
+#define ctt_size _u._size /* for fundamental types that have a size */
+#define ctt_type _u._type /* for types that reference another type */
+
+/*
+ * The following macros compose and decompose values for ctt_info and
+ * ctt_name, as well as other structures that contain name references.
+ *
+ * ------------------------
+ * ctt_info: | kind | isroot | vlen |
+ * ------------------------
+ * 15 11 10 9 0
+ *
+ * kind = CTF_INFO_KIND(c.ctt_info); <-- CTF_K_* value (see below)
+ * vlen = CTF_INFO_VLEN(c.ctt_info); <-- length of variable data list
+ *
+ * stid = CTF_NAME_STID(c.ctt_name); <-- string table id number (0 or 1)
+ * offset = CTF_NAME_OFFSET(c.ctt_name); <-- string table byte offset
+ *
+ * c.ctt_info = CTF_TYPE_INFO(kind, vlen);
+ * c.ctt_name = CTF_TYPE_NAME(stid, offset);
+ */
+
+#define CTF_INFO_KIND(info) (((info) & 0xf800) >> 11)
+#define CTF_INFO_ISROOT(info) (((info) & 0x0400) >> 10)
+#define CTF_INFO_VLEN(info) (((info) & CTF_MAX_VLEN))
+
+#define CTF_NAME_STID(name) ((name) >> 31)
+#define CTF_NAME_OFFSET(name) ((name) & 0x7fffffff)
+
+#define CTF_TYPE_INFO(kind, isroot, vlen) \
+ (((kind) << 11) | (((isroot) ? 1 : 0) << 10) | ((vlen) & CTF_MAX_VLEN))
+
+#define CTF_TYPE_NAME(stid, offset) \
+ (((stid) << 31) | ((offset) & 0x7fffffff))
+
+#define CTF_TYPE_ISPARENT(id) ((id) < 0x8000)
+#define CTF_TYPE_ISCHILD(id) ((id) > 0x7fff)
+
+#define CTF_TYPE_TO_INDEX(id) ((id) & 0x7fff)
+#define CTF_INDEX_TO_TYPE(id, child) ((child) ? ((id) | 0x8000) : (id))
+#define CTF_PARENT_SHIFT 15
+
+#define CTF_STRTAB_0 0 /* symbolic define for string table id 0 */
+#define CTF_STRTAB_1 1 /* symbolic define for string table id 1 */
+
+#define CTF_TYPE_LSIZE(cttp) \
+ (((uint64_t)(cttp)->ctt_lsizehi) << 32 | (cttp)->ctt_lsizelo)
+#define CTF_SIZE_TO_LSIZE_HI(size) ((uint32_t)((uint64_t)(size) >> 32))
+#define CTF_SIZE_TO_LSIZE_LO(size) ((uint32_t)(size))
+
+#ifdef CTF_OLD_VERSIONS
+
+#define CTF_INFO_KIND_V1(info) (((info) & 0xf000) >> 12)
+#define CTF_INFO_ISROOT_V1(info) (((info) & 0x0800) >> 11)
+#define CTF_INFO_VLEN_V1(info) (((info) & 0x07ff))
+
+#define CTF_TYPE_INFO_V1(kind, isroot, vlen) \
+ (((kind) << 12) | (((isroot) ? 1 : 0) << 11) | ((vlen) & 0x07ff))
+
+#endif /* CTF_OLD_VERSIONS */
+
+/*
+ * Values for CTF_TYPE_KIND(). If the kind has an associated data list,
+ * CTF_INFO_VLEN() will extract the number of elements in the list, and
+ * the type of each element is shown in the comments below.
+ */
+#define CTF_K_UNKNOWN 0 /* unknown type (used for padding) */
+#define CTF_K_INTEGER 1 /* variant data is CTF_INT_DATA() (see below) */
+#define CTF_K_FLOAT 2 /* variant data is CTF_FP_DATA() (see below) */
+#define CTF_K_POINTER 3 /* ctt_type is referenced type */
+#define CTF_K_ARRAY 4 /* variant data is single ctf_array_t */
+#define CTF_K_FUNCTION 5 /* ctt_type is return type, variant data is */
+ /* list of argument types (ushort_t's) */
+#define CTF_K_STRUCT 6 /* variant data is list of ctf_member_t's */
+#define CTF_K_UNION 7 /* variant data is list of ctf_member_t's */
+#define CTF_K_ENUM 8 /* variant data is list of ctf_enum_t's */
+#define CTF_K_FORWARD 9 /* no additional data; ctt_name is tag */
+#define CTF_K_TYPEDEF 10 /* ctt_type is referenced type */
+#define CTF_K_VOLATILE 11 /* ctt_type is base type */
+#define CTF_K_CONST 12 /* ctt_type is base type */
+#define CTF_K_RESTRICT 13 /* ctt_type is base type */
+
+#define CTF_K_MAX 31 /* Maximum possible CTF_K_* value */
+
+/*
+ * Values for ctt_type when kind is CTF_K_INTEGER. The flags, offset in bits,
+ * and size in bits are encoded as a single word using the following macros.
+ */
+#define CTF_INT_ENCODING(data) (((data) & 0xff000000) >> 24)
+#define CTF_INT_OFFSET(data) (((data) & 0x00ff0000) >> 16)
+#define CTF_INT_BITS(data) (((data) & 0x0000ffff))
+
+#define CTF_INT_DATA(encoding, offset, bits) \
+ (((encoding) << 24) | ((offset) << 16) | (bits))
+
+#define CTF_INT_SIGNED 0x01 /* integer is signed (otherwise unsigned) */
+#define CTF_INT_CHAR 0x02 /* character display format */
+#define CTF_INT_BOOL 0x04 /* boolean display format */
+#define CTF_INT_VARARGS 0x08 /* varargs display format */
+
+/*
+ * Values for ctt_type when kind is CTF_K_FLOAT. The encoding, offset in bits,
+ * and size in bits are encoded as a single word using the following macros.
+ */
+#define CTF_FP_ENCODING(data) (((data) & 0xff000000) >> 24)
+#define CTF_FP_OFFSET(data) (((data) & 0x00ff0000) >> 16)
+#define CTF_FP_BITS(data) (((data) & 0x0000ffff))
+
+#define CTF_FP_DATA(encoding, offset, bits) \
+ (((encoding) << 24) | ((offset) << 16) | (bits))
+
+#define CTF_FP_SINGLE 1 /* IEEE 32-bit float encoding */
+#define CTF_FP_DOUBLE 2 /* IEEE 64-bit float encoding */
+#define CTF_FP_CPLX 3 /* Complex encoding */
+#define CTF_FP_DCPLX 4 /* Double complex encoding */
+#define CTF_FP_LDCPLX 5 /* Long double complex encoding */
+#define CTF_FP_LDOUBLE 6 /* Long double encoding */
+#define CTF_FP_INTRVL 7 /* Interval (2x32-bit) encoding */
+#define CTF_FP_DINTRVL 8 /* Double interval (2x64-bit) encoding */
+#define CTF_FP_LDINTRVL 9 /* Long double interval (2x128-bit) encoding */
+#define CTF_FP_IMAGRY 10 /* Imaginary (32-bit) encoding */
+#define CTF_FP_DIMAGRY 11 /* Long imaginary (64-bit) encoding */
+#define CTF_FP_LDIMAGRY 12 /* Long double imaginary (128-bit) encoding */
+
+#define CTF_FP_MAX 12 /* Maximum possible CTF_FP_* value */
+
+typedef struct ctf_array {
+ ushort_t cta_contents; /* reference to type of array contents */
+ ushort_t cta_index; /* reference to type of array index */
+ uint_t cta_nelems; /* number of elements */
+} ctf_array_t;
+
+/*
+ * Most structure members have bit offsets that can be expressed using a
+ * short. Some don't. ctf_member_t is used for structs which cannot
+ * contain any of these large offsets, whereas ctf_lmember_t is used in the
+ * latter case. If ctt_size for a given struct is >= 8192 bytes, all members
+ * will be stored as type ctf_lmember_t.
+ */
+
+#define CTF_LSTRUCT_THRESH 8192
+
+typedef struct ctf_member {
+ uint_t ctm_name; /* reference to name in string table */
+ ushort_t ctm_type; /* reference to type of member */
+ ushort_t ctm_offset; /* offset of this member in bits */
+} ctf_member_t;
+
+typedef struct ctf_lmember {
+ uint_t ctlm_name; /* reference to name in string table */
+ ushort_t ctlm_type; /* reference to type of member */
+ ushort_t ctlm_pad; /* padding */
+ uint_t ctlm_offsethi; /* high 32 bits of member offset in bits */
+ uint_t ctlm_offsetlo; /* low 32 bits of member offset in bits */
+} ctf_lmember_t;
+
+#define CTF_LMEM_OFFSET(ctlmp) \
+ (((uint64_t)(ctlmp)->ctlm_offsethi) << 32 | (ctlmp)->ctlm_offsetlo)
+#define CTF_OFFSET_TO_LMEMHI(offset) ((uint32_t)((uint64_t)(offset) >> 32))
+#define CTF_OFFSET_TO_LMEMLO(offset) ((uint32_t)(offset))
+
+typedef struct ctf_enum {
+ uint_t cte_name; /* reference to name in string table */
+ int cte_value; /* value associated with this name */
+} ctf_enum_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTF_H */
diff --git a/pctf/ctf_api.h b/pctf/ctf_api.h
new file mode 100644
index 0000000..a26a39e
--- /dev/null
+++ b/pctf/ctf_api.h
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ */
+
+/*
+ * This header file defines the interfaces available from the CTF debugger
+ * library, libctf, and an equivalent kernel module. This API can be used by
+ * a debugger to operate on data in the Compact ANSI-C Type Format (CTF).
+ * This is NOT a public interface, although it may eventually become one in
+ * the fullness of time after we gain more experience with the interfaces.
+ *
+ * In the meantime, be aware that any program linked with this API in this
+ * release of Solaris is almost guaranteed to break in the next release.
+ *
+ * In short, do not user this header file or the CTF routines for any purpose.
+ */
+
+#ifndef _CTF_API_H
+#define _CTF_API_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/elf.h>
+#include <pctf/ctf.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Clients can open one or more CTF containers and obtain a pointer to an
+ * opaque ctf_file_t. Types are identified by an opaque ctf_id_t token.
+ * These opaque definitions allow libctf to evolve without breaking clients.
+ */
+typedef struct ctf_file ctf_file_t;
+typedef long ctf_id_t;
+
+/*
+ * If the debugger needs to provide the CTF library with a set of raw buffers
+ * for use as the CTF data, symbol table, and string table, it can do so by
+ * filling in ctf_sect_t structures and passing them to ctf_bufopen():
+ */
+typedef struct ctf_sect {
+ char *cts_name; /* section name (if any) */
+ ulong_t cts_type; /* section type (ELF SHT_... value) */
+ ulong_t cts_flags; /* section flags (ELF SHF_... value) */
+#if defined(sun)
+ const void *cts_data; /* pointer to section data */
+#else
+ void *cts_data; /* pointer to section data */
+#endif
+ size_t cts_size; /* size of data in bytes */
+ size_t cts_entsize; /* size of each section entry (symtab only) */
+ off64_t cts_offset; /* file offset of this section (if any) */
+} ctf_sect_t;
+
+/*
+ * Encoding information for integers, floating-point values, and certain other
+ * intrinsics can be obtained by calling ctf_type_encoding(), below. The flags
+ * field will contain values appropriate for the type defined in <sys/ctf.h>.
+ */
+typedef struct ctf_encoding {
+ uint_t cte_format; /* data format (CTF_INT_* or CTF_FP_* flags) */
+ uint_t cte_offset; /* offset of value in bits */
+ uint_t cte_bits; /* size of storage in bits */
+} ctf_encoding_t;
+
+typedef struct ctf_membinfo {
+ ctf_id_t ctm_type; /* type of struct or union member */
+ ulong_t ctm_offset; /* offset of member in bits */
+} ctf_membinfo_t;
+
+typedef struct ctf_arinfo {
+ ctf_id_t ctr_contents; /* type of array contents */
+ ctf_id_t ctr_index; /* type of array index */
+ uint_t ctr_nelems; /* number of elements */
+} ctf_arinfo_t;
+
+typedef struct ctf_funcinfo {
+ ctf_id_t ctc_return; /* function return type */
+ uint_t ctc_argc; /* number of typed arguments to function */
+ uint_t ctc_flags; /* function attributes (see below) */
+} ctf_funcinfo_t;
+
+typedef struct ctf_lblinfo {
+ ctf_id_t ctb_typeidx; /* last type associated with the label */
+} ctf_lblinfo_t;
+
+#define CTF_FUNC_VARARG 0x1 /* function arguments end with varargs */
+
+/*
+ * Functions that return integer status or a ctf_id_t use the following value
+ * to indicate failure. ctf_errno() can be used to obtain an error code.
+ */
+#define CTF_ERR (-1L)
+
+/*
+ * The CTF data model is inferred to be the caller's data model or the data
+ * model of the given object, unless ctf_setmodel() is explicitly called.
+ */
+#define CTF_MODEL_ILP32 1 /* object data model is ILP32 */
+#define CTF_MODEL_LP64 2 /* object data model is LP64 */
+#ifdef _LP64
+#define CTF_MODEL_NATIVE CTF_MODEL_LP64
+#else
+#define CTF_MODEL_NATIVE CTF_MODEL_ILP32
+#endif
+
+/*
+ * Dynamic CTF containers can be created using ctf_create(). The ctf_add_*
+ * routines can be used to add new definitions to the dynamic container.
+ * New types are labeled as root or non-root to determine whether they are
+ * visible at the top-level program scope when subsequently doing a lookup.
+ */
+#define CTF_ADD_NONROOT 0 /* type only visible in nested scope */
+#define CTF_ADD_ROOT 1 /* type visible at top-level scope */
+
+/*
+ * These typedefs are used to define the signature for callback functions
+ * that can be used with the iteration and visit functions below:
+ */
+typedef int ctf_visit_f(const char *, ctf_id_t, ulong_t, int, void *);
+typedef int ctf_member_f(const char *, ctf_id_t, ulong_t, void *);
+typedef int ctf_enum_f(const char *, int, void *);
+typedef int ctf_type_f(ctf_id_t, void *);
+typedef int ctf_label_f(const char *, const ctf_lblinfo_t *, void *);
+
+extern ctf_file_t *ctf_bufopen(const ctf_sect_t *, const ctf_sect_t *,
+ const ctf_sect_t *, int *);
+extern ctf_file_t *ctf_fdopen(int, int *);
+extern ctf_file_t *ctf_open(const char *, int *);
+extern ctf_file_t *ctf_create(int *);
+extern void ctf_close(ctf_file_t *);
+
+extern ctf_file_t *ctf_parent_file(ctf_file_t *);
+extern const char *ctf_parent_name(ctf_file_t *);
+
+extern int ctf_import(ctf_file_t *, ctf_file_t *);
+extern int ctf_setmodel(ctf_file_t *, int);
+extern int ctf_getmodel(ctf_file_t *);
+
+extern void ctf_setspecific(ctf_file_t *, void *);
+extern void *ctf_getspecific(ctf_file_t *);
+
+extern int ctf_errno(ctf_file_t *);
+extern const char *ctf_errmsg(int);
+extern int ctf_version(int);
+
+extern int ctf_func_info(ctf_file_t *, ulong_t, ctf_funcinfo_t *);
+extern int ctf_func_args(ctf_file_t *, ulong_t, uint_t, ctf_id_t *);
+
+extern ctf_id_t ctf_lookup_by_name(ctf_file_t *, const char *);
+extern ctf_id_t ctf_lookup_by_symbol(ctf_file_t *, ulong_t);
+
+extern ctf_id_t ctf_type_resolve(ctf_file_t *, ctf_id_t);
+extern ssize_t ctf_type_lname(ctf_file_t *, ctf_id_t, char *, size_t);
+extern char *ctf_type_name(ctf_file_t *, ctf_id_t, char *, size_t);
+extern ssize_t ctf_type_size(ctf_file_t *, ctf_id_t);
+extern ssize_t ctf_type_align(ctf_file_t *, ctf_id_t);
+extern int ctf_type_kind(ctf_file_t *, ctf_id_t);
+extern ctf_id_t ctf_type_reference(ctf_file_t *, ctf_id_t);
+extern ctf_id_t ctf_type_pointer(ctf_file_t *, ctf_id_t);
+extern int ctf_type_encoding(ctf_file_t *, ctf_id_t, ctf_encoding_t *);
+extern int ctf_type_visit(ctf_file_t *, ctf_id_t, ctf_visit_f *, void *);
+extern int ctf_type_cmp(ctf_file_t *, ctf_id_t, ctf_file_t *, ctf_id_t);
+extern int ctf_type_compat(ctf_file_t *, ctf_id_t, ctf_file_t *, ctf_id_t);
+
+extern int ctf_member_info(ctf_file_t *, ctf_id_t, const char *,
+ ctf_membinfo_t *);
+extern int ctf_array_info(ctf_file_t *, ctf_id_t, ctf_arinfo_t *);
+
+extern const char *ctf_enum_name(ctf_file_t *, ctf_id_t, int);
+extern int ctf_enum_value(ctf_file_t *, ctf_id_t, const char *, int *);
+
+extern const char *ctf_label_topmost(ctf_file_t *);
+extern int ctf_label_info(ctf_file_t *, const char *, ctf_lblinfo_t *);
+
+extern int ctf_member_iter(ctf_file_t *, ctf_id_t, ctf_member_f *, void *);
+extern int ctf_enum_iter(ctf_file_t *, ctf_id_t, ctf_enum_f *, void *);
+extern int ctf_type_iter(ctf_file_t *, ctf_type_f *, void *);
+extern int ctf_label_iter(ctf_file_t *, ctf_label_f *, void *);
+
+extern ctf_id_t ctf_add_array(ctf_file_t *, uint_t, const ctf_arinfo_t *);
+extern ctf_id_t ctf_add_const(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_enum(ctf_file_t *, uint_t, const char *);
+extern ctf_id_t ctf_add_float(ctf_file_t *, uint_t,
+ const char *, const ctf_encoding_t *);
+extern ctf_id_t ctf_add_forward(ctf_file_t *, uint_t, const char *, uint_t);
+extern ctf_id_t ctf_add_function(ctf_file_t *, uint_t,
+ const ctf_funcinfo_t *, const ctf_id_t *);
+extern ctf_id_t ctf_add_integer(ctf_file_t *, uint_t,
+ const char *, const ctf_encoding_t *);
+extern ctf_id_t ctf_add_pointer(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_type(ctf_file_t *, ctf_file_t *, ctf_id_t);
+extern ctf_id_t ctf_add_typedef(ctf_file_t *, uint_t, const char *, ctf_id_t);
+extern ctf_id_t ctf_add_restrict(ctf_file_t *, uint_t, ctf_id_t);
+extern ctf_id_t ctf_add_struct(ctf_file_t *, uint_t, const char *);
+extern ctf_id_t ctf_add_union(ctf_file_t *, uint_t, const char *);
+extern ctf_id_t ctf_add_volatile(ctf_file_t *, uint_t, ctf_id_t);
+
+extern int ctf_add_enumerator(ctf_file_t *, ctf_id_t, const char *, int);
+extern int ctf_add_member(ctf_file_t *, ctf_id_t, const char *, ctf_id_t);
+
+extern int ctf_set_array(ctf_file_t *, ctf_id_t, const ctf_arinfo_t *);
+
+extern int ctf_update(ctf_file_t *);
+extern int ctf_discard(ctf_file_t *);
+extern int ctf_write(ctf_file_t *, int);
+
+#ifdef _KERNEL
+
+struct module;
+extern ctf_file_t *ctf_modopen(struct module *, int *);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _CTF_API_H */
diff --git a/pctf/libctf.h b/pctf/libctf.h
new file mode 100644
index 0000000..3fd6931
--- /dev/null
+++ b/pctf/libctf.h
@@ -0,0 +1,60 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * This header file defines the interfaces available from the CTF debugger
+ * library, libctf. This library provides functions that a debugger can
+ * use to operate on data in the Compact ANSI-C Type Format (CTF). This
+ * is NOT a public interface, although it may eventually become one in
+ * the fullness of time after we gain more experience with the interfaces.
+ *
+ * In the meantime, be aware that any program linked with libctf in this
+ * release of Solaris is almost guaranteed to break in the next release.
+ *
+ * In short, do not user this header file or libctf for any purpose.
+ */
+
+#ifndef _LIBCTF_H
+#define _LIBCTF_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/ctf_api.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This flag can be used to enable debug messages.
+ */
+extern int _libctf_debug;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LIBCTF_H */
diff --git a/pctf/sysmacros.h b/pctf/sysmacros.h
new file mode 100644
index 0000000..7e104cb
--- /dev/null
+++ b/pctf/sysmacros.h
@@ -0,0 +1,143 @@
+/*
+ * 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
+ *
+ * $FreeBSD: src/sys/cddl/compat/opensolaris/sys/sysmacros.h,v 1.5.2.2.4.1 2010/12/21 17:09:25 kensmith Exp $
+ */
+/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
+/* All Rights Reserved */
+
+
+/*
+ * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _OPENSOLARIS_SYS_SYSMACROS_H_
+#define _OPENSOLARIS_SYS_SYSMACROS_H_
+
+#include <sys/param.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ABS
+#define ABS(a) ((a) < 0 ? -(a) : (a))
+#endif
+
+#ifndef SIGNOF
+#define SIGNOF(a) ((a) < 0 ? -1 : (a) > 0)
+#endif
+
+/*
+ * Macro for checking power of 2 address alignment.
+ */
+#define IS_P2ALIGNED(v, a) ((((uintptr_t)(v)) & ((uintptr_t)(a) - 1)) == 0)
+
+/*
+ * Macro to determine if value is a power of 2
+ */
+#define ISP2(x) (((x) & ((x) - 1)) == 0)
+
+/*
+ * Macros for various sorts of alignment and rounding when the alignment
+ * is known to be a power of 2.
+ */
+#define P2ALIGN(x, align) ((x) & -(align))
+#define P2PHASE(x, align) ((x) & ((align) - 1))
+#define P2NPHASE(x, align) (-(x) & ((align) - 1))
+#define P2ROUNDUP(x, align) (-(-(x) & -(align)))
+#define P2END(x, align) (-(~(x) & -(align)))
+#define P2PHASEUP(x, align, phase) ((phase) - (((phase) - (x)) & -(align)))
+#define P2BOUNDARY(off, len, align) (((off) ^ ((off) + (len) - 1)) > (align) - 1)
+/*
+ * Determine whether two numbers have the same high-order bit.
+ */
+#define P2SAMEHIGHBIT(x, y) (((x) ^ (y)) < ((x) & (y)))
+
+/*
+ * Typed version of the P2* macros. These macros should be used to ensure
+ * that the result is correctly calculated based on the data type of (x),
+ * which is passed in as the last argument, regardless of the data
+ * type of the alignment. For example, if (x) is of type uint64_t,
+ * and we want to round it up to a page boundary using "PAGESIZE" as
+ * the alignment, we can do either
+ * P2ROUNDUP(x, (uint64_t)PAGESIZE)
+ * or
+ * P2ROUNDUP_TYPED(x, PAGESIZE, uint64_t)
+ */
+#define P2ALIGN_TYPED(x, align, type) \
+ ((type)(x) & -(type)(align))
+#define P2PHASE_TYPED(x, align, type) \
+ ((type)(x) & ((type)(align) - 1))
+#define P2NPHASE_TYPED(x, align, type) \
+ (-(type)(x) & ((type)(align) - 1))
+#define P2ROUNDUP_TYPED(x, align, type) \
+ (-(-(type)(x) & -(type)(align)))
+#define P2END_TYPED(x, align, type) \
+ (-(~(type)(x) & -(type)(align)))
+#define P2PHASEUP_TYPED(x, align, phase, type) \
+ ((type)(phase) - (((type)(phase) - (type)(x)) & -(type)(align)))
+#define P2CROSS_TYPED(x, y, align, type) \
+ (((type)(x) ^ (type)(y)) > (type)(align) - 1)
+#define P2SAMEHIGHBIT_TYPED(x, y, type) \
+ (((type)(x) ^ (type)(y)) < ((type)(x) & (type)(y)))
+
+/*
+ * Find highest one bit set.
+ * Returns bit number + 1 of highest bit that is set, otherwise returns 0.
+ * High order bit is 31 (or 63 in _LP64 kernel).
+ */
+static inline int
+highbit(ulong_t i)
+{
+ register int h = 1;
+
+ if (i == 0)
+ return (0);
+#ifdef _LP64
+ if (i & 0xffffffff00000000ul) {
+ h += 32; i >>= 32;
+ }
+#endif
+ if (i & 0xffff0000) {
+ h += 16; i >>= 16;
+ }
+ if (i & 0xff00) {
+ h += 8; i >>= 8;
+ }
+ if (i & 0xf0) {
+ h += 4; i >>= 4;
+ }
+ if (i & 0xc) {
+ h += 2; i >>= 2;
+ }
+ if (i & 0x2) {
+ h += 1;
+ }
+ return (h);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _OPENSOLARIS_SYS_SYSMACROS_H_ */