Native.cpp revision 99409883d9c4c0ffb49b070ce307bb33a9dfe9f1
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16/* 17 * Native method resolution. 18 * 19 * Currently the "Dalvik native" methods are only used for internal methods. 20 * Someday we may want to export the interface as a faster but riskier 21 * alternative to JNI. 22 */ 23#include "Dalvik.h" 24 25#include <stdlib.h> 26#include <dlfcn.h> 27 28static void freeSharedLibEntry(void* ptr); 29static void* lookupSharedLibMethod(const Method* method); 30 31 32/* 33 * Initialize the native code loader. 34 */ 35bool dvmNativeStartup(void) 36{ 37 gDvm.nativeLibs = dvmHashTableCreate(4, freeSharedLibEntry); 38 if (gDvm.nativeLibs == NULL) 39 return false; 40 41 return true; 42} 43 44/* 45 * Free up our tables. 46 */ 47void dvmNativeShutdown(void) 48{ 49 dvmHashTableFree(gDvm.nativeLibs); 50 gDvm.nativeLibs = NULL; 51} 52 53 54/* 55 * Resolve a native method and invoke it. 56 * 57 * This is executed as if it were a native bridge or function. If the 58 * resolution succeeds, method->insns is replaced, and we don't go through 59 * here again. 60 * 61 * Initializes method's class if necessary. 62 * 63 * An exception is thrown on resolution failure. 64 */ 65void dvmResolveNativeMethod(const u4* args, JValue* pResult, 66 const Method* method, Thread* self) 67{ 68 ClassObject* clazz = method->clazz; 69 void* func; 70 71 /* 72 * If this is a static method, it could be called before the class 73 * has been initialized. 74 */ 75 if (dvmIsStaticMethod(method)) { 76 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 77 assert(dvmCheckException(dvmThreadSelf())); 78 return; 79 } 80 } else { 81 assert(dvmIsClassInitialized(clazz) || 82 dvmIsClassInitializing(clazz)); 83 } 84 85 /* start with our internal-native methods */ 86 func = dvmLookupInternalNativeMethod(method); 87 if (func != NULL) { 88 /* resolution always gets the same answer, so no race here */ 89 IF_LOGVV() { 90 char* desc = dexProtoCopyMethodDescriptor(&method->prototype); 91 LOGVV("+++ resolved native %s.%s %s, invoking\n", 92 clazz->descriptor, method->name, desc); 93 free(desc); 94 } 95 if (dvmIsSynchronizedMethod(method)) { 96 LOGE("ERROR: internal-native can't be declared 'synchronized'\n"); 97 LOGE("Failing on %s.%s\n", method->clazz->descriptor, method->name); 98 dvmAbort(); // harsh, but this is VM-internal problem 99 } 100 DalvikBridgeFunc dfunc = (DalvikBridgeFunc) func; 101 dvmSetNativeFunc(method, dfunc, NULL); 102 assert(method->insns == NULL); 103 dfunc(args, pResult, method, self); 104 return; 105 } 106 107 /* now scan any DLLs we have loaded for JNI signatures */ 108 func = lookupSharedLibMethod(method); 109 if (func != NULL) { 110 if (dvmIsSynchronizedMethod(method)) 111 dvmSetNativeFunc(method, dvmCallSynchronizedJNIMethod, func); 112 else 113 dvmSetNativeFunc(method, dvmCallJNIMethod, func); 114 dvmCallJNIMethod(args, pResult, method, self); 115 return; 116 } 117 118 IF_LOGW() { 119 char* desc = dexProtoCopyMethodDescriptor(&method->prototype); 120 LOGW("No implementation found for native %s.%s %s\n", 121 clazz->descriptor, method->name, desc); 122 free(desc); 123 } 124 125 dvmThrowException("Ljava/lang/UnsatisfiedLinkError;", method->name); 126} 127 128 129/* 130 * =========================================================================== 131 * Native shared library support 132 * =========================================================================== 133 */ 134 135// TODO? if a ClassLoader is unloaded, we need to unload all DLLs that 136// are associated with it. (Or not -- can't determine if native code 137// is still using parts of it.) 138 139/* 140 * We add one of these to the hash table for every library we load. The 141 * hash is on the "pathName" field. 142 */ 143typedef struct SharedLib { 144 char* pathName; /* absolute path to library */ 145 void* handle; /* from dlopen */ 146 Object* classLoader; /* ClassLoader we are associated with */ 147} SharedLib; 148 149/* 150 * (This is a dvmHashTableLookup callback.) 151 * 152 * Find an entry that matches the string. 153 */ 154static int hashcmpNameStr(const void* ventry, const void* vname) 155{ 156 const SharedLib* pLib = (const SharedLib*) ventry; 157 const char* name = (const char*) vname; 158 159 return strcmp(pLib->pathName, name); 160} 161 162/* 163 * (This is a dvmHashTableLookup callback.) 164 * 165 * Find an entry that matches the new entry. 166 */ 167static int hashcmpSharedLib(const void* ventry, const void* vnewEntry) 168{ 169 const SharedLib* pLib = (const SharedLib*) ventry; 170 const SharedLib* pNewLib = (const SharedLib*) vnewEntry; 171 172 LOGD("--- comparing %p '%s' %p '%s'\n", 173 pLib, pLib->pathName, pNewLib, pNewLib->pathName); 174 return strcmp(pLib->pathName, pNewLib->pathName); 175} 176 177/* 178 * Check to see if an entry with the same pathname already exists. 179 */ 180static const SharedLib* findSharedLibEntry(const char* pathName) 181{ 182 u4 hash = dvmComputeUtf8Hash(pathName); 183 void* ent; 184 185 ent = dvmHashTableLookup(gDvm.nativeLibs, hash, (void*)pathName, 186 hashcmpNameStr, false); 187 return ent; 188} 189 190/* 191 * Add the new entry to the table. 192 * 193 * Returns "true" on success, "false" if the entry already exists. 194 */ 195static bool addSharedLibEntry(SharedLib* pLib) 196{ 197 u4 hash = dvmComputeUtf8Hash(pLib->pathName); 198 void* ent; 199 200 /* 201 * Do the lookup with the "add" flag set. If we add it, we will get 202 * our own pointer back. If somebody beat us to the punch, we'll get 203 * their pointer back instead. 204 */ 205 ent = dvmHashTableLookup(gDvm.nativeLibs, hash, pLib, hashcmpSharedLib, 206 true); 207 return (ent == pLib); 208} 209 210/* 211 * Free up an entry. (This is a dvmHashTableFree callback.) 212 */ 213static void freeSharedLibEntry(void* ptr) 214{ 215 SharedLib* pLib = (SharedLib*) ptr; 216 217 /* 218 * Calling dlclose() here is somewhat dangerous, because it's possible 219 * that a thread outside the VM is still accessing the code we loaded. 220 */ 221 if (false) 222 dlclose(pLib->handle); 223 free(pLib->pathName); 224 free(pLib); 225} 226 227/* 228 * Convert library name to system-dependent form, e.g. "jpeg" becomes 229 * "libjpeg.so". 230 * 231 * (Should we have this take buffer+len and avoid the alloc? It gets 232 * called very rarely.) 233 */ 234char* dvmCreateSystemLibraryName(char* libName) 235{ 236 char buf[256]; 237 int len; 238 239 len = snprintf(buf, sizeof(buf), OS_SHARED_LIB_FORMAT_STR, libName); 240 if (len >= (int) sizeof(buf)) 241 return NULL; 242 else 243 return strdup(buf); 244} 245 246 247#if 0 248/* 249 * Find a library, given the lib's system-dependent name (e.g. "libjpeg.so"). 250 * 251 * We need to search through the path defined by the java.library.path 252 * property. 253 * 254 * Returns NULL if the library was not found. 255 */ 256static char* findLibrary(const char* libSysName) 257{ 258 char* javaLibraryPath = NULL; 259 char* testName = NULL; 260 char* start; 261 char* cp; 262 bool done; 263 264 javaLibraryPath = dvmGetProperty("java.library.path"); 265 if (javaLibraryPath == NULL) 266 goto bail; 267 268 LOGVV("+++ path is '%s'\n", javaLibraryPath); 269 270 start = cp = javaLibraryPath; 271 while (cp != NULL) { 272 char pathBuf[256]; 273 int len; 274 275 cp = strchr(start, ':'); 276 if (cp != NULL) 277 *cp = '\0'; 278 279 len = snprintf(pathBuf, sizeof(pathBuf), "%s/%s", start, libSysName); 280 if (len >= (int) sizeof(pathBuf)) { 281 LOGW("Path overflowed %d bytes: '%s' / '%s'\n", 282 len, start, libSysName); 283 /* keep going, next one might fit */ 284 } else { 285 LOGVV("+++ trying '%s'\n", pathBuf); 286 if (access(pathBuf, R_OK) == 0) { 287 testName = strdup(pathBuf); 288 break; 289 } 290 } 291 292 start = cp +1; 293 } 294 295bail: 296 free(javaLibraryPath); 297 return testName; 298} 299 300/* 301 * Load a native shared library, given the system-independent piece of 302 * the library name. 303 * 304 * Throws an exception on failure. 305 */ 306void dvmLoadNativeLibrary(StringObject* libNameObj, Object* classLoader) 307{ 308 char* libName = NULL; 309 char* libSysName = NULL; 310 char* libPath = NULL; 311 312 /* 313 * If "classLoader" isn't NULL, call the class loader's "findLibrary" 314 * method with the lib name. If it returns a non-NULL result, we use 315 * that as the pathname. 316 */ 317 if (classLoader != NULL) { 318 Method* findLibrary; 319 Object* findLibResult; 320 321 findLibrary = dvmFindVirtualMethodByDescriptor(classLoader->clazz, 322 "findLibrary", "(Ljava/lang/String;)Ljava/lang/String;"); 323 if (findLibrary == NULL) { 324 LOGW("Could not find findLibrary() in %s\n", 325 classLoader->clazz->name); 326 dvmThrowException("Ljava/lang/UnsatisfiedLinkError;", 327 "findLibrary"); 328 goto bail; 329 } 330 331 findLibResult = (Object*)(u4) dvmCallMethod(findLibrary, classLoader, 332 libNameObj); 333 if (dvmCheckException()) { 334 LOGV("returning early on exception\n"); 335 goto bail; 336 } 337 if (findLibResult != NULL) { 338 /* success! */ 339 libPath = dvmCreateCstrFromString(libNameObj); 340 LOGI("Found library through CL: '%s'\n", libPath); 341 dvmLoadNativeCode(libPath, classLoader); 342 goto bail; 343 } 344 } 345 346 libName = dvmCreateCstrFromString(libNameObj); 347 if (libName == NULL) 348 goto bail; 349 libSysName = dvmCreateSystemLibraryName(libName); 350 if (libSysName == NULL) 351 goto bail; 352 353 libPath = findLibrary(libSysName); 354 if (libPath != NULL) { 355 LOGD("Found library through path: '%s'\n", libPath); 356 dvmLoadNativeCode(libPath, classLoader); 357 } else { 358 LOGW("Unable to locate shared lib matching '%s'\n", libSysName); 359 dvmThrowException("Ljava/lang/UnsatisfiedLinkError;", libName); 360 } 361 362bail: 363 free(libName); 364 free(libSysName); 365 free(libPath); 366} 367#endif 368 369typedef int (*OnLoadFunc)(JavaVM*, void*); 370 371/* 372 * Load native code from the specified absolute pathname. Per the spec, 373 * if we've already loaded a library with the specified pathname, we 374 * return without doing anything. 375 * 376 * TODO? for better results we should absolutify the pathname. For fully 377 * correct results we should stat to get the inode and compare that. The 378 * existing implementation is fine so long as everybody is using 379 * System.loadLibrary. 380 * 381 * The library will be associated with the specified class loader. The JNI 382 * spec says we can't load the same library into more than one class loader. 383 * 384 * Returns "true" on success. 385 */ 386bool dvmLoadNativeCode(const char* pathName, Object* classLoader) 387{ 388 const SharedLib* pEntry; 389 void* handle; 390 391 LOGD("Trying to load lib %s %p\n", pathName, classLoader); 392 393 /* 394 * See if we've already loaded it. If we have, and the class loader 395 * matches, return successfully without doing anything. 396 */ 397 pEntry = findSharedLibEntry(pathName); 398 if (pEntry != NULL) { 399 if (pEntry->classLoader != classLoader) { 400 LOGW("Shared lib '%s' already opened by CL %p; can't open in %p\n", 401 pathName, pEntry->classLoader, classLoader); 402 return false; 403 } 404 LOGD("Shared lib '%s' already loaded in same CL %p\n", 405 pathName, classLoader); 406 return true; 407 } 408 409 /* 410 * Open the shared library. Because we're using a full path, the system 411 * doesn't have to search through LD_LIBRARY_PATH. (It may do so to 412 * resolve this library's dependencies though.) 413 * 414 * Failures here are expected when java.library.path has several entries 415 * and we have to hunt for the lib. 416 * 417 * The current android-arm dynamic linker implementation tends to 418 * return "Cannot find library" from dlerror() regardless of the actual 419 * problem. A more useful diagnostic may be sent to stdout/stderr if 420 * linker diagnostics are enabled, but that's not usually visible in 421 * Android apps. Some things to try: 422 * - make sure the library exists on the device 423 * - verify that the right path is being opened (the debug log message 424 * above can help with that) 425 * - check to see if the library is valid (e.g. not zero bytes long) 426 * - check config/prelink-linux-arm.map to ensure that the library 427 * is listed and is not being overrun by the previous entry (if 428 * loading suddenly stops working on a prelinked library, this is 429 * a good one to check) 430 * - write a trivial app that calls sleep() then dlopen(), attach 431 * to it with "strace -p <pid>" while it sleeps, and watch for 432 * attempts to open nonexistent dependent shared libs 433 */ 434 handle = dlopen(pathName, RTLD_LAZY); 435 if (handle == NULL) { 436 LOGI("Unable to dlopen(%s): %s\n", pathName, dlerror()); 437 return false; 438 } 439 440 SharedLib* pNewEntry; 441 pNewEntry = (SharedLib*) malloc(sizeof(SharedLib)); 442 pNewEntry->pathName = strdup(pathName); 443 pNewEntry->handle = handle; 444 pNewEntry->classLoader = classLoader; 445 if (!addSharedLibEntry(pNewEntry)) { 446 LOGI("WOW: we lost a race to add a shared lib (%s %p)\n", 447 pathName, classLoader); 448 /* free up our entry, and just return happy that one exists */ 449 freeSharedLibEntry(pNewEntry); 450 } else { 451 LOGD("Added shared lib %s %p\n", pathName, classLoader); 452 453 void* vonLoad; 454 int version; 455 456 vonLoad = dlsym(handle, "JNI_OnLoad"); 457 if (vonLoad == NULL) { 458 LOGD("No JNI_OnLoad found in %s %p\n", pathName, classLoader); 459 } else { 460 /* 461 * Call JNI_OnLoad. We have to override the current class 462 * loader, which will always be "null" since the stuff at the 463 * top of the stack is around Runtime.loadLibrary(). 464 */ 465 OnLoadFunc func = vonLoad; 466 Thread* self = dvmThreadSelf(); 467 Object* prevOverride = self->classLoaderOverride; 468 469 self->classLoaderOverride = classLoader; 470 dvmChangeStatus(NULL, THREAD_NATIVE); 471 version = (*func)(gDvm.vmList, NULL); 472 dvmChangeStatus(NULL, THREAD_RUNNING); 473 self->classLoaderOverride = prevOverride; 474 475 if (version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && 476 version != JNI_VERSION_1_6) 477 { 478 LOGW("JNI_OnLoad returned bad version (%d) in %s %p\n", 479 version, pathName, classLoader); 480 // TODO: dlclose, remove hash table entry 481 return false; 482 } 483 } 484 } 485 486 return true; 487} 488 489 490/* 491 * =========================================================================== 492 * Signature-based method lookup 493 * =========================================================================== 494 */ 495 496/* 497 * Create the pre-mangled form of the class+method string. 498 * 499 * Returns a newly-allocated string, and sets "*pLen" to the length. 500 */ 501static char* createJniNameString(const char* classDescriptor, 502 const char* methodName, int* pLen) 503{ 504 char* result; 505 size_t descriptorLength = strlen(classDescriptor); 506 507 *pLen = 4 + descriptorLength + strlen(methodName); 508 509 result = malloc(*pLen +1); 510 if (result == NULL) 511 return NULL; 512 513 /* 514 * Add one to classDescriptor to skip the "L", and then replace 515 * the final ";" with a "/" after the sprintf() call. 516 */ 517 sprintf(result, "Java/%s%s", classDescriptor + 1, methodName); 518 result[5 + (descriptorLength - 2)] = '/'; 519 520 return result; 521} 522 523/* 524 * Returns a newly-allocated, mangled copy of "str". 525 * 526 * "str" is a "modified UTF-8" string. We convert it to UTF-16 first to 527 * make life simpler. 528 */ 529static char* mangleString(const char* str, int len) 530{ 531 u2* utf16 = NULL; 532 char* mangle = NULL; 533 int charLen; 534 535 //LOGI("mangling '%s' %d\n", str, len); 536 537 assert(str[len] == '\0'); 538 539 charLen = dvmUtf8Len(str); 540 utf16 = (u2*) malloc(sizeof(u2) * charLen); 541 if (utf16 == NULL) 542 goto bail; 543 544 dvmConvertUtf8ToUtf16(utf16, str); 545 546 /* 547 * Compute the length of the mangled string. 548 */ 549 int i, mangleLen = 0; 550 551 for (i = 0; i < charLen; i++) { 552 u2 ch = utf16[i]; 553 554 if (ch > 127) { 555 mangleLen += 6; 556 } else { 557 switch (ch) { 558 case '_': 559 case ';': 560 case '[': 561 mangleLen += 2; 562 break; 563 default: 564 mangleLen++; 565 break; 566 } 567 } 568 } 569 570 char* cp; 571 572 mangle = (char*) malloc(mangleLen +1); 573 if (mangle == NULL) 574 goto bail; 575 576 for (i = 0, cp = mangle; i < charLen; i++) { 577 u2 ch = utf16[i]; 578 579 if (ch > 127) { 580 sprintf(cp, "_0%04x", ch); 581 cp += 6; 582 } else { 583 switch (ch) { 584 case '_': 585 *cp++ = '_'; 586 *cp++ = '1'; 587 break; 588 case ';': 589 *cp++ = '_'; 590 *cp++ = '2'; 591 break; 592 case '[': 593 *cp++ = '_'; 594 *cp++ = '3'; 595 break; 596 case '/': 597 *cp++ = '_'; 598 break; 599 default: 600 *cp++ = (char) ch; 601 break; 602 } 603 } 604 } 605 606 *cp = '\0'; 607 608bail: 609 free(utf16); 610 return mangle; 611} 612 613/* 614 * Create the mangled form of the parameter types. 615 */ 616static char* createMangledSignature(const DexProto* proto) 617{ 618 DexStringCache sigCache; 619 const char* interim; 620 char* result; 621 622 dexStringCacheInit(&sigCache); 623 interim = dexProtoGetParameterDescriptors(proto, &sigCache); 624 result = mangleString(interim, strlen(interim)); 625 dexStringCacheRelease(&sigCache); 626 627 return result; 628} 629 630/* 631 * (This is a dvmHashForeach callback.) 632 * 633 * Search for a matching method in this shared library. 634 */ 635static int findMethodInLib(void* vlib, void* vmethod) 636{ 637 const SharedLib* pLib = (const SharedLib*) vlib; 638 const Method* meth = (const Method*) vmethod; 639 char* preMangleCM = NULL; 640 char* mangleCM = NULL; 641 char* mangleSig = NULL; 642 char* mangleCMSig = NULL; 643 void* func = NULL; 644 int len; 645 646 if (meth->clazz->classLoader != pLib->classLoader) { 647 LOGD("+++ not scanning '%s' for '%s' (wrong CL)\n", 648 pLib->pathName, meth->name); 649 return 0; 650 } else 651 LOGV("+++ scanning '%s' for '%s'\n", pLib->pathName, meth->name); 652 653 /* 654 * First, we try it without the signature. 655 */ 656 preMangleCM = 657 createJniNameString(meth->clazz->descriptor, meth->name, &len); 658 if (preMangleCM == NULL) 659 goto bail; 660 661 mangleCM = mangleString(preMangleCM, len); 662 if (mangleCM == NULL) 663 goto bail; 664 665 LOGV("+++ calling dlsym(%s)\n", mangleCM); 666 func = dlsym(pLib->handle, mangleCM); 667 if (func == NULL) { 668 mangleSig = 669 createMangledSignature(&meth->prototype); 670 if (mangleSig == NULL) 671 goto bail; 672 673 mangleCMSig = (char*) malloc(strlen(mangleCM) + strlen(mangleSig) +3); 674 if (mangleCMSig == NULL) 675 goto bail; 676 677 sprintf(mangleCMSig, "%s__%s", mangleCM, mangleSig); 678 679 LOGV("+++ calling dlsym(%s)\n", mangleCMSig); 680 func = dlsym(pLib->handle, mangleCMSig); 681 if (func != NULL) { 682 LOGV("Found '%s' with dlsym\n", mangleCMSig); 683 } 684 } else { 685 LOGV("Found '%s' with dlsym\n", mangleCM); 686 } 687 688bail: 689 free(preMangleCM); 690 free(mangleCM); 691 free(mangleSig); 692 free(mangleCMSig); 693 return (int) func; 694} 695 696/* 697 * See if the requested method lives in any of the currently-loaded 698 * shared libraries. We do this by checking each of them for the expected 699 * method signature. 700 */ 701static void* lookupSharedLibMethod(const Method* method) 702{ 703 if (gDvm.nativeLibs == NULL) { 704 LOGE("Unexpected init state: nativeLibs not ready\n"); 705 dvmAbort(); 706 } 707 return (void*) dvmHashForeach(gDvm.nativeLibs, findMethodInLib, 708 (void*) method); 709} 710 711