// Copyright (c) 2015-2026 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.
// TODO : Documentation
#include "binaryninjaapi.h"
using namespace BinaryNinja;
using namespace std;
///////////////
// DebugInfo //
///////////////
DebugInfo::DebugInfo(BNDebugInfo* debugInfo)
{
m_object = debugInfo;
}
vector DebugInfo::GetParsers() const
{
size_t count;
char** parsers = BNGetDebugParserNames(m_object, &count);
vector result;
result.reserve(count);
for (size_t i = 0; i < count; ++i)
{
result.emplace_back(parsers[i]);
}
BNFreeStringList(parsers, count);
return result;
}
TypeContainer DebugInfo::GetTypeContainer(const std::string& parserName)
{
return TypeContainer(BNGetDebugInfoTypeContainer(m_object, parserName.c_str()));
}
vector DebugInfo::GetTypes(const string& parserName) const
{
size_t count;
BNNameAndType* nameAndTypes =
BNGetDebugTypes(m_object, parserName.size() == 0 ? nullptr : parserName.c_str(), &count);
if (nameAndTypes == nullptr)
return {};
vector result;
result.reserve(count);
for (size_t i = 0; i < count; ++i)
{
result.emplace_back(nameAndTypes[i].name,
Confidence(new Type(BNNewTypeReference(nameAndTypes[i].type)), nameAndTypes[i].typeConfidence));
}
BNFreeDebugTypes(nameAndTypes, count);
return result;
}
vector DebugInfo::GetFunctions(const string& parserName) const
{
size_t count;
BNDebugFunctionInfo* functions =
BNGetDebugFunctions(m_object, parserName.size() == 0 ? nullptr : parserName.c_str(), &count);
if (functions == nullptr)
return {};
vector result;
result.reserve(count);
for (size_t i = 0; i < count; ++i)
{
vector components;
components.reserve(functions[i].componentN);
for (size_t componentN = 0; componentN < functions[i].componentN; ++componentN)
components.emplace_back(functions[i].components[componentN]);
vector localVariables;
localVariables.reserve(functions[i].localVariableN);
for (size_t localVariableN = 0; localVariableN < functions[i].localVariableN; ++localVariableN)
{
auto& bnVar = functions[i].localVariables[localVariableN];
VariableNameAndType vnt;
vnt.var = bnVar.var;
vnt.name = bnVar.name;
vnt.autoDefined = bnVar.autoDefined;
vnt.type = Confidence(new Type(BNNewTypeReference(bnVar.type)), bnVar.typeConfidence);
localVariables.push_back(vnt);
}
result.emplace_back(functions[i].shortName ? functions[i].shortName : "",
functions[i].fullName ? functions[i].fullName : "", functions[i].rawName ? functions[i].rawName : "",
functions[i].address,
functions[i].type ? new Type(BNNewTypeReference(functions[i].type)) : nullptr,
functions[i].platform ? new CorePlatform(BNNewPlatformReference(functions[i].platform)) : nullptr,
components, localVariables);
}
BNFreeDebugFunctions(functions, count);
return result;
}
vector DebugInfo::GetDataVariables(const string& parserName) const
{
size_t count;
BNDataVariableAndName* variablesAndName =
BNGetDebugDataVariables(m_object, parserName.size() == 0 ? nullptr : parserName.c_str(), &count);
if (variablesAndName == nullptr)
return {};
vector result;
result.reserve(count);
for (size_t i = 0; i < count; ++i)
{
result.emplace_back(variablesAndName[i].address,
Confidence(new Type(BNNewTypeReference(variablesAndName[i].type)), variablesAndName[i].typeConfidence),
variablesAndName[i].autoDiscovered, variablesAndName[i].name);
}
BNFreeDataVariablesAndName(variablesAndName, count);
return result;
}
// May return nullptr
Ref DebugInfo::GetTypeByName(const string& parserName, const string& name) const
{
BNType* result = BNGetDebugTypeByName(m_object, parserName.c_str(), name.c_str());
if (result)
return Ref(new Type(result));
return nullptr;
}
optional DebugInfo::GetDataVariableByName(
const string& parserName, const string& name) const
{
BNDataVariableAndName result;
if (!BNGetDebugDataVariableByName(m_object, parserName.c_str(), name.c_str(), &result))
return std::nullopt;
Ref type = new Type(BNNewTypeReference(result.type));
BNFreeDataVariableAndName(&result);
return {{result.address, type}};
}
optional DebugInfo::GetDataVariableByAddress(
const string& parserName, const uint64_t address) const
{
BNDataVariableAndName result;
if (!BNGetDebugDataVariableByAddress(m_object, parserName.c_str(), address, &result))
return std::nullopt;
string name = result.name;
Ref type = new Type(BNNewTypeReference(result.type));
BNFreeDataVariableAndName(&result);
return {{name, type}};
}
// The tuple is (DebugInfoParserName, type)
vector DebugInfo::GetTypesByName(const string& name) const
{
size_t count;
BNNameAndType* namesAndTypes = BNGetDebugTypesByName(m_object, name.c_str(), &count);
if (namesAndTypes == nullptr)
return {};
vector result;
result.reserve(count);
for (size_t i = 0; i < count; ++i)
{
result.emplace_back(namesAndTypes[i].name, Ref(new Type(BNNewTypeReference(namesAndTypes[i].type))));
}
BNFreeNameAndTypeList(namesAndTypes, count);
return result;
}
// The tuple is (DebugInfoParserName, address, type)
vector DebugInfo::GetDataVariablesByName(const string& name) const
{
size_t count;
BNDataVariableAndName* variablesAndName = BNGetDebugDataVariablesByName(m_object, name.c_str(), &count);
if (variablesAndName == nullptr)
return {};
vector result;
result.reserve(count);
for (size_t i = 0; i < count; ++i)
{
result.emplace_back(variablesAndName[i].name, variablesAndName[i].address,
Ref(new Type(BNNewTypeReference(variablesAndName[i].type))));
}
BNFreeDataVariablesAndName(variablesAndName, count);
return result;
}
// The tuple is (DebugInfoParserName, TypeName, type)
vector DebugInfo::GetDataVariablesByAddress(const uint64_t address) const
{
size_t count;
BNDataVariableAndNameAndDebugParser* variablesAndName = BNGetDebugDataVariablesByAddress(m_object, address, &count);
if (variablesAndName == nullptr)
return {};
vector result;
result.reserve(count);
for (size_t i = 0; i < count; ++i)
{
result.emplace_back(variablesAndName[i].parser, variablesAndName[i].name,
Ref(new Type(BNNewTypeReference(variablesAndName[i].type))));
}
BNFreeDataVariableAndNameAndDebugParserList(variablesAndName, count);
return result;
}
bool DebugInfo::RemoveParserInfo(const string& parserName)
{
return BNRemoveDebugParserInfo(m_object, parserName.c_str());
}
bool DebugInfo::RemoveParserTypes(const string& parserName)
{
return BNRemoveDebugParserTypes(m_object, parserName.c_str());
}
bool DebugInfo::RemoveParserFunctions(const string& parserName)
{
return BNRemoveDebugParserFunctions(m_object, parserName.c_str());
}
bool DebugInfo::RemoveParserDataVariables(const string& parserName)
{
return BNRemoveDebugParserDataVariables(m_object, parserName.c_str());
}
bool DebugInfo::RemoveTypeByName(const string& parserName, const string& name)
{
return BNRemoveDebugTypeByName(m_object, parserName.c_str(), name.c_str());
}
bool DebugInfo::RemoveFunctionByIndex(const string& parserName, const size_t index)
{
return BNRemoveDebugFunctionByIndex(m_object, parserName.c_str(), index);
}
bool DebugInfo::RemoveDataVariableByAddress(const string& parserName, const uint64_t address)
{
return BNRemoveDebugDataVariableByAddress(m_object, parserName.c_str(), address);
}
bool DebugInfo::AddType(const string& name, Ref type, const vector& components)
{
const char** const componentArray = new const char*[components.size()];
for (size_t i = 0; i < components.size(); ++i)
componentArray[i] = components[i].c_str();
bool result = BNAddDebugType(m_object, name.c_str(), type->GetObject(), componentArray, components.size());
delete[] componentArray;
return result;
}
bool DebugInfo::AddFunction(const DebugFunctionInfo& function)
{
char** components = new char*[function.components.size()];
for (size_t i = 0; i < function.components.size(); ++i)
components[i] = (char*)function.components[i].c_str();
BNVariableNameAndType* localVariables = new BNVariableNameAndType[function.localVariables.size()];
for (size_t i = 0; i < function.localVariables.size(); i++)
{
auto v = function.localVariables[i];
localVariables[i].var = v.var;
localVariables[i].type = v.type.GetValue()->m_object;
localVariables[i].typeConfidence = v.type.GetConfidence();
localVariables[i].name = (char*)v.name.c_str();
localVariables[i].autoDefined = v.autoDefined;
}
BNDebugFunctionInfo input;
input.shortName = function.shortName.size() ? BNAllocString(function.shortName.c_str()) : nullptr;
input.fullName = function.fullName.size() ? BNAllocString(function.fullName.c_str()) : nullptr;
input.rawName = function.rawName.size() ? BNAllocString(function.rawName.c_str()) : nullptr;
input.address = function.address;
input.type = function.type ? function.type->GetObject() : nullptr;
input.platform = function.platform ? function.platform->GetObject() : nullptr;
input.components = components;
input.componentN = function.components.size();
input.localVariables = localVariables;
input.localVariableN = function.localVariables.size();
bool result = BNAddDebugFunction(m_object, &input);
BNFreeString(input.shortName);
BNFreeString(input.fullName);
BNFreeString(input.rawName);
delete[] components;
delete[] localVariables;
return result;
}
bool DebugInfo::AddDataVariable(uint64_t address, Ref type, const string& name, const vector& components)
{
const char** const componentArray = new const char*[components.size()];
for (size_t i = 0; i < components.size(); ++i)
componentArray[i] = components[i].c_str();
if (name.size() == 0)
return BNAddDebugDataVariable(m_object, address, type->GetObject(), nullptr, componentArray, components.size());
return BNAddDebugDataVariable(m_object, address, type->GetObject(), name.c_str(), componentArray, components.size());
}
/////////////////////
// DebugInfoParser //
/////////////////////
DebugInfoParser::DebugInfoParser(BNDebugInfoParser* parser)
{
m_object = parser;
}
Ref DebugInfoParser::GetByName(const string& name)
{
BNDebugInfoParser* parser = BNGetDebugInfoParserByName(name.c_str());
if (parser)
return new DebugInfoParser(parser);
return nullptr;
}
vector DebugInfoParser::GetList()
{
size_t count = 0;
BNDebugInfoParser** parsers = BNGetDebugInfoParsers(&count);
vector result;
result.reserve(count);
for (size_t i = 0; i < count; ++i)
{
result.emplace_back(new DebugInfoParser(BNNewDebugInfoParserReference(parsers[i])));
}
BNFreeDebugInfoParserList(parsers, count);
return result;
}
vector DebugInfoParser::GetListForView(const Ref data)
{
size_t count = 0;
BNDebugInfoParser** parsers = BNGetDebugInfoParsersForView(data->GetObject(), &count);
vector result;
result.reserve(count);
for (size_t i = 0; i < count; ++i)
{
result.emplace_back(new DebugInfoParser(BNNewDebugInfoParserReference(parsers[i])));
}
BNFreeDebugInfoParserList(parsers, count);
return result;
}
string DebugInfoParser::GetName() const
{
return BNGetDebugInfoParserName(m_object);
}
Ref DebugInfoParser::Parse(Ref view, Ref debugFile, Ref existingDebugInfo,
ProgressFunction progress) const
{
ProgressContext ctxt;
if (progress)
ctxt.callback = progress;
else
ctxt.callback = [=](size_t, size_t) { return true; };
BNDebugInfo* info = nullptr;
if (existingDebugInfo)
{
info = BNParseDebugInfo(m_object, view->GetObject(), debugFile->GetObject(), existingDebugInfo->GetObject(),
ProgressCallback, &ctxt);
if (!info)
return nullptr;
info = BNNewDebugInfoReference(info);
}
else
{
info = BNParseDebugInfo(m_object, view->GetObject(), debugFile->GetObject(), nullptr, ProgressCallback, &ctxt);
if (!info)
return nullptr;
}
return new DebugInfo(info);
}
bool DebugInfoParser::IsValidForView(const Ref view) const
{
return BNIsDebugInfoParserValidForView(m_object, view->GetObject());
}
//////////////////////////////
// Plugin registration APIs //
//////////////////////////////
bool CustomDebugInfoParser::IsValidCallback(void* ctxt, BNBinaryView* view)
{
CustomDebugInfoParser* parser = (CustomDebugInfoParser*)ctxt;
return parser->IsValid(new BinaryView(view));
}
bool CustomDebugInfoParser::ParseCallback(void* ctxt, BNDebugInfo* debugInfo, BNBinaryView* view,
BNBinaryView* debugFile, BNProgressFunction progress, void* progressCtxt)
{
CustomDebugInfoParser* parser = (CustomDebugInfoParser*)ctxt;
return parser->ParseInfo(new DebugInfo(debugInfo), new BinaryView(view), new BinaryView(debugFile),
[=](size_t cur, size_t max) { return progress(progressCtxt, cur, max); });
}
CustomDebugInfoParser::CustomDebugInfoParser(const string& name) :
DebugInfoParser(BNRegisterDebugInfoParser(name.c_str(), IsValidCallback, ParseCallback, this))
{}