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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 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 if (text[i] == 0xff && DT_MODRM_REG(text[i + 1]) == 4) { 104 dt_dprintf("found a suspected jump table at %s:%lx\n", 105 ftp->ftps_func, i); 106 return (1); 107 } 108 } 109 110 return (0); 111 } 112 113 /*ARGSUSED*/ 114 int 115 dt_pid_create_return_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 116 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, uint64_t *stret) 117 { 118 uint8_t *text; 119 ulong_t i, end; 120 int size; 121 pid_t pid = Pstatus(P)->pr_pid; 122 char dmodel = Pstatus(P)->pr_dmodel; 123 124 /* 125 * We allocate a few extra bytes at the end so we don't have to check 126 * for overrunning the buffer. 127 */ 128 if ((text = calloc(1, symp->st_size + 4)) == NULL) { 129 dt_dprintf("mr sparkle: malloc() failed\n"); 130 return (DT_PROC_ERR); 131 } 132 133 if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { 134 dt_dprintf("mr sparkle: Pread() failed\n"); 135 free(text); 136 return (DT_PROC_ERR); 137 } 138 139 ftp->ftps_type = DTFTP_RETURN; 140 ftp->ftps_pc = (uintptr_t)symp->st_value; 141 ftp->ftps_size = (size_t)symp->st_size; 142 ftp->ftps_noffs = 0; 143 144 /* 145 * If there's a jump table in the function we're only willing to 146 * instrument these specific (and equivalent) instruction sequences: 147 * leave 148 * [rep] ret 149 * and 150 * movl %ebp,%esp 151 * popl %ebp 152 * [rep] ret 153 * 154 * We do this to avoid accidentally interpreting jump table 155 * offsets as actual instructions. 156 */ 157 if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { 158 for (i = 0, end = ftp->ftps_size; i < end; i += size) { 159 size = dt_instr_size(&text[i], dtp, pid, 160 symp->st_value + i, dmodel); 161 162 /* bail if we hit an invalid opcode */ 163 if (size <= 0) 164 break; 165 166 if (text[i] == DT_LEAVE && text[i + 1] == DT_RET) { 167 dt_dprintf("leave/ret at %lx\n", i + 1); 168 ftp->ftps_offs[ftp->ftps_noffs++] = i + 1; 169 size = 2; 170 } else if (text[i] == DT_LEAVE && 171 text[i + 1] == DT_REP && text[i + 2] == DT_RET) { 172 dt_dprintf("leave/rep ret at %lx\n", i + 1); 173 ftp->ftps_offs[ftp->ftps_noffs++] = i + 1; 174 size = 3; 175 } else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP && 176 text[i + 2] == DT_POPL_EBP && 177 text[i + 3] == DT_RET) { 178 dt_dprintf("movl/popl/ret at %lx\n", i + 3); 179 ftp->ftps_offs[ftp->ftps_noffs++] = i + 3; 180 size = 4; 181 } else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP && 182 text[i + 2] == DT_POPL_EBP && 183 text[i + 3] == DT_REP && 184 text[i + 4] == DT_RET) { 185 dt_dprintf("movl/popl/rep ret at %lx\n", i + 3); 186 ftp->ftps_offs[ftp->ftps_noffs++] = i + 3; 187 size = 5; 188 } 189 } 190 } else { 191 for (i = 0, end = ftp->ftps_size; i < end; i += size) { 192 size = dt_instr_size(&text[i], dtp, pid, 193 symp->st_value + i, dmodel); 194 195 /* bail if we hit an invalid opcode */ 196 if (size <= 0) 197 break; 198 199 /* ordinary ret */ 200 if (size == 1 && text[i] == DT_RET) 201 goto is_ret; 202 203 /* two-byte ret */ 204 if (size == 2 && text[i] == DT_REP && 205 text[i + 1] == DT_RET) 206 goto is_ret; 207 208 /* ret <imm16> */ 209 if (size == 3 && text[i] == DT_RET16) 210 goto is_ret; 211 212 /* two-byte ret <imm16> */ 213 if (size == 4 && text[i] == DT_REP && 214 text[i + 1] == DT_RET16) 215 goto is_ret; 216 217 /* 32-bit displacement jmp outside of the function */ 218 if (size == 5 && text[i] == DT_JMP32 && symp->st_size <= 219 (uintptr_t)(i + size + *(int32_t *)&text[i + 1])) 220 goto is_ret; 221 222 /* 8-bit displacement jmp outside of the function */ 223 if (size == 2 && text[i] == DT_JMP8 && symp->st_size <= 224 (uintptr_t)(i + size + *(int8_t *)&text[i + 1])) 225 goto is_ret; 226 227 /* 32-bit disp. conditional jmp outside of the func. */ 228 if (size == 6 && DT_ISJ32(*(uint16_t *)&text[i]) && 229 symp->st_size <= 230 (uintptr_t)(i + size + *(int32_t *)&text[i + 2])) 231 goto is_ret; 232 233 /* 8-bit disp. conditional jmp outside of the func. */ 234 if (size == 2 && DT_ISJ8(text[i]) && symp->st_size <= 235 (uintptr_t)(i + size + *(int8_t *)&text[i + 1])) 236 goto is_ret; 237 238 continue; 239 is_ret: 240 dt_dprintf("return at offset %lx\n", i); 241 ftp->ftps_offs[ftp->ftps_noffs++] = i; 242 } 243 } 244 245 free(text); 246 if (ftp->ftps_noffs > 0) { 247 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 248 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 249 strerror(errno)); 250 return (dt_set_errno(dtp, errno)); 251 } 252 } 253 254 return (ftp->ftps_noffs); 255 } 256 257 /*ARGSUSED*/ 258 int 259 dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 260 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off) 261 { 262 ftp->ftps_type = DTFTP_OFFSETS; 263 ftp->ftps_pc = (uintptr_t)symp->st_value; 264 ftp->ftps_size = (size_t)symp->st_size; 265 ftp->ftps_noffs = 1; 266 267 if (strcmp("-", ftp->ftps_func) == 0) { 268 ftp->ftps_offs[0] = off; 269 } else { 270 uint8_t *text; 271 ulong_t i; 272 int size; 273 pid_t pid = Pstatus(P)->pr_pid; 274 char dmodel = Pstatus(P)->pr_dmodel; 275 276 if ((text = malloc(symp->st_size)) == NULL) { 277 dt_dprintf("mr sparkle: malloc() failed\n"); 278 return (DT_PROC_ERR); 279 } 280 281 if (Pread(P, text, symp->st_size, symp->st_value) != 282 symp->st_size) { 283 dt_dprintf("mr sparkle: Pread() failed\n"); 284 free(text); 285 return (DT_PROC_ERR); 286 } 287 288 /* 289 * We can't instrument offsets in functions with jump tables 290 * as we might interpret a jump table offset as an 291 * instruction. 292 */ 293 if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { 294 free(text); 295 return (0); 296 } 297 298 for (i = 0; i < symp->st_size; i += size) { 299 if (i == off) { 300 ftp->ftps_offs[0] = i; 301 break; 302 } 303 304 /* 305 * If we've passed the desired offset without a 306 * match, then the given offset must not lie on a 307 * instruction boundary. 308 */ 309 if (i > off) { 310 free(text); 311 return (DT_PROC_ALIGN); 312 } 313 314 size = dt_instr_size(&text[i], dtp, pid, 315 symp->st_value + i, dmodel); 316 317 /* 318 * If we hit an invalid instruction, bail as if we 319 * couldn't find the offset. 320 */ 321 if (size <= 0) { 322 free(text); 323 return (DT_PROC_ALIGN); 324 } 325 } 326 327 free(text); 328 } 329 330 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 331 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 332 strerror(errno)); 333 return (dt_set_errno(dtp, errno)); 334 } 335 336 return (ftp->ftps_noffs); 337 } 338 339 /*ARGSUSED*/ 340 int 341 dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp, 342 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern) 343 { 344 uint8_t *text; 345 ulong_t i, end; 346 int size; 347 pid_t pid = Pstatus(P)->pr_pid; 348 char dmodel = Pstatus(P)->pr_dmodel; 349 350 if ((text = malloc(symp->st_size)) == NULL) { 351 dt_dprintf("mr sparkle: malloc() failed\n"); 352 return (DT_PROC_ERR); 353 } 354 355 if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { 356 dt_dprintf("mr sparkle: Pread() failed\n"); 357 free(text); 358 return (DT_PROC_ERR); 359 } 360 361 /* 362 * We can't instrument offsets in functions with jump tables as 363 * we might interpret a jump table offset as an instruction. 364 */ 365 if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { 366 free(text); 367 return (0); 368 } 369 370 ftp->ftps_type = DTFTP_OFFSETS; 371 ftp->ftps_pc = (uintptr_t)symp->st_value; 372 ftp->ftps_size = (size_t)symp->st_size; 373 ftp->ftps_noffs = 0; 374 375 end = ftp->ftps_size; 376 377 if (strcmp("*", pattern) == 0) { 378 for (i = 0; i < end; i += size) { 379 ftp->ftps_offs[ftp->ftps_noffs++] = i; 380 381 size = dt_instr_size(&text[i], dtp, pid, 382 symp->st_value + i, dmodel); 383 384 /* bail if we hit an invalid opcode */ 385 if (size <= 0) 386 break; 387 } 388 } else { 389 char name[sizeof (i) * 2 + 1]; 390 391 for (i = 0; i < end; i += size) { 392 (void) snprintf(name, sizeof (name), "%x", i); 393 if (gmatch(name, pattern)) 394 ftp->ftps_offs[ftp->ftps_noffs++] = i; 395 396 size = dt_instr_size(&text[i], dtp, pid, 397 symp->st_value + i, dmodel); 398 399 /* bail if we hit an invalid opcode */ 400 if (size <= 0) 401 break; 402 } 403 } 404 405 free(text); 406 if (ftp->ftps_noffs > 0) { 407 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 408 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 409 strerror(errno)); 410 return (dt_set_errno(dtp, errno)); 411 } 412 } 413 414 return (ftp->ftps_noffs); 415 } 416 417 typedef struct dtrace_dis { 418 uchar_t *instr; 419 dtrace_hdl_t *dtp; 420 pid_t pid; 421 uintptr_t addr; 422 } dtrace_dis_t; 423 424 static int 425 dt_getbyte(void *data) 426 { 427 dtrace_dis_t *dis = data; 428 int ret = *dis->instr; 429 430 if (ret == FASTTRAP_INSTR) { 431 fasttrap_instr_query_t instr; 432 433 instr.ftiq_pid = dis->pid; 434 instr.ftiq_pc = dis->addr; 435 436 /* 437 * If we hit a byte that looks like the fasttrap provider's 438 * trap instruction (which doubles as the breakpoint 439 * instruction for debuggers) we need to query the kernel 440 * for the real value. This may just be part of an immediate 441 * value so there's no need to return an error if the 442 * kernel doesn't know about this address. 443 */ 444 if (ioctl(dis->dtp->dt_ftfd, FASTTRAPIOC_GETINSTR, &instr) == 0) 445 ret = instr.ftiq_instr; 446 } 447 448 dis->addr++; 449 dis->instr++; 450 451 return (ret); 452 } 453 454 static int 455 dt_instr_size(uchar_t *instr, dtrace_hdl_t *dtp, pid_t pid, uintptr_t addr, 456 char dmodel) 457 { 458 dtrace_dis_t data; 459 dis86_t x86dis; 460 uint_t cpu_mode; 461 462 data.instr = instr; 463 data.dtp = dtp; 464 data.pid = pid; 465 data.addr = addr; 466 467 x86dis.d86_data = &data; 468 x86dis.d86_get_byte = dt_getbyte; 469 x86dis.d86_check_func = NULL; 470 471 cpu_mode = (dmodel == PR_MODEL_ILP32) ? SIZE32 : SIZE64; 472 473 if (dtrace_disx86(&x86dis, cpu_mode) != 0) 474 return (-1); 475 476 /* 477 * If the instruction was a single-byte breakpoint, there may be 478 * another debugger attached to this process. The original instruction 479 * can't be recovered so this must fail. 480 */ 481 if (x86dis.d86_len == 1 && instr[0] == FASTTRAP_INSTR) 482 return (-1); 483 484 return (x86dis.d86_len); 485 } 486