Use real locations when opening dex files from the context

The oat file assistant uses real locations when opening dex files which
will later be checked against the class loader context embedded in the oat
file. In order to guarantee a match we need to use real paths in the
context as well.

Bug: 64460009
Test: m test-art-host-gtest
Change-Id: I1b564baa0c933172891a10a448ce1ef129be5ea2
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index e7051b3..e97c6a0 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -16,6 +16,9 @@
 
 #include "class_loader_context.h"
 
+#include <stdlib.h>
+
+#include "android-base/file.h"
 #include "art_field-inl.h"
 #include "base/dchecked_vector.h"
 #include "base/stl_util.h"
@@ -202,10 +205,21 @@
   for (ClassLoaderInfo& info : class_loader_chain_) {
     for (const std::string& cp_elem : info.classpath) {
       // If path is relative, append it to the provided base directory.
-      std::string location = cp_elem;
-      if (location[0] != '/') {
-        location = classpath_dir + '/' + location;
+      std::string raw_location = cp_elem;
+      if (raw_location[0] != '/') {
+        raw_location = classpath_dir + '/' + raw_location;
       }
+
+      std::string location;  // the real location of the class path element.
+
+      if (!android::base::Realpath(raw_location, &location)) {
+        // If we can't get the realpath of the location there might be something wrong with the
+        // classpath (maybe the file was deleted).
+        // Do not continue in this case and return false.
+        PLOG(ERROR) << "Could not get the realpath of dex location " << raw_location;
+        return false;
+      }
+
       std::string error_msg;
       // When opening the dex files from the context we expect their checksum to match their
       // contents. So pass true to verify_checksum.
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index d4688c1..458f9f3 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -15,14 +15,16 @@
  */
 
 #include <gtest/gtest.h>
+#include <stdlib.h>
 
-#include "class_loader_context.h"
-#include "common_runtime_test.h"
 
 #include "base/dchecked_vector.h"
 #include "base/stl_util.h"
+#include "class_loader_context.h"
 #include "class_linker.h"
+#include "common_runtime_test.h"
 #include "dex_file.h"
+#include "dex2oat_environment_test.h"
 #include "handle_scope-inl.h"
 #include "mirror/class.h"
 #include "mirror/class_loader.h"
@@ -89,9 +91,22 @@
             info.opened_dex_files[cur_open_dex_index++];
         std::unique_ptr<const DexFile>& expected_dex_file = dex_files_for_cp_elem[i];
 
-        ASSERT_EQ(expected_dex_file->GetLocation(), opened_dex_file->GetLocation());
+        std::string expected_location = expected_dex_file->GetBaseLocation();
+        UniqueCPtr<const char[]> expected_real_location(
+            realpath(expected_location.c_str(), nullptr));
+        ASSERT_TRUE(expected_real_location != nullptr) << expected_location;
+        expected_location.assign(expected_real_location.get());
+        expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation());
+
+        ASSERT_EQ(expected_location, opened_dex_file->GetLocation());
         ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
-        ASSERT_EQ(info.classpath[k], opened_dex_file->GetBaseLocation());
+
+        std::string class_path_location = info.classpath[k];
+        UniqueCPtr<const char[]> class_path_location_real(
+            realpath(class_path_location.c_str(), nullptr));
+        ASSERT_TRUE(class_path_location_real != nullptr);
+        class_path_location.assign(class_path_location_real.get());
+        ASSERT_EQ(class_path_location, opened_dex_file->GetBaseLocation());
       }
     }
   }
@@ -234,7 +249,6 @@
   std::string dex_name = GetTestDexFileName("Main");
   std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main");
 
-
   std::unique_ptr<ClassLoaderContext> context =
       ClassLoaderContext::Create(
           "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
@@ -253,6 +267,43 @@
   VerifyOpenDexFiles(context.get(), 1, all_dex_files1);
 }
 
+class ScratchSymLink {
+ public:
+  explicit ScratchSymLink(const std::string& file) {
+    // Use a temporary scratch file to get a unique name for the link.
+    ScratchFile scratchFile;
+    scratch_link_name_ = scratchFile.GetFilename() + ".link.jar";
+    CHECK_EQ(0, symlink(file.c_str(), scratch_link_name_.c_str()));
+  }
+
+  ~ScratchSymLink() {
+    CHECK_EQ(0, unlink(scratch_link_name_.c_str()));
+  }
+
+  const std::string& GetFilename() { return scratch_link_name_; }
+
+ private:
+  std::string scratch_link_name_;
+};
+
+TEST_F(ClassLoaderContextTest, OpenValidDexFilesSymLink) {
+  std::string myclass_dex_name = GetTestDexFileName("MyClass");
+  // Now replace the dex location with a symlink.
+  ScratchSymLink link(myclass_dex_name);
+
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[" + link.GetFilename() + "]");
+
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ ""));
+
+  VerifyContextSize(context.get(), 1);
+  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
+  std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+  all_dex_files0.push_back(&myclass_dex_files);
+
+  VerifyOpenDexFiles(context.get(), 0, all_dex_files0);
+}
+
 TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) {
   std::string dex_name = GetTestDexFileName("Main");
   std::unique_ptr<ClassLoaderContext> context =