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