blob: e789db6a16546382c0a06c3992f778186b6352d6 [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "class_linker.h"
#include <algorithm>
#include <deque>
#include <iostream>
#include <memory>
#include <queue>
#include <string>
#include <tuple>
#include <unistd.h>
#include <unordered_map>
#include <utility>
#include <vector>
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/arena_allocator.h"
#include "base/casts.h"
#include "base/logging.h"
#include "base/scoped_arena_containers.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
#include "base/systrace.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "base/value_object.h"
#include "class_linker-inl.h"
#include "class_table-inl.h"
#include "compiler_callbacks.h"
#include "debugger.h"
#include "dex_file-inl.h"
#include "entrypoints/entrypoint_utils.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "experimental_flags.h"
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap-inl.h"
#include "gc/heap.h"
#include "gc/scoped_gc_critical_section.h"
#include "gc/space/image_space.h"
#include "handle_scope-inl.h"
#include "image-inl.h"
#include "intern_table.h"
#include "interpreter/interpreter.h"
#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "jit/offline_profiling_info.h"
#include "leb128.h"
#include "linear_alloc.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/field.h"
#include "mirror/iftable-inl.h"
#include "mirror/method.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "mirror/proxy.h"
#include "mirror/reference-inl.h"
#include "mirror/stack_trace_element.h"
#include "mirror/string-inl.h"
#include "native/dalvik_system_DexFile.h"
#include "oat.h"
#include "oat_file.h"
#include "oat_file-inl.h"
#include "oat_file_assistant.h"
#include "oat_file_manager.h"
#include "object_lock.h"
#include "os.h"
#include "runtime.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
#include "thread-inl.h"
#include "trace.h"
#include "utils.h"
#include "utils/dex_cache_arrays_layout-inl.h"
#include "verifier/method_verifier.h"
#include "well_known_classes.h"
namespace art {
static constexpr bool kSanityCheckObjects = kIsDebugBuild;
static constexpr bool kVerifyArtMethodDeclaringClasses = kIsDebugBuild;
static void ThrowNoClassDefFoundError(const char* fmt, ...)
__attribute__((__format__(__printf__, 1, 2)))
SHARED_REQUIRES(Locks::mutator_lock_);
static void ThrowNoClassDefFoundError(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
Thread* self = Thread::Current();
self->ThrowNewExceptionV("Ljava/lang/NoClassDefFoundError;", fmt, args);
va_end(args);
}
static bool HasInitWithString(Thread* self, ClassLinker* class_linker, const char* descriptor)
SHARED_REQUIRES(Locks::mutator_lock_) {
ArtMethod* method = self->GetCurrentMethod(nullptr);
StackHandleScope<1> hs(self);
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(method != nullptr ?
method->GetDeclaringClass()->GetClassLoader() : nullptr));
mirror::Class* exception_class = class_linker->FindClass(self, descriptor, class_loader);
if (exception_class == nullptr) {
// No exc class ~ no <init>-with-string.
CHECK(self->IsExceptionPending());
self->ClearException();
return false;
}
ArtMethod* exception_init_method = exception_class->FindDeclaredDirectMethod(
"<init>", "(Ljava/lang/String;)V", class_linker->GetImagePointerSize());
return exception_init_method != nullptr;
}
// Helper for ThrowEarlierClassFailure. Throws the stored error.
static void HandleEarlierVerifyError(Thread* self, ClassLinker* class_linker, mirror::Class* c)
SHARED_REQUIRES(Locks::mutator_lock_) {
mirror::Object* obj = c->GetVerifyError();
DCHECK(obj != nullptr);
self->AssertNoPendingException();
if (obj->IsClass()) {
// Previous error has been stored as class. Create a new exception of that type.
// It's possible the exception doesn't have a <init>(String).
std::string temp;
const char* descriptor = obj->AsClass()->GetDescriptor(&temp);
if (HasInitWithString(self, class_linker, descriptor)) {
self->ThrowNewException(descriptor, PrettyDescriptor(c).c_str());
} else {
self->ThrowNewException(descriptor, nullptr);
}
} else {
// Previous error has been stored as an instance. Just rethrow.
mirror::Class* throwable_class =
self->DecodeJObject(WellKnownClasses::java_lang_Throwable)->AsClass();
mirror::Class* error_class = obj->GetClass();
CHECK(throwable_class->IsAssignableFrom(error_class));
self->SetException(obj->AsThrowable());
}
self->AssertPendingException();
}
void ClassLinker::ThrowEarlierClassFailure(mirror::Class* c, bool wrap_in_no_class_def) {
// The class failed to initialize on a previous attempt, so we want to throw
// a NoClassDefFoundError (v2 2.17.5). The exception to this rule is if we
// failed in verification, in which case v2 5.4.1 says we need to re-throw
// the previous error.
Runtime* const runtime = Runtime::Current();
if (!runtime->IsAotCompiler()) { // Give info if this occurs at runtime.
std::string extra;
if (c->GetVerifyError() != nullptr) {
mirror::Object* verify_error = c->GetVerifyError();
if (verify_error->IsClass()) {
extra = PrettyDescriptor(verify_error->AsClass());
} else {
extra = verify_error->AsThrowable()->Dump();
}
}
LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c) << ": " << extra;
}
CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus();
Thread* self = Thread::Current();
if (runtime->IsAotCompiler()) {
// At compile time, accurate errors and NCDFE are disabled to speed compilation.
mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
} else {
if (c->GetVerifyError() != nullptr) {
// Rethrow stored error.
HandleEarlierVerifyError(self, this, c);
}
if (c->GetVerifyError() == nullptr || wrap_in_no_class_def) {
// If there isn't a recorded earlier error, or this is a repeat throw from initialization,
// the top-level exception must be a NoClassDefFoundError. The potentially already pending
// exception will be a cause.
self->ThrowNewWrappedException("Ljava/lang/NoClassDefFoundError;",
PrettyDescriptor(c).c_str());
}
}
}
static void VlogClassInitializationFailure(Handle<mirror::Class> klass)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (VLOG_IS_ON(class_linker)) {
std::string temp;
LOG(INFO) << "Failed to initialize class " << klass->GetDescriptor(&temp) << " from "
<< klass->GetLocation() << "\n" << Thread::Current()->GetException()->Dump();
}
}
static void WrapExceptionInInitializer(Handle<mirror::Class> klass)
SHARED_REQUIRES(Locks::mutator_lock_) {
Thread* self = Thread::Current();
JNIEnv* env = self->GetJniEnv();
ScopedLocalRef<jthrowable> cause(env, env->ExceptionOccurred());
CHECK(cause.get() != nullptr);
env->ExceptionClear();
bool is_error = env->IsInstanceOf(cause.get(), WellKnownClasses::java_lang_Error);
env->Throw(cause.get());
// We only wrap non-Error exceptions; an Error can just be used as-is.
if (!is_error) {
self->ThrowNewWrappedException("Ljava/lang/ExceptionInInitializerError;", nullptr);
}
VlogClassInitializationFailure(klass);
}
// Gap between two fields in object layout.
struct FieldGap {
uint32_t start_offset; // The offset from the start of the object.
uint32_t size; // The gap size of 1, 2, or 4 bytes.
};
struct FieldGapsComparator {
explicit FieldGapsComparator() {
}
bool operator() (const FieldGap& lhs, const FieldGap& rhs)
NO_THREAD_SAFETY_ANALYSIS {
// Sort by gap size, largest first. Secondary sort by starting offset.
// Note that the priority queue returns the largest element, so operator()
// should return true if lhs is less than rhs.
return lhs.size < rhs.size || (lhs.size == rhs.size && lhs.start_offset > rhs.start_offset);
}
};
typedef std::priority_queue<FieldGap, std::vector<FieldGap>, FieldGapsComparator> FieldGaps;
// Adds largest aligned gaps to queue of gaps.
static void AddFieldGap(uint32_t gap_start, uint32_t gap_end, FieldGaps* gaps) {
DCHECK(gaps != nullptr);
uint32_t current_offset = gap_start;
while (current_offset != gap_end) {
size_t remaining = gap_end - current_offset;
if (remaining >= sizeof(uint32_t) && IsAligned<4>(current_offset)) {
gaps->push(FieldGap {current_offset, sizeof(uint32_t)});
current_offset += sizeof(uint32_t);
} else if (remaining >= sizeof(uint16_t) && IsAligned<2>(current_offset)) {
gaps->push(FieldGap {current_offset, sizeof(uint16_t)});
current_offset += sizeof(uint16_t);
} else {
gaps->push(FieldGap {current_offset, sizeof(uint8_t)});
current_offset += sizeof(uint8_t);
}
DCHECK_LE(current_offset, gap_end) << "Overran gap";
}
}
// Shuffle fields forward, making use of gaps whenever possible.
template<int n>
static void ShuffleForward(size_t* current_field_idx,
MemberOffset* field_offset,
std::deque<ArtField*>* grouped_and_sorted_fields,
FieldGaps* gaps)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(current_field_idx != nullptr);
DCHECK(grouped_and_sorted_fields != nullptr);
DCHECK(gaps != nullptr);
DCHECK(field_offset != nullptr);
DCHECK(IsPowerOfTwo(n));
while (!grouped_and_sorted_fields->empty()) {
ArtField* field = grouped_and_sorted_fields->front();
Primitive::Type type = field->GetTypeAsPrimitiveType();
if (Primitive::ComponentSize(type) < n) {
break;
}
if (!IsAligned<n>(field_offset->Uint32Value())) {
MemberOffset old_offset = *field_offset;
*field_offset = MemberOffset(RoundUp(field_offset->Uint32Value(), n));
AddFieldGap(old_offset.Uint32Value(), field_offset->Uint32Value(), gaps);
}
CHECK(type != Primitive::kPrimNot) << PrettyField(field); // should be primitive types
grouped_and_sorted_fields->pop_front();
if (!gaps->empty() && gaps->top().size >= n) {
FieldGap gap = gaps->top();
gaps->pop();
DCHECK_ALIGNED(gap.start_offset, n);
field->SetOffset(MemberOffset(gap.start_offset));
if (gap.size > n) {
AddFieldGap(gap.start_offset + n, gap.start_offset + gap.size, gaps);
}
} else {
DCHECK_ALIGNED(field_offset->Uint32Value(), n);
field->SetOffset(*field_offset);
*field_offset = MemberOffset(field_offset->Uint32Value() + n);
}
++(*current_field_idx);
}
}
ClassLinker::ClassLinker(InternTable* intern_table)
// dex_lock_ is recursive as it may be used in stack dumping.
: dex_lock_("ClassLinker dex lock", kDefaultMutexLevel),
dex_cache_boot_image_class_lookup_required_(false),
failed_dex_cache_class_lookups_(0),
class_roots_(nullptr),
array_iftable_(nullptr),
find_array_class_cache_next_victim_(0),
init_done_(false),
log_new_class_table_roots_(false),
intern_table_(intern_table),
quick_resolution_trampoline_(nullptr),
quick_imt_conflict_trampoline_(nullptr),
quick_generic_jni_trampoline_(nullptr),
quick_to_interpreter_bridge_trampoline_(nullptr),
image_pointer_size_(sizeof(void*)) {
CHECK(intern_table_ != nullptr);
static_assert(kFindArrayCacheSize == arraysize(find_array_class_cache_),
"Array cache size wrong.");
std::fill_n(find_array_class_cache_, kFindArrayCacheSize, GcRoot<mirror::Class>(nullptr));
}
void ClassLinker::CheckSystemClass(Thread* self, Handle<mirror::Class> c1, const char* descriptor) {
mirror::Class* c2 = FindSystemClass(self, descriptor);
if (c2 == nullptr) {
LOG(FATAL) << "Could not find class " << descriptor;
UNREACHABLE();
}
if (c1.Get() != c2) {
std::ostringstream os1, os2;
c1->DumpClass(os1, mirror::Class::kDumpClassFullDetail);
c2->DumpClass(os2, mirror::Class::kDumpClassFullDetail);
LOG(FATAL) << "InitWithoutImage: Class mismatch for " << descriptor
<< ". This is most likely the result of a broken build. Make sure that "
<< "libcore and art projects match.\n\n"
<< os1.str() << "\n\n" << os2.str();
UNREACHABLE();
}
}
bool ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path,
std::string* error_msg) {
VLOG(startup) << "ClassLinker::Init";
Thread* const self = Thread::Current();
Runtime* const runtime = Runtime::Current();
gc::Heap* const heap = runtime->GetHeap();
CHECK(!heap->HasBootImageSpace()) << "Runtime has image. We should use it.";
CHECK(!init_done_);
// Use the pointer size from the runtime since we are probably creating the image.
image_pointer_size_ = InstructionSetPointerSize(runtime->GetInstructionSet());
if (!ValidPointerSize(image_pointer_size_)) {
*error_msg = StringPrintf("Invalid image pointer size: %zu", image_pointer_size_);
return false;
}
// java_lang_Class comes first, it's needed for AllocClass
// The GC can't handle an object with a null class since we can't get the size of this object.
heap->IncrementDisableMovingGC(self);
StackHandleScope<64> hs(self); // 64 is picked arbitrarily.
auto class_class_size = mirror::Class::ClassClassSize(image_pointer_size_);
Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>(
heap->AllocNonMovableObject<true>(self, nullptr, class_class_size, VoidFunctor()))));
CHECK(java_lang_Class.Get() != nullptr);
mirror::Class::SetClassClass(java_lang_Class.Get());
java_lang_Class->SetClass(java_lang_Class.Get());
if (kUseBakerOrBrooksReadBarrier) {
java_lang_Class->AssertReadBarrierPointer();
}
java_lang_Class->SetClassSize(class_class_size);
java_lang_Class->SetPrimitiveType(Primitive::kPrimNot);
heap->DecrementDisableMovingGC(self);
// AllocClass(mirror::Class*) can now be used
// Class[] is used for reflection support.
auto class_array_class_size = mirror::ObjectArray<mirror::Class>::ClassSize(image_pointer_size_);
Handle<mirror::Class> class_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), class_array_class_size)));
class_array_class->SetComponentType(java_lang_Class.Get());
// java_lang_Object comes next so that object_array_class can be created.
Handle<mirror::Class> java_lang_Object(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Object::ClassSize(image_pointer_size_))));
CHECK(java_lang_Object.Get() != nullptr);
// backfill Object as the super class of Class.
java_lang_Class->SetSuperClass(java_lang_Object.Get());
mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusLoaded, self);
java_lang_Object->SetObjectSize(sizeof(mirror::Object));
// Allocate in non-movable so that it's possible to check if a JNI weak global ref has been
// cleared without triggering the read barrier and unintentionally mark the sentinel alive.
runtime->SetSentinel(heap->AllocNonMovableObject<true>(self,
java_lang_Object.Get(),
java_lang_Object->GetObjectSize(),
VoidFunctor()));
// Object[] next to hold class roots.
Handle<mirror::Class> object_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(),
mirror::ObjectArray<mirror::Object>::ClassSize(image_pointer_size_))));
object_array_class->SetComponentType(java_lang_Object.Get());
// Setup the char (primitive) class to be used for char[].
Handle<mirror::Class> char_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(),
mirror::Class::PrimitiveClassSize(image_pointer_size_))));
// The primitive char class won't be initialized by
// InitializePrimitiveClass until line 459, but strings (and
// internal char arrays) will be allocated before that and the
// component size, which is computed from the primitive type, needs
// to be set here.
char_class->SetPrimitiveType(Primitive::kPrimChar);
// Setup the char[] class to be used for String.
Handle<mirror::Class> char_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_))));
char_array_class->SetComponentType(char_class.Get());
mirror::CharArray::SetArrayClass(char_array_class.Get());
// Setup String.
Handle<mirror::Class> java_lang_String(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::String::ClassSize(image_pointer_size_))));
java_lang_String->SetStringClass();
mirror::String::SetClass(java_lang_String.Get());
mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusResolved, self);
// Setup java.lang.ref.Reference.
Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize(image_pointer_size_))));
mirror::Reference::SetClass(java_lang_ref_Reference.Get());
java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize());
mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusResolved, self);
// Create storage for root classes, save away our work so far (requires descriptors).
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(
mirror::ObjectArray<mirror::Class>::Alloc(self, object_array_class.Get(),
kClassRootsMax));
CHECK(!class_roots_.IsNull());
SetClassRoot(kJavaLangClass, java_lang_Class.Get());
SetClassRoot(kJavaLangObject, java_lang_Object.Get());
SetClassRoot(kClassArrayClass, class_array_class.Get());
SetClassRoot(kObjectArrayClass, object_array_class.Get());
SetClassRoot(kCharArrayClass, char_array_class.Get());
SetClassRoot(kJavaLangString, java_lang_String.Get());
SetClassRoot(kJavaLangRefReference, java_lang_ref_Reference.Get());
// Setup the primitive type classes.
SetClassRoot(kPrimitiveBoolean, CreatePrimitiveClass(self, Primitive::kPrimBoolean));
SetClassRoot(kPrimitiveByte, CreatePrimitiveClass(self, Primitive::kPrimByte));
SetClassRoot(kPrimitiveShort, CreatePrimitiveClass(self, Primitive::kPrimShort));
SetClassRoot(kPrimitiveInt, CreatePrimitiveClass(self, Primitive::kPrimInt));
SetClassRoot(kPrimitiveLong, CreatePrimitiveClass(self, Primitive::kPrimLong));
SetClassRoot(kPrimitiveFloat, CreatePrimitiveClass(self, Primitive::kPrimFloat));
SetClassRoot(kPrimitiveDouble, CreatePrimitiveClass(self, Primitive::kPrimDouble));
SetClassRoot(kPrimitiveVoid, CreatePrimitiveClass(self, Primitive::kPrimVoid));
// Create array interface entries to populate once we can load system classes.
array_iftable_ = GcRoot<mirror::IfTable>(AllocIfTable(self, 2));
// Create int array type for AllocDexCache (done in AppendToBootClassPath).
Handle<mirror::Class> int_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_))));
int_array_class->SetComponentType(GetClassRoot(kPrimitiveInt));
mirror::IntArray::SetArrayClass(int_array_class.Get());
SetClassRoot(kIntArrayClass, int_array_class.Get());
// Create long array type for AllocDexCache (done in AppendToBootClassPath).
Handle<mirror::Class> long_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Array::ClassSize(image_pointer_size_))));
long_array_class->SetComponentType(GetClassRoot(kPrimitiveLong));
mirror::LongArray::SetArrayClass(long_array_class.Get());
SetClassRoot(kLongArrayClass, long_array_class.Get());
// now that these are registered, we can use AllocClass() and AllocObjectArray
// Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache.
Handle<mirror::Class> java_lang_DexCache(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::DexCache::ClassSize(image_pointer_size_))));
SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get());
java_lang_DexCache->SetDexCacheClass();
java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize());
mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusResolved, self);
// Set up array classes for string, field, method
Handle<mirror::Class> object_array_string(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(),
mirror::ObjectArray<mirror::String>::ClassSize(image_pointer_size_))));
object_array_string->SetComponentType(java_lang_String.Get());
SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get());
LinearAlloc* linear_alloc = runtime->GetLinearAlloc();
// Create runtime resolution and imt conflict methods.
runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod(linear_alloc));
runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod(linear_alloc));
// Setup boot_class_path_ and register class_path now that we can use AllocObjectArray to create
// DexCache instances. Needs to be after String, Field, Method arrays since AllocDexCache uses
// these roots.
if (boot_class_path.empty()) {
*error_msg = "Boot classpath is empty.";
return false;
}
for (auto& dex_file : boot_class_path) {
if (dex_file.get() == nullptr) {
*error_msg = "Null dex file.";
return false;
}
AppendToBootClassPath(self, *dex_file);
boot_dex_files_.push_back(std::move(dex_file));
}
// now we can use FindSystemClass
// run char class through InitializePrimitiveClass to finish init
InitializePrimitiveClass(char_class.Get(), Primitive::kPrimChar);
SetClassRoot(kPrimitiveChar, char_class.Get()); // needs descriptor
// Set up GenericJNI entrypoint. That is mainly a hack for common_compiler_test.h so that
// we do not need friend classes or a publicly exposed setter.
quick_generic_jni_trampoline_ = GetQuickGenericJniStub();
if (!runtime->IsAotCompiler()) {
// We need to set up the generic trampolines since we don't have an image.
quick_resolution_trampoline_ = GetQuickResolutionStub();
quick_imt_conflict_trampoline_ = GetQuickImtConflictStub();
quick_to_interpreter_bridge_trampoline_ = GetQuickToInterpreterBridge();
}
// Object, String and DexCache need to be rerun through FindSystemClass to finish init
mirror::Class::SetStatus(java_lang_Object, mirror::Class::kStatusNotReady, self);
CheckSystemClass(self, java_lang_Object, "Ljava/lang/Object;");
CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize());
mirror::Class::SetStatus(java_lang_String, mirror::Class::kStatusNotReady, self);
CheckSystemClass(self, java_lang_String, "Ljava/lang/String;");
mirror::Class::SetStatus(java_lang_DexCache, mirror::Class::kStatusNotReady, self);
CheckSystemClass(self, java_lang_DexCache, "Ljava/lang/DexCache;");
CHECK_EQ(java_lang_DexCache->GetObjectSize(), mirror::DexCache::InstanceSize());
// Setup the primitive array type classes - can't be done until Object has a vtable.
SetClassRoot(kBooleanArrayClass, FindSystemClass(self, "[Z"));
mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
SetClassRoot(kByteArrayClass, FindSystemClass(self, "[B"));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
CheckSystemClass(self, char_array_class, "[C");
SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S"));
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
CheckSystemClass(self, int_array_class, "[I");
CheckSystemClass(self, long_array_class, "[J");
SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F"));
mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D"));
mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
// Run Class through FindSystemClass. This initializes the dex_cache_ fields and register it
// in class_table_.
CheckSystemClass(self, java_lang_Class, "Ljava/lang/Class;");
CheckSystemClass(self, class_array_class, "[Ljava/lang/Class;");
CheckSystemClass(self, object_array_class, "[Ljava/lang/Object;");
// Setup the single, global copy of "iftable".
auto java_lang_Cloneable = hs.NewHandle(FindSystemClass(self, "Ljava/lang/Cloneable;"));
CHECK(java_lang_Cloneable.Get() != nullptr);
auto java_io_Serializable = hs.NewHandle(FindSystemClass(self, "Ljava/io/Serializable;"));
CHECK(java_io_Serializable.Get() != nullptr);
// We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to
// crawl up and explicitly list all of the supers as well.
array_iftable_.Read()->SetInterface(0, java_lang_Cloneable.Get());
array_iftable_.Read()->SetInterface(1, java_io_Serializable.Get());
// Sanity check Class[] and Object[]'s interfaces. GetDirectInterface may cause thread
// suspension.
CHECK_EQ(java_lang_Cloneable.Get(),
mirror::Class::GetDirectInterface(self, class_array_class, 0));
CHECK_EQ(java_io_Serializable.Get(),
mirror::Class::GetDirectInterface(self, class_array_class, 1));
CHECK_EQ(java_lang_Cloneable.Get(),
mirror::Class::GetDirectInterface(self, object_array_class, 0));
CHECK_EQ(java_io_Serializable.Get(),
mirror::Class::GetDirectInterface(self, object_array_class, 1));
CHECK_EQ(object_array_string.Get(),
FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass)));
// End of special init trickery, all subsequent classes may be loaded via FindSystemClass.
// Create java.lang.reflect.Proxy root.
SetClassRoot(kJavaLangReflectProxy, FindSystemClass(self, "Ljava/lang/reflect/Proxy;"));
// Create java.lang.reflect.Field.class root.
auto* class_root = FindSystemClass(self, "Ljava/lang/reflect/Field;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectField, class_root);
mirror::Field::SetClass(class_root);
// Create java.lang.reflect.Field array root.
class_root = FindSystemClass(self, "[Ljava/lang/reflect/Field;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectFieldArrayClass, class_root);
mirror::Field::SetArrayClass(class_root);
// Create java.lang.reflect.Constructor.class root and array root.
class_root = FindSystemClass(self, "Ljava/lang/reflect/Constructor;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectConstructor, class_root);
mirror::Constructor::SetClass(class_root);
class_root = FindSystemClass(self, "[Ljava/lang/reflect/Constructor;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectConstructorArrayClass, class_root);
mirror::Constructor::SetArrayClass(class_root);
// Create java.lang.reflect.Method.class root and array root.
class_root = FindSystemClass(self, "Ljava/lang/reflect/Method;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectMethod, class_root);
mirror::Method::SetClass(class_root);
class_root = FindSystemClass(self, "[Ljava/lang/reflect/Method;");
CHECK(class_root != nullptr);
SetClassRoot(kJavaLangReflectMethodArrayClass, class_root);
mirror::Method::SetArrayClass(class_root);
// java.lang.ref classes need to be specially flagged, but otherwise are normal classes
// finish initializing Reference class
mirror::Class::SetStatus(java_lang_ref_Reference, mirror::Class::kStatusNotReady, self);
CheckSystemClass(self, java_lang_ref_Reference, "Ljava/lang/ref/Reference;");
CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize());
CHECK_EQ(java_lang_ref_Reference->GetClassSize(),
mirror::Reference::ClassSize(image_pointer_size_));
class_root = FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
CHECK_EQ(class_root->GetClassFlags(), mirror::kClassFlagNormal);
class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagFinalizerReference);
class_root = FindSystemClass(self, "Ljava/lang/ref/PhantomReference;");
CHECK_EQ(class_root->GetClassFlags(), mirror::kClassFlagNormal);
class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagPhantomReference);
class_root = FindSystemClass(self, "Ljava/lang/ref/SoftReference;");
CHECK_EQ(class_root->GetClassFlags(), mirror::kClassFlagNormal);
class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagSoftReference);
class_root = FindSystemClass(self, "Ljava/lang/ref/WeakReference;");
CHECK_EQ(class_root->GetClassFlags(), mirror::kClassFlagNormal);
class_root->SetClassFlags(class_root->GetClassFlags() | mirror::kClassFlagWeakReference);
// Setup the ClassLoader, verifying the object_size_.
class_root = FindSystemClass(self, "Ljava/lang/ClassLoader;");
class_root->SetClassLoaderClass();
CHECK_EQ(class_root->GetObjectSize(), mirror::ClassLoader::InstanceSize());
SetClassRoot(kJavaLangClassLoader, class_root);
// Set up java.lang.Throwable, java.lang.ClassNotFoundException, and
// java.lang.StackTraceElement as a convenience.
SetClassRoot(kJavaLangThrowable, FindSystemClass(self, "Ljava/lang/Throwable;"));
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
SetClassRoot(kJavaLangClassNotFoundException,
FindSystemClass(self, "Ljava/lang/ClassNotFoundException;"));
SetClassRoot(kJavaLangStackTraceElement, FindSystemClass(self, "Ljava/lang/StackTraceElement;"));
SetClassRoot(kJavaLangStackTraceElementArrayClass,
FindSystemClass(self, "[Ljava/lang/StackTraceElement;"));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
// Ensure void type is resolved in the core's dex cache so java.lang.Void is correctly
// initialized.
{
const DexFile& dex_file = java_lang_Object->GetDexFile();
const DexFile::TypeId* void_type_id = dex_file.FindTypeId("V");
CHECK(void_type_id != nullptr);
uint16_t void_type_idx = dex_file.GetIndexForTypeId(*void_type_id);
// Now we resolve void type so the dex cache contains it. We use java.lang.Object class
// as referrer so the used dex cache is core's one.
mirror::Class* resolved_type = ResolveType(dex_file, void_type_idx, java_lang_Object.Get());
CHECK_EQ(resolved_type, GetClassRoot(kPrimitiveVoid));
self->AssertNoPendingException();
}
// Create conflict tables that depend on the class linker.
runtime->FixupConflictTables();
FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
return true;
}
void ClassLinker::FinishInit(Thread* self) {
VLOG(startup) << "ClassLinker::FinishInit entering";
// Let the heap know some key offsets into java.lang.ref instances
// Note: we hard code the field indexes here rather than using FindInstanceField
// as the types of the field can't be resolved prior to the runtime being
// fully initialized
mirror::Class* java_lang_ref_Reference = GetClassRoot(kJavaLangRefReference);
mirror::Class* java_lang_ref_FinalizerReference =
FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
CHECK_STREQ(pendingNext->GetName(), "pendingNext");
CHECK_STREQ(pendingNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;");
ArtField* queue = java_lang_ref_Reference->GetInstanceField(1);
CHECK_STREQ(queue->GetName(), "queue");
CHECK_STREQ(queue->GetTypeDescriptor(), "Ljava/lang/ref/ReferenceQueue;");
ArtField* queueNext = java_lang_ref_Reference->GetInstanceField(2);
CHECK_STREQ(queueNext->GetName(), "queueNext");
CHECK_STREQ(queueNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;");
ArtField* referent = java_lang_ref_Reference->GetInstanceField(3);
CHECK_STREQ(referent->GetName(), "referent");
CHECK_STREQ(referent->GetTypeDescriptor(), "Ljava/lang/Object;");
ArtField* zombie = java_lang_ref_FinalizerReference->GetInstanceField(2);
CHECK_STREQ(zombie->GetName(), "zombie");
CHECK_STREQ(zombie->GetTypeDescriptor(), "Ljava/lang/Object;");
// ensure all class_roots_ are initialized
for (size_t i = 0; i < kClassRootsMax; i++) {
ClassRoot class_root = static_cast<ClassRoot>(i);
mirror::Class* klass = GetClassRoot(class_root);
CHECK(klass != nullptr);
DCHECK(klass->IsArrayClass() || klass->IsPrimitive() || klass->GetDexCache() != nullptr);
// note SetClassRoot does additional validation.
// if possible add new checks there to catch errors early
}
CHECK(!array_iftable_.IsNull());
// disable the slow paths in FindClass and CreatePrimitiveClass now
// that Object, Class, and Object[] are setup
init_done_ = true;
VLOG(startup) << "ClassLinker::FinishInit exiting";
}
void ClassLinker::RunRootClinits() {
Thread* self = Thread::Current();
for (size_t i = 0; i < ClassLinker::kClassRootsMax; ++i) {
mirror::Class* c = GetClassRoot(ClassRoot(i));
if (!c->IsArrayClass() && !c->IsPrimitive()) {
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(GetClassRoot(ClassRoot(i))));
EnsureInitialized(self, h_class, true, true);
self->AssertNoPendingException();
}
}
}
static void SanityCheckArtMethod(ArtMethod* m,
mirror::Class* expected_class,
const std::vector<gc::space::ImageSpace*>& spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
if (m->IsRuntimeMethod()) {
mirror::Class* declaring_class = m->GetDeclaringClassUnchecked();
CHECK(declaring_class == nullptr) << declaring_class << " " << PrettyMethod(m);
} else if (m->IsCopied()) {
CHECK(m->GetDeclaringClass() != nullptr) << PrettyMethod(m);
} else if (expected_class != nullptr) {
CHECK_EQ(m->GetDeclaringClassUnchecked(), expected_class) << PrettyMethod(m);
}
if (!spaces.empty()) {
bool contains = false;
for (gc::space::ImageSpace* space : spaces) {
auto& header = space->GetImageHeader();
size_t offset = reinterpret_cast<uint8_t*>(m) - space->Begin();
const ImageSection& methods = header.GetMethodsSection();
contains = contains || methods.Contains(offset);
const ImageSection& runtime_methods = header.GetRuntimeMethodsSection();
contains = contains || runtime_methods.Contains(offset);
}
CHECK(contains) << m << " not found";
}
}
static void SanityCheckArtMethodPointerArray(mirror::PointerArray* arr,
mirror::Class* expected_class,
size_t pointer_size,
const std::vector<gc::space::ImageSpace*>& spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK(arr != nullptr);
for (int32_t j = 0; j < arr->GetLength(); ++j) {
auto* method = arr->GetElementPtrSize<ArtMethod*>(j, pointer_size);
// expected_class == null means we are a dex cache.
if (expected_class != nullptr) {
CHECK(method != nullptr);
}
if (method != nullptr) {
SanityCheckArtMethod(method, expected_class, spaces);
}
}
}
static void SanityCheckArtMethodPointerArray(ArtMethod** arr,
size_t size,
size_t pointer_size,
const std::vector<gc::space::ImageSpace*>& spaces)
SHARED_REQUIRES(Locks::mutator_lock_) {
CHECK_EQ(arr != nullptr, size != 0u);
if (arr != nullptr) {
bool contains = false;
for (auto space : spaces) {
auto offset = reinterpret_cast<uint8_t*>(arr) - space->Begin();
if (space->GetImageHeader().GetImageSection(
ImageHeader::kSectionDexCacheArrays).Contains(offset)) {
contains = true;
break;
}
}
CHECK(contains);
}
for (size_t j = 0; j < size; ++j) {
ArtMethod* method = mirror::DexCache::GetElementPtrSize(arr, j, pointer_size);
// expected_class == null means we are a dex cache.
if (method != nullptr) {
SanityCheckArtMethod(method, nullptr, spaces);
}
}
}
static void SanityCheckObjectsCallback(mirror::Object* obj, void* arg ATTRIBUTE_UNUSED)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(obj != nullptr);
CHECK(obj->GetClass() != nullptr) << "Null class in object " << obj;
CHECK(obj->GetClass()->GetClass() != nullptr) << "Null class class " << obj;
if (obj->IsClass()) {
auto klass = obj->AsClass();
for (ArtField& field : klass->GetIFields()) {
CHECK_EQ(field.GetDeclaringClass(), klass);
}
for (ArtField& field : klass->GetSFields()) {
CHECK_EQ(field.GetDeclaringClass(), klass);
}
auto* runtime = Runtime::Current();
auto image_spaces = runtime->GetHeap()->GetBootImageSpaces();
auto pointer_size = runtime->GetClassLinker()->GetImagePointerSize();
for (auto& m : klass->GetMethods(pointer_size)) {
SanityCheckArtMethod(&m, klass, image_spaces);
}
auto* vtable = klass->GetVTable();
if (vtable != nullptr) {
SanityCheckArtMethodPointerArray(vtable, nullptr, pointer_size, image_spaces);
}
if (klass->ShouldHaveEmbeddedImtAndVTable()) {
for (size_t i = 0; i < mirror::Class::kImtSize; ++i) {
SanityCheckArtMethod(
klass->GetEmbeddedImTableEntry(i, pointer_size), nullptr, image_spaces);
}
for (int32_t i = 0; i < klass->GetEmbeddedVTableLength(); ++i) {
SanityCheckArtMethod(klass->GetEmbeddedVTableEntry(i, pointer_size), nullptr, image_spaces);
}
}
auto* iftable = klass->GetIfTable();
if (iftable != nullptr) {
for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) {
if (iftable->GetMethodArrayCount(i) > 0) {
SanityCheckArtMethodPointerArray(
iftable->GetMethodArray(i), nullptr, pointer_size, image_spaces);
}
}
}
}
}
// Set image methods' entry point to interpreter.
class SetInterpreterEntrypointArtMethodVisitor : public ArtMethodVisitor {
public:
explicit SetInterpreterEntrypointArtMethodVisitor(size_t image_pointer_size)
: image_pointer_size_(image_pointer_size) {}
void Visit(ArtMethod* method) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
if (kIsDebugBuild && !method->IsRuntimeMethod()) {
CHECK(method->GetDeclaringClass() != nullptr);
}
if (!method->IsNative() && !method->IsRuntimeMethod() && !method->IsResolutionMethod()) {
method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
image_pointer_size_);
}
}
private:
const size_t image_pointer_size_;
DISALLOW_COPY_AND_ASSIGN(SetInterpreterEntrypointArtMethodVisitor);
};
struct TrampolineCheckData {
const void* quick_resolution_trampoline;
const void* quick_imt_conflict_trampoline;
const void* quick_generic_jni_trampoline;
const void* quick_to_interpreter_bridge_trampoline;
size_t pointer_size;
ArtMethod* m;
bool error;
};
static void CheckTrampolines(mirror::Object* obj, void* arg) NO_THREAD_SAFETY_ANALYSIS {
if (obj->IsClass()) {
mirror::Class* klass = obj->AsClass();
TrampolineCheckData* d = reinterpret_cast<TrampolineCheckData*>(arg);
for (ArtMethod& m : klass->GetMethods(d->pointer_size)) {
const void* entrypoint = m.GetEntryPointFromQuickCompiledCodePtrSize(d->pointer_size);
if (entrypoint == d->quick_resolution_trampoline ||
entrypoint == d->quick_imt_conflict_trampoline ||
entrypoint == d->quick_generic_jni_trampoline ||
entrypoint == d->quick_to_interpreter_bridge_trampoline) {
d->m = &m;
d->error = true;
return;
}
}
}
}
bool ClassLinker::InitFromBootImage(std::string* error_msg) {
VLOG(startup) << __FUNCTION__ << " entering";
CHECK(!init_done_);
Runtime* const runtime = Runtime::Current();
Thread* const self = Thread::Current();
gc::Heap* const heap = runtime->GetHeap();
std::vector<gc::space::ImageSpace*> spaces = heap->GetBootImageSpaces();
CHECK(!spaces.empty());
image_pointer_size_ = spaces[0]->GetImageHeader().GetPointerSize();
if (!ValidPointerSize(image_pointer_size_)) {
*error_msg = StringPrintf("Invalid image pointer size: %zu", image_pointer_size_);
return false;
}
if (!runtime->IsAotCompiler()) {
// Only the Aot compiler supports having an image with a different pointer size than the
// runtime. This happens on the host for compiling 32 bit tests since we use a 64 bit libart
// compiler. We may also use 32 bit dex2oat on a system with 64 bit apps.
if (image_pointer_size_ != sizeof(void*)) {
*error_msg = StringPrintf("Runtime must use current image pointer size: %zu vs %zu",
image_pointer_size_,
sizeof(void*));
return false;
}
}
dex_cache_boot_image_class_lookup_required_ = true;
std::vector<const OatFile*> oat_files =
runtime->GetOatFileManager().RegisterImageOatFiles(spaces);
DCHECK(!oat_files.empty());
const OatHeader& default_oat_header = oat_files[0]->GetOatHeader();
CHECK_EQ(default_oat_header.GetImageFileLocationOatChecksum(), 0U);
CHECK_EQ(default_oat_header.GetImageFileLocationOatDataBegin(), 0U);
const char* image_file_location = oat_files[0]->GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
CHECK(image_file_location == nullptr || *image_file_location == 0);
quick_resolution_trampoline_ = default_oat_header.GetQuickResolutionTrampoline();
quick_imt_conflict_trampoline_ = default_oat_header.GetQuickImtConflictTrampoline();
quick_generic_jni_trampoline_ = default_oat_header.GetQuickGenericJniTrampoline();
quick_to_interpreter_bridge_trampoline_ = default_oat_header.GetQuickToInterpreterBridge();
if (kIsDebugBuild) {
// Check that the other images use the same trampoline.
for (size_t i = 1; i < oat_files.size(); ++i) {
const OatHeader& ith_oat_header = oat_files[i]->GetOatHeader();
const void* ith_quick_resolution_trampoline =
ith_oat_header.GetQuickResolutionTrampoline();
const void* ith_quick_imt_conflict_trampoline =
ith_oat_header.GetQuickImtConflictTrampoline();
const void* ith_quick_generic_jni_trampoline =
ith_oat_header.GetQuickGenericJniTrampoline();
const void* ith_quick_to_interpreter_bridge_trampoline =
ith_oat_header.GetQuickToInterpreterBridge();
if (ith_quick_resolution_trampoline != quick_resolution_trampoline_ ||
ith_quick_imt_conflict_trampoline != quick_imt_conflict_trampoline_ ||
ith_quick_generic_jni_trampoline != quick_generic_jni_trampoline_ ||
ith_quick_to_interpreter_bridge_trampoline != quick_to_interpreter_bridge_trampoline_) {
// Make sure that all methods in this image do not contain those trampolines as
// entrypoints. Otherwise the class-linker won't be able to work with a single set.
TrampolineCheckData data;
data.error = false;
data.pointer_size = GetImagePointerSize();
data.quick_resolution_trampoline = ith_quick_resolution_trampoline;
data.quick_imt_conflict_trampoline = ith_quick_imt_conflict_trampoline;
data.quick_generic_jni_trampoline = ith_quick_generic_jni_trampoline;
data.quick_to_interpreter_bridge_trampoline = ith_quick_to_interpreter_bridge_trampoline;
ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
spaces[i]->GetLiveBitmap()->Walk(CheckTrampolines, &data);
if (data.error) {
ArtMethod* m = data.m;
LOG(ERROR) << "Found a broken ArtMethod: " << PrettyMethod(m);
*error_msg = "Found an ArtMethod with a bad entrypoint";
return false;
}
}
}
}
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(
down_cast<mirror::ObjectArray<mirror::Class>*>(
spaces[0]->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)));
mirror::Class::SetClassClass(class_roots_.Read()->Get(kJavaLangClass));
// Special case of setting up the String class early so that we can test arbitrary objects
// as being Strings or not
mirror::String::SetClass(GetClassRoot(kJavaLangString));
mirror::Class* java_lang_Object = GetClassRoot(kJavaLangObject);
java_lang_Object->SetObjectSize(sizeof(mirror::Object));
// Allocate in non-movable so that it's possible to check if a JNI weak global ref has been
// cleared without triggering the read barrier and unintentionally mark the sentinel alive.
runtime->SetSentinel(heap->AllocNonMovableObject<true>(
self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor()));
// reinit array_iftable_ from any array class instance, they should be ==
array_iftable_ = GcRoot<mirror::IfTable>(GetClassRoot(kObjectArrayClass)->GetIfTable());
DCHECK_EQ(array_iftable_.Read(), GetClassRoot(kBooleanArrayClass)->GetIfTable());
// String class root was set above
mirror::Field::SetClass(GetClassRoot(kJavaLangReflectField));
mirror::Field::SetArrayClass(GetClassRoot(kJavaLangReflectFieldArrayClass));
mirror::Constructor::SetClass(GetClassRoot(kJavaLangReflectConstructor));
mirror::Constructor::SetArrayClass(GetClassRoot(kJavaLangReflectConstructorArrayClass));
mirror::Method::SetClass(GetClassRoot(kJavaLangReflectMethod));
mirror::Method::SetArrayClass(GetClassRoot(kJavaLangReflectMethodArrayClass));
mirror::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
mirror::BooleanArray::SetArrayClass(GetClassRoot(kBooleanArrayClass));
mirror::ByteArray::SetArrayClass(GetClassRoot(kByteArrayClass));
mirror::CharArray::SetArrayClass(GetClassRoot(kCharArrayClass));
mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
mirror::IntArray::SetArrayClass(GetClassRoot(kIntArrayClass));
mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass));
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
mirror::Throwable::SetClass(GetClassRoot(kJavaLangThrowable));
mirror::StackTraceElement::SetClass(GetClassRoot(kJavaLangStackTraceElement));
for (gc::space::ImageSpace* image_space : spaces) {
// Boot class loader, use a null handle.
std::vector<std::unique_ptr<const DexFile>> dex_files;
if (!AddImageSpace(image_space,
ScopedNullHandle<mirror::ClassLoader>(),
/*dex_elements*/nullptr,
/*dex_location*/nullptr,
/*out*/&dex_files,
error_msg)) {
return false;
}
// Append opened dex files at the end.
boot_dex_files_.insert(boot_dex_files_.end(),
std::make_move_iterator(dex_files.begin()),
std::make_move_iterator(dex_files.end()));
}
FinishInit(self);
VLOG(startup) << __FUNCTION__ << " exiting";
return true;
}
bool ClassLinker::IsBootClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
mirror::ClassLoader* class_loader) {
return class_loader == nullptr ||
class_loader->GetClass() ==
soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader);
}
static mirror::String* GetDexPathListElementName(ScopedObjectAccessUnchecked& soa,
mirror::Object* element)
SHARED_REQUIRES(Locks::mutator_lock_) {
ArtField* const dex_file_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
ArtField* const dex_file_name_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_fileName);
DCHECK(dex_file_field != nullptr);
DCHECK(dex_file_name_field != nullptr);
DCHECK(element != nullptr);
CHECK_EQ(dex_file_field->GetDeclaringClass(), element->GetClass()) << PrettyTypeOf(element);
mirror::Object* dex_file = dex_file_field->GetObject(element);
if (dex_file == nullptr) {
return nullptr;
}
mirror::Object* const name_object = dex_file_name_field->GetObject(dex_file);
if (name_object != nullptr) {
return name_object->AsString();
}
return nullptr;
}
static bool FlattenPathClassLoader(mirror::ClassLoader* class_loader,
std::list<mirror::String*>* out_dex_file_names,
std::string* error_msg)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(out_dex_file_names != nullptr);
DCHECK(error_msg != nullptr);
ScopedObjectAccessUnchecked soa(Thread::Current());
ArtField* const dex_path_list_field =
soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList);
ArtField* const dex_elements_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements);
CHECK(dex_path_list_field != nullptr);
CHECK(dex_elements_field != nullptr);
while (!ClassLinker::IsBootClassLoader(soa, class_loader)) {
if (class_loader->GetClass() !=
soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) {
*error_msg = StringPrintf("Unknown class loader type %s", PrettyTypeOf(class_loader).c_str());
// Unsupported class loader.
return false;
}
mirror::Object* dex_path_list = dex_path_list_field->GetObject(class_loader);
if (dex_path_list != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
mirror::Object* dex_elements_obj = dex_elements_field->GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj != nullptr) {
mirror::ObjectArray<mirror::Object>* dex_elements =
dex_elements_obj->AsObjectArray<mirror::Object>();
// Reverse order since we insert the parent at the front.
for (int32_t i = dex_elements->GetLength() - 1; i >= 0; --i) {
mirror::Object* const element = dex_elements->GetWithoutChecks(i);
if (element == nullptr) {
*error_msg = StringPrintf("Null dex element at index %d", i);
return false;
}
mirror::String* const name = GetDexPathListElementName(soa, element);
if (name == nullptr) {
*error_msg = StringPrintf("Null name for dex element at index %d", i);
return false;
}
out_dex_file_names->push_front(name);
}
}
}
class_loader = class_loader->GetParent();
}
return true;
}
class FixupArtMethodArrayVisitor : public ArtMethodVisitor {
public:
explicit FixupArtMethodArrayVisitor(const ImageHeader& header) : header_(header) {}
virtual void Visit(ArtMethod* method) SHARED_REQUIRES(Locks::mutator_lock_) {
GcRoot<mirror::Class>* resolved_types = method->GetDexCacheResolvedTypes(sizeof(void*));
const bool is_copied = method->IsCopied();
if (resolved_types != nullptr) {
bool in_image_space = false;
if (kIsDebugBuild || is_copied) {
in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
reinterpret_cast<const uint8_t*>(resolved_types) - header_.GetImageBegin());
}
// Must be in image space for non-miranda method.
DCHECK(is_copied || in_image_space)
<< resolved_types << " is not in image starting at "
<< reinterpret_cast<void*>(header_.GetImageBegin());
if (!is_copied || in_image_space) {
// Go through the array so that we don't need to do a slow map lookup.
method->SetDexCacheResolvedTypes(*reinterpret_cast<GcRoot<mirror::Class>**>(resolved_types),
sizeof(void*));
}
}
ArtMethod** resolved_methods = method->GetDexCacheResolvedMethods(sizeof(void*));
if (resolved_methods != nullptr) {
bool in_image_space = false;
if (kIsDebugBuild || is_copied) {
in_image_space = header_.GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
reinterpret_cast<const uint8_t*>(resolved_methods) - header_.GetImageBegin());
}
// Must be in image space for non-miranda method.
DCHECK(is_copied || in_image_space)
<< resolved_methods << " is not in image starting at "
<< reinterpret_cast<void*>(header_.GetImageBegin());
if (!is_copied || in_image_space) {
// Go through the array so that we don't need to do a slow map lookup.
method->SetDexCacheResolvedMethods(*reinterpret_cast<ArtMethod***>(resolved_methods),
sizeof(void*));
}
}
}
private:
const ImageHeader& header_;
};
class VerifyClassInTableArtMethodVisitor : public ArtMethodVisitor {
public:
explicit VerifyClassInTableArtMethodVisitor(ClassTable* table) : table_(table) {}
virtual void Visit(ArtMethod* method)
SHARED_REQUIRES(Locks::mutator_lock_, Locks::classlinker_classes_lock_) {
mirror::Class* klass = method->GetDeclaringClass();
if (klass != nullptr && !Runtime::Current()->GetHeap()->ObjectIsInBootImageSpace(klass)) {
CHECK_EQ(table_->LookupByDescriptor(klass), klass) << PrettyClass(klass);
}
}
private:
ClassTable* const table_;
};
class VerifyDeclaringClassVisitor : public ArtMethodVisitor {
public:
VerifyDeclaringClassVisitor() SHARED_REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_)
: live_bitmap_(Runtime::Current()->GetHeap()->GetLiveBitmap()) {}
virtual void Visit(ArtMethod* method)
SHARED_REQUIRES(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
mirror::Class* klass = method->GetDeclaringClassUnchecked();
if (klass != nullptr) {
CHECK(live_bitmap_->Test(klass)) << "Image method has unmarked declaring class";
}
}
private:
gc::accounting::HeapBitmap* const live_bitmap_;
};
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;
}
// Update the class loader and resolved string dex cache array of classes. Should only be used on
// classes in the image space.
class UpdateClassLoaderAndResolvedStringsVisitor {
public:
UpdateClassLoaderAndResolvedStringsVisitor(gc::space::ImageSpace* space,
mirror::ClassLoader* class_loader,
bool forward_strings)
: space_(space),
class_loader_(class_loader),
forward_strings_(forward_strings) {}
bool operator()(mirror::Class* klass) const SHARED_REQUIRES(Locks::mutator_lock_) {
if (forward_strings_) {
GcRoot<mirror::String>* strings = klass->GetDexCacheStrings();
if (strings != nullptr) {
DCHECK(
space_->GetImageHeader().GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
reinterpret_cast<uint8_t*>(strings) - space_->Begin()))
<< "String dex cache array for " << PrettyClass(klass) << " is not in app image";
// Dex caches have already been updated, so take the strings pointer from there.
GcRoot<mirror::String>* new_strings = klass->GetDexCache()->GetStrings();
DCHECK_NE(strings, new_strings);
klass->SetDexCacheStrings(new_strings);
}
}
// Finally, update class loader.
klass->SetClassLoader(class_loader_);
return true;
}
gc::space::ImageSpace* const space_;
mirror::ClassLoader* const class_loader_;
const bool forward_strings_;
};
static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
const char* location,
std::string* error_msg)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(error_msg != nullptr);
std::unique_ptr<const DexFile> dex_file;
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(location, nullptr);
if (oat_dex_file == nullptr) {
*error_msg = StringPrintf("Failed finding oat dex file for %s %s",
oat_file->GetLocation().c_str(),
location);
return std::unique_ptr<const DexFile>();
}
std::string inner_error_msg;
dex_file = oat_dex_file->OpenDexFile(&inner_error_msg);
if (dex_file == nullptr) {
*error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'",
location,
oat_file->GetLocation().c_str(),
inner_error_msg.c_str());
return std::unique_ptr<const DexFile>();
}
if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) {
*error_msg = StringPrintf("Checksums do not match for %s: %x vs %x",
location,
dex_file->GetLocationChecksum(),
oat_dex_file->GetDexFileLocationChecksum());
return std::unique_ptr<const DexFile>();
}
return dex_file;
}
bool ClassLinker::OpenImageDexFiles(gc::space::ImageSpace* space,
std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
std::string* error_msg) {
ScopedAssertNoThreadSuspension nts(Thread::Current(), __FUNCTION__);
const ImageHeader& header = space->GetImageHeader();
mirror::Object* dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches);
DCHECK(dex_caches_object != nullptr);
mirror::ObjectArray<mirror::DexCache>* dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>();
const OatFile* oat_file = space->GetOatFile();
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
mirror::DexCache* dex_cache = dex_caches->Get(i);
std::string dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
dex_file_location.c_str(),
error_msg);
if (dex_file == nullptr) {
return false;
}
dex_cache->SetDexFile(dex_file.get());
out_dex_files->push_back(std::move(dex_file));
}
return true;
}
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;
}
bool ClassLinker::ClassInClassTable(mirror::Class* klass) {
ClassTable* const class_table = ClassTableForClassLoader(klass->GetClassLoader());
return class_table != nullptr && class_table->Contains(klass);
}
void ClassLinker::VisitClassRoots(RootVisitor* visitor, VisitRootFlags flags) {
// Acquire tracing_enabled before locking class linker lock to prevent lock order violation. Since
// enabling tracing requires the mutator lock, there are no race conditions here.
const bool tracing_enabled = Trace::IsTracingEnabled();
Thread* const self = Thread::Current();
WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
visitor, RootInfo(kRootStickyClass));
if ((flags & kVisitRootFlagAllRoots) != 0) {
// Argument for how root visiting deals with ArtField and ArtMethod roots.
// There is 3 GC cases to handle:
// Non moving concurrent:
// This case is easy to handle since the reference members of ArtMethod and ArtFields are held
// live by the class and class roots.
//
// Moving non-concurrent:
// This case needs to call visit VisitNativeRoots in case the classes or dex cache arrays move.
// To prevent missing roots, this case needs to ensure that there is no
// suspend points between the point which we allocate ArtMethod arrays and place them in a
// class which is in the class table.
//
// Moving concurrent:
// Need to make sure to not copy ArtMethods without doing read barriers since the roots are
// marked concurrently and we don't hold the classlinker_classes_lock_ when we do the copy.
boot_class_table_.VisitRoots(buffered_visitor);
// If tracing is enabled, then mark all the class loaders to prevent unloading.
if (tracing_enabled) {
for (const ClassLoaderData& data : class_loaders_) {
GcRoot<mirror::Object> root(GcRoot<mirror::Object>(self->DecodeJObject(data.weak_root)));
root.VisitRoot(visitor, RootInfo(kRootVMInternal));
}
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
root.VisitRoot(visitor, RootInfo(kRootStickyClass));
mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
// Concurrent moving GC marked new roots through the to-space invariant.
CHECK_EQ(new_ref, old_ref);
}
}
buffered_visitor.Flush(); // Flush before clearing new_class_roots_.
if ((flags & kVisitRootFlagClearRootLog) != 0) {
new_class_roots_.clear();
}
if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
log_new_class_table_roots_ = true;
} else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
log_new_class_table_roots_ = false;
}
// We deliberately ignore the class roots in the image since we
// handle image roots by using the MS/CMS rescanning of dirty cards.
}
// Keep in sync with InitCallback. Anything we visit, we need to
// reinit references to when reinitializing a ClassLinker from a
// mapped image.
void ClassLinker::VisitRoots(RootVisitor* visitor, VisitRootFlags flags) {
class_roots_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
VisitClassRoots(visitor, flags);
array_iftable_.VisitRootIfNonNull(visitor, RootInfo(kRootVMInternal));
// Instead of visiting the find_array_class_cache_ drop it so that it doesn't prevent class
// unloading if we are marking roots.
DropFindArrayClassCache();
}
class VisitClassLoaderClassesVisitor : public ClassLoaderVisitor {
public:
explicit VisitClassLoaderClassesVisitor(ClassVisitor* visitor)
: visitor_(visitor),
done_(false) {}
void Visit(mirror::ClassLoader* class_loader)
SHARED_REQUIRES(Locks::classlinker_classes_lock_, Locks::mutator_lock_) OVERRIDE {
ClassTable* const class_table = class_loader->GetClassTable();
if (!done_ && class_table != nullptr && !class_table->Visit(*visitor_)) {
// If the visitor ClassTable returns false it means that we don't need to continue.
done_ = true;
}
}
private:
ClassVisitor* const visitor_;
// If done is true then we don't need to do any more visiting.
bool done_;
};
void ClassLinker::VisitClassesInternal(ClassVisitor* visitor) {
if (boot_class_table_.Visit(*visitor)) {
VisitClassLoaderClassesVisitor loader_visitor(visitor);
VisitClassLoaders(&loader_visitor);
}
}
void ClassLinker::VisitClasses(ClassVisitor* visitor) {
if (dex_cache_boot_image_class_lookup_required_) {
AddBootImageClassesToClassTable();
}
Thread* const self = Thread::Current();
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
// Not safe to have thread suspension when we are holding a lock.
if (self != nullptr) {
ScopedAssertNoThreadSuspension nts(self, __FUNCTION__);
VisitClassesInternal(visitor);
} else {
VisitClassesInternal(visitor);
}
}
class GetClassesInToVector : public ClassVisitor {
public:
bool operator()(mirror::Class* klass) OVERRIDE {
classes_.push_back(klass);
return true;
}
std::vector<mirror::Class*> classes_;
};
class GetClassInToObjectArray : public ClassVisitor {
public:
explicit GetClassInToObjectArray(mirror::ObjectArray<mirror::Class>* arr)
: arr_(arr), index_(0) {}
bool operator()(mirror::Class* klass) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
++index_;
if (index_ <= arr_->GetLength()) {
arr_->Set(index_ - 1, klass);
return true;
}
return false;
}
bool Succeeded() const SHARED_REQUIRES(Locks::mutator_lock_) {
return index_ <= arr_->GetLength();
}
private:
mirror::ObjectArray<mirror::Class>* const arr_;
int32_t index_;
};
void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor) {
// TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem
// is avoiding duplicates.
Thread* const self = Thread::Current();
if (!kMovingClasses) {
ScopedAssertNoThreadSuspension nts(self, __FUNCTION__);
GetClassesInToVector accumulator;
VisitClasses(&accumulator);
for (mirror::Class* klass : accumulator.classes_) {
if (!visitor->operator()(klass)) {
return;
}
}
} else {
StackHandleScope<1> hs(self);
auto classes = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
// We size the array assuming classes won't be added to the class table during the visit.
// If this assumption fails we iterate again.
while (true) {
size_t class_table_size;
{
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
// Add 100 in case new classes get loaded when we are filling in the object array.
class_table_size = NumZygoteClasses() + NumNonZygoteClasses() + 100;
}
mirror::Class* class_type = mirror::Class::GetJavaLangClass();
mirror::Class* array_of_class = FindArrayClass(self, &class_type);
classes.Assign(
mirror::ObjectArray<mirror::Class>::Alloc(self, array_of_class, class_table_size));
CHECK(classes.Get() != nullptr); // OOME.
GetClassInToObjectArray accumulator(classes.Get());
VisitClasses(&accumulator);
if (accumulator.Succeeded()) {
break;
}
}
for (int32_t i = 0; i < classes->GetLength(); ++i) {
// If the class table shrank during creation of the clases array we expect null elements. If
// the class table grew then the loop repeats. If classes are created after the loop has
// finished then we don't visit.
mirror::Class* klass = classes->Get(i);
if (klass != nullptr && !visitor->operator()(klass)) {
return;
}
}
}
}
ClassLinker::~ClassLinker() {
mirror::Class::ResetClass();
mirror::Constructor::ResetClass();
mirror::Field::ResetClass();
mirror::Method::ResetClass();
mirror::Reference::ResetClass();
mirror::StackTraceElement::ResetClass();
mirror::String::ResetClass();
mirror::Throwable::ResetClass();
mirror::BooleanArray::ResetArrayClass();
mirror::ByteArray::ResetArrayClass();
mirror::CharArray::ResetArrayClass();
mirror::Constructor::ResetArrayClass();
mirror::DoubleArray::ResetArrayClass();
mirror::Field::ResetArrayClass();
mirror::FloatArray::ResetArrayClass();
mirror::Method::ResetArrayClass();
mirror::IntArray::ResetArrayClass();
mirror::LongArray::ResetArrayClass();
mirror::ShortArray::ResetArrayClass();
Thread* const self = Thread::Current();
for (const ClassLoaderData& data : class_loaders_) {
DeleteClassLoader(self, data);
}
class_loaders_.clear();
}
void ClassLinker::DeleteClassLoader(Thread* self, const ClassLoaderData& data) {
Runtime* const runtime = Runtime::Current();
JavaVMExt* const vm = runtime->GetJavaVM();
vm->DeleteWeakGlobalRef(self, data.weak_root);
// Notify the JIT that we need to remove the methods and/or profiling info.
if (runtime->GetJit() != nullptr) {
jit::JitCodeCache* code_cache = runtime->GetJit()->GetCodeCache();
if (code_cache != nullptr) {
code_cache->RemoveMethodsIn(self, *data.allocator);
}
}
delete data.allocator;
delete data.class_table;
}
mirror::PointerArray* ClassLinker::AllocPointerArray(Thread* self, size_t length) {
return down_cast<mirror::PointerArray*>(image_pointer_size_ == 8u ?
static_cast<mirror::Array*>(mirror::LongArray::Alloc(self, length)) :
static_cast<mirror::Array*>(mirror::IntArray::Alloc(self, length)));
}
mirror::DexCache* ClassLinker::AllocDexCache(Thread* self,
const DexFile& dex_file,
LinearAlloc* linear_alloc) {
StackHandleScope<6> hs(self);
auto dex_cache(hs.NewHandle(down_cast<mirror::DexCache*>(
GetClassRoot(kJavaLangDexCache)->AllocObject(self))));
if (dex_cache.Get() == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
auto location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str())));
if (location.Get() == nullptr) {
self->AssertPendingOOMException();
return nullptr;
}
DexCacheArraysLayout layout(image_pointer_size_, &dex_file);
uint8_t* raw_arrays = nullptr;
if (dex_file.GetOatDexFile() != nullptr &&
dex_file.GetOatDexFile()->GetDexCacheArrays() != nullptr) {
raw_arrays = dex_file.GetOatDexFile()->GetDexCacheArrays();
} else if (dex_file.NumStringIds() != 0u || dex_file.NumTypeIds() != 0u ||
dex_file.NumMethodIds() != 0u || dex_file.NumFieldIds() != 0u) {
// NOTE: We "leak" the raw_arrays because we never destroy the dex cache.
DCHECK(image_pointer_size_ == 4u || image_pointer_size_ == 8u);
// Zero-initialized.
raw_arrays = reinterpret_cast<uint8_t*>(linear_alloc->Alloc(self, layout.Size()));
}
GcRoot<mirror::String>* strings = (dex_file.NumStringIds() == 0u) ? nullptr :
reinterpret_cast<GcRoot<mirror::String>*>(raw_arrays + layout.StringsOffset());
GcRoot<mirror::Class>* types = (dex_file.NumTypeIds() == 0u) ? nullptr :
reinterpret_cast<GcRoot<mirror::Class>*>(raw_arrays + layout.TypesOffset());
ArtMethod** methods = (dex_file.NumMethodIds() == 0u) ? nullptr :
reinterpret_cast<ArtMethod**>(raw_arrays + layout.MethodsOffset());
ArtField** fields = (dex_file.NumFieldIds() == 0u) ? nullptr :
reinterpret_cast<ArtField**>(raw_arrays + layout.FieldsOffset());
// Temporarily disabled since the JIT may hit this case. b/29083330
if (kIsDebugBuild && (false)) {
// Sanity check to make sure all the dex cache arrays are empty. b/28992179
for (size_t i = 0; i < dex_file.NumStringIds(); ++i) {
CHECK(strings[i].Read<kWithoutReadBarrier>() == nullptr);
}
for (size_t i = 0; i < dex_file.NumTypeIds(); ++i) {
CHECK(types[i].Read<kWithoutReadBarrier>() == nullptr);
}
for (size_t i = 0; i < dex_file.NumMethodIds(); ++i) {
CHECK(mirror::DexCache::GetElementPtrSize(methods, i, image_pointer_size_) == nullptr);
}
for (size_t i = 0; i < dex_file.NumFieldIds(); ++i) {
CHECK(mirror::DexCache::GetElementPtrSize(fields, i, image_pointer_size_) == nullptr);
}
}
dex_cache->Init(&dex_file,
location.Get(),
strings,
dex_file.NumStringIds(),
types,
dex_file.NumTypeIds(),
methods,
dex_file.NumMethodIds(),
fields,
dex_file.NumFieldIds(),
image_pointer_size_);
return dex_cache.Get();
}
mirror::Class* ClassLinker::AllocClass(Thread* self, mirror::Class* java_lang_Class,
uint32_t class_size) {
DCHECK_GE(class_size, sizeof(mirror::Class));
gc::Heap* heap = Runtime::Current()->GetHeap();
mirror::Class::InitializeClassVisitor visitor(class_size);
mirror::Object* k = kMovingClasses ?
heap->AllocObject<true>(self, java_lang_Class, class_size, visitor) :
heap->AllocNonMovableObject<true>(self, java_lang_Class, class_size, visitor);
if (UNLIKELY(k == nullptr)) {
self->AssertPendingOOMException();
return nullptr;
}
return k->AsClass();
}
mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) {
return AllocClass(self, GetClassRoot(kJavaLangClass), class_size);
}
mirror::ObjectArray<mirror::StackTraceElement>* ClassLinker::AllocStackTraceElementArray(
Thread* self,
size_t length) {
return mirror::ObjectArray<mirror::StackTraceElement>::Alloc(
self, GetClassRoot(kJavaLangStackTraceElementArrayClass), length);
}
mirror::Class* ClassLinker::EnsureResolved(Thread* self,
const char* descriptor,
mirror::Class* klass) {
DCHECK(klass != nullptr);
// For temporary classes we must wait for them to be retired.
if (init_done_ && klass->IsTemp()) {
CHECK(!klass->IsResolved());
if (klass->IsErroneous()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
StackHandleScope<1> hs(self);
Handle<mirror::Class> h_class(hs.NewHandle(klass));
ObjectLock<mirror::Class> lock(self, h_class);
// Loop and wait for the resolving thread to retire this class.
while (!h_class->IsRetired() && !h_class->IsErroneous()) {
lock.WaitIgnoringInterrupts();
}
if (h_class->IsErroneous()) {
ThrowEarlierClassFailure(h_class.Get());
return nullptr;
}
CHECK(h_class->IsRetired());
// Get the updated class from class table.
klass = LookupClass(self, descriptor, ComputeModifiedUtf8Hash(descriptor),
h_class.Get()->GetClassLoader());
}
// Wait for the class if it has not already been linked.
if (!klass->IsResolved() && !klass->IsErroneous()) {
StackHandleScope<1> hs(self);
HandleWrapper<mirror::Class> h_class(hs.NewHandleWrapper(&klass));
ObjectLock<mirror::Class> lock(self, h_class);
// Check for circular dependencies between classes.
if (!h_class->IsResolved() && h_class->GetClinitThreadId() == self->GetTid()) {
ThrowClassCircularityError(h_class.Get());
mirror::Class::SetStatus(h_class, mirror::Class::kStatusError, self);
return nullptr;
}
// Wait for the pending initialization to complete.
while (!h_class->IsResolved() && !h_class->IsErroneous()) {
lock.WaitIgnoringInterrupts();
}
}
if (klass->IsErroneous()) {
ThrowEarlierClassFailure(klass);
return nullptr;
}
// Return the loaded class. No exceptions should be pending.
CHECK(klass->IsResolved()) << PrettyClass(klass);
self->AssertNoPendingException();
return klass;
}
typedef std::pair<const DexFile*, const DexFile::ClassDef*> ClassPathEntry;
// Search a collection of DexFiles for a descriptor
ClassPathEntry FindInClassPath(const char* descriptor,
size_t hash, const std::vector<const DexFile*>& class_path) {
for (const DexFile* dex_file : class_path) {
const DexFile::ClassDef* dex_class_def = dex_file->FindClassDef(descriptor, hash);
if (dex_class_def != nullptr) {
return ClassPathEntry(dex_file, dex_class_def);
}
}
return ClassPathEntry(nullptr, nullptr);
}
bool ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self,
const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader,
mirror::Class** result) {
// Termination case: boot class-loader.
if (IsBootClassLoader(soa, class_loader.Get())) {
// The boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr);
if (klass != nullptr) {
*result = EnsureResolved(self, descriptor, klass);
} else {
*result = DefineClass(self,
descriptor,
hash,
ScopedNullHandle<mirror::ClassLoader>(),
*pair.first,
*pair.second);
}
if (*result == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
}
} else {
*result = nullptr;
}
return true;
}
// Unsupported class-loader?
if (class_loader->GetClass() !=
soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader)) {
*result = nullptr;
return false;
}
// Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
StackHandleScope<4> hs(self);
Handle<mirror::ClassLoader> h_parent(hs.NewHandle(class_loader->GetParent()));
bool recursive_result = FindClassInPathClassLoader(soa, self, descriptor, hash, h_parent, result);
if (!recursive_result) {
// Something wrong up the chain.
return false;
}
if (*result != nullptr) {
// Found the class up the chain.
return true;
}
// Handle this step.
// Handle as if this is the child PathClassLoader.
// The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
// We need to get the DexPathList and loop through it.
ArtField* const cookie_field = soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie);
ArtField* const dex_file_field =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList__Element_dexFile);
mirror::Object* dex_path_list =
soa.DecodeField(WellKnownClasses::dalvik_system_PathClassLoader_pathList)->
GetObject(class_loader.Get());
if (dex_path_list != nullptr && dex_file_field != nullptr && cookie_field != nullptr) {
// DexPathList has an array dexElements of Elements[] which each contain a dex file.
mirror::Object* dex_elements_obj =
soa.DecodeField(WellKnownClasses::dalvik_system_DexPathList_dexElements)->
GetObject(dex_path_list);
// Loop through each dalvik.system.DexPathList$Element's dalvik.system.DexFile and look
// at the mCookie which is a DexFile vector.
if (dex_elements_obj != nullptr) {
Handle<mirror::ObjectArray<mirror::Object>> dex_elements =
hs.NewHandle(dex_elements_obj->AsObjectArray<mirror::Object>());
for (int32_t i = 0; i < dex_elements->GetLength(); ++i) {
mirror::Object* element = dex_elements->GetWithoutChecks(i);
if (element == nullptr) {
// Should never happen, fall back to java code to throw a NPE.
break;
}
mirror::Object* dex_file = dex_file_field->GetObject(element);
if (dex_file != nullptr) {
mirror::LongArray* long_array = cookie_field->GetObject(dex_file)->AsLongArray();
if (long_array == nullptr) {
// This should never happen so log a warning.
LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
break;
}
int32_t long_array_size = long_array->GetLength();
// First element is the oat file.
for (int32_t j = kDexFileIndexStart; j < long_array_size; ++j) {
const DexFile* cp_dex_file = reinterpret_cast<const DexFile*>(static_cast<uintptr_t>(
long_array->GetWithoutChecks(j)));
const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash);
if (dex_class_def != nullptr) {
mirror::Class* klass = DefineClass(self,
descriptor,
hash,