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