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