mirror of
https://github.com/cea-sec/miasm
synced 2026-06-21 13:48:18 +00:00
1640 lines
46 KiB
Python
1640 lines
46 KiB
Python
#-*- coding:utf-8 -*-
|
|
|
|
import re
|
|
import struct
|
|
import logging
|
|
from collections import defaultdict
|
|
|
|
import pyparsing
|
|
|
|
import miasm2.expression.expression as m2_expr
|
|
from miasm2.core import asmblock
|
|
from miasm2.core.bin_stream import bin_stream, bin_stream_str
|
|
from miasm2.core.utils import Disasm_Exception
|
|
from miasm2.expression.simplifications import expr_simp
|
|
|
|
log = logging.getLogger("cpuhelper")
|
|
console_handler = logging.StreamHandler()
|
|
console_handler.setFormatter(logging.Formatter("%(levelname)-5s: %(message)s"))
|
|
log.addHandler(console_handler)
|
|
log.setLevel(logging.WARN)
|
|
|
|
|
|
class bitobj:
|
|
|
|
def __init__(self, s=""):
|
|
if not s:
|
|
bits = []
|
|
else:
|
|
bits = list(bin(int(str(s).encode('hex'), 16))[2:])
|
|
bits = [int(x) for x in bits]
|
|
if len(bits) % 8:
|
|
bits = [0 for x in xrange(8 - (len(bits) % 8))] + bits
|
|
bits = ['0' for x in xrange(len(s) * 8 - len(bits))] + bits
|
|
self.bits = bits
|
|
self.offset = 0
|
|
|
|
def __len__(self):
|
|
return len(self.bits) - self.offset
|
|
|
|
def getbits(self, n):
|
|
if not n:
|
|
return 0
|
|
if n > len(self.bits) - self.offset:
|
|
raise ValueError('not enought bits %r %r' % (n, len(self.bits)))
|
|
b = self.bits[self.offset:self.offset + n]
|
|
b = int("".join([str(x) for x in b]), 2)
|
|
self.offset += n
|
|
return b
|
|
|
|
def putbits(self, b, n):
|
|
if not n:
|
|
return
|
|
bits = list(bin(b)[2:])
|
|
bits = [int(x) for x in bits]
|
|
bits = [0 for x in xrange(n - len(bits))] + bits
|
|
self.bits += bits
|
|
|
|
def tostring(self):
|
|
if len(self.bits) % 8:
|
|
raise ValueError(
|
|
'num bits must be 8 bit aligned: %d' % len(self.bits))
|
|
b = int("".join([str(x) for x in self.bits]), 2)
|
|
b = "%X" % b
|
|
b = '0' * (len(self.bits) / 4 - len(b)) + b
|
|
b = b.decode('hex')
|
|
return b
|
|
|
|
def reset(self):
|
|
self.offset = 0
|
|
|
|
def copy_state(self):
|
|
b = self.__class__()
|
|
b.bits = self.bits
|
|
b.offset = self.offset
|
|
return b
|
|
|
|
|
|
def literal_list(l):
|
|
l = l[:]
|
|
l.sort()
|
|
l = l[::-1]
|
|
o = pyparsing.Literal(l[0])
|
|
for x in l[1:]:
|
|
o |= pyparsing.Literal(x)
|
|
return o
|
|
|
|
|
|
class reg_info:
|
|
|
|
def __init__(self, reg_str, reg_expr):
|
|
self.str = reg_str
|
|
self.expr = reg_expr
|
|
self.parser = literal_list(reg_str).setParseAction(self.reg2expr)
|
|
|
|
def reg2expr(self, s):
|
|
i = self.str.index(s[0])
|
|
return self.expr[i]
|
|
|
|
def expr2regi(self, e):
|
|
return self.expr.index(e)
|
|
|
|
|
|
def gen_reg(rname, env, sz=32):
|
|
"""
|
|
Gen reg expr and parser
|
|
Equivalent to:
|
|
PC = ExprId('PC')
|
|
reg_pc_str = ['PC']
|
|
reg_pc_expr = [ExprId(x, sz) for x in reg_pc_str]
|
|
regpc = reg_info(reg_pc_str, reg_pc_expr)
|
|
|
|
class bs_rname(m_reg):
|
|
reg = regi_rname
|
|
|
|
bsrname = bs(l=0, cls=(bs_rname,))
|
|
|
|
"""
|
|
rnamel = rname.lower()
|
|
r = m2_expr.ExprId(rname, sz)
|
|
reg_str = [rname]
|
|
reg_expr = [r]
|
|
regi = reg_info(reg_str, reg_expr)
|
|
# define as global val
|
|
cname = "bs_" + rnamel
|
|
c = type(cname, (m_reg,), {'reg': regi})
|
|
env[rname] = r
|
|
env["regi_" + rnamel] = regi
|
|
env[cname] = c
|
|
env["bs" + rnamel] = bs(l=0, cls=(c,))
|
|
return r, regi
|
|
|
|
|
|
def gen_regs(rnames, env, sz=32):
|
|
regs_str = []
|
|
regs_expr = []
|
|
regs_init = []
|
|
for rname in rnames:
|
|
r = m2_expr.ExprId(rname, sz)
|
|
r_init = m2_expr.ExprId(rname+'_init', sz)
|
|
regs_str.append(rname)
|
|
regs_expr.append(r)
|
|
regs_init.append(r_init)
|
|
env[rname] = r
|
|
|
|
reginfo = reg_info(regs_str, regs_expr)
|
|
return regs_expr, regs_init, reginfo
|
|
|
|
|
|
LPARENTHESIS = pyparsing.Literal("(")
|
|
RPARENTHESIS = pyparsing.Literal(")")
|
|
|
|
|
|
def int2expr(t):
|
|
v = t[0]
|
|
return (m2_expr.ExprInt, v)
|
|
|
|
|
|
def parse_op(t):
|
|
v = t[0]
|
|
return (m2_expr.ExprOp, v)
|
|
|
|
|
|
def parse_id(t):
|
|
v = t[0]
|
|
return (m2_expr.ExprId, v)
|
|
|
|
|
|
def ast_parse_op(t):
|
|
if len(t) == 1:
|
|
return t[0]
|
|
if len(t) == 2:
|
|
if t[0] in ['-', '+', '!']:
|
|
return m2_expr.ExprOp(t[0], t[1])
|
|
if len(t) == 3:
|
|
if t[1] == '-':
|
|
# a - b => a + (-b)
|
|
t[1] = '+'
|
|
t[2] = - t[2]
|
|
return m2_expr.ExprOp(t[1], t[0], t[2])
|
|
t = t[::-1]
|
|
while len(t) >= 3:
|
|
o1, op, o2 = t.pop(), t.pop(), t.pop()
|
|
if op == '-':
|
|
# a - b => a + (-b)
|
|
op = '+'
|
|
o2 = - o2
|
|
e = m2_expr.ExprOp(op, o1, o2)
|
|
t.append(e)
|
|
if len(t) != 1:
|
|
raise NotImplementedError('strange op')
|
|
return t[0]
|
|
|
|
|
|
def ast_id2expr(a):
|
|
return m2_expr.ExprId(a, 32)
|
|
|
|
|
|
def ast_int2expr(a):
|
|
return m2_expr.ExprInt(a, 32)
|
|
|
|
|
|
|
|
class ParseAst(object):
|
|
|
|
def __init__(self, id2expr, int2expr, default_size=32):
|
|
self.id2expr = id2expr
|
|
self.int2expr = int2expr
|
|
self.default_size = default_size
|
|
|
|
def int_from_size(self, size, value):
|
|
"""Transform a string into ExprInt.
|
|
* if @size is None, use provided int2expr
|
|
* else, use @size to generate integer
|
|
@size: size of int; None if not forced.
|
|
@value: string representing an integer
|
|
"""
|
|
if size is None:
|
|
return self.int2expr(value)
|
|
else:
|
|
return m2_expr.ExprInt(value, size)
|
|
|
|
def id_from_size(self, size, value):
|
|
"""Transform a string into ExprId.
|
|
* if @size is None, use provided id2expr
|
|
* else, use @size to generate id
|
|
@size: size of id; None if not forced.
|
|
@value: string representing the id
|
|
"""
|
|
value = self.id2expr(value)
|
|
if isinstance(value, m2_expr.Expr):
|
|
return value
|
|
if size is None:
|
|
size = self.default_size
|
|
assert value is not None
|
|
return m2_expr.ExprId(asmblock.AsmLabel(value), size)
|
|
|
|
def ast_to_expr(self, size, ast):
|
|
"""Transform a typed ast into a Miasm expression
|
|
@size: default size
|
|
@ast: typed ast
|
|
"""
|
|
assert(isinstance(ast, tuple))
|
|
if ast[0] is m2_expr.ExprId:
|
|
expr = self.id_from_size(size, ast[1])
|
|
if isinstance(expr, str):
|
|
expr = self.id_from_size(size, expr)
|
|
elif ast[0] is m2_expr.ExprInt:
|
|
expr = self.int_from_size(size, ast[1])
|
|
elif ast[0] is m2_expr.ExprOp:
|
|
out = []
|
|
for arg in ast[1]:
|
|
if isinstance(arg, tuple):
|
|
arg = self.ast_to_expr(size, arg)
|
|
out.append(arg)
|
|
expr = ast_parse_op(out)
|
|
else:
|
|
raise TypeError('unknown type')
|
|
return expr
|
|
|
|
def ast_get_ids(self, ast):
|
|
"""Retrieve every node of type ExprId in @ast
|
|
@ast: typed ast
|
|
"""
|
|
assert(isinstance(ast, tuple))
|
|
if ast[0] is m2_expr.ExprId:
|
|
return set([ast[1]])
|
|
elif ast[0] is m2_expr.ExprInt:
|
|
return set()
|
|
elif ast[0] is m2_expr.ExprOp:
|
|
out = set()
|
|
for x in ast[1]:
|
|
if isinstance(x, tuple):
|
|
out.update(self.ast_get_ids(x))
|
|
return out
|
|
raise TypeError('unknown type')
|
|
|
|
def _extract_ast_core(self, ast):
|
|
assert(isinstance(ast, tuple))
|
|
if ast[0] in [m2_expr.ExprInt, m2_expr.ExprId]:
|
|
return ast
|
|
elif ast[0] is m2_expr.ExprOp:
|
|
out = []
|
|
for arg in ast[1]:
|
|
if isinstance(arg, tuple):
|
|
arg = self._extract_ast_core(arg)
|
|
out.append(arg)
|
|
return tuple([ast[0]] + [out])
|
|
else:
|
|
raise TypeError('unknown type')
|
|
|
|
def extract_ast_core(self, ast):
|
|
"""
|
|
Trasform an @ast into a Miasm expression.
|
|
Use registers size to deduce label and integers sizes.
|
|
"""
|
|
ast = self._extract_ast_core(ast)
|
|
ids = self.ast_get_ids(ast)
|
|
ids_expr = [self.id2expr(x) for x in ids]
|
|
sizes = set([expr.size for expr in ids_expr
|
|
if isinstance(expr, m2_expr.Expr)])
|
|
if not sizes:
|
|
size = None
|
|
elif len(sizes) == 1:
|
|
size = sizes.pop()
|
|
else:
|
|
# Multiple sizes in ids
|
|
raise StopIteration
|
|
return self.ast_to_expr(size, ast)
|
|
|
|
def __call__(self, ast):
|
|
"""
|
|
Trasform an @ast into a Miasm expression.
|
|
Use registers size to deduce label and integers sizes.
|
|
"""
|
|
ast = ast[0]
|
|
if isinstance(ast, m2_expr.Expr):
|
|
return ast
|
|
return self.extract_ast_core(ast)
|
|
|
|
|
|
def neg_int(t):
|
|
x = -t[0]
|
|
return x
|
|
|
|
|
|
integer = pyparsing.Word(pyparsing.nums).setParseAction(lambda _a, _b, t:
|
|
int(t[0]))
|
|
hex_word = pyparsing.Literal('0x') + pyparsing.Word(pyparsing.hexnums)
|
|
hex_int = pyparsing.Combine(hex_word).setParseAction(lambda _a, _b, t:
|
|
int(t[0], 16))
|
|
|
|
# str_int = (Optional('-') + (hex_int | integer))
|
|
str_int_pos = (hex_int | integer)
|
|
str_int_neg = (pyparsing.Suppress('-') + \
|
|
(hex_int | integer)).setParseAction(neg_int)
|
|
|
|
str_int = str_int_pos | str_int_neg
|
|
str_int.setParseAction(int2expr)
|
|
|
|
logicop = pyparsing.oneOf('& | ^ >> << <<< >>>')
|
|
signop = pyparsing.oneOf('+ -')
|
|
multop = pyparsing.oneOf('* / %')
|
|
plusop = pyparsing.oneOf('+ -')
|
|
|
|
|
|
def gen_base_expr():
|
|
variable = pyparsing.Word(pyparsing.alphas + "_$.",
|
|
pyparsing.alphanums + "_")
|
|
variable.setParseAction(parse_id)
|
|
operand = str_int | variable
|
|
base_expr = pyparsing.operatorPrecedence(operand,
|
|
[("!", 1, pyparsing.opAssoc.RIGHT, parse_op),
|
|
(logicop, 2, pyparsing.opAssoc.RIGHT,
|
|
parse_op),
|
|
(signop, 1, pyparsing.opAssoc.RIGHT,
|
|
parse_op),
|
|
(multop, 2, pyparsing.opAssoc.LEFT,
|
|
parse_op),
|
|
(plusop, 2, pyparsing.opAssoc.LEFT,
|
|
parse_op),
|
|
])
|
|
return variable, operand, base_expr
|
|
|
|
|
|
variable, operand, base_expr = gen_base_expr()
|
|
|
|
my_var_parser = ParseAst(ast_id2expr, ast_int2expr)
|
|
base_expr.setParseAction(my_var_parser)
|
|
|
|
default_prio = 0x1337
|
|
|
|
|
|
def isbin(s):
|
|
return re.match('[0-1]+$', s)
|
|
|
|
|
|
def int2bin(i, l):
|
|
s = '0' * l + bin(i)[2:]
|
|
return s[-l:]
|
|
|
|
|
|
def myror32(v, r):
|
|
return ((v & 0xFFFFFFFFL) >> r) | ((v << (32 - r)) & 0xFFFFFFFFL)
|
|
|
|
|
|
def myrol32(v, r):
|
|
return ((v & 0xFFFFFFFFL) >> (32 - r)) | ((v << r) & 0xFFFFFFFFL)
|
|
|
|
|
|
class bs(object):
|
|
all_new_c = {}
|
|
prio = default_prio
|
|
|
|
def __init__(self, strbits=None, l=None, cls=None,
|
|
fname=None, order=0, flen=None, **kargs):
|
|
if fname is None:
|
|
fname = hex(id(str((strbits, l, cls, fname, order, flen, kargs))))
|
|
if strbits is None:
|
|
strbits = "" # "X"*l
|
|
elif l is None:
|
|
l = len(strbits)
|
|
if strbits and isbin(strbits):
|
|
value = int(strbits, 2)
|
|
elif 'default_val' in kargs:
|
|
value = int(kargs['default_val'], 2)
|
|
else:
|
|
value = None
|
|
allbits = list(strbits)
|
|
allbits.reverse()
|
|
fbits = 0
|
|
fmask = 0
|
|
while allbits:
|
|
a = allbits.pop()
|
|
if a == " ":
|
|
continue
|
|
fbits <<= 1
|
|
fmask <<= 1
|
|
if a in '01':
|
|
a = int(a)
|
|
fbits |= a
|
|
fmask |= 1
|
|
lmask = (1 << l) - 1
|
|
# gen conditional field
|
|
if cls:
|
|
for b in cls:
|
|
if 'flen' in b.__dict__:
|
|
flen = getattr(b, 'flen')
|
|
|
|
self.strbits = strbits
|
|
self.l = l
|
|
self.cls = cls
|
|
self.fname = fname
|
|
self.order = order
|
|
self.lmask = lmask
|
|
self.fbits = fbits
|
|
self.fmask = fmask
|
|
self.flen = flen
|
|
self.value = value
|
|
self.kargs = kargs
|
|
|
|
def __getitem__(self, item):
|
|
return getattr(self, item)
|
|
|
|
def __repr__(self):
|
|
o = self.__class__.__name__
|
|
if self.fname:
|
|
o += "_%s" % self.fname
|
|
o += "_%(strbits)s" % self
|
|
if self.cls:
|
|
o += '_' + '_'.join([x.__name__ for x in self.cls])
|
|
return o
|
|
|
|
def gen(self, parent):
|
|
c_name = 'nbsi'
|
|
if self.cls:
|
|
c_name += '_' + '_'.join([x.__name__ for x in self.cls])
|
|
bases = list(self.cls)
|
|
else:
|
|
bases = []
|
|
# bsi added at end of list
|
|
# used to use first function of added class
|
|
bases += [bsi]
|
|
k = c_name, tuple(bases)
|
|
if k in self.all_new_c:
|
|
new_c = self.all_new_c[k]
|
|
else:
|
|
new_c = type(c_name, tuple(bases), {})
|
|
self.all_new_c[k] = new_c
|
|
c = new_c(parent,
|
|
self.strbits, self.l, self.cls,
|
|
self.fname, self.order, self.lmask, self.fbits,
|
|
self.fmask, self.value, self.flen, **self.kargs)
|
|
return c
|
|
|
|
def check_fbits(self, v):
|
|
return v & self.fmask == self.fbits
|
|
|
|
@classmethod
|
|
def flen(cls, v):
|
|
raise NotImplementedError('not fully functional')
|
|
|
|
|
|
class dum_arg(object):
|
|
|
|
def __init__(self, e=None):
|
|
self.expr = e
|
|
|
|
|
|
class bsopt(bs):
|
|
|
|
def ispresent(self):
|
|
return True
|
|
|
|
|
|
class bsi(object):
|
|
|
|
def __init__(self, parent, strbits, l, cls, fname, order,
|
|
lmask, fbits, fmask, value, flen, **kargs):
|
|
self.parent = parent
|
|
self.strbits = strbits
|
|
self.l = l
|
|
self.cls = cls
|
|
self.fname = fname
|
|
self.order = order
|
|
self.lmask = lmask
|
|
self.fbits = fbits
|
|
self.fmask = fmask
|
|
self.flen = flen
|
|
self.value = value
|
|
self.kargs = kargs
|
|
self.__dict__.update(self.kargs)
|
|
|
|
def decode(self, v):
|
|
self.value = v & self.lmask
|
|
return True
|
|
|
|
def encode(self):
|
|
return True
|
|
|
|
def clone(self):
|
|
s = self.__class__(self.parent,
|
|
self.strbits, self.l, self.cls,
|
|
self.fname, self.order, self.lmask, self.fbits,
|
|
self.fmask, self.value, self.flen, **self.kargs)
|
|
s.__dict__.update(self.kargs)
|
|
if hasattr(self, 'expr'):
|
|
s.expr = self.expr
|
|
return s
|
|
|
|
def __hash__(self):
|
|
kargs = []
|
|
for k, v in self.kargs.items():
|
|
if isinstance(v, list):
|
|
v = tuple(v)
|
|
kargs.append((k, v))
|
|
l = [self.strbits, self.l, self.cls,
|
|
self.fname, self.order, self.lmask, self.fbits,
|
|
self.fmask, self.value] # + kargs
|
|
|
|
return hash(tuple(l))
|
|
|
|
|
|
class bs_divert(object):
|
|
prio = default_prio
|
|
|
|
def __init__(self, **kargs):
|
|
self.args = kargs
|
|
|
|
def __getattr__(self, item):
|
|
if item in self.__dict__:
|
|
return self.__dict__[item]
|
|
elif item in self.args:
|
|
return self.args.get(item)
|
|
else:
|
|
raise AttributeError
|
|
|
|
|
|
class bs_name(bs_divert):
|
|
prio = 1
|
|
|
|
def divert(self, i, candidates):
|
|
out = []
|
|
for cls, _, bases, dct, fields in candidates:
|
|
for new_name, value in self.args['name'].iteritems():
|
|
nfields = fields[:]
|
|
s = int2bin(value, self.args['l'])
|
|
args = dict(self.args)
|
|
args.update({'strbits': s})
|
|
f = bs(**args)
|
|
nfields[i] = f
|
|
ndct = dict(dct)
|
|
ndct['name'] = new_name
|
|
out.append((cls, new_name, bases, ndct, nfields))
|
|
return out
|
|
|
|
|
|
class bs_mod_name(bs_divert):
|
|
prio = 2
|
|
|
|
def divert(self, i, candidates):
|
|
out = []
|
|
for cls, _, bases, dct, fields in candidates:
|
|
tab = self.args['mn_mod']
|
|
if isinstance(tab, list):
|
|
tmp = {}
|
|
for j, v in enumerate(tab):
|
|
tmp[j] = v
|
|
tab = tmp
|
|
for value, new_name in tab.iteritems():
|
|
nfields = fields[:]
|
|
s = int2bin(value, self.args['l'])
|
|
args = dict(self.args)
|
|
args.update({'strbits': s})
|
|
f = bs(**args)
|
|
nfields[i] = f
|
|
ndct = dict(dct)
|
|
ndct['name'] = self.modname(ndct['name'], value)
|
|
out.append((cls, new_name, bases, ndct, nfields))
|
|
return out
|
|
|
|
def modname(self, name, i):
|
|
return name + self.args['mn_mod'][i]
|
|
|
|
|
|
class bs_cond(bsi):
|
|
pass
|
|
|
|
|
|
class bs_swapargs(bs_divert):
|
|
|
|
def divert(self, i, candidates):
|
|
out = []
|
|
for cls, name, bases, dct, fields in candidates:
|
|
# args not permuted
|
|
ndct = dict(dct)
|
|
nfields = fields[:]
|
|
# gen fix field
|
|
f = gen_bsint(0, self.args['l'], self.args)
|
|
nfields[i] = f
|
|
out.append((cls, name, bases, ndct, nfields))
|
|
|
|
# args permuted
|
|
ndct = dict(dct)
|
|
nfields = fields[:]
|
|
ap = ndct['args_permut'][:]
|
|
a = ap.pop(0)
|
|
b = ap.pop(0)
|
|
ndct['args_permut'] = [b, a] + ap
|
|
# gen fix field
|
|
f = gen_bsint(1, self.args['l'], self.args)
|
|
nfields[i] = f
|
|
|
|
out.append((cls, name, bases, ndct, nfields))
|
|
return out
|
|
|
|
|
|
class m_arg(object):
|
|
|
|
def fromstring(self, s, parser_result=None):
|
|
if parser_result:
|
|
e, start, stop = parser_result[self.parser]
|
|
self.expr = e
|
|
return start, stop
|
|
try:
|
|
v, start, stop = self.parser.scanString(s).next()
|
|
except StopIteration:
|
|
return None, None
|
|
self.expr = v[0]
|
|
return start, stop
|
|
|
|
|
|
class m_reg(m_arg):
|
|
prio = default_prio
|
|
|
|
@property
|
|
def parser(self):
|
|
return self.reg.parser
|
|
|
|
def decode(self, v):
|
|
self.expr = self.reg.expr[0]
|
|
return True
|
|
|
|
def encode(self):
|
|
return self.expr == self.reg.expr[0]
|
|
|
|
|
|
class reg_noarg(object):
|
|
reg_info = None
|
|
parser = None
|
|
|
|
def fromstring(self, s, parser_result=None):
|
|
if parser_result:
|
|
e, start, stop = parser_result[self.parser]
|
|
self.expr = e
|
|
return start, stop
|
|
try:
|
|
v, start, stop = self.parser.scanString(s).next()
|
|
except StopIteration:
|
|
return None, None
|
|
self.expr = v[0]
|
|
return start, stop
|
|
|
|
def decode(self, v):
|
|
v = v & self.lmask
|
|
if v >= len(self.reg_info.expr):
|
|
return False
|
|
self.expr = self.reg_info.expr[v]
|
|
return True
|
|
|
|
def encode(self):
|
|
if not self.expr in self.reg_info.expr:
|
|
log.debug("cannot encode reg %r", self.expr)
|
|
return False
|
|
self.value = self.reg_info.expr.index(self.expr)
|
|
if self.value > self.lmask:
|
|
log.debug("cannot encode field value %x %x",
|
|
self.value, self.lmask)
|
|
return False
|
|
return True
|
|
|
|
def check_fbits(self, v):
|
|
return v & self.fmask == self.fbits
|
|
|
|
|
|
class mn_prefix:
|
|
pass
|
|
|
|
|
|
def swap16(v):
|
|
return struct.unpack('<H', struct.pack('>H', v))[0]
|
|
|
|
|
|
def swap32(v):
|
|
return struct.unpack('<I', struct.pack('>I', v))[0]
|
|
|
|
|
|
def perm_inv(p):
|
|
o = [None for x in xrange(len(p))]
|
|
for i, x in enumerate(p):
|
|
o[x] = i
|
|
return o
|
|
|
|
|
|
def gen_bsint(value, l, args):
|
|
s = int2bin(value, l)
|
|
args = dict(args)
|
|
args.update({'strbits': s})
|
|
f = bs(**args)
|
|
return f
|
|
|
|
total_scans = 0
|
|
|
|
|
|
def branch2nodes(branch, nodes=None):
|
|
if nodes is None:
|
|
nodes = []
|
|
for k, v in branch.items():
|
|
if not isinstance(v, dict):
|
|
continue
|
|
for k2 in v.keys():
|
|
nodes.append((k, k2))
|
|
branch2nodes(v, nodes)
|
|
|
|
|
|
def factor_one_bit(tree):
|
|
if isinstance(tree, set):
|
|
return tree
|
|
new_keys = defaultdict(lambda: defaultdict(dict))
|
|
if len(tree) == 1:
|
|
return tree
|
|
for k, v in tree.items():
|
|
if k == "mn":
|
|
new_keys[k] = v
|
|
continue
|
|
l, fmask, fbits, fname, flen = k
|
|
if flen is not None or l <= 1:
|
|
new_keys[k] = v
|
|
continue
|
|
cfmask = fmask >> (l - 1)
|
|
nfmask = fmask & ((1 << (l - 1)) - 1)
|
|
cfbits = fbits >> (l - 1)
|
|
nfbits = fbits & ((1 << (l - 1)) - 1)
|
|
ck = 1, cfmask, cfbits, None, flen
|
|
nk = l - 1, nfmask, nfbits, fname, flen
|
|
if nk in new_keys[ck]:
|
|
raise NotImplementedError('not fully functional')
|
|
new_keys[ck][nk] = v
|
|
for k, v in new_keys.items():
|
|
new_keys[k] = factor_one_bit(v)
|
|
# try factor sons
|
|
if len(new_keys) != 1:
|
|
return new_keys
|
|
subtree = new_keys.values()[0]
|
|
if len(subtree) != 1:
|
|
return new_keys
|
|
if subtree.keys()[0] == 'mn':
|
|
return new_keys
|
|
|
|
return new_keys
|
|
|
|
|
|
def factor_fields(tree):
|
|
if not isinstance(tree, dict):
|
|
return tree
|
|
if len(tree) != 1:
|
|
return tree
|
|
# merge
|
|
k1, v1 = tree.items()[0]
|
|
if k1 == "mn":
|
|
return tree
|
|
l1, fmask1, fbits1, fname1, flen1 = k1
|
|
if fname1 is not None:
|
|
return tree
|
|
if flen1 is not None:
|
|
return tree
|
|
|
|
if not isinstance(v1, dict):
|
|
return tree
|
|
if len(v1) != 1:
|
|
return tree
|
|
k2, v2 = v1.items()[0]
|
|
if k2 == "mn":
|
|
return tree
|
|
l2, fmask2, fbits2, fname2, flen2 = k2
|
|
if fname2 is not None:
|
|
return tree
|
|
if flen2 is not None:
|
|
return tree
|
|
l = l1 + l2
|
|
fmask = (fmask1 << l2) | fmask2
|
|
fbits = (fbits1 << l2) | fbits2
|
|
fname = fname2
|
|
flen = flen2
|
|
k = l, fmask, fbits, fname, flen
|
|
new_keys = {k: v2}
|
|
return new_keys
|
|
|
|
|
|
def factor_fields_all(tree):
|
|
if not isinstance(tree, dict):
|
|
return tree
|
|
new_keys = {}
|
|
for k, v in tree.items():
|
|
v = factor_fields(v)
|
|
new_keys[k] = factor_fields_all(v)
|
|
return new_keys
|
|
|
|
|
|
def graph_tree(tree):
|
|
nodes = []
|
|
branch2nodes(tree, nodes)
|
|
|
|
out = """
|
|
digraph G {
|
|
"""
|
|
for a, b in nodes:
|
|
if b == 'mn':
|
|
continue
|
|
out += "%s -> %s;\n" % (id(a), id(b))
|
|
out += "}"
|
|
open('graph.txt', 'w').write(out)
|
|
|
|
|
|
def add_candidate_to_tree(tree, c):
|
|
branch = tree
|
|
for f in c.fields:
|
|
if f.l == 0:
|
|
continue
|
|
node = f.l, f.fmask, f.fbits, f.fname, f.flen
|
|
|
|
if not node in branch:
|
|
branch[node] = {}
|
|
branch = branch[node]
|
|
if not 'mn' in branch:
|
|
branch['mn'] = set()
|
|
branch['mn'].add(c)
|
|
|
|
|
|
def add_candidate(bases, c):
|
|
add_candidate_to_tree(bases[0].bintree, c)
|
|
|
|
|
|
def getfieldby_name(fields, fname):
|
|
f = filter(lambda x: hasattr(x, 'fname') and x.fname == fname, fields)
|
|
if len(f) != 1:
|
|
raise ValueError('more than one field with name: %s' % fname)
|
|
return f[0]
|
|
|
|
|
|
def getfieldindexby_name(fields, fname):
|
|
for i, f in enumerate(fields):
|
|
if hasattr(f, 'fname') and f.fname == fname:
|
|
return f, i
|
|
return None
|
|
|
|
|
|
class metamn(type):
|
|
|
|
def __new__(mcs, name, bases, dct):
|
|
if name == "cls_mn" or name.startswith('mn_'):
|
|
return type.__new__(mcs, name, bases, dct)
|
|
alias = dct.get('alias', False)
|
|
|
|
fields = bases[0].mod_fields(dct['fields'])
|
|
if not 'name' in dct:
|
|
dct["name"] = bases[0].getmn(name)
|
|
if 'args' in dct:
|
|
# special case for permuted arguments
|
|
o = []
|
|
p = []
|
|
for i, a in enumerate(dct['args']):
|
|
o.append((i, a))
|
|
if a in fields:
|
|
p.append((fields.index(a), a))
|
|
p.sort()
|
|
p = [x[1] for x in p]
|
|
p = [dct['args'].index(x) for x in p]
|
|
dct['args_permut'] = perm_inv(p)
|
|
# order fields
|
|
f_ordered = [x for x in enumerate(fields)]
|
|
f_ordered.sort(key=lambda x: (x[1].prio, x[0]))
|
|
candidates = bases[0].gen_modes(mcs, name, bases, dct, fields)
|
|
for i, fc in f_ordered:
|
|
if isinstance(fc, bs_divert):
|
|
candidates = fc.divert(i, candidates)
|
|
for cls, name, bases, dct, fields in candidates:
|
|
ndct = dict(dct)
|
|
fields = [f for f in fields if f]
|
|
ndct['fields'] = fields
|
|
ndct['mn_len'] = sum([x.l for x in fields])
|
|
c = type.__new__(cls, name, bases, ndct)
|
|
c.alias = alias
|
|
c.check_mnemo(fields)
|
|
c.num = bases[0].num
|
|
bases[0].num += 1
|
|
bases[0].all_mn.append(c)
|
|
mode = dct['mode']
|
|
bases[0].all_mn_mode[mode].append(c)
|
|
bases[0].all_mn_name[c.name].append(c)
|
|
i = c()
|
|
i.init_class()
|
|
bases[0].all_mn_inst[c].append(i)
|
|
add_candidate(bases, c)
|
|
# gen byte lookup
|
|
o = ""
|
|
for f in i.fields_order:
|
|
if not isinstance(f, bsi):
|
|
raise ValueError('f is not bsi')
|
|
if f.l == 0:
|
|
continue
|
|
o += f.strbits
|
|
return c
|
|
|
|
|
|
class instruction(object):
|
|
__slots__ = ["name", "mode", "args",
|
|
"l", "b", "offset", "data",
|
|
"additional_info", "delayslot"]
|
|
|
|
def __init__(self, name, mode, args, additional_info=None):
|
|
self.name = name
|
|
self.mode = mode
|
|
self.args = args
|
|
self.additional_info = additional_info
|
|
|
|
def gen_args(self, args):
|
|
out = ', '.join([str(x) for x in args])
|
|
return out
|
|
|
|
def __str__(self):
|
|
o = "%-10s " % self.name
|
|
args = []
|
|
for i, arg in enumerate(self.args):
|
|
if not isinstance(arg, m2_expr.Expr):
|
|
raise ValueError('zarb arg type')
|
|
x = self.arg2str(arg, pos = i)
|
|
args.append(x)
|
|
o += self.gen_args(args)
|
|
return o
|
|
|
|
def get_asm_offset(self, expr):
|
|
return m2_expr.ExprInt(self.offset, expr.size)
|
|
|
|
def get_asm_next_offset(self, expr):
|
|
return m2_expr.ExprInt(self.offset+self.l, expr.size)
|
|
|
|
def resolve_args_with_symbols(self, symbols=None):
|
|
if symbols is None:
|
|
symbols = {}
|
|
args_out = []
|
|
for a in self.args:
|
|
e = a
|
|
# try to resolve symbols using symbols (0 for default value)
|
|
ids = m2_expr.get_expr_ids(e)
|
|
fixed_ids = {}
|
|
for x in ids:
|
|
if isinstance(x.name, asmblock.AsmLabel):
|
|
name = x.name.name
|
|
# special symbol $
|
|
if name == '$':
|
|
fixed_ids[x] = self.get_asm_offset(x)
|
|
continue
|
|
if name == '_':
|
|
fixed_ids[x] = self.get_asm_next_offset(x)
|
|
continue
|
|
if not name in symbols:
|
|
raise ValueError('unresolved symbol! %r' % x)
|
|
else:
|
|
name = x.name
|
|
if not name in symbols:
|
|
continue
|
|
if symbols[name].offset is None:
|
|
raise ValueError('The offset of label "%s" cannot be '
|
|
'determined' % name)
|
|
else:
|
|
size = x.size
|
|
if size is None:
|
|
default_size = self.get_symbol_size(x, symbols)
|
|
size = default_size
|
|
value = m2_expr.ExprInt(symbols[name].offset, size)
|
|
fixed_ids[x] = value
|
|
e = e.replace_expr(fixed_ids)
|
|
e = expr_simp(e)
|
|
args_out.append(e)
|
|
return args_out
|
|
|
|
def get_info(self, c):
|
|
return
|
|
|
|
|
|
class cls_mn(object):
|
|
__metaclass__ = metamn
|
|
args_symb = []
|
|
instruction = instruction
|
|
# Block's offset alignement
|
|
alignment = 1
|
|
|
|
@classmethod
|
|
def guess_mnemo(cls, bs, attrib, pre_dis_info, offset):
|
|
candidates = []
|
|
|
|
candidates = set()
|
|
|
|
fname_values = pre_dis_info
|
|
todo = [(dict(fname_values), branch, offset * 8)
|
|
for branch in cls.bintree.items()]
|
|
for fname_values, branch, offset_b in todo:
|
|
(l, fmask, fbits, fname, flen), vals = branch
|
|
|
|
if flen is not None:
|
|
l = flen(attrib, fname_values)
|
|
if l is not None:
|
|
try:
|
|
v = cls.getbits(bs, attrib, offset_b, l)
|
|
except IOError:
|
|
# Raised if offset is out of bound
|
|
continue
|
|
offset_b += l
|
|
if v & fmask != fbits:
|
|
continue
|
|
if fname is not None and not fname in fname_values:
|
|
fname_values[fname] = v
|
|
for nb, v in vals.items():
|
|
if 'mn' in nb:
|
|
candidates.update(v)
|
|
else:
|
|
todo.append((dict(fname_values), (nb, v), offset_b))
|
|
|
|
return [c for c in candidates]
|
|
|
|
def reset_class(self):
|
|
for f in self.fields_order:
|
|
if f.strbits and isbin(f.strbits):
|
|
f.value = int(f.strbits, 2)
|
|
elif 'default_val' in f.kargs:
|
|
f.value = int(f.kargs['default_val'], 2)
|
|
else:
|
|
f.value = None
|
|
if f.fname:
|
|
setattr(self, f.fname, f)
|
|
|
|
def init_class(self):
|
|
args = []
|
|
fields_order = []
|
|
to_decode = []
|
|
off = 0
|
|
for i, fc in enumerate(self.fields):
|
|
f = fc.gen(self)
|
|
f.offset = off
|
|
off += f.l
|
|
fields_order.append(f)
|
|
to_decode.append((i, f))
|
|
|
|
if isinstance(f, m_arg):
|
|
args.append(f)
|
|
if f.fname:
|
|
setattr(self, f.fname, f)
|
|
if hasattr(self, 'args_permut'):
|
|
args = [args[self.args_permut[i]]
|
|
for i in xrange(len(self.args_permut))]
|
|
to_decode.sort(key=lambda x: (x[1].order, x[0]))
|
|
to_decode = [fields_order.index(f[1]) for f in to_decode]
|
|
self.args = args
|
|
self.fields_order = fields_order
|
|
self.to_decode = to_decode
|
|
|
|
def add_pre_dis_info(self, prefix=None):
|
|
return True
|
|
|
|
@classmethod
|
|
def getbits(cls, bs, attrib, offset_b, l):
|
|
return bs.getbits(offset_b, l)
|
|
|
|
@classmethod
|
|
def getbytes(cls, bs, offset, l):
|
|
return bs.getbytes(offset, l)
|
|
|
|
@classmethod
|
|
def pre_dis(cls, v_o, attrib, offset):
|
|
return {}, v_o, attrib, offset, 0
|
|
|
|
def post_dis(self):
|
|
return self
|
|
|
|
@classmethod
|
|
def check_mnemo(cls, fields):
|
|
pass
|
|
|
|
@classmethod
|
|
def mod_fields(cls, fields):
|
|
return fields
|
|
|
|
@classmethod
|
|
def dis(cls, bs_o, mode_o = None, offset=0):
|
|
if not isinstance(bs_o, bin_stream):
|
|
bs_o = bin_stream_str(bs_o)
|
|
|
|
bs_o.enter_atomic_mode()
|
|
|
|
offset_o = offset
|
|
try:
|
|
pre_dis_info, bs, mode, offset, prefix_len = cls.pre_dis(
|
|
bs_o, mode_o, offset)
|
|
except:
|
|
bs_o.leave_atomic_mode()
|
|
raise
|
|
candidates = cls.guess_mnemo(bs, mode, pre_dis_info, offset)
|
|
if not candidates:
|
|
bs_o.leave_atomic_mode()
|
|
raise Disasm_Exception('cannot disasm (guess) at %X' % offset)
|
|
|
|
out = []
|
|
out_c = []
|
|
if hasattr(bs, 'getlen'):
|
|
bs_l = bs.getlen()
|
|
else:
|
|
bs_l = len(bs)
|
|
|
|
alias = False
|
|
for c in candidates:
|
|
log.debug("*" * 40, mode, c.mode)
|
|
log.debug(c.fields)
|
|
|
|
c = cls.all_mn_inst[c][0]
|
|
|
|
c.reset_class()
|
|
c.mode = mode
|
|
|
|
if not c.add_pre_dis_info(pre_dis_info):
|
|
continue
|
|
|
|
todo = {}
|
|
getok = True
|
|
fname_values = dict(pre_dis_info)
|
|
offset_b = offset * 8
|
|
|
|
total_l = 0
|
|
for i, f in enumerate(c.fields_order):
|
|
if f.flen is not None:
|
|
l = f.flen(mode, fname_values)
|
|
else:
|
|
l = f.l
|
|
if l is not None:
|
|
total_l += l
|
|
f.l = l
|
|
f.is_present = True
|
|
log.debug("FIELD %s %s %s %s", f.__class__, f.fname,
|
|
offset_b, l)
|
|
if bs_l * 8 - offset_b < l:
|
|
getok = False
|
|
break
|
|
bv = cls.getbits(bs, mode, offset_b, l)
|
|
offset_b += l
|
|
if not f.fname in fname_values:
|
|
fname_values[f.fname] = bv
|
|
todo[i] = bv
|
|
else:
|
|
f.is_present = False
|
|
todo[i] = None
|
|
|
|
if not getok:
|
|
continue
|
|
|
|
c.l = prefix_len + total_l / 8
|
|
for i in c.to_decode:
|
|
f = c.fields_order[i]
|
|
if f.is_present:
|
|
ret = f.decode(todo[i])
|
|
if not ret:
|
|
log.debug("cannot decode %r", f)
|
|
break
|
|
|
|
if not ret:
|
|
continue
|
|
for a in c.args:
|
|
a.expr = expr_simp(a.expr)
|
|
|
|
c.b = cls.getbytes(bs, offset_o, c.l)
|
|
c.offset = offset_o
|
|
c = c.post_dis()
|
|
if c is None:
|
|
continue
|
|
c_args = [a.expr for a in c.args]
|
|
instr = cls.instruction(c.name, mode, c_args,
|
|
additional_info=c.additional_info())
|
|
instr.l = prefix_len + total_l / 8
|
|
instr.b = cls.getbytes(bs, offset_o, instr.l)
|
|
instr.offset = offset_o
|
|
instr.get_info(c)
|
|
if c.alias:
|
|
alias = True
|
|
out.append(instr)
|
|
out_c.append(c)
|
|
|
|
bs_o.leave_atomic_mode()
|
|
|
|
if not out:
|
|
raise Disasm_Exception('cannot disasm at %X' % offset_o)
|
|
if len(out) != 1:
|
|
if not alias:
|
|
log.warning('dis multiple args ret default')
|
|
|
|
for i, o in enumerate(out_c):
|
|
if o.alias:
|
|
return out[i]
|
|
raise NotImplementedError('Multiple disas: \n' +
|
|
"\n".join([str(x) for x in out]))
|
|
return out[0]
|
|
|
|
@classmethod
|
|
def fromstring(cls, s, mode = None):
|
|
global total_scans
|
|
name = re.search('(\S+)', s).groups()
|
|
if not name:
|
|
raise ValueError('cannot find name', s)
|
|
name = name[0]
|
|
|
|
if not name in cls.all_mn_name:
|
|
raise ValueError('unknown name', name)
|
|
clist = [x for x in cls.all_mn_name[name]]
|
|
out = []
|
|
out_args = []
|
|
parsers = defaultdict(dict)
|
|
|
|
for cc in clist:
|
|
for c in cls.get_cls_instance(cc, mode):
|
|
args_expr = []
|
|
args_str = s[len(name):].strip(' ')
|
|
|
|
start = 0
|
|
cannot_parse = False
|
|
len_o = len(args_str)
|
|
|
|
for i, f in enumerate(c.args):
|
|
start_i = len_o - len(args_str)
|
|
if type(f.parser) == tuple:
|
|
parser = f.parser
|
|
else:
|
|
parser = (f.parser,)
|
|
for p in parser:
|
|
if p in parsers[(i, start_i)]:
|
|
continue
|
|
try:
|
|
total_scans += 1
|
|
v, start, stop = p.scanString(args_str).next()
|
|
except StopIteration:
|
|
v, start, stop = [None], None, None
|
|
if start != 0:
|
|
v, start, stop = [None], None, None
|
|
parsers[(i, start_i)][p] = v[0], start, stop
|
|
|
|
start, stop = f.fromstring(args_str, parsers[(i, start_i)])
|
|
if start != 0:
|
|
log.debug("cannot fromstring %r", args_str)
|
|
cannot_parse = True
|
|
break
|
|
if f.expr is None:
|
|
raise NotImplementedError('not fully functional')
|
|
f.expr = expr_simp(f.expr)
|
|
args_expr.append(f.expr)
|
|
args_str = args_str[stop:].strip(' ')
|
|
if args_str.startswith(','):
|
|
args_str = args_str[1:]
|
|
args_str = args_str.strip(' ')
|
|
if args_str:
|
|
cannot_parse = True
|
|
if cannot_parse:
|
|
continue
|
|
|
|
out.append(c)
|
|
out_args.append(args_expr)
|
|
break
|
|
|
|
if len(out) == 0:
|
|
raise ValueError('cannot fromstring %r' % s)
|
|
if len(out) != 1:
|
|
log.debug('fromstring multiple args ret default')
|
|
c = out[0]
|
|
c_args = out_args[0]
|
|
|
|
instr = cls.instruction(c.name, mode, c_args,
|
|
additional_info=c.additional_info())
|
|
return instr
|
|
|
|
def dup_info(self, infos):
|
|
return
|
|
|
|
@classmethod
|
|
def get_cls_instance(cls, cc, mode, infos=None):
|
|
c = cls.all_mn_inst[cc][0]
|
|
|
|
c.reset_class()
|
|
c.add_pre_dis_info()
|
|
c.dup_info(infos)
|
|
|
|
c.mode = mode
|
|
yield c
|
|
|
|
@classmethod
|
|
def asm(cls, instr, symbols=None):
|
|
"""
|
|
Re asm instruction by searching mnemo using name and args. We then
|
|
can modify args and get the hex of a modified instruction
|
|
"""
|
|
clist = cls.all_mn_name[instr.name]
|
|
clist = [x for x in clist]
|
|
vals = []
|
|
candidates = []
|
|
args = instr.resolve_args_with_symbols(symbols)
|
|
|
|
for cc in clist:
|
|
|
|
for c in cls.get_cls_instance(
|
|
cc, instr.mode, instr.additional_info):
|
|
|
|
cannot_parse = False
|
|
if len(c.args) != len(instr.args):
|
|
continue
|
|
|
|
# only fix args expr
|
|
for i in xrange(len(c.args)):
|
|
c.args[i].expr = args[i]
|
|
|
|
v = c.value(instr.mode)
|
|
if not v:
|
|
log.debug("cannot encode %r", c)
|
|
cannot_parse = True
|
|
if cannot_parse:
|
|
continue
|
|
vals += v
|
|
candidates.append((c, v))
|
|
if len(vals) == 0:
|
|
raise ValueError('cannot asm %r %r' %
|
|
(instr.name, [str(x) for x in instr.args]))
|
|
if len(vals) != 1:
|
|
log.debug('asm multiple args ret default')
|
|
|
|
vals = cls.filter_asm_candidates(instr, candidates)
|
|
return vals
|
|
|
|
@classmethod
|
|
def filter_asm_candidates(cls, instr, candidates):
|
|
o = []
|
|
for _, v in candidates:
|
|
o += v
|
|
o.sort(key=len)
|
|
return o
|
|
|
|
def value(self, mode):
|
|
todo = [(0, 0, [(x, self.fields_order[x]) for x in self.to_decode[::-1]])]
|
|
|
|
result = []
|
|
done = []
|
|
|
|
while todo:
|
|
index, cur_len, to_decode = todo.pop()
|
|
# TEST XXX
|
|
for _, f in to_decode:
|
|
setattr(self, f.fname, f)
|
|
if (index, [x[1].value for x in to_decode]) in done:
|
|
continue
|
|
done.append((index, [x[1].value for x in to_decode]))
|
|
|
|
can_encode = True
|
|
for i, f in to_decode[index:]:
|
|
f.parent.l = cur_len
|
|
ret = f.encode()
|
|
if not ret:
|
|
log.debug('cannot encode %r', f)
|
|
can_encode = False
|
|
break
|
|
if f.value is not None and f.l:
|
|
cur_len += f.l
|
|
index += 1
|
|
if ret is True:
|
|
continue
|
|
|
|
for _ in ret:
|
|
o = []
|
|
if ((index, cur_len, [xx[1].value for xx in to_decode]) in todo or
|
|
(index, cur_len, [xx[1].value for xx in to_decode]) in done):
|
|
raise NotImplementedError('not fully functional')
|
|
|
|
for p, f in to_decode:
|
|
fnew = f.clone()
|
|
o.append((p, fnew))
|
|
todo.append((index, cur_len, o))
|
|
can_encode = False
|
|
|
|
break
|
|
if not can_encode:
|
|
continue
|
|
result.append(to_decode)
|
|
|
|
return self.decoded2bytes(result)
|
|
|
|
def encodefields(self, decoded):
|
|
bits = bitobj()
|
|
for _, f in decoded:
|
|
setattr(self, f.fname, f)
|
|
|
|
if f.value is None:
|
|
continue
|
|
bits.putbits(f.value, f.l)
|
|
|
|
return bits.tostring()
|
|
|
|
def decoded2bytes(self, result):
|
|
if not result:
|
|
return []
|
|
|
|
out = []
|
|
for decoded in result:
|
|
decoded.sort()
|
|
|
|
o = self.encodefields(decoded)
|
|
if o is None:
|
|
continue
|
|
out.append(o)
|
|
out = list(set(out))
|
|
return out
|
|
|
|
def gen_args(self, args):
|
|
out = ', '.join([str(x) for x in args])
|
|
return out
|
|
|
|
def args2str(self):
|
|
args = []
|
|
for arg in self.args:
|
|
# XXX todo test
|
|
if not (isinstance(arg, m2_expr.Expr) or
|
|
isinstance(arg.expr, m2_expr.Expr)):
|
|
raise ValueError('zarb arg type')
|
|
x = str(arg)
|
|
args.append(x)
|
|
return args
|
|
|
|
def __str__(self):
|
|
o = "%-10s " % self.name
|
|
args = []
|
|
for arg in self.args:
|
|
# XXX todo test
|
|
if not (isinstance(arg, m2_expr.Expr) or
|
|
isinstance(arg.expr, m2_expr.Expr)):
|
|
raise ValueError('zarb arg type')
|
|
x = str(arg)
|
|
args.append(x)
|
|
|
|
o += self.gen_args(args)
|
|
return o
|
|
|
|
def parse_prefix(self, v):
|
|
return 0
|
|
|
|
def set_dst_symbol(self, symbol_pool):
|
|
dst = self.getdstflow(symbol_pool)
|
|
args = []
|
|
for d in dst:
|
|
if isinstance(d, m2_expr.ExprInt):
|
|
l = symbol_pool.getby_offset_create(int(d))
|
|
|
|
a = m2_expr.ExprId(l.name, d.size)
|
|
else:
|
|
a = d
|
|
args.append(a)
|
|
self.args_symb = args
|
|
|
|
def getdstflow(self, symbol_pool):
|
|
return [self.args[0].expr]
|
|
|
|
|
|
class imm_noarg(object):
|
|
intsize = 32
|
|
intmask = (1 << intsize) - 1
|
|
|
|
def int2expr(self, v):
|
|
if (v & ~self.intmask) != 0:
|
|
return None
|
|
return m2_expr.ExprInt(v, self.intsize)
|
|
|
|
def expr2int(self, e):
|
|
if not isinstance(e, m2_expr.ExprInt):
|
|
return None
|
|
v = int(e)
|
|
if v & ~self.intmask != 0:
|
|
return None
|
|
return v
|
|
|
|
def fromstring(self, s, parser_result=None):
|
|
if parser_result:
|
|
e, start, stop = parser_result[self.parser]
|
|
else:
|
|
try:
|
|
e, start, stop = self.parser.scanString(s).next()
|
|
except StopIteration:
|
|
return None, None
|
|
if e is None:
|
|
return None, None
|
|
|
|
assert(isinstance(e, m2_expr.Expr))
|
|
if isinstance(e, tuple):
|
|
self.expr = self.int2expr(e[1])
|
|
elif isinstance(e, m2_expr.Expr):
|
|
self.expr = e
|
|
else:
|
|
raise TypeError('zarb expr')
|
|
if self.expr is None:
|
|
log.debug('cannot fromstring int %r', s)
|
|
return None, None
|
|
return start, stop
|
|
|
|
def decodeval(self, v):
|
|
return v
|
|
|
|
def encodeval(self, v):
|
|
if v > self.lmask:
|
|
return False
|
|
return v
|
|
|
|
def decode(self, v):
|
|
v = v & self.lmask
|
|
v = self.decodeval(v)
|
|
e = self.int2expr(v)
|
|
if not e:
|
|
return False
|
|
self.expr = e
|
|
return True
|
|
|
|
def encode(self):
|
|
v = self.expr2int(self.expr)
|
|
if v is None:
|
|
return False
|
|
v = self.encodeval(v)
|
|
if v is False:
|
|
return False
|
|
self.value = v
|
|
return True
|
|
|
|
|
|
class imm08_noarg(object):
|
|
int2expr = lambda self, x: m2_expr.ExprInt(x, 8)
|
|
|
|
|
|
class imm16_noarg(object):
|
|
int2expr = lambda self, x: m2_expr.ExprInt(x, 16)
|
|
|
|
|
|
class imm32_noarg(object):
|
|
int2expr = lambda self, x: m2_expr.ExprInt(x, 32)
|
|
|
|
|
|
class imm64_noarg(object):
|
|
int2expr = lambda self, x: m2_expr.ExprInt(x, 64)
|
|
|
|
|
|
class int32_noarg(imm_noarg):
|
|
intsize = 32
|
|
intmask = (1 << intsize) - 1
|
|
|
|
def decode(self, v):
|
|
v = sign_ext(v, self.l, self.intsize)
|
|
v = self.decodeval(v)
|
|
self.expr = self.int2expr(v)
|
|
return True
|
|
|
|
def encode(self):
|
|
if not isinstance(self.expr, m2_expr.ExprInt):
|
|
return False
|
|
v = int(self.expr)
|
|
if sign_ext(v & self.lmask, self.l, self.intsize) != v:
|
|
return False
|
|
v = self.encodeval(v & self.lmask)
|
|
self.value = v & self.lmask
|
|
return True
|
|
|
|
class bs8(bs):
|
|
prio = default_prio
|
|
|
|
def __init__(self, v, cls=None, fname=None, **kargs):
|
|
super(bs8, self).__init__(int2bin(v, 8), 8,
|
|
cls=cls, fname=fname, **kargs)
|
|
|
|
|
|
|
|
|
|
def swap_uint(size, i):
|
|
if size == 8:
|
|
return i & 0xff
|
|
elif size == 16:
|
|
return struct.unpack('<H', struct.pack('>H', i & 0xffff))[0]
|
|
elif size == 32:
|
|
return struct.unpack('<I', struct.pack('>I', i & 0xffffffff))[0]
|
|
elif size == 64:
|
|
return struct.unpack('<Q', struct.pack('>Q', i & 0xffffffffffffffff))[0]
|
|
raise ValueError('unknown int len %r' % size)
|
|
|
|
|
|
def swap_sint(size, i):
|
|
if size == 8:
|
|
return i
|
|
elif size == 16:
|
|
return struct.unpack('<h', struct.pack('>H', i & 0xffff))[0]
|
|
elif size == 32:
|
|
return struct.unpack('<i', struct.pack('>I', i & 0xffffffff))[0]
|
|
elif size == 64:
|
|
return struct.unpack('<q', struct.pack('>Q', i & 0xffffffffffffffff))[0]
|
|
raise ValueError('unknown int len %r' % size)
|
|
|
|
|
|
def sign_ext(v, s_in, s_out):
|
|
assert(s_in <= s_out)
|
|
v &= (1 << s_in) - 1
|
|
sign_in = v & (1 << (s_in - 1))
|
|
if not sign_in:
|
|
return v
|
|
m = (1 << (s_out)) - 1
|
|
m ^= (1 << s_in) - 1
|
|
v |= m
|
|
return v
|