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