diff options
| -rw-r--r-- | binaryninjaapi.h | 30 | ||||
| -rw-r--r-- | binaryninjacore.h | 30 | ||||
| -rw-r--r-- | datarenderer.cpp | 184 | ||||
| -rw-r--r-- | python/__init__.py | 1 | ||||
| -rw-r--r-- | python/datarender.py | 181 | ||||
| -rw-r--r-- | python/downloadprovider.py | 3 | ||||
| -rw-r--r-- | python/types.py | 14 | ||||
| -rw-r--r-- | type.cpp | 20 |
8 files changed, 460 insertions, 3 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index 2b3273a8..958697c7 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -2185,6 +2185,9 @@ namespace BinaryNinja public: Type(BNType* type); + bool operator==(const Type& other); + bool operator!=(const Type& other); + BNTypeClass GetClass() const; uint64_t GetWidth() const; size_t GetAlignment() const; @@ -2209,6 +2212,7 @@ namespace BinaryNinja void SetVolatile(const Confidence<bool>& vltl); void SetTypeName(const QualifiedName& name); Confidence<int64_t> GetStackAdjustment() const; + QualifiedName GetStructureName() const; uint64_t GetElementCount() const; uint64_t GetOffset() const; @@ -4324,4 +4328,30 @@ namespace BinaryNinja bool IsArray() const; bool IsKeyValueStore() const; }; + + class DataRenderer: public CoreRefCountObject<BNDataRenderer, BNNewDataRendererReference, BNFreeDataRenderer> + { + static bool IsValidForDataCallback(void* ctxt, BNBinaryView* data, uint64_t addr, BNType* type, + BNType** typeCtx, size_t ctxCount); + static BNDisassemblyTextLine* GetLinesForDataCallback(void* ctxt, BNBinaryView* data, uint64_t addr, BNType* type, + const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, BNType** typeCxt, + size_t ctxCount); + static void FreeCallback(void* ctxt); + public: + DataRenderer(); + DataRenderer(BNDataRenderer* renderer); + virtual bool IsValidForData(BinaryView* data, uint64_t addr, Type* type, std::vector<Type*>& context); + virtual std::vector<DisassemblyTextLine> GetLinesForData(BinaryView* data, uint64_t addr, Type* type, + const std::vector<InstructionTextToken>& prefix, size_t width, std::vector<Type*>& context); + + static bool IsStructOfTypeName(Type* type, const QualifiedName& name, std::vector<Type*>& context); + static bool IsStructOfTypeName(Type* type, const std::string& name, std::vector<Type*>& context); + }; + + class DataRendererContainer + { + public: + static void RegisterGenericDataRenderer(DataRenderer* renderer); + static void RegisterTypeSpecificDataRenderer(DataRenderer* renderer); + }; } diff --git a/binaryninjacore.h b/binaryninjacore.h index 6dd5ae85..161a23af 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -156,6 +156,9 @@ extern "C" struct BNSection; struct BNRelocationInfo; struct BNRelocationHandler; + struct BNDataBuffer; + struct BNDataRenderer; + struct BNDataRendererContainer; typedef bool (*BNLoadPluginCallback)(const char* repoPath, const char* pluginPath, void* ctx); @@ -1818,6 +1821,17 @@ extern "C" void (*destructFunction)(void* ctxt, BNFunction* func); }; + struct BNCustomDataRenderer + { + void* context; + void (*freeObject)(void* ctxt); + bool (*isValidForData)(void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, BNType** typeCtx, + size_t ctxCount); + BNDisassemblyTextLine* (*getLinesForData)(void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, + const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, BNType** typeCtx, + size_t ctxCount); + }; + enum BNSegmentFlag { SegmentExecutable = 1, @@ -3131,6 +3145,8 @@ extern "C" BINARYNINJACOREAPI BNTypeWithConfidence BNGetMediumLevelILExprType(BNMediumLevelILFunction* func, size_t expr); // Types + BINARYNINJACOREAPI bool BNTypesEqual(BNType* a, BNType* b); + BINARYNINJACOREAPI bool BNTypesNotEqual(BNType* a, BNType* b); BINARYNINJACOREAPI BNType* BNCreateVoidType(void); BINARYNINJACOREAPI BNType* BNCreateBoolType(void); BINARYNINJACOREAPI BNType* BNCreateIntegerType(size_t width, BNBoolWithConfidence* sign, const char* altName); @@ -3178,6 +3194,7 @@ extern "C" BINARYNINJACOREAPI void BNTypeSetConst(BNType* type, BNBoolWithConfidence* cnst); BINARYNINJACOREAPI void BNTypeSetVolatile(BNType* type, BNBoolWithConfidence* vltl); BINARYNINJACOREAPI BNOffsetWithConfidence BNGetTypeStackAdjustment(BNType* type); + BINARYNINJACOREAPI BNQualifiedName BNTypeGetStructureName(BNType* type); BINARYNINJACOREAPI char* BNGetTypeString(BNType* type, BNPlatform* platform); BINARYNINJACOREAPI char* BNGetTypeStringBeforeName(BNType* type, BNPlatform* platform); @@ -3776,6 +3793,19 @@ extern "C" BINARYNINJACOREAPI uint64_t BNSectionGetEntrySize(BNSection* section); BINARYNINJACOREAPI BNSectionSemantics BNSectionGetSemantics(BNSection* section); BINARYNINJACOREAPI bool BNSectionIsAutoDefined(BNSection* section); + + // Custom Data Render methods + BINARYNINJACOREAPI BNDataRenderer* BNCreateDataRenderer(BNCustomDataRenderer* renderer); + BINARYNINJACOREAPI BNDataRenderer* BNNewDataRendererReference(BNDataRenderer* renderer); + BINARYNINJACOREAPI bool BNIsValidForData(void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, + BNType** typeCtx, size_t ctxCount); + BINARYNINJACOREAPI BNDisassemblyTextLine* BNGetLinesForData(void* ctxt, BNBinaryView* view, uint64_t addr, + BNType* type, const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, + BNType** typeCtx, size_t ctxCount); + BINARYNINJACOREAPI void BNFreeDataRenderer(BNDataRenderer* renderer); + BINARYNINJACOREAPI BNDataRendererContainer* BNGetDataRendererContainer(); + BINARYNINJACOREAPI void BNRegisterGenericDataRenderer(BNDataRendererContainer* container, BNDataRenderer* renderer); + BINARYNINJACOREAPI void BNRegisterTypeSpecificDataRenderer(BNDataRendererContainer* container, BNDataRenderer* renderer); #ifdef __cplusplus } #endif diff --git a/datarenderer.cpp b/datarenderer.cpp new file mode 100644 index 00000000..1fce92a7 --- /dev/null +++ b/datarenderer.cpp @@ -0,0 +1,184 @@ +#include "binaryninjaapi.h" + +using namespace std; +using namespace BinaryNinja; + + +DataRenderer::DataRenderer(BNDataRenderer* renderer) +{ + m_object = renderer; +} + + +DataRenderer::DataRenderer() +{ + BNCustomDataRenderer renderer; + renderer.context = this; + renderer.freeObject = FreeCallback; + renderer.isValidForData = IsValidForDataCallback; + renderer.getLinesForData = GetLinesForDataCallback; + AddRefForRegistration(); + m_object = BNCreateDataRenderer(&renderer); +} + + +bool DataRenderer::IsStructOfTypeName(Type* type, const QualifiedName& name, vector<Type*>& context) +{ + return (type->GetClass() == StructureTypeClass) && + (context.size() > 0) && + (context[0]->GetClass() == NamedTypeReferenceClass) && + (context[0]->GetNamedTypeReference()->GetName() == name); +} + + +bool DataRenderer::IsStructOfTypeName(Type* type, const string& name, vector<Type*>& context) +{ + return DataRenderer::IsStructOfTypeName(type, QualifiedName(name), context); +} + + +bool DataRenderer::IsValidForDataCallback(void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, + BNType** typeCtx, size_t ctxCount) +{ + DataRenderer* renderer = (DataRenderer*)ctxt; + Ref<BinaryView> viewObj = new BinaryView(BNNewViewReference(view)); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + vector<Type*> context; + context.reserve(ctxCount); + for (size_t i = 0; i < ctxCount; i++) + context.push_back(new Type(BNNewTypeReference(typeCtx[i]))); + + return renderer->IsValidForData(viewObj, addr, typeObj, context); +} + + +BNDisassemblyTextLine* DataRenderer::GetLinesForDataCallback(void* ctxt, BNBinaryView* view, uint64_t addr, BNType* type, + const BNInstructionTextToken* prefix, size_t prefixCount, size_t width, size_t* count, BNType** typeCtx, + size_t ctxCount) +{ + DataRenderer* renderer = (DataRenderer*)ctxt; + Ref<BinaryView> viewObj = new BinaryView(BNNewViewReference(view)); + Ref<Type> typeObj = new Type(BNNewTypeReference(type)); + vector<InstructionTextToken> prefixes; + prefixes.reserve(prefixCount); + for (size_t i = 0; i < prefixCount; i++) + { + prefixes.emplace_back(prefix[i].type, prefix[i].context, prefix[i].text, prefix[i].address, + prefix[i].value, prefix[i].size, prefix[i].operand, prefix[i].confidence); + } + vector<Type*> context; + context.reserve(ctxCount); + for (size_t i = 0; i < ctxCount; i++) + context.push_back(new Type(BNNewTypeReference(typeCtx[i]))); + auto lines = renderer->GetLinesForData(viewObj, addr, typeObj, prefixes, width, context); + *count = lines.size(); + BNDisassemblyTextLine* buf = new BNDisassemblyTextLine[lines.size()]; + for (size_t i = 0; i < lines.size(); i++) + { + const DisassemblyTextLine& line = lines[i]; + buf[i].addr = line.addr; + buf[i].instrIndex = line.instrIndex; + buf[i].highlight = line.highlight; + buf[i].tokens = new BNInstructionTextToken[line.tokens.size()]; + buf[i].count = line.tokens.size(); + for (size_t j = 0; j < line.tokens.size(); j++) + { + const InstructionTextToken& token = line.tokens[j]; + buf[i].tokens[j].type = token.type; + buf[i].tokens[j].text = BNAllocString(token.text.c_str()); + buf[i].tokens[j].value = token.value; + buf[i].tokens[j].size = token.size; + buf[i].tokens[j].operand = token.operand; + buf[i].tokens[j].context = token.context; + buf[i].tokens[j].confidence = token.confidence; + buf[i].tokens[j].address = token.address; + } + } + return buf; +} + + +void DataRenderer::FreeCallback(void* ctxt) +{ + DataRenderer* renderer = (DataRenderer*)ctxt; + renderer->ReleaseForRegistration(); +} + + +bool DataRenderer::IsValidForData(BinaryView* data, uint64_t addr, Type* type, vector<Type*>& context) +{ + BNType** typeCtx = new BNType*[context.size()]; + for (size_t i = 0; i < context.size(); i++) + typeCtx[i] = context[i]->GetObject(); + bool result = BNIsValidForData(m_object, data->GetObject(), addr, type->GetObject(), typeCtx, context.size()); + delete[] typeCtx; + return result; +} + + +vector<DisassemblyTextLine> DataRenderer::GetLinesForData(BinaryView* data, uint64_t addr, Type* type, + const std::vector<InstructionTextToken>& prefix, size_t width, vector<Type*>& context) +{ + BNInstructionTextToken* prefixes = new BNInstructionTextToken[prefix.size()]; + for (size_t i = 0; i < prefix.size(); i++) + { + prefixes[i].type = prefix[i].type; + prefixes[i].text = BNAllocString(prefix[i].text.c_str()); + prefixes[i].value = prefix[i].value; + prefixes[i].size = prefix[i].size; + prefixes[i].operand = prefix[i].operand; + prefixes[i].context = prefix[i].context; + prefixes[i].confidence = prefix[i].confidence; + prefixes[i].address = prefix[i].address; + } + BNType** typeCtx = new BNType*[context.size()]; + for (size_t i = 0; i < context.size(); i++) + typeCtx[i] = context[i]->GetObject(); + + size_t count = 0; + BNDisassemblyTextLine* lines = BNGetLinesForData(m_object, data->GetObject(), addr, type->GetObject(), prefixes, + prefix.size(), width, &count, typeCtx, context.size()); + + delete[] typeCtx; + for (size_t i = 0; i < prefix.size(); i++) + BNFreeString(prefixes[i].text); + + delete[] prefixes; + vector<DisassemblyTextLine> result; + result.reserve(count); + for (size_t i = 0; i < count; i++) + { + DisassemblyTextLine line; + line.addr = lines[i].addr; + line.instrIndex = lines[i].instrIndex; + line.highlight = lines[i].highlight; + line.tokens.reserve(lines[i].count); + for (size_t j = 0; j < lines[i].count; j++) + { + InstructionTextToken token; + token.type = lines[i].tokens[j].type; + token.text = lines[i].tokens[j].text; + token.value = lines[i].tokens[j].value; + token.size = lines[i].tokens[j].size; + token.operand = lines[i].tokens[j].operand; + token.context = lines[i].tokens[j].context; + token.confidence = lines[i].tokens[j].confidence; + token.address = lines[i].tokens[j].address; + line.tokens.push_back(token); + } + result.push_back(line); + } + return result; +} + + +void DataRendererContainer::RegisterGenericDataRenderer(DataRenderer* renderer) +{ + BNRegisterGenericDataRenderer(BNGetDataRendererContainer(), renderer->GetObject()); +} + + +void DataRendererContainer::RegisterTypeSpecificDataRenderer(DataRenderer* renderer) +{ + BNRegisterTypeSpecificDataRenderer(BNGetDataRendererContainer(), renderer->GetObject()); +}
\ No newline at end of file diff --git a/python/__init__.py b/python/__init__.py index 2c6cb311..d2140f23 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -128,6 +128,7 @@ from binaryninja.pluginmanager import * from binaryninja.settings import * from binaryninja.metadata import * from binaryninja.flowgraph import * +from binaryninja.datarender import * def shutdown(): diff --git a/python/datarender.py b/python/datarender.py new file mode 100644 index 00000000..db60f42e --- /dev/null +++ b/python/datarender.py @@ -0,0 +1,181 @@ +# Copyright (c) 2015-2017 Vector 35 LLC +# +# 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 traceback +import ctypes + +import binaryninja +from binaryninja import _binaryninjacore as core +from binaryninja.filemetadata import FileMetadata +from binaryninja.binaryview import BinaryView +from binaryninja.function import (DisassemblyTextLine, InstructionTextToken) +from binaryninja.enums import InstructionTextTokenType, TypeClass, HighlightStandardColor +from binaryninja.log import log_error +from binaryninja.types import Type +from binaryninja import highlight + +class DataRenderer(object): + """ + DataRender objects tell the Linear View how to render specific types. + + The `perform_is_valid_for_data` method returns a boolean to indicate if your derrived class + is able to render the type, given the `addr` and `context`. The `context` is a list of Type + objects which represents the chain of nested objects that is being displayed. + + The `perform_get_lines_for_data` method returns a list of `DisassemblyTextLine` objects each one + representing a single line of Linear View output. The `prefix` variable is a list of `InstructionTextToken`'s + which have already been generated by other `DataRender`'s. + + After defining the `DataRenderer` subclass you must then register it with the core. This is done by calling + either `register_type_sepcific` or `register_generic`. A "generic" type renderer is able to be overridden by + a "type specific" renderer. For instance there is a generic struct render which renders any struct that hasn't + been explicitly overridden by a "type specific" renderer. + + In the below example we create a data renderer that overrides the default display for `struct BAR`. + + class BarDataRenderer(DataRenderer): + def __init__(self): + DataRenderer.__init__(self) + + def perform_is_valid_for_data(self, ctxt, view, addr, type, context): + return DataRenderer.is_type_of_struct_name(type, "BAR", context) + + def perform_get_lines_for_data(self, ctxt, view, addr, type, prefix, width, context): + prefix.append(InstructionTextToken(InstructionTextTokenType.TextToken, "I'm in ur BAR")) + return [DisassemblyTextLine(prefix, addr)] + + def __del__(self): + pass + + BarDataRenderer().register_type_sepcific() + """ + def __init__(self, context=None): + self._cb = core.BNCustomDataRenderer() + self._cb.context = context + self._cb.freeObject = self._cb.freeObject.__class__(self._free_object) + self._cb.isValidForData = self._cb.isValidForData.__class__(self._is_valid_for_data) + self._cb.getLinesForData = self._cb.getLinesForData.__class__(self._get_lines_for_data) + self.handle = core.BNCreateDataRenderer(self._cb) + + @classmethod + def is_type_of_struct_name(cls, type, name, context): + return (type.type_class == TypeClass.StructureTypeClass and len(context) > 0 + and context[0].type_class == TypeClass.NamedTypeReferenceClass and + context[0].named_type_reference.name == name) + + def register_type_sepcific(self): + core.BNRegisterTypeSpecificDataRenderer(core.BNGetDataRendererContainer(), self.handle) + + def register_generic(self): + core.BNRegisterGenericDataRenderer(core.BNGetDataRendererContainer(), self.handle) + + def _free_object(self, ctxt): + try: + self.perform_free_object(ctxt) + except: + log_error(traceback.format_exc()) + + def _is_valid_for_data(self, ctxt, view, addr, type, context, ctxCount): + try: + file_metadata = FileMetadata(handle=core.BNGetFileForView(view)) + view = BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(view)) + type = Type(handle=core.BNNewTypeReference(type)) + pycontext = [] + for i in range(0, ctxCount): + pycontext.append(Type(core.BNNewTypeReference(context[i]))) + return self.perform_is_valid_for_data(ctxt, view, addr, type, pycontext) + except: + log_error(traceback.format_exc()) + return False + + def _get_lines_for_data(self, ctxt, view, addr, type, prefix, prefixCount, width, count, typeCtx, ctxCount): + try: + file_metadata = FileMetadata(handle=core.BNGetFileForView(view)) + view = BinaryView(file_metadata=file_metadata, handle=core.BNNewViewReference(view)) + type = Type(handle=core.BNNewTypeReference(type)) + + prefixTokens = [] + for i in range(prefixCount): + token_type = InstructionTextTokenType(prefix[i].type) + text = prefix[i].text + value = prefix[i].value + size = prefix[i].size + operand = prefix[i].operand + context = prefix[i].context + address = prefix[i].address + confidence = prefix[i].confidence + prefixTokens.append(InstructionTextToken(token_type, text, value, size, operand, context, address, confidence)) + + pycontext = [] + for i in range(ctxCount): + pycontext.append(Type(core.BNNewTypeReference(typeCtx[i]))) + + result = self.perform_get_lines_for_data(ctxt, view, addr, type, prefixTokens, width, pycontext) + + count[0] = len(result) + line_buf = (core.BNDisassemblyTextLine * len(result))() + for i in range(len(result)): + line = result[i] + color = line.highlight + if not isinstance(color, HighlightStandardColor) and not isinstance(color, highlight.HighlightColor): + raise ValueError("Specified color is not one of HighlightStandardColor, highlight.HighlightColor") + if isinstance(color, HighlightStandardColor): + color = highlight.HighlightColor(color) + line_buf[i].highlight = color._get_core_struct() + line_buf[i].count = len(line.tokens) + line_buf[i].tokens = (core.BNInstructionTextToken * len(line.tokens))() + if line.address is None: + if len(line.tokens) > 0: + line_buf[i].addr = line.tokens[0].address + else: + line_buf[i].addr = 0 + else: + line_buf[i].addr = line.address + if line.il_instruction is not None: + line_buf[i].instrIndex = line.il_instruction.instr_index + else: + line_buf[i].instrIndex = 0xffffffffffffffff + for j in range(len(line.tokens)): + line_buf[i].tokens[j].type = line.tokens[j].type + line_buf[i].tokens[j].text = line.tokens[j].text + line_buf[i].tokens[j].value = line.tokens[j].value + line_buf[i].tokens[j].size = line.tokens[j].size + line_buf[i].tokens[j].operand = line.tokens[j].operand + line_buf[i].tokens[j].context = line.tokens[j].context + line_buf[i].tokens[j].confidence = line.tokens[j].confidence + line_buf[i].tokens[j].address = line.tokens[j].address + + return ctypes.cast(line_buf, ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + def perform_free_object(self, ctxt): + pass + + def perform_is_valid_for_data(self, ctxt, view, addr, type, context): + return False + + def perform_get_lines_for_data(self, ctxt, view, addr, type, prefix, width, context): + return [] + + def __del__(self): + pass diff --git a/python/downloadprovider.py b/python/downloadprovider.py index 5ce8cfce..53bd488a 100644 --- a/python/downloadprovider.py +++ b/python/downloadprovider.py @@ -58,7 +58,8 @@ class DownloadInstance(object): def _destroy_instance(self, ctxt): try: - self.__class__._registered_instances.remove(self) + if self in self.__class__._registered_instances: + self.__class__._registered_instances.remove(self) self.perform_destroy_instance() except: log.log_error(traceback.format_exc()) diff --git a/python/types.py b/python/types.py index c7ebaf19..4de55700 100644 --- a/python/types.py +++ b/python/types.py @@ -266,12 +266,12 @@ class Type(object): def __eq__(self, value): if not isinstance(value, Type): return False - return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) + return core.BNTypesEqual(self.handle, value.handle) def __ne__(self, value): if not isinstance(value, Type): return True - return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) + return core.BNTypesNotEqual(self.handle, value.handle) @property def type_class(self): @@ -300,6 +300,16 @@ class Type(object): result = core.BNIsTypeConst(self.handle) return BoolWithConfidence(result.value, confidence = result.confidence) + @const.setter + def const(self, value): + bc = core.BNBoolWithConfidence() + bc.value = bool(value) + if hasattr(value, 'confidence'): + bc.confidence = value.confidence + else: + bc.confidence = max_confidence + core.BNTypeSetConst(self.handle, bc) + @property def modified(self): """Whether type is modified (read-only)""" @@ -441,6 +441,17 @@ Type::Type(BNType* type) m_object = type; } +bool Type::operator==(const Type& other) +{ + return BNTypesEqual(m_object, other.m_object); +} + + +bool Type::operator!=(const Type& other) +{ + return BNTypesNotEqual(m_object, other.m_object); +} + BNTypeClass Type::GetClass() const { @@ -964,6 +975,15 @@ Confidence<Ref<Type>> Type::WithConfidence(uint8_t conf) } +QualifiedName Type::GetStructureName() const +{ + BNQualifiedName name = BNTypeGetStructureName(m_object); + QualifiedName result = QualifiedName::FromAPIObject(&name); + BNFreeQualifiedName(&name); + return result; +} + + NamedTypeReference::NamedTypeReference(BNNamedTypeReference* nt) { m_object = nt; |
