【Android9.0.0_r61 so加载】

一: Android so加载整体流程

System.loadLibrary() → \rightarrow nativeLoad (Runtime_NativeLoad) → \rightarrow JVM_NativeLoad → \rightarrow vm → \rightarrow LoadNativeLibrary

  1. dlopen
  2. dlsym找到JNI_Onload符号执行

首先我们先看一张图,这张图就是详细的Android so加载整体流程。
在这里插入图片描述
孔子曾说:工欲善其事必先利其器,所以我们一起来分析下so文件是怎么加载的吧

二:Android系统源码链接

Android系统源码在线查看网址:

但是推荐使用第一个AOSP,服务器在国内。
第二个网址并没有收录最新的安卓源码,并且网站在国外所以访问很慢。
AOSPXRef包含的安卓版本
AndroidXRef

三:JAVA层

System.loadLibrary() → \rightarrow nativeLoad (Runtime_NativeLoad)

System.loadLibrary("libxxx.so")

JAVA层加载library代码位于

/libcore/ojluni/src/main/java/java/lang/Runtime.java

1. loadLibrary

@CallerSensitive
public void loadLibrary(String libname) {
    loadLibrary0(VMStack.getCallingClassLoader(), libname);
}

2. loadLibrary0

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) {
        // 通过findLibrary寻找该lib
        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) + "\"");
        }
        // 找到了就使用nativeLoad去加载lib
        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()) {
        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);
}

3. nativeLoad

从其函数签名来看,nativeLoad是一个native函数

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

四:Native层

Native层代码位于

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

1. Runtime_nativeLoad

// 导出JNI函数
JNIEXPORT jstring JNICALL
Runtime_nativeLoad(JNIEnv* env, jclass ignored, jstring javaFilename,
                   jobject javaLoader, jstring javaLibrarySearchPath)
{
	// 调用了JVM_NativeLoad函数
    return JVM_NativeLoad(env, javaFilename, javaLoader, javaLibrarySearchPath);
}

static JNINativeMethod gMethods[] = {
  FAST_NATIVE_METHOD(Runtime, freeMemory, "()J"),
  FAST_NATIVE_METHOD(Runtime, totalMemory, "()J"),
  FAST_NATIVE_METHOD(Runtime, maxMemory, "()J"),
  NATIVE_METHOD(Runtime, gc, "()V"),
  NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
  // 动态加载,注册nativeLoad
  NATIVE_METHOD(Runtime, nativeLoad,
                "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)"
                    "Ljava/lang/String;"),
};

2. JVM_NativeLoad

JVM_NativeLoad声明于

/libcore/ojluni/src/main/native/jvm.h

JVM_NativeLoad实现于

/art/runtime/openjdkjvm/OpenjdkJvm.cc

OpenjdkJvm.cc文件头注释显示:这是对jvm.h中的接口的实现

This file implements interfaces from the file jvm.h.
This implementationis licensed under the same terms as the file jvm.h.

在JVM_NativeLoad中使用了vm → \rightarrow LoadNativeLibrary来对so文件进行加载。
源码如下

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;
  {
    art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
    // 使用vm的LoadNativeLibrary来进行加载
    bool success = vm->LoadNativeLibrary(env,
                                         filename.c_str(),
                                         javaLoader,
                                         javaLibrarySearchPath,
                                         &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());
}

3. vm->LoadNativeLibrary

LoadNativeLibrary位于

/art/runtime/java_vm_ext.cc

在该方法中,使用OpenNativeLibrary()来调用dlopen该系统调用。
这部分代码过多,感兴趣可以自行看源码# 766

// /art/runtime/java_vm_ext.cc#836
void *handle = android::OpenNativeLibrary(env,
                                              runtime_->GetTargetSdkVersion(),
                                              path_str,
                                              class_loader,
                                              library_path,
                                              &needs_native_bridge,
                                              error_msg);

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

if (handle == nullptr)
{
  VLOG(jni) << "dlopen(\"" << path << "\", RTLD_NOW) failed: " << *error_msg;
  return false;
}

会发现该函数中使用了JNI_Onload,JNI_Onload的加载在加载so之后

// /art/runtime/java_vm_ext.cc#885
bool was_successful = false;
// 寻找JNI_Onload
void *sym = library->FindSymbol("JNI_OnLoad", nullptr);
if (sym == nullptr)
{
  VLOG(jni) << "[No JNI_OnLoad found in \"" << path << "\"]";
  was_successful = true;
}
// 加载JNI_Onload
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 << "\"]";
  ... //省略
}

