1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef JNI_ZERO_JNI_ZERO_HELPER_H_
6 #define JNI_ZERO_JNI_ZERO_HELPER_H_
7
8 #include <jni.h>
9
10 #include "base/android/jni_android.h"
11 #include "base/android/scoped_java_ref.h"
12 #include "base/compiler_specific.h"
13 #include "base/memory/raw_ptr.h"
14 #if defined(USE_CHROMIUM_BASE)
15 // Used for ARCH_CPU_X86 - embedder must define this correctly if they want
16 // 16-byte stack alignment on x86.
17 #include "build/build_config.h"
18 #endif // defined(USE_CHROMIUM_BASE)
19 #include "third_party/jni_zero/jni_export.h"
20 #include "third_party/jni_zero/jni_int_wrapper.h"
21 #include "third_party/jni_zero/logging.h"
22
23 // Project-specific macros used by the header files generated by
24 // jni_generator.py. Different projects can then specify their own
25 // implementation for this file.
26 #define CHECK_NATIVE_PTR(env, jcaller, native_ptr, method_name, ...) \
27 JNI_ZERO_DCHECK(native_ptr);
28
29 #define CHECK_CLAZZ(env, jcaller, clazz, ...) JNI_ZERO_DCHECK(clazz);
30
31 namespace jni_generator {
32
HandleRegistrationError(JNIEnv * env,jclass clazz,const char * filename)33 inline void HandleRegistrationError(JNIEnv* env,
34 jclass clazz,
35 const char* filename) {
36 JNI_ZERO_ELOG("RegisterNatives failed in %s", filename);
37 }
38
CheckException(JNIEnv * env)39 inline void CheckException(JNIEnv* env) {
40 base::android::CheckException(env);
41 }
42
43 // A 32 bit number could be an address on stack. Random 64 bit marker on the
44 // stack is much less likely to be present on stack.
45 constexpr uint64_t kJniStackMarkerValue = 0xbdbdef1bebcade1b;
46
47 // Context about the JNI call with exception checked to be stored in stack.
48 struct JNI_ZERO_COMPONENT_BUILD_EXPORT JniJavaCallContextUnchecked {
JniJavaCallContextUncheckedJniJavaCallContextUnchecked49 ALWAYS_INLINE JniJavaCallContextUnchecked() {
50 // TODO(ssid): Implement for other architectures.
51 #if defined(__arm__) || defined(__aarch64__)
52 // This assumes that this method does not increment the stack pointer.
53 asm volatile("mov %0, sp" : "=r"(sp));
54 #else
55 sp = 0;
56 #endif
57 }
58
59 // Force no inline to reduce code size.
60 template <base::android::MethodID::Type type>
InitJniJavaCallContextUnchecked61 NOINLINE void Init(JNIEnv* env,
62 jclass clazz,
63 const char* method_name,
64 const char* jni_signature,
65 std::atomic<jmethodID>* atomic_method_id) {
66 env1 = env;
67
68 // Make sure compiler doesn't optimize out the assignment.
69 memcpy(&marker, &kJniStackMarkerValue, sizeof(kJniStackMarkerValue));
70 // Gets PC of the calling function.
71 pc = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
72
73 method_id = base::android::MethodID::LazyGet<type>(
74 env, clazz, method_name, jni_signature, atomic_method_id);
75 }
76
~JniJavaCallContextUncheckedJniJavaCallContextUnchecked77 NOINLINE ~JniJavaCallContextUnchecked() {
78 // Reset so that spurious marker finds are avoided.
79 memset(&marker, 0, sizeof(marker));
80 }
81
82 uint64_t marker;
83 uintptr_t sp;
84 uintptr_t pc;
85
86 raw_ptr<JNIEnv> env1;
87 jmethodID method_id;
88 };
89
90 // Context about the JNI call with exception unchecked to be stored in stack.
91 struct JNI_ZERO_COMPONENT_BUILD_EXPORT JniJavaCallContextChecked {
92 // Force no inline to reduce code size.
93 template <base::android::MethodID::Type type>
InitJniJavaCallContextChecked94 NOINLINE void Init(JNIEnv* env,
95 jclass clazz,
96 const char* method_name,
97 const char* jni_signature,
98 std::atomic<jmethodID>* atomic_method_id) {
99 base.Init<type>(env, clazz, method_name, jni_signature, atomic_method_id);
100 // Reset |pc| to correct caller.
101 base.pc = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
102 }
103
~JniJavaCallContextCheckedJniJavaCallContextChecked104 NOINLINE ~JniJavaCallContextChecked() {
105 jni_generator::CheckException(base.env1);
106 }
107
108 JniJavaCallContextUnchecked base;
109 };
110
111 static_assert(sizeof(JniJavaCallContextChecked) ==
112 sizeof(JniJavaCallContextUnchecked),
113 "Stack unwinder cannot work with structs of different sizes.");
114
115 } // namespace jni_generator
116
117 #endif // JNI_ZERO_JNI_ZERO_HELPER_H_
118