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));
 }