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