首页 > 代码库 > Android调用JNI本地方法经过有点改变
Android调用JNI本地方法经过有点改变
方法注册好后要经过哪些路
Android一个异常捕获项目 https://github.com/xroche/coffeecatch
coffeecatch
CoffeeCatch, a tiny native POSIX signal catcher (especially useful for JNI code on Android/Dalvik, but it can be used in non-Java projects)
It allows to "gracefully" recover from a signal (SIGSEGV
, SIGBUS
...) as if it was an exception. It will not gracefully recover from allocator/mutexes corruption etc., however, but at least "most" gentle crashes (null pointer dereferencing, integer division, stack overflow etc.) should be handled without too much troubles.
/** Enter protected section. **/COFFEE_TRY() { /** Try to call ‘call_some_native_function‘. **/ call_some_protected_function();} COFFEE_CATCH() { /** Caught a signal: throw Java exception. **/ /** In pure C projects, you may print an error message (coffeecatch_get_message()). **/ coffeecatch_throw_exception(env);} COFFEE_END();
You may read the corresponding discussion about this project.
The handler is thread-safe, but client must have exclusive control on the signal handlers (ie. the library is installing its own signal handlers on top of the existing ones).
Libraries
If you want to get useful stack traces, you should build all your libraries with -funwind-tables
(this adds unwinding information). On ARM, you may also use the --no-merge-exidx-entries
linker switch, to solve certain issues with unwinding (the switch is possibly not needed anymore). On Android, this can be achieved by using this line in the Android.mk
file in each library block:
LOCAL_CFLAGS := -funwind-tables -Wl,--no-merge-exidx-entries
Example
- Inside JNI (typically, Android)
First, build the library, or just add the two files in the list of local files to be built:
LOCAL_SRC_FILES += coffeecatch.c coffeejni.c
then, use the COFFEE_TRY_JNI() macro to protect your call(s):
/** The potentially dangerous function. **/jint call_dangerous_function(JNIEnv* env, jobject object) { // ... do dangerous things! return 42;}/** Protected function stub. **/void foo_protected(JNIEnv* env, jobject object, jint *retcode) { /* Try to call ‘call_dangerous_function‘, and raise proper Java Error upon * fatal error (SEGV, etc.). **/ COFFEE_TRY_JNI(env, *retcode = call_dangerous_function(env, object));}/** Regular JNI entry point. **/jint Java_com_example_android_MyNative_foo(JNIEnv* env, jobject object) { jint retcode = 0; foo_protected(env, object, &retcode); return retcode;}
and, in case of crash, get something like this (note: the last Exception with native backtrace is produced on Android >= 4.1.1):
FATAL EXCEPTION: AsyncTask #5java.lang.RuntimeException: An error occured while executing doInBackground() at android.os.AsyncTask$3.done(AsyncTask.java:299) at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352) at java.util.concurrent.FutureTask.setException(FutureTask.java:219) at java.util.concurrent.FutureTask.run(FutureTask.java:239) at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573) at java.lang.Thread.run(Thread.java:841)Caused by: java.lang.Error: signal 11 (Address not mapped to object) at address 0x42 [at libexample.so:0xa024] at com.example.jni.ExampleLib.main(Native Method) at com.example.ExampleActivity$Runner.runInternal(ExampleActivity.java:998) at com.example.ExampleActivity$Runner.doInBackground(ExampleActivity.java:919) at com.example.ExampleActivity$Runner.doInBackground(ExampleActivity.java:1) at android.os.AsyncTask$2.call(AsyncTask.java:287) at java.util.concurrent.FutureTask.run(FutureTask.java:234) ... 4 moreCaused by: java.lang.Error: signal 11 (Address not mapped to object) at address 0x42 [at libexample.so:0xa024] at data.app_lib.com_example.libexample_so.0xa024(Native Method) at data.app_lib.com_example.libexample_so.0x705fc(hts_main2:0x8f74:0) at data.app_lib.com_example.libexamplejni_so.0x4cc8(ExampleLib_main:0xf8:0) at data.app_lib.com_example.libexamplejni_so.0x52d8(Java_com_example_jni_ExampleLib_main:0x64:0) at system.lib.libdvm_so.0x1dc4c(dvmPlatformInvoke:0x70:0) at system.lib.libdvm_so.0x4dcab(dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*):0x18a:0) at system.lib.libdvm_so.0x385e1(dvmCheckCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*):0x8:0) at system.lib.libdvm_so.0x4f699(dvmResolveNativeMethod(unsigned int const*, JValue*, Method const*, Thread*):0xb8:0) at system.lib.libdvm_so.0x27060(Native Method) at system.lib.libdvm_so.0x2b580(dvmInterpret(Thread*, Method const*, JValue*):0xb8:0) at system.lib.libdvm_so.0x5fcbd(dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list):0x124:0) at system.lib.libdvm_so.0x5fce7(dvmCallMethod(Thread*, Method const*, Object*, JValue*, ...):0x14:0) at system.lib.libdvm_so.0x54a6f(Native Method) at system.lib.libc_so.0xca58(__thread_entry:0x48:0) at system.lib.libc_so.0xcbd4(pthread_create:0xd0:0)
- Outside JNI code
The COFFEE_TRY()/COFFEE_CATCH()/COFFEE_END() syntax can be used:
void my_function() { COFFEE_TRY() { /** Try to call ‘call_some_native_function‘. **/ call_some_native_function(); } COFFEE_CATCH() { /** Caught a signal. **/ const char*const message = coffeecatch_get_message(); fprintf(stderr, "**FATAL ERROR: %s\n", message); } COFFEE_END();}
- Hints
If you wish to catch signals and continue running your program rather than ending it (this may be dangerous, especially if a crash was spotted within a C library function, such as malloc()
), use thecoffeecatch_cancel_pending_alarm()
function to cancel the default pending alarm triggered to avoid deadlocks.
JNI方法调用改变
dvmCallJNIMethod_general不知从哪个版本就没了,但从http://androidxref.com/这里看,Gingerbread - 2.3.7还有,ICS - 4.0.3就没了。
这里模拟器里导出的libdvm.so,符号,只有dvmCallJNIMethod,如下(4.4.2_API19):
File: /cygdrive/d/Developer/sdk/platforms/android-19/lib/libdvm.so
Symbol table ‘.dynsym‘ contains 1713 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize
394: 0004dd75 664 FUNC GLOBAL DEFAULT 8 _Z16dvmCallJNIMethodPKjP6JValuePK6MethodP6Thread
1 //xref: 4.4.2_r2 /dalvik/vm/interp/Stack.cpp 2 //http://androidxref.com/4.4.2_r2/xref/dalvik/vm/interp/Stack.cpp 3 4 /* 5 * Copyright (C) 2008 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 /* 21 * Stacks and their uses (e.g. native --> interpreted method calls). 22 * 23 * See the majestic ASCII art in Stack.h. 24 */ 25 #include "Dalvik.h" 26 #include "jni.h" 27 28 #include <stdlib.h> 29 #include <stdarg.h> 30 31 #ifdef HAVE_ANDROID_OS 32 #include <corkscrew/backtrace.h> 33 #endif 34 35 /* 36 * Initialize the interpreter stack in a new thread. 37 * 38 * Currently this doesn‘t do much, since we don‘t need to zero out the 39 * stack (and we really don‘t want to if it was created with mmap). 40 */ 41 bool dvmInitInterpStack(Thread* thread, int stackSize) 42 { 43 assert(thread->interpStackStart != NULL); 44 45 assert(thread->interpSave.curFrame == NULL); 46 47 return true; 48 } 49 50 /* 51 * We‘re calling an interpreted method from an internal VM function or 52 * via reflection. 53 * 54 * Push a frame for an interpreted method onto the stack. This is only 55 * used when calling into interpreted code from native code. (The 56 * interpreter does its own stack frame manipulation for interp-->interp 57 * calls.) 58 * 59 * The size we need to reserve is the sum of parameters, local variables, 60 * saved goodies, and outbound parameters. 61 * 62 * We start by inserting a "break" frame, which ensures that the interpreter 63 * hands control back to us after the function we call returns or an 64 * uncaught exception is thrown. 65 */ 66 static bool dvmPushInterpFrame(Thread* self, const Method* method) 67 { 68 StackSaveArea* saveBlock; 69 StackSaveArea* breakSaveBlock; 70 int stackReq; 71 u1* stackPtr; 72 73 assert(!dvmIsNativeMethod(method)); 74 assert(!dvmIsAbstractMethod(method)); 75 76 stackReq = method->registersSize * 4 // params + locals 77 + sizeof(StackSaveArea) * 2 // break frame + regular frame 78 + method->outsSize * 4; // args to other methods 79 80 if (self->interpSave.curFrame != NULL) 81 stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame); 82 else 83 stackPtr = self->interpStackStart; 84 85 if (stackPtr - stackReq < self->interpStackEnd) { 86 /* not enough space */ 87 ALOGW("Stack overflow on call to interp " 88 "(req=%d top=%p cur=%p size=%d %s.%s)", 89 stackReq, self->interpStackStart, self->interpSave.curFrame, 90 self->interpStackSize, method->clazz->descriptor, method->name); 91 dvmHandleStackOverflow(self, method); 92 assert(dvmCheckException(self)); 93 return false; 94 } 95 96 /* 97 * Shift the stack pointer down, leaving space for the function‘s 98 * args/registers and save area. 99 */ 100 stackPtr -= sizeof(StackSaveArea); 101 breakSaveBlock = (StackSaveArea*)stackPtr; 102 stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea); 103 saveBlock = (StackSaveArea*) stackPtr; 104 105 #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) 106 /* debug -- memset the new stack, unless we want valgrind‘s help */ 107 memset(stackPtr - (method->outsSize*4), 0xaf, stackReq); 108 #endif 109 #ifdef EASY_GDB 110 breakSaveBlock->prevSave = 111 (StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame); 112 saveBlock->prevSave = breakSaveBlock; 113 #endif 114 115 breakSaveBlock->prevFrame = self->interpSave.curFrame; 116 breakSaveBlock->savedPc = NULL; // not required 117 breakSaveBlock->xtra.localRefCookie = 0; // not required 118 breakSaveBlock->method = NULL; 119 saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock); 120 saveBlock->savedPc = NULL; // not required 121 saveBlock->xtra.currentPc = NULL; // not required? 122 saveBlock->method = method; 123 124 LOGVV("PUSH frame: old=%p new=%p (size=%d)", 125 self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock), 126 (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); 127 128 self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock); 129 130 return true; 131 } 132 133 /* 134 * We‘re calling a JNI native method from an internal VM fuction or 135 * via reflection. This is also used to create the "fake" native-method 136 * frames at the top of the interpreted stack. 137 * 138 * This actually pushes two frames; the first is a "break" frame. 139 * 140 * The top frame has additional space for JNI local reference tracking. 141 */ 142 bool dvmPushJNIFrame(Thread* self, const Method* method) 143 { 144 StackSaveArea* saveBlock; 145 StackSaveArea* breakSaveBlock; 146 int stackReq; 147 u1* stackPtr; 148 149 assert(dvmIsNativeMethod(method)); 150 151 stackReq = method->registersSize * 4 // params only 152 + sizeof(StackSaveArea) * 2; // break frame + regular frame 153 154 if (self->interpSave.curFrame != NULL) 155 stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame); 156 else 157 stackPtr = self->interpStackStart; 158 159 if (stackPtr - stackReq < self->interpStackEnd) { 160 /* not enough space */ 161 ALOGW("Stack overflow on call to native " 162 "(req=%d top=%p cur=%p size=%d ‘%s‘)", 163 stackReq, self->interpStackStart, self->interpSave.curFrame, 164 self->interpStackSize, method->name); 165 dvmHandleStackOverflow(self, method); 166 assert(dvmCheckException(self)); 167 return false; 168 } 169 170 /* 171 * Shift the stack pointer down, leaving space for just the stack save 172 * area for the break frame, then shift down farther for the full frame. 173 * We leave space for the method args, which are copied in later. 174 */ 175 stackPtr -= sizeof(StackSaveArea); 176 breakSaveBlock = (StackSaveArea*)stackPtr; 177 stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea); 178 saveBlock = (StackSaveArea*) stackPtr; 179 180 #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) 181 /* debug -- memset the new stack */ 182 memset(stackPtr, 0xaf, stackReq); 183 #endif 184 #ifdef EASY_GDB 185 if (self->interpSave.curFrame == NULL) 186 breakSaveBlock->prevSave = NULL; 187 else { 188 void* fp = FP_FROM_SAVEAREA(self->interpSave.curFrame); 189 breakSaveBlock->prevSave = (StackSaveArea*)fp; 190 } 191 saveBlock->prevSave = breakSaveBlock; 192 #endif 193 194 breakSaveBlock->prevFrame = self->interpSave.curFrame; 195 breakSaveBlock->savedPc = NULL; // not required 196 breakSaveBlock->xtra.localRefCookie = 0; // not required 197 breakSaveBlock->method = NULL; 198 saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock); 199 saveBlock->savedPc = NULL; // not required 200 saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all; 201 saveBlock->method = method; 202 203 LOGVV("PUSH JNI frame: old=%p new=%p (size=%d)", 204 self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock), 205 (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); 206 207 self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock); 208 209 return true; 210 } 211 212 /* 213 * This is used by the JNI PushLocalFrame call. We push a new frame onto 214 * the stack that has no ins, outs, or locals, and no break frame above it. 215 * It‘s strictly used for tracking JNI local refs, and will be popped off 216 * by dvmPopFrame if it‘s not removed explicitly. 217 */ 218 bool dvmPushLocalFrame(Thread* self, const Method* method) 219 { 220 StackSaveArea* saveBlock; 221 int stackReq; 222 u1* stackPtr; 223 224 assert(dvmIsNativeMethod(method)); 225 226 stackReq = sizeof(StackSaveArea); // regular frame 227 228 assert(self->interpSave.curFrame != NULL); 229 stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame); 230 231 if (stackPtr - stackReq < self->interpStackEnd) { 232 /* not enough space; let JNI throw the exception */ 233 ALOGW("Stack overflow on PushLocal " 234 "(req=%d top=%p cur=%p size=%d ‘%s‘)", 235 stackReq, self->interpStackStart, self->interpSave.curFrame, 236 self->interpStackSize, method->name); 237 dvmHandleStackOverflow(self, method); 238 assert(dvmCheckException(self)); 239 return false; 240 } 241 242 /* 243 * Shift the stack pointer down, leaving space for just the stack save 244 * area for the break frame, then shift down farther for the full frame. 245 */ 246 stackPtr -= sizeof(StackSaveArea); 247 saveBlock = (StackSaveArea*) stackPtr; 248 249 #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) 250 /* debug -- memset the new stack */ 251 memset(stackPtr, 0xaf, stackReq); 252 #endif 253 #ifdef EASY_GDB 254 saveBlock->prevSave = 255 (StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame); 256 #endif 257 258 saveBlock->prevFrame = self->interpSave.curFrame; 259 saveBlock->savedPc = NULL; // not required 260 saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all; 261 saveBlock->method = method; 262 263 LOGVV("PUSH JNI local frame: old=%p new=%p (size=%d)", 264 self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock), 265 (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); 266 267 self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock); 268 269 return true; 270 } 271 272 /* 273 * Pop one frame pushed on by JNI PushLocalFrame. 274 * 275 * If we‘ve gone too far, the previous frame is either a break frame or 276 * an interpreted frame. Either way, the method pointer won‘t match. 277 */ 278 bool dvmPopLocalFrame(Thread* self) 279 { 280 StackSaveArea* saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame); 281 282 assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame)); 283 if (saveBlock->method != SAVEAREA_FROM_FP(saveBlock->prevFrame)->method) { 284 /* 285 * The previous frame doesn‘t have the same method pointer -- we‘ve 286 * been asked to pop too much. 287 */ 288 assert(dvmIsBreakFrame((u4*)saveBlock->prevFrame) || 289 !dvmIsNativeMethod( 290 SAVEAREA_FROM_FP(saveBlock->prevFrame)->method)); 291 return false; 292 } 293 294 LOGVV("POP JNI local frame: removing %s, now %s", 295 saveBlock->method->name, 296 SAVEAREA_FROM_FP(saveBlock->prevFrame)->method->name); 297 dvmPopJniLocals(self, saveBlock); 298 self->interpSave.curFrame = saveBlock->prevFrame; 299 300 return true; 301 } 302 303 /* 304 * Pop a frame we added. There should be one method frame and one break 305 * frame. 306 * 307 * If JNI Push/PopLocalFrame calls were mismatched, we might end up 308 * popping multiple method frames before we find the break. 309 * 310 * Returns "false" if there was no frame to pop. 311 */ 312 static bool dvmPopFrame(Thread* self) 313 { 314 StackSaveArea* saveBlock; 315 316 if (self->interpSave.curFrame == NULL) 317 return false; 318 319 saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame); 320 assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame)); 321 322 /* 323 * Remove everything up to the break frame. If this was a call into 324 * native code, pop the JNI local references table. 325 */ 326 while (saveBlock->prevFrame != NULL && saveBlock->method != NULL) { 327 /* probably a native->native JNI call */ 328 329 if (dvmIsNativeMethod(saveBlock->method)) { 330 LOGVV("Popping JNI stack frame for %s.%s%s", 331 saveBlock->method->clazz->descriptor, 332 saveBlock->method->name, 333 (SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ? 334 "" : " (JNI local)"); 335 dvmPopJniLocals(self, saveBlock); 336 } 337 338 saveBlock = SAVEAREA_FROM_FP(saveBlock->prevFrame); 339 } 340 if (saveBlock->method != NULL) { 341 ALOGE("PopFrame missed the break"); 342 assert(false); 343 dvmAbort(); // stack trashed -- nowhere to go in this thread 344 } 345 346 LOGVV("POP frame: cur=%p new=%p", 347 self->interpSave.curFrame, saveBlock->prevFrame); 348 349 self->interpSave.curFrame = saveBlock->prevFrame; 350 return true; 351 } 352 353 /* 354 * Common code for dvmCallMethodV/A and dvmInvokeMethod. 355 * 356 * Pushes a call frame on, advancing self->interpSave.curFrame. 357 */ 358 static ClassObject* callPrep(Thread* self, const Method* method, Object* obj, 359 bool checkAccess) 360 { 361 ClassObject* clazz; 362 363 #ifndef NDEBUG 364 if (self->status != THREAD_RUNNING) { 365 ALOGW("threadid=%d: status=%d on call to %s.%s -", 366 self->threadId, self->status, 367 method->clazz->descriptor, method->name); 368 } 369 #endif 370 371 assert(self != NULL); 372 assert(method != NULL); 373 374 if (obj != NULL) 375 clazz = obj->clazz; 376 else 377 clazz = method->clazz; 378 379 IF_LOGVV() { 380 char* desc = dexProtoCopyMethodDescriptor(&method->prototype); 381 LOGVV("thread=%d native code calling %s.%s %s", self->threadId, 382 clazz->descriptor, method->name, desc); 383 free(desc); 384 } 385 386 if (checkAccess) { 387 /* needed for java.lang.reflect.Method.invoke */ 388 if (!dvmCheckMethodAccess(dvmGetCaller2Class(self->interpSave.curFrame), 389 method)) 390 { 391 /* note this throws IAException, not IAError */ 392 dvmThrowIllegalAccessException("access to method denied"); 393 return NULL; 394 } 395 } 396 397 /* 398 * Push a call frame on. If there isn‘t enough room for ins, locals, 399 * outs, and the saved state, it will throw an exception. 400 * 401 * This updates self->interpSave.curFrame. 402 */ 403 if (dvmIsNativeMethod(method)) { 404 /* native code calling native code the hard way */ 405 if (!dvmPushJNIFrame(self, method)) { 406 assert(dvmCheckException(self)); 407 return NULL; 408 } 409 } else { 410 /* native code calling interpreted code */ 411 if (!dvmPushInterpFrame(self, method)) { 412 assert(dvmCheckException(self)); 413 return NULL; 414 } 415 } 416 417 return clazz; 418 } 419 420 /* 421 * Issue a method call. 422 * 423 * Pass in NULL for "obj" on calls to static methods. 424 * 425 * (Note this can‘t be inlined because it takes a variable number of args.) 426 */ 427 void dvmCallMethod(Thread* self, const Method* method, Object* obj, 428 JValue* pResult, ...) 429 { 430 va_list args; 431 va_start(args, pResult); 432 dvmCallMethodV(self, method, obj, false, pResult, args); 433 va_end(args); 434 } 435 436 /* 437 * Issue a method call with a variable number of arguments. We process 438 * the contents of "args" by scanning the method signature. 439 * 440 * Pass in NULL for "obj" on calls to static methods. 441 * 442 * We don‘t need to take the class as an argument because, in Dalvik, 443 * we don‘t need to worry about static synchronized methods. 444 */ 445 void dvmCallMethodV(Thread* self, const Method* method, Object* obj, 446 bool fromJni, JValue* pResult, va_list args) 447 { 448 const char* desc = &(method->shorty[1]); // [0] is the return type. 449 int verifyCount = 0; 450 ClassObject* clazz; 451 u4* ins; 452 453 clazz = callPrep(self, method, obj, false); 454 if (clazz == NULL) 455 return; 456 457 /* "ins" for new frame start at frame pointer plus locals */ 458 ins = ((u4*)self->interpSave.curFrame) + 459 (method->registersSize - method->insSize); 460 461 //ALOGD(" FP is %p, INs live at >= %p", self->interpSave.curFrame, ins); 462 463 /* put "this" pointer into in0 if appropriate */ 464 if (!dvmIsStaticMethod(method)) { 465 #ifdef WITH_EXTRA_OBJECT_VALIDATION 466 assert(obj != NULL && dvmIsHeapAddress(obj)); 467 #endif 468 *ins++ = (u4) obj; 469 verifyCount++; 470 } 471 472 while (*desc != ‘\0‘) { 473 switch (*(desc++)) { 474 case ‘D‘: case ‘J‘: { 475 u8 val = va_arg(args, u8); 476 memcpy(ins, &val, 8); // EABI prevents direct store 477 ins += 2; 478 verifyCount += 2; 479 break; 480 } 481 case ‘F‘: { 482 /* floats were normalized to doubles; convert back */ 483 float f = (float) va_arg(args, double); 484 *ins++ = dvmFloatToU4(f); 485 verifyCount++; 486 break; 487 } 488 case ‘L‘: { /* ‘shorty‘ descr uses L for all refs, incl array */ 489 void* arg = va_arg(args, void*); 490 assert(obj == NULL || dvmIsHeapAddress(obj)); 491 jobject argObj = reinterpret_cast<jobject>(arg); 492 if (fromJni) 493 *ins++ = (u4) dvmDecodeIndirectRef(self, argObj); 494 else 495 *ins++ = (u4) argObj; 496 verifyCount++; 497 break; 498 } 499 default: { 500 /* Z B C S I -- all passed as 32-bit integers */ 501 *ins++ = va_arg(args, u4); 502 verifyCount++; 503 break; 504 } 505 } 506 } 507 508 #ifndef NDEBUG 509 if (verifyCount != method->insSize) { 510 ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount, 511 method->insSize, clazz->descriptor, method->name); 512 assert(false); 513 goto bail; 514 } 515 #endif 516 517 //dvmDumpThreadStack(dvmThreadSelf()); 518 519 if (dvmIsNativeMethod(method)) { 520 TRACE_METHOD_ENTER(self, method); 521 /* 522 * Because we leave no space for local variables, "curFrame" points 523 * directly at the method arguments. 524 */ 525 (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult, 526 method, self); 527 TRACE_METHOD_EXIT(self, method); 528 } else { 529 dvmInterpret(self, method, pResult); 530 } 531 532 #ifndef NDEBUG 533 bail: 534 #endif 535 dvmPopFrame(self); 536 } 537 538 /* 539 * Issue a method call with arguments provided in an array. We process 540 * the contents of "args" by scanning the method signature. 541 * 542 * The values were likely placed into an uninitialized jvalue array using 543 * the field specifiers, which means that sub-32-bit fields (e.g. short, 544 * boolean) may not have 32 or 64 bits of valid data. This is different 545 * from the varargs invocation where the C compiler does a widening 546 * conversion when calling a function. As a result, we have to be a 547 * little more precise when pulling stuff out. 548 * 549 * "args" may be NULL if the method has no arguments. 550 */ 551 void dvmCallMethodA(Thread* self, const Method* method, Object* obj, 552 bool fromJni, JValue* pResult, const jvalue* args) 553 { 554 const char* desc = &(method->shorty[1]); // [0] is the return type. 555 int verifyCount = 0; 556 ClassObject* clazz; 557 u4* ins; 558 559 clazz = callPrep(self, method, obj, false); 560 if (clazz == NULL) 561 return; 562 563 /* "ins" for new frame start at frame pointer plus locals */ 564 ins = ((u4*)self->interpSave.curFrame) + 565 (method->registersSize - method->insSize); 566 567 /* put "this" pointer into in0 if appropriate */ 568 if (!dvmIsStaticMethod(method)) { 569 assert(obj != NULL); 570 *ins++ = (u4) obj; /* obj is a "real" ref */ 571 verifyCount++; 572 } 573 574 while (*desc != ‘\0‘) { 575 switch (*desc++) { 576 case ‘D‘: /* 64-bit quantity; have to use */ 577 case ‘J‘: /* memcpy() in case of mis-alignment */ 578 memcpy(ins, &args->j, 8); 579 ins += 2; 580 verifyCount++; /* this needs an extra push */ 581 break; 582 case ‘L‘: /* includes array refs */ 583 if (fromJni) 584 *ins++ = (u4) dvmDecodeIndirectRef(self, args->l); 585 else 586 *ins++ = (u4) args->l; 587 break; 588 case ‘F‘: 589 case ‘I‘: 590 *ins++ = args->i; /* full 32 bits */ 591 break; 592 case ‘S‘: 593 *ins++ = args->s; /* 16 bits, sign-extended */ 594 break; 595 case ‘C‘: 596 *ins++ = args->c; /* 16 bits, unsigned */ 597 break; 598 case ‘B‘: 599 *ins++ = args->b; /* 8 bits, sign-extended */ 600 break; 601 case ‘Z‘: 602 *ins++ = args->z; /* 8 bits, zero or non-zero */ 603 break; 604 default: 605 ALOGE("Invalid char %c in short signature of %s.%s", 606 *(desc-1), clazz->descriptor, method->name); 607 assert(false); 608 goto bail; 609 } 610 611 verifyCount++; 612 args++; 613 } 614 615 #ifndef NDEBUG 616 if (verifyCount != method->insSize) { 617 ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount, 618 method->insSize, clazz->descriptor, method->name); 619 assert(false); 620 goto bail; 621 } 622 #endif 623 624 if (dvmIsNativeMethod(method)) { 625 TRACE_METHOD_ENTER(self, method); 626 /* 627 * Because we leave no space for local variables, "curFrame" points 628 * directly at the method arguments. 629 */ 630 (*method->nativeFunc)((u4*)self->interpSave.curFrame, pResult, 631 method, self); 632 TRACE_METHOD_EXIT(self, method); 633 } else { 634 dvmInterpret(self, method, pResult); 635 } 636 637 bail: 638 dvmPopFrame(self); 639 } 640 641 static void throwArgumentTypeMismatch(int argIndex, ClassObject* expected, DataObject* arg) { 642 std::string expectedClassName(dvmHumanReadableDescriptor(expected->descriptor)); 643 std::string actualClassName = dvmHumanReadableType(arg); 644 dvmThrowExceptionFmt(gDvm.exIllegalArgumentException, "argument %d should have type %s, got %s", 645 argIndex + 1, expectedClassName.c_str(), actualClassName.c_str()); 646 } 647 648 /* 649 * Invoke a method, using the specified arguments and return type, through 650 * one of the reflection interfaces. Could be a virtual or direct method 651 * (including constructors). Used for reflection. 652 * 653 * Deals with boxing/unboxing primitives and performs widening conversions. 654 * 655 * "invokeObj" will be null for a static method. 656 * 657 * If the invocation returns with an exception raised, we have to wrap it. 658 */ 659 Object* dvmInvokeMethod(Object* obj, const Method* method, 660 ArrayObject* argList, ArrayObject* params, ClassObject* returnType, 661 bool noAccessCheck) 662 { 663 ClassObject* clazz; 664 Object* retObj = NULL; 665 Thread* self = dvmThreadSelf(); 666 s4* ins; 667 int verifyCount, argListLength; 668 JValue retval; 669 bool needPop = false; 670 671 /* verify arg count */ 672 if (argList != NULL) 673 argListLength = argList->length; 674 else 675 argListLength = 0; 676 if (argListLength != (int) params->length) { 677 dvmThrowExceptionFmt(gDvm.exIllegalArgumentException, 678 "wrong number of arguments; expected %d, got %d", 679 params->length, argListLength); 680 return NULL; 681 } 682 683 clazz = callPrep(self, method, obj, !noAccessCheck); 684 if (clazz == NULL) 685 return NULL; 686 needPop = true; 687 688 /* "ins" for new frame start at frame pointer plus locals */ 689 ins = ((s4*)self->interpSave.curFrame) + 690 (method->registersSize - method->insSize); 691 verifyCount = 0; 692 693 //ALOGD(" FP is %p, INs live at >= %p", self->interpSave.curFrame, ins); 694 695 /* put "this" pointer into in0 if appropriate */ 696 if (!dvmIsStaticMethod(method)) { 697 assert(obj != NULL); 698 *ins++ = (s4) obj; 699 verifyCount++; 700 } 701 702 /* 703 * Copy the args onto the stack. Primitive types are converted when 704 * necessary, and object types are verified. 705 */ 706 DataObject** args = (DataObject**)(void*)argList->contents; 707 ClassObject** types = (ClassObject**)(void*)params->contents; 708 for (int i = 0; i < argListLength; i++) { 709 int width = dvmConvertArgument(*args++, *types++, ins); 710 if (width < 0) { 711 dvmPopFrame(self); // throw wants to pull PC out of stack 712 needPop = false; 713 throwArgumentTypeMismatch(i, *(types-1), *(args-1)); 714 goto bail; 715 } 716 717 ins += width; 718 verifyCount += width; 719 } 720 721 #ifndef NDEBUG 722 if (verifyCount != method->insSize) { 723 ALOGE("Got vfycount=%d insSize=%d for %s.%s", verifyCount, 724 method->insSize, clazz->descriptor, method->name); 725 assert(false); 726 goto bail; 727 } 728 #endif 729 730 if (dvmIsNativeMethod(method)) { 731 TRACE_METHOD_ENTER(self, method); 732 /* 733 * Because we leave no space for local variables, "curFrame" points 734 * directly at the method arguments. 735 */ 736 (*method->nativeFunc)((u4*)self->interpSave.curFrame, &retval, 737 method, self); 738 TRACE_METHOD_EXIT(self, method); 739 } else { 740 dvmInterpret(self, method, &retval); 741 } 742 743 /* 744 * Pop the frame immediately. The "wrap" calls below can cause 745 * allocations, and we don‘t want the GC to walk the now-dead frame. 746 */ 747 dvmPopFrame(self); 748 needPop = false; 749 750 /* 751 * If an exception is raised, wrap and replace. This is necessary 752 * because the invoked method could have thrown a checked exception 753 * that the caller wasn‘t prepared for. 754 * 755 * We might be able to do this up in the interpreted code, but that will 756 * leave us with a shortened stack trace in the top-level exception. 757 */ 758 if (dvmCheckException(self)) { 759 dvmWrapException("Ljava/lang/reflect/InvocationTargetException;"); 760 } else { 761 /* 762 * If this isn‘t a void method or constructor, convert the return type 763 * to an appropriate object. 764 * 765 * We don‘t do this when an exception is raised because the value 766 * in "retval" is undefined. 767 */ 768 if (returnType != NULL) { 769 retObj = (Object*)dvmBoxPrimitive(retval, returnType); 770 dvmReleaseTrackedAlloc(retObj, NULL); 771 } 772 } 773 774 bail: 775 if (needPop) { 776 dvmPopFrame(self); 777 } 778 return retObj; 779 } 780 781 struct LineNumFromPcContext { 782 u4 address; 783 u4 lineNum; 784 }; 785 786 static int lineNumForPcCb(void *cnxt, u4 address, u4 lineNum) 787 { 788 LineNumFromPcContext *pContext = (LineNumFromPcContext *)cnxt; 789 790 // We know that this callback will be called in 791 // ascending address order, so keep going until we find 792 // a match or we‘ve just gone past it. 793 794 if (address > pContext->address) { 795 // The line number from the previous positions callback 796 // wil be the final result. 797 return 1; 798 } 799 800 pContext->lineNum = lineNum; 801 802 return (address == pContext->address) ? 1 : 0; 803 } 804 805 /* 806 * Determine the source file line number based on the program counter. 807 * "pc" is an offset, in 16-bit units, from the start of the method‘s code. 808 * 809 * Returns -1 if no match was found (possibly because the source files were 810 * compiled without "-g", so no line number information is present). 811 * Returns -2 for native methods (as expected in exception traces). 812 */ 813 int dvmLineNumFromPC(const Method* method, u4 relPc) 814 { 815 const DexCode* pDexCode = dvmGetMethodCode(method); 816 817 if (pDexCode == NULL) { 818 if (dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method)) 819 return -2; 820 return -1; /* can happen for abstract method stub */ 821 } 822 823 LineNumFromPcContext context; 824 memset(&context, 0, sizeof(context)); 825 context.address = relPc; 826 // A method with no line number info should return -1 827 context.lineNum = -1; 828 829 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile, pDexCode, 830 method->clazz->descriptor, 831 method->prototype.protoIdx, 832 method->accessFlags, 833 lineNumForPcCb, NULL, &context); 834 835 return context.lineNum; 836 } 837 838 /* 839 * Compute the frame depth. 840 * 841 * Excludes "break" frames. 842 */ 843 int dvmComputeExactFrameDepth(const void* fp) 844 { 845 int count = 0; 846 847 for ( ; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) { 848 if (!dvmIsBreakFrame((u4*)fp)) 849 count++; 850 } 851 852 return count; 853 } 854 855 /* 856 * Compute the "vague" frame depth, which is just a pointer subtraction. 857 * The result is NOT an overly generous assessment of the number of 858 * frames; the only meaningful use is to compare against the result of 859 * an earlier invocation. 860 * 861 * Useful for implementing single-step debugger modes, which may need to 862 * call this for every instruction. 863 */ 864 int dvmComputeVagueFrameDepth(Thread* thread, const void* fp) 865 { 866 const u1* interpStackStart = thread->interpStackStart; 867 868 assert((u1*) fp >= interpStackStart - thread->interpStackSize); 869 assert((u1*) fp < interpStackStart); 870 return interpStackStart - (u1*) fp; 871 } 872 873 /* 874 * Get the calling frame. Pass in the current fp. 875 * 876 * Skip "break" frames and reflection invoke frames. 877 */ 878 void* dvmGetCallerFP(const void* curFrame) 879 { 880 void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame; 881 StackSaveArea* saveArea; 882 883 retry: 884 if (dvmIsBreakFrame((u4*)caller)) { 885 /* pop up one more */ 886 caller = SAVEAREA_FROM_FP(caller)->prevFrame; 887 if (caller == NULL) 888 return NULL; /* hit the top */ 889 890 /* 891 * If we got here by java.lang.reflect.Method.invoke(), we don‘t 892 * want to return Method‘s class loader. Shift up one and try 893 * again. 894 */ 895 saveArea = SAVEAREA_FROM_FP(caller); 896 if (dvmIsReflectionMethod(saveArea->method)) { 897 caller = saveArea->prevFrame; 898 assert(caller != NULL); 899 goto retry; 900 } 901 } 902 903 return caller; 904 } 905 906 /* 907 * Get the caller‘s class. Pass in the current fp. 908 * 909 * This is used by e.g. java.lang.Class. 910 */ 911 ClassObject* dvmGetCallerClass(const void* curFrame) 912 { 913 void* caller; 914 915 caller = dvmGetCallerFP(curFrame); 916 if (caller == NULL) 917 return NULL; 918 919 return SAVEAREA_FROM_FP(caller)->method->clazz; 920 } 921 922 /* 923 * Get the caller‘s caller‘s class. Pass in the current fp. 924 * 925 * This is used by e.g. java.lang.Class, which wants to know about the 926 * class loader of the method that called it. 927 */ 928 ClassObject* dvmGetCaller2Class(const void* curFrame) 929 { 930 void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame; 931 void* callerCaller; 932 933 /* at the top? */ 934 if (dvmIsBreakFrame((u4*)caller) && 935 SAVEAREA_FROM_FP(caller)->prevFrame == NULL) 936 return NULL; 937 938 /* go one more */ 939 callerCaller = dvmGetCallerFP(caller); 940 if (callerCaller == NULL) 941 return NULL; 942 943 return SAVEAREA_FROM_FP(callerCaller)->method->clazz; 944 } 945 946 /* 947 * Get the caller‘s caller‘s caller‘s class. Pass in the current fp. 948 * 949 * This is used by e.g. java.lang.Class, which wants to know about the 950 * class loader of the method that called it. 951 */ 952 ClassObject* dvmGetCaller3Class(const void* curFrame) 953 { 954 void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame; 955 int i; 956 957 /* at the top? */ 958 if (dvmIsBreakFrame((u4*)caller) && 959 SAVEAREA_FROM_FP(caller)->prevFrame == NULL) 960 return NULL; 961 962 /* Walk up two frames if possible. */ 963 for (i = 0; i < 2; i++) { 964 caller = dvmGetCallerFP(caller); 965 if (caller == NULL) 966 return NULL; 967 } 968 969 return SAVEAREA_FROM_FP(caller)->method->clazz; 970 } 971 972 /* 973 * Fill a flat array of methods that comprise the current interpreter 974 * stack trace. Pass in the current frame ptr. Break frames are 975 * skipped, but reflection invocations are not. 976 * 977 * The current frame will be in element 0. 978 */ 979 void dvmFillStackTraceArray(const void* fp, const Method** array, size_t length) 980 { 981 assert(fp != NULL); 982 assert(array != NULL); 983 size_t i = 0; 984 while (fp != NULL) { 985 if (!dvmIsBreakFrame((u4*)fp)) { 986 assert(i < length); 987 array[i++] = SAVEAREA_FROM_FP(fp)->method; 988 } 989 fp = SAVEAREA_FROM_FP(fp)->prevFrame; 990 } 991 } 992 993 /* 994 * Open up the reserved area and throw an exception. The reserved area 995 * should only be needed to create and initialize the exception itself. 996 * 997 * If we already opened it and we‘re continuing to overflow, abort the VM. 998 * 999 * We have to leave the "reserved" area open until the "catch" handler has1000 * finished doing its processing. This is because the catch handler may1001 * need to resolve classes, which requires calling into the class loader if1002 * the classes aren‘t already in the "initiating loader" list.1003 */1004 void dvmHandleStackOverflow(Thread* self, const Method* method)1005 {1006 /*1007 * Can we make the reserved area available?1008 */1009 if (self->stackOverflowed) {1010 /*1011 * Already did, nothing to do but bail.1012 */1013 ALOGE("DalvikVM: double-overflow of stack in threadid=%d; aborting",1014 self->threadId);1015 dvmDumpThread(self, false);1016 dvmAbort();1017 }1018 1019 /* open it up to the full range */1020 ALOGI("threadid=%d: stack overflow on call to %s.%s:%s",1021 self->threadId,1022 method->clazz->descriptor, method->name, method->shorty);1023 StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->interpSave.curFrame);1024 ALOGI(" method requires %d+%d+%d=%d bytes, fp is %p (%d left)",1025 method->registersSize * 4, sizeof(StackSaveArea), method->outsSize * 4,1026 (method->registersSize + method->outsSize) * 4 + sizeof(StackSaveArea),1027 saveArea, (u1*) saveArea - self->interpStackEnd);1028 ALOGI(" expanding stack end (%p to %p)", self->interpStackEnd,1029 self->interpStackStart - self->interpStackSize);1030 //dvmDumpThread(self, false);1031 self->interpStackEnd = self->interpStackStart - self->interpStackSize;1032 self->stackOverflowed = true;1033 1034 /*1035 * If we were trying to throw an exception when the stack overflowed,1036 * we will blow up when doing the class lookup on StackOverflowError1037 * because of the pending exception. So, we clear it and make it1038 * the cause of the SOE.1039 */1040 Object* excep = dvmGetException(self);1041 if (excep != NULL) {1042 ALOGW("Stack overflow while throwing exception");1043 dvmClearException(self);1044 }1045 dvmThrowChainedException(gDvm.exStackOverflowError, NULL, excep);1046 }1047 1048 /*1049 * Reduce the available stack size. By this point we should have finished1050 * our overflow processing.1051 */1052 void dvmCleanupStackOverflow(Thread* self, const Object* exception)1053 {1054 const u1* newStackEnd;1055 1056 assert(self->stackOverflowed);1057 1058 if (exception->clazz != gDvm.exStackOverflowError) {1059 /* exception caused during SOE, not the SOE itself */1060 return;1061 }1062 1063 newStackEnd = (self->interpStackStart - self->interpStackSize)1064 + STACK_OVERFLOW_RESERVE;1065 if ((u1*)self->interpSave.curFrame <= newStackEnd) {1066 ALOGE("Can‘t shrink stack: curFrame is in reserved area (%p %p)",1067 self->interpStackEnd, self->interpSave.curFrame);1068 dvmDumpThread(self, false);1069 dvmAbort();1070 }1071 1072 self->interpStackEnd = newStackEnd;1073 self->stackOverflowed = false;1074 1075 ALOGI("Shrank stack (to %p, curFrame is %p)", self->interpStackEnd,1076 self->interpSave.curFrame);1077 }1078 1079 1080 /*1081 * Extract the object that is the target of a monitor-enter instruction1082 * in the top stack frame of "thread".1083 *1084 * The other thread might be alive, so this has to work carefully.1085 *1086 * The thread list lock must be held.1087 *1088 * Returns "true" if we successfully recover the object. "*pOwner" will1089 * be NULL if we can‘t determine the owner for some reason (e.g. race1090 * condition on ownership transfer).1091 */1092 static bool extractMonitorEnterObject(Thread* thread, Object** pLockObj,1093 Thread** pOwner)1094 {1095 void* framePtr = thread->interpSave.curFrame;1096 1097 if (framePtr == NULL || dvmIsBreakFrame((u4*)framePtr))1098 return false;1099 1100 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);1101 const Method* method = saveArea->method;1102 const u2* currentPc = saveArea->xtra.currentPc;1103 1104 /* check Method* */1105 if (!dvmLinearAllocContains(method, sizeof(Method))) {1106 ALOGD("ExtrMon: method %p not valid", method);1107 return false;1108 }1109 1110 /* check currentPc */1111 u4 insnsSize = dvmGetMethodInsnsSize(method);1112 if (currentPc < method->insns ||1113 currentPc >= method->insns + insnsSize)1114 {1115 ALOGD("ExtrMon: insns %p not valid (%p - %p)",1116 currentPc, method->insns, method->insns + insnsSize);1117 return false;1118 }1119 1120 /* check the instruction */1121 if ((*currentPc & 0xff) != OP_MONITOR_ENTER) {1122 ALOGD("ExtrMon: insn at %p is not monitor-enter (0x%02x)",1123 currentPc, *currentPc & 0xff);1124 return false;1125 }1126 1127 /* get and check the register index */1128 unsigned int reg = *currentPc >> 8;1129 if (reg >= method->registersSize) {1130 ALOGD("ExtrMon: invalid register %d (max %d)",1131 reg, method->registersSize);1132 return false;1133 }1134 1135 /* get and check the object in that register */1136 u4* fp = (u4*) framePtr;1137 Object* obj = (Object*) fp[reg];1138 if (obj != NULL && !dvmIsHeapAddress(obj)) {1139 ALOGD("ExtrMon: invalid object %p at %p[%d]", obj, fp, reg);1140 return false;1141 }1142 *pLockObj = obj;1143 1144 /*1145 * Try to determine the object‘s lock holder; it‘s okay if this fails.1146 *1147 * We‘re assuming the thread list lock is already held by this thread.1148 * If it‘s not, we may be living dangerously if we have to scan through1149 * the thread list to find a match. (The VM will generally be in a1150 * suspended state when executing here, so this is a minor concern1151 * unless we‘re dumping while threads are running, in which case there‘s1152 * a good chance of stuff blowing up anyway.)1153 */1154 *pOwner = dvmGetObjectLockHolder(obj);1155 1156 return true;1157 }1158 1159 static void printWaitMessage(const DebugOutputTarget* target, const char* detail, Object* obj,1160 Thread* thread)1161 {1162 std::string msg(StringPrintf(" - waiting %s <%p> ", detail, obj));1163 1164 if (obj->clazz != gDvm.classJavaLangClass) {1165 // I(16573) - waiting on <0xf5feda38> (a java.util.LinkedList)1166 // I(16573) - waiting on <0xf5ed54f8> (a java.lang.Class<java.lang.ref.ReferenceQueue>)1167 msg += "(a " + dvmHumanReadableType(obj) + ")";1168 }1169 1170 if (thread != NULL) {1171 std::string threadName(dvmGetThreadName(thread));1172 StringAppendF(&msg, " held by tid=%d (%s)", thread->threadId, threadName.c_str());1173 }1174 1175 dvmPrintDebugMessage(target, "%s\n", msg.c_str());1176 }1177 1178 /*1179 * Dump stack frames, starting from the specified frame and moving down.1180 *1181 * Each frame holds a pointer to the currently executing method, and the1182 * saved program counter from the caller ("previous" frame). This means1183 * we don‘t have the PC for the current method on the stack, which is1184 * pretty reasonable since it‘s in the "PC register" for the VM. Because1185 * exceptions need to show the correct line number we actually *do* have1186 * an updated version in the fame‘s "xtra.currentPc", but it‘s unreliable.1187 *1188 * Note "framePtr" could be NULL in rare circumstances.1189 */1190 static void dumpFrames(const DebugOutputTarget* target, void* framePtr,1191 Thread* thread)1192 {1193 const StackSaveArea* saveArea;1194 const Method* method;1195 int checkCount = 0;1196 const u2* currentPc = NULL;1197 bool first = true;1198 1199 /*1200 * We call functions that require us to be holding the thread list lock.1201 * It‘s probable that the caller has already done so, but it‘s not1202 * guaranteed. If it‘s not locked, lock it now.1203 */1204 bool needThreadUnlock = dvmTryLockThreadList();1205 1206 /*1207 * The "currentPc" is updated whenever we execute an instruction that1208 * might throw an exception. Show it here.1209 */1210 if (framePtr != NULL && !dvmIsBreakFrame((u4*)framePtr)) {1211 saveArea = SAVEAREA_FROM_FP(framePtr);1212 1213 if (saveArea->xtra.currentPc != NULL)1214 currentPc = saveArea->xtra.currentPc;1215 }1216 1217 while (framePtr != NULL) {1218 saveArea = SAVEAREA_FROM_FP(framePtr);1219 method = saveArea->method;1220 1221 if (dvmIsBreakFrame((u4*)framePtr)) {1222 //dvmPrintDebugMessage(target, " (break frame)\n");1223 } else {1224 int relPc;1225 1226 if (currentPc != NULL)1227 relPc = currentPc - saveArea->method->insns;1228 else1229 relPc = -1;1230 1231 std::string methodName(dvmHumanReadableMethod(method, false));1232 if (dvmIsNativeMethod(method)) {1233 dvmPrintDebugMessage(target, " at %s(Native Method)\n",1234 methodName.c_str());1235 } else {1236 dvmPrintDebugMessage(target, " at %s(%s:%s%d)\n",1237 methodName.c_str(), dvmGetMethodSourceFile(method),1238 (relPc >= 0 && first) ? "~" : "",1239 relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc));1240 }1241 1242 if (first) {1243 /*1244 * Decorate WAIT and MONITOR threads with some detail on1245 * the first frame.1246 *1247 * warning: wait status not stable, even in suspend1248 */1249 if (thread->status == THREAD_WAIT ||1250 thread->status == THREAD_TIMED_WAIT)1251 {1252 Monitor* mon = thread->waitMonitor;1253 Object* obj = dvmGetMonitorObject(mon);1254 if (obj != NULL) {1255 Thread* joinThread = NULL;1256 if (obj->clazz == gDvm.classJavaLangVMThread) {1257 joinThread = dvmGetThreadFromThreadObject(obj);1258 }1259 if (joinThread == NULL) {1260 joinThread = dvmGetObjectLockHolder(obj);1261 }1262 printWaitMessage(target, "on", obj, joinThread);1263 }1264 } else if (thread->status == THREAD_MONITOR) {1265 Object* obj;1266 Thread* owner;1267 if (extractMonitorEnterObject(thread, &obj, &owner)) {1268 printWaitMessage(target, "to lock", obj, owner);1269 }1270 }1271 }1272 }1273 1274 /*1275 * Get saved PC for previous frame. There‘s no savedPc in a "break"1276 * frame, because that represents native or interpreted code1277 * invoked by the VM. The saved PC is sitting in the "PC register",1278 * a local variable on the native stack.1279 */1280 currentPc = saveArea->savedPc;1281 1282 first = false;1283 1284 if (saveArea->prevFrame != NULL && saveArea->prevFrame <= framePtr) {1285 ALOGW("Warning: loop in stack trace at frame %d (%p -> %p)",1286 checkCount, framePtr, saveArea->prevFrame);1287 break;1288 }1289 framePtr = saveArea->prevFrame;1290 1291 checkCount++;1292 if (checkCount > 300) {1293 dvmPrintDebugMessage(target,1294 " ***** printed %d frames, not showing any more\n",1295 checkCount);1296 break;1297 }1298 }1299 1300 if (needThreadUnlock) {1301 dvmUnlockThreadList();1302 }1303 }1304 1305 1306 /*1307 * Dump the stack for the specified thread.1308 */1309 void dvmDumpThreadStack(const DebugOutputTarget* target, Thread* thread)1310 {1311 dumpFrames(target, thread->interpSave.curFrame, thread);1312 }1313 1314 /*1315 * Dump the stack for the specified thread, which is still running.1316 *1317 * This is very dangerous, because stack frames are being pushed on and1318 * popped off, and if the thread exits we‘ll be looking at freed memory.1319 * The plan here is to take a snapshot of the stack and then dump that1320 * to try to minimize the chances of catching it mid-update. This should1321 * work reasonably well on a single-CPU system.1322 *1323 * There is a small chance that calling here will crash the VM.1324 */1325 void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread)1326 {1327 StackSaveArea* saveArea;1328 const u1* origStack;1329 u1* stackCopy = NULL;1330 int origSize, fpOffset;1331 void* fp;1332 int depthLimit = 200;1333 1334 if (thread == NULL || thread->interpSave.curFrame == NULL) {1335 dvmPrintDebugMessage(target,1336 "DumpRunning: Thread at %p has no curFrame (threadid=%d)\n",1337 thread, (thread != NULL) ? thread->threadId : 0);1338 return;1339 }1340 1341 /* wait for a full quantum */1342 sched_yield();1343 1344 /* copy the info we need, then the stack itself */1345 origSize = thread->interpStackSize;1346 origStack = (const u1*) thread->interpStackStart - origSize;1347 stackCopy = (u1*) malloc(origSize);1348 fpOffset = (u1*) thread->interpSave.curFrame - origStack;1349 memcpy(stackCopy, origStack, origSize);1350 1351 /*1352 * Run through the stack and rewrite the "prev" pointers.1353 */1354 //ALOGI("DR: fpOff=%d (from %p %p)",fpOffset, origStack,1355 // thread->interpSave.curFrame);1356 fp = stackCopy + fpOffset;1357 while (true) {1358 int prevOffset;1359 1360 if (depthLimit-- < 0) {1361 /* we‘re probably screwed */1362 dvmPrintDebugMessage(target, "DumpRunning: depth limit hit\n");1363 dvmAbort();1364 }1365 saveArea = SAVEAREA_FROM_FP(fp);1366 if (saveArea->prevFrame == NULL)1367 break;1368 1369 prevOffset = (u1*) saveArea->prevFrame - origStack;1370 if (prevOffset < 0 || prevOffset > origSize) {1371 dvmPrintDebugMessage(target,1372 "DumpRunning: bad offset found: %d (from %p %p)\n",1373 prevOffset, origStack, saveArea->prevFrame);1374 saveArea->prevFrame = NULL;1375 break;1376 }1377 1378 saveArea->prevFrame = (u4*)(stackCopy + prevOffset);1379 fp = saveArea->prevFrame;1380 }1381 1382 /*1383 * We still need to pass the Thread for some monitor wait stuff.1384 */1385 dumpFrames(target, stackCopy + fpOffset, thread);1386 free(stackCopy);1387 }1388 1389 /*1390 * Dump the native stack for the specified thread.1391 */1392 void dvmDumpNativeStack(const DebugOutputTarget* target, pid_t tid)1393 {1394 #ifdef HAVE_ANDROID_OS1395 const size_t MAX_DEPTH = 32;1396 backtrace_frame_t backtrace[MAX_DEPTH];1397 ssize_t frames = unwind_backtrace_thread(tid, backtrace, 0, MAX_DEPTH);1398 if (frames > 0) {1399 backtrace_symbol_t backtrace_symbols[MAX_DEPTH];1400 get_backtrace_symbols(backtrace, frames, backtrace_symbols);1401 1402 for (size_t i = 0; i < size_t(frames); i++) {1403 char line[MAX_BACKTRACE_LINE_LENGTH];1404 format_backtrace_line(i, &backtrace[i], &backtrace_symbols[i],1405 line, MAX_BACKTRACE_LINE_LENGTH);1406 dvmPrintDebugMessage(target, " %s\n", line);1407 }1408 1409 free_backtrace_symbols(backtrace_symbols, frames);1410 } else {1411 dvmPrintDebugMessage(target, " (native backtrace unavailable)\n");1412 }1413 #endif1414 }
1 //xref : /dalvik/vm/Jni.cpp 2 //http://androidxref.com/4.4.2_r2/xref/dalvik/vm/Jni.cpp 3 4 /* 5 * Copyright (C) 2008 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 /* 21 * Dalvik implementation of JNI interfaces. 22 */ 23 #include "Dalvik.h" 24 #include "JniInternal.h" 25 #include "Misc.h" 26 #include "ScopedPthreadMutexLock.h" 27 #include "UniquePtr.h" 28 29 #include <stdlib.h> 30 #include <stdarg.h> 31 #include <limits.h> 32 33 /* 34 Native methods and interaction with the GC 35 36 All JNI methods must start by changing their thread status to 37 THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before 38 returning to native code. The switch to "running" triggers a thread 39 suspension check. 40 41 With a rudimentary GC we should be able to skip the status change for 42 simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe 43 even access to fields with primitive types. Our options are more limited 44 with a compacting GC. 45 46 For performance reasons we do as little error-checking as possible here. 47 For example, we don‘t check to make sure the correct type of Object is 48 passed in when setting a field, and we don‘t prevent you from storing 49 new values in a "final" field. Such things are best handled in the 50 "check" version. For actions that are common, dangerous, and must be 51 checked at runtime, such as array bounds checks, we do the tests here. 52 53 54 General notes on local/global reference tracking 55 56 JNI provides explicit control over natively-held references that the GC 57 needs to know about. These can be local, in which case they‘re released 58 when the native method returns into the VM, or global, which are held 59 until explicitly released. (There are also weak-global references, 60 which have the lifespan and visibility of global references, but the 61 object they refer to may be collected.) 62 63 The references can be created with explicit JNI NewLocalRef / NewGlobalRef 64 calls. The former is very unusual, the latter is reasonably common 65 (e.g. for caching references to class objects). 66 67 Local references are most often created as a side-effect of JNI functions. 68 For example, the AllocObject/NewObject functions must create local 69 references to the objects returned, because nothing else in the GC root 70 set has a reference to the new objects. 71 72 The most common mode of operation is for a method to create zero or 73 more local references and return. Explicit "local delete" operations 74 are expected to be exceedingly rare, except when walking through an 75 object array, and the Push/PopLocalFrame calls are expected to be used 76 infrequently. For efficient operation, we want to add new local refs 77 with a simple store/increment operation; to avoid infinite growth in 78 pathological situations, we need to reclaim the space used by deleted 79 entries. 80 81 If we just want to maintain a list for the GC root set, we can use an 82 expanding append-only array that compacts when objects are deleted. 83 In typical situations, e.g. running through an array of objects, we will 84 be deleting one of the most recently added entries, so we can minimize 85 the number of elements moved (or avoid having to move any). 86 87 If we want to conceal the pointer values from native code, which is 88 necessary to allow the GC to move JNI-referenced objects around, then we 89 have to use a more complicated indirection mechanism. 90 91 The spec says, "Local references are only valid in the thread in which 92 they are created. The native code must not pass local references from 93 one thread to another." 94 95 96 Pinned objects 97 98 For some large chunks of data, notably primitive arrays and String data, 99 JNI allows the VM to choose whether it wants to pin the array object or 100 make a copy. We currently pin the memory for better execution performance. 101 102 TODO: we‘re using simple root set references to pin primitive array data, 103 because they have the property we need (i.e. the pointer we return is 104 guaranteed valid until we explicitly release it). However, if we have a 105 compacting GC and don‘t want to pin all memory held by all global refs, 106 we need to treat these differently. 107 108 109 Global reference tracking 110 111 There should be a small "active" set centered around the most-recently 112 added items. 113 114 Because it‘s global, access to it has to be synchronized. Additions and 115 removals require grabbing a mutex. If the table serves as an indirection 116 mechanism (i.e. it‘s not just a list for the benefit of the garbage 117 collector), reference lookups may also require grabbing a mutex. 118 119 The JNI spec does not define any sort of limit, so the list must be able 120 to expand to a reasonable size. It may be useful to log significant 121 increases in usage to help identify resource leaks. 122 123 124 Weak-global reference tracking 125 126 [TBD] 127 128 129 Local reference tracking 130 131 Each Thread/JNIEnv points to an IndirectRefTable. 132 133 We implement Push/PopLocalFrame with actual stack frames. Before a JNI 134 frame gets popped, we set "nextEntry" to the "top" pointer of the current 135 frame, effectively releasing the references. 136 137 The GC will scan all references in the table. 138 139 */ 140 141 static void ReportJniError() { 142 dvmDumpThread(dvmThreadSelf(), false); 143 dvmAbort(); 144 } 145 146 #ifdef WITH_JNI_STACK_CHECK 147 # define COMPUTE_STACK_SUM(_self) computeStackSum(_self); 148 # define CHECK_STACK_SUM(_self) checkStackSum(_self); 149 150 /* 151 * Compute a CRC on the entire interpreted stack. 152 * 153 * Would be nice to compute it on "self" as well, but there are parts of 154 * the Thread that can be altered by other threads (e.g. prev/next pointers). 155 */ 156 static void computeStackSum(Thread* self) { 157 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->interpSave.curFrame); 158 u4 crc = dvmInitCrc32(); 159 self->stackCrc = 0; 160 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low); 161 self->stackCrc = crc; 162 } 163 164 /* 165 * Compute a CRC on the entire interpreted stack, and compare it to what 166 * we previously computed. 167 * 168 * We can execute JNI directly from native code without calling in from 169 * interpreted code during VM initialization and immediately after JNI 170 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather 171 * than catching these cases we just ignore them here, which is marginally 172 * less accurate but reduces the amount of code we have to touch with #ifdefs. 173 */ 174 static void checkStackSum(Thread* self) { 175 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->interpSave.curFrame); 176 u4 stackCrc = self->stackCrc; 177 self->stackCrc = 0; 178 u4 crc = dvmInitCrc32(); 179 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low); 180 if (crc != stackCrc) { 181 const Method* meth = dvmGetCurrentJNIMethod(); 182 if (dvmComputeExactFrameDepth(self->interpSave.curFrame) == 1) { 183 ALOGD("JNI: bad stack CRC (0x%08x) -- okay during init", stackCrc); 184 } else if (strcmp(meth->name, "nativeLoad") == 0 && 185 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0)) { 186 ALOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad", stackCrc); 187 } else { 188 ALOGW("JNI: bad stack CRC (%08x vs %08x)", crc, stackCrc); 189 ReportJniError(); 190 } 191 } 192 self->stackCrc = (u4) -1; /* make logic errors more noticeable */ 193 } 194 195 #else 196 # define COMPUTE_STACK_SUM(_self) ((void)0) 197 # define CHECK_STACK_SUM(_self) ((void)0) 198 #endif 199 200 201 /* 202 * =========================================================================== 203 * Utility functions 204 * =========================================================================== 205 */ 206 207 /* 208 * Entry/exit processing for all JNI calls. 209 * 210 * We skip the (curiously expensive) thread-local storage lookup on our Thread*. 211 * If the caller has passed the wrong JNIEnv in, we‘re going to be accessing unsynchronized 212 * structures from more than one thread, and things are going to fail 213 * in bizarre ways. This is only sensible if the native code has been 214 * fully exercised with CheckJNI enabled. 215 */ 216 class ScopedJniThreadState { 217 public: 218 explicit ScopedJniThreadState(JNIEnv* env) { 219 mSelf = ((JNIEnvExt*) env)->self; 220 221 if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) { 222 // When emulating direct pointers with indirect references, it‘s critical 223 // that we use the correct per-thread indirect reference table. 224 Thread* self = gDvmJni.workAroundAppJniBugs ? dvmThreadSelf() : mSelf; 225 if (self != mSelf) { 226 ALOGE("JNI ERROR: env->self != thread-self (%p vs. %p); auto-correcting", mSelf, self); 227 mSelf = self; 228 } 229 } 230 231 CHECK_STACK_SUM(mSelf); 232 dvmChangeStatus(mSelf, THREAD_RUNNING); 233 } 234 235 ~ScopedJniThreadState() { 236 dvmChangeStatus(mSelf, THREAD_NATIVE); 237 COMPUTE_STACK_SUM(mSelf); 238 } 239 240 inline Thread* self() { 241 return mSelf; 242 } 243 244 private: 245 Thread* mSelf; 246 247 // Disallow copy and assignment. 248 ScopedJniThreadState(const ScopedJniThreadState&); 249 void operator=(const ScopedJniThreadState&); 250 }; 251 252 #define kGlobalRefsTableInitialSize 512 253 #define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */ 254 255 #define kWeakGlobalRefsTableInitialSize 16 256 257 #define kPinTableInitialSize 16 258 #define kPinTableMaxSize 1024 259 #define kPinComplainThreshold 10 260 261 bool dvmJniStartup() { 262 if (!gDvm.jniGlobalRefTable.init(kGlobalRefsTableInitialSize, 263 kGlobalRefsTableMaxSize, 264 kIndirectKindGlobal)) { 265 return false; 266 } 267 if (!gDvm.jniWeakGlobalRefTable.init(kWeakGlobalRefsTableInitialSize, 268 kGlobalRefsTableMaxSize, 269 kIndirectKindWeakGlobal)) { 270 return false; 271 } 272 273 dvmInitMutex(&gDvm.jniGlobalRefLock); 274 dvmInitMutex(&gDvm.jniWeakGlobalRefLock); 275 276 if (!dvmInitReferenceTable(&gDvm.jniPinRefTable, kPinTableInitialSize, kPinTableMaxSize)) { 277 return false; 278 } 279 280 dvmInitMutex(&gDvm.jniPinRefLock); 281 282 return true; 283 } 284 285 void dvmJniShutdown() { 286 gDvm.jniGlobalRefTable.destroy(); 287 gDvm.jniWeakGlobalRefTable.destroy(); 288 dvmClearReferenceTable(&gDvm.jniPinRefTable); 289 } 290 291 bool dvmIsBadJniVersion(int version) { 292 // We don‘t support JNI_VERSION_1_1. These are the only other valid versions. 293 return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6; 294 } 295 296 /* 297 * Find the JNIEnv associated with the current thread. 298 * 299 * Currently stored in the Thread struct. Could also just drop this into 300 * thread-local storage. 301 */ 302 JNIEnvExt* dvmGetJNIEnvForThread() { 303 Thread* self = dvmThreadSelf(); 304 if (self == NULL) { 305 return NULL; 306 } 307 return (JNIEnvExt*) dvmGetThreadJNIEnv(self); 308 } 309 310 /* 311 * Convert an indirect reference to an Object reference. The indirect 312 * reference may be local, global, or weak-global. 313 * 314 * If "jobj" is NULL, or is a weak global reference whose reference has 315 * been cleared, this returns NULL. If jobj is an invalid indirect 316 * reference, kInvalidIndirectRefObject is returned. 317 * 318 * Note "env" may be NULL when decoding global references. 319 */ 320 Object* dvmDecodeIndirectRef(Thread* self, jobject jobj) { 321 if (jobj == NULL) { 322 return NULL; 323 } 324 325 switch (indirectRefKind(jobj)) { 326 case kIndirectKindLocal: 327 { 328 Object* result = self->jniLocalRefTable.get(jobj); 329 if (UNLIKELY(result == NULL)) { 330 ALOGE("JNI ERROR (app bug): use of deleted local reference (%p)", jobj); 331 ReportJniError(); 332 } 333 return result; 334 } 335 case kIndirectKindGlobal: 336 { 337 // TODO: find a way to avoid the mutex activity here 338 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable; 339 ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock); 340 Object* result = pRefTable->get(jobj); 341 if (UNLIKELY(result == NULL)) { 342 ALOGE("JNI ERROR (app bug): use of deleted global reference (%p)", jobj); 343 ReportJniError(); 344 } 345 return result; 346 } 347 case kIndirectKindWeakGlobal: 348 { 349 // TODO: find a way to avoid the mutex activity here 350 IndirectRefTable* pRefTable = &gDvm.jniWeakGlobalRefTable; 351 ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock); 352 Object* result = pRefTable->get(jobj); 353 if (result == kClearedJniWeakGlobal) { 354 result = NULL; 355 } else if (UNLIKELY(result == NULL)) { 356 ALOGE("JNI ERROR (app bug): use of deleted weak global reference (%p)", jobj); 357 ReportJniError(); 358 } 359 return result; 360 } 361 case kIndirectKindInvalid: 362 default: 363 if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) { 364 // Assume an invalid local reference is actually a direct pointer. 365 return reinterpret_cast<Object*>(jobj); 366 } 367 ALOGW("Invalid indirect reference %p in decodeIndirectRef", jobj); 368 ReportJniError(); 369 return kInvalidIndirectRefObject; 370 } 371 } 372 373 static void AddLocalReferenceFailure(IndirectRefTable* pRefTable) { 374 pRefTable->dump("JNI local"); 375 ALOGE("Failed adding to JNI local ref table (has %zd entries)", pRefTable->capacity()); 376 ReportJniError(); // spec says call FatalError; this is equivalent 377 } 378 379 /* 380 * Add a local reference for an object to the current stack frame. When 381 * the native function returns, the reference will be discarded. 382 * 383 * We need to allow the same reference to be added multiple times. 384 * 385 * This will be called on otherwise unreferenced objects. We cannot do 386 * GC allocations here, and it‘s best if we don‘t grab a mutex. 387 */ 388 static inline jobject addLocalReference(Thread* self, Object* obj) { 389 if (obj == NULL) { 390 return NULL; 391 } 392 393 IndirectRefTable* pRefTable = &self->jniLocalRefTable; 394 void* curFrame = self->interpSave.curFrame; 395 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie; 396 jobject jobj = (jobject) pRefTable->add(cookie, obj); 397 if (UNLIKELY(jobj == NULL)) { 398 AddLocalReferenceFailure(pRefTable); 399 } 400 401 if (UNLIKELY(gDvmJni.workAroundAppJniBugs)) { 402 // Hand out direct pointers to support broken old apps. 403 return reinterpret_cast<jobject>(obj); 404 } 405 return jobj; 406 } 407 408 /* 409 * Ensure that at least "capacity" references can be held in the local 410 * refs table of the current thread. 411 */ 412 static bool ensureLocalCapacity(Thread* self, int capacity) { 413 int numEntries = self->jniLocalRefTable.capacity(); 414 // TODO: this isn‘t quite right, since "numEntries" includes holes 415 return ((kJniLocalRefMax - numEntries) >= capacity); 416 } 417 418 /* 419 * Explicitly delete a reference from the local list. 420 */ 421 static void deleteLocalReference(Thread* self, jobject jobj) { 422 if (jobj == NULL) { 423 return; 424 } 425 426 IndirectRefTable* pRefTable = &self->jniLocalRefTable; 427 void* curFrame = self->interpSave.curFrame; 428 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie; 429 if (!pRefTable->remove(cookie, jobj)) { 430 /* 431 * Attempting to delete a local reference that is not in the 432 * topmost local reference frame is a no-op. DeleteLocalRef returns 433 * void and doesn‘t throw any exceptions, but we should probably 434 * complain about it so the user will notice that things aren‘t 435 * going quite the way they expect. 436 */ 437 ALOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry", jobj); 438 } 439 } 440 441 /* 442 * Add a global reference for an object. 443 * 444 * We may add the same object more than once. Add/remove calls are paired, 445 * so it needs to appear on the list multiple times. 446 */ 447 static jobject addGlobalReference(Object* obj) { 448 if (obj == NULL) { 449 return NULL; 450 } 451 452 //ALOGI("adding obj=%p", obj); 453 //dvmDumpThread(dvmThreadSelf(), false); 454 455 if (false && dvmIsClassObject((Object*)obj)) { 456 ClassObject* clazz = (ClassObject*) obj; 457 ALOGI("-------"); 458 ALOGI("Adding global ref on class %s", clazz->descriptor); 459 dvmDumpThread(dvmThreadSelf(), false); 460 } 461 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) { 462 StringObject* strObj = (StringObject*) obj; 463 char* str = dvmCreateCstrFromString(strObj); 464 if (strcmp(str, "sync-response") == 0) { 465 ALOGI("-------"); 466 ALOGI("Adding global ref on string ‘%s‘", str); 467 dvmDumpThread(dvmThreadSelf(), false); 468 //dvmAbort(); 469 } 470 free(str); 471 } 472 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) { 473 ArrayObject* arrayObj = (ArrayObject*) obj; 474 if (arrayObj->length == 8192 /*&& 475 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/) 476 { 477 ALOGI("Adding global ref on byte array %p (len=%d)", 478 arrayObj, arrayObj->length); 479 dvmDumpThread(dvmThreadSelf(), false); 480 } 481 } 482 483 ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock); 484 485 /* 486 * Throwing an exception on failure is problematic, because JNI code 487 * may not be expecting an exception, and things sort of cascade. We 488 * want to have a hard limit to catch leaks during debugging, but this 489 * otherwise needs to expand until memory is consumed. As a practical 490 * matter, if we have many thousands of global references, chances are 491 * we‘re either leaking global ref table entries or we‘re going to 492 * run out of space in the GC heap. 493 */ 494 jobject jobj = (jobject) gDvm.jniGlobalRefTable.add(IRT_FIRST_SEGMENT, obj); 495 if (jobj == NULL) { 496 gDvm.jniGlobalRefTable.dump("JNI global"); 497 ALOGE("Failed adding to JNI global ref table (%zd entries)", 498 gDvm.jniGlobalRefTable.capacity()); 499 ReportJniError(); 500 } 501 502 LOGVV("GREF add %p (%s.%s)", obj, 503 dvmGetCurrentJNIMethod()->clazz->descriptor, 504 dvmGetCurrentJNIMethod()->name); 505 506 return jobj; 507 } 508 509 static jobject addWeakGlobalReference(Object* obj) { 510 if (obj == NULL) { 511 return NULL; 512 } 513 514 ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock); 515 IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable; 516 jobject jobj = (jobject) table->add(IRT_FIRST_SEGMENT, obj); 517 if (jobj == NULL) { 518 gDvm.jniWeakGlobalRefTable.dump("JNI weak global"); 519 ALOGE("Failed adding to JNI weak global ref table (%zd entries)", table->capacity()); 520 ReportJniError(); 521 } 522 return jobj; 523 } 524 525 static void deleteWeakGlobalReference(jobject jobj) { 526 if (jobj == NULL) { 527 return; 528 } 529 530 ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock); 531 IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable; 532 if (!table->remove(IRT_FIRST_SEGMENT, jobj)) { 533 ALOGW("JNI: DeleteWeakGlobalRef(%p) failed to find entry", jobj); 534 } 535 } 536 537 /* 538 * Remove a global reference. In most cases it‘s the entry most recently 539 * added, which makes this pretty quick. 540 * 541 * Thought: if it‘s not the most recent entry, just null it out. When we 542 * fill up, do a compaction pass before we expand the list. 543 */ 544 static void deleteGlobalReference(jobject jobj) { 545 if (jobj == NULL) { 546 return; 547 } 548 549 ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock); 550 if (!gDvm.jniGlobalRefTable.remove(IRT_FIRST_SEGMENT, jobj)) { 551 ALOGW("JNI: DeleteGlobalRef(%p) failed to find entry", jobj); 552 return; 553 } 554 } 555 556 /* 557 * Objects don‘t currently move, so we just need to create a reference 558 * that will ensure the array object isn‘t collected. 559 * 560 * We use a separate reference table, which is part of the GC root set. 561 */ 562 static void pinPrimitiveArray(ArrayObject* arrayObj) { 563 if (arrayObj == NULL) { 564 return; 565 } 566 567 ScopedPthreadMutexLock lock(&gDvm.jniPinRefLock); 568 569 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) { 570 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array"); 571 ALOGE("Failed adding to JNI pinned array ref table (%d entries)", 572 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable)); 573 ReportJniError(); 574 } 575 576 /* 577 * The total number of pinned primitive arrays should be pretty small. 578 * A single array should not be pinned more than once or twice; any 579 * more than that is a strong indicator that a Release function is 580 * not being called. 581 */ 582 int count = 0; 583 Object** ppObj = gDvm.jniPinRefTable.table; 584 while (ppObj < gDvm.jniPinRefTable.nextEntry) { 585 if (*ppObj++ == (Object*) arrayObj) { 586 count++; 587 } 588 } 589 590 if (count > kPinComplainThreshold) { 591 ALOGW("JNI: pin count on array %p (%s) is now %d", 592 arrayObj, arrayObj->clazz->descriptor, count); 593 /* keep going */ 594 } 595 } 596 597 /* 598 * Un-pin the array object. If an object was pinned twice, it must be 599 * unpinned twice before it‘s free to move. 600 */ 601 static void unpinPrimitiveArray(ArrayObject* arrayObj) { 602 if (arrayObj == NULL) { 603 return; 604 } 605 606 ScopedPthreadMutexLock lock(&gDvm.jniPinRefLock); 607 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable, 608 gDvm.jniPinRefTable.table, (Object*) arrayObj)) 609 { 610 ALOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)", 611 arrayObj, dvmIsHeapAddress((Object*) arrayObj)); 612 return; 613 } 614 } 615 616 /* 617 * Dump the contents of the JNI reference tables to the log file. 618 * 619 * We only dump the local refs associated with the current thread. 620 */ 621 void dvmDumpJniReferenceTables() { 622 Thread* self = dvmThreadSelf(); 623 self->jniLocalRefTable.dump("JNI local"); 624 gDvm.jniGlobalRefTable.dump("JNI global"); 625 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array"); 626 } 627 628 void dvmDumpJniStats(DebugOutputTarget* target) { 629 dvmPrintDebugMessage(target, "JNI: CheckJNI is %s", gDvmJni.useCheckJni ? "on" : "off"); 630 if (gDvmJni.forceCopy) { 631 dvmPrintDebugMessage(target, " (with forcecopy)"); 632 } 633 dvmPrintDebugMessage(target, "; workarounds are %s", gDvmJni.workAroundAppJniBugs ? "on" : "off"); 634 635 dvmLockMutex(&gDvm.jniPinRefLock); 636 dvmPrintDebugMessage(target, "; pins=%d", dvmReferenceTableEntries(&gDvm.jniPinRefTable)); 637 dvmUnlockMutex(&gDvm.jniPinRefLock); 638 639 dvmLockMutex(&gDvm.jniGlobalRefLock); 640 dvmPrintDebugMessage(target, "; globals=%d", gDvm.jniGlobalRefTable.capacity()); 641 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 642 643 dvmLockMutex(&gDvm.jniWeakGlobalRefLock); 644 size_t weaks = gDvm.jniWeakGlobalRefTable.capacity(); 645 if (weaks > 0) { 646 dvmPrintDebugMessage(target, " (plus %d weak)", weaks); 647 } 648 dvmUnlockMutex(&gDvm.jniWeakGlobalRefLock); 649 650 dvmPrintDebugMessage(target, "\n\n"); 651 } 652 653 /* 654 * Verify that a reference passed in from native code is one that the 655 * code is allowed to have. 656 * 657 * It‘s okay for native code to pass us a reference that: 658 * - was passed in as an argument when invoked by native code (and hence 659 * is in the JNI local refs table) 660 * - was returned to it from JNI (and is now in the local refs table) 661 * - is present in the JNI global refs table 662 * 663 * Used by -Xcheck:jni and GetObjectRefType. 664 */ 665 jobjectRefType dvmGetJNIRefType(Thread* self, jobject jobj) { 666 /* 667 * IndirectRefKind is currently defined as an exact match of 668 * jobjectRefType, so this is easy. We have to decode it to determine 669 * if it‘s a valid reference and not merely valid-looking. 670 */ 671 assert(jobj != NULL); 672 673 Object* obj = dvmDecodeIndirectRef(self, jobj); 674 if (obj == reinterpret_cast<Object*>(jobj) && gDvmJni.workAroundAppJniBugs) { 675 // If we‘re handing out direct pointers, check whether ‘jobj‘ is a direct reference 676 // to a local reference. 677 return self->jniLocalRefTable.contains(obj) ? JNILocalRefType : JNIInvalidRefType; 678 } else if (obj == kInvalidIndirectRefObject) { 679 return JNIInvalidRefType; 680 } else { 681 return (jobjectRefType) indirectRefKind(jobj); 682 } 683 } 684 685 static void dumpMethods(Method* methods, size_t methodCount, const char* name) { 686 size_t i; 687 for (i = 0; i < methodCount; ++i) { 688 Method* method = &methods[i]; 689 if (strcmp(name, method->name) == 0) { 690 char* desc = dexProtoCopyMethodDescriptor(&method->prototype); 691 ALOGE("Candidate: %s.%s:%s", method->clazz->descriptor, name, desc); 692 free(desc); 693 } 694 } 695 } 696 697 static void dumpCandidateMethods(ClassObject* clazz, const char* methodName, const char* signature) { 698 ALOGE("ERROR: couldn‘t find native method"); 699 ALOGE("Requested: %s.%s:%s", clazz->descriptor, methodName, signature); 700 dumpMethods(clazz->virtualMethods, clazz->virtualMethodCount, methodName); 701 dumpMethods(clazz->directMethods, clazz->directMethodCount, methodName); 702 } 703 704 static void throwNoSuchMethodError(ClassObject* c, const char* name, const char* sig, const char* kind) { 705 std::string msg(StringPrintf("no %s method \"%s.%s%s\"", kind, c->descriptor, name, sig)); 706 dvmThrowNoSuchMethodError(msg.c_str()); 707 } 708 709 /* 710 * Register a method that uses JNI calling conventions. 711 */ 712 static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName, 713 const char* signature, void* fnPtr) 714 { 715 if (fnPtr == NULL) { 716 return false; 717 } 718 719 // If a signature starts with a ‘!‘, we take that as a sign that the native code doesn‘t 720 // need the extra JNI arguments (the JNIEnv* and the jclass). 721 bool fastJni = false; 722 if (*signature == ‘!‘) { 723 fastJni = true; 724 ++signature; 725 ALOGV("fast JNI method %s.%s:%s detected", clazz->descriptor, methodName, signature); 726 } 727 728 Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature); 729 if (method == NULL) { 730 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature); 731 } 732 if (method == NULL) { 733 dumpCandidateMethods(clazz, methodName, signature); 734 throwNoSuchMethodError(clazz, methodName, signature, "static or non-static"); 735 return false; 736 } 737 738 if (!dvmIsNativeMethod(method)) { 739 ALOGW("Unable to register: not native: %s.%s:%s", clazz->descriptor, methodName, signature); 740 throwNoSuchMethodError(clazz, methodName, signature, "native"); 741 return false; 742 } 743 744 if (fastJni) { 745 // In this case, we have extra constraints to check... 746 if (dvmIsSynchronizedMethod(method)) { 747 // Synchronization is usually provided by the JNI bridge, 748 // but we won‘t have one. 749 ALOGE("fast JNI method %s.%s:%s cannot be synchronized", 750 clazz->descriptor, methodName, signature); 751 return false; 752 } 753 if (!dvmIsStaticMethod(method)) { 754 // There‘s no real reason for this constraint, but since we won‘t 755 // be supplying a JNIEnv* or a jobject ‘this‘, you‘re effectively 756 // static anyway, so it seems clearer to say so. 757 ALOGE("fast JNI method %s.%s:%s cannot be non-static", 758 clazz->descriptor, methodName, signature); 759 return false; 760 } 761 } 762 763 if (method->nativeFunc != dvmResolveNativeMethod) { 764 /* this is allowed, but unusual */ 765 ALOGV("Note: %s.%s:%s was already registered", clazz->descriptor, methodName, signature); 766 } 767 768 method->fastJni = fastJni; 769 dvmUseJNIBridge(method, fnPtr); 770 771 ALOGV("JNI-registered %s.%s:%s", clazz->descriptor, methodName, signature); 772 return true; 773 } 774 775 static const char* builtInPrefixes[] = { 776 "Landroid/", 777 "Lcom/android/", 778 "Lcom/google/android/", 779 "Ldalvik/", 780 "Ljava/", 781 "Ljavax/", 782 "Llibcore/", 783 "Lorg/apache/harmony/", 784 }; 785 786 static bool shouldTrace(Method* method) { 787 const char* className = method->clazz->descriptor; 788 // Return true if the -Xjnitrace setting implies we should trace ‘method‘. 789 if (gDvm.jniTrace && strstr(className, gDvm.jniTrace)) { 790 return true; 791 } 792 // Return true if we‘re trying to log all third-party JNI activity and ‘method‘ doesn‘t look 793 // like part of Android. 794 if (gDvmJni.logThirdPartyJni) { 795 for (size_t i = 0; i < NELEM(builtInPrefixes); ++i) { 796 if (strstr(className, builtInPrefixes[i]) == className) { 797 return false; 798 } 799 } 800 return true; 801 } 802 return false; 803 } 804 805 /* 806 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns" 807 * to point at the actual function. 808 */ 809 void dvmUseJNIBridge(Method* method, void* func) { 810 method->shouldTrace = shouldTrace(method); 811 812 // Does the method take any reference arguments? 813 method->noRef = true; 814 const char* cp = method->shorty; 815 while (*++cp != ‘\0‘) { // Pre-increment to skip return type. 816 if (*cp == ‘L‘) { 817 method->noRef = false; 818 break; 819 } 820 } 821 822 DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod; 823 dvmSetNativeFunc(method, bridge, (const u2*) func); 824 } 825 826 // TODO: rewrite this to share code with CheckJNI‘s tracing... 827 static void appendValue(char type, const JValue value, char* buf, size_t n, bool appendComma) 828 { 829 size_t len = strlen(buf); 830 if (len >= n - 32) { // 32 should be longer than anything we could append. 831 buf[len - 1] = ‘.‘; 832 buf[len - 2] = ‘.‘; 833 buf[len - 3] = ‘.‘; 834 return; 835 } 836 char* p = buf + len; 837 switch (type) { 838 case ‘B‘: 839 if (value.b >= 0 && value.b < 10) { 840 sprintf(p, "%d", value.b); 841 } else { 842 sprintf(p, "%#x (%d)", value.b, value.b); 843 } 844 break; 845 case ‘C‘: 846 if (value.c < 0x7f && value.c >= ‘ ‘) { 847 sprintf(p, "U+%x (‘%c‘)", value.c, value.c); 848 } else { 849 sprintf(p, "U+%x", value.c); 850 } 851 break; 852 case ‘D‘: 853 sprintf(p, "%g", value.d); 854 break; 855 case ‘F‘: 856 sprintf(p, "%g", value.f); 857 break; 858 case ‘I‘: 859 sprintf(p, "%d", value.i); 860 break; 861 case ‘L‘: 862 sprintf(p, "%#x", value.i); 863 break; 864 case ‘J‘: 865 sprintf(p, "%lld", value.j); 866 break; 867 case ‘S‘: 868 sprintf(p, "%d", value.s); 869 break; 870 case ‘V‘: 871 strcpy(p, "void"); 872 break; 873 case ‘Z‘: 874 strcpy(p, value.z ? "true" : "false"); 875 break; 876 default: 877 sprintf(p, "unknown type ‘%c‘", type); 878 break; 879 } 880 881 if (appendComma) { 882 strcat(p, ", "); 883 } 884 } 885 886 static void logNativeMethodEntry(const Method* method, const u4* args) 887 { 888 char thisString[32] = { 0 }; 889 const u4* sp = args; 890 if (!dvmIsStaticMethod(method)) { 891 sprintf(thisString, "this=0x%08x ", *sp++); 892 } 893 894 char argsString[128]= { 0 }; 895 const char* desc = &method->shorty[1]; 896 while (*desc != ‘\0‘) { 897 char argType = *desc++; 898 JValue value; 899 if (argType == ‘D‘ || argType == ‘J‘) { 900 value.j = dvmGetArgLong(sp, 0); 901 sp += 2; 902 } else { 903 value.i = *sp++; 904 } 905 appendValue(argType, value, argsString, sizeof(argsString), 906 *desc != ‘\0‘); 907 } 908 909 std::string className(dvmHumanReadableDescriptor(method->clazz->descriptor)); 910 char* signature = dexProtoCopyMethodDescriptor(&method->prototype); 911 ALOGI("-> %s %s%s %s(%s)", className.c_str(), method->name, signature, thisString, argsString); 912 free(signature); 913 } 914 915 static void logNativeMethodExit(const Method* method, Thread* self, const JValue returnValue) 916 { 917 std::string className(dvmHumanReadableDescriptor(method->clazz->descriptor)); 918 char* signature = dexProtoCopyMethodDescriptor(&method->prototype); 919 if (dvmCheckException(self)) { 920 Object* exception = dvmGetException(self); 921 std::string exceptionClassName(dvmHumanReadableDescriptor(exception->clazz->descriptor)); 922 ALOGI("<- %s %s%s threw %s", className.c_str(), 923 method->name, signature, exceptionClassName.c_str()); 924 } else { 925 char returnValueString[128] = { 0 }; 926 char returnType = method->shorty[0]; 927 appendValue(returnType, returnValue, returnValueString, sizeof(returnValueString), false); 928 ALOGI("<- %s %s%s returned %s", className.c_str(), 929 method->name, signature, returnValueString); 930 } 931 free(signature); 932 } 933 934 /* 935 * Get the method currently being executed by examining the interp stack. 936 */ 937 const Method* dvmGetCurrentJNIMethod() { 938 assert(dvmThreadSelf() != NULL); 939 940 void* fp = dvmThreadSelf()->interpSave.curFrame; 941 const Method* meth = SAVEAREA_FROM_FP(fp)->method; 942 943 assert(meth != NULL); 944 assert(dvmIsNativeMethod(meth)); 945 return meth; 946 } 947 948 /* 949 * Track a JNI MonitorEnter in the current thread. 950 * 951 * The goal is to be able to "implicitly" release all JNI-held monitors 952 * when the thread detaches. 953 * 954 * Monitors may be entered multiple times, so we add a new entry for each 955 * enter call. It would be more efficient to keep a counter. At present 956 * there‘s no real motivation to improve this however. 957 */ 958 static void trackMonitorEnter(Thread* self, Object* obj) { 959 static const int kInitialSize = 16; 960 ReferenceTable* refTable = &self->jniMonitorRefTable; 961 962 /* init table on first use */ 963 if (refTable->table == NULL) { 964 assert(refTable->maxEntries == 0); 965 966 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) { 967 ALOGE("Unable to initialize monitor tracking table"); 968 ReportJniError(); 969 } 970 } 971 972 if (!dvmAddToReferenceTable(refTable, obj)) { 973 /* ran out of memory? could throw exception instead */ 974 ALOGE("Unable to add entry to monitor tracking table"); 975 ReportJniError(); 976 } else { 977 LOGVV("--- added monitor %p", obj); 978 } 979 } 980 981 /* 982 * Track a JNI MonitorExit in the current thread. 983 */ 984 static void trackMonitorExit(Thread* self, Object* obj) { 985 ReferenceTable* pRefTable = &self->jniMonitorRefTable; 986 987 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) { 988 ALOGE("JNI monitor %p not found in tracking list", obj); 989 /* keep going? */ 990 } else { 991 LOGVV("--- removed monitor %p", obj); 992 } 993 } 994 995 /* 996 * Release all monitors held by the jniMonitorRefTable list. 997 */ 998 void dvmReleaseJniMonitors(Thread* self) { 999 ReferenceTable* pRefTable = &self->jniMonitorRefTable;1000 Object** top = pRefTable->table;1001 1002 if (top == NULL) {1003 return;1004 }1005 Object** ptr = pRefTable->nextEntry;1006 while (--ptr >= top) {1007 if (!dvmUnlockObject(self, *ptr)) {1008 ALOGW("Unable to unlock monitor %p at thread detach", *ptr);1009 } else {1010 LOGVV("--- detach-releasing monitor %p", *ptr);1011 }1012 }1013 1014 /* zap it */1015 pRefTable->nextEntry = pRefTable->table;1016 }1017 1018 /*1019 * Determine if the specified class can be instantiated from JNI. This1020 * is used by AllocObject / NewObject, which are documented as throwing1021 * an exception for abstract and interface classes, and not accepting1022 * array classes. We also want to reject attempts to create new Class1023 * objects, since only DefineClass should do that.1024 */1025 static bool canAllocClass(ClassObject* clazz) {1026 if (dvmIsAbstractClass(clazz) || dvmIsInterfaceClass(clazz)) {1027 /* JNI spec defines what this throws */1028 dvmThrowInstantiationException(clazz, "abstract class or interface");1029 return false;1030 } else if (dvmIsArrayClass(clazz) || dvmIsTheClassClass(clazz)) {1031 /* spec says "must not" for arrays, ignores Class */1032 dvmThrowInstantiationException(clazz, "wrong JNI function");1033 return false;1034 }1035 return true;1036 }1037 1038 1039 /*1040 * ===========================================================================1041 * JNI call bridge1042 * ===========================================================================1043 */1044 1045 /*1046 * The functions here form a bridge between interpreted code and JNI native1047 * functions. The basic task is to convert an array of primitives and1048 * references into C-style function arguments. This is architecture-specific1049 * and usually requires help from assembly code.1050 *1051 * The bridge takes four arguments: the array of parameters, a place to1052 * store the function result (if any), the method to call, and a pointer1053 * to the current thread.1054 *1055 * These functions aren‘t called directly from elsewhere in the VM.1056 * A pointer in the Method struct points to one of these, and when a native1057 * method is invoked the interpreter jumps to it.1058 *1059 * (The "internal native" methods are invoked the same way, but instead1060 * of calling through a bridge, the target method is called directly.)1061 *1062 * The "args" array should not be modified, but we do so anyway for1063 * performance reasons. We know that it points to the "outs" area on1064 * the current method‘s interpreted stack. This area is ignored by the1065 * precise GC, because there is no register map for a native method (for1066 * an interpreted method the args would be listed in the argument set).1067 * We know all of the values exist elsewhere on the interpreted stack,1068 * because the method call setup copies them right before making the call,1069 * so we don‘t have to worry about concealing stuff from the GC.1070 *1071 * If we don‘t want to modify "args", we either have to create a local1072 * copy and modify it before calling dvmPlatformInvoke, or we have to do1073 * the local reference replacement within dvmPlatformInvoke. The latter1074 * has some performance advantages, though if we can inline the local1075 * reference adds we may win when there‘s a lot of reference args (unless1076 * we want to code up some local ref table manipulation in assembly.1077 */1078 1079 /*1080 * If necessary, convert the value in pResult from a local/global reference1081 * to an object pointer.1082 *1083 * If the returned reference is invalid, kInvalidIndirectRefObject will1084 * be returned in pResult.1085 */1086 static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,1087 const Method* method, Thread* self)1088 {1089 if (method->shorty[0] == ‘L‘ && !dvmCheckException(self) && pResult->l != NULL) {1090 pResult->l = dvmDecodeIndirectRef(self, (jobject) pResult->l);1091 }1092 }1093 1094 /*1095 * General form, handles all cases.1096 */1097 void dvmCallJNIMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) {1098 u4* modArgs = (u4*) args;1099 jclass staticMethodClass = NULL;1100 1101 u4 accessFlags = method->accessFlags;1102 bool isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0;1103 1104 //ALOGI("JNI calling %p (%s.%s:%s):", method->insns,1105 // method->clazz->descriptor, method->name, method->shorty);1106 1107 /*1108 * Walk the argument list, creating local references for appropriate1109 * arguments.1110 */1111 int idx = 0;1112 Object* lockObj;1113 if ((accessFlags & ACC_STATIC) != 0) {1114 lockObj = (Object*) method->clazz;1115 /* add the class object we pass in */1116 staticMethodClass = (jclass) addLocalReference(self, (Object*) method->clazz);1117 } else {1118 lockObj = (Object*) args[0];1119 /* add "this" */1120 modArgs[idx++] = (u4) addLocalReference(self, (Object*) modArgs[0]);1121 }1122 1123 if (!method->noRef) {1124 const char* shorty = &method->shorty[1]; /* skip return type */1125 while (*shorty != ‘\0‘) {1126 switch (*shorty++) {1127 case ‘L‘:1128 //ALOGI(" local %d: 0x%08x", idx, modArgs[idx]);1129 if (modArgs[idx] != 0) {1130 modArgs[idx] = (u4) addLocalReference(self, (Object*) modArgs[idx]);1131 }1132 break;1133 case ‘D‘:1134 case ‘J‘:1135 idx++;1136 break;1137 default:1138 /* Z B C S I -- do nothing */1139 break;1140 }1141 idx++;1142 }1143 }1144 1145 if (UNLIKELY(method->shouldTrace)) {1146 logNativeMethodEntry(method, args);1147 }1148 if (UNLIKELY(isSynchronized)) {1149 dvmLockObject(self, lockObj);1150 }1151 1152 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_NATIVE);1153 1154 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */1155 assert(method->insns != NULL);1156 1157 JNIEnv* env = self->jniEnv;1158 COMPUTE_STACK_SUM(self);1159 dvmPlatformInvoke(env,1160 (ClassObject*) staticMethodClass,1161 method->jniArgInfo, method->insSize, modArgs, method->shorty,1162 (void*) method->insns, pResult);1163 CHECK_STACK_SUM(self);1164 1165 dvmChangeStatus(self, oldStatus);1166 1167 convertReferenceResult(env, pResult, method, self);1168 1169 if (UNLIKELY(isSynchronized)) {1170 dvmUnlockObject(self, lockObj);1171 }1172 if (UNLIKELY(method->shouldTrace)) {1173 logNativeMethodExit(method, self, *pResult);1174 }1175 }1176 1177 /*1178 * ===========================================================================1179 * JNI implementation1180 * ===========================================================================1181 */1182 1183 /*1184 * Return the version of the native method interface.1185 */1186 static jint GetVersion(JNIEnv* env) {1187 /*1188 * There is absolutely no need to toggle the mode for correct behavior.1189 * However, it does provide native code with a simple "suspend self1190 * if necessary" call.1191 */1192 ScopedJniThreadState ts(env);1193 return JNI_VERSION_1_6;1194 }1195 1196 /*1197 * Create a new class from a bag of bytes.1198 *1199 * This is not currently supported within Dalvik.1200 */1201 static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,1202 const jbyte* buf, jsize bufLen)1203 {1204 UNUSED_PARAMETER(name);1205 UNUSED_PARAMETER(loader);1206 UNUSED_PARAMETER(buf);1207 UNUSED_PARAMETER(bufLen);1208 1209 ScopedJniThreadState ts(env);1210 ALOGW("JNI DefineClass is not supported");1211 return NULL;1212 }1213 1214 /*1215 * Find a class by name.1216 *1217 * We have to use the "no init" version of FindClass here, because we might1218 * be getting the class prior to registering native methods that will be1219 * used in <clinit>.1220 *1221 * We need to get the class loader associated with the current native1222 * method. If there is no native method, e.g. we‘re calling this from native1223 * code right after creating the VM, the spec says we need to use the class1224 * loader returned by "ClassLoader.getBaseClassLoader". There is no such1225 * method, but it‘s likely they meant ClassLoader.getSystemClassLoader.1226 * We can‘t get that until after the VM has initialized though.1227 */1228 static jclass FindClass(JNIEnv* env, const char* name) {1229 ScopedJniThreadState ts(env);1230 1231 const Method* thisMethod = dvmGetCurrentJNIMethod();1232 assert(thisMethod != NULL);1233 1234 Object* loader;1235 Object* trackedLoader = NULL;1236 if (ts.self()->classLoaderOverride != NULL) {1237 /* hack for JNI_OnLoad */1238 assert(strcmp(thisMethod->name, "nativeLoad") == 0);1239 loader = ts.self()->classLoaderOverride;1240 } else if (thisMethod == gDvm.methDalvikSystemNativeStart_main ||1241 thisMethod == gDvm.methDalvikSystemNativeStart_run) {1242 /* start point of invocation interface */1243 if (!gDvm.initializing) {1244 loader = trackedLoader = dvmGetSystemClassLoader();1245 } else {1246 loader = NULL;1247 }1248 } else {1249 loader = thisMethod->clazz->classLoader;1250 }1251 1252 char* descriptor = dvmNameToDescriptor(name);1253 if (descriptor == NULL) {1254 return NULL;1255 }1256 ClassObject* clazz = dvmFindClassNoInit(descriptor, loader);1257 free(descriptor);1258 1259 jclass jclazz = (jclass) addLocalReference(ts.self(), (Object*) clazz);1260 dvmReleaseTrackedAlloc(trackedLoader, ts.self());1261 return jclazz;1262 }1263 1264 /*1265 * Return the superclass of a class.1266 */1267 static jclass GetSuperclass(JNIEnv* env, jclass jclazz) {1268 ScopedJniThreadState ts(env);1269 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1270 return (jclass) addLocalReference(ts.self(), (Object*)clazz->super);1271 }1272 1273 /*1274 * Determine whether an object of clazz1 can be safely cast to clazz2.1275 *1276 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.1277 */1278 static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2) {1279 ScopedJniThreadState ts(env);1280 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz1);1281 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz2);1282 return dvmInstanceof(clazz1, clazz2);1283 }1284 1285 /*1286 * Given a java.lang.reflect.Method or .Constructor, return a methodID.1287 */1288 static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod) {1289 ScopedJniThreadState ts(env);1290 Object* method = dvmDecodeIndirectRef(ts.self(), jmethod);1291 return (jmethodID) dvmGetMethodFromReflectObj(method);1292 }1293 1294 /*1295 * Given a java.lang.reflect.Field, return a fieldID.1296 */1297 static jfieldID FromReflectedField(JNIEnv* env, jobject jfield) {1298 ScopedJniThreadState ts(env);1299 Object* field = dvmDecodeIndirectRef(ts.self(), jfield);1300 return (jfieldID) dvmGetFieldFromReflectObj(field);1301 }1302 1303 /*1304 * Convert a methodID to a java.lang.reflect.Method or .Constructor.1305 *1306 * (The "isStatic" field does not appear in the spec.)1307 *1308 * Throws OutOfMemory and returns NULL on failure.1309 */1310 static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID, jboolean isStatic) {1311 ScopedJniThreadState ts(env);1312 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jcls);1313 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);1314 dvmReleaseTrackedAlloc(obj, NULL);1315 return addLocalReference(ts.self(), obj);1316 }1317 1318 /*1319 * Convert a fieldID to a java.lang.reflect.Field.1320 *1321 * (The "isStatic" field does not appear in the spec.)1322 *1323 * Throws OutOfMemory and returns NULL on failure.1324 */1325 static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID, jboolean isStatic) {1326 ScopedJniThreadState ts(env);1327 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jcls);1328 Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID);1329 dvmReleaseTrackedAlloc(obj, NULL);1330 return addLocalReference(ts.self(), obj);1331 }1332 1333 /*1334 * Take this exception and throw it.1335 */1336 static jint Throw(JNIEnv* env, jthrowable jobj) {1337 ScopedJniThreadState ts(env);1338 if (jobj != NULL) {1339 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);1340 dvmSetException(ts.self(), obj);1341 return JNI_OK;1342 }1343 return JNI_ERR;1344 }1345 1346 /*1347 * Constructs an exception object from the specified class with the message1348 * specified by "message", and throws it.1349 */1350 static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message) {1351 ScopedJniThreadState ts(env);1352 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1353 dvmThrowException(clazz, message);1354 // TODO: should return failure if this didn‘t work (e.g. OOM)1355 return JNI_OK;1356 }1357 1358 /*1359 * If an exception is being thrown, return the exception object. Otherwise,1360 * return NULL.1361 *1362 * TODO: if there is no pending exception, we should be able to skip the1363 * enter/exit checks. If we find one, we need to enter and then re-fetch1364 * the exception (in case it got moved by a compacting GC).1365 */1366 static jthrowable ExceptionOccurred(JNIEnv* env) {1367 ScopedJniThreadState ts(env);1368 Object* exception = dvmGetException(ts.self());1369 jthrowable localException = (jthrowable) addLocalReference(ts.self(), exception);1370 if (localException == NULL && exception != NULL) {1371 /*1372 * We were unable to add a new local reference, and threw a new1373 * exception. We can‘t return "exception", because it‘s not a1374 * local reference. So we have to return NULL, indicating that1375 * there was no exception, even though it‘s pretty much raining1376 * exceptions in here.1377 */1378 ALOGW("JNI WARNING: addLocal/exception combo");1379 }1380 return localException;1381 }1382 1383 /*1384 * Print an exception and stack trace to stderr.1385 */1386 static void ExceptionDescribe(JNIEnv* env) {1387 ScopedJniThreadState ts(env);1388 Object* exception = dvmGetException(ts.self());1389 if (exception != NULL) {1390 dvmPrintExceptionStackTrace();1391 } else {1392 ALOGI("Odd: ExceptionDescribe called, but no exception pending");1393 }1394 }1395 1396 /*1397 * Clear the exception currently being thrown.1398 *1399 * TODO: we should be able to skip the enter/exit stuff.1400 */1401 static void ExceptionClear(JNIEnv* env) {1402 ScopedJniThreadState ts(env);1403 dvmClearException(ts.self());1404 }1405 1406 /*1407 * Kill the VM. This function does not return.1408 */1409 static void FatalError(JNIEnv* env, const char* msg) {1410 //dvmChangeStatus(NULL, THREAD_RUNNING);1411 ALOGE("JNI posting fatal error: %s", msg);1412 ReportJniError();1413 }1414 1415 /*1416 * Push a new JNI frame on the stack, with a new set of locals.1417 *1418 * The new frame must have the same method pointer. (If for no other1419 * reason than FindClass needs it to get the appropriate class loader.)1420 */1421 static jint PushLocalFrame(JNIEnv* env, jint capacity) {1422 ScopedJniThreadState ts(env);1423 if (!ensureLocalCapacity(ts.self(), capacity) ||1424 !dvmPushLocalFrame(ts.self(), dvmGetCurrentJNIMethod()))1425 {1426 /* yes, OutOfMemoryError, not StackOverflowError */1427 dvmClearException(ts.self());1428 dvmThrowOutOfMemoryError("out of stack in JNI PushLocalFrame");1429 return JNI_ERR;1430 }1431 return JNI_OK;1432 }1433 1434 /*1435 * Pop the local frame off. If "jresult" is not null, add it as a1436 * local reference on the now-current frame.1437 */1438 static jobject PopLocalFrame(JNIEnv* env, jobject jresult) {1439 ScopedJniThreadState ts(env);1440 Object* result = dvmDecodeIndirectRef(ts.self(), jresult);1441 if (!dvmPopLocalFrame(ts.self())) {1442 ALOGW("JNI WARNING: too many PopLocalFrame calls");1443 dvmClearException(ts.self());1444 dvmThrowRuntimeException("too many PopLocalFrame calls");1445 }1446 return addLocalReference(ts.self(), result);1447 }1448 1449 /*1450 * Add a reference to the global list.1451 */1452 static jobject NewGlobalRef(JNIEnv* env, jobject jobj) {1453 ScopedJniThreadState ts(env);1454 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);1455 return addGlobalReference(obj);1456 }1457 1458 /*1459 * Delete a reference from the global list.1460 */1461 static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef) {1462 ScopedJniThreadState ts(env);1463 deleteGlobalReference(jglobalRef);1464 }1465 1466 1467 /*1468 * Add a reference to the local list.1469 */1470 static jobject NewLocalRef(JNIEnv* env, jobject jobj) {1471 ScopedJniThreadState ts(env);1472 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);1473 return addLocalReference(ts.self(), obj);1474 }1475 1476 /*1477 * Delete a reference from the local list.1478 */1479 static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef) {1480 ScopedJniThreadState ts(env);1481 deleteLocalReference(ts.self(), jlocalRef);1482 }1483 1484 /*1485 * Ensure that the local references table can hold at least this many1486 * references.1487 */1488 static jint EnsureLocalCapacity(JNIEnv* env, jint capacity) {1489 ScopedJniThreadState ts(env);1490 bool okay = ensureLocalCapacity(ts.self(), capacity);1491 if (!okay) {1492 dvmThrowOutOfMemoryError("can‘t ensure local reference capacity");1493 }1494 return okay ? 0 : -1;1495 }1496 1497 1498 /*1499 * Determine whether two Object references refer to the same underlying object.1500 */1501 static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2) {1502 ScopedJniThreadState ts(env);1503 Object* obj1 = dvmDecodeIndirectRef(ts.self(), jref1);1504 Object* obj2 = dvmDecodeIndirectRef(ts.self(), jref2);1505 return (obj1 == obj2);1506 }1507 1508 /*1509 * Allocate a new object without invoking any constructors.1510 */1511 static jobject AllocObject(JNIEnv* env, jclass jclazz) {1512 ScopedJniThreadState ts(env);1513 1514 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1515 if (!canAllocClass(clazz) ||1516 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))1517 {1518 assert(dvmCheckException(ts.self()));1519 return NULL;1520 }1521 1522 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);1523 return addLocalReference(ts.self(), newObj);1524 }1525 1526 /*1527 * Allocate a new object and invoke the supplied constructor.1528 */1529 static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) {1530 ScopedJniThreadState ts(env);1531 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1532 1533 if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {1534 assert(dvmCheckException(ts.self()));1535 return NULL;1536 }1537 1538 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);1539 jobject result = addLocalReference(ts.self(), newObj);1540 if (newObj != NULL) {1541 JValue unused;1542 va_list args;1543 va_start(args, methodID);1544 dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args);1545 va_end(args);1546 }1547 return result;1548 }1549 1550 static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, va_list args) {1551 ScopedJniThreadState ts(env);1552 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1553 1554 if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {1555 assert(dvmCheckException(ts.self()));1556 return NULL;1557 }1558 1559 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);1560 jobject result = addLocalReference(ts.self(), newObj);1561 if (newObj != NULL) {1562 JValue unused;1563 dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args);1564 }1565 return result;1566 }1567 1568 static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, jvalue* args) {1569 ScopedJniThreadState ts(env);1570 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1571 1572 if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) {1573 assert(dvmCheckException(ts.self()));1574 return NULL;1575 }1576 1577 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);1578 jobject result = addLocalReference(ts.self(), newObj);1579 if (newObj != NULL) {1580 JValue unused;1581 dvmCallMethodA(ts.self(), (Method*) methodID, newObj, true, &unused, args);1582 }1583 return result;1584 }1585 1586 /*1587 * Returns the class of an object.1588 *1589 * JNI spec says: obj must not be NULL.1590 */1591 static jclass GetObjectClass(JNIEnv* env, jobject jobj) {1592 ScopedJniThreadState ts(env);1593 1594 assert(jobj != NULL);1595 1596 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);1597 return (jclass) addLocalReference(ts.self(), (Object*) obj->clazz);1598 }1599 1600 /*1601 * Determine whether "obj" is an instance of "clazz".1602 */1603 static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz) {1604 ScopedJniThreadState ts(env);1605 1606 assert(jclazz != NULL);1607 if (jobj == NULL) {1608 return true;1609 }1610 1611 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);1612 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1613 return dvmInstanceof(obj->clazz, clazz);1614 }1615 1616 /*1617 * Get a method ID for an instance method.1618 *1619 * While Dalvik bytecode has distinct instructions for virtual, super,1620 * static, direct, and interface method invocation, JNI only provides1621 * two functions for acquiring a method ID. This call handles everything1622 * but static methods.1623 *1624 * JNI defines <init> as an instance method, but Dalvik considers it a1625 * "direct" method, so we have to special-case it here.1626 *1627 * Dalvik also puts all private methods into the "direct" list, so we1628 * really need to just search both lists.1629 */1630 static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {1631 ScopedJniThreadState ts(env);1632 1633 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1634 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {1635 assert(dvmCheckException(ts.self()));1636 } else if (dvmIsInterfaceClass(clazz)) {1637 Method* meth = dvmFindInterfaceMethodHierByDescriptor(clazz, name, sig);1638 if (meth == NULL) {1639 dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,1640 "no method with name=‘%s‘ signature=‘%s‘ in interface %s",1641 name, sig, clazz->descriptor);1642 }1643 return (jmethodID) meth;1644 }1645 Method* meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);1646 if (meth == NULL) {1647 /* search private methods and constructors; non-hierarchical */1648 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);1649 }1650 if (meth != NULL && dvmIsStaticMethod(meth)) {1651 IF_ALOGD() {1652 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);1653 ALOGD("GetMethodID: not returning static method %s.%s %s",1654 clazz->descriptor, meth->name, desc);1655 free(desc);1656 }1657 meth = NULL;1658 }1659 if (meth == NULL) {1660 dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,1661 "no method with name=‘%s‘ signature=‘%s‘ in class %s",1662 name, sig, clazz->descriptor);1663 } else {1664 /*1665 * The method‘s class may not be the same as clazz, but if1666 * it isn‘t this must be a virtual method and the class must1667 * be a superclass (and, hence, already initialized).1668 */1669 assert(dvmIsClassInitialized(meth->clazz) || dvmIsClassInitializing(meth->clazz));1670 }1671 return (jmethodID) meth;1672 }1673 1674 /*1675 * Get a field ID (instance fields).1676 */1677 static jfieldID GetFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {1678 ScopedJniThreadState ts(env);1679 1680 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1681 1682 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {1683 assert(dvmCheckException(ts.self()));1684 return NULL;1685 }1686 1687 jfieldID id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);1688 if (id == NULL) {1689 dvmThrowExceptionFmt(gDvm.exNoSuchFieldError,1690 "no field with name=‘%s‘ signature=‘%s‘ in class %s",1691 name, sig, clazz->descriptor);1692 }1693 return id;1694 }1695 1696 /*1697 * Get the method ID for a static method in a class.1698 */1699 static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {1700 ScopedJniThreadState ts(env);1701 1702 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1703 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {1704 assert(dvmCheckException(ts.self()));1705 return NULL;1706 }1707 1708 Method* meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);1709 1710 /* make sure it‘s static, not virtual+private */1711 if (meth != NULL && !dvmIsStaticMethod(meth)) {1712 IF_ALOGD() {1713 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);1714 ALOGD("GetStaticMethodID: not returning nonstatic method %s.%s %s",1715 clazz->descriptor, meth->name, desc);1716 free(desc);1717 }1718 meth = NULL;1719 }1720 1721 jmethodID id = (jmethodID) meth;1722 if (id == NULL) {1723 dvmThrowExceptionFmt(gDvm.exNoSuchMethodError,1724 "no static method with name=‘%s‘ signature=‘%s‘ in class %s",1725 name, sig, clazz->descriptor);1726 }1727 return id;1728 }1729 1730 /*1731 * Get a field ID (static fields).1732 */1733 static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) {1734 ScopedJniThreadState ts(env);1735 1736 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);1737 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {1738 assert(dvmCheckException(ts.self()));1739 return NULL;1740 }1741 1742 jfieldID id = (jfieldID) dvmFindStaticFieldHier(clazz, name, sig);1743 if (id == NULL) {1744 dvmThrowExceptionFmt(gDvm.exNoSuchFieldError,1745 "no static field with name=‘%s‘ signature=‘%s‘ in class %s",1746 name, sig, clazz->descriptor);1747 }1748 return id;1749 }1750 1751 /*1752 * Get a static field.1753 *1754 * If we get an object reference, add it to the local refs list.1755 */1756 #define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) 1757 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, 1758 jfieldID fieldID) 1759 { 1760 UNUSED_PARAMETER(jclazz); 1761 ScopedJniThreadState ts(env); 1762 StaticField* sfield = (StaticField*) fieldID; 1763 _ctype value; 1764 if (dvmIsVolatileField(sfield)) { 1765 if (_isref) { /* only when _ctype==jobject */ 1766 Object* obj = dvmGetStaticFieldObjectVolatile(sfield); 1767 value =http://www.mamicode.com/ (_ctype)(u4)addLocalReference(ts.self(), obj); 1768 } else { 1769 value =http://www.mamicode.com/ (_ctype) dvmGetStaticField##_jname##Volatile(sfield);1770 } 1771 } else { 1772 if (_isref) { 1773 Object* obj = dvmGetStaticFieldObject(sfield); 1774 value =http://www.mamicode.com/ (_ctype)(u4)addLocalReference(ts.self(), obj); 1775 } else { 1776 value =http://www.mamicode.com/ (_ctype) dvmGetStaticField##_jname(sfield); 1777 } 1778 } 1779 return value; 1780 }1781 GET_STATIC_TYPE_FIELD(jobject, Object, true);1782 GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);1783 GET_STATIC_TYPE_FIELD(jbyte, Byte, false);1784 GET_STATIC_TYPE_FIELD(jchar, Char, false);1785 GET_STATIC_TYPE_FIELD(jshort, Short, false);1786 GET_STATIC_TYPE_FIELD(jint, Int, false);1787 GET_STATIC_TYPE_FIELD(jlong, Long, false);1788 GET_STATIC_TYPE_FIELD(jfloat, Float, false);1789 GET_STATIC_TYPE_FIELD(jdouble, Double, false);1790 1791 /*1792 * Set a static field.1793 */1794 #define SET_STATIC_TYPE_FIELD(_ctype, _ctype2, _jname, _isref) 1795 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, 1796 jfieldID fieldID, _ctype value) 1797 { 1798 UNUSED_PARAMETER(jclazz); 1799 ScopedJniThreadState ts(env); 1800 StaticField* sfield = (StaticField*) fieldID; 1801 if (dvmIsVolatileField(sfield)) { 1802 if (_isref) { /* only when _ctype==jobject */ 1803 Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); 1804 dvmSetStaticFieldObjectVolatile(sfield, valObj); 1805 } else { 1806 dvmSetStaticField##_jname##Volatile(sfield, (_ctype2)value);1807 } 1808 } else { 1809 if (_isref) { 1810 Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); 1811 dvmSetStaticFieldObject(sfield, valObj); 1812 } else { 1813 dvmSetStaticField##_jname(sfield, (_ctype2)value); 1814 } 1815 } 1816 }1817 SET_STATIC_TYPE_FIELD(jobject, Object*, Object, true);1818 SET_STATIC_TYPE_FIELD(jboolean, bool, Boolean, false);1819 SET_STATIC_TYPE_FIELD(jbyte, s1, Byte, false);1820 SET_STATIC_TYPE_FIELD(jchar, u2, Char, false);1821 SET_STATIC_TYPE_FIELD(jshort, s2, Short, false);1822 SET_STATIC_TYPE_FIELD(jint, s4, Int, false);1823 SET_STATIC_TYPE_FIELD(jlong, s8, Long, false);1824 SET_STATIC_TYPE_FIELD(jfloat, float, Float, false);1825 SET_STATIC_TYPE_FIELD(jdouble, double, Double, false);1826 1827 /*1828 * Get an instance field.1829 *1830 * If we get an object reference, add it to the local refs list.1831 */1832 #define GET_TYPE_FIELD(_ctype, _jname, _isref) 1833 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, 1834 jfieldID fieldID) 1835 { 1836 ScopedJniThreadState ts(env); 1837 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); 1838 InstField* field = (InstField*) fieldID; 1839 _ctype value; 1840 if (dvmIsVolatileField(field)) { 1841 if (_isref) { /* only when _ctype==jobject */ 1842 Object* valObj = 1843 dvmGetFieldObjectVolatile(obj, field->byteOffset); 1844 value =http://www.mamicode.com/ (_ctype)(u4)addLocalReference(ts.self(), valObj); 1845 } else { 1846 value =http://www.mamicode.com/ (_ctype) 1847 dvmGetField##_jname##Volatile(obj, field->byteOffset); 1848 } 1849 } else { 1850 if (_isref) { 1851 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); 1852 value =http://www.mamicode.com/ (_ctype)(u4)addLocalReference(ts.self(), valObj); 1853 } else { 1854 value = http://www.mamicode.com/(_ctype) dvmGetField##_jname(obj, field->byteOffset);1855 } 1856 } 1857 return value; 1858 }1859 GET_TYPE_FIELD(jobject, Object, true);1860 GET_TYPE_FIELD(jboolean, Boolean, false);1861 GET_TYPE_FIELD(jbyte, Byte, false);1862 GET_TYPE_FIELD(jchar, Char, false);1863 GET_TYPE_FIELD(jshort, Short, false);1864 GET_TYPE_FIELD(jint, Int, false);1865 GET_TYPE_FIELD(jlong, Long, false);1866 GET_TYPE_FIELD(jfloat, Float, false);1867 GET_TYPE_FIELD(jdouble, Double, false);1868 1869 /*1870 * Set an instance field.1871 */1872 #define SET_TYPE_FIELD(_ctype, _ctype2, _jname, _isref) 1873 static void Set##_jname##Field(JNIEnv* env, jobject jobj, 1874 jfieldID fieldID, _ctype value) 1875 { 1876 ScopedJniThreadState ts(env); 1877 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); 1878 InstField* field = (InstField*) fieldID; 1879 if (dvmIsVolatileField(field)) { 1880 if (_isref) { /* only when _ctype==jobject */ 1881 Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); 1882 dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj); 1883 } else { 1884 dvmSetField##_jname##Volatile(obj, 1885 field->byteOffset, (_ctype2)value); 1886 } 1887 } else { 1888 if (_isref) { 1889 Object* valObj = dvmDecodeIndirectRef(ts.self(), (jobject)(u4)value); 1890 dvmSetFieldObject(obj, field->byteOffset, valObj); 1891 } else { 1892 dvmSetField##_jname(obj, 1893 field->byteOffset, (_ctype2)value); 1894 } 1895 } 1896 }1897 SET_TYPE_FIELD(jobject, Object*, Object, true);1898 SET_TYPE_FIELD(jboolean, bool, Boolean, false);1899 SET_TYPE_FIELD(jbyte, s1, Byte, false);1900 SET_TYPE_FIELD(jchar, u2, Char, false);1901 SET_TYPE_FIELD(jshort, s2, Short, false);1902 SET_TYPE_FIELD(jint, s4, Int, false);1903 SET_TYPE_FIELD(jlong, s8, Long, false);1904 SET_TYPE_FIELD(jfloat, float, Float, false);1905 SET_TYPE_FIELD(jdouble, double, Double, false);1906 1907 /*1908 * Make a virtual method call.1909 *1910 * Three versions (..., va_list, jvalue[]) for each return type. If we‘re1911 * returning an Object, we have to add it to the local references table.1912 */1913 #define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) 1914 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, 1915 jmethodID methodID, ...) 1916 { 1917 ScopedJniThreadState ts(env); 1918 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); 1919 const Method* meth; 1920 va_list args; 1921 JValue result; 1922 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); 1923 if (meth == NULL) { 1924 return _retfail; 1925 } 1926 va_start(args, methodID); 1927 dvmCallMethodV(ts.self(), meth, obj, true, &result, args); 1928 va_end(args); 1929 if (_isref && !dvmCheckException(ts.self())) 1930 result.l = (Object*)addLocalReference(ts.self(), result.l); 1931 return _retok; 1932 } 1933 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, 1934 jmethodID methodID, va_list args) 1935 { 1936 ScopedJniThreadState ts(env); 1937 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); 1938 const Method* meth; 1939 JValue result; 1940 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); 1941 if (meth == NULL) { 1942 return _retfail; 1943 } 1944 dvmCallMethodV(ts.self(), meth, obj, true, &result, args); 1945 if (_isref && !dvmCheckException(ts.self())) 1946 result.l = (Object*)addLocalReference(ts.self(), result.l); 1947 return _retok; 1948 } 1949 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, 1950 jmethodID methodID, jvalue* args) 1951 { 1952 ScopedJniThreadState ts(env); 1953 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); 1954 const Method* meth; 1955 JValue result; 1956 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); 1957 if (meth == NULL) { 1958 return _retfail; 1959 } 1960 dvmCallMethodA(ts.self(), meth, obj, true, &result, args); 1961 if (_isref && !dvmCheckException(ts.self())) 1962 result.l = (Object*)addLocalReference(ts.self(), result.l); 1963 return _retok; 1964 }1965 CALL_VIRTUAL(jobject, Object, NULL, (jobject) result.l, true);1966 CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);1967 CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);1968 CALL_VIRTUAL(jchar, Char, 0, result.c, false);1969 CALL_VIRTUAL(jshort, Short, 0, result.s, false);1970 CALL_VIRTUAL(jint, Int, 0, result.i, false);1971 CALL_VIRTUAL(jlong, Long, 0, result.j, false);1972 CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);1973 CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);1974 CALL_VIRTUAL(void, Void, , , false);1975 1976 /*1977 * Make a "non-virtual" method call. We‘re still calling a virtual method,1978 * but this time we‘re not doing an indirection through the object‘s vtable.1979 * The "clazz" parameter defines which implementation of a method we want.1980 *1981 * Three versions (..., va_list, jvalue[]) for each return type.1982 */1983 #define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) 1984 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, 1985 jclass jclazz, jmethodID methodID, ...) 1986 { 1987 ScopedJniThreadState ts(env); 1988 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); 1989 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz); 1990 const Method* meth; 1991 va_list args; 1992 JValue result; 1993 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); 1994 if (meth == NULL) { 1995 return _retfail; 1996 } 1997 va_start(args, methodID); 1998 dvmCallMethodV(ts.self(), meth, obj, true, &result, args); 1999 if (_isref && !dvmCheckException(ts.self())) 2000 result.l = (Object*)addLocalReference(ts.self(), result.l); 2001 va_end(args); 2002 return _retok; 2003 } 2004 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,2005 jclass jclazz, jmethodID methodID, va_list args) 2006 { 2007 ScopedJniThreadState ts(env); 2008 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); 2009 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz); 2010 const Method* meth; 2011 JValue result; 2012 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); 2013 if (meth == NULL) { 2014 return _retfail; 2015 } 2016 dvmCallMethodV(ts.self(), meth, obj, true, &result, args); 2017 if (_isref && !dvmCheckException(ts.self())) 2018 result.l = (Object*)addLocalReference(ts.self(), result.l); 2019 return _retok; 2020 } 2021 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,2022 jclass jclazz, jmethodID methodID, jvalue* args) 2023 { 2024 ScopedJniThreadState ts(env); 2025 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj); 2026 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz); 2027 const Method* meth; 2028 JValue result; 2029 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); 2030 if (meth == NULL) { 2031 return _retfail; 2032 } 2033 dvmCallMethodA(ts.self(), meth, obj, true, &result, args); 2034 if (_isref && !dvmCheckException(ts.self())) 2035 result.l = (Object*)addLocalReference(ts.self(), result.l); 2036 return _retok; 2037 }2038 CALL_NONVIRTUAL(jobject, Object, NULL, (jobject) result.l, true);2039 CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);2040 CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);2041 CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);2042 CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);2043 CALL_NONVIRTUAL(jint, Int, 0, result.i, false);2044 CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);2045 CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);2046 CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);2047 CALL_NONVIRTUAL(void, Void, , , false);2048 2049 2050 /*2051 * Call a static method.2052 */2053 #define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) 2054 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, 2055 jmethodID methodID, ...) 2056 { 2057 UNUSED_PARAMETER(jclazz); 2058 ScopedJniThreadState ts(env); 2059 JValue result; 2060 va_list args; 2061 va_start(args, methodID); 2062 dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);2063 va_end(args); 2064 if (_isref && !dvmCheckException(ts.self())) 2065 result.l = (Object*)addLocalReference(ts.self(), result.l); 2066 return _retok; 2067 } 2068 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, 2069 jmethodID methodID, va_list args) 2070 { 2071 UNUSED_PARAMETER(jclazz); 2072 ScopedJniThreadState ts(env); 2073 JValue result; 2074 dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);2075 if (_isref && !dvmCheckException(ts.self())) 2076 result.l = (Object*)addLocalReference(ts.self(), result.l); 2077 return _retok; 2078 } 2079 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, 2080 jmethodID methodID, jvalue* args) 2081 { 2082 UNUSED_PARAMETER(jclazz); 2083 ScopedJniThreadState ts(env); 2084 JValue result; 2085 dvmCallMethodA(ts.self(), (Method*)methodID, NULL, true, &result, args);2086 if (_isref && !dvmCheckException(ts.self())) 2087 result.l = (Object*)addLocalReference(ts.self(), result.l); 2088 return _retok; 2089 }2090 CALL_STATIC(jobject, Object, NULL, (jobject) result.l, true);2091 CALL_STATIC(jboolean, Boolean, 0, result.z, false);2092 CALL_STATIC(jbyte, Byte, 0, result.b, false);2093 CALL_STATIC(jchar, Char, 0, result.c, false);2094 CALL_STATIC(jshort, Short, 0, result.s, false);2095 CALL_STATIC(jint, Int, 0, result.i, false);2096 CALL_STATIC(jlong, Long, 0, result.j, false);2097 CALL_STATIC(jfloat, Float, 0.0f, result.f, false);2098 CALL_STATIC(jdouble, Double, 0.0, result.d, false);2099 CALL_STATIC(void, Void, , , false);2100 2101 /*2102 * Create a new String from Unicode data.2103 *2104 * If "len" is zero, we will return an empty string even if "unicodeChars"2105 * is NULL. (The JNI spec is vague here.)2106 */2107 static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) {2108 ScopedJniThreadState ts(env);2109 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);2110 if (jstr == NULL) {2111 return NULL;2112 }2113 dvmReleaseTrackedAlloc((Object*) jstr, NULL);2114 return (jstring) addLocalReference(ts.self(), (Object*) jstr);2115 }2116 2117 /*2118 * Return the length of a String in Unicode character units.2119 */2120 static jsize GetStringLength(JNIEnv* env, jstring jstr) {2121 ScopedJniThreadState ts(env);2122 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);2123 return strObj->length();2124 }2125 2126 2127 /*2128 * Get a string‘s character data.2129 *2130 * The result is guaranteed to be valid until ReleaseStringChars is2131 * called, which means we have to pin it or return a copy.2132 */2133 static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy) {2134 ScopedJniThreadState ts(env);2135 2136 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);2137 ArrayObject* strChars = strObj->array();2138 2139 pinPrimitiveArray(strChars);2140 2141 const u2* data = http://www.mamicode.com/strObj->chars();2142 if (isCopy != NULL) {2143 *isCopy = JNI_FALSE;2144 }2145 return (jchar*) data;2146 }2147 2148 /*2149 * Release our grip on some characters from a string.2150 */2151 static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars) {2152 ScopedJniThreadState ts(env);2153 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);2154 ArrayObject* strChars = strObj->array();2155 unpinPrimitiveArray(strChars);2156 }2157 2158 /*2159 * Create a new java.lang.String object from chars in modified UTF-8 form.2160 *2161 * The spec doesn‘t say how to handle a NULL string. Popular desktop VMs2162 * accept it and return a NULL pointer in response.2163 */2164 static jstring NewStringUTF(JNIEnv* env, const char* bytes) {2165 ScopedJniThreadState ts(env);2166 if (bytes == NULL) {2167 return NULL;2168 }2169 /* note newStr could come back NULL on OOM */2170 StringObject* newStr = dvmCreateStringFromCstr(bytes);2171 jstring result = (jstring) addLocalReference(ts.self(), (Object*) newStr);2172 dvmReleaseTrackedAlloc((Object*)newStr, NULL);2173 return result;2174 }2175 2176 /*2177 * Return the length in bytes of the modified UTF-8 form of the string.2178 */2179 static jsize GetStringUTFLength(JNIEnv* env, jstring jstr) {2180 ScopedJniThreadState ts(env);2181 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);2182 if (strObj == NULL) {2183 return 0; // Should we throw something or assert?2184 }2185 return strObj->utfLength();2186 }2187 2188 /*2189 * Convert "string" to modified UTF-8 and return a pointer. The returned2190 * value must be released with ReleaseStringUTFChars.2191 *2192 * According to the JNI reference, "Returns a pointer to a UTF-8 string,2193 * or NULL if the operation fails. Returns NULL if and only if an invocation2194 * of this function has thrown an exception."2195 *2196 * The behavior here currently follows that of other open-source VMs, which2197 * quietly return NULL if "string" is NULL. We should consider throwing an2198 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,2199 * which should catch this sort of thing during development.) Certain other2200 * VMs will crash with a segmentation fault.2201 */2202 static const char* GetStringUTFChars(JNIEnv* env, jstring jstr, jboolean* isCopy) {2203 ScopedJniThreadState ts(env);2204 if (jstr == NULL) {2205 /* this shouldn‘t happen; throw NPE? */2206 return NULL;2207 }2208 if (isCopy != NULL) {2209 *isCopy = JNI_TRUE;2210 }2211 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);2212 char* newStr = dvmCreateCstrFromString(strObj);2213 if (newStr == NULL) {2214 /* assume memory failure */2215 dvmThrowOutOfMemoryError("native heap string alloc failed");2216 }2217 return newStr;2218 }2219 2220 /*2221 * Release a string created by GetStringUTFChars().2222 */2223 static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf) {2224 ScopedJniThreadState ts(env);2225 free((char*) utf);2226 }2227 2228 /*2229 * Return the capacity of the array.2230 */2231 static jsize GetArrayLength(JNIEnv* env, jarray jarr) {2232 ScopedJniThreadState ts(env);2233 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);2234 return arrObj->length;2235 }2236 2237 /*2238 * Construct a new array that holds objects from class "elementClass".2239 */2240 static jobjectArray NewObjectArray(JNIEnv* env, jsize length,2241 jclass jelementClass, jobject jinitialElement)2242 {2243 ScopedJniThreadState ts(env);2244 2245 if (jelementClass == NULL) {2246 dvmThrowNullPointerException("JNI NewObjectArray elementClass == NULL");2247 return NULL;2248 }2249 2250 ClassObject* elemClassObj = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jelementClass);2251 ClassObject* arrayClass = dvmFindArrayClassForElement(elemClassObj);2252 ArrayObject* newObj = dvmAllocArrayByClass(arrayClass, length, ALLOC_DEFAULT);2253 if (newObj == NULL) {2254 assert(dvmCheckException(ts.self()));2255 return NULL;2256 }2257 jobjectArray newArray = (jobjectArray) addLocalReference(ts.self(), (Object*) newObj);2258 dvmReleaseTrackedAlloc((Object*) newObj, NULL);2259 2260 /*2261 * Initialize the array.2262 */2263 if (jinitialElement != NULL) {2264 Object* initialElement = dvmDecodeIndirectRef(ts.self(), jinitialElement);2265 Object** arrayData = http://www.mamicode.com/(Object**) (void*) newObj->contents;2266 for (jsize i = 0; i < length; ++i) {2267 arrayData[i] = initialElement;2268 }2269 }2270 2271 return newArray;2272 }2273 2274 static bool checkArrayElementBounds(ArrayObject* arrayObj, jsize index) {2275 assert(arrayObj != NULL);2276 if (index < 0 || index >= (int) arrayObj->length) {2277 dvmThrowArrayIndexOutOfBoundsException(arrayObj->length, index);2278 return false;2279 }2280 return true;2281 }2282 2283 /*2284 * Get one element of an Object array.2285 *2286 * Add the object to the local references table in case the array goes away.2287 */2288 static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index) {2289 ScopedJniThreadState ts(env);2290 2291 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);2292 if (!checkArrayElementBounds(arrayObj, index)) {2293 return NULL;2294 }2295 2296 Object* value = http://www.mamicode.com/((Object**) (void*) arrayObj->contents)[index];2297 return addLocalReference(ts.self(), value);2298 }2299 2300 /*2301 * Set one element of an Object array.2302 */2303 static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index, jobject jobj) {2304 ScopedJniThreadState ts(env);2305 2306 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);2307 if (!checkArrayElementBounds(arrayObj, index)) {2308 return;2309 }2310 2311 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);2312 2313 if (obj != NULL && !dvmCanPutArrayElement(obj->clazz, arrayObj->clazz)) {2314 ALOGV("Can‘t put a ‘%s‘(%p) into array type=‘%s‘(%p)",2315 obj->clazz->descriptor, obj,2316 arrayObj->clazz->descriptor, arrayObj);2317 dvmThrowArrayStoreExceptionIncompatibleElement(obj->clazz, arrayObj->clazz);2318 return;2319 }2320 2321 //ALOGV("JNI: set element %d in array %p to %p", index, array, value);2322 2323 dvmSetObjectArrayElement(arrayObj, index, obj);2324 }2325 2326 /*2327 * Create a new array of primitive elements.2328 */2329 #define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) 2330 static _artype New##_jname##Array(JNIEnv* env, jsize length) { 2331 ScopedJniThreadState ts(env); 2332 ArrayObject* arrayObj = dvmAllocPrimitiveArray(_typechar, length, ALLOC_DEFAULT); 2333 if (arrayObj == NULL) { 2334 return NULL; 2335 } 2336 _artype result = (_artype) addLocalReference(ts.self(), (Object*) arrayObj); 2337 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); 2338 return result; 2339 }2340 NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, ‘Z‘);2341 NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, ‘B‘);2342 NEW_PRIMITIVE_ARRAY(jcharArray, Char, ‘C‘);2343 NEW_PRIMITIVE_ARRAY(jshortArray, Short, ‘S‘);2344 NEW_PRIMITIVE_ARRAY(jintArray, Int, ‘I‘);2345 NEW_PRIMITIVE_ARRAY(jlongArray, Long, ‘J‘);2346 NEW_PRIMITIVE_ARRAY(jfloatArray, Float, ‘F‘);2347 NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, ‘D‘);2348 2349 /*2350 * Get a pointer to a C array of primitive elements from an array object2351 * of the matching type.2352 *2353 * In a compacting GC, we either need to return a copy of the elements or2354 * "pin" the memory. Otherwise we run the risk of native code using the2355 * buffer as the destination of e.g. a blocking read() call that wakes up2356 * during a GC.2357 */2358 #define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) 2359 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, 2360 _ctype##Array jarr, jboolean* isCopy) 2361 { 2362 ScopedJniThreadState ts(env); 2363 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); 2364 pinPrimitiveArray(arrayObj); 2365 _ctype* data = http://www.mamicode.com/(_ctype*) (void*) arrayObj->contents; 2366 if (isCopy != NULL) { 2367 *isCopy = JNI_FALSE; 2368 } 2369 return data; 2370 }2371 2372 /*2373 * Release the storage locked down by the "get" function.2374 *2375 * The spec says, "‘mode‘ has no effect if ‘elems‘ is not a copy of the2376 * elements in ‘array‘." They apparently did not anticipate the need to2377 * un-pin memory.2378 */2379 #define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) 2380 static void Release##_jname##ArrayElements(JNIEnv* env, 2381 _ctype##Array jarr, _ctype* elems, jint mode) 2382 { 2383 UNUSED_PARAMETER(elems); 2384 if (mode != JNI_COMMIT) { 2385 ScopedJniThreadState ts(env); 2386 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); 2387 unpinPrimitiveArray(arrayObj); 2388 } 2389 }2390 2391 static void throwArrayRegionOutOfBounds(ArrayObject* arrayObj, jsize start,2392 jsize len, const char* arrayIdentifier)2393 {2394 dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,2395 "%s offset=%d length=%d %s.length=%d",2396 arrayObj->clazz->descriptor, start, len, arrayIdentifier,2397 arrayObj->length);2398 }2399 2400 /*2401 * Copy a section of a primitive array to a buffer.2402 */2403 #define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) 2404 static void Get##_jname##ArrayRegion(JNIEnv* env, 2405 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) 2406 { 2407 ScopedJniThreadState ts(env); 2408 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); 2409 _ctype* data = http://www.mamicode.com/(_ctype*) (void*) arrayObj->contents; 2410 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { 2411 throwArrayRegionOutOfBounds(arrayObj, start, len, "src"); 2412 } else { 2413 memcpy(buf, data + start, len * sizeof(_ctype)); 2414 } 2415 }2416 2417 /*2418 * Copy a section of a primitive array from a buffer.2419 */2420 #define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) 2421 static void Set##_jname##ArrayRegion(JNIEnv* env, 2422 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) 2423 { 2424 ScopedJniThreadState ts(env); 2425 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr); 2426 _ctype* data = http://www.mamicode.com/(_ctype*) (void*) arrayObj->contents; 2427 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { 2428 throwArrayRegionOutOfBounds(arrayObj, start, len, "dst"); 2429 } else { 2430 memcpy(data + start, buf, len * sizeof(_ctype)); 2431 } 2432 }2433 2434 /*2435 * 4-in-1:2436 * Get<Type>ArrayElements2437 * Release<Type>ArrayElements2438 * Get<Type>ArrayRegion2439 * Set<Type>ArrayRegion2440 */2441 #define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) 2442 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); 2443 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); 2444 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); 2445 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);2446 2447 PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);2448 PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);2449 PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);2450 PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);2451 PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);2452 PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);2453 PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);2454 PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);2455 2456 /*2457 * Register one or more native functions in one class.2458 *2459 * This can be called multiple times on the same method, allowing the2460 * caller to redefine the method implementation at will.2461 */2462 static jint RegisterNatives(JNIEnv* env, jclass jclazz,2463 const JNINativeMethod* methods, jint nMethods)2464 {2465 ScopedJniThreadState ts(env);2466 2467 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);2468 2469 if (gDvm.verboseJni) {2470 ALOGI("[Registering JNI native methods for class %s]",2471 clazz->descriptor);2472 }2473 2474 for (int i = 0; i < nMethods; i++) {2475 if (!dvmRegisterJNIMethod(clazz, methods[i].name,2476 methods[i].signature, methods[i].fnPtr))2477 {2478 return JNI_ERR;2479 }2480 }2481 return JNI_OK;2482 }2483 2484 /*2485 * Un-register all native methods associated with the class.2486 *2487 * The JNI docs refer to this as a way to reload/relink native libraries,2488 * and say it "should not be used in normal native code". In particular,2489 * there is no need to do this during shutdown, and you do not need to do2490 * this before redefining a method implementation with RegisterNatives.2491 *2492 * It‘s chiefly useful for a native "plugin"-style library that wasn‘t2493 * loaded with System.loadLibrary() (since there‘s no way to unload those).2494 * For example, the library could upgrade itself by:2495 *2496 * 1. call UnregisterNatives to unbind the old methods2497 * 2. ensure that no code is still executing inside it (somehow)2498 * 3. dlclose() the library2499 * 4. dlopen() the new library2500 * 5. use RegisterNatives to bind the methods from the new library2501 *2502 * The above can work correctly without the UnregisterNatives call, but2503 * creates a window of opportunity in which somebody might try to call a2504 * method that is pointing at unmapped memory, crashing the VM. In theory2505 * the same guards that prevent dlclose() from unmapping executing code could2506 * prevent that anyway, but with this we can be more thorough and also deal2507 * with methods that only exist in the old or new form of the library (maybe2508 * the lib wants to try the call and catch the UnsatisfiedLinkError).2509 */2510 static jint UnregisterNatives(JNIEnv* env, jclass jclazz) {2511 ScopedJniThreadState ts(env);2512 2513 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);2514 if (gDvm.verboseJni) {2515 ALOGI("[Unregistering JNI native methods for class %s]",2516 clazz->descriptor);2517 }2518 dvmUnregisterJNINativeMethods(clazz);2519 return JNI_OK;2520 }2521 2522 /*2523 * Lock the monitor.2524 *2525 * We have to track all monitor enters and exits, so that we can undo any2526 * outstanding synchronization before the thread exits.2527 */2528 static jint MonitorEnter(JNIEnv* env, jobject jobj) {2529 ScopedJniThreadState ts(env);2530 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);2531 dvmLockObject(ts.self(), obj);2532 trackMonitorEnter(ts.self(), obj);2533 return JNI_OK;2534 }2535 2536 /*2537 * Unlock the monitor.2538 *2539 * Throws an IllegalMonitorStateException if the current thread2540 * doesn‘t own the monitor. (dvmUnlockObject() takes care of the throw.)2541 *2542 * According to the 1.6 spec, it‘s legal to call here with an exception2543 * pending. If this fails, we‘ll stomp the original exception.2544 */2545 static jint MonitorExit(JNIEnv* env, jobject jobj) {2546 ScopedJniThreadState ts(env);2547 Object* obj = dvmDecodeIndirectRef(ts.self(), jobj);2548 bool success = dvmUnlockObject(ts.self(), obj);2549 if (success) {2550 trackMonitorExit(ts.self(), obj);2551 }2552 return success ? JNI_OK : JNI_ERR;2553 }2554 2555 /*2556 * Return the JavaVM interface associated with the current thread.2557 */2558 static jint GetJavaVM(JNIEnv* env, JavaVM** vm) {2559 ScopedJniThreadState ts(env);2560 *vm = gDvmJni.jniVm;2561 return (*vm == NULL) ? JNI_ERR : JNI_OK;2562 }2563 2564 /*2565 * Copies "len" Unicode characters, from offset "start".2566 */2567 static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, jchar* buf) {2568 ScopedJniThreadState ts(env);2569 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);2570 int strLen = strObj->length();2571 if (((start|len) < 0) || (start + len > strLen)) {2572 dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len);2573 return;2574 }2575 memcpy(buf, strObj->chars() + start, len * sizeof(u2));2576 }2577 2578 /*2579 * Translates "len" Unicode characters, from offset "start", into2580 * modified UTF-8 encoding.2581 */2582 static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, char* buf) {2583 ScopedJniThreadState ts(env);2584 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);2585 int strLen = strObj->length();2586 if (((start|len) < 0) || (start + len > strLen)) {2587 dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len);2588 return;2589 }2590 dvmGetStringUtfRegion(strObj, start, len, buf);2591 }2592 2593 /*2594 * Get a raw pointer to array data.2595 *2596 * The caller is expected to call "release" before doing any JNI calls2597 * or blocking I/O operations.2598 *2599 * We need to pin the memory or block GC.2600 */2601 static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr, jboolean* isCopy) {2602 ScopedJniThreadState ts(env);2603 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);2604 pinPrimitiveArray(arrayObj);2605 void* data = http://www.mamicode.com/arrayObj->contents;2606 if (UNLIKELY(isCopy != NULL)) {2607 *isCopy = JNI_FALSE;2608 }2609 return data;2610 }2611 2612 /*2613 * Release an array obtained with GetPrimitiveArrayCritical.2614 */2615 static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr, void* carray, jint mode) {2616 if (mode != JNI_COMMIT) {2617 ScopedJniThreadState ts(env);2618 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(ts.self(), jarr);2619 unpinPrimitiveArray(arrayObj);2620 }2621 }2622 2623 /*2624 * Like GetStringChars, but with restricted use.2625 */2626 static const jchar* GetStringCritical(JNIEnv* env, jstring jstr, jboolean* isCopy) {2627 ScopedJniThreadState ts(env);2628 2629 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);2630 ArrayObject* strChars = strObj->array();2631 2632 pinPrimitiveArray(strChars);2633 2634 const u2* data = http://www.mamicode.com/strObj->chars();2635 if (isCopy != NULL) {2636 *isCopy = JNI_FALSE;2637 }2638 return (jchar*) data;2639 }2640 2641 /*2642 * Like ReleaseStringChars, but with restricted use.2643 */2644 static void ReleaseStringCritical(JNIEnv* env, jstring jstr, const jchar* carray) {2645 ScopedJniThreadState ts(env);2646 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(ts.self(), jstr);2647 ArrayObject* strChars = strObj->array();2648 unpinPrimitiveArray(strChars);2649 }2650 2651 /*2652 * Create a new weak global reference.2653 */2654 static jweak NewWeakGlobalRef(JNIEnv* env, jobject jobj) {2655 ScopedJniThreadState ts(env);2656 Object *obj = dvmDecodeIndirectRef(ts.self(), jobj);2657 return (jweak) addWeakGlobalReference(obj);2658 }2659 2660 /*2661 * Delete the specified weak global reference.2662 */2663 static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref) {2664 ScopedJniThreadState ts(env);2665 deleteWeakGlobalReference(wref);2666 }2667 2668 /*2669 * Quick check for pending exceptions.2670 *2671 * TODO: we should be able to skip the enter/exit macros here.2672 */2673 static jboolean ExceptionCheck(JNIEnv* env) {2674 ScopedJniThreadState ts(env);2675 return dvmCheckException(ts.self());2676 }2677 2678 /*2679 * Returns the type of the object referred to by "obj". It can be local,2680 * global, or weak global.2681 *2682 * In the current implementation, references can be global and local at2683 * the same time, so while the return value is accurate it may not tell2684 * the whole story.2685 */2686 static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj) {2687 ScopedJniThreadState ts(env);2688 return dvmGetJNIRefType(ts.self(), jobj);2689 }2690 2691 /*2692 * Allocate and return a new java.nio.ByteBuffer for this block of memory.2693 */2694 static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) {2695 ScopedJniThreadState ts(env);2696 2697 if (capacity < 0) {2698 ALOGE("JNI ERROR (app bug): negative buffer capacity: %lld", capacity);2699 ReportJniError();2700 }2701 if (address == NULL && capacity != 0) {2702 ALOGE("JNI ERROR (app bug): non-zero capacity for NULL pointer: %lld", capacity);2703 ReportJniError();2704 }2705 2706 /* create an instance of java.nio.DirectByteBuffer */2707 ClassObject* bufferClazz = gDvm.classJavaNioDirectByteBuffer;2708 if (!dvmIsClassInitialized(bufferClazz) && !dvmInitClass(bufferClazz)) {2709 return NULL;2710 }2711 Object* newObj = dvmAllocObject(bufferClazz, ALLOC_DONT_TRACK);2712 if (newObj == NULL) {2713 return NULL;2714 }2715 /* call the constructor */2716 jobject result = addLocalReference(ts.self(), newObj);2717 JValue unused;2718 dvmCallMethod(ts.self(), gDvm.methJavaNioDirectByteBuffer_init,2719 newObj, &unused, (jlong) address, (jint) capacity);2720 if (dvmGetException(ts.self()) != NULL) {2721 deleteLocalReference(ts.self(), result);2722 return NULL;2723 }2724 return result;2725 }2726 2727 /*2728 * Get the starting address of the buffer for the specified java.nio.Buffer.2729 *2730 * If this is not a "direct" buffer, we return NULL.2731 */2732 static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf) {2733 ScopedJniThreadState ts(env);2734 2735 // All Buffer objects have an effectiveDirectAddress field.2736 Object* bufObj = dvmDecodeIndirectRef(ts.self(), jbuf);2737 return (void*) dvmGetFieldLong(bufObj, gDvm.offJavaNioBuffer_effectiveDirectAddress);2738 }2739 2740 /*2741 * Get the capacity of the buffer for the specified java.nio.Buffer.2742 *2743 * Returns -1 if the object is not a direct buffer. (We actually skip2744 * this check, since it‘s expensive to determine, and just return the2745 * capacity regardless.)2746 */2747 static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf) {2748 ScopedJniThreadState ts(env);2749 2750 /*2751 * The capacity is always in the Buffer.capacity field.2752 *2753 * (The "check" version should verify that this is actually a Buffer,2754 * but we‘re not required to do so here.)2755 */2756 Object* buf = dvmDecodeIndirectRef(ts.self(), jbuf);2757 return dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);2758 }2759 2760 2761 /*2762 * ===========================================================================2763 * JNI invocation functions2764 * ===========================================================================2765 */2766 2767 /*2768 * Handle AttachCurrentThread{AsDaemon}.2769 *2770 * We need to make sure the VM is actually running. For example, if we start2771 * up, issue an Attach, and the VM exits almost immediately, by the time the2772 * attaching happens the VM could already be shutting down.2773 *2774 * It‘s hard to avoid a race condition here because we don‘t want to hold2775 * a lock across the entire operation. What we can do is temporarily2776 * increment the thread count to prevent a VM exit.2777 *2778 * This could potentially still have problems if a daemon thread calls here2779 * while the VM is shutting down. dvmThreadSelf() will work, since it just2780 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when2781 * you shut down a VM while threads are still running inside it.2782 *2783 * Remember that some code may call this as a way to find the per-thread2784 * JNIEnv pointer. Don‘t do excess work for that case.2785 */2786 static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args, bool isDaemon) {2787 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;2788 2789 /*2790 * Return immediately if we‘re already one with the VM.2791 */2792 Thread* self = dvmThreadSelf();2793 if (self != NULL) {2794 *p_env = self->jniEnv;2795 return JNI_OK;2796 }2797 2798 /*2799 * No threads allowed in zygote mode.2800 */2801 if (gDvm.zygote) {2802 return JNI_ERR;2803 }2804 2805 /* increment the count to keep the VM from bailing while we run */2806 dvmLockThreadList(NULL);2807 if (gDvm.nonDaemonThreadCount == 0) {2808 // dead or dying2809 ALOGV("Refusing to attach thread ‘%s‘ -- VM is shutting down",2810 (thr_args == NULL) ? "(unknown)" : args->name);2811 dvmUnlockThreadList();2812 return JNI_ERR;2813 }2814 gDvm.nonDaemonThreadCount++;2815 dvmUnlockThreadList();2816 2817 /* tweak the JavaVMAttachArgs as needed */2818 JavaVMAttachArgs argsCopy;2819 if (args == NULL) {2820 /* allow the v1.1 calling convention */2821 argsCopy.version = JNI_VERSION_1_2;2822 argsCopy.name = NULL;2823 argsCopy.group = (jobject) dvmGetMainThreadGroup();2824 } else {2825 if (dvmIsBadJniVersion(args->version)) {2826 ALOGE("Bad JNI version passed to %s: %d",2827 (isDaemon ? "AttachCurrentThreadAsDaemon" : "AttachCurrentThread"),2828 args->version);2829 return JNI_EVERSION;2830 }2831 2832 argsCopy.version = args->version;2833 argsCopy.name = args->name;2834 if (args->group != NULL) {2835 argsCopy.group = (jobject) dvmDecodeIndirectRef(NULL, args->group);2836 } else {2837 argsCopy.group = (jobject) dvmGetMainThreadGroup();2838 }2839 }2840 2841 bool result = dvmAttachCurrentThread(&argsCopy, isDaemon);2842 2843 /* restore the count */2844 dvmLockThreadList(NULL);2845 gDvm.nonDaemonThreadCount--;2846 dvmUnlockThreadList();2847 2848 /*2849 * Change the status to indicate that we‘re out in native code. This2850 * call is not guarded with state-change macros, so we have to do it2851 * by hand.2852 */2853 if (result) {2854 self = dvmThreadSelf();2855 assert(self != NULL);2856 dvmChangeStatus(self, THREAD_NATIVE);2857 *p_env = self->jniEnv;2858 return JNI_OK;2859 } else {2860 return JNI_ERR;2861 }2862 }2863 2864 /*2865 * Attach the current thread to the VM. If the thread is already attached,2866 * this is a no-op.2867 */2868 static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {2869 return attachThread(vm, p_env, thr_args, false);2870 }2871 2872 /*2873 * Like AttachCurrentThread, but set the "daemon" flag.2874 */2875 static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args)2876 {2877 return attachThread(vm, p_env, thr_args, true);2878 }2879 2880 /*2881 * Dissociate the current thread from the VM.2882 */2883 static jint DetachCurrentThread(JavaVM* vm) {2884 Thread* self = dvmThreadSelf();2885 if (self == NULL) {2886 /* not attached, can‘t do anything */2887 return JNI_ERR;2888 }2889 2890 /* switch to "running" to check for suspension */2891 dvmChangeStatus(self, THREAD_RUNNING);2892 2893 /* detach the thread */2894 dvmDetachCurrentThread();2895 2896 /* (no need to change status back -- we have no status) */2897 return JNI_OK;2898 }2899 2900 /*2901 * If current thread is attached to VM, return the associated JNIEnv.2902 * Otherwise, stuff NULL in and return JNI_EDETACHED.2903 *2904 * JVMTI overloads this by specifying a magic value for "version", so we2905 * do want to check that here.2906 */2907 static jint GetEnv(JavaVM* vm, void** env, jint version) {2908 Thread* self = dvmThreadSelf();2909 2910 // GetEnv also accepts JNI_VERSION_1_1, but always returns a JNIEnv*2911 // corresponding to the most current supported JNI version.2912 if (dvmIsBadJniVersion(version) && version != JNI_VERSION_1_1) {2913 ALOGE("Bad JNI version passed to GetEnv: %d", version);2914 return JNI_EVERSION;2915 }2916 2917 if (self == NULL) {2918 *env = NULL;2919 } else {2920 /* TODO: status change is probably unnecessary */2921 dvmChangeStatus(self, THREAD_RUNNING);2922 *env = (void*) dvmGetThreadJNIEnv(self);2923 dvmChangeStatus(self, THREAD_NATIVE);2924 }2925 return (*env != NULL) ? JNI_OK : JNI_EDETACHED;2926 }2927 2928 /*2929 * Destroy the VM. This may be called from any thread.2930 *2931 * If the current thread is attached, wait until the current thread is2932 * the only non-daemon user-level thread. If the current thread is not2933 * attached, we attach it and do the processing as usual. (If the attach2934 * fails, it‘s probably because all the non-daemon threads have already2935 * exited and the VM doesn‘t want to let us back in.)2936 *2937 * TODO: we don‘t really deal with the situation where more than one thread2938 * has called here. One thread wins, the other stays trapped waiting on2939 * the condition variable forever. Not sure this situation is interesting2940 * in real life.2941 */2942 static jint DestroyJavaVM(JavaVM* vm) {2943 JavaVMExt* ext = (JavaVMExt*) vm;2944 if (ext == NULL) {2945 return JNI_ERR;2946 }2947 2948 if (gDvm.verboseShutdown) {2949 ALOGD("DestroyJavaVM waiting for non-daemon threads to exit");2950 }2951 2952 /*2953 * Sleep on a condition variable until it‘s okay to exit.2954 */2955 Thread* self = dvmThreadSelf();2956 if (self == NULL) {2957 JNIEnv* tmpEnv;2958 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {2959 ALOGV("Unable to reattach main for Destroy; assuming VM is shutting down (count=%d)",2960 gDvm.nonDaemonThreadCount);2961 goto shutdown;2962 } else {2963 ALOGV("Attached to wait for shutdown in Destroy");2964 }2965 }2966 dvmChangeStatus(self, THREAD_VMWAIT);2967 2968 dvmLockThreadList(self);2969 gDvm.nonDaemonThreadCount--; // remove current thread from count2970 2971 while (gDvm.nonDaemonThreadCount > 0) {2972 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);2973 }2974 2975 dvmUnlockThreadList();2976 self = NULL;2977 2978 shutdown:2979 // TODO: call System.exit() to run any registered shutdown hooks2980 // (this may not return -- figure out how this should work)2981 2982 if (gDvm.verboseShutdown) {2983 ALOGD("DestroyJavaVM shutting VM down");2984 }2985 dvmShutdown();2986 2987 // TODO - free resources associated with JNI-attached daemon threads2988 free(ext->envList);2989 free(ext);2990 2991 return JNI_OK;2992 }2993 2994 2995 /*2996 * ===========================================================================2997 * Function tables2998 * ===========================================================================2999 */3000 3001 static const struct JNINativeInterface gNativeInterface = {3002 NULL,3003 NULL,3004 NULL,3005 NULL,3006 3007 GetVersion,3008 3009 DefineClass,3010 FindClass,3011 3012 FromReflectedMethod,3013 FromReflectedField,3014 ToReflectedMethod,3015 3016 GetSuperclass,3017 IsAssignableFrom,3018 3019 ToReflectedField,3020 3021 Throw,3022 ThrowNew,3023 ExceptionOccurred,3024 ExceptionDescribe,3025 ExceptionClear,3026 FatalError,3027 3028 PushLocalFrame,3029 PopLocalFrame,3030 3031 NewGlobalRef,3032 DeleteGlobalRef,3033 DeleteLocalRef,3034 IsSameObject,3035 NewLocalRef,3036 EnsureLocalCapacity,3037 3038 AllocObject,3039 NewObject,3040 NewObjectV,3041 NewObjectA,3042 3043 GetObjectClass,3044 IsInstanceOf,3045 3046 GetMethodID,3047 3048 CallObjectMethod,3049 CallObjectMethodV,3050 CallObjectMethodA,3051 CallBooleanMethod,3052 CallBooleanMethodV,3053 CallBooleanMethodA,3054 CallByteMethod,3055 CallByteMethodV,3056 CallByteMethodA,3057 CallCharMethod,3058 CallCharMethodV,3059 CallCharMethodA,3060 CallShortMethod,3061 CallShortMethodV,3062 CallShortMethodA,3063 CallIntMethod,3064 CallIntMethodV,3065 CallIntMethodA,3066 CallLongMethod,3067 CallLongMethodV,3068 CallLongMethodA,3069 CallFloatMethod,3070 CallFloatMethodV,3071 CallFloatMethodA,3072 CallDoubleMethod,3073 CallDoubleMethodV,3074 CallDoubleMethodA,3075 CallVoidMethod,3076 CallVoidMethodV,3077 CallVoidMethodA,3078 3079 CallNonvirtualObjectMethod,3080 CallNonvirtualObjectMethodV,3081 CallNonvirtualObjectMethodA,3082 CallNonvirtualBooleanMethod,3083 CallNonvirtualBooleanMethodV,3084 CallNonvirtualBooleanMethodA,3085 CallNonvirtualByteMethod,3086 CallNonvirtualByteMethodV,3087 CallNonvirtualByteMethodA,3088 CallNonvirtualCharMethod,3089 CallNonvirtualCharMethodV,3090 CallNonvirtualCharMethodA,3091 CallNonvirtualShortMethod,3092 CallNonvirtualShortMethodV,3093 CallNonvirtualShortMethodA,3094 CallNonvirtualIntMethod,3095 CallNonvirtualIntMethodV,3096 CallNonvirtualIntMethodA,3097 CallNonvirtualLongMethod,3098 CallNonvirtualLongMethodV,3099 CallNonvirtualLongMethodA,3100 CallNonvirtualFloatMethod,3101 CallNonvirtualFloatMethodV,3102 CallNonvirtualFloatMethodA,3103 CallNonvirtualDoubleMethod,3104 CallNonvirtualDoubleMethodV,3105 CallNonvirtualDoubleMethodA,3106 CallNonvirtualVoidMethod,3107 CallNonvirtualVoidMethodV,3108 CallNonvirtualVoidMethodA,3109 3110 GetFieldID,3111 3112 GetObjectField,3113 GetBooleanField,3114 GetByteField,3115 GetCharField,3116 GetShortField,3117 GetIntField,3118 GetLongField,3119 GetFloatField,3120 GetDoubleField,3121 SetObjectField,3122 SetBooleanField,3123 SetByteField,3124 SetCharField,3125 SetShortField,3126 SetIntField,3127 SetLongField,3128 SetFloatField,3129 SetDoubleField,3130 3131 GetStaticMethodID,3132 3133 CallStaticObjectMethod,3134 CallStaticObjectMethodV,3135 CallStaticObjectMethodA,3136 CallStaticBooleanMethod,3137 CallStaticBooleanMethodV,3138 CallStaticBooleanMethodA,3139 CallStaticByteMethod,3140 CallStaticByteMethodV,3141 CallStaticByteMethodA,3142 CallStaticCharMethod,3143 CallStaticCharMethodV,3144 CallStaticCharMethodA,3145 CallStaticShortMethod,3146 CallStaticShortMethodV,3147 CallStaticShortMethodA,3148 CallStaticIntMethod,3149 CallStaticIntMethodV,3150 CallStaticIntMethodA,3151 CallStaticLongMethod,3152 CallStaticLongMethodV,3153 CallStaticLongMethodA,3154 CallStaticFloatMethod,3155 CallStaticFloatMethodV,3156 CallStaticFloatMethodA,3157 CallStaticDoubleMethod,3158 CallStaticDoubleMethodV,3159 CallStaticDoubleMethodA,3160 CallStaticVoidMethod,3161 CallStaticVoidMethodV,3162 CallStaticVoidMethodA,3163 3164 GetStaticFieldID,3165 3166 GetStaticObjectField,3167 GetStaticBooleanField,3168 GetStaticByteField,3169 GetStaticCharField,3170 GetStaticShortField,3171 GetStaticIntField,3172 GetStaticLongField,3173 GetStaticFloatField,3174 GetStaticDoubleField,3175 3176 SetStaticObjectField,3177 SetStaticBooleanField,3178 SetStaticByteField,3179 SetStaticCharField,3180 SetStaticShortField,3181 SetStaticIntField,3182 SetStaticLongField,3183 SetStaticFloatField,3184 SetStaticDoubleField,3185 3186 NewString,3187 3188 GetStringLength,3189 GetStringChars,3190 ReleaseStringChars,3191 3192 NewStringUTF,3193 GetStringUTFLength,3194 GetStringUTFChars,3195 ReleaseStringUTFChars,3196 3197 GetArrayLength,3198 NewObjectArray,3199 GetObjectArrayElement,3200 SetObjectArrayElement,3201 3202 NewBooleanArray,3203 NewByteArray,3204 NewCharArray,3205 NewShortArray,3206 NewIntArray,3207 NewLongArray,3208 NewFloatArray,3209 NewDoubleArray,3210 3211 GetBooleanArrayElements,3212 GetByteArrayElements,3213 GetCharArrayElements,3214 GetShortArrayElements,3215 GetIntArrayElements,3216 GetLongArrayElements,3217 GetFloatArrayElements,3218 GetDoubleArrayElements,3219 3220 ReleaseBooleanArrayElements,3221 ReleaseByteArrayElements,3222 ReleaseCharArrayElements,3223 ReleaseShortArrayElements,3224 ReleaseIntArrayElements,3225 ReleaseLongArrayElements,3226 ReleaseFloatArrayElements,3227 ReleaseDoubleArrayElements,3228 3229 GetBooleanArrayRegion,3230 GetByteArrayRegion,3231 GetCharArrayRegion,3232 GetShortArrayRegion,3233 GetIntArrayRegion,3234 GetLongArrayRegion,3235 GetFloatArrayRegion,3236 GetDoubleArrayRegion,3237 SetBooleanArrayRegion,3238 SetByteArrayRegion,3239 SetCharArrayRegion,3240 SetShortArrayRegion,3241 SetIntArrayRegion,3242 SetLongArrayRegion,3243 SetFloatArrayRegion,3244 SetDoubleArrayRegion,3245 3246 RegisterNatives,3247 UnregisterNatives,3248 3249 MonitorEnter,3250 MonitorExit,3251 3252 GetJavaVM,3253 3254 GetStringRegion,3255 GetStringUTFRegion,3256 3257 GetPrimitiveArrayCritical,3258 ReleasePrimitiveArrayCritical,3259 3260 GetStringCritical,3261 ReleaseStringCritical,3262 3263 NewWeakGlobalRef,3264 DeleteWeakGlobalRef,3265 3266 ExceptionCheck,3267 3268 NewDirectByteBuffer,3269 GetDirectBufferAddress,3270 GetDirectBufferCapacity,3271 3272 GetObjectRefType3273 };3274 3275 static const struct JNIInvokeInterface gInvokeInterface = {3276 NULL,3277 NULL,3278 NULL,3279 3280 DestroyJavaVM,3281 AttachCurrentThread,3282 DetachCurrentThread,3283 3284 GetEnv,3285 3286 AttachCurrentThreadAsDaemon,3287 };3288 3289 /*3290 * ===========================================================================3291 * VM/Env creation3292 * ===========================================================================3293 */3294 3295 /*3296 * Create a new JNIEnv struct and add it to the VM‘s list.3297 *3298 * "self" will be NULL for the main thread, since the VM hasn‘t started3299 * yet; the value will be filled in later.3300 */3301 JNIEnv* dvmCreateJNIEnv(Thread* self) {3302 JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;3303 3304 //if (self != NULL)3305 // ALOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self);3306 3307 assert(vm != NULL);3308 3309 JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));3310 newEnv->funcTable = &gNativeInterface;3311 if (self != NULL) {3312 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);3313 assert(newEnv->envThreadId != 0);3314 } else {3315 /* make it obvious if we fail to initialize these later */3316 newEnv->envThreadId = 0x77777775;3317 newEnv->self = (Thread*) 0x77777779;3318 }3319 if (gDvmJni.useCheckJni) {3320 dvmUseCheckedJniEnv(newEnv);3321 }3322 3323 ScopedPthreadMutexLock lock(&vm->envListLock);3324 3325 /* insert at head of list */3326 newEnv->next = vm->envList;3327 assert(newEnv->prev == NULL);3328 if (vm->envList == NULL) {3329 // rare, but possible3330 vm->envList = newEnv;3331 } else {3332 vm->envList->prev = newEnv;3333 }3334 vm->envList = newEnv;3335 3336 //if (self != NULL)3337 // ALOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self);3338 return (JNIEnv*) newEnv;3339 }3340 3341 /*3342 * Remove a JNIEnv struct from the list and free it.3343 */3344 void dvmDestroyJNIEnv(JNIEnv* env) {3345 if (env == NULL) {3346 return;3347 }3348 3349 //ALOGI("Ent DestroyJNIEnv: threadid=%d %p", self->threadId, self);3350 3351 JNIEnvExt* extEnv = (JNIEnvExt*) env;3352 JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm;3353 3354 ScopedPthreadMutexLock lock(&vm->envListLock);3355 3356 if (extEnv == vm->envList) {3357 assert(extEnv->prev == NULL);3358 vm->envList = extEnv->next;3359 } else {3360 assert(extEnv->prev != NULL);3361 extEnv->prev->next = extEnv->next;3362 }3363 if (extEnv->next != NULL) {3364 extEnv->next->prev = extEnv->prev;3365 }3366 3367 free(env);3368 //ALOGI("Xit DestroyJNIEnv: threadid=%d %p", self->threadId, self);3369 }3370 3371 /*3372 * Enable "checked JNI" after the VM has partially started. This must3373 * only be called in "zygote" mode, when we have one thread running.3374 *3375 * This doesn‘t attempt to rewrite the JNI call bridge associated with3376 * native methods, so we won‘t get those checks for any methods that have3377 * already been resolved.3378 */3379 void dvmLateEnableCheckedJni() {3380 JNIEnvExt* extEnv = dvmGetJNIEnvForThread();3381 if (extEnv == NULL) {3382 ALOGE("dvmLateEnableCheckedJni: thread has no JNIEnv");3383 return;3384 }3385 JavaVMExt* extVm = (JavaVMExt*) gDvmJni.jniVm;3386 assert(extVm != NULL);3387 3388 if (!gDvmJni.useCheckJni) {3389 ALOGD("Late-enabling CheckJNI");3390 dvmUseCheckedJniVm(extVm);3391 dvmUseCheckedJniEnv(extEnv);3392 } else {3393 ALOGD("Not late-enabling CheckJNI (already on)");3394 }3395 }3396 3397 /*3398 * Not supported.3399 */3400 jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) {3401 return JNI_ERR;3402 }3403 3404 /*3405 * Return a buffer full of created VMs.3406 *3407 * We always have zero or one.3408 */3409 jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) {3410 if (gDvmJni.jniVm != NULL) {3411 *nVMs = 1;3412 if (bufLen > 0) {3413 *vmBuf++ = gDvmJni.jniVm;3414 }3415 } else {3416 *nVMs = 0;3417 }3418 return JNI_OK;3419 }3420 3421 /*3422 * Create a new VM instance.3423 *3424 * The current thread becomes the main VM thread. We return immediately,3425 * which effectively means the caller is executing in a native method.3426 */3427 jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {3428 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;3429 if (dvmIsBadJniVersion(args->version)) {3430 ALOGE("Bad JNI version passed to CreateJavaVM: %d", args->version);3431 return JNI_EVERSION;3432 }3433 3434 // TODO: don‘t allow creation of multiple VMs -- one per customer for now3435 3436 /* zero globals; not strictly necessary the first time a VM is started */3437 memset(&gDvm, 0, sizeof(gDvm));3438 3439 /*3440 * Set up structures for JNIEnv and VM.3441 */3442 JavaVMExt* pVM = (JavaVMExt*) calloc(1, sizeof(JavaVMExt));3443 pVM->funcTable = &gInvokeInterface;3444 pVM->envList = NULL;3445 dvmInitMutex(&pVM->envListLock);3446 3447 UniquePtr<const char*[]> argv(new const char*[args->nOptions]);3448 memset(argv.get(), 0, sizeof(char*) * (args->nOptions));3449 3450 /*3451 * Convert JNI args to argv.3452 *3453 * We have to pull out vfprintf/exit/abort, because they use the3454 * "extraInfo" field to pass function pointer "hooks" in. We also3455 * look for the -Xcheck:jni stuff here.3456 */3457 int argc = 0;3458 for (int i = 0; i < args->nOptions; i++) {3459 const char* optStr = args->options[i].optionString;3460 if (optStr == NULL) {3461 dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i);3462 return JNI_ERR;3463 } else if (strcmp(optStr, "vfprintf") == 0) {3464 gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo;3465 } else if (strcmp(optStr, "exit") == 0) {3466 gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo;3467 } else if (strcmp(optStr, "abort") == 0) {3468 gDvm.abortHook = (void (*)(void))args->options[i].extraInfo;3469 } else if (strcmp(optStr, "sensitiveThread") == 0) {3470 gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo;3471 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {3472 gDvmJni.useCheckJni = true;3473 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {3474 char* jniOpts = strdup(optStr + 10);3475 size_t jniOptCount = 1;3476 for (char* p = jniOpts; *p != 0; ++p) {3477 if (*p == ‘,‘) {3478 ++jniOptCount;3479 *p = 0;3480 }3481 }3482 char* jniOpt = jniOpts;3483 for (size_t i = 0; i < jniOptCount; ++i) {3484 if (strcmp(jniOpt, "warnonly") == 0) {3485 gDvmJni.warnOnly = true;3486 } else if (strcmp(jniOpt, "forcecopy") == 0) {3487 gDvmJni.forceCopy = true;3488 } else if (strcmp(jniOpt, "logThirdPartyJni") == 0) {3489 gDvmJni.logThirdPartyJni = true;3490 } else {3491 dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option ‘%s‘\n",3492 jniOpt);3493 free(pVM);3494 free(jniOpts);3495 return JNI_ERR;3496 }3497 jniOpt += strlen(jniOpt) + 1;3498 }3499 free(jniOpts);3500 } else {3501 /* regular option */3502 argv[argc++] = optStr;3503 }3504 }3505 3506 if (gDvmJni.useCheckJni) {3507 dvmUseCheckedJniVm(pVM);3508 }3509 3510 if (gDvmJni.jniVm != NULL) {3511 dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n");3512 free(pVM);3513 return JNI_ERR;3514 }3515 gDvmJni.jniVm = (JavaVM*) pVM;3516 3517 /*3518 * Create a JNIEnv for the main thread. We need to have something set up3519 * here because some of the class initialization we do when starting3520 * up the VM will call into native code.3521 */3522 JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);3523 3524 /* Initialize VM. */3525 gDvm.initializing = true;3526 std::string status =3527 dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv);3528 gDvm.initializing = false;3529 3530 if (!status.empty()) {3531 free(pEnv);3532 free(pVM);3533 ALOGW("CreateJavaVM failed: %s", status.c_str());3534 return JNI_ERR;3535 }3536 3537 /*3538 * Success! Return stuff to caller.3539 */3540 dvmChangeStatus(NULL, THREAD_NATIVE);3541 *p_env = (JNIEnv*) pEnv;3542 *p_vm = (JavaVM*) pVM;3543 ALOGV("CreateJavaVM succeeded");3544 return JNI_OK;3545 }
拿2.3.3的
print(GetString(R1,-1, ASCSTR_C)),使用0 * print(GetString(DbgDword(R2+0x10),-1, ASCSTR_C)) 跟踪而不断下来,虽然文档说‘void‘ means that function returns no meaningful value (always 0),但是还会段下来,乘以0就不一样,即运行语句又判断值。
IDA的帮助文档
// Print variables in the message window// This function print text representation of all its arguments to the output window.// This function can be used to debug IDC scriptsvoid print (...);
Alphabetical list of IDC functions
The following conventions are used in the function descriptions:
‘ea‘ is a linear address ‘success‘ is 0 if a function fails, 1 otherwise ‘void‘ means that function returns no meaningful value (always 0) ‘anyvalue‘ means that function may return value of any type
下面一个截图跟踪方法调用,最后到我们的jni方法。
看这里http://blog.csdn.net/luoshengyang/article/details/8923483,了解到注册jni方法以后再调用jni方法需要经过最直接看出的就是第一二参数变量等准备,这个来自注册时由Dalvik虚拟机的启动选项来为即将要注册的JNI选择一个合适的Bridge函数。以后通过这个Bridge函数(DalvikBridgeFunc)调用你的jni方法。但是这里有2类,每类4个。一般只有能选择1类,则对于jni方法调用存在4种调用过程。正如前面描述,从哪个版本开始就不会出现了,已经只有 一个了。
Dalvik虚拟机提供的Bridge函数主要是分为两类。第一类Bridge函数在调用完成JNI方法之后,会检查该JNI方法的返回结果是否与声明的一致,这是因为一个声明返回String的JNI方法在执行时返回的可能会是一个Byte Array。如果不一致,取决于Dalvik虚拟机的启动选项,它可能会停机。第二类Bridge函数不对JNI方法的返回结果进行上述检查。选择哪一类Bridge函数可以通过-Xcheck:jni选项来决定。不过由于检查一个JNI方法的返回结果是否与声明的一致是很耗时的,因此,我们一般都不会使用第一类Bridge函数。
此外,每一类Bridge函数又分为四个子类:Genernal、Sync、VirtualNoRef和StaticNoRef,它们的选择规则为:
1. 一个JNI方法的参数列表中如果包含有引用类型的参数,那么对应的Bridge函数就是Genernal类型的,即为dvmCallJNIMethod_general或者dvmCheckCallJNIMethod_general。
2. 一个JNI方法如果声明为同步方法,即带有synchronized修饰符,那么对应的Bridge函数就是Sync类型的,即为dvmCallJNIMethod_synchronized或者dvmCheckCallJNIMethod_synchronized。
3. 一个JNI方法的参数列表中如果不包含有引用类型的参数,并且它是一个虚成员函数,那么对应的Bridge函数就是kJNIVirtualNoRef类型的,即为dvmCallJNIMethod_virtualNoRef或者dvmCheckCallJNIMethod_virtualNoRef。
4. 一个JNI方法的参数列表中如果不包含有引用类型的参数,并且它是一个静态成员函数,那么对应的Bridge函数就是StaticNoRef类型的,即为dvmCallJNIMethod_staticNoRef或者dvmCheckCallJNIMethod_staticNoRef。
每一类Bridge函数之所以要划分为上述四个子类,是因为每一个子类的Bridge函数在调用真正的JNI方法之前,所要进行的准备工作是不一样的。例如,Genernal类型的Bridge函数需要为引用类型的参数增加一个本地引用,避免它在JNI方法执行的过程中被回收。又如,Sync类型的Bridge函数在调用JNI方法之前,需要执行同步原始,以避免多线程访问的竞争问题。
void dvmCallJNIMethod_general(const u4* args, JValue* pResult, const Method* method, Thread* self)
这样是不是说根据"method" == GetString(DbgDword(R2+0x10),-1, ASCSTR_C)条件断点,找到调用的函数地址。
//这个头可以选择VC++编译器直接导入到ida里typedef void ClassObject;typedef void DexFile;typedef void JValue;typedef void RegisterMap;typedef int u4 ;typedef short u2; //xref: KitKat 4.4.2_r2 /dalvik/libdex/DexProto.h/** Method prototype structure, which refers to a protoIdx in a* particular DexFile.*/struct DexProto { const DexFile* dexFile; /* file the idx refers to */ u4 protoIdx; /* index into proto_ids table of dexFile */};/* * Native function pointer type. * * "args[0]" holds the "this" pointer for virtual methods. * * The "Bridge" form is a super-set of the "Native" form; in many places * they are used interchangeably. Currently, all functions have all * arguments passed in, but some functions only care about the first two. * Passing extra arguments to a C function is (mostly) harmless. */typedef void (*DalvikBridgeFunc)(const u4* args, JValue* pResult, const Method* method, void* self);/* * A method. We create one of these for every method in every class * we load, so try to keep the size to a minimum. * * Much of this comes from and could be accessed in the data held in shared * memory. We hold it all together here for speed. Everything but the * pointers could be held in a shared table generated by the optimizer; * if we‘re willing to convert them to offsets and take the performance * hit (e.g. "meth->insns" becomes "baseAddr + meth->insnsOffset") we * could move everything but "nativeFunc". */struct Method { /* the class we are a part of */ ClassObject* clazz; /* access flags; low 16 bits are defined by spec (could be u2?) */ u4 accessFlags; /* * For concrete virtual methods, this is the offset of the method * in "vtable". * * For abstract methods in an interface class, this is the offset * of the method in "iftable[n]->methodIndexArray". */ u2 methodIndex; /* * Method bounds; not needed for an abstract method. * * For a native method, we compute the size of the argument list, and * set "insSize" and "registerSize" equal to it. */ u2 registersSize; /* ins + locals */ u2 outsSize; u2 insSize; /* method name, e.g. "<init>" or "eatLunch" */ const char* name; /* * Method prototype descriptor string (return and argument types). * * TODO: This currently must specify the DexFile as well as the proto_ids * index, because generated Proxy classes don‘t have a DexFile. We can * remove the DexFile* and reduce the size of this struct if we generate * a DEX for proxies. */ DexProto prototype; /* short-form method descriptor string */ const char* shorty; /* * The remaining items are not used for abstract or native methods. * (JNI is currently hijacking "insns" as a function pointer, set * after the first call. For internal-native this stays null.) */ /* the actual code */ const u2* insns; /* instructions, in memory-mapped .dex */ /* JNI: cached argument and return-type hints */ int jniArgInfo; /* * JNI: native method ptr; could be actual function or a JNI bridge. We * don‘t currently discriminate between DalvikBridgeFunc and * DalvikNativeFunc; the former takes an argument superset (i.e. two * extra args) which will be ignored. If necessary we can use * insns==NULL to detect JNI bridge vs. internal native. */ DalvikBridgeFunc nativeFunc; /* * JNI: true if this static non-synchronized native method (that has no * reference arguments) needs a JNIEnv* and jclass/jobject. Libcore * uses this. */ bool fastJni; /* * JNI: true if this method has no reference arguments. This lets the JNI * bridge avoid scanning the shorty for direct pointers that need to be * converted to local references. * * TODO: replace this with a list of indexes of the reference arguments. */ bool noRef; /* * JNI: true if we should log entry and exit. This is the only way * developers can log the local references that are passed into their code. * Used for debugging JNI problems in third-party code. */ bool shouldTrace; /* * Register map data, if available. This will point into the DEX file * if the data was computed during pre-verification, or into the * linear alloc area if not. */ const RegisterMap* registerMap; /* set if method was called during method profiling */ bool inProfile;};
#include <jni.h>jstring com_fang_atest_MainActivity_method(JNIEnv *pEnv, jobject o, jstring param){ return pEnv->NewStringUTF("222");}/* * 由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时, * 可多次调用registerNativeMethods()函数来更换本地函数的指针, * 从而达到弹性调用本地函数的目的。 */static JNINativeMethod gMethods[] ={/* name, signature, funcPtr */{ "method", "(Ljava/lang/String;)Ljava/lang/String;", (void*) com_fang_atest_MainActivity_method }, };static const char* const className = "com/fang/atest/MainActivity";//在native 注册的时候首先保存java的调用方法:以便事件回调而不必每次获取,即在native里调用java对象,方法等。int register_com_fang_atest(JNIEnv* pEnv){ jclass clazz; //ALOGI(TAG, "Registering %s natives\n", className); clazz = pEnv->FindClass(className); if (clazz == 0) { //ALOGE("Native registration unable to find class ‘%s‘\n", className); return -1; } if (pEnv->RegisterNatives(clazz, gMethods, 1) < 0) { //ALOGE("RegisterNatives failed for ‘%s‘\n", className); return -1; } return 0;}// Set some test stuff up.//Used by WithFramework to register native functions.//Returns the JNI version on success, -1 on failure.//Dalvik虚拟机加载C库时,第一件事是调用JNI_OnLoad()函数extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved){ JNIEnv* env = 0; /*JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint); * GetEnv()函数返回的 Jni 环境对每个线程来说是不同的, * 由于Dalvik虚拟机通常是Multi-threading的。每一个线程调用JNI_OnLoad()时, * 所用的JNI Env是不同的,因此我们必须在每次进入函数时都要通过vm->GetEnv重新获取 */ //得到JNI Env if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4) != JNI_OK) { return JNI_ERR; } // Get jclass with env->FindClass. // Register methods with env->RegisterNatives //assert(env != NULL); register_com_fang_atest(env); // success -- return valid version number return JNI_VERSION_1_4;}
package com.fang.atest;import java.io.PrintWriter;import java.io.StringWriter;import java.io.Writer;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends Activity { /* * static { // load library: libADemo.so try { System.loadLibrary("ATest"); * } catch (UnsatisfiedLinkError ule) { * System.err.println("WARNING: Could not load library!"); } * * } */ private native String method(String paramString1); TextView tv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) MainActivity.this.findViewById(R.id.textView1); Button btnCall = (Button) findViewById(R.id.button1); btnCall.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub try { tv.setText(method("888")); } catch (UnsatisfiedLinkError ule) { tv.setText(getStackTrace(ule)); ule.printStackTrace(); } } }); Button btnLoad = (Button) findViewById(R.id.button2); btnLoad.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub try { System.loadLibrary("ATest"); } catch (UnsatisfiedLinkError ule) { tv.setText(getStackTrace(ule)); System.err.println("WARNING: Could not load library!"); } } }); /* * Button btnUnLoad = (Button) findViewById(R.id.button1); * btnLoad.setOnClickListener(new View.OnClickListener() { * * @Override public void onClick(View v) { // TODO Auto-generated method * stub try { System.loadLibrary("ATest"); } catch (UnsatisfiedLinkError * ule) { System.err.println("WARNING: Could not load library!"); } } * }); */ } /** * 将异常堆栈转换为字符串 * * @param aThrowable * 异常 * @return String */ public static String getStackTrace(Throwable aThrowable) { final Writer result = new StringWriter(); final PrintWriter printWriter = new PrintWriter(result); aThrowable.printStackTrace(printWriter); return result.toString(); }}
Android调用JNI本地方法经过有点改变