summaryrefslogtreecommitdiff
path: root/defaultabb.cpp
diff options
context:
space:
mode:
authorBrandon Miller <brandon@vector35.com>2025-04-25 13:30:45 -0400
committerRyan Snyder <ryan@vector35.com>2025-06-23 13:44:12 -0400
commitb079d1a1e19151f5c39de84c2e1e6cc6a91abc74 (patch)
tree8cf27224630b690fcfdfd490ee43ec1cd56fc9b6 /defaultabb.cpp
parent76215b86ff629da58aa94d4cf4093d2e67017412 (diff)
Perform BB analysis from Architecture C++ API
This commit moves AnalyzeBasicBlocks from the binary ninja core to the API and allows architecture plugins to optionally override AnalyzeBasicBlocks for a custom implementation Supply ABB inputs in BNBasicBlockAnalysisContext Register default analyze basic blocks callback This allows the nanomips and rust core architecture plugins to work again while using the C++ API DefaultAnalyzeBasicBlocks Use default ABB from Python plugins Fix bug in API ArchAndAddr operator overload Python APIs for basic block analysis
Diffstat (limited to 'defaultabb.cpp')
-rw-r--r--defaultabb.cpp664
1 files changed, 664 insertions, 0 deletions
diff --git a/defaultabb.cpp b/defaultabb.cpp
new file mode 100644
index 00000000..c6592fc0
--- /dev/null
+++ b/defaultabb.cpp
@@ -0,0 +1,664 @@
+#include <map>
+#include <set>
+#include <queue>
+#include <inttypes.h>
+#include "binaryninjaapi.h"
+#include "binaryninjacore.h"
+#include "lowlevelilinstruction.h"
+
+using namespace std;
+using namespace BinaryNinja;
+
+// TODO: Decomposed from BinaryView::IsOffsetCodeSemantics BinaryView::IsOffsetExternSemantics
+// TODO: When the better sections model is merged, remove this
+static bool IsOffsetCodeSemanticsFast(BinaryView* data, const vector<Section*>& readOnlySections, const vector<Section*>& dataExternSections, uint64_t offset)
+{
+ if (!data->IsOffsetBackedByFile(offset))
+ return false;
+
+ for (const auto& i : readOnlySections)
+ {
+ if ((offset >= i->GetStart()) && (offset < i->GetEnd()))
+ return true;
+ }
+ for (const auto& i : dataExternSections)
+ {
+ if ((offset >= i->GetStart()) && (offset < i->GetEnd()))
+ return false;
+ }
+
+ return data->IsOffsetExecutable(offset);
+}
+
+
+static bool IsOffsetExternSemanticsFast(BinaryView* data, const vector<Section*>& externSections, uint64_t offset)
+{
+ if (data->IsOffsetBackedByFile(offset))
+ return false;
+ if (data->IsOffsetExecutable(offset))
+ return false;
+
+ for (const auto& i : externSections)
+ {
+ if ((offset >= i->GetStart()) && (offset < i->GetEnd()))
+ return true;
+ }
+
+ return false;
+}
+
+
+static bool GetNextFunctionAfterAddress(Ref<BinaryView> data, Ref<Platform> platform, uint64_t address, Ref<Function>& nextFunc)
+{
+ uint64_t nextFuncAddr = data->GetNextFunctionStartAfterAddress(address);
+ nextFunc = data->GetAnalysisFunction(platform, nextFuncAddr);
+ return nextFunc != nullptr;
+}
+
+
+void Architecture::DefaultAnalyzeBasicBlocks(Function& function, BasicBlockAnalysisContext& context)
+{
+ auto data = function.GetView();
+ queue<ArchAndAddr> blocksToProcess;
+ map<ArchAndAddr, Ref<BasicBlock>> instrBlocks;
+ set<ArchAndAddr> seenBlocks;
+ map<ArchAndAddr, set<ArchAndAddr>> indirectBranches;
+ for (size_t i = 0; i < context.indirectBranchesCount; i++)
+ {
+ auto sourceLocation = ArchAndAddr(new CoreArchitecture(context.indirectBranches[i].sourceArch),
+ context.indirectBranches[i].sourceAddr);
+ auto destLocation = ArchAndAddr(new CoreArchitecture(context.indirectBranches[i].destArch),
+ context.indirectBranches[i].destAddr);
+ indirectBranches[sourceLocation].insert(destLocation);
+ }
+
+ BNStringReference strRef;
+ auto targetExceedsByteLimit = [](const BNStringReference& strRef) {
+ size_t byteLimit = 8;
+ if (strRef.type == Utf16String) byteLimit *= 2;
+ else if (strRef.type == Utf32String) byteLimit *= 4;
+ return (strRef.length >= byteLimit);
+ };
+
+ // TODO: Decomposed from BinaryView::IsOffsetCodeSemantics BinaryView::IsOffsetExternSemantics
+ // TODO: When the better sections model is merged, remove this
+ auto sections = data->GetSections();
+ vector<Section*> externSections, readOnlySections, dataExternSections;
+ externSections.reserve(sections.size());
+ readOnlySections.reserve(sections.size());
+ dataExternSections.reserve(sections.size());
+ for (auto& section: sections)
+ {
+ if (section->GetSemantics() == ExternalSectionSemantics)
+ {
+ externSections.push_back(section);
+ }
+ if (section->GetSemantics() == ReadOnlyCodeSectionSemantics)
+ {
+ readOnlySections.push_back(section);
+ }
+ if ((section->GetSemantics() == ReadOnlyDataSectionSemantics) ||
+ (section->GetSemantics() == ReadWriteDataSectionSemantics) ||
+ (section->GetSemantics() == ExternalSectionSemantics))
+ {
+ dataExternSections.push_back(section);
+ }
+ }
+
+ // Start by processing the entry point of the function
+ auto funcPlatform = function.GetPlatform();
+ auto start = function.GetStart();
+ blocksToProcess.emplace(funcPlatform->GetArchitecture(), start);
+ seenBlocks.emplace(funcPlatform->GetArchitecture(), start);
+
+ // Only validate that branch destinations are executable if the start of the function is executable. This allows
+ // data to be disassembled manually
+ bool validateExecutable = data->IsOffsetExecutable(start);
+
+ bool fastValidate = false;
+ uint64_t fastEndAddr = 0;
+ uint64_t fastStartAddr = UINT64_MAX;
+ if (validateExecutable)
+ {
+ // Extract the bounds of the section containing this
+ // function, to avoid calling into the BinaryView on
+ // every instruction.
+ for (auto& sec : data->GetSectionsAt(start))
+ {
+ if (sec->GetSemantics() == ReadOnlyDataSectionSemantics)
+ continue;
+ if (sec->GetSemantics() == ReadWriteDataSectionSemantics)
+ continue;
+ if (!data->IsOffsetBackedByFile(sec->GetStart()))
+ continue;
+ if (!data->IsOffsetExecutable(sec->GetStart()))
+ continue;
+ if (fastStartAddr > sec->GetStart())
+ fastStartAddr = sec->GetStart();
+ if (fastEndAddr < (sec->GetEnd() - 1))
+ {
+ fastEndAddr = sec->GetEnd() - 1;
+ Ref<Segment> segment = data->GetSegmentAt(fastEndAddr);
+ if (segment)
+ fastEndAddr = (std::min)(fastEndAddr, segment->GetDataEnd() - 1);
+ }
+ fastValidate = true;
+ break;
+ }
+ }
+
+ uint64_t totalSize = 0;
+ uint64_t maxSize = context.maxFunctionSize;
+ bool maxSizeReached = false;
+ while (blocksToProcess.size() != 0)
+ {
+ if (data->AnalysisIsAborted())
+ return;
+
+ // Get the next block to process
+ ArchAndAddr location = blocksToProcess.front();
+ ArchAndAddr instructionGroupStart = location;
+ blocksToProcess.pop();
+
+ // Create a new basic block
+ Ref<BasicBlock> block = function.CreateBasicBlock(location.arch, location.address);
+
+ // Get the next function to prevent disassembling into the next function if the block falls through
+ Ref<Function> nextFunc;
+ bool hasNextFunc = GetNextFunctionAfterAddress(data, funcPlatform, location.address, nextFunc);
+ uint64_t nextFuncAddr = (hasNextFunc && nextFunc) ? nextFunc->GetStart() : 0;
+ set<Ref<Function>> calledFunctions;
+
+ // we mostly only case if this is 0, or more than 0. after handling an instruction,
+ // we decrement. the architecture can change this value arbitrarily during callbacks.
+ uint8_t delaySlotCount = 0;
+ bool delayInstructionEndsBlock = false;
+
+ // Disassemble the instructions in the block
+ while (true)
+ {
+ if (data->AnalysisIsAborted())
+ return;
+
+ if (!delaySlotCount)
+ {
+ auto blockIter = instrBlocks.find(location);
+ if (blockIter != instrBlocks.end())
+ {
+ // This instruction has already been seen, go to it directly insread of creating a copy
+ Ref<BasicBlock> targetBlock = blockIter->second;
+ if (targetBlock->GetStart() == location.address)
+ {
+ // Instruction is the start of a block, add an unconditional branch to it
+ block->AddPendingOutgoingEdge(UnconditionalBranch, location.address, nullptr,
+ (block->GetStart() != location.address));
+ break;
+ }
+ else
+ {
+ // Instruction is in the middle of a block, need to split the basic block into two
+ Ref<BasicBlock> splitBlock = function.CreateBasicBlock(location.arch, location.address);
+ size_t instrDataLen;
+ const uint8_t* instrData = targetBlock->GetInstructionData(location.address, &instrDataLen);
+ splitBlock->AddInstructionData(instrData, instrDataLen);
+ splitBlock->SetFallThroughToFunction(targetBlock->IsFallThroughToFunction());
+ splitBlock->SetUndeterminedOutgoingEdges(targetBlock->HasUndeterminedOutgoingEdges());
+ splitBlock->SetCanExit(targetBlock->CanExit());
+ splitBlock->SetEnd(targetBlock->GetEnd());
+
+ targetBlock->SetFallThroughToFunction(false);
+ targetBlock->SetUndeterminedOutgoingEdges(false);
+ targetBlock->SetCanExit(true);
+ targetBlock->SetEnd(location.address);
+
+ // Place instructions after the split point into the new block
+ for (size_t j = location.address; j < splitBlock->GetEnd(); j++)
+ {
+ auto k = instrBlocks.find(ArchAndAddr(location.arch, j));
+ if ((k != instrBlocks.end()) && (k->second == targetBlock))
+ k->second = splitBlock;
+ }
+
+ for (auto& k : targetBlock->GetPendingOutgoingEdges())
+ splitBlock->AddPendingOutgoingEdge(k.type, k.target, k.arch, k.fallThrough);
+ targetBlock->ClearPendingOutgoingEdges();
+ targetBlock->AddPendingOutgoingEdge(UnconditionalBranch, location.address, nullptr, true);
+
+ // Mark the new block so that it will not be processed again
+ seenBlocks.insert(location);
+ function.AddBasicBlock(splitBlock);
+
+ // Add an outgoing edge from the current block to the new block
+ block->AddPendingOutgoingEdge(UnconditionalBranch, location.address);
+ break;
+ }
+ }
+ }
+
+ uint8_t opcode[BN_MAX_INSTRUCTION_LENGTH];
+ size_t maxLen = data->Read(opcode, location.address, location.arch->GetMaxInstructionLength());
+ if (maxLen == 0)
+ {
+ string text = fmt::format("Could not read instruction at {:#x}", location.address);
+ function.CreateAutoAddressTag(location.arch, location.address, "Invalid Instruction", text, true);
+ if (location.arch->GetInstructionAlignment() == 0)
+ location.address++;
+ else
+ location.address += location.arch->GetInstructionAlignment();
+ block->SetHasInvalidInstructions(true);
+ break;
+ }
+
+ InstructionInfo info;
+ info.delaySlots = delaySlotCount;
+ if (!location.arch->GetInstructionInfo(opcode, location.address, maxLen, info))
+ {
+ string text = fmt::format("Could not get instruction info at {:#x}", location.address);
+ function.CreateAutoAddressTag(location.arch, location.address, "Invalid Instruction", text, true);
+ if (location.arch->GetInstructionAlignment() == 0)
+ location.address++;
+ else
+ location.address += location.arch->GetInstructionAlignment();
+ block->SetHasInvalidInstructions(true);
+ break;
+ }
+
+ // The instruction is invalid if it has no length or is above maximum length
+ if ((info.length == 0) || (info.length > maxLen))
+ {
+ string text = fmt::format("Instruction of invalid length at {:#x}", location.address);
+ function.CreateAutoAddressTag(location.arch, location.address, "Invalid Instruction", text, true);
+ if (location.arch->GetInstructionAlignment() == 0)
+ location.address++;
+ else
+ location.address += location.arch->GetInstructionAlignment();
+ block->SetHasInvalidInstructions(true);
+ break;
+ }
+
+ // Instruction is invalid when straddling a boundary to a section that is non-code, or not back by file
+ uint64_t instrEnd = location.address + info.length - 1;
+ bool slowPath = !fastValidate || (instrEnd < fastStartAddr) || (instrEnd > fastEndAddr);
+ if (slowPath &&
+ ((!IsOffsetCodeSemanticsFast(data, readOnlySections, dataExternSections, instrEnd) && IsOffsetCodeSemanticsFast(data, readOnlySections, dataExternSections,location.address)) ||
+ (!data->IsOffsetBackedByFile(instrEnd) && data->IsOffsetBackedByFile(location.address))))
+ {
+ string text = fmt::format("Instruction at {:#x} straddles a non-code section", location.address);
+ function.CreateAutoAddressTag(location.arch, location.address, "Invalid Instruction", text, true);
+ if (location.arch->GetInstructionAlignment() == 0)
+ location.address++;
+ else
+ location.address += location.arch->GetInstructionAlignment();
+ block->SetHasInvalidInstructions(true);
+ break;
+ }
+
+ bool endsBlock = false;
+ ArchAndAddr target;
+ map<ArchAndAddr, set<ArchAndAddr>>::const_iterator indirectBranchIter, endIter;
+ if (!delaySlotCount)
+ {
+ // Register the address as belonging to this block if not in a delay slot,
+ // this prevents basic blocks from being split between an instruction and
+ // any of its delay slots
+ instrBlocks[location] = block;
+
+ // Keep track of where the current 'group' of instructions started. A 'group'
+ // is an instruction and all of its delay slot instructions.
+ instructionGroupStart = location;
+
+ // Don't process branches in delay slots
+ for (size_t i = 0; i < info.branchCount; i++)
+ {
+ bool fastPath;
+
+ switch (info.branchType[i])
+ {
+ case UnconditionalBranch:
+ case TrueBranch:
+ case FalseBranch:
+ // Normal branch, resume disassembly at targets
+ endsBlock = true;
+ // Target of a call instruction, add the function to the analysis
+ if (IsOffsetExternSemanticsFast(data, externSections, info.branchTarget[i]))
+ {
+ // Deal with direct pointers into the extern section
+ DataVariable dataVar;
+ if (data->GetDataVariableAtAddress(info.branchTarget[i], dataVar) &&
+ (dataVar.address == info.branchTarget[i]) && dataVar.type &&
+ (dataVar.type->GetClass() == FunctionTypeClass))
+ {
+ function.AddDirectCodeReference(location, info.branchTarget[i]);
+ if (!dataVar.type->CanReturn())
+ {
+ function.AddDirectNoReturnCall(location);
+ endsBlock = true;
+ block->SetCanExit(false);
+ }
+ }
+ break;
+ }
+
+ fastPath = fastValidate && (info.branchTarget[i] >= fastStartAddr) && (info.branchTarget[i] <= fastEndAddr);
+ if (fastPath || (data->IsValidOffset(info.branchTarget[i]) &&
+ data->IsOffsetBackedByFile(info.branchTarget[i]) &&
+ ((!validateExecutable) || data->IsOffsetExecutable(info.branchTarget[i]))))
+ {
+ target = ArchAndAddr(info.branchArch[i] ? new CoreArchitecture(info.branchArch[i]) : location.arch, info.branchTarget[i]);
+
+ // Check if valid target
+ if (data->ShouldSkipTargetAnalysis(location, &function, instrEnd, target))
+ break;
+
+ Platform* targetPlatform = funcPlatform;
+ if (target.arch != funcPlatform->GetArchitecture())
+ targetPlatform = funcPlatform->GetRelatedPlatform(target.arch);
+
+ function.AddDirectCodeReference(location, info.branchTarget[i]);
+
+ auto otherFunc = function.GetCalleeForAnalysis(targetPlatform, target.address, true);
+ if (context.translateTailCalls && targetPlatform && otherFunc && (otherFunc->GetStart() != function.GetStart()))
+ {
+ calledFunctions.insert(otherFunc);
+ if (info.branchType[i] == UnconditionalBranch)
+ {
+ if (!otherFunc->CanReturn())
+ {
+ function.AddDirectNoReturnCall(location);
+ endsBlock = true;
+ block->SetCanExit(false);
+ }
+
+ break;
+ }
+ }
+ else if (context.disallowBranchToString && data->GetStringAtAddress(location.address, strRef) && targetExceedsByteLimit(strRef))
+ {
+ BNLogInfo("Not adding branch target from 0x%" PRIx64 " to string at 0x%" PRIx64
+ " length:%zu",
+ location.address, target.address, strRef.length);
+ break;
+ }
+ else
+ {
+
+ block->AddPendingOutgoingEdge(info.branchType[i], target.address, target.arch);
+ // Add the block to the list of blocks to process if it is not already processed
+ if (seenBlocks.count(target) == 0)
+ {
+ blocksToProcess.push(target);
+ seenBlocks.insert(target);
+ }
+ }
+ }
+ break;
+
+ case CallDestination:
+ // Target of a call instruction, add the function to the analysis
+ if (IsOffsetExternSemanticsFast(data, externSections, info.branchTarget[i]))
+ {
+ // Deal with direct pointers into the extern section
+ DataVariable dataVar;
+ if (data->GetDataVariableAtAddress(info.branchTarget[i], dataVar) &&
+ (dataVar.address == info.branchTarget[i]) && dataVar.type &&
+ (dataVar.type->GetClass() == FunctionTypeClass))
+ {
+ function.AddDirectCodeReference(location, info.branchTarget[i]);
+ if (!dataVar.type->CanReturn())
+ {
+ function.AddDirectNoReturnCall(location);
+ endsBlock = true;
+ block->SetCanExit(false);
+ }
+ // No need to add the target to the calledFunctions list since a call to external code
+ // can never be the 'next' function
+ }
+ break;
+ }
+
+ fastPath = fastValidate && (info.branchTarget[i] >= fastStartAddr) && (info.branchTarget[i] <= fastEndAddr);
+ if (fastPath || (data->IsValidOffset(info.branchTarget[i]) && data->IsOffsetBackedByFile(info.branchTarget[i]) &&
+ ((!validateExecutable) || data->IsOffsetExecutable(info.branchTarget[i]))))
+ {
+ target = ArchAndAddr(info.branchArch[i] ? new CoreArchitecture(info.branchArch[i]) : location.arch, info.branchTarget[i]);
+
+ if (!fastPath && !IsOffsetCodeSemanticsFast(data, readOnlySections, dataExternSections, target.address) &&
+ IsOffsetCodeSemanticsFast(data, readOnlySections, dataExternSections, location.address))
+ {
+ string message = fmt::format("Non-code call target {:#x}", target.address);
+ function.CreateAutoAddressTag(target.arch, location.address, "Non-code Branch", message, true);
+ break;
+ }
+
+ Ref<Platform> platform = funcPlatform;
+ if (target.arch != platform->GetArchitecture())
+ {
+ platform = funcPlatform->GetRelatedPlatform(target.arch);
+ if (!platform)
+ platform = funcPlatform;
+ }
+
+ // Check if valid target
+ if (data->ShouldSkipTargetAnalysis(location, &function, instrEnd, target))
+ break;
+
+ Ref<Function> func = data->AddFunctionForAnalysis(platform, target.address, true);
+ if (!func)
+ {
+ if (!data->IsOffsetBackedByFile(target.address))
+ BNLogError("Function at 0x%" PRIx64 " failed to add target not backed by file.", function.GetStart());
+ break;
+ }
+
+ function.AddDirectCodeReference(location, target.address);
+ if (!func->CanReturn())
+ {
+ function.AddDirectNoReturnCall(location);
+ endsBlock = true;
+ block->SetCanExit(false);
+ }
+
+ // Add function as an early reference in case it gets updated before this
+ // function finishes analysis.
+ if (!function.HasTempOutgoingReference(func))
+ {
+ function.AddTempOutgoingReference(func);
+ func->AddTempIncomingReference(&function);
+ }
+
+ calledFunctions.insert(func);
+ }
+ break;
+
+ case SystemCall:
+ break;
+
+ default:
+ // Undefined type or target, check for targets from analysis and stop disassembling this block
+ endsBlock = true;
+
+ if (info.branchType[i] == IndirectBranch)
+ {
+ // Indirect calls need not end the block early.
+ Ref<LowLevelILFunction> ilFunc = new LowLevelILFunction(location.arch, nullptr);
+ location.arch->GetInstructionLowLevelIL(opcode, location.address, maxLen, *ilFunc);
+ for (size_t idx = 0; idx < ilFunc->GetInstructionCount(); idx++)
+ {
+ if ((*ilFunc)[idx].operation == LLIL_CALL)
+ {
+ endsBlock = false;
+ break;
+ }
+ }
+ }
+
+ indirectBranchIter = indirectBranches.find(location);
+ endIter = indirectBranches.end();
+ if (indirectBranchIter != endIter)
+ {
+ for (auto& branch : indirectBranchIter->second)
+ {
+ function.AddDirectCodeReference(location, branch.address);
+ Ref<Platform> targetPlatform = funcPlatform;
+ if (branch.arch != function.GetArchitecture())
+ targetPlatform = funcPlatform->GetRelatedPlatform(branch.arch);
+
+ // Normal analysis should not inline indirect targets that are function starts
+ if (context.translateTailCalls && data->GetAnalysisFunction(targetPlatform, branch.address))
+ continue;
+
+ block->AddPendingOutgoingEdge(IndirectBranch, branch.address, branch.arch);
+ if (seenBlocks.count(branch) == 0)
+ {
+ blocksToProcess.push(branch);
+ seenBlocks.insert(branch);
+ }
+ }
+ }
+ else if (info.branchType[i] == ExceptionBranch)
+ {
+ block->SetCanExit(false);
+ }
+ else if (info.branchType[i] == FunctionReturn)
+ {
+ // Support for contextual function returns. This is mainly used for ARM/Thumb with 'blx lr'. It's most common for this to be treated
+ // as a function return, however it can also be a function call. For now this transform is described as follows:
+ // 1) Architecture lifts a call instruction as LLIL_CALL with a branch type of FunctionReturn
+ // 2) By default, contextualFunctionReturns is used to translate this to a LLIL_RET (conservative)
+ // 3) Downstream analysis uses dataflow to validate the return target
+ // 4) If the target is not the ReturnAddressValue, then we avoid the translation to a return and leave the instruction as a call
+ bool value;
+ if (function.GetContextualFunctionReturn(location, value))
+ endsBlock = value;
+ else
+ {
+ Ref<LowLevelILFunction> ilFunc = new LowLevelILFunction(location.arch, nullptr);
+ location.arch->GetInstructionLowLevelIL(opcode, location.address, maxLen, *ilFunc);
+ if (ilFunc->GetInstructionCount() && ((*ilFunc)[0].operation == LLIL_CALL))
+ function.SetContextualFunctionReturn(location, true);
+ }
+ }
+ else
+ {
+ // If analysis did not find any valid branch targets, don't assume anything about global
+ // function state, such as __noreturn analysis, since we can't see the entire function.
+ block->SetUndeterminedOutgoingEdges(true);
+ }
+ break;
+ }
+ }
+ }
+
+ if (function.LocationHasNoReturnCalls(location))
+ {
+ size_t instrLength = info.length;
+ if (info.delaySlots)
+ {
+ InstructionInfo delayInfo;
+ delayInfo.delaySlots = info.delaySlots; // we'll decrement this inside the loop
+ size_t archMax = location.arch->GetMaxInstructionLength();
+ uint8_t delayOpcode[BN_MAX_INSTRUCTION_LENGTH];
+ do
+ {
+ delayInfo.delaySlots--;
+ if (!location.arch->GetInstructionInfo(delayOpcode, location.address + instrLength, archMax - instrLength, delayInfo))
+ break;
+ instrLength += delayInfo.length;
+ } while (delayInfo.delaySlots && (instrLength < archMax));
+ }
+
+ // Conditional Call Support (Part 1)
+ // Do not halt basic block analysis if this is a conditional call to a function that is 'no return'
+ // This works for both direct and indirect calls.
+ // Note: Do not lift a conditional call (direct or not) with branch information.
+ Ref<LowLevelILFunction> ilFunc = new LowLevelILFunction(location.arch, nullptr);
+ ilFunc->SetCurrentAddress(location.arch, location.address);
+ location.arch->GetInstructionLowLevelIL(opcode, location.address, maxLen, *ilFunc);
+ if (!(ilFunc->GetInstructionCount() && ((*ilFunc)[0].operation == LLIL_IF)))
+ {
+ endsBlock = true;
+ block->SetCanExit(false);
+ }
+ }
+
+ location.address += info.length;
+ block->AddInstructionData(opcode, info.length);
+
+ if (endsBlock && !info.delaySlots)
+ break;
+
+ // Respect the 'analysis.limits.maxFunctionSize' setting while allowing for overridable behavior as well.
+ // We prefer to allow disassembly when function analysis is disabled, but only up to the maximum size.
+ // The log message and tag are generated in ProcessAnalysisSkip
+ totalSize += info.length;
+ if (context.analysisSkipOverride == NeverSkipFunctionAnalysis)
+ maxSize = 0;
+ else if (!maxSize && (context.analysisSkipOverride == AlwaysSkipFunctionAnalysis))
+ maxSize = context.maxFunctionSize;
+
+ if (maxSize && (totalSize > maxSize))
+ {
+ maxSizeReached = true;
+ break;
+ }
+
+ if (delaySlotCount)
+ {
+ delaySlotCount--;
+ if (!delaySlotCount && delayInstructionEndsBlock)
+ break;
+ }
+ else
+ {
+ delaySlotCount = info.delaySlots;
+ delayInstructionEndsBlock = endsBlock;
+ }
+
+ if (block->CanExit() && context.translateTailCalls && !delaySlotCount && hasNextFunc && (location.address == nextFuncAddr))
+ {
+ // Falling through into another function. Don't consider this a tail call if the current block
+ // called the function, as this indicates a get PC construct.
+ if (calledFunctions.count(nextFunc) == 0)
+ {
+ block->SetFallThroughToFunction(true);
+ if (!nextFunc->CanReturn())
+ {
+ function.AddDirectNoReturnCall(instructionGroupStart);
+ block->SetCanExit(false);
+ }
+ break;
+ }
+ hasNextFunc = GetNextFunctionAfterAddress(data, funcPlatform, location.address, nextFunc);
+ nextFuncAddr = (hasNextFunc && nextFunc) ? nextFunc->GetStart() : 0;
+ }
+ }
+
+ if (location.address != block->GetStart())
+ {
+ // Block has one or more instructions, add it to the fucntion
+ block->SetEnd(location.address);
+ function.AddBasicBlock(block);
+ }
+
+ if (maxSizeReached)
+ break;
+
+ if (context.haltOnInvalidInstructions && block->HasInvalidInstructions())
+ {
+ // TODO add haltedDisassemblyAddresses
+ }
+ }
+
+ // Finalize the function basic block list
+ function.FinalizeBasicBlocks();
+
+ if (maxSizeReached)
+ context.maxSizeReached = true;
+}
+
+
+void Architecture::DefaultAnalyzeBasicBlocksCallback(BNFunction* function, BNBasicBlockAnalysisContext* context)
+{
+ Ref<Function> func(new Function(BNNewFunctionReference(function)));
+ Architecture::DefaultAnalyzeBasicBlocks(*func, *context);
+}