blob: 4f76a42fbbc1124a352d95d52c4d01f35569df26 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "crazy_linker_search_path_list.h"
#include "crazy_linker.h"
#include "crazy_linker_debug.h"
#include "crazy_linker_system.h"
#include "crazy_linker_zip.h"
#include <utility>
#include <string.h>
namespace crazy {
namespace {
// Helper class used to parse over the items of two column-separated lists.
// Usage is the following:
// 1) Create new instance, passing the first and second list as parameters.
// 2) Call NextItem() in a loop until it returns false. Each call will
// return the current item.
//
// Items of the first list are returned in order before items of the second
// list.
class MultiListParser {
public:
// Constructor.
MultiListParser(const String& a_list, const String& b_list)
: p_(a_list.cbegin()), end_(a_list.cend()), b_list_(b_list) {}
// Grab next item. On success return true and sets |*result|. On end of list,
// just return false.
bool NextItem(String* result) {
for (;;) {
if (p_ == end_) {
if (p_ != b_list_.cend()) {
p_ = b_list_.cbegin();
end_ = b_list_.cend();
continue;
}
return false;
}
// compute current list item, and next item start at the same time.
const char* item = p_;
const char* item_end = item;
while (item_end < end_ && item_end[0] != ':')
item_end++;
p_ = item_end + (item_end < end_);
if (item_end > item) { // Skip over empty entries.
result->Assign(item, item_end - item);
return true;
}
}
}
private:
const char* p_;
const char* end_;
const String& b_list_;
};
// Look into zip archive at |zip_path| for a file named |file_name|.
// As a special convenience trick, this will also try to find a file with
// the same name with a 'crazy.' prefix. In other words, when looking for
// 'lib/libfoo.so', this will first look for 'lib/libfoo.so', then for
// 'lib/crazy.libfoo.so'. This allows storing uncompressed libraries inside
// Android APKs while preventing the system from extracting them at installation
// time (which will always happen before Android M). Note that these files
// should just be renamed, i.e. their internal soname should still be
// 'libfoo.so'.
//
// On success, return offset within zip archive, or CRAZY_OFFSET_FAILED
// if the library is not found.
int32_t FindLibFileInZipArchive(const char* zip_path, const char* file_name) {
// First attempt is direct lookup.
int32_t offset = FindStartOffsetOfFileInZipFile(zip_path, file_name);
if (offset != CRAZY_OFFSET_FAILED) {
LOG(" FOUND_IN_ZIP %s!%s @ 0x%x", zip_path, file_name, offset);
return offset;
}
// Second attempt adding a crazy. prefix to the library name.
String crazy_name;
const char* pos = ::strrchr(file_name, '/');
if (pos) {
crazy_name.Assign(file_name, (pos + 1 - file_name));
file_name = pos + 1;
}
crazy_name.Append("crazy.");
crazy_name.Append(file_name);
offset = FindStartOffsetOfFileInZipFile(zip_path, crazy_name.c_str());
if (offset != CRAZY_OFFSET_FAILED) {
LOG(" FOUND IN ZIP %s!%s @ 0x%x", zip_path, crazy_name.c_str(), offset);
}
return offset;
}
// Try to find the library file pointed by |path|.
// If |path| contains an exclamation mark, this is interpreted as a separator
// between a zip archive file path, and a file contained inside it.
// Also supports crazy. prefix for storing renamed libraries inside the zip
// archive (see comment for FindLibFileInZipArchive).
SearchPathList::Result FindLibFile(const char* path) {
// An exclamation mark in the file name indicates that one should look
// inside a zip archive. This is supported by the platform, see the
// "Opening shared libraries directly from an APK" in the following article:
// https://github.com/aosp-mirror/platform_bionic/blob/master/android-changes-for-ndk-developers.md
const char* bang = ::strchr(path, '!');
if (bang) {
if (bang == path || bang[1] == '\0') {
// An initial or final '!' is always an error.
LOG(" INVALID_ZIP_PATH %s", path);
} else {
String zip_path = MakeAbsolutePathFrom(path, bang - path);
const char* file_name = bang + 1;
int32_t offset = FindLibFileInZipArchive(zip_path.c_str(), bang + 1);
if (offset != CRAZY_OFFSET_FAILED) {
return {std::move(zip_path), offset};
}
}
} else {
// Regular file path.
String file_path = MakeAbsolutePathFrom(path);
if (PathIsFile(file_path.c_str())) {
LOG(" FOUND FILE %s", file_path.c_str());
return {std::move(file_path), 0};
}
}
LOG(" skip %s", path);
return {};
}
} // namespace
void SearchPathList::Reset() {
list_.Resize(0);
env_list_.Resize(0);
}
void SearchPathList::ResetFromEnv(const char* var_name) {
Reset();
const char* env = GetEnv(var_name);
if (env && *env)
env_list_ = env;
}
void SearchPathList::AddPaths(const char* list, const char* list_end) {
// Append a column to the current list, if necessary
if (list_.size() > 0 && list_[list_.size() - 1] != ':')
list_ += ':';
list_.Append(list, list_end - list);
}
SearchPathList::Result SearchPathList::FindFile(const char* file_name) const {
LOG("Looking for %s", file_name);
if (::strchr(file_name, '/') != nullptr ||
::strchr(file_name, '!') != nullptr) {
// This is an absolute or relative file path, so ignore the search list.
return FindLibFile(file_name);
}
// Build full list by appending the env_list_ after the regular one.
MultiListParser parser(list_, env_list_);
String file_path;
Result result;
while (parser.NextItem(&file_path)) {
// Add trailing directory separator if needed.
if (file_path[file_path.size() - 1] != '/')
file_path += '/';
file_path += file_name;
result = FindLibFile(file_path.c_str());
if (result.IsValid()) {
return result;
}
LOG(" SKIPPED %s", file_path.c_str());
}
LOG(" MISSING %s", file_name);
return result;
}
} // namespace crazy