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 #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 * 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 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 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 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 506 default_walk_init(mdb_walk_state_t *wsp) 507 { 508 return (WALK_NEXT); 509 } 510 511 /*ARGSUSED*/ 512 static void 513 default_walk_fini(mdb_walk_state_t *wsp) 514 { 515 /* Nothing to do here */ 516 } 517 518 int 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 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 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