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/new_decomposer.h"
16 :
17 : #include "base/strings/string_split.h"
18 : #include "gtest/gtest.h"
19 : #include "syzygy/block_graph/block_graph_serializer.h"
20 : #include "syzygy/block_graph/typed_block.h"
21 : #include "syzygy/block_graph/unittest_util.h"
22 : #include "syzygy/core/unittest_util.h"
23 : #include "syzygy/pdb/pdb_byte_stream.h"
24 : #include "syzygy/pdb/pdb_file.h"
25 : #include "syzygy/pdb/pdb_reader.h"
26 : #include "syzygy/pdb/pdb_util.h"
27 : #include "syzygy/pe/pe_relinker.h"
28 : #include "syzygy/pe/pe_utils.h"
29 : #include "syzygy/pe/unittest_util.h"
30 :
31 : namespace pe {
32 :
33 : namespace {
34 :
35 : using block_graph::BlockGraph;
36 : using block_graph::ConstTypedBlock;
37 : using core::RelativeAddress;
38 :
39 : const size_t kPointerSize = BlockGraph::Reference::kMaximumSize;
40 :
41 : // Exposes the protected methods for testing.
42 : class TestNewDecomposer : public NewDecomposer {
43 : public:
44 : explicit TestNewDecomposer(const PEFile& image_file)
45 : : NewDecomposer(image_file) {
46 : }
47 :
48 : // Expose as public for testing.
49 : using NewDecomposer::LoadBlockGraphFromPdbStream;
50 : using NewDecomposer::LoadBlockGraphFromPdb;
51 : };
52 :
53 : class NewDecomposerTest : public testing::PELibUnitTest {
54 : typedef testing::PELibUnitTest Super;
55 :
56 : public:
57 E : void SetUp() {
58 E : Super::SetUp();
59 :
60 E : ASSERT_NO_FATAL_FAILURE(CreateTemporaryDir(&temp_dir_));
61 E : }
62 :
63 : base::FilePath temp_dir_;
64 : };
65 :
66 : } // namespace
67 :
68 E : TEST_F(NewDecomposerTest, MutatorsAndAccessors) {
69 E : base::FilePath image_path(testing::GetExeRelativePath(testing::kTestDllName));
70 : base::FilePath pdb_path(
71 E : testing::GetExeRelativePath(testing::kTestDllPdbName));
72 :
73 E : PEFile image_file;
74 E : ASSERT_TRUE(image_file.Init(image_path));
75 :
76 E : NewDecomposer decomposer(image_file);
77 E : EXPECT_TRUE(decomposer.pdb_path().empty());
78 E : EXPECT_TRUE(decomposer.parse_debug_info());
79 :
80 E : decomposer.set_pdb_path(pdb_path);
81 E : EXPECT_EQ(pdb_path, decomposer.pdb_path());
82 :
83 E : decomposer.set_parse_debug_info(false);
84 E : EXPECT_FALSE(decomposer.parse_debug_info());
85 E : }
86 :
87 E : TEST_F(NewDecomposerTest, 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 : NewDecomposer 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 : // Retrieve and validate the DOS header.
103 : BlockGraph::Block* dos_header_block =
104 E : image_layout.blocks.GetBlockByAddress(RelativeAddress(0));
105 E : ASSERT_TRUE(dos_header_block != NULL);
106 E : ASSERT_TRUE(IsValidDosHeaderBlock(dos_header_block));
107 :
108 : // Retrieve and validate the NT header.
109 : BlockGraph::Block* nt_headers_block =
110 E : GetNtHeadersBlockFromDosHeaderBlock(dos_header_block);
111 E : ASSERT_TRUE(nt_headers_block != NULL);
112 E : ASSERT_TRUE(IsValidNtHeadersBlock(nt_headers_block));
113 :
114 : // There should be some blocks in the graph and in the layout, and the same
115 : // number in the block-graph and image layout.
116 E : EXPECT_LT(0u, block_graph.blocks().size());
117 E : EXPECT_LT(0u, image_layout.blocks.size());
118 E : EXPECT_EQ(block_graph.blocks().size(), image_layout.blocks.size());
119 :
120 E : EXPECT_EQ(6u, block_graph.sections().size());
121 E : EXPECT_EQ(6u, image_layout.sections.size());
122 :
123 E : EXPECT_EQ(".text", image_layout.sections[0].name);
124 E : EXPECT_NE(0U, image_layout.sections[0].addr.value());
125 E : EXPECT_NE(0U, image_layout.sections[0].size);
126 E : EXPECT_NE(0U, image_layout.sections[0].data_size);
127 : EXPECT_EQ(IMAGE_SCN_CNT_CODE | IMAGE_SCN_MEM_EXECUTE | IMAGE_SCN_MEM_READ,
128 E : image_layout.sections[0].characteristics);
129 :
130 E : EXPECT_EQ(".rdata", image_layout.sections[1].name);
131 E : EXPECT_NE(0U, image_layout.sections[1].addr.value());
132 E : EXPECT_NE(0U, image_layout.sections[1].size);
133 E : EXPECT_NE(0U, image_layout.sections[1].data_size);
134 : EXPECT_EQ(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
135 E : image_layout.sections[1].characteristics);
136 :
137 E : EXPECT_EQ(".data", image_layout.sections[2].name);
138 E : EXPECT_NE(0U, image_layout.sections[2].addr.value());
139 E : EXPECT_NE(0U, image_layout.sections[2].size);
140 E : EXPECT_NE(0U, image_layout.sections[2].data_size);
141 : EXPECT_EQ(
142 : IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
143 E : image_layout.sections[2].characteristics);
144 :
145 E : EXPECT_EQ(".tls", image_layout.sections[3].name);
146 E : EXPECT_NE(0U, image_layout.sections[3].addr.value());
147 E : EXPECT_NE(0U, image_layout.sections[3].size);
148 E : EXPECT_NE(0U, image_layout.sections[3].data_size);
149 : EXPECT_EQ(
150 : IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE,
151 E : image_layout.sections[3].characteristics);
152 :
153 E : EXPECT_EQ(".rsrc", image_layout.sections[4].name);
154 E : EXPECT_NE(0U, image_layout.sections[4].addr.value());
155 E : EXPECT_NE(0U, image_layout.sections[4].size);
156 E : EXPECT_NE(0U, image_layout.sections[4].data_size);
157 : EXPECT_EQ(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_READ,
158 E : image_layout.sections[4].characteristics);
159 :
160 E : EXPECT_EQ(".reloc", image_layout.sections[5].name);
161 E : EXPECT_NE(0U, image_layout.sections[5].addr.value());
162 E : EXPECT_NE(0U, image_layout.sections[5].size);
163 E : EXPECT_NE(0U, image_layout.sections[5].data_size);
164 : EXPECT_EQ(IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_MEM_DISCARDABLE |
165 E : IMAGE_SCN_MEM_READ, image_layout.sections[5].characteristics);
166 :
167 : // We expect the ImageLayout sections to agree with the BlockGraph sections
168 : // in number, id, name and characteristics.
169 E : EXPECT_EQ(block_graph.sections().size(), image_layout.sections.size());
170 E : for (size_t i = 0; i < image_layout.sections.size(); ++i) {
171 : const BlockGraph::Section* section =
172 E : block_graph.GetSectionById(i);
173 E : ASSERT_TRUE(section != NULL);
174 E : EXPECT_EQ(section->id(), i);
175 E : EXPECT_EQ(section->name(), image_layout.sections[i].name);
176 : EXPECT_EQ(section->characteristics(),
177 E : image_layout.sections[i].characteristics);
178 E : }
179 :
180 : // We expect every block to be associated with a section, and only two blocks
181 : // should not be assigned to a section--the two header blocks.
182 E : size_t non_section_blocks = 0;
183 : BlockGraph::BlockMap::const_iterator it =
184 E : block_graph.blocks().begin();
185 E : for (; it != block_graph.blocks().end(); ++it) {
186 E : const BlockGraph::Block& block = it->second;
187 E : if (block.section() == BlockGraph::kInvalidSectionId) {
188 E : ++non_section_blocks;
189 E : } else {
190 : // If this is not a header block, it should refer to a valid section id.
191 E : EXPECT_LE(0u, block.section());
192 E : EXPECT_LT(block.section(), block_graph.sections().size());
193 : }
194 E : }
195 E : EXPECT_EQ(2u, non_section_blocks);
196 :
197 : // Every byte of each section must be accounted for by all of the non-header
198 : // blocks.
199 E : size_t section_blocks = 0;
200 E : for (size_t i = 0; i < image_layout.sections.size(); ++i) {
201 : BlockGraph::AddressSpace::RangeMapConstIterPair it_pair =
202 : image_layout.blocks.GetIntersectingBlocks(
203 : image_layout.sections[i].addr,
204 E : image_layout.sections[i].size);
205 :
206 : // Make sure the first iterator is dereferenceable.
207 E : ASSERT_TRUE(it_pair.first != image_layout.blocks.end());
208 :
209 : // The first and last iterators should not be the same.
210 E : EXPECT_TRUE(it_pair.first != it_pair.second);
211 :
212 : // The first iterator should start at the beginning of the section.
213 E : EXPECT_EQ(image_layout.sections[i].addr, it_pair.first->first.start());
214 :
215 : // Ensure the blocks are contiguous, and count the number of blocks as we
216 : // go.
217 E : BlockGraph::AddressSpace::RangeMapConstIter it_old = it_pair.first;
218 E : BlockGraph::AddressSpace::RangeMapConstIter it_cur = it_pair.first;
219 E : ++it_cur;
220 E : ++section_blocks;
221 E : while (it_cur != it_pair.second) {
222 E : EXPECT_EQ(it_old->first.end(), it_cur->first.start());
223 E : it_old = it_cur;
224 E : ++it_cur;
225 E : ++section_blocks;
226 E : }
227 :
228 : // Make sure the last block perfectly covers the section.
229 : EXPECT_EQ(image_layout.sections[i].addr + image_layout.sections[i].size,
230 E : it_old->first.end());
231 E : }
232 :
233 : // All of the blocks should have been covered, save the 2 header blocks.
234 E : EXPECT_EQ(section_blocks + 2, image_layout.blocks.size());
235 :
236 : // Make sure that all bracketed COFF groups have been parsed. There are 8
237 : // of them that we currently know of:
238 : // .CRT$XCA -> .CRT$XCZ: C initializers
239 : // .CRT$XIA -> .CRT$XLZ: C++ initializers
240 : // .CRT$XLA -> .CRT$XLZ: TLS callbacks
241 : // .CRT$XPA -> .CRT$XPZ: CRT pre-termination functions.
242 : // .CRT$XTA -> .CRT$XTZ: CRT termination functions.
243 : // .rtc$IAA -> .rtc$IZZ: Run-time checking initializers.
244 : // .rtc$TAA -> .rtc$TZZ: Run-time checking termination functions.
245 : // .tls -> .tls$ZZZ: TLS data.
246 E : size_t coff_group_blocks = 0;
247 E : it = block_graph.blocks().begin();
248 E : for (; it != block_graph.blocks().end(); ++it) {
249 E : const BlockGraph::Block& block = it->second;
250 E : if (block.attributes() & BlockGraph::COFF_GROUP)
251 E : ++coff_group_blocks;
252 E : }
253 E : EXPECT_EQ(8u, coff_group_blocks);
254 E : }
255 :
256 E : TEST_F(NewDecomposerTest, DecomposeFailsWithNonexistentPdb) {
257 E : base::FilePath image_path(testing::GetExeRelativePath(testing::kTestDllName));
258 E : PEFile image_file;
259 :
260 E : ASSERT_TRUE(image_file.Init(image_path));
261 :
262 E : NewDecomposer decomposer(image_file);
263 E : decomposer.set_pdb_path(testing::GetExeRelativePath(L"nonexistent.pdb"));
264 :
265 E : BlockGraph block_graph;
266 E : ImageLayout image_layout(&block_graph);
267 E : EXPECT_FALSE(decomposer.Decompose(&image_layout));
268 E : }
269 :
270 E : TEST_F(NewDecomposerTest, LabelsAndAttributes) {
271 E : base::FilePath image_path(testing::GetExeRelativePath(testing::kTestDllName));
272 E : PEFile image_file;
273 :
274 E : ASSERT_TRUE(image_file.Init(image_path));
275 :
276 : // Decompose the test image and look at the result.
277 E : NewDecomposer decomposer(image_file);
278 E : BlockGraph block_graph;
279 E : ImageLayout image_layout(&block_graph);
280 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
281 :
282 : // Locate various specific function blocks in the block-graph.
283 E : const BlockGraph::Block* dll_main_block = NULL;
284 E : const BlockGraph::Block* func_with_inl_asm_block = NULL;
285 E : const BlockGraph::Block* strchr_block = NULL;
286 E : const BlockGraph::Block* imp_load_block = NULL;
287 : {
288 : BlockGraph::BlockMap::const_iterator it =
289 E : block_graph.blocks().begin();
290 E : for (; it != block_graph.blocks().end(); ++it) {
291 E : const BlockGraph::Block* block = &(it->second);
292 :
293 E : BlockGraph::Label label;
294 E : if (!block->GetLabel(0, &label))
295 E : continue;
296 E : std::vector<std::string> names;
297 : base::SplitStringUsingSubstr(label.name(), NewDecomposer::kLabelNameSep,
298 E : &names);
299 :
300 E : base::StringPiece block_name(block->name());
301 :
302 : if (std::find(names.begin(), names.end(), "DllMain") != names.end() &&
303 E : block_name.ends_with("\\test_dll.obj")) {
304 E : ASSERT_TRUE(dll_main_block == NULL);
305 E : dll_main_block = &it->second;
306 E : } else if (std::find(names.begin(), names.end(),
307 : "FunctionWithInlineAssembly") != names.end() &&
308 E : block_name.ends_with("\\test_dll.obj")) {
309 E : ASSERT_TRUE(func_with_inl_asm_block == NULL);
310 E : func_with_inl_asm_block = &it->second;
311 E : } else if (std::find(names.begin(), names.end(),
312 : "found_bx") != names.end() &&
313 E : block_name.ends_with("\\strchr.obj")) {
314 E : ASSERT_TRUE(strchr_block == NULL);
315 E : strchr_block = &it->second;
316 E : } else if (std::find(names.begin(), names.end(),
317 E : "__imp_load_CoCreateGuid") != names.end()) {
318 E : ASSERT_TRUE(imp_load_block == NULL);
319 E : imp_load_block = &it->second;
320 : }
321 E : }
322 : }
323 E : ASSERT_TRUE(dll_main_block != NULL);
324 E : ASSERT_TRUE(func_with_inl_asm_block != NULL);
325 E : ASSERT_TRUE(strchr_block != NULL);
326 E : ASSERT_TRUE(imp_load_block != NULL);
327 :
328 E : ASSERT_NE(0UL, imp_load_block->attributes() & BlockGraph::THUNK);
329 :
330 : // TODO(chrisha): When alignment calculations are complete, re-enable this
331 : // test.
332 : // DllMain has a jump table so it should have pointer alignment.
333 : //ASSERT_EQ(kPointerSize, dll_main_block->alignment());
334 :
335 : // Validate that the FunctionWithInlineAssembly block has the appropriate
336 : // attributes.
337 : ASSERT_TRUE(func_with_inl_asm_block->attributes() &
338 E : BlockGraph::HAS_INLINE_ASSEMBLY);
339 :
340 : // Validate that the strchr block has the appropriate attributes.
341 : ASSERT_TRUE(strchr_block->attributes() &
342 E : BlockGraph::BUILT_BY_UNSUPPORTED_COMPILER);
343 :
344 : #ifdef OFFICIAL_BUILD
345 : static const size_t kDllMainLabelCount = 42;
346 : static const size_t kCallSiteLabelCount = 26;
347 : #else
348 : static const size_t kDllMainLabelCount = 32;
349 : static const size_t kCallSiteLabelCount = 10;
350 : #endif
351 :
352 : // Validate that the DllMain block has the expected population of labels.
353 E : EXPECT_EQ(kDllMainLabelCount, dll_main_block->labels().size());
354 :
355 E : std::map<BlockGraph::LabelAttributes, size_t> label_attr_counts;
356 : {
357 : BlockGraph::Block::LabelMap::const_iterator it =
358 E : dll_main_block->labels().begin();
359 E : for (; it != dll_main_block->labels().end(); ++it) {
360 E : BlockGraph::LabelAttributes attr_mask = 1;
361 E : for (; attr_mask != BlockGraph::LABEL_ATTRIBUTES_MAX; attr_mask <<= 1) {
362 E : if (it->second.has_attributes(attr_mask))
363 E : label_attr_counts[attr_mask]++;
364 E : }
365 E : }
366 : }
367 :
368 E : EXPECT_EQ(19, label_attr_counts[BlockGraph::CODE_LABEL]);
369 : EXPECT_EQ(kCallSiteLabelCount,
370 E : label_attr_counts[BlockGraph::CALL_SITE_LABEL]);
371 E : EXPECT_EQ(5, label_attr_counts[BlockGraph::DATA_LABEL]);
372 E : EXPECT_EQ(3, label_attr_counts[BlockGraph::JUMP_TABLE_LABEL]);
373 E : EXPECT_EQ(2, label_attr_counts[BlockGraph::CASE_TABLE_LABEL]);
374 E : EXPECT_EQ(1, label_attr_counts[BlockGraph::DEBUG_START_LABEL]);
375 E : EXPECT_EQ(1, label_attr_counts[BlockGraph::DEBUG_END_LABEL]);
376 E : }
377 :
378 : namespace {
379 :
380 : void GetNtHeadersBlock(const BlockGraph::Block* dos_header_block,
381 E : BlockGraph::Block** out_nt_headers_block) {
382 E : DCHECK(out_nt_headers_block != NULL);
383 :
384 E : ConstTypedBlock<IMAGE_DOS_HEADER> dos_header;
385 E : ASSERT_TRUE(dos_header.Init(0, dos_header_block));
386 E : ConstTypedBlock<IMAGE_NT_HEADERS> nt_headers;
387 E : ASSERT_TRUE(dos_header.Dereference(dos_header->e_lfanew, &nt_headers));
388 E : ASSERT_TRUE(nt_headers.block() != NULL);
389 E : *out_nt_headers_block = const_cast<BlockGraph::Block*>(nt_headers.block());
390 E : }
391 :
392 : // This test fixture class contains all the tests that need files generated by
393 : // the relinker (the new image and its corresponding PDB).
394 : class NewDecomposerAfterRelinkTest : public NewDecomposerTest {
395 : typedef NewDecomposerTest Super;
396 :
397 : public:
398 E : virtual void SetUp() OVERRIDE {
399 E : Super::SetUp();
400 E : }
401 :
402 E : void Relink(bool compress_pdb) {
403 : // Initialize a relinker and generate a pdb that contains a block-graph
404 : // stream.
405 E : relinked_dll_ = temp_dir_.Append(testing::kTestDllName);
406 E : relinked_pdb_ = temp_dir_.Append(testing::kTestDllPdbName);
407 :
408 : relinker_.set_input_path(testing::GetExeRelativePath(
409 E : testing::kTestDllName));
410 : relinker_.set_input_pdb_path(testing::GetExeRelativePath(
411 E : testing::kTestDllPdbName));
412 E : relinker_.set_allow_overwrite(true);
413 E : relinker_.set_augment_pdb(true);
414 E : relinker_.set_compress_pdb(compress_pdb);
415 E : relinker_.set_output_path(relinked_dll_);
416 E : relinker_.set_output_pdb_path(relinked_pdb_);
417 E : ASSERT_TRUE(relinker_.Init());
418 E : ASSERT_TRUE(relinker_.Relink());
419 E : }
420 :
421 : // Given a decomposed image this checks its NT headers against those
422 : // contained in the transformed image stored in the relinker.
423 E : void ReconcileNtHeaders(ImageLayout* image_layout) {
424 E : DCHECK(image_layout != NULL);
425 :
426 : // Get the NT headers block associated with the in-memory representation of
427 : // the relinked image.
428 E : BlockGraph::Block* nt1 = NULL;
429 E : ASSERT_NO_FATAL_FAILURE(GetNtHeadersBlock(relinker_.dos_header_block(),
430 : &nt1));
431 E : ASSERT_TRUE(nt1 != NULL);
432 :
433 : // Get the NT headers block associated with the decomposition just performed
434 : // on the relinked image.
435 : BlockGraph::Block* dos_header_block =
436 E : image_layout->blocks.GetBlockByAddress(core::RelativeAddress(0));
437 E : ASSERT_TRUE(dos_header_block != NULL);
438 E : BlockGraph::Block* nt2 = NULL;
439 E : ASSERT_NO_FATAL_FAILURE(GetNtHeadersBlock(dos_header_block, &nt2));
440 E : ASSERT_TRUE(nt2 != NULL);
441 :
442 : // The NT headers don't compare equal because things like the timestamp and
443 : // checksum are filled out post-transform. We copy the old NT headers into
444 : // the new image so that we can do a simple comparison afterwards.
445 E : ASSERT_EQ(nt1->data_size(), nt2->data_size());
446 E : nt1->SetData(nt2->data(), nt2->data_size());
447 E : }
448 :
449 : // Used to ensure that round-trip decomposition works (where the image is not
450 : // decomposed, but rather deserialized from the PDB).
451 E : void LoadRedecompositionData(bool compressed) {
452 E : ASSERT_NO_FATAL_FAILURE(Relink(compressed));
453 :
454 E : PEFile image_file;
455 E : ASSERT_TRUE(image_file.Init(relinked_dll_));
456 :
457 : // Decompose the test image and look at the result.
458 E : NewDecomposer decomposer(image_file);
459 E : BlockGraph block_graph;
460 E : ImageLayout image_layout(&block_graph);
461 :
462 E : ASSERT_TRUE(decomposer.Decompose(&image_layout));
463 :
464 : // Certain data is written to the NT headers post-transform (checksum), so
465 : // it's not reflected in the relinker's block-graph. We reconcile the
466 : // headers prior to doing the comparison.
467 E : ASSERT_NO_FATAL_FAILURE(ReconcileNtHeaders(&image_layout));
468 :
469 : // Ensure that the post-relink block-graph and the deserialized one from the
470 : // PDB are the same.
471 E : block_graph::BlockGraphSerializer bgs;
472 E : ASSERT_TRUE(::testing::BlockGraphsEqual(relinker_.block_graph(),
473 : block_graph,
474 : bgs));
475 E : }
476 :
477 : PERelinker relinker_;
478 : base::FilePath relinked_dll_;
479 : base::FilePath relinked_pdb_;
480 : };
481 :
482 : } // namespace
483 :
484 E : TEST_F(NewDecomposerAfterRelinkTest, LoadRedecompositionDataUncompressed) {
485 E : ASSERT_NO_FATAL_FAILURE(LoadRedecompositionData(false));
486 E : }
487 :
488 E : TEST_F(NewDecomposerAfterRelinkTest, LoadRedecompositionDataCompressed) {
489 E : ASSERT_NO_FATAL_FAILURE(LoadRedecompositionData(true));
490 E : }
491 :
492 E : TEST_F(NewDecomposerAfterRelinkTest, FailToLoadBlockGraphWithInvalidVersion) {
493 E : ASSERT_NO_FATAL_FAILURE(Relink(true));
494 :
495 : // Get the block-graph stream from the PDB and change the version of it.
496 :
497 : // Get the stream.
498 E : pdb::PdbFile pdb_file;
499 E : pdb::PdbReader pdb_reader;
500 E : pdb_reader.Read(relinked_pdb_, &pdb_file);
501 E : scoped_refptr<pdb::PdbStream> block_graph_stream;
502 : EXPECT_TRUE(pdb::LoadNamedStreamFromPdbFile(pdb::kSyzygyBlockGraphStreamName,
503 : &pdb_file,
504 E : &block_graph_stream));
505 :
506 : // Create a copy of the stream. We need to do this to have a stream that we
507 : // can modify.
508 E : scoped_refptr<pdb::PdbByteStream> new_stream = new pdb::PdbByteStream();
509 E : ASSERT_TRUE(new_stream->Init(block_graph_stream.get()));
510 E : block_graph_stream = new_stream.get();
511 : scoped_refptr<pdb::WritablePdbStream> block_graph_writer =
512 E : block_graph_stream->GetWritablePdbStream();
513 E : ASSERT_TRUE(block_graph_writer.get() != NULL);
514 :
515 : // Change the version of the stream.
516 E : block_graph_writer->set_pos(0);
517 E : block_graph_writer->Write(pdb::kSyzygyBlockGraphStreamVersion + 1);
518 :
519 E : BlockGraph block_graph;
520 E : ImageLayout image_layout(&block_graph);
521 :
522 : // We've invalided the version previously so this test should fail.
523 E : PEFile image_file;
524 E : ASSERT_TRUE(image_file.Init(relinked_dll_));
525 : ASSERT_FALSE(TestNewDecomposer::LoadBlockGraphFromPdbStream(
526 E : image_file, block_graph_stream.get(), &image_layout));
527 E : }
528 :
529 : } // namespace pe
|