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 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 /*
  28  * Copyright 2007 Jason King.  All rights reserved.
  29  * Use is subject to license terms.
  30  */
  31 
  32 
  33 #pragma ident   "%Z%%M% %I%     %E% SMI"
  34 
  35 /*
  36  * The sparc disassembler is mostly straightforward, each instruction is
  37  * represented by an inst_t structure.  The inst_t definitions are organized
  38  * into tables.  The tables are correspond to the opcode maps documented in the
  39  * various sparc architecture manuals.  Each table defines the bit range of the
  40  * instruction whose value act as an index into the array of instructions.  A
  41  * table can also refer to another table if needed.  Each table also contains
  42  * a function pointer of type format_fcn that knows how to output the
  43  * instructions in the table, as well as handle any synthetic instructions
  44  *
  45  * Unfortunately, the changes from sparcv8 -> sparcv9 not only include new
  46  * instructions, they sometimes renamed or just reused the same instruction to
  47  * do different operations (i.e. the sparcv8 coprocessor instructions).  To
  48  * accommodate this, each table can define an overlay table.  The overlay table
  49  * is a list of (table index, architecture, new instruction definition) values.
  50  *
  51  *
  52  * Traversal starts with the first table,
  53  *   get index value from the instruction
  54  *   if an relevant overlay entry exists for this index,
  55  *        grab the overlay definition
  56  *   else
  57  *        grab the definition from the array (corresponding to the index value)
  58  *
  59  * If the entry is an instruction,
  60  *     call print function of instruction.
  61  * If the entry is a pointer to another table
  62  *     traverse the table
  63  * If not valid,
  64  *     return an error
  65  *
  66  *
  67  * To keep dis happy, for sparc, instead of actually returning an error, if
  68  * the instruction cannot be disassembled, we instead merely place the value
  69  * of the instruction into the output buffer.
  70  *
  71  * Adding new instructions:
  72  *
  73  * With the above information, it hopefully makes it clear how to add support
  74  * for decoding new instructions.  Presumably, with new instructions will come
  75  * a new dissassembly mode (I.e. DIS_SPARC_V8, DIS_SPARC_V9, etc.).
  76  *
  77  * If the dissassembled format does not correspond to one of the existing
  78  * formats, a new formatter will have to be written.  The 'flags' value of
  79  * inst_t is intended to instruct the corresponding formatter about how to
  80  * output the instruction.
  81  *
  82  * If the corresponding entry in the correct table is currently unoccupied,
  83  * simply replace the INVALID entry with the correct definition.  The INST and
  84  * TABLE macros are suggested to be used for this.  If there is already an
  85  * instruction defined, then the entry must be placed in an overlay table.  If
  86  * no overlay table exists for the instruction table, one will need to be
  87  * created.
  88  */
  89 
  90 #include <libdisasm.h>
  91 #include <stdlib.h>
  92 #include <stdio.h>
  93 #include <sys/types.h>
  94 #include <sys/byteorder.h>
  95 #include <string.h>
  96 
  97 #include "libdisasm_impl.h"
  98 #include "dis_sparc.h"
  99 
 100 static const inst_t *dis_get_overlay(dis_handle_t *, const table_t *,
 101     uint32_t);
 102 static uint32_t dis_get_bits(uint32_t, int, int);
 103 
 104 #if !defined(DIS_STANDALONE)
 105 static void do_binary(uint32_t);
 106 #endif /* DIS_STANDALONE */
 107 
 108 dis_handle_t *
 109 dis_handle_create(int flags, void *data, dis_lookup_f lookup_func,
 110     dis_read_f read_func)
 111 {
 112 
 113 #if !defined(DIS_STANDALONE)
 114         char *opt = NULL;
 115         char *opt2, *save, *end;
 116 #endif
 117         dis_handle_t *dhp;
 118 
 119         if ((flags & (DIS_SPARC_V8|DIS_SPARC_V9|DIS_SPARC_V9_SGI)) == 0) {
 120                 (void) dis_seterrno(E_DIS_INVALFLAG);
 121                 return (NULL);
 122         }
 123 
 124         if ((dhp = dis_zalloc(sizeof (struct dis_handle))) == NULL) {
 125                 (void) dis_seterrno(E_DIS_NOMEM);
 126                 return (NULL);
 127         }
 128 
 129         dhp->dh_lookup = lookup_func;
 130         dhp->dh_read = read_func;
 131         dhp->dh_flags = flags;
 132         dhp->dh_data = data;
 133         dhp->dh_debug = DIS_DEBUG_COMPAT;
 134 
 135 #if !defined(DIS_STANDALONE)
 136 
 137         opt = getenv("_LIBDISASM_DEBUG");
 138         if (opt == NULL)
 139                 return (dhp);
 140 
 141         opt2 = strdup(opt);
 142         if (opt2 == NULL) {
 143                 dis_handle_destroy(dhp);
 144                 (void) dis_seterrno(E_DIS_NOMEM);
 145                 return (NULL);
 146         }
 147         save = opt2;
 148 
 149         while (opt2 != NULL) {
 150                 end = strchr(opt2, ',');
 151 
 152                 if (end != 0)
 153                         *end++ = '\0';
 154 
 155                 if (strcasecmp("synth-all", opt2) == 0)
 156                         dhp->dh_debug |= DIS_DEBUG_SYN_ALL;
 157 
 158                 if (strcasecmp("compat", opt2) == 0)
 159                         dhp->dh_debug |= DIS_DEBUG_COMPAT;
 160 
 161                 if (strcasecmp("synth-none", opt2) == 0)
 162                         dhp->dh_debug &= ~(DIS_DEBUG_SYN_ALL|DIS_DEBUG_COMPAT);
 163 
 164                 if (strcasecmp("binary", opt2) == 0)
 165                         dhp->dh_debug |= DIS_DEBUG_PRTBIN;
 166 
 167                 if (strcasecmp("format", opt2) == 0)
 168                         dhp->dh_debug |= DIS_DEBUG_PRTFMT;
 169 
 170                 if (strcasecmp("all", opt2) == 0)
 171                         dhp->dh_debug = DIS_DEBUG_ALL;
 172 
 173                 if (strcasecmp("none", opt2) == 0)
 174                         dhp->dh_debug = DIS_DEBUG_NONE;
 175 
 176                 opt2 = end;
 177         }
 178         free(save);
 179 #endif /* DIS_STANDALONE */
 180         return (dhp);
 181 }
 182 
 183 void
 184 dis_handle_destroy(dis_handle_t *dhp)
 185 {
 186         dis_free(dhp, sizeof (dis_handle_t));
 187 }
 188 
 189 void
 190 dis_set_data(dis_handle_t *dhp, void *data)
 191 {
 192         dhp->dh_data = data;
 193 }
 194 
 195 void
 196 dis_flags_set(dis_handle_t *dhp, int f)
 197 {
 198         dhp->dh_flags |= f;
 199 }
 200 
 201 void
 202 dis_flags_clear(dis_handle_t *dhp, int f)
 203 {
 204         dhp->dh_flags &= ~f;
 205 }
 206 
 207 /* ARGSUSED */
 208 int
 209 dis_max_instrlen(dis_handle_t *dhp)
 210 {
 211         return (4);
 212 }
 213 
 214 /*
 215  * The dis_i386.c comment for this says it returns the previous instruction,
 216  * however, I'm fairly sure it's actually returning the _address_ of the
 217  * nth previous instruction.
 218  */
 219 /* ARGSUSED */
 220 uint64_t
 221 dis_previnstr(dis_handle_t *dhp, uint64_t pc, int n)
 222 {
 223         if (n <= 0)
 224                 return (pc);
 225 
 226         if (pc < n)
 227                 return (pc);
 228 
 229         return (pc - n*4);
 230 }
 231 
 232 int
 233 dis_disassemble(dis_handle_t *dhp, uint64_t addr, char *buf, size_t buflen)
 234 {
 235         const table_t *tp = &initial_table;
 236         const inst_t *inp = NULL;
 237 
 238         uint32_t instr;
 239         uint32_t idx = 0;
 240 
 241         if (dhp->dh_read(dhp->dh_data, addr, &instr, sizeof (instr)) !=
 242             sizeof (instr))
 243                 return (-1);
 244 
 245         dhp->dh_buf    = buf;
 246         dhp->dh_buflen = buflen;
 247         dhp->dh_addr   = addr;
 248 
 249         buf[0] = '\0';
 250 
 251         /* this allows sparc code to be tested on x86 */
 252         instr = BE_32(instr);
 253 
 254 #if !defined(DIS_STANDALONE)
 255         if ((dhp->dh_debug & DIS_DEBUG_PRTBIN) != 0)
 256                 do_binary(instr);
 257 #endif /* DIS_STANDALONE */
 258 
 259         /* CONSTCOND */
 260         while (1) {
 261                 idx = dis_get_bits(instr, tp->tbl_field, tp->tbl_len);
 262                 inp = &tp->tbl_inp[idx];
 263 
 264                 inp = dis_get_overlay(dhp, tp, idx);
 265 
 266                 if ((inp->in_type == INST_NONE) ||
 267                     ((inp->in_arch & dhp->dh_flags) == 0))
 268                         goto error;
 269 
 270                 if (inp->in_type == INST_TBL) {
 271                         tp = inp->in_data.in_tbl;
 272                         continue;
 273                 }
 274 
 275                 break;
 276         }
 277 
 278         if (tp->tbl_fmt(dhp, instr, inp, idx) == 0)
 279                 return (0);
 280 
 281 error:
 282 
 283         (void) snprintf(buf, buflen,
 284             ((dhp->dh_flags & DIS_OCTAL) != 0) ? "0%011lo" : "0x%08lx",
 285             instr);
 286 
 287         return (0);
 288 }
 289 
 290 static uint32_t
 291 dis_get_bits(uint32_t instr, int offset, int length)
 292 {
 293         uint32_t mask, val;
 294         int i;
 295 
 296         for (i = 0, mask = 0; i < length; ++i)
 297                 mask |= (1UL << i);
 298 
 299         mask = mask << (offset - length + 1);
 300 
 301         val = instr & mask;
 302 
 303         val = val >> (offset - length + 1);
 304 
 305         return (val);
 306 }
 307 
 308 static const inst_t *
 309 dis_get_overlay(dis_handle_t *dhp, const table_t *tp, uint32_t idx)
 310 {
 311         const inst_t *ip = &tp->tbl_inp[idx];
 312         int i;
 313 
 314         if (tp->tbl_ovp == NULL)
 315                 return (ip);
 316 
 317         for (i = 0; tp->tbl_ovp[i].ov_idx != -1; ++i) {
 318                 if (tp->tbl_ovp[i].ov_idx != idx)
 319                         continue;
 320 
 321                 if ((tp->tbl_ovp[i].ov_inst.in_arch & dhp->dh_flags) == 0)
 322                         continue;
 323 
 324                 ip = &tp->tbl_ovp[i].ov_inst;
 325                 break;
 326         }
 327 
 328         return (ip);
 329 }
 330 
 331 #if !defined(DIS_STANDALONE)
 332 static void
 333 do_binary(uint32_t instr)
 334 {
 335         (void) fprintf(stderr, "DISASM: ");
 336         prt_binary(instr, 32);
 337         (void) fprintf(stderr, "\n");
 338 }
 339 #endif /* DIS_STANDALONE */