3. android::OpenNativeLibrary

OpenNativeLibrary位于

/system/core/libnativeloader/native_loader.cpp#458

在OpenNativeLibrary中调用了dlopen来进行so的加载
源码如下

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,
                              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;
  }
#else
  UNUSED(env, target_sdk_version, class_loader, library_path);
  *needs_native_bridge = false;
  void* handle = dlopen(path, RTLD_NOW);
  if (handle == nullptr) {
    if (NativeBridgeIsSupported(path)) {
      *needs_native_bridge = true;
      handle = NativeBridgeLoadLibrary(path, RTLD_NOW);
      if (handle == nullptr) {
        *error_msg = NativeBridgeGetError();
      }
    } else {
      *needs_native_bridge = false;
      *error_msg = dlerror();
    }
  }
  return handle;
#endif
}

4. dlopen

dlopen位于

/bionic/linker/libdl.cpp

源码如下

// Proxy calls to bionic loader
__attribute__((__weak__))
void* dlopen(const char* filename, int flag) {
  const void* caller_addr = __builtin_return_address(0);
  return __loader_dlopen(filename, flag, caller_addr);
}

__loader_dlopen()位于

/bionic/linker/dlfcn.cpp

源码如下

// 函数 __loader_dlopen()
void* __loader_dlopen(const char* filename, int flags, const void* caller_addr) {
  return dlopen_ext(filename, flags, nullptr, caller_addr);
}
// 函数dlopen_ext
static void* dlopen_ext(const char* filename,
                        int flags,
                        const android_dlextinfo* extinfo,
                        const void* caller_addr) {
  ScopedPthreadMutexLocker locker(&g_dl_mutex);
  g_linker_logger.ResetState();
  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;
}

5. do_dlopen()

do_dlopen函数签名为

void* do_dlopen(const char* name, int flags,
                const android_dlextinfo* extinfo,
                const void* caller_addr)

源码位于

/bionic/linker/linker.cpp

void* do_dlopen(const char* name, int flags,
                const android_dlextinfo* extinfo,
                const void* caller_addr) {
  std::string trace_prefix = std::string("dlopen: ") + (name == nullptr ? "(nullptr)" : name);
  ScopedTrace trace(trace_prefix.c_str());
  ScopedTrace loading_trace((trace_prefix + " - loading and linking").c_str());
  soinfo* const caller = find_containing_library(caller_addr);
  android_namespace_t* ns = get_caller_namespace(caller);
  // ...省略
  std::string asan_name_holder;

  const char* translated_name = name;
  if (g_is_asan && translated_name != nullptr && translated_name[0] == '/') {
    char original_path[PATH_MAX];
    if (realpath(name, original_path) != nullptr) {
      asan_name_holder = std::string(kAsanLibDirPrefix) + original_path;
      if (file_exists(asan_name_holder.c_str())) {
        soinfo* si = nullptr;
        if (find_loaded_library_by_realpath(ns, original_path, true, &si)) {
          PRINT("linker_asan dlopen NOT translating \"%s\" -> \"%s\": library already loaded", name,
                asan_name_holder.c_str());
        } else {
          PRINT("linker_asan dlopen translating \"%s\" -> \"%s\"", name, translated_name);
          translated_name = asan_name_holder.c_str();
        }
      }
    }
  }
  ProtectedDataGuard guard;
  // 加载并链接so
  soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
  loading_trace.End();

  if (si != nullptr) {
    void* handle = si->to_handle();
    LD_LOG(kLogDlopen,
           "... dlopen calling constructors: realpath=\"%s\", soname=\"%s\", handle=%p",
           si->get_realpath(), si->get_soname(), handle);
    si->call_constructors();
    failure_guard.Disable();
    LD_LOG(kLogDlopen,
           "... dlopen successful: realpath=\"%s\", soname=\"%s\", handle=%p",
           si->get_realpath(), si->get_soname(), handle);
    return handle;
  }
  return nullptr;
}

soinfo结构体结构如下所示

 
#define SOINFO_NAME_LEN 128
struct link_map
{
	uintptr_t l_addr;
	char * l_name;
	uintptr_t l_ld;
	struct link_map * l_next;
	struct link_map * l_prev;
};
typedef struct soinfo soinfo;
struct soinfo
{
	const char name[SOINFO_NAME_LEN];
	Elf32_Phdr *phdr;
	int phnum;
	unsigned entry;
	unsigned base;
	unsigned size;
 
