--- old/usr/src/lib/libctf/common/ctf_dwarf.c	Thu Apr 25 02:20:43 2019
+++ new/usr/src/lib/libctf/common/ctf_dwarf.c	Thu Apr 25 02:20:43 2019
@@ -883,7 +883,7 @@
 		break;
 	default:
 		(void) snprintf(cup->cu_errbuf, cup->cu_errlen,
-		    "encountered unkown DWARF encoding: %d", type);
+		    "encountered unknown DWARF encoding: %d", type);
 		return (ECTF_CONVBKERR);
 	}
 
@@ -1778,6 +1778,7 @@
 		break;
 	default:
 		ctf_dprintf("ignoring tag type %x\n", tag);
+		*idp = CTF_ERR;
 		ret = 0;
 		break;
 	}
@@ -2677,7 +2678,8 @@
 	}
 
 	ctf_dprintf("Trying to clean up dwarf_t: %p\n", cup->cu_dwarf);
-	(void) dwarf_finish(cup->cu_dwarf, &derr);
+	if (cup->cu_dwarf != NULL)
+		(void) dwarf_finish(cup->cu_dwarf, &derr);
 	cup->cu_dwarf = NULL;
 	ctf_close(cup->cu_ctfp);
 
@@ -2727,13 +2729,6 @@
 		*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);
 }
 
@@ -2769,10 +2764,9 @@
 		cup->cu_elf = elf;
 		cup->cu_maxoff = nexthdr - 1;
 		cup->cu_ctfp = ctf_fdcreate(fd, &ret);
-		if (cup->cu_ctfp == NULL) {
-			ctf_free(cup, sizeof (ctf_cu_t));
+		if (cup->cu_ctfp == NULL)
 			return (ret);
-		}
+
 		avl_create(&cup->cu_map, ctf_dwmap_comp, sizeof (ctf_dwmap_t),
 		    offsetof(ctf_dwmap_t, cdm_avl));
 		cup->cu_errbuf = errbuf;
@@ -2782,46 +2776,32 @@
 		bzero(&cup->cu_bitfields, sizeof (ctf_list_t));
 
 		if ((ret = ctf_dwarf_die_elfenc(elf, cup, errbuf,
-		    errlen)) != 0) {
-			avl_destroy(&cup->cu_map);
-			ctf_free(cup, sizeof (ctf_cu_t));
+		    errlen)) != 0)
 			return (ret);
-		}
 
-		if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0) {
-			avl_destroy(&cup->cu_map);
-			ctf_free(cup, sizeof (ctf_cu_t));
+		if ((ret = ctf_dwarf_sib(cup, NULL, &cu)) != 0)
 			return (ret);
-		}
+
 		if (cu == NULL) {
 			(void) snprintf(errbuf, errlen,
-			    "file does not contain DWARF data\n");
-			avl_destroy(&cup->cu_map);
-			ctf_free(cup, sizeof (ctf_cu_t));
-			return (ECTF_CONVBKERR);
+			    "file does not contain DWARF data");
+			return (ECTF_CONVNODEBUG);
 		}
 
-		if ((ret = ctf_dwarf_child(cup, cu, &child)) != 0) {
-			avl_destroy(&cup->cu_map);
-			ctf_free(cup, sizeof (ctf_cu_t));
+		if ((ret = ctf_dwarf_child(cup, cu, &child)) != 0)
 			return (ret);
-		}
+
 		if (child == NULL) {
 			(void) snprintf(errbuf, errlen,
-			    "file does not contain DWARF data\n");
-			avl_destroy(&cup->cu_map);
-			ctf_free(cup, sizeof (ctf_cu_t));
-			return (ECTF_CONVBKERR);
+			    "file does not contain DWARF data");
+			return (ECTF_CONVNODEBUG);
 		}
 
 		cup->cu_cuoff = offset;
 		cup->cu_cu = child;
 
-		if ((cup->cu_cmh = ctf_merge_init(fd, &ret)) == NULL) {
-			avl_destroy(&cup->cu_map);
-			ctf_free(cup, sizeof (ctf_cu_t));
+		if ((cup->cu_cmh = ctf_merge_init(fd, &ret)) == NULL)
 			return (ret);
-		}
 
 		if (ctf_dwarf_string(cup, cu, DW_AT_name, &name) == 0) {
 			size_t len = strlen(name) + 1;
@@ -2835,11 +2815,123 @@
 	return (0);
 }
 
