1 ----------------------------------------------------------------------------
2 -- LuaJIT x86/x64 disassembler module.
4 -- Copyright (C) 2005-2023 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 [0x96] = "||fmaddsub132pHXrvm",[0x97] = "||fmsubadd132pHXrvm",
244 [0x98] = "||fmadd132pHXrvm",[0x99] = "||fmadd132sHXrvm",
245 [0x9a] = "||fmsub132pHXrvm",[0x9b] = "||fmsub132sHXrvm",
246 [0x9c] = "||fnmadd132pHXrvm",[0x9d] = "||fnmadd132sHXrvm",
247 [0x9e] = "||fnmsub132pHXrvm",[0x9f] = "||fnmsub132sHXrvm",
249 [0xa6] = "||fmaddsub213pHXrvm",[0xa7] = "||fmsubadd213pHXrvm",
250 [0xa8] = "||fmadd213pHXrvm",[0xa9] = "||fmadd213sHXrvm",
251 [0xaa] = "||fmsub213pHXrvm",[0xab] = "||fmsub213sHXrvm",
252 [0xac] = "||fnmadd213pHXrvm",[0xad] = "||fnmadd213sHXrvm",
253 [0xae] = "||fnmsub213pHXrvm",[0xaf] = "||fnmsub213sHXrvm",
255 [0xb6] = "||fmaddsub231pHXrvm",[0xb7] = "||fmsubadd231pHXrvm",
256 [0xb8] = "||fmadd231pHXrvm",[0xb9] = "||fmadd231sHXrvm",
257 [0xba] = "||fmsub231pHXrvm",[0xbb] = "||fmsub231sHXrvm",
258 [0xbc] = "||fnmadd231pHXrvm",[0xbd] = "||fnmadd231sHXrvm",
259 [0xbe] = "||fnmsub231pHXrvm",[0xbf] = "||fnmsub231sHXrvm",
261 [0xdc] = "||aesencXrvm", [0xdd] = "||aesenclastXrvm",
262 [0xde] = "||aesdecXrvm", [0xdf] = "||aesdeclastXrvm",
264 [0xf0] = "|||crc32TrBmt",[0xf1] = "|||crc32TrVmt",
265 [0xf7] = "| sarxVrmv| shlxVrmv| shrxVrmv",
268 ["3a"] = { -- [66] 0f 3a xx
270 [0x00]="||permqXrmu","||permpdXrmu","||pblenddXrvmu",nil,
271 "||permilpsXrmu","||permilpdXrmu","||perm2f128Xrvmu",nil,
272 "||roundpsXrmu","||roundpdXrmu","||roundssXrvmu","||roundsdXrvmu",
273 "||blendpsXrvmu","||blendpdXrvmu","||pblendwXrvmu","palignrPrvmu",
276 "||pextrbVmXru","||pextrwVmXru","||pextrVmSXru","||extractpsVmXru",
277 "||insertf128XrvlXmu","||extractf128XlXmYru",nil,nil,
280 "||pinsrbXrvVmu","||insertpsXrvmu","||pinsrXrvVmuS",nil,
282 [0x38] = "||inserti128Xrvmu",[0x39] = "||extracti128XlXmYru",
284 [0x40] = "||dppsXrvmu",
285 [0x41] = "||dppdXrvmu",
286 [0x42] = "||mpsadbwXrvmu",
287 [0x44] = "||pclmulqdqXrvmu",
288 [0x46] = "||perm2i128Xrvmu",
289 [0x4a] = "||blendvpsXrvmb",[0x4b] = "||blendvpdXrvmb",
290 [0x4c] = "||pblendvbXrvmb",
292 [0x60] = "||pcmpestrmXrmu",[0x61] = "||pcmpestriXrmu",
293 [0x62] = "||pcmpistrmXrmu",[0x63] = "||pcmpistriXrmu",
294 [0xdf] = "||aeskeygenassistXrmu",
296 [0xf0] = "||| rorxVrmu",
300 -- Map for VMX/SVM opcodes 0F 01 C0-FF (sgdt group with register operands).
302 [0xc1]="vmcall",[0xc2]="vmlaunch",[0xc3]="vmresume",[0xc4]="vmxoff",
303 [0xc8]="monitor",[0xc9]="mwait",
304 [0xd8]="vmrun",[0xd9]="vmmcall",[0xda]="vmload",[0xdb]="vmsave",
305 [0xdc]="stgi",[0xdd]="clgi",[0xde]="skinit",[0xdf]="invlpga",
306 [0xf8]="swapgs",[0xf9]="rdtscp",
309 -- Map for FP opcodes. And you thought stack machines are simple?
311 -- D8-DF 00-BF: opcodes with a memory operand.
313 [0]="faddFm","fmulFm","fcomFm","fcompFm","fsubFm","fsubrFm","fdivFm","fdivrFm",
314 "fldFm",nil,"fstFm","fstpFm","fldenvVm","fldcwWm","fnstenvVm","fnstcwWm",
316 "fiaddDm","fimulDm","ficomDm","ficompDm",
317 "fisubDm","fisubrDm","fidivDm","fidivrDm",
319 "fildDm","fisttpDm","fistDm","fistpDm",nil,"fld twordFmp",nil,"fstp twordFmp",
321 "faddGm","fmulGm","fcomGm","fcompGm","fsubGm","fsubrGm","fdivGm","fdivrGm",
323 "fldGm","fisttpQm","fstGm","fstpGm","frstorDmp",nil,"fnsaveDmp","fnstswWm",
325 "fiaddWm","fimulWm","ficomWm","ficompWm",
326 "fisubWm","fisubrWm","fidivWm","fidivrWm",
328 "fildWm","fisttpWm","fistWm","fistpWm",
329 "fbld twordFmp","fildQm","fbstp twordFmp","fistpQm",
330 -- xx C0-FF: opcodes with a pseudo-register operand.
332 "faddFf","fmulFf","fcomFf","fcompFf","fsubFf","fsubrFf","fdivFf","fdivrFf",
334 "fldFf","fxchFf",{"fnop"},nil,
335 {"fchs","fabs",nil,nil,"ftst","fxam"},
336 {"fld1","fldl2t","fldl2e","fldpi","fldlg2","fldln2","fldz"},
337 {"f2xm1","fyl2x","fptan","fpatan","fxtract","fprem1","fdecstp","fincstp"},
338 {"fprem","fyl2xp1","fsqrt","fsincos","frndint","fscale","fsin","fcos"},
340 "fcmovbFf","fcmoveFf","fcmovbeFf","fcmovuFf",nil,{nil,"fucompp"},nil,nil,
342 "fcmovnbFf","fcmovneFf","fcmovnbeFf","fcmovnuFf",
343 {nil,nil,"fnclex","fninit"},"fucomiFf","fcomiFf",nil,
345 "fadd toFf","fmul toFf",nil,nil,
346 "fsub toFf","fsubr toFf","fdivr toFf","fdiv toFf",
348 "ffreeFf",nil,"fstFf","fstpFf","fucomFf","fucompFf",nil,nil,
350 "faddpFf","fmulpFf",nil,{nil,"fcompp"},
351 "fsubrpFf","fsubpFf","fdivrpFf","fdivpFf",
353 nil,nil,nil,nil,{"fnstsw ax"},"fucomipFf","fcomipFf",nil,
355 assert(map_opcfp
[126] == "fcomipFf")
357 -- Map for opcode groups. The subkey is sp from the ModRM byte.
358 local map_opcgroup
= {
359 arith
= { "add", "or", "adc", "sbb", "and", "sub", "xor", "cmp" },
360 shift
= { "rol", "ror", "rcl", "rcr", "shl", "shr", "sal", "sar" },
361 testb
= { "testBmi", "testBmi", "not", "neg", "mul", "imul", "div", "idiv" },
362 testv
= { "testVmi", "testVmi", "not", "neg", "mul", "imul", "div", "idiv" },
363 incb
= { "inc", "dec" },
364 incd
= { "inc", "dec", "callUmp", "$call farDmp",
365 "jmpUmp", "$jmp farDmp", "pushUm" },
366 sldt
= { "sldt", "str", "lldt", "ltr", "verr", "verw" },
367 sgdt
= { "vm*$sgdt", "vm*$sidt", "$lgdt", "vm*$lidt",
368 "smsw", nil, "lmsw", "vm*$invlpg" },
369 bt
= { nil, nil, nil, nil, "bt", "bts", "btr", "btc" },
370 cmpxchg
= { nil, "sz*,cmpxchg8bQmp,cmpxchg16bXmp", nil, nil,
371 nil, nil, "vmptrld|vmxon|vmclear", "vmptrst" },
372 pshiftw
= { nil, nil, "psrlw", nil, "psraw", nil, "psllw" },
373 pshiftd
= { nil, nil, "psrld", nil, "psrad", nil, "pslld" },
374 pshiftq
= { nil, nil, "psrlq", nil, nil, nil, "psllq" },
375 pshiftdq
= { nil, nil, "psrlq", "psrldq", nil, nil, "psllq", "pslldq" },
376 fxsave
= { "$fxsave", "$fxrstor", "$ldmxcsr", "$stmxcsr",
377 nil, "lfenceDp$", "mfenceDp$", "sfenceDp$clflush" },
378 prefetch
= { "prefetch", "prefetchw" },
379 prefetcht
= { "prefetchnta", "prefetcht0", "prefetcht1", "prefetcht2" },
382 ------------------------------------------------------------------------------
384 -- Maps for register names.
386 B
= { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
387 "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
388 B64
= { "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
389 "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b" },
390 W
= { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
391 "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w" },
392 D
= { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
393 "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d" },
394 Q
= { "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
395 "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15" },
396 M
= { "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
397 "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" }, -- No x64 ext!
398 X
= { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
399 "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" },
400 Y
= { "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
401 "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15" },
403 local map_segregs
= { "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7" }
405 -- Maps for size names.
407 B
= 1, W
= 2, D
= 4, Q
= 8, M
= 8, X
= 16, Y
= 32,
409 local map_sz2prefix
= {
410 B
= "byte", W
= "word", D
= "dword",
412 M
= "qword", X
= "xword", Y
= "yword",
413 F
= "dword", G
= "qword", -- No need for sizes/register names for these two.
416 ------------------------------------------------------------------------------
418 -- Output a nicely formatted line with an opcode and operands.
419 local function putop(ctx
, text
, operands
)
420 local code
, pos
, hex
= ctx
.code
, ctx
.pos
, ""
421 local hmax
= ctx
.hexdump
423 for i
=ctx
.start
,pos
-1 do
424 hex
= hex
..format("%02X", byte(code
, i
, i
))
426 if #hex
> hmax
then hex
= sub(hex
, 1, hmax
)..". "
427 else hex
= hex
..rep(" ", hmax
-#hex
+2) end
429 if operands
then text
= text
.." "..operands
end
430 if ctx
.o16
then text
= "o16 "..text
; ctx
.o16
= false end
431 if ctx
.a32
then text
= "a32 "..text
; ctx
.a32
= false end
432 if ctx
.rep
then text
= ctx
.rep
.." "..text
; ctx
.rep
= false end
434 local t
= (ctx
.rexw
and "w" or "")..(ctx
.rexr
and "r" or "")..
435 (ctx
.rexx
and "x" or "")..(ctx
.rexb
and "b" or "")..
436 (ctx
.vexl
and "l" or "")
437 if ctx
.vexv
and ctx
.vexv
~= 0 then t
= t
.."v"..ctx
.vexv
end
438 if t
~= "" then text
= ctx
.rex
.."."..t
.." "..gsub(text
, "^ ", "")
439 elseif ctx
.rex
== "vex" then text
= gsub("v"..text
, "^v ", "") end
440 ctx
.rexw
= false; ctx
.rexr
= false; ctx
.rexx
= false; ctx
.rexb
= false
441 ctx
.rex
= false; ctx
.vexl
= false; ctx
.vexv
= false
444 local text2
, n
= gsub(text
, "%[", "["..ctx
.seg
..":")
445 if n
== 0 then text
= ctx
.seg
.." "..text
else text
= text2
end
448 if ctx
.lock
then text
= "lock "..text
; ctx
.lock
= false end
451 local sym
= ctx
.symtab
[imm
]
452 if sym
then text
= text
.."\t->"..sym
end
454 ctx
.out(format("%08x %s%s\n", ctx
.addr
+ctx
.start
, hex
, text
))
461 -- Clear all prefix flags.
462 local function clearprefixes(ctx
)
463 ctx
.o16
= false; ctx
.seg
= false; ctx
.lock
= false; ctx
.rep
= false
464 ctx
.rexw
= false; ctx
.rexr
= false; ctx
.rexx
= false; ctx
.rexb
= false
465 ctx
.rex
= false; ctx
.a32
= false; ctx
.vexl
= false
468 -- Fallback for incomplete opcodes at the end.
469 local function incomplete(ctx
)
472 return putop(ctx
, "(incomplete)")
475 -- Fallback for unknown opcodes.
476 local function unknown(ctx
)
478 return putop(ctx
, "(unknown)")
481 -- Return an immediate of the specified size.
482 local function getimm(ctx
, pos
, n
)
483 if pos
+n
-1 > ctx
.stop
then return incomplete(ctx
) end
484 local code
= ctx
.code
486 local b1
= byte(code
, pos
, pos
)
489 local b1
, b2
= byte(code
, pos
, pos
+1)
492 local b1
, b2
, b3
, b4
= byte(code
, pos
, pos
+3)
493 local imm
= b1
+b2
*256+b3
*65536+b4
*16777216
499 -- Process pattern string and generate the operands.
500 local function putpat(ctx
, name
, pat
)
501 local operands
, regs
, sz
, mode
, sp
, rm
, sc
, rx
, sdisp
502 local code
, pos
, stop
, vexl
= ctx
.code
, ctx
.pos
, ctx
.stop
, ctx
.vexl
504 -- Chars used: 1DFGHIMPQRSTUVWXYabcdfgijlmoprstuvwxyz
505 for p
in gmatch(pat
, ".") do
507 if p
== "V" or p
== "U" then
508 if ctx
.rexw
then sz
= "Q"; ctx
.rexw
= false
509 elseif ctx
.o16
then sz
= "W"; ctx
.o16
= false
510 elseif p
== "U" and ctx
.x64
then sz
= "Q"
514 if ctx
.rexw
then sz
= "Q"; ctx
.rexw
= false else sz
= "D" end
518 regs
= ctx
.rex
and map_regs
.B64
or map_regs
.B
519 elseif match(p
, "[WDQMXYFG]") then
521 if sz
== "X" and vexl
then sz
= "Y"; ctx
.vexl
= false end
524 sz
= ctx
.o16
and "X" or "M"; ctx
.o16
= false
525 if sz
== "X" and vexl
then sz
= "Y"; ctx
.vexl
= false end
528 name
= name
..(ctx
.rexw
and "d" or "s")
531 name
= name
..lower(sz
)
533 local imm
= getimm(ctx
, pos
, 1); if not imm
then return end
534 x
= imm
<= 127 and format("+0x%02x", imm
)
535 or format("-0x%02x", 256-imm
)
538 local imm
= getimm(ctx
, pos
, 1); if not imm
then return end
539 x
= format("0x%02x", imm
)
542 local imm
= getimm(ctx
, pos
, 1); if not imm
then return end
546 local imm
= getimm(ctx
, pos
, 2); if not imm
then return end
547 x
= format("0x%x", imm
)
549 elseif p
== "o" then -- [offset]
551 local imm1
= getimm(ctx
, pos
, 4); if not imm1
then return end
552 local imm2
= getimm(ctx
, pos
+4, 4); if not imm2
then return end
553 x
= format("[0x%08x%08x]", imm2
, imm1
)
556 local imm
= getimm(ctx
, pos
, 4); if not imm
then return end
557 x
= format("[0x%08x]", imm
)
560 elseif p
== "i" or p
== "I" then
561 local n
= map_sz2n
[sz
]
562 if n
== 8 and ctx
.x64
and p
== "I" then
563 local imm1
= getimm(ctx
, pos
, 4); if not imm1
then return end
564 local imm2
= getimm(ctx
, pos
+4, 4); if not imm2
then return end
565 x
= format("0x%08x%08x", imm2
, imm1
)
567 if n
== 8 then n
= 4 end
568 local imm
= getimm(ctx
, pos
, n
); if not imm
then return end
569 if sz
== "Q" and (imm
< 0 or imm
> 0x7fffffff) then
570 imm
= (0xffffffff+1)-imm
571 x
= format(imm
> 65535 and "-0x%08x" or "-0x%x", imm
)
573 x
= format(imm
> 65535 and "0x%08x" or "0x%x", imm
)
578 local n
= map_sz2n
[sz
]
579 if n
== 8 then n
= 4 end
580 local imm
= getimm(ctx
, pos
, n
); if not imm
then return end
581 if sz
== "B" and imm
> 127 then imm
= imm
-256
582 elseif imm
> 2147483647 then imm
= imm
-4294967296 end
584 imm
= imm
+ pos
+ ctx
.addr
585 if imm
> 4294967295 and not ctx
.x64
then imm
= imm
-4294967296 end
588 x
= format("word 0x%04x", imm
%65536)
590 local lo
= imm
% 0x1000000
591 x
= format("0x%02x%06x", (imm
-lo
) / 0x1000000, lo
)
596 local r
= byte(code
, pos
-1, pos
-1)%8
597 if ctx
.rexb
then r
= r
+ 8; ctx
.rexb
= false end
599 elseif p
== "a" then x
= regs
[1]
600 elseif p
== "c" then x
= "cl"
601 elseif p
== "d" then x
= "dx"
602 elseif p
== "1" then x
= "1"
607 if pos
> stop
then return incomplete(ctx
) end
608 mode
= byte(code
, pos
, pos
)
611 rm
= mode
%8; mode
= (mode
-rm
)/8
612 sp
= mode
%8; mode
= (mode
-sp
)/8
616 if pos
> stop
then return incomplete(ctx
) end
617 sc
= byte(code
, pos
, pos
)
619 rm
= sc
%8; sc
= (sc
-rm
)/8
620 rx
= sc
%8; sc
= (sc
-rx
)/8
621 if ctx
.rexx
then rx
= rx
+ 8; ctx
.rexx
= false end
622 if rx
== 4 then rx
= nil end
624 if mode
> 0 or rm
== 5 then
626 if dsz
~= 1 then dsz
= 4 end
627 local disp
= getimm(ctx
, pos
, dsz
); if not disp
then return end
628 if mode
== 0 then rm
= nil end
629 if rm
or rx
or (not sc
and ctx
.x64
and not ctx
.a32
) then
630 if dsz
== 1 and disp
> 127 then
631 sdisp
= format("-0x%x", 256-disp
)
632 elseif disp
>= 0 and disp
<= 0x7fffffff then
633 sdisp
= format("+0x%x", disp
)
635 sdisp
= format("-0x%x", (0xffffffff+1)-disp
)
638 sdisp
= format(ctx
.x64
and not ctx
.a32
and
639 not (disp
>= 0 and disp
<= 0x7fffffff)
640 and "0xffffffff%08x" or "0x%08x", disp
)
645 if rm
and ctx
.rexb
then rm
= rm
+ 8; ctx
.rexb
= false end
646 if ctx
.rexr
then sp
= sp
+ 8; ctx
.rexr
= false end
649 if mode
== 3 then x
= regs
[rm
+1]
651 local aregs
= ctx
.a32
and map_regs
.D
or ctx
.aregs
652 local srm
, srx
= "", ""
653 if rm
then srm
= aregs
[rm
+1]
654 elseif not sc
and ctx
.x64
and not ctx
.a32
then srm
= "rip" end
657 if rm
then srm
= srm
.."+" end
659 if sc
> 0 then srx
= srx
.."*"..(2^sc
) end
661 x
= format("[%s%s%s]", srm
, srx
, sdisp
)
664 (not match(pat
, "[aRrgp]") or match(pat
, "t")) then -- Yuck.
665 x
= map_sz2prefix
[sz
].." "..x
667 elseif p
== "r" then x
= regs
[sp
+1]
668 elseif p
== "g" then x
= map_segregs
[sp
+1]
669 elseif p
== "p" then -- Suppress prefix.
670 elseif p
== "f" then x
= "st"..rm
672 if sp
== 0 and ctx
.lock
and not ctx
.x64
then
673 x
= "CR8"; ctx
.lock
= false
679 x
= regs
[ctx
.vexv
+1]; ctx
.vexv
= false
681 elseif p
== "y" then x
= "DR"..sp
682 elseif p
== "z" then x
= "TR"..sp
683 elseif p
== "l" then vexl
= false
686 error("bad pattern `"..pat
.."'")
689 if x
then operands
= operands
and operands
..", "..x
or x
end
692 return putop(ctx
, name
, operands
)
695 -- Forward declaration.
698 -- Fetch and cache MRM byte.
699 local function getmrm(ctx
)
703 if pos
> ctx
.stop
then return nil end
704 mrm
= byte(ctx
.code
, pos
, pos
)
711 -- Dispatch to handler depending on pattern.
712 local function dispatch(ctx
, opat
, patgrp
)
713 if not opat
then return unknown(ctx
) end
714 if match(opat
, "%|") then -- MMX/SSE variants depending on prefix.
717 p
= ctx
.rep
=="rep" and "%|([^%|]*)" or "%|[^%|]*%|[^%|]*%|([^%|]*)"
719 elseif ctx
.o16
then p
= "%|[^%|]*%|([^%|]*)"; ctx
.o16
= false
720 else p
= "^[^%|]*" end
721 opat
= match(opat
, p
)
722 if not opat
then return unknown(ctx
) end
723 -- ctx.rep = false; ctx.o16 = false
724 --XXX fails for 66 f2 0f 38 f1 06 crc32 eax,WORD PTR [esi]
725 --XXX remove in branches?
727 if match(opat
, "%$") then -- reg$mem variants.
728 local mrm
= getmrm(ctx
); if not mrm
then return incomplete(ctx
) end
729 opat
= match(opat
, mrm
>= 192 and "^[^%$]*" or "%$(.*)")
730 if opat
== "" then return unknown(ctx
) end
732 if opat
== "" then return unknown(ctx
) end
733 local name
, pat
= match(opat
, "^([a-z0-9 ]*)(.*)")
734 if pat
== "" and patgrp
then pat
= patgrp
end
735 return map_act
[sub(pat
, 1, 1)](ctx
, name
, pat
)
738 -- Get a pattern from an opcode map and dispatch to handler.
739 local function dispatchmap(ctx
, opcmap
)
741 local opat
= opcmap
[byte(ctx
.code
, pos
, pos
)]
744 return dispatch(ctx
, opat
)
747 -- Map for action codes. The key is the first char after the name.
749 -- Simple opcodes without operands.
750 [""] = function(ctx
, name
, pat
)
751 return putop(ctx
, name
)
754 -- Operand size chars fall right through.
755 B
= putpat
, W
= putpat
, D
= putpat
, Q
= putpat
,
756 V
= putpat
, U
= putpat
, T
= putpat
,
757 M
= putpat
, X
= putpat
, P
= putpat
,
758 F
= putpat
, G
= putpat
, Y
= putpat
,
762 [":"] = function(ctx
, name
, pat
)
763 ctx
[pat
== ":" and name
or sub(pat
, 2)] = name
764 if ctx
.pos
- ctx
.start
> 5 then return unknown(ctx
) end -- Limit #prefixes.
767 -- Chain to special handler specified by name.
768 ["*"] = function(ctx
, name
, pat
)
769 return map_act
[name
](ctx
, name
, sub(pat
, 2))
772 -- Use named subtable for opcode group.
773 ["!"] = function(ctx
, name
, pat
)
774 local mrm
= getmrm(ctx
); if not mrm
then return incomplete(ctx
) end
775 return dispatch(ctx
, map_opcgroup
[name
][((mrm
-(mrm
%8))/8)%8+1], sub(pat
, 2))
778 -- o16,o32[,o64] variants.
779 sz
= function(ctx
, name
, pat
)
780 if ctx
.o16
then ctx
.o16
= false
782 pat
= match(pat
, ",(.*)")
784 local p
= match(pat
, ",(.*)")
785 if p
then pat
= p
; ctx
.rexw
= false end
788 pat
= match(pat
, "^[^,]*")
789 return dispatch(ctx
, pat
)
792 -- Two-byte opcode dispatch.
793 opc2
= function(ctx
, name
, pat
)
794 return dispatchmap(ctx
, map_opc2
)
797 -- Three-byte opcode dispatch.
798 opc3
= function(ctx
, name
, pat
)
799 return dispatchmap(ctx
, map_opc3
[pat
])
803 vm
= function(ctx
, name
, pat
)
804 return dispatch(ctx
, map_opcvm
[ctx
.mrm
])
807 -- Floating point opcode dispatch.
808 fp
= function(ctx
, name
, pat
)
809 local mrm
= getmrm(ctx
); if not mrm
then return incomplete(ctx
) end
811 local idx
= pat
*8 + ((mrm
-rm
)/8)%8
812 if mrm
>= 192 then idx
= idx
+ 64 end
813 local opat
= map_opcfp
[idx
]
814 if type(opat
) == "table" then opat
= opat
[rm
+1] end
815 return dispatch(ctx
, opat
)
819 rex
= function(ctx
, name
, pat
)
820 if ctx
.rex
then return unknown(ctx
) end -- Only 1 REX or VEX prefix allowed.
821 for p
in gmatch(pat
, ".") do ctx
["rex"..p
] = true end
826 vex
= function(ctx
, name
, pat
)
827 if ctx
.rex
then return unknown(ctx
) end -- Only 1 REX or VEX prefix allowed.
834 local b
= byte(ctx
.code
, pos
, pos
)
835 if not b
then return incomplete(ctx
) end
837 if b
< 128 then ctx
.rexr
= true end
840 m
= b
%32; b
= (b
-m
)/32
841 local nb
= b
%2; b
= (b
-nb
)/2
842 if nb
== 0 then ctx
.rexb
= true end
844 if nx
== 0 then ctx
.rexx
= true end
845 b
= byte(ctx
.code
, pos
, pos
)
846 if not b
then return incomplete(ctx
) end
848 if b
>= 128 then ctx
.rexw
= true end
852 if m
== 1 then map
= map_opc2
853 elseif m
== 2 then map
= map_opc3
["38"]
854 elseif m
== 3 then map
= map_opc3
["3a"]
855 else return unknown(ctx
) end
856 local p
= b
%4; b
= (b
-p
)/4
857 if p
== 1 then ctx
.o16
= "o16"
858 elseif p
== 2 then ctx
.rep
= "rep"
859 elseif p
== 3 then ctx
.rep
= "repne" end
860 local l
= b
%2; b
= (b
-l
)/2
861 if l
~= 0 then ctx
.vexl
= true end
863 return dispatchmap(ctx
, map
)
866 -- Special case for nop with REX prefix.
867 nop
= function(ctx
, name
, pat
)
868 return dispatch(ctx
, ctx
.rex
and pat
or "nop")
871 -- Special case for 0F 77.
872 emms
= function(ctx
, name
, pat
)
873 if ctx
.rex
~= "vex" then
874 return putop(ctx
, "emms")
877 return putop(ctx
, "zeroall")
879 return putop(ctx
, "zeroupper")
884 ------------------------------------------------------------------------------
886 -- Disassemble a block of code.
887 local function disass_block(ctx
, ofs
, len
)
888 if not ofs
then ofs
= 0 end
889 local stop
= len
and ofs
+len
or #ctx
.code
897 while ctx
.pos
<= stop
do dispatchmap(ctx
, ctx
.map1
) end
898 if ctx
.pos
~= ctx
.start
then incomplete(ctx
) end
901 -- Extended API: create a disassembler context. Then call ctx:disass(ofs, len).
902 local function create(code
, addr
, out
)
905 ctx
.addr
= (addr
or 0) - 1
906 ctx
.out
= out
or io
.write
908 ctx
.disass
= disass_block
911 ctx
.map1
= map_opc1_32
912 ctx
.aregs
= map_regs
.D
916 local function create64(code
, addr
, out
)
917 local ctx
= create(code
, addr
, out
)
919 ctx
.map1
= map_opc1_64
920 ctx
.aregs
= map_regs
.Q
924 -- Simple API: disassemble code (a string) at address and output via out.
925 local function disass(code
, addr
, out
)
926 create(code
, addr
, out
):disass()
929 local function disass64(code
, addr
, out
)
930 create64(code
, addr
, out
):disass()
933 -- Return register name for RID.
934 local function regname(r
)
935 if r
< 8 then return map_regs
.D
[r
+1] end
936 return map_regs
.X
[r
-7]
939 local function regname64(r
)
940 if r
< 16 then return map_regs
.Q
[r
+1] end
941 return map_regs
.X
[r
-15]
944 -- Public module functions.
951 regname64
= regname64