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