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
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.
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]
23 * Copyright (c) 1994, by Sun Microsytems, Inc.
27 * Load object and probe discovery in target process. This file is
28 * not exercised for kernel probes.
40 #include <sys/types.h>
45 #include "tnfctl_int.h"
46 #include "kernel_int.h"
50 * Defines - Project private interfaces
53 #define PROBE_SYMBOL "__tnf_probe_version_1"
59 typedef struct link_args
{
64 typedef struct link_args2
{
65 tnfctl_handle_t
*la_hndl
;
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
*,
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
*);
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.
91 _tnfctl_lmap_update(tnfctl_handle_t
*hndl
, boolean_t
*lmap_ok
,
92 enum event_op_t
*dl_evt
)
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
;
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
;
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.
115 if ((hndl
->mode
== INDIRECT_MODE
) ||
116 (hndl
->mode
== INTERNAL_MODE
))
117 return (TNFCTL_ERR_INTERNAL
);
119 assert(hndl
->mode
== 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.
127 if ((prb_status_t
) miscstat
== PRB_STATUS_BADLMAPSTATE
)
129 return (_tnfctl_map_to_errcode((prb_status_t
) miscstat
));
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
140 for (cur_obj
= hndl
->objlist
; cur_obj
; cur_obj
= cur_obj
->next
) {
141 if (cur_obj
->old
== B_TRUE
) {
145 if (cur_obj
->new == B_TRUE
) {
152 * reset new_probe field only if there was a dlopen or dlclose
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;
161 return (TNFCTL_ERR_NONE
);
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)
171 _tnfctl_find_all_probes(tnfctl_handle_t
*hndl
)
173 tnfctl_errcode_t prexstat
;
175 objlist_t
*cur_obj
, *prev_obj
, *tmp_obj
;
176 boolean_t saw_new_probes
= B_FALSE
;
179 cur_obj
= hndl
->objlist
;
181 if (cur_obj
->old
== B_TRUE
) {
182 /* dlclosed library : stitch out probes in target */
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
);
190 prexstat
= unlink_targ_obj_probes(hndl
, cur_obj
);
193 free_obj_fields(cur_obj
);
194 /* remove this object from linked list */
196 cur_obj
= cur_obj
->next
;
197 if (prev_obj
== NULL
)
198 hndl
->objlist
= cur_obj
;
200 prev_obj
->next
= cur_obj
;
205 if (cur_obj
->new == B_TRUE
) {
206 /* dlopened library : read in probes */
207 prexstat
= get_num_probes(hndl
, cur_obj
, &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
);
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
);
230 cur_obj
= cur_obj
->next
;
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
,
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
;
248 if (cur_obj
->new == B_FALSE
)
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
);
259 return (TNFCTL_ERR_NONE
);
263 * _tnfctl_free_objs_and_probes() - cleans up objects and probes
266 _tnfctl_free_objs_and_probes(tnfctl_handle_t
*hndl
)
268 objlist_t
*obj
, *tmp
;
272 free_obj_fields(obj
);
277 hndl
->objlist
= NULL
;
281 * Free members of objlist_t
284 free_obj_fields(objlist_t
*obj
)
287 prbctlref_t
*probe_p
;
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
;
301 if (obj
->objfd
!= -1)
306 * _tnfctl_probes_traverse() - iterate over all probes by calling the
307 * callback function supplied.
310 _tnfctl_probes_traverse(tnfctl_handle_t
*hndl
,
311 _tnfctl_traverse_probe_func_t func_p
, void *calldata_p
)
313 tnfctl_errcode_t prexstat
;
314 boolean_t release_lock
;
318 /*LINTED statement has no consequent: else*/
319 LOCK_SYNC(hndl
, prexstat
, release_lock
);
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
]),
326 /*LINTED statement has no consequent: else*/
327 UNLOCK(hndl
, release_lock
);
333 /*LINTED statement has no consequent: else*/
334 UNLOCK(hndl
, release_lock
);
336 return (TNFCTL_ERR_NONE
);
340 * function that is called by loadobject iterator function for every
341 * loadobject. If a new loadobject, add it to to our list.
344 per_loadobj(void *proc_p
, const tnfctl_ind_obj_info_t
*obj
, void *cd
)
346 tnfctl_handle_t
*hndl
= cd
;
347 objlist_t
*entry_p
, *cur_p
, *next_p
;
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 */
354 /* successful return */
358 /* add new loadobject */
359 entry_p
= calloc(1, sizeof (objlist_t
));
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
)
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)
374 /* dup the fd because iterator function will close it */
375 entry_p
->objfd
= dup(obj
->objfd
);
376 if (entry_p
->objfd
== -1)
380 entry_p
->min_probe_num
= 0;
381 entry_p
->probecnt
= 0;
382 entry_p
->probes
= NULL
;
383 entry_p
->next
= NULL
;
385 if (hndl
->objlist
== NULL
) {
386 hndl
->objlist
= entry_p
;
388 /* add to end of list */
389 next_p
= hndl
->objlist
;
392 next_p
= next_p
->next
;
394 /* cur_p now points to last element on list */
395 cur_p
->next
= entry_p
;
402 * check if this loadobject already exists in our linked list.
405 loadobj_find(tnfctl_handle_t
*hndl
, const tnfctl_ind_obj_info_t
*this_obj
)
409 for (obj
= hndl
->objlist
; obj
; obj
= obj
->next
) {
410 if (obj
->baseaddr
== this_obj
->text_base
)
417 * find the number of probes in a loadobject
419 static tnfctl_errcode_t
420 get_num_probes(tnfctl_handle_t
*hndl
, objlist_t
*obj
, int *num_probes
)
422 tnfctl_errcode_t prexstat
;
424 tnfctl_elf_search_t search_info
;
426 DBG_TNF_PROBE_0(get_num_probes_1
, "libtnfctl", "sunw%verbosity 1");
428 largs
.la_probename
= PROBE_SYMBOL
;
431 search_info
.section_func
= _tnfctl_traverse_rela
;
432 search_info
.record_func
= count_probes
;
433 search_info
.record_data
= &largs
;
435 prexstat
= _tnfctl_traverse_object(obj
->objfd
, obj
->baseaddr
,
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
);
444 *num_probes
= largs
.ret_val
;
445 return (TNFCTL_ERR_NONE
);
449 * discover all probes in a loadobject and read it into our array.
451 static tnfctl_errcode_t
452 read_probes_in_obj(tnfctl_handle_t
*hndl
, objlist_t
*obj
, ulong_t num_probes
,
453 ulong_t probe_base_num
)
455 tnfctl_errcode_t prexstat
;
457 tnfctl_elf_search_t search_info
;
459 DBG_TNF_PROBE_0(read_probes_in_obj_1
, "libtnfctl", "sunw%verbosity 2");
461 largs2
.la_hndl
= hndl
;
462 largs2
.la_probename
= PROBE_SYMBOL
;
465 largs2
.la_base
= probe_base_num
;
467 search_info
.section_func
= _tnfctl_traverse_rela
;
468 search_info
.record_func
= read_a_probe
;
469 search_info
.record_data
= &largs2
;
471 prexstat
= _tnfctl_traverse_object(obj
->objfd
, obj
->baseaddr
,
476 return (TNFCTL_ERR_NONE
);
480 * checks if this relocation entry is a probe and if so,
481 * increments a counter for every probe seen
484 static tnfctl_errcode_t
485 count_probes(char *name
, uintptr_t addr
, void *rel_entry
,
486 tnfctl_elf_search_t
* search_info_p
)
488 link_args_t
*largs_p
= (link_args_t
*) search_info_p
->record_data
;
490 if (strcmp(name
, largs_p
->la_probename
) == 0) {
493 return (TNFCTL_ERR_NONE
);
497 * checks if this relocation entry is a probe and if so, reads in info
501 static tnfctl_errcode_t
502 read_a_probe(char *name
, uintptr_t addr
, void *rel_entry
,
503 tnfctl_elf_search_t
* search_info_p
)
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
;
513 assert((hndl
->mode
== INTERNAL_MODE
) ?
514 (MUTEX_HELD(&_tnfctl_lmap_lock
)) : 1);
516 if (strcmp(name
, largs2_p
->la_probename
) != 0)
517 return (TNFCTL_ERR_NONE
);
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
++;
526 /* read in probe structure */
527 miscstat
= hndl
->p_read(hndl
->proc_p
, addr
,
528 &prbctl_p
->wrkprbctl
, sizeof (prbctl_p
->wrkprbctl
));
530 DBG((void) fprintf(stderr
,
531 "read_a_probe: read from target failed: %d\n",
533 return (TNFCTL_ERR_INTERNAL
);
537 * dereference the attrs (read it into our address space only for
540 attrs
= (uintptr_t) prbctl_p
->wrkprbctl
.attrs
;
541 prexstat
= _tnfctl_readstr_targ(hndl
, attrs
, &prbctl_p
->attr_string
);
543 DBG((void) fprintf(stderr
,
544 "read_a_probe: _tnfctl_readstr_targ (attrs) failed: %s\n",
545 tnfctl_strerror(prexstat
)));
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
);
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
;
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.
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
;
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
581 miscstat
= hndl
->p_write(hndl
->proc_p
, addr
,
582 &prbctl_p
->wrkprbctl
, sizeof (prbctl_p
->wrkprbctl
));
584 return (TNFCTL_ERR_INTERNAL
);
587 return (TNFCTL_ERR_NONE
);
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.
599 static tnfctl_errcode_t
600 link_targ_obj_probes(tnfctl_handle_t
*hndl
, objlist_t
*cur
)
603 prbctlref_t
*probe_p
;
604 tnf_probe_control_t
*next_probe
;
606 objlist_t
*cur_tmp
, *prev_w_probes
, *next_w_probes
;
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
;
618 /* find next object with probes */
619 next_w_probes
= NULL
;
621 while (cur_tmp
!= NULL
) {
622 if (cur_tmp
->probecnt
!= 0)
623 next_w_probes
= cur_tmp
;
624 cur_tmp
= cur_tmp
->next
;
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
));
636 return (TNFCTL_ERR_INTERNAL
);
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
;
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
);
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
));
654 return (TNFCTL_ERR_INTERNAL
);
656 /* link last probe in object */
657 if (next_w_probes
== NULL
)
660 next_probe
= (tnf_probe_control_t
*)
661 next_w_probes
->probes
[0].addr
;
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
));
669 return (TNFCTL_ERR_INTERNAL
);
670 return (TNFCTL_ERR_NONE
);
674 * An object has been closed. Stitch probes around this object in
677 static tnfctl_errcode_t
678 unlink_targ_obj_probes(tnfctl_handle_t
*hndl
, objlist_t
*cur
)
680 prbctlref_t
*probe_p
;
681 tnf_probe_control_t
*next_probe
;
683 objlist_t
*cur_tmp
, *prev_w_probes
, *next_w_probes
;
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
;
695 /* find next object with probes */
696 next_w_probes
= NULL
;
698 while (cur_tmp
!= NULL
) {
699 if (cur_tmp
->probecnt
!= 0)
700 next_w_probes
= cur_tmp
;
701 cur_tmp
= cur_tmp
->next
;
704 if (next_w_probes
== NULL
)
707 next_probe
= (tnf_probe_control_t
*)
708 next_w_probes
->probes
[0].addr
;
711 if (prev_w_probes
== NULL
) {
712 /* removing first object in list */
713 next_addr
= hndl
->probelist_head
;
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
);
721 /* point next_addr to next_probe */
722 miscstat
= hndl
->p_write(hndl
->proc_p
, next_addr
,
723 &next_probe
, sizeof (next_probe
));
725 return (TNFCTL_ERR_INTERNAL
);
726 return (TNFCTL_ERR_NONE
);
730 * _tnfctl_flush_a_probe() - write a changed probe into the target process'
734 _tnfctl_flush_a_probe(tnfctl_handle_t
*hndl
, prbctlref_t
*ref_p
, size_t offset
,
737 tnfctl_errcode_t prexstat
;
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
750 if (hndl
->mode
== KERNEL_MODE
) {
751 prexstat
= _tnfctl_prbk_flush(hndl
, ref_p
);
755 miscstat
= hndl
->p_write(hndl
->proc_p
,
756 ref_p
->addr
+ offset
,
757 ((char *)&(ref_p
->wrkprbctl
)) + offset
, size
);
759 return (TNFCTL_ERR_INTERNAL
);
762 return (TNFCTL_ERR_NONE
);