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 */ 25 26 #include <sys/param.h> 27 #include <unistd.h> 28 #include <strings.h> 29 #include <dlfcn.h> 30 #include <link.h> 31 32 #include <mdb/mdb_module.h> 33 #include <mdb/mdb_modapi.h> 34 #include <mdb/mdb_ctf.h> 35 #include <mdb/mdb_debug.h> 36 #include <mdb/mdb_callb.h> 37 #include <mdb/mdb_string.h> 38 #include <mdb/mdb_ks.h> 39 #include <mdb/mdb_err.h> 40 #include <mdb/mdb_io.h> 41 #include <mdb/mdb_frame.h> 42 #include <mdb/mdb_whatis_impl.h> 43 #include <mdb/mdb.h> 44 45 /* 46 * For builtin modules, we set mod_init to this function, which just 47 * returns a constant modinfo struct with no dcmds and walkers. 48 */ 49 static const mdb_modinfo_t * 50 builtin_init(void) 51 { 52 static const mdb_modinfo_t info = { MDB_API_VERSION }; 53 return (&info); 54 } 55 56 int 57 mdb_module_validate_name(const char *name, const char **errmsgp) 58 { 59 if (strlen(name) == 0) { 60 *errmsgp = "no module name was specified\n"; 61 return (0); 62 } 63 64 if (strlen(name) > MDB_NV_NAMELEN) { 65 *errmsgp = "module name '%s' exceeds name length limit\n"; 66 return (0); 67 } 68 69 if (strbadid(name) != NULL) { 70 *errmsgp = "module name '%s' contains illegal characters\n"; 71 return (0); 72 } 73 74 if (mdb_nv_lookup(&mdb.m_modules, name) != NULL) { 75 *errmsgp = "%s module is already loaded\n"; 76 return (0); 77 } 78 79 return (1); 80 } 81 82 int 83 mdb_module_create(const char *name, const char *fname, int mode, 84 mdb_module_t **mpp) 85 { 86 static const mdb_walker_t empty_walk_list[] = { 0 }; 87 static const mdb_dcmd_t empty_dcmd_list[] = { 0 }; 88 89 int dlmode = (mode & MDB_MOD_GLOBAL) ? RTLD_GLOBAL : RTLD_LOCAL; 90 91 const mdb_modinfo_t *info; 92 const mdb_dcmd_t *dcp; 93 const mdb_walker_t *wp; 94 95 mdb_module_t *mod; 96 97 mod = mdb_zalloc(sizeof (mdb_module_t), UM_SLEEP); 98 mod->mod_info = mdb_alloc(sizeof (mdb_modinfo_t), UM_SLEEP); 99 100 (void) mdb_nv_create(&mod->mod_dcmds, UM_SLEEP); 101 (void) mdb_nv_create(&mod->mod_walkers, UM_SLEEP); 102 103 mod->mod_name = strdup(name); 104 mdb.m_lmod = mod; /* Mark module as currently loading */ 105 106 if (!(mode & MDB_MOD_BUILTIN)) { 107 mdb_dprintf(MDB_DBG_MODULE, "dlopen %s %x\n", fname, dlmode); 108 mod->mod_hdl = dlmopen(LM_ID_BASE, fname, RTLD_NOW | dlmode); 109 110 if (mod->mod_hdl == NULL) { 111 warn("%s\n", dlerror()); 112 goto err; 113 } 114 115 mod->mod_init = (const mdb_modinfo_t *(*)(void)) 116 dlsym(mod->mod_hdl, "_mdb_init"); 117 118 mod->mod_fini = (void (*)(void)) 119 dlsym(mod->mod_hdl, "_mdb_fini"); 120 121 mod->mod_tgt_ctor = (mdb_tgt_ctor_f *) 122 dlsym(mod->mod_hdl, "_mdb_tgt_create"); 123 124 mod->mod_dis_ctor = (mdb_dis_ctor_f *) 125 dlsym(mod->mod_hdl, "_mdb_dis_create"); 126 127 if (!(mdb.m_flags & MDB_FL_NOCTF)) 128 mod->mod_ctfp = mdb_ctf_open(fname, NULL); 129 } else { 130 #ifdef _KMDB 131 /* 132 * mdb_ks is a special case - a builtin with _mdb_init and 133 * _mdb_fini routines. If we don't hack it in here, we'll have 134 * to duplicate most of the module creation code elsewhere. 135 */ 136 if (strcmp(name, "mdb_ks") == 0) 137 mod->mod_init = mdb_ks_init; 138 else 139 #endif 140 mod->mod_init = builtin_init; 141 } 142 143 if (mod->mod_init == NULL) { 144 warn("%s module is missing _mdb_init definition\n", name); 145 goto err; 146 } 147 148 if ((info = mod->mod_init()) == NULL) { 149 warn("%s module failed to initialize\n", name); 150 goto err; 151 } 152 153 /* 154 * Reject modules compiled for a newer version of the debugger. 155 */ 156 if (info->mi_dvers > MDB_API_VERSION) { 157 warn("%s module requires newer mdb API version (%hu) than " 158 "debugger (%d)\n", name, info->mi_dvers, MDB_API_VERSION); 159 goto err; 160 } 161 162 /* 163 * Load modules compiled for the current API version. 164 */ 165 switch (info->mi_dvers) { 166 case MDB_API_VERSION: 167 case 2: 168 case 1: 169 /* 170 * Current API version -- copy entire modinfo 171 * structure into our own private storage. 172 */ 173 bcopy(info, mod->mod_info, sizeof (mdb_modinfo_t)); 174 if (mod->mod_info->mi_dcmds == NULL) 175 mod->mod_info->mi_dcmds = empty_dcmd_list; 176 if (mod->mod_info->mi_walkers == NULL) 177 mod->mod_info->mi_walkers = empty_walk_list; 178 break; 179 default: 180 /* 181 * Too old to be compatible -- abort the load. 182 */ 183 warn("%s module is compiled for obsolete mdb API " 184 "version %hu\n", name, info->mi_dvers); 185 goto err; 186 } 187 188 /* 189 * Before we actually go ahead with the load, we need to check 190 * each dcmd and walk structure for any invalid values: 191 */ 192 for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL; dcp++) { 193 if (strbadid(dcp->dc_name) != NULL) { 194 warn("dcmd name '%s' contains illegal characters\n", 195 dcp->dc_name); 196 goto err; 197 } 198 199 if (dcp->dc_descr == NULL) { 200 warn("dcmd '%s' must have a description\n", 201 dcp->dc_name); 202 goto err; 203 } 204 205 if (dcp->dc_funcp == NULL) { 206 warn("dcmd '%s' has a NULL function pointer\n", 207 dcp->dc_name); 208 goto err; 209 } 210 } 211 212 for (wp = &mod->mod_info->mi_walkers[0]; wp->walk_name != NULL; wp++) { 213 if (strbadid(wp->walk_name) != NULL) { 214 warn("walk name '%s' contains illegal characters\n", 215 wp->walk_name); 216 goto err; 217 } 218 219 if (wp->walk_descr == NULL) { 220 warn("walk '%s' must have a description\n", 221 wp->walk_name); 222 goto err; 223 } 224 225 if (wp->walk_step == NULL) { 226 warn("walk '%s' has a NULL walk_step function\n", 227 wp->walk_name); 228 goto err; 229 } 230 } 231 232 /* 233 * Now that we've established that there are no problems, 234 * we can go ahead and hash the module, and its dcmds and walks: 235 */ 236 (void) mdb_nv_insert(&mdb.m_modules, mod->mod_name, NULL, 237 (uintptr_t)mod, MDB_NV_RDONLY|MDB_NV_EXTNAME); 238 239 for (dcp = &mod->mod_info->mi_dcmds[0]; dcp->dc_name != NULL; dcp++) { 240 if (mdb_module_add_dcmd(mod, dcp, mode) == -1) 241 warn("failed to load dcmd %s`%s", name, dcp->dc_name); 242 } 243 244 for (wp = &mod->mod_info->mi_walkers[0]; wp->walk_name != NULL; wp++) { 245 if (mdb_module_add_walker(mod, wp, mode) == -1) 246 warn("failed to load walk %s`%s", name, wp->walk_name); 247 } 248 249 /* 250 * Add the module to the end of the list of modules in load-dependency 251 * order. We maintain this list so we can unload in reverse order. 252 */ 253 if (mdb.m_mtail != NULL) { 254 ASSERT(mdb.m_mtail->mod_next == NULL); 255 mdb.m_mtail->mod_next = mod; 256 mod->mod_prev = mdb.m_mtail; 257 mdb.m_mtail = mod; 258 } else { 259 ASSERT(mdb.m_mhead == NULL); 260 mdb.m_mtail = mdb.m_mhead = mod; 261 } 262 263 mdb.m_lmod = NULL; 264 if (mpp != NULL) 265 *mpp = mod; 266 return (0); 267 268 err: 269 mdb_whatis_unregister_module(mod); 270 271 if (mod->mod_ctfp != NULL) 272 ctf_close(mod->mod_ctfp); 273 274 if (mod->mod_hdl != NULL) 275 (void) dlclose(mod->mod_hdl); 276 277 mdb_nv_destroy(&mod->mod_dcmds); 278 mdb_nv_destroy(&mod->mod_walkers); 279 280 strfree((char *)mod->mod_name); 281 mdb_free(mod->mod_info, sizeof (mdb_modinfo_t)); 282 mdb_free(mod, sizeof (mdb_module_t)); 283 284 mdb.m_lmod = NULL; 285 return (-1); 286 } 287 288 mdb_module_t * 289 mdb_module_load_builtin(const char *name) 290 { 291 mdb_module_t *mp; 292 293 if (mdb_module_create(name, NULL, MDB_MOD_BUILTIN, &mp) < 0) 294 return (NULL); 295 return (mp); 296 } 297 298 int 299 mdb_module_unload_common(const char *name) 300 { 301 mdb_var_t *v = mdb_nv_lookup(&mdb.m_modules, name); 302 mdb_module_t *mod; 303 304 if (v == NULL) 305 return (set_errno(EMDB_NOMOD)); 306 307 mod = mdb_nv_get_cookie(v); 308 309 if (mod == &mdb.m_rmod || mod->mod_hdl == NULL) 310 return (set_errno(EMDB_BUILTINMOD)); 311 312 mdb_dprintf(MDB_DBG_MODULE, "unloading %s\n", name); 313 314 if (mod->mod_fini != NULL) { 315 mdb_dprintf(MDB_DBG_MODULE, "calling %s`_mdb_fini\n", name); 316 mod->mod_fini(); 317 } 318 319 mdb_whatis_unregister_module(mod); 320 321 if (mod->mod_ctfp != NULL) 322 ctf_close(mod->mod_ctfp); 323 324 if (mod->mod_cb != NULL) 325 mdb_callb_remove_by_mod(mod); 326 327 if (mod->mod_prev == NULL) { 328 ASSERT(mdb.m_mhead == mod); 329 mdb.m_mhead = mod->mod_next; 330 } else 331 mod->mod_prev->mod_next = mod->mod_next; 332 333 if (mod->mod_next == NULL) { 334 ASSERT(mdb.m_mtail == mod); 335 mdb.m_mtail = mod->mod_prev; 336 } else 337 mod->mod_next->mod_prev = mod->mod_prev; 338 339 while (mdb_nv_size(&mod->mod_walkers) != 0) { 340 mdb_nv_rewind(&mod->mod_walkers); 341 v = mdb_nv_peek(&mod->mod_walkers); 342 (void) mdb_module_remove_walker(mod, mdb_nv_get_name(v)); 343 } 344 345 while (mdb_nv_size(&mod->mod_dcmds) != 0) { 346 mdb_nv_rewind(&mod->mod_dcmds); 347 v = mdb_nv_peek(&mod->mod_dcmds); 348 (void) mdb_module_remove_dcmd(mod, mdb_nv_get_name(v)); 349 } 350 351 v = mdb_nv_lookup(&mdb.m_modules, name); 352 ASSERT(v != NULL); 353 mdb_nv_remove(&mdb.m_modules, v); 354 355 (void) dlclose(mod->mod_hdl); 356 357 mdb_nv_destroy(&mod->mod_walkers); 358 mdb_nv_destroy(&mod->mod_dcmds); 359 360 strfree((char *)mod->mod_name); 361 mdb_free(mod->mod_info, sizeof (mdb_modinfo_t)); 362 mdb_free(mod, sizeof (mdb_module_t)); 363 364 return (0); 365 } 366 367 int 368 mdb_module_add_dcmd(mdb_module_t *mod, const mdb_dcmd_t *dcp, int flags) 369 { 370 mdb_var_t *v = mdb_nv_lookup(&mod->mod_dcmds, dcp->dc_name); 371 mdb_idcmd_t *idcp; 372 373 uint_t nflag = MDB_NV_OVERLOAD | MDB_NV_SILENT; 374 375 if (flags & MDB_MOD_FORCE) 376 nflag |= MDB_NV_INTERPOS; 377 378 if (v != NULL) 379 return (set_errno(EMDB_DCMDEXISTS)); 380 381 idcp = mdb_alloc(sizeof (mdb_idcmd_t), UM_SLEEP); 382 383 idcp->idc_usage = dcp->dc_usage; 384 idcp->idc_descr = dcp->dc_descr; 385 idcp->idc_help = dcp->dc_help; 386 idcp->idc_funcp = dcp->dc_funcp; 387 idcp->idc_modp = mod; 388 389 v = mdb_nv_insert(&mod->mod_dcmds, dcp->dc_name, NULL, 390 (uintptr_t)idcp, MDB_NV_SILENT | MDB_NV_RDONLY); 391 392 idcp->idc_name = mdb_nv_get_name(v); 393 idcp->idc_var = mdb_nv_insert(&mdb.m_dcmds, idcp->idc_name, NULL, 394 (uintptr_t)v, nflag); 395 396 mdb_dprintf(MDB_DBG_DCMD, "added dcmd %s`%s\n", 397 mod->mod_name, idcp->idc_name); 398 399 return (0); 400 } 401 402 int 403 mdb_module_remove_dcmd(mdb_module_t *mod, const char *dname) 404 { 405 mdb_var_t *v = mdb_nv_lookup(&mod->mod_dcmds, dname); 406 mdb_idcmd_t *idcp; 407 mdb_cmd_t *cp; 408 409 if (v == NULL) 410 return (set_errno(EMDB_NODCMD)); 411 412 mdb_dprintf(MDB_DBG_DCMD, "removed dcmd %s`%s\n", mod->mod_name, dname); 413 idcp = mdb_nv_get_cookie(v); 414 415 /* 416 * If we're removing a dcmd that is part of the most recent command, 417 * we need to free mdb.m_lastcp so we don't attempt to execute some 418 * text we've removed from our address space if -o repeatlast is set. 419 */ 420 for (cp = mdb_list_next(&mdb.m_lastc); cp; cp = mdb_list_next(cp)) { 421 if (cp->c_dcmd == idcp) { 422 while ((cp = mdb_list_next(&mdb.m_lastc)) != NULL) { 423 mdb_list_delete(&mdb.m_lastc, cp); 424 mdb_cmd_destroy(cp); 425 } 426 break; 427 } 428 } 429 430 mdb_nv_remove(&mdb.m_dcmds, idcp->idc_var); 431 mdb_nv_remove(&mod->mod_dcmds, v); 432 mdb_free(idcp, sizeof (mdb_idcmd_t)); 433 434 return (0); 435 } 436 437 /*ARGSUSED*/ 438 static int 439 default_walk_init(mdb_walk_state_t *wsp) 440 { 441 return (WALK_NEXT); 442 } 443 444 /*ARGSUSED*/ 445 static void 446 default_walk_fini(mdb_walk_state_t *wsp) 447 { 448 /* Nothing to do here */ 449 } 450 451 int 452 mdb_module_add_walker(mdb_module_t *mod, const mdb_walker_t *wp, int flags) 453 { 454 mdb_var_t *v = mdb_nv_lookup(&mod->mod_walkers, wp->walk_name); 455 mdb_iwalker_t *iwp; 456 457 uint_t nflag = MDB_NV_OVERLOAD | MDB_NV_SILENT; 458 459 if (flags & MDB_MOD_FORCE) 460 nflag |= MDB_NV_INTERPOS; 461 462 if (v != NULL) 463 return (set_errno(EMDB_WALKEXISTS)); 464 465 if (wp->walk_descr == NULL || wp->walk_step == NULL) 466 return (set_errno(EINVAL)); 467 468 iwp = mdb_alloc(sizeof (mdb_iwalker_t), UM_SLEEP); 469 470 iwp->iwlk_descr = strdup(wp->walk_descr); 471 iwp->iwlk_init = wp->walk_init; 472 iwp->iwlk_step = wp->walk_step; 473 iwp->iwlk_fini = wp->walk_fini; 474 iwp->iwlk_init_arg = wp->walk_init_arg; 475 iwp->iwlk_modp = mod; 476 477 if (iwp->iwlk_init == NULL) 478 iwp->iwlk_init = default_walk_init; 479 if (iwp->iwlk_fini == NULL) 480 iwp->iwlk_fini = default_walk_fini; 481 482 v = mdb_nv_insert(&mod->mod_walkers, wp->walk_name, NULL, 483 (uintptr_t)iwp, MDB_NV_SILENT | MDB_NV_RDONLY); 484 485 iwp->iwlk_name = mdb_nv_get_name(v); 486 iwp->iwlk_var = mdb_nv_insert(&mdb.m_walkers, iwp->iwlk_name, NULL, 487 (uintptr_t)v, nflag); 488 489 mdb_dprintf(MDB_DBG_WALK, "added walk %s`%s\n", 490 mod->mod_name, iwp->iwlk_name); 491 492 return (0); 493 } 494 495 int 496 mdb_module_remove_walker(mdb_module_t *mod, const char *wname) 497 { 498 mdb_var_t *v = mdb_nv_lookup(&mod->mod_walkers, wname); 499 mdb_iwalker_t *iwp; 500 501 if (v == NULL) 502 return (set_errno(EMDB_NOWALK)); 503 504 mdb_dprintf(MDB_DBG_WALK, "removed walk %s`%s\n", mod->mod_name, wname); 505 506 iwp = mdb_nv_get_cookie(v); 507 mdb_nv_remove(&mdb.m_walkers, iwp->iwlk_var); 508 mdb_nv_remove(&mod->mod_walkers, v); 509 510 strfree(iwp->iwlk_descr); 511 mdb_free(iwp, sizeof (mdb_iwalker_t)); 512 513 return (0); 514 } 515 516 void 517 mdb_module_unload_all(int mode) 518 { 519 mdb_module_t *mod, *pmod; 520 521 /* 522 * We unload modules in the reverse order in which they were loaded 523 * so as to allow _mdb_fini routines to invoke code which may be 524 * present in a previously-loaded module (such as mdb_ks, etc.). 525 */ 526 for (mod = mdb.m_mtail; mod != NULL; mod = pmod) { 527 pmod = mod->mod_prev; 528 (void) mdb_module_unload(mod->mod_name, mode); 529 } 530 } 531