--- old/usr/src/common/ctf/ctf_create.c	Tue Apr 23 05:24:00 2019
+++ new/usr/src/common/ctf/ctf_create.c	Tue Apr 23 05:24:00 2019
@@ -25,7 +25,7 @@
  * Use is subject to license terms.
  */
 /*
- * Copyright (c) 2015, Joyent, Inc.
+ * Copyright (c) 2019, Joyent, Inc.
  */
 
 #include <sys/sysmacros.h>
@@ -2126,6 +2126,7 @@
 		return (ctf_set_errno(fp, EAGAIN));
 	}
 
+	ctf_dprintf("adding label %s, %ld\n", name, type);
 	dld->dld_type = type;
 	fp->ctf_dtstrlen += strlen(name) + 1;
 	ctf_dld_insert(fp, dld, position);
--- old/usr/src/lib/libctf/common/ctf_convert.c	Tue Apr 23 05:24:01 2019
+++ new/usr/src/lib/libctf/common/ctf_convert.c	Tue Apr 23 05:24:01 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
  */
 
 /*
@@ -168,9 +168,7 @@
 	/*
 	 * Succsesful conversion.
 	 */
-	if (fp != NULL) {
-		if (label == NULL)
-			label = "";
+	if (fp != NULL && label != NULL) {
 		if (ctf_add_label(fp, label, fp->ctf_typemax, 0) == CTF_ERR) {
 			*errp = ctf_errno(fp);
 			ctf_close(fp);
--- old/usr/src/lib/libctf/common/ctf_dwarf.c	Tue Apr 23 05:24:01 2019
+++ new/usr/src/lib/libctf/common/ctf_dwarf.c	Tue Apr 23 05:24:01 2019
@@ -28,7 +28,7 @@
  */
 
 /*
- * Copyright 2018 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
  */
 
 /*
@@ -282,9 +282,6 @@
 static int ctf_dwarf_convert_fargs(ctf_cu_t *, Dwarf_Die, ctf_funcinfo_t *,
     ctf_id_t *);
 
-typedef int (ctf_dwarf_symtab_f)(ctf_cu_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
@@ -2166,126 +2163,132 @@
 	return (0);
 }
 
+/*
+ * The DWARF information about a symbol and the information in the symbol table
+ * may not be the same due to symbol reduction that is performed by ld due to a
+ * mapfile or other such directive. We process weak symbols at a later time.
+ *
+ * The following are the rules that we employ:
+ *
+ * 1. A DWARF function that is considered exported matches STB_GLOBAL entries
+ * with the same name.
+ *
+ * 2. A DWARF function that is considered exported matches STB_LOCAL entries
+ * with the same name and the same file. This case may happen due to mapfile
+ * reduction.
+ *
+ * 3. A DWARF function that is not considered exported matches STB_LOCAL entries
+ * with the same name and the same file.
+ *
+ * 4. A DWARF function that has the same name as the symbol table entry, but the
+ * files do not match. This is considered a 'fuzzy' match. This may also happen
+ * due to a mapfile reduction. Fuzzy matching is only used when we know that the
+ * file in question refers to the primary object. This is because when a symbol
+ * is reduced in a mapfile, it's always going to be tagged as a local value in
+ * the generated output and it is considered as to belong to the primary file
+ * which is the first STT_FILE symbol we see.
+ */
+static boolean_t
+ctf_dwarf_symbol_match(const char *symtab_file, const char *symtab_name,
+    uint_t symtab_bind, const char *dwarf_file, const char *dwarf_name,
+    boolean_t dwarf_global, boolean_t *is_fuzzy)
+{
+	*is_fuzzy = B_FALSE;
+
+	if (symtab_bind != STB_LOCAL && symtab_bind != STB_GLOBAL) {
+		return (B_FALSE);
+	}
+
+	if (strcmp(symtab_name, dwarf_name) != 0) {
+		return (B_FALSE);
+	}
+
+	if (symtab_bind == STB_GLOBAL) {
+		return (dwarf_global);
+	}
+
+	if (strcmp(symtab_file, dwarf_file) == 0) {
+		return (B_TRUE);
+	}
+
+	if (dwarf_global) {
+		*is_fuzzy = B_TRUE;
+		return (B_TRUE);
+	}
+
+	return (B_FALSE);
+}
+
 static ctf_dwfunc_t *
 ctf_dwarf_match_func(ctf_cu_t *cup, const char *file, const char *name,
-    int bind)
+    uint_t bind, boolean_t primary)
 {
-	ctf_dwfunc_t *cdf;
+	ctf_dwfunc_t *cdf, *fuzzy = NULL;
 
 	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 || cup->cu_name == NULL))
 		return (NULL);
 
 	for (cdf = ctf_list_next(&cup->cu_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, cup->cu_name) != 0)
-			continue;
-		return (cdf);
+		boolean_t is_fuzzy = B_FALSE;
+
+		if (ctf_dwarf_symbol_match(file, name, bind, cup->cu_name,
+		    cdf->cdf_name, cdf->cdf_global, &is_fuzzy)) {
+			if (is_fuzzy) {
+				if (primary) {
+					fuzzy = cdf;
+				}
+				continue;
+			} else {
+				return (cdf);
+			}
+		}
 	}
 
-	return (NULL);
+	return (fuzzy);
 }
