--- old/usr/src/cmd/ctfdump/ctfdump.c	Fri Apr 26 04:01:28 2019
+++ new/usr/src/cmd/ctfdump/ctfdump.c	Fri Apr 26 04:01:28 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 /*
@@ -685,7 +685,7 @@
  * - function arguments aren't expanded recursively
  */
 
-static void
+static const char *
 ctfsrc_refname(ctf_id_t id, char *buf, size_t bufsize)
 {
 	ctf_id_t ref;
@@ -695,7 +695,7 @@
 		    "%s\n", id, ctf_errmsg(ctf_errno(g_fp)));
 	}
 
-	(void) ctf_type_name(g_fp, ref, buf, bufsize);
+	return (ctf_type_name(g_fp, ref, buf, bufsize));
 }
 
 static int
@@ -718,10 +718,10 @@
 	 * aligned (i.e. a bitfield).
 	 */
 	if (off % NBBY != 0) {
-		(void) printf("\t%s; /* offset: %lu bytes (%lu bits) */\n",
+		printf("\t%s; /* offset: %lu bytes (%lu bits) */\n",
 		    name, off / NBBY, off);
 	} else {
-		(void) printf("\t%s; /* offset: %lu bytes */\n",
+		printf("\t%s; /* offset: %lu bytes */\n",
 		    name, off / NBBY);
 	}
 	return (0);
@@ -731,7 +731,7 @@
 ctfsrc_enum_cb(const char *name, int value, void *arg)
 {
 	_NOTE(ARGUNUSED(arg));
-	(void) printf("\t%s = %d,\n", name, value);
+	printf("\t%s = %d,\n", name, value);
 	return (0);
 }
 
@@ -756,7 +756,7 @@
 static void
 ctfsrc_type(ctf_id_t id, const char *name)
 {
-	char refname[MAX_NAMELEN];
+	char refname[MAX_NAMELEN] = "unknown_t";
 	ctf_id_t ref;
 	ssize_t size;
 	int kind;
@@ -781,7 +781,7 @@
 			    ctf_errmsg(ctf_errno(g_fp)));
 		}
 
-		(void) printf("%s { /* 0x%x bytes */\n", name, size);
+		printf("%s { /* 0x%x bytes */\n", name, size);
 
 		if (ctf_member_iter(g_fp, id, ctfsrc_member_cb, NULL) != 0) {
 			ctfdump_fatal("failed to iterate members of %s: %s\n",
@@ -788,7 +788,7 @@
 			    name, ctf_errmsg(ctf_errno(g_fp)));
 		}
 
-		(void) printf("};\n\n");
+		printf("};\n\n");
 		break;
 	case CTF_K_ENUM:
 		/*
@@ -798,7 +798,7 @@
 		if (is_anon_refname(name))
 			break;
 
-		(void) printf("%s {\n", name);
+		printf("%s {\n", name);
 
 		if (ctf_enum_iter(g_fp, id, ctfsrc_enum_cb, NULL) != 0) {
 			ctfdump_fatal("failed to iterate enumerators of %s: "
@@ -805,10 +805,17 @@
 			    "%s\n", name, ctf_errmsg(ctf_errno(g_fp)));
 		}
 
-		(void) printf("};\n\n");
+		printf("};\n\n");
 		break;
 	case CTF_K_TYPEDEF:
-		ctfsrc_refname(id, refname, sizeof (refname));
+		/*
+		 * If this fails, it's probably because the referent type is in
+		 * a parent container that was not supplied via -p.
+		 */
+		if (ctfsrc_refname(id, refname, sizeof (refname)) == NULL) {
+			printf("typedef %s %s;\n\n", refname, name);
+			break;
+		}
 
 		if (!is_anon_refname(refname)) {
 			(void) ctf_type_cname(g_fp,
@@ -815,7 +822,7 @@
 			    ctf_type_reference(g_fp, id), refname,
 			    sizeof (refname), name);
 
-			(void) printf("typedef %s;\n\n", refname);
+			printf("typedef %s;\n\n", refname);
 			break;
 		}
 
@@ -822,7 +829,7 @@
 		ref = ctf_type_reference(g_fp, id);
 
 		if (ctf_type_kind(g_fp, ref) == CTF_K_ENUM) {
-			(void) printf("typedef enum {\n");
+			printf("typedef enum {\n");
 
 			if (ctf_enum_iter(g_fp, ref,
 			    ctfsrc_enum_cb, NULL) != 0) {
@@ -831,7 +838,7 @@
 				    ctf_errmsg(ctf_errno(g_fp)));
 			}
 
-			(void) printf("} %s;\n\n", name);
+			printf("} %s;\n\n", name);
 		} else {
 			if ((size = ctf_type_size(g_fp, ref)) == CTF_ERR) {
 				ctfdump_fatal("failed to get size of %s: %s\n",
@@ -838,7 +845,7 @@
 				    refname, ctf_errmsg(ctf_errno(g_fp)));
 			}
 
-			(void) printf("typedef %s{ /* 0x%zx bytes */\n",
+			printf("typedef %s{ /* 0x%zx bytes */\n",
 			    refname, size);
 
 			if (ctf_member_iter(g_fp, ref,
@@ -848,12 +855,12 @@
 				    ctf_errmsg(ctf_errno(g_fp)));
 			}
 
-			(void) printf("} %s;\n\n", name);
+			printf("} %s;\n\n", name);
 		}
 
 		break;
 	case CTF_K_FORWARD:
-		(void) printf("%s;\n\n", name);
+		printf("%s;\n\n", name);
 		break;
 	case CTF_K_UNKNOWN:
 	case CTF_K_INTEGER:
@@ -903,7 +910,7 @@
 		(void) snprintf(tname, sizeof (tname), "unknown_t %s", name);
 	}
 
-	(void) printf("extern %s;\n", tname);
+	printf("extern %s;\n", tname);
 }
 
 static int
@@ -929,7 +936,7 @@
 
 	(void) ctf_type_name(g_fp, cfi->ctc_return, name, sizeof (name));
 
