diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libtnfctl/internal.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libtnfctl/internal.c')
-rw-r--r-- | usr/src/lib/libtnfctl/internal.c | 428 |
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 |