platform_bionic/linker/linker_config.cpp
Jiyong Park 01de74e76d linker: add android_get_exported_namespace
Depending on how ld.config.txt is configured, there can be multiple
built-in namespaces created by the linker from the beginning of a
process. android_get_exported_namespace is a platform only API for
getting a handle (android_namespace_t*) to one of the built-in namespaces
with given name. The returned namespace can then be given to
android_dlopen_ext in order to explicitly specify the target namespace
where the library is searched and loaded from.

Note that this function only returns 'exported' namespaces created via
ld.config.txt file. In order to export a namespace, the visible property
should be set to true:

namespace.<name>.visible = true

Namespaces are hidden by default. Hidden namespaces and namespaces
that are created programmatically, notably 'classloader-namespace',
aren't returned by this function.

Bug: 36851137
Test: confirmed that namespaces created with ld.config.txt is retrieved.
Test: linker-unit-tests passes
Merged-in: I714b510fa24f77e42c3dfc4c827b3befa8bb2951
Change-Id: I0d05fa7e0e116009edf8ea362ab46774bc617cbf
(cherry picked from commit d7c4832e6a)
2017-04-28 10:14:28 +09:00

485 lines
15 KiB
C++

/*
* Copyright (C) 2017 The Android Open Source Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "linker_config.h"
#include "linker_globals.h"
#include "linker_debug.h"
#include "linker_utils.h"
#include <android-base/file.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <stdlib.h>
#include <string>
#include <unordered_map>
class ConfigParser {
public:
enum {
kProperty,
kSection,
kEndOfFile,
kError,
};
explicit ConfigParser(std::string&& content)
: content_(content), p_(0), lineno_(0), was_end_of_file_(false) {}
/*
* Possible return values
* kProperty: name is set to property name and value is set to property value
* kSection: name is set to section name.
* kEndOfFile: reached end of file.
* kError: error_msg is set.
*/
int next_token(std::string* name, std::string* value, std::string* error_msg) {
std::string line;
while(NextLine(&line)) {
size_t found = line.find('#');
line = android::base::Trim(line.substr(0, found));
if (line.empty()) {
continue;
}
if (line[0] == '[' && line[line.size() - 1] == ']') {
*name = line.substr(1, line.size() - 2);
return kSection;
}
found = line.find('=');
if (found == std::string::npos) {
*error_msg = std::string("invalid format: ") +
line +
", expected \"name = property\" or \"[section]\"";
return kError;
}
*name = android::base::Trim(line.substr(0, found));
*value = android::base::Trim(line.substr(found + 1));
return kProperty;
}
// to avoid infinite cycles when programmer makes a mistake
CHECK(!was_end_of_file_);
was_end_of_file_ = true;
return kEndOfFile;
}
size_t lineno() const {
return lineno_;
}
private:
bool NextLine(std::string* line) {
if (p_ == std::string::npos) {
return false;
}
size_t found = content_.find('\n', p_);
if (found != std::string::npos) {
*line = content_.substr(p_, found - p_);
p_ = found + 1;
} else {
*line = content_.substr(p_);
p_ = std::string::npos;
}
lineno_++;
return true;
}
std::string content_;
size_t p_;
size_t lineno_;
bool was_end_of_file_;
DISALLOW_IMPLICIT_CONSTRUCTORS(ConfigParser);
};
class PropertyValue {
public:
PropertyValue() = default;
PropertyValue(std::string&& value, size_t lineno)
: value_(value), lineno_(lineno) {}
const std::string& value() const {
return value_;
}
size_t lineno() const {
return lineno_;
}
private:
std::string value_;
size_t lineno_;
};
static std::string create_error_msg(const char* file,
size_t lineno,
const std::string& msg) {
char buf[1024];
__libc_format_buffer(buf, sizeof(buf), "%s:%zu: error: %s", file, lineno, msg.c_str());
return std::string(buf);
}
static bool parse_config_file(const char* ld_config_file_path,
const char* binary_realpath,
std::unordered_map<std::string, PropertyValue>* properties,
std::string* error_msg) {
std::string content;
if (!android::base::ReadFileToString(ld_config_file_path, &content)) {
if (errno != ENOENT) {
*error_msg = std::string("error reading file \"") +
ld_config_file_path + "\": " + strerror(errno);
}
return false;
}
ConfigParser cp(std::move(content));
std::string section_name;
while(true) {
std::string name;
std::string value;
std::string error;
int result = cp.next_token(&name, &value, &error);
if (result == ConfigParser::kError) {
DL_WARN("error parsing %s:%zd: %s (ignoring this line)",
ld_config_file_path,
cp.lineno(),
error.c_str());
continue;
}
if (result == ConfigParser::kSection || result == ConfigParser::kEndOfFile) {
return false;
}
if (result == ConfigParser::kProperty) {
if (!android::base::StartsWith(name, "dir.")) {
DL_WARN("error parsing %s:%zd: unexpected property name \"%s\", "
"expected format dir.<section_name> (ignoring this line)",
ld_config_file_path,
cp.lineno(),
name.c_str());
continue;
}
// remove trailing '/'
while (value[value.size() - 1] == '/') {
value = value.substr(0, value.size() - 1);
}
if (value.empty()) {
DL_WARN("error parsing %s:%zd: property value is empty (ignoring this line)",
ld_config_file_path,
cp.lineno());
continue;
}
if (file_is_under_dir(binary_realpath, value)) {
section_name = name.substr(4);
break;
}
}
}
// skip everything until we meet a correct section
while (true) {
std::string name;
std::string value;
std::string error;
int result = cp.next_token(&name, &value, &error);
if (result == ConfigParser::kSection && name == section_name) {
break;
}
if (result == ConfigParser::kEndOfFile) {
*error_msg = create_error_msg(ld_config_file_path,
cp.lineno(),
std::string("section \"") + section_name + "\" not found");
return false;
}
}
// found the section - parse it
while (true) {
std::string name;
std::string value;
std::string error;
int result = cp.next_token(&name, &value, &error);
if (result == ConfigParser::kEndOfFile || result == ConfigParser::kSection) {
break;
}
if (result == ConfigParser::kProperty) {
if (properties->find(name) != properties->end()) {
DL_WARN("%s:%zd: warning: property \"%s\" redefinition",
ld_config_file_path,
cp.lineno(),
name.c_str());
}
(*properties)[name] = PropertyValue(std::move(value), cp.lineno());
}
if (result == ConfigParser::kError) {
DL_WARN("error parsing %s:%zd: %s (ignoring this line)",
ld_config_file_path,
cp.lineno(),
error.c_str());
continue;
}
}
return true;
}
static Config g_config;
static constexpr const char* kDefaultConfigName = "default";
static constexpr const char* kPropertyAdditionalNamespaces = "additional.namespaces";
#if defined(__LP64__)
static constexpr const char* kLibParamValue = "lib64";
#else
static constexpr const char* kLibParamValue = "lib";
#endif
class Properties {
public:
explicit Properties(std::unordered_map<std::string, PropertyValue>&& properties)
: properties_(properties), target_sdk_version_(__ANDROID_API__) {}
std::vector<std::string> get_strings(const std::string& name, size_t* lineno = nullptr) const {
auto it = find_property(name, lineno);
if (it == properties_.end()) {
// return empty vector
return std::vector<std::string>();
}
std::vector<std::string> strings = android::base::Split(it->second.value(), ",");
for (size_t i = 0; i < strings.size(); ++i) {
strings[i] = android::base::Trim(strings[i]);
}
return strings;
}
bool get_bool(const std::string& name, size_t* lineno = nullptr) const {
auto it = find_property(name, lineno);
if (it == properties_.end()) {
return false;
}
return it->second.value() == "true";
}
std::string get_string(const std::string& name, size_t* lineno = nullptr) const {
auto it = find_property(name, lineno);
return (it == properties_.end()) ? "" : it->second.value();
}
std::vector<std::string> get_paths(const std::string& name, size_t* lineno = nullptr) {
std::string paths_str = get_string(name, lineno);
std::vector<std::string> paths;
split_path(paths_str.c_str(), ":", &paths);
std::vector<std::pair<std::string, std::string>> params;
params.push_back({ "LIB", kLibParamValue });
if (target_sdk_version_ != 0) {
char buf[16];
__libc_format_buffer(buf, sizeof(buf), "%d", target_sdk_version_);
params.push_back({ "SDK_VER", buf });
}
for (auto&& path : paths) {
format_string(&path, params);
}
std::vector<std::string> resolved_paths;
// do not remove paths that do not exist
resolve_paths(paths, &resolved_paths);
return resolved_paths;
}
void set_target_sdk_version(int target_sdk_version) {
target_sdk_version_ = target_sdk_version;
}
private:
std::unordered_map<std::string, PropertyValue>::const_iterator
find_property(const std::string& name, size_t* lineno) const {
auto it = properties_.find(name);
if (it != properties_.end() && lineno != nullptr) {
*lineno = it->second.lineno();
}
return it;
}
std::unordered_map<std::string, PropertyValue> properties_;
int target_sdk_version_;
DISALLOW_IMPLICIT_CONSTRUCTORS(Properties);
};
bool Config::read_binary_config(const char* ld_config_file_path,
const char* binary_realpath,
bool is_asan,
const Config** config,
std::string* error_msg) {
g_config.clear();
std::unordered_map<std::string, PropertyValue> property_map;
if (!parse_config_file(ld_config_file_path, binary_realpath, &property_map, error_msg)) {
return false;
}
Properties properties(std::move(property_map));
auto failure_guard = android::base::make_scope_guard([] { g_config.clear(); });
std::unordered_map<std::string, NamespaceConfig*> namespace_configs;
namespace_configs[kDefaultConfigName] = g_config.create_namespace_config(kDefaultConfigName);
std::vector<std::string> additional_namespaces = properties.get_strings(kPropertyAdditionalNamespaces);
for (const auto& name : additional_namespaces) {
namespace_configs[name] = g_config.create_namespace_config(name);
}
bool versioning_enabled = properties.get_bool("enable.target.sdk.version");
int target_sdk_version = __ANDROID_API__;
if (versioning_enabled) {
std::string version_file = dirname(binary_realpath) + "/.version";
std::string content;
if (!android::base::ReadFileToString(version_file, &content)) {
if (errno != ENOENT) {
*error_msg = std::string("error reading version file \"") +
version_file + "\": " + strerror(errno);
return false;
}
} else {
content = android::base::Trim(content);
errno = 0;
char* end = nullptr;
const char* content_str = content.c_str();
int result = strtol(content_str, &end, 10);
if (errno == 0 && *end == '\0' && result > 0) {
target_sdk_version = result;
properties.set_target_sdk_version(target_sdk_version);
} else {
*error_msg = std::string("invalid version \"") + version_file + "\": \"" + content +"\"";
return false;
}
}
}
g_config.set_target_sdk_version(target_sdk_version);
for (auto ns_config_it : namespace_configs) {
auto& name = ns_config_it.first;
NamespaceConfig* ns_config = ns_config_it.second;
std::string property_name_prefix = std::string("namespace.") + name;
size_t lineno = 0;
std::vector<std::string> linked_namespaces =
properties.get_strings(property_name_prefix + ".links", &lineno);
for (const auto& linked_ns_name : linked_namespaces) {
if (namespace_configs.find(linked_ns_name) == namespace_configs.end()) {
*error_msg = create_error_msg(ld_config_file_path,
lineno,
std::string("undefined namespace: ") + linked_ns_name);
return false;
}
std::string shared_libs = properties.get_string(property_name_prefix +
".link." +
linked_ns_name +
".shared_libs", &lineno);
if (shared_libs.empty()) {
*error_msg = create_error_msg(ld_config_file_path,
lineno,
std::string("list of shared_libs for ") +
name +
"->" +
linked_ns_name +
" link is not specified or is empty.");
return false;
}
ns_config->add_namespace_link(linked_ns_name, shared_libs);
}
ns_config->set_isolated(properties.get_bool(property_name_prefix + ".isolated"));
ns_config->set_visible(properties.get_bool(property_name_prefix + ".visible"));
// these are affected by is_asan flag
if (is_asan) {
property_name_prefix += ".asan";
}
ns_config->set_search_paths(properties.get_paths(property_name_prefix + ".search.paths"));
ns_config->set_permitted_paths(properties.get_paths(property_name_prefix + ".permitted.paths"));
}
failure_guard.Disable();
*config = &g_config;
return true;
}
NamespaceConfig* Config::create_namespace_config(const std::string& name) {
namespace_configs_.push_back(std::unique_ptr<NamespaceConfig>(new NamespaceConfig(name)));
NamespaceConfig* ns_config_ptr = namespace_configs_.back().get();
namespace_configs_map_[name] = ns_config_ptr;
return ns_config_ptr;
}
void Config::clear() {
namespace_configs_.clear();
namespace_configs_map_.clear();
}