android_system_core/libnativeloader/public_libraries.cpp
Jiyong Park 62870e295d public libs from APEXes are pre-loadable
This change fixes a bug that public libs from APEXes are never
pre-loaded. This was happening because those libs are removed by
InitDefaultPublicLibraries which was originaly designed for listing up
the libs that needs to be provided to the app namespace from the default
namespace. Since the libs from APEXes are not available from the default
namespace, but from their own namespaces (e.g."runtime", etc.), the
removal was fine. But the problem happend when the same function is
parameterized (with bool for_preload) and used to get the libs for
preloading.

This change fixes the problem by not removing the libs from APEXes when
the function is called with for_preload == true.

Bug: 141847343
Test: m
Change-Id: I9edf2f94bac473ac7d0651514a2dabe976f81058
2019-10-03 11:58:29 +09:00

371 lines
12 KiB
C++

/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "nativeloader"
#include "public_libraries.h"
#include <dirent.h>
#include <algorithm>
#include <memory>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/result.h>
#include <android-base/strings.h>
#include <log/log.h>
#include "utils.h"
namespace android::nativeloader {
using namespace internal;
using namespace ::std::string_literals;
using android::base::ErrnoError;
using android::base::Errorf;
using android::base::Result;
namespace {
constexpr const char* kDefaultPublicLibrariesFile = "/etc/public.libraries.txt";
constexpr const char* kExtendedPublicLibrariesFilePrefix = "public.libraries-";
constexpr const char* kExtendedPublicLibrariesFileSuffix = ".txt";
constexpr const char* kVendorPublicLibrariesFile = "/vendor/etc/public.libraries.txt";
constexpr const char* kLlndkLibrariesFile = "/system/etc/llndk.libraries.txt";
constexpr const char* kVndkLibrariesFile = "/system/etc/vndksp.libraries.txt";
const std::vector<const std::string> kArtApexPublicLibraries = {
"libicuuc.so",
"libicui18n.so",
};
constexpr const char* kArtApexLibPath = "/apex/com.android.art/" LIB;
constexpr const char* kNeuralNetworksApexPublicLibrary = "libneuralnetworks.so";
// TODO(b/130388701): do we need this?
std::string root_dir() {
static const char* android_root_env = getenv("ANDROID_ROOT");
return android_root_env != nullptr ? android_root_env : "/system";
}
bool debuggable() {
static bool debuggable = android::base::GetBoolProperty("ro.debuggable", false);
return debuggable;
}
std::string vndk_version_str() {
static std::string version = android::base::GetProperty("ro.vndk.version", "");
if (version != "" && version != "current") {
return "." + version;
}
return "";
}
// For debuggable platform builds use ANDROID_ADDITIONAL_PUBLIC_LIBRARIES environment
// variable to add libraries to the list. This is intended for platform tests only.
std::string additional_public_libraries() {
if (debuggable()) {
const char* val = getenv("ANDROID_ADDITIONAL_PUBLIC_LIBRARIES");
return val ? val : "";
}
return "";
}
void InsertVndkVersionStr(std::string* file_name) {
CHECK(file_name != nullptr);
size_t insert_pos = file_name->find_last_of(".");
if (insert_pos == std::string::npos) {
insert_pos = file_name->length();
}
file_name->insert(insert_pos, vndk_version_str());
}
const std::function<Result<bool>(const struct ConfigEntry&)> always_true =
[](const struct ConfigEntry&) -> Result<bool> { return true; };
Result<std::vector<std::string>> ReadConfig(
const std::string& configFile,
const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
std::string file_content;
if (!base::ReadFileToString(configFile, &file_content)) {
return ErrnoError();
}
Result<std::vector<std::string>> result = ParseConfig(file_content, filter_fn);
if (!result) {
return Errorf("Cannot parse {}: {}", configFile, result.error().message());
}
return result;
}
void ReadExtensionLibraries(const char* dirname, std::vector<std::string>* sonames) {
std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(dirname), closedir);
if (dir != nullptr) {
// Failing to opening the dir is not an error, which can happen in
// webview_zygote.
while (struct dirent* ent = readdir(dir.get())) {
if (ent->d_type != DT_REG && ent->d_type != DT_LNK) {
continue;
}
const std::string filename(ent->d_name);
std::string_view fn = filename;
if (android::base::ConsumePrefix(&fn, kExtendedPublicLibrariesFilePrefix) &&
android::base::ConsumeSuffix(&fn, kExtendedPublicLibrariesFileSuffix)) {
const std::string company_name(fn);
const std::string config_file_path = dirname + "/"s + filename;
LOG_ALWAYS_FATAL_IF(
company_name.empty(),
"Error extracting company name from public native library list file path \"%s\"",
config_file_path.c_str());
auto ret = ReadConfig(
config_file_path, [&company_name](const struct ConfigEntry& entry) -> Result<bool> {
if (android::base::StartsWith(entry.soname, "lib") &&
android::base::EndsWith(entry.soname, "." + company_name + ".so")) {
return true;
} else {
return Errorf("Library name \"{}\" does not end with the company name {}.",
entry.soname, company_name);
}
});
if (ret) {
sonames->insert(sonames->end(), ret->begin(), ret->end());
} else {
LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
config_file_path.c_str(), ret.error().message().c_str());
}
}
}
}
}
static std::string InitDefaultPublicLibraries(bool for_preload) {
std::string config_file = root_dir() + kDefaultPublicLibrariesFile;
auto sonames =
ReadConfig(config_file, [&for_preload](const struct ConfigEntry& entry) -> Result<bool> {
if (for_preload) {
return !entry.nopreload;
} else {
return true;
}
});
if (!sonames) {
LOG_ALWAYS_FATAL("Error reading public native library list from \"%s\": %s",
config_file.c_str(), sonames.error().message().c_str());
return "";
}
std::string additional_libs = additional_public_libraries();
if (!additional_libs.empty()) {
auto vec = base::Split(additional_libs, ":");
std::copy(vec.begin(), vec.end(), std::back_inserter(*sonames));
}
// If this is for preloading libs, don't remove the libs from APEXes.
if (for_preload) {
return android::base::Join(*sonames, ':');
}
// Remove the public libs in the runtime 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 runtime 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 : kArtApexPublicLibraries) {
std::string path(kArtApexLibPath);
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) {
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);
}
return android::base::Join(*sonames, ':');
}
static std::string InitArtPublicLibraries() {
CHECK(sizeof(kArtApexPublicLibraries) > 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 InitVendorPublicLibraries() {
// This file is optional, quietly ignore if the file does not exist.
auto sonames = ReadConfig(kVendorPublicLibrariesFile, always_true);
if (!sonames) {
return "";
}
return android::base::Join(*sonames, ':');
}
// read /system/etc/public.libraries-<companyname>.txt and
// /product/etc/public.libraries-<companyname>.txt which contain partner defined
// system libs that are exposed to apps. The libs in the txt files must be
// named as lib<name>.<companyname>.so.
static std::string InitExtendedPublicLibraries() {
std::vector<std::string> sonames;
ReadExtensionLibraries("/system/etc", &sonames);
ReadExtensionLibraries("/product/etc", &sonames);
return android::base::Join(sonames, ':');
}
static std::string InitLlndkLibraries() {
std::string config_file = kLlndkLibrariesFile;
InsertVndkVersionStr(&config_file);
auto sonames = ReadConfig(config_file, always_true);
if (!sonames) {
LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
return "";
}
return android::base::Join(*sonames, ':');
}
static std::string InitVndkspLibraries() {
std::string config_file = kVndkLibrariesFile;
InsertVndkVersionStr(&config_file);
auto sonames = ReadConfig(config_file, always_true);
if (!sonames) {
LOG_ALWAYS_FATAL("%s", sonames.error().message().c_str());
return "";
}
return android::base::Join(*sonames, ':');
}
static std::string InitNeuralNetworksPublicLibraries() {
return kNeuralNetworksApexPublicLibrary;
}
} // namespace
const std::string& preloadable_public_libraries() {
static std::string list = InitDefaultPublicLibraries(/*for_preload*/ true);
return list;
}
const std::string& default_public_libraries() {
static std::string list = InitDefaultPublicLibraries(/*for_preload*/ false);
return list;
}
const std::string& runtime_public_libraries() {
static std::string list = InitArtPublicLibraries();
return list;
}
const std::string& vendor_public_libraries() {
static std::string list = InitVendorPublicLibraries();
return list;
}
const std::string& extended_public_libraries() {
static std::string list = InitExtendedPublicLibraries();
return list;
}
const std::string& neuralnetworks_public_libraries() {
static std::string list = InitNeuralNetworksPublicLibraries();
return list;
}
const std::string& llndk_libraries() {
static std::string list = InitLlndkLibraries();
return list;
}
const std::string& vndksp_libraries() {
static std::string list = InitVndkspLibraries();
return list;
}
namespace internal {
// Exported for testing
Result<std::vector<std::string>> ParseConfig(
const std::string& file_content,
const std::function<Result<bool>(const ConfigEntry& /* entry */)>& filter_fn) {
std::vector<std::string> lines = base::Split(file_content, "\n");
std::vector<std::string> sonames;
for (auto& line : lines) {
auto trimmed_line = base::Trim(line);
if (trimmed_line[0] == '#' || trimmed_line.empty()) {
continue;
}
std::vector<std::string> tokens = android::base::Split(trimmed_line, " ");
if (tokens.size() < 1 || tokens.size() > 3) {
return Errorf("Malformed line \"{}\"", line);
}
struct ConfigEntry entry = {.soname = "", .nopreload = false, .bitness = ALL};
size_t i = tokens.size();
while (i-- > 0) {
if (tokens[i] == "nopreload") {
entry.nopreload = true;
} else if (tokens[i] == "32" || tokens[i] == "64") {
if (entry.bitness != ALL) {
return Errorf("Malformed line \"{}\": bitness can be specified only once", line);
}
entry.bitness = tokens[i] == "32" ? ONLY_32 : ONLY_64;
} else {
if (i != 0) {
return Errorf("Malformed line \"{}\"", line);
}
entry.soname = tokens[i];
}
}
// skip 32-bit lib on 64-bit process and vice versa
#if defined(__LP64__)
if (entry.bitness == ONLY_32) continue;
#else
if (entry.bitness == ONLY_64) continue;
#endif
Result<bool> ret = filter_fn(entry);
if (!ret) {
return ret.error();
}
if (*ret) {
// filter_fn has returned true.
sonames.push_back(entry.soname);
}
}
return sonames;
}
} // namespace internal
} // namespace android::nativeloader