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