summaryrefslogtreecommitdiffstats
path: root/lib/libthread_db
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libthread_db')
-rw-r--r--lib/libthread_db/Makefile23
-rw-r--r--lib/libthread_db/Symbol.map38
-rw-r--r--lib/libthread_db/arch/amd64/libpthread_md.c124
-rw-r--r--lib/libthread_db/arch/arm/libpthread_md.c115
-rw-r--r--lib/libthread_db/arch/i386/libpthread_md.c118
-rw-r--r--lib/libthread_db/arch/ia64/libpthread_md.c65
-rw-r--r--lib/libthread_db/arch/mips/libpthread_md.c90
-rw-r--r--lib/libthread_db/arch/powerpc/libpthread_md.c83
-rw-r--r--lib/libthread_db/arch/sparc64/libpthread_md.c90
-rw-r--r--lib/libthread_db/kse.h135
-rw-r--r--lib/libthread_db/libpthread_db.c1145
-rw-r--r--lib/libthread_db/libpthread_db.h94
-rw-r--r--lib/libthread_db/libthr_db.c803
-rw-r--r--lib/libthread_db/thread_db.c442
-rw-r--r--lib/libthread_db/thread_db.h250
-rw-r--r--lib/libthread_db/thread_db_int.h129
16 files changed, 3744 insertions, 0 deletions
diff --git a/lib/libthread_db/Makefile b/lib/libthread_db/Makefile
new file mode 100644
index 0000000..b612f4f
--- /dev/null
+++ b/lib/libthread_db/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/arch/${MACHINE_CPUARCH}
+
+LIB= thread_db
+SHLIB_MAJOR= 3
+SRCS= thread_db.c
+SRCS+= libpthread_md.c
+SRCS+= libpthread_db.c
+SRCS+= libthr_db.c
+INCS= thread_db.h
+
+CFLAGS+=-I. -I${.CURDIR}
+SYM_MAPS+=${.CURDIR}/Symbol.map
+
+SYMBOL_MAPS=${SYM_MAPS}
+VERSION_DEF=${.CURDIR}/../libc/Versions.def
+
+# Unfortunately, clang gives an incorrect warning about alignment in
+# arch/i386/libpthread_md.c, so turn that off for now.
+NO_WCAST_ALIGN.clang=
+
+.include <bsd.lib.mk>
diff --git a/lib/libthread_db/Symbol.map b/lib/libthread_db/Symbol.map
new file mode 100644
index 0000000..4e690f9
--- /dev/null
+++ b/lib/libthread_db/Symbol.map
@@ -0,0 +1,38 @@
+/*
+ * $FreeBSD$
+ */
+
+FBSD_1.0 {
+ td_init;
+ td_ta_clear_event;
+ td_ta_delete;
+ td_ta_event_addr;
+ td_ta_event_getmsg;
+ td_ta_map_id2thr;
+ td_ta_map_lwp2thr;
+ td_ta_new;
+ td_ta_set_event;
+ td_ta_thr_iter;
+ td_ta_tsd_iter;
+ td_thr_clear_event;
+ td_thr_dbresume;
+ td_thr_dbsuspend;
+ td_thr_event_enable;
+ td_thr_event_getmsg;
+ td_thr_getfpregs;
+ td_thr_getgregs;
+#if defined(i386)
+ td_thr_getxmmregs;
+ td_thr_setxmmregs;
+#endif
+ td_thr_set_event;
+ td_thr_setfpregs;
+ td_thr_setgregs;
+ td_thr_sstep; /* FreeBSD extension to GDB<->thread interface */
+ td_thr_tls_get_addr;
+ td_thr_validate;
+};
+
+FBSD_1.2 {
+ td_thr_get_info;
+};
diff --git a/lib/libthread_db/arch/amd64/libpthread_md.c b/lib/libthread_db/arch/amd64/libpthread_md.c
new file mode 100644
index 0000000..624e650
--- /dev/null
+++ b/lib/libthread_db/arch/amd64/libpthread_md.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * Copyright (c) 2004 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/procfs.h>
+#include <string.h>
+#include <thread_db.h>
+#include <ucontext.h>
+
+#include "libpthread_db.h"
+
+void
+pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc)
+{
+ mcontext_t *mc = &uc->uc_mcontext;
+
+ mc->mc_rdi = r->r_rdi;
+ mc->mc_rsi = r->r_rsi;
+ mc->mc_rdx = r->r_rdx;
+ mc->mc_rcx = r->r_rcx;
+ mc->mc_r8 = r->r_r8;
+ mc->mc_r9 = r->r_r9;
+ mc->mc_rax = r->r_rax;
+ mc->mc_rbx = r->r_rbx;
+ mc->mc_rbp = r->r_rbp;
+ mc->mc_r10 = r->r_r10;
+ mc->mc_r11 = r->r_r11;
+ mc->mc_r12 = r->r_r12;
+ mc->mc_r13 = r->r_r13;
+ mc->mc_r14 = r->r_r14;
+ mc->mc_r15 = r->r_r15;
+ mc->mc_rip = r->r_rip;
+ mc->mc_cs = r->r_cs;
+ mc->mc_rflags = r->r_rflags;
+ mc->mc_rsp = r->r_rsp;
+ mc->mc_ss = r->r_ss;
+}
+
+void
+pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r)
+{
+ const mcontext_t *mc = &uc->uc_mcontext;
+
+ r->r_rdi = mc->mc_rdi;
+ r->r_rsi = mc->mc_rsi;
+ r->r_rdx = mc->mc_rdx;
+ r->r_rcx = mc->mc_rcx;
+ r->r_r8 = mc->mc_r8;
+ r->r_r9 = mc->mc_r9;
+ r->r_rax = mc->mc_rax;
+ r->r_rbx = mc->mc_rbx;
+ r->r_rbp = mc->mc_rbp;
+ r->r_r10 = mc->mc_r10;
+ r->r_r11 = mc->mc_r11;
+ r->r_r12 = mc->mc_r12;
+ r->r_r13 = mc->mc_r13;
+ r->r_r14 = mc->mc_r14;
+ r->r_r15 = mc->mc_r15;
+ r->r_rip = mc->mc_rip;
+ r->r_cs = mc->mc_cs;
+ r->r_rflags = mc->mc_rflags;
+ r->r_rsp = mc->mc_rsp;
+ r->r_ss = mc->mc_ss;
+}
+
+void
+pt_fpreg_to_ucontext(const struct fpreg* r, ucontext_t *uc)
+{
+
+ memcpy(&uc->uc_mcontext.mc_fpstate, r, sizeof(*r));
+}
+
+void
+pt_ucontext_to_fpreg(const ucontext_t *uc, struct fpreg *r)
+{
+
+ memcpy(r, &uc->uc_mcontext.mc_fpstate, sizeof(*r));
+}
+
+void
+pt_md_init(void)
+{
+
+ /* Nothing to do */
+}
+
+int
+pt_reg_sstep(struct reg *reg, int step)
+{
+ register_t old;
+
+ old = reg->r_rflags;
+ if (step)
+ reg->r_rflags |= 0x0100;
+ else
+ reg->r_rflags &= ~0x0100;
+ return (old != reg->r_rflags); /* changed ? */
+}
diff --git a/lib/libthread_db/arch/arm/libpthread_md.c b/lib/libthread_db/arch/arm/libpthread_md.c
new file mode 100644
index 0000000..6e4b2bd
--- /dev/null
+++ b/lib/libthread_db/arch/arm/libpthread_md.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2007 Olivier Houchard
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <string.h>
+#include <thread_db.h>
+
+#include "libpthread_db.h"
+
+void
+pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc)
+{
+ mcontext_t *mc = &uc->uc_mcontext;
+ __greg_t *gr = mc->__gregs;
+
+ gr[_REG_R0] = r->r[0];
+ gr[_REG_R1] = r->r[1];
+ gr[_REG_R2] = r->r[2];
+ gr[_REG_R3] = r->r[3];
+ gr[_REG_R4] = r->r[4];
+ gr[_REG_R5] = r->r[5];
+ gr[_REG_R6] = r->r[6];
+ gr[_REG_R7] = r->r[7];
+ gr[_REG_R8] = r->r[8];
+ gr[_REG_R9] = r->r[9];
+ gr[_REG_R10] = r->r[10];
+ gr[_REG_R11] = r->r[11];
+ gr[_REG_R12] = r->r[12];
+ gr[_REG_SP] = r->r_sp;
+ gr[_REG_LR] = r->r_lr;
+ gr[_REG_PC] = r->r_pc;
+ gr[_REG_CPSR] = r->r_cpsr;
+}
+
+void
+pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r)
+{
+ const mcontext_t *mc = &uc->uc_mcontext;
+
+ const __greg_t *gr = mc->__gregs;
+
+ r->r[0] = gr[_REG_R0];
+ r->r[1] = gr[_REG_R1];
+ r->r[2] = gr[_REG_R2];
+ r->r[3] = gr[_REG_R3];
+ r->r[4] = gr[_REG_R4];
+ r->r[5] = gr[_REG_R5];
+ r->r[6] = gr[_REG_R6];
+ r->r[7] = gr[_REG_R7];
+ r->r[8] = gr[_REG_R8];
+ r->r[9] = gr[_REG_R9];
+ r->r[10] = gr[_REG_R10];
+ r->r[11] = gr[_REG_R11];
+ r->r[12] = gr[_REG_R12];
+ r->r_sp = gr[_REG_SP];
+ r->r_lr = gr[_REG_LR];
+ r->r_pc = gr[_REG_PC];
+ r->r_cpsr = gr[_REG_CPSR];
+}
+
+void
+pt_fpreg_to_ucontext(const struct fpreg *r __unused, ucontext_t *uc)
+{
+ mcontext_t *mc = &uc->uc_mcontext;
+
+ /* XXX */
+ memset(&mc->__fpu.__fpregs, 0, sizeof(__fpregset_t));
+}
+
+void
+pt_ucontext_to_fpreg(const ucontext_t *uc __unused, struct fpreg *r)
+{
+
+ /* XXX */
+ memset(r, 0, sizeof(*r));
+}
+
+void
+pt_md_init(void)
+{
+}
+
+int
+pt_reg_sstep(struct reg *reg __unused, int step __unused)
+{
+
+ /* XXX */
+ return (0);
+}
diff --git a/lib/libthread_db/arch/i386/libpthread_md.c b/lib/libthread_db/arch/i386/libpthread_md.c
new file mode 100644
index 0000000..d41865f
--- /dev/null
+++ b/lib/libthread_db/arch/i386/libpthread_md.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <machine/npx.h>
+#include <string.h>
+#include <thread_db.h>
+
+#include "libpthread_db.h"
+
+static int has_xmm_regs;
+
+void
+pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc)
+{
+ memcpy(&uc->uc_mcontext.mc_fs, &r->r_fs, 18*4);
+ uc->uc_mcontext.mc_gs = r->r_gs;
+}
+
+void
+pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r)
+{
+ memcpy(&r->r_fs, &uc->uc_mcontext.mc_fs, 18*4);
+ r->r_gs = uc->uc_mcontext.mc_gs;
+}
+
+void
+pt_fpreg_to_ucontext(const struct fpreg* r, ucontext_t *uc)
+{
+ if (!has_xmm_regs)
+ memcpy(&uc->uc_mcontext.mc_fpstate, r,
+ sizeof(struct save87));
+ else {
+ int i;
+ struct savexmm *sx = (struct savexmm *)&uc->uc_mcontext.mc_fpstate;
+ memcpy(&sx->sv_env, &r->fpr_env, sizeof(r->fpr_env));
+ for (i = 0; i < 8; ++i)
+ memcpy(&sx->sv_fp[i].fp_acc, &r->fpr_acc[i], 10);
+ }
+}
+
+void
+pt_ucontext_to_fpreg(const ucontext_t *uc, struct fpreg *r)
+{
+ if (!has_xmm_regs)
+ memcpy(r, &uc->uc_mcontext.mc_fpstate, sizeof(struct save87));
+ else {
+ int i;
+ struct savexmm *sx = (struct savexmm *)&uc->uc_mcontext.mc_fpstate;
+ memcpy(&r->fpr_env, &sx->sv_env, sizeof(r->fpr_env));
+ for (i = 0; i < 8; ++i)
+ memcpy(&r->fpr_acc[i], &sx->sv_fp[i].fp_acc, 10);
+ }
+}
+
+void
+pt_fxsave_to_ucontext(const char* r, ucontext_t *uc)
+{
+ if (has_xmm_regs)
+ memcpy(&uc->uc_mcontext.mc_fpstate, r, sizeof(struct savexmm));
+}
+
+void
+pt_ucontext_to_fxsave(const ucontext_t *uc, char *r)
+{
+ if (has_xmm_regs)
+ memcpy(r, &uc->uc_mcontext.mc_fpstate, sizeof(struct savexmm));
+}
+
+void
+pt_md_init(void)
+{
+ ucontext_t uc;
+
+ getcontext(&uc);
+ if (uc.uc_mcontext.mc_fpformat == _MC_FPFMT_XMM)
+ has_xmm_regs = 1;
+}
+
+int
+pt_reg_sstep(struct reg *reg, int step)
+{
+ unsigned int old;
+
+ old = reg->r_eflags;
+ if (step)
+ reg->r_eflags |= 0x0100;
+ else
+ reg->r_eflags &= ~0x0100;
+ return (old != reg->r_eflags); /* changed ? */
+}
+
diff --git a/lib/libthread_db/arch/ia64/libpthread_md.c b/lib/libthread_db/arch/ia64/libpthread_md.c
new file mode 100644
index 0000000..f5f12d3
--- /dev/null
+++ b/lib/libthread_db/arch/ia64/libpthread_md.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2004 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/procfs.h>
+#include <thread_db.h>
+#include <ucontext.h>
+
+#include "libpthread_db.h"
+
+void
+pt_reg_to_ucontext(const struct reg *r __unused, ucontext_t *uc __unused)
+{
+}
+
+void
+pt_ucontext_to_reg(const ucontext_t *uc __unused, struct reg *r __unused)
+{
+}
+
+void
+pt_fpreg_to_ucontext(const struct fpreg* r __unused, ucontext_t *uc __unused)
+{
+}
+
+void
+pt_ucontext_to_fpreg(const ucontext_t *uc __unused, struct fpreg *r __unused)
+{
+}
+
+void
+pt_md_init(void)
+{
+}
+
+int
+pt_reg_sstep(struct reg *reg __unused, int step __unused)
+{
+ return (0);
+}
diff --git a/lib/libthread_db/arch/mips/libpthread_md.c b/lib/libthread_db/arch/mips/libpthread_md.c
new file mode 100644
index 0000000..6d4556f
--- /dev/null
+++ b/lib/libthread_db/arch/mips/libpthread_md.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2006-2007, Juniper Networks, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Juniper Networks nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JUNIPER NETWORKS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/procfs.h>
+#include <ucontext.h>
+#include <string.h>
+#include <thread_db.h>
+#include "libpthread_db.h"
+
+void
+pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc)
+{
+
+ memcpy(uc->uc_mcontext.mc_regs, &r->r_regs[ZERO],
+ sizeof(uc->uc_mcontext.mc_regs));
+ uc->uc_mcontext.mc_pc = r->r_regs[PC];
+ uc->uc_mcontext.mullo = r->r_regs[MULLO];
+ uc->uc_mcontext.mulhi = r->r_regs[MULHI];
+}
+
+void
+pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r)
+{
+ memcpy(&r->r_regs[ZERO], uc->uc_mcontext.mc_regs,
+ sizeof(uc->uc_mcontext.mc_regs));
+ r->r_regs[PC] = uc->uc_mcontext.mc_pc;
+ r->r_regs[MULLO] = uc->uc_mcontext.mullo;
+ r->r_regs[MULHI] = uc->uc_mcontext.mulhi;
+}
+
+void
+pt_fpreg_to_ucontext(const struct fpreg* r, ucontext_t *uc)
+{
+
+ memcpy(uc->uc_mcontext.mc_fpregs, r->r_regs,
+ sizeof(uc->uc_mcontext.mc_fpregs));
+}
+
+void
+pt_ucontext_to_fpreg(const ucontext_t *uc, struct fpreg *r)
+{
+
+ memcpy(r->r_regs, uc->uc_mcontext.mc_fpregs,
+ sizeof(uc->uc_mcontext.mc_fpregs));
+}
+
+void
+pt_md_init(void)
+{
+ /* Nothing to do */
+}
+
+int
+pt_reg_sstep(struct reg *reg __unused, int step __unused)
+{
+ /*
+ * XXX: mips doesnt store single step info in any registers
+ */
+ return (0);
+}
diff --git a/lib/libthread_db/arch/powerpc/libpthread_md.c b/lib/libthread_db/arch/powerpc/libpthread_md.c
new file mode 100644
index 0000000..fbe77f4
--- /dev/null
+++ b/lib/libthread_db/arch/powerpc/libpthread_md.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2006 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <string.h>
+#include <thread_db.h>
+
+#include "libpthread_db.h"
+
+void
+pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc)
+{
+ mcontext_t *mc = &uc->uc_mcontext;
+
+ memcpy(mc->mc_frame, r, MIN(sizeof(mc->mc_frame), sizeof(*r)));
+}
+
+void
+pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r)
+{
+ const mcontext_t *mc = &uc->uc_mcontext;
+
+ memcpy(r, mc->mc_frame, MIN(sizeof(mc->mc_frame), sizeof(*r)));
+}
+
+void
+pt_fpreg_to_ucontext(const struct fpreg *r, ucontext_t *uc)
+{
+ mcontext_t *mc = &uc->uc_mcontext;
+
+ memcpy(mc->mc_fpreg, r, MIN(sizeof(mc->mc_fpreg), sizeof(*r)));
+ mc->mc_flags |= _MC_FP_VALID;
+}
+
+void
+pt_ucontext_to_fpreg(const ucontext_t *uc, struct fpreg *r)
+{
+ const mcontext_t *mc = &uc->uc_mcontext;
+
+ if (mc->mc_flags & _MC_FP_VALID)
+ memcpy(r, mc->mc_fpreg, MIN(sizeof(mc->mc_fpreg), sizeof(*r)));
+ else
+ memset(r, 0, sizeof(*r));
+}
+
+void
+pt_md_init(void)
+{
+}
+
+int
+pt_reg_sstep(struct reg *reg __unused, int step __unused)
+{
+
+ /* XXX */
+ return (0);
+}
diff --git a/lib/libthread_db/arch/sparc64/libpthread_md.c b/lib/libthread_db/arch/sparc64/libpthread_md.c
new file mode 100644
index 0000000..6a84518
--- /dev/null
+++ b/lib/libthread_db/arch/sparc64/libpthread_md.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2004 Marcel Moolenaar
+ * Copyright (c) 2011 Marius Strobl <marius@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <string.h>
+#include <thread_db.h>
+#include <ucontext.h>
+#include <machine/fsr.h>
+
+#include "libpthread_db.h"
+
+void
+pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc)
+{
+
+ memcpy(&uc->uc_mcontext, r, MIN(sizeof(uc->uc_mcontext), sizeof(*r)));
+}
+
+void
+pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r)
+{
+
+ memcpy(r, &uc->uc_mcontext, MIN(sizeof(uc->uc_mcontext), sizeof(*r)));
+}
+
+void
+pt_fpreg_to_ucontext(const struct fpreg* r, ucontext_t *uc)
+{
+ mcontext_t *mc = &uc->uc_mcontext;
+
+ memcpy(mc->mc_fp, r->fr_regs, MIN(sizeof(mc->mc_fp),
+ sizeof(r->fr_regs)));
+ mc->mc_fsr = r->fr_fsr;
+ mc->mc_gsr = r->fr_gsr;
+ mc->mc_fprs |= FPRS_FEF;
+}
+
+void
+pt_ucontext_to_fpreg(const ucontext_t *uc, struct fpreg *r)
+{
+ const mcontext_t *mc = &uc->uc_mcontext;
+
+ if ((mc->mc_fprs & FPRS_FEF) != 0) {
+ memcpy(r->fr_regs, mc->mc_fp, MIN(sizeof(mc->mc_fp),
+ sizeof(r->fr_regs)));
+ r->fr_fsr = mc->mc_fsr;
+ r->fr_gsr = mc->mc_gsr;
+ } else
+ memset(r, 0, sizeof(*r));
+}
+
+void
+pt_md_init(void)
+{
+
+}
+
+int
+pt_reg_sstep(struct reg *reg __unused, int step __unused)
+{
+
+ return (0);
+}
diff --git a/lib/libthread_db/kse.h b/lib/libthread_db/kse.h
new file mode 100644
index 0000000..9eb5449
--- /dev/null
+++ b/lib/libthread_db/kse.h
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (C) 2001 Julian Elischer <julian@freebsd.org>
+ * for the FreeBSD Foundation.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice(s), this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified other than the possible
+ * addition of one or more copyright notices.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice(s), this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_KSE_H_
+#define _SYS_KSE_H_
+
+#include <sys/ucontext.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+
+/*
+ * This file defines the structures needed for communication between
+ * the userland and the kernel when running a KSE-based threading system.
+ * The only programs that should see this file are the user thread
+ * scheduler (UTS) and the kernel.
+ */
+struct kse_mailbox;
+
+typedef void kse_func_t(struct kse_mailbox *);
+
+/*
+ * Thread mailbox.
+ *
+ * This describes a user thread to the kernel scheduler.
+ */
+struct kse_thr_mailbox {
+ ucontext_t tm_context; /* User and machine context */
+ uint32_t tm_flags; /* Thread flags */
+ struct kse_thr_mailbox *tm_next; /* Next thread in list */
+ void *tm_udata; /* For use by the UTS */
+ uint32_t tm_uticks; /* Time in userland */
+ uint32_t tm_sticks; /* Time in kernel */
+ siginfo_t tm_syncsig;
+ uint32_t tm_dflags; /* Debug flags */
+ lwpid_t tm_lwp; /* kernel thread UTS runs on */
+ uint32_t __spare__[6];
+};
+
+/*
+ * KSE mailbox.
+ *
+ * Communication path between the UTS and the kernel scheduler specific to
+ * a single KSE.
+ */
+struct kse_mailbox {
+ uint32_t km_version; /* Mailbox version */
+ struct kse_thr_mailbox *km_curthread; /* Currently running thread */
+ struct kse_thr_mailbox *km_completed; /* Threads back from kernel */
+ sigset_t km_sigscaught; /* Caught signals */
+ uint32_t km_flags; /* Mailbox flags */
+ kse_func_t *km_func; /* UTS function */
+ stack_t km_stack; /* UTS stack */
+ void *km_udata; /* For use by the UTS */
+ struct timespec km_timeofday; /* Time of day */
+ uint32_t km_quantum; /* Upcall quantum in msecs */
+ lwpid_t km_lwp; /* kernel thread UTS runs on */
+ uint32_t __spare2__[7];
+};
+
+#define KSE_VER_0 0
+#define KSE_VERSION KSE_VER_0
+
+/* These flags are kept in km_flags */
+#define KMF_NOUPCALL 0x01
+#define KMF_NOCOMPLETED 0x02
+#define KMF_DONE 0x04
+#define KMF_BOUND 0x08
+#define KMF_WAITSIGEVENT 0x10
+
+/* These flags are kept in tm_flags */
+#define TMF_NOUPCALL 0x01
+
+/* These flags are kept in tm_dlfags */
+#define TMDF_SSTEP 0x01
+#define TMDF_SUSPEND 0x02
+
+/* Flags for kse_switchin */
+#define KSE_SWITCHIN_SETTMBX 0x01
+
+/* Commands for kse_thr_interrupt */
+#define KSE_INTR_INTERRUPT 1
+#define KSE_INTR_RESTART 2
+#define KSE_INTR_SENDSIG 3
+#define KSE_INTR_SIGEXIT 4
+#define KSE_INTR_DBSUSPEND 5
+#define KSE_INTR_EXECVE 6
+
+struct kse_execve_args {
+ sigset_t sigmask;
+ sigset_t sigpend;
+ char *path;
+ char **argv;
+ char **envp;
+ void *reserved;
+};
+
+#ifndef _KERNEL
+int kse_create(struct kse_mailbox *, int);
+int kse_exit(void);
+int kse_release(struct timespec *);
+int kse_thr_interrupt(struct kse_thr_mailbox *, int, long);
+int kse_wakeup(struct kse_mailbox *);
+int kse_switchin(struct kse_thr_mailbox *, int flags);
+#endif /* !_KERNEL */
+
+#endif /* !_SYS_KSE_H_ */
diff --git a/lib/libthread_db/libpthread_db.c b/lib/libthread_db/libpthread_db.c
new file mode 100644
index 0000000..31ea15d
--- /dev/null
+++ b/lib/libthread_db/libpthread_db.c
@@ -0,0 +1,1145 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/linker_set.h>
+#include <sys/ptrace.h>
+#include <proc_service.h>
+#include <thread_db.h>
+
+#include "libpthread_db.h"
+#include "kse.h"
+
+#define P2T(c) ps2td(c)
+
+static void pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp);
+static int pt_validate(const td_thrhandle_t *th);
+
+static int
+ps2td(int c)
+{
+ switch (c) {
+ case PS_OK:
+ return TD_OK;
+ case PS_ERR:
+ return TD_ERR;
+ case PS_BADPID:
+ return TD_BADPH;
+ case PS_BADLID:
+ return TD_NOLWP;
+ case PS_BADADDR:
+ return TD_ERR;
+ case PS_NOSYM:
+ return TD_NOLIBTHREAD;
+ case PS_NOFREGS:
+ return TD_NOFPREGS;
+ default:
+ return TD_ERR;
+ }
+}
+
+static long
+pt_map_thread(const td_thragent_t *const_ta, psaddr_t pt, enum pt_type type)
+{
+ td_thragent_t *ta = __DECONST(td_thragent_t *, const_ta);
+ struct pt_map *new;
+ int i, first = -1;
+
+ /* leave zero out */
+ for (i = 1; i < ta->map_len; ++i) {
+ if (ta->map[i].type == PT_NONE) {
+ if (first == -1)
+ first = i;
+ } else if (ta->map[i].type == type && ta->map[i].thr == pt) {
+ return (i);
+ }
+ }
+
+ if (first == -1) {
+ if (ta->map_len == 0) {
+ ta->map = calloc(20, sizeof(struct pt_map));
+ if (ta->map == NULL)
+ return (-1);
+ ta->map_len = 20;
+ first = 1;
+ } else {
+ new = realloc(ta->map,
+ sizeof(struct pt_map) * ta->map_len * 2);
+ if (new == NULL)
+ return (-1);
+ memset(new + ta->map_len, '\0', sizeof(struct pt_map) *
+ ta->map_len);
+ first = ta->map_len;
+ ta->map = new;
+ ta->map_len *= 2;
+ }
+ }
+
+ ta->map[first].type = type;
+ ta->map[first].thr = pt;
+ return (first);
+}
+
+static td_err_e
+pt_init(void)
+{
+ pt_md_init();
+ return (0);
+}
+
+static td_err_e
+pt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
+{
+#define LOOKUP_SYM(proc, sym, addr) \
+ ret = ps_pglobal_lookup(proc, NULL, sym, addr); \
+ if (ret != 0) { \
+ TDBG("can not find symbol: %s\n", sym); \
+ ret = TD_NOLIBTHREAD; \
+ goto error; \
+ }
+
+#define LOOKUP_VAL(proc, sym, val) \
+ ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
+ if (ret != 0) { \
+ TDBG("can not find symbol: %s\n", sym); \
+ ret = TD_NOLIBTHREAD; \
+ goto error; \
+ } \
+ ret = ps_pread(proc, vaddr, val, sizeof(int)); \
+ if (ret != 0) { \
+ TDBG("can not read value of %s\n", sym);\
+ ret = TD_NOLIBTHREAD; \
+ goto error; \
+ }
+
+ td_thragent_t *ta;
+ psaddr_t vaddr;
+ int dbg;
+ int ret;
+
+ TDBG_FUNC();
+
+ ta = malloc(sizeof(td_thragent_t));
+ if (ta == NULL)
+ return (TD_MALLOC);
+
+ ta->ph = ph;
+ ta->thread_activated = 0;
+ ta->map = NULL;
+ ta->map_len = 0;
+
+ LOOKUP_SYM(ph, "_libkse_debug", &ta->libkse_debug_addr);
+ LOOKUP_SYM(ph, "_thread_list", &ta->thread_list_addr);
+ LOOKUP_SYM(ph, "_thread_activated", &ta->thread_activated_addr);
+ LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
+ LOOKUP_SYM(ph, "_thread_keytable", &ta->thread_keytable_addr);
+ LOOKUP_VAL(ph, "_thread_off_dtv", &ta->thread_off_dtv);
+ LOOKUP_VAL(ph, "_thread_off_kse_locklevel", &ta->thread_off_kse_locklevel);
+ LOOKUP_VAL(ph, "_thread_off_kse", &ta->thread_off_kse);
+ LOOKUP_VAL(ph, "_thread_off_tlsindex", &ta->thread_off_tlsindex);
+ LOOKUP_VAL(ph, "_thread_off_attr_flags", &ta->thread_off_attr_flags);
+ LOOKUP_VAL(ph, "_thread_size_key", &ta->thread_size_key);
+ LOOKUP_VAL(ph, "_thread_off_tcb", &ta->thread_off_tcb);
+ LOOKUP_VAL(ph, "_thread_off_linkmap", &ta->thread_off_linkmap);
+ LOOKUP_VAL(ph, "_thread_off_tmbx", &ta->thread_off_tmbx);
+ LOOKUP_VAL(ph, "_thread_off_thr_locklevel", &ta->thread_off_thr_locklevel);
+ LOOKUP_VAL(ph, "_thread_off_next", &ta->thread_off_next);
+ LOOKUP_VAL(ph, "_thread_off_state", &ta->thread_off_state);
+ LOOKUP_VAL(ph, "_thread_max_keys", &ta->thread_max_keys);
+ LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
+ LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
+ LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
+ LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
+ LOOKUP_VAL(ph, "_thread_off_sigmask", &ta->thread_off_sigmask);
+ LOOKUP_VAL(ph, "_thread_off_sigpend", &ta->thread_off_sigpend);
+ dbg = getpid();
+ /*
+ * If this fails it probably means we're debugging a core file and
+ * can't write to it.
+ */
+ ps_pwrite(ph, ta->libkse_debug_addr, &dbg, sizeof(int));
+ *pta = ta;
+ return (0);
+
+error:
+ free(ta);
+ return (ret);
+}
+
+static td_err_e
+pt_ta_delete(td_thragent_t *ta)
+{
+ int dbg;
+
+ TDBG_FUNC();
+
+ dbg = 0;
+ /*
+ * Error returns from this write are not really a problem;
+ * the process doesn't exist any more.
+ */
+ ps_pwrite(ta->ph, ta->libkse_debug_addr, &dbg, sizeof(int));
+ if (ta->map)
+ free(ta->map);
+ free(ta);
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
+{
+ prgregset_t gregs;
+ psaddr_t pt, tcb_addr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ if (id < 0 || id >= ta->map_len || ta->map[id].type == PT_NONE)
+ return (TD_NOTHR);
+
+ ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ if (ta->map[id].type == PT_LWP) {
+ /*
+ * if we are referencing a lwp, make sure it was not already
+ * mapped to user thread.
+ */
+ while (pt != 0) {
+ ret = thr_pread_ptr(ta, pt + ta->thread_off_tcb,
+ &tcb_addr);
+ if (ret != 0)
+ return (TD_ERR);
+ ret = thr_pread_int(ta, tcb_addr + ta->thread_off_tmbx +
+ offsetof(struct kse_thr_mailbox, tm_lwp), &lwp);
+ if (ret != 0)
+ return (TD_ERR);
+ /*
+ * If the lwp was already mapped to userland thread,
+ * we shouldn't reference it directly in future.
+ */
+ if (lwp == ta->map[id].lwp) {
+ ta->map[id].type = PT_NONE;
+ return (TD_NOTHR);
+ }
+ /* get next thread */
+ ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ }
+ /* check lwp */
+ ret = ps_lgetregs(ta->ph, ta->map[id].lwp, gregs);
+ if (ret != PS_OK) {
+ /* no longer exists */
+ ta->map[id].type = PT_NONE;
+ return (TD_NOTHR);
+ }
+ } else {
+ while (pt != 0 && ta->map[id].thr != pt) {
+ ret = thr_pread_ptr(ta, pt + ta->thread_off_tcb,
+ &tcb_addr);
+ if (ret != 0)
+ return (TD_ERR);
+ /* get next thread */
+ ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ }
+
+ if (pt == 0) {
+ /* no longer exists */
+ ta->map[id].type = PT_NONE;
+ return (TD_NOTHR);
+ }
+ }
+ th->th_ta = ta;
+ th->th_tid = id;
+ th->th_thread = pt;
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
+{
+ psaddr_t pt, tcb_addr;
+ lwpid_t lwp1;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ while (pt != 0) {
+ ret = thr_pread_ptr(ta, pt + ta->thread_off_tcb, &tcb_addr);
+ if (ret != 0)
+ return (TD_ERR);
+ ret = thr_pread_int(ta, tcb_addr + ta->thread_off_tmbx +
+ offsetof(struct kse_thr_mailbox, tm_lwp), &lwp1);
+ if (ret != 0)
+ return (TD_ERR);
+ if (lwp1 == lwp) {
+ th->th_ta = ta;
+ th->th_tid = pt_map_thread(ta, pt, PT_USER);
+ if (th->th_tid == -1)
+ return (TD_MALLOC);
+ pt_unmap_lwp(ta, lwp);
+ th->th_thread = pt;
+ return (TD_OK);
+ }
+
+ /* get next thread */
+ ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ }
+
+ return (TD_NOTHR);
+}
+
+static td_err_e
+pt_ta_thr_iter(const td_thragent_t *ta, td_thr_iter_f *callback,
+ void *cbdata_p, td_thr_state_e state __unused, int ti_pri __unused,
+ sigset_t *ti_sigmask_p __unused, unsigned int ti_user_flags __unused)
+{
+ td_thrhandle_t th;
+ psaddr_t pt;
+ ps_err_e pserr;
+ int activated, ret;
+
+ TDBG_FUNC();
+
+ pserr = ps_pread(ta->ph, ta->thread_activated_addr, &activated,
+ sizeof(int));
+ if (pserr != PS_OK)
+ return (P2T(pserr));
+ if (!activated)
+ return (TD_OK);
+
+ ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ while (pt != 0) {
+ th.th_ta = ta;
+ th.th_tid = pt_map_thread(ta, pt, PT_USER);
+ th.th_thread = pt;
+ /* should we unmap lwp here ? */
+ if (th.th_tid == -1)
+ return (TD_MALLOC);
+ if ((*callback)(&th, cbdata_p))
+ return (TD_DBERR);
+ /* get next thread */
+ ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ }
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
+{
+ void *keytable;
+ void *destructor;
+ int i, ret, allocated;
+
+ TDBG_FUNC();
+
+ keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
+ if (keytable == NULL)
+ return (TD_MALLOC);
+ ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
+ ta->thread_max_keys * ta->thread_size_key);
+ if (ret != 0) {
+ free(keytable);
+ return (P2T(ret));
+ }
+ for (i = 0; i < ta->thread_max_keys; i++) {
+ allocated = *(int *)(void *)((uintptr_t)keytable +
+ i * ta->thread_size_key + ta->thread_off_key_allocated);
+ destructor = *(void **)(void *)((uintptr_t)keytable +
+ i * ta->thread_size_key + ta->thread_off_key_destructor);
+ if (allocated) {
+ ret = (ki)(i, destructor, arg);
+ if (ret != 0) {
+ free(keytable);
+ return (TD_DBERR);
+ }
+ }
+ }
+ free(keytable);
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_event_addr(const td_thragent_t *ta __unused, td_event_e event __unused,
+ td_notify_t *ptr __unused)
+{
+ TDBG_FUNC();
+ return (TD_ERR);
+}
+
+static td_err_e
+pt_ta_set_event(const td_thragent_t *ta __unused,
+ td_thr_events_t *events __unused)
+{
+ TDBG_FUNC();
+ return (0);
+}
+
+static td_err_e
+pt_ta_clear_event(const td_thragent_t *ta __unused,
+ td_thr_events_t *events __unused)
+{
+ TDBG_FUNC();
+ return (0);
+}
+
+static td_err_e
+pt_ta_event_getmsg(const td_thragent_t *ta __unused,
+ td_event_msg_t *msg __unused)
+{
+ TDBG_FUNC();
+ return (TD_NOMSG);
+}
+
+static td_err_e
+pt_dbsuspend(const td_thrhandle_t *th, int suspend)
+{
+ const td_thragent_t *ta = th->th_ta;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ uint32_t dflags;
+ int attrflags, locklevel, ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_tid].type == PT_LWP) {
+ if (suspend)
+ ret = ps_lstop(ta->ph, ta->map[th->th_tid].lwp);
+ else
+ ret = ps_lcontinue(ta->ph, ta->map[th->th_tid].lwp);
+ return (P2T(ret));
+ }
+
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
+ ta->thread_off_attr_flags,
+ &attrflags, sizeof(attrflags));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
+ ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + ta->thread_off_tmbx;
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+
+ if (lwp != 0) {
+ /* don't suspend signal thread */
+ if (attrflags & 0x200)
+ return (0);
+ if (attrflags & PTHREAD_SCOPE_SYSTEM) {
+ /*
+ * don't suspend system scope thread if it is holding
+ * some low level locks
+ */
+ ptr = ta->map[th->th_tid].thr + ta->thread_off_kse;
+ ret = ps_pread(ta->ph, ptr, &ptr, sizeof(ptr));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pread(ta->ph, ptr + ta->thread_off_kse_locklevel,
+ &locklevel, sizeof(int));
+ if (ret != 0)
+ return (P2T(ret));
+ if (locklevel <= 0) {
+ ptr = ta->map[th->th_tid].thr +
+ ta->thread_off_thr_locklevel;
+ ret = ps_pread(ta->ph, ptr, &locklevel,
+ sizeof(int));
+ if (ret != 0)
+ return (P2T(ret));
+ }
+ if (suspend) {
+ if (locklevel <= 0)
+ ret = ps_lstop(ta->ph, lwp);
+ } else {
+ ret = ps_lcontinue(ta->ph, lwp);
+ }
+ if (ret != 0)
+ return (P2T(ret));
+ /* FALLTHROUGH */
+ } else {
+ struct ptrace_lwpinfo pl;
+
+ if (ps_linfo(ta->ph, lwp, (caddr_t)&pl))
+ return (TD_ERR);
+ if (suspend) {
+ if (!(pl.pl_flags & PL_FLAG_BOUND))
+ ret = ps_lstop(ta->ph, lwp);
+ } else {
+ ret = ps_lcontinue(ta->ph, lwp);
+ }
+ if (ret != 0)
+ return (P2T(ret));
+ /* FALLTHROUGH */
+ }
+ }
+ /* read tm_dflags */
+ ret = ps_pread(ta->ph,
+ tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
+ &dflags, sizeof(dflags));
+ if (ret != 0)
+ return (P2T(ret));
+ if (suspend)
+ dflags |= TMDF_SUSPEND;
+ else
+ dflags &= ~TMDF_SUSPEND;
+ ret = ps_pwrite(ta->ph,
+ tmbx_addr + offsetof(struct kse_thr_mailbox, tm_dflags),
+ &dflags, sizeof(dflags));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_dbresume(const td_thrhandle_t *th)
+{
+ TDBG_FUNC();
+
+ return pt_dbsuspend(th, 0);
+}
+
+static td_err_e
+pt_thr_dbsuspend(const td_thrhandle_t *th)
+{
+ TDBG_FUNC();
+
+ return pt_dbsuspend(th, 1);
+}
+
+static td_err_e
+pt_thr_validate(const td_thrhandle_t *th)
+{
+ td_thrhandle_t temp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_ta_map_id2thr(th->th_ta, th->th_tid,
+ &temp);
+ return (ret);
+}
+
+static td_err_e
+pt_thr_old_get_info(const td_thrhandle_t *th, td_old_thrinfo_t *info)
+{
+ const td_thragent_t *ta = th->th_ta;
+ struct ptrace_lwpinfo linfo;
+ psaddr_t tcb_addr;
+ uint32_t dflags;
+ lwpid_t lwp;
+ int state;
+ int ret;
+ int attrflags;
+
+ TDBG_FUNC();
+
+ bzero(info, sizeof(*info));
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ memset(info, 0, sizeof(*info));
+ if (ta->map[th->th_tid].type == PT_LWP) {
+ info->ti_type = TD_THR_SYSTEM;
+ info->ti_lid = ta->map[th->th_tid].lwp;
+ info->ti_tid = th->th_tid;
+ info->ti_state = TD_THR_RUN;
+ info->ti_type = TD_THR_SYSTEM;
+ return (TD_OK);
+ }
+
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
+ ta->thread_off_attr_flags,
+ &attrflags, sizeof(attrflags));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_state,
+ &state, sizeof(state));
+ ret = ps_pread(ta->ph,
+ tcb_addr + ta->thread_off_tmbx +
+ offsetof(struct kse_thr_mailbox, tm_lwp),
+ &info->ti_lid, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pread(ta->ph,
+ tcb_addr + ta->thread_off_tmbx +
+ offsetof(struct kse_thr_mailbox, tm_dflags),
+ &dflags, sizeof(dflags));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_tmbx +
+ offsetof(struct kse_thr_mailbox, tm_lwp), &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ info->ti_ta_p = th->th_ta;
+ info->ti_tid = th->th_tid;
+
+ if (attrflags & PTHREAD_SCOPE_SYSTEM) {
+ ret = ps_linfo(ta->ph, lwp, &linfo);
+ if (ret == PS_OK) {
+ info->ti_sigmask = linfo.pl_sigmask;
+ info->ti_pending = linfo.pl_siglist;
+ } else
+ return (ret);
+ } else {
+ ret = ps_pread(ta->ph,
+ ta->map[th->th_tid].thr + ta->thread_off_sigmask,
+ &info->ti_sigmask, sizeof(sigset_t));
+ if (ret)
+ return (ret);
+ ret = ps_pread(ta->ph,
+ ta->map[th->th_tid].thr + ta->thread_off_sigpend,
+ &info->ti_pending, sizeof(sigset_t));
+ if (ret)
+ return (ret);
+ }
+
+ if (state == ta->thread_state_running)
+ info->ti_state = TD_THR_RUN;
+ else if (state == ta->thread_state_zoombie)
+ info->ti_state = TD_THR_ZOMBIE;
+ else
+ info->ti_state = TD_THR_SLEEP;
+ info->ti_db_suspended = ((dflags & TMDF_SUSPEND) != 0);
+ info->ti_type = TD_THR_USER;
+ return (0);
+}
+
+static td_err_e
+pt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
+{
+ td_err_e e;
+
+ e = pt_thr_old_get_info(th, (td_old_thrinfo_t *)info);
+ bzero(&info->ti_siginfo, sizeof(info->ti_siginfo));
+ return (e);
+}
+
+#ifdef __i386__
+static td_err_e
+pt_thr_getxmmregs(const td_thrhandle_t *th, char *fxsave)
+{
+ const td_thragent_t *ta = th->th_ta;
+ struct kse_thr_mailbox tmbx;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ return TD_ERR;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_tid].type == PT_LWP) {
+ ret = ps_lgetxmmregs(ta->ph, ta->map[th->th_tid].lwp, fxsave);
+ return (P2T(ret));
+ }
+
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + ta->thread_off_tmbx;
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lgetxmmregs(ta->ph, lwp, fxsave);
+ return (P2T(ret));
+ }
+
+ ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+ pt_ucontext_to_fxsave(&tmbx.tm_context, fxsave);
+ return (0);
+}
+#endif
+
+static td_err_e
+pt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ struct kse_thr_mailbox tmbx;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_tid].type == PT_LWP) {
+ ret = ps_lgetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + ta->thread_off_tmbx;
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lgetfpregs(ta->ph, lwp, fpregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+ pt_ucontext_to_fpreg(&tmbx.tm_context, fpregs);
+ return (0);
+}
+
+static td_err_e
+pt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ struct kse_thr_mailbox tmbx;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_tid].type == PT_LWP) {
+ ret = ps_lgetregs(ta->ph,
+ ta->map[th->th_tid].lwp, gregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr + ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + ta->thread_off_tmbx;
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lgetregs(ta->ph, lwp, gregs);
+ return (P2T(ret));
+ }
+ ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+ pt_ucontext_to_reg(&tmbx.tm_context, gregs);
+ return (0);
+}
+
+#ifdef __i386__
+static td_err_e
+pt_thr_setxmmregs(const td_thrhandle_t *th, const char *fxsave)
+{
+ const td_thragent_t *ta = th->th_ta;
+ struct kse_thr_mailbox tmbx;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ return TD_ERR;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_tid].type == PT_LWP) {
+ ret = ps_lsetxmmregs(ta->ph, ta->map[th->th_tid].lwp, fxsave);
+ return (P2T(ret));
+ }
+
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
+ ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + ta->thread_off_tmbx;
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lsetxmmregs(ta->ph, lwp, fxsave);
+ return (P2T(ret));
+ }
+ /*
+ * Read a copy of context, this makes sure that registers
+ * not covered by structure reg won't be clobbered
+ */
+ ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+
+ pt_fxsave_to_ucontext(fxsave, &tmbx.tm_context);
+ ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ return (P2T(ret));
+}
+#endif
+
+static td_err_e
+pt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ struct kse_thr_mailbox tmbx;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_tid].type == PT_LWP) {
+ ret = ps_lsetfpregs(ta->ph, ta->map[th->th_tid].lwp, fpregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
+ ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + ta->thread_off_tmbx;
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lsetfpregs(ta->ph, lwp, fpregs);
+ return (P2T(ret));
+ }
+ /*
+ * Read a copy of context, this makes sure that registers
+ * not covered by structure reg won't be clobbered
+ */
+ ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+
+ pt_fpreg_to_ucontext(fpregs, &tmbx.tm_context);
+ ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ struct kse_thr_mailbox tmbx;
+ psaddr_t tcb_addr, tmbx_addr, ptr;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_tid].type == PT_LWP) {
+ ret = ps_lsetregs(ta->ph, ta->map[th->th_tid].lwp, gregs);
+ return (P2T(ret));
+ }
+
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
+ ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ tmbx_addr = tcb_addr + ta->thread_off_tmbx;
+ ptr = tmbx_addr + offsetof(struct kse_thr_mailbox, tm_lwp);
+ ret = ps_pread(ta->ph, ptr, &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0) {
+ ret = ps_lsetregs(ta->ph, lwp, gregs);
+ return (P2T(ret));
+ }
+
+ /*
+ * Read a copy of context, make sure that registers
+ * not covered by structure reg won't be clobbered
+ */
+ ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret != 0)
+ return (P2T(ret));
+ pt_reg_to_ucontext(gregs, &tmbx.tm_context);
+ ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_event_enable(const td_thrhandle_t *th __unused, int en __unused)
+{
+ TDBG_FUNC();
+ return (0);
+}
+
+static td_err_e
+pt_thr_set_event(const td_thrhandle_t *th __unused,
+ td_thr_events_t *setp __unused)
+{
+ TDBG_FUNC();
+ return (0);
+}
+
+static td_err_e
+pt_thr_clear_event(const td_thrhandle_t *th __unused,
+ td_thr_events_t *setp __unused)
+{
+ TDBG_FUNC();
+ return (0);
+}
+
+static td_err_e
+pt_thr_event_getmsg(const td_thrhandle_t *th __unused,
+ td_event_msg_t *msg __unused)
+{
+ TDBG_FUNC();
+ return (TD_NOMSG);
+}
+
+static td_err_e
+pt_thr_sstep(const td_thrhandle_t *th, int step)
+{
+ const td_thragent_t *ta = th->th_ta;
+ struct kse_thr_mailbox tmbx;
+ struct reg regs;
+ psaddr_t tcb_addr, tmbx_addr;
+ uint32_t dflags;
+ lwpid_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (ta->map[th->th_tid].type == PT_LWP)
+ return (TD_BADTH);
+
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
+ ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+
+ /* Clear or set single step flag in thread mailbox */
+ ret = ps_pread(ta->ph,
+ tcb_addr + ta->thread_off_tmbx +
+ offsetof(struct kse_thr_mailbox, tm_dflags),
+ &dflags, sizeof(uint32_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (step != 0)
+ dflags |= TMDF_SSTEP;
+ else
+ dflags &= ~TMDF_SSTEP;
+ ret = ps_pwrite(ta->ph,
+ tcb_addr + ta->thread_off_tmbx +
+ offsetof(struct kse_thr_mailbox, tm_dflags),
+ &dflags, sizeof(uint32_t));
+ if (ret != 0)
+ return (P2T(ret));
+ /* Get lwp */
+ ret = ps_pread(ta->ph,
+ tcb_addr + ta->thread_off_tmbx +
+ offsetof(struct kse_thr_mailbox, tm_lwp),
+ &lwp, sizeof(lwpid_t));
+ if (ret != 0)
+ return (P2T(ret));
+ if (lwp != 0)
+ return (0);
+
+ tmbx_addr = tcb_addr + ta->thread_off_tmbx;
+ /*
+ * context is in userland, some architectures store
+ * single step status in registers, we should change
+ * these registers.
+ */
+ ret = ps_pread(ta->ph, tmbx_addr, &tmbx, sizeof(tmbx));
+ if (ret == 0) {
+ pt_ucontext_to_reg(&tmbx.tm_context, &regs);
+ /* only write out if it is really changed. */
+ if (pt_reg_sstep(&regs, step) != 0) {
+ pt_reg_to_ucontext(&regs, &tmbx.tm_context);
+ ret = ps_pwrite(ta->ph, tmbx_addr, &tmbx,
+ sizeof(tmbx));
+ }
+ }
+ return (P2T(ret));
+}
+
+static void
+pt_unmap_lwp(const td_thragent_t *ta, lwpid_t lwp)
+{
+ int i;
+
+ for (i = 0; i < ta->map_len; ++i) {
+ if (ta->map[i].type == PT_LWP && ta->map[i].lwp == lwp) {
+ ta->map[i].type = PT_NONE;
+ return;
+ }
+ }
+}
+
+static int
+pt_validate(const td_thrhandle_t *th)
+{
+
+ if (th->th_tid < 0 || th->th_tid >= th->th_ta->map_len ||
+ th->th_ta->map[th->th_tid].type == PT_NONE)
+ return (TD_NOTHR);
+ return (TD_OK);
+}
+
+static td_err_e
+pt_thr_tls_get_addr(const td_thrhandle_t *th, psaddr_t _linkmap, size_t offset,
+ psaddr_t *address)
+{
+ const td_thragent_t *ta = th->th_ta;
+ psaddr_t dtv_addr, obj_entry, tcb_addr;
+ int tls_index, ret;
+
+ /* linkmap is a member of Obj_Entry */
+ obj_entry = _linkmap - ta->thread_off_linkmap;
+
+ /* get tlsindex of the object file */
+ ret = ps_pread(ta->ph,
+ obj_entry + ta->thread_off_tlsindex,
+ &tls_index, sizeof(tls_index));
+ if (ret != 0)
+ return (P2T(ret));
+
+ /* get thread tcb */
+ ret = ps_pread(ta->ph, ta->map[th->th_tid].thr +
+ ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+
+ /* get dtv array address */
+ ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
+ &dtv_addr, sizeof(dtv_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ /* now get the object's tls block base address */
+ ret = ps_pread(ta->ph, dtv_addr + sizeof(void *) * (tls_index + 1),
+ address, sizeof(*address));
+ if (ret != 0)
+ return (P2T(ret));
+
+ *address += offset;
+ return (TD_OK);
+}
+
+struct ta_ops libpthread_db_ops = {
+ .to_init = pt_init,
+ .to_ta_clear_event = pt_ta_clear_event,
+ .to_ta_delete = pt_ta_delete,
+ .to_ta_event_addr = pt_ta_event_addr,
+ .to_ta_event_getmsg = pt_ta_event_getmsg,
+ .to_ta_map_id2thr = pt_ta_map_id2thr,
+ .to_ta_map_lwp2thr = pt_ta_map_lwp2thr,
+ .to_ta_new = pt_ta_new,
+ .to_ta_set_event = pt_ta_set_event,
+ .to_ta_thr_iter = pt_ta_thr_iter,
+ .to_ta_tsd_iter = pt_ta_tsd_iter,
+ .to_thr_clear_event = pt_thr_clear_event,
+ .to_thr_dbresume = pt_thr_dbresume,
+ .to_thr_dbsuspend = pt_thr_dbsuspend,
+ .to_thr_event_enable = pt_thr_event_enable,
+ .to_thr_event_getmsg = pt_thr_event_getmsg,
+ .to_thr_old_get_info = pt_thr_old_get_info,
+ .to_thr_get_info = pt_thr_get_info,
+ .to_thr_getfpregs = pt_thr_getfpregs,
+ .to_thr_getgregs = pt_thr_getgregs,
+ .to_thr_set_event = pt_thr_set_event,
+ .to_thr_setfpregs = pt_thr_setfpregs,
+ .to_thr_setgregs = pt_thr_setgregs,
+ .to_thr_validate = pt_thr_validate,
+ .to_thr_tls_get_addr = pt_thr_tls_get_addr,
+
+ /* FreeBSD specific extensions. */
+ .to_thr_sstep = pt_thr_sstep,
+#ifdef __i386__
+ .to_thr_getxmmregs = pt_thr_getxmmregs,
+ .to_thr_setxmmregs = pt_thr_setxmmregs,
+#endif
+};
+
+DATA_SET(__ta_ops, libpthread_db_ops);
diff --git a/lib/libthread_db/libpthread_db.h b/lib/libthread_db/libpthread_db.h
new file mode 100644
index 0000000..44c5de6
--- /dev/null
+++ b/lib/libthread_db/libpthread_db.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _LIBPTHREAD_DB_H_
+#define _LIBPTHREAD_DB_H_
+
+#include <sys/ucontext.h>
+#include <machine/reg.h>
+
+#include "thread_db_int.h"
+
+enum pt_type {
+ PT_NONE,
+ PT_USER,
+ PT_LWP
+};
+
+struct pt_map {
+ enum pt_type type;
+ union {
+ lwpid_t lwp;
+ psaddr_t thr;
+ };
+};
+
+struct td_thragent {
+ TD_THRAGENT_FIELDS;
+ psaddr_t libkse_debug_addr;
+ psaddr_t thread_list_addr;
+ psaddr_t thread_listgen_addr;
+ psaddr_t thread_activated_addr;
+ psaddr_t thread_active_threads_addr;
+ psaddr_t thread_keytable_addr;
+ int thread_activated;
+ int thread_off_dtv;
+ int thread_off_kse_locklevel;
+ int thread_off_kse;
+ int thread_off_tlsindex;
+ int thread_off_attr_flags;
+ int thread_size_key;
+ int thread_off_tcb;
+ int thread_off_linkmap;
+ int thread_off_tmbx;
+ int thread_off_thr_locklevel;
+ int thread_off_next;
+ int thread_off_state;
+ int thread_max_keys;
+ int thread_off_key_allocated;
+ int thread_off_key_destructor;
+ int thread_state_zoombie;
+ int thread_state_running;
+ int thread_off_sigmask;
+ int thread_off_sigpend;
+ struct pt_map *map;
+ int map_len;
+};
+
+void pt_md_init(void);
+void pt_reg_to_ucontext(const struct reg *, ucontext_t *);
+void pt_ucontext_to_reg(const ucontext_t *, struct reg *);
+void pt_fpreg_to_ucontext(const struct fpreg *, ucontext_t *);
+void pt_ucontext_to_fpreg(const ucontext_t *, struct fpreg *);
+#ifdef __i386__
+void pt_fxsave_to_ucontext(const char *, ucontext_t *);
+void pt_ucontext_to_fxsave(const ucontext_t *, char *);
+#endif
+int pt_reg_sstep(struct reg *reg, int step);
+
+#endif /* _LIBPTHREAD_DB_H_ */
diff --git a/lib/libthread_db/libthr_db.c b/lib/libthread_db/libthr_db.c
new file mode 100644
index 0000000..b24385f
--- /dev/null
+++ b/lib/libthread_db/libthr_db.c
@@ -0,0 +1,803 @@
+/*
+ * Copyright (c) 2004 Marcel Moolenaar
+ * Copyright (c) 2005 David Xu
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <proc_service.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/linker_set.h>
+#include <sys/ptrace.h>
+#include <thread_db.h>
+#include <unistd.h>
+
+#include "thread_db_int.h"
+
+#define TERMINATED 1
+
+struct td_thragent {
+ TD_THRAGENT_FIELDS;
+ psaddr_t libthr_debug_addr;
+ psaddr_t thread_list_addr;
+ psaddr_t thread_active_threads_addr;
+ psaddr_t thread_keytable_addr;
+ psaddr_t thread_last_event_addr;
+ psaddr_t thread_event_mask_addr;
+ psaddr_t thread_bp_create_addr;
+ psaddr_t thread_bp_death_addr;
+ int thread_off_dtv;
+ int thread_off_tlsindex;
+ int thread_off_attr_flags;
+ int thread_size_key;
+ int thread_off_tcb;
+ int thread_off_linkmap;
+ int thread_off_next;
+ int thread_off_state;
+ int thread_off_tid;
+ int thread_max_keys;
+ int thread_off_key_allocated;
+ int thread_off_key_destructor;
+ int thread_off_report_events;
+ int thread_off_event_mask;
+ int thread_off_event_buf;
+ int thread_state_zoombie;
+ int thread_state_running;
+};
+
+#define P2T(c) ps2td(c)
+
+static int pt_validate(const td_thrhandle_t *th);
+
+static int
+ps2td(int c)
+{
+ switch (c) {
+ case PS_OK:
+ return TD_OK;
+ case PS_ERR:
+ return TD_ERR;
+ case PS_BADPID:
+ return TD_BADPH;
+ case PS_BADLID:
+ return TD_NOLWP;
+ case PS_BADADDR:
+ return TD_ERR;
+ case PS_NOSYM:
+ return TD_NOLIBTHREAD;
+ case PS_NOFREGS:
+ return TD_NOFPREGS;
+ default:
+ return TD_ERR;
+ }
+}
+
+static td_err_e
+pt_init(void)
+{
+ return (0);
+}
+
+static td_err_e
+pt_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
+{
+#define LOOKUP_SYM(proc, sym, addr) \
+ ret = ps_pglobal_lookup(proc, NULL, sym, addr); \
+ if (ret != 0) { \
+ TDBG("can not find symbol: %s\n", sym); \
+ ret = TD_NOLIBTHREAD; \
+ goto error; \
+ }
+
+#define LOOKUP_VAL(proc, sym, val) \
+ ret = ps_pglobal_lookup(proc, NULL, sym, &vaddr);\
+ if (ret != 0) { \
+ TDBG("can not find symbol: %s\n", sym); \
+ ret = TD_NOLIBTHREAD; \
+ goto error; \
+ } \
+ ret = ps_pread(proc, vaddr, val, sizeof(int)); \
+ if (ret != 0) { \
+ TDBG("can not read value of %s\n", sym);\
+ ret = TD_NOLIBTHREAD; \
+ goto error; \
+ }
+
+ td_thragent_t *ta;
+ psaddr_t vaddr;
+ int dbg;
+ int ret;
+
+ TDBG_FUNC();
+
+ ta = malloc(sizeof(td_thragent_t));
+ if (ta == NULL)
+ return (TD_MALLOC);
+
+ ta->ph = ph;
+
+ LOOKUP_SYM(ph, "_libthr_debug", &ta->libthr_debug_addr);
+ LOOKUP_SYM(ph, "_thread_list", &ta->thread_list_addr);
+ LOOKUP_SYM(ph, "_thread_active_threads",&ta->thread_active_threads_addr);
+ LOOKUP_SYM(ph, "_thread_keytable", &ta->thread_keytable_addr);
+ LOOKUP_SYM(ph, "_thread_last_event", &ta->thread_last_event_addr);
+ LOOKUP_SYM(ph, "_thread_event_mask", &ta->thread_event_mask_addr);
+ LOOKUP_SYM(ph, "_thread_bp_create", &ta->thread_bp_create_addr);
+ LOOKUP_SYM(ph, "_thread_bp_death", &ta->thread_bp_death_addr);
+ LOOKUP_VAL(ph, "_thread_off_dtv", &ta->thread_off_dtv);
+ LOOKUP_VAL(ph, "_thread_off_tlsindex", &ta->thread_off_tlsindex);
+ LOOKUP_VAL(ph, "_thread_off_attr_flags", &ta->thread_off_attr_flags);
+ LOOKUP_VAL(ph, "_thread_size_key", &ta->thread_size_key);
+ LOOKUP_VAL(ph, "_thread_off_tcb", &ta->thread_off_tcb);
+ LOOKUP_VAL(ph, "_thread_off_tid", &ta->thread_off_tid);
+ LOOKUP_VAL(ph, "_thread_off_linkmap", &ta->thread_off_linkmap);
+ LOOKUP_VAL(ph, "_thread_off_next", &ta->thread_off_next);
+ LOOKUP_VAL(ph, "_thread_off_state", &ta->thread_off_state);
+ LOOKUP_VAL(ph, "_thread_max_keys", &ta->thread_max_keys);
+ LOOKUP_VAL(ph, "_thread_off_key_allocated", &ta->thread_off_key_allocated);
+ LOOKUP_VAL(ph, "_thread_off_key_destructor", &ta->thread_off_key_destructor);
+ LOOKUP_VAL(ph, "_thread_state_running", &ta->thread_state_running);
+ LOOKUP_VAL(ph, "_thread_state_zoombie", &ta->thread_state_zoombie);
+ LOOKUP_VAL(ph, "_thread_off_report_events", &ta->thread_off_report_events);
+ LOOKUP_VAL(ph, "_thread_off_event_mask", &ta->thread_off_event_mask);
+ LOOKUP_VAL(ph, "_thread_off_event_buf", &ta->thread_off_event_buf);
+ dbg = getpid();
+ /*
+ * If this fails it probably means we're debugging a core file and
+ * can't write to it.
+ */
+ ps_pwrite(ph, ta->libthr_debug_addr, &dbg, sizeof(int));
+ *pta = ta;
+ return (0);
+
+error:
+ free(ta);
+ return (ret);
+}
+
+static td_err_e
+pt_ta_delete(td_thragent_t *ta)
+{
+ int dbg;
+
+ TDBG_FUNC();
+
+ dbg = 0;
+ /*
+ * Error returns from this write are not really a problem;
+ * the process doesn't exist any more.
+ */
+ ps_pwrite(ta->ph, ta->libthr_debug_addr, &dbg, sizeof(int));
+ free(ta);
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
+{
+ psaddr_t pt;
+ int64_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ if (id == 0)
+ return (TD_NOTHR);
+ ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ /* Iterate through thread list to find pthread */
+ while (pt != 0) {
+ ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
+ if (ret != 0)
+ return (TD_ERR);
+ if (lwp == id)
+ break;
+ /* get next thread */
+ ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ }
+ if (pt == 0)
+ return (TD_NOTHR);
+ th->th_ta = ta;
+ th->th_tid = id;
+ th->th_thread = pt;
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwp, td_thrhandle_t *th)
+{
+ return (pt_ta_map_id2thr(ta, lwp, th));
+}
+
+static td_err_e
+pt_ta_thr_iter(const td_thragent_t *ta, td_thr_iter_f *callback,
+ void *cbdata_p, td_thr_state_e state __unused, int ti_pri __unused,
+ sigset_t *ti_sigmask_p __unused, unsigned int ti_user_flags __unused)
+{
+ td_thrhandle_t th;
+ psaddr_t pt;
+ int64_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = thr_pread_ptr(ta, ta->thread_list_addr, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ while (pt != 0) {
+ ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
+ if (ret != 0)
+ return (TD_ERR);
+ if (lwp != 0 && lwp != TERMINATED) {
+ th.th_ta = ta;
+ th.th_tid = (thread_t)lwp;
+ th.th_thread = pt;
+ if ((*callback)(&th, cbdata_p))
+ return (TD_DBERR);
+ }
+ /* get next thread */
+ ret = thr_pread_ptr(ta, pt + ta->thread_off_next, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ }
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg)
+{
+ void *keytable;
+ void *destructor;
+ int i, ret, allocated;
+
+ TDBG_FUNC();
+
+ keytable = malloc(ta->thread_max_keys * ta->thread_size_key);
+ if (keytable == NULL)
+ return (TD_MALLOC);
+ ret = ps_pread(ta->ph, (psaddr_t)ta->thread_keytable_addr, keytable,
+ ta->thread_max_keys * ta->thread_size_key);
+ if (ret != 0) {
+ free(keytable);
+ return (P2T(ret));
+ }
+ for (i = 0; i < ta->thread_max_keys; i++) {
+ allocated = *(int *)(void *)((uintptr_t)keytable +
+ i * ta->thread_size_key + ta->thread_off_key_allocated);
+ destructor = *(void **)(void *)((uintptr_t)keytable +
+ i * ta->thread_size_key + ta->thread_off_key_destructor);
+ if (allocated) {
+ ret = (ki)(i, destructor, arg);
+ if (ret != 0) {
+ free(keytable);
+ return (TD_DBERR);
+ }
+ }
+ }
+ free(keytable);
+ return (TD_OK);
+}
+
+static td_err_e
+pt_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
+{
+
+ TDBG_FUNC();
+
+ switch (event) {
+ case TD_CREATE:
+ ptr->type = NOTIFY_BPT;
+ ptr->u.bptaddr = ta->thread_bp_create_addr;
+ return (0);
+ case TD_DEATH:
+ ptr->type = NOTIFY_BPT;
+ ptr->u.bptaddr = ta->thread_bp_death_addr;
+ return (0);
+ default:
+ return (TD_ERR);
+ }
+}
+
+static td_err_e
+pt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
+{
+ td_thr_events_t mask;
+ int ret;
+
+ TDBG_FUNC();
+ ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
+ sizeof(mask));
+ if (ret != 0)
+ return (P2T(ret));
+ mask |= *events;
+ ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
+ sizeof(mask));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
+{
+ td_thr_events_t mask;
+ int ret;
+
+ TDBG_FUNC();
+ ret = ps_pread(ta->ph, ta->thread_event_mask_addr, &mask,
+ sizeof(mask));
+ if (ret != 0)
+ return (P2T(ret));
+ mask &= ~*events;
+ ret = ps_pwrite(ta->ph, ta->thread_event_mask_addr, &mask,
+ sizeof(mask));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
+{
+ static td_thrhandle_t handle;
+
+ psaddr_t pt;
+ td_thr_events_e tmp;
+ int64_t lwp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = thr_pread_ptr(ta, ta->thread_last_event_addr, &pt);
+ if (ret != 0)
+ return (TD_ERR);
+ if (pt == 0)
+ return (TD_NOMSG);
+ /*
+ * Take the event pointer, at the time, libthr only reports event
+ * once a time, so it is not a link list.
+ */
+ thr_pwrite_ptr(ta, ta->thread_last_event_addr, 0);
+
+ /* Read event info */
+ ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
+ if (ret != 0)
+ return (P2T(ret));
+ if (msg->event == 0)
+ return (TD_NOMSG);
+ /* Clear event */
+ tmp = 0;
+ ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
+ /* Convert event */
+ pt = msg->th_p;
+ ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
+ if (ret != 0)
+ return (TD_ERR);
+ handle.th_ta = ta;
+ handle.th_tid = lwp;
+ handle.th_thread = pt;
+ msg->th_p = (uintptr_t)&handle;
+ return (0);
+}
+
+static td_err_e
+pt_dbsuspend(const td_thrhandle_t *th, int suspend)
+{
+ const td_thragent_t *ta = th->th_ta;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ if (suspend)
+ ret = ps_lstop(ta->ph, th->th_tid);
+ else
+ ret = ps_lcontinue(ta->ph, th->th_tid);
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_dbresume(const td_thrhandle_t *th)
+{
+ TDBG_FUNC();
+
+ return pt_dbsuspend(th, 0);
+}
+
+static td_err_e
+pt_thr_dbsuspend(const td_thrhandle_t *th)
+{
+ TDBG_FUNC();
+
+ return pt_dbsuspend(th, 1);
+}
+
+static td_err_e
+pt_thr_validate(const td_thrhandle_t *th)
+{
+ td_thrhandle_t temp;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_ta_map_id2thr(th->th_ta, th->th_tid, &temp);
+ return (ret);
+}
+
+static td_err_e
+pt_thr_get_info_common(const td_thrhandle_t *th, td_thrinfo_t *info, int old)
+{
+ const td_thragent_t *ta = th->th_ta;
+ struct ptrace_lwpinfo linfo;
+ int traceme;
+ int state;
+ int ret;
+
+ TDBG_FUNC();
+
+ bzero(info, sizeof(*info));
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+ ret = thr_pread_int(ta, th->th_thread + ta->thread_off_state, &state);
+ if (ret != 0)
+ return (TD_ERR);
+ ret = thr_pread_int(ta, th->th_thread + ta->thread_off_report_events,
+ &traceme);
+ info->ti_traceme = traceme;
+ if (ret != 0)
+ return (TD_ERR);
+ ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
+ &info->ti_events, sizeof(td_thr_events_t));
+ if (ret != 0)
+ return (P2T(ret));
+ ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
+ &info->ti_tls, sizeof(void *));
+ info->ti_lid = th->th_tid;
+ info->ti_tid = th->th_tid;
+ info->ti_thread = th->th_thread;
+ info->ti_ta_p = th->th_ta;
+ ret = ps_linfo(ta->ph, th->th_tid, &linfo);
+ if (ret == PS_OK) {
+ info->ti_sigmask = linfo.pl_sigmask;
+ info->ti_pending = linfo.pl_siglist;
+ if (!old) {
+ if ((linfo.pl_flags & PL_FLAG_SI) != 0)
+ info->ti_siginfo = linfo.pl_siginfo;
+ else
+ bzero(&info->ti_siginfo,
+ sizeof(info->ti_siginfo));
+ }
+ } else
+ return (ret);
+ if (state == ta->thread_state_running)
+ info->ti_state = TD_THR_RUN;
+ else if (state == ta->thread_state_zoombie)
+ info->ti_state = TD_THR_ZOMBIE;
+ else
+ info->ti_state = TD_THR_SLEEP;
+ info->ti_type = TD_THR_USER;
+ return (0);
+}
+
+static td_err_e
+pt_thr_old_get_info(const td_thrhandle_t *th, td_old_thrinfo_t *info)
+{
+
+ return (pt_thr_get_info_common(th, (td_thrinfo_t *)info, 1));
+}
+
+static td_err_e
+pt_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
+{
+
+ return (pt_thr_get_info_common(th, info, 0));
+}
+
+#ifdef __i386__
+static td_err_e
+pt_thr_getxmmregs(const td_thrhandle_t *th, char *fxsave)
+{
+ const td_thragent_t *ta = th->th_ta;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ ret = ps_lgetxmmregs(ta->ph, th->th_tid, fxsave);
+ return (P2T(ret));
+}
+#endif
+
+static td_err_e
+pt_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ ret = ps_lgetfpregs(ta->ph, th->th_tid, fpregs);
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ ret = ps_lgetregs(ta->ph, th->th_tid, gregs);
+ return (P2T(ret));
+}
+
+#ifdef __i386__
+static td_err_e
+pt_thr_setxmmregs(const td_thrhandle_t *th, const char *fxsave)
+{
+ const td_thragent_t *ta = th->th_ta;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ ret = ps_lsetxmmregs(ta->ph, th->th_tid, fxsave);
+ return (P2T(ret));
+}
+#endif
+
+static td_err_e
+pt_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ ret = ps_lsetfpregs(ta->ph, th->th_tid, fpregs);
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ int ret;
+
+ TDBG_FUNC();
+
+ ret = pt_validate(th);
+ if (ret)
+ return (ret);
+
+ ret = ps_lsetregs(ta->ph, th->th_tid, gregs);
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_event_enable(const td_thrhandle_t *th, int en)
+{
+ const td_thragent_t *ta = th->th_ta;
+ int ret;
+
+ TDBG_FUNC();
+ ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_report_events,
+ &en, sizeof(int));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp)
+{
+ const td_thragent_t *ta = th->th_ta;
+ td_thr_events_t mask;
+ int ret;
+
+ TDBG_FUNC();
+ ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
+ &mask, sizeof(mask));
+ mask |= *setp;
+ ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
+ &mask, sizeof(mask));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp)
+{
+ const td_thragent_t *ta = th->th_ta;
+ td_thr_events_t mask;
+ int ret;
+
+ TDBG_FUNC();
+ ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_event_mask,
+ &mask, sizeof(mask));
+ mask &= ~*setp;
+ ret = ps_pwrite(ta->ph, th->th_thread + ta->thread_off_event_mask,
+ &mask, sizeof(mask));
+ return (P2T(ret));
+}
+
+static td_err_e
+pt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
+{
+ static td_thrhandle_t handle;
+ const td_thragent_t *ta = th->th_ta;
+ psaddr_t pt, pt_temp;
+ int64_t lwp;
+ int ret;
+ td_thr_events_e tmp;
+
+ TDBG_FUNC();
+ pt = th->th_thread;
+ ret = thr_pread_ptr(ta, ta->thread_last_event_addr, &pt_temp);
+ if (ret != 0)
+ return (TD_ERR);
+ /* Get event */
+ ret = ps_pread(ta->ph, pt + ta->thread_off_event_buf, msg, sizeof(*msg));
+ if (ret != 0)
+ return (P2T(ret));
+ if (msg->event == 0)
+ return (TD_NOMSG);
+ /*
+ * Take the event pointer, at the time, libthr only reports event
+ * once a time, so it is not a link list.
+ */
+ if (pt == pt_temp)
+ thr_pwrite_ptr(ta, ta->thread_last_event_addr, 0);
+
+ /* Clear event */
+ tmp = 0;
+ ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp));
+ /* Convert event */
+ pt = msg->th_p;
+ ret = thr_pread_long(ta, pt + ta->thread_off_tid, &lwp);
+ if (ret != 0)
+ return (TD_ERR);
+ handle.th_ta = ta;
+ handle.th_tid = lwp;
+ handle.th_thread = pt;
+ msg->th_p = (uintptr_t)&handle;
+ return (0);
+}
+
+static td_err_e
+pt_thr_sstep(const td_thrhandle_t *th, int step __unused)
+{
+ TDBG_FUNC();
+
+ return pt_validate(th);
+}
+
+static int
+pt_validate(const td_thrhandle_t *th)
+{
+
+ if (th->th_tid == 0 || th->th_thread == 0)
+ return (TD_ERR);
+ return (TD_OK);
+}
+
+static td_err_e
+pt_thr_tls_get_addr(const td_thrhandle_t *th, psaddr_t _linkmap, size_t offset,
+ psaddr_t *address)
+{
+ const td_thragent_t *ta = th->th_ta;
+ psaddr_t dtv_addr, obj_entry, tcb_addr;
+ int tls_index, ret;
+
+ /* linkmap is a member of Obj_Entry */
+ obj_entry = _linkmap - ta->thread_off_linkmap;
+
+ /* get tlsindex of the object file */
+ ret = ps_pread(ta->ph,
+ obj_entry + ta->thread_off_tlsindex,
+ &tls_index, sizeof(tls_index));
+ if (ret != 0)
+ return (P2T(ret));
+
+ /* get thread tcb */
+ ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_tcb,
+ &tcb_addr, sizeof(tcb_addr));
+ if (ret != 0)
+ return (P2T(ret));
+
+ /* get dtv array address */
+ ret = ps_pread(ta->ph, tcb_addr + ta->thread_off_dtv,
+ &dtv_addr, sizeof(dtv_addr));
+ if (ret != 0)
+ return (P2T(ret));
+ /* now get the object's tls block base address */
+ ret = ps_pread(ta->ph, dtv_addr + sizeof(void *) * (tls_index+1),
+ address, sizeof(*address));
+ if (ret != 0)
+ return (P2T(ret));
+
+ *address += offset;
+ return (TD_OK);
+}
+
+struct ta_ops libthr_db_ops = {
+ .to_init = pt_init,
+ .to_ta_clear_event = pt_ta_clear_event,
+ .to_ta_delete = pt_ta_delete,
+ .to_ta_event_addr = pt_ta_event_addr,
+ .to_ta_event_getmsg = pt_ta_event_getmsg,
+ .to_ta_map_id2thr = pt_ta_map_id2thr,
+ .to_ta_map_lwp2thr = pt_ta_map_lwp2thr,
+ .to_ta_new = pt_ta_new,
+ .to_ta_set_event = pt_ta_set_event,
+ .to_ta_thr_iter = pt_ta_thr_iter,
+ .to_ta_tsd_iter = pt_ta_tsd_iter,
+ .to_thr_clear_event = pt_thr_clear_event,
+ .to_thr_dbresume = pt_thr_dbresume,
+ .to_thr_dbsuspend = pt_thr_dbsuspend,
+ .to_thr_event_enable = pt_thr_event_enable,
+ .to_thr_event_getmsg = pt_thr_event_getmsg,
+ .to_thr_old_get_info = pt_thr_old_get_info,
+ .to_thr_get_info = pt_thr_get_info,
+ .to_thr_getfpregs = pt_thr_getfpregs,
+ .to_thr_getgregs = pt_thr_getgregs,
+ .to_thr_set_event = pt_thr_set_event,
+ .to_thr_setfpregs = pt_thr_setfpregs,
+ .to_thr_setgregs = pt_thr_setgregs,
+ .to_thr_validate = pt_thr_validate,
+ .to_thr_tls_get_addr = pt_thr_tls_get_addr,
+
+ /* FreeBSD specific extensions. */
+ .to_thr_sstep = pt_thr_sstep,
+#ifdef __i386__
+ .to_thr_getxmmregs = pt_thr_getxmmregs,
+ .to_thr_setxmmregs = pt_thr_setxmmregs,
+#endif
+};
+
+DATA_SET(__ta_ops, libthr_db_ops);
diff --git a/lib/libthread_db/thread_db.c b/lib/libthread_db/thread_db.c
new file mode 100644
index 0000000..121855b
--- /dev/null
+++ b/lib/libthread_db/thread_db.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <proc_service.h>
+#include <stddef.h>
+#include <thread_db.h>
+#include <unistd.h>
+#include <sys/cdefs.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/linker_set.h>
+
+#include "thread_db_int.h"
+
+struct td_thragent
+{
+ TD_THRAGENT_FIELDS;
+};
+
+static TAILQ_HEAD(, td_thragent) proclist = TAILQ_HEAD_INITIALIZER(proclist);
+
+SET_DECLARE(__ta_ops, struct ta_ops);
+
+td_err_e
+td_init(void)
+{
+ td_err_e ret, tmp;
+ struct ta_ops *ops_p, **ops_pp;
+
+ ret = 0;
+ SET_FOREACH(ops_pp, __ta_ops) {
+ ops_p = *ops_pp;
+ if (ops_p->to_init != NULL) {
+ tmp = ops_p->to_init();
+ if (tmp != TD_OK)
+ ret = tmp;
+ }
+ }
+ return (ret);
+}
+
+td_err_e
+td_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events)
+{
+ return (ta->ta_ops->to_ta_clear_event(ta, events));
+}
+
+td_err_e
+td_ta_delete(td_thragent_t *ta)
+{
+ TAILQ_REMOVE(&proclist, ta, ta_next);
+ return (ta->ta_ops->to_ta_delete(ta));
+}
+
+td_err_e
+td_ta_event_addr(const td_thragent_t *ta, td_event_e event, td_notify_t *ptr)
+{
+ return (ta->ta_ops->to_ta_event_addr(ta, event, ptr));
+}
+
+td_err_e
+td_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg)
+{
+ return (ta->ta_ops->to_ta_event_getmsg(ta, msg));
+}
+
+td_err_e
+td_ta_map_id2thr(const td_thragent_t *ta, thread_t id, td_thrhandle_t *th)
+{
+ return (ta->ta_ops->to_ta_map_id2thr(ta, id, th));
+}
+
+td_err_e
+td_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwpid, td_thrhandle_t *th)
+{
+ return (ta->ta_ops->to_ta_map_lwp2thr(ta, lwpid, th));
+}
+
+td_err_e
+td_ta_new(struct ps_prochandle *ph, td_thragent_t **pta)
+{
+ struct ta_ops *ops_p, **ops_pp;
+
+ SET_FOREACH(ops_pp, __ta_ops) {
+ ops_p = *ops_pp;
+ if (ops_p->to_ta_new(ph, pta) == TD_OK) {
+ TAILQ_INSERT_HEAD(&proclist, *pta, ta_next);
+ (*pta)->ta_ops = ops_p;
+ return (TD_OK);
+ }
+ }
+ return (TD_NOLIBTHREAD);
+}
+
+td_err_e
+td_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events)
+{
+ return (ta->ta_ops->to_ta_set_event(ta, events));
+}
+
+td_err_e
+td_ta_thr_iter(const td_thragent_t *ta, td_thr_iter_f *callback,
+ void *cbdata_p, td_thr_state_e state, int ti_pri, sigset_t *ti_sigmask_p,
+ unsigned int ti_user_flags)
+{
+ return (ta->ta_ops->to_ta_thr_iter(ta, callback, cbdata_p, state,
+ ti_pri, ti_sigmask_p, ti_user_flags));
+}
+
+td_err_e
+td_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *callback,
+ void *cbdata_p)
+{
+ return (ta->ta_ops->to_ta_tsd_iter(ta, callback, cbdata_p));
+}
+
+td_err_e
+td_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *events)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_clear_event(th, events));
+}
+
+td_err_e
+td_thr_dbresume(const td_thrhandle_t *th)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_dbresume(th));
+}
+
+td_err_e
+td_thr_dbsuspend(const td_thrhandle_t *th)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_dbsuspend(th));
+}
+
+td_err_e
+td_thr_event_enable(const td_thrhandle_t *th, int en)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_event_enable(th, en));
+}
+
+td_err_e
+td_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_event_getmsg(th, msg));
+}
+
+td_err_e
+td_thr_old_get_info(const td_thrhandle_t *th, td_old_thrinfo_t *info)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_old_get_info(th, info));
+}
+__sym_compat(td_thr_get_info, td_thr_old_get_info, FBSD_1.0);
+
+td_err_e
+td_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *info)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_get_info(th, info));
+}
+
+#ifdef __i386__
+td_err_e
+td_thr_getxmmregs(const td_thrhandle_t *th, char *fxsave)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_getxmmregs(th, fxsave));
+}
+#endif
+
+
+td_err_e
+td_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *fpregset)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_getfpregs(th, fpregset));
+}
+
+td_err_e
+td_thr_getgregs(const td_thrhandle_t *th, prgregset_t gregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_getgregs(th, gregs));
+}
+
+td_err_e
+td_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *events)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_set_event(th, events));
+}
+
+#ifdef __i386__
+td_err_e
+td_thr_setxmmregs(const td_thrhandle_t *th, const char *fxsave)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_setxmmregs(th, fxsave));
+}
+#endif
+
+td_err_e
+td_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *fpregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_setfpregs(th, fpregs));
+}
+
+td_err_e
+td_thr_setgregs(const td_thrhandle_t *th, const prgregset_t gregs)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_setgregs(th, gregs));
+}
+
+td_err_e
+td_thr_validate(const td_thrhandle_t *th)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_validate(th));
+}
+
+td_err_e
+td_thr_tls_get_addr(const td_thrhandle_t *th, psaddr_t linkmap, size_t offset,
+ psaddr_t *address)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_tls_get_addr(th, linkmap, offset, address));
+}
+
+/* FreeBSD specific extensions. */
+
+td_err_e
+td_thr_sstep(const td_thrhandle_t *th, int step)
+{
+ const td_thragent_t *ta = th->th_ta;
+ return (ta->ta_ops->to_thr_sstep(th, step));
+}
+
+/*
+ * Support functions for reading from and writing to the target
+ * address space.
+ */
+
+static int
+thr_pread(struct ps_prochandle *ph, psaddr_t addr, uint64_t *val,
+ u_int size, u_int byteorder)
+{
+ uint8_t buf[sizeof(*val)];
+ ps_err_e err;
+
+ if (size > sizeof(buf))
+ return (EOVERFLOW);
+
+ err = ps_pread(ph, addr, buf, size);
+ if (err != PS_OK)
+ return (EFAULT);
+
+ switch (byteorder) {
+ case BIG_ENDIAN:
+ switch (size) {
+ case 1:
+ *val = buf[0];
+ break;
+ case 2:
+ *val = be16dec(buf);
+ break;
+ case 4:
+ *val = be32dec(buf);
+ break;
+ case 8:
+ *val = be64dec(buf);
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+ case LITTLE_ENDIAN:
+ switch (size) {
+ case 1:
+ *val = buf[0];
+ break;
+ case 2:
+ *val = le16dec(buf);
+ break;
+ case 4:
+ *val = le32dec(buf);
+ break;
+ case 8:
+ *val = le64dec(buf);
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ return (0);
+}
+
+int
+thr_pread_int(const struct td_thragent *ta, psaddr_t addr, uint32_t *val)
+{
+ uint64_t tmp;
+ int error;
+
+ error = thr_pread(ta->ph, addr, &tmp, sizeof(int), BYTE_ORDER);
+ if (!error)
+ *val = tmp;
+
+ return (error);
+}
+
+int
+thr_pread_long(const struct td_thragent *ta, psaddr_t addr, uint64_t *val)
+{
+
+ return (thr_pread(ta->ph, addr, val, sizeof(long), BYTE_ORDER));
+}
+
+int
+thr_pread_ptr(const struct td_thragent *ta, psaddr_t addr, psaddr_t *val)
+{
+ uint64_t tmp;
+ int error;
+
+ error = thr_pread(ta->ph, addr, &tmp, sizeof(void *), BYTE_ORDER);
+ if (!error)
+ *val = tmp;
+
+ return (error);
+}
+
+static int
+thr_pwrite(struct ps_prochandle *ph, psaddr_t addr, uint64_t val,
+ u_int size, u_int byteorder)
+{
+ uint8_t buf[sizeof(val)];
+ ps_err_e err;
+
+ if (size > sizeof(buf))
+ return (EOVERFLOW);
+
+ switch (byteorder) {
+ case BIG_ENDIAN:
+ switch (size) {
+ case 1:
+ buf[0] = (uint8_t)val;
+ break;
+ case 2:
+ be16enc(buf, (uint16_t)val);
+ break;
+ case 4:
+ be32enc(buf, (uint32_t)val);
+ break;
+ case 8:
+ be64enc(buf, (uint64_t)val);
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+ case LITTLE_ENDIAN:
+ switch (size) {
+ case 1:
+ buf[0] = (uint8_t)val;
+ break;
+ case 2:
+ le16enc(buf, (uint16_t)val);
+ break;
+ case 4:
+ le32enc(buf, (uint32_t)val);
+ break;
+ case 8:
+ le64enc(buf, (uint64_t)val);
+ break;
+ default:
+ return (EINVAL);
+ }
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ err = ps_pwrite(ph, addr, buf, size);
+ return ((err != PS_OK) ? EFAULT : 0);
+}
+
+int
+thr_pwrite_int(const struct td_thragent *ta, psaddr_t addr, uint32_t val)
+{
+
+ return (thr_pwrite(ta->ph, addr, val, sizeof(int), BYTE_ORDER));
+}
+
+int
+thr_pwrite_long(const struct td_thragent *ta, psaddr_t addr, uint64_t val)
+{
+
+ return (thr_pwrite(ta->ph, addr, val, sizeof(long), BYTE_ORDER));
+}
+
+int
+thr_pwrite_ptr(const struct td_thragent *ta, psaddr_t addr, psaddr_t val)
+{
+
+ return (thr_pwrite(ta->ph, addr, val, sizeof(void *), BYTE_ORDER));
+}
+
diff --git a/lib/libthread_db/thread_db.h b/lib/libthread_db/thread_db.h
new file mode 100644
index 0000000..8c30812
--- /dev/null
+++ b/lib/libthread_db/thread_db.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * Copyright (c) 2004 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _THREAD_DB_H_
+#define _THREAD_DB_H_
+
+#include <sys/procfs.h>
+#include <pthread.h>
+
+typedef enum {
+ TD_ERR = -1, /* Unspecified error. */
+ TD_OK = 0, /* No error. */
+ TD_BADKEY,
+ TD_BADPH,
+ TD_BADSH,
+ TD_BADTA,
+ TD_BADTH,
+ TD_DBERR,
+ TD_MALLOC,
+ TD_NOAPLIC,
+ TD_NOCAPAB,
+ TD_NOEVENT,
+ TD_NOFPREGS,
+ TD_NOLIBTHREAD,
+ TD_NOLWP,
+ TD_NOMSG,
+ TD_NOSV,
+ TD_NOTHR,
+ TD_NOTSD,
+ TD_NOXREGS,
+ TD_PARTIALREG
+} td_err_e;
+
+struct ps_prochandle;
+typedef struct td_thragent td_thragent_t;
+typedef long thread_t; /* Must be an integral type. */
+
+typedef struct {
+ const td_thragent_t *th_ta;
+ psaddr_t th_thread;
+ thread_t th_tid;
+} td_thrhandle_t; /* Used non-opaguely. */
+
+/*
+ * Events.
+ */
+
+typedef enum {
+ TD_EVENT_NONE = 0,
+ TD_CATCHSIG = 0x0001,
+ TD_CONCURRENCY= 0x0002,
+ TD_CREATE = 0x0004,
+ TD_DEATH = 0x0008,
+ TD_IDLE = 0x0010,
+ TD_LOCK_TRY = 0x0020,
+ TD_PREEMPT = 0x0040,
+ TD_PRI_INHERIT= 0x0080,
+ TD_READY = 0x0100,
+ TD_REAP = 0x0200,
+ TD_SLEEP = 0x0400,
+ TD_SWITCHFROM = 0x0800,
+ TD_SWITCHTO = 0x1000,
+ TD_TIMEOUT = 0x2000,
+ TD_ALL_EVENTS = ~0
+} td_thr_events_e;
+
+/* Compatibility with Linux. */
+#define td_event_e td_thr_events_e
+
+typedef struct {
+ td_thr_events_e event;
+ psaddr_t th_p;
+ uintptr_t data;
+} td_event_msg_t;
+
+typedef unsigned int td_thr_events_t;
+
+typedef enum {
+ NOTIFY_BPT, /* User inserted breakpoint. */
+ NOTIFY_AUTOBPT, /* Automatic breakpoint. */
+ NOTIFY_SYSCALL /* Invocation of system call. */
+} td_notify_e;
+
+typedef struct {
+ td_notify_e type;
+ union {
+ psaddr_t bptaddr;
+ int syscallno;
+ } u;
+} td_notify_t;
+
+static __inline void
+td_event_addset(td_thr_events_t *es, td_thr_events_e e)
+{
+ *es |= e;
+}
+
+static __inline void
+td_event_delset(td_thr_events_t *es, td_thr_events_e e)
+{
+ *es &= ~e;
+}
+
+static __inline void
+td_event_emptyset(td_thr_events_t *es)
+{
+ *es = TD_EVENT_NONE;
+}
+
+static __inline void
+td_event_fillset(td_thr_events_t *es)
+{
+ *es = TD_ALL_EVENTS;
+}
+
+static __inline int
+td_eventisempty(td_thr_events_t *es)
+{
+ return ((*es == TD_EVENT_NONE) ? 1 : 0);
+}
+
+static __inline int
+td_eventismember(td_thr_events_t *es, td_thr_events_e e)
+{
+ return ((*es & e) ? 1 : 0);
+}
+
+/*
+ * Thread info.
+ */
+
+typedef enum {
+ TD_THR_UNKNOWN = -1,
+ TD_THR_ANY_STATE = 0,
+ TD_THR_ACTIVE,
+ TD_THR_RUN,
+ TD_THR_SLEEP,
+ TD_THR_STOPPED,
+ TD_THR_STOPPED_ASLEEP,
+ TD_THR_ZOMBIE
+} td_thr_state_e;
+
+typedef enum
+{
+ TD_THR_SYSTEM = 1,
+ TD_THR_USER
+} td_thr_type_e;
+
+typedef pthread_key_t thread_key_t;
+
+typedef struct {
+ const td_thragent_t *ti_ta_p;
+ thread_t ti_tid;
+ psaddr_t ti_thread;
+ td_thr_state_e ti_state;
+ td_thr_type_e ti_type;
+ td_thr_events_t ti_events;
+ int ti_pri;
+ lwpid_t ti_lid;
+ char ti_db_suspended;
+ char ti_traceme;
+ sigset_t ti_sigmask;
+ sigset_t ti_pending;
+ psaddr_t ti_tls;
+ psaddr_t ti_startfunc;
+ psaddr_t ti_stkbase;
+ size_t ti_stksize;
+ siginfo_t ti_siginfo;
+} td_thrinfo_t;
+
+/*
+ * Prototypes.
+ */
+
+typedef int td_key_iter_f(thread_key_t, void (*)(void *), void *);
+typedef int td_thr_iter_f(const td_thrhandle_t *, void *);
+
+/* Flags for `td_ta_thr_iter'. */
+#define TD_THR_ANY_USER_FLAGS 0xffffffff
+#define TD_THR_LOWEST_PRIORITY -20
+#define TD_SIGNO_MASK NULL
+
+__BEGIN_DECLS
+td_err_e td_init(void);
+
+td_err_e td_ta_clear_event(const td_thragent_t *, td_thr_events_t *);
+td_err_e td_ta_delete(td_thragent_t *);
+td_err_e td_ta_event_addr(const td_thragent_t *, td_thr_events_e,
+ td_notify_t *);
+td_err_e td_ta_event_getmsg(const td_thragent_t *, td_event_msg_t *);
+td_err_e td_ta_map_id2thr(const td_thragent_t *, thread_t, td_thrhandle_t *);
+td_err_e td_ta_map_lwp2thr(const td_thragent_t *, lwpid_t, td_thrhandle_t *);
+td_err_e td_ta_new(struct ps_prochandle *, td_thragent_t **);
+td_err_e td_ta_set_event(const td_thragent_t *, td_thr_events_t *);
+td_err_e td_ta_thr_iter(const td_thragent_t *, td_thr_iter_f *, void *,
+ td_thr_state_e, int, sigset_t *, unsigned int);
+td_err_e td_ta_tsd_iter(const td_thragent_t *, td_key_iter_f *, void *);
+
+td_err_e td_thr_clear_event(const td_thrhandle_t *, td_thr_events_t *);
+td_err_e td_thr_dbresume(const td_thrhandle_t *);
+td_err_e td_thr_dbsuspend(const td_thrhandle_t *);
+td_err_e td_thr_event_enable(const td_thrhandle_t *, int);
+td_err_e td_thr_event_getmsg(const td_thrhandle_t *, td_event_msg_t *);
+td_err_e td_thr_get_info(const td_thrhandle_t *, td_thrinfo_t *);
+#ifdef __i386__
+td_err_e td_thr_getxmmregs(const td_thrhandle_t *, char *);
+#endif
+td_err_e td_thr_getfpregs(const td_thrhandle_t *, prfpregset_t *);
+td_err_e td_thr_getgregs(const td_thrhandle_t *, prgregset_t);
+td_err_e td_thr_set_event(const td_thrhandle_t *, td_thr_events_t *);
+#ifdef __i386__
+td_err_e td_thr_setxmmregs(const td_thrhandle_t *, const char *);
+#endif
+td_err_e td_thr_setfpregs(const td_thrhandle_t *, const prfpregset_t *);
+td_err_e td_thr_setgregs(const td_thrhandle_t *, const prgregset_t);
+td_err_e td_thr_validate(const td_thrhandle_t *);
+td_err_e td_thr_tls_get_addr(const td_thrhandle_t *, psaddr_t, size_t,
+ psaddr_t *);
+
+/* FreeBSD specific extensions. */
+td_err_e td_thr_sstep(const td_thrhandle_t *, int);
+__END_DECLS
+
+#endif /* _THREAD_DB_H_ */
diff --git a/lib/libthread_db/thread_db_int.h b/lib/libthread_db/thread_db_int.h
new file mode 100644
index 0000000..92ba6e5
--- /dev/null
+++ b/lib/libthread_db/thread_db_int.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2004 David Xu <davidxu@freebsd.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _THREAD_DB_INT_H_
+#define _THREAD_DB_INT_H_
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+typedef struct {
+ const td_thragent_t *ti_ta_p;
+ thread_t ti_tid;
+ psaddr_t ti_thread;
+ td_thr_state_e ti_state;
+ td_thr_type_e ti_type;
+ td_thr_events_t ti_events;
+ int ti_pri;
+ lwpid_t ti_lid;
+ char ti_db_suspended;
+ char ti_traceme;
+ sigset_t ti_sigmask;
+ sigset_t ti_pending;
+ psaddr_t ti_tls;
+ psaddr_t ti_startfunc;
+ psaddr_t ti_stkbase;
+ size_t ti_stksize;
+} td_old_thrinfo_t;
+
+#define TD_THRAGENT_FIELDS \
+ struct ta_ops *ta_ops; \
+ TAILQ_ENTRY(td_thragent) ta_next; \
+ struct ps_prochandle *ph
+
+struct ta_ops {
+ td_err_e (*to_init)(void);
+
+ td_err_e (*to_ta_clear_event)(const td_thragent_t *,
+ td_thr_events_t *);
+ td_err_e (*to_ta_delete)(td_thragent_t *);
+ td_err_e (*to_ta_event_addr)(const td_thragent_t *, td_thr_events_e,
+ td_notify_t *);
+ td_err_e (*to_ta_event_getmsg)(const td_thragent_t *,
+ td_event_msg_t *);
+ td_err_e (*to_ta_map_id2thr)(const td_thragent_t *, thread_t,
+ td_thrhandle_t *);
+ td_err_e (*to_ta_map_lwp2thr)(const td_thragent_t *, lwpid_t,
+ td_thrhandle_t *);
+ td_err_e (*to_ta_new)(struct ps_prochandle *, td_thragent_t **);
+ td_err_e (*to_ta_set_event)(const td_thragent_t *, td_thr_events_t *);
+ td_err_e (*to_ta_thr_iter)(const td_thragent_t *, td_thr_iter_f *,
+ void *, td_thr_state_e, int, sigset_t *, unsigned int);
+ td_err_e (*to_ta_tsd_iter)(const td_thragent_t *, td_key_iter_f *,
+ void *);
+
+ td_err_e (*to_thr_clear_event)(const td_thrhandle_t *,
+ td_thr_events_t *);
+ td_err_e (*to_thr_dbresume)(const td_thrhandle_t *);
+ td_err_e (*to_thr_dbsuspend)(const td_thrhandle_t *);
+ td_err_e (*to_thr_event_enable)(const td_thrhandle_t *, int);
+ td_err_e (*to_thr_event_getmsg)(const td_thrhandle_t *,
+ td_event_msg_t *);
+ td_err_e (*to_thr_old_get_info)(const td_thrhandle_t *,
+ td_old_thrinfo_t *);
+ td_err_e (*to_thr_get_info)(const td_thrhandle_t *, td_thrinfo_t *);
+ td_err_e (*to_thr_getfpregs)(const td_thrhandle_t *, prfpregset_t *);
+ td_err_e (*to_thr_getgregs)(const td_thrhandle_t *, prgregset_t);
+ td_err_e (*to_thr_set_event)(const td_thrhandle_t *,
+ td_thr_events_t *);
+ td_err_e (*to_thr_setfpregs)(const td_thrhandle_t *,
+ const prfpregset_t *);
+ td_err_e (*to_thr_setgregs)(const td_thrhandle_t *, const prgregset_t);
+ td_err_e (*to_thr_validate)(const td_thrhandle_t *);
+ td_err_e (*to_thr_tls_get_addr)(const td_thrhandle_t *, psaddr_t,
+ size_t, psaddr_t *);
+
+ /* FreeBSD specific extensions. */
+ td_err_e (*to_thr_sstep)(const td_thrhandle_t *, int);
+#if defined(__i386__)
+ td_err_e (*to_thr_getxmmregs)(const td_thrhandle_t *, char *);
+ td_err_e (*to_thr_setxmmregs)(const td_thrhandle_t *, const char *);
+#endif
+};
+
+#ifdef TD_DEBUG
+#define TDBG(...) ps_plog(__VA_ARGS__)
+#define TDBG_FUNC() ps_plog("%s\n", __func__)
+#else
+#define TDBG(...)
+#define TDBG_FUNC()
+#endif
+
+struct td_thragent;
+
+int thr_pread_int(const struct td_thragent *, psaddr_t, uint32_t *);
+int thr_pread_long(const struct td_thragent *, psaddr_t, uint64_t *);
+int thr_pread_ptr(const struct td_thragent *, psaddr_t, psaddr_t *);
+
+int thr_pwrite_int(const struct td_thragent *, psaddr_t, uint32_t);
+int thr_pwrite_long(const struct td_thragent *, psaddr_t, uint64_t);
+int thr_pwrite_ptr(const struct td_thragent *, psaddr_t, psaddr_t);
+
+td_err_e td_thr_old_get_info(const td_thrhandle_t *th, td_old_thrinfo_t *info);
+
+#endif /* _THREAD_DB_INT_H_ */
OpenPOWER on IntegriCloud