+/*
+ * This is our only recourse to identify a C source file that is missing debug
+ * info: it will be mentioned as an STT_FILE, but not have a compile unit entry.
+ * (A traditional ctfmerge works on individual files, so can identify missing
+ * DWARF more directly, via ctf_has_c_source() on the .o file.)
+ *
+ * As we operate on basenames, this can of course miss some cases, but it's
+ * better than not checking at all.
+ *
+ * We explicitly whitelist some CRT components.  Failing that, there's always
+ * the -m option.
+ */
+static boolean_t
+c_source_has_debug(const char *file, ctf_cu_t *cus, size_t nr_cus)
+{
+	const char *basename = strrchr(file, '/');
 
-ctf_conv_status_t
-ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, int *errp, ctf_file_t **fpp,
+	if (basename == NULL)
+		basename = file;
+	else
+		basename++;
+
+	if (strcmp(basename, "common-crt.c") == 0 ||
+	    strcmp(basename, "gmon.c") == 0 ||
+	    strcmp(basename, "dlink_init.c") == 0 ||
+	    strcmp(basename, "dlink_common.c") == 0 ||
+	    strncmp(basename, "crt", strlen("crt")) == 0 ||
+	    strncmp(basename, "values-", strlen("values-")) == 0)
+		return (B_TRUE);
+
+	for (size_t i = 0; i < nr_cus; i++) {
+		if (strcmp(basename, cus[i].cu_name) == 0)
+			return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
+static int
+ctf_dwarf_check_missing(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;
+
+	scn = NULL;
+	while ((scn = elf_nextscn(elf, scn)) != NULL) {
+		if (gelf_getshdr(scn, &shdr) == NULL) {
+			(void) snprintf(errmsg, errlen,
+			    "failed to get section header: %s\n",
+			    elf_errmsg(elf_errno()));
+			return (EINVAL);
+		}
+
+		if (shdr.sh_type == SHT_SYMTAB)
+			break;
+	}
+
+	if (scn == NULL)
+		return (0);
+
+	if ((strscn = elf_getscn(elf, shdr.sh_link)) == NULL) {
+		(void) snprintf(errmsg, errlen,
+		    "failed to get str section: %s\n",
+		    elf_errmsg(elf_errno()));
+		return (EINVAL);
+	}
+
+	if ((data = elf_getdata(scn, NULL)) == NULL) {
+		(void) snprintf(errmsg, errlen, "failed to read section: %s\n",
+		    elf_errmsg(elf_errno()));
+		return (EINVAL);
+	}
+
+	if ((strdata = elf_getdata(strscn, NULL)) == NULL) {
+		(void) snprintf(errmsg, errlen,
+		    "failed to read string table: %s\n",
+		    elf_errmsg(elf_errno()));
+		return (EINVAL);
+	}
+
+	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) {
+			(void) snprintf(errmsg, errlen,
+			    "failed to read sym %lu: %s\n",
+			    i, elf_errmsg(elf_errno()));
+			return (EINVAL);
+		}
+
+		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 || strncmp(".c", &file[len - 2], 2) != 0)
+			continue;
+
+		if (!c_source_has_debug(file, cus, nr_cus)) {
+			(void) snprintf(errmsg, errlen,
+			    "file %s is missing debug info\n", file);
+			return (ECTF_CONVNODEBUG);
+		}
+	}
+
+	return (0);
+}
+
+int
+ctf_dwarf_convert(int fd, Elf *elf, uint_t nthrs, uint_t flags,
+    ctf_file_t **fpp, char *errbuf, size_t errlen)
+{
 	int err, ret, ndies, i;
 	Dwarf_Debug dw;
 	Dwarf_Error derr;
@@ -2846,30 +2938,20 @@
 	ctf_cu_t *cdies = NULL, *cup;
 	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) {
-		/*
-		 * We may want to expect DWARF data here and fail conversion if
-		 * it's missing. 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(errbuf, errlen,
+			    "file does not contain DWARF data\n");
+			return (ECTF_CONVNODEBUG);
 		}
-		(void) snprintf(errmsg, errlen,
-		    "failed to initialize DWARF: %s\n",
-		    dwarf_errmsg(derr));
-		*errp = ECTF_CONVBKERR;
-		return (CTF_CONV_ERROR);
+
+		(void) snprintf(errbuf, errlen,
+		    "dwarf_elf_init() failed: %s\n", dwarf_errmsg(derr));
+		return (ECTF_CONVBKERR);
 	}
 
 	/*
@@ -2880,19 +2962,24 @@
 	 * only a single Dwarf_Debug as well.
 	 */
 	ndies = 0;
-	ret = ctf_dwarf_count_dies(dw, &derr, &ndies, errmsg, errlen);
-	if (ret != 0) {
-		*errp = ret;
-		goto out;
+	err = ctf_dwarf_count_dies(dw, &derr, &ndies, errbuf, errlen);
+
+	ctf_dprintf("found %d DWARF CUs\n", ndies);
+
+	if (ndies == 0) {
+		(void) snprintf(errbuf, errlen,
+		    "file does not contain DWARF data\n");
+		return (ECTF_CONVNODEBUG);
 	}
 
 	(void) dwarf_finish(dw, &derr);
 	cdies = ctf_alloc(sizeof (ctf_cu_t) * ndies);
 	if (cdies == NULL) {
-		*errp = ENOMEM;
-		return (CTF_CONV_ERROR);
+		return (ENOMEM);
 	}
 
+	bzero(cdies, sizeof (ctf_cu_t) * ndies);
+
 	for (i = 0; i < ndies; i++) {
 		cup = &cdies[i];
 		ret = dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL,
@@ -2899,23 +2986,23 @@
 		    &cup->cu_dwarf, &derr);
 		if (ret != 0) {
 			ctf_free(cdies, sizeof (ctf_cu_t) * ndies);
-			(void) snprintf(errmsg, errlen,
+			(void) snprintf(errbuf, errlen,
 			    "failed to initialize DWARF: %s\n",
 			    dwarf_errmsg(derr));
-			*errp = ECTF_CONVBKERR;
-			return (CTF_CONV_ERROR);
+			return (ECTF_CONVBKERR);
 		}
 
-		ret = ctf_dwarf_init_die(fd, elf, &cdies[i], i, errmsg, errlen);
-		if (ret != 0) {
-			*errp = ret;
+		err = ctf_dwarf_init_die(fd, elf, cup, i, errbuf, errlen);
+		if (err != 0)
 			goto out;
-		}
 
 		cup->cu_doweaks = ndies > 1 ? B_FALSE : B_TRUE;
 	}
 
-	ctf_dprintf("found %d DWARF CUs\n", ndies);
+	if (!(flags & CTF_ALLOW_MISSING_DEBUG) &&
+	    (err = ctf_dwarf_check_missing(cdies, ndies,
+	    elf, errbuf, errlen)) != 0)
+		goto out;
 
 	/*
 	 * If we only have one compilation unit, there's no reason to use
@@ -2926,7 +3013,7 @@
 		nthrs = 1;
 
 	if (workq_init(&wqp, nthrs) == -1) {
-		*errp = errno;
+		err = errno;
 		goto out;
 	}
 
@@ -2935,18 +3022,18 @@
 		ctf_dprintf("adding cu %s: %p, %x %x\n", cup->cu_name,
 		    cup->cu_cu, cup->cu_cuoff, cup->cu_maxoff);
 		if (workq_add(wqp, cup) == -1) {
-			*errp = errno;
+			err = errno;
 			goto out;
 		}
 	}
 
-	ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, errp);
+	ret = workq_work(wqp, ctf_dwarf_convert_one, NULL, &err);
 	if (ret == WORKQ_ERROR) {
-		*errp = errno;
+		err = errno;
 		goto out;
 	} else if (ret == WORKQ_UERROR) {
 		ctf_dprintf("internal convert failed: %s\n",
-		    ctf_errmsg(*errp));
+		    ctf_errmsg(err));
 		goto out;
 	}
 
@@ -2954,44 +3041,37 @@
 	if (ndies != 1) {
 		ctf_merge_t *cmp;
 
-		cmp = ctf_merge_init(fd, &ret);
-		if (cmp == NULL) {
-			*errp = ret;
+		cmp = ctf_merge_init(fd, &err);
+		if (cmp == NULL)
 			goto out;
-		}
 
 		ctf_dprintf("setting threads\n");
-		if ((ret = ctf_merge_set_nthreads(cmp, nthrs)) != 0) {
+		if ((err = ctf_merge_set_nthreads(cmp, nthrs)) != 0) {
 			ctf_merge_fini(cmp);
-			*errp = ret;
 			goto out;
 		}
 
 		for (i = 0; i < ndies; i++) {
 			cup = &cdies[i];
-			ctf_dprintf("adding cu %s (%p)\n", cup->cu_name,
-			    cup->cu_ctfp);
-			if ((ret = ctf_merge_add(cmp, cup->cu_ctfp)) != 0) {
+			if ((err = ctf_merge_add(cmp, cup->cu_ctfp)) != 0) {
 				ctf_merge_fini(cmp);
-				*errp = ret;
 				goto out;
 			}
 		}
 
 		ctf_dprintf("performing merge\n");
-		ret = ctf_merge_merge(cmp, fpp);
-		if (ret != 0) {
+		err = ctf_merge_merge(cmp, fpp);
+		if (err != 0) {
 			ctf_dprintf("failed merge!\n");
 			*fpp = NULL;
 			ctf_merge_fini(cmp);
-			*errp = ret;
 			goto out;
 		}
 		ctf_merge_fini(cmp);
-		*errp = 0;
+		err = 0;
 		ctf_dprintf("successfully converted!\n");
 	} else {
-		*errp = 0;
+		err = 0;
 		*fpp = cdies->cu_ctfp;
 		cdies->cu_ctfp = NULL;
 		ctf_dprintf("successfully converted!\n");
@@ -3000,5 +3080,5 @@
 out:
 	workq_fini(wqp);
 	ctf_dwarf_free_dies(cdies, ndies);
-	return (*fpp != NULL ? CTF_CONV_SUCCESS : CTF_CONV_ERROR);
+	return (err);
 }
