#!/bin/python3
from __future__ import print_function
import argparse
import sys
from section_structs import *
from utils import *
from opcodes import *
from copy import deepcopy
from init import *
import code
_DBG_ = True
def SigHandler_SIGINT(signum, frame):
print()
sys.exit(0)
# we first read the object file and put all the sections in this class
class ParsedStruct:
def __init__(self):
self.version_number = int()
self.section_list = []
# like the above. currently unused
class ParsedStructV2:
def __init__(self, version_number, section_list):
self.version_number = version_number
self.section_list = section_list
# @DEVI-Deprecated-convert a bytearray to int
def Conver2Int(little_endian, size, bytelist):
modifier = size - 1
sum = 0
if little_endian:
for bit in reversed(bytelist):
if bit != 0x80:
sum += bit * (pow(16, modifier))
modifier -= 1
return sum
else:
for bit in reversed(bytelist):
if bit != 0x80:
sum += bit * (pow(16, modifier))
modifier -= 1
return sum
# the argparser
class Argparser(object):
def __init__(self):
parser = argparse.ArgumentParser()
parser.add_argument(
"--wast", type=str, help="path to the wasm text file"
)
parser.add_argument(
"--wasm", type=str, nargs="+", help="path to the wasm object file"
)
parser.add_argument(
"--asb", type=str, help="path to the wast file to assemble"
)
parser.add_argument(
"--dis", type=str, help="path to the wasm file to disassemble"
)
parser.add_argument("-o", type=str, help="the path to the output file")
parser.add_argument(
"--dbg",
action="store_true",
help="print debug info",
default=False,
)
parser.add_argument(
"--unval",
action="store_true",
help="skips validation tests",
default=False,
)
parser.add_argument(
"--memdump", type=int, help="dumps the linear memory"
)
parser.add_argument(
"--idxspc",
action="store_true",
help="print index space data",
default=False,
)
parser.add_argument(
"--run",
action="store_true",
help="runs the start function",
default=False,
)
parser.add_argument(
"--metric",
action="store_true",
help="print metrics",
default=False,
)
parser.add_argument(
"--gas", action="store_true", help="print gas usage", default=False
)
parser.add_argument(
"--entry",
type=str,
help="name of the function that will act as the entry point into execution",
)
parser.add_argument(
"--link",
type=str,
nargs="+",
help="link the following wasm modules",
)
parser.add_argument(
"--sectiondump", type=str, help="dumps the section provided"
)
parser.add_argument("--hexdump", type=int, help="dumps all sections")
parser.add_argument(
"--dbgsection",
type=str,
help="dumps the parsed section provided",
default="",
)
parser.add_argument(
"--interactive",
action="store_true",
help="open in cli mode",
default=False,
)
parser.add_argument(
"--rawdump", type=int, nargs=2, help="dumps all sections"
)
self.args = parser.parse_args()
if self.args.wasm is not None and self.args.wast is not None:
raise Exception(
"the --wast option and the --wasm option cannot\
be set at the same time. you need to choose one."
)
def getParseFlags(self):
return ParseFlags(
self.args.wast,
self.args.wasm,
self.args.asb,
self.args.dis,
self.args.o,
self.args.dbg,
self.args.unval,
self.args.memdump,
self.args.idxspc,
self.args.run,
self.args.metric,
self.args.gas,
self.args.entry,
)
# reads a wasm-obj file, returns a parsedstruct that holds all the sections'
# bytecode, their section type and their length
def ReadWASM(file_path, endianness, is_extended_isa, dbg):
temp_obj_file = []
wasm_file = open(file_path, "rb")
parsedstruct = ParsedStruct()
# read the magic cookie
byte = wasm_file.read(WASM_OP_Code.uint32)
if byte != WASM_OP_Code.magic_number.to_bytes(
WASM_OP_Code.uint32, byteorder=endianness, signed=False
):
raise Exception("bad magic cookie")
# read the version number
byte = wasm_file.read(WASM_OP_Code.uint32)
if byte != WASM_OP_Code.version_number.to_bytes(
WASM_OP_Code.uint32, byteorder=endianness, signed=False
):
raise Exception("bad version number")
else:
parsedstruct.version_number = byte
while True:
byte = wasm_file.read(1)
if byte != b"":
temp_obj_file.append(
int.from_bytes(byte, byteorder="big", signed=False)
)
else:
break
offset = 0
loop = True
while loop:
try:
# section_id, offset, dummy = Read(temp_obj_file, offset, 'varuint7')
section_id, offset, dummy = Read(
temp_obj_file, offset, "varuint32"
)
except IndexError:
break
payload_length, offset, dummy = Read(
temp_obj_file, offset, "varuint32"
)
if section_id == 0:
is_custom_section = True
name_len, offset, dummy = Read(temp_obj_file, offset, "varuint32")
name = temp_obj_file[offset : offset + name_len]
offset += name_len
"""
if type(name) == list:
print(''.join([chr(elem) for elem in name]))
name = ''.join([chr(elem) for elem in name])
else:
print(name)
if name.find("reloc", 0, 5) == 0:
is_reloc_section = True
reloc_entry_count = Read(temp_obj_file, offset, 'varuint32')
for i in range(0, reloc_entry_count):
reloc_entry, offset, dummy = Read(tmp_obj, offset, 'varuint32')
reloc_entries.append(reloc_entry)
"""
else:
is_custom_section = False
name_len = 0
name = ""
dummy = 0
payload_data = temp_obj_file[
offset : offset + payload_length - name_len - dummy
]
offset += payload_length - name_len - dummy
# @DEVI-the second field is for general use. it is unused right
# now so we are filling it with jojo.
parsedstruct.section_list.append(
[
section_id,
"jojo",
payload_length,
is_custom_section,
name_len,
name,
payload_data,
]
)
# prints out the sections in the wasm object
for section in parsedstruct.section_list:
pass
# print(section)
wasm_file.close()
return parsedstruct
# Receives a parsedstruct returned from ReadWASM, parses all the sections and
# fills up a module class. the parse method, then can return the module.
# the returned class objects are all defined in section_structs.py.
class ObjReader(object):
def __init__(self, parsedstruct):
self.parsedstruct = parsedstruct
# we use this method to read the operands of instructions. it's only
# called by ReadCodeSection
def Disassemble(self, section_byte, offset):
# @DEVI-FIXME- not sure why i was using instruction. its a string...
matched = False
read_bytes = 0
read_bytes_temp = 0
read_bytes_temp_iter = 0
instruction = str()
operands = []
temp_wasm_ins = WASM_Ins()
# @DEVI-FIXME-for v1.0 opcodes. needs to get fixed for extended
# op-codes. ideally the mosule should hold its version number so we can
# check it here.
byte = format(section_byte[6][offset], "02x")
offset += 1
read_bytes += 1
for op_code in WASM_OP_Code.all_ops:
if op_code[1] == byte:
matched = True
# br_table has special immediates
# @DEVI-FIXME-this is costing us quite dearly for every opcode
# we read(at least two ticks per opcode). I could have the
# br_table opcode done separately but kinda hurts the codes
# uniformity. anyways.
if op_code[1] == "0e":
matched = True
temp, offset, read_bytes_temp_iter = Read(
section_byte[6], offset, op_code[3][0]
)
instruction += repr(temp) + " "
operands.append(repr(temp))
read_bytes_temp += read_bytes_temp_iter
for target_table in range(0, temp):
temp, offset, read_bytes_temp_iter = Read(
section_byte[6], offset, op_code[3][1]
)
read_bytes_temp += read_bytes_temp_iter
instruction += repr(temp) + " "
operands.append(repr(temp))
temp, offset, read_bytes_temp_iter = Read(
section_byte[6], offset, op_code[3][2]
)
instruction += repr(temp) + " "
operands.append(repr(temp))
read_bytes_temp += read_bytes_temp_iter
elif op_code[2]:
if isinstance(op_code[3], tuple):
for i in range(0, len(op_code[3])):
temp, offset, read_bytes_temp_iter = Read(
section_byte[6], offset, op_code[3][i]
)
read_bytes_temp += read_bytes_temp_iter
instruction += repr(temp) + " "
operands.append(repr(temp))
else:
temp, offset, read_bytes_temp = Read(
section_byte[6], offset, op_code[3]
)
instruction += repr(temp)
operands.append(repr(temp))
temp_wasm_ins.opcode = op_code[0]
temp_wasm_ins.opcodeint = int(byte, 16)
# temp_wasm_ins.operands = instruction
temp_wasm_ins.operands = operands
break
read_bytes += read_bytes_temp
return offset, matched, read_bytes, temp_wasm_ins
# parses the code section. returns a Code_Section class
def ReadCodeSection(self):
offset = 0
CS = Code_Section()
temp_func_bodies = Func_Body()
temp_local_entry = Local_Entry()
section_exists = False
for whatever in self.parsedstruct.section_list:
# 10 is the code section
if whatever[0] == 10:
code_section = whatever.copy()
section_exists = True
if not section_exists:
return None
fn_cn, offset, dummy = Read(code_section[6], offset, "varuint32")
function_cnt = fn_cn
CS.count = function_cnt
while function_cnt > 0:
function_body_length, offset, dummy = Read(
code_section[6], offset, "varuint32"
)
temp_func_bodies.body_size = function_body_length
local_count, offset, dummy = Read(
code_section[6], offset, "varuint32"
)
temp_func_bodies.local_count = local_count
# local_count_size will eventually hold how many bytes we will read
# in total because of the local section
local_count_size = dummy
if local_count != 0:
for i in range(0, local_count):
partial_local_count, offset, dummy = Read(
code_section[6], offset, "varuint32"
)
local_count_size += dummy
partial_local_type, offset, dummy = Read(
code_section[6], offset, "uint8"
)
local_count_size += dummy
temp_local_entry.count = partial_local_count
temp_local_entry.type = partial_local_type
temp_func_bodies.locals.append(deepcopy(temp_local_entry))
local_count -= partial_local_count
else:
pass
read_bytes_so_far = local_count_size
for i in range(0, function_body_length - local_count_size):
offset, matched, read_bytes, temp_wasm_ins = self.Disassemble(
code_section, offset
)
temp_func_bodies.code.append(deepcopy(temp_wasm_ins))
if not matched:
print(Colors.red + "did not match anything" + Colors.ENDC)
print(
Colors.red
+ "code section offset: "
+ repr(offset)
+ Colors.ENDC
)
print(
Colors.red
+ "read bytes: "
+ repr(read_bytes)
+ Colors.ENDC
)
print(
Colors.red
+ "wasm ins: "
+ repr(temp_wasm_ins.opcode)
+ Colors.ENDC
)
for iter in temp_func_bodies.code:
print(iter.opcode)
print(iter.operands)
sys.exit(1)
else:
pass
matched = False
read_bytes_so_far += read_bytes
if read_bytes_so_far == function_body_length:
break
CS.func_bodies.append(deepcopy(temp_func_bodies))
temp_func_bodies.locals = []
temp_func_bodies.code = []
function_cnt -= 1
return CS
# parsed the data section. returns a Data_Section class
def ReadDataSection(self):
loop = True
section_exists = False
offset = 0
DS = Data_Section()
temp_data_segment = Data_Segment()
init_expr = []
for whatever in self.parsedstruct.section_list:
if whatever[0] == 11:
data_section = whatever.copy()
section_exists = True
if not section_exists:
return None
data_entry_count, offset, dummy = Read(
data_section[6], offset, "varuint32"
)
DS.count = data_entry_count
while data_entry_count != 0:
linear_memory_index, offset, dummy = Read(
data_section[6], offset, "varuint32"
)
temp_data_segment.index = linear_memory_index
# reading in the init-expr
while loop:
# @DEVI-FIXME-this only works for none extended opcodes
if data_section[6][offset] == 0x0B:
loop = False
data_char, offset, dummy = Read(
data_section[6], offset, "uint8"
)
init_expr.append(data_char)
temp_data_segment.offset = init_expr
data_entry_length, offset, dummy = Read(
data_section[6], offset, "varuint32"
)
temp_data_segment.size = data_entry_length
data_itself = data_section[6][offset : offset + data_entry_length]
temp_data_segment.data = data_itself
offset += data_entry_length
DS.data_segments.append(deepcopy(temp_data_segment))
data_entry_count -= 1
init_expr = []
loop = True
return DS
# parses the import section. returns an Import_Section class
def ReadImportSection(self):
offset = 0
section_exists = False
module_name = []
field_name = []
IS = Import_Section()
temp_import_entry = Import_Entry()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 2:
import_section = whatever.copy()
section_exists = True
if not section_exists:
return None
import_cnt, offset, dummy = Read(
import_section[6], offset, "varuint32"
)
IS.count = import_cnt
while import_cnt != 0:
module_length, offset, dummy = Read(
import_section[6], offset, "varuint32"
)
temp_import_entry.module_len = module_length
for i in range(0, module_length):
module_name.append(import_section[6][offset + i])
temp_import_entry.module_str = module_name
offset += module_length
field_length, offset, dummy = Read(
import_section[6], offset, "varuint32"
)
temp_import_entry.field_len = field_length
for i in range(0, field_length):
field_name.append(import_section[6][offset + i])
temp_import_entry.field_str = field_name
offset += field_length
kind, offset, dummy = Read(import_section[6], offset, "uint8")
temp_import_entry.kind = kind
# function type
if kind == 0:
import_type, offset, dummy = Read(
import_section[6], offset, "varuint32"
)
temp_import_entry.type = import_type
# table type
elif kind == 1:
table_type = Table_Type()
table_type.elemet_type, offset, dummy = Read(
import_section[6], offset, "varint7"
)
rsz_limits = Resizable_Limits()
rsz_limits.flags, offset, dummy = Read(
import_section[6], offset, "varuint1"
)
rsz_limits.initial, offset, dummy = Read(
import_section[6], offset, "varuint32"
)
if rsz_limits.flags:
rsz_limits.maximum, offset, dummy = Read(
import_section[6], offset, "varuint32"
)
table_type.limit = rsz_limits
temp_import_entry.type = table_type
elif kind == 2:
memory_type = Memory_Type()
rsz_limits = Resizable_Limits()
rsz_limits.flags, offset, dummy = Read(
import_section[6], offset, "varuint1"
)
rsz_limits.initial, offset, dummy = Read(
import_section[6], offset, "varuint32"
)
if rsz_limits.flags:
rsz_limits.maximum, offset, dummy = Read(
import_section[6], offset, "varuint32"
)
memory_type.limits = rsz_limits
temp_import_entry.type = memory_type
elif kind == 3:
global_type = Global_Type()
global_type.content_type, offset, dummy = Read(
import_section[6], offset, "uint8"
)
global_type.mutability, offset, dummy = Read(
import_section[6], offset, "varuint1"
)
temp_import_entry.type = global_type
IS.import_entry.append(deepcopy(temp_import_entry))
import_cnt -= 1
module_name = []
field_name = []
return IS
# parses the export section, returns an Export_Section class
def ReadExportSection(self):
offset = 0
section_exists = False
field_name = []
ES = Export_Section()
temp_export_entry = Export_Entry()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 7:
export_section = whatever.copy()
section_exists = True
if not section_exists:
return None
export_entry_cnt, offset, dummy = Read(
export_section[6], offset, "varuint32"
)
ES.count = export_entry_cnt
while export_entry_cnt != 0:
field_length, offset, dummy = Read(
export_section[6], offset, "varuint32"
)
temp_export_entry.field_len = field_length
for i in range(0, field_length):
field_name.append(export_section[6][offset + i])
temp_export_entry.fiels_str = field_name
offset += field_length
kind, offset, dummy = Read(export_section[6], offset, "uint8")
temp_export_entry.kind = kind
index, offset, dummy = Read(export_section[6], offset, "varuint32")
temp_export_entry.index = index
ES.export_entries.append(deepcopy(temp_export_entry))
export_entry_cnt -= 1
field_name = []
return ES
# parses the type section, returns a Type_Section class
def ReadTypeSection(self):
offset = 0
section_exists = False
param_list = []
return_list = []
TS = Type_Section()
temp_func_type = Func_Type()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 1:
type_section = whatever.copy()
section_exists = True
if not section_exists:
return None
type_entry_count, offset, dummy = Read(
type_section[6], offset, "varuint32"
)
TS.count = type_entry_count
while type_entry_count != 0:
form, offset, dummy = Read(type_section[6], offset, "varint7")
temp_func_type.form = form
param_count, offset, dummy = Read(
type_section[6], offset, "varuint32"
)
temp_func_type.param_cnt = param_count
for i in range(0, param_count):
param_list.append(type_section[6][offset + i])
temp_func_type.param_types = param_list
offset += param_count
# @DEVI-FIXME- only works for MVP || single return value
return_count, offset, dummy = Read(
type_section[6], offset, "varuint1"
)
temp_func_type.return_cnt = return_count
for i in range(0, return_count):
return_list.append(type_section[6][offset + i])
temp_func_type.return_type = return_list
offset += return_count
TS.func_types.append(deepcopy(temp_func_type))
type_entry_count -= 1
param_list = []
return_list = []
return TS
# parses the function section, returns a Function_section class
def ReadFunctionSection(self):
offset = 0
section_exists = False
index_to_type = []
FS = Function_Section()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 3:
function_section = whatever.copy()
section_exists = True
if not section_exists:
return None
function_entry_count, offset, dummy = Read(
function_section[6], offset, "varuint32"
)
FS.count = function_entry_count
for i in range(0, function_entry_count):
index, offset, dummy = Read(
function_section[6], offset, "varuint32"
)
index_to_type.append(index)
FS.type_section_index = index_to_type
offset += function_entry_count
return FS
# parses the element secction, returns an Element_Section class
def ReadElementSection(self):
offset = 0
section_exists = False
init_expr = []
loop = True
function_indices = []
ES = Element_Section()
temp_elem_segment = Elem_Segment()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 9:
element_section = whatever.copy()
section_exists = True
if not section_exists:
return None
element_entry_count, offset, dummy = Read(
element_section[6], offset, "varuint32"
)
ES.count = element_entry_count
while element_entry_count != 0:
table_index, offset, dummy = Read(
element_section[6], offset, "varuint32"
)
temp_elem_segment.index = table_index
# @DEVI-FIXME-only works for non-extneded opcodes
while loop:
if element_section[6][offset] == 0x0B:
loop = False
init_expr.append(element_section[6][offset])
offset += 1
temp_elem_segment.offset = init_expr
num_elements, offset, dummy = Read(
element_section[6], offset, "varuint32"
)
temp_elem_segment.num_elem = num_elements
for i in range(0, num_elements):
index, offset, dummy = Read(
element_section[6], offset, "varuint32"
)
function_indices.append(index)
temp_elem_segment.elems = function_indices
offset += num_elements
ES.elem_segments.append(deepcopy(temp_elem_segment))
loop = True
init_expr = []
function_indices = []
element_entry_count -= 1
return ES
# parses the memory section, returns a Memory_Section class
def ReadMemorySection(self):
offset = 0
section_exists = False
MS = Memory_Section()
temp_rsz_limits = Resizable_Limits()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 5:
memory_section = whatever.copy()
section_exists = True
if not section_exists:
return None
num_linear_mems, offset, dummy = Read(
memory_section[6], offset, "varuint32"
)
MS.count = num_linear_mems
while num_linear_mems != 0:
flag, offset, dummy = Read(memory_section[6], offset, "varuint1")
temp_rsz_limits.flags = flag
initial, offset, dummy = Read(
memory_section[6], offset, "varuint32"
)
temp_rsz_limits.initial = initial
if flag:
maximum, offset, dummy = Read(
memory_section[6], offset, "varuint32"
)
temp_rsz_limits.maximum = maximum
MS.memory_types.append(deepcopy(temp_rsz_limits))
num_linear_mems -= 1
return MS
# parses the table section, returns a Table_Section class
def ReadTableSection(self):
offset = 0
section_exists = False
TS = Table_Section()
temp_table_type = Table_Type()
temp_rsz_limits = Resizable_Limits()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 4:
table_section = whatever.copy()
section_exists = True
if not section_exists:
return None
table_count, offset, dummy = Read(
table_section[6], offset, "varuint32"
)
TS.count = table_count
while table_count != 0:
element_type, offset, dummy = Read(
table_section[6], offset, "varint7"
)
temp_table_type.element_type = element_type
flag, offset, dummy = Read(table_section[6], offset, "varuint1")
temp_rsz_limits.flags = flag
initial, offset, dummy = Read(
table_section[6], offset, "varuint32"
)
temp_rsz_limits.initial = initial
if flag:
maximum, offset, dummy = Read(
table_section[6], offset, "varuint32"
)
temp_rsz_limits.maximum = maximum
temp_table_type.limit = temp_rsz_limits
TS.table_types.append(deepcopy(temp_table_type))
table_count -= 1
return TS
# parses the global section, returns a Global_Section class
def ReadGlobalSection(self):
offset = 0
loop = True
section_exists = False
init_expr = []
GS = Global_Section()
temp_gl_var = Global_Variable()
temp_gl_type = Global_Type()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 6:
global_section = whatever.copy()
section_exists = True
if not section_exists:
return None
count, offset, dummy = Read(global_section[6], offset, "varuint32")
GS.count = count
while count != 0:
content_type, offset, dummy = Read(
global_section[6], offset, "uint8"
)
temp_gl_type.content_type = content_type
mutability, offset, dummy = Read(
global_section[6], offset, "varuint1"
)
temp_gl_type.mutability = mutability
# @DEVI-FIXME-only works for non-extended opcodes
while loop:
if global_section[6][offset] == 0x0B:
loop = False
init_expr.append(global_section[6][offset])
offset += 1
temp_gl_var.init_expr = init_expr
temp_gl_var.global_type = temp_gl_type
GS.global_variables.append(deepcopy(temp_gl_var))
count -= 1
loop = True
init_expr = []
return GS
# parses the start section, returns a Start_Section
def ReadStartSection(self):
offset = 0
section_exists = False
SS = Start_Section()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 8:
start_section = whatever.copy()
section_exists = True
if not section_exists:
return None
function_index, offset, dummy = Read(
start_section[6], offset, "varuint32"
)
SS.function_section_index = function_index
return SS
def ReadRelocationSection(self):
section_exists = False
RS = Relocation_Section()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 0 and whatever[1] == "reloc":
# reloc_section = whatever.copy()
section_exists = True
if not section_exists:
return None
return RS
def ReadNameSection(self):
section_exists = False
NS = Name_Section()
for whatever in self.parsedstruct.section_list:
if whatever[0] == 0 and whatever[1] == "name":
# name_section = whatever.copy()
section_exists = True
if not section_exists:
return None
return NS
def ReadLinkingSection(self):
pass
def ReadLinkingDotSections(self):
pass
# unused-returns the cursor location in the object file
def getCursorLocation(self):
return self.wasm_file.tell()
# a convinience method-builds a module class and returns it
def parse(self):
return Module(
self.ReadTypeSection(),
self.ReadImportSection(),
self.ReadFunctionSection(),
self.ReadTableSection(),
self.ReadMemorySection(),
self.ReadGlobalSection(),
self.ReadExportSection(),
self.ReadStartSection(),
self.ReadElementSection(),
self.ReadCodeSection(),
self.ReadDataSection(),
)
# WIP-basically how the assembler is constructed
class ParserV1(object):
def __init__(self, path):
self.path = path
def run(self):
wasmtobj = WASMText(self.path)
# wasmtobj.test_print()
wasmtobj.RegExSearch()
wasmtobj.PrintTypeDict()
wasmtobj.PrintImportDict()
wasmtobj.PrintTableDict()
wasmtobj.PrintElemDict()
wasmtobj.PrintMemoryDict()
wasmtobj.PrintDataDict()
wasmtobj.PrintExportDict()
wasmtobj.PrintFuncDict()
wasmtobj.PrintElemDict()
wasmtobj.FuncParser()
wasmtobj.FuncParserTest()
funcbodyparser = FuncBodyParser(wasmtobj.getFuncBodies())
headerparser = FuncBodyParser(wasmtobj.getTypeHeader())
expr_stack = funcbodyparser.ParseBodyV3(False)
header_stack = headerparser.ParseBodyV3(True)
wasm_codeemitter = WASM_CodeEmitter(expr_stack)
# wasm_codeemitter.Obj_Header_32()
# wasm_codeemitter.Dump_Obj_STDOUT()
# wasm_codeemitter.SetNewStack(header_stack)
# wasm_codeemitter.EmitTypeHeader()
# wasm_codeemitter.PrintTypeHeaderObj()
# our interpreter class
class PythonInterpreter(object):
def __init__(self):
self.modules = []
self.sections = []
# appends a module to the module list that PythonInterpreter has
def appendmodule(self, module):
self.modules.append(module)
# returns the list of modules that we have parsed so far
def getmodules(self):
return self.modules
def appendsection(self, section):
self.sections.append(section)
def getsections(self):
return self.sections
# convinience method.calls the ObjReader to parse a wasm obj file.
# returns a module class.
def parse(self, file_path):
section = ReadWASM(file_path, "little", False, True)
self.appendsection(section)
parser = ObjReader(section)
return parser.parse()
# dumps the object sections' info to stdout. pretty print.
def dump_sections(self, module, dbgsection):
all = False
if dbgsection == "":
all = True
print(Colors.blue + Colors.BOLD + "BEGENNING OF MODULE" + Colors.ENDC)
# type_section
if module.type_section is not None and (dbgsection == "type" or all):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Type Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.type_section.count))
for iter in module.type_section.func_types:
print(Colors.cyan + "form: " + repr(iter.form) + Colors.ENDC)
print(
Colors.green
+ "param count: "
+ repr(iter.param_cnt)
+ Colors.ENDC
)
print(
Colors.red
+ "param types: "
+ repr(iter.param_types)
+ Colors.ENDC
)
print(
Colors.purple
+ "return count: "
+ repr(iter.return_cnt)
+ Colors.ENDC
)
print(
Colors.yellow
+ "return type: "
+ repr(iter.return_type)
+ Colors.ENDC
)
# import_section
if module.import_section is not None and (
dbgsection == "import" or all
):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Import Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.import_section.count))
for iter in module.import_section.import_entry:
print(
Colors.cyan
+ "module length: "
+ repr(iter.module_len)
+ Colors.ENDC
)
print(
Colors.green
+ "module str: "
+ repr(iter.module_str)
+ Colors.ENDC
)
print(
Colors.red
+ "field length: "
+ repr(iter.field_len)
+ Colors.ENDC
)
print(
Colors.purple
+ "field str: "
+ repr(iter.field_str)
+ Colors.ENDC
)
print(
Colors.purple
+ "field str: "
+ "".join([chr(elem) for elem in iter.field_str])
+ Colors.ENDC
)
print(Colors.yellow + "kind: " + repr(iter.kind) + Colors.ENDC)
print(Colors.grey + "type: " + repr(iter.type) + Colors.ENDC)
if type(iter.type) == Table_Type:
print("element_type: " + repr(iter.type.element_type))
print("limit: " + repr(iter.type.limit))
print("flags: " + repr(iter.type.limit.flags))
print("initial: " + repr(iter.type.limit.initial))
print("maximum: " + repr(iter.type.limit.maximum))
if type(iter.type) == Global_Type:
print("value_type: " + repr(iter.type.content_type))
print("mutability: " + repr(iter.type.mutability))
if type(iter.type) == Memory_Type:
print("flags: " + repr(iter.type.limits.flags))
print("flags: " + repr(iter.type.limits.initial))
print("flags: " + repr(iter.type.limits.maximum))
# function_section
if module.function_section is not None and (
dbgsection == "function" or all
):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(
Colors.blue + Colors.BOLD + "Function Section:" + Colors.ENDC
)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.function_section.count))
for iter in module.function_section.type_section_index:
print(
Colors.cyan
+ "type section index: "
+ repr(iter)
+ Colors.ENDC
)
# table_section
if module.table_section is not None and (dbgsection == "table" or all):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Table Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.table_section.count))
for iter in module.table_section.table_types:
print(
Colors.cyan
+ "element type: "
+ repr(iter.element_type)
+ Colors.ENDC
)
print(
Colors.green
+ "Resizable_Limits:flags: "
+ repr(iter.limit.flags)
+ Colors.ENDC
)
print(
Colors.red
+ "Resizable_Limits:initial: "
+ repr(iter.limit.initial)
+ Colors.ENDC
)
print(
Colors.purple
+ "Resizable_Limits:maximum: "
+ repr(iter.limit.maximum)
+ Colors.ENDC
)
# memory_section
if module.memory_section is not None and (
dbgsection == "memory" or all
):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Memory Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.memory_section.count))
for iter in module.memory_section.memory_types:
print(
Colors.green
+ "Resizable_Limits:flags: "
+ repr(iter.flags)
+ Colors.ENDC
)
print(
Colors.red
+ "Resizable_Limits:initial: "
+ repr(iter.initial)
+ Colors.ENDC
)
print(
Colors.purple
+ "Resizable_Limits:maximum: "
+ repr(iter.maximum)
+ Colors.ENDC
)
# global_section
if module.global_section is not None and (
dbgsection == "global" or all
):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Global Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.global_section.count))
for iter in module.global_section.global_variables:
print(
Colors.green
+ "global type: "
+ repr(iter.global_type)
+ Colors.ENDC
)
print(
Colors.red
+ "init expr: "
+ repr(iter.init_expr)
+ Colors.ENDC
)
# export_section
if module.export_section is not None and (
dbgsection == "export" or all
):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Export Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.export_section.count))
for iter in module.export_section.export_entries:
print(
Colors.green
+ "field length: "
+ repr(iter.field_len)
+ Colors.ENDC
)
print(
Colors.red
+ "field str: "
+ repr(iter.field_str)
+ Colors.ENDC
)
print(Colors.purple + "kind: " + repr(iter.kind) + Colors.ENDC)
print(Colors.cyan + "index: " + repr(iter.index) + Colors.ENDC)
# start_section
if module.start_section is not None and (dbgsection == "start" or all):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Start Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(
"start function index: "
+ repr(module.start_section.function_section_index)
)
# element_section
if module.element_section is not None and (
dbgsection == "element" or all
):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Element Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.element_section.count))
for iter in module.element_section.elem_segments:
print(
Colors.green + "index: " + repr(iter.index) + Colors.ENDC
)
print(
Colors.red + "offset: " + repr(iter.offset) + Colors.ENDC
)
print(
Colors.purple
+ "num_elem: "
+ repr(iter.num_elem)
+ Colors.ENDC
)
print(Colors.cyan + "elemes:" + repr(iter.elems) + Colors.ENDC)
# code_section
if module.code_section is not None and (dbgsection == "code" or all):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Code Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.code_section.count))
for iter in module.code_section.func_bodies:
print(
Colors.green
+ "body size: "
+ repr(iter.body_size)
+ Colors.ENDC
)
print(
Colors.red
+ "local enrty count: "
+ repr(iter.local_count)
+ Colors.ENDC
)
for iterer in iter.locals:
print(
Colors.blue
+ "local count: "
+ repr(iterer.count)
+ Colors.ENDC
)
print(
Colors.blue
+ "local type: "
+ repr(iterer.type)
+ Colors.ENDC
)
for iterer in iter.code:
instruction = iterer.opcode + " " + repr(iterer.operands)
print(Colors.yellow + instruction + Colors.ENDC, end="")
print("\t", end="")
print(
Colors.cyan
+ "opcode: "
+ repr(iterer.opcode)
+ Colors.ENDC,
end="",
)
print("\t", end="")
print(
Colors.grey
+ "immediate: "
+ repr(iterer.operands)
+ Colors.ENDC,
end="",
)
print("\t", end="")
print(
Colors.UNDERLINE
+ "num of operands: "
+ repr(len(iterer.operands))
+ Colors.ENDC
)
# data_section
if module.data_section is not None and (dbgsection == "data" or all):
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print(Colors.blue + Colors.BOLD + "Data Section:" + Colors.ENDC)
print(
Colors.blue
+ "------------------------------------------------------"
+ Colors.ENDC
)
print("count: " + repr(module.data_section.count))
for iter in module.data_section.data_segments:
print(
Colors.green + "index: " + repr(iter.index) + Colors.ENDC
)
print(
Colors.red + "offset: " + repr(iter.offset) + Colors.ENDC
)
print(Colors.purple + "size: " + repr(iter.size) + Colors.ENDC)
print(Colors.cyan + "data:" + repr(iter.data) + Colors.ENDC)
# palceholder for the validation tests
def runValidations(self):
modulevalidation = ModuleValidation(self.modules[0])
return modulevalidation.ValidateAll()
def premain(argparser):
interpreter = PythonInterpreter()
if argparser.args.wasm:
for file_path in argparser.args.wasm:
module = interpreter.parse(file_path)
print(type(module))
interpreter.appendmodule(module)
if argparser.args.dbg or argparser.args.dbgsection:
interpreter.dump_sections(module, argparser.args.dbgsection)
if interpreter.runValidations():
print(
Colors.red
+ "validations are not implemented yet"
+ Colors.ENDC
)
else:
print(Colors.red + "failed validation tests" + Colors.ENDC)
vm = VM(interpreter.getmodules())
vm.setFlags(argparser.getParseFlags())
ms = vm.getState()
if argparser.args.idxspc:
DumpIndexSpaces(ms)
if argparser.args.memdump:
DumpLinearMems(ms.Linear_Memory, argparser.getMEMDUMP())
if argparser.args.run:
vm.run()
# merklizer = Merklizer(ms.Linear_Memory[0][0:512], module)
# treelength, hashtree = merklizer.run()
if argparser.args.interactive:
variables = globals().copy()
variables.update(locals())
shell = code.InteractiveConsole(variables)
shell.interact(banner="WASM python REPL")
if argparser.args.hexdump:
dumpprettysections(
interpreter.getsections(), argparser.args.hexdump, ""
)
if argparser.args.sectiondump is not None:
dumpprettysections(
interpreter.getsections(), 24, argparser.args.sectiondump
)
if argparser.args.wast:
# print(argparser.getWASTPath())
parser = ParserV1(argparser.getWASTPath())
parser.run()
# WIP-the assmebler
if argparser.args.asb:
print("not implemented yet")
sys.exit(1)
# WIP-the disassmebler
if argparser.args.dis:
print("not implemented yet")
sys.exit(1)
def main():
argparser = Argparser()
if argparser.args.dbg:
try:
premain(argparser)
except Exception:
variables = globals().copy()
variables.update(locals())
shell = code.InteractiveConsole(variables)
shell.interact(banner="DEVIWASM REPL")
else:
premain(argparser)
if __name__ == "__main__":
main()