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 */