+
 static ctf_dwvar_t *
 ctf_dwarf_match_var(ctf_cu_t *cup, const char *file, const char *name,
-    int bind)
+    uint_t bind, boolean_t primary)
 {
-	ctf_dwvar_t *cdv;
+	ctf_dwvar_t *cdv, *fuzzy = NULL;
 
-	/* Nothing we can do if we can't find a name to compare it to. */
+	if (bind == STB_WEAK)
+		return (NULL);
+
 	if (bind == STB_LOCAL && (file == NULL || cup->cu_name == NULL))
 		return (NULL);
-	ctf_dprintf("Still considering %s\n", name);
 
 	for (cdv = ctf_list_next(&cup->cu_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, cup->cu_name) != 0)
-			continue;
-		return (cdv);
-	}
+		boolean_t is_fuzzy = B_FALSE;
 
-	return (NULL);
-}
-
-static int
-ctf_dwarf_symtab_iter(ctf_cu_t *cup, ctf_dwarf_symtab_f *func, void *arg)
-{
-	int ret;
-	ulong_t i;
-	ctf_file_t *fp = cup->cu_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 (ctf_dwarf_symbol_match(file, name, bind, cup->cu_name,
+		    cdv->cdv_name, cdv->cdv_global, &is_fuzzy)) {
+			if (is_fuzzy) {
+				if (primary) {
+					fuzzy = cdv;
+				}
+			} else {
+				return (cdv);
 			}
-			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(cup, gsymp, i, file, name, arg);
-		if (ret != 0)
-			return (ret);
 	}
 
-	return (0);
+	return (fuzzy);
 }
 
 static int
-ctf_dwarf_conv_funcvars_cb(ctf_cu_t *cup, const GElf_Sym *symp, ulong_t idx,
-    const char *file, const char *name, void *arg)
+ctf_dwarf_conv_funcvars_cb(const Elf64_Sym *symp, ulong_t idx,
+    const char *file, const char *name, boolean_t primary, void *arg)
 {
-	int ret, bind, type;
+	int ret;
+	uint_t bind, type;
+	ctf_cu_t *cup = arg;
 
 	bind = GELF_ST_BIND(symp->st_info);
 	type = GELF_ST_TYPE(symp->st_info);
@@ -2298,19 +2301,19 @@
 
 	if (type == STT_OBJECT) {
 		ctf_dwvar_t *cdv = ctf_dwarf_match_var(cup, file, name,
-		    bind);
-		ctf_dprintf("match for %s (%d): %p\n", name, idx, cdv);
+		    bind, primary);
 		if (cdv == NULL)
 			return (0);
 		ret = ctf_add_object(cup->cu_ctfp, idx, cdv->cdv_type);
-		ctf_dprintf("added object %s\n", name);
+		ctf_dprintf("added object %s->%ld\n", name, cdv->cdv_type);
 	} else {
 		ctf_dwfunc_t *cdf = ctf_dwarf_match_func(cup, file, name,
-		    bind);
+		    bind, primary);
 		if (cdf == NULL)
 			return (0);
 		ret = ctf_add_function(cup->cu_ctfp, idx, &cdf->cdf_fip,
 		    cdf->cdf_argv);
