首页
论坛
课程
招聘
[原创]andorid linker 解读1----从loadLibrary到dlopen
2020-12-31 13:21 1522

[原创]andorid linker 解读1----从loadLibrary到dlopen

2020-12-31 13:21
1522

Android 版本:android-8.0.0_r4

目的

  • [x] elf的加载过程
  • [x] 在so加载过程中确定依赖库先被调用construct 还是后调用。
  • [x] elf 重定位过程做了什么?

过程

System.loadLibrary源码位于:./libcore/ojluni/src/main/java/java/lang/System.java

1
2
3
public static void loadLibrary(String libname) {
    Runtime.getRuntime().loadLibrary0(VMStack.getCallingClassLoader(), libname);
}

可以看到然后又调用了Runtime.java的loadLibrary0方法:./libcore/ojluni/src/main/java/java/lang/Runtime.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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) {
        String filename = loader.findLibrary(libraryName);
        if (filename == null) {
            throw new UnsatisfiedLinkError(loader + " couldn't find \"" +
                                           System.mapLibraryName(libraryName) + "\"");
        }
        String error = doLoad(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 = 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);
}

这个方法主要有两个作用:

  1. 找到lib的全称
  2. 调用doLoad加载lib库

首先会判断loader 不为空来执行上面两步,如果为空,则换个方法继续执行上面两步。

1
2
3
4
5
6
7
8
9
10
11
12
private String doLoad(String name, ClassLoader loader) {
 
    String librarySearchPath = null;
    if (loader != null && loader instanceof BaseDexClassLoader) {
        BaseDexClassLoader dexClassLoader = (BaseDexClassLoader) loader;
        librarySearchPath = dexClassLoader.getLdLibraryPath();
    }
 
    synchronized (this) {
        return nativeLoad(name, loader, librarySearchPath);
    }
}

拿到库的搜索路径,调用了native 的nativeLoad方法。

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

native函数在libcore/ojluni/src/main/native/register.cpp,统一注册,

 

其中我们需要的nativeLoad函数在libcore/ojluni/src/main/native/Runtime.c 文件下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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();
    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());
}

继续跟:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
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.
。。。。。 , 已经加载了so
  if (library != nullptr) {
    if (library->GetClassLoaderAllocator() != class_loader_allocator) {
 
      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;
    }
    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;
  }
  .........
//没有加载过so , 就调用OpenNativeLibrary来加载so。
  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,
                                            &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;
  }
 
  .........
  //要是so加载成功后,还要看有没有JNI_OnLoad函数,来判断native函数是静态注册还是Jni动态注册。
  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 {
    // 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*);
    JNI_OnLoadFn jni_on_load = reinterpret_cast<JNI_OnLoadFn>(sym);
    int version = (*jni_on_load)(this, nullptr);
    .......
}

继续跟android::OpenNativeLibrary , system/core/libnativeloader/native_loader.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
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)) {
    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;
  }
#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
}

有两个点,class_loader == NULL , 和非空的情况。 。不晓得这两种是什么情况。。。。大神可以指教下。第一个过程直接走dlopen。第二个过程,走android_dlopen_ext , 两个函数最终都会走到do_dlopen 来打开so 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void* do_dlopen(const char* name, int flags,
                const android_dlextinfo* extinfo,
                const void* caller_addr) {
 
// 在此之前都是针对,flag 和extinfo flag的处理。
  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;
      LD_LOG(kLogDlopen,"... pareto's here realpath =\"%s\"",name);
      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;
    //关注点 find_library
  soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
  loading_trace.End();
 
  if (si != nullptr) {
    void* handle = si->to_handle();
    //调用init
    si->call_constructors();
 
    return handle;
  }
 
  return nullptr;
}

find_library内部调用重载函数find_library ,内部的重载函数就是本次分析的重点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
static soinfo* find_library(android_namespace_t* ns,
                            const char* name, int rtld_flags,
                            const android_dlextinfo* extinfo,
                            soinfo* needed_by) {
  soinfo* si;
 
  // readers_map is shared across recursive calls to find_libraries.
  // However, the map is not shared across different threads.
  std::unordered_map<const soinfo*, ElfReader> readers_map;
  if (name == nullptr) {
    si = solist_get_somain();
  } else if (!find_libraries(ns,
                             needed_by,
                             &name,
                             1,
                             &si,
                             nullptr,
                             0,
                             rtld_flags,
                             extinfo,
                             false /* add_as_children */,
                             true /* search_linked_namespaces */,
                             readers_map)) {
    return nullptr;
  }
    //  增加so的索引计数
  si->increment_ref_count();
 
  return si;
}

