summaryrefslogtreecommitdiff
path: root/usr/src/lib/libtnfctl/internal.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libtnfctl/internal.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libtnfctl/internal.c')
-rw-r--r--usr/src/lib/libtnfctl/internal.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/usr/src/lib/libtnfctl/internal.c b/usr/src/lib/libtnfctl/internal.c
new file mode 100644
index 0000000000..4ea4833dfd
--- /dev/null
+++ b/usr/src/lib/libtnfctl/internal.c
@@ -0,0 +1,428 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License"). You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright (c) 1994,1999 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Implements the routines that are needed only for internal process
+ * control.
+ */
+
+#ifndef DEBUG
+#define NDEBUG 1
+#endif
+
+#include "tnfctl_int.h"
+#include "kernel_int.h"
+#include "dbg.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <link.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <sys/procfs.h>
+#include <assert.h>
+#include <dlfcn.h>
+
+static int inprocess_read(void *ignore,
+ uintptr_t addr, void *buf, size_t size);
+static int inprocess_write(void *ignore,
+ uintptr_t addr, void *buf, size_t size);
+static pid_t inprocess_getpid(void *ignore);
+static tnfctl_errcode_t inprocess_get_dtdebug(void *hndl, uintptr_t *ret_val);
+static int inprocess_loadobj_iter(void *opq, tnfctl_ind_obj_f *obj_func,
+ void *cd);
+
+/*
+ * Cause interposition on _dlclose(), dlclose(), _dlopen(), and dlopen()
+ */
+#pragma weak _dlclose = _tnfctl_dlclose
+#pragma weak dlclose = _tnfctl_dlclose
+
+#pragma weak _dlopen = _tnfctl_dlopen
+#pragma weak dlopen = _tnfctl_dlopen
+
+/*
+ * The lock used to protect the _tnfctl_internal_tracing_flag variable.
+ *
+ */
+mutex_t _tnfctl_internalguard_lock = DEFAULTMUTEX;
+boolean_t _tnfctl_internal_tracing_flag = 0;
+pid_t _tnfctl_externally_traced_pid = NOPID;
+
+/*
+ * Returns a pointer to a tnfctl handle that can do in process probe control.
+ */
+tnfctl_errcode_t
+tnfctl_internal_open(tnfctl_handle_t **ret_val)
+{
+ tnfctl_handle_t *hdl;
+ tnfctl_errcode_t prexstat;
+ uintptr_t dbgaddr;
+
+ /* allocate hdl and zero fill */
+ hdl = calloc(1, sizeof (*hdl));
+ if (hdl == NULL) {
+ return (TNFCTL_ERR_ALLOCFAIL);
+ }
+
+ hdl->mode = INTERNAL_MODE;
+ hdl->called_exit = B_FALSE;
+
+ /* plug in inprocess call back functions */
+ hdl->p_read = inprocess_read;
+ hdl->p_write = inprocess_write;
+ hdl->p_obj_iter = inprocess_loadobj_iter;
+ hdl->p_getpid = inprocess_getpid;
+
+ /*
+ * get the address of DT_DEBUG and store it in proc_p
+ * (the handle on the same process is the dbg address)
+ */
+ prexstat = inprocess_get_dtdebug(hdl, &dbgaddr);
+ if (prexstat) {
+ free(hdl);
+ return (prexstat);
+ }
+ hdl->proc_p = (void *) dbgaddr;
+
+ /* initialize state in handle */
+ prexstat = _tnfctl_set_state(hdl);
+ if (prexstat) {
+ free(hdl);
+ return (prexstat);
+ }
+ /* see if process is already being traced */
+ prexstat = _tnfctl_internal_getlock();
+ if (prexstat) {
+ free(hdl);
+ return (prexstat);
+ }
+ *ret_val = hdl;
+ return (TNFCTL_ERR_NONE);
+}
+
+/*
+ * reads a block of memory from the same address space.
+ */
+static int
+inprocess_read(void *ignore, uintptr_t addr, void *buf, size_t size)
+{
+
+ DBG_TNF_PROBE_2(inprocess_read_1, "libtnfctl", "sunw%verbosity 3;",
+ tnf_long, num_bytes, size,
+ tnf_opaque, from_address, addr);
+
+ (void) memcpy(buf, (void *) addr, size);
+ return (0);
+}
+
+/*
+ * writes a block of memory to the same address space.
+ */
+static int
+inprocess_write(void *ignore, uintptr_t addr, void *buf, size_t size)
+{
+
+ DBG_TNF_PROBE_2(inprocess_write_1, "libtnfctl", "sunw%verbosity 3;",
+ tnf_long, num_bytes, size,
+ tnf_opaque, to_address, addr);
+
+ (void) memcpy((void *)addr, buf, size);
+ return (0);
+}
+
+/*
+ * returns the pid of the process.
+ */
+static pid_t
+inprocess_getpid(void *ignore)
+{
+ return (getpid());
+}
+extern Elf3264_Dyn _DYNAMIC;
+
+/*
+ * returns the address of the DT_DEBUG field in the _DYNAMIC array
+ * of the same address space.
+ */
+static tnfctl_errcode_t
+inprocess_get_dtdebug(void *hndl, uintptr_t *ret_val)
+{
+ Elf3264_Dyn *dyn = &_DYNAMIC;
+ Elf3264_Dyn *dp;
+
+ for (dp = dyn; dp->d_tag != DT_NULL; dp++) {
+ if (dp->d_tag == DT_DEBUG) {
+ *ret_val = (uintptr_t) dp;
+ return (TNFCTL_ERR_NONE);
+ }
+ }
+ return (TNFCTL_ERR_INTERNAL);
+}
+
+#define PROCFORMAT "/proc/%d"
+
+/*
+ * iterate over all loadobjects in the same address space calling the
+ * callback function "obj_func".
+ */
+static int
+inprocess_loadobj_iter(void *opq, tnfctl_ind_obj_f *obj_func, void *cd)
+{
+ Elf3264_Dyn *dtdebug = opq;
+ struct r_debug *r_dbg;
+ struct link_map *lmap;
+ char path[MAXPATHLEN];
+ int procfd;
+ tnfctl_ind_obj_info_t loadobj;
+ int retval = 0; /* sucessful return */
+
+ DBG_TNF_PROBE_0(inprocess_loadobj_iter_start, "libtnfctl",
+ "start inprocess_loadobj_iter; sunw%verbosity 1");
+
+ r_dbg = (struct r_debug *)dtdebug->d_un.d_ptr;
+
+ DBG_TNF_PROBE_1(inprocess_loadobj_iter_1, "libtnfctl",
+ "sunw%verbosity 1",
+ tnf_string, link_map_state,
+ (r_dbg->r_state == RT_CONSISTENT) ? "RT_CONSISTENT" :
+ (r_dbg->r_state == RT_ADD) ? "RT_ADD" : "RT_DELETE");
+
+ /* bail if link map is not consistent */
+ if (r_dbg->r_state != RT_CONSISTENT)
+ return (1);
+
+ (void) sprintf(path, PROCFORMAT, (int) getpid());
+
+ /*
+ * opening /proc readonly, so debuggers can still run
+ * We use /proc in order to get fd on the object.
+ */
+ procfd = open(path, O_RDONLY);
+ if (procfd == -1)
+ return (1);
+
+ for (lmap = r_dbg->r_map; lmap; lmap = lmap->l_next) {
+ loadobj.text_base = lmap->l_addr;
+ loadobj.data_base = lmap->l_addr;
+ loadobj.objname = lmap->l_name;
+ /*
+ * client of this interface should deal with -1 for objfd,
+ * so no error checking is needed on this ioctl
+ */
+ loadobj.objfd = ioctl(procfd, PIOCOPENM, &(lmap->l_addr));
+
+ retval = obj_func(opq, &loadobj, cd);
+
+ /* close the fd */
+ if (loadobj.objfd != -1)
+ close(loadobj.objfd);
+
+ /* check for error */
+ if (retval == 1)
+ goto end_of_func;
+ }
+
+end_of_func:
+ close(procfd);
+
+ DBG_TNF_PROBE_0(inprocess_loadobj_iter_end, "libtnfctl",
+ "end inprocess_loadobj_iter; sunw%verbosity 1");
+ return (retval);
+}
+
+/*
+ * The lock that prevents a thread from accessing our cached library list
+ * and a dlopen or dlclose happening at the same time in another thread.
+ */
+mutex_t _tnfctl_lmap_lock = DEFAULTMUTEX;
+
+/*
+ * The flag that indicates that the library list has changed via a
+ * dlopen or dlclose.
+ */
+boolean_t _tnfctl_libs_changed = B_FALSE;
+
+/*
+ * Thread id of the owner of the lock in order to implement a
+ * recursive lock i.e. no deadlock if the same thread tries to lock
+ * a lock it already holds.
+ */
+static thread_t lock_holder = 0; /* XXX - no tid with 0 */
+NOTE(MUTEX_PROTECTS_DATA(warlock::lmap_lock, lock_holder))
+NOTE(DATA_READABLE_WITHOUT_LOCK(lock_holder))
+
+/*
+ * In the routines below, we will appear to use a different lock if we
+ * are running lock_lint/warlock. We define a macro to represent whichever
+ * lock is appropriate.
+ */
+#if defined(__lock_lint)
+#define LMAP_LOCK (&warlock_kludge->lmap_lock)
+#else
+#define LMAP_LOCK (&_tnfctl_lmap_lock)
+#endif
+
+/*
+ * dlclose interposition with a recursive lock so that a .fini section
+ * can recursively call dlopen or dlclose while holding _tnfctl_lmap_lock
+ * This interposition serializes access to rtld's loadobject list and
+ * also updates the flag _tnfctl_libs_changed to indicate a change in
+ * the library list. This flag is checked by operations that update
+ * probes so that it can sync up with the new library list and potential
+ * new/deleted probes.
+ */
+int
+_tnfctl_dlclose(void *handle)
+{
+ static int (*real_dlclose)(void *handle) = NULL;
+ int retval;
+ thread_t tid;
+
+ if (real_dlclose == NULL) {
+ real_dlclose = (int (*)(void *)) dlsym(RTLD_NEXT, "dlclose");
+ }
+ assert(real_dlclose);
+
+ if (mutex_trylock(LMAP_LOCK) != 0) {
+ /* don't have lock */
+ tid = thr_self();
+ if (tid == lock_holder) {
+ /* recursive dlopen/dlclose by same thread */
+ return ((*real_dlclose)(handle));
+ }
+ /* not a recursive dlopen/dlclose - wait on lock */
+ mutex_lock(LMAP_LOCK);
+ }
+
+ /* lock is held now */
+ lock_holder = thr_self();
+ retval = (*real_dlclose)(handle);
+
+ /*
+ * reset lock_holder so that if _tnfctl_lmap_lock is held by some
+ * other part of the code, we don't assume it is a recursive
+ * dlopen/dlclose
+ */
+ lock_holder = 0;
+ _tnfctl_libs_changed = B_TRUE;
+ mutex_unlock(LMAP_LOCK);
+
+ return (retval);
+}
+
+/*
+ * dlopen interposition with a recursive lock so that a .init section
+ * can recursively call dlopen or dlclose while holding _tnfctl_lmap_lock
+ * This interposition serializes access to rtld's loadobject list and
+ * also updates the flag _tnfctl_libs_changed to indicate a change in
+ * the library list. This flag is checked by operations that update
+ * probes so that it can sync up with the new library list and potential
+ * new/deleted probes.
+ */
+void *
+_tnfctl_dlopen(const char *pathname, int mode)
+{
+ static void * (*real_dlopen)(const char *, int) = NULL;
+ void *retval;
+ thread_t tid;
+
+ if (real_dlopen == NULL) {
+ real_dlopen = (void * (*)(const char *, int))
+ dlsym(RTLD_NEXT, "dlopen");
+ }
+ assert(real_dlopen);
+
+ if (mutex_trylock(LMAP_LOCK) != 0) {
+ /* don't have lock */
+ tid = thr_self();
+ if (tid == lock_holder) {
+ /* recursive dlopen/dlclose by same thread */
+ return ((*real_dlopen)(pathname, mode));
+ }
+ /* not a recursive dlopen/dlclose - wait on lock */
+ mutex_lock(LMAP_LOCK);
+ }
+
+ /* lock is held now */
+ lock_holder = thr_self();
+ retval = (*real_dlopen)(pathname, mode);
+
+ /*
+ * reset lock_holder so that if _tnfctl_lmap_lock is held by some
+ * other part of the code, we don't assume it is a recursive
+ * dlopen/dlclose
+ */
+ lock_holder = 0;
+ _tnfctl_libs_changed = B_TRUE;
+ mutex_unlock(LMAP_LOCK);
+
+ return (retval);
+}
+
+tnfctl_errcode_t
+_tnfctl_internal_getlock()
+{
+ mutex_lock(&_tnfctl_internalguard_lock);
+ if (_tnfctl_internal_tracing_flag == 1) {
+ /* internal trace control active */
+ mutex_unlock(&_tnfctl_internalguard_lock);
+ return (TNFCTL_ERR_BUSY);
+ }
+ _tnfctl_internal_tracing_flag = 1;
+ if (_tnfctl_externally_traced_pid == getpid()) {
+ /* external trace control is active */
+ _tnfctl_internal_tracing_flag = 0;
+ mutex_unlock(&_tnfctl_internalguard_lock);
+ return (TNFCTL_ERR_BUSY);
+ }
+ DBG((void) fprintf(stderr, "_tnfctl_internal_getlock: ok to trace %d\n",
+ getpid()));
+ mutex_unlock(&_tnfctl_internalguard_lock);
+ return (TNFCTL_ERR_NONE);
+}
+
+
+#ifdef __lock_lint
+
+/*
+ * dummy function for lock_lint (warlock) static lock analysis.
+ */
+int
+warlock_dummy()
+{
+ int (*fp)();
+
+ return ((*fp)());
+}
+
+#endif