Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / courgette / courgette_tool.cc
blobeb3e8237630fd8f8d656c16e33d2ee5ce273b907
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.
5 #include <string>
6 #include <vector>
8 #include "base/at_exit.h"
9 #include "base/basictypes.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.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/courgette_config.h"
19 #include "courgette/streams.h"
20 #include "courgette/third_party/bsdiff.h"
23 void PrintHelp() {
24 fprintf(stderr,
25 "Usage:\n"
26 " courgette -supported <executable_file>\n"
27 " courgette -dis <executable_file> <binary_assembly_file>\n"
28 " courgette -asm <binary_assembly_file> <executable_file>\n"
29 " courgette -disadj <executable_file> <reference> <binary_assembly_file>\n"
30 " courgette -gen <v1> <v2> <patch>\n"
31 " courgette -apply <v1> <patch> <v2>\n"
32 "\n");
35 void UsageProblem(const char* message) {
36 fprintf(stderr, "%s", message);
37 fprintf(stderr, "\n");
38 PrintHelp();
39 exit(1);
42 void Problem(const char* format, ...) {
43 va_list args;
44 va_start(args, format);
45 vfprintf(stderr, format, args);
46 fprintf(stderr, "\n");
47 va_end(args);
48 exit(1);
51 std::string ReadOrFail(const base::FilePath& file_name, const char* kind) {
52 int64 file_size = 0;
53 if (!base::GetFileSize(file_name, &file_size))
54 Problem("Can't read %s file.", kind);
55 std::string buffer;
56 buffer.reserve(static_cast<size_t>(file_size));
57 if (!base::ReadFileToString(file_name, &buffer))
58 Problem("Can't read %s file.", kind);
59 return buffer;
62 void WriteSinkToFile(const courgette::SinkStream *sink,
63 const base::FilePath& output_file) {
64 int count =
65 base::WriteFile(output_file,
66 reinterpret_cast<const char*>(sink->Buffer()),
67 static_cast<int>(sink->Length()));
68 if (count == -1)
69 Problem("Can't write output.");
70 if (static_cast<size_t>(count) != sink->Length())
71 Problem("Incomplete write.");
74 void Disassemble(const base::FilePath& input_file,
75 const base::FilePath& output_file) {
76 std::string buffer = ReadOrFail(input_file, "input");
78 courgette::AssemblyProgram* program = NULL;
79 const courgette::Status parse_status =
80 courgette::ParseDetectedExecutable(buffer.c_str(), buffer.length(),
81 &program);
83 if (parse_status != courgette::C_OK)
84 Problem("Can't parse input.");
86 courgette::EncodedProgram* encoded = NULL;
87 const courgette::Status encode_status = Encode(program, &encoded);
89 courgette::DeleteAssemblyProgram(program);
91 if (encode_status != courgette::C_OK)
92 Problem("Can't encode program.");
94 courgette::SinkStreamSet sinks;
96 const courgette::Status write_status =
97 courgette::WriteEncodedProgram(encoded, &sinks);
98 if (write_status != courgette::C_OK)
99 Problem("Can't serialize encoded program.");
101 courgette::DeleteEncodedProgram(encoded);
103 courgette::SinkStream sink;
104 if (!sinks.CopyTo(&sink))
105 Problem("Can't combine serialized encoded program streams.");
107 WriteSinkToFile(&sink, output_file);
110 bool Supported(const base::FilePath& input_file) {
111 bool result = false;
113 std::string buffer = ReadOrFail(input_file, "input");
115 courgette::ExecutableType type;
116 size_t detected_length;
118 DetectExecutableType(buffer.c_str(), buffer.length(),
119 &type,
120 &detected_length);
122 // If the detection fails, we just fall back on UNKNOWN
123 std::string format = "Unsupported";
125 switch (type)
127 case courgette::EXE_UNKNOWN:
128 break;
130 case courgette::EXE_WIN_32_X86:
131 format = "Windows 32 PE";
132 result = true;
133 break;
135 case courgette::EXE_ELF_32_X86:
136 format = "ELF 32 X86";
137 result = true;
138 break;
140 case courgette::EXE_ELF_32_ARM:
141 format = "ELF 32 ARM";
142 result = true;
143 break;
145 case courgette::EXE_WIN_32_X64:
146 format = "Windows 64 PE";
147 result = true;
148 break;
151 printf("%s Executable\n", format.c_str());
152 return result;
155 void DisassembleAndAdjust(const base::FilePath& program_file,
156 const base::FilePath& model_file,
157 const base::FilePath& output_file) {
158 std::string program_buffer = ReadOrFail(program_file, "program");
159 std::string model_buffer = ReadOrFail(model_file, "reference");
161 courgette::AssemblyProgram* program = NULL;
162 const courgette::Status parse_program_status =
163 courgette::ParseDetectedExecutable(program_buffer.c_str(),
164 program_buffer.length(),
165 &program);
166 if (parse_program_status != courgette::C_OK)
167 Problem("Can't parse program input.");
169 courgette::AssemblyProgram* model = NULL;
170 const courgette::Status parse_model_status =
171 courgette::ParseDetectedExecutable(model_buffer.c_str(),
172 model_buffer.length(),
173 &model);
174 if (parse_model_status != courgette::C_OK)
175 Problem("Can't parse model input.");
177 const courgette::Status adjust_status = Adjust(*model, program);
178 if (adjust_status != courgette::C_OK)
179 Problem("Can't adjust program.");
181 courgette::EncodedProgram* encoded = NULL;
182 const courgette::Status encode_status = Encode(program, &encoded);
184 courgette::DeleteAssemblyProgram(program);
186 if (encode_status != courgette::C_OK)
187 Problem("Can't encode program.");
189 courgette::SinkStreamSet sinks;
191 const courgette::Status write_status =
192 courgette::WriteEncodedProgram(encoded, &sinks);
193 if (write_status != courgette::C_OK)
194 Problem("Can't serialize encoded program.");
196 courgette::DeleteEncodedProgram(encoded);
198 courgette::SinkStream sink;
199 if (!sinks.CopyTo(&sink))
200 Problem("Can't combine serialized encoded program streams.");
202 WriteSinkToFile(&sink, output_file);
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& model_file,
211 const base::FilePath& program_file,
212 const base::FilePath& output_file_root,
213 bool adjust) {
214 std::string model_buffer = ReadOrFail(model_file, "'old'");
215 std::string program_buffer = ReadOrFail(program_file, "'new'");
217 courgette::AssemblyProgram* model = NULL;
218 const courgette::Status parse_model_status =
219 courgette::ParseDetectedExecutable(model_buffer.c_str(),
220 model_buffer.length(),
221 &model);
222 if (parse_model_status != courgette::C_OK)
223 Problem("Can't parse model input.");
225 courgette::AssemblyProgram* program = NULL;
226 const courgette::Status parse_program_status =
227 courgette::ParseDetectedExecutable(program_buffer.c_str(),
228 program_buffer.length(),
229 &program);
230 if (parse_program_status != courgette::C_OK)
231 Problem("Can't parse program input.");
233 if (adjust) {
234 const courgette::Status adjust_status = Adjust(*model, program);
235 if (adjust_status != courgette::C_OK)
236 Problem("Can't adjust program.");
239 courgette::EncodedProgram* encoded_program = NULL;
240 const courgette::Status encode_program_status =
241 Encode(program, &encoded_program);
242 courgette::DeleteAssemblyProgram(program);
243 if (encode_program_status != courgette::C_OK)
244 Problem("Can't encode program.");
246 courgette::EncodedProgram* encoded_model = NULL;
247 const courgette::Status encode_model_status = Encode(model, &encoded_model);
248 courgette::DeleteAssemblyProgram(model);
249 if (encode_model_status != courgette::C_OK)
250 Problem("Can't encode model.");
252 courgette::SinkStreamSet program_sinks;
253 const courgette::Status write_program_status =
254 courgette::WriteEncodedProgram(encoded_program, &program_sinks);
255 if (write_program_status != courgette::C_OK)
256 Problem("Can't serialize encoded program.");
257 courgette::DeleteEncodedProgram(encoded_program);
259 courgette::SinkStreamSet model_sinks;
260 const courgette::Status write_model_status =
261 courgette::WriteEncodedProgram(encoded_model, &model_sinks);
262 if (write_model_status != courgette::C_OK)
263 Problem("Can't serialize encoded model.");
264 courgette::DeleteEncodedProgram(encoded_model);
266 courgette::SinkStream empty_sink;
267 for (int i = 0; ; ++i) {
268 courgette::SinkStream* old_stream = model_sinks.stream(i);
269 courgette::SinkStream* new_stream = program_sinks.stream(i);
270 if (old_stream == NULL && new_stream == NULL)
271 break;
273 courgette::SourceStream old_source;
274 courgette::SourceStream new_source;
275 old_source.Init(old_stream ? *old_stream : empty_sink);
276 new_source.Init(new_stream ? *new_stream : empty_sink);
277 courgette::SinkStream patch_stream;
278 courgette::BSDiffStatus status =
279 courgette::CreateBinaryPatch(&old_source, &new_source, &patch_stream);
280 if (status != courgette::OK) Problem("-xxx failed.");
282 std::string append = std::string("-") + base::IntToString(i);
284 WriteSinkToFile(&patch_stream,
285 output_file_root.InsertBeforeExtensionASCII(append));
289 void Assemble(const base::FilePath& input_file,
290 const base::FilePath& output_file) {
291 std::string buffer = ReadOrFail(input_file, "input");
293 courgette::SourceStreamSet sources;
294 if (!sources.Init(buffer.c_str(), buffer.length()))
295 Problem("Bad input file.");
297 courgette::EncodedProgram* encoded = NULL;
298 const courgette::Status read_status = ReadEncodedProgram(&sources, &encoded);
299 if (read_status != courgette::C_OK)
300 Problem("Bad encoded program.");
302 courgette::SinkStream sink;
304 const courgette::Status assemble_status = courgette::Assemble(encoded, &sink);
305 if (assemble_status != courgette::C_OK)
306 Problem("Can't assemble.");
308 WriteSinkToFile(&sink, output_file);
311 void GenerateEnsemblePatch(const base::FilePath& old_file,
312 const base::FilePath& new_file,
313 const base::FilePath& patch_file) {
314 std::string old_buffer = ReadOrFail(old_file, "'old' input");
315 std::string new_buffer = ReadOrFail(new_file, "'new' input");
317 courgette::SourceStream old_stream;
318 courgette::SourceStream new_stream;
319 old_stream.Init(old_buffer);
320 new_stream.Init(new_buffer);
322 courgette::SinkStream patch_stream;
323 courgette::Status status =
324 courgette::GenerateEnsemblePatch(&old_stream, &new_stream, &patch_stream);
326 if (status != courgette::C_OK) Problem("-gen failed.");
328 WriteSinkToFile(&patch_stream, patch_file);
331 void ApplyEnsemblePatch(const base::FilePath& old_file,
332 const base::FilePath& patch_file,
333 const base::FilePath& new_file) {
334 // We do things a little differently here in order to call the same Courgette
335 // entry point as the installer. That entry point point takes file names and
336 // returns an status code but does not output any diagnostics.
338 courgette::Status status =
339 courgette::ApplyEnsemblePatch(old_file.value().c_str(),
340 patch_file.value().c_str(),
341 new_file.value().c_str());
343 if (status == courgette::C_OK)
344 return;
346 // Diagnose the error.
347 switch (status) {
348 case courgette::C_BAD_ENSEMBLE_MAGIC:
349 Problem("Not a courgette patch");
350 break;
352 case courgette::C_BAD_ENSEMBLE_VERSION:
353 Problem("Wrong version patch");
354 break;
356 case courgette::C_BAD_ENSEMBLE_HEADER:
357 Problem("Corrupt patch");
358 break;
360 case courgette::C_DISASSEMBLY_FAILED:
361 Problem("Disassembly failed (could be because of memory issues)");
362 break;
364 case courgette::C_STREAM_ERROR:
365 Problem("Stream error (likely out of memory or disk space)");
366 break;
368 default:
369 break;
372 // If we failed due to a missing input file, this will
373 // print the message.
374 std::string old_buffer = ReadOrFail(old_file, "'old' input");
375 old_buffer.clear();
376 std::string patch_buffer = ReadOrFail(patch_file, "'patch' input");
377 patch_buffer.clear();
379 // Non-input related errors:
380 if (status == courgette::C_WRITE_OPEN_ERROR)
381 Problem("Can't open output");
382 if (status == courgette::C_WRITE_ERROR)
383 Problem("Can't write output");
385 Problem("-apply failed.");
388 void GenerateBSDiffPatch(const base::FilePath& old_file,
389 const base::FilePath& new_file,
390 const base::FilePath& patch_file) {
391 std::string old_buffer = ReadOrFail(old_file, "'old' input");
392 std::string new_buffer = ReadOrFail(new_file, "'new' input");
394 courgette::SourceStream old_stream;
395 courgette::SourceStream new_stream;
396 old_stream.Init(old_buffer);
397 new_stream.Init(new_buffer);
399 courgette::SinkStream patch_stream;
400 courgette::BSDiffStatus status =
401 courgette::CreateBinaryPatch(&old_stream, &new_stream, &patch_stream);
403 if (status != courgette::OK) Problem("-genbsdiff failed.");
405 WriteSinkToFile(&patch_stream, patch_file);
408 void ApplyBSDiffPatch(const base::FilePath& old_file,
409 const base::FilePath& patch_file,
410 const base::FilePath& new_file) {
411 std::string old_buffer = ReadOrFail(old_file, "'old' input");
412 std::string patch_buffer = ReadOrFail(patch_file, "'patch' input");
414 courgette::SourceStream old_stream;
415 courgette::SourceStream patch_stream;
416 old_stream.Init(old_buffer);
417 patch_stream.Init(patch_buffer);
419 courgette::SinkStream new_stream;
420 courgette::BSDiffStatus status =
421 courgette::ApplyBinaryPatch(&old_stream, &patch_stream, &new_stream);
423 if (status != courgette::OK) Problem("-applybsdiff failed.");
425 WriteSinkToFile(&new_stream, new_file);
428 int main(int argc, const char* argv[]) {
429 base::AtExitManager at_exit_manager;
430 base::CommandLine::Init(argc, argv);
431 const base::CommandLine& command_line =
432 *base::CommandLine::ForCurrentProcess();
433 courgette::CourgetteConfig::GetInstance()->Initialize(command_line);
435 logging::LoggingSettings settings;
436 settings.logging_dest = logging::LOG_TO_ALL;
437 settings.log_file = FILE_PATH_LITERAL("courgette.log");
438 (void)logging::InitLogging(settings);
439 logging::SetMinLogLevel(logging::LOG_VERBOSE);
441 bool cmd_sup = command_line.HasSwitch("supported");
442 bool cmd_dis = command_line.HasSwitch("dis");
443 bool cmd_asm = command_line.HasSwitch("asm");
444 bool cmd_disadj = command_line.HasSwitch("disadj");
445 bool cmd_make_patch = command_line.HasSwitch("gen");
446 bool cmd_apply_patch = command_line.HasSwitch("apply");
447 bool cmd_make_bsdiff_patch = command_line.HasSwitch("genbsdiff");
448 bool cmd_apply_bsdiff_patch = command_line.HasSwitch("applybsdiff");
449 bool cmd_spread_1_adjusted = command_line.HasSwitch("gen1a");
450 bool cmd_spread_1_unadjusted = command_line.HasSwitch("gen1u");
452 std::vector<base::FilePath> values;
453 const base::CommandLine::StringVector& args = command_line.GetArgs();
454 for (size_t i = 0; i < args.size(); ++i) {
455 values.push_back(base::FilePath(args[i]));
458 // '-repeat=N' is for debugging. Running many iterations can reveal leaks and
459 // bugs in cleanup.
460 int repeat_count = 1;
461 std::string repeat_switch = command_line.GetSwitchValueASCII("repeat");
462 if (!repeat_switch.empty())
463 if (!base::StringToInt(repeat_switch, &repeat_count))
464 repeat_count = 1;
466 if (cmd_sup + cmd_dis + cmd_asm + cmd_disadj + cmd_make_patch +
467 cmd_apply_patch + cmd_make_bsdiff_patch + cmd_apply_bsdiff_patch +
468 cmd_spread_1_adjusted + cmd_spread_1_unadjusted
469 != 1)
470 UsageProblem(
471 "Must have exactly one of:\n"
472 " -supported -asm, -dis, -disadj, -gen or -apply, -genbsdiff"
473 " or -applybsdiff.");
475 if (courgette::CourgetteConfig::GetInstance()->is_experimental()) {
476 fprintf(stderr, "Experimental flag enabled. Do not use in production.\n");
479 while (repeat_count-- > 0) {
480 if (cmd_sup) {
481 if (values.size() != 1)
482 UsageProblem("-supported <executable_file>");
483 return !Supported(values[0]);
484 } else if (cmd_dis) {
485 if (values.size() != 2)
486 UsageProblem("-dis <executable_file> <courgette_file>");
487 Disassemble(values[0], values[1]);
488 } else if (cmd_asm) {
489 if (values.size() != 2)
490 UsageProblem("-asm <courgette_file_input> <executable_file_output>");
491 Assemble(values[0], values[1]);
492 } else if (cmd_disadj) {
493 if (values.size() != 3)
494 UsageProblem("-disadj <executable_file> <model> <courgette_file>");
495 DisassembleAndAdjust(values[0], values[1], values[2]);
496 } else if (cmd_make_patch) {
497 if (values.size() != 3)
498 UsageProblem("-gen <old_file> <new_file> <patch_file>");
499 GenerateEnsemblePatch(values[0], values[1], values[2]);
500 } else if (cmd_apply_patch) {
501 if (values.size() != 3)
502 UsageProblem("-apply <old_file> <patch_file> <new_file>");
503 ApplyEnsemblePatch(values[0], values[1], values[2]);
504 } else if (cmd_make_bsdiff_patch) {
505 if (values.size() != 3)
506 UsageProblem("-genbsdiff <old_file> <new_file> <patch_file>");
507 GenerateBSDiffPatch(values[0], values[1], values[2]);
508 } else if (cmd_apply_bsdiff_patch) {
509 if (values.size() != 3)
510 UsageProblem("-applybsdiff <old_file> <patch_file> <new_file>");
511 ApplyBSDiffPatch(values[0], values[1], values[2]);
512 } else if (cmd_spread_1_adjusted || cmd_spread_1_unadjusted) {
513 if (values.size() != 3)
514 UsageProblem("-gen1[au] <old_file> <new_file> <patch_files_root>");
515 DisassembleAdjustDiff(values[0], values[1], values[2],
516 cmd_spread_1_adjusted);
517 } else {
518 UsageProblem("No operation specified");
522 return 0;