blob: 58765326f11b1dc06b853e62e2c03954748b1cf9 [file] [log] [blame]
/*
* Copyright (C) 2016 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.
*/
/*
* Tests accessibility of platform native libraries
*/
#include <dirent.h>
#include <dlfcn.h>
#include <jni.h>
#include <JNIHelp.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <list>
#include <string>
#include <unordered_set>
#include <vector>
static std::vector<std::string> kDefaultLibraryPaths = {
#if defined(__LP64__)
"/system/lib64",
"/vendor/lib64",
#else
"/system/lib",
"/vendor/lib",
#endif
};
static std::unordered_set<std::string> kPublicLibraries = {
"libandroid.so",
"libc.so",
"libdl.so",
"libEGL.so",
"libGLESv1_CM.so",
"libGLESv2.so",
"libGLESv3.so",
"libicui18n.so",
"libicuuc.so",
"libjnigraphics.so",
"liblog.so",
"libmediandk.so",
"libm.so",
"libOpenMAXAL.so",
"libOpenSLES.so",
"libRS.so",
"libstdc++.so",
"libwebviewchromium_plat_support.so",
"libz.so"
};
template <typename F>
static bool for_each_file(const std::string& dir, F functor, std::string* error_msg) {
auto dir_deleter = [](DIR* handle) { closedir(handle); };
std::unique_ptr<DIR, decltype(dir_deleter)> dirp(opendir(dir.c_str()), dir_deleter);
if (dirp == nullptr) {
*error_msg = strerror(errno);
return false;
}
dirent* dp;
while ((dp = readdir(dirp.get())) != nullptr) {
// skip "." and ".."
if (strcmp(".", dp->d_name) == 0 ||
strcmp("..", dp->d_name) == 0) {
continue;
}
if (!functor(dp->d_name, error_msg)) {
return false;
}
}
return true;
}
static bool should_be_accessible(const std::string& path) {
std::string name = basename(path.c_str());
return (kPublicLibraries.find(name) != kPublicLibraries.end()) &&
(kDefaultLibraryPaths.front() + "/" + name == path);
}
static bool is_directory(const std::string path) {
struct stat sb;
if (stat(path.c_str(), &sb) != -1) {
return S_ISDIR(sb.st_mode);
}
return false;
}
static bool is_libdl(const std::string path) {
return kDefaultLibraryPaths.front() + "/libdl.so" == path;
}
extern "C" JNIEXPORT jstring JNICALL Java_android_jni_cts_LinkerNamespacesHelper_runAccessibilityTest(
JNIEnv* env, jclass clazz __attribute__((unused))) {
std::list<std::string> dirs(kDefaultLibraryPaths.begin(), kDefaultLibraryPaths.end());
while (!dirs.empty()) {
const auto dir = dirs.front();
dirs.pop_front();
std::string error;
bool success = for_each_file(dir, [&dirs,&dir](const char* name, std::string* error_msg) {
std::string path = dir + "/" + name;
if (is_directory(path)) {
dirs.push_back(path);
return true;
}
if (is_libdl(path)) {
// TODO (dimitry): we skip check for libdl.so because
// 1. Linker will fail to check accessibility because it imposes as libdl.so (see http://b/27106625)
// 2. It is impractical to dlopen libdl.so because this library already depends
// on it in order to have dlopen()
return true;
}
auto dlcloser = [](void* handle) { dlclose(handle); };
std::unique_ptr<void, decltype(dlcloser)> handle(dlopen(path.c_str(), RTLD_NOW), dlcloser);
if (should_be_accessible(path)) {
if (handle.get() == nullptr) {
*error_msg = "The library \"" + path + "\" should be accessible but isn't: " + dlerror();
return false;
}
} else if (handle != nullptr) {
*error_msg = "The library \"" + path + "\" should not be accessible";
return false;
} else { // (handle == nullptr && !shouldBeAccessible(path))
// Check the error message
std::string err = dlerror();
if (err != "dlopen failed: library \"" + path + "\" is not accessible for the namespace \"classloader-namespace\"") {
*error_msg = "unexpected dlerror: " + err;
return false;
}
}
return true;
}, &error);
if (!success) {
return env->NewStringUTF(error.c_str());
}
}
return nullptr;
}