Skip to content

Commit

Permalink
Refactorings to make more logic available at the top level in
Browse files Browse the repository at this point in the history
native_loader.cpp.

- Make the code that determines the partition from the dex path
  available to code in native_loader.cpp, and rename it since it'll be
  applied to arbitrary paths, not just APK's.
- Move the linker namespace constants to a header file.
- Various other minor code cleanups.

To prepare for a later CL that needs to access these things from
OpenNativeLibrary. No functional changes.

Test: atest libnativeloader_e2e_tests libnativeloader_test
Bug: 237577392
Change-Id: Ifc762bf6d4664b2d477c4ed3af58f149fb4c1189
  • Loading branch information
marstj committed Feb 15, 2024
1 parent 149b912 commit 8a9b51e
Show file tree
Hide file tree
Showing 7 changed files with 228 additions and 184 deletions.
183 changes: 84 additions & 99 deletions libnativeloader/library_namespaces.cpp

Large diffs are not rendered by default.

45 changes: 35 additions & 10 deletions libnativeloader/library_namespaces.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,40 @@
#error "Not available for host or linux target"
#endif

#define LOG_TAG "nativeloader"

#include "native_loader_namespace.h"

#include <list>
#include <string>

#include <android-base/result.h>
#include <jni.h>
#include "android-base/result.h"
#include "jni.h"
#include "native_loader_namespace.h"

namespace android::nativeloader {

using android::base::Result;

// The device may be configured to have the vendor libraries loaded to a separate namespace.
// For historical reasons this namespace was named sphal but effectively it is intended
// to use to load vendor libraries to separate namespace with controlled interface between
// vendor and system namespaces.
constexpr const char* kVendorNamespaceName = "sphal";
// Similar to sphal namespace, product namespace provides some product libraries.
constexpr const char* kProductNamespaceName = "product";

// vndk namespace for unbundled vendor apps
constexpr const char* kVndkNamespaceName = "vndk";
// vndk_product namespace for unbundled product apps
constexpr const char* kVndkProductNamespaceName = "vndk_product";

// API domains, roughly corresponding to partitions. Interdependencies between
// these must follow API restrictions, while intradependencies do not.
using ApiDomain = enum {
API_DOMAIN_DEFAULT = 0, // Locations other than those below, in particular for ordinary apps
API_DOMAIN_VENDOR = 1, // Vendor partition
API_DOMAIN_PRODUCT = 2, // Product partition
};

nativeloader::ApiDomain GetApiDomainFromPath(const std::string& path);

// LibraryNamespaces is a singleton object that manages NativeLoaderNamespace
// objects for an app process. Its main job is to create (and configure) a new
// NativeLoaderNamespace object for a Java ClassLoader, and to find an existing
Expand All @@ -53,10 +73,15 @@ class LibraryNamespaces {
initialized_ = false;
app_main_namespace_ = nullptr;
}
Result<NativeLoaderNamespace*> Create(JNIEnv* env, uint32_t target_sdk_version,
jobject class_loader, bool is_shared, jstring dex_path,
jstring java_library_path, jstring java_permitted_path,
jstring uses_library_list);
Result<NativeLoaderNamespace*> Create(JNIEnv* env,
uint32_t target_sdk_version,
jobject class_loader,
ApiDomain api_domain,
bool is_shared,
const std::string& dex_path,
jstring library_path_j,
jstring permitted_path_j,
jstring uses_library_list_j);
NativeLoaderNamespace* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);

private:
Expand Down
130 changes: 81 additions & 49 deletions libnativeloader/native_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@
#include <string>
#include <vector>

#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/strings.h>
#include <android-base/thread_annotations.h>
#include <nativebridge/native_bridge.h>
#include <nativehelper/scoped_utf_chars.h>
#include "android-base/file.h"
#include "android-base/macros.h"
#include "android-base/strings.h"
#include "android-base/thread_annotations.h"
#include "nativebridge/native_bridge.h"
#include "nativehelper/scoped_utf_chars.h"
#include "public_libraries.h"

#ifdef ART_TARGET_ANDROID
#include <log/log.h>
#include "library_namespaces.h"
#include "log/log.h"
#include "nativeloader/dlext_namespaces.h"
#endif

