Android system — 链接器命名空间共享库配置方法(Android 11后)

Android system — 链接器命名空间共享库配置方法

  • 1. 应用进程
    • 1.1 应用进程类加载器的命名空间初始化
      • 1.1.1 OpenNativeLibrary
      • 1.1.2 LibraryNamespaces::Create
    • 1.2 配置共享库位置
  • 2. native进程
    • 2.1 native 命名空间配置初始化
      • 2.1.1 android_namespace_t::is_accessible
      • 2.1.2 init_default_namespaces
    • 2.2 配置共享库位置

1. 应用进程

1.1 应用进程类加载器的命名空间初始化

  在应用程序对应的可执行文件app_process根据/linkerconfig/ld.config.txt配置文件初始化命名空间之后,每当应用程序创建一个类加载器classloader并调用System.loadLibrary加载so库时都会创建一个与此类加载器对应的命名空间。从源码角度分析一下这个过程,System.loadLibrary函数最后会调用OpenNativeLibrary函数。

1.1.1 OpenNativeLibrary

System.loadLibrary()-->nativeLoad()-->Runtime.c::Runtime_nativeLoad()-->JVM_NativeLoad()-->Openjdkjvm.cc::JVM_NativeLoad()-->java_vm_ext.cc::LoadNativeLibrary()-->native_loader.cpp::OpenNativeLibrary() 也就是java层的System.loadLibrary()最终会调用libnativeloader.so的OpenNativeLibrary函数。

  • OpenNativeLibrary先判断classloader类加载器是否为空,如果为空直接调用android_dlopen_ext加载库文件
  • 如果判断classloader类加载器不为空,并且classloader类加载器没有对应的命名空间(第一次调用System.loadLibrary)就调用LibraryNamespaces::Create创建新的命名空间。
void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
                        jobject class_loader, const char* caller_location, jstring library_path,
                        bool* needs_native_bridge, char** error_msg) {
#if defined(ART_TARGET_ANDROID)
  UNUSED(target_sdk_version);

  if (class_loader == nullptr) { //如果类加载器为空直接调用android_dlopen_ext加载库文件
    *needs_native_bridge = false;
    if (caller_location != nullptr) {
      android_namespace_t* boot_namespace = FindExportedNamespace(caller_location);
      if (boot_namespace != nullptr) {
        const android_dlextinfo dlextinfo = {
            .flags = ANDROID_DLEXT_USE_NAMESPACE,
            .library_namespace = boot_namespace,
        };
        void* handle = android_dlopen_ext(path, RTLD_NOW, &dlextinfo);
        if (handle == nullptr) {
          *error_msg = strdup(dlerror());
        }
        return handle;
      }
    }

    // Check if the library is in NATIVELOADER_DEFAULT_NAMESPACE_LIBS and should
    // be loaded from the kNativeloaderExtraLibs namespace.
    {
      Result<void*> handle = TryLoadNativeloaderExtraLib(path);
      if (!handle.ok()) {
        *error_msg = strdup(handle.error().message().c_str());
        return nullptr;
      }
      if (handle.value() != nullptr) {
        return handle.value();
      }
    }

    // Fall back to the system namespace. This happens for preloaded JNI
    // libraries in the zygote.
    // TODO(b/185833744): Investigate if this should fall back to the app main
    // namespace (aka anonymous namespace) instead.
    void* handle = OpenSystemLibrary(path, RTLD_NOW);
    if (handle == nullptr) {
      *error_msg = strdup(dlerror());
    }
    return handle;
  }

  std::lock_guard<std::mutex> guard(g_namespaces_mutex);
  NativeLoaderNamespace* ns;

  if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == 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.
    Result<NativeLoaderNamespace*> isolated_ns =
        CreateClassLoaderNamespaceLocked(env,
                                         target_sdk_version,
                                         class_loader,
                                         /*is_shared=*/false,
                                         /*dex_path=*/nullptr,
                                         library_path,
                                         /*permitted_path=*/nullptr,
                                         /*uses_library_list=*/nullptr);
    if (!isolated_ns.ok()) {
      *error_msg = strdup(isolated_ns.error().message().c_str());
      return nullptr;
    } else {
      ns = *isolated_ns;
    }
  }

  return OpenNativeLibraryInNamespace(ns, path, needs_native_bridge, error_msg);

CreateClassLoaderNamespaceLocked中我们可以看到调用了LibraryNamespaces::Create创建新的命名空间


