public bool HasAnyEffect(StackRelatedInstructionEffect effect) => (Effects & effect) != StackRelatedInstructionEffect.None;
StackRelatedInstructionEffects ClassifyInstruction(StackAnalyzerInstruction insn) { string remainingArgs = insn.Arguments; if (insn.Opcode == "push" || insn.Opcode == "pop") { int sign = insn.Opcode == "push" ? 1 : -1; int listLength = GetRegisterListLength(insn.Arguments, out var hasPC); StackRelatedInstructionEffect extraEffects = StackRelatedInstructionEffect.None; if (hasPC) { extraEffects |= StackRelatedInstructionEffect.ReturnFromCall; } return(new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.MovesStackPointer | extraEffects, StackDelta = sign * WordSize * listLength }); } else if (insn.Opcode.StartsWith("mov")) { string arg = TakeFirstArgument(ref remainingArgs); string arg2 = TakeFirstArgument(ref remainingArgs); if (arg == "sp") { if (arg2 == "r7") { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.RestoresStackPointer } } ; else { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.ChangesStackPointerUnpredictably } }; } else if (arg == "r7" && arg2 == "sp") { return(new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.SavesStackPointerWithDelta }); } } else if (insn.Opcode == "ldr" || insn.Opcode.StartsWith("ldr.w") || insn.Opcode == "ldrd" || insn.Opcode.StartsWith("ldrd.") || insn.Opcode == "str" || insn.Opcode.StartsWith("str.w") || insn.Opcode == "strd" || insn.Opcode.StartsWith("strd.")) { string arg = TakeFirstArgument(ref remainingArgs); if (insn.Opcode.StartsWith("ldr") && arg == "sp") { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.ChangesStackPointerUnpredictably } } ; if (insn.Opcode.StartsWith("strd") || insn.Opcode.StartsWith("ldrd")) { TakeFirstArgument(ref remainingArgs); //Ignore the second loaded/stored register } Match m; if (remainingArgs.StartsWith("[sp,") && (m = rgSpRelativeAddressing.Match(remainingArgs)).Success) { return(new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.MovesStackPointer, StackDelta = -int.Parse(m.Groups[1].Value) }); } else { string arg2 = TakeFirstArgument(ref remainingArgs); if (arg2 == "[sp]") { if (int.TryParse(remainingArgs.Trim('#', '!'), out int offset)) { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.MovesStackPointer, StackDelta = -offset } } ; } } } else if (insn.Opcode == "add" || insn.Opcode == "sub" || insn.Opcode == "adds" || insn.Opcode == "subs" || insn.Opcode == "addw" || insn.Opcode == "subw" || insn.Opcode == "add.w" || insn.Opcode == "sub.w") { int sign = insn.Opcode.StartsWith("sub") ? 1 : -1; string targetRegister = TakeFirstArgument(ref remainingArgs); string sourceRegister = TakeFirstArgument(ref remainingArgs); if (remainingArgs == "") { remainingArgs = sourceRegister; sourceRegister = targetRegister; } if (targetRegister == "sp") { var delta = ParseImmediateValue(remainingArgs); if (delta.HasValue) { if (targetRegister == "sp") { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.MovesStackPointer, StackDelta = sign * delta.Value } } ; else if (targetRegister == "r7") { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.SavesStackPointerWithDelta, StackDelta = sign * delta.Value } } ; } return(new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.ChangesStackPointerUnpredictably }); } else if (targetRegister == "r7") { if (sourceRegister == "sp") { var delta = ParseImmediateValue(remainingArgs); if (delta.HasValue) { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.SavesStackPointerWithDelta, StackDelta = sign * delta.Value } } ; } else { var delta = ParseImmediateValue(remainingArgs); if (delta.HasValue) { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.MovesSavedStackPointer, StackDelta = sign * delta.Value } } ; } return(new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.ChangesFramePointerUnpredictably }); } } else if (insn.Opcode == "cbz" || insn.Opcode == "cbnz") { string arg1 = TakeFirstArgument(ref remainingArgs); var target = ParseAddress(remainingArgs); if (target.HasValue) { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.ConditionalJump | StackRelatedInstructionEffect.JumpTargetKnown, JumpTarget = StripThumbBit(target.Value) } } ; else { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.UnpredictableJump } }; } else if (insn.Opcode.StartsWith("b")) { string arg = TakeFirstArgument(ref remainingArgs, ' '); if (insn.Opcode == "bx" && arg == "lr") { return new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.ReturnFromCall | StackRelatedInstructionEffect.JumpsViaLinkRegister } } ; StackRelatedInstructionEffect effect = StackRelatedInstructionEffect.None; foreach (var cc in ARMConditionCodes) { if (insn.Opcode.StartsWith("bl" + cc)) { effect = StackRelatedInstructionEffect.FunctionCall; break; } } if (effect == StackRelatedInstructionEffect.None) { foreach (var cc in ARMConditionCodes) { if (insn.Opcode.StartsWith("b" + cc)) { effect = StackRelatedInstructionEffect.ConditionalJump; break; } } } if (effect == StackRelatedInstructionEffect.None) { if (insn.Opcode.StartsWith("bl")) { effect = StackRelatedInstructionEffect.FunctionCall; } else if (insn.Opcode == "b" || insn.Opcode == "bx" || insn.Opcode.StartsWith("b.") || insn.Opcode.StartsWith("bx.")) { effect = StackRelatedInstructionEffect.UnconditionalJump; } } if (effect != StackRelatedInstructionEffect.None) { var target = ParseAddress(arg); if (target.HasValue) { return new StackRelatedInstructionEffects { Effects = effect | StackRelatedInstructionEffect.JumpTargetKnown, JumpTarget = StripThumbBit(target.Value) } } ; else { if (IsSingleRegister(arg) && remainingArgs == "") { return new StackRelatedInstructionEffects { Effects = effect | StackRelatedInstructionEffect.RegisterJump } } ; else { return new StackRelatedInstructionEffects { Effects = effect | StackRelatedInstructionEffect.UnpredictableJump } }; } } } else if (insn.Opcode.StartsWith("ldmia") || insn.Opcode.StartsWith("stmdb")) { int sign = insn.Opcode.StartsWith("stmdb") ? 1 : -1; string arg0 = TakeFirstArgument(ref remainingArgs); if (arg0 == "sp!") { var result = new StackRelatedInstructionEffects { Effects = StackRelatedInstructionEffect.MovesStackPointer, StackDelta = sign * WordSize * GetRegisterListLength(remainingArgs, out var includesPC) }; if (includesPC) { result.Effects |= StackRelatedInstructionEffect.ReturnFromCall; } return(result); } } return(default(StackRelatedInstructionEffects)); }