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 
PrintHelp()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 
UsageProblem(const char * message)61 void UsageProblem(const char* message) {
62   fprintf(stderr, "%s", message);
63   fprintf(stderr, "\n");
64   PrintHelp();
65   exit(1);
66 }
67 
Problem(const char * format,...)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:
BufferedFileReader(const base::FilePath & file_name,const char * kind)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:
data() const89   const uint8_t* data() const override { return buffer_.data(); }
length() const90   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 
WriteSinkToFile(const courgette::SinkStream * sink,const base::FilePath & output_file)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 
Supported(const base::FilePath & input_file)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_WIN_32_X64:
139       format = "Windows 64 PE";
140       result = true;
141       break;
142   }
143 
144   printf("%s Executable\n", format.c_str());
145   return result;
146 }
147 
Disassemble(const base::FilePath & input_file,const base::FilePath & output_file)148 void Disassemble(const base::FilePath& input_file,
149                  const base::FilePath& output_file) {
150   CourgetteFlow flow;
151   BufferedFileReader input_buffer(input_file, flow.name(flow.ONLY));
152   flow.ReadDisassemblerFromBuffer(flow.ONLY, input_buffer);
153   flow.CreateAssemblyProgramFromDisassembler(flow.ONLY, false);
154   flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.ONLY);
155   flow.DestroyDisassembler(flow.ONLY);
156   flow.DestroyAssemblyProgram(flow.ONLY);
157   flow.WriteSinkStreamSetFromEncodedProgram(flow.ONLY);
158   flow.DestroyEncodedProgram(flow.ONLY);
159   courgette::SinkStream sink;
160   flow.WriteSinkStreamFromSinkStreamSet(flow.ONLY, &sink);
161   if (flow.failed())
162     Problem(flow.message().c_str());
163 
164   WriteSinkToFile(&sink, output_file);
165 }
166 
DisassembleAndAdjust(const base::FilePath & old_file,const base::FilePath & new_file,const base::FilePath & output_file)167 void DisassembleAndAdjust(const base::FilePath& old_file,
168                           const base::FilePath& new_file,
169                           const base::FilePath& output_file) {
170   // Flow graph and process sequence (DA = Disassembler, AP = AssemblyProgram,
171   // EP = EncodedProgram, Adj = Adjusted):
172   //   [1 Old DA] --> [2 Old AP]    [4 New AP] <-- [3 New DA]
173   //                      |             |              |
174   //                      |             v (move)       v
175   //                      +---> [5 Adj New AP] --> [6 New EP]
176   //                                               (7 Write)
177   CourgetteFlow flow;
178   BufferedFileReader old_buffer(old_file, flow.name(flow.OLD));
179   BufferedFileReader new_buffer(new_file, flow.name(flow.NEW));
180   flow.ReadDisassemblerFromBuffer(flow.OLD, old_buffer);       // 1
181   flow.CreateAssemblyProgramFromDisassembler(flow.OLD, true);  // 2
182   flow.DestroyDisassembler(flow.OLD);
183   flow.ReadDisassemblerFromBuffer(flow.NEW, new_buffer);       // 3
184   flow.CreateAssemblyProgramFromDisassembler(flow.NEW, true);  // 4
185   flow.AdjustNewAssemblyProgramToMatchOld();                   // 5
186   flow.DestroyAssemblyProgram(flow.OLD);
187   flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.NEW);  // 6
188   flow.DestroyAssemblyProgram(flow.NEW);
189   flow.DestroyDisassembler(flow.NEW);
190   flow.WriteSinkStreamSetFromEncodedProgram(flow.NEW);  // 7
191   flow.DestroyEncodedProgram(flow.NEW);
192   courgette::SinkStream sink;
193   flow.WriteSinkStreamFromSinkStreamSet(flow.NEW, &sink);
194   if (flow.failed())
195     Problem(flow.message().c_str());
196 
197   WriteSinkToFile(&sink, output_file);
198 }
199 
200 // Diffs two executable files, write a set of files for the diff, one file per
201 // stream of the EncodedProgram format.  Each file is the bsdiff between the
202 // original file's stream and the new file's stream.  This is completely
203 // uninteresting to users, but it is handy for seeing how much each which
204 // streams are contributing to the final file size.  Adjustment is optional.
DisassembleAdjustDiff(const base::FilePath & old_file,const base::FilePath & new_file,const base::FilePath & output_file_root,bool adjust)205 void DisassembleAdjustDiff(const base::FilePath& old_file,
206                            const base::FilePath& new_file,
207                            const base::FilePath& output_file_root,
208                            bool adjust) {
209   // Same as PatchGeneratorX86_32::Transform(), except Adjust is optional, and
210   // |flow|'s internal SinkStreamSet get used.
211   // Flow graph and process sequence (DA = Disassembler, AP = AssemblyProgram,
212   // EP = EncodedProgram, Adj = Adjusted):
213   //   [1 Old DA] --> [2 Old AP]   [6 New AP] <-- [5 New DA]
214   //       |            |   |          |              |
215   //       v            |   |          v (move)       v
216   //   [3 Old EP] <-----+   +->[7 Adj New AP] --> [8 New EP]
217   //   (4 Write)                                  (9 Write)
218   CourgetteFlow flow;
219   BufferedFileReader old_buffer(old_file, flow.name(flow.OLD));
220   BufferedFileReader new_buffer(new_file, flow.name(flow.NEW));
221   flow.ReadDisassemblerFromBuffer(flow.OLD, old_buffer);                  // 1
222   flow.CreateAssemblyProgramFromDisassembler(flow.OLD, adjust);           // 2
223   flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.OLD);  // 3
224   flow.DestroyDisassembler(flow.OLD);
225   flow.WriteSinkStreamSetFromEncodedProgram(flow.OLD);  // 4
226   flow.DestroyEncodedProgram(flow.OLD);
227   flow.ReadDisassemblerFromBuffer(flow.NEW, new_buffer);         // 5
228   flow.CreateAssemblyProgramFromDisassembler(flow.NEW, adjust);  // 6
229   if (adjust)
230     flow.AdjustNewAssemblyProgramToMatchOld();  // 7, optional
231   flow.DestroyAssemblyProgram(flow.OLD);
232   flow.CreateEncodedProgramFromDisassemblerAndAssemblyProgram(flow.NEW);  // 8
233   flow.DestroyAssemblyProgram(flow.NEW);
234   flow.DestroyDisassembler(flow.NEW);
235   flow.WriteSinkStreamSetFromEncodedProgram(flow.NEW);  // 9
236   flow.DestroyEncodedProgram(flow.NEW);
237   if (flow.failed())
238     Problem(flow.message().c_str());
239 
240   courgette::SinkStream empty_sink;
241   for (int i = 0;; ++i) {
242     courgette::SinkStream* old_stream = flow.data(flow.OLD)->sinks.stream(i);
243     courgette::SinkStream* new_stream = flow.data(flow.NEW)->sinks.stream(i);
244     if (old_stream == nullptr && new_stream == nullptr)
245       break;
246 
247     courgette::SourceStream old_source;
248     courgette::SourceStream new_source;
249     old_source.Init(old_stream ? *old_stream : empty_sink);
250     new_source.Init(new_stream ? *new_stream : empty_sink);
251     courgette::SinkStream patch_stream;
252     bsdiff::BSDiffStatus status =
253         bsdiff::CreateBinaryPatch(&old_source, &new_source, &patch_stream);
254     if (status != bsdiff::OK)
255       Problem("-xxx failed.");
256 
257     std::string append = std::string("-") + base::NumberToString(i);
258 
259     WriteSinkToFile(&patch_stream,
260                     output_file_root.InsertBeforeExtensionASCII(append));
261   }
262 }
263 
Assemble(const base::FilePath & input_file,const base::FilePath & output_file)264 void Assemble(const base::FilePath& input_file,
265               const base::FilePath& output_file) {
266   CourgetteFlow flow;
267   BufferedFileReader input_buffer(input_file, flow.name(flow.ONLY));
268   flow.ReadSourceStreamSetFromBuffer(flow.ONLY, input_buffer);
269   flow.ReadEncodedProgramFromSourceStreamSet(flow.ONLY);
270   courgette::SinkStream sink;
271   flow.WriteExecutableFromEncodedProgram(flow.ONLY, &sink);
272   if (flow.failed())
273     Problem(flow.message().c_str());
274 
275   WriteSinkToFile(&sink, output_file);
276 }
277 
GenerateEnsemblePatch(const base::FilePath & old_file,const base::FilePath & new_file,const base::FilePath & patch_file)278 void GenerateEnsemblePatch(const base::FilePath& old_file,
279                            const base::FilePath& new_file,
280                            const base::FilePath& patch_file) {
281   BufferedFileReader old_buffer(old_file, "'old' input");
282   BufferedFileReader new_buffer(new_file, "'new' input");
283 
284   courgette::SourceStream old_stream;
285   courgette::SourceStream new_stream;
286   old_stream.Init(old_buffer.data(), old_buffer.length());
287   new_stream.Init(new_buffer.data(), new_buffer.length());
288 
289   courgette::SinkStream patch_stream;
290   courgette::Status status =
291       courgette::GenerateEnsemblePatch(&old_stream, &new_stream, &patch_stream);
292 
293   if (status != courgette::C_OK)
294     Problem("-gen failed.");
295 
296   WriteSinkToFile(&patch_stream, patch_file);
297 }
298 
ApplyEnsemblePatch(const base::FilePath & old_file,const base::FilePath & patch_file,const base::FilePath & new_file)299 void ApplyEnsemblePatch(const base::FilePath& old_file,
300                         const base::FilePath& patch_file,
301                         const base::FilePath& new_file) {
302   // We do things a little differently here in order to call the same Courgette
303   // entry point as the installer.  That entry point point takes file names and
304   // returns an status code but does not output any diagnostics.
305 
306   courgette::Status status = courgette::ApplyEnsemblePatch(
307       old_file.value().c_str(), patch_file.value().c_str(),
308       new_file.value().c_str());
309 
310   if (status == courgette::C_OK)
311     return;
312 
313   // Diagnose the error.
314   switch (status) {
315     case courgette::C_BAD_ENSEMBLE_MAGIC:
316       Problem("Not a courgette patch");
317       break;
318 
319     case courgette::C_BAD_ENSEMBLE_VERSION:
320       Problem("Wrong version patch");
321       break;
322 
323     case courgette::C_BAD_ENSEMBLE_HEADER:
324       Problem("Corrupt patch");
325       break;
326 
327     case courgette::C_DISASSEMBLY_FAILED:
328       Problem("Disassembly failed (could be because of memory issues)");
329       break;
330 
331     case courgette::C_STREAM_ERROR:
332       Problem("Stream error (likely out of memory or disk space)");
333       break;
334 
335     default:
336       break;
337   }
338 
339   // If we failed due to a missing input file, this will print the message.
340   { BufferedFileReader old_buffer(old_file, "'old' input"); }
341   { BufferedFileReader patch_buffer(patch_file, "'patch' input"); }
342 
343   // Non-input related errors:
344   if (status == courgette::C_WRITE_OPEN_ERROR)
345     Problem("Can't open output");
346   if (status == courgette::C_WRITE_ERROR)
347     Problem("Can't write output");
348 
349   Problem("-apply failed.");
350 }
351 
GenerateBSDiffPatch(const base::FilePath & old_file,const base::FilePath & new_file,const base::FilePath & patch_file)352 void GenerateBSDiffPatch(const base::FilePath& old_file,
353                          const base::FilePath& new_file,
354                          const base::FilePath& patch_file) {
355   BufferedFileReader old_buffer(old_file, "'old' input");
356   BufferedFileReader new_buffer(new_file, "'new' input");
357 
358   courgette::SourceStream old_stream;
359   courgette::SourceStream new_stream;
360   old_stream.Init(old_buffer.data(), old_buffer.length());
361   new_stream.Init(new_buffer.data(), new_buffer.length());
362 
363   courgette::SinkStream patch_stream;
364   bsdiff::BSDiffStatus status =
365       bsdiff::CreateBinaryPatch(&old_stream, &new_stream, &patch_stream);
366 
367   if (status != bsdiff::OK)
368     Problem("-genbsdiff failed.");
369 
370   WriteSinkToFile(&patch_stream, patch_file);
371 }
372 
ApplyBSDiffPatch(const base::FilePath & old_file,const base::FilePath & patch_file,const base::FilePath & new_file)373 void ApplyBSDiffPatch(const base::FilePath& old_file,
374                       const base::FilePath& patch_file,
375                       const base::FilePath& new_file) {
376   BufferedFileReader old_buffer(old_file, "'old' input");
377   BufferedFileReader patch_buffer(patch_file, "'patch' input");
378 
379   courgette::SourceStream old_stream;
380   courgette::SourceStream patch_stream;
381   old_stream.Init(old_buffer.data(), old_buffer.length());
382   patch_stream.Init(patch_buffer.data(), patch_buffer.length());
383 
384   courgette::SinkStream new_stream;
385   bsdiff::BSDiffStatus status =
386       bsdiff::ApplyBinaryPatch(&old_stream, &patch_stream, &new_stream);
387 
388   if (status != bsdiff::OK)
389     Problem("-applybsdiff failed.");
390 
391   WriteSinkToFile(&new_stream, new_file);
392 }
393 
394 }  // namespace
395 
main(int argc,const char * argv[])396 int main(int argc, const char* argv[]) {
397   base::AtExitManager at_exit_manager;
398   base::CommandLine::Init(argc, argv);
399   const base::CommandLine& command_line =
400       *base::CommandLine::ForCurrentProcess();
401 
402   logging::LoggingSettings settings;
403   if (command_line.HasSwitch("nologfile")) {
404     settings.logging_dest =
405         logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
406   } else {
407     settings.logging_dest = logging::LOG_TO_ALL;
408     settings.log_file_path = FILE_PATH_LITERAL("courgette.log");
409   }
410   (void)logging::InitLogging(settings);
411   logging::SetMinLogLevel(logging::LOG_VERBOSE);
412 
413   bool cmd_sup = command_line.HasSwitch("supported");
414   bool cmd_dis = command_line.HasSwitch("dis");
415   bool cmd_asm = command_line.HasSwitch("asm");
416   bool cmd_disadj = command_line.HasSwitch("disadj");
417   bool cmd_make_patch = command_line.HasSwitch("gen");
418   bool cmd_apply_patch = command_line.HasSwitch("apply");
419   bool cmd_make_bsdiff_patch = command_line.HasSwitch("genbsdiff");
420   bool cmd_apply_bsdiff_patch = command_line.HasSwitch("applybsdiff");
421   bool cmd_spread_1_adjusted = command_line.HasSwitch("gen1a");
422   bool cmd_spread_1_unadjusted = command_line.HasSwitch("gen1u");
423 
424   std::vector<base::FilePath> values;
425   const base::CommandLine::StringVector& args = command_line.GetArgs();
426   for (size_t i = 0; i < args.size(); ++i) {
427     values.push_back(base::FilePath(args[i]));
428   }
429 
430   // '-repeat=N' is for debugging.  Running many iterations can reveal leaks and
431   // bugs in cleanup.
432   int repeat_count = 1;
433   std::string repeat_switch = command_line.GetSwitchValueASCII("repeat");
434   if (!repeat_switch.empty())
435     if (!base::StringToInt(repeat_switch, &repeat_count))
436       repeat_count = 1;
437 
438   if (cmd_sup + cmd_dis + cmd_asm + cmd_disadj + cmd_make_patch +
439           cmd_apply_patch + cmd_make_bsdiff_patch + cmd_apply_bsdiff_patch +
440           cmd_spread_1_adjusted + cmd_spread_1_unadjusted !=
441       1) {
442     UsageProblem(
443         "First argument must be one of:\n"
444         "  -supported, -asm, -dis, -disadj, -gen, -apply, -genbsdiff,"
445         " -applybsdiff, or -gen1[au].");
446   }
447 
448   while (repeat_count-- > 0) {
449     if (cmd_sup) {
450       if (values.size() != 1)
451         UsageProblem(kUsageSupported);
452       return !Supported(values[0]);
453     } else if (cmd_dis) {
454       if (values.size() != 2)
455         UsageProblem(kUsageDis);
456       Disassemble(values[0], values[1]);
457     } else if (cmd_asm) {
458       if (values.size() != 2)
459         UsageProblem(kUsageAsm);
460       Assemble(values[0], values[1]);
461     } else if (cmd_disadj) {
462       if (values.size() != 3)
463         UsageProblem(kUsageDisadj);
464       DisassembleAndAdjust(values[0], values[1], values[2]);
465     } else if (cmd_make_patch) {
466       if (values.size() != 3)
467         UsageProblem(kUsageGen);
468       GenerateEnsemblePatch(values[0], values[1], values[2]);
469     } else if (cmd_apply_patch) {
470       if (values.size() != 3)
471         UsageProblem(kUsageApply);
472       ApplyEnsemblePatch(values[0], values[1], values[2]);
473     } else if (cmd_make_bsdiff_patch) {
474       if (values.size() != 3)
475         UsageProblem(kUsageGenbsdiff);
476       GenerateBSDiffPatch(values[0], values[1], values[2]);
477     } else if (cmd_apply_bsdiff_patch) {
478       if (values.size() != 3)
479         UsageProblem(kUsageApplybsdiff);
480       ApplyBSDiffPatch(values[0], values[1], values[2]);
481     } else if (cmd_spread_1_adjusted || cmd_spread_1_unadjusted) {
482       if (values.size() != 3)
483         UsageProblem(kUsageGen1);
484       DisassembleAdjustDiff(values[0], values[1], values[2],
485                             cmd_spread_1_adjusted);
486     } else {
487       UsageProblem("No operation specified");
488     }
489   }
490 
491   return 0;
492 }
493