-	(void) printf("extern %s %s(", name, idn->ci_name);
+	printf("extern %s %s(", name, idn->ci_name);
 
 	if (cfi->ctc_argc != 0) {
 		ctfdump_fargs_grow(cfi->ctc_argc);
@@ -943,22 +950,22 @@
 		for (size_t i = 0; i < cfi->ctc_argc; i++) {
 			ctf_id_t aid = g_fargc[i];
 
-			name[0] = '\0';
+			(void) strlcpy(name, "unknown_t", sizeof (name));
 
 			(void) ctf_type_name(g_fp, aid, name, sizeof (name));
 
-			(void) printf("%s%s", name,
+			printf("%s%s", name,
 			    i + 1 == cfi->ctc_argc ? "" : ", ");
 		}
 	} else {
 		if (!(cfi->ctc_flags & CTF_FUNC_VARARG))
-			(void) printf("void");
+			printf("void");
 	}
 
 	if (cfi->ctc_flags & CTF_FUNC_VARARG)
-		(void) printf("%s...", cfi->ctc_argc == 0 ? "" : ", ");
+		printf("%s...", cfi->ctc_argc == 0 ? "" : ", ");
 
-	(void) printf(");\n");
+	printf(");\n");
 }
 
 static int
@@ -975,7 +982,7 @@
 	ctf_id_t max_id = ctf_max_id(g_fp);
 	size_t count = 0;
 
-	(void) printf("/* Types */\n\n");
+	printf("/* Types */\n\n");
 
 	if ((idnames = calloc(max_id + 1, sizeof (idnames[0]))) == NULL) {
 		ctfdump_fatal("failed to alloc idnames: %s\n",
@@ -982,6 +989,15 @@
 		    strerror(errno));
 	}
 
