Source code for cle.backends.symbol
import logging
from enum import Enum
from typing import TYPE_CHECKING
from cle.address_translator import AT
if TYPE_CHECKING:
from .backend import Backend
log = logging.getLogger(name=__name__)
__all__ = [
"SymbolType",
"SymbolSubType",
"Symbol",
]
[docs]class SymbolType(Enum):
"""
ABI-agnostic symbol types
"""
TYPE_OTHER = 0
TYPE_NONE = 1
TYPE_FUNCTION = 2
TYPE_OBJECT = 3
TYPE_SECTION = 4
TYPE_TLS_OBJECT = 5
[docs]class SymbolSubType(Enum):
"""
Abstract base class for ABI-specific symbol types
"""
[docs] def to_base_type(self) -> SymbolType: # pylint: disable=no-self-use
"""
A subclass' ABI-specific mapping to :SymbolType:
"""
raise ValueError("Abstract base class SymbolSubType has no base_type")
[docs]class Symbol:
"""
Representation of a symbol from a binary file. Smart enough to rebase itself.
There should never be more than one Symbol instance representing a single symbol. To make sure of this, only use
the :meth:`cle.backends.Backend.get_symbol()` to create new symbols.
:ivar owner: The object that contains this symbol
:vartype owner: cle.backends.Backend
:ivar str name: The name of this symbol
:ivar int addr: The un-based address of this symbol, an RVA
:ivar int size: The size of this symbol
:ivar _type: The ABI-agnostic type of this symbol
:ivar bool resolved: Whether this import symbol has been resolved to a real symbol
:ivar resolvedby: The real symbol this import symbol has been resolve to
:vartype resolvedby: None or cle.backends.Symbol
:ivar str resolvewith: The name of the library we must use to resolve this symbol, or None if none is required.
"""
[docs] def __init__(self, owner: "Backend", name: str, relative_addr: int, size: int, sym_type: SymbolType):
"""
Not documenting this since if you try calling it, you're wrong.
"""
self.owner: Backend = owner
self.name = name
self.relative_addr = relative_addr
self.size = size
self._type: SymbolType = SymbolType(sym_type)
self.resolved = False
self.resolvedby = None
def __repr__(self):
if self.is_import:
return f'<Symbol "{self.name}" in {self.owner.binary_basename} (import)>'
else:
return f'<Symbol "{self.name}" in {self.owner.binary_basename} at {self.rebased_addr:#x}>'
[docs] def resolve(self, obj):
self.resolved = True
self.resolvedby = obj
self.owner.resolved_imports.append(self)
@property
def type(self) -> SymbolType:
"""
The ABI-agnostic SymbolType. Must be overridden by derived types.
"""
return self._type
@property
def subtype(self) -> SymbolSubType:
"""
A subclass' ABI-specific types
"""
raise ValueError("Base class Symbol has no subtype")
@property
def rebased_addr(self):
"""
The address of this symbol in the global memory space
"""
return AT.from_rva(self.relative_addr, self.owner).to_mva()
@property
def linked_addr(self):
return AT.from_rva(self.relative_addr, self.owner).to_lva()
@property
def is_function(self):
"""
Whether this symbol is a function
"""
return self.type is SymbolType.TYPE_FUNCTION
# These may be overridden in subclasses
is_static = False
is_common = False
is_import = False
is_export = False
is_local = False
is_weak = False
is_extern = False
is_forward = False
[docs] def resolve_forwarder(self):
"""
If this symbol is a forwarding export, return the symbol the forwarding refers to, or None if it cannot be found
"""
return self
# compatibility layer
_complained_owner = False
@property
def owner_obj(self):
if not Symbol._complained_owner:
Symbol._complained_owner = True
log.critical("Deprecation warning: use symbol.owner instead of symbol.owner_obj")
return self.owner
def __getstate__(self):
return {k: v for k, v in self.__dict__.items() if k != "owner"}
def __setstate__(self, state):
self.__dict__.update(state)