changeset 20196:266b962d1d4a

13280 CTF: provide option to truncate and continue Reviewed by: Robert Mustacchi <rm@fingolfin.org> Reviewed by: Igor Kozhukhov <igor@dilos.org> Approved by: Rich Lowe <richlowe@richlowe.net>
author Andy Fiddaman <omnios@citrus-it.co.uk>
date Wed, 04 Nov 2020 12:07:58 +0000
parents 8b343bbde246
children 1c416c6c616f
files usr/src/cmd/ctfconvert/ctfconvert.c usr/src/lib/libctf/common/ctf_convert.c usr/src/lib/libctf/common/ctf_dwarf.c usr/src/lib/libctf/common/libctf.h usr/src/lib/libctf/common/libctf_impl.h usr/src/lib/libctf/common/mapfile-vers usr/src/tools/scripts/ctfconvert.1onbld
diffstat 7 files changed, 380 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/ctfconvert/ctfconvert.c	Tue Nov 10 02:25:25 2020 +0000
+++ b/usr/src/cmd/ctfconvert/ctfconvert.c	Wed Nov 04 12:07:58 2020 +0000
@@ -11,6 +11,7 @@
 
 /*
  * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*
@@ -37,9 +38,6 @@
 #define	CTFCONVERT_FATAL	1
 #define	CTFCONVERT_USAGE	2
 
-#define	CTFCONVERT_DEFAULT_BATCHSIZE	256
-#define	CTFCONVERT_DEFAULT_NTHREADS	4
-
 static char *ctfconvert_progname;
 
 static void
@@ -55,6 +53,20 @@
 	exit(CTFCONVERT_FATAL);
 }
 
+static void
+ctfconvert_warning(void *arg, const char *fmt, ...)
+{
+	va_list ap;
+	char *buf;
+
+	va_start(ap, fmt);
+	if (vasprintf(&buf, fmt, ap) != -1) {
+		(void) fprintf(stderr, "%s: WARNING: %s", ctfconvert_progname,
+		    buf);
+		free(buf);
+	}
+	va_end(ap);
+}
 
 static void
 ctfconvert_usage(const char *fmt, ...)
@@ -68,7 +80,7 @@
 		va_end(ap);
 	}
 
-	(void) fprintf(stderr, "Usage: %s [-ikm] [-j nthrs] [-l label | "
+	(void) fprintf(stderr, "Usage: %s [-ikms] [-j nthrs] [-l label | "
 	    "-L labelenv] [-b batchsize]\n"
 	    "                  [-o outfile] input\n"
 	    "\n"
@@ -79,10 +91,11 @@
 	    "\t-l  set output container's label to specified value\n"
 	    "\t-L  set output container's label to value from environment\n"
 	    "\t-m  allow input to have missing debug info\n"
-	    "\t-o  copy input to outfile and add CTF\n",
+	    "\t-o  copy input to outfile and add CTF\n"
+	    "\t-s  allow truncation of data that cannot be fully converted\n",
 	    ctfconvert_progname,
-	    CTFCONVERT_DEFAULT_BATCHSIZE,
-	    CTFCONVERT_DEFAULT_NTHREADS);
+	    CTF_CONVERT_DEFAULT_BATCHSIZE,
+	    CTF_CONVERT_DEFAULT_NTHREADS);
 }
 
 /*
@@ -192,13 +205,13 @@
 		    CTF_K_STRUCT);
 		if (mcpu == CTF_ERR) {
 			ctfconvert_fatal("failed to add 'struct machcpu' "
-			    "forward: %s", ctf_errmsg(ctf_errno(fp)));
+			    "forward: %s\n", ctf_errmsg(ctf_errno(fp)));
 		}
 	} else {
 		int kind;
 		if ((kind = ctf_type_kind(fp, mcpu)) == CTF_ERR) {
 			ctfconvert_fatal("failed to get the type kind for "
-			    "the struct machcpu: %s",
+			    "the struct machcpu: %s\n",
 			    ctf_errmsg(ctf_errno(fp)));
 		}
 
@@ -230,21 +243,22 @@
 {
 	int c, ifd, err;
 	boolean_t keep = B_FALSE;
-	uint_t flags = 0;
-	uint_t bsize = CTFCONVERT_DEFAULT_BATCHSIZE;
-	uint_t nthreads = CTFCONVERT_DEFAULT_NTHREADS;
+	ctf_convert_flag_t flags = 0;
+	uint_t bsize = CTF_CONVERT_DEFAULT_BATCHSIZE;
+	uint_t nthreads = CTF_CONVERT_DEFAULT_NTHREADS;
 	const char *outfile = NULL;
 	const char *label = NULL;
 	const char *infile = NULL;
 	char *tmpfile;
 	ctf_file_t *ofp;
-	char buf[4096];
+	char buf[4096] = "";
 	boolean_t optx = B_FALSE;
 	boolean_t ignore_non_c = B_FALSE;
+	ctf_convert_t *cch;
 
 	ctfconvert_progname = basename(argv[0]);
 
-	while ((c = getopt(argc, argv, ":b:ij:kl:L:mo:X")) != -1) {
+	while ((c = getopt(argc, argv, ":b:ij:kl:L:mo:sX")) != -1) {
 		switch (c) {
 		case 'b': {
 			long argno;
@@ -288,6 +302,9 @@
 		case 'o':
 			outfile = optarg;
 			break;
+		case 's':
+			flags |= CTF_ALLOW_TRUNCATION;
+			break;
 		case 'X':
 			optx = B_TRUE;
 			break;
@@ -327,8 +344,32 @@
 	if (outfile != NULL && strcmp(infile, outfile) != 0)
 		keep = B_TRUE;
 
-	ofp = ctf_fdconvert(ifd, label, bsize, nthreads, flags, &err, buf,
-	    sizeof (buf));
+	cch = ctf_convert_init(&err);
+	if (cch == NULL) {
+		ctfconvert_fatal(
+		    "failed to create libctf conversion handle: %s\n",
+		    strerror(err));
+	}
+	if ((err = ctf_convert_set_nthreads(cch, nthreads)) != 0)
+		ctfconvert_fatal("Could not set number of threads: %s\n",
+		    strerror(err));
+	if ((err = ctf_convert_set_batchsize(cch, bsize)) != 0)
+		ctfconvert_fatal("Could not set batch size: %s\n",
+		    strerror(err));
+	if ((err = ctf_convert_set_flags(cch, flags)) != 0)
+		ctfconvert_fatal("Could not set conversion flags: %s\n",
+		    strerror(err));
+	if (label != NULL && (err = ctf_convert_set_label(cch, label)) != 0)
+		ctfconvert_fatal("Could not set label: %s\n",
+		    strerror(err));
+	if ((err = ctf_convert_set_warncb(cch, ctfconvert_warning, NULL)) != 0)
+		ctfconvert_fatal("Could not set warning callback: %s\n",
+		    strerror(err));
+
+	ofp = ctf_fdconvert(cch, ifd, &err, buf, sizeof (buf));
+
+	ctf_convert_fini(cch);
+
 	if (ofp == NULL) {
 		/*
 		 * Normally, ctfconvert requires that its input file has at
@@ -351,9 +392,19 @@
 		if (keep == B_FALSE)
 			(void) unlink(infile);
 
-		if (err == ECTF_CONVBKERR || err == ECTF_CONVNODEBUG) {
-			ctfconvert_fatal("%s\n", buf);
-		} else {
+		switch (err) {
+		case ECTF_CONVBKERR:
+			ctfconvert_fatal("CTF conversion failed: %s", buf);
+			break;
+		case ECTF_CONVNODEBUG:
+			ctfconvert_fatal("CTF conversion failed due to "
+			    "missing debug data; use -m to override\n");
+			break;
+		default:
+			if (*buf != '\0') {
+				(void) fprintf(stderr, "%s: %s",
+				    ctfconvert_progname, buf);
+			}
 			ctfconvert_fatal("CTF conversion failed: %s\n",
 			    ctf_errmsg(err));
 		}
@@ -378,7 +429,7 @@
 		if (keep == B_FALSE)
 			(void) unlink(infile);
 		ctfconvert_fatal("failed to write CTF section to output file: "
-		    "%s", ctf_errmsg(ctf_errno(ofp)));
+		    "%s\n", ctf_errmsg(ctf_errno(ofp)));
 	}
 	ctf_close(ofp);
 
--- a/usr/src/lib/libctf/common/ctf_convert.c	Tue Nov 10 02:25:25 2020 +0000
+++ b/usr/src/lib/libctf/common/ctf_convert.c	Wed Nov 04 12:07:58 2020 +0000
@@ -11,12 +11,13 @@
 
 /*
  * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*
  * 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
+ * understands DWARFv2 and DWARFv4. Each backend should be placed in
  * the ctf_converters list and each will be tried in turn.
  */
 