+	/*
+	 * Prep for any unknown types (most likely, they exist in the parent,
+	 * but we weren't given the -p option).
+	 */
+	for (size_t i = 0; i <= max_id; i++) {
+		(void) strlcpy(idnames[i].ci_name, "unknown_t",
+		    sizeof (idnames[i].ci_name));
+	}
+
 	if (ctf_type_iter(g_fp, B_TRUE, ctfsrc_collect_types_cb,
 	    idnames) == CTF_ERR) {
 		warnx("failed to collect types: %s",
@@ -991,7 +1007,7 @@
 
 	qsort(idnames, max_id, sizeof (ctf_idname_t), idname_compare);
 
-	for (size_t i = 0; i < max_id; i++) {
+	for (size_t i = 0; i <= max_id; i++) {
 		if (idnames[i].ci_id != 0)
 			ctfsrc_type(idnames[i].ci_id, idnames[i].ci_name);
 	}
@@ -998,7 +1014,7 @@
 
 	free(idnames);
 
-	(void) printf("\n\n/* Data Objects */\n\n");
+	printf("\n\n/* Data Objects */\n\n");
 
 	if ((idnames = calloc(nr_syms, sizeof (idnames[0]))) == NULL) {
 		ctfdump_fatal("failed to alloc idnames: %s\n",
@@ -1019,7 +1035,7 @@
 
 	free(idnames);
 
-	(void) printf("\n\n/* Functions */\n\n");
+	printf("\n\n/* Functions */\n\n");
 
 	if ((idnames = calloc(nr_syms, sizeof (idnames[0]))) == NULL) {
 		ctfdump_fatal("failed to alloc idnames: %s\n",
@@ -1196,6 +1212,16 @@
 		if (ctf_import(g_fp, pfp) != 0)
 			ctfdump_fatal("failed to import parent %s: %s\n",
 			    parent, ctf_errmsg(ctf_errno(g_fp)));
+	} else {
+		if (g_dump & CTFDUMP_SOURCE) {
+			printf("/* Warning: parent \"%s\" not supplied: many "
+			    "types will be unknown. */\n\n",
+			    ctf_parent_name(g_fp));
+		} else {
+			fprintf(stderr, "warning: parent \"%s\" not supplied: "
+			    "many types will be unknown\n\n",
+			    ctf_parent_name(g_fp));
+		}
 	}
 
 	if (g_dump & CTFDUMP_SOURCE) {
--- old/usr/src/common/ctf/ctf_impl.h	Fri Apr 26 04:01:28 2019
+++ new/usr/src/common/ctf/ctf_impl.h	Fri Apr 26 04:01:28 2019
@@ -25,7 +25,7 @@
  * Use is subject to license terms.
  */
 /*
- * Copyright (c) 2015, Joyent, Inc.  All rights reserved.
+ * Copyright 2019, Joyent, Inc.
  */
 
 #ifndef	_CTF_IMPL_H
@@ -315,6 +315,9 @@
 extern boolean_t ctf_sym_valid(uintptr_t, int, uint16_t, uint64_t,
     uint32_t);
 
+extern const ctf_type_t *ctf_dyn_lookup_by_id(ctf_file_t *, ctf_id_t);
+extern int ctf_dyn_array_info(ctf_file_t *, ctf_id_t, ctf_arinfo_t *);
+
 extern const char _CTF_SECTION[];	/* name of CTF ELF section */
 extern const char _CTF_NULLSTR[];	/* empty string */
 
--- old/usr/src/common/ctf/ctf_lookup.c	Fri Apr 26 04:01:28 2019
+++ new/usr/src/common/ctf/ctf_lookup.c	Fri Apr 26 04:01:28 2019
@@ -25,7 +25,9 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
+/*
+ * Copyright 2019, Joyent, Inc.
+ */
 
 #include <sys/sysmacros.h>
 #include <ctf_impl.h>
@@ -311,3 +313,50 @@
 
 	return (0);
 }
+
+/*
+ * Unlike the normal lookup routines, ctf_dyn_*() variants consult both the
+ * processed CTF contents of a ctf_file_t as well as the dynamic types in the
+ * dtdef list.
+ */
+
+const ctf_type_t *
+ctf_dyn_lookup_by_id(ctf_file_t *fp, ctf_id_t id)
+{
+	ctf_file_t **fpp = &fp;
+	const ctf_type_t *t;
+	ctf_dtdef_t *dtd;
+
+	if ((t = ctf_lookup_by_id(fpp, id)) != NULL)
+		return (t);
+
+	if ((dtd = ctf_dtd_lookup(fp, id)) == NULL)
+		return (NULL);
+
+	return (&dtd->dtd_data);
+}
+
+int
+ctf_dyn_array_info(ctf_file_t *infp, ctf_id_t id, ctf_arinfo_t *arinfop)
+{
+	ctf_file_t *fp = infp;
+	const ctf_type_t *t;
+	ctf_dtdef_t *dtd;
+
+	if ((t = ctf_lookup_by_id(&fp, id)) != NULL) {
+
+		if (LCTF_INFO_KIND(fp, t->ctt_info) != CTF_K_ARRAY)
+			return (ctf_set_errno(infp, ECTF_NOTARRAY));
+
+		return (ctf_array_info(fp, id, arinfop));
+	}
+
+	if ((dtd = ctf_dtd_lookup(fp, id)) == NULL)
+		return (ctf_set_errno(infp, ENOENT));
+
+	if (LCTF_INFO_KIND(fp, dtd->dtd_data.ctt_info) != CTF_K_ARRAY)
+		return (ctf_set_errno(infp, ECTF_NOTARRAY));
+
+	bcopy(&dtd->dtd_u.dtu_arr, arinfop, sizeof (*arinfop));
+	return (0);
+}
--- old/usr/src/lib/libctf/common/ctf_dwarf.c	Fri Apr 26 04:01:29 2019
+++ new/usr/src/lib/libctf/common/ctf_dwarf.c	Fri Apr 26 04:01:29 2019
@@ -28,7 +28,7 @@
  */
 
 /*
- * Copyright 2019 Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 /*
@@ -1485,7 +1485,76 @@
 	return (ctf_dwmap_add(cup, *idp, die, B_FALSE));
 }
 
+/*
+ * Given "const int const_array3[11]", GCC7 at least will create a DIE tree of
+ * DW_TAG_const_type:DW_TAG_array_type:DW_Tag_const_type:<member_type>.
+ *
+ * Given C's syntax, this renders out as "const const int const_array3[11]".  To
+ * get closer to round-tripping (and make the unit tests work), we'll peek for
+ * this case, and avoid adding the extraneous qualifier if we see that the
+ * underlying array referent already has the same qualifier.
+ *
+ * This is unfortunately less trivial than it could be: this issue applies to
+ * qualifier sets like "const volatile", as well as multi-dimensional arrays, so
+ * we need to descend down those.
+ *
+ * Returns CTF_ERR on error, or a boolean value otherwise.
+ */
 static int
+needed_array_qualifier(ctf_cu_t *cup, int kind, ctf_id_t ref_id)
+{
+	const ctf_type_t *t;
+	ctf_arinfo_t arinfo;
+	int akind;
+
+	if (kind != CTF_K_CONST && kind != CTF_K_VOLATILE &&
+	    kind != CTF_K_RESTRICT)
+		return (1);
+
+	if ((t = ctf_dyn_lookup_by_id(cup->cu_ctfp, ref_id)) == NULL)
+		return (CTF_ERR);
+
+	if (LCTF_INFO_KIND(cup->cu_ctfp, t->ctt_info) != CTF_K_ARRAY)
+		return (1);
+
+	if (ctf_dyn_array_info(cup->cu_ctfp, ref_id, &arinfo) != 0)
+		return (CTF_ERR);
+
+	ctf_id_t id = arinfo.ctr_contents;
+
+	for (;;) {
+		if ((t = ctf_dyn_lookup_by_id(cup->cu_ctfp, id)) == NULL)
+			return (CTF_ERR);
+
+		akind = LCTF_INFO_KIND(cup->cu_ctfp, t->ctt_info);
+
+		if (akind == kind)
+			break;
+
+		if (akind == CTF_K_ARRAY) {
+			if (ctf_dyn_array_info(cup->cu_ctfp,
+			    id, &arinfo) != 0)
+				return (CTF_ERR);
+			id = arinfo.ctr_contents;
+			continue;
+		}
+
+		if (akind != CTF_K_CONST && akind != CTF_K_VOLATILE &&
+		    akind != CTF_K_RESTRICT)
+			break;
+
+		id = t->ctt_type;
+	}
+
+	if (kind == akind) {
+		ctf_dprintf("ignoring extraneous %s qualifier for array %d\n",
+		    ctf_kind_name(cup->cu_ctfp, kind), ref_id);
+	}
+
+	return (kind != akind);
+}
+
+static int
 ctf_dwarf_create_reference(ctf_cu_t *cup, Dwarf_Die die, ctf_id_t *idp,
     int kind, int isroot)
 {
@@ -1524,6 +1593,17 @@
 		}
 	}
 
+	if ((ret = needed_array_qualifier(cup, kind, id)) <= 0) {
+		if (ret != 0) {
+			ret = (ctf_errno(cup->cu_ctfp));
+		} else {
+			*idp = id;
+		}
+
+		ctf_free(name, namelen);
+		return (ret);
+	}
+
 	if ((*idp = ctf_add_reftype(cup->cu_ctfp, isroot, name, id, kind)) ==
 	    CTF_ERR) {
 		ctf_free(name, namelen);
@@ -1821,7 +1901,7 @@
 			return (ret);
 
 		/*
-		 * We have to check for a varargs type decleration. This will
+		 * We have to check for a varargs type declaration. 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
@@ -1906,10 +1986,11 @@
 static int
 ctf_dwarf_convert_function(ctf_cu_t *cup, Dwarf_Die die)
 {
-	int ret;
-	char *name;
 	ctf_dwfunc_t *cdf;
 	Dwarf_Die tdie;
+	Dwarf_Bool b;
+	char *name;
+	int ret;
 
 	/*
 	 * Functions that don't have a name are generally functions that have
@@ -1923,7 +2004,25 @@
 		return (ret);
 	}
 
-	ctf_dprintf("beginning work on function %s\n", name);
+	ctf_dprintf("beginning work on function %s (die %llx)\n",
+	    name, ctf_die_offset(die));
+
+	if ((ret = ctf_dwarf_boolean(cup, die, DW_AT_declaration, &b)) != 0) {
+		if (ret != ENOENT)
+			return (ret);
+	} else if (b != 0) {
+		/*
+		 * GCC7 at least creates empty DW_AT_declarations for functions
+		 * defined in headers.  As they lack details on the function
+		 * prototype, we need to ignore them.  If we later actually
+		 * see the relevant function's definition, we will see another
+		 * DW_TAG_subprogram that is more complete.
+		 */
+		ctf_dprintf("ignoring declaration of function %s (die %llx)\n",
+		    name, ctf_die_offset(die));
+		return (0);
+	}
+
 	if ((cdf = ctf_alloc(sizeof (ctf_dwfunc_t))) == NULL) {
 		ctf_free(name, strlen(name) + 1);
 		return (ENOMEM);
--- old/usr/src/pkg/manifests/system-test-utiltest.mf	Fri Apr 26 04:01:29 2019
+++ new/usr/src/pkg/manifests/system-test-utiltest.mf	Fri Apr 26 04:01:29 2019
@@ -13,7 +13,7 @@
 # Copyright (c) 2012 by Delphix. All rights reserved.
 # Copyright 2014, OmniTI Computer Consulting, Inc. All rights reserved.
 # Copyright 2014 Nexenta Systems, Inc. All rights reserved.
-# Copyright 2017 Joyent, Inc.
+# Copyright 2019, Joyent, Inc.
 # Copyright 2017 Jason King.
 #
 
@@ -27,6 +27,12 @@
 dir path=opt/util-tests/bin
 dir path=opt/util-tests/runfiles
 dir path=opt/util-tests/tests
+dir path=opt/util-tests/tests/ctf
+dir path=opt/util-tests/tests/ctf/test-merge-dedup
+dir path=opt/util-tests/tests/ctf/test-merge-forward
+dir path=opt/util-tests/tests/ctf/test-merge-reduction
+dir path=opt/util-tests/tests/ctf/test-merge-static
+dir path=opt/util-tests/tests/ctf/test-merge-weak
 dir path=opt/util-tests/tests/demangle
 dir path=opt/util-tests/tests/dis
 dir path=opt/util-tests/tests/dis/i386
@@ -43,6 +49,68 @@
 file path=opt/util-tests/runfiles/default.run mode=0444
 file path=opt/util-tests/tests/allowed-ips mode=0555
 file path=opt/util-tests/tests/chown_test mode=0555
+file path=opt/util-tests/tests/ctf/Makefile.ctftest.com mode=0555
+file path=opt/util-tests/tests/ctf/check-array mode=0555
+file path=opt/util-tests/tests/ctf/check-enum mode=0555
+file path=opt/util-tests/tests/ctf/check-float-32 mode=0555
+file path=opt/util-tests/tests/ctf/check-float-64 mode=0555
+file path=opt/util-tests/tests/ctf/check-forward-32 mode=0555
+file path=opt/util-tests/tests/ctf/check-forward-64 mode=0555
+file path=opt/util-tests/tests/ctf/check-function mode=0555
+file path=opt/util-tests/tests/ctf/check-int-32 mode=0555
+file path=opt/util-tests/tests/ctf/check-int-64 mode=0555
+file path=opt/util-tests/tests/ctf/check-merge-dedup mode=0555
+file path=opt/util-tests/tests/ctf/check-merge-forward-32 mode=0555
+file path=opt/util-tests/tests/ctf/check-merge-forward-64 mode=0555
+file path=opt/util-tests/tests/ctf/check-merge-reduction mode=0555
+file path=opt/util-tests/tests/ctf/check-merge-static mode=0555
+file path=opt/util-tests/tests/ctf/check-merge-weak mode=0555
+file path=opt/util-tests/tests/ctf/check-qualifiers mode=0555
+file path=opt/util-tests/tests/ctf/check-reference mode=0555
+file path=opt/util-tests/tests/ctf/check-sou-32 mode=0555
+file path=opt/util-tests/tests/ctf/check-sou-64 mode=0555
+file path=opt/util-tests/tests/ctf/check-weak mode=0555
+file path=opt/util-tests/tests/ctf/ctftest mode=0555
+file path=opt/util-tests/tests/ctf/ctftest-convert-no-dwarf mode=0555
+file path=opt/util-tests/tests/ctf/ctftest-convert-non-c mode=0555
+file path=opt/util-tests/tests/ctf/ctftest-merge-no-ctf mode=0555
+file path=opt/util-tests/tests/ctf/precheck mode=0555
+file path=opt/util-tests/tests/ctf/test-array.c mode=0555
+file path=opt/util-tests/tests/ctf/test-enum.c mode=0555
+file path=opt/util-tests/tests/ctf/test-float.c mode=0555
+file path=opt/util-tests/tests/ctf/test-forward.c mode=0555
+file path=opt/util-tests/tests/ctf/test-function.c mode=0555
+file path=opt/util-tests/tests/ctf/test-int.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-dedup/Makefile.ctftest mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-dedup/test-merge-1.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-dedup/test-merge-2.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-dedup/test-merge-3.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-dedup/test-merge-dedup.c \
+    mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-forward/Makefile.ctftest \
+    mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-forward/test-impl.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-forward/test-merge.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-reduction/Makefile.ctftest \
+    mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-reduction/mapfile-vers mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-reduction/test-global.c \
+    mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-reduction/test-scoped.c \
+    mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-static/Makefile.ctftest \
+    mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-static/test-a.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-static/test-b.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-static/test-c.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-static/test-d.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-static/test-main.c mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-weak/Makefile.ctftest mode=0555
+file path=opt/util-tests/tests/ctf/test-merge-weak/test-merge-weak.c mode=0555
+file path=opt/util-tests/tests/ctf/test-qualifiers.c mode=0555
+file path=opt/util-tests/tests/ctf/test-reference.c mode=0555
+file path=opt/util-tests/tests/ctf/test-sou.c mode=0555
+file path=opt/util-tests/tests/ctf/test-weak.c mode=0555
 file path=opt/util-tests/tests/date_test mode=0555
 file path=opt/util-tests/tests/demangle/afl-fast mode=0555
 file path=opt/util-tests/tests/demangle/gcc-libstdc++ mode=0555
--- old/usr/src/test/util-tests/tests/ctf/Makefile	Fri Apr 26 04:01:29 2019
+++ new/usr/src/test/util-tests/tests/ctf/Makefile	Fri Apr 26 04:01:29 2019
@@ -10,7 +10,7 @@
 #
 
 #
-# Copyright (c) 2019, Joyent, Inc.
+# Copyright 2019, Joyent, Inc.
 #
 
 include $(SRC)/Makefile.master
@@ -32,6 +32,7 @@
 		test-forward.c \
 		test-sou.c \
 		test-function.c \
+		test-qualifiers.c \
 		test-merge-static/Makefile.ctftest \
 		test-merge-static/test-a.c \
 		test-merge-static/test-b.c \
@@ -73,6 +74,7 @@
 		check-forward-32 \
 		check-forward-64 \
 		check-function \
+		check-qualifiers \
 		check-merge-static \
 		check-merge-forward-32 \
 		check-merge-forward-64 \
@@ -96,6 +98,8 @@
 include $(SRC)/cmd/Makefile.cmd
 include $(SRC)/test/Makefile.com
 
+CSTD = $(CSTD_GNU99)
+
 LDLIBS +=	-lctf
 
 check-merge-static :=	LDLIBS += -lelf
--- old/usr/src/test/util-tests/tests/ctf/check-array.c	Fri Apr 26 04:01:29 2019
+++ new/usr/src/test/util-tests/tests/ctf/check-array.c	Fri Apr 26 04:01:29 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 /*
@@ -105,7 +105,7 @@
 			ret = EXIT_FAILURE;
 		for (d = 0; descents[d].cdt_sym != NULL; d++) {
 			if (!ctftest_check_descent(descents[d].cdt_sym, fp,
-			    descents[d].cdt_tests)) {
+			    descents[d].cdt_tests, B_FALSE)) {
 				ret = EXIT_FAILURE;
 			}
 		}
--- old/usr/src/test/util-tests/tests/ctf/check-common.c	Fri Apr 26 04:01:30 2019
+++ new/usr/src/test/util-tests/tests/ctf/check-common.c	Fri Apr 26 04:01:30 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 /*
@@ -248,7 +248,7 @@
 
 boolean_t
 ctftest_check_descent(const char *symbol, ctf_file_t *fp,
-    const check_descent_t *tests)
+    const check_descent_t *tests, boolean_t quiet)
 {
 	ctf_id_t base;
 	uint_t layer = 0;
@@ -268,30 +268,42 @@
 		ctf_arinfo_t ari;
 
 		if (base == CTF_ERR) {
-			warnx("encountered non-reference type at layer %u "
-			    "while still expecting type %s for symbol %s",
-			    layer, tests->cd_tname, symbol);
+			if (!quiet) {
+				warnx("encountered non-reference type at layer "
+				    "%u while still expecting type %s for "
+				    "symbol %s", layer,
+				    tests->cd_tname, symbol);
+			}
 			return (B_FALSE);
 		}
 
 		tid = ctftest_lookup_type(fp, tests->cd_tname);
 		if (tid == CTF_ERR) {
-			warnx("failed to lookup type %s", tests->cd_tname);
+			if (!quiet) {
+				warnx("failed to lookup type %s",
+				    tests->cd_tname);
+			}
 			return (B_FALSE);
 		}
 
 		if (tid != base) {
-			warnx("type mismatch at layer %u: found id %u, but "
-			    "expecting type id %u for type %s, symbol %s",
-			    layer, base, tid, tests->cd_tname, symbol);
+			if (!quiet) {
+				warnx("type mismatch at layer %u: found id %u, "
+				    "but expecting type id %u for type %s, "
+				    "symbol %s", layer, base, tid,
+				    tests->cd_tname, symbol);
+			}
 			return (B_FALSE);
 		}
 
 		kind = ctf_type_kind(fp, base);
 		if (kind != tests->cd_kind) {
-			warnx("type kind mismatch at layer %u: found kind %u, "
-			    "but expected kind %u for %s, symbol %s", layer,
-			    kind, tests->cd_kind, tests->cd_tname, symbol);
+			if (!quiet) {
+				warnx("type kind mismatch at layer %u: found "
+				    "kind %u, but expected kind %u for %s, "
+				    "symbol %s", layer, kind, tests->cd_kind,
+				    tests->cd_tname, symbol);
+			}
 			return (B_FALSE);
 		}
 
@@ -298,35 +310,44 @@
 		switch (kind) {
 		case CTF_K_ARRAY:
 			if (ctf_array_info(fp, base, &ari) == CTF_ERR) {
-				warnx("failed to lookup array info at layer "
-				    "%u for type %s, symbol %s: %s", base,
-				    tests->cd_tname, symbol,
-				    ctf_errmsg(ctf_errno(fp)));
+				if (!quiet) {
+					warnx("failed to lookup array info at "
+					    "layer %u for type %s, symbol "
+					    "%s: %s", base, tests->cd_tname,
+					    symbol, ctf_errmsg(ctf_errno(fp)));
+				}
 				return (B_FALSE);
 			}
 
 			if (tests->cd_nents != ari.ctr_nelems) {
-				warnx("array element mismatch at layer %u "
-				    "for type %s, symbol %s: found %u, "
-				    "expected %u", layer, tests->cd_tname,
-				    symbol, ari.ctr_nelems, tests->cd_nents);
+				if (!quiet) {
+					warnx("array element mismatch at layer "
+					    "%u for type %s, symbol %s: found "
+					    "%u, expected %u", layer,
+					    tests->cd_tname, symbol,
+					    ari.ctr_nelems, tests->cd_nents);
+				}
 				return (B_FALSE);
 			}
 
 			tid = ctftest_lookup_type(fp, tests->cd_contents);
 			if (tid == CTF_ERR) {
-				warnx("failed to look up type %s",
-				    tests->cd_contents);
+				if (!quiet) {
+					warnx("failed to look up type %s",
+					    tests->cd_contents);
+				}
 				return (B_FALSE);
 			}
 
 			if (ari.ctr_contents != tid) {
-				warnx("array contents mismatch at layer %u "
-				    "for type %s, symbol %s: found %u, "
-				    "expected %s/%u", layer, tests->cd_tname,
-				    symbol, ari.ctr_contents,
-				    tests->cd_contents, tid);
-
+				if (!quiet) {
+					warnx("array contents mismatch at "
+					    "layer %u for type %s, symbol %s: "
+					    "found %u, expected %s/%u", layer,
+					    tests->cd_tname, symbol,
+					    ari.ctr_contents,
+					    tests->cd_contents, tid);
+				}
 				return (B_FALSE);
 			}
 			base = ari.ctr_contents;
@@ -341,8 +362,10 @@
 	}
 
 	if (base != CTF_ERR) {
-		warnx("found additional type %u in chain, but expected no more",
-		    base);
+		if (!quiet) {
+			warnx("found additional type %u in chain, "
+			    "but expected no more", base);
+		}
 		return (B_FALSE);
 	}
 
--- old/usr/src/test/util-tests/tests/ctf/check-common.h	Fri Apr 26 04:01:30 2019
+++ new/usr/src/test/util-tests/tests/ctf/check-common.h	Fri Apr 26 04:01:30 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 #ifndef _CHECK_COMMON_H
@@ -103,7 +103,7 @@
  * type and checks against it with each subsequent entry.
  */
 extern boolean_t ctftest_check_descent(const char *, ctf_file_t *,
-    const check_descent_t *);
+    const check_descent_t *, boolean_t);
 
 /*
  * Checks that all of the listed members of an enum are present and have the
--- old/usr/src/test/util-tests/tests/ctf/check-enum.c	Fri Apr 26 04:01:30 2019
+++ new/usr/src/test/util-tests/tests/ctf/check-enum.c	Fri Apr 26 04:01:30 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 /*
@@ -124,7 +124,7 @@
 			ret = EXIT_FAILURE;
 		for (d = 0; descents[d].cdt_sym != NULL; d++) {
 			if (!ctftest_check_descent(descents[d].cdt_sym, fp,
-			    descents[d].cdt_tests)) {
+			    descents[d].cdt_tests, B_FALSE)) {
 				ret = EXIT_FAILURE;
 			}
 		}
--- old/usr/src/test/util-tests/tests/ctf/check-forward.c	Fri Apr 26 04:01:30 2019
+++ new/usr/src/test/util-tests/tests/ctf/check-forward.c	Fri Apr 26 04:01:30 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 /*
@@ -109,7 +109,7 @@
 
 		for (j = 0; descents[j].cdt_sym != NULL; j++) {
 			if (!ctftest_check_descent(descents[j].cdt_sym, fp,
-			    descents[j].cdt_tests)) {
+			    descents[j].cdt_tests, B_FALSE)) {
 				ret = EXIT_FAILURE;
 			}
 		}
--- /dev/null	Fri Apr 26 04:01:31 2019
+++ new/usr/src/test/util-tests/tests/ctf/check-qualifiers.c	Fri Apr 26 04:01:31 2019
@@ -0,0 +1,318 @@
+/*
+ * 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 2019, Joyent, Inc.
+ */
+
+/*
+ * Check qualifier encoding. Note that the needed_qualifier() workaround applies
+ * to most of these.
+ */
+
+#include "check-common.h"
+
+static check_descent_t check_descent_const_union_array_gcc4[] = {
+	{ "const union const_union [5]", CTF_K_CONST },
+	{ "union const_union [5]", CTF_K_ARRAY, "union const_union", 5 },
+	{ "union const_union", CTF_K_UNION },
+	{ NULL }
+};
+
+static check_descent_t check_descent_const_union_array_gcc7[] = {
+	{ "const union const_union [5]", CTF_K_ARRAY,
+	    "const union const_union", 5 },
+	{ "const union const_union", CTF_K_CONST },
+	{ "union const_union", CTF_K_UNION },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_const_union_array[] = {
+	{ "const_union_array", check_descent_const_union_array_gcc4 },
+	{ "const_union_array", check_descent_const_union_array_gcc7 },
+	{ NULL }
+};
+
+static check_descent_t check_descent_const_struct_array_gcc4[] = {
+	{ "const struct const_struct [7]", CTF_K_CONST },
+	{ "struct const_struct [7]", CTF_K_ARRAY, "struct const_struct", 7 },
+	{ "struct const_struct", CTF_K_STRUCT },
+	{ NULL }
+};
+
+static check_descent_t check_descent_const_struct_array_gcc7[] = {
+	{ "const struct const_struct [7]", CTF_K_ARRAY,
+	    "const struct const_struct", 7 },
+	{ "const struct const_struct", CTF_K_CONST },
+	{ "struct const_struct", CTF_K_STRUCT },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_const_struct_array[] = {
+	{ "const_struct_array", check_descent_const_struct_array_gcc4 },
+	{ "const_struct_array", check_descent_const_struct_array_gcc7 },
+	{ NULL }
+};
+
+static check_descent_t check_descent_volatile_struct_array_gcc4[] = {
+	{ "volatile struct volatile_struct [9]", CTF_K_VOLATILE },
+	{ "struct volatile_struct [9]", CTF_K_ARRAY,
+	    "struct volatile_struct", 9 },
+	{ "struct volatile_struct", CTF_K_STRUCT },
+	{ NULL }
+};
+
+static check_descent_t check_descent_volatile_struct_array_gcc7[] = {
+	{ "volatile struct volatile_struct [9]", CTF_K_ARRAY,
+	    "volatile struct volatile_struct", 9 },
+	{ "volatile struct volatile_struct", CTF_K_VOLATILE },
+	{ "struct volatile_struct", CTF_K_STRUCT },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_volatile_struct_array[] = {
+	{ "volatile_struct_array", check_descent_volatile_struct_array_gcc4 },
+	{ "volatile_struct_array", check_descent_volatile_struct_array_gcc7 },
+	{ NULL }
+};
+
+static check_descent_t check_descent_c_int_array_gcc4[] = {
+	{ "const int [11]", CTF_K_CONST },
+	{ "int [11]", CTF_K_ARRAY, "int", 11 },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_t check_descent_c_int_array_gcc7[] = {
+	{ "const int [11]", CTF_K_ARRAY, "const int", 11 },
+	{ "const int", CTF_K_CONST },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_c_int_array[] = {
+	{ "c_int_array", check_descent_c_int_array_gcc4 },
+	{ "c_int_array", check_descent_c_int_array_gcc7 },
+	{ NULL }
+};
+
+static check_descent_t check_descent_cv_int_array_gcc4[] = {
+	{ "const volatile int [13]", CTF_K_CONST },
+	{ "volatile int [13]", CTF_K_VOLATILE },
+	{ "int [13]", CTF_K_ARRAY, "int", 13 },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_t check_descent_cv_int_array_gcc7[] = {
+	{ "volatile const int [13]", CTF_K_ARRAY, "volatile const int", 13 },
+	{ "volatile const int", CTF_K_VOLATILE },
+	{ "const int", CTF_K_CONST },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_cv_int_array[] = {
+	{ "cv_int_array", check_descent_cv_int_array_gcc4 },
+	{ "cv_int_array", check_descent_cv_int_array_gcc7 },
+	{ NULL }
+};
+
+static check_descent_t check_descent_vc_int_array_gcc4[] = {
+	{ "const volatile int [15]", CTF_K_CONST },
+	{ "volatile int [15]", CTF_K_VOLATILE },
+	{ "int [15]", CTF_K_ARRAY, "int", 15 },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_t check_descent_vc_int_array_gcc7[] = {
+	{ "volatile const int [15]", CTF_K_ARRAY, "volatile const int", 15 },
+	{ "volatile const int", CTF_K_VOLATILE },
+	{ "const int", CTF_K_CONST },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_vc_int_array[] = {
+	{ "vc_int_array", check_descent_vc_int_array_gcc4 },
+	{ "vc_int_array", check_descent_vc_int_array_gcc7 },
+	{ NULL }
+};
+
+static check_descent_t check_descent_vc_int_array2_gcc4[] = {
+	{ "const volatile int [17]", CTF_K_CONST },
+	{ "volatile int [17]", CTF_K_VOLATILE },
+	{ "int [17]", CTF_K_ARRAY, "int", 17 },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_t check_descent_vc_int_array2_gcc7[] = {
+	{ "volatile const int [17]", CTF_K_ARRAY, "volatile const int", 17 },
+	{ "volatile const int", CTF_K_VOLATILE },
+	{ "const int", CTF_K_CONST },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_vc_int_array2[] = {
+	{ "vc_int_array2", check_descent_vc_int_array2_gcc4 },
+	{ "vc_int_array2", check_descent_vc_int_array2_gcc7 },
+	{ NULL }
+};
+
+static check_descent_t check_descent_c_2d_array_gcc4[] = {
+	{ "const int [4][2]", CTF_K_CONST },
+	{ "int [4][2]", CTF_K_ARRAY, "int [2]", 4 },
+	{ "int [2]", CTF_K_ARRAY, "int", 2 },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_t check_descent_c_2d_array_gcc7[] = {
+	{ "const int [4][2]", CTF_K_ARRAY, "const int [2]", 4 },
+	{ "const int [2]", CTF_K_ARRAY, "const int", 2 },
+	{ "const int", CTF_K_CONST },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_c_2d_array[] = {
+	{ "c_2d_array", check_descent_c_2d_array_gcc4 },
+	{ "c_2d_array", check_descent_c_2d_array_gcc7 },
+	{ NULL }
+};
+
+static check_descent_t check_descent_cv_3d_array_gcc4[] = {
+	{ "const volatile int [3][2][1]", CTF_K_CONST },
+	{ "volatile int [3][2][1]", CTF_K_VOLATILE },
+	{ "int [3][2][1]", CTF_K_ARRAY, "int [2][1]", 3 },
+	{ "int [2][1]", CTF_K_ARRAY, "int [1]", 2 },
+	{ "int [1]", CTF_K_ARRAY, "int", 1 },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_t check_descent_cv_3d_array_gcc7[] = {
+	{ "volatile const int [3][2][1]", CTF_K_ARRAY,
+	    "volatile const int [2][1]", 3 },
+	{ "volatile const int [2][1]", CTF_K_ARRAY,
+	    "volatile const int [1]", 2 },
+	{ "volatile const int [1]", CTF_K_ARRAY, "volatile const int", 1 },
+	{ "volatile const int", CTF_K_VOLATILE },
+	{ "const int", CTF_K_CONST },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_cv_3d_array[] = {
+	{ "cv_3d_array", check_descent_cv_3d_array_gcc4 },
+	{ "cv_3d_array", check_descent_cv_3d_array_gcc7 },
+	{ NULL }
+};
+
+static check_descent_t check_descent_ptr_to_const_int[] = {
+	{ "const int *", CTF_K_POINTER },
+	{ "const int", CTF_K_CONST },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_ptr_to_const_int[] = {
+	{ "ptr_to_const_int", check_descent_ptr_to_const_int },
+	{ NULL }
+};
+
+static check_descent_t check_descent_const_ptr_to_int[] = {
+	{ "int *const", CTF_K_CONST },
+	{ "int *", CTF_K_POINTER },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_const_ptr_to_int[] = {
+	{ "const_ptr_to_int", check_descent_const_ptr_to_int },
+	{ NULL }
+};
+
+static check_descent_t check_descent_const_ptr_to_const_int[] = {
+	{ "const int *const", CTF_K_CONST },
+	{ "const int *", CTF_K_POINTER },
+	{ "const int", CTF_K_CONST },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_test_t alt_descents_const_ptr_to_const_int[] = {
+	{ "const_ptr_to_const_int", check_descent_const_ptr_to_const_int },
+	{ NULL }
+};
+
+static check_descent_test_t *alt_descents[] = {
+	alt_descents_const_union_array,
+	alt_descents_const_struct_array,
+	alt_descents_volatile_struct_array,
+	alt_descents_c_int_array,
+	alt_descents_cv_int_array,
+	alt_descents_vc_int_array,
+	alt_descents_vc_int_array2,
+	alt_descents_c_2d_array,
+	alt_descents_cv_3d_array,
+	alt_descents_ptr_to_const_int,
+	alt_descents_const_ptr_to_int,
+	alt_descents_const_ptr_to_const_int,
+	NULL
+};
+
+int
+main(int argc, char *argv[])
+{
+	int i, ret = 0;
+
+	if (argc < 2) {
+		errx(EXIT_FAILURE, "missing test files");
+	}
+
+	for (i = 1; i < argc; i++) {
+		ctf_file_t *fp;
+
+		if ((fp = ctf_open(argv[i], &ret)) == NULL) {
+			warnx("failed to open %s: %s", argv[i],
+			    ctf_errmsg(ret));
+			ret = EXIT_FAILURE;
+			continue;
+		}
+
+		for (uint_t j = 0; alt_descents[j] != NULL; j++) {
+			check_descent_test_t *descents = alt_descents[j];
+			int alt_ok = 0;
+
+			for (uint_t k = 0; descents[k].cdt_sym != NULL; k++) {
+				if (ctftest_check_descent(descents[k].cdt_sym,
+				    fp, descents[k].cdt_tests, B_TRUE)) {
+					alt_ok = 1;
+					break;
+				}
+			}
+
+			if (!alt_ok) {
+				warnx("all descents failed for %s",
+				    descents[0].cdt_sym);
+				ret = EXIT_FAILURE;
+			}
+		}
+
+		ctf_close(fp);
+	}
+
+	return (ret);
+}
--- old/usr/src/test/util-tests/tests/ctf/check-reference.c	Fri Apr 26 04:01:31 2019
+++ new/usr/src/test/util-tests/tests/ctf/check-reference.c	Fri Apr 26 04:01:31 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 /*
@@ -131,18 +131,6 @@
 	{ NULL }
 };
 
-static check_descent_t check_descent_cvh[] = {
-	{ "const volatile foo_t *", CTF_K_POINTER },
-	{ "const volatile foo_t", CTF_K_CONST },
-	{ "volatile foo_t", CTF_K_VOLATILE },
-	{ "foo_t", CTF_K_TYPEDEF },
-	{ "int *const *", CTF_K_POINTER },
-	{ "int *const", CTF_K_CONST },
-	{ "int *", CTF_K_POINTER },
-	{ "int", CTF_K_INTEGER },
-	{ NULL }
-};
-
 static check_descent_test_t descents[] = {
 	{ "aa", check_descent_aa },
 	{ "b", check_descent_b },
@@ -156,10 +144,43 @@
 	{ "cve", check_descent_cve },
 	{ "f", check_descent_f },
 	{ "g", check_descent_g },
-	{ "cvh", check_descent_cvh },
 	{ NULL }
 };
 
+static check_descent_t check_descent_cvh_gcc4[] = {
+	{ "const volatile foo_t *", CTF_K_POINTER },
+	{ "const volatile foo_t", CTF_K_CONST },
+	{ "volatile foo_t", CTF_K_VOLATILE },
+	{ "foo_t", CTF_K_TYPEDEF },
+	{ "int *const *", CTF_K_POINTER },
+	{ "int *const", CTF_K_CONST },
+	{ "int *", CTF_K_POINTER },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+static check_descent_t check_descent_cvh_gcc7[] = {
+	{ "volatile const foo_t *", CTF_K_POINTER },
+	{ "volatile const foo_t", CTF_K_VOLATILE },
+	{ "const foo_t", CTF_K_CONST },
+	{ "foo_t", CTF_K_TYPEDEF },
+	{ "int *const *", CTF_K_POINTER },
+	{ "int *const", CTF_K_CONST },
+	{ "int *", CTF_K_POINTER },
+	{ "int", CTF_K_INTEGER },
+	{ NULL }
+};
+
+/*
+ * GCC versions differ in how they order qualifiers, which is a shame for
+ * round-tripping; but as they're clearly both valid, we should cope.  We'll
+ * just insist that at least one of these checks passes.
+ */
+static check_descent_test_t alt_descents[] = {
+	{ "cvh", check_descent_cvh_gcc4 },
+	{ "cvh", check_descent_cvh_gcc7 },
+};
+
 int
 main(int argc, char *argv[])
 {
@@ -171,6 +192,7 @@
 
 	for (i = 1; i < argc; i++) {
 		ctf_file_t *fp;
+		int alt_ok = 0;
 		uint_t d;
 
 		if ((fp = ctf_open(argv[i], &ret)) == NULL) {
@@ -186,10 +208,25 @@
 			ret = EXIT_FAILURE;
 		for (d = 0; descents[d].cdt_sym != NULL; d++) {
 			if (!ctftest_check_descent(descents[d].cdt_sym, fp,
-			    descents[d].cdt_tests)) {
+			    descents[d].cdt_tests, B_FALSE)) {
 				ret = EXIT_FAILURE;
 			}
 		}
+
+		for (d = 0; alt_descents[d].cdt_sym != NULL; d++) {
+			if (ctftest_check_descent(alt_descents[d].cdt_sym, fp,
+			    alt_descents[d].cdt_tests, B_TRUE)) {
+				alt_ok = 1;
+				break;
+			}
+		}
+
+		if (!alt_ok) {
+			warnx("all descents failed for %s",
+			    alt_descents[0].cdt_sym);
+			ret = EXIT_FAILURE;
+		}
+
 		ctf_close(fp);
 	}
 
--- old/usr/src/test/util-tests/tests/ctf/check-sou.c	Fri Apr 26 04:01:31 2019
+++ new/usr/src/test/util-tests/tests/ctf/check-sou.c	Fri Apr 26 04:01:31 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 /*
@@ -345,7 +345,13 @@
 	{ NULL }
 };
 
-static check_descent_t check_descent_regress[] = {
+static check_descent_test_t descents[] = {
+	{ "head", check_descent_head },
+	{ "forward", check_descent_forward },
+	{ NULL }
+};
+
+static check_descent_t check_descent_regress_gcc4[] = {
 	{ "const union regress [9]", CTF_K_CONST },
 	{ "union regress [9]", CTF_K_ARRAY, "union regress", 9 },
 	{ "union regress", CTF_K_UNION },
@@ -352,13 +358,23 @@
 	{ NULL }
 };
 
-static check_descent_test_t descents[] = {
-	{ "head", check_descent_head },
-	{ "forward", check_descent_forward },
-	{ "regress", check_descent_regress },
+static check_descent_t check_descent_regress_gcc7[] = {
+	{ "const union regress [9]", CTF_K_ARRAY, "const union regress", 9 },
+	{ "const union regress", CTF_K_CONST },
+	{ "union regress", CTF_K_UNION },
 	{ NULL }
 };
 
+/*
+ * See needed_array_qualifier(): applying this fix means the qualifier order is
+ * different between GCC versions. Accept either form.
+ */
+static check_descent_test_t alt_descents[] = {
+	{ "regress", check_descent_regress_gcc4 },
+	{ "regress", check_descent_regress_gcc7 },
+	{ NULL }
+};
+
 int
 main(int argc, char *argv[])
 {
@@ -370,6 +386,7 @@
 
 	for (i = 1; i < argc; i++) {
 		ctf_file_t *fp;
+		int alt_ok = 0;
 		uint_t j;
 
 		if ((fp = ctf_open(argv[i], &ret)) == NULL) {
@@ -385,11 +402,25 @@
 			ret = EXIT_FAILURE;
 		for (j = 0; descents[j].cdt_sym != NULL; j++) {
 			if (!ctftest_check_descent(descents[j].cdt_sym, fp,
-			    descents[j].cdt_tests)) {
+			    descents[j].cdt_tests, B_FALSE)) {
 				ret = EXIT_FAILURE;
 			}
 		}
 
+		for (j = 0; alt_descents[j].cdt_sym != NULL; j++) {
+			if (ctftest_check_descent(alt_descents[j].cdt_sym, fp,
+			    alt_descents[j].cdt_tests, B_TRUE)) {
+				alt_ok = 1;
+				break;
+			}
+		}
+
+		if (!alt_ok) {
+			warnx("all descents failed for %s",
+			    alt_descents[0].cdt_sym);
+			ret = EXIT_FAILURE;
+		}
+
 		for (j = 0; members[j].cmt_type != NULL; j++) {
 			if (!ctftest_check_members(members[j].cmt_type, fp,
 			    members[j].cmt_kind, members[j].cmt_size,
--- old/usr/src/test/util-tests/tests/ctf/test-merge-weak/test-merge-weak.c	Fri Apr 26 04:01:31 2019
+++ new/usr/src/test/util-tests/tests/ctf/test-merge-weak/test-merge-weak.c	Fri Apr 26 04:01:31 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2019, Joyent, Inc.
+ * Copyright 2019, Joyent, Inc.
  */
 
 #include <stdlib.h>
@@ -26,6 +26,8 @@
 	return ((int)arc4random());
 }
 
+extern int mumble(void);
+
 int
 main(void)
 {
--- /dev/null	Fri Apr 26 04:01:31 2019
+++ new/usr/src/test/util-tests/tests/ctf/test-qualifiers.c	Fri Apr 26 04:01:31 2019
@@ -0,0 +1,42 @@
+/*
+ * 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 2019, Joyent, Inc.
+ */
+
+/*
+ * Make sure that we're encoding qualifiers correctly.
+ */
+
+const union const_union {
+	int i;
+} const_union_array[5];
+
+const struct const_struct {
+	int i;
+} const_struct_array[7];
+
+volatile struct volatile_struct {
+	int i;
+} volatile_struct_array[9];
+
+const int c_int_array[11];
+const volatile int cv_int_array[13];
+volatile const int vc_int_array[15];
+volatile int const vc_int_array2[17];
+
+const int c_2d_array[4][2];
+const volatile int cv_3d_array[3][2][1];
+
+const int *ptr_to_const_int;
+int * const const_ptr_to_int;
+const int * const const_ptr_to_const_int;