Result<NativeLoaderNamespace*> CreateClassLoaderNamespaceLocked(JNIEnv* env,
                                                                int32_t target_sdk_version,
                                                                jobject class_loader,
                                                                bool is_shared,
                                                                jstring dex_path,
                                                                jstring library_path,
                                                                jstring permitted_path,
                                                                jstring uses_library_list)
    REQUIRES(g_namespaces_mutex) {
  Result<NativeLoaderNamespace*> ns = g_namespaces->Create(env,
                                                           target_sdk_version,
                                                           class_loader,
                                                           is_shared,
                                                           dex_path,
                                                           library_path,
                                                           permitted_path,
                                                           uses_library_list);
  if (!ns.ok()) {
    return ns;
  }
  Result<void> linked = CreateNativeloaderDefaultNamespaceLibsLink(*ns.value());
  if (!linked.ok()) {
    return linked.error();
  }
  return ns;
}

1.1.2 LibraryNamespaces::Create

  • 先调用android_create_namespace创建一个clns命名空间
  • 调用android_linker_namespace设置新创建的命名空间链接到system、APEX和vendor等命名空间
  • 设置链接到system、APEX等命名空间的共享库

Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version,
                                                         jobject class_loader, bool is_shared,
                                                         jstring dex_path_j,
                                                         jstring java_library_path,
                                                         jstring java_permitted_path,
                                                         jstring uses_library_list) {
  std::string library_path;  // empty string by default.
  std::string dex_path;

  ......

  // Create the app namespace
  NativeLoaderNamespace* parent_ns = FindParentNamespaceByClassLoader(env, class_loader);
  // Heuristic: the first classloader with non-empty library_path is assumed to
  // be the main classloader for app
  // TODO(b/139178525) remove this heuristic by determining this in LoadedApk (or its
  // friends) and then passing it down to here.
  bool is_main_classloader = app_main_namespace_ == nullptr && !library_path.empty();
  // Policy: the namespace for the main classloader is also used as the
  // anonymous namespace.
  bool also_used_as_anonymous = is_main_classloader;
  // Note: this function is executed with g_namespaces_mutex held, thus no
  // racing here.
  //创建命名空间,namespace_name为kClassloaderNamespaceName = "clns"
  auto app_ns = NativeLoaderNamespace::Create(
      namespace_name, library_path, permitted_path, parent_ns, is_shared,
      target_sdk_version < 24 /* is_exempt_list_enabled */, also_used_as_anonymous);
  if (!app_ns.ok()) {
    return app_ns.error();
  }
  // ... and link to other namespaces to allow access to some public libraries
  bool is_bridged = app_ns->IsBridged();

  auto system_ns = NativeLoaderNamespace::GetSystemNamespace(is_bridged);
  if (!system_ns.ok()) {
    return system_ns.error();
  }
  // 新创建的命名空间链接到system,共享库设置为system_exposed_libraries
  auto linked = app_ns->Link(&system_ns.value(), system_exposed_libraries);
  if (!linked.ok()) {
    return linked.error();
  }

  for (const auto&[apex_ns_name, public_libs] : apex_public_libraries()) {
    auto ns = NativeLoaderNamespace::GetExportedNamespace(apex_ns_name, is_bridged);
    // Even if APEX namespace is visible, it may not be available to bridged.
    if (ns.ok()) {
      // 新创建的命名空间链接到APEX,共享库设置为public_libs
      linked = app_ns->Link(&ns.value(), public_libs);
      if (!linked.ok()) {
        return linked.error();
      }
    }
  }

  // Give access to VNDK-SP libraries from the 'vndk' namespace for unbundled vendor apps.
  if (unbundled_app_origin == APK_ORIGIN_VENDOR && !vndksp_libraries_vendor().empty()) {
    auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkNamespaceName, is_bridged);
    if (vndk_ns.ok()) {
      // 新创建的命名空间链接到vndk,共享库设置为vndksp_libraries_vendor()
      linked = app_ns->Link(&vndk_ns.value(), vndksp_libraries_vendor());
      if (!linked.ok()) {
        return linked.error();
      }
    }
  }

  // Give access to VNDK-SP libraries from the 'vndk_product' namespace for unbundled product apps.
  if (unbundled_app_origin == APK_ORIGIN_PRODUCT && !vndksp_libraries_product().empty()) {
    auto vndk_ns = NativeLoaderNamespace::GetExportedNamespace(kVndkProductNamespaceName, is_bridged);
    if (vndk_ns.ok()) {
      linked = app_ns->Link(&vndk_ns.value(), vndksp_libraries_product());
      if (!linked.ok()) {
        return linked.error();
      }
    }
  }

  for (const std::string& each_jar_path : android::base::Split(dex_path, ":")) {
    auto apex_ns_name = FindApexNamespaceName(each_jar_path);
    if (apex_ns_name.ok()) {
      const auto& jni_libs = apex_jni_libraries(*apex_ns_name);
      if (jni_libs != "") {
        auto apex_ns = NativeLoaderNamespace::GetExportedNamespace(*apex_ns_name, is_bridged);
        if (apex_ns.ok()) {
          linked = app_ns->Link(&apex_ns.value(), jni_libs);
          if (!linked.ok()) {
            return linked.error();
          }
        }
      }
    }
  }

  auto vendor_libs = filter_public_libraries(target_sdk_version, uses_libraries,
                                             vendor_public_libraries());
  if (!vendor_libs.empty()) {
    auto vendor_ns = NativeLoaderNamespace::GetExportedNamespace(kVendorNamespaceName, is_bridged);
    // when vendor_ns is not configured, link to the system namespace
    auto target_ns = vendor_ns.ok() ? vendor_ns : system_ns;
    if (target_ns.ok()) {
      linked = app_ns->Link(&target_ns.value(), vendor_libs);
      if (!linked.ok()) {
        return linked.error();
      }
    }
  }

  auto product_libs = filter_public_libraries(target_sdk_version, uses_libraries,
                                              product_public_libraries());
  if (!product_libs.empty()) {
    auto target_ns = system_ns;
    if (is_product_vndk_version_defined()) {
      // If ro.product.vndk.version is defined, product namespace provides the product libraries.
      target_ns = NativeLoaderNamespace::GetExportedNamespace(kProductNamespaceName, is_bridged);
    }
    if (target_ns.ok()) {
      linked = app_ns->Link(&target_ns.value(), product_libs);
      if (!linked.ok()) {
        return linked.error();
      }
    } else {
      // The linkerconfig must have a problem on defining the product namespace in the system
      // section. Skip linking product namespace. This will not affect most of the apps. Only the
      // apps that requires the product public libraries will fail.
      ALOGW("Namespace for product libs not found: %s", target_ns.error().message().c_str());
    }
  }

  auto& emplaced = namespaces_.emplace_back(
      std::make_pair(env->NewWeakGlobalRef(class_loader), *app_ns));
  if (is_main_classloader) {
    app_main_namespace_ = &emplaced.second;
  }
  return &emplaced.second;
}

