xref: /titanic_50/usr/src/lib/libtnfctl/comb.c (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
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  * Functions that know how to create and decode combinations that are
30  * used for connecting probe functions.
31  */
32 
33 #ifndef DEBUG
34 #define	NDEBUG	1
35 #endif
36 
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <search.h>
41 #include <assert.h>
42 #include <sys/types.h>
43 
44 #include "tnfctl_int.h"
45 #include "dbg.h"
46 
47 
48 /*
49  * Typedefs
50  */
51 
52 typedef struct comb_callinfo {
53 	unsigned	offset;
54 	unsigned	shift;	/* shift right <n> bits */
55 	unsigned	mask;
56 
57 } comb_callinfo_t;
58 
59 typedef struct comb_calltmpl {
60 	uintptr_t	entry;
61 	uintptr_t	down;
62 	uintptr_t	next;
63 	uintptr_t	end;
64 
65 } comb_calltmpl_t;
66 
67 typedef struct comb_key {
68 	comb_op_t	op;
69 	uintptr_t	down;
70 	uintptr_t	next;
71 	uintptr_t	comb;
72 } comb_key_t;
73 
74 typedef struct decode_key {
75 	uintptr_t	addr;
76 	char		**name_ptrs;
77 	uintptr_t	*func_addrs;
78 } decode_key_t;
79 
80 
81 /*
82  * Global - defined in assembler file
83  */
84 extern comb_callinfo_t prb_callinfo;
85 
86 extern void	 prb_chain_entry(void);
87 extern void	 prb_chain_down(void);
88 extern void	 prb_chain_next(void);
89 extern void	 prb_chain_end(void);
90 
91 static comb_calltmpl_t calltmpl[PRB_COMB_COUNT] = {
92 {
93 		(uintptr_t) prb_chain_entry,
94 		(uintptr_t) prb_chain_down,
95 		(uintptr_t) prb_chain_next,
96 		(uintptr_t) prb_chain_end}
97 };
98 
99 /*
100  * Declarations
101  */
102 
103 static tnfctl_errcode_t decode(tnfctl_handle_t *hndl, uintptr_t addr,
104 	char ***func_names, uintptr_t **func_addrs);
105 static boolean_t find(tnfctl_handle_t *hndl, comb_op_t op, uintptr_t down,
106 	uintptr_t next, uintptr_t * comb_p);
107 static tnfctl_errcode_t build(tnfctl_handle_t *hndl, comb_op_t op,
108 	uintptr_t down, uintptr_t next, uintptr_t * comb_p);
109 static tnfctl_errcode_t add(tnfctl_handle_t *hndl, comb_op_t op, uintptr_t down,
110 	uintptr_t next, uintptr_t comb);
111 static int comb_compare(const void *a, const void *b);
112 static int decode_compare(const void *v0p, const void *v1p);
113 static tnfctl_errcode_t iscomb(tnfctl_handle_t *hndl, uintptr_t addr,
114 	uintptr_t *down_p, uintptr_t *next_p, boolean_t *ret_val);
115 static tnfctl_errcode_t findname(tnfctl_handle_t *hndl, uintptr_t addr,
116 	char **ret_name);
117 
118 
119 /* ---------------------------------------------------------------- */
120 /* ----------------------- Public Functions ----------------------- */
121 /* ---------------------------------------------------------------- */
122 
123 /*
124  * _tnfctl_comb_build() - finds (or builds) a combination satisfing the op,
125  * down and next constraints of the caller.
126  */
127 tnfctl_errcode_t
128 _tnfctl_comb_build(tnfctl_handle_t *hndl, comb_op_t op,
129 	uintptr_t down, uintptr_t next, uintptr_t *comb_p)
130 {
131 	tnfctl_errcode_t	prexstat;
132 
133 	*comb_p = NULL;
134 
135 	DBG_TNF_PROBE_0(_tnfctl_comb_build_start, "libtnfctl",
136 			"start _tnfctl_comb_build; sunw%verbosity 1");
137 
138 	if (find(hndl, op, down, next, comb_p)) {
139 
140 		DBG_TNF_PROBE_1(_tnfctl_comb_build_end, "libtnfctl",
141 			"end _tnfctl_comb_build; sunw%verbosity 1",
142 			tnf_opaque, found_comb_at, *comb_p);
143 
144 		return (TNFCTL_ERR_NONE);
145 	}
146 	prexstat = build(hndl, op, down, next, comb_p);
147 
148 	DBG_TNF_PROBE_1(_tnfctl_comb_build_end, "libtnfctl",
149 			"end _tnfctl_comb_build; sunw%verbosity 1",
150 			tnf_opaque, built_comb_at, *comb_p);
151 
152 	return (prexstat);
153 }
154 
155 
156 /*
157  * _tnfctl_comb_decode() - returns a string describing the probe functions
158  * NOTE - the string is for reference purposes ONLY, it should not be freed
159  * by the client.
160  */
161 tnfctl_errcode_t
162 _tnfctl_comb_decode(tnfctl_handle_t *hndl, uintptr_t addr, char ***func_names,
163 	uintptr_t **func_addrs)
164 {
165 	tnfctl_errcode_t prexstat;
166 
167 	DBG_TNF_PROBE_0(_tnfctl_comb_decode_start, "libtnfctl",
168 			"start _tnfctl_comb_decode; sunw%verbosity 2");
169 
170 	prexstat = decode(hndl, addr, func_names, func_addrs);
171 
172 	DBG_TNF_PROBE_0(_tnfctl_comb_decode_end, "libtnfctl",
173 			"end _tnfctl_comb_decode; sunw%verbosity 2");
174 
175 	return (prexstat);
176 }
177 
178 
179 /* ---------------------------------------------------------------- */
180 /* ----------------------- Private Functions ---------------------- */
181 /* ---------------------------------------------------------------- */
182 
183 /*
184  * if combination has been decoded, return decoded info., else
185  * decode combination and cache information
186  */
187 static tnfctl_errcode_t
188 decode(tnfctl_handle_t *hndl, uintptr_t addr, char ***func_names,
189 	uintptr_t **func_addrs)
190 {
191 	tnfctl_errcode_t	prexstat = TNFCTL_ERR_NONE;
192 	decode_key_t	key;
193 	decode_key_t	*new_p = NULL;
194 	decode_key_t	**find_pp;
195 	uintptr_t	down;
196 	uintptr_t	next;
197 	char 		*thisname = NULL;
198 	boolean_t	is_combination;
199 
200 	/* see if we can find the previously decoded answer */
201 	key.addr = addr;
202 	find_pp = (decode_key_t **) tfind(&key, &hndl->decoderoot,
203 			decode_compare);
204 	if (find_pp) {
205 		DBG_TNF_PROBE_0(decode_1, "libtnfctl",
206 			"sunw%verbosity 2; sunw%debug 'found existing'");
207 		*func_names = (*find_pp)->name_ptrs;
208 		*func_addrs = (*find_pp)->func_addrs;
209 		return (TNFCTL_ERR_NONE);
210 	}
211 
212 	new_p = (decode_key_t *) calloc(1, sizeof (decode_key_t));
213 	if (!new_p)
214 		return (TNFCTL_ERR_ALLOCFAIL);
215 	new_p->addr = addr;
216 
217 	prexstat = iscomb(hndl, addr, &down, &next, &is_combination);
218 	if (prexstat)
219 		goto Error;
220 
221 	if (is_combination) {
222 		char **nextnames;
223 		uintptr_t *nextaddrs;
224 		char **name_pp;
225 		uintptr_t *addr_p;
226 		int count, j;
227 
228 		DBG_TNF_PROBE_2(decode_2, "libtnfctl", "sunw%verbosity 2;",
229 			tnf_opaque, down, down,
230 			tnf_opaque, next, next);
231 
232 		prexstat = findname(hndl, down, &thisname);
233 		if (prexstat == TNFCTL_ERR_USR1) {
234 			/*
235 			 * should never happen - combination should not
236 			 * point at the end function
237 			 */
238 			prexstat = TNFCTL_ERR_INTERNAL;
239 			goto Error;
240 		} else if (prexstat)
241 			goto Error;
242 
243 		prexstat = decode(hndl, next, &nextnames, &nextaddrs);
244 		if (prexstat)
245 			goto Error;
246 
247 		/* count number of elements - caution: empty 'for' loop */
248 		for (count = 0; nextnames[count]; count++);
249 		count++;	/* since it was 0 based */
250 
251 		/* allocate one more for new function name */
252 		new_p->name_ptrs = malloc((count + 1) *
253 					sizeof (new_p->name_ptrs[0]));
254 		if (new_p->name_ptrs == NULL) {
255 			prexstat = TNFCTL_ERR_ALLOCFAIL;
256 			goto Error;
257 		}
258 		new_p->func_addrs = malloc((count + 1) *
259 					sizeof (new_p->func_addrs[0]));
260 		if (new_p->func_addrs == NULL) {
261 			prexstat = TNFCTL_ERR_ALLOCFAIL;
262 			goto Error;
263 		}
264 		name_pp = new_p->name_ptrs;
265 		addr_p = new_p->func_addrs;
266 		addr_p[0] = down;
267 		name_pp[0] = thisname;
268 		for (j = 0; j < count; j++) {
269 			name_pp[j + 1] = nextnames[j];
270 			addr_p[j + 1] = nextaddrs[j];
271 		}
272 	} else {
273 		prexstat = findname(hndl, addr, &thisname);
274 		if (prexstat != TNFCTL_ERR_USR1) {
275 			/*
276 			 * base case - end function is the only function
277 			 * that can be pointed at directly
278 			 */
279 			if (prexstat == TNFCTL_ERR_NONE)
280 				prexstat = TNFCTL_ERR_NONE;
281 			goto Error;
282 		}
283 		new_p->name_ptrs = malloc(sizeof (new_p->name_ptrs[0]));
284 		if (new_p->name_ptrs == NULL) {
285 			prexstat = TNFCTL_ERR_ALLOCFAIL;
286 			goto Error;
287 		}
288 		new_p->func_addrs = malloc(sizeof (new_p->func_addrs[0]));
289 		if (new_p->func_addrs == NULL) {
290 			prexstat = TNFCTL_ERR_ALLOCFAIL;
291 			goto Error;
292 		}
293 		new_p->name_ptrs[0] = NULL;
294 		new_p->func_addrs[0] = NULL;
295 	}
296 
297 	DBG_TNF_PROBE_1(decode_3, "libtnfctl",
298 			"sunw%verbosity 2; sunw%debug 'decode built'",
299 			tnf_string, func_name,
300 				(thisname) ? (thisname) : "end_func");
301 
302 	find_pp = (decode_key_t **) tsearch(new_p,
303 		&hndl->decoderoot, decode_compare);
304 	assert(*find_pp == new_p);
305 	*func_names = new_p->name_ptrs;
306 	*func_addrs = new_p->func_addrs;
307 	return (TNFCTL_ERR_NONE);
308 
309 Error:
310 	if (new_p) {
311 		if (new_p->name_ptrs)
312 			free(new_p->name_ptrs);
313 		if (new_p->func_addrs)
314 			free(new_p->func_addrs);
315 		free(new_p);
316 	}
317 	return (prexstat);
318 }
319 
320 
321 /*
322  * iscomb() - determine whether the pointed to function is a combination.  If
323  * it is, return the down and next pointers
324  */
325 static tnfctl_errcode_t
326 iscomb(tnfctl_handle_t *hndl,
327 	uintptr_t addr, uintptr_t *down_p, uintptr_t *next_p,
328 	boolean_t *ret_val)
329 {
330 	int		type;
331 	boolean_t	matched = B_FALSE;
332 
333 	for (type = 0; type < PRB_COMB_COUNT; type++) {
334 		size_t		size;
335 		int		miscstat;
336 		char		*targ_p;
337 		char		*ptr;
338 		char		*tptr;
339 		uintptr_t	downaddr;
340 		uintptr_t	nextaddr;
341 		int		num_bits = 0;
342 		int		tmp_bits = prb_callinfo.mask;
343 
344 		/* allocate room to copy the target code */
345 		size = (size_t) (calltmpl[type].end - calltmpl[type].entry);
346 		targ_p = (char *) malloc(size);
347 		if (!targ_p)
348 			return (TNFCTL_ERR_ALLOCFAIL);
349 
350 		/* copy code from target */
351 		miscstat = hndl->p_read(hndl->proc_p, addr, targ_p, size);
352 		if (miscstat) {
353 			free(targ_p);
354 			return (TNFCTL_ERR_INTERNAL);
355 		}
356 
357 		/* find the number of bits before the highest bit in mask */
358 		while (tmp_bits > 0) {
359 			num_bits++;
360 			tmp_bits <<= 1;
361 		}
362 
363 		/* loop over all the words */
364 		tptr = (char *) calltmpl[type].entry;
365 		for (ptr = targ_p; ptr < (targ_p + size); ptr++, tptr++) {
366 			int			 downbits;
367 			int			 nextbits;
368 		/* LINTED pointer cast may result in improper alignment */
369 			int			*uptr = (int *) ptr;
370 
371 			/*
372 			 * If we are pointing at one of the words that we
373 			 * patch, * (down or next displ) then read that value
374 			 * in. * Otherwise make sure the words match.
375 			 */
376 			if ((uintptr_t) tptr == calltmpl[type].down +
377 				prb_callinfo.offset) {
378 				downbits = *uptr;
379 				downbits &= prb_callinfo.mask;
380 				/* sign extend */
381 				downbits  = (downbits << num_bits) >> num_bits;
382 				downbits <<= prb_callinfo.shift;
383 				downaddr = addr + (ptr - targ_p) + downbits;
384 #if defined(i386)
385 				downaddr += 4;
386 				/* intel is relative to *next* instruction */
387 #endif
388 
389 				ptr += 3;
390 				tptr += 3;
391 			} else if ((uintptr_t) tptr == calltmpl[type].next +
392 				prb_callinfo.offset) {
393 				nextbits = *uptr;
394 				nextbits &= prb_callinfo.mask;
395 				/* sign extend */
396 				nextbits  = (nextbits << num_bits) >> num_bits;
397 				nextbits <<= prb_callinfo.shift;
398 				nextaddr = addr + (ptr - targ_p) + nextbits;
399 #if defined(i386)
400 				nextaddr += 4;
401 				/* intel is relative to *next* instruction */
402 #endif
403 
404 				ptr += 3;
405 				tptr += 3;
406 			} else {
407 				/* the byte better match or we bail */
408 				if (*ptr != *tptr)
409 					goto NextComb;
410 			}
411 		}
412 
413 		/* YOWSA! - its a match */
414 		matched = B_TRUE;
415 
416 NextComb:
417 		/* free allocated memory */
418 		if (targ_p)
419 			free(targ_p);
420 
421 		if (matched) {
422 			*down_p = downaddr;
423 			*next_p = nextaddr;
424 			*ret_val = B_TRUE;
425 			return (TNFCTL_ERR_NONE);
426 		}
427 	}
428 
429 	*ret_val = B_FALSE;
430 	return (TNFCTL_ERR_NONE);
431 }
432 
433 
434 #define	FUNC_BUF_SIZE	32
435 /*
436  * findname() - find a name for a function given its address.
437  */
438 static tnfctl_errcode_t
439 findname(tnfctl_handle_t *hndl, uintptr_t addr, char **ret_name)
440 {
441 	char		*symname;
442 	tnfctl_errcode_t prexstat;
443 
444 	symname = NULL;
445 	prexstat = _tnfctl_sym_findname(hndl, addr, &symname);
446 	if ((prexstat == TNFCTL_ERR_NONE) && (symname != NULL)) {
447 		/* found a name */
448 
449 		/*
450 		 * SPECIAL CASE
451 		 * If we find "tnf_trace_end" then we should not report it
452 		 * as this is the "end-cap" function and should be hidden
453 		 * from the user.  Return a null string instead ...
454 		 */
455 		if (strcmp(symname, TRACE_END_FUNC) == 0) {
456 			return (TNFCTL_ERR_USR1);
457 		} else {
458 			*ret_name = symname;
459 			return (TNFCTL_ERR_NONE);
460 		}
461 	} else {
462 		char *buffer;
463 
464 		buffer = malloc(FUNC_BUF_SIZE);
465 		if (buffer == NULL)
466 			return (TNFCTL_ERR_ALLOCFAIL);
467 
468 		/* no name found, use the address */
469 		(void) sprintf(buffer, "func@0x%p", addr);
470 		*ret_name = buffer;
471 		return (TNFCTL_ERR_NONE);
472 	}
473 }
474 
475 
476 /*
477  * find() - try to find an existing combination that satisfies ...
478  */
479 static boolean_t
480 find(tnfctl_handle_t *hndl, comb_op_t op, uintptr_t down, uintptr_t next,
481 	uintptr_t * comb_p)
482 {
483 	comb_key_t	key;
484 	comb_key_t	**find_pp;
485 
486 	key.op = op;
487 	key.down = down;
488 	key.next = next;
489 	key.comb = NULL;
490 
491 	find_pp = (comb_key_t **) tfind(&key, &hndl->buildroot, comb_compare);
492 	if (find_pp) {
493 		*comb_p = (*find_pp)->comb;
494 		return (B_TRUE);
495 	} else
496 		return (B_FALSE);
497 }
498 
499 
500 /*
501  * add() - adds a combination to combination cache
502  */
503 static tnfctl_errcode_t
504 add(tnfctl_handle_t *hndl, comb_op_t op, uintptr_t down, uintptr_t next,
505 	uintptr_t comb)
506 {
507 	comb_key_t	 *new_p;
508 	/* LINTED set but not used in function */
509 	comb_key_t	**ret_pp;
510 
511 	new_p = (comb_key_t *) malloc(sizeof (comb_key_t));
512 	if (!new_p)
513 		return (TNFCTL_ERR_ALLOCFAIL);
514 
515 	new_p->op = op;
516 	new_p->down = down;
517 	new_p->next = next;
518 	new_p->comb = comb;
519 
520 	ret_pp = (comb_key_t **) tsearch(new_p, &hndl->buildroot,
521 			comb_compare);
522 	assert(*ret_pp == new_p);
523 	return (TNFCTL_ERR_NONE);
524 }
525 
526 
527 /*
528  * decode_compare() - comparison function used for tree search for
529  * combinations
530  */
531 static int
532 decode_compare(const void *v0p, const void *v1p)
533 {
534 	decode_key_t   *k0p = (decode_key_t *) v0p;
535 	decode_key_t   *k1p = (decode_key_t *) v1p;
536 
537 	return (int) ((uintptr_t) k1p->addr - (uintptr_t) k0p->addr);
538 }				/* end decode_compare */
539 
540 
541 /*
542  * comb_compare() - comparison function used for tree search for combinations
543  */
544 static int
545 comb_compare(const void *v0p, const void *v1p)
546 {
547 	comb_key_t	 *k0p = (comb_key_t *) v0p;
548 	comb_key_t	 *k1p = (comb_key_t *) v1p;
549 
550 	if (k0p->op != k1p->op)
551 		return ((k0p->op < k1p->op) ? -1 : 1);
552 
553 	if (k0p->down != k1p->down)
554 		return ((k0p->down < k1p->down) ? -1 : 1);
555 
556 	if (k0p->next != k1p->next)
557 		return ((k0p->next < k1p->next) ? -1 : 1);
558 
559 	return (0);
560 
561 }				/* end comb_compare */
562 
563 /*
564  * build() - build a composition
565  */
566 static tnfctl_errcode_t
567 build(tnfctl_handle_t *hndl, comb_op_t op, uintptr_t down, uintptr_t next,
568 	uintptr_t *comb_p)
569 {
570 	size_t		size;
571 	uintptr_t	addr;
572 	char		*buffer_p = NULL;
573 	uintptr_t	offset;
574 	uintptr_t	contents;
575 	unsigned	*word_p;
576 	int		miscstat;
577 	tnfctl_errcode_t	prexstat;
578 
579 #if 0
580 	(void) fprintf(stderr, "off=0x%x shift=0x%x mask=0x%x size=%d\n",
581 		prb_callinfo.offset,
582 		prb_callinfo.shift,
583 		prb_callinfo.mask,
584 		calltmpl[op].end - calltmpl[op].entry);
585 #endif
586 
587 	*comb_p = NULL;
588 	size = calltmpl[op].end - calltmpl[op].entry;
589 
590 	/* allocate memory in the target process */
591 	prexstat = _tnfctl_targmem_alloc(hndl, size, &addr);
592 	if (prexstat) {
593 		DBG((void) fprintf(stderr,
594 			"build: trouble allocating target memory:\n"));
595 		goto Error;
596 	}
597 
598 	/* allocate a scratch buffer, copy the template into it */
599 	buffer_p = malloc(size);
600 	if (!buffer_p) {
601 		DBG((void) fprintf(stderr, "build: alloc failed\n"));
602 		prexstat = TNFCTL_ERR_ALLOCFAIL;
603 		goto Error;
604 	}
605 	(void) memcpy(buffer_p, (void *) calltmpl[op].entry, size);
606 
607 	/* poke the down address */
608 	offset = calltmpl[op].down - calltmpl[op].entry;
609 	/*LINTED pointer cast may result in improper alignment*/
610 	word_p = (unsigned *) (buffer_p + offset + prb_callinfo.offset);
611 	contents = down - (addr + offset);
612 #if defined(i386)
613 	contents -= 5;		/* intel offset is relative to *next* instr */
614 #endif
615 
616 	DBG_TNF_PROBE_4(build_1, "libtnfctl", "sunw%verbosity 3",
617 			tnf_opaque, down, down,
618 			tnf_opaque, contents, contents,
619 			tnf_opaque, word_p, word_p,
620 			tnf_long, offset, offset);
621 
622 	*word_p &= ~prb_callinfo.mask;	/* clear the relevant field */
623 	*word_p |= ((contents >> prb_callinfo.shift) & prb_callinfo.mask);
624 
625 	/* poke the next address */
626 	offset = calltmpl[op].next - calltmpl[op].entry;
627 	/*LINTED pointer cast may result in improper alignment*/
628 	word_p = (unsigned *) (buffer_p + offset + prb_callinfo.offset);
629 	contents = next - (addr + offset);
630 #if defined(i386)
631 	contents -= 5;		/* intel offset is relative to *next* instr */
632 #endif
633 
634 	DBG_TNF_PROBE_4(build_2, "libtnfctl", "sunw%verbosity 3",
635 			tnf_opaque, next, next,
636 			tnf_opaque, contents, contents,
637 			tnf_opaque, word_p, word_p,
638 			tnf_long, offset, offset);
639 
640 	*word_p &= ~prb_callinfo.mask;	/* clear the relevant field */
641 	*word_p |= ((contents >> prb_callinfo.shift) & prb_callinfo.mask);
642 
643 	/* copy the combination template into target memory */
644 	miscstat = hndl->p_write(hndl->proc_p, addr, buffer_p, size);
645 	if (miscstat) {
646 		DBG((void) fprintf(stderr,
647 			"build: trouble writing combination: \n"));
648 		prexstat = TNFCTL_ERR_INTERNAL;
649 		goto Error;
650 	}
651 	*comb_p = addr;
652 	prexstat = add(hndl, op, down, next, addr);
653 
654 Error:
655 	if (buffer_p)
656 		free(buffer_p);
657 	return (prexstat);
658 }
659