aboutsummaryrefslogtreecommitdiff
path: root/src/jit/dis_arm64.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/dis_arm64.lua')
-rw-r--r--src/jit/dis_arm64.lua1233
1 files changed, 1233 insertions, 0 deletions
diff --git a/src/jit/dis_arm64.lua b/src/jit/dis_arm64.lua
new file mode 100644
index 00000000..896fab79
--- /dev/null
+++ b/src/jit/dis_arm64.lua
@@ -0,0 +1,1233 @@
1----------------------------------------------------------------------------
2-- LuaJIT ARM64 disassembler module.
3--
4-- Copyright (C) 2005-2026 Mike Pall. All rights reserved.
5-- Released under the MIT license. See Copyright Notice in luajit.h
6--
7-- Contributed by Djordje Kovacevic and Stefan Pejic from RT-RK.com.
8-- Sponsored by Cisco Systems, Inc.
9----------------------------------------------------------------------------
10-- This is a helper module used by the LuaJIT machine code dumper module.
11--
12-- It disassembles most user-mode AArch64 instructions.
13-- NYI: Advanced SIMD and VFP instructions.
14------------------------------------------------------------------------------
15
16local type = type
17local sub, byte, format = string.sub, string.byte, string.format
18local match, gmatch, gsub = string.match, string.gmatch, string.gsub
19local concat = table.concat
20local bit = require("bit")
21local band, bor, bxor, tohex = bit.band, bit.bor, bit.bxor, bit.tohex
22local lshift, rshift, arshift = bit.lshift, bit.rshift, bit.arshift
23local ror = bit.ror
24
25------------------------------------------------------------------------------
26-- Opcode maps
27------------------------------------------------------------------------------
28
29local map_adr = { -- PC-relative addressing.
30 shift = 31, mask = 1,
31 [0] = "adrDBx", "adrpDBx"
32}
33
34local map_addsubi = { -- Add/subtract immediate.
35 shift = 29, mask = 3,
36 [0] = "add|movDNIg", "adds|cmnD0NIg", "subDNIg", "subs|cmpD0NIg",
37}
38
39local map_logi = { -- Logical immediate.
40 shift = 31, mask = 1,
41 [0] = {
42 shift = 22, mask = 1,
43 [0] = {
44 shift = 29, mask = 3,
45 [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig"
46 },
47 false -- unallocated
48 },
49 {
50 shift = 29, mask = 3,
51 [0] = "andDNig", "orr|movDN0ig", "eorDNig", "ands|tstD0Nig"
52 }
53}
54
55local map_movwi = { -- Move wide immediate.
56 shift = 31, mask = 1,
57 [0] = {
58 shift = 22, mask = 1,
59 [0] = {
60 shift = 29, mask = 3,
61 [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg"
62 }, false -- unallocated
63 },
64 {
65 shift = 29, mask = 3,
66 [0] = "movnDWRg", false, "movz|movDYRg", "movkDWRg"
67 },
68}
69
70local map_bitf = { -- Bitfield.
71 shift = 31, mask = 1,
72 [0] = {
73 shift = 22, mask = 1,
74 [0] = {
75 shift = 29, mask = 3,
76 [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12w",
77 "bfm|bfi|bfxilDN13w",
78 "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12w"
79 }
80 },
81 {
82 shift = 22, mask = 1,
83 {
84 shift = 29, mask = 3,
85 [0] = "sbfm|sbfiz|sbfx|asr|sxtw|sxth|sxtbDN12x",
86 "bfm|bfi|bfxilDN13x",
87 "ubfm|ubfiz|ubfx|lsr|lsl|uxth|uxtbDN12x"
88 }
89 }
90}
91
92local map_datai = { -- Data processing - immediate.
93 shift = 23, mask = 7,
94 [0] = map_adr, map_adr, map_addsubi, false,
95 map_logi, map_movwi, map_bitf,
96 {
97 shift = 15, mask = 0x1c0c1,
98 [0] = "extr|rorDNM4w", [0x10080] = "extr|rorDNM4x",
99 [0x10081] = "extr|rorDNM4x"
100 }
101}
102
103local map_logsr = { -- Logical, shifted register.
104 shift = 31, mask = 1,
105 [0] = {
106 shift = 15, mask = 1,
107 [0] = {
108 shift = 29, mask = 3,
109 [0] = {
110 shift = 21, mask = 1,
111 [0] = "andDNMSg", "bicDNMSg"
112 },
113 {
114 shift = 21, mask = 1,
115 [0] = "orr|movDN0MSg", "orn|mvnDN0MSg"
116 },
117 {
118 shift = 21, mask = 1,
119 [0] = "eorDNMSg", "eonDNMSg"
120 },
121 {
122 shift = 21, mask = 1,
123 [0] = "ands|tstD0NMSg", "bicsDNMSg"
124 }
125 },
126 false -- unallocated
127 },
128 {
129 shift = 29, mask = 3,
130 [0] = {
131 shift = 21, mask = 1,
132 [0] = "andDNMSg", "bicDNMSg"
133 },
134 {
135 shift = 21, mask = 1,
136 [0] = "orr|movDN0MSg", "orn|mvnDN0MSg"
137 },
138 {
139 shift = 21, mask = 1,
140 [0] = "eorDNMSg", "eonDNMSg"
141 },
142 {
143 shift = 21, mask = 1,
144 [0] = "ands|tstD0NMSg", "bicsDNMSg"
145 }
146 }
147}
148
149local map_assh = {
150 shift = 31, mask = 1,
151 [0] = {
152 shift = 15, mask = 1,
153 [0] = {
154 shift = 29, mask = 3,
155 [0] = {
156 shift = 22, mask = 3,
157 [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg"
158 },
159 {
160 shift = 22, mask = 3,
161 [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg",
162 "adds|cmnD0NMSg", "adds|cmnD0NMg"
163 },
164 {
165 shift = 22, mask = 3,
166 [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg"
167 },
168 {
169 shift = 22, mask = 3,
170 [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg",
171 "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg"
172 },
173 },
174 false -- unallocated
175 },
176 {
177 shift = 29, mask = 3,
178 [0] = {
179 shift = 22, mask = 3,
180 [0] = "addDNMSg", "addDNMSg", "addDNMSg", "addDNMg"
181 },
182 {
183 shift = 22, mask = 3,
184 [0] = "adds|cmnD0NMSg", "adds|cmnD0NMSg", "adds|cmnD0NMSg",
185 "adds|cmnD0NMg"
186 },
187 {
188 shift = 22, mask = 3,
189 [0] = "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0MSg", "sub|negDN0Mg"
190 },
191 {
192 shift = 22, mask = 3,
193 [0] = "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0MzSg",
194 "subs|cmp|negsD0N0MzSg", "subs|cmp|negsD0N0Mzg"
195 }
196 }
197}
198
199local map_addsubsh = { -- Add/subtract, shifted register.
200 shift = 22, mask = 3,
201 [0] = map_assh, map_assh, map_assh
202}
203
204local map_addsubex = { -- Add/subtract, extended register.
205 shift = 22, mask = 3,
206 [0] = {
207 shift = 29, mask = 3,
208 [0] = "addDNMXg", "adds|cmnD0NMXg", "subDNMXg", "subs|cmpD0NMzXg",
209 }
210}
211
212local map_addsubc = { -- Add/subtract, with carry.
213 shift = 10, mask = 63,
214 [0] = {
215 shift = 29, mask = 3,
216 [0] = "adcDNMg", "adcsDNMg", "sbc|ngcDN0Mg", "sbcs|ngcsDN0Mg",
217 }
218}
219
220local map_ccomp = {
221 shift = 4, mask = 1,
222 [0] = {
223 shift = 10, mask = 3,
224 [0] = { -- Conditional compare register.
225 shift = 29, mask = 3,
226 "ccmnNMVCg", false, "ccmpNMVCg",
227 },
228 [2] = { -- Conditional compare immediate.
229 shift = 29, mask = 3,
230 "ccmnN5VCg", false, "ccmpN5VCg",
231 }
232 }
233}
234
235local map_csel = { -- Conditional select.
236 shift = 11, mask = 1,
237 [0] = {
238 shift = 10, mask = 1,
239 [0] = {
240 shift = 29, mask = 3,
241 [0] = "cselDNMzCg", false, "csinv|cinv|csetmDNMcg", false,
242 },
243 {
244 shift = 29, mask = 3,
245 [0] = "csinc|cinc|csetDNMcg", false, "csneg|cnegDNMcg", false,
246 }
247 }
248}
249
250local map_data1s = { -- Data processing, 1 source.
251 shift = 29, mask = 1,
252 [0] = {
253 shift = 31, mask = 1,
254 [0] = {
255 shift = 10, mask = 0x7ff,
256 [0] = "rbitDNg", "rev16DNg", "revDNw", false, "clzDNg", "clsDNg"
257 },
258 {
259 shift = 10, mask = 0x7ff,
260 [0] = "rbitDNg", "rev16DNg", "rev32DNx", "revDNx", "clzDNg", "clsDNg"
261 }
262 }
263}
264
265local map_data2s = { -- Data processing, 2 sources.
266 shift = 29, mask = 1,
267 [0] = {
268 shift = 10, mask = 63,
269 false, "udivDNMg", "sdivDNMg", false, false, false, false, "lslDNMg",
270 "lsrDNMg", "asrDNMg", "rorDNMg"
271 }
272}
273
274local map_data3s = { -- Data processing, 3 sources.
275 shift = 29, mask = 7,
276 [0] = {
277 shift = 21, mask = 7,
278 [0] = {
279 shift = 15, mask = 1,
280 [0] = "madd|mulDNMA0g", "msub|mnegDNMA0g"
281 }
282 }, false, false, false,
283 {
284 shift = 15, mask = 1,
285 [0] = {
286 shift = 21, mask = 7,
287 [0] = "madd|mulDNMA0g", "smaddl|smullDxNMwA0x", "smulhDNMx", false,
288 false, "umaddl|umullDxNMwA0x", "umulhDNMx"
289 },
290 {
291 shift = 21, mask = 7,
292 [0] = "msub|mnegDNMA0g", "smsubl|smneglDxNMwA0x", false, false,
293 false, "umsubl|umneglDxNMwA0x"
294 }
295 }
296}
297
298local map_datar = { -- Data processing, register.
299 shift = 28, mask = 1,
300 [0] = {
301 shift = 24, mask = 1,
302 [0] = map_logsr,
303 {
304 shift = 21, mask = 1,
305 [0] = map_addsubsh, map_addsubex
306 }
307 },
308 {
309 shift = 21, mask = 15,
310 [0] = map_addsubc, false, map_ccomp, false, map_csel, false,
311 {
312 shift = 30, mask = 1,
313 [0] = map_data2s, map_data1s
314 },
315 false, map_data3s, map_data3s, map_data3s, map_data3s, map_data3s,
316 map_data3s, map_data3s, map_data3s
317 }
318}
319
320local map_lrl = { -- Load register, literal.
321 shift = 26, mask = 1,
322 [0] = {
323 shift = 30, mask = 3,
324 [0] = "ldrDwB", "ldrDxB", "ldrswDxB"
325 },
326 {
327 shift = 30, mask = 3,
328 [0] = "ldrDsB", "ldrDdB"
329 }
330}
331
332local map_lsriind = { -- Load/store register, immediate pre/post-indexed.
333 shift = 30, mask = 3,
334 [0] = {
335 shift = 26, mask = 1,
336 [0] = {
337 shift = 22, mask = 3,
338 [0] = "strbDwzL", "ldrbDwzL", "ldrsbDxzL", "ldrsbDwzL"
339 }
340 },
341 {
342 shift = 26, mask = 1,
343 [0] = {
344 shift = 22, mask = 3,
345 [0] = "strhDwzL", "ldrhDwzL", "ldrshDxzL", "ldrshDwzL"
346 }
347 },
348 {
349 shift = 26, mask = 1,
350 [0] = {
351 shift = 22, mask = 3,
352 [0] = "strDwzL", "ldrDwzL", "ldrswDxzL"
353 },
354 {
355 shift = 22, mask = 3,
356 [0] = "strDszL", "ldrDszL"
357 }
358 },
359 {
360 shift = 26, mask = 1,
361 [0] = {
362 shift = 22, mask = 3,
363 [0] = "strDxzL", "ldrDxzL"
364 },
365 {
366 shift = 22, mask = 3,
367 [0] = "strDdzL", "ldrDdzL"
368 }
369 }
370}
371
372local map_lsriro = {
373 shift = 21, mask = 1,
374 [0] = { -- Load/store register immediate.
375 shift = 10, mask = 3,
376 [0] = { -- Unscaled immediate.
377 shift = 26, mask = 1,
378 [0] = {
379 shift = 30, mask = 3,
380 [0] = {
381 shift = 22, mask = 3,
382 [0] = "sturbDwK", "ldurbDwK"
383 },
384 {
385 shift = 22, mask = 3,
386 [0] = "sturhDwK", "ldurhDwK"
387 },
388 {
389 shift = 22, mask = 3,
390 [0] = "sturDwK", "ldurDwK"
391 },
392 {
393 shift = 22, mask = 3,
394 [0] = "sturDxK", "ldurDxK"
395 }
396 }
397 }, map_lsriind, false, map_lsriind
398 },
399 { -- Load/store register, register offset.
400 shift = 10, mask = 3,
401 [2] = {
402 shift = 26, mask = 1,
403 [0] = {
404 shift = 30, mask = 3,
405 [0] = {
406 shift = 22, mask = 3,
407 [0] = "strbDwO", "ldrbDwO", "ldrsbDxO", "ldrsbDwO"
408 },
409 {
410 shift = 22, mask = 3,
411 [0] = "strhDwO", "ldrhDwO", "ldrshDxO", "ldrshDwO"
412 },
413 {
414 shift = 22, mask = 3,
415 [0] = "strDwO", "ldrDwO", "ldrswDxO"
416 },
417 {
418 shift = 22, mask = 3,
419 [0] = "strDxO", "ldrDxO"
420 }
421 },
422 {
423 shift = 30, mask = 3,
424 [2] = {
425 shift = 22, mask = 3,
426 [0] = "strDsO", "ldrDsO"
427 },
428 [3] = {
429 shift = 22, mask = 3,
430 [0] = "strDdO", "ldrDdO"
431 }
432 }
433 }
434 }
435}
436
437local map_lsp = { -- Load/store register pair, offset.
438 shift = 22, mask = 1,
439 [0] = {
440 shift = 30, mask = 3,
441 [0] = {
442 shift = 26, mask = 1,
443 [0] = "stpDzAzwP", "stpDzAzsP",
444 },
445 {
446 shift = 26, mask = 1,
447 "stpDzAzdP"
448 },
449 {
450 shift = 26, mask = 1,
451 [0] = "stpDzAzxP"
452 }
453 },
454 {
455 shift = 30, mask = 3,
456 [0] = {
457 shift = 26, mask = 1,
458 [0] = "ldpDzAzwP", "ldpDzAzsP",
459 },
460 {
461 shift = 26, mask = 1,
462 [0] = "ldpswDAxP", "ldpDzAzdP"
463 },
464 {
465 shift = 26, mask = 1,
466 [0] = "ldpDzAzxP"
467 }
468 }
469}
470
471local map_ls = { -- Loads and stores.
472 shift = 24, mask = 0x31,
473 [0x10] = map_lrl, [0x30] = map_lsriro,
474 [0x20] = {
475 shift = 23, mask = 3,
476 map_lsp, map_lsp, map_lsp
477 },
478 [0x21] = {
479 shift = 23, mask = 3,
480 map_lsp, map_lsp, map_lsp
481 },
482 [0x31] = {
483 shift = 26, mask = 1,
484 [0] = {
485 shift = 30, mask = 3,
486 [0] = {
487 shift = 22, mask = 3,
488 [0] = "strbDwzU", "ldrbDwzU", "ldrsbDwzU", "ldrsbDxzU"
489 },
490 {
491 shift = 22, mask = 3,
492 [0] = "strhDwzU", "ldrhDwzU", "ldrshDwzU", "ldrshDxzU"
493 },
494 {
495 shift = 22, mask = 3,
496 [0] = "strDwzU", "ldrDwzU", "ldrswDxzU"
497 },
498 {
499 shift = 22, mask = 3,
500 [0] = "strDxzU", "ldrDxzU"
501 }
502 },
503 {
504 shift = 30, mask = 3,
505 [2] = {
506 shift = 22, mask = 3,
507 [0] = "strDszU", "ldrDszU"
508 },
509 [3] = {
510 shift = 22, mask = 3,
511 [0] = "strDdzU", "ldrDdzU"
512 }
513 }
514 },
515}
516
517local map_datafp = { -- Data processing, SIMD and FP.
518 shift = 28, mask = 7,
519 { -- 001
520 shift = 24, mask = 1,
521 [0] = {
522 shift = 21, mask = 1,
523 {
524 shift = 10, mask = 3,
525 [0] = {
526 shift = 12, mask = 1,
527 [0] = {
528 shift = 13, mask = 1,
529 [0] = {
530 shift = 14, mask = 1,
531 [0] = {
532 shift = 15, mask = 1,
533 [0] = { -- FP/int conversion.
534 shift = 31, mask = 1,
535 [0] = {
536 shift = 16, mask = 0xff,
537 [0x20] = "fcvtnsDwNs", [0x21] = "fcvtnuDwNs",
538 [0x22] = "scvtfDsNw", [0x23] = "ucvtfDsNw",
539 [0x24] = "fcvtasDwNs", [0x25] = "fcvtauDwNs",
540 [0x26] = "fmovDwNs", [0x27] = "fmovDsNw",
541 [0x28] = "fcvtpsDwNs", [0x29] = "fcvtpuDwNs",
542 [0x30] = "fcvtmsDwNs", [0x31] = "fcvtmuDwNs",
543 [0x38] = "fcvtzsDwNs", [0x39] = "fcvtzuDwNs",
544 [0x60] = "fcvtnsDwNd", [0x61] = "fcvtnuDwNd",
545 [0x62] = "scvtfDdNw", [0x63] = "ucvtfDdNw",
546 [0x64] = "fcvtasDwNd", [0x65] = "fcvtauDwNd",
547 [0x68] = "fcvtpsDwNd", [0x69] = "fcvtpuDwNd",
548 [0x70] = "fcvtmsDwNd", [0x71] = "fcvtmuDwNd",
549 [0x78] = "fcvtzsDwNd", [0x79] = "fcvtzuDwNd"
550 },
551 {
552 shift = 16, mask = 0xff,
553 [0x20] = "fcvtnsDxNs", [0x21] = "fcvtnuDxNs",
554 [0x22] = "scvtfDsNx", [0x23] = "ucvtfDsNx",
555 [0x24] = "fcvtasDxNs", [0x25] = "fcvtauDxNs",
556 [0x28] = "fcvtpsDxNs", [0x29] = "fcvtpuDxNs",
557 [0x30] = "fcvtmsDxNs", [0x31] = "fcvtmuDxNs",
558 [0x38] = "fcvtzsDxNs", [0x39] = "fcvtzuDxNs",
559 [0x60] = "fcvtnsDxNd", [0x61] = "fcvtnuDxNd",
560 [0x62] = "scvtfDdNx", [0x63] = "ucvtfDdNx",
561 [0x64] = "fcvtasDxNd", [0x65] = "fcvtauDxNd",
562 [0x66] = "fmovDxNd", [0x67] = "fmovDdNx",
563 [0x68] = "fcvtpsDxNd", [0x69] = "fcvtpuDxNd",
564 [0x70] = "fcvtmsDxNd", [0x71] = "fcvtmuDxNd",
565 [0x78] = "fcvtzsDxNd", [0x79] = "fcvtzuDxNd"
566 }
567 }
568 },
569 { -- FP data-processing, 1 source.
570 shift = 31, mask = 1,
571 [0] = {
572 shift = 22, mask = 3,
573 [0] = {
574 shift = 15, mask = 63,
575 [0] = "fmovDNf", "fabsDNf", "fnegDNf",
576 "fsqrtDNf", false, "fcvtDdNs", false, false,
577 "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf",
578 "frintaDNf", false, "frintxDNf", "frintiDNf",
579 },
580 {
581 shift = 15, mask = 63,
582 [0] = "fmovDNf", "fabsDNf", "fnegDNf",
583 "fsqrtDNf", "fcvtDsNd", false, false, false,
584 "frintnDNf", "frintpDNf", "frintmDNf", "frintzDNf",
585 "frintaDNf", false, "frintxDNf", "frintiDNf",
586 }
587 }
588 }
589 },
590 { -- FP compare.
591 shift = 31, mask = 1,
592 [0] = {
593 shift = 14, mask = 3,
594 [0] = {
595 shift = 23, mask = 1,
596 [0] = {
597 shift = 0, mask = 31,
598 [0] = "fcmpNMf", [8] = "fcmpNZf",
599 [16] = "fcmpeNMf", [24] = "fcmpeNZf",
600 }
601 }
602 }
603 }
604 },
605 { -- FP immediate.
606 shift = 31, mask = 1,
607 [0] = {
608 shift = 5, mask = 31,
609 [0] = {
610 shift = 23, mask = 1,
611 [0] = "fmovDFf"
612 }
613 }
614 }
615 },
616 { -- FP conditional compare.
617 shift = 31, mask = 1,
618 [0] = {
619 shift = 23, mask = 1,
620 [0] = {
621 shift = 4, mask = 1,
622 [0] = "fccmpNMVCf", "fccmpeNMVCf"
623 }
624 }
625 },
626 { -- FP data-processing, 2 sources.
627 shift = 31, mask = 1,
628 [0] = {
629 shift = 23, mask = 1,
630 [0] = {
631 shift = 12, mask = 15,
632 [0] = "fmulDNMf", "fdivDNMf", "faddDNMf", "fsubDNMf",
633 "fmaxDNMf", "fminDNMf", "fmaxnmDNMf", "fminnmDNMf",
634 "fnmulDNMf"
635 }
636 }
637 },
638 { -- FP conditional select.
639 shift = 31, mask = 1,
640 [0] = {
641 shift = 23, mask = 1,
642 [0] = "fcselDNMCf"
643 }
644 }
645 }
646 },
647 { -- FP data-processing, 3 sources.
648 shift = 31, mask = 1,
649 [0] = {
650 shift = 15, mask = 1,
651 [0] = {
652 shift = 21, mask = 5,
653 [0] = "fmaddDNMAf", "fnmaddDNMAf"
654 },
655 {
656 shift = 21, mask = 5,
657 [0] = "fmsubDNMAf", "fnmsubDNMAf"
658 }
659 }
660 }
661 },
662 { -- 010
663 shift = 0, mask = 0x81f8fc00,
664 [0x100e400] = "moviDdG"
665 }
666}
667
668local map_br = { -- Branches, exception generating and system instructions.
669 shift = 29, mask = 7,
670 [0] = "bB",
671 { -- Compare & branch, immediate.
672 shift = 24, mask = 3,
673 [0] = "cbzDBg", "cbnzDBg", "tbzDTBw", "tbnzDTBw"
674 },
675 { -- Conditional branch, immediate.
676 shift = 24, mask = 3,
677 [0] = {
678 shift = 4, mask = 1,
679 [0] = {
680 shift = 0, mask = 15,
681 [0] = "beqB", "bneB", "bhsB", "bloB", "bmiB", "bplB", "bvsB", "bvcB",
682 "bhiB", "blsB", "bgeB", "bltB", "bgtB", "bleB", "balB"
683 }
684 }
685 }, false, "blB",
686 { -- Compare & branch, immediate.
687 shift = 24, mask = 3,
688 [0] = "cbzDBg", "cbnzDBg", "tbzDTBx", "tbnzDTBx"
689 },
690 {
691 shift = 24, mask = 3,
692 [0] = { -- Exception generation.
693 shift = 0, mask = 0xe0001f,
694 [0x200000] = "brkW"
695 },
696 { -- System instructions.
697 shift = 0, mask = 0x3fffff,
698 [0x03201f] = "nop",
699 [0x03245f] = "bti c",
700 [0x03249f] = "bti j",
701 [0x0324df] = "bti jc",
702 },
703 { -- Unconditional branch, register.
704 shift = 0, mask = 0xfffc1f,
705 [0x1f0000] = "brNx", [0x3f0000] = "blrNx",
706 [0x5f0000] = "retNx"
707 },
708 }
709}
710
711local map_init = {
712 shift = 25, mask = 15,
713 [0] = false, false, false, false, map_ls, map_datar, map_ls, map_datafp,
714 map_datai, map_datai, map_br, map_br, map_ls, map_datar, map_ls, map_datafp
715}
716
717------------------------------------------------------------------------------
718
719local map_regs = { x = {}, w = {}, d = {}, s = {} }
720
721for i=0,30 do
722 map_regs.x[i] = "x"..i
723 map_regs.w[i] = "w"..i
724 map_regs.d[i] = "d"..i
725 map_regs.s[i] = "s"..i
726end
727map_regs.x[31] = "sp"
728map_regs.w[31] = "wsp"
729map_regs.d[31] = "d31"
730map_regs.s[31] = "s31"
731
732local map_cond = {
733 [0] = "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
734 "hi", "ls", "ge", "lt", "gt", "le", "al",
735}
736
737local map_shift = { [0] = "lsl", "lsr", "asr", "ror"}
738
739local map_extend = {
740 [0] = "uxtb", "uxth", "uxtw", "uxtx", "sxtb", "sxth", "sxtw", "sxtx",
741}
742
743------------------------------------------------------------------------------
744
745-- Output a nicely formatted line with an opcode and operands.
746local function putop(ctx, text, operands)
747 local pos = ctx.pos
748 local extra = ""
749 if ctx.rel then
750 local sym = ctx.symtab[ctx.rel]
751 if sym then
752 extra = "\t->"..sym
753 end
754 end
755 if ctx.hexdump > 0 then
756 ctx.out(format("%08x %s %-5s %s%s\n",
757 ctx.addr+pos, tohex(ctx.op), text, concat(operands, ", "), extra))
758 else
759 ctx.out(format("%08x %-5s %s%s\n",
760 ctx.addr+pos, text, concat(operands, ", "), extra))
761 end
762 ctx.pos = pos + 4
763end
764
765-- Fallback for unknown opcodes.
766local function unknown(ctx)
767 return putop(ctx, ".long", { "0x"..tohex(ctx.op) })
768end
769
770local function match_reg(p, pat, regnum)
771 return map_regs[match(pat, p.."%w-([xwds])")][regnum]
772end
773
774local function fmt_hex32(x)
775 if x < 0 then
776 return tohex(x)
777 else
778 return format("%x", x)
779 end
780end
781
782local imm13_rep = { 0x55555555, 0x11111111, 0x01010101, 0x00010001, 0x00000001 }
783
784local function decode_imm13(op)
785 local imms = band(rshift(op, 10), 63)
786 local immr = band(rshift(op, 16), 63)
787 if band(op, 0x00400000) == 0 then
788 local len = 5
789 if imms >= 56 then
790 if imms >= 60 then len = 1 else len = 2 end
791 elseif imms >= 48 then len = 3 elseif imms >= 32 then len = 4 end
792 local l = lshift(1, len)-1
793 local s = band(imms, l)
794 local r = band(immr, l)
795 local imm = ror(rshift(-1, 31-s), r)
796 if len ~= 5 then imm = band(imm, lshift(1, l)-1) + rshift(imm, 31-l) end
797 imm = imm * imm13_rep[len]
798 local ix = fmt_hex32(imm)
799 if rshift(op, 31) ~= 0 then
800 return ix..tohex(imm)
801 else
802 return ix
803 end
804 else
805 local lo, hi = -1, 0
806 if imms < 32 then lo = rshift(-1, 31-imms) else hi = rshift(-1, 63-imms) end
807 if immr ~= 0 then
808 lo, hi = ror(lo, immr), ror(hi, immr)
809 local x = immr == 32 and 0 or band(bxor(lo, hi), lshift(-1, 32-immr))
810 lo, hi = bxor(lo, x), bxor(hi, x)
811 if immr >= 32 then lo, hi = hi, lo end
812 end
813 if hi ~= 0 then
814 return fmt_hex32(hi)..tohex(lo)
815 else
816 return fmt_hex32(lo)
817 end
818 end
819end
820
821local function parse_immpc(op, name)
822 if name == "b" or name == "bl" then
823 return arshift(lshift(op, 6), 4)
824 elseif name == "adr" or name == "adrp" then
825 local immlo = band(rshift(op, 29), 3)
826 local immhi = lshift(arshift(lshift(op, 8), 13), 2)
827 return bor(immhi, immlo)
828 elseif name == "tbz" or name == "tbnz" then
829 return lshift(arshift(lshift(op, 13), 18), 2)
830 else
831 return lshift(arshift(lshift(op, 8), 13), 2)
832 end
833end
834
835local function parse_fpimm8(op)
836 local sign = band(op, 0x100000) == 0 and 1 or -1
837 local exp = bxor(rshift(arshift(lshift(op, 12), 5), 24), 0x80) - 131
838 local frac = 16+band(rshift(op, 13), 15)
839 return sign * frac * 2^exp
840end
841
842local function decode_fpmovi(op)
843 local lo = rshift(op, 5)
844 local hi = rshift(op, 9)
845 lo = bor(band(lo, 1) * 0xff, band(lo, 2) * 0x7f80, band(lo, 4) * 0x3fc000,
846 band(lo, 8) * 0x1fe00000)
847 hi = bor(band(hi, 1) * 0xff, band(hi, 0x80) * 0x1fe,
848 band(hi, 0x100) * 0xff00, band(hi, 0x200) * 0x7f8000)
849 if hi ~= 0 then
850 return fmt_hex32(hi)..tohex(lo)
851 else
852 return fmt_hex32(lo)
853 end
854end
855
856local function prefer_bfx(sf, uns, imms, immr)
857 if imms < immr or imms == 31 or imms == 63 then
858 return false
859 end
860 if immr == 0 then
861 if sf == 0 and (imms == 7 or imms == 15) then
862 return false
863 end
864 if sf ~= 0 and uns == 0 and (imms == 7 or imms == 15 or imms == 31) then
865 return false
866 end
867 end
868 return true
869end
870
871-- Disassemble a single instruction.
872local function disass_ins(ctx)
873 local pos = ctx.pos
874 local b0, b1, b2, b3 = byte(ctx.code, pos+1, pos+4)
875 local op = bor(lshift(b3, 24), lshift(b2, 16), lshift(b1, 8), b0)
876 local operands = {}
877 local suffix = ""
878 local last, name, pat
879 local map_reg
880 ctx.op = op
881 ctx.rel = nil
882 last = nil
883 local opat
884 opat = map_init[band(rshift(op, 25), 15)]
885 while type(opat) ~= "string" do
886 if not opat then return unknown(ctx) end
887 opat = opat[band(rshift(op, opat.shift), opat.mask)] or opat._
888 end
889 name, pat = match(opat, "^([a-z0-9]*)(.*)")
890 local altname, pat2 = match(pat, "|([a-z0-9_.|]*)(.*)")
891 if altname then pat = pat2 end
892 if sub(pat, 1, 1) == "." then
893 local s2, p2 = match(pat, "^([a-z0-9.]*)(.*)")
894 suffix = suffix..s2
895 pat = p2
896 end
897
898 local rt = match(pat, "[gf]")
899 if rt then
900 if rt == "g" then
901 map_reg = band(op, 0x80000000) ~= 0 and map_regs.x or map_regs.w
902 else
903 map_reg = band(op, 0x400000) ~= 0 and map_regs.d or map_regs.s
904 end
905 end
906
907 local second0, immr
908
909 for p in gmatch(pat, ".") do
910 local x = nil
911 if p == "D" then
912 local regnum = band(op, 31)
913 x = rt and map_reg[regnum] or match_reg(p, pat, regnum)
914 elseif p == "N" then
915 local regnum = band(rshift(op, 5), 31)
916 x = rt and map_reg[regnum] or match_reg(p, pat, regnum)
917 elseif p == "M" then
918 local regnum = band(rshift(op, 16), 31)
919 x = rt and map_reg[regnum] or match_reg(p, pat, regnum)
920 elseif p == "A" then
921 local regnum = band(rshift(op, 10), 31)
922 x = rt and map_reg[regnum] or match_reg(p, pat, regnum)
923 elseif p == "B" then
924 local addr = ctx.addr + pos + parse_immpc(op, name)
925 ctx.rel = addr
926 x = format("0x%08x", addr)
927 elseif p == "T" then
928 x = bor(band(rshift(op, 26), 32), band(rshift(op, 19), 31))
929 elseif p == "V" then
930 x = band(op, 15)
931 elseif p == "C" then
932 x = map_cond[band(rshift(op, 12), 15)]
933 elseif p == "c" then
934 local rn = band(rshift(op, 5), 31)
935 local rm = band(rshift(op, 16), 31)
936 local cond = band(rshift(op, 12), 15)
937 local invc = bxor(cond, 1)
938 x = map_cond[cond]
939 if altname and cond ~= 14 and cond ~= 15 then
940 local a1, a2 = match(altname, "([^|]*)|(.*)")
941 if rn == rm then
942 local n = #operands
943 operands[n] = nil
944 x = map_cond[invc]
945 if rn ~= 31 then
946 if a1 then name = a1 else name = altname end
947 else
948 operands[n-1] = nil
949 name = a2
950 end
951 end
952 end
953 elseif p == "W" then
954 x = band(rshift(op, 5), 0xffff)
955 elseif p == "Y" then
956 x = band(rshift(op, 5), 0xffff)
957 local hw = band(rshift(op, 21), 3)
958 if altname and (hw == 0 or x ~= 0) then
959 name = altname
960 end
961 elseif p == "L" then
962 local rn = map_regs.x[band(rshift(op, 5), 31)]
963 local imm9 = arshift(lshift(op, 11), 23)
964 if band(op, 0x800) ~= 0 then
965 x = "["..rn..", #"..imm9.."]!"
966 else
967 x = "["..rn.."], #"..imm9
968 end
969 elseif p == "U" then
970 local rn = map_regs.x[band(rshift(op, 5), 31)]
971 local sz = band(rshift(op, 30), 3)
972 local imm12 = lshift(rshift(lshift(op, 10), 20), sz)
973 if imm12 ~= 0 then
974 x = "["..rn..", #"..imm12.."]"
975 else
976 x = "["..rn.."]"
977 end
978 elseif p == "K" then
979 local rn = map_regs.x[band(rshift(op, 5), 31)]
980 local imm9 = arshift(lshift(op, 11), 23)
981 if imm9 ~= 0 then
982 x = "["..rn..", #"..imm9.."]"
983 else
984 x = "["..rn.."]"
985 end
986 elseif p == "O" then
987 local rn, rm = map_regs.x[band(rshift(op, 5), 31)]
988 local m = band(rshift(op, 13), 1)
989 if m == 0 then
990 rm = map_regs.w[band(rshift(op, 16), 31)]
991 else
992 rm = map_regs.x[band(rshift(op, 16), 31)]
993 end
994 x = "["..rn..", "..rm
995 local opt = band(rshift(op, 13), 7)
996 local s = band(rshift(op, 12), 1)
997 local sz = band(rshift(op, 30), 3)
998 -- extension to be applied
999 if opt == 3 then
1000 if s == 0 then x = x.."]"
1001 else x = x..", lsl #"..sz.."]" end
1002 elseif opt == 2 or opt == 6 or opt == 7 then
1003 if s == 0 then x = x..", "..map_extend[opt].."]"
1004 else x = x..", "..map_extend[opt].." #"..sz.."]" end
1005 else
1006 x = x.."]"
1007 end
1008 elseif p == "P" then
1009 local sh = 2 + rshift(op, 31 - band(rshift(op, 26), 1))
1010 local imm7 = lshift(arshift(lshift(op, 10), 25), sh)
1011 local rn = map_regs.x[band(rshift(op, 5), 31)]
1012 local ind = band(rshift(op, 23), 3)
1013 if ind == 1 then
1014 x = "["..rn.."], #"..imm7
1015 elseif ind == 2 then
1016 if imm7 == 0 then
1017 x = "["..rn.."]"
1018 else
1019 x = "["..rn..", #"..imm7.."]"
1020 end
1021 elseif ind == 3 then
1022 x = "["..rn..", #"..imm7.."]!"
1023 end
1024 elseif p == "I" then
1025 local shf = band(rshift(op, 22), 3)
1026 local imm12 = band(rshift(op, 10), 0x0fff)
1027 local rn, rd = band(rshift(op, 5), 31), band(op, 31)
1028 if altname == "mov" and shf == 0 and imm12 == 0 and (rn == 31 or rd == 31) then
1029 name = altname
1030 x = nil
1031 elseif shf == 0 then
1032 x = imm12
1033 elseif shf == 1 then
1034 x = imm12..", lsl #12"
1035 end
1036 elseif p == "i" then
1037 x = "#0x"..decode_imm13(op)
1038 elseif p == "1" then
1039 immr = band(rshift(op, 16), 63)
1040 x = immr
1041 elseif p == "2" then
1042 x = band(rshift(op, 10), 63)
1043 if altname then
1044 local a1, a2, a3, a4, a5, a6 =
1045 match(altname, "([^|]*)|([^|]*)|([^|]*)|([^|]*)|([^|]*)|(.*)")
1046 local sf = band(rshift(op, 26), 32)
1047 local uns = band(rshift(op, 30), 1)
1048 if prefer_bfx(sf, uns, x, immr) then
1049 name = a2
1050 x = x - immr + 1
1051 elseif immr == 0 and x == 7 then
1052 local n = #operands
1053 operands[n] = nil
1054 if sf ~= 0 then
1055 operands[n-1] = gsub(operands[n-1], "x", "w")
1056 end
1057 last = operands[n-1]
1058 name = a6
1059 x = nil
1060 elseif immr == 0 and x == 15 then
1061 local n = #operands
1062 operands[n] = nil
1063 if sf ~= 0 then
1064 operands[n-1] = gsub(operands[n-1], "x", "w")
1065 end
1066 last = operands[n-1]
1067 name = a5
1068 x = nil
1069 elseif x == 31 or x == 63 then
1070 if x == 31 and immr == 0 and name == "sbfm" then
1071 name = a4
1072 local n = #operands
1073 operands[n] = nil
1074 if sf ~= 0 then
1075 operands[n-1] = gsub(operands[n-1], "x", "w")
1076 end
1077 last = operands[n-1]
1078 else
1079 name = a3
1080 end
1081 x = nil
1082 elseif band(x, 31) ~= 31 and immr == x+1 and name == "ubfm" then
1083 name = a4
1084 last = "#"..(sf+32 - immr)
1085 operands[#operands] = last
1086 x = nil
1087 elseif x < immr then
1088 name = a1
1089 last = "#"..(sf+32 - immr)
1090 operands[#operands] = last
1091 x = x + 1
1092 end
1093 end
1094 elseif p == "3" then
1095 x = band(rshift(op, 10), 63)
1096 if altname then
1097 local a1, a2 = match(altname, "([^|]*)|(.*)")
1098 if x < immr then
1099 name = a1
1100 local sf = band(rshift(op, 26), 32)
1101 last = "#"..(sf+32 - immr)
1102 operands[#operands] = last
1103 x = x + 1
1104 else
1105 name = a2
1106 x = x - immr + 1
1107 end
1108 end
1109 elseif p == "4" then
1110 x = band(rshift(op, 10), 63)
1111 local rn = band(rshift(op, 5), 31)
1112 local rm = band(rshift(op, 16), 31)
1113 if altname and rn == rm then
1114 local n = #operands
1115 operands[n] = nil
1116 last = operands[n-1]
1117 name = altname
1118 end
1119 elseif p == "5" then
1120 x = band(rshift(op, 16), 31)
1121 elseif p == "S" then
1122 x = band(rshift(op, 10), 63)
1123 if x == 0 then x = nil
1124 else x = map_shift[band(rshift(op, 22), 3)].." #"..x end
1125 elseif p == "X" then
1126 local opt = band(rshift(op, 13), 7)
1127 -- Width specifier <R>.
1128 if opt ~= 3 and opt ~= 7 then
1129 last = map_regs.w[band(rshift(op, 16), 31)]
1130 operands[#operands] = last
1131 end
1132 x = band(rshift(op, 10), 7)
1133 -- Extension.
1134 if opt == 2 + band(rshift(op, 31), 1) and
1135 band(rshift(op, second0 and 5 or 0), 31) == 31 then
1136 if x == 0 then x = nil
1137 else x = "lsl #"..x end
1138 else
1139 if x == 0 then x = map_extend[band(rshift(op, 13), 7)]
1140 else x = map_extend[band(rshift(op, 13), 7)].." #"..x end
1141 end
1142 elseif p == "R" then
1143 x = band(rshift(op,21), 3)
1144 if x == 0 then x = nil
1145 else x = "lsl #"..x*16 end
1146 elseif p == "z" then
1147 local n = #operands
1148 if operands[n] == "sp" then operands[n] = "xzr"
1149 elseif operands[n] == "wsp" then operands[n] = "wzr"
1150 end
1151 elseif p == "Z" then
1152 x = 0
1153 elseif p == "F" then
1154 x = parse_fpimm8(op)
1155 elseif p == "G" then
1156 x = "#0x"..decode_fpmovi(op)
1157 elseif p == "g" or p == "f" or p == "x" or p == "w" or
1158 p == "d" or p == "s" then
1159 -- These are handled in D/N/M/A.
1160 elseif p == "0" then
1161 if last == "sp" or last == "wsp" then
1162 local n = #operands
1163 operands[n] = nil
1164 last = operands[n-1]
1165 if altname then
1166 local a1, a2 = match(altname, "([^|]*)|(.*)")
1167 if not a1 then
1168 name = altname
1169 elseif second0 then
1170 name, altname = a2, a1
1171 else
1172 name, altname = a1, a2
1173 end
1174 end
1175 end
1176 second0 = true
1177 elseif p == " " then
1178 operands[#operands+1] = pat:match(" (.*)")
1179 break
1180 else
1181 assert(false)
1182 end
1183 if x then
1184 last = x
1185 if type(x) == "number" then x = "#"..x end
1186 operands[#operands+1] = x
1187 end
1188 end
1189
1190 return putop(ctx, name..suffix, operands)
1191end
1192
1193------------------------------------------------------------------------------
1194
1195-- Disassemble a block of code.
1196local function disass_block(ctx, ofs, len)
1197 if not ofs then ofs = 0 end
1198 local stop = len and ofs+len or #ctx.code
1199 ctx.pos = ofs
1200 ctx.rel = nil
1201 while ctx.pos < stop do disass_ins(ctx) end
1202end
1203
1204-- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
1205local function create(code, addr, out)
1206 local ctx = {}
1207 ctx.code = code
1208 ctx.addr = addr or 0
1209 ctx.out = out or io.write
1210 ctx.symtab = {}
1211 ctx.disass = disass_block
1212 ctx.hexdump = 8
1213 return ctx
1214end
1215
1216-- Simple API: disassemble code (a string) at address and output via out.
1217local function disass(code, addr, out)
1218 create(code, addr, out):disass()
1219end
1220
1221-- Return register name for RID.
1222local function regname(r)
1223 if r < 32 then return map_regs.x[r] end
1224 return map_regs.d[r-32]
1225end
1226
1227-- Public module functions.
1228return {
1229 create = create,
1230 disass = disass,
1231 regname = regname
1232}
1233