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