ljx

FORK: LuaJIT with native 5.2 and 5.3 support
git clone https://git.neptards.moe/neptards/ljx.git
Log | Files | Refs | README

dis_mips.lua (13228B)


      1 ----------------------------------------------------------------------------
      2 -- LuaJIT MIPS disassembler module.
      3 --
      4 -- Copyright (C) 2005-2016 Mike Pall. All rights reserved.
      5 -- Released under the MIT/X license. See Copyright Notice in luajit.h
      6 ----------------------------------------------------------------------------
      7 -- This is a helper module used by the LuaJIT machine code dumper module.
      8 --
      9 -- It disassembles all standard MIPS32R1/R2 instructions.
     10 -- Default mode is big-endian, but see: dis_mipsel.lua
     11 ------------------------------------------------------------------------------
     12 
     13 local type = type
     14 local sub, byte, format = string.sub, string.byte, string.format
     15 local match, gmatch, gsub = string.match, string.gmatch, string.gsub
     16 local concat = table.concat
     17 local bit = require("bit")
     18 local band, bor, tohex = bit.band, bit.bor, bit.tohex
     19 local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
     20 
     21 ------------------------------------------------------------------------------
     22 -- Primary and extended opcode maps
     23 ------------------------------------------------------------------------------
     24 
     25 local map_movci = { shift = 16, mask = 1, [0] = "movfDSC", "movtDSC", }
     26 local map_srl = { shift = 21, mask = 1, [0] = "srlDTA", "rotrDTA", }
     27 local map_srlv = { shift = 6, mask = 1, [0] = "srlvDTS", "rotrvDTS", }
     28 
     29 local map_special = {
     30   shift = 0, mask = 63,
     31   [0] = { shift = 0, mask = -1, [0] = "nop", _ = "sllDTA" },
     32   map_movci,	map_srl,	"sraDTA",
     33   "sllvDTS",	false,		map_srlv,	"sravDTS",
     34   "jrS",	"jalrD1S",	"movzDST",	"movnDST",
     35   "syscallY",	"breakY",	false,		"sync",
     36   "mfhiD",	"mthiS",	"mfloD",	"mtloS",
     37   false,	false,		false,		false,
     38   "multST",	"multuST",	"divST",	"divuST",
     39   false,	false,		false,		false,
     40   "addDST",	"addu|moveDST0", "subDST",	"subu|neguDS0T",
     41   "andDST",	"or|moveDST0",	"xorDST",	"nor|notDST0",
     42   false,	false,		"sltDST",	"sltuDST",
     43   false,	false,		false,		false,
     44   "tgeSTZ",	"tgeuSTZ",	"tltSTZ",	"tltuSTZ",
     45   "teqSTZ",	false,		"tneSTZ",
     46 }
     47 
     48 local map_special2 = {
     49   shift = 0, mask = 63,
     50   [0] = "maddST", "madduST",	"mulDST",	false,
     51   "msubST",	"msubuST",
     52   [32] = "clzDS", [33] = "cloDS",
     53   [63] = "sdbbpY",
     54 }
     55 
     56 local map_bshfl = {
     57   shift = 6, mask = 31,
     58   [2] = "wsbhDT",
     59   [16] = "sebDT",
     60   [24] = "sehDT",
     61 }
     62 
     63 local map_special3 = {
     64   shift = 0, mask = 63,
     65   [0] = "extTSAK", [4] = "insTSAL",
     66   [32] = map_bshfl,
     67   [59] = "rdhwrTD",
     68 }
     69 
     70 local map_regimm = {
     71   shift = 16, mask = 31,
     72   [0] = "bltzSB",	"bgezSB",	"bltzlSB",	"bgezlSB",
     73   false,	false,		false,		false,
     74   "tgeiSI",	"tgeiuSI",	"tltiSI",	"tltiuSI",
     75   "teqiSI",	false,		"tneiSI",	false,
     76   "bltzalSB",	"bgezalSB",	"bltzallSB",	"bgezallSB",
     77   false,	false,		false,		false,
     78   false,	false,		false,		false,
     79   false,	false,		false,		"synciSO",
     80 }
     81 
     82 local map_cop0 = {
     83   shift = 25, mask = 1,
     84   [0] = {
     85     shift = 21, mask = 15,
     86     [0] = "mfc0TDW", [4] = "mtc0TDW",
     87     [10] = "rdpgprDT",
     88     [11] = { shift = 5, mask = 1, [0] = "diT0", "eiT0", },
     89     [14] = "wrpgprDT",
     90   }, {
     91     shift = 0, mask = 63,
     92     [1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
     93     [24] = "eret", [31] = "deret",
     94     [32] = "wait",
     95   },
     96 }
     97 
     98 local map_cop1s = {
     99   shift = 0, mask = 63,
    100   [0] = "add.sFGH",	"sub.sFGH",	"mul.sFGH",	"div.sFGH",
    101   "sqrt.sFG",		"abs.sFG",	"mov.sFG",	"neg.sFG",
    102   "round.l.sFG",	"trunc.l.sFG",	"ceil.l.sFG",	"floor.l.sFG",
    103   "round.w.sFG",	"trunc.w.sFG",	"ceil.w.sFG",	"floor.w.sFG",
    104   false,
    105   { shift = 16, mask = 1, [0] = "movf.sFGC", "movt.sFGC" },
    106   "movz.sFGT",	"movn.sFGT",
    107   false,	"recip.sFG",	"rsqrt.sFG",	false,
    108   false,	false,		false,		false,
    109   false,	false,		false,		false,
    110   false,	"cvt.d.sFG",	false,		false,
    111   "cvt.w.sFG",	"cvt.l.sFG",	"cvt.ps.sFGH",	false,
    112   false,	false,		false,		false,
    113   false,	false,		false,		false,
    114   "c.f.sVGH",	"c.un.sVGH",	"c.eq.sVGH",	"c.ueq.sVGH",
    115   "c.olt.sVGH",	"c.ult.sVGH",	"c.ole.sVGH",	"c.ule.sVGH",
    116   "c.sf.sVGH",	"c.ngle.sVGH",	"c.seq.sVGH",	"c.ngl.sVGH",
    117   "c.lt.sVGH",	"c.nge.sVGH",	"c.le.sVGH",	"c.ngt.sVGH",
    118 }
    119 
    120 local map_cop1d = {
    121   shift = 0, mask = 63,
    122   [0] = "add.dFGH",	"sub.dFGH",	"mul.dFGH",	"div.dFGH",
    123   "sqrt.dFG",		"abs.dFG",	"mov.dFG",	"neg.dFG",
    124   "round.l.dFG",	"trunc.l.dFG",	"ceil.l.dFG",	"floor.l.dFG",
    125   "round.w.dFG",	"trunc.w.dFG",	"ceil.w.dFG",	"floor.w.dFG",
    126   false,
    127   { shift = 16, mask = 1, [0] = "movf.dFGC", "movt.dFGC" },
    128   "movz.dFGT",	"movn.dFGT",
    129   false,	"recip.dFG",	"rsqrt.dFG",	false,
    130   false,	false,		false,		false,
    131   false,	false,		false,		false,
    132   "cvt.s.dFG",	false,		false,		false,
    133   "cvt.w.dFG",	"cvt.l.dFG",	false,		false,
    134   false,	false,		false,		false,
    135   false,	false,		false,		false,
    136   "c.f.dVGH",	"c.un.dVGH",	"c.eq.dVGH",	"c.ueq.dVGH",
    137   "c.olt.dVGH",	"c.ult.dVGH",	"c.ole.dVGH",	"c.ule.dVGH",
    138   "c.df.dVGH",	"c.ngle.dVGH",	"c.deq.dVGH",	"c.ngl.dVGH",
    139   "c.lt.dVGH",	"c.nge.dVGH",	"c.le.dVGH",	"c.ngt.dVGH",
    140 }
    141 
    142 local map_cop1ps = {
    143   shift = 0, mask = 63,
    144   [0] = "add.psFGH",	"sub.psFGH",	"mul.psFGH",	false,
    145   false,		"abs.psFG",	"mov.psFG",	"neg.psFG",
    146   false,		false,		false,		false,
    147   false,		false,		false,		false,
    148   false,
    149   { shift = 16, mask = 1, [0] = "movf.psFGC", "movt.psFGC" },
    150   "movz.psFGT",	"movn.psFGT",
    151   false,	false,		false,		false,
    152   false,	false,		false,		false,
    153   false,	false,		false,		false,
    154   "cvt.s.puFG",	false,		false,		false,
    155   false,	false,		false,		false,
    156   "cvt.s.plFG",	false,		false,		false,
    157   "pll.psFGH",	"plu.psFGH",	"pul.psFGH",	"puu.psFGH",
    158   "c.f.psVGH",	"c.un.psVGH",	"c.eq.psVGH",	"c.ueq.psVGH",
    159   "c.olt.psVGH", "c.ult.psVGH",	"c.ole.psVGH",	"c.ule.psVGH",
    160   "c.psf.psVGH", "c.ngle.psVGH", "c.pseq.psVGH", "c.ngl.psVGH",
    161   "c.lt.psVGH",	"c.nge.psVGH",	"c.le.psVGH",	"c.ngt.psVGH",
    162 }
    163 
    164 local map_cop1w = {
    165   shift = 0, mask = 63,
    166   [32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
    167 }
    168 
    169 local map_cop1l = {
    170   shift = 0, mask = 63,
    171   [32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
    172 }
    173 
    174 local map_cop1bc = {
    175   shift = 16, mask = 3,
    176   [0] = "bc1fCB", "bc1tCB",	"bc1flCB",	"bc1tlCB",
    177 }
    178 
    179 local map_cop1 = {
    180   shift = 21, mask = 31,
    181   [0] = "mfc1TG", false,	"cfc1TG",	"mfhc1TG",
    182   "mtc1TG",	false,		"ctc1TG",	"mthc1TG",
    183   map_cop1bc,	false,		false,		false,
    184   false,	false,		false,		false,
    185   map_cop1s,	map_cop1d,	false,		false,
    186   map_cop1w,	map_cop1l,	map_cop1ps,
    187 }
    188 
    189 local map_cop1x = {
    190   shift = 0, mask = 63,
    191   [0] = "lwxc1FSX",	"ldxc1FSX",	false,		false,
    192   false,	"luxc1FSX",	false,		false,
    193   "swxc1FSX",	"sdxc1FSX",	false,		false,
    194   false,	"suxc1FSX",	false,		"prefxMSX",
    195   false,	false,		false,		false,
    196   false,	false,		false,		false,
    197   false,	false,		false,		false,
    198   false,	false,		"alnv.psFGHS",	false,
    199   "madd.sFRGH",	"madd.dFRGH",	false,		false,
    200   false,	false,		"madd.psFRGH",	false,
    201   "msub.sFRGH",	"msub.dFRGH",	false,		false,
    202   false,	false,		"msub.psFRGH",	false,
    203   "nmadd.sFRGH", "nmadd.dFRGH",	false,		false,
    204   false,	false,		"nmadd.psFRGH",	false,
    205   "nmsub.sFRGH", "nmsub.dFRGH",	false,		false,
    206   false,	false,		"nmsub.psFRGH",	false,
    207 }
    208 
    209 local map_pri = {
    210   [0] = map_special,	map_regimm,	"jJ",	"jalJ",
    211   "beq|beqz|bST00B",	"bne|bnezST0B",		"blezSB",	"bgtzSB",
    212   "addiTSI",	"addiu|liTS0I",	"sltiTSI",	"sltiuTSI",
    213   "andiTSU",	"ori|liTS0U",	"xoriTSU",	"luiTU",
    214   map_cop0,	map_cop1,	false,		map_cop1x,
    215   "beql|beqzlST0B",	"bnel|bnezlST0B",	"blezlSB",	"bgtzlSB",
    216   false,	false,		false,		false,
    217   map_special2,	"jalxJ",	false,		map_special3,
    218   "lbTSO",	"lhTSO",	"lwlTSO",	"lwTSO",
    219   "lbuTSO",	"lhuTSO",	"lwrTSO",	false,
    220   "sbTSO",	"shTSO",	"swlTSO",	"swTSO",
    221   false,	false,		"swrTSO",	"cacheNSO",
    222   "llTSO",	"lwc1HSO",	"lwc2TSO",	"prefNSO",
    223   false,	"ldc1HSO",	"ldc2TSO",	false,
    224   "scTSO",	"swc1HSO",	"swc2TSO",	false,
    225   false,	"sdc1HSO",	"sdc2TSO",	false,
    226 }
    227 
    228 ------------------------------------------------------------------------------
    229 
    230 local map_gpr = {
    231   [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
    232   "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15",
    233   "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
    234   "r24", "r25", "r26", "r27", "r28", "sp", "r30", "ra",
    235 }
    236 
    237 ------------------------------------------------------------------------------
    238 
    239 -- Output a nicely formatted line with an opcode and operands.
    240 local function putop(ctx, text, operands)
    241   local pos = ctx.pos
    242   local extra = ""
    243   if ctx.rel then
    244     local sym = ctx.symtab[ctx.rel]
    245     if sym then extra = "\t->"..sym end
    246   end
    247   if ctx.hexdump > 0 then
    248     ctx.out(format("%08x  %s  %-7s %s%s\n",
    249 	    ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
    250   else
    251     ctx.out(format("%08x  %-7s %s%s\n",
    252 	    ctx.addr+pos, text, concat(operands, ", "), extra))
    253   end
    254   ctx.pos = pos + 4
    255 end
    256 
    257 -- Fallback for unknown opcodes.
    258 local function unknown(ctx)
    259   return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
    260 end
    261 
    262 local function get_be(ctx)
    263   local pos = ctx.pos
    264   local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
    265   return bor(lshift(b0, 24), lshift(b1, 16), lshift(b2, 8), b3)
    266 end
    267 
    268 local function get_le(ctx)
    269   local pos = ctx.pos
    270   local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
    271   return bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
    272 end
    273 
    274 -- Disassemble a single instruction.
    275 local function disass_ins(ctx)
    276   local op = ctx:get()
    277   local operands = {}
    278   local last = nil
    279   ctx.op = op
    280   ctx.rel = nil
    281 
    282   local opat = map_pri[rshift(op, 26)]
    283   while type(opat) ~= "string" do
    284     if not opat then return unknown(ctx) end
    285     opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
    286   end
    287   local name, pat = match(opat, "^([a-z0-9_.]*)(.*)")
    288   local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
    289   if altname then pat = pat2 end
    290 
    291   for p in gmatch(pat, ".") do
    292     local x = nil
    293     if p == "S" then
    294       x = map_gpr[band(rshift(op, 21), 31)]
    295     elseif p == "T" then
    296       x = map_gpr[band(rshift(op, 16), 31)]
    297     elseif p == "D" then
    298       x = map_gpr[band(rshift(op, 11), 31)]
    299     elseif p == "F" then
    300       x = "f"..band(rshift(op, 6), 31)
    301     elseif p == "G" then
    302       x = "f"..band(rshift(op, 11), 31)
    303     elseif p == "H" then
    304       x = "f"..band(rshift(op, 16), 31)
    305     elseif p == "R" then
    306       x = "f"..band(rshift(op, 21), 31)
    307     elseif p == "A" then
    308       x = band(rshift(op, 6), 31)
    309     elseif p == "M" then
    310       x = band(rshift(op, 11), 31)
    311     elseif p == "N" then
    312       x = band(rshift(op, 16), 31)
    313     elseif p == "C" then
    314       x = band(rshift(op, 18), 7)
    315       if x == 0 then x = nil end
    316     elseif p == "K" then
    317       x = band(rshift(op, 11), 31) + 1
    318     elseif p == "L" then
    319       x = band(rshift(op, 11), 31) - last + 1
    320     elseif p == "I" then
    321       x = arshift(lshift(op, 16), 16)
    322     elseif p == "U" then
    323       x = band(op, 0xffff)
    324     elseif p == "O" then
    325       local disp = arshift(lshift(op, 16), 16)
    326       operands[#operands] = format("%d(%s)", disp, last)
    327     elseif p == "X" then
    328       local index = map_gpr[band(rshift(op, 16), 31)]
    329       operands[#operands] = format("%s(%s)", index, last)
    330     elseif p == "B" then
    331       x = ctx.addr + ctx.pos + arshift(lshift(op, 16), 16)*4 + 4
    332       ctx.rel = x
    333       x = "0x"..tohex(x)
    334     elseif p == "J" then
    335       x = band(ctx.addr + ctx.pos, 0xf0000000) + band(op, 0x03ffffff)*4
    336       ctx.rel = x
    337       x = "0x"..tohex(x)
    338     elseif p == "V" then
    339       x = band(rshift(op, 8), 7)
    340       if x == 0 then x = nil end
    341     elseif p == "W" then
    342       x = band(op, 7)
    343       if x == 0 then x = nil end
    344     elseif p == "Y" then
    345       x = band(rshift(op, 6), 0x000fffff)
    346       if x == 0 then x = nil end
    347     elseif p == "Z" then
    348       x = band(rshift(op, 6), 1023)
    349       if x == 0 then x = nil end
    350     elseif p == "0" then
    351       if last == "r0" or last == 0 then
    352 	local n = #operands
    353 	operands[n] = nil
    354 	last = operands[n-1]
    355 	if altname then
    356 	  local a1, a2 = match(altname, "([^|]*)|(.*)")
    357 	  if a1 then name, altname = a1, a2
    358 	  else name = altname end
    359 	end
    360       end
    361     elseif p == "1" then
    362       if last == "ra" then
    363 	operands[#operands] = nil
    364       end
    365     else
    366       assert(false)
    367     end
    368     if x then operands[#operands+1] = x; last = x end
    369   end
    370 
    371   return putop(ctx, name, operands)
    372 end
    373 
    374 ------------------------------------------------------------------------------
    375 
    376 -- Disassemble a block of code.
    377 local function disass_block(ctx, ofs, len)
    378   if not ofs then ofs = 0 end
    379   local stop = len and ofs+len or #ctx.code
    380   stop = stop - stop % 4
    381   ctx.pos = ofs - ofs % 4
    382   ctx.rel = nil
    383   while ctx.pos < stop do disass_ins(ctx) end
    384 end
    385 
    386 -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
    387 local function create(code, addr, out)
    388   local ctx = {}
    389   ctx.code = code
    390   ctx.addr = addr or 0
    391   ctx.out = out or io.write
    392   ctx.symtab = {}
    393   ctx.disass = disass_block
    394   ctx.hexdump = 8
    395   ctx.get = get_be
    396   return ctx
    397 end
    398 
    399 local function create_el(code, addr, out)
    400   local ctx = create(code, addr, out)
    401   ctx.get = get_le
    402   return ctx
    403 end
    404 
    405 -- Simple API: disassemble code (a string) at address and output via out.
    406 local function disass(code, addr, out)
    407   create(code, addr, out):disass()
    408 end
    409 
    410 local function disass_el(code, addr, out)
    411   create_el(code, addr, out):disass()
    412 end
    413 
    414 -- Return register name for RID.
    415 local function regname(r)
    416   if r < 32 then return map_gpr[r] end
    417   return "f"..(r-32)
    418 end
    419 
    420 -- Public module functions.
    421 return {
    422   create = create,
    423   create_el = create_el,
    424   disass = disass,
    425   disass_el = disass_el,
    426   regname = regname
    427 }
    428