关键函数到位了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
bool find_libraries(android_namespace_t* ns,
                    soinfo* start_with,
                    const char* const library_names[],
                    size_t library_names_count,
                    soinfo* soinfos[],
                    std::vector<soinfo*>* ld_preloads,
                    size_t ld_preloads_count,
                    int rtld_flags,
                    const android_dlextinfo* extinfo,
                    bool add_as_children,
                    bool search_linked_namespaces,
                    std::unordered_map<const soinfo*, ElfReader>& readers_map,
                    std::vector<android_namespace_t*>* namespaces) {
  // Step 0: prepare.
  LoadTaskList load_tasks;
 
  for (size_t i = 0; i < library_names_count; ++i) {
    const char* name = library_names[i];
    LD_LOG(kLogDlopen,"[linker.cpp] step 1 ,so_name",name);
 
    load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map));
  }
 
  // If soinfos array is null allocate one on stack.
  // The array is needed in case of failure; for example
  // when library_names[] = {libone.so, libtwo.so} and libone.so
  // is loaded correctly but libtwo.so failed for some reason.
  // In this case libone.so should be unloaded on return.
  // See also implementation of failure_guard below.
 
  if (soinfos == nullptr) {
    size_t soinfos_size = sizeof(soinfo*)*library_names_count;
    soinfos = reinterpret_cast<soinfo**>(alloca(soinfos_size));
    memset(soinfos, 0, soinfos_size);
  }
 
  // list of libraries to link - see step 2.
  size_t soinfos_count = 0;
 
  auto scope_guard = android::base::make_scope_guard([&]() {
    for (LoadTask* t : load_tasks) {
      LD_LOG(kLogDlopen,"[linker.cpp] before call deleter %s",t->get_name());
      LoadTask::deleter(t);
    }
  });
 
  auto failure_guard = android::base::make_scope_guard([&]() {
    // Housekeeping
    soinfo_unload(soinfos, soinfos_count);
  });
 
  ZipArchiveCache zip_archive_cache;
 
  // Step 1: expand the list of load_tasks to include
  // all DT_NEEDED libraries (do not load them just yet)
  for (size_t i = 0; i<load_tasks.size(); ++i) {
    LoadTask* task = load_tasks[i];
    LD_LOG(kLogDlopen,"[linker.cpp] traverse load_tasks %s",task->get_name());
    soinfo* needed_by = task->get_needed_by();
    LD_LOG(kLogDlopen,"[linker.cpp] print needed_by so_name %s ,add_as_children  %d ",needed_by->get_soname(),add_as_children);
    bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
    LD_LOG(kLogDlopen,"[linker.cpp] value of is_dt_needed %d",is_dt_needed);
    task->set_extinfo(is_dt_needed ? nullptr : extinfo);
    task->set_dt_needed(is_dt_needed);
 
    // try to find the load.
    // Note: start from the namespace that is stored in the LoadTask. This namespace
    // is different from the current namespace when the LoadTask is for a transitive
    // dependency and the lib that created the LoadTask is not found in the
    // current namespace but in one of the linked namespace.
    if (!find_library_internal(const_cast<android_namespace_t*>(task->get_start_from()),
                               task,
                               &zip_archive_cache,
                               &load_tasks,
                               rtld_flags,
                               search_linked_namespaces || is_dt_needed)) {
      return false;
    }
 
    soinfo* si = task->get_soinfo();
 
    if (is_dt_needed) {
      needed_by->add_child(si);
 
      if (si->is_linked()) {
        si->increment_ref_count();
      }
    }
 
    // When ld_preloads is not null, the first
    // ld_preloads_count libs are in fact ld_preloads.
    if (ld_preloads != nullptr && soinfos_count < ld_preloads_count) {
      ld_preloads->push_back(si);
    }
 
    if (soinfos_count < library_names_count) {
      soinfos[soinfos_count++] = si;
    }
  }
 
  // Step 2: Load libraries in random order (see b/24047022)
  LoadTaskList load_list;
  for (auto&& task : load_tasks) {
    soinfo* si = task->get_soinfo();
    auto pred = [&](const LoadTask* t) {
      return t->get_soinfo() == si;
    };
 
    if (!si->is_linked() &&
        std::find_if(load_list.begin(), load_list.end(), pred) == load_list.end() ) {
      load_list.push_back(task);
    }
  }
  shuffle(&load_list);
 
  for (auto&& task : load_list) {
    LD_LOG(kLogDlopen,"[linker.cpp] Step2 list all task %s ",task->get_name());
    if (!task->load()) {
      return false;
    }
  }
 
  // Step 3: pre-link all DT_NEEDED libraries in breadth first order.
  for (auto&& task : load_tasks) {
    soinfo* si = task->get_soinfo();
    if (!si->is_linked() && !si->prelink_image()) {
      return false;
    }
  }
 
  // Step 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is
  // determined at step 3.
 
  // Step 4-1: DF_1_GLOBAL bit is force set for LD_PRELOADed libs because they
  // must be added to the global group
  if (ld_preloads != nullptr) {
    for (auto&& si : *ld_preloads) {
      si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL);
    }
  }
 
  // Step 4-2: Gather all DF_1_GLOBAL libs which were newly loaded during this
  // run. These will be the new member of the global group
  soinfo_list_t new_global_group_members;
  for (auto&& task : load_tasks) {
    soinfo* si = task->get_soinfo();
    if (!si->is_linked() && (si->get_dt_flags_1() & DF_1_GLOBAL) != 0) {
      new_global_group_members.push_back(si);
    }
  }
 
  // Step 4-3: Add the new global group members to all the linked namespaces
  for (auto si : new_global_group_members) {
    for (auto linked_ns : *namespaces) {
      if (si->get_primary_namespace() != linked_ns) {
        linked_ns->add_soinfo(si);
        si->add_secondary_namespace(linked_ns);
      }
    }
  }
 
  // Step 5: link libraries that are not destined to this namespace.
  // Do this by recursively calling find_libraries on the namespace where the lib
  // was found during Step 1.
  for (auto&& task : load_tasks) {
    soinfo* si = task->get_soinfo();
    if (si->get_primary_namespace() != ns) {
      const char* name = task->get_name();
        //这里可以看出先递归needed_by。
      if (find_libraries(si->get_primary_namespace(), task->get_needed_by(), &name, 1,
                         nullptr /* soinfos */, nullptr /* ld_preloads */, 0 /* ld_preload_count */,
                         rtld_flags, nullptr /* extinfo */, false /* add_as_children */,
                         false /* search_linked_namespaces */, readers_map, namespaces)) {
        // If this lib is directly needed by one of the libs in this namespace,
        // then increment the count
        soinfo* needed_by = task->get_needed_by();
        if (needed_by != nullptr && needed_by->get_primary_namespace() == ns && si->is_linked()) {
          si->increment_ref_count();
        }
      } else {
        return false;
      }
    }
  }
 
  // Step 6: link libraries in this namespace
  soinfo_list_t local_group;
  walk_dependencies_tree(
      (start_with != nullptr && add_as_children) ? &start_with : soinfos,
      (start_with != nullptr && add_as_children) ? 1 : soinfos_count,
      [&] (soinfo* si) {
    if (ns->is_accessible(si)) {
      local_group.push_back(si);
      return kWalkContinue;
    } else {
      return kWalkSkip;
    }
  });
  LD_LOG(kLogDlopen,"[linker.cpp] link libraries in this namespace");
  soinfo_list_t global_group = ns->get_global_group();
  bool linked = local_group.visit([&](soinfo* si) {
    if (!si->is_linked()) {
      LD_LOG(kLogDlopen,"so %s is not linked , now try to link ",si->get_soname());
      if (!si->link_image(global_group, local_group, extinfo) ||
          !get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
        return false;
      }
    }
 
    return true;
  });
  LD_LOG(kLogDlopen,"[linker.cpp] all link libraries in this namespace has linker");
  if (linked) {
    local_group.for_each([](soinfo* si) {
      LD_LOG(kLogDlopen,"travser local_group list %s" , si->get_soname());
      if (!si->is_linked()) {
        si->set_linked();
      }
    });
 
    failure_guard.Disable();
  }
 
  return linked;
}

[注意] 欢迎加入看雪团队!base上海,招聘安全工程师、逆向工程师多个坑位等你投递!

最后于 2020-12-31 16:48 被pareto编辑 ,原因: 修改标题
收藏
点赞1
打赏
分享
最新回复 (1)
雪    币: 15488
活跃值: 活跃值 (20446)
能力值: (RANK:75 )
在线值:
发帖
回帖
粉丝
Editor 活跃值 2020-12-31 13:23
2
0
感谢分享!建议帖子用一个有意义的标题
游客
登录 | 注册 方可回帖
返回