1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 : //
15 : // Unittests for BlockGraph transform wrapper.
16 :
17 : #include "syzygy/block_graph/transform.h"
18 :
19 : #include "gmock/gmock.h"
20 : #include "gtest/gtest.h"
21 : #include "syzygy/block_graph/unittest_util.h"
22 :
23 : namespace block_graph {
24 : namespace {
25 :
26 : using testing::_;
27 : using testing::DummyTransformPolicy;
28 : using testing::Invoke;
29 : using testing::Return;
30 :
31 : // A constant data structure/buffer from which to initialize a data block.
32 : // The structure contains a core reference (function pointer) and an integer
33 : // data element.
34 : struct MyData {
35 : void (*code)(int);
36 : int data;
37 : };
38 : const BlockGraph::Offset kOffsetOfReferenceToCode = offsetof(MyData, code);
39 : const BlockGraph::Offset kOffsetOfData = offsetof(MyData, data);
40 : const MyData kDataBytes = { reinterpret_cast<void(*)(int)>(0xCAFEBABE),
41 : 0xDEADBEEF };
42 :
43 : // A byte buffer from which to initialize a code block. The original C source
44 : // code for this function is:
45 : //
46 : // static int y = 1;
47 : // void add(int x) {
48 : // y += x;
49 : // }
50 : //
51 : // Note the reference to
52 : // y starts 5 bytes from the end.
53 : const uint8 kCodeBytes[] = {
54 : 0x8B, 0x44, 0x24, 0x04, // mov eax,dword ptr [esp+4]
55 : 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, // add dword ptr [_y],eax
56 : 0xC3 // ret
57 : };
58 : const BlockGraph::Offset kOffsetOfCode = 0;
59 : const BlockGraph::Offset kOffsetOfReferenceToData = sizeof(kCodeBytes) - 5;
60 :
61 : class ApplyBlockGraphTransformTest : public testing::Test {
62 : public:
63 E : virtual void SetUp() {
64 E : header_block_ = block_graph_.AddBlock(BlockGraph::DATA_BLOCK, 10, "Header");
65 E : }
66 :
67 : protected:
68 : DummyTransformPolicy policy_;
69 : BlockGraph block_graph_;
70 : BlockGraph::Block* header_block_;
71 : };
72 :
73 : class LenientMockBlockGraphTransform : public BlockGraphTransformInterface {
74 : public:
75 E : virtual ~LenientMockBlockGraphTransform() { }
76 :
77 E : virtual const char* name() const { return "MockBlockGraphTransform"; }
78 :
79 : MOCK_METHOD3(TransformBlockGraph,
80 : bool(const TransformPolicyInterface*,
81 : BlockGraph*,
82 E : BlockGraph::Block*));
83 :
84 : bool DeleteHeader(const TransformPolicyInterface* policy,
85 : BlockGraph* block_graph,
86 E : BlockGraph::Block* header_block) {
87 E : CHECK(block_graph->RemoveBlock(header_block));
88 E : return true;
89 E : }
90 : };
91 : typedef testing::StrictMock<LenientMockBlockGraphTransform>
92 : MockBlockGraphTransform;
93 :
94 : class ApplyBasicBlockSubGraphTransformTest : public testing::Test {
95 : public:
96 E : ApplyBasicBlockSubGraphTransformTest()
97 : : data_block_(NULL), code_block_(NULL) {
98 E : }
99 :
100 E : virtual void SetUp() {
101 : // Create some blocks to test with.
102 : data_block_ = block_graph_.AddBlock(
103 E : BlockGraph::DATA_BLOCK, sizeof(kDataBytes), "Data");
104 E : ASSERT_TRUE(data_block_ != NULL);
105 : code_block_ = block_graph_.AddBlock(
106 E : BlockGraph::CODE_BLOCK, sizeof(kCodeBytes), "Code");
107 E : ASSERT_TRUE(code_block_ != NULL);
108 :
109 : // Set up the data block.
110 : data_block_->SetData(reinterpret_cast<const uint8*>(&kDataBytes),
111 E : sizeof(kDataBytes));
112 :
113 : // Set up the code block.
114 E : ASSERT_TRUE(code_block_->SetLabel(
115 : kOffsetOfCode,
116 : BlockGraph::Label("Code", BlockGraph::CODE_LABEL)));
117 E : code_block_->SetData(kCodeBytes, sizeof(kCodeBytes));
118 :
119 : // Set up the references
120 E : ASSERT_TRUE(
121 : data_block_->SetReference(kOffsetOfReferenceToCode,
122 : MakeReference(code_block_, kOffsetOfCode)));
123 E : ASSERT_TRUE(
124 : code_block_->SetReference(kOffsetOfReferenceToData,
125 : MakeReference(data_block_, kOffsetOfData)));
126 E : }
127 :
128 : static BlockGraph::Reference MakeReference(BlockGraph::Block* target,
129 E : BlockGraph::Offset offset) {
130 E : EXPECT_TRUE(target != NULL);
131 : return BlockGraph::Reference(BlockGraph::RELATIVE_REF,
132 : BlockGraph::Reference::kMaximumSize,
133 E : target, offset, offset);
134 E : }
135 :
136 : protected:
137 : DummyTransformPolicy policy_;
138 : BlockGraph block_graph_;
139 : BlockGraph::Block* data_block_;
140 : BlockGraph::Block* code_block_;
141 : };
142 :
143 : class MockBasicBlockSubGraphTransform :
144 : public BasicBlockSubGraphTransformInterface {
145 : public:
146 E : virtual ~MockBasicBlockSubGraphTransform() { }
147 :
148 i : virtual const char* name() const { return "MockBasicBlockSubGraphTransform"; }
149 :
150 : MOCK_METHOD3(TransformBasicBlockSubGraph,
151 : bool(const TransformPolicyInterface*,
152 : BlockGraph*,
153 E : BasicBlockSubGraph*));
154 : };
155 :
156 : } // namespace
157 :
158 E : TEST_F(ApplyBlockGraphTransformTest, NormalTransformSucceeds) {
159 E : MockBlockGraphTransform transform;
160 : EXPECT_CALL(transform, TransformBlockGraph(_, _, _)).Times(1).
161 E : WillOnce(Return(true));
162 : EXPECT_TRUE(ApplyBlockGraphTransform(&transform,
163 : &policy_,
164 : &block_graph_,
165 E : header_block_));
166 E : }
167 :
168 E : TEST_F(ApplyBlockGraphTransformTest, DeletingHeaderFails) {
169 E : MockBlockGraphTransform transform;
170 : EXPECT_CALL(transform, TransformBlockGraph(_, _, _)).Times(1).WillOnce(
171 E : Invoke(&transform, &MockBlockGraphTransform::DeleteHeader));
172 : EXPECT_FALSE(ApplyBlockGraphTransform(&transform,
173 : &policy_,
174 : &block_graph_,
175 E : header_block_));
176 E : }
177 :
178 E : TEST_F(ApplyBlockGraphTransformTest, VectorTransformSucceeds) {
179 E : MockBlockGraphTransform tx1, tx2, tx3;
180 E : std::vector<BlockGraphTransformInterface*> txs;
181 E : txs.push_back(&tx1);
182 E : txs.push_back(&tx2);
183 E : txs.push_back(&tx3);
184 :
185 : EXPECT_CALL(tx1, TransformBlockGraph(&policy_, &block_graph_, header_block_))
186 E : .WillOnce(Return(true));
187 : EXPECT_CALL(tx2, TransformBlockGraph(&policy_, &block_graph_, header_block_))
188 E : .WillOnce(Return(true));
189 : EXPECT_CALL(tx3, TransformBlockGraph(&policy_, &block_graph_, header_block_))
190 E : .WillOnce(Return(true));
191 :
192 : EXPECT_TRUE(ApplyBlockGraphTransforms(
193 E : txs, &policy_, &block_graph_, header_block_));
194 E : }
195 :
196 E : TEST_F(ApplyBlockGraphTransformTest, VectorTransformFails) {
197 E : MockBlockGraphTransform tx1, tx2, tx3;
198 E : std::vector<BlockGraphTransformInterface*> txs;
199 E : txs.push_back(&tx1);
200 E : txs.push_back(&tx2);
201 E : txs.push_back(&tx3);
202 :
203 : EXPECT_CALL(tx1, TransformBlockGraph(&policy_, &block_graph_, header_block_))
204 E : .WillOnce(Return(true));
205 : EXPECT_CALL(tx2, TransformBlockGraph(&policy_, &block_graph_, header_block_))
206 E : .WillOnce(Return(false));
207 :
208 : EXPECT_FALSE(ApplyBlockGraphTransforms(
209 E : txs, &policy_, &block_graph_, header_block_));
210 E : }
211 :
212 E : TEST_F(ApplyBasicBlockSubGraphTransformTest, TransformFails) {
213 : // Remember the block ids of the original blocks.
214 E : BlockGraph::BlockId data_block_id = data_block_->id();
215 E : BlockGraph::BlockId code_block_id = code_block_->id();
216 :
217 : // Apply an empty transform that reports failure.
218 E : MockBasicBlockSubGraphTransform transform;
219 : EXPECT_CALL(transform, TransformBasicBlockSubGraph(_, _, _)).Times(1).
220 E : WillOnce(Return(false));
221 : EXPECT_FALSE(ApplyBasicBlockSubGraphTransform(&transform,
222 : &policy_,
223 : &block_graph_,
224 : code_block_,
225 E : NULL));
226 :
227 : // The original block graph should be unchanged.
228 E : EXPECT_EQ(2U, block_graph_.blocks().size());
229 E : EXPECT_EQ(data_block_, block_graph_.GetBlockById(data_block_id));
230 E : EXPECT_EQ(code_block_, block_graph_.GetBlockById(code_block_id));
231 E : }
232 :
233 E : TEST_F(ApplyBasicBlockSubGraphTransformTest, EmptyTransformSucceeds) {
234 : // Remember the block ids of the original blocks.
235 E : BlockGraph::BlockId data_block_id = data_block_->id();
236 E : BlockGraph::BlockId code_block_id = code_block_->id();
237 :
238 : // Apply an empty transform that reports success.
239 E : MockBasicBlockSubGraphTransform transform;
240 E : BlockVector new_blocks;
241 : EXPECT_CALL(transform, TransformBasicBlockSubGraph(_, _, _)).Times(1).
242 E : WillOnce(Return(true));
243 : EXPECT_TRUE(ApplyBasicBlockSubGraphTransform(&transform,
244 : &policy_,
245 : &block_graph_,
246 : code_block_,
247 E : &new_blocks));
248 :
249 : // The code block should have been replaced with an equivalent one. We'll
250 : // have the same number of blocks, but the code block should no longer
251 : // be in the graph.
252 E : EXPECT_EQ(2U, block_graph_.blocks().size());
253 E : EXPECT_EQ(data_block_, block_graph_.GetBlockById(data_block_id));
254 E : EXPECT_EQ(NULL, block_graph_.GetBlockById(code_block_id));
255 :
256 : // Clean up our dangling pointer.
257 E : code_block_ = NULL;
258 :
259 : // Find the new block.
260 E : ASSERT_EQ(1U, new_blocks.size());
261 E : const BlockGraph::Block* new_block = new_blocks[0];
262 :
263 : // Validate the references.
264 E : EXPECT_EQ(1U, new_block->references().size());
265 E : BlockGraph::Reference ref;
266 E : EXPECT_TRUE(new_block->GetReference(kOffsetOfReferenceToData, &ref));
267 E : EXPECT_EQ(kOffsetOfData, ref.offset());
268 E : EXPECT_EQ(data_block_, ref.referenced());
269 :
270 : // Validate the referrers.
271 E : EXPECT_EQ(1U, new_block->referrers().size());
272 E : EXPECT_EQ(data_block_, new_block->referrers().begin()->first);
273 E : EXPECT_EQ(kOffsetOfReferenceToCode, new_block->referrers().begin()->second);
274 : EXPECT_TRUE(new_block->referrers().begin()->first->GetReference(
275 E : kOffsetOfReferenceToCode, &ref));
276 E : EXPECT_EQ(new_block, ref.referenced());
277 E : }
278 :
279 E : TEST_F(ApplyBasicBlockSubGraphTransformTest, VectorTransformSucceeds) {
280 : // Validate applying a vector of transforms.
281 E : MockBasicBlockSubGraphTransform transform1;
282 E : MockBasicBlockSubGraphTransform transform2;
283 E : BlockVector new_blocks;
284 : EXPECT_CALL(transform1, TransformBasicBlockSubGraph(_, _, _)).Times(1).
285 E : WillOnce(Return(true));
286 : EXPECT_CALL(transform2, TransformBasicBlockSubGraph(_, _, _)).Times(1).
287 E : WillOnce(Return(true));
288 :
289 E : std::vector<BasicBlockSubGraphTransformInterface*> transforms;
290 E : transforms.push_back(&transform1);
291 E : transforms.push_back(&transform2);
292 : EXPECT_TRUE(ApplyBasicBlockSubGraphTransforms(transforms,
293 : &policy_,
294 : &block_graph_,
295 : code_block_,
296 E : &new_blocks));
297 E : }
298 :
299 : } // namespace block_graph
|