summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorRobert Mustacchi <rm@joyent.com>2014-07-30 19:32:50 +0000
committerRobert Mustacchi <rm@joyent.com>2015-02-15 17:26:09 +0000
commitb9050054bed684df58fc1c855197eec06a2036bf (patch)
treecc8ec42bb149c4c00b068fc8cab890ec5cdfc61a /usr/src
parentf31c6fa33bcc9a608ce6f9ffd671ffc2b65a30ef (diff)
downloadillumos-joyent-b9050054bed684df58fc1c855197eec06a2036bf.tar.gz
OS-3851 ctfdump should be written in terms of libctf
OS-3852 ctfdiff could be more useful OS-3853 ctfmerge and ctfconvert could have an altexec OS-3854 ctfmerge should be implemented in terms of libctf Reviewed by: Keith M Wesolowski <wesolows@foobazco.org>
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/cmd/Makefile2
-rw-r--r--usr/src/cmd/ctfdiff/Makefile29
-rw-r--r--usr/src/cmd/ctfdiff/ctfdiff.c382
-rw-r--r--usr/src/cmd/ctfdump/Makefile (renamed from usr/src/cmd/mdb/intel/ia32/libctf/Makefile)26
-rw-r--r--usr/src/cmd/ctfdump/ctfdump.c803
-rw-r--r--usr/src/cmd/ctfmerge/Makefile (renamed from usr/src/cmd/mdb/sparc/v7/libctf/Makefile)26
-rw-r--r--usr/src/cmd/ctfmerge/ctfmerge.c487
-rw-r--r--usr/src/cmd/mdb/Makefile.common1
-rw-r--r--usr/src/cmd/mdb/common/kmdb/kmdb_promif.c7
-rw-r--r--usr/src/cmd/mdb/common/libstandctf/ctf_subr.c9
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_ctf.c15
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_debug.c4
-rw-r--r--usr/src/cmd/mdb/common/mdb/mdb_debug.h4
-rw-r--r--usr/src/cmd/mdb/common/modules/ctf/mdb_modctf.c86
-rw-r--r--usr/src/common/ctf/ctf_create.c606
-rw-r--r--usr/src/common/ctf/ctf_error.c9
-rw-r--r--usr/src/common/ctf/ctf_hash.c157
-rw-r--r--usr/src/common/ctf/ctf_impl.h86
-rw-r--r--usr/src/common/ctf/ctf_open.c40
-rw-r--r--usr/src/common/ctf/ctf_types.c222
-rw-r--r--usr/src/common/ctf/ctf_util.c43
-rw-r--r--usr/src/lib/Makefile.lib4
-rw-r--r--usr/src/lib/libctf/Makefile.shared.com20
-rw-r--r--usr/src/lib/libctf/Makefile.shared.targ6
-rw-r--r--usr/src/lib/libctf/common/ctf_diff.c724
-rw-r--r--usr/src/lib/libctf/common/ctf_elfwrite.c420
-rw-r--r--usr/src/lib/libctf/common/ctf_lib.c86
-rw-r--r--usr/src/lib/libctf/common/ctf_merge.c1231
-rw-r--r--usr/src/lib/libctf/common/libctf.h23
-rw-r--r--usr/src/lib/libctf/common/mapfile-vers32
-rw-r--r--usr/src/lib/libdtrace/common/dt_decl.c4
-rw-r--r--usr/src/lib/libdtrace/common/dt_open.c14
-rw-r--r--usr/src/lib/libdtrace/common/dt_parser.c4
-rw-r--r--usr/src/man/man1/Makefile2
-rw-r--r--usr/src/man/man1/ctfdiff.1337
-rw-r--r--usr/src/man/man1/ctfdump.1214
-rw-r--r--usr/src/tools/ctf/Makefile6
-rw-r--r--usr/src/tools/ctf/ctfdiff/i386/Makefile29
-rw-r--r--usr/src/tools/ctf/ctfdiff/sparc/Makefile29
-rw-r--r--usr/src/tools/ctf/ctfdump/Makefile (renamed from usr/src/cmd/mdb/intel/amd64/libctf/Makefile)27
-rw-r--r--usr/src/tools/ctf/ctfdump/Makefile.com44
-rw-r--r--usr/src/tools/ctf/ctfdump/i386/Makefile (renamed from usr/src/cmd/mdb/sparc/v9/libctf/Makefile)14
-rw-r--r--usr/src/tools/ctf/ctfdump/sparc/Makefile16
-rw-r--r--usr/src/tools/ctf/ctfmerge/Makefile (renamed from usr/src/tools/ctf/dump/Makefile)1
-rw-r--r--usr/src/tools/ctf/ctfmerge/Makefile.com46
-rw-r--r--usr/src/tools/ctf/ctfmerge/i386/Makefile (renamed from usr/src/tools/ctf/dump/i386/Makefile)1
-rw-r--r--usr/src/tools/ctf/ctfmerge/sparc/Makefile (renamed from usr/src/tools/ctf/dump/sparc/Makefile)1
-rw-r--r--usr/src/tools/ctf/cvt/Makefile.com1
-rw-r--r--usr/src/tools/ctf/cvt/altexec.c45
-rw-r--r--usr/src/tools/ctf/cvt/ctfconvert.c12
-rw-r--r--usr/src/tools/ctf/cvt/ctfmerge.c4
-rw-r--r--usr/src/tools/ctf/cvt/ctftools.h3
-rw-r--r--usr/src/tools/ctf/dump/Makefile.com67
-rw-r--r--usr/src/tools/ctf/dump/dump.c1028
-rw-r--r--usr/src/tools/ctf/libctf/Makefile.com4
-rw-r--r--usr/src/tools/ctf/libctf/i386/Makefile14
-rw-r--r--usr/src/tools/ctf/libctf/sparc/Makefile14
-rw-r--r--usr/src/uts/common/ctf/ctf_mod.c11
-rw-r--r--usr/src/uts/common/sys/ctf_api.h123
59 files changed, 5868 insertions, 1837 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index f78feac556..e72340af29 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -105,6 +105,8 @@ COMMON_SUBDIRS= \
csh \
csplit \
ctfdiff \
+ ctfdump \
+ ctfmerge \
ctrun \
ctstat \
ctwatch \
diff --git a/usr/src/cmd/ctfdiff/Makefile b/usr/src/cmd/ctfdiff/Makefile
index 5779f280bc..268bf9f3ed 100644
--- a/usr/src/cmd/ctfdiff/Makefile
+++ b/usr/src/cmd/ctfdiff/Makefile
@@ -1,27 +1,16 @@
#
-# CDDL HEADER START
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
#
-# 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
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
#
+
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
+# Copyright 2015, Joyent, Inc.
#
PROG= ctfdiff
diff --git a/usr/src/cmd/ctfdiff/ctfdiff.c b/usr/src/cmd/ctfdiff/ctfdiff.c
index 9d4c65e698..2537c15bcb 100644
--- a/usr/src/cmd/ctfdiff/ctfdiff.c
+++ b/usr/src/cmd/ctfdiff/ctfdiff.c
@@ -10,7 +10,7 @@
*/
/*
- * Copyright (c) 2014 Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
*/
/*
@@ -22,11 +22,31 @@
#include <errno.h>
#include <strings.h>
#include <libctf.h>
+#include <libgen.h>
+#include <stdarg.h>
+
+#define CTFDIFF_NAMELEN 256
#define CTFDIFF_EXIT_SIMILAR 0
#define CTFDIFF_EXIT_DIFFERENT 1
-#define CTFDIFF_EXIT_ERROR 2
+#define CTFDIFF_EXIT_USAGE 2
+#define CTFDIFF_EXIT_ERROR 3
+
+typedef enum ctf_diff_cmd {
+ CTF_DIFF_TYPES = 0x01,
+ CTF_DIFF_FUNCS = 0x02,
+ CTF_DIFF_OBJS = 0x04,
+ CTF_DIFF_DEFAULT = 0x07,
+ CTF_DIFF_LABEL = 0x08,
+ CTF_DIFF_ALL = 0x0f
+} ctf_diff_cmd_t;
+typedef struct {
+ int dil_next;
+ const char **dil_labels;
+} diff_label_t;
+
+static char *g_progname;
static const char *g_iname;
static ctf_file_t *g_ifp;
static const char *g_oname;
@@ -34,8 +54,28 @@ static ctf_file_t *g_ofp;
static char **g_typelist = NULL;
static int g_nexttype = 0;
static int g_ntypes = 0;
+static char **g_objlist = NULL;
+static int g_nextfunc = 0;
+static int g_nfuncs = 0;
+static char **g_funclist = NULL;
+static int g_nextobj = 0;
+static int g_nobjs = 0;
static boolean_t g_onlydiff = B_FALSE;
static boolean_t g_different = B_FALSE;
+static ctf_diff_cmd_t g_flag = 0;
+
+static void
+ctfdiff_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(CTFDIFF_EXIT_ERROR);
+}
static const char *
fp_to_name(ctf_file_t *fp)
@@ -49,26 +89,92 @@ fp_to_name(ctf_file_t *fp)
/* ARGSUSED */
static void
+diff_func_cb(ctf_file_t *ifp, ulong_t iidx, boolean_t similar, ctf_file_t *ofp,
+ ulong_t oidx, void *arg)
+{
+ char namebuf[CTFDIFF_NAMELEN];
+
+ if (similar == B_TRUE)
+ return;
+
+ if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) {
+ if (g_nextfunc != 0)
+ return;
+ (void) printf("ctf container %s function %ld is different\n",
+ fp_to_name(ifp), iidx);
+ } else {
+ if (g_nextfunc != 0) {
+ int i;
+ for (i = 0; i < g_nextfunc; i++) {
+ if (strcmp(g_funclist[i], namebuf) == 0)
+ break;
+ }
+ if (i == g_nextfunc)
+ return;
+ }
+ (void) printf("ctf container %s function %s (%ld) is "
+ "different\n", fp_to_name(ifp), namebuf, iidx);
+ }
+
+ g_different = B_TRUE;
+}
+
+/* ARGSUSED */
+static void
+diff_obj_cb(ctf_file_t *ifp, ulong_t iidx, ctf_id_t iid, boolean_t similar,
+ ctf_file_t *ofp, ulong_t oidx, ctf_id_t oid, void *arg)
+{
+ char namebuf[CTFDIFF_NAMELEN];
+
+ if (similar == B_TRUE)
+ return;
+
+ if (ctf_symbol_name(ifp, iidx, namebuf, sizeof (namebuf)) == NULL) {
+ if (g_nextobj != 0)
+ return;
+ (void) printf("ctf container %s object %ld is different\n",
+ fp_to_name(ifp), iidx);
+ } else {
+ if (g_nextobj != 0) {
+ int i;
+ for (i = 0; i < g_nextobj; i++) {
+ if (strcmp(g_objlist[i], namebuf) == 0)
+ break;
+ }
+ if (i == g_nextobj)
+ return;
+ }
+ (void) printf("ctf container %s object %s (%ld) is different\n",
+ fp_to_name(ifp), namebuf, iidx);
+ }
+
+ g_different = B_TRUE;
+}
+
+/* ARGSUSED */
+static void
diff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp,
ctf_id_t oid, void *arg)
{
if (similar == B_TRUE)
return;
+ if (ctf_type_kind(ifp, iid) == CTF_K_UNKNOWN)
+ return;
+
/*
* Check if it's the type the user cares about.
*/
if (g_nexttype != 0) {
int i;
- char namebuf[256];
+ char namebuf[CTFDIFF_NAMELEN];
if (ctf_type_name(ifp, iid, namebuf, sizeof (namebuf)) ==
NULL) {
- (void) fprintf(stderr, "failed to obtain the name "
+ ctfdiff_fatal("failed to obtain the name "
"of type %ld from %s: %s\n",
iid, fp_to_name(ifp),
ctf_errmsg(ctf_errno(ifp)));
- exit(CTFDIFF_EXIT_ERROR);
}
for (i = 0; i < g_nexttype; i++) {
@@ -85,13 +191,125 @@ diff_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t similar, ctf_file_t *ofp,
if (g_onlydiff == B_TRUE)
return;
- (void) printf("fp %s type %ld ", fp_to_name(ifp), iid);
- if (similar == B_TRUE) {
- (void) printf("is the same as fp %s type %ld\n",
- fp_to_name(ofp), oid);
- } else {
- (void) printf("is different\n");
+ (void) printf("ctf container %s type %ld is different\n",
+ fp_to_name(ifp), iid);
+}
+
+/* ARGSUSED */
+static int
+diff_labels_count(const char *name, const ctf_lblinfo_t *li, void *arg)
+{
+ uint32_t *count = arg;
+ *count = *count + 1;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+diff_labels_fill(const char *name, const ctf_lblinfo_t *li, void *arg)
+{
+ diff_label_t *dil = arg;
+
+ dil->dil_labels[dil->dil_next] = name;
+ dil->dil_next++;
+
+ return (0);
+}
+
+static int
+diff_labels(ctf_file_t *ifp, ctf_file_t *ofp)
+{
+ int ret;
+ uint32_t nilabel, nolabel, i, j;
+ diff_label_t idl, odl;
+ const char **ilptr, **olptr;
+
+ nilabel = nolabel = 0;
+ ret = ctf_label_iter(ifp, diff_labels_count, &nilabel);
+ if (ret == CTF_ERR)
+ return (ret);
+ ret = ctf_label_iter(ofp, diff_labels_count, &nolabel);
+ if (ret == CTF_ERR)
+ return (ret);
+
+ if (nilabel != nolabel) {
+ (void) printf("ctf container %s labels differ from ctf "
+ "container %s\n", fp_to_name(ifp), fp_to_name(ofp));
+ g_different = B_TRUE;
+ return (0);
+ }
+
+ if (nilabel == 0)
+ return (0);
+
+ ilptr = malloc(sizeof (char *) * nilabel);
+ olptr = malloc(sizeof (char *) * nolabel);
+ if (ilptr == NULL || olptr == NULL) {
+ ctfdiff_fatal("failed to allocate memory for label "
+ "comparison\n");
+ }
+
+ idl.dil_next = 0;
+ idl.dil_labels = ilptr;
+ odl.dil_next = 0;
+ odl.dil_labels = olptr;
+
+ if ((ret = ctf_label_iter(ifp, diff_labels_fill, &idl)) != 0)
+ goto out;
+ if ((ret = ctf_label_iter(ofp, diff_labels_fill, &odl)) != 0)
+ goto out;
+
+ for (i = 0; i < nilabel; i++) {
+ for (j = 0; j < nolabel; j++) {
+ if (strcmp(ilptr[i], olptr[j]) == 0)
+ break;
+ }
+
+ if (j == nolabel) {
+ (void) printf("ctf container %s labels differ from ctf "
+ "container %s\n", fp_to_name(ifp), fp_to_name(ofp));
+ g_different = B_TRUE;
+ break;
+ }
}
+
+ ret = 0;
+out:
+ free(ilptr);
+ free(olptr);
+ return (ret);
+}
+
+static void
+diff_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-afIloqt] [-F function] [-O object]"
+ "[-p parent] [-P parent]\n"
+ "\t[-T type] file1 file2\n"
+ "\n"
+ "\t-a diff label, types, objects, and functions\n"
+ "\t-f diff function type information\n"
+ "\t-F when diffing functions, only consider those named\n"
+ "\t-I ignore the names of integral types\n"
+ "\t-l diff CTF labels\n"
+ "\t-o diff global object type information\n"
+ "\t-O when diffing objects, only consider those named\n"
+ "\t-p set the CTF parent for file1\n"
+ "\t-P set the CTF parent for file2\n"
+ "\t-q set quiet mode (no diff information sent to stdout)\n"
+ "\t-t diff CTF type information\n"
+ "\t-T when diffing types, only consider those named\n",
+ g_progname);
}
int
@@ -104,19 +322,72 @@ main(int argc, char *argv[])
ctf_file_t *pifp = NULL;
ctf_file_t *pofp = NULL;
- while ((c = getopt(argc, argv, "qIp:P:T:")) != -1) {
+ g_progname = basename(argv[0]);
+
+ while ((c = getopt(argc, argv, "aqtfolIp:F:O:P:T:")) != -1) {
switch (c) {
+ case 'a':
+ g_flag |= CTF_DIFF_ALL;
+ break;
+ case 't':
+ g_flag |= CTF_DIFF_TYPES;
+ break;
+ case 'f':
+ g_flag |= CTF_DIFF_FUNCS;
+ break;
+ case 'o':
+ g_flag |= CTF_DIFF_OBJS;
+ break;
+ case 'l':
+ g_flag |= CTF_DIFF_LABEL;
+ break;
case 'q':
g_onlydiff = B_TRUE;
break;
case 'p':
pifp = ctf_open(optarg, &err);
if (pifp == NULL) {
- (void) fprintf(stderr, "ctfdiff: failed to "
- "open parent input container %s: %s\n",
- optarg, ctf_errmsg(err));
- return (CTFDIFF_EXIT_ERROR);
+ ctfdiff_fatal("failed to open parent input "
+ "container %s: %s\n", optarg,
+ ctf_errmsg(err));
+ }
+ break;
+ case 'F':
+ if (g_nextfunc == g_nfuncs) {
+ if (g_nfuncs == 0)
+ g_nfuncs = 16;
+ else
+ g_nfuncs *= 2;
+ g_funclist = realloc(g_funclist,
+ sizeof (char *) * g_nfuncs);
+ if (g_funclist == NULL) {
+ ctfdiff_fatal("failed to allocate "
+ "memory for the %dth -F option: "
+ "%s\n", g_nexttype + 1,
+ strerror(errno));
+ }
}
+ g_funclist[g_nextfunc] = optarg;
+ g_nextfunc++;
+ break;
+ case 'O':
+ if (g_nextobj == g_nobjs) {
+ if (g_nobjs == 0)
+ g_nobjs = 16;
+ else
+ g_nobjs *= 2;
+ g_objlist = realloc(g_objlist,
+ sizeof (char *) * g_nobjs);
+ if (g_objlist == NULL) {
+ ctfdiff_fatal("failed to allocate "
+ "memory for the %dth -F option: "
+ "%s\n", g_nexttype + 1,
+ strerror(errno));
+ return (CTFDIFF_EXIT_ERROR);
+ }
+ }
+ g_objlist[g_nextobj] = optarg;
+ g_nextobj++;
break;
case 'I':
flags |= CTF_DIFF_F_IGNORE_INTNAMES;
@@ -124,10 +395,9 @@ main(int argc, char *argv[])
case 'P':
pofp = ctf_open(optarg, &err);
if (pofp == NULL) {
- (void) fprintf(stderr, "ctfdiff: failed to "
- "open parent output container %s: %s\n",
- optarg, ctf_errmsg(err));
- return (CTFDIFF_EXIT_ERROR);
+ ctfdiff_fatal("failed to open parent output "
+ "container %s: %s\n", optarg,
+ ctf_errmsg(err));
}
break;
case 'T':
@@ -139,10 +409,10 @@ main(int argc, char *argv[])
g_typelist = realloc(g_typelist,
sizeof (char *) * g_ntypes);
if (g_typelist == NULL) {
- (void) fprintf(stderr, "ctfdiff: "
- "failed to allocate memory for "
- "the %dth -t option: %s\n",
- g_nexttype + 1, strerror(errno));
+ ctfdiff_fatal("failed to allocate "
+ "memory for the %dth -T option: "
+ "%s\n", g_nexttype + 1,
+ strerror(errno));
}
}
g_typelist[g_nexttype] = optarg;
@@ -153,24 +423,39 @@ main(int argc, char *argv[])
argc -= optind - 1;
argv += optind - 1;
+ if (g_flag == 0)
+ g_flag = CTF_DIFF_DEFAULT;
+
if (argc != 3) {
- (void) fprintf(stderr, "usage: ctfdiff [-qI] [-p parent] "
- "[-P parent] [-T type]... input output");
- return (CTFDIFF_EXIT_ERROR);
+ diff_usage(NULL);
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ if (g_nexttype != 0 && !(g_flag & CTF_DIFF_TYPES)) {
+ diff_usage("-T cannot be used if not diffing types\n");
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ if (g_nextfunc != 0 && !(g_flag & CTF_DIFF_FUNCS)) {
+ diff_usage("-F cannot be used if not diffing functions\n");
+ return (CTFDIFF_EXIT_USAGE);
+ }
+
+ if (g_nextobj != 0 && !(g_flag & CTF_DIFF_OBJS)) {
+ diff_usage("-O cannot be used if not diffing objects\n");
+ return (CTFDIFF_EXIT_USAGE);
}
ifp = ctf_open(argv[1], &err);
if (ifp == NULL) {
- (void) fprintf(stderr, "ctfdiff: failed to open %s: %s\n",
- argv[1], ctf_errmsg(err));
- return (CTFDIFF_EXIT_ERROR);
+ ctfdiff_fatal("failed to open %s: %s\n", argv[1],
+ ctf_errmsg(err));
}
if (pifp != NULL) {
err = ctf_import(ifp, pifp);
if (err != 0) {
- (void) fprintf(stderr, "ctfdiff: failed to set parent "
- "container: %s\n", ctf_errmsg(ctf_errno(pifp)));
- return (CTFDIFF_EXIT_ERROR);
+ ctfdiff_fatal("failed to set parent container: %s\n",
+ ctf_errmsg(ctf_errno(pifp)));
}
}
g_iname = argv[1];
@@ -178,41 +463,44 @@ main(int argc, char *argv[])
ofp = ctf_open(argv[2], &err);
if (ofp == NULL) {
- (void) fprintf(stderr, "ctfdiff: failed to open %s: %s\n",
- argv[2], ctf_errmsg(err));
- return (CTFDIFF_EXIT_ERROR);
+ ctfdiff_fatal("failed to open %s: %s\n", argv[2],
+ ctf_errmsg(err));
}
if (pofp != NULL) {
err = ctf_import(ofp, pofp);
if (err != 0) {
- (void) fprintf(stderr, "ctfdiff: failed to set parent "
- "container: %s\n", ctf_errmsg(ctf_errno(pofp)));
- return (CTFDIFF_EXIT_ERROR);
+ ctfdiff_fatal("failed to set parent container: %s\n",
+ ctf_errmsg(ctf_errno(pofp)));
}
}
g_oname = argv[2];
g_ofp = ofp;
if (ctf_diff_init(ifp, ofp, &cdp) != 0) {
- (void) fprintf(stderr,
- "ctfdiff: failed to initialize libctf diff engine: %s\n",
+ ctfdiff_fatal("failed to initialize libctf diff engine: %s\n",
ctf_errmsg(ctf_errno(ifp)));
- return (CTFDIFF_EXIT_ERROR);
}
if (ctf_diff_setflags(cdp, flags) != 0) {
- (void) fprintf(stderr,
- "ctfdiff: failed to set ctfdiff flags: %s\n",
+ ctfdiff_fatal("failed to set ctfdiff flags: %s\n",
ctf_errmsg(ctf_errno(ifp)));
}
- err = ctf_diff_types(cdp, diff_cb, NULL);
+ err = 0;
+ if ((g_flag & CTF_DIFF_TYPES) && err != CTF_ERR)
+ err = ctf_diff_types(cdp, diff_cb, NULL);
+ if ((g_flag & CTF_DIFF_FUNCS) && err != CTF_ERR)
+ err = ctf_diff_functions(cdp, diff_func_cb, NULL);
+ if ((g_flag & CTF_DIFF_OBJS) && err != CTF_ERR)
+ err = ctf_diff_objects(cdp, diff_obj_cb, NULL);
+ if ((g_flag & CTF_DIFF_LABEL) && err != CTF_ERR)
+ err = diff_labels(ifp, ofp);
+
ctf_diff_fini(cdp);
if (err == CTF_ERR) {
- (void) fprintf(stderr, "encountered a libctf error: %s!\n",
+ ctfdiff_fatal("encountered a libctf error: %s!\n",
ctf_errmsg(ctf_errno(ifp)));
- return (CTFDIFF_EXIT_ERROR);
}
return (g_different == B_TRUE ? CTFDIFF_EXIT_DIFFERENT :
diff --git a/usr/src/cmd/mdb/intel/ia32/libctf/Makefile b/usr/src/cmd/ctfdump/Makefile
index 16923da4fe..962ca43a8f 100644
--- a/usr/src/cmd/mdb/intel/ia32/libctf/Makefile
+++ b/usr/src/cmd/ctfdump/Makefile
@@ -10,16 +10,24 @@
#
#
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc.
#
-MODULE = libctf.so
-MDBTGT = proc
-MODSRCS = mdb_modctf.c
+PROG= ctfdump
-include ../../../../Makefile.cmd
-include ../../Makefile.ia32
-include ../../../Makefile.module
+include ../Makefile.cmd
-MODSRCS_DIR = ../../../common/modules/ctf
-CPPFLAGS += -I$(SRC)/common/ctf
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/ctfdump/ctfdump.c b/usr/src/cmd/ctfdump/ctfdump.c
new file mode 100644
index 0000000000..327da82d63
--- /dev/null
+++ b/usr/src/cmd/ctfdump/ctfdump.c
@@ -0,0 +1,803 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
+
+/*
+ * Dump information about CTF containers. This was inspired by the original
+ * ctfdump written in tools/ctf, but this has been reimplemented in terms of
+ * libctf.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <libctf.h>
+#include <libgen.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+typedef enum ctfdump_arg {
+ CTFDUMP_OBJECTS = 0x01,
+ CTFDUMP_FUNCTIONS = 0x02,
+ CTFDUMP_HEADER = 0x04,
+ CTFDUMP_LABELS = 0x08,
+ CTFDUMP_STRINGS = 0x10,
+ CTFDUMP_STATS = 0x20,
+ CTFDUMP_TYPES = 0x40,
+ CTFDUMP_DEFAULT = 0x7f,
+ CTFDUMP_OUTPUT = 0x80,
+ CTFDUMP_ALL = 0xff
+} ctfdump_arg_t;
+
+typedef struct ctfdump_stat {
+ ulong_t cs_ndata; /* number of data objects */
+ ulong_t cs_nfuncs; /* number of functions */
+ ulong_t cs_nfuncargs; /* number of function args */
+ ulong_t cs_nfuncmax; /* largest number of args */
+ ulong_t cs_ntypes[CTF_K_MAX]; /* number of types */
+ ulong_t cs_nsmembs; /* number of struct members */
+ ulong_t cs_nsmax; /* largest number of members */
+ ulong_t cs_structsz; /* sum of structures sizes */
+ ulong_t cs_sszmax; /* largest structure */
+ ulong_t cs_numembs; /* number of union members */
+ ulong_t cs_numax; /* largest number of members */
+ ulong_t cs_unionsz; /* sum of unions sizes */
+ ulong_t cs_uszmax; /* largest union */
+ ulong_t cs_nemembs; /* number of enum members */
+ ulong_t cs_nemax; /* largest number of members */
+ ulong_t cs_nstrings; /* number of strings */
+ ulong_t cs_strsz; /* string size */
+ ulong_t cs_strmax; /* longest string */
+} ctfdump_stat_t;
+
+static const char *g_progname;
+static ctfdump_arg_t g_dump;
+static ctf_file_t *g_fp;
+static ctfdump_stat_t g_stats;
+static ctf_id_t *g_fargc;
+static int g_nfargc;
+
+static const char *ctfdump_fpenc[] = {
+ NULL,
+ "SINGLE",
+ "DOUBLE",
+ "COMPLEX",
+ "DCOMPLEX",
+ "LDCOMPLEX",
+ "LDOUBLE",
+ "INTERVAL",
+ "DINTERVAL",
+ "LDINTERVAL",
+ "IMAGINARY",
+ "DIMAGINARY",
+ "LDIMAGINARY"
+};
+
+static const char *ctfdump_knames[CTF_K_MAX+1] = {
+ "unknown", "integer", "float", "pointer",
+ "array", "function", "struct", "union",
+ "enum", "forward", "typedef", "volatile",
+ "const", "restrict", "CTF_K_14", "CTF_K_15",
+ "CTF_K_16", "CTF_K_17", "CTF_K_18", "CTF_K_19",
+ "CTF_K_20", "CTF_K_21", "CTF_K_22", "CTF_K_23",
+ "CTF_K_24", "CTF_K_25", "CTF_K_26", "CTF_K_27",
+ "CTF_K_28", "CTF_K_29", "CTF_K_30", "CTF_K_MAX"
+};
+
+/*
+ * When stats are requested, we have to go through everything. To make our lives
+ * easier, we'll just always allow the code to print everything out, but only
+ * output it if we have actually enabled that section.
+ */
+static void
+ctfdump_printf(ctfdump_arg_t arg, const char *fmt, ...)
+{
+ va_list ap;
+
+ if ((arg & g_dump) == 0)
+ return;
+
+ va_start(ap, fmt);
+ vfprintf(stdout, fmt, ap);
+ va_end(ap);
+}
+
+static void
+ctfdump_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+static void
+ctfdump_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-dfhlsSt] [-p parent] [-u outfile] "
+ "file\n"
+ "\n"
+ "\t-d dump object data\n"
+ "\t-f dump function data\n"
+ "\t-h dump the CTF header\n"
+ "\t-l dump the label table\n"
+ "\t-p use parent to supply additional information\n"
+ "\t-s dump the string table\n"
+ "\t-S dump statistics about the CTF container\n"
+ "\t-t dump type information\n"
+ "\t-u dump uncompressed CTF data to outfile\n",
+ g_progname);
+}
+
+static void
+ctfdump_title(ctfdump_arg_t arg, const char *header)
+{
+ static const char line[] = "----------------------------------------"
+ "----------------------------------------";
+ ctfdump_printf(arg, "\n- %s %.*s\n\n", header, (int)78 - strlen(header),
+ line);
+}
+
+static int
+ctfdump_objects_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
+{
+ int len;
+
+ len = snprintf(NULL, 0, " [%u] %u", g_stats.cs_ndata, id);
+ ctfdump_printf(CTFDUMP_OBJECTS, " [%u] %u %*s%s (%u)\n",
+ g_stats.cs_ndata, id, MAX(15 - len, 0), "", name, symidx);
+ g_stats.cs_ndata++;
+ return (0);
+}
+
+static void
+ctfdump_objects(void)
+{
+ ctfdump_title(CTFDUMP_OBJECTS, "Data Objects");
+ if (ctf_object_iter(g_fp, ctfdump_objects_cb, NULL) == CTF_ERR)
+ ctfdump_fatal("failed to dump objects: %s\n",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static void
+ctfdump_fargs_grow(int nargs)
+{
+ if (g_nfargc < nargs) {
+ g_fargc = realloc(g_fargc, sizeof (ctf_id_t) * nargs);
+ if (g_fargc == NULL)
+ ctfdump_fatal("failed to get memory for %d "
+ "ctf_id_t's\n", nargs);
+ g_nfargc = nargs;
+ }
+}
+
+static int
+ctfdump_functions_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *ctc,
+ void *arg)
+{
+ int i;
+
+ if (ctc->ctc_argc != 0) {
+ ctfdump_fargs_grow(ctc->ctc_argc);
+ if (ctf_func_args(g_fp, symidx, g_nfargc, g_fargc) == CTF_ERR)
+ ctfdump_fatal("failed to get arguments for function "
+ "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ ctfdump_printf(CTFDUMP_FUNCTIONS,
+ " [%lu] %s (%lu) returns: %u args: (", g_stats.cs_nfuncs, name,
+ symidx, ctc->ctc_return);
+ for (i = 0; i < ctc->ctc_argc; i++)
+ ctfdump_printf(CTFDUMP_FUNCTIONS, "%lu%s", g_fargc[i],
+ i + 1 == ctc->ctc_argc ? "" : ", ");
+ if (ctc->ctc_flags & CTF_FUNC_VARARG)
+ ctfdump_printf(CTFDUMP_FUNCTIONS, "%s...",
+ ctc->ctc_argc == 0 ? "" : ", ");
+ ctfdump_printf(CTFDUMP_FUNCTIONS, ")\n");
+
+ g_stats.cs_nfuncs++;
+ g_stats.cs_nfuncargs += ctc->ctc_argc;
+ g_stats.cs_nfuncmax = MAX(ctc->ctc_argc, g_stats.cs_nfuncmax);
+
+ return (0);
+}
+
+static void
+ctfdump_functions(void)
+{
+ ctfdump_title(CTFDUMP_FUNCTIONS, "Functions");
+
+ if (ctf_function_iter(g_fp, ctfdump_functions_cb, NULL) == CTF_ERR)
+ ctfdump_fatal("failed to dump functions: %s\n",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static void
+ctfdump_header(void)
+{
+ const ctf_header_t *hp;
+ const char *parname, *parlabel;
+
+ ctfdump_title(CTFDUMP_HEADER, "CTF Header");
+ ctf_dataptr(g_fp, (const void **)&hp, NULL);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_magic = 0x%04x\n",
+ hp->cth_magic);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_version = %u\n",
+ hp->cth_version);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_flags = 0x%02x\n",
+ ctf_flags(g_fp));
+ parname = ctf_parent_name(g_fp);
+ parlabel = ctf_parent_label(g_fp);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_parlabel = %s\n",
+ parlabel == NULL ? "(anon)" : parlabel);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_parname = %s\n",
+ parname == NULL ? "(anon)" : parname);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_lbloff = %u\n",
+ hp->cth_lbloff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_objtoff = %u\n",
+ hp->cth_objtoff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_funcoff = %u\n",
+ hp->cth_funcoff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_typeoff = %u\n",
+ hp->cth_typeoff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_stroff = %u\n",
+ hp->cth_stroff);
+ ctfdump_printf(CTFDUMP_HEADER, " cth_strlen = %u\n",
+ hp->cth_strlen);
+}
+
+static int
+ctfdump_labels_cb(const char *name, const ctf_lblinfo_t *li, void *arg)
+{
+ ctfdump_printf(CTFDUMP_LABELS, " %5lu %s\n", li->ctb_typeidx, name);
+ return (0);
+}
+
+static void
+ctfdump_labels(void)
+{
+ ctfdump_title(CTFDUMP_LABELS, "Label Table");
+ if (ctf_label_iter(g_fp, ctfdump_labels_cb, NULL) == CTF_ERR)
+ ctfdump_fatal("failed to dump labels: %s\n",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static int
+ctfdump_strings_cb(const char *s, void *arg)
+{
+ size_t len = strlen(s) + 1;
+ ulong_t *stroff = arg;
+ ctfdump_printf(CTFDUMP_STRINGS, " [%lu] %s\n", *stroff,
+ *s == '\0' ? "\\0" : s);
+ *stroff = *stroff + len;
+ g_stats.cs_nstrings++;
+ g_stats.cs_strsz += len;
+ g_stats.cs_strmax = MAX(g_stats.cs_strmax, len);
+ return (0);
+}
+
+static void
+ctfdump_strings(void)
+{
+ ulong_t stroff = 0;
+
+ ctfdump_title(CTFDUMP_STRINGS, "String Table");
+ if (ctf_string_iter(g_fp, ctfdump_strings_cb, &stroff) == CTF_ERR)
+ ctfdump_fatal("failed to dump strings: %s\n",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static void
+ctfdump_stat_int(const char *name, ulong_t value)
+{
+ ctfdump_printf(CTFDUMP_STATS, " %-36s= %lu\n", name, value);
+}
+
+static void
+ctfdump_stat_fp(const char *name, float value)
+{
+ ctfdump_printf(CTFDUMP_STATS, " %-36s= %.2f\n", name, value);
+}
+
+static void
+ctfdump_stats(void)
+{
+ int i;
+ ulong_t sum;
+
+ ctfdump_title(CTFDUMP_STATS, "CTF Statistics");
+
+ ctfdump_stat_int("total number of data objects", g_stats.cs_ndata);
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+ ctfdump_stat_int("total number of functions", g_stats.cs_nfuncs);
+ ctfdump_stat_int("total number of function arguments",
+ g_stats.cs_nfuncargs);
+ ctfdump_stat_int("maximum argument list length", g_stats.cs_nfuncmax);
+ if (g_stats.cs_nfuncs != 0)
+ ctfdump_stat_fp("average argument list length",
+ (float)g_stats.cs_nfuncargs / (float)g_stats.cs_nfuncs);
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+
+ sum = 0;
+ for (i = 0; i < CTF_K_MAX; i++)
+ sum += g_stats.cs_ntypes[i];
+ ctfdump_stat_int("total number of types", sum);
+ ctfdump_stat_int("total number of integers",
+ g_stats.cs_ntypes[CTF_K_INTEGER]);
+ ctfdump_stat_int("total number of floats",
+ g_stats.cs_ntypes[CTF_K_FLOAT]);
+ ctfdump_stat_int("total number of pointers",
+ g_stats.cs_ntypes[CTF_K_POINTER]);
+ ctfdump_stat_int("total number of arrays",
+ g_stats.cs_ntypes[CTF_K_ARRAY]);
+ ctfdump_stat_int("total number of func types",
+ g_stats.cs_ntypes[CTF_K_FUNCTION]);
+ ctfdump_stat_int("total number of structs",
+ g_stats.cs_ntypes[CTF_K_STRUCT]);
+ ctfdump_stat_int("total number of unions",
+ g_stats.cs_ntypes[CTF_K_UNION]);
+ ctfdump_stat_int("total number of enums",
+ g_stats.cs_ntypes[CTF_K_ENUM]);
+ ctfdump_stat_int("total number of forward tags",
+ g_stats.cs_ntypes[CTF_K_FORWARD]);
+ ctfdump_stat_int("total number of typedefs",
+ g_stats.cs_ntypes[CTF_K_TYPEDEF]);
+ ctfdump_stat_int("total number of volatile types",
+ g_stats.cs_ntypes[CTF_K_VOLATILE]);
+ ctfdump_stat_int("total number of const types",
+ g_stats.cs_ntypes[CTF_K_CONST]);
+ ctfdump_stat_int("total number of restrict types",
+ g_stats.cs_ntypes[CTF_K_RESTRICT]);
+ ctfdump_stat_int("total number of unknowns (holes)",
+ g_stats.cs_ntypes[CTF_K_UNKNOWN]);
+
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+ ctfdump_stat_int("total number of struct members", g_stats.cs_nsmembs);
+ ctfdump_stat_int("maximum number of struct members", g_stats.cs_nsmax);
+ ctfdump_stat_int("total size of all structs", g_stats.cs_structsz);
+ ctfdump_stat_int("maximum size of a struct", g_stats.cs_sszmax);
+ if (g_stats.cs_ntypes[CTF_K_STRUCT] != 0) {
+ ctfdump_stat_fp("average number of struct members",
+ (float)g_stats.cs_nsmembs /
+ (float)g_stats.cs_ntypes[CTF_K_STRUCT]);
+ ctfdump_stat_fp("average size of a struct",
+ (float)g_stats.cs_structsz /
+ (float)g_stats.cs_ntypes[CTF_K_STRUCT]);
+ }
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+ ctfdump_stat_int("total number of union members", g_stats.cs_numembs);
+ ctfdump_stat_int("maximum number of union members", g_stats.cs_numax);
+ ctfdump_stat_int("total size of all unions", g_stats.cs_unionsz);
+ ctfdump_stat_int("maximum size of a union", g_stats.cs_uszmax);
+ if (g_stats.cs_ntypes[CTF_K_UNION] != 0) {
+ ctfdump_stat_fp("average number of union members",
+ (float)g_stats.cs_numembs /
+ (float)g_stats.cs_ntypes[CTF_K_UNION]);
+ ctfdump_stat_fp("average size of a union",
+ (float)g_stats.cs_unionsz /
+ (float)g_stats.cs_ntypes[CTF_K_UNION]);
+ }
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+
+ ctfdump_stat_int("total number of enum members", g_stats.cs_nemembs);
+ ctfdump_stat_int("maximum number of enum members", g_stats.cs_nemax);
+ if (g_stats.cs_ntypes[CTF_K_ENUM] != 0) {
+ ctfdump_stat_fp("average number of enum members",
+ (float)g_stats.cs_nemembs /
+ (float)g_stats.cs_ntypes[CTF_K_ENUM]);
+ }
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+
+ ctfdump_stat_int("total number of strings", g_stats.cs_nstrings);
+ ctfdump_stat_int("bytes of string data", g_stats.cs_strsz);
+ ctfdump_stat_int("maximum string length", g_stats.cs_strmax);
+ if (g_stats.cs_nstrings != 0)
+ ctfdump_stat_fp("average string length",
+ (float)g_stats.cs_strsz / (float)g_stats.cs_nstrings);
+ ctfdump_printf(CTFDUMP_STATS, "\n");
+}
+
+static void
+ctfdump_intenc_name(ctf_encoding_t *cte, char *buf, int len)
+{
+ int off = 0;
+ boolean_t space = B_FALSE;
+
+ if (cte->cte_format == 0 || (cte->cte_format &
+ ~(CTF_INT_SIGNED | CTF_INT_CHAR | CTF_INT_BOOL |
+ CTF_INT_VARARGS)) != 0) {
+ (void) snprintf(buf, len, "0x%x", cte->cte_format);
+ return;
+ }
+
+ if (cte->cte_format & CTF_INT_SIGNED) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sSIGNED",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+
+ if (cte->cte_format & CTF_INT_CHAR) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sCHAR",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+
+ if (cte->cte_format & CTF_INT_BOOL) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sBOOL",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+
+ if (cte->cte_format & CTF_INT_VARARGS) {
+ off += snprintf(buf + off, MAX(len - off, 0), "%sVARARGS",
+ space == B_TRUE ? " " : "");
+ space = B_TRUE;
+ }
+}
+
+static int
+ctfdump_member_cb(const char *member, ctf_id_t type, ulong_t off, void *arg)
+{
+ int *count = arg;
+ ctfdump_printf(CTFDUMP_TYPES, "\t%s type=%lu off=%lu\n", member, type,
+ off);
+ *count = *count + 1;
+ return (0);
+}
+
+static int
+ctfdump_enum_cb(const char *name, int value, void *arg)
+{
+ int *count = arg;
+ ctfdump_printf(CTFDUMP_TYPES, "\t%s = %d\n", name, value);
+ *count = *count + 1;
+ return (0);
+}
+
+static int
+ctfdump_types_cb(ctf_id_t id, boolean_t root, void *arg)
+{
+ int kind, i, count;
+ ctf_id_t ref;
+ char name[512], ienc[128];
+ const char *encn;
+ ctf_funcinfo_t ctc;
+ ctf_arinfo_t ar;
+ ctf_encoding_t cte;
+ ssize_t size;
+
+ if ((kind = ctf_type_kind(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("encountered malformed ctf, type %s does not "
+ "have a kind: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+
+ if (ctf_type_name(g_fp, id, name, sizeof (name)) == NULL) {
+ if (ctf_errno(g_fp) != ECTF_NOPARENT)
+ ctfdump_fatal("type %lu missing name: %s\n", id,
+ ctf_errmsg(ctf_errno(g_fp)));
+ (void) snprintf(name, sizeof (name), "(unknown %s)",
+ ctfdump_knames[kind]);
+ }
+
+ g_stats.cs_ntypes[kind]++;
+ if (root == B_TRUE)
+ ctfdump_printf(CTFDUMP_TYPES, " <%lu> ", id);
+ else
+ ctfdump_printf(CTFDUMP_TYPES, " [%lu] ", id);
+
+ switch (kind) {
+ case CTF_K_UNKNOWN:
+ break;
+ case CTF_K_INTEGER:
+ if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR)
+ ctfdump_fatal("failed to get encoding information "
+ "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_intenc_name(&cte, ienc, sizeof (ienc));
+ ctfdump_printf(CTFDUMP_TYPES,
+ "%s encoding=%s offset=%u bits=%u",
+ name, ienc, cte.cte_offset, cte.cte_bits);
+ break;
+ case CTF_K_FLOAT:
+ if (ctf_type_encoding(g_fp, id, &cte) == CTF_ERR)
+ ctfdump_fatal("failed to get encoding information "
+ "for %s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ if (cte.cte_format < 1 || cte.cte_format > 12)
+ encn = "unknown";
+ else
+ encn = ctfdump_fpenc[cte.cte_format];
+ ctfdump_printf(CTFDUMP_TYPES, "%s encoding=%s offset=%u "
+ "bits=%u", name, encn, cte.cte_offset, cte.cte_bits);
+ break;
+ case CTF_K_POINTER:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name,
+ ref);
+ break;
+ case CTF_K_ARRAY:
+ if (ctf_array_info(g_fp, id, &ar) == CTF_ERR)
+ ctfdump_fatal("failed to get array information for "
+ "%s: %s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s contents: %lu, index: %lu",
+ name, ar.ctr_contents, ar.ctr_index);
+ break;
+ case CTF_K_FUNCTION:
+ if (ctf_func_info_by_id(g_fp, id, &ctc) == CTF_ERR)
+ ctfdump_fatal("failed to get function info for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ if (ctc.ctc_argc > 0) {
+ ctfdump_fargs_grow(ctc.ctc_argc);
+ if (ctf_func_args_by_id(g_fp, id, g_nfargc, g_fargc) ==
+ CTF_ERR)
+ ctfdump_fatal("failed to get function "
+ "arguments for %s: %s\n", name,
+ ctf_errmsg(ctf_errno(g_fp)));
+ }
+ ctfdump_printf(CTFDUMP_TYPES,
+ "%s returns: %lu args: (", name, ctc.ctc_return);
+ for (i = 0; i < ctc.ctc_argc; i++) {
+ ctfdump_printf(CTFDUMP_TYPES, "%lu%s", g_fargc[i],
+ i + 1 == ctc.ctc_argc ? "" : ", ");
+ }
+ if (ctc.ctc_flags & CTF_FUNC_VARARG)
+ ctfdump_printf(CTFDUMP_TYPES, "%s...",
+ ctc.ctc_argc == 0 ? "" : ", ");
+ ctfdump_printf(CTFDUMP_TYPES, ")");
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ size = ctf_type_size(g_fp, id);
+ if (size == CTF_ERR)
+ ctfdump_fatal("failed to get size of %s: %s\n", name,
+ ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s (%d bytes)\n", name, size);
+ count = 0;
+ if (ctf_member_iter(g_fp, id, ctfdump_member_cb, &count) != 0)
+ ctfdump_fatal("failed to iterate members of %s: %s\n",
+ name, ctf_errmsg(ctf_errno(g_fp)));
+ if (kind == CTF_K_STRUCT) {
+ g_stats.cs_nsmembs += count;
+ g_stats.cs_nsmax = MAX(count, g_stats.cs_nsmax);
+ g_stats.cs_structsz += size;
+ g_stats.cs_sszmax = MAX(size, g_stats.cs_sszmax);
+ } else {
+ g_stats.cs_numembs += count;
+ g_stats.cs_numax = MAX(count, g_stats.cs_numax);
+ g_stats.cs_unionsz += size;
+ g_stats.cs_uszmax = MAX(count, g_stats.cs_uszmax);
+ }
+ break;
+ case CTF_K_ENUM:
+ ctfdump_printf(CTFDUMP_TYPES, "%s\n", name);
+ count = 0;
+ if (ctf_enum_iter(g_fp, id, ctfdump_enum_cb, &count) != 0)
+ ctfdump_fatal("failed to iterate enumerators of %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ g_stats.cs_nemembs += count;
+ g_stats.cs_nemax = MAX(g_stats.cs_nemax, count);
+ break;
+ case CTF_K_FORWARD:
+ ctfdump_printf(CTFDUMP_TYPES, "forward %s\n", name);
+ break;
+ case CTF_K_TYPEDEF:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "typedef %s refers to %lu", name,
+ ref);
+ break;
+ case CTF_K_VOLATILE:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name,
+ ref);
+ break;
+ case CTF_K_CONST:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name,
+ ref);
+ break;
+ case CTF_K_RESTRICT:
+ if ((ref = ctf_type_reference(g_fp, id)) == CTF_ERR)
+ ctfdump_fatal("failed to get reference type for %s: "
+ "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
+ ctfdump_printf(CTFDUMP_TYPES, "%s refers to %lu", name,
+ ref);
+ break;
+ default:
+ ctfdump_fatal("encountered unknown kind for type %s: %d\n",
+ name, kind);
+ }
+
+ ctfdump_printf(CTFDUMP_TYPES, "\n");
+
+ return (0);
+}
+
+static void
+ctfdump_types(void)
+{
+ ctfdump_title(CTFDUMP_TYPES, "Types");
+
+ if (ctf_type_iter(g_fp, B_TRUE, ctfdump_types_cb, NULL) == CTF_ERR)
+ ctfdump_fatal("failed to dump labels: %s\n",
+ ctf_errmsg(ctf_errno(g_fp)));
+}
+
+static void
+ctfdump_output(const char *out)
+{
+ int fd, ret;
+ const void *data;
+ size_t len;
+
+ ctf_dataptr(g_fp, &data, &len);
+ if ((fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0)
+ ctfdump_fatal("failed to open output file %s: %s\n", out,
+ strerror(errno));
+
+ while (len > 0) {
+ ret = write(fd, data, len);
+ if (ret == -1 && errno == EINTR)
+ continue;
+ else if (ret == -1 && (errno == EFAULT || errno == EBADF))
+ abort();
+ else if (ret == -1)
+ ctfdump_fatal("failed to write to %s: %s\n", out,
+ strerror(errno));
+ data += ret;
+ len -= ret;
+ }
+
+ do {
+ ret = close(fd);
+ } while (ret == -1 && errno == EINTR);
+ if (ret != 0 && errno == EBADF)
+ abort();
+ if (ret != 0)
+ ctfdump_fatal("failed to close %s: %s\n", out, strerror(errno));
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, fd, err;
+ const char *ufile = NULL, *parent = NULL;
+
+ g_progname = basename(argv[0]);
+ while ((c = getopt(argc, argv, ":dfhlp:sStu:")) != -1) {
+ switch (c) {
+ case 'd':
+ g_dump |= CTFDUMP_OBJECTS;
+ break;
+ case 'f':
+ g_dump |= CTFDUMP_FUNCTIONS;
+ break;
+ case 'h':
+ g_dump |= CTFDUMP_HEADER;
+ break;
+ case 'l':
+ g_dump |= CTFDUMP_LABELS;
+ break;
+ case 'p':
+ parent = optarg;
+ break;
+ case 's':
+ g_dump |= CTFDUMP_STRINGS;
+ break;
+ case 'S':
+ g_dump |= CTFDUMP_STATS;
+ break;
+ case 't':
+ g_dump |= CTFDUMP_TYPES;
+ break;
+ case 'u':
+ g_dump |= CTFDUMP_OUTPUT;
+ ufile = optarg;
+ break;
+ case '?':
+ ctfdump_usage("Option -%c requires an operand\n",
+ optopt);
+ return (2);
+ case ':':
+ ctfdump_usage("Unknown option: -%c\n", optopt);
+ return (2);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Dump all information by default.
+ */
+ if (g_dump == 0)
+ g_dump = CTFDUMP_DEFAULT;
+
+ if (argc != 1) {
+ ctfdump_usage("no file to dump\n");
+ return (2);
+ }
+
+ if ((fd = open(argv[0], O_RDONLY)) < 0)
+ ctfdump_fatal("failed to open file %s: %s\n", argv[0],
+ strerror(errno));
+
+ g_fp = ctf_fdopen(fd, &err);
+ if (g_fp == NULL)
+ ctfdump_fatal("failed to open file %s: %s\n", argv[0],
+ ctf_errmsg(err));
+
+ if (parent != NULL) {
+ ctf_file_t *pfp = ctf_open(parent, &err);
+
+ if (pfp == NULL)
+ ctfdump_fatal("failed to open parent file %s: %s\n",
+ parent, ctf_errmsg(err));
+ if (ctf_import(g_fp, pfp) != 0)
+ ctfdump_fatal("failed to import parent %s: %s\n",
+ parent, ctf_errmsg(ctf_errno(g_fp)));
+ }
+
+ /*
+ * If stats is set, we must run through everything exect CTFDUMP_OUTPUT.
+ * We also do CTFDUMP_STATS last as a result.
+ */
+ if (g_dump & CTFDUMP_HEADER)
+ ctfdump_header();
+
+ if (g_dump & (CTFDUMP_LABELS | CTFDUMP_STATS))
+ ctfdump_labels();
+
+ if (g_dump & (CTFDUMP_OBJECTS | CTFDUMP_STATS))
+ ctfdump_objects();
+
+ if (g_dump & (CTFDUMP_FUNCTIONS | CTFDUMP_STATS))
+ ctfdump_functions();
+
+ if (g_dump & (CTFDUMP_TYPES | CTFDUMP_STATS))
+ ctfdump_types();
+
+ if (g_dump & (CTFDUMP_STRINGS | CTFDUMP_STATS))
+ ctfdump_strings();
+
+ if (g_dump & CTFDUMP_STATS)
+ ctfdump_stats();
+
+ if (g_dump & CTFDUMP_OUTPUT)
+ ctfdump_output(ufile);
+
+ return (0);
+}
diff --git a/usr/src/cmd/mdb/sparc/v7/libctf/Makefile b/usr/src/cmd/ctfmerge/Makefile
index 477cf40df5..02ead98687 100644
--- a/usr/src/cmd/mdb/sparc/v7/libctf/Makefile
+++ b/usr/src/cmd/ctfmerge/Makefile
@@ -10,16 +10,24 @@
#
#
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc.
#
-MODULE = libctf.so
-MDBTGT = proc
-MODSRCS = mdb_modctf.c
+PROG= ctfmerge
-include ../../../../Makefile.cmd
-include ../../Makefile.sparcv7
-include ../../../Makefile.module
+include ../Makefile.cmd
-MODSRCS_DIR = ../../../common/modules/ctf
-CPPFLAGS += -I$(SRC)/common/ctf
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf -lelf
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/ctfmerge/ctfmerge.c b/usr/src/cmd/ctfmerge/ctfmerge.c
new file mode 100644
index 0000000000..71f00d12af
--- /dev/null
+++ b/usr/src/cmd/ctfmerge/ctfmerge.c
@@ -0,0 +1,487 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
+
+/*
+ * merge CTF containers
+ */
+
+#include <stdio.h>
+#include <libctf.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <strings.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <stdlib.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <sys/mman.h>
+#include <libgen.h>
+#include <stdarg.h>
+
+static char *g_progname;
+static char *g_unique;
+static char *g_outfile;
+static boolean_t g_req;
+static uint_t g_nctf;
+
+static void
+ctfmerge_fatal(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (g_outfile != NULL)
+ (void) unlink(g_outfile);
+
+ exit(1);
+}
+
+static boolean_t
+ctfmerge_expect_ctf(const char *name, Elf *elf)
+{
+ Elf_Scn *scn, *strscn;
+ Elf_Data *data, *strdata;
+ GElf_Shdr shdr;
+ ulong_t i;
+
+ if (g_req == B_FALSE)
+ return (B_FALSE);
+
+ scn = NULL;
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ ctfmerge_fatal("failed to get section header for file "
+ "%s: %s\n", name, elf_errmsg(elf_errno()));
+ }
+
+ if (shdr.sh_type == SHT_SYMTAB)
+ break;
+ }
+
+ if (scn == NULL)
+ return (B_FALSE);
+
+ if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL)
+ ctfmerge_fatal("failed to get section header for file %s: %s\n",
+ name, elf_errmsg(elf_errno()));
+
+ if ((data = elf_getdata(scn, NULL)) == NULL)
+ ctfmerge_fatal("failed to read symbol table for %s: %s\n",
+ name, elf_errmsg(elf_errno()));
+
+ if ((strdata = elf_getdata(strscn, NULL)) == NULL)
+ ctfmerge_fatal("failed to read string table for %s: %s\n",
+ name, elf_errmsg(elf_errno()));
+
+ for (i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) {
+ GElf_Sym sym;
+ const char *file;
+ size_t len;
+
+ if (gelf_getsym(data, i, &sym) == NULL)
+ ctfmerge_fatal("failed to read symbol table entry %d "
+ "for %s: %s\n", i, name, elf_errmsg(elf_errno()));
+
+ if (GELF_ST_TYPE(sym.st_info) != STT_FILE)
+ continue;
+
+ file = (const char *)((uintptr_t)strdata->d_buf + sym.st_name);
+ len = strlen(file);
+ if (len < 2 || name[len - 2] != '.')
+ continue;
+
+ if (name[len - 1] == 'c')
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+/*
+ * Go through and construct enough information for this Elf Object to try and do
+ * a ctf_bufopen().
+ */
+static void
+ctfmerge_elfopen(const char *name, Elf *elf, ctf_merge_t *cmh)
+{
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Scn *scn;
+ Elf_Data *ctf_data, *str_data, *sym_data;
+ ctf_sect_t ctfsect, symsect, strsect;
+ ctf_file_t *fp;
+ int err;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ ctfmerge_fatal("failed to get ELF header for %s: %s\n",
+ name, elf_errmsg(elf_errno()));
+
+ bzero(&ctfsect, sizeof (ctf_sect_t));
+ bzero(&symsect, sizeof (ctf_sect_t));
+ bzero(&strsect, sizeof (ctf_sect_t));
+
+ scn = NULL;
+ while ((scn = elf_nextscn(elf, scn)) != NULL) {
+ const char *sname;
+
+ if (gelf_getshdr(scn, &shdr) == NULL)
+ ctfmerge_fatal("failed to get section header for "
+ "file %s: %s\n", name, elf_errmsg(elf_errno()));
+
+ sname = elf_strptr(elf, ehdr.e_shstrndx, shdr.sh_name);
+ if (shdr.sh_type == SHT_PROGBITS &&
+ strcmp(sname, ".SUNW_ctf") == 0) {
+ ctfsect.cts_name = sname;
+ ctfsect.cts_type = shdr.sh_type;
+ ctfsect.cts_flags = shdr.sh_flags;
+ ctfsect.cts_size = shdr.sh_size;
+ ctfsect.cts_entsize = shdr.sh_entsize;
+ ctfsect.cts_offset = (off64_t)shdr.sh_offset;
+
+ ctf_data = elf_getdata(scn, NULL);
+ if (ctf_data == NULL)
+ ctfmerge_fatal("failed to get ELF CTF "
+ "data section for %s: %s\n", name,
+ elf_errmsg(elf_errno()));
+ ctfsect.cts_data = ctf_data->d_buf;
+ } else if (shdr.sh_type == SHT_SYMTAB) {
+ Elf_Scn *strscn;
+ GElf_Shdr strhdr;
+
+ symsect.cts_name = sname;
+ symsect.cts_type = shdr.sh_type;
+ symsect.cts_flags = shdr.sh_flags;
+ symsect.cts_size = shdr.sh_size;
+ symsect.cts_entsize = shdr.sh_entsize;
+ symsect.cts_offset = (off64_t)shdr.sh_offset;
+
+ if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL ||
+ gelf_getshdr(strscn, &strhdr) == NULL)
+ ctfmerge_fatal("failed to get "
+ "string table for file %s: %s\n", name,
+ elf_errmsg(elf_errno()));
+
+ strsect.cts_name = elf_strptr(elf, ehdr.e_shstrndx,
+ strhdr.sh_name);
+ strsect.cts_type = strhdr.sh_type;
+ strsect.cts_flags = strhdr.sh_flags;
+ strsect.cts_size = strhdr.sh_size;
+ strsect.cts_entsize = strhdr.sh_entsize;
+ strsect.cts_offset = (off64_t)strhdr.sh_offset;
+
+ sym_data = elf_getdata(scn, NULL);
+ if (sym_data == NULL)
+ ctfmerge_fatal("failed to get ELF CTF "
+ "data section for %s: %s\n", name,
+ elf_errmsg(elf_errno()));
+ symsect.cts_data = sym_data->d_buf;
+
+ str_data = elf_getdata(strscn, NULL);
+ if (str_data == NULL)
+ ctfmerge_fatal("failed to get ELF CTF "
+ "data section for %s: %s\n", name,
+ elf_errmsg(elf_errno()));
+ strsect.cts_data = str_data->d_buf;
+ }
+ }
+
+ if (ctfsect.cts_type == SHT_NULL) {
+ if (ctfmerge_expect_ctf(name, elf) == B_FALSE)
+ return;
+ ctfmerge_fatal("failed to open %s: %s\n", name,
+ ctf_errmsg(ECTF_NOCTFDATA));
+ }
+
+ if (symsect.cts_type != SHT_NULL && strsect.cts_type != SHT_NULL) {
+ fp = ctf_bufopen(&ctfsect, &symsect, &strsect, &err);
+ } else {
+ fp = ctf_bufopen(&ctfsect, NULL, NULL, &err);
+ }
+
+ if (fp == NULL) {
+ if (ctfmerge_expect_ctf(name, elf) == B_TRUE) {
+ ctfmerge_fatal("failed to open file %s: %s\n",
+ name, ctf_errmsg(err));
+ }
+ } else {
+ if (ctf_merge_add(cmh, fp) != 0) {
+ ctfmerge_fatal("failed to add input %s: %s\n",
+ name, ctf_errmsg(ctf_errno(fp)));
+ exit(1);
+ }
+ g_nctf++;
+ }
+}
+
+static void
+ctfmerge_read_archive(const char *name, int fd, Elf *elf,
+ ctf_merge_t *cmh)
+{
+ Elf *aelf;
+ Elf_Cmd cmd = ELF_C_READ;
+ int cursec = 1;
+ char *nname;
+
+ while ((aelf = elf_begin(fd, cmd, elf)) != NULL) {
+ Elf_Arhdr *arhdr;
+ boolean_t leakelf = B_FALSE;
+
+ if ((arhdr = elf_getarhdr(aelf)) == NULL)
+ ctfmerge_fatal("failed to get archive header %d for "
+ "%s: %s\n", cursec, name, elf_errmsg(elf_errno()));
+
+ if (*(arhdr->ar_name) == '/')
+ goto next;
+
+ if (asprintf(&nname, "%s.%s.%d", name, arhdr->ar_name,
+ cursec) < 0)
+ ctfmerge_fatal("failed to allocate memory for archive "
+ "%d of file %s\n", cursec, name);
+
+ switch (elf_kind(aelf)) {
+ case ELF_K_AR:
+ ctfmerge_read_archive(nname, fd, aelf, cmh);
+ free(nname);
+ break;
+ case ELF_K_ELF:
+ ctfmerge_elfopen(nname, aelf, cmh);
+ free(nname);
+ leakelf = B_TRUE;
+ break;
+ default:
+ ctfmerge_fatal("unknown elf kind (%d) in archive %d "
+ "for %s\n", elf_kind(aelf), cursec, name);
+ }
+
+next:
+ cmd = elf_next(aelf);
+ if (leakelf == B_FALSE)
+ (void) elf_end(aelf);
+ cursec++;
+ }
+}
+
+static void
+ctfmerge_usage(const char *fmt, ...)
+{
+ if (fmt != NULL) {
+ va_list ap;
+
+ (void) fprintf(stderr, "%s: ", g_progname);
+ va_start(ap, fmt);
+ (void) vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
+ (void) fprintf(stderr, "Usage: %s [-gt] [-d uniqfile] [-l label] "
+ "[-L labelenv] -o outfile file ...\n"
+ "\n"
+ "\t-d uniquify merged output against uniqfile\n"
+ "\t-g do not remove source debug information (STABS, DWARF)\n"
+ "\t-l set output container's label to specified value\n"
+ "\t-L set output container's label to value from environment\n"
+ "\t-o file to add CTF data to\n"
+ "\t-t require CTF data from all inputs built from C sources\n",
+ g_progname);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int err, i, c, ofd;
+ char *tmpfile = NULL, *label = NULL;
+ int wflags = CTF_ELFWRITE_F_COMPRESS;
+ ctf_file_t *ofp;
+ ctf_merge_t *cmh;
+
+ g_progname = basename(argv[0]);
+
+ /*
+ * We support a subset of the old CTF merge flags, mostly for
+ * compatability.
+ */
+ while ((c = getopt(argc, argv, ":d:fgL:o:t")) != -1) {
+ switch (c) {
+ case 'd':
+ g_unique = optarg;
+ break;
+ case 'f':
+ /* Silently ignored for compatibility */
+ break;
+ case 'g':
+ wflags |= CTF_ELFWRITE_F_KEEP_STABS;
+ break;
+ case 'l':
+ label = optarg;
+ break;
+ case 'L':
+ label = getenv(optarg);
+ break;
+ case 'o':
+ g_outfile = optarg;
+ break;
+ case 't':
+ g_req = B_TRUE;
+ break;
+ case ':':
+ ctfmerge_usage("ctfmerge: Option -%c requires an "
+ "operand\n", optopt);
+ return (2);
+ case '?':
+ ctfmerge_usage("Unknown option: -%c\n", optopt);
+ return (2);
+ }
+ }
+
+ if (g_outfile == NULL) {
+ ctfmerge_usage("missing required -o output file\n");
+ return (2);
+ }
+
+ (void) elf_version(EV_CURRENT);
+
+ /*
+ * Obviously this isn't atomic, but at least gives us a good starting
+ * point.
+ */
+ if ((ofd = open(g_outfile, O_RDWR)) < 0)
+ ctfmerge_fatal("cannot open output file %s: %s\n", g_outfile,
+ strerror(errno));
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ ctfmerge_usage("no input files specified");
+ return (2);
+ }
+
+ cmh = ctf_merge_init(ofd, &err);
+ if (cmh == NULL)
+ ctfmerge_fatal("failed to create merge handle: %s\n",
+ ctf_errmsg(err));
+
+ for (i = 0; i < argc; i++) {
+ ctf_file_t *ifp;
+ int fd;
+
+ if ((fd = open(argv[i], O_RDONLY)) < 0)
+ ctfmerge_fatal("failed to open file %s: %s\n",
+ argv[i], strerror(errno));
+ ifp = ctf_fdopen(fd, &err);
+ if (ifp == NULL) {
+ Elf *e;
+
+ if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
+ (void) close(fd);
+ ctfmerge_fatal("failed to open %s: %s\n",
+ argv[i], ctf_errmsg(err));
+ }
+
+ /*
+ * It's an ELF file, check if we have an archive or if
+ * we're expecting CTF here.
+ */
+ switch (elf_kind(e)) {
+ case ELF_K_AR:
+ break;
+ case ELF_K_ELF:
+ if (ctfmerge_expect_ctf(argv[i], e) == B_TRUE) {
+ (void) elf_end(e);
+ (void) close(fd);
+ ctfmerge_fatal("failed to "
+ "open %s: file was built from C "
+ "sources, but missing CTF\n",
+ argv[i]);
+ }
+ (void) elf_end(e);
+ (void) close(fd);
+ continue;
+ default:
+ (void) elf_end(e);
+ (void) close(fd);
+ ctfmerge_fatal("failed to open %s: "
+ "unsupported ELF file type", argv[i]);
+ }
+
+ ctfmerge_read_archive(argv[i], fd, e, cmh);
+ (void) elf_end(e);
+ (void) close(fd);
+ continue;
+ }
+ (void) close(fd);
+ if ((err = ctf_merge_add(cmh, ifp)) != 0)
+ ctfmerge_fatal("failed to add input %s: %s\n",
+ argv[i], ctf_errmsg(err));
+ g_nctf++;
+ }
+
+ if (g_nctf == 0) {
+ ctf_merge_fini(cmh);
+ return (0);
+ }
+
+ if (g_unique != NULL) {
+ ctf_file_t *ufp;
+ char *base;
+
+ ufp = ctf_open(g_unique, &err);
+ if (ufp == NULL) {
+ ctfmerge_fatal("failed to open uniquify file %s: %s\n",
+ g_unique, ctf_errmsg(err));
+ return (1);
+ }
+
+ base = basename(g_unique);
+ (void) ctf_merge_uniquify(cmh, ufp, base);
+ }
+
+ if (label != NULL) {
+ if ((err = ctf_merge_label(cmh, label)) != 0)
+ ctfmerge_fatal("failed to add label %s: %s\n", label,
+ ctf_errmsg(err));
+ }
+
+ err = ctf_merge_merge(cmh, &ofp);
+ if (err != 0)
+ ctfmerge_fatal("failed to merge types: %s\n", ctf_errmsg(err));
+ ctf_merge_fini(cmh);
+
+ if (asprintf(&tmpfile, "%s.ctf", g_outfile) == -1)
+ ctfmerge_fatal("ran out of memory for temporary file name\n");
+ err = ctf_elfwrite(ofp, g_outfile, tmpfile, wflags);
+ free(tmpfile);
+ if (err == CTF_ERR) {
+ (void) unlink(tmpfile);
+ ctfmerge_fatal("encountered a libctf error: %s!\n",
+ ctf_errmsg(ctf_errno(ofp)));
+ }
+
+ if (rename(tmpfile, g_outfile) != 0) {
+ (void) unlink(tmpfile);
+ ctfmerge_fatal("failed to rename temporary file: %s\n",
+ strerror(errno));
+ }
+
+ return (0);
+}
diff --git a/usr/src/cmd/mdb/Makefile.common b/usr/src/cmd/mdb/Makefile.common
index ea319d65ae..b78836c741 100644
--- a/usr/src/cmd/mdb/Makefile.common
+++ b/usr/src/cmd/mdb/Makefile.common
@@ -28,7 +28,6 @@ COMMON_MODULES_PROC = \
dof \
libavl \
libc \
- libctf \
libcmdutils \
libnvpair \
libproc \
diff --git a/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c
index 81ac1eacfc..f39fb83c9b 100644
--- a/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c
+++ b/usr/src/cmd/mdb/common/kmdb/kmdb_promif.c
@@ -742,10 +742,10 @@ kmdb_prom_debugger_exit(void)
mdb.m_pio = NULL;
}
-#ifdef DEBUG
/*
- * The prom_* files use ASSERT, which is #defined as assfail().
- * We need to redirect that to our assert function.
+ * The prom_* files use ASSERT, which is #defined as assfail(). We need to
+ * redirect that to our assert function. This is also used by the various STAND
+ * libraries.
*/
int
kmdb_prom_assfail(const char *assertion, const char *file, int line)
@@ -754,7 +754,6 @@ kmdb_prom_assfail(const char *assertion, const char *file, int line)
/*NOTREACHED*/
return (0);
}
-#endif
/*
* Begin the initialization of the debugger/PROM interface. Initialization is
diff --git a/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c b/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c
index 0a211fcd22..e8c703e754 100644
--- a/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c
+++ b/usr/src/cmd/mdb/common/libstandctf/ctf_subr.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_debug.h>
#include <mdb/mdb.h>
@@ -67,6 +65,13 @@ ctf_fdopen(int fd, int *errp)
/*ARGSUSED*/
ctf_file_t *
+ctf_fdcreate_int(int fd, int *errp, ctf_sect_t *ctfp)
+{
+ return (ctf_set_open_errno(errp, ENOTSUP));
+}
+
+/*ARGSUSED*/
+ctf_file_t *
ctf_open(const char *filename, int *errp)
{
return (ctf_set_open_errno(errp, ENOTSUP));
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
index 1ae0952619..dc0aabd308 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_ctf.c
@@ -24,7 +24,7 @@
*/
/*
* Copyright (c) 2013 by Delphix. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <mdb/mdb_ctf.h>
@@ -37,6 +37,7 @@
#include <libctf.h>
#include <string.h>
+#include <limits.h>
typedef struct tnarg {
mdb_tgt_t *tn_tgt; /* target to use for lookup */
@@ -781,8 +782,9 @@ mdb_ctf_enum_iter(mdb_ctf_id_t id, mdb_ctf_enum_f *cb, void *data)
/*
* callback proxy for mdb_ctf_type_iter
*/
+/* ARGSUSED */
static int
-type_iter_cb(ctf_id_t type, void *data)
+type_iter_cb(ctf_id_t type, boolean_t root, void *data)
{
type_iter_t *tip = data;
mdb_ctf_id_t id;
@@ -812,7 +814,7 @@ mdb_ctf_type_iter(const char *object, mdb_ctf_type_f *cb, void *data)
ti.ti_arg = data;
ti.ti_fp = fp;
- if ((ret = ctf_type_iter(fp, type_iter_cb, &ti)) == CTF_ERR)
+ if ((ret = ctf_type_iter(fp, B_FALSE, type_iter_cb, &ti)) == CTF_ERR)
return (set_errno(ctf_to_errno(ctf_errno(fp))));
return (ret);
@@ -1938,7 +1940,7 @@ mdb_ctf_add_member(const mdb_ctf_id_t *p, const char *name,
return (set_errno(ctf_to_errno(ctf_errno(mdb.m_synth))));
}
- id = ctf_add_member(mdb.m_synth, mcip->mci_id, name, mtid);
+ id = ctf_add_member(mdb.m_synth, mcip->mci_id, name, mtid, ULONG_MAX);
if (id == CTF_ERR) {
mdb_dprintf(MDB_DBG_CTF, "failed to add member %s: %s\n",
name, ctf_errmsg(ctf_errno(mdb.m_synth)));
@@ -2039,7 +2041,7 @@ mdb_ctf_add_pointer(const mdb_ctf_id_t *p, mdb_ctf_id_t *rid)
}
- id = ctf_add_pointer(mdb.m_synth, CTF_ADD_ROOT, id);
+ id = ctf_add_pointer(mdb.m_synth, CTF_ADD_ROOT, NULL, id);
if (id == CTF_ERR) {
mdb_dprintf(MDB_DBG_CTF, "failed to add pointer: %s\n",
ctf_errmsg(ctf_errno(mdb.m_synth)));
@@ -2087,6 +2089,7 @@ mdb_ctf_type_delete(const mdb_ctf_id_t *id)
return (0);
}
+/* ARGSUSED */
static int
mdb_ctf_synthetics_file_cb(mdb_ctf_id_t id, void *arg)
{
@@ -2124,7 +2127,7 @@ mdb_ctf_synthetics_from_file(const char *file)
ti.ti_fp = fp;
ti.ti_arg = syn;
ti.ti_cb = mdb_ctf_synthetics_file_cb;
- if (ctf_type_iter(fp, type_iter_cb, &ti) == CTF_ERR) {
+ if (ctf_type_iter(fp, B_TRUE, type_iter_cb, &ti) == CTF_ERR) {
ret = set_errno(ctf_to_errno(ctf_errno(fp)));
mdb_warn("failed to add types");
goto cleanup;
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_debug.c b/usr/src/cmd/mdb/common/mdb/mdb_debug.c
index d373d3726e..a158864a28 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_debug.c
+++ b/usr/src/cmd/mdb/common/mdb/mdb_debug.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <mdb/mdb_debug.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_io.h>
@@ -157,7 +155,6 @@ mdb_dmode(uint_t bits)
mdb.m_debug = bits;
}
-#ifdef DEBUG
int
mdb_dassert(const char *expr, const char *file, int line)
{
@@ -165,7 +162,6 @@ mdb_dassert(const char *expr, const char *file, int line)
/*NOTREACHED*/
return (0);
}
-#endif
/*
* Function to convert mdb longjmp codes (see <mdb/mdb.h>) into a string for
diff --git a/usr/src/cmd/mdb/common/mdb/mdb_debug.h b/usr/src/cmd/mdb/common/mdb/mdb_debug.h
index cf3b97b499..f889238e4f 100644
--- a/usr/src/cmd/mdb/common/mdb/mdb_debug.h
+++ b/usr/src/cmd/mdb/common/mdb/mdb_debug.h
@@ -27,8 +27,6 @@
#ifndef _MDB_DEBUG_H
#define _MDB_DEBUG_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -66,8 +64,8 @@ extern void mdb_dmode(uint_t);
extern const char *mdb_err2str(int);
-#ifdef DEBUG
extern int mdb_dassert(const char *, const char *, int);
+#ifdef DEBUG
#define ASSERT(x) ((void)((x) || mdb_dassert(#x, __FILE__, __LINE__)))
#else
#define ASSERT(x)
diff --git a/usr/src/cmd/mdb/common/modules/ctf/mdb_modctf.c b/usr/src/cmd/mdb/common/modules/ctf/mdb_modctf.c
deleted file mode 100644
index dcaa078457..0000000000
--- a/usr/src/cmd/mdb/common/modules/ctf/mdb_modctf.c
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * This file and its contents are supplied under the terms of the
- * Common Development and Distribution License ("CDDL"), version 1.0.
- * You may only use this file in accordance with the terms of version
- * 1.0 of the CDDL.
- *
- * A full copy of the text of the CDDL should have accompanied this
- * source. A copy of the CDDL is also available via the Internet at
- * http://www.illumos.org/license/CDDL.
- */
-
-/*
- * Copyright (c) 2013 Joyent, Inc. All rights reserved.
- */
-
-/*
- * Routines for debugging ctf containers
- */
-
-#include <mdb/mdb_modapi.h>
-#include <ctf_impl.h>
-
-static int
-mdb_ctf_idhash_walk_init(mdb_walk_state_t *wsp)
-{
- ctf_idhash_t *ihp;
-
- if (wsp->walk_addr == NULL) {
- mdb_warn("ctf_idhash walker does not support global walks\n");
- return (WALK_ERR);
- }
-
- ihp = mdb_alloc(sizeof (ctf_idhash_t), UM_NOSLEEP | UM_GC);
- if (ihp == NULL) {
- mdb_warn("failed to allocate memory for a ctf_idhash_t");
- return (WALK_ERR);
- }
-
- if (mdb_vread(ihp, sizeof (ctf_idhash_t), wsp->walk_addr) !=
- sizeof (ctf_idhash_t)) {
- mdb_warn("failed to read ctf_idhash_t at %p", wsp->walk_addr);
- return (WALK_ERR);
- }
-
- if (ihp->ih_free == 0)
- return (WALK_DONE);
- wsp->walk_data = ihp;
- wsp->walk_arg = (void *)(uintptr_t)1;
-
- return (WALK_NEXT);
-}
-
-static int
-mdb_ctf_idhash_walk_step(mdb_walk_state_t *wsp)
-{
- ctf_ihelem_t ihe;
- ctf_idhash_t *ihp = wsp->walk_data;
- int index = (uintptr_t)wsp->walk_arg;
-
- if (index == ihp->ih_free)
- return (WALK_DONE);
-
- if (mdb_vread(&ihe, sizeof (ihe),
- (uintptr_t)(ihp->ih_chains + index)) != sizeof (ihe)) {
- mdb_warn("failed to read index %d at %p", index,
- ihp->ih_chains + index);
- return (WALK_ERR);
- }
- wsp->walk_arg = (void *)(uintptr_t)(index + 1);
- return (wsp->walk_callback((uintptr_t)(ihp->ih_chains + index), &ihe,
- wsp->walk_cbdata));
-}
-
-static const mdb_walker_t walkers[] = {
- { "ctf_idhash", "walk entries in a ctf idhash",
- mdb_ctf_idhash_walk_init, mdb_ctf_idhash_walk_step },
- { NULL }
-};
-
-static const mdb_modinfo_t modinfo = { MDB_API_VERSION, NULL, walkers };
-
-const mdb_modinfo_t *
-_mdb_init(void)
-{
- return (&modinfo);
-}
diff --git a/usr/src/common/ctf/ctf_create.c b/usr/src/common/ctf/ctf_create.c
index f9feca81cf..b51765dd0c 100644
--- a/usr/src/common/ctf/ctf_create.c
+++ b/usr/src/common/ctf/ctf_create.c
@@ -25,7 +25,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
*/
#include <sys/sysmacros.h>
@@ -86,6 +86,43 @@ ctf_create(int *errp)
return (fp);
}
+ctf_file_t *
+ctf_fdcreate(int fd, int *errp)
+{
+ ctf_file_t *fp;
+ 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;
+
+ 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_fdcreate_int(fd, errp, &cts)) == 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)
{
@@ -236,14 +273,23 @@ int
ctf_update(ctf_file_t *fp)
{
ctf_file_t ofp, *nfp;
- ctf_header_t hdr;
+ ctf_header_t hdr, *bhdr;
ctf_dtdef_t *dtd;
- ctf_sect_t cts;
+ ctf_dsdef_t *dsd;
+ ctf_dldef_t *dld;
+ ctf_sect_t cts, *symp, *strp;
uchar_t *s, *s0, *t;
- size_t size;
+ ctf_lblent_t *label;
+ uint16_t *obj, *func;
+ size_t size, objsize, funcsize, labelsize, plen;
void *buf;
int err;
+ ulong_t i;
+ const char *plabel;
+
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
if (!(fp->ctf_flags & LCTF_RDWR))
return (ctf_set_errno(fp, ECTF_RDONLY));
@@ -261,8 +307,26 @@ ctf_update(ctf_file_t *fp)
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] */
+ if (fp->ctf_flags & LCTF_CHILD) {
+ if (fp->ctf_parname == NULL) {
+ plen = 0;
+ hdr.cth_parname = 1; /* i.e. _CTF_STRTAB_TEMPLATE[1] */
+ plabel = NULL;
+ } else {
+ plen = strlen(fp->ctf_parname) + 1;
+ plabel = ctf_label_topmost(fp->ctf_parent);
+ }
+ } else {
+ plabel = NULL;
+ plen = 0;
+ }
+
+ /*
+ * Iterate over the labels that we have.
+ */
+ for (labelsize = 0, dld = ctf_list_next(&fp->ctf_dldefs);
+ dld != NULL; dld = ctf_list_next(dld))
+ labelsize += sizeof (ctf_lblent_t);
/*
* Iterate through the dynamic type definition list and compute the
@@ -304,25 +368,121 @@ ctf_update(ctf_file_t *fp)
}
/*
+ * An entry for each object must exist in the data section. However, if
+ * the symbol is SHN_UNDEF, then it is skipped. For objects, the storage
+ * is just the size of the 2-byte id. For functions it's always 2 bytes,
+ * plus 2 bytes per argument and the return type.
+ */
+ dsd = ctf_list_next(&fp->ctf_dsdefs);
+ for (objsize = 0, funcsize = 0, i = 0; i < fp->ctf_nsyms; i++) {
+ int type;
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+
+ type = ELF32_ST_TYPE(symp->st_info);
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+
+ type = ELF64_ST_TYPE(symp->st_info);
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ }
+
+ while (dsd != NULL && i > dsd->dts_symidx)
+ dsd = ctf_list_next(dsd);
+ if (type == STT_OBJECT) {
+ objsize += sizeof (uint16_t);
+ } else {
+ /* Every function has a uint16_t info no matter what */
+ if (dsd == NULL || i < dsd->dts_symidx) {
+ funcsize += sizeof (uint16_t);
+ } else {
+ funcsize += sizeof (uint16_t) *
+ (dsd->dts_nargs + 2);
+ }
+ }
+ }
+
+ /*
+ * The objtoff and funcoffset must be 2-byte aligned. We're guaranteed
+ * that this is always true for the objtoff because labels are always 8
+ * bytes large. Similarly, because objects are always two bytes of data,
+ * this will always be true for funcoff.
+ */
+ hdr.cth_objtoff = hdr.cth_lbloff + labelsize;
+ hdr.cth_funcoff = hdr.cth_objtoff + objsize;
+
+ /*
+ * The type offset must be 4 byte aligned.
+ */
+ hdr.cth_typeoff = hdr.cth_funcoff + funcsize;
+ if (hdr.cth_typeoff & 3)
+ hdr.cth_typeoff += 4 - (hdr.cth_typeoff & 3);
+ ASSERT((hdr.cth_typeoff & 3) == 0);
+
+ /*
* 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;
+ hdr.cth_strlen = fp->ctf_dtstrlen + plen;
size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen;
+ ctf_dprintf("lbloff: %d\nobjtoff: %d\nfuncoff: %d\n"
+ "typeoff: %d\nstroff: %d\nstrlen: %d\n",
+ hdr.cth_lbloff, hdr.cth_objtoff, hdr.cth_funcoff,
+ hdr.cth_typeoff, 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);
+ bhdr = buf;
+ label = (ctf_lblent_t *)((uintptr_t)buf + sizeof (ctf_header_t));
+ t = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_typeoff;
s = s0 = (uchar_t *)buf + sizeof (ctf_header_t) + hdr.cth_stroff;
+ obj = (uint16_t *)((uintptr_t)buf + sizeof (ctf_header_t) +
+ hdr.cth_objtoff);
+ func = (uint16_t *)((uintptr_t)buf + sizeof (ctf_header_t) +
+ hdr.cth_funcoff);
bcopy(_CTF_STRTAB_TEMPLATE, s, sizeof (_CTF_STRTAB_TEMPLATE));
s += sizeof (_CTF_STRTAB_TEMPLATE);
/*
+ * We have an actual parent name and we're a child container, therefore
+ * we should make sure to note our parent's name here.
+ */
+ if (plen != 0) {
+ VERIFY(s + plen - s0 <= hdr.cth_strlen);
+ bcopy(fp->ctf_parname, s, plen);
+ bhdr->cth_parname = s - s0;
+ s += plen;
+ }
+
+ /*
+ * First pass over the labels and copy them out.
+ */
+ for (dld = ctf_list_next(&fp->ctf_dldefs); dld != NULL;
+ dld = ctf_list_next(dld), label++) {
+ size_t len = strlen(dld->dld_name) + 1;
+
+ VERIFY(s + len - s0 <= hdr.cth_strlen);
+ bcopy(dld->dld_name, s, len);
+ label->ctl_typeidx = dld->dld_type;
+ label->ctl_label = s - s0;
+ s += len;
+
+ if (plabel != NULL && strcmp(plabel, dld->dld_name) == 0)
+ bhdr->cth_parlabel = label->ctl_label;
+ }
+
+ /*
* We now take a final lap through the dynamic type definition list and
* copy the appropriate type records and strings to the output buffer.
*/
@@ -339,6 +499,7 @@ ctf_update(ctf_file_t *fp)
if (dtd->dtd_name != NULL) {
dtd->dtd_data.ctt_name = (uint_t)(s - s0);
len = strlen(dtd->dtd_name) + 1;
+ VERIFY(s + len - s0 <= hdr.cth_strlen);
bcopy(dtd->dtd_name, s, len);
s += len;
} else
@@ -411,6 +572,61 @@ ctf_update(ctf_file_t *fp)
}
/*
+ * Now we fill in our dynamic data and function sections. We use the
+ * same criteria as above, but also consult the dsd list.
+ */
+ dsd = ctf_list_next(&fp->ctf_dsdefs);
+ for (i = 0; i < fp->ctf_nsyms; i++) {
+ int type;
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ type = ELF32_ST_TYPE(symp->st_info);
+
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ type = ELF64_ST_TYPE(symp->st_info);
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ }
+
+ while (dsd != NULL && i > dsd->dts_symidx) {
+ dsd = ctf_list_next(dsd);
+ }
+ if (type == STT_OBJECT) {
+ if (dsd == NULL || i < dsd->dts_symidx) {
+ *obj = 0;
+ } else {
+ *obj = dsd->dts_tid;
+ }
+ obj++;
+ VERIFY((uintptr_t)obj <= (uintptr_t)func);
+ } else {
+ if (dsd == NULL || i < dsd->dts_symidx) {
+ ushort_t data = CTF_TYPE_INFO(CTF_K_UNKNOWN,
+ 0, 0);
+ *func = data;
+ func++;
+ } else {
+ int j;
+ ushort_t data = CTF_TYPE_INFO(CTF_K_FUNCTION, 0,
+ dsd->dts_nargs);
+
+ *func = data;
+ func++;
+ *func = dsd->dts_tid;
+ func++;
+ for (j = 0; j < dsd->dts_nargs; j++)
+ func[j] = dsd->dts_argc[j];
+ func += dsd->dts_nargs;
+ }
+ }
+ }
+
+ /*
* 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.
*/
@@ -423,7 +639,15 @@ ctf_update(ctf_file_t *fp)
cts.cts_entsize = 1;
cts.cts_offset = 0;
- if ((nfp = ctf_bufopen(&cts, NULL, NULL, &err)) == NULL) {
+ if (fp->ctf_nsyms == 0) {
+ symp = NULL;
+ strp = NULL;
+ } else {
+ symp = &fp->ctf_symtab;
+ strp = &fp->ctf_strtab;
+ }
+
+ if ((nfp = ctf_bufopen(&cts, symp, strp, &err)) == NULL) {
ctf_data_free(buf, size);
return (ctf_set_errno(fp, err));
}
@@ -433,10 +657,11 @@ ctf_update(ctf_file_t *fp)
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_dsdefs = fp->ctf_dsdefs;
+ nfp->ctf_dldefs = fp->ctf_dldefs;
nfp->ctf_dtstrlen = fp->ctf_dtstrlen;
nfp->ctf_dtnextid = fp->ctf_dtnextid;
nfp->ctf_dtoldid = fp->ctf_dtnextid - 1;
@@ -445,6 +670,11 @@ ctf_update(ctf_file_t *fp)
fp->ctf_dthash = NULL;
fp->ctf_dthashlen = 0;
bzero(&fp->ctf_dtdefs, sizeof (ctf_list_t));
+ bzero(&fp->ctf_dsdefs, sizeof (ctf_list_t));
+ bzero(&fp->ctf_dldefs, sizeof (ctf_list_t));
+
+ bzero(&fp->ctf_symtab, sizeof (ctf_sect_t));
+ bzero(&fp->ctf_strtab, sizeof (ctf_sect_t));
bcopy(fp, &ofp, sizeof (ctf_file_t));
bcopy(nfp, fp, sizeof (ctf_file_t));
@@ -563,6 +793,101 @@ ctf_dtd_lookup(ctf_file_t *fp, ctf_id_t type)
return (dtd);
}
+ctf_dsdef_t *
+ctf_dsd_lookup(ctf_file_t *fp, ulong_t idx)
+{
+ ctf_dsdef_t *dsd;
+
+ for (dsd = ctf_list_next(&fp->ctf_dsdefs); dsd != NULL;
+ dsd = ctf_list_next(dsd)) {
+ if (dsd->dts_symidx == idx)
+ return (dsd);
+ }
+
+ return (NULL);
+}
+
+/*
+ * We order the ctf_dsdef_t by symbol index to make things better for updates.
+ */
+void
+ctf_dsd_insert(ctf_file_t *fp, ctf_dsdef_t *dsd)
+{
+ ctf_dsdef_t *i;
+
+ for (i = ctf_list_next(&fp->ctf_dsdefs); i != NULL;
+ i = ctf_list_next(i)) {
+ if (i->dts_symidx > dsd->dts_symidx)
+ break;
+ }
+
+ if (i == NULL) {
+ ctf_list_append(&fp->ctf_dsdefs, dsd);
+ return;
+ }
+
+ ctf_list_insert_before(&fp->ctf_dsdefs, i, dsd);
+}
+
+/* ARGSUSED */
+void
+ctf_dsd_delete(ctf_file_t *fp, ctf_dsdef_t *dsd)
+{
+ if (dsd->dts_argc != NULL)
+ ctf_free(dsd->dts_argc,
+ sizeof (ctf_id_t) * dsd->dts_nargs);
+ ctf_list_delete(&fp->ctf_dsdefs, dsd);
+ ctf_free(dsd, sizeof (ctf_dsdef_t));
+}
+
+ctf_dldef_t *
+ctf_dld_lookup(ctf_file_t *fp, const char *name)
+{
+ ctf_dldef_t *dld;
+
+ for (dld = ctf_list_next(&fp->ctf_dldefs); dld != NULL;
+ dld = ctf_list_next(dld)) {
+ if (strcmp(name, dld->dld_name) == 0)
+ return (dld);
+ }
+
+ return (NULL);
+}
+
+void
+ctf_dld_insert(ctf_file_t *fp, ctf_dldef_t *dld, uint_t pos)
+{
+ ctf_dldef_t *l;
+
+ if (pos == 0) {
+ ctf_list_prepend(&fp->ctf_dldefs, dld);
+ return;
+ }
+
+ for (l = ctf_list_next(&fp->ctf_dldefs); pos != 0 && dld != NULL;
+ l = ctf_list_next(l), pos--)
+ ;
+
+ if (l == NULL)
+ ctf_list_append(&fp->ctf_dldefs, dld);
+ else
+ ctf_list_insert_before(&fp->ctf_dsdefs, l, dld);
+}
+
+void
+ctf_dld_delete(ctf_file_t *fp, ctf_dldef_t *dld)
+{
+ ctf_list_delete(&fp->ctf_dldefs, dld);
+
+ if (dld->dld_name != NULL) {
+ size_t len = strlen(dld->dld_name) + 1;
+ ctf_free(dld->dld_name, len);
+ fp->ctf_dtstrlen -= len;
+ }
+
+ ctf_free(dld, sizeof (ctf_dldef_t));
+}
+
/*
* 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
@@ -574,7 +899,7 @@ ctf_dtd_lookup(ctf_file_t *fp, ctf_id_t type)
int
ctf_discard(ctf_file_t *fp)
{
- ctf_dtdef_t *dtd, *ntd = NULL;
+ ctf_dtdef_t *dtd, *ntd;
if (!(fp->ctf_flags & LCTF_RDWR))
return (ctf_set_errno(fp, ECTF_RDONLY));
@@ -583,10 +908,10 @@ ctf_discard(ctf_file_t *fp)
return (0); /* no update required */
for (dtd = ctf_list_prev(&fp->ctf_dtdefs); dtd != NULL; dtd = ntd) {
+ ntd = ctf_list_prev(dtd);
if (dtd->dtd_type <= fp->ctf_dtoldid)
continue; /* skip types that have been committed */
- ntd = ctf_list_prev(dtd);
ctf_dtd_delete(fp, dtd);
}
@@ -656,7 +981,7 @@ clp2(size_t x)
return (x + 1);
}
-static ctf_id_t
+ctf_id_t
ctf_add_encoded(ctf_file_t *fp, uint_t flag,
const char *name, const ctf_encoding_t *ep, uint_t kind)
{
@@ -676,8 +1001,9 @@ ctf_add_encoded(ctf_file_t *fp, uint_t flag,
return (type);
}
-static ctf_id_t
-ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind)
+ctf_id_t
+ctf_add_reftype(ctf_file_t *fp, uint_t flag,
+ const char *name, ctf_id_t ref, uint_t kind)
{
ctf_dtdef_t *dtd;
ctf_id_t type;
@@ -685,7 +1011,7 @@ ctf_add_reftype(ctf_file_t *fp, uint_t flag, ctf_id_t ref, uint_t kind)
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)
+ if ((type = ctf_add_generic(fp, flag, name, &dtd)) == CTF_ERR)
return (CTF_ERR); /* errno is set for us */
ctf_ref_inc(fp, ref);
@@ -711,9 +1037,9 @@ ctf_add_float(ctf_file_t *fp, uint_t flag,
}
ctf_id_t
-ctf_add_pointer(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+ctf_add_pointer(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
{
- return (ctf_add_reftype(fp, flag, ref, CTF_K_POINTER));
+ return (ctf_add_reftype(fp, flag, name, ref, CTF_K_POINTER));
}
ctf_id_t
@@ -781,7 +1107,7 @@ ctf_set_array(ctf_file_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)
}
ctf_id_t
-ctf_add_function(ctf_file_t *fp, uint_t flag,
+ctf_add_funcptr(ctf_file_t *fp, uint_t flag,
const ctf_funcinfo_t *ctc, const ctf_id_t *argv)
{
ctf_dtdef_t *dtd;
@@ -965,21 +1291,21 @@ ctf_add_typedef(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
}
ctf_id_t
-ctf_add_volatile(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+ctf_add_volatile(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
{
- return (ctf_add_reftype(fp, flag, ref, CTF_K_VOLATILE));
+ return (ctf_add_reftype(fp, flag, name, ref, CTF_K_VOLATILE));
}
ctf_id_t
-ctf_add_const(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+ctf_add_const(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
{
- return (ctf_add_reftype(fp, flag, ref, CTF_K_CONST));
+ return (ctf_add_reftype(fp, flag, name, ref, CTF_K_CONST));
}
ctf_id_t
-ctf_add_restrict(ctf_file_t *fp, uint_t flag, ctf_id_t ref)
+ctf_add_restrict(ctf_file_t *fp, uint_t flag, const char *name, ctf_id_t ref)
{
- return (ctf_add_reftype(fp, flag, ref, CTF_K_RESTRICT));
+ return (ctf_add_reftype(fp, flag, name, ref, CTF_K_RESTRICT));
}
int
@@ -1039,7 +1365,8 @@ ctf_add_enumerator(ctf_file_t *fp, ctf_id_t enid, const char *name, int value)
}
int
-ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)
+ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type,
+ ulong_t offset)
{
ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, souid);
ctf_dmdef_t *dmd;
@@ -1064,7 +1391,12 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)
if (vlen == CTF_MAX_VLEN)
return (ctf_set_errno(fp, ECTF_DTFULL));
- if (name != NULL) {
+ /*
+ * Structures may have members which are anonymous. If they have two of
+ * these, then the duplicte member detection would find it due to the
+ * string of "", so we skip it.
+ */
+ if (name != NULL && *name != '\0') {
for (dmd = ctf_list_next(&dtd->dtd_u.dtu_members);
dmd != NULL; dmd = ctf_list_next(dmd)) {
if (dmd->dmd_name != NULL &&
@@ -1092,29 +1424,36 @@ ctf_add_member(ctf_file_t *fp, ctf_id_t souid, const char *name, ctf_id_t type)
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;
+ size_t off;
+
+ if (offset == ULONG_MAX) {
+ ctf_encoding_t linfo;
+ ssize_t lsize;
+
+ off = lmd->dmd_offset;
+ 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 = offset;
+ ssize = offset / NBBY + msize;
+ }
} else {
dmd->dmd_offset = 0;
ssize = ctf_get_ctt_size(fp, &dtd->dtd_data, NULL, NULL);
@@ -1380,7 +1719,7 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t 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);
+ dst_type = ctf_add_reftype(dst_fp, flag, NULL, src_type, kind);
break;
case CTF_K_ARRAY:
@@ -1415,7 +1754,7 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)
if (ctc.ctc_return == CTF_ERR)
return (CTF_ERR); /* errno is set for us */
- dst_type = ctf_add_function(dst_fp, flag, &ctc, NULL);
+ dst_type = ctf_add_funcptr(dst_fp, flag, &ctc, NULL);
break;
case CTF_K_STRUCT:
@@ -1540,3 +1879,166 @@ ctf_add_type(ctf_file_t *dst_fp, ctf_file_t *src_fp, ctf_id_t src_type)
return (dst_type);
}
+
+int
+ctf_add_function(ctf_file_t *fp, ulong_t idx, const ctf_funcinfo_t *fip,
+ const ctf_id_t *argc)
+{
+ int i;
+ ctf_dsdef_t *dsd;
+ ctf_file_t *afp;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (ctf_dsd_lookup(fp, idx) != NULL)
+ return (ctf_set_errno(fp, ECTF_CONFLICT));
+
+ if (symbase == NULL)
+ return (ctf_set_errno(fp, ECTF_STRTAB));
+
+ if (idx > fp->ctf_nsyms)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
+ return (ctf_set_errno(fp, ECTF_NOTFUNC));
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
+ return (ctf_set_errno(fp, ECTF_NOTFUNC));
+ }
+
+ afp = fp;
+ if (ctf_lookup_by_id(&afp, fip->ctc_return) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ for (i = 0; i < fip->ctc_argc; i++) {
+ afp = fp;
+ if (ctf_lookup_by_id(&afp, argc[i]) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+ }
+
+ dsd = ctf_alloc(sizeof (ctf_dsdef_t));
+ if (dsd == NULL)
+ return (ctf_set_errno(fp, ENOMEM));
+ dsd->dts_nargs = fip->ctc_argc;
+ if (fip->ctc_flags & CTF_FUNC_VARARG)
+ dsd->dts_nargs++;
+ if (dsd->dts_nargs != 0) {
+ dsd->dts_argc = ctf_alloc(sizeof (ctf_id_t) * dsd->dts_nargs);
+ if (dsd->dts_argc == NULL) {
+ ctf_free(dsd, sizeof (ctf_dsdef_t));
+ return (ctf_set_errno(fp, ENOMEM));
+ }
+ bcopy(argc, dsd->dts_argc, sizeof (ctf_id_t) * fip->ctc_argc);
+ if (fip->ctc_flags & CTF_FUNC_VARARG)
+ dsd->dts_argc[fip->ctc_argc] = 0;
+ }
+ dsd->dts_symidx = idx;
+ dsd->dts_tid = fip->ctc_return;
+
+ ctf_dsd_insert(fp, dsd);
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ return (0);
+}
+
+int
+ctf_add_object(ctf_file_t *fp, ulong_t idx, ctf_id_t type)
+{
+ ctf_dsdef_t *dsd;
+ ctf_file_t *afp;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ if (ctf_dsd_lookup(fp, idx) != NULL)
+ return (ctf_set_errno(fp, ECTF_CONFLICT));
+
+ if (symbase == NULL)
+ return (ctf_set_errno(fp, ECTF_STRTAB));
+
+ if (idx > fp->ctf_nsyms)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT)
+ return (ctf_set_errno(fp, ECTF_NOTDATA));
+ }
+
+ afp = fp;
+ if (ctf_lookup_by_id(&afp, type) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ dsd = ctf_alloc(sizeof (ctf_dsdef_t));
+ if (dsd == NULL)
+ return (ctf_set_errno(fp, ENOMEM));
+ dsd->dts_symidx = idx;
+ dsd->dts_tid = type;
+ dsd->dts_argc = NULL;
+
+ ctf_dsd_insert(fp, dsd);
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ return (0);
+}
+
+void
+ctf_dataptr(ctf_file_t *fp, const void **addrp, size_t *sizep)
+{
+ if (addrp != NULL)
+ *addrp = fp->ctf_base;
+ if (sizep != NULL)
+ *sizep = fp->ctf_size;
+}
+
+int
+ctf_add_label(ctf_file_t *fp, const char *name, ctf_id_t type, uint_t position)
+{
+ ctf_file_t *fpd;
+ ctf_dldef_t *dld;
+
+ if (name == NULL)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (!(fp->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(fp, ECTF_RDONLY));
+
+ fpd = fp;
+ if (type != 0 && ctf_lookup_by_id(&fpd, type) == NULL)
+ return (CTF_ERR); /* errno is set for us */
+
+ if (type != 0 && (fp->ctf_flags & LCTF_CHILD) &&
+ CTF_TYPE_ISPARENT(type))
+ return (ctf_set_errno(fp, ECTF_NOPARENT));
+
+ if (ctf_dld_lookup(fp, name) != NULL)
+ return (ctf_set_errno(fp, ECTF_LABELEXISTS));
+
+ if ((dld = ctf_alloc(sizeof (ctf_dldef_t))) == NULL)
+ return (ctf_set_errno(fp, EAGAIN));
+
+ if ((dld->dld_name = ctf_strdup(name)) == NULL) {
+ ctf_free(dld, sizeof (ctf_dldef_t));
+ return (ctf_set_errno(fp, EAGAIN));
+ }
+
+ dld->dld_type = type;
+ fp->ctf_dtstrlen += strlen(name) + 1;
+ ctf_dld_insert(fp, dld, position);
+ fp->ctf_flags |= LCTF_DIRTY;
+
+ return (0);
+}
diff --git a/usr/src/common/ctf/ctf_error.c b/usr/src/common/ctf/ctf_error.c
index fe3d0de0cb..e2eb12e9dd 100644
--- a/usr/src/common/ctf/ctf_error.c
+++ b/usr/src/common/ctf/ctf_error.c
@@ -24,7 +24,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2012, Joyent, Inc.
+ * Copyright (c) 2015, Joyent, Inc.
*/
#include <ctf_impl.h>
@@ -75,7 +75,12 @@ static const char *const _ctf_errlist[] = {
"Duplicate member name definition", /* ECTF_DUPMEMBER */
"Conflicting type is already defined", /* ECTF_CONFLICT */
"Type has outstanding references", /* ECTF_REFERENCED */
- "Type is not a dynamic type" /* ECTF_NOTDYN */
+ "Type is not a dynamic type", /* ECTF_NOTDYN */
+ "Elf library failure", /* ECTF_ELF */
+ "Cannot merge child container", /* ECTF_MCHILD */
+ "Label already exists", /* ECTF_LABEL */
+ "Merged labels conflict", /* ECTF_LCONFLICT */
+ "Zlib library failure" /* ECTF_ZLIB */
};
static const int _ctf_nerr = sizeof (_ctf_errlist) / sizeof (_ctf_errlist[0]);
diff --git a/usr/src/common/ctf/ctf_hash.c b/usr/src/common/ctf/ctf_hash.c
index e68fe8e516..0c5a71a5ac 100644
--- a/usr/src/common/ctf/ctf_hash.c
+++ b/usr/src/common/ctf/ctf_hash.c
@@ -175,160 +175,3 @@ ctf_hash_destroy(ctf_hash_t *hp)
hp->h_chains = NULL;
}
}
-
-int
-ctf_idhash_create(ctf_idhash_t *ihp, 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(ihp, sizeof (ctf_idhash_t));
- ihp->ih_buckets = (ushort_t *)_CTF_EMPTY;
- ihp->ih_nbuckets = 1;
- return (0);
- }
-
- ihp->ih_nbuckets = 211; /* use a prime number of hash buckets */
- ihp->ih_nelems = nelems + 1; /* we use index zero as a sentinel */
- ihp->ih_free = 1; /* first free element is index 1 */
-
- ihp->ih_buckets = ctf_alloc(sizeof (ushort_t) * ihp->ih_nbuckets);
- ihp->ih_chains = ctf_alloc(sizeof (ctf_ihelem_t) * ihp->ih_nelems);
-
- if (ihp->ih_buckets == NULL || ihp->ih_chains == NULL) {
- ctf_idhash_destroy(ihp);
- return (EAGAIN);
- }
-
- bzero(ihp->ih_buckets, sizeof (ushort_t) * ihp->ih_nbuckets);
- bzero(ihp->ih_chains, sizeof (ctf_ihelem_t) * ihp->ih_nelems);
-
- return (0);
-}
-
-void
-ctf_idhash_clear(ctf_idhash_t *ihp)
-{
- /* Nothing to do for a sentinel hash */
- if (ihp->ih_nbuckets == 1) {
- ASSERT(ihp->ih_buckets == (ushort_t *)_CTF_EMPTY);
- return;
- }
-
- ihp->ih_free = 1;
- bzero(ihp->ih_buckets, sizeof (ushort_t) * ihp->ih_nbuckets);
- bzero(ihp->ih_chains, sizeof (ctf_ihelem_t) * ihp->ih_nelems);
-}
-
-uint_t
-ctf_idhash_size(const ctf_idhash_t *hp)
-{
- return (hp->ih_nelems ? hp->ih_nelems - 1 : 0);
-}
-
-static ulong_t
-ctf_idhash_compute(ctf_id_t id)
-{
- return (id);
-}
-
-int
-ctf_idhash_insert(ctf_idhash_t *ihp, ushort_t type, ushort_t value)
-{
- ctf_ihelem_t *ihep = &ihp->ih_chains[ihp->ih_free];
- ulong_t h;
-
- if (ihp->ih_free >= ihp->ih_nelems)
- return (EOVERFLOW);
-
- ihep->ih_type = type;
- ihep->ih_value = value;
- h = ctf_idhash_compute(type) % ihp->ih_nbuckets;
- ihep->ih_next = ihp->ih_buckets[h];
- ihp->ih_buckets[h] = ihp->ih_free++;
-
- return (0);
-}
-
-int
-ctf_idhash_define(ctf_idhash_t *ihp, ushort_t type, ushort_t value)
-{
- ctf_ihelem_t *hep = ctf_idhash_lookup(ihp, type);
-
- if (hep == NULL)
- return (ctf_idhash_insert(ihp, type, value));
-
- hep->ih_value = value;
- return (0);
-}
-
-
-ctf_ihelem_t *
-ctf_idhash_lookup(ctf_idhash_t *ihp, ushort_t key)
-{
- ctf_ihelem_t *ihep;
- ushort_t i;
-
- ulong_t h = ctf_idhash_compute(key) % ihp->ih_nbuckets;
-
- for (i = ihp->ih_buckets[h]; i != 0; i = ihep->ih_next) {
- ihep = &ihp->ih_chains[i];
-
- if (ihep->ih_type == key)
- return (ihep);
- }
-
- return (NULL);
-}
-
-void
-ctf_idhash_destroy(ctf_idhash_t *ihp)
-{
- if (ihp->ih_buckets != NULL && ihp->ih_nbuckets != 1) {
- ctf_free(ihp->ih_buckets, sizeof (ushort_t) * ihp->ih_nbuckets);
- ihp->ih_buckets = NULL;
- }
-
- if (ihp->ih_chains != NULL) {
- ctf_free(ihp->ih_chains, sizeof (ctf_helem_t) * ihp->ih_nelems);
- ihp->ih_chains = NULL;
- }
-}
-
-/*ARGSUSED*/
-int
-ctf_idhash_iter_init(ctf_idhash_t *ihp, ctf_idhash_iter_t **iterp)
-{
-
- *iterp = ctf_alloc(sizeof (ctf_idhash_iter_t));
- if (*iterp == NULL)
- return (ENOMEM);
-
- if (ihp->ih_free == 0)
- (*iterp)->cii_id = 0;
- else
- (*iterp)->cii_id = 1;
-
- return (0);
-}
-
-const ctf_ihelem_t *
-ctf_idhash_iter(ctf_idhash_t *ihp, ctf_idhash_iter_t *iter)
-{
- if (iter->cii_id >= ihp->ih_free)
- return (NULL);
-
- return (&ihp->ih_chains[iter->cii_id++]);
-}
-
-/*ARGSUSED*/
-void
-ctf_idhash_iter_fini(ctf_idhash_t *ihp, ctf_idhash_iter_t *iter)
-{
- ctf_free(iter, sizeof (ctf_idhash_iter_t));
-}
diff --git a/usr/src/common/ctf/ctf_impl.h b/usr/src/common/ctf/ctf_impl.h
index 42aec80a43..04b12418ae 100644
--- a/usr/src/common/ctf/ctf_impl.h
+++ b/usr/src/common/ctf/ctf_impl.h
@@ -25,7 +25,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#ifndef _CTF_IMPL_H
@@ -41,6 +41,8 @@
#include <sys/systm.h>
#include <sys/cmn_err.h>
#include <sys/varargs.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
#define isspace(c) \
((c) == ' ' || (c) == '\t' || (c) == '\n' || \
@@ -56,6 +58,7 @@
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
+#include <stddef.h>
#endif /* _KERNEL */
@@ -163,6 +166,20 @@ typedef struct ctf_dtdef {
} dtd_u;
} ctf_dtdef_t;
+typedef struct ctf_dsdef {
+ ctf_list_t dts_list; /* list forward/back pointers */
+ ulong_t dts_symidx; /* symbol id */
+ ctf_id_t dts_tid; /* type for obj, 0 if function */
+ uint_t dts_nargs;
+ ctf_id_t *dts_argc; /* function argv */
+} ctf_dsdef_t;
+
+typedef struct ctf_dldef {
+ ctf_list_t dld_list; /* list forward/back pointers */
+ char *dld_name; /* name of the label */
+ ctf_id_t dld_type; /* type ID associated with the label */
+} ctf_dldef_t;
+
typedef struct ctf_bundle {
ctf_file_t *ctb_file; /* CTF container handle */
ctf_id_t ctb_type; /* CTF type identifier */
@@ -215,6 +232,9 @@ struct ctf_file {
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 */
+ ctf_list_t ctf_dsdefs; /* list of dynamic obj/func definitions */
+ ctf_list_t ctf_dldefs; /* list of dynamic labels */
+ uint_t ctf_hflags; /* original flags on the header */
};
#define LCTF_INDEX_TO_TYPEPTR(fp, i) \
@@ -229,62 +249,15 @@ struct ctf_file {
#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 */
- ECTF_REFERENCED, /* type has outstanding references */
- ECTF_NOTDYN /* type is not a dynamic type */
-};
+#define CTF_ELF_SCN_NAME ".SUNW_ctf"
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 ctf_file_t *ctf_fdcreate_int(int, int *, ctf_sect_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);
@@ -298,12 +271,16 @@ extern void ctf_hash_destroy(ctf_hash_t *);
extern void ctf_list_append(ctf_list_t *, void *);
extern void ctf_list_prepend(ctf_list_t *, void *);
+extern void ctf_list_insert_before(ctf_list_t *, void *, 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_dsd_delete(ctf_file_t *, ctf_dsdef_t *);
+extern void ctf_dld_delete(ctf_file_t *, ctf_dldef_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);
@@ -331,6 +308,13 @@ extern void ctf_dprintf(const char *, ...);
extern void *ctf_zopen(int *);
+extern ctf_id_t ctf_add_encoded(ctf_file_t *, uint_t, const char *,
+ const ctf_encoding_t *, uint_t);
+extern ctf_id_t ctf_add_reftype(ctf_file_t *, uint_t, const char *, ctf_id_t,
+ uint_t);
+extern boolean_t ctf_sym_valid(uintptr_t, int, uint16_t, uint64_t,
+ uint32_t);
+
extern const char _CTF_SECTION[]; /* name of CTF ELF section */
extern const char _CTF_NULLSTR[]; /* empty string */
diff --git a/usr/src/common/ctf/ctf_open.c b/usr/src/common/ctf/ctf_open.c
index 001cf5c591..82b396e825 100644
--- a/usr/src/common/ctf/ctf_open.c
+++ b/usr/src/common/ctf/ctf_open.c
@@ -25,7 +25,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc. All rights reserved.
*/
#include <ctf_impl.h>
@@ -550,6 +550,7 @@ ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
void *buf, *base;
size_t size, hdrsz;
int err;
+ uint_t hflags;
if (ctfsect == NULL || ((symsect == NULL) != (strsect == NULL)))
return (ctf_set_open_errno(errp, EINVAL));
@@ -631,6 +632,7 @@ ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
* the CTF data buffer if it is compressed. Otherwise we just put
* the data section's buffer pointer into ctf_buf, below.
*/
+ hflags = hp.cth_flags;
if (hp.cth_flags & CTF_F_COMPRESS) {
size_t srclen, dstlen;
const void *src;
@@ -680,6 +682,7 @@ ctf_bufopen(const ctf_sect_t *ctfsect, const ctf_sect_t *symsect,
bzero(fp, sizeof (ctf_file_t));
fp->ctf_version = hp.cth_version;
fp->ctf_fileops = &ctf_fileops[hp.cth_version];
+ fp->ctf_hflags = hflags;
bcopy(ctfsect, &fp->ctf_data, sizeof (ctf_sect_t));
if (symsect != NULL) {
@@ -883,6 +886,8 @@ void
ctf_close(ctf_file_t *fp)
{
ctf_dtdef_t *dtd, *ntd;
+ ctf_dsdef_t *dsd, *nsd;
+ ctf_dldef_t *dld, *nld;
if (fp == NULL)
return; /* allow ctf_close(NULL) to simplify caller code */
@@ -906,10 +911,25 @@ ctf_close(ctf_file_t *fp)
ctf_dtd_delete(fp, dtd);
}
+ for (dsd = ctf_list_prev(&fp->ctf_dsdefs); dsd != NULL; dsd = nsd) {
+ nsd = ctf_list_prev(dsd);
+ ctf_dsd_delete(fp, dsd);
+ }
+
+ for (dld = ctf_list_prev(&fp->ctf_dldefs); dld != NULL; dld = nld) {
+ nld = ctf_list_prev(dld);
+ ctf_dld_delete(fp, dld);
+ }
+
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)
+ /*
+ * Writeable containers shouldn't necessairily have the CTF
+ * section freed.
+ */
+ if (fp->ctf_data.cts_data != NULL &&
+ !(fp->ctf_flags & LCTF_RDWR))
ctf_sect_munmap(&fp->ctf_data);
if (fp->ctf_symtab.cts_data != NULL)
ctf_sect_munmap(&fp->ctf_symtab);
@@ -980,6 +1000,16 @@ ctf_parent_name(ctf_file_t *fp)
}
/*
+ * Return the label of the parent CTF container, if one exists. Otherwise return
+ * NULL.
+ */
+const char *
+ctf_parent_label(ctf_file_t *fp)
+{
+ return (fp->ctf_parlabel);
+}
+
+/*
* 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.
@@ -1043,3 +1073,9 @@ ctf_getspecific(ctf_file_t *fp)
{
return (fp->ctf_specific);
}
+
+uint_t
+ctf_flags(ctf_file_t *fp)
+{
+ return (fp->ctf_hflags);
+}
diff --git a/usr/src/common/ctf/ctf_types.c b/usr/src/common/ctf/ctf_types.c
index 93168f25cb..b5dbb260a3 100644
--- a/usr/src/common/ctf/ctf_types.c
+++ b/usr/src/common/ctf/ctf_types.c
@@ -24,6 +24,9 @@
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
#include <ctf_impl.h>
#include <sys/debug.h>
@@ -139,19 +142,21 @@ ctf_enum_iter(ctf_file_t *fp, ctf_id_t type, ctf_enum_f *func, void *arg)
}
/*
- * 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.
+ * Iterate over every type in the given CTF container. If the user doesn't ask
+ * for all types, then we only give them the user visible, aka root, types. 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_type_iter(ctf_file_t *fp, boolean_t nonroot, 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)
+ if ((nonroot || CTF_INFO_ISROOT(tp->ctt_info)) &&
+ (rc = func(CTF_INDEX_TO_TYPE(id, child),
+ CTF_INFO_ISROOT(tp->ctt_info), arg)) != 0)
return (rc);
}
@@ -381,7 +386,22 @@ ctf_type_size(ctf_file_t *fp, ctf_id_t type)
return (-1); /* errno is set for us */
return (size * ar.ctr_nelems);
-
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ /*
+ * If we have a zero size, we may be in the process of adding a
+ * structure or union but having not called ctf_update() to deal
+ * with the circular dependencies in such structures and unions.
+ * To handle that case, if we get a size of zero from the ctt,
+ * we look up the dtdef and use its size instead.
+ */
+ size = ctf_get_ctt_size(fp, tp, NULL, NULL);
+ if (size == 0) {
+ ctf_dtdef_t *dtd = ctf_dtd_lookup(fp, type);
+ if (dtd != NULL)
+ return (dtd->dtd_data.ctt_size);
+ }
+ return (size);
default:
return (ctf_get_ctt_size(fp, tp, NULL, NULL));
}
@@ -932,3 +952,193 @@ ctf_func_args_by_id(ctf_file_t *fp, ctf_id_t type, uint_t argc, ctf_id_t *argv)
return (0);
}
+
+int
+ctf_object_iter(ctf_file_t *fp, ctf_object_f *func, void *arg)
+{
+ int i, ret;
+ ctf_id_t id;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+ if (fp->ctf_symtab.cts_data == NULL)
+ return (ctf_set_errno(fp, ECTF_NOSYMTAB));
+
+ for (i = 0; i < fp->ctf_nsyms; i++) {
+ char *name;
+ if (fp->ctf_sxlate[i] == -1u)
+ continue;
+ id = *(ushort_t *)((uintptr_t)fp->ctf_buf +
+ fp->ctf_sxlate[i]);
+
+ /*
+ * Validate whether or not we're looking at a data object as
+ * oposed to a function.
+ */
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT)
+ continue;
+ if (fp->ctf_strtab.cts_data != NULL &&
+ symp->st_name != 0)
+ name = (char *)(strbase + symp->st_name);
+ else
+ name = NULL;
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_OBJECT)
+ continue;
+ if (fp->ctf_strtab.cts_data != NULL &&
+ symp->st_name != 0)
+ name = (char *)(strbase + symp->st_name);
+ else
+ name = NULL;
+ }
+
+ if ((ret = func(name, id, i, arg)) != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+int
+ctf_function_iter(ctf_file_t *fp, ctf_function_f *func, void *arg)
+{
+ int i, ret;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+ if (fp->ctf_symtab.cts_data == NULL)
+ return (ctf_set_errno(fp, ECTF_NOSYMTAB));
+
+ for (i = 0; i < fp->ctf_nsyms; i++) {
+ char *name;
+ ushort_t info, *dp;
+ ctf_funcinfo_t fi;
+ if (fp->ctf_sxlate[i] == -1u)
+ continue;
+
+ dp = (ushort_t *)((uintptr_t)fp->ctf_buf +
+ fp->ctf_sxlate[i]);
+ info = *dp;
+ if (info == 0)
+ continue;
+
+ /*
+ * This may be a function or it may be a data object. We have to
+ * consult the symbol table to be certain. Functions are encoded
+ * with their info, data objects with their actual type.
+ */
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
+ continue;
+ if (fp->ctf_strtab.cts_data != NULL)
+ name = (char *)(strbase + symp->st_name);
+ else
+ name = NULL;
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
+ continue;
+ if (fp->ctf_strtab.cts_data != NULL)
+ name = (char *)(strbase + symp->st_name);
+ else
+ name = NULL;
+ }
+
+ if (LCTF_INFO_KIND(fp, info) != CTF_K_FUNCTION)
+ continue;
+ dp++;
+ fi.ctc_return = *dp;
+ dp++;
+ fi.ctc_argc = LCTF_INFO_VLEN(fp, info);
+ fi.ctc_flags = 0;
+
+ if (fi.ctc_argc != 0 && dp[fi.ctc_argc - 1] == 0) {
+ fi.ctc_flags |= CTF_FUNC_VARARG;
+ fi.ctc_argc--;
+ }
+
+ if ((ret = func(name, i, &fi, arg)) != 0)
+ return (ret);
+
+ }
+
+ return (0);
+}
+
+char *
+ctf_symbol_name(ctf_file_t *fp, ulong_t idx, char *buf, size_t len)
+{
+ const char *name;
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+ if (fp->ctf_symtab.cts_data == NULL) {
+ (void) ctf_set_errno(fp, ECTF_NOSYMTAB);
+ return (NULL);
+ }
+
+ if (fp->ctf_strtab.cts_data == NULL) {
+ (void) ctf_set_errno(fp, ECTF_STRTAB);
+ return (NULL);
+ }
+
+ if (idx > fp->ctf_nsyms) {
+ (void) ctf_set_errno(fp, ECTF_NOTDATA);
+ return (NULL);
+ }
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + idx;
+ if (ELF32_ST_TYPE(symp->st_info) != STT_OBJECT &&
+ ELF32_ST_TYPE(symp->st_info) != STT_FUNC) {
+ (void) ctf_set_errno(fp, ECTF_NOTDATA);
+ return (NULL);
+ }
+ if (symp->st_name == 0) {
+ (void) ctf_set_errno(fp, ENOENT);
+ return (NULL);
+ }
+ name = (const char *)(strbase + symp->st_name);
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + idx;
+ if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC &&
+ ELF64_ST_TYPE(symp->st_info) != STT_OBJECT) {
+ (void) ctf_set_errno(fp, ECTF_NOTDATA);
+ return (NULL);
+ }
+ if (symp->st_name == 0) {
+ (void) ctf_set_errno(fp, ENOENT);
+ return (NULL);
+ }
+ name = (const char *)(strbase + symp->st_name);
+ }
+
+ (void) strlcpy(buf, name, len);
+
+ return (buf);
+}
+
+int
+ctf_string_iter(ctf_file_t *fp, ctf_string_f *func, void *arg)
+{
+ int rc;
+ const char *strp = fp->ctf_str[CTF_STRTAB_0].cts_strs;
+ size_t strl = fp->ctf_str[CTF_STRTAB_0].cts_len;
+
+ while (strl > 0) {
+ size_t len;
+
+ if ((rc = func(strp, arg)) != 0)
+ return (rc);
+
+ len = strlen(strp) + 1;
+ strl -= len;
+ strp += len;
+ }
+
+ return (0);
+}
diff --git a/usr/src/common/ctf/ctf_util.c b/usr/src/common/ctf/ctf_util.c
index 740d403e8c..550195b5e1 100644
--- a/usr/src/common/ctf/ctf_util.c
+++ b/usr/src/common/ctf/ctf_util.c
@@ -23,10 +23,12 @@
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
-
-#pragma ident "%Z%%M% %I% %E% SMI"
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
#include <ctf_impl.h>
+#include <sys/debug.h>
/*
* Simple doubly-linked list append routine. This implementation assumes that
@@ -71,6 +73,24 @@ ctf_list_prepend(ctf_list_t *lp, void *new)
lp->l_prev = p;
}
+void
+ctf_list_insert_before(ctf_list_t *head, void *item, void *nitem)
+{
+ ctf_list_t *lp = item;
+ ctf_list_t *new = nitem;
+ ctf_list_t *prev = lp->l_prev;
+
+ lp->l_prev = new;
+ new->l_next = lp;
+ new->l_prev = prev;
+ if (prev != NULL) {
+ prev->l_next = new;
+ } else {
+ ASSERT(head->l_next == lp);
+ head->l_next = new;
+ }
+}
+
/*
* 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.
@@ -150,3 +170,22 @@ ctf_set_errno(ctf_file_t *fp, int err)
fp->ctf_errno = err;
return (CTF_ERR);
}
+
+boolean_t
+ctf_sym_valid(uintptr_t strbase, int type, uint16_t shndx, uint64_t val,
+ uint32_t noff)
+{
+ const char *name;
+
+ if (type != STT_OBJECT && type != STT_FUNC)
+ return (B_FALSE);
+ if (shndx == SHN_UNDEF || noff == 0)
+ return (B_FALSE);
+ if (type == STT_OBJECT && shndx == SHN_ABS && val == 0)
+ return (B_FALSE);
+ name = (char *)(strbase + noff);
+ if (strcmp(name, "_START_") == 0 || strcmp(name, "_END_") == 0)
+ return (B_FALSE);
+
+ return (B_TRUE);
+}
diff --git a/usr/src/lib/Makefile.lib b/usr/src/lib/Makefile.lib
index eb5ccb1db6..592fa1c2d1 100644
--- a/usr/src/lib/Makefile.lib
+++ b/usr/src/lib/Makefile.lib
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
# Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
-# Copyright (c) 2014, Joyent, Inc.
+# Copyright (c) 2015, Joyent, Inc.
#
#
# Definitions common to libraries.
@@ -280,5 +280,5 @@ CLOBBERTARGFILES= $(LIBS) $(DYNLIB) $(CLOBBERFILES)
TYPECHECK_LIB32 = $(TYPECHECK_LIB:%=$(MACH)/%)
TYPECHECK_LIB64 = $(TYPECHECK_LIB:%=$(MACH64)/%)
TYPECHECK_LIST= $(TYPELIST:%=-T %)
-$(BUILD64)TYPECHECK.lib = $(CTFDIFF) -I $(TYPECHECK_LIST) $(TYPECHECK_LIB32) $(TYPECHECK_LIB64)
+$(BUILD64)TYPECHECK.lib = $(CTFDIFF) -t -I $(TYPECHECK_LIST) $(TYPECHECK_LIB32) $(TYPECHECK_LIB64)
TYPECHECK = $(TYPECHECK_LIB:%=%.typecheck)
diff --git a/usr/src/lib/libctf/Makefile.shared.com b/usr/src/lib/libctf/Makefile.shared.com
index 92f33a93b6..3181d0b72a 100644
--- a/usr/src/lib/libctf/Makefile.shared.com
+++ b/usr/src/lib/libctf/Makefile.shared.com
@@ -22,7 +22,7 @@
# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# Copyright (c) 2014, Joyent, Inc. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc. All rights reserved.
#
#
@@ -43,19 +43,31 @@ COMMON_OBJS = \
ctf_types.o \
ctf_util.o
+LIST_OBJS = \
+ list.o
+
LIB_OBJS = \
+ ctf_elfwrite.o \
ctf_diff.o \
ctf_lib.o \
+ ctf_merge.o \
ctf_subr.o
-OBJECTS = $(COMMON_OBJS) $(LIB_OBJS)
+OBJECTS = $(COMMON_OBJS) $(LIB_OBJS) $(LIST_OBJS)
MAPFILEDIR = $(SRC)/lib/libctf
include $(SRC)/lib/Makefile.lib
-SRCS = $(COMMON_OBJS:%.o=$(SRC)/common/ctf/%.c) $(LIB_OBJS:%.o=$(SRC)/lib/libctf/common/%.c)
+SRCS = \
+ $(COMMON_OBJS:%.o=$(SRC)/common/ctf/%.c) \
+ $(LIB_OBJS:%.o=$(SRC)/lib/libctf/common/%.c) \
+ $(LIST_OBJS:%.o=$(SRC)/common/list/%.c) \
+
LIBS = $(DYNLIB) $(LINTLIB)
-LDLIBS = -lc
+LDLIBS = -lc -lelf
+
+C99MODE= -xc99=%all
+C99LMODE= -Xc99=%all
SRCDIR = $(SRC)/lib/libctf/common
diff --git a/usr/src/lib/libctf/Makefile.shared.targ b/usr/src/lib/libctf/Makefile.shared.targ
index 7fc28aefcd..282be86075 100644
--- a/usr/src/lib/libctf/Makefile.shared.targ
+++ b/usr/src/lib/libctf/Makefile.shared.targ
@@ -10,7 +10,7 @@
#
#
-# Copyright (c) 2014, Joyent, Inc. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc. All rights reserved.
#
#
@@ -20,3 +20,7 @@
pics/%.o: $(SRC)/common/ctf/%.c
$(COMPILE.c) -o $@ $<
$(POST_PROCESS_O)
+
+pics/%.o: $(SRC)/common/list/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/libctf/common/ctf_diff.c b/usr/src/lib/libctf/common/ctf_diff.c
index aabfd38fcf..e819fe02cb 100644
--- a/usr/src/lib/libctf/common/ctf_diff.c
+++ b/usr/src/lib/libctf/common/ctf_diff.c
@@ -10,28 +10,91 @@
*/
/*
- * Copyright (c) 2014 Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
+ */
+
+/*
+ * The following ia a basic overview of how we diff types in containers (the
+ * generally interesting part of diff, and what's used by merge). We maintain
+ * two mapping tables, a table of forward mappings (src->dest), and a reverse
+ * mapping (dest->src). Both are initialized to contain no mapping, and can also
+ * be updated to contain a negative mapping.
+ *
+ * What we do first is iterate over each type in the src container, and compare
+ * it with a type in the destination container. This may involve doing recursive
+ * comparisons -- which can involve cycles. To deal with this, whenever we
+ * encounter something which may be cyclic, we insert a guess. In other words,
+ * we assume that it may be true. This is necessary for the classic case of the
+ * following structure:
+ *
+ * struct foo {
+ * struct foo *foo_next;
+ * };
+ *
+ * If it turns out that we were wrong, we discard our guesses.
+ *
+ * If we find that a given type in src has no corresponding entry in dst, we
+ * then mark its map as CTF_ERR (-1) to indicate that it has *no* match, as
+ * opposed to the default value of 0, which indicates an unknown match.
+ * Once we've done the first iteration through src, we know at that point in
+ * time whether everything in dst is similar or not and can simply walk over it
+ * and don't have to do any additional checks.
*/
#include <libctf.h>
#include <ctf_impl.h>
#include <sys/debug.h>
+typedef struct ctf_diff_func {
+ const char *cdf_name;
+ ulong_t cdf_symidx;
+ ulong_t cdf_matchidx;
+} ctf_diff_func_t;
+
+typedef struct ctf_diff_obj {
+ const char *cdo_name;
+ ulong_t cdo_symidx;
+ ctf_id_t cdo_id;
+ ulong_t cdo_matchidx;
+} ctf_diff_obj_t;
+
+typedef struct ctf_diff_guess {
+ struct ctf_diff_guess *cdg_next;
+ ctf_id_t cdg_iid;
+ ctf_id_t cdg_oid;
+} ctf_diff_guess_t;
+
/* typedef in libctf.h */
struct ctf_diff {
uint_t cds_flags;
+ boolean_t cds_tvalid; /* types valid */
ctf_file_t *cds_ifp;
ctf_file_t *cds_ofp;
- ctf_idhash_t cds_forward;
- ctf_idhash_t cds_reverse;
- ctf_idhash_t cds_fneg;
- ctf_idhash_t cds_f_visited;
- ctf_idhash_t cds_r_visited;
+ ctf_id_t *cds_forward;
+ ctf_id_t *cds_reverse;
ctf_diff_type_f cds_func;
- int cds_visitid;
+ ctf_diff_guess_t *cds_guess;
void *cds_arg;
+ uint_t cds_nifuncs;
+ uint_t cds_nofuncs;
+ uint_t cds_nextifunc;
+ uint_t cds_nextofunc;
+ ctf_diff_func_t *cds_ifuncs;
+ ctf_diff_func_t *cds_ofuncs;
+ boolean_t cds_ffillip;
+ boolean_t cds_fvalid;
+ uint_t cds_niobj;
+ uint_t cds_noobj;
+ uint_t cds_nextiobj;
+ uint_t cds_nextoobj;
+ ctf_diff_obj_t *cds_iobj;
+ ctf_diff_obj_t *cds_oobj;
+ boolean_t cds_ofillip;
+ boolean_t cds_ovalid;
};
+#define TINDEX(tid) (tid - 1)
+
/*
* Team Diff
*/
@@ -124,14 +187,23 @@ ctf_diff_array(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
if (ret != B_FALSE)
return (ret);
- if (iar.ctr_nelems == oar.ctr_nelems)
- return (B_FALSE);
+ if (iar.ctr_nelems != oar.ctr_nelems)
+ return (B_TRUE);
- ret = ctf_diff_type(cds, ifp, iar.ctr_index, ofp, oar.ctr_index);
- if (ret != B_FALSE)
- return (ret);
+ /*
+ * If we're ignoring integer types names, then we're trying to do a bit
+ * of a logical diff and we don't really care about the fact that the
+ * index element might not be the same here, what we care about are the
+ * number of elements and that they're the same type.
+ */
+ if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0) {
+ ret = ctf_diff_type(cds, ifp, iar.ctr_index, ofp,
+ oar.ctr_index);
+ if (ret != B_FALSE)
+ return (ret);
+ }
- return (B_TRUE);
+ return (B_FALSE);
}
/*
@@ -143,7 +215,7 @@ ctf_diff_array(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
* o They have the same flags
*/
static int
-ctf_diff_func(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
+ctf_diff_fptr(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
ctf_id_t oid)
{
int ret, i;
@@ -214,6 +286,7 @@ ctf_diff_struct(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
const ctf_member_t *imp, *omp;
const ctf_lmember_t *ilmp, *olmp;
int n;
+ ctf_diff_guess_t *cdg;
oifp = ifp;
@@ -246,6 +319,19 @@ ctf_diff_struct(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
olmp = (const ctf_lmember_t *)((uintptr_t)otp + oincr);
}
+ /*
+ * Insert our assumption that they're equal for the moment.
+ */
+ cdg = ctf_alloc(sizeof (ctf_diff_guess_t));
+ if (cdg == NULL)
+ return (ctf_set_errno(ifp, ENOMEM));
+ cdg->cdg_iid = iid;
+ cdg->cdg_oid = oid;
+ cdg->cdg_next = cds->cds_guess;
+ cds->cds_guess = cdg;
+ cds->cds_forward[TINDEX(iid)] = oid;
+ cds->cds_reverse[TINDEX(oid)] = iid;
+
for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0; n--) {
const char *iname, *oname;
ulong_t ioff, ooff;
@@ -279,8 +365,9 @@ ctf_diff_struct(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
return (B_TRUE);
}
ret = ctf_diff_type(cds, ifp, itype, ofp, otype);
- if (ret != B_FALSE)
+ if (ret != B_FALSE) {
return (ret);
+ }
/* Advance our pointers */
if (imp != NULL)
@@ -385,6 +472,7 @@ ctf_diff_union(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
ctf_file_t *oifp;
const ctf_type_t *itp, *otp;
ctf_diff_union_fp_t cduf;
+ ctf_diff_guess_t *cdg;
int ret;
oifp = ifp;
@@ -397,6 +485,16 @@ ctf_diff_union(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
LCTF_INFO_VLEN(ofp, otp->ctt_info))
return (B_TRUE);
+ cdg = ctf_alloc(sizeof (ctf_diff_guess_t));
+ if (cdg == NULL)
+ return (ctf_set_errno(ifp, ENOMEM));
+ cdg->cdg_iid = iid;
+ cdg->cdg_oid = oid;
+ cdg->cdg_next = cds->cds_guess;
+ cds->cds_guess = cdg;
+ cds->cds_forward[TINDEX(iid)] = oid;
+ cds->cds_reverse[TINDEX(oid)] = iid;
+
cduf.cduf_cds = cds;
cduf.cduf_curfp = ifp;
cduf.cduf_altfp = ofp;
@@ -481,7 +579,6 @@ ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
ctf_id_t oid)
{
int ret, ikind, okind;
- ctf_ihelem_t *lookup, *fv, *rv;
/* Do a quick short circuit */
if (ifp == ofp && iid == oid)
@@ -489,29 +586,26 @@ ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
/*
* Check if it's something we've already encountered in a forward
- * reference or forward negative table.
+ * reference or forward negative table. Also double check the reverse
+ * table.
*/
- if ((lookup = ctf_idhash_lookup(&cds->cds_forward, iid)) != NULL) {
- if (lookup->ih_value == oid)
- return (B_FALSE);
- else
- return (B_TRUE);
- }
-
- if (ctf_idhash_lookup(&cds->cds_forward, iid) != NULL) {
+ if (cds->cds_forward[TINDEX(iid)] == oid)
+ return (B_FALSE);
+ if (cds->cds_forward[TINDEX(iid)] != 0)
return (B_TRUE);
- }
-
- fv = ctf_idhash_lookup(&cds->cds_f_visited, iid);
- rv = ctf_idhash_lookup(&cds->cds_r_visited, oid);
- if (fv != NULL && rv != NULL)
- return (fv->ih_value != rv->ih_value);
- else if (fv != NULL || rv != NULL)
+ if (cds->cds_reverse[TINDEX(oid)] == iid)
+ return (B_FALSE);
+ if ((cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0 &&
+ cds->cds_reverse[TINDEX(oid)] != 0)
return (B_TRUE);
ikind = ctf_type_kind(ifp, iid);
okind = ctf_type_kind(ofp, oid);
+ if (ikind != okind &&
+ ikind != CTF_K_FORWARD && okind != CTF_K_FORWARD)
+ return (B_TRUE);
+
/* Check names */
if ((ret = ctf_diff_name(ifp, iid, ofp, oid)) != B_FALSE) {
if (ikind != okind || ikind != CTF_K_INTEGER ||
@@ -519,12 +613,8 @@ ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
return (ret);
}
- if (ikind != okind) {
- if (ikind == CTF_K_FORWARD || okind == CTF_K_FORWARD)
- return (ctf_diff_forward(ifp, iid, ofp, oid));
- else
- return (B_TRUE);
- }
+ if (ikind == CTF_K_FORWARD || okind == CTF_K_FORWARD)
+ return (ctf_diff_forward(ifp, iid, ofp, oid));
switch (ikind) {
case CTF_K_INTEGER:
@@ -535,22 +625,12 @@ ctf_diff_type(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp,
ret = ctf_diff_array(cds, ifp, iid, ofp, oid);
break;
case CTF_K_FUNCTION:
- ret = ctf_diff_func(cds, ifp, iid, ofp, oid);
+ ret = ctf_diff_fptr(cds, ifp, iid, ofp, oid);
break;
case CTF_K_STRUCT:
- VERIFY(ctf_idhash_insert(&cds->cds_f_visited, iid,
- cds->cds_visitid) == 0);
- VERIFY(ctf_idhash_insert(&cds->cds_r_visited, oid,
- cds->cds_visitid) == 0);
- cds->cds_visitid++;
ret = ctf_diff_struct(cds, ifp, iid, ofp, oid);
break;
case CTF_K_UNION:
- VERIFY(ctf_idhash_insert(&cds->cds_f_visited, iid,
- cds->cds_visitid) == 0);
- VERIFY(ctf_idhash_insert(&cds->cds_r_visited, oid,
- cds->cds_visitid) == 0);
- cds->cds_visitid++;
ret = ctf_diff_union(cds, ifp, iid, ofp, oid);
break;
case CTF_K_ENUM:
@@ -610,35 +690,41 @@ ctf_diff_pass1(ctf_diff_t *cds)
for (i = istart; i <= iend; i++) {
diff = B_TRUE;
for (j = jstart; j <= jend; j++) {
- cds->cds_visitid = 1;
- ctf_idhash_clear(&cds->cds_f_visited);
- ctf_idhash_clear(&cds->cds_r_visited);
+ ctf_diff_guess_t *cdg, *tofree;
+ ASSERT(cds->cds_guess == NULL);
diff = ctf_diff_type(cds, cds->cds_ifp, i,
cds->cds_ofp, j);
if (diff == CTF_ERR)
return (CTF_ERR);
+ /* Clean up our guesses */
+ cdg = cds->cds_guess;
+ cds->cds_guess = NULL;
+ while (cdg != NULL) {
+ if (diff == B_TRUE) {
+ cds->cds_forward[TINDEX(cdg->cdg_iid)] =
+ 0;
+ cds->cds_reverse[TINDEX(cdg->cdg_oid)] =
+ 0;
+ }
+ tofree = cdg;
+ cdg = cdg->cdg_next;
+ ctf_free(tofree, sizeof (ctf_diff_guess_t));
+ }
+
/* Found a hit, update the tables */
if (diff == B_FALSE) {
- VERIFY(ctf_idhash_lookup(&cds->cds_forward,
- i) == NULL);
- VERIFY(ctf_idhash_insert(&cds->cds_forward,
- i, j) == 0);
- if (ctf_idhash_lookup(&cds->cds_reverse, j) ==
- NULL) {
- VERIFY(ctf_idhash_lookup(
- &cds->cds_reverse, j) == NULL);
- VERIFY(ctf_idhash_insert(
- &cds->cds_reverse, j, i) == 0);
- }
+ cds->cds_forward[TINDEX(i)] = j;
+ if (cds->cds_reverse[TINDEX(j)] == 0)
+ cds->cds_reverse[TINDEX(j)] = i;
break;
}
}
/* Call the callback at this point */
if (diff == B_TRUE) {
- VERIFY(ctf_idhash_insert(&cds->cds_fneg, i, 1) == 0);
+ cds->cds_forward[TINDEX(i)] = CTF_ERR;
cds->cds_func(cds->cds_ifp, i, B_FALSE, NULL, CTF_ERR,
cds->cds_arg);
} else {
@@ -657,10 +743,17 @@ ctf_diff_pass1(ctf_diff_t *cds)
static int
ctf_diff_pass2(ctf_diff_t *cds)
{
- int i;
+ int i, start, end;
- for (i = 1; i <= cds->cds_ofp->ctf_typemax; i++) {
- if (ctf_idhash_lookup(&cds->cds_reverse, i) != NULL)
+ start = 0x1;
+ end = cds->cds_ofp->ctf_typemax;
+ if (cds->cds_ofp->ctf_flags & LCTF_CHILD) {
+ start += 0x8000;
+ end += 0x8000;
+ }
+
+ for (i = start; i <= end; i++) {
+ if (cds->cds_reverse[TINDEX(i)] != 0)
continue;
cds->cds_func(cds->cds_ofp, i, B_FALSE, NULL, CTF_ERR,
cds->cds_arg);
@@ -672,8 +765,8 @@ ctf_diff_pass2(ctf_diff_t *cds)
int
ctf_diff_init(ctf_file_t *ifp, ctf_file_t *ofp, ctf_diff_t **cdsp)
{
- int ret;
ctf_diff_t *cds;
+ size_t fsize, rsize;
cds = ctf_alloc(sizeof (ctf_diff_t));
if (cds == NULL)
@@ -682,41 +775,27 @@ ctf_diff_init(ctf_file_t *ifp, ctf_file_t *ofp, ctf_diff_t **cdsp)
bzero(cds, sizeof (ctf_diff_t));
cds->cds_ifp = ifp;
cds->cds_ofp = ofp;
- ret = ctf_idhash_create(&cds->cds_forward, ifp->ctf_typemax);
- if (ret != 0) {
- ctf_free(cds, sizeof (ctf_diff_t));
- return (ctf_set_errno(ifp, ret));
- }
- ret = ctf_idhash_create(&cds->cds_reverse, ofp->ctf_typemax);
- if (ret != 0) {
- ctf_idhash_destroy(&cds->cds_forward);
- ctf_free(cds, sizeof (ctf_diff_t));
- return (ctf_set_errno(ifp, ret));
- }
- ret = ctf_idhash_create(&cds->cds_f_visited, ifp->ctf_typemax);
- if (ret != 0) {
- ctf_idhash_destroy(&cds->cds_reverse);
- ctf_idhash_destroy(&cds->cds_forward);
+
+ fsize = sizeof (ctf_id_t) * ifp->ctf_typemax;
+ rsize = sizeof (ctf_id_t) * ofp->ctf_typemax;
+ if (ifp->ctf_flags & LCTF_CHILD)
+ fsize += 0x8000 * sizeof (ctf_id_t);
+ if (ofp->ctf_flags & LCTF_CHILD)
+ rsize += 0x8000 * sizeof (ctf_id_t);
+
+ cds->cds_forward = ctf_alloc(fsize);
+ if (cds->cds_forward == NULL) {
ctf_free(cds, sizeof (ctf_diff_t));
- return (ctf_set_errno(ifp, ret));
+ return (ctf_set_errno(ifp, ENOMEM));
}
- ret = ctf_idhash_create(&cds->cds_r_visited, ofp->ctf_typemax);
- if (ret != 0) {
- ctf_idhash_destroy(&cds->cds_f_visited);
- ctf_idhash_destroy(&cds->cds_reverse);
- ctf_idhash_destroy(&cds->cds_forward);
- ctf_free(cds, sizeof (ctf_diff_t));
- return (ctf_set_errno(ifp, ret));
- }
- ret = ctf_idhash_create(&cds->cds_fneg, ifp->ctf_typemax);
- if (ret != 0) {
- ctf_idhash_destroy(&cds->cds_r_visited);
- ctf_idhash_destroy(&cds->cds_f_visited);
- ctf_idhash_destroy(&cds->cds_reverse);
- ctf_idhash_destroy(&cds->cds_forward);
+ cds->cds_reverse = ctf_alloc(rsize);
+ if (cds->cds_reverse == NULL) {
+ ctf_free(cds->cds_forward, fsize);
ctf_free(cds, sizeof (ctf_diff_t));
- return (ctf_set_errno(ifp, ret));
+ return (ctf_set_errno(ifp, ENOMEM));
}
+ bzero(cds->cds_forward, fsize);
+ bzero(cds->cds_reverse, rsize);
cds->cds_ifp->ctf_refcnt++;
cds->cds_ofp->ctf_refcnt++;
@@ -732,37 +811,50 @@ ctf_diff_types(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg)
cds->cds_func = cb;
cds->cds_arg = arg;
- /*
- * For the moment clear all idhashes and rerun this phase. Ideally we
- * should reuse this, but we can save that for when we add things like
- * taking the diff of the objects and the like.
- */
- ctf_idhash_clear(&cds->cds_forward);
- ctf_idhash_clear(&cds->cds_reverse);
- ctf_idhash_clear(&cds->cds_fneg);
- ctf_idhash_clear(&cds->cds_f_visited);
- ctf_idhash_clear(&cds->cds_r_visited);
-
ret = ctf_diff_pass1(cds);
if (ret == 0)
ret = ctf_diff_pass2(cds);
cds->cds_func = NULL;
cds->cds_arg = NULL;
+ cds->cds_tvalid = B_TRUE;
return (ret);
}
void
ctf_diff_fini(ctf_diff_t *cds)
{
+ ctf_diff_guess_t *cdg;
+ size_t fsize, rsize;
+
cds->cds_ifp->ctf_refcnt--;
cds->cds_ofp->ctf_refcnt--;
- ctf_idhash_destroy(&cds->cds_fneg);
- ctf_idhash_destroy(&cds->cds_r_visited);
- ctf_idhash_destroy(&cds->cds_f_visited);
- ctf_idhash_destroy(&cds->cds_reverse);
- ctf_idhash_destroy(&cds->cds_forward);
+ fsize = sizeof (ctf_id_t) * cds->cds_ifp->ctf_typemax;
+ rsize = sizeof (ctf_id_t) * cds->cds_ofp->ctf_typemax;
+ if (cds->cds_ifp->ctf_flags & LCTF_CHILD)
+ fsize += 0x8000 * sizeof (ctf_id_t);
+ if (cds->cds_ofp->ctf_flags & LCTF_CHILD)
+ rsize += 0x8000 * sizeof (ctf_id_t);
+
+ if (cds->cds_ifuncs != NULL)
+ ctf_free(cds->cds_ifuncs,
+ sizeof (ctf_diff_func_t) * cds->cds_nifuncs);
+ if (cds->cds_ofuncs != NULL)
+ ctf_free(cds->cds_ofuncs,
+ sizeof (ctf_diff_func_t) * cds->cds_nofuncs);
+ if (cds->cds_iobj != NULL)
+ ctf_free(cds->cds_iobj,
+ sizeof (ctf_diff_obj_t) * cds->cds_niobj);
+ if (cds->cds_oobj != NULL)
+ ctf_free(cds->cds_oobj,
+ sizeof (ctf_diff_obj_t) * cds->cds_noobj);
+ cdg = cds->cds_guess;
+ while (cdg != NULL) {
+ ctf_diff_guess_t *tofree = cdg;
+ cdg = cdg->cdg_next;
+ ctf_free(tofree, sizeof (ctf_diff_guess_t));
+ }
ctf_free(cds, sizeof (ctf_diff_t));
}
@@ -781,3 +873,405 @@ ctf_diff_setflags(ctf_diff_t *cds, uint_t flags)
cds->cds_flags = flags;
return (0);
}
+
+static boolean_t
+ctf_diff_symid(ctf_diff_t *cds, ctf_id_t iid, ctf_id_t oid)
+{
+ ctf_file_t *ifp, *ofp;
+
+ ifp = cds->cds_ifp;
+ ofp = cds->cds_ofp;
+
+ /*
+ * If we have parent containers on the scene here, we need to go through
+ * and do a full diff check because while a diff for types will not
+ * actually go through and check types in the parent container.
+ */
+ if (iid == 0 || oid == 0)
+ return (iid == oid ? B_FALSE: B_TRUE);
+
+ if (!(ifp->ctf_flags & LCTF_CHILD) && !(ofp->ctf_flags & LCTF_CHILD)) {
+ if (cds->cds_forward[TINDEX(iid)] != oid)
+ return (B_TRUE);
+ return (B_FALSE);
+ }
+
+ return (ctf_diff_type(cds, ifp, iid, ofp, oid));
+}
+
+/* ARGSUSED */
+static void
+ctf_diff_void_cb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
+ ctf_id_t oid, void *arg)
+{
+}
+
+/* ARGSUSED */
+static int
+ctf_diff_func_count(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
+ void *arg)
+{
+ uint32_t *ip = arg;
+
+ *ip = *ip + 1;
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ctf_diff_func_fill_cb(const char *name, ulong_t symidx, ctf_funcinfo_t *fip,
+ void *arg)
+{
+ uint_t *next, max;
+ ctf_diff_func_t *funcptr;
+ ctf_diff_t *cds = arg;
+
+ if (cds->cds_ffillip == B_TRUE) {
+ max = cds->cds_nifuncs;
+ next = &cds->cds_nextifunc;
+ funcptr = cds->cds_ifuncs + *next;
+ } else {
+ max = cds->cds_nofuncs;
+ next = &cds->cds_nextofunc;
+ funcptr = cds->cds_ofuncs + *next;
+
+ }
+
+ VERIFY(*next < max);
+ funcptr->cdf_name = name;
+ funcptr->cdf_symidx = symidx;
+ funcptr->cdf_matchidx = ULONG_MAX;
+ *next = *next + 1;
+
+ return (0);
+}
+
+int
+ctf_diff_func_fill(ctf_diff_t *cds)
+{
+ int ret;
+ uint32_t ifcount, ofcount, idcnt, cti;
+ ulong_t i, j;
+ ctf_id_t *iids, *oids;
+
+ ifcount = 0;
+ ofcount = 0;
+ idcnt = 0;
+ iids = NULL;
+ oids = NULL;
+
+ ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_count, &ifcount);
+ if (ret != 0)
+ return (ret);
+ ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_count, &ofcount);
+ if (ret != 0)
+ return (ret);
+
+ cds->cds_ifuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ifcount);
+ if (cds->cds_ifuncs == NULL)
+ return (ctf_set_errno(cds->cds_ifp, ENOMEM));
+
+ cds->cds_nifuncs = ifcount;
+ cds->cds_nextifunc = 0;
+
+ cds->cds_ofuncs = ctf_alloc(sizeof (ctf_diff_func_t) * ofcount);
+ if (cds->cds_ofuncs == NULL)
+ return (ctf_set_errno(cds->cds_ifp, ENOMEM));
+
+ cds->cds_nofuncs = ofcount;
+ cds->cds_nextofunc = 0;
+
+ cds->cds_ffillip = B_TRUE;
+ if ((ret = ctf_function_iter(cds->cds_ifp, ctf_diff_func_fill_cb,
+ cds)) != 0)
+ return (ret);
+
+ cds->cds_ffillip = B_FALSE;
+ if ((ret = ctf_function_iter(cds->cds_ofp, ctf_diff_func_fill_cb,
+ cds)) != 0)
+ return (ret);
+
+ /*
+ * Everything is initialized to not match. This could probably be faster
+ * with something that used a hash. But this part of the diff isn't used
+ * by merge.
+ */
+ for (i = 0; i < cds->cds_nifuncs; i++) {
+ for (j = 0; j < cds->cds_nofuncs; j++) {
+ ctf_diff_func_t *ifd, *ofd;
+ ctf_funcinfo_t ifip, ofip;
+ boolean_t match;
+
+ ifd = &cds->cds_ifuncs[i];
+ ofd = &cds->cds_ofuncs[j];
+ if (strcmp(ifd->cdf_name, ofd->cdf_name) != 0)
+ continue;
+
+ ret = ctf_func_info(cds->cds_ifp, ifd->cdf_symidx,
+ &ifip);
+ if (ret != 0)
+ goto out;
+ ret = ctf_func_info(cds->cds_ofp, ofd->cdf_symidx,
+ &ofip);
+ if (ret != 0) {
+ ret = ctf_set_errno(cds->cds_ifp,
+ ctf_errno(cds->cds_ofp));
+ goto out;
+ }
+
+ if (ifip.ctc_argc != ofip.ctc_argc &&
+ ifip.ctc_flags != ofip.ctc_flags)
+ continue;
+
+ /* Validate return type and arguments are the same */
+ if (ctf_diff_symid(cds, ifip.ctc_return,
+ ofip.ctc_return))
+ continue;
+
+ if (ifip.ctc_argc > idcnt) {
+ if (iids != NULL)
+ ctf_free(iids,
+ sizeof (ctf_id_t) * idcnt);
+ if (oids != NULL)
+ ctf_free(oids,
+ sizeof (ctf_id_t) * idcnt);
+ iids = oids = NULL;
+ idcnt = ifip.ctc_argc;
+ iids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
+ if (iids == NULL) {
+ ret = ctf_set_errno(cds->cds_ifp,
+ ENOMEM);
+ goto out;
+ }
+ oids = ctf_alloc(sizeof (ctf_id_t) * idcnt);
+ if (iids == NULL) {
+ ret = ctf_set_errno(cds->cds_ifp,
+ ENOMEM);
+ goto out;
+ }
+ }
+
+ if ((ret = ctf_func_args(cds->cds_ifp, ifd->cdf_symidx,
+ ifip.ctc_argc, iids)) != 0)
+ goto out;
+ if ((ret = ctf_func_args(cds->cds_ofp, ofd->cdf_symidx,
+ ofip.ctc_argc, oids)) != 0)
+ goto out;
+
+ match = B_TRUE;
+ for (cti = 0; cti < ifip.ctc_argc; cti++) {
+ if (ctf_diff_symid(cds, iids[cti], oids[cti])) {
+ match = B_FALSE;
+ break;
+ }
+ }
+
+ if (match == B_FALSE)
+ continue;
+
+ ifd->cdf_matchidx = j;
+ ofd->cdf_matchidx = i;
+ break;
+ }
+ }
+
+ ret = 0;
+
+out:
+ if (iids != NULL)
+ ctf_free(iids, sizeof (ctf_id_t) * idcnt);
+ if (oids != NULL)
+ ctf_free(oids, sizeof (ctf_id_t) * idcnt);
+
+ return (ret);
+}
+
+/*
+ * In general, two functions are the same, if they have the same name and their
+ * arguments have the same types, including the return type. Like types, we
+ * basically have to do this in two passes. In the first phase we walk every
+ * type in the first container and try to find a match in the second.
+ */
+int
+ctf_diff_functions(ctf_diff_t *cds, ctf_diff_func_f cb, void *arg)
+{
+ int ret;
+ ulong_t i;
+
+ if (cds->cds_tvalid == B_FALSE) {
+ if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
+ return (ret);
+ }
+
+ if (cds->cds_fvalid == B_FALSE) {
+ if ((ret = ctf_diff_func_fill(cds)) != 0)
+ return (ret);
+ cds->cds_fvalid = B_TRUE;
+ }
+
+ for (i = 0; i < cds->cds_nifuncs; i++) {
+ if (cds->cds_ifuncs[i].cdf_matchidx == ULONG_MAX) {
+ cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx,
+ B_FALSE, NULL, ULONG_MAX, arg);
+ } else {
+ ulong_t idx = cds->cds_ifuncs[i].cdf_matchidx;
+ cb(cds->cds_ifp, cds->cds_ifuncs[i].cdf_symidx, B_TRUE,
+ cds->cds_ofp, cds->cds_ofuncs[idx].cdf_symidx, arg);
+ }
+ }
+
+ for (i = 0; i < cds->cds_nofuncs; i++) {
+ if (cds->cds_ofuncs[i].cdf_matchidx != ULONG_MAX)
+ continue;
+ cb(cds->cds_ofp, cds->cds_ofuncs[i].cdf_symidx, B_FALSE,
+ NULL, ULONG_MAX, arg);
+ }
+
+ return (0);
+}
+
+static int
+ctf_diff_obj_fill_cb(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
+{
+ uint_t *next, max;
+ ctf_diff_obj_t *objptr;
+ ctf_diff_t *cds = arg;
+
+ if (cds->cds_ofillip == B_TRUE) {
+ max = cds->cds_niobj;
+ next = &cds->cds_nextiobj;
+ objptr = cds->cds_iobj + *next;
+ } else {
+ max = cds->cds_noobj;
+ next = &cds->cds_nextoobj;
+ objptr = cds->cds_oobj+ *next;
+
+ }
+
+ VERIFY(*next < max);
+ objptr->cdo_name = name;
+ objptr->cdo_symidx = symidx;
+ objptr->cdo_id = id;
+ objptr->cdo_matchidx = ULONG_MAX;
+ *next = *next + 1;
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+ctf_diff_obj_count(const char *name, ctf_id_t id, ulong_t symidx, void *arg)
+{
+ uint32_t *count = arg;
+
+ *count = *count + 1;
+
+ return (0);
+}
+
+
+static int
+ctf_diff_obj_fill(ctf_diff_t *cds)
+{
+ int ret;
+ uint32_t iocount, oocount;
+ ulong_t i, j;
+
+ iocount = 0;
+ oocount = 0;
+
+ ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_count, &iocount);
+ if (ret != 0)
+ return (ret);
+
+ ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_count, &oocount);
+ if (ret != 0)
+ return (ret);
+
+ cds->cds_iobj = ctf_alloc(sizeof (ctf_diff_obj_t) * iocount);
+ if (cds->cds_iobj == NULL)
+ return (ctf_set_errno(cds->cds_ifp, ENOMEM));
+ cds->cds_niobj = iocount;
+ cds->cds_nextiobj = 0;
+
+ cds->cds_oobj = ctf_alloc(sizeof (ctf_diff_obj_t) * oocount);
+ if (cds->cds_oobj == NULL)
+ return (ctf_set_errno(cds->cds_ifp, ENOMEM));
+ cds->cds_noobj = oocount;
+ cds->cds_nextoobj = 0;
+
+ cds->cds_ofillip = B_TRUE;
+ if ((ret = ctf_object_iter(cds->cds_ifp, ctf_diff_obj_fill_cb,
+ cds)) != 0)
+ return (ret);
+
+ cds->cds_ofillip = B_FALSE;
+ if ((ret = ctf_object_iter(cds->cds_ofp, ctf_diff_obj_fill_cb,
+ cds)) != 0)
+ return (ret);
+
+ for (i = 0; i < cds->cds_niobj; i++) {
+ for (j = 0; j < cds->cds_noobj; j++) {
+ ctf_diff_obj_t *id, *od;
+
+ id = &cds->cds_iobj[i];
+ od = &cds->cds_oobj[j];
+
+ if (id->cdo_name == NULL || od->cdo_name == NULL)
+ continue;
+ if (strcmp(id->cdo_name, od->cdo_name) != 0)
+ continue;
+
+ if (ctf_diff_symid(cds, id->cdo_id, od->cdo_id)) {
+ continue;
+ }
+
+ id->cdo_matchidx = j;
+ od->cdo_matchidx = i;
+ break;
+ }
+ }
+
+ return (0);
+}
+
+int
+ctf_diff_objects(ctf_diff_t *cds, ctf_diff_obj_f cb, void *arg)
+{
+ int ret;
+ ulong_t i;
+
+ if (cds->cds_tvalid == B_FALSE) {
+ if ((ret = ctf_diff_types(cds, ctf_diff_void_cb, NULL)) != 0)
+ return (ret);
+ }
+
+ if (cds->cds_ovalid == B_FALSE) {
+ if ((ret = ctf_diff_obj_fill(cds)) != 0)
+ return (ret);
+ cds->cds_ovalid = B_TRUE;
+ }
+
+ for (i = 0; i < cds->cds_niobj; i++) {
+ ctf_diff_obj_t *o = &cds->cds_iobj[i];
+
+ if (cds->cds_iobj[i].cdo_matchidx == ULONG_MAX) {
+ cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_FALSE,
+ NULL, ULONG_MAX, CTF_ERR, arg);
+ } else {
+ ctf_diff_obj_t *alt = &cds->cds_oobj[o->cdo_matchidx];
+ cb(cds->cds_ifp, o->cdo_symidx, o->cdo_id, B_TRUE,
+ cds->cds_ofp, alt->cdo_symidx, alt->cdo_id, arg);
+ }
+ }
+
+ for (i = 0; i < cds->cds_noobj; i++) {
+ ctf_diff_obj_t *o = &cds->cds_oobj[i];
+ if (o->cdo_matchidx != ULONG_MAX)
+ continue;
+ cb(cds->cds_ofp, o->cdo_symidx, o->cdo_id, B_FALSE, NULL,
+ ULONG_MAX, CTF_ERR, arg);
+ }
+
+ return (0);
+}
diff --git a/usr/src/lib/libctf/common/ctf_elfwrite.c b/usr/src/lib/libctf/common/ctf_elfwrite.c
new file mode 100644
index 0000000000..8c4ba62229
--- /dev/null
+++ b/usr/src/lib/libctf/common/ctf_elfwrite.c
@@ -0,0 +1,420 @@
+/*
+ * 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.
+ */
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
+
+/*
+ * Routines for writing ctf data to elf files, originally from the ctf tools.
+ */
+
+#include <ctf_impl.h>
+#include <libctf.h>
+#include <gelf.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <libelf.h>
+#include <sys/zmod.h>
+
+static int
+ctf_write_elf(ctf_file_t *fp, Elf *src, Elf *dst, int flags)
+{
+ GElf_Ehdr sehdr, dehdr;
+ Elf_Scn *sscn, *dscn;
+ Elf_Data *sdata, *ddata;
+ GElf_Shdr shdr;
+ int symtab_idx = -1;
+ off_t new_offset = 0;
+ off_t ctfnameoff = 0;
+ int keep_stabs = (flags & CTF_ELFWRITE_F_KEEP_STABS);
+ int compress = (flags & CTF_ELFWRITE_F_COMPRESS);
+ int *secxlate;
+ int srcidx, dstidx, pad, i;
+ int curnmoff = 0;
+ int changing = 0;
+ size_t nshdr, nphdr, strndx;
+
+ void *cdata = NULL;
+
+ if ((flags & ~(CTF_ELFWRITE_F_KEEP_STABS |
+ CTF_ELFWRITE_F_COMPRESS)) != 0)
+ return (ctf_set_errno(fp, EINVAL));
+
+ if (gelf_newehdr(dst, gelf_getclass(src)) == NULL)
+ return (ctf_set_errno(fp, ECTF_ELF));
+
+ if (gelf_getehdr(src, &sehdr) == NULL)
+ return (ctf_set_errno(fp, ECTF_ELF));
+ (void) memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr));
+ if (gelf_update_ehdr(dst, &dehdr) == 0)
+ return (ctf_set_errno(fp, ECTF_ELF));
+
+ /*
+ * Use libelf to get the number of sections and the string section to
+ * deal with ELF files that may have a large number of sections. We just
+ * always use this to make our live easier.
+ */
+ if (elf_getphdrnum(src, &nphdr) != 0)
+ return (ctf_set_errno(fp, ECTF_ELF));
+ if (elf_getshdrnum(src, &nshdr) != 0)
+ return (ctf_set_errno(fp, ECTF_ELF));
+ if (elf_getshdrstrndx(src, &strndx) != 0)
+ return (ctf_set_errno(fp, ECTF_ELF));
+
+ /*
+ * Neither the existing debug 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 (nphdr != 0) {
+ (void) elf_flagelf(dst, ELF_C_SET, ELF_F_LAYOUT);
+ if (gelf_newphdr(dst, nphdr) == NULL)
+ return (ctf_set_errno(fp, ECTF_ELF));
+
+ for (i = 0; i < nphdr; i++) {
+ GElf_Phdr phdr;
+
+ if (gelf_getphdr(src, i, &phdr) == NULL)
+ return (ctf_set_errno(fp, ECTF_ELF));
+ if (gelf_update_phdr(dst, i, &phdr) == 0)
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ }
+
+ secxlate = ctf_alloc(sizeof (int) * nshdr);
+ for (srcidx = dstidx = 0; srcidx < nshdr; srcidx++) {
+ Elf_Scn *scn = elf_getscn(src, srcidx);
+ GElf_Shdr shdr;
+ char *sname;
+
+ if (gelf_getshdr(scn, &shdr) == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ sname = elf_strptr(src, strndx, shdr.sh_name);
+ if (sname == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+
+ 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 {
+ secxlate[srcidx] = dstidx++;
+ curnmoff += strlen(sname) + 1;
+ }
+
+ new_offset = (off_t)dehdr.e_phoff;
+ }
+
+ for (srcidx = 1; srcidx < nshdr; srcidx++) {
+ char *sname;
+
+ sscn = elf_getscn(src, srcidx);
+ if (gelf_getshdr(sscn, &shdr) == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+
+ if (secxlate[srcidx] == -1) {
+ changing = 1;
+ continue;
+ }
+
+ dscn = elf_newscn(dst);
+ if (dscn == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+
+ /*
+ * 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 && nphdr != 0) {
+ pad = new_offset % shdr.sh_addralign;
+
+ if (pad != 0)
+ 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, strndx, shdr.sh_name);
+ if (sname == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ if ((sdata = elf_getdata(sscn, NULL)) == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ if ((ddata = elf_newdata(dscn)) == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ bcopy(sdata, ddata, sizeof (Elf_Data));
+
+ if (srcidx == strndx) {
+ char seclen = strlen(CTF_ELF_SCN_NAME);
+
+ ddata->d_buf = ctf_alloc(ddata->d_size + shdr.sh_size +
+ seclen + 1);
+ if (ddata->d_buf == NULL) {
+ ctf_free(secxlate,
+ sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
+ (void) 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 (nphdr != 0)
+ changing = 1;
+ }
+
+ if (shdr.sh_type == SHT_SYMTAB && shdr.sh_entsize != 0) {
+ int nsym = shdr.sh_size / shdr.sh_entsize;
+
+ symtab_idx = secxlate[srcidx];
+
+ ddata->d_buf = ctf_alloc(shdr.sh_size);
+ if (ddata->d_buf == NULL) {
+ ctf_free(secxlate,
+ sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ (void) bcopy(sdata->d_buf, ddata->d_buf, shdr.sh_size);
+
+ for (i = 0; i < nsym; i++) {
+ GElf_Sym sym;
+ short newscn;
+
+ (void) gelf_getsym(ddata, i, &sym);
+
+ if (sym.st_shndx >= SHN_LORESERVE)
+ continue;
+
+ if ((newscn = secxlate[sym.st_shndx]) !=
+ sym.st_shndx) {
+ sym.st_shndx =
+ (newscn == -1 ? 1 : newscn);
+
+ if (gelf_update_sym(ddata, i, &sym) ==
+ 0) {
+ ctf_free(secxlate,
+ sizeof (int) *
+ nshdr);
+ return (ctf_set_errno(fp,
+ ECTF_ELF));
+ }
+ }
+ }
+ }
+
+ if (gelf_update_shdr(dscn, &shdr) == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+
+ new_offset = (off_t)shdr.sh_offset;
+ if (shdr.sh_type != SHT_NOBITS)
+ new_offset += shdr.sh_size;
+ }
+
+ if (symtab_idx == -1) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+
+ /* Add the ctf section */
+ if ((dscn = elf_newscn(dst)) == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ if (gelf_getshdr(dscn, &shdr) == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ shdr.sh_name = ctfnameoff;
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_size = fp->ctf_size;
+ shdr.sh_link = symtab_idx;
+ shdr.sh_addralign = 4;
+ if (changing && nphdr != 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;
+ }
+
+ if ((ddata = elf_newdata(dscn)) == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+
+ if (compress != 0) {
+ size_t dlen;
+ ctf_header_t *cthp;
+ int err;
+
+ if (ctf_zopen(&err) == NULL) {
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, err));
+ }
+
+ dlen = fp->ctf_size;
+ cdata = ctf_data_alloc(dlen);
+ bcopy(fp->ctf_base, cdata, sizeof (ctf_header_t));
+ cthp = cdata;
+ cthp->cth_flags |= CTF_F_COMPRESS;
+ dlen -= sizeof (ctf_header_t);
+ if (z_compress((void *)((uintptr_t)cdata +
+ sizeof (ctf_header_t)), &dlen,
+ fp->ctf_base + sizeof (ctf_header_t),
+ fp->ctf_size - sizeof (ctf_header_t)) != Z_OK) {
+ ctf_data_free(cdata, fp->ctf_size);
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ZLIB));
+ }
+ ddata->d_buf = cdata;
+ ddata->d_size = dlen + sizeof (ctf_header_t);
+ } else {
+ ddata->d_buf = (void *)fp->ctf_base;
+ ddata->d_size = fp->ctf_size;
+ }
+ ddata->d_align = shdr.sh_addralign;
+
+ if (gelf_update_shdr(dscn, &shdr) == 0) {
+ if (cdata != NULL)
+ ctf_data_free(cdata, fp->ctf_size);
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+
+ /* update the section header location */
+ if (nphdr != 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 */
+ if (sehdr.e_shstrndx == SHN_XINDEX)
+ dehdr.e_shstrndx = SHN_XINDEX;
+ else
+ dehdr.e_shstrndx = secxlate[sehdr.e_shstrndx];
+ if (gelf_update_ehdr(dst, &dehdr) == NULL) {
+ if (cdata != NULL)
+ ctf_data_free(cdata, fp->ctf_size);
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+ if (elf_update(dst, ELF_C_WRITE) < 0) {
+ if (cdata != NULL)
+ ctf_data_free(cdata, fp->ctf_size);
+ ctf_free(secxlate, sizeof (int) * nshdr);
+ return (ctf_set_errno(fp, ECTF_ELF));
+ }
+
+ if (cdata != NULL)
+ ctf_data_free(cdata, fp->ctf_size);
+ ctf_free(secxlate, sizeof (int) * nshdr);
+
+ return (0);
+}
+
+int
+ctf_elffdwrite(ctf_file_t *fp, int ifd, int ofd, int flags)
+{
+ int ret;
+ Elf *ielf, *oelf;
+
+ (void) elf_version(EV_CURRENT);
+ if ((ielf = elf_begin(ifd, ELF_C_READ, NULL)) == NULL)
+ return (ctf_set_errno(fp, ECTF_ELF));
+
+ if ((oelf = elf_begin(ofd, ELF_C_WRITE, NULL)) == NULL)
+ return (ctf_set_errno(fp, ECTF_ELF));
+
+ ret = ctf_write_elf(fp, ielf, oelf, flags);
+
+ (void) elf_end(ielf);
+ (void) elf_end(oelf);
+
+ return (ret);
+}
+
+int
+ctf_elfwrite(ctf_file_t *fp, const char *input, const char *output, int flags)
+{
+ struct stat st;
+ int ifd, ofd, ret;
+
+ if ((ifd = open(input, O_RDONLY)) < 0)
+ return (ctf_set_errno(fp, errno));
+
+ if (fstat(ifd, &st) < 0)
+ return (ctf_set_errno(fp, errno));
+
+ if ((ofd = open(output, O_RDWR | O_CREAT | O_TRUNC, st.st_mode)) < 0)
+ return (ctf_set_errno(fp, errno));
+
+ ret = ctf_elffdwrite(fp, ifd, ofd, flags);
+
+ if (close(ifd) != 0 && ret != 0)
+ ret = ctf_set_errno(fp, errno);
+ if (close(ofd) != 0 && ret != 0)
+ ret = ctf_set_errno(fp, errno);
+
+ return (ret);
+}
diff --git a/usr/src/lib/libctf/common/ctf_lib.c b/usr/src/lib/libctf/common/ctf_lib.c
index e71ebc6d9d..5071f693a2 100644
--- a/usr/src/lib/libctf/common/ctf_lib.c
+++ b/usr/src/lib/libctf/common/ctf_lib.c
@@ -23,6 +23,9 @@
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
#include <sys/types.h>
#include <sys/stat.h>
@@ -33,6 +36,7 @@
#include <errno.h>
#include <dlfcn.h>
#include <gelf.h>
+#include <zlib.h>
#ifdef _LP64
static const char *_libctf_zlib = "/usr/lib/64/libz.so.1";
@@ -42,6 +46,9 @@ static const char *_libctf_zlib = "/usr/lib/libz.so.1";
static struct {
int (*z_uncompress)(uchar_t *, ulong_t *, const uchar_t *, ulong_t);
+ int (*z_initcomp)(z_stream *, int, const char *, int);
+ int (*z_compress)(z_stream *, int);
+ int (*z_finicomp)(z_stream *);
const char *(*z_error)(int);
void *z_dlp;
} zlib;
@@ -84,9 +91,14 @@ ctf_zopen(int *errp)
return (ctf_set_open_errno(errp, ECTF_ZINIT));
zlib.z_uncompress = (int (*)()) dlsym(zlib.z_dlp, "uncompress");
+ zlib.z_initcomp = (int (*)()) dlsym(zlib.z_dlp, "deflateInit_");
+ zlib.z_compress = (int (*)()) dlsym(zlib.z_dlp, "deflate");
+ zlib.z_finicomp = (int (*)()) dlsym(zlib.z_dlp, "deflateEnd");
zlib.z_error = (const char *(*)()) dlsym(zlib.z_dlp, "zError");
- if (zlib.z_uncompress == NULL || zlib.z_error == NULL) {
+ if (zlib.z_uncompress == NULL || zlib.z_error == NULL ||
+ zlib.z_initcomp == NULL|| zlib.z_compress == NULL ||
+ zlib.z_finicomp == NULL) {
(void) dlclose(zlib.z_dlp);
bzero(&zlib, sizeof (zlib));
return (ctf_set_open_errno(errp, ECTF_ZINIT));
@@ -111,6 +123,31 @@ z_strerror(int err)
return (zlib.z_error(err));
}
+int
+z_compress(void *dst, size_t *dstlen, const void *src, size_t srclen)
+{
+ z_stream zs;
+ int err;
+
+ bzero(&zs, sizeof (z_stream));
+ zs.next_in = (uchar_t *)src;
+ zs.avail_in = srclen;
+ zs.next_out = dst;
+ zs.avail_out = *dstlen;
+
+ if ((err = zlib.z_initcomp(&zs, Z_BEST_COMPRESSION, ZLIB_VERSION,
+ sizeof (z_stream))) != Z_OK)
+ return (err);
+
+ if ((err = zlib.z_compress(&zs, Z_FINISH)) != Z_STREAM_END) {
+ (void) zlib.z_finicomp(&zs);
+ return (err == Z_OK ? Z_BUF_ERROR : err);
+ }
+
+ *dstlen = zs.total_out;
+ return (zlib.z_finicomp(&zs));
+}
+
/*
* Convert a 32-bit ELF file header into GElf.
*/
@@ -189,7 +226,7 @@ ctf_sect_munmap(const ctf_sect_t *sp)
* responsible for closing the file descriptor when it is no longer needed.
*/
ctf_file_t *
-ctf_fdopen(int fd, int *errp)
+ctf_fdcreate_int(int fd, int *errp, ctf_sect_t *ctfp)
{
ctf_sect_t ctfsect, symsect, strsect;
ctf_file_t *fp = NULL;
@@ -221,6 +258,9 @@ ctf_fdopen(int fd, int *errp)
*/
if (nbytes >= sizeof (ctf_preamble_t) &&
hdr.ctf.ctp_magic == CTF_MAGIC) {
+ if (ctfp != NULL)
+ return (ctf_set_open_errno(errp, EINVAL));
+
if (hdr.ctf.ctp_version > CTF_VERSION)
return (ctf_set_open_errno(errp, ECTF_CTFVERS));
@@ -370,7 +410,8 @@ ctf_fdopen(int fd, int *errp)
continue; /* corrupt sh_name field */
if (shp->sh_type == SHT_PROGBITS &&
- strcmp(strs + shp->sh_name, _CTF_SECTION) == 0) {
+ strcmp(strs + shp->sh_name, _CTF_SECTION) == 0 &&
+ ctfp == NULL) {
ctfsect.cts_name = strs + shp->sh_name;
ctfsect.cts_type = shp->sh_type;
ctfsect.cts_flags = shp->sh_flags;
@@ -397,18 +438,22 @@ ctf_fdopen(int fd, int *errp)
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));
- }
+ if (ctfp == NULL) {
+ if (ctfsect.cts_type == SHT_NULL && ctfp == 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));
+ /*
+ * 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));
+ }
+ ctfp = &ctfsect;
}
if (symsect.cts_type != SHT_NULL &&
@@ -418,12 +463,13 @@ ctf_fdopen(int fd, int *errp)
(void) ctf_set_open_errno(errp, ECTF_MMAP);
goto bad; /* unmap all and abort */
}
- fp = ctf_bufopen(&ctfsect, &symsect, &strsect, errp);
+ fp = ctf_bufopen(ctfp, &symsect, &strsect, errp);
} else
- fp = ctf_bufopen(&ctfsect, NULL, NULL, errp);
+ fp = ctf_bufopen(ctfp, NULL, NULL, errp);
bad:
if (fp == NULL) {
- ctf_sect_munmap(&ctfsect);
+ if (ctfp == NULL)
+ ctf_sect_munmap(&ctfsect);
ctf_sect_munmap(&symsect);
ctf_sect_munmap(&strsect);
} else
@@ -436,6 +482,12 @@ bad:
return (ctf_set_open_errno(errp, ECTF_FMT));
}
+ctf_file_t *
+ctf_fdopen(int fd, int *errp)
+{
+ return (ctf_fdcreate_int(fd, errp, NULL));
+}
+
/*
* 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
diff --git a/usr/src/lib/libctf/common/ctf_merge.c b/usr/src/lib/libctf/common/ctf_merge.c
new file mode 100644
index 0000000000..70676bb0fd
--- /dev/null
+++ b/usr/src/lib/libctf/common/ctf_merge.c
@@ -0,0 +1,1231 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015 Joyent, Inc.
+ */
+
+/*
+ * To perform a merge of two CTF containers, we first diff the two containers
+ * types. For every type that's in the src container, but not in the dst
+ * container, we note it and add it to dst container. If there are any objects
+ * or functions associated with src, we go through and update the types that
+ * they refer to such that they all refer to types in the dst container.
+ *
+ * The bulk of the logic for the merge, after we've run the diff, occurs in
+ * ctf_merge_common().
+ *
+ * In terms of exported APIs, we don't really export a simple merge two
+ * containers, as the general way this is used, in something like ctfmerge(1),
+ * is to add all the containers and then let us figure out the best way to merge
+ * it. In the future we'll want to grow some control over the number of threads
+ * that we use to do this, if we end up wanting a muli-threaded merge. If we do,
+ * we should take care to do the merge in the same way every time.
+ */
+
+#include <ctf_impl.h>
+#include <libctf.h>
+#include <sys/debug.h>
+#include <sys/list.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdio.h>
+
+typedef struct ctf_merge_tinfo {
+ uint16_t cmt_map; /* Map to the type in out */
+ boolean_t cmt_fixup;
+ boolean_t cmt_forward;
+ boolean_t cmt_missing;
+} ctf_merge_tinfo_t;
+
+/*
+ * State required for doing an individual merge of two containers.
+ */
+typedef struct ctf_merge_types {
+ ctf_file_t *cm_out; /* Output CTF file */
+ ctf_file_t *cm_src; /* Input CTF file */
+ ctf_merge_tinfo_t *cm_tmap; /* Type state information */
+} ctf_merge_types_t;
+
+typedef struct ctf_merge_objmap {
+ list_node_t cmo_node;
+ const char *cmo_name; /* Symbol name */
+ ulong_t cmo_idx; /* Symbol ID */
+ ctf_id_t cmo_tid; /* Type ID */
+} ctf_merge_objmap_t;
+
+typedef struct ctf_merge_funcmap {
+ list_node_t cmf_node;
+ const char *cmf_name; /* Symbol name */
+ ulong_t cmf_idx; /* Symbol ID */
+ ctf_id_t cmf_rtid; /* Type ID */
+ uint_t cmf_flags; /* ctf_funcinfo_t ctc_flags */
+ uint_t cmf_argc; /* Number of arguments */
+ ctf_id_t cmf_args[]; /* Types of arguments */
+} ctf_merge_funcmap_t;
+
+typedef struct ctf_merge_input {
+ list_node_t cmi_node;
+ ctf_file_t *cmi_input;
+ list_t cmi_omap;
+ list_t cmi_fmap;
+} ctf_merge_input_t;
+
+struct ctf_merge_handle {
+ ctf_file_t *cmh_output; /* Final output */
+ list_t cmh_inputs; /* Input list */
+ ctf_file_t *cmh_unique; /* ctf to uniquify against */
+ boolean_t cmh_msyms; /* Should we merge symbols/funcs? */
+ int cmh_ofd; /* FD for output file */
+ int cmh_flags; /* Flags that control merge behavior */
+ char *cmh_label; /* Optional label */
+ char *cmh_pname; /* Parent name */
+};
+
+static int ctf_merge_add_type(ctf_merge_types_t *, ctf_id_t);
+
+static void
+ctf_merge_diffcb(ctf_file_t *ifp, ctf_id_t iid, boolean_t same, ctf_file_t *ofp,
+ ctf_id_t oid, void *arg)
+{
+ ctf_merge_types_t *cmp = arg;
+ ctf_merge_tinfo_t *cmt = cmp->cm_tmap;
+
+ if (same == B_TRUE) {
+ if (ctf_type_kind(ifp, iid) == CTF_K_FORWARD &&
+ ctf_type_kind(ofp, oid) != CTF_K_FORWARD) {
+ VERIFY(cmt[oid].cmt_map == 0);
+ cmt[oid].cmt_map = iid;
+ cmt[oid].cmt_forward = B_TRUE;
+ return;
+ }
+
+ /*
+ * We could have multiple things that a given type ends up
+ * matching in the world of forwards and pointers to forwards.
+ * For now just take the first one...
+ */
+ if (cmt[oid].cmt_map != 0)
+ return;
+ cmt[oid].cmt_map = iid;
+ } else if (ifp == cmp->cm_src) {
+ VERIFY(cmt[iid].cmt_map == 0);
+ cmt[iid].cmt_missing = B_TRUE;
+ }
+}
+
+static int
+ctf_merge_add_number(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ const char *name;
+ ctf_encoding_t en;
+
+ if (ctf_type_encoding(cmp->cm_src, id, &en) != 0)
+ return (CTF_ERR);
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ ret = ctf_add_encoded(cmp->cm_out, flags, name, &en,
+ ctf_type_kind(cmp->cm_src, id));
+
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+static int
+ctf_merge_add_array(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ ctf_arinfo_t ar;
+
+ if (ctf_array_info(cmp->cm_src, id, &ar) == CTF_ERR)
+ return (CTF_ERR);
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ if (cmp->cm_tmap[ar.ctr_contents].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, ar.ctr_contents);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[ar.ctr_contents].cmt_map != 0);
+ }
+ ar.ctr_contents = cmp->cm_tmap[ar.ctr_contents].cmt_map;
+
+ if (cmp->cm_tmap[ar.ctr_index].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, ar.ctr_index);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[ar.ctr_index].cmt_map != 0);
+ }
+ ar.ctr_index = cmp->cm_tmap[ar.ctr_index].cmt_map;
+
+ ret = ctf_add_array(cmp->cm_out, flags, &ar);
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+
+ return (0);
+}
+
+static int
+ctf_merge_add_reftype(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ ctf_id_t reftype;
+ const char *name;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ reftype = ctf_type_reference(cmp->cm_src, id);
+ if (reftype == CTF_ERR)
+ return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src)));
+
+ if (cmp->cm_tmap[reftype].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, reftype);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[reftype].cmt_map != 0);
+ }
+ reftype = cmp->cm_tmap[reftype].cmt_map;
+
+ ret = ctf_add_reftype(cmp->cm_out, flags, name, reftype,
+ ctf_type_kind(cmp->cm_src, id));
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+static int
+ctf_merge_add_typedef(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ const char *name;
+ ctf_id_t reftype;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ reftype = ctf_type_reference(cmp->cm_src, id);
+ if (reftype == CTF_ERR)
+ return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src)));
+
+ if (cmp->cm_tmap[reftype].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, reftype);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[reftype].cmt_map != 0);
+ }
+ reftype = cmp->cm_tmap[reftype].cmt_map;
+
+ ret = ctf_add_typedef(cmp->cm_out, flags, name, reftype);
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+typedef struct ctf_merge_enum {
+ ctf_file_t *cme_fp;
+ ctf_id_t cme_id;
+} ctf_merge_enum_t;
+
+static int
+ctf_merge_add_enumerator(const char *name, int value, void *arg)
+{
+ ctf_merge_enum_t *cmep = arg;
+
+ return (ctf_add_enumerator(cmep->cme_fp, cmep->cme_id, name, value) ==
+ CTF_ERR);
+}
+
+static int
+ctf_merge_add_enum(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int flags;
+ const ctf_type_t *tp;
+ const char *name;
+ ctf_id_t enumid;
+ ctf_merge_enum_t cme;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ enumid = ctf_add_enum(cmp->cm_out, flags, name);
+ if (enumid == CTF_ERR)
+ return (enumid);
+
+ cme.cme_fp = cmp->cm_out;
+ cme.cme_id = enumid;
+ if (ctf_enum_iter(cmp->cm_src, id, ctf_merge_add_enumerator,
+ &cme) != 0)
+ return (CTF_ERR);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = enumid;
+ return (0);
+}
+
+static int
+ctf_merge_add_func(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags, i;
+ const ctf_type_t *tp;
+ ctf_funcinfo_t ctc;
+ ctf_id_t *argv;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ if (ctf_func_info_by_id(cmp->cm_src, id, &ctc) == CTF_ERR)
+ return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src)));
+
+ argv = ctf_alloc(sizeof (ctf_id_t) * ctc.ctc_argc);
+ if (argv == NULL)
+ return (ctf_set_errno(cmp->cm_out, ENOMEM));
+ if (ctf_func_args_by_id(cmp->cm_src, id, ctc.ctc_argc, argv) ==
+ CTF_ERR) {
+ ctf_free(argv, sizeof (ctf_id_t) * ctc.ctc_argc);
+ return (ctf_set_errno(cmp->cm_out, ctf_errno(cmp->cm_src)));
+ }
+
+ if (cmp->cm_tmap[ctc.ctc_return].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, ctc.ctc_return);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[ctc.ctc_return].cmt_map != 0);
+ }
+ ctc.ctc_return = cmp->cm_tmap[ctc.ctc_return].cmt_map;
+
+ for (i = 0; i < ctc.ctc_argc; i++) {
+ if (cmp->cm_tmap[argv[i]].cmt_map == 0) {
+ ret = ctf_merge_add_type(cmp, argv[i]);
+ if (ret != 0)
+ return (ret);
+ ASSERT(cmp->cm_tmap[argv[i]].cmt_map != 0);
+ }
+ argv[i] = cmp->cm_tmap[argv[i]].cmt_map;
+ }
+
+ ret = ctf_add_funcptr(cmp->cm_out, flags, &ctc, argv);
+ ctf_free(argv, sizeof (ctf_id_t) * ctc.ctc_argc);
+ if (ret == CTF_ERR)
+ return (ret);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+static int
+ctf_merge_add_forward(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int ret, flags;
+ const ctf_type_t *tp;
+ const char *name;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+
+ /*
+ * ctf_add_forward tries to check to see if a given forward already
+ * exists in one of its hash tables. If we're here then we know that we
+ * have a forward in a container that isn't present in another.
+ * Therefore, we choose a token hash table to satisfy the API choice
+ * here.
+ */
+ ret = ctf_add_forward(cmp->cm_out, flags, name, CTF_K_STRUCT);
+ if (ret == CTF_ERR)
+ return (CTF_ERR);
+
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = ret;
+ return (0);
+}
+
+typedef struct ctf_merge_su {
+ ctf_merge_types_t *cms_cm;
+ ctf_id_t cms_id;
+} ctf_merge_su_t;
+
+static int
+ctf_merge_add_member(const char *name, ctf_id_t type, ulong_t offset, void *arg)
+{
+ ctf_merge_su_t *cms = arg;
+
+ VERIFY(cms->cms_cm->cm_tmap[type].cmt_map != 0);
+ type = cms->cms_cm->cm_tmap[type].cmt_map;
+
+ return (ctf_add_member(cms->cms_cm->cm_out, cms->cms_id, name,
+ type, offset) == CTF_ERR);
+}
+
+/*
+ * During the first pass, we always add the generic structure and union but none
+ * of its members as they might not all have been mapped yet. Instead we just
+ * mark all structures and unions as needing to be fixed up.
+ */
+static int
+ctf_merge_add_su(ctf_merge_types_t *cmp, ctf_id_t id, boolean_t forward)
+{
+ int flags, kind;
+ const ctf_type_t *tp;
+ const char *name;
+ ctf_id_t suid;
+
+ tp = LCTF_INDEX_TO_TYPEPTR(cmp->cm_src, id);
+ name = ctf_strraw(cmp->cm_src, tp->ctt_name);
+ if (CTF_INFO_ISROOT(tp->ctt_info) != 0)
+ flags = CTF_ADD_ROOT;
+ else
+ flags = CTF_ADD_NONROOT;
+ kind = ctf_type_kind(cmp->cm_src, id);
+
+ if (kind == CTF_K_STRUCT)
+ suid = ctf_add_struct(cmp->cm_out, flags, name);
+ else
+ suid = ctf_add_union(cmp->cm_out, flags, name);
+
+ if (suid == CTF_ERR)
+ return (suid);
+
+ /*
+ * If this is a forward reference then it's mapping should already
+ * exist.
+ */
+ if (forward == B_FALSE) {
+ VERIFY(cmp->cm_tmap[id].cmt_map == 0);
+ cmp->cm_tmap[id].cmt_map = suid;
+ } else {
+ VERIFY(cmp->cm_tmap[id].cmt_map == suid);
+ }
+ cmp->cm_tmap[id].cmt_fixup = B_TRUE;
+
+ return (0);
+}
+
+static int
+ctf_merge_add_type(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int kind, ret;
+
+ /*
+ * We may end up evaluating a type more than once as we may deal with it
+ * as we recursively evaluate some kind of reference and then we may see
+ * it normally.
+ */
+ if (cmp->cm_tmap[id].cmt_map != 0)
+ return (0);
+
+ kind = ctf_type_kind(cmp->cm_src, id);
+ switch (kind) {
+ case CTF_K_INTEGER:
+ case CTF_K_FLOAT:
+ ret = ctf_merge_add_number(cmp, id);
+ break;
+ case CTF_K_ARRAY:
+ ret = ctf_merge_add_array(cmp, id);
+ break;
+ case CTF_K_POINTER:
+ case CTF_K_VOLATILE:
+ case CTF_K_CONST:
+ case CTF_K_RESTRICT:
+ ret = ctf_merge_add_reftype(cmp, id);
+ break;
+ case CTF_K_TYPEDEF:
+ ret = ctf_merge_add_typedef(cmp, id);
+ break;
+ case CTF_K_ENUM:
+ ret = ctf_merge_add_enum(cmp, id);
+ break;
+ case CTF_K_FUNCTION:
+ ret = ctf_merge_add_func(cmp, id);
+ break;
+ case CTF_K_FORWARD:
+ ret = ctf_merge_add_forward(cmp, id);
+ break;
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ ret = ctf_merge_add_su(cmp, id, B_FALSE);
+ break;
+ case CTF_K_UNKNOWN:
+ /*
+ * We don't add uknown types, and we later assert that nothing
+ * should reference them.
+ */
+ return (0);
+ default:
+ abort();
+ }
+
+ return (ret);
+}
+
+static int
+ctf_merge_fixup_su(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ ctf_dtdef_t *dtd;
+ ctf_merge_su_t cms;
+ ctf_id_t mapid;
+
+ mapid = cmp->cm_tmap[id].cmt_map;
+ VERIFY(mapid != 0);
+ dtd = ctf_dtd_lookup(cmp->cm_out, mapid);
+ VERIFY(dtd != NULL);
+
+ cms.cms_cm = cmp;
+ cms.cms_id = mapid;
+ if (ctf_member_iter(cmp->cm_src, id, ctf_merge_add_member, &cms) != 0)
+ return (CTF_ERR);
+
+ return (0);
+}
+
+static int
+ctf_merge_fixup_type(ctf_merge_types_t *cmp, ctf_id_t id)
+{
+ int kind, ret;
+
+ kind = ctf_type_kind(cmp->cm_src, id);
+ switch (kind) {
+ case CTF_K_STRUCT:
+ case CTF_K_UNION:
+ ret = ctf_merge_fixup_su(cmp, id);
+ break;
+ default:
+ VERIFY(0);
+ ret = CTF_ERR;
+ }
+
+ return (ret);
+}
+
+
+/*
+ * We're going to do three passes over the containers.
+ *
+ * Pass 1 checks for forward references in the output container that we know
+ * exist in the source container.
+ *
+ * Pass 2 adds all the missing types from the source container. As part of this
+ * we may be adding a type as a forward reference that doesn't exist yet.
+ * Any types that we encounter in this form, we need to add to a third pass.
+ *
+ * Pass 3 is the fixup pass. Here we go through and find all the types that were
+ * missing in the first.
+ *
+ * Importantly, we *must* call ctf_update between the second and third pass,
+ * otherwise several of the libctf functions will not properly find the data in
+ * the container.
+ */
+static int
+ctf_merge_common(ctf_merge_types_t *cmp)
+{
+ int ret, i;
+
+ /* Pass 1 */
+ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
+ if (cmp->cm_tmap[i].cmt_forward == B_TRUE) {
+ ret = ctf_merge_add_su(cmp, i, B_TRUE);
+ if (ret != 0)
+ return (ret);
+ }
+ }
+
+ /* Pass 2 */
+ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
+ if (cmp->cm_tmap[i].cmt_missing == B_TRUE) {
+ ret = ctf_merge_add_type(cmp, i);
+ if (ret != 0)
+ return (ret);
+ }
+ }
+
+ ret = ctf_update(cmp->cm_out);
+ if (ret != 0)
+ return (ret);
+
+ /* Pass 3 */
+ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
+ if (cmp->cm_tmap[i].cmt_fixup == B_TRUE) {
+ ret = ctf_merge_fixup_type(cmp, i);
+ if (ret != 0)
+ return (ret);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Uniquification is slightly different from a stock merge. For starters, we
+ * don't need to replace any forward references in the output. In this case
+ * though, the types that already exist are in a parent container to the empty
+ * output container.
+ */
+static int
+ctf_merge_uniquify_types(ctf_merge_types_t *cmp)
+{
+ int i, ret;
+
+ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
+ if (cmp->cm_tmap[i].cmt_missing == B_FALSE)
+ continue;
+ ret = ctf_merge_add_type(cmp, i);
+ if (ret != 0)
+ return (ret);
+ }
+
+ ret = ctf_update(cmp->cm_out);
+ if (ret != 0)
+ return (ret);
+
+ for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
+ if (cmp->cm_tmap[i].cmt_fixup == B_FALSE)
+ continue;
+ ret = ctf_merge_fixup_type(cmp, i);
+ if (ret != 0)
+ return (ret);
+ }
+
+ return (0);
+}
+
+static int
+ctf_merge_types_init(ctf_merge_types_t *cmp)
+{
+ cmp->cm_tmap = ctf_alloc(sizeof (ctf_merge_tinfo_t) *
+ (cmp->cm_src->ctf_typemax + 1));
+ if (cmp->cm_tmap == NULL)
+ return (ctf_set_errno(cmp->cm_out, ENOMEM));
+ bzero(cmp->cm_tmap, sizeof (ctf_merge_tinfo_t) *
+ (cmp->cm_src->ctf_typemax + 1));
+ return (0);
+}
+
+static void
+ctf_merge_types_fini(ctf_merge_types_t *cmp)
+{
+ ctf_free(cmp->cm_tmap, sizeof (ctf_merge_tinfo_t) *
+ (cmp->cm_src->ctf_typemax + 1));
+}
+
+/*
+ * Merge types from targ into dest.
+ */
+static int
+ctf_merge(ctf_file_t **outp, ctf_merge_input_t *cmi)
+{
+ int ret;
+ ctf_merge_types_t cm;
+ ctf_diff_t *cdp;
+ ctf_merge_objmap_t *cmo;
+ ctf_merge_funcmap_t *cmf;
+ ctf_file_t *out = *outp;
+ ctf_file_t *source = cmi->cmi_input;
+
+ if (!(out->ctf_flags & LCTF_RDWR))
+ return (ctf_set_errno(out, ECTF_RDONLY));
+
+ if (ctf_getmodel(out) != ctf_getmodel(source))
+ return (ctf_set_errno(out, ECTF_DMODEL));
+
+ if ((ret = ctf_diff_init(out, source, &cdp)) != 0)
+ return (ret);
+
+ cm.cm_out = out;
+ cm.cm_src = source;
+ ret = ctf_merge_types_init(&cm);
+ if (ret != 0) {
+ ctf_diff_fini(cdp);
+ return (ctf_set_errno(out, ret));
+ }
+
+ ret = ctf_diff_types(cdp, ctf_merge_diffcb, &cm);
+ if (ret != 0)
+ goto cleanup;
+ ret = ctf_merge_common(&cm);
+ if (ret == 0)
+ ret = ctf_update(out);
+ else
+ goto cleanup;
+
+ /*
+ * Now we need to fix up the object and function maps.
+ */
+ for (cmo = list_head(&cmi->cmi_omap); cmo != NULL;
+ cmo = list_next(&cmi->cmi_omap, cmo)) {
+ if (cmo->cmo_tid == 0)
+ continue;
+ VERIFY(cm.cm_tmap[cmo->cmo_tid].cmt_map != 0);
+ cmo->cmo_tid = cm.cm_tmap[cmo->cmo_tid].cmt_map;
+ }
+
+ for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL;
+ cmf = list_next(&cmi->cmi_fmap, cmf)) {
+ int i;
+
+ VERIFY(cm.cm_tmap[cmf->cmf_rtid].cmt_map != 0);
+ cmf->cmf_rtid = cm.cm_tmap[cmf->cmf_rtid].cmt_map;
+ for (i = 0; i < cmf->cmf_argc; i++) {
+ VERIFY(cm.cm_tmap[cmf->cmf_args[i]].cmt_map != 0);
+ cmf->cmf_args[i] = cm.cm_tmap[cmf->cmf_args[i]].cmt_map;
+ }
+ }
+
+cleanup:
+ ctf_merge_types_fini(&cm);
+ ctf_diff_fini(cdp);
+ return (ret);
+}
+
+static int
+ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t **outp)
+{
+ int err, ret;
+ ctf_file_t *out;
+ ctf_merge_types_t cm;
+ ctf_diff_t *cdp;
+ ctf_merge_input_t *cmi;
+ ctf_file_t *src = cmh->cmh_output;
+ ctf_file_t *parent = cmh->cmh_unique;
+
+ *outp = NULL;
+ if (cmh->cmh_ofd == -1)
+ out = ctf_create(&err);
+ else
+ out = ctf_fdcreate(cmh->cmh_ofd, &err);
+ if (out == NULL)
+ return (ctf_set_errno(src, err));
+
+ out->ctf_parname = cmh->cmh_pname;
+ if (ctf_setmodel(out, ctf_getmodel(parent)) != 0) {
+ (void) ctf_set_errno(src, ctf_errno(out));
+ ctf_close(out);
+ return (CTF_ERR);
+ }
+
+ if (ctf_import(out, parent) != 0) {
+ (void) ctf_set_errno(src, ctf_errno(out));
+ ctf_close(out);
+ return (CTF_ERR);
+ }
+
+ if ((ret = ctf_diff_init(parent, src, &cdp)) != 0) {
+ ctf_close(out);
+ return (ctf_set_errno(src, ctf_errno(parent)));
+ }
+
+ cm.cm_out = parent;
+ cm.cm_src = src;
+ ret = ctf_merge_types_init(&cm);
+ if (ret != 0) {
+ ctf_close(out);
+ ctf_diff_fini(cdp);
+ return (ctf_set_errno(src, ret));
+ }
+
+ ret = ctf_diff_types(cdp, ctf_merge_diffcb, &cm);
+ if (ret == 0) {
+ cm.cm_out = out;
+ ret = ctf_merge_uniquify_types(&cm);
+ if (ret == 0)
+ ret = ctf_update(out);
+ }
+
+ if (ret != 0) {
+ ctf_merge_types_fini(&cm);
+ ctf_diff_fini(cdp);
+ return (ctf_set_errno(src, ctf_errno(cm.cm_out)));
+ }
+
+ for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
+ cmi = list_next(&cmh->cmh_inputs, cmi)) {
+ ctf_merge_objmap_t *cmo;
+ ctf_merge_funcmap_t *cmf;
+
+ for (cmo = list_head(&cmi->cmi_omap); cmo != NULL;
+ cmo = list_next(&cmi->cmi_omap, cmo)) {
+ if (cmo->cmo_tid == 0)
+ continue;
+ VERIFY(cm.cm_tmap[cmo->cmo_tid].cmt_map != 0);
+ cmo->cmo_tid = cm.cm_tmap[cmo->cmo_tid].cmt_map;
+ }
+
+ for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL;
+ cmf = list_next(&cmi->cmi_fmap, cmf)) {
+ int i;
+
+ VERIFY(cm.cm_tmap[cmf->cmf_rtid].cmt_map != 0);
+ cmf->cmf_rtid = cm.cm_tmap[cmf->cmf_rtid].cmt_map;
+ for (i = 0; i < cmf->cmf_argc; i++) {
+ VERIFY(cm.cm_tmap[cmf->cmf_args[i]].cmt_map !=
+ 0);
+ cmf->cmf_args[i] =
+ cm.cm_tmap[cmf->cmf_args[i]].cmt_map;
+ }
+ }
+ }
+
+ ctf_merge_types_fini(&cm);
+ ctf_diff_fini(cdp);
+ *outp = out;
+ return (0);
+}
+
+static void
+ctf_merge_fini_input(ctf_merge_input_t *cmi)
+{
+ ctf_merge_objmap_t *cmo;
+ ctf_merge_funcmap_t *cmf;
+
+ while ((cmo = list_remove_head(&cmi->cmi_omap)) != NULL)
+ ctf_free(cmo, sizeof (ctf_merge_objmap_t));
+
+ while ((cmf = list_remove_head(&cmi->cmi_fmap)) != NULL)
+ ctf_free(cmf, sizeof (ctf_merge_funcmap_t) +
+ sizeof (ctf_id_t) * cmf->cmf_argc);
+
+ ctf_free(cmi, sizeof (ctf_merge_input_t));
+}
+
+void
+ctf_merge_fini(ctf_merge_t *cmh)
+{
+ size_t len;
+ ctf_merge_input_t *cmi;
+
+ if (cmh->cmh_output != NULL)
+ ctf_close(cmh->cmh_output);
+
+ if (cmh->cmh_label != NULL) {
+ len = strlen(cmh->cmh_label) + 1;
+ ctf_free(cmh->cmh_label, len);
+ }
+
+ if (cmh->cmh_pname != NULL) {
+ len = strlen(cmh->cmh_pname) + 1;
+ ctf_free(cmh->cmh_pname, len);
+ }
+
+ while ((cmi = list_remove_head(&cmh->cmh_inputs)) != NULL)
+ ctf_merge_fini_input(cmi);
+
+ ctf_free(cmh, sizeof (ctf_merge_t));
+}
+
+ctf_merge_t *
+ctf_merge_init(int fd, int *errp)
+{
+ int err;
+ ctf_merge_t *out;
+ struct stat st;
+
+ if (errp == NULL)
+ errp = &err;
+
+ if (fd != -1 && fstat(fd, &st) != 0) {
+ *errp = EINVAL;
+ return (NULL);
+ }
+
+ out = ctf_alloc(sizeof (ctf_merge_t));
+ if (out == NULL) {
+ *errp = ENOMEM;
+ return (NULL);
+ }
+
+ if (fd == -1) {
+ out->cmh_output = ctf_create(errp);
+ out->cmh_msyms = B_FALSE;
+ } else {
+ out->cmh_output = ctf_fdcreate(fd, errp);
+ out->cmh_msyms = B_TRUE;
+ }
+
+ if (out->cmh_output == NULL) {
+ ctf_free(out, sizeof (ctf_merge_t));
+ return (NULL);
+ }
+
+ list_create(&out->cmh_inputs, sizeof (ctf_merge_input_t),
+ offsetof(ctf_merge_input_t, cmi_node));
+ out->cmh_unique = NULL;
+ out->cmh_ofd = fd;
+ out->cmh_label = NULL;
+ out->cmh_pname = NULL;
+
+ return (out);
+}
+
+int
+ctf_merge_label(ctf_merge_t *cmh, const char *label)
+{
+ char *dup;
+
+ if (cmh->cmh_output == NULL)
+ return (EINVAL);
+
+ if (label == NULL)
+ return (EINVAL);
+
+ dup = ctf_strdup(label);
+ if (dup == NULL)
+ return (EAGAIN);
+
+ if (cmh->cmh_label != NULL) {
+ size_t len = strlen(cmh->cmh_label) + 1;
+ ctf_free(cmh->cmh_label, len);
+ }
+
+ cmh->cmh_label = dup;
+ return (0);
+}
+
+static int
+ctf_merge_add_funcs(const char *name, ulong_t idx, ctf_funcinfo_t *fip,
+ void *arg)
+{
+ ctf_merge_input_t *cmi = arg;
+ ctf_merge_funcmap_t *fmap;
+
+ fmap = ctf_alloc(sizeof (ctf_merge_funcmap_t) +
+ sizeof (ctf_id_t) * fip->ctc_argc);
+ if (fmap == NULL)
+ return (ENOMEM);
+
+ fmap->cmf_idx = idx;
+ fmap->cmf_rtid = fip->ctc_return;
+ fmap->cmf_flags = fip->ctc_flags;
+ fmap->cmf_argc = fip->ctc_argc;
+ fmap->cmf_name = name;
+
+ if (ctf_func_args(cmi->cmi_input, idx, fmap->cmf_argc,
+ fmap->cmf_args) != 0) {
+ ctf_free(fmap, sizeof (ctf_merge_funcmap_t) +
+ sizeof (ctf_id_t) * fip->ctc_argc);
+ return (ctf_errno(cmi->cmi_input));
+ }
+
+ list_insert_tail(&cmi->cmi_fmap, fmap);
+ return (0);
+}
+
+static int
+ctf_merge_add_objs(const char *name, ctf_id_t id, ulong_t idx, void *arg)
+{
+ ctf_merge_input_t *cmi = arg;
+ ctf_merge_objmap_t *cmo;
+
+ cmo = ctf_alloc(sizeof (ctf_merge_objmap_t));
+ if (cmo == NULL)
+ return (ENOMEM);
+
+ cmo->cmo_name = name;
+ cmo->cmo_idx = idx;
+ cmo->cmo_tid = id;
+ list_insert_tail(&cmi->cmi_omap, cmo);
+ return (0);
+}
+
+int
+ctf_merge_add(ctf_merge_t *cmh, ctf_file_t *input)
+{
+ int ret;
+ ctf_merge_input_t *cmi;
+
+ if (cmh->cmh_output == NULL)
+ return (EINVAL);
+
+ if (input->ctf_flags & LCTF_CHILD)
+ return (ECTF_MCHILD);
+
+ cmi = ctf_alloc(sizeof (ctf_merge_input_t));
+ if (cmi == NULL)
+ return (ENOMEM);
+
+ cmi->cmi_input = input;
+ list_create(&cmi->cmi_fmap, sizeof (ctf_merge_funcmap_t),
+ offsetof(ctf_merge_funcmap_t, cmf_node));
+ list_create(&cmi->cmi_omap, sizeof (ctf_merge_funcmap_t),
+ offsetof(ctf_merge_objmap_t, cmo_node));
+
+ if (cmh->cmh_msyms == B_TRUE) {
+ if ((ret = ctf_function_iter(input, ctf_merge_add_funcs,
+ cmi)) != 0) {
+ ctf_merge_fini_input(cmi);
+ return (ret);
+ }
+
+ if ((ret = ctf_object_iter(input, ctf_merge_add_objs,
+ cmi)) != 0) {
+ ctf_merge_fini_input(cmi);
+ return (ret);
+ }
+ }
+
+ list_insert_tail(&cmh->cmh_inputs, cmi);
+ return (0);
+}
+
+int
+ctf_merge_uniquify(ctf_merge_t *cmh, ctf_file_t *u, const char *pname)
+{
+ char *dup;
+
+ if (cmh->cmh_output == NULL)
+ return (EINVAL);
+ if (u->ctf_flags & LCTF_CHILD)
+ return (ECTF_MCHILD);
+ if (pname == NULL)
+ return (EINVAL);
+ dup = ctf_strdup(pname);
+ if (dup == NULL)
+ return (EINVAL);
+ if (cmh->cmh_pname != NULL) {
+ size_t len = strlen(cmh->cmh_pname) + 1;
+ ctf_free(cmh->cmh_pname, len);
+ }
+ cmh->cmh_pname = dup;
+ cmh->cmh_unique = u;
+ return (0);
+}
+
+static int
+ctf_merge_symbols(ctf_merge_t *cmh, ctf_file_t *fp)
+{
+ int err;
+ ulong_t i;
+
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+ for (i = 0; i < fp->ctf_nsyms; i++) {
+ const char *name;
+ ctf_merge_input_t *cmi;
+ ctf_merge_objmap_t *cmo;
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ int type = ELF32_ST_TYPE(symp->st_info);
+ if (type != STT_OBJECT)
+ continue;
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ name = (char *)(strbase + symp->st_name);
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ int type = ELF64_ST_TYPE(symp->st_info);
+ if (type != STT_OBJECT)
+ continue;
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ name = (char *)(strbase + symp->st_name);
+ }
+
+ cmo = NULL;
+ for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
+ cmi = list_next(&cmh->cmh_inputs, cmi)) {
+ for (cmo = list_head(&cmi->cmi_omap); cmo != NULL;
+ cmo = list_next(&cmi->cmi_omap, cmo)) {
+ if (strcmp(cmo->cmo_name, name) == 0)
+ goto found;
+ }
+ }
+found:
+ if (cmo != NULL) {
+ if (cmo->cmo_tid == 0)
+ continue;
+ if ((err = ctf_add_object(fp, i, cmo->cmo_tid)) != 0)
+ return (err);
+ }
+ }
+
+ return (0);
+}
+
+static int
+ctf_merge_functions(ctf_merge_t *cmh, ctf_file_t *fp)
+{
+ int err;
+ ulong_t i;
+ ctf_funcinfo_t fi;
+
+ uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+ uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+ for (i = 0; i < fp->ctf_nsyms; i++) {
+ const char *name;
+ ctf_merge_input_t *cmi;
+ ctf_merge_funcmap_t *cmf;
+
+ if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+ const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+ int type = ELF32_ST_TYPE(symp->st_info);
+ if (ELF32_ST_TYPE(symp->st_info) != STT_FUNC)
+ continue;
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ name = (char *)(strbase + symp->st_name);
+ } else {
+ const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+ int type = ELF64_ST_TYPE(symp->st_info);
+ if (ELF64_ST_TYPE(symp->st_info) != STT_FUNC)
+ continue;
+ if (ctf_sym_valid(strbase, type, symp->st_shndx,
+ symp->st_value, symp->st_name) == B_FALSE)
+ continue;
+ name = (char *)(strbase + symp->st_name);
+ }
+
+ cmf = NULL;
+ for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
+ cmi = list_next(&cmh->cmh_inputs, cmi)) {
+ for (cmf = list_head(&cmi->cmi_fmap); cmf != NULL;
+ cmf = list_next(&cmi->cmi_fmap, cmf)) {
+ if (strcmp(cmf->cmf_name, name) == 0)
+ goto found;
+ }
+ }
+found:
+ if (cmf != NULL) {
+ fi.ctc_return = cmf->cmf_rtid;
+ fi.ctc_argc = cmf->cmf_argc;
+ fi.ctc_flags = cmf->cmf_flags;
+ if ((err = ctf_add_function(fp, i, &fi,
+ cmf->cmf_args)) != 0)
+ return (err);
+ }
+ }
+
+ return (0);
+
+}
+
+int
+ctf_merge_merge(ctf_merge_t *cmh, ctf_file_t **out)
+{
+ int err;
+ ctf_merge_input_t *cmi;
+ boolean_t mset = B_FALSE;
+ ctf_id_t ltype;
+
+ if (cmh->cmh_label != NULL && cmh->cmh_unique != NULL) {
+ const char *label = ctf_label_topmost(cmh->cmh_unique);
+ if (label == NULL)
+ return (ECTF_NOLABEL);
+ if (strcmp(label, cmh->cmh_label) != 0)
+ return (ECTF_LCONFLICT);
+ }
+
+ /*
+ * We should consider doing a divide and conquer and parallel merge
+ * here. If we did, we'd want to use some number of threads to perform
+ * this operation.
+ */
+ for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
+ cmi = list_next(&cmh->cmh_inputs, cmi)) {
+ if (mset == B_FALSE) {
+ if (ctf_setmodel(cmh->cmh_output,
+ ctf_getmodel(cmi->cmi_input)) != 0) {
+ return (ctf_errno(cmh->cmh_output));
+ }
+ mset = B_TRUE;
+ }
+ err = ctf_merge(&cmh->cmh_output, cmi);
+ if (err != 0)
+ return (ctf_errno(cmh->cmh_output));
+ }
+
+ if (cmh->cmh_unique != NULL) {
+ err = ctf_uniquify_types(cmh, out);
+ if (err != 0)
+ return (ctf_errno(cmh->cmh_output));
+ ctf_close(cmh->cmh_output);
+ } else {
+ *out = cmh->cmh_output;
+ }
+
+ ltype = (*out)->ctf_typemax;
+ if (((*out)->ctf_flags & LCTF_CHILD) && ltype != 0)
+ ltype += 0x8000;
+ if (ctf_add_label(*out, cmh->cmh_label, ltype, 0) != 0) {
+ return (ctf_errno(*out));
+ }
+
+ if (cmh->cmh_msyms == B_TRUE) {
+ err = ctf_merge_symbols(cmh, *out);
+ if (err != 0)
+ return (ctf_errno(*out));
+
+ err = ctf_merge_functions(cmh, *out);
+ if (err != 0)
+ return (ctf_errno(*out));
+ }
+
+ err = ctf_update(*out);
+ if (err != 0)
+ return (ctf_errno(*out));
+
+ cmh->cmh_output = NULL;
+
+ return (0);
+}
diff --git a/usr/src/lib/libctf/common/libctf.h b/usr/src/lib/libctf/common/libctf.h
index 07601b4ee4..5e7db9dff9 100644
--- a/usr/src/lib/libctf/common/libctf.h
+++ b/usr/src/lib/libctf/common/libctf.h
@@ -24,7 +24,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2014, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
*/
/*
@@ -35,7 +35,7 @@
* 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.
+ * release of illumos is almost guaranteed to break in the next release.
*
* In short, do not user this header file or libctf for any purpose.
*/
@@ -62,13 +62,32 @@ typedef enum ctf_diff_flag {
typedef struct ctf_diff ctf_diff_t;
typedef void (*ctf_diff_type_f)(ctf_file_t *, ctf_id_t, boolean_t, ctf_file_t *,
ctf_id_t, void *);
+typedef void (*ctf_diff_func_f)(ctf_file_t *, ulong_t, boolean_t, ctf_file_t *,
+ ulong_t, void *);
+typedef void (*ctf_diff_obj_f)(ctf_file_t *, ulong_t, ctf_id_t, boolean_t,
+ ctf_file_t *, ulong_t, ctf_id_t, void *);
extern int ctf_diff_init(ctf_file_t *, ctf_file_t *, ctf_diff_t **);
extern uint_t ctf_diff_getflags(ctf_diff_t *);
extern int ctf_diff_setflags(ctf_diff_t *, uint_t);
extern int ctf_diff_types(ctf_diff_t *, ctf_diff_type_f, void *);
+extern int ctf_diff_functions(ctf_diff_t *, ctf_diff_func_f, void *);
+extern int ctf_diff_objects(ctf_diff_t *, ctf_diff_obj_f, void *);
extern void ctf_diff_fini(ctf_diff_t *);
+typedef struct ctf_merge_handle ctf_merge_t;
+extern ctf_merge_t *ctf_merge_init(int, int *);
+extern int ctf_merge_add(ctf_merge_t *, ctf_file_t *);
+extern int ctf_merge_label(ctf_merge_t *, const char *);
+extern int ctf_merge_uniquify(ctf_merge_t *, ctf_file_t *, const char *);
+extern int ctf_merge_merge(ctf_merge_t *, ctf_file_t **);
+extern void ctf_merge_fini(ctf_merge_t *);
+
+#define CTF_ELFWRITE_F_COMPRESS 0x1
+#define CTF_ELFWRITE_F_KEEP_STABS 0x2
+extern int ctf_elffdwrite(ctf_file_t *, int, int, int);
+extern int ctf_elfwrite(ctf_file_t *, const char *, const char *, int);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/lib/libctf/common/mapfile-vers b/usr/src/lib/libctf/common/mapfile-vers
index d09b88fd01..95ddfd5d6c 100644
--- a/usr/src/lib/libctf/common/mapfile-vers
+++ b/usr/src/lib/libctf/common/mapfile-vers
@@ -23,7 +23,7 @@
#
#
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc. All rights reserved.
#
#
@@ -53,10 +53,13 @@ SYMBOL_VERSION SUNWprivate_1.2 {
ctf_add_enumerator;
ctf_add_float;
ctf_add_forward;
+ ctf_add_funcptr;
ctf_add_function;
ctf_add_integer;
+ ctf_add_label;
ctf_add_member;
ctf_add_pointer;
+ ctf_add_object;
ctf_add_restrict;
ctf_add_struct;
ctf_add_type;
@@ -64,34 +67,41 @@ SYMBOL_VERSION SUNWprivate_1.2 {
ctf_add_union;
ctf_add_volatile;
ctf_create;
+ ctf_dataptr;
ctf_delete_type;
ctf_diff_init;
ctf_diff_fini;
+ ctf_diff_functions;
ctf_diff_getflags;
+ ctf_diff_objects;
ctf_diff_setflags;
ctf_diff_types;
ctf_discard;
ctf_dup;
+ ctf_elffdwrite;
+ ctf_elfwrite;
ctf_enum_value;
+ ctf_flags;
ctf_func_args_by_id;
ctf_func_info_by_id;
- ctf_idhash_clear;
- ctf_idhash_create;
- ctf_idhash_define;
- ctf_idhash_insert;
- ctf_idhash_iter;
- ctf_idhash_iter_init;
- ctf_idhash_iter_fini;
- ctf_idhash_lookup;
- ctf_idhash_size;
- ctf_idhash_destroy;
+ ctf_function_iter;
ctf_label_info;
ctf_label_iter;
ctf_label_topmost;
ctf_member_info;
+ ctf_merge_add;
+ ctf_merge_fini;
+ ctf_merge_init;
+ ctf_merge_label;
+ ctf_merge_merge;
+ ctf_merge_uniquify;
+ ctf_object_iter;
ctf_parent_file;
+ ctf_parent_label;
ctf_parent_name;
ctf_set_array;
+ ctf_string_iter;
+ ctf_symbol_name;
ctf_type_align;
ctf_type_cmp;
ctf_type_compat;
diff --git a/usr/src/lib/libdtrace/common/dt_decl.c b/usr/src/lib/libdtrace/common/dt_decl.c
index bbb561d027..c9bd469ddb 100644
--- a/usr/src/lib/libdtrace/common/dt_decl.c
+++ b/usr/src/lib/libdtrace/common/dt_decl.c
@@ -22,7 +22,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2014 by Delphix. All rights reserved.
- * Copyright (c) 2013 Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015 Joyent, Inc. All rights reserved.
*/
#include <strings.h>
@@ -652,7 +652,7 @@ dt_decl_member(dt_node_t *dnp)
}
if (ctf_add_member(dsp->ds_ctfp, dsp->ds_type,
- ident, dtt.dtt_type) == CTF_ERR) {
+ ident, dtt.dtt_type, ULONG_MAX) == CTF_ERR) {
xyerror(D_UNKNOWN, "failed to define member '%s': %s\n",
idname, ctf_errmsg(ctf_errno(dsp->ds_ctfp)));
}
diff --git a/usr/src/lib/libdtrace/common/dt_open.c b/usr/src/lib/libdtrace/common/dt_open.c
index 7edeac8f10..74a9cccfef 100644
--- a/usr/src/lib/libdtrace/common/dt_open.c
+++ b/usr/src/lib/libdtrace/common/dt_open.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
* Copyright (c) 2012 by Delphix. All rights reserved.
*/
@@ -1145,13 +1145,13 @@ alloc:
* Add intrinsic pointer types that are needed to initialize printf
* format dictionary types (see table in dt_printf.c).
*/
- (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT,
+ (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL,
ctf_lookup_by_name(dmp->dm_ctfp, "void"));
- (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT,
+ (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL,
ctf_lookup_by_name(dmp->dm_ctfp, "char"));
- (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT,
+ (void) ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL,
ctf_lookup_by_name(dmp->dm_ctfp, "int"));
if (ctf_update(dmp->dm_ctfp) != 0) {
@@ -1211,11 +1211,11 @@ alloc:
ctc.ctc_argc = 0;
ctc.ctc_flags = 0;
- dtp->dt_type_func = ctf_add_function(dmp->dm_ctfp,
+ dtp->dt_type_func = ctf_add_funcptr(dmp->dm_ctfp,
CTF_ADD_ROOT, &ctc, NULL);
- dtp->dt_type_fptr = ctf_add_pointer(dmp->dm_ctfp,
- CTF_ADD_ROOT, dtp->dt_type_func);
+ dtp->dt_type_fptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL,
+ dtp->dt_type_func);
/*
* We also insert CTF definitions for the special D intrinsic types
diff --git a/usr/src/lib/libdtrace/common/dt_parser.c b/usr/src/lib/libdtrace/common/dt_parser.c
index 753009f857..4e7c0c8869 100644
--- a/usr/src/lib/libdtrace/common/dt_parser.c
+++ b/usr/src/lib/libdtrace/common/dt_parser.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2013, Joyent Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent Inc. All rights reserved.
* Copyright (c) 2013 by Delphix. All rights reserved.
*/
@@ -288,7 +288,7 @@ dt_type_pointer(dtrace_typeinfo_t *tip)
return (dt_set_errno(dtp, EDT_CTF));
}
- ptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, type);
+ ptr = ctf_add_pointer(dmp->dm_ctfp, CTF_ADD_ROOT, NULL, type);
if (ptr == CTF_ERR || ctf_update(dmp->dm_ctfp) == CTF_ERR) {
dtp->dt_ctferr = ctf_errno(dmp->dm_ctfp);
diff --git a/usr/src/man/man1/Makefile b/usr/src/man/man1/Makefile
index b3a6091f93..5864e86d53 100644
--- a/usr/src/man/man1/Makefile
+++ b/usr/src/man/man1/Makefile
@@ -87,6 +87,8 @@ MANFILES= acctcom.1 \
csh.1 \
csplit.1 \
ctags.1 \
+ ctfdiff.1 \
+ ctfdump.1 \
ctrun.1 \
ctstat.1 \
ctwatch.1 \
diff --git a/usr/src/man/man1/ctfdiff.1 b/usr/src/man/man1/ctfdiff.1
new file mode 100644
index 0000000000..1934c64c52
--- /dev/null
+++ b/usr/src/man/man1/ctfdiff.1
@@ -0,0 +1,337 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright (c) 2015, Joyent, Inc.
+.\"
+.Dd Oct 4, 2014
+.Dt CTFDIFF 1
+.Os
+.Sh NAME
+.Nm ctfdiff
+.Nd compare two CTF containers
+.Sh SYNOPSIS
+.Nm ctfdiff
+.Op Fl afIloqt
+.Op Fl F Ar function
+.Op Fl O Ar object
+.Op Fl p Ar parent1
+.Op Fl p Ar parent2
+.Op Fl T Ar type
+.Ar file1 file2
+.Sh DESCRIPTION
+The
+.Nm
+utility identifies differences between the contents of the
+.Sy CTF
+containers found in
+.Em file1
+and
+.Em file2 .
+.Lp
+.Nm
+can find differences between two
+.Sy CTF
+container's
+.Sy labels ,
+.Sy functions ,
+.Sy objects ,
+and
+.Sy types .
+When no options are specified,
+.Nm
+will only consider
+.Sy functions ,
+.Sy objects,
+and
+.Sy types .
+.Lp
+Two
+.Sy labels
+are considered the same, if they have the same name. Two
+.Sy objects
+are considered the same if they have the same name and the type of the
+object is the same. Two
+.Sy functions
+are considered the same if they have the same, the same return type, the
+same number of arguments, and the types of their arguments are the same.
+.Lp
+Two
+.Sy types
+are considered the same if they have the same, they represent the same
+kind of thing, and the contents of the type are the same. This varies
+for each specific kind, for example, two structs are the same if they
+have the same members whose types, offsets, and names are all the same.
+For more information on the specifics for what we look at for each kind
+of type, and the kinds themselves, see the information we use to encode
+them in
+.Xr ctf 4 . If the option
+.Fl I
+is specified, then the names of basic integer types are ignored. For an
+example of where this makes sense, see
+.Sy Example 4 .
+.Lp
+If the
+.Sy CTF
+container found inside of either
+.Em file1
+or
+.Em file2
+has been uniquified (see
+.Xr ctf 4
+for more on uniquification), then the parent
+.Sy CTF
+container is also required for the diff to complete.
+.Sh OPTIONS
+The following options are supported:
+.Bl -hang -width Ds
+.It Fl a
+.Bd -filled -compact
+Diff
+.Sy labels ,
+.Sy types ,
+.Sy objects ,
+and
+.Sy functions .
+.Ed
+.It Fl f
+.Bd -filled -compact
+Diff
+.Sy function
+type argument information.
+.Ed
+.It Fl F Ar function
+.Bd -filled -compact
+When diffing
+.Sy functions ,
+only consider the function
+.Em function .
+This option requires that the option
+.Fl -f
+be specified and can be repeated multiple times.
+.Ed
+.It Fl I
+.Bd -filled -compact
+Ignore the names of integral types. This option is useful when comparing
+types between two
+.Sy CTF
+containers that have different programming models. In this case, when
+comparing integers, the name of the type is not considered. This means
+that the ILP32 type long which is a 32-bit wide signed integer is the
+same as the LP64 type int which is a 32-bit wide signed integer, even
+though they have different names.
+.Ed
+.It Fl l
+.Bd -filled -compact
+Diff the
+.Sy labels
+contained inside the
+.Sy CTF
+containers.
+.Ed
+.It Fl o
+.Bd -filled -compact
+Diff type information for
+.Sy objects .
+.Ed
+.It Fl O Ar object
+.Bd -filled -compact
+When diffing type information for
+.Sy objects ,
+only compare if the object is name
+.Em object . This option requires
+.Fl o
+to be specified and can be repeated multiple times.
+.Ed
+.It Fl p Ar parent1
+.Bd -filled -compact
+Specifies the path of file that is the parent of the
+.Sy CTF
+container inside of
+.Em file1
+is
+.Em parent1 .
+This option is required if
+.Em file1
+has been uniquified. For more information on uniquification, see
+.Xr ctf 4 .
+.Ed
+.It Fl P Ar parent2
+.Bd -filled -compact
+Specifies the path of file that is the parent of the
+.Sy CTF
+container inside of
+.Em file2 is
+.Em parent2 .
+This option is required if
+.Em file1
+has been uniquified. For more information on uniquification, see
+.Xr ctf 4 .
+.Ed
+.It Fl q
+.Bd -filled -compact
+Enables quiet mode. Standard output from the diff will not be emitted.
+However, diagnostics messages will still be emitted to standard error.
+.Ed
+.It Fl t
+.Bd -filled -compact
+Diff the
+.Sy type
+information sections in the
+.Sy CTF
+containers.
+.Ed
+.It Fl T Ar type
+.Bd -filled -compact
+When diffing the
+.Sy types
+section, only consider it if the type is name
+.Em type .
+Types specified here do not impact the diffing of
+.Sy objects
+or
+.Sy functions .
+Even with
+.Fl -T
+specified, other types will be diffed as necessary for the evaluation of
+the named types; however, the results of those intermediate differences
+will not impact the results of
+.Nm ,
+only named types are considered when evaluating the exit status of
+.Nm .
+.Ed
+.El
+.Sh EXIT STATUS
+.Bl -inset
+.It Sy 0
+.Bd -filled -offset indent -compact
+Execution completed successfully, no differences were detected
+between
+.Em file1
+and
+.Em file2 .
+.Ed
+.It Sy 1
+.Bd -filled -offset indent -compact
+Execution completed successfully, but differences were detected
+between
+.Em file1
+and
+.Em file2 .
+.Ed
+.It Sy 2
+.D1 Invalid command line options were specified.
+.It Sy 3
+.D1 A fatal error occured.
+.El
+.Sh EXAMPLES
+.Sy Example 1
+Diffing Two
+.Sy CTF
+Containers
+.Lp
+The following example compares two
+.Sy CTF
+containers using the default set
+of comparisons:
+.Sy objects ,
+.Sy functions ,
+and
+.Sy types .
+.Bd -literal -offset 6n
+$ ctfdiff /usr/lib/libc.so.1 /usr/lib/libdtrace.so.1
+ctf container /usr/lib/libc.so.1 type 37 is different
+ctf container /usr/lib/libc.so.1 type 38 is different
+ctf container /usr/lib/libc.so.1 type 39 is different
+ctf container /usr/lib/libc.so.1 type 40 is different
+ctf container /usr/lib/libc.so.1 type 41 is different
+ctf container /usr/lib/libc.so.1 type 42 is different
+ctf container /usr/lib/libc.so.1 type 43 is different
+ctf container /usr/lib/libc.so.1 type 47 is different
+ctf container /usr/lib/libc.so.1 type 48 is different
+ctf container /usr/lib/libc.so.1 type 49 is different
+\&...
+.Ed
+.Sy Example 2
+Diffing Types Between Two
+.Sy CTF
+Containers with Parents
+.Lp
+The following example compares two
+.Sy CTF
+containers
+.Sy /ws/rm/zlan/proto/kernel/drv/amd64/vnd
+and
+.Sy /ws/rm/zlan/proto/kernel/drv/amd64/overlay
+that have been uniquified against the same container
+.Sy /ws/rm/zlan/proto/kernel/amd64/genunix .
+.Bd -literal -offset 6n
+$ ctfdiff -t -p /ws/rm/zlan/proto/kernel/amd64/genunix \\
+ -P /ws/rm/zlan/proto/kernel/amd64/genunix \\
+ /ws/rm/zlan/proto/kernel/drv/amd64/vnd \\
+ /ws/rm/zlan/proto/kernel/drv/amd64/overlay
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32769 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32770 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32771 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32772 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32774 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32775 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32776 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32777 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32778 is different
+ctf container /ws/rm/zlan/proto/kernel/drv/amd64/vnd type 32779 is different
+\&...
+.Ed
+.Lp
+.Sy Example 3
+Diffing a Specific Function in Two
+.Sy CTF
+Containers
+.Lp
+This example shows us looking for differences in the function
+.Sy libzfs_core_init
+in two different version of the library
+.Sy libzfs_core.so.1 .
+.Bd -literal -offset 6n
+$ ctfdiff -f -F libzfs_core_init /usr/lib/libzfs_core.so.1 \\
+ /ws/rm/ctf/proto/usr/lib/libzfs_core.so.1
+$ echo $?
+.Ed
+.Lp
+.Sy Example 4
+Diffing Types to Find Differences Between Different Data Models.
+.Lp
+This example looks for differences between structures used in an ioctl
+that the kernel wants to be bitness neutral by comparing a 32-bit and
+64-bit library that consumes it. In this example, we'll use the library
+.Sy libvnd.so.1
+and the types
+.Sy vnd_ioc_attach_t ,
+.Sy vnd_ioc_link_t ,
+.Sy vnd_ioc_unlink_t ,
+.Sy vnd_ioc_buf_t ,
+and
+.Sy vnd_ioc_info_t .
+.Bd -literal -offset 6n
+$ ctfdiff -t -I -T vnd_ioc_attach_t -T vnd_ioc_link_t \\
+ -T vnd_ioc_unlink_t -T vnd_ioc_buf_t -T vnd_ioc_info_t \\
+ i386/libvnd.so.1 amd64/libvnd.so.1
+$ echo $?
+0
+.Ed
+.Sh INTERFACE STABILITY
+The command syntax is
+.Sy Committed .
+The output format is
+.Sy Uncommitted .
+.Sh SEE ALSO
+.Xr ctfdump 1 ,
+.Xr diff 1 ,
+.Xr ctf 4
diff --git a/usr/src/man/man1/ctfdump.1 b/usr/src/man/man1/ctfdump.1
new file mode 100644
index 0000000000..d502352c8a
--- /dev/null
+++ b/usr/src/man/man1/ctfdump.1
@@ -0,0 +1,214 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright (c) 2015, Joyent, Inc.
+.\"
+.Dd Oct 4, 2014
+.Dt CTFDUMP 1
+.Os
+.Sh NAME
+.Nm ctfdump
+.Nd dump parts of ctf data from files
+.Sh SYNOPSIS
+.Nm ctfdump
+.Op Fl dfhlsSt
+.Op Fl p Ar parent
+.Op Fl u Ar outfile
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility dumps and decodes the
+.Sy CTF
+data contained inside of
+.Sy ELF
+objects and raw
+.Sy CTF
+files.
+.Lp
+.Nm
+can dump information about the
+.Sy CTF header ,
+the
+.Sy labels
+encoded in the
+.Sy CTF
+container,
+the types of
+.Sy data objects ,
+the internal
+.Sy string
+table,
+the types of the return function and the arguments for
+.Sy functions ,
+and of course, it displays information about the
+.Sy types
+defined in the
+.Sy CTF
+container.
+.Lp
+.Nm
+can also be used to dump out the raw
+.Sy CTF
+data and send it to another file. When writing out data, it always
+ensures that the
+.Sy CTF
+data is decompressed. In this form, the
+.Sy CTF
+data can be inspected using
+.Nm
+and other tools such as
+.Xr mdb 1 .
+.Lp
+When no options are specified,
+.Nm
+displays all information. However, when the
+.Fl u
+option is used, then no information is displayed by default, unless
+requested through the the appropriate option.
+.Sh OPTIONS
+The following options are supported:
+.Bl -hang -width Ds
+.It Fl d
+.Bd -filled -compact
+Dump the types of symbols that correspond to objects.
+.Ed
+.It Fl f
+.Bd -filled -compact
+Dump the types of the return values and arguments of the functions.
+.Ed
+.It Fl h
+.Bd -filled -compact
+Dump the
+.Sy CTF
+header
+.Ed
+.It Fl l
+.Bd -filled -compact
+Dump all
+.Sy CTF
+labels associated with the file.
+.Ed
+.It Fl p Ar parent
+.Bd -filled -compact
+Use the type information in
+.Em parent
+to supplement output. This is useful when a
+.Nm CTF
+container has been
+.Sy uniquified
+against
+.Em parent .
+This allows
+.Nm
+to use the names of types when used with
+.Fl t .
+.Ed
+.It Fl s
+.Bd -filled -compact
+Dump the internal
+.Sy CTF
+string table
+.Ed
+.It Fl S
+.Bd -filled -compact
+Displays statistics about the
+.Sy CTF
+container.
+.Ed
+.It Fl t
+.Bd -filled -compact
+Dump the type information contained in the
+.Sy CTF
+conatiner.
+.Ed
+.It Fl u Ar outfile
+.Bd -filled -compact
+Copies the uncompressed
+.Sy CTF
+data to the file specified by
+.Em outfile .
+This can be used to make it easier to inspect the raw
+.Sy CTF
+data.
+.Ed
+.El
+.Sh EXIT STATUS
+.Bl -inset
+.It Sy 0
+.Dl Execution completed successfully.
+.It Sy 1
+.Dl A fatal error occured.
+.It Sy 2
+.Dl Invalid command line options were specified.
+.El
+.Sh EXAMPLES
+.Sy Example 1
+Displaying the Type Section of a Single File
+.Lp
+The following example dumps the type section of the file
+.Sy /usr/lib/libc.so.1 .
+.Bd -literal -offset 6n
+$ ctfdump -t /usr/lib/libc.so.1
+- Types ----------------------------------------------------
+
+ <1> int encoding=SIGNED offset=0 bits=32
+ <2> long encoding=SIGNED offset=0 bits=32
+ <3> typedef pid_t refers to 2
+ <4> unsigned int encoding=0x0 offset=0 bits=32
+ <5> typedef uid_t refers to 4
+ <6> typedef gid_t refers to 5
+ <7> typedef uintptr_t refers to 4
+\&...
+.Ed
+.Lp
+.Sy Example 2
+Dumping the CTF data to Another File
+.Lp
+The following example dumps the entire CTF data from the file
+.Sy /usr/lib/libc.so.1
+and places it into the file
+.Sy ctf.out .
+This then shows how you can use the
+.Xr mdb 1
+to inspect its contents.
+.Bd -literal -offset 6n
+$ ctfdump -u ctf.out /usr/lib/libc.so.1
+$ mdb ./ctf.out
+> ::typedef -r /usr/lib/libctf.so.1
+> 0::print ctf_header_t
+{
+ cth_preamble = {
+ ctp_magic = 0xcff1
+ ctp_version = 0x2
+ ctp_flags = 0
+ }
+ cth_parlabel = 0
+ cth_parname = 0
+ cth_lbloff = 0
+ cth_objtoff = 0x8
+ cth_funcoff = 0x5e0
+ cth_typeoff = 0x7178
+ cth_stroff = 0x12964
+ cth_strlen = 0x7c9c
+}
+.Ed
+.Sh INTERFACE STABILITY
+The command syntax is
+.Sy Committed .
+The output format is
+.Sy Uncommitted .
+.Sh SEE ALSO
+.Xr ctfdiff 1 ,
+.Xr dump 1 ,
+.Xr elfdump 1 ,
+.Xr mdb 1 ,
+.Xr ctf 4
diff --git a/usr/src/tools/ctf/Makefile b/usr/src/tools/ctf/Makefile
index 675c16aa84..938b242fe9 100644
--- a/usr/src/tools/ctf/Makefile
+++ b/usr/src/tools/ctf/Makefile
@@ -26,7 +26,7 @@
include ../Makefile.tools
-SUBDIRS = cvt dump stabs scripts ctfstrip libctf .WAIT ctfdiff
+SUBDIRS = cvt stabs scripts ctfstrip libctf ctfdiff ctfmerge ctfdump
.PARALLEL: $(SUBDIRS)
@@ -38,6 +38,10 @@ lint := TARGET= lint
.KEEP_STATE:
+ctfmerge: libctf
+ctfdiff: libctf
+ctfdump: libctf
+
all clean clobber install lint: dwarf .WAIT $(SUBDIRS)
dwarf $(SUBDIRS): FRC
diff --git a/usr/src/tools/ctf/ctfdiff/i386/Makefile b/usr/src/tools/ctf/ctfdiff/i386/Makefile
index cd5462bee3..92c756cb42 100644
--- a/usr/src/tools/ctf/ctfdiff/i386/Makefile
+++ b/usr/src/tools/ctf/ctfdiff/i386/Makefile
@@ -1,27 +1,16 @@
#
-# CDDL HEADER START
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
#
-# 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
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
#
+
#
-# Copyright (c) 2001 by Sun Microsystems, Inc.
-# All rights reserved.
+# Copyright (c) 2015, Joyent, Inc.
#
include ../Makefile.com
diff --git a/usr/src/tools/ctf/ctfdiff/sparc/Makefile b/usr/src/tools/ctf/ctfdiff/sparc/Makefile
index cd5462bee3..92c756cb42 100644
--- a/usr/src/tools/ctf/ctfdiff/sparc/Makefile
+++ b/usr/src/tools/ctf/ctfdiff/sparc/Makefile
@@ -1,27 +1,16 @@
#
-# CDDL HEADER START
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
#
-# 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
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
#
+
#
-# Copyright (c) 2001 by Sun Microsystems, Inc.
-# All rights reserved.
+# Copyright (c) 2015, Joyent, Inc.
#
include ../Makefile.com
diff --git a/usr/src/cmd/mdb/intel/amd64/libctf/Makefile b/usr/src/tools/ctf/ctfdump/Makefile
index a65e7bd2e8..20d304216a 100644
--- a/usr/src/cmd/mdb/intel/amd64/libctf/Makefile
+++ b/usr/src/tools/ctf/ctfdump/Makefile
@@ -10,17 +10,24 @@
#
#
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc.
#
-MODULE = libctf.so
-MDBTGT = proc
-MODSRCS = mdb_modctf.c
+include ../../Makefile.tools
-include ../../../../Makefile.cmd
-include ../../../../Makefile.cmd.64
-include ../../Makefile.amd64
-include ../../../Makefile.module
+SUBDIRS = $(MACH)
-MODSRCS_DIR = ../../../common/modules/ctf
-CPPFLAGS += -I$(SRC)/common/ctf
+all := TARGET= all
+install := TARGET= install
+clean := TARGET= clean
+clobber := TARGET= clobber
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+install all clean clobber lint: $(SUBDIRS)
+
+$(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/tools/ctf/ctfdump/Makefile.com b/usr/src/tools/ctf/ctfdump/Makefile.com
new file mode 100644
index 0000000000..3e1414ea59
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdump/Makefile.com
@@ -0,0 +1,44 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+PROG = ctfdump
+SRCS = ctfdump.c
+
+include ../../Makefile.ctf
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf
+
+LDFLAGS = \
+ -L$(ROOTONBLDLIBMACH) \
+ '-R$$ORIGIN/../../lib/$(MACH)' \
+
+CPPFLAGS += -include ../../common/ctf_headers.h
+
+OBJS = $(SRCS:%.c=%.o)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: $(SRC)/cmd/ctfdump/%.c
+ $(COMPILE.c) $<
+
+$(ROOTONBLDMACHPROG): $(PROG)
+
+install: $(ROOTONBLDMACHPROG)
+
+clean:
+ $(RM) $(OBJS) $(LINTFILES)
+
+include $(SRC)/tools/Makefile.targ
diff --git a/usr/src/cmd/mdb/sparc/v9/libctf/Makefile b/usr/src/tools/ctf/ctfdump/i386/Makefile
index a55126e198..92c756cb42 100644
--- a/usr/src/cmd/mdb/sparc/v9/libctf/Makefile
+++ b/usr/src/tools/ctf/ctfdump/i386/Makefile
@@ -10,17 +10,7 @@
#
#
-# Copyright (c) 2013, Joyent, Inc. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc.
#
-MODULE = libctf.so
-MDBTGT = proc
-MODSRCS = mdb_modctf.c
-
-include ../../../../Makefile.cmd
-include ../../../../Makefile.cmd.64
-include ../../Makefile.sparcv9
-include ../../../Makefile.module
-
-MODSRCS_DIR = ../../../common/modules/ctf
-CPPFLAGS += -I$(SRC)/common/ctf
+include ../Makefile.com
diff --git a/usr/src/tools/ctf/ctfdump/sparc/Makefile b/usr/src/tools/ctf/ctfdump/sparc/Makefile
new file mode 100644
index 0000000000..92c756cb42
--- /dev/null
+++ b/usr/src/tools/ctf/ctfdump/sparc/Makefile
@@ -0,0 +1,16 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2015, Joyent, Inc.
+#
+
+include ../Makefile.com
diff --git a/usr/src/tools/ctf/dump/Makefile b/usr/src/tools/ctf/ctfmerge/Makefile
index 16d8280cdd..07fadc5f8f 100644
--- a/usr/src/tools/ctf/dump/Makefile
+++ b/usr/src/tools/ctf/ctfmerge/Makefile
@@ -23,7 +23,6 @@
# Copyright (c) 2001 by Sun Microsystems, Inc.
# All rights reserved.
#
-#ident "%Z%%M% %I% %E% SMI"
include ../../Makefile.tools
diff --git a/usr/src/tools/ctf/ctfmerge/Makefile.com b/usr/src/tools/ctf/ctfmerge/Makefile.com
new file mode 100644
index 0000000000..b227e8b7b0
--- /dev/null
+++ b/usr/src/tools/ctf/ctfmerge/Makefile.com
@@ -0,0 +1,46 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+PROG = ctfmerge-altexec
+SRCS = ctfmerge.c
+
+include ../../Makefile.ctf
+
+CFLAGS += $(CCVERBOSE)
+LDLIBS += -lctf -lelf
+
+LDFLAGS = \
+ -L$(ROOTONBLDLIBMACH) \
+ '-R$$ORIGIN/../../lib/$(MACH)' \
+
+CPPFLAGS += -include ../../common/ctf_headers.h
+CERRWARN += -_gcc=-Wno-unused-variable
+CERRWARN += -_gcc=-Wno-uninitialized
+
+OBJS = $(SRCS:%.c=%.o)
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+%.o: $(SRC)/cmd/ctfmerge/%.c
+ $(COMPILE.c) $<
+
+$(ROOTONBLDMACHPROG): $(PROG)
+
+install: $(ROOTONBLDMACHPROG)
+
+clean:
+ $(RM) $(OBJS) $(LINTFILES)
+
+include $(SRC)/tools/Makefile.targ
diff --git a/usr/src/tools/ctf/dump/i386/Makefile b/usr/src/tools/ctf/ctfmerge/i386/Makefile
index 94e5883ae2..cd5462bee3 100644
--- a/usr/src/tools/ctf/dump/i386/Makefile
+++ b/usr/src/tools/ctf/ctfmerge/i386/Makefile
@@ -23,6 +23,5 @@
# Copyright (c) 2001 by Sun Microsystems, Inc.
# All rights reserved.
#
-#ident "%Z%%M% %I% %E% SMI"
include ../Makefile.com
diff --git a/usr/src/tools/ctf/dump/sparc/Makefile b/usr/src/tools/ctf/ctfmerge/sparc/Makefile
index 94e5883ae2..cd5462bee3 100644
--- a/usr/src/tools/ctf/dump/sparc/Makefile
+++ b/usr/src/tools/ctf/ctfmerge/sparc/Makefile
@@ -23,6 +23,5 @@
# Copyright (c) 2001 by Sun Microsystems, Inc.
# All rights reserved.
#
-#ident "%Z%%M% %I% %E% SMI"
include ../Makefile.com
diff --git a/usr/src/tools/ctf/cvt/Makefile.com b/usr/src/tools/ctf/cvt/Makefile.com
index 5385b3769f..d909031167 100644
--- a/usr/src/tools/ctf/cvt/Makefile.com
+++ b/usr/src/tools/ctf/cvt/Makefile.com
@@ -32,6 +32,7 @@ PROG=ctfconvert ctfmerge
GENSRCS= \
alist.c \
+ altexec.c \
barrier.c \
ctf.c \
fifo.c \
diff --git a/usr/src/tools/ctf/cvt/altexec.c b/usr/src/tools/ctf/cvt/altexec.c
new file mode 100644
index 0000000000..c986c0731a
--- /dev/null
+++ b/usr/src/tools/ctf/cvt/altexec.c
@@ -0,0 +1,45 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright (c) 2015, Joyent, Inc.
+ */
+
+/*
+ * Alternate execution engine for CTF tools
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ctftools.h"
+
+void
+ctf_altexec(const char *env, int argc, char **argv)
+{
+ const char *alt;
+ char *altexec;
+
+ alt = getenv(env);
+ if (alt == NULL || *alt == '\0')
+ return;
+
+ altexec = strdup(alt);
+ if (altexec == NULL)
+ terminate("failed to allocate memory for altexec\n");
+
+ if (unsetenv(env) != 0)
+ aborterr("failed to remove %s from environment", env);
+
+ (void) execv(altexec, argv);
+ terminate("failed to altexec %s", altexec);
+}
diff --git a/usr/src/tools/ctf/cvt/ctfconvert.c b/usr/src/tools/ctf/cvt/ctfconvert.c
index 756549e545..5cb39d0929 100644
--- a/usr/src/tools/ctf/cvt/ctfconvert.c
+++ b/usr/src/tools/ctf/cvt/ctfconvert.c
@@ -23,8 +23,6 @@
* 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.
@@ -148,6 +146,7 @@ main(int argc, char **argv)
{
tdata_t *filetd, *mstrtd;
char *label = NULL;
+ char *altexec;
int verbose = 0;
int ignore_non_c = 0;
int keep_stabs = 0;
@@ -159,11 +158,20 @@ main(int argc, char **argv)
progname = basename(argv[0]);
+ ctf_altexec("CTFCONVERT_ALTEXEC", argc, argv);
+
if (getenv("CTFCONVERT_DEBUG_LEVEL"))
debug_level = atoi(getenv("CTFCONVERT_DEBUG_LEVEL"));
if (getenv("CTFCONVERT_DEBUG_PARSE"))
debug_parse = atoi(getenv("CTFCONVERT_DEBUG_PARSE"));
+ if ((altexec = getenv("CTFCONVERT_ALTEXEC")) != NULL) {
+ (void) unsetenv("CTFCONVERT_ALTEXEC");
+ (void) execv(altexec, argv);
+ (void) fprintf(stderr, "ctfconvert altexec failed to "
+ "run %s: %s\n", altexec, strerror(errno));
+ }
+
while ((c = getopt(argc, argv, ":l:L:o:givs")) != EOF) {
switch (c) {
case 'l':
diff --git a/usr/src/tools/ctf/cvt/ctfmerge.c b/usr/src/tools/ctf/cvt/ctfmerge.c
index 2d00a566be..c1de781df8 100644
--- a/usr/src/tools/ctf/cvt/ctfmerge.c
+++ b/usr/src/tools/ctf/cvt/ctfmerge.c
@@ -23,8 +23,6 @@
* 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.
@@ -748,6 +746,8 @@ main(int argc, char **argv)
progname = basename(argv[0]);
+ ctf_altexec("CTFMERGE_ALTEXEC", argc, argv);
+
if (getenv("CTFMERGE_DEBUG_LEVEL"))
debug_level = atoi(getenv("CTFMERGE_DEBUG_LEVEL"));
diff --git a/usr/src/tools/ctf/cvt/ctftools.h b/usr/src/tools/ctf/cvt/ctftools.h
index fd9d454e06..895d2f8491 100644
--- a/usr/src/tools/ctf/cvt/ctftools.h
+++ b/usr/src/tools/ctf/cvt/ctftools.h
@@ -453,6 +453,9 @@ void warning(char *, ...);
void vadebug(int, char *, va_list);
void debug(int, char *, ...);
+/* altexec.c */
+void ctf_altexec(const char *, int argc, char **);
+
#ifdef __cplusplus
}
#endif
diff --git a/usr/src/tools/ctf/dump/Makefile.com b/usr/src/tools/ctf/dump/Makefile.com
deleted file mode 100644
index 9877fa06a3..0000000000
--- a/usr/src/tools/ctf/dump/Makefile.com
+++ /dev/null
@@ -1,67 +0,0 @@
-#
-# 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.
-#
-
-.KEEP_STATE:
-.SUFFIXES:
-
-PROG = ctfdump
-SRCS = dump.c utils.c symbol.c
-
-include ../../Makefile.ctf
-
-LDFLAGS += -L$(NATIVE_ADJUNCT)/lib
-LDLIBS += -lelf -lz
-
-OBJS = $(SRCS:%.c=%.o)
-LINTFILES = $(SRCS:%.c=%.ln)
-
-CERRWARN += -_gcc=-Wno-uninitialized
-
-.NO_PARALLEL:
-.PARALLEL: $(OBJS) $(LINTFILES)
-
-all: $(PROG)
-
-$(PROG): $(OBJS)
- $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
- $(POST_PROCESS)
-
-%.o: ../%.c
- $(COMPILE.c) $<
-
-$(ROOTONBLDMACHPROG): $(PROG)
-
-install: $(ROOTONBLDMACHPROG)
-
-clean:
- $(RM) $(OBJS) $(LINTFILES)
-
-%.ln: ../%.c
- $(LINT.c) -c $<
-
-lint: $(LINTFILES)
- $(LINT) $(LINTFLAGS) $(LINTFILES) $(LDLIBS)
-
-include ../../Makefile.ctf.targ
diff --git a/usr/src/tools/ctf/dump/dump.c b/usr/src/tools/ctf/dump/dump.c
deleted file mode 100644
index 5579bae596..0000000000
--- a/usr/src/tools/ctf/dump/dump.c
+++ /dev/null
@@ -1,1028 +0,0 @@
-/*
- * 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 <sys/types.h>
-#include <sys/sysmacros.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-#include <strings.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <fcntl.h>
-#include <gelf.h>
-#include <zlib.h>
-
-#include "ctf_headers.h"
-#include "utils.h"
-#include "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)
-{
- /* LINTED - pointer alignment */
- const ctf_lblent_t *ctl = (ctf_lblent_t *)(cd->cd_ctfdata +
- hp->cth_lbloff);
- 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)
-{
- /* LINTED - pointer alignment */
- const ushort_t *idp = (ushort_t *)(cd->cd_ctfdata + hp->cth_objtoff);
- 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 < 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)
-{
- /* LINTED - pointer alignment */
- const ushort_t *fp = (ushort_t *)(cd->cd_ctfdata + hp->cth_funcoff);
-
- /* LINTED - pointer alignment */
- const ushort_t *end = (ushort_t *)(cd->cd_ctfdata + hp->cth_typeoff);
-
- 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)
-{
- /* LINTED - pointer alignment */
- const ctf_type_t *tp = (ctf_type_t *)(cd->cd_ctfdata + hp->cth_typeoff);
-
- /* LINTED - pointer alignment */
- const ctf_type_t *end = (ctf_type_t *)(cd->cd_ctfdata + hp->cth_stroff);
-
- 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;
- const 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 = (caddr_t)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 (%d 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,
- 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, 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;
- 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;
- 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);
-
- /* LINTED - pointer alignment */
- pp = (const ctf_preamble_t *)cd.cd_ctfdata;
-
- if (pp->ctp_magic != CTF_MAGIC)
- die("%s does not appear to contain CTF data\n", filename);
-
- if (pp->ctp_version == CTF_VERSION) {
- /* LINTED - pointer alignment */
- hp = (ctf_header_t *)cd.cd_ctfdata;
- 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) != 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/usr/src/tools/ctf/libctf/Makefile.com b/usr/src/tools/ctf/libctf/Makefile.com
index 22e8e82318..d7c0b68b66 100644
--- a/usr/src/tools/ctf/libctf/Makefile.com
+++ b/usr/src/tools/ctf/libctf/Makefile.com
@@ -10,14 +10,14 @@
#
#
-# Copyright (c) 2014, Joyent, Inc. All rights reserved.
+# Copyright (c) 2015, Joyent, Inc.
#
include $(SRC)/lib/libctf/Makefile.shared.com
include ../../Makefile.ctf
CPPFLAGS += -include ../../common/ctf_headers.h -DCTF_OLD_VERSIONS
-LDLIBS += -lc
+LDLIBS += -lc -lelf
.KEEP_STATE:
diff --git a/usr/src/tools/ctf/libctf/i386/Makefile b/usr/src/tools/ctf/libctf/i386/Makefile
index fff958ac6e..bb2f077f86 100644
--- a/usr/src/tools/ctf/libctf/i386/Makefile
+++ b/usr/src/tools/ctf/libctf/i386/Makefile
@@ -1,6 +1,16 @@
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2015, Joyent, Inc.
#
include ../Makefile.com
diff --git a/usr/src/tools/ctf/libctf/sparc/Makefile b/usr/src/tools/ctf/libctf/sparc/Makefile
index fff958ac6e..bb2f077f86 100644
--- a/usr/src/tools/ctf/libctf/sparc/Makefile
+++ b/usr/src/tools/ctf/libctf/sparc/Makefile
@@ -1,6 +1,16 @@
#
-# Copyright 2004 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright (c) 2015, Joyent, Inc.
#
include ../Makefile.com
diff --git a/usr/src/uts/common/ctf/ctf_mod.c b/usr/src/uts/common/ctf/ctf_mod.c
index b34cf400cd..421b922c96 100644
--- a/usr/src/uts/common/ctf/ctf_mod.c
+++ b/usr/src/uts/common/ctf/ctf_mod.c
@@ -24,8 +24,6 @@
* Use is subject to license terms.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#include <sys/sysmacros.h>
#include <sys/modctl.h>
#include <sys/debug.h>
@@ -117,6 +115,15 @@ ctf_version(int version)
/*ARGSUSED*/
ctf_file_t *
+ctf_fdcreate_int(int fd, int *errp, ctf_sect_t *ctfp)
+{
+ if (errp != NULL)
+ *errp = ENOTSUP;
+ return (NULL);
+}
+
+/*ARGSUSED*/
+ctf_file_t *
ctf_modopen(struct module *mp, int *error)
{
ctf_sect_t ctfsect, symsect, strsect;
diff --git a/usr/src/uts/common/sys/ctf_api.h b/usr/src/uts/common/sys/ctf_api.h
index ab648d5489..73beb2d244 100644
--- a/usr/src/uts/common/sys/ctf_api.h
+++ b/usr/src/uts/common/sys/ctf_api.h
@@ -24,7 +24,7 @@
* Use is subject to license terms.
*/
/*
- * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2015, Joyent, Inc.
*/
/*
@@ -60,6 +60,62 @@ extern "C" {
typedef struct ctf_file ctf_file_t;
typedef long ctf_id_t;
+#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 */
+ ECTF_REFERENCED, /* type has outstanding references */
+ ECTF_NOTDYN, /* type is not a dynamic type */
+ ECTF_ELF, /* elf library failure */
+ ECTF_MCHILD, /* cannot merge child container */
+ ECTF_LABELEXISTS, /* label already exists */
+ ECTF_LCONFLICT, /* merged labels conflict */
+ ECTF_ZLIB /* zlib library failure */
+};
+
/*
* 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
@@ -143,19 +199,24 @@ typedef struct ctf_lblinfo {
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_type_f(ctf_id_t, boolean_t, void *);
typedef int ctf_label_f(const char *, const ctf_lblinfo_t *, void *);
+typedef int ctf_function_f(const char *, ulong_t, ctf_funcinfo_t *, void *);
+typedef int ctf_object_f(const char *, ctf_id_t, ulong_t, void *);
+typedef int ctf_string_f(const char *, 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 ctf_file_t *ctf_fdcreate(int, int *);
extern ctf_file_t *ctf_dup(ctf_file_t *);
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 const char *ctf_parent_label(ctf_file_t *);
extern int ctf_import(ctf_file_t *, ctf_file_t *);
extern int ctf_setmodel(ctf_file_t *, int);
@@ -165,6 +226,7 @@ extern void ctf_setspecific(ctf_file_t *, void *);
extern void *ctf_getspecific(ctf_file_t *);
extern int ctf_errno(ctf_file_t *);
+extern uint_t ctf_flags(ctf_file_t *);
extern const char *ctf_errmsg(int);
extern int ctf_version(int);
@@ -176,6 +238,8 @@ extern int ctf_func_args_by_id(ctf_file_t *, ctf_id_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 char *ctf_symbol_name(ctf_file_t *, ulong_t, char *, size_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);
@@ -203,29 +267,39 @@ 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_type_iter(ctf_file_t *, boolean_t, ctf_type_f *, void *);
extern int ctf_label_iter(ctf_file_t *, ctf_label_f *, void *);
+extern int ctf_function_iter(ctf_file_t *, ctf_function_f *, void *);
+extern int ctf_object_iter(ctf_file_t *, ctf_object_f *, void *);
+extern int ctf_string_iter(ctf_file_t *, ctf_string_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_const(ctf_file_t *, uint_t, const char *, 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_funcptr(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_pointer(ctf_file_t *, uint_t, const char *, 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_restrict(ctf_file_t *, uint_t, const char *, 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 ctf_id_t ctf_add_volatile(ctf_file_t *, uint_t, const char *, 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_add_member(ctf_file_t *, ctf_id_t, const char *, ctf_id_t,
+ ulong_t);
+
+
+extern int ctf_add_function(ctf_file_t *, ulong_t, const ctf_funcinfo_t *,
+ const ctf_id_t *);
+extern int ctf_add_object(ctf_file_t *, ulong_t, ctf_id_t);
+extern int ctf_add_label(ctf_file_t *, const char *, ctf_id_t, uint_t);
extern int ctf_set_array(ctf_file_t *, ctf_id_t, const ctf_arinfo_t *);
@@ -234,6 +308,7 @@ extern int ctf_delete_type(ctf_file_t *, ctf_id_t);
extern int ctf_update(ctf_file_t *);
extern int ctf_discard(ctf_file_t *);
extern int ctf_write(ctf_file_t *, int);
+extern void ctf_dataptr(ctf_file_t *, const void **, size_t *);
#ifdef _KERNEL
@@ -242,34 +317,6 @@ extern ctf_file_t *ctf_modopen(struct module *, int *);
#endif
-typedef struct ctf_ihelem {
- ushort_t ih_type; /* type ID number */
- ushort_t ih_value; /* type ID number in a different fp */
- ushort_t ih_next; /* index of next element in hash chain */
-} ctf_ihelem_t;
-
-typedef struct ctf_idhash {
- ushort_t *ih_buckets; /* hash bucket array (chain indices) */
- ctf_ihelem_t *ih_chains; /* hash chains buffer */
- ushort_t ih_nbuckets; /* number of elements in bucket array */
- ushort_t ih_nelems; /* number of elements in hash table */
- uint_t ih_free; /* index of next free hash element */
-} ctf_idhash_t;
-
-extern int ctf_idhash_create(ctf_idhash_t *, ulong_t);
-extern void ctf_idhash_clear(ctf_idhash_t *);
-extern int ctf_idhash_define(ctf_idhash_t *, ushort_t, ushort_t);
-extern int ctf_idhash_insert(ctf_idhash_t *, ushort_t, ushort_t);
-extern ctf_ihelem_t *ctf_idhash_lookup(ctf_idhash_t *, ushort_t);
-extern uint_t ctf_idhash_size(const ctf_idhash_t *);
-extern void ctf_idhash_destroy(ctf_idhash_t *);
-
-typedef struct ctf_idhash_iter ctf_idhash_iter_t;
-extern int ctf_idhash_iter_init(ctf_idhash_t *, ctf_idhash_iter_t **);
-extern const ctf_ihelem_t *ctf_idhash_iter(ctf_idhash_t *,
- ctf_idhash_iter_t *);
-extern void ctf_idhash_iter_fini(ctf_idhash_t *, ctf_idhash_iter_t *);
-
#ifdef __cplusplus
}
#endif