@@ -24,7 +25,7 @@
 #include <assert.h>
 #include <gelf.h>
 
-ctf_convert_f ctf_converters[] = {
+static ctf_convert_f ctf_converters[] = {
 	ctf_dwarf_convert
 };
 
@@ -100,8 +101,8 @@
 }
 
 static ctf_file_t *
-ctf_elfconvert(int fd, Elf *elf, const char *label, uint_t bsize, uint_t nthrs,
-    uint_t flags, int *errp, char *errbuf, size_t errlen)
+ctf_elfconvert(ctf_convert_t *cch, int fd, Elf *elf, int *errp, char *errbuf,
+    size_t errlen)
 {
 	int err, i;
 	ctf_file_t *fp = NULL;
@@ -114,11 +115,6 @@
 		return (NULL);
 	}
 
-	if (flags & ~CTF_ALLOW_MISSING_DEBUG) {
-		*errp = EINVAL;
-		return (NULL);
-	}
-
 	if (elf_kind(elf) != ELF_K_ELF) {
 		*errp = ECTF_FMT;
 		return (NULL);
@@ -139,8 +135,7 @@
 
 	for (i = 0; i < NCONVERTS; i++) {
 		fp = NULL;
-		err = ctf_converters[i](fd, elf, bsize, nthrs, flags,
-		    &fp, errbuf, errlen);
+		err = ctf_converters[i](cch, fd, elf, &fp, errbuf, errlen);
 
 		if (err != ECTF_CONVNODEBUG)
 			break;
@@ -152,8 +147,9 @@
 		return (NULL);
 	}
 
-	if (label != NULL) {
-		if (ctf_add_label(fp, label, fp->ctf_typemax, 0) == CTF_ERR) {
+	if (cch->cch_label != NULL) {
+		if (ctf_add_label(fp, cch->cch_label, fp->ctf_typemax, 0) ==
+		    CTF_ERR) {
 			*errp = ctf_errno(fp);
 			ctf_close(fp);
 			return (NULL);
@@ -168,9 +164,101 @@
 	return (fp);
 }
 
+ctf_convert_t *
+ctf_convert_init(int *errp)
+{
+	struct ctf_convert_handle *cch;
+	int err;
+
+	if (errp == NULL)
+		errp = &err;
+	*errp = 0;
+
+	cch = ctf_alloc(sizeof (struct ctf_convert_handle));
+	if (cch == NULL) {
+		*errp = ENOMEM;
+		return (NULL);
+	}
+
+	cch->cch_label = NULL;
+	cch->cch_flags = 0;
+	cch->cch_nthreads = CTF_CONVERT_DEFAULT_NTHREADS;
+	cch->cch_batchsize = CTF_CONVERT_DEFAULT_BATCHSIZE;
+	cch->cch_warncb = NULL;
+	cch->cch_warncb_arg = NULL;
+
+	return (cch);
+}
+
+void
+ctf_convert_fini(ctf_convert_t *cch)
+{
+	if (cch->cch_label != NULL) {
+		size_t len = strlen(cch->cch_label) + 1;
+		ctf_free(cch->cch_label, len);
+	}
+	ctf_free(cch, sizeof (struct ctf_convert_handle));
+}
+
+int
+ctf_convert_set_nthreads(ctf_convert_t *cch, uint_t nthrs)
+{
+	if (nthrs == 0)
+		return (EINVAL);
+	cch->cch_nthreads = nthrs;
+	return (0);
+}
+
+int
+ctf_convert_set_batchsize(ctf_convert_t *cch, uint_t bsize)
+{
+	if (bsize == 0)
+		return (EINVAL);
+	cch->cch_batchsize = bsize;
+	return (0);
+}
+
+int
+ctf_convert_set_flags(ctf_convert_t *cch, uint_t flags)
+{
+	if ((flags & ~CTF_CONVERT_ALL_FLAGS) != 0)
+		return (EINVAL);
+	cch->cch_flags = flags;
+	return (0);
+}
+
+int
+ctf_convert_set_label(ctf_convert_t *cch, const char *label)
+{
+	char *dup;
+
+	if (label == NULL)
+		return (EINVAL);
+
+	dup = ctf_strdup(label);
+	if (dup == NULL)
+		return (ENOMEM);
+
+	if (cch->cch_label != NULL) {
+		size_t len = strlen(cch->cch_label) + 1;
+		ctf_free(cch->cch_label, len);
+	}
+
+	cch->cch_label = dup;
+	return (0);
+}
+
+int
+ctf_convert_set_warncb(ctf_convert_t *cch, ctf_convert_warn_f cb, void *arg)
+{
+	cch->cch_warncb = cb;
+	cch->cch_warncb_arg = arg;
+	return (0);
+}
+
 ctf_file_t *
-ctf_fdconvert(int fd, const char *label, uint_t bsize, uint_t nthrs,
-    uint_t flags, int *errp, char *errbuf, size_t errlen)
+ctf_fdconvert(ctf_convert_t *cch, int fd, int *errp,
+    char *errbuf, size_t errlen)
 {
 	int err;
 	Elf *elf;
@@ -185,8 +273,7 @@
 		return (NULL);
 	}
 
-	fp = ctf_elfconvert(fd, elf, label, bsize, nthrs, flags, errp, errbuf,
-	    errlen);
+	fp = ctf_elfconvert(cch, fd, elf, errp, errbuf, errlen);
 
 	(void) elf_end(elf);
 	return (fp);
--- a/usr/src/lib/libctf/common/ctf_dwarf.c	Tue Nov 10 02:25:25 2020 +0000
+++ b/usr/src/lib/libctf/common/ctf_dwarf.c	Wed Nov 04 12:07:58 2020 +0000
@@ -274,6 +274,7 @@
 	avl_tree_t	cu_map;		/* map die offsets to CTF types */
 	char		*cu_errbuf;	/* error message buffer */
 	size_t		cu_errlen;	/* error message buffer length */
+	ctf_convert_t	*cu_handle;	/* ctf convert handle */
 	size_t		cu_ptrsz;	/* object's pointer size */
 	boolean_t	cu_bigend;	/* is it big endian */
 	boolean_t	cu_doweaks;	/* should we convert weak symbols? */
@@ -485,7 +486,7 @@
 	}
 
 	(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-	    "failed to get unsigned attribute for type: %s\n",
+	    "failed to get attribute descriptor offset: %s\n",
 	    dwarf_errmsg(derr));
 	return (ECTF_CONVBKERR);
 }
@@ -535,7 +536,7 @@
 	}
 
 	(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-	    "failed to get unsigned attribute for type: %s\n",
+	    "failed to get signed attribute for type: %s\n",
 	    dwarf_errmsg(derr));
 	return (ECTF_CONVBKERR);
 }
