• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "disassembler_x86.h"
18 
19 #include <iostream>
20 
21 #include "base/logging.h"
22 #include "base/stringprintf.h"
23 #include "thread.h"
24 
25 namespace art {
26 namespace x86 {
27 
DisassemblerX86()28 DisassemblerX86::DisassemblerX86() {}
29 
Dump(std::ostream & os,const uint8_t * begin)30 size_t DisassemblerX86::Dump(std::ostream& os, const uint8_t* begin) {
31   return DumpInstruction(os, begin);
32 }
33 
Dump(std::ostream & os,const uint8_t * begin,const uint8_t * end)34 void DisassemblerX86::Dump(std::ostream& os, const uint8_t* begin, const uint8_t* end) {
35   size_t length = 0;
36   for (const uint8_t* cur = begin; cur < end; cur += length) {
37     length = DumpInstruction(os, cur);
38   }
39 }
40 
41 static const char* gReg8Names[]  = { "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh" };
42 static const char* gReg16Names[] = { "ax", "cx", "dx", "bx", "sp", "bp", "si", "di" };
43 static const char* gReg32Names[] = { "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi" };
44 
DumpReg0(std::ostream & os,uint8_t,size_t reg,bool byte_operand,uint8_t size_override)45 static void DumpReg0(std::ostream& os, uint8_t /*rex*/, size_t reg,
46                      bool byte_operand, uint8_t size_override) {
47   DCHECK_LT(reg, 8u);
48   // TODO: combine rex into size
49   size_t size = byte_operand ? 1 : (size_override == 0x66 ? 2 : 4);
50   switch (size) {
51     case 1: os << gReg8Names[reg]; break;
52     case 2: os << gReg16Names[reg]; break;
53     case 4: os << gReg32Names[reg]; break;
54     default: LOG(FATAL) << "unexpected size " << size;
55   }
56 }
57 
58 enum RegFile { GPR, MMX, SSE };
59 
DumpReg(std::ostream & os,uint8_t rex,uint8_t reg,bool byte_operand,uint8_t size_override,RegFile reg_file)60 static void DumpReg(std::ostream& os, uint8_t rex, uint8_t reg,
61                     bool byte_operand, uint8_t size_override, RegFile reg_file) {
62   size_t reg_num = reg;  // TODO: combine with REX.R on 64bit
63   if (reg_file == GPR) {
64     DumpReg0(os, rex, reg_num, byte_operand, size_override);
65   } else if (reg_file == SSE) {
66     os << "xmm" << reg_num;
67   } else {
68     os << "mm" << reg_num;
69   }
70 }
71 
DumpBaseReg(std::ostream & os,uint8_t rex,uint8_t reg)72 static void DumpBaseReg(std::ostream& os, uint8_t rex, uint8_t reg) {
73   size_t reg_num = reg;  // TODO: combine with REX.B on 64bit
74   DumpReg0(os, rex, reg_num, false, 0);
75 }
76 
DumpIndexReg(std::ostream & os,uint8_t rex,uint8_t reg)77 static void DumpIndexReg(std::ostream& os, uint8_t rex, uint8_t reg) {
78   int reg_num = reg;  // TODO: combine with REX.X on 64bit
79   DumpReg0(os, rex, reg_num, false, 0);
80 }
81 
82 enum SegmentPrefix {
83   kCs = 0x2e,
84   kSs = 0x36,
85   kDs = 0x3e,
86   kEs = 0x26,
87   kFs = 0x64,
88   kGs = 0x65,
89 };
90 
DumpSegmentOverride(std::ostream & os,uint8_t segment_prefix)91 static void DumpSegmentOverride(std::ostream& os, uint8_t segment_prefix) {
92   switch (segment_prefix) {
93     case kCs: os << "cs:"; break;
94     case kSs: os << "ss:"; break;
95     case kDs: os << "ds:"; break;
96     case kEs: os << "es:"; break;
97     case kFs: os << "fs:"; break;
98     case kGs: os << "gs:"; break;
99     default: break;
100   }
101 }
102 
DumpInstruction(std::ostream & os,const uint8_t * instr)103 size_t DisassemblerX86::DumpInstruction(std::ostream& os, const uint8_t* instr) {
104   const uint8_t* begin_instr = instr;
105   bool have_prefixes = true;
106   uint8_t prefix[4] = {0, 0, 0, 0};
107   const char** modrm_opcodes = NULL;
108   do {
109     switch (*instr) {
110         // Group 1 - lock and repeat prefixes:
111       case 0xF0:
112       case 0xF2:
113       case 0xF3:
114         prefix[0] = *instr;
115         break;
116         // Group 2 - segment override prefixes:
117       case kCs:
118       case kSs:
119       case kDs:
120       case kEs:
121       case kFs:
122       case kGs:
123         prefix[1] = *instr;
124         break;
125         // Group 3 - operand size override:
126       case 0x66:
127         prefix[2] = *instr;
128         break;
129         // Group 4 - address size override:
130       case 0x67:
131         prefix[3] = *instr;
132         break;
133       default:
134         have_prefixes = false;
135         break;
136     }
137     if (have_prefixes) {
138       instr++;
139     }
140   } while (have_prefixes);
141   uint8_t rex = (*instr >= 0x40 && *instr <= 0x4F) ? *instr : 0;
142   bool has_modrm = false;
143   bool reg_is_opcode = false;
144   size_t immediate_bytes = 0;
145   size_t branch_bytes = 0;
146   std::ostringstream opcode;
147   bool store = false;  // stores to memory (ie rm is on the left)
148   bool load = false;  // loads from memory (ie rm is on the right)
149   bool byte_operand = false;
150   bool ax = false;  // implicit use of ax
151   bool cx = false;  // implicit use of cx
152   bool reg_in_opcode = false;  // low 3-bits of opcode encode register parameter
153   bool no_ops = false;
154   RegFile src_reg_file = GPR;
155   RegFile dst_reg_file = GPR;
156   switch (*instr) {
157 #define DISASSEMBLER_ENTRY(opname, \
158                      rm8_r8, rm32_r32, \
159                      r8_rm8, r32_rm32, \
160                      ax8_i8, ax32_i32) \
161   case rm8_r8:   opcode << #opname; store = true; has_modrm = true; byte_operand = true; break; \
162   case rm32_r32: opcode << #opname; store = true; has_modrm = true; break; \
163   case r8_rm8:   opcode << #opname; load = true; has_modrm = true; byte_operand = true; break; \
164   case r32_rm32: opcode << #opname; load = true; has_modrm = true; break; \
165   case ax8_i8:   opcode << #opname; ax = true; immediate_bytes = 1; byte_operand = true; break; \
166   case ax32_i32: opcode << #opname; ax = true; immediate_bytes = 4; break;
167 
168 DISASSEMBLER_ENTRY(add,
169   0x00 /* RegMem8/Reg8 */,     0x01 /* RegMem32/Reg32 */,
170   0x02 /* Reg8/RegMem8 */,     0x03 /* Reg32/RegMem32 */,
171   0x04 /* Rax8/imm8 opcode */, 0x05 /* Rax32/imm32 */)
172 DISASSEMBLER_ENTRY(or,
173   0x08 /* RegMem8/Reg8 */,     0x09 /* RegMem32/Reg32 */,
174   0x0A /* Reg8/RegMem8 */,     0x0B /* Reg32/RegMem32 */,
175   0x0C /* Rax8/imm8 opcode */, 0x0D /* Rax32/imm32 */)
176 DISASSEMBLER_ENTRY(adc,
177   0x10 /* RegMem8/Reg8 */,     0x11 /* RegMem32/Reg32 */,
178   0x12 /* Reg8/RegMem8 */,     0x13 /* Reg32/RegMem32 */,
179   0x14 /* Rax8/imm8 opcode */, 0x15 /* Rax32/imm32 */)
180 DISASSEMBLER_ENTRY(sbb,
181   0x18 /* RegMem8/Reg8 */,     0x19 /* RegMem32/Reg32 */,
182   0x1A /* Reg8/RegMem8 */,     0x1B /* Reg32/RegMem32 */,
183   0x1C /* Rax8/imm8 opcode */, 0x1D /* Rax32/imm32 */)
184 DISASSEMBLER_ENTRY(and,
185   0x20 /* RegMem8/Reg8 */,     0x21 /* RegMem32/Reg32 */,
186   0x22 /* Reg8/RegMem8 */,     0x23 /* Reg32/RegMem32 */,
187   0x24 /* Rax8/imm8 opcode */, 0x25 /* Rax32/imm32 */)
188 DISASSEMBLER_ENTRY(sub,
189   0x28 /* RegMem8/Reg8 */,     0x29 /* RegMem32/Reg32 */,
190   0x2A /* Reg8/RegMem8 */,     0x2B /* Reg32/RegMem32 */,
191   0x2C /* Rax8/imm8 opcode */, 0x2D /* Rax32/imm32 */)
192 DISASSEMBLER_ENTRY(xor,
193   0x30 /* RegMem8/Reg8 */,     0x31 /* RegMem32/Reg32 */,
194   0x32 /* Reg8/RegMem8 */,     0x33 /* Reg32/RegMem32 */,
195   0x34 /* Rax8/imm8 opcode */, 0x35 /* Rax32/imm32 */)
196 DISASSEMBLER_ENTRY(cmp,
197   0x38 /* RegMem8/Reg8 */,     0x39 /* RegMem32/Reg32 */,
198   0x3A /* Reg8/RegMem8 */,     0x3B /* Reg32/RegMem32 */,
199   0x3C /* Rax8/imm8 opcode */, 0x3D /* Rax32/imm32 */)
200 
201 #undef DISASSEMBLER_ENTRY
202   case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
203     opcode << "push";
204     reg_in_opcode = true;
205     break;
206   case 0x58: case 0x59: case 0x5A: case 0x5B: case 0x5C: case 0x5D: case 0x5E: case 0x5F:
207     opcode << "pop";
208     reg_in_opcode = true;
209     break;
210   case 0x68: opcode << "push"; immediate_bytes = 4; break;
211   case 0x6A: opcode << "push"; immediate_bytes = 1; break;
212   case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77:
213   case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D: case 0x7E: case 0x7F:
214     static const char* condition_codes[] =
215     {"o", "no", "b/nae/c", "nb/ae/nc", "z/eq",  "nz/ne", "be/na", "nbe/a",
216      "s", "ns", "p/pe",    "np/po",    "l/nge", "nl/ge", "le/ng", "nle/g"
217     };
218     opcode << "j" << condition_codes[*instr & 0xF];
219     branch_bytes = 1;
220     break;
221   case 0x88: opcode << "mov"; store = true; has_modrm = true; byte_operand = true; break;
222   case 0x89: opcode << "mov"; store = true; has_modrm = true; break;
223   case 0x8A: opcode << "mov"; load = true; has_modrm = true; byte_operand = true; break;
224   case 0x8B: opcode << "mov"; load = true; has_modrm = true; break;
225 
226   case 0x0F:  // 2 byte extended opcode
227     instr++;
228     switch (*instr) {
229       case 0x10: case 0x11:
230         if (prefix[0] == 0xF2) {
231           opcode << "movsd";
232           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
233         } else if (prefix[0] == 0xF3) {
234           opcode << "movss";
235           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
236         } else if (prefix[2] == 0x66) {
237           opcode << "movupd";
238           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
239         } else {
240           opcode << "movups";
241         }
242         has_modrm = true;
243         src_reg_file = dst_reg_file = SSE;
244         load = *instr == 0x10;
245         store = !load;
246         break;
247       case 0x2A:
248         if (prefix[2] == 0x66) {
249           opcode << "cvtpi2pd";
250           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
251         } else if (prefix[0] == 0xF2) {
252           opcode << "cvtsi2sd";
253           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
254         } else if (prefix[0] == 0xF3) {
255           opcode << "cvtsi2ss";
256           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
257         } else {
258           opcode << "cvtpi2ps";
259         }
260         load = true;
261         has_modrm = true;
262         dst_reg_file = SSE;
263         break;
264       case 0x2C:
265         if (prefix[2] == 0x66) {
266           opcode << "cvttpd2pi";
267           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
268         } else if (prefix[0] == 0xF2) {
269           opcode << "cvttsd2si";
270           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
271         } else if (prefix[0] == 0xF3) {
272           opcode << "cvttss2si";
273           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
274         } else {
275           opcode << "cvttps2pi";
276         }
277         load = true;
278         has_modrm = true;
279         src_reg_file = SSE;
280         break;
281       case 0x2D:
282         if (prefix[2] == 0x66) {
283           opcode << "cvtpd2pi";
284           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
285         } else if (prefix[0] == 0xF2) {
286           opcode << "cvtsd2si";
287           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
288         } else if (prefix[0] == 0xF3) {
289           opcode << "cvtss2si";
290           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
291         } else {
292           opcode << "cvtps2pi";
293         }
294         load = true;
295         has_modrm = true;
296         src_reg_file = SSE;
297         break;
298       case 0x2E:
299         opcode << "u";
300         // FALLTHROUGH
301       case 0x2F:
302         if (prefix[2] == 0x66) {
303           opcode << "comisd";
304           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
305         } else {
306           opcode << "comiss";
307         }
308         has_modrm = true;
309         load = true;
310         src_reg_file = dst_reg_file = SSE;
311         break;
312       case 0x38:  // 3 byte extended opcode
313         opcode << StringPrintf("unknown opcode '0F 38 %02X'", *instr);
314         break;
315       case 0x3A:  // 3 byte extended opcode
316         opcode << StringPrintf("unknown opcode '0F 3A %02X'", *instr);
317         break;
318       case 0x50: case 0x51: case 0x52: case 0x53: case 0x54: case 0x55: case 0x56: case 0x57:
319       case 0x58: case 0x59: case 0x5C: case 0x5D: case 0x5E: case 0x5F: {
320         switch (*instr) {
321           case 0x50: opcode << "movmsk"; break;
322           case 0x51: opcode << "sqrt"; break;
323           case 0x52: opcode << "rsqrt"; break;
324           case 0x53: opcode << "rcp"; break;
325           case 0x54: opcode << "and"; break;
326           case 0x55: opcode << "andn"; break;
327           case 0x56: opcode << "or"; break;
328           case 0x57: opcode << "xor"; break;
329           case 0x58: opcode << "add"; break;
330           case 0x59: opcode << "mul"; break;
331           case 0x5C: opcode << "sub"; break;
332           case 0x5D: opcode << "min"; break;
333           case 0x5E: opcode << "div"; break;
334           case 0x5F: opcode << "max"; break;
335           default: LOG(FATAL) << "Unreachable";
336         }
337         if (prefix[2] == 0x66) {
338           opcode << "pd";
339           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
340         } else if (prefix[0] == 0xF2) {
341           opcode << "sd";
342           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
343         } else if (prefix[0] == 0xF3) {
344           opcode << "ss";
345           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
346         } else {
347           opcode << "ps";
348         }
349         load = true;
350         has_modrm = true;
351         src_reg_file = dst_reg_file = SSE;
352         break;
353       }
354       case 0x5A:
355         if (prefix[2] == 0x66) {
356           opcode << "cvtpd2ps";
357           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
358         } else if (prefix[0] == 0xF2) {
359           opcode << "cvtsd2ss";
360           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
361         } else if (prefix[0] == 0xF3) {
362           opcode << "cvtss2sd";
363           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
364         } else {
365           opcode << "cvtps2pd";
366         }
367         load = true;
368         has_modrm = true;
369         src_reg_file = dst_reg_file = SSE;
370         break;
371       case 0x5B:
372         if (prefix[2] == 0x66) {
373           opcode << "cvtps2dq";
374           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
375         } else if (prefix[0] == 0xF2) {
376           opcode << "bad opcode F2 0F 5B";
377         } else if (prefix[0] == 0xF3) {
378           opcode << "cvttps2dq";
379           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
380         } else {
381           opcode << "cvtdq2ps";
382         }
383         load = true;
384         has_modrm = true;
385         src_reg_file = dst_reg_file = SSE;
386         break;
387       case 0x6E:
388         if (prefix[2] == 0x66) {
389           dst_reg_file = SSE;
390           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
391         } else {
392           dst_reg_file = MMX;
393         }
394         opcode << "movd";
395         load = true;
396         has_modrm = true;
397         break;
398       case 0x6F:
399         if (prefix[2] == 0x66) {
400           dst_reg_file = SSE;
401           opcode << "movdqa";
402           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
403         } else if (prefix[0] == 0xF3) {
404           dst_reg_file = SSE;
405           opcode << "movdqu";
406           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
407         } else {
408           dst_reg_file = MMX;
409           opcode << "movq";
410         }
411         load = true;
412         has_modrm = true;
413         break;
414       case 0x71:
415         if (prefix[2] == 0x66) {
416           dst_reg_file = SSE;
417           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
418         } else {
419           dst_reg_file = MMX;
420         }
421         static const char* x71_opcodes[] = {"unknown-71", "unknown-71", "psrlw", "unknown-71", "psraw", "unknown-71", "psllw", "unknown-71"};
422         modrm_opcodes = x71_opcodes;
423         reg_is_opcode = true;
424         has_modrm = true;
425         store = true;
426         immediate_bytes = 1;
427         break;
428       case 0x72:
429         if (prefix[2] == 0x66) {
430           dst_reg_file = SSE;
431           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
432         } else {
433           dst_reg_file = MMX;
434         }
435         static const char* x72_opcodes[] = {"unknown-72", "unknown-72", "psrld", "unknown-72", "psrad", "unknown-72", "pslld", "unknown-72"};
436         modrm_opcodes = x72_opcodes;
437         reg_is_opcode = true;
438         has_modrm = true;
439         store = true;
440         immediate_bytes = 1;
441         break;
442       case 0x73:
443         if (prefix[2] == 0x66) {
444           dst_reg_file = SSE;
445           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
446         } else {
447           dst_reg_file = MMX;
448         }
449         static const char* x73_opcodes[] = {"unknown-73", "unknown-73", "psrlq", "unknown-73", "unknown-73", "unknown-73", "psllq", "unknown-73"};
450         modrm_opcodes = x73_opcodes;
451         reg_is_opcode = true;
452         has_modrm = true;
453         store = true;
454         immediate_bytes = 1;
455         break;
456       case 0x7E:
457         if (prefix[2] == 0x66) {
458           src_reg_file = SSE;
459           prefix[2] = 0;  // clear prefix now it's served its purpose as part of the opcode
460         } else {
461           src_reg_file = MMX;
462         }
463         opcode << "movd";
464         has_modrm = true;
465         store = true;
466         break;
467       case 0x80: case 0x81: case 0x82: case 0x83: case 0x84: case 0x85: case 0x86: case 0x87:
468       case 0x88: case 0x89: case 0x8A: case 0x8B: case 0x8C: case 0x8D: case 0x8E: case 0x8F:
469         opcode << "j" << condition_codes[*instr & 0xF];
470         branch_bytes = 4;
471         break;
472       case 0x90: case 0x91: case 0x92: case 0x93: case 0x94: case 0x95: case 0x96: case 0x97:
473       case 0x98: case 0x99: case 0x9A: case 0x9B: case 0x9C: case 0x9D: case 0x9E: case 0x9F:
474         opcode << "set" << condition_codes[*instr & 0xF];
475         modrm_opcodes = NULL;
476         reg_is_opcode = true;
477         has_modrm = true;
478         store = true;
479         break;
480       case 0xAE:
481         if (prefix[0] == 0xF3) {
482           prefix[0] = 0;  // clear prefix now it's served its purpose as part of the opcode
483           static const char* xAE_opcodes[] = {"rdfsbase", "rdgsbase", "wrfsbase", "wrgsbase", "unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE"};
484           modrm_opcodes = xAE_opcodes;
485           reg_is_opcode = true;
486           has_modrm = true;
487           uint8_t reg_or_opcode = (instr[1] >> 3) & 7;
488           switch (reg_or_opcode) {
489             case 0:
490               prefix[1] = kFs;
491               load = true;
492               break;
493             case 1:
494               prefix[1] = kGs;
495               load = true;
496               break;
497             case 2:
498               prefix[1] = kFs;
499               store = true;
500               break;
501             case 3:
502               prefix[1] = kGs;
503               store = true;
504               break;
505             default:
506               load = true;
507               break;
508           }
509         } else {
510           static const char* xAE_opcodes[] = {"unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE", "unknown-AE", "lfence", "mfence", "sfence"};
511           modrm_opcodes = xAE_opcodes;
512           reg_is_opcode = true;
513           has_modrm = true;
514           load = true;
515           no_ops = true;
516         }
517         break;
518       case 0xB1: opcode << "cmpxchg"; has_modrm = true; store = true; break;
519       case 0xB6: opcode << "movzxb"; has_modrm = true; load = true; break;
520       case 0xB7: opcode << "movzxw"; has_modrm = true; load = true; break;
521       case 0xBE: opcode << "movsxb"; has_modrm = true; load = true; break;
522       case 0xBF: opcode << "movsxw"; has_modrm = true; load = true; break;
523       default:
524         opcode << StringPrintf("unknown opcode '0F %02X'", *instr);
525         break;
526     }
527     break;
528   case 0x80: case 0x81: case 0x82: case 0x83:
529     static const char* x80_opcodes[] = {"add", "or", "adc", "sbb", "and", "sub", "xor", "cmp"};
530     modrm_opcodes = x80_opcodes;
531     has_modrm = true;
532     reg_is_opcode = true;
533     store = true;
534     byte_operand = (*instr & 1) == 0;
535     immediate_bytes = *instr == 0x81 ? 4 : 1;
536     break;
537   case 0x84: case 0x85:
538     opcode << "test";
539     has_modrm = true;
540     load = true;
541     byte_operand = (*instr & 1) == 0;
542     break;
543   case 0x8D:
544     opcode << "lea";
545     has_modrm = true;
546     load = true;
547     break;
548   case 0x8F:
549     opcode << "pop";
550     has_modrm = true;
551     reg_is_opcode = true;
552     store = true;
553     break;
554   case 0xB0: case 0xB1: case 0xB2: case 0xB3: case 0xB4: case 0xB5: case 0xB6: case 0xB7:
555     opcode << "mov";
556     immediate_bytes = 1;
557     reg_in_opcode = true;
558     break;
559   case 0xB8: case 0xB9: case 0xBA: case 0xBB: case 0xBC: case 0xBD: case 0xBE: case 0xBF:
560     opcode << "mov";
561     immediate_bytes = 4;
562     reg_in_opcode = true;
563     break;
564   case 0xC0: case 0xC1:
565   case 0xD0: case 0xD1: case 0xD2: case 0xD3:
566     static const char* shift_opcodes[] =
567         {"rol", "ror", "rcl", "rcr", "shl", "shr", "unknown-shift", "sar"};
568     modrm_opcodes = shift_opcodes;
569     has_modrm = true;
570     reg_is_opcode = true;
571     store = true;
572     immediate_bytes = ((*instr & 0xf0) == 0xc0) ? 1 : 0;
573     cx = (*instr == 0xD2) || (*instr == 0xD3);
574     byte_operand = (*instr == 0xC0);
575     break;
576   case 0xC3: opcode << "ret"; break;
577   case 0xC7:
578     static const char* c7_opcodes[] = {"mov", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7", "unknown-c7"};
579     modrm_opcodes = c7_opcodes;
580     store = true;
581     immediate_bytes = 4;
582     has_modrm = true;
583     reg_is_opcode = true;
584     break;
585   case 0xCC: opcode << "int 3"; break;
586   case 0xE8: opcode << "call"; branch_bytes = 4; break;
587   case 0xE9: opcode << "jmp"; branch_bytes = 4; break;
588   case 0xEB: opcode << "jmp"; branch_bytes = 1; break;
589   case 0xF5: opcode << "cmc"; break;
590   case 0xF6: case 0xF7:
591     static const char* f7_opcodes[] = {"test", "unknown-f7", "not", "neg", "mul edx:eax, eax *", "imul edx:eax, eax *", "div edx:eax, edx:eax /", "idiv edx:eax, edx:eax /"};
592     modrm_opcodes = f7_opcodes;
593     has_modrm = true;
594     reg_is_opcode = true;
595     store = true;
596     immediate_bytes = ((instr[1] & 0x38) == 0) ? 1 : 0;
597     break;
598   case 0xFF:
599     static const char* ff_opcodes[] = {"inc", "dec", "call", "call", "jmp", "jmp", "push", "unknown-ff"};
600     modrm_opcodes = ff_opcodes;
601     has_modrm = true;
602     reg_is_opcode = true;
603     load = true;
604     break;
605   default:
606     opcode << StringPrintf("unknown opcode '%02X'", *instr);
607     break;
608   }
609   std::ostringstream args;
610   if (reg_in_opcode) {
611     DCHECK(!has_modrm);
612     DumpReg(args, rex, *instr & 0x7, false, prefix[2], GPR);
613   }
614   instr++;
615   uint32_t address_bits = 0;
616   if (has_modrm) {
617     uint8_t modrm = *instr;
618     instr++;
619     uint8_t mod = modrm >> 6;
620     uint8_t reg_or_opcode = (modrm >> 3) & 7;
621     uint8_t rm = modrm & 7;
622     std::ostringstream address;
623     if (mod == 0 && rm == 5) {  // fixed address
624       address_bits = *reinterpret_cast<const uint32_t*>(instr);
625       address << StringPrintf("[0x%x]", address_bits);
626       instr += 4;
627     } else if (rm == 4 && mod != 3) {  // SIB
628       uint8_t sib = *instr;
629       instr++;
630       uint8_t ss = (sib >> 6) & 3;
631       uint8_t index = (sib >> 3) & 7;
632       uint8_t base = sib & 7;
633       address << "[";
634       if (base != 5 || mod != 0) {
635         DumpBaseReg(address, rex, base);
636         if (index != 4) {
637           address << " + ";
638         }
639       }
640       if (index != 4) {
641         DumpIndexReg(address, rex, index);
642         if (ss != 0) {
643           address << StringPrintf(" * %d", 1 << ss);
644         }
645       }
646       if (mod == 1) {
647         address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(instr));
648         instr++;
649       } else if (mod == 2) {
650         address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
651         instr += 4;
652       }
653       address << "]";
654     } else {
655       if (mod == 3) {
656         if (!no_ops) {
657           DumpReg(address, rex, rm, byte_operand, prefix[2], load ? src_reg_file : dst_reg_file);
658         }
659       } else {
660         address << "[";
661         DumpBaseReg(address, rex, rm);
662         if (mod == 1) {
663           address << StringPrintf(" + %d", *reinterpret_cast<const int8_t*>(instr));
664           instr++;
665         } else if (mod == 2) {
666           address << StringPrintf(" + %d", *reinterpret_cast<const int32_t*>(instr));
667           instr += 4;
668         }
669         address << "]";
670       }
671     }
672 
673     if (reg_is_opcode && modrm_opcodes != NULL) {
674       opcode << modrm_opcodes[reg_or_opcode];
675     }
676     if (load) {
677       if (!reg_is_opcode) {
678         DumpReg(args, rex, reg_or_opcode, byte_operand, prefix[2], dst_reg_file);
679         args << ", ";
680       }
681       DumpSegmentOverride(args, prefix[1]);
682       args << address.str();
683     } else {
684       DCHECK(store);
685       DumpSegmentOverride(args, prefix[1]);
686       args << address.str();
687       if (!reg_is_opcode) {
688         args << ", ";
689         DumpReg(args, rex, reg_or_opcode, byte_operand, prefix[2], src_reg_file);
690       }
691     }
692   }
693   if (ax) {
694     // If this opcode implicitly uses ax, ax is always the first arg.
695     DumpReg(args, rex, 0 /* EAX */, byte_operand, prefix[2], GPR);
696   }
697   if (cx) {
698     args << ", ";
699     DumpReg(args, rex, 1 /* ECX */, true, prefix[2], GPR);
700   }
701   if (immediate_bytes > 0) {
702     if (has_modrm || reg_in_opcode || ax || cx) {
703       args << ", ";
704     }
705     if (immediate_bytes == 1) {
706       args << StringPrintf("%d", *reinterpret_cast<const int8_t*>(instr));
707       instr++;
708     } else {
709       CHECK_EQ(immediate_bytes, 4u);
710       args << StringPrintf("%d", *reinterpret_cast<const int32_t*>(instr));
711       instr += 4;
712     }
713   } else if (branch_bytes > 0) {
714     DCHECK(!has_modrm);
715     int32_t displacement;
716     if (branch_bytes == 1) {
717       displacement = *reinterpret_cast<const int8_t*>(instr);
718       instr++;
719     } else {
720       CHECK_EQ(branch_bytes, 4u);
721       displacement = *reinterpret_cast<const int32_t*>(instr);
722       instr += 4;
723     }
724     args << StringPrintf("%+d (%p)", displacement, instr + displacement);
725   }
726   if (prefix[1] == kFs) {
727     args << "  ; ";
728     Thread::DumpThreadOffset(args, address_bits, 4);
729   }
730   std::stringstream hex;
731   for (size_t i = 0; begin_instr + i < instr; ++i) {
732     hex << StringPrintf("%02X", begin_instr[i]);
733   }
734   std::stringstream prefixed_opcode;
735   switch (prefix[0]) {
736     case 0xF0: prefixed_opcode << "lock "; break;
737     case 0xF2: prefixed_opcode << "repne "; break;
738     case 0xF3: prefixed_opcode << "repe "; break;
739     case 0: break;
740     default: LOG(FATAL) << "Unreachable";
741   }
742   prefixed_opcode << opcode.str();
743   os << StringPrintf("%p: %22s    \t%-7s ", begin_instr, hex.str().c_str(),
744                      prefixed_opcode.str().c_str())
745      << args.str() << '\n';
746   return instr - begin_instr;
747 }  // NOLINT(readability/fn_size)
748 
749 }  // namespace x86
750 }  // namespace art
751