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 : #include "syzygy/pe/decomposer.h"
16 :
17 : #include "base/strings/string_split.h"
18 : #include "base/strings/string_util.h"
19 : #include "gmock/gmock.h"
20 : #include "gtest/gtest.h"
21 : #include "syzygy/block_graph/block_graph_serializer.h"
22 : #include "syzygy/block_graph/typed_block.h"
23 : #include "syzygy/block_graph/unittest_util.h"
24 : #include "syzygy/core/unittest_util.h"
25 : #include "syzygy/pdb/pdb_byte_stream.h"
26 : #include "syzygy/pdb/pdb_file.h"
27 : #include "syzygy/pdb/pdb_reader.h"
28 : #include "syzygy/pdb/pdb_util.h"
29 : #include "syzygy/pe/pe_relinker.h"
30 : #include "syzygy/pe/pe_utils.h"
31 : #include "syzygy/pe/unittest_util.h"
32 :
33 : namespace pe {
34 :
35 : namespace {
36 :
37 : using block_graph::BlockGraph;
38 : using block_graph::ConstTypedBlock;
39 : using core::RelativeAddress;
40 : using testing::ContainerEq;
41 :
42 : const size_t kPointerSize = BlockGraph::Reference::kMaximumSize;
43 :
44 : static const BlockGraph::BlockAttributes kGapOrPadding =
45 : BlockGraph::GAP_BLOCK | BlockGraph::PADDING_BLOCK;
46 :
47 : // Exposes the protected methods for testing.
48 : class TestDecomposer : public Decomposer {
49 : public:
50 : explicit TestDecomposer(const PEFile& image_file)
51 : : Decomposer(image_file) {
52 : }
53 :
54 : // Expose as public for testing.
55 : using Decomposer::LoadBlockGraphFromPdbStream;
56 : using Decomposer::LoadBlockGraphFromPdb;
57 : };
58 :
59 : class DecomposerTest : public testing::PELibUnitTest {
60 : typedef testing::PELibUnitTest Super;
61 :
62 : public:
63 E : void SetUp() {
64 E : Super::SetUp();
65 :
66 E : ASSERT_NO_FATAL_FAILURE(CreateTemporaryDir(&temp_dir_));
67 E : }
68 :
69 : base::FilePath temp_dir_;
70 : };
71 :
72 : } // namespace
73 :
74 E : TEST_F(DecomposerTest, MutatorsAndAccessors) {
75 E : base::FilePath image_path(testing::GetExeRelativePath(testing::kTestDllName));
76 : base::FilePath pdb_path(
77 E : testing::GetExeRelativePath(testing::kTestDllPdbName));
78 :
79 E : PEFile image_file;
80 E : ASSERT_TRUE(image_file.Init(image_path));
81 :
82 E : Decomposer decomposer(image_file);
83 E : EXPECT_TRUE(decomposer.pdb_path().empty());
84 :
85 E : decomposer.set_pdb_path(pdb_path);
86 E : EXPECT_EQ(pdb_path, decomposer.pdb_path());
87 E : }
88 :
89 E : TEST_F(DecomposerTest, Decompose) {
90 E : base::FilePath image_path(testing::GetExeRelativePath(testing::kTestDllName));
91 E : PEFile image_file;
92 :
93 E : ASSERT_TRUE(image_file.Init(image_path));
94 :
95 : // Decompose the test image.
96 E : Decomposer decomposer(image_file);
97 E : EXPECT_TRUE(decomposer.pdb_path().empty());
98 :
99 E : BlockGraph block_graph;
100 E : ImageLayout image_layout(&block_graph);
101 E : EXPECT_TRUE(decomposer.Decompose(&image_layout));
102 E : EXPECT_FALSE(decomposer.pdb_path().empty());
103 :
104 E : EXPECT_EQ(BlockGraph::PE_IMAGE, block_graph.image_format());
105 :
106 : // Retrieve and validate the DOS header.
107 : BlockGraph::Block* dos_header_block =
108 E : image_layout.blocks.GetBlockByAddress(RelativeAddress(0));
109 E : ASSERT_TRUE(dos_header_block != NULL);
110 E : ASSERT_TRUE(IsValidDosHeaderBlock(dos_header_block));
111 :
112 : // Retrieve and validate the NT header.
113 : BlockGraph::Block* nt_headers_block =
114 E : GetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
115 E : ASSERT_TRUE(nt_headers_block != NULL);
116 E : ASSERT_TRUE(IsValidNtHeadersBlock(nt_headers_block));
117 :
118 : // There should be some blocks in the graph and in the layout, and the same
119 : // number in the block-graph and image layout.
120 E : EXPECT_LT(0u, block_graph.blocks().size());
121 E : EXPECT_LT(0u, image_layout.blocks.size());
122 E : EXPECT_EQ(block_graph.blocks().size(), image_layout.blocks.size());
123 :
124 E : EXPECT_EQ(6u, block_graph.sections().size());
125 E : EXPECT_EQ(6u, image_layout.sections.size());
126 :
127 E : EXPECT_EQ(".text", image_layout.sections[0].name);
128 E : EXPECT_NE(0U, image_layout.sections[0].addr.value());
129 E : EXPECT_NE(0U, image_layout.sections[0].size);
130 E : EXPECT_NE(0U, image_layout.sections[0].data_size);
131 : EXPECT_EQ(IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ,
132 E : image_layout.sections[0].characteristics);
133 :
134 E : EXPECT_EQ(".rdata", image_layout.sections[1].name);
135 E : EXPECT_NE(0U, image_layout.sections[1].addr.value());
136 E : EXPECT_NE(0U, image_layout.sections[1].size);
137 E : EXPECT_NE(0U, image_layout.sections[1].data_size);
138 : EXPECT_EQ(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
139 E : image_layout.sections[1].characteristics);
140 :
141 E : EXPECT_EQ(".data", image_layout.sections[2].name);
142 E : EXPECT_NE(0U, image_layout.sections[2].addr.value());
143 E : EXPECT_NE(0U, image_layout.sections[2].size);
144 E : EXPECT_NE(0U, image_layout.sections[2].data_size);
145 : EXPECT_EQ(
146 : IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
147 E : image_layout.sections[2].characteristics);
148 :
149 E : EXPECT_EQ(".tls", image_layout.sections[3].name);
150 E : EXPECT_NE(0U, image_layout.sections[3].addr.value());
151 E : EXPECT_NE(0U, image_layout.sections[3].size);
152 E : EXPECT_NE(0U, image_layout.sections[3].data_size);
153 : EXPECT_EQ(
154 : IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
155 E : image_layout.sections[3].characteristics);
156 :
157 E : EXPECT_EQ(".rsrc", image_layout.sections[4].name);
158 E : EXPECT_NE(0U, image_layout.sections[4].addr.value());
159 E : EXPECT_NE(0U, image_layout.sections[4].size);
160 E : EXPECT_NE(0U, image_layout.sections[4].data_size);
161 : EXPECT_EQ(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
162 E : image_layout.sections[4].characteristics);
163 :
164 E : EXPECT_EQ(".reloc", image_layout.sections[5].name);
165 E : EXPECT_NE(0U, image_layout.sections[5].addr.value());
166 E : EXPECT_NE(0U, image_layout.sections[5].size);
167 E : EXPECT_NE(0U, image_layout.sections[5].data_size);
168 : EXPECT_EQ(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE |
169 E : IMAGE_SCN_MEM_READ, image_layout.sections[5].characteristics);
170 :
171 : // We expect the ImageLayout sections to agree with the BlockGraph sections
172 : // in number, id, name and characteristics.
173 E : EXPECT_EQ(block_graph.sections().size(), image_layout.sections.size());
174 E : for (size_t i = 0; i < image_layout.sections.size(); ++i) {
175 : const BlockGraph::Section* section =
176 E : block_graph.GetSectionById(i);
177 E : ASSERT_TRUE(section != NULL);
178 E : EXPECT_EQ(section->id(), i);
179 E : EXPECT_EQ(section->name(), image_layout.sections[i].name);
180 : EXPECT_EQ(section->characteristics(),
181 E : image_layout.sections[i].characteristics);
182 E : }
183 :
184 : typedef std::map<BlockGraph::SectionId, size_t> SectionCountMap;
185 : typedef std::map<BlockGraph::BlockType, size_t> BlockTypeCountMap;
186 :
187 : // We expect every block to be associated with a section, and only two blocks
188 : // should not be assigned to a section--the two header blocks. Similarly, set
189 : // expectations on the number of blocks per section, and the number of blocks
190 : // by type.
191 E : SectionCountMap section_counts;
192 E : BlockTypeCountMap block_type_counts;
193 : BlockGraph::BlockMap::const_iterator it =
194 E : block_graph.blocks().begin();
195 E : for (; it != block_graph.blocks().end(); ++it) {
196 E : const BlockGraph::Block& block = it->second;
197 :
198 : // We can't count gap or padding blocks, as more or less of these can appear
199 : // based on the directory in which the user performed the build.
200 E : if (block.attributes() & kGapOrPadding)
201 E : continue;
202 :
203 E : ++section_counts[block.section()];
204 E : ++block_type_counts[block.type()];
205 E : }
206 :
207 E : SectionCountMap expected_section_counts;
208 : #ifndef NDEBUG
209 : // Debug build.
210 E : expected_section_counts[-1] = 2;
211 E : expected_section_counts[0] = 323;
212 E : expected_section_counts[1] = 747;
213 E : expected_section_counts[2] = 91;
214 E : expected_section_counts[3] = 1;
215 E : expected_section_counts[4] = 1;
216 E : expected_section_counts[5] = 1;
217 : #else
218 : #ifndef OFFICIAL_BUILD
219 : // Release build.
220 : expected_section_counts[-1] = 2;
221 : expected_section_counts[0] = 298;
222 : expected_section_counts[1] = 724;
223 : expected_section_counts[2] = 85;
224 : expected_section_counts[3] = 1;
225 : expected_section_counts[4] = 1;
226 : expected_section_counts[5] = 1;
227 : #else
228 : // Official build.
229 : expected_section_counts[-1] = 2;
230 : expected_section_counts[0] = 297;
231 : expected_section_counts[1] = 724;
232 : expected_section_counts[2] = 85;
233 : expected_section_counts[3] = 1;
234 : expected_section_counts[4] = 1;
235 : expected_section_counts[5] = 1;
236 : #endif
237 : #endif
238 E : EXPECT_THAT(section_counts, ContainerEq(expected_section_counts));
239 :
240 E : BlockTypeCountMap expected_block_type_counts;
241 : #ifndef NDEBUG
242 : // Debug build.
243 E : expected_block_type_counts[BlockGraph::CODE_BLOCK] = 323;
244 E : expected_block_type_counts[BlockGraph::DATA_BLOCK] = 843;
245 : #else
246 : #ifndef OFFICIAL_BUILD
247 : // Release build.
248 : expected_block_type_counts[BlockGraph::CODE_BLOCK] = 298;
249 : expected_block_type_counts[BlockGraph::DATA_BLOCK] = 814;
250 : #else
251 : // Official build.
252 : expected_block_type_counts[BlockGraph::CODE_BLOCK] = 297;
253 : expected_block_type_counts[BlockGraph::DATA_BLOCK] = 814;
254 : #endif
255 : #endif
256 E : EXPECT_THAT(block_type_counts, ContainerEq(expected_block_type_counts));
257 :
258 : // Every byte of each section must be accounted for by all of the non-header
259 : // blocks.
260 E : size_t section_blocks = 0;
261 E : for (size_t i = 0; i < image_layout.sections.size(); ++i) {
262 : BlockGraph::AddressSpace::RangeMapConstIterPair it_pair =
263 : image_layout.blocks.GetIntersectingBlocks(
264 : image_layout.sections[i].addr,
265 E : image_layout.sections[i].size);
266 :
267 : // Make sure the first iterator is dereferenceable.
268 E : ASSERT_TRUE(it_pair.first != image_layout.blocks.end());
269 :
270 : // The first and last iterators should not be the same.
271 E : EXPECT_TRUE(it_pair.first != it_pair.second);
272 :
273 : // The first iterator should start at the beginning of the section.
274 E : EXPECT_EQ(image_layout.sections[i].addr, it_pair.first->first.start());
275 :
276 : // Ensure the blocks are contiguous, and count the number of blocks as we
277 : // go.
278 E : BlockGraph::AddressSpace::RangeMapConstIter it_old = it_pair.first;
279 E : BlockGraph::AddressSpace::RangeMapConstIter it_cur = it_pair.first;
280 E : ++it_cur;
281 E : ++section_blocks;
282 E : while (it_cur != it_pair.second) {
283 E : EXPECT_EQ(it_old->first.end(), it_cur->first.start());
284 E : it_old = it_cur;
285 E : ++it_cur;
286 E : ++section_blocks;
287 E : }
288 :
289 : // Make sure the last block perfectly covers the section.
290 : EXPECT_EQ(image_layout.sections[i].addr + image_layout.sections[i].size,
291 E : it_old->first.end());
292 E : }
293 :
294 : // All of the blocks should have been covered, save the 2 header blocks.
295 E : EXPECT_EQ(section_blocks + 2, image_layout.blocks.size());
296 :
297 : // Make sure that all bracketed COFF groups have been parsed. There are 8
298 : // of them that we currently know of:
299 : // .CRT$XCA -> .CRT$XCZ: C initializers
300 : // .CRT$XIA -> .CRT$XLZ: C++ initializers
301 : // .CRT$XLA -> .CRT$XLZ: TLS callbacks
302 : // .CRT$XPA -> .CRT$XPZ: CRT pre-termination functions.
303 : // .CRT$XTA -> .CRT$XTZ: CRT termination functions.
304 : // .rtc$IAA -> .rtc$IZZ: Run-time checking initializers.
305 : // .rtc$TAA -> .rtc$TZZ: Run-time checking termination functions.
306 : // .tls -> .tls$ZZZ: TLS data.
307 E : size_t coff_group_blocks = 0;
308 E : it = block_graph.blocks().begin();
309 E : for (; it != block_graph.blocks().end(); ++it) {
310 E : const BlockGraph::Block& block = it->second;
311 E : if (block.attributes() & BlockGraph::COFF_GROUP)
312 E : ++coff_group_blocks;
313 E : }
314 E : EXPECT_EQ(8u, coff_group_blocks);
315 E : }
316 :
317 E : TEST_F(DecomposerTest, DecomposeFailsWithNonexistentPdb) {
318 E : base::FilePath image_path(testing::GetExeRelativePath(testing::kTestDllName));
319 E : PEFile image_file;
320 :
321 E : ASSERT_TRUE(image_file.Init(image_path));
322 :
323 E : Decomposer decomposer(image_file);
324 E : decomposer.set_pdb_path(testing::GetExeRelativePath(L"nonexistent.pdb"));
325 :
326 E : BlockGraph block_graph;
327 E : ImageLayout image_layout(&block_graph);
328 E : EXPECT_FALSE(decomposer.Decompose(&image_layout));
329 E : }
330 :
331 E : TEST_F(DecomposerTest, LabelsAndAttributes) {
332 E : base::FilePath image_path(testing::GetExeRelativePath(testing::kTestDllName));
333 E : PEFile image_file;
334 :
335 E : ASSERT_TRUE(image_file.Init(image_path));
336 :
337 : // Decompose the test image and look at the result.
338 E : Decomposer decomposer(image_file);
339 E : BlockGraph block_graph;
340 E : ImageLayout image_layout(&block_graph);
341 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
342 :
343 : // Locate various specific function blocks in the block-graph.
344 E : const BlockGraph::Block* dll_main_block = NULL;
345 E : const BlockGraph::Block* func_with_inl_asm_block = NULL;
346 E : const BlockGraph::Block* strchr_block = NULL;
347 E : const BlockGraph::Block* imp_load_block = NULL;
348 E : const BlockGraph::Block* no_private_symbols_block = NULL;
349 E : const BlockGraph::Block* test_string_block = NULL;
350 :
351 : typedef std::map<BlockGraph::BlockAttributeEnum, size_t> AttribCountMap;
352 E : AttribCountMap attrib_counts;
353 : {
354 : typedef std::map<std::string, const BlockGraph::Block**> TestBlockMap;
355 :
356 E : TestBlockMap test_blocks;
357 E : test_blocks.insert(std::make_pair("DllMain", &dll_main_block));
358 : test_blocks.insert(std::make_pair("FunctionWithInlineAssembly",
359 E : &func_with_inl_asm_block));
360 E : test_blocks.insert(std::make_pair("strchr", &strchr_block));
361 : test_blocks.insert(std::make_pair("__imp_load_CoCreateGuid",
362 E : &imp_load_block));
363 : test_blocks.insert(std::make_pair("TestFunctionWithNoPrivateSymbols",
364 E : &no_private_symbols_block));
365 : test_blocks.insert(std::make_pair("kTestString",
366 E : &test_string_block));
367 :
368 E : BlockGraph::BlockMap::const_iterator it = block_graph.blocks().begin();
369 E : for (; it != block_graph.blocks().end(); ++it) {
370 E : const BlockGraph::Block& block = it->second;
371 :
372 : // Count the attributes across the entire block-graph.
373 E : for (size_t i = 0; i < BlockGraph::BLOCK_ATTRIBUTES_MAX_BIT; ++i) {
374 : BlockGraph::BlockAttributeEnum attr =
375 E : static_cast<BlockGraph::BlockAttributeEnum>(1 << i);
376 :
377 : // We don't count gap or padding blocks as they vary from machine to
378 : // machine depending on lengths of certain strings.
379 E : if (attr & kGapOrPadding)
380 E : continue;
381 :
382 E : if (block.attributes() & attr)
383 E : ++attrib_counts[attr];
384 E : }
385 :
386 E : TestBlockMap::const_iterator test_it = test_blocks.find(block.name());
387 E : if (test_it == test_blocks.end())
388 E : continue;
389 :
390 E : ASSERT_TRUE(*test_it->second == NULL);
391 E : *test_it->second = █
392 E : }
393 E : }
394 :
395 E : ASSERT_TRUE(dll_main_block != NULL);
396 E : ASSERT_TRUE(func_with_inl_asm_block != NULL);
397 E : ASSERT_TRUE(strchr_block != NULL);
398 E : ASSERT_TRUE(imp_load_block != NULL);
399 E : ASSERT_TRUE(no_private_symbols_block != NULL);
400 :
401 : // Check the attribute counts.
402 E : AttribCountMap expected_attrib_counts;
403 :
404 : #ifndef NDEBUG
405 : // Debug build.
406 E : expected_attrib_counts[BlockGraph::NON_RETURN_FUNCTION] = 10;
407 E : expected_attrib_counts[BlockGraph::PE_PARSED] = 89;
408 E : expected_attrib_counts[BlockGraph::SECTION_CONTRIB] = 1162;
409 E : expected_attrib_counts[BlockGraph::HAS_INLINE_ASSEMBLY] = 15;
410 E : expected_attrib_counts[BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER] = 136;
411 E : expected_attrib_counts[BlockGraph::HAS_EXCEPTION_HANDLING] = 26;
412 E : expected_attrib_counts[BlockGraph::THUNK] = 7;
413 E : expected_attrib_counts[BlockGraph::COFF_GROUP] = 8;
414 : #else
415 : #ifndef OFFICIAL_BUILD
416 : // Release build.
417 : expected_attrib_counts[BlockGraph::NON_RETURN_FUNCTION] = 8;
418 : expected_attrib_counts[BlockGraph::PE_PARSED] = 88;
419 : expected_attrib_counts[BlockGraph::SECTION_CONTRIB] = 1108;
420 : expected_attrib_counts[BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER] = 135;
421 : expected_attrib_counts[BlockGraph::HAS_INLINE_ASSEMBLY] = 13;
422 : expected_attrib_counts[BlockGraph::HAS_EXCEPTION_HANDLING] = 24;
423 : expected_attrib_counts[BlockGraph::THUNK] = 7;
424 : expected_attrib_counts[BlockGraph::COFF_GROUP] = 8;
425 : #else
426 : // Official build.
427 : expected_attrib_counts[BlockGraph::NON_RETURN_FUNCTION] = 8;
428 : expected_attrib_counts[BlockGraph::PE_PARSED] = 88;
429 : expected_attrib_counts[BlockGraph::SECTION_CONTRIB] = 1107;
430 : expected_attrib_counts[BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER] = 136;
431 : expected_attrib_counts[BlockGraph::HAS_INLINE_ASSEMBLY] = 13;
432 : expected_attrib_counts[BlockGraph::HAS_EXCEPTION_HANDLING] = 24;
433 : expected_attrib_counts[BlockGraph::THUNK] = 7;
434 : expected_attrib_counts[BlockGraph::COFF_GROUP] = 8;
435 : #endif
436 : #endif
437 E : EXPECT_THAT(attrib_counts, ContainerEq(expected_attrib_counts));
438 :
439 : // The kTestString block should have alignment >= 64.
440 E : ASSERT_FALSE(test_string_block == NULL);
441 E : EXPECT_LE(64u, test_string_block->alignment());
442 :
443 : // The block with no private symbols should only have a single public symbol
444 : // label.
445 E : ASSERT_FALSE(no_private_symbols_block == NULL);
446 E : EXPECT_EQ(1u, no_private_symbols_block->labels().size());
447 : BlockGraph::Block::LabelMap::const_iterator label_it =
448 E : no_private_symbols_block->labels().begin();
449 E : EXPECT_EQ(0, label_it->first);
450 E : EXPECT_EQ(BlockGraph::PUBLIC_SYMBOL_LABEL, label_it->second.attributes());
451 :
452 : // The imp_load block should be a thunk.
453 E : ASSERT_NE(0UL, imp_load_block->attributes() & BlockGraph::THUNK);
454 :
455 : // DllMain has a jump table so it should have pointer alignment.
456 E : ASSERT_EQ(kPointerSize, dll_main_block->alignment());
457 :
458 : // Validate that the FunctionWithInlineAssembly block has the appropriate
459 : // attributes.
460 : ASSERT_TRUE(func_with_inl_asm_block->attributes() &
461 E : BlockGraph::HAS_INLINE_ASSEMBLY);
462 :
463 : // Validate that the strchr block has the appropriate attributes.
464 : ASSERT_TRUE(strchr_block->attributes() &
465 E : BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER);
466 :
467 : #ifdef OFFICIAL_BUILD
468 : static const size_t kDllMainLabelCount = 44;
469 : static const size_t kCallSiteLabelCount = 26;
470 : #else
471 : #ifndef NDEBUG
472 : // Debug build.
473 : static const size_t kDllMainLabelCount = 32;
474 : static const size_t kCallSiteLabelCount = 10;
475 : #else
476 : // Release build.
477 : static const size_t kDllMainLabelCount = 33;
478 : static const size_t kCallSiteLabelCount = 10;
479 : #endif
480 : #endif
481 :
482 : // Validate compiland names.
483 : // MSVS produces files named test_dll.obj and Ninja build produces files
484 : // named test_dll.test_dll.obj.
485 : EXPECT_TRUE(EndsWith(dll_main_block->compiland_name(),
486 : "test_dll.obj",
487 E : true));
488 : EXPECT_TRUE(EndsWith(func_with_inl_asm_block->compiland_name(),
489 : "test_dll.obj",
490 E : true));
491 : EXPECT_TRUE(EndsWith(strchr_block->compiland_name(),
492 : "strchr.obj",
493 E : true));
494 :
495 : // Validate that the DllMain block has the expected population of labels.
496 E : EXPECT_EQ(kDllMainLabelCount, dll_main_block->labels().size());
497 :
498 E : std::map<BlockGraph::LabelAttributes, size_t> label_attr_counts;
499 : {
500 : BlockGraph::Block::LabelMap::const_iterator it =
501 E : dll_main_block->labels().begin();
502 E : for (; it != dll_main_block->labels().end(); ++it) {
503 E : BlockGraph::LabelAttributes attr_mask = 1;
504 E : for (; attr_mask != BlockGraph::LABEL_ATTRIBUTES_MAX; attr_mask <<= 1) {
505 E : if (it->second.has_attributes(attr_mask))
506 E : label_attr_counts[attr_mask]++;
507 E : }
508 E : }
509 : }
510 :
511 E : EXPECT_EQ(19, label_attr_counts[BlockGraph::CODE_LABEL]);
512 : EXPECT_EQ(kCallSiteLabelCount,
513 E : label_attr_counts[BlockGraph::CALL_SITE_LABEL]);
514 E : EXPECT_EQ(5, label_attr_counts[BlockGraph::DATA_LABEL]);
515 E : EXPECT_EQ(3, label_attr_counts[BlockGraph::JUMP_TABLE_LABEL]);
516 E : EXPECT_EQ(2, label_attr_counts[BlockGraph::CASE_TABLE_LABEL]);
517 E : EXPECT_EQ(1, label_attr_counts[BlockGraph::DEBUG_START_LABEL]);
518 E : EXPECT_EQ(1, label_attr_counts[BlockGraph::DEBUG_END_LABEL]);
519 E : }
520 :
521 E : TEST_F(DecomposerTest, DecomposeTestDllMSVS2010) {
522 : base::FilePath dll_path = testing::GetSrcRelativePath(
523 E : L"syzygy\\pe\\test_data\\test_dll_vs2010.dll");
524 : base::FilePath pdb_path = testing::GetSrcRelativePath(
525 E : L"syzygy\\pe\\test_data\\test_dll_vs2010.dll.pdb");
526 :
527 E : PEFile pe_file;
528 E : ASSERT_TRUE(pe_file.Init(dll_path));
529 :
530 E : Decomposer decomposer(pe_file);
531 E : BlockGraph block_graph;
532 E : ImageLayout image_layout(&block_graph);
533 E : decomposer.set_pdb_path(pdb_path);
534 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
535 E : }
536 :
537 E : TEST_F(DecomposerTest, DecomposeTestDllMSVS2013) {
538 : base::FilePath dll_path = testing::GetSrcRelativePath(
539 E : L"syzygy\\pe\\test_data\\test_dll_vs2013.dll");
540 : base::FilePath pdb_path = testing::GetSrcRelativePath(
541 E : L"syzygy\\pe\\test_data\\test_dll_vs2013.dll.pdb");
542 :
543 E : PEFile pe_file;
544 E : ASSERT_TRUE(pe_file.Init(dll_path));
545 :
546 E : Decomposer decomposer(pe_file);
547 E : BlockGraph block_graph;
548 E : ImageLayout image_layout(&block_graph);
549 E : decomposer.set_pdb_path(pdb_path);
550 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
551 E : }
552 :
553 E : TEST_F(DecomposerTest, DecomposeSyzyAsanRtlDllWithPGO) {
554 : base::FilePath dll_path = testing::GetSrcRelativePath(
555 E : L"syzygy\\pe\\test_data\\syzyasan_rtl.dll");
556 : base::FilePath pdb_path = testing::GetSrcRelativePath(
557 E : L"syzygy\\pe\\test_data\\syzyasan_rtl.dll.pdb");
558 :
559 E : PEFile pe_file;
560 E : ASSERT_TRUE(pe_file.Init(dll_path));
561 :
562 E : Decomposer decomposer(pe_file);
563 E : BlockGraph block_graph;
564 E : ImageLayout image_layout(&block_graph);
565 E : decomposer.set_pdb_path(pdb_path);
566 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
567 E : }
568 :
569 : namespace {
570 :
571 : void GetNtHeadersBlock(const BlockGraph::Block* dos_header_block,
572 E : BlockGraph::Block** out_nt_headers_block) {
573 E : DCHECK(out_nt_headers_block != NULL);
574 :
575 E : ConstTypedBlock<IMAGE_DOS_HEADER> dos_header;
576 E : ASSERT_TRUE(dos_header.Init(0, dos_header_block));
577 E : ConstTypedBlock<IMAGE_NT_HEADERS> nt_headers;
578 E : ASSERT_TRUE(dos_header.Dereference(dos_header->e_lfanew, &nt_headers));
579 E : ASSERT_TRUE(nt_headers.block() != NULL);
580 E : *out_nt_headers_block = const_cast<BlockGraph::Block*>(nt_headers.block());
581 E : }
582 :
583 : // This test fixture class contains all the tests that need files generated by
584 : // the relinker (the new image and its corresponding PDB).
585 : class DecomposerAfterRelinkTest : public DecomposerTest {
586 : public:
587 : typedef DecomposerTest Super;
588 :
589 E : DecomposerAfterRelinkTest() : relinker_(&policy_) { }
590 :
591 E : virtual void SetUp() OVERRIDE {
592 E : Super::SetUp();
593 E : }
594 :
595 E : void Relink(bool compress_pdb) {
596 : // Initialize a relinker and generate a pdb that contains a block-graph
597 : // stream.
598 E : relinked_dll_ = temp_dir_.Append(testing::kTestDllName);
599 E : relinked_pdb_ = temp_dir_.Append(testing::kTestDllPdbName);
600 :
601 : relinker_.set_input_path(testing::GetExeRelativePath(
602 E : testing::kTestDllName));
603 : relinker_.set_input_pdb_path(testing::GetExeRelativePath(
604 E : testing::kTestDllPdbName));
605 E : relinker_.set_allow_overwrite(true);
606 E : relinker_.set_augment_pdb(true);
607 E : relinker_.set_compress_pdb(compress_pdb);
608 E : relinker_.set_output_path(relinked_dll_);
609 E : relinker_.set_output_pdb_path(relinked_pdb_);
610 E : ASSERT_TRUE(relinker_.Init());
611 E : ASSERT_TRUE(relinker_.Relink());
612 E : }
613 :
614 : // Given a decomposed image this checks its NT headers against those
615 : // contained in the transformed image stored in the relinker.
616 E : void ReconcileNtHeaders(ImageLayout* image_layout) {
617 E : DCHECK(image_layout != NULL);
618 :
619 : // Get the NT headers block associated with the in-memory representation of
620 : // the relinked image.
621 E : BlockGraph::Block* nt1 = NULL;
622 E : ASSERT_NO_FATAL_FAILURE(GetNtHeadersBlock(relinker_.headers_block(), &nt1));
623 E : ASSERT_TRUE(nt1 != NULL);
624 :
625 : // Get the NT headers block associated with the decomposition just performed
626 : // on the relinked image.
627 : BlockGraph::Block* dos_header_block =
628 E : image_layout->blocks.GetBlockByAddress(core::RelativeAddress(0));
629 E : ASSERT_TRUE(dos_header_block != NULL);
630 E : BlockGraph::Block* nt2 = NULL;
631 E : ASSERT_NO_FATAL_FAILURE(GetNtHeadersBlock(dos_header_block, &nt2));
632 E : ASSERT_TRUE(nt2 != NULL);
633 :
634 : // The NT headers don't compare equal because things like the timestamp and
635 : // checksum are filled out post-transform. We copy the old NT headers into
636 : // the new image so that we can do a simple comparison afterwards.
637 E : ASSERT_EQ(nt1->data_size(), nt2->data_size());
638 E : nt1->SetData(nt2->data(), nt2->data_size());
639 E : }
640 :
641 : // Used to ensure that round-trip decomposition works (where the image is not
642 : // decomposed, but rather deserialized from the PDB).
643 E : void LoadRedecompositionData(bool compressed) {
644 E : ASSERT_NO_FATAL_FAILURE(Relink(compressed));
645 :
646 E : PEFile image_file;
647 E : ASSERT_TRUE(image_file.Init(relinked_dll_));
648 :
649 : // Decompose the test image and look at the result.
650 E : Decomposer decomposer(image_file);
651 E : BlockGraph block_graph;
652 E : ImageLayout image_layout(&block_graph);
653 :
654 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
655 :
656 : // Certain data is written to the NT headers post-transform (checksum), so
657 : // it's not reflected in the relinker's block-graph. We reconcile the
658 : // headers prior to doing the comparison.
659 E : ASSERT_NO_FATAL_FAILURE(ReconcileNtHeaders(&image_layout));
660 :
661 : // Ensure that the post-relink block-graph and the deserialized one from the
662 : // PDB are the same.
663 E : block_graph::BlockGraphSerializer bgs;
664 E : ASSERT_TRUE(::testing::BlockGraphsEqual(relinker_.block_graph(),
665 : block_graph,
666 : bgs));
667 E : }
668 :
669 : PETransformPolicy policy_;
670 : PERelinker relinker_;
671 : base::FilePath relinked_dll_;
672 : base::FilePath relinked_pdb_;
673 : };
674 :
675 : } // namespace
676 :
677 E : TEST_F(DecomposerAfterRelinkTest, LoadRedecompositionDataUncompressed) {
678 E : ASSERT_NO_FATAL_FAILURE(LoadRedecompositionData(false));
679 E : }
680 :
681 E : TEST_F(DecomposerAfterRelinkTest, LoadRedecompositionDataCompressed) {
682 E : ASSERT_NO_FATAL_FAILURE(LoadRedecompositionData(true));
683 E : }
684 :
685 E : TEST_F(DecomposerAfterRelinkTest, FailToLoadBlockGraphWithInvalidVersion) {
686 E : ASSERT_NO_FATAL_FAILURE(Relink(true));
687 :
688 : // Get the block-graph stream from the PDB and change the version of it.
689 :
690 : // Get the stream.
691 E : pdb::PdbFile pdb_file;
692 E : pdb::PdbReader pdb_reader;
693 E : pdb_reader.Read(relinked_pdb_, &pdb_file);
694 E : scoped_refptr<pdb::PdbStream> block_graph_stream;
695 : EXPECT_TRUE(pdb::LoadNamedStreamFromPdbFile(pdb::kSyzygyBlockGraphStreamName,
696 : &pdb_file,
697 E : &block_graph_stream));
698 :
699 : // Create a copy of the stream. We need to do this to have a stream that we
700 : // can modify.
701 E : scoped_refptr<pdb::PdbByteStream> new_stream = new pdb::PdbByteStream();
702 E : ASSERT_TRUE(new_stream->Init(block_graph_stream.get()));
703 E : block_graph_stream = new_stream.get();
704 : scoped_refptr<pdb::WritablePdbStream> block_graph_writer =
705 E : block_graph_stream->GetWritablePdbStream();
706 E : ASSERT_TRUE(block_graph_writer.get() != NULL);
707 :
708 : // Change the version of the stream.
709 E : block_graph_writer->set_pos(0);
710 E : block_graph_writer->Write(pdb::kSyzygyBlockGraphStreamVersion + 1);
711 :
712 E : BlockGraph block_graph;
713 E : ImageLayout image_layout(&block_graph);
714 :
715 : // We've invalided the version previously so this test should fail.
716 E : PEFile image_file;
717 E : ASSERT_TRUE(image_file.Init(relinked_dll_));
718 : ASSERT_FALSE(TestDecomposer::LoadBlockGraphFromPdbStream(
719 E : image_file, block_graph_stream.get(), &image_layout));
720 E : }
721 :
722 : } // namespace pe
|