+		ctf_dprintf("added function %s\n", name);
 	}
 
 	if (ret == CTF_ERR) {
@@ -2323,7 +2326,7 @@
 static int
 ctf_dwarf_conv_funcvars(ctf_cu_t *cup)
 {
-	return (ctf_dwarf_symtab_iter(cup, ctf_dwarf_conv_funcvars_cb, NULL));
+	return (ctf_symtab_iter(cup->cu_ctfp, ctf_dwarf_conv_funcvars_cb, cup));
 }
 
 /*
@@ -2365,7 +2368,7 @@
  * that we can consume.
  */
 typedef struct ctf_dwarf_weak_arg {
-	const GElf_Sym *cweak_symp;
+	const Elf64_Sym *cweak_symp;
 	const char *cweak_file;
 	boolean_t cweak_candidate;
 	ulong_t cweak_idx;
@@ -2372,12 +2375,13 @@
 } ctf_dwarf_weak_arg_t;
 
 static int
-ctf_dwarf_conv_check_weak(ctf_cu_t *cup, const GElf_Sym *symp,
-    ulong_t idx, const char *file, const char *name, void *arg)
+ctf_dwarf_conv_check_weak(const Elf64_Sym *symp, ulong_t idx, const char *file,
+    const char *name, boolean_t primary, void *arg)
 {
 	ctf_dwarf_weak_arg_t *cweak = arg;
-	const GElf_Sym *wsymp = cweak->cweak_symp;
 
+	const Elf64_Sym *wsymp = cweak->cweak_symp;
+
 	ctf_dprintf("comparing weak to %s\n", name);
 
 	if (GELF_ST_BIND(symp->st_info) == STB_WEAK) {
@@ -2476,11 +2480,12 @@
 }
 
 static int
-ctf_dwarf_conv_weaks_cb(ctf_cu_t *cup, const GElf_Sym *symp,
-    ulong_t idx, const char *file, const char *name, void *arg)
+ctf_dwarf_conv_weaks_cb(const Elf64_Sym *symp, ulong_t idx, const char *file,
+    const char *name, boolean_t primary, void *arg)
 {
 	int ret, type;
 	ctf_dwarf_weak_arg_t cweak;
+	ctf_cu_t *cup = arg;
 
 	/*
 	 * We only care about weak symbols.
@@ -2503,7 +2508,7 @@
 
 	ctf_dprintf("Trying to find weak equiv for %s\n", name);
 
-	ret = ctf_dwarf_symtab_iter(cup, ctf_dwarf_conv_check_weak, &cweak);
+	ret = ctf_symtab_iter(cup->cu_ctfp, ctf_dwarf_conv_check_weak, &cweak);
 	VERIFY(ret == 0 || ret == 1);
 
 	/*
@@ -2518,6 +2523,7 @@
 	/*
 	 * Now, finally go and add the type based on the match.
 	 */
+	ctf_dprintf("matched weak symbol %lu to %lu\n", idx, cweak.cweak_idx);
 	if (type == STT_OBJECT) {
 		ret = ctf_dwarf_duplicate_sym(cup, idx, cweak.cweak_idx);
 	} else {
@@ -2530,7 +2536,7 @@
 static int
 ctf_dwarf_conv_weaks(ctf_cu_t *cup)
 {
-	return (ctf_dwarf_symtab_iter(cup, ctf_dwarf_conv_weaks_cb, NULL));
+	return (ctf_symtab_iter(cup->cu_ctfp, ctf_dwarf_conv_weaks_cb, cup));
 }
 
 /* ARGSUSED */
@@ -2601,7 +2607,7 @@
 		}
 	}
 
-	ctf_phase_dump(cup->cu_ctfp, "pre-dedup");
+	ctf_phase_dump(cup->cu_ctfp, "pre-dwarf-dedup", cup->cu_name);
 	ctf_dprintf("adding inputs for dedup\n");
 	if ((ret = ctf_merge_add(cup->cu_cmh, cup->cu_ctfp)) != 0) {
 		return (ctf_dwarf_error(cup, NULL, ret,
@@ -2608,7 +2614,7 @@
 		    "failed to add inputs for merge"));
 	}
 
-	ctf_dprintf("starting merge\n");
+	ctf_dprintf("starting dedup of %s\n", cup->cu_name);
 	if ((ret = ctf_merge_dedup(cup->cu_cmh, &dedup)) != 0) {
 		return (ctf_dwarf_error(cup, NULL, ret,
 		    "failed to deduplicate die"));
@@ -2615,6 +2621,7 @@
 	}
 	ctf_close(cup->cu_ctfp);
 	cup->cu_ctfp = dedup;
+	ctf_phase_dump(cup->cu_ctfp, "post-dwarf-dedup", cup->cu_name);
 
 	return (0);
 }
@@ -2904,10 +2911,11 @@
 			*errp = ret;
 			goto out;
 		}
+
 		cup->cu_doweaks = ndies > 1 ? B_FALSE : B_TRUE;
 	}
 
-	ctf_dprintf("found %d DWARF die(s)\n", ndies);
+	ctf_dprintf("found %d DWARF CUs\n", ndies);
 
 	/*
 	 * If we only have one compilation unit, there's no reason to use
@@ -2924,7 +2932,7 @@
 
 	for (i = 0; i < ndies; i++) {
 		cup = &cdies[i];
-		ctf_dprintf("adding die %s: %p, %x %x\n", cup->cu_name,
+		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;
@@ -2942,7 +2950,7 @@
 		goto out;
 	}
 
-	ctf_dprintf("Determining next phase: have %d dies\n", ndies);
+	ctf_dprintf("Determining next phase: have %d CUs\n", ndies);
 	if (ndies != 1) {
 		ctf_merge_t *cmp;
 
@@ -2959,9 +2967,10 @@
 			goto out;
 		}
 
-		ctf_dprintf("adding dies\n");
 		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) {
 				ctf_merge_fini(cmp);
 				*errp = ret;
--- old/usr/src/lib/libctf/common/ctf_lib.c	Tue Apr 23 05:24:01 2019
+++ new/usr/src/lib/libctf/common/ctf_lib.c	Tue Apr 23 05:24:01 2019
@@ -24,13 +24,13 @@
  * Use is subject to license terms.
  */
 /*
- * Copyright (c) 2015, Joyent, Inc.
+ * Copyright (c) 2019, Joyent, Inc.
  */
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
-#include <ctf_impl.h>
+#include <libctf_impl.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -747,7 +747,7 @@
  * A utility function for folks debugging CTF conversion and merging.
  */
 void
