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