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