1#include <stdlib.h> 2 3#include "ia32_implicit.h" 4#include "ia32_insn.h" 5#include "ia32_reg.h" 6#include "x86_operand_list.h" 7 8/* Conventions: Register operands which are aliases of another register 9 * operand (e.g. AX in one operand and AL in another) assume that the 10 * operands are different registers and that alias tracking will resolve 11 * data flow. This means that something like 12 * mov ax, al 13 * would have 'write only' access for AX and 'read only' access for AL, 14 * even though both AL and AX are read and written */ 15typedef struct { 16 uint32_t type; 17 uint32_t operand; 18} op_implicit_list_t; 19 20static op_implicit_list_t list_aaa[] = 21 /* 37 : AAA : rw AL */ 22 /* 3F : AAS : rw AL */ 23 {{ OP_R | OP_W, REG_BYTE_OFFSET }, {0}}; /* aaa */ 24 25static op_implicit_list_t list_aad[] = 26 /* D5 0A, D5 (ib) : AAD : rw AX */ 27 /* D4 0A, D4 (ib) : AAM : rw AX */ 28 {{ OP_R | OP_W, REG_WORD_OFFSET }, {0}}; /* aad */ 29 30static op_implicit_list_t list_call[] = 31 /* E8, FF, 9A, FF : CALL : rw ESP, rw EIP */ 32 /* C2, C3, CA, CB : RET : rw ESP, rw EIP */ 33 {{ OP_R | OP_W, REG_EIP_INDEX }, 34 { OP_R | OP_W, REG_ESP_INDEX }, {0}}; /* call, ret */ 35 36static op_implicit_list_t list_cbw[] = 37 /* 98 : CBW : r AL, rw AX */ 38 {{ OP_R | OP_W, REG_WORD_OFFSET }, 39 { OP_R, REG_BYTE_OFFSET}, {0}}; /* cbw */ 40 41static op_implicit_list_t list_cwde[] = 42 /* 98 : CWDE : r AX, rw EAX */ 43 {{ OP_R | OP_W, REG_DWORD_OFFSET }, 44 { OP_R, REG_WORD_OFFSET }, {0}}; /* cwde */ 45 46static op_implicit_list_t list_clts[] = 47 /* 0F 06 : CLTS : rw CR0 */ 48 {{ OP_R | OP_W, REG_CTRL_OFFSET}, {0}}; /* clts */ 49 50static op_implicit_list_t list_cmpxchg[] = 51 /* 0F B0 : CMPXCHG : rw AL */ 52 {{ OP_R | OP_W, REG_BYTE_OFFSET }, {0}}; /* cmpxchg */ 53 54static op_implicit_list_t list_cmpxchgb[] = 55 /* 0F B1 : CMPXCHG : rw EAX */ 56 {{ OP_R | OP_W, REG_DWORD_OFFSET }, {0}}; /* cmpxchg */ 57 58static op_implicit_list_t list_cmpxchg8b[] = 59 /* 0F C7 : CMPXCHG8B : rw EDX, rw EAX, r ECX, r EBX */ 60 {{ OP_R | OP_W, REG_DWORD_OFFSET }, 61 { OP_R | OP_W, REG_DWORD_OFFSET + 2 }, 62 { OP_R, REG_DWORD_OFFSET + 1 }, 63 { OP_R, REG_DWORD_OFFSET + 3 }, {0}}; /* cmpxchg8b */ 64 65static op_implicit_list_t list_cpuid[] = 66 /* 0F A2 : CPUID : rw EAX, w EBX, w ECX, w EDX */ 67 {{ OP_R | OP_W, REG_DWORD_OFFSET }, 68 { OP_W, REG_DWORD_OFFSET + 1 }, 69 { OP_W, REG_DWORD_OFFSET + 2 }, 70 { OP_W, REG_DWORD_OFFSET + 3 }, {0}}; /* cpuid */ 71 72static op_implicit_list_t list_cwd[] = 73 /* 99 : CWD/CWQ : rw EAX, w EDX */ 74 {{ OP_R | OP_W, REG_DWORD_OFFSET }, 75 { OP_W, REG_DWORD_OFFSET + 2 }, {0}}; /* cwd */ 76 77static op_implicit_list_t list_daa[] = 78 /* 27 : DAA : rw AL */ 79 /* 2F : DAS : rw AL */ 80 {{ OP_R | OP_W, REG_BYTE_OFFSET }, {0}}; /* daa */ 81 82static op_implicit_list_t list_idiv[] = 83 /* F6 : DIV, IDIV : r AX, w AL, w AH */ 84 /* FIXED: first op was EAX, not Aw. TODO: verify! */ 85 {{ OP_R, REG_WORD_OFFSET }, 86 { OP_W, REG_BYTE_OFFSET }, 87 { OP_W, REG_BYTE_OFFSET + 4 }, {0}}; /* div */ 88 89static op_implicit_list_t list_div[] = 90 /* F7 : DIV, IDIV : rw EDX, rw EAX */ 91 {{ OP_R | OP_W, REG_DWORD_OFFSET + 2 }, 92 { OP_R | OP_W, REG_DWORD_OFFSET }, {0}}; /* div */ 93 94static op_implicit_list_t list_enter[] = 95 /* C8 : ENTER : rw ESP w EBP */ 96 {{ OP_R | OP_W, REG_DWORD_OFFSET + 4 }, 97 { OP_R, REG_DWORD_OFFSET + 5 }, {0}}; /* enter */ 98 99static op_implicit_list_t list_f2xm1[] = 100 /* D9 F0 : F2XM1 : rw ST(0) */ 101 /* D9 E1 : FABS : rw ST(0) */ 102 /* D9 E0 : FCHS : rw ST(0) */ 103 /* D9 FF : FCOS : rw ST(0)*/ 104 /* D8, DA : FDIV : rw ST(0) */ 105 /* D8, DA : FDIVR : rw ST(0) */ 106 /* D9 F2 : FPTAN : rw ST(0) */ 107 /* D9 FC : FRNDINT : rw ST(0) */ 108 /* D9 FB : FSINCOS : rw ST(0) */ 109 /* D9 FE : FSIN : rw ST(0) */ 110 /* D9 FA : FSQRT : rw ST(0) */ 111 /* D9 F4 : FXTRACT : rw ST(0) */ 112 {{ OP_R | OP_W, REG_FPU_OFFSET }, {0}}; /* f2xm1 */ 113 114static op_implicit_list_t list_fcom[] = 115 /* D8, DC, DE D9 : FCOM : r ST(0) */ 116 /* DE, DA : FICOM : r ST(0) */ 117 /* DF, D8 : FIST : r ST(0) */ 118 /* D9 E4 : FTST : r ST(0) */ 119 /* D9 E5 : FXAM : r ST(0) */ 120 {{ OP_R, REG_FPU_OFFSET }, {0}}; /* fcom */ 121 122static op_implicit_list_t list_fpatan[] = 123 /* D9 F3 : FPATAN : r ST(0), rw ST(1) */ 124 {{ OP_R, REG_FPU_OFFSET }, {0}}; /* fpatan */ 125 126static op_implicit_list_t list_fprem[] = 127 /* D9 F8, D9 F5 : FPREM : rw ST(0) r ST(1) */ 128 /* D9 FD : FSCALE : rw ST(0), r ST(1) */ 129 {{ OP_R | OP_W, REG_FPU_OFFSET }, 130 { OP_R, REG_FPU_OFFSET + 1 }, {0}}; /* fprem */ 131 132static op_implicit_list_t list_faddp[] = 133 /* DE C1 : FADDP : r ST(0), rw ST(1) */ 134 /* DE E9 : FSUBP : r ST(0), rw ST(1) */ 135 /* D9 F1 : FYL2X : r ST(0), rw ST(1) */ 136 /* D9 F9 : FYL2XP1 : r ST(0), rw ST(1) */ 137 {{ OP_R, REG_FPU_OFFSET }, 138 { OP_R | OP_W, REG_FPU_OFFSET + 1 }, {0}}; /* faddp */ 139 140static op_implicit_list_t list_fucompp[] = 141 /* DA E9 : FUCOMPP : r ST(0), r ST(1) */ 142 {{ OP_R, REG_FPU_OFFSET }, 143 { OP_R, REG_FPU_OFFSET + 1 }, {0}}; /* fucompp */ 144 145static op_implicit_list_t list_imul[] = 146 /* F6 : IMUL : r AL, w AX */ 147 /* F6 : MUL : r AL, w AX */ 148 {{ OP_R, REG_BYTE_OFFSET }, 149 { OP_W, REG_WORD_OFFSET }, {0}}; /* imul */ 150 151static op_implicit_list_t list_mul[] = 152 /* F7 : IMUL : rw EAX, w EDX */ 153 /* F7 : MUL : rw EAX, w EDX */ 154 {{ OP_R | OP_W, REG_DWORD_OFFSET }, 155 { OP_W, REG_DWORD_OFFSET + 2 }, {0}}; /* imul */ 156 157static op_implicit_list_t list_lahf[] = 158 /* 9F : LAHF : r EFLAGS, w AH */ 159 {{ OP_R, REG_FLAGS_INDEX }, 160 { OP_W, REG_BYTE_OFFSET + 4 }, {0}}; /* lahf */ 161 162static op_implicit_list_t list_ldmxcsr[] = 163 /* 0F AE : LDMXCSR : w MXCSR SSE Control Status Reg */ 164 {{ OP_W, REG_MXCSG_INDEX }, {0}}; /* ldmxcsr */ 165 166static op_implicit_list_t list_leave[] = 167 /* C9 : LEAVE : rw ESP, w EBP */ 168 {{ OP_R | OP_W, REG_ESP_INDEX }, 169 { OP_W, REG_DWORD_OFFSET + 5 }, {0}}; /* leave */ 170 171static op_implicit_list_t list_lgdt[] = 172 /* 0F 01 : LGDT : w GDTR */ 173 {{ OP_W, REG_GDTR_INDEX }, {0}}; /* lgdt */ 174 175static op_implicit_list_t list_lidt[] = 176 /* 0F 01 : LIDT : w IDTR */ 177 {{ OP_W, REG_IDTR_INDEX }, {0}}; /* lidt */ 178 179static op_implicit_list_t list_lldt[] = 180 /* 0F 00 : LLDT : w LDTR */ 181 {{ OP_W, REG_LDTR_INDEX }, {0}}; /* lldt */ 182 183static op_implicit_list_t list_lmsw[] = 184 /* 0F 01 : LMSW : w CR0 */ 185 {{ OP_W, REG_CTRL_OFFSET }, {0}}; /* lmsw */ 186 187static op_implicit_list_t list_loop[] = 188 /* E0, E1, E2 : LOOP : rw ECX */ 189 {{ OP_R | OP_W, REG_DWORD_OFFSET + 1 }, {0}};/* loop */ 190 191static op_implicit_list_t list_ltr[] = 192 /* 0F 00 : LTR : w Task Register */ 193 {{ OP_W, REG_TR_INDEX }, {0}}; /* ltr */ 194 195static op_implicit_list_t list_pop[] = 196 /* 8F, 58, 1F, 07, 17, 0F A1, 0F A9 : POP : rw ESP */ 197 /* FF, 50, 6A, 68, 0E, 16, 1E, 06, 0F A0, 0F A8 : PUSH : rw ESP */ 198 {{ OP_R | OP_W, REG_ESP_INDEX }, {0}}; /* pop, push */ 199 200static op_implicit_list_t list_popad[] = 201 /* 61 : POPAD : rw esp, w edi esi ebp ebx edx ecx eax */ 202 {{ OP_R | OP_W, REG_ESP_INDEX }, 203 { OP_W, REG_DWORD_OFFSET + 7 }, 204 { OP_W, REG_DWORD_OFFSET + 6 }, 205 { OP_W, REG_DWORD_OFFSET + 5 }, 206 { OP_W, REG_DWORD_OFFSET + 3 }, 207 { OP_W, REG_DWORD_OFFSET + 2 }, 208 { OP_W, REG_DWORD_OFFSET + 1 }, 209 { OP_W, REG_DWORD_OFFSET }, {0}}; /* popad */ 210 211static op_implicit_list_t list_popfd[] = 212 /* 9D : POPFD : rw esp, w eflags */ 213 {{ OP_R | OP_W, REG_ESP_INDEX }, 214 { OP_W, REG_FLAGS_INDEX }, {0}}; /* popfd */ 215 216static op_implicit_list_t list_pushad[] = 217 /* FF, 50, 6A, 68, 0E, 16, 1E, 06, 0F A0, 0F A8 : PUSH : rw ESP */ 218 /* 60 : PUSHAD : rw esp, r eax ecx edx ebx esp ebp esi edi */ 219 {{ OP_R | OP_W, REG_ESP_INDEX }, 220 { OP_R, REG_DWORD_OFFSET }, 221 { OP_R, REG_DWORD_OFFSET + 1 }, 222 { OP_R, REG_DWORD_OFFSET + 2 }, 223 { OP_R, REG_DWORD_OFFSET + 3 }, 224 { OP_R, REG_DWORD_OFFSET + 5 }, 225 { OP_R, REG_DWORD_OFFSET + 6 }, 226 { OP_R, REG_DWORD_OFFSET + 7 }, {0}}; /* pushad */ 227 228static op_implicit_list_t list_pushfd[] = 229 /* 9C : PUSHFD : rw esp, r eflags */ 230 {{ OP_R | OP_W, REG_ESP_INDEX }, 231 { OP_R, REG_FLAGS_INDEX }, {0}}; /* pushfd */ 232 233static op_implicit_list_t list_rdmsr[] = 234 /* 0F 32 : RDMSR : r ECX, w EDX, w EAX */ 235 {{ OP_R, REG_DWORD_OFFSET + 1 }, 236 { OP_W, REG_DWORD_OFFSET + 2 }, 237 { OP_W, REG_DWORD_OFFSET }, {0}}; /* rdmsr */ 238 239static op_implicit_list_t list_rdpmc[] = 240 /* 0F 33 : RDPMC : r ECX, w EDX, w EAX */ 241 {{ OP_R, REG_DWORD_OFFSET + 1 }, 242 { OP_W, REG_DWORD_OFFSET + 2 }, 243 { OP_W, REG_DWORD_OFFSET }, {0}}; /* rdpmc */ 244 245static op_implicit_list_t list_rdtsc[] = 246 /* 0F 31 : RDTSC : rw EDX, rw EAX */ 247 {{ OP_R | OP_W, REG_DWORD_OFFSET + 2 }, 248 { OP_R | OP_W, REG_DWORD_OFFSET }, {0}}; /* rdtsc */ 249 250static op_implicit_list_t list_rep[] = 251 /* F3, F2 ... : REP : rw ECX */ 252 {{ OP_R | OP_W, REG_DWORD_OFFSET + 1 }, {0}};/* rep */ 253 254static op_implicit_list_t list_rsm[] = 255 /* 0F AA : RSM : r CR4, r CR0 */ 256 {{ OP_R, REG_CTRL_OFFSET + 4 }, 257 { OP_R, REG_CTRL_OFFSET }, {0}}; /* rsm */ 258 259static op_implicit_list_t list_sahf[] = 260 /* 9E : SAHF : r ah, rw eflags (set SF ZF AF PF CF) */ 261 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sahf */ 262 263static op_implicit_list_t list_sgdt[] = 264 /* 0F : SGDT : r gdtr */ 265 /* TODO: finish this! */ 266 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sgdt */ 267 268static op_implicit_list_t list_sidt[] = 269 /* 0F : SIDT : r idtr */ 270 /* TODO: finish this! */ 271 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sidt */ 272 273static op_implicit_list_t list_sldt[] = 274 /* 0F : SLDT : r ldtr */ 275 /* TODO: finish this! */ 276 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sldt */ 277 278static op_implicit_list_t list_smsw[] = 279 /* 0F : SMSW : r CR0 */ 280 /* TODO: finish this! */ 281 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* smsw */ 282 283static op_implicit_list_t list_stmxcsr[] = 284 /* 0F AE : STMXCSR : r MXCSR */ 285 /* TODO: finish this! */ 286 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* stmxcsr */ 287 288static op_implicit_list_t list_str[] = 289 /* 0F 00 : STR : r TR (task register) */ 290 /* TODO: finish this! */ 291 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* str */ 292 293static op_implicit_list_t list_sysenter[] = 294 /* 0F 34 : SYSENTER : w cs, w eip, w ss, w esp, r CR0, w eflags 295 * r sysenter_cs_msr, sysenter_esp_msr, sysenter_eip_msr */ 296 /* TODO: finish this! */ 297 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sysenter */ 298 299static op_implicit_list_t list_sysexit[] = 300 /* 0F 35 : SYSEXIT : r edx, r ecx, w cs, w eip, w ss, w esp 301 * r sysenter_cs_msr */ 302 /* TODO: finish this! */ 303 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sysexit */ 304 305static op_implicit_list_t list_wrmsr[] = 306 /* 0F 30 : WRMST : r edx, r eax, r ecx */ 307 /* TODO: finish this! */ 308 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* wrmsr */ 309 310static op_implicit_list_t list_xlat[] = 311 /* D7 : XLAT : rw al r ebx (ptr) */ 312 /* TODO: finish this! */ 313 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* xlat */ 314/* TODO: 315 * monitor 0f 01 c8 eax OP_R ecx OP_R edx OP_R 316 * mwait 0f 01 c9 eax OP_R ecx OP_R 317 */ 318static op_implicit_list_t list_monitor[] = 319 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* monitor */ 320static op_implicit_list_t list_mwait[] = 321 {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* mwait */ 322 323op_implicit_list_t *op_implicit_list[] = { 324 /* This is a list of implicit operands which are read/written by 325 * various x86 instructions. Note that modifications to the stack 326 * register are mentioned here, but that additional information on 327 * the effect an instruction has on the stack is contained in the 328 * x86_insn_t 'stack_mod' and 'stack_mod_val' fields. Use of the 329 * eflags register, i.e. setting, clearing, and testing flags, is 330 * not recorded here but rather in the flags_set and flags_tested 331 * fields of the x86_insn_t.*/ 332 NULL, 333 list_aaa, list_aad, list_call, list_cbw, /* 1 - 4 */ 334 list_cwde, list_clts, list_cmpxchg, list_cmpxchgb, /* 5 - 8 */ 335 list_cmpxchg8b, list_cpuid, list_cwd, list_daa, /* 9 - 12 */ 336 list_idiv, list_div, list_enter, list_f2xm1, /* 13 - 16 */ 337 list_fcom, list_fpatan, list_fprem, list_faddp, /* 17 - 20 */ 338 list_fucompp, list_imul, list_mul, list_lahf, /* 21 - 24 */ 339 list_ldmxcsr, list_leave, list_lgdt, list_lidt, /* 25 - 28 */ 340 list_lldt, list_lmsw, list_loop, list_ltr, /* 29 - 32 */ 341 list_pop, list_popad, list_popfd, list_pushad, /* 33 - 36 */ 342 list_pushfd, list_rdmsr, list_rdpmc, list_rdtsc, /* 37 - 40 */ 343 /* NOTE: 'REP' is a hack since it is a prefix: if its position 344 * in the table changes, then change IDX_IMPLICIT_REP in the .h */ 345 list_rep, list_rsm, list_sahf, list_sgdt, /* 41 - 44 */ 346 list_sidt, list_sldt, list_smsw, list_stmxcsr, /* 45 - 48 */ 347 list_str, list_sysenter, list_sysexit, list_wrmsr, /* 49 - 52 */ 348 list_xlat, list_monitor, list_mwait, /* 53 - 55*/ 349 NULL /* end of list */ 350 }; 351 352#define LAST_IMPL_IDX 55 353 354static void handle_impl_reg( x86_op_t *op, uint32_t val ) { 355 x86_reg_t *reg = &op->data.reg; 356 op->type = op_register; 357 ia32_handle_register( reg, (unsigned int) val ); 358 switch (reg->size) { 359 case 1: 360 op->datatype = op_byte; break; 361 case 2: 362 op->datatype = op_word; break; 363 case 4: 364 op->datatype = op_dword; break; 365 case 8: 366 op->datatype = op_qword; break; 367 case 10: 368 op->datatype = op_extreal; break; 369 case 16: 370 op->datatype = op_dqword; break; 371 } 372 return; 373} 374 375/* 'impl_idx' is the value from the opcode table: between 1 and LAST_IMPL_IDX */ 376/* returns number of operands added */ 377unsigned int ia32_insn_implicit_ops( x86_insn_t *insn, unsigned int impl_idx ) { 378 op_implicit_list_t *list; 379 x86_op_t *op; 380 unsigned int num = 0; 381 382 if (! impl_idx || impl_idx > LAST_IMPL_IDX ) { 383 return 0; 384 } 385 386 for ( list = op_implicit_list[impl_idx]; list->type; list++, num++ ) { 387 enum x86_op_access access = (enum x86_op_access) OP_PERM(list->type); 388 enum x86_op_flags flags = (enum x86_op_flags) (OP_FLAGS(list->type) >> 12); 389 390 op = NULL; 391 /* In some cases (MUL), EAX is an implicit operand hardcoded in 392 * the instruction without being explicitly listed in assembly. 393 * For this situation, find the hardcoded operand and add the 394 * implied flag rather than adding a new implicit operand. */ 395 x86_oplist_t * existing; 396 if (ia32_true_register_id(list->operand) == REG_DWORD_OFFSET) { 397 for ( existing = insn->operands; existing; existing = existing->next ) { 398 if (existing->op.type == op_register && 399 existing->op.data.reg.id == list->operand) { 400 op = &existing->op; 401 break; 402 } 403 } 404 } 405 if (!op) { 406 op = x86_operand_new( insn ); 407 /* all implicit operands are registers */ 408 handle_impl_reg( op, list->operand ); 409 /* decrement the 'explicit count' incremented by default in 410 * x86_operand_new */ 411 insn->explicit_count = insn->explicit_count -1; 412 } 413 if (!op) { 414 return num; /* gah! return early */ 415 } 416 op->access |= access; 417 op->flags |= flags; 418 op->flags |= op_implied; 419 } 420 421 return num; 422} 423