1.2 配置共享库位置

通过上面的代码跟踪我们会发现,其实在Android 11后,应用进程共享库的配置位置都在public_libraries.cpp

// art/libnativeloader/public_libraries.cpp
namespace {

constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt";
constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-";
constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
constexpr const char* kApexLibrariesConfigFile = "/linkerconfig/apex.libraries.config.txt";
constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
constexpr const char* kLlndkLibrariesFile = "/apex/com.android.vndk.v{}/etc/llndk.libraries.{}.txt";
constexpr const char* kVndkLibrariesFile = "/apex/com.android.vndk.v{}/etc/vndksp.libraries.{}.txt";

只要在命名空间对应的配置文件中加上自己需要的共享库即可

2. native进程

2.1 native 命名空间配置初始化

native进程的so库是直接通过dlopen打开的,而其对应的命名空间也是直接通过linker进行判断的,并没有调用到libnativeloader.so,因此native进程的配置方式也与应用进程不同

2.1.1 android_namespace_t::is_accessible

前一篇Android system — 链接器命名空间(linker namespace)源码分析 介绍了dlopen时会通过is_accessible进行权限判断

// bionic/linker/linker_namespaces.cpp
// Given an absolute path, can this library be loaded into this namespace?
bool android_namespace_t::is_accessible(const std::string& file) {
  //判断命名空间的is_isolated_,即是否为严格隔离,如果不是则权限检查通过。
  if (!is_isolated_) {
    return true;
  }
  //判断是否在允许名单中
  if (!allowed_libs_.empty()) {
    const char *lib_name = basename(file.c_str());
    if (std::find(allowed_libs_.begin(), allowed_libs_.end(), lib_name) == allowed_libs_.end()) {
      return false;
    }
  }
  //判断是否在ld_library_paths中(LD_LIBRARY_PATH设置)
  for (const auto& dir : ld_library_paths_) {
    if (file_is_in_dir(file, dir)) {
      return true;
    }
  }
  //判断是否在default_library_paths中
  for (const auto& dir : default_library_paths_) {
    if (file_is_in_dir(file, dir)) {
      return true;
    }
  }
  //判断是否在特权路径permitted_paths中
  for (const auto& dir : permitted_paths_) {
    if (file_is_under_dir(file, dir)) {
      return true;
    }
  }

  return false;
}

我们可以看到这里有个allowed_libs_,我们跟踪代码可以发现linker_namespaces.h中有set_allowed_libs接口

// bionic/linker/linker_namespaces.h 
  const std::vector<std::string>& get_allowed_libs() const { return allowed_libs_; }
  void set_allowed_libs(std::vector<std::string>&& allowed_libs) {
    allowed_libs_ = std::move(allowed_libs);
  }
  void set_allowed_libs(const std::vector<std::string>& allowed_libs) {
    allowed_libs_ = allowed_libs;
  }

2.1.2 init_default_namespaces

通过这个思路我们发现调用其实在init_default_namespaces

