diff options
-rw-r--r-- | runtime/art_method.cc | 12 | ||||
-rw-r--r-- | runtime/art_method.h | 16 | ||||
-rw-r--r-- | runtime/entrypoints/jni/jni_entrypoints.cc | 7 | ||||
-rw-r--r-- | runtime/entrypoints/quick/quick_trampoline_entrypoints.cc | 6 | ||||
-rw-r--r-- | runtime/jni_internal.cc | 3 | ||||
-rw-r--r-- | runtime/openjdkjvmti/OpenjdkJvmTi.cc | 2 | ||||
-rw-r--r-- | runtime/openjdkjvmti/art_jvmti.h | 2 | ||||
-rw-r--r-- | runtime/openjdkjvmti/events-inl.h | 21 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_method.cc | 45 | ||||
-rw-r--r-- | runtime/openjdkjvmti/ti_method.h | 5 | ||||
-rw-r--r-- | runtime/runtime_callbacks.cc | 22 | ||||
-rw-r--r-- | runtime/runtime_callbacks.h | 14 | ||||
-rw-r--r-- | test/986-native-method-bind/expected.txt | 8 | ||||
-rw-r--r-- | test/986-native-method-bind/info.txt | 1 | ||||
-rw-r--r-- | test/986-native-method-bind/native_bind.cc | 110 | ||||
-rwxr-xr-x | test/986-native-method-bind/run | 17 | ||||
-rw-r--r-- | test/986-native-method-bind/src/Main.java | 21 | ||||
-rw-r--r-- | test/986-native-method-bind/src/art/Main.java | 28 | ||||
-rw-r--r-- | test/986-native-method-bind/src/art/Redefinition.java | 96 | ||||
-rw-r--r-- | test/986-native-method-bind/src/art/Test986.java | 82 | ||||
-rw-r--r-- | test/Android.bp | 1 |
21 files changed, 504 insertions, 15 deletions
diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 5a71be6eb9..76fdd43992 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -43,6 +43,7 @@ #include "mirror/object-inl.h" #include "mirror/string.h" #include "oat_file-inl.h" +#include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" #include "well_known_classes.h" @@ -372,20 +373,25 @@ void ArtMethod::Invoke(Thread* self, uint32_t* args, uint32_t args_size, JValue* self->PopManagedStackFragment(fragment); } -void ArtMethod::RegisterNative(const void* native_method, bool is_fast) { +const void* ArtMethod::RegisterNative(const void* native_method, bool is_fast) { CHECK(IsNative()) << PrettyMethod(); CHECK(!IsFastNative()) << PrettyMethod(); CHECK(native_method != nullptr) << PrettyMethod(); if (is_fast) { AddAccessFlags(kAccFastNative); } - SetEntryPointFromJni(native_method); + void* new_native_method = nullptr; + Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this, + native_method, + /*out*/&new_native_method); + SetEntryPointFromJni(new_native_method); + return new_native_method; } void ArtMethod::UnregisterNative() { CHECK(IsNative() && !IsFastNative()) << PrettyMethod(); // restore stub to lookup native pointer via dlsym - RegisterNative(GetJniDlsymLookupStub(), false); + SetEntryPointFromJni(GetJniDlsymLookupStub()); } bool ArtMethod::IsOverridableByDefaultMethod() { diff --git a/runtime/art_method.h b/runtime/art_method.h index 51b65760a1..b01b344bda 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -398,8 +398,10 @@ class ArtMethod FINAL { pointer_size); } - void RegisterNative(const void* native_method, bool is_fast) - REQUIRES_SHARED(Locks::mutator_lock_); + // 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(const void* native_method, bool is_fast) + REQUIRES_SHARED(Locks::mutator_lock_) WARN_UNUSED; void UnregisterNative() REQUIRES_SHARED(Locks::mutator_lock_); @@ -744,6 +746,16 @@ class ArtMethod FINAL { DISALLOW_COPY_AND_ASSIGN(ArtMethod); // Need to use CopyFrom to deal with 32 vs 64 bits. }; +class MethodCallback { + public: + virtual ~MethodCallback() {} + + virtual void RegisterNativeMethod(ArtMethod* method, + const void* original_implementation, + /*out*/void** new_implementation) + REQUIRES_SHARED(Locks::mutator_lock_) = 0; +}; + } // namespace art #endif // ART_RUNTIME_ART_METHOD_H_ diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc index fd23ced7f7..546e59d223 100644 --- a/runtime/entrypoints/jni/jni_entrypoints.cc +++ b/runtime/entrypoints/jni/jni_entrypoints.cc @@ -25,10 +25,10 @@ namespace art { // Used by the JNI dlsym stub to find the native method to invoke if none is registered. #if defined(__arm__) || defined(__aarch64__) -extern "C" void* artFindNativeMethod() { +extern "C" const void* artFindNativeMethod() { Thread* self = Thread::Current(); #else -extern "C" void* artFindNativeMethod(Thread* self) { +extern "C" const void* artFindNativeMethod(Thread* self) { DCHECK_EQ(self, Thread::Current()); #endif Locks::mutator_lock_->AssertNotHeld(self); // We come here as Native. @@ -45,8 +45,7 @@ extern "C" void* artFindNativeMethod(Thread* self) { return nullptr; } else { // Register so that future calls don't come here - method->RegisterNative(native_code, false); - return native_code; + return method->RegisterNative(native_code, false); } } diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc index 354ae205e8..2b349e39a0 100644 --- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc +++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc @@ -2025,9 +2025,9 @@ void BuildGenericJniFrameVisitor::FinalizeHandleScope(Thread* self) { } #if defined(__arm__) || defined(__aarch64__) -extern "C" void* artFindNativeMethod(); +extern "C" const void* artFindNativeMethod(); #else -extern "C" void* artFindNativeMethod(Thread* self); +extern "C" const void* artFindNativeMethod(Thread* self); #endif static uint64_t artQuickGenericJniEndJNIRef(Thread* self, @@ -2126,7 +2126,7 @@ extern "C" TwoWordReturn artQuickGenericJniTrampoline(Thread* self, ArtMethod** } // Retrieve the stored native code. - void* nativeCode = called->GetEntryPointFromJni(); + void const* nativeCode = called->GetEntryPointFromJni(); // There are two cases for the content of nativeCode: // 1) Pointer to the native function. diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc index 5418d3569e..b146b51033 100644 --- a/runtime/jni_internal.cc +++ b/runtime/jni_internal.cc @@ -2277,7 +2277,8 @@ class JNI { // TODO: make this a hard register error in the future. } - m->RegisterNative(fnPtr, is_fast); + const void* final_function_ptr = m->RegisterNative(fnPtr, is_fast); + UNUSED(final_function_ptr); } return JNI_OK; } diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc index 39e603e1e7..c3a94b93a0 100644 --- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc +++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc @@ -1556,6 +1556,7 @@ extern "C" bool ArtPlugin_Initialize() { ThreadUtil::Register(&gEventHandler); ClassUtil::Register(&gEventHandler); DumpUtil::Register(&gEventHandler); + MethodUtil::Register(&gEventHandler); SearchUtil::Register(); HeapUtil::Register(); @@ -1569,6 +1570,7 @@ extern "C" bool ArtPlugin_Deinitialize() { ThreadUtil::Unregister(); ClassUtil::Unregister(); DumpUtil::Unregister(); + MethodUtil::Unregister(); SearchUtil::Unregister(); HeapUtil::Unregister(); diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h index 2ff3a478c4..2a2aa4c199 100644 --- a/runtime/openjdkjvmti/art_jvmti.h +++ b/runtime/openjdkjvmti/art_jvmti.h @@ -223,7 +223,7 @@ const jvmtiCapabilities kPotentialCapabilities = { .can_generate_compiled_method_load_events = 0, .can_generate_monitor_events = 0, .can_generate_vm_object_alloc_events = 1, - .can_generate_native_method_bind_events = 0, + .can_generate_native_method_bind_events = 1, .can_generate_garbage_collection_events = 1, .can_generate_object_free_events = 1, .can_force_early_return = 0, diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h index 233b45cda8..57abf3142d 100644 --- a/runtime/openjdkjvmti/events-inl.h +++ b/runtime/openjdkjvmti/events-inl.h @@ -191,6 +191,27 @@ inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, A } } +// Need to give a custom specialization for NativeMethodBind since it has to deal with an out +// variable. +template <> +inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>(art::Thread* thread, + JNIEnv* jnienv, + jthread jni_thread, + jmethodID method, + void* cur_method, + void** new_method) const { + *new_method = cur_method; + for (ArtJvmTiEnv* env : envs) { + if (env != nullptr && ShouldDispatch<ArtJvmtiEvent::kNativeMethodBind>(env, thread)) { + auto callback = impl::GetCallback<ArtJvmtiEvent::kNativeMethodBind>(env); + (*callback)(env, jnienv, jni_thread, method, cur_method, new_method); + if (*new_method != nullptr) { + cur_method = *new_method; + } + } + } +} + // C++ does not allow partial template function specialization. The dispatch for our separated // ClassFileLoadHook event types is the same, and in the DispatchClassFileLoadHookEvent helper. // The following two DispatchEvent specializations dispatch to it. diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc index 01bf21d53e..2adabbaff4 100644 --- a/runtime/openjdkjvmti/ti_method.cc +++ b/runtime/openjdkjvmti/ti_method.cc @@ -35,14 +35,59 @@ #include "art_method-inl.h" #include "base/enums.h" #include "dex_file_annotations.h" +#include "events-inl.h" #include "jni_internal.h" #include "mirror/object_array-inl.h" #include "modifiers.h" +#include "runtime_callbacks.h" #include "scoped_thread_state_change-inl.h" +#include "ScopedLocalRef.h" #include "thread-inl.h" +#include "thread_list.h" namespace openjdkjvmti { +struct TiMethodCallback : public art::MethodCallback { + void RegisterNativeMethod(art::ArtMethod* method, + const void* cur_method, + /*out*/void** new_method) + OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kNativeMethodBind)) { + art::Thread* thread = art::Thread::Current(); + ScopedLocalRef<jthread> thread_jni( + thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer())); + art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative); + event_handler->DispatchEvent<ArtJvmtiEvent::kNativeMethodBind>( + thread, + static_cast<JNIEnv*>(thread->GetJniEnv()), + thread_jni.get(), + art::jni::EncodeArtMethod(method), + const_cast<void*>(cur_method), + new_method); + } + } + + EventHandler* event_handler = nullptr; +}; + +TiMethodCallback gMethodCallback; + +void MethodUtil::Register(EventHandler* handler) { + gMethodCallback.event_handler = handler; + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Add method callback"); + art::Runtime::Current()->GetRuntimeCallbacks()->AddMethodCallback(&gMethodCallback); +} + +void MethodUtil::Unregister() { + art::ScopedThreadStateChange stsc(art::Thread::Current(), + art::ThreadState::kWaitingForDebuggerToAttach); + art::ScopedSuspendAll ssa("Remove method callback"); + art::Runtime* runtime = art::Runtime::Current(); + runtime->GetRuntimeCallbacks()->RemoveMethodCallback(&gMethodCallback); +} + jvmtiError MethodUtil::GetArgumentsSize(jvmtiEnv* env ATTRIBUTE_UNUSED, jmethodID method, jint* size_ptr) { diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h index e5c1705ada..cc161c8fed 100644 --- a/runtime/openjdkjvmti/ti_method.h +++ b/runtime/openjdkjvmti/ti_method.h @@ -37,8 +37,13 @@ namespace openjdkjvmti { +class EventHandler; + class MethodUtil { public: + static void Register(EventHandler* event_handler); + static void Unregister(); + static jvmtiError GetArgumentsSize(jvmtiEnv* env, jmethodID method, jint* size_ptr); static jvmtiError GetMaxLocals(jvmtiEnv* env, jmethodID method, jint* max_ptr); diff --git a/runtime/runtime_callbacks.cc b/runtime/runtime_callbacks.cc index 25324b52d1..16d6c13722 100644 --- a/runtime/runtime_callbacks.cc +++ b/runtime/runtime_callbacks.cc @@ -18,6 +18,7 @@ #include <algorithm> +#include "art_method.h" #include "base/macros.h" #include "class_linker.h" #include "thread.h" @@ -131,4 +132,25 @@ void RuntimeCallbacks::NextRuntimePhase(RuntimePhaseCallback::RuntimePhase phase } } +void RuntimeCallbacks::AddMethodCallback(MethodCallback* cb) { + method_callbacks_.push_back(cb); +} + +void RuntimeCallbacks::RemoveMethodCallback(MethodCallback* cb) { + Remove(cb, &method_callbacks_); +} + +void RuntimeCallbacks::RegisterNativeMethod(ArtMethod* method, + const void* in_cur_method, + /*out*/void** new_method) { + void* cur_method = const_cast<void*>(in_cur_method); + *new_method = cur_method; + for (MethodCallback* cb : method_callbacks_) { + cb->RegisterNativeMethod(method, cur_method, new_method); + if (*new_method != nullptr) { + cur_method = *new_method; + } + } +} + } // namespace art diff --git a/runtime/runtime_callbacks.h b/runtime/runtime_callbacks.h index d321254e17..e8f1824262 100644 --- a/runtime/runtime_callbacks.h +++ b/runtime/runtime_callbacks.h @@ -31,8 +31,10 @@ class Class; class ClassLoader; } // namespace mirror +class ArtMethod; class ClassLoadCallback; class Thread; +class MethodCallback; class ThreadLifecycleCallback; // Note: RuntimeCallbacks uses the mutator lock to synchronize the callback lists. A thread must @@ -110,6 +112,14 @@ class RuntimeCallbacks { /*out*/DexFile::ClassDef const** final_class_def) REQUIRES_SHARED(Locks::mutator_lock_); + void AddMethodCallback(MethodCallback* cb) REQUIRES(Locks::mutator_lock_); + void RemoveMethodCallback(MethodCallback* cb) REQUIRES(Locks::mutator_lock_); + + void RegisterNativeMethod(ArtMethod* method, + const void* original_implementation, + /*out*/void** new_implementation) + REQUIRES_SHARED(Locks::mutator_lock_); + private: std::vector<ThreadLifecycleCallback*> thread_callbacks_ GUARDED_BY(Locks::mutator_lock_); @@ -118,7 +128,9 @@ class RuntimeCallbacks { std::vector<RuntimeSigQuitCallback*> sigquit_callbacks_ GUARDED_BY(Locks::mutator_lock_); std::vector<RuntimePhaseCallback*> phase_callbacks_ - GUARDED_BY(Locks::mutator_lock_); + GUARDED_BY(Locks::mutator_lock_); + std::vector<MethodCallback*> method_callbacks_ + GUARDED_BY(Locks::mutator_lock_); }; } // namespace art diff --git a/test/986-native-method-bind/expected.txt b/test/986-native-method-bind/expected.txt new file mode 100644 index 0000000000..189217d761 --- /dev/null +++ b/test/986-native-method-bind/expected.txt @@ -0,0 +1,8 @@ +private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi -> Java_art_Test986_00024Transform_sayHi +Hello +private static native void art.Test986$Transform.sayHi() = Java_art_Test986_00024Transform_sayHi -> NoReallySayGoodbye +Bye +public static native void art.Main.bindAgentJNI(java.lang.String,java.lang.ClassLoader) = Java_art_Main_bindAgentJNI -> Java_art_Main_bindAgentJNI +public static native void art.Main.bindAgentJNIForClass(java.lang.Class) = Java_art_Main_bindAgentJNIForClass -> Java_art_Main_bindAgentJNIForClass +private static native void art.Test986.setNativeBindNotify(boolean) = Java_art_Test986_setNativeBindNotify -> Java_art_Test986_setNativeBindNotify +private static native void art.Test986.setupNativeBindNotify() = Java_art_Test986_setupNativeBindNotify -> Java_art_Test986_setupNativeBindNotify diff --git a/test/986-native-method-bind/info.txt b/test/986-native-method-bind/info.txt new file mode 100644 index 0000000000..1939936ca9 --- /dev/null +++ b/test/986-native-method-bind/info.txt @@ -0,0 +1 @@ +Tests native-method-bind callback and native method replacement. diff --git a/test/986-native-method-bind/native_bind.cc b/test/986-native-method-bind/native_bind.cc new file mode 100644 index 0000000000..4f93f87dfe --- /dev/null +++ b/test/986-native-method-bind/native_bind.cc @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2017 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 <inttypes.h> +#include <memory> +#include <stdio.h> +#include <dlfcn.h> + +#include "android-base/stringprintf.h" +#include "jni.h" +#include "jvmti.h" + +// Test infrastructure +#include "jni_binder.h" +#include "jvmti_helper.h" +#include "test_env.h" +#include "scoped_local_ref.h" + +namespace art { +namespace Test986NativeBind { + +static void doUpPrintCall(JNIEnv* env, const char* function) { + ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986")); + jmethodID targetMethod = env->GetStaticMethodID(klass.get(), function, "()V"); + env->CallStaticVoidMethod(klass.get(), targetMethod); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test986_00024Transform_sayHi( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { + doUpPrintCall(env, "doSayHi"); +} + +extern "C" JNIEXPORT void JNICALL NoReallySayGoodbye(JNIEnv* env, jclass klass ATTRIBUTE_UNUSED) { + doUpPrintCall(env, "doSayBye"); +} + +static void doJvmtiMethodBind(jvmtiEnv* jvmtienv ATTRIBUTE_UNUSED, + JNIEnv* env, + jthread thread ATTRIBUTE_UNUSED, + jmethodID m, + void* address, + /*out*/void** out_address) { + ScopedLocalRef<jclass> method_class(env, env->FindClass("java/lang/reflect/Method")); + ScopedLocalRef<jobject> method_obj(env, env->ToReflectedMethod(method_class.get(), m, false)); + Dl_info addr_info; + if (dladdr(address, &addr_info) == 0 || addr_info.dli_sname == nullptr) { + ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception")); + env->ThrowNew(exception_class.get(), "dladdr failure!"); + return; + } + ScopedLocalRef<jstring> sym_name(env, env->NewStringUTF(addr_info.dli_sname)); + ScopedLocalRef<jclass> klass(env, env->FindClass("art/Test986")); + jmethodID upcallMethod = env->GetStaticMethodID( + klass.get(), + "doNativeMethodBind", + "(Ljava/lang/reflect/Method;Ljava/lang/String;)Ljava/lang/String;"); + if (env->ExceptionCheck()) { + return; + } + ScopedLocalRef<jstring> new_symbol(env, + reinterpret_cast<jstring>( + env->CallStaticObjectMethod(klass.get(), + upcallMethod, + method_obj.get(), + sym_name.get()))); + const char* new_symbol_chars = env->GetStringUTFChars(new_symbol.get(), nullptr); + if (strcmp(new_symbol_chars, addr_info.dli_sname) != 0) { + *out_address = dlsym(RTLD_DEFAULT, new_symbol_chars); + if (*out_address == nullptr) { + ScopedLocalRef<jclass> exception_class(env, env->FindClass("java/lang/Exception")); + env->ThrowNew(exception_class.get(), "dlsym failure!"); + return; + } + } + env->ReleaseStringUTFChars(new_symbol.get(), new_symbol_chars); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test986_setupNativeBindNotify( + JNIEnv* env ATTRIBUTE_UNUSED, jclass klass ATTRIBUTE_UNUSED) { + jvmtiEventCallbacks cb; + memset(&cb, 0, sizeof(cb)); + cb.NativeMethodBind = doJvmtiMethodBind; + jvmti_env->SetEventCallbacks(&cb, sizeof(cb)); +} + +extern "C" JNIEXPORT void JNICALL Java_art_Test986_setNativeBindNotify( + JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jboolean enable) { + jvmtiError res = jvmti_env->SetEventNotificationMode(enable ? JVMTI_ENABLE : JVMTI_DISABLE, + JVMTI_EVENT_NATIVE_METHOD_BIND, + nullptr); + if (res != JVMTI_ERROR_NONE) { + JvmtiErrorToException(env, jvmti_env, res); + } +} + +} // namespace Test986NativeBind +} // namespace art diff --git a/test/986-native-method-bind/run b/test/986-native-method-bind/run new file mode 100755 index 0000000000..e92b873956 --- /dev/null +++ b/test/986-native-method-bind/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright 2017 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. + +./default-run "$@" --jvmti diff --git a/test/986-native-method-bind/src/Main.java b/test/986-native-method-bind/src/Main.java new file mode 100644 index 0000000000..fac9d8e2a9 --- /dev/null +++ b/test/986-native-method-bind/src/Main.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2017 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. + */ + +public class Main { + public static void main(String[] args) throws Exception { + art.Test986.run(); + } +} diff --git a/test/986-native-method-bind/src/art/Main.java b/test/986-native-method-bind/src/art/Main.java new file mode 100644 index 0000000000..8b01920638 --- /dev/null +++ b/test/986-native-method-bind/src/art/Main.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 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. + */ + +package art; + +// Binder class so the agent's C code has something that can be bound and exposed to tests. +// In a package to separate cleanly and work around CTS reference issues (though this class +// should be replaced in the CTS version). +public class Main { + // Load the given class with the given classloader, and bind all native methods to corresponding + // C methods in the agent. Will abort if any of the steps fail. + public static native void bindAgentJNI(String className, ClassLoader classLoader); + // Same as above, giving the class directly. + public static native void bindAgentJNIForClass(Class<?> klass); +} diff --git a/test/986-native-method-bind/src/art/Redefinition.java b/test/986-native-method-bind/src/art/Redefinition.java new file mode 100644 index 0000000000..0350ab42ad --- /dev/null +++ b/test/986-native-method-bind/src/art/Redefinition.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2017 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. + */ + +package art; + +import java.util.ArrayList; +// Common Redefinition functions. Placed here for use by CTS +public class Redefinition { + // Bind native functions. + static { + Main.bindAgentJNIForClass(Redefinition.class); + } + + public static final class CommonClassDefinition { + public final Class<?> target; + public final byte[] class_file_bytes; + public final byte[] dex_file_bytes; + + public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) { + this.target = target; + this.class_file_bytes = class_file_bytes; + this.dex_file_bytes = dex_file_bytes; + } + } + + // A set of possible test configurations. Test should set this if they need to. + // This must be kept in sync with the defines in ti-agent/common_helper.cc + public static enum Config { + COMMON_REDEFINE(0), + COMMON_RETRANSFORM(1), + COMMON_TRANSFORM(2); + + private final int val; + private Config(int val) { + this.val = val; + } + } + + public static void setTestConfiguration(Config type) { + nativeSetTestConfiguration(type.val); + } + + private static native void nativeSetTestConfiguration(int type); + + // Transforms the class + public static native void doCommonClassRedefinition(Class<?> target, + byte[] classfile, + byte[] dexfile); + + public static void doMultiClassRedefinition(CommonClassDefinition... defs) { + ArrayList<Class<?>> classes = new ArrayList<>(); + ArrayList<byte[]> class_files = new ArrayList<>(); + ArrayList<byte[]> dex_files = new ArrayList<>(); + + for (CommonClassDefinition d : defs) { + classes.add(d.target); + class_files.add(d.class_file_bytes); + dex_files.add(d.dex_file_bytes); + } + doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]), + class_files.toArray(new byte[0][]), + dex_files.toArray(new byte[0][])); + } + + public static void addMultiTransformationResults(CommonClassDefinition... defs) { + for (CommonClassDefinition d : defs) { + addCommonTransformationResult(d.target.getCanonicalName(), + d.class_file_bytes, + d.dex_file_bytes); + } + } + + public static native void doCommonMultiClassRedefinition(Class<?>[] targets, + byte[][] classfiles, + byte[][] dexfiles); + public static native void doCommonClassRetransformation(Class<?>... target); + public static native void setPopRetransformations(boolean pop); + public static native void popTransformationFor(String name); + public static native void enableCommonRetransformation(boolean enable); + public static native void addCommonTransformationResult(String target_name, + byte[] class_bytes, + byte[] dex_bytes); +} diff --git a/test/986-native-method-bind/src/art/Test986.java b/test/986-native-method-bind/src/art/Test986.java new file mode 100644 index 0000000000..edd2e9d79f --- /dev/null +++ b/test/986-native-method-bind/src/art/Test986.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2017 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. + */ + +package art; + +import java.lang.reflect.Method; +import java.util.HashMap; + +public class Test986 { + static { + // NB This is called before any setup is done so we don't need to worry about getting bind + // events. + Main.bindAgentJNIForClass(Test986.class); + } + + + private static final HashMap<Method, String> SymbolMap = new HashMap<>(); + + // A class with a native method we can play with. + static class Transform { + private static native void sayHi(); + } + + public static void run() throws Exception { + setupNativeBindNotify(); + setNativeBindNotify(true); + doTest(); + } + + private static void setNativeTransform(Method method, String dest) { + SymbolMap.put(method, dest); + } + + /** + * Notifies java that a native method bind has occurred and requests the new symbol to bind to. + */ + public static String doNativeMethodBind(Method method, String nativeSym) { + // Disable native bind notify for now to avoid infinite loops. + setNativeBindNotify(false); + String transSym = SymbolMap.getOrDefault(method, nativeSym); + System.out.println(method + " = " + nativeSym + " -> " + transSym); + setNativeBindNotify(true); + return transSym; + } + + public static void doTest() throws Exception { + Method say_hi_method = Transform.class.getDeclaredMethod("sayHi"); + // TODO We should test auto-binding but due to the way this is run that will be annoying. + Main.bindAgentJNIForClass(Transform.class); + Transform.sayHi(); + setNativeTransform(say_hi_method, "NoReallySayGoodbye"); + Main.bindAgentJNIForClass(Transform.class); + Transform.sayHi(); + Main.bindAgentJNIForClass(Main.class); + Main.bindAgentJNIForClass(Test986.class); + } + + // Functions called from native code. + public static void doSayHi() { + System.out.println("Hello"); + } + + public static void doSayBye() { + System.out.println("Bye"); + } + + private static native void setNativeBindNotify(boolean enable); + private static native void setupNativeBindNotify(); +} diff --git a/test/Android.bp b/test/Android.bp index c5d96da20c..15b3f8a438 100644 --- a/test/Android.bp +++ b/test/Android.bp @@ -275,6 +275,7 @@ art_cc_defaults { "933-misc-events/misc_events.cc", "945-obsolete-native/obsolete_native.cc", "984-obsolete-invoke/obsolete_invoke.cc", + "986-native-method-bind/native_bind.cc", ], shared_libs: [ "libbase", |