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