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