diff options
Diffstat (limited to 'usr/src/lib/libctf')
-rw-r--r-- | usr/src/lib/libctf/Makefile.com | 41 | ||||
-rw-r--r-- | usr/src/lib/libctf/Makefile.shared.com | 79 | ||||
-rw-r--r-- | usr/src/lib/libctf/Makefile.shared.targ | 26 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_diff.c | 1277 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_elfwrite.c | 412 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_lib.c | 114 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/ctf_merge.c | 1231 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/libctf.h | 40 | ||||
-rw-r--r-- | usr/src/lib/libctf/common/mapfile-vers | 29 |
9 files changed, 3186 insertions, 63 deletions
diff --git a/usr/src/lib/libctf/Makefile.com b/usr/src/lib/libctf/Makefile.com index 4d1e01d4eb..0169c2a367 100644 --- a/usr/src/lib/libctf/Makefile.com +++ b/usr/src/lib/libctf/Makefile.com @@ -23,43 +23,9 @@ # Use is subject to license terms. # -LIBRARY = libctf.a -VERS = .1 - -COMMON_OBJS = \ - ctf_create.o \ - ctf_decl.o \ - ctf_error.o \ - ctf_hash.o \ - ctf_labels.o \ - ctf_lookup.o \ - ctf_open.o \ - ctf_types.o \ - ctf_util.o - -LIB_OBJS = \ - ctf_lib.o \ - ctf_subr.o - -OBJECTS = $(COMMON_OBJS) $(LIB_OBJS) - -include ../../Makefile.lib +include ../Makefile.shared.com include ../../Makefile.rootfs -SRCS = $(COMMON_OBJS:%.o=../../../common/ctf/%.c) $(LIB_OBJS:%.o=../common/%.c) -LIBS = $(DYNLIB) $(LINTLIB) - -SRCDIR = ../common - -CPPFLAGS += -I../common -I../../../common/ctf -DCTF_OLD_VERSIONS -CFLAGS += $(CCVERBOSE) - -CERRWARN += -_gcc=-Wno-uninitialized - -LDLIBS += -lc - -$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) - .KEEP_STATE: all: $(LIBS) @@ -67,7 +33,4 @@ all: $(LIBS) lint: lintcheck include ../../Makefile.targ - -objs/%.o pics/%.o: ../../../common/ctf/%.c - $(COMPILE.c) -o $@ $< - $(POST_PROCESS_O) +include ../Makefile.shared.targ diff --git a/usr/src/lib/libctf/Makefile.shared.com b/usr/src/lib/libctf/Makefile.shared.com new file mode 100644 index 0000000000..a0370fc944 --- /dev/null +++ b/usr/src/lib/libctf/Makefile.shared.com @@ -0,0 +1,79 @@ +# +# 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. All rights reserved. +# + +# +# This Makefile is shared between the libctf native build in tools and +# the libctf build here for the system. +# +LIBRARY = libctf.a +VERS = .1 + +COMMON_OBJS = \ + ctf_create.o \ + ctf_decl.o \ + ctf_error.o \ + ctf_hash.o \ + ctf_labels.o \ + ctf_lookup.o \ + ctf_open.o \ + 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) $(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) \ + $(LIST_OBJS:%.o=$(SRC)/common/list/%.c) \ + +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc -lelf + +C99MODE= -xc99=%all +C99LMODE= -Xc99=%all + +SRCDIR = $(SRC)/lib/libctf/common + +CPPFLAGS += -I$(SRC)/lib/libctf/common -I$(SRC)/common/ctf -DCTF_OLD_VERSIONS +CFLAGS += $(CCVERBOSE) + +CERRWARN += -_gcc=-Wno-uninitialized + +$(LINTLIB) := SRCS = $(SRCDIR)/$(LINTSRC) diff --git a/usr/src/lib/libctf/Makefile.shared.targ b/usr/src/lib/libctf/Makefile.shared.targ new file mode 100644 index 0000000000..282be86075 --- /dev/null +++ b/usr/src/lib/libctf/Makefile.shared.targ @@ -0,0 +1,26 @@ +# +# 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. All rights reserved. +# + +# +# This Makefile is shared between both the tools and the normal library build. +# + +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 new file mode 100644 index 0000000000..e819fe02cb --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_diff.c @@ -0,0 +1,1277 @@ +/* + * 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. 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_id_t *cds_forward; + ctf_id_t *cds_reverse; + ctf_diff_type_f cds_func; + 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 + */ +static int ctf_diff_type(ctf_diff_t *, ctf_file_t *, ctf_id_t, ctf_file_t *, + ctf_id_t); + +static int +ctf_diff_name(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid) +{ + const char *iname, *oname; + const ctf_type_t *itp, *otp; + + if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL) + return (CTF_ERR); + + if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL) + return (ctf_set_errno(ifp, iid)); + + iname = ctf_strptr(ifp, itp->ctt_name); + oname = ctf_strptr(ofp, otp->ctt_name); + + if ((iname == NULL || oname == NULL) && (iname != oname)) + return (B_TRUE); + + /* Two anonymous names are the same */ + if (iname == NULL && oname == NULL) + return (B_FALSE); + + return (strcmp(iname, oname) == 0 ? B_FALSE: B_TRUE); +} + +/* + * For floats and ints + */ +static int +ctf_diff_number(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid) +{ + ctf_encoding_t ien, den; + + if (ctf_type_encoding(ifp, iid, &ien) != 0) + return (CTF_ERR); + + if (ctf_type_encoding(ofp, oid, &den) != 0) + return (ctf_set_errno(ifp, iid)); + + if (bcmp(&ien, &den, sizeof (ctf_encoding_t)) != 0) + return (B_TRUE); + + return (B_FALSE); +} + +/* + * Two qualifiers are equivalent iff they point to two equivalent types. + */ +static int +ctf_diff_qualifier(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, + ctf_file_t *ofp, ctf_id_t oid) +{ + ctf_id_t iref, oref; + + iref = ctf_type_reference(ifp, iid); + if (iref == CTF_ERR) + return (CTF_ERR); + + oref = ctf_type_reference(ofp, oid); + if (oref == CTF_ERR) + return (ctf_set_errno(ifp, ctf_errno(ofp))); + + return (ctf_diff_type(cds, ifp, iref, ofp, oref)); +} + +/* + * Two arrays are the same iff they have the same type for contents, the same + * type for the index, and the same number of elements. + */ +static int +ctf_diff_array(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, + ctf_id_t oid) +{ + int ret; + ctf_arinfo_t iar, oar; + + if (ctf_array_info(ifp, iid, &iar) == CTF_ERR) + return (CTF_ERR); + + if (ctf_array_info(ofp, oid, &oar) == CTF_ERR) + return (ctf_set_errno(ifp, ctf_errno(ofp))); + + ret = ctf_diff_type(cds, ifp, iar.ctr_contents, ofp, oar.ctr_contents); + if (ret != B_FALSE) + return (ret); + + if (iar.ctr_nelems != oar.ctr_nelems) + return (B_TRUE); + + /* + * 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_FALSE); +} + +/* + * Two function pointers are the same if the following is all true: + * + * o They have the same return type + * o They have the same number of arguments + * o The arguments are of the same type + * o They have the same flags + */ +static int +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; + ctf_funcinfo_t ifunc, ofunc; + ctf_id_t *iids, *oids; + + if (ctf_func_info_by_id(ifp, iid, &ifunc) == CTF_ERR) + return (CTF_ERR); + + if (ctf_func_info_by_id(ofp, oid, &ofunc) == CTF_ERR) + return (ctf_set_errno(ifp, ctf_errno(ofp))); + + if (ifunc.ctc_argc != ofunc.ctc_argc) + return (B_TRUE); + + if (ifunc.ctc_flags != ofunc.ctc_flags) + return (B_TRUE); + + ret = ctf_diff_type(cds, ifp, ifunc.ctc_return, ofp, ofunc.ctc_return); + if (ret != B_FALSE) + return (ret); + + iids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc); + if (iids == NULL) + return (ctf_set_errno(ifp, ENOMEM)); + + oids = ctf_alloc(sizeof (ctf_id_t) * ifunc.ctc_argc); + if (oids == NULL) { + ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc); + return (ctf_set_errno(ifp, ENOMEM)); + } + + if (ctf_func_args_by_id(ifp, iid, ifunc.ctc_argc, iids) == CTF_ERR) { + ret = CTF_ERR; + goto out; + } + + if (ctf_func_args_by_id(ofp, oid, ofunc.ctc_argc, oids) == CTF_ERR) { + ret = ctf_set_errno(ifp, ctf_errno(ofp)); + goto out; + } + + ret = B_TRUE; + for (i = 0; i < ifunc.ctc_argc; i++) { + ret = ctf_diff_type(cds, ifp, iids[i], ofp, oids[i]); + if (ret != B_FALSE) + goto out; + } + ret = B_FALSE; + +out: + ctf_free(iids, sizeof (ctf_id_t) * ifunc.ctc_argc); + ctf_free(oids, sizeof (ctf_id_t) * ofunc.ctc_argc); + return (ret); +} + +/* + * Two structures are the same if every member is identical to its corresponding + * type, at the same offset, and has the same name. + */ +static int +ctf_diff_struct(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, + ctf_id_t oid) +{ + ctf_file_t *oifp; + const ctf_type_t *itp, *otp; + ssize_t isize, iincr, osize, oincr; + const ctf_member_t *imp, *omp; + const ctf_lmember_t *ilmp, *olmp; + int n; + ctf_diff_guess_t *cdg; + + oifp = ifp; + + if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL) + return (CTF_ERR); + + if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL) + return (ctf_set_errno(oifp, ctf_errno(ofp))); + + if (LCTF_INFO_VLEN(ifp, itp->ctt_info) != + LCTF_INFO_VLEN(ofp, otp->ctt_info)) + return (B_TRUE); + + (void) ctf_get_ctt_size(ifp, itp, &isize, &iincr); + (void) ctf_get_ctt_size(ofp, otp, &osize, &oincr); + + if (ifp->ctf_version == CTF_VERSION_1 || isize < CTF_LSTRUCT_THRESH) { + imp = (const ctf_member_t *)((uintptr_t)itp + iincr); + ilmp = NULL; + } else { + imp = NULL; + ilmp = (const ctf_lmember_t *)((uintptr_t)itp + iincr); + } + + if (ofp->ctf_version == CTF_VERSION_1 || osize < CTF_LSTRUCT_THRESH) { + omp = (const ctf_member_t *)((uintptr_t)otp + oincr); + olmp = NULL; + } else { + omp = NULL; + 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; + ctf_id_t itype, otype; + int ret; + + if (imp != NULL) { + iname = ctf_strptr(ifp, imp->ctm_name); + ioff = imp->ctm_offset; + itype = imp->ctm_type; + } else { + iname = ctf_strptr(ifp, ilmp->ctlm_name); + ioff = CTF_LMEM_OFFSET(ilmp); + itype = ilmp->ctlm_type; + } + + if (omp != NULL) { + oname = ctf_strptr(ofp, omp->ctm_name); + ooff = omp->ctm_offset; + otype = omp->ctm_type; + } else { + oname = ctf_strptr(ofp, olmp->ctlm_name); + ooff = CTF_LMEM_OFFSET(olmp); + otype = olmp->ctlm_type; + } + + if (ioff != ooff) { + return (B_TRUE); + } + if (strcmp(iname, oname) != 0) { + return (B_TRUE); + } + ret = ctf_diff_type(cds, ifp, itype, ofp, otype); + if (ret != B_FALSE) { + return (ret); + } + + /* Advance our pointers */ + if (imp != NULL) + imp++; + if (ilmp != NULL) + ilmp++; + if (omp != NULL) + omp++; + if (olmp != NULL) + olmp++; + } + + return (B_FALSE); +} + +/* + * Two unions are the same if they have the same set of members. This is similar + * to, but slightly different from a struct. The offsets of members don't + * matter. However, their is no guarantee of ordering so we have to fall back to + * doing an O(N^2) scan. + */ +typedef struct ctf_diff_union_member { + ctf_diff_t *cdum_cds; + ctf_file_t *cdum_fp; + ctf_file_t *cdum_iterfp; + const char *cdum_name; + ctf_id_t cdum_type; + int cdum_ret; +} ctf_diff_union_member_t; + +typedef struct ctf_diff_union_fp { + ctf_diff_t *cduf_cds; + ctf_file_t *cduf_curfp; + ctf_file_t *cduf_altfp; + ctf_id_t cduf_type; + int cduf_ret; +} ctf_diff_union_fp_t; + +/* ARGSUSED */ +static int +ctf_diff_union_check_member(const char *name, ctf_id_t id, ulong_t off, + void *arg) +{ + int ret; + ctf_diff_union_member_t *cdump = arg; + + if (strcmp(name, cdump->cdum_name) != 0) + return (0); + + ret = ctf_diff_type(cdump->cdum_cds, cdump->cdum_fp, cdump->cdum_type, + cdump->cdum_iterfp, id); + if (ret == CTF_ERR) { + cdump->cdum_ret = CTF_ERR; + return (1); + } + + if (ret == B_FALSE) { + cdump->cdum_ret = B_FALSE; + /* Return non-zero to stop iteration as we have a match */ + return (1); + } + + return (0); +} + +/* ARGSUSED */ +static int +ctf_diff_union_check_fp(const char *name, ctf_id_t id, ulong_t off, void *arg) +{ + int ret; + ctf_diff_union_member_t cdum; + ctf_diff_union_fp_t *cdufp = arg; + + cdum.cdum_cds = cdufp->cduf_cds; + cdum.cdum_fp = cdufp->cduf_curfp; + cdum.cdum_iterfp = cdufp->cduf_altfp; + cdum.cdum_name = name; + cdum.cdum_type = id; + cdum.cdum_ret = B_TRUE; + + ret = ctf_member_iter(cdum.cdum_iterfp, cdufp->cduf_type, + ctf_diff_union_check_member, &cdum); + if (ret == 0 || cdum.cdum_ret == CTF_ERR) { + /* No match found or error, terminate now */ + cdufp->cduf_ret = cdum.cdum_ret; + return (1); + } else if (ret == CTF_ERR) { + (void) ctf_set_errno(cdum.cdum_fp, ctf_errno(cdum.cdum_iterfp)); + cdufp->cduf_ret = CTF_ERR; + return (1); + } else { + ASSERT(cdum.cdum_ret == B_FALSE); + cdufp->cduf_ret = cdum.cdum_ret; + return (0); + } +} + +static int +ctf_diff_union(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, + ctf_id_t oid) +{ + 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; + if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL) + return (CTF_ERR); + if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL) + return (ctf_set_errno(oifp, ctf_errno(ofp))); + + if (LCTF_INFO_VLEN(ifp, itp->ctt_info) != + 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; + cduf.cduf_type = oid; + cduf.cduf_ret = B_TRUE; + ret = ctf_member_iter(ifp, iid, ctf_diff_union_check_fp, &cduf); + if (ret != CTF_ERR) + ret = cduf.cduf_ret; + + return (ret); +} + +/* + * Two enums are equivalent if they share the same underlying type and they have + * the same set of members. + */ +static int +ctf_diff_enum(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid) +{ + ctf_file_t *oifp; + const ctf_type_t *itp, *otp; + ssize_t iincr, oincr; + const ctf_enum_t *iep, *oep; + int n; + + oifp = ifp; + if ((itp = ctf_lookup_by_id(&ifp, iid)) == NULL) + return (CTF_ERR); + if ((otp = ctf_lookup_by_id(&ofp, oid)) == NULL) + return (ctf_set_errno(oifp, ctf_errno(ofp))); + + if (LCTF_INFO_VLEN(ifp, itp->ctt_info) != + LCTF_INFO_VLEN(ofp, otp->ctt_info)) + return (B_TRUE); + + (void) ctf_get_ctt_size(ifp, itp, NULL, &iincr); + (void) ctf_get_ctt_size(ofp, otp, NULL, &oincr); + iep = (const ctf_enum_t *)((uintptr_t)itp + iincr); + oep = (const ctf_enum_t *)((uintptr_t)otp + oincr); + + for (n = LCTF_INFO_VLEN(ifp, itp->ctt_info); n != 0; + n--, iep++, oep++) { + if (strcmp(ctf_strptr(ifp, iep->cte_name), + ctf_strptr(ofp, oep->cte_name)) != 0) + return (B_TRUE); + + if (iep->cte_value != oep->cte_value) + return (B_TRUE); + } + + return (B_FALSE); +} + +/* + * Two forwards are equivalent in one of two cases. If both are forwards, than + * they are the same. Otherwise, they're equivalent if one is a struct or union + * and the other is a forward. + */ +static int +ctf_diff_forward(ctf_file_t *ifp, ctf_id_t iid, ctf_file_t *ofp, ctf_id_t oid) +{ + int ikind, okind; + + ikind = ctf_type_kind(ifp, iid); + okind = ctf_type_kind(ofp, oid); + + if (ikind == okind) { + ASSERT(ikind == CTF_K_FORWARD); + return (B_FALSE); + } else if (ikind == CTF_K_FORWARD) { + return (okind != CTF_K_UNION && okind != CTF_K_STRUCT); + } else { + return (ikind != CTF_K_UNION && ikind != CTF_K_STRUCT); + } +} + +/* + * Are two types equivalent? + */ +int +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; + + /* Do a quick short circuit */ + if (ifp == ofp && iid == oid) + return (B_FALSE); + + /* + * Check if it's something we've already encountered in a forward + * reference or forward negative table. Also double check the reverse + * table. + */ + if (cds->cds_forward[TINDEX(iid)] == oid) + return (B_FALSE); + if (cds->cds_forward[TINDEX(iid)] != 0) + return (B_TRUE); + 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 || + (cds->cds_flags & CTF_DIFF_F_IGNORE_INTNAMES) == 0) + return (ret); + } + + if (ikind == CTF_K_FORWARD || okind == CTF_K_FORWARD) + return (ctf_diff_forward(ifp, iid, ofp, oid)); + + switch (ikind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + ret = ctf_diff_number(ifp, iid, ofp, oid); + break; + case CTF_K_ARRAY: + ret = ctf_diff_array(cds, ifp, iid, ofp, oid); + break; + case CTF_K_FUNCTION: + ret = ctf_diff_fptr(cds, ifp, iid, ofp, oid); + break; + case CTF_K_STRUCT: + ret = ctf_diff_struct(cds, ifp, iid, ofp, oid); + break; + case CTF_K_UNION: + ret = ctf_diff_union(cds, ifp, iid, ofp, oid); + break; + case CTF_K_ENUM: + ret = ctf_diff_enum(ifp, iid, ofp, oid); + break; + case CTF_K_FORWARD: + ret = ctf_diff_forward(ifp, iid, ofp, oid); + break; + case CTF_K_POINTER: + case CTF_K_TYPEDEF: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + ret = ctf_diff_qualifier(cds, ifp, iid, ofp, oid); + break; + case CTF_K_UNKNOWN: + /* + * The current CTF tools use CTF_K_UNKNOWN as a padding type. We + * always declare two instances of CTF_K_UNKNOWN as different, + * even though this leads to additional diff noise. + */ + ret = B_TRUE; + break; + default: + abort(); + } + + return (ret); +} + +/* + * Walk every type in the first container and try to find a match in the second. + * If there is a match, then update both the forward and reverse mapping tables. + */ +static int +ctf_diff_pass1(ctf_diff_t *cds) +{ + int i, j, diff; + int istart, iend, jstart, jend; + + if (cds->cds_ifp->ctf_flags & LCTF_CHILD) { + istart = 0x8001; + iend = cds->cds_ifp->ctf_typemax + 0x8000; + } else { + istart = 1; + iend = cds->cds_ifp->ctf_typemax; + } + + if (cds->cds_ofp->ctf_flags & LCTF_CHILD) { + jstart = 0x8001; + jend = cds->cds_ofp->ctf_typemax + 0x8000; + } else { + jstart = 1; + jend = cds->cds_ofp->ctf_typemax; + } + + for (i = istart; i <= iend; i++) { + diff = B_TRUE; + for (j = jstart; j <= jend; j++) { + 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) { + 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) { + cds->cds_forward[TINDEX(i)] = CTF_ERR; + cds->cds_func(cds->cds_ifp, i, B_FALSE, NULL, CTF_ERR, + cds->cds_arg); + } else { + cds->cds_func(cds->cds_ifp, i, B_TRUE, cds->cds_ofp, j, + cds->cds_arg); + } + } + + return (0); +} + +/* + * Now we need to walk the second container and emit anything that we didn't + * find as common in the first pass. + */ +static int +ctf_diff_pass2(ctf_diff_t *cds) +{ + int i, start, end; + + 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); + } + + return (0); +} + +int +ctf_diff_init(ctf_file_t *ifp, ctf_file_t *ofp, ctf_diff_t **cdsp) +{ + ctf_diff_t *cds; + size_t fsize, rsize; + + cds = ctf_alloc(sizeof (ctf_diff_t)); + if (cds == NULL) + return (ctf_set_errno(ifp, ENOMEM)); + + bzero(cds, sizeof (ctf_diff_t)); + cds->cds_ifp = ifp; + cds->cds_ofp = ofp; + + 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, ENOMEM)); + } + 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, ENOMEM)); + } + bzero(cds->cds_forward, fsize); + bzero(cds->cds_reverse, rsize); + + cds->cds_ifp->ctf_refcnt++; + cds->cds_ofp->ctf_refcnt++; + *cdsp = cds; + return (0); +} + +int +ctf_diff_types(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg) +{ + int ret; + + cds->cds_func = cb; + cds->cds_arg = arg; + + 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--; + + 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)); +} + +uint_t +ctf_diff_getflags(ctf_diff_t *cds) +{ + return (cds->cds_flags); +} + +int +ctf_diff_setflags(ctf_diff_t *cds, uint_t flags) +{ + if ((flags & ~CTF_DIFF_F_MASK) != 0) + return (ctf_set_errno(cds->cds_ifp, EINVAL)); + + 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..c8a4d22cf7 --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_elfwrite.c @@ -0,0 +1,412 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ +/* + * 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 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_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 { + 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..4658fc8ddc 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,8 @@ #include <errno.h> #include <dlfcn.h> #include <gelf.h> +#include <zlib.h> +#include <zone.h> #ifdef _LP64 static const char *_libctf_zlib = "/usr/lib/64/libz.so.1"; @@ -42,6 +47,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; @@ -72,21 +80,47 @@ _libctf_init(void) void * ctf_zopen(int *errp) { - ctf_dprintf("decompressing CTF data using %s\n", _libctf_zlib); + char buf[MAXPATHLEN]; + const char *path = _libctf_zlib, *zroot; if (zlib.z_dlp != NULL) return (zlib.z_dlp); /* library is already loaded */ - if (access(_libctf_zlib, R_OK) == -1) + /* + * Get the zone native root. For the tools build, we don't need + * this (it seems fair to impose that we always build the system in + * a native zone), and we want to allow build machines that are older + * that the notion of the native root, so we only actually make this + * call if we're not the tools build. + */ +#ifndef CTF_TOOLS_BUILD + zroot = zone_get_nroot(); +#else + zroot = NULL; +#endif + + if (zroot != NULL) { + (void) snprintf(buf, MAXPATHLEN, "%s/%s", zroot, _libctf_zlib); + path = buf; + } + + ctf_dprintf("decompressing CTF data using %s\n", path); + + if (access(path, R_OK) == -1) return (ctf_set_open_errno(errp, ECTF_ZMISSING)); - if ((zlib.z_dlp = dlopen(_libctf_zlib, RTLD_LAZY | RTLD_LOCAL)) == NULL) + if ((zlib.z_dlp = dlopen(path, RTLD_LAZY | RTLD_LOCAL)) == NULL) 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 +145,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 +248,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 +280,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 +432,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 +460,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 +485,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 +504,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 3fd69318de..13c7e07e99 100644 --- a/usr/src/lib/libctf/common/libctf.h +++ b/usr/src/lib/libctf/common/libctf.h @@ -23,6 +23,9 @@ * Copyright 2001-2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ +/* + * Copyright (c) 2015, Joyent, Inc. + */ /* * This header file defines the interfaces available from the CTF debugger @@ -32,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. */ @@ -40,8 +43,6 @@ #ifndef _LIBCTF_H #define _LIBCTF_H -#pragma ident "%Z%%M% %I% %E% SMI" - #include <sys/ctf_api.h> #ifdef __cplusplus @@ -53,6 +54,39 @@ extern "C" { */ extern int _libctf_debug; +typedef enum ctf_diff_flag { + CTF_DIFF_F_IGNORE_INTNAMES = 0x01, + CTF_DIFF_F_MASK = 0x01 +} ctf_diff_flag_t; + +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 +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 5573e8db25..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,17 +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_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; |