diff options
author | 2022-03-04 13:23:59 +0000 | |
---|---|---|
committer | 2022-03-08 09:32:34 +0000 | |
commit | 6ec990559f0da11e0b31a9aa6fda7e224a580bda (patch) | |
tree | 8486975b15f27b3823544fc30c98938b945c4126 | |
parent | 1f5b158f5c111f3c81ec374e0aead39273e2f94e (diff) |
Eagerly loopkup native symbols in zygote.
To avoid forked apps dirtying ArtMethods in the boot image.
Test: imgdiag
Bug: 162110941
Change-Id: Ib2be1b4e5ea55d010c5a795f9b24cb65b2e4474d
-rw-r--r-- | runtime/base/locks.cc | 8 | ||||
-rw-r--r-- | runtime/base/locks.h | 2 | ||||
-rw-r--r-- | runtime/class_linker.h | 2 | ||||
-rw-r--r-- | runtime/entrypoints/jni/jni_entrypoints.cc | 6 | ||||
-rw-r--r-- | runtime/jni/java_vm_ext.cc | 55 | ||||
-rw-r--r-- | runtime/jni/java_vm_ext.h | 2 | ||||
-rw-r--r-- | runtime/runtime.cc | 41 | ||||
-rwxr-xr-x | test/139-register-natives/check | 2 |
8 files changed, 82 insertions, 36 deletions
diff --git a/runtime/base/locks.cc b/runtime/base/locks.cc index e53007316a..b375fb4b24 100644 --- a/runtime/base/locks.cc +++ b/runtime/base/locks.cc @@ -216,10 +216,6 @@ void Locks::Init() { DCHECK(thread_list_lock_ == nullptr); thread_list_lock_ = new Mutex("thread list lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kJniLoadLibraryLock); - DCHECK(jni_libraries_lock_ == nullptr); - jni_libraries_lock_ = new Mutex("JNI shared libraries map lock", current_lock_level); - UPDATE_CURRENT_LOCK_LEVEL(kBreakpointLock); DCHECK(breakpoint_lock_ == nullptr); breakpoint_lock_ = new ReaderWriterMutex("breakpoint lock", current_lock_level); @@ -255,6 +251,10 @@ void Locks::Init() { DCHECK(dex_cache_lock_ == nullptr); dex_cache_lock_ = new Mutex("DexCache lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kJniLoadLibraryLock); + DCHECK(jni_libraries_lock_ == nullptr); + jni_libraries_lock_ = new Mutex("JNI shared libraries map lock", current_lock_level); + UPDATE_CURRENT_LOCK_LEVEL(kOatFileManagerLock); DCHECK(oat_file_manager_lock_ == nullptr); oat_file_manager_lock_ = new ReaderWriterMutex("OatFile manager lock", current_lock_level); diff --git a/runtime/base/locks.h b/runtime/base/locks.h index 52d196b30d..4449cd0a78 100644 --- a/runtime/base/locks.h +++ b/runtime/base/locks.h @@ -95,6 +95,7 @@ enum LockLevel : uint8_t { kOatFileManagerLock, kTracingUniqueMethodsLock, kTracingStreamingLock, + kJniLoadLibraryLock, kClassLoaderClassesLock, kDefaultMutexLevel, kDexCacheLock, @@ -113,7 +114,6 @@ enum LockLevel : uint8_t { kPostMonitorLock, kMonitorLock, kMonitorListLock, - kJniLoadLibraryLock, kThreadListLock, kAllocTrackerLock, kDeoptimizationLock, diff --git a/runtime/class_linker.h b/runtime/class_linker.h index a503946634..dfa18ccc43 100644 --- a/runtime/class_linker.h +++ b/runtime/class_linker.h @@ -787,7 +787,7 @@ class ClassLinker { // Registers the native method and returns the new entry point. NB The returned entry point // might be different from the native_method argument if some MethodCallback modifies it. const void* RegisterNative(Thread* self, ArtMethod* method, const void* native_method) - REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED; + REQUIRES_SHARED(Locks::mutator_lock_); // Unregister native code for a method. void UnregisterNative(Thread* self, ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_); diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index ddc6839014..3860042427 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -108,9 +108,11 @@ extern "C" const void* artFindNativeMethodRunnable(Thread* self) // Lookup symbol address for method, on failure we'll return null with an exception set, // otherwise we return the address of the method we found. JavaVMExt* vm = down_cast<JNIEnvExt*>(self->GetJniEnv())->GetVm(); - native_code = vm->FindCodeForNativeMethod(method); + std::string error_msg; + native_code = vm->FindCodeForNativeMethod(method, &error_msg, /*can_suspend=*/ true); if (native_code == nullptr) { - self->AssertPendingException(); + LOG(ERROR) << error_msg; + self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", error_msg.c_str()); return nullptr; } diff --git a/runtime/jni/java_vm_ext.cc b/runtime/jni/java_vm_ext.cc index f4a47ebcb4..f41b6c06fa 100644 --- a/runtime/jni/java_vm_ext.cc +++ b/runtime/jni/java_vm_ext.cc @@ -30,6 +30,7 @@ #include "base/systrace.h" #include "check_jni.h" #include "dex/dex_file-inl.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "fault_handler.h" #include "gc/allocation_record.h" #include "gc/heap.h" @@ -270,34 +271,42 @@ class Libraries { } // See section 11.3 "Linking Native Methods" of the JNI spec. - void* FindNativeMethod(Thread* self, ArtMethod* m, std::string& detail) + void* FindNativeMethod(Thread* self, ArtMethod* m, std::string* detail, bool can_suspend) REQUIRES(!Locks::jni_libraries_lock_) REQUIRES_SHARED(Locks::mutator_lock_) { std::string jni_short_name(m->JniShortName()); std::string jni_long_name(m->JniLongName()); const ObjPtr<mirror::ClassLoader> declaring_class_loader = m->GetDeclaringClass()->GetClassLoader(); - ScopedObjectAccessUnchecked soa(Thread::Current()); void* const declaring_class_loader_allocator = Runtime::Current()->GetClassLinker()->GetAllocatorForClassLoader(declaring_class_loader); CHECK(declaring_class_loader_allocator != nullptr); // TODO: Avoid calling GetShorty here to prevent dirtying dex pages? const char* shorty = m->GetShorty(); - { + void* native_code = nullptr; + if (can_suspend) { // Go to suspended since dlsym may block for a long time if other threads are using dlopen. ScopedThreadSuspension sts(self, ThreadState::kNative); - void* native_code = FindNativeMethodInternal(self, - declaring_class_loader_allocator, - shorty, - jni_short_name, - jni_long_name); - if (native_code != nullptr) { - return native_code; - } + native_code = FindNativeMethodInternal(self, + declaring_class_loader_allocator, + shorty, + jni_short_name, + jni_long_name); + } else { + native_code = FindNativeMethodInternal(self, + declaring_class_loader_allocator, + shorty, + jni_short_name, + jni_long_name); + } + if (native_code != nullptr) { + return native_code; + } + if (detail != nullptr) { + *detail += "No implementation found for "; + *detail += m->PrettyMethod(); + *detail += " (tried " + jni_short_name + " and " + jni_long_name + ")"; } - detail += "No implementation found for "; - detail += m->PrettyMethod(); - detail += " (tried " + jni_short_name + " and " + jni_long_name + ")"; return nullptr; } @@ -306,8 +315,7 @@ class Libraries { const char* shorty, const std::string& jni_short_name, const std::string& jni_long_name) - REQUIRES(!Locks::jni_libraries_lock_) - REQUIRES(!Locks::mutator_lock_) { + REQUIRES(!Locks::jni_libraries_lock_) { MutexLock mu(self, *Locks::jni_libraries_lock_); for (const auto& lib : libraries_) { SharedLibrary* const library = lib.second; @@ -1148,24 +1156,19 @@ static void* FindCodeForNativeMethodInAgents(ArtMethod* m) REQUIRES_SHARED(Locks return nullptr; } -void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m) { +void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m, std::string* error_msg, bool can_suspend) { CHECK(m->IsNative()); ObjPtr<mirror::Class> c = m->GetDeclaringClass(); // If this is a static method, it could be called before the class has been initialized. - CHECK(c->IsInitializing()) << c->GetStatus() << " " << m->PrettyMethod(); - std::string detail; + CHECK(c->IsInitializing() || !NeedsClinitCheckBeforeCall(m)) + << c->GetStatus() << " " << m->PrettyMethod(); Thread* const self = Thread::Current(); - void* native_method = libraries_->FindNativeMethod(self, m, detail); - if (native_method == nullptr) { + void* native_method = libraries_->FindNativeMethod(self, m, error_msg, can_suspend); + if (native_method == nullptr && can_suspend) { // Lookup JNI native methods from native TI Agent libraries. See runtime/ti/agent.h for more // information. Agent libraries are searched for native methods after all jni libraries. native_method = FindCodeForNativeMethodInAgents(m); } - // Throwing can cause libraries_lock to be reacquired. - if (native_method == nullptr) { - LOG(ERROR) << detail; - self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", detail.c_str()); - } return native_method; } diff --git a/runtime/jni/java_vm_ext.h b/runtime/jni/java_vm_ext.h index 8fa716e0ae..08de18b2ac 100644 --- a/runtime/jni/java_vm_ext.h +++ b/runtime/jni/java_vm_ext.h @@ -123,7 +123,7 @@ class JavaVMExt : public JavaVM { * Returns a pointer to the code for the native method 'm', found * using dlsym(3) on every native library that's been loaded so far. */ - void* FindCodeForNativeMethod(ArtMethod* m) + void* FindCodeForNativeMethod(ArtMethod* m, std::string* error_msg, bool can_suspend) REQUIRES_SHARED(Locks::mutator_lock_); void DumpForSigQuit(std::ostream& os) diff --git a/runtime/runtime.cc b/runtime/runtime.cc index b2fc369877..d0136cdaba 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -77,6 +77,7 @@ #include "dex/dex_file_loader.h" #include "elf_file.h" #include "entrypoints/runtime_asm_entrypoints.h" +#include "entrypoints/entrypoint_utils-inl.h" #include "experimental_flags.h" #include "fault_handler.h" #include "gc/accounting/card_table-inl.h" @@ -694,10 +695,50 @@ void Runtime::Abort(const char* msg) { // notreached } +class FindNativeMethodsVisitor : public ClassVisitor { + public: + FindNativeMethodsVisitor(Thread* self, ClassLinker* class_linker) + : vm_(down_cast<JNIEnvExt*>(self->GetJniEnv())->GetVm()), + self_(self), + class_linker_(class_linker) {} + + bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) { + bool is_initialized = klass->IsVisiblyInitialized(); + for (ArtMethod& method : klass->GetDeclaredMethods(kRuntimePointerSize)) { + if (method.IsNative() && (!NeedsClinitCheckBeforeCall(&method) || is_initialized)) { + const void* native_code = + vm_->FindCodeForNativeMethod(&method, /*error_msg=*/ nullptr, /*can_suspend=*/ false); + if (native_code != nullptr) { + class_linker_->RegisterNative(self_, &method, native_code); + } + } + } + return true; + } + + private: + JavaVMExt* vm_; + Thread* self_; + ClassLinker* class_linker_; + + DISALLOW_COPY_AND_ASSIGN(FindNativeMethodsVisitor); +}; + void Runtime::PreZygoteFork() { if (GetJit() != nullptr) { GetJit()->PreZygoteFork(); } + if (!heap_->HasZygoteSpace()) { + // This is the first fork. Update ArtMethods in the boot classpath now to + // avoid having forked apps dirty the memory. + ScopedObjectAccess soa(Thread::Current()); + // Ensure we call FixupStaticTrampolines on all methods that are + // initialized. + class_linker_->MakeInitializedClassesVisiblyInitialized(soa.Self(), /*wait=*/ true); + // Update native method JNI entrypoints. + FindNativeMethodsVisitor visitor(soa.Self(), class_linker_); + class_linker_->VisitClasses(&visitor); + } heap_->PreZygoteFork(); PreZygoteForkNativeBridge(); } diff --git a/test/139-register-natives/check b/test/139-register-natives/check index 098d3c5f4c..f9b35f60ca 100755 --- a/test/139-register-natives/check +++ b/test/139-register-natives/check @@ -21,7 +21,7 @@ # $4: Test's actual standard error # Strip any JNI registration error messages -sed -e '/java_vm_ext/d' -e '/jni_internal.cc/d' "$4" > "$4.tmp" +sed -e '/jni_entrypoints/d' -e '/jni_internal.cc/d' "$4" > "$4.tmp" diff --strip-trailing-cr -q "$1" "$2" >/dev/null \ && diff --strip-trailing-cr -q "$3" "$4.tmp" >/dev/null |