xref: /freebsd/lib/libthread_db/libpthread_db.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*
2  * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <stddef.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <pthread.h>
35 #include <sys/types.h>
36 #include <sys/kse.h>
37 #include <sys/ptrace.h>
38 #include <proc_service.h>
39 #include <thread_db.h>
40 
41 #include "libpthread.h"
42 #include "libpthread_db.h"
43 
44 #define P2T(c) ps2td(c)
45 
46 static void pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp);
47 static int pt_validate(const td_thrhandle_t *th);
48 
49 static int
50 ps2td(int c)
51 {
52 	switch (c) {
53 	case PS_OK:
54 		return TD_OK;
55 	case PS_ERR:
56 		return TD_ERR;
57 	case PS_BADPID:
58 		return TD_BADPH;
59 	case PS_BADLID:
60 		return TD_NOLWP;
61 	case PS_BADADDR:
62 		return TD_ERR;
63 	case PS_NOSYM:
64 		return TD_NOLIBTHREAD;
65 	case PS_NOFREGS:
66 		return TD_NOFPREGS;
67 	default:
68 		return TD_ERR;
69 	}
70 }
71 
72 static long
73 pt_map_thread(const td_thragent_t *const_ta, psaddr_t pt, int type)
74 {
75 	td_thragent_t *ta = __DECONST(td_thragent_t *, const_ta);
76 	struct pt_map *new;
77 	int i, first = -1;
78 
79 	/* leave zero out */
80 	for (i = 1; i < ta->map_len; ++i) {
81 		if (ta->map[i].type == PT_NONE) {
82 			if (first == -1)
83 				first = i;
84 		} else if (ta->map[i].type == type && ta->map[i].thr == pt) {
85 				return (i);
86 		}
87 	}
88 
89 	if (first == -1) {
90 		if (ta->map_len == 0) {
91 			ta->map = calloc(20, sizeof(struct pt_map));
92 			if (ta->map == NULL)
93 				return (-1);
94 			ta->map_len = 20;
95 			first = 1;
96 		} else {
97 			new = realloc(ta->map,
98 			              sizeof(struct pt_map) * ta->map_len * 2);
99 			if (new == NULL)
100 				return (-1);
101 			memset(new + ta->map_len, '\0', sizeof(struct pt_map) *
102 			       ta->map_len);
103 			first = ta->map_len;
104 			ta->map = new;
105 			ta->map_len *= 2;
106 		}
107 	}
108 
109 	ta->map[first].type = type;
110 	ta->map[first].thr = pt;
111 	return (first);
112 }
113 
114 static td_err_e
115 pt_init(void)
116 {
117 	pt_md_init();
118 	return (0);
119 }
120 
121 static td_err_e
122 pt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
123 {
124 #define LOOKUP_SYM(proc, sym, addr) 			\
125 	ret = ps_pglobal_lookup(proc, NULL, sym, addr);	\
126 	if (ret != 0) {					\
127 		TDBG("can not find symbol: %s\n", sym);	\
128 		ret = TD_NOLIBTHREAD;			\
129 		goto error;				\
130 	}
131 
132 	td_thragent_t *ta;
133 	int dbg;
134 	int ret;
135 
136 	TDBG_FUNC();
137 
138 	ta = malloc(sizeof(td_thragent_t));
139 	if (ta == NULL)
140 		return (TD_MALLOC);
141 
142 	ta->ph = ph;
143 	ta->thread_activated = 0;
144 	ta->map = NULL;
145 	ta->map_len = 0;
146 
147 	LOOKUP_SYM(ph, "_libkse_debug",		&ta->libkse_debug_addr);
148 	LOOKUP_SYM(ph, "_thread_list",		&ta->thread_list_addr);
149 	LOOKUP_SYM(ph, "_thread_activated",	&ta->thread_activated_addr);
150 	LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
151 	LOOKUP_SYM(ph, "_thread_keytable",	&ta->thread_keytable_addr);
152 
153 	dbg = getpid();
154 	/*
155 	 * If this fails it probably means we're debugging a core file and
156 	 * can't write to it.
157 	 */
158 	ps_pwrite(ph, ta->libkse_debug_addr, &dbg, sizeof(int));
159 	*pta = ta;
160 	return (0);
161 
162 error:
163 	free(ta);
164 	return (ret);
165 }
166 
167 static td_err_e
168 pt_ta_delete(td_thragent_t *ta)
169 {
170 	int dbg;
171 
172 	TDBG_FUNC();
173 
174 	dbg = 0;
175 	/*
176 	 * Error returns from this write are not really a problem;
177 	 * the process doesn't exist any more.
178 	 */
179 	ps_pwrite(ta->ph, ta->libkse_debug_addr, &dbg, sizeof(int));
180 	if (ta->map)
181 		free(ta->map);
182 	free(ta);
183 	return (TD_OK);
184 }
185 
186 static td_err_e
187 pt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
188 {
189 	prgregset_t gregs;
190 	TAILQ_HEAD(, pthread) thread_list;
191 	psaddr_t pt, tcb_addr;
192 	lwpid_t lwp;
193 	int ret;
194 
195 	TDBG_FUNC();
196 
197 	if (id < 0 || id >= ta->map_len || ta->map[id].type == PT_NONE)
198 		return (TD_NOTHR);
199 	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
200 			sizeof(thread_list));
201 	if (ret != 0)
202 		return (P2T(ret));
203 	pt = (psaddr_t)thread_list.tqh_first;
204 	if (ta->map[id].type == PT_LWP) {
205 		/*
206 		 * if we are referencing a lwp, make sure it was not already
207 		 * mapped to user thread.
208 		 */
209 		while (pt != 0) {
210 			ret = ps_pread(ta->ph,
211 			        pt + offsetof(struct pthread, tcb),
212 			        &tcb_addr, sizeof(tcb_addr));
213 			if (ret != 0)
214 				return (P2T(ret));
215 			ret = ps_pread(ta->ph,
216 			        tcb_addr + offsetof(struct tcb,
217 				  tcb_tmbx.tm_lwp),
218 				&lwp, sizeof(lwp));
219 			if (ret != 0)
220 				return (P2T(ret));
221 			/*
222 			 * If the lwp was already mapped to userland thread,
223 			 * we shouldn't reference it directly in future.
224 			 */
225 			if (lwp == ta->map[id].lwp) {
226 				ta->map[id].type = PT_NONE;
227 				return (TD_NOTHR);
228 			}
229 			/* get next thread */
230 			ret = ps_pread(ta->ph,
231 			        pt + offsetof(struct pthread, tle.tqe_next),
232 			        &pt, sizeof(pt));
233 			if (ret != 0)
234 				return (P2T(ret));
235 		}
236 		/* check lwp */
237 		ret = ptrace(PT_GETREGS, ta->map[id].lwp, (caddr_t)&gregs, 0);
238 		if (ret != 0) {
239 			/* no longer exists */
240 			ta->map[id].type = PT_NONE;
241 			return (TD_NOTHR);
242 		}
243 	} else {
244 		while (pt != 0 && ta->map[id].thr != pt) {
245 			ret = ps_pread(ta->ph,
246 				pt + offsetof(struct pthread, tcb),
247 				&tcb_addr, sizeof(tcb_addr));
248 			if (ret != 0)
249 				return (P2T(ret));
250 			/* get next thread */
251 			ret = ps_pread(ta->ph,
252 				pt + offsetof(struct pthread, tle.tqe_next),
253 				&pt, sizeof(pt));
254 			if (ret != 0)
255 				return (P2T(ret));
256 		}
257 
258 		if (pt == 0) {
259 			/* no longer exists */
260 			ta->map[id].type = PT_NONE;
261 			return (TD_NOTHR);
262 		}
263 	}
264 	th->th_ta = ta;
265 	th->th_tid = id;
266 	return (TD_OK);
267 }
268 
269 static td_err_e
270 pt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
271 {
272 	TAILQ_HEAD(, pthread) thread_list;
273 	psaddr_t pt, ptr;
274 	lwpid_t tmp_lwp;
275 	int ret;
276 
277 	TDBG_FUNC();
278 
279 	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
280 	                sizeof(thread_list));
281 	if (ret != 0)
282 		return (P2T(ret));
283 	pt = (psaddr_t)thread_list.tqh_first;
284 	while (pt != 0) {
285 		ret = ps_pread(ta->ph, pt + offsetof(struct pthread, tcb),
286 				&ptr, sizeof(ptr));
287 		if (ret != 0)
288 			return (P2T(ret));
289 		ptr += offsetof(struct tcb, tcb_tmbx.tm_lwp);
290 		ret = ps_pread(ta->ph, ptr, &tmp_lwp, sizeof(lwpid_t));
291 		if (ret != 0)
292 			return (P2T(ret));
293 		if (tmp_lwp == lwp) {
294 			th->th_ta = ta;
295 			th->th_tid = pt_map_thread(ta, pt, PT_USER);
296 			if (th->th_tid == -1)
297 				return (TD_MALLOC);
298 			pt_unmap_lwp(ta, lwp);
299 			return (TD_OK);
300 		}
301 
302 		/* get next thread */
303 		ret = ps_pread(ta->ph,
304 		           pt + offsetof(struct pthread, tle.tqe_next),
305 		           &pt, sizeof(pt));
306 		if (ret != 0)
307 			return (P2T(ret));
308 	}
309 
310 	return (TD_NOTHR);
311 }
312 
313 static td_err_e
314 pt_ta_thr_iter(const td_thragent_t *ta,
315                td_thr_iter_f *callback, void *cbdata_p,
316                td_thr_state_e state, int ti_pri,
317                sigset_t *ti_sigmask_p,
318                unsigned int ti_user_flags)
319 {
320 	TAILQ_HEAD(, pthread) thread_list;
321 	td_thrhandle_t th;
322 	psaddr_t pt;
323 	ps_err_e pserr;
324 	int activated;
325 
326 	TDBG_FUNC();
327 
328 	pserr = ps_pread(ta->ph, ta->thread_activated_addr, &activated,
329 	    sizeof(int));
330 	if (pserr != PS_OK)
331 		return (P2T(pserr));
332 	if (!activated)
333 		return (TD_OK);
334 
335 	pserr = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
336 	    sizeof(thread_list));
337 	if (pserr != 0)
338 		return (P2T(pserr));
339 	pt = (psaddr_t)thread_list.tqh_first;
340 	while (pt != 0) {
341 		th.th_ta = ta;
342 		th.th_tid = pt_map_thread(ta, pt, PT_USER);
343 		/* should we unmap lwp here ? */
344 		if (th.th_tid == -1)
345 			return (TD_MALLOC);
346 		if ((*callback)(&th, cbdata_p))
347 			return (TD_DBERR);
348 		/* get next thread */
349 		pserr = ps_pread(ta->ph,
350 		    pt + offsetof(struct pthread, tle.tqe_next), &pt,
351 		    sizeof(pt));
352 		if (pserr != PS_OK)
353 			return (P2T(pserr));
354 	}
355 	return (TD_OK);
356 }
357 
358 static td_err_e
359 pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
360 {
361 	struct pthread_key keytable[PTHREAD_KEYS_MAX];
362 	int i, ret;
363 
364 	TDBG_FUNC();
365 
366 	ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
367 	                sizeof(keytable));
368 	if (ret != 0)
369 		return (P2T(ret));
370 
371 	for (i = 0; i < PTHREAD_KEYS_MAX; i++) {
372 		if (keytable[i].allocated) {
373 			ret = (ki)(i, keytable[i].destructor, arg);
374 			if (ret != 0)
375 				return (TD_DBERR);
376 		}
377 	}
378 	return (TD_OK);
379 }
380 
381 static td_err_e
382 pt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
383 {
384 	TDBG_FUNC();
385 	return (TD_NOEVENT);
386 }
387 
388 static td_err_e
389 pt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
390 {
391 	TDBG_FUNC();
392 	return (TD_ERR);
393 }
394 
395 static td_err_e
396 pt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
397 {
398 	TDBG_FUNC();
399 	return (TD_ERR);
400 }
401 
402 static td_err_e
403 pt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
404 {
405 	TDBG_FUNC();
406 	return (TD_NOMSG);
407 }
408 
409 static td_err_e
410 pt_thr_dbresume(const td_thrhandle_t *th)
411 {
412 	TDBG_FUNC();
413 	return (TD_ERR);
414 }
415 
416 static td_err_e
417 pt_thr_dbsuspend(const td_thrhandle_t *th)
418 {
419 	TDBG_FUNC();
420 	return (TD_ERR);
421 }
422 
423 static td_err_e
424 pt_thr_validate(const td_thrhandle_t *th)
425 {
426 	td_thrhandle_t temp;
427 	int ret;
428 
429 	TDBG_FUNC();
430 
431 	ret = pt_ta_map_id2thr(th->th_ta, th->th_tid,
432 	                       &temp);
433 	return (P2T(ret));
434 }
435 
436 static td_err_e
437 pt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
438 {
439 	const td_thragent_t *ta = th->th_ta;
440 	struct pthread pt;
441 	int ret;
442 
443 	TDBG_FUNC();
444 
445 	ret = pt_validate(th);
446 	if (ret)
447 		return (ret);
448 
449 	memset(info, 0, sizeof(*info));
450 	if (ta->map[th->th_tid].type == PT_LWP) {
451 		info->ti_type = TD_THR_SYSTEM;
452 		info->ti_lid = ta->map[th->th_tid].lwp;
453 		info->ti_tid = th->th_tid;
454 		info->ti_state = TD_THR_RUN;
455 		info->ti_type = TD_THR_SYSTEM;
456 		return (TD_OK);
457 	}
458 
459 	ret = ps_pread(ta->ph, (psaddr_t)(ta->map[th->th_tid].thr),
460 	                &pt, sizeof(pt));
461 	if (ret != 0)
462 		return (P2T(ret));
463 	if (pt.magic != THR_MAGIC)
464 		return (TD_BADTH);
465 	ret = ps_pread(ta->ph,
466 	        ((psaddr_t)pt.tcb) + offsetof(struct tcb, tcb_tmbx.tm_lwp),
467 	        &info->ti_lid, sizeof(lwpid_t));
468 	if (ret != 0)
469 		return (P2T(ret));
470 
471 	info->ti_ta_p = th->th_ta;
472 	info->ti_tid = th->th_tid;
473 	info->ti_tls = (char *)pt.specific;
474 	info->ti_startfunc = (psaddr_t)pt.start_routine;
475 	info->ti_stkbase = (psaddr_t) pt.attr.stackaddr_attr;
476 	info->ti_stksize = pt.attr.stacksize_attr;
477 	switch (pt.state) {
478 	case PS_RUNNING:
479 		info->ti_state = TD_THR_RUN;
480 		break;
481 	case PS_LOCKWAIT:
482 	case PS_MUTEX_WAIT:
483 	case PS_COND_WAIT:
484 	case PS_SIGSUSPEND:
485 	case PS_SIGWAIT:
486 	case PS_JOIN:
487 	case PS_SUSPENDED:
488 	case PS_DEADLOCK:
489 	case PS_SLEEP_WAIT:
490 		info->ti_state = TD_THR_SLEEP;
491 		break;
492 	case PS_DEAD:
493 		info->ti_state = TD_THR_ZOMBIE;
494 		break;
495 	default:
496 		info->ti_state = TD_THR_UNKNOWN;
497 		break;
498 	}
499 
500 	info->ti_db_suspended = 0;
501 	info->ti_type = TD_THR_USER;
502 	info->ti_pri = pt.active_priority;
503 	info->ti_sigmask = pt.sigmask;
504 	info->ti_traceme = 0;
505 	info->ti_pending = pt.sigpend;
506 	info->ti_events = 0;
507 	return (0);
508 }
509 
510 static td_err_e
511 pt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
512 {
513 	const td_thragent_t *ta = th->th_ta;
514 	struct kse_thr_mailbox tmbx;
515 	psaddr_t tcb_addr, tmbx_addr, ptr;
516 	lwpid_t lwp;
517 	int ret;
518 
519 	TDBG_FUNC();
520 
521 	ret = pt_validate(th);
522 	if (ret)
523 		return (ret);
524 
525 	if (ta->map[th->th_tid].type == PT_LWP) {
526 		ret = ps_lgetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
527 		return (P2T(ret));
528 	}
529 
530 	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
531  	                offsetof(struct pthread, tcb),
532                         &tcb_addr, sizeof(tcb_addr));
533 	if (ret != 0)
534 		return (P2T(ret));
535 	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
536 	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
537 	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
538 	if (ret != 0)
539 		return (P2T(ret));
540 	if (lwp != 0) {
541 		ret = ps_lgetfpregs(ta->ph, lwp, fpregs);
542 		return (P2T(ret));
543 	}
544 
545 	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
546 	if (ret != 0)
547 		return (P2T(ret));
548 	pt_ucontext_to_fpreg(&tmbx.tm_context, fpregs);
549 	return (0);
550 }
551 
552 static td_err_e
553 pt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
554 {
555 	const td_thragent_t *ta = th->th_ta;
556 	struct kse_thr_mailbox tmbx;
557 	psaddr_t tcb_addr, tmbx_addr, ptr;
558 	lwpid_t lwp;
559 	int ret;
560 
561 	TDBG_FUNC();
562 
563 	ret = pt_validate(th);
564 	if (ret)
565 		return (ret);
566 
567 	if (ta->map[th->th_tid].type == PT_LWP) {
568 		ret = ps_lgetregs(ta->ph,
569 		                  ta->map[th->th_tid].lwp, gregs);
570 		return (P2T(ret));
571 	}
572 
573 	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
574 	                offsetof(struct pthread, tcb),
575 			&tcb_addr, sizeof(tcb_addr));
576 	if (ret != 0)
577 		return (P2T(ret));
578 	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
579 	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
580 	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
581 	if (ret != 0)
582 		return (P2T(ret));
583 	if (lwp != 0) {
584 		ret = ps_lgetregs(ta->ph, lwp, gregs);
585 		return (P2T(ret));
586 	}
587 	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
588 	if (ret != 0)
589 		return (P2T(ret));
590 	pt_ucontext_to_reg(&tmbx.tm_context, gregs);
591 	return (0);
592 }
593 
594 static td_err_e
595 pt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
596 {
597 	const td_thragent_t *ta = th->th_ta;
598 	struct kse_thr_mailbox tmbx;
599 	psaddr_t tcb_addr, tmbx_addr, ptr;
600 	lwpid_t lwp;
601 	int ret;
602 
603 	TDBG_FUNC();
604 
605 	ret = pt_validate(th);
606 	if (ret)
607 		return (ret);
608 
609 	if (ta->map[th->th_tid].type == PT_LWP) {
610 		ret = ps_lsetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
611 		return (P2T(ret));
612 	}
613 
614 	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
615 	                offsetof(struct pthread, tcb),
616                         &tcb_addr, sizeof(tcb_addr));
617 	if (ret != 0)
618 		return (P2T(ret));
619 	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
620 	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
621 	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
622 	if (ret != 0)
623 		return (P2T(ret));
624 	if (lwp != 0) {
625 		ret = ps_lsetfpregs(ta->ph, lwp, fpregs);
626 		return (P2T(ret));
627 	}
628 	/*
629 	 * Read a copy of context, this makes sure that registers
630 	 * not covered by structure reg won't be clobbered
631 	 */
632 	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
633 	if (ret != 0)
634 		return (P2T(ret));
635 
636 	pt_fpreg_to_ucontext(fpregs, &tmbx.tm_context);
637 	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
638 	return (P2T(ret));
639 }
640 
641 static td_err_e
642 pt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
643 {
644 	const td_thragent_t *ta = th->th_ta;
645 	struct kse_thr_mailbox tmbx;
646 	psaddr_t tcb_addr, tmbx_addr, ptr;
647 	lwpid_t lwp;
648 	int ret;
649 
650 	TDBG_FUNC();
651 
652 	ret = pt_validate(th);
653 	if (ret)
654 		return (ret);
655 
656 	if (ta->map[th->th_tid].type == PT_LWP) {
657 		ret = ps_lsetregs(ta->ph, ta->map[th->th_tid].lwp, gregs);
658 		return (P2T(ret));
659 	}
660 
661 	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
662 	                offsetof(struct pthread, tcb),
663 	                &tcb_addr, sizeof(tcb_addr));
664 	if (ret != 0)
665 		return (P2T(ret));
666 	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
667 	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
668 	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
669 	if (ret != 0)
670 		return (P2T(ret));
671 	if (lwp != 0) {
672 		ret = ps_lsetregs(ta->ph, lwp, gregs);
673 		return (P2T(ret));
674 	}
675 
676 	/*
677 	 * Read a copy of context, make sure that registers
678 	 * not covered by structure reg won't be clobbered
679 	 */
680 	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
681 	if (ret != 0)
682 		return (P2T(ret));
683 	pt_reg_to_ucontext(gregs, &tmbx.tm_context);
684 	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
685 	return (P2T(ret));
686 }
687 
688 static td_err_e
689 pt_thr_event_enable(const td_thrhandle_t *th, int en)
690 {
691 	TDBG_FUNC();
692 	return (TD_ERR);
693 }
694 
695 static td_err_e
696 pt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
697 {
698 	TDBG_FUNC();
699 	return (TD_ERR);
700 }
701 
702 static td_err_e
703 pt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
704 {
705 	TDBG_FUNC();
706 	return (TD_ERR);
707 }
708 
709 static td_err_e
710 pt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
711 {
712 	TDBG_FUNC();
713 	return (TD_NOMSG);
714 }
715 
716 static td_err_e
717 pt_thr_sstep(const td_thrhandle_t *th, int step)
718 {
719 	const td_thragent_t *ta = th->th_ta;
720 	struct kse_thr_mailbox tmbx;
721 	struct reg regs;
722 	psaddr_t tcb_addr, tmbx_addr;
723 	uint32_t tmp;
724 	lwpid_t lwp;
725 	int ret;
726 
727 	TDBG_FUNC();
728 
729 	ret = pt_validate(th);
730 	if (ret)
731 		return (ret);
732 
733 	if (ta->map[th->th_tid].type == PT_LWP)
734 		return (TD_BADTH);
735 
736 	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
737 	                offsetof(struct pthread, tcb),
738 	                &tcb_addr, sizeof(tcb_addr));
739 	if (ret != 0)
740 		return (P2T(ret));
741 
742 	/* Clear or set single step flag in thread mailbox */
743 	tmp = step ? TMDF_SSTEP : 0;
744 	ret = ps_pwrite(ta->ph, tcb_addr + offsetof(struct tcb,
745 	                 tcb_tmbx.tm_dflags), &tmp, sizeof(tmp));
746 	if (ret != 0)
747 		return (P2T(ret));
748 	/* Get lwp */
749 	ret = ps_pread(ta->ph, tcb_addr + offsetof(struct tcb,
750 	                tcb_tmbx.tm_lwp), &lwp, sizeof(lwpid_t));
751 	if (ret != 0)
752 		return (P2T(ret));
753 	if (lwp != 0)
754 		return (TD_BADTH);
755 
756 	tmbx_addr = tcb_addr + offsetof(struct tcb, tcb_tmbx);
757 	/*
758 	 * context is in userland, some architectures store
759 	 * single step status in registers, we should change
760 	 * these registers.
761 	 */
762 	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
763 	if (ret == 0) {
764 		pt_ucontext_to_reg(&tmbx.tm_context, &regs);
765 		/* only write out if it is really changed. */
766 		if (pt_reg_sstep(&regs, step) != 0) {
767 			pt_reg_to_ucontext(&regs, &tmbx.tm_context);
768 			ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx,
769 			                 sizeof(tmbx));
770 		}
771 	}
772 	return (P2T(ret));
773 }
774 
775 static void
776 pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp)
777 {
778 	int i;
779 
780 	for (i = 0; i < ta->map_len; ++i) {
781 		if (ta->map[i].type == PT_LWP && ta->map[i].lwp == lwp) {
782 			ta->map[i].type = PT_NONE;
783 			return;
784 		}
785 	}
786 }
787 
788 static int
789 pt_validate(const td_thrhandle_t *th)
790 {
791 
792 	if (th->th_tid < 0 || th->th_tid >= th->th_ta->map_len ||
793 	    th->th_ta->map[th->th_tid].type == PT_NONE)
794 		return (TD_NOTHR);
795 	return (TD_OK);
796 }
797 
798 struct ta_ops libpthread_db_ops = {
799 	.to_init		= pt_init,
800 	.to_ta_clear_event	= pt_ta_clear_event,
801 	.to_ta_delete		= pt_ta_delete,
802 	.to_ta_event_addr	= pt_ta_event_addr,
803 	.to_ta_event_getmsg	= pt_ta_event_getmsg,
804 	.to_ta_map_id2thr	= pt_ta_map_id2thr,
805 	.to_ta_map_lwp2thr	= pt_ta_map_lwp2thr,
806 	.to_ta_new		= pt_ta_new,
807 	.to_ta_set_event	= pt_ta_set_event,
808 	.to_ta_thr_iter		= pt_ta_thr_iter,
809 	.to_ta_tsd_iter		= pt_ta_tsd_iter,
810 	.to_thr_clear_event	= pt_thr_clear_event,
811 	.to_thr_dbresume	= pt_thr_dbresume,
812 	.to_thr_dbsuspend	= pt_thr_dbsuspend,
813 	.to_thr_event_enable	= pt_thr_event_enable,
814 	.to_thr_event_getmsg	= pt_thr_event_getmsg,
815 	.to_thr_get_info	= pt_thr_get_info,
816 	.to_thr_getfpregs	= pt_thr_getfpregs,
817 	.to_thr_getgregs	= pt_thr_getgregs,
818 	.to_thr_set_event	= pt_thr_set_event,
819 	.to_thr_setfpregs	= pt_thr_setfpregs,
820 	.to_thr_setgregs	= pt_thr_setgregs,
821 	.to_thr_validate	= pt_thr_validate,
822 
823 	/* FreeBSD specific extensions. */
824 	.to_thr_sstep		= pt_thr_sstep,
825 };
826