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