@@ -649,7 +650,8 @@
 	DWARF_UNLOCK(cup);
 	if (ret != DW_DLV_OK) {
 		(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-		    "failed to get dwarf attribute for for member location: %s",
+		    "failed to get dwarf attribute for for member "
+		    "location: %s\n",
 		    dwarf_errmsg(derr));
 		ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR);
 		return (ECTF_CONVBKERR);
@@ -679,7 +681,7 @@
 			if (sign < 0) {
 				(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
 				    "encountered negative member data "
-				    "location: %d", sign);
+				    "location: %d\n", sign);
 			}
 			*valp = (Dwarf_Unsigned)sign;
 			return (0);
@@ -689,7 +691,7 @@
 	if (dwarf_loclist(attr, &loc, &locnum, &derr) != DW_DLV_OK) {
 		DWARF_UNLOCK(cup);
 		(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-		    "failed to obtain location list for member offset: %s",
+		    "failed to obtain location list for member offset: %s\n",
 		    dwarf_errmsg(derr));
 		ctf_dwarf_dealloc(cup, attr, DW_DLA_ATTR);
 		return (ECTF_CONVBKERR);
@@ -699,7 +701,7 @@
 
 	if (locnum != 1 || loc->ld_s->lr_atom != DW_OP_plus_uconst) {
 		(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-		    "failed to parse location structure for member");
+		    "failed to parse location structure for member\n");
 		ctf_dwarf_dealloc(cup, loc->ld_s, DW_DLA_LOC_BLOCK);
 		ctf_dwarf_dealloc(cup, loc, DW_DLA_LOCDESC);
 		return (ECTF_CONVBKERR);
@@ -853,7 +855,7 @@
 		VERIFY(ctf_setmodel(cup->cu_ctfp, CTF_MODEL_LP64) == 0);
 	} else {
 		(void) snprintf(errbuf, errlen,
-		    "unknown ELF class %d", ehdr.e_ident[EI_CLASS]);
+		    "unknown ELF class %d\n", ehdr.e_ident[EI_CLASS]);
 		return (ECTF_CONVBKERR);
 	}
 
