Coverage for /Syzygy/pehacker/pehacker_app.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
74.4%2363170.C++source

Line-by-line coverage:

   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    :  // Defines the PEHackerApp class, which implements the command-line
  16    :  // "pehacker" tool.
  17    :  
  18    :  #include "syzygy/pehacker/pehacker_app.h"
  19    :  
  20    :  #include "base/file_util.h"
  21    :  #include "base/json/json_reader.h"
  22    :  #include "base/strings/string_split.h"
  23    :  #include "base/strings/stringprintf.h"
  24    :  #include "base/strings/utf_string_conversions.h"
  25    :  #include "syzygy/block_graph/orderers/original_orderer.h"
  26    :  #include "syzygy/pdb/pdb_reader.h"
  27    :  #include "syzygy/pdb/pdb_writer.h"
  28    :  #include "syzygy/pe/decomposer.h"
  29    :  #include "syzygy/pe/pe_file_writer.h"
  30    :  #include "syzygy/pe/pe_relinker_util.h"
  31    :  #include "syzygy/pehacker/operation.h"
  32    :  #include "syzygy/pehacker/variables.h"
  33    :  #include "syzygy/pehacker/operations/add_imports_operation.h"
  34    :  #include "syzygy/pehacker/operations/redirect_imports_operation.h"
  35    :  
  36    :  namespace pehacker {
  37    :  
  38    :  namespace {
  39    :  
  40    :  using block_graph::BlockGraph;
  41    :  
  42    :  static const char kUsageFormatStr[] = "Usage: %ls [options]\n"
  43    :      "  Required Options:\n"
  44    :      "    --config-file=<path>  Path to the configuration file to be used.\n"
  45    :      "  Options:\n"
  46    :      "    -Dvar=val             Defines variable 'var' with value 'val'.\n"
  47    :      "                          Variable names defined on the command-line\n"
  48    :      "                          will be normalized to all lowercase. Values\n"
  49    :      "                          will be parsed as JSON.\n"
  50    :      "    --overwrite           Allow output files to be overwritten.\n"
  51    :      "    --verbose             Log verbosely.\n"
  52    :      "\n";
  53    :  
  54    :  // Gets the value under key |name| in |dictionary|, performing variable
  55    :  // expansion using |variables|, and finally converting it to a normalized path
  56    :  // in |path|. If |optional| this will return true if the key doesn't exist
  57    :  // and leave |path| unchanged. Returns true on success, false otherwise.
  58    :  bool GetFilePath(bool optional,
  59    :                   const base::DictionaryValue& dictionary,
  60    :                   const base::DictionaryValue& variables,
  61    :                   const std::string& name,
  62  E :                   base::FilePath* path) {
  63  E :    DCHECK_NE(reinterpret_cast<base::FilePath*>(NULL), path);
  64    :  
  65    :    const base::Value* value;
  66  E :    if (!dictionary.Get(name, &value)) {
  67  E :      if (optional)
  68  E :        return true;
  69    :  
  70  i :      LOG(ERROR) << "Dictionary does not contain key \"" << name << "\".";
  71  i :      return false;
  72    :    }
  73    :  
  74  E :    std::string s;
  75  E :    if (!ConvertVariableToString(*value, &s))
  76  i :      return false;
  77    :  
  78  E :    if (!ExpandVariables(variables, s, &s))
  79  i :      return false;
  80    :  
  81  E :    *path = base::FilePath(base::UTF8ToWide(s)).NormalizePathSeparators();
  82  E :    VLOG(1) << "Parsed \"" << name << "\" as \"" << path->value() << "\".";
  83  E :    return true;
  84  E :  }
  85    :  
  86  E :  void RemovePaddingBlocks(BlockGraph* block_graph) {
  87  E :    DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), block_graph);
  88  E :    BlockGraph::BlockMap::iterator it = block_graph->blocks_mutable().begin();
  89  E :    while (it != block_graph->blocks_mutable().end()) {
  90  E :      BlockGraph::BlockMap::iterator it_next = it;
  91  E :      ++it_next;
  92    :  
  93  E :      BlockGraph::Block* block = &it->second;
  94  E :      if (block->attributes() & BlockGraph::PADDING_BLOCK)
  95  E :        block_graph->RemoveBlock(block);
  96    :  
  97  E :      it = it_next;
  98  E :    }
  99  E :  }
 100    :  
 101    :  }  // namespace
 102    :  
 103  i :  bool PEHackerApp::ImageId::operator<(const ImageId& rhs) const {
 104  i :    if (input_module.value() < rhs.input_module.value())
 105  i :      return true;
 106  i :    if (input_module.value() > rhs.input_module.value())
 107  i :      return false;
 108  i :    return output_module.value() < rhs.output_module.value();
 109  i :  }
 110    :  
 111  E :  bool PEHackerApp::ParseCommandLine(const CommandLine* cmd_line) {
 112  E :    DCHECK_NE(reinterpret_cast<const CommandLine*>(NULL), cmd_line);
 113    :  
 114  E :    if (cmd_line->HasSwitch("help"))
 115  E :      return Usage(cmd_line, "");
 116    :  
 117  E :    if (cmd_line->HasSwitch("verbose")) {
 118  i :      logging::SetMinLogLevel(logging::LOG_VERBOSE);
 119  i :      VLOG(1) << "Parsed --verbose switch.";
 120    :    }
 121    :  
 122    :    config_file_ = cmd_line->GetSwitchValuePath("config-file").
 123  E :        NormalizePathSeparators();
 124  E :    if (config_file_.empty()) {
 125  E :      LOG(ERROR) << "Must specify --config-file!";
 126  E :      return false;
 127    :    }
 128    :  
 129  E :    overwrite_ = cmd_line->HasSwitch("overwrite");
 130  E :    if (overwrite_) {
 131  E :      VLOG(1) << "Parsed --overwrite switch.";
 132    :    }
 133    :  
 134    :    // Set built-in variables.
 135  E :    if (!SetBuiltInVariables())
 136  i :      return false;
 137    :  
 138    :    // Parse any variables defined as arguments.
 139  E :    VLOG(1) << "Parsing command-line variables.";
 140  E :    const CommandLine::SwitchMap& switches = cmd_line->GetSwitches();
 141  E :    CommandLine::SwitchMap::const_iterator it = switches.begin();
 142  E :    for (; it != switches.end(); ++it) {
 143  E :      if (it->first[0] != 'd')
 144  E :        continue;
 145  E :      const std::wstring wname(it->first.begin() + 1, it->first.end());
 146  E :      std::string name = base::WideToUTF8(wname);
 147  E :      std::string value = base::WideToUTF8(it->second);
 148  E :      if (!ParseVariable(name, value, &variables_))
 149  E :        return false;
 150  E :    }
 151    :  
 152  E :    return true;
 153  E :  }
 154    :  
 155  E :  int PEHackerApp::Run() {
 156  E :    if (!LoadAndValidateConfigurationFile())
 157  i :      return 1;
 158    :  
 159  E :    if (!ProcessConfigurationFile(false))
 160  i :      return 1;
 161    :  
 162  E :    if (!WriteImages())
 163  i :      return 1;
 164    :  
 165  E :    return 0;
 166  E :  }
 167    :  
 168    :  bool PEHackerApp::Usage(const CommandLine* cmd_line,
 169  E :                          const base::StringPiece& message) const {
 170  E :    if (!message.empty()) {
 171  i :      ::fwrite(message.data(), 1, message.length(), err());
 172  i :      ::fprintf(err(), "\n\n");
 173    :    }
 174    :  
 175    :    ::fprintf(err(),
 176    :              kUsageFormatStr,
 177  E :              cmd_line->GetProgram().BaseName().value().c_str());
 178    :  
 179  E :    return false;
 180  E :  }
 181    :  
 182  E :  bool PEHackerApp::SetBuiltInVariables() {
 183  E :    VLOG(1) << "Setting built-in variables.";
 184  E :    std::wstring wroot = config_file_.DirName().value();
 185  E :    std::string root = base::WideToUTF8(wroot);
 186  E :    variables_.Set("ROOT", new base::StringValue(root));
 187  E :    return true;
 188  E :  }
 189    :  
 190  E :  bool PEHackerApp::LoadAndValidateConfigurationFile() {
 191    :    // Parse the configuration file.
 192  E :    if (!ParseConfigFile())
 193  E :      return false;
 194    :  
 195    :    // Build the variables dictionary.
 196  E :    if (!UpdateVariablesFromConfig())
 197  i :      return false;
 198    :  
 199    :    // If we're logging verbosely then dump the variables for debugging.
 200  E :    if (logging::LOG_VERBOSE >= logging::GetMinLogLevel()) {
 201  i :      base::DictionaryValue::Iterator it(variables_);
 202  i :      for (; !it.IsAtEnd(); it.Advance()) {
 203  i :        std::string value;
 204  i :        ConvertVariableToJson(it.value(), &value);
 205  i :        VLOG(1) << "Have variable \"" << it.key() << "\" with value "
 206    :                << value << ".";
 207  i :      }
 208  i :    }
 209    :  
 210    :    // Process the configuration in dry-run mode. This doesn't do any work, but
 211    :    // validates that the configuration makes sense and can be run.
 212  E :    if (!ProcessConfigurationFile(true))
 213  E :      return false;
 214    :  
 215  E :    return true;
 216  E :  }
 217    :  
 218  E :  bool PEHackerApp::ParseConfigFile() {
 219  E :    LOG(INFO) << "Loading configuration file \"" << config_file_.value()
 220    :              << "\".";
 221    :  
 222  E :    VLOG(1) << "Reading configuration file from disk.";
 223  E :    std::string json;
 224  E :    if (!base::ReadFileToString(config_file_, &json)) {
 225  E :      LOG(ERROR) << "Unable to read configuration file \""
 226    :                 << config_file_.value() << "\".";
 227  E :      return false;
 228    :    }
 229    :  
 230  E :    VLOG(1) << "Parsing configuration file contents.";
 231  E :    scoped_ptr<base::Value> config;
 232  E :    int error_code = 0;
 233  E :    std::string error_message;
 234    :    config.reset(base::JSONReader::ReadAndReturnError(
 235    :        json,
 236    :        base::JSON_ALLOW_TRAILING_COMMAS,
 237    :        &error_code,
 238  E :        &error_message));
 239  E :    if (config.get() == NULL) {
 240  E :      LOG(ERROR) << "Failed to parse configuration file: "
 241    :                 << error_message << "(" << error_code << ").";
 242  E :      return false;
 243    :    }
 244    :  
 245    :    // Ensure the configuration is a dictionary, and transfer ownership to
 246    :    // config_ if it is.
 247  E :    base::DictionaryValue* dict = NULL;
 248  E :    if (!config->GetAsDictionary(&dict)) {
 249  E :      LOG(ERROR) << "Configuration must be a dictionary.";
 250  E :      return false;
 251    :    }
 252  E :    config_.reset(dict);
 253  E :    config.release();
 254    :  
 255  E :    return true;
 256  E :  }
 257    :  
 258  E :  bool PEHackerApp::UpdateVariablesFromConfig() {
 259  E :    base::Value* value = NULL;
 260  E :    if (!config_->Get("variables", &value))
 261  E :      return true;
 262    :  
 263  E :    base::DictionaryValue* variables = NULL;
 264  E :    if (!value->GetAsDictionary(&variables)) {
 265  i :      LOG(ERROR) << "Expect a dictionary for \"variables\".";
 266  i :      return false;
 267    :    }
 268    :  
 269  E :    VLOG(1) << "Merging configuration variables with command-line variables.";
 270  E :    if (!MergeVariables(*variables, &variables_))
 271  i :      return false;
 272  E :    return true;
 273  E :  }
 274    :  
 275  E :  bool PEHackerApp::ProcessConfigurationFile(bool dry_run) {
 276  E :    if (dry_run) {
 277  E :      VLOG(1) << "Validating configuration file.";
 278    :    }
 279    :  
 280  E :    base::ListValue* targets = NULL;
 281  E :    if (!config_->GetList("targets", &targets)) {
 282  i :      LOG(ERROR) << "Configuration must contain a \"targets\" list.";
 283  i :      return false;
 284    :    }
 285    :  
 286  E :    if (!ProcessTargets(dry_run, targets))
 287  E :      return false;
 288    :  
 289  E :    return true;
 290  E :  }
 291    :  
 292  E :  bool PEHackerApp::ProcessTargets(bool dry_run, base::ListValue* targets) {
 293  E :    DCHECK_NE(reinterpret_cast<base::ListValue*>(NULL), targets);
 294    :  
 295  E :    if (targets->GetSize() == 0) {
 296  i :      LOG(ERROR) << "No targets to process.";
 297  i :      return false;
 298    :    }
 299    :  
 300    :    // Process the targets in order.
 301  E :    for (size_t i = 0; i < targets->GetSize(); ++i) {
 302  E :      base::DictionaryValue* target = NULL;
 303  E :      if (!targets->GetDictionary(i, &target)) {
 304  i :        LOG(ERROR) << "Each target must be a dictionary.";
 305  i :        return false;
 306    :      }
 307    :  
 308  E :      if (!ProcessTarget(dry_run, target))
 309  E :        return false;
 310  E :    }
 311    :  
 312  E :    return true;
 313  E :  }
 314    :  
 315  E :  bool PEHackerApp::ProcessTarget(bool dry_run, base::DictionaryValue* target) {
 316  E :    DCHECK_NE(reinterpret_cast<base::DictionaryValue*>(NULL), target);
 317    :  
 318  E :    base::FilePath input_module;
 319  E :    base::FilePath output_module;
 320  E :    base::FilePath input_pdb;
 321  E :    base::FilePath output_pdb;
 322  E :    bool opt = false;
 323  E :    if (!GetFilePath(opt, *target, variables_, "input_module", &input_module))
 324  i :      return false;
 325  E :    if (!GetFilePath(opt, *target, variables_, "output_module", &output_module))
 326  i :      return false;
 327  E :    opt = true;
 328  E :    if (!GetFilePath(opt, *target, variables_, "input_pdb", &input_pdb))
 329  i :      return false;
 330  E :    if (!GetFilePath(opt, *target, variables_, "output_pdb", &output_pdb))
 331  i :      return false;
 332    :  
 333  E :    base::ListValue* operations = NULL;
 334  E :    if (!target->GetList("operations", &operations)) {
 335  i :      LOG(ERROR) << "Each target must specify an \"operations\" list.";
 336  i :      return false;
 337    :    }
 338    :  
 339    :    // Validate and infer module-related paths.
 340    :    if (!pe::ValidateAndInferPaths(
 341  E :            input_module, output_module, overwrite_, &input_pdb, &output_pdb)) {
 342  E :      return false;
 343    :    }
 344    :  
 345  E :    ImageInfo* image_info = NULL;
 346  E :    if (!dry_run) {
 347    :      // Get the decomposed image.
 348    :      image_info = GetImageInfo(
 349  E :          input_module, output_module, input_pdb, output_pdb);
 350  E :      if (image_info == NULL)
 351  i :        return false;
 352    :    }
 353    :  
 354  E :    VLOG(1) << "Processing operations for module \"" << input_module.value()
 355    :            << "\".";
 356  E :    if (!ProcessOperations(dry_run, operations, image_info))
 357  i :      return false;
 358    :  
 359  E :    return true;
 360  E :  }
 361    :  
 362    :  bool PEHackerApp::ProcessOperations(bool dry_run,
 363    :                                      base::ListValue* operations,
 364  E :                                      ImageInfo* image_info) {
 365  E :    DCHECK_NE(reinterpret_cast<base::ListValue*>(NULL), operations);
 366  E :    if (!dry_run)
 367  E :      DCHECK_NE(reinterpret_cast<ImageInfo*>(NULL), image_info);
 368    :  
 369  E :    for (size_t i = 0; i < operations->GetSize(); ++i) {
 370  E :      base::DictionaryValue* operation = NULL;
 371  E :      if (!operations->GetDictionary(i, &operation)) {
 372  i :        LOG(ERROR) << "Each operation must be a dictionary.";
 373  i :        return false;
 374    :      }
 375    :  
 376  E :      if (!ProcessOperation(dry_run, operation, image_info))
 377  i :        return false;
 378  E :    }
 379    :  
 380  E :    return true;
 381  E :  }
 382    :  
 383    :  bool PEHackerApp::ProcessOperation(bool dry_run,
 384    :                                     base::DictionaryValue* operation,
 385  E :                                     ImageInfo* image_info) {
 386  E :    DCHECK_NE(reinterpret_cast<base::DictionaryValue*>(NULL), operation);
 387  E :    if (!dry_run)
 388  E :      DCHECK_NE(reinterpret_cast<ImageInfo*>(NULL), image_info);
 389    :  
 390  E :    std::string type;
 391  E :    if (!operation->GetString("type", &type)) {
 392  i :      LOG(ERROR) << "Each operation must specify a \"type\".";
 393  i :      return false;
 394    :    }
 395    :  
 396    :    // Dispatch to the appropriate operation implementation.
 397  E :    scoped_ptr<OperationInterface> operation_impl;
 398  E :    if (type == "none") {
 399    :      // The 'none' operation is always defined, and does nothing. This is
 400    :      // mainly there for simple unittesting of configuration files.
 401  E :      return true;
 402  i :    } else if (type == "add_imports") {
 403  i :      operation_impl.reset(new operations::AddImportsOperation());
 404  i :    } else if (type == "redirect_imports") {
 405  i :      operation_impl.reset(new operations::RedirectImportsOperation());
 406  i :    } else {
 407  i :      LOG(ERROR) << "Unrecognized operation type \"" << type << "\".";
 408  i :      return false;
 409    :    }
 410    :  
 411    :    // Initialize the operation.
 412  i :    DCHECK_NE(reinterpret_cast<OperationInterface*>(NULL), operation_impl.get());
 413  i :    if (!operation_impl->Init(&policy_, operation)) {
 414  i :      LOG(ERROR) << "Failed to initialize \"" << operation_impl->name()
 415    :                 << "\".";
 416  i :      return false;
 417    :    }
 418    :  
 419    :    // If not in a dry-run then apply the operation.
 420  i :    if (!dry_run) {
 421  i :      LOG(INFO) << "Applying operation \"" << type << "\" to \""
 422    :                << image_info->input_module.value() << "\".";
 423    :      if (!operation_impl->Apply(&policy_,
 424    :                                 &image_info->block_graph,
 425  i :                                 image_info->header_block)) {
 426  i :        LOG(ERROR) << "Failed to apply \"" << operation_impl->name() << "\".";
 427  i :        return false;
 428    :      }
 429    :    }
 430    :  
 431  i :    return true;
 432  E :  }
 433    :  
 434    :  PEHackerApp::ImageInfo* PEHackerApp::GetImageInfo(
 435    :      const base::FilePath& input_module,
 436    :      const base::FilePath& output_module,
 437    :      const base::FilePath& input_pdb,
 438  E :      const base::FilePath& output_pdb) {
 439  E :    DCHECK(!input_module.empty());
 440  E :    DCHECK(!output_module.empty());
 441  E :    DCHECK(!input_pdb.empty());
 442  E :    DCHECK(!output_pdb.empty());
 443    :  
 444    :    // Return the existing module if it exists.
 445  E :    ImageId image_id = { input_module, output_module };
 446  E :    ImageInfoMap::iterator it = image_info_map_.find(image_id);
 447  E :    if (it != image_info_map_.end())
 448  i :      return it->second;
 449    :  
 450    :    // Initialize a new ImageInfo struct.
 451  E :    scoped_ptr<ImageInfo> image_info(new ImageInfo());
 452  E :    image_info->input_module = input_module;
 453  E :    image_info->output_module = output_module;
 454  E :    image_info->input_pdb = input_pdb;
 455  E :    image_info->output_pdb = output_pdb;
 456  E :    if (!image_info->pe_file.Init(input_module)) {
 457  i :      LOG(ERROR) << "Failed to read image: " << input_module.value();
 458  i :      return NULL;
 459    :    }
 460    :  
 461    :    // Decompose the image.
 462  E :    pe::ImageLayout image_layout(&image_info->block_graph);
 463  E :    pe::Decomposer decomposer(image_info->pe_file);
 464  E :    if (!decomposer.Decompose(&image_layout)) {
 465  i :      LOG(ERROR) << "Failed to decompose image: " << input_module.value();
 466  i :      return NULL;
 467    :    }
 468    :  
 469    :    // Lookup the header block.
 470    :    image_info->header_block = image_layout.blocks.GetBlockByAddress(
 471  E :        BlockGraph::RelativeAddress(0));
 472    :    DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL),
 473  E :              image_info->header_block);
 474    :  
 475    :    // Remove padding blocks. No need to carry these through the pipeline.
 476  E :    VLOG(1) << "Removing padding blocks.";
 477  E :    RemovePaddingBlocks(&image_info->block_graph);
 478    :  
 479    :    // Get the input range to use in generating OMAP information. This is required
 480    :    // when finalizing the PDB.
 481  E :    pe::GetOmapRange(image_layout.sections, &image_info->input_omap_range);
 482    :  
 483    :    // Decomposition was successful. Add it to the map, transfer the image info to
 484    :    // the scoped array and return it.
 485  E :    it = image_info_map_.insert(std::make_pair(image_id, image_info.get())).first;
 486  E :    image_infos_.push_back(image_info.release());
 487  E :    return it->second;
 488  E :  }
 489    :  
 490  E :  bool PEHackerApp::WriteImages() {
 491  E :    ImageInfoMap::iterator it = image_info_map_.begin();
 492  E :    for (; it != image_info_map_.end(); ++it) {
 493  E :      ImageInfo* image_info = it->second;
 494    :  
 495  E :      LOG(INFO) << "Finalizing and writing image \""
 496    :                << image_info->output_module.value() << "\".";
 497    :  
 498    :      // Create a GUID for the output PDB.
 499  E :      GUID pdb_guid = {};
 500  E :      if (FAILED(::CoCreateGuid(&pdb_guid))) {
 501  i :        LOG(ERROR) << "Failed to create new GUID for output PDB.";
 502  i :        return false;
 503    :      }
 504    :  
 505    :      // Finalize the block-graph.
 506  E :      VLOG(1) << "Finalizing the block-graph.";
 507    :      if (!pe::FinalizeBlockGraph(image_info->input_module,
 508    :                                  image_info->output_pdb,
 509    :                                  pdb_guid,
 510    :                                  true,
 511    :                                  &policy_,
 512    :                                  &image_info->block_graph,
 513  E :                                  image_info->header_block)) {
 514  i :        return false;
 515    :      }
 516    :  
 517    :      // Build the ordered block-graph.
 518    :      block_graph::OrderedBlockGraph ordered_block_graph(
 519  E :          &image_info->block_graph);
 520  E :      block_graph::orderers::OriginalOrderer orderer;
 521  E :      VLOG(1) << "Ordering the block-graph.";
 522    :      if (!orderer.OrderBlockGraph(&ordered_block_graph,
 523  E :                                   image_info->header_block)) {
 524  i :        return false;
 525    :      }
 526    :  
 527    :      // Finalize the ordered block-graph.
 528  E :      VLOG(1) << "Finalizing the ordered block-graph.";
 529    :      if (!pe::FinalizeOrderedBlockGraph(&ordered_block_graph,
 530  E :                                         image_info->header_block)) {
 531  i :        return false;
 532    :      }
 533    :  
 534    :      // Build the image layout.
 535  E :      pe::ImageLayout image_layout(&image_info->block_graph);
 536  E :      VLOG(1) << "Building the image layout.";
 537    :      if (!pe::BuildImageLayout(0, 1, ordered_block_graph,
 538  E :                                image_info->header_block, &image_layout)) {
 539  i :        return false;
 540    :      }
 541    :  
 542    :      // Write the image.
 543  E :      pe::PEFileWriter pe_writer(image_layout);
 544  E :      VLOG(1) << "Writing image to disk.";
 545  E :      if (!pe_writer.WriteImage(image_info->output_module))
 546  i :        return false;
 547    :  
 548  E :      LOG(INFO) << "Finalizing and writing PDB file \""
 549    :                << image_info->output_pdb.value() << "\".";
 550    :  
 551    :      // Parse the original PDB.
 552  E :      pdb::PdbFile pdb_file;
 553  E :      pdb::PdbReader pdb_reader;
 554  E :      VLOG(1) << "Reading original PDB.";
 555  E :      if (!pdb_reader.Read(image_info->input_pdb, &pdb_file))
 556  i :        return false;
 557    :  
 558    :      // Finalize the PDB to reflect the transformed image.
 559  E :      VLOG(1) << "Finalizing PDB.";
 560    :      if (!pe::FinalizePdbFile(image_info->input_module,
 561    :                               image_info->output_module,
 562    :                               image_info->input_omap_range,
 563    :                               image_layout,
 564    :                               pdb_guid,
 565    :                               false,
 566    :                               false,
 567    :                               false,
 568  E :                               &pdb_file)) {
 569  i :        return false;
 570    :      }
 571    :  
 572    :      // Write the PDB.
 573  E :      pdb::PdbWriter pdb_writer;
 574  E :      VLOG(1) << "Writing transformed PDB.";
 575  E :      if (!pdb_writer.Write(image_info->output_pdb, pdb_file))
 576  i :        return false;
 577  E :    }
 578    :  
 579  E :    return true;
 580  E :  }
 581    :  
 582    :  }  // namespace pehacker

Coverage information generated Thu Mar 26 16:15:41 2015.