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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 *
21 * Portions Copyright 2006-2008 John Birrell jb@freebsd.org
22 *
23 */
24
25 /*
26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
27 * Use is subject to license terms.
28 */
29
30 #include <sys/cdefs.h>
31 #include <sys/param.h>
32
33 #include <sys/dtrace.h>
34
35 #include <machine/cpufunc.h>
36 #include <machine/md_var.h>
37
38 #include "fbt.h"
39
40 #define FBT_PUSHL_EBP 0x55
41 #define FBT_MOVL_ESP_EBP0_V0 0x8b
42 #define FBT_MOVL_ESP_EBP1_V0 0xec
43 #define FBT_MOVL_ESP_EBP0_V1 0x89
44 #define FBT_MOVL_ESP_EBP1_V1 0xe5
45 #define FBT_REX_RSP_RBP 0x48
46
47 #define FBT_POPL_EBP 0x5d
48 #define FBT_RET 0xc3
49 #define FBT_RET_IMM16 0xc2
50 #define FBT_LEAVE 0xc9
51
52 #ifdef __amd64__
53 #define FBT_PATCHVAL 0xcc
54 #else
55 #define FBT_PATCHVAL 0xf0
56 #endif
57
58 #define FBT_AFRAMES 3
59
60 int
fbt_invop(uintptr_t addr,struct trapframe * frame,uintptr_t scratch __unused)61 fbt_invop(uintptr_t addr, struct trapframe *frame, uintptr_t scratch __unused)
62 {
63 solaris_cpu_t *cpu;
64 uintptr_t *stack;
65 uintptr_t arg0, arg1, arg2, arg3, arg4, rval;
66 fbt_probe_t *fbt;
67 int8_t fbtrval;
68
69 #ifdef __amd64__
70 stack = (uintptr_t *)frame->tf_rsp;
71 rval = frame->tf_rax;
72 #else
73 /* Skip hardware-saved registers. */
74 stack = (uintptr_t *)frame->tf_isp + 3;
75 rval = frame->tf_eax;
76 #endif
77
78 cpu = &solaris_cpu[curcpu];
79 fbt = fbt_probetab[FBT_ADDR2NDX(addr)];
80 for (; fbt != NULL; fbt = fbt->fbtp_hashnext) {
81 if ((uintptr_t)fbt->fbtp_patchpoint != addr)
82 continue;
83 fbtrval = fbt->fbtp_rval;
84
85 /*
86 * Report the address of the breakpoint for the benefit
87 * of consumers fetching register values with regs[].
88 */
89 #ifdef __i386__
90 frame->tf_eip--;
91 #else
92 frame->tf_rip--;
93 #endif
94 for (; fbt != NULL; fbt = fbt->fbtp_tracenext) {
95 ASSERT(fbt->fbtp_rval == fbtrval);
96 if (fbt->fbtp_roffset == 0) {
97 #ifdef __amd64__
98 /* fbt->fbtp_rval == DTRACE_INVOP_PUSHQ_RBP */
99 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
100 cpu->cpu_dtrace_caller = stack[0];
101 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
102 CPU_DTRACE_BADADDR);
103
104 arg0 = frame->tf_rdi;
105 arg1 = frame->tf_rsi;
106 arg2 = frame->tf_rdx;
107 arg3 = frame->tf_rcx;
108 arg4 = frame->tf_r8;
109 #else
110 int i = 0;
111
112 /*
113 * When accessing the arguments on the stack,
114 * we must protect against accessing beyond
115 * the stack. We can safely set NOFAULT here
116 * -- we know that interrupts are already
117 * disabled.
118 */
119 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
120 cpu->cpu_dtrace_caller = stack[i++];
121 arg0 = stack[i++];
122 arg1 = stack[i++];
123 arg2 = stack[i++];
124 arg3 = stack[i++];
125 arg4 = stack[i++];
126 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
127 CPU_DTRACE_BADADDR);
128 #endif
129
130 dtrace_probe(fbt->fbtp_id, arg0, arg1,
131 arg2, arg3, arg4);
132
133 cpu->cpu_dtrace_caller = 0;
134 } else {
135 #ifdef __amd64__
136 /*
137 * On amd64, we instrument the ret, not the
138 * leave. We therefore need to set the caller
139 * to ensure that the top frame of a stack()
140 * action is correct.
141 */
142 DTRACE_CPUFLAG_SET(CPU_DTRACE_NOFAULT);
143 cpu->cpu_dtrace_caller = stack[0];
144 DTRACE_CPUFLAG_CLEAR(CPU_DTRACE_NOFAULT |
145 CPU_DTRACE_BADADDR);
146 #endif
147
148 dtrace_probe(fbt->fbtp_id, fbt->fbtp_roffset,
149 rval, 0, 0, 0);
150 cpu->cpu_dtrace_caller = 0;
151 }
152 }
153 /* Advance to the instruction following the breakpoint. */
154 #ifdef __i386__
155 frame->tf_eip++;
156 #else
157 frame->tf_rip++;
158 #endif
159 return (fbtrval);
160 }
161
162 return (0);
163 }
164
165 void
fbt_patch_tracepoint(fbt_probe_t * fbt,fbt_patchval_t val)166 fbt_patch_tracepoint(fbt_probe_t *fbt, fbt_patchval_t val)
167 {
168 register_t intr;
169 bool old_wp;
170
171 intr = intr_disable();
172 old_wp = disable_wp();
173 *fbt->fbtp_patchpoint = val;
174 restore_wp(old_wp);
175 intr_restore(intr);
176 }
177
178 int
fbt_provide_module_function(linker_file_t lf,int symindx,linker_symval_t * symval,void * opaque)179 fbt_provide_module_function(linker_file_t lf, int symindx,
180 linker_symval_t *symval, void *opaque)
181 {
182 char *modname = opaque;
183 const char *name = symval->name;
184 fbt_probe_t *fbt, *hash, *retfbt;
185 int j;
186 int size;
187 uint8_t *instr, *limit;
188
189 if (fbt_excluded(name))
190 return (0);
191
192 /*
193 * trap_check() is a wrapper for DTrace's fault handler, so we don't
194 * want to be able to instrument it.
195 */
196 if (strcmp(name, "trap_check") == 0)
197 return (0);
198
199 size = symval->size;
200
201 instr = (uint8_t *) symval->value;
202 limit = (uint8_t *) symval->value + symval->size;
203
204 #ifdef __amd64__
205 while (instr < limit) {
206 if (*instr == FBT_PUSHL_EBP)
207 break;
208
209 if ((size = dtrace_instr_size(instr)) <= 0)
210 break;
211
212 instr += size;
213 }
214
215 if (instr >= limit || *instr != FBT_PUSHL_EBP) {
216 /*
217 * We either don't save the frame pointer in this
218 * function, or we ran into some disassembly
219 * screw-up. Either way, we bail.
220 */
221 return (0);
222 }
223 #else
224 if (instr[0] != FBT_PUSHL_EBP)
225 return (0);
226
227 if (!(instr[1] == FBT_MOVL_ESP_EBP0_V0 &&
228 instr[2] == FBT_MOVL_ESP_EBP1_V0) &&
229 !(instr[1] == FBT_MOVL_ESP_EBP0_V1 &&
230 instr[2] == FBT_MOVL_ESP_EBP1_V1))
231 return (0);
232 #endif
233
234 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
235 fbt->fbtp_name = name;
236 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
237 name, FBT_ENTRY, FBT_AFRAMES, fbt);
238 fbt->fbtp_patchpoint = instr;
239 fbt->fbtp_ctl = lf;
240 fbt->fbtp_loadcnt = lf->loadcnt;
241 fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP;
242 fbt->fbtp_savedval = *instr;
243 fbt->fbtp_patchval = FBT_PATCHVAL;
244 fbt->fbtp_symindx = symindx;
245
246 for (hash = fbt_probetab[FBT_ADDR2NDX(instr)]; hash != NULL;
247 hash = hash->fbtp_hashnext) {
248 if (hash->fbtp_patchpoint == fbt->fbtp_patchpoint) {
249 fbt->fbtp_tracenext = hash->fbtp_tracenext;
250 hash->fbtp_tracenext = fbt;
251 break;
252 }
253 }
254 if (hash == NULL) {
255 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
256 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
257 }
258
259 lf->fbt_nentries++;
260
261 retfbt = NULL;
262 again:
263 if (instr >= limit)
264 return (0);
265
266 /*
267 * If this disassembly fails, then we've likely walked off into
268 * a jump table or some other unsuitable area. Bail out of the
269 * disassembly now.
270 */
271 if ((size = dtrace_instr_size(instr)) <= 0)
272 return (0);
273
274 #ifdef __amd64__
275 /*
276 * We only instrument "ret" on amd64 -- we don't yet instrument
277 * ret imm16, largely because the compiler doesn't seem to
278 * (yet) emit them in the kernel...
279 */
280 if (*instr != FBT_RET) {
281 instr += size;
282 goto again;
283 }
284 #else
285 if (!(size == 1 &&
286 (*instr == FBT_POPL_EBP || *instr == FBT_LEAVE) &&
287 (*(instr + 1) == FBT_RET ||
288 *(instr + 1) == FBT_RET_IMM16))) {
289 instr += size;
290 goto again;
291 }
292 #endif
293
294 /*
295 * We (desperately) want to avoid erroneously instrumenting a
296 * jump table, especially given that our markers are pretty
297 * short: two bytes on x86, and just one byte on amd64. To
298 * determine if we're looking at a true instruction sequence
299 * or an inline jump table that happens to contain the same
300 * byte sequences, we resort to some heuristic sleeze: we
301 * treat this instruction as being contained within a pointer,
302 * and see if that pointer points to within the body of the
303 * function. If it does, we refuse to instrument it.
304 */
305 for (j = 0; j < sizeof (uintptr_t); j++) {
306 caddr_t check = (caddr_t) instr - j;
307 uint8_t *ptr;
308
309 if (check < symval->value)
310 break;
311
312 if (check + sizeof (caddr_t) > (caddr_t)limit)
313 continue;
314
315 ptr = *(uint8_t **)check;
316
317 if (ptr >= (uint8_t *) symval->value && ptr < limit) {
318 instr += size;
319 goto again;
320 }
321 }
322
323 /*
324 * We have a winner!
325 */
326 fbt = malloc(sizeof (fbt_probe_t), M_FBT, M_WAITOK | M_ZERO);
327 fbt->fbtp_name = name;
328
329 if (retfbt == NULL) {
330 fbt->fbtp_id = dtrace_probe_create(fbt_id, modname,
331 name, FBT_RETURN, FBT_AFRAMES, fbt);
332 } else {
333 retfbt->fbtp_probenext = fbt;
334 fbt->fbtp_id = retfbt->fbtp_id;
335 }
336
337 retfbt = fbt;
338 fbt->fbtp_patchpoint = instr;
339 fbt->fbtp_ctl = lf;
340 fbt->fbtp_loadcnt = lf->loadcnt;
341 fbt->fbtp_symindx = symindx;
342
343 #ifndef __amd64__
344 if (*instr == FBT_POPL_EBP) {
345 fbt->fbtp_rval = DTRACE_INVOP_POPL_EBP;
346 } else {
347 ASSERT(*instr == FBT_LEAVE);
348 fbt->fbtp_rval = DTRACE_INVOP_LEAVE;
349 }
350 fbt->fbtp_roffset =
351 (uintptr_t)(instr - (uint8_t *) symval->value) + 1;
352
353 #else
354 ASSERT(*instr == FBT_RET);
355 fbt->fbtp_rval = DTRACE_INVOP_RET;
356 fbt->fbtp_roffset =
357 (uintptr_t)(instr - (uint8_t *) symval->value);
358 #endif
359
360 fbt->fbtp_savedval = *instr;
361 fbt->fbtp_patchval = FBT_PATCHVAL;
362 fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)];
363 fbt_probetab[FBT_ADDR2NDX(instr)] = fbt;
364
365 lf->fbt_nentries++;
366
367 instr += size;
368 goto again;
369 }
370