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 }