From 59952167697d3b7e6655cb76d087a809c4f615d8 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 17 Jul 2014 18:52:42 -0700 Subject: [PATCH 001/150] Avoid race in single thread suspension. Don't allow more than one concurrent single thread suspension to avoid potential cycles and deadlocks where threads try to suspend each other. Bug: 16364458, 16354227 (cherry picked from commit f3d874c60ee3ada19ce26a5c4e532312b6f3a9e9) Change-Id: I907f1d5591a6aa5c241d37d6b4a34f968f98df77 --- runtime/base/mutex.cc | 20 +++++++++---- runtime/base/mutex.h | 12 +++++++- runtime/debugger.cc | 22 +++++++++----- runtime/monitor.cc | 2 ++ runtime/native/dalvik_system_VMStack.cc | 7 ++++- runtime/native/java_lang_Thread.cc | 9 +++++- ...pache_harmony_dalvik_ddmc_DdmVmInternal.cc | 7 ++++- runtime/thread-inl.h | 30 +++++++++---------- runtime/thread_list.cc | 29 +++++++++--------- runtime/thread_list.h | 2 ++ 10 files changed, 93 insertions(+), 47 deletions(-) diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 77795477250..f856ddc2822 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -41,6 +41,7 @@ Mutex* Locks::modify_ldt_lock_ = nullptr; ReaderWriterMutex* Locks::mutator_lock_ = nullptr; Mutex* Locks::runtime_shutdown_lock_ = nullptr; Mutex* Locks::thread_list_lock_ = nullptr; +Mutex* Locks::thread_list_suspend_thread_lock_ = nullptr; Mutex* Locks::thread_suspend_count_lock_ = nullptr; Mutex* Locks::trace_lock_ = nullptr; Mutex* Locks::profiler_lock_ = nullptr; @@ -149,7 +150,8 @@ void BaseMutex::CheckSafeToWait(Thread* self) { for (int i = kLockLevelCount - 1; i >= 0; --i) { if (i != level_) { BaseMutex* held_mutex = self->GetHeldMutex(static_cast(i)); - if (held_mutex != NULL) { + // We expect waits to happen while holding the thread list suspend thread lock. + if (held_mutex != NULL && i != kThreadListSuspendThreadLock) { LOG(ERROR) << "Holding \"" << held_mutex->name_ << "\" " << "(level " << LockLevel(i) << ") while performing wait on " << "\"" << name_ << "\" (level " << level_ << ")"; @@ -841,6 +843,7 @@ void Locks::Init() { DCHECK(logging_lock_ != nullptr); DCHECK(mutator_lock_ != nullptr); DCHECK(thread_list_lock_ != nullptr); + DCHECK(thread_list_suspend_thread_lock_ != nullptr); DCHECK(thread_suspend_count_lock_ != nullptr); DCHECK(trace_lock_ != nullptr); DCHECK(profiler_lock_ != nullptr); @@ -848,13 +851,18 @@ void Locks::Init() { DCHECK(intern_table_lock_ != nullptr); } else { // Create global locks in level order from highest lock level to lowest. - LockLevel current_lock_level = kMutatorLock; - DCHECK(mutator_lock_ == nullptr); - mutator_lock_ = new ReaderWriterMutex("mutator lock", current_lock_level); + LockLevel current_lock_level = kThreadListSuspendThreadLock; + DCHECK(thread_list_suspend_thread_lock_ == nullptr); + thread_list_suspend_thread_lock_ = + new Mutex("thread list suspend thread by .. lock", current_lock_level); #define UPDATE_CURRENT_LOCK_LEVEL(new_level) \ - DCHECK_LT(new_level, current_lock_level); \ - current_lock_level = new_level; + DCHECK_LT(new_level, current_lock_level); \ + current_lock_level = new_level; + + UPDATE_CURRENT_LOCK_LEVEL(kMutatorLock); + DCHECK(mutator_lock_ == nullptr); + mutator_lock_ = new ReaderWriterMutex("mutator lock", current_lock_level); UPDATE_CURRENT_LOCK_LEVEL(kHeapBitmapLock); DCHECK(heap_bitmap_lock_ == nullptr); diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 8d2cd07aeab..7adaa1bcce1 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -93,6 +93,7 @@ enum LockLevel { kRuntimeShutdownLock, kHeapBitmapLock, kMutatorLock, + kThreadListSuspendThreadLock, kZygoteCreationLock, kLockLevelCount // Must come last. @@ -474,6 +475,15 @@ class Locks { public: static void Init(); + // There's a potential race for two threads to try to suspend each other and for both of them + // to succeed and get blocked becoming runnable. This lock ensures that only one thread is + // requesting suspension of another at any time. As the the thread list suspend thread logic + // transitions to runnable, if the current thread were tried to be suspended then this thread + // would block holding this lock until it could safely request thread suspension of the other + // thread without that thread having a suspension request against this thread. This avoids a + // potential deadlock cycle. + static Mutex* thread_list_suspend_thread_lock_; + // The mutator_lock_ is used to allow mutators to execute in a shared (reader) mode or to block // mutators by having an exclusive (writer) owner. In normal execution each mutator thread holds // a share on the mutator_lock_. The garbage collector may also execute with shared access but @@ -532,7 +542,7 @@ class Locks { // else | .. running .. // Goto x | .. running .. // .. running .. | .. running .. - static ReaderWriterMutex* mutator_lock_; + static ReaderWriterMutex* mutator_lock_ ACQUIRED_AFTER(thread_list_suspend_thread_lock_); // Allow reader-writer mutual exclusion on the mark and live bitmaps of the heap. static ReaderWriterMutex* heap_bitmap_lock_ ACQUIRED_AFTER(mutator_lock_); diff --git a/runtime/debugger.cc b/runtime/debugger.cc index c95be0154ab..c7739fe3feb 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -2249,15 +2249,18 @@ void Dbg::ResumeVM() { } JDWP::JdwpError Dbg::SuspendThread(JDWP::ObjectId thread_id, bool request_suspension) { - ScopedLocalRef peer(Thread::Current()->GetJniEnv(), NULL); + Thread* self = Thread::Current(); + ScopedLocalRef peer(self->GetJniEnv(), NULL); { - ScopedObjectAccess soa(Thread::Current()); + ScopedObjectAccess soa(self); peer.reset(soa.AddLocalReference(gRegistry->Get(thread_id))); } if (peer.get() == NULL) { return JDWP::ERR_THREAD_NOT_ALIVE; } - // Suspend thread to build stack trace. + // Suspend thread to build stack trace. Take suspend thread lock to avoid races with threads + // trying to suspend this one. + MutexLock mu(self, *Locks::thread_list_suspend_thread_lock_); bool timed_out; Thread* thread = ThreadList::SuspendThreadByPeer(peer.get(), request_suspension, true, &timed_out); @@ -3143,7 +3146,7 @@ class ScopedThreadSuspension { ScopedThreadSuspension(Thread* self, JDWP::ObjectId thread_id) LOCKS_EXCLUDED(Locks::thread_list_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : - thread_(NULL), + thread_(nullptr), error_(JDWP::ERR_NONE), self_suspend_(false), other_suspend_(false) { @@ -3159,10 +3162,15 @@ class ScopedThreadSuspension { soa.Self()->TransitionFromRunnableToSuspended(kWaitingForDebuggerSuspension); jobject thread_peer = gRegistry->GetJObject(thread_id); bool timed_out; - Thread* suspended_thread = ThreadList::SuspendThreadByPeer(thread_peer, true, true, - &timed_out); + Thread* suspended_thread; + { + // Take suspend thread lock to avoid races with threads trying to suspend this one. + MutexLock mu(soa.Self(), *Locks::thread_list_suspend_thread_lock_); + suspended_thread = ThreadList::SuspendThreadByPeer(thread_peer, true, true, + &timed_out); + } CHECK_EQ(soa.Self()->TransitionFromSuspendedToRunnable(), kWaitingForDebuggerSuspension); - if (suspended_thread == NULL) { + if (suspended_thread == nullptr) { // Thread terminated from under us while suspending. error_ = JDWP::ERR_INVALID_THREAD; } else { diff --git a/runtime/monitor.cc b/runtime/monitor.cc index da481e41710..a6873efdcb5 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -681,6 +681,8 @@ void Monitor::InflateThinLocked(Thread* self, Handle obj, LockWo Thread* owner; { ScopedThreadStateChange tsc(self, kBlocked); + // Take suspend thread lock to avoid races with threads trying to suspend this one. + MutexLock mu(self, *Locks::thread_list_suspend_thread_lock_); owner = thread_list->SuspendThreadByThreadId(owner_thread_id, false, &timed_out); } if (owner != nullptr) { diff --git a/runtime/native/dalvik_system_VMStack.cc b/runtime/native/dalvik_system_VMStack.cc index cf310648e53..5f718ba213f 100644 --- a/runtime/native/dalvik_system_VMStack.cc +++ b/runtime/native/dalvik_system_VMStack.cc @@ -35,7 +35,12 @@ static jobject GetThreadStack(const ScopedFastNativeObjectAccess& soa, jobject p // Suspend thread to build stack trace. soa.Self()->TransitionFromRunnableToSuspended(kNative); bool timed_out; - Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out); + Thread* thread; + { + // Take suspend thread lock to avoid races with threads trying to suspend this one. + MutexLock mu(soa.Self(), *Locks::thread_list_suspend_thread_lock_); + thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out); + } if (thread != nullptr) { // Must be runnable to create returned array. CHECK_EQ(soa.Self()->TransitionFromSuspendedToRunnable(), kNative); diff --git a/runtime/native/java_lang_Thread.cc b/runtime/native/java_lang_Thread.cc index bae67f20e89..8f83f963182 100644 --- a/runtime/native/java_lang_Thread.cc +++ b/runtime/native/java_lang_Thread.cc @@ -116,18 +116,25 @@ static void Thread_nativeInterrupt(JNIEnv* env, jobject java_thread) { static void Thread_nativeSetName(JNIEnv* env, jobject peer, jstring java_name) { ScopedUtfChars name(env, java_name); + Thread* self; { ScopedObjectAccess soa(env); if (soa.Decode(peer) == soa.Self()->GetPeer()) { soa.Self()->SetThreadName(name.c_str()); return; } + self = soa.Self(); } // Suspend thread to avoid it from killing itself while we set its name. We don't just hold the // thread list lock to avoid this, as setting the thread name causes mutator to lock/unlock // in the DDMS send code. bool timed_out; - Thread* thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out); + // Take suspend thread lock to avoid races with threads trying to suspend this one. + Thread* thread; + { + MutexLock mu(self, *Locks::thread_list_suspend_thread_lock_); + thread = ThreadList::SuspendThreadByPeer(peer, true, false, &timed_out); + } if (thread != NULL) { { ScopedObjectAccess soa(env); diff --git a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc index e17e60a7cee..45ef9ae7272 100644 --- a/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc +++ b/runtime/native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc @@ -61,7 +61,12 @@ static jobjectArray DdmVmInternal_getStackTraceById(JNIEnv* env, jclass, jint th } // Suspend thread to build stack trace. - Thread* thread = thread_list->SuspendThreadByThreadId(thin_lock_id, false, &timed_out); + Thread* thread; + { + // Take suspend thread lock to avoid races with threads trying to suspend this one. + MutexLock mu(self, *Locks::thread_list_suspend_thread_lock_); + thread = thread_list->SuspendThreadByThreadId(thin_lock_id, false, &timed_out); + } if (thread != nullptr) { { ScopedObjectAccess soa(env); diff --git a/runtime/thread-inl.h b/runtime/thread-inl.h index 38f1307321a..a5caa075ee2 100644 --- a/runtime/thread-inl.h +++ b/runtime/thread-inl.h @@ -57,26 +57,24 @@ inline ThreadState Thread::SetState(ThreadState new_state) { } inline void Thread::AssertThreadSuspensionIsAllowable(bool check_locks) const { -#ifdef NDEBUG - UNUSED(check_locks); // Keep GCC happy about unused parameters. -#else - CHECK_EQ(0u, tls32_.no_thread_suspension) << tlsPtr_.last_no_thread_suspension_cause; - if (check_locks) { - bool bad_mutexes_held = false; - for (int i = kLockLevelCount - 1; i >= 0; --i) { - // We expect no locks except the mutator_lock_. - if (i != kMutatorLock) { - BaseMutex* held_mutex = GetHeldMutex(static_cast(i)); - if (held_mutex != NULL) { - LOG(ERROR) << "holding \"" << held_mutex->GetName() - << "\" at point where thread suspension is expected"; - bad_mutexes_held = true; + if (kIsDebugBuild) { + CHECK_EQ(0u, tls32_.no_thread_suspension) << tlsPtr_.last_no_thread_suspension_cause; + if (check_locks) { + bool bad_mutexes_held = false; + for (int i = kLockLevelCount - 1; i >= 0; --i) { + // We expect no locks except the mutator_lock_ or thread list suspend thread lock. + if (i != kMutatorLock && i != kThreadListSuspendThreadLock) { + BaseMutex* held_mutex = GetHeldMutex(static_cast(i)); + if (held_mutex != NULL) { + LOG(ERROR) << "holding \"" << held_mutex->GetName() + << "\" at point where thread suspension is expected"; + bad_mutexes_held = true; + } } } + CHECK(!bad_mutexes_held); } - CHECK(!bad_mutexes_held); } -#endif } inline void Thread::TransitionFromRunnableToSuspended(ThreadState new_state) { diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index b649b626ca0..ff1a07974a1 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -170,16 +170,7 @@ static void UnsafeLogFatalForThreadSuspendAllTimeout() { // individual thread requires polling. delay_us is the requested sleep and total_delay_us // accumulates the total time spent sleeping for timeouts. The first sleep is just a yield, // subsequently sleeps increase delay_us from 1ms to 500ms by doubling. -static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us, - bool holding_locks) { - if (!holding_locks) { - for (int i = kLockLevelCount - 1; i >= 0; --i) { - BaseMutex* held_mutex = self->GetHeldMutex(static_cast(i)); - if (held_mutex != NULL) { - LOG(FATAL) << "Holding " << held_mutex->GetName() << " while sleeping for thread suspension"; - } - } - } +static void ThreadSuspendSleep(Thread* self, useconds_t* delay_us, useconds_t* total_delay_us) { useconds_t new_delay_us = (*delay_us) * 2; CHECK_GE(new_delay_us, *delay_us); if (new_delay_us < 500000) { // Don't allow sleeping to be more than 0.5s. @@ -244,7 +235,7 @@ size_t ThreadList::RunCheckpoint(Closure* checkpoint_function) { useconds_t total_delay_us = 0; do { useconds_t delay_us = 100; - ThreadSuspendSleep(self, &delay_us, &total_delay_us, true); + ThreadSuspendSleep(self, &delay_us, &total_delay_us); } while (!thread->IsSuspended()); // Shouldn't need to wait for longer than 1000 microseconds. constexpr useconds_t kLongWaitThresholdUS = 1000; @@ -444,6 +435,11 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension, while (true) { Thread* thread; { + // Note: this will transition to runnable and potentially suspend. We ensure only one thread + // is requesting another suspend, to avoid deadlock, by requiring this function be called + // holding Locks::thread_list_suspend_thread_lock_. Its important this thread suspend rather + // than request thread suspension, to avoid potential cycles in threads requesting each other + // suspend. ScopedObjectAccess soa(self); MutexLock mu(self, *Locks::thread_list_lock_); thread = Thread::FromManagedThread(soa, peer); @@ -483,7 +479,7 @@ Thread* ThreadList::SuspendThreadByPeer(jobject peer, bool request_suspension, } // Release locks and come out of runnable state. } - ThreadSuspendSleep(self, &delay_us, &total_delay_us, false); + ThreadSuspendSleep(self, &delay_us, &total_delay_us); } } @@ -502,9 +498,14 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe CHECK_NE(thread_id, kInvalidThreadId); while (true) { { - Thread* thread = NULL; + // Note: this will transition to runnable and potentially suspend. We ensure only one thread + // is requesting another suspend, to avoid deadlock, by requiring this function be called + // holding Locks::thread_list_suspend_thread_lock_. Its important this thread suspend rather + // than request thread suspension, to avoid potential cycles in threads requesting each other + // suspend. ScopedObjectAccess soa(self); MutexLock mu(self, *Locks::thread_list_lock_); + Thread* thread = nullptr; for (const auto& it : list_) { if (it->GetThreadId() == thread_id) { thread = it; @@ -550,7 +551,7 @@ Thread* ThreadList::SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspe } // Release locks and come out of runnable state. } - ThreadSuspendSleep(self, &delay_us, &total_delay_us, false); + ThreadSuspendSleep(self, &delay_us, &total_delay_us); } } diff --git a/runtime/thread_list.h b/runtime/thread_list.h index d46987a8b81..1b67ac0588b 100644 --- a/runtime/thread_list.h +++ b/runtime/thread_list.h @@ -68,6 +68,7 @@ class ThreadList { // is set to true. static Thread* SuspendThreadByPeer(jobject peer, bool request_suspension, bool debug_suspension, bool* timed_out) + EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_suspend_thread_lock_) LOCKS_EXCLUDED(Locks::mutator_lock_, Locks::thread_list_lock_, Locks::thread_suspend_count_lock_); @@ -77,6 +78,7 @@ class ThreadList { // the thread terminating. Note that as thread ids are recycled this may not suspend the expected // thread, that may be terminating. If the suspension times out then *timeout is set to true. Thread* SuspendThreadByThreadId(uint32_t thread_id, bool debug_suspension, bool* timed_out) + EXCLUSIVE_LOCKS_REQUIRED(Locks::thread_list_suspend_thread_lock_) LOCKS_EXCLUDED(Locks::mutator_lock_, Locks::thread_list_lock_, Locks::thread_suspend_count_lock_); From c5c82589bc012f775dc22d4a1f576ac2992382c8 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 23 Jul 2014 18:45:17 -0700 Subject: [PATCH 002/150] Fix stale remembered sets error. We were forgetting to remove the remembered set in transition to background. This resulted in remembered sets being added for spaces which no longer existed. This finally caused an error when a new space happened to have the same address as the old space, resulting in a CHECK failure. Also tuned the number of ParallelGC to prevent spurrious failures and removed the ParallelGC from broken tests in the make file. Bug: 16532086 Bug: 16406852 Change-Id: I00bbcbd7daa03c867732d165be62b72e6c43bce1 (cherry picked from c5a8347ac491a5f521945d3835a322123830456b) --- runtime/gc/heap.cc | 4 ++++ test/ParallelGC/ParallelGC.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 4d842f17f0f..9548022b541 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -1609,6 +1609,10 @@ void Heap::TransitionCollector(CollectorType collector_type) { // Remove the main space so that we don't try to trim it, this doens't work for debug // builds since RosAlloc attempts to read the magic number from a protected page. RemoveSpace(main_space_); + RemoveRememberedSet(main_space_); + RemoveRememberedSet(main_space_backup_.get()); + main_space_backup_.reset(nullptr); + main_space_ = nullptr; temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2", mem_map.release()); AddSpace(temp_space_); diff --git a/test/ParallelGC/ParallelGC.java b/test/ParallelGC/ParallelGC.java index eb9e04e70a6..fd95c6ccd3b 100644 --- a/test/ParallelGC/ParallelGC.java +++ b/test/ParallelGC/ParallelGC.java @@ -39,7 +39,7 @@ private ParallelGC(int id) { public void run() { List l = new ArrayList(); - for (int i = 0; i < 1000; i++) { + for (int i = 0; i < 400; i++) { l.add(new ArrayList(i)); System.out.print(id); } From a1b730c90691321edeb67bd11baea261da59128e Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 25 Jul 2014 10:13:51 -0700 Subject: [PATCH 003/150] Fix dangling pointer bug when transitioning to background. Dangling pointer left behind from the old rosalloc / dlmalloc spaces. We now avoid using this pointer by using main_space_ and non_moving_space_ as well as clear the pointer when we remove the space. Bug: 16567203 (cherry picked from commit 00b5915828f89daaefd9e8fb215658360f76762c) Change-Id: I5962929e4a0bb4db6f531d25ee322da7ab3f5dd4 --- runtime/gc/heap.cc | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 9548022b541..c4d0c41cc27 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -670,18 +670,11 @@ void Heap::VisitObjects(ObjectCallback callback, void* arg) { } void Heap::MarkAllocStackAsLive(accounting::ObjectStack* stack) { - space::ContinuousSpace* space1 = rosalloc_space_ != nullptr ? rosalloc_space_ : non_moving_space_; - space::ContinuousSpace* space2 = dlmalloc_space_ != nullptr ? dlmalloc_space_ : non_moving_space_; - // This is just logic to handle a case of either not having a rosalloc or dlmalloc space. + space::ContinuousSpace* space1 = main_space_ != nullptr ? main_space_ : non_moving_space_; + space::ContinuousSpace* space2 = non_moving_space_; // TODO: Generalize this to n bitmaps? - if (space1 == nullptr) { - DCHECK(space2 != nullptr); - space1 = space2; - } - if (space2 == nullptr) { - DCHECK(space1 != nullptr); - space2 = space1; - } + CHECK(space1 != nullptr); + CHECK(space2 != nullptr); MarkAllocStack(space1->GetLiveBitmap(), space2->GetLiveBitmap(), large_object_space_->GetLiveBitmap(), stack); } @@ -1609,6 +1602,12 @@ void Heap::TransitionCollector(CollectorType collector_type) { // Remove the main space so that we don't try to trim it, this doens't work for debug // builds since RosAlloc attempts to read the magic number from a protected page. RemoveSpace(main_space_); + // Unset the pointers just in case. + if (dlmalloc_space_ == main_space_) { + dlmalloc_space_ = nullptr; + } else if (rosalloc_space_ == main_space_) { + rosalloc_space_ = nullptr; + } RemoveRememberedSet(main_space_); RemoveRememberedSet(main_space_backup_.get()); main_space_backup_.reset(nullptr); From 1c46a24c1ddaac587b8baa38626cbc9e502872d1 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 25 Jul 2014 11:50:47 -0700 Subject: [PATCH 004/150] Fix main space memory leak and add checks. The hypothesis is that we were leaking the main space and its bitmaps, then eventually we would run out of virtual address space, which would cause a null bitmap (DCHECK). Finally when we tried adding the space with a null bitmap to the heap bitmap it segfaulted. Changed some non performance critical DCHECK -> CHECK. Bug: 16563323 (cherry picked from commit 2796a1669ae0f3b96db8432fbd8be1b93bf335c4) Change-Id: Ifa9d866c6c89eff22a547af4db70bc79a77690ed --- runtime/gc/heap.cc | 15 ++++++++------- runtime/gc/space/malloc_space.cc | 4 ++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index c4d0c41cc27..0b57e959ab1 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -693,7 +693,7 @@ void Heap::AddSpace(space::Space* space) { accounting::ContinuousSpaceBitmap* live_bitmap = continuous_space->GetLiveBitmap(); accounting::ContinuousSpaceBitmap* mark_bitmap = continuous_space->GetMarkBitmap(); if (live_bitmap != nullptr) { - DCHECK(mark_bitmap != nullptr); + CHECK(mark_bitmap != nullptr); live_bitmap_->AddContinuousSpaceBitmap(live_bitmap); mark_bitmap_->AddContinuousSpaceBitmap(mark_bitmap); } @@ -704,7 +704,7 @@ void Heap::AddSpace(space::Space* space) { return a->Begin() < b->Begin(); }); } else { - DCHECK(space->IsDiscontinuousSpace()); + CHECK(space->IsDiscontinuousSpace()); space::DiscontinuousSpace* discontinuous_space = space->AsDiscontinuousSpace(); live_bitmap_->AddLargeObjectBitmap(discontinuous_space->GetLiveBitmap()); mark_bitmap_->AddLargeObjectBitmap(discontinuous_space->GetMarkBitmap()); @@ -1599,19 +1599,20 @@ void Heap::TransitionCollector(CollectorType collector_type) { Compact(bump_pointer_space_, main_space_, kGcCauseCollectorTransition); // Use the now empty main space mem map for the bump pointer temp space. mem_map.reset(main_space_->ReleaseMemMap()); - // Remove the main space so that we don't try to trim it, this doens't work for debug - // builds since RosAlloc attempts to read the magic number from a protected page. - RemoveSpace(main_space_); // Unset the pointers just in case. if (dlmalloc_space_ == main_space_) { dlmalloc_space_ = nullptr; } else if (rosalloc_space_ == main_space_) { rosalloc_space_ = nullptr; } + // Remove the main space so that we don't try to trim it, this doens't work for debug + // builds since RosAlloc attempts to read the magic number from a protected page. + RemoveSpace(main_space_); RemoveRememberedSet(main_space_); - RemoveRememberedSet(main_space_backup_.get()); - main_space_backup_.reset(nullptr); + delete main_space_; // Delete the space since it has been removed. main_space_ = nullptr; + RemoveRememberedSet(main_space_backup_.get()); + main_space_backup_.reset(nullptr); // Deletes the space. temp_space_ = space::BumpPointerSpace::CreateFromMemMap("Bump pointer space 2", mem_map.release()); AddSpace(temp_space_); diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index 27f92b571ed..ba7e5c1eca0 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -51,12 +51,12 @@ MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map, live_bitmap_.reset(accounting::ContinuousSpaceBitmap::Create( StringPrintf("allocspace %s live-bitmap %d", name.c_str(), static_cast(bitmap_index)), Begin(), NonGrowthLimitCapacity())); - DCHECK(live_bitmap_.get() != nullptr) << "could not create allocspace live bitmap #" + CHECK(live_bitmap_.get() != nullptr) << "could not create allocspace live bitmap #" << bitmap_index; mark_bitmap_.reset(accounting::ContinuousSpaceBitmap::Create( StringPrintf("allocspace %s mark-bitmap %d", name.c_str(), static_cast(bitmap_index)), Begin(), NonGrowthLimitCapacity())); - DCHECK(live_bitmap_.get() != nullptr) << "could not create allocspace mark bitmap #" + CHECK(live_bitmap_.get() != nullptr) << "could not create allocspace mark bitmap #" << bitmap_index; } for (auto& freed : recent_freed_objects_) { From 0e7f37de5bcebb413712eddd1831f30bd0818664 Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Wed, 16 Jul 2014 10:44:41 -0700 Subject: [PATCH 005/150] Set vtable in class object to null after linking. This is follow-up work of embedding imt and vtable for faster interface/virtual call dispatching. Once vtable becomes embedded, the original vtable is nulled. (cherry picked from commit 2cdbad7c62f126581ec5177104de961c4d71adaa) Change-Id: I6acdcd1ee560d387fb77c55c58bbe3598c197ba1 --- compiler/driver/compiler_driver-inl.h | 4 +- runtime/class_linker.cc | 40 ++++++++++------- runtime/class_linker_test.cc | 6 +-- runtime/entrypoints/entrypoint_utils-inl.h | 25 ++++++----- runtime/interpreter/interpreter_common.h | 3 +- runtime/mirror/art_method.cc | 5 +-- runtime/mirror/class-inl.h | 50 +++++++++++++++++++--- runtime/mirror/class.cc | 10 ++++- runtime/mirror/class.h | 18 +++++++- runtime/verifier/method_verifier.cc | 15 ++++--- test/100-reflect2/expected.txt | 2 +- 11 files changed, 126 insertions(+), 52 deletions(-) diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 99fcc26aa5f..02f4e3a179d 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -234,8 +234,8 @@ inline int CompilerDriver::IsFastInvoke( // the super class. bool can_sharpen_super_based_on_type = (*invoke_type == kSuper) && (referrer_class != methods_class) && referrer_class->IsSubClass(methods_class) && - resolved_method->GetMethodIndex() < methods_class->GetVTable()->GetLength() && - (methods_class->GetVTable()->Get(resolved_method->GetMethodIndex()) == resolved_method); + resolved_method->GetMethodIndex() < methods_class->GetVTableLength() && + (methods_class->GetVTableEntry(resolved_method->GetMethodIndex()) == resolved_method); if (can_sharpen_virtual_based_on_type || can_sharpen_super_based_on_type) { // Sharpen a virtual call into a direct call. The method_idx is into referrer's diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 1ea42bb09bc..e0fa0f87257 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3574,9 +3574,9 @@ bool ClassLinker::ValidateSuperClassDescriptors(Handle klass) { MethodHelper super_mh(hs.NewHandle(nullptr)); if (klass->HasSuperClass() && klass->GetClassLoader() != klass->GetSuperClass()->GetClassLoader()) { - for (int i = klass->GetSuperClass()->GetVTable()->GetLength() - 1; i >= 0; --i) { - mh.ChangeMethod(klass->GetVTable()->GetWithoutChecks(i)); - super_mh.ChangeMethod(klass->GetSuperClass()->GetVTable()->GetWithoutChecks(i)); + for (int i = klass->GetSuperClass()->GetVTableLength() - 1; i >= 0; --i) { + mh.ChangeMethod(klass->GetVTableEntry(i)); + super_mh.ChangeMethod(klass->GetSuperClass()->GetVTableEntry(i)); if (mh.GetMethod() != super_mh.GetMethod() && !mh.HasSameSignatureWithDifferentClassLoaders(&super_mh)) { ThrowLinkageError(klass.Get(), @@ -3734,10 +3734,6 @@ bool ClassLinker::LinkClass(Thread* self, const char* descriptor, HandleSetStatus(mirror::Class::kStatusResolved, self); - - // Only embedded imt should be used from this point. - new_class_h->SetImTable(NULL); - // TODO: remove vtable and only use embedded vtable. } return true; } @@ -3870,17 +3866,31 @@ bool ClassLinker::LinkMethods(Thread* self, Handle klass, bool ClassLinker::LinkVirtualMethods(Thread* self, Handle klass) { if (klass->HasSuperClass()) { uint32_t max_count = klass->NumVirtualMethods() + - klass->GetSuperClass()->GetVTable()->GetLength(); - size_t actual_count = klass->GetSuperClass()->GetVTable()->GetLength(); + klass->GetSuperClass()->GetVTableLength(); + size_t actual_count = klass->GetSuperClass()->GetVTableLength(); CHECK_LE(actual_count, max_count); - // TODO: do not assign to the vtable field until it is fully constructed. StackHandleScope<3> hs(self); - Handle> vtable( - hs.NewHandle(klass->GetSuperClass()->GetVTable()->CopyOf(self, max_count))); - if (UNLIKELY(vtable.Get() == NULL)) { - CHECK(self->IsExceptionPending()); // OOME. - return false; + Handle> vtable; + mirror::Class* super_class = klass->GetSuperClass(); + if (super_class->ShouldHaveEmbeddedImtAndVTable()) { + vtable = hs.NewHandle(AllocArtMethodArray(self, max_count)); + if (UNLIKELY(vtable.Get() == nullptr)) { + CHECK(self->IsExceptionPending()); // OOME. + return false; + } + int len = super_class->GetVTableLength(); + for (int i=0; iSet(i, super_class->GetVTableEntry(i)); + } + } else { + CHECK(super_class->GetVTable() != nullptr) << PrettyClass(super_class); + vtable = hs.NewHandle(super_class->GetVTable()->CopyOf(self, max_count)); + if (UNLIKELY(vtable.Get() == nullptr)) { + CHECK(self->IsExceptionPending()); // OOME. + return false; + } } + // See if any of our virtual methods override the superclass. MethodHelper local_mh(hs.NewHandle(nullptr)); MethodHelper super_mh(hs.NewHandle(nullptr)); diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc index 7b5a5026df4..89e42da6af8 100644 --- a/runtime/class_linker_test.cc +++ b/runtime/class_linker_test.cc @@ -88,7 +88,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_EQ(0U, primitive->NumInstanceFields()); EXPECT_EQ(0U, primitive->NumStaticFields()); EXPECT_EQ(0U, primitive->NumDirectInterfaces()); - EXPECT_TRUE(primitive->GetVTable() == NULL); + EXPECT_FALSE(primitive->HasVTable()); EXPECT_EQ(0, primitive->GetIfTableCount()); EXPECT_TRUE(primitive->GetIfTable() == NULL); EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags()); @@ -140,7 +140,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_EQ(0U, array->NumInstanceFields()); EXPECT_EQ(0U, array->NumStaticFields()); EXPECT_EQ(2U, array->NumDirectInterfaces()); - EXPECT_TRUE(array->GetVTable() != NULL); + EXPECT_TRUE(array->ShouldHaveEmbeddedImtAndVTable()); EXPECT_EQ(2, array->GetIfTableCount()); ASSERT_TRUE(array->GetIfTable() != NULL); mirror::Class* direct_interface0 = mirror::Class::GetDirectInterface(self, array, 0); @@ -213,7 +213,7 @@ class ClassLinkerTest : public CommonRuntimeTest { EXPECT_NE(0U, klass->NumDirectMethods()); } } - EXPECT_EQ(klass->IsInterface(), klass->GetVTable() == NULL); + EXPECT_EQ(klass->IsInterface(), !klass->HasVTable()); mirror::IfTable* iftable = klass->GetIfTable(); for (int i = 0; i < klass->GetIfTableCount(); i++) { mirror::Class* interface = iftable->GetInterface(i); diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h index 482ad47d5d1..bf663ee7434 100644 --- a/runtime/entrypoints/entrypoint_utils-inl.h +++ b/runtime/entrypoints/entrypoint_utils-inl.h @@ -398,26 +398,26 @@ static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, case kDirect: return resolved_method; case kVirtual: { - mirror::ObjectArray* vtable = (*this_object)->GetClass()->GetVTable(); + mirror::Class* klass = (*this_object)->GetClass(); uint16_t vtable_index = resolved_method->GetMethodIndex(); if (access_check && - (vtable == nullptr || vtable_index >= static_cast(vtable->GetLength()))) { + (!klass->HasVTable() || + vtable_index >= static_cast(klass->GetVTableLength()))) { // Behavior to agree with that of the verifier. ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), resolved_method->GetName(), resolved_method->GetSignature()); return nullptr; // Failure. } - DCHECK(vtable != nullptr); - return vtable->GetWithoutChecks(vtable_index); + DCHECK(klass->HasVTable()) << PrettyClass(klass); + return klass->GetVTableEntry(vtable_index); } case kSuper: { mirror::Class* super_class = (*referrer)->GetDeclaringClass()->GetSuperClass(); uint16_t vtable_index = resolved_method->GetMethodIndex(); - mirror::ObjectArray* vtable; if (access_check) { // Check existence of super class. - vtable = (super_class != nullptr) ? super_class->GetVTable() : nullptr; - if (vtable == nullptr || vtable_index >= static_cast(vtable->GetLength())) { + if (super_class == nullptr || !super_class->HasVTable() || + vtable_index >= static_cast(super_class->GetVTableLength())) { // Behavior to agree with that of the verifier. ThrowNoSuchMethodError(type, resolved_method->GetDeclaringClass(), resolved_method->GetName(), resolved_method->GetSignature()); @@ -426,10 +426,9 @@ static inline mirror::ArtMethod* FindMethodFromCode(uint32_t method_idx, } else { // Super class must exist. DCHECK(super_class != nullptr); - vtable = super_class->GetVTable(); } - DCHECK(vtable != nullptr); - return vtable->GetWithoutChecks(vtable_index); + DCHECK(super_class->HasVTable()); + return super_class->GetVTableEntry(vtable_index); } case kInterface: { uint32_t imt_index = resolved_method->GetDexMethodIndex() % mirror::Class::kImtSize; @@ -565,11 +564,11 @@ static inline mirror::ArtMethod* FindMethodFast(uint32_t method_idx, } else if (is_direct) { return resolved_method; } else if (type == kSuper) { - return referrer->GetDeclaringClass()->GetSuperClass()->GetVTable()-> - Get(resolved_method->GetMethodIndex()); + return referrer->GetDeclaringClass()->GetSuperClass() + ->GetVTableEntry(resolved_method->GetMethodIndex()); } else { DCHECK(type == kVirtual); - return this_object->GetClass()->GetVTable()->Get(resolved_method->GetMethodIndex()); + return this_object->GetClass()->GetVTableEntry(resolved_method->GetMethodIndex()); } } diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index db42eb06f1a..c264157a13f 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -140,7 +140,8 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, return false; } const uint32_t vtable_idx = (is_range) ? inst->VRegB_3rc() : inst->VRegB_35c(); - ArtMethod* const method = receiver->GetClass()->GetVTable()->GetWithoutChecks(vtable_idx); + CHECK(receiver->GetClass()->ShouldHaveEmbeddedImtAndVTable()); + ArtMethod* const method = receiver->GetClass()->GetEmbeddedVTableEntry(vtable_idx); if (UNLIKELY(method == nullptr)) { CHECK(self->IsExceptionPending()); result->SetJ(0); diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 1fa680d1483..f50fa6e3fc6 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -129,12 +129,11 @@ ArtMethod* ArtMethod::FindOverriddenMethod() { Class* declaring_class = GetDeclaringClass(); Class* super_class = declaring_class->GetSuperClass(); uint16_t method_index = GetMethodIndex(); - ObjectArray* super_class_vtable = super_class->GetVTable(); ArtMethod* result = NULL; // Did this method override a super class method? If so load the result from the super class' // vtable - if (super_class_vtable != NULL && method_index < super_class_vtable->GetLength()) { - result = super_class_vtable->Get(method_index); + if (super_class->HasVTable() && method_index < super_class->GetVTableLength()) { + result = super_class->GetVTableEntry(method_index); } else { // Method didn't override superclass method so search interfaces if (IsProxyMethod()) { diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index 329a9848426..c3754d7967e 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -161,6 +161,37 @@ inline void Class::SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) { CHECK(method == GetImTable()->Get(i)); } +inline bool Class::HasVTable() { + return (GetVTable() != nullptr) || ShouldHaveEmbeddedImtAndVTable(); +} + +inline int32_t Class::GetVTableLength() { + if (ShouldHaveEmbeddedImtAndVTable()) { + return GetEmbeddedVTableLength(); + } + return (GetVTable() != nullptr) ? GetVTable()->GetLength() : 0; +} + +inline ArtMethod* Class::GetVTableEntry(uint32_t i) { + if (ShouldHaveEmbeddedImtAndVTable()) { + return GetEmbeddedVTableEntry(i); + } + return (GetVTable() != nullptr) ? GetVTable()->Get(i) : nullptr; +} + +inline int32_t Class::GetEmbeddedVTableLength() { + return GetField32(EmbeddedVTableLengthOffset()); +} + +inline void Class::SetEmbeddedVTableLength(int32_t len) { + SetField32(EmbeddedVTableLengthOffset(), len); +} + +inline ArtMethod* Class::GetEmbeddedVTableEntry(uint32_t i) { + uint32_t offset = EmbeddedVTableOffset().Uint32Value() + i * sizeof(VTableEntry); + return GetFieldObject(MemberOffset(offset)); +} + inline void Class::SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) { uint32_t offset = EmbeddedVTableOffset().Uint32Value() + i * sizeof(VTableEntry); SetFieldObject(MemberOffset(offset), method); @@ -340,12 +371,12 @@ inline ArtMethod* Class::FindVirtualMethodForVirtual(ArtMethod* method) { DCHECK(!method->GetDeclaringClass()->IsInterface() || method->IsMiranda()); // The argument method may from a super class. // Use the index to a potentially overridden one for this instance's class. - return GetVTable()->Get(method->GetMethodIndex()); + return GetVTableEntry(method->GetMethodIndex()); } inline ArtMethod* Class::FindVirtualMethodForSuper(ArtMethod* method) { DCHECK(!method->GetDeclaringClass()->IsInterface()); - return GetSuperClass()->GetVTable()->Get(method->GetMethodIndex()); + return GetSuperClass()->GetVTableEntry(method->GetMethodIndex()); } inline ArtMethod* Class::FindVirtualMethodForVirtualOrInterface(ArtMethod* method) { @@ -534,13 +565,19 @@ inline uint32_t Class::ComputeClassSize(bool has_embedded_tables, if (has_embedded_tables) { uint32_t embedded_imt_size = kImtSize * sizeof(ImTableEntry); uint32_t embedded_vtable_size = num_vtable_entries * sizeof(VTableEntry); - size += embedded_imt_size + embedded_vtable_size; + size += embedded_imt_size + + sizeof(int32_t) /* vtable len */ + + embedded_vtable_size; } // Space used by reference statics. size += num_ref_static_fields * sizeof(HeapReference); // Possible pad for alignment. - if (((size & 7) != 0) && (num_64bit_static_fields > 0) && (num_32bit_static_fields == 0)) { + if (((size & 7) != 0) && (num_64bit_static_fields > 0)) { size += sizeof(uint32_t); + if (num_32bit_static_fields != 0) { + // Shuffle one 32 bit static field forward. + num_32bit_static_fields--; + } } // Space used for primitive static fields. size += (num_32bit_static_fields * sizeof(uint32_t)) + @@ -574,7 +611,10 @@ inline void Class::VisitEmbeddedImtAndVTable(const Visitor& visitor) { pos += sizeof(ImTableEntry); } - count = ((GetVTable() != NULL) ? GetVTable()->GetLength() : 0); + // Skip vtable length. + pos += sizeof(int32_t); + + count = GetEmbeddedVTableLength(); for (size_t i = 0; i < count; ++i) { MemberOffset offset = MemberOffset(pos); visitor(this, offset, true); diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index be05fb8a9b7..036d904eace 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -828,10 +828,18 @@ void Class::PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_ } table = GetVTableDuringLinking(); - CHECK(table != nullptr); + CHECK(table != nullptr) << PrettyClass(this); + SetEmbeddedVTableLength(table->GetLength()); for (int32_t i = 0; i < table->GetLength(); i++) { SetEmbeddedVTableEntry(i, table->Get(i)); } + + SetImTable(nullptr); + // Keep java.lang.Object class's vtable around for since it's easier + // to be reused by array classes during their linking. + if (!IsObjectClass()) { + SetVTable(nullptr); + } } Class* Class::CopyOf(Thread* self, int32_t new_length) { diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h index 648bddeca59..0525abf27ab 100644 --- a/runtime/mirror/class.h +++ b/runtime/mirror/class.h @@ -692,18 +692,34 @@ class MANAGED Class FINAL : public Object { return MemberOffset(sizeof(Class)); } - static MemberOffset EmbeddedVTableOffset() { + static MemberOffset EmbeddedVTableLengthOffset() { return MemberOffset(sizeof(Class) + kImtSize * sizeof(mirror::Class::ImTableEntry)); } + static MemberOffset EmbeddedVTableOffset() { + return MemberOffset(sizeof(Class) + kImtSize * sizeof(mirror::Class::ImTableEntry) + sizeof(int32_t)); + } + bool ShouldHaveEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return IsInstantiable(); } + bool HasVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ArtMethod* GetEmbeddedImTableEntry(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void SetEmbeddedImTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + int32_t GetVTableLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ArtMethod* GetVTableEntry(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + int32_t GetEmbeddedVTableLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + void SetEmbeddedVTableLength(int32_t len) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ArtMethod* GetEmbeddedVTableEntry(uint32_t i) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void SetEmbeddedVTableEntry(uint32_t i, ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void PopulateEmbeddedImtAndVTable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 3214592cbc5..80c72835855 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3254,7 +3254,7 @@ mirror::ArtMethod* MethodVerifier::VerifyInvocationArgs(const Instruction* inst, return nullptr; } mirror::Class* super_klass = super.GetClass(); - if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) { + if (res_method->GetMethodIndex() >= super_klass->GetVTableLength()) { Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from " << PrettyMethod(dex_method_idx_, *dex_file_) << " to super " << super @@ -3279,20 +3279,21 @@ mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst VLOG(verifier) << "Failed to get mirror::Class* from '" << actual_arg_type << "'"; return nullptr; } - mirror::ObjectArray* vtable = nullptr; mirror::Class* klass = actual_arg_type.GetClass(); + mirror::Class* dispatch_class; if (klass->IsInterface()) { // Derive Object.class from Class.class.getSuperclass(). mirror::Class* object_klass = klass->GetClass()->GetSuperClass(); CHECK(object_klass->IsObjectClass()); - vtable = object_klass->GetVTable(); + dispatch_class = object_klass; } else { - vtable = klass->GetVTable(); + dispatch_class = klass; } - CHECK(vtable != nullptr) << PrettyDescriptor(klass); + CHECK(dispatch_class->HasVTable()) << PrettyDescriptor(dispatch_class); uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - CHECK_LT(static_cast(vtable_index), vtable->GetLength()) << PrettyDescriptor(klass); - mirror::ArtMethod* res_method = vtable->Get(vtable_index); + CHECK_LT(static_cast(vtable_index), dispatch_class->GetVTableLength()) + << PrettyDescriptor(klass); + mirror::ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index); CHECK(!Thread::Current()->IsExceptionPending()); return res_method; } diff --git a/test/100-reflect2/expected.txt b/test/100-reflect2/expected.txt index bed0689e828..1af4121d494 100644 --- a/test/100-reflect2/expected.txt +++ b/test/100-reflect2/expected.txt @@ -32,7 +32,7 @@ z (class java.lang.Character) 62 (class java.lang.Long) 14 (class java.lang.Short) [public java.lang.String(), java.lang.String(int,int,char[]), public java.lang.String(java.lang.String), public java.lang.String(java.lang.StringBuffer), public java.lang.String(java.lang.StringBuilder), public java.lang.String(byte[]), public java.lang.String(byte[],int), public java.lang.String(byte[],int,int), public java.lang.String(byte[],int,int,int), public java.lang.String(byte[],int,int,java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],int,int,java.nio.charset.Charset), public java.lang.String(byte[],java.lang.String) throws java.io.UnsupportedEncodingException, public java.lang.String(byte[],java.nio.charset.Charset), public java.lang.String(char[]), public java.lang.String(char[],int,int), public java.lang.String(int[],int,int)] -[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final char java.lang.String.REPLACEMENT_CHAR, private static final long java.lang.String.serialVersionUID] +[private final char[] java.lang.String.value, private final int java.lang.String.count, private int java.lang.String.hashCode, private final int java.lang.String.offset, private static final char[] java.lang.String.ASCII, public static final java.util.Comparator java.lang.String.CASE_INSENSITIVE_ORDER, private static final long java.lang.String.serialVersionUID, private static final char java.lang.String.REPLACEMENT_CHAR] [void java.lang.String._getChars(int,int,char[],int), public char java.lang.String.charAt(int), public int java.lang.String.codePointAt(int), public int java.lang.String.codePointBefore(int), public int java.lang.String.codePointCount(int,int), public volatile int java.lang.String.compareTo(java.lang.Object), public native int java.lang.String.compareTo(java.lang.String), public int java.lang.String.compareToIgnoreCase(java.lang.String), public java.lang.String java.lang.String.concat(java.lang.String), public boolean java.lang.String.contains(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.CharSequence), public boolean java.lang.String.contentEquals(java.lang.StringBuffer), public boolean java.lang.String.endsWith(java.lang.String), public boolean java.lang.String.equals(java.lang.Object), public boolean java.lang.String.equalsIgnoreCase(java.lang.String), public void java.lang.String.getBytes(int,int,byte[],int), public [B java.lang.String.getBytes(), public [B java.lang.String.getBytes(java.lang.String) throws java.io.UnsupportedEncodingException, public [B java.lang.String.getBytes(java.nio.charset.Charset), public void java.lang.String.getChars(int,int,char[],int), public int java.lang.String.hashCode(), public int java.lang.String.indexOf(int), public int java.lang.String.indexOf(int,int), public int java.lang.String.indexOf(java.lang.String), public int java.lang.String.indexOf(java.lang.String,int), public native java.lang.String java.lang.String.intern(), public boolean java.lang.String.isEmpty(), public int java.lang.String.lastIndexOf(int), public int java.lang.String.lastIndexOf(int,int), public int java.lang.String.lastIndexOf(java.lang.String), public int java.lang.String.lastIndexOf(java.lang.String,int), public int java.lang.String.length(), public boolean java.lang.String.matches(java.lang.String), public int java.lang.String.offsetByCodePoints(int,int), public boolean java.lang.String.regionMatches(int,java.lang.String,int,int), public boolean java.lang.String.regionMatches(boolean,int,java.lang.String,int,int), public java.lang.String java.lang.String.replace(char,char), public java.lang.String java.lang.String.replace(java.lang.CharSequence,java.lang.CharSequence), public java.lang.String java.lang.String.replaceAll(java.lang.String,java.lang.String), public java.lang.String java.lang.String.replaceFirst(java.lang.String,java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String), public [Ljava.lang.String; java.lang.String.split(java.lang.String,int), public boolean java.lang.String.startsWith(java.lang.String), public boolean java.lang.String.startsWith(java.lang.String,int), public java.lang.CharSequence java.lang.String.subSequence(int,int), public java.lang.String java.lang.String.substring(int), public java.lang.String java.lang.String.substring(int,int), public [C java.lang.String.toCharArray(), public java.lang.String java.lang.String.toLowerCase(), public java.lang.String java.lang.String.toLowerCase(java.util.Locale), public java.lang.String java.lang.String.toString(), public java.lang.String java.lang.String.toUpperCase(), public java.lang.String java.lang.String.toUpperCase(java.util.Locale), public java.lang.String java.lang.String.trim(), public static java.lang.String java.lang.String.copyValueOf(char[]), public static java.lang.String java.lang.String.copyValueOf(char[],int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.failedBoundsCheck(int,int,int), private native int java.lang.String.fastIndexOf(int,int), private char java.lang.String.foldCase(char), public static transient java.lang.String java.lang.String.format(java.lang.String,java.lang.Object[]), public static transient java.lang.String java.lang.String.format(java.util.Locale,java.lang.String,java.lang.Object[]), private java.lang.StringIndexOutOfBoundsException java.lang.String.indexAndLength(int), private static int java.lang.String.indexOf(java.lang.String,java.lang.String,int,int,char), private int java.lang.String.indexOfSupplementary(int,int), private int java.lang.String.lastIndexOfSupplementary(int,int), private java.lang.StringIndexOutOfBoundsException java.lang.String.startEndAndLength(int,int), public static java.lang.String java.lang.String.valueOf(char), public static java.lang.String java.lang.String.valueOf(double), public static java.lang.String java.lang.String.valueOf(float), public static java.lang.String java.lang.String.valueOf(int), public static java.lang.String java.lang.String.valueOf(long), public static java.lang.String java.lang.String.valueOf(java.lang.Object), public static java.lang.String java.lang.String.valueOf(boolean), public static java.lang.String java.lang.String.valueOf(char[]), public static java.lang.String java.lang.String.valueOf(char[],int,int)] [] [interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence] From 95e15588732684223ed7291e33af2e03e8e8ebfb Mon Sep 17 00:00:00 2001 From: Mingyao Yang Date: Tue, 22 Jul 2014 17:33:25 -0700 Subject: [PATCH 006/150] Fix build, missing spaces around =/<. (cherry picked from commit 1a12858eb15a14788478c4aca82c052bc84fcafa) Change-Id: Id2d276cd1fb8bb95c46ff5ceacc7cfe1f5acf192 --- runtime/class_linker.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index e0fa0f87257..c8d1f50934f 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3879,7 +3879,7 @@ bool ClassLinker::LinkVirtualMethods(Thread* self, Handle klass) return false; } int len = super_class->GetVTableLength(); - for (int i=0; iSet(i, super_class->GetVTableEntry(i)); } } else { From 320299b32285fe638ab46036759e374c905112ce Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 29 Jul 2014 14:45:33 -0700 Subject: [PATCH 007/150] ART: Bump oat version for embedded vtable Change-Id: Ieb84a8d9486a3d99d48e8395ef3634a365142f2f --- runtime/oat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/oat.cc b/runtime/oat.cc index 1421baffcf2..1cdcaeb94f1 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -23,7 +23,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '3', '7', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '3', '8', '\0' }; static size_t ComputeOatHeaderSize(const SafeMap* variable_data) { size_t estimate = 0U; From cd21f85f65197b7d52be1bd793d09cdfb05238ad Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 30 Jul 2014 18:59:05 -0700 Subject: [PATCH 008/150] ART: Fix verifier mishandling erroneous array component types The verifier must not assume that component types are not erroneous. Bug: 16661259 (cherry picked from commit aa910d5ef43256102809e397de305c23f1c315e6) Change-Id: I6d607310593ac337616581bfdff5eb29a8dd1b9d --- compiler/dex/quick/gen_common.cc | 6 ++++- runtime/mirror/dex_cache-inl.h | 8 ++++++ runtime/mirror/dex_cache.h | 7 ++---- runtime/verifier/reg_type_cache.cc | 39 +++++++++++++++++++----------- runtime/verifier/reg_type_cache.h | 2 ++ 5 files changed, 42 insertions(+), 20 deletions(-) diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index 2a77341d4c0..c668dff62eb 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -21,6 +21,7 @@ #include "mirror/array.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" +#include "mirror/object_reference.h" #include "verifier/method_verifier.h" #include @@ -813,6 +814,7 @@ void Mir2Lir::GenIGet(MIR* mir, int opt_flags, OpSize size, if (!SLOW_FIELD_PATH && field_info.FastGet() && (!field_info.IsVolatile() || SupportsVolatileLoadStore(load_size))) { RegisterClass reg_class = RegClassForFieldLoadStore(load_size, field_info.IsVolatile()); + // A load of the class will lead to an iget with offset 0. DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); rl_obj = LoadValue(rl_obj, kRefReg); GenNullCheck(rl_obj.reg, opt_flags); @@ -870,7 +872,9 @@ void Mir2Lir::GenIPut(MIR* mir, int opt_flags, OpSize size, if (!SLOW_FIELD_PATH && field_info.FastPut() && (!field_info.IsVolatile() || SupportsVolatileLoadStore(store_size))) { RegisterClass reg_class = RegClassForFieldLoadStore(store_size, field_info.IsVolatile()); - DCHECK_GE(field_info.FieldOffset().Int32Value(), 0); + // Dex code never writes to the class field. + DCHECK_GE(static_cast(field_info.FieldOffset().Int32Value()), + sizeof(mirror::HeapReference)); rl_obj = LoadValue(rl_obj, kRefReg); if (is_long_or_double) { rl_src = LoadValueWide(rl_src, reg_class); diff --git a/runtime/mirror/dex_cache-inl.h b/runtime/mirror/dex_cache-inl.h index 08cff999b15..d3fcb550c67 100644 --- a/runtime/mirror/dex_cache-inl.h +++ b/runtime/mirror/dex_cache-inl.h @@ -19,6 +19,8 @@ #include "dex_cache.h" +#include "base/logging.h" +#include "mirror/class.h" #include "runtime.h" namespace art { @@ -41,6 +43,12 @@ inline ArtMethod* DexCache::GetResolvedMethod(uint32_t method_idx) } } +inline void DexCache::SetResolvedType(uint32_t type_idx, Class* resolved) { + // TODO default transaction support. + DCHECK(resolved == nullptr || !resolved->IsErroneous()); + GetResolvedTypes()->Set(type_idx, resolved); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index bfd603a1859..2c5fbcdeed2 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -103,11 +103,8 @@ class MANAGED DexCache FINAL : public Object { return GetResolvedTypes()->Get(type_idx); } - void SetResolvedType(uint32_t type_idx, Class* resolved) ALWAYS_INLINE - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // TODO default transaction support. - GetResolvedTypes()->Set(type_idx, resolved); - } + void SetResolvedType(uint32_t type_idx, Class* resolved) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); ArtMethod* GetResolvedMethod(uint32_t method_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/verifier/reg_type_cache.cc b/runtime/verifier/reg_type_cache.cc index ff9edbbeafa..e4e20c5a147 100644 --- a/runtime/verifier/reg_type_cache.cc +++ b/runtime/verifier/reg_type_cache.cc @@ -186,7 +186,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descr } else { entry = new ReferenceType(klass, descriptor, entries_.size()); } - entries_.push_back(entry); + AddEntry(entry); return *entry; } else { // Class not resolved. // We tried loading the class and failed, this might get an exception raised @@ -199,7 +199,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descr } if (IsValidDescriptor(descriptor)) { RegType* entry = new UnresolvedReferenceType(descriptor, entries_.size()); - entries_.push_back(entry); + AddEntry(entry); return *entry; } else { // The descriptor is broken return the unknown type as there's nothing sensible that @@ -210,7 +210,7 @@ const RegType& RegTypeCache::From(mirror::ClassLoader* loader, const char* descr } const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* klass, bool precise) { - DCHECK(klass != nullptr); + DCHECK(klass != nullptr && !klass->IsErroneous()); if (klass->IsPrimitive()) { // Note: precise isn't used for primitive classes. A char is assignable to an int. All // primitive classes are final. @@ -230,7 +230,7 @@ const RegType& RegTypeCache::FromClass(const char* descriptor, mirror::Class* kl } else { entry = new ReferenceType(klass, descriptor, entries_.size()); } - entries_.push_back(entry); + AddEntry(entry); return *entry; } } @@ -340,7 +340,7 @@ const RegType& RegTypeCache::FromUnresolvedMerge(const RegType& left, const RegT } // Create entry. RegType* entry = new UnresolvedMergedType(left.GetId(), right.GetId(), this, entries_.size()); - entries_.push_back(entry); + AddEntry(entry); if (kIsDebugBuild) { UnresolvedMergedType* tmp_entry = down_cast(entry); std::set check_types = tmp_entry->GetMergedTypes(); @@ -364,7 +364,7 @@ const RegType& RegTypeCache::FromUnresolvedSuperClass(const RegType& child) { } } RegType* entry = new UnresolvedSuperClass(child.GetId(), this, entries_.size()); - entries_.push_back(entry); + AddEntry(entry); return *entry; } @@ -394,7 +394,7 @@ const UninitializedType& RegTypeCache::Uninitialized(const RegType& type, uint32 } entry = new UninitializedReferenceType(klass, descriptor, allocation_pc, entries_.size()); } - entries_.push_back(entry); + AddEntry(entry); return *entry; } @@ -436,7 +436,7 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { return Conflict(); } } - entries_.push_back(entry); + AddEntry(entry); return *entry; } @@ -499,7 +499,7 @@ const UninitializedType& RegTypeCache::UninitializedThisArgument(const RegType& } entry = new UninitializedThisReferenceType(klass, descriptor, entries_.size()); } - entries_.push_back(entry); + AddEntry(entry); return *entry; } @@ -518,7 +518,7 @@ const ConstantType& RegTypeCache::FromCat1NonSmallConstant(int32_t value, bool p } else { entry = new ImpreciseConstType(value, entries_.size()); } - entries_.push_back(entry); + AddEntry(entry); return *entry; } @@ -536,7 +536,7 @@ const ConstantType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) { } else { entry = new ImpreciseConstLoType(value, entries_.size()); } - entries_.push_back(entry); + AddEntry(entry); return *entry; } @@ -554,7 +554,7 @@ const ConstantType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) { } else { entry = new ImpreciseConstHiType(value, entries_.size()); } - entries_.push_back(entry); + AddEntry(entry); return *entry; } @@ -567,8 +567,15 @@ const RegType& RegTypeCache::GetComponentType(const RegType& array, mirror::Clas return FromDescriptor(loader, component.c_str(), false); } else { mirror::Class* klass = array.GetClass()->GetComponentType(); - return FromClass(klass->GetDescriptor().c_str(), klass, - klass->CannotBeAssignedFromOtherTypes()); + if (klass->IsErroneous()) { + // Arrays may have erroneous component types, use unresolved in that case. + // We assume that the primitive classes are not erroneous, so we know it is a + // reference type. + return FromDescriptor(loader, klass->GetDescriptor().c_str(), false); + } else { + return FromClass(klass->GetDescriptor().c_str(), klass, + klass->CannotBeAssignedFromOtherTypes()); + } } } @@ -587,5 +594,9 @@ void RegTypeCache::VisitRoots(RootCallback* callback, void* arg) { } } +void RegTypeCache::AddEntry(RegType* new_entry) { + entries_.push_back(new_entry); +} + } // namespace verifier } // namespace art diff --git a/runtime/verifier/reg_type_cache.h b/runtime/verifier/reg_type_cache.h index 70d5f0731cf..f42fdd1c161 100644 --- a/runtime/verifier/reg_type_cache.h +++ b/runtime/verifier/reg_type_cache.h @@ -154,6 +154,8 @@ class RegTypeCache { const ConstantType& FromCat1NonSmallConstant(int32_t value, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void AddEntry(RegType* new_entry); + template static Type* CreatePrimitiveTypeInstance(const std::string& descriptor) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); From 6b932180c768d2150386e4e6c8e415d935b25471 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Thu, 7 Aug 2014 18:07:18 +0100 Subject: [PATCH 009/150] Fix performance regression in OatFile::GetOatDexFile(). Try to avoid calculating the canonical location of the dex file if possible and when we have to calculate it, cache the lookup result for subsequent lookups. Bug: 16828525 Bug: 16859671 (cherry picked from commit 3f5838d7d0b9fc63db0ccc35c2ea05ed29264986) Change-Id: Ifd9a45dada2cc724382fd03c10f6437a6b71e666 --- runtime/base/mutex.h | 1 + runtime/oat_file.cc | 76 ++++++++++++++++++++++++++++++++------------ runtime/oat_file.h | 39 ++++++++++++++++++++--- 3 files changed, 92 insertions(+), 24 deletions(-) diff --git a/runtime/base/mutex.h b/runtime/base/mutex.h index 818f9d99082..d898e49df20 100644 --- a/runtime/base/mutex.h +++ b/runtime/base/mutex.h @@ -70,6 +70,7 @@ enum LockLevel { kMarkSweepMarkStackLock, kTransactionLogLock, kInternTableLock, + kOatFileSecondaryLookupLock, kDefaultMutexLevel, kMarkSweepLargeObjectLock, kPinTableLock, diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c4c6b10a7d9..971daf8bbfc 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -121,14 +121,12 @@ OatFile* OatFile::OpenElfFile(File* file, } OatFile::OatFile(const std::string& location) - : location_(location), begin_(NULL), end_(NULL), dlopen_handle_(NULL) { + : location_(location), begin_(NULL), end_(NULL), dlopen_handle_(NULL), + secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) { CHECK(!location_.empty()); } OatFile::~OatFile() { - for (auto it : oat_dex_files_) { - delete it.first.data(); - } STLDeleteValues(&oat_dex_files_); if (dlopen_handle_ != NULL) { dlclose(dlopen_handle_); @@ -304,19 +302,13 @@ bool OatFile::Setup(std::string* error_msg) { return false; } + // Create the OatDexFile and add it to the owning map indexed by the dex file location. OatDexFile* oat_dex_file = new OatDexFile(this, dex_file_location, dex_file_checksum, dex_file_pointer, methods_offsets_pointer); - - std::string dex_canonical_location_str = DexFile::GetDexCanonicalLocation(dex_file_location.c_str()); - // make a copy since we need to persist it as a key in the object's field. - int location_size = dex_canonical_location_str.size() + 1; - char* dex_canonical_location = new char[location_size ]; - strncpy(dex_canonical_location, dex_canonical_location_str.c_str(), location_size); - - StringPiece key(dex_canonical_location); + StringPiece key(oat_dex_file->GetDexFileLocation()); oat_dex_files_.Put(key, oat_dex_file); } return true; @@ -339,18 +331,62 @@ const byte* OatFile::End() const { const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, const uint32_t* dex_location_checksum, bool warn_if_not_found) const { - std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location); - - Table::const_iterator it = oat_dex_files_.find(dex_canonical_location); - if (it != oat_dex_files_.end()) { - const OatFile::OatDexFile* oat_dex_file = it->second; - if (dex_location_checksum == NULL || - oat_dex_file->GetDexFileLocationChecksum() == *dex_location_checksum) { - return oat_dex_file; + // NOTE: We assume here that the canonical location for a given dex_location never + // changes. If it does (i.e. some symlink used by the filename changes) we may return + // an incorrect OatDexFile. As long as we have a checksum to check, we shall return + // an identical file or fail; otherwise we may see some unpredictable failures. + + // TODO: Additional analysis of usage patterns to see if this can be simplified + // without any performance loss, for example by not doing the first lock-free lookup. + + const OatFile::OatDexFile* oat_dex_file = nullptr; + StringPiece key(dex_location); + // Try to find the key cheaply in the oat_dex_files_ map which holds dex locations + // directly mentioned in the oat file and doesn't require locking. + auto primary_it = oat_dex_files_.find(key); + if (primary_it != oat_dex_files_.end()) { + oat_dex_file = primary_it->second; + DCHECK(oat_dex_file != nullptr); + } else { + // This dex_location is not one of the dex locations directly mentioned in the + // oat file. The correct lookup is via the canonical location but first see in + // the secondary_oat_dex_files_ whether we've looked up this location before. + MutexLock mu(Thread::Current(), secondary_lookup_lock_); + auto secondary_lb = secondary_oat_dex_files_.lower_bound(key); + if (secondary_lb != secondary_oat_dex_files_.end() && key == secondary_lb->first) { + oat_dex_file = secondary_lb->second; // May be nullptr. + } else { + // We haven't seen this dex_location before, we must check the canonical location. + if (UNLIKELY(oat_dex_files_by_canonical_location_.empty())) { + // Lazily fill in the oat_dex_files_by_canonical_location_. + for (const auto& entry : oat_dex_files_) { + const std::string& dex_location = entry.second->GetDexFileLocation(); + string_cache_.emplace_back(DexFile::GetDexCanonicalLocation(dex_location.c_str())); + StringPiece canonical_location_key(string_cache_.back()); + oat_dex_files_by_canonical_location_.Put(canonical_location_key, entry.second); + } + } + std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location); + StringPiece canonical_key(dex_canonical_location); + auto canonical_it = oat_dex_files_by_canonical_location_.find(canonical_key); + if (canonical_it != oat_dex_files_by_canonical_location_.end()) { + oat_dex_file = canonical_it->second; + } // else keep nullptr. + + // Copy the key to the string_cache_ and store the result in secondary map. + string_cache_.emplace_back(key.data(), key.length()); + StringPiece key_copy(string_cache_.back()); + secondary_oat_dex_files_.PutBefore(secondary_lb, key_copy, oat_dex_file); } } + if (oat_dex_file != nullptr && + (dex_location_checksum == nullptr || + oat_dex_file->GetDexFileLocationChecksum() == *dex_location_checksum)) { + return oat_dex_file; + } if (warn_if_not_found) { + std::string dex_canonical_location = DexFile::GetDexCanonicalLocation(dex_location); std::string checksum(""); if (dex_location_checksum != NULL) { checksum = StringPrintf("0x%08x", *dex_location_checksum); diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 3ec2e843459..9710a2addd3 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -17,9 +17,11 @@ #ifndef ART_RUNTIME_OAT_FILE_H_ #define ART_RUNTIME_OAT_FILE_H_ +#include #include #include +#include "base/mutex.h" #include "base/stringpiece.h" #include "dex_file.h" #include "invoke_type.h" @@ -227,7 +229,8 @@ class OatFile { const OatDexFile* GetOatDexFile(const char* dex_location, const uint32_t* const dex_location_checksum, - bool exception_if_not_found = true) const; + bool exception_if_not_found = true) const + LOCKS_EXCLUDED(secondary_lookup_lock_); std::vector GetOatDexFiles() const; @@ -279,10 +282,38 @@ class OatFile { // dlopen handle during runtime. void* dlopen_handle_; - // NOTE: We use a StringPiece as the key type to avoid a memory allocation on every lookup - // with a const char* key. + // NOTE: We use a StringPiece as the key type to avoid a memory allocation on every + // lookup with a const char* key. The StringPiece doesn't own its backing storage, + // therefore we're using the OatDexFile::dex_file_location_ as the backing storage + // for keys in oat_dex_files_ and the string_cache_ entries for the backing storage + // of keys in secondary_oat_dex_files_ and oat_dex_files_by_canonical_location_. typedef SafeMap Table; - Table oat_dex_files_; + + // Map each plain dex file location retrieved from the oat file to its OatDexFile. + // This map doesn't change after it's constructed in Setup() and therefore doesn't + // need any locking and provides the cheapest dex file lookup for GetOatDexFile() + // for a very frequent use case. Never contains a nullptr value. + Table oat_dex_files_; // Owns the OatDexFile* values. + + // Lock guarding all members needed for secondary lookup in GetOatDexFile(). + mutable Mutex secondary_lookup_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; + + // If the primary oat_dex_files_ lookup fails, use a secondary map. This map stores + // the results of all previous secondary lookups, whether successful (non-null) or + // failed (null). If it doesn't contain an entry we need to calculate the canonical + // location and use oat_dex_files_by_canonical_location_. + mutable Table secondary_oat_dex_files_ GUARDED_BY(secondary_lookup_lock_); + + // Map the canonical location to an OatDexFile. This lazily constructed map is used + // when we're doing the secondary lookup for a given location for the first time. + mutable Table oat_dex_files_by_canonical_location_ GUARDED_BY(secondary_lookup_lock_); + + // Cache of strings. Contains the backing storage for keys in the secondary_oat_dex_files_ + // and the lazily initialized oat_dex_files_by_canonical_location_. + // NOTE: We're keeping references to contained strings in form of StringPiece and adding + // new strings to the end. The adding of a new element must not touch any previously stored + // elements. std::list<> and std::deque<> satisfy this requirement, std::vector<> doesn't. + mutable std::list string_cache_ GUARDED_BY(secondary_lookup_lock_); friend class OatClass; friend class OatDexFile; From 564db521eeccf24e44ce63966573294ca0863c6e Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Fri, 8 Aug 2014 16:19:44 +0100 Subject: [PATCH 010/150] Use the right instruction set for checking dex-cache staleness. We should use the instruction set we're asked to use, and not the instruction set of the current runtime. bug: 16876489 Change-Id: Ic2a30b7e7d23192b0189c1f868b73c5f4c7eb8f9 --- runtime/native/dalvik_system_DexFile.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 0af2c22cb60..8df3614b930 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -510,14 +510,14 @@ static jbyte IsDexOptNeededInternal(JNIEnv* env, const char* filename, bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate(); - InstructionSet isa = Runtime::Current()->GetInstructionSet(); jbyte dalvik_cache_decision = -1; // Lets try the cache first (since we want to load from there since thats where the relocated // versions will be). if (have_cache_filename && !force_system_only) { // We can use the dalvik-cache if we find a good file. dalvik_cache_decision = - IsDexOptNeededForFile(cache_filename, filename, isa); + IsDexOptNeededForFile(cache_filename, filename, + target_instruction_set); // We will only return DexOptNeeded if both the cache and system return it. if (dalvik_cache_decision != kDexoptNeeded && !require_system_version) { CHECK(!(dalvik_cache_decision == kPatchoatNeeded && !should_relocate_if_possible)) @@ -528,7 +528,8 @@ static jbyte IsDexOptNeededInternal(JNIEnv* env, const char* filename, } jbyte system_decision = - IsDexOptNeededForFile(odex_filename, filename, isa); + IsDexOptNeededForFile(odex_filename, filename, + target_instruction_set); CHECK(!(system_decision == kPatchoatNeeded && !should_relocate_if_possible)) << "May not return PatchoatNeeded when patching is disabled."; From aeb5f8142e8ea3f63636334a22b9a8b8a089eeb6 Mon Sep 17 00:00:00 2001 From: Zheng Xu Date: Tue, 12 Aug 2014 17:19:12 +0800 Subject: [PATCH 011/150] AArch64: Fix art_quick_string_compareto. Though __memcmp16() is implemented in the same module as the caller, it is still possible that the toolchain would put __memcmp16() into plt. In that case, IP registers can be trashed when loading the function address. Use x14/x15 to replace IP0/IP1. Bug: 16974467 (cherry picked from commit 62ddb328860e907eb76ccd3abed63ba75438fea8) Change-Id: I40e39d075860bc78624ce6ef8b4f8e33e57fc58c --- runtime/arch/arm64/quick_entrypoints_arm64.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 04be4a29991..62ae099c1c7 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1843,17 +1843,17 @@ ENTRY art_quick_string_compareto ret .Ldo_memcmp16: - mov xIP0, x0 // Save x0 and LR. __memcmp16 does not use these temps. - mov xIP1, xLR // TODO: Codify and check that? + mov x14, x0 // Save x0 and LR. __memcmp16 does not use these temps. + mov x15, xLR // TODO: Codify and check that? mov x0, x2 uxtw x2, w3 bl __memcmp16 - mov xLR, xIP1 // Restore LR. + mov xLR, x15 // Restore LR. cmp x0, #0 // Check the memcmp difference. - csel x0, x0, xIP0, ne // x0 := x0 != 0 ? xIP0(prev x0=length diff) : x1. + csel x0, x0, x14, ne // x0 := x0 != 0 ? x14(prev x0=length diff) : x1. ret END art_quick_string_compareto From ab9a0dbf3b63d517da5278b8298e6cd316e09f68 Mon Sep 17 00:00:00 2001 From: Dave Allison Date: Thu, 14 Aug 2014 16:54:09 +0000 Subject: [PATCH 012/150] Revert "Reduce stack usage for overflow checks" This reverts commit 63c051a540e6dfc806f656b88ac3a63e99395429. Change-Id: I282a048994fcd130fe73842b16c21680053c592f --- compiler/dex/quick/arm/call_arm.cc | 11 +- compiler/dex/quick/arm64/call_arm64.cc | 10 +- compiler/dex/quick/mips/call_mips.cc | 2 +- compiler/dex/quick/x86/call_x86.cc | 2 +- compiler/optimizing/code_generator_arm.cc | 2 +- compiler/optimizing/code_generator_x86.cc | 2 +- compiler/optimizing/code_generator_x86_64.cc | 2 +- compiler/utils/stack_checks.h | 7 +- runtime/arch/arm/asm_support_arm.h | 4 +- runtime/arch/arm/fault_handler_arm.cc | 31 +++- runtime/arch/arm/quick_entrypoints_arm.S | 25 +++ runtime/arch/arm64/asm_support_arm64.h | 4 +- runtime/arch/arm64/fault_handler_arm64.cc | 31 +++- runtime/arch/arm64/quick_entrypoints_arm64.S | 25 +++ runtime/arch/x86/asm_support_x86.h | 6 +- runtime/arch/x86/fault_handler_x86.cc | 44 ++--- runtime/arch/x86/quick_entrypoints_x86.S | 15 ++ runtime/arch/x86_64/asm_support_x86_64.h | 6 +- .../arch/x86_64/quick_entrypoints_x86_64.S | 12 ++ runtime/entrypoints/entrypoint_utils.cc | 5 - .../quick/quick_throw_entrypoints.cc | 4 - runtime/entrypoints_order_test.cc | 4 - runtime/instruction_set.cc | 9 +- runtime/mirror/art_method.cc | 5 - runtime/oat.cc | 2 +- runtime/reflection.cc | 49 ------ runtime/thread.cc | 151 ++++++++++-------- runtime/thread.h | 58 +------ test/004-InterfaceTest/src/Main.java | 4 +- test/004-SignalTest/signaltest.cc | 8 - 30 files changed, 269 insertions(+), 271 deletions(-) diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc index 4ba3c4b9bb8..b1339916f0e 100644 --- a/compiler/dex/quick/arm/call_arm.cc +++ b/compiler/dex/quick/arm/call_arm.cc @@ -354,14 +354,13 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kArm); + bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kArm); NewLIR0(kPseudoMethodEntry); - const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm); + const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm) - + Thread::kStackOverflowSignalReservedBytes; bool large_frame = (static_cast(frame_size_) > kStackOverflowReservedUsableBytes); - bool generate_explicit_stack_overflow_check = large_frame || - !cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks(); if (!skip_overflow_check) { - if (generate_explicit_stack_overflow_check) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { if (!large_frame) { /* Load stack limit */ LockTemp(rs_r12); @@ -400,7 +399,7 @@ void ArmMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { const int spill_size = spill_count * 4; const int frame_size_without_spills = frame_size_ - spill_size; if (!skip_overflow_check) { - if (generate_explicit_stack_overflow_check) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { class StackOverflowSlowPath : public LIRSlowPath { public: StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, bool restore_lr, size_t sp_displace) diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc index 0538c31fb82..28b747b5912 100644 --- a/compiler/dex/quick/arm64/call_arm64.cc +++ b/compiler/dex/quick/arm64/call_arm64.cc @@ -329,20 +329,16 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kArm64); + bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kArm64); NewLIR0(kPseudoMethodEntry); - const size_t kStackOverflowReservedUsableBytes = GetStackOverflowReservedBytes(kArm64); - const bool large_frame = static_cast(frame_size_) > kStackOverflowReservedUsableBytes; - bool generate_explicit_stack_overflow_check = large_frame || - !cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks(); const int spill_count = num_core_spills_ + num_fp_spills_; const int spill_size = (spill_count * kArm64PointerSize + 15) & ~0xf; // SP 16 byte alignment. const int frame_size_without_spills = frame_size_ - spill_size; if (!skip_overflow_check) { - if (generate_explicit_stack_overflow_check) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { // Load stack limit LoadWordDisp(rs_xSELF, Thread::StackEndOffset<8>().Int32Value(), rs_xIP1); } else { @@ -369,7 +365,7 @@ void Arm64Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) } if (!skip_overflow_check) { - if (generate_explicit_stack_overflow_check) { + if (!cu_->compiler_driver->GetCompilerOptions().GetImplicitStackOverflowChecks()) { class StackOverflowSlowPath: public LIRSlowPath { public: StackOverflowSlowPath(Mir2Lir* m2l, LIR* branch, size_t sp_displace) : diff --git a/compiler/dex/quick/mips/call_mips.cc b/compiler/dex/quick/mips/call_mips.cc index e8cb3561865..4577a4c9044 100644 --- a/compiler/dex/quick/mips/call_mips.cc +++ b/compiler/dex/quick/mips/call_mips.cc @@ -303,7 +303,7 @@ void MipsMir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) * We can safely skip the stack overflow check if we're * a leaf *and* our frame size < fudge factor. */ - bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, kMips); + bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, kMips); NewLIR0(kPseudoMethodEntry); RegStorage check_reg = AllocTemp(); RegStorage new_sp = AllocTemp(); diff --git a/compiler/dex/quick/x86/call_x86.cc b/compiler/dex/quick/x86/call_x86.cc index 996689a46c9..f5f86717b49 100644 --- a/compiler/dex/quick/x86/call_x86.cc +++ b/compiler/dex/quick/x86/call_x86.cc @@ -219,7 +219,7 @@ void X86Mir2Lir::GenEntrySequence(RegLocation* ArgLocs, RegLocation rl_method) { * a leaf *and* our frame size < fudge factor. */ InstructionSet isa = cu_->target64 ? kX86_64 : kX86; - bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !FrameNeedsStackCheck(frame_size_, isa); + const bool skip_overflow_check = mir_graph_->MethodIsLeaf() && !IsLargeFrame(frame_size_, isa); // If we doing an implicit stack overflow check, perform the load immediately // before the stack pointer is decremented and anything is saved. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 2c954a05023..eccc970042a 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -265,7 +265,7 @@ InstructionCodeGeneratorARM::InstructionCodeGeneratorARM(HGraph* graph, CodeGene codegen_(codegen) {} void CodeGeneratorARM::GenerateFrameEntry() { - bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kArm); + bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kArm); if (!skip_overflow_check) { if (kExplicitStackOverflowCheck) { SlowPathCode* slow_path = new (GetGraph()->GetArena()) StackOverflowCheckSlowPathARM(); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index f544d47c393..2264638110d 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -241,7 +241,7 @@ void CodeGeneratorX86::GenerateFrameEntry() { static const int kFakeReturnRegister = 8; core_spill_mask_ |= (1 << kFakeReturnRegister); - bool skip_overflow_check = IsLeafMethod() && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86); + bool skip_overflow_check = IsLeafMethod() && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86); if (!skip_overflow_check && !kExplicitStackOverflowCheck) { __ testl(EAX, Address(ESP, -static_cast(GetStackOverflowReservedBytes(kX86)))); RecordPcInfo(0); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index e1807dc7fde..2ff2a1710ef 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -213,7 +213,7 @@ void CodeGeneratorX86_64::GenerateFrameEntry() { Immediate(GetFrameSize() - kNumberOfPushedRegistersAtEntry * kX86_64WordSize)); bool skip_overflow_check = IsLeafMethod() - && !FrameNeedsStackCheck(GetFrameSize(), InstructionSet::kX86_64); + && !IsLargeFrame(GetFrameSize(), InstructionSet::kX86_64); if (!skip_overflow_check) { if (kExplicitStackOverflowCheck) { diff --git a/compiler/utils/stack_checks.h b/compiler/utils/stack_checks.h index ce010778086..63adbc2328a 100644 --- a/compiler/utils/stack_checks.h +++ b/compiler/utils/stack_checks.h @@ -33,9 +33,10 @@ static constexpr size_t kSmallFrameSize = 1 * KB; // Determine whether a frame is small or large, used in the decision on whether to elide a // stack overflow check on method entry. // -// A frame is considered large when it's above kLargeFrameSize. -static inline bool FrameNeedsStackCheck(size_t size, InstructionSet isa) { - return size >= kLargeFrameSize; +// A frame is considered large when it's either above kLargeFrameSize, or a quarter of the +// overflow-usable stack space. +static inline bool IsLargeFrame(size_t size, InstructionSet isa) { + return size >= kLargeFrameSize || size >= GetStackOverflowReservedBytes(isa) / 4; } } // namespace art diff --git a/runtime/arch/arm/asm_support_arm.h b/runtime/arch/arm/asm_support_arm.h index 330924ef0f6..ae30aee24d8 100644 --- a/runtime/arch/arm/asm_support_arm.h +++ b/runtime/arch/arm/asm_support_arm.h @@ -24,9 +24,9 @@ // Offset of field Thread::tls32_.thin_lock_thread_id verified in InitCpu #define THREAD_ID_OFFSET 12 // Offset of field Thread::tlsPtr_.card_table verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 120 +#define THREAD_CARD_TABLE_OFFSET 112 // Offset of field Thread::tlsPtr_.exception verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 124 +#define THREAD_EXCEPTION_OFFSET 116 #define FRAME_SIZE_SAVE_ALL_CALLEE_SAVE 176 #define FRAME_SIZE_REFS_ONLY_CALLEE_SAVE 32 diff --git a/runtime/arch/arm/fault_handler_arm.cc b/runtime/arch/arm/fault_handler_arm.cc index 28b69ecf278..be28544af00 100644 --- a/runtime/arch/arm/fault_handler_arm.cc +++ b/runtime/arch/arm/fault_handler_arm.cc @@ -35,7 +35,7 @@ namespace art { extern "C" void art_quick_throw_null_pointer_exception(); -extern "C" void art_quick_throw_stack_overflow(); +extern "C" void art_quick_throw_stack_overflow_from_signal(); extern "C" void art_quick_implicit_suspend(); // Get the size of a thumb2 instruction in bytes. @@ -194,19 +194,40 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kArm); + Thread* self = reinterpret_cast(sc->arm_r9); + CHECK_EQ(self, Thread::Current()); + uintptr_t pregion = reinterpret_cast(self->GetStackEnd()) - + Thread::kStackOverflowProtectedSize; + // Check that the fault address is the value expected for a stack overflow. if (fault_addr != overflow_addr) { VLOG(signals) << "Not a stack overflow"; return false; } - VLOG(signals) << "Stack overflow found"; + // We know this is a stack overflow. We need to move the sp to the overflow region + // that exists below the protected region. Determine the address of the next + // available valid address below the protected region. + uintptr_t prevsp = sp; + sp = pregion; + VLOG(signals) << "setting sp to overflow region at " << std::hex << sp; + + // Since the compiler puts the implicit overflow + // check before the callee save instructions, the SP is already pointing to + // the previous frame. + VLOG(signals) << "previous frame: " << std::hex << prevsp; + + // Now establish the stack pointer for the signal return. + sc->arm_sp = prevsp; + + // Tell the stack overflow code where the new stack pointer should be. + sc->arm_ip = sp; // aka r12 - // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from. + // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. // The value of LR must be the same as it was when we entered the code that // caused this fault. This will be inserted into a callee save frame by - // the function to which this handler returns (art_quick_throw_stack_overflow). - sc->arm_pc = reinterpret_cast(art_quick_throw_stack_overflow); + // the function to which this handler returns (art_quick_throw_stack_overflow_from_signal). + sc->arm_pc = reinterpret_cast(art_quick_throw_stack_overflow_from_signal); // The kernel will now return to the address in sc->arm_pc. return true; diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index dd1f04a9b4a..6c63a1afc12 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -235,6 +235,31 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFr */ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFromCode + /* + * Invoke stack overflow exception from signal handler. + * On entry: + * r9: thread + * sp: address of last known frame + * r12: address of next valid SP below protected region in stack + * + * This is deceptively simple but hides some complexity. It is called in the case of + * a stack overflow condition during implicit checks. The signal handler has been + * called by the kernel due to a load from the protected stack region. The handler + * works out the address of the previous frame and passes this in SP. However there + * is a piece of memory somewhere below the current SP that is not accessible (the + * memory that caused the signal). The signal handler works out the next + * accessible value of SP and passes this in r12. This code then sets up the SP + * to be this new value and calls the code to create and throw the stack overflow + * exception. + */ +ENTRY art_quick_throw_stack_overflow_from_signal + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + mov r0, r9 @ pass Thread::Current + mov r1, sp @ pass SP + mov sp, r12 @ move SP down to below protected region. + b artThrowStackOverflowFromCode @ artThrowStackOverflowFromCode(Thread*, SP) +END art_quick_throw_stack_overflow_from_signal + /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/r0 with the target Method*, arg0/r0 will contain diff --git a/runtime/arch/arm64/asm_support_arm64.h b/runtime/arch/arm64/asm_support_arm64.h index a9264499648..7f0f56f2747 100644 --- a/runtime/arch/arm64/asm_support_arm64.h +++ b/runtime/arch/arm64/asm_support_arm64.h @@ -30,9 +30,9 @@ // Offset of field Thread::suspend_count_ #define THREAD_FLAGS_OFFSET 0 // Offset of field Thread::card_table_ -#define THREAD_CARD_TABLE_OFFSET 120 +#define THREAD_CARD_TABLE_OFFSET 112 // Offset of field Thread::exception_ -#define THREAD_EXCEPTION_OFFSET 128 +#define THREAD_EXCEPTION_OFFSET 120 // Offset of field Thread::thin_lock_thread_id_ #define THREAD_ID_OFFSET 12 diff --git a/runtime/arch/arm64/fault_handler_arm64.cc b/runtime/arch/arm64/fault_handler_arm64.cc index b5948cbc9eb..3a7e6896a15 100644 --- a/runtime/arch/arm64/fault_handler_arm64.cc +++ b/runtime/arch/arm64/fault_handler_arm64.cc @@ -27,7 +27,7 @@ #include "thread.h" #include "thread-inl.h" -extern "C" void art_quick_throw_stack_overflow(); +extern "C" void art_quick_throw_stack_overflow_from_signal(); extern "C" void art_quick_throw_null_pointer_exception(); extern "C" void art_quick_implicit_suspend(); @@ -157,19 +157,40 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kArm64); + Thread* self = reinterpret_cast(sc->regs[art::arm64::TR]); + CHECK_EQ(self, Thread::Current()); + uintptr_t pregion = reinterpret_cast(self->GetStackEnd()) - + Thread::kStackOverflowProtectedSize; + // Check that the fault address is the value expected for a stack overflow. if (fault_addr != overflow_addr) { VLOG(signals) << "Not a stack overflow"; return false; } - VLOG(signals) << "Stack overflow found"; + // We know this is a stack overflow. We need to move the sp to the overflow region + // that exists below the protected region. Determine the address of the next + // available valid address below the protected region. + uintptr_t prevsp = sp; + sp = pregion; + VLOG(signals) << "setting sp to overflow region at " << std::hex << sp; + + // Since the compiler puts the implicit overflow + // check before the callee save instructions, the SP is already pointing to + // the previous frame. + VLOG(signals) << "previous frame: " << std::hex << prevsp; + + // Now establish the stack pointer for the signal return. + sc->sp = prevsp; + + // Tell the stack overflow code where the new stack pointer should be. + sc->regs[art::arm64::IP0] = sp; // aka x16 - // Now arrange for the signal handler to return to art_quick_throw_stack_overflow. + // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. // The value of LR must be the same as it was when we entered the code that // caused this fault. This will be inserted into a callee save frame by - // the function to which this handler returns (art_quick_throw_stack_overflow). - sc->pc = reinterpret_cast(art_quick_throw_stack_overflow); + // the function to which this handler returns (art_quick_throw_stack_overflow_from_signal). + sc->pc = reinterpret_cast(art_quick_throw_stack_overflow_from_signal); // The kernel will now return to the address in sc->pc. return true; diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index ab9035ae45c..62ae099c1c7 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -435,6 +435,31 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFr */ ONE_ARG_RUNTIME_EXCEPTION art_quick_throw_no_such_method, artThrowNoSuchMethodFromCode + /* + * Invoke stack overflow exception from signal handler. + * On entry: + * xSELF: thread + * SP: address of last known frame + * IP0: address of next valid SP below protected region in stack + * + * This is deceptively simple but hides some complexity. It is called in the case of + * a stack overflow condition during implicit checks. The signal handler has been + * called by the kernel due to a load from the protected stack region. The handler + * works out the address of the previous frame and passes this in SP. However there + * is a piece of memory somewhere below the current SP that is not accessible (the + * memory that caused the signal). The signal handler works out the next + * accessible value of SP and passes this in x16/IP0. This code then sets up the SP + * to be this new value and calls the code to create and throw the stack overflow + * exception. + */ +ENTRY art_quick_throw_stack_overflow_from_signal + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + mov x0, xSELF // pass Thread::Current + mov x1, sp // pass SP + mov sp, xIP0 // move SP down to below protected region. + b artThrowStackOverflowFromCode // artThrowStackOverflowFromCode(Thread*, SP) +END art_quick_throw_stack_overflow_from_signal + /* * All generated callsites for interface invokes and invocation slow paths will load arguments * as usual - except instead of loading arg0/x0 with the target Method*, arg0/x0 will contain diff --git a/runtime/arch/x86/asm_support_x86.h b/runtime/arch/x86/asm_support_x86.h index c9f5a25eea9..531ed77ef4e 100644 --- a/runtime/arch/x86/asm_support_x86.h +++ b/runtime/arch/x86/asm_support_x86.h @@ -20,11 +20,11 @@ #include "asm_support.h" // Offset of field Thread::self_ verified in InitCpu -#define THREAD_SELF_OFFSET 156 +#define THREAD_SELF_OFFSET 148 // Offset of field Thread::card_table_ verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 120 +#define THREAD_CARD_TABLE_OFFSET 112 // Offset of field Thread::exception_ verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 124 +#define THREAD_EXCEPTION_OFFSET 116 // Offset of field Thread::thin_lock_thread_id_ verified in InitCpu #define THREAD_ID_OFFSET 12 diff --git a/runtime/arch/x86/fault_handler_x86.cc b/runtime/arch/x86/fault_handler_x86.cc index c143c5d8258..8b6c9b1ae3d 100644 --- a/runtime/arch/x86/fault_handler_x86.cc +++ b/runtime/arch/x86/fault_handler_x86.cc @@ -28,29 +28,16 @@ #if defined(__APPLE__) #define ucontext __darwin_ucontext - -#if defined(__x86_64__) -// 64 bit mac build. -#define CTX_ESP uc_mcontext->__ss.__rsp -#define CTX_EIP uc_mcontext->__ss.__rip -#define CTX_EAX uc_mcontext->__ss.__rax -#define CTX_METHOD uc_mcontext->__ss.__rdi -#else -// 32 bit mac build. #define CTX_ESP uc_mcontext->__ss.__esp #define CTX_EIP uc_mcontext->__ss.__eip #define CTX_EAX uc_mcontext->__ss.__eax #define CTX_METHOD uc_mcontext->__ss.__eax -#endif - #elif defined(__x86_64__) -// 64 bit linux build. #define CTX_ESP uc_mcontext.gregs[REG_RSP] #define CTX_EIP uc_mcontext.gregs[REG_RIP] #define CTX_EAX uc_mcontext.gregs[REG_RAX] #define CTX_METHOD uc_mcontext.gregs[REG_RDI] #else -// 32 bit linux build. #define CTX_ESP uc_mcontext.gregs[REG_ESP] #define CTX_EIP uc_mcontext.gregs[REG_EIP] #define CTX_EAX uc_mcontext.gregs[REG_EAX] @@ -63,18 +50,9 @@ namespace art { -#if defined(__APPLE__) && defined(__x86_64__) -// mac symbols have a prefix of _ on x86_64 -extern "C" void _art_quick_throw_null_pointer_exception(); -extern "C" void _art_quick_throw_stack_overflow_from_signal(); -extern "C" void _art_quick_test_suspend(); -#define EXT_SYM(sym) _ ## sym -#else extern "C" void art_quick_throw_null_pointer_exception(); -extern "C" void art_quick_throw_stack_overflow(); +extern "C" void art_quick_throw_stack_overflow_from_signal(); extern "C" void art_quick_test_suspend(); -#define EXT_SYM(sym) sym -#endif // Get the size of an instruction in bytes. // Return 0 if the instruction is not handled. @@ -275,7 +253,7 @@ bool NullPointerHandler::Action(int sig, siginfo_t* info, void* context) { *next_sp = retaddr; uc->CTX_ESP = reinterpret_cast(next_sp); - uc->CTX_EIP = reinterpret_cast(EXT_SYM(art_quick_throw_null_pointer_exception)); + uc->CTX_EIP = reinterpret_cast(art_quick_throw_null_pointer_exception); VLOG(signals) << "Generating null pointer exception"; return true; } @@ -349,7 +327,7 @@ bool SuspensionHandler::Action(int sig, siginfo_t* info, void* context) { *next_sp = retaddr; uc->CTX_ESP = reinterpret_cast(next_sp); - uc->CTX_EIP = reinterpret_cast(EXT_SYM(art_quick_test_suspend)); + uc->CTX_EIP = reinterpret_cast(art_quick_test_suspend); // Now remove the suspend trigger that caused this fault. Thread::Current()->RemoveSuspendTrigger(); @@ -382,20 +360,30 @@ bool StackOverflowHandler::Action(int sig, siginfo_t* info, void* context) { uintptr_t overflow_addr = sp - GetStackOverflowReservedBytes(kX86); #endif + Thread* self = Thread::Current(); + uintptr_t pregion = reinterpret_cast(self->GetStackEnd()) - + Thread::kStackOverflowProtectedSize; + // Check that the fault address is the value expected for a stack overflow. if (fault_addr != overflow_addr) { VLOG(signals) << "Not a stack overflow"; return false; } - VLOG(signals) << "Stack overflow found"; + // We know this is a stack overflow. We need to move the sp to the overflow region + // that exists below the protected region. Determine the address of the next + // available valid address below the protected region. + VLOG(signals) << "setting sp to overflow region at " << std::hex << pregion; // Since the compiler puts the implicit overflow // check before the callee save instructions, the SP is already pointing to // the previous frame. - // Now arrange for the signal handler to return to art_quick_throw_stack_overflow. - uc->CTX_EIP = reinterpret_cast(art_quick_throw_stack_overflow); + // Tell the stack overflow code where the new stack pointer should be. + uc->CTX_EAX = pregion; + + // Now arrange for the signal handler to return to art_quick_throw_stack_overflow_from_signal. + uc->CTX_EIP = reinterpret_cast(art_quick_throw_stack_overflow_from_signal); return true; } diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 75ec49deb0a..dc4019d2a43 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -174,6 +174,21 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode +// On entry to this function, EAX contains the ESP value for the overflow region. +DEFINE_FUNCTION art_quick_throw_stack_overflow_from_signal + // Here, the ESP is above the protected region. We need to create a + // callee save frame and then move ESP down to the overflow region. + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + mov %esp, %ecx // get current stack pointer + mov %eax, %esp // move ESP to the overflow region. + PUSH ecx // pass SP + pushl %fs:THREAD_SELF_OFFSET // pass Thread::Current() + CFI_ADJUST_CFA_OFFSET(4) + SETUP_GOT_NOSAVE // clobbers ebx (harmless here) + call PLT_SYMBOL(artThrowStackOverflowFromCode) // artThrowStackOverflowFromCode(Thread*, SP) + int3 // unreached +END_FUNCTION art_quick_throw_stack_overflow_from_signal + /* * Called by managed code, saves callee saves and then calls artThrowException * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception. diff --git a/runtime/arch/x86_64/asm_support_x86_64.h b/runtime/arch/x86_64/asm_support_x86_64.h index 40958dcdf11..c3637ef7aed 100644 --- a/runtime/arch/x86_64/asm_support_x86_64.h +++ b/runtime/arch/x86_64/asm_support_x86_64.h @@ -28,11 +28,11 @@ #define RUNTIME_REF_AND_ARGS_CALLEE_SAVE_FRAME_OFFSET 16 // Offset of field Thread::self_ verified in InitCpu -#define THREAD_SELF_OFFSET 192 +#define THREAD_SELF_OFFSET 184 // Offset of field Thread::card_table_ verified in InitCpu -#define THREAD_CARD_TABLE_OFFSET 120 +#define THREAD_CARD_TABLE_OFFSET 112 // Offset of field Thread::exception_ verified in InitCpu -#define THREAD_EXCEPTION_OFFSET 128 +#define THREAD_EXCEPTION_OFFSET 120 // Offset of field Thread::thin_lock_thread_id_ verified in InitCpu #define THREAD_ID_OFFSET 12 diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 48bc240dab8..f021ada6bac 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -284,6 +284,18 @@ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_div_zero, artThrowDivZeroFromCode */ NO_ARG_RUNTIME_EXCEPTION art_quick_throw_stack_overflow, artThrowStackOverflowFromCode +// On entry to this function, RAX contains the ESP value for the overflow region. +DEFINE_FUNCTION art_quick_throw_stack_overflow_from_signal + // Here, the RSP is above the protected region. We need to create a + // callee save frame and then move RSP down to the overflow region. + SETUP_SAVE_ALL_CALLEE_SAVE_FRAME // save all registers as basis for long jump context + mov %rsp, %rsi // get current stack pointer, pass SP as second arg + mov %rax, %rsp // move RSP to the overflow region. + mov %gs:THREAD_SELF_OFFSET, %rdi // pass Thread::Current() as first arg + call PLT_SYMBOL(artThrowStackOverflowFromCode) // artThrowStackOverflowFromCode(Thread*, SP) + int3 // unreached +END_FUNCTION art_quick_throw_stack_overflow_from_signal + /* * Called by managed code, saves callee saves and then calls artThrowException * that will place a mock Method* at the bottom of the stack. Arg1 holds the exception. diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 2767dc4893d..be3895a7265 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -212,11 +212,6 @@ void ThrowStackOverflowError(Thread* self) { bool explicit_overflow_check = Runtime::Current()->ExplicitStackOverflowChecks(); self->ResetDefaultStackEnd(!explicit_overflow_check); // Return to default stack size. - - // And restore protection if implicit checks are on. - if (!explicit_overflow_check) { - self->ProtectStack(); - } } void CheckReferenceResult(mirror::Object* o, Thread* self) { diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc index 13decc80684..879010e36e4 100644 --- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc @@ -58,10 +58,8 @@ extern "C" void artThrowNullPointerExceptionFromCode(Thread* self, StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); - self->NoteSignalBeingHandled(); ThrowLocation throw_location = self->GetCurrentLocationForThrow(); ThrowNullPointerExceptionFromDexPC(throw_location); - self->NoteSignalHandlerDone(); self->QuickDeliverException(); } @@ -85,9 +83,7 @@ extern "C" void artThrowArrayBoundsFromCode(int index, int length, Thread* self, extern "C" void artThrowStackOverflowFromCode(Thread* self, StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { FinishCalleeSaveFrameSetup(self, sp, Runtime::kSaveAll); - self->NoteSignalBeingHandled(); ThrowStackOverflowError(self); - self->NoteSignalHandlerDone(); self->QuickDeliverException(); } diff --git a/runtime/entrypoints_order_test.cc b/runtime/entrypoints_order_test.cc index f572d27c393..ae1b94fa902 100644 --- a/runtime/entrypoints_order_test.cc +++ b/runtime/entrypoints_order_test.cc @@ -70,10 +70,6 @@ class EntrypointsOrderTest : public CommonRuntimeTest { EXPECT_OFFSET_DIFFP(Thread, tls32_, daemon, throwing_OutOfMemoryError, 4); EXPECT_OFFSET_DIFFP(Thread, tls32_, throwing_OutOfMemoryError, no_thread_suspension, 4); EXPECT_OFFSET_DIFFP(Thread, tls32_, no_thread_suspension, thread_exit_check_count, 4); - EXPECT_OFFSET_DIFFP(Thread, tls32_, thread_exit_check_count, - is_exception_reported_to_instrumentation_, 4); - EXPECT_OFFSET_DIFFP(Thread, tls32_, is_exception_reported_to_instrumentation_, - handling_signal_, 4); // TODO: Better connection. Take alignment into account. EXPECT_OFFSET_DIFF_GT3(Thread, tls32_.thread_exit_check_count, tls64_.trace_clock_base, 4, diff --git a/runtime/instruction_set.cc b/runtime/instruction_set.cc index d8a38f42d70..d7e358ce964 100644 --- a/runtime/instruction_set.cc +++ b/runtime/instruction_set.cc @@ -87,10 +87,11 @@ size_t GetInstructionSetAlignment(InstructionSet isa) { static constexpr size_t kDefaultStackOverflowReservedBytes = 16 * KB; static constexpr size_t kMipsStackOverflowReservedBytes = kDefaultStackOverflowReservedBytes; -static constexpr size_t kArmStackOverflowReservedBytes = 8 * KB; -static constexpr size_t kArm64StackOverflowReservedBytes = 8 * KB; -static constexpr size_t kX86StackOverflowReservedBytes = 8 * KB; -static constexpr size_t kX86_64StackOverflowReservedBytes = 8 * KB; +// TODO: Lower once implicit stack-overflow checks can work with less than 16K. +static constexpr size_t kArmStackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB; +static constexpr size_t kArm64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB; +static constexpr size_t kX86StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB; +static constexpr size_t kX86_64StackOverflowReservedBytes = (kIsDebugBuild ? 16 : 16) * KB; size_t GetStackOverflowReservedBytes(InstructionSet isa) { switch (isa) { diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 8206cd40656..84137638349 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -283,11 +283,6 @@ uint32_t ArtMethod::FindCatchBlock(Handle h_this, Handle excep void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* result, const char* shorty) { - if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { - ThrowStackOverflowError(self); - return; - } - if (kIsDebugBuild) { self->AssertThreadSuspensionIsAllowable(); CHECK_EQ(kRunnable, self->GetState()); diff --git a/runtime/oat.cc b/runtime/oat.cc index ede108cd194..0a8c35b561f 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -23,7 +23,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '3', '9', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '3', '8', '\0' }; static size_t ComputeOatHeaderSize(const SafeMap* variable_data) { size_t estimate = 0U; diff --git a/runtime/reflection.cc b/runtime/reflection.cc index 4cd61a53052..0d38d3d9363 100644 --- a/runtime/reflection.cc +++ b/runtime/reflection.cc @@ -420,14 +420,6 @@ static void InvokeWithArgArray(const ScopedObjectAccessAlreadyRunnable& soa, JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, va_list args) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // We want to make sure that the stack is not within a small distance from the - // protected region in case we are calling into a leaf function whose stack - // check has been elided. - if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { - ThrowStackOverflowError(soa.Self()); - return JValue(); - } - mirror::ArtMethod* method = soa.DecodeMethod(mid); mirror::Object* receiver = method->IsStatic() ? nullptr : soa.Decode(obj); uint32_t shorty_len = 0; @@ -441,14 +433,6 @@ JValue InvokeWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject o JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver, jmethodID mid, jvalue* args) { - // We want to make sure that the stack is not within a small distance from the - // protected region in case we are calling into a leaf function whose stack - // check has been elided. - if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { - ThrowStackOverflowError(soa.Self()); - return JValue(); - } - mirror::ArtMethod* method = soa.DecodeMethod(mid); uint32_t shorty_len = 0; const char* shorty = method->GetShorty(&shorty_len); @@ -461,14 +445,6 @@ JValue InvokeWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::O JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnable& soa, mirror::Object* receiver, jmethodID mid, jvalue* args) { - // We want to make sure that the stack is not within a small distance from the - // protected region in case we are calling into a leaf function whose stack - // check has been elided. - if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { - ThrowStackOverflowError(soa.Self()); - return JValue(); - } - mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid)); uint32_t shorty_len = 0; const char* shorty = method->GetShorty(&shorty_len); @@ -481,14 +457,6 @@ JValue InvokeVirtualOrInterfaceWithJValues(const ScopedObjectAccessAlreadyRunnab JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnable& soa, jobject obj, jmethodID mid, va_list args) { - // We want to make sure that the stack is not within a small distance from the - // protected region in case we are calling into a leaf function whose stack - // check has been elided. - if (UNLIKELY(__builtin_frame_address(0) < soa.Self()->GetStackEnd())) { - ThrowStackOverflowError(soa.Self()); - return JValue(); - } - mirror::Object* receiver = soa.Decode(obj); mirror::ArtMethod* method = FindVirtualMethod(receiver, soa.DecodeMethod(mid)); uint32_t shorty_len = 0; @@ -502,14 +470,6 @@ JValue InvokeVirtualOrInterfaceWithVarArgs(const ScopedObjectAccessAlreadyRunnab void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg_offset, MethodHelper& mh, JValue* result) { - // We want to make sure that the stack is not within a small distance from the - // protected region in case we are calling into a leaf function whose stack - // check has been elided. - if (UNLIKELY(__builtin_frame_address(0) < self->GetStackEnd())) { - ThrowStackOverflowError(self); - return; - } - ArgArray arg_array(mh.GetShorty(), mh.GetShortyLength()); arg_array.BuildArgArrayFromFrame(shadow_frame, arg_offset); shadow_frame->GetMethod()->Invoke(self, arg_array.GetArray(), arg_array.GetNumBytes(), result, @@ -518,15 +478,6 @@ void InvokeWithShadowFrame(Thread* self, ShadowFrame* shadow_frame, uint16_t arg jobject InvokeMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject javaMethod, jobject javaReceiver, jobject javaArgs, bool accessible) { - // We want to make sure that the stack is not within a small distance from the - // protected region in case we are calling into a leaf function whose stack - // check has been elided. - if (UNLIKELY(__builtin_frame_address(0) < - soa.Self()->GetStackEndForInterpreter(true))) { - ThrowStackOverflowError(soa.Self()); - return nullptr; - } - mirror::ArtMethod* m = mirror::ArtMethod::FromReflectedMethod(soa, javaMethod); mirror::Class* declaring_class = m->GetDeclaringClass(); diff --git a/runtime/thread.cc b/runtime/thread.cc index 4ce8c96eb58..18e28ea11c0 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -76,7 +76,8 @@ namespace art { bool Thread::is_started_ = false; pthread_key_t Thread::pthread_key_self_; ConditionVariable* Thread::resume_cond_ = nullptr; -const size_t Thread::kStackOverflowImplicitCheckSize = GetStackOverflowReservedBytes(kRuntimeISA); +const size_t Thread::kStackOverflowImplicitCheckSize = kStackOverflowProtectedSize + + GetStackOverflowReservedBytes(kRuntimeISA); static const char* kThreadNameDuringStartup = ""; @@ -237,48 +238,92 @@ static size_t FixStackSize(size_t stack_size) { byte dont_optimize_this; // Install a protected region in the stack. This is used to trigger a SIGSEGV if a stack -// overflow is detected. It is located right below the stack_begin_. +// overflow is detected. It is located right below the stack_end_. Just below that +// is the StackOverflow reserved region used when creating the StackOverflow +// exception. // -// There is a little complexity here that deserves a special mention. On some -// architectures, the stack created using a VM_GROWSDOWN flag +// There is a little complexity here that deserves a special mention. When running on the +// host (glibc), the process's main thread's stack is allocated with a special flag // to prevent memory being allocated when it's not needed. This flag makes the // kernel only allocate memory for the stack by growing down in memory. Because we // want to put an mprotected region far away from that at the stack top, we need // to make sure the pages for the stack are mapped in before we call mprotect. We do // this by reading every page from the stack bottom (highest address) to the stack top. // We then madvise this away. -void Thread::InstallImplicitProtection() { - byte* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize; - byte* stack_himem = tlsPtr_.stack_end; - byte* stack_top = reinterpret_cast(reinterpret_cast(&stack_himem) & +void Thread::InstallImplicitProtection(bool is_main_stack) { + byte* pregion = tlsPtr_.stack_end; + byte* stack_lowmem = tlsPtr_.stack_begin; + byte* stack_top = reinterpret_cast(reinterpret_cast(&pregion) & ~(kPageSize - 1)); // Page containing current top of stack. - // First remove the protection on the protected region as will want to read and - // write it. This may fail (on the first attempt when the stack is not mapped) - // but we ignore that. - UnprotectStack(); + const bool running_on_intel = (kRuntimeISA == kX86) || (kRuntimeISA == kX86_64); + + if (running_on_intel) { + // On Intel, we need to map in the main stack. This must be done by reading from the + // current stack pointer downwards as the stack is mapped using VM_GROWSDOWN + // in the kernel. Any access more than a page below the current SP will cause + // a segv. + if (is_main_stack) { + // First we need to unprotect the protected region because this may + // be called more than once for a particular stack and we will crash + // if we try to read the protected page. + mprotect(pregion - kStackOverflowProtectedSize, kStackOverflowProtectedSize, PROT_READ); + + // Read every page from the high address to the low. + for (byte* p = stack_top; p > stack_lowmem; p -= kPageSize) { + dont_optimize_this = *p; + } + } + } - // Map in the stack. This must be done by reading from the - // current stack pointer downwards as the stack may be mapped using VM_GROWSDOWN - // in the kernel. Any access more than a page below the current SP might cause - // a segv. + // Check and place a marker word at the lowest usable address in the stack. This + // is used to prevent a double protection. + constexpr uint32_t kMarker = 0xdadadada; + uintptr_t *marker = reinterpret_cast(pregion); + if (*marker == kMarker) { + // The region has already been set up. But on the main stack on the host we have + // removed the protected region in order to read the stack memory. We need to put + // this back again. + if (is_main_stack && running_on_intel) { + mprotect(pregion - kStackOverflowProtectedSize, kStackOverflowProtectedSize, PROT_NONE); + madvise(stack_lowmem, stack_top - stack_lowmem, MADV_DONTNEED); + } + return; + } + // Add marker so that we can detect a second attempt to do this. + *marker = kMarker; - // Read every page from the high address to the low. - for (byte* p = stack_top; p > pregion; p -= kPageSize) { - dont_optimize_this = *p; + if (!running_on_intel) { + // Running on !Intel, stacks are mapped cleanly. The protected region for the + // main stack just needs to be mapped in. We do this by writing one byte per page. + for (byte* p = pregion - kStackOverflowProtectedSize; p < pregion; p += kPageSize) { + *p = 0; + } } + pregion -= kStackOverflowProtectedSize; + VLOG(threads) << "installing stack protected region at " << std::hex << static_cast(pregion) << " to " << static_cast(pregion + kStackOverflowProtectedSize - 1); - // Protect the bottom of the stack to prevent read/write to it. - ProtectStack(); + + if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) { + LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. Reason:" + << strerror(errno) << kStackOverflowProtectedSize; + } // Tell the kernel that we won't be needing these pages any more. // NB. madvise will probably write zeroes into the memory (on linux it does). - uint32_t unwanted_size = stack_top - pregion - kPageSize; - madvise(pregion, unwanted_size, MADV_DONTNEED); + if (is_main_stack) { + if (running_on_intel) { + // On the host, it's the whole stack (minus a page to prevent overwrite of stack top). + madvise(stack_lowmem, stack_top - stack_lowmem - kPageSize, MADV_DONTNEED); + } else { + // On Android, just the protected region. + madvise(pregion, kStackOverflowProtectedSize, MADV_DONTNEED); + } + } } void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) { @@ -493,13 +538,7 @@ void Thread::InitStackHwm() { tlsPtr_.stack_begin = reinterpret_cast(read_stack_base); tlsPtr_.stack_size = read_stack_size; - // The minimum stack size we can cope with is the overflow reserved bytes (typically - // 8K) + the protected region size (4K) + another page (4K). Typically this will - // be 8+4+4 = 16K. The thread won't be able to do much with this stack even the GC takes - // between 8K and 12K. - uint32_t min_stack = GetStackOverflowReservedBytes(kRuntimeISA) + kStackOverflowProtectedSize - + 4 * KB; - if (read_stack_size <= min_stack) { + if (read_stack_size <= GetStackOverflowReservedBytes(kRuntimeISA)) { LOG(FATAL) << "Attempt to attach a thread with a too-small stack (" << read_stack_size << " bytes)"; } @@ -543,19 +582,20 @@ void Thread::InitStackHwm() { // Install the protected region if we are doing implicit overflow checks. if (implicit_stack_check) { - size_t guardsize; - pthread_attr_t attributes; - CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), "guard size query"); - CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, &guardsize), "guard size query"); - CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), "guard size query"); - // The thread might have protected region at the bottom. We need - // to install our own region so we need to move the limits - // of the stack to make room for it. - tlsPtr_.stack_begin += guardsize; - tlsPtr_.stack_end += guardsize; - tlsPtr_.stack_size -= guardsize; - - InstallImplicitProtection(); + if (is_main_thread) { + size_t guardsize; + pthread_attr_t attributes; + CHECK_PTHREAD_CALL(pthread_attr_init, (&attributes), "guard size query"); + CHECK_PTHREAD_CALL(pthread_attr_getguardsize, (&attributes, &guardsize), "guard size query"); + CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attributes), "guard size query"); + // The main thread might have protected region at the bottom. We need + // to install our own region so we need to move the limits + // of the stack to make room for it. + tlsPtr_.stack_begin += guardsize; + tlsPtr_.stack_end += guardsize; + tlsPtr_.stack_size -= guardsize; + } + InstallImplicitProtection(is_main_thread); } // Sanity check. @@ -2219,14 +2259,6 @@ void Thread::SetStackEndForStackOverflow() { } tlsPtr_.stack_end = tlsPtr_.stack_begin; - - // Remove the stack overflow protection if is it set up. - bool implicit_stack_check = !Runtime::Current()->ExplicitStackOverflowChecks(); - if (implicit_stack_check) { - if (!UnprotectStack()) { - LOG(ERROR) << "Unable to remove stack protection for stack overflow"; - } - } } void Thread::SetTlab(byte* start, byte* end) { @@ -2252,21 +2284,4 @@ std::ostream& operator<<(std::ostream& os, const Thread& thread) { return os; } -void Thread::ProtectStack() { - void* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize; - VLOG(threads) << "Protecting stack at " << pregion; - if (mprotect(pregion, kStackOverflowProtectedSize, PROT_NONE) == -1) { - LOG(FATAL) << "Unable to create protected region in stack for implicit overflow check. " - "Reason: " - << strerror(errno) << " size: " << kStackOverflowProtectedSize; - } -} - -bool Thread::UnprotectStack() { - void* pregion = tlsPtr_.stack_begin - kStackOverflowProtectedSize; - VLOG(threads) << "Unprotecting stack at " << pregion; - return mprotect(pregion, kStackOverflowProtectedSize, PROT_READ|PROT_WRITE) == 0; -} - - } // namespace art diff --git a/runtime/thread.h b/runtime/thread.h index 49f42445be5..3e9372fe1a2 100644 --- a/runtime/thread.h +++ b/runtime/thread.h @@ -94,41 +94,16 @@ enum ThreadFlag { static constexpr size_t kNumRosAllocThreadLocalSizeBrackets = 34; -// Thread's stack layout for implicit stack overflow checks: -// -// +---------------------+ <- highest address of stack memory -// | | -// . . <- SP -// | | -// | | -// +---------------------+ <- stack_end -// | | -// | Gap | -// | | -// +---------------------+ <- stack_begin -// | | -// | Protected region | -// | | -// +---------------------+ <- lowest address of stack memory -// -// The stack always grows down in memory. At the lowest address is a region of memory -// that is set mprotect(PROT_NONE). Any attempt to read/write to this region will -// result in a segmentation fault signal. At any point, the thread's SP will be somewhere -// between the stack_end and the highest address in stack memory. An implicit stack -// overflow check is a read of memory at a certain offset below the current SP (4K typically). -// If the thread's SP is below the stack_end address this will be a read into the protected -// region. If the SP is above the stack_end address, the thread is guaranteed to have -// at least 4K of space. Because stack overflow checks are only performed in generated code, -// if the thread makes a call out to a native function (through JNI), that native function -// might only have 4K of memory (if the SP is adjacent to stack_end). - class Thread { public: + // How much of the reserved bytes is reserved for incoming signals. + static constexpr size_t kStackOverflowSignalReservedBytes = 2 * KB; + // For implicit overflow checks we reserve an extra piece of memory at the bottom // of the stack (lowest memory). The higher portion of the memory // is protected against reads and the lower is available for use while // throwing the StackOverflow exception. - static constexpr size_t kStackOverflowProtectedSize = 4 * KB; + static constexpr size_t kStackOverflowProtectedSize = 16 * KB; static const size_t kStackOverflowImplicitCheckSize; // Creates a new native thread corresponding to the given managed peer. @@ -608,7 +583,7 @@ class Thread { } // Install the protected region for implicit stack checks. - void InstallImplicitProtection(); + void InstallImplicitProtection(bool is_main_stack); bool IsHandlingStackOverflow() const { return tlsPtr_.stack_end == tlsPtr_.stack_begin; @@ -840,20 +815,6 @@ class Thread { tls32_.is_exception_reported_to_instrumentation_ = reported; } - void ProtectStack(); - bool UnprotectStack(); - - void NoteSignalBeingHandled() { - if (tls32_.handling_signal_) { - LOG(FATAL) << "Detected signal while processing a signal"; - } - tls32_.handling_signal_ = true; - } - - void NoteSignalHandlerDone() { - tls32_.handling_signal_ = false; - } - private: explicit Thread(bool daemon); ~Thread() LOCKS_EXCLUDED(Locks::mutator_lock_, @@ -958,8 +919,7 @@ class Thread { explicit tls_32bit_sized_values(bool is_daemon) : suspend_count(0), debug_suspend_count(0), thin_lock_thread_id(0), tid(0), daemon(is_daemon), throwing_OutOfMemoryError(false), no_thread_suspension(0), - thread_exit_check_count(0), is_exception_reported_to_instrumentation_(false), - handling_signal_(false), padding_(0) { + thread_exit_check_count(0), is_exception_reported_to_instrumentation_(false) { } union StateAndFlags state_and_flags; @@ -999,12 +959,6 @@ class Thread { // When true this field indicates that the exception associated with this thread has already // been reported to instrumentation. bool32_t is_exception_reported_to_instrumentation_; - - // True if signal is being handled by this thread. - bool32_t handling_signal_; - - // Padding to make the size aligned to 8. Remove this if we add another 32 bit field. - int32_t padding_; } tls32_; struct PACKED(8) tls_64bit_sized_values { diff --git a/test/004-InterfaceTest/src/Main.java b/test/004-InterfaceTest/src/Main.java index 297cbb04f11..9ebac594451 100644 --- a/test/004-InterfaceTest/src/Main.java +++ b/test/004-InterfaceTest/src/Main.java @@ -23,7 +23,7 @@ public static long test_virtual(HashMap map) { Integer intobj = new Integer(0); String s = "asdf"; long start = System.currentTimeMillis(); - for (int i = 0; i < 10000; i++) { + for (int i = 0; i < 1000000; i++) { map.put(intobj, s); } long end = System.currentTimeMillis(); @@ -34,7 +34,7 @@ public static long test_interface(Map map) { Integer intobj = new Integer(0); String s = "asdf"; long start = System.currentTimeMillis(); - for (int i = 0; i < 10000; i++) { + for (int i = 0; i < 1000000; i++) { map.put(intobj, s); } long end = System.currentTimeMillis(); diff --git a/test/004-SignalTest/signaltest.cc b/test/004-SignalTest/signaltest.cc index c05dc22b8b6..a2dd66459c4 100644 --- a/test/004-SignalTest/signaltest.cc +++ b/test/004-SignalTest/signaltest.cc @@ -16,7 +16,6 @@ #include #include -#include #include #include "jni.h" @@ -25,15 +24,8 @@ #include #endif -static int signal_count; -static const int kMaxSignal = 2; - static void signalhandler(int sig, siginfo_t* info, void* context) { printf("signal caught\n"); - ++signal_count; - if (signal_count > kMaxSignal) { - abort(); - } #ifdef __arm__ // On ARM we do a more exhaustive test to make sure the signal // context is OK. From 2d5a21cf732ebc00396eedb3b97e951c65ec1449 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 13 Aug 2014 18:07:31 -0700 Subject: [PATCH 013/150] Use homogeneous space compaction if proper flag is set. If the flag ART_USE_HSPACE_COMPACT is set then we use hspace compaction. Bug: 16401001 Change-Id: I74e1db764f9ff16c007fa3bd16cb2c9b468ec83c --- runtime/Android.mk | 4 ++++ runtime/parsed_options.cc | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/runtime/Android.mk b/runtime/Android.mk index 09ec0040642..bdecc7ef59a 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -325,6 +325,10 @@ else LIBART_CFLAGS += -DUSE_JEMALLOC endif +ifeq ($(ART_USE_HSPACE_COMPACT),true) + LIBART_CFLAGS += -DART_USE_HSPACE_COMPACT +endif + # $(1): target or host # $(2): ndebug or debug define build-libart diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 49f658507d8..36ad56bf614 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -205,7 +205,11 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize // If background_collector_type_ is kCollectorTypeNone, it defaults to the collector_type_ after // parsing options. If you set this to kCollectorTypeHSpaceCompact then we will do an hspace // compaction when we transition to background instead of a normal collector transition. +#ifdef ART_USE_HSPACE_COMPACT + background_collector_type_ = gc::kCollectorTypeHomogeneousSpaceCompact; +#else background_collector_type_ = gc::kCollectorTypeSS; +#endif stack_size_ = 0; // 0 means default. max_spins_before_thin_lock_inflation_ = Monitor::kDefaultMaxSpinsBeforeThinLockInflation; low_memory_mode_ = false; @@ -390,6 +394,10 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize } else if (option == "-XX:IgnoreMaxFootprint") { ignore_max_footprint_ = true; } else if (option == "-XX:LowMemoryMode") { + if (background_collector_type_ == gc::kCollectorTypeHomogeneousSpaceCompact) { + // Use semispace instead of homogenous space compact for low memory mode. + background_collector_type_ = gc::kCollectorTypeSS; + } low_memory_mode_ = true; // TODO Might want to turn off must_relocate here. } else if (option == "-XX:UseTLAB") { From a4962aaa16a401da6f9db3ef26209b4ba6e99045 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Fri, 15 Aug 2014 11:09:28 -0700 Subject: [PATCH 014/150] Make Monitor::Wait more robust to spurious Inflate failures. Bug: 17062710 Change-Id: Ife5f6b335caacc70cab543cd568676d277d3beb6 --- runtime/mirror/object.cc | 3 ++- runtime/monitor.cc | 49 +++++++++++++++++++--------------------- runtime/monitor.h | 7 ++++++ 3 files changed, 32 insertions(+), 27 deletions(-) diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index 35436548688..a1177d645d2 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -162,7 +162,8 @@ int32_t Object::IdentityHashCode() const { break; } case LockWord::kThinLocked: { - // Inflate the thin lock to a monitor and stick the hash code inside of the monitor. + // Inflate the thin lock to a monitor and stick the hash code inside of the monitor. May + // fail spuriously. Thread* self = Thread::Current(); StackHandleScope<1> hs(self); Handle h_this(hs.NewHandle(current_this)); diff --git a/runtime/monitor.cc b/runtime/monitor.cc index 433c1b2d6d6..4c780ee9e6e 100644 --- a/runtime/monitor.cc +++ b/runtime/monitor.cc @@ -641,11 +641,6 @@ bool Monitor::Deflate(Thread* self, mirror::Object* obj) { return true; } -/* - * Changes the shape of a monitor from thin to fat, preserving the internal lock state. The calling - * thread must own the lock or the owner must be suspended. There's a race with other threads - * inflating the lock and so the caller should read the monitor following the call. - */ void Monitor::Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) { DCHECK(self != nullptr); DCHECK(obj != nullptr); @@ -831,30 +826,32 @@ void Monitor::Wait(Thread* self, mirror::Object *obj, int64_t ms, int32_t ns, DCHECK(self != nullptr); DCHECK(obj != nullptr); LockWord lock_word = obj->GetLockWord(true); - switch (lock_word.GetState()) { - case LockWord::kHashCode: - // Fall-through. - case LockWord::kUnlocked: - ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); - return; // Failure. - case LockWord::kThinLocked: { - uint32_t thread_id = self->GetThreadId(); - uint32_t owner_thread_id = lock_word.ThinLockOwner(); - if (owner_thread_id != thread_id) { + while (lock_word.GetState() != LockWord::kFatLocked) { + switch (lock_word.GetState()) { + case LockWord::kHashCode: + // Fall-through. + case LockWord::kUnlocked: ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); return; // Failure. - } else { - // We own the lock, inflate to enqueue ourself on the Monitor. - Inflate(self, self, obj, 0); - lock_word = obj->GetLockWord(true); + case LockWord::kThinLocked: { + uint32_t thread_id = self->GetThreadId(); + uint32_t owner_thread_id = lock_word.ThinLockOwner(); + if (owner_thread_id != thread_id) { + ThrowIllegalMonitorStateExceptionF("object not locked by thread before wait()"); + return; // Failure. + } else { + // We own the lock, inflate to enqueue ourself on the Monitor. May fail spuriously so + // re-load. + Inflate(self, self, obj, 0); + lock_word = obj->GetLockWord(true); + } + break; + } + case LockWord::kFatLocked: // Unreachable given the loop condition above. Fall-through. + default: { + LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); + return; } - break; - } - case LockWord::kFatLocked: - break; // Already set for a wait. - default: { - LOG(FATAL) << "Invalid monitor state " << lock_word.GetState(); - return; } } Monitor* mon = lock_word.FatLockMonitor(); diff --git a/runtime/monitor.h b/runtime/monitor.h index 26d43c953b2..ae14fc1d78f 100644 --- a/runtime/monitor.h +++ b/runtime/monitor.h @@ -117,6 +117,7 @@ class Monitor { return monitor_id_; } + // Inflate the lock on obj. May fail to inflate for spurious reasons, always re-check. static void InflateThinLocked(Thread* self, Handle obj, LockWord lock_word, uint32_t hash_code) NO_THREAD_SAFETY_ANALYSIS; @@ -138,6 +139,12 @@ class Monitor { void AppendToWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_); void RemoveFromWaitSet(Thread* thread) EXCLUSIVE_LOCKS_REQUIRED(monitor_lock_); + /* + * Changes the shape of a monitor from thin to fat, preserving the internal lock state. The + * calling thread must own the lock or the owner must be suspended. There's a race with other + * threads inflating the lock, installing hash codes and spurious failures. The caller should + * re-read the lock word following the call. + */ static void Inflate(Thread* self, Thread* owner, mirror::Object* obj, int32_t hash_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); From 9660918229e895c4d6c4c361fd84a75abc683de0 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Thu, 21 Aug 2014 17:38:09 +0100 Subject: [PATCH 015/150] Make a couple of map checks debug only. This cost us close to 80ms in app startup times. The checks that a reused region was within an already existent map has been demoted to a debug check. A couple of other negative checks have been removed outright because one of them was superflous and the other wasn't guaranteed to be correct. bug: 16828525 Change-Id: I00f76de06df0ea4ced40fdcb7825248d4b662045 --- runtime/mem_map.cc | 40 +++++++++++++++++++++++++++------------- runtime/mem_map.h | 3 ++- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index c281b2200fb..6cda2588471 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -131,9 +131,9 @@ uintptr_t MemMap::next_mem_pos_ = GenerateNextMemPos(); #endif // Return true if the address range is contained in a single /proc/self/map entry. -static bool CheckOverlapping(uintptr_t begin, - uintptr_t end, - std::string* error_msg) { +static bool ContainedWithinExistingMap(uintptr_t begin, + uintptr_t end, + std::string* error_msg) { std::unique_ptr map(BacktraceMap::Create(getpid(), true)); if (map.get() == nullptr) { *error_msg = StringPrintf("Failed to build process map"); @@ -212,12 +212,25 @@ static bool CheckMapRequest(byte* expected_ptr, void* actual_ptr, size_t byte_co PLOG(WARNING) << StringPrintf("munmap(%p, %zd) failed", actual_ptr, byte_count); } - if (!CheckNonOverlapping(expected, limit, error_msg)) { - return false; + // We call this here so that we can try and generate a full error + // message with the overlapping mapping. There's no guarantee that + // that there will be an overlap though, since + // - The kernel is not *required* to honour expected_ptr unless MAP_FIXED is + // true, even if there is no overlap + // - There might have been an overlap at the point of mmap, but the + // overlapping region has since been unmapped. + std::string error_detail; + CheckNonOverlapping(expected, limit, &error_detail); + + std::ostringstream os; + os << StringPrintf("Failed to mmap at expected address, mapped at " + "0x%08" PRIxPTR " instead of 0x%08" PRIxPTR, + actual, expected); + if (!error_detail.empty()) { + os << " : " << error_detail; } - *error_msg = StringPrintf("Failed to mmap at expected address, mapped at " - "0x%08" PRIxPTR " instead of 0x%08" PRIxPTR, actual, expected); + *error_msg = os.str(); return false; } @@ -375,19 +388,20 @@ MemMap* MemMap::MapFileAtAddress(byte* expected_ptr, size_t byte_count, int prot CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE)); uintptr_t expected = reinterpret_cast(expected_ptr); uintptr_t limit = expected + byte_count; + + // Note that we do not allow MAP_FIXED unless reuse == true, i.e we + // expect his mapping to be contained within an existing map. if (reuse) { // reuse means it is okay that it overlaps an existing page mapping. // Only use this if you actually made the page reservation yourself. CHECK(expected_ptr != nullptr); - if (!CheckOverlapping(expected, limit, error_msg)) { - return nullptr; - } + + DCHECK(ContainedWithinExistingMap(expected, limit, error_msg)); flags |= MAP_FIXED; } else { CHECK_EQ(0, flags & MAP_FIXED); - if (expected_ptr != nullptr && !CheckNonOverlapping(expected, limit, error_msg)) { - return nullptr; - } + // Don't bother checking for an overlapping region here. We'll + // check this if required after the fact inside CheckMapRequest. } if (byte_count == 0) { diff --git a/runtime/mem_map.h b/runtime/mem_map.h index 872c63b193c..9bfcd96d7f0 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -77,7 +77,8 @@ class MemMap { // mapping. "reuse" allows us to create a view into an existing // mapping where we do not take ownership of the memory. // - // On success, returns returns a MemMap instance. On failure, returns a NULL; + // On success, returns returns a MemMap instance. On failure, returns a + // nullptr; static MemMap* MapFileAtAddress(byte* addr, size_t byte_count, int prot, int flags, int fd, off_t start, bool reuse, const char* filename, std::string* error_msg); From 6c4da4e0ac87955f45702b00edc532c344b9f874 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 21 Aug 2014 12:21:48 -0700 Subject: [PATCH 016/150] Fix GC memory overhead accounting. There was some missing null checks. Bug: 16238192 Change-Id: Iaf8d752db5f21e76f668c0066a063239ff374eee --- runtime/gc/accounting/gc_allocator.cc | 10 ++++++++-- runtime/gc/accounting/gc_allocator.h | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/runtime/gc/accounting/gc_allocator.cc b/runtime/gc/accounting/gc_allocator.cc index 49d84faf3c8..ff6a1356ebb 100644 --- a/runtime/gc/accounting/gc_allocator.cc +++ b/runtime/gc/accounting/gc_allocator.cc @@ -24,12 +24,18 @@ namespace gc { namespace accounting { void* RegisterGcAllocation(size_t bytes) { - Runtime::Current()->GetHeap()->RegisterGCAllocation(bytes); + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (heap != nullptr) { + heap->RegisterGCAllocation(bytes); + } return malloc(bytes); } void RegisterGcDeallocation(void* p, size_t bytes) { - Runtime::Current()->GetHeap()->RegisterGCDeAllocation(bytes); + gc::Heap* heap = Runtime::Current()->GetHeap(); + if (heap != nullptr) { + heap->RegisterGCDeAllocation(bytes); + } free(p); } diff --git a/runtime/gc/accounting/gc_allocator.h b/runtime/gc/accounting/gc_allocator.h index 1d96112b0c0..d4142f824cc 100644 --- a/runtime/gc/accounting/gc_allocator.h +++ b/runtime/gc/accounting/gc_allocator.h @@ -30,7 +30,7 @@ namespace accounting { void* RegisterGcAllocation(size_t bytes); void RegisterGcDeallocation(void* p, size_t bytes); -static const bool kMeasureGcMemoryOverhead = false; +static constexpr bool kMeasureGcMemoryOverhead = false; template class GcAllocatorImpl : public std::allocator { From d04f113d1471c3d6b6dadc342835ec6370da2507 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 25 Aug 2014 16:32:41 -0700 Subject: [PATCH 017/150] Map heap maps at a lower address. We now map the heap mmaps at a lower address in case the app needs larger continuous address space. Bug: 16502380 Change-Id: I2cc11d0c207c0eae8db0c5025a48b11119c5a802 --- runtime/gc/heap.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 9fe0509f262..3a61f675504 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -259,6 +259,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max MemMap::MapAnonymous("non moving space", requested_alloc_space_begin, non_moving_space_capacity, PROT_READ | PROT_WRITE, true, &error_str)); CHECK(non_moving_space_mem_map != nullptr) << error_str; + // Try to reserve virtual memory at a lower address if we have a separate non moving space. + request_begin = reinterpret_cast(0x1000000); } // Attempt to create 2 mem maps at or after the requested begin. main_mem_map_1.reset(MapAnonymousPreferredAddress(kMemMapSpaceName[0], request_begin, capacity_, From 70c71ab1c2e44a74fc4eb76d6a0530f773850196 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Fri, 29 Aug 2014 18:16:58 -0700 Subject: [PATCH 018/150] Change intern table to unordered set. Intern table active used bytes goes from 430k to 317k on system server. Similar %wise savings on other apps. Bug: 16238192 Change-Id: I92de72de3eae0b76629e9da83db2c2d0fa613052 --- runtime/gc_root-inl.h | 5 ++ runtime/gc_root.h | 1 + runtime/intern_table.cc | 150 +++++++++++++++++++----------------- runtime/intern_table.h | 38 +++++---- runtime/mirror/string-inl.h | 11 +++ runtime/mirror/string.cc | 17 +--- runtime/mirror/string.h | 3 +- runtime/runtime.cc | 16 ++-- runtime/runtime.h | 8 +- runtime/transaction.cc | 24 +++--- runtime/transaction.h | 13 ++-- 11 files changed, 153 insertions(+), 133 deletions(-) diff --git a/runtime/gc_root-inl.h b/runtime/gc_root-inl.h index 482f7bca0e3..2661e54261e 100644 --- a/runtime/gc_root-inl.h +++ b/runtime/gc_root-inl.h @@ -29,5 +29,10 @@ inline MirrorType* GcRoot::Read() { return ReadBarrier::BarrierForRoot(&root_); } +template +inline void GcRoot::Assign(MirrorType* value) { + root_ = value; +} + } // namespace art #endif // ART_RUNTIME_GC_ROOT_INL_H_ diff --git a/runtime/gc_root.h b/runtime/gc_root.h index b10a55c1a23..3928f5d3e28 100644 --- a/runtime/gc_root.h +++ b/runtime/gc_root.h @@ -28,6 +28,7 @@ class PACKED(4) GcRoot { public: template ALWAYS_INLINE MirrorType* Read() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + ALWAYS_INLINE void Assign(MirrorType* value) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void VisitRoot(RootCallback* callback, void* arg, uint32_t thread_id, RootType root_type) { callback(reinterpret_cast(&root_), arg, thread_id, root_type); diff --git a/runtime/intern_table.cc b/runtime/intern_table.cc index aadd85a0f4f..c66f99e50cc 100644 --- a/runtime/intern_table.cc +++ b/runtime/intern_table.cc @@ -22,7 +22,7 @@ #include "mirror/dex_cache.h" #include "mirror/object_array-inl.h" #include "mirror/object-inl.h" -#include "mirror/string.h" +#include "mirror/string-inl.h" #include "thread.h" #include "utf.h" @@ -58,25 +58,23 @@ void InternTable::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags f MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); if ((flags & kVisitRootFlagAllRoots) != 0) { for (auto& strong_intern : strong_interns_) { - strong_intern.second.VisitRoot(callback, arg, 0, kRootInternedString); - DCHECK(!strong_intern.second.IsNull()); + const_cast&>(strong_intern). + VisitRoot(callback, arg, 0, kRootInternedString); + DCHECK(!strong_intern.IsNull()); } } else if ((flags & kVisitRootFlagNewRoots) != 0) { - for (auto& pair : new_strong_intern_roots_) { - mirror::String* old_ref = pair.second.Read(); - pair.second.VisitRoot(callback, arg, 0, kRootInternedString); - mirror::String* new_ref = pair.second.Read(); + for (auto& root : new_strong_intern_roots_) { + mirror::String* old_ref = root.Read(); + root.VisitRoot(callback, arg, 0, kRootInternedString); + mirror::String* new_ref = root.Read(); if (UNLIKELY(new_ref != old_ref)) { - // Uh ohes, GC moved a root in the log. Need to search the strong interns and update the + // The GC moved a root in the log. Need to search the strong interns and update the // corresponding object. This is slow, but luckily for us, this may only happen with a // concurrent moving GC. - for (auto it = strong_interns_.lower_bound(pair.first), end = strong_interns_.end(); - it != end && it->first == pair.first; ++it) { - // If the class stored matches the old class, update it to the new value. - if (old_ref == it->second.Read()) { - it->second = GcRoot(new_ref); - } - } + auto it = strong_interns_.find(GcRoot(old_ref)); + DCHECK(it != strong_interns_.end()); + strong_interns_.erase(it); + strong_interns_.insert(GcRoot(new_ref)); } } } @@ -92,87 +90,79 @@ void InternTable::VisitRoots(RootCallback* callback, void* arg, VisitRootFlags f // Note: we deliberately don't visit the weak_interns_ table and the immutable image roots. } -mirror::String* InternTable::LookupStrong(mirror::String* s, int32_t hash_code) { - return Lookup(&strong_interns_, s, hash_code); +mirror::String* InternTable::LookupStrong(mirror::String* s) { + return Lookup(&strong_interns_, s); } -mirror::String* InternTable::LookupWeak(mirror::String* s, int32_t hash_code) { +mirror::String* InternTable::LookupWeak(mirror::String* s) { // Weak interns need a read barrier because they are weak roots. - return Lookup(&weak_interns_, s, hash_code); + return Lookup(&weak_interns_, s); } -mirror::String* InternTable::Lookup(Table* table, mirror::String* s, int32_t hash_code) { +mirror::String* InternTable::Lookup(Table* table, mirror::String* s) { Locks::intern_table_lock_->AssertHeld(Thread::Current()); - for (auto it = table->lower_bound(hash_code), end = table->end(); - it != end && it->first == hash_code; ++it) { - mirror::String* existing_string = it->second.Read(); - if (existing_string->Equals(s)) { - return existing_string; - } + auto it = table->find(GcRoot(s)); + if (LIKELY(it != table->end())) { + return const_cast&>(*it).Read(); } - return NULL; + return nullptr; } -mirror::String* InternTable::InsertStrong(mirror::String* s, int32_t hash_code) { +mirror::String* InternTable::InsertStrong(mirror::String* s) { Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { - runtime->RecordStrongStringInsertion(s, hash_code); + runtime->RecordStrongStringInsertion(s); } if (log_new_roots_) { - new_strong_intern_roots_.push_back(std::make_pair(hash_code, GcRoot(s))); + new_strong_intern_roots_.push_back(GcRoot(s)); } - strong_interns_.insert(std::make_pair(hash_code, GcRoot(s))); + strong_interns_.insert(GcRoot(s)); return s; } -mirror::String* InternTable::InsertWeak(mirror::String* s, int32_t hash_code) { +mirror::String* InternTable::InsertWeak(mirror::String* s) { Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { - runtime->RecordWeakStringInsertion(s, hash_code); + runtime->RecordWeakStringInsertion(s); } - weak_interns_.insert(std::make_pair(hash_code, GcRoot(s))); + weak_interns_.insert(GcRoot(s)); return s; } -void InternTable::RemoveStrong(mirror::String* s, int32_t hash_code) { - Remove(&strong_interns_, s, hash_code); +void InternTable::RemoveStrong(mirror::String* s) { + Remove(&strong_interns_, s); } -void InternTable::RemoveWeak(mirror::String* s, int32_t hash_code) { +void InternTable::RemoveWeak(mirror::String* s) { Runtime* runtime = Runtime::Current(); if (runtime->IsActiveTransaction()) { - runtime->RecordWeakStringRemoval(s, hash_code); + runtime->RecordWeakStringRemoval(s); } - Remove(&weak_interns_, s, hash_code); + Remove(&weak_interns_, s); } -void InternTable::Remove(Table* table, mirror::String* s, int32_t hash_code) { - for (auto it = table->lower_bound(hash_code), end = table->end(); - it != end && it->first == hash_code; ++it) { - mirror::String* existing_string = it->second.Read(); - if (existing_string == s) { - table->erase(it); - return; - } - } +void InternTable::Remove(Table* table, mirror::String* s) { + auto it = table->find(GcRoot(s)); + DCHECK(it != table->end()); + table->erase(it); } // Insert/remove methods used to undo changes made during an aborted transaction. -mirror::String* InternTable::InsertStrongFromTransaction(mirror::String* s, int32_t hash_code) { +mirror::String* InternTable::InsertStrongFromTransaction(mirror::String* s) { DCHECK(!Runtime::Current()->IsActiveTransaction()); - return InsertStrong(s, hash_code); + return InsertStrong(s); } -mirror::String* InternTable::InsertWeakFromTransaction(mirror::String* s, int32_t hash_code) { +mirror::String* InternTable::InsertWeakFromTransaction(mirror::String* s) { DCHECK(!Runtime::Current()->IsActiveTransaction()); - return InsertWeak(s, hash_code); + return InsertWeak(s); } -void InternTable::RemoveStrongFromTransaction(mirror::String* s, int32_t hash_code) { +void InternTable::RemoveStrongFromTransaction(mirror::String* s) { DCHECK(!Runtime::Current()->IsActiveTransaction()); - RemoveStrong(s, hash_code); + RemoveStrong(s); } -void InternTable::RemoveWeakFromTransaction(mirror::String* s, int32_t hash_code) { +void InternTable::RemoveWeakFromTransaction(mirror::String* s) { DCHECK(!Runtime::Current()->IsActiveTransaction()); - RemoveWeak(s, hash_code); + RemoveWeak(s); } static mirror::String* LookupStringFromImage(mirror::String* s) @@ -218,7 +208,6 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { MutexLock mu(self, *Locks::intern_table_lock_); DCHECK(s != NULL); - uint32_t hash_code = s->GetHashCode(); while (UNLIKELY(!allow_new_interns_)) { new_intern_condition_.WaitHoldingLocks(self); @@ -226,7 +215,7 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { if (is_strong) { // Check the strong table for a match. - mirror::String* strong = LookupStrong(s, hash_code); + mirror::String* strong = LookupStrong(s); if (strong != NULL) { return strong; } @@ -234,39 +223,39 @@ mirror::String* InternTable::Insert(mirror::String* s, bool is_strong) { // Check the image for a match. mirror::String* image = LookupStringFromImage(s); if (image != NULL) { - return InsertStrong(image, hash_code); + return InsertStrong(image); } // There is no match in the strong table, check the weak table. - mirror::String* weak = LookupWeak(s, hash_code); + mirror::String* weak = LookupWeak(s); if (weak != NULL) { // A match was found in the weak table. Promote to the strong table. - RemoveWeak(weak, hash_code); - return InsertStrong(weak, hash_code); + RemoveWeak(weak); + return InsertStrong(weak); } // No match in the strong table or the weak table. Insert into the strong // table. - return InsertStrong(s, hash_code); + return InsertStrong(s); } // Check the strong table for a match. - mirror::String* strong = LookupStrong(s, hash_code); + mirror::String* strong = LookupStrong(s); if (strong != NULL) { return strong; } // Check the image for a match. mirror::String* image = LookupStringFromImage(s); if (image != NULL) { - return InsertWeak(image, hash_code); + return InsertWeak(image); } // Check the weak table for a match. - mirror::String* weak = LookupWeak(s, hash_code); + mirror::String* weak = LookupWeak(s); if (weak != NULL) { return weak; } // Insert into the weak table. - return InsertWeak(s, hash_code); + return InsertWeak(s); } mirror::String* InternTable::InternStrong(int32_t utf16_length, const char* utf8_data) { @@ -296,7 +285,7 @@ mirror::String* InternTable::InternWeak(mirror::String* s) { bool InternTable::ContainsWeak(mirror::String* s) { MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); - const mirror::String* found = LookupWeak(s, s->GetHashCode()); + const mirror::String* found = LookupWeak(s); return found == s; } @@ -304,16 +293,33 @@ void InternTable::SweepInternTableWeaks(IsMarkedCallback* callback, void* arg) { MutexLock mu(Thread::Current(), *Locks::intern_table_lock_); for (auto it = weak_interns_.begin(), end = weak_interns_.end(); it != end;) { // This does not need a read barrier because this is called by GC. - mirror::Object* object = it->second.Read(); + GcRoot& root = const_cast&>(*it); + mirror::Object* object = root.Read(); mirror::Object* new_object = callback(object, arg); if (new_object == nullptr) { - // TODO: use it = weak_interns_.erase(it) when we get a c++11 stl. - weak_interns_.erase(it++); + it = weak_interns_.erase(it); } else { - it->second = GcRoot(down_cast(new_object)); + root.Assign(down_cast(new_object)); ++it; } } } +std::size_t InternTable::StringHashEquals::operator()(const GcRoot& root) { + if (kIsDebugBuild) { + Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); + } + return static_cast( + const_cast&>(root).Read()->GetHashCode()); +} + +bool InternTable::StringHashEquals::operator()(const GcRoot& a, + const GcRoot& b) { + if (kIsDebugBuild) { + Locks::mutator_lock_->AssertSharedHeld(Thread::Current()); + } + return const_cast&>(a).Read()->Equals( + const_cast&>(b).Read()); +} + } // namespace art diff --git a/runtime/intern_table.h b/runtime/intern_table.h index 21f80464c70..e3223c86162 100644 --- a/runtime/intern_table.h +++ b/runtime/intern_table.h @@ -17,7 +17,7 @@ #ifndef ART_RUNTIME_INTERN_TABLE_H_ #define ART_RUNTIME_INTERN_TABLE_H_ -#include +#include #include "base/allocator.h" #include "base/mutex.h" @@ -79,42 +79,48 @@ class InternTable { void AllowNewInterns() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); private: - typedef AllocationTrackingMultiMap, - kAllocatorTagInternTable> Table; + class StringHashEquals { + public: + std::size_t operator()(const GcRoot& root) NO_THREAD_SAFETY_ANALYSIS; + bool operator()(const GcRoot& a, const GcRoot& b) + NO_THREAD_SAFETY_ANALYSIS; + }; + typedef std::unordered_set, StringHashEquals, StringHashEquals, + TrackingAllocator, kAllocatorTagInternTable>> Table; mirror::String* Insert(mirror::String* s, bool is_strong) LOCKS_EXCLUDED(Locks::intern_table_lock_) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::String* LookupStrong(mirror::String* s, int32_t hash_code) + mirror::String* LookupStrong(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::String* LookupWeak(mirror::String* s, int32_t hash_code) + mirror::String* LookupWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::String* Lookup(Table* table, mirror::String* s, int32_t hash_code) + mirror::String* Lookup(Table* table, mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::String* InsertStrong(mirror::String* s, int32_t hash_code) + mirror::String* InsertStrong(mirror::String* s) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - mirror::String* InsertWeak(mirror::String* s, int32_t hash_code) + mirror::String* InsertWeak(mirror::String* s) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - void RemoveStrong(mirror::String* s, int32_t hash_code) + void RemoveStrong(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - void RemoveWeak(mirror::String* s, int32_t hash_code) + void RemoveWeak(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - void Remove(Table* table, mirror::String* s, int32_t hash_code) + void Remove(Table* table, mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); // Transaction rollback access. - mirror::String* InsertStrongFromTransaction(mirror::String* s, int32_t hash_code) + mirror::String* InsertStrongFromTransaction(mirror::String* s) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - mirror::String* InsertWeakFromTransaction(mirror::String* s, int32_t hash_code) + mirror::String* InsertWeakFromTransaction(mirror::String* s) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - void RemoveStrongFromTransaction(mirror::String* s, int32_t hash_code) + void RemoveStrongFromTransaction(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - void RemoveWeakFromTransaction(mirror::String* s, int32_t hash_code) + void RemoveWeakFromTransaction(mirror::String* s) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); friend class Transaction; @@ -127,7 +133,7 @@ class InternTable { // directly access the strings in it. Use functions that contain // read barriers. Table strong_interns_ GUARDED_BY(Locks::intern_table_lock_); - std::vector>> new_strong_intern_roots_ + std::vector> new_strong_intern_roots_ GUARDED_BY(Locks::intern_table_lock_); // Since this contains (weak) roots, they need a read barrier. Do // not directly access the strings in it. Use functions that contain diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 67364975939..d9241417854 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -23,6 +23,7 @@ #include "runtime.h" #include "string.h" #include "thread.h" +#include "utf.h" namespace art { namespace mirror { @@ -67,6 +68,16 @@ inline uint16_t String::CharAt(int32_t index) { return GetCharArray()->Get(index + GetOffset()); } +inline int32_t String::GetHashCode() { + int32_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_)); + if (UNLIKELY(result == 0)) { + result = ComputeHashCode(); + } + DCHECK(result != 0 || ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength()) == 0) + << ToModifiedUtf8() << " " << result; + return result; +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/string.cc b/runtime/mirror/string.cc index e81e4312e72..01599ae9074 100644 --- a/runtime/mirror/string.cc +++ b/runtime/mirror/string.cc @@ -62,19 +62,10 @@ void String::ResetClass() { java_lang_String_ = GcRoot(nullptr); } -int32_t String::GetHashCode() { - int32_t result = GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_)); - if (UNLIKELY(result == 0)) { - ComputeHashCode(); - } - result = GetField32(OFFSET_OF_OBJECT_MEMBER(String, hash_code_)); - DCHECK(result != 0 || ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength()) == 0) - << ToModifiedUtf8() << " " << result; - return result; -} - -void String::ComputeHashCode() { - SetHashCode(ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength())); +int32_t String::ComputeHashCode() { + const int32_t hash_code = ComputeUtf16Hash(GetCharArray(), GetOffset(), GetLength()); + SetHashCode(hash_code); + return hash_code; } int32_t String::GetUtfLength() { diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 66a5dd827d1..1320ab71a31 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -66,7 +66,8 @@ class MANAGED String FINAL : public Object { int32_t GetHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ComputeHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Computes, stores, and returns the hash code. + int32_t ComputeHashCode() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int32_t GetUtfLength() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index c438ef7d0cd..a68c658cee0 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1308,28 +1308,28 @@ void Runtime::RecordWriteArray(mirror::Array* array, size_t index, uint64_t valu preinitialization_transaction_->RecordWriteArray(array, index, value); } -void Runtime::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) const { +void Runtime::RecordStrongStringInsertion(mirror::String* s) const { DCHECK(IsCompiler()); DCHECK(IsActiveTransaction()); - preinitialization_transaction_->RecordStrongStringInsertion(s, hash_code); + preinitialization_transaction_->RecordStrongStringInsertion(s); } -void Runtime::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) const { +void Runtime::RecordWeakStringInsertion(mirror::String* s) const { DCHECK(IsCompiler()); DCHECK(IsActiveTransaction()); - preinitialization_transaction_->RecordWeakStringInsertion(s, hash_code); + preinitialization_transaction_->RecordWeakStringInsertion(s); } -void Runtime::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) const { +void Runtime::RecordStrongStringRemoval(mirror::String* s) const { DCHECK(IsCompiler()); DCHECK(IsActiveTransaction()); - preinitialization_transaction_->RecordStrongStringRemoval(s, hash_code); + preinitialization_transaction_->RecordStrongStringRemoval(s); } -void Runtime::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) const { +void Runtime::RecordWeakStringRemoval(mirror::String* s) const { DCHECK(IsCompiler()); DCHECK(IsActiveTransaction()); - preinitialization_transaction_->RecordWeakStringRemoval(s, hash_code); + preinitialization_transaction_->RecordWeakStringRemoval(s); } void Runtime::SetFaultMessage(const std::string& message) { diff --git a/runtime/runtime.h b/runtime/runtime.h index 254e31acb79..183bf52aa44 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -424,13 +424,13 @@ class Runtime { mirror::Object* value, bool is_volatile) const; void RecordWriteArray(mirror::Array* array, size_t index, uint64_t value) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) const + void RecordStrongStringInsertion(mirror::String* s) const EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - void RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) const + void RecordWeakStringInsertion(mirror::String* s) const EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - void RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) const + void RecordStrongStringRemoval(mirror::String* s) const EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); - void RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) const + void RecordWeakStringRemoval(mirror::String* s) const EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_); void SetFaultMessage(const std::string& message); diff --git a/runtime/transaction.cc b/runtime/transaction.cc index cc02a8de74f..50f1ecaa79a 100644 --- a/runtime/transaction.cc +++ b/runtime/transaction.cc @@ -90,23 +90,23 @@ void Transaction::RecordWriteArray(mirror::Array* array, size_t index, uint64_t array_log.LogValue(index, value); } -void Transaction::RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) { - InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kInsert); +void Transaction::RecordStrongStringInsertion(mirror::String* s) { + InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kInsert); LogInternedString(log); } -void Transaction::RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) { - InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kInsert); +void Transaction::RecordWeakStringInsertion(mirror::String* s) { + InternStringLog log(s, InternStringLog::kWeakString, InternStringLog::kInsert); LogInternedString(log); } -void Transaction::RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) { - InternStringLog log(s, hash_code, InternStringLog::kStrongString, InternStringLog::kRemove); +void Transaction::RecordStrongStringRemoval(mirror::String* s) { + InternStringLog log(s, InternStringLog::kStrongString, InternStringLog::kRemove); LogInternedString(log); } -void Transaction::RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) { - InternStringLog log(s, hash_code, InternStringLog::kWeakString, InternStringLog::kRemove); +void Transaction::RecordWeakStringRemoval(mirror::String* s) { + InternStringLog log(s, InternStringLog::kWeakString, InternStringLog::kRemove); LogInternedString(log); } @@ -332,10 +332,10 @@ void Transaction::InternStringLog::Undo(InternTable* intern_table) { case InternStringLog::kInsert: { switch (string_kind_) { case InternStringLog::kStrongString: - intern_table->RemoveStrongFromTransaction(str_, hash_code_); + intern_table->RemoveStrongFromTransaction(str_); break; case InternStringLog::kWeakString: - intern_table->RemoveWeakFromTransaction(str_, hash_code_); + intern_table->RemoveWeakFromTransaction(str_); break; default: LOG(FATAL) << "Unknown interned string kind"; @@ -346,10 +346,10 @@ void Transaction::InternStringLog::Undo(InternTable* intern_table) { case InternStringLog::kRemove: { switch (string_kind_) { case InternStringLog::kStrongString: - intern_table->InsertStrongFromTransaction(str_, hash_code_); + intern_table->InsertStrongFromTransaction(str_); break; case InternStringLog::kWeakString: - intern_table->InsertWeakFromTransaction(str_, hash_code_); + intern_table->InsertWeakFromTransaction(str_); break; default: LOG(FATAL) << "Unknown interned string kind"; diff --git a/runtime/transaction.h b/runtime/transaction.h index 78591267644..66253908114 100644 --- a/runtime/transaction.h +++ b/runtime/transaction.h @@ -57,16 +57,16 @@ class Transaction { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Record intern string table changes. - void RecordStrongStringInsertion(mirror::String* s, uint32_t hash_code) + void RecordStrongStringInsertion(mirror::String* s) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) LOCKS_EXCLUDED(log_lock_); - void RecordWeakStringInsertion(mirror::String* s, uint32_t hash_code) + void RecordWeakStringInsertion(mirror::String* s) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) LOCKS_EXCLUDED(log_lock_); - void RecordStrongStringRemoval(mirror::String* s, uint32_t hash_code) + void RecordStrongStringRemoval(mirror::String* s) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) LOCKS_EXCLUDED(log_lock_); - void RecordWeakStringRemoval(mirror::String* s, uint32_t hash_code) + void RecordWeakStringRemoval(mirror::String* s) EXCLUSIVE_LOCKS_REQUIRED(Locks::intern_table_lock_) LOCKS_EXCLUDED(log_lock_); @@ -142,8 +142,8 @@ class Transaction { kInsert, kRemove }; - InternStringLog(mirror::String* s, uint32_t hash_code, StringKind kind, StringOp op) - : str_(s), hash_code_(hash_code), string_kind_(kind), string_op_(op) { + InternStringLog(mirror::String* s, StringKind kind, StringOp op) + : str_(s), string_kind_(kind), string_op_(op) { DCHECK(s != nullptr); } @@ -154,7 +154,6 @@ class Transaction { private: mirror::String* str_; - uint32_t hash_code_; StringKind string_kind_; StringOp string_op_; }; From d81877cad7a6e9b97dfdc4c63984f218b7e31df5 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 3 Sep 2014 21:26:36 -0700 Subject: [PATCH 019/150] Fix heap trimmer daemon sleeping. Problem: Heap trimmer daemon sleeping caused app launch occasionally to take a while due stopping the heap trimming thread taking a long time. The heap trimmer thread now never sleeps, we prevent issues caused by frequent back and forth process state changes by only transitioning to background if it has been kCollectorTransitionWait time since the last transition to background. Similar logic for heap trimming. Bug: 17310019 Change-Id: I23980421cc388f36b66a4c03ed15dd11d43f59b5 --- runtime/gc/heap.cc | 55 +++++++++++++++------------------------------- runtime/gc/heap.h | 4 ++-- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 4d8dbc8e41a..3dfa8ad3963 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -120,7 +120,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max desired_collector_type_(foreground_collector_type_), heap_trim_request_lock_(nullptr), last_trim_time_(0), - heap_transition_or_trim_target_time_(0), + last_heap_transition_time_(0), heap_trim_request_pending_(false), parallel_gc_threads_(parallel_gc_threads), conc_gc_threads_(conc_gc_threads), @@ -931,35 +931,6 @@ void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType } void Heap::DoPendingTransitionOrTrim() { - Thread* self = Thread::Current(); - CollectorType desired_collector_type; - // Wait until we reach the desired transition time. - while (true) { - uint64_t wait_time; - { - MutexLock mu(self, *heap_trim_request_lock_); - desired_collector_type = desired_collector_type_; - uint64_t current_time = NanoTime(); - if (current_time >= heap_transition_or_trim_target_time_) { - break; - } - wait_time = heap_transition_or_trim_target_time_ - current_time; - } - ScopedThreadStateChange tsc(self, kSleeping); - usleep(wait_time / 1000); // Usleep takes microseconds. - } - // Launch homogeneous space compaction if it is desired. - if (desired_collector_type == kCollectorTypeHomogeneousSpaceCompact) { - if (!CareAboutPauseTimes()) { - PerformHomogeneousSpaceCompact(); - } - // No need to Trim(). Homogeneous space compaction may free more virtual and physical memory. - desired_collector_type = collector_type_; - return; - } - // Transition the collector if the desired collector type is not the same as the current - // collector type. - TransitionCollector(desired_collector_type); if (!CareAboutPauseTimes()) { // Deflate the monitors, this can cause a pause but shouldn't matter since we don't care // about pauses. @@ -971,7 +942,23 @@ void Heap::DoPendingTransitionOrTrim() { << PrettyDuration(NanoTime() - start_time); runtime->GetThreadList()->ResumeAll(); } - // Do a heap trim if it is needed. + if (NanoTime() - last_heap_transition_time_ > kCollectorTransitionWait) { + // Launch homogeneous space compaction if it is desired. + if (desired_collector_type_ == kCollectorTypeHomogeneousSpaceCompact) { + if (!CareAboutPauseTimes()) { + PerformHomogeneousSpaceCompact(); + last_heap_transition_time_ = NanoTime(); + } + desired_collector_type_ = collector_type_; + } else { + // Transition the collector if the desired collector type is not the same as the current + // collector type. + TransitionCollector(desired_collector_type_); + last_heap_transition_time_ = NanoTime(); + } + } + // Do a heap trim if it is needed. This is good to do even with hspace compaction since it may + // trim the native heap and dlmalloc spaces. Trim(); } @@ -2994,8 +2981,6 @@ void Heap::RequestCollectorTransition(CollectorType desired_collector_type, uint if (desired_collector_type_ == desired_collector_type) { return; } - heap_transition_or_trim_target_time_ = - std::max(heap_transition_or_trim_target_time_, NanoTime() + delta_time); desired_collector_type_ = desired_collector_type; } SignalHeapTrimDaemon(self); @@ -3031,10 +3016,6 @@ void Heap::RequestHeapTrim() { return; } heap_trim_request_pending_ = true; - uint64_t current_time = NanoTime(); - if (heap_transition_or_trim_target_time_ < current_time) { - heap_transition_or_trim_target_time_ = current_time + kHeapTrimWait; - } } // Notify the daemon thread which will actually do the heap trim. SignalHeapTrimDaemon(self); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index d3d613f5662..2fe63e20177 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -824,8 +824,8 @@ class Heap { Mutex* heap_trim_request_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; // When we want to perform the next heap trim (nano seconds). uint64_t last_trim_time_ GUARDED_BY(heap_trim_request_lock_); - // When we want to perform the next heap transition (nano seconds) or heap trim. - uint64_t heap_transition_or_trim_target_time_ GUARDED_BY(heap_trim_request_lock_); + // When we last performed a heap transition or hspace compact. + uint64_t last_heap_transition_time_; // If we have a heap trim request pending. bool heap_trim_request_pending_ GUARDED_BY(heap_trim_request_lock_); From ec61aea293ee29256fb49b5c27d3f834c9c5f55e Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 8 Sep 2014 13:35:11 -0700 Subject: [PATCH 020/150] Ignore heap trim requests if we are the zygote Done to prevent app launching lag due to sleep in heap trimmer daemon. Bug: 17310019 Change-Id: Ia593e7eced1c1583771985ec9e7b60ee0c0e7912 --- runtime/gc/heap.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 3dfa8ad3963..404aff3243f 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3002,7 +3002,10 @@ void Heap::RequestHeapTrim() { Thread* self = Thread::Current(); Runtime* runtime = Runtime::Current(); - if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self)) { + if (runtime == nullptr || !runtime->IsFinishedStarting() || runtime->IsShuttingDown(self) || + runtime->IsZygote()) { + // Ignore the request if we are the zygote to prevent app launching lag due to sleep in heap + // trimmer daemon. b/17310019 // Heap trimming isn't supported without a Java runtime or Daemons (such as at dex2oat time) // Also: we do not wish to start a heap trim if the runtime is shutting down (a racy check // as we don't hold the lock while requesting the trim). From df96098c5d079e20c92a8de06dcbe5dacc6b1ab7 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Mon, 8 Sep 2014 19:49:50 -0700 Subject: [PATCH 021/150] Temporarily disable LOS on 64 bit Problem is that MAP_32BIT simulation can take seconds on large object allocations in the worst case. Bug: 17414549 Change-Id: I463bb0b728eed14847a32a27a93640ec6080f6b3 --- runtime/gc/heap.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index d3d613f5662..7304887bd1c 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -129,8 +129,12 @@ class Heap { // If true, measure the total allocation time. static constexpr bool kMeasureAllocationTime = false; // Primitive arrays larger than this size are put in the large object space. +#ifdef __LP64__ + // Temporarily disable LOS on 64 bit until MAP_32BIT performance issues are fixed. b/17414549 + static constexpr size_t kDefaultLargeObjectThreshold = 0xFFFFFFFFU; +#else static constexpr size_t kDefaultLargeObjectThreshold = 3 * kPageSize; - +#endif static constexpr size_t kDefaultStartingSize = kPageSize; static constexpr size_t kDefaultInitialSize = 2 * MB; static constexpr size_t kDefaultMaximumSize = 256 * MB; From 8165fda0a8e0a54b4b622835f4962e2dd31b89f2 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 10 Sep 2014 19:44:39 +0200 Subject: [PATCH 022/150] Ensure class is linked before resolution Adds a missing call to ClassLinker::EnsureResolved to ensure we did link the class and retired the temp class (placeholder) before doing the resolution. Bug: 17435441 Change-Id: Ib1a7181d6e5e814ca9299d0504e739a2b69475ef --- runtime/class_linker.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index eff2f6f4c7f..36247eefef0 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2008,7 +2008,7 @@ mirror::Class* ClassLinker::FindClass(Thread* self, const char* descriptor, if (pair.second != nullptr) { mirror::Class* klass = LookupClass(descriptor, nullptr); if (klass != nullptr) { - return klass; + return EnsureResolved(self, descriptor, klass); } klass = DefineClass(descriptor, NullHandle(), *pair.first, *pair.second); From f1d9b049b8e61a61dcacfe2fc98a8263c0215042 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Thu, 11 Sep 2014 16:23:44 +0200 Subject: [PATCH 023/150] Fix crash when connected to DDMS Restore init of WellKnownClasses::org_apache_harmony_dalvik_ddmc_Chunk_data and removed duplicated line. Bug: 17467328 Bug: 17463118 Change-Id: I64638e23615b7c20061b49c41a9c0e9f35a4d537 --- runtime/well_known_classes.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index f66e11a3d51..6b67dfac808 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -222,9 +222,9 @@ void WellKnownClasses::Init(JNIEnv* env) { java_lang_reflect_Proxy_h = CacheField(env, java_lang_reflect_Proxy, false, "h", "Ljava/lang/reflect/InvocationHandler;"); java_nio_DirectByteBuffer_capacity = CacheField(env, java_nio_DirectByteBuffer, false, "capacity", "I"); java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "effectiveDirectAddress", "J"); - java_nio_DirectByteBuffer_effectiveDirectAddress = CacheField(env, java_nio_DirectByteBuffer, false, "effectiveDirectAddress", "J"); java_util_Collections_EMPTY_LIST = CacheField(env, java_util_Collections, true, "EMPTY_LIST", "Ljava/util/List;"); libcore_util_EmptyArray_STACK_TRACE_ELEMENT = CacheField(env, libcore_util_EmptyArray, true, "STACK_TRACE_ELEMENT", "[Ljava/lang/StackTraceElement;"); + org_apache_harmony_dalvik_ddmc_Chunk_data = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "data", "[B"); org_apache_harmony_dalvik_ddmc_Chunk_length = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "length", "I"); org_apache_harmony_dalvik_ddmc_Chunk_offset = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "offset", "I"); org_apache_harmony_dalvik_ddmc_Chunk_type = CacheField(env, org_apache_harmony_dalvik_ddmc_Chunk, false, "type", "I"); From 0624965c286ecd0971e44c060059251e75ba4b84 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 11 Sep 2014 10:59:33 -0700 Subject: [PATCH 024/150] ART: Fix preverified setting in VerifyClass Make sure soft-failed classes cannot set methods to pre-verified. Bug: 16828525, 17465185 Change-Id: I09c0a68ca722978459741311148eae7614f9ca49 --- runtime/class_linker.cc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index f2c6af53982..b8c7eee906b 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -3413,11 +3413,13 @@ void ClassLinker::VerifyClass(Handle klass) { ObjectLock lock(self, klass); // Don't attempt to re-verify if already sufficiently verified. - if (klass->IsVerified() || - (klass->IsCompileTimeVerified() && Runtime::Current()->IsCompiler())) { + if (klass->IsVerified()) { EnsurePreverifiedMethods(klass); return; } + if (klass->IsCompileTimeVerified() && Runtime::Current()->IsCompiler()) { + return; + } // The class might already be erroneous, for example at compile time if we attempted to verify // this class as a parent to another. @@ -3524,6 +3526,9 @@ void ClassLinker::VerifyClass(Handle klass) { klass->SetStatus(mirror::Class::kStatusRetryVerificationAtRuntime, self); } else { klass->SetStatus(mirror::Class::kStatusVerified, self); + // As this is a fake verified status, make sure the methods are _not_ marked preverified + // later. + klass->SetAccessFlags(klass->GetAccessFlags() | kAccPreverified); } } } else { From 412aec93e3acdba1214f1601ceb6202d5c433f09 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 16 Sep 2014 11:23:23 -0700 Subject: [PATCH 025/150] ART: Check for exceptions from unresolved classes In no-verify mode, classes may be unresolved because of missing dependencies. Ignore and clear the exception. Bug: 17506140 Change-Id: I70602b089e6631b1e177dbe8316c5fefdaf777a0 --- compiler/driver/compiler_driver.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 6c614a3f230..fa2a560f9b7 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -608,7 +608,7 @@ void CompilerDriver::PreCompile(jobject class_loader, const std::vectorIsVerificationEnabled()) { - VLOG(compiler) << "Verify none mode specified, skipping verification."; + LOG(INFO) << "Verify none mode specified, skipping verification."; SetVerified(class_loader, dex_files, thread_pool, timings); return; } @@ -1758,8 +1758,11 @@ static void SetVerifiedClass(const ParallelCompilationManager* manager, size_t c ClassReference ref(manager->GetDexFile(), class_def_index); manager->GetCompiler()->RecordClassStatus(ref, klass->GetStatus()); } + } else { + Thread* self = soa.Self(); + DCHECK(self->IsExceptionPending()); + self->ClearException(); } - soa.Self()->AssertNoPendingException(); } void CompilerDriver::SetVerifiedDexFile(jobject class_loader, const DexFile& dex_file, From 3740332e2cb3f72658b7cecda1ebe86d0ebc1e08 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Thu, 18 Sep 2014 20:56:04 -0700 Subject: [PATCH 026/150] ART: Only allow the zygote to create the global boot image Do not allow arbitrary processes, even when root, to write the boot image in /data/dalvik-cache. Bug: 17478752, 17510489, 17439961 Change-Id: Iba2b74be6d0752f4221f4ff5ee295b45a34cb2e1 --- patchoat/patchoat.cc | 7 ++- runtime/class_linker.cc | 3 +- runtime/gc/space/image_space.cc | 57 +++++++++++++++++++------ runtime/gc/space/image_space.h | 3 +- runtime/native/dalvik_system_DexFile.cc | 4 +- runtime/runtime.cc | 4 +- runtime/utils.cc | 6 ++- runtime/utils.h | 3 +- 8 files changed, 64 insertions(+), 23 deletions(-) diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 74036607c6e..c84d9111d5c 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -79,9 +79,10 @@ static bool LocationToFilename(const std::string& location, InstructionSet isa, bool have_android_data = false; bool dalvik_cache_exists = false; + bool is_global_cache = false; std::string dalvik_cache; GetDalvikCache(GetInstructionSetString(isa), false, &dalvik_cache, - &have_android_data, &dalvik_cache_exists); + &have_android_data, &dalvik_cache_exists, &is_global_cache); std::string cache_filename; if (have_android_data && dalvik_cache_exists) { @@ -967,9 +968,11 @@ static int patchoat(int argc, char **argv) { std::string cache_filename; bool has_cache = false; bool has_android_data_unused = false; + bool is_global_cache = false; if (!gc::space::ImageSpace::FindImageFilename(patched_image_location.c_str(), isa, &system_filename, &has_system, &cache_filename, - &has_android_data_unused, &has_cache)) { + &has_android_data_unused, &has_cache, + &is_global_cache)) { Usage("Unable to determine image file for location %s", patched_image_location.c_str()); } if (has_cache) { diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 95a2e6309f1..43433dc7330 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1241,8 +1241,9 @@ const OatFile* ClassLinker::OpenOatFileFromDexLocation(const std::string& dex_lo 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); + &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()); diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 41c34c9bf5a..353d00c3dd4 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -125,7 +125,7 @@ static bool GenerateImage(const std::string& image_filename, InstructionSet imag } // We should clean up so we are more likely to have room for the image. if (Runtime::Current()->IsZygote()) { - LOG(INFO) << "Pruning dalvik-cache since we are relocating an image and will need to recompile"; + LOG(INFO) << "Pruning dalvik-cache since we are generating an image and will need to recompile"; PruneDexCache(image_isa); } @@ -177,7 +177,8 @@ bool ImageSpace::FindImageFilename(const char* image_location, bool* has_system, std::string* cache_filename, bool* dalvik_cache_exists, - bool* has_cache) { + bool* has_cache, + bool* is_global_cache) { *has_system = false; *has_cache = false; // image_location = /system/framework/boot.art @@ -192,7 +193,7 @@ bool ImageSpace::FindImageFilename(const char* image_location, *dalvik_cache_exists = false; std::string dalvik_cache; GetDalvikCache(GetInstructionSetString(image_isa), true, &dalvik_cache, - &have_android_data, dalvik_cache_exists); + &have_android_data, dalvik_cache_exists, is_global_cache); if (have_android_data && *dalvik_cache_exists) { // Always set output location even if it does not exist, @@ -285,8 +286,9 @@ ImageHeader* ImageSpace::ReadImageHeaderOrDie(const char* image_location, std::string cache_filename; bool has_cache = false; bool dalvik_cache_exists = false; + bool is_global_cache = false; if (FindImageFilename(image_location, image_isa, &system_filename, &has_system, - &cache_filename, &dalvik_cache_exists, &has_cache)) { + &cache_filename, &dalvik_cache_exists, &has_cache, &is_global_cache)) { if (Runtime::Current()->ShouldRelocate()) { if (has_system && has_cache) { std::unique_ptr sys_hdr(new ImageHeader); @@ -344,6 +346,21 @@ static bool ChecksumsMatch(const char* image_a, const char* image_b) { && hdr_a.GetOatChecksum() == hdr_b.GetOatChecksum(); } +static bool ImageCreationAllowed(bool is_global_cache, std::string* error_msg) { + // Anyone can write into a "local" cache. + if (!is_global_cache) { + return true; + } + + // Only the zygote is allowed to create the global boot image. + if (Runtime::Current()->IsZygote()) { + return true; + } + + *error_msg = "Only the zygote can create the global boot image."; + return false; +} + ImageSpace* ImageSpace::Create(const char* image_location, const InstructionSet image_isa, std::string* error_msg) { @@ -352,9 +369,10 @@ ImageSpace* ImageSpace::Create(const char* image_location, std::string cache_filename; bool has_cache = false; bool dalvik_cache_exists = false; + bool is_global_cache = true; const bool found_image = FindImageFilename(image_location, image_isa, &system_filename, &has_system, &cache_filename, &dalvik_cache_exists, - &has_cache); + &has_cache, &is_global_cache); ImageSpace* space; bool relocate = Runtime::Current()->ShouldRelocate(); @@ -377,18 +395,27 @@ ImageSpace* ImageSpace::Create(const char* image_location, relocated_version_used = true; } else { // We cannot have a relocated version, Relocate the system one and use it. - if (can_compile && RelocateImage(image_location, cache_filename.c_str(), image_isa, - error_msg)) { + + std::string reason; + bool success; + + // Check whether we are allowed to relocate. + if (!can_compile) { + reason = "Image dex2oat disabled by -Xnoimage-dex2oat."; + success = false; + } else if (!ImageCreationAllowed(is_global_cache, &reason)) { + // Whether we can write to the cache. + success = false; + } else { + // Try to relocate. + success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &reason); + } + + if (success) { relocated_version_used = true; image_filename = &cache_filename; } else { - std::string reason; - if (can_compile) { - reason = StringPrintf(": %s", error_msg->c_str()); - } else { - reason = " because image dex2oat is disabled."; - } - *error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s'%s", + *error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s': %s", image_location, system_filename.c_str(), cache_filename.c_str(), reason.c_str()); return nullptr; @@ -460,6 +487,8 @@ ImageSpace* ImageSpace::Create(const char* image_location, } else if (!dalvik_cache_exists) { *error_msg = StringPrintf("No place to put generated image."); return nullptr; + } else if (!ImageCreationAllowed(is_global_cache, error_msg)) { + return nullptr; } else if (!GenerateImage(cache_filename, image_isa, error_msg)) { *error_msg = StringPrintf("Failed to generate image '%s': %s", cache_filename.c_str(), error_msg->c_str()); diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 28ebca6e944..2586ece4abd 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -110,7 +110,8 @@ class ImageSpace : public MemMapSpace { bool* has_system, std::string* data_location, bool* dalvik_cache_exists, - bool* has_data); + bool* has_data, + bool *is_global_cache); private: // Tries to initialize an ImageSpace from the given image path, diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 14d6cd950ef..7787a94dcaf 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -504,7 +504,9 @@ static jbyte IsDexOptNeededInternal(JNIEnv* env, const char* filename, std::string cache_dir; bool have_android_data = false; bool dalvik_cache_exists = false; - GetDalvikCache(instruction_set, false, &cache_dir, &have_android_data, &dalvik_cache_exists); + bool is_global_cache = false; + GetDalvikCache(instruction_set, false, &cache_dir, &have_android_data, &dalvik_cache_exists, + &is_global_cache); std::string cache_filename; // was cache_location bool have_cache_filename = false; if (dalvik_cache_exists) { diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 60d641e2b5d..b48fd53a9d3 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -570,13 +570,15 @@ static bool OpenDexFilesFromImage(const std::vector& dex_filenames, std::string cache_filename_unused; bool dalvik_cache_exists_unused; bool has_cache_unused; + bool is_global_cache_unused; bool found_image = gc::space::ImageSpace::FindImageFilename(image_location.c_str(), kRuntimeISA, &system_filename, &has_system, &cache_filename_unused, &dalvik_cache_exists_unused, - &has_cache_unused); + &has_cache_unused, + &is_global_cache_unused); *failures = 0; if (!found_image || !has_system) { return false; diff --git a/runtime/utils.cc b/runtime/utils.cc index c4e84f3473a..d01c2cb7fcf 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1202,13 +1202,14 @@ const char* GetAndroidDataSafe(std::string* error_msg) { } void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string* dalvik_cache, - bool* have_android_data, bool* dalvik_cache_exists) { + bool* have_android_data, bool* dalvik_cache_exists, bool* is_global_cache) { CHECK(subdir != nullptr); std::string error_msg; const char* android_data = GetAndroidDataSafe(&error_msg); if (android_data == nullptr) { *have_android_data = false; *dalvik_cache_exists = false; + *is_global_cache = false; return; } else { *have_android_data = true; @@ -1216,7 +1217,8 @@ void GetDalvikCache(const char* subdir, const bool create_if_absent, std::string const std::string dalvik_cache_root(StringPrintf("%s/dalvik-cache/", android_data)); *dalvik_cache = dalvik_cache_root + subdir; *dalvik_cache_exists = OS::DirectoryExists(dalvik_cache->c_str()); - if (create_if_absent && !*dalvik_cache_exists && strcmp(android_data, "/data") != 0) { + *is_global_cache = strcmp(android_data, "/data") == 0; + if (create_if_absent && !*dalvik_cache_exists && !*is_global_cache) { // Don't create the system's /data/dalvik-cache/... because it needs special permissions. *dalvik_cache_exists = ((mkdir(dalvik_cache_root.c_str(), 0700) == 0 || errno == EEXIST) && (mkdir(dalvik_cache->c_str(), 0700) == 0 || errno == EEXIST)); diff --git a/runtime/utils.h b/runtime/utils.h index 942b6ad0b92..1dfa02a3698 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -451,8 +451,9 @@ std::string GetDalvikCacheOrDie(const char* subdir, bool create_if_absent = true // Return true if we found the dalvik cache and stored it in the dalvik_cache argument. // have_android_data will be set to true if we have an ANDROID_DATA that exists, // dalvik_cache_exists will be true if there is a dalvik-cache directory that is present. +// The flag is_global_cache tells whether this cache is /data/dalvik-cache. void GetDalvikCache(const char* subdir, bool create_if_absent, std::string* dalvik_cache, - bool* have_android_data, bool* dalvik_cache_exists); + bool* have_android_data, bool* dalvik_cache_exists, bool* is_global_cache); // Returns the absolute dalvik-cache path for a DexFile or OatFile. The path returned will be // rooted at cache_location. From 40fc2a8ba2e0b86c7a7c16dcd2eaa8a9985efbbb Mon Sep 17 00:00:00 2001 From: buzbee Date: Fri, 26 Sep 2014 15:09:06 -0700 Subject: [PATCH 027/150] Quick compiler: aarch64 codegen & long_min literal Int64 overflow during instruction selection caused incorrect code patterns to emitted in some cases of long operations with an immediate value of 0x8000000000000000. The code in question was attempting to determine if the immediate operand would fit in aarch64 immediate instruction variants. Internal b/17630605 Change-Id: I8177021b73e51302bc1032387d83b1dd567ed6db --- compiler/dex/quick/arm64/utility_arm64.cc | 6 +++--- test/083-compiler-regressions/expected.txt | 1 + test/083-compiler-regressions/src/Main.java | 13 +++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc index 5326e74e162..f58f83070be 100644 --- a/compiler/dex/quick/arm64/utility_arm64.cc +++ b/compiler/dex/quick/arm64/utility_arm64.cc @@ -345,7 +345,7 @@ bool Arm64Mir2Lir::InexpensiveConstantInt(int32_t value, Instruction::Code opcod case Instruction::SUB_INT_2ADDR: // The code below is consistent with the implementation of OpRegRegImm(). { - int32_t abs_value = std::abs(value); + uint32_t abs_value = (value == INT_MIN) ? value : std::abs(value); if (abs_value < 0x1000) { return true; } else if ((abs_value & UINT64_C(0xfff)) == 0 && ((abs_value >> 12) < 0x1000)) { @@ -809,7 +809,7 @@ LIR* Arm64Mir2Lir::OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, LIR* Arm64Mir2Lir::OpRegRegImm64(OpKind op, RegStorage r_dest, RegStorage r_src1, int64_t value) { LIR* res; bool neg = (value < 0); - int64_t abs_value = (neg) ? -value : value; + uint64_t abs_value = (neg & !(value == LLONG_MIN)) ? -value : value; ArmOpcode opcode = kA64Brk1d; ArmOpcode alt_opcode = kA64Brk1d; bool is_logical = false; @@ -942,7 +942,7 @@ LIR* Arm64Mir2Lir::OpRegImm64(OpKind op, RegStorage r_dest_src1, int64_t value) ArmOpcode neg_opcode = kA64Brk1d; bool shift; bool neg = (value < 0); - uint64_t abs_value = (neg) ? -value : value; + uint64_t abs_value = (neg & !(value == LLONG_MIN)) ? -value : value; if (LIKELY(abs_value < 0x1000)) { // abs_value is a 12-bit immediate. diff --git a/test/083-compiler-regressions/expected.txt b/test/083-compiler-regressions/expected.txt index e907fd1d58a..5251c17335a 100644 --- a/test/083-compiler-regressions/expected.txt +++ b/test/083-compiler-regressions/expected.txt @@ -1,3 +1,4 @@ +b17630605 passes b17411468 passes b2296099 passes b2302318 passes diff --git a/test/083-compiler-regressions/src/Main.java b/test/083-compiler-regressions/src/Main.java index 8d7bf011927..80107117250 100644 --- a/test/083-compiler-regressions/src/Main.java +++ b/test/083-compiler-regressions/src/Main.java @@ -30,6 +30,7 @@ public static long const0x123443211234() { } public static void main(String args[]) throws Exception { + b17630605(); b17411468(); b2296099Test(); b2302318Test(); @@ -62,6 +63,18 @@ public static void main(String args[]) throws Exception { minDoubleWith3ConstsTest(); } + public static void b17630605() { + // b/17630605 - failure to properly handle min long immediates. + long a1 = 40455547223404749L; + long a2 = Long.MIN_VALUE; + long answer = a1 + a2; + if (answer == -9182916489631371059L) { + System.out.println("b17630605 passes"); + } else { + System.out.println("b17630605 fails: " + answer); + } + } + public static void b17411468() { // b/17411468 - inline Math.round failure. double d1 = 1.0; From c4b2f1667fd55ca82a06fe5b09aa55320a350698 Mon Sep 17 00:00:00 2001 From: Dave Allison Date: Tue, 16 Sep 2014 10:01:01 -0700 Subject: [PATCH 028/150] Don't call dlsym from signal context in signal chain It is dangerous to call dlsym from within a signal context since it takes a lock and can lead to a mutex reentry attempt if timing is bad. This change adds an initialization function to the signal chain that calls dlsym for sigaction and sigprocmask from outside the signal context (from Runtime::Init()). The results are cached in a static variable and used from within the signal context if necessary. However, tests don't necessarily call Runtime::Init() so we also need to deal with the case where the signal chain is not initialized and perform a lazy initialization from inside sigaction or sigprocmask. This is always outside a signal context since we have not initialized the runtime. Bug: 17498571, 17896006 (cherry picked from commit cefcea838729287a04174664a76514dd793dd77d) Change-Id: I9bf8540a1250eadf977ff9af249dbe1c73b5ac63 --- runtime/runtime.cc | 6 ++++ sigchainlib/sigchain.cc | 68 ++++++++++++++++++++++++++++++++--------- sigchainlib/sigchain.h | 2 ++ 3 files changed, 62 insertions(+), 14 deletions(-) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 1544aa38c31..ed7b2066f35 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -72,6 +72,7 @@ #include "reflection.h" #include "ScopedLocalRef.h" #include "scoped_thread_state_change.h" +#include "sigchain.h" #include "signal_catcher.h" #include "signal_set.h" #include "handle_scope-inl.h" @@ -747,6 +748,11 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) break; } + // Always initialize the signal chain so that any calls to sigaction get + // correctly routed to the next in the chain regardless of whether we + // have claimed the signal or not. + InitializeSignalChain(); + if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) { fault_manager.Init(); diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index c655226d302..386bbd608fa 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -26,6 +26,8 @@ #include #include +#include "sigchain.h" + #if defined(__APPLE__) #define _NSIG NSIG #endif @@ -71,6 +73,9 @@ class SignalAction { // User's signal handlers static SignalAction user_sigactions[_NSIG]; +static bool initialized; +static void* linked_sigaction_sym; +static void* linked_sigprocmask_sym; static void log(const char* format, ...) { char buf[256]; @@ -92,6 +97,7 @@ static void CheckSignalValid(int signal) { } } + // Claim a signal chain for a particular signal. void ClaimSignalChain(int signal, struct sigaction* oldaction) { CheckSignalValid(signal); @@ -153,14 +159,17 @@ int sigaction(int signal, const struct sigaction* new_action, struct sigaction* // Will only get here if the signal chain has not been claimed. We want // to pass the sigaction on to the kernel via the real sigaction in libc. - void* linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction"); if (linked_sigaction_sym == nullptr) { - linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction"); - if (linked_sigaction_sym == nullptr || - linked_sigaction_sym == reinterpret_cast(sigaction)) { - log("Unable to find next sigaction in signal chain"); - abort(); - } + // Perform lazy initialization. + // This will only occur outside of a signal context since we have + // not been initialized and therefore cannot be within the ART + // runtime. + InitializeSignalChain(); + } + + if (linked_sigaction_sym == nullptr) { + log("Unable to find next sigaction in signal chain"); + abort(); } typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*); @@ -186,14 +195,14 @@ int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_se new_set_ptr = &tmpset; } - void* linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask"); if (linked_sigprocmask_sym == nullptr) { - linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask"); - if (linked_sigprocmask_sym == nullptr || - linked_sigprocmask_sym == reinterpret_cast(sigprocmask)) { - log("Unable to find next sigprocmask in signal chain"); - abort(); - } + // Perform lazy initialization. + InitializeSignalChain(); + } + + if (linked_sigprocmask_sym == nullptr) { + log("Unable to find next sigprocmask in signal chain"); + abort(); } typedef int (*SigProcMask)(int how, const sigset_t*, sigset_t*); @@ -201,5 +210,36 @@ int sigprocmask(int how, const sigset_t* bionic_new_set, sigset_t* bionic_old_se return linked_sigprocmask(how, new_set_ptr, bionic_old_set); } } // extern "C" + +void InitializeSignalChain() { + // Warning. + // Don't call this from within a signal context as it makes calls to + // dlsym. Calling into the dynamic linker will result in locks being + // taken and if it so happens that a signal occurs while one of these + // locks is already taken, dlsym will block trying to reenter a + // mutex and we will never get out of it. + if (initialized) { + // Don't initialize twice. + return; + } + linked_sigaction_sym = dlsym(RTLD_NEXT, "sigaction"); + if (linked_sigaction_sym == nullptr) { + linked_sigaction_sym = dlsym(RTLD_DEFAULT, "sigaction"); + if (linked_sigaction_sym == nullptr || + linked_sigaction_sym == reinterpret_cast(sigaction)) { + linked_sigaction_sym = nullptr; + } + } + + linked_sigprocmask_sym = dlsym(RTLD_NEXT, "sigprocmask"); + if (linked_sigprocmask_sym == nullptr) { + linked_sigprocmask_sym = dlsym(RTLD_DEFAULT, "sigprocmask"); + if (linked_sigprocmask_sym == nullptr || + linked_sigprocmask_sym == reinterpret_cast(sigprocmask)) { + linked_sigprocmask_sym = nullptr; + } + } + initialized = true; +} } // namespace art diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h index a4ce81ce4c3..5bc40268500 100644 --- a/sigchainlib/sigchain.h +++ b/sigchainlib/sigchain.h @@ -21,6 +21,8 @@ namespace art { +void InitializeSignalChain(); + void ClaimSignalChain(int signal, struct sigaction* oldaction); void UnclaimSignalChain(int signal); From d254c2b18fb8d398136dbaf5c7cf2573a88334fd Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 15 Oct 2014 16:59:47 -0700 Subject: [PATCH 029/150] Add way to ensure we are at the front of the sigaction chain Calling this after jni_on_load fixes the unity apps. This is not exactly correct since we may already have the following chain. Start up: Us -> debuggerd After app goes in front: App -> us -> debuggerd After we put ourself back at the front: Us -> app -> us -> app -> .... stack overflow. Bug: 17620677 Change-Id: I9183997e3d5ebd51c320b5d51425be5142e938f3 --- runtime/fault_handler.cc | 26 +++++++++++++++++++------- runtime/fault_handler.h | 2 +- runtime/jni_internal.cc | 4 ++++ sigchainlib/sigchain.cc | 21 ++++++++++++++++++--- sigchainlib/sigchain.h | 2 ++ 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/runtime/fault_handler.cc b/runtime/fault_handler.cc index 25f87c5d48f..56d709ad2a6 100644 --- a/runtime/fault_handler.cc +++ b/runtime/fault_handler.cc @@ -94,16 +94,29 @@ FaultManager::FaultManager() : initialized_(false) { FaultManager::~FaultManager() { } +static void SetUpArtAction(struct sigaction* action) { + action->sa_sigaction = art_fault_handler; + sigemptyset(&action->sa_mask); + action->sa_flags = SA_SIGINFO | SA_ONSTACK; +#if !defined(__APPLE__) && !defined(__mips__) + action->sa_restorer = nullptr; +#endif +} + +void FaultManager::EnsureArtActionInFrontOfSignalChain() { + if (initialized_) { + struct sigaction action; + SetUpArtAction(&action); + EnsureFrontOfChain(SIGSEGV, &action); + } else { + LOG(WARNING) << "Can't call " << __FUNCTION__ << " due to unitialized fault manager"; + } +} void FaultManager::Init() { CHECK(!initialized_); struct sigaction action; - action.sa_sigaction = art_fault_handler; - sigemptyset(&action.sa_mask); - action.sa_flags = SA_SIGINFO | SA_ONSTACK; -#if !defined(__APPLE__) && !defined(__mips__) - action.sa_restorer = nullptr; -#endif + SetUpArtAction(&action); // Set our signal handler now. int e = sigaction(SIGSEGV, &action, &oldaction_); @@ -127,7 +140,6 @@ void FaultManager::HandleFault(int sig, siginfo_t* info, void* context) { // // If malloc calls abort, it will be holding its lock. // If the handler tries to call malloc, it will deadlock. - VLOG(signals) << "Handling fault"; if (IsInGeneratedCode(info, context, true)) { VLOG(signals) << "in generated code, looking for handler"; diff --git a/runtime/fault_handler.h b/runtime/fault_handler.h index bb26780bd9c..89c93607179 100644 --- a/runtime/fault_handler.h +++ b/runtime/fault_handler.h @@ -40,6 +40,7 @@ class FaultManager { void Init(); void Shutdown(); + void EnsureArtActionInFrontOfSignalChain(); void HandleFault(int sig, siginfo_t* info, void* context); void HandleNestedSignal(int sig, siginfo_t* info, void* context); @@ -121,7 +122,6 @@ class JavaStackTraceHandler FINAL : public FaultHandler { DISALLOW_COPY_AND_ASSIGN(JavaStackTraceHandler); }; - // Statically allocated so the the signal handler can Get access to it. extern FaultManager fault_manager; diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 73effca152f..bdda5c87077 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -30,6 +30,7 @@ #include "base/stl_util.h" #include "class_linker-inl.h" #include "dex_file-inl.h" +#include "fault_handler.h" #include "gc_root.h" #include "gc/accounting/card_table-inl.h" #include "indirect_reference_table-inl.h" @@ -3342,6 +3343,9 @@ bool JavaVMExt::LoadNativeLibrary(const std::string& path, version = (*jni_on_load)(this, nullptr); } + if (runtime->GetTargetSdkVersion() != 0 && runtime->GetTargetSdkVersion() <= 21) { + fault_manager.EnsureArtActionInFrontOfSignalChain(); + } self->SetClassLoaderOverride(old_class_loader.Get()); if (version == JNI_ERR) { diff --git a/sigchainlib/sigchain.cc b/sigchainlib/sigchain.cc index 386bbd608fa..4991891cfd8 100644 --- a/sigchainlib/sigchain.cc +++ b/sigchainlib/sigchain.cc @@ -34,6 +34,8 @@ namespace art { +typedef int (*SigActionFnPtr)(int, const struct sigaction*, struct sigaction*); + class SignalAction { public: SignalAction() : claimed_(false) { @@ -138,6 +140,21 @@ void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context) { } } +void EnsureFrontOfChain(int signal, struct sigaction* expected_action) { + CheckSignalValid(signal); + // Read the current action without looking at the chain, it should be the expected action. + SigActionFnPtr linked_sigaction = reinterpret_cast(linked_sigaction_sym); + struct sigaction current_action; + linked_sigaction(signal, nullptr, ¤t_action); + // If the sigactions don't match then we put the current action on the chain and make ourself as + // the main action. + if (current_action.sa_sigaction != expected_action->sa_sigaction) { + log("Warning: Unexpected sigaction action found %p\n", current_action.sa_sigaction); + user_sigactions[signal].Claim(current_action); + linked_sigaction(signal, expected_action, nullptr); + } +} + extern "C" { // These functions are C linkage since they replace the functions in libc. @@ -171,9 +188,7 @@ int sigaction(int signal, const struct sigaction* new_action, struct sigaction* log("Unable to find next sigaction in signal chain"); abort(); } - - typedef int (*SigAction)(int, const struct sigaction*, struct sigaction*); - SigAction linked_sigaction = reinterpret_cast(linked_sigaction_sym); + SigActionFnPtr linked_sigaction = reinterpret_cast(linked_sigaction_sym); return linked_sigaction(signal, new_action, old_action); } diff --git a/sigchainlib/sigchain.h b/sigchainlib/sigchain.h index 5bc40268500..be69ca4d7ec 100644 --- a/sigchainlib/sigchain.h +++ b/sigchainlib/sigchain.h @@ -25,6 +25,8 @@ void InitializeSignalChain(); void ClaimSignalChain(int signal, struct sigaction* oldaction); +void EnsureFrontOfChain(int signal, struct sigaction* expected_action); + void UnclaimSignalChain(int signal); void InvokeUserSignalHandler(int sig, siginfo_t* info, void* context); From 02ccfa40fae7e7e1743d18d15bcb4a62ad27d84f Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Mon, 27 Oct 2014 16:27:06 -0700 Subject: [PATCH 030/150] Remove boot.art/boot.oat on failure to create Bug: 18143314 Change-Id: I5f4733ed7f359158a2bda58ff4f228ba60e2f493 --- runtime/gc/space/image_space.cc | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 59630fe71c8..cd5e196fa1d 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -114,6 +114,18 @@ static void RealPruneDexCache(const std::string& cache_dir_path) { CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(cache_dir))) << "Unable to close directory."; } +static void RemoveImageFiles(const std::string& image_filename, std::string* error_msg) { + if (TEMP_FAILURE_RETRY(unlink(image_filename.c_str())) != 0) { + *error_msg = StringPrintf("Failed to remove image file after previous error: %s", + error_msg->c_str()); + } + std::string oat_filename(ImageHeader::GetOatLocationFromImageLocation(image_filename)); + if (TEMP_FAILURE_RETRY(unlink(oat_filename.c_str())) != 0) { + *error_msg = StringPrintf("Failed to remove oat file after previous error: %s", + error_msg->c_str()); + } +} + static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa, std::string* error_msg) { const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString()); @@ -143,9 +155,7 @@ static bool GenerateImage(const std::string& image_filename, InstructionSet imag } std::string oat_file_option_string("--oat-file="); - oat_file_option_string += image_filename; - oat_file_option_string.erase(oat_file_option_string.size() - 3); - oat_file_option_string += "oat"; + oat_file_option_string += ImageHeader::GetOatLocationFromImageLocation(image_filename); arg_vector.push_back(oat_file_option_string); Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&arg_vector); @@ -434,6 +444,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, *error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s': %s", image_location, system_filename.c_str(), cache_filename.c_str(), reason.c_str()); + RemoveImageFiles(cache_filename, error_msg); return nullptr; } } @@ -508,6 +519,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, } else if (!GenerateImage(cache_filename, image_isa, error_msg)) { *error_msg = StringPrintf("Failed to generate image '%s': %s", cache_filename.c_str(), error_msg->c_str()); + RemoveImageFiles(cache_filename, error_msg); return nullptr; } else { // Note that we must not use the file descriptor associated with From 020110b3df32ef92cca7270a0d617b396c320853 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 24 Oct 2014 21:55:52 -0700 Subject: [PATCH 031/150] ART: Fix ImageSpace error message. Use *error_msg instead of error_msg. (cherry picked from commit 50f2e9a6b935ba1c456a687e5f0822d9356d71e5) Bug: 18143314 Change-Id: I62f6aeda142235eb1a412a4c1ae1104b813e6967 --- runtime/gc/space/image_space.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index cd5e196fa1d..b67257f0db4 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -497,7 +497,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, if (relocated_version_used) { LOG(FATAL) << "Attempted to use relocated version of " << image_location << " " << "at " << cache_filename << " generated from " << system_filename << " " - << "but image failed to load: " << error_msg; + << "but image failed to load: " << *error_msg; return nullptr; } else if (is_system) { *error_msg = StringPrintf("Failed to load /system image '%s': %s", From e016182afa1cd8adf6343cbcc91bde4a866d0880 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 28 Oct 2014 15:53:43 -0700 Subject: [PATCH 032/150] Work around relocated boot.oat corruption Bug: 18143314 Change-Id: Ibecade061d6c887bde2f8a085ed7dede3345125a --- runtime/gc/space/image_space.cc | 40 ++++++++++++++++----------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index b67257f0db4..63b09e7108a 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -114,18 +114,6 @@ static void RealPruneDexCache(const std::string& cache_dir_path) { CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(cache_dir))) << "Unable to close directory."; } -static void RemoveImageFiles(const std::string& image_filename, std::string* error_msg) { - if (TEMP_FAILURE_RETRY(unlink(image_filename.c_str())) != 0) { - *error_msg = StringPrintf("Failed to remove image file after previous error: %s", - error_msg->c_str()); - } - std::string oat_filename(ImageHeader::GetOatLocationFromImageLocation(image_filename)); - if (TEMP_FAILURE_RETRY(unlink(oat_filename.c_str())) != 0) { - *error_msg = StringPrintf("Failed to remove oat file after previous error: %s", - error_msg->c_str()); - } -} - static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa, std::string* error_msg) { const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString()); @@ -444,7 +432,11 @@ ImageSpace* ImageSpace::Create(const char* image_location, *error_msg = StringPrintf("Unable to relocate image '%s' from '%s' to '%s': %s", image_location, system_filename.c_str(), cache_filename.c_str(), reason.c_str()); - RemoveImageFiles(cache_filename, error_msg); + // We failed to create files, remove any possibly garbage output. + // Since ImageCreationAllowed was true above, we are the zygote + // and therefore the only process expected to generate these for + // the device. + PruneDexCache(image_isa); return nullptr; } } @@ -491,19 +483,23 @@ ImageSpace* ImageSpace::Create(const char* image_location, return space; } - // If the /system file exists, it should be up-to-date, don't try to generate it. Same if it is - // a relocated copy from something in /system (i.e. checksum's match). - // Otherwise, log a warning and fall through to GenerateImage. if (relocated_version_used) { - LOG(FATAL) << "Attempted to use relocated version of " << image_location << " " - << "at " << cache_filename << " generated from " << system_filename << " " - << "but image failed to load: " << *error_msg; + // Something is wrong with the relocated copy (even though checksums match). Cleanup. + // This can happen if the .oat is corrupt, since the above only checks the .art checksums. + // TODO: Check the oat file validity earlier. + *error_msg = StringPrintf("Attempted to use relocated version of %s at %s generated from %s " + "but image failed to load: %s", + image_location, cache_filename.c_str(), system_filename.c_str(), + error_msg->c_str()); + PruneDexCache(image_isa); return nullptr; } else if (is_system) { + // If the /system file exists, it should be up-to-date, don't try to generate it. *error_msg = StringPrintf("Failed to load /system image '%s': %s", image_filename->c_str(), error_msg->c_str()); return nullptr; } else { + // Otherwise, log a warning and fall through to GenerateImage. LOG(WARNING) << *error_msg; } } @@ -519,7 +515,11 @@ ImageSpace* ImageSpace::Create(const char* image_location, } else if (!GenerateImage(cache_filename, image_isa, error_msg)) { *error_msg = StringPrintf("Failed to generate image '%s': %s", cache_filename.c_str(), error_msg->c_str()); - RemoveImageFiles(cache_filename, error_msg); + // We failed to create files, remove any possibly garbage output. + // Since ImageCreationAllowed was true above, we are the zygote + // and therefore the only process expected to generate these for + // the device. + PruneDexCache(image_isa); return nullptr; } else { // Note that we must not use the file descriptor associated with From 8db4a405fe283d8769d5f38299d8692c009feb1a Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Fri, 31 Oct 2014 00:01:54 -0700 Subject: [PATCH 033/150] Fix FindDeclaredVirtualMethod(DexCache...) for miranda methods If a class in classes.dex implements an interface from classes2.dex, the miranda method will be in the dex cache for classes2.dex, but pointed to by the virtual methods of the class in the dex caches for classes.dex. Therefore the fast path for DexCache::ResolveMethod that searches via class and superclass virtual methods should ensure that any method matching on dex method_idx should be from the same dex cache as the class itself, which is not the case for miranda methods. Bug: 18193682 Change-Id: I10da4f5472e929b3dc0be58051a726a4bc14e438 --- runtime/mirror/class.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 5b8eb829f51..c10e8b1c8ef 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -499,7 +499,9 @@ ArtMethod* Class::FindDeclaredVirtualMethod(const DexCache* dex_cache, uint32_t if (GetDexCache() == dex_cache) { for (size_t i = 0; i < NumVirtualMethods(); ++i) { ArtMethod* method = GetVirtualMethod(i); - if (method->GetDexMethodIndex() == dex_method_idx) { + if (method->GetDexMethodIndex() == dex_method_idx && + // A miranda method may have a different DexCache. + method->GetDexCache() == dex_cache) { return method; } } From f8e82d44fc2ad23d2b7d672ea561698fb3075050 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 11 Aug 2014 13:37:35 -0700 Subject: [PATCH 034/150] ART Extension Vendor extension hooks. Change-Id: I2b33c2294415bc909084be5a33ddedd91826559a --- compiler/Android.mk | 3 ++ compiler/dex/bb_optimizations.h | 11 ++++++ compiler/dex/compiler_enums.h | 1 + compiler/dex/mir_dataflow.cc | 32 ++++++++--------- compiler/dex/mir_graph.cc | 18 ++++++---- compiler/dex/mir_graph.h | 34 ++++++++++++++++-- compiler/dex/pass_driver.h | 8 +++++ compiler/dex/pass_driver_me_opts.cc | 5 +++ compiler/dex/quick/arm/arm_lir.h | 3 ++ compiler/dex/quick/arm/assemble_arm.cc | 44 +++++++++++++++-------- compiler/dex/quick/arm/codegen_arm.h | 28 +++++++++++++++ compiler/dex/quick/arm/int_arm.cc | 3 +- compiler/dex/quick/arm/target_arm.cc | 37 ++++++++++++++++--- compiler/dex/quick/arm/utility_arm.cc | 19 ++++++---- compiler/dex/quick/codegen_util.cc | 2 ++ compiler/dex/quick/gen_common.cc | 7 ++-- compiler/dex/quick/mir_to_lir-inl.h | 4 ++- compiler/dex/quick/mir_to_lir.cc | 9 +++-- compiler/dex/quick/mir_to_lir.h | 16 ++++++++- compiler/dex/quick/ralloc_util.cc | 21 +++++++++++ compiler/dex/quick/resource_mask.h | 8 +++++ compiler/dex/quick/x86/codegen_x86.h | 3 +- compiler/dex/quick/x86/int_x86.cc | 2 +- compiler/dex/reg_storage.h | 50 ++++++++++++++++++++++++++ compiler/utils/growable_array.h | 1 - disassembler/disassembler_arm.cc | 2 +- runtime/Android.mk | 3 ++ 27 files changed, 310 insertions(+), 64 deletions(-) diff --git a/compiler/Android.mk b/compiler/Android.mk index 69f93870d59..be6535d7149 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -258,6 +258,9 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk ifeq ($$(art_target_or_host),target) LOCAL_SHARED_LIBRARIES += libcutils libvixl + ifeq ($(BOARD_USES_QCOM_HARDWARE),true) + LOCAL_WHOLE_STATIC_LIBRARIES += libqc-art-compiler + endif include $(BUILD_SHARED_LIBRARY) else # host LOCAL_STATIC_LIBRARIES += libcutils libvixl diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h index 7395324ea44..8c3dbd646db 100644 --- a/compiler/dex/bb_optimizations.h +++ b/compiler/dex/bb_optimizations.h @@ -273,6 +273,17 @@ class BBOptimizations : public PassME { void Start(PassDataHolder* data) const; }; +// dummy pass, for placeholder only +class DummyPass : public PassME { + public: + DummyPass() : PassME("DummyPass", kNoNodes, "") { + } + + bool Gate(const PassDataHolder* data) const { + return false; + } +}; + } // namespace art #endif // ART_COMPILER_DEX_BB_OPTIMIZATIONS_H_ diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index dcc67c39865..62be1f3fc7d 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -241,6 +241,7 @@ enum MIROptimizationFlagPositions { kMIRInlinedPred, // Invoke is inlined via prediction. kMIRCallee, // Instruction is inlined from callee. kMIRIgnoreSuspendCheck, + kMIRIgnoreZeroDivCheck, kMIRDup, kMIRMark, // Temporary node mark. kMIRLastMIRFlag, diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc index b82c5c7f00e..fbda48030f4 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -472,10 +472,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, // 93 DIV_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, + DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C | DF_ZERO_DIV_CHECK, // 94 REM_INT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, + DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C | DF_ZERO_DIV_CHECK, // 95 AND_INT vAA, vBB, vCC DF_DA | DF_UB | DF_UC | DF_CORE_A | DF_CORE_B | DF_CORE_C, @@ -505,10 +505,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, // 9E DIV_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, + DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C | DF_ZERO_DIV_CHECK, // 9F REM_LONG vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, + DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C | DF_ZERO_DIV_CHECK, // A0 AND_LONG vAA, vBB, vCC DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_CORE_A | DF_CORE_B | DF_CORE_C, @@ -538,10 +538,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, // A9 DIV_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, + DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C | DF_ZERO_DIV_CHECK, // AA REM_FLOAT vAA, vBB, vCC - DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C, + DF_DA | DF_UB | DF_UC | DF_FP_A | DF_FP_B | DF_FP_C | DF_ZERO_DIV_CHECK, // AB ADD_DOUBLE vAA, vBB, vCC DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, @@ -553,10 +553,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, // AE DIV_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, + DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C | DF_ZERO_DIV_CHECK, // AF REM_DOUBLE vAA, vBB, vCC - DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C, + DF_DA | DF_A_WIDE | DF_UB | DF_B_WIDE | DF_UC | DF_C_WIDE | DF_FP_A | DF_FP_B | DF_FP_C | DF_ZERO_DIV_CHECK, // B0 ADD_INT_2ADDR vA, vB DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, @@ -568,10 +568,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, // B3 DIV_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, + DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B | DF_ZERO_DIV_CHECK, // B4 REM_INT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, + DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B | DF_ZERO_DIV_CHECK, // B5 AND_INT_2ADDR vA, vB DF_DA | DF_UA | DF_UB | DF_CORE_A | DF_CORE_B, @@ -601,10 +601,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, // BE DIV_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, + DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B | DF_ZERO_DIV_CHECK, // BF REM_LONG_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, + DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B | DF_ZERO_DIV_CHECK, // C0 AND_LONG_2ADDR vA, vB DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_CORE_A | DF_CORE_B, @@ -634,10 +634,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, // C9 DIV_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, + DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B | DF_ZERO_DIV_CHECK, // CA REM_FLOAT_2ADDR vA, vB - DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B, + DF_DA | DF_UA | DF_UB | DF_FP_A | DF_FP_B | DF_ZERO_DIV_CHECK, // CB ADD_DOUBLE_2ADDR vA, vB DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, @@ -649,10 +649,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, // CE DIV_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, + DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B | DF_ZERO_DIV_CHECK, // CF REM_DOUBLE_2ADDR vA, vB - DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B, + DF_DA | DF_A_WIDE | DF_UA | DF_UB | DF_B_WIDE | DF_FP_A | DF_FP_B | DF_ZERO_DIV_CHECK, // D0 ADD_INT_LIT16 vA, vB, #+CCCC DF_DA | DF_UB | DF_CORE_A | DF_CORE_B, diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 6aee56373c3..885d413929d 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -124,7 +124,8 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) ifield_lowering_infos_(arena, 0u), sfield_lowering_infos_(arena, 0u), method_lowering_infos_(arena, 0u), - gen_suspend_test_list_(arena, 0u) { + gen_suspend_test_list_(arena, 0u), + qcm(nullptr) { try_block_addr_ = new (arena_) ArenaBitVector(arena_, 0, true /* expandable */); max_available_special_compiler_temps_ = std::abs(static_cast(kVRegNonSpecialTempBaseReg)) - std::abs(static_cast(kVRegTempBaseReg)); @@ -867,6 +868,11 @@ uint64_t MIRGraph::GetDataFlowAttributes(MIR* mir) { return GetDataFlowAttributes(opcode); } + +const char * MIRGraph::GetExtendedMirOpName(int index){ + return extended_mir_op_names_[index]; +} + // TODO: use a configurable base prefix, and adjust callers to supply pass name. /* Dump the CFG into a DOT graph */ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suffix) { @@ -914,7 +920,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suff if (opcode > kMirOpSelect && opcode < kMirOpLast) { if (opcode == kMirOpConstVector) { fprintf(file, " {%04x %s %d %d %d %d %d %d\\l}%s\\\n", mir->offset, - extended_mir_op_names_[kMirOpConstVector - kMirOpFirst], + MIRGraph::GetExtendedMirOpName(kMirOpConstVector - kMirOpFirst), mir->dalvikInsn.vA, mir->dalvikInsn.vB, mir->dalvikInsn.arg[0], @@ -924,7 +930,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suff mir->next ? " | " : " "); } else { fprintf(file, " {%04x %s %d %d %d\\l}%s\\\n", mir->offset, - extended_mir_op_names_[opcode - kMirOpFirst], + MIRGraph::GetExtendedMirOpName(opcode - kMirOpFirst), mir->dalvikInsn.vA, mir->dalvikInsn.vB, mir->dalvikInsn.vC, @@ -935,7 +941,7 @@ void MIRGraph::DumpCFG(const char* dir_prefix, bool all_blocks, const char *suff mir->ssa_rep ? GetDalvikDisassembly(mir) : !MIR::DecodedInstruction::IsPseudoMirOp(opcode) ? Instruction::Name(mir->dalvikInsn.opcode) : - extended_mir_op_names_[opcode - kMirOpFirst], + MIRGraph::GetExtendedMirOpName(opcode - kMirOpFirst), (mir->optimization_flags & MIR_IGNORE_RANGE_CHECK) != 0 ? " no_rangecheck" : " ", (mir->optimization_flags & MIR_IGNORE_NULL_CHECK) != 0 ? " no_nullcheck" : " ", (mir->optimization_flags & MIR_IGNORE_SUSPEND_CHECK) != 0 ? " no_suspendcheck" : " ", @@ -1224,7 +1230,7 @@ char* MIRGraph::GetDalvikDisassembly(const MIR* mir) { // Handle special cases. if ((opcode == kMirOpCheck) || (opcode == kMirOpCheckPart2)) { - str.append(extended_mir_op_names_[opcode - kMirOpFirst]); + str.append(MIRGraph::GetExtendedMirOpName(opcode - kMirOpFirst)); str.append(": "); // Recover the original Dex instruction. insn = mir->meta.throw_insn->dalvikInsn; @@ -1241,7 +1247,7 @@ char* MIRGraph::GetDalvikDisassembly(const MIR* mir) { } if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { - str.append(extended_mir_op_names_[opcode - kMirOpFirst]); + str.append(MIRGraph::GetExtendedMirOpName(opcode - kMirOpFirst)); } else { dalvik_format = Instruction::FormatOf(insn.opcode); flags = Instruction::FlagsOf(insn.opcode); diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index 5817f921469..d990dc6bf0d 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -32,6 +32,12 @@ #include "reg_location.h" #include "reg_storage.h" +#ifdef QC_STRONG +#define QC_WEAK +#else +#define QC_WEAK __attribute__((weak)) +#endif + namespace art { class GlobalValueNumbering; @@ -107,6 +113,7 @@ enum DataFlowAttributePos { kUsesIField, // Accesses an instance field (IGET/IPUT). kUsesSField, // Accesses a static field (SGET/SPUT). kDoLVN, // Worth computing local value numbers. + kZeroDivCheck, // check for zero divider }; #define DF_NOP UINT64_C(0) @@ -146,6 +153,7 @@ enum DataFlowAttributePos { #define DF_IFIELD (UINT64_C(1) << kUsesIField) #define DF_SFIELD (UINT64_C(1) << kUsesSField) #define DF_LVN (UINT64_C(1) << kDoLVN) +#define DF_ZERO_DIV_CHECK (UINT64_C(1) << kZeroDivCheck) #define DF_HAS_USES (DF_UA | DF_UB | DF_UC) @@ -193,6 +201,7 @@ enum OatMethodAttributes { #define MIR_INLINED_PRED (1 << kMIRInlinedPred) #define MIR_CALLEE (1 << kMIRCallee) #define MIR_IGNORE_SUSPEND_CHECK (1 << kMIRIgnoreSuspendCheck) +#define MIR_IGNORE_ZERO_DIV_CHECK (1 << kMIRIgnoreZeroDivCheck) #define MIR_DUP (1 << kMIRDup) #define BLOCK_NAME_LEN 80 @@ -261,6 +270,8 @@ struct SSARepresentation { static uint32_t GetStartUseIndex(Instruction::Code opcode); }; +struct ExtendedMIR; + /* * The Midlevel Intermediate Representation node, which may be largely considered a * wrapper around a Dalvik byte code. @@ -369,7 +380,7 @@ struct MIR { } meta; explicit MIR():offset(0), optimization_flags(0), m_unit_index(0), bb(NullBasicBlockId), - next(nullptr), ssa_rep(nullptr) { + next(nullptr), ssa_rep(nullptr) , extraData(nullptr){ memset(&meta, 0, sizeof(meta)); } @@ -384,6 +395,9 @@ struct MIR { return arena->Alloc(sizeof(MIR), kArenaAllocMIR); } static void operator delete(void* p) {} // Nop. + + ExtendedMIR* extraData; + }; struct SuccessorBlockInfo; @@ -536,6 +550,9 @@ struct CallInfo { const RegLocation bad_loc = {kLocDalvikFrame, 0, 0, 0, 0, 0, 0, 0, 0, RegStorage(), INVALID_SREG, INVALID_SREG}; + +class QCMIRGraph; + class MIRGraph { public: MIRGraph(CompilationUnit* cu, ArenaAllocator* arena); @@ -732,6 +749,12 @@ class MIRGraph { Low32Bits(static_cast(constant_values_[loc.orig_sreg])); } + int64_t ConstantValueWide(int32_t s_reg) const { + DCHECK(IsConst(s_reg)); + return (static_cast(constant_values_[s_reg + 1]) << 32) | + Low32Bits(static_cast(constant_values_[s_reg])); + } + bool IsConstantNullRef(RegLocation loc) const { return loc.ref && loc.is_const && (ConstantValue(loc) == 0); } @@ -1025,7 +1048,7 @@ class MIRGraph { */ void CountUses(struct BasicBlock* bb); - static uint64_t GetDataFlowAttributes(Instruction::Code opcode); + static uint64_t GetDataFlowAttributes(Instruction::Code opcode) QC_WEAK; static uint64_t GetDataFlowAttributes(MIR* mir); /** @@ -1060,6 +1083,7 @@ class MIRGraph { static const char* extended_mir_op_names_[kMirOpLast - kMirOpFirst]; static const uint32_t analysis_attributes_[kMirOpLast]; + static const char * GetExtendedMirOpName(int index) QC_WEAK; void HandleSSADef(int* defs, int dalvik_reg, int reg_index); bool InferTypeAndSize(BasicBlock* bb, MIR* mir, bool changed); @@ -1205,6 +1229,12 @@ class MIRGraph { friend class GlobalValueNumberingTest; friend class LocalValueNumberingTest; friend class TopologicalSortOrderTest; + + friend class QCMIRGraph; + + public: + QCMIRGraph* qcm; + }; } // namespace art diff --git a/compiler/dex/pass_driver.h b/compiler/dex/pass_driver.h index bd8f53cd5a1..bc5913c0431 100644 --- a/compiler/dex/pass_driver.h +++ b/compiler/dex/pass_driver.h @@ -21,6 +21,12 @@ #include "pass.h" #include "safe_map.h" +#ifdef QC_STRONG +#define QC_WEAK +#else +#define QC_WEAK __attribute__((weak)) +#endif + // Forward Declarations. class Pass; class PassDriver; @@ -35,6 +41,8 @@ const Pass* GetPassInstance() { return &pass; } +const Pass* GetMorePassInstance() QC_WEAK; + // Empty holder for the constructor. class PassDriverDataHolder { }; diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc index c72a4a667e0..d003cf918df 100644 --- a/compiler/dex/pass_driver_me_opts.cc +++ b/compiler/dex/pass_driver_me_opts.cc @@ -23,6 +23,10 @@ namespace art { +const Pass* GetMorePassInstance() { + static const DummyPass pass; + return &pass; +} /* * Create the pass list. These passes are immutable and are shared across the threads. * @@ -42,6 +46,7 @@ const Pass* const PassDriver::g_passes[] = { GetPassInstance(), GetPassInstance(), GetPassInstance(), + GetMorePassInstance(), }; // The number of the passes in the initial list of Passes (g_passes). diff --git a/compiler/dex/quick/arm/arm_lir.h b/compiler/dex/quick/arm/arm_lir.h index 6272555983c..a54e8b7e33a 100644 --- a/compiler/dex/quick/arm/arm_lir.h +++ b/compiler/dex/quick/arm/arm_lir.h @@ -542,6 +542,9 @@ enum ArmOpcode { kThumb2LdrdPcRel8, // ldrd rt, rt2, pc +-/1024. kThumb2LdrdI8, // ldrd rt, rt2, [rn +-/1024]. kThumb2StrdI8, // strd rt, rt2, [rn +-/1024]. + + kThumb2Mls, + kArmLast, }; diff --git a/compiler/dex/quick/arm/assemble_arm.cc b/compiler/dex/quick/arm/assemble_arm.cc index 35c3597491e..12d06eb6664 100644 --- a/compiler/dex/quick/arm/assemble_arm.cc +++ b/compiler/dex/quick/arm/assemble_arm.cc @@ -1035,6 +1035,10 @@ const ArmEncodingMap ArmMir2Lir::EncodingMap[kArmLast] = { kFmtBitBlt, 7, 0, IS_QUAD_OP | REG_USE0 | REG_USE1 | REG_USE2 | IS_STORE_OFF4, "strd", "!0C, !1C, [!2C, #!3E]", 4, kFixupNone), + ENCODING_MAP(kThumb2Mls, 0xfb000010, + kFmtBitBlt, 11, 8, kFmtBitBlt, 19, 16, kFmtBitBlt, 3, 0, + kFmtBitBlt, 15, 12, IS_QUAD_OP | REG_DEF0 | REG_USE1 | REG_USE2 | REG_USE3, + "mls", "!0C, !1C, !2C, !3C", 4, kFixupNone), }; // new_lir replaces orig_lir in the pcrel_fixup list. @@ -1067,6 +1071,17 @@ void ArmMir2Lir::InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) { */ #define PADDING_MOV_R5_R5 0x1C2D +uint32_t ArmMir2Lir::ProcessMoreEncodings(const ArmEncodingMap* encoder, int i, uint32_t operand) { + LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind; + uint32_t value=0; + return value; +} + +const ArmEncodingMap * ArmMir2Lir::GetEncoder(int opcode) { + const ArmEncodingMap *encoder = &EncodingMap[opcode]; + return encoder; +} + uint8_t* ArmMir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { for (; lir != NULL; lir = NEXT_LIR(lir)) { if (!lir->flags.is_nop) { @@ -1081,7 +1096,7 @@ uint8_t* ArmMir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { } } } else if (LIKELY(!lir->flags.is_nop)) { - const ArmEncodingMap *encoder = &EncodingMap[lir->opcode]; + const ArmEncodingMap *encoder = GetEncoder(lir->opcode); uint32_t bits = encoder->skeleton; for (int i = 0; i < 4; i++) { uint32_t operand; @@ -1184,7 +1199,8 @@ uint8_t* ArmMir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { } break; default: - LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind; + bits |= ProcessMoreEncodings(encoder, i, operand); + break; } } } @@ -1294,7 +1310,7 @@ void ArmMir2Lir::AssembleLIR() { base_reg, 0, 0, 0, 0, lir->target); new_adr->offset = lir->offset; new_adr->flags.fixup = kFixupAdr; - new_adr->flags.size = EncodingMap[kThumb2Adr].size; + new_adr->flags.size = GetEncoder(kThumb2Adr)->size; InsertLIRBefore(lir, new_adr); lir->offset += new_adr->flags.size; offset_adjustment += new_adr->flags.size; @@ -1309,7 +1325,7 @@ void ArmMir2Lir::AssembleLIR() { } else if (lir->opcode == kThumb2LdrdPcRel8) { lir->opcode = kThumb2LdrdI8; } - lir->flags.size = EncodingMap[lir->opcode].size; + lir->flags.size = GetEncoder(lir->opcode)->size; offset_adjustment += lir->flags.size; // Change the load to be relative to the new Adr base. if (lir->opcode == kThumb2LdrdI8) { @@ -1359,13 +1375,13 @@ void ArmMir2Lir::AssembleLIR() { /* operand[0] is src1 in both cb[n]z & CmpRI8 */ lir->operands[1] = 0; lir->target = 0; - lir->flags.size = EncodingMap[lir->opcode].size; + lir->flags.size = GetEncoder(lir->opcode)->size; // Add back the new size. offset_adjustment += lir->flags.size; // Set up the new following inst. new_inst->offset = lir->offset + lir->flags.size; new_inst->flags.fixup = kFixupCondBranch; - new_inst->flags.size = EncodingMap[new_inst->opcode].size; + new_inst->flags.size = GetEncoder(new_inst->opcode)->size; offset_adjustment += new_inst->flags.size; // lir no longer pcrel, unlink and link in new_inst. @@ -1415,7 +1431,7 @@ void ArmMir2Lir::AssembleLIR() { if ((lir->opcode == kThumbBCond) && (delta > 254 || delta < -256)) { offset_adjustment -= lir->flags.size; lir->opcode = kThumb2BCond; - lir->flags.size = EncodingMap[lir->opcode].size; + lir->flags.size = GetEncoder(lir->opcode)->size; // Fixup kind remains the same. offset_adjustment += lir->flags.size; res = kRetryAll; @@ -1451,7 +1467,7 @@ void ArmMir2Lir::AssembleLIR() { offset_adjustment -= lir->flags.size; lir->opcode = kThumb2BUncond; lir->operands[0] = 0; - lir->flags.size = EncodingMap[lir->opcode].size; + lir->flags.size = GetEncoder(lir->opcode)->size; lir->flags.fixup = kFixupT2Branch; offset_adjustment += lir->flags.size; res = kRetryAll; @@ -1513,7 +1529,7 @@ void ArmMir2Lir::AssembleLIR() { LIR *new_mov16L = RawLIR(lir->dalvik_offset, kThumb2MovImm16LST, lir->operands[0], 0, WrapPointer(lir), WrapPointer(tab_rec), 0, lir->target); - new_mov16L->flags.size = EncodingMap[new_mov16L->opcode].size; + new_mov16L->flags.size = GetEncoder(new_mov16L->opcode)->size; new_mov16L->flags.fixup = kFixupMovImmLST; new_mov16L->offset = lir->offset; // Link the new instruction, retaining lir. @@ -1525,7 +1541,7 @@ void ArmMir2Lir::AssembleLIR() { LIR *new_mov16H = RawLIR(lir->dalvik_offset, kThumb2MovImm16HST, lir->operands[0], 0, WrapPointer(lir), WrapPointer(tab_rec), 0, lir->target); - new_mov16H->flags.size = EncodingMap[new_mov16H->opcode].size; + new_mov16H->flags.size = GetEncoder(new_mov16H->opcode)->size; new_mov16H->flags.fixup = kFixupMovImmHST; new_mov16H->offset = lir->offset; // Link the new instruction, retaining lir. @@ -1542,7 +1558,7 @@ void ArmMir2Lir::AssembleLIR() { lir->opcode = kThumbAddRRHH; } lir->operands[1] = rs_rARM_PC.GetReg(); - lir->flags.size = EncodingMap[lir->opcode].size; + lir->flags.size = GetEncoder(lir->opcode)->size; offset_adjustment += lir->flags.size; // Must stay in fixup list and have offset updated; will be used by LST/HSP pair. lir->flags.fixup = kFixupNone; @@ -1630,7 +1646,7 @@ void ArmMir2Lir::AssembleLIR() { size_t ArmMir2Lir::GetInsnSize(LIR* lir) { DCHECK(!IsPseudoLirOp(lir->opcode)); - return EncodingMap[lir->opcode].size; + return GetEncoder(lir->opcode)->size; } // Encode instruction bit pattern and assign offsets. @@ -1642,8 +1658,8 @@ uint32_t ArmMir2Lir::LinkFixupInsns(LIR* head_lir, LIR* tail_lir, uint32_t offse if (!lir->flags.is_nop) { if (lir->flags.fixup != kFixupNone) { if (!IsPseudoLirOp(lir->opcode)) { - lir->flags.size = EncodingMap[lir->opcode].size; - lir->flags.fixup = EncodingMap[lir->opcode].fixup; + lir->flags.size = GetEncoder(lir->opcode)->size; + lir->flags.fixup = GetEncoder(lir->opcode)->fixup; } else if (UNLIKELY(lir->opcode == kPseudoPseudoAlign4)) { lir->flags.size = (offset & 0x2); lir->flags.fixup = kFixupAlign4; diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index cd6c9cc1e15..32149c900fc 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -20,8 +20,15 @@ #include "arm_lir.h" #include "dex/compiler_internals.h" +#ifdef QC_STRONG +#define QC_WEAK +#else +#define QC_WEAK __attribute__((weak)) +#endif + namespace art { +class QCArmMir2Lir; class ArmMir2Lir FINAL : public Mir2Lir { public: ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); @@ -147,6 +154,7 @@ class ArmMir2Lir FINAL : public Mir2Lir { LIR* OpMem(OpKind op, RegStorage r_base, int disp); LIR* OpPcRelLoad(RegStorage reg, LIR* target); LIR* OpReg(OpKind op, RegStorage r_dest_src); + LIR* OpBkpt(); void OpRegCopy(RegStorage r_dest, RegStorage r_src); LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src); LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value); @@ -186,6 +194,9 @@ class ArmMir2Lir FINAL : public Mir2Lir { LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE; size_t GetInstructionOffset(LIR* lir); + void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir); + void GenMoreMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) QC_WEAK; + //void MachineSpecificPreprocessMIR(BasicBlock* bb, MIR* mir); private: void GenNegLong(RegLocation rl_dest, RegLocation rl_src); @@ -211,9 +222,26 @@ class ArmMir2Lir FINAL : public Mir2Lir { bool GetEasyMultiplyTwoOps(int lit, EasyMultiplyOp* ops); void GenEasyMultiplyTwoOps(RegStorage r_dest, RegStorage r_src, EasyMultiplyOp* ops); + + + static uint32_t ProcessMoreEncodings(const ArmEncodingMap* encoder, int i, uint32_t operand) QC_WEAK; + + static const ArmEncodingMap * GetEncoder(int opcode) QC_WEAK; + static constexpr ResourceMask GetRegMaskArm(RegStorage reg); static constexpr ResourceMask EncodeArmRegList(int reg_list); static constexpr ResourceMask EncodeArmRegFpcsList(int reg_list); + + virtual void ApplyArchOptimizations(LIR* head_lir, LIR* tail_lir, BasicBlock* bb) QC_WEAK; + + void CompilerPostInitializeRegAlloc() QC_WEAK; + void ArmMir2LirPostInit(ArmMir2Lir* mir_to_lir) QC_WEAK; + + friend class QCArmMir2Lir; + + public: + QCArmMir2Lir * qcm2l ; + }; } // namespace art diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc index 0de2a445d02..4a35c2ef252 100644 --- a/compiler/dex/quick/arm/int_arm.cc +++ b/compiler/dex/quick/arm/int_arm.cc @@ -712,8 +712,7 @@ RegLocation ArmMir2Lir::GenDivRem(RegLocation rl_dest, RegStorage reg1, RegStora RegStorage temp = AllocTemp(); OpRegRegReg(kOpDiv, temp, reg1, reg2); - OpRegReg(kOpMul, temp, reg2); - OpRegRegReg(kOpSub, rl_result.reg, reg1, temp); + NewLIR4(kThumb2Mls, rl_result.reg.GetReg(), temp.GetReg(), reg2.GetReg(), reg1.GetReg()); FreeTemp(temp); } diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 0509ad3f763..7c280eca4a8 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -139,11 +139,18 @@ ResourceMask ArmMir2Lir::GetRegMaskCommon(const RegStorage& reg) const { return GetRegMaskArm(reg); } +void ArmMir2Lir::CompilerPostInitializeRegAlloc() +{ + //nothing here +} + constexpr ResourceMask ArmMir2Lir::GetRegMaskArm(RegStorage reg) { - return reg.IsDouble() + return (reg.IsQuad()) + ? (ResourceMask::FourBits((reg.GetRegNum() * 4) + kArmFPReg0)) + : (reg.IsDouble() /* Each double register is equal to a pair of single-precision FP registers */ ? ResourceMask::TwoBits(reg.GetRegNum() * 2 + kArmFPReg0) - : ResourceMask::Bit(reg.IsSingle() ? reg.GetRegNum() + kArmFPReg0 : reg.GetRegNum()); + : ResourceMask::Bit(reg.IsSingle() ? reg.GetRegNum() + kArmFPReg0 : reg.GetRegNum())); } constexpr ResourceMask ArmMir2Lir::EncodeArmRegList(int reg_list) { @@ -559,6 +566,11 @@ ArmMir2Lir::ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* << static_cast(ArmMir2Lir::EncodingMap[i].opcode); } } + qcm2l = nullptr; + ArmMir2LirPostInit(this); +} + +void ArmMir2Lir::ArmMir2LirPostInit(ArmMir2Lir* mir_to_lir) { } Mir2Lir* ArmCodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, @@ -606,6 +618,8 @@ void ArmMir2Lir::CompilerInitializeRegAlloc() { reg_pool_->next_core_reg_ = 2; reg_pool_->next_sp_reg_ = 0; reg_pool_->next_dp_reg_ = 0; + + CompilerPostInitializeRegAlloc(); } /* @@ -734,17 +748,17 @@ LIR* ArmMir2Lir::CheckSuspendUsingLoad() { uint64_t ArmMir2Lir::GetTargetInstFlags(int opcode) { DCHECK(!IsPseudoLirOp(opcode)); - return ArmMir2Lir::EncodingMap[opcode].flags; + return GetEncoder(opcode)->flags; } const char* ArmMir2Lir::GetTargetInstName(int opcode) { DCHECK(!IsPseudoLirOp(opcode)); - return ArmMir2Lir::EncodingMap[opcode].name; + return GetEncoder(opcode)->name; } const char* ArmMir2Lir::GetTargetInstFmt(int opcode) { DCHECK(!IsPseudoLirOp(opcode)); - return ArmMir2Lir::EncodingMap[opcode].fmt; + return GetEncoder(opcode)->fmt; } /* @@ -824,4 +838,17 @@ RegStorage ArmMir2Lir::AllocPreservedSingle(int s_reg) { return res; } +void ArmMir2Lir::GenMoreMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir){ + // nothing here +} + +void ArmMir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) { + + GenMoreMachineSpecificExtendedMethodMIR(bb, mir); +} + + +void ArmMir2Lir::ApplyArchOptimizations(LIR* head_lir, LIR* tail_lir, BasicBlock* bb) { +} + } // namespace art diff --git a/compiler/dex/quick/arm/utility_arm.cc b/compiler/dex/quick/arm/utility_arm.cc index bba1a8c65b4..1537eff852b 100644 --- a/compiler/dex/quick/arm/utility_arm.cc +++ b/compiler/dex/quick/arm/utility_arm.cc @@ -235,6 +235,11 @@ LIR* ArmMir2Lir::OpReg(OpKind op, RegStorage r_dest_src) { return NewLIR1(opcode, r_dest_src.GetReg()); } +LIR* ArmMir2Lir::OpBkpt() { + LOG(ERROR) << "Inserting breakpoint"; + return NewLIR0(kThumbBkpt); +} + LIR* ArmMir2Lir::OpRegRegShift(OpKind op, RegStorage r_dest_src1, RegStorage r_src2, int shift) { bool thumb_form = @@ -352,15 +357,15 @@ LIR* ArmMir2Lir::OpRegRegShift(OpKind op, RegStorage r_dest_src1, RegStorage r_s break; } DCHECK(!IsPseudoLirOp(opcode)); - if (EncodingMap[opcode].flags & IS_BINARY_OP) { + if (GetEncoder(opcode)->flags & IS_BINARY_OP) { return NewLIR2(opcode, r_dest_src1.GetReg(), r_src2.GetReg()); - } else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) { - if (EncodingMap[opcode].field_loc[2].kind == kFmtShift) { + } else if (GetEncoder(opcode)->flags & IS_TERTIARY_OP) { + if (GetEncoder(opcode)->field_loc[2].kind == kFmtShift) { return NewLIR3(opcode, r_dest_src1.GetReg(), r_src2.GetReg(), shift); } else { return NewLIR3(opcode, r_dest_src1.GetReg(), r_dest_src1.GetReg(), r_src2.GetReg()); } - } else if (EncodingMap[opcode].flags & IS_QUAD_OP) { + } else if (GetEncoder(opcode)->flags & IS_QUAD_OP) { return NewLIR4(opcode, r_dest_src1.GetReg(), r_dest_src1.GetReg(), r_src2.GetReg(), shift); } else { LOG(FATAL) << "Unexpected encoding operand count"; @@ -448,10 +453,10 @@ LIR* ArmMir2Lir::OpRegRegRegShift(OpKind op, RegStorage r_dest, RegStorage r_src break; } DCHECK(!IsPseudoLirOp(opcode)); - if (EncodingMap[opcode].flags & IS_QUAD_OP) { + if (GetEncoder(opcode)->flags & IS_QUAD_OP) { return NewLIR4(opcode, r_dest.GetReg(), r_src1.GetReg(), r_src2.GetReg(), shift); } else { - DCHECK(EncodingMap[opcode].flags & IS_TERTIARY_OP); + DCHECK(GetEncoder(opcode)->flags & IS_TERTIARY_OP); return NewLIR3(opcode, r_dest.GetReg(), r_src1.GetReg(), r_src2.GetReg()); } } @@ -587,7 +592,7 @@ LIR* ArmMir2Lir::OpRegRegImm(OpKind op, RegStorage r_dest, RegStorage r_src1, in } else { RegStorage r_scratch = AllocTemp(); LoadConstant(r_scratch, value); - if (EncodingMap[alt_opcode].flags & IS_QUAD_OP) + if (GetEncoder(alt_opcode)->flags & IS_QUAD_OP) res = NewLIR4(alt_opcode, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg(), 0); else res = NewLIR3(alt_opcode, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg()); diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index ee1c46797c8..b1f3527e5a8 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -1036,6 +1036,8 @@ void Mir2Lir::Materialize() { CodegenDump(); } } + + Cleanup(); } CompiledMethod* Mir2Lir::GetCompiledMethod() { diff --git a/compiler/dex/quick/gen_common.cc b/compiler/dex/quick/gen_common.cc index f6c77fcea8a..0dcab991ecd 100644 --- a/compiler/dex/quick/gen_common.cc +++ b/compiler/dex/quick/gen_common.cc @@ -1386,7 +1386,8 @@ void Mir2Lir::GenShiftOpLong(Instruction::Code opcode, RegLocation rl_dest, void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2) { + RegLocation rl_src1, RegLocation rl_src2, + int opt_flags) { DCHECK(cu_->instruction_set != kX86 && cu_->instruction_set != kX86_64); OpKind op = kOpBkpt; bool is_div_rem = false; @@ -1417,14 +1418,14 @@ void Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, break; case Instruction::DIV_INT: case Instruction::DIV_INT_2ADDR: - check_zero = true; + check_zero = (opt_flags & MIR_IGNORE_ZERO_DIV_CHECK) ? false : true; op = kOpDiv; is_div_rem = true; break; /* NOTE: returns in kArg1 */ case Instruction::REM_INT: case Instruction::REM_INT_2ADDR: - check_zero = true; + check_zero = (opt_flags & MIR_IGNORE_ZERO_DIV_CHECK) ? false : true; op = kOpRem; is_div_rem = true; break; diff --git a/compiler/dex/quick/mir_to_lir-inl.h b/compiler/dex/quick/mir_to_lir-inl.h index 2e4e2921bd2..941dd7f33d0 100644 --- a/compiler/dex/quick/mir_to_lir-inl.h +++ b/compiler/dex/quick/mir_to_lir-inl.h @@ -143,7 +143,9 @@ inline LIR* Mir2Lir::NewLIR5(int opcode, int dest, int src1, int src2, int info1 inline void Mir2Lir::SetupRegMask(ResourceMask* mask, int reg) { DCHECK_EQ((reg & ~RegStorage::kRegValMask), 0); DCHECK(reginfo_map_.Get(reg) != nullptr) << "No info for 0x" << reg; - *mask = mask->Union(reginfo_map_.Get(reg)->DefUseMask()); + if (reginfo_map_.Get(reg)) { + *mask = mask->Union(reginfo_map_.Get(reg)->DefUseMask()); + } } /* diff --git a/compiler/dex/quick/mir_to_lir.cc b/compiler/dex/quick/mir_to_lir.cc index e5190118eb4..f4236ddec2e 100644 --- a/compiler/dex/quick/mir_to_lir.cc +++ b/compiler/dex/quick/mir_to_lir.cc @@ -869,7 +869,7 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list case Instruction::NEG_INT: case Instruction::NOT_INT: - GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[0]); + GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[0], opt_flags); break; case Instruction::NEG_LONG: @@ -934,7 +934,7 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list GenArithOpIntLit(opcode, rl_dest, rl_src[0], mir_graph_->ConstantValue(rl_src[1].orig_sreg)); } else { - GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1]); + GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1], opt_flags); } break; @@ -954,7 +954,7 @@ void Mir2Lir::CompileDalvikInstruction(MIR* mir, BasicBlock* bb, LIR* label_list InexpensiveConstantInt(mir_graph_->ConstantValue(rl_src[1]), opcode)) { GenArithOpIntLit(opcode, rl_dest, rl_src[0], mir_graph_->ConstantValue(rl_src[1])); } else { - GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1]); + GenArithOpInt(opcode, rl_dest, rl_src[0], rl_src[1], opt_flags); } break; @@ -1178,6 +1178,7 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { work_half->meta.throw_insn = mir; } + MachineSpecificPreprocessMIR(bb, mir); if (MIR::DecodedInstruction::IsPseudoMirOp(opcode)) { HandleExtendedMethodMIR(bb, mir); continue; @@ -1189,6 +1190,8 @@ bool Mir2Lir::MethodBlockCodeGen(BasicBlock* bb) { if (head_lir) { // Eliminate redundant loads/stores and delay stores into later slots. ApplyLocalOptimizations(head_lir, last_lir_insn_); + // Apply architecture-specific optimizations + ApplyArchOptimizations(head_lir, last_lir_insn_, bb); } return false; } diff --git a/compiler/dex/quick/mir_to_lir.h b/compiler/dex/quick/mir_to_lir.h index 3dc111f8a38..35a352a28cd 100644 --- a/compiler/dex/quick/mir_to_lir.h +++ b/compiler/dex/quick/mir_to_lir.h @@ -149,6 +149,7 @@ class DexFileMethodInliner; class MIRGraph; class Mir2Lir; + typedef int (*NextCallInsn)(CompilationUnit*, CallInfo*, int, const MethodReference& target_method, uint32_t method_idx, uintptr_t direct_code, @@ -348,6 +349,8 @@ class Mir2Lir : public Backend { static const uint32_t kLowSingleStorageMask = 0x00000001; static const uint32_t kHighSingleStorageMask = 0x00000002; static const uint32_t k64SoloStorageMask = 0x00000003; + static const uint32_t kLowDoubleStorageMask = 0x00000003; + static const uint32_t kHighDoubleStorageMask = 0x0000000c; static const uint32_t k128SoloStorageMask = 0x0000000f; static const uint32_t k256SoloStorageMask = 0x000000ff; static const uint32_t k512SoloStorageMask = 0x0000ffff; @@ -723,11 +726,13 @@ class Mir2Lir : public Backend { void ApplyLoadStoreElimination(LIR* head_lir, LIR* tail_lir); void ApplyLoadHoisting(LIR* head_lir, LIR* tail_lir); virtual void ApplyLocalOptimizations(LIR* head_lir, LIR* tail_lir); + virtual void ApplyArchOptimizations(LIR* head_lir, LIR* tail_lir, BasicBlock* bb) { return; }; // Shared by all targets - implemented in ralloc_util.cc int GetSRegHi(int lowSreg); bool LiveOut(int s_reg); void SimpleRegAlloc(); + void Cleanup(); void ResetRegPool(); void CompilerInitPool(RegisterInfo* info, RegStorage* regs, int num); void DumpRegPool(GrowableArray* regs); @@ -783,6 +788,8 @@ class Mir2Lir : public Backend { void MarkClean(RegLocation loc); void MarkDirty(RegLocation loc); void MarkInUse(RegStorage reg); + void MarkFree(RegStorage reg); + void MarkDead(RegStorage reg); bool CheckCorePoolSanity(); virtual RegLocation UpdateLoc(RegLocation loc); virtual RegLocation UpdateLocWide(RegLocation loc); @@ -880,7 +887,7 @@ class Mir2Lir : public Backend { // This will be overridden by x86 implementation. virtual void GenConstWide(RegLocation rl_dest, int64_t value); virtual void GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_src1, RegLocation rl_src2); + RegLocation rl_src1, RegLocation rl_src2, int opt_flags); // Shared by all targets - implemented in gen_invoke.cc. LIR* CallHelper(RegStorage r_tgt, QuickEntrypointEnum trampoline, bool safepoint_pc, @@ -980,6 +987,7 @@ class Mir2Lir : public Backend { bool GenInlinedUnsafeGet(CallInfo* info, bool is_long, bool is_volatile); bool GenInlinedUnsafePut(CallInfo* info, bool is_long, bool is_object, bool is_volatile, bool is_ordered); + virtual int LoadArgRegs(CallInfo* info, int call_state, NextCallInsn next_call_insn, const MethodReference& target_method, @@ -1328,6 +1336,9 @@ class Mir2Lir : public Backend { */ virtual void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir); + /* non virtual so it doesn't have to be implemented */ + virtual void MachineSpecificPreprocessMIR(BasicBlock* bb, MIR* mir) { }; + /** * @brief Lowers the kMirOpSelect MIR into LIR. * @param bb The basic block in which the MIR is from. @@ -1392,6 +1403,9 @@ class Mir2Lir : public Backend { virtual LIR* OpMem(OpKind op, RegStorage r_base, int disp) = 0; virtual LIR* OpPcRelLoad(RegStorage reg, LIR* target) = 0; virtual LIR* OpReg(OpKind op, RegStorage r_dest_src) = 0; + virtual LIR* OpBkpt() { // not abstract so it doesn't have to be implemeted for other platforms + return NULL; + }; virtual void OpRegCopy(RegStorage r_dest, RegStorage r_src) = 0; virtual LIR* OpRegCopyNoInsert(RegStorage r_dest, RegStorage r_src) = 0; virtual LIR* OpRegImm(OpKind op, RegStorage r_dest_src1, int value) = 0; diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index bed86d86a3c..eb3fbc7ba78 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -944,6 +944,24 @@ void Mir2Lir::MarkInUse(RegStorage reg) { } } +void Mir2Lir::MarkFree(RegStorage reg) { + if (reg.IsPair()) { + GetRegInfo(reg.GetLow())->MarkFree(); + GetRegInfo(reg.GetHigh())->MarkFree(); + } else { + GetRegInfo(reg)->MarkFree(); + } +} + +void Mir2Lir::MarkDead(RegStorage reg) { + if (reg.IsPair()) { + GetRegInfo(reg.GetLow())->MarkDead(); + GetRegInfo(reg.GetHigh())->MarkDead(); + } else { + GetRegInfo(reg)->MarkDead(); + } +} + bool Mir2Lir::CheckCorePoolSanity() { GrowableArray::Iterator it(&tempreg_info_); for (RegisterInfo* info = it.Next(); info != nullptr; info = it.Next()) { @@ -1407,6 +1425,9 @@ void Mir2Lir::SimpleRegAlloc() { frame_size_ = ComputeFrameSize(); } +void Mir2Lir::Cleanup() { +} + /* * Get the "real" sreg number associated with an s_reg slot. In general, * s_reg values passed through codegen are the SSA names created by diff --git a/compiler/dex/quick/resource_mask.h b/compiler/dex/quick/resource_mask.h index 436cdb54404..48e2555e58a 100644 --- a/compiler/dex/quick/resource_mask.h +++ b/compiler/dex/quick/resource_mask.h @@ -85,6 +85,14 @@ class ResourceMask { start_bit >= 64u ? UINT64_C(3) << (start_bit - 64u) : 0u); } + // Four consecutive bits. The start_bit must be even. + static constexpr ResourceMask FourBits(size_t start_bit) { + return + DCHECK_CONSTEXPR((start_bit & 1u) == 0u, << start_bit << " isn't even", Bit(0)) + ResourceMask(start_bit >= 64u ? 0u : UINT64_C(0xf) << start_bit, + start_bit >= 64u ? UINT64_C(0xf) << (start_bit - 64u) : 0u); + } + static constexpr ResourceMask NoBits() { return ResourceMask(UINT64_C(0), UINT64_C(0)); } diff --git a/compiler/dex/quick/x86/codegen_x86.h b/compiler/dex/quick/x86/codegen_x86.h index d74caae0c21..4755b6aff53 100644 --- a/compiler/dex/quick/x86/codegen_x86.h +++ b/compiler/dex/quick/x86/codegen_x86.h @@ -305,7 +305,8 @@ class X86Mir2Lir : public Mir2Lir { * @param rl_rhs Right hand operand. */ void GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, RegLocation rl_lhs, - RegLocation rl_rhs) OVERRIDE; + RegLocation rl_rhs, int opt_flags) OVERRIDE; + /* * @brief Load the Method* of a dex method into the register. diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc index 00a26212bfe..ea4fb6a9e1f 100755 --- a/compiler/dex/quick/x86/int_x86.cc +++ b/compiler/dex/quick/x86/int_x86.cc @@ -2688,7 +2688,7 @@ void X86Mir2Lir::GenInstanceofFinal(bool use_declaring_class, uint32_t type_idx, } void X86Mir2Lir::GenArithOpInt(Instruction::Code opcode, RegLocation rl_dest, - RegLocation rl_lhs, RegLocation rl_rhs) { + RegLocation rl_lhs, RegLocation rl_rhs, int opt_flags) { OpKind op = kOpBkpt; bool is_div_rem = false; bool unary = false; diff --git a/compiler/dex/reg_storage.h b/compiler/dex/reg_storage.h index 706933a1b47..94c051838b0 100644 --- a/compiler/dex/reg_storage.h +++ b/compiler/dex/reg_storage.h @@ -158,6 +158,10 @@ class RegStorage { return ((reg_ & kShapeMask) == k64BitSolo); } + constexpr bool Is128BitSolo() const { + return ((reg_ & kShapeMask) == k128BitSolo); + } + constexpr bool IsPair() const { return ((reg_ & kShapeMask) == k64BitPair); } @@ -174,6 +178,12 @@ class RegStorage { (reg_ & (kFloatingPoint | k64BitMask)) == (kFloatingPoint | k64Bits); } + constexpr bool IsQuad() const { + return + DCHECK_CONSTEXPR(Valid(), , false) + (reg_ & (kFloatingPoint | k128BitSolo)) == (kFloatingPoint | k128BitSolo); + } + constexpr bool IsSingle() const { return DCHECK_CONSTEXPR(Valid(), , false) @@ -188,6 +198,10 @@ class RegStorage { return (reg & (kFloatingPoint | k64BitMask)) == (kFloatingPoint | k64Bits); } + static constexpr bool IsQuad(uint16_t reg) { + return (reg & (kFloatingPoint | k128BitSolo)) == (kFloatingPoint | k128BitSolo); + } + static constexpr bool IsSingle(uint16_t reg) { return (reg & (kFloatingPoint | k64BitMask)) == kFloatingPoint; } @@ -229,24 +243,60 @@ class RegStorage { return ((reg_ & kRegTypeMask) | k32BitSolo); } + // Retrieve the low register num of a pair + int GetLowRegNum() const { + DCHECK(IsPair()); + return (reg_ & kRegNumMask); + } + // Create a stand-alone RegStorage from the low reg of a pair. RegStorage GetLow() const { DCHECK(IsPair()); return RegStorage(k32BitSolo, reg_ & kRegTypeMask); } + // Create a stand-alone RegStorage from the low 32bit of 64bit float solo. + RegStorage GetLowFromFloatSolo64() const { + DCHECK(IsFloat() && Is64BitSolo()); + return RegStorage(k32BitSolo, ((reg_ & kRegNumMask)<<1) | kFloatingPoint); + } + + // Create a stand-alone RegStorage from the low 64bit of 128bit float solo. + RegStorage GetLowFromFloatSolo128() const { + DCHECK(IsFloat() && Is128BitSolo()); + return RegStorage(k64BitSolo, ((reg_ & kRegNumMask)<<1) | kFloatingPoint); + } + // Retrieve the most significant register of a pair. int GetHighReg() const { DCHECK(IsPair()); return k32BitSolo | ((reg_ & kHighRegMask) >> kHighRegShift) | (reg_ & kFloatingPoint); } + // Retrieve the high register num of a pair. + int GetHighRegNum() const { + DCHECK(IsPair()); + return ((reg_ & kHighRegMask) >> kHighRegShift); + } + // Create a stand-alone RegStorage from the high reg of a pair. RegStorage GetHigh() const { DCHECK(IsPair()); return RegStorage(kValid | GetHighReg()); } + // Create a stand-alone RegStorage from the high 32bit of 64bit float solo. + RegStorage GetHighFromFloatSolo64() const { + DCHECK(IsFloat() && Is64BitSolo()); + return RegStorage(k32BitSolo, (((reg_ & kRegNumMask)<<1) +1) | kFloatingPoint); + } + + // Create a stand-alone RegStorage from the high 64bit of 128bit float solo. + RegStorage GetHighFromFloatSolo128() const { + DCHECK(IsFloat() && Is128BitSolo()); + return RegStorage(k64BitSolo, (((reg_ & kRegNumMask)<<1) +1) | kFloatingPoint); + } + void SetHighReg(int reg) { DCHECK(IsPair()); reg_ = (reg_ & ~kHighRegMask) | ((reg & kHighRegNumMask) << kHighRegShift); diff --git a/compiler/utils/growable_array.h b/compiler/utils/growable_array.h index a1a3312576e..d1669a73291 100644 --- a/compiler/utils/growable_array.h +++ b/compiler/utils/growable_array.h @@ -166,7 +166,6 @@ class GrowableArray { } // We should either have found the element, or it was the last (unscanned) element. DCHECK(found || (element == elem_list_[num_used_ - 1])); - num_used_--; }; void DeleteAt(size_t index) { diff --git a/disassembler/disassembler_arm.cc b/disassembler/disassembler_arm.cc index 54e77612a37..9ae66e6ee4d 100644 --- a/disassembler/disassembler_arm.cc +++ b/disassembler/disassembler_arm.cc @@ -1536,7 +1536,7 @@ size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) } else if ((op2 >> 3) == 6) { // 0110xxx // Multiply, multiply accumulate, and absolute difference op1 = (instr >> 20) & 0x7; - op2 = (instr >> 4) & 0x2; + op2 = (instr >> 4) & 0x1; ArmRegister Ra(instr, 12); ArmRegister Rn(instr, 16); ArmRegister Rm(instr, 0); diff --git a/runtime/Android.mk b/runtime/Android.mk index 17ee8ab2c1d..e86affb049d 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -426,6 +426,9 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT ifeq ($$(art_target_or_host),target) LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux libutils libsigchain LOCAL_STATIC_LIBRARIES := libziparchive libz + ifeq ($(BOARD_USES_QCOM_HARDWARE),true) + LOCAL_WHOLE_STATIC_LIBRARIES += libqc-art + endif else # host LOCAL_STATIC_LIBRARIES += libcutils libziparchive-host libz libutils LOCAL_SHARED_LIBRARIES += libsigchain From 07b8211edc3ff3e71e268ef220217603216ae5b9 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Thu, 4 Sep 2014 14:10:24 -0700 Subject: [PATCH 035/150] ART: minor bugfix 1. reset reference counters during initialization 2. check element existence when removing from GrowableArray Change-Id: I3a48da94346ecd407febb91ffe61a0ca7da9e8f2 --- compiler/dex/mir_graph.cc | 3 +++ compiler/utils/growable_array.h | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 885d413929d..584cef60d3f 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -1510,6 +1510,9 @@ void MIRGraph::InitializeMethodUses() { int num_ssa_regs = GetNumSSARegs(); use_counts_.Resize(num_ssa_regs + 32); raw_use_counts_.Resize(num_ssa_regs + 32); + // reset both lists to restart fresh + use_counts_.Reset(); + raw_use_counts_.Reset(); // Initialize list. for (int i = 0; i < num_ssa_regs; i++) { use_counts_.Insert(0); diff --git a/compiler/utils/growable_array.h b/compiler/utils/growable_array.h index d1669a73291..e93ee866053 100644 --- a/compiler/utils/growable_array.h +++ b/compiler/utils/growable_array.h @@ -166,6 +166,10 @@ class GrowableArray { } // We should either have found the element, or it was the last (unscanned) element. DCHECK(found || (element == elem_list_[num_used_ - 1])); + // if element is not in array, don't touch anything + if(!found && element != elem_list_[num_used_ - 1]) + return; + num_used_--; }; void DeleteAt(size_t index) { From 2fc46c371de174b664e4690dbf586fd92cad9c70 Mon Sep 17 00:00:00 2001 From: Maunik Shah Date: Thu, 4 Sep 2014 01:37:44 +0530 Subject: [PATCH 036/150] setting RLIMIT to INFINITY. This is to generate coredumps for processes spawned by zygote Change-Id: Iecc1eb7784373ffaf3ebfdc60d32d2ae0beb3235 --- runtime/native/dalvik_system_ZygoteHooks.cc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc index a4ef839ae01..de20c4a17ab 100644 --- a/runtime/native/dalvik_system_ZygoteHooks.cc +++ b/runtime/native/dalvik_system_ZygoteHooks.cc @@ -26,7 +26,9 @@ #if defined(HAVE_PRCTL) #include #endif - +#ifdef HAVE_ANDROID_OS +#include +#endif #include namespace art { @@ -41,7 +43,18 @@ static void EnableDebugger() { #endif // We don't want core dumps, though, so set the core dump size to 0. rlimit rl; +#ifdef HAVE_ANDROID_OS + char prop_value[PROPERTY_VALUE_MAX]; + property_get("persist.debug.trace", prop_value, "0"); + if (prop_value[0] == '1') { + LOG(INFO) << "setting RLIM to infinity for process " << getpid(); + rl.rlim_cur = RLIM_INFINITY; + } else { + rl.rlim_cur = 0; + } +#else rl.rlim_cur = 0; +#endif rl.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_CORE, &rl) == -1) { PLOG(ERROR) << "setrlimit(RLIMIT_CORE) failed for pid " << getpid(); From a9df2579886bc3b5184020dca5a5155dc6a2b6c6 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Tue, 30 Sep 2014 11:46:29 -0700 Subject: [PATCH 037/150] more ART Extension Vendor extension hooks. Change-Id: I9f03153bba698e82d1a1e992baf2bc1c48a5d059 --- compiler/dex/quick/arm/codegen_arm.h | 2 +- compiler/dex/quick/arm64/arm64_lir.h | 1 + compiler/dex/quick/arm64/assemble_arm64.cc | 32 +++++++++++++++++----- compiler/dex/quick/arm64/codegen_arm64.h | 25 +++++++++++++++-- compiler/dex/quick/arm64/target_arm64.cc | 29 ++++++++++++++++++-- compiler/dex/quick/arm64/utility_arm64.cc | 24 ++++++++-------- compiler/dex/reg_storage.h | 13 ++++++--- 7 files changed, 97 insertions(+), 29 deletions(-) diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index 32149c900fc..d9e6c011025 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -194,7 +194,7 @@ class ArmMir2Lir FINAL : public Mir2Lir { LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE; size_t GetInstructionOffset(LIR* lir); - void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir); + void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) OVERRIDE; void GenMoreMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) QC_WEAK; //void MachineSpecificPreprocessMIR(BasicBlock* bb, MIR* mir); diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h index a449cbd4f7d..83d0590e32a 100644 --- a/compiler/dex/quick/arm64/arm64_lir.h +++ b/compiler/dex/quick/arm64/arm64_lir.h @@ -317,6 +317,7 @@ enum ArmOpcode { kA64Mov2rr, // mov [00101010000] rm[20-16] [000000] [11111] rd[4-0]. kA64Mvn2rr, // mov [00101010001] rm[20-16] [000000] [11111] rd[4-0]. kA64Mul3rrr, // mul [00011011000] rm[20-16] [011111] rn[9-5] rd[4-0]. + kA64Madd4rrrr, // madd[s0011011000] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. kA64Msub4rrrr, // msub[s0011011000] rm[20-16] [1] ra[14-10] rn[9-5] rd[4-0]. kA64Neg3rro, // neg alias of "sub arg0, rzr, arg1, arg2". kA64Orr3Rrl, // orr [s01100100] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0]. diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index 15c89f2f188..0898f7fa413 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -462,6 +462,10 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "mul", "!0r, !1r, !2r", kFixupNone), + ENCODING_MAP(WIDE(kA64Madd4rrrr), SF_VARIANTS(0x1b000000), + kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 14, 10, + kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, + "madd", "!0r, !1r, !3r, !2r", kFixupNone), ENCODING_MAP(WIDE(kA64Msub4rrrr), SF_VARIANTS(0x1b008000), kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 14, 10, kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, @@ -646,20 +650,33 @@ void Arm64Mir2Lir::InsertFixupBefore(LIR* prev_lir, LIR* orig_lir, LIR* new_lir) } } +const ArmEncodingMap* Arm64Mir2Lir::GetEncoder(int opcode) { + const ArmEncodingMap* encoder = &EncodingMap[opcode]; + return encoder; +} + /* Nop, used for aligning code. Nop is an alias for hint #0. */ #define PADDING_NOP (UINT32_C(0xd503201f)) +uint32_t Arm64Mir2Lir::ProcessMoreEncodings(const ArmEncodingMap *encoder, + int i, uint32_t operand) { + LOG(FATAL) << "Bad fmt:" << encoder->field_loc[i].kind; + uint32_t value = 0; + return value; +} + uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { for (; lir != nullptr; lir = NEXT_LIR(lir)) { bool opcode_is_wide = IS_WIDE(lir->opcode); ArmOpcode opcode = UNWIDE(lir->opcode); + bool extendedOpcode = false; if (UNLIKELY(IsPseudoLirOp(opcode))) { continue; } if (LIKELY(!lir->flags.is_nop)) { - const ArmEncodingMap *encoder = &EncodingMap[opcode]; + const ArmEncodingMap *encoder = GetEncoder(opcode); // Select the right variant of the skeleton. uint32_t bits = opcode_is_wide ? encoder->xskeleton : encoder->wskeleton; @@ -788,8 +805,9 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { bits |= value; break; default: - LOG(FATAL) << "Bad fmt for arg. " << i << " in " << encoder->name - << " (" << kind << ")"; + bits |= ProcessMoreEncodings(encoder, i, operand); + extendedOpcode = true; + break; } } } @@ -903,7 +921,7 @@ void Arm64Mir2Lir::AssembleLIR() { break; } default: - LOG(FATAL) << "Unexpected case " << lir->flags.fixup; + LOG(FATAL) << "Unexpected case: opcode: " << lir->opcode << ", fixup: " << lir->flags.fixup; } prev_lir = lir; lir = lir->u.a.pcrel_next; @@ -953,7 +971,7 @@ void Arm64Mir2Lir::AssembleLIR() { size_t Arm64Mir2Lir::GetInsnSize(LIR* lir) { ArmOpcode opcode = UNWIDE(lir->opcode); DCHECK(!IsPseudoLirOp(opcode)); - return EncodingMap[opcode].size; + return GetEncoder(opcode)->size; } // Encode instruction bit pattern and assign offsets. @@ -966,8 +984,8 @@ uint32_t Arm64Mir2Lir::LinkFixupInsns(LIR* head_lir, LIR* tail_lir, uint32_t off if (!lir->flags.is_nop) { if (lir->flags.fixup != kFixupNone) { if (!IsPseudoLirOp(opcode)) { - lir->flags.size = EncodingMap[opcode].size; - lir->flags.fixup = EncodingMap[opcode].fixup; + lir->flags.size = GetEncoder(opcode)->size; + lir->flags.fixup = GetEncoder(opcode)->fixup; } else { DCHECK_NE(static_cast(opcode), kPseudoPseudoAlign4); lir->flags.size = 0; diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h index 3e1c18baf4f..0edafe0884a 100644 --- a/compiler/dex/quick/arm64/codegen_arm64.h +++ b/compiler/dex/quick/arm64/codegen_arm64.h @@ -22,8 +22,14 @@ #include -namespace art { +#ifdef QC_STRONG +#define QC_WEAK +#else +#define QC_WEAK __attribute__((weak)) +#endif +namespace art { +class QCArm64Mir2Lir; class Arm64Mir2Lir FINAL : public Mir2Lir { protected: // TODO: consolidate 64-bit target support. @@ -259,7 +265,10 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { LIR* InvokeTrampoline(OpKind op, RegStorage r_tgt, QuickEntrypointEnum trampoline) OVERRIDE; - private: + virtual void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) OVERRIDE; + void GenMoreMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) QC_WEAK; + +private: /** * @brief Given register xNN (dNN), returns register wNN (sNN). * @param reg #RegStorage containing a Solo64 input register (e.g. @c x1 or @c d2). @@ -394,6 +403,18 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { InToRegStorageMapping in_to_reg_storage_mapping_; static const ArmEncodingMap EncodingMap[kA64Last]; + +private: + static uint32_t ProcessMoreEncodings(const ArmEncodingMap* encoder, int i, uint32_t operand) QC_WEAK; + static const ArmEncodingMap* GetEncoder(int opcode) QC_WEAK; + + virtual void ApplyArchOptimizations(LIR* head_lir, LIR* tail_lir, BasicBlock* bb) QC_WEAK; + + void CompilerPostInitializeRegAlloc() QC_WEAK; + void Arm64Mir2LirPostInit(Arm64Mir2Lir* mir_to_lir) QC_WEAK; + + friend class QCArm64Mir2Lir; + QCArm64Mir2Lir* qcm2l; }; } // namespace art diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc index 9b4546a94bb..8368c5b7bcf 100644 --- a/compiler/dex/quick/arm64/target_arm64.cc +++ b/compiler/dex/quick/arm64/target_arm64.cc @@ -138,6 +138,11 @@ RegStorage Arm64Mir2Lir::TargetReg(SpecialTargetRegister reg) { return res_reg; } +void Arm64Mir2Lir::CompilerPostInitializeRegAlloc() +{ + //nothing here +} + /* * Decode the register id. This routine makes assumptions on the encoding made by RegStorage. */ @@ -587,6 +592,12 @@ Arm64Mir2Lir::Arm64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAlloca << static_cast(Arm64Mir2Lir::EncodingMap[i].opcode); } } + + qcm2l = nullptr; + Arm64Mir2LirPostInit(this); +} + +void Arm64Mir2Lir::Arm64Mir2LirPostInit(Arm64Mir2Lir* mir_to_lir) { } Mir2Lir* Arm64CodeGenerator(CompilationUnit* const cu, MIRGraph* const mir_graph, @@ -633,6 +644,8 @@ void Arm64Mir2Lir::CompilerInitializeRegAlloc() { reg_pool_->next_core_reg_ = 2; reg_pool_->next_sp_reg_ = 0; reg_pool_->next_dp_reg_ = 0; + + CompilerPostInitializeRegAlloc(); } /* @@ -772,17 +785,17 @@ LIR* Arm64Mir2Lir::CheckSuspendUsingLoad() { uint64_t Arm64Mir2Lir::GetTargetInstFlags(int opcode) { DCHECK(!IsPseudoLirOp(opcode)); - return Arm64Mir2Lir::EncodingMap[UNWIDE(opcode)].flags; + return GetEncoder(UNWIDE(opcode))->flags; } const char* Arm64Mir2Lir::GetTargetInstName(int opcode) { DCHECK(!IsPseudoLirOp(opcode)); - return Arm64Mir2Lir::EncodingMap[UNWIDE(opcode)].name; + return GetEncoder(UNWIDE(opcode))->name; } const char* Arm64Mir2Lir::GetTargetInstFmt(int opcode) { DCHECK(!IsPseudoLirOp(opcode)); - return Arm64Mir2Lir::EncodingMap[UNWIDE(opcode)].fmt; + return GetEncoder(UNWIDE(opcode))->fmt; } RegStorage Arm64Mir2Lir::InToRegStorageArm64Mapper::GetNextReg(bool is_double_or_float, @@ -1199,4 +1212,14 @@ int Arm64Mir2Lir::GenDalvikArgsRange(CallInfo* info, int call_state, return call_state; } +void Arm64Mir2Lir::GenMoreMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) { +} + +void Arm64Mir2Lir::GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) { + GenMoreMachineSpecificExtendedMethodMIR(bb, mir); +} + +void Arm64Mir2Lir::ApplyArchOptimizations(LIR* head_lir, LIR* tail_lir, BasicBlock* bb) { +} + } // namespace art diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc index f58f83070be..070175ac9f7 100644 --- a/compiler/dex/quick/arm64/utility_arm64.cc +++ b/compiler/dex/quick/arm64/utility_arm64.cc @@ -91,7 +91,7 @@ size_t Arm64Mir2Lir::GetLoadStoreSize(LIR* lir) { bool opcode_is_wide = IS_WIDE(lir->opcode); ArmOpcode opcode = UNWIDE(lir->opcode); DCHECK(!IsPseudoLirOp(opcode)); - const ArmEncodingMap *encoder = &EncodingMap[opcode]; + const ArmEncodingMap *encoder = GetEncoder(opcode); uint32_t bits = opcode_is_wide ? encoder->xskeleton : encoder->wskeleton; return (bits >> 30); } @@ -617,11 +617,11 @@ LIR* Arm64Mir2Lir::OpRegRegShift(OpKind op, RegStorage r_dest_src1, RegStorage r } DCHECK(!IsPseudoLirOp(opcode)); - if (EncodingMap[opcode].flags & IS_BINARY_OP) { + if (GetEncoder(opcode)->flags & IS_BINARY_OP) { DCHECK_EQ(shift, ENCODE_NO_SHIFT); return NewLIR2(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg()); - } else if (EncodingMap[opcode].flags & IS_TERTIARY_OP) { - ArmEncodingKind kind = EncodingMap[opcode].field_loc[2].kind; + } else if (GetEncoder(opcode)->flags & IS_TERTIARY_OP) { + ArmEncodingKind kind = GetEncoder(opcode)->field_loc[2].kind; if (kind == kFmtShift) { return NewLIR3(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg(), shift); } @@ -654,8 +654,8 @@ LIR* Arm64Mir2Lir::OpRegRegExtend(OpKind op, RegStorage r_dest_src1, RegStorage } DCHECK(!IsPseudoLirOp(opcode)); - if (EncodingMap[opcode].flags & IS_TERTIARY_OP) { - ArmEncodingKind kind = EncodingMap[opcode].field_loc[2].kind; + if (GetEncoder(opcode)->flags & IS_TERTIARY_OP) { + ArmEncodingKind kind = GetEncoder(opcode)->field_loc[2].kind; if (kind == kFmtExtend) { return NewLIR3(opcode | wide, r_dest_src1.GetReg(), r_src2.GetReg(), EncodeExtend(ext, amount)); @@ -750,11 +750,11 @@ LIR* Arm64Mir2Lir::OpRegRegRegShift(OpKind op, RegStorage r_dest, RegStorage r_s ArmOpcode widened_opcode = r_dest.Is64Bit() ? WIDE(opcode) : opcode; CHECK_EQ(r_dest.Is64Bit(), r_src1.Is64Bit()); CHECK_EQ(r_dest.Is64Bit(), r_src2.Is64Bit()); - if (EncodingMap[opcode].flags & IS_QUAD_OP) { + if (GetEncoder(opcode)->flags & IS_QUAD_OP) { DCHECK(!IsExtendEncoding(shift)); return NewLIR4(widened_opcode, r_dest.GetReg(), r_src1.GetReg(), r_src2.GetReg(), shift); } else { - DCHECK(EncodingMap[opcode].flags & IS_TERTIARY_OP); + DCHECK(GetEncoder(opcode)->flags & IS_TERTIARY_OP); DCHECK_EQ(shift, ENCODE_NO_SHIFT); return NewLIR3(widened_opcode, r_dest.GetReg(), r_src1.GetReg(), r_src2.GetReg()); } @@ -924,7 +924,7 @@ LIR* Arm64Mir2Lir::OpRegRegImm64(OpKind op, RegStorage r_dest, RegStorage r_src1 r_scratch = AllocTemp(); LoadConstant(r_scratch, value); } - if (EncodingMap[alt_opcode].flags & IS_QUAD_OP) + if (GetEncoder(alt_opcode)->flags & IS_QUAD_OP) res = NewLIR4(alt_opcode | wide, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg(), info); else res = NewLIR3(alt_opcode | wide, r_dest.GetReg(), r_src1.GetReg(), r_scratch.GetReg()); @@ -998,7 +998,7 @@ LIR* Arm64Mir2Lir::OpRegImm64(OpKind op, RegStorage r_dest_src1, int64_t value) if (UNLIKELY(neg)) opcode = neg_opcode; - if (EncodingMap[opcode].flags & IS_QUAD_OP) + if (GetEncoder(opcode)->flags & IS_QUAD_OP) return NewLIR4(opcode | wide, r_dest_src1.GetReg(), r_dest_src1.GetReg(), abs_value, (shift) ? 1 : 0); else @@ -1092,7 +1092,7 @@ LIR* Arm64Mir2Lir::LoadBaseIndexed(RegStorage r_base, RegStorage r_index, RegSto if (UNLIKELY(expected_scale == 0)) { // This is a tertiary op (e.g. ldrb, ldrsb), it does not not support scale. - DCHECK_NE(EncodingMap[UNWIDE(opcode)].flags & IS_TERTIARY_OP, 0U); + DCHECK_NE(GetEncoder(UNWIDE(opcode))->flags & IS_TERTIARY_OP, 0U); DCHECK_EQ(scale, 0); load = NewLIR3(opcode, r_dest.GetReg(), r_base.GetReg(), r_index.GetReg()); } else { @@ -1173,7 +1173,7 @@ LIR* Arm64Mir2Lir::StoreBaseIndexed(RegStorage r_base, RegStorage r_index, RegSt if (UNLIKELY(expected_scale == 0)) { // This is a tertiary op (e.g. strb), it does not not support scale. - DCHECK_NE(EncodingMap[UNWIDE(opcode)].flags & IS_TERTIARY_OP, 0U); + DCHECK_NE(GetEncoder(UNWIDE(opcode))->flags & IS_TERTIARY_OP, 0U); DCHECK_EQ(scale, 0); store = NewLIR3(opcode, r_src.GetReg(), r_base.GetReg(), r_index.GetReg()); } else { diff --git a/compiler/dex/reg_storage.h b/compiler/dex/reg_storage.h index 94c051838b0..6178bee5700 100644 --- a/compiler/dex/reg_storage.h +++ b/compiler/dex/reg_storage.h @@ -258,13 +258,13 @@ class RegStorage { // Create a stand-alone RegStorage from the low 32bit of 64bit float solo. RegStorage GetLowFromFloatSolo64() const { DCHECK(IsFloat() && Is64BitSolo()); - return RegStorage(k32BitSolo, ((reg_ & kRegNumMask)<<1) | kFloatingPoint); + return RegStorage(k32BitSolo, ((reg_ & kRegNumMask) << 1) | kFloatingPoint); } // Create a stand-alone RegStorage from the low 64bit of 128bit float solo. RegStorage GetLowFromFloatSolo128() const { DCHECK(IsFloat() && Is128BitSolo()); - return RegStorage(k64BitSolo, ((reg_ & kRegNumMask)<<1) | kFloatingPoint); + return RegStorage(k64BitSolo, ((reg_ & kRegNumMask) << 1) | kFloatingPoint); } // Retrieve the most significant register of a pair. @@ -288,13 +288,13 @@ class RegStorage { // Create a stand-alone RegStorage from the high 32bit of 64bit float solo. RegStorage GetHighFromFloatSolo64() const { DCHECK(IsFloat() && Is64BitSolo()); - return RegStorage(k32BitSolo, (((reg_ & kRegNumMask)<<1) +1) | kFloatingPoint); + return RegStorage(k32BitSolo, (((reg_ & kRegNumMask) << 1) +1) | kFloatingPoint); } // Create a stand-alone RegStorage from the high 64bit of 128bit float solo. RegStorage GetHighFromFloatSolo128() const { DCHECK(IsFloat() && Is128BitSolo()); - return RegStorage(k64BitSolo, (((reg_ & kRegNumMask)<<1) +1) | kFloatingPoint); + return RegStorage(k64BitSolo, (((reg_ & kRegNumMask) << 1) +1) | kFloatingPoint); } void SetHighReg(int reg) { @@ -359,6 +359,11 @@ class RegStorage { return RegStorage(k64BitSolo, (reg_num & kRegNumMask) | kFloatingPoint); } + // Create a floating point 128-bit solo. + static RegStorage FloatSolo128(int reg_num) { + return RegStorage(k128BitSolo, (reg_num & kRegNumMask) | kFloatingPoint); + } + static constexpr RegStorage InvalidReg() { return RegStorage(kInvalid); } From a047ad4abe7f517643dd213ca08c78758e15a353 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Tue, 7 Oct 2014 12:51:26 +0100 Subject: [PATCH 038/150] Fix thread priorities for unstarted threads. Calls to Thread.setPriority for unstarted threads now behave similar to dalvik. Note that there's still some inconsistent behaviour carried over from dalvik. - high priority threads from bg_non_interactive processes are not always moved to the SP_FOREGROUND cgroup. - we do not attempt to adjust the cgroup of a native thread that's attaching. Note that on android, the system_server will change the cgroups for all running threads in a process when it moves into the foreground and background. It's by design that threads in a background process can request to be moved to the foreground by setting a higher priority. bug: 17893086 Change-Id: I1662982b1c7b3ac509698e2e12c9768d082c8053 --- runtime/thread.cc | 3 ++ runtime/thread_android.cc | 7 +++++ test/051-thread/expected.txt | 2 ++ test/051-thread/src/Main.java | 54 ++++++++++++++++++++++++++++++++++ test/051-thread/thread_test.cc | 34 +++++++++++++++++++++ test/Android.libarttest.mk | 1 + 6 files changed, 101 insertions(+) create mode 100644 test/051-thread/thread_test.cc diff --git a/runtime/thread.cc b/runtime/thread.cc index 6b65f12fb59..7fb631c0733 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -168,6 +168,9 @@ void* Thread::CreateCallback(void* arg) { self->GetJniEnv()->DeleteGlobalRef(self->tlsPtr_.jpeer); self->tlsPtr_.jpeer = nullptr; self->SetThreadName(self->GetThreadName(soa)->ToModifiedUtf8().c_str()); + + mirror::ArtField* priorityField = soa.DecodeField(WellKnownClasses::java_lang_Thread_priority); + self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer)); Dbg::PostThreadStart(self); // Invoke the 'run' method of our java.lang.Thread. diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc index 73a9e54ea56..d5db9838abc 100644 --- a/runtime/thread_android.cc +++ b/runtime/thread_android.cc @@ -55,6 +55,13 @@ void Thread::SetNativePriority(int newPriority) { int newNice = kNiceValues[newPriority-1]; pid_t tid = GetTid(); + // TODO: b/18249098 The code below is broken. It uses getpriority() as a proxy for whether a + // thread is already in the SP_FOREGROUND cgroup. This is not necessarily true for background + // processes, where all threads are in the SP_BACKGROUND cgroup. This means that callers will + // have to call setPriority twice to do what they want : + // + // Thread.setPriority(Thread.MIN_PRIORITY); // no-op wrt to cgroups + // Thread.setPriority(Thread.MAX_PRIORITY); // will actually change cgroups. if (newNice >= ANDROID_PRIORITY_BACKGROUND) { set_sched_policy(tid, SP_BACKGROUND); } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { diff --git a/test/051-thread/expected.txt b/test/051-thread/expected.txt index 943d1dfac27..54e34af3aa1 100644 --- a/test/051-thread/expected.txt +++ b/test/051-thread/expected.txt @@ -9,4 +9,6 @@ testSleepZero finished testSetName starting testSetName running testSetName finished +testThreadPriorities starting +testThreadPriorities finished thread test done diff --git a/test/051-thread/src/Main.java b/test/051-thread/src/Main.java index 390685d0492..b81273ea4eb 100644 --- a/test/051-thread/src/Main.java +++ b/test/051-thread/src/Main.java @@ -20,12 +20,17 @@ * Test some basic thread stuff. */ public class Main { + static { + System.loadLibrary("arttest"); + } + public static void main(String[] args) throws Exception { System.out.println("thread test starting"); testThreadCapacity(); testThreadDaemons(); testSleepZero(); testSetName(); + testThreadPriorities(); System.out.println("thread test done"); } @@ -133,4 +138,53 @@ public void run() { } System.out.print("testSetName finished\n"); } + + private static void testThreadPriorities() throws Exception { + System.out.print("testThreadPriorities starting\n"); + + PriorityStoringThread t1 = new PriorityStoringThread(false); + t1.setPriority(Thread.MAX_PRIORITY); + t1.start(); + t1.join(); + if (supportsThreadPriorities() && (t1.getNativePriority() != Thread.MAX_PRIORITY)) { + System.out.print("thread priority for t1 was " + t1.getNativePriority() + + " [expected Thread.MAX_PRIORITY]\n"); + } + + PriorityStoringThread t2 = new PriorityStoringThread(true); + t2.start(); + t2.join(); + if (supportsThreadPriorities() && (t2.getNativePriority() != Thread.MAX_PRIORITY)) { + System.out.print("thread priority for t2 was " + t2.getNativePriority() + + " [expected Thread.MAX_PRIORITY]\n"); + } + + System.out.print("testThreadPriorities finished\n"); + } + + private static native int getNativePriority(); + private static native boolean supportsThreadPriorities(); + + static class PriorityStoringThread extends Thread { + private final boolean setPriority; + private volatile int nativePriority; + + public PriorityStoringThread(boolean setPriority) { + this.setPriority = setPriority; + this.nativePriority = -1; + } + + @Override + public void run() { + if (setPriority) { + setPriority(Thread.MAX_PRIORITY); + } + + nativePriority = Main.getNativePriority(); + } + + public int getNativePriority() { + return nativePriority; + } + } } diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc new file mode 100644 index 00000000000..2f5fffc4002 --- /dev/null +++ b/test/051-thread/thread_test.cc @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 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 "jni.h" +#include "thread-inl.h" + +namespace art { + +extern "C" JNIEXPORT jint JNICALL Java_Main_getNativePriority(JNIEnv* env, jclass) { + return ThreadForEnv(env)->GetNativePriority(); +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(JNIEnv* env, jclass) { +#if defined(HAVE_ANDROID_OS) + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +} // namespace art diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index 2d139a64b93..2e12335d3d9 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -24,6 +24,7 @@ LIBARTTEST_COMMON_SRC_FILES := \ 004-ReferenceMap/stack_walk_refmap_jni.cc \ 004-StackWalk/stack_walk_jni.cc \ 004-UnsafeTest/unsafe_test.cc \ + 051-thread/thread_test.cc \ 116-nodex2oat/nodex2oat.cc \ 117-nopatchoat/nopatchoat.cc \ 118-noimage-dex2oat/noimage-dex2oat.cc From dd1e0f8c3afbc7d570af9435e302cce3284cfe3d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 12 Nov 2014 15:41:57 +0000 Subject: [PATCH 039/150] Fix bad OOM check in Thread::CreatePeer(). Bug: 18342629 Change-Id: Ic070ed6b72d11b2e1e2a89904bd9659b2146f863 --- runtime/thread.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/thread.cc b/runtime/thread.cc index 129c311618f..bc2d5ff1b24 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -429,7 +429,7 @@ void Thread::CreatePeer(const char* name, bool as_daemon, jobject thread_group) thread_group = runtime->GetMainThreadGroup(); } ScopedLocalRef thread_name(env, env->NewStringUTF(name)); - if (thread_name.get() == nullptr) { + if (name != nullptr && thread_name.get() == nullptr) { CHECK(IsExceptionPending()); return; } From ca45725c80d2836f3f7441b27ff6e7c2f6421fc7 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 12 Nov 2014 17:02:02 +0000 Subject: [PATCH 040/150] Fix LinkFieldsComparator. Define order for primitive types with the same sizes. Previously, the comparator would consider the fields equal so the order would depend on std::sort() implementation. Changing the STL implementation could silently change the field offsets. (And, unlike std::stable_sort(), the std::sort() doesn't even need to be deterministic.) (cherry picked from commit d577748c041aa6df599218f3cb31697ecf032730) Change-Id: I9b769d023864aa36c52918c7c3dd11c0f6b8f40e --- runtime/class_linker.cc | 25 +++++++++++++++---------- runtime/oat.cc | 2 +- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index b80aa5aa1e4..eb3ee0d89e1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5143,17 +5143,22 @@ struct LinkFieldsComparator { Primitive::Type type1 = field1->GetTypeAsPrimitiveType(); Primitive::Type type2 = field2->GetTypeAsPrimitiveType(); if (type1 != type2) { - bool is_primitive1 = type1 != Primitive::kPrimNot; - bool is_primitive2 = type2 != Primitive::kPrimNot; - bool is64bit1 = is_primitive1 && (type1 == Primitive::kPrimLong || - type1 == Primitive::kPrimDouble); - bool is64bit2 = is_primitive2 && (type2 == Primitive::kPrimLong || - type2 == Primitive::kPrimDouble); - int order1 = !is_primitive1 ? 0 : (is64bit1 ? 1 : 2); - int order2 = !is_primitive2 ? 0 : (is64bit2 ? 1 : 2); - if (order1 != order2) { - return order1 < order2; + if (type1 == Primitive::kPrimNot) { + // Reference always goes first. + return true; + } + if (type2 == Primitive::kPrimNot) { + // Reference always goes first. + return false; + } + size_t size1 = Primitive::ComponentSize(type1); + size_t size2 = Primitive::ComponentSize(type2); + if (size1 != size2) { + // Larger primitive types go first. + return size1 > size2; } + // Primitive types differ but sizes match. Arbitrarily order by primitive type. + return type1 < type2; } // same basic group? then sort by string. return strcmp(field1->GetName(), field2->GetName()) < 0; diff --git a/runtime/oat.cc b/runtime/oat.cc index 9d98f30075a..a1b99db28e0 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -23,7 +23,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '4', '1', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '4', '2', '\0' }; static size_t ComputeOatHeaderSize(const SafeMap* variable_data) { size_t estimate = 0U; From a8d6729501595d82004ec0d0e334d1ff33159d63 Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Mon, 17 Nov 2014 15:13:34 +0000 Subject: [PATCH 041/150] Fix ordering of fields with the same name. While the Java language doesn't allow multiple fields with the same name in a single class (excluding fields from super classes), the bytecode specification permits it and tools such as proguard actually generate them. Define the order of these fields by their dex file index and relax the check of field ordering to permit identical names. Bug: 18211592 (cherry picked from commit 7a7c1db21782fb922d3ffc5c576117812624ea58) Change-Id: I8547eeac890b7483c8925367a85382197be9ea7a --- runtime/class_linker.cc | 10 +++++++--- runtime/oat.cc | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index eb3ee0d89e1..6dbd16d2326 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5160,8 +5160,10 @@ struct LinkFieldsComparator { // Primitive types differ but sizes match. Arbitrarily order by primitive type. return type1 < type2; } - // same basic group? then sort by string. - return strcmp(field1->GetName(), field2->GetName()) < 0; + // Same basic group? Then sort by dex field index. This is guaranteed to be sorted + // by name and for equal names by type id index. + // NOTE: This works also for proxies. Their static fields are assigned appropriate indexes. + return field1->GetDexFieldIndex() < field2->GetDexFieldIndex(); } }; @@ -5303,7 +5305,9 @@ bool ClassLinker::LinkFields(Handle klass, bool is_static, size_t } if (i != 0) { mirror::ArtField* prev_field = fields->Get(i - 1u); - CHECK_LT(strcmp(prev_field->GetName(), field->GetName()), 0); + // NOTE: The field names can be the same. This is not possible in the Java language + // but it's valid Java/dex bytecode and for example proguard can generate such bytecode. + CHECK_LE(strcmp(prev_field->GetName(), field->GetName()), 0); } Primitive::Type type = field->GetTypeAsPrimitiveType(); bool is_primitive = type != Primitive::kPrimNot; diff --git a/runtime/oat.cc b/runtime/oat.cc index a1b99db28e0..4819396d187 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -23,7 +23,7 @@ namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; -const uint8_t OatHeader::kOatVersion[] = { '0', '4', '2', '\0' }; +const uint8_t OatHeader::kOatVersion[] = { '0', '4', '3', '\0' }; static size_t ComputeOatHeaderSize(const SafeMap* variable_data) { size_t estimate = 0U; From 38078c6449ea5c53ce390343eb57327552a7eaff Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Wed, 10 Dec 2014 20:51:45 -0800 Subject: [PATCH 042/150] ART: More ELF and oat file safety measures In an ELF file, look for a shstrtab section when loading in program-header-only mode. If the section is outside the file size, it strongly indicates a broken compile. When compiling oat files in the class linker, explicitly unlink on failure. This should catch cases when dex2oat is killed or crashes and doesn't have a chance to delete its (partial) output. Bug: 15567083 Change-Id: Ia0c75f151d91c6f26a71696967255d6d409ca882 --- runtime/class_linker.cc | 10 +++++++++- runtime/elf_file.cc | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index cb2831cf330..cd1a93a30c5 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -662,7 +663,14 @@ bool ClassLinker::GenerateOatFile(const char* dex_filename, argv.push_back(compiler_options[i].c_str()); } - return Exec(argv, error_msg); + 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) { diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc index 6ec0712f0dc..29628f1e0d8 100644 --- a/runtime/elf_file.cc +++ b/runtime/elf_file.cc @@ -432,6 +432,20 @@ bool ElfFile::CheckSectionsExist(std::string* error_msg) const { return false; } + // We'd also like to confirm a shstrtab in program_header_only_ mode (else Open() does this for + // us). This is usually the last in an oat file, and a good indicator of whether writing was + // successful (or the process crashed and left garbage). + if (program_header_only_) { + // It might not be mapped, but we can compare against the file size. + int64_t offset = static_cast(GetHeader().e_shoff + + (GetHeader().e_shstrndx * GetHeader().e_shentsize)); + if (offset >= file_->GetLength()) { + *error_msg = StringPrintf("Shstrtab is not in the mapped ELF file: '%s'", + file_->GetPath().c_str()); + return false; + } + } + return true; } From a1e6d9a360c4fa20ec689636b3886dad3885bf23 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 10 Dec 2014 18:45:30 -0800 Subject: [PATCH 043/150] Increase default allocation stack size Prevents some GC for alloc which can occur if the allocation stack overflows. Bug: 18707404 Change-Id: I00e8e518aa5105b69c70603a8395c81617b87d14 --- runtime/gc/heap.cc | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 122701d41a8..0c14ded3ac3 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -75,8 +75,6 @@ namespace gc { static constexpr size_t kCollectorTransitionStressIterations = 0; static constexpr size_t kCollectorTransitionStressWait = 10 * 1000; // Microseconds -static constexpr bool kGCALotMode = false; -static constexpr size_t kGcAlotInterval = KB; // Minimum amount of remaining bytes before a concurrent GC is triggered. static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; static constexpr size_t kMaxConcurrentRemainingBytes = 512 * KB; @@ -105,6 +103,15 @@ static const char* kMemMapSpaceName[2] = {"main space", "main space 1"}; static const char* kNonMovingSpaceName = "non moving space"; static const char* kZygoteSpaceName = "zygote space"; static constexpr size_t kGSSBumpPointerSpaceCapacity = 32 * MB; +static constexpr bool kGCALotMode = false; +// GC alot mode uses a small allocation stack to stress test a lot of GC. +static constexpr size_t kGcAlotAllocationStackSize = 4 * KB / + sizeof(mirror::HeapReference); +// Verify objet has a small allocation stack size since searching the allocation stack is slow. +static constexpr size_t kVerifyObjectAllocationStackSize = 16 * KB / + sizeof(mirror::HeapReference); +static constexpr size_t kDefaultAllocationStackSize = 8 * MB / + sizeof(mirror::HeapReference); Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, double foreground_heap_growth_multiplier, @@ -170,8 +177,9 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max * verification is enabled, we limit the size of allocation stacks to speed up their * searching. */ - max_allocation_stack_size_(kGCALotMode ? kGcAlotInterval - : (kVerifyObjectSupport > kVerifyObjectModeFast) ? KB : MB), + max_allocation_stack_size_(kGCALotMode ? kGcAlotAllocationStackSize + : (kVerifyObjectSupport > kVerifyObjectModeFast) ? kVerifyObjectAllocationStackSize : + kDefaultAllocationStackSize), current_allocator_(kAllocatorTypeDlMalloc), current_non_moving_allocator_(kAllocatorTypeNonMoving), bump_pointer_space_(nullptr), From 00377384ccbc87d8e8caef0fbd093ed0bd523243 Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Wed, 8 Oct 2014 14:26:23 -0700 Subject: [PATCH 044/150] Dump mutator lock for thread suspend timeout Should help us see if thread suspend timeouts are due to someone holding on the mutator lock for too long or if it is a missed futex wake. (cherry picked from commit f924d2381a5ea53967ba1e279766d601b9be05ea) Bug: 17837911 Conflicts: runtime/thread_list.cc Change-Id: Ife2b846ed4e58871065691512267a75aa4d07d70 --- runtime/base/mutex.cc | 8 +++++++- runtime/thread_list.cc | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 9b9741121cf..495ea5c106b 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -647,7 +647,13 @@ bool ReaderWriterMutex::IsSharedHeld(const Thread* self) const { void ReaderWriterMutex::Dump(std::ostream& os) const { os << name_ << " level=" << static_cast(level_) - << " owner=" << GetExclusiveOwnerTid() << " "; + << " owner=" << GetExclusiveOwnerTid() +#if ART_USE_FUTEXES + << " state=" << state_.LoadSequentiallyConsistent() + << " num_pending_writers=" << num_pending_writers_.LoadSequentiallyConsistent() + << " num_pending_readers=" << num_pending_readers_.LoadSequentiallyConsistent() +#endif + << " "; DumpContention(os); } diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc index b48fcd4f521..725a7084130 100644 --- a/runtime/thread_list.cc +++ b/runtime/thread_list.cc @@ -166,6 +166,8 @@ static void UnsafeLogFatalForThreadSuspendAllTimeout() { Runtime* runtime = Runtime::Current(); std::ostringstream ss; ss << "Thread suspend timeout\n"; + Locks::mutator_lock_->Dump(ss); + ss << "\n"; runtime->GetThreadList()->DumpLocked(ss); LOG(FATAL) << ss.str(); exit(0); From e1db521e77524da230171877fc8ec860179fca74 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Thu, 6 Nov 2014 19:30:10 +0100 Subject: [PATCH 045/150] [Xposed] Disable host build They don't compile with the Xposed modifications and aren't necessary. --- build/Android.common_build.mk | 16 +++++----------- compiler/Android.mk | 7 ------- dex2oat/Android.mk | 8 -------- disassembler/Android.mk | 7 ------- oatdump/Android.mk | 7 ------- patchoat/Android.mk | 8 -------- runtime/Android.mk | 9 --------- 7 files changed, 5 insertions(+), 57 deletions(-) diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 0dcefead389..ed2c51d821a 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -29,21 +29,15 @@ include art/build/Android.common.mk # Beware that tests may use the non-debug build for performance, notable 055-enum-performance # ART_BUILD_TARGET_NDEBUG ?= true -ART_BUILD_TARGET_DEBUG ?= true -ART_BUILD_HOST_NDEBUG ?= true -ART_BUILD_HOST_DEBUG ?= true +ART_BUILD_TARGET_DEBUG ?= false +ART_BUILD_HOST_NDEBUG = false +ART_BUILD_HOST_DEBUG = false ifeq ($(ART_BUILD_TARGET_NDEBUG),false) $(info Disabling ART_BUILD_TARGET_NDEBUG) endif -ifeq ($(ART_BUILD_TARGET_DEBUG),false) -$(info Disabling ART_BUILD_TARGET_DEBUG) -endif -ifeq ($(ART_BUILD_HOST_NDEBUG),false) -$(info Disabling ART_BUILD_HOST_NDEBUG) -endif -ifeq ($(ART_BUILD_HOST_DEBUG),false) -$(info Disabling ART_BUILD_HOST_DEBUG) +ifneq ($(ART_BUILD_TARGET_DEBUG),false) +$(info Enabling ART_BUILD_TARGET_DEBUG) endif # diff --git a/compiler/Android.mk b/compiler/Android.mk index 69f93870d59..d87d7b6943f 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -281,13 +281,6 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT endef -# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target. -ifeq ($(ART_BUILD_NDEBUG),true) - $(eval $(call build-libart-compiler,host,ndebug)) -endif -ifeq ($(ART_BUILD_DEBUG),true) - $(eval $(call build-libart-compiler,host,debug)) -endif ifeq ($(ART_BUILD_TARGET_NDEBUG),true) $(eval $(call build-libart-compiler,target,ndebug)) endif diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk index 28db7115d1e..c24f6d6a3b1 100644 --- a/dex2oat/Android.mk +++ b/dex2oat/Android.mk @@ -35,11 +35,3 @@ endif ifeq ($(ART_BUILD_TARGET_DEBUG),true) $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libartd-compiler,art/compiler,target,debug,$(dex2oat_arch))) endif - -# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target. -ifeq ($(ART_BUILD_NDEBUG),true) - $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libart-compiler,art/compiler,host,ndebug)) -endif -ifeq ($(ART_BUILD_DEBUG),true) - $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libartd-compiler,art/compiler,host,debug)) -endif diff --git a/disassembler/Android.mk b/disassembler/Android.mk index a0abc9e1f06..e87f818f09d 100644 --- a/disassembler/Android.mk +++ b/disassembler/Android.mk @@ -98,10 +98,3 @@ endif ifeq ($(ART_BUILD_TARGET_DEBUG),true) $(eval $(call build-libart-disassembler,target,debug)) endif -# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target. -ifeq ($(ART_BUILD_NDEBUG),true) - $(eval $(call build-libart-disassembler,host,ndebug)) -endif -ifeq ($(ART_BUILD_DEBUG),true) - $(eval $(call build-libart-disassembler,host,debug)) -endif diff --git a/oatdump/Android.mk b/oatdump/Android.mk index c35ff857c4a..68e8a2bbbe3 100644 --- a/oatdump/Android.mk +++ b/oatdump/Android.mk @@ -28,13 +28,6 @@ ifeq ($(ART_BUILD_TARGET_DEBUG),true) $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libcutils libartd-disassembler,art/disassembler,target,debug)) endif -ifeq ($(ART_BUILD_HOST_NDEBUG),true) - $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libart-disassembler,art/disassembler,host,ndebug)) -endif -ifeq ($(ART_BUILD_HOST_DEBUG),true) - $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),libartd-disassembler,art/disassembler,host,debug)) -endif - ######################################################################## # oatdump targets diff --git a/patchoat/Android.mk b/patchoat/Android.mk index 8b6b9ad773b..1eaf7815088 100644 --- a/patchoat/Android.mk +++ b/patchoat/Android.mk @@ -35,11 +35,3 @@ endif ifeq ($(ART_BUILD_TARGET_DEBUG),true) $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,debug,$(patchoat_arch))) endif - -# We always build patchoat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target. -ifeq ($(ART_BUILD_NDEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,ndebug)) -endif -ifeq ($(ART_BUILD_DEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,debug)) -endif diff --git a/runtime/Android.mk b/runtime/Android.mk index 17ee8ab2c1d..688a432cdd2 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -469,15 +469,6 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT art_ndebug_or_debug := endef -# We always build dex2oat and dependencies, even if the host build is otherwise disabled, since -# they are used to cross compile for the target. -ifeq ($(ART_BUILD_NDEBUG),true) - $(eval $(call build-libart,host,ndebug)) -endif -ifeq ($(ART_BUILD_DEBUG),true) - $(eval $(call build-libart,host,debug)) -endif - ifeq ($(ART_BUILD_TARGET_NDEBUG),true) # $(error $(call build-libart,target,ndebug)) $(eval $(call build-libart,target,ndebug)) From 3a33ccacc4de619cae3649f1fd76d77925fb54be Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 9 Nov 2014 11:55:48 +0100 Subject: [PATCH 046/150] [Xposed] Make dex2oat capable of recompiling .oat/.odex files We need to inject NOPs at the beginning of every method and possible disable some optimizations like method inlining. Therefore, we cannot accept the pre-compiled .oat/.odex files on the system partition, but have to recompile them. Luckily, these files contain the .dex file(s) they were created from. They have been pre-optimized, so more opcodes are valid. When recompiling the image, also store the original checksum in the header. --- dex2oat/dex2oat.cc | 51 +++++++++++++++++++++++++++-- runtime/oat.cc | 12 +++++++ runtime/oat.h | 3 ++ runtime/runtime.cc | 1 + runtime/runtime.h | 9 +++++ runtime/verifier/method_verifier.cc | 2 +- 6 files changed, 75 insertions(+), 3 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 6d7f1157079..a54a4e9691a 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -70,6 +70,8 @@ namespace art { static int original_argc; static char** original_argv; +static uint32_t original_oat_checksum = 0; +static bool is_recompiling = false; static std::string CommandLine() { std::vector command; @@ -472,6 +474,10 @@ class Dex2Oat { return true; } + void SetRuntimeRecompiling(bool new_value) { + runtime_->SetRecompiling(true); + } + private: explicit Dex2Oat(const CompilerOptions* compiler_options, Compiler::Kind compiler_kind, @@ -572,7 +578,27 @@ static size_t OpenDexFiles(const std::vector& dex_filenames, LOG(WARNING) << "Skipping non-existent dex file '" << dex_filename << "'"; continue; } - if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) { + if (EndsWith(dex_filename, ".oat") || EndsWith(dex_filename, ".odex")) { + const OatFile* oat_file = OatFile::Open(dex_filename, dex_location, nullptr, false, &error_msg); + if (oat_file == nullptr) { + LOG(WARNING) << "Failed to open oat file from '" << dex_filename << "': " << error_msg; + ++failure_count; + } else { + for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { + CHECK(oat_dex_file != nullptr); + std::unique_ptr dex_file(oat_dex_file->OpenDexFile(&error_msg)); + if (dex_file.get() != nullptr) { + dex_files.push_back(dex_file.release()); + } else { + LOG(WARNING) << "Failed to open dex file '" << oat_dex_file->GetDexFileLocation() + << "' from file '" << dex_filename << "': " << error_msg; + ++failure_count; + } + } + } + is_recompiling = true; + original_oat_checksum = oat_file->GetOatHeader().GetChecksum(); + } else if (!DexFile::Open(dex_filename, dex_location, &error_msg, &dex_files)) { LOG(WARNING) << "Failed to open .dex from file '" << dex_filename << "': " << error_msg; ++failure_count; } @@ -821,6 +847,7 @@ static int dex2oat(int argc, char** argv) { std::vector dex_locations; int zip_fd = -1; std::string zip_location; + std::string odex_filename; std::string oat_filename; std::string oat_symbols; std::string oat_location; @@ -1279,6 +1306,10 @@ static int dex2oat(int argc, char** argv) { } std::unique_ptr dex2oat(p_dex2oat); + if (is_recompiling) { + dex2oat->SetRuntimeRecompiling(true); + } + // Runtime::Create acquired the mutator_lock_ that is normally given away when we Runtime::Start, // give it away now so that we don't starve GC. Thread* self = Thread::Current(); @@ -1313,6 +1344,16 @@ static int dex2oat(int argc, char** argv) { if (boot_image_option.empty()) { dex_files = Runtime::Current()->GetClassLinker()->GetBootClassPath(); } else { + if (dex_filenames.empty()) { + odex_filename = DexFilenameToOdexFilename(zip_location, instruction_set); + if (OS::FileExists(odex_filename.c_str())) { + LOG(INFO) << "Using '" << odex_filename << "' instead of file descriptor"; + dex_filenames.push_back(odex_filename.data()); + dex_locations.push_back(odex_filename.data()); + is_recompiling = true; + dex2oat->SetRuntimeRecompiling(true); + } + } if (dex_filenames.empty()) { ATRACE_BEGIN("Opening zip archive from file descriptor"); std::string error_msg; @@ -1335,6 +1376,9 @@ static int dex2oat(int argc, char** argv) { LOG(ERROR) << "Failed to open some dex files: " << failure_count; return EXIT_FAILURE; } + if (is_recompiling) { + dex2oat->SetRuntimeRecompiling(true); + } } const bool kSaveDexInput = false; @@ -1355,7 +1399,7 @@ static int dex2oat(int argc, char** argv) { } // Ensure opened dex files are writable for dex-to-dex transformations. for (const auto& dex_file : dex_files) { - if (!dex_file->EnableWrite()) { + if (!is_recompiling && !dex_file->EnableWrite()) { PLOG(ERROR) << "Failed to make .dex file writeable '" << dex_file->GetLocation() << "'\n"; } } @@ -1393,6 +1437,9 @@ static int dex2oat(int argc, char** argv) { oss.str(""); // Reset. oss << kRuntimeISA; key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str()); + if (image && original_oat_checksum != 0) { + key_value_store->Put(OatHeader::kOriginalOatChecksumKey, StringPrintf("0x%08x", original_oat_checksum)); + } std::unique_ptr compiler(dex2oat->CreateOatFile(boot_image_option, android_root, diff --git a/runtime/oat.cc b/runtime/oat.cc index ede108cd194..e39184a6e6d 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -138,6 +138,18 @@ uint32_t OatHeader::GetChecksum() const { return adler32_checksum_; } +uint32_t OatHeader::GetOriginalChecksum(bool fallback) const { + CHECK(IsValid()); + const char* value = GetStoreValueByKey(OatHeader::kOriginalOatChecksumKey); + if (value != nullptr) { + uint32_t checksum = strtoul(value, nullptr, 0); + if (checksum != 0) { + return checksum; + } + } + return fallback ? adler32_checksum_ : 0; +} + void OatHeader::UpdateChecksum(const void* data, size_t length) { DCHECK(IsValid()); const uint8_t* bytes = reinterpret_cast(data); diff --git a/runtime/oat.h b/runtime/oat.h index 6d5fefe2cec..997150b01f0 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -36,6 +36,8 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline"; static constexpr const char* kDex2OatHostKey = "dex2oat-host"; + static constexpr const char* kOriginalOatChecksumKey = "original-oat-checksum"; + static OatHeader* Create(InstructionSet instruction_set, const InstructionSetFeatures& instruction_set_features, const std::vector* dex_files, @@ -46,6 +48,7 @@ class PACKED(4) OatHeader { bool IsValid() const; const char* GetMagic() const; uint32_t GetChecksum() const; + uint32_t GetOriginalChecksum(bool fallback) const; void UpdateChecksum(const void* data, size_t length); uint32_t GetDexFileCount() const { DCHECK(IsValid()); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index bcb9e0c1ea2..f6ae236a0b9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -100,6 +100,7 @@ Runtime* Runtime::instance_ = NULL; Runtime::Runtime() : instruction_set_(kNone), compiler_callbacks_(nullptr), + is_recompiling_(false), is_zygote_(false), must_relocate_(false), is_concurrent_gc_enabled_(true), diff --git a/runtime/runtime.h b/runtime/runtime.h index 8cfa8aa0fb2..8efbac763be 100644 --- a/runtime/runtime.h +++ b/runtime/runtime.h @@ -94,6 +94,14 @@ class Runtime { return compiler_callbacks_ != nullptr; } + bool IsRecompiling() const { + return is_recompiling_; + } + + void SetRecompiling(bool new_value) { + is_recompiling_ = new_value; + } + bool CanRelocate() const { return !IsCompiler() || compiler_callbacks_->IsRelocationPossible(); } @@ -521,6 +529,7 @@ class Runtime { QuickMethodFrameInfo callee_save_method_frame_infos_[kLastCalleeSaveType]; CompilerCallbacks* compiler_callbacks_; + bool is_recompiling_; bool is_zygote_; bool must_relocate_; bool is_concurrent_gc_enabled_; diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 1720e18b491..36ad27c99f8 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -755,7 +755,7 @@ bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_of result = false; break; } - if (inst->GetVerifyIsRuntimeOnly() && Runtime::Current()->IsCompiler() && !verify_to_dump_) { + if (inst->GetVerifyIsRuntimeOnly() && Runtime::Current()->IsCompiler() && !Runtime::Current()->IsRecompiling() && !verify_to_dump_) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "opcode only expected at runtime " << inst->Name(); result = false; } From 8b13ecf66577d4dfa203cdb0c91e8b2b02c845bd Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 9 Nov 2014 17:42:27 +0100 Subject: [PATCH 047/150] [Xposed] Store an Xposed-specific version in the oat file header This helps to determine whether the file has already been (re-)compiled for the current Xposed version. --- dex2oat/dex2oat.cc | 1 + runtime/oat.cc | 6 ++++++ runtime/oat.h | 3 +++ 3 files changed, 10 insertions(+) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index a54a4e9691a..14d454b692d 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1437,6 +1437,7 @@ static int dex2oat(int argc, char** argv) { oss.str(""); // Reset. oss << kRuntimeISA; key_value_store->Put(OatHeader::kDex2OatHostKey, oss.str()); + key_value_store->Put(OatHeader::kXposedOatVersionKey, OatHeader::kXposedOatCurrentVersion); if (image && original_oat_checksum != 0) { key_value_store->Put(OatHeader::kOriginalOatChecksumKey, StringPrintf("0x%08x", original_oat_checksum)); } diff --git a/runtime/oat.cc b/runtime/oat.cc index e39184a6e6d..910ad9c4428 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -128,6 +128,12 @@ bool OatHeader::IsValid() const { return true; } +bool OatHeader::IsXposedOatVersionValid() const { + CHECK(IsValid()); + const char* version = GetStoreValueByKey(OatHeader::kXposedOatVersionKey); + return version != nullptr && strcmp(version, kXposedOatCurrentVersion) == 0; +} + const char* OatHeader::GetMagic() const { CHECK(IsValid()); return reinterpret_cast(magic_); diff --git a/runtime/oat.h b/runtime/oat.h index 997150b01f0..0bf38b1bb11 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -37,6 +37,8 @@ class PACKED(4) OatHeader { static constexpr const char* kDex2OatHostKey = "dex2oat-host"; static constexpr const char* kOriginalOatChecksumKey = "original-oat-checksum"; + static constexpr const char* kXposedOatVersionKey = "xposed-oat-version"; + static constexpr const char* kXposedOatCurrentVersion = "1"; static OatHeader* Create(InstructionSet instruction_set, const InstructionSetFeatures& instruction_set_features, @@ -46,6 +48,7 @@ class PACKED(4) OatHeader { const SafeMap* variable_data); bool IsValid() const; + bool IsXposedOatVersionValid() const; const char* GetMagic() const; uint32_t GetChecksum() const; uint32_t GetOriginalChecksum(bool fallback) const; From fbba3301bc983a5efd8013522d566e98526ee870 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 9 Nov 2014 14:42:18 +0100 Subject: [PATCH 048/150] [Xposed] Force recompilation of .oat files with Xposed This triggers dex2oat (again) if the .oat file hasn't been compiled with support for the current Xposed version. --- runtime/class_linker.cc | 26 ++++++++++++++++++++----- runtime/native/dalvik_system_DexFile.cc | 7 +++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index d71836778d6..aa78495cf34 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -611,8 +611,9 @@ bool ClassLinker::GenerateOatFile(const char* dex_filename, } boot_image_option += heap->GetImageSpace()->GetImageLocation(); + std::string odex_filename(DexFilenameToOdexFilename(dex_filename, kRuntimeISA)); std::string dex_file_option("--dex-file="); - dex_file_option += dex_filename; + dex_file_option += OS::FileExists(odex_filename.c_str()) ? odex_filename : dex_filename; std::string oat_fd_option("--oat-fd="); StringAppendF(&oat_fd_option, "%d", oat_fd); @@ -762,8 +763,8 @@ static bool LoadMultiDexFilesFromOatFile(const OatFile* oat_file, break; // Not found, done. } - // Checksum test. Test must succeed when generated. - success = !generated; + // Checksum test. + success = true; if (next_location_checksum_pointer != nullptr) { success = next_location_checksum == oat_dex_file->GetDexFileLocationChecksum(); } @@ -910,8 +911,13 @@ bool ClassLinker::OpenDexFilesFromOat(const char* dex_location, const char* oat_ // Need a checksum, fail else. if (!have_checksum) { - error_msgs->push_back(checksum_error_msg); - return false; + std::string odex_filename(DexFilenameToOdexFilename(dex_location, kRuntimeISA)); + if (OS::FileExists(odex_filename.c_str())) { + error_msgs->clear(); + } else { + error_msgs->push_back(checksum_error_msg); + return false; + } } // Look in cache location if no oat_location is given. @@ -1002,6 +1008,11 @@ const OatFile* ClassLinker::FindOatFileInOatLocationForDexFile(const char* dex_l } } + if (!oat_file->GetOatHeader().IsXposedOatVersionValid()) { + *error_msg = StringPrintf("Failed to find oat file at '%s' with expected Xposed oat version", oat_location); + return nullptr; + } + const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_location, &dex_location_checksum); if (oat_dex_file == nullptr) { @@ -1540,6 +1551,11 @@ bool ClassLinker::CheckOatFile(const OatFile* oat_file, InstructionSet isa, real_patch_delta, oat_patch_delta); } + if (!oat_header.IsXposedOatVersionValid()) { + compound_msg += " Oat Image Xposed oat version invalid"; + *checksum_verified = false; + } + bool ret = (*checksum_verified && offset_verified && patch_delta_verified); if (ret) { *error_msg = compound_msg; diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 01c8978a2b3..9205d676847 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -299,6 +299,13 @@ static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* error_msg.clear(); return kDexoptNeeded; } + if (!oat_file->GetOatHeader().IsXposedOatVersionValid()) { + if (kReasonLogging) { + LOG(INFO) << "DexFile_isDexOptNeeded file " << oat_filename + << " needs to be recompiled with Xposed for " << filename; + } + return kDexoptNeeded; + } bool should_relocate_if_possible = Runtime::Current()->ShouldRelocate(); uint32_t location_checksum = 0; const art::OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(filename, nullptr, From 0c9ba3a403272943067f9629696ff4cd85ee48db Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 9 Nov 2014 18:10:06 +0100 Subject: [PATCH 049/150] [Xposed] Force recompilation of the boot image with Xposed The boot image needs to be recompiled again to allow hooking framework classes. The invalidation algorithm needs to ensure that the used image fits to the original dex/odex files and was compiled for the correct Xposed oat version. We assume that the system odex is never compiled for Xposed and therefore just check whether the dalvik-cache version is correct. The checksum will be different after recompilation, so the original checksum is compared. If the criteria are not fulfilled, the image will be recompiled. --- runtime/gc/space/image_space.cc | 62 +++++++++++++++++++++++++++++---- runtime/oat.cc | 30 ++++++++++++++++ runtime/oat.h | 4 +++ 3 files changed, 89 insertions(+), 7 deletions(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 63b09e7108a..9f875edbc79 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -115,7 +115,7 @@ static void RealPruneDexCache(const std::string& cache_dir_path) { } static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa, - std::string* error_msg) { + std::string* error_msg, std::string system_filename) { const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString()); std::vector boot_class_path; Split(boot_class_path_string, ':', boot_class_path); @@ -138,8 +138,14 @@ static bool GenerateImage(const std::string& image_filename, InstructionSet imag image_option_string += image_filename; arg_vector.push_back(image_option_string); - for (size_t i = 0; i < boot_class_path.size(); i++) { - arg_vector.push_back(std::string("--dex-file=") + boot_class_path[i]); + if (system_filename == nullptr) { + for (size_t i = 0; i < boot_class_path.size(); i++) { + arg_vector.push_back(std::string("--dex-file=") + boot_class_path[i]); + } + } else { + std::string dex_file_option_string("--dex-file="); + dex_file_option_string += ImageHeader::GetOatLocationFromImageLocation(system_filename); + arg_vector.push_back(dex_file_option_string); } std::string oat_file_option_string("--oat-file="); @@ -313,7 +319,14 @@ ImageHeader* ImageSpace::ReadImageHeader(const char* image_location, image_location, cache_filename.c_str()); return nullptr; } - if (sys_hdr->GetOatChecksum() != cache_hdr->GetOatChecksum()) { + std::string cache_oat_filename = ImageHeader::GetOatLocationFromImageLocation(cache_filename); + std::unique_ptr cache_oat_hdr(OatHeader::FromFile(cache_oat_filename, error_msg)); + if (cache_oat_hdr.get() == nullptr) { + *error_msg = StringPrintf("Unable to read oat file header for %s at %s: %s", + image_location, cache_oat_filename.c_str(), error_msg->c_str()); + return nullptr; + } + if (sys_hdr->GetOatChecksum() != cache_oat_hdr->GetOriginalChecksum(true)) { *error_msg = StringPrintf("Unable to find a relocated version of image file %s", image_location); return nullptr; @@ -335,8 +348,11 @@ ImageHeader* ImageSpace::ReadImageHeader(const char* image_location, error_msg)); std::unique_ptr cache(ReadSpecificImageHeader(cache_filename.c_str(), error_msg)); + std::string cache_oat_filename = ImageHeader::GetOatLocationFromImageLocation(cache_filename); + std::unique_ptr cache_oat_hdr(OatHeader::FromFile(cache_oat_filename, error_msg)); if (system.get() == nullptr || - (cache.get() != nullptr && cache->GetOatChecksum() == system->GetOatChecksum())) { + (cache.get() != nullptr && cache_oat_hdr.get() != nullptr && + cache_oat_hdr->GetOriginalChecksum(true) == system->GetOatChecksum())) { return cache.release(); } else { return system.release(); @@ -391,6 +407,37 @@ ImageSpace* ImageSpace::Create(const char* image_location, ImageSpace* space; bool relocate = Runtime::Current()->ShouldRelocate(); bool can_compile = Runtime::Current()->IsImageDex2OatEnabled(); + + const std::string* image_filename = nullptr; + bool is_system = false; + bool relocated_version_used = false; + if (has_cache) { + std::string cache_oat_filename = ImageHeader::GetOatLocationFromImageLocation(cache_filename); + std::unique_ptr cache_oat_hdr(OatHeader::FromFile(cache_oat_filename, error_msg)); + if (cache_oat_hdr.get() == nullptr) { + LOG(INFO) << "Forcing image recompilation because " << cache_oat_filename + << " could not be opened: " << *error_msg; + } else if (!cache_oat_hdr->IsXposedOatVersionValid()) { + XLOG(INFO) << "Forcing image recompilation because " << cache_filename + << " is not compiled for the current Xposed version"; + } else { + std::unique_ptr system_hdr(new ImageHeader); + if (has_system && ReadSpecificImageHeader(system_filename.c_str(), system_hdr.get())) { + if (system_hdr->GetOatChecksum() == cache_oat_hdr->GetOriginalChecksum(true)) { + image_filename = &cache_filename; + is_system = true; + } + } else { + image_filename = &cache_filename; + is_system = has_system; + } + } + } + // TODO: Consider relocating in the rare case that the system image is already prepared for Xposed + + if (image_filename != nullptr) { + +#if 0 if (found_image) { const std::string* image_filename; bool is_system = false; @@ -463,6 +510,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, image_filename = &cache_filename; } } +#endif { // Note that we must not use the file descriptor associated with // ScopedFlock::GetFile to Init the image file. We want the file @@ -512,7 +560,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, return nullptr; } else if (!ImageCreationAllowed(is_global_cache, error_msg)) { return nullptr; - } else if (!GenerateImage(cache_filename, image_isa, error_msg)) { + } else if (!GenerateImage(cache_filename, image_isa, error_msg, system_filename)) { *error_msg = StringPrintf("Failed to generate image '%s': %s", cache_filename.c_str(), error_msg->c_str()); // We failed to create files, remove any possibly garbage output. @@ -528,7 +576,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, // we leave Create. ScopedFlock image_lock; image_lock.Init(cache_filename.c_str(), error_msg); - space = ImageSpace::Init(cache_filename.c_str(), image_location, true, error_msg); + space = ImageSpace::Init(cache_filename.c_str(), image_location, !has_system, error_msg); if (space == nullptr) { *error_msg = StringPrintf("Failed to load generated image '%s': %s", cache_filename.c_str(), error_msg->c_str()); diff --git a/runtime/oat.cc b/runtime/oat.cc index 910ad9c4428..b3d7b354e41 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -20,6 +20,12 @@ #include #include +#include "base/stringprintf.h" +#include "base/unix_file/fd_file.h" +#include "elf_file.h" +#include "oat_file.h" +#include "os.h" + namespace art { const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' }; @@ -59,6 +65,30 @@ OatHeader* OatHeader::Create(InstructionSet instruction_set, variable_data); } +OatHeader* OatHeader::FromFile(const std::string& filename, std::string* error_msg) { + std::unique_ptr file(OS::OpenFileForReading(filename.c_str())); + if (file.get() == nullptr) { + *error_msg = StringPrintf("Could not get oat header because file could not be opened: %s", filename.c_str()); + return nullptr; + } + std::unique_ptr elf_file(ElfFile::Open(file.release(), false, false, error_msg)); + if (elf_file.get() == nullptr) { + return nullptr; + } + std::unique_ptr oat_file(OatFile::OpenWithElfFile(elf_file.release(), filename, + error_msg)); + if (oat_file.get() == nullptr) { + return nullptr; + } + + const OatHeader& oat_header = oat_file->GetOatHeader(); + size_t header_size = oat_header.GetHeaderSize(); + void* memory = operator new (header_size); + std::unique_ptr hdr(new (memory) OatHeader()); + memcpy(hdr.get(), &oat_header, header_size); + return hdr.release(); +} + OatHeader::OatHeader(InstructionSet instruction_set, const InstructionSetFeatures& instruction_set_features, const std::vector* dex_files, diff --git a/runtime/oat.h b/runtime/oat.h index 0bf38b1bb11..6e7b91b1436 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -47,6 +47,8 @@ class PACKED(4) OatHeader { uint32_t image_file_location_oat_data_begin, const SafeMap* variable_data); + static OatHeader* FromFile(const std::string& filename, std::string* error_msg); + bool IsValid() const; bool IsXposedOatVersionValid() const; const char* GetMagic() const; @@ -111,6 +113,8 @@ class PACKED(4) OatHeader { size_t GetHeaderSize() const; private: + OatHeader() {} + OatHeader(InstructionSet instruction_set, const InstructionSetFeatures& instruction_set_features, const std::vector* dex_files, From 47257bd99e8c92fdda3edeef3872c58228bed76e Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 16 Nov 2014 09:29:17 +0100 Subject: [PATCH 050/150] [Xposed] Introduce dummy method access flags One flag marks a method that has been hooked by Xposed. The other one is set on the backup of the ArtMethod structure, which is used when invoking the original method. Hooked methods behave similar to proxy methods and reuse parts of their code. In some places, updates to the method structure (fixing up trampolines, adding instrumentation handlers) should be applied to the original (backup) method instead of the hooked (proxy) method. --- runtime/class_linker.cc | 3 +++ runtime/instrumentation.cc | 3 +++ runtime/mirror/art_method-inl.h | 2 +- runtime/mirror/art_method.cc | 2 +- runtime/mirror/art_method.h | 24 ++++++++++++++++++++++++ runtime/modifiers.h | 3 +++ 6 files changed, 35 insertions(+), 2 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index aa78495cf34..87049c80aca 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2566,6 +2566,9 @@ void ClassLinker::FixupStaticTrampolines(mirror::Class* klass) { portable_code = oat_method.GetPortableCode(); quick_code = oat_method.GetQuickCode(); } + if (UNLIKELY(method->IsXposedHookedMethod())) { + method = method->GetXposedOriginalMethod(); + } const bool enter_interpreter = NeedsInterpreter(method, quick_code, portable_code); bool have_portable_code = false; if (enter_interpreter) { diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc index 652d29bf05b..052efa651cb 100644 --- a/runtime/instrumentation.cc +++ b/runtime/instrumentation.cc @@ -123,6 +123,9 @@ static void UpdateEntrypoints(mirror::ArtMethod* method, const void* quick_code, } void Instrumentation::InstallStubsForMethod(mirror::ArtMethod* method) { + if (UNLIKELY(method->IsXposedHookedMethod())) { + method = method->GetXposedOriginalMethod(); + } if (method->IsAbstract() || method->IsProxyMethod()) { // Do not change stubs for these methods. return; diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 9782dde6870..ca4ebd66ca4 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -356,7 +356,7 @@ inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() { // Direct method is cloned from original java.lang.reflect.Proxy class together with code // and as a result it is executed as usual quick compiled method without any stubs. // So the frame info should be returned as it is a quick method not a stub. - if (UNLIKELY(IsAbstract()) || UNLIKELY(IsProxyMethod() && !IsDirect())) { + if (UNLIKELY(IsAbstract()) || UNLIKELY(IsProxyMethod() && !IsDirect()) || UNLIKELY(IsXposedHookedMethod())) { return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); } if (UNLIKELY(IsRuntimeMethod())) { diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 27499c2ffb2..349115cd24a 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -119,7 +119,7 @@ size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) { } bool ArtMethod::IsProxyMethod() { - return GetDeclaringClass()->IsProxyClass(); + return GetDeclaringClass()->IsProxyClass() || IsXposedHookedMethod(); } ArtMethod* ArtMethod::FindOverriddenMethod() { diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index abfdd426f3b..7ada8bff2af 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -36,6 +36,12 @@ class ScopedObjectAccessAlreadyRunnable; class StringPiece; class ShadowFrame; +struct XposedHookInfo { + jobject reflectedMethod; + jobject additionalInfo; + mirror::ArtMethod* originalMethod; +}; + namespace mirror { typedef void (EntryPointFromInterpreter)(Thread* self, MethodHelper& mh, @@ -288,6 +294,7 @@ class MANAGED ArtMethod FINAL : public Object { template void SetEntryPointFromQuickCompiledCode(const void* entry_point_from_quick_compiled_code) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(!IsXposedHookedMethod()); SetFieldPtr( EntryPointFromQuickCompiledCodeOffset(), entry_point_from_quick_compiled_code); } @@ -479,6 +486,23 @@ class MANAGED ArtMethod FINAL : public Object { ALWAYS_INLINE ArtMethod* GetInterfaceMethodIfProxy() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Xposed + bool IsXposedHookedMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccXposedHookedMethod) != 0; + } + + bool IsXposedOriginalMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return (GetAccessFlags() & kAccXposedOriginalMethod) != 0; + } + + XposedHookInfo* GetXposedHookInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return (XposedHookInfo*) GetNativeMethod(); + } + + ArtMethod* GetXposedOriginalMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return GetXposedHookInfo()->originalMethod; + } + protected: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of. diff --git a/runtime/modifiers.h b/runtime/modifiers.h index 23c18f86ff4..4a901dce6f8 100644 --- a/runtime/modifiers.h +++ b/runtime/modifiers.h @@ -68,6 +68,9 @@ static constexpr uint32_t kAccReferenceFlagsMask = (kAccClassIsReference | kAccClassIsFinalizerReference | kAccClassIsPhantomReference); +static constexpr uint32_t kAccXposedHookedMethod = 0x10000000; // method has been hooked by Xposed +static constexpr uint32_t kAccXposedOriginalMethod = 0x20000000; // method is a backup created by Xposed + // Valid (meaningful) bits for a field. static constexpr uint32_t kAccValidFieldFlags = kAccPublic | kAccPrivate | kAccProtected | kAccStatic | kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum; From 384513be225309b615ea6fad9c542ac70cd3e2ed Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 9 Nov 2014 20:25:35 +0100 Subject: [PATCH 051/150] [Xposed] Invocation handler for hooked methods Add logic to the proxy invocation handler so it can handler hooked methods. Much of the code can be shared, it mainly differs in the Java handler method calls and less strict checks (e.g. a hooked method can be static, a proxy method cannot; exceptions are not wrapped). The reference to the handler class/method is stored as a static reference and is set in Xposed. This is not the best code style and could be exchanged for getter/setter later (with no practical benefit though). --- runtime/entrypoints/entrypoint_utils.cc | 72 +++++++++++++++++++ runtime/entrypoints/entrypoint_utils.h | 5 ++ .../quick/quick_trampoline_entrypoints.cc | 24 +++++-- runtime/mirror/art_method.cc | 2 + runtime/mirror/art_method.h | 3 + 5 files changed, 100 insertions(+), 6 deletions(-) diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 6ede6de6704..6e5e6b83c9e 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -343,4 +343,76 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons return zero; } } + +JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty, + jobject rcvr_jobj, jmethodID method, + std::vector& args) { + // Build argument array possibly triggering GC. + soa.Self()->AssertThreadSuspensionIsAllowable(); + jobjectArray args_jobj = NULL; + const JValue zero; + int32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion(); + // Do not create empty arrays unless needed to maintain Dalvik bug compatibility. + if (args.size() > 0 || (target_sdk_version > 0 && target_sdk_version <= 21)) { + args_jobj = soa.Env()->NewObjectArray(args.size(), WellKnownClasses::java_lang_Object, NULL); + if (args_jobj == NULL) { + CHECK(soa.Self()->IsExceptionPending()); + return zero; + } + for (size_t i = 0; i < args.size(); ++i) { + if (shorty[i + 1] == 'L') { + jobject val = args.at(i).l; + soa.Env()->SetObjectArrayElement(args_jobj, i, val); + } else { + JValue jv; + jv.SetJ(args.at(i).j); + mirror::Object* val = BoxPrimitive(Primitive::GetType(shorty[i + 1]), jv); + if (val == NULL) { + CHECK(soa.Self()->IsExceptionPending()); + return zero; + } + soa.Decode* >(args_jobj)->Set(i, val); + } + } + } + + XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo(); + + // Call XposedBridge.handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, + // Object thisObject, Object[] args) + jvalue invocation_args[5]; + invocation_args[0].l = hookInfo->reflectedMethod; + invocation_args[1].i = 0; + invocation_args[2].l = hookInfo->additionalInfo; + invocation_args[3].l = rcvr_jobj; + invocation_args[4].l = args_jobj; + jobject result = + soa.Env()->CallStaticObjectMethodA(mirror::ArtMethod::xposed_callback_class, + mirror::ArtMethod::xposed_callback_method, + invocation_args); + + + // Unbox the result if necessary and return it. + if (UNLIKELY(soa.Self()->IsExceptionPending())) { + return zero; + } else { + if (shorty[0] == 'V' || (shorty[0] == 'L' && result == NULL)) { + return zero; + } + StackHandleScope<1> hs(soa.Self()); + MethodHelper mh_method(hs.NewHandle(soa.DecodeMethod(method))); + // This can cause thread suspension. + mirror::Object* rcvr = soa.Decode(rcvr_jobj); + ThrowLocation throw_location(rcvr, mh_method.GetMethod(), -1); + mirror::Object* result_ref = soa.Decode(result); + mirror::Class* result_type = mh_method.GetReturnType(); + JValue result_unboxed; + if (!UnboxPrimitiveForResult(throw_location, result_ref, result_type, &result_unboxed)) { + DCHECK(soa.Self()->IsExceptionPending()); + return zero; + } + return result_unboxed; + } +} + } // namespace art diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h index bc95c234fec..50c5aacc3b1 100644 --- a/runtime/entrypoints/entrypoint_utils.h +++ b/runtime/entrypoints/entrypoint_utils.h @@ -181,6 +181,11 @@ JValue InvokeProxyInvocationHandler(ScopedObjectAccessAlreadyRunnable& soa, cons std::vector& args) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); +JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, const char* shorty, + jobject rcvr_jobj, jmethodID method, + std::vector& args) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Entry point for deoptimization. extern "C" void art_quick_deoptimize(); static inline uintptr_t GetQuickDeoptimizationEntryPoint() { diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index dfd2e11fc29..4fe72640235 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -587,7 +587,9 @@ extern "C" uint64_t artQuickProxyInvokeHandler(mirror::ArtMethod* proxy_method, Thread* self, StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(proxy_method->IsProxyMethod()) << PrettyMethod(proxy_method); - DCHECK(receiver->GetClass()->IsProxyClass()) << PrettyMethod(proxy_method); + const bool is_xposed = proxy_method->IsXposedHookedMethod(); + const bool is_static = proxy_method->IsStatic(); + DCHECK(is_xposed || receiver->GetClass()->IsProxyClass()) << PrettyMethod(proxy_method); // Ensure we don't get thread suspension until the object arguments are safely in jobjects. const char* old_cause = self->StartAssertNoThreadSuspension("Adding to IRT proxy object arguments"); @@ -603,20 +605,30 @@ extern "C" uint64_t artQuickProxyInvokeHandler(mirror::ArtMethod* proxy_method, ScopedObjectAccessUnchecked soa(env); ScopedJniEnvLocalRefState env_state(env); // Create local ref. copies of proxy method and the receiver. - jobject rcvr_jobj = soa.AddLocalReference(receiver); + jobject rcvr_jobj = is_static ? nullptr : soa.AddLocalReference(receiver); // Placing arguments into args vector and remove the receiver. mirror::ArtMethod* non_proxy_method = proxy_method->GetInterfaceMethodIfProxy(); - CHECK(!non_proxy_method->IsStatic()) << PrettyMethod(proxy_method) << " " - << PrettyMethod(non_proxy_method); + CHECK(is_xposed || !non_proxy_method->IsStatic()) << PrettyMethod(proxy_method) << " " + << PrettyMethod(non_proxy_method); std::vector args; uint32_t shorty_len = 0; const char* shorty = proxy_method->GetShorty(&shorty_len); - BuildQuickArgumentVisitor local_ref_visitor(sp, false, shorty, shorty_len, &soa, &args); + BuildQuickArgumentVisitor local_ref_visitor(sp, is_static, shorty, shorty_len, &soa, &args); local_ref_visitor.VisitArguments(); DCHECK_GT(args.size(), 0U) << PrettyMethod(proxy_method); - args.erase(args.begin()); + if (!is_static) { + args.erase(args.begin()); + } + + if (is_xposed) { + jmethodID proxy_methodid = soa.EncodeMethod(proxy_method); + self->EndAssertNoThreadSuspension(old_cause); + JValue result = InvokeXposedHandleHookedMethod(soa, shorty, rcvr_jobj, proxy_methodid, args); + local_ref_visitor.FixupReferences(); + return result.GetJ(); + } // Convert proxy method into expected interface method. mirror::ArtMethod* interface_method = proxy_method->FindOverriddenMethod(); diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 349115cd24a..328d7ae66de 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -48,6 +48,8 @@ extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Th // TODO: get global references for these GcRoot ArtMethod::java_lang_reflect_ArtMethod_; +jclass ArtMethod::xposed_callback_class = NULL; +jmethodID ArtMethod::xposed_callback_method = NULL; ArtMethod* ArtMethod::FromReflectedMethod(const ScopedObjectAccessAlreadyRunnable& soa, jobject jlr_method) { diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 7ada8bff2af..5d32fdd3eab 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -503,6 +503,9 @@ class MANAGED ArtMethod FINAL : public Object { return GetXposedHookInfo()->originalMethod; } + static jclass xposed_callback_class; + static jmethodID xposed_callback_method; + protected: // Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses". // The class we are a part of. From 31b8ff843a613a365e00917e52f2742925344bc3 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 16 Nov 2014 09:36:40 +0100 Subject: [PATCH 052/150] [Xposed] Add a method to place the hook for a method This creates a backup of the original method description structure (which is later used to invoke the original code) and turns the hooked method into a proxy. The modified proxy invocation handler will take care of routing calls to XposedBridge.handleHookedMethod(). Some additional information is stored in the field for the native code pointer. --- runtime/mirror/art_method.cc | 45 ++++++++++++++++++++++++++++++++++++ runtime/mirror/art_method.h | 2 ++ 2 files changed, 47 insertions(+) diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 328d7ae66de..721060791e3 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -390,5 +390,50 @@ void ArtMethod::UnregisterNative(Thread* self) { RegisterNative(self, GetJniDlsymLookupStub(), false); } +void ArtMethod::EnableXposedHook(JNIEnv* env, jobject additional_info) { + if (IsXposedHookedMethod()) { + // Already hooked + return; + } else if (IsXposedOriginalMethod()) { + // This should never happen + XLOG(FATAL) << "You cannot hook the original method"; + } else if (IsNative()) { + // Hooking native methods should hopefully work fine, but needs testing + XLOG(INFO) << "Not hooking native method " << PrettyMethod(this); + return; + } + + + ScopedObjectAccess soa(env); + + // Create a backup of the ArtMethod object + ArtMethod* backup_method = down_cast(Clone(soa.Self())); + // Set private flag to avoid virtual table lookups during invocation + backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccPrivate | kAccXposedOriginalMethod); + + // Create a Method/Constructor object for the backup ArtMethod object + jobject reflect_method; + if (IsConstructor()) { + reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Constructor); + } else { + reflect_method = env->AllocObject(WellKnownClasses::java_lang_reflect_Method); + } + env->SetObjectField(reflect_method, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, + env->NewGlobalRef(soa.AddLocalReference(backup_method))); + + // Save extra information in a separate structure, stored instead of the native method + XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo)); + hookInfo->reflectedMethod = env->NewGlobalRef(reflect_method); + hookInfo->additionalInfo = env->NewGlobalRef(additional_info); + hookInfo->originalMethod = backup_method; + SetNativeMethod((uint8_t*) hookInfo); + + SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()); + SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); + + // Adjust access flags + SetAccessFlags((GetAccessFlags() & ~kAccNative) | kAccXposedHookedMethod); +} + } // namespace mirror } // namespace art diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 5d32fdd3eab..0bf34d1a7f6 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -495,6 +495,8 @@ class MANAGED ArtMethod FINAL : public Object { return (GetAccessFlags() & kAccXposedOriginalMethod) != 0; } + void EnableXposedHook(JNIEnv* env, jobject additional_info) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + XposedHookInfo* GetXposedHookInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return (XposedHookInfo*) GetNativeMethod(); } From 2b65a9cdd230ddee9beb7ee4806e525475582f09 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 9 Nov 2014 20:58:04 +0100 Subject: [PATCH 053/150] [Xposed] Optional target size for Object.Clone() This makes it possible to allocate more bytes than actually needed to store the object. Used by Xposed to clone an object to a subclass (the class of the object is changed after cloning). --- runtime/gc/heap.cc | 2 +- runtime/mirror/object.cc | 13 +++++++++++-- runtime/mirror/object.h | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 6af98cf14ea..041242ec841 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -3167,7 +3167,7 @@ void Heap::AddModUnionTable(accounting::ModUnionTable* mod_union_table) { void Heap::CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) { CHECK(c == NULL || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) || - (c->IsVariableSize() || c->GetObjectSize() == byte_count)); + (c->IsVariableSize() || c->GetObjectSize() <= byte_count)); CHECK_GE(byte_count, sizeof(mirror::Object)); } diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc index a1177d645d2..7c4a225e854 100644 --- a/runtime/mirror/object.cc +++ b/runtime/mirror/object.cc @@ -118,19 +118,28 @@ class CopyObjectVisitor { }; Object* Object::Clone(Thread* self) { + return Clone(self, 0); +} + +Object* Object::Clone(Thread* self, size_t num_target_bytes) { CHECK(!IsClass()) << "Can't clone classes."; // Object::SizeOf gets the right size even if we're an array. Using c->AllocObject() here would // be wrong. gc::Heap* heap = Runtime::Current()->GetHeap(); size_t num_bytes = SizeOf(); + if (LIKELY(num_target_bytes == 0)) { + num_target_bytes = num_bytes; + } else { + CHECK(num_target_bytes >= num_bytes); + } StackHandleScope<1> hs(self); Handle this_object(hs.NewHandle(this)); Object* copy; CopyObjectVisitor visitor(self, &this_object, num_bytes); if (heap->IsMovableObject(this)) { - copy = heap->AllocObject(self, GetClass(), num_bytes, visitor); + copy = heap->AllocObject(self, GetClass(), num_target_bytes, visitor); } else { - copy = heap->AllocNonMovableObject(self, GetClass(), num_bytes, visitor); + copy = heap->AllocNonMovableObject(self, GetClass(), num_target_bytes, visitor); } return copy; } diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h index a6b622719e8..5f9f22e3e6d 100644 --- a/runtime/mirror/object.h +++ b/runtime/mirror/object.h @@ -103,6 +103,7 @@ class MANAGED LOCKABLE Object { size_t SizeOf() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Object* Clone(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + Object* Clone(Thread* self, size_t num_target_bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int32_t IdentityHashCode() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); From 5f70203fd3ab8c6e3dbf2772e8444fdcf22ee657 Mon Sep 17 00:00:00 2001 From: Nicolas Geoffray Date: Wed, 12 Nov 2014 18:02:36 +0000 Subject: [PATCH 054/150] Add a --method-filter option to oatdump. Cherry-picked from 3fcd22051f5ac12f0825204b534912af38f02c61 Change-Id: Iaf7fef7228d9d8a901bd9b98452d244d42ca497e --- oatdump/oatdump.cc | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 83c7871300e..9203d3d1116 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -102,6 +102,10 @@ static void usage() { " --no-disassemble may be used to disable disassembly.\n" " Example: --no-disassemble\n" "\n"); + fprintf(stderr, + " --method-filter=: only dumps methods that contain the filter.\n" + " Example: --method-filter=foo\n" + "\n"); exit(EXIT_FAILURE); } @@ -122,18 +126,21 @@ class OatDumperOptions { bool dump_raw_gc_map, bool dump_vmap, bool disassemble_code, - bool absolute_addresses) + bool absolute_addresses, + const char* method_filter) : dump_raw_mapping_table_(dump_raw_mapping_table), dump_raw_gc_map_(dump_raw_gc_map), dump_vmap_(dump_vmap), disassemble_code_(disassemble_code), - absolute_addresses_(absolute_addresses) {} + absolute_addresses_(absolute_addresses), + method_filter_(method_filter) {} const bool dump_raw_mapping_table_; const bool dump_raw_gc_map_; const bool dump_vmap_; const bool disassemble_code_; const bool absolute_addresses_; + const char* const method_filter_; }; class OatDumper { @@ -443,8 +450,12 @@ class OatDumper { uint32_t dex_method_idx, const DexFile::CodeItem* code_item, uint32_t method_access_flags) { bool success = true; + std::string pretty_method = PrettyMethod(dex_method_idx, dex_file, true); + if (pretty_method.find(options_->method_filter_) == std::string::npos) { + return success; + } os << StringPrintf("%d: %s (dex_method_idx=%d)\n", - class_method_index, PrettyMethod(dex_method_idx, dex_file, true).c_str(), + class_method_index, pretty_method.c_str(), dex_method_idx); Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count); std::unique_ptr indent1_os(new std::ostream(&indent1_filter)); @@ -1686,6 +1697,7 @@ static int oatdump(int argc, char** argv) { } const char* oat_filename = nullptr; + const char* method_filter_ = ""; const char* image_location = nullptr; const char* boot_image_location = nullptr; InstructionSet instruction_set = kRuntimeISA; @@ -1734,6 +1746,8 @@ static int oatdump(int argc, char** argv) { usage(); } os = out.get(); + } else if (option.starts_with("--method-filter=")) { + method_filter_ = option.substr(strlen("--method-filter=")).data(); } else { fprintf(stderr, "Unknown argument %s\n", option.data()); usage(); @@ -1756,7 +1770,8 @@ static int oatdump(int argc, char** argv) { dump_raw_gc_map, dump_vmap, disassemble_code, - absolute_addresses)); + absolute_addresses, + method_filter_)); MemMap::Init(); if (oat_filename != nullptr) { std::string error_msg; From c6117a3c5e6d72d8195d50120184b709109a2954 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 7 Dec 2014 09:44:23 +0100 Subject: [PATCH 055/150] [Xposed] Disable direct branching optimization Under certain circumstances (e.g. the method must be part of the boot image and cannot be a virtual method), ART calls methods directly using their known code offset in the image. That makes hooking these methods impossible (or rather ineffective, as the hook isn't triggered). Hence, this optimization has to be disabled. --- compiler/driver/compiler_driver.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index fa2a560f9b7..83b9ba0e383 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1118,6 +1118,9 @@ void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType // TODO: support patching on all architectures. use_dex_cache = force_relocations && !support_boot_image_fixup_; } + // Direct branching to the method's code offset means that Xposed hooks are not considered + // So we always need to go through the dex cache/ArtMethod + use_dex_cache = true; bool method_code_in_boot = (method->GetDeclaringClass()->GetClassLoader() == nullptr); if (!use_dex_cache) { if (!method_code_in_boot) { From ec49e88ac013ed8689ea8f4c8f56ef02d0e41745 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Mon, 9 Feb 2015 21:29:23 +0100 Subject: [PATCH 056/150] [Xposed] Special logging function/flag Usually, all messages related to ART are written to logcat with tag "art". In order to facilitate analysis, we introduce XLOG instead of LOG to write messages with tag "Xposed" instead. --- runtime/base/logging.h | 1 + runtime/base/logging_android.cc | 7 ++++++- runtime/log_severity.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/base/logging.h b/runtime/base/logging.h index caeb946ff0b..eb91c8a46ed 100644 --- a/runtime/base/logging.h +++ b/runtime/base/logging.h @@ -133,6 +133,7 @@ #endif #define LOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, -1).stream() +#define XLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity|LOG_XPOSED, -1).stream() #define PLOG(severity) ::art::LogMessage(__FILE__, __LINE__, severity, errno).stream() #define LG LOG(INFO) diff --git a/runtime/base/logging_android.cc b/runtime/base/logging_android.cc index 9b1ac58461f..97d48409e79 100644 --- a/runtime/base/logging_android.cc +++ b/runtime/base/logging_android.cc @@ -32,7 +32,12 @@ static const int kLogSeverityToAndroidLogPriority[] = { void LogMessage::LogLine(const LogMessageData& data, const char* message) { const char* tag = ProgramInvocationShortName(); - int priority = kLogSeverityToAndroidLogPriority[data.severity]; + LogSeverity severity = data.severity; + if ((severity & LOG_XPOSED) != 0) { + tag = "Xposed"; + severity &= ~LOG_XPOSED; + } + int priority = kLogSeverityToAndroidLogPriority[severity]; if (priority == ANDROID_LOG_FATAL) { LOG_PRI(priority, tag, "%s:%d] %s", data.file, data.line_number, message); } else { diff --git a/runtime/log_severity.h b/runtime/log_severity.h index 31682dfeee8..f552143e291 100644 --- a/runtime/log_severity.h +++ b/runtime/log_severity.h @@ -21,5 +21,6 @@ typedef int LogSeverity; const int VERBOSE = 0, DEBUG = 1, INFO = 2, WARNING = 3, ERROR = 4, FATAL = 5; const int INTERNAL_FATAL = 6; // For Runtime::Abort. +const int LOG_XPOSED = 1024; #endif // ART_RUNTIME_LOG_SEVERITY_H_ From 46cbd7a5a0348810667c218ab031466df907017b Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 23 Jan 2015 15:47:45 -0800 Subject: [PATCH 057/150] ART: clean up compiler data clean up compiler allocated data when compilation is done Change-Id: I9b73de2c43657a66759f3c85dc00c5b25ec3f76c --- compiler/dex/mir_graph.cc | 5 +++++ compiler/dex/mir_graph.h | 2 ++ compiler/dex/quick/arm/codegen_arm.h | 2 ++ compiler/dex/quick/arm/target_arm.cc | 9 +++++++++ compiler/dex/quick/arm64/codegen_arm64.h | 2 ++ compiler/dex/quick/arm64/target_arm64.cc | 9 +++++++++ 6 files changed, 29 insertions(+) diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 584cef60d3f..ac897c850d4 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -133,6 +133,11 @@ MIRGraph::MIRGraph(CompilationUnit* cu, ArenaAllocator* arena) MIRGraph::~MIRGraph() { STLDeleteElements(&m_units_); + CleanupGraphData(); +} + +void MIRGraph::CleanupGraphData() +{ } /* diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h index d990dc6bf0d..0feb28a6e7c 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -1101,6 +1101,8 @@ class MIRGraph { */ bool HasSuspendTestBetween(BasicBlock* source, BasicBlockId target_id); + void CleanupGraphData() QC_WEAK; + protected: int FindCommonParent(int block1, int block2); void ComputeSuccLineIn(ArenaBitVector* dest, const ArenaBitVector* src1, diff --git a/compiler/dex/quick/arm/codegen_arm.h b/compiler/dex/quick/arm/codegen_arm.h index d9e6c011025..2185864a473 100644 --- a/compiler/dex/quick/arm/codegen_arm.h +++ b/compiler/dex/quick/arm/codegen_arm.h @@ -32,6 +32,7 @@ class QCArmMir2Lir; class ArmMir2Lir FINAL : public Mir2Lir { public: ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); + ~ArmMir2Lir(); // Required for target - codegen helpers. bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, @@ -196,6 +197,7 @@ class ArmMir2Lir FINAL : public Mir2Lir { size_t GetInstructionOffset(LIR* lir); void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) OVERRIDE; void GenMoreMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) QC_WEAK; + void Cleanup() QC_WEAK; //void MachineSpecificPreprocessMIR(BasicBlock* bb, MIR* mir); private: diff --git a/compiler/dex/quick/arm/target_arm.cc b/compiler/dex/quick/arm/target_arm.cc index 7c280eca4a8..3be2b805428 100644 --- a/compiler/dex/quick/arm/target_arm.cc +++ b/compiler/dex/quick/arm/target_arm.cc @@ -570,6 +570,15 @@ ArmMir2Lir::ArmMir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* ArmMir2LirPostInit(this); } +void ArmMir2Lir::Cleanup() +{ +} + +ArmMir2Lir::~ArmMir2Lir() +{ + Cleanup(); +} + void ArmMir2Lir::ArmMir2LirPostInit(ArmMir2Lir* mir_to_lir) { } diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h index 0edafe0884a..390cbb61291 100644 --- a/compiler/dex/quick/arm64/codegen_arm64.h +++ b/compiler/dex/quick/arm64/codegen_arm64.h @@ -67,6 +67,7 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { public: Arm64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAllocator* arena); + ~Arm64Mir2Lir(); // Required for target - codegen helpers. bool SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_div, RegLocation rl_src, @@ -267,6 +268,7 @@ class Arm64Mir2Lir FINAL : public Mir2Lir { virtual void GenMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) OVERRIDE; void GenMoreMachineSpecificExtendedMethodMIR(BasicBlock* bb, MIR* mir) QC_WEAK; + void Cleanup() QC_WEAK; private: /** diff --git a/compiler/dex/quick/arm64/target_arm64.cc b/compiler/dex/quick/arm64/target_arm64.cc index 8368c5b7bcf..75b9bfae2f8 100644 --- a/compiler/dex/quick/arm64/target_arm64.cc +++ b/compiler/dex/quick/arm64/target_arm64.cc @@ -597,6 +597,15 @@ Arm64Mir2Lir::Arm64Mir2Lir(CompilationUnit* cu, MIRGraph* mir_graph, ArenaAlloca Arm64Mir2LirPostInit(this); } +void Arm64Mir2Lir::Cleanup() +{ +} + +Arm64Mir2Lir::~Arm64Mir2Lir() +{ + Cleanup(); +} + void Arm64Mir2Lir::Arm64Mir2LirPostInit(Arm64Mir2Lir* mir_to_lir) { } From 72e99756152e79abb699eb854d9ed7a2ca26b121 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Thu, 12 Feb 2015 20:51:51 +0100 Subject: [PATCH 058/150] [Xposed] Don't crash for dex files without extension DexFilenameToOdexFilename assumes that all dex files have a three-letter extension, but that is not always true. In particular, the app "Quizduell" creates temporary dex files without extension (and non-alphabetic characters) and crashes during startup. Xposed uses this function in more places than ART itself so this assumption becomes visible. DexFile.isDexOptNeeded() would probably crash as well if it was called. --- runtime/utils.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/runtime/utils.cc b/runtime/utils.cc index 376575918fa..5c726d40661 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1301,9 +1301,12 @@ std::string DexFilenameToOdexFilename(const std::string& location, const Instruc std::string odex_location(location); InsertIsaDirectory(isa, &odex_location); size_t dot_index = odex_location.size() - 3 - 1; // 3=dex or zip or apk - CHECK_EQ('.', odex_location[dot_index]) << location; - odex_location.resize(dot_index + 1); - CHECK_EQ('.', odex_location[odex_location.size()-1]) << location << " " << odex_location; + if (odex_location[dot_index] == '.') { + odex_location.resize(dot_index + 1); + CHECK_EQ('.', odex_location[odex_location.size()-1]) << location << " " << odex_location; + } else { + odex_location += "."; + } odex_location += "odex"; return odex_location; } From d586da3e8940e6433acb86d6526a6ffbfc0b2c0d Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 8 Mar 2015 21:08:49 +0100 Subject: [PATCH 059/150] [Xposed] Disable method inlining Some very easy methods are inlined into the caller's code, making it impossible to hook them (or rather, the hook won't be called). Thus, this optimization has to be disabled for Xposed to work properly. Increasing the version number ensures that already compiled files in the Dalvik cache are thrown away and recompiled with the new setting. Hopefully fixes #4. --- compiler/dex/frontend.cc | 2 +- runtime/oat.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/dex/frontend.cc b/compiler/dex/frontend.cc index 45fc19e6645..e6553f81927 100644 --- a/compiler/dex/frontend.cc +++ b/compiler/dex/frontend.cc @@ -55,7 +55,7 @@ static uint32_t kCompilerOptimizerDisableFlags = 0 | // Disable specific optimi // (1 << kMatch) | // (1 << kPromoteCompilerTemps) | // (1 << kSuppressExceptionEdges) | - // (1 << kSuppressMethodInlining) | + (1 << kSuppressMethodInlining) | 0; static uint32_t kCompilerDebugFlags = 0 | // Enable debug/testing modes diff --git a/runtime/oat.h b/runtime/oat.h index 6e7b91b1436..170dcc71d75 100644 --- a/runtime/oat.h +++ b/runtime/oat.h @@ -38,7 +38,7 @@ class PACKED(4) OatHeader { static constexpr const char* kOriginalOatChecksumKey = "original-oat-checksum"; static constexpr const char* kXposedOatVersionKey = "xposed-oat-version"; - static constexpr const char* kXposedOatCurrentVersion = "1"; + static constexpr const char* kXposedOatCurrentVersion = "2"; static OatHeader* Create(InstructionSet instruction_set, const InstructionSetFeatures& instruction_set_features, From 1dcbe0a165d3baae292af3113bbbebf45012bcde Mon Sep 17 00:00:00 2001 From: Scott Date: Mon, 23 Mar 2015 17:47:00 -0500 Subject: [PATCH 060/150] Xposed: Use C++-style casts instead of C-style --- runtime/mirror/art_method.cc | 4 ++-- runtime/mirror/art_method.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index c30999301a9..8ca4b3cfa93 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -405,11 +405,11 @@ void ArtMethod::EnableXposedHook(JNIEnv* env, jobject additional_info) { env->NewGlobalRef(soa.AddLocalReference(backup_method))); // Save extra information in a separate structure, stored instead of the native method - XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo)); + XposedHookInfo* hookInfo = reinterpret_cast(calloc(1, sizeof(XposedHookInfo))); hookInfo->reflectedMethod = env->NewGlobalRef(reflect_method); hookInfo->additionalInfo = env->NewGlobalRef(additional_info); hookInfo->originalMethod = backup_method; - SetNativeMethod((uint8_t*) hookInfo); + SetEntryPointFromJni(reinterpret_cast(hookInfo)); SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()); SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index c590282ec5c..e397214fdc4 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -561,7 +561,7 @@ class MANAGED ArtMethod FINAL : public Object { void EnableXposedHook(JNIEnv* env, jobject additional_info) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); XposedHookInfo* GetXposedHookInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return (XposedHookInfo*) GetNativeMethod(); + return reinterpret_cast(GetEntryPointFromJni()); } ArtMethod* GetXposedOriginalMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { From 9c9132f78af7280adabadbdf5a5b9e1984dff6ce Mon Sep 17 00:00:00 2001 From: Scott Date: Mon, 23 Mar 2015 17:47:30 -0500 Subject: [PATCH 061/150] Xposed: Duplicate function definition --- runtime/mirror/art_method-inl.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 29af8cf6823..d56a85cc771 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -530,10 +530,6 @@ inline mirror::DexCache* ArtMethod::GetDexCache() { return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetDexCache(); } -inline bool ArtMethod::IsProxyMethod() { - return GetDeclaringClass()->IsProxyClass(); -} - inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy() { if (LIKELY(!IsProxyMethod())) { return this; From 2843068750ca51d502302c63777fb74eb2dae951 Mon Sep 17 00:00:00 2001 From: Scott Date: Mon, 23 Mar 2015 17:47:52 -0500 Subject: [PATCH 062/150] Xposed: OatFile::Open function signature has changed --- dex2oat/dex2oat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 777feabb1e9..749aa52dbdd 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -611,7 +611,7 @@ static size_t OpenDexFiles(const std::vector& dex_filenames, continue; } if (EndsWith(dex_filename, ".oat") || EndsWith(dex_filename, ".odex")) { - const OatFile* oat_file = OatFile::Open(dex_filename, dex_location, nullptr, false, &error_msg); + const OatFile* oat_file = OatFile::Open(dex_filename, dex_location, nullptr, nullptr, false, &error_msg); if (oat_file == nullptr) { LOG(WARNING) << "Failed to open oat file from '" << dex_filename << "': " << error_msg; ++failure_count; From cb71712200a2b6148a8c08000b1a27c74df9e33b Mon Sep 17 00:00:00 2001 From: Scott Date: Mon, 23 Mar 2015 17:48:29 -0500 Subject: [PATCH 063/150] Xposed: Linting cleanup --- runtime/gc/space/image_space.cc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index 0ecf9e3c6ff..51349db8417 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -500,7 +500,6 @@ ImageSpace* ImageSpace::Create(const char* image_location, // TODO: Consider relocating in the rare case that the system image is already prepared for Xposed if (image_filename != nullptr) { - #if 0 if (found_image) { const std::string* image_filename; @@ -854,7 +853,6 @@ void ImageSpace::Dump(std::ostream& os) const { << ",size=" << PrettySize(Size()) << ",name=\"" << GetName() << "\"]"; } - +} // namespace space } // namespace space } // namespace gc -} // namespace art From 6455e77839200f980241c8b361ee23d793220b4c Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sat, 8 Nov 2014 06:14:18 -0800 Subject: [PATCH 064/150] art: Update QC-PERF board flag to TARGET_HAVE_QC_PERF * Some targets will set QCOM_HARDWARE but won't have these libs. Change-Id: I84292c190c120b646f5017d4801e7893fed902ff --- compiler/Android.mk | 2 +- runtime/Android.mk | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/Android.mk b/compiler/Android.mk index da97e479ae7..3985bbd3281 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -259,7 +259,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk ifeq ($$(art_target_or_host),target) LOCAL_SHARED_LIBRARIES += libcutils libvixl - ifeq ($(BOARD_USES_QCOM_HARDWARE),true) + ifeq ($(TARGET_HAVE_QC_PERF),true) LOCAL_WHOLE_STATIC_LIBRARIES += libqc-art-compiler endif include $(BUILD_SHARED_LIBRARY) diff --git a/runtime/Android.mk b/runtime/Android.mk index 20b82a4daa3..898f5ecdae6 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -427,7 +427,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT ifeq ($$(art_target_or_host),target) LOCAL_SHARED_LIBRARIES += libcutils libdl libselinux libutils libsigchain LOCAL_STATIC_LIBRARIES := libziparchive libz - ifeq ($(BOARD_USES_QCOM_HARDWARE),true) + ifeq ($(TARGET_HAVE_QC_PERF),true) LOCAL_WHOLE_STATIC_LIBRARIES += libqc-art endif else # host From 4880c1b2985e9327ce85ea3b251fc835ab81a5c4 Mon Sep 17 00:00:00 2001 From: Brian Chu Date: Fri, 9 Jan 2015 13:43:28 -0800 Subject: [PATCH 065/150] libsigchain: Fix OSX compilation Change-Id: I8555280cbcee85c44af098cb9c804c958709125d --- sigchainlib/Android.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sigchainlib/Android.mk b/sigchainlib/Android.mk index b7ff3606fdb..e8793d3c74f 100644 --- a/sigchainlib/Android.mk +++ b/sigchainlib/Android.mk @@ -53,6 +53,9 @@ LOCAL_SRC_FILES := sigchain_dummy.cc LOCAL_MODULE:= libsigchain LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk LOCAL_LDLIBS = -ldl +ifeq ($(strip $(HOST_OS)),darwin) +LOCAL_LDFLAGS += -Wl,-lstdc++ +endif LOCAL_MULTILIB := both include $(BUILD_HOST_SHARED_LIBRARY) From 5c8b65b03d4284cd5f6e912dfe344edc858ef559 Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 1 Apr 2015 17:07:13 -0500 Subject: [PATCH 066/150] Xposed: Move IsProxyMethod() to match upstream --- runtime/mirror/art_method-inl.h | 4 ++++ runtime/mirror/art_method.cc | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index d56a85cc771..29f4e1da704 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -530,6 +530,10 @@ inline mirror::DexCache* ArtMethod::GetDexCache() { return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetDexCache(); } +inline bool ArtMethod::IsProxyMethod() { + return GetDeclaringClass()->IsProxyClass() || IsXposedHookedMethod(); +} + inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy() { if (LIKELY(!IsProxyMethod())) { return this; diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 8ca4b3cfa93..8acb65130e3 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -103,10 +103,6 @@ size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) { return num_registers; } -bool ArtMethod::IsProxyMethod() { - return GetDeclaringClass()->IsProxyClass() || IsXposedHookedMethod(); -} - ArtMethod* ArtMethod::FindOverriddenMethod() { if (IsStatic()) { return NULL; From 8bde2310a69c936e3424c5f8824e124147775483 Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 1 Apr 2015 17:21:48 -0500 Subject: [PATCH 067/150] Xposed: Fix mis-merge from android-5.1.0_r1 --- runtime/mirror/art_method.h | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index e397214fdc4..4cdd29b5154 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -330,6 +330,7 @@ class MANAGED ArtMethod FINAL : public Object { ALWAYS_INLINE void SetEntryPointFromQuickCompiledCodePtrSize( const void* entry_point_from_quick_compiled_code, size_t pointer_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(!IsXposedHookedMethod()); SetFieldPtrWithSize( EntryPointFromQuickCompiledCodeOffset(pointer_size), entry_point_from_quick_compiled_code, pointer_size); From 928bcff0c827cd2349d87dc926c1a5fd3066c643 Mon Sep 17 00:00:00 2001 From: Scott Date: Wed, 15 Apr 2015 09:33:35 -0500 Subject: [PATCH 068/150] Revert "Support proxy method in StackVisitor::GetThisObject" This reverts commit fcfc8a70894be642dae08350361d04c14a6d7be5. --- .../quick/quick_trampoline_entrypoints.cc | 23 ------------------- runtime/stack.cc | 9 -------- 2 files changed, 32 deletions(-) diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 5392357a128..428df17e662 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -199,22 +199,6 @@ class QuickArgumentVisitor { #endif public: - // Special handling for proxy methods. Proxy methods are instance methods so the - // 'this' object is the 1st argument. They also have the same frame layout as the - // kRefAndArgs runtime method. Since 'this' is a reference, it is located in the - // 1st GPR. - static mirror::Object* GetProxyThisObject(StackReference* sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - CHECK(sp->AsMirrorPtr()->IsProxyMethod()); - CHECK_EQ(kQuickCalleeSaveFrame_RefAndArgs_FrameSize, sp->AsMirrorPtr()->GetFrameSizeInBytes()); - CHECK_GT(kNumQuickGprArgs, 0u); - constexpr uint32_t kThisGprIndex = 0u; // 'this' is in the 1st GPR. - size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset + - GprIndexToGprOffset(kThisGprIndex); - uint8_t* this_arg_address = reinterpret_cast(sp) + this_arg_offset; - return reinterpret_cast*>(this_arg_address)->AsMirrorPtr(); - } - static mirror::ArtMethod* GetCallingMethod(StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod()); @@ -426,13 +410,6 @@ class QuickArgumentVisitor { bool is_split_long_or_double_; }; -// Returns the 'this' object of a proxy method. This function is only used by StackVisitor. It -// allows to use the QuickArgumentVisitor constants without moving all the code in its own module. -extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference* sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return QuickArgumentVisitor::GetProxyThisObject(sp); -} - // Visits arguments on the stack placing them into the shadow frame. class BuildQuickShadowFrameVisitor FINAL : public QuickArgumentVisitor { public: diff --git a/runtime/stack.cc b/runtime/stack.cc index 7dd4f081d19..0bd7032fed1 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -112,9 +112,6 @@ uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const { } } -extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference* sp) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - mirror::Object* StackVisitor::GetThisObject() const { mirror::ArtMethod* m = GetMethod(); if (m->IsStatic()) { @@ -127,12 +124,6 @@ mirror::Object* StackVisitor::GetThisObject() const { } else { return cur_shadow_frame_->GetVRegReference(0); } - } else if (m->IsProxyMethod()) { - if (cur_quick_frame_ != nullptr) { - return artQuickGetProxyThisObject(cur_quick_frame_); - } else { - return cur_shadow_frame_->GetVRegReference(0); - } } else { const DexFile::CodeItem* code_item = m->GetCodeItem(); if (code_item == NULL) { From 2c7a0d6d3f7f0e2bc0006e89c9619236ece6e95e Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 17 Apr 2015 10:20:40 -0500 Subject: [PATCH 069/150] Revert "Remove method verification results right after compiling a method" This reverts commit 25fda92083d5b93b38cc1f6b12ac6a44d992d6a4. Conflicts: compiler/driver/compiler_driver.cc compiler/driver/compiler_driver.h dex2oat/dex2oat.cc --- compiler/dex/quick/codegen_util.cc | 4 -- compiler/dex/verification_results.cc | 9 ----- compiler/dex/verification_results.h | 1 - compiler/driver/compiler_driver.cc | 52 +++----------------------- compiler/driver/compiler_driver.h | 4 -- compiler/driver/dex_compilation_unit.h | 6 +-- dex2oat/dex2oat.cc | 21 +++++++++-- 7 files changed, 25 insertions(+), 72 deletions(-) diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index cb5fbb3d0c9..edd72714585 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -773,10 +773,6 @@ void Mir2Lir::CreateNativeGcMap() { ": " << PrettyMethod(cu_->method_idx, *cu_->dex_file); native_gc_map_builder.AddEntry(native_offset, references); } - - // Maybe not necessary, but this could help prevent errors where we access the verified method - // after it has been deleted. - mir_graph_->GetCurrentDexCompilationUnit()->ClearVerifiedMethod(); } /* Determine the offset of each literal field */ diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 2db2be73b55..b3c6e4d1c89 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -84,15 +84,6 @@ const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref return (it != verified_methods_.end()) ? it->second : nullptr; } -void VerificationResults::RemoveVerifiedMethod(MethodReference ref) { - WriterMutexLock mu(Thread::Current(), verified_methods_lock_); - auto it = verified_methods_.find(ref); - if (it != verified_methods_.end()) { - delete it->second; - verified_methods_.erase(it); - } -} - void VerificationResults::AddRejectedClass(ClassReference ref) { { WriterMutexLock mu(Thread::Current(), rejected_classes_lock_); diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h index 7fc2a2363d2..0e7923fbc3b 100644 --- a/compiler/dex/verification_results.h +++ b/compiler/dex/verification_results.h @@ -48,7 +48,6 @@ class VerificationResults { const VerifiedMethod* GetVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_); - void RemoveVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_); void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 1e32c2e9a13..551eb8df04a 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -22,10 +22,6 @@ #include #include -#ifndef __APPLE__ -#include // For mallinfo -#endif - #include "base/stl_util.h" #include "base/timing_logger.h" #include "class_linker.h" @@ -508,7 +504,6 @@ void CompilerDriver::CompileAll(jobject class_loader, TimingLogger* timings) { DCHECK(!Runtime::Current()->IsStarted()); std::unique_ptr thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1)); - VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false); PreCompile(class_loader, dex_files, thread_pool.get(), timings); Compile(class_loader, dex_files, thread_pool.get(), timings); if (dump_stats_) { @@ -605,25 +600,20 @@ void CompilerDriver::Resolve(jobject class_loader, const std::vector& dex_files, ThreadPool* thread_pool, TimingLogger* timings) { LoadImageClasses(timings); - VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false); Resolve(class_loader, dex_files, thread_pool, timings); - VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false); if (!compiler_options_->IsVerificationEnabled()) { - VLOG(compiler) << "Verify none mode specified, skipping verification."; + LOG(INFO) << "Verify none mode specified, skipping verification."; SetVerified(class_loader, dex_files, thread_pool, timings); return; } Verify(class_loader, dex_files, thread_pool, timings); - VLOG(compiler) << "Verify: " << GetMemoryUsageString(false); InitializeClasses(class_loader, dex_files, thread_pool, timings); - VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false); UpdateImageClasses(timings); - VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); } bool CompilerDriver::IsImageClass(const char* descriptor) const { @@ -1988,7 +1978,6 @@ void CompilerDriver::Compile(jobject class_loader, const std::vectorGetVerifiedMethod(method_ref) != nullptr; bool compile = compilation_enabled && // Basic checks, e.g., not . @@ -2145,17 +2134,15 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t Thread* self = Thread::Current(); if (compiled_method != nullptr) { - DCHECK(GetCompiledMethod(method_ref) == nullptr) << PrettyMethod(method_idx, dex_file); + MethodReference ref(&dex_file, method_idx); + DCHECK(GetCompiledMethod(ref) == nullptr) << PrettyMethod(method_idx, dex_file); { MutexLock mu(self, compiled_methods_lock_); - compiled_methods_.Put(method_ref, compiled_method); + compiled_methods_.Put(ref, compiled_method); } - DCHECK(GetCompiledMethod(method_ref) != nullptr) << PrettyMethod(method_idx, dex_file); + DCHECK(GetCompiledMethod(ref) != nullptr) << PrettyMethod(method_idx, dex_file); } - // Done compiling, delete the verified method to reduce native memory usage. - verification_results_->RemoveVerifiedMethod(method_ref); - if (self->IsExceptionPending()) { ScopedObjectAccess soa(self); LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n" @@ -2300,31 +2287,4 @@ bool CompilerDriver::SkipCompilation(const std::string& method_name) { } return !compile; } - -std::string CompilerDriver::GetMemoryUsageString(bool extended) const { - std::ostringstream oss; - const ArenaPool* arena_pool = GetArenaPool(); - gc::Heap* heap = Runtime::Current()->GetHeap(); - oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated()); - oss << " java alloc=" << PrettySize(heap->GetBytesAllocated()); -#ifdef HAVE_MALLOC_H - struct mallinfo info = mallinfo(); - const size_t allocated_space = static_cast(info.uordblks); - const size_t free_space = static_cast(info.fordblks); - oss << " native alloc=" << PrettySize(allocated_space) << " free=" - << PrettySize(free_space); -#endif - if (swap_space_.get() != nullptr) { - oss << " swap=" << PrettySize(swap_space_->GetSize()); - } - if (extended) { - oss << "\nCode dedupe: " << dedupe_code_.DumpStats(); - oss << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats(); - oss << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(); - oss << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats(); - oss << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(); - } - return oss.str(); -} - } // namespace art diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index db07a61085c..a74162f0ef5 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -40,7 +40,6 @@ #include "utils/arena_allocator.h" #include "utils/dedupe_set.h" #include "utils/swap_space.h" -#include "dex/verified_method.h" namespace art { @@ -664,9 +663,6 @@ class CompilerDriver { // Should the compiler run on this method given profile information? bool SkipCompilation(const std::string& method_name); - // Get memory usage during compilation. - std::string GetMemoryUsageString(bool extended) const; - private: // These flags are internal to CompilerDriver for collecting INVOKE resolution statistics. // The only external contract is that unresolved method has flags 0 and resolved non-0. diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h index 03ae489da11..84f57991c33 100644 --- a/compiler/driver/dex_compilation_unit.h +++ b/compiler/driver/dex_compilation_unit.h @@ -102,10 +102,6 @@ class DexCompilationUnit { return verified_method_; } - void ClearVerifiedMethod() { - verified_method_ = nullptr; - } - const std::string& GetSymbol(); private: @@ -121,7 +117,7 @@ class DexCompilationUnit { const uint16_t class_def_idx_; const uint32_t dex_method_idx_; const uint32_t access_flags_; - const VerifiedMethod* verified_method_; + const VerifiedMethod* const verified_method_; std::string symbol_; }; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 749aa52dbdd..91c97011d3c 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -25,6 +25,10 @@ #include #include +#ifndef __APPLE__ +#include // For mallinfo +#endif + #if defined(__linux__) && defined(__arm__) #include #include @@ -275,9 +279,20 @@ class Dex2Oat { } void LogCompletionTime(const CompilerDriver* compiler) { + std::ostringstream mallinfostr; +#ifdef HAVE_MALLOC_H + struct mallinfo info = mallinfo(); + const size_t allocated_space = static_cast(info.uordblks); + const size_t free_space = static_cast(info.fordblks); + mallinfostr << " native alloc=" << PrettySize(allocated_space) << " free=" + << PrettySize(free_space); +#endif + const ArenaPool* arena_pool = compiler->GetArenaPool(); + gc::Heap* heap = Runtime::Current()->GetHeap(); LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_) - << " (threads: " << thread_count_ << ") " - << compiler->GetMemoryUsageString(kIsDebugBuild || VLOG_IS_ON(compiler)); + << " (threads: " << thread_count_ << ")" + << " arena alloc=" << PrettySize(arena_pool->GetBytesAllocated()) + << " java alloc=" << PrettySize(heap->GetBytesAllocated()) << mallinfostr.str(); } @@ -899,7 +914,7 @@ static int dex2oat(int argc, char** argv) { ? Compiler::kPortable : Compiler::kQuick; const char* compiler_filter_string = nullptr; - bool compile_pic = false; + bool compile_pic = true; int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold; int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold; int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold; From 87833d8abd311a82be4383e13d371351cb4c4946 Mon Sep 17 00:00:00 2001 From: Yevgeny Rouban Date: Thu, 19 Mar 2015 19:01:11 +0600 Subject: [PATCH 070/150] ART: patchoat gets gzip support for compressed odex files The patchoat is capable to inflate a compressed odex file into a temporary file. Then the temporary file is patched as if it is specified with --input-oat-fd or --input-oat-file. The new options are the following: --input-oat-gz-fd --input-oat-gz-file --swap-fd --swap-file Could not make in-memory decompression and patching because the ElfFile class maps Elf file using its file descriptor. So a temporary file is used as it is done by dex2oat. Both gzip and zip are supported by zlib library and they have the same compression algorithm. Gzip is chosen because zip needs a zip entry name, which is unnecessary for single-file compression. Change-Id: I54dbfcdc58bc0f57f7eccc3929c09295c597794b Signed-off-by: Yevgeny Rouban --- build/Android.executable.mk | 2 + patchoat/Android.mk | 8 +- patchoat/patchoat.cc | 148 +++++++++++++++++++++++++++++++++++- 3 files changed, 151 insertions(+), 7 deletions(-) diff --git a/build/Android.executable.mk b/build/Android.executable.mk index 412f2ddfee2..6fd6b2bfff7 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -50,6 +50,7 @@ define build-art-executable art_target_or_host := $(5) art_ndebug_or_debug := $(6) art_multilib := $(7) + art_static_libraries := $(8) include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) @@ -57,6 +58,7 @@ define build-art-executable LOCAL_SRC_FILES := $$(art_source) LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes) LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries) + LOCAL_STATIC_LIBRARIES += $$(art_static_libraries) LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain ifeq ($$(art_ndebug_or_debug),ndebug) diff --git a/patchoat/Android.mk b/patchoat/Android.mk index 8b6b9ad773b..b1fc9a9bd82 100644 --- a/patchoat/Android.mk +++ b/patchoat/Android.mk @@ -30,16 +30,16 @@ else endif ifeq ($(ART_BUILD_TARGET_NDEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,ndebug,$(patchoat_arch))) + $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,ndebug,$(patchoat_arch),libz)) endif ifeq ($(ART_BUILD_TARGET_DEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,debug,$(patchoat_arch))) + $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,debug,$(patchoat_arch),libz)) endif # We always build patchoat and dependencies, even if the host build is otherwise disabled, since they are used to cross compile for the target. ifeq ($(ART_BUILD_NDEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,ndebug)) + $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,ndebug,,libz)) endif ifeq ($(ART_BUILD_DEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,debug)) + $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),,art/compiler,host,debug,,libz)) endif diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index b046ea1ef43..0b356975050 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -45,6 +45,7 @@ #include "runtime.h" #include "scoped_thread_state_change.h" #include "thread.h" +#include "zlib.h" #include "utils.h" namespace art { @@ -800,6 +801,16 @@ static void Usage(const char *fmt, ...) { UsageError(" --input-oat-fd=: Specifies the file-descriptor of the oat file"); UsageError(" to be patched."); UsageError(""); + UsageError(" --input-oat-gz-file=: Specifies the exact filename of"); + UsageError(" the gzip-compressed oat file to be patched."); + UsageError(""); + UsageError(" --input-oat-gz-fd=: Specifies the file-descriptor of"); + UsageError(" the gzip-compressed oat file to be patched."); + UsageError(""); + UsageError(" --swap-file=: Specifies a temporary gzip file."); + UsageError(""); + UsageError(" --swap-fd=: Specifies a temporary gzip file descriptor."); + UsageError(""); UsageError(" --input-oat-location=: Specifies the 'location' to read the patched"); UsageError(" oat file from. If used one must also supply the --instruction-set"); UsageError(""); @@ -908,6 +919,67 @@ static bool FinishFile(File* file, bool close) { } } +static int Inflate(int input_oat_gz_fd, const std::string& input_oat_gz_filename, int tmp_fd, std::string* err) { + gzFile in_gzfile = input_oat_gz_fd != -1 ? gzdopen(input_oat_gz_fd, "rb") : + gzopen(input_oat_gz_filename.c_str(), "rb"); + if (in_gzfile == nullptr) { + *err = input_oat_gz_fd != -1 ? + StringPrintf("Could not open gzip file fd=%d: %s", + input_oat_gz_fd, strerror(errno)) : + StringPrintf("Could not open gzip file %s: %s", + input_oat_gz_filename.c_str(), strerror(errno)); + return -1; + } + + int out_fd = dup(tmp_fd); + if (out_fd == -1) { + *err = strerror(errno); + return -1; + } + + constexpr size_t INFLATE_BUFLEN = 16384; + std::unique_ptr out_file(new File(out_fd, false)); + std::unique_ptr buf(new Byte[INFLATE_BUFLEN]); + int len; + + while (0 < (len = gzread(in_gzfile, buf.get(), INFLATE_BUFLEN))) { + if (!out_file->WriteFully(buf.get(), len)) { + *err = StringPrintf("Could not write to fd=%d: %s", tmp_fd, out_file->GetPath().c_str()); + gzclose(in_gzfile); + return -1; + } + } + + int errnum; + const char* gzerrstr = gzerror(in_gzfile, &errnum); + + if (len < 0 || errnum != Z_OK) { + *err = input_oat_gz_fd != -1 ? + StringPrintf("Could not inflate gzip file fd=%d: %s", + input_oat_gz_fd, gzerrstr) : + StringPrintf("Could not inflate gzip file %s: %s", + input_oat_gz_filename.c_str(), gzerrstr); + gzclose(in_gzfile); + return -1; + } + + if ((errnum = gzclose(in_gzfile)) != Z_OK) { + *err = input_oat_gz_fd != -1 ? + StringPrintf("Could not close gzip file fd=%d: gzclose() returned %d", + input_oat_gz_fd, errnum) : + StringPrintf("Could not close gzip file %s: gzclose() returned %d", + input_oat_gz_filename.c_str(), errnum); + } + + if (out_file->Flush() != 0) { + *err = StringPrintf("Could not flush tmp file fd=%d", tmp_fd); + return -1; + } + + out_file->DisableAutoClose(); + return out_fd; +} + static int patchoat(int argc, char **argv) { InitLogging(argv); MemMap::Init(); @@ -932,8 +1004,12 @@ static int patchoat(int argc, char **argv) { bool isa_set = false; InstructionSet isa = kNone; std::string input_oat_filename; + std::string input_oat_gz_filename; std::string input_oat_location; int input_oat_fd = -1; + int input_oat_gz_fd = -1; + std::string swap_file_name; + int swap_fd = -1; bool have_input_oat = false; std::string input_image_location; std::string output_oat_filename; @@ -968,19 +1044,29 @@ static int patchoat(int argc, char **argv) { } } else if (option.starts_with("--input-oat-location=")) { if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); } have_input_oat = true; input_oat_location = option.substr(strlen("--input-oat-location=")).data(); } else if (option.starts_with("--input-oat-file=")) { if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); } have_input_oat = true; input_oat_filename = option.substr(strlen("--input-oat-file=")).data(); + } else if (option.starts_with("--input-oat-gz-file=")) { + if (have_input_oat) { + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); + } + have_input_oat = true; + input_oat_gz_filename = option.substr(strlen("--input-oat-gz-file=")).data(); } else if (option.starts_with("--input-oat-fd=")) { if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); } have_input_oat = true; const char* oat_fd_str = option.substr(strlen("--input-oat-fd=")).data(); @@ -990,6 +1076,29 @@ static int patchoat(int argc, char **argv) { if (input_oat_fd < 0) { Usage("--input-oat-fd pass a negative value %d", input_oat_fd); } + } else if (option.starts_with("--input-oat-gz-fd=")) { + if (have_input_oat) { + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); + } + have_input_oat = true; + const char* oat_gz_fd_str = option.substr(strlen("--input-oat-gz-fd=")).data(); + if (!ParseInt(oat_gz_fd_str, &input_oat_gz_fd)) { + Usage("Failed to parse --input-oat-fd argument '%s' as an integer", oat_gz_fd_str); + } + if (input_oat_gz_fd < 0) { + Usage("--input-oat-gz-fd pass a negative value %d", input_oat_gz_fd); + } + } else if (option.starts_with("--swap-file=")) { + swap_file_name = option.substr(strlen("--swap-file=")).data(); + } else if (option.starts_with("--swap-fd=")) { + const char* swap_fd_str = option.substr(strlen("--swap-fd=")).data(); + if (!ParseInt(swap_fd_str, &swap_fd)) { + Usage("Failed to parse --swap-fd argument '%s' as an integer", swap_fd_str); + } + if (swap_fd < 0) { + Usage("--swap-fd passed a negative value %d", swap_fd); + } } else if (option.starts_with("--input-image-location=")) { input_image_location = option.substr(strlen("--input-image-location=")).data(); } else if (option.starts_with("--output-oat-file=")) { @@ -1088,6 +1197,11 @@ static int patchoat(int argc, char **argv) { Usage("Either both input and output image must be supplied or niether must be."); } + if ((input_oat_gz_fd != -1 || !input_oat_gz_filename.empty()) != + (swap_fd != -1 || !swap_file_name.empty())) { + Usage("Either both input gzip and swap must be supplied or niether must be."); + } + // We know we have both the input and output so rename for clarity. bool have_image_files = have_output_image; bool have_oat_files = have_output_oat; @@ -1111,6 +1225,26 @@ static int patchoat(int argc, char **argv) { LOG(INFO) << "Using input-oat-file " << input_oat_filename; } } + + // Swap file handling. + // + // If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file + // that we can use for swap. + // + // If the swap fd is -1 and we have a swap-file string, open the given file as a swap file. We + // will immediately unlink to satisfy the swap fd assumption. + std::unique_ptr swap_file; + if (swap_fd == -1 && !swap_file_name.empty()) { + swap_file.reset(OS::CreateEmptyFile(swap_file_name.c_str())); + if (swap_file.get() == nullptr) { + PLOG(ERROR) << "Failed to create swap file: " << swap_file_name; + return EXIT_FAILURE; + } + swap_fd = swap_file->Fd(); + swap_file->MarkUnchecked(); // We don't want to track this, it will be unlinked immediately. + unlink(swap_file_name.c_str()); + } + if (!patched_image_location.empty()) { if (!isa_set) { Usage("specifying a location requires specifying an instruction set"); @@ -1189,8 +1323,16 @@ static int patchoat(int argc, char **argv) { } if (have_oat_files) { + if (input_oat_gz_fd != -1 || !input_oat_gz_filename.empty()) { + std::string err; + input_oat_fd = Inflate(input_oat_gz_fd, input_oat_gz_filename, swap_fd, &err); + if (input_oat_fd == -1) { + LOG(ERROR) << "Failed to inflate input file: " << err; + } + } if (input_oat_fd != -1) { if (input_oat_filename.empty()) { + // TODO: make sure this will not be used to create symlink if input_oat_fd is PIC input_oat_filename = "input-oat-file"; } input_oat.reset(new File(input_oat_fd, input_oat_filename, false)); From f25a2263400719aa473a83e4aec101120b0d12a1 Mon Sep 17 00:00:00 2001 From: Yevgeny Rouban Date: Fri, 20 Mar 2015 15:42:24 +0600 Subject: [PATCH 071/150] ART: prevent patchoat from symlinking with dummy files If OAT file is position independent (PIC, dex2oat's option --compile-pic) then the patchoat just creates a symlink with the input oat file. But if the input oat file is set by its file descriptor (option --input-oat-fd) or just has been inflated to a temporary file (options --input-oat-gz-file or --input-oat-gz-fd) then its file name is set to a dummy 'input-oat-file' and making a symlink with this dummy path makes no sense. This patch fixes the patchoat to make a copy of the input file in such cases. Change-Id: Ib63b0c80b1da06b60cbb754206c510a7396624a9 Signed-off-by: Yevgeny Rouban --- patchoat/patchoat.cc | 114 +++++++++++++++++++++++++++---------------- patchoat/patchoat.h | 15 +++--- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 0b356975050..73c31ebf24b 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -196,7 +196,7 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d File* output_oat, File* output_image, InstructionSet isa, TimingLogger* timings, bool output_oat_opened_from_fd, - bool new_oat_out) { + bool new_oat_out, bool input_oat_filename_dummy) { CHECK(Runtime::Current() == nullptr); CHECK(output_image != nullptr); CHECK_GE(output_image->Fd(), 0); @@ -285,11 +285,9 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d // Error logged by IsOatPic return false; } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching. - if (!ReplaceOatFileWithSymlink(input_oat->GetPath(), - output_oat->GetPath(), - output_oat_opened_from_fd, - new_oat_out)) { + // Do not need to do ELF-file patching. Create a symlink or make a copy. + if (!SymlinkOrCopy(input_oat, output_oat, output_oat_opened_from_fd, + new_oat_out, input_oat_filename_dummy)) { // Errors already logged by above call. return false; } @@ -399,36 +397,70 @@ PatchOat::MaybePic PatchOat::IsOatPic(const ElfFile* oat_in) { return is_pic ? PIC : NOT_PIC; } -bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out) { - // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD. - if (output_oat_opened_from_fd) { - // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC? - LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC"; - return false; - } +const size_t COPY_BUFLEN = 16384; + +bool PatchOat::SymlinkOrCopy(File* input_oat, + File* output_oat, + bool output_oat_opened_from_fd, + bool new_oat_out, bool make_copy) { + std::string output_oat_filename = output_oat->GetPath(); + std::string input_oat_filename = input_oat->GetPath(); + + if (make_copy) { + // Make a copy of the PIC oat file. + std::unique_ptr buf(new char[COPY_BUFLEN]); + int64_t len; + int64_t read_size = 0; + while (true) { + len = input_oat->Read(buf.get(), COPY_BUFLEN, read_size); + if (len <= 0) { + break; + } + if (!output_oat->WriteFully(buf.get(), len)) { + len = -1; + break; + } + read_size += len; + } - // Image was PIC. Create symlink where the oat is supposed to go. - if (!new_oat_out) { - LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite"; - return false; - } + if (len < 0) { + int err = errno; + LOG(ERROR) << "Failed to copy " << input_oat_filename << " to " << output_oat_filename + << ": error(" << err << "): " << strerror(err); + return false; + } + + if (kIsDebugBuild) { + LOG(INFO) << "Copied " << input_oat_filename << " -> " << output_oat_filename; + } + } else { + // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD. + if (output_oat_opened_from_fd) { + // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC? + LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC"; + return false; + } - // Delete the original file, since we won't need it. - TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str())); + // Image was PIC. Create symlink where the oat is supposed to go. + if (!new_oat_out) { + LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite"; + return false; + } - // Create a symlink from the old oat to the new oat - if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) < 0) { - int err = errno; - LOG(ERROR) << "Failed to create symlink at " << output_oat_filename - << " error(" << err << "): " << strerror(err); - return false; - } + // Delete the original file, since we won't need it. + TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str())); - if (kIsDebugBuild) { - LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename; + // Create a symlink from the old oat to the new oat. + if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) == 0) { + if (kIsDebugBuild) { + LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename; + } + } else { + int err = errno; + LOG(ERROR) << "Failed to create symlink at " << output_oat_filename + << " error(" << err << "): " << strerror(err); + return false; + } } return true; @@ -562,7 +594,8 @@ void PatchOat::FixupMethod(mirror::ArtMethod* object, mirror::ArtMethod* copy) { } bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings, - bool output_oat_opened_from_fd, bool new_oat_out) { + bool output_oat_opened_from_fd, bool new_oat_out, + bool input_oat_filename_dummy) { CHECK(input_oat != nullptr); CHECK(output_oat != nullptr); CHECK_GE(input_oat->Fd(), 0); @@ -582,12 +615,10 @@ bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogge // Error logged by IsOatPic return false; } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the rest. + // Do not need to do ELF-file patching. Create a symlink or make a copy. // Any errors will be logged by the function call. - return ReplaceOatFileWithSymlink(input_oat->GetPath(), - output_oat->GetPath(), - output_oat_opened_from_fd, - new_oat_out); + return SymlinkOrCopy(input_oat, output_oat, output_oat_opened_from_fd, + new_oat_out, input_oat_filename_dummy); } else { CHECK(is_oat_pic == NOT_PIC); } @@ -1004,6 +1035,7 @@ static int patchoat(int argc, char **argv) { bool isa_set = false; InstructionSet isa = kNone; std::string input_oat_filename; + bool input_oat_filename_dummy = false; std::string input_oat_gz_filename; std::string input_oat_location; int input_oat_fd = -1; @@ -1332,8 +1364,8 @@ static int patchoat(int argc, char **argv) { } if (input_oat_fd != -1) { if (input_oat_filename.empty()) { - // TODO: make sure this will not be used to create symlink if input_oat_fd is PIC input_oat_filename = "input-oat-file"; + input_oat_filename_dummy = true; } input_oat.reset(new File(input_oat_fd, input_oat_filename, false)); if (input_oat == nullptr) { @@ -1426,7 +1458,7 @@ static int patchoat(int argc, char **argv) { ret = PatchOat::Patch(input_oat.get(), input_image_location, base_delta, output_oat.get(), output_image.get(), isa, &timings, output_oat_fd >= 0, // was it opened from FD? - new_oat_out); + new_oat_out, input_oat_filename_dummy); // The order here doesn't matter. If the first one is successfully saved and the second one // erased, ImageSpace will still detect a problem and not use the files. ret = ret && FinishFile(output_image.get(), ret); @@ -1435,7 +1467,7 @@ static int patchoat(int argc, char **argv) { TimingLogger::ScopedTiming pt("patch oat", &timings); ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings, output_oat_fd >= 0, // was it opened from FD? - new_oat_out); + new_oat_out, input_oat_filename_dummy); ret = ret && FinishFile(output_oat.get(), ret); } else if (have_image_files) { TimingLogger::ScopedTiming pt("patch image", &timings); diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index 03d915abddb..3a21d8514ff 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -44,7 +44,8 @@ class PatchOat { // Patch only the oat file static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings, bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? - bool new_oat_out); // Output oat was a new file created by us? + bool new_oat_out, // Output oat was a new file created by us? + bool input_oat_filename_dummy); // Input cannot be symlinked // Patch only the image (art file) static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa, @@ -55,7 +56,8 @@ class PatchOat { off_t delta, File* oat_out, File* art_out, InstructionSet isa, TimingLogger* timings, bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? - bool new_oat_out); // Output oat was a new file created by us? + bool new_oat_out, // Output oat was a new file created by us? + bool input_oat_filename_dummy); // Input cannot be symlinked private: // Takes ownership only of the ElfFile. All other pointers are only borrowed. @@ -87,10 +89,11 @@ class PatchOat { // Attempt to replace the file with a symlink // Returns false if it fails - static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out); // Output oat was newly created? + static bool SymlinkOrCopy(File* input_oat, + File* output_oat, + bool output_oat_opened_from_fd, + bool new_oat_out, // Output oat was newly created? + bool make_copy); static void BitmapCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { From 55b5e0b03cdb52329568fa5d92cd381ec8cd70f1 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Thu, 30 Apr 2015 20:43:34 +0200 Subject: [PATCH 072/150] [Xposed] Close .oat files after reading their header Otherwise, resources will be exhausted sooner or later on some ROMs. This can lead to bootloops or sudden restarts. Some parts of the code were copied from OpenDexFilesFromImage(), which opens the file for later use. But in OatHeader::FromFile(), we just want to read the header and then close the file again. Fixes rovo89/Xposed#31 --- runtime/oat.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/oat.cc b/runtime/oat.cc index b3d7b354e41..7eb913ae580 100644 --- a/runtime/oat.cc +++ b/runtime/oat.cc @@ -71,7 +71,7 @@ OatHeader* OatHeader::FromFile(const std::string& filename, std::string* error_m *error_msg = StringPrintf("Could not get oat header because file could not be opened: %s", filename.c_str()); return nullptr; } - std::unique_ptr elf_file(ElfFile::Open(file.release(), false, false, error_msg)); + std::unique_ptr elf_file(ElfFile::Open(file.get(), false, false, error_msg)); if (elf_file.get() == nullptr) { return nullptr; } From 9aec5e8443e824050a80a60aca5769aaf44daa4e Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 1 May 2015 11:08:03 -0500 Subject: [PATCH 073/150] Revert "Revert "Remove method verification results right after compiling a method"" This reverts commit 2c7a0d6d3f7f0e2bc0006e89c9619236ece6e95e. --- compiler/dex/quick/codegen_util.cc | 4 ++ compiler/dex/verification_results.cc | 9 +++++ compiler/dex/verification_results.h | 1 + compiler/driver/compiler_driver.cc | 52 +++++++++++++++++++++++--- compiler/driver/compiler_driver.h | 4 ++ compiler/driver/dex_compilation_unit.h | 6 ++- dex2oat/dex2oat.cc | 21 ++--------- 7 files changed, 72 insertions(+), 25 deletions(-) diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index edd72714585..cb5fbb3d0c9 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -773,6 +773,10 @@ void Mir2Lir::CreateNativeGcMap() { ": " << PrettyMethod(cu_->method_idx, *cu_->dex_file); native_gc_map_builder.AddEntry(native_offset, references); } + + // Maybe not necessary, but this could help prevent errors where we access the verified method + // after it has been deleted. + mir_graph_->GetCurrentDexCompilationUnit()->ClearVerifiedMethod(); } /* Determine the offset of each literal field */ diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index b3c6e4d1c89..2db2be73b55 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -84,6 +84,15 @@ const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref return (it != verified_methods_.end()) ? it->second : nullptr; } +void VerificationResults::RemoveVerifiedMethod(MethodReference ref) { + WriterMutexLock mu(Thread::Current(), verified_methods_lock_); + auto it = verified_methods_.find(ref); + if (it != verified_methods_.end()) { + delete it->second; + verified_methods_.erase(it); + } +} + void VerificationResults::AddRejectedClass(ClassReference ref) { { WriterMutexLock mu(Thread::Current(), rejected_classes_lock_); diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h index 0e7923fbc3b..7fc2a2363d2 100644 --- a/compiler/dex/verification_results.h +++ b/compiler/dex/verification_results.h @@ -48,6 +48,7 @@ class VerificationResults { const VerifiedMethod* GetVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_); + void RemoveVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_); void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 551eb8df04a..1e32c2e9a13 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -22,6 +22,10 @@ #include #include +#ifndef __APPLE__ +#include // For mallinfo +#endif + #include "base/stl_util.h" #include "base/timing_logger.h" #include "class_linker.h" @@ -504,6 +508,7 @@ void CompilerDriver::CompileAll(jobject class_loader, TimingLogger* timings) { DCHECK(!Runtime::Current()->IsStarted()); std::unique_ptr thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1)); + VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false); PreCompile(class_loader, dex_files, thread_pool.get(), timings); Compile(class_loader, dex_files, thread_pool.get(), timings); if (dump_stats_) { @@ -600,20 +605,25 @@ void CompilerDriver::Resolve(jobject class_loader, const std::vector& dex_files, ThreadPool* thread_pool, TimingLogger* timings) { LoadImageClasses(timings); + VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false); Resolve(class_loader, dex_files, thread_pool, timings); + VLOG(compiler) << "Resolve: " << GetMemoryUsageString(false); if (!compiler_options_->IsVerificationEnabled()) { - LOG(INFO) << "Verify none mode specified, skipping verification."; + VLOG(compiler) << "Verify none mode specified, skipping verification."; SetVerified(class_loader, dex_files, thread_pool, timings); return; } Verify(class_loader, dex_files, thread_pool, timings); + VLOG(compiler) << "Verify: " << GetMemoryUsageString(false); InitializeClasses(class_loader, dex_files, thread_pool, timings); + VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false); UpdateImageClasses(timings); + VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false); } bool CompilerDriver::IsImageClass(const char* descriptor) const { @@ -1978,6 +1988,7 @@ void CompilerDriver::Compile(jobject class_loader, const std::vectorGetVerifiedMethod(method_ref) != nullptr; bool compile = compilation_enabled && // Basic checks, e.g., not . @@ -2134,15 +2145,17 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t Thread* self = Thread::Current(); if (compiled_method != nullptr) { - MethodReference ref(&dex_file, method_idx); - DCHECK(GetCompiledMethod(ref) == nullptr) << PrettyMethod(method_idx, dex_file); + DCHECK(GetCompiledMethod(method_ref) == nullptr) << PrettyMethod(method_idx, dex_file); { MutexLock mu(self, compiled_methods_lock_); - compiled_methods_.Put(ref, compiled_method); + compiled_methods_.Put(method_ref, compiled_method); } - DCHECK(GetCompiledMethod(ref) != nullptr) << PrettyMethod(method_idx, dex_file); + DCHECK(GetCompiledMethod(method_ref) != nullptr) << PrettyMethod(method_idx, dex_file); } + // Done compiling, delete the verified method to reduce native memory usage. + verification_results_->RemoveVerifiedMethod(method_ref); + if (self->IsExceptionPending()) { ScopedObjectAccess soa(self); LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n" @@ -2287,4 +2300,31 @@ bool CompilerDriver::SkipCompilation(const std::string& method_name) { } return !compile; } + +std::string CompilerDriver::GetMemoryUsageString(bool extended) const { + std::ostringstream oss; + const ArenaPool* arena_pool = GetArenaPool(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated()); + oss << " java alloc=" << PrettySize(heap->GetBytesAllocated()); +#ifdef HAVE_MALLOC_H + struct mallinfo info = mallinfo(); + const size_t allocated_space = static_cast(info.uordblks); + const size_t free_space = static_cast(info.fordblks); + oss << " native alloc=" << PrettySize(allocated_space) << " free=" + << PrettySize(free_space); +#endif + if (swap_space_.get() != nullptr) { + oss << " swap=" << PrettySize(swap_space_->GetSize()); + } + if (extended) { + oss << "\nCode dedupe: " << dedupe_code_.DumpStats(); + oss << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats(); + oss << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(); + oss << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats(); + oss << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(); + } + return oss.str(); +} + } // namespace art diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index a74162f0ef5..db07a61085c 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -40,6 +40,7 @@ #include "utils/arena_allocator.h" #include "utils/dedupe_set.h" #include "utils/swap_space.h" +#include "dex/verified_method.h" namespace art { @@ -663,6 +664,9 @@ class CompilerDriver { // Should the compiler run on this method given profile information? bool SkipCompilation(const std::string& method_name); + // Get memory usage during compilation. + std::string GetMemoryUsageString(bool extended) const; + private: // These flags are internal to CompilerDriver for collecting INVOKE resolution statistics. // The only external contract is that unresolved method has flags 0 and resolved non-0. diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h index 84f57991c33..03ae489da11 100644 --- a/compiler/driver/dex_compilation_unit.h +++ b/compiler/driver/dex_compilation_unit.h @@ -102,6 +102,10 @@ class DexCompilationUnit { return verified_method_; } + void ClearVerifiedMethod() { + verified_method_ = nullptr; + } + const std::string& GetSymbol(); private: @@ -117,7 +121,7 @@ class DexCompilationUnit { const uint16_t class_def_idx_; const uint32_t dex_method_idx_; const uint32_t access_flags_; - const VerifiedMethod* const verified_method_; + const VerifiedMethod* verified_method_; std::string symbol_; }; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 91c97011d3c..749aa52dbdd 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -25,10 +25,6 @@ #include #include -#ifndef __APPLE__ -#include // For mallinfo -#endif - #if defined(__linux__) && defined(__arm__) #include #include @@ -279,20 +275,9 @@ class Dex2Oat { } void LogCompletionTime(const CompilerDriver* compiler) { - std::ostringstream mallinfostr; -#ifdef HAVE_MALLOC_H - struct mallinfo info = mallinfo(); - const size_t allocated_space = static_cast(info.uordblks); - const size_t free_space = static_cast(info.fordblks); - mallinfostr << " native alloc=" << PrettySize(allocated_space) << " free=" - << PrettySize(free_space); -#endif - const ArenaPool* arena_pool = compiler->GetArenaPool(); - gc::Heap* heap = Runtime::Current()->GetHeap(); LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_) - << " (threads: " << thread_count_ << ")" - << " arena alloc=" << PrettySize(arena_pool->GetBytesAllocated()) - << " java alloc=" << PrettySize(heap->GetBytesAllocated()) << mallinfostr.str(); + << " (threads: " << thread_count_ << ") " + << compiler->GetMemoryUsageString(kIsDebugBuild || VLOG_IS_ON(compiler)); } @@ -914,7 +899,7 @@ static int dex2oat(int argc, char** argv) { ? Compiler::kPortable : Compiler::kQuick; const char* compiler_filter_string = nullptr; - bool compile_pic = true; + bool compile_pic = false; int huge_method_threshold = CompilerOptions::kDefaultHugeMethodThreshold; int large_method_threshold = CompilerOptions::kDefaultLargeMethodThreshold; int small_method_threshold = CompilerOptions::kDefaultSmallMethodThreshold; From 932bb7cf0977374401b00416f6a8a5c61b385697 Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 1 May 2015 11:08:12 -0500 Subject: [PATCH 074/150] Revert "Revert "Support proxy method in StackVisitor::GetThisObject"" This reverts commit 928bcff0c827cd2349d87dc926c1a5fd3066c643. --- .../quick/quick_trampoline_entrypoints.cc | 23 +++++++++++++++++++ runtime/stack.cc | 9 ++++++++ 2 files changed, 32 insertions(+) diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 428df17e662..5392357a128 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -199,6 +199,22 @@ class QuickArgumentVisitor { #endif public: + // Special handling for proxy methods. Proxy methods are instance methods so the + // 'this' object is the 1st argument. They also have the same frame layout as the + // kRefAndArgs runtime method. Since 'this' is a reference, it is located in the + // 1st GPR. + static mirror::Object* GetProxyThisObject(StackReference* sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(sp->AsMirrorPtr()->IsProxyMethod()); + CHECK_EQ(kQuickCalleeSaveFrame_RefAndArgs_FrameSize, sp->AsMirrorPtr()->GetFrameSizeInBytes()); + CHECK_GT(kNumQuickGprArgs, 0u); + constexpr uint32_t kThisGprIndex = 0u; // 'this' is in the 1st GPR. + size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset + + GprIndexToGprOffset(kThisGprIndex); + uint8_t* this_arg_address = reinterpret_cast(sp) + this_arg_offset; + return reinterpret_cast*>(this_arg_address)->AsMirrorPtr(); + } + static mirror::ArtMethod* GetCallingMethod(StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod()); @@ -410,6 +426,13 @@ class QuickArgumentVisitor { bool is_split_long_or_double_; }; +// Returns the 'this' object of a proxy method. This function is only used by StackVisitor. It +// allows to use the QuickArgumentVisitor constants without moving all the code in its own module. +extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference* sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return QuickArgumentVisitor::GetProxyThisObject(sp); +} + // Visits arguments on the stack placing them into the shadow frame. class BuildQuickShadowFrameVisitor FINAL : public QuickArgumentVisitor { public: diff --git a/runtime/stack.cc b/runtime/stack.cc index 0bd7032fed1..7dd4f081d19 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -112,6 +112,9 @@ uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const { } } +extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference* sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* StackVisitor::GetThisObject() const { mirror::ArtMethod* m = GetMethod(); if (m->IsStatic()) { @@ -124,6 +127,12 @@ mirror::Object* StackVisitor::GetThisObject() const { } else { return cur_shadow_frame_->GetVRegReference(0); } + } else if (m->IsProxyMethod()) { + if (cur_quick_frame_ != nullptr) { + return artQuickGetProxyThisObject(cur_quick_frame_); + } else { + return cur_shadow_frame_->GetVRegReference(0); + } } else { const DexFile::CodeItem* code_item = m->GetCodeItem(); if (code_item == NULL) { From 893a91fe88b01dcef52a279eba81e55f6d07820b Mon Sep 17 00:00:00 2001 From: C3C0 Date: Thu, 14 May 2015 08:41:37 +0200 Subject: [PATCH 075/150] [Xposed] don't get proxy interface method for xposed hooked method --- runtime/mirror/art_method-inl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 29f4e1da704..6ffa2da3e63 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -535,10 +535,10 @@ inline bool ArtMethod::IsProxyMethod() { } inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy() { - if (LIKELY(!IsProxyMethod())) { + mirror::Class* klass = GetDeclaringClass(); + if (LIKELY(!klass->IsProxyClass())) { return this; } - mirror::Class* klass = GetDeclaringClass(); mirror::ArtMethod* interface_method = GetDexCacheResolvedMethods()->Get(GetDexMethodIndex()); DCHECK(interface_method != nullptr); DCHECK_EQ(interface_method, From f74f12dafd2c12917737d514ebb893527d1fd71d Mon Sep 17 00:00:00 2001 From: C3C0 Date: Thu, 14 May 2015 09:49:54 +0200 Subject: [PATCH 076/150] [Xposed] don't do artQuickGetProxyThisObject on xposed hooked method --- runtime/stack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/stack.cc b/runtime/stack.cc index 7dd4f081d19..3b0989e83f6 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -127,7 +127,7 @@ mirror::Object* StackVisitor::GetThisObject() const { } else { return cur_shadow_frame_->GetVRegReference(0); } - } else if (m->IsProxyMethod()) { + } else if (m->IsProxyMethod() && !m->IsXposedHookedMethod()) { if (cur_quick_frame_ != nullptr) { return artQuickGetProxyThisObject(cur_quick_frame_); } else { From 47e066e3e83b2caaaf6babb82a3324c54e7d2e17 Mon Sep 17 00:00:00 2001 From: Matteo Franchin Date: Mon, 27 Oct 2014 13:29:30 +0000 Subject: [PATCH 077/150] AArch64: Addressing Cortex-A53 erratum 835769. Some early revisions of the Cortex-A53 have an erratum (835769) whereby it is possible for a 64-bit multiply-accumulate instruction in AArch64 state to generate an incorrect result. The conditions which a portion of code must satisfy in order for the issue to be observed are somewhat complex, but all cases end with a memory (load, store, or prefetch) instruction followed immediately by the multiply-accumulate operation. This commit makes sure to insert a nop instruction before a 64-bit msub instruction, whenever the latter is preceded by a memory instruction. This behaviour should make it impossible for the Arm64 backend to generate a sequence of instructions which matches the erratum conditions. Conflicts: compiler/dex/compiler_enums.h Change-Id: I115e509fbc7e9e1c3ddbc061d29a32d99f6bddb7 --- compiler/dex/compiler_enums.h | 1 + compiler/dex/quick/arm64/arm64_lir.h | 3 +- compiler/dex/quick/arm64/assemble_arm64.cc | 42 ++++++++++++++++++---- compiler/dex/quick/arm64/int_arm64.cc | 5 ++- dex2oat/dex2oat.cc | 6 ++++ runtime/base/macros.h | 2 +- runtime/instruction_set.h | 9 +++++ 7 files changed, 56 insertions(+), 12 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 62be1f3fc7d..daff79d8388 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -539,6 +539,7 @@ enum FixupKind { kFixupMovImmLST, // kThumb2MovImm16LST. kFixupMovImmHST, // kThumb2MovImm16HST. kFixupAlign4, // Align to 4-byte boundary. + kFixupA53Erratum835769, // Cortex A53 Erratum 835769. }; std::ostream& operator<<(std::ostream& os, const FixupKind& kind); diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h index 83d0590e32a..ff4543de976 100644 --- a/compiler/dex/quick/arm64/arm64_lir.h +++ b/compiler/dex/quick/arm64/arm64_lir.h @@ -320,6 +320,7 @@ enum ArmOpcode { kA64Madd4rrrr, // madd[s0011011000] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. kA64Msub4rrrr, // msub[s0011011000] rm[20-16] [1] ra[14-10] rn[9-5] rd[4-0]. kA64Neg3rro, // neg alias of "sub arg0, rzr, arg1, arg2". + kA64Nop0, // nop alias of "hint #0" [11010101000000110010000000011111]. kA64Orr3Rrl, // orr [s01100100] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0]. kA64Orr4rrro, // orr [s0101010] shift[23-22] [0] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0]. kA64Ret, // ret [11010110010111110000001111000000]. @@ -332,7 +333,7 @@ enum ArmOpcode { kA64Scvtf2fw, // scvtf [000111100s100010000000] rn[9-5] rd[4-0]. kA64Scvtf2fx, // scvtf [100111100s100010000000] rn[9-5] rd[4-0]. kA64Sdiv3rrr, // sdiv[s0011010110] rm[20-16] [000011] rn[9-5] rd[4-0]. - kA64Smaddl4xwwx, // smaddl [10011011001] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. + kA64Smull3xww, // smull [10011011001] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Smulh3xxx, // smulh [10011011010] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Stp4ffXD, // stp [0s10110100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. kA64Stp4rrXD, // stp [s010100100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index 0898f7fa413..39bd858cca4 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "arm64_lir.h" #include "codegen_arm64.h" + +#include "arm64_lir.h" #include "dex/quick/mir_to_lir-inl.h" namespace art { @@ -467,13 +468,17 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, "madd", "!0r, !1r, !3r, !2r", kFixupNone), ENCODING_MAP(WIDE(kA64Msub4rrrr), SF_VARIANTS(0x1b008000), - kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 14, 10, - kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, - "msub", "!0r, !1r, !3r, !2r", kFixupNone), + kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, + kFmtRegR, 14, 10, IS_QUAD_OP | REG_DEF0_USE123 | NEEDS_FIXUP, + "msub", "!0r, !1r, !2r, !3r", kFixupA53Erratum835769), ENCODING_MAP(WIDE(kA64Neg3rro), SF_VARIANTS(0x4b0003e0), kFmtRegR, 4, 0, kFmtRegR, 20, 16, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "neg", "!0r, !1r!2o", kFixupNone), + ENCODING_MAP(kA64Nop0, NO_VARIANTS(0xd503201f), + kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, NO_OPERAND, + "nop", "", kFixupNone), ENCODING_MAP(WIDE(kA64Orr3Rrl), SF_VARIANTS(0x32000000), kFmtRegROrSp, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 22, 10, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, @@ -522,10 +527,10 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "sdiv", "!0r, !1r, !2r", kFixupNone), - ENCODING_MAP(WIDE(kA64Smaddl4xwwx), NO_VARIANTS(0x9b200000), + ENCODING_MAP(kA64Smull3xww, NO_VARIANTS(0x9b207c00), kFmtRegX, 4, 0, kFmtRegW, 9, 5, kFmtRegW, 20, 16, - kFmtRegX, 14, 10, IS_QUAD_OP | REG_DEF0_USE123, - "smaddl", "!0x, !1w, !2w, !3x", kFixupNone), + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "smull", "!0x, !1w, !2w", kFixupNone), ENCODING_MAP(kA64Smulh3xxx, NO_VARIANTS(0x9b407c00), kFmtRegX, 4, 0, kFmtRegX, 9, 5, kFmtRegX, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, @@ -920,6 +925,29 @@ void Arm64Mir2Lir::AssembleLIR() { lir->operands[1] = delta; break; } + case kFixupA53Erratum835769: + // Avoid emitting code that could trigger Cortex A53's erratum 835769. + // This fixup should be carried out for all multiply-accumulate instructions: madd, msub, + // smaddl, smsubl, umaddl and umsubl. + if (cu_->GetInstructionSetFeatures().NeedFix835769()) { + // Check that this is a 64-bit multiply-accumulate. + if (IS_WIDE(lir->opcode)) { + uint64_t prev_insn_flags = EncodingMap[UNWIDE(lir->prev->opcode)].flags; + // Check that the instruction preceding the multiply-accumulate is a load or store. + if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) { + // insert a NOP between the load/store and the multiply-accumulate. + LIR* new_lir = RawLIR(lir->dalvik_offset, kA64Nop0, 0, 0, 0, 0, 0, NULL); + new_lir->offset = lir->offset; + new_lir->flags.fixup = kFixupNone; + new_lir->flags.size = EncodingMap[kA64Nop0].size; + InsertLIRBefore(lir, new_lir); + lir->offset += new_lir->flags.size; + offset_adjustment += new_lir->flags.size; + res = kRetryAll; + } + } + } + break; default: LOG(FATAL) << "Unexpected case: opcode: " << lir->opcode << ", fixup: " << lir->flags.fixup; } diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index 1e97a3246c2..a9b4658edff 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -419,8 +419,7 @@ bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_d rl_src = LoadValue(rl_src, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); RegStorage r_long_mul = AllocTemp(); - NewLIR4(kA64Smaddl4xwwx, As64BitReg(r_long_mul).GetReg(), - r_magic.GetReg(), rl_src.reg.GetReg(), rxzr); + NewLIR3(kA64Smull3xww, As64BitReg(r_long_mul).GetReg(), r_magic.GetReg(), rl_src.reg.GetReg()); switch (pattern) { case Divide3: OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32); @@ -635,7 +634,7 @@ RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegS } OpRegRegReg(kOpDiv, temp, r_src1, r_src2); NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(), - r_src1.GetReg(), r_src2.GetReg()); + r_src2.GetReg(), r_src1.GetReg()); FreeTemp(temp); } return rl_result; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index c41147265f9..2294c93a46b 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -751,6 +751,12 @@ static InstructionSetFeatures ParseFeatureList(std::string str) { } else if (feature == "nolpae") { // Turn off support for Large Physical Address Extension. result.SetHasLpae(false); + } else if (feature == "needfix_835769") { + // need fix CortexA53 errata 835769 + result.SetFix835769(true); + } else if (feature == "noneedfix_835769") { + // no need fix CortexA53 errata 835769 + result.SetFix835769(false); } else { Usage("Unknown instruction set feature: '%s'", feature.c_str()); } diff --git a/runtime/base/macros.h b/runtime/base/macros.h index fae9271d9e4..b1eab92f0c0 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -151,7 +151,7 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #define UNLIKELY(x) __builtin_expect((x), false) // Stringify the argument. -#define QUOTE(x) #x +#define QUOTE(x...) #x #define STRINGIFY(x) QUOTE(x) #ifndef NDEBUG diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h index da7d153c08d..257c3d38e19 100644 --- a/runtime/instruction_set.h +++ b/runtime/instruction_set.h @@ -180,6 +180,7 @@ size_t GetStackOverflowReservedBytes(InstructionSet isa); enum InstructionFeatures { kHwDiv = 0x1, // Supports hardware divide. kHwLpae = 0x2, // Supports Large Physical Address Extension. + kFix835769 = 0x4, // need fix CortexA53 errata 835769 }; // This is a bitmask of supported features per architecture. @@ -206,6 +207,14 @@ class PACKED(4) InstructionSetFeatures { mask_ = (mask_ & ~kHwLpae) | (v ? kHwLpae : 0); } + bool NeedFix835769() const { + return (mask_ & kFix835769) != 0; + } + + void SetFix835769(bool v) { + mask_ = (mask_ & ~kFix835769) | (v ? kFix835769 : 0); + } + std::string GetFeatureString() const; // Other features in here. From 1e1400846c5b90770530669690e85d4e23acde85 Mon Sep 17 00:00:00 2001 From: Ningsheng Jian Date: Wed, 10 Dec 2014 15:13:22 +0800 Subject: [PATCH 078/150] AArch64: Fix incorrect use of preceding LIR. In Cortex-A53 erratum 835769 fixup, we should check the LIR for previous emitted instruction, skipping pseudo LIRs and other non-emitting LIRs. Change-Id: I908f4c638650d7ad9c91112b74095bba2d81612e --- compiler/dex/quick/arm64/assemble_arm64.cc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index 39bd858cca4..72236de08f4 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -833,6 +833,20 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { // are better set directly from the code (they will require no more than 2 instructions). #define ALIGNED_DATA_OFFSET(offset) (((offset) + 0x7) & ~0x7) +/* + * Get the LIR which emits the instruction preceding the given LIR. + * Returns nullptr, if no previous emitting insn found. + */ +static LIR* GetPrevEmittingLIR(LIR* lir) { + DCHECK(lir != nullptr); + LIR* prev_lir = lir->prev; + while ((prev_lir != nullptr) && + (prev_lir->flags.is_nop || Mir2Lir::IsPseudoLirOp(prev_lir->opcode))) { + prev_lir = prev_lir->prev; + } + return prev_lir; +} + // Assemble the LIR into binary instruction format. void Arm64Mir2Lir::AssembleLIR() { LIR* lir; @@ -932,7 +946,11 @@ void Arm64Mir2Lir::AssembleLIR() { if (cu_->GetInstructionSetFeatures().NeedFix835769()) { // Check that this is a 64-bit multiply-accumulate. if (IS_WIDE(lir->opcode)) { - uint64_t prev_insn_flags = EncodingMap[UNWIDE(lir->prev->opcode)].flags; + LIR* prev_insn = GetPrevEmittingLIR(lir); + if (prev_insn == nullptr) { + break; + } + uint64_t prev_insn_flags = EncodingMap[UNWIDE(prev_insn->opcode)].flags; // Check that the instruction preceding the multiply-accumulate is a load or store. if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) { // insert a NOP between the load/store and the multiply-accumulate. From be627376eb8dd5a07414e966cf24ba94030cff96 Mon Sep 17 00:00:00 2001 From: Ricardo Cerqueira Date: Wed, 20 May 2015 01:29:55 +0100 Subject: [PATCH 079/150] Revert "AArch64: Fix incorrect use of preceding LIR." This reverts commit 1e1400846c5b90770530669690e85d4e23acde85. Revert "AArch64: Addressing Cortex-A53 erratum 835769." This reverts commit 47e066e3e83b2caaaf6babb82a3324c54e7d2e17. These are causing msm8916 ART to throw SIGILL. Revisit later Change-Id: I6fea6474c38fbbe2a009716e2e0f370081a2456d --- compiler/dex/compiler_enums.h | 1 - compiler/dex/quick/arm64/arm64_lir.h | 3 +- compiler/dex/quick/arm64/assemble_arm64.cc | 60 +++------------------- compiler/dex/quick/arm64/int_arm64.cc | 5 +- dex2oat/dex2oat.cc | 6 --- runtime/base/macros.h | 2 +- runtime/instruction_set.h | 9 ---- 7 files changed, 12 insertions(+), 74 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index daff79d8388..62be1f3fc7d 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -539,7 +539,6 @@ enum FixupKind { kFixupMovImmLST, // kThumb2MovImm16LST. kFixupMovImmHST, // kThumb2MovImm16HST. kFixupAlign4, // Align to 4-byte boundary. - kFixupA53Erratum835769, // Cortex A53 Erratum 835769. }; std::ostream& operator<<(std::ostream& os, const FixupKind& kind); diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h index ff4543de976..83d0590e32a 100644 --- a/compiler/dex/quick/arm64/arm64_lir.h +++ b/compiler/dex/quick/arm64/arm64_lir.h @@ -320,7 +320,6 @@ enum ArmOpcode { kA64Madd4rrrr, // madd[s0011011000] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. kA64Msub4rrrr, // msub[s0011011000] rm[20-16] [1] ra[14-10] rn[9-5] rd[4-0]. kA64Neg3rro, // neg alias of "sub arg0, rzr, arg1, arg2". - kA64Nop0, // nop alias of "hint #0" [11010101000000110010000000011111]. kA64Orr3Rrl, // orr [s01100100] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0]. kA64Orr4rrro, // orr [s0101010] shift[23-22] [0] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0]. kA64Ret, // ret [11010110010111110000001111000000]. @@ -333,7 +332,7 @@ enum ArmOpcode { kA64Scvtf2fw, // scvtf [000111100s100010000000] rn[9-5] rd[4-0]. kA64Scvtf2fx, // scvtf [100111100s100010000000] rn[9-5] rd[4-0]. kA64Sdiv3rrr, // sdiv[s0011010110] rm[20-16] [000011] rn[9-5] rd[4-0]. - kA64Smull3xww, // smull [10011011001] rm[20-16] [011111] rn[9-5] rd[4-0]. + kA64Smaddl4xwwx, // smaddl [10011011001] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. kA64Smulh3xxx, // smulh [10011011010] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Stp4ffXD, // stp [0s10110100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. kA64Stp4rrXD, // stp [s010100100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index 72236de08f4..0898f7fa413 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -14,9 +14,8 @@ * limitations under the License. */ -#include "codegen_arm64.h" - #include "arm64_lir.h" +#include "codegen_arm64.h" #include "dex/quick/mir_to_lir-inl.h" namespace art { @@ -468,17 +467,13 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, "madd", "!0r, !1r, !3r, !2r", kFixupNone), ENCODING_MAP(WIDE(kA64Msub4rrrr), SF_VARIANTS(0x1b008000), - kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, - kFmtRegR, 14, 10, IS_QUAD_OP | REG_DEF0_USE123 | NEEDS_FIXUP, - "msub", "!0r, !1r, !2r, !3r", kFixupA53Erratum835769), + kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 14, 10, + kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, + "msub", "!0r, !1r, !3r, !2r", kFixupNone), ENCODING_MAP(WIDE(kA64Neg3rro), SF_VARIANTS(0x4b0003e0), kFmtRegR, 4, 0, kFmtRegR, 20, 16, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "neg", "!0r, !1r!2o", kFixupNone), - ENCODING_MAP(kA64Nop0, NO_VARIANTS(0xd503201f), - kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, - kFmtUnused, -1, -1, NO_OPERAND, - "nop", "", kFixupNone), ENCODING_MAP(WIDE(kA64Orr3Rrl), SF_VARIANTS(0x32000000), kFmtRegROrSp, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 22, 10, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, @@ -527,10 +522,10 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "sdiv", "!0r, !1r, !2r", kFixupNone), - ENCODING_MAP(kA64Smull3xww, NO_VARIANTS(0x9b207c00), + ENCODING_MAP(WIDE(kA64Smaddl4xwwx), NO_VARIANTS(0x9b200000), kFmtRegX, 4, 0, kFmtRegW, 9, 5, kFmtRegW, 20, 16, - kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, - "smull", "!0x, !1w, !2w", kFixupNone), + kFmtRegX, 14, 10, IS_QUAD_OP | REG_DEF0_USE123, + "smaddl", "!0x, !1w, !2w, !3x", kFixupNone), ENCODING_MAP(kA64Smulh3xxx, NO_VARIANTS(0x9b407c00), kFmtRegX, 4, 0, kFmtRegX, 9, 5, kFmtRegX, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, @@ -833,20 +828,6 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { // are better set directly from the code (they will require no more than 2 instructions). #define ALIGNED_DATA_OFFSET(offset) (((offset) + 0x7) & ~0x7) -/* - * Get the LIR which emits the instruction preceding the given LIR. - * Returns nullptr, if no previous emitting insn found. - */ -static LIR* GetPrevEmittingLIR(LIR* lir) { - DCHECK(lir != nullptr); - LIR* prev_lir = lir->prev; - while ((prev_lir != nullptr) && - (prev_lir->flags.is_nop || Mir2Lir::IsPseudoLirOp(prev_lir->opcode))) { - prev_lir = prev_lir->prev; - } - return prev_lir; -} - // Assemble the LIR into binary instruction format. void Arm64Mir2Lir::AssembleLIR() { LIR* lir; @@ -939,33 +920,6 @@ void Arm64Mir2Lir::AssembleLIR() { lir->operands[1] = delta; break; } - case kFixupA53Erratum835769: - // Avoid emitting code that could trigger Cortex A53's erratum 835769. - // This fixup should be carried out for all multiply-accumulate instructions: madd, msub, - // smaddl, smsubl, umaddl and umsubl. - if (cu_->GetInstructionSetFeatures().NeedFix835769()) { - // Check that this is a 64-bit multiply-accumulate. - if (IS_WIDE(lir->opcode)) { - LIR* prev_insn = GetPrevEmittingLIR(lir); - if (prev_insn == nullptr) { - break; - } - uint64_t prev_insn_flags = EncodingMap[UNWIDE(prev_insn->opcode)].flags; - // Check that the instruction preceding the multiply-accumulate is a load or store. - if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) { - // insert a NOP between the load/store and the multiply-accumulate. - LIR* new_lir = RawLIR(lir->dalvik_offset, kA64Nop0, 0, 0, 0, 0, 0, NULL); - new_lir->offset = lir->offset; - new_lir->flags.fixup = kFixupNone; - new_lir->flags.size = EncodingMap[kA64Nop0].size; - InsertLIRBefore(lir, new_lir); - lir->offset += new_lir->flags.size; - offset_adjustment += new_lir->flags.size; - res = kRetryAll; - } - } - } - break; default: LOG(FATAL) << "Unexpected case: opcode: " << lir->opcode << ", fixup: " << lir->flags.fixup; } diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index a9b4658edff..1e97a3246c2 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -419,7 +419,8 @@ bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_d rl_src = LoadValue(rl_src, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); RegStorage r_long_mul = AllocTemp(); - NewLIR3(kA64Smull3xww, As64BitReg(r_long_mul).GetReg(), r_magic.GetReg(), rl_src.reg.GetReg()); + NewLIR4(kA64Smaddl4xwwx, As64BitReg(r_long_mul).GetReg(), + r_magic.GetReg(), rl_src.reg.GetReg(), rxzr); switch (pattern) { case Divide3: OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32); @@ -634,7 +635,7 @@ RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegS } OpRegRegReg(kOpDiv, temp, r_src1, r_src2); NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(), - r_src2.GetReg(), r_src1.GetReg()); + r_src1.GetReg(), r_src2.GetReg()); FreeTemp(temp); } return rl_result; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 2294c93a46b..c41147265f9 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -751,12 +751,6 @@ static InstructionSetFeatures ParseFeatureList(std::string str) { } else if (feature == "nolpae") { // Turn off support for Large Physical Address Extension. result.SetHasLpae(false); - } else if (feature == "needfix_835769") { - // need fix CortexA53 errata 835769 - result.SetFix835769(true); - } else if (feature == "noneedfix_835769") { - // no need fix CortexA53 errata 835769 - result.SetFix835769(false); } else { Usage("Unknown instruction set feature: '%s'", feature.c_str()); } diff --git a/runtime/base/macros.h b/runtime/base/macros.h index b1eab92f0c0..fae9271d9e4 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -151,7 +151,7 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #define UNLIKELY(x) __builtin_expect((x), false) // Stringify the argument. -#define QUOTE(x...) #x +#define QUOTE(x) #x #define STRINGIFY(x) QUOTE(x) #ifndef NDEBUG diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h index 257c3d38e19..da7d153c08d 100644 --- a/runtime/instruction_set.h +++ b/runtime/instruction_set.h @@ -180,7 +180,6 @@ size_t GetStackOverflowReservedBytes(InstructionSet isa); enum InstructionFeatures { kHwDiv = 0x1, // Supports hardware divide. kHwLpae = 0x2, // Supports Large Physical Address Extension. - kFix835769 = 0x4, // need fix CortexA53 errata 835769 }; // This is a bitmask of supported features per architecture. @@ -207,14 +206,6 @@ class PACKED(4) InstructionSetFeatures { mask_ = (mask_ & ~kHwLpae) | (v ? kHwLpae : 0); } - bool NeedFix835769() const { - return (mask_ & kFix835769) != 0; - } - - void SetFix835769(bool v) { - mask_ = (mask_ & ~kFix835769) | (v ? kFix835769 : 0); - } - std::string GetFeatureString() const; // Other features in here. From e6f088702d36f4b34a7e36810aafc4dc091779d5 Mon Sep 17 00:00:00 2001 From: Yevgeny Rouban Date: Thu, 19 Mar 2015 19:01:11 +0600 Subject: [PATCH 080/150] ART: patchoat gets gzip support for compressed odex files The patchoat is capable to inflate a compressed odex file into a temporary file. Then the temporary file is patched as if it is specified with --input-oat-fd or --input-oat-file. The new options are the following: --input-oat-gz-fd --input-oat-gz-file --swap-fd --swap-file Could not make in-memory decompression and patching because the ElfFile class maps Elf file using its file descriptor. So a temporary file is used as it is done by dex2oat. Both gzip and zip are supported by zlib library and they have the same compression algorithm. Gzip is chosen because zip needs a zip entry name, which is unnecessary for single-file compression. Change-Id: I54dbfcdc58bc0f57f7eccc3929c09295c597794b Signed-off-by: Yevgeny Rouban Conflicts: patchoat/Android.mk --- build/Android.executable.mk | 2 + patchoat/Android.mk | 4 +- patchoat/patchoat.cc | 148 +++++++++++++++++++++++++++++++++++- 3 files changed, 149 insertions(+), 5 deletions(-) diff --git a/build/Android.executable.mk b/build/Android.executable.mk index 412f2ddfee2..6fd6b2bfff7 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -50,6 +50,7 @@ define build-art-executable art_target_or_host := $(5) art_ndebug_or_debug := $(6) art_multilib := $(7) + art_static_libraries := $(8) include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) @@ -57,6 +58,7 @@ define build-art-executable LOCAL_SRC_FILES := $$(art_source) LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes) LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries) + LOCAL_STATIC_LIBRARIES += $$(art_static_libraries) LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain ifeq ($$(art_ndebug_or_debug),ndebug) diff --git a/patchoat/Android.mk b/patchoat/Android.mk index 1eaf7815088..1f59bbdc6ed 100644 --- a/patchoat/Android.mk +++ b/patchoat/Android.mk @@ -30,8 +30,8 @@ else endif ifeq ($(ART_BUILD_TARGET_NDEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,ndebug,$(patchoat_arch))) + $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,ndebug,$(patchoat_arch),libz)) endif ifeq ($(ART_BUILD_TARGET_DEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,debug,$(patchoat_arch))) + $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,debug,$(patchoat_arch),libz)) endif diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index b046ea1ef43..0b356975050 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -45,6 +45,7 @@ #include "runtime.h" #include "scoped_thread_state_change.h" #include "thread.h" +#include "zlib.h" #include "utils.h" namespace art { @@ -800,6 +801,16 @@ static void Usage(const char *fmt, ...) { UsageError(" --input-oat-fd=: Specifies the file-descriptor of the oat file"); UsageError(" to be patched."); UsageError(""); + UsageError(" --input-oat-gz-file=: Specifies the exact filename of"); + UsageError(" the gzip-compressed oat file to be patched."); + UsageError(""); + UsageError(" --input-oat-gz-fd=: Specifies the file-descriptor of"); + UsageError(" the gzip-compressed oat file to be patched."); + UsageError(""); + UsageError(" --swap-file=: Specifies a temporary gzip file."); + UsageError(""); + UsageError(" --swap-fd=: Specifies a temporary gzip file descriptor."); + UsageError(""); UsageError(" --input-oat-location=: Specifies the 'location' to read the patched"); UsageError(" oat file from. If used one must also supply the --instruction-set"); UsageError(""); @@ -908,6 +919,67 @@ static bool FinishFile(File* file, bool close) { } } +static int Inflate(int input_oat_gz_fd, const std::string& input_oat_gz_filename, int tmp_fd, std::string* err) { + gzFile in_gzfile = input_oat_gz_fd != -1 ? gzdopen(input_oat_gz_fd, "rb") : + gzopen(input_oat_gz_filename.c_str(), "rb"); + if (in_gzfile == nullptr) { + *err = input_oat_gz_fd != -1 ? + StringPrintf("Could not open gzip file fd=%d: %s", + input_oat_gz_fd, strerror(errno)) : + StringPrintf("Could not open gzip file %s: %s", + input_oat_gz_filename.c_str(), strerror(errno)); + return -1; + } + + int out_fd = dup(tmp_fd); + if (out_fd == -1) { + *err = strerror(errno); + return -1; + } + + constexpr size_t INFLATE_BUFLEN = 16384; + std::unique_ptr out_file(new File(out_fd, false)); + std::unique_ptr buf(new Byte[INFLATE_BUFLEN]); + int len; + + while (0 < (len = gzread(in_gzfile, buf.get(), INFLATE_BUFLEN))) { + if (!out_file->WriteFully(buf.get(), len)) { + *err = StringPrintf("Could not write to fd=%d: %s", tmp_fd, out_file->GetPath().c_str()); + gzclose(in_gzfile); + return -1; + } + } + + int errnum; + const char* gzerrstr = gzerror(in_gzfile, &errnum); + + if (len < 0 || errnum != Z_OK) { + *err = input_oat_gz_fd != -1 ? + StringPrintf("Could not inflate gzip file fd=%d: %s", + input_oat_gz_fd, gzerrstr) : + StringPrintf("Could not inflate gzip file %s: %s", + input_oat_gz_filename.c_str(), gzerrstr); + gzclose(in_gzfile); + return -1; + } + + if ((errnum = gzclose(in_gzfile)) != Z_OK) { + *err = input_oat_gz_fd != -1 ? + StringPrintf("Could not close gzip file fd=%d: gzclose() returned %d", + input_oat_gz_fd, errnum) : + StringPrintf("Could not close gzip file %s: gzclose() returned %d", + input_oat_gz_filename.c_str(), errnum); + } + + if (out_file->Flush() != 0) { + *err = StringPrintf("Could not flush tmp file fd=%d", tmp_fd); + return -1; + } + + out_file->DisableAutoClose(); + return out_fd; +} + static int patchoat(int argc, char **argv) { InitLogging(argv); MemMap::Init(); @@ -932,8 +1004,12 @@ static int patchoat(int argc, char **argv) { bool isa_set = false; InstructionSet isa = kNone; std::string input_oat_filename; + std::string input_oat_gz_filename; std::string input_oat_location; int input_oat_fd = -1; + int input_oat_gz_fd = -1; + std::string swap_file_name; + int swap_fd = -1; bool have_input_oat = false; std::string input_image_location; std::string output_oat_filename; @@ -968,19 +1044,29 @@ static int patchoat(int argc, char **argv) { } } else if (option.starts_with("--input-oat-location=")) { if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); } have_input_oat = true; input_oat_location = option.substr(strlen("--input-oat-location=")).data(); } else if (option.starts_with("--input-oat-file=")) { if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); } have_input_oat = true; input_oat_filename = option.substr(strlen("--input-oat-file=")).data(); + } else if (option.starts_with("--input-oat-gz-file=")) { + if (have_input_oat) { + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); + } + have_input_oat = true; + input_oat_gz_filename = option.substr(strlen("--input-oat-gz-file=")).data(); } else if (option.starts_with("--input-oat-fd=")) { if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); } have_input_oat = true; const char* oat_fd_str = option.substr(strlen("--input-oat-fd=")).data(); @@ -990,6 +1076,29 @@ static int patchoat(int argc, char **argv) { if (input_oat_fd < 0) { Usage("--input-oat-fd pass a negative value %d", input_oat_fd); } + } else if (option.starts_with("--input-oat-gz-fd=")) { + if (have_input_oat) { + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); + } + have_input_oat = true; + const char* oat_gz_fd_str = option.substr(strlen("--input-oat-gz-fd=")).data(); + if (!ParseInt(oat_gz_fd_str, &input_oat_gz_fd)) { + Usage("Failed to parse --input-oat-fd argument '%s' as an integer", oat_gz_fd_str); + } + if (input_oat_gz_fd < 0) { + Usage("--input-oat-gz-fd pass a negative value %d", input_oat_gz_fd); + } + } else if (option.starts_with("--swap-file=")) { + swap_file_name = option.substr(strlen("--swap-file=")).data(); + } else if (option.starts_with("--swap-fd=")) { + const char* swap_fd_str = option.substr(strlen("--swap-fd=")).data(); + if (!ParseInt(swap_fd_str, &swap_fd)) { + Usage("Failed to parse --swap-fd argument '%s' as an integer", swap_fd_str); + } + if (swap_fd < 0) { + Usage("--swap-fd passed a negative value %d", swap_fd); + } } else if (option.starts_with("--input-image-location=")) { input_image_location = option.substr(strlen("--input-image-location=")).data(); } else if (option.starts_with("--output-oat-file=")) { @@ -1088,6 +1197,11 @@ static int patchoat(int argc, char **argv) { Usage("Either both input and output image must be supplied or niether must be."); } + if ((input_oat_gz_fd != -1 || !input_oat_gz_filename.empty()) != + (swap_fd != -1 || !swap_file_name.empty())) { + Usage("Either both input gzip and swap must be supplied or niether must be."); + } + // We know we have both the input and output so rename for clarity. bool have_image_files = have_output_image; bool have_oat_files = have_output_oat; @@ -1111,6 +1225,26 @@ static int patchoat(int argc, char **argv) { LOG(INFO) << "Using input-oat-file " << input_oat_filename; } } + + // Swap file handling. + // + // If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file + // that we can use for swap. + // + // If the swap fd is -1 and we have a swap-file string, open the given file as a swap file. We + // will immediately unlink to satisfy the swap fd assumption. + std::unique_ptr swap_file; + if (swap_fd == -1 && !swap_file_name.empty()) { + swap_file.reset(OS::CreateEmptyFile(swap_file_name.c_str())); + if (swap_file.get() == nullptr) { + PLOG(ERROR) << "Failed to create swap file: " << swap_file_name; + return EXIT_FAILURE; + } + swap_fd = swap_file->Fd(); + swap_file->MarkUnchecked(); // We don't want to track this, it will be unlinked immediately. + unlink(swap_file_name.c_str()); + } + if (!patched_image_location.empty()) { if (!isa_set) { Usage("specifying a location requires specifying an instruction set"); @@ -1189,8 +1323,16 @@ static int patchoat(int argc, char **argv) { } if (have_oat_files) { + if (input_oat_gz_fd != -1 || !input_oat_gz_filename.empty()) { + std::string err; + input_oat_fd = Inflate(input_oat_gz_fd, input_oat_gz_filename, swap_fd, &err); + if (input_oat_fd == -1) { + LOG(ERROR) << "Failed to inflate input file: " << err; + } + } if (input_oat_fd != -1) { if (input_oat_filename.empty()) { + // TODO: make sure this will not be used to create symlink if input_oat_fd is PIC input_oat_filename = "input-oat-file"; } input_oat.reset(new File(input_oat_fd, input_oat_filename, false)); From 597b0b8d84aa73dff9fa93bf64b61aa5da83fc76 Mon Sep 17 00:00:00 2001 From: Yevgeny Rouban Date: Fri, 20 Mar 2015 15:42:24 +0600 Subject: [PATCH 081/150] ART: prevent patchoat from symlinking with dummy files If OAT file is position independent (PIC, dex2oat's option --compile-pic) then the patchoat just creates a symlink with the input oat file. But if the input oat file is set by its file descriptor (option --input-oat-fd) or just has been inflated to a temporary file (options --input-oat-gz-file or --input-oat-gz-fd) then its file name is set to a dummy 'input-oat-file' and making a symlink with this dummy path makes no sense. This patch fixes the patchoat to make a copy of the input file in such cases. Change-Id: Ib63b0c80b1da06b60cbb754206c510a7396624a9 Signed-off-by: Yevgeny Rouban --- patchoat/patchoat.cc | 114 +++++++++++++++++++++++++++---------------- patchoat/patchoat.h | 15 +++--- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 0b356975050..73c31ebf24b 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -196,7 +196,7 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d File* output_oat, File* output_image, InstructionSet isa, TimingLogger* timings, bool output_oat_opened_from_fd, - bool new_oat_out) { + bool new_oat_out, bool input_oat_filename_dummy) { CHECK(Runtime::Current() == nullptr); CHECK(output_image != nullptr); CHECK_GE(output_image->Fd(), 0); @@ -285,11 +285,9 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d // Error logged by IsOatPic return false; } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching. - if (!ReplaceOatFileWithSymlink(input_oat->GetPath(), - output_oat->GetPath(), - output_oat_opened_from_fd, - new_oat_out)) { + // Do not need to do ELF-file patching. Create a symlink or make a copy. + if (!SymlinkOrCopy(input_oat, output_oat, output_oat_opened_from_fd, + new_oat_out, input_oat_filename_dummy)) { // Errors already logged by above call. return false; } @@ -399,36 +397,70 @@ PatchOat::MaybePic PatchOat::IsOatPic(const ElfFile* oat_in) { return is_pic ? PIC : NOT_PIC; } -bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out) { - // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD. - if (output_oat_opened_from_fd) { - // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC? - LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC"; - return false; - } +const size_t COPY_BUFLEN = 16384; + +bool PatchOat::SymlinkOrCopy(File* input_oat, + File* output_oat, + bool output_oat_opened_from_fd, + bool new_oat_out, bool make_copy) { + std::string output_oat_filename = output_oat->GetPath(); + std::string input_oat_filename = input_oat->GetPath(); + + if (make_copy) { + // Make a copy of the PIC oat file. + std::unique_ptr buf(new char[COPY_BUFLEN]); + int64_t len; + int64_t read_size = 0; + while (true) { + len = input_oat->Read(buf.get(), COPY_BUFLEN, read_size); + if (len <= 0) { + break; + } + if (!output_oat->WriteFully(buf.get(), len)) { + len = -1; + break; + } + read_size += len; + } - // Image was PIC. Create symlink where the oat is supposed to go. - if (!new_oat_out) { - LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite"; - return false; - } + if (len < 0) { + int err = errno; + LOG(ERROR) << "Failed to copy " << input_oat_filename << " to " << output_oat_filename + << ": error(" << err << "): " << strerror(err); + return false; + } + + if (kIsDebugBuild) { + LOG(INFO) << "Copied " << input_oat_filename << " -> " << output_oat_filename; + } + } else { + // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD. + if (output_oat_opened_from_fd) { + // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC? + LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC"; + return false; + } - // Delete the original file, since we won't need it. - TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str())); + // Image was PIC. Create symlink where the oat is supposed to go. + if (!new_oat_out) { + LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite"; + return false; + } - // Create a symlink from the old oat to the new oat - if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) < 0) { - int err = errno; - LOG(ERROR) << "Failed to create symlink at " << output_oat_filename - << " error(" << err << "): " << strerror(err); - return false; - } + // Delete the original file, since we won't need it. + TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str())); - if (kIsDebugBuild) { - LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename; + // Create a symlink from the old oat to the new oat. + if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) == 0) { + if (kIsDebugBuild) { + LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename; + } + } else { + int err = errno; + LOG(ERROR) << "Failed to create symlink at " << output_oat_filename + << " error(" << err << "): " << strerror(err); + return false; + } } return true; @@ -562,7 +594,8 @@ void PatchOat::FixupMethod(mirror::ArtMethod* object, mirror::ArtMethod* copy) { } bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings, - bool output_oat_opened_from_fd, bool new_oat_out) { + bool output_oat_opened_from_fd, bool new_oat_out, + bool input_oat_filename_dummy) { CHECK(input_oat != nullptr); CHECK(output_oat != nullptr); CHECK_GE(input_oat->Fd(), 0); @@ -582,12 +615,10 @@ bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogge // Error logged by IsOatPic return false; } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the rest. + // Do not need to do ELF-file patching. Create a symlink or make a copy. // Any errors will be logged by the function call. - return ReplaceOatFileWithSymlink(input_oat->GetPath(), - output_oat->GetPath(), - output_oat_opened_from_fd, - new_oat_out); + return SymlinkOrCopy(input_oat, output_oat, output_oat_opened_from_fd, + new_oat_out, input_oat_filename_dummy); } else { CHECK(is_oat_pic == NOT_PIC); } @@ -1004,6 +1035,7 @@ static int patchoat(int argc, char **argv) { bool isa_set = false; InstructionSet isa = kNone; std::string input_oat_filename; + bool input_oat_filename_dummy = false; std::string input_oat_gz_filename; std::string input_oat_location; int input_oat_fd = -1; @@ -1332,8 +1364,8 @@ static int patchoat(int argc, char **argv) { } if (input_oat_fd != -1) { if (input_oat_filename.empty()) { - // TODO: make sure this will not be used to create symlink if input_oat_fd is PIC input_oat_filename = "input-oat-file"; + input_oat_filename_dummy = true; } input_oat.reset(new File(input_oat_fd, input_oat_filename, false)); if (input_oat == nullptr) { @@ -1426,7 +1458,7 @@ static int patchoat(int argc, char **argv) { ret = PatchOat::Patch(input_oat.get(), input_image_location, base_delta, output_oat.get(), output_image.get(), isa, &timings, output_oat_fd >= 0, // was it opened from FD? - new_oat_out); + new_oat_out, input_oat_filename_dummy); // The order here doesn't matter. If the first one is successfully saved and the second one // erased, ImageSpace will still detect a problem and not use the files. ret = ret && FinishFile(output_image.get(), ret); @@ -1435,7 +1467,7 @@ static int patchoat(int argc, char **argv) { TimingLogger::ScopedTiming pt("patch oat", &timings); ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings, output_oat_fd >= 0, // was it opened from FD? - new_oat_out); + new_oat_out, input_oat_filename_dummy); ret = ret && FinishFile(output_oat.get(), ret); } else if (have_image_files) { TimingLogger::ScopedTiming pt("patch image", &timings); diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index 03d915abddb..3a21d8514ff 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -44,7 +44,8 @@ class PatchOat { // Patch only the oat file static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings, bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? - bool new_oat_out); // Output oat was a new file created by us? + bool new_oat_out, // Output oat was a new file created by us? + bool input_oat_filename_dummy); // Input cannot be symlinked // Patch only the image (art file) static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa, @@ -55,7 +56,8 @@ class PatchOat { off_t delta, File* oat_out, File* art_out, InstructionSet isa, TimingLogger* timings, bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? - bool new_oat_out); // Output oat was a new file created by us? + bool new_oat_out, // Output oat was a new file created by us? + bool input_oat_filename_dummy); // Input cannot be symlinked private: // Takes ownership only of the ElfFile. All other pointers are only borrowed. @@ -87,10 +89,11 @@ class PatchOat { // Attempt to replace the file with a symlink // Returns false if it fails - static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out); // Output oat was newly created? + static bool SymlinkOrCopy(File* input_oat, + File* output_oat, + bool output_oat_opened_from_fd, + bool new_oat_out, // Output oat was newly created? + bool make_copy); static void BitmapCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { From 9514788ea796de82c2e4871b4d716ec2c3995fac Mon Sep 17 00:00:00 2001 From: Chirayu Desai Date: Wed, 3 Jun 2015 23:21:33 +0530 Subject: [PATCH 082/150] Allow skipping of dalvik cache pruning if boot marker is in place. c38276177 "Prune image cache if the boot marker is still in place." wipes the entire dalvik cache if interruped. Allow overriding that behaviour with a property "persist.art.pruneimagecache", set it to false / 0 to skip the deletion. Change-Id: I2acb0c6011e2e6cb8e123a4cdbd090ae1d838774 --- runtime/gc/space/image_space.cc | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index f765f0e168e..d42eeb8fa49 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -34,6 +34,10 @@ #include "space-inl.h" #include "utils.h" +#ifdef HAVE_ANDROID_OS +#include +#endif + namespace art { namespace gc { namespace space { @@ -124,17 +128,32 @@ static void RealPruneDalvikCache(const std::string& cache_dir_path) { static void MarkZygoteStart(const InstructionSet isa) { const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false); const std::string boot_marker = isa_subdir + "/.booting"; + bool pruned_image_cache = true; + +#ifdef HAVE_ANDROID_OS + int8_t prune_image_cache = property_get_bool("persist.art.pruneimagecache", 1); +#else + int8_t prune_image_cache = 1; +#endif if (OS::FileExists(boot_marker.c_str())) { - LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache"; - RealPruneDalvikCache(isa_subdir); + if (!prune_image_cache) { + LOG(WARNING) << "Incomplete boot detected. Skipped prunning of dalvik cache due to property"; + pruned_image_cache = false; + } else { + LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache"; + RealPruneDalvikCache(isa_subdir); + pruned_image_cache = true; + } } - VLOG(startup) << "Creating boot start marker: " << boot_marker; - std::unique_ptr f(OS::CreateEmptyFile(boot_marker.c_str())); - if (f.get() != nullptr) { - if (f->FlushCloseOrErase() != 0) { - PLOG(WARNING) << "Failed to write boot marker."; + if (pruned_image_cache || !OS::FileExists(boot_marker.c_str())) { + VLOG(startup) << "Creating boot start marker: " << boot_marker; + std::unique_ptr f(OS::CreateEmptyFile(boot_marker.c_str())); + if (f.get() != nullptr) { + if (f->FlushCloseOrErase() != 0) { + PLOG(WARNING) << "Failed to write boot marker."; + } } } } From 5184a0542b81c297afa97d4540e4751db5fc9876 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Thu, 4 Jun 2015 23:40:37 +0200 Subject: [PATCH 083/150] [Xposed] Don't reuse open oat files from Zygote Xposed modules are loaded in the Zygote process. ART compiles the .dex file and opens the resulting .oat file in the Dalvik cache for that. However, the same happens again when the user installs a new version of the module, overwriting the file in the Dalvik cache. When opening the new .oat file though, ART detects that the file is already opened and reuses this instance. Later, it detects that the checksum doesn't match, which causes a crash. This will only occur on every second update (without reboot) as Android uses alternating suffixes for the .apk files (-1.apk / -2.apk). Let's work around it by ignoring already opened .oat files if they have been opened in Zygote. The behavior for files in /system (boot image and framework) is unchanged. Fixes rovo89/Xposed#22. --- runtime/class_linker.cc | 4 ++++ runtime/oat_file.cc | 2 +- runtime/oat_file.h | 7 +++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 87049c80aca..c1c3b93e89b 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1229,6 +1229,10 @@ const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& const OatFile* oat_file = oat_files_[i]; DCHECK(oat_file != nullptr); if (oat_file->GetLocation() == oat_location) { + if (oat_file->IsCreatedInZygote() && !Runtime::Current()->IsZygote() && oat_location.find("/system@") == std::string::npos) { + LOG(INFO) << "Ignoring oat file opened by Zygote: " << oat_location; + continue; + } return oat_file; } } diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 025f87d832d..19b232400ae 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -139,7 +139,7 @@ OatFile* OatFile::OpenElfFile(File* file, OatFile::OatFile(const std::string& location, bool is_executable) : location_(location), begin_(NULL), end_(NULL), is_executable_(is_executable), - dlopen_handle_(NULL), + is_created_in_zygote_(Runtime::Current()->IsZygote()), dlopen_handle_(NULL), secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) { CHECK(!location_.empty()); } diff --git a/runtime/oat_file.h b/runtime/oat_file.h index e5cd6ec5381..37dd44c9171 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -72,6 +72,10 @@ class OatFile { return is_executable_; } + bool IsCreatedInZygote() const { + return is_created_in_zygote_; + } + ElfFile* GetElfFile() const { CHECK_NE(reinterpret_cast(elf_file_.get()), reinterpret_cast(nullptr)) << "Cannot get an elf file from " << GetLocation(); @@ -327,6 +331,9 @@ class OatFile { // Was this oat_file loaded executable? const bool is_executable_; + // Was this file loaded in Zygote? + const bool is_created_in_zygote_; + // Backing memory map for oat file during when opened by ElfWriter during initial compilation. std::unique_ptr mem_map_; From b86014f9b9d487d5feb2848caaf2e5e6cb02da2c Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Tue, 7 Oct 2014 12:51:26 +0100 Subject: [PATCH 084/150] Fix thread priorities for unstarted threads. Calls to Thread.setPriority for unstarted threads now behave similar to dalvik. Note that there's still some inconsistent behaviour carried over from dalvik. - high priority threads from bg_non_interactive processes are not always moved to the SP_FOREGROUND cgroup. - we do not attempt to adjust the cgroup of a native thread that's attaching. Note that on android, the system_server will change the cgroups for all running threads in a process when it moves into the foreground and background. It's by design that threads in a background process can request to be moved to the foreground by setting a higher priority. bug: 17893086 Change-Id: I1662982b1c7b3ac509698e2e12c9768d082c8053 --- runtime/thread.cc | 3 ++ runtime/thread_android.cc | 7 +++++ test/051-thread/expected.txt | 2 ++ test/051-thread/src/Main.java | 54 ++++++++++++++++++++++++++++++++++ test/051-thread/thread_test.cc | 34 +++++++++++++++++++++ test/Android.libarttest.mk | 1 + 6 files changed, 101 insertions(+) create mode 100644 test/051-thread/thread_test.cc diff --git a/runtime/thread.cc b/runtime/thread.cc index 6b65f12fb59..7fb631c0733 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -168,6 +168,9 @@ void* Thread::CreateCallback(void* arg) { self->GetJniEnv()->DeleteGlobalRef(self->tlsPtr_.jpeer); self->tlsPtr_.jpeer = nullptr; self->SetThreadName(self->GetThreadName(soa)->ToModifiedUtf8().c_str()); + + mirror::ArtField* priorityField = soa.DecodeField(WellKnownClasses::java_lang_Thread_priority); + self->SetNativePriority(priorityField->GetInt(self->tlsPtr_.opeer)); Dbg::PostThreadStart(self); // Invoke the 'run' method of our java.lang.Thread. diff --git a/runtime/thread_android.cc b/runtime/thread_android.cc index 73a9e54ea56..d5db9838abc 100644 --- a/runtime/thread_android.cc +++ b/runtime/thread_android.cc @@ -55,6 +55,13 @@ void Thread::SetNativePriority(int newPriority) { int newNice = kNiceValues[newPriority-1]; pid_t tid = GetTid(); + // TODO: b/18249098 The code below is broken. It uses getpriority() as a proxy for whether a + // thread is already in the SP_FOREGROUND cgroup. This is not necessarily true for background + // processes, where all threads are in the SP_BACKGROUND cgroup. This means that callers will + // have to call setPriority twice to do what they want : + // + // Thread.setPriority(Thread.MIN_PRIORITY); // no-op wrt to cgroups + // Thread.setPriority(Thread.MAX_PRIORITY); // will actually change cgroups. if (newNice >= ANDROID_PRIORITY_BACKGROUND) { set_sched_policy(tid, SP_BACKGROUND); } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { diff --git a/test/051-thread/expected.txt b/test/051-thread/expected.txt index 943d1dfac27..54e34af3aa1 100644 --- a/test/051-thread/expected.txt +++ b/test/051-thread/expected.txt @@ -9,4 +9,6 @@ testSleepZero finished testSetName starting testSetName running testSetName finished +testThreadPriorities starting +testThreadPriorities finished thread test done diff --git a/test/051-thread/src/Main.java b/test/051-thread/src/Main.java index 390685d0492..b81273ea4eb 100644 --- a/test/051-thread/src/Main.java +++ b/test/051-thread/src/Main.java @@ -20,12 +20,17 @@ * Test some basic thread stuff. */ public class Main { + static { + System.loadLibrary("arttest"); + } + public static void main(String[] args) throws Exception { System.out.println("thread test starting"); testThreadCapacity(); testThreadDaemons(); testSleepZero(); testSetName(); + testThreadPriorities(); System.out.println("thread test done"); } @@ -133,4 +138,53 @@ public void run() { } System.out.print("testSetName finished\n"); } + + private static void testThreadPriorities() throws Exception { + System.out.print("testThreadPriorities starting\n"); + + PriorityStoringThread t1 = new PriorityStoringThread(false); + t1.setPriority(Thread.MAX_PRIORITY); + t1.start(); + t1.join(); + if (supportsThreadPriorities() && (t1.getNativePriority() != Thread.MAX_PRIORITY)) { + System.out.print("thread priority for t1 was " + t1.getNativePriority() + + " [expected Thread.MAX_PRIORITY]\n"); + } + + PriorityStoringThread t2 = new PriorityStoringThread(true); + t2.start(); + t2.join(); + if (supportsThreadPriorities() && (t2.getNativePriority() != Thread.MAX_PRIORITY)) { + System.out.print("thread priority for t2 was " + t2.getNativePriority() + + " [expected Thread.MAX_PRIORITY]\n"); + } + + System.out.print("testThreadPriorities finished\n"); + } + + private static native int getNativePriority(); + private static native boolean supportsThreadPriorities(); + + static class PriorityStoringThread extends Thread { + private final boolean setPriority; + private volatile int nativePriority; + + public PriorityStoringThread(boolean setPriority) { + this.setPriority = setPriority; + this.nativePriority = -1; + } + + @Override + public void run() { + if (setPriority) { + setPriority(Thread.MAX_PRIORITY); + } + + nativePriority = Main.getNativePriority(); + } + + public int getNativePriority() { + return nativePriority; + } + } } diff --git a/test/051-thread/thread_test.cc b/test/051-thread/thread_test.cc new file mode 100644 index 00000000000..2f5fffc4002 --- /dev/null +++ b/test/051-thread/thread_test.cc @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2014 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 "jni.h" +#include "thread-inl.h" + +namespace art { + +extern "C" JNIEXPORT jint JNICALL Java_Main_getNativePriority(JNIEnv* env, jclass) { + return ThreadForEnv(env)->GetNativePriority(); +} + +extern "C" JNIEXPORT jboolean JNICALL Java_Main_supportsThreadPriorities(JNIEnv* env, jclass) { +#if defined(HAVE_ANDROID_OS) + return JNI_TRUE; +#else + return JNI_FALSE; +#endif +} + +} // namespace art diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk index 2d139a64b93..2e12335d3d9 100644 --- a/test/Android.libarttest.mk +++ b/test/Android.libarttest.mk @@ -24,6 +24,7 @@ LIBARTTEST_COMMON_SRC_FILES := \ 004-ReferenceMap/stack_walk_refmap_jni.cc \ 004-StackWalk/stack_walk_jni.cc \ 004-UnsafeTest/unsafe_test.cc \ + 051-thread/thread_test.cc \ 116-nodex2oat/nodex2oat.cc \ 117-nopatchoat/nopatchoat.cc \ 118-noimage-dex2oat/noimage-dex2oat.cc From 345f1a71c590517d9fd2459fd661e714a969e007 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 6 Jan 2015 12:05:34 -0800 Subject: [PATCH 085/150] Mute common case DexFile.isDexOptNeeded log spam Bug: 18914409 Change-Id: I885f1aa87c48ff25353d44b720c7cff24ed01e45 --- runtime/native/dalvik_system_DexFile.cc | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc index 9205d676847..dc0c3e092e7 100644 --- a/runtime/native/dalvik_system_DexFile.cc +++ b/runtime/native/dalvik_system_DexFile.cc @@ -292,7 +292,10 @@ static jbyte IsDexOptNeededForFile(const std::string& oat_filename, const char* std::unique_ptr oat_file(OatFile::Open(oat_filename, oat_filename, nullptr, false, &error_msg)); if (oat_file.get() == nullptr) { - if (kReasonLogging) { + // Note that even though this is kDexoptNeeded, we use + // kVerboseLogging instead of the usual kReasonLogging since it is + // the common case on first boot and very spammy. + if (kVerboseLogging) { LOG(INFO) << "DexFile_isDexOptNeeded failed to open oat file '" << oat_filename << "' for file location '" << filename << "': " << error_msg; } From afd9bd1d9423c5f1d5ffe5cbf1c4fc4019403155 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 5 Jun 2015 20:23:39 +0200 Subject: [PATCH 086/150] [Xposed] Ignore unknown arguments and option values Some ROMs are configured to make use of options that are only available in these ROMs, but not in AOSP (and therefore, not in Xposed). There's little chance that these options can be implemented in Xposed's version of ART as well. However, these options aren't necessarily required, they might just be minor tweaks. So we just log them, but don't prevent the device from booting (unless it happened to be a strictly required option, but then it wouldn't boot either way). Fixes #7. --- dex2oat/dex2oat.cc | 6 +++--- oatdump/oatdump.cc | 1 - patchoat/patchoat.cc | 2 +- runtime/parsed_options.cc | 16 ++++++---------- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 14d454b692d..686872c1b66 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -769,7 +769,7 @@ static InstructionSetFeatures ParseFeatureList(std::string str) { // Turn off support for Large Physical Address Extension. result.SetHasLpae(false); } else { - Usage("Unknown instruction set feature: '%s'", feature.c_str()); + LOG(WARNING) << StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); } } // others... @@ -1077,7 +1077,7 @@ static int dex2oat(int argc, char** argv) { } else if (option == "--no-include-patch-information") { include_patch_information = false; } else { - Usage("Unknown argument %s", option.data()); + LOG(WARNING) << StringPrintf("Unknown argument %s", option.data()); } } @@ -1199,7 +1199,7 @@ static int dex2oat(int argc, char** argv) { } else if (strcmp(compiler_filter_string, "everything") == 0) { compiler_filter = CompilerOptions::kEverything; } else { - Usage("Unknown --compiler-filter value %s", compiler_filter_string); + LOG(WARNING) << StringPrintf("Unknown --compiler-filter value %s", compiler_filter_string); } // Set the compilation target's implicit checks options. diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 9203d3d1116..4343f74c7c2 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1750,7 +1750,6 @@ static int oatdump(int argc, char** argv) { method_filter_ = option.substr(strlen("--method-filter=")).data(); } else { fprintf(stderr, "Unknown argument %s\n", option.data()); - usage(); } } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 74f6779b0f0..da509876d8b 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -911,7 +911,7 @@ static int patchoat(int argc, char **argv) { } else if (option == "--no-dump-timings") { dump_timings = false; } else { - Usage("Unknown argument %s", option.data()); + LOG(WARNING) << StringPrintf("Unknown argument %s", option.data()); } } diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index bb2ad44a4f5..10fa7a3c95f 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -21,6 +21,7 @@ #endif #include "base/stringpiece.h" +#include "base/stringprintf.h" #include "debugger.h" #include "gc/heap.h" #include "monitor.h" @@ -159,8 +160,7 @@ bool ParsedOptions::ParseXGcOption(const std::string& option) { (gc_option == "noverifycardtable")) { // Ignored for backwards compatibility. } else { - Usage("Unknown -Xgc option %s\n", gc_option.c_str()); - return false; + LOG(WARNING) << StringPrintf("Unknown -Xgc option %s\n", gc_option.c_str()); } } return true; @@ -466,8 +466,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize if (collector_type != gc::kCollectorTypeNone) { background_collector_type_ = collector_type; } else { - Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str()); - return false; + LOG(WARNING) << StringPrintf("Unknown -XX:BackgroundGC option %s\n", substring.c_str()); } } } else if (option == "-XX:+DisableExplicitGC") { @@ -503,8 +502,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize } else if (verbose_options[i] == "verifier") { gLogVerbosity.verifier = true; } else { - Usage("Unknown -verbose option %s\n", verbose_options[i].c_str()); - return false; + LOG(WARNING) << StringPrintf("Unknown -verbose option %s\n", verbose_options[i].c_str()); } } } else if (StartsWith(option, "-verbose-methods:")) { @@ -622,8 +620,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize } else if (verify_mode == "remote" || verify_mode == "all") { verify_ = true; } else { - Usage("Unknown -Xverify option %s\n", verify_mode.c_str()); - return false; + LOG(WARNING) << StringPrintf("Unknown -Xverify option %s\n", verify_mode.c_str()); } } else if (StartsWith(option, "-XX:NativeBridge=")) { if (!ParseStringAfterChar(option, '=', &native_bridge_library_filename_)) { @@ -666,8 +663,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize StartsWith(option, "-XX:mainThreadStackSize=")) { // Ignored for backwards compatibility. } else if (!ignore_unrecognized) { - Usage("Unrecognized option %s\n", option.c_str()); - return false; + LOG(WARNING) << StringPrintf("Unrecognized option %s\n", option.c_str()); } } // If not set, background collector type defaults to homogeneous compaction From c99431be851b79d134fa89ad9e8e1d7fbdd377f5 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sat, 6 Jun 2015 22:48:27 +0200 Subject: [PATCH 087/150] [Xposed] Don't include make files for tests This saves about 15 seconds per build, which is a nice speed-up for incremental builds. --- Android.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Android.mk b/Android.mk index 15e8308ba0b..8c78580949f 100644 --- a/Android.mk +++ b/Android.mk @@ -124,6 +124,8 @@ ifdef TARGET_2ND_ARCH ART_TARGET_DEPENDENCIES += $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so endif +ifdef DISABLED_FOR_XPOSED + ######################################################################## # test rules @@ -290,6 +292,8 @@ test-art-target-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target- $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) endif +endif # DISABLED_FOR_XPOSED + ######################################################################## # oat-target and oat-target-sync rules From b24d238d312c7628b782282023d41ff27bb7fa6a Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sat, 6 Jun 2015 22:51:01 +0200 Subject: [PATCH 088/150] [Xposed] Fix oatdump crash This was a regression from 5184a0542b81c297afa97d4540e4751db5fc9876. --- runtime/oat_file.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 19b232400ae..af68530a742 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -139,7 +139,7 @@ OatFile* OatFile::OpenElfFile(File* file, OatFile::OatFile(const std::string& location, bool is_executable) : location_(location), begin_(NULL), end_(NULL), is_executable_(is_executable), - is_created_in_zygote_(Runtime::Current()->IsZygote()), dlopen_handle_(NULL), + is_created_in_zygote_(Runtime::Current() != nullptr && Runtime::Current()->IsZygote()), dlopen_handle_(NULL), secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) { CHECK(!location_.empty()); } From 6191536483058232dc241ce2ea90b3da1511e5ad Mon Sep 17 00:00:00 2001 From: C3C0 Date: Tue, 9 Jun 2015 07:23:47 +0200 Subject: [PATCH 089/150] Revert "[Xposed] don't do artQuickGetProxyThisObject on xposed hooked method" This reverts commit f74f12dafd2c12917737d514ebb893527d1fd71d. Not needed --- runtime/stack.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/stack.cc b/runtime/stack.cc index 3b0989e83f6..7dd4f081d19 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -127,7 +127,7 @@ mirror::Object* StackVisitor::GetThisObject() const { } else { return cur_shadow_frame_->GetVRegReference(0); } - } else if (m->IsProxyMethod() && !m->IsXposedHookedMethod()) { + } else if (m->IsProxyMethod()) { if (cur_quick_frame_ != nullptr) { return artQuickGetProxyThisObject(cur_quick_frame_); } else { From a74b45d77bcbc68b8528edeba7770ca077f034e5 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Thu, 4 Jun 2015 23:40:37 +0200 Subject: [PATCH 090/150] [Xposed] Don't reuse open oat files from Zygote Xposed modules are loaded in the Zygote process. ART compiles the .dex file and opens the resulting .oat file in the Dalvik cache for that. However, the same happens again when the user installs a new version of the module, overwriting the file in the Dalvik cache. When opening the new .oat file though, ART detects that the file is already opened and reuses this instance. Later, it detects that the checksum doesn't match, which causes a crash. This will only occur on every second update (without reboot) as Android uses alternating suffixes for the .apk files (-1.apk / -2.apk). Let's work around it by ignoring already opened .oat files if they have been opened in Zygote. The behavior for files in /system (boot image and framework) is unchanged. Fixes rovo89/Xposed#22. Conflicts: runtime/oat_file.h --- runtime/class_linker.cc | 4 ++++ runtime/oat_file.cc | 1 + runtime/oat_file.h | 7 +++++++ 3 files changed, 12 insertions(+) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 6dcb74671fd..31b83f221c1 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -1249,6 +1249,10 @@ const OatFile* ClassLinker::FindOpenedOatFileFromOatLocation(const std::string& const OatFile* oat_file = oat_files_[i]; DCHECK(oat_file != nullptr); if (oat_file->GetLocation() == oat_location) { + if (oat_file->IsCreatedInZygote() && !Runtime::Current()->IsZygote() && oat_location.find("/system@") == std::string::npos) { + LOG(INFO) << "Ignoring oat file opened by Zygote: " << oat_location; + continue; + } return oat_file; } } diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 8bf7ad4864d..b5886a2a43f 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -143,6 +143,7 @@ OatFile* OatFile::OpenElfFile(File* file, OatFile::OatFile(const std::string& location, bool is_executable) : location_(location), begin_(NULL), end_(NULL), is_executable_(is_executable), + is_created_in_zygote_(Runtime::Current() != NULL && Runtime::Current()->IsZygote()), dlopen_handle_(NULL), secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) { CHECK(!location_.empty()); diff --git a/runtime/oat_file.h b/runtime/oat_file.h index 5d92a05a6e5..8e118cbc4e3 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -75,6 +75,10 @@ class OatFile { bool IsPic() const; + bool IsCreatedInZygote() const { + return is_created_in_zygote_; + } + ElfFile* GetElfFile() const { CHECK_NE(reinterpret_cast(elf_file_.get()), reinterpret_cast(nullptr)) << "Cannot get an elf file from " << GetLocation(); @@ -327,6 +331,9 @@ class OatFile { // Was this oat_file loaded executable? const bool is_executable_; + // Was this file loaded in Zygote? + const bool is_created_in_zygote_; + // Backing memory map for oat file during when opened by ElfWriter during initial compilation. std::unique_ptr mem_map_; From c10f139b639d514b9e28a4977f1d2793a7a85aed Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 5 Jun 2015 20:23:39 +0200 Subject: [PATCH 091/150] [Xposed] Ignore unknown arguments and option values Some ROMs are configured to make use of options that are only available in these ROMs, but not in AOSP (and therefore, not in Xposed). There's little chance that these options can be implemented in Xposed's version of ART as well. However, these options aren't necessarily required, they might just be minor tweaks. So we just log them, but don't prevent the device from booting (unless it happened to be a strictly required option, but then it wouldn't boot either way). Fixes #7. --- dex2oat/dex2oat.cc | 6 +++--- oatdump/oatdump.cc | 1 - patchoat/patchoat.cc | 2 +- runtime/parsed_options.cc | 16 ++++++---------- 4 files changed, 10 insertions(+), 15 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 749aa52dbdd..439ed6dc62d 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -778,7 +778,7 @@ static InstructionSetFeatures ParseFeatureList(std::string str) { // Turn off support for Large Physical Address Extension. result.SetHasLpae(false); } else { - Usage("Unknown instruction set feature: '%s'", feature.c_str()); + LOG(WARNING) << StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); } } // others... @@ -1132,7 +1132,7 @@ static int dex2oat(int argc, char** argv) { Usage("--swap-fd passed a negative value %d", swap_fd); } } else { - Usage("Unknown argument %s", option.data()); + LOG(WARNING) << StringPrintf("Unknown argument %s", option.data()); } } @@ -1266,7 +1266,7 @@ static int dex2oat(int argc, char** argv) { } else if (strcmp(compiler_filter_string, "everything") == 0) { compiler_filter = CompilerOptions::kEverything; } else { - Usage("Unknown --compiler-filter value %s", compiler_filter_string); + LOG(WARNING) << StringPrintf("Unknown --compiler-filter value %s", compiler_filter_string); } // Set the compilation target's implicit checks options. diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index b3fa2dae9e9..a92ac20e809 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1765,7 +1765,6 @@ static int oatdump(int argc, char** argv) { method_filter_ = option.substr(strlen("--method-filter=")).data(); } else { fprintf(stderr, "Unknown argument %s\n", option.data()); - usage(); } } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index b046ea1ef43..d8e92bec567 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -1060,7 +1060,7 @@ static int patchoat(int argc, char **argv) { } else if (option == "--no-dump-timings") { dump_timings = false; } else { - Usage("Unknown argument %s", option.data()); + LOG(WARNING) << StringPrintf("Unknown argument %s", option.data()); } } diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 6fd88147484..5b9beab2e03 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -21,6 +21,7 @@ #endif #include "base/stringpiece.h" +#include "base/stringprintf.h" #include "debugger.h" #include "gc/heap.h" #include "monitor.h" @@ -159,8 +160,7 @@ bool ParsedOptions::ParseXGcOption(const std::string& option) { (gc_option == "noverifycardtable")) { // Ignored for backwards compatibility. } else { - Usage("Unknown -Xgc option %s\n", gc_option.c_str()); - return false; + LOG(WARNING) << StringPrintf("Unknown -Xgc option %s\n", gc_option.c_str()); } } return true; @@ -465,8 +465,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize if (collector_type != gc::kCollectorTypeNone) { background_collector_type_ = collector_type; } else { - Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str()); - return false; + LOG(WARNING) << StringPrintf("Unknown -XX:BackgroundGC option %s\n", substring.c_str()); } } } else if (option == "-XX:+DisableExplicitGC") { @@ -502,8 +501,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize } else if (verbose_options[i] == "verifier") { gLogVerbosity.verifier = true; } else { - Usage("Unknown -verbose option %s\n", verbose_options[i].c_str()); - return false; + LOG(WARNING) << StringPrintf("Unknown -verbose option %s\n", verbose_options[i].c_str()); } } } else if (StartsWith(option, "-verbose-methods:")) { @@ -621,8 +619,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize } else if (verify_mode == "remote" || verify_mode == "all") { verify_ = true; } else { - Usage("Unknown -Xverify option %s\n", verify_mode.c_str()); - return false; + LOG(WARNING) << StringPrintf("Unknown -Xverify option %s\n", verify_mode.c_str()); } } else if (StartsWith(option, "-XX:NativeBridge=")) { if (!ParseStringAfterChar(option, '=', &native_bridge_library_filename_)) { @@ -665,8 +662,7 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize StartsWith(option, "-XX:mainThreadStackSize=")) { // Ignored for backwards compatibility. } else if (!ignore_unrecognized) { - Usage("Unrecognized option %s\n", option.c_str()); - return false; + LOG(WARNING) << StringPrintf("Unrecognized option %s\n", option.c_str()); } } // If not set, background collector type defaults to homogeneous compaction From ebfb5b731e90fcdab0b4b28333fc76f128e1655f Mon Sep 17 00:00:00 2001 From: Yevgeny Rouban Date: Thu, 19 Mar 2015 19:01:11 +0600 Subject: [PATCH 092/150] ART: patchoat gets gzip support for compressed odex files The patchoat is capable to inflate a compressed odex file into a temporary file. Then the temporary file is patched as if it is specified with --input-oat-fd or --input-oat-file. The new options are the following: --input-oat-gz-fd --input-oat-gz-file --swap-fd --swap-file Could not make in-memory decompression and patching because the ElfFile class maps Elf file using its file descriptor. So a temporary file is used as it is done by dex2oat. Both gzip and zip are supported by zlib library and they have the same compression algorithm. Gzip is chosen because zip needs a zip entry name, which is unnecessary for single-file compression. Change-Id: I54dbfcdc58bc0f57f7eccc3929c09295c597794b Signed-off-by: Yevgeny Rouban Conflicts: patchoat/Android.mk --- build/Android.executable.mk | 2 + patchoat/Android.mk | 5 +- patchoat/patchoat.cc | 148 +++++++++++++++++++++++++++++++++++- 3 files changed, 150 insertions(+), 5 deletions(-) diff --git a/build/Android.executable.mk b/build/Android.executable.mk index 412f2ddfee2..6fd6b2bfff7 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -50,6 +50,7 @@ define build-art-executable art_target_or_host := $(5) art_ndebug_or_debug := $(6) art_multilib := $(7) + art_static_libraries := $(8) include $(CLEAR_VARS) LOCAL_CPP_EXTENSION := $(ART_CPP_EXTENSION) @@ -57,6 +58,7 @@ define build-art-executable LOCAL_SRC_FILES := $$(art_source) LOCAL_C_INCLUDES += $(ART_C_INCLUDES) art/runtime $$(art_c_includes) LOCAL_SHARED_LIBRARIES += $$(art_shared_libraries) + LOCAL_STATIC_LIBRARIES += $$(art_static_libraries) LOCAL_WHOLE_STATIC_LIBRARIES += libsigchain ifeq ($$(art_ndebug_or_debug),ndebug) diff --git a/patchoat/Android.mk b/patchoat/Android.mk index 1eaf7815088..429e4efe8e5 100644 --- a/patchoat/Android.mk +++ b/patchoat/Android.mk @@ -30,8 +30,9 @@ else endif ifeq ($(ART_BUILD_TARGET_NDEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,ndebug,$(patchoat_arch))) + $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,ndebug,$(patchoat_arch),libz)) endif ifeq ($(ART_BUILD_TARGET_DEBUG),true) - $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,debug,$(patchoat_arch))) + $(eval $(call build-art-executable,patchoat,$(PATCHOAT_SRC_FILES),libcutils,art/compiler,target,debug,$(patchoat_arch),libz)) endif + diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index d8e92bec567..168a1df7317 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -45,6 +45,7 @@ #include "runtime.h" #include "scoped_thread_state_change.h" #include "thread.h" +#include "zlib.h" #include "utils.h" namespace art { @@ -800,6 +801,16 @@ static void Usage(const char *fmt, ...) { UsageError(" --input-oat-fd=: Specifies the file-descriptor of the oat file"); UsageError(" to be patched."); UsageError(""); + UsageError(" --input-oat-gz-file=: Specifies the exact filename of"); + UsageError(" the gzip-compressed oat file to be patched."); + UsageError(""); + UsageError(" --input-oat-gz-fd=: Specifies the file-descriptor of"); + UsageError(" the gzip-compressed oat file to be patched."); + UsageError(""); + UsageError(" --swap-file=: Specifies a temporary gzip file."); + UsageError(""); + UsageError(" --swap-fd=: Specifies a temporary gzip file descriptor."); + UsageError(""); UsageError(" --input-oat-location=: Specifies the 'location' to read the patched"); UsageError(" oat file from. If used one must also supply the --instruction-set"); UsageError(""); @@ -908,6 +919,67 @@ static bool FinishFile(File* file, bool close) { } } +static int Inflate(int input_oat_gz_fd, const std::string& input_oat_gz_filename, int tmp_fd, std::string* err) { + gzFile in_gzfile = input_oat_gz_fd != -1 ? gzdopen(input_oat_gz_fd, "rb") : + gzopen(input_oat_gz_filename.c_str(), "rb"); + if (in_gzfile == nullptr) { + *err = input_oat_gz_fd != -1 ? + StringPrintf("Could not open gzip file fd=%d: %s", + input_oat_gz_fd, strerror(errno)) : + StringPrintf("Could not open gzip file %s: %s", + input_oat_gz_filename.c_str(), strerror(errno)); + return -1; + } + + int out_fd = dup(tmp_fd); + if (out_fd == -1) { + *err = strerror(errno); + return -1; + } + + constexpr size_t INFLATE_BUFLEN = 16384; + std::unique_ptr out_file(new File(out_fd, false)); + std::unique_ptr buf(new Byte[INFLATE_BUFLEN]); + int len; + + while (0 < (len = gzread(in_gzfile, buf.get(), INFLATE_BUFLEN))) { + if (!out_file->WriteFully(buf.get(), len)) { + *err = StringPrintf("Could not write to fd=%d: %s", tmp_fd, out_file->GetPath().c_str()); + gzclose(in_gzfile); + return -1; + } + } + + int errnum; + const char* gzerrstr = gzerror(in_gzfile, &errnum); + + if (len < 0 || errnum != Z_OK) { + *err = input_oat_gz_fd != -1 ? + StringPrintf("Could not inflate gzip file fd=%d: %s", + input_oat_gz_fd, gzerrstr) : + StringPrintf("Could not inflate gzip file %s: %s", + input_oat_gz_filename.c_str(), gzerrstr); + gzclose(in_gzfile); + return -1; + } + + if ((errnum = gzclose(in_gzfile)) != Z_OK) { + *err = input_oat_gz_fd != -1 ? + StringPrintf("Could not close gzip file fd=%d: gzclose() returned %d", + input_oat_gz_fd, errnum) : + StringPrintf("Could not close gzip file %s: gzclose() returned %d", + input_oat_gz_filename.c_str(), errnum); + } + + if (out_file->Flush() != 0) { + *err = StringPrintf("Could not flush tmp file fd=%d", tmp_fd); + return -1; + } + + out_file->DisableAutoClose(); + return out_fd; +} + static int patchoat(int argc, char **argv) { InitLogging(argv); MemMap::Init(); @@ -932,8 +1004,12 @@ static int patchoat(int argc, char **argv) { bool isa_set = false; InstructionSet isa = kNone; std::string input_oat_filename; + std::string input_oat_gz_filename; std::string input_oat_location; int input_oat_fd = -1; + int input_oat_gz_fd = -1; + std::string swap_file_name; + int swap_fd = -1; bool have_input_oat = false; std::string input_image_location; std::string output_oat_filename; @@ -968,19 +1044,29 @@ static int patchoat(int argc, char **argv) { } } else if (option.starts_with("--input-oat-location=")) { if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); } have_input_oat = true; input_oat_location = option.substr(strlen("--input-oat-location=")).data(); } else if (option.starts_with("--input-oat-file=")) { if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); } have_input_oat = true; input_oat_filename = option.substr(strlen("--input-oat-file=")).data(); + } else if (option.starts_with("--input-oat-gz-file=")) { + if (have_input_oat) { + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); + } + have_input_oat = true; + input_oat_gz_filename = option.substr(strlen("--input-oat-gz-file=")).data(); } else if (option.starts_with("--input-oat-fd=")) { if (have_input_oat) { - Usage("Only one of --input-oat-file, --input-oat-location and --input-oat-fd may be used."); + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); } have_input_oat = true; const char* oat_fd_str = option.substr(strlen("--input-oat-fd=")).data(); @@ -990,6 +1076,29 @@ static int patchoat(int argc, char **argv) { if (input_oat_fd < 0) { Usage("--input-oat-fd pass a negative value %d", input_oat_fd); } + } else if (option.starts_with("--input-oat-gz-fd=")) { + if (have_input_oat) { + Usage("Only one of --input-oat-file, --input-oat-gz-file, --input-oat-location, " + "--input-oat-fd and --input-oat-gz-fd may be used."); + } + have_input_oat = true; + const char* oat_gz_fd_str = option.substr(strlen("--input-oat-gz-fd=")).data(); + if (!ParseInt(oat_gz_fd_str, &input_oat_gz_fd)) { + Usage("Failed to parse --input-oat-fd argument '%s' as an integer", oat_gz_fd_str); + } + if (input_oat_gz_fd < 0) { + Usage("--input-oat-gz-fd pass a negative value %d", input_oat_gz_fd); + } + } else if (option.starts_with("--swap-file=")) { + swap_file_name = option.substr(strlen("--swap-file=")).data(); + } else if (option.starts_with("--swap-fd=")) { + const char* swap_fd_str = option.substr(strlen("--swap-fd=")).data(); + if (!ParseInt(swap_fd_str, &swap_fd)) { + Usage("Failed to parse --swap-fd argument '%s' as an integer", swap_fd_str); + } + if (swap_fd < 0) { + Usage("--swap-fd passed a negative value %d", swap_fd); + } } else if (option.starts_with("--input-image-location=")) { input_image_location = option.substr(strlen("--input-image-location=")).data(); } else if (option.starts_with("--output-oat-file=")) { @@ -1088,6 +1197,11 @@ static int patchoat(int argc, char **argv) { Usage("Either both input and output image must be supplied or niether must be."); } + if ((input_oat_gz_fd != -1 || !input_oat_gz_filename.empty()) != + (swap_fd != -1 || !swap_file_name.empty())) { + Usage("Either both input gzip and swap must be supplied or niether must be."); + } + // We know we have both the input and output so rename for clarity. bool have_image_files = have_output_image; bool have_oat_files = have_output_oat; @@ -1111,6 +1225,26 @@ static int patchoat(int argc, char **argv) { LOG(INFO) << "Using input-oat-file " << input_oat_filename; } } + + // Swap file handling. + // + // If the swap fd is not -1, we assume this is the file descriptor of an open but unlinked file + // that we can use for swap. + // + // If the swap fd is -1 and we have a swap-file string, open the given file as a swap file. We + // will immediately unlink to satisfy the swap fd assumption. + std::unique_ptr swap_file; + if (swap_fd == -1 && !swap_file_name.empty()) { + swap_file.reset(OS::CreateEmptyFile(swap_file_name.c_str())); + if (swap_file.get() == nullptr) { + PLOG(ERROR) << "Failed to create swap file: " << swap_file_name; + return EXIT_FAILURE; + } + swap_fd = swap_file->Fd(); + swap_file->MarkUnchecked(); // We don't want to track this, it will be unlinked immediately. + unlink(swap_file_name.c_str()); + } + if (!patched_image_location.empty()) { if (!isa_set) { Usage("specifying a location requires specifying an instruction set"); @@ -1189,8 +1323,16 @@ static int patchoat(int argc, char **argv) { } if (have_oat_files) { + if (input_oat_gz_fd != -1 || !input_oat_gz_filename.empty()) { + std::string err; + input_oat_fd = Inflate(input_oat_gz_fd, input_oat_gz_filename, swap_fd, &err); + if (input_oat_fd == -1) { + LOG(ERROR) << "Failed to inflate input file: " << err; + } + } if (input_oat_fd != -1) { if (input_oat_filename.empty()) { + // TODO: make sure this will not be used to create symlink if input_oat_fd is PIC input_oat_filename = "input-oat-file"; } input_oat.reset(new File(input_oat_fd, input_oat_filename, false)); From 92be051e1070b50a28abf8578813315255398046 Mon Sep 17 00:00:00 2001 From: Yevgeny Rouban Date: Fri, 20 Mar 2015 15:42:24 +0600 Subject: [PATCH 093/150] ART: prevent patchoat from symlinking with dummy files If OAT file is position independent (PIC, dex2oat's option --compile-pic) then the patchoat just creates a symlink with the input oat file. But if the input oat file is set by its file descriptor (option --input-oat-fd) or just has been inflated to a temporary file (options --input-oat-gz-file or --input-oat-gz-fd) then its file name is set to a dummy 'input-oat-file' and making a symlink with this dummy path makes no sense. This patch fixes the patchoat to make a copy of the input file in such cases. Change-Id: Ib63b0c80b1da06b60cbb754206c510a7396624a9 Signed-off-by: Yevgeny Rouban --- patchoat/patchoat.cc | 114 +++++++++++++++++++++++++++---------------- patchoat/patchoat.h | 15 +++--- 2 files changed, 82 insertions(+), 47 deletions(-) diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 168a1df7317..9017a7e7044 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -196,7 +196,7 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d File* output_oat, File* output_image, InstructionSet isa, TimingLogger* timings, bool output_oat_opened_from_fd, - bool new_oat_out) { + bool new_oat_out, bool input_oat_filename_dummy) { CHECK(Runtime::Current() == nullptr); CHECK(output_image != nullptr); CHECK_GE(output_image->Fd(), 0); @@ -285,11 +285,9 @@ bool PatchOat::Patch(File* input_oat, const std::string& image_location, off_t d // Error logged by IsOatPic return false; } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the ELF patching. - if (!ReplaceOatFileWithSymlink(input_oat->GetPath(), - output_oat->GetPath(), - output_oat_opened_from_fd, - new_oat_out)) { + // Do not need to do ELF-file patching. Create a symlink or make a copy. + if (!SymlinkOrCopy(input_oat, output_oat, output_oat_opened_from_fd, + new_oat_out, input_oat_filename_dummy)) { // Errors already logged by above call. return false; } @@ -399,36 +397,70 @@ PatchOat::MaybePic PatchOat::IsOatPic(const ElfFile* oat_in) { return is_pic ? PIC : NOT_PIC; } -bool PatchOat::ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out) { - // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD. - if (output_oat_opened_from_fd) { - // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC? - LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC"; - return false; - } +const size_t COPY_BUFLEN = 16384; + +bool PatchOat::SymlinkOrCopy(File* input_oat, + File* output_oat, + bool output_oat_opened_from_fd, + bool new_oat_out, bool make_copy) { + std::string output_oat_filename = output_oat->GetPath(); + std::string input_oat_filename = input_oat->GetPath(); + + if (make_copy) { + // Make a copy of the PIC oat file. + std::unique_ptr buf(new char[COPY_BUFLEN]); + int64_t len; + int64_t read_size = 0; + while (true) { + len = input_oat->Read(buf.get(), COPY_BUFLEN, read_size); + if (len <= 0) { + break; + } + if (!output_oat->WriteFully(buf.get(), len)) { + len = -1; + break; + } + read_size += len; + } - // Image was PIC. Create symlink where the oat is supposed to go. - if (!new_oat_out) { - LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite"; - return false; - } + if (len < 0) { + int err = errno; + LOG(ERROR) << "Failed to copy " << input_oat_filename << " to " << output_oat_filename + << ": error(" << err << "): " << strerror(err); + return false; + } + + if (kIsDebugBuild) { + LOG(INFO) << "Copied " << input_oat_filename << " -> " << output_oat_filename; + } + } else { + // Need a file when we are PIC, since we symlink over it. Refusing to symlink into FD. + if (output_oat_opened_from_fd) { + // TODO: installd uses --output-oat-fd. Should we change class linking logic for PIC? + LOG(ERROR) << "No output oat filename specified, needs filename for when we are PIC"; + return false; + } - // Delete the original file, since we won't need it. - TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str())); + // Image was PIC. Create symlink where the oat is supposed to go. + if (!new_oat_out) { + LOG(ERROR) << "Oat file " << output_oat_filename << " already exists, refusing to overwrite"; + return false; + } - // Create a symlink from the old oat to the new oat - if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) < 0) { - int err = errno; - LOG(ERROR) << "Failed to create symlink at " << output_oat_filename - << " error(" << err << "): " << strerror(err); - return false; - } + // Delete the original file, since we won't need it. + TEMP_FAILURE_RETRY(unlink(output_oat_filename.c_str())); - if (kIsDebugBuild) { - LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename; + // Create a symlink from the old oat to the new oat. + if (symlink(input_oat_filename.c_str(), output_oat_filename.c_str()) == 0) { + if (kIsDebugBuild) { + LOG(INFO) << "Created symlink " << output_oat_filename << " -> " << input_oat_filename; + } + } else { + int err = errno; + LOG(ERROR) << "Failed to create symlink at " << output_oat_filename + << " error(" << err << "): " << strerror(err); + return false; + } } return true; @@ -562,7 +594,8 @@ void PatchOat::FixupMethod(mirror::ArtMethod* object, mirror::ArtMethod* copy) { } bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogger* timings, - bool output_oat_opened_from_fd, bool new_oat_out) { + bool output_oat_opened_from_fd, bool new_oat_out, + bool input_oat_filename_dummy) { CHECK(input_oat != nullptr); CHECK(output_oat != nullptr); CHECK_GE(input_oat->Fd(), 0); @@ -582,12 +615,10 @@ bool PatchOat::Patch(File* input_oat, off_t delta, File* output_oat, TimingLogge // Error logged by IsOatPic return false; } else if (is_oat_pic == PIC) { - // Do not need to do ELF-file patching. Create a symlink and skip the rest. + // Do not need to do ELF-file patching. Create a symlink or make a copy. // Any errors will be logged by the function call. - return ReplaceOatFileWithSymlink(input_oat->GetPath(), - output_oat->GetPath(), - output_oat_opened_from_fd, - new_oat_out); + return SymlinkOrCopy(input_oat, output_oat, output_oat_opened_from_fd, + new_oat_out, input_oat_filename_dummy); } else { CHECK(is_oat_pic == NOT_PIC); } @@ -1004,6 +1035,7 @@ static int patchoat(int argc, char **argv) { bool isa_set = false; InstructionSet isa = kNone; std::string input_oat_filename; + bool input_oat_filename_dummy = false; std::string input_oat_gz_filename; std::string input_oat_location; int input_oat_fd = -1; @@ -1332,8 +1364,8 @@ static int patchoat(int argc, char **argv) { } if (input_oat_fd != -1) { if (input_oat_filename.empty()) { - // TODO: make sure this will not be used to create symlink if input_oat_fd is PIC input_oat_filename = "input-oat-file"; + input_oat_filename_dummy = true; } input_oat.reset(new File(input_oat_fd, input_oat_filename, false)); if (input_oat == nullptr) { @@ -1426,7 +1458,7 @@ static int patchoat(int argc, char **argv) { ret = PatchOat::Patch(input_oat.get(), input_image_location, base_delta, output_oat.get(), output_image.get(), isa, &timings, output_oat_fd >= 0, // was it opened from FD? - new_oat_out); + new_oat_out, input_oat_filename_dummy); // The order here doesn't matter. If the first one is successfully saved and the second one // erased, ImageSpace will still detect a problem and not use the files. ret = ret && FinishFile(output_image.get(), ret); @@ -1435,7 +1467,7 @@ static int patchoat(int argc, char **argv) { TimingLogger::ScopedTiming pt("patch oat", &timings); ret = PatchOat::Patch(input_oat.get(), base_delta, output_oat.get(), &timings, output_oat_fd >= 0, // was it opened from FD? - new_oat_out); + new_oat_out, input_oat_filename_dummy); ret = ret && FinishFile(output_oat.get(), ret); } else if (have_image_files) { TimingLogger::ScopedTiming pt("patch image", &timings); diff --git a/patchoat/patchoat.h b/patchoat/patchoat.h index 03d915abddb..3a21d8514ff 100644 --- a/patchoat/patchoat.h +++ b/patchoat/patchoat.h @@ -44,7 +44,8 @@ class PatchOat { // Patch only the oat file static bool Patch(File* oat_in, off_t delta, File* oat_out, TimingLogger* timings, bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? - bool new_oat_out); // Output oat was a new file created by us? + bool new_oat_out, // Output oat was a new file created by us? + bool input_oat_filename_dummy); // Input cannot be symlinked // Patch only the image (art file) static bool Patch(const std::string& art_location, off_t delta, File* art_out, InstructionSet isa, @@ -55,7 +56,8 @@ class PatchOat { off_t delta, File* oat_out, File* art_out, InstructionSet isa, TimingLogger* timings, bool output_oat_opened_from_fd, // Was this using --oatput-oat-fd ? - bool new_oat_out); // Output oat was a new file created by us? + bool new_oat_out, // Output oat was a new file created by us? + bool input_oat_filename_dummy); // Input cannot be symlinked private: // Takes ownership only of the ElfFile. All other pointers are only borrowed. @@ -87,10 +89,11 @@ class PatchOat { // Attempt to replace the file with a symlink // Returns false if it fails - static bool ReplaceOatFileWithSymlink(const std::string& input_oat_filename, - const std::string& output_oat_filename, - bool output_oat_opened_from_fd, - bool new_oat_out); // Output oat was newly created? + static bool SymlinkOrCopy(File* input_oat, + File* output_oat, + bool output_oat_opened_from_fd, + bool new_oat_out, // Output oat was newly created? + bool make_copy); static void BitmapCallback(mirror::Object* obj, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { From 18952020164b5ca9133d211a80f38aac90accb42 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Wed, 10 Jun 2015 22:09:43 +0200 Subject: [PATCH 094/150] [Xposed] Don't make original methods private They need to be handled as direct methods (e.g. no vtable lookups), but changing the access makes it harder to determine later whether it originally was a private method already. This distinction is required when retrieving the oat method index. Instead, we simulate the direct method flag in IsDirect(), with an optional parameter to use the original meaning (i.e. handle the method as if it wasn't modified by Xposed). For consistency, we add the same parameter to IsProxyMethod() as well. --- runtime/class_linker.cc | 2 +- runtime/mirror/art_method.cc | 6 +++--- runtime/mirror/art_method.h | 14 +++++++++----- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index c1c3b93e89b..b8e4e5a7aaf 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2388,7 +2388,7 @@ bool ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod // method for direct methods (or virtual methods made direct). mirror::Class* declaring_class = method->GetDeclaringClass(); size_t oat_method_index; - if (method->IsStatic() || method->IsDirect()) { + if (method->IsStatic() || method->IsDirect(true)) { // Simple case where the oat method index was stashed at load time. oat_method_index = method->GetMethodIndex(); } else { diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 721060791e3..e426e0d1b31 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -120,8 +120,8 @@ size_t ArtMethod::NumArgRegisters(const StringPiece& shorty) { return num_registers; } -bool ArtMethod::IsProxyMethod() { - return GetDeclaringClass()->IsProxyClass() || IsXposedHookedMethod(); +bool ArtMethod::IsProxyMethod(bool ignore_xposed) { + return GetDeclaringClass()->IsProxyClass() || (!ignore_xposed && IsXposedHookedMethod()); } ArtMethod* ArtMethod::FindOverriddenMethod() { @@ -409,7 +409,7 @@ void ArtMethod::EnableXposedHook(JNIEnv* env, jobject additional_info) { // Create a backup of the ArtMethod object ArtMethod* backup_method = down_cast(Clone(soa.Self())); // Set private flag to avoid virtual table lookups during invocation - backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccPrivate | kAccXposedOriginalMethod); + backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod); // Create a Method/Constructor object for the backup ArtMethod object jobject reflect_method; diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 0bf34d1a7f6..6b3199bd046 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -106,12 +106,16 @@ class MANAGED ArtMethod FINAL : public Object { } // Returns true if the method is static, private, or a constructor. - bool IsDirect() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return IsDirect(GetAccessFlags()); + bool IsDirect(bool ignore_xposed = false) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return IsDirect(GetAccessFlags(), ignore_xposed); } - static bool IsDirect(uint32_t access_flags) { - return (access_flags & (kAccStatic | kAccPrivate | kAccConstructor)) != 0; + static bool IsDirect(uint32_t access_flags, bool ignore_xposed = false) { + uint32_t mask = kAccStatic | kAccPrivate | kAccConstructor; + if (LIKELY(!ignore_xposed)) { + mask |= kAccXposedOriginalMethod; + } + return (access_flags & mask) != 0; } // Returns true if the method is declared synchronized. @@ -145,7 +149,7 @@ class MANAGED ArtMethod FINAL : public Object { return (GetAccessFlags() & kAccSynthetic) != 0; } - bool IsProxyMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsProxyMethod(bool ignore_xposed = false) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsPreverified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return (GetAccessFlags() & kAccPreverified) != 0; From a0f714a728db236a37823543c0949a3eae3ff1ee Mon Sep 17 00:00:00 2001 From: Daniel Mihalyi Date: Fri, 10 Oct 2014 18:24:11 +0200 Subject: [PATCH 095/150] Fixed ArtMethod::GetQuickFrameInfo() for proxy methods If we call ArtMethod::GetQuickFrameInfo() on a proxy constructor when instrumentation stubs are installed, the entry point returned by Instrumentation::GetQuickCodeFor() would be a artQuickProxyInvokeHandler, and not a regular quick oat code pointer. This patch adds special handling for proxy constructors to always compute frame info from the regular quick code entry point. (These entry points are never updated by instrumentation for proxy methods.) Change-Id: I8e51f5622b6d327037d5c968b07505b2bf4ac737 Signed-off-by: Daniel Mihalyi (cherry picked from commit e14f2b3047fd310c5df65cf13470aa2786522a3b) --- runtime/mirror/art_method-inl.h | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index ca4ebd66ca4..53b43782ffc 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -352,17 +352,32 @@ inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() { return QuickMethodFrameInfo(kStackAlignment, 0u, 0u); } Runtime* runtime = Runtime::Current(); - // For Proxy method we exclude direct method (there is only one direct method - constructor). - // Direct method is cloned from original java.lang.reflect.Proxy class together with code - // and as a result it is executed as usual quick compiled method without any stubs. - // So the frame info should be returned as it is a quick method not a stub. - if (UNLIKELY(IsAbstract()) || UNLIKELY(IsProxyMethod() && !IsDirect()) || UNLIKELY(IsXposedHookedMethod())) { + + if (UNLIKELY(IsAbstract() || IsXposedHookedMethod())) { return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); } + + // This goes before IsProxyMethod since runtime methods have a null declaring class. if (UNLIKELY(IsRuntimeMethod())) { return runtime->GetRuntimeMethodFrameInfo(this); } + // For Proxy method we add special handling for the direct method case (there is only one + // direct method - constructor). Direct method is cloned from original + // java.lang.reflect.Proxy class together with code and as a result it is executed as usual + // quick compiled method without any stubs. So the frame info should be returned as it is a + // quick method not a stub. However, if instrumentation stubs are installed, the + // instrumentation->GetQuickCodeFor() returns the artQuickProxyInvokeHandler instead of an + // oat code pointer, thus we have to add a special case here. + if (UNLIKELY(IsProxyMethod(true))) { + if (IsDirect()) { + CHECK(IsConstructor()); + return GetQuickFrameInfo(EntryPointToCodePointer(GetEntryPointFromQuickCompiledCode())); + } else { + return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); + } + } + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this); // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline) From 70db322906bde186ddb97181242cf00852a4fcc1 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 25 Nov 2014 16:30:53 +0100 Subject: [PATCH 096/150] Support proxy method in StackVisitor::GetThisObject Adds function artQuickGetProxyThisObject which returns the 'this' object of the proxy method using the QuickArgumentVisitor. Since proxy methods have the same layout than the kRefsAndArgs runtime method and 'this' is the 1st method argument, it is located in the first GPR. Bug: 17965861 (cherry picked from commit a836bc9760419af4a515f96c66100a39e865f3b9) Change-Id: I09b5c1cdfc051d9395bba929d4650804eb3b1f7f --- .../quick/quick_trampoline_entrypoints.cc | 23 +++++++++++++++++++ runtime/stack.cc | 9 ++++++++ 2 files changed, 32 insertions(+) diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 4fe72640235..18fe7252602 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -199,6 +199,22 @@ class QuickArgumentVisitor { #endif public: + // Special handling for proxy methods. Proxy methods are instance methods so the + // 'this' object is the 1st argument. They also have the same frame layout as the + // kRefAndArgs runtime method. Since 'this' is a reference, it is located in the + // 1st GPR. + static mirror::Object* GetProxyThisObject(StackReference* sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + CHECK(sp->AsMirrorPtr()->IsProxyMethod()); + CHECK_EQ(kQuickCalleeSaveFrame_RefAndArgs_FrameSize, sp->AsMirrorPtr()->GetFrameSizeInBytes()); + CHECK_GT(kNumQuickGprArgs, 0u); + constexpr uint32_t kThisGprIndex = 0u; // 'this' is in the 1st GPR. + size_t this_arg_offset = kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset + + GprIndexToGprOffset(kThisGprIndex); + uint8_t* this_arg_address = reinterpret_cast(sp) + this_arg_offset; + return reinterpret_cast*>(this_arg_address)->AsMirrorPtr(); + } + static mirror::ArtMethod* GetCallingMethod(StackReference* sp) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { DCHECK(sp->AsMirrorPtr()->IsCalleeSaveMethod()); @@ -410,6 +426,13 @@ class QuickArgumentVisitor { bool is_split_long_or_double_; }; +// Returns the 'this' object of a proxy method. This function is only used by StackVisitor. It +// allows to use the QuickArgumentVisitor constants without moving all the code in its own module. +extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference* sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return QuickArgumentVisitor::GetProxyThisObject(sp); +} + // Visits arguments on the stack placing them into the shadow frame. class BuildQuickShadowFrameVisitor FINAL : public QuickArgumentVisitor { public: diff --git a/runtime/stack.cc b/runtime/stack.cc index 2d0060eb69f..b63e82ad308 100644 --- a/runtime/stack.cc +++ b/runtime/stack.cc @@ -112,6 +112,9 @@ uint32_t StackVisitor::GetDexPc(bool abort_on_failure) const { } } +extern "C" mirror::Object* artQuickGetProxyThisObject(StackReference* sp) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Object* StackVisitor::GetThisObject() const { mirror::ArtMethod* m = GetMethod(); if (m->IsStatic()) { @@ -124,6 +127,12 @@ mirror::Object* StackVisitor::GetThisObject() const { } else { return cur_shadow_frame_->GetVRegReference(0); } + } else if (m->IsProxyMethod()) { + if (cur_quick_frame_ != nullptr) { + return artQuickGetProxyThisObject(cur_quick_frame_); + } else { + return cur_shadow_frame_->GetVRegReference(0); + } } else { const DexFile::CodeItem* code_item = m->GetCodeItem(); if (code_item == NULL) { From 5be00d0d75dc135d38e48bc75218965dc3921b95 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 12 Jun 2015 19:05:01 +0200 Subject: [PATCH 097/150] [Xposed] Debugger fixes Debuggers need to know about all methods in a class. For methods hooked with Xposed, both the hooked (proxy-like) and the original method have to be returned. Also, the hooked method isn't directly attached to a code item anymore, which would otherwise be used to calculate offsets into the method code. Fixes #1. --- runtime/debugger.cc | 24 +++++++++++++++++++++++- runtime/mirror/art_method-inl.h | 3 +++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index e074fc17af3..eff98f9d6ed 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1605,10 +1605,32 @@ JDWP::JdwpError Dbg::OutputDeclaredMethods(JDWP::RefTypeId class_id, bool with_g size_t direct_method_count = c->NumDirectMethods(); size_t virtual_method_count = c->NumVirtualMethods(); - expandBufAdd4BE(pReply, direct_method_count + virtual_method_count); + size_t xposed_method_count = 0; + for (size_t i = 0; i < direct_method_count + virtual_method_count; ++i) { + mirror::ArtMethod* m = (i < direct_method_count) ? c->GetDirectMethod(i) : c->GetVirtualMethod(i - direct_method_count); + if (UNLIKELY(m->IsXposedHookedMethod())) { + ++xposed_method_count; + } + } + + expandBufAdd4BE(pReply, direct_method_count + virtual_method_count + xposed_method_count); for (size_t i = 0; i < direct_method_count + virtual_method_count; ++i) { mirror::ArtMethod* m = (i < direct_method_count) ? c->GetDirectMethod(i) : c->GetVirtualMethod(i - direct_method_count); + + if (UNLIKELY(xposed_method_count > 0 && m->IsXposedHookedMethod())) { + expandBufAddMethodId(pReply, ToMethodId(m)); + expandBufAddUtf8String(pReply, m->GetName()); + expandBufAddUtf8String(pReply, m->GetSignature().ToString()); + if (with_generic) { + static const char genericSignature[1] = ""; + expandBufAddUtf8String(pReply, genericSignature); + } + expandBufAdd4BE(pReply, MangleAccessFlags(m->GetAccessFlags())); + + m = m->GetXposedOriginalMethod(); + } + expandBufAddMethodId(pReply, ToMethodId(m)); expandBufAddUtf8String(pReply, m->GetName()); expandBufAddUtf8String(pReply, m->GetSignature().ToString()); diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 53b43782ffc..c7aba5d6ea0 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -467,6 +467,9 @@ inline const char* ArtMethod::GetName() { } inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { + if (UNLIKELY(IsXposedHookedMethod())) { + return nullptr; + } mirror::ArtMethod* method = GetInterfaceMethodIfProxy(); return method->GetDexFile()->GetCodeItem(method->GetCodeItemOffset()); } From e77de6722d6de709a727a2666c5db0ac3bc73511 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 12 Jun 2015 20:16:11 +0200 Subject: [PATCH 098/150] [Xposed] Mark methods hooked by Xposed in traces In order to facilitate debugging, append special strings to hooked methods: - Java stack trace - native stack trace - debugger - PrettyMethod() utility method --- runtime/debugger.cc | 2 +- runtime/mirror/art_method-inl.h | 3 +++ runtime/thread.cc | 3 +++ runtime/utils.cc | 5 +++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index eff98f9d6ed..6e4d208dece 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1620,7 +1620,7 @@ JDWP::JdwpError Dbg::OutputDeclaredMethods(JDWP::RefTypeId class_id, bool with_g if (UNLIKELY(xposed_method_count > 0 && m->IsXposedHookedMethod())) { expandBufAddMethodId(pReply, ToMethodId(m)); - expandBufAddUtf8String(pReply, m->GetName()); + expandBufAddUtf8String(pReply, StringPrintf("%s", m->GetName()).c_str()); expandBufAddUtf8String(pReply, m->GetSignature().ToString()); if (with_generic) { static const char genericSignature[1] = ""; diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index c7aba5d6ea0..0716bc67f1e 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -502,6 +502,9 @@ inline const DexFile::TypeList* ArtMethod::GetParameterTypeList() { } inline const char* ArtMethod::GetDeclaringClassSourceFile() { + if (UNLIKELY(IsXposedHookedMethod())) { + return ""; + } return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetSourceFile(); } diff --git a/runtime/thread.cc b/runtime/thread.cc index 7fb631c0733..2d13bd31837 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1561,6 +1561,9 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( line_number = -1; class_name_object.Assign(method->GetDeclaringClass()->GetName()); // source_name_object intentionally left null for proxy methods + if (method->IsXposedHookedMethod()) { + source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), "")); + } } else { mirror::IntArray* pc_trace = down_cast(method_trace->Get(depth)); uint32_t dex_pc = pc_trace->Get(i); diff --git a/runtime/utils.cc b/runtime/utils.cc index 5c726d40661..2c7f0402ed9 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -385,6 +385,11 @@ std::string PrettyMethod(mirror::ArtMethod* m, bool with_signature) { result = PrettyReturnType(sig_as_string.c_str()) + " " + result + PrettyArguments(sig_as_string.c_str()); } + if (UNLIKELY(m->IsXposedHookedMethod())) { + result += " [XposedHooked]"; + } else if (UNLIKELY(m->IsXposedOriginalMethod())) { + result += " [XposedOriginal]"; + } return result; } From fd063c9e5e26f3e120428712f1156e17c9638b6d Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 12 Jun 2015 23:02:28 +0200 Subject: [PATCH 099/150] [Xposed] Enable hooks for native methods Fixes rovo89/Xposed#28. --- runtime/mirror/art_method.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index e426e0d1b31..1acbb6d3449 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -375,6 +375,10 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) { DCHECK(Thread::Current() == self); + if (UNLIKELY(IsXposedHookedMethod())) { + GetXposedOriginalMethod()->RegisterNative(self, native_method, is_fast); + return; + } CHECK(IsNative()) << PrettyMethod(this); CHECK(!IsFastNative()) << PrettyMethod(this); CHECK(native_method != NULL) << PrettyMethod(this); @@ -385,6 +389,10 @@ void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_ } void ArtMethod::UnregisterNative(Thread* self) { + if (UNLIKELY(IsXposedHookedMethod())) { + GetXposedOriginalMethod()->UnregisterNative(self); + return; + } CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this); // restore stub to lookup native pointer via dlsym RegisterNative(self, GetJniDlsymLookupStub(), false); @@ -397,10 +405,6 @@ void ArtMethod::EnableXposedHook(JNIEnv* env, jobject additional_info) { } else if (IsXposedOriginalMethod()) { // This should never happen XLOG(FATAL) << "You cannot hook the original method"; - } else if (IsNative()) { - // Hooking native methods should hopefully work fine, but needs testing - XLOG(INFO) << "Not hooking native method " << PrettyMethod(this); - return; } From 9655eefacdd3c3b1d10528fead1e12532f4862c4 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 12 Jun 2015 23:55:53 +0200 Subject: [PATCH 100/150] [Xposed] Minor (style) corrections --- runtime/entrypoints/entrypoint_utils.cc | 2 +- runtime/mirror/art_method.cc | 13 ++++++------- runtime/mirror/art_method.h | 5 +++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 6e5e6b83c9e..96b0b20a3ee 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -376,7 +376,7 @@ JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, co } } - XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo(); + const XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo(); // Call XposedBridge.handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, // Object thisObject, Object[] args) diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 1acbb6d3449..fb1fa34ba9f 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -399,20 +399,19 @@ void ArtMethod::UnregisterNative(Thread* self) { } void ArtMethod::EnableXposedHook(JNIEnv* env, jobject additional_info) { - if (IsXposedHookedMethod()) { + if (UNLIKELY(IsXposedHookedMethod())) { // Already hooked return; - } else if (IsXposedOriginalMethod()) { + } else if (UNLIKELY(IsXposedOriginalMethod())) { // This should never happen - XLOG(FATAL) << "You cannot hook the original method"; + ThrowIllegalArgumentException(nullptr, StringPrintf("Cannot hook the method backup: %s", PrettyMethod(this).c_str()).c_str()); + return; } - ScopedObjectAccess soa(env); // Create a backup of the ArtMethod object ArtMethod* backup_method = down_cast(Clone(soa.Self())); - // Set private flag to avoid virtual table lookups during invocation backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod); // Create a Method/Constructor object for the backup ArtMethod object @@ -426,11 +425,11 @@ void ArtMethod::EnableXposedHook(JNIEnv* env, jobject additional_info) { env->NewGlobalRef(soa.AddLocalReference(backup_method))); // Save extra information in a separate structure, stored instead of the native method - XposedHookInfo* hookInfo = (XposedHookInfo*) calloc(1, sizeof(XposedHookInfo)); + XposedHookInfo* hookInfo = reinterpret_cast(calloc(1, sizeof(XposedHookInfo))); hookInfo->reflectedMethod = env->NewGlobalRef(reflect_method); hookInfo->additionalInfo = env->NewGlobalRef(additional_info); hookInfo->originalMethod = backup_method; - SetNativeMethod((uint8_t*) hookInfo); + SetNativeMethod(reinterpret_cast(hookInfo)); SetEntryPointFromQuickCompiledCode(GetQuickProxyInvokeHandler()); SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge); diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 6b3199bd046..97366d2e761 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -501,8 +501,9 @@ class MANAGED ArtMethod FINAL : public Object { void EnableXposedHook(JNIEnv* env, jobject additional_info) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - XposedHookInfo* GetXposedHookInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return (XposedHookInfo*) GetNativeMethod(); + const XposedHookInfo* GetXposedHookInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(IsXposedHookedMethod()); + return reinterpret_cast(GetNativeMethod()); } ArtMethod* GetXposedOriginalMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { From 9cddb53605b4f542bfb4853ff21261a8725f6fc0 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sat, 6 Jun 2015 22:48:27 +0200 Subject: [PATCH 101/150] [Xposed] Don't include make files for tests This saves about 15 seconds per build, which is a nice speed-up for incremental builds. --- Android.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Android.mk b/Android.mk index 15e8308ba0b..8c78580949f 100644 --- a/Android.mk +++ b/Android.mk @@ -124,6 +124,8 @@ ifdef TARGET_2ND_ARCH ART_TARGET_DEPENDENCIES += $(2ND_TARGET_OUT_SHARED_LIBRARIES)/libjavacore.so endif +ifdef DISABLED_FOR_XPOSED + ######################################################################## # test rules @@ -290,6 +292,8 @@ test-art-target-interpreter$(2ND_ART_PHONY_TEST_TARGET_SUFFIX): test-art-target- $(hide) $(call ART_TEST_PREREQ_FINISHED,$@) endif +endif # DISABLED_FOR_XPOSED + ######################################################################## # oat-target and oat-target-sync rules From a50fcdc32b3b61df0733da1cd16ba4a5019442e4 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sat, 6 Jun 2015 22:51:01 +0200 Subject: [PATCH 102/150] [Xposed] Fix oatdump crash This was a regression from 5184a0542b81c297afa97d4540e4751db5fc9876. Conflicts: runtime/oat_file.cc --- runtime/oat_file.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index b5886a2a43f..671bb277426 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -143,8 +143,7 @@ OatFile* OatFile::OpenElfFile(File* file, OatFile::OatFile(const std::string& location, bool is_executable) : location_(location), begin_(NULL), end_(NULL), is_executable_(is_executable), - is_created_in_zygote_(Runtime::Current() != NULL && Runtime::Current()->IsZygote()), - dlopen_handle_(NULL), + is_created_in_zygote_(Runtime::Current() != nullptr && Runtime::Current()->IsZygote()), dlopen_handle_(NULL), secondary_lookup_lock_("OatFile secondary lookup lock", kOatFileSecondaryLookupLock) { CHECK(!location_.empty()); } From efafd9ac9e372f6086e5df27cbe73d51873f020c Mon Sep 17 00:00:00 2001 From: rovo89 Date: Wed, 10 Jun 2015 22:09:43 +0200 Subject: [PATCH 103/150] [Xposed] Don't make original methods private They need to be handled as direct methods (e.g. no vtable lookups), but changing the access makes it harder to determine later whether it originally was a private method already. This distinction is required when retrieving the oat method index. Instead, we simulate the direct method flag in IsDirect(), with an optional parameter to use the original meaning (i.e. handle the method as if it wasn't modified by Xposed). For consistency, we add the same parameter to IsProxyMethod() as well. Conflicts: runtime/mirror/art_method.cc --- runtime/class_linker.cc | 2 +- runtime/mirror/art_method-inl.h | 8 ++++---- runtime/mirror/art_method.cc | 2 +- runtime/mirror/art_method.h | 14 +++++++++----- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 31b83f221c1..b6e72d1016c 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -2415,7 +2415,7 @@ bool ClassLinker::FindOatMethodFor(mirror::ArtMethod* method, OatFile::OatMethod // method for direct methods (or virtual methods made direct). mirror::Class* declaring_class = method->GetDeclaringClass(); size_t oat_method_index; - if (method->IsStatic() || method->IsDirect()) { + if (method->IsStatic() || method->IsDirect(true)) { // Simple case where the oat method index was stashed at load time. oat_method_index = method->GetMethodIndex(); } else { diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 6ffa2da3e63..1ab30660dc0 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -530,15 +530,15 @@ inline mirror::DexCache* ArtMethod::GetDexCache() { return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetDexCache(); } -inline bool ArtMethod::IsProxyMethod() { - return GetDeclaringClass()->IsProxyClass() || IsXposedHookedMethod(); +inline bool ArtMethod::IsProxyMethod(bool ignore_xposed) { + return GetDeclaringClass()->IsProxyClass() || (!ignore_xposed && IsXposedHookedMethod()); } inline ArtMethod* ArtMethod::GetInterfaceMethodIfProxy() { - mirror::Class* klass = GetDeclaringClass(); - if (LIKELY(!klass->IsProxyClass())) { + if (LIKELY(!IsProxyMethod(true))) { return this; } + mirror::Class* klass = GetDeclaringClass(); mirror::ArtMethod* interface_method = GetDexCacheResolvedMethods()->Get(GetDexMethodIndex()); DCHECK(interface_method != nullptr); DCHECK_EQ(interface_method, diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index 8acb65130e3..c592ded3e3b 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -388,7 +388,7 @@ void ArtMethod::EnableXposedHook(JNIEnv* env, jobject additional_info) { // Create a backup of the ArtMethod object ArtMethod* backup_method = down_cast(Clone(soa.Self())); // Set private flag to avoid virtual table lookups during invocation - backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccPrivate | kAccXposedOriginalMethod); + backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod); // Create a Method/Constructor object for the backup ArtMethod object jobject reflect_method; diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index 4cdd29b5154..d73611ac7de 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -104,12 +104,16 @@ class MANAGED ArtMethod FINAL : public Object { } // Returns true if the method is static, private, or a constructor. - bool IsDirect() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return IsDirect(GetAccessFlags()); + bool IsDirect(bool ignore_xposed = false) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return IsDirect(GetAccessFlags(), ignore_xposed); } - static bool IsDirect(uint32_t access_flags) { - return (access_flags & (kAccStatic | kAccPrivate | kAccConstructor)) != 0; + static bool IsDirect(uint32_t access_flags, bool ignore_xposed = false) { + uint32_t mask = kAccStatic | kAccPrivate | kAccConstructor; + if (LIKELY(!ignore_xposed)) { + mask |= kAccXposedOriginalMethod; + } + return (access_flags & mask) != 0; } // Returns true if the method is declared synchronized. @@ -143,7 +147,7 @@ class MANAGED ArtMethod FINAL : public Object { return (GetAccessFlags() & kAccSynthetic) != 0; } - bool IsProxyMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + bool IsProxyMethod(bool ignore_xposed = false) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); bool IsPreverified() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return (GetAccessFlags() & kAccPreverified) != 0; From 6523b896086d3d1567dd6c0c7540b7434096ba79 Mon Sep 17 00:00:00 2001 From: Daniel Mihalyi Date: Fri, 10 Oct 2014 18:24:11 +0200 Subject: [PATCH 104/150] Fixed ArtMethod::GetQuickFrameInfo() for proxy methods If we call ArtMethod::GetQuickFrameInfo() on a proxy constructor when instrumentation stubs are installed, the entry point returned by Instrumentation::GetQuickCodeFor() would be a artQuickProxyInvokeHandler, and not a regular quick oat code pointer. This patch adds special handling for proxy constructors to always compute frame info from the regular quick code entry point. (These entry points are never updated by instrumentation for proxy methods.) Change-Id: I8e51f5622b6d327037d5c968b07505b2bf4ac737 Signed-off-by: Daniel Mihalyi (cherry picked from commit e14f2b3047fd310c5df65cf13470aa2786522a3b) Conflicts: runtime/mirror/art_method-inl.h --- runtime/mirror/art_method-inl.h | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 1ab30660dc0..85e2d45812e 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -363,17 +363,32 @@ inline QuickMethodFrameInfo ArtMethod::GetQuickFrameInfo() { return QuickMethodFrameInfo(kStackAlignment, 0u, 0u); } Runtime* runtime = Runtime::Current(); - // For Proxy method we exclude direct method (there is only one direct method - constructor). - // Direct method is cloned from original java.lang.reflect.Proxy class together with code - // and as a result it is executed as usual quick compiled method without any stubs. - // So the frame info should be returned as it is a quick method not a stub. - if (UNLIKELY(IsAbstract()) || UNLIKELY(IsProxyMethod() && !IsDirect()) || UNLIKELY(IsXposedHookedMethod())) { + + if (UNLIKELY(IsAbstract() || IsXposedHookedMethod())) { return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); } + + // This goes before IsProxyMethod since runtime methods have a null declaring class. if (UNLIKELY(IsRuntimeMethod())) { return runtime->GetRuntimeMethodFrameInfo(this); } + // For Proxy method we add special handling for the direct method case (there is only one + // direct method - constructor). Direct method is cloned from original + // java.lang.reflect.Proxy class together with code and as a result it is executed as usual + // quick compiled method without any stubs. So the frame info should be returned as it is a + // quick method not a stub. However, if instrumentation stubs are installed, the + // instrumentation->GetQuickCodeFor() returns the artQuickProxyInvokeHandler instead of an + // oat code pointer, thus we have to add a special case here. + if (UNLIKELY(IsProxyMethod(true))) { + if (IsDirect()) { + CHECK(IsConstructor()); + return GetQuickFrameInfo(EntryPointToCodePointer(GetEntryPointFromQuickCompiledCode())); + } else { + return runtime->GetCalleeSaveMethodFrameInfo(Runtime::kRefsAndArgs); + } + } + const void* entry_point = runtime->GetInstrumentation()->GetQuickCodeFor(this, sizeof(void*)); // On failure, instead of nullptr we get the quick-generic-jni-trampoline for native method // indicating the generic JNI, or the quick-to-interpreter-bridge (but not the trampoline) From 97aa9e1d42126b109620a42decb947016453ebed Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 12 Jun 2015 19:05:01 +0200 Subject: [PATCH 105/150] [Xposed] Debugger fixes Debuggers need to know about all methods in a class. For methods hooked with Xposed, both the hooked (proxy-like) and the original method have to be returned. Also, the hooked method isn't directly attached to a code item anymore, which would otherwise be used to calculate offsets into the method code. Fixes #1. --- runtime/debugger.cc | 24 +++++++++++++++++++++++- runtime/mirror/art_method-inl.h | 3 +++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 2e23eb8bdad..9a566f3b1be 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1617,10 +1617,32 @@ JDWP::JdwpError Dbg::OutputDeclaredMethods(JDWP::RefTypeId class_id, bool with_g size_t direct_method_count = c->NumDirectMethods(); size_t virtual_method_count = c->NumVirtualMethods(); - expandBufAdd4BE(pReply, direct_method_count + virtual_method_count); + size_t xposed_method_count = 0; + for (size_t i = 0; i < direct_method_count + virtual_method_count; ++i) { + mirror::ArtMethod* m = (i < direct_method_count) ? c->GetDirectMethod(i) : c->GetVirtualMethod(i - direct_method_count); + if (UNLIKELY(m->IsXposedHookedMethod())) { + ++xposed_method_count; + } + } + + expandBufAdd4BE(pReply, direct_method_count + virtual_method_count + xposed_method_count); for (size_t i = 0; i < direct_method_count + virtual_method_count; ++i) { mirror::ArtMethod* m = (i < direct_method_count) ? c->GetDirectMethod(i) : c->GetVirtualMethod(i - direct_method_count); + + if (UNLIKELY(xposed_method_count > 0 && m->IsXposedHookedMethod())) { + expandBufAddMethodId(pReply, ToMethodId(m)); + expandBufAddUtf8String(pReply, m->GetName()); + expandBufAddUtf8String(pReply, m->GetSignature().ToString()); + if (with_generic) { + static const char genericSignature[1] = ""; + expandBufAddUtf8String(pReply, genericSignature); + } + expandBufAdd4BE(pReply, MangleAccessFlags(m->GetAccessFlags())); + + m = m->GetXposedOriginalMethod(); + } + expandBufAddMethodId(pReply, ToMethodId(m)); expandBufAddUtf8String(pReply, m->GetName()); expandBufAddUtf8String(pReply, m->GetSignature().ToString()); diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 85e2d45812e..52c17a15647 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -478,6 +478,9 @@ inline const char* ArtMethod::GetName() { } inline const DexFile::CodeItem* ArtMethod::GetCodeItem() { + if (UNLIKELY(IsXposedHookedMethod())) { + return nullptr; + } mirror::ArtMethod* method = GetInterfaceMethodIfProxy(); return method->GetDexFile()->GetCodeItem(method->GetCodeItemOffset()); } From 0e5d8942ae8eab4cc4366736243201c52af70b1a Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 12 Jun 2015 20:16:11 +0200 Subject: [PATCH 106/150] [Xposed] Mark methods hooked by Xposed in traces In order to facilitate debugging, append special strings to hooked methods: - Java stack trace - native stack trace - debugger - PrettyMethod() utility method --- runtime/debugger.cc | 2 +- runtime/mirror/art_method-inl.h | 3 +++ runtime/thread.cc | 3 +++ runtime/utils.cc | 5 +++++ 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/runtime/debugger.cc b/runtime/debugger.cc index 9a566f3b1be..22af7eeb703 100644 --- a/runtime/debugger.cc +++ b/runtime/debugger.cc @@ -1632,7 +1632,7 @@ JDWP::JdwpError Dbg::OutputDeclaredMethods(JDWP::RefTypeId class_id, bool with_g if (UNLIKELY(xposed_method_count > 0 && m->IsXposedHookedMethod())) { expandBufAddMethodId(pReply, ToMethodId(m)); - expandBufAddUtf8String(pReply, m->GetName()); + expandBufAddUtf8String(pReply, StringPrintf("%s", m->GetName()).c_str()); expandBufAddUtf8String(pReply, m->GetSignature().ToString()); if (with_generic) { static const char genericSignature[1] = ""; diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h index 52c17a15647..aaf9ca7bab6 100644 --- a/runtime/mirror/art_method-inl.h +++ b/runtime/mirror/art_method-inl.h @@ -513,6 +513,9 @@ inline const DexFile::TypeList* ArtMethod::GetParameterTypeList() { } inline const char* ArtMethod::GetDeclaringClassSourceFile() { + if (UNLIKELY(IsXposedHookedMethod())) { + return ""; + } return GetInterfaceMethodIfProxy()->GetDeclaringClass()->GetSourceFile(); } diff --git a/runtime/thread.cc b/runtime/thread.cc index 2f474f7ae13..49e5c2665c7 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -1595,6 +1595,9 @@ jobjectArray Thread::InternalStackTraceToStackTraceElementArray( line_number = -1; class_name_object.Assign(method->GetDeclaringClass()->GetName()); // source_name_object intentionally left null for proxy methods + if (method->IsXposedHookedMethod()) { + source_name_object.Assign(mirror::String::AllocFromModifiedUtf8(soa.Self(), "")); + } } else { mirror::IntArray* pc_trace = down_cast(method_trace->Get(depth)); uint32_t dex_pc = pc_trace->Get(i); diff --git a/runtime/utils.cc b/runtime/utils.cc index 5c726d40661..2c7f0402ed9 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -385,6 +385,11 @@ std::string PrettyMethod(mirror::ArtMethod* m, bool with_signature) { result = PrettyReturnType(sig_as_string.c_str()) + " " + result + PrettyArguments(sig_as_string.c_str()); } + if (UNLIKELY(m->IsXposedHookedMethod())) { + result += " [XposedHooked]"; + } else if (UNLIKELY(m->IsXposedOriginalMethod())) { + result += " [XposedOriginal]"; + } return result; } From 169b6aca962c01a0d49dab2278a8708972de47cb Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 12 Jun 2015 23:02:28 +0200 Subject: [PATCH 107/150] [Xposed] Enable hooks for native methods Fixes rovo89/Xposed#28. --- runtime/mirror/art_method.cc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index c592ded3e3b..e8446c324e0 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -354,6 +354,10 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_fast) { DCHECK(Thread::Current() == self); + if (UNLIKELY(IsXposedHookedMethod())) { + GetXposedOriginalMethod()->RegisterNative(self, native_method, is_fast); + return; + } CHECK(IsNative()) << PrettyMethod(this); CHECK(!IsFastNative()) << PrettyMethod(this); CHECK(native_method != NULL) << PrettyMethod(this); @@ -364,6 +368,10 @@ void ArtMethod::RegisterNative(Thread* self, const void* native_method, bool is_ } void ArtMethod::UnregisterNative(Thread* self) { + if (UNLIKELY(IsXposedHookedMethod())) { + GetXposedOriginalMethod()->UnregisterNative(self); + return; + } CHECK(IsNative() && !IsFastNative()) << PrettyMethod(this); // restore stub to lookup native pointer via dlsym RegisterNative(self, GetJniDlsymLookupStub(), false); @@ -376,10 +384,6 @@ void ArtMethod::EnableXposedHook(JNIEnv* env, jobject additional_info) { } else if (IsXposedOriginalMethod()) { // This should never happen XLOG(FATAL) << "You cannot hook the original method"; - } else if (IsNative()) { - // Hooking native methods should hopefully work fine, but needs testing - XLOG(INFO) << "Not hooking native method " << PrettyMethod(this); - return; } From 9b40b1a2f75c87abdf522340e16ec5a4d55245b6 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 12 Jun 2015 23:55:53 +0200 Subject: [PATCH 108/150] [Xposed] Minor (style) corrections Conflicts: runtime/mirror/art_method.cc runtime/mirror/art_method.h --- runtime/entrypoints/entrypoint_utils.cc | 2 +- runtime/mirror/art_method.cc | 9 ++++----- runtime/mirror/art_method.h | 5 +++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc index 6e5e6b83c9e..96b0b20a3ee 100644 --- a/runtime/entrypoints/entrypoint_utils.cc +++ b/runtime/entrypoints/entrypoint_utils.cc @@ -376,7 +376,7 @@ JValue InvokeXposedHandleHookedMethod(ScopedObjectAccessAlreadyRunnable& soa, co } } - XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo(); + const XposedHookInfo* hookInfo = soa.DecodeMethod(method)->GetXposedHookInfo(); // Call XposedBridge.handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj, // Object thisObject, Object[] args) diff --git a/runtime/mirror/art_method.cc b/runtime/mirror/art_method.cc index e8446c324e0..62516a4582e 100644 --- a/runtime/mirror/art_method.cc +++ b/runtime/mirror/art_method.cc @@ -378,20 +378,19 @@ void ArtMethod::UnregisterNative(Thread* self) { } void ArtMethod::EnableXposedHook(JNIEnv* env, jobject additional_info) { - if (IsXposedHookedMethod()) { + if (UNLIKELY(IsXposedHookedMethod())) { // Already hooked return; - } else if (IsXposedOriginalMethod()) { + } else if (UNLIKELY(IsXposedOriginalMethod())) { // This should never happen - XLOG(FATAL) << "You cannot hook the original method"; + ThrowIllegalArgumentException(nullptr, StringPrintf("Cannot hook the method backup: %s", PrettyMethod(this).c_str()).c_str()); + return; } - ScopedObjectAccess soa(env); // Create a backup of the ArtMethod object ArtMethod* backup_method = down_cast(Clone(soa.Self())); - // Set private flag to avoid virtual table lookups during invocation backup_method->SetAccessFlags(backup_method->GetAccessFlags() | kAccXposedOriginalMethod); // Create a Method/Constructor object for the backup ArtMethod object diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h index d73611ac7de..131daf80245 100644 --- a/runtime/mirror/art_method.h +++ b/runtime/mirror/art_method.h @@ -565,8 +565,9 @@ class MANAGED ArtMethod FINAL : public Object { void EnableXposedHook(JNIEnv* env, jobject additional_info) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - XposedHookInfo* GetXposedHookInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return reinterpret_cast(GetEntryPointFromJni()); + const XposedHookInfo* GetXposedHookInfo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(IsXposedHookedMethod()); + return reinterpret_cast(GetEntryPointFromJni()); } ArtMethod* GetXposedOriginalMethod() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { From e29f4d07f7ce45d5728f371781b424d7c8b5110f Mon Sep 17 00:00:00 2001 From: Scott Date: Fri, 26 Jun 2015 10:54:31 -0500 Subject: [PATCH 109/150] dex2oat: minor cleanup --- dex2oat/dex2oat.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 749aa52dbdd..793a1777012 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -507,7 +507,7 @@ class Dex2Oat { } void SetRuntimeRecompiling(bool new_value) { - runtime_->SetRecompiling(true); + runtime_->SetRecompiling(new_value); } private: @@ -611,7 +611,7 @@ static size_t OpenDexFiles(const std::vector& dex_filenames, continue; } if (EndsWith(dex_filename, ".oat") || EndsWith(dex_filename, ".odex")) { - const OatFile* oat_file = OatFile::Open(dex_filename, dex_location, nullptr, nullptr, false, &error_msg); + const OatFile* oat_file = OatFile::Open(dex_filename, dex_location, nullptr, nullptr, false, &error_msg); if (oat_file == nullptr) { LOG(WARNING) << "Failed to open oat file from '" << dex_filename << "': " << error_msg; ++failure_count; From 5f4bc91b6baacacc358fb875ce169a5bf20d6548 Mon Sep 17 00:00:00 2001 From: Moritz Horstmann Date: Sun, 28 Jun 2015 03:40:08 +0200 Subject: [PATCH 110/150] Fix registering of hooked native methods through AndroidRuntime::registerNativeMethods --- runtime/jni_internal.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index bdda5c87077..c9f9d0b384f 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -2413,7 +2413,7 @@ class JNI { << c->GetDexCache()->GetLocation()->ToModifiedUtf8(); ThrowNoSuchMethodError(soa, c, name, sig, "static or non-static"); return JNI_ERR; - } else if (!m->IsNative()) { + } else if (!m->IsNative() && !(m->IsXposedHookedMethod() && m->GetXposedOriginalMethod()->IsNative())) { LOG(return_errors ? ERROR : FATAL) << "Failed to register non-native method " << PrettyDescriptor(c) << "." << name << sig << " as native"; @@ -2438,14 +2438,14 @@ class JNI { size_t unregistered_count = 0; for (size_t i = 0; i < c->NumDirectMethods(); ++i) { mirror::ArtMethod* m = c->GetDirectMethod(i); - if (m->IsNative()) { + if (m->IsNative() || (m->IsXposedHookedMethod() && m->GetXposedOriginalMethod()->IsNative())) { m->UnregisterNative(soa.Self()); unregistered_count++; } } for (size_t i = 0; i < c->NumVirtualMethods(); ++i) { mirror::ArtMethod* m = c->GetVirtualMethod(i); - if (m->IsNative()) { + if (m->IsNative() || (m->IsXposedHookedMethod() && m->GetXposedOriginalMethod()->IsNative())) { m->UnregisterNative(soa.Self()); unregistered_count++; } From 837e87a14d9dac312a5fd1ed97ca0443973b375a Mon Sep 17 00:00:00 2001 From: arter97 Date: Wed, 20 May 2015 23:15:19 +0900 Subject: [PATCH 111/150] Implement String.clear() to the runtime Samsung Lollipop firmware has a weird java.lang.String.clear() function. ART needs to know about this to boot properly. Signed-off-by: arter97 --- runtime/mirror/string-inl.h | 2 +- runtime/mirror/string.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index d9241417854..85a26cc2244 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -29,7 +29,7 @@ namespace art { namespace mirror { inline uint32_t String::ClassSize() { - uint32_t vtable_entries = Object::kVTableLength + 51; + uint32_t vtable_entries = Object::kVTableLength + 52; return Class::ComputeClassSize(true, vtable_entries, 1, 1, 2); } diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index 631c19bcf5f..d833ec806d2 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -73,6 +73,8 @@ class MANAGED String FINAL : public Object { uint16_t CharAt(int32_t index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + String* Intern() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static String* AllocFromUtf16(Thread* self, From 380b6f910d88684fe702feaa162fd658a4495789 Mon Sep 17 00:00:00 2001 From: arter97 Date: Wed, 8 Jul 2015 01:49:36 +0900 Subject: [PATCH 112/150] Revert "[Xposed] Ignore unknown arguments and option values" This reverts commit c10f139b639d514b9e28a4977f1d2793a7a85aed. Signed-off-by: arter97 --- dex2oat/dex2oat.cc | 6 +++--- oatdump/oatdump.cc | 1 + patchoat/patchoat.cc | 2 +- runtime/parsed_options.cc | 16 ++++++++++------ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 52b9e672b35..793a1777012 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -778,7 +778,7 @@ static InstructionSetFeatures ParseFeatureList(std::string str) { // Turn off support for Large Physical Address Extension. result.SetHasLpae(false); } else { - LOG(WARNING) << StringPrintf("Unknown instruction set feature: '%s'", feature.c_str()); + Usage("Unknown instruction set feature: '%s'", feature.c_str()); } } // others... @@ -1132,7 +1132,7 @@ static int dex2oat(int argc, char** argv) { Usage("--swap-fd passed a negative value %d", swap_fd); } } else { - LOG(WARNING) << StringPrintf("Unknown argument %s", option.data()); + Usage("Unknown argument %s", option.data()); } } @@ -1266,7 +1266,7 @@ static int dex2oat(int argc, char** argv) { } else if (strcmp(compiler_filter_string, "everything") == 0) { compiler_filter = CompilerOptions::kEverything; } else { - LOG(WARNING) << StringPrintf("Unknown --compiler-filter value %s", compiler_filter_string); + Usage("Unknown --compiler-filter value %s", compiler_filter_string); } // Set the compilation target's implicit checks options. diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index a92ac20e809..b3fa2dae9e9 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1765,6 +1765,7 @@ static int oatdump(int argc, char** argv) { method_filter_ = option.substr(strlen("--method-filter=")).data(); } else { fprintf(stderr, "Unknown argument %s\n", option.data()); + usage(); } } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 9017a7e7044..73c31ebf24b 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -1201,7 +1201,7 @@ static int patchoat(int argc, char **argv) { } else if (option == "--no-dump-timings") { dump_timings = false; } else { - LOG(WARNING) << StringPrintf("Unknown argument %s", option.data()); + Usage("Unknown argument %s", option.data()); } } diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 5b9beab2e03..6fd88147484 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -21,7 +21,6 @@ #endif #include "base/stringpiece.h" -#include "base/stringprintf.h" #include "debugger.h" #include "gc/heap.h" #include "monitor.h" @@ -160,7 +159,8 @@ bool ParsedOptions::ParseXGcOption(const std::string& option) { (gc_option == "noverifycardtable")) { // Ignored for backwards compatibility. } else { - LOG(WARNING) << StringPrintf("Unknown -Xgc option %s\n", gc_option.c_str()); + Usage("Unknown -Xgc option %s\n", gc_option.c_str()); + return false; } } return true; @@ -465,7 +465,8 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize if (collector_type != gc::kCollectorTypeNone) { background_collector_type_ = collector_type; } else { - LOG(WARNING) << StringPrintf("Unknown -XX:BackgroundGC option %s\n", substring.c_str()); + Usage("Unknown -XX:BackgroundGC option %s\n", substring.c_str()); + return false; } } } else if (option == "-XX:+DisableExplicitGC") { @@ -501,7 +502,8 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize } else if (verbose_options[i] == "verifier") { gLogVerbosity.verifier = true; } else { - LOG(WARNING) << StringPrintf("Unknown -verbose option %s\n", verbose_options[i].c_str()); + Usage("Unknown -verbose option %s\n", verbose_options[i].c_str()); + return false; } } } else if (StartsWith(option, "-verbose-methods:")) { @@ -619,7 +621,8 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize } else if (verify_mode == "remote" || verify_mode == "all") { verify_ = true; } else { - LOG(WARNING) << StringPrintf("Unknown -Xverify option %s\n", verify_mode.c_str()); + Usage("Unknown -Xverify option %s\n", verify_mode.c_str()); + return false; } } else if (StartsWith(option, "-XX:NativeBridge=")) { if (!ParseStringAfterChar(option, '=', &native_bridge_library_filename_)) { @@ -662,7 +665,8 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize StartsWith(option, "-XX:mainThreadStackSize=")) { // Ignored for backwards compatibility. } else if (!ignore_unrecognized) { - LOG(WARNING) << StringPrintf("Unrecognized option %s\n", option.c_str()); + Usage("Unrecognized option %s\n", option.c_str()); + return false; } } // If not set, background collector type defaults to homogeneous compaction From ab00611b438a6788809acf8df44a53c4a777f424 Mon Sep 17 00:00:00 2001 From: arter97 Date: Wed, 20 May 2015 23:06:57 +0900 Subject: [PATCH 113/150] oattools: skip unknown arguments Samsung Lollipop firmware tends to pass more arguments than AOSP does. (e.g. --art-fd) So skip unknown arguments. Signed-off-by: arter97 Conflicts: dex2oat/dex2oat.cc --- dex2oat/dex2oat.cc | 2 -- oatdump/oatdump.cc | 3 --- patchoat/patchoat.cc | 2 -- 3 files changed, 7 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 793a1777012..88b4b5bf061 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1131,8 +1131,6 @@ static int dex2oat(int argc, char** argv) { if (swap_fd < 0) { Usage("--swap-fd passed a negative value %d", swap_fd); } - } else { - Usage("Unknown argument %s", option.data()); } } diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index b3fa2dae9e9..b76b7020a9e 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -1763,9 +1763,6 @@ static int oatdump(int argc, char** argv) { os = out.get(); } else if (option.starts_with("--method-filter=")) { method_filter_ = option.substr(strlen("--method-filter=")).data(); - } else { - fprintf(stderr, "Unknown argument %s\n", option.data()); - usage(); } } diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc index 73c31ebf24b..23d58b642c1 100644 --- a/patchoat/patchoat.cc +++ b/patchoat/patchoat.cc @@ -1200,8 +1200,6 @@ static int patchoat(int argc, char **argv) { dump_timings = true; } else if (option == "--no-dump-timings") { dump_timings = false; - } else { - Usage("Unknown argument %s", option.data()); } } From 8bf642a43dfe69dda476891f79dd6ac6844c46b0 Mon Sep 17 00:00:00 2001 From: arter97 Date: Tue, 26 May 2015 01:29:02 +0900 Subject: [PATCH 114/150] runtime: skip unknown arguments #2 Samsung Lollipop firmware tends to pass more arguments than AOSP does. (e.g. -XX:debugExplicitGc=0) So skip unknown arguments. This should fix boot issues with devices like Galaxy Alpha. Signed-off-by: arter97 --- dalvikvm/dalvikvm.cc | 1 - runtime/jni_internal.cc | 2 +- runtime/parsed_options.cc | 39 --------------------------------------- 3 files changed, 1 insertion(+), 41 deletions(-) diff --git a/dalvikvm/dalvikvm.cc b/dalvikvm/dalvikvm.cc index 67794c8e055..e31072b5295 100644 --- a/dalvikvm/dalvikvm.cc +++ b/dalvikvm/dalvikvm.cc @@ -168,7 +168,6 @@ static int dalvikvm(int argc, char** argv) { init_args.version = JNI_VERSION_1_6; init_args.options = options.get(); init_args.nOptions = curr_opt; - init_args.ignoreUnrecognized = JNI_FALSE; // Start the runtime. The current thread becomes the main thread. JavaVM* vm = NULL; diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 094b034a206..74b1ed89a80 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -3022,7 +3022,7 @@ extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { JavaVMOption* option = &args->options[i]; options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo)); } - bool ignore_unrecognized = args->ignoreUnrecognized; + bool ignore_unrecognized = true; if (!Runtime::Create(options, ignore_unrecognized)) { return JNI_ERR; } diff --git a/runtime/parsed_options.cc b/runtime/parsed_options.cc index 6fd88147484..b40d9c3584f 100644 --- a/runtime/parsed_options.cc +++ b/runtime/parsed_options.cc @@ -628,45 +628,6 @@ bool ParsedOptions::Parse(const RuntimeOptions& options, bool ignore_unrecognize if (!ParseStringAfterChar(option, '=', &native_bridge_library_filename_)) { return false; } - } else if (StartsWith(option, "-ea") || - StartsWith(option, "-da") || - StartsWith(option, "-enableassertions") || - StartsWith(option, "-disableassertions") || - (option == "--runtime-arg") || - (option == "-esa") || - (option == "-dsa") || - (option == "-enablesystemassertions") || - (option == "-disablesystemassertions") || - (option == "-Xrs") || - StartsWith(option, "-Xint:") || - StartsWith(option, "-Xdexopt:") || - (option == "-Xnoquithandler") || - StartsWith(option, "-Xjniopts:") || - StartsWith(option, "-Xjnigreflimit:") || - (option == "-Xgenregmap") || - (option == "-Xnogenregmap") || - StartsWith(option, "-Xverifyopt:") || - (option == "-Xcheckdexsum") || - (option == "-Xincludeselectedop") || - StartsWith(option, "-Xjitop:") || - (option == "-Xincludeselectedmethod") || - StartsWith(option, "-Xjitthreshold:") || - StartsWith(option, "-Xjitcodecachesize:") || - (option == "-Xjitblocking") || - StartsWith(option, "-Xjitmethod:") || - StartsWith(option, "-Xjitclass:") || - StartsWith(option, "-Xjitoffset:") || - StartsWith(option, "-Xjitconfig:") || - (option == "-Xjitcheckcg") || - (option == "-Xjitverbose") || - (option == "-Xjitprofile") || - (option == "-Xjitdisableopt") || - (option == "-Xjitsuspendpoll") || - StartsWith(option, "-XX:mainThreadStackSize=")) { - // Ignored for backwards compatibility. - } else if (!ignore_unrecognized) { - Usage("Unrecognized option %s\n", option.c_str()); - return false; } } // If not set, background collector type defaults to homogeneous compaction From 91b1a2717022816abf5f87c80c54d86ac9992b9a Mon Sep 17 00:00:00 2001 From: Mathieu Chartier Date: Thu, 8 Jan 2015 11:28:13 -0800 Subject: [PATCH 115/150] Add clamp growth limit Clamp growth limit shrinks the space memmaps to the current growth limit. This reduces virtual memory usage for apps with small heaps. Bug: 18387825 Bug: 17131630 Change-Id: I4a8fdc335d2c40492e991708adabcc46299efb7d Conflicts: runtime/mem_map.h --- runtime/gc/accounting/space_bitmap.h | 6 ++++++ runtime/gc/heap.cc | 14 ++++++++++++++ runtime/gc/heap.h | 6 +++++- runtime/gc/space/malloc_space.cc | 10 ++++++++++ runtime/gc/space/malloc_space.h | 4 ++++ runtime/mem_map.cc | 13 +++++++++++++ runtime/mem_map.h | 3 +++ runtime/native/dalvik_system_VMRuntime.cc | 5 +++++ 8 files changed, 60 insertions(+), 1 deletion(-) diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h index f72b30f81b7..73d18f6d62d 100644 --- a/runtime/gc/accounting/space_bitmap.h +++ b/runtime/gc/accounting/space_bitmap.h @@ -160,6 +160,12 @@ class SpaceBitmap { return IndexToOffset(Size() / kWordSize); } + void SetHeapSize(size_t bytes) { + // TODO: Un-map the end of the mem map. + bitmap_size_ = OffsetToIndex(bytes) * sizeof(intptr_t); + CHECK_EQ(HeapSize(), bytes); + } + uintptr_t HeapBegin() const { return heap_begin_; } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index db65a69280b..c267bffb1a2 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2997,6 +2997,20 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran) { } } +void Heap::ClampGrowthLimit() { + capacity_ = growth_limit_; + for (const auto& space : continuous_spaces_) { + if (space->IsMallocSpace()) { + gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); + malloc_space->ClampGrowthLimit(); + } + } + // This space isn't added for performance reasons. + if (main_space_backup_.get() != nullptr) { + main_space_backup_->ClampGrowthLimit(); + } +} + void Heap::ClearGrowthLimit() { growth_limit_ = capacity_; for (const auto& space : continuous_spaces_) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 530ec187e85..5d83efc8ab0 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -291,6 +291,10 @@ class Heap { // implement dalvik.system.VMRuntime.clearGrowthLimit. void ClearGrowthLimit(); + // Make the current growth limit the new maximum capacity, unmaps pages at the end of spaces + // which will never be used. Used to implement dalvik.system.VMRuntime.clampGrowthLimit. + void ClampGrowthLimit(); + // Target ideal heap utilization ratio, implements // dalvik.system.VMRuntime.getTargetHeapUtilization. double GetTargetHeapUtilization() const { @@ -873,7 +877,7 @@ class Heap { collector::GcType next_gc_type_; // Maximum size that the heap can reach. - const size_t capacity_; + size_t capacity_; // The size the heap is limited to. This is initially smaller than capacity, but for largeHeap // programs it is "cleared" making it the same as capacity. diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index ba7e5c1eca0..ee94214e8d9 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -247,6 +247,16 @@ void MallocSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* ar context->freed.bytes += space->FreeList(self, num_ptrs, ptrs); } +void MallocSpace::ClampGrowthLimit() { + size_t new_capacity = Capacity(); + CHECK_LE(new_capacity, NonGrowthLimitCapacity()); + GetLiveBitmap()->SetHeapSize(new_capacity); + GetMarkBitmap()->SetHeapSize(new_capacity); + GetMemMap()->SetSize(new_capacity); + limit_ = Begin() + new_capacity; + CHECK(temp_bitmap_.get() == nullptr); +} + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index bace3f6e634..74cd94508c3 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -110,6 +110,10 @@ class MallocSpace : public ContinuousMemMapAllocSpace { return GetMemMap()->Size(); } + // Change the non growth limit capacity by shrinking or expanding the map. Currently, only + // shrinking is supported. + void ClampGrowthLimit(); + void Dump(std::ostream& os) const; void SetGrowthLimit(size_t growth_limit); diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index bad86db2c2c..952b18ff1e4 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -659,6 +659,19 @@ void MemMap::Shutdown() { maps_ = nullptr; } +void MemMap::SetSize(size_t new_size) { + if (new_size == base_size_) { + return; + } + CHECK_ALIGNED(new_size, kPageSize); + CHECK_EQ(base_size_, size_) << "Unsupported"; + CHECK_LE(new_size, base_size_); + CHECK_EQ(munmap(reinterpret_cast(reinterpret_cast(BaseBegin()) + new_size), + base_size_ - new_size), 0) << new_size << " " << base_size_; + base_size_ = new_size; + size_ = new_size; +} + std::ostream& operator<<(std::ostream& os, const MemMap& mem_map) { os << StringPrintf("[MemMap: %p-%p prot=0x%x %s]", mem_map.BaseBegin(), mem_map.BaseEnd(), mem_map.GetProtect(), diff --git a/runtime/mem_map.h b/runtime/mem_map.h index ad62f83cb87..807291dc914 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -107,6 +107,9 @@ class MemMap { return size_; } + // Resize the mem-map by unmapping pages at the end. Currently only supports shrinking. + void SetSize(size_t new_size); + byte* End() const { return Begin() + Size(); } diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 9fbb9385d81..235e92008df 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -126,6 +126,10 @@ static void VMRuntime_clearGrowthLimit(JNIEnv*, jobject) { Runtime::Current()->GetHeap()->ClearGrowthLimit(); } +static void VMRuntime_clampGrowthLimit(JNIEnv*, jobject) { + Runtime::Current()->GetHeap()->ClampGrowthLimit(); +} + static jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) { return Dbg::IsDebuggerActive(); } @@ -547,6 +551,7 @@ static jstring VMRuntime_getCurrentInstructionSet(JNIEnv* env, jclass) { static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"), NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), + NATIVE_METHOD(VMRuntime, clampGrowthLimit, "()V"), NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"), NATIVE_METHOD(VMRuntime, concurrentGC, "()V"), From c4a9ec0423aa5d57e15eff654beef89927f8f0b6 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 26 Jun 2015 19:49:24 -0700 Subject: [PATCH 116/150] ART: Fix CFI annotation in arm64, x86 and x86-64 assembly To be able to unroll in the exception case, the state needs to be reset to before the jump. Bug: 22014525 Change-Id: Ic60400b5bf0efcb713c24df1728623d072f344ab --- runtime/arch/arm64/quick_entrypoints_arm64.S | 2 ++ runtime/arch/x86/quick_entrypoints_x86.S | 2 ++ runtime/arch/x86_64/quick_entrypoints_x86_64.S | 2 ++ 3 files changed, 6 insertions(+) diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index 3c5db50f92d..a0f30f052e7 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -1060,6 +1060,8 @@ ENTRY art_quick_check_cast .cfi_adjust_cfa_offset -32 ret + .cfi_adjust_cfa_offset 32 // Reset unwind info so following code unwinds. + .Lthrow_class_cast_exception: // Restore ldp x0, x1, [sp] diff --git a/runtime/arch/x86/quick_entrypoints_x86.S b/runtime/arch/x86/quick_entrypoints_x86.S index 6a10755d758..6d5b0402719 100644 --- a/runtime/arch/x86/quick_entrypoints_x86.S +++ b/runtime/arch/x86/quick_entrypoints_x86.S @@ -629,6 +629,8 @@ DEFINE_FUNCTION art_quick_check_cast addl LITERAL(12), %esp // pop arguments CFI_ADJUST_CFA_OFFSET(-12) ret + + CFI_ADJUST_CFA_OFFSET(12) // Reset unwind info so following code unwinds. 1: POP eax // pop arguments POP ecx diff --git a/runtime/arch/x86_64/quick_entrypoints_x86_64.S b/runtime/arch/x86_64/quick_entrypoints_x86_64.S index 0de8dfd8549..81183ef7230 100644 --- a/runtime/arch/x86_64/quick_entrypoints_x86_64.S +++ b/runtime/arch/x86_64/quick_entrypoints_x86_64.S @@ -930,6 +930,8 @@ DEFINE_FUNCTION art_quick_check_cast CFI_ADJUST_CFA_OFFSET(-16) ret + + CFI_ADJUST_CFA_OFFSET(16 + 4 * 8) // Reset unwind info so following code unwinds. 1: RESTORE_FP_CALLEE_SAVE_FRAME POP rsi // Pop arguments From 344448715e35f6b1e80ed23f85698337415f419d Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Wed, 27 May 2015 14:07:08 +0100 Subject: [PATCH 117/150] ART: Use kBadFprBase on mips64 and arm64. Fix incorrect usage of kBadGprBase to avoid misleading and ambiguous data in crash investigations. Bug: 21266656 Change-Id: I1de6cd621d770be586f8140e86833769865b42c6 --- runtime/arch/arm64/context_arm64.cc | 2 +- runtime/arch/mips/context_mips.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/arch/arm64/context_arm64.cc b/runtime/arch/arm64/context_arm64.cc index 3eb92c85565..0fd8a051c19 100644 --- a/runtime/arch/arm64/context_arm64.cc +++ b/runtime/arch/arm64/context_arm64.cc @@ -154,7 +154,7 @@ void Arm64Context::DoLongJump() { gprs[i] = gprs_[i] != nullptr ? *gprs_[i] : Arm64Context::kBadGprBase + i; } for (size_t i = 0; i < kNumberOfDRegisters; ++i) { - fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : Arm64Context::kBadGprBase + i; + fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : Arm64Context::kBadFprBase + i; } DCHECK_EQ(reinterpret_cast(Thread::Current()), gprs[TR]); art_quick_do_long_jump(gprs, fprs); diff --git a/runtime/arch/mips/context_mips.cc b/runtime/arch/mips/context_mips.cc index 789dbbb6d7d..ee472c4e458 100644 --- a/runtime/arch/mips/context_mips.cc +++ b/runtime/arch/mips/context_mips.cc @@ -108,7 +108,7 @@ void MipsContext::DoLongJump() { gprs[i] = gprs_[i] != nullptr ? *gprs_[i] : MipsContext::kBadGprBase + i; } for (size_t i = 0; i < kNumberOfFRegisters; ++i) { - fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : MipsContext::kBadGprBase + i; + fprs[i] = fprs_[i] != nullptr ? *fprs_[i] : MipsContext::kBadFprBase + i; } art_quick_do_long_jump(gprs, fprs); } From 2a2dec1e6abfd64b1d395d24c579e3689f770cbb Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 5 May 2015 20:03:50 +0200 Subject: [PATCH 118/150] Fix instrumentation exit stub for arm64 Fixes bad offset to restore register x21. Bug: 20798393 (cherry picked from commit a538effb3f848bfec232c55f167b9f35b416948f) Change-Id: I030d848ab0f46043e86f53abae204463f0aaccbe --- runtime/arch/arm64/quick_entrypoints_arm64.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/arch/arm64/quick_entrypoints_arm64.S b/runtime/arch/arm64/quick_entrypoints_arm64.S index a0f30f052e7..300a6d63707 100644 --- a/runtime/arch/arm64/quick_entrypoints_arm64.S +++ b/runtime/arch/arm64/quick_entrypoints_arm64.S @@ -174,7 +174,7 @@ // Restore xSELF as it might be scratched. mov xSELF, xETR // ETR - ldr xETR, [sp, #16] + ldr xETR, [sp, #32] .cfi_restore x21 add sp, sp, #96 From de3f7f406654e9c1e301d39f82df6f6f320d6a49 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Fri, 30 Jan 2015 15:37:34 -0800 Subject: [PATCH 119/150] Disable clang optimization to boot up on arm64. On arm64 devices, clang compiled lib64/libart.so repeatedly crashes during boot up stage. That prevents a stable adb connection. When optimization of BitVector::NumSetBits is disabled, we can boot up to Android home screen, although some Apps are still unstable. This is a temporary workaround only for clang/llvm arm64 target, to enable concurrent debugging on other bugs. BUG: 19180814 Change-Id: Id82497ce4be0b2c30d36679d15394848d826f50c Conflicts: runtime/base/bit_vector.cc --- runtime/base/bit_vector.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc index 1b9022e1705..69abdece757 100644 --- a/runtime/base/bit_vector.cc +++ b/runtime/base/bit_vector.cc @@ -380,6 +380,10 @@ bool BitVector::IsBitSet(const uint32_t* storage, uint32_t num) { return (val != 0); } +#if defined(__clang__) && defined(__ARM_64BIT_STATE) +// b/19180814 When POPCOUNT is inlined, boot up failed on arm64 devices. +__attribute__((optnone)) +#endif uint32_t BitVector::NumSetBits(const uint32_t* storage, uint32_t end) { uint32_t word_end = end >> 5; uint32_t partial_word_bits = end & 0x1f; From 77f8a14fbf29298727ca0b9448cdc76a00e4e95d Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Tue, 20 Jan 2015 18:06:47 -0800 Subject: [PATCH 120/150] ART: Fix arm64 backend Fix a register size problem after previous commit f681570077563bb529a30f9e7c572b837cecfb83. Change-Id: If04e647324bcd6fe279c25e70214a9f7c5b816ec --- compiler/dex/quick/arm64/utility_arm64.cc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/dex/quick/arm64/utility_arm64.cc b/compiler/dex/quick/arm64/utility_arm64.cc index 070175ac9f7..ab3ad03af41 100644 --- a/compiler/dex/quick/arm64/utility_arm64.cc +++ b/compiler/dex/quick/arm64/utility_arm64.cc @@ -1259,7 +1259,9 @@ LIR* Arm64Mir2Lir::LoadBaseDispBody(RegStorage r_base, int displacement, RegStor // TODO: cleaner support for index/displacement registers? Not a reference, but must match width. RegStorage r_scratch = AllocTempWide(); LoadConstantWide(r_scratch, displacement); - load = LoadBaseIndexed(r_base, r_scratch, r_dest, 0, size); + load = LoadBaseIndexed(r_base, r_scratch, + (size == kReference) ? As64BitReg(r_dest) : r_dest, + 0, size); FreeTemp(r_scratch); } @@ -1350,7 +1352,9 @@ LIR* Arm64Mir2Lir::StoreBaseDispBody(RegStorage r_base, int displacement, RegSto // Use long sequence. RegStorage r_scratch = AllocTempWide(); LoadConstantWide(r_scratch, displacement); - store = StoreBaseIndexed(r_base, r_scratch, r_src, 0, size); + store = StoreBaseIndexed(r_base, r_scratch, + (size == kReference) ? As64BitReg(r_src) : r_src, + 0, size); FreeTemp(r_scratch); } From b4c17e75f6ce0f55b924cb19ca0ad1f933dc926e Mon Sep 17 00:00:00 2001 From: Vladimir Marko Date: Fri, 7 Nov 2014 16:11:00 +0000 Subject: [PATCH 121/150] Quick: Fix arm64 AGET/APUT to use 32-bit index. Change-Id: Iaa230024c934fc0ff7e45dcbebeb6c0e94e1af63 Conflicts: compiler/dex/quick/arm64/int_arm64.cc --- compiler/dex/quick/arm64/int_arm64.cc | 81 ++++++++------------------- 1 file changed, 24 insertions(+), 57 deletions(-) diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index 1e97a3246c2..5a3f6317f4b 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -1116,11 +1116,6 @@ void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); } - // If index is constant, just fold it into the data offset - if (constant_index) { - data_offset += mir_graph_->ConstantValue(rl_index) << scale; - } - /* null object? */ GenNullCheck(rl_array.reg, opt_flags); @@ -1134,43 +1129,23 @@ void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, } else { ForceImplicitNullCheck(rl_array.reg, opt_flags); } - if (rl_dest.wide || rl_dest.fp || constant_index) { - RegStorage reg_ptr; - if (constant_index) { - reg_ptr = rl_array.reg; // NOTE: must not alter reg_ptr in constant case. - } else { - // No special indexed operation, lea + load w/ displacement - reg_ptr = AllocTempRef(); - OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.reg, As64BitReg(rl_index.reg), - EncodeShift(kA64Lsl, scale)); - FreeTemp(rl_index.reg); - } + if (constant_index) { rl_result = EvalLoc(rl_dest, reg_class, true); if (needs_range_check) { - if (constant_index) { - GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len); - } else { - GenArrayBoundsCheck(rl_index.reg, reg_len); - } + GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len); FreeTemp(reg_len); } + // Fold the constant index into the data offset. + data_offset += mir_graph_->ConstantValue(rl_index) << scale; if (rl_result.ref) { - LoadRefDisp(reg_ptr, data_offset, rl_result.reg, kNotVolatile); + LoadRefDisp(rl_array.reg, data_offset, rl_result.reg, kNotVolatile); } else { - LoadBaseDisp(reg_ptr, data_offset, rl_result.reg, size, kNotVolatile); + LoadBaseDisp(rl_array.reg, data_offset, rl_result.reg, size, kNotVolatile); } MarkPossibleNullPointerException(opt_flags); - if (!constant_index) { - FreeTemp(reg_ptr); - } - if (rl_dest.wide) { - StoreValueWide(rl_dest, rl_result); - } else { - StoreValue(rl_dest, rl_result); - } } else { - // Offset base, then use indexed load + // Offset base, then use indexed load. RegStorage reg_ptr = AllocTempRef(); OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset); FreeTemp(rl_array.reg); @@ -1181,12 +1156,16 @@ void Arm64Mir2Lir::GenArrayGet(int opt_flags, OpSize size, RegLocation rl_array, FreeTemp(reg_len); } if (rl_result.ref) { - LoadRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale); + LoadRefIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale); } else { - LoadBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_result.reg, scale, size); + LoadBaseIndexed(reg_ptr, rl_index.reg, rl_result.reg, scale, size); } MarkPossibleNullPointerException(opt_flags); FreeTemp(reg_ptr); + } + if (rl_dest.wide) { + StoreValueWide(rl_dest, rl_result); + } else { StoreValue(rl_dest, rl_result); } } @@ -1208,11 +1187,6 @@ void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, data_offset = mirror::Array::DataOffset(sizeof(int32_t)).Int32Value(); } - // If index is constant, just fold it into the data offset. - if (constant_index) { - data_offset += mir_graph_->ConstantValue(rl_index) << scale; - } - rl_array = LoadValue(rl_array, kRefReg); if (!constant_index) { rl_index = LoadValue(rl_index, kCoreReg); @@ -1245,24 +1219,18 @@ void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, ForceImplicitNullCheck(rl_array.reg, opt_flags); } /* at this point, reg_ptr points to array, 2 live temps */ - if (rl_src.wide || rl_src.fp || constant_index) { - if (rl_src.wide) { - rl_src = LoadValueWide(rl_src, reg_class); - } else { - rl_src = LoadValue(rl_src, reg_class); - } - if (!constant_index) { - OpRegRegRegShift(kOpAdd, reg_ptr, rl_array.reg, As64BitReg(rl_index.reg), - EncodeShift(kA64Lsl, scale)); - } + if (rl_src.wide) { + rl_src = LoadValueWide(rl_src, reg_class); + } else { + rl_src = LoadValue(rl_src, reg_class); + } + if (constant_index) { if (needs_range_check) { - if (constant_index) { - GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len); - } else { - GenArrayBoundsCheck(rl_index.reg, reg_len); - } + GenArrayBoundsCheck(mir_graph_->ConstantValue(rl_index), reg_len); FreeTemp(reg_len); } + // Fold the constant index into the data offset. + data_offset += mir_graph_->ConstantValue(rl_index) << scale; if (rl_src.ref) { StoreRefDisp(reg_ptr, data_offset, rl_src.reg, kNotVolatile); } else { @@ -1272,15 +1240,14 @@ void Arm64Mir2Lir::GenArrayPut(int opt_flags, OpSize size, RegLocation rl_array, } else { /* reg_ptr -> array data */ OpRegRegImm(kOpAdd, reg_ptr, rl_array.reg, data_offset); - rl_src = LoadValue(rl_src, reg_class); if (needs_range_check) { GenArrayBoundsCheck(rl_index.reg, reg_len); FreeTemp(reg_len); } if (rl_src.ref) { - StoreRefIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale); + StoreRefIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale); } else { - StoreBaseIndexed(reg_ptr, As64BitReg(rl_index.reg), rl_src.reg, scale, size); + StoreBaseIndexed(reg_ptr, rl_index.reg, rl_src.reg, scale, size); } MarkPossibleNullPointerException(opt_flags); } From d07021b38087ca0e385ebf46dd1b78239ed702b2 Mon Sep 17 00:00:00 2001 From: Matteo Franchin Date: Mon, 27 Oct 2014 13:29:30 +0000 Subject: [PATCH 122/150] AArch64: Addressing Cortex-A53 erratum 835769. Some early revisions of the Cortex-A53 have an erratum (835769) whereby it is possible for a 64-bit multiply-accumulate instruction in AArch64 state to generate an incorrect result. The conditions which a portion of code must satisfy in order for the issue to be observed are somewhat complex, but all cases end with a memory (load, store, or prefetch) instruction followed immediately by the multiply-accumulate operation. This commit makes sure to insert a nop instruction before a 64-bit msub instruction, whenever the latter is preceded by a memory instruction. This behaviour should make it impossible for the Arm64 backend to generate a sequence of instructions which matches the erratum conditions. Conflicts: compiler/dex/compiler_enums.h Change-Id: I115e509fbc7e9e1c3ddbc061d29a32d99f6bddb7 --- compiler/dex/compiler_enums.h | 1 + compiler/dex/quick/arm64/arm64_lir.h | 3 +- compiler/dex/quick/arm64/assemble_arm64.cc | 42 ++++++++++++++++++---- compiler/dex/quick/arm64/int_arm64.cc | 5 ++- dex2oat/dex2oat.cc | 6 ++++ runtime/base/macros.h | 2 +- runtime/instruction_set.h | 9 +++++ 7 files changed, 56 insertions(+), 12 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 62be1f3fc7d..daff79d8388 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -539,6 +539,7 @@ enum FixupKind { kFixupMovImmLST, // kThumb2MovImm16LST. kFixupMovImmHST, // kThumb2MovImm16HST. kFixupAlign4, // Align to 4-byte boundary. + kFixupA53Erratum835769, // Cortex A53 Erratum 835769. }; std::ostream& operator<<(std::ostream& os, const FixupKind& kind); diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h index 83d0590e32a..ff4543de976 100644 --- a/compiler/dex/quick/arm64/arm64_lir.h +++ b/compiler/dex/quick/arm64/arm64_lir.h @@ -320,6 +320,7 @@ enum ArmOpcode { kA64Madd4rrrr, // madd[s0011011000] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. kA64Msub4rrrr, // msub[s0011011000] rm[20-16] [1] ra[14-10] rn[9-5] rd[4-0]. kA64Neg3rro, // neg alias of "sub arg0, rzr, arg1, arg2". + kA64Nop0, // nop alias of "hint #0" [11010101000000110010000000011111]. kA64Orr3Rrl, // orr [s01100100] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0]. kA64Orr4rrro, // orr [s0101010] shift[23-22] [0] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0]. kA64Ret, // ret [11010110010111110000001111000000]. @@ -332,7 +333,7 @@ enum ArmOpcode { kA64Scvtf2fw, // scvtf [000111100s100010000000] rn[9-5] rd[4-0]. kA64Scvtf2fx, // scvtf [100111100s100010000000] rn[9-5] rd[4-0]. kA64Sdiv3rrr, // sdiv[s0011010110] rm[20-16] [000011] rn[9-5] rd[4-0]. - kA64Smaddl4xwwx, // smaddl [10011011001] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. + kA64Smull3xww, // smull [10011011001] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Smulh3xxx, // smulh [10011011010] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Stp4ffXD, // stp [0s10110100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. kA64Stp4rrXD, // stp [s010100100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index 0898f7fa413..39bd858cca4 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "arm64_lir.h" #include "codegen_arm64.h" + +#include "arm64_lir.h" #include "dex/quick/mir_to_lir-inl.h" namespace art { @@ -467,13 +468,17 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, "madd", "!0r, !1r, !3r, !2r", kFixupNone), ENCODING_MAP(WIDE(kA64Msub4rrrr), SF_VARIANTS(0x1b008000), - kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 14, 10, - kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, - "msub", "!0r, !1r, !3r, !2r", kFixupNone), + kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, + kFmtRegR, 14, 10, IS_QUAD_OP | REG_DEF0_USE123 | NEEDS_FIXUP, + "msub", "!0r, !1r, !2r, !3r", kFixupA53Erratum835769), ENCODING_MAP(WIDE(kA64Neg3rro), SF_VARIANTS(0x4b0003e0), kFmtRegR, 4, 0, kFmtRegR, 20, 16, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "neg", "!0r, !1r!2o", kFixupNone), + ENCODING_MAP(kA64Nop0, NO_VARIANTS(0xd503201f), + kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, NO_OPERAND, + "nop", "", kFixupNone), ENCODING_MAP(WIDE(kA64Orr3Rrl), SF_VARIANTS(0x32000000), kFmtRegROrSp, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 22, 10, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, @@ -522,10 +527,10 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "sdiv", "!0r, !1r, !2r", kFixupNone), - ENCODING_MAP(WIDE(kA64Smaddl4xwwx), NO_VARIANTS(0x9b200000), + ENCODING_MAP(kA64Smull3xww, NO_VARIANTS(0x9b207c00), kFmtRegX, 4, 0, kFmtRegW, 9, 5, kFmtRegW, 20, 16, - kFmtRegX, 14, 10, IS_QUAD_OP | REG_DEF0_USE123, - "smaddl", "!0x, !1w, !2w, !3x", kFixupNone), + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "smull", "!0x, !1w, !2w", kFixupNone), ENCODING_MAP(kA64Smulh3xxx, NO_VARIANTS(0x9b407c00), kFmtRegX, 4, 0, kFmtRegX, 9, 5, kFmtRegX, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, @@ -920,6 +925,29 @@ void Arm64Mir2Lir::AssembleLIR() { lir->operands[1] = delta; break; } + case kFixupA53Erratum835769: + // Avoid emitting code that could trigger Cortex A53's erratum 835769. + // This fixup should be carried out for all multiply-accumulate instructions: madd, msub, + // smaddl, smsubl, umaddl and umsubl. + if (cu_->GetInstructionSetFeatures().NeedFix835769()) { + // Check that this is a 64-bit multiply-accumulate. + if (IS_WIDE(lir->opcode)) { + uint64_t prev_insn_flags = EncodingMap[UNWIDE(lir->prev->opcode)].flags; + // Check that the instruction preceding the multiply-accumulate is a load or store. + if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) { + // insert a NOP between the load/store and the multiply-accumulate. + LIR* new_lir = RawLIR(lir->dalvik_offset, kA64Nop0, 0, 0, 0, 0, 0, NULL); + new_lir->offset = lir->offset; + new_lir->flags.fixup = kFixupNone; + new_lir->flags.size = EncodingMap[kA64Nop0].size; + InsertLIRBefore(lir, new_lir); + lir->offset += new_lir->flags.size; + offset_adjustment += new_lir->flags.size; + res = kRetryAll; + } + } + } + break; default: LOG(FATAL) << "Unexpected case: opcode: " << lir->opcode << ", fixup: " << lir->flags.fixup; } diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index 5a3f6317f4b..0d3e38ad3e4 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -419,8 +419,7 @@ bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_d rl_src = LoadValue(rl_src, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); RegStorage r_long_mul = AllocTemp(); - NewLIR4(kA64Smaddl4xwwx, As64BitReg(r_long_mul).GetReg(), - r_magic.GetReg(), rl_src.reg.GetReg(), rxzr); + NewLIR3(kA64Smull3xww, As64BitReg(r_long_mul).GetReg(), r_magic.GetReg(), rl_src.reg.GetReg()); switch (pattern) { case Divide3: OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32); @@ -635,7 +634,7 @@ RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegS } OpRegRegReg(kOpDiv, temp, r_src1, r_src2); NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(), - r_src1.GetReg(), r_src2.GetReg()); + r_src2.GetReg(), r_src1.GetReg()); FreeTemp(temp); } return rl_result; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 88b4b5bf061..19f024db772 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -777,6 +777,12 @@ static InstructionSetFeatures ParseFeatureList(std::string str) { } else if (feature == "nolpae") { // Turn off support for Large Physical Address Extension. result.SetHasLpae(false); + } else if (feature == "needfix_835769") { + // need fix CortexA53 errata 835769 + result.SetFix835769(true); + } else if (feature == "noneedfix_835769") { + // no need fix CortexA53 errata 835769 + result.SetFix835769(false); } else { Usage("Unknown instruction set feature: '%s'", feature.c_str()); } diff --git a/runtime/base/macros.h b/runtime/base/macros.h index fae9271d9e4..b1eab92f0c0 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -151,7 +151,7 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #define UNLIKELY(x) __builtin_expect((x), false) // Stringify the argument. -#define QUOTE(x) #x +#define QUOTE(x...) #x #define STRINGIFY(x) QUOTE(x) #ifndef NDEBUG diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h index da7d153c08d..257c3d38e19 100644 --- a/runtime/instruction_set.h +++ b/runtime/instruction_set.h @@ -180,6 +180,7 @@ size_t GetStackOverflowReservedBytes(InstructionSet isa); enum InstructionFeatures { kHwDiv = 0x1, // Supports hardware divide. kHwLpae = 0x2, // Supports Large Physical Address Extension. + kFix835769 = 0x4, // need fix CortexA53 errata 835769 }; // This is a bitmask of supported features per architecture. @@ -206,6 +207,14 @@ class PACKED(4) InstructionSetFeatures { mask_ = (mask_ & ~kHwLpae) | (v ? kHwLpae : 0); } + bool NeedFix835769() const { + return (mask_ & kFix835769) != 0; + } + + void SetFix835769(bool v) { + mask_ = (mask_ & ~kFix835769) | (v ? kFix835769 : 0); + } + std::string GetFeatureString() const; // Other features in here. From c5d9beb95d2c392820048ca42f582e4a50a9ca4d Mon Sep 17 00:00:00 2001 From: Ningsheng Jian Date: Wed, 10 Dec 2014 15:13:22 +0800 Subject: [PATCH 123/150] AArch64: Fix incorrect use of preceding LIR. In Cortex-A53 erratum 835769 fixup, we should check the LIR for previous emitted instruction, skipping pseudo LIRs and other non-emitting LIRs. Change-Id: I908f4c638650d7ad9c91112b74095bba2d81612e --- compiler/dex/quick/arm64/assemble_arm64.cc | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index 39bd858cca4..72236de08f4 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -833,6 +833,20 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { // are better set directly from the code (they will require no more than 2 instructions). #define ALIGNED_DATA_OFFSET(offset) (((offset) + 0x7) & ~0x7) +/* + * Get the LIR which emits the instruction preceding the given LIR. + * Returns nullptr, if no previous emitting insn found. + */ +static LIR* GetPrevEmittingLIR(LIR* lir) { + DCHECK(lir != nullptr); + LIR* prev_lir = lir->prev; + while ((prev_lir != nullptr) && + (prev_lir->flags.is_nop || Mir2Lir::IsPseudoLirOp(prev_lir->opcode))) { + prev_lir = prev_lir->prev; + } + return prev_lir; +} + // Assemble the LIR into binary instruction format. void Arm64Mir2Lir::AssembleLIR() { LIR* lir; @@ -932,7 +946,11 @@ void Arm64Mir2Lir::AssembleLIR() { if (cu_->GetInstructionSetFeatures().NeedFix835769()) { // Check that this is a 64-bit multiply-accumulate. if (IS_WIDE(lir->opcode)) { - uint64_t prev_insn_flags = EncodingMap[UNWIDE(lir->prev->opcode)].flags; + LIR* prev_insn = GetPrevEmittingLIR(lir); + if (prev_insn == nullptr) { + break; + } + uint64_t prev_insn_flags = EncodingMap[UNWIDE(prev_insn->opcode)].flags; // Check that the instruction preceding the multiply-accumulate is a load or store. if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) { // insert a NOP between the load/store and the multiply-accumulate. From cf4003c51f938b2db01c83c58d74615449081133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernhard=20Rosenkr=C3=A4nzer?= Date: Mon, 24 Nov 2014 09:05:27 +0100 Subject: [PATCH 124/150] Force building art with clang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Force building art with clang to work around gcc 4.9 bug Change-Id: I75ee984aa4d9f8a08a5fbc87f70add51fc8675a8 Signed-off-by: Bernhard Rosenkränzer Signed-off-by: arter97 --- build/Android.common_build.mk | 12 ++++++------ build/Android.executable.mk | 4 +++- compiler/Android.mk | 1 + dex2oat/Android.mk | 3 +++ runtime/Android.mk | 2 ++ 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 65b36ef74e9..06afc9a590a 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -116,12 +116,12 @@ ifneq ($(WITHOUT_HOST_CLANG),true) endif # Clang on the target. Target builds use GCC by default. -ART_TARGET_CLANG := -ART_TARGET_CLANG_arm := -ART_TARGET_CLANG_arm64 := -ART_TARGET_CLANG_mips := -ART_TARGET_CLANG_x86 := -ART_TARGET_CLANG_x86_64 := +ART_TARGET_CLANG := true +ART_TARGET_CLANG_arm := true +ART_TARGET_CLANG_arm64 := true +ART_TARGET_CLANG_mips := true +ART_TARGET_CLANG_x86 := true +ART_TARGET_CLANG_x86_64 := true define set-target-local-clang-vars LOCAL_CLANG := $(ART_TARGET_CLANG) diff --git a/build/Android.executable.mk b/build/Android.executable.mk index 6fd6b2bfff7..9beeb00ea11 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -19,7 +19,7 @@ include art/build/Android.common_build.mk ART_HOST_EXECUTABLES ?= ART_TARGET_EXECUTABLES ?= -ART_EXECUTABLES_CFLAGS := +ART_EXECUTABLES_CFLAGS := -Wframe-larger-than=4096 ifeq ($(ART_USE_PORTABLE_COMPILER),true) ART_EXECUTABLES_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif @@ -66,6 +66,7 @@ define build-art-executable else #debug LOCAL_MODULE := $$(art_executable)d endif + LOCAL_CLANG := true LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS) # Mac OS linker doesn't understand --export-dynamic/--version-script. @@ -101,6 +102,7 @@ define build-art-executable LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH) endif LOCAL_MULTILIB := $$(art_multilib) + LOCAL_CFLAGS += -Wframe-larger-than=4096 include external/libcxx/libcxx.mk ifeq ($$(art_target_or_host),target) diff --git a/compiler/Android.mk b/compiler/Android.mk index 350d5a962a1..b1663a2d151 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -194,6 +194,7 @@ define build-libart-compiler LOCAL_SHARED_LIBRARIES += libartd endif + LOCAL_CLANG := true LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := SHARED_LIBRARIES diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk index c24f6d6a3b1..12edecc00a9 100644 --- a/dex2oat/Android.mk +++ b/dex2oat/Android.mk @@ -29,6 +29,9 @@ else dex2oat_arch := 32 endif +LOCAL_CLANG := true +LOCAL_CFLAGS := -Wframe-larger-than=4096 + ifeq ($(ART_BUILD_TARGET_NDEBUG),true) $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler,art/compiler,target,ndebug,$(dex2oat_arch))) endif diff --git a/runtime/Android.mk b/runtime/Android.mk index d31fa5cfd18..889f8d1bd23 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -357,6 +357,7 @@ define build-libart LOCAL_MODULE := libartd endif + LOCAL_CLANG := true LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := SHARED_LIBRARIES @@ -460,6 +461,7 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT # produce meaningful name resolution. LOCAL_STRIP_MODULE := keep_symbols endif + LOCAL_CLANG := true include $$(BUILD_SHARED_LIBRARY) else # host include $$(BUILD_HOST_SHARED_LIBRARY) From 913010c500e9c3ac9d348a19d5a03694ae08a408 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Sun, 12 Jul 2015 17:13:50 -0700 Subject: [PATCH 125/150] Revert "Revert "AArch64: Fix incorrect use of preceding LIR."" * Restore A53 workarounds. We were missing the rest of the series. This reverts commit be627376eb8dd5a07414e966cf24ba94030cff96. Change-Id: I6bef49b5381047fa381c77031d883bba3dae6d9f --- compiler/dex/compiler_enums.h | 1 + compiler/dex/quick/arm64/arm64_lir.h | 3 +- compiler/dex/quick/arm64/assemble_arm64.cc | 60 +++++++++++++++++++--- compiler/dex/quick/arm64/int_arm64.cc | 5 +- dex2oat/dex2oat.cc | 6 +++ runtime/base/macros.h | 2 +- runtime/instruction_set.h | 9 ++++ 7 files changed, 74 insertions(+), 12 deletions(-) diff --git a/compiler/dex/compiler_enums.h b/compiler/dex/compiler_enums.h index 62be1f3fc7d..daff79d8388 100644 --- a/compiler/dex/compiler_enums.h +++ b/compiler/dex/compiler_enums.h @@ -539,6 +539,7 @@ enum FixupKind { kFixupMovImmLST, // kThumb2MovImm16LST. kFixupMovImmHST, // kThumb2MovImm16HST. kFixupAlign4, // Align to 4-byte boundary. + kFixupA53Erratum835769, // Cortex A53 Erratum 835769. }; std::ostream& operator<<(std::ostream& os, const FixupKind& kind); diff --git a/compiler/dex/quick/arm64/arm64_lir.h b/compiler/dex/quick/arm64/arm64_lir.h index 83d0590e32a..ff4543de976 100644 --- a/compiler/dex/quick/arm64/arm64_lir.h +++ b/compiler/dex/quick/arm64/arm64_lir.h @@ -320,6 +320,7 @@ enum ArmOpcode { kA64Madd4rrrr, // madd[s0011011000] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. kA64Msub4rrrr, // msub[s0011011000] rm[20-16] [1] ra[14-10] rn[9-5] rd[4-0]. kA64Neg3rro, // neg alias of "sub arg0, rzr, arg1, arg2". + kA64Nop0, // nop alias of "hint #0" [11010101000000110010000000011111]. kA64Orr3Rrl, // orr [s01100100] N[22] imm_r[21-16] imm_s[15-10] rn[9-5] rd[4-0]. kA64Orr4rrro, // orr [s0101010] shift[23-22] [0] rm[20-16] imm_6[15-10] rn[9-5] rd[4-0]. kA64Ret, // ret [11010110010111110000001111000000]. @@ -332,7 +333,7 @@ enum ArmOpcode { kA64Scvtf2fw, // scvtf [000111100s100010000000] rn[9-5] rd[4-0]. kA64Scvtf2fx, // scvtf [100111100s100010000000] rn[9-5] rd[4-0]. kA64Sdiv3rrr, // sdiv[s0011010110] rm[20-16] [000011] rn[9-5] rd[4-0]. - kA64Smaddl4xwwx, // smaddl [10011011001] rm[20-16] [0] ra[14-10] rn[9-5] rd[4-0]. + kA64Smull3xww, // smull [10011011001] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Smulh3xxx, // smulh [10011011010] rm[20-16] [011111] rn[9-5] rd[4-0]. kA64Stp4ffXD, // stp [0s10110100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. kA64Stp4rrXD, // stp [s010100100] imm_7[21-15] rt2[14-10] rn[9-5] rt[4-0]. diff --git a/compiler/dex/quick/arm64/assemble_arm64.cc b/compiler/dex/quick/arm64/assemble_arm64.cc index 0898f7fa413..72236de08f4 100644 --- a/compiler/dex/quick/arm64/assemble_arm64.cc +++ b/compiler/dex/quick/arm64/assemble_arm64.cc @@ -14,8 +14,9 @@ * limitations under the License. */ -#include "arm64_lir.h" #include "codegen_arm64.h" + +#include "arm64_lir.h" #include "dex/quick/mir_to_lir-inl.h" namespace art { @@ -467,13 +468,17 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, "madd", "!0r, !1r, !3r, !2r", kFixupNone), ENCODING_MAP(WIDE(kA64Msub4rrrr), SF_VARIANTS(0x1b008000), - kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 14, 10, - kFmtRegR, 20, 16, IS_QUAD_OP | REG_DEF0_USE123, - "msub", "!0r, !1r, !3r, !2r", kFixupNone), + kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, + kFmtRegR, 14, 10, IS_QUAD_OP | REG_DEF0_USE123 | NEEDS_FIXUP, + "msub", "!0r, !1r, !2r, !3r", kFixupA53Erratum835769), ENCODING_MAP(WIDE(kA64Neg3rro), SF_VARIANTS(0x4b0003e0), kFmtRegR, 4, 0, kFmtRegR, 20, 16, kFmtShift, -1, -1, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, "neg", "!0r, !1r!2o", kFixupNone), + ENCODING_MAP(kA64Nop0, NO_VARIANTS(0xd503201f), + kFmtUnused, -1, -1, kFmtUnused, -1, -1, kFmtUnused, -1, -1, + kFmtUnused, -1, -1, NO_OPERAND, + "nop", "", kFixupNone), ENCODING_MAP(WIDE(kA64Orr3Rrl), SF_VARIANTS(0x32000000), kFmtRegROrSp, 4, 0, kFmtRegR, 9, 5, kFmtBitBlt, 22, 10, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE1, @@ -522,10 +527,10 @@ const ArmEncodingMap Arm64Mir2Lir::EncodingMap[kA64Last] = { kFmtRegR, 4, 0, kFmtRegR, 9, 5, kFmtRegR, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, "sdiv", "!0r, !1r, !2r", kFixupNone), - ENCODING_MAP(WIDE(kA64Smaddl4xwwx), NO_VARIANTS(0x9b200000), + ENCODING_MAP(kA64Smull3xww, NO_VARIANTS(0x9b207c00), kFmtRegX, 4, 0, kFmtRegW, 9, 5, kFmtRegW, 20, 16, - kFmtRegX, 14, 10, IS_QUAD_OP | REG_DEF0_USE123, - "smaddl", "!0x, !1w, !2w, !3x", kFixupNone), + kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, + "smull", "!0x, !1w, !2w", kFixupNone), ENCODING_MAP(kA64Smulh3xxx, NO_VARIANTS(0x9b407c00), kFmtRegX, 4, 0, kFmtRegX, 9, 5, kFmtRegX, 20, 16, kFmtUnused, -1, -1, IS_TERTIARY_OP | REG_DEF0_USE12, @@ -828,6 +833,20 @@ uint8_t* Arm64Mir2Lir::EncodeLIRs(uint8_t* write_pos, LIR* lir) { // are better set directly from the code (they will require no more than 2 instructions). #define ALIGNED_DATA_OFFSET(offset) (((offset) + 0x7) & ~0x7) +/* + * Get the LIR which emits the instruction preceding the given LIR. + * Returns nullptr, if no previous emitting insn found. + */ +static LIR* GetPrevEmittingLIR(LIR* lir) { + DCHECK(lir != nullptr); + LIR* prev_lir = lir->prev; + while ((prev_lir != nullptr) && + (prev_lir->flags.is_nop || Mir2Lir::IsPseudoLirOp(prev_lir->opcode))) { + prev_lir = prev_lir->prev; + } + return prev_lir; +} + // Assemble the LIR into binary instruction format. void Arm64Mir2Lir::AssembleLIR() { LIR* lir; @@ -920,6 +939,33 @@ void Arm64Mir2Lir::AssembleLIR() { lir->operands[1] = delta; break; } + case kFixupA53Erratum835769: + // Avoid emitting code that could trigger Cortex A53's erratum 835769. + // This fixup should be carried out for all multiply-accumulate instructions: madd, msub, + // smaddl, smsubl, umaddl and umsubl. + if (cu_->GetInstructionSetFeatures().NeedFix835769()) { + // Check that this is a 64-bit multiply-accumulate. + if (IS_WIDE(lir->opcode)) { + LIR* prev_insn = GetPrevEmittingLIR(lir); + if (prev_insn == nullptr) { + break; + } + uint64_t prev_insn_flags = EncodingMap[UNWIDE(prev_insn->opcode)].flags; + // Check that the instruction preceding the multiply-accumulate is a load or store. + if ((prev_insn_flags & IS_LOAD) != 0 || (prev_insn_flags & IS_STORE) != 0) { + // insert a NOP between the load/store and the multiply-accumulate. + LIR* new_lir = RawLIR(lir->dalvik_offset, kA64Nop0, 0, 0, 0, 0, 0, NULL); + new_lir->offset = lir->offset; + new_lir->flags.fixup = kFixupNone; + new_lir->flags.size = EncodingMap[kA64Nop0].size; + InsertLIRBefore(lir, new_lir); + lir->offset += new_lir->flags.size; + offset_adjustment += new_lir->flags.size; + res = kRetryAll; + } + } + } + break; default: LOG(FATAL) << "Unexpected case: opcode: " << lir->opcode << ", fixup: " << lir->flags.fixup; } diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index 1e97a3246c2..a9b4658edff 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -419,8 +419,7 @@ bool Arm64Mir2Lir::SmallLiteralDivRem(Instruction::Code dalvik_opcode, bool is_d rl_src = LoadValue(rl_src, kCoreReg); RegLocation rl_result = EvalLoc(rl_dest, kCoreReg, true); RegStorage r_long_mul = AllocTemp(); - NewLIR4(kA64Smaddl4xwwx, As64BitReg(r_long_mul).GetReg(), - r_magic.GetReg(), rl_src.reg.GetReg(), rxzr); + NewLIR3(kA64Smull3xww, As64BitReg(r_long_mul).GetReg(), r_magic.GetReg(), rl_src.reg.GetReg()); switch (pattern) { case Divide3: OpRegRegImm(kOpLsr, As64BitReg(r_long_mul), As64BitReg(r_long_mul), 32); @@ -635,7 +634,7 @@ RegLocation Arm64Mir2Lir::GenDivRem(RegLocation rl_dest, RegStorage r_src1, RegS } OpRegRegReg(kOpDiv, temp, r_src1, r_src2); NewLIR4(kA64Msub4rrrr | wide, rl_result.reg.GetReg(), temp.GetReg(), - r_src1.GetReg(), r_src2.GetReg()); + r_src2.GetReg(), r_src1.GetReg()); FreeTemp(temp); } return rl_result; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index c41147265f9..2294c93a46b 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -751,6 +751,12 @@ static InstructionSetFeatures ParseFeatureList(std::string str) { } else if (feature == "nolpae") { // Turn off support for Large Physical Address Extension. result.SetHasLpae(false); + } else if (feature == "needfix_835769") { + // need fix CortexA53 errata 835769 + result.SetFix835769(true); + } else if (feature == "noneedfix_835769") { + // no need fix CortexA53 errata 835769 + result.SetFix835769(false); } else { Usage("Unknown instruction set feature: '%s'", feature.c_str()); } diff --git a/runtime/base/macros.h b/runtime/base/macros.h index fae9271d9e4..b1eab92f0c0 100644 --- a/runtime/base/macros.h +++ b/runtime/base/macros.h @@ -151,7 +151,7 @@ char (&ArraySizeHelper(T (&array)[N]))[N]; #define UNLIKELY(x) __builtin_expect((x), false) // Stringify the argument. -#define QUOTE(x) #x +#define QUOTE(x...) #x #define STRINGIFY(x) QUOTE(x) #ifndef NDEBUG diff --git a/runtime/instruction_set.h b/runtime/instruction_set.h index da7d153c08d..257c3d38e19 100644 --- a/runtime/instruction_set.h +++ b/runtime/instruction_set.h @@ -180,6 +180,7 @@ size_t GetStackOverflowReservedBytes(InstructionSet isa); enum InstructionFeatures { kHwDiv = 0x1, // Supports hardware divide. kHwLpae = 0x2, // Supports Large Physical Address Extension. + kFix835769 = 0x4, // need fix CortexA53 errata 835769 }; // This is a bitmask of supported features per architecture. @@ -206,6 +207,14 @@ class PACKED(4) InstructionSetFeatures { mask_ = (mask_ & ~kHwLpae) | (v ? kHwLpae : 0); } + bool NeedFix835769() const { + return (mask_ & kFix835769) != 0; + } + + void SetFix835769(bool v) { + mask_ = (mask_ & ~kFix835769) | (v ? kFix835769 : 0); + } + std::string GetFeatureString() const; // Other features in here. From 1782e2d855f416a43d58db68ca6d8ba3a656c1a3 Mon Sep 17 00:00:00 2001 From: Chirayu Desai Date: Fri, 10 Jul 2015 01:08:23 +0530 Subject: [PATCH 126/150] Improve dalvik cache pruning Squashed commit of the following: (Revert this to pick Google's change below) commit cc8ef2227d45eef1563b8f6be69bd18d068ab519 Author: Chirayu Desai Date: Fri Jul 10 01:03:15 2015 +0530 Revert "Allow skipping of dalvik cache pruning if boot marker is in place." This reverts commit 9514788ea796de82c2e4871b4d716ec2c3995fac. (Grabbed the ImageSpace::MarkZygoteStart change only as we don't have the underly runtime option implementation. Hardcoded to 1, the default here.) commit d17209cb6511c7bd646810e4c86188addbe17450 Author: Narayan Kamath Date: Mon Feb 16 13:51:51 2015 +0000 Implement a simple count based boot marker. We write the number of failed boots to the marker and only prune the dalvik cache if the number of consecutive failed boots is higher than a predefined threshold. Note that the code is forgiving of errors related to boot markers; we continue the boot process even if we're unable to create / write or flush the boot marker. bug: 19360096 Change-Id: Ia17c3b783318ddf43c9199d0f7f09c54a4176667 (Change the hardcode to 10) commit bb2af23233e44d7f58a57874a948b7610447838e Author: Andreas Gampe Date: Tue Mar 31 14:54:03 2015 -0700 ART: Set default prune counter to 10 By default, prune cache at boot after ten failed boots. Bug: 19983101 Change-Id: Ib88f807e0082d71292c14c7af38e02cca5a5602c commit 182fd993566abd922b920c5a67aa49fdf1d23e3b Author: Chirayu Desai Date: Fri Jul 10 19:46:13 2015 +0530 Fix max failed boots allowed check and update the method's comment. Change-Id: I773a5d0e5fea2a3eca301e683f3bfc9a3eeb58ef Ticket: CYNGNOS-145 Change-Id: I1c64be9d0556c840cb1117f34c5c760d22e338b3 --- runtime/gc/space/image_space.cc | 79 +++++++++++++++++++-------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc index d42eeb8fa49..e9187b20342 100644 --- a/runtime/gc/space/image_space.cc +++ b/runtime/gc/space/image_space.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -34,10 +35,6 @@ #include "space-inl.h" #include "utils.h" -#ifdef HAVE_ANDROID_OS -#include -#endif - namespace art { namespace gc { namespace space { @@ -121,40 +118,54 @@ static void RealPruneDalvikCache(const std::string& cache_dir_path) { CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(cache_dir))) << "Unable to close directory."; } -// We write out an empty file to the zygote's ISA specific cache dir at the start of -// every zygote boot and delete it when the boot completes. If we find a file already -// present, it usually means the boot didn't complete. We wipe the entire dalvik -// cache if that's the case. -static void MarkZygoteStart(const InstructionSet isa) { +// We write out a file with the number of failed boots to the zygote's ISA specific cache dir +// at the start of every zygote boot and delete it when the boot completes. If we find a file +// already present, we check if the number of failed boots is greater than the maximum failed +// boots allowed, and wipe the entire dalvik cache if that's the case. +static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) { const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false); const std::string boot_marker = isa_subdir + "/.booting"; - bool pruned_image_cache = true; - -#ifdef HAVE_ANDROID_OS - int8_t prune_image_cache = property_get_bool("persist.art.pruneimagecache", 1); -#else - int8_t prune_image_cache = 1; -#endif - - if (OS::FileExists(boot_marker.c_str())) { - if (!prune_image_cache) { - LOG(WARNING) << "Incomplete boot detected. Skipped prunning of dalvik cache due to property"; - pruned_image_cache = false; - } else { - LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache"; - RealPruneDalvikCache(isa_subdir); - pruned_image_cache = true; + const char* file_name = boot_marker.c_str(); + + uint32_t num_failed_boots = 0; + std::unique_ptr file(OS::OpenFileReadWrite(file_name)); + if (file.get() == nullptr) { + file.reset(OS::CreateEmptyFile(file_name)); + + if (file.get() == nullptr) { + PLOG(WARNING) << "Failed to create boot marker."; + return; + } + } else { + if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) { + PLOG(WARNING) << "Failed to read boot marker."; + file->Erase(); + return; } } - if (pruned_image_cache || !OS::FileExists(boot_marker.c_str())) { - VLOG(startup) << "Creating boot start marker: " << boot_marker; - std::unique_ptr f(OS::CreateEmptyFile(boot_marker.c_str())); - if (f.get() != nullptr) { - if (f->FlushCloseOrErase() != 0) { - PLOG(WARNING) << "Failed to write boot marker."; - } - } + if (max_failed_boots != 0 && num_failed_boots >= max_failed_boots) { + LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache"; + RealPruneDalvikCache(isa_subdir); + } + + ++num_failed_boots; + VLOG(startup) << "Number of failed boots on : " << boot_marker << " = " << num_failed_boots; + + if (lseek(file->Fd(), 0, SEEK_SET) == -1) { + PLOG(WARNING) << "Failed to write boot marker."; + file->Erase(); + return; + } + + if (!file->WriteFully(&num_failed_boots, sizeof(num_failed_boots))) { + PLOG(WARNING) << "Failed to write boot marker."; + file->Erase(); + return; + } + + if (file->FlushCloseOrErase() != 0) { + PLOG(WARNING) << "Failed to flush boot marker."; } } @@ -468,7 +479,7 @@ ImageSpace* ImageSpace::Create(const char* image_location, &has_cache, &is_global_cache); if (Runtime::Current()->IsZygote()) { - MarkZygoteStart(image_isa); + MarkZygoteStart(image_isa, 10); } ImageSpace* space; From 4c1d73c8b42ed0ecc9a71bd589b7ab4c68b82101 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Mon, 20 Jul 2015 20:53:45 +0200 Subject: [PATCH 127/150] [Xposed] Fix a few debug checks Only relevant when ART is compiled in debug mode, i.e. with CFLAG -UNDEBUG. --- dex2oat/dex2oat.cc | 2 +- runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 2 +- runtime/verifier/method_verifier.cc | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 686872c1b66..b1b92fa9a1d 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -475,7 +475,7 @@ class Dex2Oat { } void SetRuntimeRecompiling(bool new_value) { - runtime_->SetRecompiling(true); + runtime_->SetRecompiling(new_value); } private: diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 18fe7252602..4416b174c2c 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -640,8 +640,8 @@ extern "C" uint64_t artQuickProxyInvokeHandler(mirror::ArtMethod* proxy_method, BuildQuickArgumentVisitor local_ref_visitor(sp, is_static, shorty, shorty_len, &soa, &args); local_ref_visitor.VisitArguments(); - DCHECK_GT(args.size(), 0U) << PrettyMethod(proxy_method); if (!is_static) { + DCHECK_GT(args.size(), 0U) << PrettyMethod(proxy_method); args.erase(args.begin()); } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 36ad27c99f8..88276f6b2ba 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -335,7 +335,7 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, Handle need_precise_constants_(need_precise_constants), has_check_casts_(false), has_virtual_or_interface_invokes_(false), - verify_to_dump_(verify_to_dump) { + verify_to_dump_(verify_to_dump || Runtime::Current()->IsRecompiling()) { Runtime::Current()->AddMethodVerifier(this); DCHECK(class_def != nullptr); } @@ -755,7 +755,7 @@ bool MethodVerifier::VerifyInstruction(const Instruction* inst, uint32_t code_of result = false; break; } - if (inst->GetVerifyIsRuntimeOnly() && Runtime::Current()->IsCompiler() && !Runtime::Current()->IsRecompiling() && !verify_to_dump_) { + if (inst->GetVerifyIsRuntimeOnly() && Runtime::Current()->IsCompiler() && !verify_to_dump_) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "opcode only expected at runtime " << inst->Name(); result = false; } From 92aefe0d56bb77151c8d1d75b4baf43bcfcd639d Mon Sep 17 00:00:00 2001 From: buzbee Date: Tue, 25 Nov 2014 14:39:38 -0800 Subject: [PATCH 128/150] Quick compiler: handle embedded switch data. Although switch data is generally placed at the end of a dex file by dx, it can occur elsewhere (and does via obsfucators). This CL fixes a parsing error related to embedded switch data by ensuring valid dex instructions following the embedded data appear in their own basic blocks. b/18524584 Change-Id: I815bb5ec939803f706f98beb3afbefc8b527f730 --- compiler/dex/mir_graph.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/dex/mir_graph.cc b/compiler/dex/mir_graph.cc index 6aee56373c3..2aafb747fcb 100644 --- a/compiler/dex/mir_graph.cc +++ b/compiler/dex/mir_graph.cc @@ -762,8 +762,10 @@ void MIRGraph::InlineMethod(const DexFile::CodeItem* code_item, uint32_t access_ } else { DCHECK(cur_block->fall_through == NullBasicBlockId); DCHECK(cur_block->taken == NullBasicBlockId); - // Unreachable instruction, mark for no continuation. + // Unreachable instruction, mark for no continuation and end basic block. flags &= ~Instruction::kContinue; + FindBlock(current_offset_ + width, /* split */ false, /* create */ true, + /* immed_pred_block_p */ NULL); } } else { cur_block->AppendMIR(insn); From c2f12444c32b741b52a589b27b2cfc14b76a8524 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Tue, 21 Jul 2015 22:01:48 +0200 Subject: [PATCH 129/150] [Xposed] Make input oat file writable (in memory) for dex2oat ART performs some dex optimizations during recompilation, especially when parameter --compiler-filter=interpret-only is used. Therefore, the memory has to be writable, otherwise dex2oat crashes with access errors. The underlying input file doesn't need to be writable, any modifications are just done in the memory. --- dex2oat/dex2oat.cc | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index b1b92fa9a1d..e09bdd05118 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -579,10 +579,23 @@ static size_t OpenDexFiles(const std::vector& dex_filenames, continue; } if (EndsWith(dex_filename, ".oat") || EndsWith(dex_filename, ".odex")) { - const OatFile* oat_file = OatFile::Open(dex_filename, dex_location, nullptr, false, &error_msg); + std::unique_ptr file(OS::OpenFileForReading(dex_filename)); + if (file.get() == nullptr) { + LOG(WARNING) << "Failed to open file '" << dex_filename << "': " << strerror(errno);; + ++failure_count; + continue; + } + std::unique_ptr elf_file(ElfFile::Open(file.release(), PROT_READ | PROT_WRITE, MAP_PRIVATE, &error_msg)); + if (elf_file.get() == nullptr) { + LOG(WARNING) << "Failed to open ELF file from '" << dex_filename << "': " << error_msg; + ++failure_count; + continue; + } + const OatFile* oat_file = OatFile::OpenWithElfFile(elf_file.release(), dex_filename, &error_msg); if (oat_file == nullptr) { LOG(WARNING) << "Failed to open oat file from '" << dex_filename << "': " << error_msg; ++failure_count; + continue; } else { for (const OatFile::OatDexFile* oat_dex_file : oat_file->GetOatDexFiles()) { CHECK(oat_dex_file != nullptr); From cfb94588726488ecd6cb2361c9a6340ac09b97c7 Mon Sep 17 00:00:00 2001 From: Elena Sayapina Date: Fri, 15 Aug 2014 15:52:42 +0700 Subject: [PATCH 130/150] ART: Fix verifier fail message. Verifier failure message is corrupted. The verification failure reason overlaps verification failure location. MethodVerifier::Fail() method creates failure message stream by std::ostringstream(failure location) constructor which by default sets the stream's position indicator to the begging of that stream. Inserting failure reason to the stream by "<<" then overrides the failure location. Using std::ostringstream(failure location, std::ostringstream::ate) fixes the issue by setting the stream's position indicator to the end of the stream on opening. Signed-off-by: Elena Sayapina (cherry picked from commit 78480ecb348beee3f27731e12ec06ade032521d6) bug: 17790245 Change-Id: I31955e859a4eb01d05318395901e8cd229a6c33c --- runtime/verifier/method_verifier.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 88276f6b2ba..4140fd25280 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -506,9 +506,9 @@ std::ostream& MethodVerifier::Fail(VerifyError error) { } } failures_.push_back(error); - std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(dex_method_idx_, *dex_file_).c_str(), + std::string location(StringPrintf("%s: [0x%X] ", PrettyMethod(dex_method_idx_, *dex_file_).c_str(), work_insn_idx_)); - std::ostringstream* failure_message = new std::ostringstream(location); + std::ostringstream* failure_message = new std::ostringstream(location, std::ostringstream::ate); failure_messages_.push_back(failure_message); return *failure_message; } @@ -523,7 +523,7 @@ void MethodVerifier::PrependToLastFailMessage(std::string prepend) { DCHECK_NE(failure_num, 0U); std::ostringstream* last_fail_message = failure_messages_[failure_num - 1]; prepend += last_fail_message->str(); - failure_messages_[failure_num - 1] = new std::ostringstream(prepend); + failure_messages_[failure_num - 1] = new std::ostringstream(prepend, std::ostringstream::ate); delete last_fail_message; } From 5fdc01a5a5447abe15c87cbd3666430f92b35244 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 27 Oct 2014 20:08:46 -0700 Subject: [PATCH 131/150] ART: Do not abort on most verifier failures Changes hard aborts to hard verifier failures, which rejects a class instead of killing the process. Bug: 17625962 Change-Id: Iba8e15676e13ea6dcd6e1e5d0484031d9ab52ae9 --- runtime/verifier/method_verifier.cc | 70 +++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 4140fd25280..298245c13fe 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -87,6 +87,23 @@ PcToRegisterLineTable::~PcToRegisterLineTable() { } } +// Note: returns true on failure. +ALWAYS_INLINE static inline bool FailOrAbort(MethodVerifier* verifier, bool condition, + const char* error_msg, uint32_t work_insn_idx) { + if (kIsDebugBuild) { + // In a debug build, abort if the error condition is wrong. + DCHECK(condition) << error_msg << work_insn_idx; + } else { + // In a non-debug build, just fail the class. + if (!condition) { + verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << error_msg << work_insn_idx; + return true; + } + } + + return false; +} + MethodVerifier::FailureKind MethodVerifier::VerifyClass(mirror::Class* klass, bool allow_soft_failures, std::string* error) { @@ -1951,7 +1968,11 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { while (0 != instance_of_idx && !insn_flags_[instance_of_idx].IsOpcode()) { instance_of_idx--; } - CHECK(insn_flags_[instance_of_idx].IsOpcode()); + if (FailOrAbort(this, insn_flags_[instance_of_idx].IsOpcode(), + "Unable to get previous instruction of if-eqz/if-nez for work index ", + work_insn_idx_)) { + break; + } } else { break; } @@ -2009,7 +2030,11 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { while (0 != move_idx && !insn_flags_[move_idx].IsOpcode()) { move_idx--; } - CHECK(insn_flags_[move_idx].IsOpcode()); + if (FailOrAbort(this, insn_flags_[move_idx].IsOpcode(), + "Unable to get previous instruction of if-eqz/if-nez for work index ", + work_insn_idx_)) { + break; + } const Instruction* move_inst = Instruction::At(code_item_->insns_ + move_idx); switch (move_inst->Opcode()) { case Instruction::MOVE_OBJECT: @@ -2982,7 +3007,12 @@ RegType& MethodVerifier::GetCaughtExceptionType() { // odd case, but nothing to do } else { common_super = &common_super->Merge(exception, ®_types_); - CHECK(reg_types_.JavaLangThrowable(false).IsAssignableFrom(*common_super)); + if (FailOrAbort(this, + reg_types_.JavaLangThrowable(false).IsAssignableFrom(*common_super), + "java.lang.Throwable is not assignable-from common_super at ", + work_insn_idx_)) { + break; + } } } } @@ -3308,17 +3338,32 @@ mirror::ArtMethod* MethodVerifier::GetQuickInvokedMethod(const Instruction* inst if (klass->IsInterface()) { // Derive Object.class from Class.class.getSuperclass(). mirror::Class* object_klass = klass->GetClass()->GetSuperClass(); - CHECK(object_klass->IsObjectClass()); + if (FailOrAbort(this, object_klass->IsObjectClass(), + "Failed to find Object class in quickened invoke receiver", + work_insn_idx_)) { + return nullptr; + } dispatch_class = object_klass; } else { dispatch_class = klass; } - CHECK(dispatch_class->HasVTable()) << PrettyDescriptor(dispatch_class); + if (FailOrAbort(this, dispatch_class->HasVTable(), + "Receiver class has no vtable for quickened invoke at ", + work_insn_idx_)) { + return nullptr; + } uint16_t vtable_index = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); - CHECK_LT(static_cast(vtable_index), dispatch_class->GetVTableLength()) - << PrettyDescriptor(klass); + if (FailOrAbort(this, static_cast(vtable_index) < dispatch_class->GetVTableLength(), + "Receiver class has not enough vtable slots for quickened invoke at ", + work_insn_idx_)) { + return nullptr; + } mirror::ArtMethod* res_method = dispatch_class->GetVTableEntry(vtable_index); - CHECK(!Thread::Current()->IsExceptionPending()); + if (FailOrAbort(this, !Thread::Current()->IsExceptionPending(), + "Unexpected exception pending for quickened invoke at ", + work_insn_idx_)) { + return nullptr; + } return res_method; } @@ -3331,7 +3376,14 @@ mirror::ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instructio Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name(); return nullptr; } - CHECK(!res_method->IsDirect() && !res_method->IsStatic()); + if (FailOrAbort(this, !res_method->IsDirect(), "Quick-invoked method is direct at ", + work_insn_idx_)) { + return nullptr; + } + if (FailOrAbort(this, !res_method->IsStatic(), "Quick-invoked method is static at ", + work_insn_idx_)) { + return nullptr; + } // We use vAA as our expected arg count, rather than res_method->insSize, because we need to // match the call to the signature. Also, we might be calling through an abstract method From 915ec8af7655c73899d367be22dec933be2216dc Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Mon, 20 Oct 2014 22:25:29 -0700 Subject: [PATCH 132/150] ART: Fix possible soft+hard failure in verifier It was possible to generate a hard failure and a subsequent soft failure, which violates a CHECKed invariant. Refactor code slightly to share common code. Bug: 17625962 Change-Id: Iccd9e30f1087363b19b2faedc10243a2290202c0 --- runtime/verifier/method_verifier.cc | 426 ++++++++++++++-------------- runtime/verifier/method_verifier.h | 26 +- 2 files changed, 217 insertions(+), 235 deletions(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 298245c13fe..6acee9dbea3 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -2117,91 +2117,95 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; case Instruction::IGET_BOOLEAN: - VerifyISGet(inst, reg_types_.Boolean(), true, false); + VerifyISFieldAccess(inst, reg_types_.Boolean(), true, false); break; case Instruction::IGET_BYTE: - VerifyISGet(inst, reg_types_.Byte(), true, false); + VerifyISFieldAccess(inst, reg_types_.Byte(), true, false); break; case Instruction::IGET_CHAR: - VerifyISGet(inst, reg_types_.Char(), true, false); + VerifyISFieldAccess(inst, reg_types_.Char(), true, false); break; case Instruction::IGET_SHORT: - VerifyISGet(inst, reg_types_.Short(), true, false); + VerifyISFieldAccess(inst, reg_types_.Short(), true, false); break; case Instruction::IGET: - VerifyISGet(inst, reg_types_.Integer(), true, false); + VerifyISFieldAccess(inst, reg_types_.Integer(), true, false); break; case Instruction::IGET_WIDE: - VerifyISGet(inst, reg_types_.LongLo(), true, false); + VerifyISFieldAccess(inst, reg_types_.LongLo(), true, false); break; case Instruction::IGET_OBJECT: - VerifyISGet(inst, reg_types_.JavaLangObject(false), false, false); + VerifyISFieldAccess(inst, reg_types_.JavaLangObject(false), false, + false); break; case Instruction::IPUT_BOOLEAN: - VerifyISPut(inst, reg_types_.Boolean(), true, false); + VerifyISFieldAccess(inst, reg_types_.Boolean(), true, false); break; case Instruction::IPUT_BYTE: - VerifyISPut(inst, reg_types_.Byte(), true, false); + VerifyISFieldAccess(inst, reg_types_.Byte(), true, false); break; case Instruction::IPUT_CHAR: - VerifyISPut(inst, reg_types_.Char(), true, false); + VerifyISFieldAccess(inst, reg_types_.Char(), true, false); break; case Instruction::IPUT_SHORT: - VerifyISPut(inst, reg_types_.Short(), true, false); + VerifyISFieldAccess(inst, reg_types_.Short(), true, false); break; case Instruction::IPUT: - VerifyISPut(inst, reg_types_.Integer(), true, false); + VerifyISFieldAccess(inst, reg_types_.Integer(), true, false); break; case Instruction::IPUT_WIDE: - VerifyISPut(inst, reg_types_.LongLo(), true, false); + VerifyISFieldAccess(inst, reg_types_.LongLo(), true, false); break; case Instruction::IPUT_OBJECT: - VerifyISPut(inst, reg_types_.JavaLangObject(false), false, false); + VerifyISFieldAccess(inst, reg_types_.JavaLangObject(false), false, + false); break; case Instruction::SGET_BOOLEAN: - VerifyISGet(inst, reg_types_.Boolean(), true, true); + VerifyISFieldAccess(inst, reg_types_.Boolean(), true, true); break; case Instruction::SGET_BYTE: - VerifyISGet(inst, reg_types_.Byte(), true, true); + VerifyISFieldAccess(inst, reg_types_.Byte(), true, true); break; case Instruction::SGET_CHAR: - VerifyISGet(inst, reg_types_.Char(), true, true); + VerifyISFieldAccess(inst, reg_types_.Char(), true, true); break; case Instruction::SGET_SHORT: - VerifyISGet(inst, reg_types_.Short(), true, true); + VerifyISFieldAccess(inst, reg_types_.Short(), true, true); break; case Instruction::SGET: - VerifyISGet(inst, reg_types_.Integer(), true, true); + VerifyISFieldAccess(inst, reg_types_.Integer(), true, true); break; case Instruction::SGET_WIDE: - VerifyISGet(inst, reg_types_.LongLo(), true, true); + VerifyISFieldAccess(inst, reg_types_.LongLo(), true, true); break; case Instruction::SGET_OBJECT: - VerifyISGet(inst, reg_types_.JavaLangObject(false), false, true); + VerifyISFieldAccess(inst, reg_types_.JavaLangObject(false), false, + true); break; case Instruction::SPUT_BOOLEAN: - VerifyISPut(inst, reg_types_.Boolean(), true, true); + VerifyISFieldAccess(inst, reg_types_.Boolean(), true, true); break; case Instruction::SPUT_BYTE: - VerifyISPut(inst, reg_types_.Byte(), true, true); + VerifyISFieldAccess(inst, reg_types_.Byte(), true, true); break; case Instruction::SPUT_CHAR: - VerifyISPut(inst, reg_types_.Char(), true, true); + VerifyISFieldAccess(inst, reg_types_.Char(), true, true); break; case Instruction::SPUT_SHORT: - VerifyISPut(inst, reg_types_.Short(), true, true); + VerifyISFieldAccess(inst, reg_types_.Short(), true, true); break; case Instruction::SPUT: - VerifyISPut(inst, reg_types_.Integer(), true, true); + VerifyISFieldAccess(inst, reg_types_.Integer(), true, true); break; case Instruction::SPUT_WIDE: - VerifyISPut(inst, reg_types_.LongLo(), true, true); + VerifyISFieldAccess(inst, reg_types_.LongLo(), true, true); break; case Instruction::SPUT_OBJECT: - VerifyISPut(inst, reg_types_.JavaLangObject(false), false, true); + VerifyISFieldAccess(inst, reg_types_.JavaLangObject(false), false, + true); break; case Instruction::INVOKE_VIRTUAL: @@ -2645,22 +2649,22 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { // As such they use Class*/Field*/AbstractMethod* as these offsets only have // meaning if the class linking and resolution were successful. case Instruction::IGET_QUICK: - VerifyIGetQuick(inst, reg_types_.Integer(), true); + VerifyQuickFieldAccess(inst, reg_types_.Integer(), true); break; case Instruction::IGET_WIDE_QUICK: - VerifyIGetQuick(inst, reg_types_.LongLo(), true); + VerifyQuickFieldAccess(inst, reg_types_.LongLo(), true); break; case Instruction::IGET_OBJECT_QUICK: - VerifyIGetQuick(inst, reg_types_.JavaLangObject(false), false); + VerifyQuickFieldAccess(inst, reg_types_.JavaLangObject(false), false); break; case Instruction::IPUT_QUICK: - VerifyIPutQuick(inst, reg_types_.Integer(), true); + VerifyQuickFieldAccess(inst, reg_types_.Integer(), true); break; case Instruction::IPUT_WIDE_QUICK: - VerifyIPutQuick(inst, reg_types_.LongLo(), true); + VerifyQuickFieldAccess(inst, reg_types_.LongLo(), true); break; case Instruction::IPUT_OBJECT_QUICK: - VerifyIPutQuick(inst, reg_types_.JavaLangObject(false), false); + VerifyQuickFieldAccess(inst, reg_types_.JavaLangObject(false), false); break; case Instruction::INVOKE_VIRTUAL_QUICK: case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: { @@ -3747,8 +3751,9 @@ mirror::ArtField* MethodVerifier::GetInstanceField(RegType& obj_type, int field_ } } -void MethodVerifier::VerifyISGet(const Instruction* inst, RegType& insn_type, - bool is_primitive, bool is_static) { +template +void MethodVerifier::VerifyISFieldAccess(const Instruction* inst, RegType& insn_type, + bool is_primitive, bool is_static) { uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); mirror::ArtField* field; if (is_static) { @@ -3756,9 +3761,20 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, RegType& insn_type, } else { RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); field = GetInstanceField(object_type, field_idx); + if (UNLIKELY(have_pending_hard_failure_)) { + return; + } } RegType* field_type = nullptr; if (field != nullptr) { + if (kAccType == FieldAccessType::kAccPut) { + if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { + Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) + << " from other class " << GetDeclaringClass(); + return; + } + } + Thread* self = Thread::Current(); mirror::Class* field_type_class; { @@ -3781,89 +3797,56 @@ void MethodVerifier::VerifyISGet(const Instruction* inst, RegType& insn_type, } DCHECK(field_type != nullptr); const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); - if (is_primitive) { - if (field_type->Equals(insn_type) || - (field_type->IsFloat() && insn_type.IsInteger()) || - (field_type->IsDouble() && insn_type.IsLong())) { - // expected that read is of the correct primitive type or that int reads are reading - // floats or long reads are reading doubles + static_assert(kAccType == FieldAccessType::kAccPut || kAccType == FieldAccessType::kAccGet, + "Unexpected third access type"); + if (kAccType == FieldAccessType::kAccPut) { + // sput or iput. + if (is_primitive) { + VerifyPrimitivePut(*field_type, insn_type, vregA); } else { - // This is a global failure rather than a class change failure as the instructions and - // the descriptors for the type should have been consistent within the same file at - // compile time - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) - << " to be of type '" << insn_type - << "' but found type '" << *field_type << "' in get"; - return; - } - } else { - if (!insn_type.IsAssignableFrom(*field_type)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) - << " to be compatible with type '" << insn_type - << "' but found type '" << *field_type - << "' in Get-object"; - work_line_->SetRegisterType(vregA, reg_types_.Conflict()); - return; - } - } - if (!field_type->IsLowHalf()) { - work_line_->SetRegisterType(vregA, *field_type); - } else { - work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_)); - } -} - -void MethodVerifier::VerifyISPut(const Instruction* inst, RegType& insn_type, - bool is_primitive, bool is_static) { - uint32_t field_idx = is_static ? inst->VRegB_21c() : inst->VRegC_22c(); - mirror::ArtField* field; - if (is_static) { - field = GetStaticField(field_idx); - } else { - RegType& object_type = work_line_->GetRegisterType(inst->VRegB_22c()); - field = GetInstanceField(object_type, field_idx); - } - RegType* field_type = nullptr; - if (field != nullptr) { - if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { - Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) - << " from other class " << GetDeclaringClass(); - return; + if (!insn_type.IsAssignableFrom(*field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << *field_type + << "' in put-object"; + return; + } + work_line_->VerifyRegisterType(vregA, *field_type); } - mirror::Class* field_type_class; - { - StackHandleScope<1> hs(Thread::Current()); - HandleWrapper h_field(hs.NewHandleWrapper(&field)); - FieldHelper fh(h_field); - field_type_class = fh.GetType(can_load_classes_); + } else if (kAccType == FieldAccessType::kAccGet) { + // sget or iget. + if (is_primitive) { + if (field_type->Equals(insn_type) || + (field_type->IsFloat() && insn_type.IsInteger()) || + (field_type->IsDouble() && insn_type.IsLong())) { + // expected that read is of the correct primitive type or that int reads are reading + // floats or long reads are reading doubles + } else { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << *field_type << "' in get"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(*field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << *field_type + << "' in get-object"; + work_line_->SetRegisterType(vregA, reg_types_.Conflict()); + return; + } } - if (field_type_class != nullptr) { - field_type = ®_types_.FromClass(field->GetTypeDescriptor(), field_type_class, - field_type_class->CannotBeAssignedFromOtherTypes()); + if (!field_type->IsLowHalf()) { + work_line_->SetRegisterType(vregA, *field_type); } else { - Thread* self = Thread::Current(); - DCHECK(!can_load_classes_ || self->IsExceptionPending()); - self->ClearException(); + work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_)); } - } - if (field_type == nullptr) { - const DexFile::FieldId& field_id = dex_file_->GetFieldId(field_idx); - const char* descriptor = dex_file_->GetFieldTypeDescriptor(field_id); - field_type = ®_types_.FromDescriptor(class_loader_->Get(), descriptor, false); - } - DCHECK(field_type != nullptr); - const uint32_t vregA = (is_static) ? inst->VRegA_21c() : inst->VRegA_22c(); - if (is_primitive) { - VerifyPrimitivePut(*field_type, insn_type, vregA); } else { - if (!insn_type.IsAssignableFrom(*field_type)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) - << " to be compatible with type '" << insn_type - << "' but found type '" << *field_type - << "' in put-object"; - return; - } - work_line_->VerifyRegisterType(vregA, *field_type); + LOG(FATAL) << "Unexpected case."; } } @@ -3890,132 +3873,137 @@ mirror::ArtField* MethodVerifier::GetQuickFieldAccess(const Instruction* inst, return f; } -void MethodVerifier::VerifyIGetQuick(const Instruction* inst, RegType& insn_type, - bool is_primitive) { +template +void MethodVerifier::VerifyQuickFieldAccess(const Instruction* inst, RegType& insn_type, + bool is_primitive) { DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_); + mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get()); if (field == nullptr) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); return; } - mirror::Class* field_type_class; - { - StackHandleScope<1> hs(Thread::Current()); - HandleWrapper h_field(hs.NewHandleWrapper(&field)); - FieldHelper fh(h_field); - field_type_class = fh.GetType(can_load_classes_); + + // For an IPUT_QUICK, we now test for final flag of the field. + if (kAccType == FieldAccessType::kAccPut) { + if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { + Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) + << " from other class " << GetDeclaringClass(); + return; + } } + + // Get the field type. RegType* field_type; - if (field_type_class != nullptr) { - field_type = ®_types_.FromClass(field->GetTypeDescriptor(), field_type_class, - field_type_class->CannotBeAssignedFromOtherTypes()); - } else { - Thread* self = Thread::Current(); - DCHECK(!can_load_classes_ || self->IsExceptionPending()); - self->ClearException(); - field_type = ®_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(), - field->GetTypeDescriptor(), false); - } - DCHECK(field_type != nullptr); - const uint32_t vregA = inst->VRegA_22c(); - if (is_primitive) { - if (field_type->Equals(insn_type) || - (field_type->IsFloat() && insn_type.IsIntegralTypes()) || - (field_type->IsDouble() && insn_type.IsLongTypes())) { - // expected that read is of the correct primitive type or that int reads are reading - // floats or long reads are reading doubles + { + mirror::Class* field_type_class; + { + StackHandleScope<1> hs(Thread::Current()); + HandleWrapper h_field(hs.NewHandleWrapper(&field)); + field_type_class = FieldHelper(h_field).GetType(can_load_classes_); + } + + if (field_type_class != nullptr) { + field_type = ®_types_.FromClass(field->GetTypeDescriptor(), field_type_class, + field_type_class->CannotBeAssignedFromOtherTypes()); } else { - // This is a global failure rather than a class change failure as the instructions and - // the descriptors for the type should have been consistent within the same file at - // compile time - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) - << " to be of type '" << insn_type - << "' but found type '" << *field_type << "' in Get"; - return; + Thread* self = Thread::Current(); + DCHECK(!can_load_classes_ || self->IsExceptionPending()); + self->ClearException(); + field_type = ®_types_.FromDescriptor(field->GetDeclaringClass()->GetClassLoader(), + field->GetTypeDescriptor(), false); } - } else { - if (!insn_type.IsAssignableFrom(*field_type)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) - << " to be compatible with type '" << insn_type - << "' but found type '" << *field_type - << "' in get-object"; - work_line_->SetRegisterType(vregA, reg_types_.Conflict()); + if (field_type == nullptr) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field type from " << inst->Name(); return; } } - if (!field_type->IsLowHalf()) { - work_line_->SetRegisterType(vregA, *field_type); - } else { - work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_)); - } -} -void MethodVerifier::VerifyIPutQuick(const Instruction* inst, RegType& insn_type, - bool is_primitive) { - DCHECK(Runtime::Current()->IsStarted() || verify_to_dump_); - mirror::ArtField* field = GetQuickFieldAccess(inst, work_line_.get()); - if (field == nullptr) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer field from " << inst->Name(); - return; - } - const char* descriptor = field->GetTypeDescriptor(); - mirror::ClassLoader* loader = field->GetDeclaringClass()->GetClassLoader(); - RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); - if (field != nullptr) { - if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { - Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) - << " from other class " << GetDeclaringClass(); - return; - } - } const uint32_t vregA = inst->VRegA_22c(); - if (is_primitive) { - // Primitive field assignability rules are weaker than regular assignability rules - bool instruction_compatible; - bool value_compatible; - RegType& value_type = work_line_->GetRegisterType(vregA); - if (field_type.IsIntegralTypes()) { - instruction_compatible = insn_type.IsIntegralTypes(); - value_compatible = value_type.IsIntegralTypes(); - } else if (field_type.IsFloat()) { - instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int - value_compatible = value_type.IsFloatTypes(); - } else if (field_type.IsLong()) { - instruction_compatible = insn_type.IsLong(); - value_compatible = value_type.IsLongTypes(); - } else if (field_type.IsDouble()) { - instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long - value_compatible = value_type.IsDoubleTypes(); + static_assert(kAccType == FieldAccessType::kAccPut || kAccType == FieldAccessType::kAccGet, + "Unexpected third access type"); + if (kAccType == FieldAccessType::kAccPut) { + if (is_primitive) { + // Primitive field assignability rules are weaker than regular assignability rules + bool instruction_compatible; + bool value_compatible; + RegType& value_type = work_line_->GetRegisterType(vregA); + if (field_type->IsIntegralTypes()) { + instruction_compatible = insn_type.IsIntegralTypes(); + value_compatible = value_type.IsIntegralTypes(); + } else if (field_type->IsFloat()) { + instruction_compatible = insn_type.IsInteger(); // no [is]put-float, so expect [is]put-int + value_compatible = value_type.IsFloatTypes(); + } else if (field_type->IsLong()) { + instruction_compatible = insn_type.IsLong(); + value_compatible = value_type.IsLongTypes(); + } else if (field_type->IsDouble()) { + instruction_compatible = insn_type.IsLong(); // no [is]put-double, so expect [is]put-long + value_compatible = value_type.IsDoubleTypes(); + } else { + instruction_compatible = false; // reference field with primitive store + value_compatible = false; // unused + } + if (!instruction_compatible) { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << *field_type + << "' in put"; + return; + } + if (!value_compatible) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA + << " of type " << value_type + << " but expected " << *field_type + << " for store to " << PrettyField(field) << " in put"; + return; + } } else { - instruction_compatible = false; // reference field with primitive store - value_compatible = false; // unused + if (!insn_type.IsAssignableFrom(*field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << *field_type + << "' in put-object"; + return; + } + work_line_->VerifyRegisterType(vregA, *field_type); } - if (!instruction_compatible) { - // This is a global failure rather than a class change failure as the instructions and - // the descriptors for the type should have been consistent within the same file at - // compile time - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) - << " to be of type '" << insn_type - << "' but found type '" << field_type - << "' in put"; - return; + } else if (kAccType == FieldAccessType::kAccGet) { + if (is_primitive) { + if (field_type->Equals(insn_type) || + (field_type->IsFloat() && insn_type.IsIntegralTypes()) || + (field_type->IsDouble() && insn_type.IsLongTypes())) { + // expected that read is of the correct primitive type or that int reads are reading + // floats or long reads are reading doubles + } else { + // This is a global failure rather than a class change failure as the instructions and + // the descriptors for the type should have been consistent within the same file at + // compile time + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "expected field " << PrettyField(field) + << " to be of type '" << insn_type + << "' but found type '" << *field_type << "' in Get"; + return; + } + } else { + if (!insn_type.IsAssignableFrom(*field_type)) { + Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) + << " to be compatible with type '" << insn_type + << "' but found type '" << *field_type + << "' in get-object"; + work_line_->SetRegisterType(vregA, reg_types_.Conflict()); + return; + } } - if (!value_compatible) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "unexpected value in v" << vregA - << " of type " << value_type - << " but expected " << field_type - << " for store to " << PrettyField(field) << " in put"; - return; + if (!field_type->IsLowHalf()) { + work_line_->SetRegisterType(vregA, *field_type); + } else { + work_line_->SetRegisterTypeWide(vregA, *field_type, field_type->HighHalf(®_types_)); } } else { - if (!insn_type.IsAssignableFrom(field_type)) { - Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "expected field " << PrettyField(field) - << " to be compatible with type '" << insn_type - << "' but found type '" << field_type - << "' in put-object"; - return; - } - work_line_->VerifyRegisterType(vregA, field_type); + LOG(FATAL) << "Unexpected case."; } } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 17713d2ab88..d7dd4b8afd7 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -506,14 +506,14 @@ class MethodVerifier { // Lookup static field and fail for resolution violations mirror::ArtField* GetStaticField(int field_idx) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Perform verification of an iget or sget instruction. - void VerifyISGet(const Instruction* inst, RegType& insn_type, - bool is_primitive, bool is_static) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Perform verification of an iput or sput instruction. - void VerifyISPut(const Instruction* inst, RegType& insn_type, - bool is_primitive, bool is_static) + // Perform verification of an iget/sget/iput/sput instruction. + enum class FieldAccessType { // private + kAccGet, + kAccPut + }; + template + void VerifyISFieldAccess(const Instruction* inst, RegType& insn_type, + bool is_primitive, bool is_static) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Returns the access field of a quick field access (iget/iput-quick) or nullptr @@ -521,14 +521,8 @@ class MethodVerifier { mirror::ArtField* GetQuickFieldAccess(const Instruction* inst, RegisterLine* reg_line) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Perform verification of an iget-quick instruction. - void VerifyIGetQuick(const Instruction* inst, RegType& insn_type, - bool is_primitive) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - - // Perform verification of an iput-quick instruction. - void VerifyIPutQuick(const Instruction* inst, RegType& insn_type, - bool is_primitive) + template + void VerifyQuickFieldAccess(const Instruction* inst, RegType& insn_type, bool is_primitive) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); // Resolves a class based on an index and performs access checks to ensure the referrer can From 0033d5a5e8766bb094c41a2c7455a3530136ffb0 Mon Sep 17 00:00:00 2001 From: Brian Carlstrom Date: Tue, 9 Dec 2014 20:15:42 -0800 Subject: [PATCH 133/150] Fix verifier bug caused by confusing ArtMethod::IsDirect vs ArtMethod::IsStatic semantics. Bug: 18485243 (cherry picked from commit be6fa5eb4a571e14481cf43f4cb264629c069153) Change-Id: I6e4d8e7587f7e03288ce039471154c1e6ebc7d8a --- runtime/verifier/method_verifier.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 6acee9dbea3..c402342feb8 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3113,7 +3113,7 @@ mirror::ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess(uint32_t dex_meth } // See if the method type implied by the invoke instruction matches the access flags for the // target method. - if ((method_type == METHOD_DIRECT && !res_method->IsDirect()) || + if ((method_type == METHOD_DIRECT && (!res_method->IsDirect() || res_method->IsStatic())) || (method_type == METHOD_STATIC && !res_method->IsStatic()) || ((method_type == METHOD_VIRTUAL || method_type == METHOD_INTERFACE) && res_method->IsDirect()) ) { From c867d4ba4087a234951fd97495b960aa67a2ab60 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 26 Jul 2015 13:33:24 +0200 Subject: [PATCH 134/150] [Xposed] Ignore invoke-virtual-quick verification errors in In rare cases, the target method for invoke-virtual-quick cannot be resolved because the "this" reference is still NULL when certain code paths are followed. ART's method verifier doesn't seem to be able to detect that previous checks ensure that the statement is never reached without an object reference. It marks the method (and therefore the class) as invalid/rejected and therefore makes the complete class unusable at runtime. So far, this was only detected in class initializers, which aren't compiled anyway. The interpreter later checks for this != NULL and throws an exception in this case, so it's safe to ignore this error situation. Fixes rovo89/Xposed#42. --- runtime/verifier/method_verifier.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index c402342feb8..20accd4fef0 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -3377,7 +3377,14 @@ mirror::ArtMethod* MethodVerifier::VerifyInvokeVirtualQuickArgs(const Instructio mirror::ArtMethod* res_method = GetQuickInvokedMethod(inst, work_line_.get(), is_range); if (res_method == nullptr) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name(); + if (((method_access_flags_ & kAccConstructor) != 0) && ((method_access_flags_ & kAccStatic) != 0)) { + // Class initializers are never compiled, but always interpreted. + // The interpreter might throw a NPE, so this error can be ignored. + LOG(WARNING) << "Cannot infer method from " << inst->Name() + << " ignored in " << PrettyMethod(dex_method_idx_, *dex_file_, false); + } else { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Cannot infer method from " << inst->Name(); + } return nullptr; } if (FailOrAbort(this, !res_method->IsDirect(), "Quick-invoked method is direct at ", From 31df113fd46d4561daee6d33a15e2cf5fe27de71 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 2 Aug 2015 15:59:44 +0200 Subject: [PATCH 135/150] [Xposed] Support for LG Almond encrypted apps LG has encrypted a few apps with their DRM implementation. Those apps can usually not be loaded. Fortunately, the decryption takes place in a separate library called liblgalmond.so. Xposed now uses this library if available to check if a dex file (usually located in a ZIP archive) is encrypted, and if yes, triggers the in-place decryption of the file before initializing it. Fixes rovo89/Xposed#27. --- runtime/dex_file.cc | 45 +++++++++++++++++++++++++++++++++++++++++++++ runtime/dex_file.h | 3 +++ runtime/runtime.cc | 2 ++ 3 files changed, 50 insertions(+) diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index c783ee40176..8f8622b4e8b 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -16,6 +16,7 @@ #include "dex_file.h" +#include #include #include #include @@ -321,6 +322,35 @@ bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& loca } } +typedef int (*Almond_IsDRMDexFn)(const void*, size_t); +typedef int (*Almond_CopyDexToMemFn)(void*, size_t, size_t*, uint8_t*, uint8_t*); +static const uint32_t kLGAlmondFormatDex = 1; +static Almond_IsDRMDexFn Almond_IsDRMDex = nullptr; +static Almond_CopyDexToMemFn Almond_CopyDexToMem = nullptr; + +void DexFile::InitLGAlmond() { + if (!OS::FileExists("/system/lib/liblgalmond.so")) { + return; + } + + void* dlopen_handle = dlopen("liblgalmond.so", RTLD_NOW); + if (dlopen_handle == nullptr) { + LOG(ERROR) << "Could not load liblgalmond.so: " << dlerror(); + return; + } + + Almond_CopyDexToMem = reinterpret_cast(dlsym(dlopen_handle, "Almond_CopyDexToMem")); + if (Almond_CopyDexToMem == nullptr) { + LOG(ERROR) << "Could not locate Almond_CopyDexToMem: " << dlerror(); + return; + } + + Almond_IsDRMDex = reinterpret_cast(dlsym(dlopen_handle, "Almond_Is_DRMDex")); + if (Almond_IsDRMDex == nullptr) { + LOG(ERROR) << "Could not locate Almond_Is_DRMDex: " << dlerror(); + return; + } +} const DexFile* DexFile::OpenMemory(const byte* base, size_t size, @@ -328,6 +358,21 @@ const DexFile* DexFile::OpenMemory(const byte* base, uint32_t location_checksum, MemMap* mem_map, std::string* error_msg) { CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned + + if (Almond_IsDRMDex != nullptr && Almond_IsDRMDex(base, size) == kLGAlmondFormatDex) { + if ((mem_map->GetProtect() & PROT_WRITE) == 0) { + LOG(ERROR) << "Could not decrypt " << location << " because it's in read-only memory"; + return nullptr; + } + + uint8_t cid_hash[20]; + uint8_t preload_id[20]; + if (Almond_CopyDexToMem(const_cast(base), size, &size, cid_hash, preload_id) != 0) { + LOG(ERROR) << "Failed to decrypt " << location << " with LG Almond"; + return nullptr; + } + } + std::unique_ptr dex_file(new DexFile(base, size, location, location_checksum, mem_map)); if (!dex_file->Init(error_msg)) { return nullptr; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 1306f115d2b..217c6d5c4b5 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -887,6 +887,9 @@ class DexFile { // the dex_location where it's file name part has been made canonical. static std::string GetDexCanonicalLocation(const char* dex_location); + // Initializes the LG Almond encryption library, if available. + static void InitLGAlmond(); + private: // Opens a .dex file static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f6ae236a0b9..379ccd33cb7 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -779,6 +779,8 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) } } + DexFile::InitLGAlmond(); + java_vm_ = new JavaVMExt(this, options.get()); Thread::Startup(); From cab8cec7fd85114b9ae77cc22ba2bcd17c8cc453 Mon Sep 17 00:00:00 2001 From: Jie Cheng Date: Fri, 10 Jul 2015 16:27:11 +0800 Subject: [PATCH 136/150] ART: Tuning compiler thread count for application runtime dex2oat decides the number of compiler threads to be spawned based on the number of cores in the system. Using this max possible compiler threads causes over parallelization on some platforms and there by leads to longer install times. Reducing to fewer threads on these platforms, improves this time by 60% to 100%. Some APPs will call dex2oat at the first launch time(runtime). The thread count tuning is needed for such scenario. Change-Id: Id3a545f0300f93397627beeb697a7b7cfd912d52 --- runtime/class_linker.cc | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 59117350c61..bf631f4fed3 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -72,6 +72,10 @@ #include "verifier/method_verifier.h" #include "well_known_classes.h" +#ifdef HAVE_ANDROID_OS +#include "cutils/properties.h" +#endif + namespace art { static void ThrowNoClassDefFoundError(const char* fmt, ...) @@ -663,6 +667,17 @@ bool ClassLinker::GenerateOatFile(const char* dex_filename, argv.push_back(compiler_options[i].c_str()); } +#ifdef HAVE_ANDROID_OS + const char* propertyName = "ro.sys.fw.dex2oat_thread_count"; + char count[PROPERTY_VALUE_MAX]; + if (property_get(propertyName, count, "") > 0) { + std::string thread_count("-j"); + StringAppendF(&thread_count, "%s", count); + argv.push_back(thread_count); + LOG(INFO) << "Adjust thread count for runtime dex2oat"; + } +#endif + 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. From b5d32b6c556a2d1d4579c1a534515f9f4b993851 Mon Sep 17 00:00:00 2001 From: Park Ju Hyung Date: Fri, 7 Aug 2015 22:22:25 +0900 Subject: [PATCH 137/150] Revert "[Xposed] Support for LG Almond encrypted apps" This reverts commit 31df113fd46d4561daee6d33a15e2cf5fe27de71. Signed-off-by: Park Ju Hyung Conflicts: runtime/dex_file.cc runtime/dex_file.h --- runtime/dex_file.cc | 45 --------------------------------------------- runtime/dex_file.h | 3 --- runtime/runtime.cc | 2 -- 3 files changed, 50 deletions(-) diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 9cf41358f1a..9d835f4abcf 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -16,7 +16,6 @@ #include "dex_file.h" -#include #include #include #include @@ -323,35 +322,6 @@ bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& loca } } -typedef int (*Almond_IsDRMDexFn)(const void*, size_t); -typedef int (*Almond_CopyDexToMemFn)(void*, size_t, size_t*, uint8_t*, uint8_t*); -static const uint32_t kLGAlmondFormatDex = 1; -static Almond_IsDRMDexFn Almond_IsDRMDex = nullptr; -static Almond_CopyDexToMemFn Almond_CopyDexToMem = nullptr; - -void DexFile::InitLGAlmond() { - if (!OS::FileExists("/system/lib/liblgalmond.so")) { - return; - } - - void* dlopen_handle = dlopen("liblgalmond.so", RTLD_NOW); - if (dlopen_handle == nullptr) { - LOG(ERROR) << "Could not load liblgalmond.so: " << dlerror(); - return; - } - - Almond_CopyDexToMem = reinterpret_cast(dlsym(dlopen_handle, "Almond_CopyDexToMem")); - if (Almond_CopyDexToMem == nullptr) { - LOG(ERROR) << "Could not locate Almond_CopyDexToMem: " << dlerror(); - return; - } - - Almond_IsDRMDex = reinterpret_cast(dlsym(dlopen_handle, "Almond_Is_DRMDex")); - if (Almond_IsDRMDex == nullptr) { - LOG(ERROR) << "Could not locate Almond_Is_DRMDex: " << dlerror(); - return; - } -} const DexFile* DexFile::OpenMemory(const byte* base, size_t size, @@ -361,21 +331,6 @@ const DexFile* DexFile::OpenMemory(const byte* base, const OatFile* oat_file, std::string* error_msg) { CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned - - if (Almond_IsDRMDex != nullptr && Almond_IsDRMDex(base, size) == kLGAlmondFormatDex) { - if ((mem_map->GetProtect() & PROT_WRITE) == 0) { - LOG(ERROR) << "Could not decrypt " << location << " because it's in read-only memory"; - return nullptr; - } - - uint8_t cid_hash[20]; - uint8_t preload_id[20]; - if (Almond_CopyDexToMem(const_cast(base), size, &size, cid_hash, preload_id) != 0) { - LOG(ERROR) << "Failed to decrypt " << location << " with LG Almond"; - return nullptr; - } - } - std::unique_ptr dex_file( new DexFile(base, size, location, location_checksum, mem_map, oat_file)); if (!dex_file->Init(error_msg)) { diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 84ff23b0563..060562b3462 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -895,9 +895,6 @@ class DexFile { return oat_file_; } - // Initializes the LG Almond encryption library, if available. - static void InitLGAlmond(); - private: // Opens a .dex file static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index cfd1853d494..0ad45c01bb6 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -795,8 +795,6 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) } } - DexFile::InitLGAlmond(); - java_vm_ = new JavaVMExt(this, options.get()); Thread::Startup(); From 8020cbc908d3d2b5203f9e65f6f0d263595da687 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 7 Aug 2015 19:49:07 +0200 Subject: [PATCH 138/150] Revert "[Xposed] Support for LG Almond encrypted apps" This reverts commit 31df113fd46d4561daee6d33a15e2cf5fe27de71. --- runtime/dex_file.cc | 45 --------------------------------------------- runtime/dex_file.h | 3 --- runtime/runtime.cc | 2 -- 3 files changed, 50 deletions(-) diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index 8f8622b4e8b..c783ee40176 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -16,7 +16,6 @@ #include "dex_file.h" -#include #include #include #include @@ -322,35 +321,6 @@ bool DexFile::OpenFromZip(const ZipArchive& zip_archive, const std::string& loca } } -typedef int (*Almond_IsDRMDexFn)(const void*, size_t); -typedef int (*Almond_CopyDexToMemFn)(void*, size_t, size_t*, uint8_t*, uint8_t*); -static const uint32_t kLGAlmondFormatDex = 1; -static Almond_IsDRMDexFn Almond_IsDRMDex = nullptr; -static Almond_CopyDexToMemFn Almond_CopyDexToMem = nullptr; - -void DexFile::InitLGAlmond() { - if (!OS::FileExists("/system/lib/liblgalmond.so")) { - return; - } - - void* dlopen_handle = dlopen("liblgalmond.so", RTLD_NOW); - if (dlopen_handle == nullptr) { - LOG(ERROR) << "Could not load liblgalmond.so: " << dlerror(); - return; - } - - Almond_CopyDexToMem = reinterpret_cast(dlsym(dlopen_handle, "Almond_CopyDexToMem")); - if (Almond_CopyDexToMem == nullptr) { - LOG(ERROR) << "Could not locate Almond_CopyDexToMem: " << dlerror(); - return; - } - - Almond_IsDRMDex = reinterpret_cast(dlsym(dlopen_handle, "Almond_Is_DRMDex")); - if (Almond_IsDRMDex == nullptr) { - LOG(ERROR) << "Could not locate Almond_Is_DRMDex: " << dlerror(); - return; - } -} const DexFile* DexFile::OpenMemory(const byte* base, size_t size, @@ -358,21 +328,6 @@ const DexFile* DexFile::OpenMemory(const byte* base, uint32_t location_checksum, MemMap* mem_map, std::string* error_msg) { CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned - - if (Almond_IsDRMDex != nullptr && Almond_IsDRMDex(base, size) == kLGAlmondFormatDex) { - if ((mem_map->GetProtect() & PROT_WRITE) == 0) { - LOG(ERROR) << "Could not decrypt " << location << " because it's in read-only memory"; - return nullptr; - } - - uint8_t cid_hash[20]; - uint8_t preload_id[20]; - if (Almond_CopyDexToMem(const_cast(base), size, &size, cid_hash, preload_id) != 0) { - LOG(ERROR) << "Failed to decrypt " << location << " with LG Almond"; - return nullptr; - } - } - std::unique_ptr dex_file(new DexFile(base, size, location, location_checksum, mem_map)); if (!dex_file->Init(error_msg)) { return nullptr; diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 217c6d5c4b5..1306f115d2b 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -887,9 +887,6 @@ class DexFile { // the dex_location where it's file name part has been made canonical. static std::string GetDexCanonicalLocation(const char* dex_location); - // Initializes the LG Almond encryption library, if available. - static void InitLGAlmond(); - private: // Opens a .dex file static const DexFile* OpenFile(int fd, const char* location, bool verify, std::string* error_msg); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 379ccd33cb7..f6ae236a0b9 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -779,8 +779,6 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) } } - DexFile::InitLGAlmond(); - java_vm_ = new JavaVMExt(this, options.get()); Thread::Startup(); From 3785207f7f84144f83eda853aad459a7c4a1545a Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 7 Aug 2015 20:06:52 +0200 Subject: [PATCH 139/150] [Xposed] Support for LG Almond encrypted apps (2nd attempt) LG has encrypted a few apps with their DRM implementation. Those apps can usually not be loaded. Fortunately, the decryption takes place in a separate library called liblgalmond.so. Xposed now uses this library if available to check if a dex file (usually located in a ZIP archive) is encrypted, and if yes, triggers the in-place decryption of the file before initializing it. For precompiled/odex files, a different way of decrypting is required. Similar to dex files, the decryption can also be done on the fly when the file is loaded. Fixes rovo89/Xposed#27. --- runtime/Android.mk | 1 + runtime/dex_file.cc | 14 ++++++ runtime/lgalmond.cc | 111 ++++++++++++++++++++++++++++++++++++++++++++ runtime/lgalmond.h | 39 ++++++++++++++++ runtime/oat_file.cc | 9 ++++ runtime/runtime.cc | 3 ++ 6 files changed, 177 insertions(+) create mode 100644 runtime/lgalmond.cc create mode 100644 runtime/lgalmond.h diff --git a/runtime/Android.mk b/runtime/Android.mk index 688a432cdd2..b3eda05fa59 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -87,6 +87,7 @@ LIBART_COMMON_SRC_FILES := \ jdwp/jdwp_request.cc \ jdwp/jdwp_socket.cc \ jdwp/object_registry.cc \ + lgalmond.cc \ jni_internal.cc \ jobject_comparator.cc \ mem_map.cc \ diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc index c783ee40176..7e4abed4e1d 100644 --- a/runtime/dex_file.cc +++ b/runtime/dex_file.cc @@ -32,6 +32,7 @@ #include "dex_file_verifier.h" #include "globals.h" #include "leb128.h" +#include "lgalmond.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/string.h" @@ -328,6 +329,19 @@ const DexFile* DexFile::OpenMemory(const byte* base, uint32_t location_checksum, MemMap* mem_map, std::string* error_msg) { CHECK_ALIGNED(base, 4); // various dex file structures must be word aligned + + if (UNLIKELY(LGAlmond::IsEncryptedDex(base, size))) { + if ((mem_map->GetProtect() & PROT_WRITE) == 0) { + LOG(ERROR) << "Could not decrypt " << location << " because it's in read-only memory"; + return nullptr; + } + + if (!LGAlmond::DecryptDex(const_cast(base), &size)) { + LOG(ERROR) << "Failed to decrypt " << location << " with LG Almond"; + return nullptr; + } + } + std::unique_ptr dex_file(new DexFile(base, size, location, location_checksum, mem_map)); if (!dex_file->Init(error_msg)) { return nullptr; diff --git a/runtime/lgalmond.cc b/runtime/lgalmond.cc new file mode 100644 index 00000000000..d114a111e3f --- /dev/null +++ b/runtime/lgalmond.cc @@ -0,0 +1,111 @@ +#include "lgalmond.h" + +#include +#include + +#include "base/logging.h" +#include "base/stringprintf.h" +#include "base/unix_file/fd_file.h" +#include "oat.h" + +namespace art { + +const uint8_t LGAlmond::kOatMagic[] = { 'a', 'l', 'm', 'd' }; +const uint8_t LGAlmond::kOarmBrand[] = { 0, 0, 0, 20, 'o', 'a', 'r', 'm', 'o', 'a', 't', '\n', 0, 0, 0, 1, 'o', 'a', 't', '\n' }; + +LGAlmond::IsDRMDexFn LGAlmond::IsDRMDex_ = nullptr; +LGAlmond::CopyDexToMemFn LGAlmond::CopyDexToMem_ = nullptr; +LGAlmond::DecOatFn LGAlmond::DecOat_ = nullptr; + +void LGAlmond::Init() { + if (!OS::FileExists("/system/lib/liblgalmond.so")) { + return; + } + + void* dlopen_handle = dlopen("liblgalmond.so", RTLD_NOW); + if (dlopen_handle == nullptr) { + LOG(ERROR) << "Could not load liblgalmond.so: " << dlerror(); + return; + } + + CopyDexToMem_ = reinterpret_cast(dlsym(dlopen_handle, "Almond_CopyDexToMem")); + if (CopyDexToMem_ == nullptr) { + LOG(ERROR) << "Could not locate Almond_CopyDexToMem: " << dlerror(); + return; + } + + IsDRMDex_ = reinterpret_cast(dlsym(dlopen_handle, "Almond_Is_DRMDex")); + if (IsDRMDex_ == nullptr) { + LOG(ERROR) << "Could not locate Almond_Is_DRMDex: " << dlerror(); + return; + } + + DecOat_ = reinterpret_cast(dlsym(dlopen_handle, "Almond_DecOat")); + if (DecOat_ == nullptr) { + LOG(ERROR) << "Could not locate Almond_DecOat: " << dlerror(); + return; + } +} + +bool LGAlmond::IsEncryptedDex(const void* data, size_t size) { + return IsDRMDex_ != nullptr && IsDRMDex_(data, size) == kFormatDex; +} + +bool LGAlmond::DecryptDex(void* data, size_t* size) { + uint8_t cid_hash[20]; + uint8_t preload_id[20]; + return CopyDexToMem_(data, *size, size, cid_hash, preload_id) == 0; +} + +bool LGAlmond::IsEncryptedOat(const void* data) { + return memcmp(data, LGAlmond::kOatMagic, 4) == 0; +} + +bool LGAlmond::DecryptOat(void* data, const File& file, std::string* error_msg) { + if (DecOat_ == nullptr) { + *error_msg = "LG Almond library was not loaded correctly"; + return false; + } + + struct { + uint8_t brand[20]; + uint32_t ignored1; + uint8_t bind_id[20]; + uint8_t hashed_cid[20]; + uint32_t protected_offset; + uint32_t protected_length; + uint32_t almond_offset; + uint32_t almond_length; + uint32_t rodata_offset; + uint32_t rodata_length; + } almond; + + if (file.Read(reinterpret_cast(&almond), sizeof(almond), file.GetLength() - 0x80) != sizeof(almond)) { + *error_msg = "Could not read LG Almond structure"; + return false; + } + + if (memcmp(almond.brand, kOarmBrand, sizeof(kOarmBrand)) != 0) { + *error_msg = "Invalid LG Almond branding"; + return false; + } + + if (mprotect(data, be32toh(almond.rodata_length), PROT_READ | PROT_WRITE) != 0) { + *error_msg = StringPrintf("Could not make memory writable: %s", strerror(errno)); + return false; + } + + byte* protected_data = reinterpret_cast(data); + protected_data += be32toh(almond.protected_offset) - be32toh(almond.rodata_offset); + + if (DecOat_(protected_data, be32toh(almond.protected_length), almond.bind_id, almond.hashed_cid) == 0) { + memcpy(data, OatHeader::kOatMagic, sizeof(OatHeader::kOatMagic)); + mprotect(data, be32toh(almond.rodata_length), PROT_READ); + return true; + } else { + *error_msg = "LG Almond decryption failed"; + return false; + } +} + +} // namespace art diff --git a/runtime/lgalmond.h b/runtime/lgalmond.h new file mode 100644 index 00000000000..8cf7c69c3c4 --- /dev/null +++ b/runtime/lgalmond.h @@ -0,0 +1,39 @@ +#ifndef ART_RUNTIME_LGALMOND_H_ +#define ART_RUNTIME_LGALMOND_H_ + +#include + +#include "globals.h" +#include "os.h" + +namespace art { + +class LGAlmond { + public: + static const uint8_t kOatMagic[4]; + static const uint8_t kOarmBrand[20]; + + // Initializes the LG Almond encryption library, if available. + static void Init(); + + static bool IsEncryptedDex(const void* data, size_t size); + static bool DecryptDex(void* data, size_t* size); + static bool IsEncryptedOat(const void* data); + static bool DecryptOat(void* data, const File& file, std::string* error_msg); + + private: + static const uint32_t kFormatDex = 1; + + typedef int (*IsDRMDexFn)(const void*, size_t); + typedef int (*CopyDexToMemFn)(void*, size_t, size_t*, uint8_t*, uint8_t*); + typedef int (*DecOatFn)(void*, size_t, uint8_t*, uint8_t*); + + static IsDRMDexFn IsDRMDex_; + static CopyDexToMemFn CopyDexToMem_; + static DecOatFn DecOat_; + +}; + +} // namespace art + +#endif // ART_RUNTIME_LGALMOND_H_ diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index af68530a742..e9a816e99d3 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -26,6 +26,7 @@ #include "base/unix_file/fd_file.h" #include "elf_file.h" #include "elf_utils.h" +#include "lgalmond.h" #include "oat.h" #include "mirror/art_method.h" #include "mirror/art_method-inl.h" @@ -50,6 +51,14 @@ OatFile* OatFile::OpenWithElfFile(ElfFile* elf_file, Elf32_Shdr* hdr = elf_file->FindSectionByName(".rodata"); oat_file->begin_ = elf_file->Begin() + hdr->sh_offset; oat_file->end_ = elf_file->Begin() + hdr->sh_size + hdr->sh_offset; + + if (UNLIKELY(LGAlmond::IsEncryptedOat(oat_file->begin_))) { + LOG(INFO) << "LG Almond Protected OAT"; + if (!LGAlmond::DecryptOat(const_cast(oat_file->begin_), elf_file->GetFile(), error_msg)) { + return false; + } + } + return oat_file->Setup(error_msg) ? oat_file.release() : nullptr; } diff --git a/runtime/runtime.cc b/runtime/runtime.cc index f6ae236a0b9..332ff099b5d 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -56,6 +56,7 @@ #include "instrumentation.h" #include "intern_table.h" #include "jni_internal.h" +#include "lgalmond.h" #include "mirror/art_field-inl.h" #include "mirror/art_method-inl.h" #include "mirror/array.h" @@ -779,6 +780,8 @@ bool Runtime::Init(const RuntimeOptions& raw_options, bool ignore_unrecognized) } } + LGAlmond::Init(); + java_vm_ = new JavaVMExt(this, options.get()); Thread::Startup(); From 7a50b3876889978a8a46e3267d2a8c7212575fd8 Mon Sep 17 00:00:00 2001 From: Park Ju Hyung Date: Fri, 7 Aug 2015 22:36:01 +0900 Subject: [PATCH 140/150] Revert "Force building art with clang" This reverts commit cf4003c51f938b2db01c83c58d74615449081133. Signed-off-by: Park Ju Hyung --- build/Android.common_build.mk | 12 ++++++------ build/Android.executable.mk | 4 +--- compiler/Android.mk | 1 - dex2oat/Android.mk | 3 --- runtime/Android.mk | 2 -- 5 files changed, 7 insertions(+), 15 deletions(-) diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 06afc9a590a..65b36ef74e9 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -116,12 +116,12 @@ ifneq ($(WITHOUT_HOST_CLANG),true) endif # Clang on the target. Target builds use GCC by default. -ART_TARGET_CLANG := true -ART_TARGET_CLANG_arm := true -ART_TARGET_CLANG_arm64 := true -ART_TARGET_CLANG_mips := true -ART_TARGET_CLANG_x86 := true -ART_TARGET_CLANG_x86_64 := true +ART_TARGET_CLANG := +ART_TARGET_CLANG_arm := +ART_TARGET_CLANG_arm64 := +ART_TARGET_CLANG_mips := +ART_TARGET_CLANG_x86 := +ART_TARGET_CLANG_x86_64 := define set-target-local-clang-vars LOCAL_CLANG := $(ART_TARGET_CLANG) diff --git a/build/Android.executable.mk b/build/Android.executable.mk index 9beeb00ea11..6fd6b2bfff7 100644 --- a/build/Android.executable.mk +++ b/build/Android.executable.mk @@ -19,7 +19,7 @@ include art/build/Android.common_build.mk ART_HOST_EXECUTABLES ?= ART_TARGET_EXECUTABLES ?= -ART_EXECUTABLES_CFLAGS := -Wframe-larger-than=4096 +ART_EXECUTABLES_CFLAGS := ifeq ($(ART_USE_PORTABLE_COMPILER),true) ART_EXECUTABLES_CFLAGS += -DART_USE_PORTABLE_COMPILER=1 endif @@ -66,7 +66,6 @@ define build-art-executable else #debug LOCAL_MODULE := $$(art_executable)d endif - LOCAL_CLANG := true LOCAL_CFLAGS := $(ART_EXECUTABLES_CFLAGS) # Mac OS linker doesn't understand --export-dynamic/--version-script. @@ -102,7 +101,6 @@ define build-art-executable LOCAL_MODULE_TARGET_ARCH := $(ART_SUPPORTED_ARCH) endif LOCAL_MULTILIB := $$(art_multilib) - LOCAL_CFLAGS += -Wframe-larger-than=4096 include external/libcxx/libcxx.mk ifeq ($$(art_target_or_host),target) diff --git a/compiler/Android.mk b/compiler/Android.mk index b1663a2d151..350d5a962a1 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -194,7 +194,6 @@ define build-libart-compiler LOCAL_SHARED_LIBRARIES += libartd endif - LOCAL_CLANG := true LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := SHARED_LIBRARIES diff --git a/dex2oat/Android.mk b/dex2oat/Android.mk index 12edecc00a9..c24f6d6a3b1 100644 --- a/dex2oat/Android.mk +++ b/dex2oat/Android.mk @@ -29,9 +29,6 @@ else dex2oat_arch := 32 endif -LOCAL_CLANG := true -LOCAL_CFLAGS := -Wframe-larger-than=4096 - ifeq ($(ART_BUILD_TARGET_NDEBUG),true) $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),libcutils libart-compiler,art/compiler,target,ndebug,$(dex2oat_arch))) endif diff --git a/runtime/Android.mk b/runtime/Android.mk index 889f8d1bd23..d31fa5cfd18 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -357,7 +357,6 @@ define build-libart LOCAL_MODULE := libartd endif - LOCAL_CLANG := true LOCAL_MODULE_TAGS := optional LOCAL_MODULE_CLASS := SHARED_LIBRARIES @@ -461,7 +460,6 @@ $$(ENUM_OPERATOR_OUT_GEN): $$(GENERATED_SRC_DIR)/%_operator_out.cc : $(LOCAL_PAT # produce meaningful name resolution. LOCAL_STRIP_MODULE := keep_symbols endif - LOCAL_CLANG := true include $$(BUILD_SHARED_LIBRARY) else # host include $$(BUILD_HOST_SHARED_LIBRARY) From dbddae0dd526b80a1f479bf59a22a1b1ec51bff2 Mon Sep 17 00:00:00 2001 From: Andreas Gampe Date: Fri, 10 Apr 2015 19:57:29 -0700 Subject: [PATCH 141/150] ART: Ignore result for exception-case JNIEndWithReference The value may not contain a valid jobject, so ignore and use null directly. Refactor a bit to have one common function for both synchronized and non-synchronized case. Add a test to the JNI compiler tests. Bug: 18135031 Change-Id: If2f004a112f36f4ff68172a946dec67ce561ae4d Conflicts: compiler/jni/jni_compiler_test.cc --- compiler/jni/jni_compiler_test.cc | 15 ++++++++ .../quick/quick_jni_entrypoints.cc | 35 ++++++++----------- test/MyClassNatives/MyClassNatives.java | 1 + 3 files changed, 31 insertions(+), 20 deletions(-) diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index ed1175b48bc..9be294bfc90 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -168,6 +168,7 @@ class JniCompilerTest : public CommonCompilerTest { void CheckParameterAlignImpl(); void MaxParamNumberImpl(); void WithoutImplementationImpl(); + void WithoutImplementationRefReturnImpl(); void StackArgsIntsFirstImpl(); void StackArgsFloatsFirstImpl(); void StackArgsMixedImpl(); @@ -1530,6 +1531,20 @@ void JniCompilerTest::WithoutImplementationImpl() { JNI_TEST(WithoutImplementation) +void JniCompilerTest::WithoutImplementationRefReturnImpl() { + // This will lead to error messages in the log. + ScopedLogSeverity sls(LogSeverity::FATAL); + + SetUpForTest(false, "withoutImplementationRefReturn", "()Ljava/lang/Object;", nullptr); + + env_->CallObjectMethod(jobj_, jmethod_); + + EXPECT_TRUE(Thread::Current()->IsExceptionPending()); + EXPECT_TRUE(env_->ExceptionCheck() == JNI_TRUE); +} + +JNI_TEST(WithoutImplementationRefReturn) + void Java_MyClassNatives_stackArgsIntsFirst(JNIEnv* env, jclass klass, jint i1, jint i2, jint i3, jint i4, jint i5, jint i6, jint i7, jint i8, jint i9, jint i10, jfloat f1, jfloat f2, jfloat f3, jfloat f4, diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc index 653724989a4..dd83b682ad2 100644 --- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc @@ -73,7 +73,6 @@ extern void JniMethodEnd(uint32_t saved_local_ref_cookie, Thread* self) { PopLocalReferences(saved_local_ref_cookie, self); } - extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject locked, Thread* self) { GoToRunnable(self); @@ -81,38 +80,34 @@ extern void JniMethodEndSynchronized(uint32_t saved_local_ref_cookie, jobject lo PopLocalReferences(saved_local_ref_cookie, self); } -extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie, - Thread* self) { - GoToRunnable(self); - mirror::Object* o = self->DecodeJObject(result); // Must decode before pop. +// Common result handling for EndWithReference. +static mirror::Object* JniMethodEndWithReferenceHandleResult(jobject result, + uint32_t saved_local_ref_cookie, + Thread* self) + NO_THREAD_SAFETY_ANALYSIS { + // Must decode before pop. The 'result' may not be valid in case of an exception, though. + mirror::Object* o = self->IsExceptionPending() ? nullptr : self->DecodeJObject(result); PopLocalReferences(saved_local_ref_cookie, self); // Process result. if (UNLIKELY(self->GetJniEnv()->check_jni)) { - if (self->IsExceptionPending()) { - return NULL; - } CheckReferenceResult(o, self); } VerifyObject(o); return o; } +extern mirror::Object* JniMethodEndWithReference(jobject result, uint32_t saved_local_ref_cookie, + Thread* self) { + GoToRunnable(self); + return JniMethodEndWithReferenceHandleResult(result, saved_local_ref_cookie, self); +} + extern mirror::Object* JniMethodEndWithReferenceSynchronized(jobject result, uint32_t saved_local_ref_cookie, jobject locked, Thread* self) { GoToRunnable(self); - UnlockJniSynchronizedMethod(locked, self); // Must decode before pop. - mirror::Object* o = self->DecodeJObject(result); - PopLocalReferences(saved_local_ref_cookie, self); - // Process result. - if (UNLIKELY(self->GetJniEnv()->check_jni)) { - if (self->IsExceptionPending()) { - return NULL; - } - CheckReferenceResult(o, self); - } - VerifyObject(o); - return o; + UnlockJniSynchronizedMethod(locked, self); + return JniMethodEndWithReferenceHandleResult(result, saved_local_ref_cookie, self); } } // namespace art diff --git a/test/MyClassNatives/MyClassNatives.java b/test/MyClassNatives/MyClassNatives.java index fab153b6247..8b4a9a4a31b 100644 --- a/test/MyClassNatives/MyClassNatives.java +++ b/test/MyClassNatives/MyClassNatives.java @@ -80,6 +80,7 @@ native void maxParamNumber(Object o0, Object o1, Object o2, Object o3, Object o4 Object o248, Object o249, Object o250, Object o251, Object o252, Object o253); native void withoutImplementation(); + native Object withoutImplementationRefReturn(); native static void stackArgsIntsFirst(int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8, int i9, int i10, float f1, float f2, float f3, float f4, float f5, float f6, From 84b8b4eefd7375b680065c6c8228e8b6857b4134 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Sun, 16 Aug 2015 09:54:48 +0200 Subject: [PATCH 142/150] [Xposed] Disable accessing TypedArray subclass fields via offset Xposed modifies the size of TypedArray objects to match those of XTypedArray objects in order to change the class of created objects later. This leads to issues when other apps create their own subclass of TypedArray with additional fields. When compiling the app, the compiler doesn't know that the TypedArray object will be a few bytes bigger, so the offsets of the additional fields differ between runtime and compile time. If the compiler optimizes the access to these fields by addressing them via their offsets, it will lead to crashes. Therefore, we can't allow this kind of optimization for subclasses of TypedArray. Fixes rovo89/Xposed#54. --- compiler/driver/compiler_driver-inl.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 022ec6bc0d0..a579ed59430 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -97,6 +97,15 @@ inline std::pair CompilerDriver::IsFastInstanceField( mirror::ArtField* resolved_field, uint16_t field_idx) { DCHECK(!resolved_field->IsStatic()); mirror::Class* fields_class = resolved_field->GetDeclaringClass(); + // Keep these classes in sync with prepareSubclassReplacement() calls in libxposed-art. + mirror::Class* super_class = fields_class->GetSuperClass(); + while (super_class != nullptr) { + if (super_class->DescriptorEquals("Landroid/content/res/TypedArray;")) { + VLOG(compiler) << "Preventing fast access to " << PrettyField(resolved_field); + return std::make_pair(false, false); + } + super_class = super_class->GetSuperClass(); + } bool fast_get = referrer_class != nullptr && referrer_class->CanAccessResolvedField(fields_class, resolved_field, dex_cache, field_idx); From 111109bd76898e240752e80ac0309645ae89b4a3 Mon Sep 17 00:00:00 2001 From: Park Ju Hyung Date: Tue, 18 Aug 2015 16:37:35 +0900 Subject: [PATCH 143/150] Fix oatdump build 1800 is still in safety threshold. Signed-off-by: Park Ju Hyung --- build/Android.common_build.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 65b36ef74e9..db3611fcbcb 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -195,7 +195,7 @@ art_target_non_debug_cflags := \ ifeq ($(HOST_OS),linux) # Larger frame-size for host clang builds today art_host_non_debug_cflags += -Wframe-larger-than=3000 - art_target_non_debug_cflags += -Wframe-larger-than=1728 + art_target_non_debug_cflags += -Wframe-larger-than=1800 endif # FIXME: upstream LLVM has a vectorizer bug that needs to be fixed From a48946a4a28d4910628ce2bf4456d758359c4368 Mon Sep 17 00:00:00 2001 From: Park Ju Hyung Date: Sat, 29 Aug 2015 23:46:42 +0900 Subject: [PATCH 144/150] Revert "Add clamp growth limit" This reverts commit ed551ed5cff73472d1ef4804cf361780bf2509a7. Signed-off-by: Park Ju Hyung --- runtime/gc/accounting/space_bitmap.h | 6 ------ runtime/gc/heap.cc | 14 -------------- runtime/gc/heap.h | 6 +----- runtime/gc/space/malloc_space.cc | 10 ---------- runtime/gc/space/malloc_space.h | 4 ---- runtime/mem_map.cc | 13 ------------- runtime/mem_map.h | 3 --- runtime/native/dalvik_system_VMRuntime.cc | 5 ----- 8 files changed, 1 insertion(+), 60 deletions(-) diff --git a/runtime/gc/accounting/space_bitmap.h b/runtime/gc/accounting/space_bitmap.h index 73d18f6d62d..f72b30f81b7 100644 --- a/runtime/gc/accounting/space_bitmap.h +++ b/runtime/gc/accounting/space_bitmap.h @@ -160,12 +160,6 @@ class SpaceBitmap { return IndexToOffset(Size() / kWordSize); } - void SetHeapSize(size_t bytes) { - // TODO: Un-map the end of the mem map. - bitmap_size_ = OffsetToIndex(bytes) * sizeof(intptr_t); - CHECK_EQ(HeapSize(), bytes); - } - uintptr_t HeapBegin() const { return heap_begin_; } diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index c267bffb1a2..db65a69280b 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -2997,20 +2997,6 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran) { } } -void Heap::ClampGrowthLimit() { - capacity_ = growth_limit_; - for (const auto& space : continuous_spaces_) { - if (space->IsMallocSpace()) { - gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); - malloc_space->ClampGrowthLimit(); - } - } - // This space isn't added for performance reasons. - if (main_space_backup_.get() != nullptr) { - main_space_backup_->ClampGrowthLimit(); - } -} - void Heap::ClearGrowthLimit() { growth_limit_ = capacity_; for (const auto& space : continuous_spaces_) { diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 5d83efc8ab0..530ec187e85 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -291,10 +291,6 @@ class Heap { // implement dalvik.system.VMRuntime.clearGrowthLimit. void ClearGrowthLimit(); - // Make the current growth limit the new maximum capacity, unmaps pages at the end of spaces - // which will never be used. Used to implement dalvik.system.VMRuntime.clampGrowthLimit. - void ClampGrowthLimit(); - // Target ideal heap utilization ratio, implements // dalvik.system.VMRuntime.getTargetHeapUtilization. double GetTargetHeapUtilization() const { @@ -877,7 +873,7 @@ class Heap { collector::GcType next_gc_type_; // Maximum size that the heap can reach. - size_t capacity_; + const size_t capacity_; // The size the heap is limited to. This is initially smaller than capacity, but for largeHeap // programs it is "cleared" making it the same as capacity. diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index ee94214e8d9..ba7e5c1eca0 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -247,16 +247,6 @@ void MallocSpace::SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* ar context->freed.bytes += space->FreeList(self, num_ptrs, ptrs); } -void MallocSpace::ClampGrowthLimit() { - size_t new_capacity = Capacity(); - CHECK_LE(new_capacity, NonGrowthLimitCapacity()); - GetLiveBitmap()->SetHeapSize(new_capacity); - GetMarkBitmap()->SetHeapSize(new_capacity); - GetMemMap()->SetSize(new_capacity); - limit_ = Begin() + new_capacity; - CHECK(temp_bitmap_.get() == nullptr); -} - } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index 74cd94508c3..bace3f6e634 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -110,10 +110,6 @@ class MallocSpace : public ContinuousMemMapAllocSpace { return GetMemMap()->Size(); } - // Change the non growth limit capacity by shrinking or expanding the map. Currently, only - // shrinking is supported. - void ClampGrowthLimit(); - void Dump(std::ostream& os) const; void SetGrowthLimit(size_t growth_limit); diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc index 952b18ff1e4..bad86db2c2c 100644 --- a/runtime/mem_map.cc +++ b/runtime/mem_map.cc @@ -659,19 +659,6 @@ void MemMap::Shutdown() { maps_ = nullptr; } -void MemMap::SetSize(size_t new_size) { - if (new_size == base_size_) { - return; - } - CHECK_ALIGNED(new_size, kPageSize); - CHECK_EQ(base_size_, size_) << "Unsupported"; - CHECK_LE(new_size, base_size_); - CHECK_EQ(munmap(reinterpret_cast(reinterpret_cast(BaseBegin()) + new_size), - base_size_ - new_size), 0) << new_size << " " << base_size_; - base_size_ = new_size; - size_ = new_size; -} - std::ostream& operator<<(std::ostream& os, const MemMap& mem_map) { os << StringPrintf("[MemMap: %p-%p prot=0x%x %s]", mem_map.BaseBegin(), mem_map.BaseEnd(), mem_map.GetProtect(), diff --git a/runtime/mem_map.h b/runtime/mem_map.h index 807291dc914..ad62f83cb87 100644 --- a/runtime/mem_map.h +++ b/runtime/mem_map.h @@ -107,9 +107,6 @@ class MemMap { return size_; } - // Resize the mem-map by unmapping pages at the end. Currently only supports shrinking. - void SetSize(size_t new_size); - byte* End() const { return Begin() + Size(); } diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc index 235e92008df..9fbb9385d81 100644 --- a/runtime/native/dalvik_system_VMRuntime.cc +++ b/runtime/native/dalvik_system_VMRuntime.cc @@ -126,10 +126,6 @@ static void VMRuntime_clearGrowthLimit(JNIEnv*, jobject) { Runtime::Current()->GetHeap()->ClearGrowthLimit(); } -static void VMRuntime_clampGrowthLimit(JNIEnv*, jobject) { - Runtime::Current()->GetHeap()->ClampGrowthLimit(); -} - static jboolean VMRuntime_isDebuggerActive(JNIEnv*, jobject) { return Dbg::IsDebuggerActive(); } @@ -551,7 +547,6 @@ static jstring VMRuntime_getCurrentInstructionSet(JNIEnv* env, jclass) { static JNINativeMethod gMethods[] = { NATIVE_METHOD(VMRuntime, addressOf, "!(Ljava/lang/Object;)J"), NATIVE_METHOD(VMRuntime, bootClassPath, "()Ljava/lang/String;"), - NATIVE_METHOD(VMRuntime, clampGrowthLimit, "()V"), NATIVE_METHOD(VMRuntime, classPath, "()Ljava/lang/String;"), NATIVE_METHOD(VMRuntime, clearGrowthLimit, "()V"), NATIVE_METHOD(VMRuntime, concurrentGC, "()V"), From 479821fb24bb30c51d4b48492151930b3fcf2d52 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 28 Aug 2015 13:02:52 +0100 Subject: [PATCH 145/150] samsung: add TW Roms checker --- runtime/utils.cc | 10 ++++++++++ runtime/utils.h | 2 ++ 2 files changed, 12 insertions(+) diff --git a/runtime/utils.cc b/runtime/utils.cc index 2c7f0402ed9..adc701f96f8 100644 --- a/runtime/utils.cc +++ b/runtime/utils.cc @@ -1388,4 +1388,14 @@ std::string PrettyDescriptor(Primitive::Type type) { return PrettyDescriptor(Primitive::Descriptor(type)); } +bool IsSamsungROM() { + static bool checked = false; + static bool result = false; + if (!checked) { + result = OS::FileExists("/system/framework/twframework.jar"); + checked = true; + } + return result; +} + } // namespace art diff --git a/runtime/utils.h b/runtime/utils.h index e6235ad5a3e..c2bf395c463 100644 --- a/runtime/utils.h +++ b/runtime/utils.h @@ -557,6 +557,8 @@ struct FreeDelete { template using UniqueCPtr = std::unique_ptr; +bool IsSamsungROM(); + } // namespace art #endif // ART_RUNTIME_UTILS_H_ From 38149c01de4abd27ba75977ecd9db884ccbf3036 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 28 Aug 2015 13:07:20 +0100 Subject: [PATCH 146/150] samsung: oat_file - adjust methods_offsets_pointer to point --- runtime/oat_file.cc | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index 671bb277426..4b76452c8b8 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -321,6 +321,22 @@ bool OatFile::Setup(std::string* error_msg) { const DexFile::Header* header = reinterpret_cast(dex_file_pointer); const uint32_t* methods_offsets_pointer = reinterpret_cast(oat); + /* + * Samsung has introduced a TypeLookupTable for each dex file in their oat files. + * Its relative offset is stored between the dex_file_offset and the methods_offsets_pointer. + * The lookup tables themselves are stored between the OatDexFiles (which we are currently + * iterating over) and the DexFiles. The OatClasses are stored further down the file. + * + * This means that on Samsung ROMs (with files created by Samsung's adjusted libart-compiler.so), + * methods_offsets_pointer[0] will actually hold the offset of the TypeLookupTable, which + * is lower than the DexFile. If we detect this case, we skip (and ignore) the value and + * adjust methods_offsets_pointer to point to the correct address. + */ + if (methods_offsets_pointer[0] < dex_file_offset) { + oat += sizeof(uint32_t); + methods_offsets_pointer = reinterpret_cast(oat); + } + oat += (sizeof(*methods_offsets_pointer) * header->class_defs_size_); if (UNLIKELY(oat > End())) { *error_msg = StringPrintf("In oat file '%s' found OatDexFile #%zd for '%s' with truncated " From 06eea3c0fd8ff99a698cd082f2a9ffdd527a509a Mon Sep 17 00:00:00 2001 From: Karami Mohamed Date: Fri, 28 Aug 2015 13:08:40 +0100 Subject: [PATCH 147/150] Revert "Implement String.clear() to the runtime" This reverts commit e16ffe92bf73907a377f0fc7049a715732d50df8. --- runtime/mirror/string-inl.h | 2 +- runtime/mirror/string.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index 85a26cc2244..d9241417854 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -29,7 +29,7 @@ namespace art { namespace mirror { inline uint32_t String::ClassSize() { - uint32_t vtable_entries = Object::kVTableLength + 52; + uint32_t vtable_entries = Object::kVTableLength + 51; return Class::ComputeClassSize(true, vtable_entries, 1, 1, 2); } diff --git a/runtime/mirror/string.h b/runtime/mirror/string.h index d833ec806d2..631c19bcf5f 100644 --- a/runtime/mirror/string.h +++ b/runtime/mirror/string.h @@ -73,8 +73,6 @@ class MANAGED String FINAL : public Object { uint16_t CharAt(int32_t index) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void clear() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - String* Intern() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); static String* AllocFromUtf16(Thread* self, From f51c9f017d7863ca07f729d43faa686b58b0d45f Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 28 Aug 2015 13:12:01 +0100 Subject: [PATCH 148/150] samsung: increase String VTableLenght to support String.clear() --- runtime/mirror/string-inl.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h index d9241417854..7bd029cd7a2 100644 --- a/runtime/mirror/string-inl.h +++ b/runtime/mirror/string-inl.h @@ -30,6 +30,11 @@ namespace mirror { inline uint32_t String::ClassSize() { uint32_t vtable_entries = Object::kVTableLength + 51; + if (IsSamsungROM()) { + // Samsung added a new method "string.clear()" + // Increase vtable length to support it + vtable_entries++; + } return Class::ComputeClassSize(true, vtable_entries, 1, 1, 2); } From 71146e0be626001c4344c94dae7f4939a334773c Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 28 Aug 2015 13:15:55 +0100 Subject: [PATCH 149/150] samsung: adjust dexcache offsets to support 2 new fileds --- runtime/mirror/dex_cache.cc | 6 +++--- runtime/mirror/dex_cache.h | 31 ++++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/runtime/mirror/dex_cache.cc b/runtime/mirror/dex_cache.cc index d6c11e8b0c1..9a49fa9e372 100644 --- a/runtime/mirror/dex_cache.cc +++ b/runtime/mirror/dex_cache.cc @@ -44,10 +44,10 @@ void DexCache::Init(const DexFile* dex_file, CHECK(resolved_methods != nullptr); CHECK(resolved_fields != nullptr); - SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, location_), location); + SetFieldPtr(DexFileOffset(), dex_file); + SetFieldObject(LocationOffset(), location); SetFieldObject(StringsOffset(), strings); - SetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_), resolved_types); + SetFieldObject(ResolvedTypesOffset(), resolved_types); SetFieldObject(ResolvedMethodsOffset(), resolved_methods); SetFieldObject(ResolvedFieldsOffset(), resolved_fields); diff --git a/runtime/mirror/dex_cache.h b/runtime/mirror/dex_cache.h index 3c947ab37bc..ddc41d35cb9 100644 --- a/runtime/mirror/dex_cache.h +++ b/runtime/mirror/dex_cache.h @@ -22,6 +22,7 @@ #include "class.h" #include "object.h" #include "object_array.h" +#include "utils.h" namespace art { @@ -41,8 +42,8 @@ class MANAGED DexCache FINAL : public Object { static uint32_t ClassSize(); // Size of an instance of java.lang.DexCache not including referenced values. - static constexpr uint32_t InstanceSize() { - return sizeof(DexCache); + static uint32_t InstanceSize() { + return sizeof(DexCache) + (IsSamsungROM() ? 8 : 0); } void Init(const DexFile* dex_file, @@ -56,19 +57,31 @@ class MANAGED DexCache FINAL : public Object { void Fixup(ArtMethod* trampoline) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); String* GetLocation() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldObject(OFFSET_OF_OBJECT_MEMBER(DexCache, location_)); + return GetFieldObject(LocationOffset()); + } + + static MemberOffset LocationOffset() { + return MemberOffset(OFFSETOF_MEMBER(DexCache, location_) + (IsSamsungROM() ? 4 : 0)); } static MemberOffset StringsOffset() { - return OFFSET_OF_OBJECT_MEMBER(DexCache, strings_); + return MemberOffset(OFFSETOF_MEMBER(DexCache, strings_) + (IsSamsungROM() ? 4 : 0)); } static MemberOffset ResolvedFieldsOffset() { - return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_fields_); + return MemberOffset(OFFSETOF_MEMBER(DexCache, resolved_fields_) + (IsSamsungROM() ? 4 : 0)); } static MemberOffset ResolvedMethodsOffset() { - return OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_methods_); + return MemberOffset(OFFSETOF_MEMBER(DexCache, resolved_methods_) + (IsSamsungROM() ? 4 : 0)); + } + + static MemberOffset ResolvedTypesOffset() { + return MemberOffset(OFFSETOF_MEMBER(DexCache, resolved_types_) + (IsSamsungROM() ? 4 : 0)); + } + + static MemberOffset DexFileOffset() { + return MemberOffset(OFFSETOF_MEMBER(DexCache, dex_file_) + (IsSamsungROM() ? 8 : 0)); } size_t NumStrings() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -133,7 +146,7 @@ class MANAGED DexCache FINAL : public Object { ObjectArray* GetResolvedTypes() ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return GetFieldObject>( - OFFSET_OF_OBJECT_MEMBER(DexCache, resolved_types_)); + ResolvedTypesOffset()); } ObjectArray* GetResolvedMethods() ALWAYS_INLINE @@ -147,12 +160,12 @@ class MANAGED DexCache FINAL : public Object { } const DexFile* GetDexFile() ALWAYS_INLINE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return GetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_)); + return GetFieldPtr(DexFileOffset()); } void SetDexFile(const DexFile* dex_file) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) ALWAYS_INLINE { - return SetFieldPtr(OFFSET_OF_OBJECT_MEMBER(DexCache, dex_file_), dex_file); + return SetFieldPtr(DexFileOffset(), dex_file); } private: From d2c136c68bc05715f799a343e2e4dcd2d8eca351 Mon Sep 17 00:00:00 2001 From: rovo89 Date: Fri, 28 Aug 2015 13:18:52 +0100 Subject: [PATCH 150/150] samsung: runtime - add samsung native methods --- runtime/Android.mk | 1 + runtime/native/samsung.cc | 273 ++++++++++++++++++++++++++++++++++++++ runtime/runtime.cc | 1 + 3 files changed, 275 insertions(+) create mode 100644 runtime/native/samsung.cc diff --git a/runtime/Android.mk b/runtime/Android.mk index d31fa5cfd18..6bd99b27d37 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -128,6 +128,7 @@ LIBART_COMMON_SRC_FILES := \ native/java_util_concurrent_atomic_AtomicLong.cc \ native/org_apache_harmony_dalvik_ddmc_DdmServer.cc \ native/org_apache_harmony_dalvik_ddmc_DdmVmInternal.cc \ + native/samsung.cc \ native/sun_misc_Unsafe.cc \ oat.cc \ oat_file.cc \ diff --git a/runtime/native/samsung.cc b/runtime/native/samsung.cc new file mode 100644 index 00000000000..ff7617182d2 --- /dev/null +++ b/runtime/native/samsung.cc @@ -0,0 +1,273 @@ +#include "field_helper.h" +#include "mirror/art_field-inl.h" +#include "mirror/art_method-inl.h" +#include "mirror/class-inl.h" +#include "scoped_fast_native_object_access.h" +#include "ScopedUtfChars.h" + +namespace art { + +//---------------------------------------------------- +// java.lang.Class + +static bool equalMethodParameters(mirror::ArtMethod* method, mirror::ObjectArray* parameterTypes) { + const DexFile::TypeList* params = method->GetParameterTypeList(); + if (params == nullptr) + return (parameterTypes->GetLength() == 0); + + int32_t numParams = params->Size(); + if (numParams != parameterTypes->GetLength()) + return false; + + for (int32_t i = 0; i < numParams; i++) { + uint16_t type_idx = params->GetTypeItem(i).type_idx_; + mirror::Class* param_type = method->GetDexCacheResolvedType(type_idx); + if (param_type == nullptr) { + param_type = Runtime::Current()->GetClassLinker()->ResolveType(type_idx, method); + CHECK(param_type != nullptr || Thread::Current()->IsExceptionPending()); + } + + if (param_type != parameterTypes->Get(i)) + return false; + } + + return true; +} + +static mirror::ArtMethod* getDeclaredMethodInternal(mirror::Class* c, const StringPiece& name, mirror::ObjectArray* parameterTypes) { + mirror::ArtMethod* potentialResult = nullptr; + + for (size_t i = 0; i < c->NumVirtualMethods(); i++) { + mirror::ArtMethod* method = c->GetVirtualMethod(i); + + if (name != method->GetName()) + continue; + + if (!equalMethodParameters(method, parameterTypes)) + continue; + + if (!method->IsMiranda()) { + if (!method->IsSynthetic()) + return method; + + // Remember as potential result if it's not a miranda method. + potentialResult = method; + } + } + + for (size_t i = 0; i < c->NumDirectMethods(); i++) { + mirror::ArtMethod* method = c->GetDirectMethod(i); + if (method->IsConstructor()) + continue; + + if (name != method->GetName()) + continue; + + if (!equalMethodParameters(method, parameterTypes)) + continue; + + if (!method->IsMiranda() && !method->IsSynthetic()) + return method; + + // Direct methods cannot be miranda methods, + // so this potential result must be synthetic. + potentialResult = method; + } + + return potentialResult; +} + +static mirror::ArtMethod* getPublicMethodRecursive(mirror::Class* c, const StringPiece& name, mirror::ObjectArray* parameterTypes) { + // search superclasses + for (mirror::Class* klass = c; klass != nullptr; klass = klass->GetSuperClass()) { + mirror::ArtMethod* result = getDeclaredMethodInternal(klass, name, parameterTypes); + if (result != nullptr && result->IsPublic()) + return result; + } + + // search iftable which has a flattened and uniqued list of interfaces + int32_t iftable_count = c->GetIfTableCount(); + mirror::IfTable* iftable = c->GetIfTable(); + for (int32_t i = 0; i < iftable_count; i++) { + mirror::ArtMethod* result = getPublicMethodRecursive(iftable->GetInterface(i), name, parameterTypes); + if (result != nullptr && result->IsPublic()) + return result; + } + + return nullptr; +} + +static jobject Class_getMethodNative(JNIEnv* env, jobject javaThis, jstring javaName, jobjectArray javaParameterTypes, jboolean recursivePublicMethods) { + ScopedObjectAccess soa(env); + if (UNLIKELY(javaName == nullptr)) { + ThrowNullPointerException(nullptr, "name == null"); + return nullptr; + } + + mirror::ObjectArray* parameterTypes = + soa.Decode*>(javaParameterTypes); + size_t numParameterTypes = parameterTypes->GetLength(); + for (size_t i = 0; i < numParameterTypes; i++) { + if (parameterTypes->Get(i) == nullptr) { + Thread* self = Thread::Current(); + ThrowLocation computed_throw_location = self->GetCurrentLocationForThrow(); + self->ThrowNewException(computed_throw_location, "Ljava/lang/NoSuchMethodException;", "parameter type is null"); + return nullptr; + } + } + + mirror::Class* const c = soa.Decode(javaThis); + ScopedUtfChars name(env, javaName); + + mirror::ArtMethod* method = (recursivePublicMethods == JNI_TRUE) ? + getPublicMethodRecursive(c, name.c_str(), parameterTypes) + : getDeclaredMethodInternal(c, name.c_str(), parameterTypes); + + if (method == nullptr) + return nullptr; + + jobject artMethod = soa.AddLocalReference(method); + jobject reflectMethod = env->AllocObject(WellKnownClasses::java_lang_reflect_Method); + if (env->ExceptionCheck()) + return nullptr; + + env->SetObjectField(reflectMethod, WellKnownClasses::java_lang_reflect_AbstractMethod_artMethod, artMethod); + return reflectMethod; +} + +static mirror::ArtField* getDeclaredFieldInternal(mirror::Class* c, const StringPiece& name) { + for (size_t i = 0; i < c->NumInstanceFields(); ++i) { + mirror::ArtField* f = c->GetInstanceField(i); + if (name == f->GetName()) { + return f; + } + } + + for (size_t i = 0; i < c->NumStaticFields(); ++i) { + mirror::ArtField* f = c->GetStaticField(i); + if (name == f->GetName()) { + return f; + } + } + + return nullptr; +} + +static mirror::ArtField* getPublicFieldRecursive(mirror::Class* c, const StringPiece& name) { + // search superclasses + for (mirror::Class* klass = c; klass != nullptr; klass = klass->GetSuperClass()) { + mirror::ArtField* result = getDeclaredFieldInternal(klass, name); + if (result != nullptr && result->IsPublic()) + return result; + } + + // search iftable which has a flattened and uniqued list of interfaces + int32_t iftable_count = c->GetIfTableCount(); + mirror::IfTable* iftable = c->GetIfTable(); + for (int32_t i = 0; i < iftable_count; i++) { + mirror::ArtField* result = getPublicFieldRecursive(iftable->GetInterface(i), name); + if (result != nullptr && result->IsPublic()) + return result; + } + + return nullptr; +} + +static jobject getDeclaredOrRecursiveField(JNIEnv* env, jobject javaThis, jstring javaName, bool recursiveFieldMethods) { + ScopedObjectAccess soa(env); + ScopedUtfChars name(env, javaName); + mirror::Class* const c = soa.Decode(javaThis); + + mirror::ArtField* field = recursiveFieldMethods ? + getPublicFieldRecursive(c, name.c_str()) + : getDeclaredFieldInternal(c, name.c_str()); + + if (field == nullptr) + return nullptr; + + jobject artField = soa.AddLocalReference(field); + jobject reflectField = env->AllocObject(WellKnownClasses::java_lang_reflect_Field); + if (env->ExceptionCheck()) + return nullptr; + + env->SetObjectField(reflectField, WellKnownClasses::java_lang_reflect_Field_artField, artField); + return reflectField; +} + +static jobject Class_getFieldNative(JNIEnv* env, jobject javaThis, jstring javaName) { + return getDeclaredOrRecursiveField(env, javaThis, javaName, true); +} + +static jobject Class_getDeclaredFieldInternalNative(JNIEnv* env, jobject javaThis, jstring javaName) { + return getDeclaredOrRecursiveField(env, javaThis, javaName, false); +} + + +//---------------------------------------------------- +// java.lang.reflect.ArtField + +static jobject ArtField_getNameNative(JNIEnv* env, jobject javaThis) { + ScopedFastNativeObjectAccess soa(env); + mirror::ArtField* const f = soa.Decode(javaThis)->AsArtField(); + return env->NewStringUTF(f->GetName()); +} + +static jclass ArtField_getTypeNative(JNIEnv* env, jobject javaThis) { + ScopedFastNativeObjectAccess soa(env); + StackHandleScope<1> hs(soa.Self()); + Handle f(hs.NewHandle(soa.Decode(javaThis)->AsArtField())); + return soa.AddLocalReference(FieldHelper(f).GetType()); +} + + +//---------------------------------------------------- +// java.lang.reflect.ArtMethod + +static jobject ArtMethod_getNameNative(JNIEnv* env, jobject javaThis) { + ScopedFastNativeObjectAccess soa(env); + mirror::ArtMethod* const f = soa.Decode(javaThis)->AsArtMethod(); + return env->NewStringUTF(f->GetName()); +} + +//---------------------------------------------------- +// dalvik.system.PathClassLoader + +static jobject PathClassLoader_openNative(JNIEnv* env, jobject javaThis) { + // Ignore Samsung native method and use the default PathClassLoader constructor + return nullptr; +} + + +//---------------------------------------------------- +static JNINativeMethod gMethodsClass[] = { + NATIVE_METHOD(Class, getMethodNative, "(Ljava/lang/String;[Ljava/lang/Class;Z)Ljava/lang/reflect/Method;"), + NATIVE_METHOD(Class, getFieldNative, "(Ljava/lang/String;)Ljava/lang/reflect/Field;"), + NATIVE_METHOD(Class, getDeclaredFieldInternalNative, "(Ljava/lang/String;)Ljava/lang/reflect/Field;"), +}; + +static JNINativeMethod gMethodsArtField[] = { + NATIVE_METHOD(ArtField, getNameNative, "!()Ljava/lang/String;"), + NATIVE_METHOD(ArtField, getTypeNative, "!()Ljava/lang/Class;"), +}; + +static JNINativeMethod gMethodsArtMethod[] = { + NATIVE_METHOD(ArtMethod, getNameNative, "!()Ljava/lang/String;"), +}; + +static JNINativeMethod gMethodsPathClassLoader[] = { + NATIVE_METHOD(PathClassLoader, openNative, "!(Ljava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)Ldalvik/system/PathClassLoader;"), +}; + + +//---------------------------------------------------- +void register_samsung_native_methods(JNIEnv* env) { + if (!IsSamsungROM()) + return; + + RegisterNativeMethods(env, "java/lang/Class", gMethodsClass, arraysize(gMethodsClass)); + RegisterNativeMethods(env, "java/lang/reflect/ArtField", gMethodsArtField, arraysize(gMethodsArtField)); + RegisterNativeMethods(env, "java/lang/reflect/ArtMethod", gMethodsArtMethod, arraysize(gMethodsArtMethod)); + RegisterNativeMethods(env, "dalvik/system/PathClassLoader", gMethodsPathClassLoader, arraysize(gMethodsPathClassLoader)); +} + +} // namespace art diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 0ad45c01bb6..e2790be7d14 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -999,6 +999,7 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) { REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmServer); REGISTER(register_org_apache_harmony_dalvik_ddmc_DdmVmInternal); REGISTER(register_sun_misc_Unsafe); + REGISTER(register_samsung_native_methods); #undef REGISTER }