Adjust the dladdr-based introspection logic used in art::GetAndroidRootSafe.
This logic originally assumed that the libartbase library linked into
the current binary was always located in a directory within the
Android Root. This is no longer true on target since libartbase moved
to the Runtime APEX; therefore we now only use that logic on host in
art::GetAndroidRootSafe.
Eventually we should be able to use this logic on target to find the
Android Runtime Root (in art::GetAndroidRuntimeRootSafe), as
libartbase is installed in the Runtime APEX. However, this is not
always true at the moment, as ART gtests still install another copy of
libartbase in /system/lib(64) (the Android Root) on target.
Also improve the documentation of methods `art::GetAndroidRoot(Safe)`
and `art::GetAndroidRuntimeRoot(Safe)`.
Test: m test-art-host-gtest-file_utils_test
Bug: 121117762
Bug: 129534335
Change-Id: I835207110dae0a550c06ac98ed4915241cc61c6f
diff --git a/libartbase/base/file_utils.cc b/libartbase/base/file_utils.cc
index 5a112c7..1a13103 100644
--- a/libartbase/base/file_utils.cc
+++ b/libartbase/base/file_utils.cc
@@ -94,9 +94,37 @@
}
}
+// Get the "root" directory containing the "lib" directory where this instance
+// of the libartbase library (which contains `GetRootContainingLibartbase`) is
+// located:
+// - on host this "root" is normally the Android Root (e.g. something like
+// "$ANDROID_BUILD_TOP/out/host/linux-x86/");
+// - on target this "root" is normally the Android Runtime Root
+// ("/apex/com.android.runtime").
+// Return the empty string if that directory cannot be found or if this code is
+// run on Windows or macOS.
+static std::string GetRootContainingLibartbase() {
+#if !defined( _WIN32) && !defined(__APPLE__)
+ // Check where libartbase is from, and derive from there.
+ Dl_info info;
+ if (dladdr(reinterpret_cast<const void*>(&GetRootContainingLibartbase), /* out */ &info) != 0) {
+ // Make a duplicate of the fname so dirname can modify it.
+ UniqueCPtr<char> fname(strdup(info.dli_fname));
+
+ char* dir1 = dirname(fname.get()); // This is the lib directory.
+ char* dir2 = dirname(dir1); // This is the "root" directory.
+ if (OS::DirectoryExists(dir2)) {
+ std::string tmp = dir2; // Make a copy here so that fname can be released.
+ return tmp;
+ }
+ }
+#endif
+ return "";
+}
+
std::string GetAndroidRootSafe(std::string* error_msg) {
#ifdef _WIN32
- UNUSED(kAndroidRootEnvVar, kAndroidRootDefaultPath);
+ UNUSED(kAndroidRootEnvVar, kAndroidRootDefaultPath, GetRootContainingLibartbase);
*error_msg = "GetAndroidRootSafe unsupported for Windows.";
return "";
#else
@@ -104,33 +132,30 @@
const char* android_root_from_env = getenv(kAndroidRootEnvVar);
if (android_root_from_env != nullptr) {
if (!OS::DirectoryExists(android_root_from_env)) {
- *error_msg = StringPrintf("Failed to find ANDROID_ROOT directory %s", android_root_from_env);
+ *error_msg =
+ StringPrintf("Failed to find %s directory %s", kAndroidRootEnvVar, android_root_from_env);
return "";
}
return android_root_from_env;
}
- // Check where libart is from, and derive from there. Only do this for non-Mac.
-#ifndef __APPLE__
- {
- Dl_info info;
- if (dladdr(reinterpret_cast<const void*>(&GetAndroidRootSafe), /* out */ &info) != 0) {
- // Make a duplicate of the fname so dirname can modify it.
- UniqueCPtr<char> fname(strdup(info.dli_fname));
-
- char* dir1 = dirname(fname.get()); // This is the lib directory.
- char* dir2 = dirname(dir1); // This is the "system" directory.
- if (OS::DirectoryExists(dir2)) {
- std::string tmp = dir2; // Make a copy here so that fname can be released.
- return tmp;
- }
+ // On host, libartbase is currently installed in "$ANDROID_ROOT/lib"
+ // (e.g. something like "$ANDROID_BUILD_TOP/out/host/linux-x86/lib". Use this
+ // information to infer the location of the Android Root (on host only).
+ //
+ // Note that this could change in the future, if we decided to install ART
+ // artifacts in a different location, e.g. within a "Runtime APEX" directory.
+ if (!kIsTargetBuild) {
+ std::string root_containing_libartbase = GetRootContainingLibartbase();
+ if (!root_containing_libartbase.empty()) {
+ return root_containing_libartbase;
}
}
-#endif
// Try the default path.
if (!OS::DirectoryExists(kAndroidRootDefaultPath)) {
- *error_msg = StringPrintf("Failed to find directory %s", kAndroidRootDefaultPath);
+ *error_msg =
+ StringPrintf("Failed to find default Android Root directory %s", kAndroidRootDefaultPath);
return "";
}
return kAndroidRootDefaultPath;
@@ -162,7 +187,7 @@
}
}
if (must_exist && !OS::DirectoryExists(android_dir)) {
- *error_msg = StringPrintf("Failed to find %s directory %s", env_var, android_dir);
+ *error_msg = StringPrintf("Failed to find directory %s", android_dir);
return nullptr;
}
return android_dir;
@@ -179,16 +204,71 @@
}
}
+static std::string GetAndroidRuntimeRootSafe(bool must_exist, /*out*/ std::string* error_msg) {
+#ifdef _WIN32
+ UNUSED(kAndroidRuntimeRootEnvVar, kAndroidRuntimeApexDefaultPath, GetRootContainingLibartbase);
+ UNUSED(must_exist);
+ *error_msg = "GetAndroidRuntimeRootSafe unsupported for Windows.";
+ return "";
+#else
+ // Prefer ANDROID_RUNTIME_ROOT if it's set.
+ const char* android_runtime_root_from_env = getenv(kAndroidRuntimeRootEnvVar);
+ if (android_runtime_root_from_env != nullptr) {
+ if (must_exist && !OS::DirectoryExists(android_runtime_root_from_env)) {
+ *error_msg = StringPrintf("Failed to find %s directory %s",
+ kAndroidRuntimeRootEnvVar,
+ android_runtime_root_from_env);
+ return "";
+ }
+ return android_runtime_root_from_env;
+ }
+
+ // On target, libartbase is normally installed in
+ // "$ANDROID_RUNTIME_ROOT/lib(64)" (e.g. something like
+ // "/apex/com.android.runtime/lib(64)". Use this information to infer the
+ // location of the Android Runtime Root (on target only).
+ if (kIsTargetBuild) {
+ // *However*, a copy of libartbase may still be installed outside the
+ // Android Runtime Root on some occasions, as ART target gtests install
+ // their binaries and their dependencies under the Android Root, i.e.
+ // "/system" (see b/129534335). For that reason, we cannot reliably use
+ // `GetRootContainingLibartbase` to find the Android Runtime Root.
+ // (Note that this is not really a problem in practice, as Android Q devices
+ // define ANDROID_RUNTIME_ROOT in their default environment, and will
+ // instead use the logic above anyway.)
+ //
+ // TODO(b/129534335): Re-enable this logic when the only instance of
+ // libartbase on target is the one from the Runtime APEX.
+ if ((false)) {
+ std::string root_containing_libartbase = GetRootContainingLibartbase();
+ if (!root_containing_libartbase.empty()) {
+ return root_containing_libartbase;
+ }
+ }
+ }
+
+ // Try the default path.
+ if (must_exist && !OS::DirectoryExists(kAndroidRuntimeApexDefaultPath)) {
+ *error_msg = StringPrintf("Failed to find default Android Runtime Root directory %s",
+ kAndroidRuntimeApexDefaultPath);
+ return "";
+ }
+ return kAndroidRuntimeApexDefaultPath;
+#endif
+}
+
std::string GetAndroidRuntimeRootSafe(std::string* error_msg) {
- const char* android_dir = GetAndroidDirSafe(kAndroidRuntimeRootEnvVar,
- kAndroidRuntimeApexDefaultPath,
- /* must_exist= */ true,
- error_msg);
- return (android_dir != nullptr) ? android_dir : "";
+ return GetAndroidRuntimeRootSafe(/* must_exist= */ true, error_msg);
}
std::string GetAndroidRuntimeRoot() {
- return GetAndroidDir(kAndroidRuntimeRootEnvVar, kAndroidRuntimeApexDefaultPath);
+ std::string error_msg;
+ std::string ret = GetAndroidRuntimeRootSafe(&error_msg);
+ if (ret.empty()) {
+ LOG(FATAL) << error_msg;
+ UNREACHABLE();
+ }
+ return ret;
}
std::string GetAndroidDataSafe(std::string* error_msg) {
@@ -309,6 +389,16 @@
}
}
+bool LocationIsOnRuntimeModule(const char* full_path) {
+ std::string unused_error_msg;
+ std::string module_path =
+ GetAndroidRuntimeRootSafe(/* must_exist= */ kIsTargetBuild, &unused_error_msg);
+ if (module_path.empty()) {
+ return false;
+ }
+ return android::base::StartsWith(full_path, module_path);
+}
+
static bool StartsWithSlash(const char* str) {
DCHECK(str != nullptr);
return str[0] == '/';
@@ -364,10 +454,6 @@
/* subdir= */ "/framework");
}
-bool LocationIsOnRuntimeModule(const char* full_path) {
- return IsLocationOnModule(full_path, kAndroidRuntimeRootEnvVar, kAndroidRuntimeApexDefaultPath);
-}
-
bool LocationIsOnConscryptModule(const char* full_path) {
return IsLocationOnModule(
full_path, kAndroidConscryptRootEnvVar, kAndroidConscryptApexDefaultPath);
diff --git a/libartbase/base/file_utils.h b/libartbase/base/file_utils.h
index 92b09c9..2115e95 100644
--- a/libartbase/base/file_utils.h
+++ b/libartbase/base/file_utils.h
@@ -29,11 +29,26 @@
bool ReadFileToString(const std::string& file_name, std::string* result);
+// These methods return the Android Root, which is the historical location of
+// the Android "system" directory, containing the built Android artifacts. On
+// target, this is normally "/system". On host this is usually a directory under
+// the build tree, e.g. "$ANDROID_BUILD_TOP/out/host/linux-x86". The location of
+// the Android Root can be overriden using the ANDROID_ROOT environment
+// variable.
+//
// Find $ANDROID_ROOT, /system, or abort.
std::string GetAndroidRoot();
// Find $ANDROID_ROOT, /system, or return an empty string.
std::string GetAndroidRootSafe(/*out*/ std::string* error_msg);
+// These methods return the Android Runtime Root, which is the location of the
+// (activated) Android Runtime APEX module. On target, this is normally
+// "/apex/com.android.runtime". On host, this is usually a subdirectory of the
+// Android Root, e.g.
+// "$ANDROID_BUILD_TOP/out/host/linux-x86/com.android.runtime". The location of
+// the Android Runtime Root can be overriden using the ANDROID_RUNTIME_ROOT
+// environment variable.
+//
// Find $ANDROID_RUNTIME_ROOT, /apex/com.android.runtime, or abort.
std::string GetAndroidRuntimeRoot();
// Find $ANDROID_RUNTIME_ROOT, /apex/com.android.runtime, or return an empty string.
diff --git a/libartbase/base/file_utils_test.cc b/libartbase/base/file_utils_test.cc
index 0a5a7a7..a217770 100644
--- a/libartbase/base/file_utils_test.cc
+++ b/libartbase/base/file_utils_test.cc
@@ -79,19 +79,22 @@
ASSERT_EQ(0, setenv("ANDROID_ROOT", "/this/is/obviously/bogus", /* overwrite */ 1));
EXPECT_EQ(GetAndroidRootSafe(&error_msg), "");
- // Unset ANDROID_ROOT and see that it still returns something (as libart code is running).
- ASSERT_EQ(0, unsetenv("ANDROID_ROOT"));
- std::string android_root3 = GetAndroidRootSafe(&error_msg);
- // This should be the same as the other root (modulo realpath), otherwise the test setup is
- // broken. On non-bionic. On bionic we can be running with a different libart that lives outside
- // of ANDROID_ROOT
- UniqueCPtr<char> real_root3(realpath(android_root3.c_str(), nullptr));
+ // Inferring the Android Root from the location of libartbase only works on host.
+ if (!kIsTargetBuild) {
+ // Unset ANDROID_ROOT and see that it still returns something (as libartbase code is running).
+ ASSERT_EQ(0, unsetenv("ANDROID_ROOT"));
+ std::string android_root3 = GetAndroidRootSafe(&error_msg);
+ // This should be the same as the other root (modulo realpath), otherwise the test setup is
+ // broken. On non-bionic. On bionic we can be running with a different libartbase that lives
+ // outside of ANDROID_ROOT.
+ UniqueCPtr<char> real_root3(realpath(android_root3.c_str(), nullptr));
#if !defined(__BIONIC__ ) || defined(__ANDROID__)
- UniqueCPtr<char> real_root(realpath(android_root.c_str(), nullptr));
- EXPECT_STREQ(real_root.get(), real_root3.get()) << error_msg;
+ UniqueCPtr<char> real_root(realpath(android_root.c_str(), nullptr));
+ EXPECT_STREQ(real_root.get(), real_root3.get()) << error_msg;
#else
- EXPECT_STRNE(real_root3.get(), "") << error_msg;
+ EXPECT_STRNE(real_root3.get(), "") << error_msg;
#endif
+ }
// Reset ANDROID_ROOT, as other things may depend on it.
ASSERT_EQ(0, setenv("ANDROID_ROOT", android_root_env.c_str(), /* overwrite */ 1));
@@ -118,6 +121,32 @@
ASSERT_EQ(0, setenv("ANDROID_RUNTIME_ROOT", "/this/is/obviously/bogus", /* overwrite */ 1));
EXPECT_EQ(GetAndroidRuntimeRootSafe(&error_msg), "");
+ // Inferring the Android Runtime Root from the location of libartbase only works on target.
+ if (kIsTargetBuild) {
+ // Disabled for now, as we cannot reliably use `GetRootContainingLibartbase`
+ // to find the Android Runtime Root on target yet (see comment in
+ // `GetAndroidRuntimeRootSafe`).
+ //
+ // TODO(b/129534335): Re-enable this part of the test on target when the
+ // only instance of libartbase is the one from the Runtime APEX.
+ if ((false)) {
+ // Unset ANDROID_RUNTIME_ROOT and see that it still returns something (as
+ // libartbase code is running).
+ ASSERT_EQ(0, unsetenv("ANDROID_RUNTIME_ROOT"));
+ std::string android_runtime_root3 = GetAndroidRuntimeRootSafe(&error_msg);
+ // This should be the same as the other root (modulo realpath), otherwise
+ // the test setup is broken. On non-bionic. On bionic we can be running
+ // with a different libartbase that lives outside of ANDROID_RUNTIME_ROOT.
+ UniqueCPtr<char> real_root3(realpath(android_runtime_root3.c_str(), nullptr));
+#if !defined(__BIONIC__ ) || defined(__ANDROID__)
+ UniqueCPtr<char> real_root(realpath(android_runtime_root.c_str(), nullptr));
+ EXPECT_STREQ(real_root.get(), real_root3.get()) << error_msg;
+#else
+ EXPECT_STRNE(real_root3.get(), "") << error_msg;
+#endif
+ }
+ }
+
// Reset ANDROID_RUNTIME_ROOT, as other things may depend on it.
ASSERT_EQ(0, setenv("ANDROID_RUNTIME_ROOT", android_runtime_root_env.c_str(), /* overwrite */ 1));
}