Support for access check in checkcast and instanceof.
Change-Id: Ie15216618b35cace7d351be2b0a1c466ed6db489
diff --git a/src/asm_support.h b/src/asm_support.h
index f81966f..8d29a35 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -10,13 +10,13 @@
#define rSELF r9
#define rLR r14
// Offset of field Thread::suspend_count_ verified in InitCpu
-#define THREAD_SUSPEND_COUNT_OFFSET 388
+#define THREAD_SUSPEND_COUNT_OFFSET 392
// Offset of field Thread::suspend_count_ verified in InitCpu
-#define THREAD_EXCEPTION_OFFSET 384
+#define THREAD_EXCEPTION_OFFSET 388
#elif defined(__i386__)
// Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 376
+#define THREAD_SELF_OFFSET 380
#endif
#endif // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/compiler/codegen/arm/MethodCodegenDriver.cc b/src/compiler/codegen/arm/MethodCodegenDriver.cc
index 2edd398..a32446a 100644
--- a/src/compiler/codegen/arm/MethodCodegenDriver.cc
+++ b/src/compiler/codegen/arm/MethodCodegenDriver.cc
@@ -164,6 +164,10 @@
art::ClassLinker* class_linker = art::Runtime::Current()->GetClassLinker();
Field* field = class_linker->ResolveField(fieldIdx, method, true);
if (field == NULL) {
+ Thread* thread = Thread::Current();
+ if (thread->IsExceptionPending()) { // clear any exception left by resolve field
+ thread->ClearException();
+ }
return NULL;
}
const art::DexFile& dex_file = class_linker->
diff --git a/src/compiler/codegen/arm/Thumb2/Gen.cc b/src/compiler/codegen/arm/Thumb2/Gen.cc
index 4e3888d..e58a982 100644
--- a/src/compiler/codegen/arm/Thumb2/Gen.cc
+++ b/src/compiler/codegen/arm/Thumb2/Gen.cc
@@ -729,8 +729,14 @@
loadValueDirectFixed(cUnit, rlSrc, r0); // r0 <= ref
int classReg = r2; // r2 will hold the Class*
if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) {
- // Check we have access to type_idx and if not throw IllegalAccessError
- UNIMPLEMENTED(FATAL);
+ // Check we have access to type_idx and if not throw IllegalAccessError,
+ // returns Class* in r0
+ loadWordDisp(cUnit, rSELF,
+ OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
+ rLR);
+ loadConstant(cUnit, r0, type_idx);
+ callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
+ genRegCopy(cUnit, classReg, r0); // Align usage with fast path
} else {
// Load dex cache entry into classReg (r2)
loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
@@ -743,7 +749,7 @@
// Call out to helper, which will return resolved type in r0
loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
loadConstant(cUnit, r0, type_idx);
- callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
+ callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
genRegCopy(cUnit, r2, r0); // Align usage with fast path
loadValueDirectFixed(cUnit, rlSrc, r0); /* reload Ref */
// Rejoin code paths
@@ -784,8 +790,14 @@
loadCurrMethodDirect(cUnit, r1); // r1 <= current Method*
int classReg = r2; // r2 will hold the Class*
if (!cUnit->compiler->CanAccessTypeWithoutChecks(cUnit->method, type_idx)) {
- // Check we have access to type_idx and if not throw IllegalAccessError
- UNIMPLEMENTED(FATAL);
+ // Check we have access to type_idx and if not throw IllegalAccessError,
+ // returns Class* in r0
+ loadWordDisp(cUnit, rSELF,
+ OFFSETOF_MEMBER(Thread, pInitializeTypeAndVerifyAccessFromCode),
+ rLR);
+ loadConstant(cUnit, r0, type_idx);
+ callRuntimeHelper(cUnit, rLR); // InitializeTypeAndVerifyAccess(idx, method)
+ genRegCopy(cUnit, classReg, r0); // Align usage with fast path
} else {
// Load dex cache entry into classReg (r2)
loadWordDisp(cUnit, r1, Method::DexCacheResolvedTypesOffset().Int32Value(), classReg);
@@ -798,8 +810,8 @@
// Call out to helper, which will return resolved type in r0
loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pInitializeTypeFromCode), rLR);
loadConstant(cUnit, r0, type_idx);
- callRuntimeHelper(cUnit, rLR); // resolveTypeFromCode(idx, method)
- genRegCopy(cUnit, r2, r0); // Align usage with fast path
+ callRuntimeHelper(cUnit, rLR); // InitializeTypeFromCode(idx, method)
+ genRegCopy(cUnit, classReg, r0); // Align usage with fast path
// Rejoin code paths
ArmLIR* hopTarget = newLIR0(cUnit, kArmPseudoTargetLabel);
hopTarget->defMask = ENCODE_ALL;
@@ -815,7 +827,7 @@
loadWordDisp(cUnit, r0, Object::ClassOffset().Int32Value(), r1);
/* r1 now contains object->clazz */
loadWordDisp(cUnit, rSELF, OFFSETOF_MEMBER(Thread, pCheckCastFromCode), rLR);
- opRegReg(cUnit, kOpCmp, r1, r2);
+ opRegReg(cUnit, kOpCmp, r1, classReg);
ArmLIR* branch2 = opCondBranch(cUnit, kArmCondEq); /* If equal, trivial yes */
genRegCopy(cUnit, r0, r1);
genRegCopy(cUnit, r1, r2);
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index 8df2abe..3c10fc3 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -763,6 +763,37 @@
CHECK(self->IsExceptionPending());
return NULL; // Failure - Indicate to caller to deliver exception
}
+ DCHECK(referrer->GetDeclaringClass()->CanAccess(klass));
+ // If we are the <clinit> of this class, just return our storage.
+ //
+ // Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
+ // running.
+ if (klass == referrer->GetDeclaringClass() && referrer->IsClassInitializer()) {
+ return klass;
+ }
+ if (!class_linker->EnsureInitialized(klass, true)) {
+ CHECK(self->IsExceptionPending());
+ return NULL; // Failure - Indicate to caller to deliver exception
+ }
+ referrer->GetDexCacheInitializedStaticStorage()->Set(type_idx, klass);
+ return klass;
+}
+
+Class* InitializeStaticStorageAndVerifyAccess(uint32_t type_idx, const Method* referrer,
+ Thread* self) {
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ Class* klass = class_linker->ResolveType(type_idx, referrer);
+ if (UNLIKELY(klass == NULL)) {
+ CHECK(self->IsExceptionPending());
+ return NULL; // Failure - Indicate to caller to deliver exception
+ }
+ // Perform access check
+ if (UNLIKELY(!referrer->GetDeclaringClass()->CanAccess(klass))) {
+ self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;",
+ "Class %s is inaccessible to method %s",
+ PrettyDescriptor(klass->GetDescriptor()).c_str(),
+ PrettyMethod(referrer, true).c_str());
+ }
// If we are the <clinit> of this class, just return our storage.
//
// Do not set the DexCache InitializedStaticStorage, since that implies <clinit> has finished
@@ -791,6 +822,15 @@
return InitializeStaticStorage(type_idx, referrer, self);
}
+extern "C" Class* artInitializeTypeAndVerifyAccessFromCode(uint32_t type_idx,
+ const Method* referrer, Thread* self,
+ Method** sp) {
+ // Called when caller isn't guaranteed to have access to a type and the dex cache may be
+ // unpopulated
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsOnly);
+ return InitializeStaticStorageAndVerifyAccess(type_idx, referrer, self);
+}
+
// TODO: placeholder. Helper function to resolve virtual method
void ResolveMethodFromCode(Method* method, uint32_t method_idx) {
/*
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 90fcf2c..be53b03 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -20,6 +20,8 @@
extern void ThrowAbstractMethodErrorFromCode(Method* method, Thread* thread, Method** sp);
void* UnresolvedDirectMethodTrampolineFromCode(int32_t, Method**, Thread*, Runtime::TrampolineType);
extern Class* InitializeStaticStorage(uint32_t type_idx, const Method* referrer, Thread* self);
+extern Class* InitializeStaticStorageAndVerifyAccess(uint32_t type_idx, const Method* referrer,
+ Thread* self);
extern Class* InitializeTypeFromCode(uint32_t type_idx, Method* method);
uint32_t IsAssignableFromCode(const Class* klass, const Class* ref_class);
void ObjectInitFromCode(Object* o);
@@ -66,6 +68,7 @@
extern "C" void* art_get_obj_static_from_code(uint32_t, void*);
extern "C" void* art_initialize_static_storage_from_code(uint32_t, void*);
extern "C" void* art_initialize_type_from_code(uint32_t, void*);
+ extern "C" void* art_initialize_type_and_verify_access_from_code(uint32_t, void*);
extern "C" void* art_resolve_string_from_code(void*, uint32_t);
/* Conversions */
diff --git a/src/runtime_support_asm.S b/src/runtime_support_asm.S
index 112a114..ad600ce 100644
--- a/src/runtime_support_asm.S
+++ b/src/runtime_support_asm.S
@@ -296,6 +296,23 @@
bxne lr @ return on success
DELIVER_PENDING_EXCEPTION
+ .global art_initialize_type_and_verify_access_from_code
+ .extern artInitializeTypeAndVerifyAccessFromCode
+ /*
+ * Entry from managed code when type_idx needs to be checked for access and dex cache may also
+ * miss
+ */
+art_initialize_type_and_verify_access_from_code:
+ SETUP_REF_ONLY_CALLEE_SAVE_FRAME @ save callee saves in case of GC
+ mov r2, r9 @ pass Thread::Current
+ mov r3, sp @ pass SP
+ @ artInitializeTypeFromCode(uint32_t type_idx, Method* referrer, Thread*, SP)
+ bl artInitializeTypeAndVerifyAccessFromCode
+ RESTORE_REF_ONLY_CALLEE_SAVE_FRAME
+ cmp r0, #0 @ success if result is non-null
+ bxne lr @ return on success
+ DELIVER_PENDING_EXCEPTION
+
.global art_find_instance_field_from_code
.extern artFindInstanceFieldFromCode
/*
diff --git a/src/thread.cc b/src/thread.cc
index a8c58bf..4f47d22 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -102,6 +102,7 @@
pHandleFillArrayDataFromCode = art_handle_fill_data_from_code;
pInitializeStaticStorage = art_initialize_static_storage_from_code;
pInitializeTypeFromCode = art_initialize_type_from_code;
+ pInitializeTypeAndVerifyAccessFromCode = art_initialize_type_and_verify_access_from_code;
pInvokeInterfaceTrampoline = art_invoke_interface_trampoline;
pLockObjectFromCode = art_lock_object_from_code;
pObjectInit = art_object_init_from_code;
diff --git a/src/thread.h b/src/thread.h
index eec9df4..547fdfa 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -138,6 +138,7 @@
uint32_t (*pInstanceofNonTrivialFromCode)(const Class*, const Class*);
void (*pInvokeInterfaceTrampoline)(uint32_t, void*);
void* (*pInitializeTypeFromCode)(uint32_t, void*);
+ void* (*pInitializeTypeAndVerifyAccessFromCode)(uint32_t, void*);
void (*pLockObjectFromCode)(void*);
void (*pObjectInit)(void*);
void (*pResolveMethodFromCode)(Method*, uint32_t);