1 ----------------------------------------------------------------------------
2 -- LuaJIT MIPS disassembler module.
4 -- Copyright (C) 2005-2015 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.
9 -- It disassembles all standard MIPS32R1/R2 instructions.
10 -- Default mode is big-endian, but see: dis_mipsel.lua
11 ------------------------------------------------------------------------------
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
21 ------------------------------------------------------------------------------
22 -- Primary and extended opcode maps
23 ------------------------------------------------------------------------------
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", }
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", "orDST", "xorDST", "nor|notDST0",
42 false, false, "sltDST", "sltuDST",
43 false, false, false, false,
44 "tgeSTZ", "tgeuSTZ", "tltSTZ", "tltuSTZ",
45 "teqSTZ", false, "tneSTZ",
48 local map_special2
= {
50 [0] = "maddST", "madduST", "mulDST", false,
52 [32] = "clzDS", [33] = "cloDS",
63 local map_special3
= {
65 [0] = "extTSAK", [4] = "insTSAL",
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",
85 shift
= 21, mask
= 15,
86 [0] = "mfc0TDW", [4] = "mtc0TDW",
88 [11] = { shift
= 5, mask
= 1, [0] = "diT0", "eiT0", },
92 [1] = "tlbr", [2] = "tlbwi", [6] = "tlbwr", [8] = "tlbp",
93 [24] = "eret", [31] = "deret",
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",
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",
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",
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",
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,
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",
165 shift
= 0, mask
= 63,
166 [32] = "cvt.s.wFG", [33] = "cvt.d.wFG",
170 shift
= 0, mask
= 63,
171 [32] = "cvt.s.lFG", [33] = "cvt.d.lFG",
175 shift
= 16, mask
= 3,
176 [0] = "bc1fCB", "bc1tCB", "bc1flCB", "bc1tlCB",
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
,
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,
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
, false, 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,
228 ------------------------------------------------------------------------------
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",
237 ------------------------------------------------------------------------------
239 -- Output a nicely formatted line with an opcode and operands.
240 local function putop(ctx
, text
, operands
)
244 local sym
= ctx
.symtab
[ctx
.rel
]
245 if sym
then extra
= "\t->"..sym
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
))
251 ctx
.out(format("%08x %-7s %s%s\n",
252 ctx
.addr
+pos
, text
, concat(operands
, ", "), extra
))
257 -- Fallback for unknown opcodes.
258 local function unknown(ctx
)
259 return putop(ctx
, ".long", { "0x"..tohex(ctx
.op
) })
262 local function get_be(ctx
)
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
)
268 local function get_le(ctx
)
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
)
274 -- Disassemble a single instruction.
275 local function disass_ins(ctx
)
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
._
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
291 for p
in gmatch(pat
, ".") do
294 x
= map_gpr
[band(rshift(op
, 21), 31)]
296 x
= map_gpr
[band(rshift(op
, 16), 31)]
298 x
= map_gpr
[band(rshift(op
, 11), 31)]
300 x
= "f"..band(rshift(op
, 6), 31)
302 x
= "f"..band(rshift(op
, 11), 31)
304 x
= "f"..band(rshift(op
, 16), 31)
306 x
= "f"..band(rshift(op
, 21), 31)
308 x
= band(rshift(op
, 6), 31)
310 x
= band(rshift(op
, 11), 31)
312 x
= band(rshift(op
, 16), 31)
314 x
= band(rshift(op
, 18), 7)
315 if x
== 0 then x
= nil end
317 x
= band(rshift(op
, 11), 31) + 1
319 x
= band(rshift(op
, 11), 31) - last
+ 1
321 x
= arshift(lshift(op
, 16), 16)
325 local disp
= arshift(lshift(op
, 16), 16)
326 operands
[#operands
] = format("%d(%s)", disp
, last
)
328 local index
= map_gpr
[band(rshift(op
, 16), 31)]
329 operands
[#operands
] = format("%s(%s)", index
, last
)
331 x
= ctx
.addr
+ ctx
.pos
+ arshift(lshift(op
, 16), 16)*4 + 4
335 x
= band(ctx
.addr
+ ctx
.pos
, 0xf0000000) + band(op
, 0x03ffffff)*4
339 x
= band(rshift(op
, 8), 7)
340 if x
== 0 then x
= nil end
343 if x
== 0 then x
= nil end
345 x
= band(rshift(op
, 6), 0x000fffff)
346 if x
== 0 then x
= nil end
348 x
= band(rshift(op
, 6), 1023)
349 if x
== 0 then x
= nil end
351 if last
== "r0" or last
== 0 then
356 local a1
, a2
= match(altname
, "([^|]*)|(.*)")
357 if a1
then name
, altname
= a1
, a2
358 else name
= altname
end
363 operands
[#operands
] = nil
368 if x
then operands
[#operands
+1] = x
; last
= x
end
371 return putop(ctx
, name
, operands
)
374 ------------------------------------------------------------------------------
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
383 while ctx
.pos
< stop
do disass_ins(ctx
) end
386 -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
387 local function create(code
, addr
, out
)
391 ctx
.out
= out
or io
.write
393 ctx
.disass
= disass_block
399 local function create_el(code
, addr
, out
)
400 local ctx
= create(code
, addr
, out
)
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()
410 local function disass_el(code
, addr
, out
)
411 create_el(code
, addr
, out
):disass()
414 -- Return register name for RID.
415 local function regname(r
)
416 if r
< 32 then return map_gpr
[r
] end
420 -- Public module functions.
423 create_el
= create_el
,
425 disass_el
= disass_el
,