1 /* 2 * Copyright (C) 2014 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 #ifndef ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_ 18 #define ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_ 19 20 #include <sys/wait.h> 21 22 #include <fstream> 23 #include <memory> 24 #include <optional> 25 #include <string> 26 #include <vector> 27 28 #include "android-base/file.h" 29 #include "android-base/result.h" 30 #include "android-base/strings.h" 31 #include "base/file_utils.h" 32 #include "base/globals.h" 33 #include "base/macros.h" 34 #include "base/os.h" 35 #include "base/stl_util.h" 36 #include "base/utils.h" 37 #include "common_runtime_test.h" 38 #include "compiler_callbacks.h" 39 #include "dex/art_dex_file_loader.h" 40 #include "dex/dex_file_loader.h" 41 #include "exec_utils.h" 42 #include "gc/heap.h" 43 #include "gc/space/image_space.h" 44 #include "gtest/gtest.h" 45 #include "oat/oat_file_assistant.h" 46 #include "oat/sdc_file.h" 47 #include "runtime.h" 48 #include "ziparchive/zip_writer.h" 49 50 namespace art HIDDEN { 51 52 using ::android::base::Result; 53 54 static constexpr bool kDebugArgs = false; 55 56 class Dex2oatScratchDirs { 57 public: SetUp(const std::string & android_data)58 void SetUp(const std::string& android_data) { 59 // Create a scratch directory to work from. 60 61 // Get the realpath of the android data. The oat dir should always point to real location 62 // when generating oat files in dalvik-cache. This avoids complicating the unit tests 63 // when matching the expected paths. 64 UniqueCPtr<const char[]> android_data_real(realpath(android_data.c_str(), nullptr)); 65 ASSERT_TRUE(android_data_real != nullptr) 66 << "Could not get the realpath of the android data" << android_data << strerror(errno); 67 68 scratch_dir_.assign(android_data_real.get()); 69 scratch_dir_ += "/Dex2oatEnvironmentTest"; 70 ASSERT_EQ(0, mkdir(scratch_dir_.c_str(), 0700)); 71 72 // Create a subdirectory in scratch for odex files. 73 odex_oat_dir_ = scratch_dir_ + "/oat"; 74 ASSERT_EQ(0, mkdir(odex_oat_dir_.c_str(), 0700)); 75 76 odex_dir_ = odex_oat_dir_ + "/" + std::string(GetInstructionSetString(kRuntimeISA)); 77 ASSERT_EQ(0, mkdir(odex_dir_.c_str(), 0700)); 78 } 79 TearDown()80 void TearDown() { 81 CommonArtTest::ClearDirectory(odex_dir_.c_str()); 82 ASSERT_EQ(0, rmdir(odex_dir_.c_str())); 83 84 CommonArtTest::ClearDirectory(odex_oat_dir_.c_str()); 85 ASSERT_EQ(0, rmdir(odex_oat_dir_.c_str())); 86 87 CommonArtTest::ClearDirectory(scratch_dir_.c_str()); 88 ASSERT_EQ(0, rmdir(scratch_dir_.c_str())); 89 } 90 91 // Scratch directory, for dex and odex files (oat files will go in the 92 // dalvik cache). GetScratchDir()93 const std::string& GetScratchDir() const { return scratch_dir_; } 94 95 // Odex directory is the subdirectory in the scratch directory where odex 96 // files should be located. GetOdexDir()97 const std::string& GetOdexDir() const { return odex_dir_; } 98 99 private: 100 std::string scratch_dir_; 101 std::string odex_oat_dir_; 102 std::string odex_dir_; 103 }; 104 105 // Test class that provides some helpers to set a test up for compilation using dex2oat. 106 class Dex2oatEnvironmentTest : public Dex2oatScratchDirs, public CommonRuntimeTest { 107 public: SetUp()108 void SetUp() override { 109 CommonRuntimeTest::SetUp(); 110 Dex2oatScratchDirs::SetUp(android_data_); 111 112 // Verify the environment is as we expect 113 std::optional<uint32_t> checksum; 114 std::string error_msg; 115 ASSERT_TRUE(OS::FileExists(GetSystemImageFile().c_str())) 116 << "Expected pre-compiled boot image to be at: " << GetSystemImageFile(); 117 ASSERT_TRUE(OS::FileExists(GetDexSrc1().c_str())) 118 << "Expected dex file to be at: " << GetDexSrc1(); 119 ASSERT_TRUE(OS::FileExists(GetResourceOnlySrc1().c_str())) 120 << "Expected stripped dex file to be at: " << GetResourceOnlySrc1(); 121 ArtDexFileLoader dex_file_loader0(GetResourceOnlySrc1()); 122 ASSERT_TRUE(dex_file_loader0.GetMultiDexChecksum(&checksum, &error_msg)) 123 << "Expected stripped dex file to be stripped: " << GetResourceOnlySrc1(); 124 ASSERT_TRUE(OS::FileExists(GetDexSrc2().c_str())) 125 << "Expected dex file to be at: " << GetDexSrc2(); 126 127 // GetMultiDexSrc2 should have the same primary dex checksum as 128 // GetMultiDexSrc1, but a different secondary dex checksum. 129 static constexpr bool kVerifyChecksum = true; 130 std::vector<std::unique_ptr<const DexFile>> multi1; 131 ArtDexFileLoader dex_file_loader1(GetMultiDexSrc1()); 132 ASSERT_TRUE(dex_file_loader1.Open(/* verify= */ true, kVerifyChecksum, &error_msg, &multi1)) 133 << error_msg; 134 ASSERT_GT(multi1.size(), 1u); 135 136 std::vector<std::unique_ptr<const DexFile>> multi2; 137 ArtDexFileLoader dex_file_loader2(GetMultiDexSrc2()); 138 ASSERT_TRUE(dex_file_loader2.Open(/* verify= */ true, kVerifyChecksum, &error_msg, &multi2)) 139 << error_msg; 140 ASSERT_GT(multi2.size(), 1u); 141 142 ASSERT_EQ(multi1[0]->GetHeader().checksum_, multi2[0]->GetHeader().checksum_); 143 ASSERT_NE(multi1[1]->GetHeader().checksum_, multi2[1]->GetHeader().checksum_); 144 145 if (multi1[0]->HasDexContainer()) { 146 // Checksum is the CRC of the whole container, so both of them should differ. 147 ASSERT_NE(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum()); 148 ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum()); 149 } else { 150 ASSERT_EQ(multi1[0]->GetLocationChecksum(), multi2[0]->GetLocationChecksum()); 151 ASSERT_NE(multi1[1]->GetLocationChecksum(), multi2[1]->GetLocationChecksum()); 152 } 153 } 154 SetUpRuntimeOptions(RuntimeOptions * options)155 void SetUpRuntimeOptions(RuntimeOptions* options) override { 156 // options->push_back(std::make_pair("-verbose:oat", nullptr)); 157 158 // Set up the image location. 159 options->push_back(std::make_pair("-Ximage:" + GetImageLocation(), 160 nullptr)); 161 // Make sure compilercallbacks are not set so that relocation will be 162 // enabled. 163 callbacks_.reset(); 164 } 165 TearDown()166 void TearDown() override { 167 Dex2oatScratchDirs::TearDown(); 168 CommonRuntimeTest::TearDown(); 169 } 170 Copy(const std::string & src,const std::string & dst)171 static void Copy(const std::string& src, const std::string& dst) { 172 std::ifstream src_stream(src, std::ios::binary); 173 std::ofstream dst_stream(dst, std::ios::binary); 174 175 dst_stream << src_stream.rdbuf(); 176 } 177 GetDexSrc1()178 std::string GetDexSrc1() const { 179 return GetTestDexFileName("Main"); 180 } 181 182 // Returns the path to a dex file equivalent to GetDexSrc1, but with the dex 183 // file stripped. GetResourceOnlySrc1()184 std::string GetResourceOnlySrc1() const { 185 return GetTestDexFileName("MainStripped"); 186 } 187 GetMultiDexSrc1()188 std::string GetMultiDexSrc1() const { 189 return GetTestDexFileName("MultiDex"); 190 } 191 GetMultiDexUncompressedAlignedSrc1()192 std::string GetMultiDexUncompressedAlignedSrc1() const { 193 return GetTestDexFileName("MultiDexUncompressedAligned"); 194 } 195 196 // Returns the path to a multidex file equivalent to GetMultiDexSrc2, but 197 // with the contents of the secondary dex file changed. GetMultiDexSrc2()198 std::string GetMultiDexSrc2() const { 199 return GetTestDexFileName("MultiDexModifiedSecondary"); 200 } 201 GetDexSrc2()202 std::string GetDexSrc2() const { 203 return GetTestDexFileName("Nested"); 204 } 205 Dex2Oat(const std::vector<std::string> & dex2oat_args,std::string * output)206 Result<int> Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* output) { 207 std::vector<std::string> argv; 208 std::string error_msg; 209 if (!CommonRuntimeTest::StartDex2OatCommandLine(&argv, &error_msg)) { 210 return Errorf("Could not start dex2oat cmd line: {}", error_msg); 211 } 212 213 Runtime* runtime = Runtime::Current(); 214 if (!runtime->IsVerificationEnabled()) { 215 argv.push_back("--compiler-filter=assume-verified"); 216 } 217 218 if (runtime->MustRelocateIfPossible()) { 219 argv.push_back("--runtime-arg"); 220 argv.push_back("-Xrelocate"); 221 } else { 222 argv.push_back("--runtime-arg"); 223 argv.push_back("-Xnorelocate"); 224 } 225 226 if (!kIsTargetBuild) { 227 argv.push_back("--host"); 228 } 229 230 argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end()); 231 232 // We must set --android-root. 233 const char* android_root = getenv("ANDROID_ROOT"); 234 CHECK(android_root != nullptr); 235 argv.push_back("--android-root=" + std::string(android_root)); 236 237 if (kDebugArgs) { 238 std::string all_args; 239 for (const std::string& arg : argv) { 240 all_args += arg + " "; 241 } 242 LOG(ERROR) << all_args; 243 } 244 245 // We need dex2oat to actually log things. 246 auto post_fork_fn = []() { return setenv("ANDROID_LOG_TAGS", "*:d", 1) == 0; }; 247 248 ForkAndExecResult res = ForkAndExec(argv, post_fork_fn, output); 249 if (res.stage != ForkAndExecResult::kFinished) { 250 return ErrnoErrorf("Failed to finish dex2oat invocation '{}'", 251 android::base::Join(argv, ' ')); 252 } 253 254 if (!WIFEXITED(res.status_code)) { 255 return Errorf("dex2oat didn't terminate normally (status_code={:#x}): {}", 256 res.status_code, 257 android::base::Join(argv, ' ')); 258 } 259 260 return WEXITSTATUS(res.status_code); 261 } 262 263 void CreateDexMetadata(const std::string& vdex, 264 const std::string& out_dm, 265 bool page_aligned = false) { 266 // Read the vdex bytes. 267 std::unique_ptr<File> vdex_file(OS::OpenFileForReading(vdex.c_str())); 268 std::vector<uint8_t> data(vdex_file->GetLength()); 269 ASSERT_TRUE(vdex_file->ReadFully(data.data(), data.size())); 270 271 // Zip the content. 272 FILE* file = fopen(out_dm.c_str(), "wbe"); 273 ZipWriter writer(file); 274 writer.StartAlignedEntry( 275 "primary.vdex", /*flags=*/0, /*alignment=*/page_aligned ? kMaxPageSize : 4); 276 writer.WriteBytes(data.data(), data.size()); 277 writer.FinishEntry(); 278 writer.Finish(); 279 fflush(file); 280 fclose(file); 281 } 282 CreateSecureDexMetadata(const std::string & odex,const std::string & art,const std::string & out_sdm)283 void CreateSecureDexMetadata(const std::string& odex, 284 const std::string& art, 285 const std::string& out_sdm) { 286 // Zip the content. 287 std::unique_ptr<File> sdm_file(OS::CreateEmptyFileWriteOnly(out_sdm.c_str())); 288 ASSERT_NE(sdm_file, nullptr); 289 ZipWriter writer(fdopen(sdm_file->Fd(), "wb")); 290 291 std::string odex_data; 292 ASSERT_TRUE(android::base::ReadFileToString(odex, &odex_data)); 293 writer.StartAlignedEntry("primary.odex", /*flags=*/0, /*alignment=*/kMaxPageSize); 294 writer.WriteBytes(odex_data.data(), odex_data.size()); 295 writer.FinishEntry(); 296 297 if (!art.empty()) { 298 std::string art_data; 299 ASSERT_TRUE(android::base::ReadFileToString(art, &art_data)); 300 writer.StartAlignedEntry("primary.art", /*flags=*/0, /*alignment=*/kMaxPageSize); 301 writer.WriteBytes(art_data.data(), art_data.size()); 302 writer.FinishEntry(); 303 } 304 305 writer.Finish(); 306 ASSERT_EQ(sdm_file->FlushClose(), 0); 307 } 308 CreateSecureDexMetadataCompanion(const std::string & sdm,const std::string & apex_versions,const std::string & out_sdc)309 void CreateSecureDexMetadataCompanion(const std::string& sdm, 310 const std::string& apex_versions, 311 const std::string& out_sdc) { 312 struct stat sdm_st; 313 ASSERT_EQ(stat(sdm.c_str(), &sdm_st), 0); 314 315 std::unique_ptr<File> sdc_file(OS::CreateEmptyFileWriteOnly(out_sdc.c_str())); 316 ASSERT_NE(sdc_file, nullptr); 317 SdcWriter sdc_writer(std::move(*sdc_file)); 318 sdc_writer.SetSdmTimestampNs(TimeSpecToNs(sdm_st.st_mtim)); 319 sdc_writer.SetApexVersions(apex_versions); 320 std::string error_msg; 321 ASSERT_TRUE(sdc_writer.Save(&error_msg)) << error_msg; 322 } 323 }; 324 325 } // namespace art 326 327 #endif // ART_RUNTIME_DEX2OAT_ENVIRONMENT_TEST_H_ 328