from elftools.dwarf.die import DIE
[docs]
class VariableType:
"""
Entry class for DW_TAG_xxx_type
:param name: name of the type
:param byte_size: amount of bytes the type take in memory
:param elf_object: elf object to reference to (useful for pointer,...)
:ivar name: name of the type
:type name: str
:ivar byte_size: amount of bytes the type take in memory
"""
[docs]
def __init__(self, name: str, byte_size: int, elf_object):
self.name = name
self.byte_size = byte_size
self._elf_object = elf_object
[docs]
@staticmethod
def read_from_die(die: DIE, elf_object):
"""
entry method to read a DW_TAG_xxx_type
"""
if die.tag == "DW_TAG_base_type":
return BaseType.read_from_die(die, elf_object)
elif die.tag == "DW_TAG_pointer_type":
return PointerType.read_from_die(die, elf_object)
elif die.tag == "DW_TAG_structure_type":
return StructType.read_from_die(die, elf_object)
elif die.tag == "DW_TAG_array_type":
return ArrayType.read_from_die(die, elf_object)
elif die.tag == "DW_TAG_typedef":
return TypedefType.read_from_die(die, elf_object)
elif die.tag == "DW_TAG_union_type":
return UnionType.read_from_die(die, elf_object)
return None
[docs]
@staticmethod
def supported_die(die: DIE) -> bool:
return die.tag in (
"DW_TAG_base_type",
"DW_TAG_pointer_type",
"DW_TAG_structure_type",
"DW_TAG_array_type",
"DW_TAG_typedef",
"DW_TAG_union_type",
)
[docs]
class PointerType(VariableType):
"""
Entry class for DW_TAG_pointer_type. It is inherited from VariableType
:param byte_size: amount of bytes the type take in memory
:param elf_object: elf object to reference to (useful for pointer,...)
:param referenced_offset: type of the referenced as offset in the compilation_unit
"""
[docs]
def __init__(self, byte_size: int, elf_object, referenced_offset: int):
super().__init__("pointer", byte_size, elf_object)
self._referenced_offset = referenced_offset
[docs]
@classmethod
def read_from_die(cls, die: DIE, elf_object):
"""
read an entry of DW_TAG_pointer_type. return None when there is no
byte_size or type attribute.
"""
byte_size = die.attributes.get("DW_AT_byte_size", None)
if byte_size is None:
return None
dw_at_type = die.attributes.get("DW_AT_type", None)
if dw_at_type is None:
referenced_offset = None
else:
referenced_offset = dw_at_type.value + die.cu.cu_offset
return cls(byte_size.value, elf_object, referenced_offset)
@property
def referenced_type(self):
"""
attribute to get the referenced type. Return None if the type is not loaded
"""
type_list = self._elf_object.type_list
if self._referenced_offset in type_list.keys():
return type_list[self._referenced_offset]
return None
[docs]
class BaseType(VariableType):
"""
Entry class for DW_TAG_base_type. It is inherited from VariableType
"""
# for __init__ see VariableType
[docs]
@classmethod
def read_from_die(cls, die: DIE, elf_object):
"""
read an entry of DW_TAG_base_type. return None when there is no
byte_size attribute.
"""
dw_at_name = die.attributes.get("DW_AT_name", None)
byte_size = die.attributes.get("DW_AT_byte_size", None)
if byte_size is None:
return None
return cls(dw_at_name.value.decode() if dw_at_name is not None else "unknown", byte_size.value, elf_object)
[docs]
class StructType(VariableType):
"""
Entry class for DW_TAG_structure_type. It is inherited from VariableType
:param name: name of the type
:param byte_size: amount of bytes the type take in memory
:param elf_object: elf object to reference to (useful for pointer,...)
"""
[docs]
def __init__(self, name: str, byte_size: int, elf_object, members):
super().__init__(name, byte_size, elf_object)
self.members = members
[docs]
@classmethod
def read_from_die(cls, die: DIE, elf_object):
"""
read an entry of DW_TAG_structure_type. return None when there is no
byte_size attribute.
"""
dw_at_name = die.attributes.get("DW_AT_name", None)
byte_size = die.attributes.get("DW_AT_byte_size", None)
if byte_size is None:
return None
members = []
for die_child in die.iter_children():
if die_child.tag == "DW_TAG_member":
members.append(StructMember.read_from_die(die_child, elf_object))
return cls(
dw_at_name.value.decode() if dw_at_name is not None else "unknown", byte_size.value, elf_object, members
)
def __getitem__(self, member_name):
for member in self.members:
if member.name == member_name:
return member
raise KeyError
[docs]
class UnionType(StructType):
"""
Entry class for DW_TAG_union_type. Inherits from StructType to make it trivial.
"""
[docs]
class StructMember:
"""
Entry class for DW_TAG_member. This is not a type but a named member inside a struct.
Use the property `type` to get its variable type.
:param name: name of the member
:param addr_offset: address offset of the member in the struct
:param elf_object: elf object to reference to (useful for pointer,...)
:param type_offset: type as offset in the compilation_unit
:ivar name: name of the member
"""
[docs]
def __init__(self, name: str, addr_offset: int, type_offset, elf_object):
self.name = name
self.addr_offset = addr_offset
self._elf_object = elf_object
self._type_offset = type_offset
[docs]
@classmethod
def read_from_die(cls, die: DIE, elf_object):
"""
read an entry of DW_TAG_member_type. return None when there is no
type attribute.
"""
dw_at_name = die.attributes.get("DW_AT_name", None)
dw_at_type = die.attributes.get("DW_AT_type", None)
dw_at_memloc = die.attributes.get("DW_AT_data_member_location", None)
name = None if dw_at_name is None else dw_at_name.value.decode()
ty = None if dw_at_type is None else dw_at_type.value + die.cu.cu_offset
# From the DWARF5 manual, page 118:
# The member entry corresponding to a data member that is defined in a structure,
# union or class may have either a DW_AT_data_member_location attribute or a
# DW_AT_data_bit_offset attribute. If the beginning of the data member is the
# same as the beginning of the containing entity then neither attribute is required.
# TODO bit_offset
addr_offset = 0 if dw_at_memloc is None else dw_at_memloc.value
return cls(name, addr_offset, ty, elf_object)
@property
def type(self):
"""
attribute to get the type of the member. Return None if the type is not loaded
"""
type_list = self._elf_object.type_list
if self._type_offset in type_list.keys():
return type_list[self._type_offset]
return None
[docs]
class ArrayType(VariableType):
"""
Entry class for DW_TAG_array_type. It is inherited from VariableType
:param byte_size: amount of bytes the type take in memory
:param elf_object: elf object to reference to (useful for pointer,...)
:param element_offset: type of the array elements as offset in the compilation_unit
"""
[docs]
def __init__(self, byte_size, elf_object, element_offset):
super().__init__("array", byte_size, elf_object)
self._element_offset = element_offset
[docs]
@classmethod
def read_from_die(cls, die: DIE, elf_object):
"""
read an entry of DW_TAG_array_type. return None when there is no
type attribute.
"""
dw_byte_size = die.attributes.get("DW_AT_byte_size", None)
dw_at_type = die.attributes.get("DW_AT_type", None)
if dw_at_type is None:
return None
return cls(
dw_byte_size.value if dw_byte_size is not None else None, elf_object, dw_at_type.value + die.cu.cu_offset
)
@property
def element_type(self):
type_list = self._elf_object.type_list
if self._element_offset in type_list.keys():
return type_list[self._element_offset]
return None
[docs]
class TypedefType(VariableType):
"""
Entry class for DW_TAG_typedef. Inherits from VariableType.
:param name: name of the new type
:param elf_object: elf object to reference to (useful for pointer,...)
:param type_offset: type as offset in the compilation_unit
"""
[docs]
def __init__(self, name: str, byte_size, elf_object, type_offset):
super().__init__(name, byte_size, elf_object)
self._type_offset = type_offset
[docs]
@classmethod
def read_from_die(cls, die: DIE, elf_object):
"""
read an entry of DW_TAG_member_type. return None when there is no
type attribute.
"""
dw_at_name = die.attributes.get("DW_AT_name", None)
dw_at_type = die.attributes.get("DW_AT_type", None)
dw_at_byte_size = die.attributes.get("DW_AT_byte_size", None)
name = None if dw_at_name is None else dw_at_name.value.decode()
type_offset = None if dw_at_type is None else dw_at_type.value + die.cu.cu_offset
byte_size = None if dw_at_byte_size is None else dw_at_byte_size.value
return cls(name, byte_size, elf_object, type_offset)
@property
def type(self):
"""
attribute to get the type of the member. Return None if the type is not loaded
"""
type_list = self._elf_object.type_list
if self._type_offset in type_list.keys():
return type_list[self._type_offset]
return None