17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
54a1c2431SJonathan Adams  * Common Development and Distribution License (the "License").
64a1c2431SJonathan Adams  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
224a1c2431SJonathan Adams  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
243b6e0a59SMatt Amdur  * Copyright (c) 2012 by Delphix. All rights reserved.
253b6e0a59SMatt Amdur  * Copyright (c) 2012 Joyent, Inc. All rights reserved.
267c478bd9Sstevel@tonic-gate  */
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <sys/param.h>
297c478bd9Sstevel@tonic-gate #include <unistd.h>
307c478bd9Sstevel@tonic-gate #include <strings.h>
317c478bd9Sstevel@tonic-gate #include <dlfcn.h>
327c478bd9Sstevel@tonic-gate #include <link.h>
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include <mdb/mdb_module.h>
357c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
367c478bd9Sstevel@tonic-gate #include <mdb/mdb_ctf.h>
377c478bd9Sstevel@tonic-gate #include <mdb/mdb_debug.h>
387c478bd9Sstevel@tonic-gate #include <mdb/mdb_callb.h>
397c478bd9Sstevel@tonic-gate #include <mdb/mdb_string.h>
407c478bd9Sstevel@tonic-gate #include <mdb/mdb_ks.h>
417c478bd9Sstevel@tonic-gate #include <mdb/mdb_err.h>
427c478bd9Sstevel@tonic-gate #include <mdb/mdb_io.h>
437c478bd9Sstevel@tonic-gate #include <mdb/mdb_frame.h>
444a1c2431SJonathan Adams #include <mdb/mdb_whatis_impl.h>
457c478bd9Sstevel@tonic-gate #include <mdb/mdb.h>
467c478bd9Sstevel@tonic-gate 
473b6e0a59SMatt Amdur /*
483b6e0a59SMatt Amdur  * The format of an mdb dcmd changed between MDB_API_VERSION 3 and 4, with an
493b6e0a59SMatt Amdur  * addition of a new field to the public interface. To maintain backwards
503b6e0a59SMatt Amdur  * compatibility with older versions, we know to keep around the old version of
513b6e0a59SMatt Amdur  * the structure so we can correctly read the set of dcmds passed in.
523b6e0a59SMatt Amdur  */
533b6e0a59SMatt Amdur typedef struct mdb_dcmd_v3 {
543b6e0a59SMatt Amdur 	const char *dco_name;		/* Command name */
553b6e0a59SMatt Amdur 	const char *dco_usage;		/* Usage message (optional) */
563b6e0a59SMatt Amdur 	const char *dco_descr;		/* Description */
573b6e0a59SMatt Amdur 	mdb_dcmd_f *dco_funcp;		/* Command function */
583b6e0a59SMatt Amdur 	void (*dco_help)(void);		/* Command help function (or NULL) */
593b6e0a59SMatt Amdur } mdb_dcmd_v3_t;
603b6e0a59SMatt Amdur 
617c478bd9Sstevel@tonic-gate /*
627c478bd9Sstevel@tonic-gate  * For builtin modules, we set mod_init to this function, which just
637c478bd9Sstevel@tonic-gate  * returns a constant modinfo struct with no dcmds and walkers.
647c478bd9Sstevel@tonic-gate  */
657c478bd9Sstevel@tonic-gate static const mdb_modinfo_t *
builtin_init(void)667c478bd9Sstevel@tonic-gate builtin_init(void)
677c478bd9Sstevel@tonic-gate {
687c478bd9Sstevel@tonic-gate 	static const mdb_modinfo_t info = { MDB_API_VERSION };
697c478bd9Sstevel@tonic-gate 	return (&info);
707c478bd9Sstevel@tonic-gate }
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate int
mdb_module_validate_name(const char * name,const char ** errmsgp)737c478bd9Sstevel@tonic-gate mdb_module_validate_name(const char *name, const char **errmsgp)
747c478bd9Sstevel@tonic-gate {
757c478bd9Sstevel@tonic-gate 	if (strlen(name) == 0) {
767c478bd9Sstevel@tonic-gate 		*errmsgp = "no module name was specified\n";
777c478bd9Sstevel@tonic-gate 		return (0);
787c478bd9Sstevel@tonic-gate 	}
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 	if (strlen(name) > MDB_NV_NAMELEN) {
817c478bd9Sstevel@tonic-gate 		*errmsgp = "module name '%s' exceeds name length limit\n";
827c478bd9Sstevel@tonic-gate 		return (0);
837c478bd9Sstevel@tonic-gate 	}
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	if (strbadid(name) != NULL) {
867c478bd9Sstevel@tonic-gate 		*errmsgp = "module name '%s' contains illegal characters\n";
877c478bd9Sstevel@tonic-gate 		return (0);
887c478bd9Sstevel@tonic-gate 	}
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	if (mdb_nv_lookup(&mdb.m_modules, name) != NULL) {
917c478bd9Sstevel@tonic-gate 		*errmsgp = "%s module is already loaded\n";
927c478bd9Sstevel@tonic-gate 		return (0);
937c478bd9Sstevel@tonic-gate 	}
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	return (1);
967c478bd9Sstevel@tonic-gate }
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate int
mdb_module_create(const char * name,const char * fname,int mode,mdb_module_t ** mpp)997c478bd9Sstevel@tonic-gate mdb_module_create(const char *name, const char *fname, int mode,
1007c478bd9Sstevel@tonic-gate     mdb_module_t **mpp)
1017c478bd9Sstevel@tonic-gate {
1027c478bd9Sstevel@tonic-gate 	static const mdb_walker_t empty_walk_list[] = { 0 };
1037c478bd9Sstevel@tonic-gate 	static const mdb_dcmd_t empty_dcmd_list[] = { 0 };
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate 	int dlmode = (mode & MDB_MOD_GLOBAL) ? RTLD_GLOBAL : RTLD_LOCAL;
1067c478bd9Sstevel@tonic-gate 
1077c478bd9Sstevel@tonic-gate 	const mdb_modinfo_t *info;
1087c478bd9Sstevel@tonic-gate 	const mdb_dcmd_t *dcp;
1097c478bd9Sstevel@tonic-gate 	const mdb_walker_t *wp;
1107c478bd9Sstevel@tonic-gate 
1113b6e0a59SMatt Amdur 	const mdb_dcmd_v3_t *dcop;
1123b6e0a59SMatt Amdur 	mdb_dcmd_t *dctp = NULL;
1133b6e0a59SMatt Amdur 
1147c478bd9Sstevel@tonic-gate 	mdb_module_t *mod;
1157c478bd9Sstevel@tonic-gate 
1167c478bd9Sstevel@tonic-gate 	mod = mdb_zalloc(sizeof (mdb_module_t), UM_SLEEP);
1177c478bd9Sstevel@tonic-gate 	mod->mod_info = mdb_alloc(sizeof (mdb_modinfo_t), UM_SLEEP);
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mod->mod_dcmds, UM_SLEEP);
1207c478bd9Sstevel@tonic-gate 	(void) mdb_nv_create(&mod->mod_walkers, UM_SLEEP);
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate 	mod->mod_name = strdup(name);
1237c478bd9Sstevel@tonic-gate 	mdb.m_lmod = mod;		/* Mark module as currently loading */
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 	if (!(mode & MDB_MOD_BUILTIN)) {
1267c478bd9Sstevel@tonic-gate 		mdb_dprintf(MDB_DBG_MODULE, "dlopen %s %x\n", fname, dlmode);
1277c478bd9Sstevel@tonic-gate 		mod->mod_hdl = dlmopen(LM_ID_BASE, fname, RTLD_NOW | dlmode);
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate 		if (mod->mod_hdl == NULL) {
1307c478bd9Sstevel@tonic-gate 			warn("%s\n", dlerror());
1317c478bd9Sstevel@tonic-gate 			goto err;
1327c478bd9Sstevel@tonic-gate 		}
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 		mod->mod_init = (const mdb_modinfo_t *(*)(void))
1357c478bd9Sstevel@tonic-gate 		    dlsym(mod->mod_hdl, "_mdb_init");
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate 		mod->mod_fini = (void (*)(void))
1387c478bd9Sstevel@tonic-gate 		    dlsym(mod->mod_hdl, "_mdb_fini");
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 		mod->mod_tgt_ctor = (mdb_tgt_ctor_f *)
1417c478bd9Sstevel@tonic-gate 		    dlsym(mod->mod_hdl, "_mdb_tgt_create");
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate 		mod->mod_dis_ctor = (mdb_dis_ctor_f *)
1447c478bd9Sstevel@tonic-gate 		    dlsym(mod->mod_hdl, "_mdb_dis_create");
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 		if (!(mdb.m_flags & MDB_FL_NOCTF))
1477c478bd9Sstevel@tonic-gate 			mod->mod_ctfp = mdb_ctf_open(fname, NULL);
1487c478bd9Sstevel@tonic-gate 	} else {
1497c478bd9Sstevel@tonic-gate #ifdef _KMDB
1507c478bd9Sstevel@tonic-gate 		/*
1517c478bd9Sstevel@tonic-gate 		 * mdb_ks is a special case - a builtin with _mdb_init and
1527c478bd9Sstevel@tonic-gate 		 * _mdb_fini routines.  If we don't hack it in here, we'll have
1537c478bd9Sstevel@tonic-gate 		 * to duplicate most of the module creation code elsewhere.
1547c478bd9Sstevel@tonic-gate 		 */
1557c478bd9Sstevel@tonic-gate 		if (strcmp(name, "mdb_ks") == 0)
1567c478bd9Sstevel@tonic-gate 			mod->mod_init = mdb_ks_init;
1577c478bd9Sstevel@tonic-gate 		else
1587c478bd9Sstevel@tonic-gate #endif
1597c478bd9Sstevel@tonic-gate 			mod->mod_init = builtin_init;
1607c478bd9Sstevel@tonic-gate 	}
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	if (mod->mod_init == NULL) {
1637c478bd9Sstevel@tonic-gate 		warn("%s module is missing _mdb_init definition\n", name);
1647c478bd9Sstevel@tonic-gate 		goto err;
1657c478bd9Sstevel@tonic-gate 	}
1667c478bd9Sstevel@tonic-gate 
1677c478bd9Sstevel@tonic-gate 	if ((info = mod->mod_init()) == NULL) {
1687c478bd9Sstevel@tonic-gate 		warn("%s module failed to initialize\n", name);
1697c478bd9Sstevel@tonic-gate 		goto err;
1707c478bd9Sstevel@tonic-gate 	}
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate 	/*
1737c478bd9Sstevel@tonic-gate 	 * Reject modules compiled for a newer version of the debugger.
1747c478bd9Sstevel@tonic-gate 	 */
1757c478bd9Sstevel@tonic-gate 	if (info->mi_dvers > MDB_API_VERSION) {
1767c478bd9Sstevel@tonic-gate 		warn("%s module requires newer mdb API version (%hu) than "
1777c478bd9Sstevel@tonic-gate 		    "debugger (%d)\n", name, info->mi_dvers, MDB_API_VERSION);
1787c478bd9Sstevel@tonic-gate 		goto err;
1797c478bd9Sstevel@tonic-gate 	}
1807c478bd9Sstevel@tonic-gate 
1817c478bd9Sstevel@tonic-gate 	/*
1827c478bd9Sstevel@tonic-gate 	 * Load modules compiled for the current API version.
1837c478bd9Sstevel@tonic-gate 	 */
184*9e181bc5SToomas Soome #if MDB_API_VERSION != 5
185*9e181bc5SToomas Soome #error "MDB_API_VERSION needs to be checked here"
186*9e181bc5SToomas Soome #endif
1877c478bd9Sstevel@tonic-gate 	switch (info->mi_dvers) {
1887c478bd9Sstevel@tonic-gate 	case MDB_API_VERSION:
189*9e181bc5SToomas Soome 	case 4:
1903b6e0a59SMatt Amdur 	case 3:
1917c478bd9Sstevel@tonic-gate 	case 2:
1927c478bd9Sstevel@tonic-gate 	case 1:
1937c478bd9Sstevel@tonic-gate 		/*
1947c478bd9Sstevel@tonic-gate 		 * Current API version -- copy entire modinfo
1957c478bd9Sstevel@tonic-gate 		 * structure into our own private storage.
1967c478bd9Sstevel@tonic-gate 		 */
1977c478bd9Sstevel@tonic-gate 		bcopy(info, mod->mod_info, sizeof (mdb_modinfo_t));
1987c478bd9Sstevel@tonic-gate 		if (mod->mod_info->mi_dcmds == NULL)
1997c478bd9Sstevel@tonic-gate 			mod->mod_info->mi_dcmds = empty_dcmd_list;
2007c478bd9Sstevel@tonic-gate 		if (mod->mod_info->mi_walkers == NULL)
2017c478bd9Sstevel@tonic-gate 			mod->mod_info->mi_walkers = empty_walk_list;
2027c478bd9Sstevel@tonic-gate 		break;
2037c478bd9Sstevel@tonic-gate 	default:
2047c478bd9Sstevel@tonic-gate 		/*
2057c478bd9Sstevel@tonic-gate 		 * Too old to be compatible -- abort the load.
2067c478bd9Sstevel@tonic-gate 		 */
2077c478bd9Sstevel@tonic-gate 		warn("%s module is compiled for obsolete mdb API "
2087c478bd9Sstevel@tonic-gate 		    "version %hu\n", name, info->mi_dvers);
2097c478bd9Sstevel@tonic-gate 		goto err;
2107c478bd9Sstevel@tonic-gate 	}
2117c478bd9Sstevel@tonic-gate 
2123b6e0a59SMatt Amdur 	/*
2133b6e0a59SMatt Amdur 	 * In MDB_API_VERSION 4, the size of the mdb_dcmd_t struct changed. If
2143b6e0a59SMatt Amdur 	 * our module is from an earlier version, we need to walk it in the old
2153b6e0a59SMatt Amdur 	 * structure and convert it to the new one.
2163b6e0a59SMatt Amdur 	 *
2173b6e0a59SMatt Amdur 	 * Note that we purposefully don't predicate on whether or not we have
2183b6e0a59SMatt Amdur 	 * the empty list case and duplicate it anyways. That case is rare and
2193b6e0a59SMatt Amdur 	 * it makes our logic simpler when we need to unload the module.
2203b6e0a59SMatt Amdur 	 */
2213b6e0a59SMatt Amdur 	if (info->mi_dvers < 4) {
2223b6e0a59SMatt Amdur 		int ii = 0;
2233b6e0a59SMatt Amdur 		for (dcop = (mdb_dcmd_v3_t *)&mod->mod_info->mi_dcmds[0];
2243b6e0a59SMatt Amdur 		    dcop->dco_name != NULL; dcop++)
2253b6e0a59SMatt Amdur 			ii++;
2263b6e0a59SMatt Amdur 		/* Don't forget null terminated one at the end */
2273b6e0a59SMatt Amdur 		dctp = mdb_zalloc(sizeof (mdb_dcmd_t) * (ii + 1), UM_SLEEP);
2283b6e0a59SMatt Amdur 		ii = 0;
2293b6e0a59SMatt Amdur 		for (dcop = (mdb_dcmd_v3_t *)&mod->mod_info->mi_dcmds[0];
2303b6e0a59SMatt Amdur 		    dcop->dco_name != NULL; dcop++, ii++) {
2313b6e0a59SMatt Amdur 			dctp[ii].dc_name = dcop->dco_name;
2323b6e0a59SMatt Amdur 			dctp[ii].dc_usage = dcop->dco_usage;
2333b6e0a59SMatt Amdur 			dctp[ii].dc_descr = dcop->dco_descr;
2343b6e0a59SMatt Amdur 			dctp[ii].dc_funcp = dcop->dco_funcp;
2353b6e0a59SMatt Amdur 			dctp[ii].dc_help = dcop->dco_help;
2363b6e0a59SMatt Amdur 			dctp[ii].dc_tabp = NULL;
2373b6e0a59SMatt Amdur 		}
2383b6e0a59SMatt Amdur 		mod->mod_info->mi_dcmds = dctp;
2393b6e0a59SMatt Amdur 	}
2403b6e0a59SMatt Amdur 
2417c478bd9Sstevel@tonic-gate 	/*
2427c478bd9Sstevel@tonic-gate 	 * Before we actually go ahead with the load, we need to check
2437c478bd9Sstevel@tonic-gate 	 * each dcmd and walk structure for any invalid values:
2447c478bd9Sstevel@tonic-gate 	 */
2457c478bd9Sstevel@tonic-gate 	for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL; dcp++) {
2467c478bd9Sstevel@tonic-gate 		if (strbadid(dcp->dc_name) != NULL) {
2477c478bd9Sstevel@tonic-gate 			warn("dcmd name '%s' contains illegal characters\n",
2487c478bd9Sstevel@tonic-gate 			    dcp->dc_name);
2497c478bd9Sstevel@tonic-gate 			goto err;
2507c478bd9Sstevel@tonic-gate 		}
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 		if (dcp->dc_descr == NULL) {
2537c478bd9Sstevel@tonic-gate 			warn("dcmd '%s' must have a description\n",
2547c478bd9Sstevel@tonic-gate 			    dcp->dc_name);
2557c478bd9Sstevel@tonic-gate 			goto err;
2567c478bd9Sstevel@tonic-gate 		}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 		if (dcp->dc_funcp == NULL) {
2597c478bd9Sstevel@tonic-gate 			warn("dcmd '%s' has a NULL function pointer\n",
2607c478bd9Sstevel@tonic-gate 			    dcp->dc_name);
2617c478bd9Sstevel@tonic-gate 			goto err;
2627c478bd9Sstevel@tonic-gate 		}
2637c478bd9Sstevel@tonic-gate 	}
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	for (wp = &mod->mod_info->mi_walkers[0]; wp->walk_name != NULL; wp++) {
2667c478bd9Sstevel@tonic-gate 		if (strbadid(wp->walk_name) != NULL) {
2677c478bd9Sstevel@tonic-gate 			warn("walk name '%s' contains illegal characters\n",
2687c478bd9Sstevel@tonic-gate 			    wp->walk_name);
2697c478bd9Sstevel@tonic-gate 			goto err;
2707c478bd9Sstevel@tonic-gate 		}
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 		if (wp->walk_descr == NULL) {
2737c478bd9Sstevel@tonic-gate 			warn("walk '%s' must have a description\n",
2747c478bd9Sstevel@tonic-gate 			    wp->walk_name);
2757c478bd9Sstevel@tonic-gate 			goto err;
2767c478bd9Sstevel@tonic-gate 		}
2777c478bd9Sstevel@tonic-gate 
2787c478bd9Sstevel@tonic-gate 		if (wp->walk_step == NULL) {
2797c478bd9Sstevel@tonic-gate 			warn("walk '%s' has a NULL walk_step function\n",
2807c478bd9Sstevel@tonic-gate 			    wp->walk_name);
2817c478bd9Sstevel@tonic-gate 			goto err;
2827c478bd9Sstevel@tonic-gate 		}
2837c478bd9Sstevel@tonic-gate 	}
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 	/*
2867c478bd9Sstevel@tonic-gate 	 * Now that we've established that there are no problems,
2877c478bd9Sstevel@tonic-gate 	 * we can go ahead and hash the module, and its dcmds and walks:
2887c478bd9Sstevel@tonic-gate 	 */
2897c478bd9Sstevel@tonic-gate 	(void) mdb_nv_insert(&mdb.m_modules, mod->mod_name, NULL,
2907c478bd9Sstevel@tonic-gate 	    (uintptr_t)mod, MDB_NV_RDONLY|MDB_NV_EXTNAME);
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL; dcp++) {
2937c478bd9Sstevel@tonic-gate 		if (mdb_module_add_dcmd(mod, dcp, mode) == -1)
2947c478bd9Sstevel@tonic-gate 			warn("failed to load dcmd %s`%s", name, dcp->dc_name);
2957c478bd9Sstevel@tonic-gate 	}
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	for (wp = &mod->mod_info->mi_walkers[0]; wp->walk_name != NULL; wp++) {
2987c478bd9Sstevel@tonic-gate 		if (mdb_module_add_walker(mod, wp, mode) == -1)
2997c478bd9Sstevel@tonic-gate 			warn("failed to load walk %s`%s", name, wp->walk_name);
3007c478bd9Sstevel@tonic-gate 	}
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	/*
3037c478bd9Sstevel@tonic-gate 	 * Add the module to the end of the list of modules in load-dependency
3047c478bd9Sstevel@tonic-gate 	 * order.  We maintain this list so we can unload in reverse order.
3057c478bd9Sstevel@tonic-gate 	 */
3067c478bd9Sstevel@tonic-gate 	if (mdb.m_mtail != NULL) {
3077c478bd9Sstevel@tonic-gate 		ASSERT(mdb.m_mtail->mod_next == NULL);
3087c478bd9Sstevel@tonic-gate 		mdb.m_mtail->mod_next = mod;
3097c478bd9Sstevel@tonic-gate 		mod->mod_prev = mdb.m_mtail;
3107c478bd9Sstevel@tonic-gate 		mdb.m_mtail = mod;
3117c478bd9Sstevel@tonic-gate 	} else {
3127c478bd9Sstevel@tonic-gate 		ASSERT(mdb.m_mhead == NULL);
3137c478bd9Sstevel@tonic-gate 		mdb.m_mtail = mdb.m_mhead = mod;
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	mdb.m_lmod = NULL;
3177c478bd9Sstevel@tonic-gate 	if (mpp != NULL)
3187c478bd9Sstevel@tonic-gate 		*mpp = mod;
3197c478bd9Sstevel@tonic-gate 	return (0);
3207c478bd9Sstevel@tonic-gate 
3217c478bd9Sstevel@tonic-gate err:
3224a1c2431SJonathan Adams 	mdb_whatis_unregister_module(mod);
3234a1c2431SJonathan Adams 
3247c478bd9Sstevel@tonic-gate 	if (mod->mod_ctfp != NULL)
3257c478bd9Sstevel@tonic-gate 		ctf_close(mod->mod_ctfp);
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate 	if (mod->mod_hdl != NULL)
3287c478bd9Sstevel@tonic-gate 		(void) dlclose(mod->mod_hdl);
3297c478bd9Sstevel@tonic-gate 
3307c478bd9Sstevel@tonic-gate 	mdb_nv_destroy(&mod->mod_dcmds);
3317c478bd9Sstevel@tonic-gate 	mdb_nv_destroy(&mod->mod_walkers);
3327c478bd9Sstevel@tonic-gate 
3337c478bd9Sstevel@tonic-gate 	strfree((char *)mod->mod_name);
3347c478bd9Sstevel@tonic-gate 	mdb_free(mod->mod_info, sizeof (mdb_modinfo_t));
3357c478bd9Sstevel@tonic-gate 	mdb_free(mod, sizeof (mdb_module_t));
3367c478bd9Sstevel@tonic-gate 
3377c478bd9Sstevel@tonic-gate 	mdb.m_lmod = NULL;
3387c478bd9Sstevel@tonic-gate 	return (-1);
3397c478bd9Sstevel@tonic-gate }
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate mdb_module_t *
mdb_module_load_builtin(const char * name)3427c478bd9Sstevel@tonic-gate mdb_module_load_builtin(const char *name)
3437c478bd9Sstevel@tonic-gate {
3447c478bd9Sstevel@tonic-gate 	mdb_module_t *mp;
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	if (mdb_module_create(name, NULL, MDB_MOD_BUILTIN, &mp) < 0)
3477c478bd9Sstevel@tonic-gate 		return (NULL);
3487c478bd9Sstevel@tonic-gate 	return (mp);
3497c478bd9Sstevel@tonic-gate }
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate int
mdb_module_unload_common(const char * name)3527c478bd9Sstevel@tonic-gate mdb_module_unload_common(const char *name)
3537c478bd9Sstevel@tonic-gate {
3547c478bd9Sstevel@tonic-gate 	mdb_var_t *v = mdb_nv_lookup(&mdb.m_modules, name);
3557c478bd9Sstevel@tonic-gate 	mdb_module_t *mod;
3563b6e0a59SMatt Amdur 	const mdb_dcmd_t *dcp;
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	if (v == NULL)
3597c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_NOMOD));
3607c478bd9Sstevel@tonic-gate 
3617c478bd9Sstevel@tonic-gate 	mod = mdb_nv_get_cookie(v);
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	if (mod == &mdb.m_rmod || mod->mod_hdl == NULL)
3647c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_BUILTINMOD));
3657c478bd9Sstevel@tonic-gate 
3667c478bd9Sstevel@tonic-gate 	mdb_dprintf(MDB_DBG_MODULE, "unloading %s\n", name);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 	if (mod->mod_fini != NULL) {
3697c478bd9Sstevel@tonic-gate 		mdb_dprintf(MDB_DBG_MODULE, "calling %s`_mdb_fini\n", name);
3707c478bd9Sstevel@tonic-gate 		mod->mod_fini();
3717c478bd9Sstevel@tonic-gate 	}
3727c478bd9Sstevel@tonic-gate 
3734a1c2431SJonathan Adams 	mdb_whatis_unregister_module(mod);
3744a1c2431SJonathan Adams 
3757c478bd9Sstevel@tonic-gate 	if (mod->mod_ctfp != NULL)
3767c478bd9Sstevel@tonic-gate 		ctf_close(mod->mod_ctfp);
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	if (mod->mod_cb != NULL)
3797c478bd9Sstevel@tonic-gate 		mdb_callb_remove_by_mod(mod);
3807c478bd9Sstevel@tonic-gate 
3817c478bd9Sstevel@tonic-gate 	if (mod->mod_prev == NULL) {
3827c478bd9Sstevel@tonic-gate 		ASSERT(mdb.m_mhead == mod);
3837c478bd9Sstevel@tonic-gate 		mdb.m_mhead = mod->mod_next;
3847c478bd9Sstevel@tonic-gate 	} else
3857c478bd9Sstevel@tonic-gate 		mod->mod_prev->mod_next = mod->mod_next;
3867c478bd9Sstevel@tonic-gate 
3877c478bd9Sstevel@tonic-gate 	if (mod->mod_next == NULL) {
3887c478bd9Sstevel@tonic-gate 		ASSERT(mdb.m_mtail == mod);
3897c478bd9Sstevel@tonic-gate 		mdb.m_mtail = mod->mod_prev;
3907c478bd9Sstevel@tonic-gate 	} else
3917c478bd9Sstevel@tonic-gate 		mod->mod_next->mod_prev = mod->mod_prev;
3927c478bd9Sstevel@tonic-gate 
3937c478bd9Sstevel@tonic-gate 	while (mdb_nv_size(&mod->mod_walkers) != 0) {
3947c478bd9Sstevel@tonic-gate 		mdb_nv_rewind(&mod->mod_walkers);
3957c478bd9Sstevel@tonic-gate 		v = mdb_nv_peek(&mod->mod_walkers);
3967c478bd9Sstevel@tonic-gate 		(void) mdb_module_remove_walker(mod, mdb_nv_get_name(v));
3977c478bd9Sstevel@tonic-gate 	}
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	while (mdb_nv_size(&mod->mod_dcmds) != 0) {
4007c478bd9Sstevel@tonic-gate 		mdb_nv_rewind(&mod->mod_dcmds);
4017c478bd9Sstevel@tonic-gate 		v = mdb_nv_peek(&mod->mod_dcmds);
4027c478bd9Sstevel@tonic-gate 		(void) mdb_module_remove_dcmd(mod, mdb_nv_get_name(v));
4037c478bd9Sstevel@tonic-gate 	}
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	v = mdb_nv_lookup(&mdb.m_modules, name);
4067c478bd9Sstevel@tonic-gate 	ASSERT(v != NULL);
4077c478bd9Sstevel@tonic-gate 	mdb_nv_remove(&mdb.m_modules, v);
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 	(void) dlclose(mod->mod_hdl);
4107c478bd9Sstevel@tonic-gate 
4117c478bd9Sstevel@tonic-gate 	mdb_nv_destroy(&mod->mod_walkers);
4127c478bd9Sstevel@tonic-gate 	mdb_nv_destroy(&mod->mod_dcmds);
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	strfree((char *)mod->mod_name);
4153b6e0a59SMatt Amdur 
4163b6e0a59SMatt Amdur 	if (mod->mod_info->mi_dvers < 4) {
4173b6e0a59SMatt Amdur 		int ii = 0;
4183b6e0a59SMatt Amdur 
4193b6e0a59SMatt Amdur 		for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL;
4203b6e0a59SMatt Amdur 		    dcp++)
4213b6e0a59SMatt Amdur 			ii++;
4223b6e0a59SMatt Amdur 
4233b6e0a59SMatt Amdur 		mdb_free((void *)mod->mod_info->mi_dcmds,
4243b6e0a59SMatt Amdur 		    sizeof (mdb_dcmd_t) * (ii + 1));
4253b6e0a59SMatt Amdur 	}
4263b6e0a59SMatt Amdur 
4277c478bd9Sstevel@tonic-gate 	mdb_free(mod->mod_info, sizeof (mdb_modinfo_t));
4287c478bd9Sstevel@tonic-gate 	mdb_free(mod, sizeof (mdb_module_t));
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	return (0);
4317c478bd9Sstevel@tonic-gate }
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate int
mdb_module_add_dcmd(mdb_module_t * mod,const mdb_dcmd_t * dcp,int flags)4347c478bd9Sstevel@tonic-gate mdb_module_add_dcmd(mdb_module_t *mod, const mdb_dcmd_t *dcp, int flags)
4357c478bd9Sstevel@tonic-gate {
4367c478bd9Sstevel@tonic-gate 	mdb_var_t *v = mdb_nv_lookup(&mod->mod_dcmds, dcp->dc_name);
4377c478bd9Sstevel@tonic-gate 	mdb_idcmd_t *idcp;
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	uint_t nflag = MDB_NV_OVERLOAD | MDB_NV_SILENT;
4407c478bd9Sstevel@tonic-gate 
4417c478bd9Sstevel@tonic-gate 	if (flags & MDB_MOD_FORCE)
4427c478bd9Sstevel@tonic-gate 		nflag |= MDB_NV_INTERPOS;
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	if (v != NULL)
4457c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_DCMDEXISTS));
4467c478bd9Sstevel@tonic-gate 
4477c478bd9Sstevel@tonic-gate 	idcp = mdb_alloc(sizeof (mdb_idcmd_t), UM_SLEEP);
4487c478bd9Sstevel@tonic-gate 
4497c478bd9Sstevel@tonic-gate 	idcp->idc_usage = dcp->dc_usage;
4507c478bd9Sstevel@tonic-gate 	idcp->idc_descr = dcp->dc_descr;
4517c478bd9Sstevel@tonic-gate 	idcp->idc_help = dcp->dc_help;
4527c478bd9Sstevel@tonic-gate 	idcp->idc_funcp = dcp->dc_funcp;
4533b6e0a59SMatt Amdur 	idcp->idc_tabp = dcp->dc_tabp;
4547c478bd9Sstevel@tonic-gate 	idcp->idc_modp = mod;
4557c478bd9Sstevel@tonic-gate 
4567c478bd9Sstevel@tonic-gate 	v = mdb_nv_insert(&mod->mod_dcmds, dcp->dc_name, NULL,
4577c478bd9Sstevel@tonic-gate 	    (uintptr_t)idcp, MDB_NV_SILENT | MDB_NV_RDONLY);
4587c478bd9Sstevel@tonic-gate 
4597c478bd9Sstevel@tonic-gate 	idcp->idc_name = mdb_nv_get_name(v);
4607c478bd9Sstevel@tonic-gate 	idcp->idc_var = mdb_nv_insert(&mdb.m_dcmds, idcp->idc_name, NULL,
4617c478bd9Sstevel@tonic-gate 	    (uintptr_t)v, nflag);
4627c478bd9Sstevel@tonic-gate 
4637c478bd9Sstevel@tonic-gate 	mdb_dprintf(MDB_DBG_DCMD, "added dcmd %s`%s\n",
4647c478bd9Sstevel@tonic-gate 	    mod->mod_name, idcp->idc_name);
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 	return (0);
4677c478bd9Sstevel@tonic-gate }
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate int
mdb_module_remove_dcmd(mdb_module_t * mod,const char * dname)4707c478bd9Sstevel@tonic-gate mdb_module_remove_dcmd(mdb_module_t *mod, const char *dname)
4717c478bd9Sstevel@tonic-gate {
4727c478bd9Sstevel@tonic-gate 	mdb_var_t *v = mdb_nv_lookup(&mod->mod_dcmds, dname);
4737c478bd9Sstevel@tonic-gate 	mdb_idcmd_t *idcp;
4747c478bd9Sstevel@tonic-gate 	mdb_cmd_t *cp;
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	if (v == NULL)
4777c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_NODCMD));
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	mdb_dprintf(MDB_DBG_DCMD, "removed dcmd %s`%s\n", mod->mod_name, dname);
4807c478bd9Sstevel@tonic-gate 	idcp = mdb_nv_get_cookie(v);
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	/*
4837c478bd9Sstevel@tonic-gate 	 * If we're removing a dcmd that is part of the most recent command,
4847c478bd9Sstevel@tonic-gate 	 * we need to free mdb.m_lastcp so we don't attempt to execute some
4857c478bd9Sstevel@tonic-gate 	 * text we've removed from our address space if -o repeatlast is set.
4867c478bd9Sstevel@tonic-gate 	 */
4877c478bd9Sstevel@tonic-gate 	for (cp = mdb_list_next(&mdb.m_lastc); cp; cp = mdb_list_next(cp)) {
4887c478bd9Sstevel@tonic-gate 		if (cp->c_dcmd == idcp) {
4897c478bd9Sstevel@tonic-gate 			while ((cp = mdb_list_next(&mdb.m_lastc)) != NULL) {
4907c478bd9Sstevel@tonic-gate 				mdb_list_delete(&mdb.m_lastc, cp);
4917c478bd9Sstevel@tonic-gate 				mdb_cmd_destroy(cp);
4927c478bd9Sstevel@tonic-gate 			}
4937c478bd9Sstevel@tonic-gate 			break;
4947c478bd9Sstevel@tonic-gate 		}
4957c478bd9Sstevel@tonic-gate 	}
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	mdb_nv_remove(&mdb.m_dcmds, idcp->idc_var);
4987c478bd9Sstevel@tonic-gate 	mdb_nv_remove(&mod->mod_dcmds, v);
4997c478bd9Sstevel@tonic-gate 	mdb_free(idcp, sizeof (mdb_idcmd_t));
5007c478bd9Sstevel@tonic-gate 
5017c478bd9Sstevel@tonic-gate 	return (0);
5027c478bd9Sstevel@tonic-gate }
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5057c478bd9Sstevel@tonic-gate static int
default_walk_init(mdb_walk_state_t * wsp)5067c478bd9Sstevel@tonic-gate default_walk_init(mdb_walk_state_t *wsp)
5077c478bd9Sstevel@tonic-gate {
5087c478bd9Sstevel@tonic-gate 	return (WALK_NEXT);
5097c478bd9Sstevel@tonic-gate }
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate /*ARGSUSED*/
5127c478bd9Sstevel@tonic-gate static void
default_walk_fini(mdb_walk_state_t * wsp)5137c478bd9Sstevel@tonic-gate default_walk_fini(mdb_walk_state_t *wsp)
5147c478bd9Sstevel@tonic-gate {
5157c478bd9Sstevel@tonic-gate 	/* Nothing to do here */
5167c478bd9Sstevel@tonic-gate }
5177c478bd9Sstevel@tonic-gate 
5187c478bd9Sstevel@tonic-gate int
mdb_module_add_walker(mdb_module_t * mod,const mdb_walker_t * wp,int flags)5197c478bd9Sstevel@tonic-gate mdb_module_add_walker(mdb_module_t *mod, const mdb_walker_t *wp, int flags)
5207c478bd9Sstevel@tonic-gate {
5217c478bd9Sstevel@tonic-gate 	mdb_var_t *v = mdb_nv_lookup(&mod->mod_walkers, wp->walk_name);
5227c478bd9Sstevel@tonic-gate 	mdb_iwalker_t *iwp;
5237c478bd9Sstevel@tonic-gate 
5247c478bd9Sstevel@tonic-gate 	uint_t nflag = MDB_NV_OVERLOAD | MDB_NV_SILENT;
5257c478bd9Sstevel@tonic-gate 
5267c478bd9Sstevel@tonic-gate 	if (flags & MDB_MOD_FORCE)
5277c478bd9Sstevel@tonic-gate 		nflag |= MDB_NV_INTERPOS;
5287c478bd9Sstevel@tonic-gate 
5297c478bd9Sstevel@tonic-gate 	if (v != NULL)
5307c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_WALKEXISTS));
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate 	if (wp->walk_descr == NULL || wp->walk_step == NULL)
5337c478bd9Sstevel@tonic-gate 		return (set_errno(EINVAL));
5347c478bd9Sstevel@tonic-gate 
5357c478bd9Sstevel@tonic-gate 	iwp = mdb_alloc(sizeof (mdb_iwalker_t), UM_SLEEP);
5367c478bd9Sstevel@tonic-gate 
5377c478bd9Sstevel@tonic-gate 	iwp->iwlk_descr = strdup(wp->walk_descr);
5387c478bd9Sstevel@tonic-gate 	iwp->iwlk_init = wp->walk_init;
5397c478bd9Sstevel@tonic-gate 	iwp->iwlk_step = wp->walk_step;
5407c478bd9Sstevel@tonic-gate 	iwp->iwlk_fini = wp->walk_fini;
5417c478bd9Sstevel@tonic-gate 	iwp->iwlk_init_arg = wp->walk_init_arg;
5427c478bd9Sstevel@tonic-gate 	iwp->iwlk_modp = mod;
5437c478bd9Sstevel@tonic-gate 
5447c478bd9Sstevel@tonic-gate 	if (iwp->iwlk_init == NULL)
5457c478bd9Sstevel@tonic-gate 		iwp->iwlk_init = default_walk_init;
5467c478bd9Sstevel@tonic-gate 	if (iwp->iwlk_fini == NULL)
5477c478bd9Sstevel@tonic-gate 		iwp->iwlk_fini = default_walk_fini;
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	v = mdb_nv_insert(&mod->mod_walkers, wp->walk_name, NULL,
5507c478bd9Sstevel@tonic-gate 	    (uintptr_t)iwp, MDB_NV_SILENT | MDB_NV_RDONLY);
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate 	iwp->iwlk_name = mdb_nv_get_name(v);
5537c478bd9Sstevel@tonic-gate 	iwp->iwlk_var = mdb_nv_insert(&mdb.m_walkers, iwp->iwlk_name, NULL,
5547c478bd9Sstevel@tonic-gate 	    (uintptr_t)v, nflag);
5557c478bd9Sstevel@tonic-gate 
5567c478bd9Sstevel@tonic-gate 	mdb_dprintf(MDB_DBG_WALK, "added walk %s`%s\n",
5577c478bd9Sstevel@tonic-gate 	    mod->mod_name, iwp->iwlk_name);
5587c478bd9Sstevel@tonic-gate 
5597c478bd9Sstevel@tonic-gate 	return (0);
5607c478bd9Sstevel@tonic-gate }
5617c478bd9Sstevel@tonic-gate 
5627c478bd9Sstevel@tonic-gate int
mdb_module_remove_walker(mdb_module_t * mod,const char * wname)5637c478bd9Sstevel@tonic-gate mdb_module_remove_walker(mdb_module_t *mod, const char *wname)
5647c478bd9Sstevel@tonic-gate {
5657c478bd9Sstevel@tonic-gate 	mdb_var_t *v = mdb_nv_lookup(&mod->mod_walkers, wname);
5667c478bd9Sstevel@tonic-gate 	mdb_iwalker_t *iwp;
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate 	if (v == NULL)
5697c478bd9Sstevel@tonic-gate 		return (set_errno(EMDB_NOWALK));
5707c478bd9Sstevel@tonic-gate 
5717c478bd9Sstevel@tonic-gate 	mdb_dprintf(MDB_DBG_WALK, "removed walk %s`%s\n", mod->mod_name, wname);
5727c478bd9Sstevel@tonic-gate 
5737c478bd9Sstevel@tonic-gate 	iwp = mdb_nv_get_cookie(v);
5747c478bd9Sstevel@tonic-gate 	mdb_nv_remove(&mdb.m_walkers, iwp->iwlk_var);
5757c478bd9Sstevel@tonic-gate 	mdb_nv_remove(&mod->mod_walkers, v);
5767c478bd9Sstevel@tonic-gate 
5777c478bd9Sstevel@tonic-gate 	strfree(iwp->iwlk_descr);
5787c478bd9Sstevel@tonic-gate 	mdb_free(iwp, sizeof (mdb_iwalker_t));
5797c478bd9Sstevel@tonic-gate 
5807c478bd9Sstevel@tonic-gate 	return (0);
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate 
5837c478bd9Sstevel@tonic-gate void
mdb_module_unload_all(int mode)5847c478bd9Sstevel@tonic-gate mdb_module_unload_all(int mode)
5857c478bd9Sstevel@tonic-gate {
5867c478bd9Sstevel@tonic-gate 	mdb_module_t *mod, *pmod;
5877c478bd9Sstevel@tonic-gate 
5887c478bd9Sstevel@tonic-gate 	/*
5897c478bd9Sstevel@tonic-gate 	 * We unload modules in the reverse order in which they were loaded
5907c478bd9Sstevel@tonic-gate 	 * so as to allow _mdb_fini routines to invoke code which may be
5917c478bd9Sstevel@tonic-gate 	 * present in a previously-loaded module (such as mdb_ks, etc.).
5927c478bd9Sstevel@tonic-gate 	 */
5937c478bd9Sstevel@tonic-gate 	for (mod = mdb.m_mtail; mod != NULL; mod = pmod) {
5947c478bd9Sstevel@tonic-gate 		pmod =  mod->mod_prev;
5957c478bd9Sstevel@tonic-gate 		(void) mdb_module_unload(mod->mod_name, mode);
5967c478bd9Sstevel@tonic-gate 	}
5977c478bd9Sstevel@tonic-gate }
598