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