patchoat_test.cc revision 24e4f73f21c098e6fdffc5fc28fd3d185f94c27d
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 <dirent.h>
18#include <sys/types.h>
19
20#include <string>
21#include <vector>
22
23#include "android-base/stringprintf.h"
24#include "android-base/strings.h"
25
26#include "dexopt_test.h"
27#include "runtime.h"
28
29#include <gtest/gtest.h>
30
31namespace art {
32
33using android::base::StringPrintf;
34
35class PatchoatTest : public DexoptTest {
36 public:
37  static bool ListDirFilesEndingWith(
38      const std::string& dir,
39      const std::string& suffix,
40      std::vector<std::string>* filenames,
41      std::string* error_msg) {
42    DIR* d = opendir(dir.c_str());
43    if (d == nullptr) {
44      *error_msg = "Failed to open directory";
45      return false;
46    }
47    dirent* e;
48    struct stat s;
49    size_t suffix_len = suffix.size();
50    while ((e = readdir(d)) != nullptr) {
51      if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) {
52        continue;
53      }
54      size_t name_len = strlen(e->d_name);
55      if ((name_len < suffix_len) || (strcmp(&e->d_name[name_len - suffix_len], suffix.c_str()))) {
56        continue;
57      }
58      std::string basename(e->d_name);
59      std::string filename = dir + "/" + basename;
60      int stat_result = lstat(filename.c_str(), &s);
61      if (stat_result != 0) {
62        *error_msg =
63            StringPrintf("Failed to stat %s: stat returned %d", filename.c_str(), stat_result);
64        return false;
65      }
66      if (S_ISDIR(s.st_mode)) {
67        continue;
68      }
69      filenames->push_back(basename);
70    }
71    closedir(d);
72    return true;
73  }
74
75  static void AddRuntimeArg(std::vector<std::string>& args, const std::string& arg) {
76    args.push_back("--runtime-arg");
77    args.push_back(arg);
78  }
79
80  bool CompileBootImage(const std::vector<std::string>& extra_args,
81                        const std::string& image_file_name_prefix,
82                        uint32_t base_addr,
83                        std::string* error_msg) {
84    Runtime* const runtime = Runtime::Current();
85    std::vector<std::string> argv;
86    argv.push_back(runtime->GetCompilerExecutable());
87    AddRuntimeArg(argv, "-Xms64m");
88    AddRuntimeArg(argv, "-Xmx64m");
89    std::vector<std::string> dex_files = GetLibCoreDexFileNames();
90    for (const std::string& dex_file : dex_files) {
91      argv.push_back("--dex-file=" + dex_file);
92      argv.push_back("--dex-location=" + dex_file);
93    }
94    if (runtime->IsJavaDebuggable()) {
95      argv.push_back("--debuggable");
96    }
97    runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
98
99    AddRuntimeArg(argv, "-Xverify:softfail");
100
101    if (!kIsTargetBuild) {
102      argv.push_back("--host");
103    }
104
105    argv.push_back("--image=" + image_file_name_prefix + ".art");
106    argv.push_back("--oat-file=" + image_file_name_prefix + ".oat");
107    argv.push_back("--oat-location=" + image_file_name_prefix + ".oat");
108    argv.push_back(StringPrintf("--base=0x%" PRIx32, base_addr));
109    argv.push_back("--compile-pic");
110    argv.push_back("--multi-image");
111    argv.push_back("--no-generate-debug-info");
112
113    std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
114    argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
115
116    // We must set --android-root.
117    const char* android_root = getenv("ANDROID_ROOT");
118    CHECK(android_root != nullptr);
119    argv.push_back("--android-root=" + std::string(android_root));
120    argv.insert(argv.end(), extra_args.begin(), extra_args.end());
121
122    return RunDex2OatOrPatchoat(argv, error_msg);
123  }
124
125  bool RelocateBootImage(const std::string& input_image_location,
126                         const std::string& output_image_filename,
127                         off_t base_offset_delta,
128                         std::string* error_msg) {
129    Runtime* const runtime = Runtime::Current();
130    std::vector<std::string> argv;
131    argv.push_back(runtime->GetPatchoatExecutable());
132    argv.push_back("--input-image-location=" + input_image_location);
133    argv.push_back("--output-image-file=" + output_image_filename);
134    argv.push_back(StringPrintf("--base-offset-delta=0x%jx", (intmax_t) base_offset_delta));
135    argv.push_back(StringPrintf("--instruction-set=%s", GetInstructionSetString(kRuntimeISA)));
136
137    return RunDex2OatOrPatchoat(argv, error_msg);
138  }
139
140  bool RunDex2OatOrPatchoat(const std::vector<std::string>& args, std::string* error_msg) {
141    int link[2];
142
143    if (pipe(link) == -1) {
144      return false;
145    }
146
147    pid_t pid = fork();
148    if (pid == -1) {
149      return false;
150    }
151
152    if (pid == 0) {
153      // We need dex2oat to actually log things.
154      setenv("ANDROID_LOG_TAGS", "*:e", 1);
155      dup2(link[1], STDERR_FILENO);
156      close(link[0]);
157      close(link[1]);
158      std::vector<const char*> c_args;
159      for (const std::string& str : args) {
160        c_args.push_back(str.c_str());
161      }
162      c_args.push_back(nullptr);
163      execv(c_args[0], const_cast<char* const*>(c_args.data()));
164      exit(1);
165      UNREACHABLE();
166    } else {
167      close(link[1]);
168      char buffer[128];
169      memset(buffer, 0, 128);
170      ssize_t bytes_read = 0;
171
172      while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) {
173        *error_msg += std::string(buffer, bytes_read);
174      }
175      close(link[0]);
176      int status = -1;
177      if (waitpid(pid, &status, 0) != -1) {
178        return (status == 0);
179      }
180      return false;
181    }
182  }
183
184  bool CompileBootImageToDir(
185      const std::string& output_dir,
186      const std::vector<std::string>& dex2oat_extra_args,
187      uint32_t base_addr,
188      std::string* error_msg) {
189    return CompileBootImage(dex2oat_extra_args, output_dir + "/boot", base_addr, error_msg);
190  }
191
192  bool CopyImageChecksumAndSetPatchDelta(
193      const std::string& src_image_filename,
194      const std::string& dest_image_filename,
195      off_t dest_patch_delta,
196      std::string* error_msg) {
197    std::unique_ptr<File> src_file(OS::OpenFileForReading(src_image_filename.c_str()));
198    if (src_file.get() == nullptr) {
199      *error_msg = StringPrintf("Failed to open source image file %s", src_image_filename.c_str());
200      return false;
201    }
202    ImageHeader src_header;
203    if (!src_file->ReadFully(&src_header, sizeof(src_header))) {
204      *error_msg = StringPrintf("Failed to read source image file %s", src_image_filename.c_str());
205      return false;
206    }
207
208    std::unique_ptr<File> dest_file(OS::OpenFileReadWrite(dest_image_filename.c_str()));
209    if (dest_file.get() == nullptr) {
210      *error_msg =
211          StringPrintf("Failed to open destination image file %s", dest_image_filename.c_str());
212      return false;
213    }
214    ImageHeader dest_header;
215    if (!dest_file->ReadFully(&dest_header, sizeof(dest_header))) {
216      *error_msg =
217          StringPrintf("Failed to read destination image file %s", dest_image_filename.c_str());
218      return false;
219    }
220    dest_header.SetOatChecksum(src_header.GetOatChecksum());
221    dest_header.SetPatchDelta(dest_patch_delta);
222    if (!dest_file->ResetOffset()) {
223      *error_msg =
224          StringPrintf(
225              "Failed to seek to start of destination image file %s", dest_image_filename.c_str());
226      return false;
227    }
228    if (!dest_file->WriteFully(&dest_header, sizeof(dest_header))) {
229      *error_msg =
230          StringPrintf("Failed to write to destination image file %s", dest_image_filename.c_str());
231      dest_file->Erase();
232      return false;
233    }
234    if (dest_file->FlushCloseOrErase() != 0) {
235      *error_msg =
236          StringPrintf(
237              "Failed to flush/close destination image file %s", dest_image_filename.c_str());
238      return false;
239    }
240
241    return true;
242  }
243
244  bool ReadFully(
245      const std::string& filename, std::vector<uint8_t>* contents, std::string* error_msg) {
246    std::unique_ptr<File> file(OS::OpenFileForReading(filename.c_str()));
247    if (file.get() == nullptr) {
248      *error_msg = "Failed to open";
249      return false;
250    }
251    int64_t size = file->GetLength();
252    if (size < 0) {
253      *error_msg = "Failed to get size";
254      return false;
255    }
256    contents->resize(size);
257    if (!file->ReadFully(&(*contents)[0], size)) {
258      *error_msg = "Failed to read";
259      contents->clear();
260      return false;
261    }
262    return true;
263  }
264
265  bool BinaryDiff(
266      const std::string& filename1, const std::string& filename2, std::string* error_msg) {
267    std::string read_error_msg;
268    std::vector<uint8_t> image1;
269    if (!ReadFully(filename1, &image1, &read_error_msg)) {
270      *error_msg = StringPrintf("Failed to read %s: %s", filename1.c_str(), read_error_msg.c_str());
271      return true;
272    }
273    std::vector<uint8_t> image2;
274    if (!ReadFully(filename2, &image2, &read_error_msg)) {
275      *error_msg = StringPrintf("Failed to read %s: %s", filename2.c_str(), read_error_msg.c_str());
276      return true;
277    }
278    if (image1.size() != image2.size()) {
279      *error_msg =
280          StringPrintf(
281              "%s and %s are of different size: %zu vs %zu",
282              filename1.c_str(),
283              filename2.c_str(),
284              image1.size(),
285              image2.size());
286      return true;
287    }
288    size_t size = image1.size();
289    for (size_t i = 0; i < size; i++) {
290      if (image1[i] != image2[i]) {
291        *error_msg =
292            StringPrintf("%s and %s differ at offset %zu", filename1.c_str(), filename2.c_str(), i);
293        return true;
294      }
295    }
296
297    return false;
298  }
299};
300
301TEST_F(PatchoatTest, PatchoatRelocationSameAsDex2oatRelocation) {
302#if defined(ART_USE_READ_BARRIER)
303  // This test checks that relocating a boot image using patchoat produces the same result as
304  // producing the boot image for that relocated base address using dex2oat. To be precise, these
305  // two files will have two small differences: the OAT checksum and base address. However, this
306  // test takes this into account.
307
308  // Compile boot image into a random directory using dex2oat
309  ScratchFile dex2oat_orig_scratch;
310  dex2oat_orig_scratch.Unlink();
311  std::string dex2oat_orig_dir = dex2oat_orig_scratch.GetFilename();
312  ASSERT_EQ(0, mkdir(dex2oat_orig_dir.c_str(), 0700));
313  const uint32_t orig_base_addr = 0x60000000;
314  // Force deterministic output. We want the boot images created by this dex2oat run and the run
315  // below to differ only in their base address.
316  std::vector<std::string> dex2oat_extra_args;
317  dex2oat_extra_args.push_back("--force-determinism");
318  dex2oat_extra_args.push_back("-j1");  // Might not be needed. Causes a 3-5x slowdown.
319  std::string error_msg;
320  if (!CompileBootImageToDir(dex2oat_orig_dir, dex2oat_extra_args, orig_base_addr, &error_msg)) {
321    FAIL() << "CompileBootImage1 failed: " << error_msg;
322  }
323
324  // Compile a "relocated" boot image into a random directory using dex2oat. This image is relocated
325  // in the sense that it uses a different base address.
326  ScratchFile dex2oat_reloc_scratch;
327  dex2oat_reloc_scratch.Unlink();
328  std::string dex2oat_reloc_dir = dex2oat_reloc_scratch.GetFilename();
329  ASSERT_EQ(0, mkdir(dex2oat_reloc_dir.c_str(), 0700));
330  const uint32_t reloc_base_addr = 0x70000000;
331  if (!CompileBootImageToDir(dex2oat_reloc_dir, dex2oat_extra_args, reloc_base_addr, &error_msg)) {
332    FAIL() << "CompileBootImage2 failed: " << error_msg;
333  }
334  const off_t base_addr_delta = reloc_base_addr - orig_base_addr;
335
336  // Relocate the original boot image using patchoat. The image is relocated by the same amount
337  // as the second/relocated image produced by dex2oat.
338  ScratchFile patchoat_scratch;
339  patchoat_scratch.Unlink();
340  std::string patchoat_dir = patchoat_scratch.GetFilename();
341  ASSERT_EQ(0, mkdir(patchoat_dir.c_str(), 0700));
342  std::string dex2oat_orig_with_arch_dir =
343      dex2oat_orig_dir + "/" + GetInstructionSetString(kRuntimeISA);
344  // The arch-including symlink is needed by patchoat
345  ASSERT_EQ(0, symlink(dex2oat_orig_dir.c_str(), dex2oat_orig_with_arch_dir.c_str()));
346  if (!RelocateBootImage(
347      dex2oat_orig_dir + "/boot.art",
348      patchoat_dir + "/boot.art",
349      base_addr_delta,
350      &error_msg)) {
351    FAIL() << "RelocateBootImage failed: " << error_msg;
352  }
353
354  // Assert that patchoat created the same set of .art files as dex2oat
355  std::vector<std::string> dex2oat_image_basenames;
356  std::vector<std::string> patchoat_image_basenames;
357  if (!ListDirFilesEndingWith(dex2oat_reloc_dir, ".art", &dex2oat_image_basenames, &error_msg)) {
358    FAIL() << "Failed to list *.art files in " << dex2oat_reloc_dir << ": " << error_msg;
359  }
360  if (!ListDirFilesEndingWith(patchoat_dir, ".art", &patchoat_image_basenames, &error_msg)) {
361    FAIL() << "Failed to list *.art files in " << patchoat_dir << ": " << error_msg;
362  }
363  std::sort(dex2oat_image_basenames.begin(), dex2oat_image_basenames.end());
364  std::sort(patchoat_image_basenames.begin(), patchoat_image_basenames.end());
365  // .art file names output by patchoat look like tmp@art-data-<random>-<random>@boot*.art. To
366  // compare these with .art file names output by dex2oat we retain only the part of the file name
367  // after the last @.
368  std::vector<std::string> patchoat_image_shortened_basenames(patchoat_image_basenames.size());
369  for (size_t i = 0; i < patchoat_image_basenames.size(); i++) {
370    patchoat_image_shortened_basenames[i] =
371        patchoat_image_basenames[i].substr(patchoat_image_basenames[i].find_last_of("@") + 1);
372  }
373  ASSERT_EQ(dex2oat_image_basenames, patchoat_image_shortened_basenames);
374
375  // Patch up the dex2oat-relocated image files so that it looks as though they were relocated by
376  // patchoat. patchoat preserves the OAT checksum header field and sets patch delta header field.
377  for (const std::string& image_basename : dex2oat_image_basenames) {
378    if (!CopyImageChecksumAndSetPatchDelta(
379        dex2oat_orig_dir + "/" + image_basename,
380        dex2oat_reloc_dir + "/" + image_basename,
381        base_addr_delta,
382        &error_msg)) {
383      FAIL() << "Unable to patch up " << image_basename << ": " << error_msg;
384    }
385  }
386
387  // Assert that the patchoat-relocated images are identical to the dex2oat-relocated images
388  for (size_t i = 0; i < dex2oat_image_basenames.size(); i++) {
389    const std::string& dex2oat_image_basename = dex2oat_image_basenames[i];
390    const std::string& dex2oat_image_filename = dex2oat_reloc_dir + "/" + dex2oat_image_basename;
391    const std::string& patchoat_image_filename = patchoat_dir + "/" + patchoat_image_basenames[i];
392    if (BinaryDiff(dex2oat_image_filename, patchoat_image_filename, &error_msg)) {
393      FAIL() << "patchoat- and dex2oat-relocated variants of " << dex2oat_image_basename
394          << " differ: " << error_msg;
395    }
396  }
397
398  ClearDirectory(dex2oat_orig_dir.c_str(), /*recursive*/ true);
399  ClearDirectory(dex2oat_reloc_dir.c_str(), /*recursive*/ true);
400  ClearDirectory(patchoat_dir.c_str(), /*recursive*/ true);
401  rmdir(dex2oat_orig_dir.c_str());
402  rmdir(dex2oat_reloc_dir.c_str());
403  rmdir(patchoat_dir.c_str());
404#else
405  LOG(INFO) << "Skipping PatchoatRelocationSameAsDex2oatRelocation";
406  // Force-print to std::cout so it's also outside the logcat.
407  std::cout << "Skipping PatchoatRelocationSameAsDex2oatRelocation" << std::endl;
408#endif
409}
410
411}  // namespace art
412