Source code for cle.backends.elf.variable

from typing import TYPE_CHECKING, Optional

from elftools.dwarf.die import DIE

from cle.address_translator import AT

from .variable_type import VariableType

if TYPE_CHECKING:
    from .elf import ELF
    from .subprogram import LexicalBlock


[docs] class Variable: """ Variable for DWARF from a DW_TAG_variable or DW_TAG_formal_parameter :ivar str name: The name of the variable :ivar relative_addr: The relative addr (base addr depends on the type) :ivar lexical_block: For a local variable, the lexical block where the variable is declared """
[docs] def __init__(self, elf_object: "ELF"): self._elf_object = elf_object # all other optional params can be set afterwards self.relative_addr = None self.name = None self._type_offset = None self.decl_line = None self.decl_file = None self.lexical_block = None self.external = False self.declaration_only = False
[docs] @staticmethod def from_die(die: DIE, expr_parser, elf_object: "ELF", lexical_block: Optional["LexicalBlock"] = None): # first the address if "DW_AT_location" in die.attributes and die.attributes["DW_AT_location"].form == "DW_FORM_exprloc": parsed_exprs = expr_parser.parse_expr(die.attributes["DW_AT_location"].value) if len(parsed_exprs) == 1 and parsed_exprs[0].op_name == "DW_OP_addr": addr = parsed_exprs[0].args[0] var = MemoryVariable(elf_object, addr) elif len(parsed_exprs) == 1 and parsed_exprs[0].op_name == "DW_OP_fbreg": addr = parsed_exprs[0].args[0] var = StackVariable(elf_object, addr) elif len(parsed_exprs) == 1 and parsed_exprs[0].op_name.startswith("DW_OP_reg"): addr = parsed_exprs[0].op - 0x50 # 0x50 == DW_OP_reg0 var = RegisterVariable(elf_object, addr) else: var = Variable(elf_object) else: var = Variable(elf_object) if "DW_AT_name" in die.attributes: var.name = die.attributes["DW_AT_name"].value.decode("utf-8") if "DW_AT_type" in die.attributes: var._type_offset = die.attributes["DW_AT_type"].value + die.cu.cu_offset if "DW_AT_decl_line" in die.attributes: var.decl_line = die.attributes["DW_AT_decl_line"].value if "DW_AT_external" in die.attributes: var.external = True if "DW_AT_declaration" in die.attributes: var.declaration_only = True var.lexical_block = lexical_block return var
# overwritten for stack variables
[docs] def rebased_addr_from_cfa(self, cfa: int): """ The address of this variable in the global memory. :param cfa: The canonical frame address as described by the DWARF standard. """ return self.rebased_addr
@property def rebased_addr(self): return None @property def addr(self): """ Please use 'relative_addr' or 'rebased_addr' instead. """ return self.relative_addr @property def type(self) -> VariableType: try: return self._elf_object.type_list[self._type_offset] except KeyError: return None @property def sort(self) -> str: # sort = 'stack' | 'register' | 'global' return "unknown"
[docs] class MemoryVariable(Variable): """ This includes all variables that are not on the stack and not in a register. So all global variables, and also local static variables in C! """
[docs] def __init__(self, elf_object: "ELF", relative_addr): super().__init__(elf_object) self.relative_addr = relative_addr
@property def rebased_addr(self): return AT.from_rva(self.relative_addr, self._elf_object).to_mva() @property def sort(self) -> str: return "global"
[docs] class StackVariable(Variable): """ Stack Variable from DWARF. """
[docs] def __init__(self, elf_object: "ELF", relative_addr): super().__init__(elf_object) self.relative_addr = relative_addr
[docs] def rebased_addr_from_cfa(self, cfa: int): return self.relative_addr + cfa
@property def sort(self) -> str: return "stack"
[docs] class RegisterVariable(Variable): """ Register Variable from DWARF. """
[docs] def __init__(self, elf_object: "ELF", register_addr): super().__init__(elf_object) # FIXME should this really go into relative addr? self.relative_addr = register_addr
@property def sort(self) -> str: return "register"