@@ -863,7 +865,7 @@
 		cup->cu_bigend = B_TRUE;
 	} else {
 		(void) snprintf(errbuf, errlen,
-		    "unknown ELF data encoding: %hhu", ehdr.e_ident[EI_DATA]);
+		    "unknown ELF data encoding: %hhu\n", ehdr.e_ident[EI_DATA]);
 		return (ECTF_CONVBKERR);
 	}
 
@@ -1031,7 +1033,7 @@
 		break;
 	default:
 		(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-		    "encountered unknown DWARF encoding: %d", type);
+		    "encountered unknown DWARF encoding: %d\n", type);
 		return (ECTF_CONVBKERR);
 	}
 
@@ -1230,7 +1232,7 @@
 		if ((tsz = ctf_type_size(cup->cu_ctfp, mid)) == CTF_ERR) {
 			int e = ctf_errno(cup->cu_ctfp);
 			(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-			    "failed to get type size: %s", ctf_errmsg(e));
+			    "failed to get type size: %s\n", ctf_errmsg(e));
 			return (ECTF_CONVBKERR);
 		}
 	} else {
@@ -1350,7 +1352,7 @@
 		    name, &e);
 		if (cdb->cdb_id == CTF_ERR) {
 			(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-			    "failed to get add bitfield type %s: %s", name,
+			    "failed to get add bitfield type %s: %s\n", name,
 			    ctf_errmsg(ctf_errno(cup->cu_ctfp)));
 			ctf_free(name, namesz + 1);
 			ctf_free(cdb, sizeof (ctf_dwbitf_t));
@@ -1437,7 +1439,7 @@
 		ret = ctf_add_member(cup->cu_ctfp, base, mname, mid, memboff);
 		if (ret == CTF_ERR) {
 			(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-			    "failed to add member %s: %s",
+			    "failed to add member %s: %s\n",
 			    mname, ctf_errmsg(ctf_errno(cup->cu_ctfp)));
 			if (mname != NULL)
 				ctf_free(mname, strlen(mname) + 1);
@@ -1466,7 +1468,7 @@
 	if ((ctf_set_size(cup->cu_ctfp, base, size)) == CTF_ERR) {
 		int e = ctf_errno(cup->cu_ctfp);
 		(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-		    "failed to set type size for %d to 0x%x: %s", base,
+		    "failed to set type size for %d to 0x%x: %s\n", base,
 		    (uint32_t)size, ctf_errmsg(e));
 		return (ECTF_CONVBKERR);
 	}
@@ -1891,14 +1893,14 @@
 	Dwarf_Die child;
 	Dwarf_Unsigned dw;
 	ctf_id_t id;
-	char *name;
+	char *enumname;
 	int ret;
 
-	if ((ret = ctf_dwarf_string(cup, die, DW_AT_name, &name)) != 0 &&
-	    ret != ENOENT)
+	ret = ctf_dwarf_string(cup, die, DW_AT_name, &enumname);
+	if (ret != 0 && ret != ENOENT)
 		return (ret);
 	if (ret == ENOENT)
-		name = NULL;
+		enumname = NULL;
 
 	/*
 	 * Enumerations may have a size associated with them, particularly if
@@ -1910,20 +1912,21 @@
 		size = (size_t)dw;
 	}
 
-	id = ctf_add_enum(cup->cu_ctfp, isroot, name, size);
-	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(cup->cu_ctfp));
+	id = ctf_add_enum(cup->cu_ctfp, isroot, enumname, size);
+	ctf_dprintf("added enum %s (%d)\n",
+	    enumname == NULL ? "<anon>" : enumname, id);
+	if (id == CTF_ERR) {
+		ret = ctf_errno(cup->cu_ctfp);
+		goto out;
+	}
 	*idp = id;
 	if ((ret = ctf_dwmap_add(cup, id, die, B_FALSE)) != 0)
-		return (ret);
+		goto out;
 
 	if ((ret = ctf_dwarf_child(cup, die, &child)) != 0) {
 		if (ret == ENOENT)
 			ret = 0;
-		return (ret);
+		goto out;
 	}
 
 	while (child != NULL) {
@@ -1931,18 +1934,20 @@
 		Dwarf_Signed sval;
 		Dwarf_Unsigned uval;
 		Dwarf_Die arg = child;
+		char *name;
 		int eval;
 
 		if ((ret = ctf_dwarf_sib(cup, arg, &child)) != 0)
-			return (ret);
+			break;
 
 		if ((ret = ctf_dwarf_tag(cup, arg, &tag)) != 0)
-			return (ret);
+			break;
 
 		if (tag != DW_TAG_enumerator) {
 			if ((ret = ctf_dwarf_convert_type(cup, arg, NULL,
-			    CTF_ADD_NONROOT)) != 0)
-				return (ret);
+			    CTF_ADD_NONROOT)) != 0) {
+				break;
+			}
 			continue;
 		}
 
@@ -1950,7 +1955,7 @@
 		 * DWARF v4 section 5.7 tells us we'll always have names.
 		 */
 		if ((ret = ctf_dwarf_string(cup, arg, DW_AT_name, &name)) != 0)
-			return (ret);
+			break;
 
 		/*
 		 * We have to be careful here: newer GCCs generate DWARF where
@@ -1961,32 +1966,62 @@
 		if ((ret = ctf_dwarf_unsigned(cup, arg, DW_AT_const_value,
 		    &uval)) == 0) {
 			eval = (int)uval;
-		} else if ((ret = ctf_dwarf_signed(cup, arg, DW_AT_const_value,
-		    &sval)) == 0) {
-			eval = sval;
+		} else {
+			/*
+			 * ctf_dwarf_unsigned will have left an error in the
+			 * buffer
+			 */
+			*cup->cu_errbuf = '\0';
+
+			if ((ret = ctf_dwarf_signed(cup, arg, DW_AT_const_value,
+			    &sval)) == 0) {
+				eval = sval;
+			}
 		}
 
 		if (ret != 0) {
-			if (ret != ENOENT)
-				return (ret);
-
-			(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-			    "encountered enumeration without constant value\n");
-			return (ECTF_CONVBKERR);
+			if (ret == ENOENT) {
+				(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+				    "encountered enumeration without constant "
+				    "value\n");
+				ret = ECTF_CONVBKERR;
+			}
+			ctf_free(name, strlen(name) + 1);
+			break;
 		}
 
 		ret = ctf_add_enumerator(cup->cu_ctfp, id, name, eval);
 		if (ret == CTF_ERR) {
-			(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-			    "failed to add enumarator %s (%d) to %d\n",
-			    name, eval, id);
+			ret = ctf_errno(cup->cu_ctfp);
+
+			if (ret == ECTF_DTFULL && (cup->cu_handle->cch_flags &
+			    CTF_ALLOW_TRUNCATION)) {
+				if (cup->cu_handle->cch_warncb != NULL) {
+					cup->cu_handle->cch_warncb(
+					    cup->cu_handle->cch_warncb_arg,
+					    "truncating enumeration %s at %s\n",
+					    name, enumname == NULL ? "<anon>" :
+					    enumname);
+				}
+				ret = 0;
+			} else {
+				(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
+				    "failed to add enumerator %s (%d) "
+				    "to %s (%d)\n", name, eval,
+				    enumname == NULL ? "<anon>" : enumname, id);
+			}
 			ctf_free(name, strlen(name) + 1);
-			return (ctf_errno(cup->cu_ctfp));
+			break;
 		}
 		ctf_free(name, strlen(name) + 1);
 	}
 
