forked from de4dot/de4dot
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathForwardScanOrder.cs
186 lines (160 loc) · 5.8 KB
/
ForwardScanOrder.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*
Copyright (C) 2011-2015 [email protected]
This file is part of de4dot.
de4dot is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
de4dot is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with de4dot. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
using System.Collections.Generic;
using dnlib.DotNet.Emit;
namespace de4dot.blocks {
// This class makes sure that each block that is entered with a non-empty stack has at
// least one of its source blocks sorted before itself. This is to make sure peverify
// doesn't complain AND also to make sure dnlib sets the correct maxstack.
class ForwardScanOrder {
ScopeBlock scopeBlock;
IList<BaseBlock> sorted;
Dictionary<BaseBlock, BlockInfo> blockInfos = new Dictionary<BaseBlock, BlockInfo>();
Dictionary<BaseBlock, bool> inNewList = new Dictionary<BaseBlock, bool>();
List<BaseBlock> newList;
class BlockInfo {
BaseBlock baseBlock;
public int stackStart = 0;
public int stackEnd = 0;
public BlockInfo(BaseBlock baseBlock, int stackStart) {
this.baseBlock = baseBlock;
this.stackStart = stackStart;
}
public void CalculateStackUsage() {
var block = baseBlock as Block;
if (block == null) {
stackEnd = stackStart;
return;
}
int stack = stackStart;
foreach (var instr in block.Instructions)
instr.Instruction.UpdateStack(ref stack, false);
stackEnd = stack;
}
}
public ForwardScanOrder(ScopeBlock scopeBlock, IList<BaseBlock> sorted) {
this.scopeBlock = scopeBlock;
this.sorted = sorted;
}
public List<BaseBlock> Fix() {
CreateBlockInfos();
CreateNewList();
return newList;
}
void CreateBlockInfos() {
int firstBlockStackStart = 0;
if ((scopeBlock is HandlerBlock || scopeBlock is FilterHandlerBlock) &&
scopeBlock.Parent is TryHandlerBlock tryHandlerBlock &&
(tryHandlerBlock.HandlerType == ExceptionHandlerType.Catch || tryHandlerBlock.HandlerType == ExceptionHandlerType.Filter)) {
firstBlockStackStart = 1;
}
foreach (var bb in GetStartBlocks()) {
int stackStart = ReferenceEquals(bb, sorted[0]) ? firstBlockStackStart : 0;
ScanBaseBlock(bb, stackStart);
}
// One reason for this to fail is if there are still dead blocks left. Could also
// be a bug in the code.
if (blockInfos.Count != sorted.Count)
throw new ApplicationException($"Didn't add all blocks: {blockInfos.Count} vs {sorted.Count}");
}
IEnumerable<BaseBlock> GetStartBlocks() {
if (sorted.Count > 0) {
yield return sorted[0];
foreach (var bb in sorted) {
if (ReferenceEquals(bb, sorted[0]))
continue;
var block = bb as Block;
if (block == null || block.Sources == null || IsOneSourceInAnotherScopeBlock(block))
yield return bb;
}
}
}
bool IsOneSourceInAnotherScopeBlock(Block block) {
foreach (var source in block.Sources) {
if (!scopeBlock.IsOurBaseBlock(source))
return true;
}
return false;
}
struct ScanBaseBlockState {
public BaseBlock bb;
public int stackStart;
public ScanBaseBlockState(BaseBlock bb, int stackStart) {
this.bb = bb;
this.stackStart = stackStart;
}
}
Stack<ScanBaseBlockState> scanBaseBlockStack = new Stack<ScanBaseBlockState>();
void ScanBaseBlock(BaseBlock bb, int stackStart) {
scanBaseBlockStack.Push(new ScanBaseBlockState(bb, stackStart));
while (scanBaseBlockStack.Count > 0) {
var state = scanBaseBlockStack.Pop();
if (blockInfos.ContainsKey(state.bb) || !scopeBlock.IsOurBaseBlock(state.bb))
continue;
var blockInfo = new BlockInfo(state.bb, state.stackStart);
blockInfos[state.bb] = blockInfo;
var block = state.bb as Block;
if (block == null) { // i.e., if try, filter, or handler block
// It's not important to know the exact values, so we set them both to 0.
// Compilers must make sure the stack is empty when entering a try block.
blockInfo.stackStart = blockInfo.stackEnd = 0;
continue;
}
blockInfo.CalculateStackUsage();
foreach (var target in block.GetTargets())
scanBaseBlockStack.Push(new ScanBaseBlockState(target, blockInfo.stackEnd));
}
}
void CreateNewList() {
newList = new List<BaseBlock>(sorted.Count);
foreach (var bb in sorted)
AddToNewList(bb);
if (newList.Count != sorted.Count)
throw new ApplicationException($"Too many/few blocks after sorting: {newList.Count} vs {sorted.Count}");
if (newList.Count > 0 && !ReferenceEquals(newList[0], sorted[0]))
throw new ApplicationException("Start block is not first block after sorting");
}
void AddToNewList(BaseBlock bb) {
if (inNewList.ContainsKey(bb) || !scopeBlock.IsOurBaseBlock(bb))
return;
inNewList[bb] = false;
var blockInfo = blockInfos[bb];
var block = bb as Block;
if (blockInfo.stackStart == 0 || ReferenceEquals(bb, sorted[0]) ||
block == null || block.Sources == null || IsInNewList(block.Sources)) {
}
else {
foreach (var source in block.Sources) {
if (!scopeBlock.IsOurBaseBlock(source))
continue;
int oldCount = newList.Count;
AddToNewList(source); // Make sure it's before this block
if (oldCount != newList.Count)
break;
}
}
inNewList[bb] = true;
newList.Add(bb);
}
bool IsInNewList(IEnumerable<Block> blocks) {
foreach (var block in blocks) {
if (inNewList.ContainsKey(block) && inNewList[block])
return true;
}
return false;
}
}
}