def _gen_default_action(self, nts): ''' add to to each nonterminal the default action that should be taken in case the no rule was satisfied ''' for nt in nts: if nt.otherwise == 'error': err_fb = 'ERROR=XED_ERROR_GENERAL_ERROR' nt.default_action = actions.action_t(err_fb) else: #creating return action which return nothing nt.default_action = actions.gen_return_action('')
def __init__(self, ii, eosz_nts, easz_nts, imm_nts, disp_nts, brdisp_nts, mode_space, state_space): self.ptrn = ii.ipattern_input self.ptrn_wrds = self.ptrn.split() self.iclass = ii.iclass self.legal = True self.category = ii.category #FIXME: remove all members of ii stored directly as members self.ii = ii #incomplete_opcode is used for expanding opcodes that have registers #embedded in them self.incomplete_opcode = False #number of missing bits in incomplete opcode. usually 0 or 3 self.missing_bits = 0 self.insn_map = None self.opcode = None self.space = None # LEGACY|VEX|EVEX self.has_modrm = False self.imm_nt_seq = None self.disp_nt_seq = None #modrm.reg bits value, set only when it is explicitly #e.g. bounded: REG[010] self.ext_opcode = None #all legal values for MODE operand in this pattern self.mode = None #an ordered string of EOSZ setting NTs in the pattern #we will use it to create the eosz lookup table for the pattern self.eosz_nt_seq = None #same for EASZ self.easz_nt_seq = None #operand deciders of the pattern self.constraints = None self._set_constraints(ii, state_space) self.vv = None # vexvalid, integer self._set_vexvalid() self.encspace = None self._set_encoding_space() mi,insn_map,opcode = self._get_map_opcode() self.map_info = mi self.insn_map = insn_map self.opcode = opcode self.has_modrm = ild_modrm.get_hasmodrm(self.ptrn) self.set_ext_opcode() self.set_mode(ii, mode_space) self.eosz_nt_seq = ild_eosz.get_eosz_nt_seq(self.ptrn_wrds, eosz_nts) self.easz_nt_seq = ild_easz.get_easz_nt_seq(self.ptrn_wrds, easz_nts) self.imm_nt_seq = ild_imm.get_imm_nt_seq(self.ptrn_wrds, imm_nts) self.disp_nt_seq = ild_disp.get_disp_nt_seq(self.ptrn_wrds, disp_nts.union(brdisp_nts)) self.actions = [actions.gen_return_action(ii.inum)]
def __init__(self, ii, is_3dnow, eosz_nts, easz_nts, imm_nts, disp_nts, brdisp_nts, mode_space, state_space): # FIXME 2012-06-19 MJC: is there a better way to do complex # init of class attributes? if pattern_t.first: pattern_t.first = False self._setup_phys_map(is_3dnow) self.ptrn = ii.ipattern_input self.ptrn_wrds = self.ptrn.split() self.iclass = ii.iclass self.legal = True #amd 3dnow instructions have nasty 0f 0f ... opcode pattern #in which second 0f is not an opcode! This should be treated #in a special way self.amd3dnow_build = is_3dnow #this one is NOT used DELETE IT ??? self.category = ii.category #FIXME: remove all members of ii stored directly as members self.ii = ii #incomplete_opcode is used for expanding opcodes that have registers #embedded in them self.incomplete_opcode = False #number of missing bits in incomplete opcode. usually 0 or 3 self.missing_bits = 0 self.insn_map = None self.opcode = None self.space = None # LEGACY|VEX|EVEX self.has_modrm = False self.imm_nt_seq = None self.disp_nt_seq = None #modrm.reg bits value, set only when it is explicitly #e.g. bounded: REG[010] self.ext_opcode = None #all legal values for MODE operand in this pattern self.mode = None #an ordered string of EOSZ setting NTs in the pattern #we will use it to create the eosz lookup table for the pattern self.eosz_nt_seq = None #same for EASZ self.easz_nt_seq = None #operand deciders of the pattern #FIXME: not finished yet self.constraints = collections.defaultdict(dict) insn_map, opcode = self.get_map_opcode() self.insn_map = insn_map self.opcode = opcode self.has_modrm = ild_modrm.get_hasmodrm(self.ptrn) self.set_ext_opcode() self.set_mode(ii, mode_space) self.eosz_nt_seq = ild_eosz.get_eosz_nt_seq(self.ptrn_wrds, eosz_nts) self.easz_nt_seq = ild_easz.get_easz_nt_seq(self.ptrn_wrds, easz_nts) self.imm_nt_seq = ild_imm.get_imm_nt_seq(self.ptrn_wrds, imm_nts) self.disp_nt_seq = ild_disp.get_disp_nt_seq(self.ptrn_wrds, disp_nts.union(brdisp_nts)) self.set_constraints(ii, state_space) self.actions = [actions.gen_return_action(ii.inum)] #Not implementing this yet. #Will implement after code review for has_modrm #self.set_hasimm() #self.set_pfx_table() #FIXME: for anaisys only if self.is_3dnow(): if not self.has_modrm: _msg('3DNOW with no MODRM: %s\n' % self)
def _get_united_cdict(ptrn_list, state_space, vexvalid, all_ops_widths): """@param ptrn_list: list of ild.pattern_t @param state_space: all legal values for xed operands: state_space['REXW'][1] = True, state_space['REXW'][0]=True @param vexvalid: VEXVALID value we want to filter by. vevxavlid=='0' will include only patterns with vexvalid=='0' constraint value. @param all_ops_widths: dict of operands to their bit widths. @return ild_cdict.constrant_dict_t which unites patterns constraint dicts This gets called with all the patterns for a specific map & opcode, but for all encoding spaces. So first we filter based on encoding space (vexvalid). """ global mod3_repl, vd7_repl, rm4_repl, masknot0_repl, mask0_repl cnames = [] # FIXME: 2019-10-30: patterns now know their vexvalid value and # encspace, and the maps are split by encspace as well, so we can # avoid the following filtering by vexvalid. #filter by encoding space (vexvalid) ptrns = [] ivv = int(vexvalid) for ptrn in ptrn_list: #FIXME: 2019-10-30: if vexvalid in list(ptrn.special_constraints['VEXVALID'].keys()): if ivv == ptrn.vv: ptrns.append(ptrn) if len(ptrns) == 0: return None for ptrn in ptrns: cnames.extend(list(ptrn.constraints.keys())) cnames = set(cnames) if _is_binary_MOD3(ptrns): mod3_repl += 1 _replace_MOD_with_MOD3(cnames, ptrns) if _has_VEXDEST210_equals_7_restriction(cnames, ptrns): vd7_repl += 1 _replace_VEXDEST210_with_VD2107(cnames, ptrns) if _is_binary_RM_4(cnames, ptrns): rm4_repl += 1 _replace_RM_with_RM4(cnames, ptrns) if _is_binary_MASK_NOT0(cnames, ptrns): masknot0_repl += 1 _replace_MASK_with_MASK_NOT0(cnames, ptrns) if _has_MASK_ZERO_restriction(cnames, ptrns): mask0_repl += 1 _replace_MASK_with_MASK_ZERO(cnames, ptrns) # For each pattern we have a list of constraints. ptrn.constraints # is the legal values for those constraints. In each map opcode # bin, we have several patterns with different constraints. We # want to make one hash table for these different patterns. Thats # why we want to take the union of all the constraints and make # one dictionary (and ultimately a hash table). Need to add all # legal variations of all constraints, cross product. (dangerous) #For example if we have two patterns: #PATTERN1: MOD=1 #PATTERN2: REG=2 #then for PATTERN1 we will create a constraint dictionary with all #combinations (MOD=1 REG=0), (MOD=1, REG=1) ,..., (MOD=1, REG=7) #and for PATTERN2 we will have (MOD=0 REG=2), (MOD=1 REG=2), ... cdicts = [] for ptrn in ptrns: cdict = constraint_dict_t(cnames, ptrn.constraints, state_space, ptrn) cdicts.append(cdict) insn_map = ptrns[0].insn_map opcode = ptrns[0].opcode msg = [] msg.append("cdict conflict in pattern") msg.append('MAP:%s OPCODE:%s\n' % (insn_map, opcode)) msg = "\n".join(msg) # now we unite (cross-product) after exploding/back-filling all the # constraints. All patterns now have same constraints. united_dict = constraint_dict_t.unite_dicts(cdicts, msg, cnames) #generate the int value for each tuple united_dict.create_tuple2int(all_ops_widths) #print "UNITED DICT: VV {} OPCODE {} MAP {}: tuples {}".format( # vexvalid, opcode, insn_map, len(united_dict.tuple2rule) ) #creating the default action that will be taken when we did not hit #a valid hash entry default_action = [actions.gen_return_action('0')] united_dict.action_codegen = actions_codegen.actions_codegen_t( united_dict.tuple2rule, default_action, united_dict.strings_dict) return united_dict
def _get_united_cdict(ptrn_list, state_space, vexvalid, all_ops_widths): """ @param ptrn_list: list of ild.pattern_t @param state_space: all legal values for xed operands: state_space['REXW'][1] = True, state_space['REXW'][0]=True @param vexvalid: VEXVALID value we want to filter by. vevxavlid==0 will include only patterns with vexvalid==0 constraint value. @param all_ops_widths: dict of operands to their bit widths. @return ild_cdict.constrant_dict_t which unites patterns constraint dicts """ cnames = [] #take only requested space patterns ptrns = [] for ptrn in ptrn_list: if vexvalid in ptrn.constraints['VEXVALID'].keys(): ptrns.append(ptrn) if len(ptrns) == 0: return None for ptrn in ptrns: cnames.extend(ptrn.constraints.keys()) cnames = set(cnames) cdicts = [] if _is_binary_MOD3(ptrns): _replace_MOD_with_MOD3(cnames, ptrns) if _is_binary_VEXDEST210_7(cnames, ptrn_list): _replace_VEXDEST210_with_VD2107(cnames, ptrn_list) if _is_binary_RM_4(cnames, ptrn_list): _replace_RM_with_RM4(cnames, ptrn_list) if _is_binary_MASK_NOT0(cnames, ptrn_list): _replace_MASK_with_MASK_NOT0(cnames, ptrn_list) if _is_binary_MASK_ZERO(cnames, ptrn_list): _replace_MASK_with_MASK_ZERO(cnames, ptrn_list) # For each pattern we have a list of constraints. ptrn.constraints # is the legal values for those constraints. In each map opcode # bin, we have several patterns with different constraints. We # want to make one hash table for these different patterns. Thats # why we want to take the union of all the constraints and make # one dictionary (and ultimately a hash table). Need to add all # legal variations of all constraints, cross product. (dangerous) #For example if we have two patterns: #PATTERN1: MOD=1 #PATTERN2: REG=2 #then for PATTERN1 we will create a constraint dictionary with all #combinations (MOD=1 REG=0), (MOD=1, REG=1) ,..., (MOD=1, REG=7) #and for PATTERN2 we will have (MOD=0 REG=2), (MOD=1 REG=2), ... for ptrn in ptrns: cdict = constraint_dict_t(cnames, ptrn.constraints, state_space, ptrn) cdicts.append(cdict) insn_map = ptrns[0].insn_map opcode = ptrns[0].opcode msg = [] msg.append("cdict conflict in pattern") msg.append('MAP:%s OPCODE:%s\n' % (insn_map, opcode)) msg = "\n".join(msg) # now we unite (cross-product) after exploding/back-filling all the # constraints. All patterns now have same constraints. united_dict = constraint_dict_t.unite_dicts(cdicts, msg, cnames) #generate the int value for each tuple united_dict.create_tuple2int(all_ops_widths) united_dict.strings_dict = ild_codegen._dec_strings #creating the default action that will be taken when we did not hit #a valid hash entry default_action = [actions.gen_return_action('0')] united_dict.action_codegen = actions_codegen.actions_codegen_t( united_dict.tuple2rule, default_action, united_dict.strings_dict) return united_dict
def parse_encode_lines(lines, state_bits): """ Returns a tuple of two dictionaries: (1) a dictionary of sequencer_t's and (2) a dictionary of nonterminal_t's """ nts = {} # nonterminals_t's ntlufs = {} # nonterminals_t's seqs = {} # sequencer_t's repeat_nts = { } # some nt/ntluf/seq has multi definition, so we use three dict to record this repeat_ntlufs = {} repeat_seqs = {} i = 0 while len(lines) > 0: line = lines.pop(0) fn = file_pattern.match(line) if fn: filename = fn.group('file') line = comment_pattern.sub("", line) line = leading_whitespace_pattern.sub("", line) if line == '': continue line = slash_expand.expand_all_slashes(line) c = curly_pattern.search(line) if c: line = re.sub("{", " { ", line) line = re.sub("}", " } ", line) sequence = sequence_pattern.match(line) if sequence: seq = sequencer_t(sequence.group('seqname'), filename) if seq.name in seqs: if seqs[seq. name]: # if is not none, shows that this seq only has been defined one time tmp = seqs[seq.name] repeat_seqs[seq.name] = [tmp, seq] seqs[seq.name] = None else: repeat_seqs[seq.name].append(seq) else: seqs[seq.name] = seq #msg("SEQ MATCH %s" % seq.name) nt = None continue p = ntluf_pattern.match(line) if p: nt_name = p.group('ntname') ret_type = p.group('rettype') # create a new nonterminal to use nt = nonterminal_t(nt_name, filename, ret_type) if nt_name in ntlufs: if ntlufs[ nt_name]: # if is not none, shows that this seq only has been defined one time tmp = ntlufs[nt_name] repeat_ntlufs[nt_name] = [tmp, nt] ntlufs[nt_name] = None else: repeat_ntlufs[nt_name].append(nt) else: ntlufs[nt_name] = nt seq = None continue m = nt_pattern.match(line) if m: nt_name = m.group('ntname') nt = nonterminal_t(nt_name, filename) if nt_name in nts: if nts[nt_name]: # if is not none, shows that this seq only has been defined one time tmp = nts[nt_name] repeat_nts[nt_name] = [tmp, nt] nts[nt_name] = None else: repeat_nts[nt_name].append(nt) else: nts[nt_name] = nt seq = None continue a = arrow_pattern.match(line) if a: conds = a.group('cond').split() actns = a.group('action').split() #msg("ARROW" + str(conds) + "=>" + str(actions)) conditions = conditions_t() for c in conds: conditions.and_cond(c) rule = rule_t(conditions, actns, nt_name) if seq: seq.add(rule) else: # we do not need the rules otherwise->error/nothing in the # new encoding structure (hash tables). # instead we are holding this info in a matching attribute if rule.conditions.and_conditions[0].is_otherwise(): if rule.actions[0].is_nothing(): nt.otherwise = [actions.gen_return_action('1')] elif rule.actions[0].is_error(): nt.otherwise = [actions.gen_return_action('0')] else: nt.otherwise = [actions.action_t(x) for x in actns] # in case we have valid action for the otherwise # rule we should finish it with returnning 1 # which is "not an error" nt.otherwise.append(actions.gen_return_action('1')) else: nt.add(rule) else: for nt in line.split(): seq.add(nt) return (seqs, nts, ntlufs, repeat_seqs, repeat_nts, repeat_ntlufs)