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
_tnfctl_comb_build(tnfctl_handle_t * hndl,comb_op_t op,uintptr_t down,uintptr_t next,uintptr_t * comb_p)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
_tnfctl_comb_decode(tnfctl_handle_t * hndl,uintptr_t addr,char *** func_names,uintptr_t ** func_addrs)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
decode(tnfctl_handle_t * hndl,uintptr_t addr,char *** func_names,uintptr_t ** func_addrs)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
iscomb(tnfctl_handle_t * hndl,uintptr_t addr,uintptr_t * down_p,uintptr_t * next_p,boolean_t * ret_val)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
findname(tnfctl_handle_t * hndl,uintptr_t addr,char ** ret_name)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
find(tnfctl_handle_t * hndl,comb_op_t op,uintptr_t down,uintptr_t next,uintptr_t * comb_p)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
add(tnfctl_handle_t * hndl,comb_op_t op,uintptr_t down,uintptr_t next,uintptr_t comb)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
decode_compare(const void * v0p,const void * v1p)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
comb_compare(const void * v0p,const void * v1p)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
build(tnfctl_handle_t * hndl,comb_op_t op,uintptr_t down,uintptr_t next,uintptr_t * comb_p)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