def lookupAddress(self, fmt, address): """Find data of an appropriate format at a (possibly symbolic) address fmt can influence the results lookupAddress(single, address_of_v) -> v.x lookupAddress(Vector, address_of_v) -> v if fmt is unknown (or no match is found), ????? """ base = None memOffset = 0 others = [] if isinstance(address, algebra.Literal): memOffset = address.value elif isinstance(address, algebra.Expression) and address.op == '+': memOffset = address.constant.value if address.constant else 0 for term in address.args: if isinstance(term.type, basicTypes.Pointer): if base: raise Exception('adding pointers') base = term else: others.append(term) elif isinstance(address.type, basicTypes.Pointer): if basicTypes.isIndexable(address.type.pointedType): base = address memOffset = 0 else: return algebra.Symbol(address.type.target if address.type.target else address.name, address.type.pointedType) if not base: #check for trig lookup if fmt == basicTypes.single and memOffset in self.bindings['trigtables']: try: angle = others[0].args[0].args[0] return algebra.Symbol('{}Table({})'.format(self.bindings['trigtables'][memOffset], angle)) except: pass pair = self.relativeToGlobals(memOffset) if pair: base = algebra.Symbol('raw', basicTypes.Pointer(pair[0].type, pair[0].name)) memOffset = pair[1] else: # no idea what we are looking at, process it anyway return buildExpr('@', fmt, address) if basicTypes.isIndexable(base.type.pointedType): if memOffset >= self.getSize(base.type.pointedType): raise Exception('trying to look up address {:#x} in {} (only {:#x} bytes)'.format( memOffset, base.type.pointedType, self.getSize(base.type.pointedType))) if base.type.target: return self.subLookup(fmt, algebra.Symbol(base.type.target, base.type.pointedType), memOffset, others) else: return self.subLookup(fmt, base, memOffset, others) elif base.type.target and memOffset == 0 and not others: return algebra.Symbol(base.type.target, base.type.pointedType) else: return buildExpr('@', fmt, address)
def subLookup(self, fmt, base, address, others=[]): """Recursively find data at the given address from the start of a type""" if isinstance(base.type, basicTypes.Array): spacing = self.getSize(base.type.pointedType) index = algebra.Literal(address // spacing) canIndex = True for o in others: if (isinstance(o, algebra.Expression) and o.op == '*' and o.constant and o.constant.value == spacing): index = buildExpr( '+', index, algebra.Expression.arithmeticMerge('*', o.args)) else: canIndex = False break if canIndex: element = buildExpr('[', base, index) if basicTypes.isIndexable(base.type.pointedType): return self.subLookup(fmt, element, address % spacing) else: return element else: return buildExpr( '@', fmt, algebra.Expression.arithmeticMerge( '+', [base, algebra.Literal(address)] + others)) parentStruct = None if isinstance(base.type, basicTypes.Pointer): parentStruct = base.type.pointedType elif isinstance(base.type, str): parentStruct = base.type if parentStruct and parentStruct in self.bindings['structs']: members = self.bindings['structs'][parentStruct].members try: bestOffset = max(x for x in members if x <= address) except ValueError: # nothing less pass else: newBase = buildExpr('.', base, algebra.Symbol(*members[bestOffset])) if address < bestOffset + self.getSize(newBase.type): if basicTypes.isIndexable(newBase.type): return self.subLookup(fmt, newBase, address - bestOffset, others) if not others: #TODO account for reading the lower short of a word, etc. return newBase if others: return buildExpr( '@', fmt, algebra.arithmeticMerge( '+', [base, algebra.Literal(address)] + others)) else: return buildExpr( '.', base, algebra.Symbol( '{}_{:#x}'.format(basicTypes.getCode(fmt), address), fmt))
def arithmeticMerge(cls, op, args, flop=False): symbols = [] newConstant = cls.opIdentities[op] for a in args: if isinstance(a, cls) and a.op == op: symbols.extend(a.args) if a.constant: newConstant = cls.opLambdas[op](newConstant, a.constant.value) elif isinstance(a, Literal): newConstant = cls.opLambdas[op](newConstant, a.value) else: symbols.append(a) newConstant = None if newConstant == cls.opIdentities[op] else Literal( newConstant) if symbols: #in case I add symbolic cancellation later if len(symbols) == 1 and not newConstant: return symbols[0] else: #multiple expressions summed if flop: newType = basicTypes.single else: newType = basicTypes.word for s in symbols: if basicTypes.isIndexable(s.type): newType = basicTypes.address break return cls(op, symbols, constant=newConstant, fmt=newType) elif newConstant: return newConstant else: return Literal(cls.opIdentities[op])
def build(cls, op, left, right, flop=False): if op == 'NOR': #why is this a thing return cls('~', [cls.build('|', left, right, flop)]) if op == '[': return cls('[', [left], fmt=left.type.pointedType, constant=right) if isinstance(left, Literal): if isinstance(right, Literal): #two literals, completely reducible return Literal(cls.opLambdas[op](left.value, right.value)) left, right = right, left #left is not a literal, right may be if op == '*' and left == right: return cls('**', [left], constant=Literal(2), fmt=basicTypes.single if flop else basicTypes.word) if op in ['==', '!='] and isinstance(right, Literal): if left.type == basicTypes.boolean and right == 0: return left if op == '!=' else left.negated() if basicTypes.isIndexable(left.type) and right == 0: return left if isinstance(left.type, basicTypes.EnumType): right.type = left.type # simplify multiplications by constants if op == '<<' and isinstance( right, Literal) and right.value < 8: #assume real shifts are by more op, right = '*', Literal(2**right.value) elif op in '+-' and isinstance(left, cls) and left.op == '*': if isinstance(left, cls) and len( left.args) == 1 and left.args[0] == right: return cls('*', [right], left.type, Literal(cls.opLambdas[op](left.constant.value, 1))) if op in '+*|': return cls.arithmeticMerge(op, [left, right], flop) else: new = cls(op, [left, right]) if op in '< > <= >= == !='.split(): new.type = basicTypes.boolean if op == '.': new.type = right.type return new
def read(self, var, fmt=basicTypes.unknown): """Retrive (an appropriate representation of) the value in a register and track its usage var should be a register or Stack() object Depending on the expected format, the stored value may be altered substantially """ if var == Register.R0: #zero is zero, shouldn't remember type info return algebra.Literal(0) if var in self.states: uncertain = False for st in reversed(self.states[var]): if self.now.implies( st.context)[0]: # this state definitely occurred if uncertain: st.explicit = True break else: if isinstance(st.value, algebra.Literal): if isinstance(fmt, basicTypes.EnumType): st.value = self.getEnumValue( fmt, st.value.value) elif basicTypes.isIndexable(fmt): st.value = self.lookupAddress(fmt, st.value) elif st.value.type in [ basicTypes.unknown, basicTypes.bad ]: st.value.type = fmt return st.value elif self.now.isCompatibleWith(st.context): st.explicit = True uncertain = True return algebra.Symbol(VariableHistory.getName(var), fmt) else: symName = VariableHistory.getName(var) if VariableHistory.couldBeArg(var): self.argList.append(var) symName = 'arg_' + symName self.states[var].append( VariableState(self.getName(var), algebra.Symbol(symName, fmt), self.now)) return self.states[var][-1].value