1 ----------------------------------------------------------------------------
2 -- LuaJIT x86/x64 disassembler module.
4 -- Copyright (C) 2005-2016 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 -- Sending small code snippets to an external disassembler and mixing the
10 -- output with our own stuff was too fragile. So I had to bite the bullet
11 -- and write yet another x86 disassembler. Oh well ...
13 -- The output format is very similar to what ndisasm generates. But it has
14 -- been developed independently by looking at the opcode tables from the
15 -- Intel and AMD manuals. The supported instruction set is quite extensive
16 -- and reflects what a current generation Intel or AMD CPU implements in
17 -- 32 bit and 64 bit mode. Yes, this includes MMX, SSE, SSE2, SSE3, SSSE3,
18 -- SSE4.1, SSE4.2, SSE4a, AVX, AVX2 and even privileged and hypervisor
19 -- (VMX/SVM) instructions.
22 -- * The (useless) a16 prefix, 3DNow and pre-586 opcodes are unsupported.
23 -- * No attempt at optimization has been made -- it's fast enough for my needs.
24 ------------------------------------------------------------------------------
27 local sub
, byte
, format = string.sub
, string.byte
, string.format
28 local match
, gmatch
, gsub = string.match
, string.gmatch
, string.gsub
29 local lower
, rep
= string.lower
, string.rep
30 local bit
= require("bit")
31 local tohex
= bit
.tohex
33 -- Map for 1st opcode byte in 32 bit mode. Ugly? Well ... read on.
36 [0]="addBmr","addVmr","addBrm","addVrm","addBai","addVai","push es","pop es",
37 "orBmr","orVmr","orBrm","orVrm","orBai","orVai","push cs","opc2*",
39 "adcBmr","adcVmr","adcBrm","adcVrm","adcBai","adcVai","push ss","pop ss",
40 "sbbBmr","sbbVmr","sbbBrm","sbbVrm","sbbBai","sbbVai","push ds","pop ds",
42 "andBmr","andVmr","andBrm","andVrm","andBai","andVai","es:seg","daa",
43 "subBmr","subVmr","subBrm","subVrm","subBai","subVai","cs:seg","das",
45 "xorBmr","xorVmr","xorBrm","xorVrm","xorBai","xorVai","ss:seg","aaa",
46 "cmpBmr","cmpVmr","cmpBrm","cmpVrm","cmpBai","cmpVai","ds:seg","aas",
48 "incVR","incVR","incVR","incVR","incVR","incVR","incVR","incVR",
49 "decVR","decVR","decVR","decVR","decVR","decVR","decVR","decVR",
51 "pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR","pushUR",
52 "popUR","popUR","popUR","popUR","popUR","popUR","popUR","popUR",
54 "sz*pushaw,pusha","sz*popaw,popa","boundVrm","arplWmr",
55 "fs:seg","gs:seg","o16:","a16",
56 "pushUi","imulVrmi","pushBs","imulVrms",
57 "insb","insVS","outsb","outsVS",
59 "joBj","jnoBj","jbBj","jnbBj","jzBj","jnzBj","jbeBj","jaBj",
60 "jsBj","jnsBj","jpeBj","jpoBj","jlBj","jgeBj","jleBj","jgBj",
62 "arith!Bmi","arith!Vmi","arith!Bmi","arith!Vms",
63 "testBmr","testVmr","xchgBrm","xchgVrm",
64 "movBmr","movVmr","movBrm","movVrm",
65 "movVmg","leaVrm","movWgm","popUm",
67 "nop*xchgVaR|pause|xchgWaR|repne nop","xchgVaR","xchgVaR","xchgVaR",
68 "xchgVaR","xchgVaR","xchgVaR","xchgVaR",
69 "sz*cbw,cwde,cdqe","sz*cwd,cdq,cqo","call farViw","wait",
70 "sz*pushfw,pushf","sz*popfw,popf","sahf","lahf",
72 "movBao","movVao","movBoa","movVoa",
73 "movsb","movsVS","cmpsb","cmpsVS",
74 "testBai","testVai","stosb","stosVS",
75 "lodsb","lodsVS","scasb","scasVS",
77 "movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi","movBRi",
78 "movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI","movVRI",
80 "shift!Bmu","shift!Vmu","retBw","ret","vex*3$lesVrm","vex*2$ldsVrm","movBmi","movVmi",
81 "enterBwu","leave","retfBw","retf","int3","intBu","into","iretVS",
83 "shift!Bm1","shift!Vm1","shift!Bmc","shift!Vmc","aamBu","aadBu","salc","xlatb",
84 "fp*0","fp*1","fp*2","fp*3","fp*4","fp*5","fp*6","fp*7",
86 "loopneBj","loopeBj","loopBj","sz*jcxzBj,jecxzBj,jrcxzBj",
87 "inBau","inVau","outBua","outVua",
88 "callVj","jmpVj","jmp farViw","jmpBj","inBad","inVad","outBda","outVda",
90 "lock:","int1","repne:rep","rep:","hlt","cmc","testb!Bm","testv!Vm",
91 "clc","stc","cli","sti","cld","std","incb!Bm","incd!Vm",
93 assert(#map_opc1_32
== 255)
95 -- Map for 1st opcode byte in 64 bit mode (overrides only).
96 local map_opc1_64
= setmetatable({
97 [0x06]=false, [0x07]=false, [0x0e]=false,
98 [0x16]=false, [0x17]=false, [0x1e]=false, [0x1f]=false,
99 [0x27]=false, [0x2f]=false, [0x37]=false, [0x3f]=false,
100 [0x60]=false, [0x61]=false, [0x62]=false, [0x63]="movsxdVrDmt", [0x67]="a32:",
101 [0x40]="rex*", [0x41]="rex*b", [0x42]="rex*x", [0x43]="rex*xb",
102 [0x44]="rex*r", [0x45]="rex*rb", [0x46]="rex*rx", [0x47]="rex*rxb",
103 [0x48]="rex*w", [0x49]="rex*wb", [0x4a]="rex*wx", [0x4b]="rex*wxb",
104 [0x4c]="rex*wr", [0x4d]="rex*wrb", [0x4e]="rex*wrx", [0x4f]="rex*wrxb",
105 [0x82]=false, [0x9a]=false, [0xc4]="vex*3", [0xc5]="vex*2", [0xce]=false,
106 [0xd4]=false, [0xd5]=false, [0xd6]=false, [0xea]=false,
107 }, { __index
= map_opc1_32
})
109 -- Map for 2nd opcode byte (0F xx). True CISC hell. Hey, I told you.
110 -- Prefix dependent MMX/SSE opcodes: (none)|rep|o16|repne, -|F3|66|F2
113 [0]="sldt!Dmp","sgdt!Ump","larVrm","lslVrm",nil,"syscall","clts","sysret",
114 "invd","wbinvd",nil,"ud1",nil,"$prefetch!Bm","femms","3dnowMrmu",
116 "movupsXrm|movssXrvm|movupdXrm|movsdXrvm",
117 "movupsXmr|movssXmvr|movupdXmr|movsdXmvr",
118 "movhlpsXrm$movlpsXrm|movsldupXrm|movlpdXrm|movddupXrm",
119 "movlpsXmr||movlpdXmr",
120 "unpcklpsXrvm||unpcklpdXrvm",
121 "unpckhpsXrvm||unpckhpdXrvm",
122 "movlhpsXrm$movhpsXrm|movshdupXrm|movhpdXrm",
123 "movhpsXmr||movhpdXmr",
124 "$prefetcht!Bm","hintnopVm","hintnopVm","hintnopVm",
125 "hintnopVm","hintnopVm","hintnopVm","hintnopVm",
127 "movUmx$","movUmy$","movUxm$","movUym$","movUmz$",nil,"movUzm$",nil,
128 "movapsXrm||movapdXrm",
129 "movapsXmr||movapdXmr",
130 "cvtpi2psXrMm|cvtsi2ssXrvVmt|cvtpi2pdXrMm|cvtsi2sdXrvVmt",
131 "movntpsXmr|movntssXmr|movntpdXmr|movntsdXmr",
132 "cvttps2piMrXm|cvttss2siVrXm|cvttpd2piMrXm|cvttsd2siVrXm",
133 "cvtps2piMrXm|cvtss2siVrXm|cvtpd2piMrXm|cvtsd2siVrXm",
134 "ucomissXrm||ucomisdXrm",
135 "comissXrm||comisdXrm",
137 "wrmsr","rdtsc","rdmsr","rdpmc","sysenter","sysexit",nil,"getsec",
138 "opc3*38",nil,"opc3*3a",nil,nil,nil,nil,nil,
140 "cmovoVrm","cmovnoVrm","cmovbVrm","cmovnbVrm",
141 "cmovzVrm","cmovnzVrm","cmovbeVrm","cmovaVrm",
142 "cmovsVrm","cmovnsVrm","cmovpeVrm","cmovpoVrm",
143 "cmovlVrm","cmovgeVrm","cmovleVrm","cmovgVrm",
145 "movmskpsVrXm$||movmskpdVrXm$","sqrtpsXrm|sqrtssXrm|sqrtpdXrm|sqrtsdXrm",
146 "rsqrtpsXrm|rsqrtssXrvm","rcppsXrm|rcpssXrvm",
147 "andpsXrvm||andpdXrvm","andnpsXrvm||andnpdXrvm",
148 "orpsXrvm||orpdXrvm","xorpsXrvm||xorpdXrvm",
149 "addpsXrvm|addssXrvm|addpdXrvm|addsdXrvm","mulpsXrvm|mulssXrvm|mulpdXrvm|mulsdXrvm",
150 "cvtps2pdXrm|cvtss2sdXrvm|cvtpd2psXrm|cvtsd2ssXrvm",
151 "cvtdq2psXrm|cvttps2dqXrm|cvtps2dqXrm",
152 "subpsXrvm|subssXrvm|subpdXrvm|subsdXrvm","minpsXrvm|minssXrvm|minpdXrvm|minsdXrvm",
153 "divpsXrvm|divssXrvm|divpdXrvm|divsdXrvm","maxpsXrvm|maxssXrvm|maxpdXrvm|maxsdXrvm",
155 "punpcklbwPrvm","punpcklwdPrvm","punpckldqPrvm","packsswbPrvm",
156 "pcmpgtbPrvm","pcmpgtwPrvm","pcmpgtdPrvm","packuswbPrvm",
157 "punpckhbwPrvm","punpckhwdPrvm","punpckhdqPrvm","packssdwPrvm",
158 "||punpcklqdqXrvm","||punpckhqdqXrvm",
159 "movPrVSm","movqMrm|movdquXrm|movdqaXrm",
161 "pshufwMrmu|pshufhwXrmu|pshufdXrmu|pshuflwXrmu","pshiftw!Pvmu",
162 "pshiftd!Pvmu","pshiftq!Mvmu||pshiftdq!Xvmu",
163 "pcmpeqbPrvm","pcmpeqwPrvm","pcmpeqdPrvm","emms*|",
164 "vmreadUmr||extrqXmuu$|insertqXrmuu$","vmwriteUrm||extrqXrm$|insertqXrm$",
166 "||haddpdXrvm|haddpsXrvm","||hsubpdXrvm|hsubpsXrvm",
167 "movVSmMr|movqXrm|movVSmXr","movqMmr|movdquXmr|movdqaXmr",
169 "joVj","jnoVj","jbVj","jnbVj","jzVj","jnzVj","jbeVj","jaVj",
170 "jsVj","jnsVj","jpeVj","jpoVj","jlVj","jgeVj","jleVj","jgVj",
172 "setoBm","setnoBm","setbBm","setnbBm","setzBm","setnzBm","setbeBm","setaBm",
173 "setsBm","setnsBm","setpeBm","setpoBm","setlBm","setgeBm","setleBm","setgBm",
175 "push fs","pop fs","cpuid","btVmr","shldVmru","shldVmrc",nil,nil,
176 "push gs","pop gs","rsm","btsVmr","shrdVmru","shrdVmrc","fxsave!Dmp","imulVrm",
178 "cmpxchgBmr","cmpxchgVmr","$lssVrm","btrVmr",
179 "$lfsVrm","$lgsVrm","movzxVrBmt","movzxVrWmt",
180 "|popcntVrm","ud2Dp","bt!Vmu","btcVmr",
181 "bsfVrm","bsrVrm|lzcntVrm|bsrWrm","movsxVrBmt","movsxVrWmt",
184 "cmppsXrvmu|cmpssXrvmu|cmppdXrvmu|cmpsdXrvmu","$movntiVmr|",
185 "pinsrwPrvWmu","pextrwDrPmu",
186 "shufpsXrvmu||shufpdXrvmu","$cmpxchg!Qmp",
187 "bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR","bswapVR",
189 "||addsubpdXrvm|addsubpsXrvm","psrlwPrvm","psrldPrvm","psrlqPrvm",
190 "paddqPrvm","pmullwPrvm",
191 "|movq2dqXrMm|movqXmr|movdq2qMrXm$","pmovmskbVrMm||pmovmskbVrXm",
192 "psubusbPrvm","psubuswPrvm","pminubPrvm","pandPrvm",
193 "paddusbPrvm","padduswPrvm","pmaxubPrvm","pandnPrvm",
195 "pavgbPrvm","psrawPrvm","psradPrvm","pavgwPrvm",
196 "pmulhuwPrvm","pmulhwPrvm",
197 "|cvtdq2pdXrm|cvttpd2dqXrm|cvtpd2dqXrm","$movntqMmr||$movntdqXmr",
198 "psubsbPrvm","psubswPrvm","pminswPrvm","porPrvm",
199 "paddsbPrvm","paddswPrvm","pmaxswPrvm","pxorPrvm",
201 "|||lddquXrm","psllwPrvm","pslldPrvm","psllqPrvm",
202 "pmuludqPrvm","pmaddwdPrvm","psadbwPrvm","maskmovqMrm||maskmovdquXrm$",
203 "psubbPrvm","psubwPrvm","psubdPrvm","psubqPrvm",
204 "paddbPrvm","paddwPrvm","padddPrvm","ud",
206 assert(map_opc2
[255] == "ud")
208 -- Map for three-byte opcodes. Can't wait for their next invention.
210 ["38"] = { -- [66] 0f 38 xx
212 [0]="pshufbPrvm","phaddwPrvm","phadddPrvm","phaddswPrvm",
213 "pmaddubswPrvm","phsubwPrvm","phsubdPrvm","phsubswPrvm",
214 "psignbPrvm","psignwPrvm","psigndPrvm","pmulhrswPrvm",
215 "||permilpsXrvm","||permilpdXrvm",nil,nil,
217 "||pblendvbXrma",nil,nil,nil,
218 "||blendvpsXrma","||blendvpdXrma","||permpsXrvm","||ptestXrm",
219 "||broadcastssXrm","||broadcastsdXrm","||broadcastf128XrlXm",nil,
220 "pabsbPrm","pabswPrm","pabsdPrm",nil,
222 "||pmovsxbwXrm","||pmovsxbdXrm","||pmovsxbqXrm","||pmovsxwdXrm",
223 "||pmovsxwqXrm","||pmovsxdqXrm",nil,nil,
224 "||pmuldqXrvm","||pcmpeqqXrvm","||$movntdqaXrm","||packusdwXrvm",
225 "||maskmovpsXrvm","||maskmovpdXrvm","||maskmovpsXmvr","||maskmovpdXmvr",
227 "||pmovzxbwXrm","||pmovzxbdXrm","||pmovzxbqXrm","||pmovzxwdXrm",
228 "||pmovzxwqXrm","||pmovzxdqXrm","||permdXrvm","||pcmpgtqXrvm",
229 "||pminsbXrvm","||pminsdXrvm","||pminuwXrvm","||pminudXrvm",
230 "||pmaxsbXrvm","||pmaxsdXrvm","||pmaxuwXrvm","||pmaxudXrvm",
232 "||pmulddXrvm","||phminposuwXrm",nil,nil,
233 nil,"||psrlvVSXrvm","||psravdXrvm","||psllvVSXrvm",
235 [0x58] = "||pbroadcastdXrlXm",[0x59] = "||pbroadcastqXrlXm",
236 [0x5a] = "||broadcasti128XrlXm",
238 [0x78] = "||pbroadcastbXrlXm",[0x79] = "||pbroadcastwXrlXm",
240 [0x8c] = "||pmaskmovXrvVSm",
241 [0x8e] = "||pmaskmovVSmXvr",
243 [0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm",
244 [0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm",
246 [0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
249 ["3a"] = { -- [66] 0f 3a xx
251 [0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil,
252 "||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil,
253 "||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu",
254 "||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu",
257 "||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
258 "||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil,
261 "||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil,
263 [0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru",
265 [0x40] = "||dppsXrvmu",
266 [0x41] = "||dppdXrvmu",
267 [0x42] = "||mpsadbwXrvmu",
268 [0x44] = "||pclmulqdqXrvmu",
269 [0x46] = "||perm2i128Xrvmu",
270 [0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb",
271 [0x4c] = "||pblendvbXrvmb",
273 [0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
274 [0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
275 [0xdf] = "||aeskeygenassistXrmu",
279 -- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
281 [0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
282 [0xc8]="monitor",[0xc9]="mwait",
283 [0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
284 [0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
285 [0xf8]="swapgs",[0xf9]="rdtscp",
288 -- Map for FP opcodes. And you thought stack machines are simple?
290 -- D8-DF 00-BF: opcodes with a memory operand.
292 [0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
293 "fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
295 "fiaddDm","fimulDm","ficomDm","ficompDm",
296 "fisubDm","fisubrDm","fidivDm","fidivrDm",
298 "fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
300 "faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
302 "fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
304 "fiaddWm","fimulWm","ficomWm","ficompWm",
305 "fisubWm","fisubrWm","fidivWm","fidivrWm",
307 "fildWm","fisttpWm","fistWm","fistpWm",
308 "fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
309 -- xx C0-FF: opcodes with a pseudo-register operand.
311 "faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
313 "fldFf","fxchFf",{"fnop"},nil,
314 {"fchs","fabs",nil,nil,"ftst","fxam"},
315 {"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
316 {"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
317 {"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
319 "fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
321 "fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
322 {nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
324 "fadd toFf","fmul toFf",nil,nil,
325 "fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
327 "ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
329 "faddpFf","fmulpFf",nil,{nil,"fcompp"},
330 "fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
332 nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
334 assert(map_opcfp
[126] == "fcomipFf")
336 -- Map for opcode groups. The subkey is sp from the ModRM byte.
337 local map_opcgroup
= {
338 arith
= { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
339 shift
= { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
340 testb
= { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
341 testv
= { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
342 incb
= { "inc", "dec" },
343 incd
= { "inc", "dec", "callUmp", "$call farDmp",
344 "jmpUmp", "$jmp farDmp", "pushUm" },
345 sldt
= { "sldt", "str", "lldt", "ltr", "verr", "verw" },
346 sgdt
= { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
347 "smsw", nil, "lmsw", "vm*$invlpg" },
348 bt
= { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
349 cmpxchg
= { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
350 nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
351 pshiftw
= { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
352 pshiftd
= { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
353 pshiftq
= { nil, nil, "psrlq", nil, nil, nil, "psllq" },
354 pshiftdq
= { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
355 fxsave
= { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
356 nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
357 prefetch
= { "prefetch", "prefetchw" },
358 prefetcht
= { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
361 ------------------------------------------------------------------------------
363 -- Maps for register names.
365 B
= { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
366 "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
367 B64
= { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
368 "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
369 W
= { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
370 "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
371 D
= { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
372 "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
373 Q
= { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
374 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
375 M
= { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
376 "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
377 X
= { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
378 "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
379 Y
= { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
380 "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" },
382 local map_segregs
= { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
384 -- Maps for size names.
386 B
= 1, W
= 2, D
= 4, Q
= 8, M
= 8, X
= 16, Y
= 32,
388 local map_sz2prefix
= {
389 B
= "byte", W
= "word", D
= "dword",
391 M
= "qword", X
= "xword", Y
= "yword",
392 F
= "dword", G
= "qword", -- No need for sizes/register names for these two.
395 ------------------------------------------------------------------------------
397 -- Output a nicely formatted line with an opcode and operands.
398 local function putop(ctx
, text
, operands
)
399 local code
, pos
, hex
= ctx
.code
, ctx
.pos
, ""
400 local hmax
= ctx
.hexdump
402 for i
=ctx
.start
,pos
-1 do
403 hex
= hex
..format("%02X", byte(code
, i
, i
))
405 if #hex
> hmax
then hex
= sub(hex
, 1, hmax
)..". "
406 else hex
= hex
..rep(" ", hmax
-#hex
+2) end
408 if operands
then text
= text
.." "..operands
end
409 if ctx
.o16
then text
= "o16 "..text
; ctx
.o16
= false end
410 if ctx
.a32
then text
= "a32 "..text
; ctx
.a32
= false end
411 if ctx
.rep
then text
= ctx
.rep
.." "..text
; ctx
.rep
= false end
413 local t
= (ctx
.rexw
and "w" or "")..(ctx
.rexr
and "r" or "")..
414 (ctx
.rexx
and "x" or "")..(ctx
.rexb
and "b" or "")..
415 (ctx
.vexl
and "l" or "")
416 if ctx
.vexv
and ctx
.vexv
~= 0 then t
= t
.."v"..ctx
.vexv
end
417 if t
~= "" then text
= ctx
.rex
.."."..t
.." "..text
418 elseif ctx
.rex
== "vex" then text
= "v"..text
end
419 ctx
.rexw
= false; ctx
.rexr
= false; ctx
.rexx
= false; ctx
.rexb
= false
420 ctx
.rex
= false; ctx
.vexl
= false; ctx
.vexv
= false
423 local text2
, n
= gsub(text
, "%[", "["..ctx
.seg
..":")
424 if n
== 0 then text
= ctx
.seg
.." "..text
else text
= text2
end
427 if ctx
.lock
then text
= "lock "..text
; ctx
.lock
= false end
430 local sym
= ctx
.symtab
[imm
]
431 if sym
then text
= text
.."\t->"..sym
end
433 ctx
.out(format("%08x %s%s\n", ctx
.addr
+ctx
.start
, hex
, text
))
440 -- Clear all prefix flags.
441 local function clearprefixes(ctx
)
442 ctx
.o16
= false; ctx
.seg
= false; ctx
.lock
= false; ctx
.rep
= false
443 ctx
.rexw
= false; ctx
.rexr
= false; ctx
.rexx
= false; ctx
.rexb
= false
444 ctx
.rex
= false; ctx
.a32
= false; ctx
.vexl
= false
447 -- Fallback for incomplete opcodes at the end.
448 local function incomplete(ctx
)
451 return putop(ctx
, "(incomplete)")
454 -- Fallback for unknown opcodes.
455 local function unknown(ctx
)
457 return putop(ctx
, "(unknown)")
460 -- Return an immediate of the specified size.
461 local function getimm(ctx
, pos
, n
)
462 if pos
+n
-1 > ctx
.stop
then return incomplete(ctx
) end
463 local code
= ctx
.code
465 local b1
= byte(code
, pos
, pos
)
468 local b1
, b2
= byte(code
, pos
, pos
+1)
471 local b1
, b2
, b3
, b4
= byte(code
, pos
, pos
+3)
472 local imm
= b1
+b2
*256+b3
*65536+b4
*16777216
478 -- Process pattern string and generate the operands.
479 local function putpat(ctx
, name
, pat
)
480 local operands
, regs
, sz
, mode
, sp
, rm
, sc
, rx
, sdisp
481 local code
, pos
, stop
, vexl
= ctx
.code
, ctx
.pos
, ctx
.stop
, ctx
.vexl
483 -- Chars used: 1DFGIMPQRSTUVWXYabcdfgijlmoprstuvwxyz
484 for p
in gmatch(pat
, ".") do
486 if p
== "V" or p
== "U" then
487 if ctx
.rexw
then sz
= "Q"; ctx
.rexw
= false
488 elseif ctx
.o16
then sz
= "W"; ctx
.o16
= false
489 elseif p
== "U" and ctx
.x64
then sz
= "Q"
493 if ctx
.rexw
then sz
= "Q"; ctx
.rexw
= false else sz
= "D" end
497 regs
= ctx
.rex
and map_regs
.B64
or map_regs
.B
498 elseif match(p
, "[WDQMXYFG]") then
500 if sz
== "X" and vexl
then sz
= "Y"; ctx
.vexl
= false end
503 sz
= ctx
.o16
and "X" or "M"; ctx
.o16
= false
504 if sz
== "X" and vexl
then sz
= "Y"; ctx
.vexl
= false end
507 name
= name
..lower(sz
)
509 local imm
= getimm(ctx
, pos
, 1); if not imm
then return end
510 x
= imm
<= 127 and format("+0x%02x", imm
)
511 or format("-0x%02x", 256-imm
)
514 local imm
= getimm(ctx
, pos
, 1); if not imm
then return end
515 x
= format("0x%02x", imm
)
518 local imm
= getimm(ctx
, pos
, 1); if not imm
then return end
522 local imm
= getimm(ctx
, pos
, 2); if not imm
then return end
523 x
= format("0x%x", imm
)
525 elseif p
== "o" then -- [offset]
527 local imm1
= getimm(ctx
, pos
, 4); if not imm1
then return end
528 local imm2
= getimm(ctx
, pos
+4, 4); if not imm2
then return end
529 x
= format("[0x%08x%08x]", imm2
, imm1
)
532 local imm
= getimm(ctx
, pos
, 4); if not imm
then return end
533 x
= format("[0x%08x]", imm
)
536 elseif p
== "i" or p
== "I" then
537 local n
= map_sz2n
[sz
]
538 if n
== 8 and ctx
.x64
and p
== "I" then
539 local imm1
= getimm(ctx
, pos
, 4); if not imm1
then return end
540 local imm2
= getimm(ctx
, pos
+4, 4); if not imm2
then return end
541 x
= format("0x%08x%08x", imm2
, imm1
)
543 if n
== 8 then n
= 4 end
544 local imm
= getimm(ctx
, pos
, n
); if not imm
then return end
545 if sz
== "Q" and (imm
< 0 or imm
> 0x7fffffff) then
546 imm
= (0xffffffff+1)-imm
547 x
= format(imm
> 65535 and "-0x%08x" or "-0x%x", imm
)
549 x
= format(imm
> 65535 and "0x%08x" or "0x%x", imm
)
554 local n
= map_sz2n
[sz
]
555 if n
== 8 then n
= 4 end
556 local imm
= getimm(ctx
, pos
, n
); if not imm
then return end
557 if sz
== "B" and imm
> 127 then imm
= imm
-256
558 elseif imm
> 2147483647 then imm
= imm
-4294967296 end
560 imm
= imm
+ pos
+ ctx
.addr
561 if imm
> 4294967295 and not ctx
.x64
then imm
= imm
-4294967296 end
564 x
= format("word 0x%04x", imm
%65536)
566 local lo
= imm
% 0x1000000
567 x
= format("0x%02x%06x", (imm
-lo
) / 0x1000000, lo
)
572 local r
= byte(code
, pos
-1, pos
-1)%8
573 if ctx
.rexb
then r
= r
+ 8; ctx
.rexb
= false end
575 elseif p
== "a" then x
= regs
[1]
576 elseif p
== "c" then x
= "cl"
577 elseif p
== "d" then x
= "dx"
578 elseif p
== "1" then x
= "1"
583 if pos
> stop
then return incomplete(ctx
) end
584 mode
= byte(code
, pos
, pos
)
587 rm
= mode
%8; mode
= (mode
-rm
)/8
588 sp
= mode
%8; mode
= (mode
-sp
)/8
592 if pos
> stop
then return incomplete(ctx
) end
593 sc
= byte(code
, pos
, pos
)
595 rm
= sc
%8; sc
= (sc
-rm
)/8
596 rx
= sc
%8; sc
= (sc
-rx
)/8
597 if ctx
.rexx
then rx
= rx
+ 8; ctx
.rexx
= false end
598 if rx
== 4 then rx
= nil end
600 if mode
> 0 or rm
== 5 then
602 if dsz
~= 1 then dsz
= 4 end
603 local disp
= getimm(ctx
, pos
, dsz
); if not disp
then return end
604 if mode
== 0 then rm
= nil end
605 if rm
or rx
or (not sc
and ctx
.x64
and not ctx
.a32
) then
606 if dsz
== 1 and disp
> 127 then
607 sdisp
= format("-0x%x", 256-disp
)
608 elseif disp
>= 0 and disp
<= 0x7fffffff then
609 sdisp
= format("+0x%x", disp
)
611 sdisp
= format("-0x%x", (0xffffffff+1)-disp
)
614 sdisp
= format(ctx
.x64
and not ctx
.a32
and
615 not (disp
>= 0 and disp
<= 0x7fffffff)
616 and "0xffffffff%08x" or "0x%08x", disp
)
621 if rm
and ctx
.rexb
then rm
= rm
+ 8; ctx
.rexb
= false end
622 if ctx
.rexr
then sp
= sp
+ 8; ctx
.rexr
= false end
625 if mode
== 3 then x
= regs
[rm
+1]
627 local aregs
= ctx
.a32
and map_regs
.D
or ctx
.aregs
628 local srm
, srx
= "", ""
629 if rm
then srm
= aregs
[rm
+1]
630 elseif not sc
and ctx
.x64
and not ctx
.a32
then srm
= "rip" end
633 if rm
then srm
= srm
.."+" end
635 if sc
> 0 then srx
= srx
.."*"..(2^sc
) end
637 x
= format("[%s%s%s]", srm
, srx
, sdisp
)
640 (not match(pat
, "[aRrgp]") or match(pat
, "t")) then -- Yuck.
641 x
= map_sz2prefix
[sz
].." "..x
643 elseif p
== "r" then x
= regs
[sp
+1]
644 elseif p
== "g" then x
= map_segregs
[sp
+1]
645 elseif p
== "p" then -- Suppress prefix.
646 elseif p
== "f" then x
= "st"..rm
648 if sp
== 0 and ctx
.lock
and not ctx
.x64
then
649 x
= "CR8"; ctx
.lock
= false
655 x
= regs
[ctx
.vexv
+1]; ctx
.vexv
= false
657 elseif p
== "y" then x
= "DR"..sp
658 elseif p
== "z" then x
= "TR"..sp
659 elseif p
== "l" then vexl
= false
662 error("bad pattern `"..pat
.."'")
665 if x
then operands
= operands
and operands
..", "..x
or x
end
668 return putop(ctx
, name
, operands
)
671 -- Forward declaration.
674 -- Fetch and cache MRM byte.
675 local function getmrm(ctx
)
679 if pos
> ctx
.stop
then return nil end
680 mrm
= byte(ctx
.code
, pos
, pos
)
687 -- Dispatch to handler depending on pattern.
688 local function dispatch(ctx
, opat
, patgrp
)
689 if not opat
then return unknown(ctx
) end
690 if match(opat
, "%|") then -- MMX/SSE variants depending on prefix.
693 p
= ctx
.rep
=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
695 elseif ctx
.o16
then p
= "%|[^%|]*%|([^%|]*)"; ctx
.o16
= false
696 else p
= "^[^%|]*" end
697 opat
= match(opat
, p
)
698 if not opat
then return unknown(ctx
) end
699 -- ctx.rep = false; ctx.o16 = false
700 --XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
701 --XXX remove in branches?
703 if match(opat
, "%$") then -- reg$mem variants.
704 local mrm
= getmrm(ctx
); if not mrm
then return incomplete(ctx
) end
705 opat
= match(opat
, mrm
>= 192 and "^[^%$]*" or "%$(.*)")
706 if opat
== "" then return unknown(ctx
) end
708 if opat
== "" then return unknown(ctx
) end
709 local name
, pat
= match(opat
, "^([a-z0-9 ]*)(.*)")
710 if pat
== "" and patgrp
then pat
= patgrp
end
711 return map_act
[sub(pat
, 1, 1)](ctx
, name
, pat
)
714 -- Get a pattern from an opcode map and dispatch to handler.
715 local function dispatchmap(ctx
, opcmap
)
717 local opat
= opcmap
[byte(ctx
.code
, pos
, pos
)]
720 return dispatch(ctx
, opat
)
723 -- Map for action codes. The key is the first char after the name.
725 -- Simple opcodes without operands.
726 [""] = function(ctx
, name
, pat
)
727 return putop(ctx
, name
)
730 -- Operand size chars fall right through.
731 B
= putpat
, W
= putpat
, D
= putpat
, Q
= putpat
,
732 V
= putpat
, U
= putpat
, T
= putpat
,
733 M
= putpat
, X
= putpat
, P
= putpat
,
734 F
= putpat
, G
= putpat
, Y
= putpat
,
737 [":"] = function(ctx
, name
, pat
)
738 ctx
[pat
== ":" and name
or sub(pat
, 2)] = name
739 if ctx
.pos
- ctx
.start
> 5 then return unknown(ctx
) end -- Limit #prefixes.
742 -- Chain to special handler specified by name.
743 ["*"] = function(ctx
, name
, pat
)
744 return map_act
[name
](ctx
, name
, sub(pat
, 2))
747 -- Use named subtable for opcode group.
748 ["!"] = function(ctx
, name
, pat
)
749 local mrm
= getmrm(ctx
); if not mrm
then return incomplete(ctx
) end
750 return dispatch(ctx
, map_opcgroup
[name
][((mrm
-(mrm
%8))/8)%8+1], sub(pat
, 2))
753 -- o16,o32[,o64] variants.
754 sz
= function(ctx
, name
, pat
)
755 if ctx
.o16
then ctx
.o16
= false
757 pat
= match(pat
, ",(.*)")
759 local p
= match(pat
, ",(.*)")
760 if p
then pat
= p
; ctx
.rexw
= false end
763 pat
= match(pat
, "^[^,]*")
764 return dispatch(ctx
, pat
)
767 -- Two-byte opcode dispatch.
768 opc2
= function(ctx
, name
, pat
)
769 return dispatchmap(ctx
, map_opc2
)
772 -- Three-byte opcode dispatch.
773 opc3
= function(ctx
, name
, pat
)
774 return dispatchmap(ctx
, map_opc3
[pat
])
778 vm
= function(ctx
, name
, pat
)
779 return dispatch(ctx
, map_opcvm
[ctx
.mrm
])
782 -- Floating point opcode dispatch.
783 fp
= function(ctx
, name
, pat
)
784 local mrm
= getmrm(ctx
); if not mrm
then return incomplete(ctx
) end
786 local idx
= pat
*8 + ((mrm
-rm
)/8)%8
787 if mrm
>= 192 then idx
= idx
+ 64 end
788 local opat
= map_opcfp
[idx
]
789 if type(opat
) == "table" then opat
= opat
[rm
+1] end
790 return dispatch(ctx
, opat
)
794 rex
= function(ctx
, name
, pat
)
795 if ctx
.rex
then return unknown(ctx
) end -- Only 1 REX or VEX prefix allowed.
796 for p
in gmatch(pat
, ".") do ctx
["rex"..p
] = true end
801 vex
= function(ctx
, name
, pat
)
802 if ctx
.rex
then return unknown(ctx
) end -- Only 1 REX or VEX prefix allowed.
809 local b
= byte(ctx
.code
, pos
, pos
)
810 if not b
then return incomplete(ctx
) end
812 if b
< 128 then ctx
.rexr
= true end
815 m
= b
%32; b
= (b
-m
)/32
816 local nb
= b
%2; b
= (b
-nb
)/2
817 if nb
== 0 then ctx
.rexb
= true end
818 local nx
= b
%2; b
= (b
-nx
)/2
819 if nx
== 0 then ctx
.rexx
= true end
820 b
= byte(ctx
.code
, pos
, pos
)
821 if not b
then return incomplete(ctx
) end
823 if b
>= 128 then ctx
.rexw
= true end
827 if m
== 1 then map
= map_opc2
828 elseif m
== 2 then map
= map_opc3
["38"]
829 elseif m
== 3 then map
= map_opc3
["3a"]
830 else return unknown(ctx
) end
831 local p
= b
%4; b
= (b
-p
)/4
832 if p
== 1 then ctx
.o16
= "o16"
833 elseif p
== 2 then ctx
.rep
= "rep"
834 elseif p
== 3 then ctx
.rep
= "repne" end
835 local l
= b
%2; b
= (b
-l
)/2
836 if l
~= 0 then ctx
.vexl
= true end
838 return dispatchmap(ctx
, map
)
841 -- Special case for nop with REX prefix.
842 nop
= function(ctx
, name
, pat
)
843 return dispatch(ctx
, ctx
.rex
and pat
or "nop")
846 -- Special case for 0F 77.
847 emms
= function(ctx
, name
, pat
)
848 if ctx
.rex
~= "vex" then
849 return putop(ctx
, "emms")
852 return putop(ctx
, "zeroall")
854 return putop(ctx
, "zeroupper")
859 ------------------------------------------------------------------------------
861 -- Disassemble a block of code.
862 local function disass_block(ctx
, ofs
, len
)
863 if not ofs
then ofs
= 0 end
864 local stop
= len
and ofs
+len
or #ctx
.code
872 while ctx
.pos
<= stop
do dispatchmap(ctx
, ctx
.map1
) end
873 if ctx
.pos
~= ctx
.start
then incomplete(ctx
) end
876 -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
877 local function create(code
, addr
, out
)
880 ctx
.addr
= (addr
or 0) - 1
881 ctx
.out
= out
or io
.write
883 ctx
.disass
= disass_block
886 ctx
.map1
= map_opc1_32
887 ctx
.aregs
= map_regs
.D
891 local function create64(code
, addr
, out
)
892 local ctx
= create(code
, addr
, out
)
894 ctx
.map1
= map_opc1_64
895 ctx
.aregs
= map_regs
.Q
899 -- Simple API: disassemble code (a string) at address and output via out.
900 local function disass(code
, addr
, out
)
901 create(code
, addr
, out
):disass()
904 local function disass64(code
, addr
, out
)
905 create64(code
, addr
, out
):disass()
908 -- Return register name for RID.
909 local function regname(r
)
910 if r
< 8 then return map_regs
.D
[r
+1] end
911 return map_regs
.X
[r
-7]
914 local function regname64(r
)
915 if r
< 16 then return map_regs
.Q
[r
+1] end
916 return map_regs
.X
[r
-15]
919 -- Public module functions.
926 regname64
= regname64