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#include <sys/ioctl.h> 37 38#include <dt_impl.h> 39#include <dt_pid.h> 40 41#include <dis_tables.h> 42 43#if defined(__FreeBSD__) || defined(__NetBSD__) 44#include <libproc.h> 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 63static int dt_instr_size(uchar_t *, dtrace_hdl_t *, pid_t, uintptr_t, char); 64 65/*ARGSUSED*/ 66int 67dt_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 85static int 86dt_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 char dmodel = proc_getmodel(P); 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*/ 136int 137dt_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#ifdef illumos 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 char dmodel = proc_getmodel(P); 149#endif 150 151 /* 152 * We allocate a few extra bytes at the end so we don't have to check 153 * for overrunning the buffer. 154 */ 155 if ((text = calloc(1, symp->st_size + 4)) == NULL) { 156 dt_dprintf("mr sparkle: malloc() failed\n"); 157 return (DT_PROC_ERR); 158 } 159 160 if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { 161 dt_dprintf("mr sparkle: Pread() failed\n"); 162 free(text); 163 return (DT_PROC_ERR); 164 } 165 166 ftp->ftps_type = DTFTP_RETURN; 167 ftp->ftps_pc = (uintptr_t)symp->st_value; 168 ftp->ftps_size = (size_t)symp->st_size; 169 ftp->ftps_noffs = 0; 170 171 /* 172 * If there's a jump table in the function we're only willing to 173 * instrument these specific (and equivalent) instruction sequences: 174 * leave 175 * [rep] ret 176 * and 177 * movl %ebp,%esp 178 * popl %ebp 179 * [rep] ret 180 * 181 * We do this to avoid accidentally interpreting jump table 182 * offsets as actual instructions. 183 */ 184 if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { 185 for (i = 0, end = ftp->ftps_size; i < end; i += size) { 186 size = dt_instr_size(&text[i], dtp, pid, 187 symp->st_value + i, dmodel); 188 189 /* bail if we hit an invalid opcode */ 190 if (size <= 0) 191 break; 192 193 if (text[i] == DT_LEAVE && text[i + 1] == DT_RET) { 194 dt_dprintf("leave/ret at %lx\n", i + 1); 195 ftp->ftps_offs[ftp->ftps_noffs++] = i + 1; 196 size = 2; 197 } else if (text[i] == DT_LEAVE && 198 text[i + 1] == DT_REP && text[i + 2] == DT_RET) { 199 dt_dprintf("leave/rep ret at %lx\n", i + 1); 200 ftp->ftps_offs[ftp->ftps_noffs++] = i + 1; 201 size = 3; 202 } else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP && 203 text[i + 2] == DT_POPL_EBP && 204 text[i + 3] == DT_RET) { 205 dt_dprintf("movl/popl/ret at %lx\n", i + 3); 206 ftp->ftps_offs[ftp->ftps_noffs++] = i + 3; 207 size = 4; 208 } else if (*(uint16_t *)&text[i] == DT_MOVL_EBP_ESP && 209 text[i + 2] == DT_POPL_EBP && 210 text[i + 3] == DT_REP && 211 text[i + 4] == DT_RET) { 212 dt_dprintf("movl/popl/rep ret at %lx\n", i + 3); 213 ftp->ftps_offs[ftp->ftps_noffs++] = i + 3; 214 size = 5; 215 } 216 } 217 } else { 218 for (i = 0, end = ftp->ftps_size; i < end; i += size) { 219 size = dt_instr_size(&text[i], dtp, pid, 220 symp->st_value + i, dmodel); 221 222 /* bail if we hit an invalid opcode */ 223 if (size <= 0) 224 break; 225 226 /* ordinary ret */ 227 if (size == 1 && text[i] == DT_RET) 228 goto is_ret; 229 230 /* two-byte ret */ 231 if (size == 2 && text[i] == DT_REP && 232 text[i + 1] == DT_RET) 233 goto is_ret; 234 235 /* ret <imm16> */ 236 if (size == 3 && text[i] == DT_RET16) 237 goto is_ret; 238 239 /* two-byte ret <imm16> */ 240 if (size == 4 && text[i] == DT_REP && 241 text[i + 1] == DT_RET16) 242 goto is_ret; 243 244 /* 32-bit displacement jmp outside of the function */ 245 if (size == 5 && text[i] == DT_JMP32 && symp->st_size <= 246 (uintptr_t)(i + size + *(int32_t *)&text[i + 1])) 247 goto is_ret; 248 249 /* 8-bit displacement jmp outside of the function */ 250 if (size == 2 && text[i] == DT_JMP8 && symp->st_size <= 251 (uintptr_t)(i + size + *(int8_t *)&text[i + 1])) 252 goto is_ret; 253 254 /* 32-bit disp. conditional jmp outside of the func. */ 255 if (size == 6 && DT_ISJ32(*(uint16_t *)&text[i]) && 256 symp->st_size <= 257 (uintptr_t)(i + size + *(int32_t *)&text[i + 2])) 258 goto is_ret; 259 260 /* 8-bit disp. conditional jmp outside of the func. */ 261 if (size == 2 && DT_ISJ8(text[i]) && symp->st_size <= 262 (uintptr_t)(i + size + *(int8_t *)&text[i + 1])) 263 goto is_ret; 264 265 continue; 266is_ret: 267 dt_dprintf("return at offset %lx\n", i); 268 ftp->ftps_offs[ftp->ftps_noffs++] = i; 269 } 270 } 271 272 free(text); 273 if (ftp->ftps_noffs > 0) { 274 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 275 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 276 strerror(errno)); 277 return (dt_set_errno(dtp, errno)); 278 } 279 } 280 281 return (ftp->ftps_noffs); 282} 283 284/*ARGSUSED*/ 285int 286dt_pid_create_offset_probe(struct ps_prochandle *P, dtrace_hdl_t *dtp, 287 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, ulong_t off) 288{ 289 ftp->ftps_type = DTFTP_OFFSETS; 290 ftp->ftps_pc = (uintptr_t)symp->st_value; 291 ftp->ftps_size = (size_t)symp->st_size; 292 ftp->ftps_noffs = 1; 293 294 if (strcmp("-", ftp->ftps_func) == 0) { 295 ftp->ftps_offs[0] = off; 296 } else { 297 uint8_t *text; 298 ulong_t i; 299 int size; 300#ifdef illumos 301 pid_t pid = Pstatus(P)->pr_pid; 302 char dmodel = Pstatus(P)->pr_dmodel; 303#else 304 pid_t pid = proc_getpid(P); 305 char dmodel = proc_getmodel(P); 306#endif 307 308 if ((text = malloc(symp->st_size)) == NULL) { 309 dt_dprintf("mr sparkle: malloc() failed\n"); 310 return (DT_PROC_ERR); 311 } 312 313 if (Pread(P, text, symp->st_size, symp->st_value) != 314 symp->st_size) { 315 dt_dprintf("mr sparkle: Pread() failed\n"); 316 free(text); 317 return (DT_PROC_ERR); 318 } 319 320 /* 321 * We can't instrument offsets in functions with jump tables 322 * as we might interpret a jump table offset as an 323 * instruction. 324 */ 325 if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { 326 free(text); 327 return (0); 328 } 329 330 for (i = 0; i < symp->st_size; i += size) { 331 if (i == off) { 332 ftp->ftps_offs[0] = i; 333 break; 334 } 335 336 /* 337 * If we've passed the desired offset without a 338 * match, then the given offset must not lie on a 339 * instruction boundary. 340 */ 341 if (i > off) { 342 free(text); 343 return (DT_PROC_ALIGN); 344 } 345 346 size = dt_instr_size(&text[i], dtp, pid, 347 symp->st_value + i, dmodel); 348 349 /* 350 * If we hit an invalid instruction, bail as if we 351 * couldn't find the offset. 352 */ 353 if (size <= 0) { 354 free(text); 355 return (DT_PROC_ALIGN); 356 } 357 } 358 359 free(text); 360 } 361 362 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 363 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 364 strerror(errno)); 365 return (dt_set_errno(dtp, errno)); 366 } 367 368 return (ftp->ftps_noffs); 369} 370 371/*ARGSUSED*/ 372int 373dt_pid_create_glob_offset_probes(struct ps_prochandle *P, dtrace_hdl_t *dtp, 374 fasttrap_probe_spec_t *ftp, const GElf_Sym *symp, const char *pattern) 375{ 376 uint8_t *text; 377 int size; 378 ulong_t i, end = symp->st_size; 379#ifdef illumos 380 pid_t pid = Pstatus(P)->pr_pid; 381 char dmodel = Pstatus(P)->pr_dmodel; 382#else 383 pid_t pid = proc_getpid(P); 384 char dmodel = proc_getmodel(P); 385#endif 386 387 ftp->ftps_type = DTFTP_OFFSETS; 388 ftp->ftps_pc = (uintptr_t)symp->st_value; 389 ftp->ftps_size = (size_t)symp->st_size; 390 ftp->ftps_noffs = 0; 391 392 if ((text = malloc(symp->st_size)) == NULL) { 393 dt_dprintf("mr sparkle: malloc() failed\n"); 394 return (DT_PROC_ERR); 395 } 396 397 if (Pread(P, text, symp->st_size, symp->st_value) != symp->st_size) { 398 dt_dprintf("mr sparkle: Pread() failed\n"); 399 free(text); 400 return (DT_PROC_ERR); 401 } 402 403 /* 404 * We can't instrument offsets in functions with jump tables as 405 * we might interpret a jump table offset as an instruction. 406 */ 407 if (dt_pid_has_jump_table(P, dtp, text, ftp, symp)) { 408 free(text); 409 return (0); 410 } 411 412 if (strcmp("*", pattern) == 0) { 413 for (i = 0; i < end; i += size) { 414 ftp->ftps_offs[ftp->ftps_noffs++] = i; 415 416 size = dt_instr_size(&text[i], dtp, pid, 417 symp->st_value + i, dmodel); 418 419 /* bail if we hit an invalid opcode */ 420 if (size <= 0) 421 break; 422 } 423 } else { 424 char name[sizeof (i) * 2 + 1]; 425 426 for (i = 0; i < end; i += size) { 427 (void) snprintf(name, sizeof (name), "%lx", i); 428 if (gmatch(name, pattern)) 429 ftp->ftps_offs[ftp->ftps_noffs++] = i; 430 431 size = dt_instr_size(&text[i], dtp, pid, 432 symp->st_value + i, dmodel); 433 434 /* bail if we hit an invalid opcode */ 435 if (size <= 0) 436 break; 437 } 438 } 439 440 free(text); 441 if (ftp->ftps_noffs > 0) { 442 if (ioctl(dtp->dt_ftfd, FASTTRAPIOC_MAKEPROBE, ftp) != 0) { 443 dt_dprintf("fasttrap probe creation ioctl failed: %s\n", 444 strerror(errno)); 445 return (dt_set_errno(dtp, errno)); 446 } 447 } 448 449 return (ftp->ftps_noffs); 450} 451 452typedef struct dtrace_dis { 453 uchar_t *instr; 454 dtrace_hdl_t *dtp; 455 pid_t pid; 456 uintptr_t addr; 457} dtrace_dis_t; 458 459static int 460dt_getbyte(void *data) 461{ 462 dtrace_dis_t *dis = data; 463 int ret = *dis->instr; 464 465 if (ret == FASTTRAP_INSTR) { 466 fasttrap_instr_query_t instr; 467 468 instr.ftiq_pid = dis->pid; 469 instr.ftiq_pc = dis->addr; 470 471 /* 472 * If we hit a byte that looks like the fasttrap provider's 473 * trap instruction (which doubles as the breakpoint 474 * instruction for debuggers) we need to query the kernel 475 * for the real value. This may just be part of an immediate 476 * value so there's no need to return an error if the 477 * kernel doesn't know about this address. 478 */ 479 if (ioctl(dis->dtp->dt_ftfd, FASTTRAPIOC_GETINSTR, &instr) == 0) 480 ret = instr.ftiq_instr; 481 } 482 483 dis->addr++; 484 dis->instr++; 485 486 return (ret); 487} 488 489static int 490dt_instr_size(uchar_t *instr, dtrace_hdl_t *dtp, pid_t pid, uintptr_t addr, 491 char dmodel) 492{ 493 dtrace_dis_t data; 494 dis86_t x86dis; 495 uint_t cpu_mode; 496 497 data.instr = instr; 498 data.dtp = dtp; 499 data.pid = pid; 500 data.addr = addr; 501 502 x86dis.d86_data = &data; 503 x86dis.d86_get_byte = dt_getbyte; 504 x86dis.d86_check_func = NULL; 505 506 cpu_mode = (dmodel == PR_MODEL_ILP32) ? SIZE32 : SIZE64; 507 508 if (dtrace_disx86(&x86dis, cpu_mode) != 0) 509 return (-1); 510 511 /* 512 * If the instruction was a single-byte breakpoint, there may be 513 * another debugger attached to this process. The original instruction 514 * can't be recovered so this must fail. 515 */ 516 if (x86dis.d86_len == 1 && 517 (uchar_t)x86dis.d86_bytes[0] == FASTTRAP_INSTR) 518 return (-1); 519 520 return (x86dis.d86_len); 521} 522