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 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <stdlib.h> 28 #include <assert.h> 29 #include <errno.h> 30 #include <string.h> 31 #include <libgen.h> 32 33 #include <dt_impl.h> 34 #include <dt_pid.h> 35 36 #include <dis_tables.h> 37 38 #if !defined(sun) 39 #define PR_MODEL_ILP32 1 40 #define PR_MODEL_LP64 2 41 #include <libproc_compat.h> 42 #endif 43 44 #define DT_POPL_EBP 0x5d 45 #define DT_RET 0xc3 46 #define DT_RET16 0xc2 47 #define DT_LEAVE 0xc9 48 #define DT_JMP32 0xe9 49 #define DT_JMP8 0xeb 50 #define DT_REP 0xf3 51 52 #define DT_MOVL_EBP_ESP 0xe58b 53 54 #define DT_ISJ32(op16) (((op16) & 0xfff0) == 0x0f80) 55 #define DT_ISJ8(op8) (((op8) & 0xf0) == 0x70) 56 57 #define DT_MODRM_REG(modrm) (((modrm) >> 3) & 0x7) 58 59 static int dt_instr_size(uchar_t *, dtrace_hdl_t *, pid_t, uintptr_t, char); 60 61 /*ARGSUSED*/ 62 int 63 dt_pid_create_entry_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 64 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp) 65 { 66 ftp->ftps_type = DTFTP_ENTRY; 67 ftp->ftps_pc = (uintptr_t)symp->st_value; 68 ftp->ftps_size = (size_t)symp->st_size; 69 ftp->ftps_noffs = 1; 70 ftp->ftps_offs[0] = 0; 71 72 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 73 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 74 strerror(errno)); 75 return (dt_set_errno(dtp, errno)); 76 } 77 78 return (1); 79 } 80 81 static int 82 dt_pid_has_jump_table(struct ps_prochandle *P, dtrace_hdl_t *dtp, 83 uint8_t *text, fasttrap_probe_spec_t *ftp, const GElf_Sym *symp) 84 { 85 ulong_t i; 86 int size; 87 #if defined(sun) 88 pid_t pid = Pstatus(P)->pr_pid; 89 char dmodel = Pstatus(P)->pr_dmodel; 90 #else 91 pid_t pid = proc_getpid(P); 92 #if __i386__ 93 char dmodel = PR_MODEL_ILP32; 94 #elif __amd64__ 95 char dmodel = PR_MODEL_LP64; 96 #endif 97 #endif 98 99 /* 100 * Take a pass through the function looking for a register-dependant 101 * jmp instruction. This could be a jump table so we have to be 102 * ultra conservative. 103 */ 104 for (i = 0; i < ftp->ftps_size; i += size) { 105 size = dt_instr_size(&text[i], dtp, pid, symp->st_value + i, 106 dmodel); 107 108 /* 109 * Assume the worst if we hit an illegal instruction. 110 */ 111 if (size <= 0) { 112 dt_dprintf("error at %#lx (assuming jump table)\n", i); 113 return (1); 114 } 115 116 #ifdef notyet 117 /* 118 * Register-dependant jmp instructions start with a 0xff byte 119 * and have the modrm.reg field set to 4. They can have an 120 * optional REX prefix on the 64-bit ISA. 121 */ 122 if ((text[i] == 0xff && DT_MODRM_REG(text[i + 1]) == 4) || 123 (dmodel == PR_MODEL_LP64 && (text[i] & 0xf0) == 0x40 && 124 text[i + 1] == 0xff && DT_MODRM_REG(text[i + 2]) == 4)) { 125 dt_dprintf("found a suspected jump table at %s:%lx\n", 126 ftp->ftps_func, i); 127 return (1); 128 } 129 #endif 130 } 131 132 return (0); 133 } 134 135 /*ARGSUSED*/ 136 int 137 dt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 138 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret) 139 { 140 uint8_t *text; 141 ulong_t i, end; 142 int size; 143 #if defined(sun) 144 pid_t pid = Pstatus(P)->pr_pid; 145 char dmodel = Pstatus(P)->pr_dmodel; 146 #else 147 pid_t pid = proc_getpid(P); 148 #if __i386__ 149 char dmodel = PR_MODEL_ILP32; 150 #elif __amd64__ 151 char dmodel = PR_MODEL_LP64; 152 #endif 153 #endif 154 155 /* 156 * We allocate a few extra bytes at the end so we don't have to check 157 * for overrunning the buffer. 158 */ 159 if ((text = calloc(1, symp->st_size + 4)) == NULL) { 160 dt_dprintf("mr sparkle: malloc() failed\n"); 161 return (DT_PROC_ERR); 162 } 163 164 if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { 165 dt_dprintf("mr sparkle: Pread() failed\n"); 166 free(text); 167 return (DT_PROC_ERR); 168 } 169 170 ftp->ftps_type = DTFTP_RETURN; 171 ftp->ftps_pc = (uintptr_t)symp->st_value; 172 ftp->ftps_size = (size_t)symp->st_size; 173 ftp->ftps_noffs = 0; 174 175 /* 176 * If there's a jump table in the function we're only willing to 177 * instrument these specific (and equivalent) instruction sequences: 178 * leave 179 * [rep] ret 180 * and 181 * movl %ebp,%esp 182 * popl %ebp 183 * [rep] ret 184 * 185 * We do this to avoid accidentally interpreting jump table 186 * offsets as actual instructions. 187 */ 188 if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { 189 for (i = 0, end = ftp->ftps_size; i < end; i += size) { 190 size = dt_instr_size(&text[i], dtp, pid, 191 symp->st_value + i, dmodel); 192 193 /* bail if we hit an invalid opcode */ 194 if (size <= 0) 195 break; 196 197 if (text[i] == DT_LEAVE && text[i + 1] == DT_RET) { 198 dt_dprintf("leave/ret at %lx\n", i + 1); 199 ftp->ftps_offs[ftp->ftps_noffs++] = i + 1; 200 size = 2; 201 } else if (text[i] == DT_LEAVE && 202 text[i + 1] == DT_REP && text[i + 2] == DT_RET) { 203 dt_dprintf("leave/rep ret at %lx\n", i + 1); 204 ftp->ftps_offs[ftp->ftps_noffs++] = i + 1; 205 size = 3; 206 } else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP && 207 text[i + 2] == DT_POPL_EBP && 208 text[i + 3] == DT_RET) { 209 dt_dprintf("movl/popl/ret at %lx\n", i + 3); 210 ftp->ftps_offs[ftp->ftps_noffs++] = i + 3; 211 size = 4; 212 } else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP && 213 text[i + 2] == DT_POPL_EBP && 214 text[i + 3] == DT_REP && 215 text[i + 4] == DT_RET) { 216 dt_dprintf("movl/popl/rep ret at %lx\n", i + 3); 217 ftp->ftps_offs[ftp->ftps_noffs++] = i + 3; 218 size = 5; 219 } 220 } 221 } else { 222 for (i = 0, end = ftp->ftps_size; i < end; i += size) { 223 size = dt_instr_size(&text[i], dtp, pid, 224 symp->st_value + i, dmodel); 225 226 /* bail if we hit an invalid opcode */ 227 if (size <= 0) 228 break; 229 230 /* ordinary ret */ 231 if (size == 1 && text[i] == DT_RET) 232 goto is_ret; 233 234 /* two-byte ret */ 235 if (size == 2 && text[i] == DT_REP && 236 text[i + 1] == DT_RET) 237 goto is_ret; 238 239 /* ret <imm16> */ 240 if (size == 3 && text[i] == DT_RET16) 241 goto is_ret; 242 243 /* two-byte ret <imm16> */ 244 if (size == 4 && text[i] == DT_REP && 245 text[i + 1] == DT_RET16) 246 goto is_ret; 247 248 /* 32-bit displacement jmp outside of the function */ 249 if (size == 5 && text[i] == DT_JMP32 && symp->st_size <= 250 (uintptr_t)(i + size + *(int32_t *)&text[i + 1])) 251 goto is_ret; 252 253 /* 8-bit displacement jmp outside of the function */ 254 if (size == 2 && text[i] == DT_JMP8 && symp->st_size <= 255 (uintptr_t)(i + size + *(int8_t *)&text[i + 1])) 256 goto is_ret; 257 258 /* 32-bit disp. conditional jmp outside of the func. */ 259 if (size == 6 && DT_ISJ32(*(uint16_t *)&text[i]) && 260 symp->st_size <= 261 (uintptr_t)(i + size + *(int32_t *)&text[i + 2])) 262 goto is_ret; 263 264 /* 8-bit disp. conditional jmp outside of the func. */ 265 if (size == 2 && DT_ISJ8(text[i]) && symp->st_size <= 266 (uintptr_t)(i + size + *(int8_t *)&text[i + 1])) 267 goto is_ret; 268 269 continue; 270 is_ret: 271 dt_dprintf("return at offset %lx\n", i); 272 ftp->ftps_offs[ftp->ftps_noffs++] = i; 273 } 274 } 275 276 free(text); 277 if (ftp->ftps_noffs > 0) { 278 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 279 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 280 strerror(errno)); 281 return (dt_set_errno(dtp, errno)); 282 } 283 } 284 285 return (ftp->ftps_noffs); 286 } 287 288 /*ARGSUSED*/ 289 int 290 dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 291 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off) 292 { 293 ftp->ftps_type = DTFTP_OFFSETS; 294 ftp->ftps_pc = (uintptr_t)symp->st_value; 295 ftp->ftps_size = (size_t)symp->st_size; 296 ftp->ftps_noffs = 1; 297 298 if (strcmp("-", ftp->ftps_func) == 0) { 299 ftp->ftps_offs[0] = off; 300 } else { 301 uint8_t *text; 302 ulong_t i; 303 int size; 304 #if defined(sun) 305 pid_t pid = Pstatus(P)->pr_pid; 306 char dmodel = Pstatus(P)->pr_dmodel; 307 #else 308 pid_t pid = proc_getpid(P); 309 #if __i386__ 310 char dmodel = PR_MODEL_ILP32; 311 #elif __amd64__ 312 char dmodel = PR_MODEL_LP64; 313 #endif 314 #endif 315 316 if ((text = malloc(symp->st_size)) == NULL) { 317 dt_dprintf("mr sparkle: malloc() failed\n"); 318 return (DT_PROC_ERR); 319 } 320 321 if (Pread(P, text, symp->st_size, symp->st_value) != 322 symp->st_size) { 323 dt_dprintf("mr sparkle: Pread() failed\n"); 324 free(text); 325 return (DT_PROC_ERR); 326 } 327 328 /* 329 * We can't instrument offsets in functions with jump tables 330 * as we might interpret a jump table offset as an 331 * instruction. 332 */ 333 if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { 334 free(text); 335 return (0); 336 } 337 338 for (i = 0; i < symp->st_size; i += size) { 339 if (i == off) { 340 ftp->ftps_offs[0] = i; 341 break; 342 } 343 344 /* 345 * If we've passed the desired offset without a 346 * match, then the given offset must not lie on a 347 * instruction boundary. 348 */ 349 if (i > off) { 350 free(text); 351 return (DT_PROC_ALIGN); 352 } 353 354 size = dt_instr_size(&text[i], dtp, pid, 355 symp->st_value + i, dmodel); 356 357 /* 358 * If we hit an invalid instruction, bail as if we 359 * couldn't find the offset. 360 */ 361 if (size <= 0) { 362 free(text); 363 return (DT_PROC_ALIGN); 364 } 365 } 366 367 free(text); 368 } 369 370 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 371 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 372 strerror(errno)); 373 return (dt_set_errno(dtp, errno)); 374 } 375 376 return (ftp->ftps_noffs); 377 } 378 379 /*ARGSUSED*/ 380 int 381 dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp, 382 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern) 383 { 384 uint8_t *text; 385 int size; 386 ulong_t i, end = symp->st_size; 387 #if defined(sun) 388 pid_t pid = Pstatus(P)->pr_pid; 389 char dmodel = Pstatus(P)->pr_dmodel; 390 #else 391 pid_t pid = proc_getpid(P); 392 #if __i386__ 393 char dmodel = PR_MODEL_ILP32; 394 #elif __amd64__ 395 char dmodel = PR_MODEL_LP64; 396 #endif 397 #endif 398 399 ftp->ftps_type = DTFTP_OFFSETS; 400 ftp->ftps_pc = (uintptr_t)symp->st_value; 401 ftp->ftps_size = (size_t)symp->st_size; 402 ftp->ftps_noffs = 0; 403 404 if ((text = malloc(symp->st_size)) == NULL) { 405 dt_dprintf("mr sparkle: malloc() failed\n"); 406 return (DT_PROC_ERR); 407 } 408 409 if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { 410 dt_dprintf("mr sparkle: Pread() failed\n"); 411 free(text); 412 return (DT_PROC_ERR); 413 } 414 415 /* 416 * We can't instrument offsets in functions with jump tables as 417 * we might interpret a jump table offset as an instruction. 418 */ 419 if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { 420 free(text); 421 return (0); 422 } 423 424 if (strcmp("*", pattern) == 0) { 425 for (i = 0; i < end; i += size) { 426 ftp->ftps_offs[ftp->ftps_noffs++] = i; 427 428 size = dt_instr_size(&text[i], dtp, pid, 429 symp->st_value + i, dmodel); 430 431 /* bail if we hit an invalid opcode */ 432 if (size <= 0) 433 break; 434 } 435 } else { 436 char name[sizeof (i) * 2 + 1]; 437 438 for (i = 0; i < end; i += size) { 439 (void) snprintf(name, sizeof (name), "%x", i); 440 if (gmatch(name, pattern)) 441 ftp->ftps_offs[ftp->ftps_noffs++] = i; 442 443 size = dt_instr_size(&text[i], dtp, pid, 444 symp->st_value + i, dmodel); 445 446 /* bail if we hit an invalid opcode */ 447 if (size <= 0) 448 break; 449 } 450 } 451 452 free(text); 453 if (ftp->ftps_noffs > 0) { 454 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 455 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 456 strerror(errno)); 457 return (dt_set_errno(dtp, errno)); 458 } 459 } 460 461 return (ftp->ftps_noffs); 462 } 463 464 typedef struct dtrace_dis { 465 uchar_t *instr; 466 dtrace_hdl_t *dtp; 467 pid_t pid; 468 uintptr_t addr; 469 } dtrace_dis_t; 470 471 static int 472 dt_getbyte(void *data) 473 { 474 dtrace_dis_t *dis = data; 475 int ret = *dis->instr; 476 477 if (ret == FASTTRAP_INSTR) { 478 fasttrap_instr_query_t instr; 479 480 instr.ftiq_pid = dis->pid; 481 instr.ftiq_pc = dis->addr; 482 483 /* 484 * If we hit a byte that looks like the fasttrap provider's 485 * trap instruction (which doubles as the breakpoint 486 * instruction for debuggers) we need to query the kernel 487 * for the real value. This may just be part of an immediate 488 * value so there's no need to return an error if the 489 * kernel doesn't know about this address. 490 */ 491 if (ioctl(dis->dtp->dt_ftfd, FASTTRAPIOC_GETINSTR, &instr) == 0) 492 ret = instr.ftiq_instr; 493 } 494 495 dis->addr++; 496 dis->instr++; 497 498 return (ret); 499 } 500 501 static int 502 dt_instr_size(uchar_t *instr, dtrace_hdl_t *dtp, pid_t pid, uintptr_t addr, 503 char dmodel) 504 { 505 dtrace_dis_t data; 506 dis86_t x86dis; 507 uint_t cpu_mode; 508 509 data.instr = instr; 510 data.dtp = dtp; 511 data.pid = pid; 512 data.addr = addr; 513 514 x86dis.d86_data = &data; 515 x86dis.d86_get_byte = dt_getbyte; 516 x86dis.d86_check_func = NULL; 517 518 cpu_mode = (dmodel == PR_MODEL_ILP32) ? SIZE32 : SIZE64; 519 520 if (dtrace_disx86(&x86dis, cpu_mode) != 0) 521 return (-1); 522 523 /* 524 * If the instruction was a single-byte breakpoint, there may be 525 * another debugger attached to this process. The original instruction 526 * can't be recovered so this must fail. 527 */ 528 if (x86dis.d86_len == 1 && instr[0] == FASTTRAP_INSTR) 529 return (-1); 530 531 return (x86dis.d86_len); 532 } 533