public bool IsNoreturnFunction(AnalyzedSymbol function, IStackAnalyzerLogger optionalLogger) { return(false); }
public FunctionStackUsage AnalyzeFunctionStackUsage(AnalyzedSymbol function, IStackAnalyzerLogger optionalLogger) { FunctionStackUsage result = new FunctionStackUsage { CalledFunctions = new List <CalledFunctionStackUsage>() }; var codePaths = new Queue <PendingCodePath>(); codePaths.Enqueue(new PendingCodePath(function.Address, new StackAnalyzerContext())); HashSet <ulong> coveredAddresses = new HashSet <ulong>(); #if DEBUG int codePathNumber = 0; #endif while (codePaths.Count > 0) { #if DEBUG codePathNumber++; #endif var path = codePaths.Dequeue(); var ctx = path.Context; optionalLogger?.LogLine($"***Starting code path at 0x{path.Address:x8} with stack depth = {path.Context.Depth}" + (path.Context.EntryStackDepthForLocalCalls != 0 ? $" (local subroutine with a stack offset of {path.Context.EntryStackDepthForLocalCalls})" : "")); foreach (var insn in _Host.TryReadInstructions(path.Address)) { if (insn.Opcode == null) { result.AppendFlag(FunctionStackUsageFlags.HasJumpsToUnreadableAddresses, insn, optionalLogger); break; } if (coveredAddresses.Contains(insn.Address)) { optionalLogger?.LogLine($"*** 0x{path.Address:x8} already analyzed"); break; //Already checked this instruction from another path. We may want to double-check that the stack depth is the same as last time though. } coveredAddresses.Add(insn.Address); var effects = ClassifyInstruction(insn); if (effects.HasAnyEffect(StackRelatedInstructionEffect.ChangesStackPointerUnpredictably)) { result.AppendFlag(FunctionStackUsageFlags.HasDynamicStack, insn, optionalLogger); } else if (effects.HasAnyEffect(StackRelatedInstructionEffect.RegisterJump)) { result.AppendFlag(FunctionStackUsageFlags.HasDynamicCalls, insn, optionalLogger); } else if (effects.HasAnyEffect(StackRelatedInstructionEffect.WarningMask)) { result.AppendFlag(FunctionStackUsageFlags.HasOtherWarning, insn, optionalLogger); } if (effects.HasAnyEffect(StackRelatedInstructionEffect.SavesStackPointerWithDelta)) { ctx.SavedStackDepth = ctx.Depth + effects.StackDelta; } if (effects.HasAnyEffect(StackRelatedInstructionEffect.RestoresStackPointer)) { ctx.Depth = ctx.SavedStackDepth; if (ctx.FramePointedChangedUnpredictably) { result.AppendFlag(FunctionStackUsageFlags.HasDynamicStack, insn, optionalLogger); } } if (effects.HasAnyEffect(StackRelatedInstructionEffect.ChangesFramePointerUnpredictably)) { ctx.FramePointedChangedUnpredictably = true; } if (effects.HasAnyEffect(StackRelatedInstructionEffect.MovesSavedStackPointer)) { ctx.SavedStackDepth += effects.StackDelta; } if (effects.HasAnyEffect(StackRelatedInstructionEffect.MovesStackPointer)) { result.MaximumOwnDepthIncludingPushedArguments = Math.Max(result.MaximumOwnDepthIncludingPushedArguments, ctx.Depth); ctx.Depth += effects.StackDelta; result.MaximumOwnDepthIncludingPushedArguments = Math.Max(result.MaximumOwnDepthIncludingPushedArguments, ctx.Depth); if (ctx.Depth < 0) { optionalLogger?.ReportWarning(insn, FunctionStackUsageFlags.HasStackImbalance); result.AppendFlag(FunctionStackUsageFlags.HasStackUnderrun, insn, optionalLogger); } } if (effects.HasAnyEffect(StackRelatedInstructionEffect.FunctionCall)) { bool isLocalCall = false; if (effects.HasAnyEffect(StackRelatedInstructionEffect.JumpTargetKnown)) { if (function.ContainsAddress(effects.JumpTarget)) { //This is a local subroutine (e.g. used by __aeabi_dmul). Once it reaches the 'bx lr' instruction, it will get back to the next instruction after the current one. isLocalCall = true; var ctx2 = ctx.Clone(); ctx2.EntryStackDepthForLocalCalls = ctx2.Depth; codePaths.Enqueue(new PendingCodePath(effects.JumpTarget, ctx2)); } else { result.CalledFunctions.Add(new CalledFunctionStackUsage(insn.Address, effects.JumpTarget, ctx.Depth, false)); } } if (!isLocalCall && _Host.IsNoreturnFunction(effects.JumpTarget)) { break; //This call will never return } } optionalLogger?.ReportInstructionStatus(ctx.Depth, insn, effects.ToString()); if (effects.HasAnyEffect(StackRelatedInstructionEffect.ReturnFromCall)) { //End of path. This is either a return from the function, or a return from a local subroutine. if (ctx.EntryStackDepthForLocalCalls != 0 && effects.HasAnyEffect(StackRelatedInstructionEffect.JumpsViaLinkRegister)) { if (ctx.Depth != ctx.EntryStackDepthForLocalCalls) { result.AppendFlag(FunctionStackUsageFlags.HasStackImbalance, insn, optionalLogger); } } else { if (ctx.Depth != 0) { result.AppendFlag(FunctionStackUsageFlags.HasStackImbalance, insn, optionalLogger); } } break; } if (effects.HasAnyEffect(StackRelatedInstructionEffect.UnconditionalJump) && ctx.Depth == 0 && !function.ContainsAddress(effects.JumpTarget)) { //This is a tail call at the end of the function if (effects.HasAnyEffect(StackRelatedInstructionEffect.JumpTargetKnown)) { result.CalledFunctions.Add(new CalledFunctionStackUsage(insn.Address, effects.JumpTarget, ctx.Depth, true)); } break; } if (effects.HasAnyEffect(StackRelatedInstructionEffect.ConditionalJump | StackRelatedInstructionEffect.UnconditionalJump)) { if (effects.HasAnyEffect(StackRelatedInstructionEffect.JumpTargetKnown)) { codePaths.Enqueue(new PendingCodePath(effects.JumpTarget, ctx.Clone())); } if (effects.HasAnyEffect(StackRelatedInstructionEffect.UnconditionalJump)) { break; //We will continue this when we start analyzing the queued path. } } } } return(result); }