blob: 3592d2cb639259868667696d8c5e416341b3d16b [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 <deque>
#include <iostream>
#include <memory>
#include <queue>
#include <string>
#include <unistd.h>
#include <utility>
#include <vector>
#include "base/casts.h"
#include "base/logging.h"
#include "base/scoped_flock.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
#include "compiler_callbacks.h"
#include "debugger.h"
#include "dex_file-inl.h"
#include "entrypoints/runtime_asm_entrypoints.h"
#include "gc_root-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/heap_bitmap.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "handle_scope.h"
#include "intern_table.h"
#include "interpreter/interpreter.h"
#include "leb128.h"
#include "oat.h"
#include "oat_file.h"
#include "object_lock.h"
#include "mirror/art_field-inl.h"
#include "mirror/art_method-inl.h"
#include "mirror/class.h"
#include "mirror/class-inl.h"
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/iftable-inl.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 "os.h"
#include "runtime.h"
#include "entrypoints/entrypoint_utils.h"
#include "ScopedLocalRef.h"
#include "scoped_thread_state_change.h"
#include "handle_scope-inl.h"
#include "thread-inl.h"
#include "utils.h"
#include "verifier/method_verifier.h"
#include "well_known_classes.h"
namespace art {
static void ThrowNoClassDefFoundError(const char* fmt, ...)
__attribute__((__format__(__printf__, 1, 2)))
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
static void ThrowNoClassDefFoundError(const char* fmt, ...) {
va_list args;
va_start(args, fmt);
Thread* self = Thread::Current();
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
self->ThrowNewExceptionV(throw_location, "Ljava/lang/NoClassDefFoundError;", fmt, args);
va_end(args);
}
static void ThrowEarlierClassFailure(mirror::Class* c)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// 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* runtime = Runtime::Current();
bool is_compiler = runtime->IsCompiler();
if (!is_compiler) { // Give info if this occurs at runtime.
LOG(INFO) << "Rejecting re-init on previously-failed class " << PrettyClass(c);
}
CHECK(c->IsErroneous()) << PrettyClass(c) << " " << c->GetStatus();
Thread* self = Thread::Current();
if (is_compiler) {
// At compile time, accurate errors and NCDFE are disabled to speed compilation.
mirror::Throwable* pre_allocated = runtime->GetPreAllocatedNoClassDefFoundError();
self->SetException(ThrowLocation(), pre_allocated);
} else {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
if (c->GetVerifyErrorClass() != NULL) {
// TODO: change the verifier to store an _instance_, with a useful detail message?
std::string temp;
self->ThrowNewException(throw_location, c->GetVerifyErrorClass()->GetDescriptor(&temp),
PrettyDescriptor(c).c_str());
} else {
self->ThrowNewException(throw_location, "Ljava/lang/NoClassDefFoundError;",
PrettyDescriptor(c).c_str());
}
}
}
static void VlogClassInitializationFailure(Handle<mirror::Class> klass)
SHARED_LOCKS_REQUIRED(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(nullptr)->Dump();
}
}
static void WrapExceptionInInitializer(Handle<mirror::Class> klass)
SHARED_LOCKS_REQUIRED(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) {
ThrowLocation throw_location = self->GetCurrentLocationForThrow();
self->ThrowNewWrappedException(throw_location, "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.
return lhs.size > rhs.size;
}
};
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<mirror::ArtField*>* grouped_and_sorted_fields,
FieldGaps* gaps)
SHARED_LOCKS_REQUIRED(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()) {
mirror::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(IsAligned<n>(gap.start_offset));
field->SetOffset(MemberOffset(gap.start_offset));
if (gap.size > n) {
AddFieldGap(gap.start_offset + n, gap.start_offset + gap.size, gaps);
}
} else {
DCHECK(IsAligned<n>(field_offset->Uint32Value()));
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_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_dex_caches_roots_(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*)) {
memset(find_array_class_cache_, 0, kFindArrayCacheSize * sizeof(mirror::Class*));
}
void ClassLinker::InitWithoutImage(std::vector<std::unique_ptr<const DexFile>> boot_class_path) {
VLOG(startup) << "ClassLinker::Init";
CHECK(!Runtime::Current()->GetHeap()->HasImageSpace()) << "Runtime has image. We should use it.";
CHECK(!init_done_);
// java_lang_Class comes first, it's needed for AllocClass
Thread* self = Thread::Current();
gc::Heap* heap = Runtime::Current()->GetHeap();
// 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.
Handle<mirror::Class> java_lang_Class(hs.NewHandle(down_cast<mirror::Class*>(
heap->AllocNonMovableObject<true>(self, nullptr,
mirror::Class::ClassClassSize(),
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(mirror::Class::ClassClassSize());
java_lang_Class->SetPrimitiveType(Primitive::kPrimNot);
heap->DecrementDisableMovingGC(self);
// AllocClass(mirror::Class*) can now be used
// Class[] is used for reflection support.
Handle<mirror::Class> class_array_class(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::ObjectArray<mirror::Class>::ClassSize())));
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())));
CHECK(java_lang_Object.Get() != nullptr);
// backfill Object as the super class of Class.
java_lang_Class->SetSuperClass(java_lang_Object.Get());
java_lang_Object->SetStatus(mirror::Class::kStatusLoaded, self);
// 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())));
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())));
// 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())));
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())));
mirror::String::SetClass(java_lang_String.Get());
java_lang_String->SetObjectSize(mirror::String::InstanceSize());
java_lang_String->SetStatus(mirror::Class::kStatusResolved, self);
// Setup Reference.
Handle<mirror::Class> java_lang_ref_Reference(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::Reference::ClassSize())));
mirror::Reference::SetClass(java_lang_ref_Reference.Get());
java_lang_ref_Reference->SetObjectSize(mirror::Reference::InstanceSize());
java_lang_ref_Reference->SetStatus(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())));
int_array_class->SetComponentType(GetClassRoot(kPrimitiveInt));
mirror::IntArray::SetArrayClass(int_array_class.Get());
SetClassRoot(kIntArrayClass, int_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())));
SetClassRoot(kJavaLangDexCache, java_lang_DexCache.Get());
java_lang_DexCache->SetObjectSize(mirror::DexCache::InstanceSize());
java_lang_DexCache->SetStatus(mirror::Class::kStatusResolved, self);
// Constructor, Field, Method, and AbstractMethod are necessary so
// that FindClass can link members.
Handle<mirror::Class> java_lang_reflect_ArtField(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::ArtField::ClassSize())));
CHECK(java_lang_reflect_ArtField.Get() != nullptr);
java_lang_reflect_ArtField->SetObjectSize(mirror::ArtField::InstanceSize());
SetClassRoot(kJavaLangReflectArtField, java_lang_reflect_ArtField.Get());
java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusResolved, self);
mirror::ArtField::SetClass(java_lang_reflect_ArtField.Get());
Handle<mirror::Class> java_lang_reflect_ArtMethod(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(), mirror::ArtMethod::ClassSize())));
CHECK(java_lang_reflect_ArtMethod.Get() != nullptr);
size_t pointer_size = GetInstructionSetPointerSize(Runtime::Current()->GetInstructionSet());
java_lang_reflect_ArtMethod->SetObjectSize(mirror::ArtMethod::InstanceSize(pointer_size));
SetClassRoot(kJavaLangReflectArtMethod, java_lang_reflect_ArtMethod.Get());
java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusResolved, self);
mirror::ArtMethod::SetClass(java_lang_reflect_ArtMethod.Get());
// 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())));
object_array_string->SetComponentType(java_lang_String.Get());
SetClassRoot(kJavaLangStringArrayClass, object_array_string.Get());
Handle<mirror::Class> object_array_art_method(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(),
mirror::ObjectArray<mirror::ArtMethod>::ClassSize())));
object_array_art_method->SetComponentType(java_lang_reflect_ArtMethod.Get());
SetClassRoot(kJavaLangReflectArtMethodArrayClass, object_array_art_method.Get());
Handle<mirror::Class> object_array_art_field(hs.NewHandle(
AllocClass(self, java_lang_Class.Get(),
mirror::ObjectArray<mirror::ArtField>::ClassSize())));
object_array_art_field->SetComponentType(java_lang_reflect_ArtField.Get());
SetClassRoot(kJavaLangReflectArtFieldArrayClass, object_array_art_field.Get());
// 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.
CHECK_NE(0U, boot_class_path.size());
for (auto& dex_file : boot_class_path) {
CHECK(dex_file.get() != nullptr);
AppendToBootClassPath(self, *dex_file);
opened_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
// Create runtime resolution and imt conflict methods. Also setup the default imt.
Runtime* runtime = Runtime::Current();
runtime->SetResolutionMethod(runtime->CreateResolutionMethod());
runtime->SetImtConflictMethod(runtime->CreateImtConflictMethod());
runtime->SetImtUnimplementedMethod(runtime->CreateImtConflictMethod());
runtime->SetDefaultImt(runtime->CreateDefaultImt(this));
// 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->IsCompiler()) {
// 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
java_lang_Object->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Object_class = FindSystemClass(self, "Ljava/lang/Object;");
CHECK_EQ(java_lang_Object.Get(), Object_class);
CHECK_EQ(java_lang_Object->GetObjectSize(), mirror::Object::InstanceSize());
java_lang_String->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* String_class = FindSystemClass(self, "Ljava/lang/String;");
std::ostringstream os1, os2;
java_lang_String->DumpClass(os1, mirror::Class::kDumpClassFullDetail);
String_class->DumpClass(os2, mirror::Class::kDumpClassFullDetail);
CHECK_EQ(java_lang_String.Get(), String_class) << os1.str() << "\n\n" << os2.str();
CHECK_EQ(java_lang_String->GetObjectSize(), mirror::String::InstanceSize());
java_lang_DexCache->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* DexCache_class = FindSystemClass(self, "Ljava/lang/DexCache;");
CHECK_EQ(java_lang_String.Get(), String_class);
CHECK_EQ(java_lang_DexCache.Get(), DexCache_class);
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));
mirror::Class* found_char_array_class = FindSystemClass(self, "[C");
CHECK_EQ(char_array_class.Get(), found_char_array_class);
SetClassRoot(kShortArrayClass, FindSystemClass(self, "[S"));
mirror::ShortArray::SetArrayClass(GetClassRoot(kShortArrayClass));
mirror::Class* found_int_array_class = FindSystemClass(self, "[I");
CHECK_EQ(int_array_class.Get(), found_int_array_class);
SetClassRoot(kLongArrayClass, FindSystemClass(self, "[J"));
mirror::LongArray::SetArrayClass(GetClassRoot(kLongArrayClass));
SetClassRoot(kFloatArrayClass, FindSystemClass(self, "[F"));
mirror::FloatArray::SetArrayClass(GetClassRoot(kFloatArrayClass));
SetClassRoot(kDoubleArrayClass, FindSystemClass(self, "[D"));
mirror::DoubleArray::SetArrayClass(GetClassRoot(kDoubleArrayClass));
mirror::Class* found_class_array_class = FindSystemClass(self, "[Ljava/lang/Class;");
CHECK_EQ(class_array_class.Get(), found_class_array_class);
mirror::Class* found_object_array_class = FindSystemClass(self, "[Ljava/lang/Object;");
CHECK_EQ(object_array_class.Get(), found_object_array_class);
// Setup the single, global copy of "iftable".
mirror::Class* java_lang_Cloneable = FindSystemClass(self, "Ljava/lang/Cloneable;");
CHECK(java_lang_Cloneable != nullptr);
mirror::Class* java_io_Serializable = FindSystemClass(self, "Ljava/io/Serializable;");
CHECK(java_io_Serializable != 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.
{
mirror::IfTable* array_iftable = array_iftable_.Read();
array_iftable->SetInterface(0, java_lang_Cloneable);
array_iftable->SetInterface(1, java_io_Serializable);
}
// Sanity check Class[] and Object[]'s interfaces.
CHECK_EQ(java_lang_Cloneable, mirror::Class::GetDirectInterface(self, class_array_class, 0));
CHECK_EQ(java_io_Serializable, mirror::Class::GetDirectInterface(self, class_array_class, 1));
CHECK_EQ(java_lang_Cloneable, mirror::Class::GetDirectInterface(self, object_array_class, 0));
CHECK_EQ(java_io_Serializable, mirror::Class::GetDirectInterface(self, object_array_class, 1));
// Run Class, ArtField, and ArtMethod through FindSystemClass. This initializes their
// dex_cache_ fields and register them in class_table_.
mirror::Class* Class_class = FindSystemClass(self, "Ljava/lang/Class;");
CHECK_EQ(java_lang_Class.Get(), Class_class);
java_lang_reflect_ArtMethod->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Art_method_class = FindSystemClass(self, "Ljava/lang/reflect/ArtMethod;");
CHECK_EQ(java_lang_reflect_ArtMethod.Get(), Art_method_class);
java_lang_reflect_ArtField->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Art_field_class = FindSystemClass(self, "Ljava/lang/reflect/ArtField;");
CHECK_EQ(java_lang_reflect_ArtField.Get(), Art_field_class);
mirror::Class* String_array_class =
FindSystemClass(self, GetClassRootDescriptor(kJavaLangStringArrayClass));
CHECK_EQ(object_array_string.Get(), String_array_class);
mirror::Class* Art_method_array_class =
FindSystemClass(self, GetClassRootDescriptor(kJavaLangReflectArtMethodArrayClass));
CHECK_EQ(object_array_art_method.Get(), Art_method_array_class);
mirror::Class* Art_field_array_class =
FindSystemClass(self, GetClassRootDescriptor(kJavaLangReflectArtFieldArrayClass));
CHECK_EQ(object_array_art_field.Get(), Art_field_array_class);
// End of special init trickery, subsequent classes may be loaded via FindSystemClass.
// Create java.lang.reflect.Proxy root.
mirror::Class* java_lang_reflect_Proxy = FindSystemClass(self, "Ljava/lang/reflect/Proxy;");
SetClassRoot(kJavaLangReflectProxy, java_lang_reflect_Proxy);
// java.lang.ref classes need to be specially flagged, but otherwise are normal classes
// finish initializing Reference class
java_lang_ref_Reference->SetStatus(mirror::Class::kStatusNotReady, self);
mirror::Class* Reference_class = FindSystemClass(self, "Ljava/lang/ref/Reference;");
CHECK_EQ(java_lang_ref_Reference.Get(), Reference_class);
CHECK_EQ(java_lang_ref_Reference->GetObjectSize(), mirror::Reference::InstanceSize());
CHECK_EQ(java_lang_ref_Reference->GetClassSize(), mirror::Reference::ClassSize());
mirror::Class* java_lang_ref_FinalizerReference =
FindSystemClass(self, "Ljava/lang/ref/FinalizerReference;");
java_lang_ref_FinalizerReference->SetAccessFlags(
java_lang_ref_FinalizerReference->GetAccessFlags() |
kAccClassIsReference | kAccClassIsFinalizerReference);
mirror::Class* java_lang_ref_PhantomReference =
FindSystemClass(self, "Ljava/lang/ref/PhantomReference;");
java_lang_ref_PhantomReference->SetAccessFlags(
java_lang_ref_PhantomReference->GetAccessFlags() |
kAccClassIsReference | kAccClassIsPhantomReference);
mirror::Class* java_lang_ref_SoftReference =
FindSystemClass(self, "Ljava/lang/ref/SoftReference;");
java_lang_ref_SoftReference->SetAccessFlags(
java_lang_ref_SoftReference->GetAccessFlags() | kAccClassIsReference);
mirror::Class* java_lang_ref_WeakReference =
FindSystemClass(self, "Ljava/lang/ref/WeakReference;");
java_lang_ref_WeakReference->SetAccessFlags(
java_lang_ref_WeakReference->GetAccessFlags() |
kAccClassIsReference | kAccClassIsWeakReference);
// Setup the ClassLoader, verifying the object_size_.
mirror::Class* java_lang_ClassLoader = FindSystemClass(self, "Ljava/lang/ClassLoader;");
CHECK_EQ(java_lang_ClassLoader->GetObjectSize(), mirror::ClassLoader::InstanceSize());
SetClassRoot(kJavaLangClassLoader, java_lang_ClassLoader);
// 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::StringId* void_string_id = dex_file.FindStringId("V");
CHECK(void_string_id != nullptr);
uint32_t void_string_index = dex_file.GetIndexForStringId(*void_string_id);
const DexFile::TypeId* void_type_id = dex_file.FindTypeId(void_string_index);
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();
}
FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromCompiler exiting";
}
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;");
mirror::ArtField* pendingNext = java_lang_ref_Reference->GetInstanceField(0);
CHECK_STREQ(pendingNext->GetName(), "pendingNext");
CHECK_STREQ(pendingNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;");
mirror::ArtField* queue = java_lang_ref_Reference->GetInstanceField(1);
CHECK_STREQ(queue->GetName(), "queue");
CHECK_STREQ(queue->GetTypeDescriptor(), "Ljava/lang/ref/ReferenceQueue;");
mirror::ArtField* queueNext = java_lang_ref_Reference->GetInstanceField(2);
CHECK_STREQ(queueNext->GetName(), "queueNext");
CHECK_STREQ(queueNext->GetTypeDescriptor(), "Ljava/lang/ref/Reference;");
mirror::ArtField* referent = java_lang_ref_Reference->GetInstanceField(3);
CHECK_STREQ(referent->GetName(), "referent");
CHECK_STREQ(referent->GetTypeDescriptor(), "Ljava/lang/Object;");
mirror::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();
}
}
}
bool ClassLinker::GenerateOatFile(const char* dex_filename,
int oat_fd,
const char* oat_cache_filename,
std::string* error_msg) {
Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
std::string dex2oat(Runtime::Current()->GetCompilerExecutable());
gc::Heap* heap = Runtime::Current()->GetHeap();
std::string boot_image_option("--boot-image=");
if (heap->GetImageSpace() == nullptr) {
// TODO If we get a dex2dex compiler working we could maybe use that, OTOH since we are likely
// out of space anyway it might not matter.
*error_msg = StringPrintf("Cannot create oat file for '%s' because we are running "
"without an image.", dex_filename);
return false;
}
boot_image_option += heap->GetImageSpace()->GetImageLocation();
std::string dex_file_option("--dex-file=");
dex_file_option += dex_filename;
std::string oat_fd_option("--oat-fd=");
StringAppendF(&oat_fd_option, "%d", oat_fd);
std::string oat_location_option("--oat-location=");
oat_location_option += oat_cache_filename;
std::vector<std::string> argv;
argv.push_back(dex2oat);
argv.push_back("--runtime-arg");
argv.push_back("-classpath");
argv.push_back("--runtime-arg");
argv.push_back(Runtime::Current()->GetClassPathString());
Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
if (!Runtime::Current()->IsVerificationEnabled()) {
argv.push_back("--compiler-filter=verify-none");
}
if (Runtime::Current()->MustRelocateIfPossible()) {
argv.push_back("--runtime-arg");
argv.push_back("-Xrelocate");
} else {
argv.push_back("--runtime-arg");
argv.push_back("-Xnorelocate");
}
if (!kIsTargetBuild) {
argv.push_back("--host");
}
argv.push_back(boot_image_option);
argv.push_back(dex_file_option);
argv.push_back(oat_fd_option);
argv.push_back(oat_location_option);
const std::vector<std::string>& compiler_options = Runtime::Current()->GetCompilerOptions();
for (size_t i = 0; i < compiler_options.size(); ++i) {
argv.push_back(compiler_options[i].c_str());
}
if (!Exec(argv, error_msg)) {
// Manually delete the file. Ensures there is no garbage left over if the process unexpectedly
// died. Ignore unlink failure, propagate the original error.
TEMP_FAILURE_RETRY(unlink(oat_cache_filename));
return false;
}
return true;
}
const OatFile* ClassLinker::RegisterOatFile(const OatFile* oat_file) {
WriterMutexLock mu(Thread::Current(), dex_lock_);
if (kIsDebugBuild) {
for (size_t i = 0; i < oat_files_.size(); ++i) {
CHECK_NE(oat_file, oat_files_[i]) << oat_file->GetLocation();
}
}
VLOG(class_linker) << "Registering " << oat_file->GetLocation();
oat_files_.push_back(oat_file);
return oat_file;
}
OatFile& ClassLinker::GetImageOatFile(gc::space::ImageSpace* space) {
VLOG(startup) << "ClassLinker::GetImageOatFile entering";
OatFile* oat_file = space->ReleaseOatFile();
CHECK_EQ(RegisterOatFile(oat_file), oat_file);
VLOG(startup) << "ClassLinker::GetImageOatFile exiting";
return *oat_file;
}
const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFileForDexFile(const DexFile& dex_file) {
const char* dex_location = dex_file.GetLocation().c_str();
uint32_t dex_location_checksum = dex_file.GetLocationChecksum();
return FindOpenedOatDexFile(nullptr, dex_location, &dex_location_checksum);
}
const OatFile::OatDexFile* ClassLinker::FindOpenedOatDexFile(const char* oat_location,
const char* dex_location,
const uint32_t* dex_location_checksum) {
ReaderMutexLock mu(Thread::Current(), dex_lock_);
for (const OatFile* oat_file : oat_files_) {
DCHECK(oat_file != nullptr);
if (oat_location != nullptr) {
if (oat_file->GetLocation() != oat_location) {
continue;
}
}
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
dex_location_checksum,
false);
if (oat_dex_file != nullptr) {
return oat_dex_file;
}
}
return nullptr;
}
// Loads all multi dex files from the given oat file returning true on success.
//
// Parameters:
// oat_file - the oat file to load from
// dex_location - the dex location used to generate the oat file
// dex_location_checksum - the checksum of the dex_location (may be null for pre-opted files)
// generated - whether or not the oat_file existed before or was just (re)generated
// error_msgs - any error messages will be appended here
// dex_files - the loaded dex_files will be appended here (only if the loading succeeds)
static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file,
const char* dex_location,
const uint32_t* dex_location_checksum,
bool generated,
std::vector<std::string>* error_msgs,
std::vector<std::unique_ptr<const DexFile>>* dex_files) {
if (oat_file == nullptr) {
return false;
}
size_t old_size = dex_files->size(); // To rollback on error.
bool success = true;
for (size_t i = 0; success; ++i) {
std::string next_name_str = DexFile::GetMultiDexClassesDexName(i, dex_location);
const char* next_name = next_name_str.c_str();
uint32_t next_location_checksum;
uint32_t* next_location_checksum_pointer = &next_location_checksum;
std::string error_msg;
if ((i == 0) && (strcmp(next_name, dex_location) == 0)) {
// When i=0 the multidex name should be the same as the location name. We already have the
// checksum it so we don't need to recompute it.
if (dex_location_checksum == nullptr) {
next_location_checksum_pointer = nullptr;
} else {
next_location_checksum = *dex_location_checksum;
}
} else if (!DexFile::GetChecksum(next_name, next_location_checksum_pointer, &error_msg)) {
DCHECK_EQ(false, i == 0 && generated);
next_location_checksum_pointer = nullptr;
}
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(next_name, nullptr, false);
if (oat_dex_file == nullptr) {
if (i == 0 && generated) {
error_msg = StringPrintf("\nFailed to find dex file '%s' (checksum 0x%x) in generated out "
" file'%s'", dex_location, next_location_checksum,
oat_file->GetLocation().c_str());
error_msgs->push_back(error_msg);
}
break; // Not found, done.
}
// Checksum test. Test must succeed when generated.
success = !generated;
if (next_location_checksum_pointer != nullptr) {
success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum();
}
if (success) {
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file.get() == nullptr) {
success = false;
error_msgs->push_back(error_msg);
} else {
dex_files->push_back(std::move(dex_file));
}
}
// When we generated the file, we expect success, or something is terribly wrong.
CHECK_EQ(false, generated && !success)
<< "dex_location=" << next_name << " oat_location=" << oat_file->GetLocation().c_str()
<< std::hex << " dex_location_checksum=" << next_location_checksum
<< " OatDexFile::GetLocationChecksum()=" << oat_dex_file->GetDexFileLocationChecksum();
}
if (dex_files->size() == old_size) {
success = false; // We did not even find classes.dex
}
if (success) {
return true;
} else {
dex_files->erase(dex_files->begin() + old_size, dex_files->end());
return false;
}
}
// Multidex files make it possible that some, but not all, dex files can be broken/outdated. This
// complicates the loading process, as we should not use an iterative loading process, because that
// would register the oat file and dex files that come before the broken one. Instead, check all
// multidex ahead of time.
bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_location,
std::vector<std::string>* error_msgs,
std::vector<std::unique_ptr<const DexFile>>* dex_files) {
// 1) Check whether we have an open oat file.
// This requires a dex checksum, use the "primary" one.
uint32_t dex_location_checksum;
uint32_t* dex_location_checksum_pointer = &dex_location_checksum;
bool have_checksum = true;
std::string checksum_error_msg;
if (!DexFile::GetChecksum(dex_location, dex_location_checksum_pointer, &checksum_error_msg)) {
// This happens for pre-opted files since the corresponding dex files are no longer on disk.
dex_location_checksum_pointer = nullptr;
have_checksum = false;
}
bool needs_registering = false;
const OatFile::OatDexFile* oat_dex_file = FindOpenedOatDexFile(oat_location, dex_location,
dex_location_checksum_pointer);
std::unique_ptr<const OatFile> open_oat_file(
oat_dex_file != nullptr ? oat_dex_file->GetOatFile() : nullptr);
// 2) If we do not have an open one, maybe there's one on disk already.
// In case the oat file is not open, we play a locking game here so
// that if two different processes race to load and register or generate
// (or worse, one tries to open a partial generated file) we will be okay.
// This is actually common with apps that use DexClassLoader to work
// around the dex method reference limit and that have a background
// service running in a separate process.
ScopedFlock scoped_flock;
if (open_oat_file.get() == nullptr) {
if (oat_location != nullptr) {
// Can only do this if we have a checksum, else error.
if (!have_checksum) {
error_msgs->push_back(checksum_error_msg);
return false;
}
std::string error_msg;
// We are loading or creating one in the future. Time to set up the file lock.
if (!scoped_flock.Init(oat_location, &error_msg)) {
error_msgs->push_back(error_msg);
return false;
}
// TODO Caller specifically asks for this oat_location. We should honor it. Probably?
open_oat_file.reset(FindOatFileInOatLocationForDexFile(dex_location, dex_location_checksum,
oat_location, &error_msg));
if (open_oat_file.get() == nullptr) {
std::string compound_msg = StringPrintf("Failed to find dex file '%s' in oat location '%s': %s",
dex_location, oat_location, error_msg.c_str());
VLOG(class_linker) << compound_msg;
error_msgs->push_back(compound_msg);
}
} else {
// TODO: What to lock here?
bool obsolete_file_cleanup_failed;
open_oat_file.reset(FindOatFileContainingDexFileFromDexLocation(dex_location,
dex_location_checksum_pointer,
kRuntimeISA, error_msgs,
&obsolete_file_cleanup_failed));
// There's no point in going forward and eventually try to regenerate the
// file if we couldn't remove the obsolete one. Mostly likely we will fail
// with the same error when trying to write the new file.
// TODO: should we maybe do this only when we get permission issues? (i.e. EACCESS).
if (obsolete_file_cleanup_failed) {
return false;
}
}
needs_registering = true;
}
// 3) If we have an oat file, check all contained multidex files for our dex_location.
// Note: LoadMultiDexFilesFromOatFile will check for nullptr in the first argument.
bool success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
dex_location_checksum_pointer,
false, error_msgs, dex_files);
if (success) {
const OatFile* oat_file = open_oat_file.release(); // Avoid deleting it.
if (needs_registering) {
// We opened the oat file, so we must register it.
RegisterOatFile(oat_file);
}
// If the file isn't executable we failed patchoat but did manage to get the dex files.
return oat_file->IsExecutable();
} else {
if (needs_registering) {
// We opened it, delete it.
open_oat_file.reset();
} else {
open_oat_file.release(); // Do not delete open oat files.
}
}
// 4) If it's not the case (either no oat file or mismatches), regenerate and load.
// Need a checksum, fail else.
if (!have_checksum) {
error_msgs->push_back(checksum_error_msg);
return false;
}
// Look in cache location if no oat_location is given.
std::string cache_location;
if (oat_location == nullptr) {
// Use the dalvik cache.
const std::string dalvik_cache(GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA)));
cache_location = GetDalvikCacheFilenameOrDie(dex_location, dalvik_cache.c_str());
oat_location = cache_location.c_str();
}
bool has_flock = true;
// Definitely need to lock now.
if (!scoped_flock.HasFile()) {
std::string error_msg;
if (!scoped_flock.Init(oat_location, &error_msg)) {
error_msgs->push_back(error_msg);
has_flock = false;
}
}
if (Runtime::Current()->IsDex2OatEnabled() && has_flock && scoped_flock.HasFile()) {
// Create the oat file.
open_oat_file.reset(CreateOatFileForDexLocation(dex_location, scoped_flock.GetFile()->Fd(),
oat_location, error_msgs));
}
// Failed, bail.
if (open_oat_file.get() == nullptr) {
std::string error_msg;
// dex2oat was disabled or crashed. Add the dex file in the list of dex_files to make progress.
DexFile::Open(dex_location, dex_location, &error_msg, dex_files);
error_msgs->push_back(error_msg);
return false;
}
// Try to load again, but stronger checks.
success = LoadMultiDexFilesFromOatFile(open_oat_file.get(), dex_location,
dex_location_checksum_pointer,
true, error_msgs, dex_files);
if (success) {
RegisterOatFile(open_oat_file.release());
return true;
} else {
return false;
}
}
const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_location,
uint32_t dex_location_checksum,
const char* oat_location,
std::string* error_msg) {
std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
!Runtime::Current()->IsCompiler(),
error_msg));
if (oat_file.get() == nullptr) {
*error_msg = StringPrintf("Failed to find existing oat file at %s: %s", oat_location,
error_msg->c_str());
return nullptr;
}
Runtime* runtime = Runtime::Current();
const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
if (image_space != nullptr) {
const ImageHeader& image_header = image_space->GetImageHeader();
uint32_t expected_image_oat_checksum = image_header.GetOatChecksum();
uint32_t actual_image_oat_checksum = oat_file->GetOatHeader().GetImageFileLocationOatChecksum();
if (expected_image_oat_checksum != actual_image_oat_checksum) {
*error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat checksum of "
"0x%x, found 0x%x", oat_location, expected_image_oat_checksum,
actual_image_oat_checksum);
return nullptr;
}
uintptr_t expected_image_oat_offset = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
uint32_t actual_image_oat_offset = oat_file->GetOatHeader().GetImageFileLocationOatDataBegin();
if (expected_image_oat_offset != actual_image_oat_offset) {
*error_msg = StringPrintf("Failed to find oat file at '%s' with expected image oat offset %"
PRIuPTR ", found %ud", oat_location, expected_image_oat_offset,
actual_image_oat_offset);
return nullptr;
}
int32_t expected_patch_delta = image_header.GetPatchDelta();
int32_t actual_patch_delta = oat_file->GetOatHeader().GetImagePatchDelta();
if (expected_patch_delta != actual_patch_delta) {
*error_msg = StringPrintf("Failed to find oat file at '%s' with expected patch delta %d, "
" found %d", oat_location, expected_patch_delta, actual_patch_delta);
return nullptr;
}
}
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
&dex_location_checksum);
if (oat_dex_file == nullptr) {
*error_msg = StringPrintf("Failed to find oat file at '%s' containing '%s'", oat_location,
dex_location);
return nullptr;
}
uint32_t expected_dex_checksum = dex_location_checksum;
uint32_t actual_dex_checksum = oat_dex_file->GetDexFileLocationChecksum();
if (expected_dex_checksum != actual_dex_checksum) {
*error_msg = StringPrintf("Failed to find oat file at '%s' with expected dex checksum of 0x%x, "
"found 0x%x", oat_location, expected_dex_checksum,
actual_dex_checksum);
return nullptr;
}
std::unique_ptr<const DexFile> dex_file(oat_dex_file->OpenDexFile(error_msg));
if (dex_file.get() != nullptr) {
return oat_file.release();
} else {
return nullptr;
}
}
const OatFile* ClassLinker::CreateOatFileForDexLocation(const char* dex_location,
int fd, const char* oat_location,
std::vector<std::string>* error_msgs) {
// Generate the output oat file for the dex file
VLOG(class_linker) << "Generating oat file " << oat_location << " for " << dex_location;
std::string error_msg;
if (!GenerateOatFile(dex_location, fd, oat_location, &error_msg)) {
CHECK(!error_msg.empty());
error_msgs->push_back(error_msg);
return nullptr;
}
std::unique_ptr<OatFile> oat_file(OatFile::Open(oat_location, oat_location, nullptr, nullptr,
!Runtime::Current()->IsCompiler(),
&error_msg));
if (oat_file.get() == nullptr) {
std::string compound_msg = StringPrintf("\nFailed to open generated oat file '%s': %s",
oat_location, error_msg.c_str());
error_msgs->push_back(compound_msg);
return nullptr;
}
return oat_file.release();
}
bool ClassLinker::VerifyOatImageChecksum(const OatFile* oat_file,
const InstructionSet instruction_set) {
Runtime* runtime = Runtime::Current();
const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
if (image_space == nullptr) {
return false;
}
uint32_t image_oat_checksum = 0;
if (instruction_set == kRuntimeISA) {
const ImageHeader& image_header = image_space->GetImageHeader();
image_oat_checksum = image_header.GetOatChecksum();
} else {
std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
image_space->GetImageLocation().c_str(), instruction_set));
image_oat_checksum = image_header->GetOatChecksum();
}
return oat_file->GetOatHeader().GetImageFileLocationOatChecksum() == image_oat_checksum;
}
bool ClassLinker::VerifyOatChecksums(const OatFile* oat_file,
const InstructionSet instruction_set,
std::string* error_msg) {
Runtime* runtime = Runtime::Current();
const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
if (image_space == nullptr) {
*error_msg = "No image space for verification against";
return false;
}
// If the requested instruction set is the same as the current runtime,
// we can use the checksums directly. If it isn't, we'll have to read the
// image header from the image for the right instruction set.
uint32_t image_oat_checksum = 0;
uintptr_t image_oat_data_begin = 0;
int32_t image_patch_delta = 0;
if (instruction_set == runtime->GetInstructionSet()) {
const ImageHeader& image_header = image_space->GetImageHeader();
image_oat_checksum = image_header.GetOatChecksum();
image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header.GetOatDataBegin());
image_patch_delta = image_header.GetPatchDelta();
} else {
std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
image_space->GetImageLocation().c_str(), instruction_set));
image_oat_checksum = image_header->GetOatChecksum();
image_oat_data_begin = reinterpret_cast<uintptr_t>(image_header->GetOatDataBegin());
image_patch_delta = image_header->GetPatchDelta();
}
const OatHeader& oat_header = oat_file->GetOatHeader();
bool ret = (oat_header.GetImageFileLocationOatChecksum() == image_oat_checksum);
// If the oat file is PIC, it doesn't care if/how image was relocated. Ignore these checks.
if (!oat_file->IsPic()) {
ret = ret && (oat_header.GetImagePatchDelta() == image_patch_delta)
&& (oat_header.GetImageFileLocationOatDataBegin() == image_oat_data_begin);
}
if (!ret) {
*error_msg = StringPrintf("oat file '%s' mismatch (0x%x, %d, %d) with (0x%x, %" PRIdPTR ", %d)",
oat_file->GetLocation().c_str(),
oat_file->GetOatHeader().GetImageFileLocationOatChecksum(),
oat_file->GetOatHeader().GetImageFileLocationOatDataBegin(),
oat_file->GetOatHeader().GetImagePatchDelta(),
image_oat_checksum, image_oat_data_begin, image_patch_delta);
}
return ret;
}
bool ClassLinker::VerifyOatAndDexFileChecksums(const OatFile* oat_file,
const char* dex_location,
uint32_t dex_location_checksum,
const InstructionSet instruction_set,
std::string* error_msg) {
if (!VerifyOatChecksums(oat_file, instruction_set, error_msg)) {
return false;
}
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location,
&dex_location_checksum);
if (oat_dex_file == nullptr) {
*error_msg = StringPrintf("oat file '%s' does not contain contents for '%s' with checksum 0x%x",
oat_file->GetLocation().c_str(), dex_location, dex_location_checksum);
for (const OatFile::OatDexFile* oat_dex_file_in : oat_file->GetOatDexFiles()) {
*error_msg += StringPrintf("\noat file '%s' contains contents for '%s' with checksum 0x%x",
oat_file->GetLocation().c_str(),
oat_dex_file_in->GetDexFileLocation().c_str(),
oat_dex_file_in->GetDexFileLocationChecksum());
}
return false;
}
DCHECK_EQ(dex_location_checksum, oat_dex_file->GetDexFileLocationChecksum());
return true;
}
bool ClassLinker::VerifyOatWithDexFile(const OatFile* oat_file,
const char* dex_location,
const uint32_t* dex_location_checksum,
std::string* error_msg) {
CHECK(oat_file != nullptr);
CHECK(dex_location != nullptr);
std::unique_ptr<const DexFile> dex_file;
if (dex_location_checksum == nullptr) {
// If no classes.dex found in dex_location, it has been stripped or is corrupt, assume oat is
// up-to-date. This is the common case in user builds for jar's and apk's in the /system
// directory.
const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, nullptr);
if (oat_dex_file == nullptr) {
*error_msg = StringPrintf("Dex checksum mismatch for location '%s' and failed to find oat "
"dex file '%s': %s", oat_file->GetLocation().c_str(), dex_location,
error_msg->c_str());
return false;
}
dex_file = oat_dex_file->OpenDexFile(error_msg);
} else {
bool verified = VerifyOatAndDexFileChecksums(oat_file, dex_location, *dex_location_checksum,
kRuntimeISA, error_msg);
if (!verified) {
return false;
}
dex_file = oat_file->GetOatDexFile(dex_location,
dex_location_checksum)->OpenDexFile(error_msg);
}
return dex_file.get() != nullptr;
}
const OatFile* ClassLinker::FindOatFileContainingDexFileFromDexLocation(
const char* dex_location,
const uint32_t* dex_location_checksum,
InstructionSet isa,
std::vector<std::string>* error_msgs,
bool* obsolete_file_cleanup_failed) {
*obsolete_file_cleanup_failed = false;
bool already_opened = false;
std::string dex_location_str(dex_location);
std::unique_ptr<const OatFile> oat_file(OpenOatFileFromDexLocation(dex_location_str, isa,
&already_opened,
obsolete_file_cleanup_failed,
error_msgs));
std::string error_msg;
if (oat_file.get() == nullptr) {
error_msgs->push_back(StringPrintf("Failed to open oat file from dex location '%s'",
dex_location));
return nullptr;
} else if (oat_file->IsExecutable() &&
!VerifyOatWithDexFile(oat_file.get(), dex_location,
dex_location_checksum, &error_msg)) {
error_msgs->push_back(StringPrintf("Failed to verify oat file '%s' found for dex location "
"'%s': %s", oat_file->GetLocation().c_str(), dex_location,
error_msg.c_str()));
return nullptr;
} else if (!oat_file->IsExecutable() &&
Runtime::Current()->GetHeap()->HasImageSpace() &&
!VerifyOatImageChecksum(oat_file.get(), isa)) {
error_msgs->push_back(StringPrintf("Failed to verify non-executable oat file '%s' found for "
"dex location '%s'. Image checksum incorrect.",
oat_file->GetLocation().c_str(), dex_location));
return nullptr;
} else {
return oat_file.release();
}
}
const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& oat_location) {
ReaderMutexLock mu(Thread::Current(), dex_lock_);
for (size_t i = 0; i < oat_files_.size(); i++) {
const OatFile* oat_file = oat_files_[i];
DCHECK(oat_file != nullptr);
if (oat_file->GetLocation() == oat_location) {
return oat_file;
}
}
return nullptr;
}
const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_location,
InstructionSet isa,
bool *already_opened,
bool *obsolete_file_cleanup_failed,
std::vector<std::string>* error_msgs) {
// Find out if we've already opened the file
const OatFile* ret = nullptr;
std::string odex_filename(DexFilenameToOdexFilename(dex_location, isa));
ret = FindOpenedOatFileFromOatLocation(odex_filename);
if (ret != nullptr) {
*already_opened = true;
return ret;
}
std::string dalvik_cache;
bool have_android_data = false;
bool have_dalvik_cache = false;
bool is_global_cache = false;
GetDalvikCache(GetInstructionSetString(kRuntimeISA), false, &dalvik_cache,
&have_android_data, &have_dalvik_cache, &is_global_cache);
std::string cache_filename;
if (have_dalvik_cache) {
cache_filename = GetDalvikCacheFilenameOrDie(dex_location.c_str(), dalvik_cache.c_str());
ret = FindOpenedOatFileFromOatLocation(cache_filename);
if (ret != nullptr) {
*already_opened = true;
return ret;
}
} else {
// If we need to relocate we should just place odex back where it started.
cache_filename = odex_filename;
}
ret = nullptr;
// We know that neither the odex nor the cache'd version is already in use, if it even exists.
//
// Now we do the following:
// 1) Try and open the odex version
// 2) If present, checksum-verified & relocated correctly return it
// 3) Close the odex version to free up its address space.
// 4) Try and open the cache version
// 5) If present, checksum-verified & relocated correctly return it
// 6) Close the cache version to free up its address space.
// 7) If we should relocate:
// a) If we have opened and checksum-verified the odex version relocate it to
// 'cache_filename' and return it
// b) If we have opened and checksum-verified the cache version relocate it in place and return
// it. This should not happen often (I think only the run-test's will hit this case).
// 8) If the cache-version was present we should delete it since it must be obsolete if we get to
// this point.
// 9) Return nullptr
*already_opened = false;
const Runtime* runtime = Runtime::Current();
CHECK(runtime != nullptr);
bool executable = !runtime->IsCompiler();
std::string odex_error_msg;
bool should_patch_system = false;
bool odex_checksum_verified = false;
bool have_system_odex = false;
{
// There is a high probability that both these oat files map similar/the same address
// spaces so we must scope them like this so they each gets its turn.
std::unique_ptr<OatFile> odex_oat_file(OatFile::Open(odex_filename, odex_filename, nullptr,
nullptr,
executable, &odex_error_msg));
if (odex_oat_file.get() != nullptr && CheckOatFile(runtime, odex_oat_file.get(), isa,
&odex_checksum_verified,
&odex_error_msg)) {
return odex_oat_file.release();
} else {
if (odex_checksum_verified) {
// We can just relocate
should_patch_system = true;
odex_error_msg = "Image Patches are incorrect";
}
if (odex_oat_file.get() != nullptr) {
have_system_odex = true;
}
}
}
std::string cache_error_msg;
bool should_patch_cache = false;
bool cache_checksum_verified = false;
if (have_dalvik_cache) {
std::unique_ptr<OatFile> cache_oat_file(OatFile::Open(cache_filename, cache_filename, nullptr,
nullptr,
executable, &cache_error_msg));
if (cache_oat_file.get() != nullptr && CheckOatFile(runtime, cache_oat_file.get(), isa,
&cache_checksum_verified,
&cache_error_msg)) {
return cache_oat_file.release();
} else if (cache_checksum_verified) {
// We can just relocate
should_patch_cache = true;
cache_error_msg = "Image Patches are incorrect";
}
} else if (have_android_data) {
// dalvik_cache does not exist but android data does. This means we should be able to create
// it, so we should try.
GetDalvikCacheOrDie(GetInstructionSetString(kRuntimeISA), true);
}
ret = nullptr;
std::string error_msg;
if (runtime->CanRelocate()) {
// Run relocation
gc::space::ImageSpace* space = Runtime::Current()->GetHeap()->GetImageSpace();
if (space != nullptr) {
const std::string& image_location = space->GetImageLocation();
if (odex_checksum_verified && should_patch_system) {
ret = PatchAndRetrieveOat(odex_filename, cache_filename, image_location, isa, &error_msg);
} else if (cache_checksum_verified && should_patch_cache) {
CHECK(have_dalvik_cache);
ret = PatchAndRetrieveOat(cache_filename, cache_filename, image_location, isa, &error_msg);
}
} else if (have_system_odex) {
ret = GetInterpretedOnlyOat(odex_filename, isa, &error_msg);
}
}
if (ret == nullptr && have_dalvik_cache && OS::FileExists(cache_filename.c_str())) {
// implicitly: were able to fine where the cached version is but we were unable to use it,
// either as a destination for relocation or to open a file. We should delete it if it is
// there.
if (TEMP_FAILURE_RETRY(unlink(cache_filename.c_str())) != 0) {
std::string rm_error_msg = StringPrintf("Failed to remove obsolete file from %s when "
"searching for dex file %s: %s",
cache_filename.c_str(), dex_location.c_str(),
strerror(errno));
error_msgs->push_back(rm_error_msg);
VLOG(class_linker) << rm_error_msg;
// Let the caller know that we couldn't remove the obsolete file.
// This is a good indication that further writes may fail as well.
*obsolete_file_cleanup_failed = true;
}
}
if (ret == nullptr) {
VLOG(class_linker) << error_msg;
error_msgs->push_back(error_msg);
std::string relocation_msg;
if (runtime->CanRelocate()) {
relocation_msg = StringPrintf(" and relocation failed");
}
if (have_dalvik_cache && cache_checksum_verified) {
error_msg = StringPrintf("Failed to open oat file from %s (error %s) or %s "
"(error %s)%s.", odex_filename.c_str(), odex_error_msg.c_str(),
cache_filename.c_str(), cache_error_msg.c_str(),
relocation_msg.c_str());
} else {
error_msg = StringPrintf("Failed to open oat file from %s (error %s) (no "
"dalvik_cache availible)%s.", odex_filename.c_str(),
odex_error_msg.c_str(), relocation_msg.c_str());
}
VLOG(class_linker) << error_msg;
error_msgs->push_back(error_msg);
}
return ret;
}
const OatFile* ClassLinker::GetInterpretedOnlyOat(const std::string& oat_path,
InstructionSet isa,
std::string* error_msg) {
// We open it non-executable
std::unique_ptr<OatFile> output(OatFile::Open(oat_path, oat_path, nullptr, nullptr, false, error_msg));
if (output.get() == nullptr) {
return nullptr;
}
if (!Runtime::Current()->GetHeap()->HasImageSpace() ||
VerifyOatImageChecksum(output.get(), isa)) {
return output.release();
} else {
*error_msg = StringPrintf("Could not use oat file '%s', image checksum failed to verify.",
oat_path.c_str());
return nullptr;
}
}
const OatFile* ClassLinker::PatchAndRetrieveOat(const std::string& input_oat,
const std::string& output_oat,
const std::string& image_location,
InstructionSet isa,
std::string* error_msg) {
Runtime* runtime = Runtime::Current();
DCHECK(runtime != nullptr);
if (!runtime->GetHeap()->HasImageSpace()) {
// We don't have an image space so there is no point in trying to patchoat.
LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted because we are "
<< "running without an image. Attempting to use oat file for interpretation.";
return GetInterpretedOnlyOat(input_oat, isa, error_msg);
}
if (!runtime->IsDex2OatEnabled()) {
// We don't have dex2oat so we can assume we don't have patchoat either. We should just use the
// input_oat but make sure we only do interpretation on it's dex files.
LOG(WARNING) << "Patching of oat file '" << input_oat << "' not attempted due to dex2oat being "
<< "disabled. Attempting to use oat file for interpretation";
return GetInterpretedOnlyOat(input_oat, isa, error_msg);
}
Locks::mutator_lock_->AssertNotHeld(Thread::Current()); // Avoid starving GC.
std::string patchoat(runtime->GetPatchoatExecutable());
std::string isa_arg("--instruction-set=");
isa_arg += GetInstructionSetString(isa);
std::string input_oat_filename_arg("--input-oat-file=");
input_oat_filename_arg += input_oat;
std::string output_oat_filename_arg("--output-oat-file=");
output_oat_filename_arg += output_oat;
std::string patched_image_arg("--patched-image-location=");
patched_image_arg += image_location;
std::vector<std::string> argv;
argv.push_back(patchoat);
argv.push_back(isa_arg);
argv.push_back(input_oat_filename_arg);
argv.push_back(output_oat_filename_arg);
argv.push_back(patched_image_arg);
std::string command_line(Join(argv, ' '));
LOG(INFO) << "Relocate Oat File: " << command_line;
bool success = Exec(argv, error_msg);
if (success) {
std::unique_ptr<OatFile> output(OatFile::Open(output_oat, output_oat, nullptr, nullptr,
!runtime->IsCompiler(), error_msg));
bool checksum_verified = false;
if (output.get() != nullptr && CheckOatFile(runtime, output.get(), isa, &checksum_verified,
error_msg)) {
return output.release();
} else if (output.get() != nullptr) {
*error_msg = StringPrintf("Patching of oat file '%s' succeeded "
"but output file '%s' failed verifcation: %s",
input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
} else {
*error_msg = StringPrintf("Patching of oat file '%s' succeeded "
"but was unable to open output file '%s': %s",
input_oat.c_str(), output_oat.c_str(), error_msg->c_str());
}
} else if (!runtime->IsCompiler()) {
// patchoat failed which means we probably don't have enough room to place the output oat file,
// instead of failing we should just run the interpreter from the dex files in the input oat.
LOG(WARNING) << "Patching of oat file '" << input_oat << "' failed. Attempting to use oat file "
<< "for interpretation. patchoat failure was: " << *error_msg;
return GetInterpretedOnlyOat(input_oat, isa, error_msg);
} else {
*error_msg = StringPrintf("Patching of oat file '%s to '%s' "
"failed: %s", input_oat.c_str(), output_oat.c_str(),
error_msg->c_str());
}
return nullptr;
}
bool ClassLinker::CheckOatFile(const Runtime* runtime, const OatFile* oat_file, InstructionSet isa,
bool* checksum_verified,
std::string* error_msg) {
const gc::space::ImageSpace* image_space = runtime->GetHeap()->GetImageSpace();
if (image_space == nullptr) {
*error_msg = "No image space present";
return false;
}
uint32_t real_image_checksum;
void* real_image_oat_offset;
int32_t real_patch_delta;
if (isa == runtime->GetInstructionSet()) {
const ImageHeader& image_header = image_space->GetImageHeader();
real_image_checksum = image_header.GetOatChecksum();
real_image_oat_offset = image_header.GetOatDataBegin();
real_patch_delta = image_header.GetPatchDelta();
} else {
std::unique_ptr<ImageHeader> image_header(gc::space::ImageSpace::ReadImageHeaderOrDie(
image_space->GetImageLocation().c_str(), isa));
real_image_checksum = image_header->GetOatChecksum();
real_image_oat_offset = image_header->GetOatDataBegin();
real_patch_delta = image_header->GetPatchDelta();
}
const OatHeader& oat_header = oat_file->GetOatHeader();
std::string compound_msg;
uint32_t oat_image_checksum = oat_header.GetImageFileLocationOatChecksum();
*checksum_verified = oat_image_checksum == real_image_checksum;
if (!*checksum_verified) {
StringAppendF(&compound_msg, " Oat Image Checksum Incorrect (expected 0x%x, received 0x%x)",
real_image_checksum, oat_image_checksum);
}
bool offset_verified;
bool patch_delta_verified;
if (!oat_file->IsPic()) {
// If an oat file is not PIC, we need to check that the image is at the expected location and
// patched in the same way.
void* oat_image_oat_offset =
reinterpret_cast<void*>(oat_header.GetImageFileLocationOatDataBegin());
offset_verified = oat_image_oat_offset == real_image_oat_offset;
if (!offset_verified) {
StringAppendF(&compound_msg, " Oat Image oat offset incorrect (expected 0x%p, received 0x%p)",
real_image_oat_offset, oat_image_oat_offset);
}
int32_t oat_patch_delta = oat_header.GetImagePatchDelta();
patch_delta_verified = oat_patch_delta == real_patch_delta;
if (!patch_delta_verified) {
StringAppendF(&compound_msg, " Oat image patch delta incorrect (expected 0x%x, "
"received 0x%x)", real_patch_delta, oat_patch_delta);
}
} else {
// If an oat file is PIC, we ignore offset and patching delta.
offset_verified = true;
patch_delta_verified = true;
}
bool ret = (*checksum_verified && offset_verified && patch_delta_verified);
if (!ret) {
*error_msg = "Oat file failed to verify:" + compound_msg;
}
return ret;
}
const OatFile* ClassLinker::FindOatFileFromOatLocation(const std::string& oat_location,
std::string* error_msg) {
const OatFile* oat_file = FindOpenedOatFileFromOatLocation(oat_location);
if (oat_file != nullptr) {
return oat_file;
}
return OatFile::Open(oat_location, oat_location, nullptr, nullptr, !Runtime::Current()->IsCompiler(),
error_msg);
}
void ClassLinker::InitFromImageInterpretOnlyCallback(mirror::Object* obj, void* arg) {
ClassLinker* class_linker = reinterpret_cast<ClassLinker*>(arg);
DCHECK(obj != nullptr);
DCHECK(class_linker != nullptr);
size_t pointer_size = class_linker->image_pointer_size_;
if (obj->IsArtMethod()) {
mirror::ArtMethod* method = obj->AsArtMethod();
if (!method->IsNative()) {
method->SetEntryPointFromInterpreterPtrSize(artInterpreterToInterpreterBridge, pointer_size);
if (method != Runtime::Current()->GetResolutionMethod()) {
method->SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(),
pointer_size);
}
}
}
}
void ClassLinker::InitFromImage() {
VLOG(startup) << "ClassLinker::InitFromImage entering";
CHECK(!init_done_);
Thread* self = Thread::Current();
gc::Heap* heap = Runtime::Current()->GetHeap();
gc::space::ImageSpace* space = heap->GetImageSpace();
dex_cache_image_class_lookup_required_ = true;
CHECK(space != nullptr);
OatFile& oat_file = GetImageOatFile(space);
CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatChecksum(), 0U);
CHECK_EQ(oat_file.GetOatHeader().GetImageFileLocationOatDataBegin(), 0U);
const char* image_file_location = oat_file.GetOatHeader().
GetStoreValueByKey(OatHeader::kImageLocationKey);
CHECK(image_file_location == nullptr || *image_file_location == 0);
quick_resolution_trampoline_ = oat_file.GetOatHeader().GetQuickResolutionTrampoline();
quick_imt_conflict_trampoline_ = oat_file.GetOatHeader().GetQuickImtConflictTrampoline();
quick_generic_jni_trampoline_ = oat_file.GetOatHeader().GetQuickGenericJniTrampoline();
quick_to_interpreter_bridge_trampoline_ = oat_file.GetOatHeader().GetQuickToInterpreterBridge();
mirror::Object* dex_caches_object = space->GetImageHeader().GetImageRoot(ImageHeader::kDexCaches);
mirror::ObjectArray<mirror::DexCache>* dex_caches =
dex_caches_object->AsObjectArray<mirror::DexCache>();
StackHandleScope<1> hs(self);
Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
space->GetImageHeader().GetImageRoot(ImageHeader::kClassRoots)->
AsObjectArray<mirror::Class>()));
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get());
// 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));
CHECK_EQ(oat_file.GetOatHeader().GetDexFileCount(),
static_cast<uint32_t>(dex_caches->GetLength()));
for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
StackHandleScope<1> hs2(self);
Handle<mirror::DexCache> dex_cache(hs2.NewHandle(dex_caches->Get(i)));
const std::string& dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(dex_file_location.c_str(),
nullptr);
CHECK(oat_dex_file != nullptr) << oat_file.GetLocation() << " " << dex_file_location;
std::string error_msg;
std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
if (dex_file.get() == nullptr) {
LOG(FATAL) << "Failed to open dex file " << dex_file_location
<< " from within oat file " << oat_file.GetLocation()
<< " error '" << error_msg << "'";
UNREACHABLE();
}
CHECK_EQ(dex_file->GetLocationChecksum(), oat_dex_file->GetDexFileLocationChecksum());
AppendToBootClassPath(*dex_file.get(), dex_cache);
opened_dex_files_.push_back(std::move(dex_file));
}
// Set classes on AbstractMethod early so that IsMethod tests can be performed during the live
// bitmap walk.
mirror::ArtMethod::SetClass(GetClassRoot(kJavaLangReflectArtMethod));
size_t art_method_object_size = mirror::ArtMethod::GetJavaLangReflectArtMethod()->GetObjectSize();
if (!Runtime::Current()->IsCompiler()) {
// Compiler supports having an image with a different pointer size than the runtime. This
// happens on the host for compile 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.
CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(sizeof(void*)))
<< sizeof(void*);
}
if (art_method_object_size == mirror::ArtMethod::InstanceSize(4)) {
image_pointer_size_ = 4;
} else {
CHECK_EQ(art_method_object_size, mirror::ArtMethod::InstanceSize(8));
image_pointer_size_ = 8;
}
// Set entry point to interpreter if in InterpretOnly mode.
Runtime* runtime = Runtime::Current();
if (!runtime->IsCompiler() && runtime->GetInstrumentation()->InterpretOnly()) {
heap->VisitObjects(InitFromImageInterpretOnlyCallback, this);
}
// reinit class_roots_
mirror::Class::SetClassClass(class_roots->Get(kJavaLangClass));
class_roots_ = GcRoot<mirror::ObjectArray<mirror::Class>>(class_roots.Get());
// 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::Reference::SetClass(GetClassRoot(kJavaLangRefReference));
mirror::ArtField::SetClass(GetClassRoot(kJavaLangReflectArtField));
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));
FinishInit(self);
VLOG(startup) << "ClassLinker::InitFromImage exiting";
}
void ClassLinker::VisitClassRoots(RootCallback* callback, void* arg, VisitRootFlags flags) {
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
for (GcRoot<mirror::Class>& root : class_table_) {
root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
}
for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (auto& root : new_class_roots_) {
mirror::Class* old_ref = root.Read<kWithoutReadBarrier>();
root.VisitRoot(callback, arg, RootInfo(kRootStickyClass));
mirror::Class* new_ref = root.Read<kWithoutReadBarrier>();
if (UNLIKELY(new_ref != old_ref)) {
// Uh ohes, GC moved a root in the log. Need to search the class_table and update the
// corresponding object. This is slow, but luckily for us, this may only happen with a
// concurrent moving GC.
auto it = class_table_.Find(GcRoot<mirror::Class>(old_ref));
DCHECK(it != class_table_.end());
*it = GcRoot<mirror::Class>(new_ref);
}
}
}
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(RootCallback* callback, void* arg, VisitRootFlags flags) {
class_roots_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
Thread* self = Thread::Current();
{
ReaderMutexLock mu(self, dex_lock_);
if ((flags & kVisitRootFlagAllRoots) != 0) {
for (GcRoot<mirror::DexCache>& dex_cache : dex_caches_) {
dex_cache.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
}
} else if ((flags & kVisitRootFlagNewRoots) != 0) {
for (size_t index : new_dex_cache_roots_) {
dex_caches_[index].VisitRoot(callback, arg, RootInfo(kRootVMInternal));
}
}
if ((flags & kVisitRootFlagClearRootLog) != 0) {
new_dex_cache_roots_.clear();
}
if ((flags & kVisitRootFlagStartLoggingNewRoots) != 0) {
log_new_dex_caches_roots_ = true;
} else if ((flags & kVisitRootFlagStopLoggingNewRoots) != 0) {
log_new_dex_caches_roots_ = false;
}
}
VisitClassRoots(callback, arg, flags);
array_iftable_.VisitRoot(callback, arg, RootInfo(kRootVMInternal));
DCHECK(!array_iftable_.IsNull());
for (size_t i = 0; i < kFindArrayCacheSize; ++i) {
find_array_class_cache_[i].VisitRootIfNonNull(callback, arg, RootInfo(kRootVMInternal));
}
}
void ClassLinker::VisitClasses(ClassVisitor* visitor, void* arg) {
if (dex_cache_image_class_lookup_required_) {
MoveImageClassesToClassTable();
}
// TODO: why isn't this a ReaderMutexLock?
WriterMutexLock mu(Thread::Current(), *Locks::classlinker_classes_lock_);
for (GcRoot<mirror::Class>& root : class_table_) {
if (!visitor(root.Read(), arg)) {
return;
}
}
for (GcRoot<mirror::Class>& root : pre_zygote_class_table_) {
if (!visitor(root.Read(), arg)) {
return;
}
}
}
static bool GetClassesVisitorSet(mirror::Class* c, void* arg) {
std::set<mirror::Class*>* classes = reinterpret_cast<std::set<mirror::Class*>*>(arg);
classes->insert(c);
return true;
}
struct GetClassesVisitorArrayArg {
Handle<mirror::ObjectArray<mirror::Class>>* classes;
int32_t index;
bool success;
};
static bool GetClassesVisitorArray(mirror::Class* c, void* varg)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
GetClassesVisitorArrayArg* arg = reinterpret_cast<GetClassesVisitorArrayArg*>(varg);
if (arg->index < (*arg->classes)->GetLength()) {
(*arg->classes)->Set(arg->index, c);
arg->index++;
return true;
} else {
arg->success = false;
return false;
}
}
void ClassLinker::VisitClassesWithoutClassesLock(ClassVisitor* visitor, void* arg) {
// TODO: it may be possible to avoid secondary storage if we iterate over dex caches. The problem
// is avoiding duplicates.
if (!kMovingClasses) {
std::set<mirror::Class*> classes;
VisitClasses(GetClassesVisitorSet, &classes);
for (mirror::Class* klass : classes) {
if (!visitor(klass, arg)) {
return;
}
}
} else {
Thread* self = Thread::Current();
StackHandleScope<1> hs(self);
MutableHandle<mirror::ObjectArray<mirror::Class>> classes =
hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
GetClassesVisitorArrayArg local_arg;
local_arg.classes = &classes;
local_arg.success = false;
// 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 (!local_arg.success) {
size_t class_table_size;
{
ReaderMutexLock mu(self, *Locks::classlinker_classes_lock_);
class_table_size = class_table_.Size() + pre_zygote_class_table_.Size();
}
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.
local_arg.index = 0;
local_arg.success = true;
VisitClasses(GetClassesVisitorArray, &local_arg);
}
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(klass, arg)) {
return;
}
}
}
}
ClassLinker::~ClassLinker() {
mirror::Class::ResetClass();
mirror::String::ResetClass();
mirror::Reference::ResetClass();
mirror::ArtField::ResetClass();
mirror::ArtMethod::ResetClass();
mirror::BooleanArray::ResetArrayClass();
mirror::ByteArray::ResetArrayClass();
mirror::CharArray::ResetArrayClass();
mirror::DoubleArray::ResetArrayClass();
mirror::FloatArray::ResetArrayClass();
mirror::IntArray::ResetArrayClass();
mirror::LongArray::ResetArrayClass();
mirror::ShortArray::ResetArrayClass();
mirror::Throwable::ResetClass();
mirror::StackTraceElement::ResetClass();
STLDeleteElements(&oat_files_);
}
mirror::DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) {
gc::Heap* heap = Runtime::Current()->GetHeap();
StackHandleScope<16> hs(self);
Handle<mirror::Class> dex_cache_class(hs.NewHandle(GetClassRoot(kJavaLangDexCache)));
Handle<mirror::DexCache> dex_cache(
hs.NewHandle(down_cast<mirror::DexCache*>(
heap->AllocObject<true>(self, dex_cache_class.Get(), dex_cache_class->GetObjectSize(),
VoidFunctor()))));
if (dex_cache.Get() == nullptr) {
return nullptr;
}
Handle<mirror::String>
location(hs.NewHandle(intern_table_->InternStrong(dex_file.GetLocation().c_str())));
if (location.Get() == nullptr) {
return nullptr;
}
Handle<mirror::ObjectArray<mirror::String>>
strings(hs.NewHandle(AllocStringArray(self, dex_file.NumStringIds())));
if (strings.Get() == nullptr) {
return nullptr;
}
Handle<mirror::ObjectArray<mirror::Class>>
types(hs.NewHandle(AllocClassArray(self, dex_file.NumTypeIds())));
if (types.Get() == nullptr) {
return nullptr;
}
Handle<mirror::ObjectArray<mirror::ArtMethod>>
methods(hs.NewHandle(AllocArtMethodArray(self, dex_file.NumMethodIds())));
if (methods.Get() == nullptr) {
return nullptr;
}
Handle<mirror::ObjectArray<mirror::ArtField>>
fields(hs.NewHandle(AllocArtFieldArray(self, dex_file.NumFieldIds())));
if (fields.Get() == nullptr) {
return nullptr;
}
dex_cache->Init(&dex_file, location.Get(), strings.Get(), types.Get(), methods.Get(),
fields.Get());
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)) {
CHECK(self->IsExceptionPending()); // OOME.
return nullptr;
}
return k->AsClass();
}
mirror::Class* ClassLinker::AllocClass(Thread* self, uint32_t class_size) {
return AllocClass(self, GetClassRoot(kJavaLangClass), class_size);
}
mirror::ArtField* ClassLinker::AllocArtField(Thread* self) {
return down_cast<mirror::ArtField*>(
GetClassRoot(kJavaLangReflectArtField)->AllocNonMovableObject(self));
}
mirror::ArtMethod* ClassLinker::AllocArtMethod(Thread* self) {
return down_cast<mirror::ArtMethod*>(
GetClassRoot(kJavaLangReflectArtMethod)->AllocNonMovableObject(self));
}
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());
h_class->SetStatus(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);
}
mirror::Class* ClassLinker::FindClassInPathClassLoader(ScopedObjectAccessAlreadyRunnable& soa,
Thread* self, const char* descriptor,
size_t hash,
Handle<mirror::ClassLoader> class_loader) {
// Can we special case for a well understood PathClassLoader with the BootClassLoader as parent?
if (class_loader->GetClass() !=
soa.Decode<mirror::Class*>(WellKnownClasses::dalvik_system_PathClassLoader) ||
class_loader->GetParent()->GetClass() !=
soa.Decode<mirror::Class*>(WellKnownClasses::java_lang_BootClassLoader)) {
return nullptr;
}
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
// Check if this would be found in the parent boot class loader.
if (pair.second != nullptr) {
mirror::Class* klass = LookupClass(self, descriptor, hash, nullptr);
if (klass != nullptr) {
// May return null if resolution on another thread fails.
klass = EnsureResolved(self, descriptor, klass);
} else {
// May OOME.
klass = DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
*pair.second);
}
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
}
return klass;
} else {
// Handle as if this is the child PathClassLoader.
// Handles as RegisterDexFile may allocate dex caches (and cause thread suspension).
StackHandleScope<3> hs(self);
// The class loader is a PathClassLoader which inherits from BaseDexClassLoader.
// We need to get the DexPathList and loop through it.
Handle<mirror::ArtField> cookie_field =
hs.NewHandle(soa.DecodeField(WellKnownClasses::dalvik_system_DexFile_cookie));
Handle<mirror::ArtField> dex_file_field =
hs.NewHandle(
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.Get() != nullptr &&
cookie_field.Get() != 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) {
const uint64_t cookie = cookie_field->GetLong(dex_file);
auto* dex_files =
reinterpret_cast<std::vector<const DexFile*>*>(static_cast<uintptr_t>(cookie));
if (dex_files == nullptr) {
// This should never happen so log a warning.
LOG(WARNING) << "Null DexFile::mCookie for " << descriptor;
break;
}
for (const DexFile* cp_dex_file : *dex_files) {
const DexFile::ClassDef* dex_class_def = cp_dex_file->FindClassDef(descriptor, hash);
if (dex_class_def != nullptr) {
RegisterDexFile(*cp_dex_file);
mirror::Class* klass = DefineClass(self, descriptor, hash, class_loader,
*cp_dex_file, *dex_class_def);
if (klass == nullptr) {
CHECK(self->IsExceptionPending()) << descriptor;
self->ClearException();
return nullptr;
}
return klass;
}
}
}
}
}
}
self->AssertNoPendingException();
return nullptr;
}
}
mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor,
Handle<mirror::ClassLoader> class_loader) {
DCHECK_NE(*descriptor, '\0') << "descriptor is empty string";
DCHECK(self != nullptr);
self->AssertNoPendingException();
if (descriptor[1] == '\0') {
// only the descriptors of primitive types should be 1 character long, also avoid class lookup
// for primitive classes that aren't backed by dex files.
return FindPrimitiveClass(descriptor[0]);
}
const size_t hash = ComputeModifiedUtf8Hash(descriptor);
// Find the class in the loaded classes table.
mirror::Class* klass = LookupClass(self, descriptor, hash, class_loader.Get());
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
// Class is not yet loaded.
if (descriptor[0] == '[') {
return CreateArrayClass(self, descriptor, hash, class_loader);
} else if (class_loader.Get() == nullptr) {
// The boot class loader, search the boot class path.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
*pair.second);
} else {
// The boot class loader is searched ahead of the application class loader, failures are
// expected and will be wrapped in a ClassNotFoundException. Use the pre-allocated error to
// trigger the chaining with a proper stack trace.
mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(ThrowLocation(), pre_allocated);
return nullptr;
}
} else if (Runtime::Current()->UseCompileTimeClassPath()) {
// First try with the bootstrap class loader.
if (class_loader.Get() != nullptr) {
klass = LookupClass(self, descriptor, hash, nullptr);
if (klass != nullptr) {
return EnsureResolved(self, descriptor, klass);
}
}
// If the lookup failed search the boot class path. We don't perform a recursive call to avoid
// a NoClassDefFoundError being allocated.
ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
if (pair.second != nullptr) {
return DefineClass(self, descriptor, hash, NullHandle<mirror::ClassLoader>(), *pair.first,
*pair.second);
}
// Next try the compile time class path.
const std::vector<const DexFile*>* class_path;
{
ScopedObjectAccessUnchecked soa(self);
ScopedLocalRef<jobject> jclass_loader(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.Get()));
class_path = &Runtime::Current()->GetCompileTimeClassPath(jclass_loader.get());
}
pair = FindInClassPath(descriptor, hash, *class_path);
if (pair.second != nullptr) {
return DefineClass(self, descriptor, hash, class_loader, *pair.first, *pair.second);
} else {
// Use the pre-allocated NCDFE at compile time to avoid wasting time constructing exceptions.
mirror::Throwable* pre_allocated = Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(ThrowLocation(), pre_allocated);
return nullptr;
}
} else {
ScopedObjectAccessUnchecked soa(self);
mirror::Class* cp_klass = FindClassInPathClassLoader(soa, self, descriptor, hash,
class_loader);
if (cp_klass != nullptr) {
return cp_klass;
}
ScopedLocalRef<jobject> class_loader_object(soa.Env(),
soa.AddLocalReference<jobject>(class_loader.Get()));
std::string class_name_string(DescriptorToDot(descriptor));
ScopedLocalRef<jobject> result(soa.Env(), nullptr);
{
ScopedThreadStateChange tsc(self, kNative);
ScopedLocalRef<jobject> class_name_object(soa.Env(),
soa.Env()->NewStringUTF(class_name_string.c_str()));
if (class_name_object.get() == nullptr) {
DCHECK(self->IsExceptionPending()); // OOME.
return nullptr;
}
CHECK(class_loader_object.get() != nullptr);
result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
WellKnownClasses::java_lang_ClassLoader_loadClass,
class_name_object.get()));
}
if (self->IsExceptionPending()) {
// If the ClassLoader threw, pass that exception up.
return nullptr;
} else if (result.get() == nullptr) {
// broken loader - throw NPE to be compatible with Dalvik
ThrowNullPointerException(nullptr, StringPrintf("ClassLoader.loadClass returned null for %s",
class_name_string.c_str()).c_str());
return nullptr;
} else {
// success, return mirror::Class*
return soa.Decode<mirror::Class*>(result.get());
}
}
UNREACHABLE();
}
mirror::Class* ClassLinker::DefineClass(Thread* self, const char* descriptor, size_t hash,
Handle<mirror::ClassLoader> class_loader,
const DexFile& dex_file,
const DexFile::ClassDef& dex_class_def) {
StackHandleScope<3> hs(self);
auto klass = hs.NewHandle<mirror::Class>(nullptr);
// Load the class from the dex file.
if (UNLIKELY(!init_done_)) {
// finish up init of hand crafted class_roots_
if (strcmp(descriptor, "Ljava/lang/Object;") == 0) {
klass.Assign(GetClassRoot(kJavaLangObject));
} else if (strcmp(descriptor, "Ljava/lang/Class;") == 0) {
klass.Assign(GetClassRoot(kJavaLangClass));
} else if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
klass.Assign(GetClassRoot(kJavaLangString));
} else if (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) {
klass.Assign(GetClassRoot(kJavaLangRefReference));
} else if (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) {
klass.Assign(GetClassRoot(kJavaLangDexCache));
} else if (strcmp(descriptor, "Ljava/lang/reflect/ArtField;") == 0) {
klass.Assign(GetClassRoot(kJavaLangReflectArtField));
} else if (strcmp(descriptor, "Ljava/lang/reflect/ArtMethod;") == 0) {
klass.Assign(GetClassRoot(kJavaLangReflectArtMethod));
}
}
if (klass.Get() == nullptr) {
// Allocate a class with the status of not ready.
// Interface object should get the right size here. Regular class will
// figure out the right size later and be replaced with one of the right
// size when the class becomes resolved.
klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
}
if (UNLIKELY(klass.Get() == nullptr)) {
CHECK(self->IsExceptionPending()); // Expect an OOME.
return nullptr;
}
klass->SetDexCache(FindDexCache(dex_file));
LoadClass(self, dex_file, dex_class_def, klass, class_loader.Get());
ObjectLock<mirror::Class> lock(self, klass);
if (self->IsExceptionPending()) {
// An exception occured during load, set status to erroneous while holding klass' lock in case
// notification is necessary.
if (!klass->IsErroneous()) {
klass->SetStatus(mirror::Class::kStatusError, self);
}
return nullptr;
}
klass->SetClinitThreadId(self->GetTid());
// Add the newly loaded class to the loaded classes table.
mirror::Class* existing = InsertClass(descriptor, klass.Get(), hash);
if (existing != nullptr) {
// We failed to insert because we raced with another thread. Calling EnsureResolved may cause
// this thread to block.
return EnsureResolved(self, descriptor, existing);
}
// Finish loading (if necessary) by finding parents
CHECK(!klass->IsLoaded());
if (!LoadSuperAndInterfaces(klass, dex_file)) {
// Loading failed.
if (!klass->IsErroneous()) {
klass->SetStatus(mirror::Class::kStatusError, self);
}
return nullptr;
}
CHECK(klass->IsLoaded());
// Link the class (if necessary)
CHECK(!klass->IsResolved());
// TODO: Use fast jobjects?
auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
mirror::Class* new_class = nullptr;
if (!LinkClass(self, descriptor, klass, interfaces, &new_class)) {
// Linking failed.
if (!klass->IsErroneous()) {
klass->SetStatus(mirror::Class::kStatusError, self);
}
return nullptr;