libpthread_db.c revision 133805
1132332Smarcel/*
2132332Smarcel * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
3132332Smarcel * All rights reserved.
4132332Smarcel *
5132332Smarcel * Redistribution and use in source and binary forms, with or without
6132332Smarcel * modification, are permitted provided that the following conditions
7132332Smarcel * are met:
8132332Smarcel * 1. Redistributions of source code must retain the above copyright
9132332Smarcel *    notice, this list of conditions and the following disclaimer.
10132332Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11132332Smarcel *    notice, this list of conditions and the following disclaimer in the
12132332Smarcel *    documentation and/or other materials provided with the distribution.
13132332Smarcel *
14132332Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15132332Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16132332Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17132332Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18132332Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19132332Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20132332Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21132332Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22132332Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23132332Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24132332Smarcel * SUCH DAMAGE.
25132332Smarcel */
26132332Smarcel
27132332Smarcel#include <sys/cdefs.h>
28132332Smarcel__FBSDID("$FreeBSD: head/lib/libthread_db/libpthread_db.c 133805 2004-08-16 05:20:12Z davidxu $");
29132332Smarcel
30132332Smarcel#include <stddef.h>
31132332Smarcel#include <stdlib.h>
32132332Smarcel#include <string.h>
33132332Smarcel#include <unistd.h>
34132332Smarcel#include <pthread.h>
35132332Smarcel#include <sys/types.h>
36132332Smarcel#include <sys/kse.h>
37132332Smarcel#include <sys/ptrace.h>
38132332Smarcel#include <proc_service.h>
39132332Smarcel#include <thread_db.h>
40132332Smarcel
41132332Smarcel#include "libpthread_db.h"
42132332Smarcel
43132332Smarcel#define P2T(c) ps2td(c)
44132332Smarcel
45132332Smarcelstatic void pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp);
46132332Smarcelstatic int pt_validate(const td_thrhandle_t *th);
47132332Smarcel
48132332Smarcelstatic int
49132332Smarcelps2td(int c)
50132332Smarcel{
51132332Smarcel	switch (c) {
52132332Smarcel	case PS_OK:
53132332Smarcel		return TD_OK;
54132332Smarcel	case PS_ERR:
55132332Smarcel		return TD_ERR;
56132332Smarcel	case PS_BADPID:
57132332Smarcel		return TD_BADPH;
58132332Smarcel	case PS_BADLID:
59132332Smarcel		return TD_NOLWP;
60132332Smarcel	case PS_BADADDR:
61132332Smarcel		return TD_ERR;
62132332Smarcel	case PS_NOSYM:
63132332Smarcel		return TD_NOLIBTHREAD;
64132332Smarcel	case PS_NOFREGS:
65132332Smarcel		return TD_NOFPREGS;
66132332Smarcel	default:
67132332Smarcel		return TD_ERR;
68132332Smarcel	}
69132332Smarcel}
70132332Smarcel
71132332Smarcelstatic long
72132332Smarcelpt_map_thread(const td_thragent_t *const_ta, psaddr_t pt, int type)
73132332Smarcel{
74132332Smarcel	td_thragent_t *ta = __DECONST(td_thragent_t *, const_ta);
75132332Smarcel	struct pt_map *new;
76132332Smarcel	int i, first = -1;
77132332Smarcel
78132332Smarcel	/* leave zero out */
79132332Smarcel	for (i = 1; i < ta->map_len; ++i) {
80132332Smarcel		if (ta->map[i].type == PT_NONE) {
81132332Smarcel			if (first == -1)
82132332Smarcel				first = i;
83132332Smarcel		} else if (ta->map[i].type == type && ta->map[i].thr == pt) {
84132332Smarcel				return (i);
85132332Smarcel		}
86132332Smarcel	}
87132332Smarcel
88132332Smarcel	if (first == -1) {
89132332Smarcel		if (ta->map_len == 0) {
90132332Smarcel			ta->map = calloc(20, sizeof(struct pt_map));
91132332Smarcel			if (ta->map == NULL)
92132332Smarcel				return (-1);
93132332Smarcel			ta->map_len = 20;
94132332Smarcel			first = 1;
95132332Smarcel		} else {
96132332Smarcel			new = realloc(ta->map,
97132332Smarcel			              sizeof(struct pt_map) * ta->map_len * 2);
98132332Smarcel			if (new == NULL)
99132332Smarcel				return (-1);
100132332Smarcel			memset(new + ta->map_len, '\0', sizeof(struct pt_map) *
101132332Smarcel			       ta->map_len);
102132332Smarcel			first = ta->map_len;
103132332Smarcel			ta->map = new;
104132332Smarcel			ta->map_len *= 2;
105132332Smarcel		}
106132332Smarcel	}
107132332Smarcel
108132332Smarcel	ta->map[first].type = type;
109132332Smarcel	ta->map[first].thr = pt;
110132332Smarcel	return (first);
111132332Smarcel}
112132332Smarcel
113132332Smarcelstatic td_err_e
114132332Smarcelpt_init(void)
115132332Smarcel{
116132332Smarcel	pt_md_init();
117132332Smarcel	return (0);
118132332Smarcel}
119132332Smarcel
120132332Smarcelstatic td_err_e
121132332Smarcelpt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
122132332Smarcel{
123132332Smarcel#define LOOKUP_SYM(proc, sym, addr) 			\
124132332Smarcel	ret = ps_pglobal_lookup(proc, NULL, sym, addr);	\
125132332Smarcel	if (ret != 0) {					\
126132332Smarcel		TDBG("can not find symbol: %s\n", sym);	\
127132332Smarcel		ret = TD_NOLIBTHREAD;			\
128132332Smarcel		goto error;				\
129132332Smarcel	}
130132332Smarcel
131133802Sdavidxu#define	LOOKUP_VAL(proc, sym, val)			\
132133802Sdavidxu	ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
133133802Sdavidxu	if (ret != 0) {					\
134133802Sdavidxu		TDBG("can not find symbol: %s\n", sym);	\
135133802Sdavidxu		ret = TD_NOLIBTHREAD;			\
136133802Sdavidxu		goto error;				\
137133802Sdavidxu	}						\
138133802Sdavidxu	ret = ps_pread(proc, vaddr, val, sizeof(int));	\
139133802Sdavidxu	if (ret != 0) {					\
140133802Sdavidxu		TDBG("can not read value of %s\n", sym);\
141133802Sdavidxu		ret = TD_NOLIBTHREAD;			\
142133802Sdavidxu		goto error;				\
143133802Sdavidxu	}
144133802Sdavidxu
145132332Smarcel	td_thragent_t *ta;
146133802Sdavidxu	psaddr_t vaddr;
147132332Smarcel	int dbg;
148132332Smarcel	int ret;
149132332Smarcel
150132332Smarcel	TDBG_FUNC();
151132332Smarcel
152132332Smarcel	ta = malloc(sizeof(td_thragent_t));
153132332Smarcel	if (ta == NULL)
154132332Smarcel		return (TD_MALLOC);
155132332Smarcel
156132332Smarcel	ta->ph = ph;
157132332Smarcel	ta->thread_activated = 0;
158132332Smarcel	ta->map = NULL;
159132332Smarcel	ta->map_len = 0;
160132332Smarcel
161132332Smarcel	LOOKUP_SYM(ph, "_libkse_debug",		&ta->libkse_debug_addr);
162132332Smarcel	LOOKUP_SYM(ph, "_thread_list",		&ta->thread_list_addr);
163132332Smarcel	LOOKUP_SYM(ph, "_thread_activated",	&ta->thread_activated_addr);
164132332Smarcel	LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
165132332Smarcel	LOOKUP_SYM(ph, "_thread_keytable",	&ta->thread_keytable_addr);
166133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_dtv",	&ta->thread_off_dtv);
167133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_kse_locklevel", &ta->thread_off_kse_locklevel);
168133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_kse",	&ta->thread_off_kse);
169133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tlsindex",	&ta->thread_off_tlsindex);
170133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_attr_flags",	&ta->thread_off_attr_flags);
171133802Sdavidxu	LOOKUP_VAL(ph, "_thread_size_key",	&ta->thread_size_key);
172133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tcb",	&ta->thread_off_tcb);
173133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_linkmap",	&ta->thread_off_linkmap);
174133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tmbx",	&ta->thread_off_tmbx);
175133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_thr_locklevel",	&ta->thread_off_thr_locklevel);
176133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_next",	&ta->thread_off_next);
177133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_state",	&ta->thread_off_state);
178133802Sdavidxu	LOOKUP_VAL(ph, "_thread_max_keys",	&ta->thread_max_keys);
179133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
180133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
181133802Sdavidxu	LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
182133802Sdavidxu	LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
183132332Smarcel	dbg = getpid();
184132332Smarcel	/*
185132332Smarcel	 * If this fails it probably means we're debugging a core file and
186132332Smarcel	 * can't write to it.
187132332Smarcel	 */
188132332Smarcel	ps_pwrite(ph, ta->libkse_debug_addr, &dbg, sizeof(int));
189132332Smarcel	*pta = ta;
190132332Smarcel	return (0);
191132332Smarcel
192132332Smarcelerror:
193132332Smarcel	free(ta);
194132332Smarcel	return (ret);
195132332Smarcel}
196132332Smarcel
197132332Smarcelstatic td_err_e
198132332Smarcelpt_ta_delete(td_thragent_t *ta)
199132332Smarcel{
200132332Smarcel	int dbg;
201132332Smarcel
202132332Smarcel	TDBG_FUNC();
203132332Smarcel
204132332Smarcel	dbg = 0;
205132332Smarcel	/*
206132332Smarcel	 * Error returns from this write are not really a problem;
207132332Smarcel	 * the process doesn't exist any more.
208132332Smarcel	 */
209132332Smarcel	ps_pwrite(ta->ph, ta->libkse_debug_addr, &dbg, sizeof(int));
210132332Smarcel	if (ta->map)
211132332Smarcel		free(ta->map);
212132332Smarcel	free(ta);
213132332Smarcel	return (TD_OK);
214132332Smarcel}
215132332Smarcel
216132332Smarcelstatic td_err_e
217132332Smarcelpt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
218132332Smarcel{
219132332Smarcel	prgregset_t gregs;
220132332Smarcel	TAILQ_HEAD(, pthread) thread_list;
221132332Smarcel	psaddr_t pt, tcb_addr;
222132332Smarcel	lwpid_t lwp;
223132332Smarcel	int ret;
224132332Smarcel
225132332Smarcel	TDBG_FUNC();
226132332Smarcel
227132332Smarcel	if (id < 0 || id >= ta->map_len || ta->map[id].type == PT_NONE)
228132332Smarcel		return (TD_NOTHR);
229132332Smarcel	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
230132332Smarcel			sizeof(thread_list));
231132332Smarcel	if (ret != 0)
232132332Smarcel		return (P2T(ret));
233132332Smarcel	pt = (psaddr_t)thread_list.tqh_first;
234132332Smarcel	if (ta->map[id].type == PT_LWP) {
235132332Smarcel		/*
236132332Smarcel		 * if we are referencing a lwp, make sure it was not already
237132332Smarcel		 * mapped to user thread.
238132332Smarcel		 */
239132332Smarcel		while (pt != 0) {
240132332Smarcel			ret = ps_pread(ta->ph,
241133802Sdavidxu			        pt + ta->thread_off_tcb,
242132332Smarcel			        &tcb_addr, sizeof(tcb_addr));
243132332Smarcel			if (ret != 0)
244132332Smarcel				return (P2T(ret));
245132332Smarcel			ret = ps_pread(ta->ph,
246133802Sdavidxu			        tcb_addr + ta->thread_off_tmbx +
247133802Sdavidxu				offsetof(struct kse_thr_mailbox, tm_lwp),
248132332Smarcel				&lwp, sizeof(lwp));
249132332Smarcel			if (ret != 0)
250132332Smarcel				return (P2T(ret));
251132332Smarcel			/*
252132332Smarcel			 * If the lwp was already mapped to userland thread,
253132332Smarcel			 * we shouldn't reference it directly in future.
254132332Smarcel			 */
255132332Smarcel			if (lwp == ta->map[id].lwp) {
256132332Smarcel				ta->map[id].type = PT_NONE;
257132332Smarcel				return (TD_NOTHR);
258132332Smarcel			}
259132332Smarcel			/* get next thread */
260132332Smarcel			ret = ps_pread(ta->ph,
261133802Sdavidxu			        pt + ta->thread_off_next,
262132332Smarcel			        &pt, sizeof(pt));
263132332Smarcel			if (ret != 0)
264132332Smarcel				return (P2T(ret));
265132332Smarcel		}
266132332Smarcel		/* check lwp */
267132332Smarcel		ret = ptrace(PT_GETREGS, ta->map[id].lwp, (caddr_t)&gregs, 0);
268132332Smarcel		if (ret != 0) {
269132332Smarcel			/* no longer exists */
270132332Smarcel			ta->map[id].type = PT_NONE;
271132332Smarcel			return (TD_NOTHR);
272132332Smarcel		}
273132332Smarcel	} else {
274132332Smarcel		while (pt != 0 && ta->map[id].thr != pt) {
275132332Smarcel			ret = ps_pread(ta->ph,
276133802Sdavidxu				pt + ta->thread_off_tcb,
277132332Smarcel				&tcb_addr, sizeof(tcb_addr));
278132332Smarcel			if (ret != 0)
279132332Smarcel				return (P2T(ret));
280132332Smarcel			/* get next thread */
281132332Smarcel			ret = ps_pread(ta->ph,
282133802Sdavidxu				pt + ta->thread_off_next,
283132332Smarcel				&pt, sizeof(pt));
284132332Smarcel			if (ret != 0)
285132332Smarcel				return (P2T(ret));
286132332Smarcel		}
287132332Smarcel
288132332Smarcel		if (pt == 0) {
289132332Smarcel			/* no longer exists */
290132332Smarcel			ta->map[id].type = PT_NONE;
291132332Smarcel			return (TD_NOTHR);
292132332Smarcel		}
293132332Smarcel	}
294132332Smarcel	th->th_ta = ta;
295132332Smarcel	th->th_tid = id;
296132332Smarcel	return (TD_OK);
297132332Smarcel}
298132332Smarcel
299132332Smarcelstatic td_err_e
300132332Smarcelpt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
301132332Smarcel{
302132332Smarcel	TAILQ_HEAD(, pthread) thread_list;
303132332Smarcel	psaddr_t pt, ptr;
304132332Smarcel	lwpid_t tmp_lwp;
305132332Smarcel	int ret;
306132332Smarcel
307132332Smarcel	TDBG_FUNC();
308132332Smarcel
309132332Smarcel	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
310132332Smarcel	                sizeof(thread_list));
311132332Smarcel	if (ret != 0)
312132332Smarcel		return (P2T(ret));
313132332Smarcel	pt = (psaddr_t)thread_list.tqh_first;
314132332Smarcel	while (pt != 0) {
315133802Sdavidxu		ret = ps_pread(ta->ph, pt + ta->thread_off_tcb,
316132332Smarcel				&ptr, sizeof(ptr));
317132332Smarcel		if (ret != 0)
318132332Smarcel			return (P2T(ret));
319133802Sdavidxu		ptr += ta->thread_off_tmbx +
320133802Sdavidxu		       offsetof(struct kse_thr_mailbox, tm_lwp);
321132332Smarcel		ret = ps_pread(ta->ph, ptr, &tmp_lwp, sizeof(lwpid_t));
322132332Smarcel		if (ret != 0)
323132332Smarcel			return (P2T(ret));
324132332Smarcel		if (tmp_lwp == lwp) {
325132332Smarcel			th->th_ta = ta;
326132332Smarcel			th->th_tid = pt_map_thread(ta, pt, PT_USER);
327132332Smarcel			if (th->th_tid == -1)
328132332Smarcel				return (TD_MALLOC);
329132332Smarcel			pt_unmap_lwp(ta, lwp);
330132332Smarcel			return (TD_OK);
331132332Smarcel		}
332132332Smarcel
333132332Smarcel		/* get next thread */
334132332Smarcel		ret = ps_pread(ta->ph,
335133802Sdavidxu		           pt + ta->thread_off_next,
336132332Smarcel		           &pt, sizeof(pt));
337132332Smarcel		if (ret != 0)
338132332Smarcel			return (P2T(ret));
339132332Smarcel	}
340132332Smarcel
341132332Smarcel	return (TD_NOTHR);
342132332Smarcel}
343132332Smarcel
344132332Smarcelstatic td_err_e
345132332Smarcelpt_ta_thr_iter(const td_thragent_t *ta,
346132332Smarcel               td_thr_iter_f *callback, void *cbdata_p,
347132332Smarcel               td_thr_state_e state, int ti_pri,
348132332Smarcel               sigset_t *ti_sigmask_p,
349132332Smarcel               unsigned int ti_user_flags)
350132332Smarcel{
351132332Smarcel	TAILQ_HEAD(, pthread) thread_list;
352132332Smarcel	td_thrhandle_t th;
353132332Smarcel	psaddr_t pt;
354132332Smarcel	ps_err_e pserr;
355132332Smarcel	int activated;
356132332Smarcel
357132332Smarcel	TDBG_FUNC();
358132332Smarcel
359132332Smarcel	pserr = ps_pread(ta->ph, ta->thread_activated_addr, &activated,
360132332Smarcel	    sizeof(int));
361132332Smarcel	if (pserr != PS_OK)
362132332Smarcel		return (P2T(pserr));
363132332Smarcel	if (!activated)
364132332Smarcel		return (TD_OK);
365132332Smarcel
366132332Smarcel	pserr = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
367132332Smarcel	    sizeof(thread_list));
368132332Smarcel	if (pserr != 0)
369132332Smarcel		return (P2T(pserr));
370132332Smarcel	pt = (psaddr_t)thread_list.tqh_first;
371132332Smarcel	while (pt != 0) {
372132332Smarcel		th.th_ta = ta;
373132332Smarcel		th.th_tid = pt_map_thread(ta, pt, PT_USER);
374132332Smarcel		/* should we unmap lwp here ? */
375132332Smarcel		if (th.th_tid == -1)
376132332Smarcel			return (TD_MALLOC);
377132332Smarcel		if ((*callback)(&th, cbdata_p))
378132332Smarcel			return (TD_DBERR);
379132332Smarcel		/* get next thread */
380132332Smarcel		pserr = ps_pread(ta->ph,
381133802Sdavidxu		    pt + ta->thread_off_next, &pt,
382132332Smarcel		    sizeof(pt));
383132332Smarcel		if (pserr != PS_OK)
384132332Smarcel			return (P2T(pserr));
385132332Smarcel	}
386132332Smarcel	return (TD_OK);
387132332Smarcel}
388132332Smarcel
389132332Smarcelstatic td_err_e
390132332Smarcelpt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
391132332Smarcel{
392133802Sdavidxu	char *keytable;
393133802Sdavidxu	void *destructor;
394133802Sdavidxu	int i, ret, allocated;
395132332Smarcel
396132332Smarcel	TDBG_FUNC();
397132332Smarcel
398133802Sdavidxu	keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
399133802Sdavidxu	if (keytable == NULL)
400133802Sdavidxu		return (TD_MALLOC);
401132332Smarcel	ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
402133802Sdavidxu	               ta->thread_max_keys * ta->thread_size_key);
403133805Sdavidxu	if (ret != 0) {
404133805Sdavidxu		free(keytable);
405132332Smarcel		return (P2T(ret));
406133805Sdavidxu	}
407133802Sdavidxu	for (i = 0; i < ta->thread_max_keys; i++) {
408133802Sdavidxu		allocated = *(int *)(keytable + i * ta->thread_size_key +
409133802Sdavidxu			ta->thread_off_key_allocated);
410133802Sdavidxu		destructor = *(void **)(keytable + i * ta->thread_size_key +
411133802Sdavidxu			ta->thread_off_key_destructor);
412133802Sdavidxu		if (allocated) {
413133802Sdavidxu			ret = (ki)(i, destructor, arg);
414133802Sdavidxu			if (ret != 0) {
415133802Sdavidxu				free(keytable);
416132332Smarcel				return (TD_DBERR);
417133802Sdavidxu			}
418132332Smarcel		}
419132332Smarcel	}
420133802Sdavidxu	free(keytable);
421132332Smarcel	return (TD_OK);
422132332Smarcel}
423132332Smarcel
424132332Smarcelstatic td_err_e
425132332Smarcelpt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
426132332Smarcel{
427132332Smarcel	TDBG_FUNC();
428132332Smarcel	return (TD_NOEVENT);
429132332Smarcel}
430132332Smarcel
431132332Smarcelstatic td_err_e
432132332Smarcelpt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
433132332Smarcel{
434132332Smarcel	TDBG_FUNC();
435132332Smarcel	return (TD_ERR);
436132332Smarcel}
437132332Smarcel
438132332Smarcelstatic td_err_e
439132332Smarcelpt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
440132332Smarcel{
441132332Smarcel	TDBG_FUNC();
442132332Smarcel	return (TD_ERR);
443132332Smarcel}
444132332Smarcel
445132332Smarcelstatic td_err_e
446132332Smarcelpt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
447132332Smarcel{
448132332Smarcel	TDBG_FUNC();
449132332Smarcel	return (TD_NOMSG);
450132332Smarcel}
451132332Smarcel
452132332Smarcelstatic td_err_e
453132951Sdavidxupt_dbsuspend(const td_thrhandle_t *th, int suspend)
454132951Sdavidxu{
455132951Sdavidxu	td_thragent_t *ta = (td_thragent_t *)th->th_ta;
456132951Sdavidxu	psaddr_t tcb_addr, tmbx_addr, ptr;
457132951Sdavidxu	lwpid_t lwp;
458132951Sdavidxu	uint32_t dflags;
459133342Sdavidxu	int attrflags, locklevel, ret;
460132951Sdavidxu
461132951Sdavidxu	TDBG_FUNC();
462132951Sdavidxu
463132951Sdavidxu	ret = pt_validate(th);
464132951Sdavidxu	if (ret)
465132951Sdavidxu		return (ret);
466132951Sdavidxu
467132951Sdavidxu	if (ta->map[th->th_tid].type == PT_LWP) {
468132951Sdavidxu		if (suspend)
469132951Sdavidxu			ret = ps_lstop(ta->ph, ta->map[th->th_tid].lwp);
470132951Sdavidxu		else
471132951Sdavidxu			ret = ps_lcontinue(ta->ph, ta->map[th->th_tid].lwp);
472132951Sdavidxu		return (P2T(ret));
473132951Sdavidxu	}
474132951Sdavidxu
475132951Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
476133802Sdavidxu		ta->thread_off_attr_flags,
477132951Sdavidxu		&attrflags, sizeof(attrflags));
478132951Sdavidxu	if (ret != 0)
479132951Sdavidxu		return (P2T(ret));
480132951Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
481133802Sdavidxu	               ta->thread_off_tcb,
482133802Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
483132951Sdavidxu	if (ret != 0)
484132951Sdavidxu		return (P2T(ret));
485133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
486132951Sdavidxu	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
487132951Sdavidxu	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
488132951Sdavidxu	if (ret != 0)
489132951Sdavidxu		return (P2T(ret));
490133342Sdavidxu
491133342Sdavidxu	if (lwp != 0) {
492133342Sdavidxu		/* don't suspend signal thread */
493133802Sdavidxu		if (attrflags & 0x200)
494133342Sdavidxu			return (0);
495133342Sdavidxu		if (attrflags & PTHREAD_SCOPE_SYSTEM) {
496133342Sdavidxu			/*
497133342Sdavidxu			 * don't suspend system scope thread if it is holding
498133342Sdavidxu			 * some low level locks
499133342Sdavidxu			 */
500133802Sdavidxu			ptr = ta->map[th->th_tid].thr + ta->thread_off_kse;
501133342Sdavidxu			ret = ps_pread(ta->ph, ptr, &ptr, sizeof(ptr));
502133342Sdavidxu			if (ret != 0)
503133342Sdavidxu				return (P2T(ret));
504133802Sdavidxu			ret = ps_pread(ta->ph, ptr + ta->thread_off_kse_locklevel,
505133802Sdavidxu				&locklevel, sizeof(int));
506133342Sdavidxu			if (ret != 0)
507133342Sdavidxu				return (P2T(ret));
508133342Sdavidxu			if (locklevel <= 0) {
509133342Sdavidxu				ptr = ta->map[th->th_tid].thr +
510133802Sdavidxu					ta->thread_off_thr_locklevel;
511133342Sdavidxu				ret = ps_pread(ta->ph, ptr, &locklevel,
512133342Sdavidxu					sizeof(int));
513133342Sdavidxu				if (ret != 0)
514133342Sdavidxu					return (P2T(ret));
515133342Sdavidxu			}
516133342Sdavidxu			if (suspend) {
517133342Sdavidxu				if (locklevel <= 0)
518133342Sdavidxu					ret = ps_lstop(ta->ph, lwp);
519133342Sdavidxu			} else {
520132951Sdavidxu				ret = ps_lcontinue(ta->ph, lwp);
521133342Sdavidxu			}
522132951Sdavidxu			if (ret != 0)
523132951Sdavidxu				return (P2T(ret));
524133342Sdavidxu			/* FALLTHROUGH */
525133342Sdavidxu		} else {
526133342Sdavidxu			struct ptrace_lwpinfo pl;
527133342Sdavidxu
528133342Sdavidxu			if (ptrace(PT_LWPINFO, lwp, (caddr_t) &pl, sizeof(pl)))
529133342Sdavidxu				return (TD_ERR);
530133342Sdavidxu			if (suspend) {
531133342Sdavidxu				if (!(pl.pl_flags & PL_FLAG_BOUND))
532133342Sdavidxu					ret = ps_lstop(ta->ph, lwp);
533133342Sdavidxu			} else {
534133342Sdavidxu				ret = ps_lcontinue(ta->ph, lwp);
535133342Sdavidxu			}
536133342Sdavidxu			if (ret != 0)
537133342Sdavidxu				return (P2T(ret));
538133342Sdavidxu			/* FALLTHROUGH */
539132951Sdavidxu		}
540132951Sdavidxu	}
541132951Sdavidxu	/* read tm_dflags */
542132951Sdavidxu	ret = ps_pread(ta->ph,
543132951Sdavidxu		tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
544132951Sdavidxu		&dflags, sizeof(dflags));
545132951Sdavidxu	if (ret != 0)
546132951Sdavidxu		return (P2T(ret));
547132951Sdavidxu	if (suspend)
548133047Sdavidxu		dflags |= TMDF_SUSPEND;
549132951Sdavidxu	else
550133047Sdavidxu		dflags &= ~TMDF_SUSPEND;
551132951Sdavidxu	ret = ps_pwrite(ta->ph,
552132951Sdavidxu	       tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
553132951Sdavidxu	       &dflags, sizeof(dflags));
554132951Sdavidxu	return (P2T(ret));
555132951Sdavidxu}
556132951Sdavidxu
557132951Sdavidxustatic td_err_e
558132332Smarcelpt_thr_dbresume(const td_thrhandle_t *th)
559132332Smarcel{
560132332Smarcel	TDBG_FUNC();
561132951Sdavidxu
562132951Sdavidxu	return pt_dbsuspend(th, 0);
563132332Smarcel}
564132332Smarcel
565132332Smarcelstatic td_err_e
566132332Smarcelpt_thr_dbsuspend(const td_thrhandle_t *th)
567132332Smarcel{
568132332Smarcel	TDBG_FUNC();
569132951Sdavidxu
570132951Sdavidxu	return pt_dbsuspend(th, 1);
571132332Smarcel}
572132332Smarcel
573132332Smarcelstatic td_err_e
574132332Smarcelpt_thr_validate(const td_thrhandle_t *th)
575132332Smarcel{
576132332Smarcel	td_thrhandle_t temp;
577132332Smarcel	int ret;
578132332Smarcel
579132332Smarcel	TDBG_FUNC();
580132332Smarcel
581132332Smarcel	ret = pt_ta_map_id2thr(th->th_ta, th->th_tid,
582132332Smarcel	                       &temp);
583132951Sdavidxu	return (ret);
584132332Smarcel}
585132332Smarcel
586132332Smarcelstatic td_err_e
587132332Smarcelpt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
588132332Smarcel{
589132332Smarcel	const td_thragent_t *ta = th->th_ta;
590133802Sdavidxu	psaddr_t tcb_addr;
591133802Sdavidxu	uint32_t dflags;
592133802Sdavidxu	int state;
593132332Smarcel	int ret;
594132332Smarcel
595132332Smarcel	TDBG_FUNC();
596132332Smarcel
597132332Smarcel	ret = pt_validate(th);
598132332Smarcel	if (ret)
599132332Smarcel		return (ret);
600132332Smarcel
601132332Smarcel	memset(info, 0, sizeof(*info));
602132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
603132332Smarcel		info->ti_type = TD_THR_SYSTEM;
604132332Smarcel		info->ti_lid = ta->map[th->th_tid].lwp;
605132332Smarcel		info->ti_tid = th->th_tid;
606132332Smarcel		info->ti_state = TD_THR_RUN;
607132332Smarcel		info->ti_type = TD_THR_SYSTEM;
608132332Smarcel		return (TD_OK);
609132332Smarcel	}
610133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
611133802Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
612132332Smarcel	if (ret != 0)
613132332Smarcel		return (P2T(ret));
614133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_state,
615133802Sdavidxu	               &state, sizeof(state));
616132332Smarcel	ret = ps_pread(ta->ph,
617133802Sdavidxu	        tcb_addr + ta->thread_off_tmbx +
618133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_lwp),
619132332Smarcel	        &info->ti_lid, sizeof(lwpid_t));
620132332Smarcel	if (ret != 0)
621132332Smarcel		return (P2T(ret));
622132951Sdavidxu	ret = ps_pread(ta->ph,
623133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
624133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_dflags),
625132951Sdavidxu		&dflags, sizeof(dflags));
626132951Sdavidxu	if (ret != 0)
627132951Sdavidxu		return (P2T(ret));
628132332Smarcel	info->ti_ta_p = th->th_ta;
629132332Smarcel	info->ti_tid = th->th_tid;
630133802Sdavidxu	if (state == ta->thread_state_running)
631132332Smarcel		info->ti_state = TD_THR_RUN;
632133802Sdavidxu	else if (state == ta->thread_state_zoombie)
633133802Sdavidxu		info->ti_state = TD_THR_ZOMBIE;
634133802Sdavidxu	else
635132332Smarcel		info->ti_state = TD_THR_SLEEP;
636133047Sdavidxu	info->ti_db_suspended = ((dflags & TMDF_SUSPEND) != 0);
637132332Smarcel	info->ti_type = TD_THR_USER;
638132332Smarcel	return (0);
639132332Smarcel}
640132332Smarcel
641132332Smarcelstatic td_err_e
642132332Smarcelpt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
643132332Smarcel{
644132332Smarcel	const td_thragent_t *ta = th->th_ta;
645132332Smarcel	struct kse_thr_mailbox tmbx;
646132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
647132332Smarcel	lwpid_t lwp;
648132332Smarcel	int ret;
649132332Smarcel
650132332Smarcel	TDBG_FUNC();
651132332Smarcel
652132332Smarcel	ret = pt_validate(th);
653132332Smarcel	if (ret)
654132332Smarcel		return (ret);
655132332Smarcel
656132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
657132332Smarcel		ret = ps_lgetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
658132332Smarcel		return (P2T(ret));
659132332Smarcel	}
660132332Smarcel
661133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
662132951Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
663132332Smarcel	if (ret != 0)
664132332Smarcel		return (P2T(ret));
665133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
666132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
667132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
668132332Smarcel	if (ret != 0)
669132332Smarcel		return (P2T(ret));
670132332Smarcel	if (lwp != 0) {
671132332Smarcel		ret = ps_lgetfpregs(ta->ph, lwp, fpregs);
672132332Smarcel		return (P2T(ret));
673132332Smarcel	}
674132332Smarcel
675132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
676132332Smarcel	if (ret != 0)
677132332Smarcel		return (P2T(ret));
678132332Smarcel	pt_ucontext_to_fpreg(&tmbx.tm_context, fpregs);
679132332Smarcel	return (0);
680132332Smarcel}
681132332Smarcel
682132332Smarcelstatic td_err_e
683132332Smarcelpt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
684132332Smarcel{
685132332Smarcel	const td_thragent_t *ta = th->th_ta;
686132332Smarcel	struct kse_thr_mailbox tmbx;
687132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
688132332Smarcel	lwpid_t lwp;
689132332Smarcel	int ret;
690132332Smarcel
691132332Smarcel	TDBG_FUNC();
692132332Smarcel
693132332Smarcel	ret = pt_validate(th);
694132332Smarcel	if (ret)
695132332Smarcel		return (ret);
696132332Smarcel
697132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
698132332Smarcel		ret = ps_lgetregs(ta->ph,
699132332Smarcel		                  ta->map[th->th_tid].lwp, gregs);
700132332Smarcel		return (P2T(ret));
701132332Smarcel	}
702132332Smarcel
703133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
704132332Smarcel			&tcb_addr, sizeof(tcb_addr));
705132332Smarcel	if (ret != 0)
706132332Smarcel		return (P2T(ret));
707133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
708132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
709132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
710132332Smarcel	if (ret != 0)
711132332Smarcel		return (P2T(ret));
712132332Smarcel	if (lwp != 0) {
713132332Smarcel		ret = ps_lgetregs(ta->ph, lwp, gregs);
714132332Smarcel		return (P2T(ret));
715132332Smarcel	}
716132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
717132332Smarcel	if (ret != 0)
718132332Smarcel		return (P2T(ret));
719132332Smarcel	pt_ucontext_to_reg(&tmbx.tm_context, gregs);
720132332Smarcel	return (0);
721132332Smarcel}
722132332Smarcel
723132332Smarcelstatic td_err_e
724132332Smarcelpt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
725132332Smarcel{
726132332Smarcel	const td_thragent_t *ta = th->th_ta;
727132332Smarcel	struct kse_thr_mailbox tmbx;
728132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
729132332Smarcel	lwpid_t lwp;
730132332Smarcel	int ret;
731132332Smarcel
732132332Smarcel	TDBG_FUNC();
733132332Smarcel
734132332Smarcel	ret = pt_validate(th);
735132332Smarcel	if (ret)
736132332Smarcel		return (ret);
737132332Smarcel
738132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
739132332Smarcel		ret = ps_lsetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
740132332Smarcel		return (P2T(ret));
741132332Smarcel	}
742132332Smarcel
743132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
744133802Sdavidxu	                ta->thread_off_tcb,
745132332Smarcel                        &tcb_addr, sizeof(tcb_addr));
746132332Smarcel	if (ret != 0)
747132332Smarcel		return (P2T(ret));
748133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
749132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
750132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
751132332Smarcel	if (ret != 0)
752132332Smarcel		return (P2T(ret));
753132332Smarcel	if (lwp != 0) {
754132332Smarcel		ret = ps_lsetfpregs(ta->ph, lwp, fpregs);
755132332Smarcel		return (P2T(ret));
756132332Smarcel	}
757132332Smarcel	/*
758132332Smarcel	 * Read a copy of context, this makes sure that registers
759132332Smarcel	 * not covered by structure reg won't be clobbered
760132332Smarcel	 */
761132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
762132332Smarcel	if (ret != 0)
763132332Smarcel		return (P2T(ret));
764132332Smarcel
765132332Smarcel	pt_fpreg_to_ucontext(fpregs, &tmbx.tm_context);
766132332Smarcel	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
767132332Smarcel	return (P2T(ret));
768132332Smarcel}
769132332Smarcel
770132332Smarcelstatic td_err_e
771132332Smarcelpt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
772132332Smarcel{
773132332Smarcel	const td_thragent_t *ta = th->th_ta;
774132332Smarcel	struct kse_thr_mailbox tmbx;
775132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
776132332Smarcel	lwpid_t lwp;
777132332Smarcel	int ret;
778132332Smarcel
779132332Smarcel	TDBG_FUNC();
780132332Smarcel
781132332Smarcel	ret = pt_validate(th);
782132332Smarcel	if (ret)
783132332Smarcel		return (ret);
784132332Smarcel
785132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
786132332Smarcel		ret = ps_lsetregs(ta->ph, ta->map[th->th_tid].lwp, gregs);
787132332Smarcel		return (P2T(ret));
788132332Smarcel	}
789132332Smarcel
790132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
791133802Sdavidxu	                ta->thread_off_tcb,
792132332Smarcel	                &tcb_addr, sizeof(tcb_addr));
793132332Smarcel	if (ret != 0)
794132332Smarcel		return (P2T(ret));
795133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
796132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
797132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
798132332Smarcel	if (ret != 0)
799132332Smarcel		return (P2T(ret));
800132332Smarcel	if (lwp != 0) {
801132332Smarcel		ret = ps_lsetregs(ta->ph, lwp, gregs);
802132332Smarcel		return (P2T(ret));
803132332Smarcel	}
804132332Smarcel
805132332Smarcel	/*
806132332Smarcel	 * Read a copy of context, make sure that registers
807132332Smarcel	 * not covered by structure reg won't be clobbered
808132332Smarcel	 */
809132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
810132332Smarcel	if (ret != 0)
811132332Smarcel		return (P2T(ret));
812132332Smarcel	pt_reg_to_ucontext(gregs, &tmbx.tm_context);
813132332Smarcel	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
814132332Smarcel	return (P2T(ret));
815132332Smarcel}
816132332Smarcel
817132332Smarcelstatic td_err_e
818132332Smarcelpt_thr_event_enable(const td_thrhandle_t *th, int en)
819132332Smarcel{
820132332Smarcel	TDBG_FUNC();
821132332Smarcel	return (TD_ERR);
822132332Smarcel}
823132332Smarcel
824132332Smarcelstatic td_err_e
825132332Smarcelpt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
826132332Smarcel{
827132332Smarcel	TDBG_FUNC();
828132332Smarcel	return (TD_ERR);
829132332Smarcel}
830132332Smarcel
831132332Smarcelstatic td_err_e
832132332Smarcelpt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
833132332Smarcel{
834132332Smarcel	TDBG_FUNC();
835132332Smarcel	return (TD_ERR);
836132332Smarcel}
837132332Smarcel
838132332Smarcelstatic td_err_e
839132332Smarcelpt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
840132332Smarcel{
841132332Smarcel	TDBG_FUNC();
842132332Smarcel	return (TD_NOMSG);
843132332Smarcel}
844132332Smarcel
845132332Smarcelstatic td_err_e
846132332Smarcelpt_thr_sstep(const td_thrhandle_t *th, int step)
847132332Smarcel{
848132332Smarcel	const td_thragent_t *ta = th->th_ta;
849132332Smarcel	struct kse_thr_mailbox tmbx;
850132332Smarcel	struct reg regs;
851132332Smarcel	psaddr_t tcb_addr, tmbx_addr;
852132951Sdavidxu	uint32_t dflags;
853132332Smarcel	lwpid_t lwp;
854132332Smarcel	int ret;
855132332Smarcel
856132332Smarcel	TDBG_FUNC();
857132332Smarcel
858132332Smarcel	ret = pt_validate(th);
859132332Smarcel	if (ret)
860132332Smarcel		return (ret);
861132332Smarcel
862132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP)
863132332Smarcel		return (TD_BADTH);
864132332Smarcel
865132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
866133802Sdavidxu	                ta->thread_off_tcb,
867132332Smarcel	                &tcb_addr, sizeof(tcb_addr));
868132332Smarcel	if (ret != 0)
869132332Smarcel		return (P2T(ret));
870132332Smarcel
871132332Smarcel	/* Clear or set single step flag in thread mailbox */
872133802Sdavidxu	ret = ps_pread(ta->ph,
873133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
874133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_dflags),
875133802Sdavidxu		&dflags, sizeof(uint32_t));
876132951Sdavidxu	if (ret != 0)
877132951Sdavidxu		return (P2T(ret));
878132951Sdavidxu	if (step != 0)
879132951Sdavidxu		dflags |= TMDF_SSTEP;
880132951Sdavidxu	else
881132951Sdavidxu		dflags &= ~TMDF_SSTEP;
882133802Sdavidxu	ret = ps_pwrite(ta->ph,
883133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
884133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_dflags),
885133802Sdavidxu	        &dflags, sizeof(uint32_t));
886132332Smarcel	if (ret != 0)
887132332Smarcel		return (P2T(ret));
888132332Smarcel	/* Get lwp */
889133802Sdavidxu	ret = ps_pread(ta->ph,
890133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
891133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_lwp),
892133802Sdavidxu		&lwp, sizeof(lwpid_t));
893132332Smarcel	if (ret != 0)
894132332Smarcel		return (P2T(ret));
895132332Smarcel	if (lwp != 0)
896132951Sdavidxu		return (0);
897132332Smarcel
898133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
899132332Smarcel	/*
900132332Smarcel	 * context is in userland, some architectures store
901132332Smarcel	 * single step status in registers, we should change
902132332Smarcel	 * these registers.
903132332Smarcel	 */
904132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
905132332Smarcel	if (ret == 0) {
906132332Smarcel		pt_ucontext_to_reg(&tmbx.tm_context, &regs);
907132332Smarcel		/* only write out if it is really changed. */
908132332Smarcel		if (pt_reg_sstep(&regs, step) != 0) {
909132332Smarcel			pt_reg_to_ucontext(&regs, &tmbx.tm_context);
910132332Smarcel			ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx,
911132332Smarcel			                 sizeof(tmbx));
912132332Smarcel		}
913132332Smarcel	}
914132332Smarcel	return (P2T(ret));
915132332Smarcel}
916132332Smarcel
917132332Smarcelstatic void
918132332Smarcelpt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp)
919132332Smarcel{
920132332Smarcel	int i;
921132332Smarcel
922132332Smarcel	for (i = 0; i < ta->map_len; ++i) {
923132332Smarcel		if (ta->map[i].type == PT_LWP && ta->map[i].lwp == lwp) {
924132332Smarcel			ta->map[i].type = PT_NONE;
925132332Smarcel			return;
926132332Smarcel		}
927132332Smarcel	}
928132332Smarcel}
929132332Smarcel
930132332Smarcelstatic int
931132332Smarcelpt_validate(const td_thrhandle_t *th)
932132332Smarcel{
933132332Smarcel
934132332Smarcel	if (th->th_tid < 0 || th->th_tid >= th->th_ta->map_len ||
935132332Smarcel	    th->th_ta->map[th->th_tid].type == PT_NONE)
936132332Smarcel		return (TD_NOTHR);
937132332Smarcel	return (TD_OK);
938132332Smarcel}
939132332Smarcel
940133342Sdavidxutd_err_e
941133342Sdavidxupt_thr_tls_get_addr(const td_thrhandle_t *th, void *_linkmap, size_t offset,
942133342Sdavidxu		    void **address)
943133342Sdavidxu{
944133802Sdavidxu	char *obj_entry;
945133342Sdavidxu	const td_thragent_t *ta = th->th_ta;
946133342Sdavidxu	psaddr_t tcb_addr, *dtv_addr, tcb_tp;
947133342Sdavidxu	int tls_index, ret;
948133342Sdavidxu
949133342Sdavidxu	/* linkmap is a member of Obj_Entry */
950133802Sdavidxu	obj_entry = (char *)_linkmap - ta->thread_off_linkmap;
951133342Sdavidxu
952133342Sdavidxu	/* get tlsindex of the object file */
953133342Sdavidxu	ret = ps_pread(ta->ph,
954133802Sdavidxu		obj_entry + ta->thread_off_tlsindex,
955133342Sdavidxu		&tls_index, sizeof(tls_index));
956133342Sdavidxu	if (ret != 0)
957133342Sdavidxu		return (P2T(ret));
958133342Sdavidxu
959133342Sdavidxu	/* get thread tcb */
960133342Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
961133802Sdavidxu		ta->thread_off_tcb,
962133342Sdavidxu		&tcb_addr, sizeof(tcb_addr));
963133342Sdavidxu	if (ret != 0)
964133342Sdavidxu		return (P2T(ret));
965133342Sdavidxu
966133342Sdavidxu	/* get dtv array address */
967133802Sdavidxu	ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
968133342Sdavidxu		&dtv_addr, sizeof(dtv_addr));
969133342Sdavidxu	if (ret != 0)
970133342Sdavidxu		return (P2T(ret));
971133342Sdavidxu	/* now get the object's tls block base address */
972133342Sdavidxu	ret = ps_pread(ta->ph, &dtv_addr[tls_index+1], address,
973133342Sdavidxu		sizeof(*address));
974133342Sdavidxu	if (ret != 0)
975133342Sdavidxu		return (P2T(ret));
976133342Sdavidxu
977133342Sdavidxu	*address += offset;
978133342Sdavidxu	return (TD_OK);
979133342Sdavidxu}
980133342Sdavidxu
981132332Smarcelstruct ta_ops libpthread_db_ops = {
982132332Smarcel	.to_init		= pt_init,
983132332Smarcel	.to_ta_clear_event	= pt_ta_clear_event,
984132332Smarcel	.to_ta_delete		= pt_ta_delete,
985132332Smarcel	.to_ta_event_addr	= pt_ta_event_addr,
986132332Smarcel	.to_ta_event_getmsg	= pt_ta_event_getmsg,
987132332Smarcel	.to_ta_map_id2thr	= pt_ta_map_id2thr,
988132332Smarcel	.to_ta_map_lwp2thr	= pt_ta_map_lwp2thr,
989132332Smarcel	.to_ta_new		= pt_ta_new,
990132332Smarcel	.to_ta_set_event	= pt_ta_set_event,
991132332Smarcel	.to_ta_thr_iter		= pt_ta_thr_iter,
992132332Smarcel	.to_ta_tsd_iter		= pt_ta_tsd_iter,
993132332Smarcel	.to_thr_clear_event	= pt_thr_clear_event,
994132332Smarcel	.to_thr_dbresume	= pt_thr_dbresume,
995132332Smarcel	.to_thr_dbsuspend	= pt_thr_dbsuspend,
996132332Smarcel	.to_thr_event_enable	= pt_thr_event_enable,
997132332Smarcel	.to_thr_event_getmsg	= pt_thr_event_getmsg,
998132332Smarcel	.to_thr_get_info	= pt_thr_get_info,
999132332Smarcel	.to_thr_getfpregs	= pt_thr_getfpregs,
1000132332Smarcel	.to_thr_getgregs	= pt_thr_getgregs,
1001132332Smarcel	.to_thr_set_event	= pt_thr_set_event,
1002132332Smarcel	.to_thr_setfpregs	= pt_thr_setfpregs,
1003132332Smarcel	.to_thr_setgregs	= pt_thr_setgregs,
1004132332Smarcel	.to_thr_validate	= pt_thr_validate,
1005133342Sdavidxu	.to_thr_tls_get_addr	= pt_thr_tls_get_addr,
1006132332Smarcel
1007132332Smarcel	/* FreeBSD specific extensions. */
1008132332Smarcel	.to_thr_sstep		= pt_thr_sstep,
1009132332Smarcel};
1010