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