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