xref: /titanic_50/usr/src/lib/libdisasm/common/libdisasm.c (revision 0472e113e9ad4a95dbf46a1d97075472136a1e7c)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  * Copyright 2012 Joshua M. Clulow <josh@sysmgr.org>
26  * Copyright 2015 Nexenta Systems, Inc.  All rights reserved.
27  */
28 
29 #include <libdisasm.h>
30 #include <stdlib.h>
31 #ifdef DIS_STANDALONE
32 #include <mdb/mdb_modapi.h>
33 #define	_MDB
34 #include <mdb/mdb_io.h>
35 #else
36 #include <stdio.h>
37 #endif
38 
39 #include "libdisasm_impl.h"
40 
41 static int _dis_errno;
42 
43 /*
44  * If we're building the standalone library, then we only want to
45  * include support for disassembly of the native architecture.
46  * The regular shared library should include support for all
47  * architectures.
48  */
49 #if !defined(DIS_STANDALONE) || defined(__i386) || defined(__amd64)
50 extern dis_arch_t dis_arch_i386;
51 #endif
52 #if !defined(DIS_STANDALONE) || defined(__sparc)
53 extern dis_arch_t dis_arch_sparc;
54 #endif
55 #if !defined(DIS_STANDALONE) || defined(__s390) || defined(__s390x)
56 extern dis_arch_t dis_arch_s390;
57 #endif
58 
59 static dis_arch_t *dis_archs[] = {
60 #if !defined(DIS_STANDALONE) || defined(__i386) || defined(__amd64)
61 	&dis_arch_i386,
62 #endif
63 #if !defined(DIS_STANDALONE) || defined(__sparc)
64 	&dis_arch_sparc,
65 #endif
66 #if !defined(DIS_STANDALONE) || defined(__s390) || defined(__s390x)
67 	&dis_arch_s390,
68 #endif
69 	NULL
70 };
71 
72 /*
73  * For the standalone library, we need to link against mdb's malloc/free.
74  * Otherwise, use the standard malloc/free.
75  */
76 #ifdef DIS_STANDALONE
77 void *
dis_zalloc(size_t bytes)78 dis_zalloc(size_t bytes)
79 {
80 	return (mdb_zalloc(bytes, UM_SLEEP));
81 }
82 
83 void
dis_free(void * ptr,size_t bytes)84 dis_free(void *ptr, size_t bytes)
85 {
86 	mdb_free(ptr, bytes);
87 }
88 #else
89 void *
dis_zalloc(size_t bytes)90 dis_zalloc(size_t bytes)
91 {
92 	return (calloc(1, bytes));
93 }
94 
95 /*ARGSUSED*/
96 void
dis_free(void * ptr,size_t bytes)97 dis_free(void *ptr, size_t bytes)
98 {
99 	free(ptr);
100 }
101 #endif
102 
103 int
dis_seterrno(int error)104 dis_seterrno(int error)
105 {
106 	_dis_errno = error;
107 	return (-1);
108 }
109 
110 int
dis_errno(void)111 dis_errno(void)
112 {
113 	return (_dis_errno);
114 }
115 
116 const char *
dis_strerror(int error)117 dis_strerror(int error)
118 {
119 	switch (error) {
120 	case E_DIS_NOMEM:
121 		return ("out of memory");
122 	case E_DIS_INVALFLAG:
123 		return ("invalid flags for this architecture");
124 	case E_DIS_UNSUPARCH:
125 		return ("unsupported machine architecture");
126 	default:
127 		return ("unknown error");
128 	}
129 }
130 
131 void
dis_set_data(dis_handle_t * dhp,void * data)132 dis_set_data(dis_handle_t *dhp, void *data)
133 {
134 	dhp->dh_data = data;
135 }
136 
137 void
dis_flags_set(dis_handle_t * dhp,int f)138 dis_flags_set(dis_handle_t *dhp, int f)
139 {
140 	dhp->dh_flags |= f;
141 }
142 
143 void
dis_flags_clear(dis_handle_t * dhp,int f)144 dis_flags_clear(dis_handle_t *dhp, int f)
145 {
146 	dhp->dh_flags &= ~f;
147 }
148 
149 void
dis_handle_destroy(dis_handle_t * dhp)150 dis_handle_destroy(dis_handle_t *dhp)
151 {
152 	if (dhp->dh_arch->da_handle_detach != NULL)
153 		dhp->dh_arch->da_handle_detach(dhp);
154 
155 	dis_free(dhp, sizeof (dis_handle_t));
156 }
157 
158 dis_handle_t *
dis_handle_create(int flags,void * data,dis_lookup_f lookup_func,dis_read_f read_func)159 dis_handle_create(int flags, void *data, dis_lookup_f lookup_func,
160     dis_read_f read_func)
161 {
162 	dis_handle_t *dhp;
163 	dis_arch_t *arch = NULL;
164 	int i;
165 
166 	/* Select an architecture based on flags */
167 	for (i = 0; dis_archs[i] != NULL; i++) {
168 		if (dis_archs[i]->da_supports_flags(flags)) {
169 			arch = dis_archs[i];
170 			break;
171 		}
172 	}
173 	if (arch == NULL) {
174 		(void) dis_seterrno(E_DIS_UNSUPARCH);
175 		return (NULL);
176 	}
177 
178 	if ((dhp = dis_zalloc(sizeof (dis_handle_t))) == NULL) {
179 		(void) dis_seterrno(E_DIS_NOMEM);
180 		return (NULL);
181 	}
182 	dhp->dh_arch = arch;
183 	dhp->dh_lookup = lookup_func;
184 	dhp->dh_read = read_func;
185 	dhp->dh_flags = flags;
186 	dhp->dh_data = data;
187 
188 	/*
189 	 * Allow the architecture-specific code to allocate
190 	 * its private data.
191 	 */
192 	if (arch->da_handle_attach != NULL &&
193 	    arch->da_handle_attach(dhp) != 0) {
194 		dis_free(dhp, sizeof (dis_handle_t));
195 		/* dis errno already set */
196 		return (NULL);
197 	}
198 
199 	return (dhp);
200 }
201 
202 int
dis_disassemble(dis_handle_t * dhp,uint64_t addr,char * buf,size_t buflen)203 dis_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf, size_t buflen)
204 {
205 	return (dhp->dh_arch->da_disassemble(dhp, addr, buf, buflen));
206 }
207 
208 /*
209  * On some instruction sets (e.g., x86), we have no choice except to
210  * disassemble everything from the start of the symbol, and stop when we
211  * have reached our instruction address.  If we're not in the middle of a
212  * known symbol, then we return the same address to indicate failure.
213  */
214 static uint64_t
dis_generic_previnstr(dis_handle_t * dhp,uint64_t pc,int n)215 dis_generic_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
216 {
217 	uint64_t *hist, addr, start;
218 	int cur, nseen;
219 	uint64_t res = pc;
220 
221 	if (n <= 0)
222 		return (pc);
223 
224 	if (dhp->dh_lookup(dhp->dh_data, pc, NULL, 0, &start, NULL) != 0 ||
225 	    start == pc)
226 		return (res);
227 
228 	hist = dis_zalloc(sizeof (uint64_t) * n);
229 
230 	for (cur = 0, nseen = 0, addr = start; addr < pc; addr = dhp->dh_addr) {
231 		hist[cur] = addr;
232 		cur = (cur + 1) % n;
233 		nseen++;
234 
235 		/* if we cannot make forward progress, give up */
236 		if (dis_disassemble(dhp, addr, NULL, 0) != 0)
237 			goto done;
238 	}
239 
240 	if (addr != pc) {
241 		/*
242 		 * We scanned past %pc, but didn't find an instruction that
243 		 * started at %pc.  This means that either the caller specified
244 		 * an invalid address, or we ran into something other than code
245 		 * during our scan.  Virtually any combination of bytes can be
246 		 * construed as a valid Intel instruction, so any non-code bytes
247 		 * we encounter will have thrown off the scan.
248 		 */
249 		goto done;
250 	}
251 
252 	res = hist[(cur + n - MIN(n, nseen)) % n];
253 
254 done:
255 	dis_free(hist, sizeof (uint64_t) * n);
256 	return (res);
257 }
258 
259 /*
260  * Return the nth previous instruction's address.  Return the same address
261  * to indicate failure.
262  */
263 uint64_t
dis_previnstr(dis_handle_t * dhp,uint64_t pc,int n)264 dis_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
265 {
266 	if (dhp->dh_arch->da_previnstr == NULL)
267 		return (dis_generic_previnstr(dhp, pc, n));
268 
269 	return (dhp->dh_arch->da_previnstr(dhp, pc, n));
270 }
271 
272 int
dis_min_instrlen(dis_handle_t * dhp)273 dis_min_instrlen(dis_handle_t *dhp)
274 {
275 	return (dhp->dh_arch->da_min_instrlen(dhp));
276 }
277 
278 int
dis_max_instrlen(dis_handle_t * dhp)279 dis_max_instrlen(dis_handle_t *dhp)
280 {
281 	return (dhp->dh_arch->da_max_instrlen(dhp));
282 }
283 
284 static int
dis_generic_instrlen(dis_handle_t * dhp,uint64_t pc)285 dis_generic_instrlen(dis_handle_t *dhp, uint64_t pc)
286 {
287 	if (dis_disassemble(dhp, pc, NULL, 0) != 0)
288 		return (-1);
289 
290 	return (dhp->dh_addr - pc);
291 }
292 
293 int
dis_instrlen(dis_handle_t * dhp,uint64_t pc)294 dis_instrlen(dis_handle_t *dhp, uint64_t pc)
295 {
296 	if (dhp->dh_arch->da_instrlen == NULL)
297 		return (dis_generic_instrlen(dhp, pc));
298 
299 	return (dhp->dh_arch->da_instrlen(dhp, pc));
300 }
301 
302 int
dis_vsnprintf(char * restrict s,size_t n,const char * restrict format,va_list args)303 dis_vsnprintf(char *restrict s, size_t n, const char *restrict format,
304     va_list args)
305 {
306 #ifdef DIS_STANDALONE
307 	return (mdb_iob_vsnprintf(s, n, format, args));
308 #else
309 	return (vsnprintf(s, n, format, args));
310 #endif
311 }
312 
313 int
dis_snprintf(char * restrict s,size_t n,const char * restrict format,...)314 dis_snprintf(char *restrict s, size_t n, const char *restrict format, ...)
315 {
316 	va_list args;
317 
318 	va_start(args, format);
319 	n = dis_vsnprintf(s, n, format, args);
320 	va_end(args);
321 
322 	return (n);
323 }
324