summaryrefslogtreecommitdiff
path: root/libnativeloader
diff options
context:
space:
mode:
authorJooyung Han <jooyung@google.com>2020-03-03 00:46:50 +0900
committerTreehugger Robot <treehugger-gerrit@google.com>2020-03-04 05:56:39 +0000
commit538f99ab285c1440969b9b3331fc0ce750c0d316 (patch)
tree018e9cbfc08ba39b3f35e457a8658fcc4cc8db75 /libnativeloader
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
Diffstat (limited to 'libnativeloader')
-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