SystemLoadLibrary

图解

graph LR
findLibrary-->mapLibraryName
findLibrary-->NativeLibraryElement.findNativeLibrary

nativeload-->JavaVMExt::LoadNativeLibrary-->checkCache("library = libraries_->Get(path);")
JavaVMExt::LoadNativeLibrary-->android_dlopen_ext
JavaVMExt::LoadNativeLibrary-->dlsym-->JNI_OnLoad-->RegisterNatives-->ArtMethod::setEntryProintFromJni

动态链接器linker过程参考

Runtime.getRuntime().load

    /**
     * Loads the native library specified by the filename argument.  The filename
     * argument must be an absolute path name.
     * (for example
     * <code>Runtime.getRuntime().load("/home/avh/lib/libX11.so");</code>).
     */
public void load(String filename) {
    load0(VMStack.getStackClass1(), filename);
}
synchronized void load0(Class<?> fromClass, String filename) {
    if (!(new File(filename).isAbsolute())) {
        throw new UnsatisfiedLinkError("Expecting an absolute path of the library: " + filename);
    }
    String error = nativeLoad(filename, fromClass.getClassLoader());
    if (error != null) {
        throw new UnsatisfiedLinkError(error);
    }
}

java/lang/System.java

System.loadLibrary

    /**
     * Loads the native library specified by the <code>libname</code>
     * argument.  The <code>libname</code> argument must not contain any platform
     * specific prefix, file extension or path. If a native library
     * called <code>libname</code> is statically linked with the VM, then the
     * JNI_OnLoad_<code>libname</code> function exported by the library is invoked.
     * See the JNI Specification for more details.
     *
     * Otherwise, the libname argument is loaded from a system library
     * location and mapped to a native library image in an implementation-
     * dependent manner.
     **/
public static void loadLibrary(String libname) {
    Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}

java/lang/Runtime.java

synchronized void loadLibrary0(ClassLoader loader, String libname) {
        String libraryName = libname;
        if (loader != null) {
            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) + "\"");
            }
            String error = nativeLoad(filename, loader);
            if (error != null) {
                throw new UnsatisfiedLinkError(error);
            }
            return;
        }
        String filename = System.mapLibraryName(libraryName);
        List<String> candidates = new ArrayList<String>();
        String lastError = null;
        for (String directory : getLibPaths()) {//getLibPaths return /system/lib/
            String candidate = directory + filename;
            candidates.add(candidate);

            if (IoUtils.canOpenReadOnly(candidate)) {
                String error = nativeLoad(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);
}

BaseDexClassLoader.findLibrary

@Override
public String findLibrary(String name) {
    return pathList.findLibrary(name);
}

DexPathList.findLibrary

/**
 * Finds the named native code library on any of the library
 * directories pointed at by this instance. This will find the
 * one in the earliest listed directory, ignoring any that are not
 * readable regular files.
 *
 * @return the complete path to the library or {@code null} if no
 * library was found
 */
public String findLibrary(String libraryName) {
    String fileName = System.mapLibraryName(libraryName);//xxx-->libxxx.so

    //[0] --> directory "/data/app/com.example.myapplication-sj3RwTZhmeZrfbRZIori-w==/lib/x86"
    //[1] --> zip file "/data/app/com.example.myapplication-sj3RwTZhmeZrfbRZIori-w==/base.apk", dir "lib/x86"
    //[2] --> directory "/system/lib";
    //[3] --> directory "/vendor/lib"
    for (NativeLibraryElement element : nativeLibraryPathElements) {
        String path = element.findNativeLibrary(fileName);
        if (path != null) {
            return path;
        }
    }
    return null;
}

NativeLibraryElement.findNativeLibrary

/**
 * Element of the native library path
 */
/*package*/ static class NativeLibraryElement {
         /** A file denoting a directory or zip file. */
        private final File path;

        /** If path denotes a zip file, this denotes a base path inside the zip.*/
        private final String zipDir;

        private ClassPathURLStreamHandler urlHandler;
        private boolean initialized;
  
  public String findNativeLibrary(String name) {
    maybeInit();

    if (zipDir == null) {
        String entryPath = new File(path, name).getPath();// /data/app/com.example.myapplication-sj3RwTZhmeZrfbRZIori-w==/lib/x86/libxhcore.so
        if (IoUtils.canOpenReadOnly(entryPath)) {
            return entryPath;
        }
    } else if (urlHandler != null) {
        // Having a urlHandler means the element has a zip file.
        // In this case Android supports loading the library iff
        // it is stored in the zip uncompressed.
        String entryName = zipDir + '/' + name;
        if (urlHandler.isEntryStored(entryName)) {
          return path.getPath() + zipSeparator + entryName;
        }
    }

    return null;
  }
}
//IoUtils
/**
 * Do not use. This is for System.loadLibrary use only.
 *
 * Checks whether {@code path} can be opened read-only. Similar to File.exists, but doesn't
 * require read permission on the parent, so it'll work in more cases, and allow you to
 * remove read permission from more directories. Everyone else should just open(2) and then
 * use the fd, but the loadLibrary API is broken by its need to ask ClassLoaders where to
 * find a .so rather than just calling dlopen(3).
 */
public static boolean canOpenReadOnly(String path) {
    try {
        // Use open(2) rather than stat(2) so we require fewer permissions. http://b/6485312.
        FileDescriptor fd = Libcore.os.open(path, O_RDONLY, 0);
        Libcore.os.close(fd);
        return true;
    } catch (ErrnoException errnoException) {
        return false;
    }
}

nativeLoad

private static native String nativeLoad(String filename, ClassLoader loader);

libcore/ojluni/src/main/native/Runtime.c

Runtime_nativeLoad

JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
                   jobject javaLoader)
{
    return JVM_NativeLoad(env, javaFilename, javaLoader);
}