-ctf_phase_dump(ctf_file_t *fp, const char *phase)
+ctf_phase_dump(ctf_file_t *fp, const char *phase, const char *name)
 {
 	int fd;
 	static char *base;
@@ -756,7 +756,10 @@
 	if (base == NULL && (base = getenv("LIBCTF_WRITE_PHASES")) == NULL)
 		return;
 
-	(void) snprintf(path, sizeof (path), "%s/libctf.%s.%d.ctf", base,
+	if (name == NULL)
+		name = "libctf";
+
+	(void) snprintf(path, sizeof (path), "%s/%s.%s.%d.ctf", base, name,
 	    phase != NULL ? phase : "",
 	    ctf_phase);
 	if ((fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0777)) < 0)
@@ -763,4 +766,90 @@
 		return;
 	(void) ctf_write(fp, fd);
 	(void) close(fd);
+}
+
+void
+ctf_phase_bump(void)
+{
+	ctf_phase++;
+}
+
+int
+ctf_symtab_iter(ctf_file_t *fp, ctf_symtab_f func, void *arg)
+{
+	ulong_t i;
+	uintptr_t symbase;
+	uintptr_t strbase;
+	const char *file = NULL;
+	boolean_t primary = B_TRUE;
+
+	if (fp->ctf_symtab.cts_data == NULL ||
+	    fp->ctf_strtab.cts_data == NULL) {
+		return (ECTF_NOSYMTAB);
+	}
+
+	symbase = (uintptr_t)fp->ctf_symtab.cts_data;
+	strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+
+	for (i = 0; i < fp->ctf_nsyms; i++) {
+		const char *name;
+		int ret;
+		uint_t type;
+		Elf64_Sym sym;
+
+		/*
+		 * The CTF library has historically tried to handle large file
+		 * offsets itself so that way clients can be unaware of such
+		 * isseus. Therefore, we translate everything to a 64-bit ELF
+		 * symbol, this is done to make it so that the rest of the
+		 * library doesn't have to know about these differences. For
+		 * more information see, lib/libctf/common/ctf_lib.c.
+		 */
+		if (fp->ctf_symtab.cts_entsize == sizeof (Elf32_Sym)) {
+			const Elf32_Sym *symp = (Elf32_Sym *)symbase + i;
+			uint_t bind, itype;
+
+			sym.st_name = symp->st_name;
+			sym.st_value = symp->st_value;
+			sym.st_size = symp->st_size;
+			bind = ELF32_ST_BIND(symp->st_info);
+			itype = ELF32_ST_TYPE(symp->st_info);
+			sym.st_info = ELF64_ST_INFO(bind, itype);
+			sym.st_other = symp->st_other;
+			sym.st_shndx = symp->st_shndx;
+		} else {
+			const Elf64_Sym *symp = (Elf64_Sym *)symbase + i;
+
+			sym = *symp;
+		}
+
+		type = ELF64_ST_TYPE(sym.st_info);
+		name = (const char *)(strbase + sym.st_name);
+
+		/*
+		 * Check first if we have an STT_FILE entry. This is used to
+		 * distinguish between various local symbols when merging.
+		 */
+		if (type == STT_FILE) {
+			if (file != NULL) {
+				primary = B_FALSE;
+			}
+			file = name;
+			continue;
+		}
+
+		/*
+		 * Check if this is a symbol that we care about.
+		 */
+		if (!ctf_sym_valid(strbase, type, sym.st_shndx, sym.st_value,
+		    sym.st_name)) {
+			continue;
+		}
+
+		if ((ret = func(&sym, i, file, name, primary, arg)) != 0) {
+			return (ret);
+		}
+	}
+
+	return (0);
 }
--- old/usr/src/lib/libctf/common/ctf_merge.c	Tue Apr 23 05:24:02 2019
+++ new/usr/src/lib/libctf/common/ctf_merge.c	Tue Apr 23 05:24:02 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright (c) 2015 Joyent, Inc.
+ * Copyright (c) 2019 Joyent, Inc.
  */
 
 /*
@@ -60,7 +60,9 @@
 typedef struct ctf_merge_objmap {
 	list_node_t cmo_node;
 	const char *cmo_name;		/* Symbol name */
+	const char *cmo_file;		/* Symbol file */
 	ulong_t cmo_idx;		/* Symbol ID */
+	Elf64_Sym cmo_sym;		/* Symbol Entry */
 	ctf_id_t cmo_tid;		/* Type ID */
 } ctf_merge_objmap_t;
 
@@ -67,7 +69,9 @@
 typedef struct ctf_merge_funcmap {
 	list_node_t cmf_node;
 	const char *cmf_name;		/* Symbol name */
+	const char *cmf_file;		/* Symbol file */
 	ulong_t cmf_idx;		/* Symbol ID */
+	Elf64_Sym cmf_sym;		/* Symbol Entry */
 	ctf_id_t cmf_rtid;		/* Type ID */
 	uint_t cmf_flags;		/* ctf_funcinfo_t ctc_flags */
 	uint_t cmf_argc;		/* Number of arguments */
@@ -94,6 +98,13 @@
 	char *cmh_pname;		/* Parent name */
 };
 
+typedef struct ctf_merge_symbol_arg {
+	list_t *cmsa_objmap;
+	list_t *cmsa_funcmap;
+	ctf_file_t *cmsa_out;
+	boolean_t cmsa_dedup;
+} ctf_merge_symbol_arg_t;
+
 static int ctf_merge_add_type(ctf_merge_types_t *, ctf_id_t);
 
 static ctf_id_t
