NDK System.loadLibrary原理实现


1.首先我们在java代码里面写了这样的代码

 static
 {
    System.loadLibrary("native-lib");
 }
 然后会调用到
public static void loadLibrary(String libname) { libname为传递的so的名称
        Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}
之后执行到Runtime中的loadLibrary0方法,第一个参数表示为当前类加载,第二个参数为要加载的so的名称
第二个实例对象,我们可以打印出来,发现这个实例对象为PathClassLoader
Log.d(TAG, " Classloader: "+this.getClassLoader().toString());
10-04 10:53:09.020 4451-4451/com.example.administrator.ndksystemloaddemo D/MainActivity:  Classloader: dalvik.system.PathClassLoader
[DexPathList[[zip file "/data/app/com.example.administrator.ndksystemloaddemo-2/base.apk", zip file "/data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_dependencies_apk.apk", 
 nativeLibraryDirectories=[/data/app/com.example.administrator.ndksystemloaddemo-2/lib/x86, /data/app/com.example.administrator.ndksystemloaddemo-2/base.apk!/lib/x86, 
 /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_dependencies_apk.apk!/lib/x86, /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_0_apk.apk!/lib/x86, 
 /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_1_apk.apk!/lib/x86, /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_2_apk.apk!/lib/x86, 
 /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_3_apk.apk!/lib/x86, /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_4_apk.apk!/lib/x86, 
 /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_5_apk.apk!/lib/x86, /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_6_apk.apk!/lib/x86,
 /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_7_apk.apk!/lib/x86, /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_8_apk.apk!/lib/x86, 
 /data/app/com.example.administrator.ndksystemloaddemo-2/split_lib_slice_9_apk.apk!/lib/x86, /system/lib, /vendor/lib]]]

synchronized void loadLibrary0(ClassLoader loader, String libname) {
        if (libname.indexOf((int)File.separatorChar) != -1) {
            throw new UnsatisfiedLinkError(
    "Directory separator should not appear in library name: " + libname);
        }
        String libraryName = libname;
        if (loader != null) {
            1,首先查找到对应的要加载so的全路径,传递的library
            String filename = loader.findLibrary(libraryName);
            if (filename == null) {
                // It's not necessarily true that the ClassLoader used
                // System.mapLibraryName, but the default setup does, and it's
                // misleading to say we didn't find "libMyLibrary.so" when we
                // actually searched for "liblibMyLibrary.so.so".
                throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                                               System.mapLibraryName(libraryName) + "\"");
            }
            2.加载so
            String error = doLoad(filename, loader);
            if (error != null) {
                throw new UnsatisfiedLinkError(error);
            }
            return;
        }

        //如果classLoad为空,就从系统的路径下查找getLibPaths()
        String filename = System.mapLibraryName(libraryName);
        List<String> candidates = new ArrayList<String>();
        String lastError = null;
        for (String directory : getLibPaths()) {
            String candidate = directory + filename;
            candidates.add(candidate);

            if (IoUtils.canOpenReadOnly(candidate)) {
                String error = doLoad(candidate, loader);
                if (error == null) {
                    return; // We successfully loaded the library. Job done.
                }
                lastError = error;
            }
        }

        if (lastError != null) {
            throw new UnsatisfiedLinkError(lastError);
        }
        throw new UnsatisfiedLinkError("Library " + libraryName + " not found; tried " + candidates);
    }

    getLibPaths() 函数的实现为,System.getProperty("java.library.path");发现也是查找这个,
    Log.d(TAG,"system librarPath :"+System.getProperty("java.library.path"));
    输出结果为D/MainActivity: system librarPath :/system/lib:/vendor/lib

    private String[] getLibPaths() {
        if (mLibPaths == null) {
            synchronized(this) {
                if (mLibPaths == null) {
                    mLibPaths = initLibPaths();
                }
            }
        }
        return mLibPaths;
    }

    private static String[] initLibPaths() {
        String javaLibraryPath = System.getProperty("java.library.path");
        if (javaLibraryPath == null) {
            return EmptyArray.STRING;
        }
        String[] paths = javaLibraryPath.split(":");
        // Add a '/' to the end of each directory so we don't have to do it every time.
        for (int i = 0; i < paths.length; ++i) {
            if (!paths[i].endsWith("/")) {
                paths[i] += "/";
            }
        }
        return paths;
    }

 先看这里面是怎么查找到我们的so的路径的 ,这个loader的实例对象为PathClassLoader
 String filename = loader.findLibrary(libraryName);
 public class PathClassLoader extends BaseDexClassLoader {
    /**
     * Creates a {@code PathClassLoader} that operates on a given list of files
     * and directories. This method is equivalent to calling
     * {@link #PathClassLoader(String, String, ClassLoader)} with a
     * {@code null} value for the second argument (see description there).
     *
     * @param dexPath the list of jar/apk files containing classes and
     * resources, delimited by {@code File.pathSeparator}, which
     * defaults to {@code ":"} on Android
     * @param parent the parent class loader
     */
    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }
}
找到了BaseDexClassLoader中有实现这个的方法
@Override
public String findLibrary(String name) {
    return pathList.findLibrary(name);
}
其中的这个pathList为BaseDexClassLoader中的一个成员变量,并且在构造函数里面完成赋值操作
private final DexPathList pathList;
所以也就找到DexPathList中的findLibrary实现
public String findLibrary(String libraryName) {
        String fileName = System.mapLibraryName(libraryName);
        //nativeLibraryPathElements 这个值代表的是什么?
        for (Element element : nativeLibraryPathElements) {
            String path = element.findNativeLibrary(fileName);
            遍历查找这个path对应的路径,直到找到为止
            if (path != null) {
                return path;
            }
        }

        return null;
    }
