1 ----------------------------------------------------------------------------
2 -- LuaJIT ARM disassembler module.
4 -- Copyright (C) 2005-2013 Mike Pall. All rights reserved.
5 -- Released under the MIT 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 most user-mode ARMv7 instructions
10 -- NYI: Advanced SIMD and VFP instructions.
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
, ror
, tohex
= bit
.band
, bit
.bor
, bit
.ror
, bit
.tohex
19 local lshift
, rshift
, arshift
= bit
.lshift
, bit
.rshift
, bit
.arshift
21 ------------------------------------------------------------------------------
23 ------------------------------------------------------------------------------
31 [0] = "vmovFmDN", "vstmFNdr",
35 { shift
= 16, mask
= 15, [13] = "vpushFdr", _
= "vstmdbFNdr", }
41 { shift
= 16, mask
= 15, [13] = "vpopFdr", _
= "vldmFNdr", },
44 [0] = "vldrFdl", "vldmdbFNdr",
52 [0] = "vmovGmDN", "vstmGNdr",
56 { shift
= 16, mask
= 15, [13] = "vpushGdr", _
= "vstmdbGNdr", }
62 { shift
= 16, mask
= 15, [13] = "vpopGdr", _
= "vldmGNdr", },
65 [0] = "vldrGdl", "vldmdbGNdr",
70 shift
= 0, mask
= 0 -- NYI ldc, mcrr, mrrc.
75 shift
= 6, mask
= 0x2c001,
76 [0] = "vmlaF.dnm", "vmlsF.dnm",
77 [0x04000] = "vnmlsF.dnm", [0x04001] = "vnmlaF.dnm",
78 [0x08000] = "vmulF.dnm", [0x08001] = "vnmulF.dnm",
79 [0x0c000] = "vaddF.dnm", [0x0c001] = "vsubF.dnm",
80 [0x20000] = "vdivF.dnm",
81 [0x24000] = "vfnmsF.dnm", [0x24001] = "vfnmaF.dnm",
82 [0x28000] = "vfmaF.dnm", [0x28001] = "vfmsF.dnm",
83 [0x2c000] = "vmovF.dY",
85 shift
= 7, mask
= 0x1e01,
86 [0] = "vmovF.dm", "vabsF.dm",
87 [0x0200] = "vnegF.dm", [0x0201] = "vsqrtF.dm",
88 [0x0800] = "vcmpF.dm", [0x0801] = "vcmpeF.dm",
89 [0x0a00] = "vcmpzF.d", [0x0a01] = "vcmpzeF.d",
90 [0x0e01] = "vcvtG.dF.m",
91 [0x1000] = "vcvt.f32.u32Fdm", [0x1001] = "vcvt.f32.s32Fdm",
92 [0x1800] = "vcvtr.u32F.dm", [0x1801] = "vcvt.u32F.dm",
93 [0x1a00] = "vcvtr.s32F.dm", [0x1a01] = "vcvt.s32F.dm",
98 shift
= 6, mask
= 0x2c001,
99 [0] = "vmlaG.dnm", "vmlsG.dnm",
100 [0x04000] = "vnmlsG.dnm", [0x04001] = "vnmlaG.dnm",
101 [0x08000] = "vmulG.dnm", [0x08001] = "vnmulG.dnm",
102 [0x0c000] = "vaddG.dnm", [0x0c001] = "vsubG.dnm",
103 [0x20000] = "vdivG.dnm",
104 [0x24000] = "vfnmsG.dnm", [0x24001] = "vfnmaG.dnm",
105 [0x28000] = "vfmaG.dnm", [0x28001] = "vfmsG.dnm",
106 [0x2c000] = "vmovG.dY",
108 shift
= 7, mask
= 0x1e01,
109 [0] = "vmovG.dm", "vabsG.dm",
110 [0x0200] = "vnegG.dm", [0x0201] = "vsqrtG.dm",
111 [0x0800] = "vcmpG.dm", [0x0801] = "vcmpeG.dm",
112 [0x0a00] = "vcmpzG.d", [0x0a01] = "vcmpzeG.d",
113 [0x0e01] = "vcvtF.dG.m",
114 [0x1000] = "vcvt.f64.u32GdFm", [0x1001] = "vcvt.f64.s32GdFm",
115 [0x1800] = "vcvtr.u32FdG.m", [0x1801] = "vcvt.u32FdG.m",
116 [0x1a00] = "vcvtr.s32FdG.m", [0x1a01] = "vcvt.s32FdG.m",
121 shift
= 24, mask
= 1,
125 shift
= 8, mask
= 15,
128 -- NYI cdp, mcr, mrc.
131 shift
= 8, mask
= 15,
133 shift
= 20, mask
= 15,
134 [0] = "vmovFnD", "vmovFDn",
136 [15] = { shift
= 12, mask
= 15, [15] = "vmrs", _
= "vmrsD", },
144 shift
= 0, mask
= 0, -- NYI unconditional CP load/store.
148 shift
= 0, mask
= 0, -- NYI unconditional CP data.
151 local map_simddata
= {
152 shift
= 0, mask
= 0, -- NYI SIMD data.
155 local map_simdload
= {
156 shift
= 0, mask
= 0, -- NYI SIMD load/store, preload.
159 local map_preload
= {
160 shift
= 0, mask
= 0, -- NYI preload.
164 shift
= 20, mask
= 31,
168 [0] = "sadd16DNM", "sasxDNM", "ssaxDNM", "ssub16DNM",
169 "sadd8DNM", false, false, "ssub8DNM",
173 [0] = "qadd16DNM", "qasxDNM", "qsaxDNM", "qsub16DNM",
174 "qadd8DNM", false, false, "qsub8DNM",
178 [0] = "shadd16DNM", "shasxDNM", "shsaxDNM", "shsub16DNM",
179 "shadd8DNM", false, false, "shsub8DNM",
184 [0] = "uadd16DNM", "uasxDNM", "usaxDNM", "usub16DNM",
185 "uadd8DNM", false, false, "usub8DNM",
189 [0] = "uqadd16DNM", "uqasxDNM", "uqsaxDNM", "uqsub16DNM",
190 "uqadd8DNM", false, false, "uqsub8DNM",
194 [0] = "uhadd16DNM", "uhasxDNM", "uhsaxDNM", "uhsub16DNM",
195 "uhadd8DNM", false, false, "uhsub8DNM",
199 [0] = "pkhbtDNMU", false, "pkhtbDNMU",
200 { shift
= 16, mask
= 15, [15] = "sxtb16DMU", _
= "sxtab16DNMU", },
201 "pkhbtDNMU", "selDNM", "pkhtbDNMU",
206 [0] = "ssatDxMu", "ssat16DxM", "ssatDxMu",
207 { shift
= 16, mask
= 15, [15] = "sxtbDMU", _
= "sxtabDNMU", },
208 "ssatDxMu", false, "ssatDxMu",
212 [0] = "ssatDxMu", "revDM", "ssatDxMu",
213 { shift
= 16, mask
= 15, [15] = "sxthDMU", _
= "sxtahDNMU", },
214 "ssatDxMu", "rev16DM", "ssatDxMu",
218 [3] = { shift
= 16, mask
= 15, [15] = "uxtb16DMU", _
= "uxtab16DNMU", },
223 [0] = "usatDwMu", "usat16DwM", "usatDwMu",
224 { shift
= 16, mask
= 15, [15] = "uxtbDMU", _
= "uxtabDNMU", },
225 "usatDwMu", false, "usatDwMu",
229 [0] = "usatDwMu", "rbitDM", "usatDwMu",
230 { shift
= 16, mask
= 15, [15] = "uxthDMU", _
= "uxtahDNMU", },
231 "usatDwMu", "revshDM", "usatDwMu",
234 shift
= 12, mask
= 15,
237 "smuadNMS", "smuadxNMS", "smusdNMS", "smusdxNMS",
241 [0] = "smladNMSD", "smladxNMSD", "smlsdNMSD", "smlsdxNMSD",
247 [0] = "smlaldDNMS", "smlaldxDNMS", "smlsldDNMS", "smlsldxDNMS",
251 [0] = { shift
= 12, mask
= 15, [15] = "smmulNMS", _
= "smmlaNMSD", },
252 { shift
= 12, mask
= 15, [15] = "smmulrNMS", _
= "smmlarNMSD", },
253 false, false, false, false,
254 "smmlsNMSD", "smmlsrNMSD",
259 [0] = { shift
= 12, mask
= 15, [15] = "usad8NMS", _
= "usada8NMSD", },
263 shift
= 5, mask
= 3, [2] = "sbfxDMvw",
266 shift
= 5, mask
= 3, [2] = "sbfxDMvw",
270 [0] = { shift
= 0, mask
= 15, [15] = "bfcDvX", _
= "bfiDMvX", },
274 [0] = { shift
= 0, mask
= 15, [15] = "bfcDvX", _
= "bfiDMvX", },
277 shift
= 5, mask
= 3, [2] = "ubfxDMvw",
280 shift
= 5, mask
= 3, [2] = "ubfxDMvw",
285 shift
= 21, mask
= 9,
287 shift
= 20, mask
= 5,
288 [0] = "strtDL", "ldrtDL", [4] = "strbtDL", [5] = "ldrbtDL",
291 shift
= 20, mask
= 5,
292 [0] = "strDL", "ldrDL", [4] = "strbDL", [5] = "ldrbDL",
298 [0] = map_load
, map_media
,
302 shift
= 20, mask
= 1,
304 shift
= 23, mask
= 3,
305 [0] = "stmdaNR", "stmNR",
306 { shift
= 16, mask
= 63, [45] = "pushR", _
= "stmdbNR", }, "stmibNR",
309 shift
= 23, mask
= 3,
310 [0] = "ldmdaNR", { shift
= 16, mask
= 63, [61] = "popR", _
= "ldmNR", },
311 "ldmdbNR", "ldmibNR",
316 shift
= 21, mask
= 15,
317 [0] = "andDNPs", "eorDNPs", "subDNPs", "rsbDNPs",
318 "addDNPs", "adcDNPs", "sbcDNPs", "rscDNPs",
319 "tstNP", "teqNP", "cmpNP", "cmnNP",
320 "orrDNPs", "movDPs", "bicDNPs", "mvnDPs",
324 shift
= 21, mask
= 7,
325 [0] = "mulNMSs", "mlaNMSDs", "umaalDNMS", "mlsDNMS",
326 "umullDNMSs", "umlalDNMSs", "smullDNMSs", "smlalDNMSs",
330 shift
= 20, mask
= 15, -- NYI: brackets around N. R(D+1) for ldrexd/strexd.
331 [0] = "swpDMN", false, false, false,
332 "swpbDMN", false, false, false,
333 "strexDMN", "ldrexDN", "strexdDN", "ldrexdDN",
334 "strexbDMN", "ldrexbDN", "strexhDN", "ldrexhDN",
338 shift
= 21, mask
= 3,
339 [0] = { shift
= 5, mask
= 3,
340 [0] = "smlabbNMSD", "smlatbNMSD", "smlabtNMSD", "smlattNMSD", },
341 { shift
= 5, mask
= 3,
342 [0] = "smlawbNMSD", "smulwbNMS", "smlawtNMSD", "smulwtNMS", },
343 { shift
= 5, mask
= 3,
344 [0] = "smlalbbDNMS", "smlaltbDNMS", "smlalbtDNMS", "smlalttDNMS", },
345 { shift
= 5, mask
= 3,
346 [0] = "smulbbNMS", "smultbNMS", "smulbtNMS", "smulttNMS", },
351 -- NYI: decode PSR bits of msr.
352 [0] = { shift
= 21, mask
= 1, [0] = "mrsD", "msrM", },
353 { shift
= 21, mask
= 3, "bxM", false, "clzDM", },
354 { shift
= 21, mask
= 3, "bxjM", },
355 { shift
= 21, mask
= 3, "blxM", },
357 { shift
= 21, mask
= 3, [0] = "qaddDMN", "qsubDMN", "qdaddDMN", "qdsubDMN", },
359 { shift
= 21, mask
= 3, "bkptK", },
366 [0] = { shift
= 24, mask
= 1, [0] = map_mul
, map_sync
, },
367 { shift
= 20, mask
= 1, [0] = "strhDL", "ldrhDL", },
368 { shift
= 20, mask
= 1, [0] = "ldrdDL", "ldrsbDL", },
369 { shift
= 20, mask
= 1, [0] = "strdDL", "ldrshDL", },
372 shift
= 20, mask
= 25,
373 [16] = { shift
= 7, mask
= 1, [0] = map_misc
, map_mulh
, },
375 shift
= 0, mask
= 0xffffffff,
376 [bor(0xe1a00000)] = "nop",
383 shift
= 20, mask
= 31, -- NYI: decode PSR bits of msr. Decode imm12.
384 [16] = "movwDW", [20] = "movtDW",
385 [18] = { shift
= 0, mask
= 0xf00ff, [0] = "nopv6", _
= "msrNW", },
391 shift
= 24, mask
= 1,
395 local map_condins
= {
396 [0] = map_datar
, map_datai
, map_load
, map_load1
,
397 map_loadm
, map_branch
, map_loadc
, map_datac
401 local map_uncondins
= {
402 [0] = false, map_simddata
, map_simdload
, map_preload
,
403 false, "blxB", map_loadcu
, map_datacu
,
406 ------------------------------------------------------------------------------
409 [0] = "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
410 "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc",
414 [0] = "eq", "ne", "hs", "lo", "mi", "pl", "vs", "vc",
415 "hi", "ls", "ge", "lt", "gt", "le", "al",
418 local map_shift
= { [0] = "lsl", "lsr", "asr", "ror", }
420 ------------------------------------------------------------------------------
422 -- Output a nicely formatted line with an opcode and operands.
423 local function putop(ctx
, text
, operands
)
427 local sym
= ctx
.symtab
[ctx
.rel
]
430 elseif band(ctx
.op
, 0x0e000000) ~= 0x0a000000 then
431 extra
= "\t; 0x"..tohex(ctx
.rel
)
434 if ctx
.hexdump
> 0 then
435 ctx
.out(format("%08x %s %-5s %s%s\n",
436 ctx
.addr
+pos
, tohex(ctx
.op
), text
, concat(operands
, ", "), extra
))
438 ctx
.out(format("%08x %-5s %s%s\n",
439 ctx
.addr
+pos
, text
, concat(operands
, ", "), extra
))
444 -- Fallback for unknown opcodes.
445 local function unknown(ctx
)
446 return putop(ctx
, ".long", { "0x"..tohex(ctx
.op
) })
449 -- Format operand 2 of load/store opcodes.
450 local function fmtload(ctx
, op
, pos
)
451 local base
= map_gpr
[band(rshift(op
, 16), 15)]
453 local ext
= (band(op
, 0x04000000) == 0)
454 if not ext
and band(op
, 0x02000000) == 0 then
456 if band(op
, 0x00800000) == 0 then ofs
= -ofs
end
457 if base
== "pc" then ctx
.rel
= ctx
.addr
+ pos
+ 8 + ofs
end
459 elseif ext
and band(op
, 0x00400000) ~= 0 then
460 ofs
= band(op
, 15) + band(rshift(op
, 4), 0xf0)
461 if band(op
, 0x00800000) == 0 then ofs
= -ofs
end
462 if base
== "pc" then ctx
.rel
= ctx
.addr
+ pos
+ 8 + ofs
end
465 ofs
= map_gpr
[band(op
, 15)]
466 if ext
or band(op
, 0xfe0) == 0 then
467 elseif band(op
, 0xfe0) == 0x60 then
468 ofs
= format("%s, rrx", ofs
)
470 local sh
= band(rshift(op
, 7), 31)
471 if sh
== 0 then sh
= 32 end
472 ofs
= format("%s, %s #%d", ofs
, map_shift
[band(rshift(op
, 5), 3)], sh
)
474 if band(op
, 0x00800000) == 0 then ofs
= "-"..ofs
end
477 x
= format("[%s]", base
)
478 elseif band(op
, 0x01000000) == 0 then
479 x
= format("[%s], %s", base
, ofs
)
481 x
= format("[%s, %s]", base
, ofs
)
483 if band(op
, 0x01200000) == 0x01200000 then x
= x
.."!" end
487 -- Format operand 2 of vector load/store opcodes.
488 local function fmtvload(ctx
, op
, pos
)
489 local base
= map_gpr
[band(rshift(op
, 16), 15)]
490 local ofs
= band(op
, 255)*4
491 if band(op
, 0x00800000) == 0 then ofs
= -ofs
end
492 if base
== "pc" then ctx
.rel
= ctx
.addr
+ pos
+ 8 + ofs
end
494 return format("[%s]", base
)
496 return format("[%s, #%d]", base
, ofs
)
500 local function fmtvr(op
, vr
, sh0
, sh1
)
502 return format("s%d", 2*band(rshift(op
, sh0
), 15)+band(rshift(op
, sh1
), 1))
504 return format("d%d", band(rshift(op
, sh0
), 15)+band(rshift(op
, sh1
-4), 16))
508 -- Disassemble a single instruction.
509 local function disass_ins(ctx
)
511 local b0
, b1
, b2
, b3
= byte(ctx
.code
, pos
+1, pos
+4)
512 local op
= bor(lshift(b3
, 24), lshift(b2
, 16), lshift(b1
, 8), b0
)
515 local last
, name
, pat
520 local cond
= rshift(op
, 28)
523 opat
= map_uncondins
[band(rshift(op
, 25), 7)]
525 if cond
~= 14 then suffix
= map_cond
[cond
] end
526 opat
= map_condins
[band(rshift(op
, 25), 7)]
528 while type(opat
) ~= "string" do
529 if not opat
then return unknown(ctx
) end
530 opat
= opat
[band(rshift(op
, opat
.shift
), opat
.mask
)] or opat
._
532 name
, pat
= match(opat
, "^([a-z0-9]*)(.*)")
533 if sub(pat
, 1, 1) == "." then
534 local s2
, p2
= match(pat
, "^([a-z0-9.]*)(.*)")
539 for p
in gmatch(pat
, ".") do
542 x
= map_gpr
[band(rshift(op
, 12), 15)]
544 x
= map_gpr
[band(rshift(op
, 16), 15)]
546 x
= map_gpr
[band(rshift(op
, 8), 15)]
548 x
= map_gpr
[band(op
, 15)]
550 x
= fmtvr(op
, vr
, 12, 22)
552 x
= fmtvr(op
, vr
, 16, 7)
554 x
= fmtvr(op
, vr
, 0, 5)
556 if band(op
, 0x02000000) ~= 0 then
557 x
= ror(band(op
, 255), 2*band(rshift(op
, 8), 15))
559 x
= map_gpr
[band(op
, 15)]
560 if band(op
, 0xff0) ~= 0 then
561 operands
[#operands
+1] = x
562 local s
= map_shift
[band(rshift(op
, 5), 3)]
564 if band(op
, 0xf90) == 0 then
565 if s
== "ror" then s
= "rrx" else r
= "#32" end
566 elseif band(op
, 0x10) == 0 then
567 r
= "#"..band(rshift(op
, 7), 31)
569 r
= map_gpr
[band(rshift(op
, 8), 15)]
571 if name
== "mov" then name
= s
; x
= r
572 elseif r
then x
= format("%s %s", s
, r
)
577 x
= fmtload(ctx
, op
, pos
)
579 x
= fmtvload(ctx
, op
, pos
)
581 local addr
= ctx
.addr
+ pos
+ 8 + arshift(lshift(op
, 8), 6)
582 if cond
== 15 then addr
= addr
+ band(rshift(op
, 23), 2) end
584 x
= "0x"..tohex(addr
)
590 suffix
= suffix
..(vr
== "s" and ".f32" or ".f64")
592 if band(op
, 0x00200000) ~= 0 and #operands
== 1 then
593 operands
[1] = operands
[1].."!"
597 if band(rshift(op
, i
), 1) == 1 then t
[#t
+1] = map_gpr
[i
] end
599 x
= "{"..concat(t
, ", ").."}"
601 if band(op
, 0x00200000) ~= 0 and #operands
== 2 then
602 operands
[1] = operands
[1].."!"
604 local s
= tonumber(sub(last
, 2))
605 local n
= band(op
, 255)
606 if vr
== "d" then n
= rshift(n
, 1) end
607 operands
[#operands
] = format("{%s-%s%d}", last
, vr
, s
+n
-1)
609 x
= band(op
, 0x0fff) + band(rshift(op
, 4), 0xf000)
611 x
= "#0x"..tohex(band(op
, 0x00ffffff), 6)
613 x
= band(rshift(op
, 7), 31)
614 if x
== 0 then x
= nil end
616 x
= band(rshift(op
, 7), 31)
617 if band(op
, 0x40) == 0 then
618 if x
== 0 then x
= nil else x
= "lsl #"..x
end
620 if x
== 0 then x
= "asr #32" else x
= "asr #"..x
end
623 x
= band(rshift(op
, 7), 31)
625 x
= band(rshift(op
, 16), 31)
627 x
= band(rshift(op
, 16), 31) + 1
629 x
= band(rshift(op
, 16), 31) - last
+ 1
631 x
= band(rshift(op
, 12), 0xf0) + band(op
, 0x0f)
633 x
= "#0x"..tohex(band(rshift(op
, 4), 0x0000fff0) + band(op
, 15), 4)
635 if band(op
, 0x00100000) ~= 0 then suffix
= "s"..suffix
end
641 if type(x
) == "number" then x
= "#"..x
end
642 operands
[#operands
+1] = x
646 return putop(ctx
, name
..suffix
, operands
)
649 ------------------------------------------------------------------------------
651 -- Disassemble a block of code.
652 local function disass_block(ctx
, ofs
, len
)
653 if not ofs
then ofs
= 0 end
654 local stop
= len
and ofs
+len
or #ctx
.code
657 while ctx
.pos
< stop
do disass_ins(ctx
) end
660 -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
661 local function create_(code
, addr
, out
)
665 ctx
.out
= out
or io
.write
667 ctx
.disass
= disass_block
672 -- Simple API: disassemble code (a string) at address and output via out.
673 local function disass_(code
, addr
, out
)
674 create_(code
, addr
, out
):disass()
677 -- Return register name for RID.
678 local function regname_(r
)
679 if r
< 16 then return map_gpr
[r
] end
683 -- Public module functions.