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 (c) 2000-2001 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * libthread_db (tdb) cache 31 * 32 * In order to properly debug multi-threaded programs, the proc target must be 33 * able to query and modify information such as a thread's register set using 34 * either the native LWP services provided by libproc (if the process is not 35 * linked with libthread), or using the services provided by libthread_db (if 36 * the process is linked with libthread). Additionally, a process may begin 37 * life as a single-threaded process and then later dlopen() libthread, so we 38 * must be prepared to switch modes on-the-fly. There are also two possible 39 * libthread implementations (one in /usr/lib and one in /usr/lib/lwp) so we 40 * cannot link mdb against libthread_db directly; instead, we must dlopen the 41 * appropriate libthread_db on-the-fly based on which libthread.so the victim 42 * process has open. Finally, mdb is designed so that multiple targets can be 43 * active simultaneously, so we could even have *both* libthread_db's open at 44 * the same time. This might happen if you were looking at two multi-threaded 45 * user processes inside of a crash dump, one using /usr/lib/libthread.so and 46 * the other using /usr/lib/lwp/libthread.so. To meet these requirements, we 47 * implement a libthread_db "cache" in this file. The proc target calls 48 * mdb_tdb_load() with the pathname of a libthread_db to load, and if it is 49 * not already open, we dlopen() it, look up the symbols we need to reference, 50 * and fill in an ops vector which we return to the caller. Once an object is 51 * loaded, we don't bother unloading it unless the entire cache is explicitly 52 * flushed. This mechanism also has the nice property that we don't bother 53 * loading libthread_db until we need it, so the debugger starts up faster. 54 */ 55 56 #include <mdb/mdb_tdb.h> 57 #include <mdb/mdb_modapi.h> 58 #include <mdb/mdb_err.h> 59 60 #include <strings.h> 61 #include <unistd.h> 62 #include <dlfcn.h> 63 #include <link.h> 64 65 static mdb_tdb_lib_t *tdb_list; 66 67 static td_err_e 68 tdb_notsup() 69 { 70 return (TD_NOCAPAB); /* return thread_db code for not supported */ 71 } 72 73 const mdb_tdb_ops_t * 74 mdb_tdb_load(const char *path) 75 { 76 td_err_e (*tdb_init)(void); 77 mdb_tdb_lib_t *t; 78 td_err_e err; 79 void *hdl; 80 81 /* 82 * Search through the existing cache of thread_db libraries and see if 83 * we have this one loaded already. If so, just return its ops vector. 84 */ 85 for (t = tdb_list; t != NULL; t = t->tdb_next) { 86 if (strcmp(path, t->tdb_pathname) == 0) 87 break; 88 } 89 90 if (t != NULL) 91 return (&t->tdb_ops); 92 93 /* 94 * Otherwise dlmopen the new library, look up its td_init() function, 95 * and call it. If any of this fails, we return NULL for failure. 96 */ 97 if (access(path, F_OK) == -1) 98 return (NULL); 99 100 if ((hdl = dlmopen(LM_ID_BASE, path, RTLD_LAZY | RTLD_LOCAL)) == NULL) { 101 (void) set_errno(EMDB_RTLD); 102 return (NULL); 103 } 104 105 if ((tdb_init = (td_err_e (*)(void))dlsym(hdl, "td_init")) == NULL) { 106 (void) dlclose(hdl); 107 (void) set_errno(tdb_to_errno(TD_NOCAPAB)); 108 return (NULL); 109 } 110 111 if ((err = tdb_init()) != TD_OK) { 112 (void) dlclose(hdl); 113 (void) set_errno(tdb_to_errno(err)); 114 return (NULL); 115 } 116 117 /* 118 * If td_init() succeeds, we can't fail from here on. Allocate a new 119 * library entry and add it to our linked list. 120 */ 121 t = mdb_alloc(sizeof (mdb_tdb_lib_t), UM_SLEEP); 122 123 (void) strncpy(t->tdb_pathname, path, MAXPATHLEN); 124 t->tdb_pathname[MAXPATHLEN - 1] = '\0'; 125 t->tdb_handle = hdl; 126 t->tdb_next = tdb_list; 127 tdb_list = t; 128 129 /* 130 * For each function we need to call in the thread_db library, look it 131 * up using dlsym(). If we find it, add it to the ops vector. If not, 132 * put the address of our default function (see above) in that slot. 133 */ 134 135 t->tdb_ops.td_ta_new = (td_err_e (*)())dlsym(hdl, "td_ta_new"); 136 if (t->tdb_ops.td_ta_new == NULL) 137 t->tdb_ops.td_ta_new = (td_err_e (*)())tdb_notsup; 138 139 t->tdb_ops.td_ta_delete = (td_err_e (*)())dlsym(hdl, "td_ta_delete"); 140 if (t->tdb_ops.td_ta_delete == NULL) 141 t->tdb_ops.td_ta_delete = (td_err_e (*)())tdb_notsup; 142 143 t->tdb_ops.td_ta_thr_iter = (td_err_e (*)()) 144 dlsym(hdl, "td_ta_thr_iter"); 145 if (t->tdb_ops.td_ta_thr_iter == NULL) 146 t->tdb_ops.td_ta_thr_iter = (td_err_e (*)())tdb_notsup; 147 148 t->tdb_ops.td_ta_map_id2thr = (td_err_e (*)()) 149 dlsym(hdl, "td_ta_map_id2thr"); 150 if (t->tdb_ops.td_ta_map_id2thr == NULL) 151 t->tdb_ops.td_ta_map_id2thr = (td_err_e (*)())tdb_notsup; 152 153 t->tdb_ops.td_ta_map_lwp2thr = (td_err_e (*)()) 154 dlsym(hdl, "td_ta_map_lwp2thr"); 155 if (t->tdb_ops.td_ta_map_lwp2thr == NULL) 156 t->tdb_ops.td_ta_map_lwp2thr = (td_err_e (*)())tdb_notsup; 157 158 t->tdb_ops.td_thr_get_info = (td_err_e (*)()) 159 dlsym(hdl, "td_thr_get_info"); 160 if (t->tdb_ops.td_thr_get_info == NULL) 161 t->tdb_ops.td_thr_get_info = (td_err_e (*)())tdb_notsup; 162 163 t->tdb_ops.td_thr_getgregs = (td_err_e (*)()) 164 dlsym(hdl, "td_thr_getgregs"); 165 if (t->tdb_ops.td_thr_getgregs == NULL) 166 t->tdb_ops.td_thr_getgregs = (td_err_e (*)())tdb_notsup; 167 168 t->tdb_ops.td_thr_setgregs = (td_err_e (*)()) 169 dlsym(hdl, "td_thr_setgregs"); 170 if (t->tdb_ops.td_thr_setgregs == NULL) 171 t->tdb_ops.td_thr_setgregs = (td_err_e (*)())tdb_notsup; 172 173 t->tdb_ops.td_thr_getfpregs = (td_err_e (*)()) 174 dlsym(hdl, "td_thr_getfpregs"); 175 if (t->tdb_ops.td_thr_getfpregs == NULL) 176 t->tdb_ops.td_thr_getfpregs = (td_err_e (*)())tdb_notsup; 177 178 t->tdb_ops.td_thr_setfpregs = (td_err_e (*)()) 179 dlsym(hdl, "td_thr_setfpregs"); 180 if (t->tdb_ops.td_thr_setfpregs == NULL) 181 t->tdb_ops.td_thr_setfpregs = (td_err_e (*)())tdb_notsup; 182 183 t->tdb_ops.td_thr_tlsbase = (td_err_e (*)()) 184 dlsym(hdl, "td_thr_tlsbase"); 185 if (t->tdb_ops.td_thr_tlsbase == NULL) 186 t->tdb_ops.td_thr_tlsbase = (td_err_e (*)())tdb_notsup; 187 188 #ifdef __sparc 189 t->tdb_ops.td_thr_getxregs = (td_err_e (*)()) 190 dlsym(hdl, "td_thr_getxregs"); 191 if (t->tdb_ops.td_thr_getxregs == NULL) 192 t->tdb_ops.td_thr_getxregs = (td_err_e (*)())tdb_notsup; 193 194 t->tdb_ops.td_thr_setxregs = (td_err_e (*)()) 195 dlsym(hdl, "td_thr_setxregs"); 196 if (t->tdb_ops.td_thr_setxregs == NULL) 197 t->tdb_ops.td_thr_setxregs = (td_err_e (*)())tdb_notsup; 198 #endif /* __sparc */ 199 200 return (&t->tdb_ops); 201 } 202 203 void 204 mdb_tdb_flush(void) 205 { 206 mdb_tdb_lib_t *t, *u; 207 208 for (t = tdb_list; t != NULL; t = u) { 209 u = t->tdb_next; 210 (void) dlclose(t->tdb_handle); 211 mdb_free(t, sizeof (mdb_tdb_lib_t)); 212 } 213 214 tdb_list = NULL; 215 } 216