summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/__init__.py2
-rw-r--r--python/generator.cpp12
-rw-r--r--python/typeparser.py589
-rw-r--r--python/typeprinter.py390
-rw-r--r--python/types.py157
5 files changed, 1088 insertions, 62 deletions
diff --git a/python/__init__.py b/python/__init__.py
index a230dec4..2d0f7065 100644
--- a/python/__init__.py
+++ b/python/__init__.py
@@ -65,6 +65,8 @@ from .workflow import *
from .commonil import *
from .database import *
from .secretsprovider import *
+from .typeparser import *
+from .typeprinter import *
# We import each of these by name to prevent conflicts between
# log.py and the function 'log' which we don't import below
from .log import (
diff --git a/python/generator.cpp b/python/generator.cpp
index 99d2d455..cc4b5102 100644
--- a/python/generator.cpp
+++ b/python/generator.cpp
@@ -239,7 +239,19 @@ int main(int argc, char* argv[])
map<QualifiedName, Ref<Type>> types, vars, funcs;
string errors;
auto arch = new CoreArchitecture(BNGetNativeTypeParserArchitecture());
+
+ string oldParser;
+ if (Settings::Instance()->Contains("analysis.types.parserNamer"))
+ oldParser = Settings::Instance()->Get<string>("analysis.types.parserNamer");
+ Settings::Instance()->Set("analysis.types.parserName", "CoreTypeParser");
+
bool ok = arch->GetStandalonePlatform()->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors);
+
+ if (!oldParser.empty())
+ Settings::Instance()->Set("analysis.types.parserName", oldParser);
+ else
+ Settings::Instance()->Reset("analysis.types.parserName");
+
fprintf(stderr, "Errors: %s\n", errors.c_str());
if (!ok)
return 1;
diff --git a/python/typeparser.py b/python/typeparser.py
new file mode 100644
index 00000000..ac5134f0
--- /dev/null
+++ b/python/typeparser.py
@@ -0,0 +1,589 @@
+# Copyright (c) 2015-2022 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 abc
+import ctypes
+import dataclasses
+from json import dumps
+from typing import List, Tuple, Optional
+
+import sys
+import traceback
+
+# Binary Ninja Components
+import binaryninja
+import binaryninja._binaryninjacore as core
+
+from .settings import Settings
+from . import platform
+from . import types
+from .log import log_error
+from .enums import TypeParserErrorSeverity
+
+
+@dataclasses.dataclass(frozen=True)
+class QualifiedNameTypeAndId:
+ name: 'types.QualifiedNameType'
+ id: str
+ type: 'types.Type'
+
+ @classmethod
+ def _from_core_struct(cls, struct: core.BNQualifiedNameTypeAndId) -> 'QualifiedNameTypeAndId':
+ name = types.QualifiedName._from_core_struct(struct.name)
+ type = types.Type.create(handle=core.BNNewTypeReference(struct.type))
+ return QualifiedNameTypeAndId(name, struct.id, type)
+
+ def _to_core_struct(self) -> core.BNQualifiedNameTypeAndId:
+ result = core.BNQualifiedNameTypeAndId()
+ result.name = types.QualifiedName(self.name)._to_core_struct()
+ result.type = core.BNNewTypeReference(self.type.handle)
+ result.id = self.id
+ return result
+
+
+@dataclasses.dataclass(frozen=True)
+class TypeParserError:
+ severity: TypeParserErrorSeverity
+ message: str
+ file_name: str
+ line: int
+ column: int
+
+ def __str__(self):
+ text = ""
+ if self.severity == TypeParserErrorSeverity.ErrorSeverity \
+ or self.severity == TypeParserErrorSeverity.FatalSeverity:
+ text += "error: "
+ if self.severity == TypeParserErrorSeverity.WarningSeverity:
+ text += "warning: "
+ if self.severity == TypeParserErrorSeverity.NoteSeverity:
+ text += "note: "
+ if self.severity == TypeParserErrorSeverity.RemarkSeverity:
+ text += "remark: "
+ if self.severity == TypeParserErrorSeverity.IgnoredSeverity:
+ text += "ignored: "
+
+ if self.file_name == "":
+ text += f"<unknown>: {self.message}\n"
+ else:
+ text += f"{self.file_name}: {self.line}:{self.column} {self.message}\n"
+ return text
+
+ @classmethod
+ def _from_core_struct(cls, struct: core.BNTypeParserError) -> 'TypeParserError':
+ return TypeParserError(struct.severity, struct.message, struct.fileName, struct.line, struct.column)
+
+ def _to_core_struct(self) -> core.BNTypeParserError:
+ result = core.BNTypeParserError()
+ result.severity = self.severity
+ result.message = self.message
+ result.fileName = self.file_name
+ result.line = self.line
+ result.column = self.column
+ return result
+
+
+@dataclasses.dataclass(frozen=True)
+class ParsedType:
+ name: 'types.QualifiedNameType'
+ type: 'types.Type'
+ is_user: bool
+
+ @classmethod
+ def _from_core_struct(cls, struct: core.BNParsedType) -> 'ParsedType':
+ name = types.QualifiedName._from_core_struct(struct.name)
+ type = types.Type.create(handle=core.BNNewTypeReference(struct.type))
+ return ParsedType(name, type, struct.isUser)
+
+ def _to_core_struct(self) -> core.BNParsedType:
+ result = core.BNParsedType()
+ result.name = types.QualifiedName(self.name)._to_core_struct()
+ result.type = core.BNNewTypeReference(self.type.handle)
+ result.isUser = self.is_user
+ return result
+
+
+@dataclasses.dataclass(frozen=True)
+class TypeParserResult:
+ types: List[ParsedType]
+ variables: List[ParsedType]
+ functions: List[ParsedType]
+
+ def __repr__(self):
+ return f"<types: {self.types}, variables: {self.variables}, functions: {self.functions}>"
+
+ @classmethod
+ def _from_core_struct(cls, struct: core.BNTypeParserResult) -> 'TypeParserResult':
+ types = []
+ variables = []
+ functions = []
+ for i in range(struct.typeCount):
+ types.append(ParsedType._from_core_struct(struct.types[i]))
+ for i in range(struct.variableCount):
+ variables.append(ParsedType._from_core_struct(struct.variables[i]))
+ for i in range(struct.functionCount):
+ functions.append(ParsedType._from_core_struct(struct.functions[i]))
+ return TypeParserResult(types, variables, functions)
+
+ def _to_core_struct(self) -> core.BNTypeParserResult:
+ result = core.BNTypeParserResult()
+ result.typeCount = len(self.types)
+ result.variableCount = len(self.variables)
+ result.functionCount = len(self.functions)
+ result.types = (core.BNParsedType * len(self.types))()
+ result.variables = (core.BNParsedType * len(self.variables))()
+ result.functions = (core.BNParsedType * len(self.functions))()
+
+ for (i, type) in enumerate(self.types):
+ result.types[i] = type._to_core_struct()
+ for (i, variable) in enumerate(self.variables):
+ result.variables[i] = variable._to_core_struct()
+ for (i, function) in enumerate(self.functions):
+ result.functions[i] = function._to_core_struct()
+
+ return result
+
+
+def to_bytes(field):
+ if type(field) == bytes:
+ return field
+ if type(field) == str:
+ return field.encode()
+ return str(field).encode()
+
+
+class _TypeParserMetaclass(type):
+ def __iter__(self):
+ binaryninja._init_plugins()
+ count = ctypes.c_ulonglong()
+ types = core.BNGetTypeParserList(count)
+ try:
+ for i in range(0, count.value):
+ yield CoreTypeParser(types[i])
+ finally:
+ core.BNFreeTypeParserList(types)
+
+ def __getitem__(self, value):
+ binaryninja._init_plugins()
+ handle = core.BNGetTypeParserByName(str(value))
+ if handle is None:
+ raise KeyError(f"'{value}' is not a valid TypeParser")
+ return CoreTypeParser(handle)
+
+ @property
+ def default(self):
+ name = binaryninja.Settings().get_string("analysis.types.parserName")
+ return CoreTypeParser[name]
+
+
+class TypeParser(metaclass=_TypeParserMetaclass):
+ name = None
+ _registered_parsers = []
+ _cached_string = None
+ _cached_result = None
+ _cached_error = None
+
+ def __init__(self, handle=None):
+ if handle is not None:
+ self.handle = core.handle_of_type(handle, core.BNTypeParser)
+ self.__dict__["name"] = core.BNGetTypeParserName(handle)
+
+ def register(self):
+ assert self.__class__.name is not None
+
+ self._cb = core.BNTypeParserCallbacks()
+ self._cb.context = 0
+ self._cb.preprocessSource = self._cb.preprocessSource.__class__(self._preprocess_source)
+ self._cb.parseTypesFromSource = self._cb.parseTypesFromSource.__class__(self._parse_types_from_source)
+ self._cb.parseTypeString = self._cb.parseTypeString.__class__(self._parse_type_string)
+ self._cb.freeString = self._cb.freeString.__class__(self._free_string)
+ self._cb.freeResult = self._cb.freeResult.__class__(self._free_result)
+ self._cb.freeErrorList = self._cb.freeErrorList.__class__(self._free_error_list)
+ self.handle = core.BNRegisterTypeParser(self.__class__.name, self._cb)
+ self.__class__._registered_parsers.append(self)
+
+ def __str__(self):
+ return f'<TypeParser: {self.name}>'
+
+ def __repr__(self):
+ return f'<TypeParser: {self.name}>'
+
+ def _preprocess_source(
+ self, ctxt, source, fileName, platform_, existingTypes, existingTypeCount,
+ options, optionCount, includeDirs, includeDirCount,
+ output, errors, errorCount
+ ) -> bool:
+ try:
+ source_py = core.pyNativeStr(source)
+ file_name_py = core.pyNativeStr(fileName)
+ platform_py = platform.Platform(handle=core.BNNewPlatformReference(platform_))
+
+ existing_types_py = []
+ for i in range(existingTypeCount):
+ existing_types_py.append(QualifiedNameTypeAndId._from_core_struct(existingTypes[i]))
+
+ options_py = []
+ for i in range(optionCount):
+ options_py.append(core.pyNativeStr(options[i]))
+
+ include_dirs_py = []
+ for i in range(includeDirCount):
+ include_dirs_py.append(core.pyNativeStr(includeDirs[i]))
+
+ (output_py, errors_py) = self.preprocess_source(
+ source_py, file_name_py, platform_py, existing_types_py, options_py,
+ include_dirs_py)
+
+ if output_py is not None and output is not None:
+ TypeParser._cached_string = core.cstr(output_py)
+ output[0] = TypeParser._cached_string
+ if errorCount is not None:
+ errorCount[0] = len(errors_py)
+ if errors is not None:
+ errors_out = (core.BNTypeParserError * len(errors_py))()
+ for i in range(len(errors_py)):
+ errors_out[i] = errors_py[i]._to_core_struct()
+ TypeParser._cached_error = errors_out
+ errors[0] = errors_out
+
+ return output_py is not None
+ except:
+ errorCount[0] = 0
+ log_error(traceback.format_exc())
+ return False
+
+ def _parse_types_from_source(
+ self, ctxt, source, fileName, platform_, existingTypes, existingTypeCount,
+ options, optionCount, includeDirs, includeDirCount, autoTypeSource,
+ result, errors, errorCount
+ ) -> bool:
+ try:
+ source_py = core.pyNativeStr(source)
+ file_name_py = core.pyNativeStr(fileName)
+ platform_py = platform.Platform(handle=core.BNNewPlatformReference(platform_))
+
+ existing_types_py = []
+ for i in range(existingTypeCount):
+ existing_types_py.append(QualifiedNameTypeAndId._from_core_struct(existingTypes[i]))
+
+ options_py = []
+ for i in range(optionCount):
+ options_py.append(core.pyNativeStr(options[i]))
+
+ include_dirs_py = []
+ for i in range(includeDirCount):
+ include_dirs_py.append(core.pyNativeStr(includeDirs[i]))
+
+ auto_type_source = core.pyNativeStr(autoTypeSource)
+
+ (result_py, errors_py) = self.parse_types_from_source(
+ source_py, file_name_py, platform_py, existing_types_py, options_py,
+ include_dirs_py, auto_type_source)
+
+ if result_py is not None and result is not None:
+ result_struct = result_py._to_core_struct()
+ TypeParser._cached_result = result_struct
+ result[0] = result_struct
+
+ if errorCount is not None:
+ errorCount[0] = len(errors_py)
+ if errors is not None:
+ errors_out = (core.BNTypeParserError * len(errors_py))()
+ for i in range(len(errors_py)):
+ errors_out[i] = errors_py[i]._to_core_struct()
+ TypeParser._cached_error = errors_out
+ errors[0] = errors_out
+
+ return result_py is not None
+ except:
+ result[0].typeCount = 0
+ result[0].variableCount = 0
+ result[0].functionCount = 0
+ errorCount[0] = 0
+ log_error(traceback.format_exc())
+ return False
+
+ def _parse_type_string(
+ self, ctxt, source, platform_, existingTypes, existingTypeCount,
+ result, errors, errorCount
+ ) -> bool:
+ try:
+ source_py = core.pyNativeStr(source)
+ platform_py = platform.Platform(handle=core.BNNewPlatformReference(platform_))
+
+ existing_types_py = []
+ for i in range(existingTypeCount):
+ existing_types_py.append(QualifiedNameTypeAndId._from_core_struct(existingTypes[i]))
+
+ (result_py, errors_py) = self.parse_type_string(
+ source_py, platform_py, existing_types_py)
+
+ if result_py is not None and result is not None:
+ result[0].name = types.QualifiedName(result_py[0])._to_core_struct()
+ result[0].type = core.BNNewTypeReference(result_py[1].handle)
+
+ if errorCount is not None:
+ errorCount[0] = len(errors_py)
+ if errors is not None:
+ errors_out = (core.BNTypeParserError * len(errors_py))()
+ for i in range(len(errors_py)):
+ errors_out[i] = errors_py[i]._to_core_struct()
+ TypeParser._cached_error = errors_out
+ errors[0] = errors_out
+
+ return result_py is not None
+ except:
+ errorCount[0] = 0
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_string(
+ self, ctxt, string
+ ) -> bool:
+ try:
+ TypeParser._cached_string = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_result(
+ self, ctxt, result
+ ) -> bool:
+ try:
+ if TypeParser._cached_result is not None:
+ for i in range(TypeParser._cached_result.typeCount):
+ core.BNFreeType(TypeParser._cached_result.types[i].type)
+ for i in range(TypeParser._cached_result.variableCount):
+ core.BNFreeType(TypeParser._cached_result.variables[i].type)
+ for i in range(TypeParser._cached_result.functionCount):
+ core.BNFreeType(TypeParser._cached_result.functions[i].type)
+ TypeParser._cached_result = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_error_list(
+ self, ctxt, errors, errorCount
+ ) -> bool:
+ try:
+ TypeParser._cached_error = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def preprocess_source(
+ self, source: str, file_name: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None,
+ options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None
+ ) -> Tuple[Optional[str], List[TypeParserError]]:
+ raise NotImplementedError("Not implemented")
+
+ def parse_types_from_source(
+ self, source: str, file_name: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None,
+ options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None,
+ auto_type_source: str = ""
+ ) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]:
+ raise NotImplementedError("Not implemented")
+
+ def parse_type_string(
+ self, source: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None
+ ) -> Tuple[Optional[Tuple['types.QualifiedNameType', 'types.Type']], List[TypeParserError]]:
+ raise NotImplementedError("Not implemented")
+
+
+class CoreTypeParser(TypeParser):
+
+ def preprocess_source(
+ self, source: str, file_name: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None,
+ options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None
+ ) -> Tuple[Optional[str], List[TypeParserError]]:
+ if existing_types is None:
+ existing_types = []
+ if options is None:
+ options = []
+ if include_dirs is None:
+ include_dirs = []
+
+ existing_types_cpp = (core.BNQualifiedNameTypeAndId * len(existing_types))()
+ for (i, qnatid) in enumerate(existing_types):
+ existing_types_cpp[i] = qnatid._to_core_struct()
+
+ options_cpp = (ctypes.c_char_p * len(options))()
+ for (i, s) in enumerate(options):
+ options_cpp[i] = core.cstr(s)
+
+ include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))()
+ for (i, s) in enumerate(include_dirs):
+ include_dirs_cpp[i] = core.cstr(s)
+
+ output_cpp = ctypes.c_char_p()
+ errors_cpp = ctypes.POINTER(core.BNTypeParserError)()
+ error_count = ctypes.c_size_t()
+
+ success = core.BNTypeParserPreprocessSource(
+ self.handle, source, file_name, platform.handle,
+ existing_types_cpp, len(existing_types), options_cpp, len(options),
+ include_dirs_cpp, len(include_dirs),
+ output_cpp, errors_cpp, error_count
+ )
+
+ if success:
+ output = core.pyNativeStr(output_cpp.value)
+ core.free_string(output_cpp)
+ else:
+ output = None
+
+ errors = []
+ for i in range(error_count.value):
+ errors.append(TypeParserError._from_core_struct(errors_cpp[i]))
+ core.BNFreeTypeParserErrors(errors_cpp, error_count.value)
+
+ return output, errors
+
+ def parse_types_from_source(
+ self, source: str, file_name: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None,
+ options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None,
+ auto_type_source: str = ""
+ ) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]:
+ if existing_types is None:
+ existing_types = []
+ if options is None:
+ options = []
+ if include_dirs is None:
+ include_dirs = []
+
+ existing_types_cpp = (core.BNQualifiedNameTypeAndId * len(existing_types))()
+ for (i, qnatid) in enumerate(existing_types):
+ existing_types_cpp[i] = qnatid._to_core_struct()
+
+ options_cpp = (ctypes.c_char_p * len(options))()
+ for (i, s) in enumerate(options):
+ options_cpp[i] = core.cstr(s)
+
+ include_dirs_cpp = (ctypes.c_char_p * len(include_dirs))()
+ for (i, s) in enumerate(include_dirs):
+ include_dirs_cpp[i] = core.cstr(s)
+
+ result_cpp = core.BNTypeParserResult()
+ errors_cpp = ctypes.POINTER(core.BNTypeParserError)()
+ error_count = ctypes.c_size_t()
+
+ success = core.BNTypeParserParseTypesFromSource(
+ self.handle, source, file_name, platform.handle,
+ existing_types_cpp, len(existing_types), options_cpp, len(options),
+ include_dirs_cpp, len(include_dirs), auto_type_source,
+ result_cpp, errors_cpp, error_count
+ )
+
+ if success:
+ result = TypeParserResult._from_core_struct(result_cpp)
+ else:
+ result = None
+ core.BNFreeTypeParserResult(result_cpp)
+
+ errors = []
+ for i in range(error_count.value):
+ errors.append(TypeParserError._from_core_struct(errors_cpp[i]))
+ core.BNFreeTypeParserErrors(errors_cpp, error_count.value)
+
+ return result, errors
+
+
+ def parse_type_string(
+ self, source: str, platform: platform.Platform,
+ existing_types: Optional[List[QualifiedNameTypeAndId]] = None
+ ) -> Tuple[Optional[Tuple['types.QualifiedNameType', 'types.Type']], List[TypeParserError]]:
+ if existing_types is None:
+ existing_types = []
+ existing_types_cpp = (core.BNQualifiedNameTypeAndId * len(existing_types))()
+ for (i, qnatid) in enumerate(existing_types):
+ existing_types_cpp[i] = qnatid._to_core_struct()
+
+ result_cpp = core.BNQualifiedNameAndType()
+ errors_cpp = ctypes.POINTER(core.BNTypeParserError)()
+ error_count = ctypes.c_size_t()
+
+ success = core.BNTypeParserParseTypeString(
+ self.handle, source, platform.handle,
+ existing_types_cpp, len(existing_types),
+ result_cpp, errors_cpp, error_count
+ )
+
+ if success:
+ result = (
+ types.QualifiedName._from_core_struct(result_cpp.name),
+ types.Type.create(handle=core.BNNewTypeReference(result_cpp.type))
+ )
+ core.BNFreeQualifiedNameAndType(result_cpp)
+ else:
+ result = None
+
+ errors = []
+ for i in range(error_count.value):
+ errors.append(TypeParserError._from_core_struct(errors_cpp[i]))
+ core.BNFreeTypeParserErrors(errors_cpp, error_count.value)
+
+ return result, errors
+
+
+def preprocess_source(source: str, filename: str = None,
+ include_dirs: Optional[List[str]] = None) -> Tuple[Optional[str], str]:
+ """
+ ``preprocess_source`` run the C preprocessor on the given source or source filename.
+
+ :param str source: source to pre-process
+ :param str filename: optional filename to pre-process
+ :param include_dirs: list of string directories to use as include directories.
+ :type include_dirs: list(str)
+ :return: returns a tuple of (preprocessed_source, error_string)
+ :rtype: tuple(str,str)
+ :Example:
+
+ >>> source = "#define TEN 10\\nint x[TEN];\\n"
+ >>> preprocess_source(source)
+ ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '')
+ >>>
+ """
+ if filename is None:
+ filename = "input"
+ 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')
+ output = ctypes.c_char_p()
+ errors = ctypes.c_char_p()
+ result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs))
+ assert output.value is not None
+ assert errors.value is not None
+ output_str = output.value.decode('utf-8')
+ error_str = errors.value.decode('utf-8')
+ core.free_string(output)
+ core.free_string(errors)
+ if result:
+ return output_str, error_str
+ return None, error_str
diff --git a/python/typeprinter.py b/python/typeprinter.py
new file mode 100644
index 00000000..354084dd
--- /dev/null
+++ b/python/typeprinter.py
@@ -0,0 +1,390 @@
+# Copyright (c) 2015-2022 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 abc
+import ctypes
+import dataclasses
+from json import dumps
+from typing import List, Tuple, Optional
+
+import sys
+import traceback
+
+# Binary Ninja Components
+import binaryninja
+import binaryninja._binaryninjacore as core
+
+from .settings import Settings
+from . import platform as _platform
+from . import types
+from . import function as _function
+from . import binaryview
+from .log import log_error
+from .enums import TokenEscapingType
+
+
+def to_bytes(field):
+ if type(field) == bytes:
+ return field
+ if type(field) == str:
+ return field.encode()
+ return str(field).encode()
+
+
+class _TypePrinterMetaclass(type):
+ def __iter__(self):
+ binaryninja._init_plugins()
+ count = ctypes.c_ulonglong()
+ types = core.BNGetTypePrinterList(count)
+ try:
+ for i in range(0, count.value):
+ yield CoreTypePrinter(types[i])
+ finally:
+ core.BNFreeTypePrinterList(types)
+
+ def __getitem__(self, value):
+ binaryninja._init_plugins()
+ handle = core.BNGetTypePrinterByName(str(value))
+ if handle is None:
+ raise KeyError(f"'{value}' is not a valid TypePrinter")
+ return CoreTypePrinter(handle)
+
+ @property
+ def default(self):
+ name = binaryninja.Settings().get_string("analysis.types.printerName")
+ return CoreTypePrinter[name]
+
+
+class TypePrinter(metaclass=_TypePrinterMetaclass):
+ name = None
+ _registered_printers = []
+ _cached_tokens = None
+ _cached_string = None
+ _cached_error = None
+
+ def __init__(self, handle=None):
+ if handle is not None:
+ self.handle = core.handle_of_type(handle, core.BNTypePrinter)
+ self.__dict__["name"] = core.BNGetTypePrinterName(handle)
+
+ def register(self):
+ assert self.__class__.name is not None
+
+ self._cb = core.BNTypePrinterCallbacks()
+ self._cb.context = 0
+ self._cb.getTypeTokens = self._cb.getTypeTokens.__class__(self._get_type_tokens)
+ self._cb.getTypeTokensBeforeName = self._cb.getTypeTokensBeforeName.__class__(self._get_type_tokens_before_name)
+ self._cb.getTypeTokensAfterName = self._cb.getTypeTokensAfterName.__class__(self._get_type_tokens_after_name)
+ self._cb.getTypeString = self._cb.getTypeString.__class__(self._get_type_string)
+ self._cb.getTypeStringBeforeName = self._cb.getTypeStringBeforeName.__class__(self._get_type_string_before_name)
+ self._cb.getTypeStringAfterName = self._cb.getTypeStringAfterName.__class__(self._get_type_string_after_name)
+ self._cb.getTypeLines = self._cb.getTypeLines.__class__(self._get_type_lines)
+ self._cb.freeTokens = self._cb.freeTokens.__class__(self._free_tokens)
+ self._cb.freeString = self._cb.freeString.__class__(self._free_string)
+ self._cb.freeLines = self._cb.freeLines.__class__(self._free_lines)
+ self.handle = core.BNRegisterTypePrinter(self.__class__.name, self._cb)
+ self.__class__._registered_printers.append(self)
+
+ def __str__(self):
+ return f'<TypePrinter: {self.name}>'
+
+ def __repr__(self):
+ return f'<TypePrinter: {self.name}>'
+
+ def _get_type_tokens(self, ctxt, type, platform, name, base_confidence, escaping, result, result_count):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ result_py = self.get_type_tokens(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ types.QualifiedName._from_core_struct(name.contents), base_confidence, escaping)
+
+ TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py)
+ result[0] = TypePrinter._cached_tokens
+ result_count[0] = len(result_py)
+
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_tokens_before_name(self, ctxt, type, platform, base_confidence, parent_type, escaping, result, result_count):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ parent_type_py = None
+ if parent_type:
+ parent_type_py = types.Type(handle=core.BNNewTypeReference(parent_type))
+ result_py = self.get_type_tokens_before_name(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ base_confidence, parent_type_py, escaping)
+
+ TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py)
+ result[0] = TypePrinter._cached_tokens
+ result_count[0] = len(result_py)
+
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_tokens_after_name(self, ctxt, type, platform, base_confidence, parent_type, escaping, result, result_count):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ parent_type_py = None
+ if parent_type:
+ parent_type_py = types.Type(handle=core.BNNewTypeReference(parent_type))
+ result_py = self.get_type_tokens_after_name(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ base_confidence, parent_type_py, escaping)
+
+ TypePrinter._cached_tokens = _function.InstructionTextToken._get_core_struct(result_py)
+ result[0] = TypePrinter._cached_tokens
+ result_count[0] = len(result_py)
+
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_string(self, ctxt, type, platform, name, escaping, result):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ result_py = self.get_type_string(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ types.QualifiedName._from_core_struct(name.contents), escaping)
+
+ TypePrinter._cached_string = core.cstr(result_py)
+ result[0] = TypePrinter._cached_string
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_string_before_name(self, ctxt, type, platform, escaping, result):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ result_py = self.get_type_string_before_name(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ escaping)
+
+ TypePrinter._cached_string = core.cstr(result_py)
+ result[0] = TypePrinter._cached_string
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_string_after_name(self, ctxt, type, platform, escaping, result):
+ try:
+ platform_py = None
+ if platform:
+ platform_py = _platform.Platform(handle=core.BNNewPlatformReference(platform))
+ result_py = self.get_type_string_after_name(
+ types.Type(handle=core.BNNewTypeReference(type)), platform_py,
+ escaping)
+
+ TypePrinter._cached_string = core.cstr(result_py)
+ result[0] = TypePrinter._cached_string
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _get_type_lines(self, ctxt, type, data, name, line_width, collapsed, escaping, result, result_count):
+ try:
+ result_py = self.get_type_lines(
+ types.Type(handle=core.BNNewTypeReference(type)),
+ binaryview.BinaryView(handle=core.BNNewViewReference(data)),
+ types.QualifiedName._from_core_struct(name.contents),
+ line_width, collapsed, escaping)
+
+ TypePrinter._cached_lines = (core.BNTypeDefinitionLine * len(result_py))()
+ for (i, line) in enumerate(result_py):
+ TypePrinter._cached_lines[i] = line._to_core_struct()
+ result[0] = TypePrinter._cached_lines
+ result_count[0] = len(result_py)
+
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_tokens(self, ctxt, tokens, count):
+ try:
+ TypePrinter._cached_tokens = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_string(self, ctxt, string):
+ try:
+ TypePrinter._cached_string = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def _free_lines(self, ctxt, lines, count):
+ try:
+ for line in TypePrinter._cached_lines:
+ core.BNFreeType(line.type)
+ core.BNFreeType(line.rootType)
+ TypePrinter._cached_lines = None
+ return True
+ except:
+ log_error(traceback.format_exc())
+ return False
+
+ def get_type_tokens(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", base_confidence: int = core.max_confidence, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]:
+ raise NotImplementedError()
+
+ def get_type_tokens_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]:
+ raise NotImplementedError()
+
+ def get_type_tokens_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[_function.InstructionTextToken]:
+ raise NotImplementedError()
+
+ def get_type_string(self, type: types.Type, platform: Optional[_platform.Platform] = None, name: types.QualifiedNameType = "", escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ raise NotImplementedError()
+
+ def get_type_string_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ raise NotImplementedError()
+
+ def get_type_string_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ raise NotImplementedError()
+
+ def get_type_lines(self, type: types.Type, data: binaryview.BinaryView, name: types.QualifiedNameType, line_width = 80, collapsed = False, escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[types.TypeDefinitionLine]:
+ raise NotImplementedError()
+
+
+class CoreTypePrinter(TypePrinter):
+
+ def get_type_tokens(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ name: types.QualifiedNameType = "", base_confidence: int = core.max_confidence,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[
+ _function.InstructionTextToken]:
+ if not isinstance(name, types.QualifiedName):
+ name = types.QualifiedName(name)
+ count = ctypes.c_ulonglong()
+ name_cpp = name._to_core_struct()
+ result_cpp = ctypes.POINTER(core.BNInstructionTextToken)()
+ if not core.BNGetTypePrinterTypeTokens(self.handle, type.handle, None if platform is None else platform.handle, name_cpp, base_confidence, ctypes.c_int(escaping), result_cpp, count):
+ raise RuntimeError("BNGetTypePrinterTypeTokens returned False")
+
+ result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value)
+ core.BNFreeInstructionText(result_cpp.contents, count.value)
+ return result
+
+ def get_type_tokens_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[
+ _function.InstructionTextToken]:
+ count = ctypes.c_ulonglong()
+ result_cpp = ctypes.POINTER(core.BNInstructionTextToken)()
+ parent_type_cpp = None
+ if parent_type is not None:
+ parent_type_cpp = parent_type.handle
+ if not core.BNGetTypePrinterTypeTokensBeforeName(self.handle, type.handle, None if platform is None else platform.handle, base_confidence, parent_type_cpp, ctypes.c_int(escaping), result_cpp, count):
+ raise RuntimeError("BNGetTypePrinterTypeTokensBeforeName returned False")
+
+ result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value)
+ core.BNFreeInstructionText(result_cpp.contents, count.value)
+ return result
+
+ def get_type_tokens_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ base_confidence: int = core.max_confidence, parent_type: Optional[types.Type] = None,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> List[
+ _function.InstructionTextToken]:
+ count = ctypes.c_ulonglong()
+ result_cpp = ctypes.POINTER(core.BNInstructionTextToken)()
+ parent_type_cpp = None
+ if parent_type is not None:
+ parent_type_cpp = parent_type.handle
+ if not core.BNGetTypePrinterTypeTokensAfterName(self.handle, type.handle, None if platform is None else platform.handle, base_confidence, parent_type_cpp, ctypes.c_int(escaping), result_cpp, count):
+ raise RuntimeError("BNGetTypePrinterTypeTokensAfterName returned False")
+
+ result = _function.InstructionTextToken._from_core_struct(result_cpp, count.value)
+ core.BNFreeInstructionText(result_cpp.contents, count.value)
+ return result
+
+ def get_type_string(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ name: types.QualifiedNameType = "",
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ if not isinstance(name, types.QualifiedName):
+ name = types.QualifiedName(name)
+ result_cpp = ctypes.c_char_p()
+ if not core.BNGetTypePrinterTypeString(self.handle, type.handle, None if platform is None else platform.handle, name._to_core_struct(), ctypes.c_int(escaping), result_cpp):
+ raise RuntimeError("BNGetTypePrinterTypeString returned False")
+
+ result = core.pyNativeStr(result_cpp.value)
+ core.free_string(result_cpp)
+ return result
+
+ def get_type_string_before_name(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ result_cpp = ctypes.c_char_p()
+ if not core.BNGetTypePrinterTypeStringBeforeName(self.handle, type.handle, None if platform is None else platform.handle, ctypes.c_int(escaping), result_cpp):
+ raise RuntimeError("BNGetTypePrinterTypeStringBeforeName returned False")
+
+ result = core.pyNativeStr(result_cpp.value)
+ core.free_string(result_cpp)
+ return result
+
+ def get_type_string_after_name(self, type: types.Type, platform: Optional[_platform.Platform] = None,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType) -> str:
+ result_cpp = ctypes.c_char_p()
+ if not core.BNGetTypePrinterTypeStringAfterName(self.handle, type.handle, None if platform is None else platform.handle, ctypes.c_int(escaping), result_cpp):
+ raise RuntimeError("BNGetTypePrinterTypeStringAfterName returned False")
+
+ result = core.pyNativeStr(result_cpp.value)
+ core.free_string(result_cpp)
+ return result
+
+ def get_type_lines(self, type: types.Type, data: binaryview.BinaryView,
+ name: types.QualifiedNameType, line_width = 80, collapsed = False,
+ escaping: TokenEscapingType = TokenEscapingType.BackticksTokenEscapingType
+ ) -> List[types.TypeDefinitionLine]:
+ if not isinstance(name, types.QualifiedName):
+ name = types.QualifiedName(name)
+ count = ctypes.c_ulonglong()
+ core_lines = ctypes.POINTER(core.BNTypeDefinitionLine)()
+ if not core.BNGetTypePrinterTypeLines(self.handle, type.handle, data.handle, name._to_core_struct(), line_width, collapsed, ctypes.c_int(escaping), core_lines, count):
+ raise RuntimeError("BNGetTypePrinterTypeLines returned False")
+ lines = []
+ for i in range(count.value):
+ tokens = _function.InstructionTextToken._from_core_struct(core_lines[i].tokens, core_lines[i].count)
+ type_ = types.Type.create(handle=core.BNNewTypeReference(core_lines[i].type), platform=data.platform)
+ root_type = types.Type.create(handle=core.BNNewTypeReference(core_lines[i].rootType), platform=data.platform)
+ root_type_name = core.pyNativeStr(core_lines[i].rootTypeName)
+ line = types.TypeDefinitionLine(core_lines[i].lineType, tokens, type_, root_type, root_type_name,
+ core_lines[i].offset, core_lines[i].fieldIndex)
+ lines.append(line)
+ core.BNFreeTypeDefinitionLineList(core_lines, count.value)
+ return lines
diff --git a/python/types.py b/python/types.py
index 65f58192..e00b3408 100644
--- a/python/types.py
+++ b/python/types.py
@@ -19,7 +19,7 @@
# IN THE SOFTWARE.
import ctypes
-from typing import Generator, List, Union, Mapping, Tuple, Optional, Iterable
+from typing import Generator, List, Union, Mapping, Tuple, Optional, Iterable, Dict
from dataclasses import dataclass
import uuid
from abc import abstractmethod
@@ -28,7 +28,8 @@ from abc import abstractmethod
from . import _binaryninjacore as core
from .enums import (
StructureVariant, SymbolType, SymbolBinding, TypeClass, NamedTypeReferenceClass, ReferenceType, VariableSourceType,
- TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType
+ TypeReferenceType, MemberAccess, MemberScope, TypeDefinitionLineType, TokenEscapingType,
+ NameType
)
from . import callingconvention
from . import function as _function
@@ -37,6 +38,7 @@ from . import architecture
from . import binaryview
from . import platform as _platform
from . import typelibrary
+from . import typeparser
QualifiedNameType = Union[Iterable[Union[str, bytes]], str, 'QualifiedName']
BoolWithConfidenceType = Union[bool, 'BoolWithConfidence']
@@ -47,6 +49,7 @@ EnumMembersType = Union[List[Tuple[str, int]], List[str], List['EnumerationMembe
SomeType = Union['TypeBuilder', 'Type']
TypeContainer = Union['binaryview.BinaryView', 'typelibrary.TypeLibrary']
NameSpaceType = Optional[Union[str, List[str], 'NameSpace']]
+TypeParserResult = typeparser.TypeParserResult
# The following are needed to prevent the type checker from getting
# confused as we have member functions in `Type` named the same thing
_int = int
@@ -223,6 +226,27 @@ class TypeDefinitionLine:
def __repr__(self):
return f"<typeDefinitionLine {self.type}: {self}>"
+ @staticmethod
+ def _from_core_struct(struct: core.BNTypeDefinitionLine, platform: Optional[_platform.Platform] = None):
+ tokens = _function.InstructionTextToken._from_core_struct(struct.tokens, struct.count)
+ type_ = Type.create(handle=core.BNNewTypeReference(struct.type), platform=platform)
+ root_type = Type.create(handle=core.BNNewTypeReference(struct.rootType), platform=platform)
+ root_type_name = core.pyNativeStr(struct.rootTypeName)
+ return TypeDefinitionLine(struct.lineType, tokens, type_, root_type, root_type_name,
+ struct.offset, struct.fieldIndex)
+
+ def _to_core_struct(self):
+ struct = core.BNTypeDefinitionLine()
+ struct.lineType = self.line_type
+ struct.tokens = _function.InstructionTextToken._get_core_struct(self.tokens)
+ struct.count = len(self.tokens)
+ struct.type = core.BNNewTypeReference(self.type.handle)
+ struct.rootType = core.BNNewTypeReference(self.root_type.handle)
+ struct.rootTypeName = self.root_type_name
+ struct.offset = self.offset
+ struct.fieldIndex = self.field_index
+ return struct
+
class CoreSymbol:
def __init__(self, handle: core.BNSymbolHandle):
@@ -888,7 +912,10 @@ class FunctionBuilder(TypeBuilder):
cls, return_type: Optional[SomeType] = None,
calling_convention: Optional['callingconvention.CallingConvention'] = None, params: Optional[ParamsType] = None,
var_args: Optional[BoolWithConfidenceType] = None, stack_adjust: Optional[OffsetWithConfidenceType] = None,
- platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence
+ platform: Optional['_platform.Platform'] = None, confidence: int = core.max_confidence,
+ can_return: Optional[BoolWithConfidence] = None, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None,
+ return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None,
+ name_type: 'NameType' = NameType.NoNameType
) -> 'FunctionBuilder':
param_buf = FunctionBuilder._to_core_struct(params)
if return_type is None:
@@ -904,11 +931,38 @@ class FunctionBuilder(TypeBuilder):
conv_conf.convention = calling_convention.handle
conv_conf.confidence = calling_convention.confidence
+ if reg_stack_adjust is None:
+ reg_stack_adjust = {}
+ reg_stack_adjust_regs = (ctypes.c_uint32 * len(reg_stack_adjust))()
+ reg_stack_adjust_values = (core.BNOffsetWithConfidence * len(reg_stack_adjust))()
+
+ for i, (reg, adjust) in enumerate(reg_stack_adjust.items()):
+ reg_stack_adjust_regs[i] = reg
+ reg_stack_adjust_values[i].value = adjust.value
+ reg_stack_adjust_values[i].confidence = adjust.confidence
+
+ return_regs_set = core.BNRegisterSetWithConfidence()
+ if return_regs is None or platform is None:
+ return_regs_set.count = 0
+ return_regs_set.confidence = 0
+ else:
+ return_regs_set.count = len(return_regs)
+ return_regs_set.confidence = 255
+ return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))()
+
+ for i, reg in enumerate(return_regs):
+ return_regs_set[i] = platform.arch.get_reg_index(reg)
+
if var_args is None:
vararg_conf = BoolWithConfidence.get_core_struct(False, 0)
else:
vararg_conf = BoolWithConfidence.get_core_struct(var_args, core.max_confidence)
+ if can_return is None:
+ can_return_conf = BoolWithConfidence.get_core_struct(True, 0)
+ else:
+ can_return_conf = BoolWithConfidence.get_core_struct(can_return, core.max_confidence)
+
if stack_adjust is None:
stack_adjust_conf = OffsetWithConfidence.get_core_struct(0, 0)
else:
@@ -916,7 +970,9 @@ class FunctionBuilder(TypeBuilder):
if params is None:
params = []
handle = core.BNCreateFunctionTypeBuilder(
- ret_conf, conv_conf, param_buf, len(params), vararg_conf, stack_adjust_conf
+ ret_conf, conv_conf, param_buf, len(params), vararg_conf, can_return_conf, stack_adjust_conf,
+ reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust),
+ return_regs_set, name_type
)
assert handle is not None, "BNCreateFunctionTypeBuilder returned None"
return cls(handle, platform, confidence)
@@ -1783,13 +1839,7 @@ class Type:
assert core_lines is not None, "core.BNGetTypeLines returned None"
lines = []
for i in range(count.value):
- tokens = _function.InstructionTextToken._from_core_struct(core_lines[i].tokens, core_lines[i].count)
- type_ = Type.create(handle=core.BNNewTypeReference(core_lines[i].type), platform=self._platform)
- root_type = Type.create(handle=core.BNNewTypeReference(core_lines[i].rootType), platform=self._platform)
- root_type_name = core.pyNativeStr(core_lines[i].rootTypeName)
- line = TypeDefinitionLine(core_lines[i].lineType, tokens, type_, root_type, root_type_name,
- core_lines[i].offset, core_lines[i].fieldIndex)
- lines.append(line)
+ lines.append(TypeDefinitionLine._from_core_struct(core_lines[i]))
core.BNFreeTypeDefinitionLineList(core_lines, count.value)
return lines
@@ -2448,7 +2498,10 @@ class FunctionType(Type):
calling_convention: Optional['callingconvention.CallingConvention'] = None,
variable_arguments: BoolWithConfidenceType = BoolWithConfidence(False),
stack_adjust: OffsetWithConfidence = OffsetWithConfidence(0), platform: Optional['_platform.Platform'] = None,
- confidence: int = core.max_confidence
+ confidence: int = core.max_confidence,
+ can_return: BoolWithConfidence = True, reg_stack_adjust: Optional[Dict['architecture.RegisterName', OffsetWithConfidenceType]] = None,
+ return_regs: Optional[Union['RegisterSet', List['architecture.RegisterType']]] = None,
+ name_type: 'NameType' = NameType.NoNameType
) -> 'FunctionType':
if ret is None:
ret = VoidType.create()
@@ -2474,9 +2527,38 @@ class FunctionType(Type):
else:
_stack_adjust = OffsetWithConfidence.get_core_struct(stack_adjust, core.max_confidence)
+
+ if reg_stack_adjust is None:
+ reg_stack_adjust = {}
+ reg_stack_adjust_regs = (ctypes.c_uint32 * len(reg_stack_adjust))()
+ reg_stack_adjust_values = (core.BNOffsetWithConfidence * len(reg_stack_adjust))()
+
+ for i, (reg, adjust) in enumerate(reg_stack_adjust.items()):
+ reg_stack_adjust_regs[i] = reg
+ reg_stack_adjust_values[i].value = adjust.value
+ reg_stack_adjust_values[i].confidence = adjust.confidence
+
+ return_regs_set = core.BNRegisterSetWithConfidence()
+ if return_regs is None or platform is None:
+ return_regs_set.count = 0
+ return_regs_set.confidence = 0
+ else:
+ return_regs_set.count = len(return_regs)
+ return_regs_set.confidence = 255
+ return_regs_set.regs = (ctypes.c_uint32 * len(return_regs))()
+
+ for i, reg in enumerate(return_regs):
+ return_regs_set[i] = platform.arch.get_reg_index(reg)
+
+ _can_return = BoolWithConfidence.get_core_struct(can_return)
+ if params is None:
+ params = []
func_type = core.BNCreateFunctionType(
- ret_conf, conv_conf, param_buf, len(params), _variable_arguments, _stack_adjust
+ ret_conf, conv_conf, param_buf, len(params), _variable_arguments, _can_return, _stack_adjust,
+ reg_stack_adjust_regs, reg_stack_adjust_values, len(reg_stack_adjust),
+ return_regs_set, name_type
)
+
assert func_type is not None, f"core.BNCreateFunctionType returned None {ret_conf} {conv_conf} {param_buf} {_variable_arguments} {_stack_adjust}"
return cls(core.BNNewTypeReference(func_type), platform, confidence)
@@ -2735,55 +2817,6 @@ class RegisterSet:
@dataclass(frozen=True)
-class TypeParserResult:
- types: Mapping[QualifiedName, Type]
- variables: Mapping[QualifiedName, Type]
- functions: Mapping[QualifiedName, Type]
-
- def __repr__(self):
- return f"<types: {self.types}, variables: {self.variables}, functions: {self.functions}>"
-
-
-def preprocess_source(source: str, filename: Optional[str] = None,
- include_dirs: Optional[List[str]] = None) -> Tuple[Optional[str], str]:
- """
- ``preprocess_source`` run the C preprocessor on the given source or source filename.
-
- :param str source: source to pre-process
- :param str filename: optional filename to pre-process
- :param include_dirs: list of string directories to use as include directories.
- :type include_dirs: list(str)
- :return: returns a tuple of (preprocessed_source, error_string)
- :rtype: tuple(str,str)
- :Example:
-
- >>> source = "#define TEN 10\\nint x[TEN];\\n"
- >>> preprocess_source(source)
- ('#line 1 "input"\\n\\n#line 2 "input"\\n int x [ 10 ] ;\\n', '')
- >>>
- """
- if filename is None:
- filename = "input"
- 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')
- output = ctypes.c_char_p()
- errors = ctypes.c_char_p()
- result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs))
- assert output.value is not None
- assert errors.value is not None
- output_str = output.value.decode('utf-8')
- error_str = errors.value.decode('utf-8')
- core.free_string(output)
- core.free_string(errors)
- if result:
- return output_str, error_str
- return None, error_str
-
-
-@dataclass(frozen=True)
class TypeFieldReference:
func: Optional['_function.Function']
arch: Optional['architecture.Architecture']