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