4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
21 * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
28 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
29 * Use is subject to license terms.
32 #include <sys/cdefs.h>
33 #include <sys/param.h>
35 #include <sys/dtrace.h>
37 #include <machine/cpufunc.h>
38 #include <machine/md_var.h>
42 #define FBT_PUSHL_EBP 0x55
43 #define FBT_MOVL_ESP_EBP0_V0 0x8b
44 #define FBT_MOVL_ESP_EBP1_V0 0xec
45 #define FBT_MOVL_ESP_EBP0_V1 0x89
46 #define FBT_MOVL_ESP_EBP1_V1 0xe5
47 #define FBT_REX_RSP_RBP 0x48
49 #define FBT_POPL_EBP 0x5d
51 #define FBT_RET_IMM16 0xc2
52 #define FBT_LEAVE 0xc9
55 #define FBT_PATCHVAL 0xcc
57 #define FBT_PATCHVAL 0xf0
60 #define FBT_ENTRY "entry"
61 #define FBT_RETURN "return"
64 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t rval)
68 uintptr_t arg0, arg1, arg2, arg3, arg4;
73 stack = (uintptr_t *)frame->tf_rsp;
75 /* Skip hardware-saved registers. */
76 stack = (uintptr_t *)frame->tf_isp + 3;
79 cpu = &solaris_cpu[curcpu];
80 fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
81 for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
82 if ((uintptr_t)fbt->fbtp_patchpoint != addr)
84 fbtrval = fbt->fbtp_rval;
85 for (; fbt != NULL; fbt = fbt->fbtp_tracenext) {
86 ASSERT(fbt->fbtp_rval == fbtrval);
87 if (fbt->fbtp_roffset == 0) {
89 /* fbt->fbtp_rval == DTRACE_INVOP_PUSHQ_RBP */
90 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
91 cpu->cpu_dtrace_caller = stack[0];
92 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
104 * When accessing the arguments on the stack,
105 * we must protect against accessing beyond
106 * the stack. We can safely set NOFAULT here
107 * -- we know that interrupts are already
110 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
111 cpu->cpu_dtrace_caller = stack[i++];
117 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
121 dtrace_probe(fbt->fbtp_id, arg0, arg1,
124 cpu->cpu_dtrace_caller = 0;
128 * On amd64, we instrument the ret, not the
129 * leave. We therefore need to set the caller
130 * to ensure that the top frame of a stack()
133 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
134 cpu->cpu_dtrace_caller = stack[0];
135 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
139 dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
141 cpu->cpu_dtrace_caller = 0;
151 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
156 intr = intr_disable();
157 old_wp = disable_wp();
158 *fbt->fbtp_patchpoint = val;
164 fbt_provide_module_function(linker_file_t lf, int symindx,
165 linker_symval_t *symval, void *opaque)
167 char *modname = opaque;
168 const char *name = symval->name;
169 fbt_probe_t *fbt, *hash, *retfbt;
172 uint8_t *instr, *limit;
174 if (fbt_excluded(name))
178 * trap_check() is a wrapper for DTrace's fault handler, so we don't
179 * want to be able to instrument it.
181 if (strcmp(name, "trap_check") == 0)
186 instr = (uint8_t *) symval->value;
187 limit = (uint8_t *) symval->value + symval->size;
190 while (instr < limit) {
191 if (*instr == FBT_PUSHL_EBP)
194 if ((size = dtrace_instr_size(instr)) <= 0)
200 if (instr >= limit || *instr != FBT_PUSHL_EBP) {
202 * We either don't save the frame pointer in this
203 * function, or we ran into some disassembly
204 * screw-up. Either way, we bail.
209 if (instr[0] != FBT_PUSHL_EBP)
212 if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
213 instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
214 !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
215 instr[2] == FBT_MOVL_ESP_EBP1_V1))
219 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
220 fbt->fbtp_name = name;
221 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
222 name, FBT_ENTRY, 3, fbt);
223 fbt->fbtp_patchpoint = instr;
225 fbt->fbtp_loadcnt = lf->loadcnt;
226 fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP;
227 fbt->fbtp_savedval = *instr;
228 fbt->fbtp_patchval = FBT_PATCHVAL;
229 fbt->fbtp_symindx = symindx;
231 for (hash = fbt_probetab[FBT_ADDR2NDX(instr)]; hash != NULL;
232 hash = hash->fbtp_hashnext) {
233 if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
234 fbt->fbtp_tracenext = hash->fbtp_tracenext;
235 hash->fbtp_tracenext = fbt;
240 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
241 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
252 * If this disassembly fails, then we've likely walked off into
253 * a jump table or some other unsuitable area. Bail out of the
256 if ((size = dtrace_instr_size(instr)) <= 0)
261 * We only instrument "ret" on amd64 -- we don't yet instrument
262 * ret imm16, largely because the compiler doesn't seem to
263 * (yet) emit them in the kernel...
265 if (*instr != FBT_RET) {
271 (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
272 (*(instr + 1) == FBT_RET ||
273 *(instr + 1) == FBT_RET_IMM16))) {
280 * We (desperately) want to avoid erroneously instrumenting a
281 * jump table, especially given that our markers are pretty
282 * short: two bytes on x86, and just one byte on amd64. To
283 * determine if we're looking at a true instruction sequence
284 * or an inline jump table that happens to contain the same
285 * byte sequences, we resort to some heuristic sleeze: we
286 * treat this instruction as being contained within a pointer,
287 * and see if that pointer points to within the body of the
288 * function. If it does, we refuse to instrument it.
290 for (j = 0; j < sizeof (uintptr_t); j++) {
291 caddr_t check = (caddr_t) instr - j;
294 if (check < symval->value)
297 if (check + sizeof (caddr_t) > (caddr_t)limit)
300 ptr = *(uint8_t **)check;
302 if (ptr >= (uint8_t *) symval->value && ptr < limit) {
311 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
312 fbt->fbtp_name = name;
314 if (retfbt == NULL) {
315 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
316 name, FBT_RETURN, 3, fbt);
318 retfbt->fbtp_probenext = fbt;
319 fbt->fbtp_id = retfbt->fbtp_id;
323 fbt->fbtp_patchpoint = instr;
325 fbt->fbtp_loadcnt = lf->loadcnt;
326 fbt->fbtp_symindx = symindx;
329 if (*instr == FBT_POPL_EBP) {
330 fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
332 ASSERT(*instr == FBT_LEAVE);
333 fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
336 (uintptr_t)(instr - (uint8_t *) symval->value) + 1;
339 ASSERT(*instr == FBT_RET);
340 fbt->fbtp_rval = DTRACE_INVOP_RET;
342 (uintptr_t)(instr - (uint8_t *) symval->value);
345 fbt->fbtp_savedval = *instr;
346 fbt->fbtp_patchval = FBT_PATCHVAL;
347 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
348 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;