diff options
Diffstat (limited to 'lib/libproc')
-rw-r--r-- | lib/libproc/Makefile | 30 | ||||
-rw-r--r-- | lib/libproc/_libproc.h | 55 | ||||
-rw-r--r-- | lib/libproc/libproc.h | 149 | ||||
-rw-r--r-- | lib/libproc/proc_bkpt.c | 187 | ||||
-rw-r--r-- | lib/libproc/proc_create.c | 157 | ||||
-rw-r--r-- | lib/libproc/proc_regs.c | 121 | ||||
-rw-r--r-- | lib/libproc/proc_rtld.c | 80 | ||||
-rw-r--r-- | lib/libproc/proc_sym.c | 589 | ||||
-rw-r--r-- | lib/libproc/proc_util.c | 224 | ||||
-rw-r--r-- | lib/libproc/test/Makefile | 5 | ||||
-rw-r--r-- | lib/libproc/test/t1-bkpt/Makefile | 12 | ||||
-rw-r--r-- | lib/libproc/test/t1-bkpt/t1-bkpt.c | 71 | ||||
-rw-r--r-- | lib/libproc/test/t2-name2map/Makefile | 12 | ||||
-rw-r--r-- | lib/libproc/test/t2-name2map/t2-name2map.c | 46 | ||||
-rw-r--r-- | lib/libproc/test/t3-name2sym/Makefile | 12 | ||||
-rw-r--r-- | lib/libproc/test/t3-name2sym/t3-name2sym.c | 51 |
16 files changed, 1801 insertions, 0 deletions
diff --git a/lib/libproc/Makefile b/lib/libproc/Makefile new file mode 100644 index 0000000..4449c06 --- /dev/null +++ b/lib/libproc/Makefile @@ -0,0 +1,30 @@ +# $FreeBSD$ + +.include <bsd.own.mk> + +LIB= proc + +SRCS= proc_bkpt.c \ + proc_create.c \ + proc_regs.c \ + proc_sym.c \ + proc_rtld.c \ + proc_util.c + +INCS= libproc.h + +CFLAGS+= -I${.CURDIR} + +.if ${MK_LIBCPLUSPLUS} != "no" +LDADD+= -lcxxrt +DPADD+= ${LIBCXXRT} +.else +LDADD+= -lsupc++ +DPADD+= ${LIBSTDCPLUSPLUS} +.endif + +SHLIB_MAJOR= 2 + +WITHOUT_MAN= + +.include <bsd.lib.mk> diff --git a/lib/libproc/_libproc.h b/lib/libproc/_libproc.h new file mode 100644 index 0000000..aee1ac1 --- /dev/null +++ b/lib/libproc/_libproc.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 2008 John Birrell (jb@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$ + */ + +#include <sys/cdefs.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/event.h> +#include <sys/ptrace.h> +#include <rtld_db.h> + +#include "libproc.h" + +struct proc_handle { + pid_t pid; /* Process ID. */ + 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 new file mode 100644 index 0000000..d80e88c --- /dev/null +++ b/lib/libproc/libproc.h @@ -0,0 +1,149 @@ +/*- + * 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: + * 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 _LIBPROC_H_ +#define _LIBPROC_H_ + +#include <gelf.h> +#include <rtld_db.h> +#include <limits.h> + +struct proc_handle; + +typedef void (*proc_child_func)(void *); + +/* Values returned by proc_state(). */ +#define PS_IDLE 1 +#define PS_STOP 2 +#define PS_RUN 3 +#define PS_UNDEAD 4 +#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 + +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); +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 *); +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 *, void *, 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 + +#endif /* !_LIBPROC_H_ */ diff --git a/lib/libproc/proc_bkpt.c b/lib/libproc/proc_bkpt.c new file mode 100644 index 0000000..e16b0fc --- /dev/null +++ b/lib/libproc/proc_bkpt.c @@ -0,0 +1,187 @@ +/* + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <machine/_inttypes.h> + +#include <assert.h> +#include <err.h> +#include <stdio.h> +#include <errno.h> +#include "_libproc.h" + +#if defined(__i386__) || defined(__amd64__) +#define BREAKPOINT_INSTR 0xcc /* int 0x3 */ +#define BREAKPOINT_INSTR_SZ 1 +#elif defined(__mips__) +#define BREAKPOINT_INSTR 0xd /* break */ +#define BREAKPOINT_INSTR_SZ 4 +#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); + } + proc_wstatus(phdl); + status = proc_getwstat(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 new file mode 100644 index 0000000..9bd24a2 --- /dev/null +++ b/lib/libproc/proc_create.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 2008 John Birrell (jb@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$ + */ + +#include "_libproc.h" +#include <stdio.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> + +int +proc_attach(pid_t pid, int flags, struct proc_handle **pphdl) +{ + struct proc_handle *phdl; + int error = 0; + int status; + + if (pid == 0 || pid == getpid()) + return (EINVAL); + + /* + * Allocate memory for the process handle, a structure containing + * all things related to the process. + */ + if ((phdl = malloc(sizeof(struct proc_handle))) == NULL) + return (ENOMEM); + + memset(phdl, 0, sizeof(struct proc_handle)); + phdl->pid = pid; + phdl->flags = flags; + phdl->status = PS_RUN; + elf_version(EV_CURRENT); + + 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. */ + 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. */ + if (WIFSTOPPED(status) == 0) + DPRINTF("ERROR: child process %d status 0x%x", pid, status); + else + phdl->status = PS_STOP; + +out: + if (error) + proc_free(phdl); + else + *pphdl = phdl; + return (error); +} + +int +proc_create(const char *file, char * const *argv, proc_child_func *pcf, + void *child_arg, struct proc_handle **pphdl) +{ + struct proc_handle *phdl; + int error = 0; + int status; + pid_t pid; + + /* + * Allocate memory for the process handle, a structure containing + * all things related to the process. + */ + if ((phdl = malloc(sizeof(struct proc_handle))) == NULL) + return (ENOMEM); + + elf_version(EV_CURRENT); + + /* Fork a new process. */ + if ((pid = vfork()) == -1) + error = errno; + else if (pid == 0) { + /* The child expects to be traced. */ + if (ptrace(PT_TRACE_ME, 0, 0, 0) != 0) + _exit(1); + + if (pcf != NULL) + (*pcf)(child_arg); + + /* Execute the specified file: */ + execvp(file, argv); + + /* Couldn't execute the file. */ + _exit(2); + } else { + /* The parent owns the process handle. */ + memset(phdl, 0, sizeof(struct proc_handle)); + phdl->pid = pid; + phdl->status = PS_IDLE; + + /* Wait for the child process to stop. */ + 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) { + 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); +} + +void +proc_free(struct proc_handle *phdl) +{ + free(phdl); +} diff --git a/lib/libproc/proc_regs.c b/lib/libproc/proc_regs.c new file mode 100644 index 0000000..c299b9b --- /dev/null +++ b/lib/libproc/proc_regs.c @@ -0,0 +1,121 @@ +/* + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/ptrace.h> + +#include <err.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#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; +#elif defined(__mips__) + *regvalue = regs.r_regs[PC]; +#endif + break; + case REG_SP: +#if defined(__amd64__) + *regvalue = regs.r_rsp; +#elif defined(__i386__) + *regvalue = regs.r_esp; +#elif defined(__mips__) + *regvalue = regs.r_regs[SP]; +#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; +#elif defined(__mips__) + regs.r_regs[PC] = regvalue; +#endif + break; + case REG_SP: +#if defined(__amd64__) + regs.r_rsp = regvalue; +#elif defined(__i386__) + regs.r_esp = regvalue; +#elif defined(__mips__) + regs.r_regs[PC] = 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..2a9ed39 --- /dev/null +++ b/lib/libproc/proc_rtld.c @@ -0,0 +1,80 @@ +/* + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <rtld_db.h> +#include "libproc.h" +#include "_libproc.h" + +static int +map_iter(const rd_loadobj_t *lop, void *arg) +{ + struct proc_handle *phdl = arg; + + 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(*lop)); + + 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); + phdl->rdobjsz = 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 new file mode 100644 index 0000000..4bef7f0 --- /dev/null +++ b/lib/libproc/proc_sym.c @@ -0,0 +1,589 @@ +/*- + * 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: + * 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 <sys/types.h> +#include <sys/user.h> + +#include <assert.h> +#include <err.h> +#include <stdio.h> +#include <libgen.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <libutil.h> + +#include "_libproc.h" + +extern char *__cxa_demangle(const char *, char *, size_t *, int *); + +static void proc_rdl2prmap(rd_loadobj_t *, prmap_t *); + +static void +demangle(const char *symbol, char *buf, size_t len) +{ + char *dembuf; + size_t demlen = len; + + dembuf = malloc(len); + if (!dembuf) + goto fail; + dembuf = __cxa_demangle(symbol, dembuf, &demlen, NULL); + if (!dembuf) + goto fail; + strlcpy(buf, dembuf, len); + free(dembuf); + + return; +fail: + strlcpy(buf, symbol, len); +} + +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) +{ + 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); +} + +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]; + char last[MAXPATHLEN]; + + if (p->nobjs == 0) + return (-1); + memset(last, 0, sizeof(last)); + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + proc_rdl2prmap(rdl, &map); + basename_r(rdl->rdl_path, path); + /* + * We shouldn't call the callback twice with the same object. + * To do that we are assuming the fact that if there are + * repeated object names (i.e. different mappings for the + * same object) they occur next to each other. + */ + if (strcmp(path, last) == 0) + continue; + (*func)(cd, &map, path); + strlcpy(last, path, sizeof(last)); + } + + return (0); +} + +prmap_t * +proc_addr2map(struct proc_handle *p, uintptr_t 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 *symcopy) +{ + 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 symtab; + } + 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) { + if (s[0] == '_' && s[1] == 'Z' && s[2]) + demangle(s, name, namesz); + else + 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; + goto out; + } + } + } +symtab: + /* + * 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) { + if (s[0] == '_' && s[1] == 'Z' && s[2]) + demangle(s, name, namesz); + else + 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); +} + +prmap_t * +proc_name2map(struct proc_handle *p, const char *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 *symcopy) +{ + 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))) { + DPRINTF("ERROR: elf_getdata() failed"); + 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))) { + 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 new file mode 100644 index 0000000..089095e --- /dev/null +++ b/lib/libproc/proc_util.c @@ -0,0 +1,224 @@ +/*- + * 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: + * 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 <sys/types.h> +#include <sys/ptrace.h> +#include <sys/wait.h> +#include <err.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <signal.h> +#include <string.h> +#include "_libproc.h" + +int +proc_clearflags(struct proc_handle *phdl, int mask) +{ + + if (phdl == NULL) + return (EINVAL); + + phdl->flags &= ~mask; + + return (0); +} + +/* + * NB: we return -1 as the Solaris libproc Psetrun() function. + */ +int +proc_continue(struct proc_handle *phdl) +{ + + if (phdl == NULL) + return (-1); + + if (ptrace(PT_CONTINUE, phdl->pid, (caddr_t)(uintptr_t) 1, 0) != 0) + return (-1); + + phdl->status = PS_RUN; + + return (0); +} + +int +proc_detach(struct proc_handle *phdl, int reason) +{ + int status; + + if (phdl == NULL) + return (EINVAL); + 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); +} + +int +proc_getflags(struct proc_handle *phdl) +{ + + if (phdl == NULL) + return (-1); + + return(phdl->flags); +} + +int +proc_setflags(struct proc_handle *phdl, int mask) +{ + + if (phdl == NULL) + return (EINVAL); + + phdl->flags |= mask; + + return (0); +} + +int +proc_state(struct proc_handle *phdl) +{ + + if (phdl == NULL) + return (-1); + + return (phdl->status); +} + +pid_t +proc_getpid(struct proc_handle *phdl) +{ + + if (phdl == NULL) + return (-1); + + return (phdl->pid); +} + +int +proc_wstatus(struct proc_handle *phdl) +{ + int status; + + if (phdl == NULL) + return (-1); + if (waitpid(phdl->pid, &status, WUNTRACED) < 0) { + if (errno != EINTR) + warn("waitpid"); + return (-1); + } + if (WIFSTOPPED(status)) + phdl->status = PS_STOP; + if (WIFEXITED(status) || WIFSIGNALED(status)) + phdl->status = PS_UNDEAD; + phdl->wstat = status; + + return (phdl->status); +} + +int +proc_getwstat(struct proc_handle *phdl) +{ + + if (phdl == NULL) + return (-1); + + 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, void *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 <bsd.subdir.mk> 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 <bsd.prog.mk> 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..37a9fcf --- /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 <sys/types.h> +#include <sys/wait.h> + +#include <assert.h> +#include <stdio.h> +#include <string.h> +#include <libproc.h> + +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); + assert(proc_bkptset(phdl, (uintptr_t)t1_bkpt_t, &saved) == 0); + proc_continue(phdl); + assert(proc_wstatus(phdl) == PS_STOP); + proc_bkptexec(phdl, saved); + proc_continue(phdl); + proc_wstatus(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 <bsd.prog.mk> 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 <sys/types.h> +#include <assert.h> +#include <stdio.h> +#include <libproc.h> + +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 <bsd.prog.mk> 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..0be8653 --- /dev/null +++ b/lib/libproc/test/t3-name2sym/t3-name2sym.c @@ -0,0 +1,51 @@ +/* + * 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 <sys/types.h> +#include <assert.h> +#include <stdio.h> +#include <libproc.h> +#include <gelf.h> +#include <string.h> + +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); +} |