libthr_db.c revision 330897
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: stable/11/lib/libthread_db/libthr_db.c 330897 2018-03-14 03:19:51Z eadler $");
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
47struct 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
78static int pt_validate(const td_thrhandle_t *th);
79
80static int
81ps2td(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
103static td_err_e
104pt_init(void)
105{
106	return (0);
107}
108
109static td_err_e
110pt_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
181error:
182	free(ta);
183	return (ret);
184}
185
186static td_err_e
187pt_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
203static td_err_e
204pt_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
237static td_err_e
238pt_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
243static td_err_e
244pt_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
277static td_err_e
278pt_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
312static td_err_e
313pt_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
332static td_err_e
333pt_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
349static td_err_e
350pt_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
366static td_err_e
367pt_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
410static td_err_e
411pt_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
429static td_err_e
430pt_thr_dbresume(const td_thrhandle_t *th)
431{
432	TDBG_FUNC();
433
434	return pt_dbsuspend(th, 0);
435}
436
437static td_err_e
438pt_thr_dbsuspend(const td_thrhandle_t *th)
439{
440	TDBG_FUNC();
441
442	return pt_dbsuspend(th, 1);
443}
444
445static td_err_e
446pt_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
457static td_err_e
458pt_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
513static td_err_e
514pt_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
520static td_err_e
521pt_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__
528static td_err_e
529pt_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
545static td_err_e
546pt_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
561static td_err_e
562pt_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__
578static td_err_e
579pt_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
595static td_err_e
596pt_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
611static td_err_e
612pt_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
627static td_err_e
628pt_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
639static td_err_e
640pt_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
655static td_err_e
656pt_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
671static td_err_e
672pt_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
714static td_err_e
715pt_thr_sstep(const td_thrhandle_t *th, int step __unused)
716{
717	TDBG_FUNC();
718
719	return pt_validate(th);
720}
721
722static int
723pt_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
731static td_err_e
732pt_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
770static 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
805DATA_SET(__ta_ops, libthr_db_ops);
806