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