diff options
author | 2020-03-03 00:46:50 +0900 | |
---|---|---|
committer | 2020-03-04 05:56:39 +0000 | |
commit | 538f99ab285c1440969b9b3331fc0ce750c0d316 (patch) | |
tree | 018e9cbfc08ba39b3f35e457a8658fcc4cc8db75 | |
parent | 6fc471e510d6a4e9c31fcab6c0542e2efdf50099 (diff) |
Loading JNI libraries in an APEX
To load JNI libraries in an APEX, libnativeloader relies on
jni.config.txt file which contains available JNI libraries for
APEX namespaces:
com_android_foo libfoo_jni.so:...
com_android_bar libbar_jni.so:...
This file is generated by linkerconfig.
Bug: 143733063
Test: cuttlestone boots
(For now, no behavioral changes because jni.config.txt is empty)
Change-Id: I066de90a73875118be53972e50d076061922d762
-rw-r--r-- | libnativeloader/library_namespaces.cpp | 69 | ||||
-rw-r--r-- | libnativeloader/library_namespaces.h | 2 | ||||
-rw-r--r-- | libnativeloader/native_loader.cpp | 20 | ||||
-rw-r--r-- | libnativeloader/native_loader_test.cpp | 24 | ||||
-rw-r--r-- | libnativeloader/public_libraries.cpp | 41 | ||||
-rw-r--r-- | libnativeloader/public_libraries.h | 5 |
6 files changed, 128 insertions, 33 deletions
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp index dfbdefd53d..44b34583a6 100644 --- a/libnativeloader/library_namespaces.cpp +++ b/libnativeloader/library_namespaces.cpp @@ -26,6 +26,7 @@ #include <android-base/logging.h> #include <android-base/macros.h> #include <android-base/properties.h> +#include <android-base/result.h> #include <android-base/strings.h> #include <nativehelper/scoped_utf_chars.h> @@ -36,6 +37,9 @@ namespace android::nativeloader { namespace { + +constexpr const char* kApexPath = "/apex/"; + // 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 @@ -94,23 +98,17 @@ jobject GetParentClassLoader(JNIEnv* env, jobject class_loader) { return env->CallObjectMethod(class_loader, get_parent); } -ApkOrigin GetApkOriginFromDexPath(JNIEnv* env, jstring dex_path) { +ApkOrigin GetApkOriginFromDexPath(const std::string& dex_path) { ApkOrigin apk_origin = APK_ORIGIN_DEFAULT; + if (std::regex_search(dex_path, kVendorDexPathRegex)) { + apk_origin = APK_ORIGIN_VENDOR; + } + if (std::regex_search(dex_path, kProductDexPathRegex)) { + LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR, + "Dex path contains both vendor and product partition : %s", + dex_path.c_str()); - if (dex_path != nullptr) { - ScopedUtfChars dex_path_utf_chars(env, dex_path); - - if (std::regex_search(dex_path_utf_chars.c_str(), kVendorDexPathRegex)) { - apk_origin = APK_ORIGIN_VENDOR; - } - - if (std::regex_search(dex_path_utf_chars.c_str(), kProductDexPathRegex)) { - LOG_ALWAYS_FATAL_IF(apk_origin == APK_ORIGIN_VENDOR, - "Dex path contains both vendor and product partition : %s", - dex_path_utf_chars.c_str()); - - apk_origin = APK_ORIGIN_PRODUCT; - } + apk_origin = APK_ORIGIN_PRODUCT; } return apk_origin; } @@ -141,17 +139,23 @@ void LibraryNamespaces::Initialize() { Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t target_sdk_version, jobject class_loader, bool is_shared, - jstring dex_path, + jstring dex_path_j, jstring java_library_path, jstring java_permitted_path) { std::string library_path; // empty string by default. + std::string dex_path; if (java_library_path != nullptr) { ScopedUtfChars library_path_utf_chars(env, java_library_path); library_path = library_path_utf_chars.c_str(); } - ApkOrigin apk_origin = GetApkOriginFromDexPath(env, dex_path); + if (dex_path_j != nullptr) { + ScopedUtfChars dex_path_chars(env, dex_path_j); + dex_path = dex_path_chars.c_str(); + } + + ApkOrigin apk_origin = GetApkOriginFromDexPath(dex_path); // (http://b/27588281) This is a workaround for apps using custom // classloaders and calling System.load() with an absolute path which @@ -298,6 +302,20 @@ Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t t } } + auto apex_ns_name = FindApexNamespaceName(dex_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()) { + auto link = app_ns->Link(*apex_ns, jni_libs); + if (!link.ok()) { + return linked.error(); + } + } + } + } + // TODO(b/143733063): Remove it after library path of apex module is supported. auto cronet_ns = NativeLoaderNamespace::GetExportedNamespace(kCronetNamespaceName, is_bridged); @@ -367,4 +385,21 @@ NativeLoaderNamespace* LibraryNamespaces::FindParentNamespaceByClassLoader(JNIEn return nullptr; } +base::Result<std::string> FindApexNamespaceName(const std::string& location) { + // Lots of implicit assumptions here: we expect `location` to be of the form: + // /apex/modulename/... + // + // And we extract from it 'modulename', and then apply mangling rule to get namespace name for it. + if (android::base::StartsWith(location, kApexPath)) { + size_t start_index = strlen(kApexPath); + size_t slash_index = location.find_first_of('/', start_index); + LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos), + "Error finding namespace of apex: no slash in path %s", location.c_str()); + std::string name = location.substr(start_index, slash_index - start_index); + std::replace(name.begin(), name.end(), '.', '_'); + return name; + } + return base::Error(); +} + } // namespace android::nativeloader diff --git a/libnativeloader/library_namespaces.h b/libnativeloader/library_namespaces.h index 8fac534302..e6d1a87cbb 100644 --- a/libnativeloader/library_namespaces.h +++ b/libnativeloader/library_namespaces.h @@ -67,6 +67,8 @@ class LibraryNamespaces { std::list<std::pair<jweak, NativeLoaderNamespace>> namespaces_; }; +Result<std::string> FindApexNamespaceName(const std::string& location); + } // namespace android::nativeloader #endif // ART_LIBNATIVELOADER_LIBRARY_NAMESPACES_H_ diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp index 988e8a841c..2a28a05c01 100644 --- a/libnativeloader/native_loader.cpp +++ b/libnativeloader/native_loader.cpp @@ -45,27 +45,15 @@ namespace { #if defined(__ANDROID__) using android::nativeloader::LibraryNamespaces; -constexpr const char* kApexPath = "/apex/"; - std::mutex g_namespaces_mutex; LibraryNamespaces* g_namespaces = new LibraryNamespaces; android_namespace_t* FindExportedNamespace(const char* caller_location) { - std::string location = caller_location; - // Lots of implicit assumptions here: we expect `caller_location` to be of the form: - // /apex/modulename/... - // - // And we extract from it 'modulename', which is the name of the linker namespace. - if (android::base::StartsWith(location, kApexPath)) { - size_t start_index = strlen(kApexPath); - size_t slash_index = location.find_first_of('/', start_index); - LOG_ALWAYS_FATAL_IF((slash_index == std::string::npos), - "Error finding namespace of apex: no slash in path %s", caller_location); - std::string name = location.substr(start_index, slash_index - start_index); - std::replace(name.begin(), name.end(), '.', '_'); - android_namespace_t* boot_namespace = android_get_exported_namespace(name.c_str()); + auto 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), - "Error finding namespace of apex: no namespace called %s", name.c_str()); + "Error finding namespace of apex: no namespace called %s", name->c_str()); return boot_namespace; } return nullptr; diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp index f0446f0db1..e36a7e6de2 100644 --- a/libnativeloader/native_loader_test.cpp +++ b/libnativeloader/native_loader_test.cpp @@ -38,6 +38,7 @@ using ::testing::StrEq; using ::testing::_; using internal::ConfigEntry; using internal::ParseConfig; +using internal::ParseJniConfig; #if defined(__LP64__) #define LIB_DIR "lib64" @@ -682,5 +683,28 @@ TEST(NativeLoaderConfigParser, RejectMalformed) { ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true).ok()); } +TEST(NativeLoaderJniConfigParser, BasicLoading) { + const char file_content[] = R"( +# comment +com_android_foo libfoo.so +# Empty line is ignored + +com_android_bar libbar.so:libbar2.so +)"; + + std::map<std::string, std::string> expected_result{ + {"com_android_foo", "libfoo.so"}, + {"com_android_bar", "libbar.so:libbar2.so"}, + }; + + Result<std::map<std::string, std::string>> result = ParseJniConfig(file_content); + ASSERT_RESULT_OK(result); + ASSERT_EQ(expected_result, *result); +} + +TEST(NativeLoaderJniConfigParser, RejectMalformed) { + ASSERT_FALSE(ParseJniConfig("com_android_foo").ok()); +} + } // namespace nativeloader } // namespace android diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp index d244f375a6..4e292eee68 100644 --- a/libnativeloader/public_libraries.cpp +++ b/libnativeloader/public_libraries.cpp @@ -21,6 +21,7 @@ #include <dirent.h> #include <algorithm> +#include <map> #include <memory> #include <android-base/file.h> @@ -42,6 +43,7 @@ using android::base::ErrnoError; using android::base::Result; using internal::ConfigEntry; using internal::ParseConfig; +using internal::ParseJniConfig; using std::literals::string_literals::operator""s; namespace { @@ -49,6 +51,7 @@ namespace { constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt"; constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-"; constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt"; +constexpr const char* kJniConfigFile = "/linkerconfig/jni.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"; @@ -313,6 +316,21 @@ static std::string InitStatsdPublicLibraries() { return kStatsdApexPublicLibrary; } +static std::map<std::string, std::string> InitApexJniLibraries() { + std::string file_content; + if (!base::ReadFileToString(kJniConfigFile, &file_content)) { + // jni config is optional + return {}; + } + auto config = ParseJniConfig(file_content); + if (!config.ok()) { + LOG_ALWAYS_FATAL("%s: %s", kJniConfigFile, config.error().message().c_str()); + // not reach here + return {}; + } + return *config; +} + } // namespace const std::string& preloadable_public_libraries() { @@ -375,6 +393,11 @@ const std::string& vndksp_libraries_vendor() { return list; } +const std::string& apex_jni_libraries(const std::string& apex_ns_name) { + static std::map<std::string, std::string> jni_libraries = InitApexJniLibraries(); + return jni_libraries[apex_ns_name]; +} + bool is_product_vndk_version_defined() { #if defined(__ANDROID__) return android::sysprop::VndkProperties::product_vndk_version().has_value(); @@ -452,6 +475,24 @@ Result<std::vector<std::string>> ParseConfig( return sonames; } +Result<std::map<std::string, std::string>> ParseJniConfig(const std::string& file_content) { + std::map<std::string, std::string> entries; + std::vector<std::string> lines = base::Split(file_content, "\n"); + for (auto& line : lines) { + auto trimmed_line = base::Trim(line); + if (trimmed_line[0] == '#' || trimmed_line.empty()) { + continue; + } + + std::vector<std::string> tokens = base::Split(trimmed_line, " "); + if (tokens.size() < 2) { + return Errorf( "Malformed line \"{}\"", line); + } + entries[tokens[0]] = tokens[1]; + } + return entries; +} + } // namespace internal } // namespace android::nativeloader diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h index 029566ae08..1d67d12150 100644 --- a/libnativeloader/public_libraries.h +++ b/libnativeloader/public_libraries.h @@ -18,6 +18,7 @@ #define ART_LIBNATIVELOADER_PUBLIC_LIBRARIES_H_ #include <algorithm> +#include <map> #include <string> #include <android-base/result.h> @@ -41,6 +42,7 @@ const std::string& llndk_libraries_product(); const std::string& llndk_libraries_vendor(); const std::string& vndksp_libraries_product(); const std::string& vndksp_libraries_vendor(); +const std::string& apex_jni_libraries(const std::string& apex_name); // Returns true if libnativeloader is running on devices and the device has // ro.product.vndk.version property. It returns false for host. @@ -63,6 +65,9 @@ Result<std::vector<std::string>> ParseConfig( const std::string& file_content, const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn); +Result<std::map<std::string, std::string>> ParseJniConfig( + const std::string& file_content); + } // namespace internal } // namespace android::nativeloader |