aboutsummaryrefslogtreecommitdiff
path: root/src/third_party/libdisasm/x86_operand_list.c
blob: 95409e0698f3fc38a97560cd1177342bf4062b13 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#include <stdlib.h>
#include "libdis.h"


static void x86_oplist_append( x86_insn_t *insn, x86_oplist_t *op ) {
	x86_oplist_t *list;

	if (! insn ) {	
		return;
	}

	list = insn->operands;
	if (! list ) {
		insn->operand_count = 1;
		/* Note that we have no way of knowing if this is an
		 * exlicit operand or not, since the caller fills 
		 * the x86_op_t after we return. We increase the
		 * explicit count automatically, and ia32_insn_implicit_ops
		 * decrements it */
		insn->explicit_count = 1;
		insn->operands = op;
		return;
	}

	/* get to end of list */
	for ( ; list->next; list = list->next ) 
		;

	insn->operand_count = insn->operand_count + 1;
	insn->explicit_count = insn->explicit_count + 1;
	list->next = op;

	return;
}
	
x86_op_t * x86_operand_new( x86_insn_t *insn ) {
	x86_oplist_t *op;

	if (! insn ) {	
		return(NULL);
	}
	op = calloc( sizeof(x86_oplist_t), 1 );
	op->op.insn = insn;
	x86_oplist_append( insn, op );
	return( &(op->op) );
}

void x86_oplist_free( x86_insn_t *insn ) {
	x86_oplist_t *op, *list;

	if (! insn ) {
		return;
	}

	for ( list = insn->operands; list; ) {
		op = list;
		list = list->next;
		free(op);
	}

	insn->operands = NULL;
	insn->operand_count = 0;
	insn->explicit_count = 0;

	return;
}

/* ================================================== LIBDISASM API */
/* these could probably just be #defines, but that means exposing the
   enum... yet one more confusing thing in the API */
int x86_operand_foreach( x86_insn_t *insn, x86_operand_fn func, void *arg, 
	       		enum x86_op_foreach_type type ){
	x86_oplist_t *list;
	char explicit = 1, implicit = 1;

	if (! insn || ! func ) {
		return 0;
	}
	
	/* note: explicit and implicit can be ORed together to
	 * allow an "all" limited by access type, even though the
	 * user is stupid to do this since it is default behavior :) */
	if ( (type & op_explicit) && ! (type & op_implicit) ) {
		implicit = 0;
	}
	if ( (type & op_implicit) && ! (type & op_explicit) ) {
		explicit = 0;
	}

	type = type & 0x0F; /* mask out explicit/implicit operands */

	for ( list = insn->operands; list; list = list->next ) {
		if (! implicit && (list->op.flags & op_implied) ) {
			/* operand is implicit */
			continue;
		}

		if (! explicit && ! (list->op.flags & op_implied) ) {
			/* operand is not implicit */
			continue;
		}

		switch ( type ) {
			case op_any:
				break;
			case op_dest:
				if (! (list->op.access & op_write) ) {
					continue;
				}
				break;
			case op_src:
				if (! (list->op.access & op_read) ) {
					continue;
				}
				break;
			case op_ro:
				if (! (list->op.access & op_read) ||
				      (list->op.access & op_write ) ) {
					continue;
				}
				break;
			case op_wo:
				if (! (list->op.access & op_write) ||
				      (list->op.access & op_read ) ) {
					continue;
				}
				break;
			case op_xo:
				if (! (list->op.access & op_execute) ) {
					continue;
				}
				break;
			case op_rw:
				if (! (list->op.access & op_write) ||
				    ! (list->op.access & op_read ) ) {
					continue;
				}
				break;
			case op_implicit: case op_explicit: /* make gcc happy */
					  break;
		}
		/* any non-continue ends up here: invoke the callback */
		(*func)( &list->op, insn, arg );
	}

	return 1;
}

static void count_operand( x86_op_t *op, x86_insn_t *insn, void *arg ) {
	size_t * count = (size_t *) arg;
	*count = *count + 1;
}

size_t x86_operand_count( x86_insn_t *insn, enum x86_op_foreach_type type ) {
	size_t count = 0;
	
	/* save us a list traversal for common counts... */
	if ( type == op_any ) {
		return insn->operand_count;
	} else if ( type == op_explicit ) {
		return insn->explicit_count;
	}

	x86_operand_foreach( insn, count_operand, &count, type );
	return count;
}

/* accessor functions */
x86_op_t * x86_operand_1st( x86_insn_t *insn ) {
	if (! insn->explicit_count ) {
		return NULL;
	}

	return &(insn->operands->op);
}

x86_op_t * x86_operand_2nd( x86_insn_t *insn ) {
	if ( insn->explicit_count < 2 ) {
		return NULL;
	}

	return &(insn->operands->next->op);
}

x86_op_t * x86_operand_3rd( x86_insn_t *insn ) {
	if ( insn->explicit_count < 3 ) {
		return NULL;
	}

	return &(insn->operands->next->next->op);
}