mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
bfed42ed1e
Add the `get_llvmcpy` helper function which returns an instance of `LLVMCPy` which uses the `llvm-config` that's present in the search paths.
652 lines
27 KiB
Python
652 lines
27 KiB
Python
#
|
|
# This file is distributed under the MIT License. See LICENSE.md for details.
|
|
#
|
|
# mypy: disable-error-code="attr-defined,name-defined"
|
|
|
|
import sys
|
|
from typing import Dict, List, Optional, Set, Tuple, Union
|
|
|
|
import idb
|
|
import idb.analysis
|
|
import idb.fileformat
|
|
import idb.typeinf
|
|
import idb.typeinf_flags
|
|
|
|
import revng.model as m
|
|
from revng.model.metaaddress import MetaAddressType
|
|
from revng.support import log_error
|
|
|
|
RevngTypeDefinitions = Union[
|
|
m.UnionDefinition,
|
|
m.StructDefinition,
|
|
m.EnumDefinition,
|
|
m.TypedefDefinition,
|
|
m.CABIFunctionDefinition,
|
|
# Omit RFDs, as this importer never emits them.
|
|
# m.RawFunctionDefinition,
|
|
]
|
|
|
|
idb_procname_to_revng_arch = {
|
|
"x86_64": m.Architecture.x86_64,
|
|
"x86": m.Architecture.x86,
|
|
"arm": m.Architecture.arm,
|
|
"aarch64": m.Architecture.aarch64,
|
|
"mips": m.Architecture.mips,
|
|
"mipsb": m.Architecture.mips,
|
|
"mipsl": m.Architecture.mipsel,
|
|
"s390x": m.Architecture.systemz,
|
|
}
|
|
|
|
revng_arch_to_metaaddr_code_type = {
|
|
m.Architecture.x86: MetaAddressType.Code_x86,
|
|
m.Architecture.x86_64: MetaAddressType.Code_x86_64,
|
|
m.Architecture.arm: MetaAddressType.Code_arm,
|
|
m.Architecture.aarch64: MetaAddressType.Code_aarch64,
|
|
m.Architecture.mips: MetaAddressType.Code_mips,
|
|
m.Architecture.mipsel: MetaAddressType.Code_mipsel,
|
|
m.Architecture.systemz: MetaAddressType.Code_systemz,
|
|
}
|
|
|
|
revng_arch_to_abi = {
|
|
m.Architecture.arm: m.ABI.AAPCS,
|
|
m.Architecture.aarch64: m.ABI.AAPCS64,
|
|
m.Architecture.x86: m.ABI.SystemV_x86,
|
|
m.Architecture.x86_64: m.ABI.SystemV_x86_64,
|
|
m.Architecture.mips: m.ABI.SystemV_MIPS_o32,
|
|
m.Architecture.mipsel: m.ABI.SystemV_MIPSEL_o32,
|
|
m.Architecture.systemz: m.ABI.SystemZ_s390x,
|
|
}
|
|
|
|
revng_arch_to_abiname = {
|
|
m.Architecture.arm: "AAPCS",
|
|
m.Architecture.aarch64: "AAPCS64",
|
|
m.Architecture.x86: "SystemV_x86",
|
|
m.Architecture.x86_64: "SystemV_x86_64",
|
|
m.Architecture.mips: "SystemV_MIPS_o32",
|
|
m.Architecture.mipsel: "SystemV_MIPSEL_o32",
|
|
m.Architecture.systemz: "SystemZ_s390x",
|
|
}
|
|
|
|
|
|
def get_pointer_size(architecture):
|
|
match architecture: # noqa: R503
|
|
case m.Architecture.x86 | m.Architecture.arm | m.Architecture.mips | m.Architecture.mipsel:
|
|
return 4
|
|
case m.Architecture.x86_64 | m.Architecture.aarch64 | m.Architecture.systemz:
|
|
return 8
|
|
case default: # noqa: F841
|
|
return None
|
|
|
|
|
|
class IDBConverter:
|
|
def __init__(self, input_idb: idb.fileformat.IDB, base_addr, verbose):
|
|
self.idb: idb.fileformat.IDB = input_idb
|
|
self.api = idb.IDAPython(self.idb)
|
|
|
|
self.arch: m.Architecture = self._get_arch()
|
|
self.is64bit: bool = self._is_64_bit()
|
|
self.segments: List[m.Segment] = []
|
|
self.revng_types_by_id: Dict[int, RevngTypeDefinitions] = {}
|
|
self.idb_types_to_revng_types: Dict[int, m.Type] = {}
|
|
self.functions: Set[m.Function] = set()
|
|
self.dynamic_functions: List[m.DynamicFunction] = []
|
|
self.imported_libraries: List[str] = []
|
|
self.base_addr = base_addr
|
|
self.verbose = verbose
|
|
|
|
self._structs_to_fixup: Set[Tuple[m.StructDefinition, idb.typeinf.TInfo]] = set()
|
|
self._ordinal_types_to_fixup: Set[Tuple[m.Type, int]] = set()
|
|
self._unions_to_fixup: Set[Tuple[m.UnionDefinition, idb.typeinf.TInfo]] = set()
|
|
|
|
self._import_types()
|
|
self._fixup_structs()
|
|
self._fixup_unions()
|
|
self._fixup_ordinal_types()
|
|
self._import_functions()
|
|
self._collect_imports()
|
|
|
|
def log(self, message):
|
|
if self.verbose:
|
|
sys.stderr.write(message + "\n")
|
|
|
|
def _import_types(self):
|
|
"""Imports initial types from the IDB. The types will be incomplete and need to be fixed"""
|
|
til = self.idb.til
|
|
for type_definition in til.types.defs:
|
|
assert isinstance(type_definition, idb.typeinf.TILTypeInfo)
|
|
the_type = type_definition.type
|
|
assert isinstance(the_type, idb.typeinf.TInfo)
|
|
self._convert_idb_type_to_revng_type(the_type, ordinal=type_definition.ordinal)
|
|
|
|
def _import_functions(self):
|
|
metaaddr_type = revng_arch_to_metaaddr_code_type[self.arch]
|
|
if self.arch == m.Architecture.arm and self.api.idc.ItemSize(0x0) == 2:
|
|
# If the instructions are 16-bit long, it is a Thumb mode.
|
|
metaaddr_type = MetaAddressType.Code_arm_thumb
|
|
|
|
for function_start_addr in self.api.idautils.Functions():
|
|
function = idb.analysis.Function(self.idb, function_start_addr)
|
|
function_name = function.get_name()
|
|
|
|
idb_function_type = function.get_signature()
|
|
|
|
function_attributes: List[m.FunctionAttribute] = []
|
|
|
|
if self.api.ida_nalt.is_noret(function_start_addr):
|
|
function_attributes.insert(0, m.FunctionAttribute.NoReturn)
|
|
|
|
function_start_addr += self.base_addr
|
|
|
|
if idb_function_type is not None:
|
|
prototype = self._convert_idb_type_to_revng_type(idb_function_type)
|
|
revng_function = m.Function(
|
|
Name=function_name,
|
|
Entry=m.MetaAddress(Address=function_start_addr, Type=metaaddr_type),
|
|
Attributes=function_attributes,
|
|
Prototype=prototype,
|
|
)
|
|
else:
|
|
self.log(f"warning: Function {function_name} without a signature.")
|
|
revng_function = m.Function(
|
|
Name=function_name,
|
|
Entry=m.MetaAddress(Address=function_start_addr, Type=metaaddr_type),
|
|
Attributes=function_attributes,
|
|
)
|
|
|
|
self.functions.add(revng_function)
|
|
|
|
def _get_arch(self):
|
|
inf_structure = self.api.idaapi.get_inf_structure()
|
|
procname = inf_structure.procname.lower()
|
|
|
|
if procname == "arm" and inf_structure.is_64bit():
|
|
procname = "aarch64"
|
|
elif procname == "metapc" and inf_structure.is_64bit():
|
|
procname = "x86_64"
|
|
elif procname == "metapc" and inf_structure.is_32bit():
|
|
procname = "x86"
|
|
|
|
return idb_procname_to_revng_arch[procname]
|
|
|
|
def _is_64_bit(self):
|
|
inf_structure = self.api.idaapi.get_inf_structure()
|
|
return inf_structure.is_64bit()
|
|
|
|
def _import_names_helper(self, function_addr, function_name):
|
|
function = idb.analysis.Function(self.idb, function_addr)
|
|
if not function:
|
|
log_error(f"Unable to find function {function_name}.")
|
|
return True
|
|
function_name = function.get_name()
|
|
function_type = None
|
|
try:
|
|
function_type = function.get_signature()
|
|
except Exception as exception:
|
|
log_error(f"Unable to parse function type for {function_name}.")
|
|
log_error(str(exception))
|
|
function_type = None
|
|
return True
|
|
if function_type is not None:
|
|
prototype = self._convert_idb_type_to_revng_type(function_type)
|
|
else:
|
|
prototype_definition = m.CABIFunctionDefinition(
|
|
ABI=revng_arch_to_abiname[self.arch],
|
|
Arguments=[],
|
|
)
|
|
self.revng_types_by_id[prototype_definition.ID] = prototype_definition
|
|
prototype = self._type_for_definition(prototype_definition)
|
|
dynamic_function = m.DynamicFunction(
|
|
Name=function_name,
|
|
Prototype=prototype,
|
|
)
|
|
self.dynamic_functions.append(dynamic_function)
|
|
return True
|
|
|
|
def _collect_imports(self):
|
|
# NOTE: If @plt was used, the functions are in Functions() already, but the name starts
|
|
# with ".", and we do import types for them during parsing of regular/local functions.
|
|
|
|
# NOTE: We cannot use api.ida_nalt.get_import_module_qty() here, since it relies
|
|
# on `library names`, but IDA prints `.dynsym` as library for each library in case of ELF.
|
|
|
|
for mod_index in range(self.api.ida_nalt.get_import_module_qty() + 1):
|
|
|
|
def import_names_callback(function_addr, function_name, ignored_always_none):
|
|
assert ignored_always_none is None
|
|
return self._import_names_helper(function_addr, function_name)
|
|
|
|
try:
|
|
mod_name = self.api.ida_nalt.get_import_module_name(mod_index)
|
|
except KeyError as exception:
|
|
# TODO: Due to the bug mentioned below, we try sometimes to
|
|
# find imported modules that do not exist.
|
|
if mod_index != 0:
|
|
# It would be very strange if the mod_index is not 0.
|
|
log_error(f"Unable to find module with index: {mod_index}.")
|
|
log_error(str(exception))
|
|
continue
|
|
# NOTE: IDA 7 has a bug when reporting imported library names - prints `.dynsym` for
|
|
# each import.
|
|
if mod_name and mod_name != ".dynsym":
|
|
self.imported_libraries.append(mod_name)
|
|
self.api.ida_nalt.enum_import_names(mod_index, import_names_callback)
|
|
|
|
def _fixup_ordinal_types(self):
|
|
"""Fixes some types that were referring an ordinal that was not observed yet."""
|
|
while self._ordinal_types_to_fixup:
|
|
placeholder_type, idb_ordinal_type = self._ordinal_types_to_fixup.pop()
|
|
assert idb_ordinal_type in self.idb_types_to_revng_types
|
|
real_type = self.idb_types_to_revng_types[idb_ordinal_type]
|
|
|
|
placeholder_definition = self.unwrap_definition(placeholder_type)
|
|
real_definition = self.unwrap_definition(real_type)
|
|
# TODO: Until now, we only ever saw this affecting structs.
|
|
assert isinstance(placeholder_definition, m.StructDefinition) and isinstance(
|
|
real_definition, m.StructDefinition
|
|
)
|
|
|
|
placeholder_definition.Fields = real_definition.Fields
|
|
placeholder_definition.Name = real_definition.Name
|
|
placeholder_definition.Size = real_definition.Size
|
|
|
|
def _fixup_structs(self):
|
|
while self._structs_to_fixup:
|
|
revng_type, idb_type = self._structs_to_fixup.pop()
|
|
|
|
assert isinstance(revng_type, m.StructDefinition)
|
|
assert idb_type.is_decl_struct()
|
|
|
|
fields = []
|
|
committed_size = 0
|
|
|
|
# TODO: For now, we are ignoring structs that contain bitfields.
|
|
# We should also improve the python-idb package to pickup the struct size directly from
|
|
# the IDB files (e.g. to populate the `type.type_details.storage_size` for structs).
|
|
for member in idb_type.type_details.members:
|
|
if member.type.is_decl_bitfield():
|
|
self.log(
|
|
f"warning: Ignoring {revng_type.Name} struct that contains a " "bitfield."
|
|
)
|
|
return
|
|
|
|
for member in idb_type.type_details.members:
|
|
underlying_type = self._convert_idb_type_to_revng_type(member.type)
|
|
revng_member = m.StructField(
|
|
Name=member.name,
|
|
Type=underlying_type,
|
|
Offset=committed_size,
|
|
)
|
|
member_size = member.type.get_size()
|
|
if member_size == 0:
|
|
self.log(
|
|
f"warning: Dropping zero-sized field {member.name} of struct "
|
|
f"{revng_type.Name}."
|
|
)
|
|
else:
|
|
fields.append(revng_member)
|
|
committed_size += member_size
|
|
|
|
revng_type.Fields = fields
|
|
revng_type.Size = committed_size
|
|
|
|
def _fixup_unions(self):
|
|
while self._unions_to_fixup:
|
|
revng_type, idb_type = self._unions_to_fixup.pop()
|
|
|
|
assert isinstance(revng_type, m.UnionDefinition)
|
|
assert idb_type.is_decl_union()
|
|
|
|
fields = []
|
|
for idx, member in enumerate(idb_type.type_details.members):
|
|
qualified_type = self._convert_idb_type_to_revng_type(member.type)
|
|
revng_member = m.UnionField(
|
|
Name=member.name,
|
|
Type=qualified_type,
|
|
Index=idx,
|
|
)
|
|
fields.append(revng_member)
|
|
|
|
revng_type.Fields = fields
|
|
|
|
def _convert_idb_type_to_revng_type(
|
|
self,
|
|
type: idb.typeinf.TInfo, # noqa: A002
|
|
ordinal=None,
|
|
) -> m.Type:
|
|
"""
|
|
Converts the given TInfo obtained from python-idb to the corresponding revng type.
|
|
If available, the integer identifying the type in the IDB (ordinal) should be supplied
|
|
to allow handling circular references. If a type with the given ordinal was already
|
|
converted the same instance is returned.
|
|
"""
|
|
assert isinstance(type, idb.typeinf.TInfo)
|
|
|
|
# Check if we already converted this type, and if so return the existing type.
|
|
# Fundamental to handle circular dependencies.
|
|
existing_revng_type = self.idb_types_to_revng_types.get(ordinal)
|
|
if existing_revng_type is not None:
|
|
return existing_revng_type
|
|
|
|
type_name = type.get_name()
|
|
|
|
resulting_definition = None
|
|
if type.is_decl_typedef():
|
|
aliased_type = type.get_final_tinfo()
|
|
|
|
# IDA's types could be generated/identified in two ways: by ordinal and by names,
|
|
# so we handle both ways here.
|
|
aliased_type_ordinal = None
|
|
if type.type_details.is_ordref:
|
|
aliased_type_ordinal = type.type_details.ordinal
|
|
else:
|
|
aliased_tiltypeinfo = type.til.types.find_by_name(aliased_type.name)
|
|
if aliased_tiltypeinfo:
|
|
aliased_type_ordinal = aliased_tiltypeinfo.ordinal
|
|
|
|
underlying = self._convert_idb_type_to_revng_type(
|
|
aliased_type, ordinal=aliased_type_ordinal
|
|
)
|
|
|
|
# IDA uses c-like type system in that for each struct/union a corresponding typedef
|
|
# is introduced. In our model type system, such typedefs are already included
|
|
# in the struct/union definitions (we emit them automatically for each aggregate, with
|
|
# no need for an explicit typedef - de facto, always introducing their names into
|
|
# the global namespace).
|
|
#
|
|
# As such, when importing from IDA, unless a typedef carries any extra information,
|
|
# there's no point of importing it (it's harmful, actually, as it introduces multiple
|
|
# types sharing the same name) - so we just skip it here.
|
|
|
|
if (
|
|
aliased_type.get_name() == type_name
|
|
and type.is_decl_const() == aliased_type.is_decl_const()
|
|
and isinstance(underlying, m.DefinedType)
|
|
):
|
|
# skip unnecessary typedefs
|
|
resulting_definition = self.unwrap_definition(underlying)
|
|
else:
|
|
resulting_definition = m.TypedefDefinition(
|
|
Name=type_name, UnderlyingType=underlying
|
|
)
|
|
|
|
elif type.is_decl_enum():
|
|
underlying = m.PrimitiveType(
|
|
PrimitiveKind=m.PrimitiveKind.Unsigned, Size=type.type_details.storage_size
|
|
)
|
|
entries = []
|
|
for member in type.type_details.members:
|
|
if member.value >= 2 ** (underlying.Size * 8):
|
|
self.log(
|
|
f"warning: Value {hex(member.value)} for enum member "
|
|
f"{type_name}.{member.name} out of range, ignoring it."
|
|
)
|
|
continue
|
|
# TODO: We should keep the user comment which might exist in member.cmt.
|
|
enum_entry = m.EnumEntry(
|
|
Name=member.name,
|
|
Value=member.value,
|
|
)
|
|
entries.append(enum_entry)
|
|
|
|
if len(entries) == 0:
|
|
self.log(f"warning: An empty enum type: {type_name}, emitting a typedef instead.")
|
|
resulting_definition = m.TypedefDefinition(
|
|
Name=type_name, UnderlyingType=underlying
|
|
)
|
|
else:
|
|
resulting_definition = m.EnumDefinition(
|
|
Name=type_name,
|
|
Entries=entries,
|
|
UnderlyingType=underlying,
|
|
)
|
|
|
|
elif type.is_decl_struct():
|
|
if type.type_details.ref is not None and type.type_details.ref.type_details.is_ordref:
|
|
if type.type_details.ref.type_details.ordinal not in self.idb_types_to_revng_types:
|
|
# This is just a forward declaration. Make a placeholder for now.
|
|
resulting_definition = m.StructDefinition(Name="", Size=0, Fields=[])
|
|
self.revng_types_by_id[resulting_definition.ID] = resulting_definition
|
|
|
|
wrapped = self._type_for_definition(resulting_definition, type.is_decl_const())
|
|
|
|
self._ordinal_types_to_fixup.add(
|
|
(wrapped, type.type_details.ref.type_details.ordinal)
|
|
)
|
|
|
|
return wrapped
|
|
else:
|
|
return self.idb_types_to_revng_types[type.type_details.ref.type_details.ordinal]
|
|
else:
|
|
# Empty structs are not valid in the revng type system.
|
|
if len(type.type_details.members) == 0:
|
|
self.log(f"warning: Ignoring empty struct {type_name}, typedef it to `void`")
|
|
typedef = m.TypedefDefinition(
|
|
Name=type_name,
|
|
UnderlyingType=m.PrimitiveType(m.PrimitiveKind.Void, 0),
|
|
)
|
|
self.revng_types_by_id[typedef.ID] = typedef
|
|
return self._type_for_definition(typedef, type.is_decl_const())
|
|
else:
|
|
# Struct members and size will be computed later.
|
|
resulting_definition = m.StructDefinition(Name=type_name, Size=0, Fields=[])
|
|
self._structs_to_fixup.add((resulting_definition, type))
|
|
|
|
elif type.is_decl_union():
|
|
if type.type_details.ref is not None and type.type_details.ref.type_details.is_ordref:
|
|
if type.type_details.ref.type_details.ordinal not in self.idb_types_to_revng_types:
|
|
# This is just a forward declaration. Make a placeholder for now.
|
|
resulting_definition = m.UnionDefinition(Name="", Fields=[])
|
|
self.revng_types_by_id[resulting_definition.ID] = resulting_definition
|
|
|
|
wrapped = self._type_for_definition(resulting_definition, type.is_decl_const())
|
|
self._ordinal_types_to_fixup.add(
|
|
(wrapped, type.type_details.ref.type_details.ordinal)
|
|
)
|
|
|
|
return wrapped
|
|
else:
|
|
return self.idb_types_to_revng_types[type.type_details.ref.type_details.ordinal]
|
|
else:
|
|
# Empty unions are not valid in the revng type system.
|
|
if len(type.type_details.members) == 0:
|
|
self.log(f"warning: Ignoring empty union {type_name}, typedef it to `void`")
|
|
typedef = m.TypedefDefinition(
|
|
Name=type_name,
|
|
UnderlyingType=m.PrimitiveType(m.PrimitiveKind.Void, 0),
|
|
)
|
|
self.revng_types_by_id[typedef.ID] = typedef
|
|
return self._type_for_definition(typedef, type.is_decl_const())
|
|
else:
|
|
# Union members will be computed later.
|
|
resulting_definition = m.UnionDefinition(Name=type_name, Fields=[])
|
|
self._unions_to_fixup.add((resulting_definition, type))
|
|
|
|
elif type.is_decl_ptr():
|
|
underlying_type = self._convert_idb_type_to_revng_type(type.type_details.obj_type)
|
|
result = m.PointerType(
|
|
PointeeType=underlying_type,
|
|
PointerSize=type.get_size(),
|
|
IsConst=type.is_decl_const(),
|
|
)
|
|
if ordinal is not None:
|
|
self.idb_types_to_revng_types[ordinal] = result
|
|
return result
|
|
|
|
elif type.is_decl_array():
|
|
if type.type_details.n_elems == 0:
|
|
self.log(f"warning: Array {type_name} has invalid zero size.")
|
|
|
|
underlying_type = self._convert_idb_type_to_revng_type(type.type_details.obj_type)
|
|
result = m.ArrayType(
|
|
ElementType=underlying_type,
|
|
ElementCount=type.type_details.n_elems,
|
|
IsConst=type.is_decl_const(),
|
|
)
|
|
if type.is_decl_const():
|
|
result.IsConst = True
|
|
if ordinal is not None:
|
|
self.idb_types_to_revng_types[ordinal] = result
|
|
return result
|
|
|
|
elif type.is_decl_bool():
|
|
result = m.PrimitiveType(
|
|
PrimitiveKind=m.PrimitiveKind.Unsigned,
|
|
Size=type.get_size(),
|
|
IsConst=type.is_decl_const(),
|
|
)
|
|
if ordinal is not None:
|
|
self.idb_types_to_revng_types[ordinal] = result
|
|
return result
|
|
|
|
elif type.is_decl_int() or type.is_decl_floating():
|
|
result = m.PrimitiveType(
|
|
PrimitiveKind=get_primitive_kind(type),
|
|
Size=type.get_size(),
|
|
IsConst=type.is_decl_const(),
|
|
)
|
|
if ordinal is not None:
|
|
self.idb_types_to_revng_types[ordinal] = result
|
|
return result
|
|
|
|
elif type.is_decl_void():
|
|
result = m.PrimitiveType(
|
|
PrimitiveKind=m.PrimitiveKind.Void, Size=0, IsConst=type.is_decl_const()
|
|
)
|
|
|
|
if type.get_name() != "":
|
|
# Treat this as `typedef void someothername`.
|
|
result = m.TypedefDefinition(
|
|
Name=type_name, UnderlyingType=result, IsConst=type.is_decl_const()
|
|
)
|
|
|
|
if ordinal is not None:
|
|
self.idb_types_to_revng_types[ordinal] = result
|
|
return result
|
|
|
|
elif type.is_decl_func():
|
|
# TODO: handle non C-ABI functions.
|
|
|
|
# We cannot handle stack arguments at the moment.
|
|
assert type.type_details.stkargs is None
|
|
|
|
arguments = []
|
|
for idx, argument in enumerate(type.type_details.args):
|
|
arguments.append(
|
|
m.Argument(
|
|
Index=idx,
|
|
Type=self._convert_idb_type_to_revng_type(argument.type),
|
|
Name=argument.name,
|
|
)
|
|
)
|
|
|
|
rt = self._convert_idb_type_to_revng_type(type.get_rettype())
|
|
if isinstance(rt, m.PrimitiveType) and rt.PrimitiveKind == m.PrimitiveKind.Void:
|
|
rt = None
|
|
|
|
resulting_definition = m.CABIFunctionDefinition(
|
|
ABI=revng_arch_to_abi[self.arch],
|
|
ReturnType=rt,
|
|
Arguments=arguments,
|
|
)
|
|
|
|
elif type.is_decl_partial():
|
|
# Represents an unknown or void type with a known size.
|
|
assert type.get_size() != 0
|
|
# The type should be compatible with being a primitive type.
|
|
# NOTE: If we find a case where this is not satisfied, we can produce a char[].
|
|
assert (
|
|
type.get_size() == 1
|
|
or type.get_size() == 2
|
|
or type.get_size() == 4
|
|
or type.get_size() == 8
|
|
or type.get_size() == 10
|
|
or type.get_size() == 16
|
|
)
|
|
|
|
result = m.PrimitiveType(
|
|
PrimitiveKind=m.PrimitiveKind.Generic,
|
|
Size=type.get_size(),
|
|
IsConst=type.is_decl_const(),
|
|
)
|
|
if ordinal is not None:
|
|
self.idb_types_to_revng_types[ordinal] = result
|
|
return result
|
|
|
|
else:
|
|
# IDA does not know anything about this type.
|
|
# TODO: In some cases we should emit a void type (e.g. when the type is only ever used
|
|
# as a pointer).
|
|
if type.get_size() == 0:
|
|
result = m.PrimitiveType(
|
|
PrimitiveKind=m.PrimitiveKind.Void, Size=0, IsConst=type.is_decl_const()
|
|
)
|
|
if ordinal is not None:
|
|
self.idb_types_to_revng_types[ordinal] = result
|
|
return result
|
|
else:
|
|
result = m.PrimitiveType(
|
|
PrimitiveKind=m.PrimitiveKind.PointerOrNumber,
|
|
Size=type.get_size(),
|
|
IsConst=type.is_decl_const(),
|
|
)
|
|
if ordinal is not None:
|
|
self.idb_types_to_revng_types[ordinal] = result
|
|
return result
|
|
|
|
assert resulting_definition is not None
|
|
|
|
result = self._type_for_definition(resulting_definition, type.is_decl_const())
|
|
if ordinal is not None:
|
|
self.idb_types_to_revng_types[ordinal] = result
|
|
|
|
if not self.revng_types_by_id.get(resulting_definition.ID):
|
|
self.revng_types_by_id[resulting_definition.ID] = resulting_definition
|
|
|
|
return result
|
|
|
|
def _type_for_definition(self, definition: m.TypeDefinition, is_const: bool = False) -> m.Type:
|
|
return m.DefinedType(Definition=m.Reference.create(m.Binary, definition), IsConst=is_const)
|
|
|
|
def get_model(self) -> m.Binary:
|
|
return m.Binary(
|
|
# NOTE: We assume that the EntryPoint can be obtained from binary itself, so we use an
|
|
# invalid address for it here.
|
|
EntryPoint=m.MetaAddress(Address=0x0, Type=MetaAddressType.Invalid),
|
|
Functions=list(self.functions),
|
|
ImportedDynamicFunctions=list(self.dynamic_functions),
|
|
TypeDefinitions=list(self.revng_types_by_id.values()),
|
|
Architecture=self.arch,
|
|
Segments=self.segments,
|
|
ImportedLibraries=self.imported_libraries,
|
|
)
|
|
|
|
def get_revng_type_by_name(self, name):
|
|
for revng_type in self.revng_types_by_id.values():
|
|
if revng_type.Name == name:
|
|
return revng_type
|
|
return None
|
|
|
|
def unwrap_definition(self, defined_type: m.Type) -> Optional[RevngTypeDefinitions]:
|
|
if not isinstance(defined_type, m.DefinedType):
|
|
raise ValueError("Trying to unwrap a nested definition:\n" + str(defined_type))
|
|
|
|
return self.revng_types_by_id.get(defined_type.Definition.id)
|
|
|
|
|
|
def get_primitive_kind(idb_type: idb.typeinf.TInfo) -> m.PrimitiveKind:
|
|
if idb_type.is_decl_void():
|
|
return m.PrimitiveKind.Void
|
|
elif idb_type.is_decl_floating():
|
|
return m.PrimitiveKind.Float
|
|
elif idb.typeinf_flags.is_type_integral(idb_type.get_decltype()):
|
|
if idb_type.is_signed():
|
|
return m.PrimitiveKind.Signed
|
|
elif idb_type.is_unsigned():
|
|
return m.PrimitiveKind.Unsigned
|
|
else:
|
|
return m.PrimitiveKind.Number
|
|
elif idb_type.is_decl_ptr():
|
|
return m.PrimitiveKind.PointerOrNumber
|
|
|
|
return m.PrimitiveKind.Generic
|