Esempio n. 1
0
def call_indirect_op(config: Configuration) -> None:
    """
    Logic function for the CALL_INDIRECT opcode
    """
    instruction = cast(CallIndirect, config.current_instruction)

    if config.enable_logic_fn_logging:
        logger.debug("%s()", instruction.opcode.text)

    table_address = config.frame_module.table_addrs[0]
    table = config.store.tables[table_address]
    function_type = config.frame_module.types[instruction.type_idx]

    element_idx = config.pop_u32()

    if len(table.elem) <= element_idx:
        raise Trap(
            "Element index out of table range: {element_idx} > {len(table.elem)}"
        )

    function_address = table.elem[element_idx]

    if function_address is None:
        raise Trap("Table element at index {element_idx} is empty")

    function = config.store.funcs[int(function_address)]

    if function.type != function_type:
        raise Trap(
            "Function type mismatch.  Expected {function_type}.  Got {function.type}"
        )

    _setup_call(config, function_address)
Esempio n. 2
0
def iXX_divs_op(config: Configuration) -> None:
    """
    Common logic function for the integer DIVS opcodes
    """
    instruction = cast(BinOp, config.current_instruction)
    b, a = config.pop2_u32()

    b_s = instruction.valtype.to_signed(b)
    a_s = instruction.valtype.to_signed(a)
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", instruction.opcode.text, a_s, b_s)

    if b == 0:
        raise Trap('DIVISION BY ZERO')

    raw_result = abs(int(a_s)) // abs(int(b_s))
    _, upper_bound = instruction.valtype.signed_bounds

    if raw_result > upper_bound:
        raise Trap('UNDEFINED')

    if (a_s < 0) is not (b_s < 0):
        signed_result = instruction.valtype.signed_type(-1 * raw_result)
    else:
        signed_result = instruction.valtype.signed_type(raw_result)

    result = instruction.valtype.from_signed(signed_result)

    config.push_operand(result)
Esempio n. 3
0
def iXX_trunc_usX_fXX_op(config: Configuration) -> None:
    """
    Common logic function for the TRUNC opcodes which convert a float to an
    integer
    """
    instruction = cast(Truncate, config.current_instruction)

    value = config.pop_f32()

    if config.enable_logic_fn_logging:
        logger.debug("%s(%s)", instruction.opcode.text, value)

    if numpy.isnan(value) or numpy.isinf(value):
        raise Trap(f"Truncation is undefined for {value}")
    else:
        trunc_value = int(numpy.trunc(value))

        if instruction.signed:
            s_lower_bound, s_upper_bound = instruction.valtype.signed_bounds
            if trunc_value < s_lower_bound or trunc_value > s_upper_bound:
                raise Trap(
                    f"Truncation is undefined for {value}. Result outside of s32 "
                    "range.")

            result = instruction.valtype.from_signed(
                instruction.valtype.signed_type(trunc_value))
        else:
            u_lower_bound, u_upper_bound = instruction.valtype.bounds
            if trunc_value < u_lower_bound or trunc_value > u_upper_bound:
                raise Trap(
                    f"Truncation is undefined for {value}. Result outside of s32 "
                    "range.")
            result = instruction.valtype.value(trunc_value)

        config.push_operand(result)
Esempio n. 4
0
    def read(self, location: numpy.uint32, size: numpy.uint32) -> memoryview:
        with no_overflow():
            try:
                end_location = location + size
            except FloatingPointError:
                raise Trap(
                    f"Attempt to read from out of bounds memory location: {int(location) + size} "
                    f"> {len(self.data)}")

        if end_location > self._length_cache:
            raise Trap(
                f"Attempt to read from out of bounds memory location: {end_location} "
                f"> {len(self.data)}")
        return memoryview(self.data)[location:end_location]
Esempio n. 5
0
def unreachable_op(config: Configuration) -> None:
    """
    Logic function for the UNREACHABLE opcode
    """
    if config.enable_logic_fn_logging:
        logger.debug("%s()", config.current_instruction.opcode.text)
    raise Trap("TRAP")
Esempio n. 6
0
 def write(self, location: numpy.uint32, value: bytes) -> None:
     end_index = location + len(value)
     if end_index > self._length_cache:
         raise Trap(f"Attempt to write to out of bounds memory location: "
                    f"{end_index} "
                    f"> {len(self.data)}")
     self.data[location:end_index] = value
Esempio n. 7
0
def idivu_op(config: Configuration) -> None:
    """
    Common logic function for the integer DIVU opcodes
    """
    b, a = config.pop2_u32()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", config.current_instruction.opcode.text, a,
                     b)

    if b == 0:
        raise Trap('DIVISION BY ZERO')
    config.push_operand(a // b)
Esempio n. 8
0
def iXX_rems_op(config: Configuration) -> None:
    """
    Common logic function for the integer REMS opcodes
    """
    instruction = cast(BinOp, config.current_instruction)
    b, a = config.pop2_u64()
    if config.enable_logic_fn_logging:
        logger.debug("%s(%s, %s)", instruction.opcode.text, a, b)

    if b == 0:
        raise Trap('DIVISION BY ZERO')

    b_s = instruction.valtype.to_signed(b)
    a_s = instruction.valtype.to_signed(a)

    raw_result = numpy.abs(a_s) % numpy.abs(b_s)
    result = -1 * raw_result if a_s < 0 else raw_result

    config.push_operand(instruction.valtype.value(result))
Esempio n. 9
0
def load_op(config: Configuration) -> None:
    """
    Logic function for the various *LOAD* memory opcodes.
    """
    instruction = cast(MemoryOp, config.current_instruction)
    if config.enable_logic_fn_logging:
        logger.debug("%s()", instruction.opcode.text)

    memarg = instruction.memarg

    memory_address = config.frame_module.memory_addrs[0]
    mem = config.store.mems[memory_address]

    base_offset = config.pop_u32()
    with no_overflow():
        try:
            memory_location = base_offset + memarg.offset
        except FloatingPointError:
            raise Trap(
                "Memory locatin exceeds u32 bounds: {int(base_offset) + memarg.offset"
            )

    value_bit_width = instruction.memory_bit_size.value
    value_byte_width = value_bit_width // constants.BYTE_SIZE

    raw_bytes = mem.read(memory_location, value_byte_width)

    if instruction.valtype.is_integer_type:
        raw_value = instruction.memory_bit_size.unpack_int_bytes(
            raw_bytes,
            bool(instruction.signed),
        )

        if instruction.signed:
            config.push_operand(instruction.valtype.from_signed(raw_value))
        else:
            config.push_operand(instruction.valtype.value(raw_value))
    elif instruction.valtype.is_float_type:
        value = instruction.valtype.unpack_float_bytes(raw_bytes)
        config.push_operand(value)
    else:
        raise Exception("Invariant")