	unsigned *dynamic;
 
	unsigned wrprotect_start;
	unsigned wrprotect_end;
 
	soinfo *next;
	unsigned flags;
 
	const char *strtab;
	Elf32_Sym *symtab;
	unsigned strsz;     //添加strsz字段,便于操作
 
	unsigned nbucket;
	unsigned nchain;
	unsigned *bucket;
	unsigned *chain;
 
	unsigned *plt_got;
 
	Elf32_Rel *plt_rel;
	unsigned plt_rel_count;
 
	Elf32_Rel *rel;
	unsigned rel_count;
 
	unsigned *preinit_array;
	unsigned preinit_array_count;
 
	unsigned *init_array;
	unsigned init_array_count;
	unsigned *fini_array;
	unsigned fini_array_count;
 
	void(*init_func)(void);
	void(*fini_func)(void);
 
#ifdef ANDROID_ARM_LINKER
	unsigned *ARM_exidx;
	unsigned ARM_exidx_count;
#endif
	unsigned refcount;
	struct link_map linkmap;
};

在这里最关键的代码如下所示

  1. 在这里find_library返回的是一个soinfo的结构体指针,find_library主要是so文件的加载和链接
  2. 在加载完so文件之后,我们可以看到si->call_constructors()也就是调用构造函数,也就是.init_xxx函数。
ProtectedDataGuard guard;
// 加载并链接so
soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
loading_trace.End();

if (si != nullptr) {
  void* handle = si->to_handle();
  LD_LOG(kLogDlopen,
         "... dlopen calling constructors: realpath=\"%s\", soname=\"%s\", handle=%p",
         si->get_realpath(), si->get_soname(), handle);
  // 构造函数初始化,即调用.init_xxx
  si->call_constructors();
  failure_guard.Disable();
  LD_LOG(kLogDlopen,
         "... dlopen successful: realpath=\"%s\", soname=\"%s\", handle=%p",
         si->get_realpath(), si->get_soname(), handle);
  return handle;
}

6. soinfo::call_constructors()

**soinfo::call_constructors()**位于

/bionic/linker/linker_soinfo.cpp

源码如下

void soinfo::call_constructors() {
  if (constructors_called) {
    return;
  }

  // We set constructors_called before actually calling the constructors, otherwise it doesn't
  // protect against recursive constructor calls. One simple example of constructor recursion
  // is the libc debug malloc, which is implemented in libc_malloc_debug_leak.so:
  // 1. The program depends on libc, so libc's constructor is called here.
  // 2. The libc constructor calls dlopen() to load libc_malloc_debug_leak.so.
  // 3. dlopen() calls the constructors on the newly created
  //    soinfo for libc_malloc_debug_leak.so.
  // 4. The debug .so depends on libc, so CallConstructors is
  //    called again with the libc soinfo. If it doesn't trigger the early-
  //    out above, the libc constructor will be called again (recursively!).
  constructors_called = true;

  if (!is_main_executable() && preinit_array_ != nullptr) {
    // The GNU dynamic linker silently ignores these, but we warn the developer.
    PRINT("\"%s\": ignoring DT_PREINIT_ARRAY in shared library!", get_realpath());
  }

  get_children().for_each([] (soinfo* si) {
    si->call_constructors();
  });

  if (!is_linker()) {
    bionic_trace_begin((std::string("calling constructors: ") + get_realpath()).c_str());
  }

  // DT_INIT should be called before DT_INIT_ARRAY if both are present.
  call_function("DT_INIT", init_func_, get_realpath());
  call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath());

  if (!is_linker()) {
    bionic_trace_end();
  }
}

结合源码和注释
我们可以理解为

  1. 全局不允许频繁调用该构造方法,也就是说在APP一次生命周期中so只能初始化一次,并且内存地址不变。
  2. 如果so没有被加载过,就调用DT_INITDT_INIT_ARRAY 也就是初始化。

五: 总结

  1. Java层加载so API:System.loadLibrary(“libxxx.so”)
  2. so层调用nativeLoadLibrary()
  3. vm中调用加载
  4. 通过find_library对该so库进行加载和链接并且返回其句柄

通过如上分析,我们可以对刚开始的图有了更深刻的了解
So文件加载流程

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值