-	return (0);
+out:
+
+	if (enumname != NULL)
+		ctf_free(enumname, strlen(enumname) + 1);
+
+	return (ret);
 }
 
 /*
@@ -3124,9 +3159,9 @@
  * be able to finish the rest in a (potentially) multithreaded context.
  */
 static int
-ctf_dwarf_preinit_dies(int fd, Elf *elf, Dwarf_Debug dw,
+ctf_dwarf_preinit_dies(ctf_convert_t *cch, int fd, Elf *elf, Dwarf_Debug dw,
     mutex_t *dwlock, Dwarf_Error *derr, uint_t ndies, ctf_cu_t *cdies,
-    uint_t flags, char *errbuf, size_t errlen)
+    char *errbuf, size_t errlen)
 {
 	Dwarf_Unsigned hdrlen, abboff, nexthdr;
 	Dwarf_Half addrsz, vers;
@@ -3144,6 +3179,7 @@
 
 		cup = &cdies[i++];
 
+		cup->cu_handle = cch;
 		cup->cu_fd = fd;
 		cup->cu_elf = elf;
 		cup->cu_dwarf = dw;
@@ -3176,7 +3212,7 @@
 		if (cu == NULL) {
 			ctf_dprintf("cu %d - no cu data\n", i);
 			(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-			    "file does not contain DWARF data");
+			    "file does not contain DWARF data\n");
 			return (ECTF_CONVNODEBUG);
 		}
 
