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 :
22 : namespace block_graph {
23 : namespace {
24 :
25 : using testing::_;
26 : using testing::Invoke;
27 : using testing::Return;
28 :
29 : // A constant data structure/buffer from which to initialize a data block.
30 : // The structure contains a core reference (function pointer) and an integer
31 : // data element.
32 : struct MyData {
33 : void (*code)(int);
34 : int data;
35 : };
36 : const BlockGraph::Offset kOffsetOfReferenceToCode = offsetof(MyData, code);
37 : const BlockGraph::Offset kOffsetOfData = offsetof(MyData, data);
38 : const MyData kDataBytes = { reinterpret_cast<void(*)(int)>(0xCAFEBABE),
39 : 0xDEADBEEF };
40 :
41 : // A byte buffer from which to initialize a code block. The original C source
42 : // code for this function is:
43 : //
44 : // static int y = 1;
45 : // void add(int x) {
46 : // y += x;
47 : // }
48 : //
49 : // Note the reference to
50 : // y starts 5 bytes from the end.
51 : const uint8 kCodeBytes[] = {
52 : 0x8B, 0x44, 0x24, 0x04, // mov eax,dword ptr [esp+4]
53 : 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, // add dword ptr [_y],eax
54 : 0xC3 // ret
55 : };
56 : const BlockGraph::Offset kOffsetOfCode = 0;
57 : const BlockGraph::Offset kOffsetOfReferenceToData = sizeof(kCodeBytes) - 5;
58 :
59 : class ApplyBlockGraphTransformTest : public testing::Test {
60 : public:
61 E : virtual void SetUp() {
62 E : header_block_ = block_graph_.AddBlock(BlockGraph::DATA_BLOCK, 10, "Header");
63 E : }
64 :
65 : protected:
66 : BlockGraph block_graph_;
67 : BlockGraph::Block* header_block_;
68 : };
69 :
70 : class MockBlockGraphTransform : public BlockGraphTransformInterface {
71 : public:
72 E : virtual ~MockBlockGraphTransform() { }
73 :
74 E : virtual const char* name() const { return "MockBlockGraphTransform"; }
75 :
76 E : MOCK_METHOD2(TransformBlockGraph, bool(BlockGraph*, BlockGraph::Block*));
77 :
78 : bool DeleteHeader(BlockGraph* block_graph,
79 E : BlockGraph::Block* header_block) {
80 E : CHECK(block_graph->RemoveBlock(header_block));
81 E : return true;
82 E : }
83 : };
84 :
85 : class ApplyBasicBlockSubGraphTransformTest : public testing::Test {
86 : public:
87 E : ApplyBasicBlockSubGraphTransformTest()
88 : : data_block_(NULL), code_block_(NULL) {
89 E : }
90 :
91 E : virtual void SetUp() {
92 : // Create some blocks to test with.
93 : data_block_ = block_graph_.AddBlock(
94 E : BlockGraph::DATA_BLOCK, sizeof(kDataBytes), "Data");
95 E : ASSERT_TRUE(data_block_ != NULL);
96 : code_block_ = block_graph_.AddBlock(
97 E : BlockGraph::CODE_BLOCK, sizeof(kCodeBytes), "Code");
98 E : ASSERT_TRUE(code_block_ != NULL);
99 :
100 : // Set up the data block.
101 : data_block_->SetData(reinterpret_cast<const uint8*>(&kDataBytes),
102 E : sizeof(kDataBytes));
103 :
104 : // Set up the code block.
105 E : ASSERT_TRUE(code_block_->SetLabel(
106 : kOffsetOfCode,
107 : BlockGraph::Label("Code", BlockGraph::CODE_LABEL)));
108 E : code_block_->SetData(kCodeBytes, sizeof(kCodeBytes));
109 :
110 : // Set up the references
111 E : ASSERT_TRUE(
112 : data_block_->SetReference(kOffsetOfReferenceToCode,
113 : MakeReference(code_block_, kOffsetOfCode)));
114 E : ASSERT_TRUE(
115 : code_block_->SetReference(kOffsetOfReferenceToData,
116 : MakeReference(data_block_, kOffsetOfData)));
117 E : }
118 :
119 : static BlockGraph::Reference MakeReference(BlockGraph::Block* target,
120 E : BlockGraph::Offset offset) {
121 E : EXPECT_TRUE(target != NULL);
122 : return BlockGraph::Reference(BlockGraph::RELATIVE_REF,
123 : BlockGraph::Reference::kMaximumSize,
124 E : target, offset, offset);
125 E : }
126 :
127 : protected:
128 : BlockGraph block_graph_;
129 : BlockGraph::Block* data_block_;
130 : BlockGraph::Block* code_block_;
131 : };
132 :
133 : class MockBasicBlockSubGraphTransform :
134 : public BasicBlockSubGraphTransformInterface {
135 : public:
136 E : virtual ~MockBasicBlockSubGraphTransform() { }
137 :
138 i : virtual const char* name() const { return "MockBasicBlockSubGraphTransform"; }
139 :
140 : MOCK_METHOD2(TransformBasicBlockSubGraph,
141 E : bool(BlockGraph*, BasicBlockSubGraph*));
142 : };
143 :
144 : } // namespace
145 :
146 E : TEST_F(ApplyBlockGraphTransformTest, NormalTransformSucceeds) {
147 E : MockBlockGraphTransform transform;
148 : EXPECT_CALL(transform, TransformBlockGraph(_, _)).Times(1).
149 E : WillOnce(Return(true));
150 : EXPECT_TRUE(ApplyBlockGraphTransform(&transform,
151 : &block_graph_,
152 E : header_block_));
153 E : }
154 :
155 E : TEST_F(ApplyBlockGraphTransformTest, DeletingHeaderFails) {
156 E : MockBlockGraphTransform transform;
157 : EXPECT_CALL(transform, TransformBlockGraph(_, _)).Times(1).WillOnce(
158 E : Invoke(&transform, &MockBlockGraphTransform::DeleteHeader));
159 : EXPECT_FALSE(ApplyBlockGraphTransform(&transform,
160 : &block_graph_,
161 E : header_block_));
162 E : }
163 :
164 E : TEST_F(ApplyBasicBlockSubGraphTransformTest, TransformFails) {
165 : // Remember the block ids of the original blocks.
166 E : BlockGraph::BlockId data_block_id = data_block_->id();
167 E : BlockGraph::BlockId code_block_id = code_block_->id();
168 :
169 : // Apply an empty transform that reports failure.
170 E : MockBasicBlockSubGraphTransform transform;
171 : EXPECT_CALL(transform, TransformBasicBlockSubGraph(_, _)).Times(1).
172 E : WillOnce(Return(false));
173 : EXPECT_FALSE(ApplyBasicBlockSubGraphTransform(&transform,
174 : &block_graph_,
175 : code_block_,
176 E : NULL));
177 :
178 : // The original block graph should be unchanged.
179 E : EXPECT_EQ(2U, block_graph_.blocks().size());
180 E : EXPECT_EQ(data_block_, block_graph_.GetBlockById(data_block_id));
181 E : EXPECT_EQ(code_block_, block_graph_.GetBlockById(code_block_id));
182 E : }
183 :
184 E : TEST_F(ApplyBasicBlockSubGraphTransformTest, EmptyTransformSucceeds) {
185 : // Remember the block ids of the original blocks.
186 E : BlockGraph::BlockId data_block_id = data_block_->id();
187 E : BlockGraph::BlockId code_block_id = code_block_->id();
188 :
189 : // Apply an empty transform that reports success.
190 E : MockBasicBlockSubGraphTransform transform;
191 E : BlockVector new_blocks;
192 : EXPECT_CALL(transform, TransformBasicBlockSubGraph(_, _)).Times(1).
193 E : WillOnce(Return(true));
194 : EXPECT_TRUE(ApplyBasicBlockSubGraphTransform(&transform,
195 : &block_graph_,
196 : code_block_,
197 E : &new_blocks));
198 :
199 : // The code block should have been replaced with an equivalent one. We'll
200 : // have the same number of blocks, but the code block should no longer
201 : // be in the graph.
202 E : EXPECT_EQ(2U, block_graph_.blocks().size());
203 E : EXPECT_EQ(data_block_, block_graph_.GetBlockById(data_block_id));
204 E : EXPECT_EQ(NULL, block_graph_.GetBlockById(code_block_id));
205 :
206 : // Clean up our dangling pointer.
207 E : code_block_ = NULL;
208 :
209 : // Find the new block.
210 E : ASSERT_EQ(1U, new_blocks.size());
211 E : const BlockGraph::Block* new_block = new_blocks[0];
212 :
213 : // Validate the references.
214 E : EXPECT_EQ(1U, new_block->references().size());
215 E : BlockGraph::Reference ref;
216 E : EXPECT_TRUE(new_block->GetReference(kOffsetOfReferenceToData, &ref));
217 E : EXPECT_EQ(kOffsetOfData, ref.offset());
218 E : EXPECT_EQ(data_block_, ref.referenced());
219 :
220 : // Validate the referrers.
221 E : EXPECT_EQ(1U, new_block->referrers().size());
222 E : EXPECT_EQ(data_block_, new_block->referrers().begin()->first);
223 E : EXPECT_EQ(kOffsetOfReferenceToCode, new_block->referrers().begin()->second);
224 : EXPECT_TRUE(new_block->referrers().begin()->first->GetReference(
225 E : kOffsetOfReferenceToCode, &ref));
226 E : EXPECT_EQ(new_block, ref.referenced());
227 E : }
228 :
229 : } // namespace block_graph
|