1 : // Copyright 2013 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 : #include "syzygy/pe/pe_transform_policy.h"
16 :
17 : #include "gtest/gtest.h"
18 : #include "syzygy/block_graph/basic_block_decomposer.h"
19 : #include "syzygy/block_graph/block_graph.h"
20 : #include "syzygy/pe/unittest_util.h"
21 :
22 : namespace pe {
23 :
24 : namespace {
25 :
26 : using block_graph::BlockGraph;
27 :
28 : class TestPETransformPolicy : public PETransformPolicy {
29 : public:
30 : typedef PETransformPolicy::BlockResultCache BlockResultCache;
31 :
32 : using PETransformPolicy::block_result_cache_;
33 : using PETransformPolicy::allow_inline_assembly_;
34 : };
35 :
36 : class PETransformPolicyTest : public testing::PELibUnitTest {
37 : public:
38 : enum ReferenceSource {
39 : kSelfCode,
40 : kSelfData,
41 : kCodeBlock,
42 : kDataBlock,
43 : };
44 :
45 : enum ReferenceTarget {
46 : kTopOfBlock,
47 : kInCode,
48 : kDataLabel,
49 : kInData
50 : };
51 :
52 : struct ReferrerConfiguration {
53 E : bool operator<(const ReferrerConfiguration& rhs) const {
54 E : if (ref_source < rhs.ref_source)
55 E : return true;
56 E : if (ref_source > rhs.ref_source)
57 E : return false;
58 E : if (ref_target < rhs.ref_target)
59 E : return true;
60 E : if (ref_target > rhs.ref_target)
61 E : return false;
62 E : if (ref_type < rhs.ref_type)
63 E : return true;
64 E : if (ref_type > rhs.ref_type)
65 E : return false;
66 E : if (ref_size < rhs.ref_size)
67 E : return true;
68 E : if (ref_size > rhs.ref_size)
69 E : return false;
70 E : if (ref_is_direct < rhs.ref_is_direct)
71 E : return true;
72 E : return false;
73 E : }
74 :
75 : ReferenceSource ref_source;
76 : ReferenceTarget ref_target;
77 : BlockGraph::ReferenceType ref_type;
78 : size_t ref_size;
79 : bool ref_is_direct;
80 : };
81 :
82 : void TestCodeBlockReferrersAreClConsistent(
83 : const ReferrerConfiguration& config,
84 E : bool expect_valid) {
85 E : BlockGraph bg;
86 E : BlockGraph::Block* dst = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "s");
87 :
88 E : BlockGraph::Offset src_offset = 0;
89 :
90 : // Get the source block and source offset.
91 E : BlockGraph::Block* src = NULL;
92 E : switch (config.ref_source) {
93 : case kSelfCode:
94 E : src = dst;
95 E : src_offset = 4;
96 E : break;
97 :
98 : case kSelfData:
99 E : src = dst;
100 E : src_offset = 24;
101 E : break;
102 :
103 : case kCodeBlock:
104 E : src = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c");
105 E : src_offset = 4;
106 E : break;
107 :
108 : case kDataBlock:
109 E : src = image_.AddBlock(BlockGraph::DATA_BLOCK, 40, "d");
110 E : src_offset = 4;
111 : break;
112 : }
113 :
114 : // Set up a data label in the destination block, which splits it in half.
115 E : ASSERT_TRUE(dst->SetLabel(20, BlockGraph::Label(
116 : "data", BlockGraph::DATA_LABEL)));
117 :
118 : // We need the data label to be self-referenced otherwise the referrers test
119 : // will always fail. This is from a different offset than what we would
120 : // ever use for src_offset (4 or 24).
121 E : ASSERT_TRUE(dst->SetReference(16,
122 : BlockGraph::Reference(BlockGraph::ABSOLUTE_REF, 4, dst, 20, 20)));
123 :
124 : // These are reference offsets in dst as a function of ref_target.
125 E : const BlockGraph::Offset kRefOffsets[] = { 0, 10, 20, 30 };
126 :
127 : // Create the offset and the reference.
128 E : BlockGraph::Offset ref_offset = kRefOffsets[config.ref_target];
129 E : BlockGraph::Offset ref_base = ref_offset;
130 E : if (!config.ref_is_direct)
131 E : ref_base += 4;
132 :
133 : // Create the reference.
134 : BlockGraph::Reference ref(config.ref_type, config.ref_size, dst, ref_offset,
135 E : ref_base);
136 E : ASSERT_TRUE(ref.IsValid());
137 E : ASSERT_EQ(config.ref_is_direct, ref.IsDirect());
138 :
139 : // Hook it up.
140 E : ASSERT_TRUE(src->SetReference(src_offset, ref));
141 :
142 : // Test the validity.
143 E : TestPETransformPolicy policy;
144 E : ASSERT_EQ(expect_valid, policy.CodeBlockReferrersAreClConsistent(dst));
145 E : }
146 :
147 : void TestAttributes(BlockGraph::BlockAttributes attributes,
148 : bool allow_inline_assembly,
149 E : bool result) {
150 E : PETransformPolicy policy;
151 E : BlockGraph bg;
152 E : BlockGraph::Block* b = bg.AddBlock(BlockGraph::CODE_BLOCK, 1, "code");
153 E : ASSERT_NE(reinterpret_cast<BlockGraph::Block*>(NULL), b);
154 E : b->set_attributes(attributes);
155 E : ASSERT_EQ(result, policy.CodeBlockAttributesAreBasicBlockSafe(
156 : b, allow_inline_assembly));
157 E : }
158 :
159 : BlockGraph image_;
160 : };
161 :
162 : } // namespace
163 :
164 E : TEST_F(PETransformPolicyTest, AccessorsAndMutators) {
165 E : TestPETransformPolicy policy;
166 E : EXPECT_FALSE(policy.allow_inline_assembly());
167 E : policy.set_allow_inline_assembly(true);
168 E : EXPECT_TRUE(policy.allow_inline_assembly());
169 E : policy.set_allow_inline_assembly(false);
170 E : EXPECT_FALSE(policy.allow_inline_assembly());
171 E : }
172 :
173 : TEST_F(PETransformPolicyTest,
174 E : CodeBlockAttributesAreBasicBlockSafeGapBlock) {
175 E : ASSERT_NO_FATAL_FAILURE(TestAttributes(BlockGraph::GAP_BLOCK, false, false));
176 E : ASSERT_NO_FATAL_FAILURE(TestAttributes(BlockGraph::GAP_BLOCK, true, false));
177 E : }
178 :
179 : TEST_F(PETransformPolicyTest,
180 E : CodeBlockAttributesAreBasicBlockSafePaddingBlock) {
181 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
182 E : BlockGraph::PADDING_BLOCK, false, false));
183 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
184 E : BlockGraph::PADDING_BLOCK, true, false));
185 E : }
186 :
187 : TEST_F(PETransformPolicyTest,
188 E : CodeBlockAttributesAreBasicBlockSafeHasInlineAssembly) {
189 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
190 E : BlockGraph::HAS_INLINE_ASSEMBLY, false, false));
191 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
192 E : BlockGraph::HAS_INLINE_ASSEMBLY, true, true));
193 E : }
194 :
195 : TEST_F(PETransformPolicyTest,
196 E : CodeBlockAttributesAreBasicBlockSafeUnsupportedCompiler) {
197 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
198 E : BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER, false, false));
199 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
200 E : BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER, true, false));
201 E : }
202 :
203 : TEST_F(PETransformPolicyTest,
204 E : CodeBlockAttributesAreBasicBlockSafeUnsupportedInstructions) {
205 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
206 E : BlockGraph::UNSUPPORTED_INSTRUCTIONS, false, false));
207 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
208 E : BlockGraph::UNSUPPORTED_INSTRUCTIONS, true, false));
209 E : }
210 :
211 : TEST_F(PETransformPolicyTest,
212 E : CodeBlockAttributesAreBasicBlockSafeExceptionHandling) {
213 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
214 E : BlockGraph::HAS_EXCEPTION_HANDLING, false, false));
215 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
216 E : BlockGraph::HAS_EXCEPTION_HANDLING, true, false));
217 E : }
218 :
219 : TEST_F(PETransformPolicyTest,
220 E : CodeBlockAttributesAreBasicBlockSafeBuiltBySyzygy) {
221 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
222 : BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER | BlockGraph::BUILT_BY_SYZYGY,
223 : false,
224 E : true));
225 : ASSERT_NO_FATAL_FAILURE(TestAttributes(
226 : BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER | BlockGraph::BUILT_BY_SYZYGY,
227 : true,
228 E : true));
229 E : }
230 :
231 E : TEST_F(PETransformPolicyTest, CodeBlockHasPrivateSymbols) {
232 E : TestPETransformPolicy policy;
233 :
234 E : pe::PEFile pe_file;
235 E : block_graph::BlockGraph block_graph;
236 E : pe::ImageLayout image_layout(&block_graph);
237 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll(&pe_file, &image_layout));
238 :
239 E : size_t count = 0;
240 E : BlockGraph::BlockMap::const_iterator it = block_graph.blocks().begin();
241 E : for (; it != block_graph.blocks().end(); ++it) {
242 E : if (it->second.type() != BlockGraph::CODE_BLOCK)
243 E : continue;
244 E : if (it->second.attributes() & BlockGraph::PADDING_BLOCK)
245 E : continue;
246 :
247 E : if (!policy.CodeBlockHasPrivateSymbols(&it->second)) {
248 E : ++count;
249 E : EXPECT_EQ(it->second.name(), "TestFunctionWithNoPrivateSymbols");
250 : }
251 E : }
252 E : EXPECT_EQ(1u, count);
253 E : }
254 :
255 E : TEST_F(PETransformPolicyTest, NoLabelsHasInvalidLayout) {
256 E : BlockGraph::Block* b = image_.AddBlock(BlockGraph::CODE_BLOCK, 1, "code");
257 E : TestPETransformPolicy policy;
258 E : EXPECT_FALSE(policy.CodeBlockLayoutIsClConsistent(b));
259 E : }
260 :
261 E : TEST_F(PETransformPolicyTest, CodeLabelPastEndHasInvalidLayout) {
262 E : BlockGraph::Block* b = image_.AddBlock(BlockGraph::CODE_BLOCK, 2, "code");
263 E : b->SetLabel(0, "code", BlockGraph::CODE_LABEL);
264 E : b->SetLabel(2, "code", BlockGraph::CODE_LABEL);
265 E : TestPETransformPolicy policy;
266 E : EXPECT_FALSE(policy.CodeBlockLayoutIsClConsistent(b));
267 E : }
268 :
269 E : TEST_F(PETransformPolicyTest, DataLabelPastEndHasInvalidLayout) {
270 E : BlockGraph::Block* b = image_.AddBlock(BlockGraph::CODE_BLOCK, 2, "code");
271 E : b->SetLabel(0, "code", BlockGraph::CODE_LABEL);
272 E : b->SetLabel(2, "data", BlockGraph::DATA_LABEL);
273 E : TestPETransformPolicy policy;
274 E : EXPECT_FALSE(policy.CodeBlockLayoutIsClConsistent(b));
275 E : }
276 :
277 E : TEST_F(PETransformPolicyTest, DebugEndInBlockAfterDataHasInvalidLayout) {
278 E : BlockGraph::Block* b = image_.AddBlock(BlockGraph::CODE_BLOCK, 3, "code");
279 E : b->SetLabel(0, "code", BlockGraph::CODE_LABEL);
280 E : b->SetLabel(1, "data", BlockGraph::DATA_LABEL);
281 E : b->SetLabel(2, "debug-end", BlockGraph::DEBUG_END_LABEL);
282 E : TestPETransformPolicy policy;
283 E : EXPECT_FALSE(policy.CodeBlockLayoutIsClConsistent(b));
284 E : }
285 :
286 E : TEST_F(PETransformPolicyTest, CodeAfterDataHasInvalidLayout) {
287 E : BlockGraph::Block* b = image_.AddBlock(BlockGraph::CODE_BLOCK, 2, "code");
288 E : b->SetLabel(0, "data", BlockGraph::DATA_LABEL);
289 E : b->SetLabel(1, "code", BlockGraph::CODE_LABEL);
290 E : TestPETransformPolicy policy;
291 E : EXPECT_FALSE(policy.CodeBlockLayoutIsClConsistent(b));
292 E : }
293 :
294 E : TEST_F(PETransformPolicyTest, DataSurroundedByCodeHasInvalidLayout) {
295 E : BlockGraph::Block* b = image_.AddBlock(BlockGraph::CODE_BLOCK, 3, "code");
296 E : b->SetLabel(0, "data", BlockGraph::DATA_LABEL);
297 E : b->SetLabel(1, "code", BlockGraph::CODE_LABEL);
298 E : b->SetLabel(2, "data", BlockGraph::DATA_LABEL);
299 E : TestPETransformPolicy policy;
300 E : EXPECT_FALSE(policy.CodeBlockLayoutIsClConsistent(b));
301 E : }
302 :
303 E : TEST_F(PETransformPolicyTest, CodeOnlyHasValidLayout) {
304 E : BlockGraph::Block* b = image_.AddBlock(BlockGraph::CODE_BLOCK, 1, "code");
305 E : b->SetLabel(0, "code", BlockGraph::CODE_LABEL);
306 E : TestPETransformPolicy policy;
307 E : EXPECT_TRUE(policy.CodeBlockLayoutIsClConsistent(b));
308 :
309 : // This should still be true even with a debug-end label beyond the end.
310 E : b->SetLabel(1, "debug-end", BlockGraph::DEBUG_END_LABEL);
311 E : EXPECT_TRUE(policy.CodeBlockLayoutIsClConsistent(b));
312 E : }
313 :
314 E : TEST_F(PETransformPolicyTest, CodeFollowedByDataHasValidLayout) {
315 E : BlockGraph::Block* b = image_.AddBlock(BlockGraph::CODE_BLOCK, 2, "code");
316 E : b->SetLabel(0, "code", BlockGraph::CODE_LABEL);
317 E : b->SetLabel(1, "data", BlockGraph::DATA_LABEL);
318 :
319 E : TestPETransformPolicy policy;
320 E : EXPECT_TRUE(policy.CodeBlockLayoutIsClConsistent(b));
321 :
322 : // This should still be true even with a debug-end label beyond the end.
323 E : b->SetLabel(2, "debug-end", BlockGraph::DEBUG_END_LABEL);
324 E : EXPECT_TRUE(policy.CodeBlockLayoutIsClConsistent(b));
325 E : }
326 :
327 E : TEST_F(PETransformPolicyTest, DirectReferencesFromCodeAreClConsistent) {
328 E : BlockGraph::Block* code1 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c1");
329 E : BlockGraph::Block* code2 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c2");
330 E : BlockGraph::Block* data1 = image_.AddBlock(BlockGraph::DATA_BLOCK, 40, "d1");
331 :
332 : // Direct code reference.
333 : EXPECT_TRUE(code1->SetReference(
334 E : 0, BlockGraph::Reference(BlockGraph::RELATIVE_REF, 4, code2, 0, 0)));
335 :
336 : // Direct data reference.
337 : EXPECT_TRUE(code1->SetReference(
338 E : 4, BlockGraph::Reference(BlockGraph::RELATIVE_REF, 4, data1, 0, 0)));
339 :
340 : // Direct self-reference.
341 : EXPECT_TRUE(code1->SetReference(
342 E : 8, BlockGraph::Reference(BlockGraph::RELATIVE_REF, 4, code1, 4, 4)));
343 :
344 E : TestPETransformPolicy policy;
345 E : EXPECT_TRUE(policy.CodeBlockReferencesAreClConsistent(code1));
346 E : }
347 :
348 : TEST_F(PETransformPolicyTest,
349 E : IndirectReferencesFromCodeToCodeAreNotClConsistent) {
350 E : BlockGraph::Block* code1 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c1");
351 E : BlockGraph::Block* code2 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c2");
352 :
353 : // Indirect code reference.
354 : EXPECT_TRUE(code1->SetReference(
355 E : 0, BlockGraph::Reference(BlockGraph::RELATIVE_REF, 4, code2, 0, 4)));
356 :
357 E : TestPETransformPolicy policy;
358 E : EXPECT_FALSE(policy.CodeBlockReferencesAreClConsistent(code1));
359 E : }
360 :
361 E : TEST_F(PETransformPolicyTest, IndirectReferencesFromCodeToDataAreClConsistent) {
362 E : BlockGraph::Block* code1 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c1");
363 E : BlockGraph::Block* data1 = image_.AddBlock(BlockGraph::DATA_BLOCK, 40, "d1");
364 :
365 : // Indirect data reference.
366 : EXPECT_TRUE(code1->SetReference(
367 E : 0, BlockGraph::Reference(BlockGraph::RELATIVE_REF, 4, data1, 0, 4)));
368 :
369 E : TestPETransformPolicy policy;
370 E : EXPECT_TRUE(policy.CodeBlockReferencesAreClConsistent(code1));
371 E : }
372 :
373 : TEST_F(PETransformPolicyTest,
374 E : IndirectSelfReferencesFromCodeAreNotClConsistent) {
375 E : BlockGraph::Block* code1 = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c1");
376 :
377 : // Indirect self reference.
378 : EXPECT_TRUE(code1->SetReference(
379 E : 0, BlockGraph::Reference(BlockGraph::RELATIVE_REF, 4, code1, 4, 8)));
380 :
381 E : TestPETransformPolicy policy;
382 E : EXPECT_FALSE(policy.CodeBlockReferencesAreClConsistent(code1));
383 E : }
384 :
385 E : TEST_F(PETransformPolicyTest, CodeBlockReferrersAreClConsistent) {
386 : // These are all the possible input values to be explored.
387 : const ReferenceSource kRefSource[] = {
388 E : kSelfCode, kSelfData, kCodeBlock, kDataBlock };
389 : const ReferenceTarget kRefTarget[] = {
390 E : kTopOfBlock, kInCode, kDataLabel, kInData };
391 : const BlockGraph::ReferenceType kRefType[] = {
392 : BlockGraph::PC_RELATIVE_REF, BlockGraph::ABSOLUTE_REF,
393 E : BlockGraph::RELATIVE_REF, BlockGraph::FILE_OFFSET_REF };
394 E : const size_t kRefSize[] = { 1, 4 };
395 E : const bool kRefIsDirect[] = { false, true };
396 :
397 : static size_t kNumberOfPermutations =
398 : arraysize(kRefSource) * arraysize(kRefTarget) * arraysize(kRefType) *
399 : arraysize(kRefSize) * arraysize(kRefIsDirect);
400 :
401 : // This is the short list of permutations that we expect to be valid. All
402 : // others should be false.
403 : const ReferrerConfiguration kValidConfigs[] = {
404 : // Self-references from code to code.
405 : { kSelfCode, kTopOfBlock, BlockGraph::PC_RELATIVE_REF, 1, true },
406 : { kSelfCode, kTopOfBlock, BlockGraph::PC_RELATIVE_REF, 4, true },
407 : { kSelfCode, kTopOfBlock, BlockGraph::ABSOLUTE_REF, 4, true },
408 : { kSelfCode, kInCode, BlockGraph::PC_RELATIVE_REF, 1, true },
409 : { kSelfCode, kInCode, BlockGraph::PC_RELATIVE_REF, 4, true },
410 : { kSelfCode, kInCode, BlockGraph::ABSOLUTE_REF, 4, true },
411 :
412 : // Self-references from code to data.
413 : { kSelfCode, kDataLabel, BlockGraph::ABSOLUTE_REF, 4, true },
414 :
415 : // Self-references from data to code.
416 : { kSelfData, kTopOfBlock, BlockGraph::ABSOLUTE_REF, 4, true },
417 : { kSelfData, kInCode, BlockGraph::ABSOLUTE_REF, 4, true },
418 :
419 : // Self-references from data to data.
420 : { kSelfData, kDataLabel, BlockGraph::ABSOLUTE_REF, 4, true },
421 : { kSelfData, kInData, BlockGraph::ABSOLUTE_REF, 4, true },
422 :
423 : // External references from code to code.
424 : { kCodeBlock, kTopOfBlock, BlockGraph::PC_RELATIVE_REF, 4, true },
425 : { kCodeBlock, kTopOfBlock, BlockGraph::ABSOLUTE_REF, 4, true },
426 :
427 : // External references from data to code.
428 : { kDataBlock, kTopOfBlock, BlockGraph::ABSOLUTE_REF, 4, true },
429 : { kDataBlock, kTopOfBlock, BlockGraph::RELATIVE_REF, 4, true },
430 E : };
431 E : std::set<ReferrerConfiguration> valid_configs;
432 E : for (size_t i = 0; i < arraysize(kValidConfigs); ++i) {
433 E : ASSERT_TRUE(valid_configs.insert(kValidConfigs[i]).second);
434 E : }
435 :
436 : // Walk through all possible permutations.
437 E : for (size_t i = 0; i < kNumberOfPermutations; ++i) {
438 E : size_t j = i;
439 :
440 E : ReferenceSource ref_source = kRefSource[j % arraysize(kRefSource)];
441 E : j /= arraysize(kRefSource);
442 :
443 E : ReferenceTarget ref_target = kRefTarget[j % arraysize(kRefTarget)];
444 E : j /= arraysize(kRefTarget);
445 :
446 E : BlockGraph::ReferenceType ref_type = kRefType[j % arraysize(kRefType)];
447 E : j /= arraysize(kRefType);
448 :
449 E : size_t ref_size = kRefSize[j % arraysize(kRefSize)];
450 E : j /= arraysize(kRefSize);
451 :
452 E : bool ref_is_direct = kRefIsDirect[j % arraysize(kRefIsDirect)];
453 :
454 : // If the reference type and size is not valid, skip this test.
455 E : if (!BlockGraph::Reference::IsValidTypeSize(ref_type, ref_size))
456 E : continue;
457 :
458 : ReferrerConfiguration config = { ref_source, ref_target, ref_type,
459 E : ref_size, ref_is_direct };
460 :
461 E : bool expect_valid = valid_configs.count(config);
462 : ASSERT_NO_FATAL_FAILURE(TestCodeBlockReferrersAreClConsistent(
463 E : config, expect_valid));
464 E : }
465 E : }
466 :
467 : TEST_F(PETransformPolicyTest,
468 E : CodeBlockReferrersAreClConsistentUnreferencedLabels) {
469 E : BlockGraph::Block* code = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c");
470 E : const BlockGraph::Offset kDataLabelOffset = 0x10;
471 : code->SetLabel(kDataLabelOffset,
472 E : BlockGraph::Label("data", BlockGraph::DATA_LABEL));
473 :
474 : // We have a single unreferenced data label.
475 E : TestPETransformPolicy policy;
476 E : ASSERT_FALSE(policy.CodeBlockReferrersAreClConsistent(code));
477 :
478 : BlockGraph::Reference ref(BlockGraph::ABSOLUTE_REF,
479 : sizeof(core::AbsoluteAddress),
480 : code,
481 : kDataLabelOffset,
482 E : kDataLabelOffset);
483 : // Add a reference from code to the data label.
484 E : code->SetReference(kDataLabelOffset - 0x8, ref);
485 :
486 : // We're now consistent.
487 E : ASSERT_TRUE(policy.CodeBlockReferrersAreClConsistent(code));
488 :
489 : // Remove the reference and move it into code.
490 E : code->RemoveReference(kDataLabelOffset - 0x8);
491 E : code->SetReference(kDataLabelOffset + 0x8, ref);
492 :
493 : // Consistent again.
494 E : ASSERT_TRUE(policy.CodeBlockReferrersAreClConsistent(code));
495 E : }
496 :
497 : TEST_F(PETransformPolicyTest,
498 E : CodeBlockReferrersAreClConsistentUnreferencedData) {
499 E : BlockGraph::Block* code = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c");
500 : ASSERT_TRUE(code->SetLabel(20, BlockGraph::Label(
501 E : "data", BlockGraph::DATA_LABEL)));
502 E : TestPETransformPolicy policy;
503 E : ASSERT_FALSE(policy.CodeBlockReferrersAreClConsistent(code));
504 E : }
505 :
506 : TEST_F(PETransformPolicyTest,
507 E : CodeBlockIsSafeToBasicBlockDecomposeableSimpleBlock) {
508 E : BlockGraph::Block* code = image_.AddBlock(BlockGraph::CODE_BLOCK, 1, "code");
509 E : code->SetLabel(0, "code", BlockGraph::CODE_LABEL);
510 E : TestPETransformPolicy policy;
511 E : ASSERT_TRUE(policy.CodeBlockIsSafeToBasicBlockDecompose(code));
512 E : }
513 :
514 : TEST_F(PETransformPolicyTest,
515 E : CodeBlockIsSafeToBasicBlockDecomposeBuiltBySyzygy) {
516 E : BlockGraph::Block* code = image_.AddBlock(BlockGraph::CODE_BLOCK, 40, "c");
517 E : code->set_attribute(BlockGraph::BUILT_BY_SYZYGY);
518 E : TestPETransformPolicy policy;
519 E : ASSERT_TRUE(policy.CodeBlockIsSafeToBasicBlockDecompose(code));
520 :
521 : // Even if this block has unreferenced data, it should be fine.
522 : ASSERT_TRUE(code->SetLabel(20, BlockGraph::Label(
523 E : "data", BlockGraph::DATA_LABEL)));
524 E : ASSERT_TRUE(policy.CodeBlockIsSafeToBasicBlockDecompose(code));
525 E : }
526 :
527 E : TEST_F(PETransformPolicyTest, DataBlockIsNotSafeToBasicBlockDecompose) {
528 E : TestPETransformPolicy policy;
529 :
530 E : BlockGraph::Block* data = image_.AddBlock(BlockGraph::DATA_BLOCK, 1, "d");
531 E : ASSERT_FALSE(policy.BlockIsSafeToBasicBlockDecompose(data));
532 E : }
533 :
534 E : TEST_F(PETransformPolicyTest, CodeBlockIsSafeToBasicBlockDecomposeCache) {
535 E : TestPETransformPolicy policy;
536 E : EXPECT_EQ(0u, policy.block_result_cache_->size());
537 :
538 E : BlockGraph::Block* code = image_.AddBlock(BlockGraph::CODE_BLOCK, 2, "c");
539 E : code->SetLabel(0, "code", BlockGraph::CODE_LABEL);
540 E : ASSERT_TRUE(policy.BlockIsSafeToBasicBlockDecompose(code));
541 E : EXPECT_EQ(1u, policy.block_result_cache_->size());
542 :
543 : TestPETransformPolicy::BlockResultCache::const_iterator it =
544 E : policy.block_result_cache_->find(code->id());
545 E : ASSERT_NE(policy.block_result_cache_->end(), it);
546 E : EXPECT_EQ(code->id(), it->first);
547 E : EXPECT_TRUE(it->second);
548 :
549 : // Add an unreferenced data label. This should make the analysis fail.
550 : // However, it should be looked up in the cache and return true.
551 : ASSERT_TRUE(code->SetLabel(1, BlockGraph::Label(
552 E : "data", BlockGraph::DATA_LABEL)));
553 E : ASSERT_FALSE(policy.CodeBlockIsSafeToBasicBlockDecompose(code));
554 E : ASSERT_TRUE(policy.BlockIsSafeToBasicBlockDecompose(code));
555 E : EXPECT_EQ(1u, policy.block_result_cache_->size());
556 E : }
557 :
558 : TEST_F(PETransformPolicyTest,
559 E : CodeBlockIsSafeToBasicBlockDecomposeAttributesNotCached) {
560 E : TestPETransformPolicy policy;
561 E : EXPECT_EQ(0u, policy.block_result_cache_->size());
562 :
563 E : BlockGraph::Block* code = image_.AddBlock(BlockGraph::CODE_BLOCK, 2, "c");
564 E : code->SetLabel(0, "code", BlockGraph::CODE_LABEL);
565 E : ASSERT_TRUE(policy.BlockIsSafeToBasicBlockDecompose(code));
566 E : EXPECT_EQ(1u, policy.block_result_cache_->size());
567 :
568 : TestPETransformPolicy::BlockResultCache::const_iterator it =
569 E : policy.block_result_cache_->find(code->id());
570 E : ASSERT_NE(policy.block_result_cache_->end(), it);
571 E : EXPECT_EQ(code->id(), it->first);
572 E : EXPECT_TRUE(it->second);
573 :
574 : // Set an attribute that disqualifies decomposition. This should return false
575 : // from both functions, ignoring the cached result.
576 E : code->set_attribute(BlockGraph::UNSUPPORTED_INSTRUCTIONS);
577 E : ASSERT_FALSE(policy.CodeBlockIsSafeToBasicBlockDecompose(code));
578 E : ASSERT_FALSE(policy.BlockIsSafeToBasicBlockDecompose(code));
579 E : EXPECT_EQ(1u, policy.block_result_cache_->size());
580 E : }
581 :
582 E : TEST_F(PETransformPolicyTest, ReferenceIsSafeToRedirect) {
583 E : TestPETransformPolicy policy;
584 E : BlockGraph bg;
585 E : BlockGraph::Block* b = bg.AddBlock(BlockGraph::CODE_BLOCK, 1, "");
586 E : BlockGraph::Reference ref(BlockGraph::ABSOLUTE_REF, 4, b, 0, 0);
587 E : EXPECT_TRUE(policy.ReferenceIsSafeToRedirect(b, ref));
588 E : }
589 :
590 E : TEST_F(PETransformPolicyTest, BasicBlockDisassemblyFilter) {
591 E : TestPETransformPolicy policy;
592 :
593 E : pe::PEFile pe_file;
594 E : block_graph::BlockGraph block_graph;
595 E : pe::ImageLayout image_layout(&block_graph);
596 E : ASSERT_NO_FATAL_FAILURE(DecomposeTestDll(&pe_file, &image_layout));
597 :
598 E : BlockGraph::BlockMap::const_iterator it = block_graph.blocks().begin();
599 E : for (; it != block_graph.blocks().end(); ++it) {
600 E : if (!policy.BlockIsSafeToBasicBlockDecompose(&it->second))
601 E : continue;
602 :
603 : // Basic-block decomposition should not fail.
604 E : block_graph::BasicBlockSubGraph bbsg;
605 E : block_graph::BasicBlockDecomposer bbdecomp(&it->second, &bbsg);
606 E : EXPECT_TRUE(bbdecomp.Decompose());
607 E : }
608 E : }
609 :
610 : } // namespace pe
|