diff options
| author | Robert Mustacchi <rm@joyent.com> | 2014-07-30 19:32:50 +0000 |
|---|---|---|
| committer | Robert Mustacchi <rm@joyent.com> | 2015-02-15 17:26:09 +0000 |
| commit | b9050054bed684df58fc1c855197eec06a2036bf (patch) | |
| tree | cc8ec42bb149c4c00b068fc8cab890ec5cdfc61a /usr/src | |
| parent | f31c6fa33bcc9a608ce6f9ffd671ffc2b65a30ef (diff) | |
| download | illumos-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')
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 |
