11545Seschrock /*
21545Seschrock * CDDL HEADER START
31545Seschrock *
41545Seschrock * The contents of this file are subject to the terms of the
51545Seschrock * Common Development and Distribution License (the "License").
61545Seschrock * You may not use this file except in compliance with the License.
71545Seschrock *
81545Seschrock * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91545Seschrock * or http://www.opensolaris.org/os/licensing.
101545Seschrock * See the License for the specific language governing permissions
111545Seschrock * and limitations under the License.
121545Seschrock *
131545Seschrock * When distributing Covered Code, include this CDDL HEADER in each
141545Seschrock * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151545Seschrock * If applicable, add the following below this CDDL HEADER, with the
161545Seschrock * fields enclosed by brackets "[]" replaced with your own identifying
171545Seschrock * information: Portions Copyright [yyyy] [name of copyright owner]
181545Seschrock *
191545Seschrock * CDDL HEADER END
201545Seschrock */
211545Seschrock
221545Seschrock /*
23*3892Sdmick * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
241545Seschrock * Use is subject to license terms.
251545Seschrock */
261545Seschrock
271545Seschrock #pragma ident "%Z%%M% %I% %E% SMI"
281545Seschrock
291545Seschrock #include <libdisasm.h>
301545Seschrock #include <stdlib.h>
311545Seschrock #include <stdio.h>
321545Seschrock
331545Seschrock #include "dis_tables.h"
341545Seschrock #include "libdisasm_impl.h"
351545Seschrock
361545Seschrock struct dis_handle {
371545Seschrock void *dh_data;
381545Seschrock int dh_flags;
391545Seschrock dis_lookup_f dh_lookup;
401545Seschrock dis_read_f dh_read;
411545Seschrock int dh_mode;
421545Seschrock dis86_t dh_dis;
431545Seschrock uint64_t dh_addr;
441545Seschrock uint64_t dh_end;
451545Seschrock };
461545Seschrock
471545Seschrock /*
481545Seschrock * Returns true if we are near the end of a function. This is a cheap hack at
491545Seschrock * detecting NULL padding between functions. If we're within a few bytes of the
501545Seschrock * next function, or past the start, then return true.
511545Seschrock */
521545Seschrock static int
check_func(void * data)531545Seschrock check_func(void *data)
541545Seschrock {
551545Seschrock dis_handle_t *dhp = data;
561545Seschrock uint64_t start;
571545Seschrock size_t len;
581545Seschrock
591545Seschrock if (dhp->dh_lookup(dhp->dh_data, dhp->dh_addr, NULL, 0, &start, &len)
601545Seschrock != 0)
611545Seschrock return (0);
621545Seschrock
631545Seschrock if (start < dhp->dh_addr)
641545Seschrock return (dhp->dh_addr > start + len - 0x10);
651545Seschrock
661545Seschrock return (1);
671545Seschrock }
681545Seschrock
691545Seschrock static int
get_byte(void * data)701545Seschrock get_byte(void *data)
711545Seschrock {
721545Seschrock uchar_t byte;
731545Seschrock dis_handle_t *dhp = data;
741545Seschrock
751768Seschrock if (dhp->dh_read(dhp->dh_data, dhp->dh_addr, &byte, sizeof (byte)) !=
761768Seschrock sizeof (byte))
771545Seschrock return (-1);
781545Seschrock
791545Seschrock dhp->dh_addr++;
801545Seschrock
811545Seschrock return ((int)byte);
821545Seschrock }
831545Seschrock
841545Seschrock static int
do_lookup(void * data,uint64_t addr,char * buf,size_t buflen)851545Seschrock do_lookup(void *data, uint64_t addr, char *buf, size_t buflen)
861545Seschrock {
871545Seschrock dis_handle_t *dhp = data;
881545Seschrock
891545Seschrock return (dhp->dh_lookup(dhp->dh_data, addr, buf, buflen, NULL, NULL));
901545Seschrock }
911545Seschrock
921545Seschrock dis_handle_t *
dis_handle_create(int flags,void * data,dis_lookup_f lookup_func,dis_read_f read_func)931545Seschrock dis_handle_create(int flags, void *data, dis_lookup_f lookup_func,
941545Seschrock dis_read_f read_func)
951545Seschrock {
961545Seschrock dis_handle_t *dhp;
971545Seschrock
981545Seschrock /*
991545Seschrock * Validate architecture flags
1001545Seschrock */
1011545Seschrock if (flags & ~(DIS_X86_SIZE16 | DIS_X86_SIZE32 | DIS_X86_SIZE64 |
102*3892Sdmick DIS_OCTAL | DIS_NOIMMSYM)) {
1031545Seschrock (void) dis_seterrno(E_DIS_INVALFLAG);
1041545Seschrock return (NULL);
1051545Seschrock }
1061545Seschrock
1071545Seschrock /*
1081545Seschrock * Create and initialize the internal structure
1091545Seschrock */
1101545Seschrock if ((dhp = dis_zalloc(sizeof (struct dis_handle))) == NULL) {
1111545Seschrock (void) dis_seterrno(E_DIS_NOMEM);
1121545Seschrock return (NULL);
1131545Seschrock }
1141545Seschrock
1151545Seschrock dhp->dh_lookup = lookup_func;
1161545Seschrock dhp->dh_read = read_func;
1171545Seschrock dhp->dh_flags = flags;
1181545Seschrock dhp->dh_data = data;
1191545Seschrock
1201545Seschrock /*
1211545Seschrock * Initialize x86-specific architecture structure
1221545Seschrock */
1231545Seschrock if (flags & DIS_X86_SIZE16)
1241545Seschrock dhp->dh_mode = SIZE16;
1251545Seschrock else if (flags & DIS_X86_SIZE64)
1261545Seschrock dhp->dh_mode = SIZE64;
1271545Seschrock else
1281545Seschrock dhp->dh_mode = SIZE32;
1291545Seschrock
1301545Seschrock if (flags & DIS_OCTAL)
1312089Sdmick dhp->dh_dis.d86_flags = DIS_F_OCTAL;
1321545Seschrock
1331545Seschrock dhp->dh_dis.d86_sprintf_func = snprintf;
1341545Seschrock dhp->dh_dis.d86_get_byte = get_byte;
1351545Seschrock dhp->dh_dis.d86_sym_lookup = do_lookup;
1361545Seschrock dhp->dh_dis.d86_check_func = check_func;
1371545Seschrock
1381545Seschrock dhp->dh_dis.d86_data = dhp;
1391545Seschrock
1401545Seschrock return (dhp);
1411545Seschrock }
1421545Seschrock
1431545Seschrock int
dis_disassemble(dis_handle_t * dhp,uint64_t addr,char * buf,size_t buflen)1441545Seschrock dis_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf, size_t buflen)
1451545Seschrock {
1461545Seschrock dhp->dh_addr = addr;
1471545Seschrock
148*3892Sdmick /* DIS_NOIMMSYM might not be set until now, so update */
149*3892Sdmick if (dhp->dh_flags & DIS_NOIMMSYM)
150*3892Sdmick dhp->dh_dis.d86_flags |= DIS_F_NOIMMSYM;
151*3892Sdmick else
152*3892Sdmick dhp->dh_dis.d86_flags &= ~DIS_F_NOIMMSYM;
153*3892Sdmick
1541545Seschrock if (dtrace_disx86(&dhp->dh_dis, dhp->dh_mode) != 0)
1551545Seschrock return (-1);
1561545Seschrock
1571545Seschrock if (buf != NULL)
1581545Seschrock dtrace_disx86_str(&dhp->dh_dis, dhp->dh_mode, addr, buf,
1591545Seschrock buflen);
1601545Seschrock
1611545Seschrock return (0);
1621545Seschrock }
1631545Seschrock
1641545Seschrock void
dis_handle_destroy(dis_handle_t * dhp)1651545Seschrock dis_handle_destroy(dis_handle_t *dhp)
1661545Seschrock {
1671545Seschrock dis_free(dhp, sizeof (dis_handle_t));
1681545Seschrock }
1691545Seschrock
1701545Seschrock void
dis_set_data(dis_handle_t * dhp,void * data)1711545Seschrock dis_set_data(dis_handle_t *dhp, void *data)
1721545Seschrock {
1731545Seschrock dhp->dh_data = data;
1741545Seschrock }
1751545Seschrock
176*3892Sdmick void
dis_flags_set(dis_handle_t * dhp,int f)177*3892Sdmick dis_flags_set(dis_handle_t *dhp, int f)
178*3892Sdmick {
179*3892Sdmick dhp->dh_flags |= f;
180*3892Sdmick }
181*3892Sdmick
182*3892Sdmick void
dis_flags_clear(dis_handle_t * dhp,int f)183*3892Sdmick dis_flags_clear(dis_handle_t *dhp, int f)
184*3892Sdmick {
185*3892Sdmick dhp->dh_flags &= ~f;
186*3892Sdmick }
187*3892Sdmick
188*3892Sdmick
1891545Seschrock /* ARGSUSED */
1901545Seschrock int
dis_max_instrlen(dis_handle_t * dhp)1911545Seschrock dis_max_instrlen(dis_handle_t *dhp)
1921545Seschrock {
1931545Seschrock return (15);
1941545Seschrock }
1951545Seschrock
1961545Seschrock #define MIN(a, b) ((a) < (b) ? (a) : (b))
1971545Seschrock
1981545Seschrock /*
1991545Seschrock * Return the previous instruction. On x86, we have no choice except to
2001545Seschrock * disassemble everything from the start of the symbol, and stop when we have
2011545Seschrock * reached our instruction address. If we're not in the middle of a known
2021545Seschrock * symbol, then we return the same address to indicate failure.
2031545Seschrock */
2041545Seschrock uint64_t
dis_previnstr(dis_handle_t * dhp,uint64_t pc,int n)2051545Seschrock dis_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
2061545Seschrock {
2071545Seschrock uint64_t *hist, addr, start;
2081545Seschrock int cur, nseen;
2091545Seschrock uint64_t res = pc;
2101545Seschrock
2111586Seschrock if (n <= 0)
2121586Seschrock return (pc);
2131586Seschrock
2141545Seschrock if (dhp->dh_lookup(dhp->dh_data, pc, NULL, 0, &start, NULL) != 0 ||
2151545Seschrock start == pc)
2161545Seschrock return (res);
2171545Seschrock
2181545Seschrock hist = dis_zalloc(sizeof (uint64_t) * n);
2191545Seschrock
2201545Seschrock for (cur = 0, nseen = 0, addr = start; addr < pc; addr = dhp->dh_addr) {
2211545Seschrock hist[cur] = addr;
2221545Seschrock cur = (cur + 1) % n;
2231545Seschrock nseen++;
2241545Seschrock
2251545Seschrock /* if we cannot make forward progress, give up */
2261545Seschrock if (dis_disassemble(dhp, addr, NULL, 0) != 0)
2271545Seschrock goto done;
2281545Seschrock }
2291545Seschrock
2301545Seschrock if (addr != pc) {
2311545Seschrock /*
2321545Seschrock * We scanned past %pc, but didn't find an instruction that
2331545Seschrock * started at %pc. This means that either the caller specified
2341545Seschrock * an invalid address, or we ran into something other than code
2351545Seschrock * during our scan. Virtually any combination of bytes can be
2361545Seschrock * construed as a valid Intel instruction, so any non-code bytes
2371545Seschrock * we encounter will have thrown off the scan.
2381545Seschrock */
2391545Seschrock goto done;
2401545Seschrock }
2411545Seschrock
2421545Seschrock res = hist[(cur + n - MIN(n, nseen)) % n];
2431545Seschrock
2441545Seschrock done:
2451545Seschrock dis_free(hist, sizeof (uint64_t) * n);
2461545Seschrock return (res);
2471545Seschrock }
248