xref: /titanic_51/usr/src/lib/libtnfctl/probes.c (revision d583b39bfb4e2571d3e41097c5c357ffe353ad45)
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, by Sun Microsytems, Inc.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Load object and probe discovery in target process.  This file is
30  * not exercised for kernel probes.
31  */
32 
33 #ifndef DEBUG
34 #define	NDEBUG	1
35 #endif
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <stddef.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <fcntl.h>
45 #include <assert.h>
46 
47 #include "tnfctl_int.h"
48 #include "kernel_int.h"
49 #include "dbg.h"
50 
51 /*
52  * Defines - Project private interfaces
53  */
54 
55 #define	PROBE_SYMBOL	"__tnf_probe_version_1"
56 
57 /*
58  * Typedefs
59  */
60 
61 typedef struct link_args {
62 	char		*la_probename;
63 	int		ret_val;
64 } link_args_t;
65 
66 typedef struct link_args2 {
67 	tnfctl_handle_t	*la_hndl;
68 	char		*la_probename;
69 	objlist_t	*la_obj;
70 	ulong_t		la_index;
71 	ulong_t		la_base;
72 } link_args2_t;
73 
74 NOTE(SCHEME_PROTECTS_DATA("always automatic", link_args link_args2))
75 
76 static int per_loadobj(void *, const tnfctl_ind_obj_info_t *, void *);
77 static objlist_t *loadobj_find(tnfctl_handle_t *,
78 			const tnfctl_ind_obj_info_t *);
79 static tnfctl_errcode_t get_num_probes(tnfctl_handle_t *, objlist_t *, int *);
80 static tnfctl_errcode_t read_probes_in_obj(tnfctl_handle_t *, objlist_t *,
81 		ulong_t, ulong_t);
82 static void free_obj_fields(objlist_t *);
83 static tnfctl_errcode_t count_probes(char *, uintptr_t, void *,
84 					tnfctl_elf_search_t *);
85 static tnfctl_errcode_t read_a_probe(char *, uintptr_t, void *,
86 					tnfctl_elf_search_t *);
87 static tnfctl_errcode_t link_targ_obj_probes(tnfctl_handle_t *, objlist_t *);
88 static tnfctl_errcode_t unlink_targ_obj_probes(tnfctl_handle_t *, objlist_t *);
89 
90 /*
91  * sync up our library list with that of the run time linker's
92  * Returns an event indicating if a dlopen or dlclose happened.
93  */
94 tnfctl_errcode_t
95 _tnfctl_lmap_update(tnfctl_handle_t *hndl, boolean_t *lmap_ok,
96 				enum event_op_t *dl_evt)
97 {
98 	int		miscstat;
99 	objlist_t	*cur_obj;
100 
101 	*lmap_ok = B_TRUE;
102 
103 	/* reset old and new of current objects */
104 	for (cur_obj = hndl->objlist; cur_obj; cur_obj = cur_obj->next) {
105 		cur_obj->old = B_TRUE;
106 		cur_obj->new = B_FALSE;
107 	}
108 
109 	/* read in object list */
110 	miscstat = hndl->p_obj_iter(hndl->proc_p, per_loadobj, hndl);
111 	/* reset libs_changed global var to indicated sync up done */
112 	_tnfctl_libs_changed = B_FALSE;
113 	if (miscstat) {
114 		/*
115 		 * for INDIRECT_MODE or INTERNAL_MODE, we should never get
116 		 * called when linkmaps are not consistent, so this is a real
117 		 * error - return without setting lmap_ok.
118 		 */
119 		if ((hndl->mode == INDIRECT_MODE) ||
120 				(hndl->mode == INTERNAL_MODE))
121 			return (TNFCTL_ERR_INTERNAL);
122 
123 		assert(hndl->mode == DIRECT_MODE);
124 		/*
125 		 * in DIRECT_MODE:
126 		 * caller needs to call tnfctl_continue on BADLMAPSTATE
127 		 * XXXX - the cast from int to prb_status_t is ok as
128 		 * we know we are in DIRECT_MODE and we are calling our
129 		 * own loadobject iterator function.
130 		 */
131 		if ((prb_status_t) miscstat == PRB_STATUS_BADLMAPSTATE)
132 			*lmap_ok = B_FALSE;
133 		return (_tnfctl_map_to_errcode((prb_status_t) miscstat));
134 	}
135 
136 	/*
137 	 * find out about dlopens or dlcloses - In direct mode, there
138 	 * can only be one since we monitor all dl activity.  The dl_evt
139 	 * field is only used by tnfctl_continue().  In proc_service
140 	 * mode or internal mode, the new_probe member indicates new probes
141 	 * correctly.
142 	 */
143 	*dl_evt = EVT_NONE;
144 	for (cur_obj = hndl->objlist; cur_obj; cur_obj = cur_obj->next) {
145 		if (cur_obj->old == B_TRUE) {
146 			*dl_evt = EVT_CLOSE;
147 			break;
148 		}
149 		if (cur_obj->new == B_TRUE) {
150 			*dl_evt = EVT_OPEN;
151 			break;
152 		}
153 	}
154 
155 	/*
156 	 * reset new_probe field only if there was a dlopen or dlclose
157 	 */
158 	if (*dl_evt != EVT_NONE) {
159 		for (cur_obj = hndl->objlist; cur_obj;
160 				cur_obj = cur_obj->next) {
161 			cur_obj->new_probe = cur_obj->new;
162 		}
163 	}
164 
165 	return (TNFCTL_ERR_NONE);
166 }
167 
168 
169 /*
170  * search through all libraries and discover all probes in target
171  * This function assumes all objects have been found and marked as
172  * appropriate (new, old, or neither)
173  */
174 tnfctl_errcode_t
175 _tnfctl_find_all_probes(tnfctl_handle_t *hndl)
176 {
177 	tnfctl_errcode_t	prexstat;
178 	int		num_probes, j;
179 	objlist_t	*cur_obj, *prev_obj, *tmp_obj;
180 	boolean_t	saw_new_probes = B_FALSE;
181 
182 	prev_obj = NULL;
183 	cur_obj = hndl->objlist;
184 	while (cur_obj) {
185 		if (cur_obj->old == B_TRUE) {
186 			/* dlclosed library : stitch out probes in target */
187 
188 			DBG_TNF_PROBE_3(_tnfctl_find_all_probes_1, "libtnfctl",
189 				"sunw%verbosity 1; sunw%debug 'lib dlclosed'",
190 				tnf_opaque, lib_baseaddr, cur_obj->baseaddr,
191 				tnf_string, lib_name, cur_obj->objname,
192 				tnf_long, lib_fd, cur_obj->objfd);
193 
194 			prexstat = unlink_targ_obj_probes(hndl, cur_obj);
195 			if (prexstat)
196 				return (prexstat);
197 			free_obj_fields(cur_obj);
198 			/* remove this object from linked list */
199 			tmp_obj = cur_obj;
200 			cur_obj = cur_obj->next;
201 			if (prev_obj == NULL)
202 				hndl->objlist = cur_obj;
203 			else
204 				prev_obj->next = cur_obj;
205 			free(tmp_obj);
206 			continue;
207 		}
208 
209 		if (cur_obj->new == B_TRUE) {
210 			/* dlopened library : read in probes */
211 			prexstat = get_num_probes(hndl, cur_obj, &num_probes);
212 			if (prexstat)
213 				return (prexstat);
214 			if (num_probes) {
215 				saw_new_probes = B_TRUE;
216 				cur_obj->probes = malloc(num_probes *
217 					sizeof (prbctlref_t));
218 				if (cur_obj->probes == NULL)
219 					return (TNFCTL_ERR_ALLOCFAIL);
220 				prexstat = read_probes_in_obj(hndl, cur_obj,
221 					num_probes, hndl->num_probes);
222 				if (prexstat)
223 					return (prexstat);
224 				cur_obj->min_probe_num = hndl->num_probes;
225 				/* increment num_probes */
226 				hndl->num_probes += num_probes;
227 				cur_obj->probecnt = num_probes;
228 				prexstat = link_targ_obj_probes(hndl, cur_obj);
229 				if (prexstat)
230 					return (prexstat);
231 			}
232 		}
233 		prev_obj = cur_obj;
234 		cur_obj = cur_obj->next;
235 	}
236 
237 #if 0
238 	for (cur_obj = hndl->objlist; cur_obj; cur_obj = cur_obj->next) {
239 			(void) fprintf(stderr, "%s 0x%08x %s fd=%d\n",
240 				(cur_obj->new) ? "*" : " ",
241 				cur_obj->baseaddr, cur_obj->objname,
242 				cur_obj->objfd);
243 	}
244 #endif
245 
246 	/* call create_func for client data if we saw new probes */
247 	if (saw_new_probes && hndl->create_func) {
248 		for (cur_obj = hndl->objlist; cur_obj;
249 						cur_obj = cur_obj->next) {
250 			tnfctl_probe_t *probe_handle;
251 
252 			if (cur_obj->new == B_FALSE)
253 				continue;
254 			/* new object */
255 			for (j = 0; j < cur_obj->probecnt; j++) {
256 				probe_handle = cur_obj->probes[j].probe_handle;
257 				probe_handle->client_registered_data =
258 					hndl->create_func(hndl, probe_handle);
259 			}
260 		}
261 	}
262 
263 	return (TNFCTL_ERR_NONE);
264 }
265 
266 /*
267  * _tnfctl_free_objs_and_probes() - cleans up objects and probes
268  */
269 void
270 _tnfctl_free_objs_and_probes(tnfctl_handle_t *hndl)
271 {
272 	objlist_t *obj, *tmp;
273 
274 	NOTE(NO_COMPETING_THREADS_NOW)
275 	obj = hndl->objlist;
276 	while (obj) {
277 		free_obj_fields(obj);
278 		tmp = obj;
279 		obj = obj->next;
280 		free(tmp);
281 	}
282 	hndl->objlist = NULL;
283 	NOTE(COMPETING_THREADS_NOW)
284 }
285 
286 /*
287  * Free members of objlist_t
288  */
289 static void
290 free_obj_fields(objlist_t *obj)
291 {
292 	int i;
293 	prbctlref_t *probe_p;
294 
295 	for (i = 0; i < obj->probecnt; i++) {
296 		probe_p = &(obj->probes[i]);
297 		if (probe_p->attr_string)
298 			free(probe_p->attr_string);
299 		if (probe_p->probe_handle)
300 			probe_p->probe_handle->valid = B_FALSE;
301 	}
302 	if (obj->probes)
303 		free(obj->probes);
304 	obj->probecnt = 0;
305 	if (obj->objname)
306 		free(obj->objname);
307 	if (obj->objfd != -1)
308 		close(obj->objfd);
309 }
310 
311 /*
312  * _tnfctl_probes_traverse() - iterate over all probes by calling the
313  * callback function supplied.
314  */
315 tnfctl_errcode_t
316 _tnfctl_probes_traverse(tnfctl_handle_t *hndl,
317 	_tnfctl_traverse_probe_func_t func_p, void *calldata_p)
318 {
319 	tnfctl_errcode_t	prexstat;
320 	boolean_t	release_lock;
321 	objlist_t	*obj;
322 	int		j;
323 
324 	/*LINTED statement has no consequent: else*/
325 	LOCK_SYNC(hndl, prexstat, release_lock);
326 
327 	for (obj = hndl->objlist; obj; obj = obj->next) {
328 		for (j = 0; j < obj->probecnt; j++) {
329 			prexstat = (*func_p) (hndl, &(obj->probes[j]),
330 							calldata_p);
331 			if (prexstat) {
332 				/*LINTED statement has no consequent: else*/
333 				UNLOCK(hndl, release_lock);
334 				return (prexstat);
335 			}
336 		}
337 	}
338 
339 	/*LINTED statement has no consequent: else*/
340 	UNLOCK(hndl, release_lock);
341 
342 	return (TNFCTL_ERR_NONE);
343 }
344 
345 /*
346  * function that is called by loadobject iterator function for every
347  * loadobject.  If a new loadobject, add it to to our list.
348  */
349 static int
350 per_loadobj(void *proc_p, const tnfctl_ind_obj_info_t *obj, void *cd)
351 {
352 	tnfctl_handle_t	*hndl = cd;
353 	objlist_t	*entry_p, *cur_p, *next_p;
354 
355 	if (entry_p = loadobj_find(hndl, obj)) {
356 		/* loadobject already exists */
357 		entry_p->old = B_FALSE;
358 		/* no need to close the objfd because iterator func will */
359 
360 		/* successful return */
361 		return (0);
362 	}
363 
364 	/* add new loadobject */
365 	entry_p = calloc(1, sizeof (objlist_t));
366 
367 	entry_p->old = B_FALSE;
368 	entry_p->new = B_TRUE;
369 	entry_p->new_probe = B_TRUE;
370 	entry_p->objname = strdup(obj->objname);
371 	if (entry_p->objname == NULL)
372 		return (1);
373 	entry_p->baseaddr = obj->text_base;
374 	/* may have to actually open the fd */
375 	if (obj->objfd == -1) {
376 		entry_p->objfd = open(obj->objname, O_RDONLY);
377 		if (entry_p->objfd == -1)
378 			return (1);
379 	} else {
380 		/* dup the fd because iterator function will close it */
381 		entry_p->objfd = dup(obj->objfd);
382 		if (entry_p->objfd == -1)
383 			return (1);
384 	}
385 
386 	entry_p->min_probe_num = 0;
387 	entry_p->probecnt = 0;
388 	entry_p->probes = NULL;
389 	entry_p->next = NULL;
390 
391 	if (hndl->objlist == NULL) {
392 		hndl->objlist = entry_p;
393 	} else {
394 		/* add to end of list */
395 		next_p = hndl->objlist;
396 		while (next_p) {
397 			cur_p = next_p;
398 			next_p = next_p->next;
399 		}
400 		/* cur_p now points to last element on list */
401 		cur_p->next = entry_p;
402 	}
403 
404 	return (0);
405 }
406 
407 /*
408  * check if this loadobject already exists in our linked list.
409  */
410 static objlist_t *
411 loadobj_find(tnfctl_handle_t *hndl, const tnfctl_ind_obj_info_t *this_obj)
412 {
413 	objlist_t *obj;
414 
415 	for (obj = hndl->objlist; obj; obj = obj->next) {
416 		if (obj->baseaddr == this_obj->text_base)
417 			return (obj);
418 	}
419 	return (NULL);
420 }
421 
422 /*
423  * find the number of probes in a loadobject
424  */
425 static tnfctl_errcode_t
426 get_num_probes(tnfctl_handle_t *hndl, objlist_t *obj, int *num_probes)
427 {
428 	tnfctl_errcode_t	prexstat;
429 	link_args_t	largs;
430 	tnfctl_elf_search_t search_info;
431 
432 	DBG_TNF_PROBE_0(get_num_probes_1, "libtnfctl", "sunw%verbosity 1");
433 
434 	largs.la_probename = PROBE_SYMBOL;
435 	largs.ret_val = 0;
436 
437 	search_info.section_func = _tnfctl_traverse_rela;
438 	search_info.record_func = count_probes;
439 	search_info.record_data = &largs;
440 
441 	prexstat = _tnfctl_traverse_object(obj->objfd, obj->baseaddr,
442 						&search_info);
443 	if (prexstat)
444 		return (prexstat);
445 
446 	DBG_TNF_PROBE_2(get_num_probes_2, "libtnfctl", "sunw%verbosity 1",
447 			tnf_long, num_probes, largs.ret_val,
448 			tnf_string, obj_name, obj->objname);
449 
450 	*num_probes = largs.ret_val;
451 	return (TNFCTL_ERR_NONE);
452 }
453 
454 /*
455  * discover all probes in a loadobject and read it into our array.
456  */
457 static tnfctl_errcode_t
458 read_probes_in_obj(tnfctl_handle_t *hndl, objlist_t *obj, ulong_t num_probes,
459 			ulong_t probe_base_num)
460 {
461 	tnfctl_errcode_t	prexstat;
462 	link_args2_t	largs2;
463 	tnfctl_elf_search_t search_info;
464 
465 	DBG_TNF_PROBE_0(read_probes_in_obj_1, "libtnfctl", "sunw%verbosity 2");
466 
467 	largs2.la_hndl = hndl;
468 	largs2.la_probename = PROBE_SYMBOL;
469 	largs2.la_obj = obj;
470 	largs2.la_index = 0;
471 	largs2.la_base = probe_base_num;
472 
473 	search_info.section_func = _tnfctl_traverse_rela;
474 	search_info.record_func = read_a_probe;
475 	search_info.record_data = &largs2;
476 
477 	prexstat = _tnfctl_traverse_object(obj->objfd, obj->baseaddr,
478 						&search_info);
479 	if (prexstat)
480 		return (prexstat);
481 
482 	return (TNFCTL_ERR_NONE);
483 }
484 
485 /*
486  * checks if this relocation entry is a probe and if so,
487  * increments a counter for every probe seen
488  */
489 /*ARGSUSED*/
490 static tnfctl_errcode_t
491 count_probes(char *name, uintptr_t addr, void *rel_entry,
492 	tnfctl_elf_search_t * search_info_p)
493 {
494 	link_args_t	*largs_p = (link_args_t *) search_info_p->record_data;
495 
496 	if (strcmp(name, largs_p->la_probename) == 0) {
497 		largs_p->ret_val++;
498 	}
499 	return (TNFCTL_ERR_NONE);
500 }
501 
502 /*
503  * checks if this relocation entry is a probe and if so, reads in info
504  * on this probe
505  */
506 /*ARGSUSED*/
507 static tnfctl_errcode_t
508 read_a_probe(char *name, uintptr_t addr, void *rel_entry,
509 	tnfctl_elf_search_t * search_info_p)
510 {
511 	link_args2_t	*largs2_p = (link_args2_t *) search_info_p->record_data;
512 	ulong_t		index = largs2_p->la_index;
513 	prbctlref_t	*prbctl_p;
514 	tnfctl_handle_t	*hndl = largs2_p->la_hndl;
515 	tnfctl_errcode_t	prexstat;
516 	int		miscstat;
517 	uintptr_t	attrs;
518 
519 	assert((hndl->mode == INTERNAL_MODE) ?
520 		(MUTEX_HELD(&_tnfctl_lmap_lock)) : 1);
521 
522 	if (strcmp(name, largs2_p->la_probename) != 0)
523 		return (TNFCTL_ERR_NONE);
524 
525 	/* found a probe */
526 	prbctl_p = &(largs2_p->la_obj->probes[index]);
527 	prbctl_p->addr = addr;
528 	prbctl_p->probe_id = largs2_p->la_base + index;
529 	prbctl_p->obj = largs2_p->la_obj;
530 	largs2_p->la_index++;
531 
532 	/* read in probe structure */
533 	miscstat = hndl->p_read(hndl->proc_p, addr,
534 		&prbctl_p->wrkprbctl, sizeof (prbctl_p->wrkprbctl));
535 	if (miscstat) {
536 		DBG((void) fprintf(stderr,
537 			"read_a_probe: read from target failed: %d\n",
538 			miscstat));
539 		return (TNFCTL_ERR_INTERNAL);
540 	}
541 
542 	/*
543 	 * dereference the attrs (read it into our address space only for
544 	 * working copy)
545 	 */
546 	attrs = (uintptr_t) prbctl_p->wrkprbctl.attrs;
547 	prexstat = _tnfctl_readstr_targ(hndl, attrs, &prbctl_p->attr_string);
548 	if (prexstat) {
549 		DBG((void) fprintf(stderr,
550 		    "read_a_probe: _tnfctl_readstr_targ (attrs) failed: %s\n",
551 				tnfctl_strerror(prexstat)));
552 		return (prexstat);
553 	}
554 
555 	DBG_TNF_PROBE_1(read_a_probe_2, "libtnfctl",
556 		"sunw%verbosity 1; sunw%debug 'found a probe'",
557 		tnf_string, probe, prbctl_p->attr_string);
558 
559 	/* create probe handle */
560 	prbctl_p->probe_handle = calloc(1, sizeof (tnfctl_probe_t));
561 	if (prbctl_p->probe_handle == NULL)
562 		return (TNFCTL_ERR_ALLOCFAIL);
563 	prbctl_p->probe_handle->valid = B_TRUE;
564 	prbctl_p->probe_handle->probe_p = prbctl_p;
565 	/* link in probe handle into chain off tnfctl_handle_t */
566 	prbctl_p->probe_handle->next = hndl->probe_handle_list_head;
567 	hndl->probe_handle_list_head = prbctl_p->probe_handle;
568 
569 	/*
570 	 * if this is a "virgin" probe, set up probe to initial state
571 	 * REMIND: Could defer this target write till we link the probes
572 	 * together in target process in link_targ_obj_probes() i.e.
573 	 * do the "write" only once.
574 	 */
575 	if (prbctl_p->wrkprbctl.commit_func == NULL) {
576 		prbctl_p->wrkprbctl.probe_func =
577 				(tnf_probe_func_t) hndl->endfunc;
578 		prbctl_p->wrkprbctl.commit_func =
579 				(tnf_probe_func_t) hndl->commitfunc;
580 		prbctl_p->wrkprbctl.alloc_func =
581 				(tnf_probe_alloc_func_t) hndl->allocfunc;
582 		/*
583 		 * update the probe in target to its initial state
584 		 * Since the probe is disabled, it is ok to write it one
585 		 * write command as opposed to updating each word individually
586 		 */
587 		miscstat = hndl->p_write(hndl->proc_p, addr,
588 			&prbctl_p->wrkprbctl, sizeof (prbctl_p->wrkprbctl));
589 		if (miscstat)
590 			return (TNFCTL_ERR_INTERNAL);
591 	}
592 
593 	return (TNFCTL_ERR_NONE);
594 }
595 
596 /*
597  * Link all the probes in a linked list in the target image in specified
598  * object.  Also, link probes from previous object and next object into
599  * this list.  The only
600  * reason this is needed is because internally in the process,
601  * tnf_probe_notify() that is called from libthread walks through all
602  * probes substituting the test function
603  * REMIND: find a way that we don't have to walk through probes internally.
604  */
605 static tnfctl_errcode_t
606 link_targ_obj_probes(tnfctl_handle_t *hndl, objlist_t *cur)
607 {
608 	int i;
609 	prbctlref_t *probe_p;
610 	tnf_probe_control_t *next_probe;
611 	int miscstat;
612 	objlist_t *cur_tmp, *prev_w_probes, *next_w_probes;
613 	uintptr_t next_addr;
614 
615 	/* find previous object that has probes */
616 	prev_w_probes = NULL;
617 	cur_tmp = hndl->objlist;
618 	while (cur_tmp != cur) {
619 		if (cur_tmp->probecnt != 0)
620 			prev_w_probes = cur_tmp;
621 		cur_tmp = cur_tmp->next;
622 	}
623 
624 	/* find next object with probes */
625 	next_w_probes = NULL;
626 	cur_tmp = cur->next;
627 	while (cur_tmp != NULL) {
628 		if (cur_tmp->probecnt != 0)
629 			next_w_probes = cur_tmp;
630 		cur_tmp = cur_tmp->next;
631 	}
632 
633 	/* link probes (except for last one) in order */
634 	for (i = 0; i < (cur->probecnt - 1); i++) {
635 		probe_p = &(cur->probes[i]);
636 		next_probe = (tnf_probe_control_t *) cur->probes[i+1].addr;
637 		probe_p->wrkprbctl.next = next_probe;
638 		miscstat = hndl->p_write(hndl->proc_p, probe_p->addr +
639 				offsetof(struct tnf_probe_control, next),
640 				&next_probe, sizeof (next_probe));
641 		if (miscstat)
642 			return (TNFCTL_ERR_INTERNAL);
643 	}
644 
645 	next_probe = (tnf_probe_control_t *) cur->probes[0].addr;
646 	if (prev_w_probes == NULL) {
647 		/* adding as first object in list */
648 		next_addr = hndl->probelist_head;
649 	} else {
650 		probe_p = &(prev_w_probes->probes[prev_w_probes->probecnt - 1]);
651 		probe_p->wrkprbctl.next = next_probe;
652 		next_addr = probe_p->addr +
653 				offsetof(struct tnf_probe_control, next);
654 	}
655 
656 	/* point next_addr to first probe in this object */
657 	miscstat = hndl->p_write(hndl->proc_p, next_addr,
658 			&next_probe, sizeof (next_probe));
659 	if (miscstat)
660 		return (TNFCTL_ERR_INTERNAL);
661 
662 	/* link last probe in object */
663 	if (next_w_probes == NULL)
664 		next_probe = NULL;
665 	else {
666 		next_probe = (tnf_probe_control_t *)
667 				next_w_probes->probes[0].addr;
668 	}
669 	probe_p = &(cur->probes[cur->probecnt - 1]);
670 	probe_p->wrkprbctl.next = next_probe;
671 	miscstat = hndl->p_write(hndl->proc_p, probe_p->addr +
672 			offsetof(struct tnf_probe_control, next),
673 			&next_probe, sizeof (next_probe));
674 	if (miscstat)
675 		return (TNFCTL_ERR_INTERNAL);
676 	return (TNFCTL_ERR_NONE);
677 }
678 
679 /*
680  * An object has been closed.  Stitch probes around this object in
681  * target image.
682  */
683 static tnfctl_errcode_t
684 unlink_targ_obj_probes(tnfctl_handle_t *hndl, objlist_t *cur)
685 {
686 	prbctlref_t *probe_p;
687 	tnf_probe_control_t *next_probe;
688 	int miscstat;
689 	objlist_t *cur_tmp, *prev_w_probes, *next_w_probes;
690 	uintptr_t next_addr;
691 
692 	/* find previous object that has probes */
693 	prev_w_probes = NULL;
694 	cur_tmp = hndl->objlist;
695 	while (cur_tmp != cur) {
696 		if (cur_tmp->probecnt != 0)
697 			prev_w_probes = cur_tmp;
698 		cur_tmp = cur_tmp->next;
699 	}
700 
701 	/* find next object with probes */
702 	next_w_probes = NULL;
703 	cur_tmp = cur->next;
704 	while (cur_tmp != NULL) {
705 		if (cur_tmp->probecnt != 0)
706 			next_w_probes = cur_tmp;
707 		cur_tmp = cur_tmp->next;
708 	}
709 
710 	if (next_w_probes == NULL)
711 		next_probe = NULL;
712 	else {
713 		next_probe = (tnf_probe_control_t *)
714 				next_w_probes->probes[0].addr;
715 	}
716 
717 	if (prev_w_probes == NULL) {
718 		/* removing first object in list */
719 		next_addr = hndl->probelist_head;
720 	} else {
721 		probe_p = &(prev_w_probes->probes[prev_w_probes->probecnt - 1]);
722 		probe_p->wrkprbctl.next = next_probe;
723 		next_addr = probe_p->addr +
724 				offsetof(struct tnf_probe_control, next);
725 	}
726 
727 	/* point next_addr to next_probe */
728 	miscstat = hndl->p_write(hndl->proc_p, next_addr,
729 			&next_probe, sizeof (next_probe));
730 	if (miscstat)
731 		return (TNFCTL_ERR_INTERNAL);
732 	return (TNFCTL_ERR_NONE);
733 }
734 
735 /*
736  * _tnfctl_flush_a_probe() - write a changed probe into the target process'
737  * address space.
738  */
739 tnfctl_errcode_t
740 _tnfctl_flush_a_probe(tnfctl_handle_t *hndl, prbctlref_t *ref_p, size_t offset,
741 			size_t size)
742 {
743 	tnfctl_errcode_t	prexstat;
744 	int			miscstat;
745 
746 	/*
747 	 * For internal control:
748 	 * There is *no race* for finding the test function (between the time
749 	 * we call find_test_func() and the time we assign it to a probe),
750 	 * because tnfctl_internal_open() cannot be called from an init section
751 	 * (look at man page of tnfctl_internal_open()).  And, after the init
752 	 * section of libthread has run, we will always use the MT test
753 	 * function.
754 	 */
755 
756 	if (hndl->mode == KERNEL_MODE) {
757 		prexstat = _tnfctl_prbk_flush(hndl, ref_p);
758 		if (prexstat)
759 			return (prexstat);
760 	} else {
761 		miscstat = hndl->p_write(hndl->proc_p,
762 			ref_p->addr + offset,
763 			((char *)&(ref_p->wrkprbctl)) + offset, size);
764 		if (miscstat)
765 			return (TNFCTL_ERR_INTERNAL);
766 	}
767 
768 	return (TNFCTL_ERR_NONE);
769 }
770