summaryrefslogtreecommitdiffstats
path: root/lib/libproc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libproc')
-rw-r--r--lib/libproc/Makefile30
-rw-r--r--lib/libproc/_libproc.h55
-rw-r--r--lib/libproc/libproc.h149
-rw-r--r--lib/libproc/proc_bkpt.c187
-rw-r--r--lib/libproc/proc_create.c157
-rw-r--r--lib/libproc/proc_regs.c121
-rw-r--r--lib/libproc/proc_rtld.c80
-rw-r--r--lib/libproc/proc_sym.c589
-rw-r--r--lib/libproc/proc_util.c224
-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.c51
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(&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;
+#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)&regs, 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)&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..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);
+}
OpenPOWER on IntegriCloud