xref: /illumos-gate/usr/src/lib/libdtrace/i386/dt_isadep.c (revision 2f172c55ef76964744bc62b4500ece87f3089b4d)
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