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
42static 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)
51extern dis_arch_t dis_arch_i386;
52#endif
53#if !defined(DIS_STANDALONE) || defined(__sparc)
54extern dis_arch_t dis_arch_sparc;
55#endif
56#if !defined(DIS_STANDALONE) || defined(__s390) || defined(__s390x)
57extern dis_arch_t dis_arch_s390;
58#endif
59#if !defined(DIS_STANDALONE) || defined(__riscv)
60extern dis_arch_t dis_arch_riscv;
61#endif
62
63static 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
84void *
85dis_zalloc(size_t bytes)
86{
87	return (mdb_zalloc(bytes, UM_SLEEP));
88}
89
90void
91dis_free(void *ptr, size_t bytes)
92{
93	mdb_free(ptr, bytes);
94}
95#else
96void *
97dis_zalloc(size_t bytes)
98{
99	return (calloc(1, bytes));
100}
101
102/*ARGSUSED*/
103void
104dis_free(void *ptr, size_t bytes)
105{
106	free(ptr);
107}
108#endif
109
110int
111dis_seterrno(int error)
112{
113	_dis_errno = error;
114	return (-1);
115}
116
117int
118dis_errno(void)
119{
120	return (_dis_errno);
121}
122
123const char *
124dis_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
138void
139dis_set_data(dis_handle_t *dhp, void *data)
140{
141	dhp->dh_data = data;
142}
143
144void
145dis_flags_set(dis_handle_t *dhp, int f)
146{
147	dhp->dh_flags |= f;
148}
149
150void
151dis_flags_clear(dis_handle_t *dhp, int f)
152{
153	dhp->dh_flags &= ~f;
154}
155
156void
157dis_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
165dis_handle_t *
166dis_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
209int
210dis_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 */
221static uint64_t
222dis_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
261done:
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 */
270uint64_t
271dis_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
279int
280dis_min_instrlen(dis_handle_t *dhp)
281{
282	return (dhp->dh_arch->da_min_instrlen(dhp));
283}
284
285int
286dis_max_instrlen(dis_handle_t *dhp)
287{
288	return (dhp->dh_arch->da_max_instrlen(dhp));
289}
290
291static int
292dis_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
300int
301dis_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
309int
310dis_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
320int
321dis_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