summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Jooyung Han <jooyung@google.com> 2020-03-03 00:46:50 +0900
committer Treehugger Robot <treehugger-gerrit@google.com> 2020-03-04 05:56:39 +0000
commit538f99ab285c1440969b9b3331fc0ce750c0d316 (patch)
tree018e9cbfc08ba39b3f35e457a8658fcc4cc8db75
parent6fc471e510d6a4e9c31fcab6c0542e2efdf50099 (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.cpp69
-rw-r--r--libnativeloader/library_namespaces.h2
-rw-r--r--libnativeloader/native_loader.cpp20
-rw-r--r--libnativeloader/native_loader_test.cpp24
-rw-r--r--libnativeloader/public_libraries.cpp41
-rw-r--r--libnativeloader/public_libraries.h5
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