Exemplo n.º 1
0
    def _pick_jump(self, base_addr: int, imm_optype: ImmOperandType,
                   model: Model, program: Program,
                   tgt_addr: Optional[int]) -> Optional[Tuple[int, int, int]]:
        '''Pick target and link register for a jump instruction

        For a JALR instruction, base_addr is the address stored in the register
        that we'll branch through. For a JAL instruction, it is zero: the
        PC-relative offset is encoded on the operand type itself. imm_optype is
        the OperandType for the immediate operand that we are generating.

        Returns (tgt, enc_offset, link_idx) where tgt is the target address,
        enc_offset is the offset (encoded as 2's complement if necessary for
        the immediate operand) and link_idx is the index of the chosen link
        register.

        '''
        # Calculate the range of offsets we can encode (this includes any
        # PC-relative adjustment)
        #
        # We can assume that get_range() returns something, because it only
        # returns None if the operand has no width: not possible because we
        # know we have an encoding for the instruction.
        imm_rng = imm_optype.get_op_val_range(model.pc)
        assert imm_rng is not None
        imm_min, imm_max = imm_rng

        # Adjust for base_addr
        tgt_min = imm_min + base_addr
        tgt_max = imm_max + base_addr

        # If there is a desired target, check it's representable. If not,
        # return None. Otherwise, narrow the range to just that.
        if tgt_addr is not None:
            if tgt_min <= tgt_addr <= tgt_max:
                tgt_min = tgt_addr
                tgt_max = tgt_addr
            else:
                return None

        # Pick a branch target. "1" here is the minimum number of instructions
        # that must fit. One is enough (we'll just end up generating another
        # branch immediately)
        tgt = program.pick_branch_target(model.pc, 1, tgt_min, tgt_max)
        if tgt is None:
            return None
        assert tgt_min <= tgt <= tgt_max
        assert tgt & 3 == 0

        # Adjust again for base_addr: we pick the offset from there
        op_val = tgt - base_addr

        enc_offset = imm_optype.op_val_to_enc_val(op_val, model.pc)
        assert enc_offset is not None

        # Pick a link register, not preferring any in particular. This should
        # never fail (it's a destination, not a source).
        link_reg_idx = model.pick_operand_value(self.jal.operands[0].op_type)
        assert link_reg_idx is not None

        return (tgt, enc_offset, link_reg_idx)
Exemplo n.º 2
0
    def _pick_imm_operand_value(self,
                                op_type: ImmOperandType) -> Optional[int]:

        op_rng = op_type.get_op_val_range(self.pc)
        if op_rng is None:
            # If we don't know the width, the only immediate that we *know*
            # is going to be valid is 0.
            return 0

        align = 1 << op_type.shift

        lo, hi = op_rng
        sh_lo = (lo + align - 1) // align
        sh_hi = hi // align

        op_val = random.randint(sh_lo, sh_hi) << op_type.shift
        return op_type.op_val_to_enc_val(op_val, self.pc)
Exemplo n.º 3
0
    def _pick_jump(self,
                   base_addr: int,
                   imm_optype: ImmOperandType,
                   model: Model,
                   program: Program) -> Optional[Tuple[int, int, int]]:
        '''Pick target and link register for a jump instruction

        base_addr is the starting address (either the current PC for a JAL or
        the value of a register for a JALR). imm_optype is the OperandType for
        the immediate operand that we are generating.

        Returns (tgt, enc_offset, link_idx) where tgt is the target address,
        enc_offset is the offset (encoded as 2's complement if necessary for
        the immediate operand) and link_idx is the index of the chosen link
        register.

        '''
        # Calculate the range of addresses we can hit, starting at base_addr.
        #
        # We can assume that get_range() returns something, because it only
        # returns None if the operand has no width: not possible because we
        # know we have an encoding for the instruction.
        offset_range = imm_optype.get_range()
        assert offset_range

        offset_min, offset_max = offset_range
        tgt_min = base_addr + offset_min
        tgt_max = base_addr + offset_max

        # Pick a branch target. "1" here is the minimum number of instructions
        # that must fit. One is enough (we'll just end up generating another
        # branch immediately)
        tgt = program.pick_branch_target(1, tgt_min, tgt_max)
        if tgt is None:
            return None

        offset = tgt - base_addr
        assert offset_min <= offset <= offset_max
        enc_offset = imm_optype.encode_val(offset)

        # Pick a link register, not preferring any in particular. This should
        # never fail (it's a destination, not a source).
        link_reg_idx = model.pick_operand_value(self.jal.operands[0].op_type)
        assert link_reg_idx is not None

        return (tgt, enc_offset, link_reg_idx)
Exemplo n.º 4
0
    def _pick_loop_shape(self, op0_type: OperandType, op1_type: ImmOperandType,
                         space_here: int, model: Model,
                         program: Program) -> Optional[Tuple[int, int, int]]:
        '''Pick the size of loop and number of iterations

        op_type is the type of the first operand (either 'grs' for loop or
        'iterations' for loopi). space_here is the number of instructions'
        space available at the current position.

        '''
        # The first upper bound on bodysize is that we've got to have an empty
        # space for the loop body.
        #
        # Note: This doesn't allow us to generate a "loop" that encloses
        # previously generated code. So, for example, we couldn't do something
        # like
        #
        #    loopi  10, 3
        #    jal    x0, .+8
        #    jal    x0, .+100  // an isolated instruction that ran earlier
        #    addi   x0, 0      // end of loop
        #
        # Since we can generate jumps in the loop, we might "fill in
        # the middle" afterwards. However, we'll never make a loop that
        # "contains" instructions we executed before.
        #
        # To weaken this, we would need to just require that the end of the
        # loop is not yet taken. But that sounds a bit hard: let's not worry
        # about it for now.
        assert 3 <= space_here
        max_bodysize = space_here - 2

        # Another upper bound comes from program.get_insn_space_left(). If
        # bodysize is 2 or more, our body will need to generate at least 2
        # instructions (either a straight line of length bodysize, or a jump
        # from the start and then a straight line instruction at the end). In
        # this case, we need space for at least 3 instructions (including the
        # LOOP/LOOPI instruction itself).
        #
        # We know that program.get_insn_space_left() is at least 2 (checked in
        # gen()), but if it's 2, we can only have a bodysize of 1.
        assert 2 <= program.space
        if program.space == 2:
            max_bodysize = min(max_bodysize, 1)

        bodysize_range = op1_type.get_op_val_range(model.pc)
        assert bodysize_range is not None
        bs_min, bs_max = bodysize_range
        if max_bodysize < max(1, bs_min):
            return None

        # Decide on the bodysize value. tail_pc is the address of the last
        # instruction in the loop body.
        bodysize = random.randint(max(1, bs_min), min(bs_max, max_bodysize))
        tail_pc = model.pc + 4 * bodysize
        assert program.get_insn_space_at(tail_pc) >= 2

        iters = self._pick_iterations(op0_type, bodysize, model)
        if iters is None:
            return None
        iter_opval, num_iters = iters

        return (iter_opval, num_iters, bodysize)