| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2009 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 <vector> | |
| 6 #include <string> | |
| 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/logging.h" | |
| 13 #include "base/string_util.h" | |
| 14 | |
| 15 #include "third_party/courgette/bsdiff.h" | |
| 16 #include "third_party/courgette/courgette.h" | |
| 17 #include "third_party/courgette/streams.h" | |
| 18 | |
| 19 | |
| 20 void PrintHelp() { | |
| 21 fprintf(stderr, | |
| 22 "Usage:\n" | |
| 23 " courgette_tool -dis <executable_file> <binary_assembly_file>\n" | |
| 24 " courgette_tool -asm <binary_assembly_file> <executable_file>\n" | |
| 25 " courgette_tool -disadj " | |
| 26 "<executable_file> <reference> <binary_assembly_file>\n" | |
| 27 " courgette_tool -gen <v1> <v2> <patch>\n" | |
| 28 " courgette_tool -apply <v1> <patch> <v2>\n" | |
| 29 "\n"); | |
| 30 } | |
| 31 | |
| 32 void UsageProblem(const char* message) { | |
| 33 fprintf(stderr, "%s", message); | |
| 34 fprintf(stderr, "\n"); | |
| 35 PrintHelp(); | |
| 36 exit(1); | |
| 37 } | |
| 38 | |
| 39 void Problem(const char* format, ...) { | |
| 40 va_list args; | |
| 41 va_start(args, format); | |
| 42 vfprintf(stderr, format, args); | |
| 43 fprintf(stderr, "\n"); | |
| 44 va_end(args); | |
| 45 exit(1); | |
| 46 } | |
| 47 | |
| 48 std::string ReadOrFail(const std::wstring& file_name, const char* kind) { | |
| 49 FilePath file_path(file_name); | |
| 50 std::string buffer; | |
| 51 if (!file_util::ReadFileToString(file_path, &buffer)) | |
| 52 Problem("Can't read %s file.", kind); | |
| 53 return buffer; | |
| 54 } | |
| 55 | |
| 56 void WriteSinkToFile(const courgette::SinkStream *sink, | |
| 57 const std::wstring& output_file) { | |
| 58 FilePath output_path(output_file); | |
| 59 int count = | |
| 60 file_util::WriteFile(output_path, | |
| 61 reinterpret_cast<const char*>(sink->Buffer()), | |
| 62 sink->Length()); | |
| 63 if (count == -1) | |
| 64 Problem("Cant write output."); | |
| 65 if (count != sink->Length()) | |
| 66 Problem("Incomplete write."); | |
| 67 } | |
| 68 | |
| 69 void Disassemble(const std::wstring& input_file, | |
| 70 const std::wstring& output_file) { | |
| 71 std::string buffer = ReadOrFail(input_file, "input"); | |
| 72 | |
| 73 courgette::AssemblyProgram* program = NULL; | |
| 74 const courgette::Status parse_status = | |
| 75 courgette::ParseWin32X86PE(buffer.c_str(), buffer.length(), &program); | |
| 76 | |
| 77 if (parse_status != courgette::C_OK) | |
| 78 Problem("Can't parse input."); | |
| 79 | |
| 80 courgette::EncodedProgram* encoded = NULL; | |
| 81 const courgette::Status encode_status = Encode(program, &encoded); | |
| 82 | |
| 83 courgette::DeleteAssemblyProgram(program); | |
| 84 | |
| 85 if (encode_status != courgette::C_OK) | |
| 86 Problem("Can't encode program."); | |
| 87 | |
| 88 courgette::SinkStreamSet sinks; | |
| 89 | |
| 90 const courgette::Status write_status = | |
| 91 courgette::WriteEncodedProgram(encoded, &sinks); | |
| 92 if (write_status != courgette::C_OK) | |
| 93 Problem("Can't serialize encoded program."); | |
| 94 | |
| 95 courgette::DeleteEncodedProgram(encoded); | |
| 96 | |
| 97 courgette::SinkStream sink; | |
| 98 sinks.CopyTo(&sink); | |
| 99 | |
| 100 WriteSinkToFile(&sink, output_file); | |
| 101 } | |
| 102 | |
| 103 void DisassembleAndAdjust(const std::wstring& program_file, | |
| 104 const std::wstring& model_file, | |
| 105 const std::wstring& output_file) { | |
| 106 std::string program_buffer = ReadOrFail(program_file, "program"); | |
| 107 std::string model_buffer = ReadOrFail(model_file, "reference"); | |
| 108 | |
| 109 courgette::AssemblyProgram* program = NULL; | |
| 110 const courgette::Status parse_program_status = | |
| 111 courgette::ParseWin32X86PE(program_buffer.c_str(), | |
| 112 program_buffer.length(), | |
| 113 &program); | |
| 114 if (parse_program_status != courgette::C_OK) | |
| 115 Problem("Can't parse program input."); | |
| 116 | |
| 117 courgette::AssemblyProgram* model = NULL; | |
| 118 const courgette::Status parse_model_status = | |
| 119 courgette::ParseWin32X86PE(model_buffer.c_str(), | |
| 120 model_buffer.length(), | |
| 121 &model); | |
| 122 if (parse_model_status != courgette::C_OK) | |
| 123 Problem("Can't parse model input."); | |
| 124 | |
| 125 const courgette::Status adjust_status = Adjust(*model, program); | |
| 126 if (adjust_status != courgette::C_OK) | |
| 127 Problem("Can't adjust program."); | |
| 128 | |
| 129 courgette::EncodedProgram* encoded = NULL; | |
| 130 const courgette::Status encode_status = Encode(program, &encoded); | |
| 131 | |
| 132 courgette::DeleteAssemblyProgram(program); | |
| 133 | |
| 134 if (encode_status != courgette::C_OK) | |
| 135 Problem("Can't encode program."); | |
| 136 | |
| 137 courgette::SinkStreamSet sinks; | |
| 138 | |
| 139 const courgette::Status write_status = | |
| 140 courgette::WriteEncodedProgram(encoded, &sinks); | |
| 141 if (write_status != courgette::C_OK) | |
| 142 Problem("Can't serialize encoded program."); | |
| 143 | |
| 144 courgette::DeleteEncodedProgram(encoded); | |
| 145 | |
| 146 courgette::SinkStream sink; | |
| 147 sinks.CopyTo(&sink); | |
| 148 | |
| 149 WriteSinkToFile(&sink, output_file); | |
| 150 } | |
| 151 | |
| 152 // Diffs two executable files, write a set of files for the diff, one file per | |
| 153 // stream of the EncodedProgram format. Each file is the bsdiff between the | |
| 154 // original file's stream and the new file's stream. This is completely | |
| 155 // uninteresting to users, but it is handy for seeing how much each which | |
| 156 // streams are contributing to the final file size. Adjustment is optional. | |
| 157 void DisassembleAdjustDiff(const std::wstring& model_file, | |
| 158 const std::wstring& program_file, | |
| 159 const std::wstring& output_file_root, | |
| 160 bool adjust) { | |
| 161 std::string model_buffer = ReadOrFail(model_file, "'old'"); | |
| 162 std::string program_buffer = ReadOrFail(program_file, "'new'"); | |
| 163 | |
| 164 courgette::AssemblyProgram* model = NULL; | |
| 165 const courgette::Status parse_model_status = | |
| 166 courgette::ParseWin32X86PE(model_buffer.c_str(), | |
| 167 model_buffer.length(), | |
| 168 &model); | |
| 169 if (parse_model_status != courgette::C_OK) | |
| 170 Problem("Can't parse model input."); | |
| 171 | |
| 172 courgette::AssemblyProgram* program = NULL; | |
| 173 const courgette::Status parse_program_status = | |
| 174 courgette::ParseWin32X86PE(program_buffer.c_str(), | |
| 175 program_buffer.length(), | |
| 176 &program); | |
| 177 if (parse_program_status != courgette::C_OK) | |
| 178 Problem("Can't parse program input."); | |
| 179 | |
| 180 if (adjust) { | |
| 181 const courgette::Status adjust_status = Adjust(*model, program); | |
| 182 if (adjust_status != courgette::C_OK) | |
| 183 Problem("Can't adjust program."); | |
| 184 } | |
| 185 | |
| 186 courgette::EncodedProgram* encoded_program = NULL; | |
| 187 const courgette::Status encode_program_status = | |
| 188 Encode(program, &encoded_program); | |
| 189 courgette::DeleteAssemblyProgram(program); | |
| 190 if (encode_program_status != courgette::C_OK) | |
| 191 Problem("Can't encode program."); | |
| 192 | |
| 193 courgette::EncodedProgram* encoded_model = NULL; | |
| 194 const courgette::Status encode_model_status = Encode(model, &encoded_model); | |
| 195 courgette::DeleteAssemblyProgram(model); | |
| 196 if (encode_model_status != courgette::C_OK) | |
| 197 Problem("Can't encode model."); | |
| 198 | |
| 199 courgette::SinkStreamSet program_sinks; | |
| 200 const courgette::Status write_program_status = | |
| 201 courgette::WriteEncodedProgram(encoded_program, &program_sinks); | |
| 202 if (write_program_status != courgette::C_OK) | |
| 203 Problem("Can't serialize encoded program."); | |
| 204 courgette::DeleteEncodedProgram(encoded_program); | |
| 205 | |
| 206 courgette::SinkStreamSet model_sinks; | |
| 207 const courgette::Status write_model_status = | |
| 208 courgette::WriteEncodedProgram(encoded_model, &model_sinks); | |
| 209 if (write_model_status != courgette::C_OK) | |
| 210 Problem("Can't serialize encoded model."); | |
| 211 courgette::DeleteEncodedProgram(encoded_program); | |
| 212 | |
| 213 for (int i = 0; ; ++i) { | |
| 214 courgette::SinkStream* old_stream = model_sinks.stream(i); | |
| 215 courgette::SinkStream* new_stream = program_sinks.stream(i); | |
| 216 if (old_stream == NULL && new_stream == NULL) | |
| 217 break; | |
| 218 | |
| 219 courgette::SourceStream old_source; | |
| 220 courgette::SourceStream new_source; | |
| 221 old_source.Init(*old_stream); | |
| 222 new_source.Init(*new_stream); | |
| 223 courgette::SinkStream patch_stream; | |
| 224 courgette::BSDiffStatus status = | |
| 225 courgette::CreateBinaryPatch(&old_source, &new_source, &patch_stream); | |
| 226 if (status != courgette::OK) Problem("-xxx failed."); | |
| 227 | |
| 228 WriteSinkToFile(&patch_stream, | |
| 229 output_file_root + L"-" + IntToWString(i)); | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 void Assemble(const std::wstring& input_file, | |
| 234 const std::wstring& output_file) { | |
| 235 std::string buffer = ReadOrFail(input_file, "input"); | |
| 236 | |
| 237 courgette::SourceStreamSet sources; | |
| 238 if (!sources.Init(buffer.c_str(), buffer.length())) | |
| 239 Problem("Bad input file."); | |
| 240 | |
| 241 courgette::EncodedProgram* encoded = NULL; | |
| 242 const courgette::Status read_status = ReadEncodedProgram(&sources, &encoded); | |
| 243 if (read_status != courgette::C_OK) | |
| 244 Problem("Bad encoded program."); | |
| 245 | |
| 246 courgette::SinkStream sink; | |
| 247 | |
| 248 const courgette::Status assemble_status = courgette::Assemble(encoded, &sink); | |
| 249 if (assemble_status != courgette::C_OK) | |
| 250 Problem("Can't assemble."); | |
| 251 | |
| 252 WriteSinkToFile(&sink, output_file); | |
| 253 } | |
| 254 | |
| 255 void GenerateEnsemblePatch(const std::wstring& old_file, | |
| 256 const std::wstring& new_file, | |
| 257 const std::wstring& patch_file) { | |
| 258 std::string old_buffer = ReadOrFail(old_file, "'old' input"); | |
| 259 std::string new_buffer = ReadOrFail(new_file, "'new' input"); | |
| 260 | |
| 261 courgette::SourceStream old_stream; | |
| 262 courgette::SourceStream new_stream; | |
| 263 old_stream.Init(old_buffer); | |
| 264 new_stream.Init(new_buffer); | |
| 265 | |
| 266 courgette::SinkStream patch_stream; | |
| 267 courgette::Status status = | |
| 268 courgette::GenerateEnsemblePatch(&old_stream, &new_stream, &patch_stream); | |
| 269 | |
| 270 if (status != courgette::C_OK) Problem("-gen failed."); | |
| 271 | |
| 272 WriteSinkToFile(&patch_stream, patch_file); | |
| 273 } | |
| 274 | |
| 275 void ApplyEnsemblePatch(const std::wstring& old_file, | |
| 276 const std::wstring& patch_file, | |
| 277 const std::wstring& new_file) { | |
| 278 std::string old_buffer = ReadOrFail(old_file, "'old' input"); | |
| 279 std::string patch_buffer = ReadOrFail(patch_file, "'patch' input"); | |
| 280 | |
| 281 courgette::SourceStream old_stream; | |
| 282 courgette::SourceStream patch_stream; | |
| 283 old_stream.Init(old_buffer); | |
| 284 patch_stream.Init(patch_buffer); | |
| 285 courgette::SinkStream new_stream; | |
| 286 courgette::Status status = | |
| 287 courgette::ApplyEnsemblePatch(&old_stream, &patch_stream, &new_stream); | |
| 288 | |
| 289 if (status != courgette::C_OK) Problem("-apply failed."); | |
| 290 | |
| 291 WriteSinkToFile(&new_stream, new_file); | |
| 292 } | |
| 293 | |
| 294 void GenerateBSDiffPatch(const std::wstring& old_file, | |
| 295 const std::wstring& new_file, | |
| 296 const std::wstring& patch_file) { | |
| 297 std::string old_buffer = ReadOrFail(old_file, "'old' input"); | |
| 298 std::string new_buffer = ReadOrFail(new_file, "'new' input"); | |
| 299 | |
| 300 courgette::SourceStream old_stream; | |
| 301 courgette::SourceStream new_stream; | |
| 302 old_stream.Init(old_buffer); | |
| 303 new_stream.Init(new_buffer); | |
| 304 | |
| 305 courgette::SinkStream patch_stream; | |
| 306 courgette::BSDiffStatus status = | |
| 307 courgette::CreateBinaryPatch(&old_stream, &new_stream, &patch_stream); | |
| 308 | |
| 309 if (status != courgette::OK) Problem("-genbsdiff failed."); | |
| 310 | |
| 311 WriteSinkToFile(&patch_stream, patch_file); | |
| 312 } | |
| 313 | |
| 314 void ApplyBSDiffPatch(const std::wstring& old_file, | |
| 315 const std::wstring& patch_file, | |
| 316 const std::wstring& new_file) { | |
| 317 std::string old_buffer = ReadOrFail(old_file, "'old' input"); | |
| 318 std::string patch_buffer = ReadOrFail(patch_file, "'patch' input"); | |
| 319 | |
| 320 courgette::SourceStream old_stream; | |
| 321 courgette::SourceStream patch_stream; | |
| 322 old_stream.Init(old_buffer); | |
| 323 patch_stream.Init(patch_buffer); | |
| 324 | |
| 325 courgette::SinkStream new_stream; | |
| 326 courgette::BSDiffStatus status = | |
| 327 courgette::ApplyBinaryPatch(&old_stream, &patch_stream, &new_stream); | |
| 328 | |
| 329 if (status != courgette::OK) Problem("-applybsdiff failed."); | |
| 330 | |
| 331 WriteSinkToFile(&new_stream, new_file); | |
| 332 } | |
| 333 | |
| 334 int main(int argc, const char* argv[]) { | |
| 335 base::AtExitManager at_exit_manager; | |
| 336 CommandLine::Init(argc, argv); | |
| 337 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 338 | |
| 339 bool cmd_dis = command_line.HasSwitch(L"dis"); | |
| 340 bool cmd_asm = command_line.HasSwitch(L"asm"); | |
| 341 bool cmd_disadj = command_line.HasSwitch(L"disadj"); | |
| 342 bool cmd_make_patch = command_line.HasSwitch(L"gen"); | |
| 343 bool cmd_apply_patch = command_line.HasSwitch(L"apply"); | |
| 344 bool cmd_make_bsdiff_patch = command_line.HasSwitch(L"genbsdiff"); | |
| 345 bool cmd_apply_bsdiff_patch = command_line.HasSwitch(L"applybsdiff"); | |
| 346 bool cmd_spread_1_adjusted = command_line.HasSwitch(L"gen1a"); | |
| 347 bool cmd_spread_1_unadjusted = command_line.HasSwitch(L"gen1u"); | |
| 348 | |
| 349 std::vector<std::wstring> values = command_line.GetLooseValues(); | |
| 350 | |
| 351 // '-repeat=N' is for debugging. Running many iterations can reveal leaks and | |
| 352 // bugs in cleanup. | |
| 353 int repeat_count = 1; | |
| 354 std::wstring repeat_switch = command_line.GetSwitchValue(L"repeat"); | |
| 355 if (!repeat_switch.empty()) | |
| 356 if (!StringToInt(repeat_switch, &repeat_count)) | |
| 357 repeat_count = 1; | |
| 358 | |
| 359 if (cmd_dis + cmd_asm + cmd_disadj + cmd_make_patch + cmd_apply_patch + | |
| 360 cmd_make_bsdiff_patch + cmd_apply_bsdiff_patch + | |
| 361 cmd_spread_1_adjusted + cmd_spread_1_unadjusted | |
| 362 != 1) | |
| 363 UsageProblem( | |
| 364 "Must have exactly one of:\n" | |
| 365 " -asm, -dis, -disadj, -gen or -apply, -genbsdiff or -applybsdiff."); | |
| 366 | |
| 367 while (repeat_count-- > 0) { | |
| 368 if (cmd_dis) { | |
| 369 if (values.size() != 2) | |
| 370 UsageProblem("-dis <executable_file> <courgette_file>"); | |
| 371 Disassemble(values[0], values[1]); | |
| 372 } else if (cmd_asm) { | |
| 373 if (values.size() != 2) | |
| 374 UsageProblem("-asm <courgette_file_input> <executable_file_output>"); | |
| 375 Assemble(values[0], values[1]); | |
| 376 } else if (cmd_disadj) { | |
| 377 if (values.size() != 3) | |
| 378 UsageProblem("-disadj <executable_file> <model> <courgette_file>"); | |
| 379 DisassembleAndAdjust(values[0], values[1], values[2]); | |
| 380 } else if (cmd_make_patch) { | |
| 381 if (values.size() != 3) | |
| 382 UsageProblem("-gen <old_file> <new_file> <patch_file>"); | |
| 383 GenerateEnsemblePatch(values[0], values[1], values[2]); | |
| 384 } else if (cmd_apply_patch) { | |
| 385 if (values.size() != 3) | |
| 386 UsageProblem("-apply <old_file> <patch_file> <new_file>"); | |
| 387 ApplyEnsemblePatch(values[0], values[1], values[2]); | |
| 388 } else if (cmd_make_bsdiff_patch) { | |
| 389 if (values.size() != 3) | |
| 390 UsageProblem("-genbsdiff <old_file> <new_file> <patch_file>"); | |
| 391 GenerateBSDiffPatch(values[0], values[1], values[2]); | |
| 392 } else if (cmd_apply_bsdiff_patch) { | |
| 393 if (values.size() != 3) | |
| 394 UsageProblem("-applybsdiff <old_file> <patch_file> <new_file>"); | |
| 395 ApplyBSDiffPatch(values[0], values[1], values[2]); | |
| 396 } else if (cmd_spread_1_adjusted || cmd_spread_1_unadjusted) { | |
| 397 if (values.size() != 3) | |
| 398 UsageProblem("-gen1[au] <old_file> <new_file> <patch_files_root>"); | |
| 399 DisassembleAdjustDiff(values[0], values[1], values[2], | |
| 400 cmd_spread_1_adjusted); | |
| 401 } else { | |
| 402 UsageProblem("No operation specified"); | |
| 403 } | |
| 404 } | |
| 405 } | |
| OLD | NEW |