@@ -661,8 +672,8 @@
 {
 	int ret, i;
 
-	ctf_phase_dump(cmp->cm_src, "merge-common-src");
-	ctf_phase_dump(cmp->cm_out, "merge-common-dest");
+	ctf_phase_dump(cmp->cm_src, "merge-common-src", NULL);
+	ctf_phase_dump(cmp->cm_out, "merge-common-dest", NULL);
 
 	/* Pass 1 */
 	for (i = 1; i <= cmp->cm_src->ctf_typemax; i++) {
@@ -764,6 +775,37 @@
 }
 
 /*
+ * After performing a pass, we need to go through the object and function type
+ * maps and potentially fix them up based on the new maps that we have.
+ */
+static void
+ctf_merge_fixup_symmaps(ctf_merge_types_t *cmp, ctf_merge_input_t *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)) {
+		VERIFY3S(cmo->cmo_tid, !=, 0);
+		VERIFY(cmp->cm_tmap[cmo->cmo_tid].cmt_map != 0);
+		cmo->cmo_tid = cmp->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(cmp->cm_tmap[cmf->cmf_rtid].cmt_map != 0);
+		cmf->cmf_rtid = cmp->cm_tmap[cmf->cmf_rtid].cmt_map;
+		for (i = 0; i < cmf->cmf_argc; i++) {
+			VERIFY(cmp->cm_tmap[cmf->cmf_args[i]].cmt_map != 0);
+			cmf->cmf_args[i] =
+			    cmp->cm_tmap[cmf->cmf_args[i]].cmt_map;
+		}
+	}
+}
+
+/*
  * 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.
@@ -774,8 +816,6 @@
 	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;
@@ -817,26 +857,8 @@
 	/*
 	 * 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;
-	}
+	ctf_merge_fixup_symmaps(&cm, scmi);
 
-	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
@@ -852,42 +874,10 @@
 	ctf_diff_fini(cdp);
 	if (ret != 0)
 		return (ctf_errno(out));
+	ctf_phase_bump();
 	return (0);
 }
 
-/*
- * After performing a pass, we need to go through the object and function type
- * maps and potentially fix them up based on the new maps that we haev.
- */
-static void
-ctf_merge_fixup_nontypes(ctf_merge_types_t *cmp, ctf_merge_input_t *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(cmp->cm_tmap[cmo->cmo_tid].cmt_map != 0);
-		cmo->cmo_tid = cmp->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(cmp->cm_tmap[cmf->cmf_rtid].cmt_map != 0);
-		cmf->cmf_rtid = cmp->cm_tmap[cmf->cmf_rtid].cmt_map;
-		for (i = 0; i < cmf->cmf_argc; i++) {
-			VERIFY(cmp->cm_tmap[cmf->cmf_args[i]].cmt_map !=
-			    0);
-			cmf->cmf_args[i] =
-			    cmp->cm_tmap[cmf->cmf_args[i]].cmt_map;
-		}
-	}
-}
-
 static int
 ctf_uniquify_types(ctf_merge_t *cmh, ctf_file_t *src, ctf_file_t **outp)
 {
@@ -948,7 +938,7 @@
 
 	for (cmi = list_head(&cmh->cmh_inputs); cmi != NULL;
 	    cmi = list_next(&cmh->cmh_inputs, cmi)) {
-		ctf_merge_fixup_nontypes(&cm, cmi);
+		ctf_merge_fixup_symmaps(&cm, cmi);
 	}
 
 	ctf_merge_types_fini(&cm);
@@ -1060,10 +1050,9 @@
 }
 
 static int
