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