1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright (c) 1994,1999 by Sun Microsystems, Inc. 24 * All rights reserved. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Implements the routines that are needed only for internal process 31 * control. 32 */ 33 34 #ifndef DEBUG 35 #define NDEBUG 1 36 #endif 37 38 #include "tnfctl_int.h" 39 #include "kernel_int.h" 40 #include "dbg.h" 41 42 #include <stdio.h> 43 #include <sys/types.h> 44 #include <stdlib.h> 45 #include <unistd.h> 46 #include <string.h> 47 #include <link.h> 48 #include <sys/stat.h> 49 #include <fcntl.h> 50 #include <sys/param.h> 51 #include <sys/procfs.h> 52 #include <assert.h> 53 #include <dlfcn.h> 54 55 static int inprocess_read(void *ignore, 56 uintptr_t addr, void *buf, size_t size); 57 static int inprocess_write(void *ignore, 58 uintptr_t addr, void *buf, size_t size); 59 static pid_t inprocess_getpid(void *ignore); 60 static tnfctl_errcode_t inprocess_get_dtdebug(void *hndl, uintptr_t *ret_val); 61 static int inprocess_loadobj_iter(void *opq, tnfctl_ind_obj_f *obj_func, 62 void *cd); 63 64 /* 65 * Cause interposition on _dlclose(), dlclose(), _dlopen(), and dlopen() 66 */ 67 #pragma weak _dlclose = _tnfctl_dlclose 68 #pragma weak dlclose = _tnfctl_dlclose 69 70 #pragma weak _dlopen = _tnfctl_dlopen 71 #pragma weak dlopen = _tnfctl_dlopen 72 73 /* 74 * The lock used to protect the _tnfctl_internal_tracing_flag variable. 75 * 76 */ 77 mutex_t _tnfctl_internalguard_lock = DEFAULTMUTEX; 78 boolean_t _tnfctl_internal_tracing_flag = 0; 79 pid_t _tnfctl_externally_traced_pid = NOPID; 80 81 /* 82 * Returns a pointer to a tnfctl handle that can do in process probe control. 83 */ 84 tnfctl_errcode_t 85 tnfctl_internal_open(tnfctl_handle_t **ret_val) 86 { 87 tnfctl_handle_t *hdl; 88 tnfctl_errcode_t prexstat; 89 uintptr_t dbgaddr; 90 91 /* allocate hdl and zero fill */ 92 hdl = calloc(1, sizeof (*hdl)); 93 if (hdl == NULL) { 94 return (TNFCTL_ERR_ALLOCFAIL); 95 } 96 97 hdl->mode = INTERNAL_MODE; 98 hdl->called_exit = B_FALSE; 99 100 /* plug in inprocess call back functions */ 101 hdl->p_read = inprocess_read; 102 hdl->p_write = inprocess_write; 103 hdl->p_obj_iter = inprocess_loadobj_iter; 104 hdl->p_getpid = inprocess_getpid; 105 106 /* 107 * get the address of DT_DEBUG and store it in proc_p 108 * (the handle on the same process is the dbg address) 109 */ 110 prexstat = inprocess_get_dtdebug(hdl, &dbgaddr); 111 if (prexstat) { 112 free(hdl); 113 return (prexstat); 114 } 115 hdl->proc_p = (void *) dbgaddr; 116 117 /* initialize state in handle */ 118 prexstat = _tnfctl_set_state(hdl); 119 if (prexstat) { 120 free(hdl); 121 return (prexstat); 122 } 123 /* see if process is already being traced */ 124 prexstat = _tnfctl_internal_getlock(); 125 if (prexstat) { 126 free(hdl); 127 return (prexstat); 128 } 129 *ret_val = hdl; 130 return (TNFCTL_ERR_NONE); 131 } 132 133 /* 134 * reads a block of memory from the same address space. 135 */ 136 static int 137 inprocess_read(void *ignore, uintptr_t addr, void *buf, size_t size) 138 { 139 140 DBG_TNF_PROBE_2(inprocess_read_1, "libtnfctl", "sunw%verbosity 3;", 141 tnf_long, num_bytes, size, 142 tnf_opaque, from_address, addr); 143 144 (void) memcpy(buf, (void *) addr, size); 145 return (0); 146 } 147 148 /* 149 * writes a block of memory to the same address space. 150 */ 151 static int 152 inprocess_write(void *ignore, uintptr_t addr, void *buf, size_t size) 153 { 154 155 DBG_TNF_PROBE_2(inprocess_write_1, "libtnfctl", "sunw%verbosity 3;", 156 tnf_long, num_bytes, size, 157 tnf_opaque, to_address, addr); 158 159 (void) memcpy((void *)addr, buf, size); 160 return (0); 161 } 162 163 /* 164 * returns the pid of the process. 165 */ 166 static pid_t 167 inprocess_getpid(void *ignore) 168 { 169 return (getpid()); 170 } 171 extern Elf3264_Dyn _DYNAMIC; 172 173 /* 174 * returns the address of the DT_DEBUG field in the _DYNAMIC array 175 * of the same address space. 176 */ 177 static tnfctl_errcode_t 178 inprocess_get_dtdebug(void *hndl, uintptr_t *ret_val) 179 { 180 Elf3264_Dyn *dyn = &_DYNAMIC; 181 Elf3264_Dyn *dp; 182 183 for (dp = dyn; dp->d_tag != DT_NULL; dp++) { 184 if (dp->d_tag == DT_DEBUG) { 185 *ret_val = (uintptr_t) dp; 186 return (TNFCTL_ERR_NONE); 187 } 188 } 189 return (TNFCTL_ERR_INTERNAL); 190 } 191 192 #define PROCFORMAT "/proc/%d" 193 194 /* 195 * iterate over all loadobjects in the same address space calling the 196 * callback function "obj_func". 197 */ 198 static int 199 inprocess_loadobj_iter(void *opq, tnfctl_ind_obj_f *obj_func, void *cd) 200 { 201 Elf3264_Dyn *dtdebug = opq; 202 struct r_debug *r_dbg; 203 struct link_map *lmap; 204 char path[MAXPATHLEN]; 205 int procfd; 206 tnfctl_ind_obj_info_t loadobj; 207 int retval = 0; /* sucessful return */ 208 209 DBG_TNF_PROBE_0(inprocess_loadobj_iter_start, "libtnfctl", 210 "start inprocess_loadobj_iter; sunw%verbosity 1"); 211 212 r_dbg = (struct r_debug *)dtdebug->d_un.d_ptr; 213 214 DBG_TNF_PROBE_1(inprocess_loadobj_iter_1, "libtnfctl", 215 "sunw%verbosity 1", 216 tnf_string, link_map_state, 217 (r_dbg->r_state == RT_CONSISTENT) ? "RT_CONSISTENT" : 218 (r_dbg->r_state == RT_ADD) ? "RT_ADD" : "RT_DELETE"); 219 220 /* bail if link map is not consistent */ 221 if (r_dbg->r_state != RT_CONSISTENT) 222 return (1); 223 224 (void) sprintf(path, PROCFORMAT, (int) getpid()); 225 226 /* 227 * opening /proc readonly, so debuggers can still run 228 * We use /proc in order to get fd on the object. 229 */ 230 procfd = open(path, O_RDONLY); 231 if (procfd == -1) 232 return (1); 233 234 for (lmap = r_dbg->r_map; lmap; lmap = lmap->l_next) { 235 loadobj.text_base = lmap->l_addr; 236 loadobj.data_base = lmap->l_addr; 237 loadobj.objname = lmap->l_name; 238 /* 239 * client of this interface should deal with -1 for objfd, 240 * so no error checking is needed on this ioctl 241 */ 242 loadobj.objfd = ioctl(procfd, PIOCOPENM, &(lmap->l_addr)); 243 244 retval = obj_func(opq, &loadobj, cd); 245 246 /* close the fd */ 247 if (loadobj.objfd != -1) 248 close(loadobj.objfd); 249 250 /* check for error */ 251 if (retval == 1) 252 goto end_of_func; 253 } 254 255 end_of_func: 256 close(procfd); 257 258 DBG_TNF_PROBE_0(inprocess_loadobj_iter_end, "libtnfctl", 259 "end inprocess_loadobj_iter; sunw%verbosity 1"); 260 return (retval); 261 } 262 263 /* 264 * The lock that prevents a thread from accessing our cached library list 265 * and a dlopen or dlclose happening at the same time in another thread. 266 */ 267 mutex_t _tnfctl_lmap_lock = DEFAULTMUTEX; 268 269 /* 270 * The flag that indicates that the library list has changed via a 271 * dlopen or dlclose. 272 */ 273 boolean_t _tnfctl_libs_changed = B_FALSE; 274 275 /* 276 * Thread id of the owner of the lock in order to implement a 277 * recursive lock i.e. no deadlock if the same thread tries to lock 278 * a lock it already holds. 279 */ 280 static thread_t lock_holder = 0; /* XXX - no tid with 0 */ 281 NOTE(MUTEX_PROTECTS_DATA(warlock::lmap_lock, lock_holder)) 282 NOTE(DATA_READABLE_WITHOUT_LOCK(lock_holder)) 283 284 /* 285 * In the routines below, we will appear to use a different lock if we 286 * are running lock_lint/warlock. We define a macro to represent whichever 287 * lock is appropriate. 288 */ 289 #if defined(__lock_lint) 290 #define LMAP_LOCK (&warlock_kludge->lmap_lock) 291 #else 292 #define LMAP_LOCK (&_tnfctl_lmap_lock) 293 #endif 294 295 /* 296 * dlclose interposition with a recursive lock so that a .fini section 297 * can recursively call dlopen or dlclose while holding _tnfctl_lmap_lock 298 * This interposition serializes access to rtld's loadobject list and 299 * also updates the flag _tnfctl_libs_changed to indicate a change in 300 * the library list. This flag is checked by operations that update 301 * probes so that it can sync up with the new library list and potential 302 * new/deleted probes. 303 */ 304 int 305 _tnfctl_dlclose(void *handle) 306 { 307 static int (*real_dlclose)(void *handle) = NULL; 308 int retval; 309 thread_t tid; 310 311 if (real_dlclose == NULL) { 312 real_dlclose = (int (*)(void *)) dlsym(RTLD_NEXT, "dlclose"); 313 } 314 assert(real_dlclose); 315 316 if (mutex_trylock(LMAP_LOCK) != 0) { 317 /* don't have lock */ 318 tid = thr_self(); 319 if (tid == lock_holder) { 320 /* recursive dlopen/dlclose by same thread */ 321 return ((*real_dlclose)(handle)); 322 } 323 /* not a recursive dlopen/dlclose - wait on lock */ 324 mutex_lock(LMAP_LOCK); 325 } 326 327 /* lock is held now */ 328 lock_holder = thr_self(); 329 retval = (*real_dlclose)(handle); 330 331 /* 332 * reset lock_holder so that if _tnfctl_lmap_lock is held by some 333 * other part of the code, we don't assume it is a recursive 334 * dlopen/dlclose 335 */ 336 lock_holder = 0; 337 _tnfctl_libs_changed = B_TRUE; 338 mutex_unlock(LMAP_LOCK); 339 340 return (retval); 341 } 342 343 /* 344 * dlopen interposition with a recursive lock so that a .init section 345 * can recursively call dlopen or dlclose while holding _tnfctl_lmap_lock 346 * This interposition serializes access to rtld's loadobject list and 347 * also updates the flag _tnfctl_libs_changed to indicate a change in 348 * the library list. This flag is checked by operations that update 349 * probes so that it can sync up with the new library list and potential 350 * new/deleted probes. 351 */ 352 void * 353 _tnfctl_dlopen(const char *pathname, int mode) 354 { 355 static void * (*real_dlopen)(const char *, int) = NULL; 356 void *retval; 357 thread_t tid; 358 359 if (real_dlopen == NULL) { 360 real_dlopen = (void * (*)(const char *, int)) 361 dlsym(RTLD_NEXT, "dlopen"); 362 } 363 assert(real_dlopen); 364 365 if (mutex_trylock(LMAP_LOCK) != 0) { 366 /* don't have lock */ 367 tid = thr_self(); 368 if (tid == lock_holder) { 369 /* recursive dlopen/dlclose by same thread */ 370 return ((*real_dlopen)(pathname, mode)); 371 } 372 /* not a recursive dlopen/dlclose - wait on lock */ 373 mutex_lock(LMAP_LOCK); 374 } 375 376 /* lock is held now */ 377 lock_holder = thr_self(); 378 retval = (*real_dlopen)(pathname, mode); 379 380 /* 381 * reset lock_holder so that if _tnfctl_lmap_lock is held by some 382 * other part of the code, we don't assume it is a recursive 383 * dlopen/dlclose 384 */ 385 lock_holder = 0; 386 _tnfctl_libs_changed = B_TRUE; 387 mutex_unlock(LMAP_LOCK); 388 389 return (retval); 390 } 391 392 tnfctl_errcode_t 393 _tnfctl_internal_getlock() 394 { 395 mutex_lock(&_tnfctl_internalguard_lock); 396 if (_tnfctl_internal_tracing_flag == 1) { 397 /* internal trace control active */ 398 mutex_unlock(&_tnfctl_internalguard_lock); 399 return (TNFCTL_ERR_BUSY); 400 } 401 _tnfctl_internal_tracing_flag = 1; 402 if (_tnfctl_externally_traced_pid == getpid()) { 403 /* external trace control is active */ 404 _tnfctl_internal_tracing_flag = 0; 405 mutex_unlock(&_tnfctl_internalguard_lock); 406 return (TNFCTL_ERR_BUSY); 407 } 408 DBG((void) fprintf(stderr, "_tnfctl_internal_getlock: ok to trace %d\n", 409 getpid())); 410 mutex_unlock(&_tnfctl_internalguard_lock); 411 return (TNFCTL_ERR_NONE); 412 } 413 414 415 #ifdef __lock_lint 416 417 /* 418 * dummy function for lock_lint (warlock) static lock analysis. 419 */ 420 int 421 warlock_dummy() 422 { 423 int (*fp)(); 424 425 return ((*fp)()); 426 } 427 428 #endif 429