bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches()

in runtime/class_linker.cc [1230:1467]


bool ClassLinker::UpdateAppImageClassLoadersAndDexCaches(
    gc::space::ImageSpace* space,
    Handle<mirror::ClassLoader> class_loader,
    Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches,
    ClassTable::ClassSet* new_class_set,
    bool* out_forward_dex_cache_array,
    std::string* out_error_msg) {
  DCHECK(out_forward_dex_cache_array != nullptr);
  DCHECK(out_error_msg != nullptr);
  Thread* const self = Thread::Current();
  gc::Heap* const heap = Runtime::Current()->GetHeap();
  const ImageHeader& header = space->GetImageHeader();
  {
    // Add image classes into the class table for the class loader, and fixup the dex caches and
    // class loader fields.
    WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
    ClassTable* table = InsertClassTableForClassLoader(class_loader.Get());
    // Dex cache array fixup is all or nothing, we must reject app images that have mixed since we
    // rely on clobering the dex cache arrays in the image to forward to bss.
    size_t num_dex_caches_with_bss_arrays = 0;
    const size_t num_dex_caches = dex_caches->GetLength();
    for (size_t i = 0; i < num_dex_caches; i++) {
      mirror::DexCache* const dex_cache = dex_caches->Get(i);
      const DexFile* const dex_file = dex_cache->GetDexFile();
      const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
      if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
        ++num_dex_caches_with_bss_arrays;
      }
    }
    *out_forward_dex_cache_array = num_dex_caches_with_bss_arrays != 0;
    if (*out_forward_dex_cache_array) {
      if (num_dex_caches_with_bss_arrays != num_dex_caches) {
        // Reject application image since we cannot forward only some of the dex cache arrays.
        // TODO: We could get around this by having a dedicated forwarding slot. It should be an
        // uncommon case.
        *out_error_msg = StringPrintf("Dex caches in bss does not match total: %zu vs %zu",
                                      num_dex_caches_with_bss_arrays,
                                      num_dex_caches);
        return false;
      }
    }
    // Only add the classes to the class loader after the points where we can return false.
    for (size_t i = 0; i < num_dex_caches; i++) {
      mirror::DexCache* const dex_cache = dex_caches->Get(i);
      const DexFile* const dex_file = dex_cache->GetDexFile();
      const OatFile::OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
      if (oat_dex_file != nullptr && oat_dex_file->GetDexCacheArrays() != nullptr) {
      // If the oat file expects the dex cache arrays to be in the BSS, then allocate there and
        // copy over the arrays.
        DCHECK(dex_file != nullptr);
        const size_t num_strings = dex_file->NumStringIds();
        const size_t num_types = dex_file->NumTypeIds();
        const size_t num_methods = dex_file->NumMethodIds();
        const size_t num_fields = dex_file->NumFieldIds();
        CHECK_EQ(num_strings, dex_cache->NumStrings());
        CHECK_EQ(num_types, dex_cache->NumResolvedTypes());
        CHECK_EQ(num_methods, dex_cache->NumResolvedMethods());
        CHECK_EQ(num_fields, dex_cache->NumResolvedFields());
        DexCacheArraysLayout layout(image_pointer_size_, dex_file);
        uint8_t* const raw_arrays = oat_dex_file->GetDexCacheArrays();
        // The space is not yet visible to the GC, we can avoid the read barriers and use
        // std::copy_n.
        if (num_strings != 0u) {
          GcRoot<mirror::String>* const image_resolved_strings = dex_cache->GetStrings();
          GcRoot<mirror::String>* const strings =
              reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
          for (size_t j = 0; kIsDebugBuild && j < num_strings; ++j) {
            DCHECK(strings[j].IsNull());
          }
          std::copy_n(image_resolved_strings, num_strings, strings);
          dex_cache->SetStrings(strings);
        }
        if (num_types != 0u) {
          GcRoot<mirror::Class>* const image_resolved_types = dex_cache->GetResolvedTypes();
          GcRoot<mirror::Class>* const types =
              reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
          for (size_t j = 0; kIsDebugBuild && j < num_types; ++j) {
            DCHECK(types[j].IsNull());
          }
          std::copy_n(image_resolved_types, num_types, types);
          // Store a pointer to the new location for fast ArtMethod patching without requiring map.
          // This leaves random garbage at the start of the dex cache array, but nobody should ever
          // read from it again.
          *reinterpret_cast<GcRoot<mirror::Class>**>(image_resolved_types) = types;
          dex_cache->SetResolvedTypes(types);
        }
        if (num_methods != 0u) {
          ArtMethod** const methods = reinterpret_cast<ArtMethod**>(
              raw_arrays + layout.MethodsOffset());
          ArtMethod** const image_resolved_methods = dex_cache->GetResolvedMethods();
          for (size_t j = 0; kIsDebugBuild && j < num_methods; ++j) {
            DCHECK(methods[j] == nullptr);
          }
          std::copy_n(image_resolved_methods, num_methods, methods);
          // Store a pointer to the new location for fast ArtMethod patching without requiring map.
          *reinterpret_cast<ArtMethod***>(image_resolved_methods) = methods;
          dex_cache->SetResolvedMethods(methods);
        }
        if (num_fields != 0u) {
          ArtField** const fields =
              reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
          for (size_t j = 0; kIsDebugBuild && j < num_fields; ++j) {
            DCHECK(fields[j] == nullptr);
          }
          std::copy_n(dex_cache->GetResolvedFields(), num_fields, fields);
          dex_cache->SetResolvedFields(fields);
        }
      }
      {
        WriterMutexLock mu2(self, dex_lock_);
        // Make sure to do this after we update the arrays since we store the resolved types array
        // in DexCacheData in RegisterDexFileLocked. We need the array pointer to be the one in the
        // BSS.
        mirror::DexCache* existing_dex_cache = FindDexCacheLocked(self,
                                                                  *dex_file,
                                                                  /*allow_failure*/true);
        CHECK(existing_dex_cache == nullptr);
        StackHandleScope<1> hs3(self);
        RegisterDexFileLocked(*dex_file, hs3.NewHandle(dex_cache));
      }
      GcRoot<mirror::Class>* const types = dex_cache->GetResolvedTypes();
      const size_t num_types = dex_cache->NumResolvedTypes();
      if (new_class_set == nullptr) {
        for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
          // The image space is not yet added to the heap, avoid read barriers.
          mirror::Class* klass = types[j].Read();
          // There may also be boot image classes,
          if (space->HasAddress(klass)) {
            DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
            // Update the class loader from the one in the image class loader to the one that loaded
            // the app image.
            klass->SetClassLoader(class_loader.Get());
            // The resolved type could be from another dex cache, go through the dex cache just in
            // case. May be null for array classes.
            if (klass->GetDexCacheStrings() != nullptr) {
              DCHECK(!klass->IsArrayClass());
              klass->SetDexCacheStrings(klass->GetDexCache()->GetStrings());
            }
            // If there are multiple dex caches, there may be the same class multiple times
            // in different dex caches. Check for this since inserting will add duplicates
            // otherwise.
            if (num_dex_caches > 1) {
              mirror::Class* existing = table->LookupByDescriptor(klass);
              if (existing != nullptr) {
                DCHECK_EQ(existing, klass) << PrettyClass(klass);
              } else {
                table->Insert(klass);
              }
            } else {
              table->Insert(klass);
            }
            // Double checked VLOG to avoid overhead.
            if (VLOG_IS_ON(image)) {
              VLOG(image) << PrettyClass(klass) << " " << klass->GetStatus();
              if (!klass->IsArrayClass()) {
                VLOG(image) << "From " << klass->GetDexCache()->GetDexFile()->GetBaseLocation();
              }
              VLOG(image) << "Direct methods";
              for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
                VLOG(image) << PrettyMethod(&m);
              }
              VLOG(image) << "Virtual methods";
              for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
                VLOG(image) << PrettyMethod(&m);
              }
            }
          } else {
            DCHECK(klass == nullptr || heap->ObjectIsInBootImageSpace(klass))
                << klass << " " << PrettyClass(klass);
          }
        }
      }
      if (kIsDebugBuild) {
        for (int32_t j = 0; j < static_cast<int32_t>(num_types); j++) {
          // The image space is not yet added to the heap, avoid read barriers.
          mirror::Class* klass = types[j].Read();
          if (space->HasAddress(klass)) {
            DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
            if (kIsDebugBuild) {
              if (new_class_set != nullptr) {
                auto it = new_class_set->Find(GcRoot<mirror::Class>(klass));
                DCHECK(it != new_class_set->end());
                DCHECK_EQ(it->Read(), klass);
                mirror::Class* super_class = klass->GetSuperClass();
                if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
                  auto it2 = new_class_set->Find(GcRoot<mirror::Class>(super_class));
                  DCHECK(it2 != new_class_set->end());
                  DCHECK_EQ(it2->Read(), super_class);
                }
              } else {
                DCHECK_EQ(table->LookupByDescriptor(klass), klass);
                mirror::Class* super_class = klass->GetSuperClass();
                if (super_class != nullptr && !heap->ObjectIsInBootImageSpace(super_class)) {
                  CHECK_EQ(table->LookupByDescriptor(super_class), super_class);
                }
              }
            }
            if (kIsDebugBuild) {
              for (ArtMethod& m : klass->GetDirectMethods(sizeof(void*))) {
                const void* code = m.GetEntryPointFromQuickCompiledCode();
                const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
                if (!IsQuickResolutionStub(code) &&
                    !IsQuickGenericJniStub(code) &&
                    !IsQuickToInterpreterBridge(code) &&
                    !m.IsNative()) {
                  DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
                }
              }
              for (ArtMethod& m : klass->GetVirtualMethods(sizeof(void*))) {
                const void* code = m.GetEntryPointFromQuickCompiledCode();
                const void* oat_code = m.IsInvokable() ? GetQuickOatCodeFor(&m) : code;
                if (!IsQuickResolutionStub(code) &&
                    !IsQuickGenericJniStub(code) &&
                    !IsQuickToInterpreterBridge(code) &&
                    !m.IsNative()) {
                  DCHECK_EQ(code, oat_code) << PrettyMethod(&m);
                }
              }
            }
          }
        }
      }
    }
  }
  if (*out_forward_dex_cache_array) {
    ScopedTrace timing("Fixup ArtMethod dex cache arrays");
    FixupArtMethodArrayVisitor visitor(header);
    header.VisitPackedArtMethods(&visitor, space->Begin(), sizeof(void*));
    Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
  }
  if (kVerifyArtMethodDeclaringClasses) {
    ScopedTrace timing("Verify declaring classes");
    ReaderMutexLock rmu(self, *Locks::heap_bitmap_lock_);
    VerifyDeclaringClassVisitor visitor;
    header.VisitPackedArtMethods(&visitor, space->Begin(), sizeof(void*));
  }
  return true;
}