svn commit: r210688 - in head/lib/libproc: . test test/t1-bkpt
test/t2-name2map test/t3-name2sym
Rui Paulo
rpaulo at FreeBSD.org
Sat Jul 31 16:10:21 UTC 2010
Author: rpaulo
Date: Sat Jul 31 16:10:20 2010
New Revision: 210688
URL: http://svn.freebsd.org/changeset/base/210688
Log:
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
Added:
head/lib/libproc/proc_bkpt.c (contents, props changed)
head/lib/libproc/proc_regs.c (contents, props changed)
head/lib/libproc/proc_rtld.c (contents, props changed)
head/lib/libproc/test/
head/lib/libproc/test/Makefile (contents, props changed)
head/lib/libproc/test/t1-bkpt/
head/lib/libproc/test/t1-bkpt/Makefile (contents, props changed)
head/lib/libproc/test/t1-bkpt/t1-bkpt.c (contents, props changed)
head/lib/libproc/test/t2-name2map/
head/lib/libproc/test/t2-name2map/Makefile (contents, props changed)
head/lib/libproc/test/t2-name2map/t2-name2map.c (contents, props changed)
head/lib/libproc/test/t3-name2sym/
head/lib/libproc/test/t3-name2sym/Makefile (contents, props changed)
head/lib/libproc/test/t3-name2sym/t3-name2sym.c (contents, props changed)
Modified:
head/lib/libproc/Makefile
head/lib/libproc/_libproc.h
head/lib/libproc/libproc.h
head/lib/libproc/proc_create.c
head/lib/libproc/proc_sym.c
head/lib/libproc/proc_util.c
Modified: head/lib/libproc/Makefile
==============================================================================
--- head/lib/libproc/Makefile Sat Jul 31 15:36:54 2010 (r210687)
+++ head/lib/libproc/Makefile Sat Jul 31 16:10:20 2010 (r210688)
@@ -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
Modified: head/lib/libproc/_libproc.h
==============================================================================
--- head/lib/libproc/_libproc.h Sat Jul 31 15:36:54 2010 (r210687)
+++ head/lib/libproc/_libproc.h Sat Jul 31 16:10:20 2010 (r210688)
@@ -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
Modified: head/lib/libproc/libproc.h
==============================================================================
--- head/lib/libproc/libproc.h Sat Jul 31 15:36:54 2010 (r210687)
+++ head/lib/libproc/libproc.h Sat Jul 31 16:10:20 2010 (r210688)
@@ -1,7 +1,11 @@
/*-
+ * Copyright (c) 2010 The FreeBSD Foundation
* Copyright (c) 2008 John Birrell (jb at 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
Added: head/lib/libproc/proc_bkpt.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/lib/libproc/proc_bkpt.c Sat Jul 31 16:10:20 2010 (r210688)
@@ -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);
+}
Modified: head/lib/libproc/proc_create.c
==============================================================================
--- head/lib/libproc/proc_create.c Sat Jul 31 15:36:54 2010 (r210687)
+++ head/lib/libproc/proc_create.c Sat Jul 31 16:10:20 2010 (r210688)
@@ -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
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_free(phdl);
else
*pphdl = phdl;
+out:
return (error);
}
@@ -94,7 +93,6 @@ proc_create(const char *file, char * con
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 * con
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 * con
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);
}
Added: head/lib/libproc/proc_regs.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/lib/libproc/proc_regs.c Sat Jul 31 16:10:20 2010 (r210688)
@@ -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(®s, 0, sizeof(regs));
+ if (ptrace(PT_GETREGS, proc_getpid(phdl), (caddr_t)®s, 0) < 0)
+ return (-1);
+ switch (reg) {
+ case REG_PC:
+#if defined(__amd64__)
+ *regvalue = regs.r_rip;
+#elif defined(__i386__)
+ *regvalue = regs.r_eip;
+#endif
+ break;
+ case REG_SP:
+#if defined(__amd64__)
+ *regvalue = regs.r_rsp;
+#elif defined(__i386__)
+ *regvalue = regs.r_esp;
+#endif
+ break;
+ default:
+ warn("ERROR: no support for reg number %d", reg);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+proc_regset(struct proc_handle *phdl, proc_reg_t reg, unsigned long regvalue)
+{
+ struct reg regs;
+
+ if (phdl->status == PS_DEAD || phdl->status == PS_UNDEAD ||
+ phdl->status == PS_IDLE) {
+ errno = ENOENT;
+ return (-1);
+ }
+ if (ptrace(PT_GETREGS, proc_getpid(phdl), (caddr_t)®s, 0) < 0)
+ return (-1);
+ switch (reg) {
+ case REG_PC:
+#if defined(__amd64__)
+ regs.r_rip = regvalue;
+#elif defined(__i386__)
+ regs.r_eip = regvalue;
+#endif
+ break;
+ case REG_SP:
+#if defined(__amd64__)
+ regs.r_rsp = regvalue;
+#elif defined(__i386__)
+ regs.r_esp = regvalue;
+#endif
+ break;
+ default:
+ warn("ERROR: no support for reg number %d", reg);
+ return (-1);
+ }
+ if (ptrace(PT_SETREGS, proc_getpid(phdl), (caddr_t)®s, 0) < 0)
+ return (-1);
+
+ return (0);
+}
Added: head/lib/libproc/proc_rtld.c
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ head/lib/libproc/proc_rtld.c Sat Jul 31 16:10:20 2010 (r210688)
@@ -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);
+}
Modified: head/lib/libproc/proc_sym.c
==============================================================================
--- head/lib/libproc/proc_sym.c Sat Jul 31 15:36:54 2010 (r210687)
+++ head/lib/libproc/proc_sym.c Sat Jul 31 16:10:20 2010 (r210688)
@@ -1,7 +1,11 @@
/*-
+ * Copyright (c) 2010 The FreeBSD Foundation
* Copyright (c) 2008 John Birrell (jb at 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;
*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
More information about the svn-src-head
mailing list