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