nativeLibraryPathElements 这个值代表的是什么?
其中的这个pathList为BaseDexClassLoader中的一个成员变量,并且在构造函数里面完成赋值操作
public BaseDexClassLoader(String dexPath, File optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(parent);
       this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory);
}
this.pathList = new DexPathList(this, dexPath, librarySearchPath, optimizedDirectory);函数的实现为
nativeLibraryPathElements 在这里完成了赋值操作
public DexPathList(ClassLoader definingContext, String dexPath,
            String librarySearchPath, File optimizedDirectory) {

        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,
                                           suppressedExceptions, definingContext);

        // Native libraries may exist in both the system and
        // application library paths, and we use this search order:
        //
        //   1. This class loader's library path for application libraries (librarySearchPath):
        //   1.1. Native library directories
        //   1.2. Path to libraries in apk-files
        //   2. The VM's library path from the system property for system libraries
        //      also known as java.library.path
        //
        // This order was reversed prior to Gingerbread; see http://b/2933456.
        根据我们传递的dex 的path,得到一组path路径
        this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);

        我们想知道System.getProperty("java.library.path")到底是什么,可以打印输出
        这也发现了一个另外一个可以存放so的地方就是system/lib,或者是/vendor/lib下,也是可以直接放so的
        Log.d(TAG,"system librarPath :"+System.getProperty("java.library.path"));
        输出结果为D/MainActivity: system librarPath :/system/lib:/vendor/lib

        this.systemNativeLibraryDirectories =
                splitPaths(System.getProperty("java.library.path"), true);

        然后将上面的路径加进allNativeLibraryDirectories集合中
        List<File> allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);

        给nativeLibraryPathElements赋值 发现他的内容有allNativeLibraryDirectories这个在上面完成了赋值
        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories,
                                                          suppressedExceptions,
                                                          definingContext);

        if (suppressedExceptions.size() > 0) {
            this.dexElementsSuppressedExceptions =
                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
        } else {
            dexElementsSuppressedExceptions = null;
        }
    }

加载so

 String error = doLoad(filename, loader); 函数的实现为:
