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