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 | 90 | ||||
| -rw-r--r-- | usr/src/lib/libctf/Makefile.shared.targ | 30 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/ctf_convert.c | 210 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/ctf_diff.c | 1362 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/ctf_dwarf.c | 2957 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/ctf_elfwrite.c | 422 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/ctf_lib.c | 325 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/ctf_merge.c | 1570 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/ctf_subr.c | 28 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/libctf.h | 48 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/libctf_impl.h | 59 | ||||
| -rw-r--r-- | usr/src/lib/libctf/common/mapfile-vers | 36 | 
13 files changed, 7113 insertions, 65 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..55f090e7f8 --- /dev/null +++ b/usr/src/lib/libctf/Makefile.shared.com @@ -0,0 +1,90 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# +# Copyright 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_dwarf.o \ +	ctf_error.o \ +	ctf_hash.o \ +	ctf_labels.o \ +	ctf_lookup.o \ +	ctf_open.o \ +	ctf_types.o \ +	ctf_util.o + +MERGEQ_OBJS = \ +	mergeq.o \ +	workq.o + +LIST_OBJS = \ +	list.o + +LIB_OBJS = \ +	ctf_convert.o \ +	ctf_elfwrite.o \ +	ctf_diff.o \ +	ctf_lib.o \ +	ctf_merge.o \ +	ctf_subr.o + +OBJECTS = $(COMMON_OBJS) $(LIB_OBJS) $(LIST_OBJS) $(MERGEQ_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) \ +	$(MERGEQ_OBJS:%.o=$(SRC)/lib/mergeq/%.c) +	 +LIBS = $(DYNLIB) $(LINTLIB) +LDLIBS += -lc -lelf -ldwarf -lavl + +C99MODE=	-xc99=%all +C99LMODE=	-Xc99=%all + +SRCDIR = $(SRC)/lib/libctf/common + +CPPFLAGS +=	-I$(SRC)/lib/libctf/common 	\ +		-I$(SRC)/common/ctf		\ +		-I$(SRC)/lib/libdwarf/common	\ +		-I$(SRC)/lib/mergeq		\ +		-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..b6520f2366 --- /dev/null +++ b/usr/src/lib/libctf/Makefile.shared.targ @@ -0,0 +1,30 @@ +# +# 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) + +pics/%.o: $(SRC)/lib/mergeq/%.c +	$(COMPILE.c) -o $@ $< +	$(POST_PROCESS_O) diff --git a/usr/src/lib/libctf/common/ctf_convert.c b/usr/src/lib/libctf/common/ctf_convert.c new file mode 100644 index 0000000000..1a433d17db --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_convert.c @@ -0,0 +1,210 @@ +/* + * 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 2015 Joyent, Inc. + */ + +/* + * Main conversion entry points. This has been designed such that there can be + * any number of different conversion backends. Currently we only have one that + * understands DWARFv2 (and bits of DWARFv4). Each backend should be placed in + * the ctf_converters list and each will be tried in turn. + */ + +#include <libctf_impl.h> +#include <gelf.h> + +ctf_convert_f ctf_converters[] = { +	ctf_dwarf_convert +}; + +#define	NCONVERTS	(sizeof (ctf_converters) / sizeof (ctf_convert_f)) + +typedef enum ctf_convert_source { +	CTFCONV_SOURCE_NONE = 0x0, +	CTFCONV_SOURCE_UNKNOWN = 0x01, +	CTFCONV_SOURCE_C = 0x02, +	CTFCONV_SOURCE_S = 0x04 +} ctf_convert_source_t; + +static void +ctf_convert_ftypes(Elf *elf, ctf_convert_source_t *types) +{ +	int i; +	Elf_Scn *scn = NULL, *strscn; +	*types = CTFCONV_SOURCE_NONE; +	GElf_Shdr shdr; +	Elf_Data *data, *strdata; + +	while ((scn = elf_nextscn(elf, scn)) != NULL) { + +		if (gelf_getshdr(scn, &shdr) == NULL) +			return; + +		if (shdr.sh_type == SHT_SYMTAB) +			break; +	} + +	if (scn == NULL) +		return; + +	if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL) +		return; + +	if ((data = elf_getdata(scn, NULL)) == NULL) +		return; + +	if ((strdata = elf_getdata(strscn, NULL)) == NULL) +		return; + +	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) +			return; + +		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 || file[len - 2] != '.') { +			*types |= CTFCONV_SOURCE_UNKNOWN; +			continue; +		} + +		switch (file[len - 1]) { +		case 'c': +			*types |= CTFCONV_SOURCE_C; +			break; +		case 'h': +			/* We traditionally ignore header files... */ +			break; +		case 's': +			*types |= CTFCONV_SOURCE_S; +			break; +		default: +			*types |= CTFCONV_SOURCE_UNKNOWN; +			break; +		} +	} +} + +ctf_file_t * +ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t nthrs, uint_t flags, +    int *errp, char *errbuf, size_t errlen) +{ +	int err, i; +	ctf_file_t *fp = NULL; +	boolean_t notsup = B_TRUE; +	ctf_convert_source_t type; + +	if (errp == NULL) +		errp = &err; + +	if (elf == NULL) { +		*errp = EINVAL; +		return (NULL); +	} + +	if (flags & ~CTF_CONVERT_F_IGNNONC) { +		*errp = EINVAL; +		return (NULL); +	} + +	if (elf_kind(elf) != ELF_K_ELF) { +		*errp = ECTF_FMT; +		return (NULL); +	} + +	ctf_convert_ftypes(elf, &type); +	ctf_dprintf("got types: %d\n", type); +	if (flags & CTF_CONVERT_F_IGNNONC) { +		if (type == CTFCONV_SOURCE_NONE || +		    (type & CTFCONV_SOURCE_UNKNOWN)) { +			*errp = ECTF_CONVNOCSRC; +			return (NULL); +		} +	} + +	for (i = 0; i < NCONVERTS; i++) { +		ctf_conv_status_t cs; + +		fp = NULL; +		cs = ctf_converters[i](fd, elf, nthrs, errp, &fp, errbuf, +		    errlen); +		if (cs == CTF_CONV_SUCCESS) { +			notsup = B_FALSE; +			break; +		} +		if (cs == CTF_CONV_ERROR) { +			fp = NULL; +			notsup = B_FALSE; +			break; +		} +	} + +	if (notsup == B_TRUE) { +		if ((flags & CTF_CONVERT_F_IGNNONC) != 0 && +		    (type & CTFCONV_SOURCE_C) == 0) { +			*errp = ECTF_CONVNOCSRC; +			return (NULL); +		} +		*errp = ECTF_NOCONVBKEND; +		return (NULL); +	} + +	/* +	 * Succsesful conversion. +	 */ +	if (fp != NULL) { +		if (label == NULL) +			label = ""; +		if (ctf_add_label(fp, label, fp->ctf_typemax, 0) == CTF_ERR) { +			*errp = ctf_errno(fp); +			ctf_close(fp); +			return (NULL); +		} +		if (ctf_update(fp) == CTF_ERR) { +			*errp = ctf_errno(fp); +			ctf_close(fp); +			return (NULL); +		} +	} + +	return (fp); +} + +ctf_file_t * +ctf_fdconvert(int fd, const char *label, uint_t nthrs, uint_t flags, int *errp, +    char *errbuf, size_t errlen) +{ +	int err; +	Elf *elf; +	ctf_file_t *fp; + +	if (errp == NULL) +		errp = &err; + +	elf = elf_begin(fd, ELF_C_READ, NULL); +	if (elf == NULL) { +		*errp = ECTF_FMT; +		return (NULL); +	} + +	fp = ctf_elfconvert(fd, elf, label, nthrs, flags, errp, errbuf, errlen); + +	(void) elf_end(elf); +	return (fp); +} 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..d070488bbb --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_diff.c @@ -0,0 +1,1362 @@ +/* + * 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; +	size_t cds_fsize; +	size_t cds_rsize; +	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 typedefs are equivalent, if after we resolve a chain of typedefs, they + * point to equivalent types. This means that if a size_t is defined as follows: + * + * size_t -> ulong_t -> unsigned long + * size_t -> unsigned long + * + * That we'll ultimately end up treating them the same. + */ +static int +ctf_diff_typedef(ctf_diff_t *cds, ctf_file_t *ifp, ctf_id_t iid, +    ctf_file_t *ofp, ctf_id_t oid) +{ +	ctf_id_t iref = CTF_ERR, oref = CTF_ERR; + +	while (ctf_type_kind(ifp, iid) == CTF_K_TYPEDEF) { +		iref = ctf_type_reference(ifp, iid); +		if (iref == CTF_ERR) +			return (CTF_ERR); +		iid = iref; +	} + +	while (ctf_type_kind(ofp, oid) == CTF_K_TYPEDEF) { +		oref = ctf_type_reference(ofp, oid); +		if (oref == CTF_ERR) +			return (CTF_ERR); +		oid = oref; +	} + +	VERIFY(iref != CTF_ERR && oref != CTF_ERR); +	return (ctf_diff_type(cds, ifp, iref, ofp, oref)); +} + +/* + * 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, as well as them having the + * same overall size. + */ +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 (ctf_type_size(ifp, iid) != ctf_type_size(ofp, oid)) +		return (B_TRUE); + +	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_TYPEDEF: +		ret = ctf_diff_typedef(cds, ifp, iid, ofp, oid); +		break; +	case CTF_K_POINTER: +	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. + * + * The self variable tells us whether or not we should be comparing the input + * ctf container with itself or not. + */ +static int +ctf_diff_pass1(ctf_diff_t *cds, boolean_t self) +{ +	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; + +		/* +		 * If we're doing a self diff for dedup purposes, then we want +		 * to ensure that we compare a type i with every type in the +		 * range, [ 1, i ). Yes, this does mean that when i equals 1, +		 * we won't compare anything. +		 */ +		if (self == B_TRUE) { +			jstart = istart; +			jend = i - 1; +		} +		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_fsize = fsize; +	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)); +	} +	cds->cds_rsize = rsize; +	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, B_FALSE); +	if (ret == 0) +		ret = ctf_diff_pass2(cds); + +	cds->cds_func = NULL; +	cds->cds_arg = NULL; +	cds->cds_tvalid = B_TRUE; +	return (ret); +} + +/* + * Do a diff where we're comparing a container with itself. In other words we'd + * like to know what types are actually duplicates of existing types in the + * container. + * + * Note this should remain private to libctf and not be exported in the public + * mapfile for the time being. + */ +int +ctf_diff_self(ctf_diff_t *cds, ctf_diff_type_f cb, void *arg) +{ +	if (cds->cds_ifp != cds->cds_ofp) +		return (EINVAL); + +	cds->cds_func = cb; +	cds->cds_arg = arg; + +	return (ctf_diff_pass1(cds, B_TRUE)); +} + + +void +ctf_diff_fini(ctf_diff_t *cds) +{ +	ctf_diff_guess_t *cdg; +	size_t fsize, rsize; + +	if (cds == NULL) +		return; + +	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)); +	} +	if (cds->cds_forward != NULL) +		ctf_free(cds->cds_forward, cds->cds_fsize); +	if (cds->cds_reverse != NULL) +		ctf_free(cds->cds_reverse, cds->cds_rsize); +	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_IGNORE_INTNAMES) != 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_dwarf.c b/usr/src/lib/libctf/common/ctf_dwarf.c new file mode 100644 index 0000000000..811a55bc64 --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_dwarf.c @@ -0,0 +1,2957 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2007 Sun Microsystems, Inc.  All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright 2012 Jason King.  All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2015 Joyent, Inc. + */ + +/* + * CTF DWARF conversion theory. + * + * DWARF data contains a series of compilation units. Each compilation unit + * generally refers to an object file or what once was, in the case of linked + * binaries and shared objects. Each compilation unit has a series of what DWARF + * calls a DIE (Debugging Information Entry). The set of entries that we care + * about have type information stored in a series of attributes. Each DIE also + * has a tag that identifies the kind of attributes that it has. + * + * A given DIE may itself have children. For example, a DIE that represents a + * structure has children which represent members. Whenever we encounter a DIE + * that has children or other values or types associated with it, we recursively + * process those children first so that way we can then refer to the generated + * CTF type id while processing its parent. This reduces the amount of unknowns + * and fixups that we need. It also ensures that we don't accidentally add types + * that an overzealous compiler might add to the DWARF data but aren't used by + * anything in the system. + * + * Once we do a conversion, we store a mapping in an AVL tree that goes from the + * DWARF's die offset, which is relative to the given compilation unit), to a + * ctf_id_t. + * + * Unfortunately, some compilers actually will emit duplicate entries for a + * given type that look similar, but aren't quite. To that end, we go through + * and do a variant on a merge once we're done processing a single compilation + * unit which deduplicates all of the types that are in the unit. + * + * Finally, if we encounter an object that has multiple compilation units, then + * we'll convert all of the compilation units separately and then do a merge, so + * that way we can result in one single ctf_file_t that represents everything + * for the object. + * + * Conversion Steps + * ---------------- + * + * Because a given object we've been given to convert may have multiple + * compilation units, we break the work into two halves. The first half + * processes each compilation unit (potentially in parallel) and then the second + * half optionally merges all of the dies in the first half. First, we'll cover + * what's involved in converting a single ctf_die_t's dwarf to CTF. This covers + * the work done in ctf_dwarf_convert_one(). + * + * An individual ctf_die_t, which represents a compilation unit, is converted to + * CTF in a series of multiple passes. + * + * Pass 1: During the first pass we walk all of the dies and if we find a + * function, variable, struct, union, enum or typedef, we recursively transform + * all of its types. We don't recurse or process everything, because we don't + * want to add some of the types that compilers may add which are effectively + * unused. + * + * During pass 1, if we encounter any structures or unions we mark them for + * fixing up later. This is necessary because we may not be able to determine + * the full size of a structure at the beginning of time. This will happen if + * the DWARF attribute DW_AT_byte_size is not present for a member. Because of + * this possibility we defer adding members to structures or even converting + * them during pass 1 and save that for pass 2. Adding all of the base + * structures without any of their members helps deal with any circular + * dependencies that we might encounter. + * + * Pass 2: This pass is used to do the first half of fixing up structures and + * unions. Rather than walk the entire type space again, we actually walk the + * list of structures and unions that we marked for later fixing up. Here, we + * iterate over every structure and add members to the underlying ctf_file_t, + * but not to the structs themselves. One might wonder why we don't, and the + * main reason is that libctf requires a ctf_update() be done before adding the + * members to structures or unions. + * + * Pass 3: This pass is used to do the second half of fixing up structures and + * unions. During this part we always go through and add members to structures + * and unions that we added to the container in the previous pass. In addition, + * we set the structure and union's actual size, which may have additional + * padding added by the compiler, it isn't simply the last offset. DWARF always + * guarantees an attribute exists for this. Importantly no ctf_id_t's change + * during pass 2. + * + * Pass 4: The next phase is to add CTF entries for all of the symbols and + * variables that are present in this die. During pass 1 we added entries to a + * map for each variable and function. During this pass, we iterate over the + * symbol table and when we encounter a symbol that we have in our lists of + * translated information which matches, we then add it to the ctf_file_t. + * + * Pass 5: Here we go and look for any weak symbols and functions and see if + * they match anything that we recognize. If so, then we add type information + * for them at this point based on the matching type. + * + * Pass 6: This pass is actually a variant on a merge. The traditional merge + * process expects there to be no duplicate types. As such, at the end of + * conversion, we do a dedup on all of the types in the system. The + * deduplication process is described in lib/libctf/common/ctf_merge.c. + * + * Once pass 6 is done, we've finished processing the individual compilation + * unit. + * + * The following steps reflect the general process of doing a conversion. + * + * 1) Walk the dwarf section and determine the number of compilation units + * 2) Create a ctf_die_t for each compilation unit + * 3) Add all ctf_die_t's to a workq + * 4) Have the workq process each die with ctf_dwarf_convert_one. This itself + *    is comprised of several steps, which were already enumerated. + * 5) If we have multiple dies, we do a ctf merge of all the dies. The mechanics + *    of the merge are discussed in lib/libctf/common/ctf_merge.c. + * 6) Free everything up and return a ctf_file_t to the user. If we only had a + *    single compilation unit, then we give that to the user. Otherwise, we + *    return the merged ctf_file_t. + * + * Threading + * --------- + * + * The process has been designed to be amenable to threading. Each compilation + * unit has its own type stream, therefore the logical place to divide and + * conquer is at the compilation unit. Each ctf_die_t has been built to be able + * to be processed independently of the others. It has its own libdwarf handle, + * as a given libdwarf handle may only be used by a single thread at a time. + * This allows the various ctf_die_t's to be processed in parallel by different + * threads. + * + * All of the ctf_die_t's are loaded into a workq which allows for a number of + * threads to be specified and used as a thread pool to process all of the + * queued work. We set the number of threads to use in the workq equal to the + * number of threads that the user has specified. + * + * After all of the compilation units have been drained, we use the same number + * of threads when performing a merge of multiple compilation units, if they + * exist. + * + * While all of these different parts do support and allow for multiple threads, + * it's important that when only a single thread is specified, that it be the + * calling thread. This allows the conversion routines to be used in a context + * that doesn't allow additional threads, such as rtld. + * + * Common DWARF Mechanics and Notes + * -------------------------------- + * + * At this time, we really only support DWARFv2, though support for DWARFv4 is + * mostly there. There is no intent to support DWARFv3. + * + * Generally types for something are stored in the DW_AT_type attribute. For + * example, a function's return type will be stored in the local DW_AT_type + * attribute while the arguments will be in child DIEs. There are also various + * times when we don't have any DW_AT_type. In that case, the lack of a type + * implies, at least for C, that it's C type is void. Because DWARF doesn't emit + * one, we have a synthetic void type that we create and manipulate instead and + * pass it off to consumers on an as-needed basis. If nothing has a void type, + * it will not be emitted. + * + * Architecture Specific Parts + * --------------------------- + * + * The CTF tooling encodes various information about the various architectures + * in the system. Importantly, the tool assumes that every architecture has a + * data model where long and pointer are the same size. This is currently the + * case, as the two data models illumos supports are ILP32 and LP64. + * + * In addition, we encode the mapping of various floating point sizes to various + * types for each architecture. If a new architecture is being added, it should + * be added to the list. The general design of the ctf conversion tools is to be + * architecture independent. eg. any of the tools here should be able to convert + * any architecture's DWARF into ctf; however, this has not been rigorously + * tested and more importantly, the ctf routines don't currently write out the + * data in an endian-aware form, they only use that of the currently running + * library. + */ + +#include <libctf_impl.h> +#include <sys/avl.h> +#include <sys/debug.h> +#include <gelf.h> +#include <libdwarf.h> +#include <dwarf.h> +#include <libgen.h> +#include <workq.h> +#include <errno.h> + +#define	DWARF_VERSION_TWO	2 +#define	DWARF_VARARGS_NAME	"..." + +/* + * Dwarf may refer recursively to other types that we've already processed. To + * see if we've already converted them, we look them up in an AVL tree that's + * sorted by the DWARF id. + */ +typedef struct ctf_dwmap { +	avl_node_t	cdm_avl; +	Dwarf_Off	cdm_off; +	Dwarf_Die	cdm_die; +	ctf_id_t	cdm_id; +	boolean_t	cdm_fix; +} ctf_dwmap_t; + +typedef struct ctf_dwvar { +	ctf_list_t	cdv_list; +	char 		*cdv_name; +	ctf_id_t	cdv_type; +	boolean_t	cdv_global; +} ctf_dwvar_t; + +typedef struct ctf_dwfunc { +	ctf_list_t	cdf_list; +	char		*cdf_name; +	ctf_funcinfo_t	cdf_fip; +	ctf_id_t	*cdf_argv; +	boolean_t	cdf_global; +} ctf_dwfunc_t; + +typedef struct ctf_dwbitf { +	ctf_list_t	cdb_list; +	ctf_id_t	cdb_base; +	uint_t		cdb_nbits; +	ctf_id_t	cdb_id; +} ctf_dwbitf_t; + +/* + * The ctf_die_t represents a single top-level DWARF die unit. While generally, + * the typical object file hs only a single die, if we're asked to convert + * something that's been linked from multiple sources, multiple dies will exist. + */ +typedef struct ctf_die { +	Elf		*cd_elf;	/* shared libelf handle */ +	char		*cd_name;	/* basename of the DIE */ +	ctf_merge_t	*cd_cmh;	/* merge handle */ +	ctf_list_t	cd_vars;	/* List of variables */ +	ctf_list_t	cd_funcs;	/* List of functions */ +	ctf_list_t	cd_bitfields;	/* Bit field members */ +	Dwarf_Debug	cd_dwarf;	/* shared libdwarf handle */ +	Dwarf_Die	cd_cu;		/* libdwarf compilation unit */ +	Dwarf_Off	cd_cuoff;	/* cu's offset */ +	Dwarf_Off	cd_maxoff;	/* maximum offset */ +	ctf_file_t	*cd_ctfp;	/* output CTF file */ +	avl_tree_t	cd_map;		/* map die offsets to CTF types */ +	char		*cd_errbuf;	/* error message buffer */ +	size_t		cd_errlen;	/* error message buffer length */ +	size_t		cd_ptrsz;	/* object's pointer size */ +	boolean_t	cd_bigend;	/* is it big endian */ +	boolean_t	cd_doweaks;	/* should we convert weak symbols? */ +	uint_t		cd_mach;	/* machine type */ +	ctf_id_t	cd_voidtid;	/* void pointer */ +	ctf_id_t	cd_longtid;	/* id for a 'long' */ +} ctf_die_t; + +static int ctf_dwarf_offset(ctf_die_t *, Dwarf_Die, Dwarf_Off *); +static int ctf_dwarf_convert_die(ctf_die_t *, Dwarf_Die); +static int ctf_dwarf_convert_type(ctf_die_t *, Dwarf_Die, ctf_id_t *, int); + +static int ctf_dwarf_function_count(ctf_die_t *, Dwarf_Die, ctf_funcinfo_t *, +    boolean_t); +static int ctf_dwarf_convert_fargs(ctf_die_t *, Dwarf_Die, ctf_funcinfo_t *, +    ctf_id_t *); + +typedef int (ctf_dwarf_symtab_f)(ctf_die_t *, const GElf_Sym *, ulong_t, +    const char *, const char *, void *); + +/* + * This is a generic way to set a CTF Conversion backend error depending on what + * we were doing. Unless it was one of a specific set of errors that don't + * indicate a programming / translation bug, eg. ENOMEM, then we transform it + * into a CTF backend error and fill in the error buffer. + */ +static int +ctf_dwarf_error(ctf_die_t *cdp, ctf_file_t *cfp, int err, const char *fmt, ...) +{ +	va_list ap; +	int ret; +	size_t off = 0; +	ssize_t rem = cdp->cd_errlen; +	if (cfp != NULL) +		err = ctf_errno(cfp); + +	if (err == ENOMEM) +		return (err); + +	ret = snprintf(cdp->cd_errbuf, rem, "die %s: ", cdp->cd_name); +	if (ret < 0) +		goto err; +	off += ret; +	rem = MAX(rem - ret, 0); + +	va_start(ap, fmt); +	ret = vsnprintf(cdp->cd_errbuf + off, rem, fmt, ap); +	va_end(ap); +	if (ret < 0) +		goto err; + +	off += ret; +	rem = MAX(rem - ret, 0); +	if (fmt[strlen(fmt) - 1] != '\n') { +		(void) snprintf(cdp->cd_errbuf + off, rem, +		    ": %s\n", ctf_errmsg(err)); +	} +	va_end(ap); +	return (ECTF_CONVBKERR); + +err: +	cdp->cd_errbuf[0] = '\0'; +	return (ECTF_CONVBKERR); +} + +/* + * DWARF often ops to put no explicit type to describe a void type. eg. if we + * have a reference type whose DW_AT_type member doesn't exist, then we should + * instead assume it points to void. Because this isn't represented, we + * instead cause it to come into existence. + */ +static ctf_id_t +ctf_dwarf_void(ctf_die_t *cdp) +{ +	if (cdp->cd_voidtid == CTF_ERR) { +		ctf_encoding_t enc = { CTF_INT_SIGNED, 0, 0 }; +		cdp->cd_voidtid = ctf_add_integer(cdp->cd_ctfp, CTF_ADD_ROOT, +		    "void", &enc); +		if (cdp->cd_voidtid == CTF_ERR) { +			(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +			    "failed to create void type: %s\n", +			    ctf_errmsg(ctf_errno(cdp->cd_ctfp))); +		} +	} + +	return (cdp->cd_voidtid); +} + +/* + * There are many different forms that an array index may take. However, we just + * always force it to be of a type long no matter what. Therefore we use this to + * have a single instance of long across everything. + */ +static ctf_id_t +ctf_dwarf_long(ctf_die_t *cdp) +{ +	if (cdp->cd_longtid == CTF_ERR) { +		ctf_encoding_t enc; + +		enc.cte_format = CTF_INT_SIGNED; +		enc.cte_offset = 0; +		/* All illumos systems are LP */ +		enc.cte_bits = cdp->cd_ptrsz * 8; +		cdp->cd_longtid = ctf_add_integer(cdp->cd_ctfp, CTF_ADD_NONROOT, +		    "long", &enc); +		if (cdp->cd_longtid == CTF_ERR) { +			(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +			    "failed to create long type: %s\n", +			    ctf_errmsg(ctf_errno(cdp->cd_ctfp))); +		} + +	} + +	return (cdp->cd_longtid); +} + +static int +ctf_dwmap_comp(const void *a, const void *b) +{ +	const ctf_dwmap_t *ca = a; +	const ctf_dwmap_t *cb = b; + +	if (ca->cdm_off > cb->cdm_off) +		return (1); +	if (ca->cdm_off < cb->cdm_off) +		return (-1); +	return (0); +} + +static int +ctf_dwmap_add(ctf_die_t *cdp, ctf_id_t id, Dwarf_Die die, boolean_t fix) +{ +	int ret; +	avl_index_t index; +	ctf_dwmap_t *dwmap; +	Dwarf_Off off; + +	VERIFY(id > 0 && id < CTF_MAX_TYPE); + +	if ((ret = ctf_dwarf_offset(cdp, die, &off)) != 0) +		return (ret); + +	if ((dwmap = ctf_alloc(sizeof (ctf_dwmap_t))) == NULL) +		return (ENOMEM); + +	dwmap->cdm_die = die; +	dwmap->cdm_off = off; +	dwmap->cdm_id = id; +	dwmap->cdm_fix = fix; + +	ctf_dprintf("dwmap: %p %x->%d\n", dwmap, (uint32_t)off, id); +	VERIFY(avl_find(&cdp->cd_map, dwmap, &index) == NULL); +	avl_insert(&cdp->cd_map, dwmap, index); +	return (0); +} + +static int +ctf_dwarf_attribute(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name, +    Dwarf_Attribute *attrp) +{ +	int ret; +	Dwarf_Error derr; + +	if ((ret = dwarf_attr(die, name, attrp, &derr)) == DW_DLV_OK) +		return (0); +	if (ret == DW_DLV_NO_ENTRY) { +		*attrp = NULL; +		return (ENOENT); +	} +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to get attribute for type: %s\n", +	    dwarf_errmsg(derr)); +	return (ECTF_CONVBKERR); +} + +static int +ctf_dwarf_ref(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name, Dwarf_Off *refp) +{ +	int ret; +	Dwarf_Attribute attr; +	Dwarf_Error derr; + +	if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0) +		return (ret); + +	if (dwarf_formref(attr, refp, &derr) == DW_DLV_OK) { +		dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR); +		return (0); +	} + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to get unsigned attribute for type: %s\n", +	    dwarf_errmsg(derr)); +	return (ECTF_CONVBKERR); +} + +static int +ctf_dwarf_refdie(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name, +    Dwarf_Die *diep) +{ +	int ret; +	Dwarf_Off off; +	Dwarf_Error derr; + +	if ((ret = ctf_dwarf_ref(cdp, die, DW_AT_type, &off)) != 0) +		return (ret); + +	off += cdp->cd_cuoff; +	if ((ret = dwarf_offdie(cdp->cd_dwarf, off, diep, &derr)) != +	    DW_DLV_OK) { +		(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +		    "failed to get die from offset %llu: %s\n", +		    off, dwarf_errmsg(derr)); +		return (ECTF_CONVBKERR); +	} + +	return (0); +} + +static int +ctf_dwarf_signed(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name, +    Dwarf_Signed *valp) +{ +	int ret; +	Dwarf_Attribute attr; +	Dwarf_Error derr; + +	if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0) +		return (ret); + +	if (dwarf_formsdata(attr, valp, &derr) == DW_DLV_OK) { +		dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR); +		return (0); +	} + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to get unsigned attribute for type: %s\n", +	    dwarf_errmsg(derr)); +	return (ECTF_CONVBKERR); +} + +static int +ctf_dwarf_unsigned(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name, +    Dwarf_Unsigned *valp) +{ +	int ret; +	Dwarf_Attribute attr; +	Dwarf_Error derr; + +	if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0) +		return (ret); + +	if (dwarf_formudata(attr, valp, &derr) == DW_DLV_OK) { +		dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR); +		return (0); +	} + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to get unsigned attribute for type: %s\n", +	    dwarf_errmsg(derr)); +	return (ECTF_CONVBKERR); +} + +static int +ctf_dwarf_boolean(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name, +    Dwarf_Bool *val) +{ +	int ret; +	Dwarf_Attribute attr; +	Dwarf_Error derr; + +	if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0) +		return (ret); + +	if (dwarf_formflag(attr, val, &derr) == DW_DLV_OK) { +		dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR); +		return (0); +	} + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to get boolean attribute for type: %s\n", +	    dwarf_errmsg(derr)); + +	return (ECTF_CONVBKERR); +} + +static int +ctf_dwarf_string(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half name, char **strp) +{ +	int ret; +	char *s; +	Dwarf_Attribute attr; +	Dwarf_Error derr; + +	*strp = NULL; +	if ((ret = ctf_dwarf_attribute(cdp, die, name, &attr)) != 0) +		return (ret); + +	if (dwarf_formstring(attr, &s, &derr) == DW_DLV_OK) { +		if ((*strp = ctf_strdup(s)) == NULL) +			ret = ENOMEM; +		else +			ret = 0; +		dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR); +		return (ret); +	} + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to get string attribute for type: %s\n", +	    dwarf_errmsg(derr)); +	return (ECTF_CONVBKERR); +} + +static int +ctf_dwarf_member_location(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Unsigned *valp) +{ +	int ret; +	Dwarf_Error derr; +	Dwarf_Attribute attr; +	Dwarf_Locdesc *loc; +	Dwarf_Signed locnum; + +	if ((ret = ctf_dwarf_attribute(cdp, die, DW_AT_data_member_location, +	    &attr)) != 0) +		return (ret); + +	if (dwarf_loclist(attr, &loc, &locnum, &derr) != DW_DLV_OK) { +		(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +		    "failed to obtain location list for member offset: %s", +		    dwarf_errmsg(derr)); +		dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR); +		return (ECTF_CONVBKERR); +	} +	dwarf_dealloc(cdp->cd_dwarf, attr, DW_DLA_ATTR); + +	if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) { +		(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +		    "failed to parse location structure for member"); +		dwarf_dealloc(cdp->cd_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK); +		dwarf_dealloc(cdp->cd_dwarf, loc, DW_DLA_LOCDESC); +		return (ECTF_CONVBKERR); +	} + +	*valp = loc->ld_s->lr_number; + +	dwarf_dealloc(cdp->cd_dwarf, loc->ld_s, DW_DLA_LOC_BLOCK); +	dwarf_dealloc(cdp->cd_dwarf, loc, DW_DLA_LOCDESC); +	return (0); +} + + +static int +ctf_dwarf_offset(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Off *offsetp) +{ +	Dwarf_Error derr; + +	if (dwarf_dieoffset(die, offsetp, &derr) == DW_DLV_OK) +		return (0); + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to get die offset: %s\n", +	    dwarf_errmsg(derr)); +	return (ECTF_CONVBKERR); +} + +static int +ctf_dwarf_tag(ctf_die_t *cdp, Dwarf_Die die, Dwarf_Half *tagp) +{ +	Dwarf_Error derr; + +	if (dwarf_tag(die, tagp, &derr) == DW_DLV_OK) +		return (0); + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to get tag type: %s\n", +	    dwarf_errmsg(derr)); +	return (ECTF_CONVBKERR); +} + +static int +ctf_dwarf_sib(ctf_die_t *cdp, Dwarf_Die base, Dwarf_Die *sibp) +{ +	Dwarf_Error derr; +	int ret; + +	*sibp = NULL; +	ret = dwarf_siblingof(cdp->cd_dwarf, base, sibp, &derr); +	if (ret == DW_DLV_OK || ret == DW_DLV_NO_ENTRY) +		return (0); + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to sibling from die: %s\n", +	    dwarf_errmsg(derr)); +	return (ECTF_CONVBKERR); +} + +static int +ctf_dwarf_child(ctf_die_t *cdp, Dwarf_Die base, Dwarf_Die *childp) +{ +	Dwarf_Error derr; +	int ret; + +	*childp = NULL; +	ret = dwarf_child(base, childp, &derr); +	if (ret == DW_DLV_OK || ret == DW_DLV_NO_ENTRY) +		return (0); + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to child from die: %s\n", +	    dwarf_errmsg(derr)); +	return (ECTF_CONVBKERR); +} + +/* + * Compilers disagree on what to do to determine if something has global + * visiblity. Traditionally gcc has used DW_AT_external to indicate this while + * Studio has used DW_AT_visibility. We check DW_AT_visibility first and then + * fall back to DW_AT_external. Lack of DW_AT_external implies that it is not. + */ +static int +ctf_dwarf_isglobal(ctf_die_t *cdp, Dwarf_Die die, boolean_t *igp) +{ +	int ret; +	Dwarf_Signed vis; +	Dwarf_Bool ext; + +	if ((ret = ctf_dwarf_signed(cdp, die, DW_AT_visibility, &vis)) == 0) { +		*igp = vis == DW_VIS_exported; +		return (0); +	} else if (ret != ENOENT) { +		return (ret); +	} + +	if ((ret = ctf_dwarf_boolean(cdp, die, DW_AT_external, &ext)) != 0) { +		if (ret == ENOENT) { +			*igp = B_FALSE; +			return (0); +		} +		return (ret); +	} +	*igp = ext != 0 ? B_TRUE : B_FALSE; +	return (0); +} + +static int +ctf_dwarf_die_elfenc(Elf *elf, ctf_die_t *cdp, char *errbuf, size_t errlen) +{ +	GElf_Ehdr ehdr; + +	if (gelf_getehdr(elf, &ehdr) == NULL) { +		(void) snprintf(errbuf, errlen, +		    "failed to get ELF header: %s\n", +		    elf_errmsg(elf_errno())); +		return (ECTF_CONVBKERR); +	} + +	cdp->cd_mach = ehdr.e_machine; + +	if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) { +		cdp->cd_ptrsz = 4; +		VERIFY(ctf_setmodel(cdp->cd_ctfp, CTF_MODEL_ILP32) == 0); +	} else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) { +		cdp->cd_ptrsz = 8; +		VERIFY(ctf_setmodel(cdp->cd_ctfp, CTF_MODEL_LP64) == 0); +	} else { +		(void) snprintf(errbuf, errlen, +		    "unknown ELF class %d", ehdr.e_ident[EI_CLASS]); +		return (ECTF_CONVBKERR); +	} + +	if (ehdr.e_ident[EI_DATA] == ELFDATA2LSB) { +		cdp->cd_bigend = B_FALSE; +	} else if (ehdr.e_ident[EI_DATA] == ELFDATA2MSB) { +		cdp->cd_bigend = B_TRUE; +	} else { +		(void) snprintf(errbuf, errlen, +		    "unknown ELF data encoding: %d", ehdr.e_ident[EI_DATA]); +		return (ECTF_CONVBKERR); +	} + +	return (0); +} + +typedef struct ctf_dwarf_fpent { +	size_t	cdfe_size; +	uint_t	cdfe_enc[3]; +} ctf_dwarf_fpent_t; + +typedef struct ctf_dwarf_fpmap { +	uint_t			cdf_mach; +	ctf_dwarf_fpent_t	cdf_ents[4]; +} ctf_dwarf_fpmap_t; + +static const ctf_dwarf_fpmap_t ctf_dwarf_fpmaps[] = { +	{ EM_SPARC, { +		{ 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } }, +		{ 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } }, +		{ 16, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } }, +		{ 0, { 0 } } +	} }, +	{ EM_SPARC32PLUS, { +		{ 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } }, +		{ 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } }, +		{ 16, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } }, +		{ 0, { 0 } } +	} }, +	{ EM_SPARCV9, { +		{ 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } }, +		{ 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } }, +		{ 16, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } }, +		{ 0, { 0 } } +	} }, +	{ EM_386, { +		{ 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } }, +		{ 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } }, +		{ 12, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } }, +		{ 0, { 0 } } +	} }, +	{ EM_X86_64, { +		{ 4, { CTF_FP_SINGLE, CTF_FP_CPLX, CTF_FP_IMAGRY } }, +		{ 8, { CTF_FP_DOUBLE, CTF_FP_DCPLX, CTF_FP_DIMAGRY } }, +		{ 16, { CTF_FP_LDOUBLE, CTF_FP_LDCPLX, CTF_FP_LDIMAGRY } }, +		{ 0, { 0 } } +	} }, +	{ EM_NONE } +}; + +static int +ctf_dwarf_float_base(ctf_die_t *cdp, Dwarf_Signed type, ctf_encoding_t *enc) +{ +	const ctf_dwarf_fpmap_t *map = &ctf_dwarf_fpmaps[0]; +	const ctf_dwarf_fpent_t *ent; +	uint_t col = 0, mult = 1; + +	for (map = &ctf_dwarf_fpmaps[0]; map->cdf_mach != EM_NONE; map++) { +		if (map->cdf_mach == cdp->cd_mach) +			break; +	} + +	if (map->cdf_mach == EM_NONE) { +		(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +		    "Unsupported machine type: %d\n", cdp->cd_mach); +		return (ENOTSUP); +	} + +	if (type == DW_ATE_complex_float) { +		mult = 2; +		col = 1; +	} else if (type == DW_ATE_imaginary_float || +	    type == DW_ATE_SUN_imaginary_float) { +		col = 2; +	} + +	ent = &map->cdf_ents[0]; +	for (ent = &map->cdf_ents[0]; ent->cdfe_size != 0; ent++) { +		if (ent->cdfe_size * mult * 8 == enc->cte_bits) { +			enc->cte_format = ent->cdfe_enc[col]; +			return (0); +		} +	} + +	(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +	    "failed to find valid fp mapping for encoding %d, size %d bits\n", +	    type, enc->cte_bits); +	return (EINVAL); +} + +static int +ctf_dwarf_dwarf_base(ctf_die_t *cdp, Dwarf_Die die, int *kindp, +    ctf_encoding_t *enc) +{ +	int ret; +	Dwarf_Signed type; + +	if ((ret = ctf_dwarf_signed(cdp, die, DW_AT_encoding, &type)) != 0) +		return (ret); + +	switch (type) { +	case DW_ATE_unsigned: +	case DW_ATE_address: +		*kindp = CTF_K_INTEGER; +		enc->cte_format = 0; +		break; +	case DW_ATE_unsigned_char: +		*kindp = CTF_K_INTEGER; +		enc->cte_format = CTF_INT_CHAR; +		break; +	case DW_ATE_signed: +		*kindp = CTF_K_INTEGER; +		enc->cte_format = CTF_INT_SIGNED; +		break; +	case DW_ATE_signed_char: +		*kindp = CTF_K_INTEGER; +		enc->cte_format = CTF_INT_SIGNED | CTF_INT_CHAR; +		break; +	case DW_ATE_boolean: +		*kindp = CTF_K_INTEGER; +		enc->cte_format = CTF_INT_SIGNED | CTF_INT_BOOL; +		break; +	case DW_ATE_float: +	case DW_ATE_complex_float: +	case DW_ATE_imaginary_float: +	case DW_ATE_SUN_imaginary_float: +	case DW_ATE_SUN_interval_float: +		*kindp = CTF_K_FLOAT; +		if ((ret = ctf_dwarf_float_base(cdp, type, enc)) != 0) +			return (ret); +		break; +	default: +		(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +		    "encountered unkown DWARF encoding: %d", type); +		return (ECTF_CONVBKERR); +	} + +	return (0); +} + +/* + * Different compilers (at least GCC and Studio) use different names for types. + * This parses the types and attempts to unify them. If this fails, we just fall + * back to using the DWARF itself. + */ +static int +ctf_dwarf_parse_base(const char *name, int *kindp, ctf_encoding_t *enc, +    char **newnamep) +{ +	char buf[256]; +	char *base, *c; +	int nlong = 0, nshort = 0, nchar = 0, nint = 0; +	int sign = 1; + +	if (strlen(name) + 1 > sizeof (buf)) +		return (EINVAL); + +	(void) strlcpy(buf, name, sizeof (buf)); +	for (c = strtok(buf, " "); c != NULL; c = strtok(NULL, " ")) { +		if (strcmp(c, "signed") == 0) { +			sign = 1; +		} else if (strcmp(c, "unsigned") == 0) { +			sign = 0; +		} else if (strcmp(c, "long") == 0) { +			nlong++; +		} else if (strcmp(c, "char") == 0) { +			nchar++; +		} else if (strcmp(c, "short") == 0) { +			nshort++; +		} else if (strcmp(c, "int") == 0) { +			nint++; +		} else { +			/* +			 * If we don't recognize any of the tokens, we'll tell +			 * the caller to fall back to the dwarf-provided +			 * encoding information. +			 */ +			return (EINVAL); +		} +	} + +	if (nchar > 1 || nshort > 1 || nint > 1 || nlong > 2) +		return (EINVAL); + +	if (nchar > 0) { +		if (nlong > 0 || nshort > 0 || nint > 0) +			return (EINVAL); +		base = "char"; +	} else if (nshort > 0) { +		if (nlong > 0) +			return (EINVAL); +		base = "short"; +	} else if (nlong > 0) { +		base = "long"; +	} else { +		base = "int"; +	} + +	if (nchar > 0) +		enc->cte_format = CTF_INT_CHAR; +	else +		enc->cte_format = 0; + +	if (sign > 0) +		enc->cte_format |= CTF_INT_SIGNED; + +	(void) snprintf(buf, sizeof (buf), "%s%s%s", +	    (sign ? "" : "unsigned "), +	    (nlong > 1 ? "long " : ""), +	    base); + +	*newnamep = ctf_strdup(buf); +	if (*newnamep == NULL) +		return (ENOMEM); +	*kindp = CTF_K_INTEGER; +	return (0); +} + +static int +ctf_dwarf_create_base(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, int isroot, +    Dwarf_Off off) +{ +	int ret; +	char *name, *nname; +	Dwarf_Unsigned sz; +	int kind; +	ctf_encoding_t enc; +	ctf_id_t id; + +	if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0) +		return (ret); +	if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_byte_size, &sz)) != 0) { +		goto out; +	} +	ctf_dprintf("Creating base type %s from off %llu, size: %d\n", name, +	    off, sz); + +	bzero(&enc, sizeof (ctf_encoding_t)); +	enc.cte_bits = sz * 8; +	if ((ret = ctf_dwarf_parse_base(name, &kind, &enc, &nname)) == 0) { +		ctf_free(name, strlen(name) + 1); +		name = nname; +	} else { +		if (ret != EINVAL) +			return (ret); +		ctf_dprintf("falling back to dwarf for base type %s\n", name); +		if ((ret = ctf_dwarf_dwarf_base(cdp, die, &kind, &enc)) != 0) +			return (ret); +	} + +	id = ctf_add_encoded(cdp->cd_ctfp, isroot, name, &enc, kind); +	if (id == CTF_ERR) { +		ret = ctf_errno(cdp->cd_ctfp); +	} else { +		*idp = id; +		ret = ctf_dwmap_add(cdp, id, die, B_FALSE); +	} +out: +	ctf_free(name, strlen(name) + 1); +	return (ret); +} + +/* + * Getting a member's offset is a surprisingly intricate dance. It works as + * follows: + * + * 1) If we're in DWARFv4, then we either have a DW_AT_data_bit_offset or we + * have a DW_AT_data_member_location. We won't have both. Thus we check first + * for DW_AT_data_bit_offset, and if it exists, we're set. + * + * Next, if we have a bitfield and we don't ahve a DW_AT_data_bit_offset, then + * we have to grab the data location and use the following dance: + * + * 2) Gather the set of DW_AT_byte_size, DW_AT_bit_offset, and DW_AT_bit_size. + * Of course, the DW_AT_byte_size may be omitted, even though it isn't always. + * When it's been omitted, we then have to say that the size is that of the + * underlying type, which forces that to be after a ctf_update(). Here, we have + * to do different things based on whether or not we're using big endian or + * little endian to obtain the proper offset. + */ +static int +ctf_dwarf_member_offset(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t mid, +    ulong_t *offp) +{ +	int ret; +	Dwarf_Unsigned loc, bitsz, bytesz; +	Dwarf_Signed bitoff; +	size_t off, tsz; + +	if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_data_bit_offset, +	    &loc)) == 0) { +		*offp = loc; +		return (0); +	} else if (ret != ENOENT) { +		return (ret); +	} + +	if ((ret = ctf_dwarf_member_location(cdp, die, &loc)) != 0) +		return (ret); +	off = loc * 8; + +	if ((ret = ctf_dwarf_signed(cdp, die, DW_AT_bit_offset, +	    &bitoff)) != 0) { +		if (ret != ENOENT) +			return (ret); +		*offp = off; +		return (0); +	} + +	/* At this point we have to have DW_AT_bit_size */ +	if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_bit_size, &bitsz)) != 0) +		return (ret); + +	if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_byte_size, +	    &bytesz)) != 0) { +		if (ret != ENOENT) +			return (ret); +		if ((tsz = ctf_type_size(cdp->cd_ctfp, mid)) == CTF_ERR) { +			int e = ctf_errno(cdp->cd_ctfp); +			(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +			    "failed to get type size: %s", ctf_errmsg(e)); +			return (ECTF_CONVBKERR); +		} +	} else { +		tsz = bytesz; +	} +	tsz *= 8; +	if (cdp->cd_bigend == B_TRUE) { +		*offp = off + bitoff; +	} else { +		*offp = off + tsz - bitoff - bitsz; +	} + +	return (0); +} + +/* + * We need to determine if the member in question is a bitfield. If it is, then + * we need to go through and create a new type that's based on the actual base + * type, but has a different size. We also rename the type as a result to help + * deal with future collisions. + * + * Here we need to look and see if we have a DW_AT_bit_size value. If we have a + * bit size member and it does not equal the byte size member, then we need to + * create a bitfield type based on this. + * + * Note: When we support DWARFv4, there may be a chance that we ned to also + * search for the DW_AT_byte_size if we don't have a DW_AT_bit_size member. + */ +static int +ctf_dwarf_member_bitfield(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp) +{ +	int ret; +	Dwarf_Unsigned bitsz; +	ctf_encoding_t e; +	ctf_dwbitf_t *cdb; +	ctf_dtdef_t *dtd; +	ctf_id_t base = *idp; +	int kind; + +	if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_bit_size, &bitsz)) != 0) { +		if (ret == ENOENT) +			return (0); +		return (ret); +	} + +	ctf_dprintf("Trying to deal with bitfields on %d:%d\n", base, bitsz); +	/* +	 * Given that we now have a bitsize, time to go do something about it. +	 * We're going to create a new type based on the current one, but first +	 * we need to find the base type. This means we need to traverse any +	 * typedef's, consts, and volatiles until we get to what should be +	 * something of type integer or enumeration. +	 */ +	VERIFY(bitsz < UINT32_MAX); +	dtd = ctf_dtd_lookup(cdp->cd_ctfp, base); +	VERIFY(dtd != NULL); +	kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info); +	while (kind == CTF_K_TYPEDEF || kind == CTF_K_CONST || +	    kind == CTF_K_VOLATILE) { +		dtd = ctf_dtd_lookup(cdp->cd_ctfp, dtd->dtd_data.ctt_type); +		VERIFY(dtd != NULL); +		kind = CTF_INFO_KIND(dtd->dtd_data.ctt_info); +	} +	ctf_dprintf("got kind %d\n", kind); +	VERIFY(kind == CTF_K_INTEGER || kind == CTF_K_ENUM); + +	/* +	 * As surprising as it may be, it is strictly possible to create a +	 * bitfield that is based on an enum. Of course, the C standard leaves +	 * enums sizing as an ABI concern more or less. To that effect, today on +	 * all illumos platforms the size of an enum is generally that of an +	 * int as our supported data models and ABIs all agree on that. So what +	 * we'll do is fake up a CTF enconding here to use. In this case, we'll +	 * treat it as an unsigned value of whatever size the underlying enum +	 * currently has (which is in the ctt_size member of its dynamic type +	 * data). +	 */ +	if (kind == CTF_K_INTEGER) { +		e = dtd->dtd_u.dtu_enc; +	} else { +		bzero(&e, sizeof (ctf_encoding_t)); +		e.cte_bits = dtd->dtd_data.ctt_size * NBBY; +	} + +	for (cdb = ctf_list_next(&cdp->cd_bitfields); cdb != NULL; +	    cdb = ctf_list_next(cdb)) { +		if (cdb->cdb_base == base && cdb->cdb_nbits == bitsz) +			break; +	} + +	/* +	 * Create a new type if none exists. We name all types in a way that is +	 * guaranteed not to conflict with the corresponding C type. We do this +	 * by using the ':' operator. +	 */ +	if (cdb == NULL) { +		size_t namesz; +		char *name; + +		e.cte_bits = bitsz; +		namesz = snprintf(NULL, 0, "%s:%d", dtd->dtd_name, +		    (uint32_t)bitsz); +		name = ctf_alloc(namesz + 1); +		if (name == NULL) +			return (ENOMEM); +		cdb = ctf_alloc(sizeof (ctf_dwbitf_t)); +		if (cdb == NULL) { +			ctf_free(name, namesz + 1); +			return (ENOMEM); +		} +		(void) snprintf(name, namesz + 1, "%s:%d", dtd->dtd_name, +		    (uint32_t)bitsz); + +		cdb->cdb_base = base; +		cdb->cdb_nbits = bitsz; +		cdb->cdb_id = ctf_add_integer(cdp->cd_ctfp, CTF_ADD_NONROOT, +		    name, &e); +		if (cdb->cdb_id == CTF_ERR) { +			(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +			    "failed to get add bitfield type %s: %s", name, +			    ctf_errmsg(ctf_errno(cdp->cd_ctfp))); +			ctf_free(name, namesz + 1); +			ctf_free(cdb, sizeof (ctf_dwbitf_t)); +			return (ECTF_CONVBKERR); +		} +		ctf_free(name, namesz + 1); +		ctf_list_append(&cdp->cd_bitfields, cdb); +	} + +	*idp = cdb->cdb_id; + +	return (0); +} + +static int +ctf_dwarf_fixup_sou(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t base, boolean_t add) +{ +	int ret, kind; +	Dwarf_Die child, memb; +	Dwarf_Unsigned size; +	ulong_t nsz; + +	kind = ctf_type_kind(cdp->cd_ctfp, base); +	VERIFY(kind != CTF_ERR); +	VERIFY(kind == CTF_K_STRUCT || kind == CTF_K_UNION); + +	/* +	 * Members are in children. However, gcc also allows empty ones. +	 */ +	if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0) +		return (ret); +	if (child == NULL) +		return (0); + +	memb = child; +	while (memb != NULL) { +		Dwarf_Die sib, tdie; +		Dwarf_Half tag; +		ctf_id_t mid; +		char *mname; +		ulong_t memboff = 0; + +		if ((ret = ctf_dwarf_tag(cdp, memb, &tag)) != 0) +			return (ret); + +		if (tag != DW_TAG_member) +			continue; + +		if ((ret = ctf_dwarf_refdie(cdp, memb, DW_AT_type, &tdie)) != 0) +			return (ret); + +		if ((ret = ctf_dwarf_convert_type(cdp, tdie, &mid, +		    CTF_ADD_NONROOT)) != 0) +			return (ret); +		ctf_dprintf("Got back type id: %d\n", mid); + +		/* +		 * If we're not adding a member, just go ahead and return. +		 */ +		if (add == B_FALSE) { +			if ((ret = ctf_dwarf_member_bitfield(cdp, memb, +			    &mid)) != 0) +				return (ret); +			goto next; +		} + +		if ((ret = ctf_dwarf_string(cdp, memb, DW_AT_name, +		    &mname)) != 0 && ret != ENOENT) +			return (ret); +		if (ret == ENOENT) +			mname = NULL; + +		if (kind == CTF_K_UNION) { +			memboff = 0; +		} else if ((ret = ctf_dwarf_member_offset(cdp, memb, mid, +		    &memboff)) != 0) { +			if (mname != NULL) +				ctf_free(mname, strlen(mname) + 1); +			return (ret); +		} + +		if ((ret = ctf_dwarf_member_bitfield(cdp, memb, &mid)) != 0) +			return (ret); + +		ret = ctf_add_member(cdp->cd_ctfp, base, mname, mid, memboff); +		if (ret == CTF_ERR) { +			(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +			    "failed to add member %s: %s", +			    mname, ctf_errmsg(ctf_errno(cdp->cd_ctfp))); +			if (mname != NULL) +				ctf_free(mname, strlen(mname) + 1); +			return (ECTF_CONVBKERR); +		} + +		if (mname != NULL) +			ctf_free(mname, strlen(mname) + 1); + +next: +		if ((ret = ctf_dwarf_sib(cdp, memb, &sib)) != 0) +			return (ret); +		memb = sib; +	} + +	/* +	 * If we're not adding members, then we don't know the final size of the +	 * structure, so end here. +	 */ +	if (add == B_FALSE) +		return (0); + +	/* Finally set the size of the structure to the actual byte size */ +	if ((ret = ctf_dwarf_unsigned(cdp, die, DW_AT_byte_size, &size)) != 0) +		return (ret); +	nsz = size; +	if ((ctf_set_size(cdp->cd_ctfp, base, nsz)) == CTF_ERR) { +		int e = ctf_errno(cdp->cd_ctfp); +		(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +		    "failed to set type size for %d to 0x%x: %s", base, +		    (uint32_t)size, ctf_errmsg(e)); +		return (ECTF_CONVBKERR); +	} + +	return (0); +} + +static int +ctf_dwarf_create_sou(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, +    int kind, int isroot) +{ +	int ret; +	char *name; +	ctf_id_t base; +	Dwarf_Die child; +	Dwarf_Bool decl; + +	/* +	 * Deal with the terribly annoying case of anonymous structs and unions. +	 * If they don't have a name, set the name to the empty string. +	 */ +	if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0 && +	    ret != ENOENT) +		return (ret); +	if (ret == ENOENT) +		name = NULL; + +	/* +	 * We need to check if we just have a declaration here. If we do, then +	 * instead of creating an actual structure or union, we're just going to +	 * go ahead and create a forward. During a dedup or merge, the forward +	 * will be replaced with the real thing. +	 */ +	if ((ret = ctf_dwarf_boolean(cdp, die, DW_AT_declaration, +	    &decl)) != 0) { +		if (ret != ENOENT) +			return (ret); +		decl = 0; +	} + +	if (decl != 0) { +		base = ctf_add_forward(cdp->cd_ctfp, isroot, name, kind); +	} else if (kind == CTF_K_STRUCT) { +		base = ctf_add_struct(cdp->cd_ctfp, isroot, name); +	} else { +		base = ctf_add_union(cdp->cd_ctfp, isroot, name); +	} +	ctf_dprintf("added sou %s (%d) (%d)\n", name, kind, base); +	if (name != NULL) +		ctf_free(name, strlen(name) + 1); +	if (base == CTF_ERR) +		return (ctf_errno(cdp->cd_ctfp)); +	*idp = base; + +	/* +	 * If it's just a declaration, we're not going to mark it for fix up or +	 * do anything else. +	 */ +	if (decl == B_TRUE) +		return (ctf_dwmap_add(cdp, base, die, B_FALSE)); +	if ((ret = ctf_dwmap_add(cdp, base, die, B_TRUE)) != 0) +		return (ret); + +	/* +	 * Members are in children. However, gcc also allows empty ones. +	 */ +	if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0) +		return (ret); +	if (child == NULL) +		return (0); + +	return (0); +} + +static int +ctf_dwarf_create_array_range(ctf_die_t *cdp, Dwarf_Die range, ctf_id_t *idp, +    ctf_id_t base, int isroot) +{ +	int ret; +	Dwarf_Die sib; +	Dwarf_Unsigned val; +	Dwarf_Signed sval; +	ctf_arinfo_t ar; + +	ctf_dprintf("creating array range\n"); + +	if ((ret = ctf_dwarf_sib(cdp, range, &sib)) != 0) +		return (ret); +	if (sib != NULL) { +		ctf_id_t id; +		if ((ret = ctf_dwarf_create_array_range(cdp, sib, &id, +		    base, CTF_ADD_NONROOT)) != 0) +			return (ret); +		ar.ctr_contents = id; +	} else { +		ar.ctr_contents = base; +	} + +	if ((ar.ctr_index = ctf_dwarf_long(cdp)) == CTF_ERR) +		return (ctf_errno(cdp->cd_ctfp)); + +	/* +	 * Array bounds can be signed or unsigned, but there are several kinds +	 * of signless forms (data1, data2, etc) that take their sign from the +	 * routine that is trying to interpret them.  That is, data1 can be +	 * either signed or unsigned, depending on whether you use the signed or +	 * unsigned accessor function.  GCC will use the signless forms to store +	 * unsigned values which have their high bit set, so we need to try to +	 * read them first as unsigned to get positive values.  We could also +	 * try signed first, falling back to unsigned if we got a negative +	 * value. +	 */ +	if ((ret = ctf_dwarf_unsigned(cdp, range, DW_AT_upper_bound, +	    &val)) == 0) { +		ar.ctr_nelems = val + 1; +	} else if (ret != ENOENT) { +		return (ret); +	} else if ((ret = ctf_dwarf_signed(cdp, range, DW_AT_upper_bound, +	    &sval)) == 0) { +		ar.ctr_nelems = sval + 1; +	} else if (ret != ENOENT) { +		return (ret); +	} else { +		ar.ctr_nelems = 0; +	} + +	if ((*idp = ctf_add_array(cdp->cd_ctfp, isroot, &ar)) == CTF_ERR) +		return (ctf_errno(cdp->cd_ctfp)); + +	return (0); +} + +/* + * Try and create an array type. First, the kind of the array is specified in + * the DW_AT_type entry. Next, the number of entries is stored in a more + * complicated form, we should have a child that has the DW_TAG_subrange type. + */ +static int +ctf_dwarf_create_array(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, int isroot) +{ +	int ret; +	Dwarf_Die tdie, rdie; +	ctf_id_t tid; +	Dwarf_Half rtag; +	ctf_arinfo_t ar; + +	if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &tdie)) != 0) +		return (ret); +	if ((ret = ctf_dwarf_convert_type(cdp, tdie, &tid, +	    CTF_ADD_NONROOT)) != 0) +		return (ret); + +	ar.ctr_contents = tid; + +	if ((ret = ctf_dwarf_child(cdp, die, &rdie)) != 0) +		return (ret); +	if ((ret = ctf_dwarf_tag(cdp, rdie, &rtag)) != 0) +		return (ret); +	if (rtag != DW_TAG_subrange_type) { +		(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +		    "encountered array without DW_TAG_subrange_type child\n"); +		return (ECTF_CONVBKERR); +	} + +	/* +	 * The compiler may opt to describe a multi-dimensional array as one +	 * giant array or it may opt to instead encode it as a series of +	 * subranges. If it's the latter, then for each subrange we introduce a +	 * type. We can always use the base type. +	 */ +	if ((ret = ctf_dwarf_create_array_range(cdp, rdie, idp, tid, +	    isroot)) != 0) +		return (ret); +	ctf_dprintf("Got back id %d\n", *idp); +	return (ctf_dwmap_add(cdp, *idp, die, B_FALSE)); +} + +static int +ctf_dwarf_create_reference(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, +    int kind, int isroot) +{ +	int ret; +	ctf_id_t id; +	Dwarf_Die tdie; +	char *name; +	size_t namelen; + +	if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0 && +	    ret != ENOENT) +		return (ret); +	if (ret == ENOENT) { +		name = NULL; +		namelen = 0; +	} else { +		namelen = strlen(name); +	} + +	ctf_dprintf("reference kind %d %s\n", kind, name != NULL ? name : "<>"); + +	if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &tdie)) != 0) { +		if (ret != ENOENT) { +			ctf_free(name, namelen); +			return (ret); +		} +		if ((id = ctf_dwarf_void(cdp)) == CTF_ERR) { +			ctf_free(name, namelen); +			return (ctf_errno(cdp->cd_ctfp)); +		} +	} else { +		if ((ret = ctf_dwarf_convert_type(cdp, tdie, &id, +		    CTF_ADD_NONROOT)) != 0) { +			ctf_free(name, namelen); +			return (ret); +		} +	} + +	if ((*idp = ctf_add_reftype(cdp->cd_ctfp, isroot, name, id, kind)) == +	    CTF_ERR) { +		ctf_free(name, namelen); +		return (ctf_errno(cdp->cd_ctfp)); +	} + +	ctf_free(name, namelen); +	return (ctf_dwmap_add(cdp, *idp, die, B_FALSE)); +} + +static int +ctf_dwarf_create_enum(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, int isroot) +{ +	int ret; +	ctf_id_t id; +	Dwarf_Die child; +	char *name; + +	if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0 && +	    ret != ENOENT) +		return (ret); +	if (ret == ENOENT) +		name = NULL; +	id = ctf_add_enum(cdp->cd_ctfp, isroot, name); +	ctf_dprintf("added enum %s (%d)\n", name, id); +	if (name != NULL) +		ctf_free(name, strlen(name) + 1); +	if (id == CTF_ERR) +		return (ctf_errno(cdp->cd_ctfp)); +	*idp = id; +	if ((ret = ctf_dwmap_add(cdp, id, die, B_FALSE)) != 0) +		return (ret); + + +	if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0) { +		if (ret == ENOENT) +			ret = 0; +		return (ret); +	} + +	while (child != NULL) { +		Dwarf_Half tag; +		Dwarf_Signed sval; +		Dwarf_Unsigned uval; +		Dwarf_Die arg = child; +		int eval; + +		if ((ret = ctf_dwarf_sib(cdp, arg, &child)) != 0) +			return (ret); + +		if ((ret = ctf_dwarf_tag(cdp, arg, &tag)) != 0) +			return (ret); + +		if (tag != DW_TAG_enumerator) { +			if ((ret = ctf_dwarf_convert_type(cdp, arg, NULL, +			    CTF_ADD_NONROOT)) != 0) +				return (ret); +			continue; +		} + +		if ((ret = ctf_dwarf_signed(cdp, arg, DW_AT_const_value, +		    &sval)) == 0) { +			eval = sval; +		} else if (ret != ENOENT) { +			return (ret); +		} else if ((ret = ctf_dwarf_unsigned(cdp, arg, +		    DW_AT_const_value, &uval)) == 0) { +			eval = (int)uval; +		} else { +			(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +			    "encountered enumration without constant value\n"); +			return (ECTF_CONVBKERR); +		} + +		/* +		 * DWARF v4 section 5.7 tells us we'll always have names. +		 */ +		if ((ret = ctf_dwarf_string(cdp, arg, DW_AT_name, +		    &name)) != 0) +			return (ret); + +		ret = ctf_add_enumerator(cdp->cd_ctfp, id, name, eval); +		if (ret == CTF_ERR) { +			(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +			    "failed to add enumarator %s (%d) to %d\n", +			    name, eval, id); +			ctf_free(name, strlen(name) + 1); +			return (ctf_errno(cdp->cd_ctfp)); +		} +		ctf_free(name, strlen(name) + 1); +	} + +	return (0); +} + +/* + * For a function pointer, walk over and process all of its children, unless we + * encounter one that's just a declaration. In which case, we error on it. + */ +static int +ctf_dwarf_create_fptr(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, int isroot) +{ +	int ret; +	Dwarf_Bool b; +	ctf_funcinfo_t fi; +	Dwarf_Die retdie; +	ctf_id_t *argv = NULL; + +	bzero(&fi, sizeof (ctf_funcinfo_t)); + +	if ((ret = ctf_dwarf_boolean(cdp, die, DW_AT_declaration, &b)) != 0) { +		if (ret != ENOENT) +			return (ret); +	} else { +		if (b != 0) +			return (EPROTOTYPE); +	} + +	/* +	 * Return type is in DW_AT_type, if none, it returns void. +	 */ +	if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &retdie)) != 0) { +		if (ret != ENOENT) +			return (ret); +		if ((fi.ctc_return = ctf_dwarf_void(cdp)) == CTF_ERR) +			return (ctf_errno(cdp->cd_ctfp)); +	} else { +		if ((ret = ctf_dwarf_convert_type(cdp, retdie, &fi.ctc_return, +		    CTF_ADD_NONROOT)) != 0) +			return (ret); +	} + +	if ((ret = ctf_dwarf_function_count(cdp, die, &fi, B_TRUE)) != 0) { +		return (ret); +	} + +	if (fi.ctc_argc != 0) { +		argv = ctf_alloc(sizeof (ctf_id_t) * fi.ctc_argc); +		if (argv == NULL) +			return (ENOMEM); + +		if ((ret = ctf_dwarf_convert_fargs(cdp, die, &fi, argv)) != 0) { +			ctf_free(argv, sizeof (ctf_id_t) * fi.ctc_argc); +			return (ret); +		} +	} + +	if ((*idp = ctf_add_funcptr(cdp->cd_ctfp, isroot, &fi, argv)) == +	    CTF_ERR) { +		ctf_free(argv, sizeof (ctf_id_t) * fi.ctc_argc); +		return (ctf_errno(cdp->cd_ctfp)); +	} + +	ctf_free(argv, sizeof (ctf_id_t) * fi.ctc_argc); +	return (ctf_dwmap_add(cdp, *idp, die, B_FALSE)); +} + +static int +ctf_dwarf_convert_type(ctf_die_t *cdp, Dwarf_Die die, ctf_id_t *idp, +    int isroot) +{ +	int ret; +	Dwarf_Off offset; +	Dwarf_Half tag; +	ctf_dwmap_t lookup, *map; +	ctf_id_t id; + +	if (idp == NULL) +		idp = &id; + +	if ((ret = ctf_dwarf_offset(cdp, die, &offset)) != 0) +		return (ret); + +	if (offset > cdp->cd_maxoff) { +		(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +		    "die offset %llu beyond maximum for header %llu\n", +		    offset, cdp->cd_maxoff); +		return (ECTF_CONVBKERR); +	} + +	/* +	 * If we've already added an entry for this offset, then we're done. +	 */ +	lookup.cdm_off = offset; +	if ((map = avl_find(&cdp->cd_map, &lookup, NULL)) != NULL) { +		*idp = map->cdm_id; +		return (0); +	} + +	if ((ret = ctf_dwarf_tag(cdp, die, &tag)) != 0) +		return (ret); + +	ret = ENOTSUP; +	switch (tag) { +	case DW_TAG_base_type: +		ctf_dprintf("base\n"); +		ret = ctf_dwarf_create_base(cdp, die, idp, isroot, offset); +		break; +	case DW_TAG_array_type: +		ctf_dprintf("array\n"); +		ret = ctf_dwarf_create_array(cdp, die, idp, isroot); +		break; +	case DW_TAG_enumeration_type: +		ctf_dprintf("enum\n"); +		ret = ctf_dwarf_create_enum(cdp, die, idp, isroot); +		break; +	case DW_TAG_pointer_type: +		ctf_dprintf("pointer\n"); +		ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_POINTER, +		    isroot); +		break; +	case DW_TAG_structure_type: +		ctf_dprintf("struct\n"); +		ret = ctf_dwarf_create_sou(cdp, die, idp, CTF_K_STRUCT, +		    isroot); +		break; +	case DW_TAG_subroutine_type: +		ctf_dprintf("fptr\n"); +		ret = ctf_dwarf_create_fptr(cdp, die, idp, isroot); +		break; +	case DW_TAG_typedef: +		ctf_dprintf("typedef\n"); +		ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_TYPEDEF, +		    isroot); +		break; +	case DW_TAG_union_type: +		ctf_dprintf("union\n"); +		ret = ctf_dwarf_create_sou(cdp, die, idp, CTF_K_UNION, +		    isroot); +		break; +	case DW_TAG_const_type: +		ctf_dprintf("const\n"); +		ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_CONST, +		    isroot); +		break; +	case DW_TAG_volatile_type: +		ctf_dprintf("volatile\n"); +		ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_VOLATILE, +		    isroot); +		break; +	case DW_TAG_restrict_type: +		ctf_dprintf("restrict\n"); +		ret = ctf_dwarf_create_reference(cdp, die, idp, CTF_K_RESTRICT, +		    isroot); +		break; +	default: +		ctf_dprintf("ignoring tag type %x\n", tag); +		ret = 0; +		break; +	} +	ctf_dprintf("ctf_dwarf_convert_type tag specific handler returned %d\n", +	    ret); + +	return (ret); +} + +static int +ctf_dwarf_walk_lexical(ctf_die_t *cdp, Dwarf_Die die) +{ +	int ret; +	Dwarf_Die child; + +	if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0) +		return (ret); + +	if (child == NULL) +		return (0); + +	return (ctf_dwarf_convert_die(cdp, die)); +} + +static int +ctf_dwarf_function_count(ctf_die_t *cdp, Dwarf_Die die, ctf_funcinfo_t *fip, +    boolean_t fptr) +{ +	int ret; +	Dwarf_Die child, sib, arg; + +	if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0) +		return (ret); + +	arg = child; +	while (arg != NULL) { +		Dwarf_Half tag; + +		if ((ret = ctf_dwarf_tag(cdp, arg, &tag)) != 0) +			return (ret); + +		/* +		 * We have to check for a varargs type decleration. This will +		 * happen in one of two ways. If we have a function pointer +		 * type, then it'll be done with a tag of type +		 * DW_TAG_unspecified_parameters. However, it only means we have +		 * a variable number of arguments, if we have more than one +		 * argument found so far. Otherwise, when we have a function +		 * type, it instead uses a formal parameter whose name is '...' +		 * to indicate a variable arguments member. +		 * +		 * Also, if we have a function pointer, then we have to expect +		 * that we might not get a name at all. +		 */ +		if (tag == DW_TAG_formal_parameter && fptr == B_FALSE) { +			char *name; +			if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, +			    &name)) != 0) +				return (ret); +			if (strcmp(name, DWARF_VARARGS_NAME) == 0) +				fip->ctc_flags |= CTF_FUNC_VARARG; +			else +				fip->ctc_argc++; +			ctf_free(name, strlen(name) + 1); +		} else if (tag == DW_TAG_formal_parameter) { +			fip->ctc_argc++; +		} else if (tag == DW_TAG_unspecified_parameters && +		    fip->ctc_argc > 0) { +			fip->ctc_flags |= CTF_FUNC_VARARG; +		} +		if ((ret = ctf_dwarf_sib(cdp, arg, &sib)) != 0) +			return (ret); +		arg = sib; +	} + +	return (0); +} + +static int +ctf_dwarf_convert_fargs(ctf_die_t *cdp, Dwarf_Die die, ctf_funcinfo_t *fip, +    ctf_id_t *argv) +{ +	int ret; +	int i = 0; +	Dwarf_Die child, sib, arg; + +	if ((ret = ctf_dwarf_child(cdp, die, &child)) != 0) +		return (ret); + +	arg = child; +	while (arg != NULL) { +		Dwarf_Half tag; + +		if ((ret = ctf_dwarf_tag(cdp, arg, &tag)) != 0) +			return (ret); +		if (tag == DW_TAG_formal_parameter) { +			Dwarf_Die tdie; + +			if ((ret = ctf_dwarf_refdie(cdp, arg, DW_AT_type, +			    &tdie)) != 0) +				return (ret); + +			if ((ret = ctf_dwarf_convert_type(cdp, tdie, &argv[i], +			    CTF_ADD_ROOT)) != 0) +				return (ret); +			i++; + +			/* +			 * Once we hit argc entries, we're done. This ensures we +			 * don't accidentally hit a varargs which should be the +			 * least entry. +			 */ +			if (i == fip->ctc_argc) +				break; +		} + +		if ((ret = ctf_dwarf_sib(cdp, arg, &sib)) != 0) +			return (ret); +		arg = sib; +	} + +	return (0); +} + +static int +ctf_dwarf_convert_function(ctf_die_t *cdp, Dwarf_Die die) +{ +	int ret; +	char *name; +	ctf_dwfunc_t *cdf; +	Dwarf_Die tdie; + +	/* +	 * Functions that don't have a name are generally functions that have +	 * been inlined and thus most information about them has been lost. If +	 * we can't get a name, then instead of returning ENOENT, we silently +	 * swallow the error. +	 */ +	if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0) { +		if (ret == ENOENT) +			return (0); +		return (ret); +	} + +	ctf_dprintf("beginning work on function %s\n", name); +	if ((cdf = ctf_alloc(sizeof (ctf_dwfunc_t))) == NULL) { +		ctf_free(name, strlen(name) + 1); +		return (ENOMEM); +	} +	bzero(cdf, sizeof (ctf_dwfunc_t)); +	cdf->cdf_name = name; + +	if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &tdie)) == 0) { +		if ((ret = ctf_dwarf_convert_type(cdp, tdie, +		    &(cdf->cdf_fip.ctc_return), CTF_ADD_ROOT)) != 0) { +			ctf_free(name, strlen(name) + 1); +			ctf_free(cdf, sizeof (ctf_dwfunc_t)); +			return (ret); +		} +	} else if (ret != ENOENT) { +		ctf_free(name, strlen(name) + 1); +		ctf_free(cdf, sizeof (ctf_dwfunc_t)); +		return (ret); +	} else { +		if ((cdf->cdf_fip.ctc_return = ctf_dwarf_void(cdp)) == +		    CTF_ERR) { +			ctf_free(name, strlen(name) + 1); +			ctf_free(cdf, sizeof (ctf_dwfunc_t)); +			return (ctf_errno(cdp->cd_ctfp)); +		} +	} + +	/* +	 * A function has a number of children, some of which may not be ones we +	 * care about. Children that we care about have a type of +	 * DW_TAG_formal_parameter. We're going to do two passes, the first to +	 * count the arguments, the second to process them. Afterwards, we +	 * should be good to go ahead and add this function. +	 * +	 * Note, we already got the return type by going in and grabbing it out +	 * of the DW_AT_type. +	 */ +	if ((ret = ctf_dwarf_function_count(cdp, die, &cdf->cdf_fip, +	    B_FALSE)) != 0) { +		ctf_free(name, strlen(name) + 1); +		ctf_free(cdf, sizeof (ctf_dwfunc_t)); +		return (ret); +	} + +	ctf_dprintf("beginning to convert function arguments %s\n", name); +	if (cdf->cdf_fip.ctc_argc != 0) { +		uint_t argc = cdf->cdf_fip.ctc_argc; +		cdf->cdf_argv = ctf_alloc(sizeof (ctf_id_t) * argc); +		if (cdf->cdf_argv == NULL) { +			ctf_free(name, strlen(name) + 1); +			ctf_free(cdf, sizeof (ctf_dwfunc_t)); +			return (ENOMEM); +		} +		if ((ret = ctf_dwarf_convert_fargs(cdp, die, +		    &cdf->cdf_fip, cdf->cdf_argv)) != 0) { +			ctf_free(cdf->cdf_argv, sizeof (ctf_id_t) * argc); +			ctf_free(name, strlen(name) + 1); +			ctf_free(cdf, sizeof (ctf_dwfunc_t)); +			return (ret); +		} +	} else { +		cdf->cdf_argv = NULL; +	} + +	if ((ret = ctf_dwarf_isglobal(cdp, die, &cdf->cdf_global)) != 0) { +		ctf_free(cdf->cdf_argv, sizeof (ctf_id_t) * +		    cdf->cdf_fip.ctc_argc); +		ctf_free(name, strlen(name) + 1); +		ctf_free(cdf, sizeof (ctf_dwfunc_t)); +		return (ret); +	} + +	ctf_list_append(&cdp->cd_funcs, cdf); +	return (ret); +} + +/* + * Convert variables, but only if they're not prototypes and have names. + */ +static int +ctf_dwarf_convert_variable(ctf_die_t *cdp, Dwarf_Die die) +{ +	int ret; +	char *name; +	Dwarf_Bool b; +	Dwarf_Die tdie; +	ctf_id_t id; +	ctf_dwvar_t *cdv; + +	if ((ret = ctf_dwarf_boolean(cdp, die, DW_AT_declaration, &b)) != 0) { +		if (ret != ENOENT) +			return (ret); +	} else if (b != 0) { +		return (0); +	} + +	if ((ret = ctf_dwarf_string(cdp, die, DW_AT_name, &name)) != 0 && +	    ret != ENOENT) +		return (ret); +	if (ret == ENOENT) +		return (0); + +	if ((ret = ctf_dwarf_refdie(cdp, die, DW_AT_type, &tdie)) != 0) { +		ctf_free(name, strlen(name) + 1); +		return (ret); +	} + +	if ((ret = ctf_dwarf_convert_type(cdp, tdie, &id, +	    CTF_ADD_ROOT)) != 0) +		return (ret); + +	if ((cdv = ctf_alloc(sizeof (ctf_dwvar_t))) == NULL) { +		ctf_free(name, strlen(name) + 1); +		return (ENOMEM); +	} + +	cdv->cdv_name = name; +	cdv->cdv_type = id; + +	if ((ret = ctf_dwarf_isglobal(cdp, die, &cdv->cdv_global)) != 0) { +		ctf_free(cdv, sizeof (ctf_dwvar_t)); +		ctf_free(name, strlen(name) + 1); +		return (ret); +	} + +	ctf_list_append(&cdp->cd_vars, cdv); +	return (0); +} + +/* + * Walk through our set of top-level types and process them. + */ +static int +ctf_dwarf_walk_toplevel(ctf_die_t *cdp, Dwarf_Die die) +{ +	int ret; +	Dwarf_Off offset; +	Dwarf_Half tag; + +	if ((ret = ctf_dwarf_offset(cdp, die, &offset)) != 0) +		return (ret); + +	if (offset > cdp->cd_maxoff) { +		(void) snprintf(cdp->cd_errbuf, cdp->cd_errlen, +		    "die offset %llu beyond maximum for header %llu\n", +		    offset, cdp->cd_maxoff); +		return (ECTF_CONVBKERR); +	} + +	if ((ret = ctf_dwarf_tag(cdp, die, &tag)) != 0) +		return (ret); + +	ret = 0; +	switch (tag) { +	case DW_TAG_subprogram: +		ctf_dprintf("top level func\n"); +		ret = ctf_dwarf_convert_function(cdp, die); +		break; +	case DW_TAG_variable: +		ctf_dprintf("top level var\n"); +		ret = ctf_dwarf_convert_variable(cdp, die); +		break; +	case DW_TAG_lexical_block: +		ctf_dprintf("top level block\n"); +		ret = ctf_dwarf_walk_lexical(cdp, die); +		break; +	case DW_TAG_enumeration_type: +	case DW_TAG_structure_type: +	case DW_TAG_typedef: +	case DW_TAG_union_type: +		ctf_dprintf("top level type\n"); +		ret = ctf_dwarf_convert_type(cdp, die, NULL, B_TRUE); +		break; +	default: +		break; +	} + +	return (ret); +} + + +/* + * We're given a node. At this node we need to convert it and then proceed to + * convert any siblings that are associaed with this die. + */ +static int +ctf_dwarf_convert_die(ctf_die_t *cdp, Dwarf_Die die) +{ +	while (die != NULL) { +		int ret; +		Dwarf_Die sib; + +		if ((ret = ctf_dwarf_walk_toplevel(cdp, die)) != 0) +			return (ret); + +		if ((ret = ctf_dwarf_sib(cdp, die, &sib)) != 0) +			return (ret); +		die = sib; +	} +	return (0); +} + +static int +ctf_dwarf_fixup_die(ctf_die_t *cdp, boolean_t addpass) +{ +	ctf_dwmap_t *map; + +	for (map = avl_first(&cdp->cd_map); map != NULL; +	    map = AVL_NEXT(&cdp->cd_map, map)) { +		int ret; +		if (map->cdm_fix == B_FALSE) +			continue; +		if ((ret = ctf_dwarf_fixup_sou(cdp, map->cdm_die, map->cdm_id, +		    addpass)) != 0) +			return (ret); +	} + +	return (0); +} + +static ctf_dwfunc_t * +ctf_dwarf_match_func(ctf_die_t *cdp, const char *file, const char *name, +    int bind) +{ +	ctf_dwfunc_t *cdf; + +	if (bind == STB_WEAK) +		return (NULL); + +	/* Nothing we can do if we can't find a name to compare it to. */ +	if (bind == STB_LOCAL && (file == NULL || cdp->cd_name == NULL)) +		return (NULL); + +	for (cdf = ctf_list_next(&cdp->cd_funcs); cdf != NULL; +	    cdf = ctf_list_next(cdf)) { +		if (bind == STB_GLOBAL && cdf->cdf_global == B_FALSE) +			continue; +		if (bind == STB_LOCAL && cdf->cdf_global == B_TRUE) +			continue; +		if (strcmp(name, cdf->cdf_name) != 0) +			continue; +		if (bind == STB_LOCAL && strcmp(file, cdp->cd_name) != 0) +			continue; +		return (cdf); +	} + +	return (NULL); +} +static ctf_dwvar_t * +ctf_dwarf_match_var(ctf_die_t *cdp, const char *file, const char *name, +    int bind) +{ +	ctf_dwvar_t *cdv; + +	/* Nothing we can do if we can't find a name to compare it to. */ +	if (bind == STB_LOCAL && (file == NULL || cdp->cd_name == NULL)) +		return (NULL); +	ctf_dprintf("Still considering %s\n", name); + +	for (cdv = ctf_list_next(&cdp->cd_vars); cdv != NULL; +	    cdv = ctf_list_next(cdv)) { +		if (bind == STB_GLOBAL && cdv->cdv_global == B_FALSE) +			continue; +		if (bind == STB_LOCAL && cdv->cdv_global == B_TRUE) +			continue; +		if (strcmp(name, cdv->cdv_name) != 0) +			continue; +		if (bind == STB_LOCAL && strcmp(file, cdp->cd_name) != 0) +			continue; +		return (cdv); +	} + +	return (NULL); +} + +static int +ctf_dwarf_symtab_iter(ctf_die_t *cdp, ctf_dwarf_symtab_f *func, void *arg) +{ +	int ret; +	ulong_t i; +	ctf_file_t *fp = cdp->cd_ctfp; +	const char *file = NULL; +	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; +		int type; +		GElf_Sym gsym; +		const GElf_Sym *gsymp; + +		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 (type == STT_FILE) { +				file = (char *)(strbase + symp->st_name); +				continue; +			} +			if (type != STT_OBJECT && type != 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); +			gsym.st_name = symp->st_name; +			gsym.st_value = symp->st_value; +			gsym.st_size = symp->st_size; +			gsym.st_info = symp->st_info; +			gsym.st_other = symp->st_other; +			gsym.st_shndx = symp->st_shndx; +			gsymp = &gsym; +		} else { +			const Elf64_Sym *symp = (Elf64_Sym *)symbase + i; +			type = ELF64_ST_TYPE(symp->st_info); +			if (type == STT_FILE) { +				file = (char *)(strbase + symp->st_name); +				continue; +			} +			if (type != STT_OBJECT && type != 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); +			gsymp = symp; +		} + +		ret = func(cdp, gsymp, i, file, name, arg); +		if (ret != 0) +			return (ret); +	} + +	return (0); +} + +static int +ctf_dwarf_conv_funcvars_cb(ctf_die_t *cdp, const GElf_Sym *symp, ulong_t idx, +    const char *file, const char *name, void *arg) +{ +	int ret, bind, type; + +	bind = GELF_ST_BIND(symp->st_info); +	type = GELF_ST_TYPE(symp->st_info); + +	/* +	 * Come back to weak symbols in another pass +	 */ +	if (bind == STB_WEAK) +		return (0); + +	if (type == STT_OBJECT) { +		ctf_dwvar_t *cdv = ctf_dwarf_match_var(cdp, file, name, +		    bind); +		ctf_dprintf("match for %s (%d): %p\n", name, idx, cdv); +		if (cdv == NULL) +			return (0); +		ret = ctf_add_object(cdp->cd_ctfp, idx, cdv->cdv_type); +		ctf_dprintf("added object %s\n", name); +	} else { +		ctf_dwfunc_t *cdf = ctf_dwarf_match_func(cdp, file, name, +		    bind); +		if (cdf == NULL) +			return (0); +		ret = ctf_add_function(cdp->cd_ctfp, idx, &cdf->cdf_fip, +		    cdf->cdf_argv); +	} + +	if (ret == CTF_ERR) { +		return (ctf_errno(cdp->cd_ctfp)); +	} + +	return (0); +} + +static int +ctf_dwarf_conv_funcvars(ctf_die_t *cdp) +{ +	return (ctf_dwarf_symtab_iter(cdp, ctf_dwarf_conv_funcvars_cb, NULL)); +} + +/* + * Note, this comment comes from the original version of the CTF tools. + * + * If we have a weak symbol, attempt to find the strong symbol it will + * resolve to.  Note: the code where this actually happens is in + * sym_process() in cmd/sgs/libld/common/syms.c + * + * Finding the matching symbol is unfortunately not trivial.  For a + * symbol to be a candidate, it must: + * + * - have the same type (function, object) + * - have the same value (address) + * - have the same size + * - not be another weak symbol + * - belong to the same section (checked via section index) + * + * If such a candidate is global, then we assume we've found it.  The + * linker generates the symbol table such that the curfile might be + * incorrect; this is OK for global symbols, since find_iidesc() doesn't + * need to check for the source file for the symbol. + * + * We might have found a strong local symbol, where the curfile is + * accurate and matches that of the weak symbol.  We assume this is a + * reasonable match. + * + * If we've got a local symbol with a non-matching curfile, there are + * two possibilities.  Either this is a completely different symbol, or + * it's a once-global symbol that was scoped to local via a mapfile.  In + * the latter case, curfile is likely inaccurate since the linker does + * not preserve the needed curfile in the order of the symbol table (see + * the comments about locally scoped symbols in libld's update_osym()). + * As we can't tell this case from the former one, we use this symbol + * iff no other matching symbol is found. + * + * What we really need here is a SUNW section containing weak<->strong + * mappings that we can consume. + */ +typedef struct ctf_dwarf_weak_arg { +	const GElf_Sym *cweak_symp; +	const char *cweak_file; +	boolean_t cweak_candidate; +	ulong_t cweak_idx; +} ctf_dwarf_weak_arg_t; + +static int +ctf_dwarf_conv_check_weak(ctf_die_t *cdp, const GElf_Sym *symp, +    ulong_t idx, const char *file, const char *name, void *arg) +{ +	ctf_dwarf_weak_arg_t *cweak = arg; +	const GElf_Sym *wsymp = cweak->cweak_symp; + +	ctf_dprintf("comparing weak to %s\n", name); + +	if (GELF_ST_BIND(symp->st_info) == STB_WEAK) { +		return (0); +	} + +	if (GELF_ST_TYPE(wsymp->st_info) != GELF_ST_TYPE(symp->st_info)) { +		return (0); +	} + +	if (wsymp->st_value != symp->st_value) { +		return (0); +	} + +	if (wsymp->st_size != symp->st_size) { +		return (0); +	} + +	if (wsymp->st_shndx != symp->st_shndx) { +		return (0); +	} + +	/* +	 * Check if it's a weak candidate. +	 */ +	if (GELF_ST_BIND(symp->st_info) == STB_LOCAL && +	    (file == NULL || cweak->cweak_file == NULL || +	    strcmp(file, cweak->cweak_file) != 0)) { +		cweak->cweak_candidate = B_TRUE; +		cweak->cweak_idx = idx; +		return (0); +	} + +	/* +	 * Found a match, break. +	 */ +	cweak->cweak_idx = idx; +	return (1); +} + +static int +ctf_dwarf_duplicate_sym(ctf_die_t *cdp, ulong_t idx, ulong_t matchidx) +{ +	ctf_id_t id = ctf_lookup_by_symbol(cdp->cd_ctfp, matchidx); + +	/* +	 * If we matched something that for some reason didn't have type data, +	 * we don't consider that a fatal error and silently swallow it. +	 */ +	if (id == CTF_ERR) { +		if (ctf_errno(cdp->cd_ctfp) == ECTF_NOTYPEDAT) +			return (0); +		else +			return (ctf_errno(cdp->cd_ctfp)); +	} + +	if (ctf_add_object(cdp->cd_ctfp, idx, id) == CTF_ERR) +		return (ctf_errno(cdp->cd_ctfp)); + +	return (0); +} + +static int +ctf_dwarf_duplicate_func(ctf_die_t *cdp, ulong_t idx, ulong_t matchidx) +{ +	int ret; +	ctf_funcinfo_t fip; +	ctf_id_t *args = NULL; + +	if (ctf_func_info(cdp->cd_ctfp, matchidx, &fip) == CTF_ERR) { +		if (ctf_errno(cdp->cd_ctfp) == ECTF_NOFUNCDAT) +			return (0); +		else +			return (ctf_errno(cdp->cd_ctfp)); +	} + +	if (fip.ctc_argc != 0) { +		args = ctf_alloc(sizeof (ctf_id_t) * fip.ctc_argc); +		if (args == NULL) +			return (ENOMEM); + +		if (ctf_func_args(cdp->cd_ctfp, matchidx, fip.ctc_argc, args) == +		    CTF_ERR) { +			ctf_free(args, sizeof (ctf_id_t) * fip.ctc_argc); +			return (ctf_errno(cdp->cd_ctfp)); +		} +	} + +	ret = ctf_add_function(cdp->cd_ctfp, idx, &fip, args); +	if (args != NULL) +		ctf_free(args, sizeof (ctf_id_t) * fip.ctc_argc); +	if (ret == CTF_ERR) +		return (ctf_errno(cdp->cd_ctfp)); + +	return (0); +} + +static int +ctf_dwarf_conv_weaks_cb(ctf_die_t *cdp, const GElf_Sym *symp, +    ulong_t idx, const char *file, const char *name, void *arg) +{ +	int ret, type; +	ctf_dwarf_weak_arg_t cweak; + +	/* +	 * We only care about weak symbols. +	 */ +	if (GELF_ST_BIND(symp->st_info) != STB_WEAK) +		return (0); + +	type = GELF_ST_TYPE(symp->st_info); +	ASSERT(type == STT_OBJECT || type == STT_FUNC); + +	/* +	 * For each weak symbol we encounter, we need to do a second iteration +	 * to try and find a match. We should probably think about other +	 * techniques to try and save us time in the future. +	 */ +	cweak.cweak_symp = symp; +	cweak.cweak_file = file; +	cweak.cweak_candidate = B_FALSE; +	cweak.cweak_idx = 0; + +	ctf_dprintf("Trying to find weak equiv for %s\n", name); + +	ret = ctf_dwarf_symtab_iter(cdp, ctf_dwarf_conv_check_weak, &cweak); +	VERIFY(ret == 0 || ret == 1); + +	/* +	 * Nothing was ever found, we're not going to add anything for this +	 * entry. +	 */ +	if (ret == 0 && cweak.cweak_candidate == B_FALSE) { +		ctf_dprintf("found no weak match for %s\n", name); +		return (0); +	} + +	/* +	 * Now, finally go and add the type based on the match. +	 */ +	if (type == STT_OBJECT) { +		ret = ctf_dwarf_duplicate_sym(cdp, idx, cweak.cweak_idx); +	} else { +		ret = ctf_dwarf_duplicate_func(cdp, idx, cweak.cweak_idx); +	} + +	return (ret); +} + +static int +ctf_dwarf_conv_weaks(ctf_die_t *cdp) +{ +	return (ctf_dwarf_symtab_iter(cdp, ctf_dwarf_conv_weaks_cb, NULL)); +} + +/* ARGSUSED */ +static int +ctf_dwarf_convert_one(void *arg, void *unused) +{ +	int ret; +	ctf_file_t *dedup; +	ctf_die_t *cdp = arg; + +	ctf_dprintf("converting die: %s\n", cdp->cd_name); +	ctf_dprintf("max offset: %x\n", cdp->cd_maxoff); +	VERIFY(cdp != NULL); + +	ret = ctf_dwarf_convert_die(cdp, cdp->cd_cu); +	ctf_dprintf("ctf_dwarf_convert_die (%s) returned %d\n", cdp->cd_name, +	    ret); +	if (ret != 0) { +		return (ret); +	} +	if (ctf_update(cdp->cd_ctfp) != 0) { +		return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0, +		    "failed to update output ctf container")); +	} + +	ret = ctf_dwarf_fixup_die(cdp, B_FALSE); +	ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cdp->cd_name, +	    ret); +	if (ret != 0) { +		return (ret); +	} +	if (ctf_update(cdp->cd_ctfp) != 0) { +		return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0, +		    "failed to update output ctf container")); +	} + +	ret = ctf_dwarf_fixup_die(cdp, B_TRUE); +	ctf_dprintf("ctf_dwarf_fixup_die (%s) returned %d\n", cdp->cd_name, +	    ret); +	if (ret != 0) { +		return (ret); +	} +	if (ctf_update(cdp->cd_ctfp) != 0) { +		return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0, +		    "failed to update output ctf container")); +	} + + +	if ((ret = ctf_dwarf_conv_funcvars(cdp)) != 0) { +		return (ctf_dwarf_error(cdp, NULL, ret, +		    "failed to convert strong functions and variables")); +	} + +	if (ctf_update(cdp->cd_ctfp) != 0) { +		return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0, +		    "failed to update output ctf container")); +	} + +	if (cdp->cd_doweaks == B_TRUE) { +		if ((ret = ctf_dwarf_conv_weaks(cdp)) != 0) { +			return (ctf_dwarf_error(cdp, NULL, ret, +			    "failed to convert weak functions and variables")); +		} + +		if (ctf_update(cdp->cd_ctfp) != 0) { +			return (ctf_dwarf_error(cdp, cdp->cd_ctfp, 0, +			    "failed to update output ctf container")); +		} +	} + +	ctf_phase_dump(cdp->cd_ctfp, "pre-dedup"); +	ctf_dprintf("adding inputs for dedup\n"); +	if ((ret = ctf_merge_add(cdp->cd_cmh, cdp->cd_ctfp)) != 0) { +		return (ctf_dwarf_error(cdp, NULL, ret, +		    "failed to add inputs for merge")); +	} + +	ctf_dprintf("starting merge\n"); +	if ((ret = ctf_merge_dedup(cdp->cd_cmh, &dedup)) != 0) { +		return (ctf_dwarf_error(cdp, NULL, ret, +		    "failed to deduplicate die")); +	} +	ctf_close(cdp->cd_ctfp); +	cdp->cd_ctfp = dedup; + +	return (0); +} + +/* + * Note, we expect that if we're returning a ctf_file_t from one of the dies, + * say in the single node case, it's been saved and the entry here has been set + * to NULL, which ctf_close happily ignores. + */ +static void +ctf_dwarf_free_die(ctf_die_t *cdp) +{ +	ctf_dwfunc_t *cdf, *ndf; +	ctf_dwvar_t *cdv, *ndv; +	ctf_dwbitf_t *cdb, *ndb; +	ctf_dwmap_t *map; +	void *cookie; +	Dwarf_Error derr; + +	ctf_dprintf("Beginning to free die: %p\n", cdp); +	cdp->cd_elf = NULL; +	ctf_dprintf("Trying to free name: %p\n", cdp->cd_name); +	if (cdp->cd_name != NULL) +		ctf_free(cdp->cd_name, strlen(cdp->cd_name) + 1); +	ctf_dprintf("Trying to free merge handle: %p\n", cdp->cd_cmh); +	if (cdp->cd_cmh != NULL) { +		ctf_merge_fini(cdp->cd_cmh); +		cdp->cd_cmh = NULL; +	} + +	ctf_dprintf("Trying to free functions\n"); +	for (cdf = ctf_list_next(&cdp->cd_funcs); cdf != NULL; cdf = ndf) { +		ndf = ctf_list_next(cdf); +		ctf_free(cdf->cdf_name, strlen(cdf->cdf_name) + 1); +		if (cdf->cdf_fip.ctc_argc != 0) { +			ctf_free(cdf->cdf_argv, +			    sizeof (ctf_id_t) * cdf->cdf_fip.ctc_argc); +		} +		ctf_free(cdf, sizeof (ctf_dwfunc_t)); +	} + +	ctf_dprintf("Trying to free variables\n"); +	for (cdv = ctf_list_next(&cdp->cd_vars); cdv != NULL; cdv = ndv) { +		ndv = ctf_list_next(cdv); +		ctf_free(cdv->cdv_name, strlen(cdv->cdv_name) + 1); +		ctf_free(cdv, sizeof (ctf_dwvar_t)); +	} + +	ctf_dprintf("Trying to free bitfields\n"); +	for (cdb = ctf_list_next(&cdp->cd_bitfields); cdb != NULL; cdb = ndb) { +		ndb = ctf_list_next(cdb); +		ctf_free(cdb, sizeof (ctf_dwbitf_t)); +	} + +	/* How do we clean up die usage? */ +	ctf_dprintf("Trying to clean up dwarf_t: %p\n", cdp->cd_dwarf); +	(void) dwarf_finish(cdp->cd_dwarf, &derr); +	cdp->cd_dwarf = NULL; +	ctf_close(cdp->cd_ctfp); + +	cookie = NULL; +	while ((map = avl_destroy_nodes(&cdp->cd_map, &cookie)) != NULL) { +		ctf_free(map, sizeof (ctf_dwmap_t)); +	} +	avl_destroy(&cdp->cd_map); +	cdp->cd_errbuf = NULL; +} + +static void +ctf_dwarf_free_dies(ctf_die_t *cdies, int ndies) +{ +	int i; + +	ctf_dprintf("Beginning to free dies\n"); +	for (i = 0; i < ndies; i++) { +		ctf_dwarf_free_die(&cdies[i]); +	} + +	ctf_free(cdies, sizeof (ctf_die_t) * ndies); +} + +static int +ctf_dwarf_count_dies(Dwarf_Debug dw, Dwarf_Error *derr, int *ndies, +    char *errbuf, size_t errlen) +{ +	int ret; +	Dwarf_Half vers; +	Dwarf_Unsigned nexthdr; + +	while ((ret = dwarf_next_cu_header(dw, NULL, &vers, NULL, NULL, +	    &nexthdr, derr)) != DW_DLV_NO_ENTRY) { +		if (ret != DW_DLV_OK) { +			(void) snprintf(errbuf, errlen, +			    "file does not contain valid DWARF data: %s\n", +			    dwarf_errmsg(*derr)); +			return (ECTF_CONVBKERR); +		} + +		if (vers != DWARF_VERSION_TWO) { +			(void) snprintf(errbuf, errlen, +			    "unsupported DWARF version: %d\n", vers); +			return (ECTF_CONVBKERR); +		} +		*ndies = *ndies + 1; +	} + +	if (*ndies == 0) { +		(void) snprintf(errbuf, errlen, +		    "file does not contain valid DWARF data: %s\n", +		    dwarf_errmsg(*derr)); +		return (ECTF_CONVBKERR); +	} + +	return (0); +} + +/* + * Iterate over all of the dies and create a ctf_die_t for each of them. This is + * used to determine if we have zero, one, or multiple dies to convert. If we + * have zero, that's an error. If there's only one die, that's the simple case. + * No merge needed and only a single Dwarf_Debug as well. + */ +static int +ctf_dwarf_init_die(int fd, Elf *elf, ctf_die_t *cdp, int ndie, char *errbuf, +    size_t errlen) +{ +	int ret; +	Dwarf_Unsigned hdrlen, abboff, nexthdr; +	Dwarf_Half addrsz; +	Dwarf_Unsigned offset = 0; +	Dwarf_Error derr; + +	while ((ret = dwarf_next_cu_header(cdp->cd_dwarf, &hdrlen, NULL, +	    &abboff, &addrsz, &nexthdr, &derr)) != DW_DLV_NO_ENTRY) { +		char *name; +		Dwarf_Die cu, child; + +		/* Based on the counting above, we should be good to go */ +		VERIFY(ret == DW_DLV_OK); +		if (ndie > 0) { +			ndie--; +			offset = nexthdr; +			continue; +		} + +		/* +		 * Compilers are apparently inconsistent. Some emit no DWARF for +		 * empty files and others emit empty compilation unit. +		 */ +		cdp->cd_voidtid = CTF_ERR; +		cdp->cd_longtid = CTF_ERR; +		cdp->cd_elf = elf; +		cdp->cd_maxoff = nexthdr - 1; +		cdp->cd_ctfp = ctf_fdcreate(fd, &ret); +		if (cdp->cd_ctfp == NULL) { +			ctf_free(cdp, sizeof (ctf_die_t)); +			return (ret); +		} +		avl_create(&cdp->cd_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t), +		    offsetof(ctf_dwmap_t, cdm_avl)); +		cdp->cd_errbuf = errbuf; +		cdp->cd_errlen = errlen; +		bzero(&cdp->cd_vars, sizeof (ctf_list_t)); +		bzero(&cdp->cd_funcs, sizeof (ctf_list_t)); +		bzero(&cdp->cd_bitfields, sizeof (ctf_list_t)); + +		if ((ret = ctf_dwarf_die_elfenc(elf, cdp, errbuf, +		    errlen)) != 0) { +			avl_destroy(&cdp->cd_map); +			ctf_free(cdp, sizeof (ctf_die_t)); +			return (ret); +		} + +		if ((ret = ctf_dwarf_sib(cdp, NULL, &cu)) != 0) { +			avl_destroy(&cdp->cd_map); +			ctf_free(cdp, sizeof (ctf_die_t)); +			return (ret); +		} +		if (cu == NULL) { +			(void) snprintf(errbuf, errlen, +			    "file does not contain DWARF data\n"); +			avl_destroy(&cdp->cd_map); +			ctf_free(cdp, sizeof (ctf_die_t)); +			return (ECTF_CONVBKERR); +		} + +		if ((ret = ctf_dwarf_child(cdp, cu, &child)) != 0) { +			avl_destroy(&cdp->cd_map); +			ctf_free(cdp, sizeof (ctf_die_t)); +			return (ret); +		} +		if (child == NULL) { +			(void) snprintf(errbuf, errlen, +			    "file does not contain DWARF data\n"); +			avl_destroy(&cdp->cd_map); +			ctf_free(cdp, sizeof (ctf_die_t)); +			return (ECTF_CONVBKERR); +		} + +		cdp->cd_cuoff = offset; +		cdp->cd_cu = child; + +		if ((cdp->cd_cmh = ctf_merge_init(fd, &ret)) == NULL) { +			avl_destroy(&cdp->cd_map); +			ctf_free(cdp, sizeof (ctf_die_t)); +			return (ret); +		} + +		if (ctf_dwarf_string(cdp, cu, DW_AT_name, &name) == 0) { +			size_t len = strlen(name) + 1; +			char *b = basename(name); +			cdp->cd_name = strdup(b); +			ctf_free(name, len); +		} +		break; +	} + +	return (0); +} + + +ctf_conv_status_t +ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, int *errp, ctf_file_t **fpp, +    char *errmsg, size_t errlen) +{ +	int err, ret, ndies, i; +	Dwarf_Debug dw; +	Dwarf_Error derr; +	ctf_die_t *cdies = NULL, *cdp; +	workq_t *wqp = NULL; + +	if (errp == NULL) +		errp = &err; +	*errp = 0; +	*fpp = NULL; + +	ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dw, &derr); +	if (ret != DW_DLV_OK) { +		/* +		 * The old CTF tools used to check if we expected DWARF data +		 * here. In this case, if we actually have some amount of DWARF, +		 * but no section, for now, just go ahead and create an empty +		 * CTF file. +		 */ +		if (ret == DW_DLV_NO_ENTRY || +		    dwarf_errno(derr) == DW_DLE_DEBUG_INFO_NULL) { +			*fpp = ctf_create(errp); +			return (*fpp != NULL ? CTF_CONV_SUCCESS : +			    CTF_CONV_ERROR); +		} +		(void) snprintf(errmsg, errlen, +		    "failed to initialize DWARF: %s\n", +		    dwarf_errmsg(derr)); +		*errp = ECTF_CONVBKERR; +		return (CTF_CONV_ERROR); +	} + +	ndies = 0; +	ret = ctf_dwarf_count_dies(dw, &derr, &ndies, errmsg, errlen); +	if (ret != 0) { +		*errp = ret; +		goto out; +	} + +	(void) dwarf_finish(dw, &derr); +	cdies = ctf_alloc(sizeof (ctf_die_t) * ndies); +	if (cdies == NULL) { +		*errp = ENOMEM; +		return (CTF_CONV_ERROR); +	} + +	for (i = 0; i < ndies; i++) { +		cdp = &cdies[i]; +		ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, +		    &cdp->cd_dwarf, &derr); +		if (ret != 0) { +			ctf_free(cdies, sizeof (ctf_die_t) * ndies); +			(void) snprintf(errmsg, errlen, +			    "failed to initialize DWARF: %s\n", +			    dwarf_errmsg(derr)); +			*errp = ECTF_CONVBKERR; +			return (CTF_CONV_ERROR); +		} + +		ret = ctf_dwarf_init_die(fd, elf, &cdies[i], i, errmsg, errlen); +		if (ret != 0) { +			*errp = ret; +			goto out; +		} +		cdp->cd_doweaks = ndies > 1 ? B_FALSE : B_TRUE; +	} + +	ctf_dprintf("found %d DWARF die(s)\n", ndies); + +	/* +	 * If we only have one die, there's no reason to use multiple threads, +	 * even if the user requested them. After all, they just gave us an +	 * upper bound. +	 */ +	if (ndies == 1) +		nthrs = 1; + +	if (workq_init(&wqp, nthrs) == -1) { +		*errp = errno; +		goto out; +	} + +	for (i = 0; i < ndies; i++) { +		cdp = &cdies[i]; +		ctf_dprintf("adding die %s: %p, %x %x\n", cdp->cd_name, +		    cdp->cd_cu, cdp->cd_cuoff, cdp->cd_maxoff); +		if (workq_add(wqp, cdp) == -1) { +			*errp = errno; +			goto out; +		} +	} + +	ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, errp); +	if (ret == WORKQ_ERROR) { +		*errp = errno; +		goto out; +	} else if (ret == WORKQ_UERROR) { +		ctf_dprintf("internal convert failed: %s\n", +		    ctf_errmsg(*errp)); +		goto out; +	} + +	ctf_dprintf("Determining next phase: have %d dies\n", ndies); +	if (ndies != 1) { +		ctf_merge_t *cmp; + +		cmp = ctf_merge_init(fd, &ret); +		if (cmp == NULL) { +			*errp = ret; +			goto out; +		} + +		ctf_dprintf("setting threads\n"); +		if ((ret = ctf_merge_set_nthreads(cmp, nthrs)) != 0) { +			ctf_merge_fini(cmp); +			*errp = ret; +			goto out; +		} + +		ctf_dprintf("adding dies\n"); +		for (i = 0; i < ndies; i++) { +			cdp = &cdies[i]; +			if ((ret = ctf_merge_add(cmp, cdp->cd_ctfp)) != 0) { +				ctf_merge_fini(cmp); +				*errp = ret; +				goto out; +			} +		} + +		ctf_dprintf("performing merge\n"); +		ret = ctf_merge_merge(cmp, fpp); +		if (ret != 0) { +			ctf_dprintf("failed merge!\n"); +			*fpp = NULL; +			ctf_merge_fini(cmp); +			*errp = ret; +			goto out; +		} +		ctf_merge_fini(cmp); +		*errp = 0; +		ctf_dprintf("successfully converted!\n"); +	} else { +		*errp = 0; +		*fpp = cdies->cd_ctfp; +		cdies->cd_ctfp = NULL; +		ctf_dprintf("successfully converted!\n"); +	} + +out: +	workq_fini(wqp); +	ctf_dwarf_free_dies(cdies, ndies); +	return (*fpp != NULL ? CTF_CONV_SUCCESS : CTF_CONV_ERROR); +} 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..4d7c10aeec --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_elfwrite.c @@ -0,0 +1,422 @@ +/* + * 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 <libctf_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> + +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 = NULL; +	int srcidx, dstidx, pad, i; +	int curnmoff = 0; +	int changing = 0; +	int ret; +	size_t nshdr, nphdr, strndx; +	void *strdatabuf = NULL, *symdatabuf = NULL; +	size_t strdatasz = 0, symdatasz = 0; + +	void *cdata = NULL; +	size_t elfsize, asize; + +	if ((flags & ~(CTF_ELFWRITE_F_COMPRESS)) != 0) { +		ret = ctf_set_errno(fp, EINVAL); +		goto out; +	} + +	if (gelf_newehdr(dst, gelf_getclass(src)) == NULL) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} +	if (gelf_getehdr(src, &sehdr) == NULL) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} +	(void) memcpy(&dehdr, &sehdr, sizeof (GElf_Ehdr)); +	if (gelf_update_ehdr(dst, &dehdr) == 0) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} + +	/* +	 * 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) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} +	if (elf_getshdrnum(src, &nshdr) != 0) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} +	if (elf_getshdrstrndx(src, &strndx) != 0) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} + +	/* +	 * 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) { +			ret = ctf_set_errno(fp, ECTF_ELF); +			goto out; +		} + +		for (i = 0; i < nphdr; i++) { +			GElf_Phdr phdr; + +			if (gelf_getphdr(src, i, &phdr) == NULL) { +				ret = ctf_set_errno(fp, ECTF_ELF); +				goto out; +			} +			if (gelf_update_phdr(dst, i, &phdr) == 0) { +				ret = ctf_set_errno(fp, ECTF_ELF); +				goto out; +			} +		} +	} + +	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) { +			ret = ctf_set_errno(fp, ECTF_ELF); +			goto out; +		} +		sname = elf_strptr(src, strndx, shdr.sh_name); +		if (sname == NULL) { +			ret = ctf_set_errno(fp, ECTF_ELF); +			goto out; +		} + +		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) { +			ret = ctf_set_errno(fp, ECTF_ELF); +			goto out; +		} + +		if (secxlate[srcidx] == -1) { +			changing = 1; +			continue; +		} + +		dscn = elf_newscn(dst); +		if (dscn == NULL) { +			ret = ctf_set_errno(fp, ECTF_ELF); +			goto out; +		} + +		/* +		 * 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) { +			ret = ctf_set_errno(fp, ECTF_ELF); +			goto out; +		} +		if ((sdata = elf_getdata(sscn, NULL)) == NULL) { +			ret = ctf_set_errno(fp, ECTF_ELF); +			goto out; +		} +		if ((ddata = elf_newdata(dscn)) == NULL) { +			ret = ctf_set_errno(fp, ECTF_ELF); +			goto out; +		} +		bcopy(sdata, ddata, sizeof (Elf_Data)); + +		if (srcidx == strndx) { +			char seclen = strlen(CTF_ELF_SCN_NAME); + +			strdatasz = ddata->d_size + shdr.sh_size + +			    seclen + 1; +			ddata->d_buf = strdatabuf = ctf_alloc(strdatasz); +			if (ddata->d_buf == NULL) { +				ret = ctf_set_errno(fp, ECTF_ELF); +				goto out; +			} +			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]; + +			symdatasz = shdr.sh_size; +			ddata->d_buf = symdatabuf = ctf_alloc(symdatasz); +			if (ddata->d_buf == NULL) { +				ret = ctf_set_errno(fp, ECTF_ELF); +				goto out; +			} +			(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) { +						ret = ctf_set_errno(fp, +						    ECTF_ELF); +						goto out; +					} +				} +			} +		} + +		if (gelf_update_shdr(dscn, &shdr) == NULL) { +			ret = ctf_set_errno(fp, ECTF_ELF); +			goto out; +		} + +		new_offset = (off_t)shdr.sh_offset; +		if (shdr.sh_type != SHT_NOBITS) +			new_offset += shdr.sh_size; +	} + +	if (symtab_idx == -1) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} + +	/* Add the ctf section */ +	if ((dscn = elf_newscn(dst)) == NULL) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} +	if (gelf_getshdr(dscn, &shdr) == NULL) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} +	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) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} + +	if (compress != 0) { +		int err; + +		if (ctf_zopen(&err) == NULL) { +			ret = ctf_set_errno(fp, err); +			goto out; +		} + +		if ((err = ctf_compress(fp, &cdata, &asize, &elfsize)) != 0) { +			ret = ctf_set_errno(fp, err); +			goto out; +		} +		ddata->d_buf = cdata; +		ddata->d_size = elfsize; +	} 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) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} + +	/* 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) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} +	if (elf_update(dst, ELF_C_WRITE) < 0) { +		ret = ctf_set_errno(fp, ECTF_ELF); +		goto out; +	} + +	ret = 0; + +out: +	if (strdatabuf != NULL) +		ctf_free(strdatabuf, strdatasz); +	if (symdatabuf != NULL) +		ctf_free(symdatabuf, symdatasz); +	if (cdata != NULL) +		ctf_data_free(cdata, fp->ctf_size); +	if (secxlate != NULL) +		ctf_free(secxlate, sizeof (int) * nshdr); + +	return (ret); +} + +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..6b637ba663 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,9 @@  #include <errno.h>  #include <dlfcn.h>  #include <gelf.h> +#include <zlib.h> +#include <zone.h> +#include <sys/debug.h>  #ifdef _LP64  static const char *_libctf_zlib = "/usr/lib/64/libz.so.1"; @@ -42,6 +48,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; @@ -49,6 +58,18 @@ static struct {  static size_t _PAGESIZE;  static size_t _PAGEMASK; +static uint64_t ctf_phase = 0; + +#define	CTF_COMPRESS_CHUNK	(64*1024) + +typedef struct ctf_zdata { +	void		*czd_buf; +	void		*czd_next; +	ctf_file_t	*czd_ctfp; +	size_t		czd_allocsz; +	z_stream	czd_zstr; +} ctf_zdata_t; +  #pragma init(_libctf_init)  void  _libctf_init(void) @@ -72,21 +93,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 +158,207 @@ z_strerror(int err)  	return (zlib.z_error(err));  } +static int +ctf_zdata_init(ctf_zdata_t *czd, ctf_file_t *fp) +{ +	int err; +	ctf_header_t *cthp; + +	bzero(czd, sizeof (ctf_zdata_t)); + +	czd->czd_allocsz = fp->ctf_size; +	czd->czd_buf = ctf_data_alloc(czd->czd_allocsz); +	if (czd->czd_buf == MAP_FAILED) +		return (ctf_set_errno(fp, ENOMEM)); + +	bcopy(fp->ctf_base, czd->czd_buf, sizeof (ctf_header_t)); +	czd->czd_ctfp = fp; +	cthp = czd->czd_buf; +	cthp->cth_flags |= CTF_F_COMPRESS; +	czd->czd_next = (void *)((uintptr_t)czd->czd_buf + +	    sizeof (ctf_header_t)); + +	if ((err = zlib.z_initcomp(&czd->czd_zstr, Z_BEST_COMPRESSION, +	    ZLIB_VERSION, sizeof (z_stream))) != Z_OK) +		return (ctf_set_errno(fp, ECTF_ZLIB)); + +	return (0); +} + +static int +ctf_zdata_grow(ctf_zdata_t *czd) +{ +	size_t off; +	size_t newsz; +	void *ndata; + +	off = (uintptr_t)czd->czd_next - (uintptr_t)czd->czd_buf; +	newsz = czd->czd_allocsz + CTF_COMPRESS_CHUNK; +	ndata = ctf_data_alloc(newsz); +	if (ndata == MAP_FAILED) { +		return (ctf_set_errno(czd->czd_ctfp, ENOMEM)); +	} + +	bcopy(czd->czd_buf, ndata, off); +	ctf_data_free(czd->czd_buf, czd->czd_allocsz); +	czd->czd_allocsz = newsz; +	czd->czd_buf = ndata; +	czd->czd_next = (void *)((uintptr_t)ndata + off); + +	czd->czd_zstr.next_out = (Bytef *)czd->czd_next; +	czd->czd_zstr.avail_out = CTF_COMPRESS_CHUNK; +	return (0); +} + +static int +ctf_zdata_compress_buffer(ctf_zdata_t *czd, const void *buf, size_t bufsize) +{ +	int err; + +	czd->czd_zstr.next_out = czd->czd_next; +	czd->czd_zstr.avail_out = czd->czd_allocsz - +	    (czd->czd_next - czd->czd_buf); +	czd->czd_zstr.next_in = (Bytef *)buf; +	czd->czd_zstr.avail_in = bufsize; + +	while (czd->czd_zstr.avail_in != 0) { +		if (czd->czd_zstr.avail_out == 0) { +			czd->czd_next = czd->czd_zstr.next_out; +			if ((err = ctf_zdata_grow(czd)) != 0) { +				return (err); +			} +		} + +		if ((err = zlib.z_compress(&czd->czd_zstr, Z_NO_FLUSH)) != Z_OK) +			return (ctf_set_errno(czd->czd_ctfp, ECTF_ZLIB)); +	} +	czd->czd_next = czd->czd_zstr.next_out; + +	return (0); +} + +static int +ctf_zdata_flush(ctf_zdata_t *czd, boolean_t finish) +{ +	int err; +	int flag = finish == B_TRUE ? Z_FINISH : Z_FULL_FLUSH; +	int bret = finish == B_TRUE ? Z_STREAM_END : Z_BUF_ERROR; + +	for (;;) { +		if (czd->czd_zstr.avail_out == 0) { +			czd->czd_next = czd->czd_zstr.next_out; +			if ((err = ctf_zdata_grow(czd)) != 0) { +				return (err); +			} +		} + +		err = zlib.z_compress(&czd->czd_zstr, flag); +		if (err == bret) { +			break; +		} +		if (err != Z_OK) +			return (ctf_set_errno(czd->czd_ctfp, ECTF_ZLIB)); + +	} + +	czd->czd_next = czd->czd_zstr.next_out; + +	return (0); +} + +static int +ctf_zdata_end(ctf_zdata_t *czd) +{ +	int ret; + +	if ((ret = ctf_zdata_flush(czd, B_TRUE)) != 0) +		return (ret); + +	if ((ret = zlib.z_finicomp(&czd->czd_zstr)) != 0) +		return (ctf_set_errno(czd->czd_ctfp, ECTF_ZLIB)); + +	return (0); +} + +static void +ctf_zdata_cleanup(ctf_zdata_t *czd) +{ +	ctf_data_free(czd->czd_buf, czd->czd_allocsz); +	(void) zlib.z_finicomp(&czd->czd_zstr); +} + +/* + * Compress our CTF data and return both the size of the compressed data and the + * size of the allocation. These may be different due to the nature of + * compression. + * + * In addition, we flush the compression inbetween our two phases such that we + * maintain a different dictionary bbetween the CTF data and the string section. + */ +int +ctf_compress(ctf_file_t *fp, void **buf, size_t *allocsz, size_t *elfsize) +{ +	int err; +	ctf_zdata_t czd; +	ctf_header_t *cthp = (ctf_header_t *)fp->ctf_base; + +	if ((err = ctf_zdata_init(&czd, fp)) != 0) +		return (err); + +	if ((err = ctf_zdata_compress_buffer(&czd, fp->ctf_buf, +	    cthp->cth_stroff)) != 0) { +		ctf_zdata_cleanup(&czd); +		return (err); +	} + +	if ((err = ctf_zdata_flush(&czd, B_FALSE)) != 0) { +		ctf_zdata_cleanup(&czd); +		return (err); +	} + +	if ((err = ctf_zdata_compress_buffer(&czd, +	    fp->ctf_buf + cthp->cth_stroff, cthp->cth_strlen)) != 0) { +		ctf_zdata_cleanup(&czd); +		return (err); +	} + +	if ((err = ctf_zdata_end(&czd)) != 0) { +		ctf_zdata_cleanup(&czd); +		return (err); +	} + +	*buf = czd.czd_buf; +	*allocsz = czd.czd_allocsz; +	*elfsize = (uintptr_t)(czd.czd_next - czd.czd_buf); + +	return (0); +} + +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 +437,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 +469,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 +621,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 +649,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 +674,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 +693,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 @@ -502,3 +765,25 @@ ctf_version(int version)  	return (_libctf_version);  } + +/* + * A utility function for folks debugging CTF conversion and merging. + */ +void +ctf_phase_dump(ctf_file_t *fp, const char *phase) +{ +	int fd; +	static char *base; +	char path[MAXPATHLEN]; + +	if (base == NULL && (base = getenv("LIBCTF_WRITE_PHASES")) == NULL) +		return; + +	(void) snprintf(path, sizeof (path), "%s/libctf.%s.%d.ctf", base, +	    phase != NULL ? phase : "", +	    ctf_phase); +	if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0777)) < 0) +		return; +	(void) ctf_write(fp, fd); +	(void) close(fd); +} 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..f23dbc232d --- /dev/null +++ b/usr/src/lib/libctf/common/ctf_merge.c @@ -0,0 +1,1570 @@ +/* + * 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 <libctf_impl.h> +#include <sys/debug.h> +#include <sys/list.h> +#include <stddef.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <mergeq.h> +#include <errno.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 */ +	boolean_t cm_dedup;		/* Are we doing a dedup? */ +	boolean_t cm_unique;		/* are we doing a uniquify? */ +} 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; +	boolean_t cmi_created; +} ctf_merge_input_t; + +struct ctf_merge_handle { +	list_t cmh_inputs;		/* Input list */ +	uint_t cmh_ninputs;		/* Number of inputs */ +	uint_t cmh_nthreads;		/* Number of threads to use */ +	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 ctf_id_t +ctf_merge_gettype(ctf_merge_types_t *cmp, ctf_id_t id) +{ +	if (cmp->cm_dedup == B_FALSE) { +		VERIFY(cmp->cm_tmap[id].cmt_map != 0); +		return (cmp->cm_tmap[id].cmt_map); +	} + +	while (cmp->cm_tmap[id].cmt_missing == B_FALSE) { +		VERIFY(cmp->cm_tmap[id].cmt_map != 0); +		id = cmp->cm_tmap[id].cmt_map; +	} +	VERIFY(cmp->cm_tmap[id].cmt_map != 0); +	return (cmp->cm_tmap[id].cmt_map); +} + +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); + +			/* +			 * If we're uniquifying types, it's possible for the +			 * container that we're uniquifying against to have a +			 * forward which exists in the container being reduced. +			 * For example, genunix has the machcpu structure as a +			 * forward which is actually in unix and we uniquify +			 * unix against genunix. In such cases, we explicitly do +			 * not do any mapping of the forward information, lest +			 * we risk losing the real definition. Instead, mark +			 * that it's missing. +			 */ +			if (cmp->cm_unique == B_TRUE) { +				cmt[oid].cmt_missing = B_TRUE; +				return; +			} + +			cmt[oid].cmt_map = iid; +			cmt[oid].cmt_forward = B_TRUE; +			ctf_dprintf("merge diff forward mapped %d->%d\n", oid, +			    iid); +			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; +		ctf_dprintf("merge diff mapped %d->%d\n", oid, iid); +	} else if (ifp == cmp->cm_src) { +		VERIFY(cmt[iid].cmt_map == 0); +		cmt[iid].cmt_missing = B_TRUE; +		ctf_dprintf("merge diff said %d is missing\n", iid); +	} +} + +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 = ctf_merge_gettype(cmp, ar.ctr_contents); + +	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 = ctf_merge_gettype(cmp, ar.ctr_index); + +	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 = ctf_merge_gettype(cmp, reftype); + +	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 = ctf_merge_gettype(cmp, reftype); + +	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 = ctf_merge_gettype(cmp, ctc.ctc_return); + +	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] = ctf_merge_gettype(cmp, argv[i]); +	} + +	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; + +	ctf_dprintf("Trying to add member %s to %d\n", name, cms->cms_id); +	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_sou(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; +		ctf_dprintf("added sou \"%s\" as (%d) %d->%d\n", name, kind, id, +		    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_sou(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_sou(ctf_merge_types_t *cmp, ctf_id_t id) +{ +	ctf_dtdef_t *dtd; +	ctf_merge_su_t cms; +	ctf_id_t mapid; +	ssize_t size; + +	mapid = cmp->cm_tmap[id].cmt_map; +	VERIFY(mapid != 0); +	dtd = ctf_dtd_lookup(cmp->cm_out, mapid); +	VERIFY(dtd != NULL); + +	ctf_dprintf("Trying to fix up sou %d\n", id); +	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); + +	if ((size = ctf_type_size(cmp->cm_src, id)) == CTF_ERR) +		return (CTF_ERR); +	if (ctf_set_size(cmp->cm_out, mapid, size) == CTF_ERR) +		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_sou(cmp, id); +		break; +	default: +		VERIFY(0); +		ret = CTF_ERR; +	} + +	return (ret); +} + +/* + * Now that we've successfully merged everything, we're going to clean + * up the merge type table. Traditionally if we had just two different + * files that we were working between, the types would be fully + * resolved. However, because we were comparing with ourself every step + * of the way and not our reduced self, we need to go through and update + * every mapped entry to what it now points to in the deduped file. + */ +static void +ctf_merge_fixup_dedup_map(ctf_merge_types_t *cmp) +{ +	int i; + +	for (i = 1; i < cmp->cm_src->ctf_typemax + 1; i++) { +		ctf_id_t tid; + +		/* +		 * Missing types always have their id updated to exactly what it +		 * should be. +		 */ +		if (cmp->cm_tmap[i].cmt_missing == B_TRUE) { +			VERIFY(cmp->cm_tmap[i].cmt_map != 0); +			continue; +		} + +		tid = i; +		while (cmp->cm_tmap[tid].cmt_missing == B_FALSE) { +			VERIFY(cmp->cm_tmap[tid].cmt_map != 0); +			tid = cmp->cm_tmap[tid].cmt_map; +		} +		VERIFY(cmp->cm_tmap[tid].cmt_map != 0); +		cmp->cm_tmap[i].cmt_map = cmp->cm_tmap[tid].cmt_map; +	} +} + + +/* + * 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. If we're doing a dedup we also fix up the type mapping. + */ +static int +ctf_merge_common(ctf_merge_types_t *cmp) +{ +	int ret, i; + +	ctf_phase_dump(cmp->cm_src, "merge-common-src"); +	ctf_phase_dump(cmp->cm_out, "merge-common-dest"); + +	/* 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_sou(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) { +				ctf_dprintf("Failed to merge type %d\n", i); +				return (ret); +			} +		} +	} + +	ret = ctf_update(cmp->cm_out); +	if (ret != 0) +		return (ret); + +	if (cmp->cm_dedup == B_TRUE) { +		ctf_merge_fixup_dedup_map(cmp); +	} + +	ctf_dprintf("Beginning merge pass 3\n"); +	/* 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); +		} +	} + +	if (cmp->cm_dedup == B_TRUE) { +		ctf_merge_fixup_dedup_map(cmp); +	} + +	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 the types contained inside of two input files. The second input file is + * always going to be the destination. We're guaranteed that it's always + * writeable. + */ +static int +ctf_merge_types(void *arg, void *arg2, void **outp, void *unsued) +{ +	int ret; +	ctf_merge_types_t cm; +	ctf_diff_t *cdp; +	ctf_merge_objmap_t *cmo; +	ctf_merge_funcmap_t *cmf; +	ctf_merge_input_t *scmi = arg; +	ctf_merge_input_t *dcmi = arg2; +	ctf_file_t *out = dcmi->cmi_input; +	ctf_file_t *source = scmi->cmi_input; + +	ctf_dprintf("merging %p->%p\n", source, out); + +	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; +	cm.cm_dedup = B_FALSE; +	cm.cm_unique = B_FALSE; +	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); +	ctf_dprintf("merge common returned with %d\n", ret); +	if (ret == 0) { +		ret = ctf_update(out); +		ctf_dprintf("update returned with %d\n", ret); +	} else { +		goto cleanup; +	} + +	/* +	 * Now we need to fix up the object and function maps. +	 */ +	for (cmo = list_head(&scmi->cmi_omap); cmo != NULL; +	    cmo = list_next(&scmi->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(&scmi->cmi_fmap); cmf != NULL; +	    cmf = list_next(&scmi->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; +		} +	} + +	/* +	 * Now that we've fixed things up, we need to give our function and +	 * object maps to the destination, such that it can continue to update +	 * them going forward. +	 */ +	list_move_tail(&dcmi->cmi_fmap, &scmi->cmi_fmap); +	list_move_tail(&dcmi->cmi_omap, &scmi->cmi_omap); + +cleanup: +	if (ret == 0) +		*outp = dcmi; +	ctf_merge_types_fini(&cm); +	ctf_diff_fini(cdp); +	if (ret != 0) +		return (ctf_errno(out)); +	return (0); +} + +static int +ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t *src, 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 *parent = cmh->cmh_unique; + +	*outp = NULL; +	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; +	cm.cm_dedup = B_FALSE; +	cm.cm_unique = B_TRUE; +	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); + +	if (cmi->cmi_created == B_TRUE && cmi->cmi_input != NULL) +		ctf_close(cmi->cmi_input); + +	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_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_msyms = B_FALSE; +	} else { +		out->cmh_msyms = B_TRUE; +	} + +	list_create(&out->cmh_inputs, sizeof (ctf_merge_input_t), +	    offsetof(ctf_merge_input_t, cmi_node)); +	out->cmh_ninputs = 0; +	out->cmh_nthreads = 1; +	out->cmh_unique = NULL; +	out->cmh_ofd = fd; +	out->cmh_flags = 0; +	out->cmh_label = NULL; +	out->cmh_pname = NULL; + +	return (out); +} + +int +ctf_merge_label(ctf_merge_t *cmh, const char *label) +{ +	char *dup; + +	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); +} + +/* + * Whenever we create an entry to merge, we then go and add a second empty + * ctf_file_t which we use for the purposes of our merging. It's not the best, + * but it's the best that we've got at the moment. + */ +int +ctf_merge_add(ctf_merge_t *cmh, ctf_file_t *input) +{ +	int ret; +	ctf_merge_input_t *cmi; +	ctf_file_t *empty; + +	if (input->ctf_flags & LCTF_CHILD) +		return (ECTF_MCHILD); + +	cmi = ctf_alloc(sizeof (ctf_merge_input_t)); +	if (cmi == NULL) +		return (ENOMEM); + +	cmi->cmi_created = B_FALSE; +	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); +	cmh->cmh_ninputs++; + +	/* And now the empty one to merge into this */ +	cmi = ctf_alloc(sizeof (ctf_merge_input_t)); +	if (cmi == NULL) +		return (ENOMEM); +	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)); + +	empty = ctf_fdcreate(cmh->cmh_ofd, &ret); +	if (empty == NULL) +		return (ret); +	cmi->cmi_input = empty; +	cmi->cmi_created = B_TRUE; + +	if (ctf_setmodel(empty, ctf_getmodel(input)) == CTF_ERR) { +		return (ctf_errno(empty)); +	} + +	list_insert_tail(&cmh->cmh_inputs, cmi); +	cmh->cmh_ninputs++; +	ctf_dprintf("added containers %p and %p\n", input, empty); +	return (0); +} + +int +ctf_merge_uniquify(ctf_merge_t *cmh, ctf_file_t *u, const char *pname) +{ +	char *dup; + +	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) { +				ctf_dprintf("Failed to add symbol %s->%d: %s\n", +				    name, cmo->cmo_tid, +				    ctf_errmsg(ctf_errno(fp))); +				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 **outp) +{ +	int err, merr; +	ctf_merge_input_t *cmi; +	ctf_id_t ltype; +	mergeq_t *mqp; +	ctf_merge_input_t *final; +	ctf_file_t *out; + +	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); +	} + +	if (mergeq_init(&mqp, cmh->cmh_nthreads) == -1) { +		return (errno); +	} + +	/* +	 * 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. +	 */ +	VERIFY(cmh->cmh_ninputs % 2 == 0); +	for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL; +	    cmi = list_next(&cmh->cmh_inputs, cmi)) { +		if (mergeq_add(mqp, cmi) == -1) { +			err = errno; +			mergeq_fini(mqp); +		} +	} + +	err = mergeq_merge(mqp, ctf_merge_types, NULL, (void **)&final, &merr); +	mergeq_fini(mqp); + +	if (err == MERGEQ_ERROR) { +		return (errno); +	} else if (err == MERGEQ_UERROR) { +		return (merr); +	} + +	/* +	 * Disassociate the generated ctf_file_t from the original input. That +	 * way when the input gets cleaned up, we don't accidentally kill the +	 * final reference to the ctf_file_t. If it gets uniquified then we'll +	 * kill it. +	 */ +	VERIFY(final->cmi_input != NULL); +	out = final->cmi_input; +	final->cmi_input = NULL; + +	ctf_dprintf("preparing to uniquify against: %p\n", cmh->cmh_unique); +	if (cmh->cmh_unique != NULL) { +		ctf_file_t *u; +		err = ctf_uniquify_types(cmh, out, &u); +		if (err != 0) { +			err = ctf_errno(out); +			ctf_close(out); +			return (err); +		} +		ctf_close(out); +		out = u; +	} + +	ltype = out->ctf_typemax; +	if ((out->ctf_flags & LCTF_CHILD) && ltype != 0) +		ltype += 0x8000; +	ctf_dprintf("trying to add the label\n"); +	if (cmh->cmh_label != NULL && +	    ctf_add_label(out, cmh->cmh_label, ltype, 0) != 0) { +		ctf_close(out); +		return (ctf_errno(out)); +	} + +	ctf_dprintf("merging symbols and the like\n"); +	if (cmh->cmh_msyms == B_TRUE) { +		err = ctf_merge_symbols(cmh, out); +		if (err != 0) { +			ctf_close(out); +			return (ctf_errno(out)); +		} + +		err = ctf_merge_functions(cmh, out); +		if (err != 0) { +			ctf_close(out); +			return (ctf_errno(out)); +		} +	} + +	err = ctf_update(out); +	if (err != 0) { +		ctf_close(out); +		return (ctf_errno(out)); +	} + +	*outp = out; +	return (0); +} + +/* + * When we get told that something is unique, eg. same is B_FALSE, then that + * tells us that we need to add it to the output. If same is B_TRUE, then we'll + * want to record it in the mapping table so that we know how to redirect types + * to the extant ones. + */ +static void +ctf_dedup_cb(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) { +		/* +		 * The output id here may itself map to something else. +		 * Therefore, we need to basically walk a chain and see what it +		 * points to until it itself points to a base type, eg. -1. +		 * Otherwise we'll dedup to something which no longer exists. +		 */ +		while (cmt[oid].cmt_missing == B_FALSE) +			oid = cmt[oid].cmt_map; +		cmt[iid].cmt_map = oid; +		ctf_dprintf("%d->%d \n", iid, oid); +	} else { +		VERIFY(cmt[iid].cmt_map == 0); +		cmt[iid].cmt_missing = B_TRUE; +		ctf_dprintf("%d is missing\n", iid); +	} +} + +/* + * Dedup a CTF container. + * + * DWARF and other encoding formats that we use to create CTF data may create + * multiple copies of a given type. However, after doing a conversion, and + * before doing a merge, we'd prefer, if possible, to have every input container + * to be unique. + * + * Doing a deduplication is like a normal merge. However, when we diff the types + * in the container, rather than doing a normal diff, we instead want to diff + * against any already processed types. eg, for a given type i in a container, + * we want to diff it from 0 to i - 1. + */ +int +ctf_merge_dedup(ctf_merge_t *cmp, ctf_file_t **outp) +{ +	int ret; +	ctf_diff_t *cdp = NULL; +	ctf_merge_input_t *cmi, *cmc; +	ctf_file_t *ifp, *ofp; +	ctf_merge_objmap_t *cmo; +	ctf_merge_funcmap_t *cmf; +	ctf_merge_types_t cm; + +	if (cmp == NULL || outp == NULL) +		return (EINVAL); + +	ctf_dprintf("encountered %d inputs\n", cmp->cmh_ninputs); +	if (cmp->cmh_ninputs != 2) +		return (EINVAL); + +	ctf_dprintf("passed argument sanity check\n"); + +	cmi = list_head(&cmp->cmh_inputs); +	VERIFY(cmi != NULL); +	cmc = list_next(&cmp->cmh_inputs, cmi); +	VERIFY(cmc != NULL); +	ifp = cmi->cmi_input; +	ofp = cmc->cmi_input; +	VERIFY(ifp != NULL); +	VERIFY(ofp != NULL); +	cm.cm_src = ifp; +	cm.cm_out = ofp; +	cm.cm_dedup = B_TRUE; +	cm.cm_unique = B_FALSE; + +	if ((ret = ctf_merge_types_init(&cm)) != 0) { +		return (ret); +	} + +	if ((ret = ctf_diff_init(ifp, ifp, &cdp)) != 0) +		goto err; + +	ctf_dprintf("Successfully initialized dedup\n"); +	if ((ret = ctf_diff_self(cdp, ctf_dedup_cb, &cm)) != 0) +		goto err; + +	ctf_dprintf("Successfully diffed types\n"); +	ret = ctf_merge_common(&cm); +	ctf_dprintf("deduping types result: %d\n", ret); +	if (ret == 0) +		ret = ctf_update(cm.cm_out); +	if (ret != 0) +		goto err; + +	ctf_dprintf("Successfully deduped types\n"); +	ctf_phase_dump(cm.cm_out, "dedup-pre-syms"); + + +	/* +	 * 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; +		ctf_dprintf("mapped %s %d->%d\n", cmo->cmo_name, +		    cmo->cmo_tid, cm.cm_tmap[cmo->cmo_tid].cmt_map); +		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; +		} +	} + +	if (cmp->cmh_msyms == B_TRUE) { +		ret = ctf_merge_symbols(cmp, cm.cm_out); +		if (ret != 0) { +			ret = ctf_errno(cm.cm_out); +			ctf_dprintf("failed to dedup symbols: %s\n", +			    ctf_errmsg(ret)); +			goto err; +		} + +		ret = ctf_merge_functions(cmp, cm.cm_out); +		if (ret != 0) { +			ret = ctf_errno(cm.cm_out); +			ctf_dprintf("failed to dedup functions: %s\n", +			    ctf_errmsg(ret)); +			goto err; +		} +	} + +	ret = ctf_update(cm.cm_out); +	if (ret == 0) { +		cmc->cmi_input = NULL; +		*outp = cm.cm_out; +	} +err: +	ctf_merge_types_fini(&cm); +	ctf_diff_fini(cdp); +	return (ret); +} + +int +ctf_merge_set_nthreads(ctf_merge_t *cmp, const uint_t nthrs) +{ +	if (nthrs == 0) +		return (EINVAL); +	cmp->cmh_nthreads = nthrs; +	return (0); +} diff --git a/usr/src/lib/libctf/common/ctf_subr.c b/usr/src/lib/libctf/common/ctf_subr.c index 467b6a8181..26f7e8c4db 100644 --- a/usr/src/lib/libctf/common/ctf_subr.c +++ b/usr/src/lib/libctf/common/ctf_subr.c @@ -24,8 +24,6 @@   * Use is subject to license terms.   */ -#pragma ident	"%Z%%M%	%I%	%E% SMI" -  #include <ctf_impl.h>  #include <libctf.h>  #include <sys/mman.h> @@ -56,6 +54,18 @@ ctf_alloc(size_t size)  	return (malloc(size));  } +void * +mergeq_alloc(size_t size) +{ +	return (malloc(size)); +} + +void * +workq_alloc(size_t size) +{ +	return (malloc(size)); +} +  /*ARGSUSED*/  void  ctf_free(void *buf, size_t size) @@ -63,6 +73,20 @@ ctf_free(void *buf, size_t size)  	free(buf);  } +/*ARGSUSED*/ +void +mergeq_free(void *buf, size_t size) +{ +	free(buf); +} + +/*ARGSUSED*/ +void +workq_free(void *buf, size_t size) +{ +	free(buf); +} +  const char *  ctf_strerror(int err)  { diff --git a/usr/src/lib/libctf/common/libctf.h b/usr/src/lib/libctf/common/libctf.h index 3fd69318de..a5c5027048 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,9 +43,8 @@  #ifndef	_LIBCTF_H  #define	_LIBCTF_H -#pragma ident	"%Z%%M%	%I%	%E% SMI" -  #include <sys/ctf_api.h> +#include <libelf.h>  #ifdef	__cplusplus  extern "C" { @@ -53,6 +55,46 @@ extern "C" {   */  extern int _libctf_debug; +typedef enum ctf_diff_flag { +	CTF_DIFF_F_IGNORE_INTNAMES = 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 *); + +#define	CTF_CONVERT_F_IGNNONC	0x01 +extern ctf_file_t *ctf_elfconvert(int, Elf *, const char *, uint_t, uint_t, +    int *, char *, size_t); +extern ctf_file_t *ctf_fdconvert(int, const char *, uint_t, uint_t, int *, +    char *, size_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_set_nthreads(ctf_merge_t *, const uint_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 int ctf_merge_dedup(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/libctf_impl.h b/usr/src/lib/libctf/common/libctf_impl.h new file mode 100644 index 0000000000..11193e97d0 --- /dev/null +++ b/usr/src/lib/libctf/common/libctf_impl.h @@ -0,0 +1,59 @@ +/* + * 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 2015 Joyent, Inc. + */ + +#ifndef _LIBCTF_IMPL_H +#define	_LIBCTF_IMPL_H + +/* + * Portions of libctf implementations that are only suitable for CTF's userland + * library, eg. converting and merging related routines. + */ + +#include <libelf.h> +#include <libctf.h> +#include <ctf_impl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum ctf_conv_status { +	CTF_CONV_SUCCESS	= 0, +	CTF_CONV_ERROR		= 1, +	CTF_CONV_NOTSUP		= 2 +} ctf_conv_status_t; + +typedef ctf_conv_status_t (*ctf_convert_f)(int, Elf *, uint_t, int *, +    ctf_file_t **, char *, size_t); +extern ctf_conv_status_t ctf_dwarf_convert(int, Elf *, uint_t, int *, +    ctf_file_t **, char *, size_t); + +/* + * zlib compression routines + */ +extern int ctf_compress(ctf_file_t *fp, void **, size_t *, size_t *); + +extern int ctf_diff_self(ctf_diff_t *, ctf_diff_type_f, void *); + +/* + * Internal debugging aids + */ +extern void ctf_phase_dump(ctf_file_t *, const char *); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIBCTF_IMPL_H */ diff --git a/usr/src/lib/libctf/common/mapfile-vers b/usr/src/lib/libctf/common/mapfile-vers index 5573e8db25..cfd2952bbe 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,48 @@ 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_elfconvert; +	ctf_elffdwrite; +	ctf_elfwrite;  	ctf_enum_value; +	ctf_fdconvert; +	ctf_flags; +	ctf_func_args_by_id; +	ctf_func_info_by_id; +	ctf_function_iter; +	ctf_kind_name;  	ctf_label_info;  	ctf_label_iter;  	ctf_label_topmost;  	ctf_member_info; +	ctf_merge_add; +	ctf_merge_dedup; +	ctf_merge_fini; +	ctf_merge_init; +	ctf_merge_label; +	ctf_merge_merge; +	ctf_merge_set_nthreads; +	ctf_merge_uniquify; +	ctf_object_iter;  	ctf_parent_file; +	ctf_parent_label;  	ctf_parent_name;  	ctf_set_array; +	ctf_set_root; +	ctf_set_size; +	ctf_string_iter; +	ctf_symbol_name;  	ctf_type_align;  	ctf_type_cmp;  	ctf_type_compat; | 
