android 动态库loadLibrary源码跟踪

    说到JNI就不得不提到so,关于so的原理我就不赘述了。我们加载so的时候经常会遇到一些问题,现在我就so的加载过程做一个简单的说明

    我们使用如下代码System.loadLibrary("hello-jni");来加载libhello-jni.so库,有朋友问这个库应该放在什么地方,其实这个库可以放在两个地方一个是系统是/system/lib下面,另外一个是/data/data/com.example.hellojni/lib下面,通常情况下前者是没有写入权限的。好了,现在我们分析android是如何加载so的

    1,我们跟踪到./libcore/luni-kernel/src/main/java/java/lang/System.java中的loadLibrary他调用了

    Runtime.getRuntime().loadLibrary(libName, VMStack.getCallingClassLoader());

    2,./libcore/luni-kernel/src/main/java/java/lang/Runtime.java这个类,我们看到调用了nativeLoad方法,我们继续跟踪下去

    3,javalangRuntime.c中我们找到了这个 nativeLoad方法,对应的DalvikjavalangRuntimenativeLoad方法,我们继续跟踪

    4,bool dvmLoadNativeCode(const char* pathName, Object* classLoader)

    {

    SharedLib* pEntry;

    void* handle;

    bool verbose;





    /* reduce noise by not chattering about system libraries */

    //这里我们先去比较是否是加载system/lib下面的so

    verbose = strncmp(pathName, "/system", sizeof("/system")-1) != 0;





    if (verbose)

    LOGD("Trying to load lib %s %pn", pathName, classLoader);





    /*

    * See if we've already loaded it. If we have, and the class loader

    * matches, return successfully without doing anything.

    */

    //查看当前so是否已经加载,这里提醒我们如果更新了so之后,必须重新启动当前虚拟机,否则不起作用。

    pEntry = findSharedLibEntry(pathName);

    if (pEntry != NULL) {

    if (pEntry->classLoader != classLoader) {

    LOGW("Shared lib '%s' already opened by CL %p; can't open in %pn",

    pathName, pEntry->classLoader, classLoader);

    return false;

    }

    if (verbose) {

    LOGD("Shared lib '%s' already loaded in same CL %pn",

    pathName, classLoader);

    }

    if (!checkOnLoadResult(pEntry))

    return false;

    return true;

    }







    Thread* self = dvmThreadSelf();

    int oldStatus = dvmChangeStatus(self, THREADVMWAIT);

    handle = dlopen(pathName, RTLDLAZY);

    dvmChangeStatus(self, oldStatus);





    if (handle == NULL) {

    LOGI("Unable to dlopen(%s): %sn", pathName, dlerror());

    return false;

    }





    /* create a new entry /

    SharedLib pNewEntry;

    pNewEntry = (SharedLib*) calloc(1, sizeof(SharedLib));

    pNewEntry->pathName = strdup(pathName);

    pNewEntry->handle = handle;

    pNewEntry->classLoader = classLoader;

    dvmInitMutex(&pNewEntry->onLoadLock);

    pthread_cond_init(&pNewEntry->onLoadCond, NULL);

    pNewEntry->onLoadThreadId = self->threadId;





    /* try to add it to the list /

    SharedLib pActualEntry = addSharedLibEntry(pNewEntry);





    if (pNewEntry != pActualEntry) {

    LOGI("WOW: we lost a race to add a shared lib (%s CL=%p)n",

    pathName, classLoader);

    freeSharedLibEntry(pNewEntry);

    return checkOnLoadResult(pActualEntry);

    } else {

    if (verbose)

    LOGD("Added shared lib %s %pn", pathName, classLoader);





    bool result = true;

    void* vonLoad;

    int version;

    //此处会坚持JNIOnLoad方法,所以ndk提供的hello-jni里面应该不算是标准的android jni方式

    vonLoad = dlsym(handle, "JNIOnLoad");

    if (vonLoad == NULL) {

    LOGD("No JNIOnLoad found in %s %p, skipping initn",

    pathName, classLoader);

    } else {

    /*

    * Call JNIOnLoad. We have to override the current class

    * loader, which will always be "null" since the stuff at the

    * top of the stack is around Runtime.loadLibrary(). (See

    * the comments in the JNI FindClass function.)

    /

    OnLoadFunc func = vonLoad;

    Object prevOverride = self->classLoaderOverride;





    self->classLoaderOverride = classLoader;

    oldStatus = dvmChangeStatus(self, THREADNATIVE);

    LOGV("+++ calling JNIOnLoad(%s)n", pathName);

    version = (*func)(gDvm.vmList, NULL);

    dvmChangeStatus(self, oldStatus);

    self->classLoaderOverride = prevOverride;





    //此处我们坚持JNIOnLoad返回的so版本号

    if (version != JNIVERSION12 && version != JNIVERSION14 &&

    version != JNIVERSION16)

    {

    LOGW("JNIOnLoad returned bad version (%d) in %s %pn",

    version, pathName, classLoader);

    /*

    * It's unwise to call dlclose() here, but we can mark it

    * as bad and ensure that future load attempts will fail.

    *

    * We don't know how far JNIOnLoad got, so there could

    * be some partially-initialized stuff accessible through

    * newly-registered native method calls. We could try to

    * unregister them, but that doesn't seem worthwhile.

    */

    result = false;

    } else {

    LOGV("+++ finished JNI_OnLoad %sn", pathName);

    }

    }





    if (result)

    pNewEntry->onLoadResult = kOnLoadOkay;

    else

    pNewEntry->onLoadResult = kOnLoadFailed;





    pNewEntry->onLoadThreadId = 0;





    /*

    * Broadcast a wakeup to anybody sleeping on the condition variable.

    */

    dvmLockMutex(&pNewEntry->onLoadLock);

    pthread_cond_broadcast(&pNewEntry->onLoadCond);

    dvmUnlockMutex(&pNewEntry->onLoadLock);

    return result;

    }

    }





    自此,我们完全分析了android虚拟机是如何加载so的,另外给大家的建议是,最好不要把so随便放在system/lib下面,因为据我的经验虚拟机优先加载这个下面的库,我没有从理论上做过分析,另外这个目录下面基本上没有写权限,所以我们自己开发的程序如果用到动态库,还是放在自己的目录下面比较好。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值