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