diff options
Diffstat (limited to 'python/basicblock.py')
| -rw-r--r-- | python/basicblock.py | 126 |
1 files changed, 55 insertions, 71 deletions
diff --git a/python/basicblock.py b/python/basicblock.py index 65069f6c..ba2e44e0 100644 --- a/python/basicblock.py +++ b/python/basicblock.py @@ -66,15 +66,31 @@ class BasicBlock: core.BNFreeBasicBlock(self.handle) def __repr__(self): - arch = self.arch - if arch: - return f"<block: {arch.name}@{self.start:#x}-{self.end:#x}>" - else: - return f"<block: {self.start:#x}-{self.end:#x}>" + return f"<block: {self.arch.name}@{self.start:#x}-{self.end:#x}>" def __len__(self): return int(core.BNGetBasicBlockLength(self.handle)) + def __lt__(self, other: 'BasicBlock') -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.start < other.start + + def __gt__(self, other: 'BasicBlock') -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.start > other.start + + def __le__(self, other: 'BasicBlock') -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.start <= other.start + + def __ge__(self, other: 'BasicBlock') -> bool: + if not isinstance(other, self.__class__): + return NotImplemented + return self.start >= other.start + def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented @@ -88,15 +104,7 @@ class BasicBlock: def __hash__(self): return hash((self.start, self.end, self.arch)) - def __setattr__(self, name, value): - try: - object.__setattr__(self, name, value) - except AttributeError: - raise AttributeError(f"attribute '{name}' is read only") - def __iter__(self) -> Generator[Tuple[List['_function.InstructionTextToken'], int], None, None]: - if self.arch is None: - raise Exception("Attempting to iterate a BasicBlock object with no Architecture set") if self.view is None: raise Exception("Attempting to iterate a Basic Block with no BinaryView") if self._instStarts is None: @@ -104,7 +112,9 @@ class BasicBlock: idx = self.start while idx < self.end: data = self.view.read(idx, min(self.arch.max_instr_length, self.end - idx)) - text, size = self.arch.get_instruction_text(data, idx) + result = self.arch.get_instruction_text(data, idx) + assert result is not None + text, size = result if size == 0: break yield text, size @@ -112,7 +122,9 @@ class BasicBlock: else: assert self._instLengths is not None for start, length in zip(self._instStarts, self._instLengths): - text, size = self.arch.get_instruction_text(self.view.read(start, length), start) + result = self.arch.get_instruction_text(self.view.read(start, length), start) + assert result is not None + text, size = result if size == 0: break yield text, size @@ -121,8 +133,6 @@ class BasicBlock: self._buildStartCache() assert self._instStarts is not None assert self._instLengths is not None - if self.arch is None: - raise Exception("Attempting to iterate a BasicBlock object with no Architecture set") if self.view is None: raise Exception("Attempting to iterate a Basic Block with no BinaryView") @@ -152,7 +162,8 @@ class BasicBlock: self._instStarts.append(start) start += length - def _create_instance(self, handle: core.BNBasicBlockHandle, view: 'binaryview.BinaryView') -> 'BasicBlock': + @staticmethod + def _create_instance(handle: core.BNBasicBlockHandle, view: Optional['binaryview.BinaryView']) -> 'BasicBlock': """Internal method used to instantiate child instances""" return BasicBlock(handle, view) @@ -184,15 +195,14 @@ class BasicBlock: return self._view @property - def arch(self) -> Optional['architecture.Architecture']: + def arch(self) -> 'architecture.Architecture': """Basic block architecture (read-only)""" # The arch for a BasicBlock isn't going to change so just cache # it the first time we need it if self._arch is not None: return self._arch arch = core.BNGetBasicBlockArchitecture(self.handle) - if arch is None: - return None + assert arch is not None, "core.BNGetBasicBlockArchitecture returned None" self._arch = architecture.CoreArchitecture._from_cache(arch) return self._arch @@ -234,7 +244,7 @@ class BasicBlock: branch_type = BranchType(edges[i].type) handle = core.BNNewBasicBlockReference(edges[i].target) assert handle is not None - target = self._create_instance(handle, self.view) + target = BasicBlock._create_instance(handle, self.view) if direction: sink, source = target, self else: @@ -276,14 +286,15 @@ class BasicBlock: """Whether basic block has any invalid instructions (read-only)""" return core.BNBasicBlockHasInvalidInstructions(self.handle) - def _make_blocks(self, blocks, count: int) -> List['BasicBlock']: + @staticmethod + def _make_blocks(blocks, count: int, view: Optional['binaryview.BinaryView'] = None) -> List['BasicBlock']: assert blocks is not None, "core returned empty block list" - result: List['BasicBlock'] = [] try: + result: List['BasicBlock'] = [] for i in range(0, count): handle = core.BNNewBasicBlockReference(blocks[i]) assert handle is not None - result.append(self._create_instance(handle, self.view)) + result.append(BasicBlock._create_instance(handle, view)) return result finally: core.BNFreeBasicBlockList(blocks, count) @@ -291,89 +302,71 @@ class BasicBlock: @property def dominators(self) -> List['BasicBlock']: """List of dominators for this basic block (read-only)""" - if self.view is None: - raise Exception("Attempting to call BasicBlock.dominators when BinaryView is None") count = ctypes.c_ulonglong() - return self._make_blocks(core.BNGetBasicBlockDominators(self.handle, count, False), count.value) + blocks = core.BNGetBasicBlockDominators(self.handle, count, False) + return BasicBlock._make_blocks(blocks, count.value, self.view) @property def post_dominators(self) -> List['BasicBlock']: """List of dominators for this basic block (read-only)""" - if self.view is None: - raise Exception("Attempting to call BasicBlock.post_dominators when BinaryView is None") count = ctypes.c_ulonglong() - return self._make_blocks(core.BNGetBasicBlockDominators(self.handle, count, True), count.value) + blocks = core.BNGetBasicBlockDominators(self.handle, count, True) + return BasicBlock._make_blocks(blocks, count.value, self.view) @property def strict_dominators(self) -> List['BasicBlock']: """List of strict dominators for this basic block (read-only)""" - if self.view is None: - raise Exception("Attempting to call BasicBlock.strict_dominators when BinaryView is None") count = ctypes.c_ulonglong() - return self._make_blocks(core.BNGetBasicBlockStrictDominators(self.handle, count, False), count.value) + blocks = core.BNGetBasicBlockStrictDominators(self.handle, count, False) + return BasicBlock._make_blocks(blocks, count.value, self.view) @property def immediate_dominator(self) -> Optional['BasicBlock']: """Immediate dominator of this basic block (read-only)""" - if self.view is None: - raise Exception("Attempting to call BasicBlock.immediate_dominator when BinaryView is None") - result = core.BNGetBasicBlockImmediateDominator(self.handle, False) if not result: return None - return self._create_instance(result, self.view) + return BasicBlock._create_instance(result, self.view) @property def immediate_post_dominator(self) -> Optional['BasicBlock']: """Immediate dominator of this basic block (read-only)""" - if self.view is None: - raise Exception("Attempting to call BasicBlock.immediate_post_dominator when BinaryView is None") - result = core.BNGetBasicBlockImmediateDominator(self.handle, True) if not result: return None - return self._create_instance(result, self.view) + return BasicBlock._create_instance(result, self.view) @property def dominator_tree_children(self) -> List['BasicBlock']: """List of child blocks in the dominator tree for this basic block (read-only)""" - if self.view is None: - raise Exception("Attempting to call BasicBlock.dominator_tree_children when BinaryView is None") - count = ctypes.c_ulonglong() - return self._make_blocks(core.BNGetBasicBlockDominatorTreeChildren(self.handle, count, False), count.value) + blocks = core.BNGetBasicBlockDominatorTreeChildren(self.handle, count, False) + return BasicBlock._make_blocks(blocks, count.value, self.view) @property def post_dominator_tree_children(self) -> List['BasicBlock']: """List of child blocks in the post dominator tree for this basic block (read-only)""" - if self.view is None: - raise Exception("Attempting to call BasicBlock.post_dominator_tree_children when BinaryView is None") - count = ctypes.c_ulonglong() - return self._make_blocks(core.BNGetBasicBlockDominatorTreeChildren(self.handle, count, True), count.value) + blocks = core.BNGetBasicBlockDominatorTreeChildren(self.handle, count, True) + return BasicBlock._make_blocks(blocks, count.value, self.view) @property def dominance_frontier(self) -> List['BasicBlock']: """Dominance frontier for this basic block (read-only)""" - if self.view is None: - raise Exception("Attempting to call BasicBlock.dominance_frontier when BinaryView is None") - count = ctypes.c_ulonglong() - return self._make_blocks(core.BNGetBasicBlockDominanceFrontier(self.handle, count, False), count.value) + blocks = core.BNGetBasicBlockDominanceFrontier(self.handle, count, False) + return BasicBlock._make_blocks(blocks, count.value, self.view) @property def post_dominance_frontier(self) -> List['BasicBlock']: """Post dominance frontier for this basic block (read-only)""" - if self.view is None: - raise Exception("Attempting to call BasicBlock.post_dominance_frontier when BinaryView is None") count = ctypes.c_ulonglong() - return self._make_blocks(core.BNGetBasicBlockDominanceFrontier(self.handle, count, True), count.value) + blocks = core.BNGetBasicBlockDominanceFrontier(self.handle, count, True) + return BasicBlock._make_blocks(blocks, count.value, self.view) @property def annotations(self) -> List[List['_function.InstructionTextToken']]: """List of automatic annotations for the start of this block (read-only)""" - if self.arch is None: - raise ValueError("attempting to get annotation from BasicBlock without architecture") if self.function is None: raise ValueError("Attempting to call BasicBlock.annotations when Function is None") @@ -428,7 +421,7 @@ class BasicBlock: return core.BNIsHighLevelILBasicBlock(self.handle) @staticmethod - def get_iterated_dominance_frontier(blocks: List['BasicBlock']) -> List['BasicBlock']: + def get_iterated_dominance_frontier(blocks: List['BasicBlock'], view: Optional['binaryview.BinaryView'] = None) -> List['BasicBlock']: if len(blocks) == 0: return [] block_set = (ctypes.POINTER(core.BNBasicBlock) * len(blocks))() # type: ignore @@ -436,22 +429,13 @@ class BasicBlock: block_set[i] = blocks[i].handle count = ctypes.c_ulonglong() out_blocks = core.BNGetBasicBlockIteratedDominanceFrontier(block_set, len(blocks), count) - assert out_blocks is not None, "core.BNGetBasicBlockIteratedDominanceFrontier returned None" - result = [] - try: - for i in range(0, count.value): - handle = core.BNNewBasicBlockReference(out_blocks[i]) - assert handle is not None - result.append(BasicBlock(handle, blocks[0].view)) - return result - finally: - core.BNFreeBasicBlockList(out_blocks, count.value) + return BasicBlock._make_blocks(out_blocks, count.value, view) def mark_recent_use(self) -> None: core.BNMarkBasicBlockAsRecentlyUsed(self.handle) def get_disassembly_text(self, - settings: '_function.DisassemblySettings' = None) -> List['_function.DisassemblyTextLine']: + settings: Optional['_function.DisassemblySettings'] = None) -> List['_function.DisassemblyTextLine']: """ ``get_disassembly_text`` returns a list of DisassemblyTextLine objects for the current basic block. |
