summaryrefslogtreecommitdiffstats
path: root/lib/libproc
diff options
context:
space:
mode:
authorrpaulo <rpaulo@FreeBSD.org>2010-07-31 16:10:20 +0000
committerrpaulo <rpaulo@FreeBSD.org>2010-07-31 16:10:20 +0000
commita049970caad5cf461ceb13d4b2fcd914b2af24f1 (patch)
tree30c1af86897f11bb9ea43da0c92ec31dbecd1c50 /lib/libproc
parentf5ec1d3118207f2f24806692cce1f78fff6d53d3 (diff)
downloadFreeBSD-src-a049970caad5cf461ceb13d4b2fcd914b2af24f1.zip
FreeBSD-src-a049970caad5cf461ceb13d4b2fcd914b2af24f1.tar.gz
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
Diffstat (limited to 'lib/libproc')
-rw-r--r--lib/libproc/Makefile4
-rw-r--r--lib/libproc/_libproc.h12
-rw-r--r--lib/libproc/libproc.h84
-rw-r--r--lib/libproc/proc_bkpt.c183
-rw-r--r--lib/libproc/proc_create.c61
-rw-r--r--lib/libproc/proc_regs.c113
-rw-r--r--lib/libproc/proc_rtld.c79
-rw-r--r--lib/libproc/proc_sym.c509
-rw-r--r--lib/libproc/proc_util.c139
-rw-r--r--lib/libproc/test/Makefile5
-rw-r--r--lib/libproc/test/t1-bkpt/Makefile12
-rw-r--r--lib/libproc/test/t1-bkpt/t1-bkpt.c71
-rw-r--r--lib/libproc/test/t2-name2map/Makefile12
-rw-r--r--lib/libproc/test/t2-name2map/t2-name2map.c46
-rw-r--r--lib/libproc/test/t3-name2sym/Makefile12
-rw-r--r--lib/libproc/test/t3-name2sym/t3-name2sym.c50
16 files changed, 1316 insertions, 76 deletions
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 <sys/types.h>
#include <sys/event.h>
#include <sys/ptrace.h>
+#include <rtld_db.h>
#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 <gelf.h>
+#include <rtld_db.h>
+#include <limits.h>
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 <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
+#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 <stdio.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@@ -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 <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(&regs, 0, sizeof(regs));
+ if (ptrace(PT_GETREGS, proc_getpid(phdl), (caddr_t)&regs, 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)&regs, 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)&regs, 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 <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;
+
+ 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 <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"
+
+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 <errno.h>
-#include <unistd.h>
+#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);
@@ -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 <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..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 <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);
+ 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 <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..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 <sys/types.h>
+#include <assert.h>
+#include <stdio.h>
+#include <libproc.h>
+#include <gelf.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);
+}
OpenPOWER on IntegriCloud