This is sparc-disasm.c in view mode; [Download] [Up]
#include <stdio.h> #include <string.h> #include <mach-o/loader.h> #include <mach-o/nlist.h> #include <mach-o/reloc.h> #include <mach-o/sparc/reloc.h> #include "bytesex.h" #include "otool.h" #include "defs.h" #include "symtab.h" #include "sparc-opcode.h" #include "dis-asm.h" #define DEBUG /* Sign-extend a value which is N bits long. */ #define SEX(value, bits) \ ((((int)(value)) << ((8 * sizeof (int)) - bits)) \ >> ((8 * sizeof (int)) - bits) ) #define freg_names (®_names[4 * 8]) union sparc_insn { unsigned long int code; struct { #ifdef __BIG_ENDIAN__ unsigned int anop:2; unsigned int anrd:5; unsigned int op3:6; unsigned int anrs1:5; unsigned int i:1; unsigned int anasi:8; unsigned int anrs2:5; #endif #ifdef __LITTLE_ENDIAN__ unsigned int anrs2:5; unsigned int anasi:8; unsigned int i:1; unsigned int anrs1:5; unsigned int op3:6; unsigned int anrd:5; unsigned int anop:2; #endif } ldst; struct { #ifdef __BIG_ENDIAN__ unsigned int anop:2; unsigned int anrd:5; unsigned int op3:6; unsigned int anrs1:5; unsigned int i:1; unsigned int IMM13:13; #endif #ifdef __LITTLE_ENDIAN__ unsigned int IMM13:13; unsigned int i:1; unsigned int anrs1:5; unsigned int op3:6; unsigned int anrd:5; unsigned int anop:2; #endif } IMM13; struct { #ifdef __BIG_ENDIAN__ unsigned int anop:2; unsigned int a:1; unsigned int cond:4; unsigned int op2:3; unsigned int DISP22:22; #endif #ifdef __LITTLE_ENDIAN__ unsigned int DISP22:22; unsigned int op2:3; unsigned int cond:4; unsigned int a:1; unsigned int anop:2; #endif } branch; struct { #ifdef __BIG_ENDIAN__ unsigned int anop:2; unsigned int adisp30:30; #endif #ifdef __LITTLE_ENDIAN__ unsigned int adisp30:30; unsigned int anop:2; #endif } call; }; #define op ldst.anop #define rd ldst.anrd #define rs1 ldst.anrs1 #define asi ldst.anasi #define rs2 ldst.anrs2 #define shcnt rs2 #define imm13 IMM13.IMM13 #define disp22 branch.DISP22 #define imm22 disp22 #define disp30 call.adisp30 static int opcodes_sorted = 0; //extern void qsort(); static int compare_opcodes (); void sparc_disassemble(buffer, addr, stream) char *buffer; unsigned long addr; FILE *stream; { int i; union sparc_insn insn; struct sparc_opcode *opcode; const char *s; if (!opcodes_sorted) { qsort ((char *) sparc_opcodes, NUMOPCODES, sizeof (sparc_opcodes[0]), compare_opcodes); opcodes_sorted = 1; } memcpy(&insn, buffer, sizeof(unsigned long)); SWAP_TARGET_AND_HOST(&insn.code,sizeof(insn.code)); /* search through the opcode table */ for (i=0; i<NUMOPCODES; ++i) { opcode = &sparc_opcodes[i]; if ((opcode->match & insn.code) == opcode->match && (opcode->lose & insn.code) == 0) { /* Can't do simple format if source and dest are different. */ if (insn.rs1 != insn.rd && strchr (opcode->args, 'r') != 0) { continue; } else { break; } } } if (i >= NUMOPCODES) { fprintf_filtered(stream,".long 0x%08x\n", (unsigned int)insn.code); return; } fprintf_filtered(stream,"%s", opcode->name); if (opcode->args[0] != ',') fprintf_filtered(stream,"\t "); for (s = opcode->args; *s != '\0'; ++s) { while (*s == ',') { fprintf_filtered(stream,","); ++s; switch (*s) { case 'a': fprintf_filtered(stream,"a\t"); ++s; continue; default: break; } /* switch on arg */ } /* while there are comma started args */ switch (*s) { default: fprintf_filtered(stream,"%c", *s); break; case '#': fprintf_filtered(stream,"0"); break; #define reg(n) fprintf_filtered(stream,"%%%s", reg_names[n]) case '1': case 'r': reg (insn.rs1); break; case '2': reg (insn.rs2); break; case 'd': reg (insn.rd); break; #undef reg #define freg(n) fprintf_filtered(stream,"%%%s", freg_names[n]) #define fregx(n) fprintf_filtered(stream,"%%%s", freg_names[((n) & ~1) | (((n) & 1) << 5)]) case 'e': freg (insn.rs1); break; case 'v': /* double/even */ case 'V': /* quad/multiple of 4 */ fregx (insn.rs1); break; case 'f': freg (insn.rs2); break; case 'B': /* double/even */ case 'R': /* quad/multiple of 4 */ fregx (insn.rs2); break; case 'g': freg (insn.rd); break; case 'H': /* double/even */ case 'J': /* quad/multiple of 4 */ fregx (insn.rd); break; #undef freg #undef fregx #define creg(n) fprintf_filtered(stream,"%%c%u", (unsigned int) (n)) case 'b': creg (insn.rs1); break; case 'c': creg (insn.rs2); break; case 'D': creg (insn.rd); break; #undef creg case 'h': print_address((int) insn.imm22<<10,stream); break; case 'i': { /* We cannot trust the compiler to sign-extend when extracting the bitfield, hence the shifts. */ int imm = SEX (insn.imm13, 13); fprintf_filtered(stream,"%#x", imm); } break; case 'M': fprintf_filtered(stream,"%%asr%d", insn.rs1); break; case 'm': fprintf_filtered(stream,"%%asr%d", insn.rd); break; case 'L': print_address(4 * SEX(insn.disp30,30) + addr, stream); break; case 'n': fprintf_filtered(stream,"%#x", (SEX (insn.disp22, 22))); break; case 'l': print_address(4 * SEX(insn.disp22,22) +addr, stream); break; case 'A': fprintf_filtered(stream,"(%d)", (int) insn.asi); break; case 'C': fprintf_filtered(stream,"%%csr"); break; case 'F': fprintf_filtered(stream,"%%fsr"); break; case 'p': fprintf_filtered(stream,"%%psr"); break; case 'q': fprintf_filtered(stream,"%%fq"); break; case 'Q': fprintf_filtered(stream,"%%cq"); break; case 't': fprintf_filtered(stream,"%%tbr"); break; case 'w': fprintf_filtered(stream,"%%wim"); break; case 'y': fprintf_filtered(stream,"%%y"); break; } } } /* Compare opcodes A and B. */ static int compare_opcodes (a, b) char *a, *b; { struct sparc_opcode *op0 = (struct sparc_opcode *) a; struct sparc_opcode *op1 = (struct sparc_opcode *) b; unsigned long int match0 = op0->match, match1 = op1->match; unsigned long int lose0 = op0->lose, lose1 = op1->lose; register unsigned int i; /* If a bit is set in both match and lose, there is something wrong with the opcode table. */ if (match0 & lose0) { fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n", op0->name, match0, lose0); op0->lose &= ~op0->match; lose0 = op0->lose; } if (match1 & lose1) { fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\", %#.8lx, %#.8lx\n", op1->name, match1, lose1); op1->lose &= ~op1->match; lose1 = op1->lose; } /* Because the bits that are variable in one opcode are constant in another, it is important to order the opcodes in the right order. */ for (i = 0; i < 32; ++i) { unsigned long int x = 1 << i; int x0 = (match0 & x) != 0; int x1 = (match1 & x) != 0; if (x0 != x1) return x1 - x0; } for (i = 0; i < 32; ++i) { unsigned long int x = 1 << i; int x0 = (lose0 & x) != 0; int x1 = (lose1 & x) != 0; if (x0 != x1) return x1 - x0; } /* They are functionally equal. So as long as the opcode table is valid, we can put whichever one first we want, on aesthetic grounds. */ /* Our first aesthetic ground is that aliases defer to real insns. */ { int alias_diff = (op0->flags & F_ALIAS) - (op1->flags & F_ALIAS); if (alias_diff != 0) /* Put the one that isn't an alias first. */ return alias_diff; } /* Except for aliases, two "identical" instructions had better have the same opcode. This is a sanity check on the table. */ i = strcmp (op0->name, op1->name); if (i) if (op0->flags & F_ALIAS) /* If they're both aliases, be arbitrary. */ return i; else fprintf (stderr, "Internal error: bad sparc-opcode.h: \"%s\" == \"%s\"\n", op0->name, op1->name); /* Fewer arguments are preferred. */ { int length_diff = strlen (op0->args) - strlen (op1->args); if (length_diff != 0) /* Put the one with fewer arguments first. */ return length_diff; } /* Put 1+i before i+1. */ { char *p0 = (char *) strchr(op0->args, '+'); char *p1 = (char *) strchr(op1->args, '+'); if (p0 && p1) { /* There is a plus in both operands. Note that a plus sign cannot be the first character in args, so the following [-1]'s are valid. */ if (p0[-1] == 'i' && p1[1] == 'i') /* op0 is i+1 and op1 is 1+i, so op1 goes first. */ return 1; if (p0[1] == 'i' && p1[-1] == 'i') /* op0 is 1+i and op1 is i+1, so op0 goes first. */ return -1; } } /* They are, as far as we can tell, identical. Since qsort may have rearranged the table partially, there is no way to tell which one was first in the opcode table as written, so just say there are equal. */ return 0; } #define INST_LEN (sizeof(long)) print_insn_sparc (memaddr, info) CORE_ADDR memaddr; disassemble_info *info; { unsigned char buffer[INST_LEN]; FILE *stream = info->stream; read_memory (memaddr, buffer, INST_LEN); sparc_disassemble(buffer, (unsigned) memaddr, stream); return INST_LEN; }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.