366 lines
11 KiB
Python
366 lines
11 KiB
Python
import logging
|
|
import struct
|
|
|
|
from ScEpTIC import tools
|
|
from ScEpTIC.exceptions import MemoryException
|
|
|
|
|
|
class Value:
|
|
"""
|
|
AST node for data representation (immediate, register data, etc)
|
|
"""
|
|
|
|
_vmstate = None
|
|
|
|
def __init__(self, value_class, operand_value, value_type):
|
|
self.value_class = value_class
|
|
self.value = operand_value
|
|
self.type = value_type
|
|
|
|
|
|
def __repr__(self):
|
|
return 'Value({}, {}, {})'.format(self.value_class, self.value, self.type)
|
|
|
|
|
|
def __str__(self):
|
|
if self.type is None:
|
|
return str(self.value)
|
|
|
|
return 'Value: ({}) {}'.format(self.type, self.value)
|
|
|
|
|
|
def __len__(self):
|
|
return len(self.type)
|
|
|
|
def is_llvm_string(self):
|
|
try:
|
|
return self.value_class == 'address' and '@.str' in self.value.element.value
|
|
except:
|
|
return False
|
|
|
|
|
|
def get_val(self):
|
|
"""
|
|
Retrieve and returns the value represented by the object.
|
|
"""
|
|
|
|
if self._vmstate is None:
|
|
raise MemoryException('VM Status not initialized!')
|
|
|
|
if self.value_class == 'immediate':
|
|
value = self._resolve_immediate()
|
|
|
|
elif self.value_class == 'array_val':
|
|
value = self._resolve_array_val()
|
|
|
|
elif self.value_class == 'struct_val':
|
|
value = self._resolve_array_val()
|
|
|
|
elif self.value_class == 'virtual_reg':
|
|
value = self._vmstate.register_file.read(self.value)
|
|
|
|
elif self.value_class == 'global_var':
|
|
# Function pointer -> same initial token of global_vars
|
|
# If function exists -> return value
|
|
if self.value in self._vmstate.functions:
|
|
value = self.value
|
|
else:
|
|
value = self._vmstate.memory.gst.get_symbol_address(self.value)
|
|
|
|
elif self.value_class == 'vector':
|
|
raise NotImplementedError('Vector operations not supported at the moment. Please compile with a different target.')
|
|
|
|
elif self.value_class == 'address':
|
|
# GetElementPointerOperation has method get_val that resolves the correct value
|
|
value = self.value.get_val()
|
|
|
|
elif self.value_class == 'conversion':
|
|
# ConversionOperation has method get_val that resolves the correct value
|
|
value = self.value.get_val()
|
|
|
|
elif self.value_class == 'metadata':
|
|
# appears only in @llvm.dbg functions, so no need to resolve that address
|
|
raise ValueError('Metadata should not be present in running functions.')
|
|
|
|
logging.info('[Value] Resolved value of {} with {}.'.format(self.value, value))
|
|
|
|
return value
|
|
|
|
|
|
def resolve_memory_tag(self, elements):
|
|
"""
|
|
Resolves and returns the memory tag of the targeted element
|
|
"""
|
|
if self.value_class == 'immediate':
|
|
return self._resolve_immediate()
|
|
|
|
elif self.value_class == 'virtual_reg':
|
|
return elements[self.value].resolve_memory_tag(elements)
|
|
|
|
elif self.value_class == 'global_var':
|
|
return self.value
|
|
|
|
elif self.value_class == 'address':
|
|
return self.value.resolve_memory_tag(elements)
|
|
|
|
elif self.value_class == 'conversion':
|
|
return self.value.resolve_memory_tag(elements)
|
|
|
|
raise NotImplementedError(f"resolve_memory_tag() not implemented for value class {self.value_class}")
|
|
|
|
|
|
def resolve_memory_tag_dependency(self, elements):
|
|
if self.value_class == 'immediate':
|
|
return []
|
|
|
|
elif self.value_class == 'virtual_reg':
|
|
return elements[self.value].resolve_memory_tag_dependency(elements)
|
|
|
|
elif self.value_class == 'global_var':
|
|
return [self.value]
|
|
|
|
elif self.value_class == 'address':
|
|
return self.value.resolve_memory_tag_dependency(elements)
|
|
|
|
elif self.value_class == 'conversion':
|
|
return self.value.resolve_memory_tag_dependency(elements)
|
|
|
|
raise NotImplementedError(f"resolve_memory_tag() not implemented for value class {self.value_class}")
|
|
|
|
|
|
def resolve_memory_address_chain(self, elements):
|
|
"""
|
|
Returns a list of all the instructions required to get the address of the targeted element(s)
|
|
"""
|
|
if self.value_class == 'immediate':
|
|
return []
|
|
|
|
elif self.value_class == 'virtual_reg':
|
|
return [elements[self.value].resolve_memory_address_chain(elements)]
|
|
|
|
elif self.value_class == 'global_var':
|
|
return []
|
|
|
|
elif self.value_class == 'address':
|
|
return [self.value.resolve_memory_address_chain(elements)]
|
|
|
|
elif self.value_class == 'conversion':
|
|
return [self.value.resolve_memory_address_chain(elements)]
|
|
|
|
raise NotImplementedError(f"resolve_memory_address_chain() not implemented for value class {self.value_class}")
|
|
|
|
|
|
|
|
def get_input_lookup(self):
|
|
"""
|
|
Returns the input lookup information for the current Value object.
|
|
"""
|
|
|
|
if self._vmstate.input_lookup_enabled:
|
|
if self.value_class == 'virtual_reg':
|
|
return self._vmstate.register_file.get_input_lookup(self.value)
|
|
|
|
elif self.value_class == 'address':
|
|
return self.value.get_input_lookup()
|
|
|
|
elif self.value_class == 'conversion':
|
|
return self.value.get_input_lookup()
|
|
|
|
return tools.build_input_lookup_data(None, None)
|
|
|
|
|
|
def get_uses(self):
|
|
"""
|
|
Returns a list containing the names of the registers used by this value.
|
|
(used by register allocation)
|
|
"""
|
|
|
|
if self.value_class == 'virtual_reg':
|
|
return [self.value]
|
|
|
|
elif self.value_class == 'address':
|
|
return self.value.get_uses()
|
|
|
|
elif self.value_class == 'conversion':
|
|
return self.value.get_uses()
|
|
|
|
return []
|
|
|
|
|
|
def get_memory_uses(self):
|
|
if self.value_class == 'global_var':
|
|
return [self.value]
|
|
|
|
|
|
def get_defs(self):
|
|
"""
|
|
Returns a list of registers defined by this instruction.
|
|
(used by register allocation)
|
|
"""
|
|
|
|
return []
|
|
|
|
|
|
def replace_reg_name(self, old_reg_name, new_reg_name):
|
|
"""
|
|
Replaces the name of a register contained in this Value object.
|
|
(used by register allocation)
|
|
"""
|
|
|
|
if self.value_class == 'virtual_reg':
|
|
if self.value == old_reg_name:
|
|
self.value = new_reg_name
|
|
|
|
elif self.value_class == 'address':
|
|
self.value.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
elif self.value_class == 'conversion':
|
|
self.value.replace_reg_name(old_reg_name, new_reg_name)
|
|
|
|
|
|
def _resolve_immediate(self):
|
|
"""
|
|
Resolves the value of a immediate.
|
|
"""
|
|
|
|
if self.value is None:
|
|
return None
|
|
|
|
if self.value == 'null':
|
|
return 0
|
|
|
|
if self.value == 'true':
|
|
return 1
|
|
|
|
if self.value == 'false':
|
|
return 0
|
|
|
|
try:
|
|
return int(self.value)
|
|
except ValueError:
|
|
pass
|
|
|
|
try:
|
|
return float(self.value)
|
|
except ValueError:
|
|
pass
|
|
|
|
if 'e+' in self.value:
|
|
return float(self.value)
|
|
|
|
if '0x' in self.value:
|
|
# self.value[2:] to remove 0x
|
|
# each value is converted as a double from llvm, even if is a float
|
|
|
|
# NB: target architecture could be in little endian (e in target_datalayout)
|
|
# or big endian (E in target_datalayout). The conversion in the llvm ir stays the same,
|
|
# independently of the type of endianness used by the backend compiler.
|
|
# So the endianness conversion stays ! (network - big-endian)
|
|
return struct.unpack('!d', bytes.fromhex(self.value[2:]))[0]
|
|
|
|
if self.value == 'zeroinitializer':
|
|
composition = self.type.get_memory_composition(True)
|
|
|
|
for i in range(0, len(composition)):
|
|
composition[i] = 0
|
|
|
|
return composition
|
|
|
|
return self.value
|
|
|
|
|
|
def _resolve_array_val(self):
|
|
"""
|
|
Resolves the value of array initialization.
|
|
"""
|
|
|
|
flat_values = []
|
|
tools.inf_depth_lst_flat(self.value, flat_values)
|
|
|
|
for i in range(0, len(flat_values)):
|
|
flat_values[i] = flat_values[i].get_val()
|
|
|
|
return flat_values
|
|
|
|
|
|
@staticmethod
|
|
def convert_sint_to_bin(val, bits):
|
|
"""
|
|
Converts a signed integer to its binary representation, using a certain number of bits
|
|
"""
|
|
|
|
format_str = '{:0'+str(bits)+'b}'
|
|
|
|
if val < 0:
|
|
# all 1s
|
|
mask = int('1' * bits, 2)
|
|
val = format_str.format(val & mask)
|
|
else:
|
|
val = format_str.format(val)
|
|
|
|
# return correct bit dimension
|
|
return val[-bits:]
|
|
|
|
@staticmethod
|
|
def convert_uint_to_bin(val, bits):
|
|
"""
|
|
Converts an unsigned integer to its binary representation.
|
|
For how data is represented, it is the same as converting a signed integer to binary.
|
|
"""
|
|
|
|
return Value.convert_sint_to_bin(val, bits)
|
|
|
|
|
|
@staticmethod
|
|
def convert_bin_to_uint(val):
|
|
"""
|
|
Converts a number in binary format to its equivalent unsigned decimal.
|
|
"""
|
|
|
|
return int(val, 2)
|
|
|
|
|
|
@staticmethod
|
|
def convert_bin_to_sint(val):
|
|
"""
|
|
Converts a number in binary format to its equivalent signed decimal.
|
|
"""
|
|
|
|
if val[0] == '0':
|
|
return int(val, 2)
|
|
|
|
if len(val) == 1:
|
|
return int(val, 2)
|
|
|
|
return int(val[1:], 2) - (2 ** (len(val) - 1))
|
|
|
|
|
|
@staticmethod
|
|
def convert_sint_to_uint(val, bits):
|
|
"""
|
|
Converts a number in signed integer form to its equivalent unsigned integer.
|
|
"""
|
|
|
|
val = Value.convert_sint_to_bin(val, bits)
|
|
return Value.convert_bin_to_uint(val)
|
|
|
|
|
|
@staticmethod
|
|
def convert_uint_to_sint(val, bits):
|
|
"""
|
|
Converts a number in unsigned integer form to its equivalent signed integer.
|
|
"""
|
|
|
|
val = Value.convert_uint_to_bin(val, bits)
|
|
return Value.convert_bin_to_sint(val)
|
|
|
|
|
|
@staticmethod
|
|
def convert_sint_to_sint(val, bits):
|
|
"""
|
|
Convert a signed integer to a signed integer, using a maximum number of bits.
|
|
"""
|
|
|
|
val = Value.convert_sint_to_bin(val, bits)
|
|
return Value.convert_bin_to_sint(val)
|