Coverage for /Syzygy/relink/relink_app.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
89.8%1151280.C++source

Line-by-line coverage:

   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/relink/relink_app.h"
  16    :  
  17    :  #include "base/string_number_conversions.h"
  18    :  #include "syzygy/block_graph/orderers/original_orderer.h"
  19    :  #include "syzygy/block_graph/orderers/random_orderer.h"
  20    :  #include "syzygy/block_graph/transforms/fuzzing_transform.h"
  21    :  #include "syzygy/pe/pe_relinker.h"
  22    :  #include "syzygy/pe/transforms/explode_basic_blocks_transform.h"
  23    :  #include "syzygy/reorder/orderers/explicit_orderer.h"
  24    :  #include "syzygy/reorder/transforms/basic_block_layout_transform.h"
  25    :  
  26    :  namespace relink {
  27    :  
  28    :  namespace {
  29    :  
  30    :  const char kUsageFormatStr[] =
  31    :      "Usage: %ls [options]\n"
  32    :      "  Required Options:\n"
  33    :      "    --input-image=<path>  The input image file to relink.\n"
  34    :      "    --output-image=<path> Output path for the rewritten image file.\n"
  35    :      "  Options:\n"
  36    :      "    --basic-blocks        Reorder at the basic-block level. At present,\n"
  37    :      "                          this is only supported for random reorderings.\n"
  38    :      "    --code-alignment=<integer>\n"
  39    :      "                          Force a minimal alignment for code blocks.\n"
  40    :      "                          Default value is 1.\n"
  41    :      "    --compress-pdb        If --no-augment-pdb is specified, causes the\n"
  42    :      "                          augmented PDB stream to be compressed.\n"
  43    :      "    --exclude-bb-padding  When randomly reordering basic blocks, exclude\n"
  44    :      "                          padding and unreachable code from the relinked\n"
  45    :      "                          output binary.\n"
  46    :      "    --input-pdb=<path>    The PDB file associated with the input DLL.\n"
  47    :      "                          Default is inferred from input-image.\n"
  48    :      "    --no-augment-pdb      Indicates that the relinker should not augment\n"
  49    :      "                          the PDB with roundtrip decomposition info.\n"
  50    :      "    --no-metadata         Prevents the relinker from adding metadata\n"
  51    :      "                          to the output DLL.\n"
  52    :      "    --no-strip-strings    Causes strings to be output in the augmented\n"
  53    :      "                          PDB stream. The default is to omit these to\n"
  54    :      "                          make smaller PDBs.\n"
  55    :      "    --old-decomposer      Use the old decomposer.\n"
  56    :      "    --order-file=<path>   Reorder based on a JSON ordering file.\n"
  57    :      "    --output-pdb=<path>   Output path for the rewritten PDB file.\n"
  58    :      "                          Default is inferred from output-image.\n"
  59    :      "    --overwrite           Allow output files to be overwritten.\n"
  60    :      "    --padding=<integer>   Add bytes of padding between blocks.\n"
  61    :      "    --verbose             Log verbosely.\n"
  62    :      "\n"
  63    :      "  Testing Options:\n"
  64    :      "    --fuzz                Fuzz the binary.\n"
  65    :      "    --seed=<integer>      Randomly reorder based on the given seed.\n"
  66    :      "\n"
  67    :      "  Deprecated Options:\n"
  68    :      "    --input-dll=<path>    Aliased to --input-image.\n"
  69    :      "    --output-dll=<path>   Aliased to --output-image.\n"
  70    :      "\n"
  71    :      "  Notes:\n"
  72    :      "    * The --seed and --order-file options are mutually exclusive\n"
  73    :      "    * If --order-file is specified, --input-image is optional.\n"
  74    :      "    * The --compress-pdb and --no-strip-strings options are only\n"
  75    :      "      effective if --no-augment-pdb is not specified.\n"
  76    :      "    * The --exclude-bb-padding option is only effective if\n"
  77    :      "      --basic-blocks is specified.\n";
  78    :  
  79  E :  bool ParseUInt32(const std::wstring& value_str, uint32* out_value) {
  80  E :    DCHECK(out_value != NULL);
  81    :    unsigned temp;
  82  E :    if (!base::StringToUint(value_str, &temp))
  83  i :      return false;
  84  E :    *out_value = temp;
  85  E :    return true;
  86  E :  }
  87    :  
  88    :  void GuessPdbPath(const base::FilePath& module_path, base::FilePath* pdb_path) {
  89    :    DCHECK(pdb_path != NULL);
  90    :    *pdb_path = module_path.ReplaceExtension(L"pdb");
  91    :  }
  92    :  
  93    :  }  // namespace
  94    :  
  95  E :  bool RelinkApp::ParseCommandLine(const CommandLine* cmd_line) {
  96  E :    if (cmd_line->HasSwitch("help"))
  97  E :      return Usage(cmd_line, "");
  98    :  
  99  E :    input_image_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-image"));
 100  E :    if (cmd_line->HasSwitch("input-dll")) {
 101  E :      if (input_image_path_.empty()) {
 102  E :        LOG(WARNING) << "Using deprecated switch --input-dll.";
 103    :        input_image_path_ =
 104  E :            AbsolutePath(cmd_line->GetSwitchValuePath("input-dll"));
 105  E :      } else {
 106    :        return Usage(cmd_line,
 107  E :                     "Can't specify both --input-dll and --input-image.");
 108    :      }
 109    :    }
 110  E :    input_pdb_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("input-pdb"));
 111    :  
 112  E :    output_image_path_ = cmd_line->GetSwitchValuePath("output-image");
 113  E :    if (cmd_line->HasSwitch("output-dll")) {
 114  E :      if (output_image_path_.empty()) {
 115  E :        LOG(WARNING) << "Using deprecated switch --output-dll.";
 116    :        output_image_path_ =
 117  E :            AbsolutePath(cmd_line->GetSwitchValuePath("output-dll"));
 118  E :      } else {
 119    :        return Usage(cmd_line,
 120  E :                     "Can't specify both --output-dll and --output-image.");
 121    :      }
 122    :    }
 123    :  
 124  E :    output_pdb_path_ = cmd_line->GetSwitchValuePath("output-pdb");
 125  E :    order_file_path_ = AbsolutePath(cmd_line->GetSwitchValuePath("order-file"));
 126  E :    no_augment_pdb_ = cmd_line->HasSwitch("no-augment-pdb");
 127  E :    compress_pdb_ = cmd_line->HasSwitch("compress-pdb");
 128  E :    no_strip_strings_ = cmd_line->HasSwitch("no-strip-strings");
 129  E :    output_metadata_ = !cmd_line->HasSwitch("no-metadata");
 130  E :    overwrite_ = cmd_line->HasSwitch("overwrite");
 131  E :    basic_blocks_ = cmd_line->HasSwitch("basic-blocks");
 132  E :    exclude_bb_padding_ = cmd_line->HasSwitch("exclude-bb-padding");
 133  E :    fuzz_ = cmd_line->HasSwitch("fuzz");
 134  E :    old_decomposer_ = cmd_line->HasSwitch("old-decomposer");
 135    :  
 136    :    // The --output-image argument is required.
 137  E :    if (output_image_path_.empty()) {
 138  E :      return Usage(cmd_line, "You must specify --output-image.");
 139    :    }
 140    :  
 141    :    // Ensure that we have an input-image, either explicitly specified, or to be
 142    :    // taken from an order file.
 143  E :    if (input_image_path_.empty() && order_file_path_.empty()) {
 144    :      return Usage(
 145    :          cmd_line,
 146  E :          "You must specify --input-image if --order-file is not given.");
 147    :    }
 148    :  
 149    :    // Parse the random seed, if given. Note that the --seed and --order-file
 150    :    // arguments are mutually exclusive.
 151  E :    if (cmd_line->HasSwitch("seed")) {
 152  E :      if (cmd_line->HasSwitch("order-file")) {
 153    :        return Usage(cmd_line,
 154  i :                     "The seed and order-file arguments are mutually exclusive.");
 155    :      }
 156  E :      std::wstring seed_str(cmd_line->GetSwitchValueNative("seed"));
 157  E :      if (!ParseUInt32(seed_str, &seed_))
 158  i :        return Usage(cmd_line, "Invalid seed value.");
 159  E :    }
 160    :  
 161    :    // Parse the padding argument.
 162  E :    if (cmd_line->HasSwitch("padding")) {
 163  E :      std::wstring padding_str(cmd_line->GetSwitchValueNative("padding"));
 164  E :      if (!ParseUInt32(padding_str, &padding_))
 165  i :        return Usage(cmd_line, "Invalid padding value.");
 166  E :    }
 167    :  
 168    :    // Parse the code alignment argument.
 169  E :    if (cmd_line->HasSwitch("code-alignment")) {
 170  E :      std::wstring align_str(cmd_line->GetSwitchValueNative("code-alignment"));
 171  E :      if (!ParseUInt32(align_str, &code_alignment_))
 172  i :        return Usage(cmd_line, "Invalid code-alignment value.");
 173  E :      if (code_alignment_ == 0)
 174  i :        return Usage(cmd_line, "Code-alignment value cannot be zero.");
 175  E :    }
 176    :  
 177  E :    return true;
 178  E :  }
 179    :  
 180  E :  bool RelinkApp::SetUp() {
 181  E :    if (input_image_path_.empty()) {
 182  E :      DCHECK(!order_file_path_.empty());
 183    :      if (!reorder::Reorderer::Order::GetOriginalModulePath(order_file_path_,
 184  E :                                                            &input_image_path_)) {
 185  E :        LOG(ERROR) << "Unable to infer input-image.";
 186  E :        return false;
 187    :      }
 188    :  
 189  i :      LOG(INFO) << "Inferring input DLL path from order file: "
 190    :                << input_image_path_.value();
 191    :    }
 192    :  
 193  E :    DCHECK(!input_image_path_.empty());
 194  E :    DCHECK(!output_image_path_.empty());
 195  E :    DCHECK(order_file_path_.empty() || seed_ == 0);
 196    :  
 197  E :    return true;
 198  E :  }
 199    :  
 200  E :  int RelinkApp::Run() {
 201  E :    pe::PETransformPolicy policy;
 202  E :    pe::PERelinker relinker(&policy);
 203  E :    relinker.set_input_path(input_image_path_);
 204  E :    relinker.set_input_pdb_path(input_pdb_path_);
 205  E :    relinker.set_output_path(output_image_path_);
 206  E :    relinker.set_output_pdb_path(output_pdb_path_);
 207  E :    relinker.set_padding(padding_);
 208  E :    relinker.set_code_alignment(code_alignment_);
 209  E :    relinker.set_add_metadata(output_metadata_);
 210  E :    relinker.set_allow_overwrite(overwrite_);
 211  E :    relinker.set_augment_pdb(!no_augment_pdb_);
 212  E :    relinker.set_compress_pdb(compress_pdb_);
 213  E :    relinker.set_strip_strings(!no_strip_strings_);
 214  E :    relinker.set_use_old_decomposer(old_decomposer_);
 215    :  
 216    :    // Initialize the relinker. This does the decomposition, etc.
 217  E :    if (!relinker.Init()) {
 218  i :      LOG(ERROR) << "Failed to initialize relinker.";
 219  i :      return 1;
 220    :    }
 221    :  
 222    :    // Transforms that may be used.
 223  E :    scoped_ptr<pe::transforms::ExplodeBasicBlocksTransform> bb_explode;
 224  E :    scoped_ptr<reorder::transforms::BasicBlockLayoutTransform> bb_layout;
 225  E :    scoped_ptr<block_graph::transforms::FuzzingTransform> fuzzing_transform;
 226    :  
 227    :    // Orderers that may be used.
 228  E :    reorder::Reorderer::Order order;
 229  E :    scoped_ptr<block_graph::orderers::OriginalOrderer> orig_orderer;
 230  E :    scoped_ptr<block_graph::BlockGraphOrdererInterface> orderer;
 231    :  
 232    :    // If fuzzing is enabled, add it to the relinker.
 233  E :    if (fuzz_) {
 234  E :      fuzzing_transform.reset(new block_graph::transforms::FuzzingTransform);
 235  E :      CHECK(relinker.AppendTransform(fuzzing_transform.get()));
 236    :    }
 237    :  
 238    :    // If an order file is provided we are performing an explicit ordering.
 239  E :    if (!order_file_path_.empty()) {
 240    :      if (!order.LoadFromJSON(relinker.input_pe_file(),
 241    :                              relinker.input_image_layout(),
 242  E :                              order_file_path_)) {
 243  i :        LOG(ERROR) << "Failed to load order file: " << order_file_path_.value();
 244  i :        return 1;
 245    :      }
 246    :  
 247    :      // Allocate a BB layout transform. This applies the basic block portion of
 248    :      // the order specification. It will modify it in place so that it is ready
 249    :      // to be used by the ExplicitOrderer to finish the job.
 250  E :      bb_layout.reset(new reorder::transforms::BasicBlockLayoutTransform(&order));
 251  E :      CHECK(relinker.AppendTransform(bb_layout.get()));
 252    :  
 253    :      // Append an OriginalOrderer to the relinker. We do this so that the
 254    :      // original order is preserved entirely for sections that are not
 255    :      // fully specified by the order file, and therefore not ordered by the
 256    :      // ExplicitOrderer.
 257  E :      orig_orderer.reset(new block_graph::orderers::OriginalOrderer());
 258  E :      CHECK(relinker.AppendOrderer(orig_orderer.get()));
 259    :  
 260    :      // Allocate an explicit orderer.
 261  E :      orderer.reset(new reorder::orderers::ExplicitOrderer(&order));
 262  E :    } else {
 263    :      // No order file was provided, so we're doing a random ordering.
 264    :  
 265    :      // If we've been asked to go down to the basic block level, then we want to
 266    :      // use an explode basic blocks transform so that we randomize the entire
 267    :      // image at the BB level.
 268  E :      if (basic_blocks_) {
 269  E :        bb_explode.reset(new pe::transforms::ExplodeBasicBlocksTransform());
 270  E :        bb_explode->set_exclude_padding(exclude_bb_padding_);
 271  E :        CHECK(relinker.AppendTransform(bb_explode.get()));
 272    :      }
 273    :  
 274    :      // Allocate the random block orderer.
 275  E :      orderer.reset(new block_graph::orderers::RandomOrderer(true, seed_));
 276    :    }
 277    :  
 278    :    // Append the orderer to the relinker.
 279  E :    CHECK(relinker.AppendOrderer(orderer.get()));
 280    :  
 281    :    // Perform the actual relink.
 282  E :    if (!relinker.Relink()) {
 283  i :      LOG(ERROR) << "Unable to relink input image.";
 284  i :      return 1;
 285    :    }
 286    :  
 287  E :    return 0;
 288  E :  }
 289    :  
 290    :  bool RelinkApp::Usage(const CommandLine* cmd_line,
 291  E :                        const base::StringPiece& message) const {
 292  E :    if (!message.empty()) {
 293  E :      ::fwrite(message.data(), 1, message.length(), err());
 294  E :      ::fprintf(err(), "\n\n");
 295    :    }
 296    :  
 297    :    ::fprintf(err(),
 298    :              kUsageFormatStr,
 299  E :              cmd_line->GetProgram().BaseName().value().c_str());
 300    :  
 301  E :    return false;
 302  E :  }
 303    :  
 304    :  }  // namespace relink

Coverage information generated Wed Dec 11 11:34:16 2013.