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