  • 初始化default命名空间
  • 初始化vndk等其他命名空间
  • 在命名空间之间建立链接
std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path) {
  g_default_namespace.set_name("(default)");

  soinfo* somain = solist_get_somain();

  const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
                                                       somain->load_bias);
  const char* bname = (interp != nullptr) ? basename(interp) : nullptr;

  g_is_asan = bname != nullptr &&
              (strcmp(bname, "linker_asan") == 0 ||
               strcmp(bname, "linker_asan64") == 0);

  const Config* config = nullptr;

  {
    std::string ld_config_file_path = get_ld_config_file_path(executable_path);
    INFO("[ Reading linker config \"%s\" ]", ld_config_file_path.c_str());
    ScopedTrace trace(("linker config " + ld_config_file_path).c_str());
    std::string error_msg;
    if (!Config::read_binary_config(ld_config_file_path.c_str(), executable_path, g_is_asan,
                                    &config, &error_msg)) {
      if (!error_msg.empty()) {
        DL_WARN("Warning: couldn't read '%s' for '%s' (using default configuration instead): %s",
                ld_config_file_path.c_str(), executable_path, error_msg.c_str());
      }
      config = nullptr;
    }
  }

  if (config == nullptr) {
    return init_default_namespace_no_config(g_is_asan);
  }

  const auto& namespace_configs = config->namespace_configs();
  std::unordered_map<std::string, android_namespace_t*> namespaces;

  // 1. Initialize default namespace
  const NamespaceConfig* default_ns_config = config->default_namespace_config();

  g_default_namespace.set_isolated(default_ns_config->isolated());
  g_default_namespace.set_default_library_paths(default_ns_config->search_paths());
  g_default_namespace.set_permitted_paths(default_ns_config->permitted_paths());

  namespaces[default_ns_config->name()] = &g_default_namespace;
  if (default_ns_config->visible()) {
    g_exported_namespaces[default_ns_config->name()] = &g_default_namespace;
  }

  // 2. Initialize other namespaces

  for (auto& ns_config : namespace_configs) {
    if (namespaces.find(ns_config->name()) != namespaces.end()) {
      continue;
    }

    android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t();
    ns->set_name(ns_config->name());
    ns->set_isolated(ns_config->isolated());
    ns->set_default_library_paths(ns_config->search_paths());
    ns->set_permitted_paths(ns_config->permitted_paths());
    ns->set_allowed_libs(ns_config->allowed_libs());

    namespaces[ns_config->name()] = ns;
    if (ns_config->visible()) {
      g_exported_namespaces[ns_config->name()] = ns;
    }
  }

  // 3. Establish links between namespaces
  for (auto& ns_config : namespace_configs) {
    auto it_from = namespaces.find(ns_config->name());
    CHECK(it_from != namespaces.end());
    android_namespace_t* namespace_from = it_from->second;
    for (const NamespaceLinkConfig& ns_link : ns_config->links()) {
      auto it_to = namespaces.find(ns_link.ns_name());
      CHECK(it_to != namespaces.end());
      android_namespace_t* namespace_to = it_to->second;
      if (ns_link.allow_all_shared_libs()) {
        link_namespaces_all_libs(namespace_from, namespace_to);
      } else {
        link_namespaces(namespace_from, namespace_to, ns_link.shared_libs().c_str());
      }
    }
  }
  // we can no longer rely on the fact that libdl.so is part of default namespace
  // this is why we want to add ld-android.so to all namespaces from ld.config.txt
  soinfo* ld_android_so = solist_get_head();

  // we also need vdso to be available for all namespaces (if present)
  soinfo* vdso = solist_get_vdso();
  for (auto it : namespaces) {
    if (it.second != &g_default_namespace) {
      it.second->add_soinfo(ld_android_so);
      if (vdso != nullptr) {
        it.second->add_soinfo(vdso);
      }
      // somain and ld_preloads are added to these namespaces after LD_PRELOAD libs are linked
    }
  }

  set_application_target_sdk_version(config->target_sdk_version());

  std::vector<android_namespace_t*> created_namespaces;
  created_namespaces.reserve(namespaces.size());
  for (const auto& kv : namespaces) {
    created_namespaces.push_back(kv.second);
  }
  return created_namespaces;
}
}

