nativebridge.cc revision 20ae1d77463d1c3f206eac8d541124b4555464de
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// A simple implementation of the native-bridge interface.
18
19#include <algorithm>
20#include <dlfcn.h>
21#include <vector>
22
23#include "jni.h"
24#include "stdio.h"
25#include "string.h"
26#include "unistd.h"
27
28#include "native_bridge.h"
29
30
31// Native bridge interfaces...
32
33struct NativeBridgeArtCallbacks {
34  const char* (*getMethodShorty)(JNIEnv* env, jmethodID mid);
35  uint32_t (*getNativeMethodCount)(JNIEnv* env, jclass clazz);
36  uint32_t (*getNativeMethods)(JNIEnv* env, jclass clazz, JNINativeMethod* methods,
37                               uint32_t method_count);
38};
39
40struct NativeBridgeCallbacks {
41  bool (*initialize)(NativeBridgeArtCallbacks* art_cbs);
42  void* (*loadLibrary)(const char* libpath, int flag);
43  void* (*getTrampoline)(void* handle, const char* name, const char* shorty, uint32_t len);
44  bool (*isSupported)(const char* libpath);
45};
46
47struct NativeBridgeMethod {
48  const char* name;
49  const char* signature;
50  bool static_method;
51  void* fnPtr;
52  void* trampoline;
53};
54
55static NativeBridgeMethod* find_native_bridge_method(const char *name);
56static NativeBridgeArtCallbacks* gNativeBridgeArtCallbacks;
57
58static jint trampoline_JNI_OnLoad(JavaVM* vm, void* reserved) {
59  JNIEnv* env = nullptr;
60  typedef jint (*FnPtr_t)(JavaVM*, void*);
61  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("JNI_OnLoad")->fnPtr);
62
63  vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
64  if (env == nullptr) {
65    return 0;
66  }
67
68  jclass klass = env->FindClass("Main");
69  if (klass != nullptr) {
70    int i, count1, count2;
71    count1 = gNativeBridgeArtCallbacks->getNativeMethodCount(env, klass);
72    std::unique_ptr<JNINativeMethod[]> methods(new JNINativeMethod[count1]);
73    if (methods == nullptr) {
74      return 0;
75    }
76    count2 = gNativeBridgeArtCallbacks->getNativeMethods(env, klass, methods.get(), count1);
77    if (count1 == count2) {
78      printf("Test ART callbacks: all JNI function number is %d.\n", count1);
79    }
80
81    for (i = 0; i < count1; i++) {
82      NativeBridgeMethod* nb_method = find_native_bridge_method(methods[i].name);
83      if (nb_method != nullptr) {
84        jmethodID mid = nullptr;
85        if (nb_method->static_method) {
86          mid = env->GetStaticMethodID(klass, methods[i].name, nb_method->signature);
87        } else {
88          mid = env->GetMethodID(klass, methods[i].name, nb_method->signature);
89        }
90        if (mid != nullptr) {
91          const char* shorty = gNativeBridgeArtCallbacks->getMethodShorty(env, mid);
92          if (strcmp(shorty, methods[i].signature) == 0) {
93            printf("    name:%s, signature:%s, shorty:%s.\n",
94                   methods[i].name, nb_method->signature, shorty);
95          }
96        }
97      }
98    }
99    methods.release();
100  }
101
102  printf("%s called!\n", __FUNCTION__);
103  return fnPtr(vm, reserved);
104}
105
106static void trampoline_Java_Main_testFindClassOnAttachedNativeThread(JNIEnv* env,
107                                                                     jclass klass) {
108  typedef void (*FnPtr_t)(JNIEnv*, jclass);
109  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
110    (find_native_bridge_method("testFindClassOnAttachedNativeThread")->fnPtr);
111  printf("%s called!\n", __FUNCTION__);
112  return fnPtr(env, klass);
113}
114
115static void trampoline_Java_Main_testFindFieldOnAttachedNativeThreadNative(JNIEnv* env,
116                                                                           jclass klass) {
117  typedef void (*FnPtr_t)(JNIEnv*, jclass);
118  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
119    (find_native_bridge_method("testFindFieldOnAttachedNativeThreadNative")->fnPtr);
120  printf("%s called!\n", __FUNCTION__);
121  return fnPtr(env, klass);
122}
123
124static void trampoline_Java_Main_testCallStaticVoidMethodOnSubClassNative(JNIEnv* env,
125                                                                          jclass klass) {
126  typedef void (*FnPtr_t)(JNIEnv*, jclass);
127  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
128    (find_native_bridge_method("testCallStaticVoidMethodOnSubClassNative")->fnPtr);
129  printf("%s called!\n", __FUNCTION__);
130  return fnPtr(env, klass);
131}
132
133static jobject trampoline_Java_Main_testGetMirandaMethodNative(JNIEnv* env, jclass klass) {
134  typedef jobject (*FnPtr_t)(JNIEnv*, jclass);
135  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
136    (find_native_bridge_method("testGetMirandaMethodNative")->fnPtr);
137  printf("%s called!\n", __FUNCTION__);
138  return fnPtr(env, klass);
139}
140
141static void trampoline_Java_Main_testZeroLengthByteBuffers(JNIEnv* env, jclass klass) {
142  typedef void (*FnPtr_t)(JNIEnv*, jclass);
143  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>
144    (find_native_bridge_method("testZeroLengthByteBuffers")->fnPtr);
145  printf("%s called!\n", __FUNCTION__);
146  return fnPtr(env, klass);
147}
148
149static jbyte trampoline_Java_Main_byteMethod(JNIEnv* env, jclass klass, jbyte b1, jbyte b2,
150                                             jbyte b3, jbyte b4, jbyte b5, jbyte b6,
151                                             jbyte b7, jbyte b8, jbyte b9, jbyte b10) {
152  typedef jbyte (*FnPtr_t)(JNIEnv*, jclass, jbyte, jbyte, jbyte, jbyte, jbyte,
153                           jbyte, jbyte, jbyte, jbyte, jbyte);
154  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("byteMethod")->fnPtr);
155  printf("%s called!\n", __FUNCTION__);
156  return fnPtr(env, klass, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10);
157}
158
159static jshort trampoline_Java_Main_shortMethod(JNIEnv* env, jclass klass, jshort s1, jshort s2,
160                                               jshort s3, jshort s4, jshort s5, jshort s6,
161                                               jshort s7, jshort s8, jshort s9, jshort s10) {
162  typedef jshort (*FnPtr_t)(JNIEnv*, jclass, jshort, jshort, jshort, jshort, jshort,
163                            jshort, jshort, jshort, jshort, jshort);
164  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("shortMethod")->fnPtr);
165  printf("%s called!\n", __FUNCTION__);
166  return fnPtr(env, klass, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10);
167}
168
169static jboolean trampoline_Java_Main_booleanMethod(JNIEnv* env, jclass klass, jboolean b1,
170                                                   jboolean b2, jboolean b3, jboolean b4,
171                                                   jboolean b5, jboolean b6, jboolean b7,
172                                                   jboolean b8, jboolean b9, jboolean b10) {
173  typedef jboolean (*FnPtr_t)(JNIEnv*, jclass, jboolean, jboolean, jboolean, jboolean, jboolean,
174                              jboolean, jboolean, jboolean, jboolean, jboolean);
175  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("booleanMethod")->fnPtr);
176  printf("%s called!\n", __FUNCTION__);
177  return fnPtr(env, klass, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10);
178}
179
180static jchar trampoline_Java_Main_charMethod(JNIEnv* env, jclass klass, jchar c1, jchar c2,
181                                             jchar c3, jchar c4, jchar c5, jchar c6,
182                                             jchar c7, jchar c8, jchar c9, jchar c10) {
183  typedef jchar (*FnPtr_t)(JNIEnv*, jclass, jchar, jchar, jchar, jchar, jchar,
184                           jchar, jchar, jchar, jchar, jchar);
185  FnPtr_t fnPtr = reinterpret_cast<FnPtr_t>(find_native_bridge_method("charMethod")->fnPtr);
186  printf("%s called!\n", __FUNCTION__);
187  return fnPtr(env, klass, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10);
188}
189
190NativeBridgeMethod gNativeBridgeMethods[] = {
191  { "JNI_OnLoad", "", true, nullptr,
192    reinterpret_cast<void*>(trampoline_JNI_OnLoad) },
193  { "booleanMethod", "(ZZZZZZZZZZ)Z", true, nullptr,
194    reinterpret_cast<void*>(trampoline_Java_Main_booleanMethod) },
195  { "byteMethod", "(BBBBBBBBBB)B", true, nullptr,
196    reinterpret_cast<void*>(trampoline_Java_Main_byteMethod) },
197  { "charMethod", "(CCCCCCCCCC)C", true, nullptr,
198    reinterpret_cast<void*>(trampoline_Java_Main_charMethod) },
199  { "shortMethod", "(SSSSSSSSSS)S", true, nullptr,
200    reinterpret_cast<void*>(trampoline_Java_Main_shortMethod) },
201  { "testCallStaticVoidMethodOnSubClassNative", "()V", true, nullptr,
202    reinterpret_cast<void*>(trampoline_Java_Main_testCallStaticVoidMethodOnSubClassNative) },
203  { "testFindClassOnAttachedNativeThread", "()V", true, nullptr,
204    reinterpret_cast<void*>(trampoline_Java_Main_testFindClassOnAttachedNativeThread) },
205  { "testFindFieldOnAttachedNativeThreadNative", "()V", true, nullptr,
206    reinterpret_cast<void*>(trampoline_Java_Main_testFindFieldOnAttachedNativeThreadNative) },
207  { "testGetMirandaMethodNative", "()Ljava/lang/reflect/Method;", true, nullptr,
208    reinterpret_cast<void*>(trampoline_Java_Main_testGetMirandaMethodNative) },
209  { "testZeroLengthByteBuffers", "()V", true, nullptr,
210    reinterpret_cast<void*>(trampoline_Java_Main_testZeroLengthByteBuffers) },
211};
212
213static NativeBridgeMethod* find_native_bridge_method(const char *name) {
214  const char* pname = name;
215  if (strncmp(name, "Java_Main_", 10) == 0) {
216    pname += 10;
217  }
218
219  for (size_t i = 0; i < sizeof(gNativeBridgeMethods) / sizeof(gNativeBridgeMethods[0]); i++) {
220    if (strcmp(pname, gNativeBridgeMethods[i].name) == 0) {
221      return &gNativeBridgeMethods[i];
222    }
223  }
224  return nullptr;
225}
226
227// NativeBridgeCallbacks implementations
228extern "C" bool native_bridge_initialize(NativeBridgeArtCallbacks* art_cbs) {
229  if (art_cbs != nullptr) {
230    gNativeBridgeArtCallbacks = art_cbs;
231    printf("Native bridge initialized.\n");
232  }
233  return true;
234}
235
236extern "C" void* native_bridge_loadLibrary(const char* libpath, int flag) {
237  size_t len = strlen(libpath);
238  char* tmp = new char[len + 10];
239  strncpy(tmp, libpath, len);
240  tmp[len - 3] = '2';
241  tmp[len - 2] = '.';
242  tmp[len - 1] = 's';
243  tmp[len] = 'o';
244  tmp[len + 1] = 0;
245  void* handle = dlopen(tmp, flag);
246  delete[] tmp;
247
248  if (handle == nullptr) {
249    printf("Handle = nullptr!\n");
250    printf("Was looking for %s.\n", libpath);
251    printf("Error = %s.\n", dlerror());
252    char cwd[1024];
253    if (getcwd(cwd, sizeof(cwd)) != nullptr) {
254      printf("Current working dir: %s\n", cwd);
255    }
256  }
257  return handle;
258}
259
260extern "C" void* native_bridge_getTrampoline(void* handle, const char* name, const char* shorty,
261                                             uint32_t len) {
262  printf("Getting trampoline for %s with shorty %s.\n", name, shorty);
263
264  // The name here is actually the JNI name, so we can directly do the lookup.
265  void* sym = dlsym(handle, name);
266  NativeBridgeMethod* method = find_native_bridge_method(name);
267  if (method == nullptr)
268    return nullptr;
269  method->fnPtr = sym;
270
271  return method->trampoline;
272}
273
274extern "C" bool native_bridge_isSupported(const char* libpath) {
275  printf("Checking for support.\n");
276
277  if (libpath == nullptr) {
278    return false;
279  }
280  // We don't want to hijack javacore. So we should get libarttest...
281  return strcmp(libpath, "libjavacore.so") != 0;
282}
283
284NativeBridgeCallbacks NativeBridgeItf {
285  .initialize = &native_bridge_initialize,
286  .loadLibrary = &native_bridge_loadLibrary,
287  .getTrampoline = &native_bridge_getTrampoline,
288  .isSupported = &native_bridge_isSupported
289};
290