Add PCMPXSTRX Instructions (#507)
* Add PCMPXSTRX Instructions * bug fixes * unittest related fixes * Full test of pcmpxstrx * Move tests
This commit is contained in:
@@ -13,7 +13,7 @@ from .abstractcpu import (
|
||||
|
||||
|
||||
from ..smtlib import Operators, BitVec, Bool, BitVecConstant, operator, visitors
|
||||
from ..memory import MemoryException
|
||||
from ..memory import MemoryException, ConcretizeMemory
|
||||
from ...utils.helpers import issymbolic
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -597,6 +597,9 @@ class AMD64RegFile(RegisterFile):
|
||||
self._cache[name] = value
|
||||
return value
|
||||
|
||||
def sizeof(self, reg):
|
||||
return self._table[reg].size
|
||||
|
||||
###########################
|
||||
# Operand Wrapper
|
||||
class AMD64Operand(Operand):
|
||||
@@ -4880,6 +4883,224 @@ class X86Cpu(Cpu):
|
||||
# res = res | (0xff << i)
|
||||
op0.write(res)
|
||||
|
||||
############################################################################
|
||||
# Implementation of PCMPxSTRx Instructions:
|
||||
# + PCMPESTRM, PCMPESTRI, PCMPISTRM, PCMPISTRI
|
||||
##
|
||||
# '''
|
||||
# See Ref: https://software.intel.com/sites/default/files/m/8/b/8/D9156103.pdf
|
||||
|
||||
# :param cpu: current CPU.
|
||||
# :param op0: compare oprand 1
|
||||
# :param op1: compare oprand 2
|
||||
# :param op2: control byte
|
||||
# - op2[3:2] :
|
||||
# + 00: Equal Any
|
||||
# + 01: Ranges
|
||||
# + 10: Equal Each
|
||||
# + 11: Equal Ordered
|
||||
# '''
|
||||
#############################################################################
|
||||
def _pcmpxstrx_srcdat_format(self, ctlbyte):
|
||||
# Parse CTL Byte
|
||||
## Source Data Format
|
||||
if ((Operators.EXTRACT(ctlbyte, 0, 2) & 1) == 0 ):
|
||||
stepsize = 8
|
||||
else:
|
||||
stepsize = 16
|
||||
return stepsize
|
||||
|
||||
def _pcmpxstri_output_selection(self, ctlbyte, res):
|
||||
## Output Selection
|
||||
### PCMPESTRI/PCMPISTRI
|
||||
stepsize = self._pcmpxstrx_srcdat_format(ctlbyte)
|
||||
if (Operators.EXTRACT(ctlbyte, 6, 1) == 0):
|
||||
oecx = 0
|
||||
tres = res
|
||||
while ((tres & 1) == 0):
|
||||
oecx += 1
|
||||
tres >>= 1
|
||||
return oecx
|
||||
else:
|
||||
oecx = 128/stepsize - 1
|
||||
tres = res
|
||||
msbmask = (1 << (128/stepsize - 1))
|
||||
while ((tres & msbmask) == 0):
|
||||
oecx -= 1
|
||||
tres = (tres << 1) & ((msbmask << 1) - 1)
|
||||
return oecx
|
||||
|
||||
def _pcmpxstrm_output_selection(self, ctlbyte, res):
|
||||
## Output Selection
|
||||
### PCMPESTRM/PCMPISTRM
|
||||
if (Operators.EXTRACT(ctlbyte, 6, 1) == 0):
|
||||
return res
|
||||
else:
|
||||
stepsize = self._pcmpxstrx_srcdat_format(ctlbyte)
|
||||
xmmres = 0
|
||||
for i in range(0, 128, stepsize):
|
||||
if (res&1 == 1):
|
||||
xmmres |= (((1 << stepsize) - 1) << i)
|
||||
res >>= 1
|
||||
return xmmres
|
||||
|
||||
def _pcmpistrx_varg(self, arg, ctlbyte):
|
||||
step = self._pcmpxstrx_srcdat_format(ctlbyte)
|
||||
result = []
|
||||
for i in range(0, 128, step):
|
||||
uc = Operators.EXTRACT(arg, i, step)
|
||||
if uc == 0:
|
||||
break
|
||||
result.append(uc)
|
||||
return result
|
||||
|
||||
def _pcmpestrx_varg(self, arg, regname, ctlbyte):
|
||||
reg = self.read_register(regname)
|
||||
if (issymbolic(reg)):
|
||||
raise ConcretizeRegister(self, regname, "Concretize PCMPESTRx ECX/EDX")
|
||||
smask = 1 << (self.regfile.sizeof(regname) - 1)
|
||||
step = self._pcmpxstrx_srcdat_format(ctlbyte)
|
||||
if (reg & smask == 1):
|
||||
val = Operators.NOT(reg - 1)
|
||||
else:
|
||||
val = reg
|
||||
if (val > 128/step):
|
||||
val = 128/step
|
||||
result = []
|
||||
for i in range(val):
|
||||
uc = Operators.EXTRACT(arg, i*step, step)
|
||||
result.append(uc)
|
||||
return result
|
||||
|
||||
def _pcmpxstrx_aggregation_operation(self, varg0, varg1, ctlbyte):
|
||||
##
|
||||
needle = [e for e in varg0]
|
||||
haystack = [e for e in varg1]
|
||||
|
||||
## Aggregation Operation
|
||||
res = 0
|
||||
stepsize = self._pcmpxstrx_srcdat_format(ctlbyte)
|
||||
xmmsize = 128
|
||||
if (Operators.EXTRACT(ctlbyte, 2, 2) == 0):
|
||||
#raise NotImplementedError("pcmpistrx Equal any")
|
||||
for i in range(len(haystack)):
|
||||
if haystack[i] in needle:
|
||||
res |= (1 << i)
|
||||
elif (Operators.EXTRACT(ctlbyte, 2, 2) == 1):
|
||||
#raise NotImplementedError("pcmpistrx Ranges")
|
||||
assert len(needle)%2 == 0
|
||||
for i in range(len(haystack)):
|
||||
for j in range(0, len(needle), 2):
|
||||
if haystack[i] >= needle[j] and haystack[i] <= needle[j+1]:
|
||||
res |= (1 << i)
|
||||
break
|
||||
elif (Operators.EXTRACT(ctlbyte, 2, 2) == 2):
|
||||
#raise NotImplementedError("pcmpistrx Equal each")
|
||||
# Equal Each requires Null Byte Comparison Here
|
||||
while len(needle) < xmmsize/stepsize:
|
||||
needle.append('\x00')
|
||||
while len(haystack) < xmmsize/stepsize:
|
||||
haystack.append('\x00')
|
||||
for i in range(xmmsize/stepsize):
|
||||
res = Operators.ITEBV(xmmsize, needle[i] == haystack[i], res|(1 << i), res)
|
||||
elif (Operators.EXTRACT(ctlbyte, 2, 2) == 3):
|
||||
#raise NotImplementedError("pcmpistrx Equal ordered")
|
||||
if len(haystack) < len(needle):
|
||||
return 0
|
||||
for i in range(len(haystack)):
|
||||
subneedle = needle[: (xmmsize/stepsize - i) if len(needle)+i > xmmsize/stepsize else len(needle)]
|
||||
res = Operators.ITEBV(xmmsize, haystack[i:i+len(subneedle)] == subneedle, res|(1 << i), res)
|
||||
return res
|
||||
|
||||
def _pcmpxstrx_polarity(self, res1, ctlbyte, arg2len):
|
||||
## Polarity
|
||||
stepsize = self._pcmpxstrx_srcdat_format(ctlbyte)
|
||||
if (Operators.EXTRACT(ctlbyte, 4, 2) == 0):
|
||||
res2 = res1
|
||||
pass
|
||||
if (Operators.EXTRACT(ctlbyte, 4, 2) == 1):
|
||||
res2 = ((1 << (128/stepsize)) - 1) ^ res1
|
||||
if (Operators.EXTRACT(ctlbyte, 4, 2) == 2):
|
||||
res2 = res1
|
||||
pass
|
||||
if (Operators.EXTRACT(ctlbyte, 4, 2) == 3):
|
||||
res2 = ((1 << arg2len) - 1) ^ res1
|
||||
pass
|
||||
return res2
|
||||
|
||||
def _pcmpxstrx_setflags(self, res, varg0, varg1, ctlbyte):
|
||||
stepsize = self._pcmpxstrx_srcdat_format(ctlbyte)
|
||||
self.ZF = len(varg1) < 128/stepsize
|
||||
self.SF = len(varg0) < 128/stepsize
|
||||
self.CF = res != 0
|
||||
self.OF = res & 1
|
||||
self.AF = False
|
||||
self.PF = False
|
||||
|
||||
def _pcmpxstrx_operands(self, op0, op1, op2):
|
||||
arg0 = op0.read()
|
||||
arg1 = op1.read()
|
||||
ctlbyte = op2.read()
|
||||
if (issymbolic(arg0)):
|
||||
# XMM Register
|
||||
assert op0.type == 'register'
|
||||
raise ConcretizeRegister(self, op0.reg, "Concretize for PCMPXSTRX")
|
||||
if (issymbolic(arg1)):
|
||||
if (op1.type == 'register'):
|
||||
# XMM Register
|
||||
raise ConcretizeRegister(self, op1.reg, "Concretize for PCMPXSTRX")
|
||||
else:
|
||||
# Memory
|
||||
raise ConcretizeMemory(self.memory, op1.address(), op0.size)
|
||||
assert(not issymbolic(ctlbyte))
|
||||
return (arg0, arg1, ctlbyte)
|
||||
|
||||
@instruction
|
||||
def PCMPISTRI(cpu, op0, op1, op2):
|
||||
arg0, arg1, ctlbyte = cpu._pcmpxstrx_operands(op0, op1, op2)
|
||||
varg0 = cpu._pcmpistrx_varg(arg0, ctlbyte)
|
||||
varg1 = cpu._pcmpistrx_varg(arg1, ctlbyte)
|
||||
res = cpu._pcmpxstrx_aggregation_operation(varg0, varg1, ctlbyte)
|
||||
res = cpu._pcmpxstrx_polarity(res, ctlbyte, len(varg1))
|
||||
if (res == 0):
|
||||
cpu.ECX = 128/cpu._pcmpxstrx_srcdat_format(ctlbyte)
|
||||
else:
|
||||
cpu.ECX = cpu._pcmpxstri_output_selection(ctlbyte, res)
|
||||
cpu._pcmpxstrx_setflags(res, varg0, varg1, ctlbyte)
|
||||
|
||||
@instruction
|
||||
def PCMPISTRM(cpu, op0, op1, op2):
|
||||
arg0, arg1, ctlbyte = cpu._pcmpxstrx_operands(op0, op1, op2)
|
||||
varg0 = cpu._pcmpistrx_varg(arg0, ctlbyte)
|
||||
varg1 = cpu._pcmpistrx_varg(arg1, ctlbyte)
|
||||
res = cpu._pcmpxstrx_aggregation_operation(varg0, varg1, ctlbyte)
|
||||
res = cpu._pcmpxstrx_polarity(res, ctlbyte, len(varg1))
|
||||
cpu.XMM0 = cpu._pcmpxstrm_output_selection(ctlbyte, res)
|
||||
cpu._pcmpxstrx_setflags(res, varg0, varg1, ctlbyte)
|
||||
|
||||
@instruction
|
||||
def PCMPESTRI(cpu, op0, op1, op2):
|
||||
arg0, arg1, ctlbyte = cpu._pcmpxstrx_operands(op0, op1, op2)
|
||||
varg0 = cpu._pcmpestrx_varg(arg0, 'EAX', ctlbyte)
|
||||
varg1 = cpu._pcmpestrx_varg(arg1, 'EDX', ctlbyte)
|
||||
res = cpu._pcmpxstrx_aggregation_operation(varg0, varg1, ctlbyte)
|
||||
res = cpu._pcmpxstrx_polarity(res, ctlbyte, len(varg1))
|
||||
if (res == 0):
|
||||
cpu.ECX = 128/cpu._pcmpxstrx_srcdat_format(ctlbyte)
|
||||
else:
|
||||
cpu.ECX = cpu._pcmpxstri_output_selection(ctlbyte, res)
|
||||
cpu._pcmpxstrx_setflags(res, varg0, varg1, ctlbyte)
|
||||
|
||||
@instruction
|
||||
def PCMPESTRM(cpu, op0, op1, op2):
|
||||
arg0, arg1, ctlbyte = cpu._pcmpxstrx_operands(op0, op1, op2)
|
||||
varg0 = cpu._pcmpestrx_varg(arg0, 'EAX', ctlbyte)
|
||||
varg1 = cpu._pcmpestrx_varg(arg1, 'EDX', ctlbyte)
|
||||
res = cpu._pcmpxstrx_aggregation_operation(varg0, varg1, ctlbyte)
|
||||
res = cpu._pcmpxstrx_polarity(res, ctlbyte, len(varg1))
|
||||
cpu.XMM0 = cpu._pcmpxstrm_output_selection(ctlbyte, res)
|
||||
cpu._pcmpxstrx_setflags(res, varg0, varg1, ctlbyte)
|
||||
|
||||
@instruction
|
||||
def PMOVMSKB(cpu, op0, op1):
|
||||
'''
|
||||
|
||||
@@ -139,7 +139,7 @@ class State(Eventful):
|
||||
except ConcretizeMemory as e:
|
||||
expression = self.cpu.read_int(e.address, e.size)
|
||||
def setstate(state, value):
|
||||
state.cpu.write_int(e.reg_name, value, e.size)
|
||||
state.cpu.write_int(e.address, value, e.size)
|
||||
raise Concretize(e.message,
|
||||
expression=expression,
|
||||
setstate=setstate,
|
||||
|
||||
@@ -16,6 +16,8 @@ for test in tests:
|
||||
cnt = op_count.get(test['mnemonic'],0)
|
||||
if cnt > 1000: #No more than 1000 instructions of each kind
|
||||
continue
|
||||
if test['mnemonic'] != 'PCMPISTRI' and test['mnemonic'] != 'PCMPISTRM' and test['mnemonic'] != 'PCMPESTRI' and test['mnemonic'] != 'PCMPESTRM':
|
||||
continue
|
||||
op_count[test['mnemonic']] = cnt+1
|
||||
test_dic["%s_%d"%(test['mnemonic'], op_count[test['mnemonic']])] = test
|
||||
except Exception,e:
|
||||
@@ -216,6 +218,7 @@ for test_name in sorted(test_dic.keys()):
|
||||
print """ cs.add(cpu.%s == 0x%x)"""%(reg_name, value)
|
||||
|
||||
|
||||
pc_reg = 'RIP' if 'RIP' in test['pre']['registers'] else 'EIP'
|
||||
print """
|
||||
done = False
|
||||
while not done:
|
||||
@@ -226,8 +229,27 @@ for test_name in sorted(test_dic.keys()):
|
||||
symbol = getattr(cpu, e.reg_name)
|
||||
values = solver.get_all_values(cs, symbol)
|
||||
self.assertEqual(len(values), 1)
|
||||
setattr(cpu, e.reg_name, values[0])"""
|
||||
|
||||
setattr(cpu, e.reg_name, values[0])
|
||||
setattr(cpu, '{PC}', {PC_VAL})""".format(
|
||||
PC=pc_reg,
|
||||
PC_VAL=hex(test['pre']['registers'][pc_reg]))
|
||||
print """
|
||||
except ConcretizeMemory,e:
|
||||
symbol = getattr(cpu, '{FRAME_BASE}')
|
||||
if isinstance(symbol, Expression):
|
||||
values = solver.get_all_values(cs, symbol)
|
||||
self.assertEqual(len(values), 1)
|
||||
setattr(cpu, '{FRAME_BASE}', values[0])
|
||||
for i in range(e.size):
|
||||
symbol = mem[e.address+i]
|
||||
if isinstance(symbol, Expression):
|
||||
values = solver.get_all_values(cs, symbol)
|
||||
self.assertEqual(len(values), 1)
|
||||
mem[e.address+i] = values[0]
|
||||
setattr(cpu, '{PC}', {PC_VAL})""".format(
|
||||
FRAME_BASE='RBP' if 'RBP' in test['pre']['registers'] else 'EBP',
|
||||
PC=pc_reg,
|
||||
PC_VAL=hex(test['pre']['registers'][pc_reg]))
|
||||
|
||||
|
||||
print """
|
||||
|
||||
@@ -121,7 +121,7 @@ while True:
|
||||
pc = gdb.getR({'i386': 'EIP', 'amd64': 'RIP'}[arch])
|
||||
print hex(pc)
|
||||
gdb.stepi()
|
||||
print gdb.correspond('info registers')
|
||||
print gdb.correspond('info registers\n')
|
||||
except Exception,e:
|
||||
print e
|
||||
print "# Processed %d instructions." % count
|
||||
|
||||
34249
tests/test_x86_pcmpxstrx.py
Normal file
34249
tests/test_x86_pcmpxstrx.py
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user