这里面我们发现配置文件是通过读取配置文件进行配置的,配置文件ld_config_file_path在Android 11后便改为/linkerconfig/ld.config.txt

static const char* const kLdConfigArchFilePath = "/system/etc/ld.config." ABI_STRING ".txt";

static const char* const kLdConfigFilePath = "/system/etc/ld.config.txt";
static const char* const kLdConfigVndkLiteFilePath = "/system/etc/ld.config.vndk_lite.txt";

static const char* const kLdGeneratedConfigFilePath = "/linkerconfig/ld.config.txt";

static std::string get_ld_config_file_path(const char* executable_path) {
#ifdef USE_LD_CONFIG_FILE
  // This is a debugging/testing only feature. Must not be available on
  // production builds.
  const char* ld_config_file_env = getenv("LD_CONFIG_FILE");
  if (ld_config_file_env != nullptr && file_exists(ld_config_file_env)) {
    return ld_config_file_env;
  }
#endif

  std::string path = get_ld_config_file_apex_path(executable_path);
  if (!path.empty()) {
    if (file_exists(path.c_str())) {
      return path;
    }
    DL_WARN("Warning: couldn't read config file \"%s\" for \"%s\"",
            path.c_str(), executable_path);
  }

  path = kLdConfigArchFilePath;
  if (file_exists(path.c_str())) {
    return path;
  }

  if (file_exists(kLdGeneratedConfigFilePath)) {
    return kLdGeneratedConfigFilePath;
  }

  if (is_linker_config_expected(executable_path)) {
    DL_WARN("Warning: failed to find generated linker configuration from \"%s\"",
            kLdGeneratedConfigFilePath);
  }

  path = get_ld_config_file_vndk_path();
  if (file_exists(path.c_str())) {
    return path;
  }

  return kLdConfigFilePath;
}

2.2 配置共享库位置

上一章我们提到了配置文件位置为/linkerconfig/ld.config.txt,
在Android 11以前ld.config.txt是通过静态文件配置的,而Android 11是改为linkerconfig动态生成

我们以一个vendor/bin下的native 程序为例,要使用system/lib下的库

在Android system — Android链接器命名空间(Android 11后)中,我们提到了“目录-区段”映射属性dir.name,指向 [name] 区段所应用到的目录的路径,其实就是baseconfig.cc中定义的dirToSection

// system/linkerconfig/contents/configuration/baseconfig.cc

android::linkerconfig::modules::Configuration CreateBaseConfiguration(
    Context& ctx) {
  std::vector<Section> sections;

  ctx.SetCurrentLinkerConfigType(LinkerConfigType::Default);

  // Don't change the order here. The first pattern that matches with the
  // absolute path of an executable is selected.
  std::vector<DirToSection> dirToSection = {
      {"/system/bin/", "system"},
      {"/system/xbin/", "system"},
      {Var("SYSTEM_EXT") + "/bin/", "system"},

      // Processes from the product partition will have a separate section if
      // PRODUCT_PRODUCT_VNDK_VERSION is defined. Otherwise, they are run from
      // the "system" section.
      {Var("PRODUCT") + "/bin/", "product"},

      {"/odm/bin/", "vendor"},
      {"/vendor/bin/", "vendor"},
      {"/data/nativetest/odm", "vendor"},
      {"/data/nativetest64/odm", "vendor"},
      {"/data/benchmarktest/odm", "vendor"},
      {"/data/benchmarktest64/odm", "vendor"},
      {"/data/nativetest/vendor", "vendor"},
      {"/data/nativetest64/vendor", "vendor"},
      {"/data/benchmarktest/vendor", "vendor"},
      {"/data/benchmarktest64/vendor", "vendor"},

      {"/data/nativetest/unrestricted", "unrestricted"},
      {"/data/nativetest64/unrestricted", "unrestricted"},

      // Create isolated namespace for development purpose.
      // This isolates binary from the system so binaries and libraries from
      // this location can be separated from system libraries.
      {"/data/local/tmp/isolated", "isolated"},

      // Create directories under shell-writable /data/local/tests for
      // each namespace in order to run tests.
      {"/data/local/tests/product", "product"},
      {"/data/local/tests/system", "system"},
      {"/data/local/tests/unrestricted", "unrestricted"},
      {"/data/local/tests/vendor", "vendor"},

      // TODO(b/123864775): Ensure tests are run from one of the subdirectories
      // above.  Then clean this up.
      {"/data/local/tmp", "unrestricted"},

      {"/postinstall", "postinstall"},
      // Fallback entry to provide APEX namespace lookups for binaries anywhere
      // else. This must be last.
      {"/data", "system"},
      // TODO(b/168556887): Remove this when we have a dedicated section for
      // binaries in APKs
      {Var("PRODUCT") + "/app/", "system"},
  };

  sections.emplace_back(BuildSystemSection(ctx));
  if (ctx.IsVndkAvailable()) {
    sections.emplace_back(BuildVendorSection(ctx));
    if (android::linkerconfig::modules::IsProductVndkVersionDefined()) {
      sections.emplace_back(BuildProductSection(ctx));
    } else {
      RedirectSection(dirToSection, "product", "system");
    }
  } else {
    RemoveSection(dirToSection, "product");
    RemoveSection(dirToSection, "vendor");
  }

  sections.emplace_back(BuildUnrestrictedSection(ctx));
  sections.emplace_back(BuildPostInstallSection(ctx));

  sections.emplace_back(BuildIsolatedSection(ctx));

  return android::linkerconfig::modules::Configuration(std::move(sections),
                                                       dirToSection);
}

