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
59typedef struct link_args {
60	char		*la_probename;
61	int		ret_val;
62} link_args_t;
63
64typedef 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
72static int per_loadobj(void *, const tnfctl_ind_obj_info_t *, void *);
73static objlist_t *loadobj_find(tnfctl_handle_t *,
74			const tnfctl_ind_obj_info_t *);
75static tnfctl_errcode_t get_num_probes(tnfctl_handle_t *, objlist_t *, int *);
76static tnfctl_errcode_t read_probes_in_obj(tnfctl_handle_t *, objlist_t *,
77		ulong_t, ulong_t);
78static void free_obj_fields(objlist_t *);
79static tnfctl_errcode_t count_probes(char *, uintptr_t, void *,
80					tnfctl_elf_search_t *);
81static tnfctl_errcode_t read_a_probe(char *, uintptr_t, void *,
82					tnfctl_elf_search_t *);
83static tnfctl_errcode_t link_targ_obj_probes(tnfctl_handle_t *, objlist_t *);
84static 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 */
90tnfctl_errcode_t
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 */
170tnfctl_errcode_t
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 */
265void
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 */
283static void
284free_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 */
309tnfctl_errcode_t
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 */
343static int
344per_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 */
404static objlist_t *
405loadobj_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 */
419static tnfctl_errcode_t
420get_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 */
451static tnfctl_errcode_t
452read_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*/
484static tnfctl_errcode_t
485count_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*/
501static tnfctl_errcode_t
502read_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 */
599static tnfctl_errcode_t
600link_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 */
677static tnfctl_errcode_t
678unlink_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 */
733tnfctl_errcode_t
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