xref: /onnv-gate/usr/src/lib/libdisasm/i386/dis_i386.c (revision 1545:8f6fb1eeee38)
1*1545Seschrock /*
2*1545Seschrock  * CDDL HEADER START
3*1545Seschrock  *
4*1545Seschrock  * The contents of this file are subject to the terms of the
5*1545Seschrock  * Common Development and Distribution License (the "License").
6*1545Seschrock  * You may not use this file except in compliance with the License.
7*1545Seschrock  *
8*1545Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1545Seschrock  * or http://www.opensolaris.org/os/licensing.
10*1545Seschrock  * See the License for the specific language governing permissions
11*1545Seschrock  * and limitations under the License.
12*1545Seschrock  *
13*1545Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14*1545Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1545Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16*1545Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17*1545Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1545Seschrock  *
19*1545Seschrock  * CDDL HEADER END
20*1545Seschrock  */
21*1545Seschrock 
22*1545Seschrock /*
23*1545Seschrock  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
24*1545Seschrock  * Use is subject to license terms.
25*1545Seschrock  */
26*1545Seschrock 
27*1545Seschrock #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*1545Seschrock 
29*1545Seschrock #include <libdisasm.h>
30*1545Seschrock #include <stdlib.h>
31*1545Seschrock #include <stdio.h>
32*1545Seschrock 
33*1545Seschrock #include "dis_tables.h"
34*1545Seschrock #include "libdisasm_impl.h"
35*1545Seschrock 
36*1545Seschrock struct dis_handle {
37*1545Seschrock 	void		*dh_data;
38*1545Seschrock 	int		dh_flags;
39*1545Seschrock 	dis_lookup_f	dh_lookup;
40*1545Seschrock 	dis_read_f	dh_read;
41*1545Seschrock 	int		dh_mode;
42*1545Seschrock 	dis86_t		dh_dis;
43*1545Seschrock 	uint64_t	dh_addr;
44*1545Seschrock 	uint64_t	dh_end;
45*1545Seschrock };
46*1545Seschrock 
47*1545Seschrock /*
48*1545Seschrock  * Returns true if we are near the end of a function.  This is a cheap hack at
49*1545Seschrock  * detecting NULL padding between functions.  If we're within a few bytes of the
50*1545Seschrock  * next function, or past the start, then return true.
51*1545Seschrock  */
52*1545Seschrock static int
53*1545Seschrock check_func(void *data)
54*1545Seschrock {
55*1545Seschrock 	dis_handle_t *dhp = data;
56*1545Seschrock 	uint64_t start;
57*1545Seschrock 	size_t len;
58*1545Seschrock 
59*1545Seschrock 	if (dhp->dh_lookup(dhp->dh_data, dhp->dh_addr, NULL, 0, &start, &len)
60*1545Seschrock 	    != 0)
61*1545Seschrock 		return (0);
62*1545Seschrock 
63*1545Seschrock 	if (start < dhp->dh_addr)
64*1545Seschrock 		return (dhp->dh_addr > start + len - 0x10);
65*1545Seschrock 
66*1545Seschrock 	return (1);
67*1545Seschrock }
68*1545Seschrock 
69*1545Seschrock static int
70*1545Seschrock get_byte(void *data)
71*1545Seschrock {
72*1545Seschrock 	uchar_t byte;
73*1545Seschrock 	dis_handle_t *dhp = data;
74*1545Seschrock 
75*1545Seschrock 	if (dhp->dh_read(dhp->dh_data, dhp->dh_addr, &byte,
76*1545Seschrock 	    sizeof (byte)) < sizeof (byte))
77*1545Seschrock 		return (-1);
78*1545Seschrock 
79*1545Seschrock 	dhp->dh_addr++;
80*1545Seschrock 
81*1545Seschrock 	return ((int)byte);
82*1545Seschrock }
83*1545Seschrock 
84*1545Seschrock static int
85*1545Seschrock do_lookup(void *data, uint64_t addr, char *buf, size_t buflen)
86*1545Seschrock {
87*1545Seschrock 	dis_handle_t *dhp = data;
88*1545Seschrock 
89*1545Seschrock 	return (dhp->dh_lookup(dhp->dh_data, addr, buf, buflen, NULL, NULL));
90*1545Seschrock }
91*1545Seschrock 
92*1545Seschrock dis_handle_t *
93*1545Seschrock dis_handle_create(int flags, void *data, dis_lookup_f lookup_func,
94*1545Seschrock     dis_read_f read_func)
95*1545Seschrock {
96*1545Seschrock 	dis_handle_t *dhp;
97*1545Seschrock 
98*1545Seschrock 	/*
99*1545Seschrock 	 * Validate architecture flags
100*1545Seschrock 	 */
101*1545Seschrock 	if (flags & ~(DIS_X86_SIZE16 | DIS_X86_SIZE32 | DIS_X86_SIZE64 |
102*1545Seschrock 	    DIS_OCTAL)) {
103*1545Seschrock 		(void) dis_seterrno(E_DIS_INVALFLAG);
104*1545Seschrock 		return (NULL);
105*1545Seschrock 	}
106*1545Seschrock 
107*1545Seschrock 	/*
108*1545Seschrock 	 * Create and initialize the internal structure
109*1545Seschrock 	 */
110*1545Seschrock 	if ((dhp = dis_zalloc(sizeof (struct dis_handle))) == NULL) {
111*1545Seschrock 		(void) dis_seterrno(E_DIS_NOMEM);
112*1545Seschrock 		return (NULL);
113*1545Seschrock 	}
114*1545Seschrock 
115*1545Seschrock 	dhp->dh_lookup = lookup_func;
116*1545Seschrock 	dhp->dh_read = read_func;
117*1545Seschrock 	dhp->dh_flags = flags;
118*1545Seschrock 	dhp->dh_data = data;
119*1545Seschrock 
120*1545Seschrock 	/*
121*1545Seschrock 	 * Initialize x86-specific architecture structure
122*1545Seschrock 	 */
123*1545Seschrock 	if (flags & DIS_X86_SIZE16)
124*1545Seschrock 		dhp->dh_mode = SIZE16;
125*1545Seschrock 	else if (flags & DIS_X86_SIZE64)
126*1545Seschrock 		dhp->dh_mode = SIZE64;
127*1545Seschrock 	else
128*1545Seschrock 		dhp->dh_mode = SIZE32;
129*1545Seschrock 
130*1545Seschrock 	if (flags & DIS_OCTAL)
131*1545Seschrock 		dhp->dh_dis.d86_flags = DIS_OP_OCTAL;
132*1545Seschrock 
133*1545Seschrock 	dhp->dh_dis.d86_sprintf_func = snprintf;
134*1545Seschrock 	dhp->dh_dis.d86_get_byte = get_byte;
135*1545Seschrock 	dhp->dh_dis.d86_sym_lookup = do_lookup;
136*1545Seschrock 	dhp->dh_dis.d86_check_func = check_func;
137*1545Seschrock 
138*1545Seschrock 	dhp->dh_dis.d86_data = dhp;
139*1545Seschrock 
140*1545Seschrock 	return (dhp);
141*1545Seschrock }
142*1545Seschrock 
143*1545Seschrock int
144*1545Seschrock dis_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf, size_t buflen)
145*1545Seschrock {
146*1545Seschrock 	dhp->dh_addr = addr;
147*1545Seschrock 
148*1545Seschrock 	if (dtrace_disx86(&dhp->dh_dis, dhp->dh_mode) != 0)
149*1545Seschrock 		return (-1);
150*1545Seschrock 
151*1545Seschrock 	if (buf != NULL)
152*1545Seschrock 		dtrace_disx86_str(&dhp->dh_dis, dhp->dh_mode, addr, buf,
153*1545Seschrock 		    buflen);
154*1545Seschrock 
155*1545Seschrock 	return (0);
156*1545Seschrock }
157*1545Seschrock 
158*1545Seschrock void
159*1545Seschrock dis_handle_destroy(dis_handle_t *dhp)
160*1545Seschrock {
161*1545Seschrock 	dis_free(dhp, sizeof (dis_handle_t));
162*1545Seschrock }
163*1545Seschrock 
164*1545Seschrock void
165*1545Seschrock dis_set_data(dis_handle_t *dhp, void *data)
166*1545Seschrock {
167*1545Seschrock 	dhp->dh_data = data;
168*1545Seschrock }
169*1545Seschrock 
170*1545Seschrock /* ARGSUSED */
171*1545Seschrock int
172*1545Seschrock dis_max_instrlen(dis_handle_t *dhp)
173*1545Seschrock {
174*1545Seschrock 	return (15);
175*1545Seschrock }
176*1545Seschrock 
177*1545Seschrock #define	MIN(a, b)	((a) < (b) ? (a) : (b))
178*1545Seschrock 
179*1545Seschrock /*
180*1545Seschrock  * Return the previous instruction.  On x86, we have no choice except to
181*1545Seschrock  * disassemble everything from the start of the symbol, and stop when we have
182*1545Seschrock  * reached our instruction address.  If we're not in the middle of a known
183*1545Seschrock  * symbol, then we return the same address to indicate failure.
184*1545Seschrock  */
185*1545Seschrock uint64_t
186*1545Seschrock dis_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
187*1545Seschrock {
188*1545Seschrock 	uint64_t *hist, addr, start;
189*1545Seschrock 	int cur, nseen;
190*1545Seschrock 	uint64_t res = pc;
191*1545Seschrock 
192*1545Seschrock 	if (dhp->dh_lookup(dhp->dh_data, pc, NULL, 0, &start, NULL) != 0 ||
193*1545Seschrock 	    start == pc)
194*1545Seschrock 		return (res);
195*1545Seschrock 
196*1545Seschrock 	hist = dis_zalloc(sizeof (uint64_t) * n);
197*1545Seschrock 
198*1545Seschrock 	for (cur = 0, nseen = 0, addr = start; addr < pc; addr = dhp->dh_addr) {
199*1545Seschrock 		hist[cur] = addr;
200*1545Seschrock 		cur = (cur + 1) % n;
201*1545Seschrock 		nseen++;
202*1545Seschrock 
203*1545Seschrock 		/* if we cannot make forward progress, give up */
204*1545Seschrock 		if (dis_disassemble(dhp, addr, NULL, 0) != 0)
205*1545Seschrock 			goto done;
206*1545Seschrock 	}
207*1545Seschrock 
208*1545Seschrock 	if (addr != pc) {
209*1545Seschrock 		/*
210*1545Seschrock 		 * We scanned past %pc, but didn't find an instruction that
211*1545Seschrock 		 * started at %pc.  This means that either the caller specified
212*1545Seschrock 		 * an invalid address, or we ran into something other than code
213*1545Seschrock 		 * during our scan.  Virtually any combination of bytes can be
214*1545Seschrock 		 * construed as a valid Intel instruction, so any non-code bytes
215*1545Seschrock 		 * we encounter will have thrown off the scan.
216*1545Seschrock 		 */
217*1545Seschrock 		goto done;
218*1545Seschrock 	}
219*1545Seschrock 
220*1545Seschrock 	res = hist[(cur + n - MIN(n, nseen)) % n];
221*1545Seschrock 
222*1545Seschrock done:
223*1545Seschrock 	dis_free(hist, sizeof (uint64_t) * n);
224*1545Seschrock 	return (res);
225*1545Seschrock }
226