xref: /freebsd/lib/libthread_db/libthr_db.c (revision f5ef5f675d9d9eb6e35ed9142d70ecf774456ad4)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2004 Marcel Moolenaar
5  * Copyright (c) 2005 David Xu
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <proc_service.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <sys/linker_set.h>
39 #include <sys/ptrace.h>
40 #include <thread_db.h>
41 #include <unistd.h>
42 
43 #include "thread_db_int.h"
44 
45 #define	TERMINATED	1
46 
47 struct td_thragent {
48 	TD_THRAGENT_FIELDS;
49 	psaddr_t	libthr_debug_addr;
50 	psaddr_t	thread_list_addr;
51 	psaddr_t	thread_active_threads_addr;
52 	psaddr_t	thread_keytable_addr;
53 	psaddr_t	thread_last_event_addr;
54 	psaddr_t	thread_event_mask_addr;
55 	psaddr_t	thread_bp_create_addr;
56 	psaddr_t	thread_bp_death_addr;
57 	int		thread_off_dtv;
58 	int		thread_off_tlsindex;
59 	int		thread_off_attr_flags;
60 	int		thread_size_key;
61 	int		thread_off_tcb;
62 	int		thread_off_linkmap;
63 	int		thread_off_next;
64 	int		thread_off_state;
65 	int		thread_off_tid;
66 	int		thread_max_keys;
67 	int		thread_off_key_allocated;
68 	int		thread_off_key_destructor;
69 	int		thread_off_report_events;
70 	int		thread_off_event_mask;
71 	int		thread_off_event_buf;
72 	int		thread_state_zoombie;
73 	int		thread_state_running;
74 };
75 
76 #define P2T(c) ps2td(c)
77 
78 static int pt_validate(const td_thrhandle_t *th);
79 
80 static int
81 ps2td(int c)
82 {
83 	switch (c) {
84 	case PS_OK:
85 		return TD_OK;
86 	case PS_ERR:
87 		return TD_ERR;
88 	case PS_BADPID:
89 		return TD_BADPH;
90 	case PS_BADLID:
91 		return TD_NOLWP;
92 	case PS_BADADDR:
93 		return TD_ERR;
94 	case PS_NOSYM:
95 		return TD_NOLIBTHREAD;
96 	case PS_NOFREGS:
97 		return TD_NOFPREGS;
98 	default:
99 		return TD_ERR;
100 	}
101 }
102 
103 static td_err_e
104 pt_init(void)
105 {
106 	return (0);
107 }
108 
109 static td_err_e
110 pt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
111 {
112 #define LOOKUP_SYM(proc, sym, addr) 			\
113 	ret = ps_pglobal_lookup(proc, NULL, sym, addr);	\
114 	if (ret != 0) {					\
115 		TDBG("can not find symbol: %s\n", sym);	\
116 		ret = TD_NOLIBTHREAD;			\
117 		goto error;				\
118 	}
119 
120 #define	LOOKUP_VAL(proc, sym, val)			\
121 	ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
122 	if (ret != 0) {					\
123 		TDBG("can not find symbol: %s\n", sym);	\
124 		ret = TD_NOLIBTHREAD;			\
125 		goto error;				\
126 	}						\
127 	ret = ps_pread(proc, vaddr, val, sizeof(int));	\
128 	if (ret != 0) {					\
129 		TDBG("can not read value of %s\n", sym);\
130 		ret = TD_NOLIBTHREAD;			\
131 		goto error;				\
132 	}
133 
134 	td_thragent_t *ta;
135 	psaddr_t vaddr;
136 	int dbg;
137 	int ret;
138 
139 	TDBG_FUNC();
140 
141 	ta = malloc(sizeof(td_thragent_t));
142 	if (ta == NULL)
143 		return (TD_MALLOC);
144 
145 	ta->ph = ph;
146 
147 	LOOKUP_SYM(ph, "_libthr_debug",		&ta->libthr_debug_addr);
148 	LOOKUP_SYM(ph, "_thread_list",		&ta->thread_list_addr);
149 	LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
150 	LOOKUP_SYM(ph, "_thread_keytable",	&ta->thread_keytable_addr);
151 	LOOKUP_SYM(ph, "_thread_last_event",	&ta->thread_last_event_addr);
152 	LOOKUP_SYM(ph, "_thread_event_mask",	&ta->thread_event_mask_addr);
153 	LOOKUP_SYM(ph, "_thread_bp_create",	&ta->thread_bp_create_addr);
154 	LOOKUP_SYM(ph, "_thread_bp_death",	&ta->thread_bp_death_addr);
155 	LOOKUP_VAL(ph, "_thread_off_dtv",	&ta->thread_off_dtv);
156 	LOOKUP_VAL(ph, "_thread_off_tlsindex",	&ta->thread_off_tlsindex);
157 	LOOKUP_VAL(ph, "_thread_off_attr_flags",	&ta->thread_off_attr_flags);
158 	LOOKUP_VAL(ph, "_thread_size_key",	&ta->thread_size_key);
159 	LOOKUP_VAL(ph, "_thread_off_tcb",	&ta->thread_off_tcb);
160 	LOOKUP_VAL(ph, "_thread_off_tid",	&ta->thread_off_tid);
161 	LOOKUP_VAL(ph, "_thread_off_linkmap",	&ta->thread_off_linkmap);
162 	LOOKUP_VAL(ph, "_thread_off_next",	&ta->thread_off_next);
163 	LOOKUP_VAL(ph, "_thread_off_state",	&ta->thread_off_state);
164 	LOOKUP_VAL(ph, "_thread_max_keys",	&ta->thread_max_keys);
165 	LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
166 	LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
167 	LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
168 	LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
169 	LOOKUP_VAL(ph, "_thread_off_report_events", &ta->thread_off_report_events);
170 	LOOKUP_VAL(ph, "_thread_off_event_mask", &ta->thread_off_event_mask);
171 	LOOKUP_VAL(ph, "_thread_off_event_buf", &ta->thread_off_event_buf);
172 	dbg = getpid();
173 	/*
174 	 * If this fails it probably means we're debugging a core file and
175 	 * can't write to it.
176 	 */
177 	ps_pwrite(ph, ta->libthr_debug_addr, &dbg, sizeof(int));
178 	*pta = ta;
179 	return (0);
180 
181 error:
182 	free(ta);
183 	return (ret);
184 }
185 
186 static td_err_e
187 pt_ta_delete(td_thragent_t *ta)
188 {
189 	int dbg;
190 
191 	TDBG_FUNC();
192 
193 	dbg = 0;
194 	/*
195 	 * Error returns from this write are not really a problem;
196 	 * the process doesn't exist any more.
197 	 */
198 	ps_pwrite(ta->ph, ta->libthr_debug_addr, &dbg, sizeof(int));
199 	free(ta);
200 	return (TD_OK);
201 }
202 
203 static td_err_e
204 pt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
205 {
206 	psaddr_t pt;
207 	int64_t lwp;
208 	int ret;
209 
210 	TDBG_FUNC();
211 
212 	if (id == 0)
213 		return (TD_NOTHR);
214 	ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
215 	if (ret != 0)
216 		return (TD_ERR);
217 	/* Iterate through thread list to find pthread */
218 	while (pt != 0) {
219 		ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
220 		if (ret != 0)
221 			return (TD_ERR);
222 		if (lwp == id)
223 			break;
224 		/* get next thread */
225 		ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
226 		if (ret != 0)
227 			return (TD_ERR);
228 	}
229 	if (pt == 0)
230 		return (TD_NOTHR);
231 	th->th_ta = ta;
232 	th->th_tid = id;
233 	th->th_thread = pt;
234 	return (TD_OK);
235 }
236 
237 static td_err_e
238 pt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
239 {
240 	return (pt_ta_map_id2thr(ta, lwp, th));
241 }
242 
243 static td_err_e
244 pt_ta_thr_iter(const td_thragent_t *ta, td_thr_iter_f *callback,
245     void *cbdata_p, td_thr_state_e state __unused, int ti_pri __unused,
246     sigset_t *ti_sigmask_p __unused, unsigned int ti_user_flags __unused)
247 {
248 	td_thrhandle_t th;
249 	psaddr_t pt;
250 	int64_t lwp;
251 	int ret;
252 
253 	TDBG_FUNC();
254 
255 	ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
256 	if (ret != 0)
257 		return (TD_ERR);
258 	while (pt != 0) {
259 		ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
260 		if (ret != 0)
261 			return (TD_ERR);
262 		if (lwp != 0 && lwp != TERMINATED) {
263 			th.th_ta = ta;
264 			th.th_tid = (thread_t)lwp;
265 			th.th_thread = pt;
266 			if ((*callback)(&th, cbdata_p))
267 				return (TD_DBERR);
268 		}
269 		/* get next thread */
270 		ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
271 		if (ret != 0)
272 			return (TD_ERR);
273 	}
274 	return (TD_OK);
275 }
276 
277 static td_err_e
278 pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
279 {
280 	void *keytable;
281 	void *destructor;
282 	int i, ret, allocated;
283 
284 	TDBG_FUNC();
285 
286 	keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
287 	if (keytable == NULL)
288 		return (TD_MALLOC);
289 	ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
290 	               ta->thread_max_keys * ta->thread_size_key);
291 	if (ret != 0) {
292 		free(keytable);
293 		return (P2T(ret));
294 	}
295 	for (i = 0; i < ta->thread_max_keys; i++) {
296 		allocated = *(int *)(void *)((uintptr_t)keytable +
297 		    i * ta->thread_size_key + ta->thread_off_key_allocated);
298 		destructor = *(void **)(void *)((uintptr_t)keytable +
299 		    i * ta->thread_size_key + ta->thread_off_key_destructor);
300 		if (allocated) {
301 			ret = (ki)(i, destructor, arg);
302 			if (ret != 0) {
303 				free(keytable);
304 				return (TD_DBERR);
305 			}
306 		}
307 	}
308 	free(keytable);
309 	return (TD_OK);
310 }
311 
312 static td_err_e
313 pt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
314 {
315 
316 	TDBG_FUNC();
317 
318 	switch (event) {
319 	case TD_CREATE:
320 		ptr->type = NOTIFY_BPT;
321 		ptr->u.bptaddr = ta->thread_bp_create_addr;
322 		return (0);
323 	case TD_DEATH:
324 		ptr->type = NOTIFY_BPT;
325 		ptr->u.bptaddr = ta->thread_bp_death_addr;
326 		return (0);
327 	default:
328 		return (TD_ERR);
329 	}
330 }
331 
332 static td_err_e
333 pt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
334 {
335 	td_thr_events_t mask;
336 	int ret;
337 
338 	TDBG_FUNC();
339 	ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
340 		sizeof(mask));
341 	if (ret != 0)
342 		return (P2T(ret));
343 	mask |= *events;
344 	ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
345 		sizeof(mask));
346 	return (P2T(ret));
347 }
348 
349 static td_err_e
350 pt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
351 {
352 	td_thr_events_t mask;
353 	int ret;
354 
355 	TDBG_FUNC();
356 	ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
357 		sizeof(mask));
358 	if (ret != 0)
359 		return (P2T(ret));
360 	mask &= ~*events;
361 	ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
362 		sizeof(mask));
363 	return (P2T(ret));
364 }
365 
366 static td_err_e
367 pt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
368 {
369 	static td_thrhandle_t handle;
370 
371 	psaddr_t pt;
372 	td_thr_events_e	tmp;
373 	int64_t lwp;
374 	int ret;
375 
376 	TDBG_FUNC();
377 
378 	ret = thr_pread_ptr(ta, ta->thread_last_event_addr, &pt);
379 	if (ret != 0)
380 		return (TD_ERR);
381 	if (pt == 0)
382 		return (TD_NOMSG);
383 	/*
384 	 * Take the event pointer, at the time, libthr only reports event
385 	 * once a time, so it is not a link list.
386 	 */
387 	thr_pwrite_ptr(ta, ta->thread_last_event_addr, 0);
388 
389 	/* Read event info */
390 	ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
391 	if (ret != 0)
392 		return (P2T(ret));
393 	if (msg->event == 0)
394 		return (TD_NOMSG);
395 	/* Clear event */
396 	tmp = 0;
397 	ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
398 	/* Convert event */
399 	pt = msg->th_p;
400 	ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
401 	if (ret != 0)
402 		return (TD_ERR);
403 	handle.th_ta = ta;
404 	handle.th_tid = lwp;
405 	handle.th_thread = pt;
406 	msg->th_p = (uintptr_t)&handle;
407 	return (0);
408 }
409 
410 static td_err_e
411 pt_dbsuspend(const td_thrhandle_t *th, int suspend)
412 {
413 	const td_thragent_t *ta = th->th_ta;
414 	int ret;
415 
416 	TDBG_FUNC();
417 
418 	ret = pt_validate(th);
419 	if (ret)
420 		return (ret);
421 
422 	if (suspend)
423 		ret = ps_lstop(ta->ph, th->th_tid);
424 	else
425 		ret = ps_lcontinue(ta->ph, th->th_tid);
426 	return (P2T(ret));
427 }
428 
429 static td_err_e
430 pt_thr_dbresume(const td_thrhandle_t *th)
431 {
432 	TDBG_FUNC();
433 
434 	return pt_dbsuspend(th, 0);
435 }
436 
437 static td_err_e
438 pt_thr_dbsuspend(const td_thrhandle_t *th)
439 {
440 	TDBG_FUNC();
441 
442 	return pt_dbsuspend(th, 1);
443 }
444 
445 static td_err_e
446 pt_thr_validate(const td_thrhandle_t *th)
447 {
448 	td_thrhandle_t temp;
449 	int ret;
450 
451 	TDBG_FUNC();
452 
453 	ret = pt_ta_map_id2thr(th->th_ta, th->th_tid, &temp);
454 	return (ret);
455 }
456 
457 static td_err_e
458 pt_thr_get_info_common(const td_thrhandle_t *th, td_thrinfo_t *info, int old)
459 {
460 	const td_thragent_t *ta = th->th_ta;
461 	struct ptrace_lwpinfo linfo;
462 	int traceme;
463 	int state;
464 	int ret;
465 
466 	TDBG_FUNC();
467 
468 	bzero(info, sizeof(*info));
469 	ret = pt_validate(th);
470 	if (ret)
471 		return (ret);
472 	ret = thr_pread_int(ta, th->th_thread + ta->thread_off_state, &state);
473 	if (ret != 0)
474 		return (TD_ERR);
475 	ret = thr_pread_int(ta, th->th_thread + ta->thread_off_report_events,
476 	    &traceme);
477 	info->ti_traceme = traceme;
478 	if (ret != 0)
479 		return (TD_ERR);
480 	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
481 		&info->ti_events, sizeof(td_thr_events_t));
482 	if (ret != 0)
483 		return (P2T(ret));
484 	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
485 		&info->ti_tls, sizeof(void *));
486 	info->ti_lid = th->th_tid;
487 	info->ti_tid = th->th_tid;
488 	info->ti_thread = th->th_thread;
489 	info->ti_ta_p = th->th_ta;
490 	ret = ps_linfo(ta->ph, th->th_tid, &linfo);
491 	if (ret == PS_OK) {
492 		info->ti_sigmask = linfo.pl_sigmask;
493 		info->ti_pending = linfo.pl_siglist;
494 		if (!old) {
495 			if ((linfo.pl_flags & PL_FLAG_SI) != 0)
496 				info->ti_siginfo = linfo.pl_siginfo;
497 			else
498 				bzero(&info->ti_siginfo,
499 				    sizeof(info->ti_siginfo));
500 		}
501 	} else
502 		return (ret);
503 	if (state == ta->thread_state_running)
504 		info->ti_state = TD_THR_RUN;
505 	else if (state == ta->thread_state_zoombie)
506 		info->ti_state = TD_THR_ZOMBIE;
507 	else
508 		info->ti_state = TD_THR_SLEEP;
509 	info->ti_type = TD_THR_USER;
510 	return (0);
511 }
512 
513 static td_err_e
514 pt_thr_old_get_info(const td_thrhandle_t *th, td_old_thrinfo_t *info)
515 {
516 
517 	return (pt_thr_get_info_common(th, (td_thrinfo_t *)info, 1));
518 }
519 
520 static td_err_e
521 pt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
522 {
523 
524 	return (pt_thr_get_info_common(th, info, 0));
525 }
526 
527 #ifdef __i386__
528 static td_err_e
529 pt_thr_getxmmregs(const td_thrhandle_t *th, char *fxsave)
530 {
531 	const td_thragent_t *ta = th->th_ta;
532 	int ret;
533 
534 	TDBG_FUNC();
535 
536 	ret = pt_validate(th);
537 	if (ret)
538 		return (ret);
539 
540 	ret = ps_lgetxmmregs(ta->ph, th->th_tid, fxsave);
541 	return (P2T(ret));
542 }
543 #endif
544 
545 static td_err_e
546 pt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
547 {
548 	const td_thragent_t *ta = th->th_ta;
549 	int ret;
550 
551 	TDBG_FUNC();
552 
553 	ret = pt_validate(th);
554 	if (ret)
555 		return (ret);
556 
557 	ret = ps_lgetfpregs(ta->ph, th->th_tid, fpregs);
558 	return (P2T(ret));
559 }
560 
561 static td_err_e
562 pt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
563 {
564 	const td_thragent_t *ta = th->th_ta;
565 	int ret;
566 
567 	TDBG_FUNC();
568 
569 	ret = pt_validate(th);
570 	if (ret)
571 		return (ret);
572 
573 	ret = ps_lgetregs(ta->ph, th->th_tid, gregs);
574 	return (P2T(ret));
575 }
576 
577 #ifdef __i386__
578 static td_err_e
579 pt_thr_setxmmregs(const td_thrhandle_t *th, const char *fxsave)
580 {
581 	const td_thragent_t *ta = th->th_ta;
582 	int ret;
583 
584 	TDBG_FUNC();
585 
586 	ret = pt_validate(th);
587 	if (ret)
588 		return (ret);
589 
590 	ret = ps_lsetxmmregs(ta->ph, th->th_tid, fxsave);
591 	return (P2T(ret));
592 }
593 #endif
594 
595 static td_err_e
596 pt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
597 {
598 	const td_thragent_t *ta = th->th_ta;
599 	int ret;
600 
601 	TDBG_FUNC();
602 
603 	ret = pt_validate(th);
604 	if (ret)
605 		return (ret);
606 
607 	ret = ps_lsetfpregs(ta->ph, th->th_tid, fpregs);
608 	return (P2T(ret));
609 }
610 
611 static td_err_e
612 pt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
613 {
614 	const td_thragent_t *ta = th->th_ta;
615 	int ret;
616 
617 	TDBG_FUNC();
618 
619 	ret = pt_validate(th);
620 	if (ret)
621 		return (ret);
622 
623 	ret = ps_lsetregs(ta->ph, th->th_tid, gregs);
624 	return (P2T(ret));
625 }
626 
627 static td_err_e
628 pt_thr_event_enable(const td_thrhandle_t *th, int en)
629 {
630 	const td_thragent_t *ta = th->th_ta;
631 	int ret;
632 
633 	TDBG_FUNC();
634 	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_report_events,
635 		&en, sizeof(int));
636 	return (P2T(ret));
637 }
638 
639 static td_err_e
640 pt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
641 {
642 	const td_thragent_t *ta = th->th_ta;
643 	td_thr_events_t mask;
644 	int ret;
645 
646 	TDBG_FUNC();
647 	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
648 			&mask, sizeof(mask));
649 	mask |= *setp;
650 	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
651 			&mask, sizeof(mask));
652 	return (P2T(ret));
653 }
654 
655 static td_err_e
656 pt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
657 {
658 	const td_thragent_t *ta = th->th_ta;
659 	td_thr_events_t mask;
660 	int ret;
661 
662 	TDBG_FUNC();
663 	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
664 			&mask, sizeof(mask));
665 	mask &= ~*setp;
666 	ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
667 			&mask, sizeof(mask));
668 	return (P2T(ret));
669 }
670 
671 static td_err_e
672 pt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
673 {
674 	static td_thrhandle_t handle;
675 	const td_thragent_t *ta = th->th_ta;
676 	psaddr_t pt, pt_temp;
677 	int64_t lwp;
678 	int ret;
679 	td_thr_events_e	tmp;
680 
681 	TDBG_FUNC();
682 	pt = th->th_thread;
683 	ret = thr_pread_ptr(ta, ta->thread_last_event_addr, &pt_temp);
684 	if (ret != 0)
685 		return (TD_ERR);
686 	/* Get event */
687 	ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
688 	if (ret != 0)
689 		return (P2T(ret));
690 	if (msg->event == 0)
691 		return (TD_NOMSG);
692 	/*
693 	 * Take the event pointer, at the time, libthr only reports event
694 	 * once a time, so it is not a link list.
695 	 */
696 	if (pt == pt_temp)
697 		thr_pwrite_ptr(ta, ta->thread_last_event_addr, 0);
698 
699 	/* Clear event */
700 	tmp = 0;
701 	ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
702 	/* Convert event */
703 	pt = msg->th_p;
704 	ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
705 	if (ret != 0)
706 		return (TD_ERR);
707 	handle.th_ta = ta;
708 	handle.th_tid = lwp;
709 	handle.th_thread = pt;
710 	msg->th_p = (uintptr_t)&handle;
711 	return (0);
712 }
713 
714 static td_err_e
715 pt_thr_sstep(const td_thrhandle_t *th, int step __unused)
716 {
717 	TDBG_FUNC();
718 
719 	return pt_validate(th);
720 }
721 
722 static int
723 pt_validate(const td_thrhandle_t *th)
724 {
725 
726 	if (th->th_tid == 0 || th->th_thread == 0)
727 		return (TD_ERR);
728 	return (TD_OK);
729 }
730 
731 static td_err_e
732 pt_thr_tls_get_addr(const td_thrhandle_t *th, psaddr_t _linkmap, size_t offset,
733     psaddr_t *address)
734 {
735 	const td_thragent_t *ta = th->th_ta;
736 	psaddr_t dtv_addr, obj_entry, tcb_addr;
737 	int tls_index, ret;
738 
739 	/* linkmap is a member of Obj_Entry */
740 	obj_entry = _linkmap - ta->thread_off_linkmap;
741 
742 	/* get tlsindex of the object file */
743 	ret = ps_pread(ta->ph,
744 		obj_entry + ta->thread_off_tlsindex,
745 		&tls_index, sizeof(tls_index));
746 	if (ret != 0)
747 		return (P2T(ret));
748 
749 	/* get thread tcb */
750 	ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
751 		&tcb_addr, sizeof(tcb_addr));
752 	if (ret != 0)
753 		return (P2T(ret));
754 
755 	/* get dtv array address */
756 	ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
757 		&dtv_addr, sizeof(dtv_addr));
758 	if (ret != 0)
759 		return (P2T(ret));
760 	/* now get the object's tls block base address */
761 	ret = ps_pread(ta->ph, dtv_addr + sizeof(void *) * (tls_index+1),
762 	    address, sizeof(*address));
763 	if (ret != 0)
764 		return (P2T(ret));
765 
766 	*address += offset;
767 	return (TD_OK);
768 }
769 
770 static struct ta_ops libthr_db_ops = {
771 	.to_init		= pt_init,
772 	.to_ta_clear_event	= pt_ta_clear_event,
773 	.to_ta_delete		= pt_ta_delete,
774 	.to_ta_event_addr	= pt_ta_event_addr,
775 	.to_ta_event_getmsg	= pt_ta_event_getmsg,
776 	.to_ta_map_id2thr	= pt_ta_map_id2thr,
777 	.to_ta_map_lwp2thr	= pt_ta_map_lwp2thr,
778 	.to_ta_new		= pt_ta_new,
779 	.to_ta_set_event	= pt_ta_set_event,
780 	.to_ta_thr_iter		= pt_ta_thr_iter,
781 	.to_ta_tsd_iter		= pt_ta_tsd_iter,
782 	.to_thr_clear_event	= pt_thr_clear_event,
783 	.to_thr_dbresume	= pt_thr_dbresume,
784 	.to_thr_dbsuspend	= pt_thr_dbsuspend,
785 	.to_thr_event_enable	= pt_thr_event_enable,
786 	.to_thr_event_getmsg	= pt_thr_event_getmsg,
787 	.to_thr_old_get_info	= pt_thr_old_get_info,
788 	.to_thr_get_info	= pt_thr_get_info,
789 	.to_thr_getfpregs	= pt_thr_getfpregs,
790 	.to_thr_getgregs	= pt_thr_getgregs,
791 	.to_thr_set_event	= pt_thr_set_event,
792 	.to_thr_setfpregs	= pt_thr_setfpregs,
793 	.to_thr_setgregs	= pt_thr_setgregs,
794 	.to_thr_validate	= pt_thr_validate,
795 	.to_thr_tls_get_addr	= pt_thr_tls_get_addr,
796 
797 	/* FreeBSD specific extensions. */
798 	.to_thr_sstep		= pt_thr_sstep,
799 #ifdef __i386__
800 	.to_thr_getxmmregs	= pt_thr_getxmmregs,
801 	.to_thr_setxmmregs	= pt_thr_setxmmregs,
802 #endif
803 };
804 
805 DATA_SET(__ta_ops, libthr_db_ops);
806