因为我们是vendor/bin下面的程序,所以可以看出来我们是vendor section,因此我们看BuildVendorSection即可。

// system/linkerconfig/contents/section/vendor.cc

Section BuildVendorSection(Context& ctx) {
  ctx.SetCurrentSection(SectionType::Vendor);
  std::vector<Namespace> namespaces;

  namespaces.emplace_back(BuildVendorDefaultNamespace(ctx));
  namespaces.emplace_back(BuildVndkNamespace(ctx, VndkUserPartition::Vendor));
  namespaces.emplace_back(BuildSystemNamespace(ctx));
  namespaces.emplace_back(BuildRsNamespace(ctx));

  if (android::linkerconfig::modules::IsVndkInSystemNamespace()) {
    namespaces.emplace_back(BuildVndkInSystemNamespace(ctx));
  }

  std::set<std::string> visible_apexes;

  // APEXes with public libs should be visible
  for (const auto& apex : ctx.GetApexModules()) {
    if (apex.public_libs.size() > 0) {
      visible_apexes.insert(apex.name);
    }
  }

  android::linkerconfig::modules::LibProviders libs_providers = {};
  if (ctx.IsVndkAvailable()) {
    libs_providers[":vndk"] = android::linkerconfig::modules::LibProvider{
        "vndk",
        std::bind(BuildVndkNamespace, ctx, VndkUserPartition::Vendor),
        {Var("VNDK_SAMEPROCESS_LIBRARIES_VENDOR"),
         Var("VNDK_CORE_LIBRARIES_VENDOR")},
    };
  }

  return BuildSection(
      ctx, "vendor", std::move(namespaces), visible_apexes, libs_providers);
}

我们可以看到最后调用了BuildSection,在其中调用了AddStandardSystemLinks 添加system的共享库

// system/linkerconfig/contents/section/sectionbuilder.cc

Section BuildSection(const Context& ctx, const std::string& name,
                     std::vector<Namespace>&& namespaces,
                     const std::set<std::string>& visible_apexes,
                     const LibProviders& providers) {
  // add additional visible APEX namespaces
  for (const auto& apex : ctx.GetApexModules()) {
    if (visible_apexes.find(apex.name) == visible_apexes.end() &&
        !apex.visible) {
      continue;
    }
    if (auto it = std::find_if(
            namespaces.begin(),
            namespaces.end(),
            [&apex](auto& ns) { return ns.GetName() == apex.namespace_name; });
        it == namespaces.end()) {
      auto ns = ctx.BuildApexNamespace(apex, true);
      namespaces.push_back(std::move(ns));
    } else {
      // override "visible" when the apex is already created
      it->SetVisible(true);
    }
  }

  // resolve provide/require constraints
  Section section(std::move(name), std::move(namespaces));
  if (auto res = section.Resolve(ctx, providers); !res.ok()) {
    LOG(ERROR) << res.error();
  }

  AddStandardSystemLinks(ctx, &section); // 添加通用的system link共享库
  return section;
}