-ctf_merge_add_funcs_cb(const char *name, ulong_t idx, ctf_funcinfo_t *fip,
-    void *arg)
+ctf_merge_add_function(ctf_merge_input_t *cmi, ctf_funcinfo_t *fip, ulong_t idx,
+    const char *file, const char *name, const Elf64_Sym *symp)
 {
-	ctf_merge_input_t *cmi = arg;
 	ctf_merge_funcmap_t *fmap;
 
 	fmap = ctf_alloc(sizeof (ctf_merge_funcmap_t) +
@@ -1072,10 +1061,16 @@
 		return (ENOMEM);
 
 	fmap->cmf_idx = idx;
+	fmap->cmf_sym = *symp;
 	fmap->cmf_rtid = fip->ctc_return;
 	fmap->cmf_flags = fip->ctc_flags;
 	fmap->cmf_argc = fip->ctc_argc;
 	fmap->cmf_name = name;
+	if (ELF64_ST_BIND(symp->st_info) == STB_LOCAL) {
+		fmap->cmf_file = file;
+	} else {
+		fmap->cmf_file = NULL;
+	}
 
 	if (ctf_func_args(cmi->cmi_input, idx, fmap->cmf_argc,
 	    fmap->cmf_args) != 0) {
@@ -1084,14 +1079,17 @@
 		return (ctf_errno(cmi->cmi_input));
 	}
 
+	ctf_dprintf("added initial function %s, %lu, %s %u\n", name, idx,
+	    fmap->cmf_file != NULL ? fmap->cmf_file : "global",
+	    ELF64_ST_BIND(symp->st_info));
 	list_insert_tail(&cmi->cmi_fmap, fmap);
 	return (0);
 }
 
 static int
-ctf_merge_add_objs_cb(const char *name, ctf_id_t id, ulong_t idx, void *arg)
+ctf_merge_add_object(ctf_merge_input_t *cmi, ctf_id_t id, ulong_t idx,
+    const char *file, const char *name, const Elf64_Sym *symp)
 {
-	ctf_merge_input_t *cmi = arg;
 	ctf_merge_objmap_t *cmo;
 
 	cmo = ctf_alloc(sizeof (ctf_merge_objmap_t));
@@ -1099,12 +1097,71 @@
 		return (ENOMEM);
 
 	cmo->cmo_name = name;
+	if (ELF64_ST_BIND(symp->st_info) == STB_LOCAL) {
+		cmo->cmo_file = file;
+	} else {
+		cmo->cmo_file = NULL;
+	}
 	cmo->cmo_idx = idx;
 	cmo->cmo_tid = id;
+	cmo->cmo_sym = *symp;
 	list_insert_tail(&cmi->cmi_omap, cmo);
+
+	ctf_dprintf("added initial object %s, %lu, %ld, %s\n", name, idx, id,
+	    cmo->cmo_file != NULL ? cmo->cmo_file : "global");
+
 	return (0);
 }
 
+static int
+ctf_merge_add_symbol(const Elf64_Sym *symp, ulong_t idx, const char *file,
+    const char *name, boolean_t primary, void *arg)
+{
+	ctf_merge_input_t *cmi = arg;
+	ctf_file_t *fp = cmi->cmi_input;
+	ushort_t *data, funcbase;
+	uint_t type;
+	ctf_funcinfo_t fi;
+
+	/*
+	 * See if there is type information for this. If there is no
+	 * type information for this entry or no translation, then we
+	 * will find the value zero. This indicates no type ID for
+	 * objects and encodes unknown information for functions.
+	 */
+	if (fp->ctf_sxlate[idx] == -1u)
+		return (0);
+	data = (ushort_t *)((uintptr_t)fp->ctf_buf + fp->ctf_sxlate[idx]);
+	if (*data == 0)
+		return (0);
+
+	type = ELF64_ST_TYPE(symp->st_info);
+
+	switch (type) {
+	case STT_FUNC:
+		funcbase = *data;
+		if (LCTF_INFO_KIND(fp, funcbase) != CTF_K_FUNCTION)
+			return (0);
+		data++;
+		fi.ctc_return = *data;
+		data++;
+		fi.ctc_argc = LCTF_INFO_VLEN(fp, funcbase);
+		fi.ctc_flags = 0;
+
+		if (fi.ctc_argc != 0 && data[fi.ctc_argc - 1] == 0) {
+			fi.ctc_flags |= CTF_FUNC_VARARG;
+			fi.ctc_argc--;
+		}
+		return (ctf_merge_add_function(cmi, &fi, idx, file, name,
+		    symp));
+	case STT_OBJECT:
+		return (ctf_merge_add_object(cmi, *data, idx, file, name,
+		    symp));
+	default:
+		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,
@@ -1117,6 +1174,8 @@
 	ctf_merge_input_t *cmi;
 	ctf_file_t *empty;
 
+	ctf_dprintf("adding input %p\n", input);
+
 	if (input->ctf_flags & LCTF_CHILD)
 		return (ECTF_MCHILD);
 
@@ -1132,17 +1191,11 @@
 	    offsetof(ctf_merge_objmap_t, cmo_node));
 
 	if (cmh->cmh_msyms == B_TRUE) {
-		if ((ret = ctf_function_iter(input, ctf_merge_add_funcs_cb,
+		if ((ret = ctf_symtab_iter(input, ctf_merge_add_symbol,
 		    cmi)) != 0) {
 			ctf_merge_fini_input(cmi);
 			return (ret);
 		}
-
-		if ((ret = ctf_object_iter(input, ctf_merge_add_objs_cb,
-		    cmi)) != 0) {
-			ctf_merge_fini_input(cmi);
-			return (ret);
-		}
 	}
 
 	list_insert_tail(&cmh->cmh_inputs, cmi);
@@ -1194,122 +1247,193 @@
 	return (0);
 }
 
-static int
-ctf_merge_symbols(ctf_merge_t *cmh, ctf_file_t *fp)
+/*
+ * Symbol matching rules: the purpose of this is to verify that the type
+ * information that we have for a given symbol actually matches the output
+ * symbol. This is unfortunately complicated by several different factors:
+ *
+ * 1. When merging multiple .o's into a single item, the symbol table index will
+ * not match.
+ *
+ * 2. Visibility of a symbol may not be identical to the object file or the
+ * DWARF information due to symbol reduction via a mapfile.
+ *
+ * As such, we have to employ the following rules:
+ *
+ * 1. A global symbol table entry always matches a global CTF symbol with the
+ * same name.
+ *
+ * 2. A local symbol table entry always matches a local CTF symbol if they have
+ * the same name and they belong to the same file.
+ *
+ * 3. A weak symbol matches a non-weak symbol. This happens if we find that the
+ * types match, the values match, the sizes match, and the section indexes
+ * match. This happens when we do a conversion in one pass, it almost never
+ * happens when we're merging multiple object files. If we match a CTF global
+ * symbol, that's a fixed match, otherwise it's a fuzzy match.
+ *
+ * 4. A local symbol table entry matches a global CTF entry if the
+ * other pieces fail, but they have the same name. This is considered a fuzzy
+ * match and is not used unless we have no other options.
+ *
+ * 5. A weak symbol table entry matches a weak CTF entry if the other pieces
+ * fail, but they have the same name. This is considered a fuzzy match and is
+ * not used unless we have no other options. When merging independent .o files,
+ * this is often the only recourse we have to matching weak symbols.
+ *
+ * In the end, this would all be much simpler if we were able to do this as part
+ * of libld which would be able to do all the symbol transformations.
+ */
+static boolean_t
+ctf_merge_symbol_match(const char *ctf_file, const char *ctf_name,
+    const Elf64_Sym *ctf_symp, const char *symtab_file, const char *symtab_name,
+    const Elf64_Sym *symtab_symp, boolean_t *is_fuzzy)
 {
-	int err;
-	ulong_t i;
+	*is_fuzzy = B_FALSE;
+	uint_t symtab_bind, ctf_bind;
 
-	uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
-	uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+	symtab_bind = ELF64_ST_BIND(symtab_symp->st_info);
+	ctf_bind = ELF64_ST_BIND(ctf_symp->st_info);
 
-	for (i = 0; i < fp->ctf_nsyms; i++) {
-		const char *name;
-		ctf_merge_input_t *cmi;
-		ctf_merge_objmap_t *cmo;
+	ctf_dprintf("comparing merge match for %s/%s/%u->%s/%s/%u\n",
+	    symtab_file, symtab_name, symtab_bind,
+	    ctf_file, ctf_name, ctf_bind);
+	if (strcmp(ctf_name, symtab_name) != 0) {
+		return (B_FALSE);
+	}
 
-		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);
+	if (symtab_bind == STB_GLOBAL && ctf_bind == STB_GLOBAL) {
+		return (B_TRUE);
+	} else if (symtab_bind == STB_GLOBAL) {
+		return (B_FALSE);
+	}
+
+	if (ctf_bind == STB_LOCAL && ctf_bind == symtab_bind &&
+	    ctf_file != NULL && symtab_file != NULL &&
+	    strcmp(ctf_file, symtab_file) == 0) {
+		return (B_TRUE);
+	}
+
+	if (symtab_bind == STB_WEAK && ctf_bind != STB_WEAK &&
+	    ELF64_ST_TYPE(symtab_symp->st_info) ==
+	    ELF64_ST_TYPE(ctf_symp->st_info) &&
+	    symtab_symp->st_value == ctf_symp->st_value &&
+	    symtab_symp->st_size == ctf_symp->st_size &&
+	    symtab_symp->st_shndx == ctf_symp->st_shndx) {
+		if (ctf_bind == STB_GLOBAL) {
+			return (B_TRUE);
 		}
 
-		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;
-			}
+		if (ctf_bind == STB_LOCAL && ctf_file != NULL &&
+		    symtab_file != NULL && strcmp(ctf_file, symtab_file) == 0) {
+			*is_fuzzy = B_TRUE;
+			return (B_TRUE);
 		}
-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);
+	if (ctf_bind == STB_GLOBAL ||
+	    (ctf_bind == STB_WEAK && symtab_bind == STB_WEAK)) {
+		*is_fuzzy = B_TRUE;
+		return (B_TRUE);
+	}
+
+	return (B_FALSE);
 }
 
+/*
+ * For each symbol, try and find a match. We will attempt to find an exact
+ * match; however, we will settle for a fuzzy match in general. There is one
+ * case where we will not opt to use a fuzzy match, which is when performing the
+ * deduplication of a container. In such a case we are trying to reduce common
+ * types and a fuzzy match would be inappropriate as if we're in the context of
+ * a single container, the conversion process should have identified any exact
+ * or fuzzy matches that were required.
+ */
 static int
-ctf_merge_functions(ctf_merge_t *cmh, ctf_file_t *fp)
+ctf_merge_symbols(const Elf64_Sym *symp, ulong_t idx, const char *file,
+    const char *name, boolean_t primary, void *arg)
 {
 	int err;
-	ulong_t i;
-	ctf_funcinfo_t fi;
+	uint_t type, bind;
+	ctf_merge_symbol_arg_t *csa = arg;
+	ctf_file_t *fp = csa->cmsa_out;
 
-	uintptr_t symbase = (uintptr_t)fp->ctf_symtab.cts_data;
-	uintptr_t strbase = (uintptr_t)fp->ctf_strtab.cts_data;
+	type = ELF64_ST_TYPE(symp->st_info);
+	bind = ELF64_ST_BIND(symp->st_info);
 
-	for (i = 0; i < fp->ctf_nsyms; i++) {
-		const char *name;
-		ctf_merge_input_t *cmi;
-		ctf_merge_funcmap_t *cmf;
+	ctf_dprintf("Trying to find match for %s/%s/%u\n", file, name,
+	    ELF64_ST_BIND(symp->st_info));
 
-		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);
+	if (type == STT_OBJECT) {
+		ctf_merge_objmap_t *cmo, *match = NULL;
+
+		for (cmo = list_head(csa->cmsa_objmap); cmo != NULL;
+		    cmo = list_next(csa->cmsa_objmap, cmo)) {
+			boolean_t is_fuzzy = B_FALSE;
+			if (ctf_merge_symbol_match(cmo->cmo_file, cmo->cmo_name,
+			    &cmo->cmo_sym, file, name, symp, &is_fuzzy)) {
+				if (is_fuzzy && csa->cmsa_dedup &&
+				    bind != STB_WEAK) {
+					continue;
+				}
+				match = cmo;
+				if (is_fuzzy) {
+					continue;
+				}
+				break;
+			}
 		}
 
-		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;
+		if (match == NULL) {
+			return (0);
+		}
+
+		if ((err = ctf_add_object(fp, idx, match->cmo_tid)) != 0) {
+			ctf_dprintf("Failed to add symbol %s->%d: %s\n", name,
+			    match->cmo_tid, ctf_errmsg(ctf_errno(fp)));
+			return (ctf_errno(fp));
+		}
+		ctf_dprintf("mapped object into output %s/%s->%ld\n", file,
+		    name, match->cmo_tid);
+	} else {
+		ctf_merge_funcmap_t *cmf, *match = NULL;
+		ctf_funcinfo_t fi;
+
+		for (cmf = list_head(csa->cmsa_funcmap); cmf != NULL;
+		    cmf = list_next(csa->cmsa_funcmap, cmf)) {
+			boolean_t is_fuzzy = B_FALSE;
+			if (ctf_merge_symbol_match(cmf->cmf_file, cmf->cmf_name,
+			    &cmf->cmf_sym, file, name, symp, &is_fuzzy)) {
+				if (is_fuzzy && csa->cmsa_dedup &&
+				    bind != STB_WEAK) {
+					continue;
+				}
+				match = cmf;
+				if (is_fuzzy) {
+					continue;
+				}
+				break;
 			}
 		}
-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);
+
+		if (match == NULL) {
+			return (0);
 		}
+
+		fi.ctc_return = match->cmf_rtid;
+		fi.ctc_argc = match->cmf_argc;
+		fi.ctc_flags = match->cmf_flags;
+		if ((err = ctf_add_function(fp, idx, &fi, match->cmf_args)) !=
+		    0) {
+			ctf_dprintf("Failed to add function %s: %s\n", name,
+			    ctf_errmsg(ctf_errno(fp)));
+			return (ctf_errno(fp));
+		}
+		ctf_dprintf("mapped function into output %s/%s\n", file,
+		    name);
 	}
 
 	return (0);
-
 }
 
 int
@@ -1322,6 +1446,7 @@
 	ctf_merge_input_t *final;
 	ctf_file_t *out;
 
+	ctf_dprintf("Beginning ctf_merge_merge()\n");
 	if (cmh->cmh_label != NULL && cmh->cmh_unique != NULL) {
 		const char *label = ctf_label_topmost(cmh->cmh_unique);
 		if (label == NULL)
@@ -1387,23 +1512,23 @@
 
 	ctf_dprintf("merging symbols and the like\n");
 	if (cmh->cmh_msyms == B_TRUE) {
-		err = ctf_merge_symbols(cmh, out);
+		ctf_merge_symbol_arg_t arg;
+		arg.cmsa_objmap = &final->cmi_omap;
+		arg.cmsa_funcmap = &final->cmi_fmap;
+		arg.cmsa_out = out;
+		arg.cmsa_dedup = B_FALSE;
+		err = ctf_symtab_iter(out, ctf_merge_symbols, &arg);
 		if (err != 0) {
 			ctf_close(out);
-			return (ctf_errno(out));
+			return (err);
 		}
-
-		err = ctf_merge_functions(cmh, out);
-		if (err != 0) {
-			ctf_close(out);
-			return (ctf_errno(out));
-		}
 	}
 
 	err = ctf_update(out);
 	if (err != 0) {
+		err = ctf_errno(out);
 		ctf_close(out);
-		return (ctf_errno(out));
+		return (err);
 	}
 
 	*outp = out;
@@ -1505,29 +1630,25 @@
 		goto err;
 
 	ctf_dprintf("Successfully deduped types\n");
-	ctf_phase_dump(cm.cm_out, "dedup-pre-syms");
+	ctf_phase_dump(cm.cm_out, "dedup-pre-syms", NULL);
 
 	/*
 	 * Now we need to fix up the object and function maps.
 	 */
-	ctf_merge_fixup_nontypes(&cm, cmi);
+	ctf_merge_fixup_symmaps(&cm, cmi);
 
 	if (cmp->cmh_msyms == B_TRUE) {
-		ret = ctf_merge_symbols(cmp, cm.cm_out);
+		ctf_merge_symbol_arg_t arg;
+		arg.cmsa_objmap = &cmi->cmi_omap;
+		arg.cmsa_funcmap = &cmi->cmi_fmap;
+		arg.cmsa_out = cm.cm_out;
+		arg.cmsa_dedup = B_TRUE;
+		ret = ctf_symtab_iter(cm.cm_out, ctf_merge_symbols, &arg);
 		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);
@@ -1535,6 +1656,7 @@
 		cmc->cmi_input = NULL;
 		*outp = cm.cm_out;
 	}
+	ctf_phase_dump(cm.cm_out, "dedup-post-syms", NULL);
 err:
 	ctf_merge_types_fini(&cm);
 	ctf_diff_fini(cdp);
--- old/usr/src/lib/libctf/common/libctf_impl.h	Tue Apr 23 05:24:02 2019
+++ new/usr/src/lib/libctf/common/libctf_impl.h	Tue Apr 23 05:24:02 2019
@@ -10,7 +10,7 @@
  */
 
 /*
- * Copyright 2015 Joyent, Inc.
+ * Copyright 2019 Joyent, Inc.
  */
 
 #ifndef _LIBCTF_IMPL_H
@@ -41,6 +41,13 @@
     ctf_file_t **, char *, size_t);
 
 /*
+ * Symbol walking
+ */
+typedef int (*ctf_symtab_f)(const Elf64_Sym *, ulong_t, const char *,
+    const char *, boolean_t, void *);
+extern int ctf_symtab_iter(ctf_file_t *, ctf_symtab_f, void *);
+
+/*
  * zlib compression routines
  */
 extern int ctf_compress(ctf_file_t *fp, void **, size_t *, size_t *);
@@ -50,7 +57,8 @@
 /*
  * Internal debugging aids
  */
-extern void ctf_phase_dump(ctf_file_t *, const char *);
+extern void ctf_phase_dump(ctf_file_t *, const char *, const char *);
+extern void ctf_phase_bump(void);
 
 #ifdef __cplusplus
 }