private String doLoad(String name, ClassLoader loader) {
    中有这样的关键代码
    synchronized (this) {
        return nativeLoad(name, loader, librarySearchPath);
    }
}
可以发现这是一个native方法
// TODO: should be synchronized, but dalvik doesn't support synchronized internal natives.
private static native String nativeLoad(String filename, ClassLoader loader,String librarySearchPath);
对应的c文件的路径为源码路径下的libcore\ojluni\src\main\native\Runtime.c 

这边是采用动态注册的方式,所以没有包括包名
static JNINativeMethod gMethods[] = {
  NATIVE_METHOD(Runtime, freeMemory, "!()J"),
  NATIVE_METHOD(Runtime, totalMemory, "!()J"),
  NATIVE_METHOD(Runtime, maxMemory, "!()J"),
  NATIVE_METHOD(Runtime, gc, "()V"),
  NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
  NATIVE_METHOD(Runtime, nativeLoad,
                "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)"
                    "Ljava/lang/String;"),
};

JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
                   jobject javaLoader, jstring javaLibrarySearchPath)
{
    return JVM_NativeLoad(env, javaFilename, javaLoader, javaLibrarySearchPath);
}
JVM_NativeLoad(env, javaFilename, javaLoader, javaLibrarySearchPath);可以通过全局搜索,最终在art/runtime/openjdkjvm/OpenjdkJvm.cc

JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
                                 jstring javaFilename,
                                 jobject javaLoader,
                                 jstring javaLibrarySearchPath) {
  ScopedUtfChars filename(env, javaFilename);
  if (filename.c_str() == NULL) {
    return NULL;
  }

  std::string error_msg;
  {
    得到JVM指针,这里要注意一般一个应用程序对应一个进程,也就对应的有一个JVM对象,
    而JNIEnv 在每一个线程里面都有一个这样的对象,所以如果将加载过的so的信息保存在JVM里面,这样每一个JNIENV都能得到
    art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();

    bool success = vm->LoadNativeLibrary(env,
                                         filename.c_str(),
                                         javaLoader,
                                         javaLibrarySearchPath,
                                         &error_msg);
    if (success) {
      return nullptr;
    }
  }

  vm->LoadNativeLibrary()函数实现为
  bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                  const std::string& path,
                                  jobject class_loader,
                                  jstring library_path,
                                  std::string* error_msg) {
  error_msg->clear();

  // See if we've already loaded this library.  If we have, and the class loader
  // matches, return successfully without doing anything.
  // TODO: for better results we should canonicalize the pathname (or even compare
  // inodes). This implementation is fine if everybody is using System.loadLibrary.
  //定义一个ShareLibrary* 指针
  SharedLibrary* library;
  Thread* self = Thread::Current();
  {
    // TODO: move the locking (and more of this logic) into Libraries.
    MutexLock mu(self, *Locks::jni_libraries_lock_);
    libraries_ 为一个成员变量JavaVMExt,std::unique_ptr<Libraries> libraries_;这里为一个智能指针
    并且在构造函数中初始了,libraries_(new Libraries), 
    这里的作用就是,防止存储的是shareLibrary 指针对象,这里先根据path去判断当前是否已经有加载过这个so
    library = libraries_->Get(path);
  }
  void* class_loader_allocator = nullptr;
  {
    ScopedObjectAccess soa(env);
    // As the incoming class loader is reachable/alive during the call of this function,
    // it's okay to decode it without worrying about unexpectedly marking it alive.
    mirror::ClassLoader* loader = soa.Decode<mirror::ClassLoader*>(class_loader);

    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
    if (class_linker->IsBootClassLoader(soa, loader)) {
      loader = nullptr;
      class_loader = nullptr;
    }

    class_loader_allocator = class_linker->GetAllocatorForClassLoader(loader);
    CHECK(class_loader_allocator != nullptr);
  }

  如果library 不为空
  if (library != nullptr) {
    // Use the allocator pointers for class loader equality to avoid unnecessary weak root decode.
    if (library->GetClassLoaderAllocator() != class_loader_allocator) {
      // The library will be associated with class_loader. The JNI
      // spec says we can't load the same library into more than one
      // class loader.
      StringAppendF(error_msg, "Shared library \"%s\" already opened by "
          "ClassLoader %p; can't open in ClassLoader %p",
          path.c_str(), library->GetClassLoader(), class_loader);
      LOG(WARNING) << error_msg;
      return false;
    }
    这就代表已经加载过这个so了,就没有必要重新的加载了,下面直接返回true,表示记载成功
    VLOG(jni) << "[Shared library \"" << path << "\" already loaded in "
              << " ClassLoader " << class_loader << "]";
    if (!library->CheckOnLoadResult()) {
      StringAppendF(error_msg, "JNI_OnLoad failed on a previous attempt "
          "to load \"%s\"", path.c_str());
      return false;
    }
    return true;
  }

  这里library 为空,第一次加载so
  // Open the shared library.  Because we're using a full path, the system
  // doesn't have to search through LD_LIBRARY_PATH.  (It may do so to
  // resolve this library's dependencies though.)

  // Failures here are expected when java.library.path has several entries
  // and we have to hunt for the lib.

  // Below we dlopen but there is no paired dlclose, this would be necessary if we supported
  // class unloading. Libraries will only be unloaded when the reference count (incremented by
  // dlopen) becomes zero from dlclose.

  Locks::mutator_lock_->AssertNotHeld(self);
  得到要加载的so的全路径
  const char* path_str = path.empty() ? nullptr : path.c_str();
  加载这个so,并且得到这个加载so的返回值,是否成功
  void* handle = android::OpenNativeLibrary(env,
                                            runtime_->GetTargetSdkVersion(),
                                            path_str,
                                            class_loader,
                                            library_path);

  bool needs_native_bridge = false;
  if (handle == nullptr) {
    if (android::NativeBridgeIsSupported(path_str)) {
      handle = android::NativeBridgeLoadLibrary(path_str, RTLD_NOW);
      needs_native_bridge = true;
    }
  }

  VLOG(jni) << "[Call to dlopen(\"" << path << "\", RTLD_NOW) returned " << handle << "]";

  如果handle为nullptr标识加载so失败
  if (handle == nullptr) {
    *error_msg = dlerror();
    VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
    return false;
  }
  下面就表示已经成功打开了这个so
  判断是否有异常产生
  if (env->ExceptionCheck() == JNI_TRUE) {
    LOG(ERROR) << "Unexpected exception:";
    env->ExceptionDescribe();
    env->ExceptionClear();
  }
  // Create a new entry.
  // TODO: move the locking (and more of this logic) into Libraries.

  bool created_library = false;
  {
    // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
    智能指针,创建一个SharedLibrary指针对象new_library,将成功打开的结果重新的封装成一个library指针对象
    std::unique_ptr<SharedLibrary> new_library(
        new SharedLibrary(env, self, path, handle, class_loader, class_loader_allocator));
    MutexLock mu(self, *Locks::jni_libraries_lock_);
    library = libraries_->Get(path);
    if (library == nullptr) {  // We won race to get libraries_lock.
      library = new_library.release();
      将library添加到了libraries_里面,以后再次访问这个so就不用重新的打开了
      libraries_->Put(path, library);
      created_library = true;
    }
  }
  if (!created_library) {
    LOG(INFO) << "WOW: we lost a race to add shared library: "
        << "\"" << path << "\" ClassLoader=" << class_loader;
    return library->CheckOnLoadResult();
  }
  VLOG(jni) << "[Added shared library \"" << path << "\" for ClassLoader " << class_loader << "]";

  bool was_successful = false;
  定义了一个void *类型的指针
  void* sym;
  if (needs_native_bridge) {
    library->SetNeedsNativeBridge();
  }
  再成功加载了so之后,找到JNI_OnLoad函数
  sym = library->FindSymbol("JNI_OnLoad", nullptr);
  if (sym == nullptr) {
    VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
    was_successful = true;
  } else {
    // Call JNI_OnLoad.  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.)
    ScopedLocalRef<jobject> old_class_loader(env, env->NewLocalRef(self->GetClassLoaderOverride()));
    self->SetClassLoaderOverride(class_loader);

    VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
    typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
    将这个void* 类型的变量强制的转换成一个JNI_OnLoadFn 类型的指针函数指针
    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
    这里执行了这个函数指针,这也就是为什么Jni头文件里面有这样的JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
    下面就是调用了这个函数,将结果传递过来,这里只会在加载so第一次调用
    int version = (*jni_on_load)(this, nullptr);

    if (runtime_->GetTargetSdkVersion() != 0 && runtime_->GetTargetSdkVersion() <= 21) {
      fault_manager.EnsureArtActionInFrontOfSignalChain();
    }

    self->SetClassLoaderOverride(old_class_loader.get());


    当加载so成功之后,还要检查jni的版本号IsBadJniVersion(version) return version != JNI_VERSION_1_2 && version != JNI_VERSION_1_4 && version != JNI_VERSION_1_6;

    if (version == JNI_ERR) {
      StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
    } else if (IsBadJniVersion(version)) {
      StringAppendF(error_msg, "Bad JNI version returned from JNI_OnLoad in \"%s\": %d",
                    path.c_str(), version);
      // 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 JNI_OnLoad 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.
    } else {
      was_successful = true;
    }
    VLOG(jni) << "[Returned " << (was_successful ? "successfully" : "failure")
              << " from JNI_OnLoad in \"" << path << "\"]";
  }
  标识已经加载成功
  library->SetResult(was_successful);
  return was_successful;
}

