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 *
dis_zalloc(size_t bytes)85 dis_zalloc(size_t bytes)
86 {
87 return (mdb_zalloc(bytes, UM_SLEEP));
88 }
89
90 void
dis_free(void * ptr,size_t bytes)91 dis_free(void *ptr, size_t bytes)
92 {
93 mdb_free(ptr, bytes);
94 }
95 #else
96 void *
dis_zalloc(size_t bytes)97 dis_zalloc(size_t bytes)
98 {
99 return (calloc(1, bytes));
100 }
101
102 /*ARGSUSED*/
103 void
dis_free(void * ptr,size_t bytes)104 dis_free(void *ptr, size_t bytes)
105 {
106 free(ptr);
107 }
108 #endif
109
110 int
dis_seterrno(int error)111 dis_seterrno(int error)
112 {
113 _dis_errno = error;
114 return (-1);
115 }
116
117 int
dis_errno(void)118 dis_errno(void)
119 {
120 return (_dis_errno);
121 }
122
123 const char *
dis_strerror(int error)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
dis_set_data(dis_handle_t * dhp,void * data)139 dis_set_data(dis_handle_t *dhp, void *data)
140 {
141 dhp->dh_data = data;
142 }
143
144 void
dis_flags_set(dis_handle_t * dhp,int f)145 dis_flags_set(dis_handle_t *dhp, int f)
146 {
147 dhp->dh_flags |= f;
148 }
149
150 void
dis_flags_clear(dis_handle_t * dhp,int f)151 dis_flags_clear(dis_handle_t *dhp, int f)
152 {
153 dhp->dh_flags &= ~f;
154 }
155
156 void
dis_handle_destroy(dis_handle_t * dhp)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 *
dis_handle_create(int flags,void * data,dis_lookup_f lookup_func,dis_read_f read_func)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
dis_disassemble(dis_handle_t * dhp,uint64_t addr,char * buf,size_t buflen)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
dis_generic_previnstr(dis_handle_t * dhp,uint64_t pc,int n)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
dis_previnstr(dis_handle_t * dhp,uint64_t pc,int n)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
dis_min_instrlen(dis_handle_t * dhp)280 dis_min_instrlen(dis_handle_t *dhp)
281 {
282 return (dhp->dh_arch->da_min_instrlen(dhp));
283 }
284
285 int
dis_max_instrlen(dis_handle_t * dhp)286 dis_max_instrlen(dis_handle_t *dhp)
287 {
288 return (dhp->dh_arch->da_max_instrlen(dhp));
289 }
290
291 static int
dis_generic_instrlen(dis_handle_t * dhp,uint64_t pc)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
dis_instrlen(dis_handle_t * dhp,uint64_t pc)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
dis_vsnprintf(char * restrict s,size_t n,const char * restrict format,va_list args)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
dis_snprintf(char * restrict s,size_t n,const char * restrict format,...)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