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