Pass boot class path to ImageSpace::LoadBootImage.

When loading the boot image by ImageSpace, do not rely on
the boot class path (BCP) recorded in the primary boot image
oat file. Instead, check the BCP from image against the BCP
provided by the Runtime, i.e. from -Xbootclasspath:_ or the
environment variable BOOTCLASSPATH. For now, allow Runtime
to extract the BCP from the primary boot image oat file when
we have -Ximage:_ but no BCP.

Also pass the boot class path components down to the OatFile
so that we can open and use the original dex files when we
do not have copies in the vdex files. This allows compiling
app prebuilts when the boot dex files are uncompressed and
aligned in the original jars.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: Pixel 2 XL boots.
Test: m test-art-target-gtest
Test: testrunner.py --target --optimizing
Test: run-libcore-tests.sh --mode=device --variant=X64
Test: Manually create boot image jars with uncompressed and
      aligned dex files, compile a boot image with dex files
      not copied into vdex files and use this boot image for
      compiling a prebuilt services.{art,vdex,odex}.
Bug: 119868597
Change-Id: If9411ddbecf6bb90bfa9233040931c5e833f6aee
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 9b2e1a1..6196612 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1037,13 +1037,20 @@
   runtime->SetSentinel(heap->AllocNonMovableObject<true>(
       self, java_lang_Object, java_lang_Object->GetObjectSize(), VoidFunctor()));
 
-  for (gc::space::ImageSpace* image_space : spaces) {
+  const std::vector<std::string>& boot_class_path = runtime->GetBootClassPath();
+  if (boot_class_path.size() != spaces.size()) {
+    *error_msg = StringPrintf("Boot class path has %zu components but there are %zu image spaces.",
+                              boot_class_path.size(),
+                              spaces.size());
+    return false;
+  }
+  for (size_t i = 0u, size = spaces.size(); i != size; ++i) {
     // Boot class loader, use a null handle.
     std::vector<std::unique_ptr<const DexFile>> dex_files;
-    if (!AddImageSpace(image_space,
+    if (!AddImageSpace(spaces[i],
                        ScopedNullHandle<mirror::ClassLoader>(),
-                       /*dex_elements=*/nullptr,
-                       /*dex_location=*/nullptr,
+                       /*dex_elements=*/ nullptr,
+                       /*dex_location=*/ boot_class_path[i].c_str(),
                        /*out*/&dex_files,
                        error_msg)) {
       return false;
@@ -1981,13 +1988,7 @@
     std::string dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
     // TODO: Only store qualified paths.
     // If non qualified, qualify it.
-    if (dex_file_location.find('/') == std::string::npos) {
-      std::string dex_location_path = dex_location;
-      const size_t pos = dex_location_path.find_last_of('/');
-      CHECK_NE(pos, std::string::npos);
-      dex_location_path = dex_location_path.substr(0, pos + 1);  // Keep trailing '/'
-      dex_file_location = dex_location_path + dex_file_location;
-    }
+    dex_file_location = OatFile::ResolveRelativeEncodedDexLocation(dex_location, dex_file_location);
     std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
                                                              dex_file_location.c_str(),
                                                              error_msg);