/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Joshua M. Clulow * Copyright 2015 Nexenta Systems, Inc. All rights reserved. * Copyright 2018, Joyent, Inc. */ #include #include #ifdef DIS_STANDALONE #include #define _MDB #include #else #include #endif #include "libdisasm_impl.h" static int _dis_errno; /* * If we're building the standalone library, then we only want to * include support for disassembly of the native architecture. * The regular shared library should include support for all * architectures. */ #if !defined(DIS_STANDALONE) || defined(__i386) || defined(__amd64) extern dis_arch_t dis_arch_i386; #endif #if !defined(DIS_STANDALONE) || defined(__sparc) extern dis_arch_t dis_arch_sparc; #endif #if !defined(DIS_STANDALONE) || defined(__s390) || defined(__s390x) extern dis_arch_t dis_arch_s390; #endif #if !defined(DIS_STANDALONE) || defined(__riscv) extern dis_arch_t dis_arch_riscv; #endif static dis_arch_t *dis_archs[] = { #if !defined(DIS_STANDALONE) || defined(__i386) || defined(__amd64) &dis_arch_i386, #endif #if !defined(DIS_STANDALONE) || defined(__sparc) &dis_arch_sparc, #endif #if !defined(DIS_STANDALONE) || defined(__s390) || defined(__s390x) &dis_arch_s390, #endif #if !defined(DIS_STANDALONE) || defined(__riscv) &dis_arch_riscv, #endif NULL }; /* * For the standalone library, we need to link against mdb's malloc/free. * Otherwise, use the standard malloc/free. */ #ifdef DIS_STANDALONE void * dis_zalloc(size_t bytes) { return (mdb_zalloc(bytes, UM_SLEEP)); } void dis_free(void *ptr, size_t bytes) { mdb_free(ptr, bytes); } #else void * dis_zalloc(size_t bytes) { return (calloc(1, bytes)); } /*ARGSUSED*/ void dis_free(void *ptr, size_t bytes) { free(ptr); } #endif int dis_seterrno(int error) { _dis_errno = error; return (-1); } int dis_errno(void) { return (_dis_errno); } const char * dis_strerror(int error) { switch (error) { case E_DIS_NOMEM: return ("out of memory"); case E_DIS_INVALFLAG: return ("invalid flags for this architecture"); case E_DIS_UNSUPARCH: return ("unsupported machine architecture"); default: return ("unknown error"); } } void dis_set_data(dis_handle_t *dhp, void *data) { dhp->dh_data = data; } void dis_flags_set(dis_handle_t *dhp, int f) { dhp->dh_flags |= f; } void dis_flags_clear(dis_handle_t *dhp, int f) { dhp->dh_flags &= ~f; } void dis_handle_destroy(dis_handle_t *dhp) { if (dhp->dh_arch->da_handle_detach != NULL) dhp->dh_arch->da_handle_detach(dhp); dis_free(dhp, sizeof (dis_handle_t)); } dis_handle_t * dis_handle_create(int flags, void *data, dis_lookup_f lookup_func, dis_read_f read_func) { dis_handle_t *dhp; dis_arch_t *arch = NULL; int i; /* Select an architecture based on flags */ for (i = 0; dis_archs[i] != NULL; i++) { if (dis_archs[i]->da_supports_flags(flags)) { arch = dis_archs[i]; break; } } if (arch == NULL) { (void) dis_seterrno(E_DIS_UNSUPARCH); return (NULL); } if ((dhp = dis_zalloc(sizeof (dis_handle_t))) == NULL) { (void) dis_seterrno(E_DIS_NOMEM); return (NULL); } dhp->dh_arch = arch; dhp->dh_lookup = lookup_func; dhp->dh_read = read_func; dhp->dh_flags = flags; dhp->dh_data = data; /* * Allow the architecture-specific code to allocate * its private data. */ if (arch->da_handle_attach != NULL && arch->da_handle_attach(dhp) != 0) { dis_free(dhp, sizeof (dis_handle_t)); /* dis errno already set */ return (NULL); } return (dhp); } int dis_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf, size_t buflen) { return (dhp->dh_arch->da_disassemble(dhp, addr, buf, buflen)); } /* * On some instruction sets (e.g., x86), we have no choice except to * disassemble everything from the start of the symbol, and stop when we * have reached our instruction address. If we're not in the middle of a * known symbol, then we return the same address to indicate failure. */ static uint64_t dis_generic_previnstr(dis_handle_t *dhp, uint64_t pc, int n) { uint64_t *hist, addr, start; int cur, nseen; uint64_t res = pc; if (n <= 0) return (pc); if (dhp->dh_lookup(dhp->dh_data, pc, NULL, 0, &start, NULL) != 0 || start == pc) return (res); hist = dis_zalloc(sizeof (uint64_t) * n); for (cur = 0, nseen = 0, addr = start; addr < pc; addr = dhp->dh_addr) { hist[cur] = addr; cur = (cur + 1) % n; nseen++; /* if we cannot make forward progress, give up */ if (dis_disassemble(dhp, addr, NULL, 0) != 0) goto done; } if (addr != pc) { /* * We scanned past %pc, but didn't find an instruction that * started at %pc. This means that either the caller specified * an invalid address, or we ran into something other than code * during our scan. Virtually any combination of bytes can be * construed as a valid Intel instruction, so any non-code bytes * we encounter will have thrown off the scan. */ goto done; } res = hist[(cur + n - MIN(n, nseen)) % n]; done: dis_free(hist, sizeof (uint64_t) * n); return (res); } /* * Return the nth previous instruction's address. Return the same address * to indicate failure. */ uint64_t dis_previnstr(dis_handle_t *dhp, uint64_t pc, int n) { if (dhp->dh_arch->da_previnstr == NULL) return (dis_generic_previnstr(dhp, pc, n)); return (dhp->dh_arch->da_previnstr(dhp, pc, n)); } int dis_min_instrlen(dis_handle_t *dhp) { return (dhp->dh_arch->da_min_instrlen(dhp)); } int dis_max_instrlen(dis_handle_t *dhp) { return (dhp->dh_arch->da_max_instrlen(dhp)); } static int dis_generic_instrlen(dis_handle_t *dhp, uint64_t pc) { if (dis_disassemble(dhp, pc, NULL, 0) != 0) return (-1); return (dhp->dh_addr - pc); } int dis_instrlen(dis_handle_t *dhp, uint64_t pc) { if (dhp->dh_arch->da_instrlen == NULL) return (dis_generic_instrlen(dhp, pc)); return (dhp->dh_arch->da_instrlen(dhp, pc)); } int dis_vsnprintf(char *restrict s, size_t n, const char *restrict format, va_list args) { #ifdef DIS_STANDALONE return (mdb_iob_vsnprintf(s, n, format, args)); #else return (vsnprintf(s, n, format, args)); #endif } int dis_snprintf(char *restrict s, size_t n, const char *restrict format, ...) { va_list args; va_start(args, format); n = dis_vsnprintf(s, n, format, args); va_end(args); return (n); }