Source code for binexport.basic_block

from __future__ import annotations
import weakref
from functools import cached_property
from typing import TYPE_CHECKING

from binexport.utils import instruction_index_range, get_instruction_address
from binexport.instruction import InstructionBinExport

if TYPE_CHECKING:
    from binexport.program import ProgramBinExport
    from binexport.function import FunctionBinExport
    from binexport.binexport2_pb2 import BinExport2
    from binexport.types import Addr


[docs] class BasicBlockBinExport: """ Basic block class. """ def __init__( self, program: weakref.ref[ProgramBinExport], function: weakref.ref[FunctionBinExport], pb_bb: BinExport2.BasicBlock, ): """ :param program: Weak reference to the program :param function: Weak reference to the function :param pb_bb: protobuf definition of the basic block """ super(BasicBlockBinExport, self).__init__() self._program = program self._function = function self.pb_bb = pb_bb self.addr: Addr = None #: basic bloc address self.bytes = b"" #: bytes of the basic block self._len = 0 #: Length of the basic block (number of instructions) # Ranges are in fact the true basic blocks but BinExport # doesn't have the same basic block semantic and merge multiple basic blocks into one. # For example: BB_1 -- unconditional_jmp --> BB_2 # might be merged into a single basic block so the edge gets lost. for rng in pb_bb.instruction_index: for idx in instruction_index_range(rng): self.bytes += self.program.proto.instruction[idx].raw_bytes self._len += 1 # The first instruction determines the basic block address if self.addr is None: self.addr = get_instruction_address(self.program.proto, idx) def __hash__(self) -> int: """ Make function hashable to be able to store them in sets (for parents, children) :return: address of the basic block """ return hash(self.addr) def __str__(self) -> str: return "\n".join(str(i) for i in self.instructions.values()) def __repr__(self) -> str: return "<%s:0x%x>" % (type(self).__name__, self.addr) def __len__(self) -> int: return self._len @property def program(self) -> ProgramBinExport: """ Wrapper on weak reference on ProgramBinExport :return: object :py:class:`ProgramBinExport`, program associated to the basic block """ return self._program() @property def function(self) -> FunctionBinExport: """ Wrapper on weak reference on FunctionBinExport :return: object :py:class:`FunctionBinExport`, function associated to the basic block """ return self._function() @cached_property def instructions(self) -> dict[Addr, InstructionBinExport]: """ Returns a dict which is used to reference all the instructions in this basic block by their address. The object returned is by default cached, to erase the cache delete the attribute. :return: dictionary of addresses to instructions """ instructions = {} # Ranges are in fact the true basic blocks but BinExport # doesn't have the same basic block semantic and merge multiple basic blocks into one. # For example: BB_1 -- unconditional_jmp --> BB_2 # might be merged into a single basic block so the edge gets lost. for rng in self.pb_bb.instruction_index: for idx in instruction_index_range(rng): inst_addr = get_instruction_address(self.program.proto, idx) instructions[inst_addr] = InstructionBinExport( self._program, self._function, inst_addr, idx ) return instructions