libpthread_db.c revision 181059
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 181059 2008-07-31 16:26:58Z marcel $");
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>
36177490Sdavidxu#include <sys/linker_set.h>
37132332Smarcel#include <sys/ptrace.h>
38132332Smarcel#include <proc_service.h>
39132332Smarcel#include <thread_db.h>
40132332Smarcel
41132332Smarcel#include "libpthread_db.h"
42177526Sjeff#include "kse.h"
43132332Smarcel
44132332Smarcel#define P2T(c) ps2td(c)
45132332Smarcel
46132332Smarcelstatic void pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp);
47132332Smarcelstatic int pt_validate(const td_thrhandle_t *th);
48132332Smarcel
49132332Smarcelstatic int
50132332Smarcelps2td(int c)
51132332Smarcel{
52132332Smarcel	switch (c) {
53132332Smarcel	case PS_OK:
54132332Smarcel		return TD_OK;
55132332Smarcel	case PS_ERR:
56132332Smarcel		return TD_ERR;
57132332Smarcel	case PS_BADPID:
58132332Smarcel		return TD_BADPH;
59132332Smarcel	case PS_BADLID:
60132332Smarcel		return TD_NOLWP;
61132332Smarcel	case PS_BADADDR:
62132332Smarcel		return TD_ERR;
63132332Smarcel	case PS_NOSYM:
64132332Smarcel		return TD_NOLIBTHREAD;
65132332Smarcel	case PS_NOFREGS:
66132332Smarcel		return TD_NOFPREGS;
67132332Smarcel	default:
68132332Smarcel		return TD_ERR;
69132332Smarcel	}
70132332Smarcel}
71132332Smarcel
72132332Smarcelstatic long
73181059Smarcelpt_map_thread(const td_thragent_t *const_ta, psaddr_t pt, enum pt_type type)
74132332Smarcel{
75132332Smarcel	td_thragent_t *ta = __DECONST(td_thragent_t *, const_ta);
76132332Smarcel	struct pt_map *new;
77132332Smarcel	int i, first = -1;
78132332Smarcel
79132332Smarcel	/* leave zero out */
80132332Smarcel	for (i = 1; i < ta->map_len; ++i) {
81132332Smarcel		if (ta->map[i].type == PT_NONE) {
82132332Smarcel			if (first == -1)
83132332Smarcel				first = i;
84132332Smarcel		} else if (ta->map[i].type == type && ta->map[i].thr == pt) {
85132332Smarcel				return (i);
86132332Smarcel		}
87132332Smarcel	}
88132332Smarcel
89132332Smarcel	if (first == -1) {
90132332Smarcel		if (ta->map_len == 0) {
91132332Smarcel			ta->map = calloc(20, sizeof(struct pt_map));
92132332Smarcel			if (ta->map == NULL)
93132332Smarcel				return (-1);
94132332Smarcel			ta->map_len = 20;
95132332Smarcel			first = 1;
96132332Smarcel		} else {
97132332Smarcel			new = realloc(ta->map,
98132332Smarcel			              sizeof(struct pt_map) * ta->map_len * 2);
99132332Smarcel			if (new == NULL)
100132332Smarcel				return (-1);
101132332Smarcel			memset(new + ta->map_len, '\0', sizeof(struct pt_map) *
102132332Smarcel			       ta->map_len);
103132332Smarcel			first = ta->map_len;
104132332Smarcel			ta->map = new;
105132332Smarcel			ta->map_len *= 2;
106132332Smarcel		}
107132332Smarcel	}
108132332Smarcel
109132332Smarcel	ta->map[first].type = type;
110132332Smarcel	ta->map[first].thr = pt;
111132332Smarcel	return (first);
112132332Smarcel}
113132332Smarcel
114132332Smarcelstatic td_err_e
115132332Smarcelpt_init(void)
116132332Smarcel{
117132332Smarcel	pt_md_init();
118132332Smarcel	return (0);
119132332Smarcel}
120132332Smarcel
121132332Smarcelstatic td_err_e
122132332Smarcelpt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
123132332Smarcel{
124132332Smarcel#define LOOKUP_SYM(proc, sym, addr) 			\
125132332Smarcel	ret = ps_pglobal_lookup(proc, NULL, sym, addr);	\
126132332Smarcel	if (ret != 0) {					\
127132332Smarcel		TDBG("can not find symbol: %s\n", sym);	\
128132332Smarcel		ret = TD_NOLIBTHREAD;			\
129132332Smarcel		goto error;				\
130132332Smarcel	}
131132332Smarcel
132133802Sdavidxu#define	LOOKUP_VAL(proc, sym, val)			\
133133802Sdavidxu	ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
134133802Sdavidxu	if (ret != 0) {					\
135133802Sdavidxu		TDBG("can not find symbol: %s\n", sym);	\
136133802Sdavidxu		ret = TD_NOLIBTHREAD;			\
137133802Sdavidxu		goto error;				\
138133802Sdavidxu	}						\
139133802Sdavidxu	ret = ps_pread(proc, vaddr, val, sizeof(int));	\
140133802Sdavidxu	if (ret != 0) {					\
141133802Sdavidxu		TDBG("can not read value of %s\n", sym);\
142133802Sdavidxu		ret = TD_NOLIBTHREAD;			\
143133802Sdavidxu		goto error;				\
144133802Sdavidxu	}
145133802Sdavidxu
146132332Smarcel	td_thragent_t *ta;
147133802Sdavidxu	psaddr_t vaddr;
148132332Smarcel	int dbg;
149132332Smarcel	int ret;
150132332Smarcel
151132332Smarcel	TDBG_FUNC();
152132332Smarcel
153132332Smarcel	ta = malloc(sizeof(td_thragent_t));
154132332Smarcel	if (ta == NULL)
155132332Smarcel		return (TD_MALLOC);
156132332Smarcel
157132332Smarcel	ta->ph = ph;
158132332Smarcel	ta->thread_activated = 0;
159132332Smarcel	ta->map = NULL;
160132332Smarcel	ta->map_len = 0;
161132332Smarcel
162132332Smarcel	LOOKUP_SYM(ph, "_libkse_debug",		&ta->libkse_debug_addr);
163132332Smarcel	LOOKUP_SYM(ph, "_thread_list",		&ta->thread_list_addr);
164132332Smarcel	LOOKUP_SYM(ph, "_thread_activated",	&ta->thread_activated_addr);
165132332Smarcel	LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
166132332Smarcel	LOOKUP_SYM(ph, "_thread_keytable",	&ta->thread_keytable_addr);
167133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_dtv",	&ta->thread_off_dtv);
168133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_kse_locklevel", &ta->thread_off_kse_locklevel);
169133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_kse",	&ta->thread_off_kse);
170133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tlsindex",	&ta->thread_off_tlsindex);
171133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_attr_flags",	&ta->thread_off_attr_flags);
172133802Sdavidxu	LOOKUP_VAL(ph, "_thread_size_key",	&ta->thread_size_key);
173133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tcb",	&ta->thread_off_tcb);
174133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_linkmap",	&ta->thread_off_linkmap);
175133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_tmbx",	&ta->thread_off_tmbx);
176133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_thr_locklevel",	&ta->thread_off_thr_locklevel);
177133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_next",	&ta->thread_off_next);
178133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_state",	&ta->thread_off_state);
179133802Sdavidxu	LOOKUP_VAL(ph, "_thread_max_keys",	&ta->thread_max_keys);
180133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
181133802Sdavidxu	LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
182133802Sdavidxu	LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
183133802Sdavidxu	LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
184158680Sdavidxu	LOOKUP_VAL(ph, "_thread_off_sigmask",	&ta->thread_off_sigmask);
185158680Sdavidxu	LOOKUP_VAL(ph, "_thread_off_sigpend",	&ta->thread_off_sigpend);
186132332Smarcel	dbg = getpid();
187132332Smarcel	/*
188132332Smarcel	 * If this fails it probably means we're debugging a core file and
189132332Smarcel	 * can't write to it.
190132332Smarcel	 */
191132332Smarcel	ps_pwrite(ph, ta->libkse_debug_addr, &dbg, sizeof(int));
192132332Smarcel	*pta = ta;
193132332Smarcel	return (0);
194132332Smarcel
195132332Smarcelerror:
196132332Smarcel	free(ta);
197132332Smarcel	return (ret);
198132332Smarcel}
199132332Smarcel
200132332Smarcelstatic td_err_e
201132332Smarcelpt_ta_delete(td_thragent_t *ta)
202132332Smarcel{
203132332Smarcel	int dbg;
204132332Smarcel
205132332Smarcel	TDBG_FUNC();
206132332Smarcel
207132332Smarcel	dbg = 0;
208132332Smarcel	/*
209132332Smarcel	 * Error returns from this write are not really a problem;
210132332Smarcel	 * the process doesn't exist any more.
211132332Smarcel	 */
212132332Smarcel	ps_pwrite(ta->ph, ta->libkse_debug_addr, &dbg, sizeof(int));
213132332Smarcel	if (ta->map)
214132332Smarcel		free(ta->map);
215132332Smarcel	free(ta);
216132332Smarcel	return (TD_OK);
217132332Smarcel}
218132332Smarcel
219132332Smarcelstatic td_err_e
220132332Smarcelpt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
221132332Smarcel{
222132332Smarcel	prgregset_t gregs;
223132332Smarcel	TAILQ_HEAD(, pthread) thread_list;
224132332Smarcel	psaddr_t pt, tcb_addr;
225132332Smarcel	lwpid_t lwp;
226132332Smarcel	int ret;
227132332Smarcel
228132332Smarcel	TDBG_FUNC();
229132332Smarcel
230132332Smarcel	if (id < 0 || id >= ta->map_len || ta->map[id].type == PT_NONE)
231132332Smarcel		return (TD_NOTHR);
232132332Smarcel	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
233132332Smarcel			sizeof(thread_list));
234132332Smarcel	if (ret != 0)
235132332Smarcel		return (P2T(ret));
236132332Smarcel	pt = (psaddr_t)thread_list.tqh_first;
237132332Smarcel	if (ta->map[id].type == PT_LWP) {
238132332Smarcel		/*
239132332Smarcel		 * if we are referencing a lwp, make sure it was not already
240132332Smarcel		 * mapped to user thread.
241132332Smarcel		 */
242132332Smarcel		while (pt != 0) {
243180982Smarcel			ret = ps_pread(ta->ph, pt + ta->thread_off_tcb,
244132332Smarcel			        &tcb_addr, sizeof(tcb_addr));
245132332Smarcel			if (ret != 0)
246132332Smarcel				return (P2T(ret));
247132332Smarcel			ret = ps_pread(ta->ph,
248133802Sdavidxu			        tcb_addr + ta->thread_off_tmbx +
249133802Sdavidxu				offsetof(struct kse_thr_mailbox, tm_lwp),
250132332Smarcel				&lwp, sizeof(lwp));
251132332Smarcel			if (ret != 0)
252132332Smarcel				return (P2T(ret));
253132332Smarcel			/*
254132332Smarcel			 * If the lwp was already mapped to userland thread,
255132332Smarcel			 * we shouldn't reference it directly in future.
256132332Smarcel			 */
257132332Smarcel			if (lwp == ta->map[id].lwp) {
258132332Smarcel				ta->map[id].type = PT_NONE;
259132332Smarcel				return (TD_NOTHR);
260132332Smarcel			}
261132332Smarcel			/* get next thread */
262132332Smarcel			ret = ps_pread(ta->ph,
263133802Sdavidxu			        pt + ta->thread_off_next,
264132332Smarcel			        &pt, sizeof(pt));
265132332Smarcel			if (ret != 0)
266132332Smarcel				return (P2T(ret));
267132332Smarcel		}
268132332Smarcel		/* check lwp */
269155411Sdavidxu		ret = ps_lgetregs(ta->ph, ta->map[id].lwp, gregs);
270155411Sdavidxu		if (ret != PS_OK) {
271132332Smarcel			/* no longer exists */
272132332Smarcel			ta->map[id].type = PT_NONE;
273132332Smarcel			return (TD_NOTHR);
274132332Smarcel		}
275132332Smarcel	} else {
276132332Smarcel		while (pt != 0 && ta->map[id].thr != pt) {
277132332Smarcel			ret = ps_pread(ta->ph,
278133802Sdavidxu				pt + ta->thread_off_tcb,
279132332Smarcel				&tcb_addr, sizeof(tcb_addr));
280132332Smarcel			if (ret != 0)
281132332Smarcel				return (P2T(ret));
282132332Smarcel			/* get next thread */
283132332Smarcel			ret = ps_pread(ta->ph,
284133802Sdavidxu				pt + ta->thread_off_next,
285132332Smarcel				&pt, sizeof(pt));
286132332Smarcel			if (ret != 0)
287132332Smarcel				return (P2T(ret));
288132332Smarcel		}
289132332Smarcel
290132332Smarcel		if (pt == 0) {
291132332Smarcel			/* no longer exists */
292132332Smarcel			ta->map[id].type = PT_NONE;
293132332Smarcel			return (TD_NOTHR);
294132332Smarcel		}
295132332Smarcel	}
296132332Smarcel	th->th_ta = ta;
297132332Smarcel	th->th_tid = id;
298144663Sdavidxu	th->th_thread = pt;
299132332Smarcel	return (TD_OK);
300132332Smarcel}
301132332Smarcel
302132332Smarcelstatic td_err_e
303132332Smarcelpt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
304132332Smarcel{
305132332Smarcel	TAILQ_HEAD(, pthread) thread_list;
306132332Smarcel	psaddr_t pt, ptr;
307132332Smarcel	lwpid_t tmp_lwp;
308132332Smarcel	int ret;
309132332Smarcel
310132332Smarcel	TDBG_FUNC();
311132332Smarcel
312132332Smarcel	ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
313132332Smarcel	                sizeof(thread_list));
314132332Smarcel	if (ret != 0)
315132332Smarcel		return (P2T(ret));
316132332Smarcel	pt = (psaddr_t)thread_list.tqh_first;
317132332Smarcel	while (pt != 0) {
318133802Sdavidxu		ret = ps_pread(ta->ph, pt + ta->thread_off_tcb,
319132332Smarcel				&ptr, sizeof(ptr));
320132332Smarcel		if (ret != 0)
321132332Smarcel			return (P2T(ret));
322133802Sdavidxu		ptr += ta->thread_off_tmbx +
323133802Sdavidxu		       offsetof(struct kse_thr_mailbox, tm_lwp);
324132332Smarcel		ret = ps_pread(ta->ph, ptr, &tmp_lwp, sizeof(lwpid_t));
325132332Smarcel		if (ret != 0)
326132332Smarcel			return (P2T(ret));
327132332Smarcel		if (tmp_lwp == lwp) {
328132332Smarcel			th->th_ta = ta;
329132332Smarcel			th->th_tid = pt_map_thread(ta, pt, PT_USER);
330132332Smarcel			if (th->th_tid == -1)
331132332Smarcel				return (TD_MALLOC);
332132332Smarcel			pt_unmap_lwp(ta, lwp);
333144663Sdavidxu			th->th_thread = pt;
334132332Smarcel			return (TD_OK);
335132332Smarcel		}
336132332Smarcel
337132332Smarcel		/* get next thread */
338132332Smarcel		ret = ps_pread(ta->ph,
339133802Sdavidxu		           pt + ta->thread_off_next,
340132332Smarcel		           &pt, sizeof(pt));
341132332Smarcel		if (ret != 0)
342132332Smarcel			return (P2T(ret));
343132332Smarcel	}
344132332Smarcel
345132332Smarcel	return (TD_NOTHR);
346132332Smarcel}
347132332Smarcel
348132332Smarcelstatic td_err_e
349132332Smarcelpt_ta_thr_iter(const td_thragent_t *ta,
350132332Smarcel               td_thr_iter_f *callback, void *cbdata_p,
351132332Smarcel               td_thr_state_e state, int ti_pri,
352132332Smarcel               sigset_t *ti_sigmask_p,
353132332Smarcel               unsigned int ti_user_flags)
354132332Smarcel{
355132332Smarcel	TAILQ_HEAD(, pthread) thread_list;
356132332Smarcel	td_thrhandle_t th;
357132332Smarcel	psaddr_t pt;
358132332Smarcel	ps_err_e pserr;
359132332Smarcel	int activated;
360132332Smarcel
361132332Smarcel	TDBG_FUNC();
362132332Smarcel
363132332Smarcel	pserr = ps_pread(ta->ph, ta->thread_activated_addr, &activated,
364132332Smarcel	    sizeof(int));
365132332Smarcel	if (pserr != PS_OK)
366132332Smarcel		return (P2T(pserr));
367132332Smarcel	if (!activated)
368132332Smarcel		return (TD_OK);
369132332Smarcel
370132332Smarcel	pserr = ps_pread(ta->ph, ta->thread_list_addr, &thread_list,
371132332Smarcel	    sizeof(thread_list));
372132332Smarcel	if (pserr != 0)
373132332Smarcel		return (P2T(pserr));
374132332Smarcel	pt = (psaddr_t)thread_list.tqh_first;
375132332Smarcel	while (pt != 0) {
376132332Smarcel		th.th_ta = ta;
377132332Smarcel		th.th_tid = pt_map_thread(ta, pt, PT_USER);
378144663Sdavidxu		th.th_thread = pt;
379132332Smarcel		/* should we unmap lwp here ? */
380132332Smarcel		if (th.th_tid == -1)
381132332Smarcel			return (TD_MALLOC);
382132332Smarcel		if ((*callback)(&th, cbdata_p))
383132332Smarcel			return (TD_DBERR);
384132332Smarcel		/* get next thread */
385132332Smarcel		pserr = ps_pread(ta->ph,
386133802Sdavidxu		    pt + ta->thread_off_next, &pt,
387132332Smarcel		    sizeof(pt));
388132332Smarcel		if (pserr != PS_OK)
389132332Smarcel			return (P2T(pserr));
390132332Smarcel	}
391132332Smarcel	return (TD_OK);
392132332Smarcel}
393132332Smarcel
394132332Smarcelstatic td_err_e
395132332Smarcelpt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
396132332Smarcel{
397133802Sdavidxu	char *keytable;
398133802Sdavidxu	void *destructor;
399133802Sdavidxu	int i, ret, allocated;
400132332Smarcel
401132332Smarcel	TDBG_FUNC();
402132332Smarcel
403133802Sdavidxu	keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
404133802Sdavidxu	if (keytable == NULL)
405133802Sdavidxu		return (TD_MALLOC);
406132332Smarcel	ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
407133802Sdavidxu	               ta->thread_max_keys * ta->thread_size_key);
408133805Sdavidxu	if (ret != 0) {
409133805Sdavidxu		free(keytable);
410132332Smarcel		return (P2T(ret));
411133805Sdavidxu	}
412133802Sdavidxu	for (i = 0; i < ta->thread_max_keys; i++) {
413133802Sdavidxu		allocated = *(int *)(keytable + i * ta->thread_size_key +
414133802Sdavidxu			ta->thread_off_key_allocated);
415133802Sdavidxu		destructor = *(void **)(keytable + i * ta->thread_size_key +
416133802Sdavidxu			ta->thread_off_key_destructor);
417133802Sdavidxu		if (allocated) {
418133802Sdavidxu			ret = (ki)(i, destructor, arg);
419133802Sdavidxu			if (ret != 0) {
420133802Sdavidxu				free(keytable);
421132332Smarcel				return (TD_DBERR);
422133802Sdavidxu			}
423132332Smarcel		}
424132332Smarcel	}
425133802Sdavidxu	free(keytable);
426132332Smarcel	return (TD_OK);
427132332Smarcel}
428132332Smarcel
429132332Smarcelstatic td_err_e
430132332Smarcelpt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
431132332Smarcel{
432132332Smarcel	TDBG_FUNC();
433144922Sdavidxu	return (TD_ERR);
434132332Smarcel}
435132332Smarcel
436132332Smarcelstatic td_err_e
437132332Smarcelpt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
438132332Smarcel{
439132332Smarcel	TDBG_FUNC();
440144922Sdavidxu	return (0);
441132332Smarcel}
442132332Smarcel
443132332Smarcelstatic td_err_e
444132332Smarcelpt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
445132332Smarcel{
446132332Smarcel	TDBG_FUNC();
447144922Sdavidxu	return (0);
448132332Smarcel}
449132332Smarcel
450132332Smarcelstatic td_err_e
451132332Smarcelpt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
452132332Smarcel{
453132332Smarcel	TDBG_FUNC();
454132332Smarcel	return (TD_NOMSG);
455132332Smarcel}
456132332Smarcel
457132332Smarcelstatic td_err_e
458132951Sdavidxupt_dbsuspend(const td_thrhandle_t *th, int suspend)
459132951Sdavidxu{
460132951Sdavidxu	td_thragent_t *ta = (td_thragent_t *)th->th_ta;
461132951Sdavidxu	psaddr_t tcb_addr, tmbx_addr, ptr;
462132951Sdavidxu	lwpid_t lwp;
463132951Sdavidxu	uint32_t dflags;
464133342Sdavidxu	int attrflags, locklevel, ret;
465132951Sdavidxu
466132951Sdavidxu	TDBG_FUNC();
467132951Sdavidxu
468132951Sdavidxu	ret = pt_validate(th);
469132951Sdavidxu	if (ret)
470132951Sdavidxu		return (ret);
471132951Sdavidxu
472132951Sdavidxu	if (ta->map[th->th_tid].type == PT_LWP) {
473132951Sdavidxu		if (suspend)
474132951Sdavidxu			ret = ps_lstop(ta->ph, ta->map[th->th_tid].lwp);
475132951Sdavidxu		else
476132951Sdavidxu			ret = ps_lcontinue(ta->ph, ta->map[th->th_tid].lwp);
477132951Sdavidxu		return (P2T(ret));
478132951Sdavidxu	}
479132951Sdavidxu
480132951Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
481133802Sdavidxu		ta->thread_off_attr_flags,
482132951Sdavidxu		&attrflags, sizeof(attrflags));
483132951Sdavidxu	if (ret != 0)
484132951Sdavidxu		return (P2T(ret));
485132951Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
486133802Sdavidxu	               ta->thread_off_tcb,
487133802Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
488132951Sdavidxu	if (ret != 0)
489132951Sdavidxu		return (P2T(ret));
490133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
491132951Sdavidxu	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
492132951Sdavidxu	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
493132951Sdavidxu	if (ret != 0)
494132951Sdavidxu		return (P2T(ret));
495133342Sdavidxu
496133342Sdavidxu	if (lwp != 0) {
497133342Sdavidxu		/* don't suspend signal thread */
498133802Sdavidxu		if (attrflags & 0x200)
499133342Sdavidxu			return (0);
500133342Sdavidxu		if (attrflags & PTHREAD_SCOPE_SYSTEM) {
501133342Sdavidxu			/*
502133342Sdavidxu			 * don't suspend system scope thread if it is holding
503133342Sdavidxu			 * some low level locks
504133342Sdavidxu			 */
505133802Sdavidxu			ptr = ta->map[th->th_tid].thr + ta->thread_off_kse;
506133342Sdavidxu			ret = ps_pread(ta->ph, ptr, &ptr, sizeof(ptr));
507133342Sdavidxu			if (ret != 0)
508133342Sdavidxu				return (P2T(ret));
509133802Sdavidxu			ret = ps_pread(ta->ph, ptr + ta->thread_off_kse_locklevel,
510133802Sdavidxu				&locklevel, sizeof(int));
511133342Sdavidxu			if (ret != 0)
512133342Sdavidxu				return (P2T(ret));
513133342Sdavidxu			if (locklevel <= 0) {
514133342Sdavidxu				ptr = ta->map[th->th_tid].thr +
515133802Sdavidxu					ta->thread_off_thr_locklevel;
516133342Sdavidxu				ret = ps_pread(ta->ph, ptr, &locklevel,
517133342Sdavidxu					sizeof(int));
518133342Sdavidxu				if (ret != 0)
519133342Sdavidxu					return (P2T(ret));
520133342Sdavidxu			}
521133342Sdavidxu			if (suspend) {
522133342Sdavidxu				if (locklevel <= 0)
523133342Sdavidxu					ret = ps_lstop(ta->ph, lwp);
524133342Sdavidxu			} else {
525132951Sdavidxu				ret = ps_lcontinue(ta->ph, lwp);
526133342Sdavidxu			}
527132951Sdavidxu			if (ret != 0)
528132951Sdavidxu				return (P2T(ret));
529133342Sdavidxu			/* FALLTHROUGH */
530133342Sdavidxu		} else {
531133342Sdavidxu			struct ptrace_lwpinfo pl;
532133342Sdavidxu
533155413Sdavidxu			if (ps_linfo(ta->ph, lwp, (caddr_t)&pl))
534133342Sdavidxu				return (TD_ERR);
535133342Sdavidxu			if (suspend) {
536133342Sdavidxu				if (!(pl.pl_flags & PL_FLAG_BOUND))
537133342Sdavidxu					ret = ps_lstop(ta->ph, lwp);
538133342Sdavidxu			} else {
539133342Sdavidxu				ret = ps_lcontinue(ta->ph, lwp);
540133342Sdavidxu			}
541133342Sdavidxu			if (ret != 0)
542133342Sdavidxu				return (P2T(ret));
543133342Sdavidxu			/* FALLTHROUGH */
544132951Sdavidxu		}
545132951Sdavidxu	}
546132951Sdavidxu	/* read tm_dflags */
547132951Sdavidxu	ret = ps_pread(ta->ph,
548132951Sdavidxu		tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
549132951Sdavidxu		&dflags, sizeof(dflags));
550132951Sdavidxu	if (ret != 0)
551132951Sdavidxu		return (P2T(ret));
552132951Sdavidxu	if (suspend)
553133047Sdavidxu		dflags |= TMDF_SUSPEND;
554132951Sdavidxu	else
555133047Sdavidxu		dflags &= ~TMDF_SUSPEND;
556132951Sdavidxu	ret = ps_pwrite(ta->ph,
557132951Sdavidxu	       tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
558132951Sdavidxu	       &dflags, sizeof(dflags));
559132951Sdavidxu	return (P2T(ret));
560132951Sdavidxu}
561132951Sdavidxu
562132951Sdavidxustatic td_err_e
563132332Smarcelpt_thr_dbresume(const td_thrhandle_t *th)
564132332Smarcel{
565132332Smarcel	TDBG_FUNC();
566132951Sdavidxu
567132951Sdavidxu	return pt_dbsuspend(th, 0);
568132332Smarcel}
569132332Smarcel
570132332Smarcelstatic td_err_e
571132332Smarcelpt_thr_dbsuspend(const td_thrhandle_t *th)
572132332Smarcel{
573132332Smarcel	TDBG_FUNC();
574132951Sdavidxu
575132951Sdavidxu	return pt_dbsuspend(th, 1);
576132332Smarcel}
577132332Smarcel
578132332Smarcelstatic td_err_e
579132332Smarcelpt_thr_validate(const td_thrhandle_t *th)
580132332Smarcel{
581132332Smarcel	td_thrhandle_t temp;
582132332Smarcel	int ret;
583132332Smarcel
584132332Smarcel	TDBG_FUNC();
585132332Smarcel
586132332Smarcel	ret = pt_ta_map_id2thr(th->th_ta, th->th_tid,
587132332Smarcel	                       &temp);
588132951Sdavidxu	return (ret);
589132332Smarcel}
590132332Smarcel
591132332Smarcelstatic td_err_e
592132332Smarcelpt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
593132332Smarcel{
594132332Smarcel	const td_thragent_t *ta = th->th_ta;
595158680Sdavidxu	struct ptrace_lwpinfo linfo;
596133802Sdavidxu	psaddr_t tcb_addr;
597133802Sdavidxu	uint32_t dflags;
598158680Sdavidxu	lwpid_t lwp;
599133802Sdavidxu	int state;
600132332Smarcel	int ret;
601158680Sdavidxu	int attrflags;
602132332Smarcel
603132332Smarcel	TDBG_FUNC();
604132332Smarcel
605155387Sdavidxu	bzero(info, sizeof(*info));
606132332Smarcel	ret = pt_validate(th);
607132332Smarcel	if (ret)
608132332Smarcel		return (ret);
609132332Smarcel
610132332Smarcel	memset(info, 0, sizeof(*info));
611132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
612132332Smarcel		info->ti_type = TD_THR_SYSTEM;
613132332Smarcel		info->ti_lid = ta->map[th->th_tid].lwp;
614132332Smarcel		info->ti_tid = th->th_tid;
615132332Smarcel		info->ti_state = TD_THR_RUN;
616132332Smarcel		info->ti_type = TD_THR_SYSTEM;
617132332Smarcel		return (TD_OK);
618132332Smarcel	}
619158680Sdavidxu
620158680Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
621158680Sdavidxu		ta->thread_off_attr_flags,
622158680Sdavidxu		&attrflags, sizeof(attrflags));
623158680Sdavidxu	if (ret != 0)
624158680Sdavidxu		return (P2T(ret));
625133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
626133802Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
627132332Smarcel	if (ret != 0)
628132332Smarcel		return (P2T(ret));
629133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_state,
630133802Sdavidxu	               &state, sizeof(state));
631132332Smarcel	ret = ps_pread(ta->ph,
632133802Sdavidxu	        tcb_addr + ta->thread_off_tmbx +
633133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_lwp),
634132332Smarcel	        &info->ti_lid, sizeof(lwpid_t));
635132332Smarcel	if (ret != 0)
636132332Smarcel		return (P2T(ret));
637132951Sdavidxu	ret = ps_pread(ta->ph,
638133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
639133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_dflags),
640132951Sdavidxu		&dflags, sizeof(dflags));
641132951Sdavidxu	if (ret != 0)
642132951Sdavidxu		return (P2T(ret));
643158680Sdavidxu	ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_tmbx +
644158680Sdavidxu		offsetof(struct kse_thr_mailbox, tm_lwp), &lwp, sizeof(lwpid_t));
645158680Sdavidxu	if (ret != 0)
646158680Sdavidxu		return (P2T(ret));
647132332Smarcel	info->ti_ta_p = th->th_ta;
648132332Smarcel	info->ti_tid = th->th_tid;
649158680Sdavidxu
650158680Sdavidxu	if (attrflags & PTHREAD_SCOPE_SYSTEM) {
651158680Sdavidxu		ret = ps_linfo(ta->ph, lwp, &linfo);
652158680Sdavidxu		if (ret == PS_OK) {
653158680Sdavidxu			info->ti_sigmask = linfo.pl_sigmask;
654158680Sdavidxu			info->ti_pending = linfo.pl_siglist;
655158680Sdavidxu		} else
656158680Sdavidxu			return (ret);
657158680Sdavidxu	} else {
658158680Sdavidxu		ret = ps_pread(ta->ph,
659158680Sdavidxu			ta->map[th->th_tid].thr + ta->thread_off_sigmask,
660158680Sdavidxu			&info->ti_sigmask, sizeof(sigset_t));
661158680Sdavidxu		if (ret)
662158680Sdavidxu			return (ret);
663158680Sdavidxu		ret = ps_pread(ta->ph,
664158680Sdavidxu			ta->map[th->th_tid].thr + ta->thread_off_sigpend,
665158680Sdavidxu			&info->ti_pending, sizeof(sigset_t));
666158680Sdavidxu		if (ret)
667158680Sdavidxu			return (ret);
668158680Sdavidxu	}
669158680Sdavidxu
670133802Sdavidxu	if (state == ta->thread_state_running)
671132332Smarcel		info->ti_state = TD_THR_RUN;
672133802Sdavidxu	else if (state == ta->thread_state_zoombie)
673133802Sdavidxu		info->ti_state = TD_THR_ZOMBIE;
674133802Sdavidxu	else
675132332Smarcel		info->ti_state = TD_THR_SLEEP;
676133047Sdavidxu	info->ti_db_suspended = ((dflags & TMDF_SUSPEND) != 0);
677132332Smarcel	info->ti_type = TD_THR_USER;
678132332Smarcel	return (0);
679132332Smarcel}
680132332Smarcel
681146818Sdfr#ifdef __i386__
682132332Smarcelstatic td_err_e
683146818Sdfrpt_thr_getxmmregs(const td_thrhandle_t *th, char *fxsave)
684146818Sdfr{
685146818Sdfr	const td_thragent_t *ta = th->th_ta;
686146818Sdfr	struct kse_thr_mailbox tmbx;
687146818Sdfr	psaddr_t tcb_addr, tmbx_addr, ptr;
688146818Sdfr	lwpid_t lwp;
689146818Sdfr	int ret;
690146818Sdfr
691146818Sdfr	return TD_ERR;
692146818Sdfr
693146818Sdfr	TDBG_FUNC();
694146818Sdfr
695146818Sdfr	ret = pt_validate(th);
696146818Sdfr	if (ret)
697146818Sdfr		return (ret);
698146818Sdfr
699146818Sdfr	if (ta->map[th->th_tid].type == PT_LWP) {
700146818Sdfr		ret = ps_lgetxmmregs(ta->ph, ta->map[th->th_tid].lwp, fxsave);
701146818Sdfr		return (P2T(ret));
702146818Sdfr	}
703146818Sdfr
704146818Sdfr	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
705146818Sdfr	               &tcb_addr, sizeof(tcb_addr));
706146818Sdfr	if (ret != 0)
707146818Sdfr		return (P2T(ret));
708146818Sdfr	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
709146818Sdfr	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
710146818Sdfr	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
711146818Sdfr	if (ret != 0)
712146818Sdfr		return (P2T(ret));
713146818Sdfr	if (lwp != 0) {
714146818Sdfr		ret = ps_lgetxmmregs(ta->ph, lwp, fxsave);
715146818Sdfr		return (P2T(ret));
716146818Sdfr	}
717146818Sdfr
718146818Sdfr	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
719146818Sdfr	if (ret != 0)
720146818Sdfr		return (P2T(ret));
721146818Sdfr	pt_ucontext_to_fxsave(&tmbx.tm_context, fxsave);
722146818Sdfr	return (0);
723146818Sdfr}
724146818Sdfr#endif
725146818Sdfr
726146818Sdfrstatic td_err_e
727132332Smarcelpt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
728132332Smarcel{
729132332Smarcel	const td_thragent_t *ta = th->th_ta;
730132332Smarcel	struct kse_thr_mailbox tmbx;
731132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
732132332Smarcel	lwpid_t lwp;
733132332Smarcel	int ret;
734132332Smarcel
735132332Smarcel	TDBG_FUNC();
736132332Smarcel
737132332Smarcel	ret = pt_validate(th);
738132332Smarcel	if (ret)
739132332Smarcel		return (ret);
740132332Smarcel
741132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
742132332Smarcel		ret = ps_lgetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
743132332Smarcel		return (P2T(ret));
744132332Smarcel	}
745132332Smarcel
746133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
747132951Sdavidxu	               &tcb_addr, sizeof(tcb_addr));
748132332Smarcel	if (ret != 0)
749132332Smarcel		return (P2T(ret));
750133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
751132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
752132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
753132332Smarcel	if (ret != 0)
754132332Smarcel		return (P2T(ret));
755132332Smarcel	if (lwp != 0) {
756132332Smarcel		ret = ps_lgetfpregs(ta->ph, lwp, fpregs);
757132332Smarcel		return (P2T(ret));
758132332Smarcel	}
759132332Smarcel
760132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
761132332Smarcel	if (ret != 0)
762132332Smarcel		return (P2T(ret));
763132332Smarcel	pt_ucontext_to_fpreg(&tmbx.tm_context, fpregs);
764132332Smarcel	return (0);
765132332Smarcel}
766132332Smarcel
767132332Smarcelstatic td_err_e
768132332Smarcelpt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
769132332Smarcel{
770132332Smarcel	const td_thragent_t *ta = th->th_ta;
771132332Smarcel	struct kse_thr_mailbox tmbx;
772132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
773132332Smarcel	lwpid_t lwp;
774132332Smarcel	int ret;
775132332Smarcel
776132332Smarcel	TDBG_FUNC();
777132332Smarcel
778132332Smarcel	ret = pt_validate(th);
779132332Smarcel	if (ret)
780132332Smarcel		return (ret);
781132332Smarcel
782132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
783132332Smarcel		ret = ps_lgetregs(ta->ph,
784132332Smarcel		                  ta->map[th->th_tid].lwp, gregs);
785132332Smarcel		return (P2T(ret));
786132332Smarcel	}
787132332Smarcel
788133802Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
789132332Smarcel			&tcb_addr, sizeof(tcb_addr));
790132332Smarcel	if (ret != 0)
791132332Smarcel		return (P2T(ret));
792133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
793132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
794132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
795132332Smarcel	if (ret != 0)
796132332Smarcel		return (P2T(ret));
797132332Smarcel	if (lwp != 0) {
798132332Smarcel		ret = ps_lgetregs(ta->ph, lwp, gregs);
799132332Smarcel		return (P2T(ret));
800132332Smarcel	}
801132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
802132332Smarcel	if (ret != 0)
803132332Smarcel		return (P2T(ret));
804132332Smarcel	pt_ucontext_to_reg(&tmbx.tm_context, gregs);
805132332Smarcel	return (0);
806132332Smarcel}
807132332Smarcel
808146818Sdfr#ifdef __i386__
809132332Smarcelstatic td_err_e
810146818Sdfrpt_thr_setxmmregs(const td_thrhandle_t *th, const char *fxsave)
811146818Sdfr{
812146818Sdfr	const td_thragent_t *ta = th->th_ta;
813146818Sdfr	struct kse_thr_mailbox tmbx;
814146818Sdfr	psaddr_t tcb_addr, tmbx_addr, ptr;
815146818Sdfr	lwpid_t lwp;
816146818Sdfr	int ret;
817146818Sdfr
818146818Sdfr	return TD_ERR;
819146818Sdfr
820146818Sdfr	TDBG_FUNC();
821146818Sdfr
822146818Sdfr	ret = pt_validate(th);
823146818Sdfr	if (ret)
824146818Sdfr		return (ret);
825146818Sdfr
826146818Sdfr	if (ta->map[th->th_tid].type == PT_LWP) {
827146818Sdfr		ret = ps_lsetxmmregs(ta->ph, ta->map[th->th_tid].lwp, fxsave);
828146818Sdfr		return (P2T(ret));
829146818Sdfr	}
830146818Sdfr
831146818Sdfr	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
832146818Sdfr	                ta->thread_off_tcb,
833146818Sdfr                        &tcb_addr, sizeof(tcb_addr));
834146818Sdfr	if (ret != 0)
835146818Sdfr		return (P2T(ret));
836146818Sdfr	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
837146818Sdfr	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
838146818Sdfr	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
839146818Sdfr	if (ret != 0)
840146818Sdfr		return (P2T(ret));
841146818Sdfr	if (lwp != 0) {
842146818Sdfr		ret = ps_lsetxmmregs(ta->ph, lwp, fxsave);
843146818Sdfr		return (P2T(ret));
844146818Sdfr	}
845146818Sdfr	/*
846146818Sdfr	 * Read a copy of context, this makes sure that registers
847146818Sdfr	 * not covered by structure reg won't be clobbered
848146818Sdfr	 */
849146818Sdfr	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
850146818Sdfr	if (ret != 0)
851146818Sdfr		return (P2T(ret));
852146818Sdfr
853146818Sdfr	pt_fxsave_to_ucontext(fxsave, &tmbx.tm_context);
854146818Sdfr	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
855146818Sdfr	return (P2T(ret));
856146818Sdfr}
857146818Sdfr#endif
858146818Sdfr
859146818Sdfrstatic td_err_e
860132332Smarcelpt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
861132332Smarcel{
862132332Smarcel	const td_thragent_t *ta = th->th_ta;
863132332Smarcel	struct kse_thr_mailbox tmbx;
864132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
865132332Smarcel	lwpid_t lwp;
866132332Smarcel	int ret;
867132332Smarcel
868132332Smarcel	TDBG_FUNC();
869132332Smarcel
870132332Smarcel	ret = pt_validate(th);
871132332Smarcel	if (ret)
872132332Smarcel		return (ret);
873132332Smarcel
874132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
875132332Smarcel		ret = ps_lsetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
876132332Smarcel		return (P2T(ret));
877132332Smarcel	}
878132332Smarcel
879132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
880133802Sdavidxu	                ta->thread_off_tcb,
881132332Smarcel                        &tcb_addr, sizeof(tcb_addr));
882132332Smarcel	if (ret != 0)
883132332Smarcel		return (P2T(ret));
884133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
885132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
886132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
887132332Smarcel	if (ret != 0)
888132332Smarcel		return (P2T(ret));
889132332Smarcel	if (lwp != 0) {
890132332Smarcel		ret = ps_lsetfpregs(ta->ph, lwp, fpregs);
891132332Smarcel		return (P2T(ret));
892132332Smarcel	}
893132332Smarcel	/*
894132332Smarcel	 * Read a copy of context, this makes sure that registers
895132332Smarcel	 * not covered by structure reg won't be clobbered
896132332Smarcel	 */
897132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
898132332Smarcel	if (ret != 0)
899132332Smarcel		return (P2T(ret));
900132332Smarcel
901132332Smarcel	pt_fpreg_to_ucontext(fpregs, &tmbx.tm_context);
902132332Smarcel	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
903132332Smarcel	return (P2T(ret));
904132332Smarcel}
905132332Smarcel
906132332Smarcelstatic td_err_e
907132332Smarcelpt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
908132332Smarcel{
909132332Smarcel	const td_thragent_t *ta = th->th_ta;
910132332Smarcel	struct kse_thr_mailbox tmbx;
911132332Smarcel	psaddr_t tcb_addr, tmbx_addr, ptr;
912132332Smarcel	lwpid_t lwp;
913132332Smarcel	int ret;
914132332Smarcel
915132332Smarcel	TDBG_FUNC();
916132332Smarcel
917132332Smarcel	ret = pt_validate(th);
918132332Smarcel	if (ret)
919132332Smarcel		return (ret);
920132332Smarcel
921132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP) {
922132332Smarcel		ret = ps_lsetregs(ta->ph, ta->map[th->th_tid].lwp, gregs);
923132332Smarcel		return (P2T(ret));
924132332Smarcel	}
925132332Smarcel
926132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
927133802Sdavidxu	                ta->thread_off_tcb,
928132332Smarcel	                &tcb_addr, sizeof(tcb_addr));
929132332Smarcel	if (ret != 0)
930132332Smarcel		return (P2T(ret));
931133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
932132332Smarcel	ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
933132332Smarcel	ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
934132332Smarcel	if (ret != 0)
935132332Smarcel		return (P2T(ret));
936132332Smarcel	if (lwp != 0) {
937132332Smarcel		ret = ps_lsetregs(ta->ph, lwp, gregs);
938132332Smarcel		return (P2T(ret));
939132332Smarcel	}
940132332Smarcel
941132332Smarcel	/*
942132332Smarcel	 * Read a copy of context, make sure that registers
943132332Smarcel	 * not covered by structure reg won't be clobbered
944132332Smarcel	 */
945132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
946132332Smarcel	if (ret != 0)
947132332Smarcel		return (P2T(ret));
948132332Smarcel	pt_reg_to_ucontext(gregs, &tmbx.tm_context);
949132332Smarcel	ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
950132332Smarcel	return (P2T(ret));
951132332Smarcel}
952132332Smarcel
953132332Smarcelstatic td_err_e
954132332Smarcelpt_thr_event_enable(const td_thrhandle_t *th, int en)
955132332Smarcel{
956132332Smarcel	TDBG_FUNC();
957144922Sdavidxu	return (0);
958132332Smarcel}
959132332Smarcel
960132332Smarcelstatic td_err_e
961132332Smarcelpt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
962132332Smarcel{
963132332Smarcel	TDBG_FUNC();
964144922Sdavidxu	return (0);
965132332Smarcel}
966132332Smarcel
967132332Smarcelstatic td_err_e
968132332Smarcelpt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
969132332Smarcel{
970132332Smarcel	TDBG_FUNC();
971144922Sdavidxu	return (0);
972132332Smarcel}
973132332Smarcel
974132332Smarcelstatic td_err_e
975132332Smarcelpt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
976132332Smarcel{
977132332Smarcel	TDBG_FUNC();
978132332Smarcel	return (TD_NOMSG);
979132332Smarcel}
980132332Smarcel
981132332Smarcelstatic td_err_e
982132332Smarcelpt_thr_sstep(const td_thrhandle_t *th, int step)
983132332Smarcel{
984132332Smarcel	const td_thragent_t *ta = th->th_ta;
985132332Smarcel	struct kse_thr_mailbox tmbx;
986132332Smarcel	struct reg regs;
987132332Smarcel	psaddr_t tcb_addr, tmbx_addr;
988132951Sdavidxu	uint32_t dflags;
989132332Smarcel	lwpid_t lwp;
990132332Smarcel	int ret;
991132332Smarcel
992132332Smarcel	TDBG_FUNC();
993132332Smarcel
994132332Smarcel	ret = pt_validate(th);
995132332Smarcel	if (ret)
996132332Smarcel		return (ret);
997132332Smarcel
998132332Smarcel	if (ta->map[th->th_tid].type == PT_LWP)
999132332Smarcel		return (TD_BADTH);
1000132332Smarcel
1001132332Smarcel	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
1002133802Sdavidxu	                ta->thread_off_tcb,
1003132332Smarcel	                &tcb_addr, sizeof(tcb_addr));
1004132332Smarcel	if (ret != 0)
1005132332Smarcel		return (P2T(ret));
1006132332Smarcel
1007132332Smarcel	/* Clear or set single step flag in thread mailbox */
1008133802Sdavidxu	ret = ps_pread(ta->ph,
1009133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
1010133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_dflags),
1011133802Sdavidxu		&dflags, sizeof(uint32_t));
1012132951Sdavidxu	if (ret != 0)
1013132951Sdavidxu		return (P2T(ret));
1014132951Sdavidxu	if (step != 0)
1015132951Sdavidxu		dflags |= TMDF_SSTEP;
1016132951Sdavidxu	else
1017132951Sdavidxu		dflags &= ~TMDF_SSTEP;
1018133802Sdavidxu	ret = ps_pwrite(ta->ph,
1019133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
1020133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_dflags),
1021133802Sdavidxu	        &dflags, sizeof(uint32_t));
1022132332Smarcel	if (ret != 0)
1023132332Smarcel		return (P2T(ret));
1024132332Smarcel	/* Get lwp */
1025133802Sdavidxu	ret = ps_pread(ta->ph,
1026133802Sdavidxu		tcb_addr + ta->thread_off_tmbx +
1027133802Sdavidxu		 offsetof(struct kse_thr_mailbox, tm_lwp),
1028133802Sdavidxu		&lwp, sizeof(lwpid_t));
1029132332Smarcel	if (ret != 0)
1030132332Smarcel		return (P2T(ret));
1031132332Smarcel	if (lwp != 0)
1032132951Sdavidxu		return (0);
1033132332Smarcel
1034133802Sdavidxu	tmbx_addr = tcb_addr + ta->thread_off_tmbx;
1035132332Smarcel	/*
1036132332Smarcel	 * context is in userland, some architectures store
1037132332Smarcel	 * single step status in registers, we should change
1038132332Smarcel	 * these registers.
1039132332Smarcel	 */
1040132332Smarcel	ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
1041132332Smarcel	if (ret == 0) {
1042132332Smarcel		pt_ucontext_to_reg(&tmbx.tm_context, &regs);
1043132332Smarcel		/* only write out if it is really changed. */
1044132332Smarcel		if (pt_reg_sstep(&regs, step) != 0) {
1045132332Smarcel			pt_reg_to_ucontext(&regs, &tmbx.tm_context);
1046132332Smarcel			ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx,
1047132332Smarcel			                 sizeof(tmbx));
1048132332Smarcel		}
1049132332Smarcel	}
1050132332Smarcel	return (P2T(ret));
1051132332Smarcel}
1052132332Smarcel
1053132332Smarcelstatic void
1054132332Smarcelpt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp)
1055132332Smarcel{
1056132332Smarcel	int i;
1057132332Smarcel
1058132332Smarcel	for (i = 0; i < ta->map_len; ++i) {
1059132332Smarcel		if (ta->map[i].type == PT_LWP && ta->map[i].lwp == lwp) {
1060132332Smarcel			ta->map[i].type = PT_NONE;
1061132332Smarcel			return;
1062132332Smarcel		}
1063132332Smarcel	}
1064132332Smarcel}
1065132332Smarcel
1066132332Smarcelstatic int
1067132332Smarcelpt_validate(const td_thrhandle_t *th)
1068132332Smarcel{
1069132332Smarcel
1070132332Smarcel	if (th->th_tid < 0 || th->th_tid >= th->th_ta->map_len ||
1071132332Smarcel	    th->th_ta->map[th->th_tid].type == PT_NONE)
1072132332Smarcel		return (TD_NOTHR);
1073132332Smarcel	return (TD_OK);
1074132332Smarcel}
1075132332Smarcel
1076181059Smarcelstatic td_err_e
1077180982Smarcelpt_thr_tls_get_addr(const td_thrhandle_t *th, psaddr_t _linkmap, size_t offset,
1078180982Smarcel    psaddr_t *address)
1079133342Sdavidxu{
1080133342Sdavidxu	const td_thragent_t *ta = th->th_ta;
1081180982Smarcel	psaddr_t dtv_addr, obj_entry, tcb_addr;
1082133342Sdavidxu	int tls_index, ret;
1083133342Sdavidxu
1084133342Sdavidxu	/* linkmap is a member of Obj_Entry */
1085180982Smarcel	obj_entry = _linkmap - ta->thread_off_linkmap;
1086133342Sdavidxu
1087133342Sdavidxu	/* get tlsindex of the object file */
1088133342Sdavidxu	ret = ps_pread(ta->ph,
1089133802Sdavidxu		obj_entry + ta->thread_off_tlsindex,
1090133342Sdavidxu		&tls_index, sizeof(tls_index));
1091133342Sdavidxu	if (ret != 0)
1092133342Sdavidxu		return (P2T(ret));
1093133342Sdavidxu
1094133342Sdavidxu	/* get thread tcb */
1095133342Sdavidxu	ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
1096133802Sdavidxu		ta->thread_off_tcb,
1097133342Sdavidxu		&tcb_addr, sizeof(tcb_addr));
1098133342Sdavidxu	if (ret != 0)
1099133342Sdavidxu		return (P2T(ret));
1100133342Sdavidxu
1101133342Sdavidxu	/* get dtv array address */
1102133802Sdavidxu	ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
1103133342Sdavidxu		&dtv_addr, sizeof(dtv_addr));
1104133342Sdavidxu	if (ret != 0)
1105133342Sdavidxu		return (P2T(ret));
1106133342Sdavidxu	/* now get the object's tls block base address */
1107180982Smarcel	ret = ps_pread(ta->ph, dtv_addr + sizeof(void *) * (tls_index + 1),
1108180982Smarcel	    address, sizeof(*address));
1109133342Sdavidxu	if (ret != 0)
1110133342Sdavidxu		return (P2T(ret));
1111133342Sdavidxu
1112133342Sdavidxu	*address += offset;
1113133342Sdavidxu	return (TD_OK);
1114133342Sdavidxu}
1115133342Sdavidxu
1116132332Smarcelstruct ta_ops libpthread_db_ops = {
1117132332Smarcel	.to_init		= pt_init,
1118132332Smarcel	.to_ta_clear_event	= pt_ta_clear_event,
1119132332Smarcel	.to_ta_delete		= pt_ta_delete,
1120132332Smarcel	.to_ta_event_addr	= pt_ta_event_addr,
1121132332Smarcel	.to_ta_event_getmsg	= pt_ta_event_getmsg,
1122132332Smarcel	.to_ta_map_id2thr	= pt_ta_map_id2thr,
1123132332Smarcel	.to_ta_map_lwp2thr	= pt_ta_map_lwp2thr,
1124132332Smarcel	.to_ta_new		= pt_ta_new,
1125132332Smarcel	.to_ta_set_event	= pt_ta_set_event,
1126132332Smarcel	.to_ta_thr_iter		= pt_ta_thr_iter,
1127132332Smarcel	.to_ta_tsd_iter		= pt_ta_tsd_iter,
1128132332Smarcel	.to_thr_clear_event	= pt_thr_clear_event,
1129132332Smarcel	.to_thr_dbresume	= pt_thr_dbresume,
1130132332Smarcel	.to_thr_dbsuspend	= pt_thr_dbsuspend,
1131132332Smarcel	.to_thr_event_enable	= pt_thr_event_enable,
1132132332Smarcel	.to_thr_event_getmsg	= pt_thr_event_getmsg,
1133132332Smarcel	.to_thr_get_info	= pt_thr_get_info,
1134132332Smarcel	.to_thr_getfpregs	= pt_thr_getfpregs,
1135132332Smarcel	.to_thr_getgregs	= pt_thr_getgregs,
1136132332Smarcel	.to_thr_set_event	= pt_thr_set_event,
1137132332Smarcel	.to_thr_setfpregs	= pt_thr_setfpregs,
1138132332Smarcel	.to_thr_setgregs	= pt_thr_setgregs,
1139132332Smarcel	.to_thr_validate	= pt_thr_validate,
1140133342Sdavidxu	.to_thr_tls_get_addr	= pt_thr_tls_get_addr,
1141132332Smarcel
1142132332Smarcel	/* FreeBSD specific extensions. */
1143132332Smarcel	.to_thr_sstep		= pt_thr_sstep,
1144146818Sdfr#ifdef __i386__
1145146818Sdfr	.to_thr_getxmmregs	= pt_thr_getxmmregs,
1146146818Sdfr	.to_thr_setxmmregs	= pt_thr_setxmmregs,
1147146818Sdfr#endif
1148132332Smarcel};
1149177490Sdavidxu
1150177490SdavidxuDATA_SET(__ta_ops, libpthread_db_ops);
1151