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
tdb_notsup()68 tdb_notsup()
69 {
70 return (TD_NOCAPAB); /* return thread_db code for not supported */
71 }
72
73 const mdb_tdb_ops_t *
mdb_tdb_load(const char * path)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
mdb_tdb_flush(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