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 : // Tests for basic block disassembler.
16 :
17 : #include "syzygy/block_graph/basic_block_decomposer.h"
18 :
19 : #include <algorithm>
20 : #include <vector>
21 :
22 : #include "base/bind.h"
23 : #include "base/command_line.h"
24 : #include "base/files/file_util.h"
25 : #include "base/memory/scoped_ptr.h"
26 : #include "gmock/gmock.h"
27 : #include "gtest/gtest.h"
28 : #include "syzygy/block_graph/basic_block_test_util.h"
29 : #include "syzygy/block_graph/block_graph_serializer.h"
30 : #include "syzygy/core/address.h"
31 : #include "syzygy/core/serialization.h"
32 : #include "syzygy/core/unittest_util.h"
33 :
34 : #include "mnemonics.h" // NOLINT
35 :
36 : namespace block_graph {
37 :
38 : namespace {
39 :
40 : using block_graph::BasicBlock;
41 : using block_graph::BasicBlockSubGraph;
42 : using block_graph::BlockGraph;
43 : using block_graph::BlockGraphSerializer;
44 : using block_graph::Successor;
45 : using core::AbsoluteAddress;
46 : using core::Disassembler;
47 : using testing::_;
48 : using testing::Return;
49 :
50 : typedef BlockGraph::Block Block;
51 : typedef BlockGraph::Offset Offset;
52 : typedef BlockGraph::Reference Reference;
53 : typedef BlockGraph::Size Size;
54 :
55 : // A helper to count basic blocks of a given type.
56 : size_t CountBasicBlocks(const BasicBlockSubGraph& subgraph,
57 E : BasicBlock::BasicBlockType type) {
58 E : size_t counter = 0;
59 : BasicBlockSubGraph::BBCollection::const_iterator bb_it =
60 E : subgraph.basic_blocks().begin();
61 E : for (; bb_it != subgraph.basic_blocks().end(); ++bb_it) {
62 E : if ((*bb_it)->type() == type)
63 E : ++counter;
64 E : }
65 :
66 E : return counter;
67 E : }
68 :
69 : // A helper to count padding basic blocks of a given type.
70 : size_t CountPaddingBasicBlocks(const BasicBlockSubGraph& subgraph,
71 E : BasicBlock::BasicBlockType type) {
72 E : size_t counter = 0;
73 : BasicBlockSubGraph::BBCollection::const_iterator bb_it =
74 E : subgraph.basic_blocks().begin();
75 E : for (; bb_it != subgraph.basic_blocks().end(); ++bb_it) {
76 E : if ((*bb_it)->type() == type && (*bb_it)->is_padding())
77 E : ++counter;
78 E : }
79 :
80 E : return counter;
81 E : }
82 :
83 : // A helper comparator to that returns true if lhs and rhs are not adjacent
84 : // and in order.
85 E : bool HasGapOrIsOutOfOrder(const BasicBlock* lhs, const BasicBlock* rhs) {
86 : typedef BasicBlock::Size Size;
87 :
88 E : Offset lhs_end = lhs->offset();
89 :
90 E : const BasicCodeBlock* lhs_code = BasicCodeBlock::Cast(lhs);
91 E : if (lhs_code != NULL) {
92 E : lhs_end += lhs_code->GetInstructionSize();
93 :
94 E : BasicBlock::Successors::const_iterator it(lhs_code->successors().begin());
95 E : for (; it != lhs_code->successors().end(); ++it) {
96 E : lhs_end += it->instruction_size();
97 E : }
98 : }
99 E : const BasicDataBlock* lhs_data = BasicDataBlock::Cast(lhs);
100 E : if (lhs_data != NULL)
101 E : lhs_end += lhs_data->size();
102 :
103 E : return lhs_end != rhs->offset();
104 E : }
105 :
106 : // A test fixture which generates a block-graph to use for basic-block
107 : // related testing.
108 : // See: basic_block_assembly_func.asm
109 : class BasicBlockDecomposerTest : public testing::BasicBlockTest {
110 : public:
111 E : void InitBlockGraphFromSerializedFile(const wchar_t* src_relative_path) {
112 E : base::FilePath path = testing::GetSrcRelativePath(src_relative_path);
113 E : base::ScopedFILE file(base::OpenFile(path, "rb"));
114 E : ASSERT_TRUE(file.get() != NULL);
115 E : core::FileInStream is(file.get());
116 E : core::InArchive ia(&is);
117 E : BlockGraphSerializer bgs;
118 E : ASSERT_TRUE(bgs.Load(&block_graph_, &ia));
119 E : }
120 : };
121 :
122 : // Calculates the net size of all bytes covered by the given basic-block
123 : // decomposition.
124 E : size_t GetNetBBSize(const BasicBlockSubGraph& bbsg) {
125 : // We expect the decomposition to cover the entire block.
126 E : size_t net_bb_size = 0;
127 E : for (BasicBlockSubGraph::BBCollection::const_iterator bb_it =
128 : bbsg.basic_blocks().begin();
129 E : bb_it != bbsg.basic_blocks().end();
130 E : ++bb_it) {
131 E : switch ((*bb_it)->type()) {
132 : case BasicBlock::BASIC_CODE_BLOCK: {
133 E : const BasicCodeBlock* bcb = BasicCodeBlock::Cast(*bb_it);
134 E : CHECK_NE(reinterpret_cast<const BasicCodeBlock*>(NULL), bcb);
135 E : net_bb_size += bcb->GetInstructionSize();
136 :
137 E : for (BasicCodeBlock::Successors::const_iterator succ_it =
138 : bcb->successors().begin();
139 E : succ_it != bcb->successors().end();
140 E : ++succ_it) {
141 E : net_bb_size += succ_it->instruction_size();
142 E : }
143 :
144 E : break;
145 : }
146 :
147 : case BasicBlock::BASIC_DATA_BLOCK: {
148 i : const BasicDataBlock* bdb = BasicDataBlock::Cast(*bb_it);
149 i : CHECK_NE(reinterpret_cast<const BasicDataBlock*>(NULL), bdb);
150 i : net_bb_size += bdb->size();
151 : break;
152 : }
153 : }
154 E : }
155 E : return net_bb_size;
156 E : }
157 :
158 : struct BasicBlockOffsetComparator {
159 E : bool operator()(const BasicBlock* bb0, const BasicBlock* bb1) {
160 E : DCHECK_NE(reinterpret_cast<const BasicBlock*>(NULL), bb0);
161 E : DCHECK_NE(reinterpret_cast<const BasicBlock*>(NULL), bb1);
162 E : return bb0->offset() < bb1->offset();
163 E : }
164 : };
165 :
166 E : void ValidateHasInlineAssemblyBlock5677(const BasicBlockSubGraph& bbsg) {
167 E : ASSERT_EQ(4u, bbsg.basic_blocks().size());
168 :
169 : // Get the basic blocks sorted by their original offsets.
170 : std::vector<const BasicBlock*> bbs(bbsg.basic_blocks().begin(),
171 E : bbsg.basic_blocks().end());
172 E : std::sort(bbs.begin(), bbs.end(), BasicBlockOffsetComparator());
173 :
174 : // cachedHasSSE2
175 : // bb0:
176 : // 0044DA50 push ebp
177 : // 0044DA51 mov ebp,esp
178 : // 0044DA53 mov eax,1
179 : // 0044DA58 sub esp,14h
180 : // 0044DA5B test byte ptr ds:[41EA07Ch],al
181 : // 0044DA61 jne bb2
182 E : EXPECT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs[0]->type());
183 E : const BasicCodeBlock* bcb0 = BasicCodeBlock::Cast(bbs[0]);
184 E : ASSERT_NE(reinterpret_cast<const BasicCodeBlock*>(NULL), bcb0);
185 E : EXPECT_EQ(0, bcb0->offset());
186 E : EXPECT_EQ(5u, bcb0->instructions().size());
187 E : EXPECT_EQ(17u, bcb0->GetInstructionSize());
188 E : EXPECT_EQ(2u, bcb0->successors().size());
189 :
190 : // bb1:
191 : // 0044DA63 or dword ptr ds:[41EA07Ch],eax
192 : // 0044DA69 xor eax,eax
193 : // 0044DA6B mov dword ptr [ebp-14h],eax
194 : // 0044DA6E mov dword ptr [ebp-10h],eax
195 : // 0044DA71 mov dword ptr [ebp-0Ch],eax
196 : // 0044DA74 mov dword ptr [ebp-8],eax
197 : // 0044DA77 push ebx
198 : // 0044DA78 lea eax,[ebp-14h]
199 : // 0044DA7B push edi
200 : // 0044DA7C mov dword ptr [ebp-4],eax
201 : // 0044DA7F mov eax,1
202 : // 0044DA84 cpuid
203 : // 0044DA86 mov edi,dword ptr [ebp-4]
204 : // 0044DA89 mov dword ptr [edi],eax
205 : // 0044DA8B mov dword ptr [edi+4],ebx
206 : // 0044DA8E mov dword ptr [edi+8],ecx
207 : // 0044DA91 mov dword ptr [edi+0Ch],edx
208 : // 0044DA94 mov eax,dword ptr [ebp-8]
209 : // 0044DA97 shr eax,1Ah
210 : // 0044DA9A and al,1
211 : // 0044DA9C pop edi
212 : // 0044DA9D mov byte ptr ds:[041EA078h],al
213 : // 0044DAA2 pop ebx
214 : // 0044DAA3 mov esp,ebp
215 : // 0044DAA5 pop ebp
216 : // 0044DAA6 ret
217 E : EXPECT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs[1]->type());
218 E : const BasicCodeBlock* bcb1 = BasicCodeBlock::Cast(bbs[1]);
219 E : ASSERT_NE(reinterpret_cast<const BasicCodeBlock*>(NULL), bcb1);
220 E : EXPECT_EQ(19, bcb1->offset());
221 E : EXPECT_EQ(26u, bcb1->instructions().size());
222 E : EXPECT_EQ(68u, bcb1->GetInstructionSize());
223 E : EXPECT_EQ(0u, bcb1->successors().size());
224 :
225 : // bb2:
226 : // 0044DAA7 mov al,byte ptr ds:[041EA078h]
227 : // 0044DAAC mov esp,ebp
228 : // 0044DAAE pop ebp
229 : // 0044DAAF ret
230 : // 0044DAB0
231 E : EXPECT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs[2]->type());
232 E : const BasicCodeBlock* bcb2 = BasicCodeBlock::Cast(bbs[2]);
233 E : ASSERT_NE(reinterpret_cast<const BasicCodeBlock*>(NULL), bcb2);
234 E : EXPECT_EQ(87, bcb2->offset());
235 E : EXPECT_EQ(4u, bcb2->instructions().size());
236 E : EXPECT_EQ(9u, bcb2->GetInstructionSize());
237 E : EXPECT_EQ(0u, bcb2->successors().size());
238 :
239 E : EXPECT_EQ(BasicBlock::BASIC_END_BLOCK, bbs[3]->type());
240 E : }
241 :
242 : } // namespace
243 :
244 E : TEST_F(BasicBlockDecomposerTest, DecomposeNoSubGraph) {
245 E : ASSERT_NO_FATAL_FAILURE(InitBlockGraph());
246 E : BasicBlockDecomposer bbd(assembly_func_, NULL);
247 E : EXPECT_TRUE(bbd.Decompose());
248 E : EXPECT_FALSE(bbd.contains_unsupported_instructions());
249 E : }
250 :
251 E : TEST_F(BasicBlockDecomposerTest, Decompose) {
252 E : ASSERT_NO_FATAL_FAILURE(InitBlockGraph());
253 E : ASSERT_NO_FATAL_FAILURE(InitBasicBlockSubGraph());
254 :
255 : // Ensure we have the expected number and types of blocks.
256 E : ASSERT_EQ(kNumBasicBlocks, subgraph_.basic_blocks().size());
257 : ASSERT_EQ(kNumCodeBasicBlocks,
258 E : CountBasicBlocks(subgraph_, BasicBlock::BASIC_CODE_BLOCK));
259 : ASSERT_EQ(kNumDataBasicBlocks,
260 E : CountBasicBlocks(subgraph_, BasicBlock::BASIC_DATA_BLOCK));
261 : ASSERT_EQ(kNumEndBasicBlocks,
262 E : CountBasicBlocks(subgraph_, BasicBlock::BASIC_END_BLOCK));
263 : ASSERT_EQ(kNumCodePaddingBasicBlocks,
264 E : CountPaddingBasicBlocks(subgraph_, BasicBlock::BASIC_CODE_BLOCK));
265 : ASSERT_EQ(kNumDataPaddingBasicBlocks,
266 E : CountPaddingBasicBlocks(subgraph_, BasicBlock::BASIC_DATA_BLOCK));
267 :
268 : // There should be no gaps and all of the blocks should be used.
269 E : ASSERT_EQ(1U, subgraph_.block_descriptions().size());
270 : const BasicBlockSubGraph::BlockDescription& desc =
271 E : subgraph_.block_descriptions().back();
272 E : EXPECT_EQ(kNumBasicBlocks, desc.basic_block_order.size());
273 : EXPECT_TRUE(
274 : std::adjacent_find(
275 : desc.basic_block_order.begin(),
276 : desc.basic_block_order.end(),
277 E : &HasGapOrIsOutOfOrder) == desc.basic_block_order.end());
278 :
279 E : BasicBlockSubGraph::ReachabilityMap rm;
280 E : subgraph_.GetReachabilityMap(&rm);
281 :
282 : // Basic-block 0 - assembly_func.
283 E : ASSERT_TRUE(BasicBlockSubGraph::IsReachable(rm, bbs_[0]));
284 E : ASSERT_FALSE(bbs_[0]->is_padding());
285 E : ASSERT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs_[0]->type());
286 E : BasicCodeBlock* bb0 = BasicCodeBlock::Cast(bbs_[0]);
287 E : ASSERT_TRUE(bb0 != NULL);
288 E : ASSERT_EQ(4u, bb0->instructions().size());
289 E : ASSERT_EQ(0u, bb0->successors().size());
290 : BasicBlock::Instructions::const_iterator inst_iter =
291 E : bb0->instructions().begin();
292 E : std::advance(inst_iter, 2);
293 E : ASSERT_EQ(1u, inst_iter->references().size());
294 E : ASSERT_EQ(bbs_[9], inst_iter->references().begin()->second.basic_block());
295 E : std::advance(inst_iter, 1);
296 E : ASSERT_EQ(1u, inst_iter->references().size());
297 E : ASSERT_EQ(bbs_[8], inst_iter->references().begin()->second.basic_block());
298 E : ASSERT_EQ(1u, bbs_[0]->alignment());
299 :
300 : // Basic-block 1 - unreachable-label.
301 E : ASSERT_FALSE(BasicBlockSubGraph::IsReachable(rm, bbs_[1]));
302 E : ASSERT_TRUE(bbs_[1]->is_padding());
303 E : ASSERT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs_[1]->type());
304 E : BasicCodeBlock* bb1 = BasicCodeBlock::Cast(bbs_[1]);
305 E : ASSERT_EQ(1u, bb1->instructions().size());
306 E : ASSERT_EQ(1u, bb1->successors().size());
307 : ASSERT_EQ(bbs_[2],
308 E : bb1->successors().front().reference().basic_block());
309 E : ASSERT_EQ(1u, bb1->alignment());
310 :
311 : // Basic-block 2 - case_0.
312 E : ASSERT_TRUE(BasicBlockSubGraph::IsReachable(rm, bbs_[2]));
313 E : ASSERT_FALSE(bbs_[2]->is_padding());
314 E : ASSERT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs_[2]->type());
315 E : BasicCodeBlock* bb2 = BasicCodeBlock::Cast(bbs_[2]);
316 E : ASSERT_TRUE(bb2 != NULL);
317 E : ASSERT_EQ(2u, bb2->instructions().size());
318 E : ASSERT_EQ(1u, bb2->successors().size());
319 E : ASSERT_EQ(bbs_[3], bb2->successors().front().reference().basic_block());
320 E : ASSERT_EQ(1u, bbs_[2]->alignment());
321 :
322 : // Basic-block 3 - sub eax to jnz.
323 E : ASSERT_TRUE(BasicBlockSubGraph::IsReachable(rm, bbs_[3]));
324 E : ASSERT_FALSE(bbs_[3]->is_padding());
325 E : ASSERT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs_[3]->type());
326 E : BasicCodeBlock* bb3 = BasicCodeBlock::Cast(bbs_[3]);
327 E : ASSERT_TRUE(bb3 != NULL);
328 E : ASSERT_EQ(1u, bb3->instructions().size());
329 E : ASSERT_EQ(2u, bb3->successors().size());
330 E : ASSERT_EQ(bb3, bb3->successors().front().reference().basic_block());
331 E : ASSERT_EQ(bbs_[4], bb3->successors().back().reference().basic_block());
332 E : ASSERT_EQ(1u, bbs_[3]->alignment());
333 :
334 : // Basic-block 4 - ret.
335 E : ASSERT_TRUE(BasicBlockSubGraph::IsReachable(rm, bbs_[4]));
336 E : ASSERT_FALSE(bbs_[4]->is_padding());
337 E : ASSERT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs_[4]->type());
338 E : BasicCodeBlock* bb4 = BasicCodeBlock::Cast(bbs_[4]);
339 E : ASSERT_TRUE(bb4 != NULL);
340 E : ASSERT_EQ(1u, bb4->instructions().size());
341 E : ASSERT_EQ(0u, bb4->successors().size());
342 E : ASSERT_EQ(1u, bbs_[4]->alignment());
343 :
344 : // Basic-block 5 - case_1.
345 E : ASSERT_TRUE(BasicBlockSubGraph::IsReachable(rm, bbs_[5]));
346 E : ASSERT_FALSE(bbs_[5]->is_padding());
347 E : ASSERT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs_[5]->type());
348 E : BasicCodeBlock* bb5 = BasicCodeBlock::Cast(bbs_[5]);
349 E : ASSERT_TRUE(bb5 != NULL);
350 E : ASSERT_EQ(1u, bb5->instructions().size());
351 : ASSERT_EQ(
352 : func1_,
353 E : bb5->instructions().front().references().begin()->second.block());
354 E : ASSERT_EQ(1u, bb5->successors().size());
355 E : ASSERT_EQ(bbs_[6], bb5->successors().front().reference().basic_block());
356 E : ASSERT_EQ(1u, bbs_[5]->alignment());
357 :
358 : // Basic-block 6 - case_default.
359 E : ASSERT_TRUE(BasicBlockSubGraph::IsReachable(rm, bbs_[6]));
360 E : ASSERT_FALSE(bbs_[6]->is_padding());
361 E : ASSERT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs_[6]->type());
362 E : BasicCodeBlock* bb6 = BasicCodeBlock::Cast(bbs_[6]);
363 E : ASSERT_TRUE(bb6 != NULL);
364 E : ASSERT_EQ(2u, bb6->instructions().size());
365 : ASSERT_EQ(
366 : func2_,
367 E : bb6->instructions().back().references().begin()->second.block());
368 E : ASSERT_EQ(0u, bb6->successors().size());
369 E : ASSERT_EQ(1u, bbs_[6]->alignment());
370 :
371 : // Basic-block 7 - interrupt_label.
372 E : ASSERT_FALSE(BasicBlockSubGraph::IsReachable(rm, bbs_[7]));
373 E : ASSERT_TRUE(bbs_[7]->is_padding());
374 E : ASSERT_EQ(BasicBlock::BASIC_CODE_BLOCK, bbs_[7]->type());
375 E : BasicCodeBlock* bb7 = BasicCodeBlock::Cast(bbs_[7]);
376 E : ASSERT_TRUE(bb7 != NULL);
377 E : ASSERT_EQ(3u, bb7->instructions().size());
378 E : ASSERT_EQ(0u, bb7->successors().size());
379 E : ASSERT_EQ(1u, bbs_[7]->alignment());
380 :
381 : // Basic-block 8 - jump_table.
382 E : ASSERT_TRUE(BasicBlockSubGraph::IsReachable(rm, bbs_[8]));
383 E : ASSERT_FALSE(bbs_[8]->is_padding());
384 E : ASSERT_EQ(BasicBlock::BASIC_DATA_BLOCK, bbs_[8]->type());
385 E : BasicDataBlock* bb8 = BasicDataBlock::Cast(bbs_[8]);
386 E : ASSERT_TRUE(bb8 != NULL);
387 E : ASSERT_EQ(3 * Reference::kMaximumSize, bb8->size());
388 E : ASSERT_EQ(3u, bb8->references().size());
389 E : ASSERT_EQ(4u, bbs_[8]->alignment());
390 :
391 : // Basic-block 9 - case_table.
392 E : ASSERT_TRUE(BasicBlockSubGraph::IsReachable(rm, bbs_[9]));
393 E : ASSERT_FALSE(bbs_[9]->is_padding());
394 E : ASSERT_EQ(BasicBlock::BASIC_DATA_BLOCK, bbs_[9]->type());
395 E : BasicDataBlock* bb9 = BasicDataBlock::Cast(bbs_[9]);
396 E : ASSERT_TRUE(bb9 != NULL);
397 E : ASSERT_EQ(256, bb9->size());
398 E : ASSERT_EQ(0u, bb9->references().size());
399 E : ASSERT_EQ(4u, bbs_[9]->alignment());
400 :
401 E : ASSERT_EQ(BasicBlock::BASIC_END_BLOCK, bbs_[10]->type());
402 :
403 : // Validate all source ranges.
404 E : core::RelativeAddress next_addr(start_addr_);
405 E : for (size_t i = 0; i < bbs_.size(); ++i) {
406 E : const BasicCodeBlock* code_block = BasicCodeBlock::Cast(bbs_[i]);
407 E : const BasicDataBlock* data_block = BasicDataBlock::Cast(bbs_[i]);
408 :
409 E : if (code_block != NULL) {
410 E : ASSERT_TRUE(data_block == NULL);
411 :
412 : BasicBlock::Instructions::const_iterator instr_it =
413 E : code_block->instructions().begin();
414 E : for (; instr_it != code_block->instructions().end(); ++instr_it) {
415 E : const Instruction& instr = *instr_it;
416 E : ASSERT_EQ(next_addr, instr.source_range().start());
417 E : ASSERT_EQ(instr.size(), instr.source_range().size());
418 :
419 E : next_addr += instr.size();
420 E : }
421 :
422 : BasicBlock::Successors::const_iterator succ_it =
423 E : code_block->successors().begin();
424 E : for (; succ_it != code_block->successors().end(); ++succ_it) {
425 E : const Successor& succ = *succ_it;
426 E : if (succ.source_range().size() != 0) {
427 E : ASSERT_EQ(next_addr, succ.source_range().start());
428 E : ASSERT_EQ(succ.instruction_size(), succ.source_range().size());
429 E : } else {
430 E : ASSERT_EQ(0, succ.instruction_size());
431 : }
432 :
433 E : next_addr += succ.instruction_size();
434 E : }
435 : }
436 :
437 E : if (data_block != NULL) {
438 E : ASSERT_TRUE(code_block == NULL);
439 E : ASSERT_TRUE(data_block->type() == BasicBlock::BASIC_DATA_BLOCK);
440 E : ASSERT_EQ(next_addr, data_block->source_range().start());
441 E : ASSERT_EQ(data_block->size(), data_block->source_range().size());
442 :
443 E : next_addr += data_block->size();
444 : }
445 E : }
446 E : }
447 :
448 E : TEST_F(BasicBlockDecomposerTest, DecomposeBlockWithLabelPastData) {
449 E : ASSERT_NO_FATAL_FAILURE(InitBasicBlockSubGraphWithLabelPastEnd());
450 E : }
451 :
452 E : TEST_F(BasicBlockDecomposerTest, HasInlineAssembly) {
453 : ASSERT_NO_FATAL_FAILURE(InitBlockGraphFromSerializedFile(
454 E : L"syzygy/block_graph/test_data/has_inline_assembly.bg"));
455 :
456 : BlockGraph::BlockMap::iterator block_it =
457 E : block_graph_.blocks_mutable().begin();
458 E : for (; block_it != block_graph_.blocks().end(); ++block_it) {
459 E : BlockGraph::Block* block = &(block_it->second);
460 :
461 : // We skip the 'master' blocks. These are simply dummy blocks that act as
462 : // sources and destinations for references, to keep the remaining blocks
463 : // intact.
464 E : if (block->name() == "CodeMaster" || block->name() == "DataMaster")
465 E : continue;
466 :
467 E : BasicBlockSubGraph bbsg;
468 E : BasicBlockDecomposer bbd(block, &bbsg);
469 E : ASSERT_TRUE(bbd.Decompose());
470 E : EXPECT_FALSE(bbd.contains_unsupported_instructions());
471 E : EXPECT_EQ(block->size(), GetNetBBSize(bbsg));
472 :
473 : // Validate a block in detail.
474 E : if (block->id() == 5677)
475 E : ASSERT_NO_FATAL_FAILURE(ValidateHasInlineAssemblyBlock5677(bbsg));
476 E : }
477 E : }
478 :
479 E : TEST_F(BasicBlockDecomposerTest, ContainsJECXZ) {
480 E : ASSERT_NO_FATAL_FAILURE(InitBlockGraph());
481 : BlockGraph::Block* jecxz = block_graph_.AddBlock(
482 E : BlockGraph::CODE_BLOCK, 4, "jecxz");
483 E : ASSERT_TRUE(jecxz != NULL);
484 E : jecxz->set_section(text_section_->id());
485 :
486 : // The following bytes are the assembly of the following code:
487 : // JECXZ done
488 : // DEC ecx
489 : // done:
490 : // RET
491 : // The JECXZ instruction has a PC-relative reference at byte 1 to
492 : // byte 3.
493 E : const uint8 kAssembly[] = { 0xE3, 0x01, 0x49, 0xC3 };
494 E : jecxz->CopyData(arraysize(kAssembly), kAssembly);
495 : jecxz->SetReference(1,
496 E : BlockGraph::Reference(BlockGraph::PC_RELATIVE_REF, 1, jecxz, 3, 3));
497 :
498 E : BasicBlockSubGraph bbsg;
499 E : BasicBlockDecomposer bbd(jecxz, &bbsg);
500 E : EXPECT_FALSE(bbd.Decompose());
501 E : EXPECT_TRUE(bbd.contains_unsupported_instructions());
502 E : }
503 :
504 : } // namespace block_graph
|