1 : // Copyright 2013 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/pe_relinker_util.h"
16 :
17 : #include "base/files/file_util.h"
18 : #include "syzygy/block_graph/transform.h"
19 : #include "syzygy/core/file_util.h"
20 : #include "syzygy/core/zstream.h"
21 : #include "syzygy/pdb/pdb_byte_stream.h"
22 : #include "syzygy/pdb/pdb_util.h"
23 : #include "syzygy/pe/find.h"
24 : #include "syzygy/pe/metadata.h"
25 : #include "syzygy/pe/pe_image_layout_builder.h"
26 : #include "syzygy/pe/pe_utils.h"
27 : #include "syzygy/pe/serialization.h"
28 : #include "syzygy/pe/orderers/pe_orderer.h"
29 : #include "syzygy/pe/transforms/add_metadata_transform.h"
30 : #include "syzygy/pe/transforms/add_pdb_info_transform.h"
31 : #include "syzygy/pe/transforms/pe_prepare_headers_transform.h"
32 : #include "syzygy/pe/transforms/pe_remove_empty_sections_transform.h"
33 :
34 : namespace pe {
35 :
36 : namespace {
37 :
38 : using block_graph::BlockGraph;
39 : using block_graph::BlockGraphTransformInterface;
40 : using block_graph::OrderedBlockGraph;
41 : using core::RelativeAddress;
42 : using pdb::NameStreamMap;
43 : using pdb::PdbByteStream;
44 : using pdb::PdbFile;
45 : using pdb::PdbInfoHeader70;
46 : using pdb::PdbStream;
47 : using pdb::WritablePdbStream;
48 : using pe::PETransformPolicy;
49 :
50 : // A utility class for wrapping a serialization OutStream around a
51 : // WritablePdbStream.
52 : // TODO(chrisha): We really need to centralize stream/buffer semantics in
53 : // a small set of clean interfaces, and make all input/output/parsing work
54 : // on these interfaces.
55 : class PdbOutStream : public core::OutStream {
56 : public:
57 E : explicit PdbOutStream(WritablePdbStream* pdb_stream)
58 E : : pdb_stream_(pdb_stream) {
59 E : DCHECK(pdb_stream != NULL);
60 E : }
61 :
62 E : virtual ~PdbOutStream() { }
63 :
64 E : bool Write(size_t length, const core::Byte* bytes) override {
65 E : return pdb_stream_->Write(length, bytes);
66 E : }
67 :
68 : private:
69 : scoped_refptr<WritablePdbStream> pdb_stream_;
70 : };
71 :
72 : void BuildOmapVectors(const RelativeAddressRange& input_range,
73 : const ImageLayout& output_image_layout,
74 : std::vector<OMAP>* omap_to,
75 E : std::vector<OMAP>* omap_from) {
76 E : DCHECK(omap_to != NULL);
77 E : DCHECK(omap_from != NULL);
78 :
79 E : LOG(INFO) << "Building OMAP vectors.";
80 :
81 : // Get the range of the output image, sans headers. This is required for
82 : // generating OMAP information.
83 E : RelativeAddressRange output_range;
84 E : GetOmapRange(output_image_layout.sections, &output_range);
85 :
86 E : ImageSourceMap reverse_map;
87 E : BuildImageSourceMap(output_image_layout, &reverse_map);
88 :
89 E : ImageSourceMap forward_map;
90 E : if (reverse_map.ComputeInverse(&forward_map) != 0) {
91 E : LOG(WARNING) << "OMAPFROM not unique (there exist repeated source ranges).";
92 : }
93 :
94 : // Build the two OMAP vectors.
95 E : BuildOmapVectorFromImageSourceMap(output_range, reverse_map, omap_to);
96 E : BuildOmapVectorFromImageSourceMap(input_range, forward_map, omap_from);
97 E : }
98 :
99 : // Get a specific named stream if it already exists, otherwise create one.
100 : // @param stream_name The name of the stream.
101 : // @param name_stream_map The map containing the names of the streams in the
102 : // PDB. If the stream doesn't already exist the map will be augmented with
103 : // another entry.
104 : // @param pdb_file The PDB file to which the stream will be added.
105 : // @param replace_stream If true, will cause a new stream to be created even if
106 : // another one already existed.
107 : // @return a pointer to the PDB stream on success, NULL on failure.
108 : PdbStream* GetOrCreatePdbStreamByName(const char* stream_name,
109 : bool replace_stream,
110 : NameStreamMap* name_stream_map,
111 E : PdbFile* pdb_file) {
112 E : DCHECK(name_stream_map != NULL);
113 E : DCHECK(pdb_file != NULL);
114 E : scoped_refptr<PdbStream> stream;
115 :
116 E : NameStreamMap::const_iterator name_it = name_stream_map->find(stream_name);
117 E : if (name_it != name_stream_map->end()) {
118 : // Replace the existing stream by a brand-new one if it's required.
119 i : if (replace_stream) {
120 i : stream = new PdbByteStream();
121 i : pdb_file->ReplaceStream(name_it->second, stream.get());
122 i : } else {
123 i : if (!pdb::EnsureStreamWritable(name_it->second, pdb_file)) {
124 i : LOG(ERROR) << "Failed to make " << stream_name << " stream writable.";
125 i : return NULL;
126 : }
127 i : stream = pdb_file->GetStream(name_it->second);
128 : }
129 i : } else {
130 E : stream = new PdbByteStream();
131 : uint32_t index = static_cast<uint32_t>(
132 E : pdb_file->AppendStream(stream.get()));
133 E : (*name_stream_map)[stream_name] = index;
134 : }
135 :
136 E : return stream.get();
137 E : }
138 :
139 : // This updates or creates the Syzygy history stream, appending the metadata
140 : // describing this module and transform. The history stream consists of
141 : // a named PDB stream with the name /Syzygy/History. It consists of:
142 : //
143 : // uint32_t version
144 : // uint32_t history_length
145 : // serialized pe::Metadata 0
146 : // ...
147 : // serialized pe::Metadata history_length - 1
148 : //
149 : // If the format is changed, be sure to update this documentation and
150 : // pdb::kSyzygyHistoryStreamVersion (in pdb_constants.h).
151 : bool WriteSyzygyHistoryStream(const base::FilePath& input_path,
152 : NameStreamMap* name_stream_map,
153 E : PdbFile* pdb_file) {
154 : // Get the history stream.
155 : scoped_refptr<PdbStream> history_reader =
156 E : GetOrCreatePdbStreamByName(pdb::kSyzygyHistoryStreamName,
157 : false,
158 : name_stream_map,
159 : pdb_file);
160 :
161 E : if (history_reader.get() == nullptr) {
162 i : LOG(ERROR) << "Failed to get the history stream.";
163 i : return false;
164 : }
165 :
166 : scoped_refptr<WritablePdbStream> history_writer =
167 E : history_reader->GetWritableStream();
168 E : DCHECK(history_writer.get() != nullptr);
169 :
170 : // Get the metadata.
171 E : Metadata metadata;
172 E : PEFile pe_file;
173 E : if (!pe_file.Init(input_path)) {
174 i : LOG(ERROR) << "Failed to initialize PE file for \"" << input_path.value()
175 : << "\".";
176 i : return false;
177 : }
178 :
179 E : PEFile::Signature pe_sig;
180 E : pe_file.GetSignature(&pe_sig);
181 E : if (!metadata.Init(pe_sig)) {
182 i : LOG(ERROR) << "Failed to initialize metadata for \"" << input_path.value()
183 : << "\".";
184 i : return false;
185 : }
186 :
187 : // Validate the history stream if it is non-empty.
188 E : if (history_reader->length() > 0) {
189 : // Read the header.
190 i : uint32_t version = 0;
191 i : uint32_t history_length = 0;
192 i : if (!history_reader->ReadBytesAt(0, sizeof(version), &version) ||
193 : !history_reader->ReadBytesAt(sizeof(version), sizeof(history_length),
194 : &history_length)) {
195 i : LOG(ERROR) << "Failed to read existing Syzygy history stream header.";
196 i : return false;
197 : }
198 :
199 : // Check the version.
200 i : if (version != pdb::kSyzygyHistoryStreamVersion) {
201 i : LOG(ERROR) << "PDB contains unsupported Syzygy history stream version "
202 : << "(got " << version << ", expected "
203 : << pdb::kSyzygyHistoryStreamVersion << ").";
204 i : return false;
205 : }
206 :
207 : // Increment the history length and rewrite it.
208 i : history_length++;
209 i : history_writer->set_pos(sizeof(pdb::kSyzygyHistoryStreamVersion));
210 i : if (!history_writer->Write(history_length)) {
211 i : LOG(ERROR) << "Failed to write new Syzygy history stream length.";
212 i : return false;
213 : }
214 i : } else {
215 : // If there wasn't already a history stream, create one and write the
216 : // header.
217 E : DCHECK_EQ(0u, history_writer->pos());
218 E : const uint32_t kHistoryLength = 1;
219 E : if (!history_writer->Write(pdb::kSyzygyHistoryStreamVersion) ||
220 : !history_writer->Write(kHistoryLength)) {
221 i : LOG(ERROR) << "Failed to write Syzygy history stream header.";
222 i : return false;
223 : }
224 : }
225 :
226 : // Append the metadata to the history.
227 E : history_writer->set_pos(history_writer->length());
228 E : PdbOutStream out_stream(history_writer.get());
229 E : core::OutArchive out_archive(&out_stream);
230 E : if (!out_archive.Save(metadata)) {
231 i : LOG(ERROR) << "Failed to write metadata to Syzygy history stream.";
232 i : return false;
233 : }
234 :
235 E : return true;
236 E : }
237 :
238 : // This writes the serialized block-graph and the image layout in a PDB stream
239 : // named /Syzygy/BlockGraph. If the format is changed, be sure to update this
240 : // documentation and pdb::kSyzygyBlockGraphStreamVersion (in pdb_constants.h).
241 : // The block graph stream will not include the data from the blocks of the
242 : // block-graph. If the strip-strings flag is set to true the strings contained
243 : // in the block-graph won't be saved.
244 : bool WriteSyzygyBlockGraphStream(const PEFile& pe_file,
245 : const ImageLayout& image_layout,
246 : bool strip_strings,
247 : bool compress,
248 : NameStreamMap* name_stream_map,
249 E : PdbFile* pdb_file) {
250 : // Get the redecomposition data stream.
251 : scoped_refptr<PdbStream> block_graph_reader =
252 E : GetOrCreatePdbStreamByName(pdb::kSyzygyBlockGraphStreamName,
253 : true,
254 : name_stream_map,
255 : pdb_file);
256 :
257 E : if (block_graph_reader.get() == nullptr) {
258 i : LOG(ERROR) << "Failed to get the block-graph stream.";
259 i : return false;
260 : }
261 E : DCHECK_EQ(0u, block_graph_reader->length());
262 :
263 : scoped_refptr<WritablePdbStream> block_graph_writer =
264 E : block_graph_reader->GetWritableStream();
265 E : DCHECK(block_graph_writer.get() != NULL);
266 :
267 : // Write the version of the BlockGraph stream, and whether or not its
268 : // contents are compressed.
269 E : if (!block_graph_writer->Write(pdb::kSyzygyBlockGraphStreamVersion) ||
270 : !block_graph_writer->Write(static_cast<unsigned char>(compress))) {
271 i : LOG(ERROR) << "Failed to write Syzygy BlockGraph stream header.";
272 i : return false;
273 : }
274 :
275 : // Set up the output stream.
276 E : PdbOutStream pdb_out_stream(block_graph_writer.get());
277 E : core::OutStream* out_stream = &pdb_out_stream;
278 :
279 : // If requested, compress the output.
280 E : std::unique_ptr<core::ZOutStream> zip_stream;
281 E : if (compress) {
282 E : zip_stream.reset(new core::ZOutStream(&pdb_out_stream));
283 E : out_stream = zip_stream.get();
284 E : if (!zip_stream->Init(core::ZOutStream::kZBestCompression)) {
285 i : LOG(ERROR) << "Failed to initialize zlib compressor.";
286 i : return false;
287 : }
288 : }
289 :
290 E : core::OutArchive out_archive(out_stream);
291 :
292 : // Set up the serialization properties.
293 E : block_graph::BlockGraphSerializer::Attributes attributes = 0;
294 E : if (strip_strings)
295 E : attributes |= block_graph::BlockGraphSerializer::OMIT_STRINGS;
296 :
297 : // And finally, perform the serialization.
298 E : if (!SaveBlockGraphAndImageLayout(pe_file, attributes, image_layout,
299 : &out_archive)) {
300 i : LOG(ERROR) << "SaveBlockGraphAndImageLayout failed.";
301 i : return false;
302 : }
303 :
304 : // We have to flush the stream in case it's a zstream.
305 E : out_stream->Flush();
306 :
307 E : return true;
308 E : }
309 :
310 : } // namespace
311 :
312 : bool ValidateAndInferPaths(
313 : const base::FilePath& input_module,
314 : const base::FilePath& output_module,
315 : bool allow_overwrite,
316 : base::FilePath* input_pdb,
317 E : base::FilePath* output_pdb) {
318 E : DCHECK(!input_module.empty());
319 E : DCHECK(!output_module.empty());
320 E : DCHECK_NE(reinterpret_cast<base::FilePath*>(NULL), input_pdb);
321 E : DCHECK_NE(reinterpret_cast<base::FilePath*>(NULL), output_pdb);
322 :
323 E : if (!base::PathExists(input_module)) {
324 E : LOG(ERROR) << "Input module not found: " << input_module.value();
325 E : return false;
326 : }
327 :
328 E : if (!allow_overwrite && base::PathExists(output_module)) {
329 E : LOG(ERROR) << "Output module exists: " << output_module.value();
330 E : LOG(ERROR) << "Specify --overwrite to ignore this error.";
331 E : return false;
332 : }
333 :
334 : // If no input PDB was specified then search for it.
335 E : if (input_pdb->empty()) {
336 E : LOG(INFO) << "Input PDB not specified, searching for it.";
337 E : if (!pe::FindPdbForModule(input_module, input_pdb) ||
338 : input_pdb->empty()) {
339 i : LOG(ERROR) << "Unable to find PDB file for module: "
340 : << input_module.value();
341 i : return NULL;
342 : }
343 : }
344 :
345 E : if (!base::PathExists(*input_pdb)) {
346 E : LOG(ERROR) << "Input PDB not found: " << input_pdb->value();
347 E : return false;
348 : }
349 :
350 : // If no output PDB path is specified, infer one.
351 E : if (output_pdb->empty()) {
352 : // If the input and output DLLs have the same basename, default to writing
353 : // using the same PDB basename, but alongside the new module.
354 E : if (input_module.BaseName() == output_module.BaseName()) {
355 E : *output_pdb = output_module.DirName().Append(input_pdb->BaseName());
356 E : } else {
357 : // Otherwise, default to using the output basename with a PDB extension
358 : // added to it.
359 E : *output_pdb = output_module.AddExtension(L"pdb");
360 : }
361 :
362 E : LOG(INFO) << "Using default output PDB path: " << output_pdb->value();
363 : }
364 :
365 E : if (!allow_overwrite && base::PathExists(*output_pdb)) {
366 E : LOG(ERROR) << "Output PDB exists: " << output_pdb->value();
367 E : LOG(ERROR) << "Specify --overwrite to ignore this error.";
368 E : return false;
369 : }
370 :
371 : // Perform some extra checking to make sure that writes aren't going to
372 : // collide. This prevents us from overwriting the input, effectively
373 : // preventing in-place transforms. This is not fool-proof in the face of
374 : // weird junctions but it will catch common errors.
375 :
376 : core::FilePathCompareResult result =
377 E : core::CompareFilePaths(input_module, output_module);
378 E : if (result == core::kEquivalentFilePaths) {
379 i : LOG(ERROR) << "Input and output module paths are equivalent.";
380 i : LOG(ERROR) << "Input module path: " << input_module.value();
381 i : LOG(ERROR) << "Output module path: " << output_module.value();
382 i : return false;
383 : }
384 :
385 E : result = core::CompareFilePaths(*input_pdb, *output_pdb);
386 E : if (result == core::kEquivalentFilePaths) {
387 i : LOG(ERROR) << "Input and output PDB paths are equivalent.";
388 i : LOG(ERROR) << "Input PDB path: " << input_pdb->value();
389 i : LOG(ERROR) << "Output PDB path: " << output_pdb->value();
390 i : return false;
391 : }
392 :
393 E : result = core::CompareFilePaths(output_module, *output_pdb);
394 E : if (result == core::kEquivalentFilePaths) {
395 i : LOG(ERROR) << "Output module and PDB paths are equivalent.";
396 i : LOG(ERROR) << "Output module path: " << output_module.value();
397 i : LOG(ERROR) << "Output PDB path: " << output_pdb->value();
398 i : return false;
399 : }
400 :
401 E : return true;
402 E : }
403 :
404 : bool FinalizeBlockGraph(const base::FilePath& input_module,
405 : const base::FilePath& output_pdb,
406 : const GUID& pdb_guid,
407 : bool add_metadata,
408 : const PETransformPolicy* policy,
409 : BlockGraph* block_graph,
410 E : BlockGraph::Block* dos_header_block) {
411 E : DCHECK_NE(reinterpret_cast<PETransformPolicy*>(NULL), policy);
412 E : DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
413 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), dos_header_block);
414 E : LOG(INFO) << "Finalizing block-graph for \"" << input_module.value() << "\".";
415 :
416 E : std::vector<BlockGraphTransformInterface*> post_transforms;
417 E : pe::transforms::AddMetadataTransform add_metadata_tx(input_module);
418 E : pe::transforms::AddPdbInfoTransform add_pdb_info_tx(output_pdb, 1,
419 : pdb_guid);
420 E : pe::transforms::PERemoveEmptySectionsTransform remove_empty_sections;
421 E : pe::transforms::PEPrepareHeadersTransform prep_headers_tx;
422 :
423 E : if (add_metadata)
424 E : post_transforms.push_back(&add_metadata_tx);
425 E : post_transforms.push_back(&add_pdb_info_tx);
426 E : post_transforms.push_back(&remove_empty_sections);
427 E : post_transforms.push_back(&prep_headers_tx);
428 :
429 E : if (!block_graph::ApplyBlockGraphTransforms(post_transforms,
430 : policy,
431 : block_graph,
432 : dos_header_block)) {
433 i : return false;
434 : }
435 :
436 E : return true;
437 E : }
438 :
439 : bool FinalizeOrderedBlockGraph(
440 : OrderedBlockGraph* ordered_block_graph,
441 E : BlockGraph::Block* dos_header_block) {
442 E : DCHECK_NE(reinterpret_cast<OrderedBlockGraph*>(NULL), ordered_block_graph);
443 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), dos_header_block);
444 E : pe::orderers::PEOrderer pe_orderer;
445 E : if (!pe_orderer.OrderBlockGraph(ordered_block_graph, dos_header_block))
446 i : return false;
447 E : return true;
448 E : }
449 :
450 : bool BuildImageLayout(size_t padding,
451 : size_t code_alignment,
452 : const OrderedBlockGraph& ordered_block_graph,
453 : BlockGraph::Block* dos_header_block,
454 E : ImageLayout* image_layout) {
455 E : DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), dos_header_block);
456 E : DCHECK_NE(reinterpret_cast<ImageLayout*>(NULL), image_layout);
457 :
458 E : LOG(INFO) << "Building image layout.";
459 :
460 E : PEImageLayoutBuilder builder(image_layout);
461 E : builder.set_padding(padding);
462 E : builder.set_code_alignment(code_alignment);
463 E : if (!builder.LayoutImageHeaders(dos_header_block)) {
464 i : LOG(ERROR) << "PEImageLayoutBuilder::LayoutImageHeaders failed.";
465 i : return false;
466 : }
467 :
468 E : if (!builder.LayoutOrderedBlockGraph(ordered_block_graph)) {
469 i : LOG(ERROR) << "PEImageLayoutBuilder::LayoutOrderedBlockGraph failed.";
470 i : return false;
471 : }
472 :
473 E : LOG(INFO) << "Finalizing image layout.";
474 E : if (!builder.Finalize()) {
475 i : LOG(ERROR) << "PEImageLayoutBuilder::Finalize failed.";
476 i : return false;
477 : }
478 :
479 E : return true;
480 E : }
481 :
482 : void GetOmapRange(const std::vector<ImageLayout::SectionInfo>& sections,
483 E : RelativeAddressRange* range) {
484 E : DCHECK_NE(reinterpret_cast<RelativeAddressRange*>(NULL), range);
485 :
486 : // There need to be at least two sections, one containing something and the
487 : // other containing the relocs.
488 E : DCHECK_GT(sections.size(), 1u);
489 E : DCHECK_EQ(sections.back().name, std::string(kRelocSectionName));
490 :
491 : // For some reason, if we output OMAP entries for the headers (before the
492 : // first section), everything falls apart. Not outputting these allows the
493 : // unittests to pass. Also, we don't want to output OMAP information for
494 : // the relocs, as these are entirely different from image to image.
495 E : RelativeAddress start_of_image = sections.front().addr;
496 E : RelativeAddress end_of_image = sections.back().addr;
497 E : *range = RelativeAddressRange(start_of_image, end_of_image - start_of_image);
498 E : }
499 :
500 : bool FinalizePdbFile(const base::FilePath input_module,
501 : const base::FilePath output_module,
502 : const RelativeAddressRange input_range,
503 : const ImageLayout& image_layout,
504 : const GUID& guid,
505 : bool augment_pdb,
506 : bool strip_strings,
507 : bool compress_pdb,
508 E : pdb::PdbFile* pdb_file) {
509 E : DCHECK(pdb_file != NULL);
510 :
511 E : LOG(INFO) << "Finalizing PDB file.";
512 :
513 E : VLOG(1) << "Updating GUID.";
514 E : if (!pdb::SetGuid(guid, pdb_file)) {
515 i : LOG(ERROR) << "Unable to set PDB GUID.";
516 i : return false;
517 : }
518 :
519 E : VLOG(1) << "Building OMAP vectors.";
520 E : std::vector<OMAP> omap_to, omap_from;
521 E : BuildOmapVectors(input_range, image_layout, &omap_to, &omap_from);
522 :
523 E : VLOG(1) << "Writing OMAP vectors.";
524 E : if (!pdb::SetOmapToStream(omap_to, pdb_file)) {
525 i : LOG(ERROR) << "Unable to set OMAP_TO.";
526 i : return false;
527 : }
528 E : if (!pdb::SetOmapFromStream(omap_from, pdb_file)) {
529 i : LOG(ERROR) << "Unable to set OMAP_FROM.";
530 i : return false;
531 : }
532 :
533 : // Parse the header and named streams.
534 E : pdb::PdbInfoHeader70 header = {};
535 E : pdb::NameStreamMap name_stream_map;
536 E : if (!pdb::ReadHeaderInfoStream(*pdb_file, &header, &name_stream_map))
537 i : return false;
538 :
539 : // Update/create the Syzygy history stream.
540 E : VLOG(1) << "Adding history stream to PDB.";
541 E : if (!WriteSyzygyHistoryStream(input_module, &name_stream_map, pdb_file))
542 i : return false;
543 :
544 : // Add redecomposition data in another stream, only if augment_pdb_ is set.
545 E : if (augment_pdb) {
546 E : PEFile new_pe_file;
547 E : if (!new_pe_file.Init(output_module)) {
548 i : LOG(ERROR) << "Failed to read newly written PE file.";
549 i : return false;
550 : }
551 :
552 E : VLOG(1) << "Adding serialized block-graph stream to PDB.";
553 E : if (!WriteSyzygyBlockGraphStream(new_pe_file,
554 : image_layout,
555 : strip_strings,
556 : compress_pdb,
557 : &name_stream_map,
558 : pdb_file)) {
559 i : return false;
560 : }
561 E : }
562 :
563 : // Write the updated name-stream map back to the header info stream.
564 E : VLOG(1) << "Updating PDB headers.";
565 E : if (!pdb::WriteHeaderInfoStream(header, name_stream_map, pdb_file))
566 i : return false;
567 :
568 : // Stream 0 contains a copy of the previous PDB's directory. This, combined
569 : // with copy-on-write semantics of individual blocks makes the file contain
570 : // its whole edit history. Since we're writing a 'new' PDB file (we reset the
571 : // GUID and age), we have no history so can safely throw away this stream.
572 E : VLOG(1) << "Removing previous PDB directory stream.";
573 E : pdb_file->ReplaceStream(0, NULL);
574 :
575 E : return true;
576 E : }
577 :
578 : } // namespace pe
|