Expand All @@ -46,6 +47,9 @@ namespace {

#if defined(ART_TARGET_ANDROID)

using ::android::base::Result;
using ::android::nativeloader::LibraryNamespaces;

// NATIVELOADER_DEFAULT_NAMESPACE_LIBS is an environment variable that can be
// used to list extra libraries (separated by ":") that libnativeloader will
// load from the default namespace. The libraries must be listed without paths,
Expand All @@ -62,14 +66,12 @@ namespace {
// test libraries that depend on ART internal libraries.
constexpr const char* kNativeloaderExtraLibs = "nativeloader-extra-libs";

using android::nativeloader::LibraryNamespaces;

std::mutex g_namespaces_mutex;
LibraryNamespaces* g_namespaces = new LibraryNamespaces;
NativeLoaderNamespace* g_nativeloader_extra_libs_namespace = nullptr;
LibraryNamespaces* g_namespaces GUARDED_BY(g_namespaces_mutex) = new LibraryNamespaces;
NativeLoaderNamespace* g_nativeloader_extra_libs_namespace GUARDED_BY(g_namespaces_mutex) = nullptr;

android_namespace_t* FindExportedNamespace(const char* caller_location) {
auto name = nativeloader::FindApexNamespaceName(caller_location);
Result<std::string> name = nativeloader::FindApexNamespaceName(caller_location);
if (name.ok()) {
android_namespace_t* boot_namespace = android_get_exported_namespace(name->c_str());
LOG_ALWAYS_FATAL_IF((boot_namespace == nullptr),
Expand Down Expand Up @@ -139,20 +141,22 @@ Result<void*> TryLoadNativeloaderExtraLib(const char* path) {
Result<NativeLoaderNamespace*> CreateClassLoaderNamespaceLocked(JNIEnv* env,
int32_t target_sdk_version,
jobject class_loader,
nativeloader::ApiDomain api_domain,
bool is_shared,
jstring dex_path,
jstring library_path,
jstring permitted_path,
jstring uses_library_list)
const std::string& dex_path,
jstring library_path_j,
jstring permitted_path_j,
jstring uses_library_list_j)
REQUIRES(g_namespaces_mutex) {
Result<NativeLoaderNamespace*> ns = g_namespaces->Create(env,
target_sdk_version,
class_loader,
api_domain,
is_shared,
dex_path,
library_path,
permitted_path,
uses_library_list);
library_path_j,
permitted_path_j,
uses_library_list_j);
if (!ns.ok()) {
return ns;
}
Expand All @@ -163,7 +167,7 @@ Result<NativeLoaderNamespace*> CreateClassLoaderNamespaceLocked(JNIEnv* env,
return ns;
}

#endif // #if defined(ART_TARGET_ANDROID)
#endif // ART_TARGET_ANDROID

} // namespace

Expand All @@ -183,36 +187,63 @@ void ResetNativeLoader() {
#endif
}

jstring CreateClassLoaderNamespace(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) {
jstring 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) {
#if defined(ART_TARGET_ANDROID)
std::string dex_path;
if (dex_path_j != nullptr) {
ScopedUtfChars dex_path_chars(env, dex_path_j);
dex_path = dex_path_chars.c_str();
}
nativeloader::ApiDomain api_domain = nativeloader::GetApiDomainFromPath(dex_path);

std::lock_guard<std::mutex> guard(g_namespaces_mutex);
Result<NativeLoaderNamespace*> ns = CreateClassLoaderNamespaceLocked(env,
target_sdk_version,
class_loader,
api_domain,
is_shared,
dex_path,
library_path,
permitted_path,
uses_library_list);
library_path_j,
permitted_path_j,
uses_library_list_j);
if (!ns.ok()) {
return env->NewStringUTF(ns.error().message().c_str());
}

#else
UNUSED(env, target_sdk_version, class_loader, is_shared, dex_path, library_path, permitted_path,
uses_library_list);
UNUSED(env,
target_sdk_version,
class_loader,
is_shared,
dex_path_j,
library_path_j,
permitted_path_j,
uses_library_list_j);
#endif

return nullptr;
}

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) {
void* 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) {
#if defined(ART_TARGET_ANDROID)
UNUSED(target_sdk_version);

if (class_loader == nullptr) {
// class_loader is null only for the boot class loader (see
// IsBootClassLoader call in JavaVMExt::LoadNativeLibrary), i.e. the caller
// is in the boot classpath.
*needs_native_bridge = false;
if (caller_location != nullptr) {
android_namespace_t* boot_namespace = FindExportedNamespace(caller_location);
Expand Down Expand Up @@ -244,8 +275,6 @@ void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* pat

// 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());
Expand All @@ -254,43 +283,46 @@ void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* pat
}

std::lock_guard<std::mutex> guard(g_namespaces_mutex);
NativeLoaderNamespace* ns;
NativeLoaderNamespace* ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader);

if ((ns = g_namespaces->FindNamespaceByClassLoader(env, class_loader)) == nullptr) {
if (ns == 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.
const std::string empty_dex_path;
Result<NativeLoaderNamespace*> isolated_ns =
CreateClassLoaderNamespaceLocked(env,
target_sdk_version,
class_loader,
nativeloader::API_DOMAIN_DEFAULT,
/*is_shared=*/false,
/*dex_path=*/nullptr,
library_path,
empty_dex_path,
library_path_j,
/*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;
ns = isolated_ns.value();
}
}

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

#else // !ART_TARGET_ANDROID
UNUSED(env, target_sdk_version, class_loader, caller_location);

// Do some best effort to emulate library-path support. It will not
// work for dependencies.
//
// Note: null has a special meaning and must be preserved.
std::string c_library_path; // Empty string by default.
if (library_path != nullptr && path != nullptr && path[0] != '/') {
ScopedUtfChars library_path_utf_chars(env, library_path);
c_library_path = library_path_utf_chars.c_str();
std::string library_path; // Empty string by default.
if (library_path_j != nullptr && path != nullptr && path[0] != '/') {
ScopedUtfChars library_path_utf_chars(env, library_path_j);
library_path = library_path_utf_chars.c_str();
}

std::vector<std::string> library_paths = base::Split(c_library_path, ":");
std::vector<std::string> library_paths = base::Split(library_path, ":");

for (const std::string& lib_path : library_paths) {
*needs_native_bridge = false;
Expand Down Expand Up @@ -323,7 +355,7 @@ void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* pat
}
}
return nullptr;
#endif
#endif // !ART_TARGET_ANDROID
}

bool CloseNativeLibrary(void* handle, const bool needs_native_bridge, char** error_msg) {
Expand Down Expand Up @@ -351,7 +383,7 @@ void NativeLoaderFreeErrorMessage(char* msg) {
#if defined(ART_TARGET_ANDROID)
void* OpenNativeLibraryInNamespace(NativeLoaderNamespace* ns, const char* path,
bool* needs_native_bridge, char** error_msg) {
auto handle = ns->Load(path);
Result<void*> handle = ns->Load(path);
if (!handle.ok() && error_msg != nullptr) {
*error_msg = strdup(handle.error().message().c_str());
}
Expand Down Expand Up @@ -397,4 +429,4 @@ void LinkNativeLoaderNamespaceToExportedNamespaceLibrary(struct NativeLoaderName

#endif // ART_TARGET_ANDROID

}; // namespace android
} // namespace android
2 changes: 1 addition & 1 deletion libnativeloader/native_loader_lazy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void* GetLibHandle() {

template <typename FuncPtr>
FuncPtr GetFuncPtr(const char* function_name) {
auto f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
FuncPtr f = reinterpret_cast<FuncPtr>(dlsym(GetLibHandle(), function_name));
LOG_FATAL_IF(f == nullptr, "Failed to get address of %s: %s", function_name, dlerror());
return f;
}
Expand Down
8 changes: 4 additions & 4 deletions libnativeloader/native_loader_namespace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ std::string GetLinkerError(bool is_bridged) {
Result<NativeLoaderNamespace> NativeLoaderNamespace::GetExportedNamespace(const std::string& name,
bool is_bridged) {
if (!is_bridged) {
auto raw = android_get_exported_namespace(name.c_str());
android_namespace_t* raw = android_get_exported_namespace(name.c_str());
if (raw != nullptr) {
return NativeLoaderNamespace(name, raw);
}
} else {
auto raw = NativeBridgeGetExportedNamespace(name.c_str());
native_bridge_namespace_t* raw = NativeBridgeGetExportedNamespace(name.c_str());
if (raw != nullptr) {
return NativeLoaderNamespace(name, raw);
}
Expand All @@ -69,7 +69,7 @@ Result<NativeLoaderNamespace> NativeLoaderNamespace::GetExportedNamespace(const
// "system" for those in the Runtime APEX. Try "system" first since
// "default" always exists.
Result<NativeLoaderNamespace> NativeLoaderNamespace::GetSystemNamespace(bool is_bridged) {
auto ns = GetExportedNamespace(kSystemNamespaceName, is_bridged);
Result<NativeLoaderNamespace> ns = GetExportedNamespace(kSystemNamespaceName, is_bridged);
if (ns.ok()) return ns;
ns = GetExportedNamespace(kDefaultNamespaceName, is_bridged);
if (ns.ok()) return ns;
Expand All @@ -96,7 +96,7 @@ Result<NativeLoaderNamespace> NativeLoaderNamespace::Create(
}

// Fall back to the system namespace if no parent is set.
auto system_ns = GetSystemNamespace(is_bridged);
Result<NativeLoaderNamespace> system_ns = GetSystemNamespace(is_bridged);
if (!system_ns.ok()) {
return system_ns.error();
}
Expand Down
Loading

0 comments on commit 8a9b51e

Please sign in to comment.