/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #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 #include #include #include #include #include #include #include #include #include #include #include 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() and dlopen() */ #pragma weak dlclose = _tnfctl_dlclose #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