Home | History | Annotate | Download | only in courgette
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <string>
      6 #include <vector>
      7 
      8 #include "base/at_exit.h"
      9 #include "base/basictypes.h"
     10 #include "base/command_line.h"
     11 #include "base/file_util.h"
     12 #include "base/files/file_path.h"
     13 #include "base/logging.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "base/strings/string_util.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "courgette/courgette.h"
     18 #include "courgette/streams.h"
     19 #include "courgette/third_party/bsdiff.h"
     20 
     21 
     22 void PrintHelp() {
     23   fprintf(stderr,
     24     "Usage:\n"
     25     "  courgette -supported <executable_file>\n"
     26     "  courgette -dis <executable_file> <binary_assembly_file>\n"
     27     "  courgette -asm <binary_assembly_file> <executable_file>\n"
     28     "  courgette -disadj <executable_file> <reference> <binary_assembly_file>\n"
     29     "  courgette -gen <v1> <v2> <patch>\n"
     30     "  courgette -apply <v1> <patch> <v2>\n"
     31     "\n");
     32 }
     33 
     34 void UsageProblem(const char* message) {
     35   fprintf(stderr, "%s", message);
     36   fprintf(stderr, "\n");
     37   PrintHelp();
     38   exit(1);
     39 }
     40 
     41 void Problem(const char* format, ...) {
     42   va_list args;
     43   va_start(args, format);
     44   vfprintf(stderr, format, args);
     45   fprintf(stderr, "\n");
     46   va_end(args);
     47   exit(1);
     48 }
     49 
     50 std::string ReadOrFail(const base::FilePath& file_name, const char* kind) {
     51   int64 file_size = 0;
     52   if (!file_util::GetFileSize(file_name, &file_size))
     53     Problem("Can't read %s file.", kind);
     54   std::string buffer;
     55   buffer.reserve(static_cast<size_t>(file_size));
     56   if (!file_util::ReadFileToString(file_name, &buffer))
     57     Problem("Can't read %s file.", kind);
     58   return buffer;
     59 }
     60 
     61 void WriteSinkToFile(const courgette::SinkStream *sink,
     62                      const base::FilePath& output_file) {
     63   int count =
     64       file_util::WriteFile(output_file,
     65                            reinterpret_cast<const char*>(sink->Buffer()),
     66                            static_cast<int>(sink->Length()));
     67   if (count == -1)
     68     Problem("Can't write output.");
     69   if (static_cast<size_t>(count) != sink->Length())
     70     Problem("Incomplete write.");
     71 }
     72 
     73 void Disassemble(const base::FilePath& input_file,
     74                  const base::FilePath& output_file) {
     75   std::string buffer = ReadOrFail(input_file, "input");
     76 
     77   courgette::AssemblyProgram* program = NULL;
     78   const courgette::Status parse_status =
     79       courgette::ParseDetectedExecutable(buffer.c_str(), buffer.length(),
     80                                          &program);
     81 
     82   if (parse_status != courgette::C_OK)
     83     Problem("Can't parse input.");
     84 
     85   courgette::EncodedProgram* encoded = NULL;
     86   const courgette::Status encode_status = Encode(program, &encoded);
     87 
     88   courgette::DeleteAssemblyProgram(program);
     89 
     90   if (encode_status != courgette::C_OK)
     91     Problem("Can't encode program.");
     92 
     93   courgette::SinkStreamSet sinks;
     94 
     95   const courgette::Status write_status =
     96     courgette::WriteEncodedProgram(encoded, &sinks);
     97   if (write_status != courgette::C_OK)
     98     Problem("Can't serialize encoded program.");
     99 
    100   courgette::DeleteEncodedProgram(encoded);
    101 
    102   courgette::SinkStream sink;
    103   if (!sinks.CopyTo(&sink))
    104     Problem("Can't combine serialized encoded program streams.");
    105 
    106   WriteSinkToFile(&sink, output_file);
    107 }
    108 
    109 bool Supported(const base::FilePath& input_file) {
    110   bool result = false;
    111 
    112   std::string buffer = ReadOrFail(input_file, "input");
    113 
    114   courgette::ExecutableType type;
    115   size_t detected_length;
    116 
    117   DetectExecutableType(buffer.c_str(), buffer.length(),
    118                        &type,
    119                        &detected_length);
    120 
    121   // If the detection fails, we just fall back on UNKNOWN
    122   std::string format = "Unsupported";
    123 
    124   switch (type)
    125   {
    126     case courgette::EXE_UNKNOWN:
    127       break;
    128 
    129     case courgette::EXE_WIN_32_X86:
    130       format = "Windows 32 PE";
    131       result = true;
    132       break;
    133 
    134     case courgette::EXE_ELF_32_X86:
    135       format = "ELF 32 X86";
    136       result = true;
    137       break;
    138 
    139     case courgette::EXE_ELF_32_ARM:
    140       format = "ELF 32 ARM";
    141       result = true;
    142       break;
    143   }
    144 
    145   printf("%s Executable\n", format.c_str());
    146   return result;
    147 }
    148 
    149 void DisassembleAndAdjust(const base::FilePath& program_file,
    150                           const base::FilePath& model_file,
    151                           const base::FilePath& output_file) {
    152   std::string program_buffer = ReadOrFail(program_file, "program");
    153   std::string model_buffer = ReadOrFail(model_file, "reference");
    154 
    155   courgette::AssemblyProgram* program = NULL;
    156   const courgette::Status parse_program_status =
    157       courgette::ParseDetectedExecutable(program_buffer.c_str(),
    158                                          program_buffer.length(),
    159                                          &program);
    160   if (parse_program_status != courgette::C_OK)
    161     Problem("Can't parse program input.");
    162 
    163   courgette::AssemblyProgram* model = NULL;
    164   const courgette::Status parse_model_status =
    165       courgette::ParseDetectedExecutable(model_buffer.c_str(),
    166                                          model_buffer.length(),
    167                                          &model);
    168   if (parse_model_status != courgette::C_OK)
    169     Problem("Can't parse model input.");
    170 
    171   const courgette::Status adjust_status = Adjust(*model, program);
    172   if (adjust_status != courgette::C_OK)
    173     Problem("Can't adjust program.");
    174 
    175   courgette::EncodedProgram* encoded = NULL;
    176   const courgette::Status encode_status = Encode(program, &encoded);
    177 
    178   courgette::DeleteAssemblyProgram(program);
    179 
    180   if (encode_status != courgette::C_OK)
    181     Problem("Can't encode program.");
    182 
    183   courgette::SinkStreamSet sinks;
    184 
    185   const courgette::Status write_status =
    186     courgette::WriteEncodedProgram(encoded, &sinks);
    187   if (write_status != courgette::C_OK)
    188     Problem("Can't serialize encoded program.");
    189 
    190   courgette::DeleteEncodedProgram(encoded);
    191 
    192   courgette::SinkStream sink;
    193   if (!sinks.CopyTo(&sink))
    194     Problem("Can't combine serialized encoded program streams.");
    195 
    196   WriteSinkToFile(&sink, output_file);
    197 }
    198 
    199 // Diffs two executable files, write a set of files for the diff, one file per
    200 // stream of the EncodedProgram format.  Each file is the bsdiff between the
    201 // original file's stream and the new file's stream.  This is completely
    202 // uninteresting to users, but it is handy for seeing how much each which
    203 // streams are contributing to the final file size.  Adjustment is optional.
    204 void DisassembleAdjustDiff(const base::FilePath& model_file,
    205                            const base::FilePath& program_file,
    206                            const base::FilePath& output_file_root,
    207                            bool adjust) {
    208   std::string model_buffer = ReadOrFail(model_file, "'old'");
    209   std::string program_buffer = ReadOrFail(program_file, "'new'");
    210 
    211   courgette::AssemblyProgram* model = NULL;
    212   const courgette::Status parse_model_status =
    213       courgette::ParseDetectedExecutable(model_buffer.c_str(),
    214                                          model_buffer.length(),
    215                                          &model);
    216   if (parse_model_status != courgette::C_OK)
    217     Problem("Can't parse model input.");
    218 
    219   courgette::AssemblyProgram* program = NULL;
    220   const courgette::Status parse_program_status =
    221       courgette::ParseDetectedExecutable(program_buffer.c_str(),
    222                                          program_buffer.length(),
    223                                          &program);
    224   if (parse_program_status != courgette::C_OK)
    225     Problem("Can't parse program input.");
    226 
    227   if (adjust) {
    228     const courgette::Status adjust_status = Adjust(*model, program);
    229     if (adjust_status != courgette::C_OK)
    230       Problem("Can't adjust program.");
    231   }
    232 
    233   courgette::EncodedProgram* encoded_program = NULL;
    234   const courgette::Status encode_program_status =
    235       Encode(program, &encoded_program);
    236   courgette::DeleteAssemblyProgram(program);
    237   if (encode_program_status != courgette::C_OK)
    238     Problem("Can't encode program.");
    239 
    240   courgette::EncodedProgram* encoded_model = NULL;
    241   const courgette::Status encode_model_status = Encode(model, &encoded_model);
    242   courgette::DeleteAssemblyProgram(model);
    243   if (encode_model_status != courgette::C_OK)
    244     Problem("Can't encode model.");
    245 
    246   courgette::SinkStreamSet program_sinks;
    247   const courgette::Status write_program_status =
    248     courgette::WriteEncodedProgram(encoded_program, &program_sinks);
    249   if (write_program_status != courgette::C_OK)
    250     Problem("Can't serialize encoded program.");
    251   courgette::DeleteEncodedProgram(encoded_program);
    252 
    253   courgette::SinkStreamSet model_sinks;
    254   const courgette::Status write_model_status =
    255     courgette::WriteEncodedProgram(encoded_model, &model_sinks);
    256   if (write_model_status != courgette::C_OK)
    257     Problem("Can't serialize encoded model.");
    258   courgette::DeleteEncodedProgram(encoded_model);
    259 
    260   courgette::SinkStream empty_sink;
    261   for (int i = 0;  ; ++i) {
    262     courgette::SinkStream* old_stream = model_sinks.stream(i);
    263     courgette::SinkStream* new_stream = program_sinks.stream(i);
    264     if (old_stream == NULL && new_stream == NULL)
    265       break;
    266 
    267     courgette::SourceStream old_source;
    268     courgette::SourceStream new_source;
    269     old_source.Init(old_stream ? *old_stream : empty_sink);
    270     new_source.Init(new_stream ? *new_stream : empty_sink);
    271     courgette::SinkStream patch_stream;
    272     courgette::BSDiffStatus status =
    273         courgette::CreateBinaryPatch(&old_source, &new_source, &patch_stream);
    274     if (status != courgette::OK) Problem("-xxx failed.");
    275 
    276     std::string append = std::string("-") + base::IntToString(i);
    277 
    278     WriteSinkToFile(&patch_stream,
    279                     output_file_root.InsertBeforeExtensionASCII(append));
    280   }
    281 }
    282 
    283 void Assemble(const base::FilePath& input_file,
    284               const base::FilePath& output_file) {
    285   std::string buffer = ReadOrFail(input_file, "input");
    286 
    287   courgette::SourceStreamSet sources;
    288   if (!sources.Init(buffer.c_str(), buffer.length()))
    289     Problem("Bad input file.");
    290 
    291   courgette::EncodedProgram* encoded = NULL;
    292   const courgette::Status read_status = ReadEncodedProgram(&sources, &encoded);
    293   if (read_status != courgette::C_OK)
    294     Problem("Bad encoded program.");
    295 
    296   courgette::SinkStream sink;
    297 
    298   const courgette::Status assemble_status = courgette::Assemble(encoded, &sink);
    299   if (assemble_status != courgette::C_OK)
    300     Problem("Can't assemble.");
    301 
    302   WriteSinkToFile(&sink, output_file);
    303 }
    304 
    305 void GenerateEnsemblePatch(const base::FilePath& old_file,
    306                            const base::FilePath& new_file,
    307                            const base::FilePath& patch_file) {
    308   std::string old_buffer = ReadOrFail(old_file, "'old' input");
    309   std::string new_buffer = ReadOrFail(new_file, "'new' input");
    310 
    311   courgette::SourceStream old_stream;
    312   courgette::SourceStream new_stream;
    313   old_stream.Init(old_buffer);
    314   new_stream.Init(new_buffer);
    315 
    316   courgette::SinkStream patch_stream;
    317   courgette::Status status =
    318       courgette::GenerateEnsemblePatch(&old_stream, &new_stream, &patch_stream);
    319 
    320   if (status != courgette::C_OK) Problem("-gen failed.");
    321 
    322   WriteSinkToFile(&patch_stream, patch_file);
    323 }
    324 
    325 void ApplyEnsemblePatch(const base::FilePath& old_file,
    326                         const base::FilePath& patch_file,
    327                         const base::FilePath& new_file) {
    328   // We do things a little differently here in order to call the same Courgette
    329   // entry point as the installer.  That entry point point takes file names and
    330   // returns an status code but does not output any diagnostics.
    331 
    332   courgette::Status status =
    333       courgette::ApplyEnsemblePatch(old_file.value().c_str(),
    334                                     patch_file.value().c_str(),
    335                                     new_file.value().c_str());
    336 
    337   if (status == courgette::C_OK)
    338     return;
    339 
    340   // Diagnose the error.
    341   switch (status) {
    342     case courgette::C_BAD_ENSEMBLE_MAGIC:
    343       Problem("Not a courgette patch");
    344       break;
    345 
    346     case courgette::C_BAD_ENSEMBLE_VERSION:
    347       Problem("Wrong version patch");
    348       break;
    349 
    350     case courgette::C_BAD_ENSEMBLE_HEADER:
    351       Problem("Corrupt patch");
    352       break;
    353 
    354     case courgette::C_DISASSEMBLY_FAILED:
    355       Problem("Disassembly failed (could be because of memory issues)");
    356       break;
    357 
    358     case courgette::C_STREAM_ERROR:
    359       Problem("Stream error (likely out of memory or disk space)");
    360       break;
    361 
    362     default:
    363       break;
    364   }
    365 
    366   //  If we failed due to a missing input file, this will
    367   // print the message.
    368   std::string old_buffer = ReadOrFail(old_file, "'old' input");
    369   old_buffer.clear();
    370   std::string patch_buffer = ReadOrFail(patch_file, "'patch' input");
    371   patch_buffer.clear();
    372 
    373   // Non-input related errors:
    374   if (status == courgette::C_WRITE_OPEN_ERROR)
    375     Problem("Can't open output");
    376   if (status == courgette::C_WRITE_ERROR)
    377     Problem("Can't write output");
    378 
    379   Problem("-apply failed.");
    380 }
    381 
    382 void GenerateBSDiffPatch(const base::FilePath& old_file,
    383                          const base::FilePath& new_file,
    384                          const base::FilePath& patch_file) {
    385   std::string old_buffer = ReadOrFail(old_file, "'old' input");
    386   std::string new_buffer = ReadOrFail(new_file, "'new' input");
    387 
    388   courgette::SourceStream old_stream;
    389   courgette::SourceStream new_stream;
    390   old_stream.Init(old_buffer);
    391   new_stream.Init(new_buffer);
    392 
    393   courgette::SinkStream patch_stream;
    394   courgette::BSDiffStatus status =
    395       courgette::CreateBinaryPatch(&old_stream, &new_stream, &patch_stream);
    396 
    397   if (status != courgette::OK) Problem("-genbsdiff failed.");
    398 
    399   WriteSinkToFile(&patch_stream, patch_file);
    400 }
    401 
    402 void ApplyBSDiffPatch(const base::FilePath& old_file,
    403                       const base::FilePath& patch_file,
    404                       const base::FilePath& new_file) {
    405   std::string old_buffer = ReadOrFail(old_file, "'old' input");
    406   std::string patch_buffer = ReadOrFail(patch_file, "'patch' input");
    407 
    408   courgette::SourceStream old_stream;
    409   courgette::SourceStream patch_stream;
    410   old_stream.Init(old_buffer);
    411   patch_stream.Init(patch_buffer);
    412 
    413   courgette::SinkStream new_stream;
    414   courgette::BSDiffStatus status =
    415       courgette::ApplyBinaryPatch(&old_stream, &patch_stream, &new_stream);
    416 
    417   if (status != courgette::OK) Problem("-applybsdiff failed.");
    418 
    419   WriteSinkToFile(&new_stream, new_file);
    420 }
    421 
    422 int main(int argc, const char* argv[]) {
    423   base::AtExitManager at_exit_manager;
    424   CommandLine::Init(argc, argv);
    425   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
    426 
    427   logging::LoggingSettings settings;
    428   settings.logging_dest = logging::LOG_TO_ALL;
    429   settings.log_file = FILE_PATH_LITERAL("courgette.log");
    430   (void)logging::InitLogging(settings);
    431   logging::SetMinLogLevel(logging::LOG_VERBOSE);
    432 
    433   bool cmd_sup = command_line.HasSwitch("supported");
    434   bool cmd_dis = command_line.HasSwitch("dis");
    435   bool cmd_asm = command_line.HasSwitch("asm");
    436   bool cmd_disadj = command_line.HasSwitch("disadj");
    437   bool cmd_make_patch = command_line.HasSwitch("gen");
    438   bool cmd_apply_patch = command_line.HasSwitch("apply");
    439   bool cmd_make_bsdiff_patch = command_line.HasSwitch("genbsdiff");
    440   bool cmd_apply_bsdiff_patch = command_line.HasSwitch("applybsdiff");
    441   bool cmd_spread_1_adjusted = command_line.HasSwitch("gen1a");
    442   bool cmd_spread_1_unadjusted = command_line.HasSwitch("gen1u");
    443 
    444   std::vector<base::FilePath> values;
    445   const CommandLine::StringVector& args = command_line.GetArgs();
    446   for (size_t i = 0; i < args.size(); ++i) {
    447     values.push_back(base::FilePath(args[i]));
    448   }
    449 
    450   // '-repeat=N' is for debugging.  Running many iterations can reveal leaks and
    451   // bugs in cleanup.
    452   int repeat_count = 1;
    453   std::string repeat_switch = command_line.GetSwitchValueASCII("repeat");
    454   if (!repeat_switch.empty())
    455     if (!base::StringToInt(repeat_switch, &repeat_count))
    456       repeat_count = 1;
    457 
    458   if (cmd_sup + cmd_dis + cmd_asm + cmd_disadj + cmd_make_patch +
    459       cmd_apply_patch + cmd_make_bsdiff_patch + cmd_apply_bsdiff_patch +
    460       cmd_spread_1_adjusted + cmd_spread_1_unadjusted
    461       != 1)
    462     UsageProblem(
    463         "Must have exactly one of:\n"
    464         "  -supported -asm, -dis, -disadj, -gen or -apply, -genbsdiff"
    465         " or -applybsdiff.");
    466 
    467   while (repeat_count-- > 0) {
    468     if (cmd_sup) {
    469       if (values.size() != 1)
    470         UsageProblem("-supported <executable_file>");
    471       return !Supported(values[0]);
    472     } else if (cmd_dis) {
    473         if (values.size() != 2)
    474           UsageProblem("-dis <executable_file> <courgette_file>");
    475         Disassemble(values[0], values[1]);
    476     } else if (cmd_asm) {
    477       if (values.size() != 2)
    478         UsageProblem("-asm <courgette_file_input> <executable_file_output>");
    479       Assemble(values[0], values[1]);
    480     } else if (cmd_disadj) {
    481       if (values.size() != 3)
    482         UsageProblem("-disadj <executable_file> <model> <courgette_file>");
    483       DisassembleAndAdjust(values[0], values[1], values[2]);
    484     } else if (cmd_make_patch) {
    485       if (values.size() != 3)
    486         UsageProblem("-gen <old_file> <new_file> <patch_file>");
    487       GenerateEnsemblePatch(values[0], values[1], values[2]);
    488     } else if (cmd_apply_patch) {
    489       if (values.size() != 3)
    490         UsageProblem("-apply <old_file> <patch_file> <new_file>");
    491       ApplyEnsemblePatch(values[0], values[1], values[2]);
    492     } else if (cmd_make_bsdiff_patch) {
    493       if (values.size() != 3)
    494         UsageProblem("-genbsdiff <old_file> <new_file> <patch_file>");
    495       GenerateBSDiffPatch(values[0], values[1], values[2]);
    496     } else if (cmd_apply_bsdiff_patch) {
    497       if (values.size() != 3)
    498         UsageProblem("-applybsdiff <old_file> <patch_file> <new_file>");
    499       ApplyBSDiffPatch(values[0], values[1], values[2]);
    500     } else if (cmd_spread_1_adjusted || cmd_spread_1_unadjusted) {
    501       if (values.size() != 3)
    502         UsageProblem("-gen1[au] <old_file> <new_file> <patch_files_root>");
    503       DisassembleAdjustDiff(values[0], values[1], values[2],
    504                             cmd_spread_1_adjusted);
    505     } else {
    506       UsageProblem("No operation specified");
    507     }
    508   }
    509 
    510   return 0;
    511 }
    512