private static void PropagatePushOp(Dictionary <ulong, Block> blocks, Block currBlock, int pushOpIndex) { OpCodePush pushOp = currBlock.PushOpCodes[pushOpIndex]; Stack <PathBlockState> workQueue = new Stack <PathBlockState>(); HashSet <Block> visited = new HashSet <Block>(); Stack <ulong> branchStack = new Stack <ulong>(); void Push(PathBlockState pbs) { // When block is null, this means we are pushing a restore operation. // Restore operations are used to undo the work done inside a block // when we return from it, for example it pops addresses pushed by // SSY/PBK instructions inside the block, and pushes addresses poped // by SYNC/BRK. // For blocks, if it's already visited, we just ignore to avoid going // around in circles and getting stuck here. if (pbs.Block == null || !visited.Contains(pbs.Block)) { workQueue.Push(pbs); } } Push(new PathBlockState(currBlock)); while (workQueue.TryPop(out PathBlockState pbs)) { if (pbs.ReturningFromVisit) { pbs.RestoreStackState(branchStack); continue; } Block current = pbs.Block; // If the block was already processed, we just ignore it, otherwise // we would push the same child blocks of an already processed block, // and go around in circles until memory is exhausted. if (!visited.Add(current)) { continue; } int pushOpsCount = current.PushOpCodes.Count; if (pushOpsCount != 0) { Push(new PathBlockState(branchStack.Count)); for (int index = pushOpIndex; index < pushOpsCount; index++) { branchStack.Push(current.PushOpCodes[index].GetAbsoluteAddress()); } } pushOpIndex = 0; if (current.Next != null) { Push(new PathBlockState(current.Next)); } if (current.Branch != null) { Push(new PathBlockState(current.Branch)); } else if (current.GetLastOp() is OpCodeBranchIndir brIndir) { // By adding them in descending order (sorted by address), we process the blocks // in order (of ascending address), since we work with a LIFO. foreach (Block possibleTarget in brIndir.PossibleTargets.OrderByDescending(x => x.Address)) { Push(new PathBlockState(possibleTarget)); } } else if (current.GetLastOp() is OpCodeBranchPop op) { ulong targetAddress = branchStack.Pop(); if (branchStack.Count == 0) { branchStack.Push(targetAddress); op.Targets.Add(pushOp, op.Targets.Count); pushOp.PopOps.TryAdd(op, Local()); } else { // First we push the target address (this will be used to push the // address back into the SSY/PBK stack when we return from that block), // then we push the block itself into the work "queue" (well, it's a stack) // for processing. Push(new PathBlockState(targetAddress)); Push(new PathBlockState(blocks[targetAddress])); } } } }
private static void PropagatePushOp(Dictionary <ulong, Block> blocks, Block currBlock, int pushOpIndex) { OpCodePush pushOp = currBlock.PushOpCodes[pushOpIndex]; Stack <PathBlockState> workQueue = new Stack <PathBlockState>(); HashSet <Block> visited = new HashSet <Block>(); Stack <ulong> branchStack = new Stack <ulong>(); void Push(PathBlockState pbs) { if (pbs.Block == null || visited.Add(pbs.Block)) { workQueue.Push(pbs); } } Push(new PathBlockState(currBlock)); while (workQueue.TryPop(out PathBlockState pbs)) { if (pbs.ReturningFromVisit) { pbs.RestoreStackState(branchStack); continue; } Block current = pbs.Block; int pushOpsCount = current.PushOpCodes.Count; if (pushOpsCount != 0) { Push(new PathBlockState(branchStack.Count)); for (int index = pushOpIndex; index < pushOpsCount; index++) { branchStack.Push(current.PushOpCodes[index].GetAbsoluteAddress()); } } pushOpIndex = 0; if (current.Next != null) { Push(new PathBlockState(current.Next)); } if (current.Branch != null) { Push(new PathBlockState(current.Branch)); } else if (current.GetLastOp() is OpCodeBranchIndir brIndir) { foreach (Block possibleTarget in brIndir.PossibleTargets) { Push(new PathBlockState(possibleTarget)); } } else if (current.GetLastOp() is OpCodeBranchPop op) { ulong targetAddress = branchStack.Pop(); if (branchStack.Count == 0) { branchStack.Push(targetAddress); op.Targets.Add(pushOp, op.Targets.Count); pushOp.PopOps.TryAdd(op, Local()); } else { Push(new PathBlockState(targetAddress)); Push(new PathBlockState(blocks[targetAddress])); } } } }