patchoat_test.cc revision 3856af0d6e09525a4e774bec729dd781a72d5549
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <openssl/sha.h> 18#include <dirent.h> 19#include <sys/types.h> 20 21#include <string> 22#include <vector> 23 24#include "android-base/stringprintf.h" 25#include "android-base/strings.h" 26 27#include "dexopt_test.h" 28#include "leb128.h" 29#include "runtime.h" 30 31#include <gtest/gtest.h> 32 33namespace art { 34 35using android::base::StringPrintf; 36 37class PatchoatTest : public DexoptTest { 38 public: 39 static bool ListDirFilesEndingWith( 40 const std::string& dir, 41 const std::string& suffix, 42 std::vector<std::string>* filenames, 43 std::string* error_msg) { 44 DIR* d = opendir(dir.c_str()); 45 if (d == nullptr) { 46 *error_msg = "Failed to open directory"; 47 return false; 48 } 49 dirent* e; 50 struct stat s; 51 size_t suffix_len = suffix.size(); 52 while ((e = readdir(d)) != nullptr) { 53 if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) { 54 continue; 55 } 56 size_t name_len = strlen(e->d_name); 57 if ((name_len < suffix_len) || (strcmp(&e->d_name[name_len - suffix_len], suffix.c_str()))) { 58 continue; 59 } 60 std::string basename(e->d_name); 61 std::string filename = dir + "/" + basename; 62 int stat_result = lstat(filename.c_str(), &s); 63 if (stat_result != 0) { 64 *error_msg = 65 StringPrintf("Failed to stat %s: stat returned %d", filename.c_str(), stat_result); 66 return false; 67 } 68 if (S_ISDIR(s.st_mode)) { 69 continue; 70 } 71 filenames->push_back(basename); 72 } 73 closedir(d); 74 return true; 75 } 76 77 static void AddRuntimeArg(std::vector<std::string>& args, const std::string& arg) { 78 args.push_back("--runtime-arg"); 79 args.push_back(arg); 80 } 81 82 bool CompileBootImage(const std::vector<std::string>& extra_args, 83 const std::string& image_file_name_prefix, 84 uint32_t base_addr, 85 std::string* error_msg) { 86 Runtime* const runtime = Runtime::Current(); 87 std::vector<std::string> argv; 88 argv.push_back(runtime->GetCompilerExecutable()); 89 AddRuntimeArg(argv, "-Xms64m"); 90 AddRuntimeArg(argv, "-Xmx64m"); 91 std::vector<std::string> dex_files = GetLibCoreDexFileNames(); 92 for (const std::string& dex_file : dex_files) { 93 argv.push_back("--dex-file=" + dex_file); 94 argv.push_back("--dex-location=" + dex_file); 95 } 96 if (runtime->IsJavaDebuggable()) { 97 argv.push_back("--debuggable"); 98 } 99 runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv); 100 101 AddRuntimeArg(argv, "-Xverify:softfail"); 102 103 if (!kIsTargetBuild) { 104 argv.push_back("--host"); 105 } 106 107 argv.push_back("--image=" + image_file_name_prefix + ".art"); 108 argv.push_back("--oat-file=" + image_file_name_prefix + ".oat"); 109 argv.push_back("--oat-location=" + image_file_name_prefix + ".oat"); 110 argv.push_back(StringPrintf("--base=0x%" PRIx32, base_addr)); 111 argv.push_back("--compile-pic"); 112 argv.push_back("--multi-image"); 113 argv.push_back("--no-generate-debug-info"); 114 115 std::vector<std::string> compiler_options = runtime->GetCompilerOptions(); 116 argv.insert(argv.end(), compiler_options.begin(), compiler_options.end()); 117 118 // We must set --android-root. 119 const char* android_root = getenv("ANDROID_ROOT"); 120 CHECK(android_root != nullptr); 121 argv.push_back("--android-root=" + std::string(android_root)); 122 argv.insert(argv.end(), extra_args.begin(), extra_args.end()); 123 124 return RunDex2OatOrPatchoat(argv, error_msg); 125 } 126 127 bool RelocateBootImage(const std::string& input_image_location, 128 const std::string& output_image_filename, 129 off_t base_offset_delta, 130 std::string* error_msg) { 131 Runtime* const runtime = Runtime::Current(); 132 std::vector<std::string> argv; 133 argv.push_back(runtime->GetPatchoatExecutable()); 134 argv.push_back("--input-image-location=" + input_image_location); 135 argv.push_back("--output-image-file=" + output_image_filename); 136 argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta)); 137 argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA))); 138 139 return RunDex2OatOrPatchoat(argv, error_msg); 140 } 141 142 bool GenerateBootImageRelFile(const std::string& input_image_location, 143 const std::string& output_rel_filename, 144 off_t base_offset_delta, 145 std::string* error_msg) { 146 Runtime* const runtime = Runtime::Current(); 147 std::vector<std::string> argv; 148 argv.push_back(runtime->GetPatchoatExecutable()); 149 argv.push_back("--input-image-location=" + input_image_location); 150 argv.push_back("--output-image-relocation-file=" + output_rel_filename); 151 argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta)); 152 argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA))); 153 154 return RunDex2OatOrPatchoat(argv, error_msg); 155 } 156 157 bool RunDex2OatOrPatchoat(const std::vector<std::string>& args, std::string* error_msg) { 158 int link[2]; 159 160 if (pipe(link) == -1) { 161 return false; 162 } 163 164 pid_t pid = fork(); 165 if (pid == -1) { 166 return false; 167 } 168 169 if (pid == 0) { 170 // We need dex2oat to actually log things. 171 setenv("ANDROID_LOG_TAGS", "*:e", 1); 172 dup2(link[1], STDERR_FILENO); 173 close(link[0]); 174 close(link[1]); 175 std::vector<const char*> c_args; 176 for (const std::string& str : args) { 177 c_args.push_back(str.c_str()); 178 } 179 c_args.push_back(nullptr); 180 execv(c_args[0], const_cast<char* const*>(c_args.data())); 181 exit(1); 182 UNREACHABLE(); 183 } else { 184 close(link[1]); 185 char buffer[128]; 186 memset(buffer, 0, 128); 187 ssize_t bytes_read = 0; 188 189 while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) { 190 *error_msg += std::string(buffer, bytes_read); 191 } 192 close(link[0]); 193 int status = -1; 194 if (waitpid(pid, &status, 0) != -1) { 195 return (status == 0); 196 } 197 return false; 198 } 199 } 200 201 bool CompileBootImageToDir( 202 const std::string& output_dir, 203 const std::vector<std::string>& dex2oat_extra_args, 204 uint32_t base_addr, 205 std::string* error_msg) { 206 return CompileBootImage(dex2oat_extra_args, output_dir + "/boot", base_addr, error_msg); 207 } 208 209 bool CopyImageChecksumAndSetPatchDelta( 210 const std::string& src_image_filename, 211 const std::string& dest_image_filename, 212 off_t dest_patch_delta, 213 std::string* error_msg) { 214 std::unique_ptr<File> src_file(OS::OpenFileForReading(src_image_filename.c_str())); 215 if (src_file.get() == nullptr) { 216 *error_msg = StringPrintf("Failed to open source image file %s", src_image_filename.c_str()); 217 return false; 218 } 219 ImageHeader src_header; 220 if (!src_file->ReadFully(&src_header, sizeof(src_header))) { 221 *error_msg = StringPrintf("Failed to read source image file %s", src_image_filename.c_str()); 222 return false; 223 } 224 225 std::unique_ptr<File> dest_file(OS::OpenFileReadWrite(dest_image_filename.c_str())); 226 if (dest_file.get() == nullptr) { 227 *error_msg = 228 StringPrintf("Failed to open destination image file %s", dest_image_filename.c_str()); 229 return false; 230 } 231 ImageHeader dest_header; 232 if (!dest_file->ReadFully(&dest_header, sizeof(dest_header))) { 233 *error_msg = 234 StringPrintf("Failed to read destination image file %s", dest_image_filename.c_str()); 235 return false; 236 } 237 dest_header.SetOatChecksum(src_header.GetOatChecksum()); 238 dest_header.SetPatchDelta(dest_patch_delta); 239 if (!dest_file->ResetOffset()) { 240 *error_msg = 241 StringPrintf( 242 "Failed to seek to start of destination image file %s", dest_image_filename.c_str()); 243 return false; 244 } 245 if (!dest_file->WriteFully(&dest_header, sizeof(dest_header))) { 246 *error_msg = 247 StringPrintf("Failed to write to destination image file %s", dest_image_filename.c_str()); 248 dest_file->Erase(); 249 return false; 250 } 251 if (dest_file->FlushCloseOrErase() != 0) { 252 *error_msg = 253 StringPrintf( 254 "Failed to flush/close destination image file %s", dest_image_filename.c_str()); 255 return false; 256 } 257 258 return true; 259 } 260 261 bool ReadFully( 262 const std::string& filename, std::vector<uint8_t>* contents, std::string* error_msg) { 263 std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str())); 264 if (file.get() == nullptr) { 265 *error_msg = "Failed to open"; 266 return false; 267 } 268 int64_t size = file->GetLength(); 269 if (size < 0) { 270 *error_msg = "Failed to get size"; 271 return false; 272 } 273 contents->resize(size); 274 if (!file->ReadFully(&(*contents)[0], size)) { 275 *error_msg = "Failed to read"; 276 contents->clear(); 277 return false; 278 } 279 return true; 280 } 281 282 bool BinaryDiff( 283 const std::string& filename1, 284 const std::vector<uint8_t>& data1, 285 const std::string& filename2, 286 const std::vector<uint8_t>& data2, 287 std::string* error_msg) { 288 if (data1.size() != data1.size()) { 289 *error_msg = 290 StringPrintf( 291 "%s and %s are of different size: %zu vs %zu", 292 filename1.c_str(), 293 filename2.c_str(), 294 data1.size(), 295 data2.size()); 296 return true; 297 } 298 size_t size = data1.size(); 299 for (size_t i = 0; i < size; i++) { 300 if (data1[i] != data2[i]) { 301 *error_msg = 302 StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i); 303 return true; 304 } 305 } 306 307 return false; 308 } 309 310 bool BinaryDiff( 311 const std::string& filename1, const std::string& filename2, std::string* error_msg) { 312 std::string read_error_msg; 313 std::vector<uint8_t> image1; 314 if (!ReadFully(filename1, &image1, &read_error_msg)) { 315 *error_msg = StringPrintf("Failed to read %s: %s", filename1.c_str(), read_error_msg.c_str()); 316 return true; 317 } 318 std::vector<uint8_t> image2; 319 if (!ReadFully(filename2, &image2, &read_error_msg)) { 320 *error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str()); 321 return true; 322 } 323 return BinaryDiff(filename1, image1, filename2, image2, error_msg); 324 } 325 326 bool IsImageIdenticalToOriginalExceptForRelocation( 327 const std::string& relocated_filename, 328 const std::string& original_filename, 329 const std::string& rel_filename, 330 std::string* error_msg) { 331 *error_msg = ""; 332 std::string read_error_msg; 333 std::vector<uint8_t> rel; 334 if (!ReadFully(rel_filename, &rel, &read_error_msg)) { 335 *error_msg = 336 StringPrintf("Failed to read %s: %s", rel_filename.c_str(), read_error_msg.c_str()); 337 return false; 338 } 339 std::vector<uint8_t> relocated; 340 if (!ReadFully(relocated_filename, &relocated, &read_error_msg)) { 341 *error_msg = 342 StringPrintf("Failed to read %s: %s", relocated_filename.c_str(), read_error_msg.c_str()); 343 return false; 344 } 345 346 size_t image_size = relocated.size(); 347 if ((image_size % 4) != 0) { 348 *error_msg = 349 StringPrintf( 350 "Relocated image file %s size not multiple of 4: %zu", 351 relocated_filename.c_str(), image_size); 352 return false; 353 } 354 if (image_size > UINT32_MAX) { 355 *error_msg = 356 StringPrintf( 357 "Relocated image file %s too large: %zu" , relocated_filename.c_str(), image_size); 358 return false; 359 } 360 361 const ImageHeader& relocated_header = *reinterpret_cast<const ImageHeader*>(relocated.data()); 362 off_t expected_diff = relocated_header.GetPatchDelta(); 363 364 if (expected_diff != 0) { 365 // Relocated image is expected to differ from the original due to relocation. 366 // Unrelocate the image in memory to compensate. 367 uint8_t* image_start = relocated.data(); 368 const uint8_t* rel_end = &rel[rel.size()]; 369 if (rel.size() < SHA256_DIGEST_LENGTH) { 370 *error_msg = 371 StringPrintf("Malformed image relocation file %s: too short", rel_filename.c_str()); 372 return false; 373 } 374 const uint8_t* rel_ptr = &rel[SHA256_DIGEST_LENGTH]; 375 // The remaining .rel file consists of offsets at which relocation should've occurred. 376 // For each offset, we "unrelocate" the image by subtracting the expected relocation 377 // diff value (as specified in the image header). 378 // 379 // Each offset is encoded as a delta/diff relative to the previous offset. With the 380 // very first offset being encoded relative to offset 0. 381 // Deltas are encoded using little-endian 7 bits per byte encoding, with all bytes except 382 // the last one having the highest bit set. 383 uint32_t offset = 0; 384 while (rel_ptr != rel_end) { 385 uint32_t offset_delta = 0; 386 if (DecodeUnsignedLeb128Checked(&rel_ptr, rel_end, &offset_delta)) { 387 offset += offset_delta; 388 uint32_t *image_value = reinterpret_cast<uint32_t*>(image_start + offset); 389 *image_value -= expected_diff; 390 } else { 391 *error_msg = 392 StringPrintf( 393 "Malformed image relocation file %s: " 394 "last byte has it's most significant bit set", 395 rel_filename.c_str()); 396 return false; 397 } 398 } 399 } 400 401 // Image in memory is now supposed to be identical to the original. Compare it to the original. 402 std::vector<uint8_t> original; 403 if (!ReadFully(original_filename, &original, &read_error_msg)) { 404 *error_msg = 405 StringPrintf("Failed to read %s: %s", original_filename.c_str(), read_error_msg.c_str()); 406 return false; 407 } 408 if (BinaryDiff(relocated_filename, relocated, original_filename, original, error_msg)) { 409 return false; 410 } 411 412 // Relocated image is identical to the original, once relocations are taken into account 413 return true; 414 } 415}; 416 417TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) { 418#if defined(ART_USE_READ_BARRIER) 419 // This test checks that relocating a boot image using patchoat produces the same result as 420 // producing the boot image for that relocated base address using dex2oat. To be precise, these 421 // two files will have two small differences: the OAT checksum and base address. However, this 422 // test takes this into account. 423 424 // Compile boot image into a random directory using dex2oat 425 ScratchFile dex2oat_orig_scratch; 426 dex2oat_orig_scratch.Unlink(); 427 std::string dex2oat_orig_dir = dex2oat_orig_scratch.GetFilename(); 428 ASSERT_EQ(0, mkdir(dex2oat_orig_dir.c_str(), 0700)); 429 const uint32_t orig_base_addr = 0x60000000; 430 // Force deterministic output. We want the boot images created by this dex2oat run and the run 431 // below to differ only in their base address. 432 std::vector<std::string> dex2oat_extra_args; 433 dex2oat_extra_args.push_back("--force-determinism"); 434 dex2oat_extra_args.push_back("-j1"); // Might not be needed. Causes a 3-5x slowdown. 435 std::string error_msg; 436 if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { 437 FAIL() << "CompileBootImage1 failed: " << error_msg; 438 } 439 440 // Compile a "relocated" boot image into a random directory using dex2oat. This image is relocated 441 // in the sense that it uses a different base address. 442 ScratchFile dex2oat_reloc_scratch; 443 dex2oat_reloc_scratch.Unlink(); 444 std::string dex2oat_reloc_dir = dex2oat_reloc_scratch.GetFilename(); 445 ASSERT_EQ(0, mkdir(dex2oat_reloc_dir.c_str(), 0700)); 446 const uint32_t reloc_base_addr = 0x70000000; 447 if (!CompileBootImageToDir(dex2oat_reloc_dir, dex2oat_extra_args, reloc_base_addr, &error_msg)) { 448 FAIL() << "CompileBootImage2 failed: " << error_msg; 449 } 450 const off_t base_addr_delta = reloc_base_addr - orig_base_addr; 451 452 // Relocate the original boot image using patchoat. The image is relocated by the same amount 453 // as the second/relocated image produced by dex2oat. 454 ScratchFile patchoat_scratch; 455 patchoat_scratch.Unlink(); 456 std::string patchoat_dir = patchoat_scratch.GetFilename(); 457 ASSERT_EQ(0, mkdir(patchoat_dir.c_str(), 0700)); 458 std::string dex2oat_orig_with_arch_dir = 459 dex2oat_orig_dir + "/" + GetInstructionSetString(kRuntimeISA); 460 // The arch-including symlink is needed by patchoat 461 ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str())); 462 if (!RelocateBootImage( 463 dex2oat_orig_dir + "/boot.art", 464 patchoat_dir + "/boot.art", 465 base_addr_delta, 466 &error_msg)) { 467 FAIL() << "RelocateBootImage failed: " << error_msg; 468 } 469 470 // Assert that patchoat created the same set of .art files as dex2oat 471 std::vector<std::string> dex2oat_image_basenames; 472 std::vector<std::string> patchoat_image_basenames; 473 if (!ListDirFilesEndingWith(dex2oat_reloc_dir, ".art", &dex2oat_image_basenames, &error_msg)) { 474 FAIL() << "Failed to list *.art files in " << dex2oat_reloc_dir << ": " << error_msg; 475 } 476 if (!ListDirFilesEndingWith(patchoat_dir, ".art", &patchoat_image_basenames, &error_msg)) { 477 FAIL() << "Failed to list *.art files in " << patchoat_dir << ": " << error_msg; 478 } 479 std::sort(dex2oat_image_basenames.begin(), dex2oat_image_basenames.end()); 480 std::sort(patchoat_image_basenames.begin(), patchoat_image_basenames.end()); 481 // .art file names output by patchoat look like tmp@art-data-<random>-<random>@boot*.art. To 482 // compare these with .art file names output by dex2oat we retain only the part of the file name 483 // after the last @. 484 std::vector<std::string> patchoat_image_shortened_basenames(patchoat_image_basenames.size()); 485 for (size_t i = 0; i < patchoat_image_basenames.size(); i++) { 486 patchoat_image_shortened_basenames[i] = 487 patchoat_image_basenames[i].substr(patchoat_image_basenames[i].find_last_of("@") + 1); 488 } 489 ASSERT_EQ(dex2oat_image_basenames, patchoat_image_shortened_basenames); 490 491 // Patch up the dex2oat-relocated image files so that it looks as though they were relocated by 492 // patchoat. patchoat preserves the OAT checksum header field and sets patch delta header field. 493 for (const std::string& image_basename : dex2oat_image_basenames) { 494 if (!CopyImageChecksumAndSetPatchDelta( 495 dex2oat_orig_dir + "/" + image_basename, 496 dex2oat_reloc_dir + "/" + image_basename, 497 base_addr_delta, 498 &error_msg)) { 499 FAIL() << "Unable to patch up " << image_basename << ": " << error_msg; 500 } 501 } 502 503 // Assert that the patchoat-relocated images are identical to the dex2oat-relocated images 504 for (size_t i = 0; i < dex2oat_image_basenames.size(); i++) { 505 const std::string& dex2oat_image_basename = dex2oat_image_basenames[i]; 506 const std::string& dex2oat_image_filename = dex2oat_reloc_dir + "/" + dex2oat_image_basename; 507 const std::string& patchoat_image_filename = patchoat_dir + "/" + patchoat_image_basenames[i]; 508 if (BinaryDiff(dex2oat_image_filename, patchoat_image_filename, &error_msg)) { 509 FAIL() << "patchoat- and dex2oat-relocated variants of " << dex2oat_image_basename 510 << " differ: " << error_msg; 511 } 512 } 513 514 ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); 515 ClearDirectory(dex2oat_reloc_dir.c_str(), /*recursive*/ true); 516 ClearDirectory(patchoat_dir.c_str(), /*recursive*/ true); 517 rmdir(dex2oat_orig_dir.c_str()); 518 rmdir(dex2oat_reloc_dir.c_str()); 519 rmdir(patchoat_dir.c_str()); 520#else 521 LOG(INFO) << "Skipping PatchoatRelocationSameAsDex2oatRelocation"; 522 // Force-print to std::cout so it's also outside the logcat. 523 std::cout << "Skipping PatchoatRelocationSameAsDex2oatRelocation" << std::endl; 524#endif 525} 526 527TEST_F(PatchoatTest, RelFileSufficientToUnpatch) { 528 // This test checks that a boot image relocated using patchoat can be unrelocated using the .rel 529 // file created by patchoat. 530 531 // This test doesn't work when heap poisoning is enabled because some of the 532 // references are negated. b/72117833 is tracking the effort to have patchoat 533 // and its tests support heap poisoning. 534 TEST_DISABLED_FOR_HEAP_POISONING(); 535 536 // Compile boot image into a random directory using dex2oat 537 ScratchFile dex2oat_orig_scratch; 538 dex2oat_orig_scratch.Unlink(); 539 std::string dex2oat_orig_dir = dex2oat_orig_scratch.GetFilename(); 540 ASSERT_EQ(0, mkdir(dex2oat_orig_dir.c_str(), 0700)); 541 const uint32_t orig_base_addr = 0x60000000; 542 std::vector<std::string> dex2oat_extra_args; 543 std::string error_msg; 544 if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) { 545 FAIL() << "CompileBootImage1 failed: " << error_msg; 546 } 547 548 // Generate image relocation file for the original boot image 549 ScratchFile rel_scratch; 550 rel_scratch.Unlink(); 551 std::string rel_dir = rel_scratch.GetFilename(); 552 ASSERT_EQ(0, mkdir(rel_dir.c_str(), 0700)); 553 std::string dex2oat_orig_with_arch_dir = 554 dex2oat_orig_dir + "/" + GetInstructionSetString(kRuntimeISA); 555 // The arch-including symlink is needed by patchoat 556 ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str())); 557 off_t base_addr_delta = 0x100000; 558 if (!GenerateBootImageRelFile( 559 dex2oat_orig_dir + "/boot.art", 560 rel_dir + "/boot.art.rel", 561 base_addr_delta, 562 &error_msg)) { 563 FAIL() << "RelocateBootImage failed: " << error_msg; 564 } 565 566 // Relocate the original boot image using patchoat 567 ScratchFile relocated_scratch; 568 relocated_scratch.Unlink(); 569 std::string relocated_dir = relocated_scratch.GetFilename(); 570 ASSERT_EQ(0, mkdir(relocated_dir.c_str(), 0700)); 571 // Use a different relocation delta from the one used when generating .rel files above. This is 572 // to make sure .rel files are not specific to a particular relocation delta. 573 base_addr_delta -= 0x10000; 574 if (!RelocateBootImage( 575 dex2oat_orig_dir + "/boot.art", 576 relocated_dir + "/boot.art", 577 base_addr_delta, 578 &error_msg)) { 579 FAIL() << "RelocateBootImage failed: " << error_msg; 580 } 581 582 // Assert that patchoat created the same set of .art and .art.rel files 583 std::vector<std::string> rel_basenames; 584 std::vector<std::string> relocated_image_basenames; 585 if (!ListDirFilesEndingWith(rel_dir, "", &rel_basenames, &error_msg)) { 586 FAIL() << "Failed to list *.art.rel files in " << rel_dir << ": " << error_msg; 587 } 588 if (!ListDirFilesEndingWith(relocated_dir, ".art", &relocated_image_basenames, &error_msg)) { 589 FAIL() << "Failed to list *.art files in " << relocated_dir << ": " << error_msg; 590 } 591 std::sort(rel_basenames.begin(), rel_basenames.end()); 592 std::sort(relocated_image_basenames.begin(), relocated_image_basenames.end()); 593 594 // .art and .art.rel file names output by patchoat look like 595 // tmp@art-data-<random>-<random>@boot*.art, encoding the name of the directory in their name. 596 // To compare these with each other, we retain only the part of the file name after the last @, 597 // and we also drop the extension. 598 std::vector<std::string> rel_shortened_basenames(rel_basenames.size()); 599 std::vector<std::string> relocated_image_shortened_basenames(relocated_image_basenames.size()); 600 for (size_t i = 0; i < rel_basenames.size(); i++) { 601 rel_shortened_basenames[i] = rel_basenames[i].substr(rel_basenames[i].find_last_of("@") + 1); 602 rel_shortened_basenames[i] = 603 rel_shortened_basenames[i].substr(0, rel_shortened_basenames[i].find(".")); 604 } 605 for (size_t i = 0; i < relocated_image_basenames.size(); i++) { 606 relocated_image_shortened_basenames[i] = 607 relocated_image_basenames[i].substr(relocated_image_basenames[i].find_last_of("@") + 1); 608 relocated_image_shortened_basenames[i] = 609 relocated_image_shortened_basenames[i].substr( 610 0, relocated_image_shortened_basenames[i].find(".")); 611 } 612 ASSERT_EQ(rel_shortened_basenames, relocated_image_shortened_basenames); 613 614 // For each image file, assert that unrelocating the image produces its original version 615 for (size_t i = 0; i < relocated_image_basenames.size(); i++) { 616 const std::string& original_image_filename = 617 dex2oat_orig_dir + "/" + relocated_image_shortened_basenames[i] + ".art"; 618 const std::string& relocated_image_filename = 619 relocated_dir + "/" + relocated_image_basenames[i]; 620 const std::string& rel_filename = rel_dir + "/" + rel_basenames[i]; 621 622 // Assert that relocated image differs from the original 623 if (!BinaryDiff(original_image_filename, relocated_image_filename, &error_msg)) { 624 FAIL() << "Relocated image " << relocated_image_filename 625 << " identical to the original image " << original_image_filename; 626 } 627 628 // Assert that relocated image is identical to the original except for relocations described in 629 // the .rel file 630 if (!IsImageIdenticalToOriginalExceptForRelocation( 631 relocated_image_filename, original_image_filename, rel_filename, &error_msg)) { 632 FAIL() << "Unrelocating " << relocated_image_filename << " using " << rel_filename 633 << " did not produce the same output as " << original_image_filename << ": " << error_msg; 634 } 635 636 // Assert that the digest of original image in .rel file is as expected 637 std::vector<uint8_t> original; 638 if (!ReadFully(original_image_filename, &original, &error_msg)) { 639 FAIL() << "Failed to read original image " << original_image_filename; 640 } 641 std::vector<uint8_t> rel; 642 if (!ReadFully(rel_filename, &rel, &error_msg)) { 643 FAIL() << "Failed to read image relocation file " << rel_filename; 644 } 645 uint8_t original_image_digest[SHA256_DIGEST_LENGTH]; 646 SHA256(original.data(), original.size(), original_image_digest); 647 const uint8_t* original_image_digest_in_rel_file = rel.data(); 648 if (memcmp(original_image_digest_in_rel_file, original_image_digest, SHA256_DIGEST_LENGTH)) { 649 FAIL() << "Digest of original image in " << rel_filename << " does not match the original" 650 " image " << original_image_filename; 651 } 652 } 653 654 ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true); 655 ClearDirectory(rel_dir.c_str(), /*recursive*/ true); 656 ClearDirectory(relocated_dir.c_str(), /*recursive*/ true); 657 658 rmdir(dex2oat_orig_dir.c_str()); 659 rmdir(rel_dir.c_str()); 660 rmdir(relocated_dir.c_str()); 661} 662 663} // namespace art 664