@@ -3212,13 +3248,20 @@
 				 * Missing DEBUG data for a .c file, return an
 				 * error unless this is permitted.
 				 */
-				if (!(flags & CTF_ALLOW_MISSING_DEBUG)) {
+				if (!(cch->cch_flags &
+				    CTF_ALLOW_MISSING_DEBUG)) {
 					(void) snprintf(
 					    cup->cu_errbuf, cup->cu_errlen,
-					    "file %s is missing debug info",
+					    "missing debug information "
+					    "(first seen in %s)\n",
 					    cup->cu_name);
 					return (ECTF_CONVNODEBUG);
 				}
+				if (cch->cch_warncb != NULL) {
+					cch->cch_warncb(cch->cch_warncb_arg,
+					    "file %s is missing debug "
+					    "information\n", cup->cu_name);
+				}
 			}
 		} else {
 			added++;
@@ -3304,13 +3347,14 @@
 }
 
 static int
-ctf_dwarf_check_missing(ctf_cu_t *cus, size_t nr_cus, Elf *elf,
-    char *errmsg, size_t errlen)
+ctf_dwarf_check_missing(ctf_convert_t *cch, ctf_cu_t *cus, size_t nr_cus,
+    Elf *elf, char *errmsg, size_t errlen)
 {
 	Elf_Scn *scn, *strscn;
 	Elf_Data *data, *strdata;
 	GElf_Shdr shdr;
 	ulong_t i;
+	int ret = 0;
 
 	scn = NULL;
 	while ((scn = elf_nextscn(elf, scn)) != NULL) {
@@ -3369,13 +3413,22 @@
 			continue;
 
 		if (!c_source_has_debug(file, cus, nr_cus)) {
-			(void) snprintf(errmsg, errlen,
-			    "file %s is missing debug info", file);
-			return (ECTF_CONVNODEBUG);
+			if (cch->cch_warncb != NULL) {
+				cch->cch_warncb(
+				    cch->cch_warncb_arg,
+				    "file %s is missing debug information\n",
+				    file);
+			}
+			if (ret != ECTF_CONVNODEBUG) {
+				(void) snprintf(errmsg, errlen,
+				    "missing debug information "
+				    "(first seen in %s)\n", file);
+				ret = ECTF_CONVNODEBUG;
+			}
 		}
 	}
 
-	return (0);
+	return (ret);
 }
 
 static int
@@ -3488,11 +3541,11 @@
 }
 
 int
