summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nicolas Geoffray <ngeoffray@google.com> 2022-03-04 13:23:59 +0000
committer Nicolas Geoffray <ngeoffray@google.com> 2022-03-08 09:32:34 +0000
commit6ec990559f0da11e0b31a9aa6fda7e224a580bda (patch)
tree8486975b15f27b3823544fc30c98938b945c4126
parent1f5b158f5c111f3c81ec374e0aead39273e2f94e (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.cc8
-rw-r--r--runtime/base/locks.h2
-rw-r--r--runtime/class_linker.h2
-rw-r--r--runtime/entrypoints/jni/jni_entrypoints.cc6
-rw-r--r--runtime/jni/java_vm_ext.cc55
-rw-r--r--runtime/jni/java_vm_ext.h2
-rw-r--r--runtime/runtime.cc41
-rwxr-xr-xtest/139-register-natives/check2
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