Restore the args in the invoke trampoline slow paths.
Previously, there were errors caused by not restoring the args for
the slow path in artInvokeInterfaceTrampoline and artInvokeCommon. The
slow path in artInvokeInterfaceTrampoline occurs when you attempt
to do an interface dispatch of a method in another dex file. The slow
path in artInvokeCommon occurs when the method in dex cache is not
already resolved.
Since FindMethodFromCode may resolve a method, it can occasionally
cause thread suspension/GC. If a moving GC occurred at this point it
resulted in this_object being invalid after the trampoline returned
the code pointer. This caused a native crash in one of the money runs
since there was an unbundeled app which did a List.iterator call.
Bug: 12934910
Change-Id: Ib454faad14bef0d7732a6d7f1ca5803472d502a6
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 20432c6..9fc173a 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -23,6 +23,7 @@
#include "interpreter/interpreter.h"
#include "mirror/art_method-inl.h"
#include "mirror/class-inl.h"
+#include "mirror/dex_cache-inl.h"
#include "mirror/object-inl.h"
#include "mirror/object_array-inl.h"
#include "object_utils.h"
@@ -618,6 +619,7 @@
// Fixup any references which may have changed.
for (const auto& pair : references_) {
pair.second->Assign(soa_->Decode<mirror::Object*>(pair.first));
+ soa_->Env()->DeleteLocalRef(pair.first);
}
}
@@ -708,6 +710,7 @@
// Fixup any references which may have changed.
for (const auto& pair : references_) {
pair.second->Assign(soa_->Decode<mirror::Object*>(pair.first));
+ soa_->Env()->DeleteLocalRef(pair.first);
}
}
@@ -1630,4 +1633,230 @@
}
}
+template<InvokeType type, bool access_check>
+uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self, mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+template<InvokeType type, bool access_check>
+uint64_t artInvokeCommon(uint32_t method_idx, mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self, mirror::ArtMethod** sp) {
+ mirror::ArtMethod* method = FindMethodFast(method_idx, this_object, caller_method, access_check,
+ type);
+ if (UNLIKELY(method == nullptr)) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
+ const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ uint32_t shorty_len;
+ const char* shorty =
+ dex_file->GetMethodShorty(dex_file->GetMethodId(method_idx), &shorty_len);
+ {
+ // Remember the args in case a GC happens in FindMethodFromCode.
+ ScopedObjectAccessUnchecked soa(self->GetJniEnv());
+ RememberForGcArgumentVisitor visitor(sp, type == kStatic, shorty, shorty_len, &soa);
+ visitor.VisitArguments();
+ method = FindMethodFromCode<type, access_check>(method_idx, this_object, caller_method, self);
+ visitor.FixupReferences();
+ }
+
+ if (UNLIKELY(method == NULL)) {
+ CHECK(self->IsExceptionPending());
+ return 0; // failure
+ }
+ }
+ DCHECK(!self->IsExceptionPending());
+ const void* code = method->GetEntryPointFromQuickCompiledCode();
+
+ // When we return, the caller will branch to this address, so it had better not be 0!
+ DCHECK(code != nullptr) << "Code was NULL in method: " << PrettyMethod(method) << " location: "
+ << MethodHelper(method).GetDexFile().GetLocation();
+#ifdef __LP64__
+ UNIMPLEMENTED(FATAL);
+ return 0;
+#else
+ uint32_t method_uint = reinterpret_cast<uint32_t>(method);
+ uint64_t code_uint = reinterpret_cast<uint32_t>(code);
+ uint64_t result = ((code_uint << 32) | method_uint);
+ return result;
+#endif
+}
+
+
+// See comments in runtime_support_asm.S
+extern "C" uint64_t artInvokeInterfaceTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kInterface, true>(method_idx, this_object, caller_method, self, sp);
+}
+
+
+extern "C" uint64_t artInvokeDirectTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kDirect, true>(method_idx, this_object, caller_method, self, sp);
+}
+
+extern "C" uint64_t artInvokeStaticTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kStatic, true>(method_idx, this_object, caller_method, self, sp);
+}
+
+extern "C" uint64_t artInvokeSuperTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kSuper, true>(method_idx, this_object, caller_method, self, sp);
+}
+
+extern "C" uint64_t artInvokeVirtualTrampolineWithAccessCheck(uint32_t method_idx,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self,
+ mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ return artInvokeCommon<kVirtual, true>(method_idx, this_object, caller_method, self, sp);
+}
+
+// Determine target of interface dispatch. This object is known non-null.
+extern "C" uint64_t artInvokeInterfaceTrampoline(mirror::ArtMethod* interface_method,
+ mirror::Object* this_object,
+ mirror::ArtMethod* caller_method,
+ Thread* self, mirror::ArtMethod** sp)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+ mirror::ArtMethod* method;
+ if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex)) {
+ method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method);
+ if (UNLIKELY(method == NULL)) {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
+ ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(interface_method, this_object,
+ caller_method);
+ return 0; // Failure.
+ }
+ } else {
+ FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs);
+ DCHECK(interface_method == Runtime::Current()->GetResolutionMethod());
+ // Determine method index from calling dex instruction.
+#if defined(__arm__)
+ // On entry the stack pointed by sp is:
+ // | argN | |
+ // | ... | |
+ // | arg4 | |
+ // | arg3 spill | | Caller's frame
+ // | arg2 spill | |
+ // | arg1 spill | |
+ // | Method* | ---
+ // | LR |
+ // | ... | callee saves
+ // | R3 | arg3
+ // | R2 | arg2
+ // | R1 | arg1
+ // | R0 |
+ // | Method* | <- sp
+ DCHECK_EQ(48U, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
+ uintptr_t* regs = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp) + kPointerSize);
+ uintptr_t caller_pc = regs[10];
+#elif defined(__i386__)
+ // On entry the stack pointed by sp is:
+ // | argN | |
+ // | ... | |
+ // | arg4 | |
+ // | arg3 spill | | Caller's frame
+ // | arg2 spill | |
+ // | arg1 spill | |
+ // | Method* | ---
+ // | Return |
+ // | EBP,ESI,EDI | callee saves
+ // | EBX | arg3
+ // | EDX | arg2
+ // | ECX | arg1
+ // | EAX/Method* | <- sp
+ DCHECK_EQ(32U, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
+ uintptr_t* regs = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp));
+ uintptr_t caller_pc = regs[7];
+#elif defined(__mips__)
+ // On entry the stack pointed by sp is:
+ // | argN | |
+ // | ... | |
+ // | arg4 | |
+ // | arg3 spill | | Caller's frame
+ // | arg2 spill | |
+ // | arg1 spill | |
+ // | Method* | ---
+ // | RA |
+ // | ... | callee saves
+ // | A3 | arg3
+ // | A2 | arg2
+ // | A1 | arg1
+ // | A0/Method* | <- sp
+ DCHECK_EQ(64U, Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs)->GetFrameSizeInBytes());
+ uintptr_t* regs = reinterpret_cast<uintptr_t*>(reinterpret_cast<byte*>(sp));
+ uintptr_t caller_pc = regs[15];
+#else
+ UNIMPLEMENTED(FATAL);
+ uintptr_t caller_pc = 0;
+#endif
+ uint32_t dex_pc = caller_method->ToDexPc(caller_pc);
+ const DexFile::CodeItem* code = MethodHelper(caller_method).GetCodeItem();
+ CHECK_LT(dex_pc, code->insns_size_in_code_units_);
+ const Instruction* instr = Instruction::At(&code->insns_[dex_pc]);
+ Instruction::Code instr_code = instr->Opcode();
+ CHECK(instr_code == Instruction::INVOKE_INTERFACE ||
+ instr_code == Instruction::INVOKE_INTERFACE_RANGE)
+ << "Unexpected call into interface trampoline: " << instr->DumpString(NULL);
+ uint32_t dex_method_idx;
+ if (instr_code == Instruction::INVOKE_INTERFACE) {
+ dex_method_idx = instr->VRegB_35c();
+ } else {
+ DCHECK_EQ(instr_code, Instruction::INVOKE_INTERFACE_RANGE);
+ dex_method_idx = instr->VRegB_3rc();
+ }
+
+ const DexFile* dex_file = caller_method->GetDeclaringClass()->GetDexCache()->GetDexFile();
+ uint32_t shorty_len;
+ const char* shorty =
+ dex_file->GetMethodShorty(dex_file->GetMethodId(dex_method_idx), &shorty_len);
+ {
+ // Remember the args in case a GC happens in FindMethodFromCode.
+ ScopedObjectAccessUnchecked soa(self->GetJniEnv());
+ RememberForGcArgumentVisitor visitor(sp, false, shorty, shorty_len, &soa);
+ visitor.VisitArguments();
+ method = FindMethodFromCode<kInterface, false>(dex_method_idx, this_object, caller_method,
+ self);
+ visitor.FixupReferences();
+ }
+
+ if (UNLIKELY(method == nullptr)) {
+ CHECK(self->IsExceptionPending());
+ return 0; // Failure.
+ }
+ }
+ const void* code = method->GetEntryPointFromQuickCompiledCode();
+
+ // When we return, the caller will branch to this address, so it had better not be 0!
+ DCHECK(code != nullptr) << "Code was NULL in method: " << PrettyMethod(method) << " location: "
+ << MethodHelper(method).GetDexFile().GetLocation();
+#ifdef __LP64__
+ UNIMPLEMENTED(FATAL);
+ return 0;
+#else
+ uint32_t method_uint = reinterpret_cast<uint32_t>(method);
+ uint64_t code_uint = reinterpret_cast<uint32_t>(code);
+ uint64_t result = ((code_uint << 32) | method_uint);
+ return result;
+#endif
+}
+
} // namespace art