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