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
63 static int dt_instr_size(uchar_t *, dtrace_hdl_t *, pid_t, uintptr_t, char);
64
65 /*ARGSUSED*/
66 int
dt_pid_create_entry_probe(struct ps_prochandle * P,dtrace_hdl_t * dtp,fasttrap_probe_spec_t * ftp,const GElf_Sym * symp)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
dt_pid_has_jump_table(struct ps_prochandle * P,dtrace_hdl_t * dtp,uint8_t * text,fasttrap_probe_spec_t * ftp,const GElf_Sym * symp)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 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*/
136 int
dt_pid_create_return_probe(struct ps_prochandle * P,dtrace_hdl_t * dtp,fasttrap_probe_spec_t * ftp,const GElf_Sym * symp,uint64_t * stret)137 dt_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;
266 is_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*/
285 int
dt_pid_create_offset_probe(struct ps_prochandle * P,dtrace_hdl_t * dtp,fasttrap_probe_spec_t * ftp,const GElf_Sym * symp,ulong_t off)286 dt_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*/
372 int
dt_pid_create_glob_offset_probes(struct ps_prochandle * P,dtrace_hdl_t * dtp,fasttrap_probe_spec_t * ftp,const GElf_Sym * symp,const char * pattern)373 dt_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
452 typedef 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
459 static int
dt_getbyte(void * data)460 dt_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
489 static int
dt_instr_size(uchar_t * instr,dtrace_hdl_t * dtp,pid_t pid,uintptr_t addr,char dmodel)490 dt_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