diff options
author | Jooyung Han <jooyung@google.com> | 2020-09-01 14:53:23 +0900 |
---|---|---|
committer | Jooyung Han <jooyung@google.com> | 2020-09-08 23:07:32 +0000 |
commit | cd616d0fa2f11ced7f979d60cc686c86c7e74d9a (patch) | |
tree | a5a930782fbaf542d66bc9eccf9d59a241cf0ca2 /libnativeloader | |
parent | d78be00e7bc1daf0a1db07ba3944a2924bba8e26 (diff) |
libnativeloader: clean-up hard-coded public libs
art, nn, i18n apexes provide some of libs listed in
public.libraries.txt. The list of these apexes are now read from
/linkerconfig/apex.libraries.config.txt generated by
/system/bin/linkerconfig.
Bug: 150767721
Test: cuttlefish boots
Test: atest libnativeloader_test
Change-Id: Ic603b42669dff89d5d3da2f6822312e827eddd86
Diffstat (limited to 'libnativeloader')
-rw-r--r-- | libnativeloader/library_namespaces.cpp | 40 | ||||
-rw-r--r-- | libnativeloader/native_loader_test.cpp | 74 | ||||
-rw-r--r-- | libnativeloader/public_libraries.cpp | 165 | ||||
-rw-r--r-- | libnativeloader/public_libraries.h | 15 |
4 files changed, 159 insertions, 135 deletions
diff --git a/libnativeloader/library_namespaces.cpp b/libnativeloader/library_namespaces.cpp index 3ef50e3abf..47050cad80 100644 --- a/libnativeloader/library_namespaces.cpp +++ b/libnativeloader/library_namespaces.cpp @@ -50,9 +50,6 @@ constexpr const char* kApexPath = "/apex/"; constexpr const char* kVendorNamespaceName = "sphal"; constexpr const char* kVndkNamespaceName = "vndk"; constexpr const char* kVndkProductNamespaceName = "vndk_product"; -constexpr const char* kArtNamespaceName = "com_android_art"; -constexpr const char* kI18nNamespaceName = "com_android_i18n"; -constexpr const char* kNeuralNetworksNamespaceName = "com_android_neuralnetworks"; constexpr const char* kStatsdNamespaceName = "com_android_os_statsd"; // classloader-namespace is a linker namespace that is created for the loaded @@ -314,31 +311,14 @@ Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t t return linked.error(); } - auto art_ns = NativeLoaderNamespace::GetExportedNamespace(kArtNamespaceName, is_bridged); - // ART APEX does not exist on host, and under certain build conditions. - if (art_ns.ok()) { - linked = app_ns->Link(*art_ns, art_public_libraries()); - if (!linked.ok()) { - return linked.error(); - } - } - - auto i18n_ns = NativeLoaderNamespace::GetExportedNamespace(kI18nNamespaceName, is_bridged); - // i18n APEX does not exist on host, and under certain build conditions. - if (i18n_ns.ok()) { - linked = app_ns->Link(*i18n_ns, i18n_public_libraries()); - if (!linked.ok()) { - return linked.error(); - } - } - - // Give access to NNAPI libraries (apex-updated LLNDK library). - auto nnapi_ns = - NativeLoaderNamespace::GetExportedNamespace(kNeuralNetworksNamespaceName, is_bridged); - if (nnapi_ns.ok()) { - linked = app_ns->Link(*nnapi_ns, neuralnetworks_public_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()) { + linked = app_ns->Link(*ns, public_libs); + if (!linked.ok()) { + return linked.error(); + } } } @@ -370,8 +350,8 @@ Result<NativeLoaderNamespace*> LibraryNamespaces::Create(JNIEnv* env, uint32_t t 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()) { + linked = app_ns->Link(*apex_ns, jni_libs); + if (!linked.ok()) { return linked.error(); } } diff --git a/libnativeloader/native_loader_test.cpp b/libnativeloader/native_loader_test.cpp index 81109b6008..146ab5a5b9 100644 --- a/libnativeloader/native_loader_test.cpp +++ b/libnativeloader/native_loader_test.cpp @@ -41,7 +41,7 @@ using ::testing::StrEq; using ::testing::_; using internal::ConfigEntry; using internal::ParseConfig; -using internal::ParseJniConfig; +using internal::ParseApexLibrariesConfig; #if defined(__LP64__) #define LIB_DIR "lib64" @@ -369,13 +369,13 @@ class NativeLoaderTest_Create : public NativeLoaderTest { bool expected_link_with_neuralnetworks_ns = true; bool expected_link_with_statsd_ns = true; std::string expected_shared_libs_to_platform_ns = default_public_libraries(); - std::string expected_shared_libs_to_art_ns = art_public_libraries(); - std::string expected_shared_libs_to_i18n_ns = i18n_public_libraries(); + std::string expected_shared_libs_to_art_ns = apex_public_libraries().at("com_android_art"); + std::string expected_shared_libs_to_i18n_ns = apex_public_libraries().at("com_android_i18n"); std::string expected_shared_libs_to_sphal_ns = vendor_public_libraries(); std::string expected_shared_libs_to_vndk_ns = vndksp_libraries_vendor(); std::string expected_shared_libs_to_vndk_product_ns = vndksp_libraries_product(); std::string expected_shared_libs_to_default_ns = default_public_libraries(); - std::string expected_shared_libs_to_neuralnetworks_ns = neuralnetworks_public_libraries(); + std::string expected_shared_libs_to_neuralnetworks_ns = apex_public_libraries().at("com_android_neuralnetworks"); std::string expected_shared_libs_to_statsd_ns = statsd_public_libraries(); void SetExpectations() { @@ -690,29 +690,77 @@ TEST(NativeLoaderConfigParser, RejectMalformed) { ASSERT_FALSE(ParseConfig("libA.so nopreload # comment", always_true).ok()); } -TEST(NativeLoaderJniConfigParser, BasicLoading) { +TEST(NativeLoaderApexLibrariesConfigParser, BasicLoading) { const char file_content[] = R"( # comment -com_android_foo libfoo.so +jni com_android_foo libfoo.so # Empty line is ignored -com_android_bar libbar.so:libbar2.so +jni com_android_bar libbar.so:libbar2.so + + public com_android_bar libpublic.so )"; - std::map<std::string, std::string> expected_result{ + auto jni_libs = ParseApexLibrariesConfig(file_content, "jni"); + ASSERT_RESULT_OK(jni_libs); + std::map<std::string, std::string> expected_jni_libs { {"com_android_foo", "libfoo.so"}, {"com_android_bar", "libbar.so:libbar2.so"}, }; + ASSERT_EQ(expected_jni_libs, *jni_libs); - Result<std::map<std::string, std::string>> result = ParseJniConfig(file_content); - ASSERT_RESULT_OK(result); - ASSERT_EQ(expected_result, *result); + auto public_libs = ParseApexLibrariesConfig(file_content, "public"); + ASSERT_RESULT_OK(public_libs); + std::map<std::string, std::string> expected_public_libs { + {"com_android_bar", "libpublic.so"}, + }; + ASSERT_EQ(expected_public_libs, *public_libs); +} + +TEST(NativeLoaderApexLibrariesConfigParser, RejectMalformedLine) { + const char file_content[] = R"( +jni com_android_foo libfoo +# missing <library list> +jni com_android_bar +)"; + auto result = ParseApexLibrariesConfig(file_content, "jni"); + ASSERT_FALSE(result.ok()); + ASSERT_EQ("Malformed line \"jni com_android_bar\"", result.error().message()); } -TEST(NativeLoaderJniConfigParser, RejectMalformed) { - ASSERT_FALSE(ParseJniConfig("com_android_foo").ok()); +TEST(NativeLoaderApexLibrariesConfigParser, RejectInvalidTag) { + const char file_content[] = R"( +jni apex1 lib +public apex2 lib +# unknown tag +unknown com_android_foo libfoo +)"; + auto result = ParseApexLibrariesConfig(file_content, "jni"); + ASSERT_FALSE(result.ok()); + ASSERT_EQ("Invalid tag \"unknown com_android_foo libfoo\"", result.error().message()); } +TEST(NativeLoaderApexLibrariesConfigParser, RejectInvalidApexNamespace) { + const char file_content[] = R"( +# apex linker namespace should be mangled ('.' -> '_') +jni com.android.foo lib +)"; + auto result = ParseApexLibrariesConfig(file_content, "jni"); + ASSERT_FALSE(result.ok()); + ASSERT_EQ("Invalid apex_namespace \"jni com.android.foo lib\"", result.error().message()); +} + +TEST(NativeLoaderApexLibrariesConfigParser, RejectInvalidLibraryList) { + const char file_content[] = R"( +# library list is ":" separated list of filenames +jni com_android_foo lib64/libfoo.so +)"; + auto result = ParseApexLibrariesConfig(file_content, "jni"); + ASSERT_FALSE(result.ok()); + ASSERT_EQ("Invalid library_list \"jni com_android_foo lib64/libfoo.so\"", result.error().message()); +} + + } // namespace nativeloader } // namespace android diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp index 575ce2d3ac..c3f38f6c8d 100644 --- a/libnativeloader/public_libraries.cpp +++ b/libnativeloader/public_libraries.cpp @@ -23,6 +23,8 @@ #include <algorithm> #include <map> #include <memory> +#include <regex> +#include <string> #include <android-base/file.h> #include <android-base/logging.h> @@ -43,7 +45,7 @@ using android::base::ErrnoError; using android::base::Result; using internal::ConfigEntry; using internal::ParseConfig; -using internal::ParseJniConfig; +using internal::ParseApexLibrariesConfig; using std::literals::string_literals::operator""s; namespace { @@ -51,23 +53,11 @@ 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* 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"; -const std::vector<const std::string> kArtApexPublicLibraries = { - "libnativehelper.so", -}; - -const std::vector<const std::string> ki18nApexPublicLibraries = { - "libicuuc.so", - "libicui18n.so", -}; - -constexpr const char* kI18nApexLibPath = "/apex/com.android.i18n/" LIB; - -constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so"; constexpr const char* kStatsdApexPublicLibrary = "libstats_jni.so"; @@ -198,54 +188,22 @@ static std::string InitDefaultPublicLibraries(bool for_preload) { return android::base::Join(*sonames, ':'); } - // Remove the public libs in the i18n namespace. - // These libs are listed in public.android.txt, but we don't want the rest of android - // in default namespace to dlopen the libs. - // For example, libicuuc.so is exposed to classloader namespace from art namespace. - // Unfortunately, it does not have stable C symbols, and default namespace should only use - // stable symbols in libandroidicu.so. http://b/120786417 - for (const std::string& lib_name : ki18nApexPublicLibraries) { - std::string path(kI18nApexLibPath); - path.append("/").append(lib_name); - - struct stat s; - // Do nothing if the path in /apex does not exist. - // Runtime APEX must be mounted since libnativeloader is in the same APEX - if (stat(path.c_str(), &s) != 0) { + // Remove the public libs provided by apexes because these libs are available + // from apex namespaces. + for (const auto& p : apex_public_libraries()) { + // TODO(b/167578583) remove this `if` block after fixing the bug + // Skip ART APEX to keep behaviors + if (p.first == "com_android_art") { continue; } - - auto it = std::find(sonames->begin(), sonames->end(), lib_name); - if (it != sonames->end()) { - sonames->erase(it); - } - } - - // Remove the public libs in the nnapi namespace. - auto it = std::find(sonames->begin(), sonames->end(), kNeuralNetworksApexPublicLibrary); - if (it != sonames->end()) { - sonames->erase(it); + auto public_libs = base::Split(p.second, ":"); + sonames->erase(std::remove_if(sonames->begin(), sonames->end(), [&public_libs](const std::string& v) { + return std::find(public_libs.begin(), public_libs.end(), v) != public_libs.end(); + }), sonames->end()); } return android::base::Join(*sonames, ':'); } -static std::string InitArtPublicLibraries() { - CHECK_GT((int)kArtApexPublicLibraries.size(), 0); - std::string list = android::base::Join(kArtApexPublicLibraries, ":"); - - std::string additional_libs = additional_public_libraries(); - if (!additional_libs.empty()) { - list = list + ':' + additional_libs; - } - return list; -} - -static std::string InitI18nPublicLibraries() { - static_assert(sizeof(ki18nApexPublicLibraries) > 0, "ki18nApexPublicLibraries is empty"); - std::string list = android::base::Join(ki18nApexPublicLibraries, ":"); - return list; -} - static std::string InitVendorPublicLibraries() { // This file is optional, quietly ignore if the file does not exist. auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true); @@ -318,29 +276,50 @@ static std::string InitVndkspLibrariesProduct() { return android::base::Join(*sonames, ':'); } -static std::string InitNeuralNetworksPublicLibraries() { - return kNeuralNetworksApexPublicLibrary; -} - static std::string InitStatsdPublicLibraries() { return kStatsdApexPublicLibrary; } -static std::map<std::string, std::string> InitApexJniLibraries() { +static std::map<std::string, std::string> InitApexLibraries(const std::string& tag) { std::string file_content; - if (!base::ReadFileToString(kJniConfigFile, &file_content)) { - // jni config is optional + if (!base::ReadFileToString(kApexLibrariesConfigFile, &file_content)) { + // config is optional return {}; } - auto config = ParseJniConfig(file_content); + auto config = ParseApexLibrariesConfig(file_content, tag); if (!config.ok()) { - LOG_ALWAYS_FATAL("%s: %s", kJniConfigFile, config.error().message().c_str()); - // not reach here + LOG_ALWAYS_FATAL("%s: %s", kApexLibrariesConfigFile, config.error().message().c_str()); return {}; } return *config; } +struct ApexLibrariesConfigLine { + std::string tag; + std::string apex_namespace; + std::string library_list; +}; + +const std::regex kApexNamespaceRegex("[0-9a-zA-Z_]+"); +const std::regex kLibraryListRegex("[0-9a-zA-Z.:@+_-]+"); + +Result<ApexLibrariesConfigLine> ParseApexLibrariesConfigLine(const std::string& line) { + std::vector<std::string> tokens = base::Split(line, " "); + if (tokens.size() != 3) { + return Errorf("Malformed line \"{}\"", line); + } + if (tokens[0] != "jni" && tokens[0] != "public") { + return Errorf("Invalid tag \"{}\"", line); + } + if (!std::regex_match(tokens[1], kApexNamespaceRegex)) { + return Errorf("Invalid apex_namespace \"{}\"", line); + } + if (!std::regex_match(tokens[2], kLibraryListRegex)) { + return Errorf("Invalid library_list \"{}\"", line); + } + return ApexLibrariesConfigLine{std::move(tokens[0]), std::move(tokens[1]), std::move(tokens[2])}; +} + } // namespace const std::string& preloadable_public_libraries() { @@ -353,16 +332,6 @@ const std::string& default_public_libraries() { return list; } -const std::string& art_public_libraries() { - static std::string list = InitArtPublicLibraries(); - return list; -} - -const std::string& i18n_public_libraries() { - static std::string list = InitI18nPublicLibraries(); - return list; -} - const std::string& vendor_public_libraries() { static std::string list = InitVendorPublicLibraries(); return list; @@ -373,11 +342,6 @@ const std::string& extended_public_libraries() { return list; } -const std::string& neuralnetworks_public_libraries() { - static std::string list = InitNeuralNetworksPublicLibraries(); - return list; -} - const std::string& statsd_public_libraries() { static std::string list = InitStatsdPublicLibraries(); return list; @@ -404,10 +368,15 @@ const std::string& vndksp_libraries_vendor() { } const std::string& apex_jni_libraries(const std::string& apex_ns_name) { - static std::map<std::string, std::string> jni_libraries = InitApexJniLibraries(); + static std::map<std::string, std::string> jni_libraries = InitApexLibraries("jni"); return jni_libraries[apex_ns_name]; } +const std::map<std::string, std::string>& apex_public_libraries() { + static std::map<std::string, std::string> public_libraries = InitApexLibraries("public"); + return public_libraries; +} + bool is_product_vndk_version_defined() { #if defined(ART_TARGET_ANDROID) return android::sysprop::VndkProperties::product_vndk_version().has_value(); @@ -485,7 +454,19 @@ Result<std::vector<std::string>> ParseConfig( return sonames; } -Result<std::map<std::string, std::string>> ParseJniConfig(const std::string& file_content) { +// Parses apex.libraries.config.txt file generated by linkerconfig which looks like +// system/linkerconfig/testdata/golden_output/stages/apex.libraries.config.txt +// and returns mapping of <apex namespace> to <library list> which matches <tag>. +// +// The file is line-based and each line consists of "<tag> <apex namespace> <library list>". +// +// <tag> explains what <library list> is. (e.g "jni", "public") +// <library list> is colon-separated list of library names. (e.g "libfoo.so:libbar.so") +// +// If <tag> is "jni", <library list> is the list of JNI libraries exposed by <apex namespace>. +// If <tag> is "public", <library list> is the list of public libraries exposed by <apex namespace>. +// Public libraries are the libs listed in /system/etc/public.libraries.txt. +Result<std::map<std::string, std::string>> ParseApexLibrariesConfig(const std::string& file_content, const std::string& tag) { std::map<std::string, std::string> entries; std::vector<std::string> lines = base::Split(file_content, "\n"); for (auto& line : lines) { @@ -493,12 +474,22 @@ Result<std::map<std::string, std::string>> ParseJniConfig(const std::string& fil if (trimmed_line[0] == '#' || trimmed_line.empty()) { continue; } + auto config_line = ParseApexLibrariesConfigLine(trimmed_line); + if (!config_line.ok()) { + return config_line.error(); + } + if (config_line->tag != tag) { + continue; + } + entries[config_line->apex_namespace] = config_line->library_list; + } - std::vector<std::string> tokens = base::Split(trimmed_line, " "); - if (tokens.size() < 2) { - return Errorf( "Malformed line \"{}\"", line); + // TODO(b/167578583) remove this `if` block after fixing the bug + if (tag == "public") { + std::string additional_libs = additional_public_libraries(); + if (!additional_libs.empty()) { + entries["com_android_art"] += ':' + additional_libs; } - entries[tokens[0]] = tokens[1]; } return entries; } diff --git a/libnativeloader/public_libraries.h b/libnativeloader/public_libraries.h index b60a2ef399..c1460db73b 100644 --- a/libnativeloader/public_libraries.h +++ b/libnativeloader/public_libraries.h @@ -32,18 +32,21 @@ using android::base::Result; // e.g., if it is a vendor app or not, different set of libraries are made available. const std::string& preloadable_public_libraries(); const std::string& default_public_libraries(); -const std::string& art_public_libraries(); const std::string& statsd_public_libraries(); const std::string& vendor_public_libraries(); const std::string& extended_public_libraries(); -const std::string& i18n_public_libraries(); -const std::string& neuralnetworks_public_libraries(); 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 the table of apexes and public libraries provided by the apexes. +// For example, com_android_foo -> libfoo.so:libbar.so +// Note that libfoo.so and libbar.so are listed in /system/etc/public.libraries.txt +// but provided by com.android.foo APEX. +const std::map<std::string, std::string>& apex_public_libraries(); + // Returns true if libnativeloader is running on devices and the device has // ro.product.vndk.version property. It returns false for host. bool is_product_vndk_version_defined(); @@ -65,8 +68,10 @@ 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); +// Parses apex.libraries.config.txt file generated by linkerconfig +// and returns mapping of <apex namespace> to <library list> which matches <tag>. +Result<std::map<std::string, std::string>> ParseApexLibrariesConfig( + const std::string& file_content, const std::string& tag); } // namespace internal |