- add sources.
[platform/framework/web/crosswalk.git] / src / courgette / courgette_tool.cc
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 (!base::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     case courgette::EXE_WIN_32_X64:
145       format = "Windows 64 PE";
146       result = true;
147       break;
148   }
149
150   printf("%s Executable\n", format.c_str());
151   return result;
152 }
153
154 void DisassembleAndAdjust(const base::FilePath& program_file,
155                           const base::FilePath& model_file,
156                           const base::FilePath& output_file) {
157   std::string program_buffer = ReadOrFail(program_file, "program");
158   std::string model_buffer = ReadOrFail(model_file, "reference");
159
160   courgette::AssemblyProgram* program = NULL;
161   const courgette::Status parse_program_status =
162       courgette::ParseDetectedExecutable(program_buffer.c_str(),
163                                          program_buffer.length(),
164                                          &program);
165   if (parse_program_status != courgette::C_OK)
166     Problem("Can't parse program input.");
167
168   courgette::AssemblyProgram* model = NULL;
169   const courgette::Status parse_model_status =
170       courgette::ParseDetectedExecutable(model_buffer.c_str(),
171                                          model_buffer.length(),
172                                          &model);
173   if (parse_model_status != courgette::C_OK)
174     Problem("Can't parse model input.");
175
176   const courgette::Status adjust_status = Adjust(*model, program);
177   if (adjust_status != courgette::C_OK)
178     Problem("Can't adjust program.");
179
180   courgette::EncodedProgram* encoded = NULL;
181   const courgette::Status encode_status = Encode(program, &encoded);
182
183   courgette::DeleteAssemblyProgram(program);
184
185   if (encode_status != courgette::C_OK)
186     Problem("Can't encode program.");
187
188   courgette::SinkStreamSet sinks;
189
190   const courgette::Status write_status =
191     courgette::WriteEncodedProgram(encoded, &sinks);
192   if (write_status != courgette::C_OK)
193     Problem("Can't serialize encoded program.");
194
195   courgette::DeleteEncodedProgram(encoded);
196
197   courgette::SinkStream sink;
198   if (!sinks.CopyTo(&sink))
199     Problem("Can't combine serialized encoded program streams.");
200
201   WriteSinkToFile(&sink, output_file);
202 }
203
204 // Diffs two executable files, write a set of files for the diff, one file per
205 // stream of the EncodedProgram format.  Each file is the bsdiff between the
206 // original file's stream and the new file's stream.  This is completely
207 // uninteresting to users, but it is handy for seeing how much each which
208 // streams are contributing to the final file size.  Adjustment is optional.
209 void DisassembleAdjustDiff(const base::FilePath& model_file,
210                            const base::FilePath& program_file,
211                            const base::FilePath& output_file_root,
212                            bool adjust) {
213   std::string model_buffer = ReadOrFail(model_file, "'old'");
214   std::string program_buffer = ReadOrFail(program_file, "'new'");
215
216   courgette::AssemblyProgram* model = NULL;
217   const courgette::Status parse_model_status =
218       courgette::ParseDetectedExecutable(model_buffer.c_str(),
219                                          model_buffer.length(),
220                                          &model);
221   if (parse_model_status != courgette::C_OK)
222     Problem("Can't parse model input.");
223
224   courgette::AssemblyProgram* program = NULL;
225   const courgette::Status parse_program_status =
226       courgette::ParseDetectedExecutable(program_buffer.c_str(),
227                                          program_buffer.length(),
228                                          &program);
229   if (parse_program_status != courgette::C_OK)
230     Problem("Can't parse program input.");
231
232   if (adjust) {
233     const courgette::Status adjust_status = Adjust(*model, program);
234     if (adjust_status != courgette::C_OK)
235       Problem("Can't adjust program.");
236   }
237
238   courgette::EncodedProgram* encoded_program = NULL;
239   const courgette::Status encode_program_status =
240       Encode(program, &encoded_program);
241   courgette::DeleteAssemblyProgram(program);
242   if (encode_program_status != courgette::C_OK)
243     Problem("Can't encode program.");
244
245   courgette::EncodedProgram* encoded_model = NULL;
246   const courgette::Status encode_model_status = Encode(model, &encoded_model);
247   courgette::DeleteAssemblyProgram(model);
248   if (encode_model_status != courgette::C_OK)
249     Problem("Can't encode model.");
250
251   courgette::SinkStreamSet program_sinks;
252   const courgette::Status write_program_status =
253     courgette::WriteEncodedProgram(encoded_program, &program_sinks);
254   if (write_program_status != courgette::C_OK)
255     Problem("Can't serialize encoded program.");
256   courgette::DeleteEncodedProgram(encoded_program);
257
258   courgette::SinkStreamSet model_sinks;
259   const courgette::Status write_model_status =
260     courgette::WriteEncodedProgram(encoded_model, &model_sinks);
261   if (write_model_status != courgette::C_OK)
262     Problem("Can't serialize encoded model.");
263   courgette::DeleteEncodedProgram(encoded_model);
264
265   courgette::SinkStream empty_sink;
266   for (int i = 0;  ; ++i) {
267     courgette::SinkStream* old_stream = model_sinks.stream(i);
268     courgette::SinkStream* new_stream = program_sinks.stream(i);
269     if (old_stream == NULL && new_stream == NULL)
270       break;
271
272     courgette::SourceStream old_source;
273     courgette::SourceStream new_source;
274     old_source.Init(old_stream ? *old_stream : empty_sink);
275     new_source.Init(new_stream ? *new_stream : empty_sink);
276     courgette::SinkStream patch_stream;
277     courgette::BSDiffStatus status =
278         courgette::CreateBinaryPatch(&old_source, &new_source, &patch_stream);
279     if (status != courgette::OK) Problem("-xxx failed.");
280
281     std::string append = std::string("-") + base::IntToString(i);
282
283     WriteSinkToFile(&patch_stream,
284                     output_file_root.InsertBeforeExtensionASCII(append));
285   }
286 }
287
288 void Assemble(const base::FilePath& input_file,
289               const base::FilePath& output_file) {
290   std::string buffer = ReadOrFail(input_file, "input");
291
292   courgette::SourceStreamSet sources;
293   if (!sources.Init(buffer.c_str(), buffer.length()))
294     Problem("Bad input file.");
295
296   courgette::EncodedProgram* encoded = NULL;
297   const courgette::Status read_status = ReadEncodedProgram(&sources, &encoded);
298   if (read_status != courgette::C_OK)
299     Problem("Bad encoded program.");
300
301   courgette::SinkStream sink;
302
303   const courgette::Status assemble_status = courgette::Assemble(encoded, &sink);
304   if (assemble_status != courgette::C_OK)
305     Problem("Can't assemble.");
306
307   WriteSinkToFile(&sink, output_file);
308 }
309
310 void GenerateEnsemblePatch(const base::FilePath& old_file,
311                            const base::FilePath& new_file,
312                            const base::FilePath& patch_file) {
313   std::string old_buffer = ReadOrFail(old_file, "'old' input");
314   std::string new_buffer = ReadOrFail(new_file, "'new' input");
315
316   courgette::SourceStream old_stream;
317   courgette::SourceStream new_stream;
318   old_stream.Init(old_buffer);
319   new_stream.Init(new_buffer);
320
321   courgette::SinkStream patch_stream;
322   courgette::Status status =
323       courgette::GenerateEnsemblePatch(&old_stream, &new_stream, &patch_stream);
324
325   if (status != courgette::C_OK) Problem("-gen failed.");
326
327   WriteSinkToFile(&patch_stream, patch_file);
328 }
329
330 void ApplyEnsemblePatch(const base::FilePath& old_file,
331                         const base::FilePath& patch_file,
332                         const base::FilePath& new_file) {
333   // We do things a little differently here in order to call the same Courgette
334   // entry point as the installer.  That entry point point takes file names and
335   // returns an status code but does not output any diagnostics.
336
337   courgette::Status status =
338       courgette::ApplyEnsemblePatch(old_file.value().c_str(),
339                                     patch_file.value().c_str(),
340                                     new_file.value().c_str());
341
342   if (status == courgette::C_OK)
343     return;
344
345   // Diagnose the error.
346   switch (status) {
347     case courgette::C_BAD_ENSEMBLE_MAGIC:
348       Problem("Not a courgette patch");
349       break;
350
351     case courgette::C_BAD_ENSEMBLE_VERSION:
352       Problem("Wrong version patch");
353       break;
354
355     case courgette::C_BAD_ENSEMBLE_HEADER:
356       Problem("Corrupt patch");
357       break;
358
359     case courgette::C_DISASSEMBLY_FAILED:
360       Problem("Disassembly failed (could be because of memory issues)");
361       break;
362
363     case courgette::C_STREAM_ERROR:
364       Problem("Stream error (likely out of memory or disk space)");
365       break;
366
367     default:
368       break;
369   }
370
371   //  If we failed due to a missing input file, this will
372   // print the message.
373   std::string old_buffer = ReadOrFail(old_file, "'old' input");
374   old_buffer.clear();
375   std::string patch_buffer = ReadOrFail(patch_file, "'patch' input");
376   patch_buffer.clear();
377
378   // Non-input related errors:
379   if (status == courgette::C_WRITE_OPEN_ERROR)
380     Problem("Can't open output");
381   if (status == courgette::C_WRITE_ERROR)
382     Problem("Can't write output");
383
384   Problem("-apply failed.");
385 }
386
387 void GenerateBSDiffPatch(const base::FilePath& old_file,
388                          const base::FilePath& new_file,
389                          const base::FilePath& patch_file) {
390   std::string old_buffer = ReadOrFail(old_file, "'old' input");
391   std::string new_buffer = ReadOrFail(new_file, "'new' input");
392
393   courgette::SourceStream old_stream;
394   courgette::SourceStream new_stream;
395   old_stream.Init(old_buffer);
396   new_stream.Init(new_buffer);
397
398   courgette::SinkStream patch_stream;
399   courgette::BSDiffStatus status =
400       courgette::CreateBinaryPatch(&old_stream, &new_stream, &patch_stream);
401
402   if (status != courgette::OK) Problem("-genbsdiff failed.");
403
404   WriteSinkToFile(&patch_stream, patch_file);
405 }
406
407 void ApplyBSDiffPatch(const base::FilePath& old_file,
408                       const base::FilePath& patch_file,
409                       const base::FilePath& new_file) {
410   std::string old_buffer = ReadOrFail(old_file, "'old' input");
411   std::string patch_buffer = ReadOrFail(patch_file, "'patch' input");
412
413   courgette::SourceStream old_stream;
414   courgette::SourceStream patch_stream;
415   old_stream.Init(old_buffer);
416   patch_stream.Init(patch_buffer);
417
418   courgette::SinkStream new_stream;
419   courgette::BSDiffStatus status =
420       courgette::ApplyBinaryPatch(&old_stream, &patch_stream, &new_stream);
421
422   if (status != courgette::OK) Problem("-applybsdiff failed.");
423
424   WriteSinkToFile(&new_stream, new_file);
425 }
426
427 int main(int argc, const char* argv[]) {
428   base::AtExitManager at_exit_manager;
429   CommandLine::Init(argc, argv);
430   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
431
432   logging::LoggingSettings settings;
433   settings.logging_dest = logging::LOG_TO_ALL;
434   settings.log_file = FILE_PATH_LITERAL("courgette.log");
435   (void)logging::InitLogging(settings);
436   logging::SetMinLogLevel(logging::LOG_VERBOSE);
437
438   bool cmd_sup = command_line.HasSwitch("supported");
439   bool cmd_dis = command_line.HasSwitch("dis");
440   bool cmd_asm = command_line.HasSwitch("asm");
441   bool cmd_disadj = command_line.HasSwitch("disadj");
442   bool cmd_make_patch = command_line.HasSwitch("gen");
443   bool cmd_apply_patch = command_line.HasSwitch("apply");
444   bool cmd_make_bsdiff_patch = command_line.HasSwitch("genbsdiff");
445   bool cmd_apply_bsdiff_patch = command_line.HasSwitch("applybsdiff");
446   bool cmd_spread_1_adjusted = command_line.HasSwitch("gen1a");
447   bool cmd_spread_1_unadjusted = command_line.HasSwitch("gen1u");
448
449   std::vector<base::FilePath> values;
450   const CommandLine::StringVector& args = command_line.GetArgs();
451   for (size_t i = 0; i < args.size(); ++i) {
452     values.push_back(base::FilePath(args[i]));
453   }
454
455   // '-repeat=N' is for debugging.  Running many iterations can reveal leaks and
456   // bugs in cleanup.
457   int repeat_count = 1;
458   std::string repeat_switch = command_line.GetSwitchValueASCII("repeat");
459   if (!repeat_switch.empty())
460     if (!base::StringToInt(repeat_switch, &repeat_count))
461       repeat_count = 1;
462
463   if (cmd_sup + cmd_dis + cmd_asm + cmd_disadj + cmd_make_patch +
464       cmd_apply_patch + cmd_make_bsdiff_patch + cmd_apply_bsdiff_patch +
465       cmd_spread_1_adjusted + cmd_spread_1_unadjusted
466       != 1)
467     UsageProblem(
468         "Must have exactly one of:\n"
469         "  -supported -asm, -dis, -disadj, -gen or -apply, -genbsdiff"
470         " or -applybsdiff.");
471
472   while (repeat_count-- > 0) {
473     if (cmd_sup) {
474       if (values.size() != 1)
475         UsageProblem("-supported <executable_file>");
476       return !Supported(values[0]);
477     } else if (cmd_dis) {
478         if (values.size() != 2)
479           UsageProblem("-dis <executable_file> <courgette_file>");
480         Disassemble(values[0], values[1]);
481     } else if (cmd_asm) {
482       if (values.size() != 2)
483         UsageProblem("-asm <courgette_file_input> <executable_file_output>");
484       Assemble(values[0], values[1]);
485     } else if (cmd_disadj) {
486       if (values.size() != 3)
487         UsageProblem("-disadj <executable_file> <model> <courgette_file>");
488       DisassembleAndAdjust(values[0], values[1], values[2]);
489     } else if (cmd_make_patch) {
490       if (values.size() != 3)
491         UsageProblem("-gen <old_file> <new_file> <patch_file>");
492       GenerateEnsemblePatch(values[0], values[1], values[2]);
493     } else if (cmd_apply_patch) {
494       if (values.size() != 3)
495         UsageProblem("-apply <old_file> <patch_file> <new_file>");
496       ApplyEnsemblePatch(values[0], values[1], values[2]);
497     } else if (cmd_make_bsdiff_patch) {
498       if (values.size() != 3)
499         UsageProblem("-genbsdiff <old_file> <new_file> <patch_file>");
500       GenerateBSDiffPatch(values[0], values[1], values[2]);
501     } else if (cmd_apply_bsdiff_patch) {
502       if (values.size() != 3)
503         UsageProblem("-applybsdiff <old_file> <patch_file> <new_file>");
504       ApplyBSDiffPatch(values[0], values[1], values[2]);
505     } else if (cmd_spread_1_adjusted || cmd_spread_1_unadjusted) {
506       if (values.size() != 3)
507         UsageProblem("-gen1[au] <old_file> <new_file> <patch_files_root>");
508       DisassembleAdjustDiff(values[0], values[1], values[2],
509                             cmd_spread_1_adjusted);
510     } else {
511       UsageProblem("No operation specified");
512     }
513   }
514
515   return 0;
516 }