diff options
author | Mike Pall <mike> | 2012-01-31 16:11:04 +0100 |
---|---|---|
committer | Mike Pall <mike> | 2012-01-31 16:11:04 +0100 |
commit | d0b1646c808a7994b6a035e7b02ab9f41febc1c3 (patch) | |
tree | a870c89f8c5bc8713c3563a7a4dd6201802c2459 /lib | |
parent | 10ef109eefdf5c1742ea4c0a62e7a127bf1bb43b (diff) | |
download | luajit-d0b1646c808a7994b6a035e7b02ab9f41febc1c3.tar.gz luajit-d0b1646c808a7994b6a035e7b02ab9f41febc1c3.tar.bz2 luajit-d0b1646c808a7994b6a035e7b02ab9f41febc1c3.zip |
MIPS: Add MIPS disassembler.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dis_mips.lua | 428 | ||||
-rw-r--r-- | lib/dis_mipsel.lua | 20 |
2 files changed, 448 insertions, 0 deletions
diff --git a/lib/dis_mips.lua b/lib/dis_mips.lua new file mode 100644 index 00000000..165405d2 --- /dev/null +++ b/lib/dis_mips.lua | |||
@@ -0,0 +1,428 @@ | |||
1 | ---------------------------------------------------------------------------- | ||
2 | -- LuaJIT MIPS disassembler module. | ||
3 | -- | ||
4 | -- Copyright (C) 2005-2011 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", "orDST", "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, 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, | ||
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", "gp", "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 | module(...) | ||
422 | |||
423 | create = create_ | ||
424 | create_el = create_el_ | ||
425 | disass = disass_ | ||
426 | disass_el = disass_el_ | ||
427 | regname = regname_ | ||
428 | |||
diff --git a/lib/dis_mipsel.lua b/lib/dis_mipsel.lua new file mode 100644 index 00000000..4c5a6515 --- /dev/null +++ b/lib/dis_mipsel.lua | |||
@@ -0,0 +1,20 @@ | |||
1 | ---------------------------------------------------------------------------- | ||
2 | -- LuaJIT MIPSEL disassembler wrapper module. | ||
3 | -- | ||
4 | -- Copyright (C) 2005-2011 Mike Pall. All rights reserved. | ||
5 | -- Released under the MIT license. See Copyright Notice in luajit.h | ||
6 | ---------------------------------------------------------------------------- | ||
7 | -- This module just exports the little-endian functions from the | ||
8 | -- MIPS disassembler module. All the interesting stuff is there. | ||
9 | ------------------------------------------------------------------------------ | ||
10 | |||
11 | local require = require | ||
12 | |||
13 | module(...) | ||
14 | |||
15 | local dis_mips = require(_PACKAGE.."dis_mips") | ||
16 | |||
17 | create = dis_mips.create_el | ||
18 | disass = dis_mips.disass_el | ||
19 | regname = dis_mips.regname | ||
20 | |||