art/openjdkjvm/OpenjdkJvm.cc

JVM_NativeLoad

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

  std::string error_msg;
  {
    art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
    bool success = vm->LoadNativeLibrary(env,
                                         filename.c_str(),
                                         javaLoader,
                                         &error_msg);
    if (success) {
      return nullptr;
    }
  }

  // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
  env->ExceptionClear();
  return env->NewStringUTF(error_msg.c_str());
}

art/runtime/java_vm_ext.cc

JavaVMExt::LoadNativeLibrary

bool JavaVMExt::LoadNativeLibrary(JNIEnv* env,
                                  const std::string& path,
                                  jobject class_loader,
                                  std::string* error_msg) {
    
  // See if we've already loaded this library.  If we have, and the class loader
  // matches, return successfully without doing anything.
  SharedLibrary* library;
  Thread* self = Thread::Current();
  {
    MutexLock mu(self, *Locks::jni_libraries_lock_);
    library = libraries_->Get(path);//libraries_ 中存储了所有已经加载了libraries的map,其中key是so全路径string,value是SharedLibrary*
  }
  if (library != nullptr) {
      return xxx
  }
  // Retrieve the library path from the classloader, if necessary.
  ScopedLocalRef<jstring> library_path(env, GetLibrarySearchPath(env, class_loader));

  Locks::mutator_lock_->AssertNotHeld(self);
  const char* path_str = path.empty() ? nullptr : path.c_str();
  bool needs_native_bridge = false;
  void* handle = android::OpenNativeLibrary(env,
                                            runtime_->GetTargetSdkVersion(),
                                            path_str,
                                            class_loader,
                                            library_path.get(),
                                            &needs_native_bridge,
                                            error_msg);
    
  if (handle == nullptr) {
    VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
    return false;
  }
    
  bool created_library = false;
  {
    // Create SharedLibrary ahead of taking the libraries lock to maintain lock ordering.
    std::unique_ptr<SharedLibrary> new_library(
        new SharedLibrary(env,
                          self,
                          path,
                          handle,
                          needs_native_bridge,
                          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();
      libraries_->Put(path, library);//path为so的全路径名
      created_library = true;
    }
  }
    
  bool was_successful = false;
  void* sym = library->FindSymbol("JNI_OnLoad", nullptr);
  if (sym == nullptr) {
    VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
    was_successful = true;
  } else {
    ......
    VLOG(jni) << "[Calling JNI_OnLoad in \"" << path << "\"]";
    typedef int (*JNI_OnLoadFn)(JavaVM*, void*);
    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
    int version = (*jni_on_load)(this, nullptr);//调用自定义.cpp的JNI_OnLoad method

    if (version == JNI_ERR) {
      StringAppendF(error_msg, "JNI_ERR returned from JNI_OnLoad in \"%s\"", path.c_str());
    } else if (JavaVMExt::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;
    }
      
  library->SetResult(was_successful);
  return was_successful;
}

system/core/libnativeloader/native_loader.cpp

OpenNativeLibrary

void* OpenNativeLibrary(JNIEnv* env,
                        int32_t target_sdk_version,
                        const char* path,
                        jobject class_loader,
                        jstring library_path,
                        bool* needs_native_bridge,
                        std::string* error_msg) {
#if defined(__ANDROID__)
  UNUSED(target_sdk_version);
  if (class_loader == nullptr) {
    *needs_native_bridge = false;
    return dlopen(path, RTLD_NOW);
  }
    
  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
  NativeLoaderNamespace ns;

  if (!g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
    // This is the case where the classloader was not created by ApplicationLoaders
    // In this case we create an isolated not-shared namespace for it.
    if (!g_namespaces->Create(env,
                              target_sdk_version,
                              class_loader,
                              false /* is_shared */,
                              false /* is_for_vendor */,
                              library_path,
                              nullptr,
                              &ns,
                              error_msg)) {
      return nullptr;
    }
  }
    
if (ns.is_android_namespace()) {
  android_dlextinfo extinfo;
  extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE;
  extinfo.library_namespace = ns.get_android_ns();

  void* handle = android_dlopen_ext(path, RTLD_NOW, &extinfo);
  if (handle == nullptr) {
    *error_msg = dlerror();
  }
  *needs_native_bridge = false;
  return handle;
} else {
  void* handle = NativeBridgeLoadLibraryExt(path, RTLD_NOW, ns.get_native_bridge_ns());
  if (handle == nullptr) {
    *error_msg = NativeBridgeGetError();
  }
  *needs_native_bridge = true;
  return handle;
}
bool FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader, NativeLoaderNamespace* ns) {
  auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
              [&](const std::pair<jweak, NativeLoaderNamespace>& value) {
                return env->IsSameObject(value.first, class_loader);
              });
  if (it != namespaces_.end()) {
    if (ns != nullptr) {
      *ns = it->second;
    }

    return true;
  }
  return false;
}

bool is_android_namespace() const {
  return native_bridge_ns_ == nullptr;
}

external/libcxx/include/algorithm

// find_if
template <class _InputIterator, class _Predicate>
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
_InputIterator
find_if(_InputIterator __first, _InputIterator __last, _Predicate __pred)
{
    for (; __first != __last; ++__first)
        if (__pred(*__first))
            break;
    return __first;
}

bionic/libdl/libdl.cpp

android_dlopen_ext
__attribute__((__weak__))
void* android_dlopen_ext(const char* filename, int flag, const android_dlextinfo* extinfo) {
  const void* caller_addr = __builtin_return_address(0);
  return __loader_android_dlopen_ext(filename, flag, extinfo, caller_addr);
}

FindSymbol

// No mutator lock since dlsym may block for a while if another thread is doing dlopen.
void* FindSymbol(const std::string& symbol_name, const char* shorty = nullptr)
    REQUIRES(!Locks::mutator_lock_) {
  return NeedsNativeBridge()
      ? FindSymbolWithNativeBridge(symbol_name.c_str(), shorty)
      : FindSymbolWithoutNativeBridge(symbol_name.c_str());
}
// No mutator lock since dlsym may block for a while if another thread is doing dlopen.
void* FindSymbolWithoutNativeBridge(const std::string& symbol_name)
  REQUIRES(!Locks::mutator_lock_) {
  CHECK(!NeedsNativeBridge());

  return dlsym(handle_, symbol_name.c_str());
}

bionic/libdl/libdl.cpp

dlsym
__attribute__((__weak__))
void* dlsym(void* handle, const char* symbol) {
  const void* caller_addr = __builtin_return_address(0);
  return __loader_dlsym(handle, symbol, caller_addr);
}

JNI_OnLoad

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
}

