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 *
builtin_init(void)66 builtin_init(void)
67 {
68 static const mdb_modinfo_t info = { MDB_API_VERSION };
69 return (&info);
70 }
71
72 int
mdb_module_validate_name(const char * name,const char ** errmsgp)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
mdb_module_create(const char * name,const char * fname,int mode,mdb_module_t ** mpp)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 #if MDB_API_VERSION != 5
185 #error "MDB_API_VERSION needs to be checked here"
186 #endif
187 switch (info->mi_dvers) {
188 case MDB_API_VERSION:
189 case 4:
190 case 3:
191 case 2:
192 case 1:
193 /*
194 * Current API version -- copy entire modinfo
195 * structure into our own private storage.
196 */
197 bcopy(info, mod->mod_info, sizeof (mdb_modinfo_t));
198 if (mod->mod_info->mi_dcmds == NULL)
199 mod->mod_info->mi_dcmds = empty_dcmd_list;
200 if (mod->mod_info->mi_walkers == NULL)
201 mod->mod_info->mi_walkers = empty_walk_list;
202 break;
203 default:
204 /*
205 * Too old to be compatible -- abort the load.
206 */
207 warn("%s module is compiled for obsolete mdb API "
208 "version %hu\n", name, info->mi_dvers);
209 goto err;
210 }
211
212 /*
213 * In MDB_API_VERSION 4, the size of the mdb_dcmd_t struct changed. If
214 * our module is from an earlier version, we need to walk it in the old
215 * structure and convert it to the new one.
216 *
217 * Note that we purposefully don't predicate on whether or not we have
218 * the empty list case and duplicate it anyways. That case is rare and
219 * it makes our logic simpler when we need to unload the module.
220 */
221 if (info->mi_dvers < 4) {
222 int ii = 0;
223 for (dcop = (mdb_dcmd_v3_t *)&mod->mod_info->mi_dcmds[0];
224 dcop->dco_name != NULL; dcop++)
225 ii++;
226 /* Don't forget null terminated one at the end */
227 dctp = mdb_zalloc(sizeof (mdb_dcmd_t) * (ii + 1), UM_SLEEP);
228 ii = 0;
229 for (dcop = (mdb_dcmd_v3_t *)&mod->mod_info->mi_dcmds[0];
230 dcop->dco_name != NULL; dcop++, ii++) {
231 dctp[ii].dc_name = dcop->dco_name;
232 dctp[ii].dc_usage = dcop->dco_usage;
233 dctp[ii].dc_descr = dcop->dco_descr;
234 dctp[ii].dc_funcp = dcop->dco_funcp;
235 dctp[ii].dc_help = dcop->dco_help;
236 dctp[ii].dc_tabp = NULL;
237 }
238 mod->mod_info->mi_dcmds = dctp;
239 }
240
241 /*
242 * Before we actually go ahead with the load, we need to check
243 * each dcmd and walk structure for any invalid values:
244 */
245 for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL; dcp++) {
246 if (strbadid(dcp->dc_name) != NULL) {
247 warn("dcmd name '%s' contains illegal characters\n",
248 dcp->dc_name);
249 goto err;
250 }
251
252 if (dcp->dc_descr == NULL) {
253 warn("dcmd '%s' must have a description\n",
254 dcp->dc_name);
255 goto err;
256 }
257
258 if (dcp->dc_funcp == NULL) {
259 warn("dcmd '%s' has a NULL function pointer\n",
260 dcp->dc_name);
261 goto err;
262 }
263 }
264
265 for (wp = &mod->mod_info->mi_walkers[0]; wp->walk_name != NULL; wp++) {
266 if (strbadid(wp->walk_name) != NULL) {
267 warn("walk name '%s' contains illegal characters\n",
268 wp->walk_name);
269 goto err;
270 }
271
272 if (wp->walk_descr == NULL) {
273 warn("walk '%s' must have a description\n",
274 wp->walk_name);
275 goto err;
276 }
277
278 if (wp->walk_step == NULL) {
279 warn("walk '%s' has a NULL walk_step function\n",
280 wp->walk_name);
281 goto err;
282 }
283 }
284
285 /*
286 * Now that we've established that there are no problems,
287 * we can go ahead and hash the module, and its dcmds and walks:
288 */
289 (void) mdb_nv_insert(&mdb.m_modules, mod->mod_name, NULL,
290 (uintptr_t)mod, MDB_NV_RDONLY|MDB_NV_EXTNAME);
291
292 for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL; dcp++) {
293 if (mdb_module_add_dcmd(mod, dcp, mode) == -1)
294 warn("failed to load dcmd %s`%s", name, dcp->dc_name);
295 }
296
297 for (wp = &mod->mod_info->mi_walkers[0]; wp->walk_name != NULL; wp++) {
298 if (mdb_module_add_walker(mod, wp, mode) == -1)
299 warn("failed to load walk %s`%s", name, wp->walk_name);
300 }
301
302 /*
303 * Add the module to the end of the list of modules in load-dependency
304 * order. We maintain this list so we can unload in reverse order.
305 */
306 if (mdb.m_mtail != NULL) {
307 ASSERT(mdb.m_mtail->mod_next == NULL);
308 mdb.m_mtail->mod_next = mod;
309 mod->mod_prev = mdb.m_mtail;
310 mdb.m_mtail = mod;
311 } else {
312 ASSERT(mdb.m_mhead == NULL);
313 mdb.m_mtail = mdb.m_mhead = mod;
314 }
315
316 mdb.m_lmod = NULL;
317 if (mpp != NULL)
318 *mpp = mod;
319 return (0);
320
321 err:
322 mdb_whatis_unregister_module(mod);
323
324 if (mod->mod_ctfp != NULL)
325 ctf_close(mod->mod_ctfp);
326
327 if (mod->mod_hdl != NULL)
328 (void) dlclose(mod->mod_hdl);
329
330 mdb_nv_destroy(&mod->mod_dcmds);
331 mdb_nv_destroy(&mod->mod_walkers);
332
333 strfree((char *)mod->mod_name);
334 mdb_free(mod->mod_info, sizeof (mdb_modinfo_t));
335 mdb_free(mod, sizeof (mdb_module_t));
336
337 mdb.m_lmod = NULL;
338 return (-1);
339 }
340
341 mdb_module_t *
mdb_module_load_builtin(const char * name)342 mdb_module_load_builtin(const char *name)
343 {
344 mdb_module_t *mp;
345
346 if (mdb_module_create(name, NULL, MDB_MOD_BUILTIN, &mp) < 0)
347 return (NULL);
348 return (mp);
349 }
350
351 int
mdb_module_unload_common(const char * name)352 mdb_module_unload_common(const char *name)
353 {
354 mdb_var_t *v = mdb_nv_lookup(&mdb.m_modules, name);
355 mdb_module_t *mod;
356 const mdb_dcmd_t *dcp;
357
358 if (v == NULL)
359 return (set_errno(EMDB_NOMOD));
360
361 mod = mdb_nv_get_cookie(v);
362
363 if (mod == &mdb.m_rmod || mod->mod_hdl == NULL)
364 return (set_errno(EMDB_BUILTINMOD));
365
366 mdb_dprintf(MDB_DBG_MODULE, "unloading %s\n", name);
367
368 if (mod->mod_fini != NULL) {
369 mdb_dprintf(MDB_DBG_MODULE, "calling %s`_mdb_fini\n", name);
370 mod->mod_fini();
371 }
372
373 mdb_whatis_unregister_module(mod);
374
375 if (mod->mod_ctfp != NULL)
376 ctf_close(mod->mod_ctfp);
377
378 if (mod->mod_cb != NULL)
379 mdb_callb_remove_by_mod(mod);
380
381 if (mod->mod_prev == NULL) {
382 ASSERT(mdb.m_mhead == mod);
383 mdb.m_mhead = mod->mod_next;
384 } else
385 mod->mod_prev->mod_next = mod->mod_next;
386
387 if (mod->mod_next == NULL) {
388 ASSERT(mdb.m_mtail == mod);
389 mdb.m_mtail = mod->mod_prev;
390 } else
391 mod->mod_next->mod_prev = mod->mod_prev;
392
393 while (mdb_nv_size(&mod->mod_walkers) != 0) {
394 mdb_nv_rewind(&mod->mod_walkers);
395 v = mdb_nv_peek(&mod->mod_walkers);
396 (void) mdb_module_remove_walker(mod, mdb_nv_get_name(v));
397 }
398
399 while (mdb_nv_size(&mod->mod_dcmds) != 0) {
400 mdb_nv_rewind(&mod->mod_dcmds);
401 v = mdb_nv_peek(&mod->mod_dcmds);
402 (void) mdb_module_remove_dcmd(mod, mdb_nv_get_name(v));
403 }
404
405 v = mdb_nv_lookup(&mdb.m_modules, name);
406 ASSERT(v != NULL);
407 mdb_nv_remove(&mdb.m_modules, v);
408
409 (void) dlclose(mod->mod_hdl);
410
411 mdb_nv_destroy(&mod->mod_walkers);
412 mdb_nv_destroy(&mod->mod_dcmds);
413
414 strfree((char *)mod->mod_name);
415
416 if (mod->mod_info->mi_dvers < 4) {
417 int ii = 0;
418
419 for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL;
420 dcp++)
421 ii++;
422
423 mdb_free((void *)mod->mod_info->mi_dcmds,
424 sizeof (mdb_dcmd_t) * (ii + 1));
425 }
426
427 mdb_free(mod->mod_info, sizeof (mdb_modinfo_t));
428 mdb_free(mod, sizeof (mdb_module_t));
429
430 return (0);
431 }
432
433 int
mdb_module_add_dcmd(mdb_module_t * mod,const mdb_dcmd_t * dcp,int flags)434 mdb_module_add_dcmd(mdb_module_t *mod, const mdb_dcmd_t *dcp, int flags)
435 {
436 mdb_var_t *v = mdb_nv_lookup(&mod->mod_dcmds, dcp->dc_name);
437 mdb_idcmd_t *idcp;
438
439 uint_t nflag = MDB_NV_OVERLOAD | MDB_NV_SILENT;
440
441 if (flags & MDB_MOD_FORCE)
442 nflag |= MDB_NV_INTERPOS;
443
444 if (v != NULL)
445 return (set_errno(EMDB_DCMDEXISTS));
446
447 idcp = mdb_alloc(sizeof (mdb_idcmd_t), UM_SLEEP);
448
449 idcp->idc_usage = dcp->dc_usage;
450 idcp->idc_descr = dcp->dc_descr;
451 idcp->idc_help = dcp->dc_help;
452 idcp->idc_funcp = dcp->dc_funcp;
453 idcp->idc_tabp = dcp->dc_tabp;
454 idcp->idc_modp = mod;
455
456 v = mdb_nv_insert(&mod->mod_dcmds, dcp->dc_name, NULL,
457 (uintptr_t)idcp, MDB_NV_SILENT | MDB_NV_RDONLY);
458
459 idcp->idc_name = mdb_nv_get_name(v);
460 idcp->idc_var = mdb_nv_insert(&mdb.m_dcmds, idcp->idc_name, NULL,
461 (uintptr_t)v, nflag);
462
463 mdb_dprintf(MDB_DBG_DCMD, "added dcmd %s`%s\n",
464 mod->mod_name, idcp->idc_name);
465
466 return (0);
467 }
468
469 int
mdb_module_remove_dcmd(mdb_module_t * mod,const char * dname)470 mdb_module_remove_dcmd(mdb_module_t *mod, const char *dname)
471 {
472 mdb_var_t *v = mdb_nv_lookup(&mod->mod_dcmds, dname);
473 mdb_idcmd_t *idcp;
474 mdb_cmd_t *cp;
475
476 if (v == NULL)
477 return (set_errno(EMDB_NODCMD));
478
479 mdb_dprintf(MDB_DBG_DCMD, "removed dcmd %s`%s\n", mod->mod_name, dname);
480 idcp = mdb_nv_get_cookie(v);
481
482 /*
483 * If we're removing a dcmd that is part of the most recent command,
484 * we need to free mdb.m_lastcp so we don't attempt to execute some
485 * text we've removed from our address space if -o repeatlast is set.
486 */
487 for (cp = mdb_list_next(&mdb.m_lastc); cp; cp = mdb_list_next(cp)) {
488 if (cp->c_dcmd == idcp) {
489 while ((cp = mdb_list_next(&mdb.m_lastc)) != NULL) {
490 mdb_list_delete(&mdb.m_lastc, cp);
491 mdb_cmd_destroy(cp);
492 }
493 break;
494 }
495 }
496
497 mdb_nv_remove(&mdb.m_dcmds, idcp->idc_var);
498 mdb_nv_remove(&mod->mod_dcmds, v);
499 mdb_free(idcp, sizeof (mdb_idcmd_t));
500
501 return (0);
502 }
503
504 /*ARGSUSED*/
505 static int
default_walk_init(mdb_walk_state_t * wsp)506 default_walk_init(mdb_walk_state_t *wsp)
507 {
508 return (WALK_NEXT);
509 }
510
511 /*ARGSUSED*/
512 static void
default_walk_fini(mdb_walk_state_t * wsp)513 default_walk_fini(mdb_walk_state_t *wsp)
514 {
515 /* Nothing to do here */
516 }
517
518 int
mdb_module_add_walker(mdb_module_t * mod,const mdb_walker_t * wp,int flags)519 mdb_module_add_walker(mdb_module_t *mod, const mdb_walker_t *wp, int flags)
520 {
521 mdb_var_t *v = mdb_nv_lookup(&mod->mod_walkers, wp->walk_name);
522 mdb_iwalker_t *iwp;
523
524 uint_t nflag = MDB_NV_OVERLOAD | MDB_NV_SILENT;
525
526 if (flags & MDB_MOD_FORCE)
527 nflag |= MDB_NV_INTERPOS;
528
529 if (v != NULL)
530 return (set_errno(EMDB_WALKEXISTS));
531
532 if (wp->walk_descr == NULL || wp->walk_step == NULL)
533 return (set_errno(EINVAL));
534
535 iwp = mdb_alloc(sizeof (mdb_iwalker_t), UM_SLEEP);
536
537 iwp->iwlk_descr = strdup(wp->walk_descr);
538 iwp->iwlk_init = wp->walk_init;
539 iwp->iwlk_step = wp->walk_step;
540 iwp->iwlk_fini = wp->walk_fini;
541 iwp->iwlk_init_arg = wp->walk_init_arg;
542 iwp->iwlk_modp = mod;
543
544 if (iwp->iwlk_init == NULL)
545 iwp->iwlk_init = default_walk_init;
546 if (iwp->iwlk_fini == NULL)
547 iwp->iwlk_fini = default_walk_fini;
548
549 v = mdb_nv_insert(&mod->mod_walkers, wp->walk_name, NULL,
550 (uintptr_t)iwp, MDB_NV_SILENT | MDB_NV_RDONLY);
551
552 iwp->iwlk_name = mdb_nv_get_name(v);
553 iwp->iwlk_var = mdb_nv_insert(&mdb.m_walkers, iwp->iwlk_name, NULL,
554 (uintptr_t)v, nflag);
555
556 mdb_dprintf(MDB_DBG_WALK, "added walk %s`%s\n",
557 mod->mod_name, iwp->iwlk_name);
558
559 return (0);
560 }
561
562 int
mdb_module_remove_walker(mdb_module_t * mod,const char * wname)563 mdb_module_remove_walker(mdb_module_t *mod, const char *wname)
564 {
565 mdb_var_t *v = mdb_nv_lookup(&mod->mod_walkers, wname);
566 mdb_iwalker_t *iwp;
567
568 if (v == NULL)
569 return (set_errno(EMDB_NOWALK));
570
571 mdb_dprintf(MDB_DBG_WALK, "removed walk %s`%s\n", mod->mod_name, wname);
572
573 iwp = mdb_nv_get_cookie(v);
574 mdb_nv_remove(&mdb.m_walkers, iwp->iwlk_var);
575 mdb_nv_remove(&mod->mod_walkers, v);
576
577 strfree(iwp->iwlk_descr);
578 mdb_free(iwp, sizeof (mdb_iwalker_t));
579
580 return (0);
581 }
582
583 void
mdb_module_unload_all(int mode)584 mdb_module_unload_all(int mode)
585 {
586 mdb_module_t *mod, *pmod;
587
588 /*
589 * We unload modules in the reverse order in which they were loaded
590 * so as to allow _mdb_fini routines to invoke code which may be
591 * present in a previously-loaded module (such as mdb_ks, etc.).
592 */
593 for (mod = mdb.m_mtail; mod != NULL; mod = pmod) {
594 pmod = mod->mod_prev;
595 (void) mdb_module_unload(mod->mod_name, mode);
596 }
597 }
598