• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "nativeloader"
18 
19 #include "nativeloader/native_loader.h"
20 
21 #include <dlfcn.h>
22 #include <sys/types.h>
23 
24 #include <algorithm>
25 #include <memory>
26 #include <mutex>
27 #include <optional>
28 #include <regex>
29 #include <string>
30 #include <vector>
31 
32 #include "android-base/file.h"
33 #include "android-base/macros.h"
34 #include <android-base/properties.h>
35 #include "android-base/strings.h"
36 #include "android-base/thread_annotations.h"
37 #include "base/macros.h"
38 #include "nativebridge/native_bridge.h"
39 #include "nativehelper/scoped_utf_chars.h"
40 #include "public_libraries.h"
41 
42 #ifdef ART_TARGET_ANDROID
43 #include "android-modules-utils/sdk_level.h"
44 #include "android/api-level.h"
45 #include "library_namespaces.h"
46 #include "log/log.h"
47 #include "nativeloader/dlext_namespaces.h"
48 #endif
49 
50 namespace android {
51 
52 namespace {
53 
54 #if defined(ART_TARGET_ANDROID)
55 
56 using ::android::base::Result;
57 using ::android::nativeloader::LibraryNamespaces;
58 
59 // NATIVELOADER_DEFAULT_NAMESPACE_LIBS is an environment variable that can be
60 // used to list extra libraries (separated by ":") that libnativeloader will
61 // load from the default namespace. The libraries must be listed without paths,
62 // and then LD_LIBRARY_PATH is typically set to the directories to load them
63 // from. The libraries will be available in all classloader namespaces, and also
64 // in the fallback namespace used when no classloader is given.
65 //
66 // kNativeloaderExtraLibs is the name of that fallback namespace.
67 //
68 // NATIVELOADER_DEFAULT_NAMESPACE_LIBS is intended to be used for testing only,
69 // and in particular in the ART run tests that are executed through dalvikvm in
70 // the APEX. In that case the default namespace links to the ART namespace
71 // (com_android_art) for all libraries, which means this can be used to load
72 // test libraries that depend on ART internal libraries.
73 //
74 // There's also code in art/dalvikvm.cc to add links from com_android_art back
75 // to the default namespace for NATIVELOADER_DEFAULT_NAMESPACE_LIBS, enabling
76 // access in the opposite direction as well. Useful e.g. to load ART plugins in
77 // NATIVELOADER_DEFAULT_NAMESPACE_LIBS.
78 constexpr const char* kNativeloaderExtraLibs = "nativeloader-extra-libs";
79 
80 std::mutex g_namespaces_mutex;
81 LibraryNamespaces* g_namespaces GUARDED_BY(g_namespaces_mutex) = new LibraryNamespaces;
82 NativeLoaderNamespace* g_nativeloader_extra_libs_namespace GUARDED_BY(g_namespaces_mutex) = nullptr;
83 
FindApexNamespace(const char * caller_location)84 std::optional<NativeLoaderNamespace> FindApexNamespace(const char* caller_location) {
85   std::optional<std::string> name = nativeloader::FindApexNamespaceName(caller_location);
86   if (name.has_value()) {
87     // Native Bridge is never used for APEXes.
88     Result<NativeLoaderNamespace> ns =
89         NativeLoaderNamespace::GetExportedNamespace(name.value(), /*is_bridged=*/false);
90     LOG_ALWAYS_FATAL_IF(!ns.ok(),
91                         "Error finding ns %s for APEX location %s: %s",
92                         name.value().c_str(),
93                         caller_location,
94                         ns.error().message().c_str());
95     return ns.value();
96   }
97   return std::nullopt;
98 }
99 
GetNamespaceForApiDomain(nativeloader::ApiDomain api_domain,bool is_bridged)100 Result<NativeLoaderNamespace> GetNamespaceForApiDomain(nativeloader::ApiDomain api_domain,
101                                                        bool is_bridged) {
102   switch (api_domain) {
103     case nativeloader::API_DOMAIN_VENDOR:
104       return NativeLoaderNamespace::GetExportedNamespace(nativeloader::kVendorNamespaceName,
105                                                          is_bridged);
106     case nativeloader::API_DOMAIN_PRODUCT:
107       return NativeLoaderNamespace::GetExportedNamespace(nativeloader::kProductNamespaceName,
108                                                          is_bridged);
109     case nativeloader::API_DOMAIN_SYSTEM:
110       return NativeLoaderNamespace::GetSystemNamespace(is_bridged);
111     default:
112       LOG_FATAL("Invalid API domain %d", api_domain);
113       UNREACHABLE();
114   }
115 }
116 
CreateNativeloaderDefaultNamespaceLibsLink(NativeLoaderNamespace & ns)117 Result<void> CreateNativeloaderDefaultNamespaceLibsLink(NativeLoaderNamespace& ns)
118     REQUIRES(g_namespaces_mutex) {
119   const char* links = getenv("NATIVELOADER_DEFAULT_NAMESPACE_LIBS");
120   if (links == nullptr || *links == 0) {
121     return {};
122   }
123   // Pass nullptr to Link() to create a link to the default namespace without
124   // requiring it to be visible.
125   return ns.Link(nullptr, links);
126 }
127 
GetNativeloaderExtraLibsNamespace()128 Result<NativeLoaderNamespace*> GetNativeloaderExtraLibsNamespace() REQUIRES(g_namespaces_mutex) {
129   if (g_nativeloader_extra_libs_namespace != nullptr) {
130     return g_nativeloader_extra_libs_namespace;
131   }
132 
133   Result<NativeLoaderNamespace> ns =
134       NativeLoaderNamespace::Create(kNativeloaderExtraLibs,
135                                     /*search_paths=*/"",
136                                     /*permitted_paths=*/"",
137                                     /*parent=*/nullptr,
138                                     /*is_shared=*/false,
139                                     /*is_exempt_list_enabled=*/false,
140                                     /*also_used_as_anonymous=*/false);
141   if (!ns.ok()) {
142     return ns.error();
143   }
144   g_nativeloader_extra_libs_namespace = new NativeLoaderNamespace(std::move(ns.value()));
145   Result<void> linked =
146       CreateNativeloaderDefaultNamespaceLibsLink(*g_nativeloader_extra_libs_namespace);
147   if (!linked.ok()) {
148     return linked.error();
149   }
150   return g_nativeloader_extra_libs_namespace;
151 }
152 
153 // If the given path matches a library in NATIVELOADER_DEFAULT_NAMESPACE_LIBS
154 // then load it in the nativeloader-extra-libs namespace, otherwise return
155 // nullptr without error.
TryLoadNativeloaderExtraLib(const char * path)156 Result<void*> TryLoadNativeloaderExtraLib(const char* path) {
157   const char* links = getenv("NATIVELOADER_DEFAULT_NAMESPACE_LIBS");
158   if (links == nullptr || *links == 0) {
159     return nullptr;
160   }
161   std::vector<std::string> lib_list = base::Split(links, ":");
162   if (std::find(lib_list.begin(), lib_list.end(), path) == lib_list.end()) {
163     return nullptr;
164   }
165 
166   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
167   Result<NativeLoaderNamespace*> ns = GetNativeloaderExtraLibsNamespace();
168   if (!ns.ok()) {
169     return ns.error();
170   }
171 
172   Result<void*> res = ns.value()->Load(path);
173   ALOGD("Load %s using ns %s from NATIVELOADER_DEFAULT_NAMESPACE_LIBS match: %s",
174         path,
175         ns.value()->name().c_str(),
176         res.ok() ? "ok" : res.error().message().c_str());
177   return res;
178 }
179 
CreateClassLoaderNamespaceLocked(JNIEnv * env,int32_t target_sdk_version,jobject class_loader,nativeloader::ApiDomain api_domain,bool is_shared,const std::string & dex_path,jstring library_path_j,jstring permitted_path_j,jstring uses_library_list_j)180 Result<NativeLoaderNamespace*> CreateClassLoaderNamespaceLocked(JNIEnv* env,
181                                                                 int32_t target_sdk_version,
182                                                                 jobject class_loader,
183                                                                 nativeloader::ApiDomain api_domain,
184                                                                 bool is_shared,
185                                                                 const std::string& dex_path,
186                                                                 jstring library_path_j,
187                                                                 jstring permitted_path_j,
188                                                                 jstring uses_library_list_j)
189     REQUIRES(g_namespaces_mutex) {
190   Result<NativeLoaderNamespace*> ns = g_namespaces->Create(env,
191                                                            target_sdk_version,
192                                                            class_loader,
193                                                            api_domain,
194                                                            is_shared,
195                                                            dex_path,
196                                                            library_path_j,
197                                                            permitted_path_j,
198                                                            uses_library_list_j);
199   if (!ns.ok()) {
200     return ns;
201   }
202   Result<void> linked = CreateNativeloaderDefaultNamespaceLibsLink(*ns.value());
203   if (!linked.ok()) {
204     return linked.error();
205   }
206   return ns;
207 }
208 
209 #endif  // ART_TARGET_ANDROID
210 
211 }  // namespace
212 
InitializeNativeLoader()213 void InitializeNativeLoader() {
214 #if defined(ART_TARGET_ANDROID)
215   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
216   g_namespaces->Initialize();
217 #endif
218 }
219 
ResetNativeLoader()220 void ResetNativeLoader() {
221 #if defined(ART_TARGET_ANDROID)
222   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
223   g_namespaces->Reset();
224   delete g_nativeloader_extra_libs_namespace;
225   g_nativeloader_extra_libs_namespace = nullptr;
226 #endif
227 }
228 
229 // dex_path_j may be a ':'-separated list of paths, e.g. when creating a shared
230 // library loader - cf. mCodePaths in android.content.pm.SharedLibraryInfo.
CreateClassLoaderNamespace(JNIEnv * env,int32_t target_sdk_version,jobject class_loader,bool is_shared,jstring dex_path_j,jstring library_path_j,jstring permitted_path_j,jstring uses_library_list_j)231 jstring CreateClassLoaderNamespace(JNIEnv* env,
232                                    int32_t target_sdk_version,
233                                    jobject class_loader,
234                                    bool is_shared,
235                                    jstring dex_path_j,
236                                    jstring library_path_j,
237                                    jstring permitted_path_j,
238                                    jstring uses_library_list_j) {
239 #if defined(ART_TARGET_ANDROID)
240   std::string dex_path;
241   if (dex_path_j != nullptr) {
242     ScopedUtfChars dex_path_chars(env, dex_path_j);
243     dex_path = dex_path_chars.c_str();
244   }
245 
246   Result<nativeloader::ApiDomain> api_domain = nativeloader::GetApiDomainFromPathList(dex_path);
247   if (!api_domain.ok()) {
248     return env->NewStringUTF(api_domain.error().message().c_str());
249   }
250 
251   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
252   Result<NativeLoaderNamespace*> ns = CreateClassLoaderNamespaceLocked(env,
253                                                                        target_sdk_version,
254                                                                        class_loader,
255                                                                        api_domain.value(),
256                                                                        is_shared,
257                                                                        dex_path,
258                                                                        library_path_j,
259                                                                        permitted_path_j,
260                                                                        uses_library_list_j);
261   if (!ns.ok()) {
262     return env->NewStringUTF(ns.error().message().c_str());
263   }
264 
265 #else
266   UNUSED(env,
267          target_sdk_version,
268          class_loader,
269          is_shared,
270          dex_path_j,
271          library_path_j,
272          permitted_path_j,
273          uses_library_list_j);
274 #endif
275 
276   return nullptr;
277 }
278 
279 #if defined(ART_TARGET_ANDROID)
ShouldBypassLoadingForB349878424()280 static bool ShouldBypassLoadingForB349878424() {
281   struct stat st;
282   if (stat("/system/lib64/libsobridge.so", &st) != 0 &&
283       stat("/system/lib64/libwalkstack.so", &st) != 0) {
284     return false;
285   }
286   std::string property = android::base::GetProperty("ro.product.build.fingerprint", "");
287   return android_get_device_api_level() == 33 &&
288       (property.starts_with("Xiaomi") ||
289        property.starts_with("Redmi") ||
290        property.starts_with("POCO"));
291 }
292 #endif
293 
OpenNativeLibrary(JNIEnv * env,int32_t target_sdk_version,const char * path,jobject class_loader,const char * caller_location,jstring library_path_j,bool * needs_native_bridge,char ** error_msg)294 void* OpenNativeLibrary(JNIEnv* env,
295                         int32_t target_sdk_version,
296                         const char* path,
297                         jobject class_loader,
298                         const char* caller_location,
299                         jstring library_path_j,
300                         bool* needs_native_bridge,
301                         char** error_msg) {
302 #if defined(ART_TARGET_ANDROID)
303   if (class_loader == nullptr) {
304     // class_loader is null only for the boot class loader (see
305     // IsBootClassLoader call in JavaVMExt::LoadNativeLibrary), i.e. the caller
306     // is in the boot classpath.
307     *needs_native_bridge = false;
308     if (caller_location != nullptr) {
309       std::optional<NativeLoaderNamespace> ns = FindApexNamespace(caller_location);
310       if (ns.has_value()) {
311         const android_dlextinfo dlextinfo = {
312             .flags = ANDROID_DLEXT_USE_NAMESPACE,
313             .library_namespace = ns.value().ToRawAndroidNamespace(),
314         };
315         void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo);
316         char* dlerror_msg = handle == nullptr ? strdup(dlerror()) : nullptr;
317         ALOGD("Load %s using APEX ns %s for caller %s: %s",
318               path,
319               ns.value().name().c_str(),
320               caller_location,
321               dlerror_msg == nullptr ? "ok" : dlerror_msg);
322         if (dlerror_msg != nullptr) {
323           *error_msg = dlerror_msg;
324         }
325         return handle;
326       }
327     }
328 
329     // Check if the library is in NATIVELOADER_DEFAULT_NAMESPACE_LIBS and should
330     // be loaded from the kNativeloaderExtraLibs namespace.
331     {
332       Result<void*> handle = TryLoadNativeloaderExtraLib(path);
333       if (!handle.ok()) {
334         *error_msg = strdup(handle.error().message().c_str());
335         return nullptr;
336       }
337       if (handle.value() != nullptr) {
338         return handle.value();
339       }
340     }
341 
342     // Handle issue b/349878424.
343     static bool bypass_loading_for_b349878424 = ShouldBypassLoadingForB349878424();
344 
345     if (bypass_loading_for_b349878424 &&
346         (strcmp("libsobridge.so", path) == 0 || strcmp("libwalkstack.so", path) == 0)) {
347       // Load a different library to pretend the loading was successful. This
348       // allows the device to boot.
349       ALOGD("Loading libbase.so instead of %s due to b/349878424", path);
350       path = "libbase.so";
351     }
352 
353     // Fall back to the system namespace. This happens for preloaded JNI
354     // libraries in the zygote.
355     void* handle = OpenSystemLibrary(path, RTLD_NOW);
356     char* dlerror_msg = handle == nullptr ? strdup(dlerror()) : nullptr;
357     ALOGD("Load %s using system ns (caller=%s): %s",
358           path,
359           caller_location == nullptr ? "<unknown>" : caller_location,
360           dlerror_msg == nullptr ? "ok" : dlerror_msg);
361     if (dlerror_msg != nullptr) {
362       *error_msg = dlerror_msg;
363     }
364     return handle;
365   }
366 
367   // If the caller is in any of the system image partitions and the library is
368   // in the same partition then load it without regards to public library
369   // restrictions. This is only done if the library is specified by an absolute
370   // path, so we don't affect the lookup process for libraries specified by name
371   // only.
372   if (caller_location != nullptr &&
373       // Apps in the partition may have their own native libraries which should
374       // be loaded with the app's classloader namespace, so only do this for
375       // libraries in the partition-wide lib(64) directories.
376       nativeloader::IsPartitionNativeLibPath(path) &&
377       // Don't do this if the system image is older than V, to avoid any compat
378       // issues with apps and shared libs in them.
379       android::modules::sdklevel::IsAtLeastV()) {
380     nativeloader::ApiDomain caller_api_domain = nativeloader::GetApiDomainFromPath(caller_location);
381     if (caller_api_domain != nativeloader::API_DOMAIN_DEFAULT) {
382       nativeloader::ApiDomain library_api_domain = nativeloader::GetApiDomainFromPath(path);
383 
384       if (library_api_domain == caller_api_domain) {
385         bool is_bridged = false;
386         if (library_path_j != nullptr) {
387           ScopedUtfChars library_path_utf_chars(env, library_path_j);
388           if (library_path_utf_chars[0] != '\0') {
389             is_bridged = NativeBridgeIsPathSupported(library_path_utf_chars.c_str());
390           }
391         }
392 
393         Result<NativeLoaderNamespace> ns = GetNamespaceForApiDomain(caller_api_domain, is_bridged);
394         if (!ns.ok()) {
395           ALOGD("Failed to find ns for caller %s in API domain %d to load %s (is_bridged=%b): %s",
396                 caller_location,
397                 caller_api_domain,
398                 path,
399                 is_bridged,
400                 ns.error().message().c_str());
401           *error_msg = strdup(ns.error().message().c_str());
402           return nullptr;
403         }
404 
405         *needs_native_bridge = ns.value().IsBridged();
406         Result<void*> handle = ns.value().Load(path);
407         ALOGD("Load %s using ns %s for caller %s in same partition (is_bridged=%b): %s",
408               path,
409               ns.value().name().c_str(),
410               caller_location,
411               is_bridged,
412               handle.ok() ? "ok" : handle.error().message().c_str());
413         if (!handle.ok()) {
414           *error_msg = strdup(handle.error().message().c_str());
415           return nullptr;
416         }
417         return handle.value();
418       }
419     }
420   }
421 
422   NativeLoaderNamespace* ns;
423   const char* ns_descr;
424   {
425     std::lock_guard<std::mutex> guard(g_namespaces_mutex);
426 
427     ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
428     ns_descr = "class loader";
429 
430     if (ns == nullptr) {
431       // This is the case where the classloader was not created by ApplicationLoaders
432       // In this case we create an isolated not-shared namespace for it.
433       const std::string empty_dex_path;
434       Result<NativeLoaderNamespace*> res =
435           CreateClassLoaderNamespaceLocked(env,
436                                            target_sdk_version,
437                                            class_loader,
438                                            nativeloader::API_DOMAIN_DEFAULT,
439                                            /*is_shared=*/false,
440                                            empty_dex_path,
441                                            library_path_j,
442                                            /*permitted_path_j=*/nullptr,
443                                            /*uses_library_list_j=*/nullptr);
444       if (!res.ok()) {
445         ALOGD("Failed to create isolated ns for %s (caller=%s)",
446               path,
447               caller_location == nullptr ? "<unknown>" : caller_location);
448         *error_msg = strdup(res.error().message().c_str());
449         return nullptr;
450       }
451       ns = res.value();
452       ns_descr = "isolated";
453     }
454   }
455 
456   *needs_native_bridge = ns->IsBridged();
457   Result<void*> handle = ns->Load(path);
458   ALOGD("Load %s using %s ns %s (caller=%s): %s",
459         path,
460         ns_descr,
461         ns->name().c_str(),
462         caller_location == nullptr ? "<unknown>" : caller_location,
463         handle.ok() ? "ok" : handle.error().message().c_str());
464   if (!handle.ok()) {
465     *error_msg = strdup(handle.error().message().c_str());
466     return nullptr;
467   }
468   return handle.value();
469 
470 #else   // !ART_TARGET_ANDROID
471   UNUSED(env, target_sdk_version, class_loader, caller_location);
472 
473   // Do some best effort to emulate library-path support. It will not
474   // work for dependencies.
475   //
476   // Note: null has a special meaning and must be preserved.
477   std::string library_path;  // Empty string by default.
478   if (library_path_j != nullptr && path != nullptr && path[0] != '/') {
479     ScopedUtfChars library_path_utf_chars(env, library_path_j);
480     library_path = library_path_utf_chars.c_str();
481   }
482 
483   std::vector<std::string> library_paths = base::Split(library_path, ":");
484 
485   for (const std::string& lib_path : library_paths) {
486     *needs_native_bridge = false;
487     const char* path_arg;
488     std::string complete_path;
489     if (path == nullptr) {
490       // Preserve null.
491       path_arg = nullptr;
492     } else {
493       complete_path = lib_path;
494       if (!complete_path.empty()) {
495         complete_path.append("/");
496       }
497       complete_path.append(path);
498       path_arg = complete_path.c_str();
499     }
500     void* handle = dlopen(path_arg, RTLD_NOW);
501     if (handle != nullptr) {
502       return handle;
503     }
504     if (NativeBridgeIsSupported(path_arg)) {
505       *needs_native_bridge = true;
506       handle = NativeBridgeLoadLibrary(path_arg, RTLD_NOW);
507       if (handle != nullptr) {
508         return handle;
509       }
510       *error_msg = strdup(NativeBridgeGetError());
511     } else {
512       *error_msg = strdup(dlerror());
513     }
514   }
515   return nullptr;
516 #endif  // !ART_TARGET_ANDROID
517 }
518 
CloseNativeLibrary(void * handle,const bool needs_native_bridge,char ** error_msg)519 bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
520   bool success;
521   if (needs_native_bridge) {
522     success = (NativeBridgeUnloadLibrary(handle) == 0);
523     if (!success) {
524       *error_msg = strdup(NativeBridgeGetError());
525     }
526   } else {
527     success = (dlclose(handle) == 0);
528     if (!success) {
529       *error_msg = strdup(dlerror());
530     }
531   }
532 
533   return success;
534 }
535 
NativeLoaderFreeErrorMessage(char * msg)536 void NativeLoaderFreeErrorMessage(char* msg) {
537   // The error messages get allocated through strdup, so we must call free on them.
538   free(msg);
539 }
540 
541 #if defined(ART_TARGET_ANDROID)
OpenNativeLibraryInNamespace(NativeLoaderNamespace * ns,const char * path,bool * needs_native_bridge,char ** error_msg)542 void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
543                                    bool* needs_native_bridge, char** error_msg) {
544   Result<void*> handle = ns->Load(path);
545   if (!handle.ok() && error_msg != nullptr) {
546     *error_msg = strdup(handle.error().message().c_str());
547   }
548   if (needs_native_bridge != nullptr) {
549     *needs_native_bridge = ns->IsBridged();
550   }
551   return handle.ok() ? *handle : nullptr;
552 }
553 
IsNamespaceNativeBridged(const struct NativeLoaderNamespace * ns)554 bool IsNamespaceNativeBridged(const struct NativeLoaderNamespace* ns) { return ns->IsBridged(); }
555 
556 // native_bridge_namespaces are not supported for callers of this function.
557 // This function will return nullptr in the case when application is running
558 // on native bridge.
FindNamespaceByClassLoader(JNIEnv * env,jobject class_loader)559 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
560   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
561   NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);
562   if (ns != nullptr && !ns->IsBridged()) {
563     return ns->ToRawAndroidNamespace();
564   }
565   return nullptr;
566 }
567 
FindNativeLoaderNamespaceByClassLoader(JNIEnv * env,jobject class_loader)568 NativeLoaderNamespace* FindNativeLoaderNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
569   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
570   return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
571 }
572 
LinkNativeLoaderNamespaceToExportedNamespaceLibrary(struct NativeLoaderNamespace * ns,const char * exported_ns_name,const char * library_name,char ** error_msg)573 void LinkNativeLoaderNamespaceToExportedNamespaceLibrary(struct NativeLoaderNamespace* ns,
574                                                          const char* exported_ns_name,
575                                                          const char* library_name,
576                                                          char** error_msg) {
577   Result<NativeLoaderNamespace> exported_ns =
578       NativeLoaderNamespace::GetExportedNamespace(exported_ns_name, ns->IsBridged());
579   if (!exported_ns.ok()) {
580     *error_msg = strdup(exported_ns.error().message().c_str());
581     return;
582   }
583 
584   Result<void> linked = ns->Link(&exported_ns.value(), std::string(library_name));
585   if (!linked.ok()) {
586     *error_msg = strdup(linked.error().message().c_str());
587   }
588 }
589 
590 #endif  // ART_TARGET_ANDROID
591 
592 }  // namespace android
593