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 (!base::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 } 517