xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_module.c (revision 3b6e0a598869dfc84461624e8699bf51738f68b3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright (c) 2012 by Delphix. All rights reserved.
25  * Copyright (c) 2012 Joyent, Inc. All rights reserved.
26  */
27 
28 #include <sys/param.h>
29 #include <unistd.h>
30 #include <strings.h>
31 #include <dlfcn.h>
32 #include <link.h>
33 
34 #include <mdb/mdb_module.h>
35 #include <mdb/mdb_modapi.h>
36 #include <mdb/mdb_ctf.h>
37 #include <mdb/mdb_debug.h>
38 #include <mdb/mdb_callb.h>
39 #include <mdb/mdb_string.h>
40 #include <mdb/mdb_ks.h>
41 #include <mdb/mdb_err.h>
42 #include <mdb/mdb_io.h>
43 #include <mdb/mdb_frame.h>
44 #include <mdb/mdb_whatis_impl.h>
45 #include <mdb/mdb.h>
46 
47 /*
48  * The format of an mdb dcmd changed between MDB_API_VERSION 3 and 4, with an
49  * addition of a new field to the public interface. To maintain backwards
50  * compatibility with older versions, we know to keep around the old version of
51  * the structure so we can correctly read the set of dcmds passed in.
52  */
53 typedef struct mdb_dcmd_v3 {
54 	const char *dco_name;		/* Command name */
55 	const char *dco_usage;		/* Usage message (optional) */
56 	const char *dco_descr;		/* Description */
57 	mdb_dcmd_f *dco_funcp;		/* Command function */
58 	void (*dco_help)(void);		/* Command help function (or NULL) */
59 } mdb_dcmd_v3_t;
60 
61 /*
62  * For builtin modules, we set mod_init to this function, which just
63  * returns a constant modinfo struct with no dcmds and walkers.
64  */
65 static const mdb_modinfo_t *
66 builtin_init(void)
67 {
68 	static const mdb_modinfo_t info = { MDB_API_VERSION };
69 	return (&info);
70 }
71 
72 int
73 mdb_module_validate_name(const char *name, const char **errmsgp)
74 {
75 	if (strlen(name) == 0) {
76 		*errmsgp = "no module name was specified\n";
77 		return (0);
78 	}
79 
80 	if (strlen(name) > MDB_NV_NAMELEN) {
81 		*errmsgp = "module name '%s' exceeds name length limit\n";
82 		return (0);
83 	}
84 
85 	if (strbadid(name) != NULL) {
86 		*errmsgp = "module name '%s' contains illegal characters\n";
87 		return (0);
88 	}
89 
90 	if (mdb_nv_lookup(&mdb.m_modules, name) != NULL) {
91 		*errmsgp = "%s module is already loaded\n";
92 		return (0);
93 	}
94 
95 	return (1);
96 }
97 
98 int
99 mdb_module_create(const char *name, const char *fname, int mode,
100     mdb_module_t **mpp)
101 {
102 	static const mdb_walker_t empty_walk_list[] = { 0 };
103 	static const mdb_dcmd_t empty_dcmd_list[] = { 0 };
104 
105 	int dlmode = (mode & MDB_MOD_GLOBAL) ? RTLD_GLOBAL : RTLD_LOCAL;
106 
107 	const mdb_modinfo_t *info;
108 	const mdb_dcmd_t *dcp;
109 	const mdb_walker_t *wp;
110 
111 	const mdb_dcmd_v3_t *dcop;
112 	mdb_dcmd_t *dctp = NULL;
113 
114 	mdb_module_t *mod;
115 
116 	mod = mdb_zalloc(sizeof (mdb_module_t), UM_SLEEP);
117 	mod->mod_info = mdb_alloc(sizeof (mdb_modinfo_t), UM_SLEEP);
118 
119 	(void) mdb_nv_create(&mod->mod_dcmds, UM_SLEEP);
120 	(void) mdb_nv_create(&mod->mod_walkers, UM_SLEEP);
121 
122 	mod->mod_name = strdup(name);
123 	mdb.m_lmod = mod;		/* Mark module as currently loading */
124 
125 	if (!(mode & MDB_MOD_BUILTIN)) {
126 		mdb_dprintf(MDB_DBG_MODULE, "dlopen %s %x\n", fname, dlmode);
127 		mod->mod_hdl = dlmopen(LM_ID_BASE, fname, RTLD_NOW | dlmode);
128 
129 		if (mod->mod_hdl == NULL) {
130 			warn("%s\n", dlerror());
131 			goto err;
132 		}
133 
134 		mod->mod_init = (const mdb_modinfo_t *(*)(void))
135 		    dlsym(mod->mod_hdl, "_mdb_init");
136 
137 		mod->mod_fini = (void (*)(void))
138 		    dlsym(mod->mod_hdl, "_mdb_fini");
139 
140 		mod->mod_tgt_ctor = (mdb_tgt_ctor_f *)
141 		    dlsym(mod->mod_hdl, "_mdb_tgt_create");
142 
143 		mod->mod_dis_ctor = (mdb_dis_ctor_f *)
144 		    dlsym(mod->mod_hdl, "_mdb_dis_create");
145 
146 		if (!(mdb.m_flags & MDB_FL_NOCTF))
147 			mod->mod_ctfp = mdb_ctf_open(fname, NULL);
148 	} else {
149 #ifdef _KMDB
150 		/*
151 		 * mdb_ks is a special case - a builtin with _mdb_init and
152 		 * _mdb_fini routines.  If we don't hack it in here, we'll have
153 		 * to duplicate most of the module creation code elsewhere.
154 		 */
155 		if (strcmp(name, "mdb_ks") == 0)
156 			mod->mod_init = mdb_ks_init;
157 		else
158 #endif
159 			mod->mod_init = builtin_init;
160 	}
161 
162 	if (mod->mod_init == NULL) {
163 		warn("%s module is missing _mdb_init definition\n", name);
164 		goto err;
165 	}
166 
167 	if ((info = mod->mod_init()) == NULL) {
168 		warn("%s module failed to initialize\n", name);
169 		goto err;
170 	}
171 
172 	/*
173 	 * Reject modules compiled for a newer version of the debugger.
174 	 */
175 	if (info->mi_dvers > MDB_API_VERSION) {
176 		warn("%s module requires newer mdb API version (%hu) than "
177 		    "debugger (%d)\n", name, info->mi_dvers, MDB_API_VERSION);
178 		goto err;
179 	}
180 
181 	/*
182 	 * Load modules compiled for the current API version.
183 	 */
184 	switch (info->mi_dvers) {
185 	case MDB_API_VERSION:
186 	case 3:
187 	case 2:
188 	case 1:
189 		/*
190 		 * Current API version -- copy entire modinfo
191 		 * structure into our own private storage.
192 		 */
193 		bcopy(info, mod->mod_info, sizeof (mdb_modinfo_t));
194 		if (mod->mod_info->mi_dcmds == NULL)
195 			mod->mod_info->mi_dcmds = empty_dcmd_list;
196 		if (mod->mod_info->mi_walkers == NULL)
197 			mod->mod_info->mi_walkers = empty_walk_list;
198 		break;
199 	default:
200 		/*
201 		 * Too old to be compatible -- abort the load.
202 		 */
203 		warn("%s module is compiled for obsolete mdb API "
204 		    "version %hu\n", name, info->mi_dvers);
205 		goto err;
206 	}
207 
208 	/*
209 	 * In MDB_API_VERSION 4, the size of the mdb_dcmd_t struct changed. If
210 	 * our module is from an earlier version, we need to walk it in the old
211 	 * structure and convert it to the new one.
212 	 *
213 	 * Note that we purposefully don't predicate on whether or not we have
214 	 * the empty list case and duplicate it anyways. That case is rare and
215 	 * it makes our logic simpler when we need to unload the module.
216 	 */
217 	if (info->mi_dvers < 4) {
218 		int ii = 0;
219 		for (dcop = (mdb_dcmd_v3_t *)&mod->mod_info->mi_dcmds[0];
220 		    dcop->dco_name != NULL; dcop++)
221 			ii++;
222 		/* Don't forget null terminated one at the end */
223 		dctp = mdb_zalloc(sizeof (mdb_dcmd_t) * (ii + 1), UM_SLEEP);
224 		ii = 0;
225 		for (dcop = (mdb_dcmd_v3_t *)&mod->mod_info->mi_dcmds[0];
226 		    dcop->dco_name != NULL; dcop++, ii++) {
227 			dctp[ii].dc_name = dcop->dco_name;
228 			dctp[ii].dc_usage = dcop->dco_usage;
229 			dctp[ii].dc_descr = dcop->dco_descr;
230 			dctp[ii].dc_funcp = dcop->dco_funcp;
231 			dctp[ii].dc_help = dcop->dco_help;
232 			dctp[ii].dc_tabp = NULL;
233 		}
234 		mod->mod_info->mi_dcmds = dctp;
235 	}
236 
237 	/*
238 	 * Before we actually go ahead with the load, we need to check
239 	 * each dcmd and walk structure for any invalid values:
240 	 */
241 	for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL; dcp++) {
242 		if (strbadid(dcp->dc_name) != NULL) {
243 			warn("dcmd name '%s' contains illegal characters\n",
244 			    dcp->dc_name);
245 			goto err;
246 		}
247 
248 		if (dcp->dc_descr == NULL) {
249 			warn("dcmd '%s' must have a description\n",
250 			    dcp->dc_name);
251 			goto err;
252 		}
253 
254 		if (dcp->dc_funcp == NULL) {
255 			warn("dcmd '%s' has a NULL function pointer\n",
256 			    dcp->dc_name);
257 			goto err;
258 		}
259 	}
260 
261 	for (wp = &mod->mod_info->mi_walkers[0]; wp->walk_name != NULL; wp++) {
262 		if (strbadid(wp->walk_name) != NULL) {
263 			warn("walk name '%s' contains illegal characters\n",
264 			    wp->walk_name);
265 			goto err;
266 		}
267 
268 		if (wp->walk_descr == NULL) {
269 			warn("walk '%s' must have a description\n",
270 			    wp->walk_name);
271 			goto err;
272 		}
273 
274 		if (wp->walk_step == NULL) {
275 			warn("walk '%s' has a NULL walk_step function\n",
276 			    wp->walk_name);
277 			goto err;
278 		}
279 	}
280 
281 	/*
282 	 * Now that we've established that there are no problems,
283 	 * we can go ahead and hash the module, and its dcmds and walks:
284 	 */
285 	(void) mdb_nv_insert(&mdb.m_modules, mod->mod_name, NULL,
286 	    (uintptr_t)mod, MDB_NV_RDONLY|MDB_NV_EXTNAME);
287 
288 	for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL; dcp++) {
289 		if (mdb_module_add_dcmd(mod, dcp, mode) == -1)
290 			warn("failed to load dcmd %s`%s", name, dcp->dc_name);
291 	}
292 
293 	for (wp = &mod->mod_info->mi_walkers[0]; wp->walk_name != NULL; wp++) {
294 		if (mdb_module_add_walker(mod, wp, mode) == -1)
295 			warn("failed to load walk %s`%s", name, wp->walk_name);
296 	}
297 
298 	/*
299 	 * Add the module to the end of the list of modules in load-dependency
300 	 * order.  We maintain this list so we can unload in reverse order.
301 	 */
302 	if (mdb.m_mtail != NULL) {
303 		ASSERT(mdb.m_mtail->mod_next == NULL);
304 		mdb.m_mtail->mod_next = mod;
305 		mod->mod_prev = mdb.m_mtail;
306 		mdb.m_mtail = mod;
307 	} else {
308 		ASSERT(mdb.m_mhead == NULL);
309 		mdb.m_mtail = mdb.m_mhead = mod;
310 	}
311 
312 	mdb.m_lmod = NULL;
313 	if (mpp != NULL)
314 		*mpp = mod;
315 	return (0);
316 
317 err:
318 	mdb_whatis_unregister_module(mod);
319 
320 	if (mod->mod_ctfp != NULL)
321 		ctf_close(mod->mod_ctfp);
322 
323 	if (mod->mod_hdl != NULL)
324 		(void) dlclose(mod->mod_hdl);
325 
326 	mdb_nv_destroy(&mod->mod_dcmds);
327 	mdb_nv_destroy(&mod->mod_walkers);
328 
329 	strfree((char *)mod->mod_name);
330 	mdb_free(mod->mod_info, sizeof (mdb_modinfo_t));
331 	mdb_free(mod, sizeof (mdb_module_t));
332 
333 	mdb.m_lmod = NULL;
334 	return (-1);
335 }
336 
337 mdb_module_t *
338 mdb_module_load_builtin(const char *name)
339 {
340 	mdb_module_t *mp;
341 
342 	if (mdb_module_create(name, NULL, MDB_MOD_BUILTIN, &mp) < 0)
343 		return (NULL);
344 	return (mp);
345 }
346 
347 int
348 mdb_module_unload_common(const char *name)
349 {
350 	mdb_var_t *v = mdb_nv_lookup(&mdb.m_modules, name);
351 	mdb_module_t *mod;
352 	const mdb_dcmd_t *dcp;
353 
354 	if (v == NULL)
355 		return (set_errno(EMDB_NOMOD));
356 
357 	mod = mdb_nv_get_cookie(v);
358 
359 	if (mod == &mdb.m_rmod || mod->mod_hdl == NULL)
360 		return (set_errno(EMDB_BUILTINMOD));
361 
362 	mdb_dprintf(MDB_DBG_MODULE, "unloading %s\n", name);
363 
364 	if (mod->mod_fini != NULL) {
365 		mdb_dprintf(MDB_DBG_MODULE, "calling %s`_mdb_fini\n", name);
366 		mod->mod_fini();
367 	}
368 
369 	mdb_whatis_unregister_module(mod);
370 
371 	if (mod->mod_ctfp != NULL)
372 		ctf_close(mod->mod_ctfp);
373 
374 	if (mod->mod_cb != NULL)
375 		mdb_callb_remove_by_mod(mod);
376 
377 	if (mod->mod_prev == NULL) {
378 		ASSERT(mdb.m_mhead == mod);
379 		mdb.m_mhead = mod->mod_next;
380 	} else
381 		mod->mod_prev->mod_next = mod->mod_next;
382 
383 	if (mod->mod_next == NULL) {
384 		ASSERT(mdb.m_mtail == mod);
385 		mdb.m_mtail = mod->mod_prev;
386 	} else
387 		mod->mod_next->mod_prev = mod->mod_prev;
388 
389 	while (mdb_nv_size(&mod->mod_walkers) != 0) {
390 		mdb_nv_rewind(&mod->mod_walkers);
391 		v = mdb_nv_peek(&mod->mod_walkers);
392 		(void) mdb_module_remove_walker(mod, mdb_nv_get_name(v));
393 	}
394 
395 	while (mdb_nv_size(&mod->mod_dcmds) != 0) {
396 		mdb_nv_rewind(&mod->mod_dcmds);
397 		v = mdb_nv_peek(&mod->mod_dcmds);
398 		(void) mdb_module_remove_dcmd(mod, mdb_nv_get_name(v));
399 	}
400 
401 	v = mdb_nv_lookup(&mdb.m_modules, name);
402 	ASSERT(v != NULL);
403 	mdb_nv_remove(&mdb.m_modules, v);
404 
405 	(void) dlclose(mod->mod_hdl);
406 
407 	mdb_nv_destroy(&mod->mod_walkers);
408 	mdb_nv_destroy(&mod->mod_dcmds);
409 
410 	strfree((char *)mod->mod_name);
411 
412 	if (mod->mod_info->mi_dvers < 4) {
413 		int ii = 0;
414 
415 		for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL;
416 		    dcp++)
417 			ii++;
418 
419 		mdb_free((void *)mod->mod_info->mi_dcmds,
420 		    sizeof (mdb_dcmd_t) * (ii + 1));
421 	}
422 
423 	mdb_free(mod->mod_info, sizeof (mdb_modinfo_t));
424 	mdb_free(mod, sizeof (mdb_module_t));
425 
426 	return (0);
427 }
428 
429 int
430 mdb_module_add_dcmd(mdb_module_t *mod, const mdb_dcmd_t *dcp, int flags)
431 {
432 	mdb_var_t *v = mdb_nv_lookup(&mod->mod_dcmds, dcp->dc_name);
433 	mdb_idcmd_t *idcp;
434 
435 	uint_t nflag = MDB_NV_OVERLOAD | MDB_NV_SILENT;
436 
437 	if (flags & MDB_MOD_FORCE)
438 		nflag |= MDB_NV_INTERPOS;
439 
440 	if (v != NULL)
441 		return (set_errno(EMDB_DCMDEXISTS));
442 
443 	idcp = mdb_alloc(sizeof (mdb_idcmd_t), UM_SLEEP);
444 
445 	idcp->idc_usage = dcp->dc_usage;
446 	idcp->idc_descr = dcp->dc_descr;
447 	idcp->idc_help = dcp->dc_help;
448 	idcp->idc_funcp = dcp->dc_funcp;
449 	idcp->idc_tabp = dcp->dc_tabp;
450 	idcp->idc_modp = mod;
451 
452 	v = mdb_nv_insert(&mod->mod_dcmds, dcp->dc_name, NULL,
453 	    (uintptr_t)idcp, MDB_NV_SILENT | MDB_NV_RDONLY);
454 
455 	idcp->idc_name = mdb_nv_get_name(v);
456 	idcp->idc_var = mdb_nv_insert(&mdb.m_dcmds, idcp->idc_name, NULL,
457 	    (uintptr_t)v, nflag);
458 
459 	mdb_dprintf(MDB_DBG_DCMD, "added dcmd %s`%s\n",
460 	    mod->mod_name, idcp->idc_name);
461 
462 	return (0);
463 }
464 
465 int
466 mdb_module_remove_dcmd(mdb_module_t *mod, const char *dname)
467 {
468 	mdb_var_t *v = mdb_nv_lookup(&mod->mod_dcmds, dname);
469 	mdb_idcmd_t *idcp;
470 	mdb_cmd_t *cp;
471 
472 	if (v == NULL)
473 		return (set_errno(EMDB_NODCMD));
474 
475 	mdb_dprintf(MDB_DBG_DCMD, "removed dcmd %s`%s\n", mod->mod_name, dname);
476 	idcp = mdb_nv_get_cookie(v);
477 
478 	/*
479 	 * If we're removing a dcmd that is part of the most recent command,
480 	 * we need to free mdb.m_lastcp so we don't attempt to execute some
481 	 * text we've removed from our address space if -o repeatlast is set.
482 	 */
483 	for (cp = mdb_list_next(&mdb.m_lastc); cp; cp = mdb_list_next(cp)) {
484 		if (cp->c_dcmd == idcp) {
485 			while ((cp = mdb_list_next(&mdb.m_lastc)) != NULL) {
486 				mdb_list_delete(&mdb.m_lastc, cp);
487 				mdb_cmd_destroy(cp);
488 			}
489 			break;
490 		}
491 	}
492 
493 	mdb_nv_remove(&mdb.m_dcmds, idcp->idc_var);
494 	mdb_nv_remove(&mod->mod_dcmds, v);
495 	mdb_free(idcp, sizeof (mdb_idcmd_t));
496 
497 	return (0);
498 }
499 
500 /*ARGSUSED*/
501 static int
502 default_walk_init(mdb_walk_state_t *wsp)
503 {
504 	return (WALK_NEXT);
505 }
506 
507 /*ARGSUSED*/
508 static void
509 default_walk_fini(mdb_walk_state_t *wsp)
510 {
511 	/* Nothing to do here */
512 }
513 
514 int
515 mdb_module_add_walker(mdb_module_t *mod, const mdb_walker_t *wp, int flags)
516 {
517 	mdb_var_t *v = mdb_nv_lookup(&mod->mod_walkers, wp->walk_name);
518 	mdb_iwalker_t *iwp;
519 
520 	uint_t nflag = MDB_NV_OVERLOAD | MDB_NV_SILENT;
521 
522 	if (flags & MDB_MOD_FORCE)
523 		nflag |= MDB_NV_INTERPOS;
524 
525 	if (v != NULL)
526 		return (set_errno(EMDB_WALKEXISTS));
527 
528 	if (wp->walk_descr == NULL || wp->walk_step == NULL)
529 		return (set_errno(EINVAL));
530 
531 	iwp = mdb_alloc(sizeof (mdb_iwalker_t), UM_SLEEP);
532 
533 	iwp->iwlk_descr = strdup(wp->walk_descr);
534 	iwp->iwlk_init = wp->walk_init;
535 	iwp->iwlk_step = wp->walk_step;
536 	iwp->iwlk_fini = wp->walk_fini;
537 	iwp->iwlk_init_arg = wp->walk_init_arg;
538 	iwp->iwlk_modp = mod;
539 
540 	if (iwp->iwlk_init == NULL)
541 		iwp->iwlk_init = default_walk_init;
542 	if (iwp->iwlk_fini == NULL)
543 		iwp->iwlk_fini = default_walk_fini;
544 
545 	v = mdb_nv_insert(&mod->mod_walkers, wp->walk_name, NULL,
546 	    (uintptr_t)iwp, MDB_NV_SILENT | MDB_NV_RDONLY);
547 
548 	iwp->iwlk_name = mdb_nv_get_name(v);
549 	iwp->iwlk_var = mdb_nv_insert(&mdb.m_walkers, iwp->iwlk_name, NULL,
550 	    (uintptr_t)v, nflag);
551 
552 	mdb_dprintf(MDB_DBG_WALK, "added walk %s`%s\n",
553 	    mod->mod_name, iwp->iwlk_name);
554 
555 	return (0);
556 }
557 
558 int
559 mdb_module_remove_walker(mdb_module_t *mod, const char *wname)
560 {
561 	mdb_var_t *v = mdb_nv_lookup(&mod->mod_walkers, wname);
562 	mdb_iwalker_t *iwp;
563 
564 	if (v == NULL)
565 		return (set_errno(EMDB_NOWALK));
566 
567 	mdb_dprintf(MDB_DBG_WALK, "removed walk %s`%s\n", mod->mod_name, wname);
568 
569 	iwp = mdb_nv_get_cookie(v);
570 	mdb_nv_remove(&mdb.m_walkers, iwp->iwlk_var);
571 	mdb_nv_remove(&mod->mod_walkers, v);
572 
573 	strfree(iwp->iwlk_descr);
574 	mdb_free(iwp, sizeof (mdb_iwalker_t));
575 
576 	return (0);
577 }
578 
579 void
580 mdb_module_unload_all(int mode)
581 {
582 	mdb_module_t *mod, *pmod;
583 
584 	/*
585 	 * We unload modules in the reverse order in which they were loaded
586 	 * so as to allow _mdb_fini routines to invoke code which may be
587 	 * present in a previously-loaded module (such as mdb_ks, etc.).
588 	 */
589 	for (mod = mdb.m_mtail; mod != NULL; mod = pmod) {
590 		pmod =  mod->mod_prev;
591 		(void) mdb_module_unload(mod->mod_name, mode);
592 	}
593 }
594