xref: /titanic_50/usr/src/lib/libtnfctl/prb_rtld.c (revision 3c112a2b34403220c06c3e2fcac403358cfba168)
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 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Interfaces to sync up with run time linker (rtld) at process start up time
31  * and at dlopen() and dlclose() time
32  * In Solaris 2.6, librtld_db.so should replace this functionality.  Issues
33  * to solve before libtnfctl.so can use librtld_db.so:
34  * 1. Should libtnfctl.so be usable before Solaris 2.6 - If so, cannot use
35  *    librtld_db.so
36  * 2. libtnfctl.so will have to provide <proc_service.h> in order to use
37  *    librtld_db.so.  If libtnfctl.so is now linked into a debugger that
38  *    also provides <proc_service.h>, how will the two co-exist - will the
39  *    linker get confused, or not ?
40  */
41 
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <errno.h>
46 #include <sys/types.h>
47 #include <sys/stat.h>
48 #include <sys/fcntl.h>
49 #include <sys/procfs.h>
50 #include <link.h>
51 
52 #include "tnfctl.h"
53 #include "prb_proc_int.h"
54 #include "dbg.h"
55 
56 
57 static prb_status_t prb_rtld_setup(prb_proc_ctl_t *proc_p, boolean_t *synced);
58 static prb_status_t prb_rtld_wait(prb_proc_ctl_t *proc_p);
59 static prb_status_t bpt(prb_proc_ctl_t *proc_p, uintptr_t addr);
60 static prb_status_t unbpt(prb_proc_ctl_t *proc_p, uintptr_t addr);
61 
62 
63 /* ---------------------------------------------------------------- */
64 /* ----------------------- Public Functions ----------------------- */
65 /* ---------------------------------------------------------------- */
66 
67 
68 /*
69  * prb_rtld_stalk() - setup for a breakpoint when rtld has opened or closed a
70  * shared object.
71  */
72 prb_status_t
73 prb_rtld_stalk(prb_proc_ctl_t *proc_p)
74 {
75 	prb_status_t	prbstat = PRB_STATUS_OK;
76 
77 	DBG_TNF_PROBE_0(prb_rtld_stalk_1, "libtnfctl", "sunw%verbosity 2");
78 
79 	if (!proc_p->bptaddr) {
80 		Elf3264_Dyn	   dentry;
81 		struct r_debug  r_dbg;
82 
83 		if (proc_p->dbgaddr == 0) {
84 			DBG((void) fprintf(stderr,
85 				"prb_rtld_stalk: dbgaddr not set\n"));
86 			return (PRB_STATUS_BADARG);
87 		}
88 
89 		prbstat = prb_proc_read(proc_p, proc_p->dbgaddr,
90 			&dentry, sizeof (dentry));
91 		if (prbstat || !dentry.d_un.d_ptr) {
92 			DBG((void) fprintf(stderr,
93 				"prb_rtld_stalk: error in d_un.d_ptr\n"));
94 			return (prbstat);
95 		}
96 		/* read in the debug struct that it points to */
97 		prbstat = prb_proc_read(proc_p, dentry.d_un.d_ptr,
98 			&r_dbg, sizeof (r_dbg));
99 		if (prbstat)
100 			return (prbstat);
101 
102 		proc_p->bptaddr = r_dbg.r_brk;
103 	}
104 	/* plant a breakpoint trap in the pointed to function */
105 	prbstat = bpt(proc_p, proc_p->bptaddr);
106 	if (prbstat)
107 		return (prbstat);
108 
109 	/* setup process to stop when breakpoint encountered */
110 	prbstat = prb_proc_tracebpt(proc_p, B_TRUE);
111 
112 	return (prbstat);
113 
114 }
115 
116 
117 /*
118  * prb_rtld_unstalk() - remove rtld breakpoint
119  */
120 prb_status_t
121 prb_rtld_unstalk(prb_proc_ctl_t *proc_p)
122 {
123 	prb_status_t	prbstat;
124 
125 	DBG_TNF_PROBE_0(prb_rtld_unstalk_1, "libtnfctl", "sunw%verbosity 2");
126 
127 	/* turn off BPT tracing while out of the water ... */
128 	prbstat = prb_proc_tracebpt(proc_p, B_FALSE);
129 
130 	prbstat = unbpt(proc_p, proc_p->bptaddr);
131 
132 	return (prbstat);
133 }
134 
135 
136 /*
137  * prb_rtld_advance() - we've hit a breakpoint, replace the original
138  * instruction, istep, put the breakpoint back ...
139  */
140 prb_status_t
141 prb_rtld_advance(prb_proc_ctl_t *proc_p)
142 {
143 	prb_status_t	prbstat;
144 
145 	DBG_TNF_PROBE_0(prb_rtld_advance_1, "libtnfctl", "sunw%verbosity 2");
146 
147 	prbstat = prb_proc_clrbptflt(proc_p);
148 	if (prbstat)
149 		return (prbstat);
150 	prbstat = unbpt(proc_p, proc_p->bptaddr);
151 	if (prbstat)
152 		return (prbstat);
153 
154 	prbstat = prb_proc_istepbpt(proc_p);
155 	if (prbstat)
156 		return (prbstat);
157 
158 	prbstat = bpt(proc_p, proc_p->bptaddr);
159 	if (prbstat)
160 		return (prbstat);
161 
162 	return (PRB_STATUS_OK);
163 }
164 
165 /*
166  * checks if process has reached rtld_sync point or not i.e. has rltld
167  * loaded in libraries or not ?  If not, it lets process run until
168  * rtld has mapped in all libraries (no user code would have been
169  * executed, including .init sections)
170  */
171 prb_status_t
172 prb_rtld_sync_if_needed(prb_proc_ctl_t *proc_p)
173 {
174 	prb_status_t	prbstat = PRB_STATUS_OK;
175 	boolean_t	synced = B_FALSE;
176 
177 	prbstat = prb_rtld_setup(proc_p, &synced);
178 	if (prbstat)
179 		return (prbstat);
180 
181 	if (synced == B_FALSE) {
182 		/* wait on target to sync up after rtld maps in all .so's */
183 		prbstat = prb_rtld_wait(proc_p);
184 		if (prbstat)
185 			return (prbstat);
186 	}
187 
188 	return (prbstat);
189 }
190 
191 /* ---------------------------------------------------------------- */
192 /* ----------------------- Private Functions ---------------------- */
193 /* ---------------------------------------------------------------- */
194 
195 /*
196  * prb_rtld_setup() - turns on the flag in the rtld structure so that rtld
197  * executes a getpid() stystem call after it done mapping all shared objects
198  * but before it executes any init code.
199  */
200 static prb_status_t
201 prb_rtld_setup(prb_proc_ctl_t *proc_p, boolean_t *synced)
202 {
203 	prb_status_t	prbstat = PRB_STATUS_OK;
204 	Elf3264_Dyn	dentry;
205 
206 	DBG_TNF_PROBE_0(prb_rtld_setup_1, "libtnfctl", "sunw%verbosity 2");
207 
208 	if (proc_p->dbgaddr == 0) {
209 		DBG((void) fprintf(stderr,
210 			"prb_rtld_setup: dbgaddr not set\n"));
211 		return (PRB_STATUS_BADARG);
212 	}
213 
214 	prbstat = prb_proc_read(proc_p, proc_p->dbgaddr, &dentry,
215 					sizeof (dentry));
216 	if (prbstat) {
217 		DBG((void) fprintf(stderr,
218 			"prb_rtld_setup: error in d_un.d_ptr\n"));
219 		return (prbstat);
220 	}
221 
222 	if ((dentry.d_un.d_ptr == 0) || (dentry.d_un.d_ptr == 1)) {
223 		*synced = B_FALSE;
224 	} else {
225 		*synced = B_TRUE;
226 		return (PRB_STATUS_OK);
227 	}
228 
229 	/* modify it  - i.e. request rtld to do getpid() */
230 	dentry.d_un.d_ptr = 1;
231 	prbstat = prb_proc_write(proc_p, proc_p->dbgaddr, &dentry,
232 					sizeof (dentry));
233 
234 	return (prbstat);
235 }
236 
237 
238 /*
239  * prb_rtld_wait() - waits on target to execute getpid()
240  */
241 static prb_status_t
242 prb_rtld_wait(prb_proc_ctl_t *proc_p)
243 {
244 	prb_proc_state_t pstate;
245 	prb_status_t	prbstat;
246 
247 	DBG_TNF_PROBE_0(prb_rtld_wait_1, "libtnfctl", "sunw%verbosity 2");
248 
249 	/* stop on exit of getpid() */
250 	prbstat = prb_proc_exit(proc_p, SYS_getpid, PRB_SYS_ADD);
251 	if (prbstat) {
252 		DBG((void) fprintf(stderr,
253 			"prb_rtld_wait: couldn't set up child to stop on "
254 			"exit of getpid(): %s\n", prb_status_str(prbstat)));
255 		return (prbstat);
256 	}
257 	/* stop on entry of exit() - i.e. exec failed */
258 	prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_ADD);
259 	if (prbstat) {
260 		DBG((void) fprintf(stderr,
261 			"prb_rtld_wait: couldn't set up child to stop on "
262 			"entry of exit(): %s\n", prb_status_str(prbstat)));
263 		return (prbstat);
264 	}
265 	/* continue target and wait for it to stop */
266 	prbstat = prb_proc_cont(proc_p);
267 	if (prbstat) {
268 		DBG((void) fprintf(stderr,
269 			"prb_rtld_wait: couldn't continue target process: %s\n",
270 				prb_status_str(prbstat)));
271 		return (prbstat);
272 	}
273 	/* wait for target to stop */
274 	prbstat = prb_proc_wait(proc_p, B_FALSE, NULL);
275 	if (prbstat) {
276 		DBG((void) fprintf(stderr,
277 			"prb_rtld_wait: couldn't wait on target process: %s\n",
278 			prb_status_str(prbstat)));
279 		return (prbstat);
280 	}
281 	/* make sure it did stop on getpid() */
282 	prbstat = prb_proc_state(proc_p, &pstate);
283 	if (prbstat) {
284 		DBG((void) fprintf(stderr,
285 			"prb_rtld_wait: couldn't get state of target: %s\n",
286 				prb_status_str(prbstat)));
287 		return (prbstat);
288 	}
289 	if (pstate.ps_issysentry && (pstate.ps_syscallnum == SYS_exit)) {
290 		DBG((void) fprintf(stderr, "prb_rtld_wait: target exited\n"));
291 		return (prb_status_map(EACCES));
292 	}
293 	/* catch any other errors */
294 	if (!(pstate.ps_issysexit && (pstate.ps_syscallnum == SYS_getpid))) {
295 		DBG((void) fprintf(stderr,
296 			"prb_rtld_wait: target didn't stop on getpid\n"));
297 		return (PRB_STATUS_BADSYNC);
298 	}
299 	/* clear wait on getpid */
300 	prbstat = prb_proc_exit(proc_p, SYS_getpid, PRB_SYS_DEL);
301 	if (prbstat) {
302 		DBG((void) fprintf(stderr,
303 			"prb_rtld_wait: couldn't clear child to stop on "
304 			"exit of getpid(): %s\n", prb_status_str(prbstat)));
305 		return (prbstat);
306 	}
307 	/* clear wait on exit */
308 	prbstat = prb_proc_entry(proc_p, SYS_exit, PRB_SYS_DEL);
309 	if (prbstat) {
310 		DBG((void) fprintf(stderr,
311 			"prb_rtld_wait: couldn't clear child to stop on "
312 			"entry of exit(): %s\n", prb_status_str(prbstat)));
313 		return (prbstat);
314 	}
315 	/* start-stop the process to clear it out of the system call */
316 	prbstat = prb_proc_prstop(proc_p);
317 	if (prbstat) {
318 		DBG((void) fprintf(stderr,
319 			"prb_rtld_wait: couldn't prstop child: %s\n",
320 			prb_status_str(prbstat)));
321 		return (prbstat);
322 	}
323 	return (PRB_STATUS_OK);
324 }
325 
326 
327 #if defined(__sparc)
328 #define	INS_BPT 0x91d02001
329 #elif defined(__i386) || defined(__amd64)
330 #define	INS_BPT 0xcc
331 #else
332 #error  What is your breakpoint instruction?
333 #endif
334 
335 /*
336  * plants a breakpoint at the specified location in
337  * the target process, and saves the existing instruction.
338  */
339 static prb_status_t
340 bpt(prb_proc_ctl_t *proc_p, uintptr_t addr)
341 {
342 	prb_status_t	prbstat;
343 	bptsave_t	instr;
344 
345 	if (!proc_p->bpt_inserted) {
346 
347 		DBG_TNF_PROBE_1(bpt_1, "libtnfctl", "sunw%verbosity 2",
348 			tnf_opaque, bpt_planted_at, addr);
349 
350 		prbstat = prb_proc_read(proc_p, addr,
351 			&(proc_p->saveinstr), sizeof (proc_p->saveinstr));
352 		if (prbstat)
353 			return (prbstat);
354 
355 		DBG_TNF_PROBE_1(bpt_2, "libtnfctl", "sunw%verbosity 2",
356 			tnf_opaque, saved_instr, (unsigned)proc_p->saveinstr);
357 
358 		instr = INS_BPT;
359 
360 		prbstat = prb_proc_write(proc_p, addr,
361 			&instr, sizeof (instr));
362 		if (prbstat)
363 			return (prbstat);
364 
365 		proc_p->bpt_inserted = B_TRUE;
366 	}
367 	return (PRB_STATUS_OK);
368 }
369 
370 /*
371  * removes  a breakpoint at the specified location in
372  * the target process, and replaces it with the original instruction.
373  */
374 prb_status_t
375 unbpt(prb_proc_ctl_t *proc_p, uintptr_t addr)
376 {
377 	prb_status_t	prbstat;
378 
379 	if (proc_p->bpt_inserted) {
380 
381 		DBG_TNF_PROBE_2(unbpt_1, "libtnfctl", "sunw%verbosity 2",
382 			tnf_opaque, unplanting_at, addr,
383 			tnf_opaque, saved_instr, (unsigned)proc_p->saveinstr);
384 
385 		prbstat = prb_proc_write(proc_p, addr, &(proc_p->saveinstr),
386 			sizeof (proc_p->saveinstr));
387 		if (prbstat)
388 			return (prbstat);
389 
390 		proc_p->bpt_inserted = B_FALSE;
391 	}
392 	return (PRB_STATUS_OK);
393 }
394