diff --git a/sdk/android/src/jni/jni_generator_helper.h b/sdk/android/src/jni/jni_generator_helper.h index 53e1d84127..e6d6f7ef15 100644 --- a/sdk/android/src/jni/jni_generator_helper.h +++ b/sdk/android/src/jni/jni_generator_helper.h @@ -33,12 +33,6 @@ RTC_CHECK(!jni->ExceptionCheck()) \ << (jni->ExceptionDescribe(), jni->ExceptionClear(), "") -namespace jni_generator { -inline void CheckException(JNIEnv* env) { - CHECK_EXCEPTION(env); -} -} // namespace jni_generator - namespace webrtc { // This function will initialize |atomic_class_id| to contain a global ref to @@ -86,4 +80,80 @@ using webrtc::MethodID; } // namespace android } // namespace base +namespace jni_generator { +inline void CheckException(JNIEnv* env) { + CHECK_EXCEPTION(env); +} + +// A 32 bit number could be an address on stack. Random 64 bit marker on the +// stack is much less likely to be present on stack. +constexpr uint64_t kJniStackMarkerValue = 0xbdbdef1bebcade1b; + +// Context about the JNI call with exception checked to be stored in stack. +struct BASE_EXPORT JniJavaCallContextUnchecked { + inline JniJavaCallContextUnchecked() { +// TODO(ssid): Implement for other architectures. +#if defined(__arm__) || defined(__aarch64__) + // This assumes that this method does not increment the stack pointer. + asm volatile("mov %0, sp" : "=r"(sp)); +#else + sp = 0; +#endif + } + + // Force no inline to reduce code size. + template + void Init(JNIEnv* env, + jclass clazz, + const char* method_name, + const char* jni_signature, + std::atomic* atomic_method_id) { + env1 = env; + + // Make sure compiler doesn't optimize out the assignment. + memcpy(&marker, &kJniStackMarkerValue, sizeof(kJniStackMarkerValue)); + // Gets PC of the calling function. + pc = reinterpret_cast(__builtin_return_address(0)); + + method_id = base::android::MethodID::LazyGet( + env, clazz, method_name, jni_signature, atomic_method_id); + } + + ~JniJavaCallContextUnchecked() { + // Reset so that spurious marker finds are avoided. + memset(&marker, 0, sizeof(marker)); + } + + uint64_t marker; + uintptr_t sp; + uintptr_t pc; + + JNIEnv* env1; + jmethodID method_id; +}; + +// Context about the JNI call with exception unchecked to be stored in stack. +struct BASE_EXPORT JniJavaCallContextChecked { + // Force no inline to reduce code size. + template + void Init(JNIEnv* env, + jclass clazz, + const char* method_name, + const char* jni_signature, + std::atomic* atomic_method_id) { + base.Init(env, clazz, method_name, jni_signature, atomic_method_id); + // Reset |pc| to correct caller. + base.pc = reinterpret_cast(__builtin_return_address(0)); + } + + ~JniJavaCallContextChecked() { jni_generator::CheckException(base.env1); } + + JniJavaCallContextUnchecked base; +}; + +static_assert(sizeof(JniJavaCallContextChecked) == + sizeof(JniJavaCallContextUnchecked), + "Stack unwinder cannot work with structs of different sizes."); +} // namespace jni_generator + #endif // SDK_ANDROID_SRC_JNI_JNI_GENERATOR_HELPER_H_