6070 libdisasm: attach/detach arch ops should be optional
[illumos-gate.git] / usr / src / lib / libdisasm / common / libdisasm.c
bloba7340ae009161c5adaf4fc2d24b19c100b3c3a49
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
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.
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
39 #include "libdisasm_impl.h"
41 static int _dis_errno;
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.
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
56 static dis_arch_t *dis_archs[] = {
57 #if !defined(DIS_STANDALONE) || defined(__i386) || defined(__amd64)
58 &dis_arch_i386,
59 #endif
60 #if !defined(DIS_STANDALONE) || defined(__sparc)
61 &dis_arch_sparc,
62 #endif
63 NULL
67 * For the standalone library, we need to link against mdb's malloc/free.
68 * Otherwise, use the standard malloc/free.
70 #ifdef DIS_STANDALONE
71 void *
72 dis_zalloc(size_t bytes)
74 return (mdb_zalloc(bytes, UM_SLEEP));
77 void
78 dis_free(void *ptr, size_t bytes)
80 mdb_free(ptr, bytes);
82 #else
83 void *
84 dis_zalloc(size_t bytes)
86 return (calloc(1, bytes));
89 /*ARGSUSED*/
90 void
91 dis_free(void *ptr, size_t bytes)
93 free(ptr);
95 #endif
97 int
98 dis_seterrno(int error)
100 _dis_errno = error;
101 return (-1);
105 dis_errno(void)
107 return (_dis_errno);
110 const char *
111 dis_strerror(int error)
113 switch (error) {
114 case E_DIS_NOMEM:
115 return ("out of memory");
116 case E_DIS_INVALFLAG:
117 return ("invalid flags for this architecture");
118 case E_DIS_UNSUPARCH:
119 return ("unsupported machine architecture");
120 default:
121 return ("unknown error");
125 void
126 dis_set_data(dis_handle_t *dhp, void *data)
128 dhp->dh_data = data;
131 void
132 dis_flags_set(dis_handle_t *dhp, int f)
134 dhp->dh_flags |= f;
137 void
138 dis_flags_clear(dis_handle_t *dhp, int f)
140 dhp->dh_flags &= ~f;
143 void
144 dis_handle_destroy(dis_handle_t *dhp)
146 if (dhp->dh_arch->da_handle_detach != NULL)
147 dhp->dh_arch->da_handle_detach(dhp);
149 dis_free(dhp, sizeof (dis_handle_t));
152 dis_handle_t *
153 dis_handle_create(int flags, void *data, dis_lookup_f lookup_func,
154 dis_read_f read_func)
156 dis_handle_t *dhp;
157 dis_arch_t *arch = NULL;
158 int i;
160 /* Select an architecture based on flags */
161 for (i = 0; dis_archs[i] != NULL; i++) {
162 if (dis_archs[i]->da_supports_flags(flags)) {
163 arch = dis_archs[i];
164 break;
167 if (arch == NULL) {
168 (void) dis_seterrno(E_DIS_UNSUPARCH);
169 return (NULL);
172 if ((dhp = dis_zalloc(sizeof (dis_handle_t))) == NULL) {
173 (void) dis_seterrno(E_DIS_NOMEM);
174 return (NULL);
176 dhp->dh_arch = arch;
177 dhp->dh_lookup = lookup_func;
178 dhp->dh_read = read_func;
179 dhp->dh_flags = flags;
180 dhp->dh_data = data;
183 * Allow the architecture-specific code to allocate
184 * its private data.
186 if (arch->da_handle_attach != NULL &&
187 arch->da_handle_attach(dhp) != 0) {
188 dis_free(dhp, sizeof (dis_handle_t));
189 /* dis errno already set */
190 return (NULL);
193 return (dhp);
197 dis_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf, size_t buflen)
199 return (dhp->dh_arch->da_disassemble(dhp, addr, buf, buflen));
203 * On some instruction sets (e.g., x86), we have no choice except to
204 * disassemble everything from the start of the symbol, and stop when we
205 * have reached our instruction address. If we're not in the middle of a
206 * known symbol, then we return the same address to indicate failure.
208 static uint64_t
209 dis_generic_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
211 uint64_t *hist, addr, start;
212 int cur, nseen;
213 uint64_t res = pc;
215 if (n <= 0)
216 return (pc);
218 if (dhp->dh_lookup(dhp->dh_data, pc, NULL, 0, &start, NULL) != 0 ||
219 start == pc)
220 return (res);
222 hist = dis_zalloc(sizeof (uint64_t) * n);
224 for (cur = 0, nseen = 0, addr = start; addr < pc; addr = dhp->dh_addr) {
225 hist[cur] = addr;
226 cur = (cur + 1) % n;
227 nseen++;
229 /* if we cannot make forward progress, give up */
230 if (dis_disassemble(dhp, addr, NULL, 0) != 0)
231 goto done;
234 if (addr != pc) {
236 * We scanned past %pc, but didn't find an instruction that
237 * started at %pc. This means that either the caller specified
238 * an invalid address, or we ran into something other than code
239 * during our scan. Virtually any combination of bytes can be
240 * construed as a valid Intel instruction, so any non-code bytes
241 * we encounter will have thrown off the scan.
243 goto done;
246 res = hist[(cur + n - MIN(n, nseen)) % n];
248 done:
249 dis_free(hist, sizeof (uint64_t) * n);
250 return (res);
254 * Return the nth previous instruction's address. Return the same address
255 * to indicate failure.
257 uint64_t
258 dis_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
260 if (dhp->dh_arch->da_previnstr == NULL)
261 return (dis_generic_previnstr(dhp, pc, n));
263 return (dhp->dh_arch->da_previnstr(dhp, pc, n));
267 dis_min_instrlen(dis_handle_t *dhp)
269 return (dhp->dh_arch->da_min_instrlen(dhp));
273 dis_max_instrlen(dis_handle_t *dhp)
275 return (dhp->dh_arch->da_max_instrlen(dhp));
278 static int
279 dis_generic_instrlen(dis_handle_t *dhp, uint64_t pc)
281 if (dis_disassemble(dhp, pc, NULL, 0) != 0)
282 return (-1);
284 return (dhp->dh_addr - pc);
288 dis_instrlen(dis_handle_t *dhp, uint64_t pc)
290 if (dhp->dh_arch->da_instrlen == NULL)
291 return (dis_generic_instrlen(dhp, pc));
293 return (dhp->dh_arch->da_instrlen(dhp, pc));
297 dis_vsnprintf(char *restrict s, size_t n, const char *restrict format,
298 va_list args)
300 #ifdef DIS_STANDALONE
301 return (mdb_iob_vsnprintf(s, n, format, args));
302 #else
303 return (vsnprintf(s, n, format, args));
304 #endif
308 dis_snprintf(char *restrict s, size_t n, const char *restrict format, ...)
310 va_list args;
312 va_start(args, format);
313 n = dis_vsnprintf(s, n, format, args);
314 va_end(args);
316 return (n);