Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion plugins/xrefer/backend/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""backend abstraction with pythonic factory pattern."""

from .base import Address, BackEnd, BackendError, Function, FunctionType, Section, String, StringEncType, Xref, XrefType
from .base import Address, BackEnd, BackendError, Function, FunctionType, Section, SectionType, String, StringEncType, Xref, XrefType, OperandType, Operand, Instruction
from .factory import backend_manager, get_backend, list_available_backends
from .utils import sample_path

Expand Down Expand Up @@ -50,6 +50,11 @@ def _ensure_backend_initialized():

def get_current_backend():
"""Get the current backend instance, initializing if needed."""
# First check if backend manager has an active backend
active_backend = backend_manager.get_active_backend()
if active_backend is not None:
return active_backend
# Fall back to legacy initialization
_ensure_backend_initialized()
return Backend

Expand All @@ -71,8 +76,13 @@ def get_current_backend():
"String",
"Xref",
"Section",
"SectionType",
"sample_path",
"get_indirect_calls",
# operand
"Instruction",
"Operand",
"OperandType",
# Base classes
"BackEnd",
]
53 changes: 50 additions & 3 deletions plugins/xrefer/backend/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,45 @@ def is_valid(self) -> bool:
return self != self.invalid()


class OperandType(Enum):
"""Canonical operand categories across backends."""
IMMEDIATE = auto()
REGISTER = auto()
MEMORY = auto()
RELATIVE = auto() # branch/call rel targets
OTHER = auto()

@dataclass(frozen=True)
class MemoryOperand:
"""Structured memory operand (best-effort, tolerant across tools)."""
base: Optional[str] = None
index: Optional[str] = None
scale: Optional[int] = None
disp: Optional[int] = None
seg: Optional[str] = None
addr_size: Optional[int] = None # in bits if known

@dataclass(frozen=True)
class Operand:
"""Unified operand."""
type: OperandType
text: str
value: Optional[Address]=None
# reg: Optional[str] = None
# imm: Optional[int] = None
# mem: Optional[MemoryOperand] = None

@dataclass
class Instruction:
address: Address
# prefixes: Tuple[str, ...] # e.g., ("lock",) or (). TODO: forget for now
mnemonic: str # canonical, lowercased, NO prefixes
operands: Tuple[Operand, ...]
text: str # full display text as shown in tool




@dataclass
class BasicBlock:
"""
Expand Down Expand Up @@ -444,9 +483,6 @@ def strings(self, min_length: int = String.MIN_LENGTH) -> Iterator[String]:
String objects for each identified string
"""
...
pass

# Symbol Resolution

@abstractmethod
def get_name_at(self, address: Address) -> str:
Expand Down Expand Up @@ -734,6 +770,12 @@ def set_function_comment(self, address: Address, comment: str) -> bool:
except Exception:
return False

def disassemble(self, address: Address) -> "Instruction":
"""
Disassemble a single instruction at `address`.
"""
return self._get_disassembly_impl(address)

#
# Backend-Specific Implementation Methods
#
Expand All @@ -757,3 +799,8 @@ def _set_function_comment_impl(self, address: Address, comment: str) -> None:
def _path_impl(self) -> str:
"""Backend-specific implementation for getting binary path."""
...

@abstractmethod
def _get_disassembly_impl(self, address: Address) -> Instruction:
"""Backend-specific implementation for getting disassembly at a specific address."""
...
Loading