在kBionicLibs 加上我们需要的库即可

 // system/linkerconfig/contents/common/system_links.cc

namespace {
const std::vector<std::string> kBionicLibs = {
    "libc.so",
    "libdl.so",
    "libdl_android.so",
    "libm.so",
};
}  // namespace

namespace android {
namespace linkerconfig {
namespace contents {

using android::linkerconfig::modules::Namespace;
using android::linkerconfig::modules::Section;

void AddStandardSystemLinks(const Context& ctx, Section* section) {
  const bool debuggable = android::base::GetBoolProperty("ro.debuggable", false);
  const std::string system_ns_name = ctx.GetSystemNamespaceName();
  const bool is_section_vndk_enabled = ctx.IsSectionVndkEnabled();
  section->ForEachNamespaces([&](Namespace& ns) {
    if (ns.GetName() != system_ns_name) {
      ns.GetLink(system_ns_name).AddSharedLib(kBionicLibs);
      if (!is_section_vndk_enabled || ns.GetName() != "default") {
        // TODO(b/185199923) remove the default value
        ns.GetLink(system_ns_name)
            .AddSharedLib(Var("SANITIZER_RUNTIME_LIBRARIES", ""));
      }
      if (debuggable) {
        // Library on the system image that can be dlopened for debugging purposes.
        ns.GetLink(system_ns_name).AddSharedLib("libfdtrack.so");
      }
    }
  });
}

}  // namespace contents
}  // namespace linkerconfig
}  // namespace android

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/588787.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

深度学习之基于CIFAR10图像分类可视化

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习之基于CIFAR-10图像分类可视化项目简介 一、项目背景 随着深度学习和计算机视觉技术的飞速发展&#xff…

边缘计算含义与应用简析

边缘计算概述 边缘计算使数据存储和处理靠近生成或收集数据的位置&#xff0c;而不是在位于数千公里的服务器上。它将通过保持灵活性在边缘无缝可靠地部署服务。它比云计算更安全&#xff0c;因为不需要传输数据。因此&#xff0c;在将数据从边缘移动到云端时&#xff0c;不用…

基于React实现B站评论区

今天继续来学习一下React&#xff0c;使用React实现B站评论区&#xff0c;如下图&#xff1a; 在使用React开发类似B站评论区的功能时&#xff0c;我们需要考虑以下几个关键点来构建一个基本的评论系统&#xff1a; 1. 设计组件结构 首先&#xff0c;设计组件结构是关键。至少…

什么是弹性云服务器(ECS)

弹性云服务器&#xff08;Elastic Cloud Server&#xff0c;ECS&#xff09;是由CPU、内存、操作系统、云硬盘组成的基础的计算组件。弹性云服务器创建成功后&#xff0c;您就可以像使用自己的本地PC或物理服务器一样&#xff0c;在云上使用弹性云服务器。 云服务器ECS&#x…

Re71:读论文 Sequence to Sequence Learning with Neural Networks

诸神缄默不语-个人CSDN博文目录 诸神缄默不语的论文阅读笔记和分类 论文名称&#xff1a;Sequence to Sequence Learning with Neural Networks ArXiv下载地址&#xff1a;https://arxiv.org/abs/1409.3215 本文是2014年NeurIPS论文&#xff08;那时候这个会还叫NIPS&#xf…

HBase的简单学习四

一 HBase的进阶 1.1 hbase的写流程 Hbase读取数据的流程&#xff1a; 1&#xff09;是由客户端发起读取数据的请求&#xff0c;首先会与zookeeper建立连接 2&#xff09;从zookeeper中获取一个hbase:meta表位置信息&#xff0c;被哪一个regionserver所管理着 hbase:meta表…

C语言:循环结构

循环结构 1. for循环概念举例示例结果分析 补充 2. while循环概念举例示例结果分析补充 3. do-while循环概念举例示例结果分析 补充 4.循环控制举例示例结果分析 C语言中的循环结构是一种重要的编程构造&#xff0c;它允许我们重复执行一段代码&#xff0c;直到满足某个条件为止…

Hive优化以及相关参数设置

1.表层面设计优化 1.1 表分区 分区表实际上就是对应一个 HDFS 文件系统上的独立的文件夹&#xff0c;该文件夹下是该分区所有的数据文件。Hive 中的分区就是分目录&#xff0c;把一个大的数据集根据业务需要分割成小的数据集。在查询时通过 WHERE 子句中的表达式选择查询所需要…

Angular基础-搭建Angular运行环境

