bool ClassLinker::AddImageSpace()

in runtime/class_linker.cc [1562:1820]


bool ClassLinker::AddImageSpace(
    gc::space::ImageSpace* space,
    Handle<mirror::ClassLoader> class_loader,
    jobjectArray dex_elements,
    const char* dex_location,
    std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
    std::string* error_msg) {
  DCHECK(out_dex_files != nullptr);
  DCHECK(error_msg != nullptr);
  const uint64_t start_time = NanoTime();
  const bool app_image = class_loader.Get() != nullptr;
  const ImageHeader& header = space->GetImageHeader();
  mirror::Object* dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches);
  DCHECK(dex_caches_object != nullptr);
  Runtime* const runtime = Runtime::Current();
  gc::Heap* const heap = runtime->GetHeap();
  Thread* const self = Thread::Current();
  StackHandleScope<2> hs(self);
  Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
      hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
  Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
      header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
  const OatFile* oat_file = space->GetOatFile();
  std::unordered_set<mirror::ClassLoader*> image_class_loaders;
  // Check that the image is what we are expecting.
  if (image_pointer_size_ != space->GetImageHeader().GetPointerSize()) {
    *error_msg = StringPrintf("Application image pointer size does not match runtime: %zu vs %zu",
                              static_cast<size_t>(space->GetImageHeader().GetPointerSize()),
                              image_pointer_size_);
    return false;
  }
  DCHECK(class_roots.Get() != nullptr);
  if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) {
    *error_msg = StringPrintf("Expected %d class roots but got %d",
                              class_roots->GetLength(),
                              static_cast<int32_t>(kClassRootsMax));
    return false;
  }
  // Check against existing class roots to make sure they match the ones in the boot image.
  for (size_t i = 0; i < kClassRootsMax; i++) {
    if (class_roots->Get(i) != GetClassRoot(static_cast<ClassRoot>(i))) {
      *error_msg = "App image class roots must have pointer equality with runtime ones.";
      return false;
    }
  }
  if (oat_file->GetOatHeader().GetDexFileCount() !=
      static_cast<uint32_t>(dex_caches->GetLength())) {
    *error_msg = "Dex cache count and dex file count mismatch while trying to initialize from "
                 "image";
    return false;
  }

  StackHandleScope<1> hs2(self);
  MutableHandle<mirror::DexCache> h_dex_cache(hs2.NewHandle<mirror::DexCache>(nullptr));
  for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
    h_dex_cache.Assign(dex_caches->Get(i));
    std::string dex_file_location(h_dex_cache->GetLocation()->ToModifiedUtf8());
    // TODO: Only store qualified paths.
    // If non qualified, qualify it.
    if (dex_file_location.find('/') == std::string::npos) {
      std::string dex_location_path = dex_location;
      const size_t pos = dex_location_path.find_last_of('/');
      CHECK_NE(pos, std::string::npos);
      dex_location_path = dex_location_path.substr(0, pos + 1);  // Keep trailing '/'
      dex_file_location = dex_location_path + dex_file_location;
    }
    std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
                                                             dex_file_location.c_str(),
                                                             error_msg);
    if (dex_file == nullptr) {
      return false;
    }

    if (app_image) {
      // The current dex file field is bogus, overwrite it so that we can get the dex file in the
      // loop below.
      h_dex_cache->SetDexFile(dex_file.get());
      // Check that each class loader resolved the same way.
      // TODO: Store image class loaders as image roots.
      GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes();
      for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
        mirror::Class* klass = types[j].Read();
        if (klass != nullptr) {
          DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
          mirror::ClassLoader* image_class_loader = klass->GetClassLoader();
          image_class_loaders.insert(image_class_loader);
        }
      }
    } else {
      if (kSanityCheckObjects) {
        SanityCheckArtMethodPointerArray(h_dex_cache->GetResolvedMethods(),
                                         h_dex_cache->NumResolvedMethods(),
                                         image_pointer_size_,
                                         heap->GetBootImageSpaces());
      }
      // Register dex files, keep track of existing ones that are conflicts.
      AppendToBootClassPath(*dex_file.get(), h_dex_cache);
    }
    out_dex_files->push_back(std::move(dex_file));
  }

  if (app_image) {
    ScopedObjectAccessUnchecked soa(Thread::Current());
    // Check that the class loader resolves the same way as the ones in the image.
    // Image class loader [A][B][C][image dex files]
    // Class loader = [???][dex_elements][image dex files]
    // Need to ensure that [???][dex_elements] == [A][B][C].
    // For each class loader, PathClassLoader, the laoder checks the parent first. Also the logic
    // for PathClassLoader does this by looping through the array of dex files. To ensure they
    // resolve the same way, simply flatten the hierarchy in the way the resolution order would be,
    // and check that the dex file names are the same.
    for (mirror::ClassLoader* image_class_loader : image_class_loaders) {
      if (IsBootClassLoader(soa, image_class_loader)) {
        // The dex cache can reference types from the boot class loader.
        continue;
      }
      std::list<mirror::String*> image_dex_file_names;
      std::string temp_error_msg;
      if (!FlattenPathClassLoader(image_class_loader, &image_dex_file_names, &temp_error_msg)) {
        *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'",
                                  temp_error_msg.c_str());
        return false;
      }
      std::list<mirror::String*> loader_dex_file_names;
      if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) {
        *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'",
                                  temp_error_msg.c_str());
        return false;
      }
      // Add the temporary dex path list elements at the end.
      auto* elements = soa.Decode<mirror::ObjectArray<mirror::Object>*>(dex_elements);
      for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
        mirror::Object* element = elements->GetWithoutChecks(i);
        if (element != nullptr) {
          // If we are somewhere in the middle of the array, there may be nulls at the end.
          loader_dex_file_names.push_back(GetDexPathListElementName(soa, element));
        }
      }
      // Ignore the number of image dex files since we are adding those to the class loader anyways.
      CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
               static_cast<size_t>(dex_caches->GetLength()));
      size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
      // Check that the dex file names match.
      bool equal = image_count == loader_dex_file_names.size();
      if (equal) {
        auto it1 = image_dex_file_names.begin();
        auto it2 = loader_dex_file_names.begin();
        for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) {
          equal = equal && (*it1)->Equals(*it2);
        }
      }
      if (!equal) {
        VLOG(image) << "Image dex files " << image_dex_file_names.size();
        for (mirror::String* name : image_dex_file_names) {
          VLOG(image) << name->ToModifiedUtf8();
        }
        VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
        for (mirror::String* name : loader_dex_file_names) {
          VLOG(image) << name->ToModifiedUtf8();
        }
        *error_msg = "Rejecting application image due to class loader mismatch";
        // Ignore class loader mismatch for now since these would just use possibly incorrect
        // oat code anyways. The structural class check should be done in the parent.
      }
    }
  }

  if (kSanityCheckObjects) {
    for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
      auto* dex_cache = dex_caches->Get(i);
      for (size_t j = 0; j < dex_cache->NumResolvedFields(); ++j) {
        auto* field = dex_cache->GetResolvedField(j, image_pointer_size_);
        if (field != nullptr) {
          CHECK(field->GetDeclaringClass()->GetClass() != nullptr);
        }
      }
    }
    if (!app_image) {
      heap->VisitObjects(SanityCheckObjectsCallback, nullptr);
    }
  }

  // Set entry point to interpreter if in InterpretOnly mode.
  if (!runtime->IsAotCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
    SetInterpreterEntrypointArtMethodVisitor visitor(image_pointer_size_);
    header.VisitPackedArtMethods(&visitor, space->Begin(), image_pointer_size_);
  }

  ClassTable* class_table = nullptr;
  {
    WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
    class_table = InsertClassTableForClassLoader(class_loader.Get());
  }
  // If we have a class table section, read it and use it for verification in
  // UpdateAppImageClassLoadersAndDexCaches.
  ClassTable::ClassSet temp_set;
  const ImageSection& class_table_section = header.GetImageSection(ImageHeader::kSectionClassTable);
  const bool added_class_table = class_table_section.Size() > 0u;
  if (added_class_table) {
    const uint64_t start_time2 = NanoTime();
    size_t read_count = 0;
    temp_set = ClassTable::ClassSet(space->Begin() + class_table_section.Offset(),
                                    /*make copy*/false,
                                    &read_count);
    if (!app_image) {
      dex_cache_boot_image_class_lookup_required_ = false;
    }
    VLOG(image) << "Adding class table classes took " << PrettyDuration(NanoTime() - start_time2);
  }
  if (app_image) {
    bool forward_dex_cache_arrays = false;
    if (!UpdateAppImageClassLoadersAndDexCaches(space,
                                                class_loader,
                                                dex_caches,
                                                added_class_table ? &temp_set : nullptr,
                                                /*out*/&forward_dex_cache_arrays,
                                                /*out*/error_msg)) {
      return false;
    }
    // Update class loader and resolved strings. If added_class_table is false, the resolved
    // strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
    UpdateClassLoaderAndResolvedStringsVisitor visitor(space,
                                                       class_loader.Get(),
                                                       forward_dex_cache_arrays);
    if (added_class_table) {
      for (GcRoot<mirror::Class>& root : temp_set) {
        visitor(root.Read());
      }
    }
    // forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss.
    // In this case, madvise away the dex cache arrays section of the image to reduce RAM usage and
    // mark as PROT_NONE to catch any invalid accesses.
    if (forward_dex_cache_arrays) {
      const ImageSection& dex_cache_section = header.GetImageSection(
          ImageHeader::kSectionDexCacheArrays);
      uint8_t* section_begin = AlignUp(space->Begin() + dex_cache_section.Offset(), kPageSize);
      uint8_t* section_end = AlignDown(space->Begin() + dex_cache_section.End(), kPageSize);
      if (section_begin < section_end) {
        madvise(section_begin, section_end - section_begin, MADV_DONTNEED);
        mprotect(section_begin, section_end - section_begin, PROT_NONE);
        VLOG(image) << "Released and protected dex cache array image section from "
                    << reinterpret_cast<const void*>(section_begin) << "-"
                    << reinterpret_cast<const void*>(section_end);
      }
    }
  }
  if (added_class_table) {
    WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
    class_table->AddClassSet(std::move(temp_set));
  }
  if (kIsDebugBuild && app_image) {
    // This verification needs to happen after the classes have been added to the class loader.
    // Since it ensures classes are in the class table.
    VerifyClassInTableArtMethodVisitor visitor2(class_table);
    header.VisitPackedArtMethods(&visitor2, space->Begin(), sizeof(void*));
  }
  VLOG(class_linker) << "Adding image space took " << PrettyDuration(NanoTime() - start_time);
  return true;
}