Skip to content

Instantly share code, notes, and snippets.

@murachue
Created April 5, 2020 02:27
Show Gist options
  • Save murachue/8fd9e643e7801a1db2f8064d810df553 to your computer and use it in GitHub Desktop.
Save murachue/8fd9e643e7801a1db2f8064d810df553 to your computer and use it in GitHub Desktop.
Processor and loader modules for reading RSP objects in N64 SDK, for IDA Pro (<7)
# coding: utf-8
import idaapi
import struct
class Struct(object):
def __init__(self, endian):
self._fmt = endian + "".join([w for w,_ in self._struct])
self._size = struct.calcsize(self._fmt)
def read(self, li):
b = li.read(self._size)
if b < self._size:
return None
for name, value in zip(map(lambda (w,n): n, self._struct), struct.unpack(self._fmt, b)):
setattr(self, name, value)
return self
class BitField(object):
def __init__(self, fields):
self._fields = fields
for n in fields:
setattr(self, n, False)
def get(self):
return sum([getattr(self, n) << i for i,n in enumerate(self._fields)])
def set(self, value):
for n in self._fields:
setattr(self, n, bool(value & 1))
value >>= 1
class ELFIdent(Struct):
_struct = [
("4s", "magic"),
("B", "eclass"),
("B", "bytesex"),
("B", "version"),
("B", "osabi"),
("B", "abiversion"),
("7s", "pad"),
]
class ELFHeader(Struct):
_struct = [
("H", "type"),
("H", "machine"),
("I", "version"),
("I", "entry"),
("I", "phoff"),
("I", "shoff"),
("I", "flags"),
("H", "ehsize"),
("H", "phentsize"),
("H", "phnum"),
("H", "shentsize"),
("H", "shnum"),
("H", "shstrndx"),
]
class ELFShdr(Struct):
_struct = [
("I", "name"),
("I", "type"),
("I", "flags"),
("I", "addr"),
("I", "offset"),
("I", "size"),
("I", "link"),
("I", "info"),
("I", "addralign"),
("I", "entsize"),
]
class Type:
NULL = 0
PROGBITS = 1
SYMTAB = 2
STRTAB = 3
RELA = 4
HASH = 5
DYNAMIC = 6
NOTE = 7
NOBITS = 8
REL = 9
SHLIB = 10
DYNSYM = 11
# abi 3
INIT_ARRAY = 14
FINI_ARRAY = 15
PREINIT_ARRAY = 16
GROUP = 17
SYMTAB_SHNDX = 18
class ELFSym(Struct):
_struct = [
("I", "name"),
("I", "value"),
("I", "size"),
("B", "info"),
("B", "other"),
("H", "shndx"),
]
class ELFRel(Struct):
_struct = [
("I", "offset"),
("I", "info"),
]
class ELFRela(Struct):
_struct = [
("I", "offset"),
("I", "info"),
("i", "addend"),
]
class ELFPhdr(Struct):
_struct = [
("I", "type"),
("I", "offset"),
("I", "vaddr"),
("I", "paddr"),
("I", "filesz"),
("I", "memsz"),
("I", "flags"),
("I", "align"),
]
def parse_elf_header(li):
# if file size is not enough, this is not an ELF.
fsize = li.size()
if fsize < 0x34:
return None
# if magic is not match, not an ELF.
li.seek(0)
eident = ELFIdent("<").read(li)
if not eident:
return None
if eident.magic != "\x7fELF":
return None
bitw = {1: 32, 2: 64}.get(eident.eclass)
if bitw is None:
return None
endian = {1: "<", 2: ">"}.get(eident.bytesex)
if endian is None:
return None
ehdr = ELFHeader(endian).read(li)
if not ehdr:
return None
if ehdr.ehsize < 0x34: # too short??
return None
if fsize < ehdr.phoff + ehdr.phentsize * ehdr.phnum: # no enough area to contain ProgHdr??
return None
if fsize < ehdr.shoff + ehdr.shentsize * ehdr.shnum: # no enough area to contain SecHdr??
return None
if ehdr.shnum <= ehdr.shstrndx: # shstrndx is over than shnum??
return None
return eident, ehdr, bitw, endian
def load_one_segm(li, voff, vsize, foff, fsize, sname, sclass):
if not idaapi.add_segm(0, voff, voff + vsize, sname, sclass):
idaapi.warning("elf_gen: could not create segment %s" % sname)
return False
if 0 < fsize:
if not li.file2base(foff, voff, voff + min((vsize, fsize)), idaapi.FILEREG_PATCHABLE):
idaapi.warning("elf_gen: could not read segment %s" % sname)
return False
return True
def create_bf_enum(name, entries):
enum_id = idaapi.add_enum(idaapi.BADADDR, name, idaapi.hexflag())
idaapi.set_enum_bf(enum_id, True)
for i, n in enumerate(entries):
idaapi.add_enum_member(enum_id, n, 1 << i, 1 << i)
return enum_id
def accept_file(li, n):
"""
Check if the file is of supported format
@param li: a file-like object which can be used to access the input data
@param n: format number. The function will be called with incrementing number until it returns zero
@return: 0 - no more supported formats
string "name" - format name to display in the chooser dialog
dictionary { 'format': "name", 'options': integer }
options: should be 1, possibly ORed with ACCEPT_FIRST (0x8000)
to indicate preferred format
"""
# only one format is supported.
if 1 <= n:
return 0
r = parse_elf_header(li)
if r is None:
return 0
eident, ehdr, bitw, endian = r
return "ELF RSP (%dbit %s type=%X machine=%X ver=%X)" % (bitw, {1: "LE", 2: "BE"}.get(eident.bytesex), ehdr.type, ehdr.machine, ehdr.version)
def load_file(li, neflags, format):
"""
Load the file into database
@param li: a file-like object which can be used to access the input data
@param neflags: options selected by the user, see loader.hpp
@return: 0-failure, 1-ok
"""
r = parse_elf_header(li)
if r is None:
return 0
eident, ehdr, bitw, endian = r
# no section header is currently not supported.
if ehdr.shoff == 0 and ehdr.shnum == 0:
return 0
li.seek(ehdr.shoff)
sections = [ELFShdr(endian).read(li) for _ in xrange(ehdr.shnum)]
shstr = sections[ehdr.shstrndx]
li.seek(shstr.offset)
shstrtab = li.read(shstr.size)
# resolve sections name
for s in sections:
s.name = shstrtab[s.name:shstrtab.index("\0", s.name)]
# determine .text and .data section.
shtext = None
shdata = None
for s in sections:
if s.type != ELFShdr.Type.PROGBITS:
continue
if s.name == ".text":
shtext = s
elif s.name == ".data":
shdata = s
if shtext is None:
idaapi.warning("elf_gen: could not find text section")
return 0
if shdata is None:
idaapi.warning("elf_gen: could not find data section")
return 0
if 0x1000 < shdata.size:
idaapi.warning("elf_gen: warning, data section is larger than 0x1000, truncate over 0x1000.")
options = BitField(["isofficial", "isboot"])
options.isofficial = True
options.isboot = False
if neflags & idaapi.NEF_LOPT:
fst = """ELF-RSP Options
<#Defines OS' struc/enum, make OSTask at 0xFC0 and set entrypoint automatically.#~O~fficial ELF file:{isofficial}>
<#Load .text at 0x1000 instead of 0x1080. Meaningful only when Official ELF is selected.#This is ~R~spBoot:{isboot}>{chk1}>
"""
cg1 = idaapi.Form.ChkGroupControl(options._fields, options.get())
form = idaapi.Form(fst, {"chk1": cg1})
form.Compile()
if form.Execute() == 0:
# Cancel loading.
return 0
options.set(cg1.value)
if neflags & idaapi.NEF_MAN:
pass
code_start = 0x1080 if options.isofficial and not options.isboot else 0x1000
if not load_one_segm(li, 0x0000, 0x1000, shdata.offset, shdata.size, shdata.name, "DATA"):
return 0
if not load_one_segm(li, code_start, shtext.size, shtext.offset, shtext.size, shtext.name, "CODE"):
return 0
idaapi.set_processor_type("mipsrsp", idaapi.SETPROC_ALL | idaapi.SETPROC_FATAL)
# TODO label symbols
# FIXME this should be go to TIL.
create_bf_enum("SP_STATUS_R", ["halt", "broke", "dmabusy", "dmafull", "iofull", "sstep", "intr_on_break"] + ["sig%d" % i for i in xrange(8)])
sp_status_w = ["%s%s" % (cs, n) for n in ["halt", "broke", "intr", "sstep", "intr_on_break"] + ["sig%d" % i for i in xrange(8)] for cs in ("C", "S")]
del sp_status_w[3] # remove sbroke
create_bf_enum("SP_STATUS_W", sp_status_w)
create_bf_enum("DPC_STATUS_R", ["xbus_dmem_dma", "freeze", "flush", "start_gclk", "tmem_busy", "pipe_busy", "cmd_busy", "cbuf_ready", "dma_busy", "end_valid", "start_valid"])
create_bf_enum("DPC_STATUS_W", ["%s%s" % (cs, n) for n in ["xbus_dmem_dma", "freeze", "flush"] for cs in ("C", "S")] + ["Ctmem_ctr", "Cpipe_ctr", "Ccmd_ctr", "Cclock_Ctr"])
if options.isofficial:
# FIXME this should be go to TIL.
ostaskflags = create_bf_enum("OSTaskFlags", ["OS_TASK_YIELDED", "OS_TASK_DP_WAIT", "OS_TASK_LOADABLE", "OS_TASK_SP_ONLY", "OS_TASK_USR0", "OS_TASK_USR1", "OS_TASK_USR2", "OS_TASK_USR3"])
ostask_tid = idaapi.add_struc(idaapi.BADADDR, "OSTask")
ostask = idaapi.get_struc(ostask_tid)
idaapi.add_struc_member(ostask, "type", idaapi.BADADDR, idaapi.dwrdflag(), None, 4)
oi = idaapi.opinfo_t()
oi.ec.tid = ostaskflags
oi.ec.serial = 0
idaapi.add_struc_member(ostask, "flags", idaapi.BADADDR, idaapi.enumflag() | idaapi.dwrdflag(), oi, 4)
for n in ("ucode_boot", "ucode", "ucode_data", "dram_stack", "output_buff"):
idaapi.add_struc_member(ostask, n, idaapi.BADADDR, idaapi.dwrdflag(), None, 4)
idaapi.add_struc_member(ostask, n + "_size", idaapi.BADADDR, idaapi.dwrdflag(), None, 4)
for n in ("data", "yield_data"):
idaapi.add_struc_member(ostask, n + "_ptr", idaapi.BADADDR, idaapi.dwrdflag(), None, 4)
idaapi.add_struc_member(ostask, n + "_size", idaapi.BADADDR, idaapi.dwrdflag(), None, 4)
# apply OSTask to 0xFC0
idaapi.doStruct(0x0FC0, idaapi.get_struc_size(ostask), ostask_tid)
# make entrypoint
idaapi.add_entry(code_start, code_start, "start", True)
idaapi.jumpto(code_start)
return 1
# ----------------------------------------------------------------------
# R4300i subset that is used for Nintendo64 RSP
import sys
import re
import io
import ctypes # only for converting Python's int/long to C's "int"
import idaapi
import idautils
# ----------------------------------------------------------------------
def zx(dw, s, w):
return (dw >> s) & ((1 << w) - 1)
def sx(dw, s, w):
sbit = (1 << (w - 1))
t = (dw >> s) & ((1 << w) - 1)
sgn = t & sbit
t = t & (sbit - 1)
# w = 8; 80 = -128; FF = -1; FE = -2
# 00 = -128; 7F = -1; 7E = -2
return t if sgn == 0 else -(sbit - t)
class MipsOp:
def __init__(self, dw):
# .3.........2.........1.........0
# .......3.......2.......1.......0
# R: oooooossssstttttdddddaaaaaffffff
# I: oooooossssstttttiiiiiiiiiiiiiiii
# J: oooooojjjjjjjjjjjjjjjjjjjjjjjjjj
# V: oooooossssstttttdddddeeeeooooooo
self.op = zx(dw, 26, 6) # RIJV
self.rs = zx(dw, 21, 5) # RI-V (1_el in V)
self.base = self.rs # -i--
self.rt = zx(dw, 16, 5) # RI-V
self.rd = zx(dw, 11, 5) # R--V (rs in V virtually)
self.sa = zx(dw, 6, 5) # R--- (rd in V virtually)
self.funct = zx(dw, 0, 6) # R---
self.imms = sx(dw, 0, 16) # -I--
self.immz = zx(dw, 0, 16) # -I--
self.target = zx(dw, 0, 26) # --J-
self.el = zx(dw, 7, 4) # ---V (load/store)
self.voffset = sx(dw, 0, 7) # ---V (load/store)
self.code = zx(dw, 6, 20) # (break)
class Decoder:
children = {}
proc = None
# decolator to register decoder class to find class from mnemonic.
def thedecoder(c):
Decoder.children[c.__name__] = c
return c
class DecoderSub(Decoder):
@classmethod
def clean_name(self, name):
""" trim non-alpha and make upcase. """
return re.sub("[^A-Z0-9]", "", name.upper())
@classmethod
def get_handler_class(self, e):
if not e:
return None
if len(e) == 1:
return self.children[self.clean_name(e[0])]
else:
return e[1]
@classmethod
def collect_insns(self, coltd=None): # XXX: there are no cross-ref'd form, so passing coltd is meaningless...
insns = []
if coltd is None:
coltd = set()
for e in self.tab:
if e is None: continue
if e[0] in coltd: continue
coltd.add(e[0])
h = self.get_handler_class(e)
if issubclass(h, DecoderLeaf):
if h.feature is None:
idaapi.warning("mips_rsp: collect_insns: BUG: No feature defined at %s" % e[0])
insns.append({'name': e[0], 'feature': h.feature, '_handler': h})
else:
insns.extend(h.collect_insns(coltd))
return insns
tab_index = None # subclassResponsibility
@classmethod
def lut(self, op, func):
e = self.tab[self.tab_index(op)]
if e is None:
return None
else:
return func(e)
@classmethod
def handler(self, op):
return self.lut(op, lambda e: self.get_handler_class(e).handler(op))
@classmethod
def decode(self, cmd, _, op):
return self.lut(op, lambda e: self.get_handler_class(e).decode(cmd, e[0], op))
class DecoderLeaf(Decoder):
@classmethod
def handler(self, _):
return self
@classmethod
def decode(self, cmd, mnem, op):
cmd.itype = self.proc._rev_instruc[mnem]
self._decode(cmd, op)
return self
@classmethod
def _decode(self, cmd, op):
raise NotImplementedError() # subclassResponsibility
#feature = None # subclassResponsibility; if you define feature here, you got "feature is None" in some insns(M{F|T}C{0|2}) even when defined in M{F|T}CX... strange Python.
outflags = (0, 0, 0, 0, 0, 0) # subclassResponsibility
regtabs = (0, 0, 0, 0, 0, 0) # subclassResponsibility; use rGPR/rRXP/rVEC.
memdir = None
MEMDIR_R = 1
MEMDIR_W = 2
MEMDIR2DT = { MEMDIR_R: idaapi.dr_R, MEMDIR_W: idaapi.dr_W }
@classmethod
def memdir2dr(self, md):
return self.MEMDIR2DT.get(md)
# TODO merge these funcs and setop using class inheritance/polymorphic.
@classmethod
def reg(self, r, dtyp=idaapi.dt_dword): return (idaapi.o_reg, r, dtyp)
@classmethod
def imm(self, i, dtyp): return (idaapi.o_imm, i, dtyp)
@classmethod
def near(self, a): return (idaapi.o_near, a, idaapi.dt_dword)
@classmethod
def disp(self, r, a, dtyp=0): return (idaapi.o_displ, (r, a), dtyp)
@classmethod
def vecel(self, r, e, dtyp=0): return (idaapi.o_idpspec0, (r, e), dtyp)
@classmethod
def vecsrc(self, r, e, dtyp=0): return (idaapi.o_idpspec1, (r, e), dtyp)
@classmethod
def ibyte(self, i): return self.imm(i, idaapi.dt_byte)
@classmethod
def ihalf(self, i): return self.imm(i, idaapi.dt_word)
@classmethod
def iword(self, i): return self.imm(i, idaapi.dt_dword)
rGPR = 0
rRXP = 1
rVEC = 2
rVCR = 3
rVACC = 4
@classmethod
def setop(self, cmd, *opnds):
for i, (t, v, d) in enumerate(opnds + ((idaapi.o_void, None, 0),)):
op = cmd.Operands[i]
op.type = t
op.dtyp = d
if t == idaapi.o_reg:
op.reg = v
elif t == idaapi.o_imm:
op.value = v
elif t == idaapi.o_near:
op.addr = v
elif t == idaapi.o_displ:
op.phrase = v[0]
op.addr = v[1]
elif t == idaapi.o_idpspec0:
op.reg = v[0]
op.specval = v[1] # XXX should use specflag1? (smaller size)
elif t == idaapi.o_idpspec1:
op.reg = v[0]
op.specval = v[1] # XXX should use specflag1? (smaller size)
CF_USES = [idaapi.CF_USE1, idaapi.CF_USE2, idaapi.CF_USE3, idaapi.CF_USE4, idaapi.CF_USE5, idaapi.CF_USE6]
CF_CHGS = [idaapi.CF_CHG1, idaapi.CF_CHG2, idaapi.CF_CHG3, idaapi.CF_CHG4, idaapi.CF_CHG5, idaapi.CF_CHG6]
DT2INTS = {
idaapi.dt_byte: 1,
idaapi.dt_word: 2,
idaapi.dt_dword: 4,
idaapi.dt_qword: 8,
idaapi.dt_byte16: 16, # note: no dt_oword!!!?
}
@classmethod
def dt2int(self, dtyp):
return self.DT2INTS.get(dtyp, 0)
doDataFuncs = {
idaapi.dt_byte: idaapi.doByte,
idaapi.dt_word: idaapi.doWord,
idaapi.dt_dword: idaapi.doDwrd,
idaapi.dt_qword: idaapi.doQwrd,
idaapi.dt_byte16: idaapi.doOwrd,
}
@classmethod
def doData(self, ea, dtyp):
f = self.doDataFuncs.get(dtyp)
if f is None:
return False
return f(ea, self.dt2int(dtyp))
@thedecoder
class R(DecoderLeaf):
feature = idaapi.CF_CHG1 | idaapi.CF_USE2 | idaapi.CF_USE3
outflags = (0, 0, 0)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rd), self.reg(op.rs), self.reg(op.rt))
class IP(DecoderLeaf):
feature = idaapi.CF_CHG1 | idaapi.CF_USE2 | idaapi.CF_USE3
@thedecoder
class IS(IP):
outflags = (0, 0, idaapi.OOF_SIGNED)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rt), self.reg(op.rs), self.ihalf(op.imms))
@thedecoder
class IZ(IP):
outflags = (0, 0, 0)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rt), self.reg(op.rs), self.ihalf(op.immz))
class ID(DecoderLeaf):
outflags = (0, 0)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rt, self.dtyp), self.disp(op.base, op.imms, self.dtyp))
class DL(ID):
feature = idaapi.CF_CHG1 | idaapi.CF_USE2
memdir = DecoderLeaf.MEMDIR_R
class DS(ID):
feature = idaapi.CF_USE1 | idaapi.CF_CHG2
memdir = DecoderLeaf.MEMDIR_W
class D1(ID):
dtyp = idaapi.dt_byte
class D2(ID):
dtyp = idaapi.dt_word
class D4(ID):
dtyp = idaapi.dt_dword
@thedecoder
class LB(DL, D1): pass
@thedecoder
class LH(DL, D2): pass
@thedecoder
class LW(DL, D4): pass
@thedecoder
class SB(DS, D1): pass
@thedecoder
class SH(DS, D2): pass
@thedecoder
class SW(DS, D4): pass
class JumpBranch(DecoderLeaf): # note: this class is present only for categorize jump/branch insns.
@classmethod
def iaddr(self, cmd, i):
# note: use cmd.ea&0xFFFFf000 instead of 0x1000 to support overlays on different segments.
return (i & 0x0FFF) | (cmd.ea & 0xFFFFf000)
@classmethod
def reliaddr(self, cmd, op):
return self.iaddr(cmd, cmd.ea + 4 + (op.imms << 2))
@thedecoder
class B2(JumpBranch):
feature = idaapi.CF_USE1 | idaapi.CF_USE2 | idaapi.CF_USE3
outflags = (0, 0, idaapi.OOF_ADDR)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rs), self.reg(op.rt), self.near(self.reliaddr(cmd, op)))
@thedecoder
class B1(JumpBranch):
feature = idaapi.CF_JUMP | idaapi.CF_USE1 | idaapi.CF_USE2 # XXX CF_JUMP is for indirect jump/call??
outflags = (0, idaapi.OOF_ADDR)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rs), self.near(self.reliaddr(cmd, op)))
@thedecoder
class J(JumpBranch):
feature = idaapi.CF_JUMP | idaapi.CF_USE1 # XXX CF_JUMP is for indirect jump/call??
outflags = (idaapi.OOF_ADDR,)
@classmethod
def _decode(self, cmd, op):
#self.setop(cmd, self.near(cmd.ea & 0xC0000000 | (op.target << 2)))
self.setop(cmd, self.near(self.iaddr(cmd, op.target << 2)))
@thedecoder
class JAL(J):
feature = idaapi.CF_CALL | idaapi.CF_USE1
@thedecoder
class BREAK(DecoderLeaf):
feature = idaapi.CF_STOP | idaapi.CF_USE1
outflags = (0,)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.iword(op.code))
@thedecoder
class JALR(JumpBranch):
feature = idaapi.CF_CALL | idaapi.CF_CHG1 | idaapi.CF_USE2
outflags = (0, 0)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rd), self.reg(op.rs))
@thedecoder
class JR(JumpBranch):
feature = idaapi.CF_JUMP | idaapi.CF_USE1
outflags = (0,)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rs))
@thedecoder
class SXX(DecoderLeaf):
feature = idaapi.CF_SHFT | idaapi.CF_CHG1 | idaapi.CF_USE2 | idaapi.CF_USE3
outflags = (0, 0, 0)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rd), self.reg(op.rt), self.ibyte(op.sa))
@thedecoder
class SXV(DecoderLeaf):
"Note: order of rt and rs are different than R form."
feature = idaapi.CF_SHFT | idaapi.CF_CHG1 | idaapi.CF_USE2 | idaapi.CF_USE3
outflags = (0, 0, 0)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rd), self.reg(op.rt), self.reg(op.rs))
class MXCX(DecoderLeaf):
outflags = (0, 0)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rt), self.reg(op.rd))
class MXC0(MXCX):
regtabs = (DecoderLeaf.rGPR, DecoderLeaf.rRXP)
class MXC2(MXCX):
regtabs = (DecoderLeaf.rGPR, DecoderLeaf.rVEC)
@classmethod
def _decode(self, cmd, op):
"note: using sa field is RSP specific!!"
self.setop(cmd, self.reg(op.rt), self.vecel(op.rd, op.sa >> 1))
class MFCX(MXCX):
feature = idaapi.CF_CHG1 | idaapi.CF_USE2
class MTCX(MXCX):
feature = idaapi.CF_USE1 | idaapi.CF_CHG2
@thedecoder
class MFC0(MXC0, MFCX): pass
@thedecoder
class MTC0(MXC0, MTCX): pass
@thedecoder
class MFC2(MXC2, MFCX): pass
@thedecoder
class MTC2(MXC2, MTCX): pass
class CXC2(DecoderLeaf):
outflags = (0, 0)
regtabs = (DecoderLeaf.rGPR, DecoderLeaf.rVCR)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.reg(op.rt), self.reg(op.rd & 3)) # FIXME dtyp is word or byte (depend on rd)
@thedecoder
class CFC2(CXC2):
feature = idaapi.CF_CHG1 | idaapi.CF_USE2
@thedecoder
class CTC2(CXC2):
feature = idaapi.CF_USE1 | idaapi.CF_CHG2
class VP(DecoderLeaf):
outflags = (0, 0)
regtabs = (DecoderLeaf.rVEC, DecoderLeaf.rGPR)
@classmethod
def _decode(self, cmd, op):
self.setop(cmd, self.vecel(op.rt, op.el, 1 << self.s), self.disp(op.base, op.voffset << self.s, 1 << self.s))
class VL(VP):
feature = idaapi.CF_CHG1 | idaapi.CF_USE2
memdir = DecoderLeaf.MEMDIR_R
class VS(VP):
feature = idaapi.CF_USE1 | idaapi.CF_CHG2
memdir = DecoderLeaf.MEMDIR_W
class V0(VP):
s = 0
dtyp = idaapi.dt_byte
class V1(VP):
s = 1
dtyp = idaapi.dt_word
class V2(VP):
s = 2
dtyp = idaapi.dt_dword
class V3(VP):
s = 3
dtyp = idaapi.dt_qword
class V4(VP):
s = 4
dtyp = idaapi.dt_byte16
@thedecoder
class VL0(VL, V0): pass
@thedecoder
class VL1(VL, V1): pass
@thedecoder
class VL2(VL, V2): pass
@thedecoder
class VL3(VL, V3): pass
@thedecoder
class VL4(VL, V4): pass
@thedecoder
class VS0(VS, V0): pass
@thedecoder
class VS1(VS, V1): pass
@thedecoder
class VS2(VS, V2): pass
@thedecoder
class VS3(VS, V3): pass
@thedecoder
class VS4(VS, V4): pass
@thedecoder
class VX(DecoderLeaf):
feature = idaapi.CF_CHG1 | idaapi.CF_USE2 | idaapi.CF_USE3
outflags = (0, 0, 0)
regtabs = (DecoderLeaf.rVEC, DecoderLeaf.rVEC, DecoderLeaf.rVEC)
@classmethod
def _decode(self, cmd, op):
# $sa <- $rd[i[$rs]] ? $rt[e[i[$rs]]] (Vlogicalop)
self.setop(cmd, self.reg(op.sa), self.reg(op.rd), self.vecsrc(op.rt, op.rs))
@thedecoder
class VY(DecoderLeaf):
feature = idaapi.CF_CHG1 | idaapi.CF_USE2
outflags = (0, 0)
regtabs = (DecoderLeaf.rVEC, DecoderLeaf.rVEC)
@classmethod
def _decode(self, cmd, op):
# $sa[i[$rd]] <- ? ( $rt[i[$rs]] ) (VRCP*,VRSQ*,VMOV)
self.setop(cmd, self.vecsrc(op.sa, 0x18 | op.rd), self.vecsrc(op.rt, 0x18 | op.rs))
@thedecoder
class VZ(DecoderLeaf):
feature = 0
outflags = ()
regtabs = ()
@classmethod
def _decode(self, cmd, op):
# VNOOP
self.setop(cmd)
# this can be VX, but more human friendly.
@thedecoder
class VW(DecoderLeaf):
feature = idaapi.CF_CHG1 | idaapi.CF_USE2
outflags = (0, 0, 0, 0)
regtabs = (DecoderLeaf.rVEC, DecoderLeaf.rVACC, DecoderLeaf.rVEC, DecoderLeaf.rVEC)
@classmethod
def _decode(self, cmd, op):
# $sa <- accu[i[$rs]]; accu[i[$rs]] <- $rd (rt is not used but shown as reference) (VSAW)
self.setop(cmd, self.reg(op.sa), self.reg(op.rs), self.reg(op.rd), self.reg(op.rt))
## note: all tables are taken from Project64 RSP plugin, including "None"s.
@thedecoder
class SP(DecoderSub):
tab_index = staticmethod(lambda op: op.funct)
tab = [
("SLL", SXX), None, ("SRL", SXX), ("SRA", SXX), ("SLLV", SXV), None, ("SRLV", SXV), ("SRAV", SXV),
("JR", ), ("JALR", ), None, None, None, ("BREAK", ), None, None,
None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None,
("ADD", R), ("ADDU", R), ("SUB", R), ("SUBU", R), ("AND", R), ("OR", R), ("XOR", R), ("NOR", R),
None, None, ("SLT", R), ("SLTU", R), None, None, None, None,
None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None,
]
@thedecoder
class REGIMM(DecoderSub):
tab_index = staticmethod(lambda op: op.rt)
tab = [
("BLTZ", B1), ("BGEZ", B1), None, None, None, None, None, None,
None, None, None, None, None, None, None, None,
("BLTZAL", B1), ("BGEZAL", B1), None, None, None, None, None, None,
None, None, None, None, None, None, None, None,
]
@thedecoder
class COP0(DecoderSub):
tab_index = staticmethod(lambda op: op.rs)
tab = [
("MFC0", ), None, None, None, ("MTC0", ), None, None, None,
None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None,
]
@thedecoder
class VEC(DecoderSub):
tab_index = staticmethod(lambda op: op.funct)
tab = [
("VMULF", VX), ("VMULU", VX), None, None, ("VMUDL", VX), ("VMUDM", VX), ("VMUDN", VX), ("VMUDH", VX),
("VMACF", VX), ("VMACU", VX), None, ("VMACQ", VX), ("VMADL", VX), ("VMADM", VX), ("VMADN", VX), ("VMADH", VX),
("VADD", VX), ("VSUB", VX), None, ("VABS", VX), ("VADDC", VX), ("VSUBC", VX), None, None,
None, None, None, None, None, ("VSAW", VW), None, None,
("VLT", VX), ("VEQ", VX), ("VNE", VX), ("VGE", VX), ("VCL", VX), ("VCH", VX), ("VCR", VX), ("VMRG", VX),
("VAND", VX), ("VNAND", VX), ("VOR", VX), ("VNOR", VX), ("VXOR", VX), ("VNXOR", VX), None, None,
("VRCP", VY), ("VRCPL", VY), ("VRCPH", VY), ("VMOV", VY), ("VRSQ", VY), ("VRSQL", VY), ("VRSQH", VY), ("VNOOP", VZ),
None, None, None, None, None, None, None, None,
]
@thedecoder
class COP2(DecoderSub):
tab_index = staticmethod(lambda op: op.rs)
tab = [ # TODO: what mnem used in IDA mips RSP about CFC2/CTC2?
("MFC2", ), None, ("CFC2", ), None, ("MTC2", ), None, ("CTC2", ), None,
None, None, None, None, None, None, None, None,
("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ),
("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ), ("Vec", ),
]
@thedecoder
class LC2(DecoderSub):
tab_index = staticmethod(lambda op: op.rd)
tab = [
("LBV", VL0), ("LSV", VL1), ("LLV", VL2), ("LDV", VL3), ("LQV", VL4), ("LRV", VL4), ("LPV", VL3), ("LUV", VL3),
("LHV", VL4), ("LFV", VL4), None, ("LTV", VL4), None, None, None, None,
None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None,
]
@thedecoder
class SC2(DecoderSub):
tab_index = staticmethod(lambda op: op.rd)
tab = [
("SBV", VS0), ("SSV", VS1), ("SLV", VS2), ("SDV", VS3), ("SQV", VS4), ("SRV", VS4), ("SPV", VS3), ("SUV", VS3),
("SHV", VS4), ("SFV", VS4), ("SWV", VS4), ("STV", VS4), None, None, None, None,
None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None,
]
class Root(DecoderSub):
tab_index = staticmethod(lambda op: op.op)
# note: at LC2/SC2 normal insn is LWC2/STC2 that is load/store word to/from Cop2, not sub-functions group!
tab = [
("Sp.", ), ("RegImm", ), ("J", ), ("JAL", ), ("BEQ", B2), ("BNE", B2), ("BLEZ", B1), ("BGTZ", B1),
("ADDI", IS), ("ADDIU", IS), ("SLTI", IS), ("SLTIU", IS), ("ANDI", IZ), ("ORI", IZ), ("XORI", IZ), ("LUI", IZ),
("COP0", ), None, ("COP2", ), None, None, None, None, None,
None, None, None, None, None, None, None, None,
("LB", ), ("LH", ), None, ("LW", ), ("LBU", LB), ("LHU", LH), None, None,
("SB", ), ("SH", ), None, ("SW", ), None, None, None, None,
None, None, ("LC2", ), None, None, None, None, None,
None, None, ("SC2", ), None, None, None, None, None,
]
"""
def __fmt(ind, arr):
def ln(mnem, fnn):
if mnem is None:
return 0
return 2 + len(mnem) + 3 + len(fnn) + 1
ws = []
for x in xrange(8):
w = 0
for y in xrange(len(arr) // 8):
e = arr[y * 8 + x]
mnem = e[0] if e is not None else None
fnn = e[1].__name__ if e is not None and 2 <= len(e) else ""
w = max((w, ln(mnem, fnn)))
ws.append(w)
ret = ""
for y in xrange(len(arr) // 8):
line = ""
for x in xrange(8):
e = arr[y * 8 + x]
w = ws[x]
if e is None:
if w != 0: # is not "all None"
line += " " # indent 2 spaces
w -= 2 # indent
w = max((w - 4, 0))
line += "None,%s" % ((" " * (w + 1)) if x < 7 else "")
else:
mnem = e[0]
fnn = e[1].__name__ if 2 <= len(e) else ""
fnn = (" " * (w - ln(mnem, fnn))) + fnn
line += '("%s", %s),%s' % (mnem, fnn, " " if x < 7 else "")
ret += ("\t" * ind) + line + "\n"
return ret
def fmt(s):
i = io.StringIO(unicode(s))
b = None
bi = None
o = ""
while True:
s = i.readline()
if s == "": break
s = s.rstrip()
g = re.match("(\t*)(.*)", s)
if not g: raise RuntimeError("Unknown re error: " + s)
ind = len(g.group(1))
s = g.group(2)
if s == "tab = [":
b = ""
bi = ind
if b is not None:
b += s + "\n"
else:
o += ("\t" * ind) + s + "\n"
if s == "]":
exec b # !!!!!!
o += ("\t" * ind) + "tab = [\n"
o += __fmt(ind + 1, tab)
o += ("\t" * ind) + "]\n"
b = None
return o
from subprocess import Popen, PIPE
p = Popen("clip", stdin=PIPE); p.stdin.write(o); p.stdin.close(); p.wait()
"""
# ----------------------------------------------------------------------
class mips_rsp_processor_t(idaapi.processor_t):
# IDP id ( Numbers above 0x8000 are reserved for the third-party modules)
id = 0x8000 + 1964
# Processor features
flag = idaapi.PR_DEFSEG32 | idaapi.PR_USE32 | idaapi.PRN_HEX | idaapi.PR_RNAMESOK | idaapi.PR_DELAYED
#idaapi.PR_ASSEMBLE | idaapi.PR_SEGS | idaapi.PR_NO_SEGMOVE
#idaapi.PR_TYPEINFO
# Number of bits in a byte for code segments (usually 8)
# IDA supports values up to 32 bits
cnbits = 8
# Number of bits in a byte for non-code segments (usually 8)
# IDA supports values up to 32 bits
dnbits = 8
# short processor names
# Each name should be shorter than 9 characters
psnames = ['mipsrsp', 'mipsrsp1']
# long processor names
# No restriction on name lengthes.
plnames = ['MIPS subset of N64 RSP', 'RSP1']
# General purpose registers
_gprnamed = [
"$zero", "$at", "$v0", "$v1",
"$a0", "$a1", "$a2", "$a3",
"$t0", "$t1", "$t2", "$t3",
"$t4", "$t5", "$t6", "$t7",
"$s0", "$s1", "$s2", "$s3",
"$s4", "$s5", "$s6", "$s7",
"$t8", "$t9", "$k0", "$k1",
"$gp", "$sp", "$s8", "$ra",
]
_gprunnamed = ["$%d" % i for i in xrange(32)]
# COP0 (SP/DP) registers
_cop0reg = [
"SP_MEM_ADDR_REG",
"SP_DRAM_ADDR_REG",
"SP_RD_LEN_REG",
"SP_WR_LEN_REG",
"SP_STATUS_REG",
"SP_DMA_FULL_REG",
"SP_DMA_BUSY_REG",
"SP_SEMAPHORE_REG",
"DPC_START_REG",
"DPC_END_REG",
"DPC_CURRENT_REG",
"DPC_STATUS_REG",
"DPC_CLOCK_REG",
"DPC_BUFBUSY_REG",
"DPC_PIPEBUSY_REG",
"DPC_TMEM_REG",
] + ["COP0_0x%02X" % i for i in xrange(16, 32)] # XXX over 0x10???
# COP2 (Vector) registers
# note1: del(element)? on out(), we built string there so not required to declare "del" here.
# note2: make differ from GPR $v0, $v1!
_cop2reg = ["$V%d" % i for i in xrange(32)]
# COP2 (Vector) element selector names
_cop2els = ["[?%X]" % i for i in xrange(16)] + ["", "[??]"] + ["[%dq]" % i for i in xrange(2)] + ["[%dh]" % i for i in xrange(4)] + ["[%d]" % i for i in xrange(8)]
# COP2 (Vector) control registers (flags)
_cop2ctlreg = ["VCO", "VCC", "VCE", "???"] # overflow, cond?, eq??
# COP2 (Vector) accumulator vector registers
_cop2accs = ["ACC-%02X" % i for i in xrange(16)] + ["ACC?%02X" % i for i in xrange(8)] + ["ACCUM_H", "ACCUM_M", "ACCUM_L"] + ["ACC?%02X" % i for i in xrange(11, 16)]
# register names; XXX: is this used only for str2reg??
regNames = []
regNames.extend(_gprnamed)
regNames.extend(_cop0reg)
regNames.extend(_cop2reg)
#regNames.extend(_cop2els) # dangerous "" included!
regNames.extend(_cop2ctlreg)
regNames.extend(_cop2accs)
regNames.extend([
# Fake segment registers
"CS",
"DS"
])
# number of registers (optional: deduced from the len(regNames))
regsNum = len(regNames)
# Segment register information (use virtual CS and DS registers if your
# processor doesn't have segment registers):
regFirstSreg = len(regNames) - 2 # index of CS
regLastSreg = len(regNames) - 1 # index of DS
# size of a segment register in bytes
segreg_size = 0
# You should define 2 virtual segment registers for CS and DS.
# number of CS/DS registers
regCodeSreg = len(regNames) - 2
regDataSreg = len(regNames) - 1
# Array of typical code start sequences (optional)
codestart = []
# Array of 'return' instruction opcodes (optional)
retcodes = []
# Array of instructions
# note: almost(all?) proc modules add NULL instruc at head. why? but follow.
instruc = [{"name": "", "feature": 0, "_handler": None}] + Root.collect_insns()
# [
# {'name': 'INSN1', 'feature': idaapi.CF_USE1},
# {'name': 'INSN2', 'feature': idaapi.CF_USE1 | idaapi.CF_CHG1}
# ]
_i2h = []
for e in instruc:
_i2h.append(e["_handler"])
del e["_handler"]
_rev_instruc = {}
for i, e in enumerate(instruc):
if e["name"] in _rev_instruc:
idaapi.msg("mips_rsp: XXX skip %r\n" % e["name"])
continue
_rev_instruc[e["name"]] = i
#idaapi.msg("%r\n" % self._rev_instruc)
# icode of the first instruction
instruc_start = 0
# icode of the last instruction + 1
instruc_end = len(instruc) + 1
# Size of long double (tbyte) for this processor (meaningful only if ash.a_tbyte != NULL) (optional)
tbyte_size = 0
#
# Number of digits in floating numbers after the decimal point.
# If an element of this array equals 0, then the corresponding
# floating point data is not used for the processor.
# This array is used to align numbers in the output.
# real_width[0] - number of digits for short floats (only PDP-11 has them)
# real_width[1] - number of digits for "float"
# real_width[2] - number of digits for "double"
# real_width[3] - number of digits for "long double"
# Example: IBM PC module has { 0,7,15,19 }
#
# (optional)
real_width = (0, 7, 15, 0)
# icode (or instruction number) of return instruction. It is ok to give any of possible return
# instructions
icode_return = -1
# If the FIXUP_VHIGH and FIXUP_VLOW fixup types are supported
# then the number of bits in the HIGH part. For example,
# SPARC will have here 22 because it has HIGH22 and LOW10 relocations.
# See also: the description of PR_FULL_HIFXP bit
# (optional)
high_fixup_bits = 16
# only one assembler is supported
assembler = {
# flag
'flag' : idaapi.ASH_HEXF3 | idaapi.AS_UNEQU | idaapi.AS_COLON | idaapi.ASB_BINF4,
# | idaapi.AS_N2CHR
# user defined flags (local only for IDP) (optional)
'uflag' : 0,
# Assembler name (displayed in menus)
'name': "Imaginal RSP Assembler",
# array of automatically generated header lines they appear at the start of disassembled text (optional)
'header': ["Line1", "Line2"],
# array of unsupported instructions (array of cmd.itype) (optional)
'badworks': [], #6,11
# org directive
'origin': "org",
# end directive
'end': "end",
# comment string (see also cmnt2)
'cmnt': "#",
# ASCII string delimiter
'ascsep': "\"",
# ASCII char constant delimiter
'accsep': "'",
# ASCII special chars (they can't appear in character and ascii constants)
'esccodes': "\"'",
#
# Data representation (db,dw,...):
#
# ASCII string directive
'a_ascii': "ascii",
# byte directive
'a_byte': ".byte",
# word directive
'a_word': ".half",
# remove if not allowed
'a_dword': ".word",
# remove if not allowed
'a_qword': ".dword",
# remove if not allowed
'a_oword': ".qword",
# remove if not allowed
#'a_yword': "ymmword",
# float; 4bytes; remove if not allowed
'a_float': ".float",
# double; 8bytes; NULL if not allowed
'a_double': ".double",
# long double; NULL if not allowed
#'a_tbyte': "",
# packed decimal real; remove if not allowed (optional)
#'a_packreal': "",
# array keyword. the following
# sequences may appear:
# #h - header
# #d - size
# #v - value
# #s(b,w,l,q,f,d,o) - size specifiers
# for byte,word,
# dword,qword,
# float,double,oword
'a_dups': "#d times #v",
# uninitialized data directive (should include '%s' for the size of data)
'a_bss': "%s times ?",
# 'equ' Used if AS_UNEQU is set (optional)
'a_equ': ".equ",
# 'seg ' prefix (example: push seg seg001)
'a_seg': "seg",
#
# translation to use in character and string constants.
# usually 1:1, i.e. trivial translation
# If specified, must be 256 chars long
# (optional)
#'XlatAsciiOutput': "".join([chr(x) for x in xrange(256)]),
# current IP (instruction pointer) symbol in assembler
'a_curip': "$",
# "public" name keyword. NULL-gen default, ""-do not generate
'a_public': ".globl",
# "weak" name keyword. NULL-gen default, ""-do not generate
'a_weak': ".weak",
# "extrn" name keyword
'a_extrn': ".extern",
# "comm" (communal variable)
'a_comdef': "",
# "align" keyword
'a_align': ".align",
# Left and right braces used in complex expressions
'lbrace': "(",
'rbrace': ")",
# % mod assembler time operation
'a_mod': "%",
# & bit and assembler time operation
'a_band': "&",
# | bit or assembler time operation
'a_bor': "|",
# ^ bit xor assembler time operation
'a_xor': "^",
# ~ bit not assembler time operation
'a_bnot': "~",
# << shift left assembler time operation
'a_shl': "<<",
# >> shift right assembler time operation
'a_shr': ">>",
# size of type (format string) (optional)
'a_sizeof_fmt': "size %s",
'flag2': 0,
# comment close string (optional)
# this is used to denote a string which closes comments, for example, if the comments are represented with (* ... *)
# then cmnt = "(*" and cmnt2 = "*)"
'cmnt2': "",
# low8 operation, should contain %s for the operand (optional fields)
'low8': "",
'high8': "",
'low16': "",
'high16': "",
# the include directive (format string) (optional)
'a_include_fmt': "include %s",
# if a named item is a structure and displayed in the verbose (multiline) form then display the name
# as printf(a_strucname_fmt, typename)
# (for asms with type checking, e.g. tasm ideal)
# (optional)
'a_vstruc_fmt': "",
# 3-byte data (optional)
'a_3byte': "",
# 'rva' keyword for image based offsets (optional)
# (see nalt.hpp, REFINFO_RVA)
'a_rva': ".rva"
} # Assembler
def __init__(self):
super(mips_rsp_processor_t, self).__init__()
#self._handler = None
#self._op = None
self._undefq = set()
for k, v in self._rev_instruc.items():
setattr(self, "itype_" + k.lower(), v)
self._node = None
Decoder.proc = self
# ----------------------------------------------------------------------
# The following callbacks are optional.
# *** Please remove the callbacks that you don't plan to implement ***
def header(self):
"""function to produce start of disassembled text"""
idaapi.MakeLine("## header")
def footer(self):
"""function to produce end of disassembled text"""
idaapi.MakeLine("## footer")
def segstart(self, ea):
"""function to produce start of segment"""
idaapi.MakeLine("## segstart %08X" % ea)
pass
def segend(self, ea):
"""function to produce end of segment"""
idaapi.MakeLine("## segend %08X" % ea)
pass
## def assumes(self, ea):
## """function to produce assume directives"""
## idaapi.MakeLine("## assume %08X" % ea)
## pass
# def notify_term(self):
# """called when the processor module is unloading"""
# pass
# def notify_setup_til(self):
# """Setup default type libraries (called after loading a new file into the database)
# The processor module may load tils, setup memory model and perform other actions required to set up the type system
# @return: None
# """
# pass
def notify_newprc(self, nproc):
"""
Before changing proccesor type
nproc - processor number in the array of processor names
return 1-ok,0-prohibit
"""
#idaapi.msg("## changing processor to %r\n" % nproc)
idaapi.cvar.inf.mf = 1 # bigendian ...setting here is too late? not! endianess changed when loaded, or explicit set, not startup of load IDB...
return 1
def notify_newfile(self, filename):
"""A new file is loaded (already)"""
idaapi.msg("## newfile file %r\n" % filename)
def notify_oldfile(self, filename):
"""An old file is loaded (already)"""
idaapi.msg("## oldfile(idb) file %r\n" % filename)
pass
# def notify_newbinary(self, filename, fileoff, basepara, binoff, nbytes):
# """
# Before loading a binary file
# args:
# filename - binary file name
# fileoff - offset in the file
# basepara - base loading paragraph
# binoff - loader offset
# nbytes - number of bytes to load
# Returns nothing
# """
# pass
def notify_undefine(self, ea):
"""
An item in the database (insn or data) is being deleted
@param args: ea
@return: >0-ok, <=0 - the kernel should stop
if the return value is positive:
bit0 - ignored
bit1 - do not delete srareas at the item end
"""
# XXX why I must do this thing? this is not DOUNK_EXPAND's work??
# XXX if you just fail to makecode, IDA undefines, then everything gone. SO FRUSTRATE! THIS MUST NOT BE MY TASK!
"""
#with open("e:\debugxx.log", "a") as f: print >>f, "UNDEF %08X" % ea
if idaapi.isCode(idaapi.getFlags(ea)):
#with open("e:\debugxx.log", "a") as f: print >>f, " this is code!"
for xea in idautils.CodeRefsFrom(ea, True):
#with open("e:\debugxx.log", "a") as f: print >>f, " chain %08X" % xea
if idaapi.isCode(idaapi.getFlags(xea)) and xea not in self._undefq:
self._undefq.add(xea)
#with open("e:\debugxx.log", "a") as f: print >>f, " doit %08X" % xea
idaapi.do_unknown(xea, idaapi.DOUNK_SIMPLE)
#with open("e:\debugxx.log", "a") as f: print >>f, " ok %08X" % ea
self._undefq.remove(xea)
#"""
return 1
def notify_endbinary(self, ok):
"""
After loading a binary file
args:
ok - file loaded successfully?
"""
idaapi.msg("## endbinary; ok=%r\n" % ok)
# def notify_assemble(self, ea, cs, ip, use32, line):
# """
# Assemble an instruction
# (make sure that PR_ASSEMBLE flag is set in the processor flags)
# (display a warning if an error occurs)
# args:
# ea - linear address of instruction
# cs - cs of instruction
# ip - ip of instruction
# use32 - is 32bit segment?
# line - line to assemble
# returns the opcode string
# """
# pass
# def notify_savebase(self):
# """The database is being saved. Processor module should save its local data"""
# idaapi.msg("## will save local data...\n")
## if define this, default IDA data output (like "00 61 .word 0x0061 # a") is totally disabled.
# def data_out(self, ea):
# """
# Generate text represenation of data items
# This function MAY change the database and create cross-references, etc.
# """
# pass
# def cmp_opnd(self, op1, op2):
# """
# Compare instruction operands.
# Returns 1-equal,0-not equal operands.
# """
# return False
# def can_have_type(self, op):
# """
# Can the operand have a type as offset, segment, decimal, etc.
# (for example, a register AX can't have a type, meaning that the user can't
# change its representation. see bytes.hpp for information about types and flags)
# Returns: bool
# """
# return True
# def translate(self, base, offset):
# """
# Translation function for offsets
# Currently used in the offset display functions
# to calculate the referenced address
# Returns: ea_t
# """
# idaapi.msg("## translate %08X+%08X\n" % (base, offset))
# return idaapi.BADADDR
def set_idp_options(self, keyword, type, value):
"""
Set IDP-specific option
args:
keyword - the option name
or empty string (check type when 0 below)
type - one of
IDPOPT_STR string constant
IDPOPT_NUM number
IDPOPT_BIT zero/one
IDPOPT_FLT float
IDPOPT_I64 64bit number
0 -> You should display a dialog to configure the processor module
value - the actual value
Returns:
IDPOPT_OK ok
IDPOPT_BADKEY illegal keyword
IDPOPT_BADTYPE illegal type of value
IDPOPT_BADVALUE illegal value (bad range, for example)
otherwise return a string containing the error messages
"""
idaapi.msg("## idp option k=%r t=%r v=%r\n" % (keyword, type, value))
if keyword == "":
st = """MIPS RSP specific option
<addi $at, $zero, 0x1234 -> #addi $1, $0, 0x1234#~U~nnamed GPR:{NamedGPR}>{chk1}>
"""
#idaapi.AskUsingForm_c(st)
cg1 = idaapi.Form.ChkGroupControl(["NamedGPR"], self._node.altval(0xFFFFffff))
form = idaapi.Form(st, {"chk1": cg1})
form.Compile()
if form.Execute() == 1:
# OK clicked.
self._node.altset(0xFFFFffff, cg1.value)
idaapi.refresh_idaview_anyway()
else:
pass
return idaapi.IDPOPT_OK
def gen_map_file(self, qfile):
"""
Generate map file. If this function is absent then the kernel will create the map file.
This function returns number of lines in output file.
0 - empty file, -1 - write error
"""
r1 = qfile.write("Line 1\n")
r2 = qfile.write("Line 2\n!")
return 2 # two lines
# def create_func_frame(self, func_ea):
# """
# Create a function frame for a newly created function.
# Set up frame size, its attributes etc.
# """
# return False
# def is_far_jump(self, icode):
# """
# Is indirect far jump or call instruction?
# meaningful only if the processor has 'near' and 'far' reference types
# """
# return False
# def is_align_insn(self, ea):
# """
# Is the instruction created only for alignment purposes?
# Returns: number of bytes in the instruction
# """
# return 0
# def outspec(self, ea, segtype):
# """
# Generate text representation of an item in a special segment
# i.e. absolute symbols, externs, communal definitions etc.
# Returns: 1-overflow, 0-ok
# """
# return 0
def get_frame_retsize(self, func_ea):
"""
Get size of function return address in bytes
If this function is absent, the kernel will assume
4 bytes for 32-bit function
2 bytes otherwise
"""
return 4
# def is_switch(self, swi):
# """
# Find 'switch' idiom.
# Fills 'si' structure with information
#
# @return: Boolean (True if switch was found and False otherwise)
# """
# return False
# def is_sp_based(self, op):
# """
# Check whether the operand is relative to stack pointer or frame pointer.
# This function is used to determine how to output a stack variable
# This function may be absent. If it is absent, then all operands
# are sp based by default.
# Define this function only if some stack references use frame pointer
# instead of stack pointer.
# returns flags:
# OP_FP_BASED operand is FP based
# OP_SP_BASED operand is SP based
# OP_SP_ADD operand value is added to the pointer
# OP_SP_SUB operand value is substracted from the pointer
# """
# return idaapi.OP_FP_BASED
# def notify_add_func(self, func_ea):
# """
# The kernel has added a function.
# @param func_ea: function start EA
# @return: Nothing
# """
# pass
# def notify_del_func(self, func_ea):
# """
# The kernel is about to delete a function
# @param func_ea: function start EA
# @return: 1-ok,<=0-do not delete
# """
# return 1
def notify_get_autocmt(self):
"""
Get instruction comment. 'cmd' describes the instruction in question
@return: None or the comment string
"""
_handler = self._i2h[self.cmd.itype]
return "comment for %s at %08X %r" % (self.instruc[self.cmd.itype]["name"], self.cmd.ea, _handler)
# def notify_create_switch_xrefs(self, jumpea, swi):
# """Create xrefs for a custom jump table
# @param jumpea: address of the jump insn
# @param swi: switch information
# @return: None
# """
# pass
# def notify_calc_step_over(self, ip):
# """
# Calculate the address of the instruction which will be
# executed after "step over". The kernel will put a breakpoint there.
# If the step over is equal to step into or we can not calculate
# the address, return BADADDR.
# args:
# ip - instruction address
# returns: target or BADADDR
# """
# return idaapi.BADADDR
# def notify_may_be_func(self, state):
# """
# can a function start here?
# the instruction is in 'cmd'
# arg: state -- autoanalysis phase
# state == 0: creating functions
# == 1: creating chunks
# returns: probability 0..100
# """
# return 0
def notify_str2reg(self, regname):
"""
Convert a register name to a register number
args: regname
Returns: register number or -1 if not avail
The register number is the register index in the regNames array
Most processor modules do not need to implement this callback
It is useful only if ph.regNames[reg] does not provide
the correct register names
additional note: it seems this is also used for highlighting?
"""
# XXX: array#index raises ValueError if not found, that sucks.
rn = regname
# remove vector element specifier if contained
if rn.startswith("$V") and "[" in rn:
rn = rn[:rn.index("[")]
r = -1
#for rs in [self._gprnamed, self._gprunnamed, self._cop0reg, self._cop2reg]:
# if rn in rs: return rs.index(rn)
if rn in self.regNames: r = self.regNames.index(rn)
#idaapi.msg("mips_rsp: str2reg: %s = %s -> %d\n" % (regname, rn, r))
return -1
# def notify_is_sane_insn(self, no_crefs):
# """
# is the instruction sane for the current file type?
# args: no_crefs
# 1: the instruction has no code refs to it.
# ida just tries to convert unexplored bytes
# to an instruction (but there is no other
# reason to convert them into an instruction)
# 0: the instruction is created because
# of some coderef, user request or another
# weighty reason.
# The instruction is in 'cmd'
# returns: 1-ok, <=0-no, the instruction isn't
# likely to appear in the program
# """
# return 0
# def notify_func_bounds(self, code, func_ea, max_func_end_ea):
# """
# find_func_bounds() finished its work
# The module may fine tune the function bounds
# args:
# possible code - one of FIND_FUNC_XXX (check find_func_bounds)
# func_ea - func start ea
# max_func_end_ea (from the kernel's point of view)
# returns: possible_return_code
# """
# return idaapi.FIND_FUNC_OK
# note: if defined header, "===SUBROUTINE===" and "sub_..." label disappear so must output manually!!
# def asm_func_header(self, func_ea):
# """generate function header lines"""
# idaapi.MakeLine("## func start %08X\n" % func_ea)
# note: if defined footer, "# End of function sub_..." disappear, so must output that manually.
# def asm_func_footer(self, func_ea):
# """generate function footer lines"""
# idaapi.MakeLine("## func end %08X\n" % func_ea)
def asm_get_type_name(self, flag, ea_or_id):
"""
Get name of type of item at ea or id.
(i.e. one of: byte,word,dword,near,far,etc...)
"""
if idaapi.isCode(flag):
pfn = idaapi.get_func(ea_or_id)
# return get func name
elif idaapi.isWord(flag):
return "word"
return "%type%"
def notify_init(self, idp_file):
# init returns non-zero on success
#idaapi.msg("## notify_init %r\n" % idp_file)
self._node = idaapi.netnode("$ mips rsp", 0, True) # FIXME swig does not support keyword args? "do_create=True" cause TypeError.
return 1
# def notify_outlabel(self, ea, colored_name):
# """
# The kernel is going to generate an instruction label line
# or a function header
# args:
# ea - instruction address
# colored_name -
# If returns value <=0, then the kernel should not generate the label
# """
# return 1
# def notify_rename(self, ea, new_name):
# """
# The kernel is going to rename a byte
# args:
# ea -
# new_name -
# If returns value <=0, then the kernel should not rename it
# """
# return 1
# def notify_may_show_sreg(self, ea):
# """
# The kernel wants to display the segment registers
# in the messages window.
# args:
# ea
# if this function returns 0
# then the kernel will not show
# the segment registers.
# (assuming that the module have done it)
# """
# return 1
# def notify_coagulate(self, start_ea):
# """
# Try to define some unexplored bytes
# This notification will be called if the
# kernel tried all possibilities and could
# not find anything more useful than to
# convert to array of bytes.
# The module can help the kernel and convert
# the bytes into something more useful.
# args:
# start_ea -
# returns: number of converted bytes
# """
# return 0
# def notify_closebase(self):
# """
# The database will be closed now
# """
# pass
# def notify_load_idasgn(self, short_sig_name):
# """
# FLIRT signature have been loaded for normal processing
# (not for recognition of startup sequences)
# args:
# short_sig_name
# """
# pass
# def notify_auto_empty(self):
# """
# Info: all analysis queues are empty.
# This callback is called once when the
# initial analysis is finished. If the queue is
# not empty upon the return from this callback,
# it will be called later again
# """
# pass
# def notify_is_call_insn(self, ea):
# """
# Is the instruction a "call"?
# args
# ea - instruction address
# returns: 1-unknown, 0-no, 2-yes
# """
# return 1
# def notify_is_ret_insn(self, ea, strict):
# """
# Is the instruction a "return"?
# ea - instruction address
# strict - 1: report only ret instructions
# 0: include instructions like "leave"
# which begins the function epilog
# returns: 1-unknown, 0-no, 2-yes
# """
# return 1
# def notify_kernel_config_loaded(self):
# """
# This callback is called when ida.cfg is parsed
# """
# pass
# def notify_is_alloca_probe(self, ea):
# """
# Does the function at 'ea' behave as __alloca_probe?
# args:
# ea
# returns: 2-yes, 1-false
# """
# return 1
def notify_out_src_file_lnnum(self, filename, lnnum):
"""
Callback: generate analog of
#line "file.c" 123
directive.
args:
file - source file (may be NULL)
lnnum - line number
returns: 2-directive has been generated
"""
idaapi.MakeLine("##line %r:%r" % (filename, lnnum))
return 2
# def notify_is_insn_table_jump(self):
# """
# Callback: determine if instruction is a table jump or call
# If CF_JUMP bit can not describe all kinds of table
# jumps, please define this callback.
# It will be called for insns with CF_JUMP bit set.
# input: cmd structure contains the current instruction
# returns: 1-yes, 0-no
# """
# return 0
# def notify_auto_empty_finally(self):
# """
# Info: all analysis queues are empty definitively
# """
# pass
# def notify_is_indirect_jump(self):
# """
# Callback: determine if instruction is an indrect jump
# If CF_JUMP bit can not describe all jump types
# jumps, please define this callback.
# input: cmd structure contains the current instruction
# returns: 1-use CF_JUMP, 2-no, 3-yes
# """
# return 1
# def notify_determined_main(self, main_ea):
# """
# The main() function has been determined
# """
# pass
# def notify_validate_flirt_func(self, ea, funcname):
# """
# flirt has recognized a library function
# this callback can be used by a plugin or proc module
# to intercept it and validate such a function
# args:
# start_ea
# funcname
# returns: -1-do not create a function,
# 1-function is validated
# the idp module is allowed to modify 'cmd'
# """
# return 1
# def notify_set_proc_options(self, options):
# """
# called if the user specified an option string in the command line:
# -p<processor name>:<options>
# can be used for e.g. setting a processor subtype
# also called if option string is passed to set_processor_type()
# and IDC's SetProcessorType()
# args:
# options
# returns: <0 - bad option string
# """
# return 1
# def notify_newseg(self, start_ea, segm_name, segm_class):
# """
# A new segment is about to be created
# args:
# start_ea
# segm_name
# segm_class
# return 1-ok, 0-segment should not be created
# """
# return 1
# def notify_auto_queue_empty(self, type):
# """
# One analysis queue is empty.
# args:
# atype_t type
# This callback can be called many times, so
# only the autoMark() functions can be used from it
# (other functions may work but it is not tested)
# """
# return 1
# def notify_gen_regvar_def(self, canon, user, cmt):
# """
# generate register variable definition line
# args:
# canon - canonical register name (case-insensitive)
# user - user-defined register name
# cmt - comment to appear near definition
# returns: 0-ok
# """
# return 1
# def notify_setsgr(self, start_ea, end_ea, regnum, value, old_value, tag):
# """
# The kernel has changed a segment register value
# args:
# startEA
# endEA
# regnum
# value
# old_value
# uchar tag (SR_... values)
# returns: 1-ok, 0-error
# """
# return 1
# def notify_set_compiler(self):
# """
# The kernel has changed the compiler information
# """
# pass
def notify_is_basic_block_end(self, call_insn_stops_block):
"""
Is the current instruction end of a basic block?
This function should be defined for processors
with delayed jump slots. The current instruction
is stored in 'cmd'
args:
call_insn_stops_block
returns: 1-unknown, 0-no, 2-yes
"""
if idaapi.decode_prev_insn(self.cmd.ea) == idaapi.BADADDR:
return 1
_handler = self._i2h[self.cmd.itype]
if issubclass(_handler, JumpBranch):
if self.instruc[self.cmd.itype]["name"] in ("JAL", "JALR", "BLTZAL", "BGEZAL") and not call_insn_stops_block: # FIXME: hard-coding...
return 0
return 2
return 0
def notify_make_code(self, ea, size):
"""
An instruction is being created
args:
ea
size
returns: 1-ok, <=0-the kernel should stop
"""
return 1 if 0x1000 <= ea and (ea & 3) == 0 else 0 # 0x1000: IMEM.
def notify_make_data(self, ea, flags, tid, size):
"""
A data item is being created
args:
ea
flags
tid
size
returns: 1-ok, <=0-the kernel should stop
"""
#idaapi.msg("%08X:%08X:%08X:%02X\n" % (ea, flags, tid, size))
esize = 1
if idaapi.isWord(flags): esize = 2
elif idaapi.isDwrd(flags): esize = 4
elif idaapi.isQwrd(flags): esize = 8
elif idaapi.isOwrd(flags): esize = 16
elif idaapi.isYwrd(flags): esize = 32
elif idaapi.isTbyt(flags): esize = 16 #10, but align.
elif idaapi.isFloat(flags): esize = 4
elif idaapi.isDouble(flags): esize = 8
elif idaapi.is3byte(flags): esize = 4 #3, but align.
#elif idaapi.isPackReal(flags): esize = ?
return 1 if (ea % esize) == 0 else 0
# def notify_moving_segm(self, start_ea, segm_name, segm_class, to_ea, flags):
# """
# May the kernel move the segment?
# args:
# start_ea, segm_name, segm_class - segment to move
# to_ea - new segment start address
# int flags - combination of MSF_... bits
# returns: 1-yes, <=0-the kernel should stop
# """
# return 1
# def notify_move_segm(self, from_ea, start_ea, segm_name, segm_class):
# """
# A segment is moved
# Fix processor dependent address sensitive information
# args:
# from_ea - old segment address
# start_ea, segm_name, segm_class - moved segment
# returns: nothing
# """
# pass
# def notify_verify_noreturn(self, func_start_ea):
# """
# The kernel wants to set 'noreturn' flags for a function
# args:
# func_start_ea
# Returns: 1-ok, any other value-do not set 'noreturn' flag
# """
# return 0
# def notify_verify_sp(self, func_start_ea):
# """
# All function instructions have been analyzed
# Now the processor module can analyze the stack pointer
# for the whole function
# args:
# func_start_ea
# Returns: 1-ok, 0-bad stack pointer
# """
# return 1
# def notify_renamed(self, ea, new_name, is_local_name):
# """
# The kernel has renamed a byte
# args:
# ea
# new_name
# is_local_name
# Returns: nothing. See also the 'rename' event
# """
# pass
## I don't know about set_func_start, but ~end have 3 args not 4.
## and I could not find where calls this func...
## def notify_set_func_start(self, func_start_ea, func_end_ea, new_ea):
## """
## Function chunk start address will be changed
## args:
## func_start_ea, func_end_ea
## new_ea
## Returns: 1-ok,<=0-do not change
## """
## return 1
## def notify_set_func_end(self, func_start_ea, func_end_ea, new_end_ea):
## """
## Function chunk end address will be changed
## args:
## func_start_ea, func_end_ea
## new_end_ea
## Returns: 1-ok,<=0-do not change
## """
## return 1
# def notify_treat_hindering_item(self, hindering_item_ea, new_item_flags, new_item_ea, new_item_length):
# """
# An item hinders creation of another item
# args:
# hindering_item_ea
# new_item_flags
# new_item_ea
# new_item_length
# Returns: 1-no reaction, <=0-the kernel may delete the hindering item
# """
# return 1
# def notify_get_operand_string(self, opnum):
# """
# Request text string for operand (cli, java, ...)
# args:
# opnum - the operand number; -1 means any string operand
# (cmd structure must contain info for the desired insn)
# Returns: requested
# """
# return ""
# def notify_coagulate_dref(self, from_ea, to_ea, may_define, code_ea):
# """
# data reference is being analyzed
# args:
# from_ea, to_ea, may_define, code_ea
# plugin may correct code_ea (e.g. for thumb mode refs, we clear the last bit)
# Returns: new code_ea or -1 - cancel dref analysis
# """
# return 0
# ----------------------------------------------------------------------
# The following callbacks are mandatory
#
def emu(self):
"""
Emulate instruction, create cross-references, plan to analyze
subsequent instructions, modify flags etc. Upon entrance to this function
all information about the instruction is in 'cmd' structure.
If zero is returned, the kernel will delete the instruction.
"""
ea = self.cmd.ea
# if this is BREAK, stop here.
if self.cmd.itype == self.itype_break:
return 1
# if this is jump/branch, cref there.
iname = self.instruc[self.cmd.itype]["name"]
_handler = self._i2h[self.cmd.itype] # note: get _handler from cmd.itype here is required! sometimes IDA kernel omits calling ana()...
if issubclass(_handler, JumpBranch) and iname not in ("JR", "JALR"): # without register jump...
if iname in ("J", "JAL"): # J(addr) form
iscall = (iname == "JAL")
addr = self.cmd.Op1.addr
elif iname in ("BLEZ", "BGTZ", "BLTZ", "BGEZ", "BLTZAL", "BGEZAL"): # I(reg, off) form
iscall = (iname in ("BLTZAL", "BGEZAL"))
addr = self.cmd.Op2.addr
elif iname in ("BEQ", "BNE"): # I(reg, reg, off) form
iscall = False
addr = self.cmd.Op3.addr
else:
idaapi.warning("mips_rsp: emu: Unknown jump/branch: %s @ 0x%08X" % (iname, self.cmd.ea))
return 1
# xref to jump target
targ = JumpBranch.iaddr(self.cmd, addr)
idaapi.ua_add_cref(0, targ, idaapi.fl_CN if iscall else idaapi.fl_JN)
if _handler.memdir is not None and self.cmd.Op2.phrase == 0: # 0: $zero
#idaapi.ua_add_dref(0, self.cmd.Op2.addr, DecoderLeaf.memdir2dr(_handler.memdir))
#idaapi.ua_add_off_drefs
idaapi.op_offset(self.cmd.ea, 1, idaapi.REF_OFF32)
for i in xrange(6):
op = self.cmd.Operands[i]
if op.type == idaapi.o_void:
break
# note: processor_t#get_uFlag gets from cvar.uFlag that is current instruction's first byte's flag.
if idaapi.isOff(self.get_uFlag(), op.n):
if _handler.memdir is not None:
dr = None
if self.cmd.get_canon_feature() & DecoderLeaf.CF_CHGS[i]:
dr = idaapi.dr_W
if self.cmd.get_canon_feature() & DecoderLeaf.CF_USES[i]:
dr = idaapi.dr_R
if dr is None:
idaapi.msg("mips_rsp: emu: BUG: unknown mem insn @ %08X op %d" % (self.cmd.ea, i + 1))
continue
# TODO: idaapi.dodata2 but if some error in insn? data broken...
# note: use c_int().value to convert Python's int/long(>=0x80000000) to adiff_t(=int32)
tgt = idaapi.calc_target(self.cmd.ea, self.cmd.ea, 1, ctypes.c_int(self.cmd.Operands[i].addr).value) # 1: Op2; from is for ri.target?
if tgt != idaapi.BADADDR:
idaapi.ua_add_dref(0, tgt, dr)
if idaapi.isUnknown(idaapi.getFlags(tgt)):
DecoderLeaf.doData(tgt, _handler.dtyp)
else:
# offset
idaapi.ua_add_off_drefs(self.cmd.Operands[i], idaapi.dr_O)
# if previous is jump, stop here.
if idaapi.decode_prev_insn(ea) != idaapi.BADADDR:
if self.instruc[self.cmd.itype]["name"] in ("J", "JR"):
# if other than code refs from prev jump/branch, or any data refs to here is exist, do not stop here. else stop here.
if list(idautils.CodeRefsTo(ea, True)) == [self.cmd.ea] and list(idautils.DataRefsTo(ea)) == []:
return 1
# re-decode current...
if idaapi.decode_insn(ea) == 0:
idaapi.msg("emu: cannot decode twice?? @ 0x%08X\n" % ea)
return 1
# this is needed to avoid generating border, and auto-makecode following code.
# make cref to next even this is jump, because of delay-slot.
idaapi.ua_add_cref(0, self.cmd.ea + self.cmd.size, idaapi.fl_F)
return 1
def outop(self, op):
"""
Generate text representation of an instructon operand.
This function shouldn't change the database, flags or anything else.
All these actions should be performed only by u_emu() function.
The output text is placed in the output buffer initialized with init_output_buffer()
This function uses out_...() functions from ua.hpp to generate the operand text
Returns: 1-ok, 0-operand is hidden.
"""
_handler = self._i2h[self.cmd.itype]
#idaapi.out_keyword("op%r:%r" % (op.n, op.type))
#idaapi.out_register("op%r:%r" % (op.n, op.type))
#idaapi.msg("outop: %r %05X n=%r t=%r r=%r\n" % (_handler, self.cmd.ea, op.n, _handler.regtabs[op.n], op.reg))
if len(_handler.outflags) < op.n:
return False
def regname(op):
return ((self._gprnamed if self._node.altval(0xFFFFffff) & 1 == 0 else self._gprunnamed), self._cop0reg, self._cop2reg, self._cop2ctlreg, self._cop2accs)[_handler.regtabs[op.n]][op.reg]
if op.type == idaapi.o_reg:
idaapi.out_register(regname(op))
elif op.type == idaapi.o_imm:
idaapi.OutValue(op, idaapi.OOFS_IFSIGN | idaapi.OOFW_IMM | _handler.outflags[op.n])
elif op.type == idaapi.o_displ:
idaapi.OutValue(op, idaapi.OOFS_IFSIGN | idaapi.OOFW_32 | idaapi.OOF_ADDR | idaapi.OOF_ZSTROFF) # XXX OOFW_32? or can't address minus (OOFW_16 is ok if 16bit full-offset). XXX OOF_ZSTROFF is sometimes ignored??
idaapi.out_symbol("(")
idaapi.out_register(regname(op)) # note: use op.phrase, but it is same as op.reg.
idaapi.out_symbol(")")
elif op.type == idaapi.o_near:
# FIXME: OOF_ADDR already set by outflags... FIXME: require OOFW??
#idaapi.OutValue(op, idaapi.OOFW_32 | idaapi.OOF_ADDR | _handler.outflags[op.n])
if not idaapi.out_name_expr(op, op.addr, idaapi.BADADDR):
idaapi.out_tagon(idaapi.COLOR_ERROR)
idaapi.OutLong(op.addr, 16)
idaapi.out_tagoff(idaapi.COLOR_ERROR)
elif op.type == idaapi.o_idpspec0:
idaapi.out_register("%s[%d]" % (regname(op), op.specval))
elif op.type == idaapi.o_idpspec1:
idaapi.out_register("%s%s" % (regname(op), self._cop2els[op.specval]))
return True
def out(self):
"""
Generate text representation of an instruction in 'cmd' structure.
This function shouldn't change the database, flags or anything else.
All these actions should be performed only by u_emu() function.
Returns: nothing
"""
buf = idaapi.init_output_buffer(1024)
if self.cmd.itype == self.itype_sll and self.cmd.Op1.reg == 0 and self.cmd.Op2.reg == 0 and self.cmd.Op3.value == 0:
idaapi.out_line("NOP".lower().ljust(8, " "), idaapi.COLOR_INSN)
else:
#idaapi.OutMnem(8, "") # width=8
idaapi.out_line(self.instruc[self.cmd.itype]["name"].lower().ljust(8, " "), idaapi.COLOR_INSN)
#idaapi.out_one_operand(0)
for i in xrange(6):
if self.cmd.Operands[i].type == idaapi.o_void:
break
if 0 < i:
idaapi.out_symbol(",")
idaapi.OutChar(" ")
idaapi.out_one_operand(i)
idaapi.term_output_buffer()
idaapi.cvar.gl_comm = 1
idaapi.MakeLine(buf)
def ana(self):
"""
Decodes an instruction into self.cmd.
Returns: self.cmd.size (=the size of the decoded instruction) or zero
"""
#print "ana: %05X" % (self.cmd.ea, )
dw = idaapi.ua_next_long()
op = MipsOp(dw)
try:
handler = Root.decode(self.cmd, None, op)
except Exception:
import traceback
traceback.print_exc()
#raise
return 0
if not handler:
return 0
#self._handler = handler
#self._op = op # cheat! use "cmd"! remove this at final!
# Return decoded instruction size or zero
return self.cmd.size
# ----------------------------------------------------------------------
# Every processor module script must provide this function.
# It should return a new instance of a class derived from idaapi.processor_t
def PROCESSOR_ENTRY():
return mips_rsp_processor_t()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment