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