Enable dev build with the latest repo
[platform/framework/web/chromium-efl.git] / 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 <stdarg.h>
6 #include <stddef.h>
7 #include <stdint.h>
8
9 #include <initializer_list>
10 #include <memory>
11 #include <string>
12 #include <vector>
13
14 #include "base/at_exit.h"
15 #include "base/command_line.h"
16 #include "base/files/file_path.h"
17 #include "base/files/file_util.h"
18 #include "base/files/memory_mapped_file.h"
19 #include "base/logging.h"
20 #include "base/macros.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/utf_string_conversions.h"
24 #include "courgette/assembly_program.h"
25 #include "courgette/courgette.h"
26 #include "courgette/courgette_flow.h"
27 #include "courgette/encoded_program.h"
28 #include "courgette/program_detector.h"
29 #include "courgette/streams.h"
30 #include "courgette/third_party/bsdiff/bsdiff.h"
31
32 namespace {
33
34 using courgette::CourgetteFlow;
35
36 const char kUsageGen[] = "-gen <old_in> <new_in> <patch_out>";
37 const char kUsageApply[] = "-apply <old_in> <patch_in> <new_out>";
38 const char kUsageGenbsdiff[] = "-genbsdiff <old_in> <new_in> <patch_out>";
39 const char kUsageApplybsdiff[] = "-applybsdiff <old_in> <patch_in> <new_out>";
40 const char kUsageSupported[] = "-supported <exec_file_in>";
41 const char kUsageDis[] = "-dis <exec_file_in> <assembly_file_out>";
42 const char kUsageAsm[] = "-asm <assembly_file_in> <exec_file_out>";
43 const char kUsageDisadj[] = "-disadj <old_in> <new_in> <new_assembly_file_out>";
44 const char kUsageGen1[] = "-gen1[au] <old_in> <new_in> <patch_base_out>";
45
46 /******** Utilities to print help and exit ********/
47
48 void PrintHelp() {
49   fprintf(stderr, "Main Usage:\n");
50   for (auto usage :
51        {kUsageGen, kUsageApply, kUsageGenbsdiff, kUsageApplybsdiff}) {
52     fprintf(stderr, "  courgette %s\n", usage);
53   }
54   fprintf(stderr, "Diagnosis Usage:\n");
55   for (auto usage :
56        {kUsageSupported, kUsageDis, kUsageAsm, kUsageDisadj, kUsageGen1}) {
57     fprintf(stderr, "  courgette %s\n", usage);
58   }
59 }
60
61 void UsageProblem(const char* message) {
62   fprintf(stderr, "%s", message);
63   fprintf(stderr, "\n");
64   PrintHelp();
65   exit(1);
66 }
67
68 void Problem(const char* format, ...) {
69   va_list args;
70   va_start(args, format);
71   vfprintf(stderr, format, args);
72   fprintf(stderr, "\n");
73   va_end(args);
74   exit(1);
75 }
76
77 /******** BufferedFileReader ********/
78
79 // A file reader that calls Problem() on failure.
80 class BufferedFileReader : public courgette::BasicBuffer {
81  public:
82   BufferedFileReader(const base::FilePath& file_name, const char* kind) {
83     if (!buffer_.Initialize(file_name))
84       Problem("Can't read %s file.", kind);
85   }
86   ~BufferedFileReader() override = default;
87
88   // courgette::BasicBuffer:
89   const uint8_t* data() const override { return buffer_.data(); }
90   size_t length() const override { return buffer_.length(); }
91
92  private:
93   base::MemoryMappedFile buffer_;
94
95   DISALLOW_COPY_AND_ASSIGN(BufferedFileReader);
96 };
97
98 /******** Various helpers ********/
99
100 void WriteSinkToFile(const courgette::SinkStream* sink,
101                      const base::FilePath& output_file) {
102   int count = base::WriteFile(output_file,
103                               reinterpret_cast<const char*>(sink->Buffer()),
104                               static_cast<int>(sink->Length()));
105   if (count == -1)
106     Problem("Can't write output.");
107   if (static_cast<size_t>(count) != sink->Length())
108     Problem("Incomplete write.");
109 }
110
111 bool Supported(const base::FilePath& input_file) {
112   bool result = false;
113
114   BufferedFileReader buffer(input_file, "input");
115
116   courgette::ExecutableType type;
117   size_t detected_length;
118
119   DetectExecutableType(buffer.data(), buffer.length(), &type, &detected_length);
120
121   // If the detection fails, we just fall back on UNKNOWN
122   std::string format = "Unsupported";
123
124   switch (type) {
125     case courgette::EXE_UNKNOWN:
126       break;
127
128     case courgette::EXE_WIN_32_X86:
129       format = "Windows 32 PE";
130       result = true;
131       break;
132
133     case courgette::EXE_ELF_32_X86:
134       format = "ELF 32 X86";
135       result = true;
136       break;
137
138     case courgette::EXE_ELF_32_ARM:
139       format = "ELF 32 ARM";
140       result = true;
141       break;
142
143     case courgette::EXE_WIN_32_X64:
144       format = "Windows 64 PE";
145       result = true;
146       break;
147   }
148
149   printf("%s Executable\n", format.c_str());
150   return result;
151 }
152
153 void Disassemble(const base::FilePath& input_file,
154                  const base::FilePath& output_file) {
155   CourgetteFlow flow;
156   BufferedFileReader input_buffer(input_file, flow.name(flow.ONLY));
157   flow.ReadDisassemblerFromBuffer(flow.ONLY, input_buffer);
158   flow.CreateAssemblyProgramFromDisassembler(flow.ONLY, false);
159   flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.ONLY);
160   flow.DestroyDisassembler(flow.ONLY);
161   flow.DestroyAssemblyProgram(flow.ONLY);
162   flow.WriteSinkStreamSetFromEncodedProgram(flow.ONLY);
163   flow.DestroyEncodedProgram(flow.ONLY);
164   courgette::SinkStream sink;
165   flow.WriteSinkStreamFromSinkStreamSet(flow.ONLY, &sink);
166   if (flow.failed())
167     Problem(flow.message().c_str());
168
169   WriteSinkToFile(&sink, output_file);
170 }
171
172 void DisassembleAndAdjust(const base::FilePath& old_file,
173                           const base::FilePath& new_file,
174                           const base::FilePath& output_file) {
175   // Flow graph and process sequence (DA = Disassembler, AP = AssemblyProgram,
176   // EP = EncodedProgram, Adj = Adjusted):
177   //   [1 Old DA] --> [2 Old AP]    [4 New AP] <-- [3 New DA]
178   //                      |             |              |
179   //                      |             v (move)       v
180   //                      +---> [5 Adj New AP] --> [6 New EP]
181   //                                               (7 Write)
182   CourgetteFlow flow;
183   BufferedFileReader old_buffer(old_file, flow.name(flow.OLD));
184   BufferedFileReader new_buffer(new_file, flow.name(flow.NEW));
185   flow.ReadDisassemblerFromBuffer(flow.OLD, old_buffer);       // 1
186   flow.CreateAssemblyProgramFromDisassembler(flow.OLD, true);  // 2
187   flow.DestroyDisassembler(flow.OLD);
188   flow.ReadDisassemblerFromBuffer(flow.NEW, new_buffer);       // 3
189   flow.CreateAssemblyProgramFromDisassembler(flow.NEW, true);  // 4
190   flow.AdjustNewAssemblyProgramToMatchOld();                   // 5
191   flow.DestroyAssemblyProgram(flow.OLD);
192   flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.NEW);  // 6
193   flow.DestroyAssemblyProgram(flow.NEW);
194   flow.DestroyDisassembler(flow.NEW);
195   flow.WriteSinkStreamSetFromEncodedProgram(flow.NEW);  // 7
196   flow.DestroyEncodedProgram(flow.NEW);
197   courgette::SinkStream sink;
198   flow.WriteSinkStreamFromSinkStreamSet(flow.NEW, &sink);
199   if (flow.failed())
200     Problem(flow.message().c_str());
201
202   WriteSinkToFile(&sink, output_file);
203 }
204
205 // Diffs two executable files, write a set of files for the diff, one file per
206 // stream of the EncodedProgram format.  Each file is the bsdiff between the
207 // original file's stream and the new file's stream.  This is completely
208 // uninteresting to users, but it is handy for seeing how much each which
209 // streams are contributing to the final file size.  Adjustment is optional.
210 void DisassembleAdjustDiff(const base::FilePath& old_file,
211                            const base::FilePath& new_file,
212                            const base::FilePath& output_file_root,
213                            bool adjust) {
214   // Same as PatchGeneratorX86_32::Transform(), except Adjust is optional, and
215   // |flow|'s internal SinkStreamSet get used.
216   // Flow graph and process sequence (DA = Disassembler, AP = AssemblyProgram,
217   // EP = EncodedProgram, Adj = Adjusted):
218   //   [1 Old DA] --> [2 Old AP]   [6 New AP] <-- [5 New DA]
219   //       |            |   |          |              |
220   //       v            |   |          v (move)       v
221   //   [3 Old EP] <-----+   +->[7 Adj New AP] --> [8 New EP]
222   //   (4 Write)                                  (9 Write)
223   CourgetteFlow flow;
224   BufferedFileReader old_buffer(old_file, flow.name(flow.OLD));
225   BufferedFileReader new_buffer(new_file, flow.name(flow.NEW));
226   flow.ReadDisassemblerFromBuffer(flow.OLD, old_buffer);                  // 1
227   flow.CreateAssemblyProgramFromDisassembler(flow.OLD, adjust);           // 2
228   flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.OLD);  // 3
229   flow.DestroyDisassembler(flow.OLD);
230   flow.WriteSinkStreamSetFromEncodedProgram(flow.OLD);  // 4
231   flow.DestroyEncodedProgram(flow.OLD);
232   flow.ReadDisassemblerFromBuffer(flow.NEW, new_buffer);         // 5
233   flow.CreateAssemblyProgramFromDisassembler(flow.NEW, adjust);  // 6
234   if (adjust)
235     flow.AdjustNewAssemblyProgramToMatchOld();  // 7, optional
236   flow.DestroyAssemblyProgram(flow.OLD);
237   flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.NEW);  // 8
238   flow.DestroyAssemblyProgram(flow.NEW);
239   flow.DestroyDisassembler(flow.NEW);
240   flow.WriteSinkStreamSetFromEncodedProgram(flow.NEW);  // 9
241   flow.DestroyEncodedProgram(flow.NEW);
242   if (flow.failed())
243     Problem(flow.message().c_str());
244
245   courgette::SinkStream empty_sink;
246   for (int i = 0;; ++i) {
247     courgette::SinkStream* old_stream = flow.data(flow.OLD)->sinks.stream(i);
248     courgette::SinkStream* new_stream = flow.data(flow.NEW)->sinks.stream(i);
249     if (old_stream == nullptr && new_stream == nullptr)
250       break;
251
252     courgette::SourceStream old_source;
253     courgette::SourceStream new_source;
254     old_source.Init(old_stream ? *old_stream : empty_sink);
255     new_source.Init(new_stream ? *new_stream : empty_sink);
256     courgette::SinkStream patch_stream;
257     bsdiff::BSDiffStatus status =
258         bsdiff::CreateBinaryPatch(&old_source, &new_source, &patch_stream);
259     if (status != bsdiff::OK)
260       Problem("-xxx failed.");
261
262     std::string append = std::string("-") + base::NumberToString(i);
263
264     WriteSinkToFile(&patch_stream,
265                     output_file_root.InsertBeforeExtensionASCII(append));
266   }
267 }
268
269 void Assemble(const base::FilePath& input_file,
270               const base::FilePath& output_file) {
271   CourgetteFlow flow;
272   BufferedFileReader input_buffer(input_file, flow.name(flow.ONLY));
273   flow.ReadSourceStreamSetFromBuffer(flow.ONLY, input_buffer);
274   flow.ReadEncodedProgramFromSourceStreamSet(flow.ONLY);
275   courgette::SinkStream sink;
276   flow.WriteExecutableFromEncodedProgram(flow.ONLY, &sink);
277   if (flow.failed())
278     Problem(flow.message().c_str());
279
280   WriteSinkToFile(&sink, output_file);
281 }
282
283 void GenerateEnsemblePatch(const base::FilePath& old_file,
284                            const base::FilePath& new_file,
285                            const base::FilePath& patch_file) {
286   BufferedFileReader old_buffer(old_file, "'old' input");
287   BufferedFileReader new_buffer(new_file, "'new' input");
288
289   courgette::SourceStream old_stream;
290   courgette::SourceStream new_stream;
291   old_stream.Init(old_buffer.data(), old_buffer.length());
292   new_stream.Init(new_buffer.data(), new_buffer.length());
293
294   courgette::SinkStream patch_stream;
295   courgette::Status status =
296       courgette::GenerateEnsemblePatch(&old_stream, &new_stream, &patch_stream);
297
298   if (status != courgette::C_OK)
299     Problem("-gen failed.");
300
301   WriteSinkToFile(&patch_stream, patch_file);
302 }
303
304 void ApplyEnsemblePatch(const base::FilePath& old_file,
305                         const base::FilePath& patch_file,
306                         const base::FilePath& new_file) {
307   // We do things a little differently here in order to call the same Courgette
308   // entry point as the installer.  That entry point point takes file names and
309   // returns an status code but does not output any diagnostics.
310
311   courgette::Status status = courgette::ApplyEnsemblePatch(
312       old_file.value().c_str(), patch_file.value().c_str(),
313       new_file.value().c_str());
314
315   if (status == courgette::C_OK)
316     return;
317
318   // Diagnose the error.
319   switch (status) {
320     case courgette::C_BAD_ENSEMBLE_MAGIC:
321       Problem("Not a courgette patch");
322       break;
323
324     case courgette::C_BAD_ENSEMBLE_VERSION:
325       Problem("Wrong version patch");
326       break;
327
328     case courgette::C_BAD_ENSEMBLE_HEADER:
329       Problem("Corrupt patch");
330       break;
331
332     case courgette::C_DISASSEMBLY_FAILED:
333       Problem("Disassembly failed (could be because of memory issues)");
334       break;
335
336     case courgette::C_STREAM_ERROR:
337       Problem("Stream error (likely out of memory or disk space)");
338       break;
339
340     default:
341       break;
342   }
343
344   // If we failed due to a missing input file, this will print the message.
345   { BufferedFileReader old_buffer(old_file, "'old' input"); }
346   { BufferedFileReader patch_buffer(patch_file, "'patch' input"); }
347
348   // Non-input related errors:
349   if (status == courgette::C_WRITE_OPEN_ERROR)
350     Problem("Can't open output");
351   if (status == courgette::C_WRITE_ERROR)
352     Problem("Can't write output");
353
354   Problem("-apply failed.");
355 }
356
357 void GenerateBSDiffPatch(const base::FilePath& old_file,
358                          const base::FilePath& new_file,
359                          const base::FilePath& patch_file) {
360   BufferedFileReader old_buffer(old_file, "'old' input");
361   BufferedFileReader new_buffer(new_file, "'new' input");
362
363   courgette::SourceStream old_stream;
364   courgette::SourceStream new_stream;
365   old_stream.Init(old_buffer.data(), old_buffer.length());
366   new_stream.Init(new_buffer.data(), new_buffer.length());
367
368   courgette::SinkStream patch_stream;
369   bsdiff::BSDiffStatus status =
370       bsdiff::CreateBinaryPatch(&old_stream, &new_stream, &patch_stream);
371
372   if (status != bsdiff::OK)
373     Problem("-genbsdiff failed.");
374
375   WriteSinkToFile(&patch_stream, patch_file);
376 }
377
378 void ApplyBSDiffPatch(const base::FilePath& old_file,
379                       const base::FilePath& patch_file,
380                       const base::FilePath& new_file) {
381   BufferedFileReader old_buffer(old_file, "'old' input");
382   BufferedFileReader patch_buffer(patch_file, "'patch' input");
383
384   courgette::SourceStream old_stream;
385   courgette::SourceStream patch_stream;
386   old_stream.Init(old_buffer.data(), old_buffer.length());
387   patch_stream.Init(patch_buffer.data(), patch_buffer.length());
388
389   courgette::SinkStream new_stream;
390   bsdiff::BSDiffStatus status =
391       bsdiff::ApplyBinaryPatch(&old_stream, &patch_stream, &new_stream);
392
393   if (status != bsdiff::OK)
394     Problem("-applybsdiff failed.");
395
396   WriteSinkToFile(&new_stream, new_file);
397 }
398
399 }  // namespace
400
401 int main(int argc, const char* argv[]) {
402   base::AtExitManager at_exit_manager;
403   base::CommandLine::Init(argc, argv);
404   const base::CommandLine& command_line =
405       *base::CommandLine::ForCurrentProcess();
406
407   logging::LoggingSettings settings;
408   if (command_line.HasSwitch("nologfile")) {
409     settings.logging_dest =
410         logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
411   } else {
412     settings.logging_dest = logging::LOG_TO_ALL;
413     settings.log_file = FILE_PATH_LITERAL("courgette.log");
414   }
415   (void)logging::InitLogging(settings);
416   logging::SetMinLogLevel(logging::LOG_VERBOSE);
417
418   bool cmd_sup = command_line.HasSwitch("supported");
419   bool cmd_dis = command_line.HasSwitch("dis");
420   bool cmd_asm = command_line.HasSwitch("asm");
421   bool cmd_disadj = command_line.HasSwitch("disadj");
422   bool cmd_make_patch = command_line.HasSwitch("gen");
423   bool cmd_apply_patch = command_line.HasSwitch("apply");
424   bool cmd_make_bsdiff_patch = command_line.HasSwitch("genbsdiff");
425   bool cmd_apply_bsdiff_patch = command_line.HasSwitch("applybsdiff");
426   bool cmd_spread_1_adjusted = command_line.HasSwitch("gen1a");
427   bool cmd_spread_1_unadjusted = command_line.HasSwitch("gen1u");
428
429   std::vector<base::FilePath> values;
430   const base::CommandLine::StringVector& args = command_line.GetArgs();
431   for (size_t i = 0; i < args.size(); ++i) {
432     values.push_back(base::FilePath(args[i]));
433   }
434
435   // '-repeat=N' is for debugging.  Running many iterations can reveal leaks and
436   // bugs in cleanup.
437   int repeat_count = 1;
438   std::string repeat_switch = command_line.GetSwitchValueASCII("repeat");
439   if (!repeat_switch.empty())
440     if (!base::StringToInt(repeat_switch, &repeat_count))
441       repeat_count = 1;
442
443   if (cmd_sup + cmd_dis + cmd_asm + cmd_disadj + cmd_make_patch +
444           cmd_apply_patch + cmd_make_bsdiff_patch + cmd_apply_bsdiff_patch +
445           cmd_spread_1_adjusted + cmd_spread_1_unadjusted !=
446       1) {
447     UsageProblem(
448         "First argument must be one of:\n"
449         "  -supported, -asm, -dis, -disadj, -gen, -apply, -genbsdiff,"
450         " -applybsdiff, or -gen1[au].");
451   }
452
453   while (repeat_count-- > 0) {
454     if (cmd_sup) {
455       if (values.size() != 1)
456         UsageProblem(kUsageSupported);
457       return !Supported(values[0]);
458     } else if (cmd_dis) {
459       if (values.size() != 2)
460         UsageProblem(kUsageDis);
461       Disassemble(values[0], values[1]);
462     } else if (cmd_asm) {
463       if (values.size() != 2)
464         UsageProblem(kUsageAsm);
465       Assemble(values[0], values[1]);
466     } else if (cmd_disadj) {
467       if (values.size() != 3)
468         UsageProblem(kUsageDisadj);
469       DisassembleAndAdjust(values[0], values[1], values[2]);
470     } else if (cmd_make_patch) {
471       if (values.size() != 3)
472         UsageProblem(kUsageGen);
473       GenerateEnsemblePatch(values[0], values[1], values[2]);
474     } else if (cmd_apply_patch) {
475       if (values.size() != 3)
476         UsageProblem(kUsageApply);
477       ApplyEnsemblePatch(values[0], values[1], values[2]);
478     } else if (cmd_make_bsdiff_patch) {
479       if (values.size() != 3)
480         UsageProblem(kUsageGenbsdiff);
481       GenerateBSDiffPatch(values[0], values[1], values[2]);
482     } else if (cmd_apply_bsdiff_patch) {
483       if (values.size() != 3)
484         UsageProblem(kUsageApplybsdiff);
485       ApplyBSDiffPatch(values[0], values[1], values[2]);
486     } else if (cmd_spread_1_adjusted || cmd_spread_1_unadjusted) {
487       if (values.size() != 3)
488         UsageProblem(kUsageGen1);
489       DisassembleAdjustDiff(values[0], values[1], values[2],
490                             cmd_spread_1_adjusted);
491     } else {
492       UsageProblem("No operation specified");
493     }
494   }
495
496   return 0;
497 }