diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 19:50:13 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 20:34:34 -0500 |
| commit | e093c21ed880ac3eb72119be15093ee04f8ce299 (patch) | |
| tree | 9f720ebdc0ae415734b1199ed341668c69710a94 /arch/armv7/thumb2_disasm/arm_pcode_parser | |
| parent | 0609276712622908254065546102381466033141 (diff) | |
Move architecture modules into the API repo
Diffstat (limited to 'arch/armv7/thumb2_disasm/arm_pcode_parser')
| -rw-r--r-- | arch/armv7/thumb2_disasm/arm_pcode_parser/README.md | 75 | ||||
| -rwxr-xr-x | arch/armv7/thumb2_disasm/arm_pcode_parser/codegencpp.py | 793 | ||||
| -rwxr-xr-x | arch/armv7/thumb2_disasm/arm_pcode_parser/filter.py | 31 | ||||
| -rw-r--r-- | arch/armv7/thumb2_disasm/arm_pcode_parser/parse.py | 756 | ||||
| -rw-r--r-- | arch/armv7/thumb2_disasm/arm_pcode_parser/pcode.ebnf | 94 |
5 files changed, 1749 insertions, 0 deletions
diff --git a/arch/armv7/thumb2_disasm/arm_pcode_parser/README.md b/arch/armv7/thumb2_disasm/arm_pcode_parser/README.md new file mode 100644 index 00000000..c3adae56 --- /dev/null +++ b/arch/armv7/thumb2_disasm/arm_pcode_parser/README.md @@ -0,0 +1,75 @@ +# goal +translate the pseudocode for arm instructions (given in the docs) to target languages + +once the pcode is extracted, automatic generation of ultra-accurate disassemblers should become possible + +# how +use Grako parser generator, describe the language (pcode.ebnf) and write code generator (codegen.py) + +# example +input statement: +``` +if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE +``` + +output parse tree: +``` +[ + "if", + [ + "n", + [], + [ + "==", + [ + "15", + [] + ], + "||", + [ + "BitCount(", + [ + "registers", + [] + ], + ")" + ], + "<", + [ + "2", + [] + ], + "||", + [ + "(", + "P", + [], + [ + "==", + [ + "'1'", + [] + ], + "&&", + [ + "M", + [] + ], + "==", + [ + "'1'", + [] + ] + ], + [], + ")" + ] + ] + ], + "then", + "UNPREDICTABLE" +] +``` + + + diff --git a/arch/armv7/thumb2_disasm/arm_pcode_parser/codegencpp.py b/arch/armv7/thumb2_disasm/arm_pcode_parser/codegencpp.py new file mode 100755 index 00000000..e66296b9 --- /dev/null +++ b/arch/armv7/thumb2_disasm/arm_pcode_parser/codegencpp.py @@ -0,0 +1,793 @@ +#!/usr/bin/python + +import re +import os +import sys + +from parse import pcodeParser, pcodeSemantics + +DEBUG = 0 + +############################################################################### +# misc utils +############################################################################### + +# convert "MOV (register)" text to the function that handles it +# ->"mov_register" +def convertHandlerName(name): + # non-word chars to underscore + name = re.sub(r'[^\w]', '_', name) + # no leading or trailing underscore + name = re.sub(r'^_*(.*?)_*$', r'\1', name) + # no multiple underscore runs + name = re.sub(r'_+', '_', name) + # lowercase + name = name.lower() + return name + +def applyIndent(text, level=0): + savedTrailingWhitespace = '' + while len(text)>0 and text[-1].isspace(): + savedTrailingWhitespace = text[-1] + savedTrailingWhitespace + text = text[0:-1] + text = text.rstrip() + spacer = '\t' * level + lines = text.split('\n') + lines = map(lambda s: '%s%s' % (spacer, s), lines) + return '\n'.join(lines) + savedTrailingWhitespace + +############################################################################### +# "better" nodes ... cleaned up AST nodes that can eval() themselves +############################################################################### + +class BetterNode(object): + def __init__(self, name, children=[], semicolon=False): + self.name = name + self.children = children + self.semicolon = semicolon + + def gen(self, extra=''): + # leaf nodes (no possible descent) + if self.name == 'ident': + tmp = (self.children[0] + extra).replace('.', '_') + if not tmp.startswith('FIELD_'): + tmp = 'FIELD_' + tmp + code = 'res->fields[%s]' % tmp + elif self.name == 'rawtext': + code = self.children[0] + elif self.name == 'number': + code = self.children[0] + elif self.name == 'bits': + code = '0x%X' % int(self.children[0], 2) + elif self.name == 'see': + code = '\nmemset(res, 0, sizeof(*res));' + code = '\nreturn %s(req, res);' % self.children[0] + self.semicolon = 0 + else: + subCode = map(lambda x: x.gen(), self.children) + subCode = tuple(subCode) + + # binary operations translate directly to C + if self.name == 'xor': + assert len(self.children) == 2 + code = '(%s) ^ (%s)' % subCode + elif self.name == 'add': + assert len(self.children) == 2 + code = '(%s) + (%s)' % subCode + elif self.name == 'sub': + assert len(self.children) == 2 + code = '(%s) - (%s)' % subCode + elif self.name == 'less_than': + assert len(self.children) == 2 + code = '(%s) < (%s)' % subCode + elif self.name == 'greater_than': + assert len(self.children) == 2 + code = '(%s) > (%s)' % subCode + elif self.name == 'log_and': + assert len(self.children) == 2 + code = '(%s) && (%s)' % subCode + elif self.name == 'log_or': + assert len(self.children) == 2 + code = '(%s) || (%s)' % subCode + elif self.name == 'log_not': + assert len(self.children) == 1 + code = '!(%s)' % subCode + elif self.name == 'equals': + assert len(self.children) == 2 + code = '(%s) == (%s)' % subCode + elif self.name == 'not_equals': + assert len(self.children) == 2 + code = '(%s) != (%s)' % subCode + elif self.name == 'less_than_or_equals': + assert len(self.children) == 2 + code = '(%s) <= (%s)' % subCode + elif self.name == 'greater_than_or_equals': + assert len(self.children) == 2 + code = '(%s) >= (%s)' % subCode + elif self.name == 'mul': + assert len(self.children) == 2 + code = '(%s) * (%s)' % subCode + elif self.name == 'div': + assert len(self.children) == 2 + code = '((%s) ? ((%s) / (%s)) : 0)' % (subCode[1], subCode[0], subCode[1]) + elif self.name == 'xor': + assert len(self.children) == 2 + code = '(%s) ^ (%s)' % subCode + elif self.name == 'shl': + assert len(self.children) == 2 + code = '(%s) << (%s)' % subCode + elif self.name == 'rshl': + assert len(self.children) == 2 + code = '(%s) >> (%s)' % subCode + + # function calls to helpers + elif self.name == 'BitCount': + assert len(self.children) == 1 + code = 'BitCount(%s)' % subCode + elif self.name == 'BadReg': + code = 'BadReg(%s)' % subCode + elif self.name == 'Consistent': + assert self.children[0].name == 'ident' + var = self.children[0].gen() + varCheck = self.children[0].gen('_check') + code = '(%s == %s)' % (var, varCheck) + elif self.name == 'DecodeImmShift': + codeA = 'DecodeImmShift_shift_t(%s, %s)' % subCode + codeB = 'DecodeImmShift_shift_n(%s, %s)' % subCode + code = codeA + ';\n' + codeB + elif self.name == 'ThumbExpandImm': + codeA = 'ThumbExpandImm_C_imm32(%s, req->carry_in)' % subCode + # see A6.3.2 ThumbExpandImm_C() for explanation + #codeB = ' if(((%s & 0xC00)==0) && ((%s & 0x300)==1||(%s & 0x300)==2) && (%s & 0xFF)==0) { res->flags |= FLAG_UNPREDICTABLE; }' % tuple([subCode]*4) + codeB = '/* TODO: handle ThumbExpandImm_C\'s possible setting of UNPREDICTABLE */ while(0)' + code = codeA + ';\n' + codeB + elif self.name == 'ThumbExpandImm_C': + codeA = 'ThumbExpandImm_C_imm32(%s, %s)' % subCode + codeB = 'ThumbExpandImm_C_cout(%s, %s)' % subCode + # codeC = ' if(((%s & 0xC00)==0) && ((%s & 0x300)==1||(%s & 0x300)==2) && (%s & 0xFF)==0) { res->flags |= FLAG_UNPREDICTABLE; }' % tuple([subCode]*4) + codeC = '/* TODO: handle ThumbExpandImm_C\'s possible setting of UNPREDICTABLE */ while(0)' + code = codeA + ';\n' + codeB + ';\n' + codeC + elif self.name == 'AdvSIMDExpandImm': + code = "AdvSIMDExpandImm(%s, %s, %s, %s)" % subCode + elif self.name == 'VFPExpandImm': + code = "VFPExpandImm(%s, %s, %s)" % subCode + elif self.name == 'UInt': + code = '(%s)' % subCode[0] + elif self.name == 'ZeroExtend': + assert subCode[1] == '32' + # zero extend is default when assigned to uint32_t + # (which is type of fields[] array) + code = '%s' % subCode[0] + elif self.name == 'Zeros': + code = '/* %s-bit */ 0' % subCode + elif self.name == 'InITBlock': + code = 'req->inIfThen == IFTHEN_YES' + elif self.name == 'LastInITBlock': + code = 'req->inIfThenLast == IFTHENLAST_YES' + elif self.name == 'ArchVersion': + code = 'req->arch' + elif self.name == 'CurrentInstrSet': + code = 'req->instrSet' + elif self.name == 'SignExtend': + code = 'SignExtend(%s,%s)' % (subCode[0], self.children[0].getWidth()) + elif self.name == 'NOT': + code = '(~(%s) & 1)' % subCode + elif self.name == 'IsSecure': + code = "req->arch & ARCH_SECURITY_EXTENSIONS /* || SCR.NS=='0' || CPSR.M=='10110' */" + elif self.name == 'bitslice': + if len(subCode) == 2: + # then there is a single bit to extract + shamt = int(subCode[1]) + if shamt: + code = '((%s >> %d) & 1)' % (subCode[0], shamt) + else: + code = '(%s & 1)' % subCode[0] + else: + # there is a bit range to extract, [hi,lo] + hi = int(subCode[1]) + lo = int(subCode[2]) + assert hi > lo + width = hi-lo+1 # spec's convention is to include the endpoints + if lo: + code = '((%s >> %d) & 0x%X)' % (subCode[0], lo, 2**width-1) + else: + code = '(%s & 0x%X)' % (subCode[0], 2**width-1) + + # if else + elif self.name == 'if': + if len(subCode) == 2: + code = 'if(%s) {\n' % subCode[0] + code += '\t%s\n' % '\n\t'.join(subCode[1].split('\n')) + code += '}' + elif len(subCode) == 3: + code = 'if(%s) {\n' % subCode[0] + code += '\t%s\n' % '\n\t'.join(subCode[1].split('\n')) + code += '}\n' + code += 'else {\n' + code += '\t%s\n' % '\n\t'.join(subCode[2].split('\n')) + code += '}' + # tuples + elif self.name == 'tuple': + code = '\n'.join(subCode) + + # registers eg "registers<t>" + # this is tough 'cause two different types of code are generated + # depending on whether this is being read or written + # we generate read code here and let assignment override it + elif self.name == 'registers': + bitIdxer = self.children[0].gen() + code = '(res->fields[FIELD_registers] & (1<<%s)) >> %s' % (bitIdxer, bitIdxer) + + elif self.name == 'cond': + assert self.children[0].name == 'number' + assert self.children[1].name == 'number' + bitHi = int(self.children[0].gen()) + bitLo = int(self.children[1].gen()) + mask = (2**(bitHi+1)-1) - (2**bitLo-1) + code = '(res->fields[FIELD_cond] & 0x%X) >> %d' % (mask, bitLo) + + # other + elif self.name == 'dummy': + code = '' + elif self.name == 'nop': + code = 'while(0)' + elif self.name == 'Unpredictable': + code = 'res->flags |= FLAG_UNPREDICTABLE' + elif self.name == 'Undefined': + code = 'res->status |= STATUS_UNDEFINED' + elif self.name == 'not_permitted': + code = 'res->flags |= FLAG_NOTPERMITTED' + elif self.name == 'assign': + codeLines = [] + + (lhs, rhs) = self.children + + # special case: tuple + if lhs.name == 'tuple': + rhsCode = rhs.gen() + #codeLines.append("// RHS before split: %s" % (repr(rhsCode))) + rhsCode = re.split(r'[\n;]+', rhsCode) + lhsCode = lhs.gen() + lhsCode = re.split(r'[\n;]+', lhsCode) + #codeLines.append("// LHS: %s RHS: %s" % (repr(lhsCode), repr(rhsCode))) + for (i, dest) in enumerate(lhsCode): + if not dest: # dummy generates '' + continue + codeLines.append('%s = %s' % (dest, rhsCode[i])) + if dest.startswith('res->fields'): + fieldName = dest[dest.index('[') + 1 : dest.index(']')] + codeLines.append('res->fields_mask[%s >> 6] |= 1LL << (%s & 63)' % (fieldName, fieldName)) + # any other statements not assigned to variables continue on + for codeLine in rhsCode[len(lhsCode):]: + codeLines.append(codeLine) + + # special case: a bit (eg: "registers<t> = 1") + elif lhs.name == 'registers': + bitIdxer = lhs.children[0].gen() + rhsBits = rhs.gen() + codeLines.append('res->fields[FIELD_registers] |= (%s << %s)' % (rhsBits, bitIdxer)) + codeLines.append('res->fields_mask[FIELD_registers >> 6] |= 1LL << (FIELD_registers & 63)') + + else: + codeLines.append('%s = %s' % subCode) + if subCode[0].startswith('res->fields'): + fieldName = subCode[0][subCode[0].index('[') + 1 : subCode[0].index(']')] + codeLines.append('res->fields_mask[%s >> 6] |= 1LL << (%s & 63)' % (fieldName, fieldName)) + + code = ';\n'.join(codeLines) + + elif self.name == 'group': + code = '(%s)' % subCode + elif self.name == 'concat': + bitsPushing = 0 + varsPushing = [] + pieces = [] + + for child in reversed(self.children): + # calculate shift amount for this piece + shContributers = [] + if bitsPushing: + shContributers += [str(bitsPushing)] + if varsPushing: + shContributers += varsPushing + + # join them into an expression + shAmt = '' + if len(shContributers) == 1: + shAmt = shContributers[0] + elif len(shContributers) > 1: + shAmt = '(%s)' % '+'.join(shContributers) + + # generate code + if shAmt: + pieces.append('(%s<<%s)' % (child.gen(), shAmt)) + else: + pieces.append('(%s)' % child.gen()) + + # adjust shift amounts for next pieces + if child.name == 'ident': + # if ident is of special form, we know the width (eg: "imm12" has width 12) + m = re.match(r'^[a-zA-Z]+(\d+)$', child.children[0]) + if m: + varsPushing.append(str(m.group(1))) + # else, we rely on a <var>_width variable being present + else: + varsPushing.append(child.children[0]+'_width') + elif child.name == 'bits': + bitsPushing += len(child.children[0]) + else: + raise Exception('concat cannot handle child type %s' % child.name) + + pieces.reverse() + code = '|'.join(pieces) + + # failure + else: + raise Exception("dunno what to do with op %s" % self.name) + + + if self.semicolon: + code += ';' + + return code + + def getWidth(self): + if self.name == 'concat': + bitsPushing = 0 + varsPushing = [] + pieces = [] + + contributors = [] + + for child in reversed(self.children): + # adjust shift amounts for next pieces + if child.name == 'ident': + # if ident is of special form, we know the width (eg: "imm12" has width 12) + m = re.match(r'^[a-zA-Z]+(\d+)$', child.children[0]) + if m: + contributors.append(str(m.group(1))) + # else, we rely on a <var>_width variable being present + else: + contributors.append(child.children[0]+'_width') + elif child.name == 'bits': + contributors += '%s' % len(child.children[0]) + else: + raise Exception('cannot get length of concat child %s' % child.name) + + return '+'.join(contributors) + + else: + raise Exception("trying to get width for %s" % str(self)) + + def __str__(self): + buf = '%s(' % self.name + buf += ','.join(map(str, self.children)) + buf += ')' + return buf + + +############################################################################### +# delegate class that the parser calls after each rule is done +# (replaces PcodeSemantics in parse.py) +# note that arguments to the production rules end up arriving here +############################################################################### + +class PcodeSemantics(object): + + def start(self, ast): + return ast + + def statement(self, ast): + rv = None + + if ast == 'UNPREDICTABLE': + rv = BetterNode('Unpredictable', [], True) + elif ast == 'UNDEFINED': + rv = BetterNode('Undefined', [], True) + elif ast == 'NOT_PERMITTED': + rv = BetterNode('not_permitted', [], True) + elif ast[0] == 'SEE': + assert len(ast)==2 + handler = convertHandlerName(ast[1]) + rv = BetterNode('see', [handler], True) + elif ast in [u'NOP', u'nop']: + rv = BetterNode('nop', [], True) + elif ast[0] == 'if': + children = None + + if len(ast) == 5: + antecedent = ast[1] + assert ast[2] == 'then' + consequent = ast[3] + consequent.semicolon = True + otherwise = None + if ast[4] != []: + assert ast[4][0][0] == 'else' + otherwise = ast[4][0][1] + if otherwise: + children = [antecedent, consequent, otherwise] + else: + children = [antecedent, consequent] + else: + raise Exception('malformed ast for if: ', str(ast)) + + rv = BetterNode('if', children) + + elif ast[1] == '=': + # simple assignments like 'foo = 5' + if len(ast) == 3: + rv = BetterNode('assign', [ast[0], ast[2]], True) + # long assignments like 'foo = if bar == 3 then 1 else 2' + elif len(ast) == 8: + lval = ast[0] + assert ast[2] == 'if' + cond = ast[3] + assert ast[4] == 'then' + trueVal = ast[5] + assert ast[6] == 'else' + falseVal = ast[7] + + trueBlock = BetterNode('assign', [lval, trueVal], True) + falseBlock = BetterNode('assign', [lval, falseVal], True) + + rv = BetterNode('if', [cond, trueBlock, falseBlock]) + else: + raise Exception('dunno what to do in statement semantics, ast is:', ast) + + global DEBUG + if DEBUG: + print("statement: returning", str(rv)) + + return rv + + def tuple(self, ast): + rv = None + + # ast[0] is the '(' + # ast[1] is the initial tuple token + initChild = ast[1] + if initChild == '-': + initChild = BetterNode('dummy') + + rv = BetterNode('tuple', [initChild]) + closure = ast[2] + for i in closure: + assert i[0]==',' + if i[1] == '-': + rv.children.append(BetterNode('dummy')) + else: + rv.children.append(i[1]) + + global DEBUG + if DEBUG: + print("tuple: returning", str(rv)) + + return rv + + def expr0(self, ast): + rv = None + + if type(ast) == type([]): + lookup = {'EOR':'xor', '+':'add', '-':'sub', + '&&':'log_and', '||':'log_or' } + + cur = ast[0] + closure = ast[1] + + for i in closure: + op = i[0] + nodeName = lookup[op] + + cur = BetterNode(nodeName, [cur, i[1]]) + + rv = cur + else: + rv = ast + + global DEBUG + if DEBUG: + print("expr0: returning", str(rv)) + + return rv + + def expr1(self, ast): + rv = ast + + if type(ast) == type([]): + lookup = {'*':'mul', '/':'div', 'XOR':'xor', 'DIV':'div', '==':'equals', '!=':'not_equals', + '<':'less_than', '>':'greater_than', '<<':'shl', '>>':'rshl', + '>=':'greater_than_or_equals', '<=':'less_than_or_equals'} + + cur = ast[0] + closure = ast[1] + + for i in closure: + op = i[0] + nodeName = lookup[op] + + cur = BetterNode(nodeName, [cur, i[1]]) + + rv = cur + else: + rv = ast + + global DEBUG + if DEBUG: + print("expr1: returning", str(rv)) + + return rv + + def expr2(self, ast): + rv = ast + + global DEBUG + if DEBUG: + print("expr2: returning", rv) + + return rv + + def expr3(self, ast): + rv = 'BLUNDER' + + if type(ast) == type([]): + #print('ast is: ', ast) + + # empty closure, return original + if len(ast)==2 and ast[1]==[]: + rv = ast[0] + elif len(ast)>1: + if ast[0] == '(': + rv = BetterNode('group', [ast[1]]) + elif ast[0] == '!': + rv = BetterNode('log_not', [ast[1]]) + elif type(ast[1]==[]): + closure = ast[1] + assert closure[0][0] == ':' + bn = BetterNode('concat', [ast[0], closure[0][1]]) + closure = closure[1:] + for i in closure: + assert i[0] == ':' + bn.children.append(i[1]) + rv = bn + else: + raise Exception("expr3(): unexpected ast: " + str(ast)) + + else: + rv = ast + + global DEBUG + if DEBUG: + print("expr3: returning", str(rv)) + + return rv + + # + def number(self, ast): + # ast is just X where X is the number itself + rv = BetterNode('number', [str(ast)]) + + global DEBUG + if DEBUG: + print("number: returning", str(rv)) + + return rv + + def bits(self, ast): + rv = BetterNode('bits', [str(ast[1:-1])]) + + global DEBUG + if DEBUG: + print("bits: returning", str(rv)) + + return rv + + def ident(self, ast): + #print('input ast is: ', str(ast)) + + # "foo" has ast ['foo', []] + # "foo<3>" has ast ['foo', [['<', BetterNode(3), '>']]] + # "foo<3,5>" has ast + + rv = BetterNode('ident', [str(ast)]) + + global DEBUG + if DEBUG: + print("ident: returning", rv) + + return rv + + def sliceable(self, ast): + #print(ast) + + m = re.match(r'^(.*)<$', ast[0]) + if not m: + raise Exception('malformed sliceable statement') + ident = BetterNode('ident', [m.group(1)]) + + if len(ast)==3: + #print(str([m.group(1), ast[1]])) + return BetterNode('bitslice', [ident, ast[1]]) + elif len(ast)==5: + return BetterNode('bitslice', [ident, ast[1], ast[3]]) + else: + raise Exception("sliceable confused by: %s" % str(ast)) + + def builtin_value(self, ast): + lookup = {'FALSE':'0', 'TRUE':'1', 'SRType_LSL':'0', 'SRType_LSR':'1', + 'SRType_ASR':'2', 'SRType_ROR':'3', 'SRType_RRX':'4', + 'ARM_GRP_INVALID':0, 'ARM_GRP_JUMP':1, 'ARM_GRP_CRYPT':128, + 'ARM_GRP_DATABARRIER':129, 'ARM_GRP_DIVIDE':130, 'ARM_GRP_FPARMV8':131, + 'ARM_GRP_MULTPRO':132, 'ARM_GRP_NEON':133, 'ARM_GRP_T2EXTRACTPACK':134, + 'ARM_GRP_THUMB2DSP':135, 'ARM_GRP_TRUSTZONE':136, 'ARM_GRP_V4T':137, + 'ARM_GRP_V5T':138, 'ARM_GRP_V5TE':139, 'ARM_GRP_V6':140, + 'ARM_GRP_V6T2':141, 'ARM_GRP_V7':142, 'ARM_GRP_V8':143, + 'ARM_GRP_VFP2':144, 'ARM_GRP_VFP3':145, 'ARM_GRP_VFP4':146, + 'ARM_GRP_ARM':147, 'ARM_GRP_MCLASS':148, 'ARM_GRP_NOTMCLASS':149, + 'ARM_GRP_THUMB':150, 'ARM_GRP_THUMB1ONLY':151, 'ARM_GRP_THUMB2':152, + 'ARM_GRP_PREV8':153, 'ARM_GRP_FPVMLX':154, 'ARM_GRP_MULOPS':155, + 'ARM_GRP_CRC':156, 'ARM_GRP_DPVFP':157, 'ARM_GRP_V6M':158} + + # directly to numbers + if ast[0] == 'registers<': + assert ast[2] == '>' + rv = BetterNode('registers', [ast[1]]) + elif ast[0] == 'cond<': + assert ast[2] == ':' + assert ast[4] == '>' + rv = BetterNode('cond', [ast[1], ast[3]]) + elif ast == 'InstrSet_ThumbEE': + rv = BetterNode('rawtext', ['INSTRSET_THUMBEE']) + elif type(ast) == type(u'foo'): + rv = BetterNode('number', [lookup[ast]]) + else: + raise Exception("builtin_value doesn't know how to handle ", ast) + + global DEBUG + if DEBUG: + print("builtin_value: returning", rv) + + return rv + + def func_call(self, ast): + funcName = 'BLUNDER' + args = [] + rv = None + + # function without arguments + if type(ast) == type(u'x'): + funcName = ast[:-2] + # function with arguments + elif type(ast) == type([]): + funcName = str(ast[0][:-1]) + args = filter(lambda x: x!=',', ast[1:-1]) + + rv = BetterNode(funcName, args) + + global DEBUG + if DEBUG: + print("func_call: returning", rv) + + return rv + +############################################################################### +# function for library consumers +############################################################################### + +# take as input a single pcode statement +def gen(pcode, rule='start', comments=True): + + # strip trailing whitespace or semicolons + while pcode[-1] in [' ', '\t', ';']: + pcode = pcode[0:-1] + + code = '' + parser = pcodeParser(parseInfo=False) + if comments: + code = '/* pcode: %s */\n' % pcode + tree = parser.parse(pcode, rule_name=rule, semantics=PcodeSemantics()) + code += tree.gen() + return code + +# take as input multiple pcode statements (separated by ";\n") +def genBlock(pcode, comments=True): + # + result = [] + + # split on newlines + lines = pcode.split('\n') + + # if there are multiple statements on a line, split them into multiple + # lines, preserving the leading whitespace + tmp = [] + for l in lines: + if not l or l.isspace(): + continue + + if l.count(';') <= 1: + tmp.append(l.replace(';', '')) + continue + + m = re.match(r'^(\s*)(.*)$', l) + leadSpace = m.group(1) + for statement in m.group(2).split(';'): + if not statement or statement.isspace(): + continue + m2 = re.match(r'^(\s*)(.*)$', statement) + tmp.append(leadSpace + m2.group(2)) + + lines = tmp + + if 0: + print('after mass-lining:') + print('\n'.join(lines)) + + # generate for each line, picking out case/when statements + (caseVar, indent) = (None, 0) + + for l in lines: + #print('line is: -%s-' % l) + if l[0:5] == 'case ': + m = re.match(r'^case (.*) of', l) + result.append('/* pcode: %s */' % l.lstrip()) + (caseVar, indent) = (m.group(1), 1) + + elif l[0:6] == '\twhen ' or l[0:9] == ' when ': + keywords = 'else\nif' + + if indent == 1: + # then we just started the "case ..." + keywords = 'if' + elif indent == 2: + result.append('}') + indent = 1 + else: + raise Exception('expect "when" with 1 or 2 tab') + + m = re.match(r'^\s+when (.*)', l) + clause = gen(m.group(1), 'expr0', False) + result.append('/* pcode: %s */' % l.lstrip()) + result.append('%s(res->fields[FIELD_%s] == %s) {' % (keywords, caseVar, clause)) + + indent = 2 + + elif l[0:2] == '\t\t' or l[0:8] == ' ': + if indent != 2: + raise Exception('unexpected indent, is it under a "when" ?') + m = re.match(r'^\s+(.*)', l) + code = gen(m.group(1)) + code = applyIndent(code, 1) + result.append(code) + + else: + if indent > 0: + result.append('}') + (caseVar, indent) = (None, 0) + code = gen(l) + result.append(code) + + return '\n'.join(result) + +############################################################################### +# main +############################################################################### + +testTarget = None + +if __name__ == '__main__': + if len(sys.argv) > 1 and os.path.isfile(sys.argv[1]): + fp = open(sys.argv[1], 'r') + stuff = fp.read() + fp.close() + + print(genBlock(stuff)) + sys.exit(0) + else: + DEBUG = 1 + statement = sys.argv[1] + + parser = pcodeParser(parseInfo=False) + ast = parser.parse(statement, rule_name='start', semantics=PcodeSemantics()) + print('true abstract syntax tree:') + print(ast) + print('generated code:') + print(ast.gen()) diff --git a/arch/armv7/thumb2_disasm/arm_pcode_parser/filter.py b/arch/armv7/thumb2_disasm/arm_pcode_parser/filter.py new file mode 100755 index 00000000..bd385d48 --- /dev/null +++ b/arch/armv7/thumb2_disasm/arm_pcode_parser/filter.py @@ -0,0 +1,31 @@ +#!/usr/bin/python + +# the docs will use a strange angled single quote, and when copy pasted +# it shows up at by sequence \xe2\x80\x98 or \xe2\x80\x99 and a similar +# case for double quotes + +# this remedies that problem on a given file + + +import os +import sys + +print "filtering %s" % sys.argv[1] +fp = open(sys.argv[1],'rb') +buf = fp.read() +fp.close() + +len0 = len(buf) +print "file size before: %d\n" % len0 +buf = buf.replace("\xe2\x80\x98", "'") +buf = buf.replace("\xe2\x80\x99", "'") +buf = buf.replace("\xe2\x80\x9C", '"') +buf = buf.replace("\xe2\x80\x9D", '"') +len1 = len(buf) +print "file size after: %d\n" % len1 +print "(%d stupid quotes replaced)" % ((len0-len1)/3) + +fp = open(sys.argv[1],'wb') +fp.write(buf) +fp.close() + diff --git a/arch/armv7/thumb2_disasm/arm_pcode_parser/parse.py b/arch/armv7/thumb2_disasm/arm_pcode_parser/parse.py new file mode 100644 index 00000000..cd886f47 --- /dev/null +++ b/arch/armv7/thumb2_disasm/arm_pcode_parser/parse.py @@ -0,0 +1,756 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# CAVEAT UTILITOR +# +# This file was automatically generated by Grako. +# +# https://pypi.python.org/pypi/grako/ +# +# Any changes you make to it will be overwritten the next time +# the file is generated. + + +from __future__ import print_function, division, absolute_import, unicode_literals + +from grako.buffering import Buffer +from grako.parsing import graken, Parser +from grako.util import re, RE_FLAGS, generic_main # noqa + + +KEYWORDS = {} + + +class pcodeBuffer(Buffer): + def __init__( + self, + text, + whitespace=None, + nameguard=None, + comments_re=None, + eol_comments_re=None, + ignorecase=None, + namechars='', + **kwargs + ): + super(pcodeBuffer, self).__init__( + text, + whitespace=whitespace, + nameguard=nameguard, + comments_re=comments_re, + eol_comments_re=eol_comments_re, + ignorecase=ignorecase, + namechars=namechars, + **kwargs + ) + + +class pcodeParser(Parser): + def __init__( + self, + whitespace=None, + nameguard=None, + comments_re=None, + eol_comments_re=None, + ignorecase=None, + left_recursion=False, + parseinfo=True, + keywords=None, + namechars='', + buffer_class=pcodeBuffer, + **kwargs + ): + if keywords is None: + keywords = KEYWORDS + super(pcodeParser, self).__init__( + whitespace=whitespace, + nameguard=nameguard, + comments_re=comments_re, + eol_comments_re=eol_comments_re, + ignorecase=ignorecase, + left_recursion=left_recursion, + parseinfo=parseinfo, + keywords=keywords, + namechars=namechars, + buffer_class=buffer_class, + **kwargs + ) + + @graken() + def _start_(self): + self._statement_() + with self._optional(): + self._token(';') + self._check_eof() + + @graken() + def _statement_(self): + with self._choice(): + with self._option(): + self._token('if') + self._expr0_() + self._token('then') + self._statement_() + + def block0(): + self._token('else') + self._statement_() + self._closure(block0) + with self._option(): + self._token('UNPREDICTABLE') + with self._option(): + self._token('UNDEFINED') + with self._option(): + self._token('NOT_PERMITTED') + with self._option(): + self._token('NOP') + with self._option(): + self._token('nop') + with self._option(): + self._token('SEE') + self._whatever_() + with self._option(): + self._tuple_() + self._token('=') + self._expr0_() + with self._option(): + self._ident_() + self._token('=') + self._token('if') + self._expr0_() + self._token('then') + self._expr0_() + self._token('else') + self._expr0_() + with self._option(): + self._expr0_() + self._token('=') + self._expr0_() + self._error('expecting one of: NOP NOT_PERMITTED UNDEFINED UNPREDICTABLE nop') + + @graken() + def _tuple_(self): + self._token('(') + with self._group(): + with self._choice(): + with self._option(): + self._token('-') + with self._option(): + self._expr0_() + self._error('expecting one of: -') + + def block1(): + self._token(',') + with self._group(): + with self._choice(): + with self._option(): + self._token('-') + with self._option(): + self._expr0_() + self._error('expecting one of: -') + self._positive_closure(block1) + self._token(')') + + @graken() + def _expr0_(self): + with self._choice(): + with self._option(): + self._expr1_() + + def block0(): + with self._group(): + with self._choice(): + with self._option(): + self._token('EOR') + with self._option(): + self._token('+') + with self._option(): + self._token('-') + with self._option(): + self._token('&&') + with self._option(): + self._token('||') + self._error('expecting one of: && + - EOR ||') + self._expr1_() + self._positive_closure(block0) + with self._option(): + self._expr1_() + self._error('no available options') + + @graken() + def _expr1_(self): + with self._choice(): + with self._option(): + self._expr2_() + + def block0(): + with self._group(): + with self._choice(): + with self._option(): + self._token('*') + with self._option(): + self._token('/') + with self._option(): + self._token('<<') + with self._option(): + self._token('>>') + with self._option(): + self._token('DIV') + with self._option(): + self._token('XOR') + self._error('expecting one of: * / << >> DIV XOR') + self._expr2_() + self._positive_closure(block0) + with self._option(): + self._expr2_() + + def block2(): + with self._group(): + with self._choice(): + with self._option(): + self._token('==') + with self._option(): + self._token('!=') + with self._option(): + self._token('<=') + with self._option(): + self._token('>=') + with self._option(): + self._token('<') + with self._option(): + self._token('>') + self._error('expecting one of: != < <= == > >=') + self._expr2_() + self._positive_closure(block2) + with self._option(): + self._expr2_() + self._error('no available options') + + @graken() + def _expr2_(self): + with self._choice(): + with self._option(): + self._func_call_() + with self._option(): + self._expr3_() + self._error('no available options') + + @graken() + def _expr3_(self): + with self._choice(): + with self._option(): + self._builtin_value_() + with self._option(): + self._sliceable_() + with self._option(): + with self._group(): + with self._choice(): + with self._option(): + self._ident_() + with self._option(): + self._number_() + with self._option(): + self._bits_() + self._error('no available options') + + def block1(): + self._token(':') + with self._group(): + with self._choice(): + with self._option(): + self._ident_() + with self._option(): + self._number_() + with self._option(): + self._bits_() + self._error('no available options') + self._closure(block1) + with self._option(): + self._tuple_() + with self._option(): + self._token('(') + self._expr0_() + self._token(')') + with self._option(): + self._token('!') + self._expr0_() + self._error('no available options') + + @graken() + def _number_(self): + self._pattern(r'\d+') + + @graken() + def _bits_(self): + self._pattern(r"'[01]+'") + + @graken() + def _ident_(self): + self._pattern(r'[a-zA-Z][\.\w]*') + + @graken() + def _whatever_(self): + self._pattern(r'.*') + + @graken() + def _sliceable_(self): + with self._choice(): + with self._option(): + self._token('index_align<') + self._number_() + self._token(':') + self._number_() + self._token('>') + with self._option(): + self._token('index_align<') + self._number_() + self._token('>') + with self._option(): + self._token('align<') + self._number_() + self._token('>') + with self._option(): + self._token('mask<') + self._number_() + self._token('>') + with self._option(): + self._token('imod<') + self._number_() + self._token('>') + with self._option(): + self._token('imm6<') + self._number_() + self._token('>') + with self._option(): + self._token('imm6<') + self._number_() + self._token(':') + self._number_() + self._token('>') + with self._option(): + self._token('imm8<') + self._number_() + self._token('>') + with self._option(): + self._token('Vd<') + self._number_() + self._token('>') + with self._option(): + self._token('Vn<') + self._number_() + self._token('>') + with self._option(): + self._token('Vm<') + self._number_() + self._token('>') + with self._option(): + self._token('Vm<') + self._number_() + self._token(':') + self._number_() + self._token('>') + with self._option(): + self._token('cc<') + self._number_() + self._token('>') + with self._option(): + self._token('cmode<') + self._number_() + self._token('>') + with self._option(): + self._token('cmode<') + self._number_() + self._token(':') + self._number_() + self._token('>') + self._error('no available options') + + @graken() + def _builtin_value_(self): + with self._choice(): + with self._option(): + self._token('TRUE') + with self._option(): + self._token('FALSE') + with self._option(): + self._token('registers<') + with self._group(): + with self._choice(): + with self._option(): + self._number_() + with self._option(): + self._ident_() + self._error('no available options') + self._token('>') + with self._option(): + self._token('list<') + with self._group(): + with self._choice(): + with self._option(): + self._number_() + with self._option(): + self._ident_() + self._error('no available options') + self._token('>') + with self._option(): + self._token('cond<') + self._number_() + self._token(':') + self._number_() + self._token('>') + with self._option(): + self._token('cond<') + self._expr0_() + self._token('>') + with self._option(): + self._token('SRType_LSL') + with self._option(): + self._token('SRType_LSR') + with self._option(): + self._token('SRType_ASR') + with self._option(): + self._token('SRType_ROR') + with self._option(): + self._token('SRType_RRX') + with self._option(): + self._token('InstrSet_ThumbEE') + with self._option(): + self._token('ARM_GRP_INVALID') + with self._option(): + self._token('ARM_GRP_JUMP') + with self._option(): + self._token('ARM_GRP_CRYPT') + with self._option(): + self._token('ARM_GRP_DATABARRIER') + with self._option(): + self._token('ARM_GRP_DIVIDE') + with self._option(): + self._token('ARM_GRP_FPARMV8') + with self._option(): + self._token('ARM_GRP_MULTPRO') + with self._option(): + self._token('ARM_GRP_NEON') + with self._option(): + self._token('ARM_GRP_T2EXTRACTPACK') + with self._option(): + self._token('ARM_GRP_THUMB2DSP') + with self._option(): + self._token('ARM_GRP_TRUSTZONE') + with self._option(): + self._token('ARM_GRP_V4T') + with self._option(): + self._token('ARM_GRP_V5T') + with self._option(): + self._token('ARM_GRP_V5TE') + with self._option(): + self._token('ARM_GRP_V6') + with self._option(): + self._token('ARM_GRP_V6T2') + with self._option(): + self._token('ARM_GRP_V7') + with self._option(): + self._token('ARM_GRP_V8') + with self._option(): + self._token('ARM_GRP_VFP2') + with self._option(): + self._token('ARM_GRP_VFP3') + with self._option(): + self._token('ARM_GRP_VFP4') + with self._option(): + self._token('ARM_GRP_ARM') + with self._option(): + self._token('ARM_GRP_MCLASS') + with self._option(): + self._token('ARM_GRP_NOTMCLASS') + with self._option(): + self._token('ARM_GRP_THUMB') + with self._option(): + self._token('ARM_GRP_THUMB1ONLY') + with self._option(): + self._token('ARM_GRP_THUMB2') + with self._option(): + self._token('ARM_GRP_PREV8') + with self._option(): + self._token('ARM_GRP_FPVMLX') + with self._option(): + self._token('ARM_GRP_MULOPS') + with self._option(): + self._token('ARM_GRP_CRC') + with self._option(): + self._token('ARM_GRP_DPVFP') + with self._option(): + self._token('ARM_GRP_V6M') + self._error('expecting one of: ARM_GRP_ARM ARM_GRP_CRC ARM_GRP_CRYPT ARM_GRP_DATABARRIER ARM_GRP_DIVIDE ARM_GRP_DPVFP ARM_GRP_FPARMV8 ARM_GRP_FPVMLX ARM_GRP_INVALID ARM_GRP_JUMP ARM_GRP_MCLASS ARM_GRP_MULOPS ARM_GRP_MULTPRO ARM_GRP_NEON ARM_GRP_NOTMCLASS ARM_GRP_PREV8 ARM_GRP_T2EXTRACTPACK ARM_GRP_THUMB ARM_GRP_THUMB1ONLY ARM_GRP_THUMB2 ARM_GRP_THUMB2DSP ARM_GRP_TRUSTZONE ARM_GRP_V4T ARM_GRP_V5T ARM_GRP_V5TE ARM_GRP_V6 ARM_GRP_V6M ARM_GRP_V6T2 ARM_GRP_V7 ARM_GRP_V8 ARM_GRP_VFP2 ARM_GRP_VFP3 ARM_GRP_VFP4 FALSE InstrSet_ThumbEE SRType_ASR SRType_LSL SRType_LSR SRType_ROR SRType_RRX TRUE') + + @graken() + def _func_call_(self): + with self._choice(): + with self._option(): + self._bitcount_() + with self._option(): + self._badreg_() + with self._option(): + self._consistent_() + with self._option(): + self._decodeimmshift_() + with self._option(): + self._thumbexpandimm_() + with self._option(): + self._thumbexpandimm_c_() + with self._option(): + self._advsimdexpandimm_() + with self._option(): + self._vfpexpandimm_() + with self._option(): + self._uint_() + with self._option(): + self._zeroextend_() + with self._option(): + self._zeros_() + with self._option(): + self._initblock_() + with self._option(): + self._lastinitblock_() + with self._option(): + self._archversion_() + with self._option(): + self._currentinstrset_() + with self._option(): + self._signextend_() + with self._option(): + self._not_() + with self._option(): + self._issecure_() + self._error('no available options') + + @graken() + def _bitcount_(self): + self._token('BitCount(') + self._expr0_() + self._token(')') + + @graken() + def _badreg_(self): + self._token('BadReg(') + self._expr0_() + self._token(')') + + @graken() + def _consistent_(self): + self._token('Consistent(') + self._expr0_() + self._token(')') + + @graken() + def _decodeimmshift_(self): + self._token('DecodeImmShift(') + self._expr0_() + self._token(',') + self._expr0_() + self._token(')') + + @graken() + def _thumbexpandimm_(self): + self._token('ThumbExpandImm(') + self._expr0_() + self._token(')') + + @graken() + def _thumbexpandimm_c_(self): + self._token('ThumbExpandImm_C(') + self._expr0_() + self._token(',') + self._expr0_() + self._token(')') + + @graken() + def _advsimdexpandimm_(self): + self._token('AdvSIMDExpandImm(') + self._expr0_() + self._token(',') + self._expr0_() + self._token(',') + self._expr0_() + self._token(',') + self._expr0_() + self._token(')') + + @graken() + def _vfpexpandimm_(self): + self._token('VFPExpandImm(') + self._expr0_() + self._token(',') + self._expr0_() + self._token(',') + self._expr0_() + self._token(')') + + @graken() + def _uint_(self): + self._token('UInt(') + self._expr0_() + self._token(')') + + @graken() + def _zeroextend_(self): + self._token('ZeroExtend(') + self._expr0_() + self._token(',') + self._expr0_() + self._token(')') + + @graken() + def _zeros_(self): + self._token('Zeros(') + self._expr0_() + self._token(')') + + @graken() + def _initblock_(self): + self._token('InITBlock()') + + @graken() + def _lastinitblock_(self): + self._token('LastInITBlock()') + + @graken() + def _archversion_(self): + self._token('ArchVersion()') + + @graken() + def _currentinstrset_(self): + self._token('CurrentInstrSet()') + + @graken() + def _signextend_(self): + self._token('SignExtend(') + self._expr3_() + self._token(', 32)') + + @graken() + def _not_(self): + self._token('NOT(') + self._expr0_() + self._token(')') + + @graken() + def _issecure_(self): + self._token('IsSecure()') + + +class pcodeSemantics(object): + def start(self, ast): + return ast + + def statement(self, ast): + return ast + + def tuple(self, ast): + return ast + + def expr0(self, ast): + return ast + + def expr1(self, ast): + return ast + + def expr2(self, ast): + return ast + + def expr3(self, ast): + return ast + + def number(self, ast): + return ast + + def bits(self, ast): + return ast + + def ident(self, ast): + return ast + + def whatever(self, ast): + return ast + + def sliceable(self, ast): + return ast + + def builtin_value(self, ast): + return ast + + def func_call(self, ast): + return ast + + def bitcount(self, ast): + return ast + + def badreg(self, ast): + return ast + + def consistent(self, ast): + return ast + + def decodeimmshift(self, ast): + return ast + + def thumbexpandimm(self, ast): + return ast + + def thumbexpandimm_c(self, ast): + return ast + + def advsimdexpandimm(self, ast): + return ast + + def vfpexpandimm(self, ast): + return ast + + def uint(self, ast): + return ast + + def zeroextend(self, ast): + return ast + + def zeros(self, ast): + return ast + + def initblock(self, ast): + return ast + + def lastinitblock(self, ast): + return ast + + def archversion(self, ast): + return ast + + def currentinstrset(self, ast): + return ast + + def signextend(self, ast): + return ast + + def not_(self, ast): + return ast + + def issecure(self, ast): + return ast + + +def main(filename, startrule, **kwargs): + with open(filename) as f: + text = f.read() + parser = pcodeParser() + return parser.parse(text, startrule, filename=filename, **kwargs) + + +if __name__ == '__main__': + import json + from grako.util import asjson + + ast = generic_main(main, pcodeParser, name='pcode') + print('AST:') + print(ast) + print() + print('JSON:') + print(json.dumps(asjson(ast), indent=2)) + print() diff --git a/arch/armv7/thumb2_disasm/arm_pcode_parser/pcode.ebnf b/arch/armv7/thumb2_disasm/arm_pcode_parser/pcode.ebnf new file mode 100644 index 00000000..b72423b0 --- /dev/null +++ b/arch/armv7/thumb2_disasm/arm_pcode_parser/pcode.ebnf @@ -0,0 +1,94 @@ +start = statement [';'] $; + +statement = 'if' expr0 'then' statement {'else' statement} | + "UNPREDICTABLE" | + "UNDEFINED" | + "NOT_PERMITTED" | + "NOP" | "nop" | + "SEE" whatever | + tuple '=' expr0 | + ident '=' 'if' expr0 'then' expr0 'else' expr0 | + expr0 '=' expr0; + +# tuples +tuple = '(' ('-'|expr0) { ',' ('-'|expr0) }+ ')'; + +# could use kleen star here instead of alternative rule, but I don't +# want to get back empty closures +expr0 = expr1 {('EOR' | '+' | '-' | '&&' | '||') expr1}+ | + expr1; + +expr1 = expr2 {('*'|'/'|'<<'|'>>'|'DIV'|'XOR') expr2}+ | + expr2 {('==' | '!=' | '<=' | '>=' | '<' | '>') expr2}+ | + expr2; + +expr2 = func_call | + expr3; + +expr3 = builtin_value | + sliceable | + (ident|number|bits) {':'(ident|number|bits)}* | + tuple | + '(' expr0 ')' | + '!' expr0; + +number = /\d+/; + +bits = /'[01]+'/; + +ident = /[a-zA-Z][\.\w]*/; + +whatever = /.*/; + +# the variables that can have bit slices ... these are made separate +# because the intersect with the greater-than, less-than comparisons +sliceable = 'index_align<' number ':' number '>' | + 'index_align<' number '>' | + 'align<' number '>' | + 'mask<' number '>' | + 'imod<' number '>' | + 'imm6<' number '>' | + 'imm6<' number ':' number '>' | + 'imm8<' number '>' | + 'Vd<' number '>' | + 'Vn<' number '>' | + 'Vm<' number '>' | + 'Vm<' number ':' number '>' | + 'cc<' number '>' | + 'cmode<' number '>' | + 'cmode<' number ':' number '>'; + +builtin_value = 'TRUE' | + 'FALSE' | + 'registers<' (number|ident) '>' | + 'list<' (number|ident) '>' | + 'cond<' number ':' number '>' | + 'cond<' expr0 '>' | + 'SRType_LSL' | 'SRType_LSR' | 'SRType_ASR' | 'SRType_ROR' | 'SRType_RRX' | + 'InstrSet_ThumbEE' | + 'ARM_GRP_INVALID' | 'ARM_GRP_JUMP' | 'ARM_GRP_CRYPT' | 'ARM_GRP_DATABARRIER' | 'ARM_GRP_DIVIDE' | 'ARM_GRP_FPARMV8' | 'ARM_GRP_MULTPRO' | 'ARM_GRP_NEON' | 'ARM_GRP_T2EXTRACTPACK' | 'ARM_GRP_THUMB2DSP' | 'ARM_GRP_TRUSTZONE' | 'ARM_GRP_V4T' | 'ARM_GRP_V5T' | 'ARM_GRP_V5TE' | 'ARM_GRP_V6' | 'ARM_GRP_V6T2' | 'ARM_GRP_V7' | 'ARM_GRP_V8' | 'ARM_GRP_VFP2' | 'ARM_GRP_VFP3' | 'ARM_GRP_VFP4' | 'ARM_GRP_ARM' | 'ARM_GRP_MCLASS' | 'ARM_GRP_NOTMCLASS' | 'ARM_GRP_THUMB' | 'ARM_GRP_THUMB1ONLY' | 'ARM_GRP_THUMB2' | 'ARM_GRP_PREV8' | 'ARM_GRP_FPVMLX' | 'ARM_GRP_MULOPS' | 'ARM_GRP_CRC' | 'ARM_GRP_DPVFP' | 'ARM_GRP_V6M'; + +# function calls +func_call = bitcount | badreg | consistent | decodeimmshift | thumbexpandimm | + thumbexpandimm_c | advsimdexpandimm | vfpexpandimm | uint | zeroextend | zeros | initblock | lastinitblock | + archversion | currentinstrset | signextend | not | issecure; + +bitcount = 'BitCount(' expr0 ')'; +badreg = 'BadReg(' expr0 ')'; +consistent = 'Consistent(' expr0 ')'; +decodeimmshift = 'DecodeImmShift(' expr0 ',' expr0 ')'; +thumbexpandimm = 'ThumbExpandImm(' expr0 ')'; +thumbexpandimm_c = 'ThumbExpandImm_C(' expr0 ',' expr0 ')'; +advsimdexpandimm = 'AdvSIMDExpandImm(' expr0 ',' expr0 ',' expr0 ',' expr0 ')'; +vfpexpandimm = 'VFPExpandImm(' expr0 ',' expr0 ',' expr0 ')'; +uint = 'UInt(' expr0 ')'; +zeroextend = 'ZeroExtend(' expr0 ',' expr0 ')'; +zeros = 'Zeros(' expr0 ')'; +initblock = 'InITBlock()'; +lastinitblock = 'LastInITBlock()'; +archversion = 'ArchVersion()'; +currentinstrset = 'CurrentInstrSet()'; +signextend = 'SignExtend(' expr3 ', 32)'; +not = 'NOT(' expr0 ')'; +issecure = 'IsSecure()'; + |