android::OpenNativeLibrary()函数的实现为: 路径为native_loader.cpp文件中
void* OpenNativeLibrary(JNIEnv* env,
                        int32_t target_sdk_version,
                        const char* path,
                        jobject class_loader,
                        jstring library_path) {
#if defined(__ANDROID__)
  UNUSED(target_sdk_version);
  if (class_loader == nullptr) {
    return dlopen(path, RTLD_NOW);
  }

  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
  android_namespace_t* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);

  if (ns == nullptr) {
    // This is the case where the classloader was not created by ApplicationLoaders
    // In this case we create an isolated not-shared namespace for it.
    ns = g_namespaces->Create(env, class_loader, false, library_path, nullptr);
    if (ns == nullptr) {
      return nullptr;
    }
  }

  android_dlextinfo extinfo;
  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
  extinfo.library_namespace = ns;

  return android_dlopen_ext(path, RTLD_NOW, &extinfo);
#else
  UNUSED(env, target_sdk_version, class_loader, library_path);
  return dlopen(path, RTLD_NOW);
#endif
}

执行 android_dlopen_ext(path, RTLD_NOW, &extinfo); 路径为dlfcn.cpp
void* android_dlopen_ext(const char* filename, int flags, const android_dlextinfo* extinfo) {
  void* caller_addr = __builtin_return_address(0);
  return dlopen_ext(filename, flags, extinfo, caller_addr);
}

static void* dlopen_ext(const char* filename, int flags,
                        const android_dlextinfo* extinfo, void* caller_addr) {
  ScopedPthreadMutexLocker locker(&g_dl_mutex);
  void* result = do_dlopen(filename, flags, extinfo, caller_addr);
  if (result == nullptr) {
    __bionic_format_dlerror("dlopen failed", linker_get_error_buffer());
    return nullptr;
  }
  return result;
}
do_dlopen(filename, flags, extinfo, caller_addr);函数实现为 路径为linker.cpp文件
void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo,
                  void* caller_addr) {
  .......
  ProtectedDataGuard guard;
  soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
  if (si != nullptr) {
    si->call_constructors();
    return si->to_handle();
  }

  return nullptr;
}
si->to_handle()函数实现为

文章作者: AheadSnail
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 AheadSnail !
评论
  目录