From a049970caad5cf461ceb13d4b2fcd914b2af24f1 Mon Sep 17 00:00:00 2001 From: rpaulo Date: Sat, 31 Jul 2010 16:10:20 +0000 Subject: New version of libproc. Changes are: * breakpoint setup support * register query * symbol to address mapping and vice-versa * more misc utility functions based on their Solaris counterpart Also, I've written some test cases. Sponsored by: The FreeBSD Foundation --- lib/libproc/Makefile | 4 +- lib/libproc/_libproc.h | 12 + lib/libproc/libproc.h | 84 ++++- lib/libproc/proc_bkpt.c | 183 +++++++++++ lib/libproc/proc_create.c | 61 ++-- lib/libproc/proc_regs.c | 113 +++++++ lib/libproc/proc_rtld.c | 79 +++++ lib/libproc/proc_sym.c | 509 ++++++++++++++++++++++++++++- lib/libproc/proc_util.c | 139 ++++++-- lib/libproc/test/Makefile | 5 + lib/libproc/test/t1-bkpt/Makefile | 12 + lib/libproc/test/t1-bkpt/t1-bkpt.c | 71 ++++ lib/libproc/test/t2-name2map/Makefile | 12 + lib/libproc/test/t2-name2map/t2-name2map.c | 46 +++ lib/libproc/test/t3-name2sym/Makefile | 12 + lib/libproc/test/t3-name2sym/t3-name2sym.c | 50 +++ 16 files changed, 1316 insertions(+), 76 deletions(-) create mode 100644 lib/libproc/proc_bkpt.c create mode 100644 lib/libproc/proc_regs.c create mode 100644 lib/libproc/proc_rtld.c create mode 100644 lib/libproc/test/Makefile create mode 100644 lib/libproc/test/t1-bkpt/Makefile create mode 100644 lib/libproc/test/t1-bkpt/t1-bkpt.c create mode 100644 lib/libproc/test/t2-name2map/Makefile create mode 100644 lib/libproc/test/t2-name2map/t2-name2map.c create mode 100644 lib/libproc/test/t3-name2sym/Makefile create mode 100644 lib/libproc/test/t3-name2sym/t3-name2sym.c (limited to 'lib/libproc') diff --git a/lib/libproc/Makefile b/lib/libproc/Makefile index edb19bb..c02e719 100644 --- a/lib/libproc/Makefile +++ b/lib/libproc/Makefile @@ -2,9 +2,11 @@ LIB= proc -SRCS= \ +SRCS= proc_bkpt.c \ proc_create.c \ + proc_regs.c \ proc_sym.c \ + proc_rtld.c \ proc_util.c INCS= libproc.h diff --git a/lib/libproc/_libproc.h b/lib/libproc/_libproc.h index 536227e..aee1ac1 100644 --- a/lib/libproc/_libproc.h +++ b/lib/libproc/_libproc.h @@ -31,6 +31,7 @@ #include #include #include +#include #include "libproc.h" @@ -39,5 +40,16 @@ struct proc_handle { int kq; /* Kernel event queue ID. */ int flags; /* Process flags. */ int status; /* Process status (PS_*). */ + int wstat; /* Process wait status. */ + rd_agent_t *rdap; /* librtld_db agent */ + rd_loadobj_t *rdobjs; + size_t rdobjsz; + size_t nobjs; + struct lwpstatus lwps; }; +#ifdef DEBUG +#define DPRINTF(fmt, ...) warn(fmt, __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) +#endif diff --git a/lib/libproc/libproc.h b/lib/libproc/libproc.h index eccf37d..ca8c7be 100644 --- a/lib/libproc/libproc.h +++ b/lib/libproc/libproc.h @@ -1,7 +1,11 @@ /*- + * Copyright (c) 2010 The FreeBSD Foundation * Copyright (c) 2008 John Birrell (jb@freebsd.org) * All rights reserved. * + * Portions of this software were developed by Rui Paulo under sponsorship + * from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -30,6 +34,8 @@ #define _LIBPROC_H_ #include +#include +#include struct proc_handle; @@ -43,30 +49,100 @@ typedef void (*proc_child_func)(void *); #define PS_DEAD 5 #define PS_LOST 6 +/* Reason values for proc_detach(). */ +#define PRELEASE_HANG 1 +#define PRELEASE_KILL 2 + typedef struct prmap { uintptr_t pr_vaddr; /* Virtual address. */ + size_t pr_size; /* Mapping size in bytes */ + size_t pr_offset; /* Mapping offset in object */ + char pr_mapname[PATH_MAX]; /* Mapping filename */ + uint8_t pr_mflags; /* Protection flags */ +#define MA_READ 0x01 +#define MA_WRITE 0x02 +#define MA_EXEC 0x04 +#define MA_COW 0x08 +#define MA_NEEDS_COPY 0x10 +#define MA_NOCOREDUMP 0x20 } prmap_t; +typedef int proc_map_f(void *, const prmap_t *, const char *); +typedef int proc_sym_f(void *, const GElf_Sym *, const char *); + +/* Values for ELF sections */ +#define PR_SYMTAB 1 +#define PR_DYNSYM 2 + +/* Values for the 'mask' parameter in the iteration functions */ +#define BIND_LOCAL 0x0001 +#define BIND_GLOBAL 0x0002 +#define BIND_WEAK 0x0004 +#define BIND_ANY (BIND_LOCAL|BIND_GLOBAL|BIND_WEAK) +#define TYPE_NOTYPE 0x0100 +#define TYPE_OBJECT 0x0200 +#define TYPE_FUNC 0x0400 +#define TYPE_SECTION 0x0800 +#define TYPE_FILE 0x1000 +#define TYPE_ANY (TYPE_NOTYPE|TYPE_OBJECT|TYPE_FUNC|TYPE_SECTION|\ + TYPE_FILE) + +typedef enum { + REG_PC, + REG_SP, + REG_RVAL1, + REG_RVAL2 +} proc_reg_t; + +#define SIG2STR_MAX 8 + +typedef struct lwpstatus { + int pr_why; +#define PR_REQUESTED 1 +#define PR_FAULTED 2 +#define PR_SYSENTRY 3 +#define PR_SYSEXIT 4 + int pr_what; +#define FLTBPT -1 +} lwpstatus_t; + /* Function prototype definitions. */ __BEGIN_DECLS -const prmap_t *proc_addr2map(struct proc_handle *, uintptr_t); -const prmap_t *proc_name2map(struct proc_handle *, const char *); +prmap_t *proc_addr2map(struct proc_handle *, uintptr_t); +prmap_t *proc_name2map(struct proc_handle *, const char *); char *proc_objname(struct proc_handle *, uintptr_t, char *, size_t); +prmap_t *proc_obj2map(struct proc_handle *, const char *); +int proc_iter_objs(struct proc_handle *, proc_map_f *, void *); +int proc_iter_symbyaddr(struct proc_handle *, const char *, int, + int, proc_sym_f *, void *); int proc_addr2sym(struct proc_handle *, uintptr_t, char *, size_t, GElf_Sym *); int proc_attach(pid_t pid, int flags, struct proc_handle **pphdl); int proc_continue(struct proc_handle *); int proc_clearflags(struct proc_handle *, int); int proc_create(const char *, char * const *, proc_child_func *, void *, struct proc_handle **); -int proc_detach(struct proc_handle *); +int proc_detach(struct proc_handle *, int); int proc_getflags(struct proc_handle *); int proc_name2sym(struct proc_handle *, const char *, const char *, GElf_Sym *); int proc_setflags(struct proc_handle *, int); int proc_state(struct proc_handle *); -int proc_wait(struct proc_handle *); pid_t proc_getpid(struct proc_handle *); +int proc_wstatus(struct proc_handle *); +int proc_getwstat(struct proc_handle *); +char * proc_signame(int, char *, size_t); +int proc_read(struct proc_handle *, char *, size_t, size_t); +const lwpstatus_t * + proc_getlwpstatus(struct proc_handle *); void proc_free(struct proc_handle *); +rd_agent_t *proc_rdagent(struct proc_handle *); +void proc_updatesyms(struct proc_handle *); +int proc_bkptset(struct proc_handle *, uintptr_t, unsigned long *); +int proc_bkptdel(struct proc_handle *, uintptr_t, unsigned long); +void proc_bkptregadj(unsigned long *); +int proc_bkptexec(struct proc_handle *, unsigned long); +int proc_regget(struct proc_handle *, proc_reg_t, unsigned long *); +int proc_regset(struct proc_handle *, proc_reg_t, unsigned long); __END_DECLS diff --git a/lib/libproc/proc_bkpt.c b/lib/libproc/proc_bkpt.c new file mode 100644 index 0000000..862a1bb --- /dev/null +++ b/lib/libproc/proc_bkpt.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Rui Paulo under sponsorship from the + * FreeBSD Foundation. + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include "_libproc.h" + +#if defined(__i386__) || defined(__amd64__) +#define BREAKPOINT_INSTR 0xcc /* int 0x3 */ +#define BREAKPOINT_INSTR_SZ 1 +#else +#error "Add support for your architecture" +#endif + +int +proc_bkptset(struct proc_handle *phdl, uintptr_t address, + unsigned long *saved) +{ + struct ptrace_io_desc piod; + unsigned long paddr, caddr; + + *saved = 0; + if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || + phdl->status == PS_IDLE) { + errno = ENOENT; + return (-1); + } + + /* + * Read the original instruction. + */ + caddr = address; + paddr = 0; + piod.piod_op = PIOD_READ_I; + piod.piod_offs = (void *)caddr; + piod.piod_addr = &paddr; + piod.piod_len = BREAKPOINT_INSTR_SZ; + if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { + DPRINTF("ERROR: couldn't read instruction at address 0x%" PRIuPTR, + address); + return (-1); + } + *saved = paddr; + /* + * Write a breakpoint instruction to that address. + */ + caddr = address; + paddr = BREAKPOINT_INSTR; + piod.piod_op = PIOD_WRITE_I; + piod.piod_offs = (void *)caddr; + piod.piod_addr = &paddr; + piod.piod_len = BREAKPOINT_INSTR_SZ; + if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { + warn("ERROR: couldn't write instruction at address 0x%" PRIuPTR, + address); + return (-1); + } + + return (0); +} + +int +proc_bkptdel(struct proc_handle *phdl, uintptr_t address, + unsigned long saved) +{ + struct ptrace_io_desc piod; + unsigned long paddr, caddr; + + if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || + phdl->status == PS_IDLE) { + errno = ENOENT; + return (-1); + } + DPRINTF("removing breakpoint at 0x%lx\n", address); + /* + * Overwrite the breakpoint instruction that we setup previously. + */ + caddr = address; + paddr = saved; + piod.piod_op = PIOD_WRITE_I; + piod.piod_offs = (void *)caddr; + piod.piod_addr = &paddr; + piod.piod_len = BREAKPOINT_INSTR_SZ; + if (ptrace(PT_IO, proc_getpid(phdl), (caddr_t)&piod, 0) < 0) { + DPRINTF("ERROR: couldn't write instruction at address 0x%" PRIuPTR, + address); + return (-1); + } + + return (0); +} + +/* + * Decrement pc so that we delete the breakpoint at the correct + * address, i.e. at the BREAKPOINT_INSTR address. + */ +void +proc_bkptregadj(unsigned long *pc) +{ + *pc = *pc - BREAKPOINT_INSTR_SZ; +} + +/* + * Step over the breakpoint. + */ +int +proc_bkptexec(struct proc_handle *phdl, unsigned long saved) +{ + unsigned long pc; + unsigned long samesaved; + int status; + + if (proc_regget(phdl, REG_PC, &pc) < 0) { + warn("ERROR: couldn't get PC register"); + return (-1); + } + proc_bkptregadj(&pc); + if (proc_bkptdel(phdl, pc, saved) < 0) { + warn("ERROR: couldn't delete breakpoint"); + return (-1); + } + /* + * Go back in time and step over the new instruction just + * set up by proc_bkptdel(). + */ + proc_regset(phdl, REG_PC, pc); + if (ptrace(PT_STEP, proc_getpid(phdl), (caddr_t)1, 0) < 0) { + warn("ERROR: ptrace step failed"); + return (-1); + } + status = proc_wstatus(phdl); + if (!WIFSTOPPED(status)) { + warn("ERROR: don't know why process stopped"); + return (-1); + } + /* + * Restore the breakpoint. The saved instruction should be + * the same as the one that we were passed in. + */ + if (proc_bkptset(phdl, pc, &samesaved) < 0) { + warn("ERROR: couldn't restore breakpoint"); + return (-1); + } + assert(samesaved == saved); + + return (0); +} diff --git a/lib/libproc/proc_create.c b/lib/libproc/proc_create.c index 1649b47..b30a77b 100644 --- a/lib/libproc/proc_create.c +++ b/lib/libproc/proc_create.c @@ -27,6 +27,7 @@ */ #include "_libproc.h" +#include #include #include #include @@ -40,11 +41,10 @@ int proc_attach(pid_t pid, int flags, struct proc_handle **pphdl) { struct proc_handle *phdl; - struct kevent kev; int error = 0; int status; - if (pid == 0 || pphdl == NULL) + if (pid == 0 || pid == getpid()) return (EINVAL); /* @@ -58,26 +58,24 @@ proc_attach(pid_t pid, int flags, struct proc_handle **pphdl) phdl->pid = pid; phdl->flags = flags; phdl->status = PS_RUN; + elf_version(EV_CURRENT); - EV_SET(&kev, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, - 0, NULL); - - if ((phdl->kq = kqueue()) == -1) - err(1, "ERROR: cannot create kernel evet queue"); - - if (kevent(phdl->kq, &kev, 1, NULL, 0, NULL) < 0) - err(2, "ERROR: cannot monitor child process %d", pid); - - if (ptrace(PT_ATTACH, phdl->pid, NULL, 0) != 0) + if (ptrace(PT_ATTACH, phdl->pid, 0, 0) != 0) { error = errno; + DPRINTF("ERROR: cannot ptrace child process %d", pid); + goto out; + } /* Wait for the child process to stop. */ - else if (waitpid(pid, &status, WUNTRACED) == -1) - err(3, "ERROR: child process %d didn't stop as expected", pid); + if (waitpid(pid, &status, WUNTRACED) == -1) { + error = errno; + DPRINTF("ERROR: child process %d didn't stop as expected", pid); + goto out; + } /* Check for an unexpected status. */ - else if (WIFSTOPPED(status) == 0) - err(4, "ERROR: child process %d status 0x%x", pid, status); + if (WIFSTOPPED(status) == 0) + DPRINTF("ERROR: child process %d status 0x%x", pid, status); else phdl->status = PS_STOP; @@ -85,6 +83,7 @@ proc_attach(pid_t pid, int flags, struct proc_handle **pphdl) proc_free(phdl); else *pphdl = phdl; +out: return (error); } @@ -94,7 +93,6 @@ proc_create(const char *file, char * const *argv, proc_child_func *pcf, void *child_arg, struct proc_handle **pphdl) { struct proc_handle *phdl; - struct kevent kev; int error = 0; int status; pid_t pid; @@ -106,6 +104,8 @@ proc_create(const char *file, char * const *argv, proc_child_func *pcf, if ((phdl = malloc(sizeof(struct proc_handle))) == NULL) return (ENOMEM); + elf_version(EV_CURRENT); + /* Fork a new process. */ if ((pid = vfork()) == -1) error = errno; @@ -128,31 +128,26 @@ proc_create(const char *file, char * const *argv, proc_child_func *pcf, phdl->pid = pid; phdl->status = PS_IDLE; - EV_SET(&kev, pid, EVFILT_PROC, EV_ADD | EV_ONESHOT, NOTE_EXIT, - 0, NULL); - - if ((phdl->kq = kqueue()) == -1) - err(1, "ERROR: cannot create kernel evet queue"); - - if (kevent(phdl->kq, &kev, 1, NULL, 0, NULL) < 0) - err(2, "ERROR: cannot monitor child process %d", pid); - /* Wait for the child process to stop. */ - if (waitpid(pid, &status, WUNTRACED) == -1) - err(3, "ERROR: child process %d didn't stop as expected", pid); + if (waitpid(pid, &status, WUNTRACED) == -1) { + error = errno; + DPRINTF("ERROR: child process %d didn't stop as expected", pid); + goto bad; + } /* Check for an unexpected status. */ - if (WIFSTOPPED(status) == 0) - err(4, "ERROR: child process %d status 0x%x", pid, status); - else + if (WIFSTOPPED(status) == 0) { + error = errno; + DPRINTF("ERROR: child process %d status 0x%x", pid, status); + goto bad; + } else phdl->status = PS_STOP; } - +bad: if (error) proc_free(phdl); else *pphdl = phdl; - return (error); } diff --git a/lib/libproc/proc_regs.c b/lib/libproc/proc_regs.c new file mode 100644 index 0000000..3450e98 --- /dev/null +++ b/lib/libproc/proc_regs.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Rui Paulo under sponsorship from the + * FreeBSD Foundation. + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include "_libproc.h" + +int +proc_regget(struct proc_handle *phdl, proc_reg_t reg, unsigned long *regvalue) +{ + struct reg regs; + + if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || + phdl->status == PS_IDLE) { + errno = ENOENT; + return (-1); + } + memset(®s, 0, sizeof(regs)); + if (ptrace(PT_GETREGS, proc_getpid(phdl), (caddr_t)®s, 0) < 0) + return (-1); + switch (reg) { + case REG_PC: +#if defined(__amd64__) + *regvalue = regs.r_rip; +#elif defined(__i386__) + *regvalue = regs.r_eip; +#endif + break; + case REG_SP: +#if defined(__amd64__) + *regvalue = regs.r_rsp; +#elif defined(__i386__) + *regvalue = regs.r_esp; +#endif + break; + default: + warn("ERROR: no support for reg number %d", reg); + return (-1); + } + + return (0); +} + +int +proc_regset(struct proc_handle *phdl, proc_reg_t reg, unsigned long regvalue) +{ + struct reg regs; + + if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD || + phdl->status == PS_IDLE) { + errno = ENOENT; + return (-1); + } + if (ptrace(PT_GETREGS, proc_getpid(phdl), (caddr_t)®s, 0) < 0) + return (-1); + switch (reg) { + case REG_PC: +#if defined(__amd64__) + regs.r_rip = regvalue; +#elif defined(__i386__) + regs.r_eip = regvalue; +#endif + break; + case REG_SP: +#if defined(__amd64__) + regs.r_rsp = regvalue; +#elif defined(__i386__) + regs.r_esp = regvalue; +#endif + break; + default: + warn("ERROR: no support for reg number %d", reg); + return (-1); + } + if (ptrace(PT_SETREGS, proc_getpid(phdl), (caddr_t)®s, 0) < 0) + return (-1); + + return (0); +} diff --git a/lib/libproc/proc_rtld.c b/lib/libproc/proc_rtld.c new file mode 100644 index 0000000..5caadd9e --- /dev/null +++ b/lib/libproc/proc_rtld.c @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Rui Paulo under sponsorship from the + * FreeBSD Foundation. + * + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include "libproc.h" +#include "_libproc.h" + +static int +map_iter(const rd_loadobj_t *lop, void *arg) +{ + struct proc_handle *phdl = arg; + + phdl->nobjs++; + if (phdl->nobjs >= phdl->rdobjsz) { + phdl->rdobjsz *= 2; + phdl->rdobjs = realloc(phdl->rdobjs, phdl->rdobjsz); + if (phdl->rdobjs == NULL) + return (-1); + } + memcpy(&phdl->rdobjs[phdl->nobjs++], lop, sizeof(*phdl->rdobjs)); + + return (0); +} + +rd_agent_t * +proc_rdagent(struct proc_handle *phdl) +{ + if (phdl->rdap == NULL && phdl->status != PS_UNDEAD && + phdl->status != PS_IDLE) { + if ((phdl->rdap = rd_new(phdl)) != NULL) { + phdl->rdobjs = malloc(sizeof(*phdl->rdobjs) * 64); + if (phdl->rdobjs == NULL) + return (phdl->rdap); + rd_loadobj_iter(phdl->rdap, map_iter, phdl); + } + } + + return (phdl->rdap); +} + +void +proc_updatesyms(struct proc_handle *phdl) +{ + memset(&phdl->rdobjs, 0, sizeof(*phdl->rdobjs) * phdl->rdobjsz); + phdl->nobjs = 0; + rd_loadobj_iter(phdl->rdap, map_iter, phdl); +} diff --git a/lib/libproc/proc_sym.c b/lib/libproc/proc_sym.c index c17c515..82c4c74 100644 --- a/lib/libproc/proc_sym.c +++ b/lib/libproc/proc_sym.c @@ -1,7 +1,11 @@ /*- + * Copyright (c) 2010 The FreeBSD Foundation * Copyright (c) 2008 John Birrell (jb@freebsd.org) * All rights reserved. * + * Portions of this software were developed by Rui Paulo under sponsorship + * from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -26,43 +30,524 @@ * $FreeBSD$ */ -#include "_libproc.h" +#include +#include + +#include +#include #include +#include +#include +#include +#include +#include +#include +#include + +#include "_libproc.h" + +static void proc_rdl2prmap(rd_loadobj_t *, prmap_t *); + +static void +proc_rdl2prmap(rd_loadobj_t *rdl, prmap_t *map) +{ + map->pr_vaddr = rdl->rdl_saddr; + map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr; + map->pr_offset = rdl->rdl_offset; + map->pr_mflags = 0; + if (rdl->rdl_prot & RD_RDL_R) + map->pr_mflags |= MA_READ; + if (rdl->rdl_prot & RD_RDL_W) + map->pr_mflags |= MA_WRITE; + if (rdl->rdl_prot & RD_RDL_X) + map->pr_mflags |= MA_EXEC; + strlcpy(map->pr_mapname, rdl->rdl_path, + sizeof(map->pr_mapname)); +} char * proc_objname(struct proc_handle *p, uintptr_t addr, char *objname, size_t objnamesz) { -printf("%s(%d): Not implemented. p %p addr 0x%lx objname %p objnamesz %zd\n",__func__,__LINE__,p,(u_long) addr,objname,objnamesz); + size_t i; + rd_loadobj_t *rdl; + + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + if (addr >= rdl->rdl_saddr && addr <= rdl->rdl_eaddr) { + strlcpy(objname, rdl->rdl_path, objnamesz); + return (objname); + } + } return (NULL); } -const prmap_t * +prmap_t * +proc_obj2map(struct proc_handle *p, const char *objname) +{ + size_t i; + prmap_t *map; + rd_loadobj_t *rdl; + char path[MAXPATHLEN]; + + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + basename_r(rdl->rdl_path, path); + if (strcmp(path, objname) == 0) { + if ((map = malloc(sizeof(*map))) == NULL) + return (NULL); + proc_rdl2prmap(rdl, map); + return (map); + } + } + return (NULL); +} + +int +proc_iter_objs(struct proc_handle *p, proc_map_f *func, void *cd) +{ + size_t i; + rd_loadobj_t *rdl; + prmap_t map; + char path[MAXPATHLEN]; + + if (p->nobjs == 0) + return (-1); + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + proc_rdl2prmap(rdl, &map); + basename_r(rdl->rdl_path, path); + (*func)(cd, &map, path); + } + + return (0); +} + +prmap_t * proc_addr2map(struct proc_handle *p, uintptr_t addr) { -printf("%s(%d): Not implemented. p %p addr 0x%lx\n",__func__,__LINE__,p,(u_long) addr); + size_t i; + int cnt, lastvn = 0; + prmap_t *map; + rd_loadobj_t *rdl; + struct kinfo_vmentry *kves, *kve; + + /* + * If we don't have a cache of listed objects, we need to query + * it ourselves. + */ + if (p->nobjs == 0) { + if ((kves = kinfo_getvmmap(p->pid, &cnt)) == NULL) + return (NULL); + for (i = 0; i < (size_t)cnt; i++) { + kve = kves + i; + if (kve->kve_type == KVME_TYPE_VNODE) + lastvn = i; + if (addr >= kve->kve_start && addr <= kve->kve_end) { + if ((map = malloc(sizeof(*map))) == NULL) { + free(kves); + return (NULL); + } + map->pr_vaddr = kve->kve_start; + map->pr_size = kve->kve_end - kve->kve_start; + map->pr_offset = kve->kve_offset; + map->pr_mflags = 0; + if (kve->kve_protection & KVME_PROT_READ) + map->pr_mflags |= MA_READ; + if (kve->kve_protection & KVME_PROT_WRITE) + map->pr_mflags |= MA_WRITE; + if (kve->kve_protection & KVME_PROT_EXEC) + map->pr_mflags |= MA_EXEC; + if (kve->kve_flags & KVME_FLAG_COW) + map->pr_mflags |= MA_COW; + if (kve->kve_flags & KVME_FLAG_NEEDS_COPY) + map->pr_mflags |= MA_NEEDS_COPY; + if (kve->kve_flags & KVME_FLAG_NOCOREDUMP) + map->pr_mflags |= MA_NOCOREDUMP; + strlcpy(map->pr_mapname, kves[lastvn].kve_path, + sizeof(map->pr_mapname)); + free(kves); + return (map); + } + } + free(kves); + return (NULL); + } + + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + if (addr >= rdl->rdl_saddr && addr <= rdl->rdl_eaddr) { + if ((map = malloc(sizeof(*map))) == NULL) + return (NULL); + proc_rdl2prmap(rdl, map); + return (map); + } + } return (NULL); } int proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, - size_t namesz, GElf_Sym *sym) + size_t namesz, GElf_Sym *symcopy) { -printf("%s(%d): Not implemented. p %p addr 0x%lx name %p namesz %zd sym %p\n",__func__,__LINE__,p,(u_long) addr,name,namesz,sym); - return (0); + Elf *e; + Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; + Elf_Data *data; + GElf_Shdr shdr; + GElf_Sym sym; + GElf_Ehdr ehdr; + int fd, error = -1; + size_t i; + uint64_t rsym; + prmap_t *map; + char *s; + unsigned long symtabstridx = 0, dynsymstridx = 0; + + if ((map = proc_addr2map(p, addr)) == NULL) + return (-1); + if (!map->pr_mapname || (fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) { + warn("ERROR: open %s failed", map->pr_mapname); + goto err0; + } + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + warn("ERROR: elf_begin() failed"); + goto err1; + } + if (gelf_getehdr(e, &ehdr) == NULL) { + warn("ERROR: gelf_getehdr() failed"); + goto err2; + } + /* + * Find the index of the STRTAB and SYMTAB sections to locate + * symbol names. + */ + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + switch (shdr.sh_type) { + case SHT_SYMTAB: + symtabscn = scn; + symtabstridx = shdr.sh_link; + break; + case SHT_DYNSYM: + dynsymscn = scn; + dynsymstridx = shdr.sh_link; + break; + default: + break; + } + } + /* + * Iterate over the Dynamic Symbols table to find the symbol. + * Then look up the string name in STRTAB (.dynstr) + */ + if ((data = elf_getdata(dynsymscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + /* + * Calculate the address mapped to the virtual memory + * by rtld. + */ + rsym = map->pr_vaddr + sym.st_value; + if (addr >= rsym && addr <= (rsym + sym.st_size)) { + s = elf_strptr(e, dynsymstridx, sym.st_name); + if (s) { + strlcpy(name, s, namesz); + memcpy(symcopy, &sym, sizeof(sym)); + /* + * DTrace expects the st_value to contain + * only the address relative to the start of + * the function. + */ + symcopy->st_value = rsym; + error = 0; + goto out; + } + } + } + /* + * Iterate over the Symbols Table to find the symbol. + * Then look up the string name in STRTAB (.dynstr) + */ + if (symtabscn == NULL) + goto err2; + if ((data = elf_getdata(symtabscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + /* + * Calculate the address mapped to the virtual memory + * by rtld. + */ + if (ehdr.e_type != ET_EXEC) + rsym = map->pr_vaddr + sym.st_value; + else + rsym = sym.st_value; + if (addr >= rsym && addr <= (rsym + sym.st_size)) { + s = elf_strptr(e, symtabstridx, sym.st_name); + if (s) { + strlcpy(name, s, namesz); + memcpy(symcopy, &sym, sizeof(sym)); + /* + * DTrace expects the st_value to contain + * only the address relative to the start of + * the function. + */ + symcopy->st_value = rsym; + error = 0; + goto out; + } + } + } +out: +err2: + elf_end(e); +err1: + close(fd); +err0: + free(map); + return (error); } -const prmap_t * +prmap_t * proc_name2map(struct proc_handle *p, const char *name) { -printf("%s(%d): Not implemented. p %p name %p\n",__func__,__LINE__,p,name); + size_t i; + int cnt; + prmap_t *map; + char tmppath[MAXPATHLEN]; + struct kinfo_vmentry *kves, *kve; + rd_loadobj_t *rdl; + + /* + * If we haven't iterated over the list of loaded objects, + * librtld_db isn't yet initialized and it's very likely + * that librtld_db called us. We need to do the heavy + * lifting here to find the symbol librtld_db is looking for. + */ + if (p->nobjs == 0) { + if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL) + return (NULL); + for (i = 0; i < (size_t)cnt; i++) { + kve = kves + i; + basename_r(kve->kve_path, tmppath); + if (strcmp(tmppath, name) == 0) { + map = proc_addr2map(p, kve->kve_start); + free(kves); + return (map); + } + } + free(kves); + return (NULL); + } + if (name == NULL || strcmp(name, "a.out") == 0) { + map = proc_addr2map(p, p->rdobjs[0].rdl_saddr); + return (map); + } + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + basename_r(rdl->rdl_path, tmppath); + if (strcmp(tmppath, name) == 0) { + if ((map = malloc(sizeof(*map))) == NULL) + return (NULL); + proc_rdl2prmap(rdl, map); + return (map); + } + } + return (NULL); } int proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, - GElf_Sym *sym) + GElf_Sym *symcopy) { -printf("%s(%d): Not implemented. p %p object %p symbol %p sym %p\n",__func__,__LINE__,p,object,symbol,sym); - return (0); + Elf *e; + Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; + Elf_Data *data; + GElf_Shdr shdr; + GElf_Sym sym; + GElf_Ehdr ehdr; + int fd, error = -1; + size_t i; + prmap_t *map; + char *s; + unsigned long symtabstridx = 0, dynsymstridx = 0; + + if ((map = proc_name2map(p, object)) == NULL) { + DPRINTF("ERROR: couldn't find object %s", object); + goto err0; + } + if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) { + DPRINTF("ERROR: open %s failed", map->pr_mapname); + goto err0; + } + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + warn("ERROR: elf_begin() failed"); + goto err1; + } + if (gelf_getehdr(e, &ehdr) == NULL) { + warn("ERROR: gelf_getehdr() failed"); + goto err2; + } + /* + * Find the index of the STRTAB and SYMTAB sections to locate + * symbol names. + */ + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + switch (shdr.sh_type) { + case SHT_SYMTAB: + symtabscn = scn; + symtabstridx = shdr.sh_link; + break; + case SHT_DYNSYM: + dynsymscn = scn; + dynsymstridx = shdr.sh_link; + break; + default: + break; + } + } + /* + * Iterate over the Dynamic Symbols table to find the symbol. + * Then look up the string name in STRTAB (.dynstr) + */ + if ((data = elf_getdata(dynsymscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + s = elf_strptr(e, dynsymstridx, sym.st_name); + if (s && strcmp(s, symbol) == 0) { + memcpy(symcopy, &sym, sizeof(sym)); + symcopy->st_value = map->pr_vaddr + sym.st_value; + error = 0; + goto out; + } + } + /* + * Iterate over the Symbols Table to find the symbol. + * Then look up the string name in STRTAB (.dynstr) + */ + if (symtabscn == NULL) + goto err2; + if ((data = elf_getdata(symtabscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + s = elf_strptr(e, symtabstridx, sym.st_name); + if (s && strcmp(s, symbol) == 0) { + memcpy(symcopy, &sym, sizeof(sym)); + error = 0; + goto out; + } + } +out: +err2: + elf_end(e); +err1: + close(fd); +err0: + free(map); + + return (error); +} + + +int +proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, + int mask, proc_sym_f *func, void *cd) +{ + Elf *e; + int i, fd; + prmap_t *map; + Elf_Scn *scn, *foundscn = NULL; + Elf_Data *data; + GElf_Shdr shdr; + GElf_Sym sym; + unsigned long stridx = -1; + char *s; + int error = -1; + + if ((map = proc_name2map(p, object)) == NULL) + return (-1); + if ((fd = open(map->pr_mapname, O_RDONLY)) < 0) { + warn("ERROR: open %s failed", map->pr_mapname); + goto err0; + } + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + warn("ERROR: elf_begin() failed"); + goto err1; + } + /* + * Find the section we are looking for. + */ + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + if (which == PR_SYMTAB && + shdr.sh_type == SHT_SYMTAB) { + foundscn = scn; + break; + } else if (which == PR_DYNSYM && + shdr.sh_type == SHT_DYNSYM) { + foundscn = scn; + break; + } + } + if (!foundscn) + return (-1); + stridx = shdr.sh_link; + if ((data = elf_getdata(foundscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && + (mask & BIND_LOCAL) == 0) + continue; + if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL && + (mask & BIND_GLOBAL) == 0) + continue; + if (GELF_ST_BIND(sym.st_info) == STB_WEAK && + (mask & BIND_WEAK) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE && + (mask & TYPE_NOTYPE) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT && + (mask & TYPE_OBJECT) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_FUNC && + (mask & TYPE_FUNC) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_SECTION && + (mask & TYPE_SECTION) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_FILE && + (mask & TYPE_FILE) == 0) + continue; + s = elf_strptr(e, stridx, sym.st_name); + sym.st_value += map->pr_vaddr; + (*func)(cd, &sym, s); + } + error = 0; +err2: + elf_end(e); +err1: + close(fd); +err0: + free(map); + return (error); } diff --git a/lib/libproc/proc_util.c b/lib/libproc/proc_util.c index 0e7020e..9a53977 100644 --- a/lib/libproc/proc_util.c +++ b/lib/libproc/proc_util.c @@ -1,6 +1,10 @@ /*- + * Copyright (c) 2010 The FreeBSD Foundation * Copyright (c) 2008 John Birrell (jb@freebsd.org) * All rights reserved. + * + * Portions of this software were developed by Rui Paulo under sponsorship + * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,16 +30,21 @@ * $FreeBSD$ */ -#include "_libproc.h" -#include -#include +#include #include #include +#include +#include +#include #include +#include +#include +#include "_libproc.h" int proc_clearflags(struct proc_handle *phdl, int mask) { + if (phdl == NULL) return (EINVAL); @@ -44,14 +53,18 @@ proc_clearflags(struct proc_handle *phdl, int mask) return (0); } +/* + * NB: we return -1 as the Solaris libproc Psetrun() function. + */ int proc_continue(struct proc_handle *phdl) { + if (phdl == NULL) - return (EINVAL); + return (-1); if (ptrace(PT_CONTINUE, phdl->pid, (caddr_t)(uintptr_t) 1, 0) != 0) - return (errno); + return (-1); phdl->status = PS_RUN; @@ -59,13 +72,25 @@ proc_continue(struct proc_handle *phdl) } int -proc_detach(struct proc_handle *phdl) +proc_detach(struct proc_handle *phdl, int reason) { + int status; + if (phdl == NULL) return (EINVAL); - - if (ptrace(PT_DETACH, phdl->pid, 0, 0) != 0) - return (errno); + if (reason == PRELEASE_KILL) { + kill(phdl->pid, SIGKILL); + return (0); + } + if (ptrace(PT_DETACH, phdl->pid, 0, 0) != 0 && errno == ESRCH) + return (0); + if (errno == EBUSY) { + kill(phdl->pid, SIGSTOP); + waitpid(phdl->pid, &status, WUNTRACED); + ptrace(PT_DETACH, phdl->pid, 0, 0); + kill(phdl->pid, SIGCONT); + return (0); + } return (0); } @@ -73,6 +98,7 @@ proc_detach(struct proc_handle *phdl) int proc_getflags(struct proc_handle *phdl) { + if (phdl == NULL) return (-1); @@ -82,6 +108,7 @@ proc_getflags(struct proc_handle *phdl) int proc_setflags(struct proc_handle *phdl, int mask) { + if (phdl == NULL) return (EINVAL); @@ -93,41 +120,101 @@ proc_setflags(struct proc_handle *phdl, int mask) int proc_state(struct proc_handle *phdl) { + if (phdl == NULL) return (-1); return (phdl->status); } -int -proc_wait(struct proc_handle *phdl) +pid_t +proc_getpid(struct proc_handle *phdl) { - int status = 0; - struct kevent kev; if (phdl == NULL) - return (EINVAL); + return (-1); - if (kevent(phdl->kq, NULL, 0, &kev, 1, NULL) <= 0) - return (0); + return (phdl->pid); +} - switch (kev.filter) { - /* Child has exited */ - case EVFILT_PROC: /* target has exited */ +int +proc_wstatus(struct proc_handle *phdl) +{ + int status; + + if (phdl == NULL) + return (-1); + if (waitpid(phdl->pid, &status, WUNTRACED) < 0) + return (-1); + if (WIFSTOPPED(status)) + phdl->status = PS_STOP; + if (WIFEXITED(status) || WIFSIGNALED(status)) phdl->status = PS_UNDEAD; - break; - default: - break; - } + phdl->wstat = status; return (status); } -pid_t -proc_getpid(struct proc_handle *phdl) +int +proc_getwstat(struct proc_handle *phdl) { + if (phdl == NULL) return (-1); - return (phdl->pid); + return (phdl->wstat); +} + +char * +proc_signame(int sig, char *name, size_t namesz) +{ + + strlcpy(name, strsignal(sig), namesz); + + return (name); +} + +int +proc_read(struct proc_handle *phdl, char *buf, size_t size, size_t addr) +{ + struct ptrace_io_desc piod; + + if (phdl == NULL) + return (-1); + piod.piod_op = PIOD_READ_D; + piod.piod_len = size; + piod.piod_addr = (void *)buf; + piod.piod_offs = (void *)addr; + + if (ptrace(PT_IO, phdl->pid, (caddr_t)&piod, 0) < 0) + return (-1); + return (piod.piod_len); +} + +const lwpstatus_t * +proc_getlwpstatus(struct proc_handle *phdl) +{ + struct ptrace_lwpinfo lwpinfo; + lwpstatus_t *psp = &phdl->lwps; + siginfo_t *siginfo; + + if (phdl == NULL) + return (NULL); + if (ptrace(PT_LWPINFO, phdl->pid, (caddr_t)&lwpinfo,sizeof(lwpinfo)) < 0) + return (NULL); + siginfo = &lwpinfo.pl_siginfo; + if (lwpinfo.pl_event == PL_EVENT_SIGNAL && + (lwpinfo.pl_flags & PL_FLAG_SI) && + siginfo->si_signo == SIGTRAP && + (siginfo->si_code == TRAP_BRKPT || + siginfo->si_code == TRAP_TRACE)) { + psp->pr_why = PR_FAULTED; + psp->pr_what = FLTBPT; + } else if (lwpinfo.pl_flags & PL_FLAG_SCE) { + psp->pr_why = PR_SYSENTRY; + } else if (lwpinfo.pl_flags & PL_FLAG_SCX) { + psp->pr_why = PR_SYSEXIT; + } + + return (psp); } diff --git a/lib/libproc/test/Makefile b/lib/libproc/test/Makefile new file mode 100644 index 0000000..e4d33f8 --- /dev/null +++ b/lib/libproc/test/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR= t1-bkpt t2-name2map t3-name2sym + +.include diff --git a/lib/libproc/test/t1-bkpt/Makefile b/lib/libproc/test/t1-bkpt/Makefile new file mode 100644 index 0000000..fd93fdd --- /dev/null +++ b/lib/libproc/test/t1-bkpt/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +PROG= t1-bkpt + +SRCS= t1-bkpt.c + +LDADD= -lproc -lelf -lrtld_db -lutil +DPADD= ${LIBPROC} ${LIBELF} + +WITHOUT_MAN= + +.include diff --git a/lib/libproc/test/t1-bkpt/t1-bkpt.c b/lib/libproc/test/t1-bkpt/t1-bkpt.c new file mode 100644 index 0000000..6b4e2fa --- /dev/null +++ b/lib/libproc/test/t1-bkpt/t1-bkpt.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Rui Paulo under sponsorship from the + * FreeBSD Foundation. + * + * 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$ + */ +#include +#include + +#include +#include +#include +#include + +int +t1_bkpt_t() +{ + printf("TEST OK\n"); +} + +int +t1_bkpt_d() +{ + struct proc_handle *phdl; + char *targv[] = { "t1-bkpt-t", NULL}; + unsigned long saved; + + proc_create("./t1-bkpt", targv, NULL, NULL, &phdl); + proc_bkptset(phdl, (uintptr_t)t1_bkpt_t, &saved); + proc_continue(phdl); + assert(WIFSTOPPED(proc_wstatus(phdl))); + proc_bkptexec(phdl, saved); + proc_continue(phdl); + proc_wait(phdl); + proc_free(phdl); +} + + +int +main(int argc, char **argv) +{ + if (!strcmp(argv[0], "t1-bkpt-t")) + t1_bkpt_t(); + else + t1_bkpt_d(); +} + diff --git a/lib/libproc/test/t2-name2map/Makefile b/lib/libproc/test/t2-name2map/Makefile new file mode 100644 index 0000000..3dca51c --- /dev/null +++ b/lib/libproc/test/t2-name2map/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +PROG= t2-name2map + +SRCS= t2-name2map.c + +LDADD= -lproc -lelf -lrtld_db -lutil +DPADD= ${LIBPROC} ${LIBELF} + +WITHOUT_MAN= + +.include diff --git a/lib/libproc/test/t2-name2map/t2-name2map.c b/lib/libproc/test/t2-name2map/t2-name2map.c new file mode 100644 index 0000000..825963d --- /dev/null +++ b/lib/libproc/test/t2-name2map/t2-name2map.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Rui Paulo under sponsorship from the + * FreeBSD Foundation. + * + * 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$ + */ + +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + prmap_t *map = NULL; + struct proc_handle *phdl; + + proc_create("./t2-name2map", argv, NULL, NULL, &phdl); + map = proc_name2map(phdl, "ld-elf.so.1"); + assert(map); +} diff --git a/lib/libproc/test/t3-name2sym/Makefile b/lib/libproc/test/t3-name2sym/Makefile new file mode 100644 index 0000000..187f9c1 --- /dev/null +++ b/lib/libproc/test/t3-name2sym/Makefile @@ -0,0 +1,12 @@ +# $FreeBSD$ + +PROG= t3-name2sym + +SRCS= t3-name2sym.c + +LDADD= -lproc -lelf -lrtld_db -lutil +DPADD= ${LIBPROC} ${LIBELF} + +WITHOUT_MAN= + +.include diff --git a/lib/libproc/test/t3-name2sym/t3-name2sym.c b/lib/libproc/test/t3-name2sym/t3-name2sym.c new file mode 100644 index 0000000..6d90a48 --- /dev/null +++ b/lib/libproc/test/t3-name2sym/t3-name2sym.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010 The FreeBSD Foundation + * All rights reserved. + * + * This software was developed by Rui Paulo under sponsorship from the + * FreeBSD Foundation. + * + * 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$ + */ +#include +#include +#include +#include +#include + +int +main(int argc, char *argv[]) +{ + prmap_t *map = NULL; + struct proc_handle *phdl; + GElf_Sym sym; + + proc_create("./t3-name2sym", argv, NULL, NULL, &phdl); + memset(&sym, 0, sizeof(sym)); + assert(proc_name2sym(phdl, "ld-elf.so.1", "r_debug_state", &sym) == 0); + printf("0x%lx\n", sym.st_value); + assert(proc_name2sym(phdl, "t3-name2sym", "main", &sym) == 0); + printf("0x%lx\n", sym.st_value); +} -- cgit v1.1