art/runtime/jni_internal.cc

RegisterNatives

  static jint RegisterNatives(JNIEnv* env,
                              jclass java_class,
                              const JNINativeMethod* methods,
                              jint method_count) {
    ScopedObjectAccess soa(env);
    StackHandleScope<1> hs(soa.Self());
    Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class));
    for (jint i = 0; i < method_count; ++i) {
      const char* name = methods[i].name;
      const char* sig = methods[i].signature;
      const void* fnPtr = methods[i].fnPtr;
      bool is_fast = false;
      // Notes about fast JNI calls:
      //
      // On a normal JNI call, the calling thread usually transitions
      // from the kRunnable state to the kNative state. But if the
      // called native function needs to access any Java object, it
      // will have to transition back to the kRunnable state.
      //
      // There is a cost to this double transition. For a JNI call
      // that should be quick, this cost may dominate the call cost.
      //
      // On a fast JNI call, the calling thread avoids this double
      // transition by not transitioning from kRunnable to kNative and
      // stays in the kRunnable state.
      //
      // There are risks to using a fast JNI call because it can delay
      // a response to a thread suspension request which is typically
      // used for a GC root scanning, etc. If a fast JNI call takes a
      // long time, it could cause longer thread suspension latency
      // and GC pauses.
      //
      // Thus, fast JNI should be used with care. It should be used
      // for a JNI call that takes a short amount of time (eg. no
      // long-running loop) and does not block (eg. no locks, I/O,
      // etc.)
      //
      // A '!' prefix in the signature in the JNINativeMethod
      // indicates that it's a fast JNI call and the runtime omits the
      // thread state transition from kRunnable to kNative at the
      // entry.
      if (*sig == '!') {
        is_fast = true;
        ++sig;
      }    
       // Note: the right order is to try to find the method locally
      // first, either as a direct or a virtual method. Then move to
      // the parent.
      ArtMethod* m = nullptr;
      for (ObjPtr<mirror::Class> current_class = c.Get();
           current_class != nullptr;
           current_class = current_class->GetSuperClass()) {
        // Search first only comparing methods which are native.
        m = FindMethod<true>(current_class.Ptr(), name, sig);
        if (m != nullptr) {
          break;
        }

        // Search again comparing to all methods, to find non-native methods that match.
        m = FindMethod<false>(current_class.Ptr(), name, sig);
        if (m != nullptr) {
          break;
        }
      }
      const void* final_function_ptr = m->RegisterNative(fnPtr);
    }
  }

