diff options
Diffstat (limited to 'bruiser/wasm/init.py')
-rw-r--r-- | bruiser/wasm/init.py | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/bruiser/wasm/init.py b/bruiser/wasm/init.py new file mode 100644 index 0000000..f3ac986 --- /dev/null +++ b/bruiser/wasm/init.py @@ -0,0 +1,415 @@ +from utils import Colors, init_interpret, ParseFlags +from opcodes import WASM_OP_Code +from section_structs import Code_Section, Func_Body, WASM_Ins, Resizable_Limits, Memory_Section +from execute import * +import datetime as dti +import os +import sys +import signal + + +# McCabe cyclomatic complexity metric +class Metric(): + def __init__(self, code_section): + self.code_section = code_section + self.metric = [] + self.soc = [] + + def mccabe(self): + soc = 0 + Edges = 1 + Nodes = 1 + for funcs in self.code_section.func_bodies: + for ins in funcs.code: + soc += 1 + #print(repr(ins.opcodeint)) + if ins.opcodeint == 4 or ins.opcodeint == 5 or ins.opcodeint == 12 \ + or ins.opcodeint == 13 or ins.opcodeint == 14: + Nodes += 2 + Edges += 4 + elif ins.opcode == 3: + Nodes += 2 + Edges += 3 + else: + pass + + self.metric.append(Edges - Nodes + 1) + self.soc.append(soc) + soc = 0 + Edges = 1 + Nodes = 1 + + def getMcCabe(self): + return self.metric + + def getSOC(self): + return self.soc + + +# handles the debug option --memdump. dumps the contents of linear memories. +def DumpLinearMems(linear_memories, threshold): + count = int() + strrep = [] + linmem_cnt = int() + for lin_mem in linear_memories: + print('-----------------------------------------') + print(Colors.blue + Colors.BOLD + 'Linear Memory '+ repr(linmem_cnt)+ ' :' + Colors.ENDC) + for byte in lin_mem: + if count >= threshold: + break + if count%16 == 0: + for ch in strrep: + # @DEVI-line feed messes the pretty format up + if ord(ch) != 10: + print(Colors.green + ' ' + ch + Colors.ENDC, end = '') + else: + pass + print() + strrep = [] + print(Colors.cyan + hex(count), ':\t' + Colors.ENDC, end='') + strrep.append(str(chr(byte))) + print(Colors.blue + format(byte, '02x') + ' ' + Colors.ENDC, end='') + else: + strrep += str(chr(byte)) + print(Colors.blue + format(byte, '02x') + ' ' + Colors.ENDC, end='') + count += 1 + count = 0 + print() + + +# handles the debug options --idxspc. dumps the index spaces. +def DumpIndexSpaces(machinestate): + print('-----------------------------------------') + print(Colors.green + 'Function Index Space: ' + Colors.ENDC) + for iter in machinestate.Index_Space_Function: + print(Colors.blue + repr(iter) + Colors.ENDC) + + print('-----------------------------------------') + print(Colors.green + 'Globa Index Space: ' + Colors.ENDC) + for iter in machinestate.Index_Space_Global: + print(Colors.blue + repr(iter) + Colors.ENDC) + + print('-----------------------------------------') + print(Colors.green + 'Linear Memory Index Space: ' + Colors.ENDC) + for iter in machinestate.Index_Space_Linear: + print(Colors.blue + repr(iter) + Colors.ENDC) + + print('-----------------------------------------') + print(Colors.green + 'Table Index Space: ' + Colors.ENDC) + for iter in machinestate.Index_Space_Table: + print(Colors.blue + repr(iter) + Colors.ENDC) + print('-----------------------------------------') + + +# WIP-the Truebit Machine class +class TBMachine(): + def __init__(self): + # bytearray of size PAGE_SIZE + self.Linear_Memory = [] + self.Stack_Label = list() + self.Stack_Label_Height = int() + self.Stack_Control_Flow = list() + self.Stack_Call = list() + self.Stack_Value = list() + self.Stack_Omni = list() + self.Vector_Globals = list() + self.Index_Space_Function = list() + self.Index_Space_Global = list() + self.Index_Space_Linear = list() + self.Index_Space_Table = list() + self.Index_Space_Locals = list() + self.Index_Space_Label = list() + + +# handles the initialization of the WASM machine +class TBInit(): + def __init__(self, module, machinestate): + self.module = module + self.machinestate = machinestate + + # a convenience function that runs the methods of the class. all methods + # can be called separately manually as well. + def run(self): + self.InitFuncIndexSpace() + self.InitGlobalIndexSpace() + self.InitLinearMemoryIndexSpace() + self.InitTableIndexSpace() + self.InitializeLinearMemory() + + def InitFuncIndexSpace(self): + if self.module.import_section is not None: + for iter in self.module.import_section.import_entry: + if iter.kind == 0: + name = str() + for i in iter.field_str: + name += str(chr(i)) + self.machinestate.Index_Space_Function.append(name) + + if self.module.function_section is not None: + for iter in self.module.function_section.type_section_index: + self.machinestate.Index_Space_Function.append(iter) + + def InitGlobalIndexSpace(self): + if self.module.import_section is not None: + for iter in self.module.import_section.import_entry: + if iter.kind == 3: + name = str() + for i in iter.field_str: + name += str(chr(i)) + self.machinestate.Index_Space_Global.append(name) + + if self.module.global_section is not None: + for iter in self.module.global_section.global_variables: + self.machinestate.Index_Space_Global.append(iter.init_expr) + + def InitLinearMemoryIndexSpace(self): + if self.module.import_section is not None: + for iter in self.module.import_section.import_entry: + if iter.kind == 2: + name = str() + for i in iter.field_str: + name += str(chr(i)) + self.machinestate.Index_Space_Linear.append(name) + + if self.module.memory_section is not None: + for iter in self.module.memory_section.memory_types: + self.machinestate.Index_Space_Linear.append(iter.initial) + + def InitTableIndexSpace(self): + if self.module.import_section is not None: + for iter in self.module.import_section.import_entry: + if iter.kind == 1: + name = str() + for i in iter.field_str: + name += str(chr(i)) + self.machinestate.Index_Space_Table.append(name) + + if self.module.table_section is not None: + for iter in self.module.table_section.table_types: + self.machinestate.Index_Space_Table.append(iter.element_type) + + def InitializeLinearMemory(self): + # @DEVI-we could try to pack the data in the linear memory ourselve to + # decrease the machinestate size + if self.module.memory_section is None: + rsz_limits = Resizable_Limits() + self.module.memory_section = Memory_Section() + self.module.memory_section.memory_types = [rsz_limits] + self.module.memory_section.count = 1 + for iter in self.module.memory_section.memory_types: + self.machinestate.Linear_Memory.append(bytearray( + WASM_OP_Code.PAGE_SIZE)) + if self.module.data_section is not None: + for iter in self.module.data_section.data_segments: + count = int() + for byte in iter.data: + self.machinestate.Linear_Memory[iter.index][init_interpret(iter.offset) + count] = byte + count += 1 + + # returns the machinestate + def getInits(self): + return(self.machinestate) + + +# WIP-holds the run-rime data structures for a wasm machine +class RTE(): + def __init__(self): + Stack_Control_Flow = list() + Stack_Value = list() + Vector_Locals = list() + Current_Position = int() + Local_Stacks = list() + + def genFuncLocalStack(func_body): + pass + + +# palceholder for the class that holds the validation functions +class ModuleValidation(): + def __init__(self, module): + self.module = module + + def TypeSection(self): + pass + + def ImportSection(self): + pass + + def FunctionSection(self): + pass + + def TableSection(self): + pass + + def MemorySection(self): + pass + + def GlobalSection(self): + pass + + def ExportSection(self): + pass + + def StartSection(self): + pass + + def ElementSection(self): + pass + + def CodeSection(self): + pass + + def DataSection(self): + pass + + def TBCustom(self): + pass + + def ValidateAll(self): + self.TypeSection() + self.ImportSection() + self.FunctionSection() + self.TableSection() + self.MemorySection() + self.GlobalSection() + self.ExportSection() + self.StartSection() + self.ElementSection() + self.CodeSection() + self.DataSection() + self.TBCustom() + + return(True) + + +# a convinience class that handles the initialization of the wasm machine and +# interpretation of the code. +class VM(): + def __init__(self, modules): + self.modules = modules + self.machinestate = TBMachine() + # @DEVI-FIXME- the first implementation is single-module only + self.init = TBInit(self.modules[0], self.machinestate) + self.init.run() + self.machinestate = self.init.getInits() + self.start_function = Func_Body() + self.ins_cache = WASM_Ins() + self.executewasm = Execute(self.machinestate) + self.totGas = int() + self.metric = Metric(modules[0].code_section) + self.parseflags = None + + def setFlags(self, parseflags): + self.parseflags = parseflags + + def getState(self): + return(self.machinestate) + + def initLocalIndexSpace(self, local_count): + for i in range(0, local_count): + self.machinestate.Index_Space_Locals.append(0) + + def getStartFunctionIndex(self): + if self.modules[0].start_section is None: + if self.parseflags.entry is None: + raise Exception(Colors.red + "module does not have a start section. no function index was provided with the --entry option.quitting..." + Colors.ENDC) + else: + start_index = int(self.parseflags.entry) + else: + print(Colors.green + "found start section: " + Colors.ENDC, end = '') + start_index = self.modules[0].start_section.function_section_index + + print(Colors.blue + Colors.BOLD + "running function at index " + repr(start_index) + Colors.ENDC) + if (start_index > len(self.modules[0].code_section.func_bodies) - 1): + raise Exception(Colors.red + "invalid function index: the function index does not exist." + Colors.ENDC) + return(start_index) + + def getStartFunctionBody(self): + start_index = self.getStartFunctionIndex() + if isinstance(start_index, int): + self.start_function = self.modules[0].code_section.func_bodies[start_index] + elif isinstance(start_index, str): + # we have to import the function from another module/library. we + # assume sys calls are not present.:w + pass + else: + raise Exception(Colors.red + "invalid entry for start function index" + Colors.ENDC) + + def execute(self): + print(Colors.blue + Colors.BOLD + 'running module with code: ' + Colors.ENDC) + for ins in self.start_function.code: + print(Colors.purple + repr(ins.opcode) + ' ' + repr(ins.operands) + Colors.ENDC) + for ins in self.start_function.code: + self.executewasm.getInstruction(ins.opcodeint, ins.operands) + self.executewasm.callExecuteMethod() + self.getState() + + # pre-execution hook + def startHook(self): + if self.parseflags.metric: + for mem in self.modules[0].memory_section.memory_types: + self.executewasm.chargeGasMem(mem.initial) + + self.metric.mccabe() + print(Colors.red + "mccabe: " + repr(self.metric.getMcCabe()) + Colors.ENDC) + print(Colors.red + "soc: " + repr(self.metric.getSOC()) + Colors.ENDC) + + # post-execution hook + def endHook(self): + if self.parseflags.gas: + self.totGas = self.executewasm.getOPGas() + print(Colors.red + "total gas cost: " + repr(self.totGas) + Colors.ENDC) + if self.machinestate.Stack_Omni: + print(Colors.green + "stack top: " + repr(self.machinestate.Stack_Omni.pop()) + Colors.ENDC) + + # a convinience method + def run(self): + self.startHook() + self.getStartFunctionBody() + self.initLocalIndexSpace(self.start_function.local_count) + self.execute() + self.endHook() + + +# a wrapper class for VM. it timeouts instructions that take too long to +# execute. +class Judicator(): + def __int__(self, op_time_table, module): + self.op_time_table = op_time_table + self.vm = VM(modules) + self.vm.getStartFunctionBody() + + def overseer(): + # @DEVI- forking introduces a new source of non-determinism + pid = os.fork() + # child process + if pid == 0: + sys.stdout = open('./jstdout', 'w') + sys.stderr = open('./jstderr', 'w') + self.vm.execute() + sys.exit() + # parent process + if pid > 0: + cpid, status = os.waitpid(pid, 0) + if status == 0: + print('overseer child exited successfully.') + else: + print('overseer child exited with non-zero.') + # pid < 0 + else: + raise Exception(Colors.red + 'could not fork judicator overseer.' + Colors.ENDC) + + def setup(self): + signal.signal(signal.SIGALRM, self.to_sighandler) + + def set_alarm(t): + signal.alaram(t) + + def to_sighandler(signum, frame): + print(Colors.red + "execution time out..." + Colors.ENDC) + raise Exception(Colors.red + "execution time out" + Colors.ENDC) + + def run(self): + self.setup() + self.set_alaram(10) + self.overseer() |