Source code for binaryninja.platform
# Copyright (c) 2015-2025 Vector 35 Inc
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
import os
import ctypes
import traceback
import warnings
from typing import List, Dict, Optional, Tuple
# Binary Ninja components
import binaryninja
from . import _binaryninjacore as core
from . import types
from . import typeparser
from . import callingconvention
from . import typelibrary
from . import architecture
from . import typecontainer
from . import binaryview
from .log import log_error
class _PlatformMetaClass(type):
def __iter__(self):
binaryninja._init_plugins()
count = ctypes.c_ulonglong()
platforms = core.BNGetPlatformList(count)
assert platforms is not None, "core.BNGetPlatformList returned None"
try:
for i in range(0, count.value):
yield CorePlatform(handle=core.BNNewPlatformReference(platforms[i]))
finally:
core.BNFreePlatformList(platforms, count.value)
def __getitem__(cls, value):
binaryninja._init_plugins()
platform = core.BNGetPlatformByName(str(value))
if platform is None:
raise KeyError("'%s' is not a valid platform" % str(value))
return CorePlatform(handle=platform)
[docs]
class Platform(metaclass=_PlatformMetaClass):
"""
``class Platform`` contains all information related to the execution environment of the binary, mainly the
calling conventions used, the operating system, and the architecture.
The following example showing the live list of platforms may not match your list. Some platforms are included only
with specific versions of Binary Ninja and others are installed by plugins. The bare architecture version of the
platform does not include any default ABI/calling convention::
>>> list(Platform)
[<platform: Solana>, <platform: decree-x86>, <platform: efi-aarch64>, <platform: efi-windows-aarch64>,
<platform: efi-x86>, <platform: efi-windows-x86>, <platform: efi-x86_64>, <platform: efi-windows-x86_64>,
<platform: efi-armv7>, <platform: efi-thumb2>, <platform: freebsd-aarch64>, <platform: freebsd-x86>,
<platform: freebsd-x86_64>, <platform: freebsd-armv7>, <platform: freebsd-thumb2>, <platform: ios-aarch64>,
<platform: ios-armv7>, <platform: ios-thumb2>, <platform: ios-kernel-aarch64>, <platform: ios-kernel-armv7>,
<platform: ios-kernel-thumb2>, <platform: linux-aarch64>, <platform: linux-mcore_le>,
<platform: linux-mcore_be>, <platform: linux-csky_le_v1>, <platform: linux-csky_le>, <platform: linux-armv7eb>,
<platform: linux-thumb2eb>, <platform: linux-mips>, <platform: linux-mipsel>, <platform: linux-mips3>,
<platform: linux-mipsel3>, <platform: linux-mips64>, <platform: linux-cnmips64>, <platform: linux-ppc32>,
<platform: linux-ppc64>, <platform: linux-ppc32_le>, <platform: linux-ppc64_le>, <platform: linux-rv32gc>,
<platform: linux-rv64gc>, <platform: linux-x86>, <platform: linux-x86_64>, <platform: linux-armv7>,
<platform: linux-thumb2>, <platform: mac-aarch64>, <platform: mac-x86>, <platform: mac-x86_64>,
<platform: mac-armv7>, <platform: mac-thumb2>, <platform: mac-kernel-aarch64>, <platform: mac-kernel-x86>,
<platform: mac-kernel-x86_64>, <platform: mac-kernel-armv7>, <platform: mac-kernel-thumb2>,
<platform: vxworks-aarch64>, <platform: vxworks-mips32>, <platform: vxworks-mipsel32>,
<platform: vxworks-mips64>, <platform: vxworks-cavium-mips64>, <platform: vxworks-ppc32>,
<platform: vxworks-ppc64>, <platform: vxworks-rv32gc>, <platform: vxworks-rv64gc>, <platform: vxworks-x86>,
<platform: vxworks-x86_64>, <platform: vxworks-armv7>, <platform: vxworks-thumb2>, <platform: windows-aarch64>,
<platform: windows-x86>, <platform: windows-x86_64>, <platform: windows-armv7>, <platform: windows-thumb2>,
<platform: windows-kernel-windows-aarch64>, <platform: windows-kernel-x86>, <platform: windows-kernel-x86_64>]
>>> thumb2 = Platform["thumb2"]
"""
name = None
type_file_path = None # path to platform types file
type_include_dirs = [] # list of directories available to #include from type_file_path
global_regs = [] # list of global registers. if empty, it populated with the arch global reg list
global_reg_types = {} # opportunity for plugin to provide default types for the entry value of global registers
_registered_platforms = []
def __init__(self, arch: Optional['architecture.Architecture'] = None, handle=None):
if handle is None:
if arch is None:
raise ValueError("platform must have an associated architecture")
assert self.__class__.name is not None, "Can not instantiate Platform directly, you probably want arch.standalone_platform"
_arch = arch
if len(self.global_regs) == 0:
self.__dict__["global_regs"] = arch.global_regs
self._cb = core.BNCustomPlatform()
self._cb.context = 0
self._cb.init = self._cb.init.__class__(self._init)
self._cb.viewInit = self._cb.viewInit.__class__(self._view_init)
self._cb.getGlobalRegisters = self._cb.getGlobalRegisters.__class__(self._get_global_regs)
self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list)
self._cb.getGlobalRegisterType = self._cb.getGlobalRegisterType.__class__(self._get_global_reg_type)
self._cb.adjustTypeParserInput = self._cb.adjustTypeParserInput.__class__(self._adjust_type_parser_input)
self._cb.freeTypeParserInput = self._cb.freeTypeParserInput.__class__(self._free_type_parser_input)
self._pending_reg_lists = {}
self._pending_parser_input_lists = {}
if self.__class__.type_file_path is None:
_handle = core.BNCreateCustomPlatform(arch.handle, self.__class__.name, self._cb)
assert _handle is not None
else:
dir_buf = (ctypes.c_char_p * len(self.__class__.type_include_dirs))()
for (i, dir) in enumerate(self.__class__.type_include_dirs):
dir_buf[i] = dir.encode('charmap')
_handle = core.BNCreateCustomPlatformWithTypes(
arch.handle, self.__class__.name, self._cb, self.__class__.type_file_path, dir_buf,
len(self.__class__.type_include_dirs)
)
assert _handle is not None
self.__class__._registered_platforms.append(self)
else:
if type(self) is Platform:
binaryninja.log_warn(
":py:func:`Platform(handle=...)` is deprecated, use :py:func:`CorePlatform._from_cache(handle=...)`"
)
_handle = handle
_arch = architecture.CoreArchitecture._from_cache(core.BNGetPlatformArchitecture(_handle))
count = ctypes.c_ulonglong()
regs = core.BNGetPlatformGlobalRegisters(handle, count)
result = []
for i in range(0, count.value):
result.append(_arch.get_reg_name(regs[i]))
core.BNFreeRegisterList(regs)
self.__dict__["global_regs"] = result
assert _handle is not None
assert _arch is not None
self.handle: ctypes.POINTER(core.BNPlatform) = _handle
self._arch = _arch
self._name = None
def _init(self, ctxt):
pass
def _view_init(self, ctxt, view):
try:
view_obj = binaryview.BinaryView(handle=core.BNNewViewReference(view))
self.view_init(view)
except:
log_error(traceback.format_exc())
def _get_global_regs(self, ctxt, count):
try:
regs = self.global_regs
count[0] = len(regs)
reg_buf = (ctypes.c_uint * len(regs))()
for i in range(0, len(regs)):
reg_buf[i] = self.arch.regs[regs[i]].index
result = ctypes.cast(reg_buf, ctypes.c_void_p)
self._pending_reg_lists[result.value] = (result, reg_buf)
return result.value
except:
log_error(traceback.format_exc())
count[0] = 0
return None
def _free_register_list(self, ctxt, regs, size):
try:
buf = ctypes.cast(regs, ctypes.c_void_p)
if buf.value not in self._pending_reg_lists:
raise ValueError("freeing register list that wasn't allocated")
del self._pending_reg_lists[buf.value]
except:
log_error(traceback.format_exc())
def _get_global_reg_type(self, ctxt, reg):
try:
reg_name = self.arch.get_reg_name(reg)
if reg_name in self.global_reg_types:
type_obj = self.global_reg_types[reg_name]
handle = core.BNNewTypeReference(type_obj.handle)
return ctypes.cast(handle, ctypes.c_void_p).value
return None
except:
log_error(traceback.format_exc())
return None
def _adjust_type_parser_input(
self,
ctxt,
parser,
arguments_in,
arguments_len_in,
source_file_names_in,
source_file_values_in,
source_files_len_in,
arguments_out,
arguments_len_out,
source_file_names_out,
source_file_values_out,
source_files_len_out
):
try:
parser_py = typeparser.TypeParser(handle=parser)
arguments = []
for i in range(arguments_len_in):
arguments.append(core.pyNativeStr(arguments_in[i]))
source_files = []
for i in range(source_files_len_in):
source_files.append((core.pyNativeStr(source_file_names_in[i]), core.pyNativeStr(source_file_values_in[i])))
arguments, source_files = self.adjust_type_parser_input(parser_py, arguments, source_files)
arguments_len_out[0] = len(arguments)
arguments_buf = (ctypes.c_char_p * len(arguments))()
for i, r in enumerate(arguments):
arguments_buf[i] = core.cstr(r)
arguments_ptr = ctypes.cast(arguments_buf, ctypes.c_void_p)
arguments_out[0] = arguments_buf
self._pending_parser_input_lists[arguments_ptr.value] = (arguments_ptr.value, arguments_buf)
source_files_len_out[0] = len(source_files)
source_file_names_buf = (ctypes.c_char_p * len(source_files))()
source_file_values_buf = (ctypes.c_char_p * len(source_files))()
for i, (name, value) in enumerate(source_files):
source_file_names_buf[i] = core.cstr(name)
source_file_values_buf[i] = core.cstr(value)
source_file_names_ptr = ctypes.cast(source_file_names_buf, ctypes.c_void_p)
source_file_names_out[0] = source_file_names_buf
source_file_values_ptr = ctypes.cast(source_file_values_buf, ctypes.c_void_p)
source_file_values_out[0] = source_file_values_buf
self._pending_parser_input_lists[source_file_names_ptr.value] = (source_file_names_ptr.value, source_file_names_buf)
self._pending_parser_input_lists[source_file_values_ptr.value] = (source_file_values_ptr.value, source_file_values_buf)
except:
arguments_out[0] = None
arguments_len_out[0] = 0
source_file_names_out[0] = None
source_file_values_out[0] = None
source_files_len_out[0] = 0
traceback.print_exc()
def _free_type_parser_input(
self,
ctxt,
arguments,
arguments_len,
source_file_names,
source_file_values,
source_files_len
):
try:
buf = ctypes.cast(arguments, ctypes.c_void_p)
if buf.value is not None:
if buf.value not in self._pending_parser_input_lists:
raise ValueError("freeing arguments list that wasn't allocated")
del self._pending_parser_input_lists[buf.value]
buf = ctypes.cast(source_file_names, ctypes.c_void_p)
if buf.value is not None:
if buf.value not in self._pending_parser_input_lists:
raise ValueError("freeing source_file_names list that wasn't allocated")
del self._pending_parser_input_lists[buf.value]
buf = ctypes.cast(source_file_values, ctypes.c_void_p)
if buf.value is not None:
if buf.value not in self._pending_parser_input_lists:
raise ValueError("freeing source_file_values list that wasn't allocated")
del self._pending_parser_input_lists[buf.value]
except:
log_error(traceback.format_exc())
[docs]
def adjust_type_parser_input(
self,
parser: 'typeparser.TypeParser',
arguments: List[str],
source_files: List[Tuple[str, str]]
) -> Tuple[List[str], List[Tuple[str, str]]]:
"""
Modify the arguments passed to the Type Parser with Platform-specific features.
:param parser: Type Parser instance
:param arguments: Default arguments passed to the parser
:param source_files: Source file names and contents
:return: A tuple of (modified arguments, modified source files)
"""
return arguments, source_files
def __del__(self):
if core is not None:
core.BNFreePlatform(self.handle)
def __repr__(self):
return f"<platform: {self.name}>"
def __str__(self):
return self.name
def __eq__(self, other: 'Platform'):
if not isinstance(other, self.__class__):
return NotImplemented
return ctypes.addressof(self.handle.contents) == ctypes.addressof(other.handle.contents)
def __ne__(self, other: 'Platform'):
if not isinstance(other, self.__class__):
return NotImplemented
return not (self == other)
def __hash__(self):
return hash(ctypes.addressof(self.handle.contents))
@property
def name(self) -> str:
if self._name is None:
self._name = core.BNGetPlatformName(self.handle)
return self._name
@classmethod
@property
def os_list(cls) -> List[str]:
binaryninja._init_plugins()
count = ctypes.c_ulonglong()
platforms = core.BNGetPlatformOSList(count)
assert platforms is not None, "core.BNGetPlatformOSList returned None"
result:List[str] = []
for i in range(count.value):
result.append(str(platforms[i]))
core.BNFreePlatformOSList(platforms, count.value)
return result
[docs]
@classmethod
def get_list(cls, os=None, arch=None):
binaryninja._init_plugins()
count = ctypes.c_ulonglong()
if os is None:
platforms = core.BNGetPlatformList(count)
assert platforms is not None, "core.BNGetPlatformList returned None"
elif arch is None:
platforms = core.BNGetPlatformListByOS(os, count)
assert platforms is not None, "core.BNGetPlatformListByOS returned None"
else:
platforms = core.BNGetPlatformListByArchitecture(arch.handle, count)
assert platforms is not None, "core.BNGetPlatformListByArchitecture returned None"
result = []
for i in range(0, count.value):
result.append(CorePlatform._from_cache(core.BNNewPlatformReference(platforms[i])))
core.BNFreePlatformList(platforms, count.value)
return result
@property
def default_calling_convention(self):
"""
Default calling convention.
:getter: returns a CallingConvention object for the default calling convention.
:setter: sets the default calling convention
:type: CallingConvention
"""
result = core.BNGetPlatformDefaultCallingConvention(self.handle)
if result is None:
return None
return callingconvention.CallingConvention(handle=result)
@default_calling_convention.setter
def default_calling_convention(self, value):
core.BNRegisterPlatformDefaultCallingConvention(self.handle, value.handle)
@property
def cdecl_calling_convention(self):
"""
CallingConvention object for the cdecl calling convention
"""
result = core.BNGetPlatformCdeclCallingConvention(self.handle)
if result is None:
return None
return callingconvention.CallingConvention(handle=result)
@cdecl_calling_convention.setter
def cdecl_calling_convention(self, value):
"""
Sets the cdecl calling convention
"""
core.BNRegisterPlatformCdeclCallingConvention(self.handle, value.handle)
@property
def stdcall_calling_convention(self):
"""
CallingConvention object for the stdcall calling convention
"""
result = core.BNGetPlatformStdcallCallingConvention(self.handle)
if result is None:
return None
return callingconvention.CallingConvention(handle=result)
@stdcall_calling_convention.setter
def stdcall_calling_convention(self, value):
"""
Sets the stdcall calling convention
"""
core.BNRegisterPlatformStdcallCallingConvention(self.handle, value.handle)
@property
def fastcall_calling_convention(self):
"""
CallingConvention object for the fastcall calling convention
"""
result = core.BNGetPlatformFastcallCallingConvention(self.handle)
if result is None:
return None
return callingconvention.CallingConvention(handle=result)
@fastcall_calling_convention.setter
def fastcall_calling_convention(self, value):
"""
Sets the fastcall calling convention
"""
core.BNRegisterPlatformFastcallCallingConvention(self.handle, value.handle)
@property
def system_call_convention(self):
"""
CallingConvention object for the system call convention
"""
result = core.BNGetPlatformSystemCallConvention(self.handle)
if result is None:
return None
return callingconvention.CallingConvention(handle=result)
@system_call_convention.setter
def system_call_convention(self, value):
"""
Sets the system call convention
"""
core.BNSetPlatformSystemCallConvention(self.handle, value.handle)
@property
def calling_conventions(self):
"""
List of platform CallingConvention objects (read-only)
:getter: returns the list of supported CallingConvention objects
:type: list(CallingConvention)
"""
count = ctypes.c_ulonglong()
cc = core.BNGetPlatformCallingConventions(self.handle, count)
assert cc is not None, "core.BNGetPlatformCallingConventions returned None"
result = []
for i in range(0, count.value):
result.append(callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i])))
core.BNFreeCallingConventionList(cc, count.value)
return result
[docs]
def get_global_register_type(self, reg: 'architecture.RegisterType'):
reg = self.arch.get_reg_index(reg)
type_obj = core.BNGetPlatformGlobalRegisterType(self.handle, reg)
if type_obj is None:
return None
return types.Type.create(type_obj, platform=self)
#raise NotImplementedError
@property
def types(self):
"""List of platform-specific types (read-only)"""
count = ctypes.c_ulonglong(0)
type_list = core.BNGetPlatformTypes(self.handle, count)
assert type_list is not None, "core.BNGetPlatformTypes returned None"
result = {}
for i in range(0, count.value):
name = types.QualifiedName._from_core_struct(type_list[i].name)
result[name] = types.Type.create(core.BNNewTypeReference(type_list[i].type), platform=self)
core.BNFreeTypeAndNameList(type_list, count.value)
return result
@property
def variables(self):
"""List of platform-specific variable definitions (read-only)"""
count = ctypes.c_ulonglong(0)
type_list = core.BNGetPlatformVariables(self.handle, count)
assert type_list is not None, "core.BNGetPlatformVariables returned None"
result = {}
for i in range(0, count.value):
name = types.QualifiedName._from_core_struct(type_list[i].name)
result[name] = types.Type.create(core.BNNewTypeReference(type_list[i].type), platform=self)
core.BNFreeTypeAndNameList(type_list, count.value)
return result
@property
def functions(self):
"""List of platform-specific function definitions (read-only)"""
count = ctypes.c_ulonglong(0)
type_list = core.BNGetPlatformFunctions(self.handle, count)
assert type_list is not None, "core.BNGetPlatformFunctions returned None"
result = {}
for i in range(0, count.value):
name = types.QualifiedName._from_core_struct(type_list[i].name)
result[name] = types.Type.create(core.BNNewTypeReference(type_list[i].type), platform=self)
core.BNFreeTypeAndNameList(type_list, count.value)
return result
@property
def system_calls(self):
"""List of system calls for this platform (read-only)"""
count = ctypes.c_ulonglong(0)
call_list = core.BNGetPlatformSystemCalls(self.handle, count)
assert call_list is not None, "core.BNGetPlatformSystemCalls returned None"
result = {}
for i in range(0, count.value):
name = types.QualifiedName._from_core_struct(call_list[i].name)
t = types.Type.create(core.BNNewTypeReference(call_list[i].type), platform=self)
result[call_list[i].number] = (name, t)
core.BNFreeSystemCallList(call_list, count.value)
return result
@property
def type_libraries(self) -> List['typelibrary.TypeLibrary']:
count = ctypes.c_ulonglong(0)
libs = core.BNGetPlatformTypeLibraries(self.handle, count)
assert libs is not None, "core.BNGetPlatformTypeLibraries returned None"
result = []
for i in range(0, count.value):
result.append(typelibrary.TypeLibrary(core.BNNewTypeLibraryReference(libs[i])))
core.BNFreeTypeLibraryList(libs, count.value)
return result
[docs]
def get_type_libraries_by_name(self, name) -> List['typelibrary.TypeLibrary']:
count = ctypes.c_ulonglong(0)
libs = core.BNGetPlatformTypeLibrariesByName(self.handle, name, count)
assert libs is not None, "core.BNGetPlatformTypeLibrariesByName returned None"
result = []
for i in range(0, count.value):
result.append(typelibrary.TypeLibrary(core.BNNewTypeLibraryReference(libs[i])))
core.BNFreeTypeLibraryList(libs, count.value)
return result
[docs]
def register(self, os):
"""
``register`` registers the platform for given OS name.
:param str os: OS name to register
:rtype: None
"""
core.BNRegisterPlatform(os, self.handle)
[docs]
def register_calling_convention(self, cc):
"""
``register_calling_convention`` register a new calling convention.
:param CallingConvention cc: a CallingConvention object to register
:rtype: None
"""
core.BNRegisterPlatformCallingConvention(self.handle, cc.handle)
[docs]
def get_associated_platform_by_address(self, addr):
new_addr = ctypes.c_ulonglong()
new_addr.value = addr
result = core.BNGetAssociatedPlatformByAddress(self.handle, new_addr)
return CorePlatform._from_cache(result), new_addr.value
@property
def type_container(self) -> 'typecontainer.TypeContainer':
"""
Type Container for all registered types in the Platform.
:return: Platform types Type Container
"""
return typecontainer.TypeContainer(core.BNGetPlatformTypeContainer(self.handle))
[docs]
def get_type_by_name(self, name):
name = types.QualifiedName(name)._to_core_struct()
obj = core.BNGetPlatformTypeByName(self.handle, name)
if not obj:
return None
return types.Type.create(obj, platform=self)
[docs]
def get_variable_by_name(self, name):
name = types.QualifiedName(name)._to_core_struct()
obj = core.BNGetPlatformVariableByName(self.handle, name)
if not obj:
return None
return types.Type.create(obj, platform=self)
[docs]
def get_function_by_name(self, name, exactMatch=False):
name = types.QualifiedName(name)._to_core_struct()
obj = core.BNGetPlatformFunctionByName(self.handle, name, exactMatch)
if not obj:
return None
return types.Type.create(obj, platform=self)
[docs]
def get_system_call_name(self, number):
return core.BNGetPlatformSystemCallName(self.handle, number)
[docs]
def get_system_call_type(self, number):
obj = core.BNGetPlatformSystemCallType(self.handle, number)
if not obj:
return None
return types.Type.create(obj, platform=self)
[docs]
def generate_auto_platform_type_id(self, name):
name = types.QualifiedName(name)._to_core_struct()
return core.BNGenerateAutoPlatformTypeId(self.handle, name)
[docs]
def generate_auto_platform_type_ref(self, type_class, name):
type_id = self.generate_auto_platform_type_id(name)
return types.NamedTypeReferenceBuilder.create(type_class, type_id, name)
[docs]
def get_auto_platform_type_id_source(self):
return core.BNGetAutoPlatformTypeIdSource(self.handle)
[docs]
def parse_types_from_source(
self, source, filename=None, include_dirs: Optional[List[str]] = None, auto_type_source=None
):
"""
``parse_types_from_source`` parses the source string and any needed headers searching for them in
the optional list of directories provided in ``include_dirs``. Note that this API does not allow
the source to rely on existing types that only exist in a specific view. Use :py:meth:`BinaryView.parse_type_string` instead.
:param str source: source string to be parsed
:param str filename: optional source filename
:param include_dirs: optional list of string filename include directories
:type include_dirs: list(str)
:param str auto_type_source: optional source of types if used for automatically generated types
:return: :py:class:`BasicTypeParserResult` (a SyntaxError is thrown on parse error)
:rtype: BasicTypeParserResult
:Example:
>>> platform.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n')
({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar':
<type: int32_t(int32_t x)>}}, '')
>>>
"""
if filename is None:
filename = "input"
if not isinstance(source, str):
raise AttributeError("Source must be a string")
if include_dirs is None:
include_dirs = []
dir_buf = (ctypes.c_char_p * len(include_dirs))()
for i in range(0, len(include_dirs)):
dir_buf[i] = include_dirs[i].encode('charmap')
parse = core.BNTypeParserResult()
errors = ctypes.c_char_p()
result = core.BNParseTypesFromSource(
self.handle, source, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source
)
assert errors.value is not None, "core.BNParseTypesFromSource returned errors set to None"
error_str = errors.value.decode("utf-8")
core.free_string(errors)
if not result:
raise SyntaxError(error_str)
type_dict: Dict[types.QualifiedName, types.Type] = {}
variables: Dict[types.QualifiedName, types.Type] = {}
functions: Dict[types.QualifiedName, types.Type] = {}
for i in range(0, parse.typeCount):
name = types.QualifiedName._from_core_struct(parse.types[i].name)
type_dict[name] = types.Type.create(core.BNNewTypeReference(parse.types[i].type), platform=self)
for i in range(0, parse.variableCount):
name = types.QualifiedName._from_core_struct(parse.variables[i].name)
variables[name] = types.Type.create(core.BNNewTypeReference(parse.variables[i].type), platform=self)
for i in range(0, parse.functionCount):
name = types.QualifiedName._from_core_struct(parse.functions[i].name)
functions[name] = types.Type.create(core.BNNewTypeReference(parse.functions[i].type), platform=self)
core.BNFreeTypeParserResult(parse)
return typeparser.BasicTypeParserResult(type_dict, variables, functions)
[docs]
def parse_types_from_source_file(self, filename, include_dirs: Optional[List[str]] = None, auto_type_source=None):
"""
``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in
the optional list of directories provided in ``include_dirs``. Note that this API does not allow
the source to rely on existing types that only exist in a specific view. Use :py:meth:`BinaryView.parse_type_string` instead.
:param str filename: filename of file to be parsed
:param include_dirs: optional list of string filename include directories
:type include_dirs: list(str)
:param str auto_type_source: optional source of types if used for automatically generated types
:return: :py:class:`BasicTypeParserResult` (a SyntaxError is thrown on parse error)
:rtype: BasicTypeParserResult
:Example:
>>> file = "/Users/binja/tmp.c"
>>> open(file).read()
'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n'
>>> platform.parse_types_from_source_file(file)
({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:
{'bar': <type: int32_t(int32_t x)>}}, '')
>>>
"""
if not (isinstance(filename, str) and os.path.isfile(filename) and os.access(filename, os.R_OK)):
raise AttributeError("File {} doesn't exist or isn't readable".format(filename))
if include_dirs is None:
include_dirs = []
dir_buf = (ctypes.c_char_p * len(include_dirs))()
for i in range(0, len(include_dirs)):
dir_buf[i] = include_dirs[i].encode('charmap')
parse = core.BNTypeParserResult()
errors = ctypes.c_char_p()
result = core.BNParseTypesFromSourceFile(
self.handle, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source
)
assert errors.value is not None, "core.BNParseTypesFromSourceFile returned errors set to None"
error_str = errors.value.decode("utf-8")
core.free_string(errors)
if not result:
raise SyntaxError(error_str)
type_dict = {}
variables = {}
functions = {}
for i in range(0, parse.typeCount):
name = types.QualifiedName._from_core_struct(parse.types[i].name)
type_dict[name] = types.Type.create(core.BNNewTypeReference(parse.types[i].type), platform=self)
for i in range(0, parse.variableCount):
name = types.QualifiedName._from_core_struct(parse.variables[i].name)
variables[name] = types.Type.create(core.BNNewTypeReference(parse.variables[i].type), platform=self)
for i in range(0, parse.functionCount):
name = types.QualifiedName._from_core_struct(parse.functions[i].name)
functions[name] = types.Type.create(core.BNNewTypeReference(parse.functions[i].type), platform=self)
core.BNFreeTypeParserResult(parse)
return typeparser.BasicTypeParserResult(type_dict, variables, functions)
@property
def arch(self):
return self._arch
_platform_cache = {}
[docs]
class CorePlatform(Platform):
def __init__(self, handle: core.BNPlatform):
super(CorePlatform, self).__init__(handle=handle)
if type(self) is CorePlatform:
global _platform_cache
_platform_cache[ctypes.addressof(handle.contents)] = self
@classmethod
def _from_cache(cls, handle) -> 'Platform':
"""
Look up a platform from a given BNPlatform handle
:param handle: BNPlatform pointer
:return: Platform instance responsible for this handle
"""
global _platform_cache
return _platform_cache.get(ctypes.addressof(handle.contents)) or cls(handle)
[docs]
def adjust_type_parser_input(
self,
parser: 'typeparser.TypeParser',
arguments: List[str],
source_files: List[Tuple[str, str]]
) -> Tuple[List[str], List[Tuple[str, str]]]:
arguments_in = (ctypes.c_char_p * len(arguments))()
for i, argument in enumerate(arguments):
arguments_in[i] = core.cstr(argument)
source_file_names_in = (ctypes.c_char_p * len(source_files))()
source_file_names_out = (ctypes.c_char_p * len(source_files))()
source_file_values_in = (ctypes.c_char_p * len(source_files))()
source_file_values_out = (ctypes.c_char_p * len(source_files))()
for i, (name, value) in enumerate(source_files):
source_file_names_in[i] = core.cstr(name)
source_file_values_in[i] = core.cstr(value)
arguments_out = ctypes.POINTER(ctypes.c_char_p)()
arguments_len_out = (ctypes.c_size_t)()
source_file_names_out = ctypes.POINTER(ctypes.c_char_p)()
source_file_values_out = ctypes.POINTER(ctypes.c_char_p)()
source_files_len_out = (ctypes.c_size_t)()
core.BNPlatformAdjustTypeParserInput(
self.handle,
parser.handle,
arguments_in,
len(arguments),
source_file_names_in,
source_file_values_in,
len(source_files),
arguments_out,
arguments_len_out,
source_file_names_out,
source_file_values_out,
source_files_len_out
)
result_arguments = []
for i in range(arguments_len_out.value):
result_arguments.append(core.pyNativeStr(arguments_out[i]))
result_source_files = []
for i in range(source_files_len_out.value):
result_source_files.append((
core.pyNativeStr(source_file_names_out[i]),
core.pyNativeStr(source_file_values_out[i]),
))
core.BNFreeStringList(arguments_out, arguments_len_out.value)
core.BNFreeStringList(source_file_names_out, source_files_len_out.value)
core.BNFreeStringList(source_file_values_out, source_files_len_out.value)
return result_arguments, result_source_files