diff options
Diffstat (limited to 'lib/libthread_db')
-rw-r--r-- | lib/libthread_db/Makefile | 19 | ||||
-rw-r--r-- | lib/libthread_db/Symbol.map | 35 | ||||
-rw-r--r-- | lib/libthread_db/arch/amd64/libc_r_md.c | 41 | ||||
-rw-r--r-- | lib/libthread_db/arch/amd64/libpthread_md.c | 118 | ||||
-rw-r--r-- | lib/libthread_db/arch/i386/libc_r_md.c | 48 | ||||
-rw-r--r-- | lib/libthread_db/arch/i386/libpthread_md.c | 119 | ||||
-rw-r--r-- | lib/libthread_db/arch/ia64/libc_r_md.c | 41 | ||||
-rw-r--r-- | lib/libthread_db/arch/ia64/libpthread_md.c | 62 | ||||
-rw-r--r-- | lib/libthread_db/arch/powerpc/libc_r_md.c | 41 | ||||
-rw-r--r-- | lib/libthread_db/arch/powerpc/libpthread_md.c | 83 | ||||
-rw-r--r-- | lib/libthread_db/arch/sparc64/libc_r_md.c | 41 | ||||
-rw-r--r-- | lib/libthread_db/arch/sparc64/libpthread_md.c | 62 | ||||
-rw-r--r-- | lib/libthread_db/libc_r_db.c | 348 | ||||
-rw-r--r-- | lib/libthread_db/libpthread_db.c | 1149 | ||||
-rw-r--r-- | lib/libthread_db/libpthread_db.h | 93 | ||||
-rw-r--r-- | lib/libthread_db/libthr_db.c | 793 | ||||
-rw-r--r-- | lib/libthread_db/thread_db.c | 261 | ||||
-rw-r--r-- | lib/libthread_db/thread_db.h | 248 | ||||
-rw-r--r-- | lib/libthread_db/thread_db_int.h | 96 |
19 files changed, 3698 insertions, 0 deletions
diff --git a/lib/libthread_db/Makefile b/lib/libthread_db/Makefile new file mode 100644 index 0000000..d8e1975 --- /dev/null +++ b/lib/libthread_db/Makefile @@ -0,0 +1,19 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/arch/${MACHINE_ARCH} + +LIB= thread_db +SHLIB_MAJOR= 3 +SRCS= thread_db.c +SRCS+= libpthread_db.c libpthread_md.c +SRCS+= libthr_db.c +INCS= thread_db.h +WARNS?= 1 + +CFLAGS+=-I. -I${.CURDIR} +SYM_MAPS+=${.CURDIR}/Symbol.map + +SYMBOL_MAPS=${SYM_MAPS} +VERSION_DEF=${.CURDIR}/../libc/Versions.def + +.include <bsd.lib.mk> diff --git a/lib/libthread_db/Symbol.map b/lib/libthread_db/Symbol.map new file mode 100644 index 0000000..65e78d4 --- /dev/null +++ b/lib/libthread_db/Symbol.map @@ -0,0 +1,35 @@ +/* + * $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_get_info; + 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; +}; diff --git a/lib/libthread_db/arch/amd64/libc_r_md.c b/lib/libthread_db/arch/amd64/libc_r_md.c new file mode 100644 index 0000000..6c53953 --- /dev/null +++ b/lib/libthread_db/arch/amd64/libc_r_md.c @@ -0,0 +1,41 @@ +/* + * 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 <machine/setjmp.h> + +void +libc_r_md_getgregs(jmp_buf jb, prgregset_t *r) +{ +} + +void +libc_r_md_getfpregs(jmp_buf jb, prfpregset_t *r) +{ +} 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..2a635c6 --- /dev/null +++ b/lib/libthread_db/arch/amd64/libpthread_md.c @@ -0,0 +1,118 @@ +/* + * 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 <ucontext.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/i386/libc_r_md.c b/lib/libthread_db/arch/i386/libc_r_md.c new file mode 100644 index 0000000..de27b68 --- /dev/null +++ b/lib/libthread_db/arch/i386/libc_r_md.c @@ -0,0 +1,48 @@ +/* + * 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 <machine/setjmp.h> + +void +libc_r_md_getgregs(jmp_buf jb, prgregset_t r) +{ + r->r_eip = jb->_jb[0]; + r->r_ebx = jb->_jb[1]; + r->r_esp = jb->_jb[2]; + r->r_ebp = jb->_jb[3]; + r->r_esi = jb->_jb[4]; + r->r_edi = jb->_jb[5]; + r->r_eax = jb->_jb[6]; +} + +void +libc_r_md_getfpregs(jmp_buf jb, prfpregset_t *r) +{ +} 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..dc14e60 --- /dev/null +++ b/lib/libthread_db/arch/i386/libpthread_md.c @@ -0,0 +1,119 @@ +/* + * 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 <string.h> +#include <sys/types.h> +#include <proc_service.h> +#include <thread_db.h> +#include <machine/npx.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/libc_r_md.c b/lib/libthread_db/arch/ia64/libc_r_md.c new file mode 100644 index 0000000..6c53953 --- /dev/null +++ b/lib/libthread_db/arch/ia64/libc_r_md.c @@ -0,0 +1,41 @@ +/* + * 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 <machine/setjmp.h> + +void +libc_r_md_getgregs(jmp_buf jb, prgregset_t *r) +{ +} + +void +libc_r_md_getfpregs(jmp_buf jb, prfpregset_t *r) +{ +} 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..7553ed1 --- /dev/null +++ b/lib/libthread_db/arch/ia64/libpthread_md.c @@ -0,0 +1,62 @@ +/* + * 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 <ucontext.h> + +void +pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc) +{ +} + +void +pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r) +{ +} + +void +pt_fpreg_to_ucontext(const struct fpreg* r, ucontext_t *uc) +{ +} + +void +pt_ucontext_to_fpreg(const ucontext_t *uc, struct fpreg *r) +{ +} + +void +pt_md_init(void) +{ +} + +int +pt_reg_sstep(struct reg *reg, int step) +{ + return (0); +} diff --git a/lib/libthread_db/arch/powerpc/libc_r_md.c b/lib/libthread_db/arch/powerpc/libc_r_md.c new file mode 100644 index 0000000..c6139f7 --- /dev/null +++ b/lib/libthread_db/arch/powerpc/libc_r_md.c @@ -0,0 +1,41 @@ +/* + * 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 ``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 <machine/setjmp.h> + +void +libc_r_md_getgregs(jmp_buf jb, struct reg *r) +{ +} + +void +libc_r_md_getfpregs(jmp_buf jb, struct fpreg *r) +{ +} 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..a8a95bd --- /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 <string.h> +#include <sys/types.h> +#include <proc_service.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, int step) +{ + + /* XXX */ +} diff --git a/lib/libthread_db/arch/sparc64/libc_r_md.c b/lib/libthread_db/arch/sparc64/libc_r_md.c new file mode 100644 index 0000000..6c53953 --- /dev/null +++ b/lib/libthread_db/arch/sparc64/libc_r_md.c @@ -0,0 +1,41 @@ +/* + * 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 <machine/setjmp.h> + +void +libc_r_md_getgregs(jmp_buf jb, prgregset_t *r) +{ +} + +void +libc_r_md_getfpregs(jmp_buf jb, prfpregset_t *r) +{ +} 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..7553ed1 --- /dev/null +++ b/lib/libthread_db/arch/sparc64/libpthread_md.c @@ -0,0 +1,62 @@ +/* + * 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 <ucontext.h> + +void +pt_reg_to_ucontext(const struct reg *r, ucontext_t *uc) +{ +} + +void +pt_ucontext_to_reg(const ucontext_t *uc, struct reg *r) +{ +} + +void +pt_fpreg_to_ucontext(const struct fpreg* r, ucontext_t *uc) +{ +} + +void +pt_ucontext_to_fpreg(const ucontext_t *uc, struct fpreg *r) +{ +} + +void +pt_md_init(void) +{ +} + +int +pt_reg_sstep(struct reg *reg, int step) +{ + return (0); +} diff --git a/lib/libthread_db/libc_r_db.c b/lib/libthread_db/libc_r_db.c new file mode 100644 index 0000000..10f7df1 --- /dev/null +++ b/lib/libthread_db/libc_r_db.c @@ -0,0 +1,348 @@ +/* + * 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 <machine/setjmp.h> +#include <proc_service.h> +#include <stdlib.h> +#include <string.h> +#include <thread_db.h> + +#include "thread_db_int.h" + +void libc_r_md_getfpregs(jmp_buf jb, prfpregset_t *); +void libc_r_md_getgregs(jmp_buf jb, prgregset_t); + +struct td_thragent { + TD_THRAGENT_FIELDS; + struct ps_prochandle *ta_ph; + psaddr_t ta_thread_initial; + psaddr_t ta_thread_list; + psaddr_t ta_thread_run; + int ta_ofs_ctx; + int ta_ofs_next; + int ta_ofs_uniqueid; +}; + +static td_err_e +libc_r_db_init() +{ + return (TD_OK); +} + +static td_err_e +libc_r_db_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *ev) +{ + return (0); +} + +static td_err_e +libc_r_db_ta_delete(td_thragent_t *ta) +{ + free(ta); + return (TD_OK); +} + +static td_err_e +libc_r_db_ta_event_addr(const td_thragent_t *ta, td_thr_events_e ev, + td_notify_t *n) +{ + return (TD_ERR); +} + +static td_err_e +libc_r_db_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg) +{ + return (TD_ERR); +} + +static td_err_e +libc_r_db_ta_map_id2thr(const td_thragent_t *ta, thread_t tid, + td_thrhandle_t *th) +{ + return (TD_ERR); +} + +static td_err_e +libc_r_db_ta_map_lwp2thr(const td_thragent_t *ta, lwpid_t lwpid, + td_thrhandle_t *th) +{ + psaddr_t addr; + ps_err_e err; + + th->th_ta = ta; + err = ps_pread(ta->ta_ph, ta->ta_thread_initial, &addr, sizeof(addr)); + if (err != PS_OK) + return (TD_ERR); + if (addr == NULL) + return (TD_NOLWP); + err = ps_pread(ta->ta_ph, ta->ta_thread_run, &th->th_thread, + sizeof(psaddr_t)); + return ((err == PS_OK) ? TD_OK : TD_ERR); +} + +static td_err_e +libc_r_db_ta_new(struct ps_prochandle *ph, td_thragent_t **ta_p) +{ + td_thragent_t *ta; + psaddr_t addr; + ps_err_e err; + + ta = malloc(sizeof(td_thragent_t)); + if (ta == NULL) + return (TD_MALLOC); + + ta->ta_ph = ph; + + err = ps_pglobal_lookup(ph, NULL, "_thread_initial", + &ta->ta_thread_initial); + if (err != PS_OK) + goto fail; + err = ps_pglobal_lookup(ph, NULL, "_thread_list", &ta->ta_thread_list); + if (err != PS_OK) + goto fail; + err = ps_pglobal_lookup(ph, NULL, "_thread_run", &ta->ta_thread_run); + if (err != PS_OK) + goto fail; + err = ps_pglobal_lookup(ph, NULL, "_thread_ctx_offset", &addr); + if (err != PS_OK) + goto fail; + err = ps_pread(ph, addr, &ta->ta_ofs_ctx, sizeof(int)); + if (err != PS_OK) + goto fail; + err = ps_pglobal_lookup(ph, NULL, "_thread_next_offset", &addr); + if (err != PS_OK) + goto fail; + err = ps_pread(ph, addr, &ta->ta_ofs_next, sizeof(int)); + if (err != PS_OK) + goto fail; + err = ps_pglobal_lookup(ph, NULL, "_thread_uniqueid_offset", &addr); + if (err != PS_OK) + goto fail; + err = ps_pread(ph, addr, &ta->ta_ofs_uniqueid, sizeof(int)); + if (err != PS_OK) + goto fail; + + *ta_p = ta; + return (TD_OK); + + fail: + free(ta); + *ta_p = NULL; + return (TD_ERR); +} + +static td_err_e +libc_r_db_ta_set_event(const td_thragent_t *ta, td_thr_events_t *ev) +{ + return (0); +} + +static td_err_e +libc_r_db_ta_thr_iter(const td_thragent_t *ta, td_thr_iter_f *cb, void *data, + td_thr_state_e state, int pri, sigset_t *mask, unsigned int flags) +{ + td_thrhandle_t th; + psaddr_t addr; + ps_err_e err; + + th.th_ta = ta; + + err = ps_pread(ta->ta_ph, ta->ta_thread_list, &th.th_thread, + sizeof(th.th_thread)); + if (err != PS_OK) + return (TD_ERR); + while (th.th_thread != NULL) { + if (cb(&th, data) != 0) + return (TD_OK); + addr = (psaddr_t)((uintptr_t)th.th_thread + ta->ta_ofs_next); + err = ps_pread(ta->ta_ph, addr, &th.th_thread, + sizeof(th.th_thread)); + if (err != PS_OK) + return (TD_ERR); + } + return (TD_OK); +} + +static td_err_e +libc_r_db_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *ev) +{ + return (0); +} + +static td_err_e +libc_r_db_thr_event_enable(const td_thrhandle_t *th, int oo) +{ + return (0); +} + +static td_err_e +libc_r_db_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg) +{ + return (TD_ERR); +} + +static td_err_e +libc_r_db_thr_get_info(const td_thrhandle_t *th, td_thrinfo_t *ti) +{ + const td_thragent_t *ta; + psaddr_t addr, current; + ps_err_e err; + + ta = th->th_ta; + ti->ti_ta_p = ta; + err = ps_pread(ta->ta_ph, ta->ta_thread_run, ¤t, + sizeof(psaddr_t)); + if (err != PS_OK) + return (TD_ERR); + ti->ti_lid = (th->th_thread == current) ? -1 : 0; + addr = (psaddr_t)((uintptr_t)th->th_thread + ta->ta_ofs_uniqueid); + err = ps_pread(ta->ta_ph, addr, &ti->ti_tid, sizeof(thread_t)); + /* libc_r numbers its threads starting with 0. Not smart. */ + ti->ti_tid++; + return ((err == PS_OK) ? TD_OK : TD_ERR); +} + +#ifdef __i386__ +static td_err_e +libc_r_db_thr_getxmmregs(const td_thrhandle_t *th, char *fxsave) +{ + return (TD_NOFPREGS); +} +#endif + +static td_err_e +libc_r_db_thr_getfpregs(const td_thrhandle_t *th, prfpregset_t *r) +{ + jmp_buf jb; + const td_thragent_t *ta; + psaddr_t addr; + ps_err_e err; + + ta = th->th_ta; + err = ps_lgetfpregs(ta->ta_ph, -1, r); + if (err != PS_OK) + return (TD_ERR); + err = ps_pread(ta->ta_ph, ta->ta_thread_run, &addr, sizeof(psaddr_t)); + if (err != PS_OK) + return (TD_ERR); + if (th->th_thread == addr) + return (TD_OK); + addr = (psaddr_t)((uintptr_t)th->th_thread + ta->ta_ofs_ctx); + err = ps_pread(ta->ta_ph, addr, jb, sizeof(jb)); + if (err != PS_OK) + return (TD_ERR); + libc_r_md_getfpregs(jb, r); + return (TD_OK); +} + +static td_err_e +libc_r_db_thr_getgregs(const td_thrhandle_t *th, prgregset_t r) +{ + jmp_buf jb; + const td_thragent_t *ta; + psaddr_t addr; + ps_err_e err; + + ta = th->th_ta; + err = ps_lgetregs(ta->ta_ph, -1, r); + if (err != PS_OK) + return (TD_ERR); + err = ps_pread(ta->ta_ph, ta->ta_thread_run, &addr, sizeof(psaddr_t)); + if (err != PS_OK) + return (TD_ERR); + if (th->th_thread == addr) + return (TD_OK); + addr = (psaddr_t)((uintptr_t)th->th_thread + ta->ta_ofs_ctx); + err = ps_pread(ta->ta_ph, addr, jb, sizeof(jb)); + if (err != PS_OK) + return (TD_ERR); + libc_r_md_getgregs(jb, r); + return (TD_OK); +} + +static td_err_e +libc_r_db_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *ev) +{ + return (0); +} + +#ifdef __i386__ +static td_err_e +libc_r_db_thr_setxmmregs(const td_thrhandle_t *th, const char *fxsave) +{ + return (TD_NOFPREGS); +} +#endif + +static td_err_e +libc_r_db_thr_setfpregs(const td_thrhandle_t *th, const prfpregset_t *r) +{ + return (TD_ERR); +} + +static td_err_e +libc_r_db_thr_setgregs(const td_thrhandle_t *th, const prgregset_t r) +{ + return (TD_ERR); +} + +static td_err_e +libc_r_db_thr_validate(const td_thrhandle_t *th) +{ + return (TD_ERR); +} + +struct ta_ops libc_r_db_ops = { + .to_init = libc_r_db_init, + + .to_ta_clear_event = libc_r_db_ta_clear_event, + .to_ta_delete = libc_r_db_ta_delete, + .to_ta_event_addr = libc_r_db_ta_event_addr, + .to_ta_event_getmsg = libc_r_db_ta_event_getmsg, + .to_ta_map_id2thr = libc_r_db_ta_map_id2thr, + .to_ta_map_lwp2thr = libc_r_db_ta_map_lwp2thr, + .to_ta_new = libc_r_db_ta_new, + .to_ta_set_event = libc_r_db_ta_set_event, + .to_ta_thr_iter = libc_r_db_ta_thr_iter, + + .to_thr_clear_event = libc_r_db_thr_clear_event, + .to_thr_event_enable = libc_r_db_thr_event_enable, + .to_thr_event_getmsg = libc_r_db_thr_event_getmsg, + .to_thr_get_info = libc_r_db_thr_get_info, + .to_thr_getfpregs = libc_r_db_thr_getfpregs, + .to_thr_getgregs = libc_r_db_thr_getgregs, + .to_thr_set_event = libc_r_db_thr_set_event, + .to_thr_setfpregs = libc_r_db_thr_setfpregs, + .to_thr_setgregs = libc_r_db_thr_setgregs, + .to_thr_validate = libc_r_db_thr_validate, +#ifdef __i386__ + .to_thr_getxmmregs = libc_r_db_thr_getxmmregs, + .to_thr_setxmmregs = libc_r_db_thr_setxmmregs, +#endif +}; diff --git a/lib/libthread_db/libpthread_db.c b/lib/libthread_db/libpthread_db.c new file mode 100644 index 0000000..021410c --- /dev/null +++ b/lib/libthread_db/libpthread_db.c @@ -0,0 +1,1149 @@ +/* + * 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/kse.h> +#include <sys/ptrace.h> +#include <proc_service.h> +#include <thread_db.h> + +#include "libpthread_db.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, int 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; + TAILQ_HEAD(, pthread) thread_list; + 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 = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, + sizeof(thread_list)); + if (ret != 0) + return (P2T(ret)); + pt = (psaddr_t)thread_list.tqh_first; + 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 = ps_pread(ta->ph, + pt + ta->thread_off_tcb, + &tcb_addr, sizeof(tcb_addr)); + 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(lwp)); + if (ret != 0) + return (P2T(ret)); + /* + * 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 = ps_pread(ta->ph, + pt + ta->thread_off_next, + &pt, sizeof(pt)); + if (ret != 0) + return (P2T(ret)); + } + /* 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 = ps_pread(ta->ph, + pt + ta->thread_off_tcb, + &tcb_addr, sizeof(tcb_addr)); + if (ret != 0) + return (P2T(ret)); + /* get next thread */ + ret = ps_pread(ta->ph, + pt + ta->thread_off_next, + &pt, sizeof(pt)); + if (ret != 0) + return (P2T(ret)); + } + + 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) +{ + TAILQ_HEAD(, pthread) thread_list; + psaddr_t pt, ptr; + lwpid_t tmp_lwp; + int ret; + + TDBG_FUNC(); + + ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, + sizeof(thread_list)); + if (ret != 0) + return (P2T(ret)); + pt = (psaddr_t)thread_list.tqh_first; + while (pt != 0) { + ret = ps_pread(ta->ph, pt + ta->thread_off_tcb, + &ptr, sizeof(ptr)); + if (ret != 0) + return (P2T(ret)); + ptr += ta->thread_off_tmbx + + offsetof(struct kse_thr_mailbox, tm_lwp); + ret = ps_pread(ta->ph, ptr, &tmp_lwp, sizeof(lwpid_t)); + if (ret != 0) + return (P2T(ret)); + if (tmp_lwp == 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 = ps_pread(ta->ph, + pt + ta->thread_off_next, + &pt, sizeof(pt)); + if (ret != 0) + return (P2T(ret)); + } + + 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, int ti_pri, + sigset_t *ti_sigmask_p, + unsigned int ti_user_flags) +{ + TAILQ_HEAD(, pthread) thread_list; + td_thrhandle_t th; + psaddr_t pt; + ps_err_e pserr; + int activated; + + 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); + + pserr = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, + sizeof(thread_list)); + if (pserr != 0) + return (P2T(pserr)); + pt = (psaddr_t)thread_list.tqh_first; + 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 */ + pserr = ps_pread(ta->ph, + pt + ta->thread_off_next, &pt, + sizeof(pt)); + if (pserr != PS_OK) + return (P2T(pserr)); + } + return (TD_OK); +} + +static td_err_e +pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg) +{ + char *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 *)(keytable + i * ta->thread_size_key + + ta->thread_off_key_allocated); + destructor = *(void **)(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(); + return (TD_ERR); +} + +static td_err_e +pt_ta_set_event(const td_thragent_t *ta, td_thr_events_t *events) +{ + TDBG_FUNC(); + return (0); +} + +static td_err_e +pt_ta_clear_event(const td_thragent_t *ta, td_thr_events_t *events) +{ + TDBG_FUNC(); + return (0); +} + +static td_err_e +pt_ta_event_getmsg(const td_thragent_t *ta, td_event_msg_t *msg) +{ + TDBG_FUNC(); + return (TD_NOMSG); +} + +static td_err_e +pt_dbsuspend(const td_thrhandle_t *th, int suspend) +{ + td_thragent_t *ta = (td_thragent_t *)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_get_info(const td_thrhandle_t *th, td_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); +} + +#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, int en) +{ + TDBG_FUNC(); + return (0); +} + +static td_err_e +pt_thr_set_event(const td_thrhandle_t *th, td_thr_events_t *setp) +{ + TDBG_FUNC(); + return (0); +} + +static td_err_e +pt_thr_clear_event(const td_thrhandle_t *th, td_thr_events_t *setp) +{ + TDBG_FUNC(); + return (0); +} + +static td_err_e +pt_thr_event_getmsg(const td_thrhandle_t *th, td_event_msg_t *msg) +{ + 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, ®s); + /* only write out if it is really changed. */ + if (pt_reg_sstep(®s, step) != 0) { + pt_reg_to_ucontext(®s, &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); +} + +td_err_e +pt_thr_tls_get_addr(const td_thrhandle_t *th, void *_linkmap, size_t offset, + void **address) +{ + char *obj_entry; + const td_thragent_t *ta = th->th_ta; + psaddr_t tcb_addr, *dtv_addr; + int tls_index, ret; + + /* linkmap is a member of Obj_Entry */ + obj_entry = (char *)_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[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_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 +}; diff --git a/lib/libthread_db/libpthread_db.h b/lib/libthread_db/libpthread_db.h new file mode 100644 index 0000000..188da48 --- /dev/null +++ b/lib/libthread_db/libpthread_db.h @@ -0,0 +1,93 @@ +/* + * 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" + +struct pt_map { + enum { + PT_NONE, + PT_USER, + PT_LWP + } 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..148da14 --- /dev/null +++ b/lib/libthread_db/libthr_db.c @@ -0,0 +1,793 @@ +/* + * 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/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) +{ + TAILQ_HEAD(, pthread) thread_list; + psaddr_t pt; + long lwp; + int ret; + + TDBG_FUNC(); + + if (id == 0) + return (TD_NOTHR); + ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, + sizeof(thread_list)); + if (ret != 0) + return (P2T(ret)); + /* Iterate through thread list to find pthread */ + pt = (psaddr_t)thread_list.tqh_first; + while (pt != NULL) { + ret = ps_pread(ta->ph, pt + ta->thread_off_tid, + &lwp, sizeof(lwp)); + if (ret != 0) + return (P2T(ret)); + if (lwp == id) + break; + /* get next thread */ + ret = ps_pread(ta->ph, + pt + ta->thread_off_next, + &pt, sizeof(pt)); + if (ret != 0) + return (P2T(ret)); + } + if (pt == NULL) + 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, int ti_pri, + sigset_t *ti_sigmask_p, + unsigned int ti_user_flags) +{ + TAILQ_HEAD(, pthread) thread_list; + td_thrhandle_t th; + psaddr_t pt; + long lwp; + int ret; + + TDBG_FUNC(); + + ret = ps_pread(ta->ph, ta->thread_list_addr, &thread_list, + sizeof(thread_list)); + if (ret != 0) + return (P2T(ret)); + pt = (psaddr_t)thread_list.tqh_first; + while (pt != 0) { + ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp, + sizeof(lwp)); + if (ret != 0) + return (P2T(ret)); + 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 = ps_pread(ta->ph, pt + ta->thread_off_next, &pt, + sizeof(pt)); + if (ret != 0) + return (P2T(ret)); + } + return (TD_OK); +} + +static td_err_e +pt_ta_tsd_iter(const td_thragent_t *ta, td_key_iter_f *ki, void *arg) +{ + char *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 *)(keytable + i * ta->thread_size_key + + ta->thread_off_key_allocated); + destructor = *(void **)(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, pt_temp; + td_thr_events_e tmp; + long lwp; + int ret; + + TDBG_FUNC(); + + ret = ps_pread(ta->ph, ta->thread_last_event_addr, &pt, sizeof(pt)); + if (ret != 0) + return (P2T(ret)); + if (pt == NULL) + return (TD_NOMSG); + /* + * Take the event pointer, at the time, libthr only reports event + * once a time, so it is not a link list. + */ + pt_temp = NULL; + ps_pwrite(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp)); + + /* 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 = (psaddr_t)msg->th_p; + ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp, sizeof(lwp)); + if (ret != 0) + return (P2T(ret)); + handle.th_ta = ta; + handle.th_tid = lwp; + handle.th_thread = pt; + msg->th_p = &handle; + return (0); +} + +static td_err_e +pt_dbsuspend(const td_thrhandle_t *th, int suspend) +{ + td_thragent_t *ta = (td_thragent_t *)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(const td_thrhandle_t *th, td_thrinfo_t *info) +{ + const td_thragent_t *ta = th->th_ta; + struct ptrace_lwpinfo linfo; + int state; + int ret; + + TDBG_FUNC(); + + bzero(info, sizeof(*info)); + ret = pt_validate(th); + if (ret) + return (ret); + ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_state, + &state, sizeof(state)); + if (ret != 0) + return (P2T(ret)); + ret = ps_pread(ta->ph, th->th_thread + ta->thread_off_report_events, + &info->ti_traceme, sizeof(int)); + if (ret != 0) + return (P2T(ret)); + 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; + } 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); +} + +#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; + td_thragent_t *ta = (td_thragent_t *)th->th_ta; + psaddr_t pt, pt_temp; + long lwp; + int ret; + td_thr_events_e tmp; + + TDBG_FUNC(); + pt = th->th_thread; + ret = ps_pread(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp)); + if (ret != 0) + return (P2T(ret)); + /* 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) { + pt_temp = NULL; + ps_pwrite(ta->ph, ta->thread_last_event_addr, &pt_temp, sizeof(pt_temp)); + } + /* Clear event */ + tmp = 0; + ps_pwrite(ta->ph, pt + ta->thread_off_event_buf, &tmp, sizeof(tmp)); + /* Convert event */ + pt = (psaddr_t)msg->th_p; + ret = ps_pread(ta->ph, pt + ta->thread_off_tid, &lwp, sizeof(lwp)); + if (ret != 0) + return (P2T(ret)); + handle.th_ta = ta; + handle.th_tid = lwp; + handle.th_thread = pt; + msg->th_p = &handle; + return (0); +} + +static td_err_e +pt_thr_sstep(const td_thrhandle_t *th, int step) +{ + TDBG_FUNC(); + + return pt_validate(th); +} + +static int +pt_validate(const td_thrhandle_t *th) +{ + + if (th->th_tid == 0 || th->th_thread == NULL) + return (TD_ERR); + return (TD_OK); +} + +static td_err_e +pt_thr_tls_get_addr(const td_thrhandle_t *th, void *_linkmap, size_t offset, + void **address) +{ + char *obj_entry; + const td_thragent_t *ta = th->th_ta; + psaddr_t tcb_addr, *dtv_addr; + int tls_index, ret; + + /* linkmap is a member of Obj_Entry */ + obj_entry = (char *)_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[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_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 +}; diff --git a/lib/libthread_db/thread_db.c b/lib/libthread_db/thread_db.c new file mode 100644 index 0000000..dc14a36 --- /dev/null +++ b/lib/libthread_db/thread_db.c @@ -0,0 +1,261 @@ +/* + * 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 "thread_db_int.h" + +struct td_thragent +{ + TD_THRAGENT_FIELDS; +}; + +static TAILQ_HEAD(, td_thragent) proclist = TAILQ_HEAD_INITIALIZER(proclist); + +extern struct ta_ops libpthread_db_ops; +extern struct ta_ops libthr_db_ops; + +static struct ta_ops *ops[] = { + &libpthread_db_ops, + &libthr_db_ops, +}; + +td_err_e +td_init(void) +{ + td_err_e ret, tmp; + size_t i; + + ret = 0; + for (i = 0; i < sizeof(ops)/sizeof(ops[0]); i++) { + if (ops[i]->to_init != NULL) { + tmp = ops[i]->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) +{ + size_t i; + + for (i = 0; i < sizeof(ops)/sizeof(ops[0]); ++i) { + if (ops[i]->to_ta_new(ph, pta) == TD_OK) { + TAILQ_INSERT_HEAD(&proclist, *pta, ta_next); + (*pta)->ta_ops = ops[i]; + 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_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, void *linkmap, size_t offset, + void **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)); +} diff --git a/lib/libthread_db/thread_db.h b/lib/libthread_db/thread_db.h new file mode 100644 index 0000000..a05274e --- /dev/null +++ b/lib/libthread_db/thread_db.h @@ -0,0 +1,248 @@ +/* + * 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; + const td_thrhandle_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; +} 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 *, void *, size_t, void **); + +/* 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..2e5ecba --- /dev/null +++ b/lib/libthread_db/thread_db_int.h @@ -0,0 +1,96 @@ +/* + * 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> + +#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_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 *, + void *, size_t, void **); + + /* 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 + +#endif /* _THREAD_DB_INT_H_ */ |