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/pe_relinker.h"
16 :
17 : #include "base/file_util.h"
18 : #include "syzygy/block_graph/orderers/original_orderer.h"
19 : #include "syzygy/core/zstream.h"
20 : #include "syzygy/pdb/pdb_byte_stream.h"
21 : #include "syzygy/pdb/pdb_file.h"
22 : #include "syzygy/pdb/pdb_reader.h"
23 : #include "syzygy/pdb/pdb_util.h"
24 : #include "syzygy/pdb/pdb_writer.h"
25 : #include "syzygy/pe/decomposer.h"
26 : #include "syzygy/pe/find.h"
27 : #include "syzygy/pe/image_layout_builder.h"
28 : #include "syzygy/pe/image_source_map.h"
29 : #include "syzygy/pe/metadata.h"
30 : #include "syzygy/pe/new_decomposer.h"
31 : #include "syzygy/pe/pdb_info.h"
32 : #include "syzygy/pe/pe_file_writer.h"
33 : #include "syzygy/pe/pe_utils.h"
34 : #include "syzygy/pe/serialization.h"
35 : #include "syzygy/pe/orderers/pe_orderer.h"
36 : #include "syzygy/pe/transforms/add_metadata_transform.h"
37 : #include "syzygy/pe/transforms/add_pdb_info_transform.h"
38 : #include "syzygy/pe/transforms/prepare_headers_transform.h"
39 :
40 : namespace pe {
41 :
42 : namespace {
43 :
44 : typedef block_graph::BlockGraphTransformInterface Transform;
45 : typedef block_graph::BlockGraphOrdererInterface Orderer;
46 :
47 : using block_graph::ApplyBlockGraphTransform;
48 : using block_graph::BlockGraph;
49 : using block_graph::OrderedBlockGraph;
50 : using core::RelativeAddress;
51 : using pdb::NameStreamMap;
52 : using pdb::PdbByteStream;
53 : using pdb::PdbFile;
54 : using pdb::PdbInfoHeader70;
55 : using pdb::PdbMutatorInterface;
56 : using pdb::PdbStream;
57 : using pdb::WritablePdbStream;
58 :
59 : // A utility class for wrapping a serialization OutStream around a
60 : // WritablePdbStream.
61 : // TODO(chrisha): We really need to centralize stream/buffer semantics in
62 : // a small set of clean interfaces, and make all input/output/parsing work
63 : // on these interfaces.
64 : class PdbOutStream : public core::OutStream {
65 : public:
66 E : explicit PdbOutStream(WritablePdbStream* pdb_stream)
67 : : pdb_stream_(pdb_stream) {
68 E : DCHECK(pdb_stream != NULL);
69 E : }
70 :
71 E : virtual ~PdbOutStream() { }
72 :
73 E : virtual bool Write(size_t length, const core::Byte* bytes) OVERRIDE {
74 E : return pdb_stream_->Write(length, bytes);
75 E : }
76 :
77 : private:
78 : scoped_refptr<WritablePdbStream> pdb_stream_;
79 : };
80 :
81 : void GetOmapRange(const std::vector<ImageLayout::SectionInfo>& sections,
82 E : RelativeAddressRange* range) {
83 E : DCHECK(range != NULL);
84 :
85 : // There need to be at least two sections, one containing something and the
86 : // other containing the relocs.
87 E : DCHECK_GT(sections.size(), 1u);
88 E : DCHECK_EQ(sections.back().name, std::string(kRelocSectionName));
89 :
90 : // For some reason, if we output OMAP entries for the headers (before the
91 : // first section), everything falls apart. Not outputting these allows the
92 : // unittests to pass. Also, we don't want to output OMAP information for
93 : // the relocs, as these are entirely different from image to image.
94 E : RelativeAddress start_of_image = sections.front().addr;
95 E : RelativeAddress end_of_image = sections.back().addr;
96 E : *range = RelativeAddressRange(start_of_image, end_of_image - start_of_image);
97 E : }
98 :
99 : // TODO(chrisha): Make this utility function part of orderer.h.
100 : bool ApplyOrderer(Orderer* orderer,
101 : OrderedBlockGraph* obg,
102 E : BlockGraph::Block* header_block) {
103 E : DCHECK(orderer != NULL);
104 E : DCHECK(obg != NULL);
105 E : DCHECK(header_block != NULL);
106 :
107 E : if (!orderer->OrderBlockGraph(obg, header_block)) {
108 E : LOG(ERROR) << "Orderer failed: " << orderer->name();
109 E : return false;
110 : }
111 :
112 E : return true;
113 E : }
114 :
115 : bool ApplyPdbMutator(PdbMutatorInterface* pdb_mutator,
116 E : PdbFile* pdb_file) {
117 E : DCHECK(pdb_mutator != NULL);
118 E : DCHECK(pdb_file != NULL);
119 :
120 E : if (!pdb_mutator->MutatePdb(pdb_file)) {
121 E : LOG(ERROR) << "PDB mutator failed: " << pdb_mutator->name();
122 E : return false;
123 : }
124 :
125 E : return true;
126 E : }
127 :
128 : // Initializes input_pdb_path, output_pdb_path, pe_file and guid. If the input
129 : // paths are unable to be found this will return false. If @p allow_overwrite is
130 : // false and output path or output_pdb_path will overwrite an existing file this
131 : // will return false. @p input_pdb_path may be left empty in which case it will
132 : // be automatically determined from the debug information in @p input_path; this
133 : // step may fail causing this to return false. @p output_pdb_path may also be
134 : // left empty in which case it will be inferred from input_pdb_path, being
135 : // placed alongside output_path.
136 : bool InitializePaths(const FilePath& input_path,
137 : const FilePath& output_path,
138 : bool allow_overwrite,
139 : FilePath* input_pdb_path,
140 E : FilePath* output_pdb_path) {
141 E : DCHECK(input_pdb_path != NULL);
142 E : DCHECK(output_pdb_path != NULL);
143 :
144 : // At a very minimum we have to specify input and outputs.
145 E : if (input_path.empty() || output_path.empty()) {
146 E : LOG(ERROR) << "input_path and output_path must be set!";
147 E : return false;
148 : }
149 :
150 E : if (!file_util::PathExists(input_path)) {
151 E : LOG(ERROR) << "Input module not found: " << input_path.value();
152 E : return false;
153 : }
154 :
155 : // No input PDB specified? Find it automagically.
156 E : if (input_pdb_path->empty()) {
157 E : LOG(INFO) << "Input PDB not specified, searching for it.";
158 : if (!FindPdbForModule(input_path, input_pdb_path) ||
159 E : input_pdb_path->empty()) {
160 i : LOG(ERROR) << "Unable to find PDB file for module: "
161 : << input_path.value();
162 i : return false;
163 : }
164 : }
165 :
166 E : if (!file_util::PathExists(*input_pdb_path)) {
167 i : LOG(ERROR) << "Input PDB not found: " << input_pdb_path->value();
168 i : return false;
169 : }
170 :
171 : // If no output PDB path is specified, infer one.
172 E : if (output_pdb_path->empty()) {
173 : // If the input and output DLLs have the same basename, default to writing
174 : // using the same PDB basename, but alongside the new module.
175 E : if (input_path.BaseName() == output_path.BaseName()) {
176 : *output_pdb_path = output_path.DirName().Append(
177 E : input_pdb_path->BaseName());
178 E : } else {
179 : // Otherwise, default to using the output basename with a PDB extension.
180 i : *output_pdb_path = output_path.ReplaceExtension(L"pdb");
181 : }
182 :
183 E : LOG(INFO) << "Using default output PDB path: " << output_pdb_path->value();
184 : }
185 :
186 : // Ensure we aren't about to overwrite anything we don't want to. We do this
187 : // early on so that we abort before decomposition, transformation, etc.
188 E : if (!allow_overwrite) {
189 E : bool terminate = false;
190 E : if (file_util::PathExists(output_path)) {
191 E : terminate = true;
192 E : LOG(ERROR) << "Output module path already exists.";
193 : }
194 E : if (file_util::PathExists(*output_pdb_path)) {
195 i : terminate = true;
196 i : LOG(ERROR) << "Output PDB path already exists.";
197 : }
198 E : if (terminate)
199 E : return false;
200 : }
201 :
202 E : return true;
203 E : }
204 :
205 : // Decomposes the module enclosed by the given PE file.
206 : bool Decompose(bool use_new_decomposer,
207 : bool parse_debug_info,
208 : const PEFile& pe_file,
209 : const FilePath& pdb_path,
210 : ImageLayout* image_layout,
211 E : BlockGraph::Block** dos_header_block) {
212 E : DCHECK(image_layout != NULL);
213 E : DCHECK(dos_header_block != NULL);
214 :
215 E : LOG(INFO) << "Decomposing module: " << pe_file.path().value();
216 :
217 E : BlockGraph* block_graph = image_layout->blocks.graph();
218 E : ImageLayout orig_image_layout(block_graph);
219 :
220 : // Decompose the input image.
221 E : if (use_new_decomposer) {
222 E : LOG(INFO) << "Using new decomposer for decomposition.";
223 E : if (!parse_debug_info)
224 E : LOG(INFO) << "Not parsing debug information.";
225 E : NewDecomposer decomposer(pe_file);
226 E : decomposer.set_pdb_path(pdb_path);
227 E : decomposer.set_parse_debug_info(parse_debug_info);
228 E : if (!decomposer.Decompose(&orig_image_layout)) {
229 i : LOG(ERROR) << "Unable to decompose module: " << pe_file.path().value();
230 i : return false;
231 : }
232 E : } else {
233 E : Decomposer decomposer(pe_file);
234 E : decomposer.set_pdb_path(pdb_path);
235 E : if (!decomposer.Decompose(&orig_image_layout)) {
236 i : LOG(ERROR) << "Unable to decompose module: " << pe_file.path().value();
237 i : return false;
238 : }
239 E : }
240 :
241 : // Make a copy of the image layout without padding. We don't want to carry
242 : // the padding through the toolchain.
243 E : LOG(INFO) << "Removing padding blocks.";
244 E : if (!pe::CopyImageLayoutWithoutPadding(orig_image_layout, image_layout)) {
245 i : LOG(ERROR) << "Failed to remove padding blocks.";
246 i : return false;
247 : }
248 :
249 : // Get the DOS header block.
250 : *dos_header_block =
251 : image_layout->blocks.GetBlockByAddress(
252 E : BlockGraph::RelativeAddress(0));
253 E : if (*dos_header_block == NULL) {
254 i : LOG(ERROR) << "Unable to find the DOS header block.";
255 i : return false;
256 : }
257 :
258 E : return true;
259 E : }
260 :
261 : bool ApplyTransforms(const FilePath& input_path,
262 : const FilePath& output_pdb_path,
263 : const GUID& guid,
264 : bool add_metadata,
265 : std::vector<Transform*>* transforms,
266 : BlockGraph* block_graph,
267 E : BlockGraph::Block* dos_header_block) {
268 E : DCHECK(transforms != NULL);
269 E : DCHECK(block_graph != NULL);
270 E : DCHECK(dos_header_block != NULL);
271 :
272 E : LOG(INFO) << "Transforming block graph.";
273 :
274 E : std::vector<Transform*> local_transforms(*transforms);
275 :
276 E : pe::transforms::AddMetadataTransform add_metadata_tx(input_path);
277 E : pe::transforms::AddPdbInfoTransform add_pdb_info_tx(output_pdb_path, 1, guid);
278 E : pe::transforms::PrepareHeadersTransform prep_headers_tx;
279 :
280 : // We first run the sequence of user requested transforms.
281 :
282 : // If we've been requested to we add metadata to the image.
283 E : if (add_metadata)
284 E : local_transforms.push_back(&add_metadata_tx);
285 :
286 : // Update the PDB information to point to the correct PDB file.
287 E : local_transforms.push_back(&add_pdb_info_tx);
288 :
289 : // Finally, run the prepare headers transform. This ensures that the header
290 : // block is properly sized to receive layout information post-ordering.
291 E : local_transforms.push_back(&prep_headers_tx);
292 :
293 : // Apply the transforms.
294 E : for (size_t i = 0; i < local_transforms.size(); ++i) {
295 E : LOG(INFO) << "Applying transform: " << local_transforms[i]->name() << ".";
296 : // ApplyBlockGraphTransform takes care of verbosely logging any failures.
297 : if (!ApplyBlockGraphTransform(local_transforms[i],
298 : block_graph,
299 E : dos_header_block)) {
300 E : return false;
301 : }
302 E : }
303 :
304 E : return true;
305 E : }
306 :
307 : bool ApplyOrderers(std::vector<Orderer*>* orderers,
308 : OrderedBlockGraph* obg,
309 E : BlockGraph::Block* dos_header_block) {
310 E : DCHECK(orderers != NULL);
311 E : DCHECK(obg != NULL);
312 E : DCHECK(dos_header_block != NULL);
313 :
314 E : LOG(INFO) << "Ordering block graph.";
315 :
316 E : std::vector<Orderer*> local_orderers(*orderers);
317 :
318 E : block_graph::orderers::OriginalOrderer orig_orderer;
319 E : pe::orderers::PEOrderer pe_orderer;
320 :
321 E : if (local_orderers.size() == 0) {
322 E : LOG(INFO) << "No orderers specified, using original orderer.";
323 E : local_orderers.push_back(&orig_orderer);
324 : }
325 :
326 E : local_orderers.push_back(&pe_orderer);
327 :
328 : // Apply the orderers.
329 E : for (size_t i = 0; i < local_orderers.size(); ++i) {
330 E : LOG(INFO) << "Applying orderer: " << local_orderers[i]->name();
331 E : if (!ApplyOrderer(local_orderers[i], obg, dos_header_block))
332 E : return false;
333 E : }
334 :
335 E : return true;
336 E : }
337 :
338 : bool ApplyPdbMutators(const std::vector<PdbMutatorInterface*>& pdb_mutators,
339 E : PdbFile* pdb_file) {
340 E : DCHECK(pdb_file != NULL);
341 :
342 E : LOG(INFO) << "Mutating PDB.";
343 :
344 : // Apply the orderers.
345 E : for (size_t i = 0; i < pdb_mutators.size(); ++i) {
346 E : LOG(INFO) << "Applying PDB mutator: " << pdb_mutators[i]->name();
347 E : if (!ApplyPdbMutator(pdb_mutators[i], pdb_file))
348 E : return false;
349 E : }
350 :
351 E : return true;
352 E : }
353 :
354 : // Lays out the image.
355 : bool BuildImageLayout(size_t padding,
356 : const OrderedBlockGraph& ordered_block_graph,
357 : BlockGraph::Block* dos_header_block,
358 E : ImageLayout* image_layout) {
359 E : DCHECK(dos_header_block != NULL);
360 E : DCHECK(image_layout != NULL);
361 :
362 E : LOG(INFO) << "Building image layout.";
363 :
364 E : ImageLayoutBuilder builder(image_layout);
365 E : builder.set_padding(padding);
366 E : if (!builder.LayoutImageHeaders(dos_header_block)) {
367 i : LOG(ERROR) << "ImageLayoutBuilder::LayoutImageHeaders failed.";
368 i : return false;
369 : }
370 :
371 E : if (!builder.LayoutOrderedBlockGraph(ordered_block_graph)) {
372 i : LOG(ERROR) << "ImageLayoutBuilder::LayoutOrderedBlockGraph failed.";
373 i : return false;
374 : }
375 :
376 E : LOG(INFO) << "Finalizing image layout.";
377 E : if (!builder.Finalize()) {
378 i : LOG(ERROR) << "ImageLayoutBuilder::Finalize failed.";
379 i : return false;
380 : }
381 :
382 E : return true;
383 E : }
384 :
385 : // Writes the image.
386 E : bool WriteImage(const ImageLayout& image_layout, const FilePath& output_path) {
387 E : PEFileWriter writer(image_layout);
388 :
389 E : LOG(INFO) << "Writing image: " << output_path.value();
390 E : if (!writer.WriteImage(output_path)) {
391 i : LOG(ERROR) << "Failed to write image \"" << output_path.value() << "\".";
392 i : return false;
393 : }
394 :
395 E : return true;
396 E : }
397 :
398 : void BuildOmapVectors(const RelativeAddressRange& input_range,
399 : const ImageLayout& output_image_layout,
400 : std::vector<OMAP>* omap_to,
401 E : std::vector<OMAP>* omap_from) {
402 E : DCHECK(omap_to != NULL);
403 E : DCHECK(omap_from != NULL);
404 :
405 E : LOG(INFO) << "Building OMAP vectors.";
406 :
407 : // Get the range of the output image, sans headers. This is required for
408 : // generating OMAP information.
409 E : RelativeAddressRange output_range;
410 E : GetOmapRange(output_image_layout.sections, &output_range);
411 :
412 E : ImageSourceMap reverse_map;
413 E : BuildImageSourceMap(output_image_layout, &reverse_map);
414 :
415 E : ImageSourceMap forward_map;
416 E : if (reverse_map.ComputeInverse(&forward_map) != 0) {
417 E : LOG(WARNING) << "OMAPFROM not unique (there exist repeated source ranges).";
418 : }
419 :
420 : // Build the two OMAP vectors.
421 E : BuildOmapVectorFromImageSourceMap(output_range, reverse_map, omap_to);
422 E : BuildOmapVectorFromImageSourceMap(input_range, forward_map, omap_from);
423 E : }
424 :
425 : // Updates the OMAP and GUID info in the given PDB file.
426 : bool SetOmapAndGuid(const RelativeAddressRange input_range,
427 : const ImageLayout& image_layout,
428 : const GUID& guid,
429 E : PdbFile* pdb_file) {
430 E : DCHECK(pdb_file != NULL);
431 :
432 E : LOG(INFO) << "Updating OMAP and GUID information.";
433 :
434 E : std::vector<OMAP> omap_to, omap_from;
435 E : BuildOmapVectors(input_range, image_layout, &omap_to, &omap_from);
436 :
437 E : if (!pdb::SetGuid(guid, pdb_file)) {
438 i : LOG(ERROR) << "Unable to set PDB GUID.";
439 i : return false;
440 : }
441 :
442 E : if (!pdb::SetOmapToStream(omap_to, pdb_file)) {
443 i : LOG(ERROR) << "Unable to set OMAP_TO.";
444 i : return false;
445 : }
446 :
447 E : if (!pdb::SetOmapFromStream(omap_from, pdb_file)) {
448 i : LOG(ERROR) << "Unable to set OMAP_FROM.";
449 i : return false;
450 : }
451 :
452 E : return true;
453 E : }
454 :
455 E : bool WritePdbFile(const FilePath& output_pdb_path, const PdbFile& pdb_file) {
456 E : LOG(INFO) << "Writing PDB file: " << output_pdb_path.value();
457 :
458 E : FilePath temp_pdb;
459 : if (!file_util::CreateTemporaryFileInDir(output_pdb_path.DirName(),
460 E : &temp_pdb)) {
461 i : LOG(ERROR) << "Unable to create temporary PDB file.";
462 i : return false;
463 : }
464 :
465 E : pdb::PdbWriter pdb_writer;
466 E : if (!pdb_writer.Write(temp_pdb, pdb_file)) {
467 i : LOG(ERROR) << "Failed to write temporary PDB file to \""
468 : << temp_pdb.value() << "\".";
469 : }
470 :
471 E : if (!file_util::ReplaceFile(temp_pdb, output_pdb_path)) {
472 i : LOG(ERROR) << "Unable to move temporary PDB file to \""
473 : << output_pdb_path.value() << "\".";
474 i : file_util::Delete(temp_pdb, false);
475 i : return false;
476 : }
477 :
478 E : return true;
479 E : }
480 :
481 : // Get a specific named stream if it already exists, otherwise create one.
482 : // @param stream_name The name of the stream.
483 : // @param name_stream_map The map containing the names of the streams in the
484 : // PDB. If the stream doesn't already exist the map will be augmented with
485 : // another entry.
486 : // @param pdb_file The PDB file to which the stream will be added.
487 : // @param replace_stream If true, will cause a new stream to be created even if
488 : // another one already existed.
489 : // @return a pointer to the PDB stream on success, NULL on failure.
490 : PdbStream* GetOrCreatePdbStreamByName(const char* stream_name,
491 : bool replace_stream,
492 : NameStreamMap* name_stream_map,
493 E : PdbFile* pdb_file) {
494 E : DCHECK(name_stream_map != NULL);
495 E : DCHECK(pdb_file != NULL);
496 E : scoped_refptr<PdbStream> stream;
497 :
498 E : NameStreamMap::const_iterator name_it = name_stream_map->find(stream_name);
499 E : if (name_it != name_stream_map->end()) {
500 : // Replace the existing stream by a brand-new one if it's required.
501 i : if (replace_stream) {
502 i : stream = new PdbByteStream();
503 i : pdb_file->ReplaceStream(name_it->second, stream.get());
504 i : } else {
505 i : if (!pdb::EnsureStreamWritable(name_it->second, pdb_file)) {
506 i : LOG(ERROR) << "Failed to make " << stream_name << " stream writable.";
507 i : return NULL;
508 : }
509 i : stream = pdb_file->GetStream(name_it->second);
510 : }
511 i : } else {
512 E : stream = new PdbByteStream();
513 E : uint32 index = pdb_file->AppendStream(stream.get());
514 E : (*name_stream_map)[stream_name] = index;
515 : }
516 :
517 E : return stream.get();
518 E : }
519 :
520 : // This updates or creates the Syzygy history stream, appending the metadata
521 : // describing this module and transform. The history stream consists of
522 : // a named PDB stream with the name /Syzygy/History. It consists of:
523 : //
524 : // uint32 version
525 : // uint32 history_length
526 : // serialized pe::Metadata 0
527 : // ...
528 : // serialized pe::Metadata history_length - 1
529 : //
530 : // If the format is changed, be sure to update this documentation and
531 : // pdb::kSyzygyHistoryStreamVersion (in pdb_constants.h).
532 : bool WriteSyzygyHistoryStream(const FilePath& input_path,
533 : NameStreamMap* name_stream_map,
534 E : PdbFile* pdb_file) {
535 : // Get the history stream.
536 : scoped_refptr<PdbStream> history_reader =
537 : GetOrCreatePdbStreamByName(pdb::kSyzygyHistoryStreamName,
538 : false,
539 : name_stream_map,
540 E : pdb_file);
541 :
542 E : if (history_reader == NULL) {
543 i : LOG(ERROR) << "Failed to get the history stream.";
544 i : return false;
545 : }
546 :
547 : scoped_refptr<WritablePdbStream> history_writer =
548 E : history_reader->GetWritablePdbStream();
549 E : DCHECK(history_writer.get() != NULL);
550 :
551 : // Get the metadata.
552 E : Metadata metadata;
553 E : PEFile pe_file;
554 E : if (!pe_file.Init(input_path)) {
555 i : LOG(ERROR) << "Failed to initialize PE file for \"" << input_path.value()
556 : << "\".";
557 i : return false;
558 : }
559 :
560 E : PEFile::Signature pe_sig;
561 E : pe_file.GetSignature(&pe_sig);
562 E : if (!metadata.Init(pe_sig)) {
563 i : LOG(ERROR) << "Failed to initialize metadata for \"" << input_path.value()
564 : << "\".";
565 i : return false;
566 : }
567 :
568 : // Validate the history stream if it is non-empty.
569 E : if (history_reader->length() > 0) {
570 : // Read the header.
571 i : uint32 version = 0;
572 i : uint32 history_length = 0;
573 : if (!history_reader->Seek(0) ||
574 : !history_reader->Read(&version, 1) ||
575 i : !history_reader->Read(&history_length, 1)) {
576 i : LOG(ERROR) << "Failed to read existing Syzygy history stream header.";
577 i : return false;
578 : }
579 :
580 : // Check the version.
581 i : if (version != pdb::kSyzygyHistoryStreamVersion) {
582 i : LOG(ERROR) << "PDB contains unsupported Syzygy history stream version "
583 : << "(got " << version << ", expected "
584 : << pdb::kSyzygyHistoryStreamVersion << ").";
585 i : return false;
586 : }
587 :
588 : // Increment the history length and rewrite it.
589 i : history_length++;
590 i : history_writer->set_pos(sizeof(pdb::kSyzygyHistoryStreamVersion));
591 i : if (!history_writer->Write(history_length)) {
592 i : LOG(ERROR) << "Failed to write new Syzygy history stream length.";
593 i : return false;
594 : }
595 i : } else {
596 : // If there wasn't already a history stream, create one and write the
597 : // header.
598 E : DCHECK_EQ(0u, history_writer->pos());
599 E : const uint32 kHistoryLength = 1;
600 : if (!history_writer->Write(pdb::kSyzygyHistoryStreamVersion) ||
601 E : !history_writer->Write(kHistoryLength)) {
602 i : LOG(ERROR) << "Failed to write Syzygy history stream header.";
603 i : return false;
604 : }
605 : }
606 :
607 : // Append the metadata to the history.
608 E : history_writer->set_pos(history_writer->length());
609 E : PdbOutStream out_stream(history_writer.get());
610 E : core::OutArchive out_archive(&out_stream);
611 E : if (!out_archive.Save(metadata)) {
612 i : LOG(ERROR) << "Failed to write metadata to Syzygy history stream.";
613 i : return false;
614 : }
615 :
616 E : return true;
617 E : }
618 :
619 : // This writes the serialized block-graph and the image layout in a PDB stream
620 : // named /Syzygy/BlockGraph. If the format is changed, be sure to update this
621 : // documentation and pdb::kSyzygyBlockGraphStreamVersion (in pdb_constants.h).
622 : // The block graph stream will not include the data from the blocks of the
623 : // block-graph. If the strip-strings flag is set to true the strings contained
624 : // in the block-graph won't be saved.
625 : bool WriteSyzygyBlockGraphStream(const PEFile& pe_file,
626 : const ImageLayout& image_layout,
627 : bool strip_strings,
628 : bool compress,
629 : NameStreamMap* name_stream_map,
630 E : PdbFile* pdb_file) {
631 : // Get the redecomposition data stream.
632 : scoped_refptr<PdbStream> block_graph_reader =
633 : GetOrCreatePdbStreamByName(pdb::kSyzygyBlockGraphStreamName,
634 : true,
635 : name_stream_map,
636 E : pdb_file);
637 :
638 E : if (block_graph_reader == NULL) {
639 i : LOG(ERROR) << "Failed to get the block-graph stream.";
640 i : return false;
641 : }
642 E : DCHECK_EQ(0u, block_graph_reader->length());
643 :
644 : scoped_refptr<WritablePdbStream> block_graph_writer =
645 E : block_graph_reader->GetWritablePdbStream();
646 E : DCHECK(block_graph_writer.get() != NULL);
647 :
648 : // Write the version of the BlockGraph stream, and whether or not its
649 : // contents are compressed.
650 : if (!block_graph_writer->Write(pdb::kSyzygyBlockGraphStreamVersion) ||
651 E : !block_graph_writer->Write(static_cast<unsigned char>(compress))) {
652 i : LOG(ERROR) << "Failed to write Syzygy BlockGraph stream header.";
653 i : return false;
654 : }
655 :
656 : // Set up the output stream.
657 E : PdbOutStream pdb_out_stream(block_graph_writer.get());
658 E : core::OutStream* out_stream = &pdb_out_stream;
659 :
660 : // If requested, compress the output.
661 E : scoped_ptr<core::ZOutStream> zip_stream;
662 E : if (compress) {
663 E : zip_stream.reset(new core::ZOutStream(&pdb_out_stream));
664 E : out_stream = zip_stream.get();
665 E : if (!zip_stream->Init(core::ZOutStream::kZBestCompression)) {
666 i : LOG(ERROR) << "Failed to initialize zlib compressor.";
667 i : return false;
668 : }
669 : }
670 :
671 E : core::OutArchive out_archive(out_stream);
672 :
673 : // Set up the serialization properties.
674 E : block_graph::BlockGraphSerializer::Attributes attributes = 0;
675 E : if (strip_strings)
676 E : attributes |= block_graph::BlockGraphSerializer::OMIT_STRINGS;
677 :
678 : // And finally, perform the serialization.
679 : if (!SaveBlockGraphAndImageLayout(pe_file, attributes, image_layout,
680 E : &out_archive)) {
681 i : LOG(ERROR) << "SaveBlockGraphAndImageLayout failed.";
682 i : return false;
683 : }
684 :
685 : // We have to flush the stream in case it's a zstream.
686 E : out_stream->Flush();
687 :
688 E : return true;
689 E : }
690 :
691 : } // namespace
692 :
693 : PERelinker::PERelinker()
694 : : add_metadata_(true), allow_overwrite_(false), augment_pdb_(true),
695 : compress_pdb_(false), parse_debug_info_(true), strip_strings_(false),
696 : use_new_decomposer_(false), padding_(0), inited_(false),
697 : input_image_layout_(&block_graph_), dos_header_block_(NULL),
698 E : output_guid_(GUID_NULL) {
699 E : }
700 :
701 E : void PERelinker::AppendTransform(Transform* transform) {
702 E : DCHECK(transform != NULL);
703 E : transforms_.push_back(transform);
704 E : }
705 :
706 E : void PERelinker::AppendTransforms(const std::vector<Transform*>& transforms) {
707 E : transforms_.insert(transforms_.end(), transforms.begin(), transforms.end());
708 E : }
709 :
710 E : void PERelinker::AppendOrderer(Orderer* orderer) {
711 E : DCHECK(orderer != NULL);
712 E : orderers_.push_back(orderer);
713 E : }
714 :
715 E : void PERelinker::AppendOrderers(const std::vector<Orderer*>& orderers) {
716 E : orderers_.insert(orderers_.end(), orderers.begin(), orderers.end());
717 E : }
718 :
719 E : void PERelinker::AppendPdbMutator(PdbMutatorInterface* pdb_mutator) {
720 E : DCHECK(pdb_mutator != NULL);
721 E : pdb_mutators_.push_back(pdb_mutator);
722 E : }
723 :
724 : void PERelinker::AppendPdbMutators(
725 E : const std::vector<PdbMutatorInterface*>& pdb_mutators) {
726 : pdb_mutators_.insert(pdb_mutators_.end(),
727 : pdb_mutators.begin(),
728 E : pdb_mutators.end());
729 E : }
730 :
731 E : bool PERelinker::Init() {
732 E : DCHECK(inited_ == false);
733 :
734 : // Initialize the paths.
735 : if (!InitializePaths(input_path_, output_path_, allow_overwrite_,
736 E : &input_pdb_path_, &output_pdb_path_)) {
737 E : return false;
738 : }
739 :
740 E : LOG(INFO) << "Input module : " << input_path_.value();
741 E : LOG(INFO) << "Input PDB : " << input_pdb_path_.value();
742 E : LOG(INFO) << "Output module: " << output_path_.value();
743 E : LOG(INFO) << "Output PDB : " << output_pdb_path_.value();
744 :
745 : // Open the input PE file.
746 E : if (!input_pe_file_.Init(input_path_)) {
747 i : LOG(ERROR) << "Unable to load \"" << input_path_.value() << "\".";
748 i : return false;
749 : }
750 :
751 : // Generate a GUID for the relinked image's PDB file.
752 E : if (FAILED(::CoCreateGuid(&output_guid_))) {
753 i : LOG(ERROR) << "Failed to create new PDB GUID.";
754 i : return false;
755 : }
756 :
757 : // Decompose the image.
758 : if (!Decompose(use_new_decomposer_, parse_debug_info_,
759 : input_pe_file_, input_pdb_path_,
760 E : &input_image_layout_, &dos_header_block_)) {
761 i : return false;
762 : }
763 :
764 E : inited_ = true;
765 :
766 E : return true;
767 E : }
768 :
769 E : bool PERelinker::Relink() {
770 E : if (!inited_) {
771 i : LOG(ERROR) << "Init has not been successfully called.";
772 i : return false;
773 : }
774 :
775 : // Transform it.
776 : if (!ApplyTransforms(input_path_, output_pdb_path_, output_guid_,
777 : add_metadata_, &transforms_, &block_graph_,
778 E : dos_header_block_)) {
779 E : return false;
780 : }
781 :
782 : // Order it.
783 E : OrderedBlockGraph ordered_block_graph(&block_graph_);
784 E : if (!ApplyOrderers(&orderers_, &ordered_block_graph, dos_header_block_))
785 E : return false;
786 :
787 : // Lay it out.
788 E : ImageLayout output_image_layout(&block_graph_);
789 : if (!BuildImageLayout(padding_, ordered_block_graph, dos_header_block_,
790 E : &output_image_layout)) {
791 i : return false;
792 : }
793 :
794 : // Write the image.
795 E : if (!WriteImage(output_image_layout, output_path_))
796 i : return false;
797 :
798 : // From here on down we are processing the PDB file.
799 :
800 : // Read the PDB file.
801 E : LOG(INFO) << "Reading PDB file: " << input_pdb_path_.value();
802 E : pdb::PdbReader pdb_reader;
803 E : PdbFile pdb_file;
804 E : if (!pdb_reader.Read(input_pdb_path_, &pdb_file)) {
805 i : LOG(ERROR) << "Unable to read PDB file: " << input_pdb_path_.value();
806 i : return false;
807 : }
808 :
809 : // Apply the mutators to the PDB file.
810 E : if (!ApplyPdbMutators(pdb_mutators_, &pdb_file))
811 E : return false;
812 :
813 : // TODO(chrisha): Make the following 3 PDB updates PdbMutatorInterface
814 : // implementations.
815 :
816 : // Update the OMAP and GUID information.
817 E : RelativeAddressRange input_range;
818 E : GetOmapRange(input_image_layout_.sections, &input_range);
819 : if (!SetOmapAndGuid(input_range, output_image_layout, output_guid_,
820 E : &pdb_file)) {
821 i : return false;
822 : }
823 :
824 : // Parse the header and named streams.
825 E : pdb::PdbInfoHeader70 header = {};
826 E : pdb::NameStreamMap name_stream_map;
827 E : if (!pdb::ReadHeaderInfoStream(pdb_file, &header, &name_stream_map))
828 i : return false;
829 :
830 : // Update/create the Syzygy history stream.
831 E : if (!WriteSyzygyHistoryStream(input_path_, &name_stream_map, &pdb_file))
832 i : return false;
833 :
834 : // Add redecomposition data in another stream, only if augment_pdb_ is set.
835 E : if (augment_pdb_) {
836 E : LOG(INFO) << "The block-graph stream is being written to the PDB.";
837 :
838 E : PEFile new_pe_file;
839 E : if (!new_pe_file.Init(output_path_)) {
840 i : LOG(ERROR) << "Failed to read newly written PE file.";
841 i : return false;
842 : }
843 :
844 : if (!WriteSyzygyBlockGraphStream(new_pe_file,
845 : output_image_layout,
846 : strip_strings_,
847 : compress_pdb_,
848 : &name_stream_map,
849 E : &pdb_file)) {
850 i : return false;
851 : }
852 E : }
853 :
854 : // Write the updated name-stream map back to the header info stream.
855 E : if (!pdb::WriteHeaderInfoStream(header, name_stream_map, &pdb_file))
856 i : return false;
857 :
858 : // Stream 0 contains a copy of the previous PDB's directory. This, combined
859 : // with copy-on-write semantics of individual blocks makes the file contain
860 : // its whole edit history. Since we're writing a 'new' PDB file (we reset the
861 : // GUID and age), we have no history so can safely throw away this stream.
862 E : pdb_file.ReplaceStream(0, NULL);
863 :
864 : // Write the PDB file. We use a helper function that first writes it to a
865 : // temporary file and then moves it, enabling overwrites.
866 E : if (!WritePdbFile(output_pdb_path_, pdb_file))
867 i : return false;
868 :
869 E : return true;
870 E : }
871 :
872 : } // namespace pe
|