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