#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 "stuff/bytesex.h"
#include "otool.h"
#include "ofile_print.h"
#include "../as/sparc-opcode.h"
#define DEBUG
#define SEX(value, bits) \
((((int)(value)) << ((8 * sizeof (int)) - bits)) \
>> ((8 * sizeof (int)) - bits) )
static char *reg_names[] =
{ "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
"o0", "o1", "o2", "o3", "o4", "o5", "sp", "o7",
"l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7",
"i0", "i1", "i2", "i3", "i4", "i5", "fp", "i7",
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7",
"f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15",
"f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23",
"f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31",
"y", "psr", "wim", "tbr", "pc", "npc", "fpsr", "cpsr"
};
#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
#define SYM_PRINT(a,b,c) print_symbolic((a),(b),(c),relocs, nrelocs, symbols, \
nsymbols, sorted_symbols, \
nsorted_symbols, strings, \
strings_size, verbose)
static int opcodes_sorted = 0;
extern void qsort();
static int compare_opcodes ();
#ifdef NOT_USED
static int
is_delayed_branch (insn)
union sparc_insn insn;
{
unsigned int i;
for (i = 0; i < NUMOPCODES; ++i) {
struct sparc_opcode *opcode = &sparc_opcodes[i];
if ((opcode->match & insn.code) == opcode->match
&& (opcode->lose & insn.code) == 0)
return (opcode->flags & F_DELAYED);
}
return 0;
}
#define MAX_LO_LOOKAHEAD 5
int
find_lo_value(
char *section,
unsigned long addr,
union sparc_insn insn)
{
int index, next_insn;
index = 0;
while (++index < MAX_LO_LOOKAHEAD) {
memcpy(&next_insn, section + 4*index, sizeof (unsigned int));
}
}
#endif
void
print_address_func(unsigned int addr)
{
printf("0x%x", addr);
}
enum bool
label_symbol(
unsigned long addr,
enum bool colon_and_newline,
struct nlist *sorted_symbols,
unsigned long nsorted_symbols)
{
long high, low, mid;
low = 0;
high = nsorted_symbols - 1;
mid = (high - low) / 2;
while(high >= low){
if(sorted_symbols[mid].n_value == addr){
printf("%s", sorted_symbols[mid].n_un.n_name);
if(colon_and_newline == TRUE)
printf(":\n");
return TRUE;
}
if(sorted_symbols[mid].n_value > addr){
high = mid - 1;
mid = (high + low) / 2;
}
else{
low = mid + 1;
mid = (high + low) / 2;
}
}
return FALSE;
}
static void
print_symbolic(
char operand,
unsigned long value,
unsigned int pc,
struct relocation_info *relocs,
unsigned long nrelocs,
struct nlist *symbols,
unsigned long nsymbols,
struct nlist *sorted_symbols,
unsigned long nsorted_symbols,
char *strings,
unsigned long strings_size,
enum bool verbose)
{
struct relocation_info *rp, *rp_pair;
unsigned long i, r_address, r_symbolnum, r_type, r_extern,
r_value, r_scattered, pair_r_type, pair_r_value;
long reloc_found, offset;
unsigned long other_half;
const char *name, *add, *sub;
struct scattered_relocation_info *srp;
r_symbolnum = 0;
r_type = 0;
r_extern = 0;
r_value = 0;
r_scattered = 0;
other_half = 0;
reloc_found = 0;
pair_r_value = 0;
if (!verbose) {
printf("0x%x", (unsigned int) value);
return;
}
if (nrelocs) {
for (i=0, rp = relocs; i<nrelocs; i++, rp++) {
if (rp->r_address & R_SCATTERED) {
srp = (struct scattered_relocation_info *) rp;
r_scattered = 1;
r_address = srp->r_address;
r_extern = 0;
r_type = srp->r_type;
r_value = srp->r_value;
} else {
r_scattered = 0;
r_address = rp->r_address;
r_symbolnum = rp->r_symbolnum;
r_extern = rp->r_extern;
r_type = rp->r_type;
}
if (r_type == SPARC_RELOC_PAIR) {
fprintf(stderr, "Stray SPARC_RELOC_PAIR entry ");
continue;
}
if(r_address == pc){
if(r_type == SPARC_RELOC_HI22 ||
r_type == SPARC_RELOC_LO10 ||
r_type == SPARC_RELOC_HI22_SECTDIFF ||
r_type == SPARC_RELOC_LO10_SECTDIFF ||
r_type == SPARC_RELOC_SECTDIFF){
if(i+1 < nrelocs){
rp_pair = rp+1;
if(rp_pair->r_address & R_SCATTERED){
srp = (struct scattered_relocation_info *) rp_pair;
other_half = srp->r_address;
pair_r_type = srp->r_type;
pair_r_value = srp->r_value;
} else {
other_half = rp_pair->r_address;
pair_r_type = rp_pair->r_type;
}
if(pair_r_type != SPARC_RELOC_PAIR){
fprintf(stderr, "No SPARC_RELOC_PAIR relocation "
"entry after entry %lu\n", i);
continue;
}
}
}
reloc_found = 1;
break;
}
if (r_type == SPARC_RELOC_HI22 ||
r_type == SPARC_RELOC_LO10 ||
r_type == SPARC_RELOC_SECTDIFF ||
r_type == SPARC_RELOC_HI22_SECTDIFF ||
r_type == SPARC_RELOC_LO10_SECTDIFF)
{
if (i+1 < nrelocs) {
rp_pair = (rp + 1);
if (rp_pair->r_address & R_SCATTERED) {
srp = (struct scattered_relocation_info *) rp_pair;
pair_r_type = srp->r_type;
} else {
pair_r_type = rp_pair->r_type;
}
if (pair_r_type == SPARC_RELOC_PAIR) {
i++;
rp++;
} else {
fprintf(stderr,
"no SPARC_RELOC_PAIR relocation entry after %lu\n", i);
}
}
}
}
}
if (reloc_found && (r_extern == 1)) {
if (symbols[r_symbolnum].n_un.n_strx < 0 ||
(unsigned long)symbols[r_symbolnum].n_un.n_strx >= strings_size)
name = "bad string offset";
else
name = strings + symbols[r_symbolnum].n_un.n_strx;
switch (r_type) {
case SPARC_RELOC_HI22:
value += other_half;
printf("%%hi(%s", name);
if (value)
printf("+0x%x)", (unsigned int) value);
else
printf(")");
break;
case SPARC_RELOC_LO10:
value |= other_half << 10;
printf("%%lo(%s", name);
if (value)
printf("+0x%x)", (unsigned int) value);
else
printf(")");
break;
case SPARC_RELOC_WDISP30:
printf("%s", name);
if (value)
printf("+0x%x", (unsigned int) value);
break;
case SPARC_RELOC_WDISP22:
printf("%s", name);
if (value)
printf("+0x%x", (unsigned int) value);
break;
default:
printf("%s", name);
}
return;
}
offset = 0;
if(reloc_found){
if(r_type == SPARC_RELOC_HI22 ||
r_type == SPARC_RELOC_HI22_SECTDIFF)
value |= other_half;
else if(r_type == SPARC_RELOC_LO10 ||
r_type == SPARC_RELOC_LO10_SECTDIFF)
value |= other_half << 10;
if(r_scattered &&
(r_type != SPARC_RELOC_HI22_SECTDIFF &&
r_type != SPARC_RELOC_LO10_SECTDIFF)){
offset = value - r_value;
value = r_value;
}
}
if (reloc_found &&
(r_type == SPARC_RELOC_HI22_SECTDIFF ||
r_type == SPARC_RELOC_LO10_SECTDIFF)) {
if (r_type == SPARC_RELOC_HI22_SECTDIFF)
printf("%%hi(");
else
printf("%%lo(");
add = guess_symbol (r_value, sorted_symbols,
nsorted_symbols, verbose);
sub = guess_symbol (pair_r_value, sorted_symbols,
nsorted_symbols, verbose);
if (add)
printf("%s", add);
else
printf("0x%x", (unsigned int) r_value);
if (sub)
printf("-%s", sub);
else
printf("-0x%x", (unsigned int) pair_r_value);
if (offset)
printf("+0x%x)", (unsigned int) offset);
else
printf(")");
return;
}
if (operand == 'l' || operand == 'L') {
if ((name = guess_symbol(value, sorted_symbols,
nsorted_symbols, verbose)) != NULL)
printf("%s", name);
else
printf("0x%x", (unsigned int) value);
} else {
name = guess_symbol(value, sorted_symbols, nsorted_symbols, TRUE);
switch (r_type) {
case SPARC_RELOC_HI22:
if (name)
printf("%%hi(%s)", name);
else
printf("%%hi(0x%x)", (unsigned int) value);
break;
case SPARC_RELOC_LO10:
if (name)
printf("%%lo(%s)", name);
else
printf("%%lo(0x%x)", (unsigned int) value);
break;
default:
if (name)
printf("%s", name);
else
printf("0x%x", (unsigned int) value);
}
}
}
unsigned long
sparc_disassemble(
char *sect,
unsigned long left,
unsigned long addr,
unsigned long sect_addr,
enum byte_sex object_byte_sex,
struct relocation_info *relocs,
unsigned long nrelocs,
struct nlist *symbols,
unsigned long nsymbols,
struct nlist *sorted_symbols,
unsigned long nsorted_symbols,
char *strings,
unsigned long strings_size,
unsigned long *indirect_symbols,
unsigned long nindirect_symbols,
struct mach_header *mh,
struct load_command *load_commands,
enum bool verbose)
{
enum byte_sex host_byte_sex;
enum bool swapped;
int i;
unsigned long sect_offset;
union sparc_insn insn;
struct sparc_opcode *opcode;
int imm_added_to_rs1 = 0;
int found_plus = 0;
int is_annulled = 0;
const char *s;
static union sparc_insn sethi_insn;
const char *indirect_symbol_name;
opcode = NULL;
if (!opcodes_sorted) {
qsort ((char *) sparc_opcodes, NUMOPCODES,
sizeof (sparc_opcodes[0]), compare_opcodes);
opcodes_sorted = 1;
}
sect_offset = addr - sect_addr;
host_byte_sex = get_host_byte_sex();
swapped = host_byte_sex != object_byte_sex;
if (left < sizeof(unsigned long)) {
if(left != 0) {
memcpy(&insn, sect, left);
if(swapped)
insn.code = SWAP_LONG(insn.code);
printf(".long\t0x%08x\n", (unsigned int)insn.code);
}
printf("(end of section)\n");
return(left);
}
memcpy(&insn, sect, sizeof(unsigned long));
if (swapped)
insn.code = SWAP_LONG(insn.code);
for (i=0; i<NUMOPCODES; ++i) {
opcode = &sparc_opcodes[i];
if ((opcode->match & insn.code) == opcode->match
&& (opcode->lose & insn.code) == 0) {
if ((insn.rs1 != insn.rd
&& strchr (opcode->args, 'r') != 0) ||
(insn.rs2 != insn.rd &&
strchr (opcode->args, 'u'))) {
continue;
}
else {
break;
}
}
}
if (i >= NUMOPCODES) {
printf(".long 0x%08x\n", (unsigned int)insn.code);
return(4);
}
printf("%s", opcode->name);
if ((opcode->match == 0x80102000 ||
opcode->match == 0x80002000 ||
opcode->match == 0xd0202000 )
)
imm_added_to_rs1 = 1;
if (opcode->args[0] != ',')
printf("\t");
for (s = opcode->args; *s != '\0'; ++s) {
while (*s == ',') {
printf(",");
++s;
switch (*s) {
case 'a':
printf("a\t");
is_annulled = 1;
++s;
continue;
default:
break;
}
}
switch (*s) {
case '+':
found_plus = 1;
default:
printf("%c", *s);
break;
case '#':
printf("0");
break;
#define reg(n) printf("%%%s", reg_names[n])
case '1':
case 'r':
reg (insn.rs1);
break;
case '2':
reg (insn.rs2);
break;
case 'u':
reg (insn.rs2);
break;
case 'd':
reg (insn.rd);
break;
#undef reg
#define freg(n) printf("%%%s", freg_names[n])
#define fregx(n) printf("%%%s", freg_names[((n) & ~1) | (((n) & 1) << 5)])
case 'e':
freg (insn.rs1);
break;
case 'v':
case 'V':
fregx (insn.rs1);
break;
case 'f':
freg (insn.rs2);
break;
case 'B':
case 'R':
fregx (insn.rs2);
break;
case 'g':
freg (insn.rd);
break;
case 'H':
case 'J':
fregx (insn.rd);
break;
#undef freg
#undef fregx
#define creg(n) printf("%%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':
sethi_insn.code = insn.code;
SYM_PRINT(*s ,((int) insn.imm22<<10), sect_offset);
break;
case 'i':
{
int imm = SEX (insn.imm13, 13);
if (found_plus)
imm_added_to_rs1 = 1;
printf("0x%x", imm);
}
break;
case 'M':
printf("%%asr%d", insn.rs1);
break;
case 'm':
printf("%%asr%d", insn.rd);
break;
case 'L':
SYM_PRINT(*s,addr + ((insn.disp30 & 0x3fffffff) << 2), sect_offset);
if(verbose){
indirect_symbol_name = guess_indirect_symbol(
addr + ((insn.disp30 & 0x3fffffff) << 2),
mh, load_commands, object_byte_sex, indirect_symbols,
nindirect_symbols, symbols, nsymbols, strings,
strings_size);
if(indirect_symbol_name != NULL)
printf("\t; symbol stub for: %s", indirect_symbol_name);
}
break;
case 'n':
printf("%#x", (SEX (insn.disp22, 22)));
break;
case 'l':
SYM_PRINT(*s,addr + (SEX(insn.disp22, 22) << 2), sect_offset);
break;
case 'A':
printf("(%d)", (int) insn.asi);
break;
case 'C':
printf("%%csr");
break;
case 'F':
printf("%%fsr");
break;
case 'p':
printf("%%psr");
break;
case 'q':
printf("%%fq");
break;
case 'Q':
printf("%%cq");
break;
case 't':
printf("%%tbr");
break;
case 'w':
printf("%%wim");
break;
case 'y':
printf("%%y");
break;
}
}
if (imm_added_to_rs1) {
union sparc_insn prev_insn;
if (sethi_insn.code)
if (insn.rs1 == sethi_insn.rs1) {
sethi_insn.code = 0;
}
if ((sect_addr - addr) >= 8) memcpy(&prev_insn, sect - 4, sizeof(unsigned long));
else
prev_insn.code = 0;
if (prev_insn.code )
memcpy(&prev_insn, sect - 8, sizeof(unsigned long));
else
prev_insn.code = 0;
if ((prev_insn.code & 0xc1c00000) == 0x01000000
&& prev_insn.rd == insn.rs1) {
printf("\t! ");
print_address_func(
(0xFFFFFFFF & (int) prev_insn.imm22 << 10)
| SEX (insn.imm13, 13));
}
}
printf("\n");
return (4);
}
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 (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;
}
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;
}
{
int alias_diff = (op0->flags & F_ALIAS) - (op1->flags & F_ALIAS);
if (alias_diff != 0)
return alias_diff;
}
i = strcmp (op0->name, op1->name);
if (i){
if (op0->flags & F_ALIAS)
return i;
else
fprintf (stderr,
"Internal error: bad sparc-opcode.h: \"%s\" == \"%s\"\n",
op0->name, op1->name);
}
{
int length_diff = strlen (op0->args) - strlen (op1->args);
if (length_diff != 0)
return length_diff;
}
{
char *p0 = (char *) strchr(op0->args, '+');
char *p1 = (char *) strchr(op1->args, '+');
if (p0 && p1)
{
if (p0[-1] == 'i' && p1[1] == 'i')
return 1;
if (p0[1] == 'i' && p1[-1] == 'i')
return -1;
}
}
return 0;
}