art_method.cc

ArtMethod::RegisterNative
const void* ArtMethod::RegisterNative(const void* native_method) {
  CHECK(IsNative()) << PrettyMethod();
  CHECK(native_method != nullptr) << PrettyMethod();
  void* new_native_method = nullptr;
  Runtime::Current()->GetRuntimeCallbacks()->RegisterNativeMethod(this,
                                                                  native_method,
                                                                  /*out*/&new_native_method);
  SetEntryPointFromJni(new_native_method);
  return new_native_method;
}
SetEntryPointFromJni
void SetEntryPointFromJni(const void* entrypoint) {
  DCHECK(IsNative());
  SetEntryPointFromJniPtrSize(entrypoint, kRuntimePointerSize);
}

ALWAYS_INLINE void SetEntryPointFromJniPtrSize(const void* entrypoint, PointerSize pointer_size) {
    SetDataPtrSize(entrypoint, pointer_size);
}

ALWAYS_INLINE void SetDataPtrSize(const void* data, PointerSize pointer_size) {
    DCHECK(IsImagePointerSize(pointer_size));
    SetNativePointer(DataOffset(pointer_size), data, pointer_size);//DataOffset,对应ArtMethod的data_字段
}
static MemberOffset EntryPointFromJniOffset(PointerSize pointer_size) {
    return DataOffset(pointer_size);
}

static MemberOffset DataOffset(PointerSize pointer_size) {
    return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
        PtrSizedFields, data_) / sizeof(void*) * static_cast<size_t>(pointer_size));
}
  static MemberOffset EntryPointFromQuickCompiledCodeOffset(PointerSize pointer_size) {
    return MemberOffset(PtrSizedFieldsOffset(pointer_size) + OFFSETOF_MEMBER(
        PtrSizedFields, entry_point_from_quick_compiled_code_) / sizeof(void*)//对应ArtMethod的entry_point_from_quick_compiled_code_字段
            * static_cast<size_t>(pointer_size));
  }

art/runtime/runtime_callbacks.cc

RuntimeCallbacks::RegisterNativeMethod
void RuntimeCallbacks::RegisterNativeMethod(ArtMethod* method,
                                            const void* in_cur_method,
                                            /*out*/void** new_method) {
  void* cur_method = const_cast<void*>(in_cur_method);
  *new_method = cur_method;
  for (MethodCallback* cb : method_callbacks_) {
    cb->RegisterNativeMethod(method, cur_method, new_method);
    if (*new_method != nullptr) {
      cur_method = *new_method;
    }
  }
}

art/openjdkjvmti/ti_method.cc

ti_method.cc.RegisterNativeMethod
void RegisterNativeMethod(art::ArtMethod* method,
                          const void* cur_method,
                          /*out*/void** new_method)
    OVERRIDE REQUIRES_SHARED(art::Locks::mutator_lock_) {
  if (event_handler->IsEventEnabledAnywhere(ArtJvmtiEvent::kNativeMethodBind)) {
      ......
  }
}

参考

【Android】动态链接库so的加载原理

https://github.com/KeepSafe/ReLinker

https://github.com/facebook/SoLoader

java.lang.UnsatisfiedLinkError 的解决办法

其他

bionic/linker/linker.cpp

linker.cpp

load_library

static bool load_library(android_namespace_t* ns,
                         LoadTask* task,
                         LoadTaskList* load_tasks,
                         int rtld_flags,
                         const std::string& realpath,
                         bool search_linked_namespaces) {