Skip to content

Commit

Permalink
Extra options for type parsers
Browse files Browse the repository at this point in the history
  • Loading branch information
CouleeApps committed Nov 11, 2022
1 parent 772ab3c commit 14d91ab
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 3 deletions.
12 changes: 12 additions & 0 deletions binaryninjaapi.h
Expand Up @@ -11529,6 +11529,7 @@ namespace BinaryNinja {
explicit TypeParser(const std::string& name);
TypeParser(BNTypeParser* parser);

static bool GetOptionTextCallback(void* ctxt, BNTypeParserOption option, const char* value, char** result);
static bool PreprocessSourceCallback(void* ctxt,
const char* source, const char* fileName, BNPlatform* platform,
const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
Expand Down Expand Up @@ -11560,6 +11561,15 @@ namespace BinaryNinja {
static Ref<TypeParser> GetByName(const std::string& name);
static Ref<TypeParser> GetDefault();

/**
Get the string representation of an option for passing to ParseTypes*
\param option Option type
\param value Option value
\param result String representing the option
\return True if the parser supports the option
*/
virtual bool GetOptionText(BNTypeParserOption option, std::string value, std::string& result) const;

/*!
Preprocess a block of source, returning the source that would be parsed
\param source Source code to process
Expand Down Expand Up @@ -11635,6 +11645,8 @@ namespace BinaryNinja {
CoreTypeParser(BNTypeParser* parser);
virtual ~CoreTypeParser() {}

virtual bool GetOptionText(BNTypeParserOption option, std::string value, std::string& result) const override;

virtual bool PreprocessSource(
const std::string& source,
const std::string& fileName,
Expand Down
13 changes: 11 additions & 2 deletions binaryninjacore.h
Expand Up @@ -36,14 +36,14 @@
// Current ABI version for linking to the core. This is incremented any time
// there are changes to the API that affect linking, including new functions,
// new types, or modifications to existing functions or types.
#define BN_CURRENT_CORE_ABI_VERSION 27
#define BN_CURRENT_CORE_ABI_VERSION 28

// Minimum ABI version that is supported for loading of plugins. Plugins that
// are linked to an ABI version less than this will not be able to load and
// will require rebuilding. The minimum version is increased when there are
// incompatible changes that break binary compatibility, such as changes to
// existing types or functions.
#define BN_MINIMUM_CORE_ABI_VERSION 24
#define BN_MINIMUM_CORE_ABI_VERSION 28

#ifdef __GNUC__
#ifdef BINARYNINJACORE_LIBRARY
Expand Down Expand Up @@ -2104,6 +2104,12 @@ extern "C"
BNLowLevelILFunction* il, BNRelocation* relocation);
};

enum BNTypeParserOption
{
IncludeSystemTypes,
BuiltinMacros,
};

struct BNParsedType
{
BNQualifiedName name;
Expand Down Expand Up @@ -2515,6 +2521,7 @@ extern "C"
struct BNTypeParserCallbacks
{
void* context;
bool (*getOptionText)(void* ctxt, BNTypeParserOption option, const char* value, char** result);
bool (*preprocessSource)(void* ctxt,
const char* source, const char* fileName, BNPlatform* platform,
const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
Expand Down Expand Up @@ -5484,6 +5491,8 @@ extern "C"

BINARYNINJACOREAPI char* BNGetTypeParserName(BNTypeParser* parser);

BINARYNINJACOREAPI bool BNGetTypeParserOptionText(BNTypeParser* parser, BNTypeParserOption option,
const char* value, char** result);
BINARYNINJACOREAPI bool BNTypeParserPreprocessSource(BNTypeParser* parser,
const char* source, const char* fileName, BNPlatform* platform,
const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
Expand Down
70 changes: 69 additions & 1 deletion python/typeparser.py
Expand Up @@ -35,7 +35,7 @@
from . import platform
from . import types
from .log import log_error
from .enums import TypeParserErrorSeverity
from .enums import TypeParserErrorSeverity, TypeParserOption


@dataclasses.dataclass(frozen=True)
Expand Down Expand Up @@ -199,6 +199,10 @@ def __getitem__(self, value):

@property
def default(self):
"""
Get the default parser as specified by the user's settings
:return: Default parser
"""
name = binaryninja.Settings().get_string("analysis.types.parserName")
return CoreTypeParser[name]

Expand All @@ -216,10 +220,14 @@ def __init__(self, handle=None):
self.__dict__["name"] = core.BNGetTypeParserName(handle)

def register(self):
"""
Register a custom parser with the API
"""
assert self.__class__.name is not None

self._cb = core.BNTypeParserCallbacks()
self._cb.context = 0
self._cb.getOptionText = self._cb.getOptionText.__class__(self._get_option_text)
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)
Expand All @@ -235,6 +243,19 @@ def __str__(self):
def __repr__(self):
return f'<TypeParser: {self.name}>'

def _get_option_text(self, option, value, result) -> bool:
try:
value_py = core.pyNativeStr(value)
result_py = self.get_option_text(option, value_py)
if result_py is None:
return False
TypeParser._cached_string = core.cstr(result_py)
result[0] = TypeParser._cached_string
return True
except:
log_error(traceback.format_exc())
return False

def _preprocess_source(
self, ctxt, source, fileName, platform_, existingTypes, existingTypeCount,
options, optionCount, includeDirs, includeDirCount,
Expand Down Expand Up @@ -401,11 +422,31 @@ def _free_error_list(
log_error(traceback.format_exc())
return False

def get_option_text(self, option: TypeParserOption, value: str) -> Optional[str]:
"""
Get the string representation of an option for passing to parse_type_*
:param option: Option type
:param value: Option value
:return: A string representing the option if the parser supports it, otherwise None
"""
return None

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]]:
"""
Preprocess a block of source, returning the source that would be parsed
:param source: Source code to process
:param file_name: Name of the file containing the source (does not need to exist on disk)
:param platform: Platform to assume the source is relevant to
:param existing_types: Optional map of all existing types to use for parsing context
:param options: Optional string arguments to pass as options, e.g. command line arguments
:param include_dirs: Optional list of directories to include in the header search path
:return: A tuple of (preproccessed source, errors), where the preproccessed source
is None if there was a fatal error.
"""
raise NotImplementedError("Not implemented")

def parse_types_from_source(
Expand All @@ -414,17 +455,44 @@ def parse_types_from_source(
options: Optional[List[str]] = None, include_dirs: Optional[List[str]] = None,
auto_type_source: str = ""
) -> Tuple[Optional[TypeParserResult], List[TypeParserError]]:
"""
Parse an entire block of source into types, variables, and functions
:param source: Source code to parse
:param file_name: Name of the file containing the source (optional: exists on disk)
:param platform: Platform to assume the types are relevant to
:param existing_types: Optional map of all existing types to use for parsing context
:param options: Optional string arguments to pass as options, e.g. command line arguments
:param include_dirs: Optional list of directories to include in the header search path
:param auto_type_source: Optional source of types if used for automatically generated types
:return: A tuple of (result, errors) where the result is None if there was a fatal error
"""
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]]:
"""
Parse a single type and name from a string containing their definition.
:param source: Source code to parse
:param platform: Platform to assume the types are relevant to
:param existing_types: Optional map of all existing types to use for parsing context
:return: A tuple of (result, errors) where result is a tuple of (type, name) or
None of there was a fatal error.
"""
raise NotImplementedError("Not implemented")


class CoreTypeParser(TypeParser):

def get_option_text(self, option: TypeParserOption, value: str) -> Optional[str]:
result_cpp = ctypes.c_char_p()
if not core.BNGetTypeParserOptionText(self.handle, option, value, result_cpp):
return None
result = core.pyNativeStr(result_cpp.value)
core.free_string(result_cpp)
return result

def preprocess_source(
self, source: str, file_name: str, platform: 'platform.Platform',
existing_types: Optional[List[QualifiedNameTypeAndId]] = None,
Expand Down
33 changes: 33 additions & 0 deletions typeparser.cpp
Expand Up @@ -46,6 +46,7 @@ void TypeParser::Register(TypeParser* parser)
{
BNTypeParserCallbacks cb;
cb.context = parser;
cb.getOptionText = GetOptionTextCallback;
cb.preprocessSource = PreprocessSourceCallback;
cb.parseTypesFromSource = ParseTypesFromSourceCallback;
cb.parseTypeString = ParseTypeStringCallback;
Expand All @@ -56,6 +57,27 @@ void TypeParser::Register(TypeParser* parser)
}


bool TypeParser::GetOptionText(BNTypeParserOption option, std::string value, std::string& result) const
{
// Default: Don't accept anything
return false;
}


bool TypeParser::GetOptionTextCallback(void* ctxt, BNTypeParserOption option, const char* value,
char** result)
{
TypeParser* parser = (TypeParser*)ctxt;
string resultCpp;
if (!parser->GetOptionText(option, value, resultCpp))
{
return false;
}
*result = BNAllocString(resultCpp.c_str());
return true;
}


bool TypeParser::PreprocessSourceCallback(void* ctxt,
const char* source, const char* fileName, BNPlatform* platform,
const BNQualifiedNameTypeAndId* existingTypes, size_t existingTypeCount,
Expand Down Expand Up @@ -295,6 +317,17 @@ CoreTypeParser::CoreTypeParser(BNTypeParser* parser): TypeParser(parser)
}


bool CoreTypeParser::GetOptionText(BNTypeParserOption option, std::string value, std::string& result) const
{
char* apiResult;
if (!BNGetTypeParserOptionText(m_object, option, value.c_str(), &apiResult))
return false;
result = apiResult;
BNFreeString(apiResult);
return true;
}


bool CoreTypeParser::PreprocessSource(const std::string& source, const std::string& fileName,
Ref<Platform> platform, const std::map<QualifiedName, TypeAndId>& existingTypes,
const std::vector<std::string>& options, const std::vector<std::string>& includeDirs,
Expand Down

0 comments on commit 14d91ab

Please sign in to comment.