-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathceval.py
291 lines (233 loc) · 6.82 KB
/
ceval.py
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
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
from pycparser.c_parser import CParser
from pycparser.c_ast import *
import struct
class AstTranslator(object):
def process(self, ast):
node = ast.__class__.__name__
if hasattr(self, node):
func = getattr(self, node)
fcode = func.im_func.func_code
argnames = fcode.co_varnames[1:fcode.co_argcount]
args = [getattr(ast, name) for name in argnames]
return func(*args)
else:
print 'Unhandled AST node:'
ast.show()
return '?unknown?'
def UnaryOp(self, op, expr):
opmap = {'*' : 'deref', '-' : 'neg', '!' : 'not', '~' : 'bnot'}
return opmap[op], self.process(expr)
def BinaryOp(self, op, left, right):
return op, self.process(left), self.process(right)
def Cast(self, to_type, expr):
return 'cast', self.process(to_type), self.process(expr)
def ID(self, name):
return 'register', name
def Typename(self, type):
return self.process(type)
def TypeDecl(self, type):
return self.process(type)
def IdentifierType(self, names):
return ' '.join(names)
def PtrDecl(self, type):
return 'ptr', self.process(type)
def ArrayRef(self, name, subscript):
return '[]', self.process(name), self.process(subscript)
def Constant(self, type, value):
if type == 'int':
if value.startswith('0x'):
return int(value[2:], 16)
elif value.startswith('0b'):
return int(value[2:], 2)
elif value.startswith('0'):
return int(value, 8)
else:
return int(value)
elif type == 'float':
return float(value)
else:
print 'Unknown constant type:', type, `value`
return '?unkconst?'
def Assignment(self, op, lvalue, rvalue):
lvalue = self.process(lvalue)
rvalue = self.process(rvalue)
if op != '=':
rvalue = op[0], lvalue, rvalue
return '=', lvalue, rvalue
dispatchers = {}
def ddp(name):
def sub(func):
dispatchers[name] = func
return func
if callable(name):
dispatchers[name.func_name] = name
return name
return sub
class Register(object):
def __init__(self, name):
self.name = name
def __repr__(self):
return self.name
class TypedValue(object):
def __init__(self, type, value):
self.type, self.value = type, value
while isinstance(self.value, TypedValue):
self.value = self.value.value
def __repr__(self):
return '%s:%r' % (self.type, self.value)
@property
def stride(self):
return int(self.type[1:].rstrip('*')) / 8
@property
def pointer(self):
return '*' in self.type
def bare(value):
if isinstance(value, TypedValue):
return value.value
else:
return value
def autotype(value):
if isinstance(value, TypedValue):
return value
elif isinstance(value, float):
return TypedValue('f64', value)
elif isinstance(value, str):
return value
else:
return TypedValue('i64', value)
class SexpRunner(object):
def __init__(self, ctu):
self.ctu = ctu
def run(self, sexp, rvalue=None):
if not isinstance(sexp, tuple):
return autotype(sexp)
if sexp[0] in dispatchers:
if rvalue is None:
return dispatchers[sexp[0]](self, *sexp[1:])
else:
return dispatchers[sexp[0]](self, *tuple(list(sexp[1:]) + [rvalue]))
else:
print 'Unhandled S-exp:', sexp
return None
@ddp('=')
def assign(self, left, right):
return self.run(left, self.run(right))
@ddp('[]')
def subscript(self, base, sub, ass=None):
base, sub = self.run(base), self.run(sub)
addr = bare(base) + bare(sub) * base.stride
return self.deref(TypedValue(base.type, addr), ass)
@ddp
def deref(self, ptr, ass=None):
ptr = self.run(ptr)
assert ptr.pointer
fmtmap = dict(u8='B', i8='b', u16='H', i16='h', u32='I', i32='i', u64='L', i64='l', f32='f', f64='d')
fmt = fmtmap[ptr.type.rstrip('*')]
size = struct.calcsize(fmt)
if ass is None:
data = self.ctu.readmem(bare(ptr), size)
return TypedValue(ptr.type[:-1], struct.unpack(fmt, data)[0])
else:
self.ctu.writemem(bare(ptr), struct.pack(fmt, bare(ass)))
@ddp
def register(self, name, ass=None):
name = name.upper()
if name == 'PC':
if ass is None:
return TypedValue('u64', self.ctu.pc)
else:
self.ctu.pc = bare(ass)
else:
typemap = dict(X='u64', W='u32', D='f64', Q='f128')
assert name[0] in 'XW' # XXX: Add float support
if ass is None:
type = typemap[name[0]]
value = self.ctu.reg(int(name[1:]))
if type == 'u32':
value &= 0xFFFFFFFF
return TypedValue(type, value)
else:
self.ctu.reg(int(name[1:]), bare(ass))
@ddp
def cast(self, type, value):
return TypedValue(self.run(type), self.run(value))
@ddp
def ptr(self, type):
return self.run(type) + '*'
@ddp('+')
def add(self, a, b):
a, b = self.run(a), self.run(b)
if b.pointer and not a.pointer:
a, b = b, a
if a.pointer and not b.pointer:
return TypedValue(a.type, bare(a) + bare(b) * a.stride)
return TypedValue(a.type, bare(a) + bare(b))
@ddp('-')
def sub(self, a, b):
a, b = self.run(a), self.run(b)
if b.pointer and not a.pointer:
return TypedValue(b.type, bare(a) * b.stride - bare(b))
elif a.pointer and not b.pointer:
return TypedValue(a.type, bare(a) - bare(b) * a.stride)
return TypedValue(a.type, bare(a) + bare(b))
@ddp('*')
def mul(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue(a.type, bare(a) * bare(b))
@ddp('/')
def div(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue(a.type, bare(a) / bare(b))
@ddp('==')
def eq(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value == b.value else 0)
@ddp('!=')
def ne(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value != b.value else 0)
@ddp('>')
def gt(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value > b.value else 0)
@ddp('>=')
def ge(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value >= b.value else 0)
@ddp('<')
def lt(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value < b.value else 0)
@ddp('<=')
def le(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if a.value <= b.value else 0)
@ddp('&&')
def booland(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if bool(a.value) and bool(b.value) else 0)
@ddp('||')
def boolor(self, a, b):
a, b = self.run(a), self.run(b)
return TypedValue('i64', 1 if bool(a.value) or bool(b.value) else 0)
def compile(code):
parser = CParser()
stypes = 'u8 i8 u16 i16 u32 i32 u64 i64 f32 f64 f128'
code = 'void runner() { ' + code + ' ; }'
for type in stypes.split(' '):
code = 'typedef void %s; %s' % (type, code)
ast = parser.parse(code)
found = None
for _, child in ast.children():
if isinstance(child, FuncDef):
found = child
break
assert found is not None
assert len(found.body.children()) == 1
ast = found.body.children()[0][1]
sexp = AstTranslator().process(ast)
def run(ctu):
return bare(SexpRunner(ctu).run(sexp))
return run
def ceval(code, ctu):
return compile(code)(ctu)