-ctf_dwarf_convert(int fd, Elf *elf, uint_t bsize, uint_t nthrs, uint_t flags,
-    ctf_file_t **fpp, char *errbuf, size_t errlen)
+ctf_dwarf_convert(ctf_convert_t *cch, int fd, Elf *elf, ctf_file_t **fpp,
+    char *errbuf, size_t errlen)
 {
 	int err, ret;
-	uint_t ndies, i;
+	uint_t ndies, i, bsize, nthrs;
 	Dwarf_Debug dw;
 	Dwarf_Error derr;
 	ctf_cu_t *cdies = NULL, *cup;
@@ -3542,15 +3595,21 @@
 
 	bzero(cdies, sizeof (ctf_cu_t) * ndies);
 
-	if ((err = ctf_dwarf_preinit_dies(fd, elf, dw, &dwlock, &derr,
-	    ndies, cdies, flags, errbuf, errlen)) != 0) {
+	if ((err = ctf_dwarf_preinit_dies(cch, fd, elf, dw, &dwlock, &derr,
+	    ndies, cdies, errbuf, errlen)) != 0) {
 		goto out;
 	}
 
-	if (!(flags & CTF_ALLOW_MISSING_DEBUG) &&
-	    (err = ctf_dwarf_check_missing(cdies, ndies,
-	    elf, errbuf, errlen)) != 0) {
-		goto out;
+	if ((err = ctf_dwarf_check_missing(cch, cdies, ndies, elf,
+	    errbuf, errlen)) != 0) {
+		if (!(cch->cch_flags & CTF_ALLOW_MISSING_DEBUG)) {
+			goto out;
+		}
+		if (err != ECTF_CONVNODEBUG && *errbuf != '\0' &&
+		    cch->cch_warncb != NULL) {
+			cch->cch_warncb(cch->cch_warncb_arg, "%s", errbuf);
+			*errbuf = '\0';
+		}
 	}
 
 	/* Only one cu, no merge required */
@@ -3570,8 +3629,8 @@
 	 * There's no need to have either more threads or a batch size larger
 	 * than the total number of dies, even if the user requested them.
 	 */
-	nthrs = min(ndies, nthrs);
-	bsize = min(ndies, bsize);
+	nthrs = min(ndies, cch->cch_nthreads);
+	bsize = min(ndies, cch->cch_batchsize);
 
 	if (workq_init(&wqp, nthrs) == -1) {
 		err = errno;
--- a/usr/src/lib/libctf/common/libctf.h	Tue Nov 10 02:25:25 2020 +0000
+++ b/usr/src/lib/libctf/common/libctf.h	Wed Nov 04 12:07:58 2020 +0000
@@ -25,6 +25,7 @@
  */
 /*
  * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 /*
@@ -37,7 +38,7 @@
  * In the meantime, be aware that any program linked with libctf in this
  * 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.
+ * In short, do not use this header file or libctf for any purpose.
  */
 
 #ifndef	_LIBCTF_H
@@ -75,14 +76,44 @@
 extern int ctf_diff_objects(ctf_diff_t *, ctf_diff_obj_f, void *);
 extern void ctf_diff_fini(ctf_diff_t *);
 
-/*
- * Normally, we return a failure if we find a C-derived compilation unit that
- * lacks DWARF or CTF (as required).  This flag over-rides this check.
- */
-#define	CTF_ALLOW_MISSING_DEBUG	0x01
+#define	CTF_CONVERT_DEFAULT_BATCHSIZE	256
+#define	CTF_CONVERT_DEFAULT_NTHREADS	4
+
+typedef enum ctf_convert_flag {
+	/*
+	 * Normally, we return a failure if we find a C-derived compilation
+	 * unit that lacks DWARF or CTF (as required).  This flag over-rides
+	 * this check.
+	 */
+	CTF_ALLOW_MISSING_DEBUG	 = 0x01,
+	/*
+	 * Normally, we return a failure if we can't fully convert a structure
+	 * to CTF format, such as an enum with too many values. This flag
+	 * allows us to continue and convert what we can.
+	 */
+	CTF_ALLOW_TRUNCATION = 0x02
+} ctf_convert_flag_t;
 
-extern ctf_file_t *ctf_fdconvert(int, const char *, uint_t, uint_t, uint_t,
-    int *, char *, size_t);
+#define	CTF_CONVERT_ALL_FLAGS	(CTF_ALLOW_MISSING_DEBUG | \
+				    CTF_ALLOW_TRUNCATION)
+
+/* opaque handle for ctfconvert functions */
+struct ctf_convert_handle;
+typedef struct ctf_convert_handle ctf_convert_t;
+
+extern ctf_convert_t *ctf_convert_init(int *);
+extern void ctf_convert_fini(ctf_convert_t *);
+
+typedef void (*ctf_convert_warn_f)(void *, const char *, ...);
+/* Any warning callback must be MT-Safe if multiple threads are used */
+extern int ctf_convert_set_warncb(ctf_convert_t *, ctf_convert_warn_f, void *);
+extern int ctf_convert_set_batchsize(ctf_convert_t *, uint_t);
+extern int ctf_convert_set_flags(ctf_convert_t *, ctf_convert_flag_t);
+extern int ctf_convert_set_label(ctf_convert_t *, const char *);
+extern int ctf_convert_set_nthreads(ctf_convert_t *, uint_t);
+
+extern ctf_file_t *ctf_fdconvert(ctf_convert_t *, int, int *, char *, size_t);
+
 
 typedef enum ctf_hsc_ret {
 	CHR_ERROR = -1,
--- a/usr/src/lib/libctf/common/libctf_impl.h	Tue Nov 10 02:25:25 2020 +0000
+++ b/usr/src/lib/libctf/common/libctf_impl.h	Wed Nov 04 12:07:58 2020 +0000
@@ -11,6 +11,7 @@
 
 /*
  * Copyright 2019 Joyent, Inc.
+ * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
  */
 
 #ifndef _LIBCTF_IMPL_H
@@ -29,10 +30,19 @@
 extern "C" {
 #endif
 
-typedef int (*ctf_convert_f)(int, Elf *, uint_t, uint_t, uint_t,
-    ctf_file_t **, char *, size_t);
-extern int ctf_dwarf_convert(int, Elf *, uint_t, uint_t, uint_t,
-    ctf_file_t **, char *, size_t);
+struct ctf_convert_handle {
+	char *cch_label;
+	uint_t cch_flags;
+	uint_t cch_nthreads;
+	uint_t cch_batchsize;
+	ctf_convert_warn_f cch_warncb;
+	void *cch_warncb_arg;
+};
+
+typedef int (*ctf_convert_f)(ctf_convert_t *, int, Elf *, ctf_file_t **,
+    char *, size_t);
+extern int ctf_dwarf_convert(ctf_convert_t *, int, Elf *, ctf_file_t **,
+    char *, size_t);
 
 /*
  * Symbol walking
--- a/usr/src/lib/libctf/common/mapfile-vers	Tue Nov 10 02:25:25 2020 +0000
+++ b/usr/src/lib/libctf/common/mapfile-vers	Wed Nov 04 12:07:58 2020 +0000
@@ -24,6 +24,7 @@
 
 #
 # Copyright 2019 Joyent, Inc.
+# Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 #
 
 #
@@ -66,6 +67,13 @@
 	ctf_add_typedef;
 	ctf_add_union;
 	ctf_add_volatile;
+	ctf_convert_fini;
+	ctf_convert_init;
+	ctf_convert_set_batchsize;
+	ctf_convert_set_flags;
+	ctf_convert_set_label;
+	ctf_convert_set_nthreads;
+	ctf_convert_set_warncb;
 	ctf_create;
 	ctf_dataptr;
 	ctf_delete_type;
--- a/usr/src/tools/scripts/ctfconvert.1onbld	Tue Nov 10 02:25:25 2020 +0000
+++ b/usr/src/tools/scripts/ctfconvert.1onbld	Wed Nov 04 12:07:58 2020 +0000
@@ -10,7 +10,7 @@
 .\"
 .\" Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 .\"
-.Dd October 23, 2020
+.Dd November 04, 2020
 .Dt ctfconvert 1ONBLD
 .Os
 .Sh NAME
@@ -18,7 +18,7 @@
 .Nd Convert ELF object debug data to CTF container
 .Sh SYNOPSIS
 .Nm
-.Op Fl ikm
+.Op Fl ikms
 .Op Fl b Ar batchsize
 .Op Fl j Ar threads
 .Op Fl l Ar label | Fl L Ar labelenv
@@ -74,7 +74,7 @@
 Note that if the file contains no debug data in any of the compilation units
 then this flag will cause
 .Nm
-to exit successfully without taking any action, and can mask missing CTF data.
+to exit successfully without taking any action, and can mask missing debug data.
 .It Fl b Ar batchsize
 Batch-process this many compilation units from the source file at once (default
 256). This helps to reduce memory usage when processing large objects which
@@ -89,6 +89,17 @@
 .It Fl o Ar outfile
 Write the new object with added CTF ta to the specified output file, rather
 than updating the input in-place.
+.It Fl s
+This option allows truncation of data that cannot be converted to CTF format
+because it exceeds the allowed size.
+Without this option being provided, conversion of such data would produce a
+fatal error.
+The current implementation allows truncation of
+.Vt enum
+definitions.
+When this occurs the resulting CTF data does not contain all possible
+.Vt enum
+values and a warning will be produced for each truncation.
 .El
 .Sh OPERANDS
 The following operands are supported:
@@ -110,6 +121,11 @@
 .Nm
 is
 .Sy Uncommitted .
+The output of
+.Nm
+is
+.Sy Not-An-Interface
+and may change at any time.
 .Sh SEE ALSO
 .Xr ctfdiff 1 ,
 .Xr ctfdump 1 ,