diff options
author | Wez Furlong <wez@netevil.org> | 2011-04-03 14:37:41 -0400 |
---|---|---|
committer | Wez Furlong <wez@netevil.org> | 2011-04-03 14:37:41 -0400 |
commit | 253acb864d82ab0406d6a6bcecd09e502c64b140 (patch) | |
tree | 115e5562e80acc02e7329053f258cf1c0e3e1e38 | |
download | ctf-253acb864d82ab0406d6a6bcecd09e502c64b140.tar.gz |
Pull in the CTF sources from the FreeBSD 8.2 tree; arrange for them
to build under autoconf.
-rw-r--r-- | .hgignore | 30 | ||||
-rw-r--r-- | Makefile.am | 58 | ||||
-rw-r--r-- | OPENSOLARIS.LICENSE | 384 | ||||
-rw-r--r-- | README.md | 12 | ||||
-rwxr-xr-x | autogen.sh | 13 | ||||
-rw-r--r-- | common/ctf_headers.h | 72 | ||||
-rw-r--r-- | common/list.c | 228 | ||||
-rw-r--r-- | common/list.h | 58 | ||||
-rw-r--r-- | common/memory.c | 103 | ||||
-rw-r--r-- | common/memory.h | 52 | ||||
-rw-r--r-- | common/symbol.c | 61 | ||||
-rw-r--r-- | common/symbol.h | 44 | ||||
-rw-r--r-- | common/utils.c | 107 | ||||
-rw-r--r-- | common/utils.h | 53 | ||||
-rw-r--r-- | configure.ac | 55 | ||||
-rw-r--r-- | cvt/alist.c | 214 | ||||
-rw-r--r-- | cvt/alist.h | 57 | ||||
-rw-r--r-- | cvt/barrier.c | 94 | ||||
-rw-r--r-- | cvt/barrier.h | 62 | ||||
-rw-r--r-- | cvt/compare.c | 93 | ||||
-rw-r--r-- | cvt/ctf.c | 1277 | ||||
-rw-r--r-- | cvt/ctfconvert.c | 254 | ||||
-rw-r--r-- | cvt/ctfmerge.c | 1011 | ||||
-rw-r--r-- | cvt/ctfmerge.h | 90 | ||||
-rw-r--r-- | cvt/ctftools.h | 446 | ||||
-rw-r--r-- | cvt/dwarf.c | 1838 | ||||
-rw-r--r-- | cvt/fifo.c | 153 | ||||
-rw-r--r-- | cvt/fifo.h | 54 | ||||
-rw-r--r-- | cvt/fixup_tdescs.c | 276 | ||||
-rw-r--r-- | cvt/hash.c | 286 | ||||
-rw-r--r-- | cvt/hash.h | 59 | ||||
-rw-r--r-- | cvt/iidesc.c | 194 | ||||
-rw-r--r-- | cvt/input.c | 412 | ||||
-rw-r--r-- | cvt/merge.c | 1139 | ||||
-rw-r--r-- | cvt/output.c | 748 | ||||
-rw-r--r-- | cvt/st_parse.c | 1205 | ||||
-rw-r--r-- | cvt/stabs.c | 371 | ||||
-rw-r--r-- | cvt/stack.c | 112 | ||||
-rw-r--r-- | cvt/stack.h | 53 | ||||
-rw-r--r-- | cvt/strtab.c | 253 | ||||
-rw-r--r-- | cvt/strtab.h | 69 | ||||
-rw-r--r-- | cvt/tdata.c | 483 | ||||
-rw-r--r-- | cvt/traverse.c | 224 | ||||
-rw-r--r-- | cvt/traverse.h | 71 | ||||
-rw-r--r-- | cvt/util.c | 273 | ||||
-rw-r--r-- | dump/dump.c | 1015 | ||||
-rw-r--r-- | libctf/ctf_create.c | 1363 | ||||
-rw-r--r-- | libctf/ctf_decl.c | 184 | ||||
-rw-r--r-- | libctf/ctf_error.c | 97 | ||||
-rw-r--r-- | libctf/ctf_hash.c | 178 | ||||
-rw-r--r-- | libctf/ctf_impl.h | 385 | ||||
-rw-r--r-- | libctf/ctf_labels.c | 153 | ||||
-rw-r--r-- | libctf/ctf_lib.c | 450 | ||||
-rw-r--r-- | libctf/ctf_lookup.c | 312 | ||||
-rw-r--r-- | libctf/ctf_open.c | 952 | ||||
-rw-r--r-- | libctf/ctf_subr.c | 81 | ||||
-rw-r--r-- | libctf/ctf_types.c | 845 | ||||
-rw-r--r-- | libctf/ctf_util.c | 152 | ||||
-rw-r--r-- | man/ctfconvert.1 | 85 | ||||
-rw-r--r-- | man/ctfdump.1 | 83 | ||||
-rw-r--r-- | man/ctfmerge.1 | 120 | ||||
-rw-r--r-- | pctf/ctf.h | 360 | ||||
-rw-r--r-- | pctf/ctf_api.h | 244 | ||||
-rw-r--r-- | pctf/libctf.h | 60 | ||||
-rw-r--r-- | pctf/sysmacros.h | 143 |
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 **)<gtid)) + 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_ */ |