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