这篇文章介绍了在Angular项目中进行开发环境搭建的关键步骤。包括node.js安装和配置、安装Angular CLI工具、安装angular-router、创建Angular项目等步骤。这篇文章为读者提供了清晰的指南&#xff0c;帮助他们快速搭建Angular开发环境&#xff0c;为后续的项目开发奠定基础。 …

Python中动画显示与gif生成

1. 动画生成 主要使用的是 matplotlib.animation &#xff0c;具体示例如下&#xff1a; import matplotlib.pyplot as plt import matplotlib.animation as animation import numpy as np fig, ax plt.subplots() t np.linspace(0, 3, 40) g -9.81 v0 12 z g * t**2 / …

【项目学习01_2024.05.02_Day04】

学习笔记 4 课程分类查询4.1需求分析4.2 接口定义4.3 接口开发4.3.1 树型表查询4.3.2 开发Mapper 4 课程分类查询 4.1需求分析 有课程分类的需求 course_category课程分类表的结构 这张表是一个树型结构&#xff0c;通过父结点id将各元素组成一个树。 利用mybatis-plus-gen…

第十五届蓝桥杯Java软件开发大学B组自我经验小结

自我介绍 23届大一 双非 计院科班 软件工程 江苏人在吉林上大学 Java蒟蒻 在学校的宣传下 有幸参加了第十五届蓝桥杯Java大学b组省赛 蓝桥杯说明 就是一个算法比赛吧 考试时间9.00到1.00 四小时 带准考证和身份证和笔 草稿纸会发 赛制是IOC就是不会给任何反馈 就是你…

IDEA 创建Servlet-HelloWorldServlet

servlet 1.创建空项目2.配置web项目3.配置Tomcat4.加载Tomcat包5.创建HelloWorldServlet类6.配置web.xml7.运行get与post请求 1.创建空项目 2.配置web项目 3.配置Tomcat 4.加载Tomcat包 5.创建HelloWorldServlet类 public class controller extends HttpServlet {Override//get…

java入门-包装类

包装类 Java语言是一个面向对象的语言&#xff0c;但是Java中的基本数据类型却是不面向对象的。基本类型的数据不具备"对象"的特性&#xff08;没有属性和方法可以调用&#xff09;&#xff0c;因此&#xff0c;java为每种数据类型分别设计了对应的类&#xff0c;即*…

《与 Apollo 共创生态——Apollo7周年大会干货分享》

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 阿波罗X企业自动驾驶解决方案自动驾驶技术提升与挑战自动驾驶系统功能与性能的详细解析<td alig…

在智慧城市的建设中智能车载监控录像机发挥什么作用

引言 随着科技的快速发展&#xff0c;智慧城市的概念逐渐深入人心。在智慧城市的建设中&#xff0c;智能车载监控录像机作为一种重要的技术工具&#xff0c;发挥着越来越重要的作用。本文将从多个方面探讨智能车载监控录像机在智慧城市建设中的作用。 一、智能车载监控录像机概…

纯血鸿蒙APP实战开发——主页瀑布流实现

介绍 本示例介绍使用ArkUIWaterFlow组件和LazyForEach实现瀑布流场景。该场景多用于购物、资讯类应用。 效果图预览 使用说明 加载完成后显示整个列表&#xff0c;超过一屏时可以上下滑动。 实现思路 创建WaterFlowDataSource类&#xff0c;实现IDataSource接口的对象&…

C语言/数据结构——每日一题(链表的中间节点)

一.前言 今天我在LeetCode刷到了一道单链表题&#xff0c;想着和大家分享一下这道题&#xff1a;https://leetcode.cn/problems/middle-of-the-linked-list。废话不多说让我们开始今天的知识分享吧。 二.正文 1.1题目描述 1.2题目分析 这道题有一个非常简便的方法——快慢指…

Vue+Element UI el-progress进度条内显示自定义数字及文字

需求 进度条内展示 具体的数字值&#xff0c;进度条外展示 百分比数值 数据 data() {return {reNum: 3214,rePer:40,warmPer: 40,warmNum:2132,}}因为样式要求&#xff0c;显示的百分数也是自己写的哈 &#xff0c;没有用进度条自带的 代码 <div class"pick"&g…

2024五一杯数学建模A题思路分析-钢板最优切割路径问题

文章目录 1 赛题选题分析 2 解题思路3 最新思路更新 1 赛题 A题 钢板最优切割路径问题 提高钢板下料切割过程中的工作效率&#xff0c;是模具加工企业降低成本和增加经济效益的重要途径&#xff0c;其中钢板切割的路径规划是钢板切割过程的一个关键环节。 钢板切割就是使用特殊…