Revert^4 "Add bss support for inlining BCP DexFiles for single image"

This reverts commit 1849c3a875aab44d9bff45623ec076b0331302f8.

Reason for revert: Relading after fix. It can be seen in PS 1..2

Bug: 154012332
Test: art/test/testrunner/testrunner.py --target --32 --optimizing
Test: art/test/testrunner/testrunner.py --host --64 --optimizing
Change-Id: I168572957363dd5ae1598279f2ecc8fb947a1fd5
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index fc1c07d..76d2a6d 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -283,7 +283,9 @@
     InvokeRuntimeCallingConvention calling_convention;
     if (must_resolve_type) {
       DCHECK(IsSameDexFile(cls_->GetDexFile(), arm64_codegen->GetGraph()->GetDexFile()) ||
-             arm64_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()));
+             arm64_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()) ||
+             ContainsElement(Runtime::Current()->GetClassLinker()->GetBootClassPath(),
+                             &cls_->GetDexFile()));
       dex::TypeIndex type_index = cls_->GetTypeIndex();
       __ Mov(calling_convention.GetRegisterAt(0).W(), type_index.index_);
       if (cls_->NeedsAccessCheck()) {
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index f65890b..1c243b6 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -531,7 +531,9 @@
     InvokeRuntimeCallingConventionARMVIXL calling_convention;
     if (must_resolve_type) {
       DCHECK(IsSameDexFile(cls_->GetDexFile(), arm_codegen->GetGraph()->GetDexFile()) ||
-             arm_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()));
+             arm_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()) ||
+             ContainsElement(Runtime::Current()->GetClassLinker()->GetBootClassPath(),
+                             &cls_->GetDexFile()));
       dex::TypeIndex type_index = cls_->GetTypeIndex();
       __ Mov(calling_convention.GetRegisterAt(0), type_index.index_);
       if (cls_->NeedsAccessCheck()) {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 0efd51c..4025684 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -290,7 +290,9 @@
     InvokeRuntimeCallingConvention calling_convention;
     if (must_resolve_type) {
       DCHECK(IsSameDexFile(cls_->GetDexFile(), x86_codegen->GetGraph()->GetDexFile()) ||
-             x86_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()));
+             x86_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()) ||
+             ContainsElement(Runtime::Current()->GetClassLinker()->GetBootClassPath(),
+                             &cls_->GetDexFile()));
       dex::TypeIndex type_index = cls_->GetTypeIndex();
       __ movl(calling_convention.GetRegisterAt(0), Immediate(type_index.index_));
       if (cls_->NeedsAccessCheck()) {
@@ -5474,7 +5476,9 @@
       ? invoke->AsInvokeInterface()->GetSpecialInputIndex()
       : invoke->AsInvokeStaticOrDirect()->GetSpecialInputIndex();
   DCHECK(IsSameDexFile(GetGraph()->GetDexFile(), *invoke->GetMethodReference().dex_file) ||
-         GetCompilerOptions().WithinOatFile(invoke->GetMethodReference().dex_file));
+         GetCompilerOptions().WithinOatFile(invoke->GetMethodReference().dex_file) ||
+         ContainsElement(Runtime::Current()->GetClassLinker()->GetBootClassPath(),
+                         invoke->GetMethodReference().dex_file));
   HX86ComputeBaseMethodAddress* method_address =
       invoke->InputAt(index)->AsX86ComputeBaseMethodAddress();
   // Add the patch entry and bind its label at the end of the instruction.
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index ff4ab59..8c1a533 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -274,7 +274,9 @@
     // Custom calling convention: RAX serves as both input and output.
     if (must_resolve_type) {
       DCHECK(IsSameDexFile(cls_->GetDexFile(), x86_64_codegen->GetGraph()->GetDexFile()) ||
-             x86_64_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()));
+             x86_64_codegen->GetCompilerOptions().WithinOatFile(&cls_->GetDexFile()) ||
+             ContainsElement(Runtime::Current()->GetClassLinker()->GetBootClassPath(),
+                             &cls_->GetDexFile()));
       dex::TypeIndex type_index = cls_->GetTypeIndex();
       __ movl(CpuRegister(RAX), Immediate(type_index.index_));
       if (cls_->NeedsAccessCheck()) {
@@ -1241,7 +1243,9 @@
 
 void CodeGeneratorX86_64::RecordMethodBssEntryPatch(HInvoke* invoke) {
   DCHECK(IsSameDexFile(GetGraph()->GetDexFile(), *invoke->GetMethodReference().dex_file) ||
-         GetCompilerOptions().WithinOatFile(invoke->GetMethodReference().dex_file));
+         GetCompilerOptions().WithinOatFile(invoke->GetMethodReference().dex_file) ||
+         ContainsElement(Runtime::Current()->GetClassLinker()->GetBootClassPath(),
+                         invoke->GetMethodReference().dex_file));
   method_bss_entry_patches_.emplace_back(invoke->GetMethodReference().dex_file,
                                          invoke->GetMethodReference().index);
   __ Bind(&method_bss_entry_patches_.back().label);
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 39f684b..741e6df 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1745,11 +1745,12 @@
   // Inline across dexfiles if the callee's DexFile is:
   // 1) in the bootclasspath, or
   if (callee->GetDeclaringClass()->GetClassLoader() == nullptr) {
-    // There are cases in which the BCP DexFiles are within the OatFile as far as the compiler
-    // options are concerned, but they have their own OatWriter (and therefore not in the same
-    // OatFile). Then, we request the BSS check for all BCP DexFiles.
-    // TODO(solanes): Add .bss support for BCP.
-    *out_needs_bss_check = true;
+    // In multi-image, each BCP DexFile has their own OatWriter. Since they don't cooperate with
+    // each other, we request the BSS check for them.
+    // TODO(solanes): Add .bss support for BCP multi-image.
+    const bool is_multi_image = codegen->GetCompilerOptions().IsBootImage() ||
+                                codegen->GetCompilerOptions().IsBootImageExtension();
+    *out_needs_bss_check = is_multi_image;
     return true;
   }
 
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 7c73187..55cee6d 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -470,6 +470,12 @@
     size_oat_dex_file_public_type_bss_mapping_offset_(0),
     size_oat_dex_file_package_type_bss_mapping_offset_(0),
     size_oat_dex_file_string_bss_mapping_offset_(0),
+    size_bcp_bss_info_size_(0),
+    size_bcp_bss_info_method_bss_mapping_offset_(0),
+    size_bcp_bss_info_type_bss_mapping_offset_(0),
+    size_bcp_bss_info_public_type_bss_mapping_offset_(0),
+    size_bcp_bss_info_package_type_bss_mapping_offset_(0),
+    size_bcp_bss_info_string_bss_mapping_offset_(0),
     size_oat_class_offsets_alignment_(0),
     size_oat_class_offsets_(0),
     size_oat_class_type_(0),
@@ -790,6 +796,10 @@
     offset = InitOatDexFiles(offset);
   }
   {
+    TimingLogger::ScopedTiming split("InitBcpBssInfo", timings_);
+    offset = InitBcpBssInfo(offset);
+  }
+  {
     TimingLogger::ScopedTiming split("InitOatCode", timings_);
     offset = InitOatCode(offset);
   }
@@ -944,10 +954,8 @@
   void AddBssReference(const DexFileReference& ref,
                        size_t number_of_indexes,
                        /*inout*/ SafeMap<const DexFile*, BitVector>* references) {
-    // We currently support inlining of throwing instructions only when they originate in the
-    // same oat file as the outer method. All .bss references are used by throwing instructions.
-    DCHECK(std::find(writer_->dex_files_->begin(), writer_->dex_files_->end(), ref.dex_file) !=
-           writer_->dex_files_->end());
+    DCHECK(ContainsElement(*writer_->dex_files_, ref.dex_file) ||
+           ContainsElement(Runtime::Current()->GetClassLinker()->GetBootClassPath(), ref.dex_file));
     DCHECK_LT(ref.index, number_of_indexes);
 
     auto refs_it = references->find(ref.dex_file);
@@ -1045,6 +1053,29 @@
   size_t compiled_methods_with_code_;
 };
 
+// .bss mapping offsets used for BCP DexFiles.
+struct OatWriter::BssMappingInfo {
+  // Offsets set in PrepareLayout.
+  uint32_t method_bss_mapping_offset = 0u;
+  uint32_t type_bss_mapping_offset = 0u;
+  uint32_t public_type_bss_mapping_offset = 0u;
+  uint32_t package_type_bss_mapping_offset = 0u;
+  uint32_t string_bss_mapping_offset = 0u;
+
+  // Offset of the BSSInfo start from beginning of OatHeader. It is used to validate file position
+  // when writing.
+  size_t offset_ = 0u;
+
+  static size_t SizeOf() {
+    return sizeof(method_bss_mapping_offset) +
+           sizeof(type_bss_mapping_offset) +
+           sizeof(public_type_bss_mapping_offset) +
+           sizeof(package_type_bss_mapping_offset) +
+           sizeof(string_bss_mapping_offset);
+  }
+  bool Write(OatWriter* oat_writer, OutputStream* out) const;
+};
+
 // CompiledMethod + metadata required to do ordered method layout.
 //
 // See also OrderedMethodVisitor.
@@ -2184,67 +2215,123 @@
   size_t number_of_public_type_dex_files = 0u;
   size_t number_of_package_type_dex_files = 0u;
   size_t number_of_string_dex_files = 0u;
-  PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet());
   for (size_t i = 0, size = dex_files_->size(); i != size; ++i) {
     const DexFile* dex_file = (*dex_files_)[i];
-    auto method_it = bss_method_entry_references_.find(dex_file);
-    if (method_it != bss_method_entry_references_.end()) {
-      const BitVector& method_indexes = method_it->second;
-      ++number_of_method_dex_files;
-      oat_dex_files_[i].method_bss_mapping_offset_ = offset;
-      offset += CalculateIndexBssMappingSize(
-          dex_file->NumMethodIds(),
-          static_cast<size_t>(pointer_size),
-          method_indexes,
-          [=](uint32_t index) {
-            return bss_method_entries_.Get({dex_file, index});
-          });
-    }
+    offset = InitIndexBssMappingsHelper(offset,
+                                        dex_file,
+                                        number_of_method_dex_files,
+                                        number_of_type_dex_files,
+                                        number_of_public_type_dex_files,
+                                        number_of_package_type_dex_files,
+                                        number_of_string_dex_files,
+                                        oat_dex_files_[i].method_bss_mapping_offset_,
+                                        oat_dex_files_[i].type_bss_mapping_offset_,
+                                        oat_dex_files_[i].public_type_bss_mapping_offset_,
+                                        oat_dex_files_[i].package_type_bss_mapping_offset_,
+                                        oat_dex_files_[i].string_bss_mapping_offset_);
+  }
 
-    auto type_it = bss_type_entry_references_.find(dex_file);
-    if (type_it != bss_type_entry_references_.end()) {
-      const BitVector& type_indexes = type_it->second;
-      ++number_of_type_dex_files;
-      oat_dex_files_[i].type_bss_mapping_offset_ = offset;
-      offset += CalculateIndexBssMappingSize(dex_file, type_indexes, bss_type_entries_);
-    }
-
-    auto public_type_it = bss_public_type_entry_references_.find(dex_file);
-    if (public_type_it != bss_public_type_entry_references_.end()) {
-      const BitVector& type_indexes = public_type_it->second;
-      ++number_of_public_type_dex_files;
-      oat_dex_files_[i].public_type_bss_mapping_offset_ = offset;
-      offset += CalculateIndexBssMappingSize(dex_file, type_indexes, bss_public_type_entries_);
-    }
-
-    auto package_type_it = bss_package_type_entry_references_.find(dex_file);
-    if (package_type_it != bss_package_type_entry_references_.end()) {
-      const BitVector& type_indexes = package_type_it->second;
-      ++number_of_package_type_dex_files;
-      oat_dex_files_[i].package_type_bss_mapping_offset_ = offset;
-      offset += CalculateIndexBssMappingSize(dex_file, type_indexes, bss_package_type_entries_);
-    }
-
-    auto string_it = bss_string_entry_references_.find(dex_file);
-    if (string_it != bss_string_entry_references_.end()) {
-      const BitVector& string_indexes = string_it->second;
-      ++number_of_string_dex_files;
-      oat_dex_files_[i].string_bss_mapping_offset_ = offset;
-      offset += CalculateIndexBssMappingSize(
-          dex_file->NumStringIds(),
-          sizeof(GcRoot<mirror::String>),
-          string_indexes,
-          [=](uint32_t index) {
-            return bss_string_entries_.Get({dex_file, dex::StringIndex(index)});
-          });
+  if (!(compiler_options_.IsBootImage() || compiler_options_.IsBootImageExtension())) {
+    ArrayRef<const DexFile* const> boot_class_path(
+        Runtime::Current()->GetClassLinker()->GetBootClassPath());
+    // We initialize bcp_bss_info for single image and purposively leave it empty for the multi
+    // image case.
+    // Note that we have an early break at the beginning of the method, so `bcp_bss_info_` will also
+    // be empty in the case of having no mappings at all.
+    DCHECK(bcp_bss_info_.empty());
+    bcp_bss_info_.resize(boot_class_path.size());
+    for (size_t i = 0, size = bcp_bss_info_.size(); i != size; ++i) {
+      const DexFile* dex_file = boot_class_path[i];
+      DCHECK(!ContainsElement(*dex_files_, dex_file));
+      offset = InitIndexBssMappingsHelper(offset,
+                                          dex_file,
+                                          number_of_method_dex_files,
+                                          number_of_type_dex_files,
+                                          number_of_public_type_dex_files,
+                                          number_of_package_type_dex_files,
+                                          number_of_string_dex_files,
+                                          bcp_bss_info_[i].method_bss_mapping_offset,
+                                          bcp_bss_info_[i].type_bss_mapping_offset,
+                                          bcp_bss_info_[i].public_type_bss_mapping_offset,
+                                          bcp_bss_info_[i].package_type_bss_mapping_offset,
+                                          bcp_bss_info_[i].string_bss_mapping_offset);
     }
   }
-  // Check that all dex files targeted by bss entries are in `*dex_files_`.
+
+  // Check that all dex files targeted by bss entries are in `*dex_files_`, or in the bootclaspath's
+  // DexFiles in the single image case.
   CHECK_EQ(number_of_method_dex_files, bss_method_entry_references_.size());
   CHECK_EQ(number_of_type_dex_files, bss_type_entry_references_.size());
   CHECK_EQ(number_of_public_type_dex_files, bss_public_type_entry_references_.size());
   CHECK_EQ(number_of_package_type_dex_files, bss_package_type_entry_references_.size());
   CHECK_EQ(number_of_string_dex_files, bss_string_entry_references_.size());
+
+  return offset;
+}
+
+size_t OatWriter::InitIndexBssMappingsHelper(size_t offset,
+                                             const DexFile* dex_file,
+                                             /*inout*/ size_t& number_of_method_dex_files,
+                                             /*inout*/ size_t& number_of_type_dex_files,
+                                             /*inout*/ size_t& number_of_public_type_dex_files,
+                                             /*inout*/ size_t& number_of_package_type_dex_files,
+                                             /*inout*/ size_t& number_of_string_dex_files,
+                                             /*inout*/ uint32_t& method_bss_mapping_offset,
+                                             /*inout*/ uint32_t& type_bss_mapping_offset,
+                                             /*inout*/ uint32_t& public_type_bss_mapping_offset,
+                                             /*inout*/ uint32_t& package_type_bss_mapping_offset,
+                                             /*inout*/ uint32_t& string_bss_mapping_offset) {
+  const PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet());
+  auto method_it = bss_method_entry_references_.find(dex_file);
+  if (method_it != bss_method_entry_references_.end()) {
+    const BitVector& method_indexes = method_it->second;
+    ++number_of_method_dex_files;
+    method_bss_mapping_offset = offset;
+    offset += CalculateIndexBssMappingSize(dex_file->NumMethodIds(),
+                                           static_cast<size_t>(pointer_size),
+                                           method_indexes,
+                                           [=](uint32_t index) {
+                                             return bss_method_entries_.Get({dex_file, index});
+                                           });
+  }
+
+  auto type_it = bss_type_entry_references_.find(dex_file);
+  if (type_it != bss_type_entry_references_.end()) {
+    const BitVector& type_indexes = type_it->second;
+    ++number_of_type_dex_files;
+    type_bss_mapping_offset = offset;
+    offset += CalculateIndexBssMappingSize(dex_file, type_indexes, bss_type_entries_);
+  }
+
+  auto public_type_it = bss_public_type_entry_references_.find(dex_file);
+  if (public_type_it != bss_public_type_entry_references_.end()) {
+    const BitVector& type_indexes = public_type_it->second;
+    ++number_of_public_type_dex_files;
+    public_type_bss_mapping_offset = offset;
+    offset += CalculateIndexBssMappingSize(dex_file, type_indexes, bss_public_type_entries_);
+  }
+
+  auto package_type_it = bss_package_type_entry_references_.find(dex_file);
+  if (package_type_it != bss_package_type_entry_references_.end()) {
+    const BitVector& type_indexes = package_type_it->second;
+    ++number_of_package_type_dex_files;
+    package_type_bss_mapping_offset = offset;
+    offset += CalculateIndexBssMappingSize(dex_file, type_indexes, bss_package_type_entries_);
+  }
+
+  auto string_it = bss_string_entry_references_.find(dex_file);
+  if (string_it != bss_string_entry_references_.end()) {
+    const BitVector& string_indexes = string_it->second;
+    ++number_of_string_dex_files;
+    string_bss_mapping_offset = offset;
+    offset += CalculateIndexBssMappingSize(
+        dex_file->NumStringIds(),
+        sizeof(GcRoot<mirror::String>),
+        string_indexes,
+        [=](uint32_t index) {
+          return bss_string_entries_.Get({dex_file, dex::StringIndex(index)});
+        });
+  }
   return offset;
 }
 
@@ -2257,6 +2344,23 @@
   return offset;
 }
 
+size_t OatWriter::InitBcpBssInfo(size_t offset) {
+  if (bcp_bss_info_.size() == 0) {
+    return offset;
+  }
+
+  // We first increase the offset to make room to store the number of BCP DexFiles, if we have at
+  // least one entry.
+  oat_header_->SetBcpBssInfoOffset(offset);
+  offset += sizeof(uint32_t);
+
+  for (BssMappingInfo& info : bcp_bss_info_) {
+    info.offset_ = offset;
+    offset += BssMappingInfo::SizeOf();
+  }
+  return offset;
+}
+
 size_t OatWriter::InitOatCode(size_t offset) {
   // calculate the offsets within OatHeader to executable code
   size_t old_offset = offset;
@@ -2487,6 +2591,12 @@
     return false;
   }
 
+  relative_offset = WriteBcpBssInfo(out, file_offset, relative_offset);
+  if (relative_offset == 0) {
+    PLOG(ERROR) << "Failed to write BCP bss information to " << out->GetLocation();
+    return false;
+  }
+
   // Write padding.
   off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent);
   relative_offset += size_executable_offset_alignment_;
@@ -2660,6 +2770,12 @@
     DO_STAT(size_oat_dex_file_public_type_bss_mapping_offset_);
     DO_STAT(size_oat_dex_file_package_type_bss_mapping_offset_);
     DO_STAT(size_oat_dex_file_string_bss_mapping_offset_);
+    DO_STAT(size_bcp_bss_info_size_);
+    DO_STAT(size_bcp_bss_info_method_bss_mapping_offset_);
+    DO_STAT(size_bcp_bss_info_type_bss_mapping_offset_);
+    DO_STAT(size_bcp_bss_info_public_type_bss_mapping_offset_);
+    DO_STAT(size_bcp_bss_info_package_type_bss_mapping_offset_);
+    DO_STAT(size_bcp_bss_info_string_bss_mapping_offset_);
     DO_STAT(size_oat_class_offsets_alignment_);
     DO_STAT(size_oat_class_offsets_);
     DO_STAT(size_oat_class_type_);
@@ -2849,6 +2965,111 @@
       [=](uint32_t index) { return bss_entries.Get({dex_file, dex::TypeIndex(index)}); });
 }
 
+size_t OatWriter::WriteIndexBssMappingsHelper(OutputStream* out,
+                                              size_t file_offset,
+                                              size_t relative_offset,
+                                              const DexFile* dex_file,
+                                              uint32_t method_bss_mapping_offset,
+                                              uint32_t type_bss_mapping_offset,
+                                              uint32_t public_type_bss_mapping_offset,
+                                              uint32_t package_type_bss_mapping_offset,
+                                              uint32_t string_bss_mapping_offset) {
+  const PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet());
+  auto method_it = bss_method_entry_references_.find(dex_file);
+  if (method_it != bss_method_entry_references_.end()) {
+    const BitVector& method_indexes = method_it->second;
+    DCHECK_EQ(relative_offset, method_bss_mapping_offset);
+    DCHECK_OFFSET();
+    size_t method_mappings_size =
+        WriteIndexBssMapping(out,
+                             dex_file->NumMethodIds(),
+                             static_cast<size_t>(pointer_size),
+                             method_indexes,
+                             [=](uint32_t index) {
+                               return bss_method_entries_.Get({dex_file, index});
+                             });
+    if (method_mappings_size == 0u) {
+      return 0u;
+    }
+    size_method_bss_mappings_ += method_mappings_size;
+    relative_offset += method_mappings_size;
+  } else {
+    DCHECK_EQ(0u, method_bss_mapping_offset);
+  }
+
+  auto type_it = bss_type_entry_references_.find(dex_file);
+  if (type_it != bss_type_entry_references_.end()) {
+    const BitVector& type_indexes = type_it->second;
+    DCHECK_EQ(relative_offset, type_bss_mapping_offset);
+    DCHECK_OFFSET();
+    size_t type_mappings_size =
+        WriteIndexBssMapping(out, dex_file, type_indexes, bss_type_entries_);
+    if (type_mappings_size == 0u) {
+      return 0u;
+    }
+    size_type_bss_mappings_ += type_mappings_size;
+    relative_offset += type_mappings_size;
+  } else {
+    DCHECK_EQ(0u, type_bss_mapping_offset);
+  }
+
+  auto public_type_it = bss_public_type_entry_references_.find(dex_file);
+  if (public_type_it != bss_public_type_entry_references_.end()) {
+    const BitVector& type_indexes = public_type_it->second;
+    DCHECK_EQ(relative_offset, public_type_bss_mapping_offset);
+    DCHECK_OFFSET();
+    size_t public_type_mappings_size =
+        WriteIndexBssMapping(out, dex_file, type_indexes, bss_public_type_entries_);
+    if (public_type_mappings_size == 0u) {
+      return 0u;
+    }
+    size_public_type_bss_mappings_ += public_type_mappings_size;
+    relative_offset += public_type_mappings_size;
+  } else {
+    DCHECK_EQ(0u, public_type_bss_mapping_offset);
+  }
+
+  auto package_type_it = bss_package_type_entry_references_.find(dex_file);
+  if (package_type_it != bss_package_type_entry_references_.end()) {
+    const BitVector& type_indexes = package_type_it->second;
+    DCHECK_EQ(relative_offset, package_type_bss_mapping_offset);
+    DCHECK_OFFSET();
+    size_t package_type_mappings_size =
+        WriteIndexBssMapping(out, dex_file, type_indexes, bss_package_type_entries_);
+    if (package_type_mappings_size == 0u) {
+      return 0u;
+    }
+    size_package_type_bss_mappings_ += package_type_mappings_size;
+    relative_offset += package_type_mappings_size;
+  } else {
+    DCHECK_EQ(0u, package_type_bss_mapping_offset);
+  }
+
+  auto string_it = bss_string_entry_references_.find(dex_file);
+  if (string_it != bss_string_entry_references_.end()) {
+    const BitVector& string_indexes = string_it->second;
+    DCHECK_EQ(relative_offset, string_bss_mapping_offset);
+    DCHECK_OFFSET();
+    size_t string_mappings_size =
+        WriteIndexBssMapping(out,
+                             dex_file->NumStringIds(),
+                             sizeof(GcRoot<mirror::String>),
+                             string_indexes,
+                             [=](uint32_t index) {
+                               return bss_string_entries_.Get({dex_file, dex::StringIndex(index)});
+                             });
+    if (string_mappings_size == 0u) {
+      return 0u;
+    }
+    size_string_bss_mappings_ += string_mappings_size;
+    relative_offset += string_mappings_size;
+  } else {
+    DCHECK_EQ(0u, string_bss_mapping_offset);
+  }
+
+  return relative_offset;
+}
+
 size_t OatWriter::WriteIndexBssMappings(OutputStream* out,
                                         size_t file_offset,
                                         size_t relative_offset) {
@@ -2862,104 +3083,45 @@
   }
   // If there are any classes, the class offsets allocation aligns the offset
   // and we cannot have method bss mappings without class offsets.
-  static_assert(alignof(IndexBssMapping) == sizeof(uint32_t),
-                "IndexBssMapping alignment check.");
+  static_assert(alignof(IndexBssMapping) == sizeof(uint32_t), "IndexBssMapping alignment check.");
   DCHECK_ALIGNED(relative_offset, sizeof(uint32_t));
 
-  PointerSize pointer_size = GetInstructionSetPointerSize(oat_header_->GetInstructionSet());
   for (size_t i = 0, size = dex_files_->size(); i != size; ++i) {
     const DexFile* dex_file = (*dex_files_)[i];
     OatDexFile* oat_dex_file = &oat_dex_files_[i];
-    auto method_it = bss_method_entry_references_.find(dex_file);
-    if (method_it != bss_method_entry_references_.end()) {
-      const BitVector& method_indexes = method_it->second;
-      DCHECK_EQ(relative_offset, oat_dex_file->method_bss_mapping_offset_);
-      DCHECK_OFFSET();
-      size_t method_mappings_size = WriteIndexBssMapping(
-          out,
-          dex_file->NumMethodIds(),
-          static_cast<size_t>(pointer_size),
-          method_indexes,
-          [=](uint32_t index) {
-            return bss_method_entries_.Get({dex_file, index});
-          });
-      if (method_mappings_size == 0u) {
-        return 0u;
-      }
-      size_method_bss_mappings_ += method_mappings_size;
-      relative_offset += method_mappings_size;
-    } else {
-      DCHECK_EQ(0u, oat_dex_file->method_bss_mapping_offset_);
+    relative_offset = WriteIndexBssMappingsHelper(out,
+                                                  file_offset,
+                                                  relative_offset,
+                                                  dex_file,
+                                                  oat_dex_file->method_bss_mapping_offset_,
+                                                  oat_dex_file->type_bss_mapping_offset_,
+                                                  oat_dex_file->public_type_bss_mapping_offset_,
+                                                  oat_dex_file->package_type_bss_mapping_offset_,
+                                                  oat_dex_file->string_bss_mapping_offset_);
+    if (relative_offset == 0u) {
+      return 0u;
     }
+  }
 
-    auto type_it = bss_type_entry_references_.find(dex_file);
-    if (type_it != bss_type_entry_references_.end()) {
-      const BitVector& type_indexes = type_it->second;
-      DCHECK_EQ(relative_offset, oat_dex_file->type_bss_mapping_offset_);
-      DCHECK_OFFSET();
-      size_t type_mappings_size =
-          WriteIndexBssMapping(out, dex_file, type_indexes, bss_type_entries_);
-      if (type_mappings_size == 0u) {
+  if (!(compiler_options_.IsBootImage() || compiler_options_.IsBootImageExtension())) {
+    ArrayRef<const DexFile* const> boot_class_path(
+        Runtime::Current()->GetClassLinker()->GetBootClassPath());
+    for (size_t i = 0, size = bcp_bss_info_.size(); i != size; ++i) {
+      const DexFile* dex_file = boot_class_path[i];
+      DCHECK(!ContainsElement(*dex_files_, dex_file));
+      relative_offset =
+          WriteIndexBssMappingsHelper(out,
+                                      file_offset,
+                                      relative_offset,
+                                      dex_file,
+                                      bcp_bss_info_[i].method_bss_mapping_offset,
+                                      bcp_bss_info_[i].type_bss_mapping_offset,
+                                      bcp_bss_info_[i].public_type_bss_mapping_offset,
+                                      bcp_bss_info_[i].package_type_bss_mapping_offset,
+                                      bcp_bss_info_[i].string_bss_mapping_offset);
+      if (relative_offset == 0u) {
         return 0u;
       }
-      size_type_bss_mappings_ += type_mappings_size;
-      relative_offset += type_mappings_size;
-    } else {
-      DCHECK_EQ(0u, oat_dex_file->type_bss_mapping_offset_);
-    }
-
-    auto public_type_it = bss_public_type_entry_references_.find(dex_file);
-    if (public_type_it != bss_public_type_entry_references_.end()) {
-      const BitVector& type_indexes = public_type_it->second;
-      DCHECK_EQ(relative_offset, oat_dex_file->public_type_bss_mapping_offset_);
-      DCHECK_OFFSET();
-      size_t public_type_mappings_size =
-          WriteIndexBssMapping(out, dex_file, type_indexes, bss_public_type_entries_);
-      if (public_type_mappings_size == 0u) {
-        return 0u;
-      }
-      size_public_type_bss_mappings_ += public_type_mappings_size;
-      relative_offset += public_type_mappings_size;
-    } else {
-      DCHECK_EQ(0u, oat_dex_file->public_type_bss_mapping_offset_);
-    }
-
-    auto package_type_it = bss_package_type_entry_references_.find(dex_file);
-    if (package_type_it != bss_package_type_entry_references_.end()) {
-      const BitVector& type_indexes = package_type_it->second;
-      DCHECK_EQ(relative_offset, oat_dex_file->package_type_bss_mapping_offset_);
-      DCHECK_OFFSET();
-      size_t package_type_mappings_size =
-          WriteIndexBssMapping(out, dex_file, type_indexes, bss_package_type_entries_);
-      if (package_type_mappings_size == 0u) {
-        return 0u;
-      }
-      size_package_type_bss_mappings_ += package_type_mappings_size;
-      relative_offset += package_type_mappings_size;
-    } else {
-      DCHECK_EQ(0u, oat_dex_file->package_type_bss_mapping_offset_);
-    }
-
-    auto string_it = bss_string_entry_references_.find(dex_file);
-    if (string_it != bss_string_entry_references_.end()) {
-      const BitVector& string_indexes = string_it->second;
-      DCHECK_EQ(relative_offset, oat_dex_file->string_bss_mapping_offset_);
-      DCHECK_OFFSET();
-      size_t string_mappings_size = WriteIndexBssMapping(
-          out,
-          dex_file->NumStringIds(),
-          sizeof(GcRoot<mirror::String>),
-          string_indexes,
-          [=](uint32_t index) {
-            return bss_string_entries_.Get({dex_file, dex::StringIndex(index)});
-          });
-      if (string_mappings_size == 0u) {
-        return 0u;
-      }
-      size_string_bss_mappings_ += string_mappings_size;
-      relative_offset += string_mappings_size;
-    } else {
-      DCHECK_EQ(0u, oat_dex_file->string_bss_mapping_offset_);
     }
   }
   return relative_offset;
@@ -2983,6 +3145,34 @@
   return relative_offset;
 }
 
+size_t OatWriter::WriteBcpBssInfo(OutputStream* out, size_t file_offset, size_t relative_offset) {
+  TimingLogger::ScopedTiming split("WriteBcpBssInfo", timings_);
+
+  const uint32_t number_of_bcp_dexfiles = bcp_bss_info_.size();
+  // We skip adding the number of DexFiles if we have no .bss mappings.
+  if (number_of_bcp_dexfiles == 0) {
+    return relative_offset;
+  }
+
+  if (!out->WriteFully(&number_of_bcp_dexfiles, sizeof(number_of_bcp_dexfiles))) {
+    PLOG(ERROR) << "Failed to write the number of BCP dexfiles to " << out->GetLocation();
+    return false;
+  }
+  size_bcp_bss_info_size_ = sizeof(number_of_bcp_dexfiles);
+  relative_offset += size_bcp_bss_info_size_;
+
+  for (size_t i = 0, size = number_of_bcp_dexfiles; i != size; ++i) {
+    DCHECK_EQ(relative_offset, bcp_bss_info_[i].offset_);
+    DCHECK_OFFSET();
+    if (!bcp_bss_info_[i].Write(this, out)) {
+      return 0u;
+    }
+    relative_offset += BssMappingInfo::SizeOf();
+  }
+
+  return relative_offset;
+}
+
 size_t OatWriter::WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset) {
   if (GetCompilerOptions().IsBootImage() && primary_oat_file_) {
     InstructionSet instruction_set = compiler_options_.GetInstructionSet();
@@ -3972,6 +4162,45 @@
   return true;
 }
 
+bool OatWriter::BssMappingInfo::Write(OatWriter* oat_writer, OutputStream* out) const {
+  const size_t file_offset = oat_writer->oat_data_offset_;
+  DCHECK_OFFSET_();
+
+  if (!out->WriteFully(&method_bss_mapping_offset, sizeof(method_bss_mapping_offset))) {
+    PLOG(ERROR) << "Failed to write method bss mapping offset to " << out->GetLocation();
+    return false;
+  }
+  oat_writer->size_bcp_bss_info_method_bss_mapping_offset_ += sizeof(method_bss_mapping_offset);
+
+  if (!out->WriteFully(&type_bss_mapping_offset, sizeof(type_bss_mapping_offset))) {
+    PLOG(ERROR) << "Failed to write type bss mapping offset to " << out->GetLocation();
+    return false;
+  }
+  oat_writer->size_bcp_bss_info_type_bss_mapping_offset_ += sizeof(type_bss_mapping_offset);
+
+  if (!out->WriteFully(&public_type_bss_mapping_offset, sizeof(public_type_bss_mapping_offset))) {
+    PLOG(ERROR) << "Failed to write public type bss mapping offset to " << out->GetLocation();
+    return false;
+  }
+  oat_writer->size_bcp_bss_info_public_type_bss_mapping_offset_ +=
+      sizeof(public_type_bss_mapping_offset);
+
+  if (!out->WriteFully(&package_type_bss_mapping_offset, sizeof(package_type_bss_mapping_offset))) {
+    PLOG(ERROR) << "Failed to write package type bss mapping offset to " << out->GetLocation();
+    return false;
+  }
+  oat_writer->size_bcp_bss_info_package_type_bss_mapping_offset_ +=
+      sizeof(package_type_bss_mapping_offset);
+
+  if (!out->WriteFully(&string_bss_mapping_offset, sizeof(string_bss_mapping_offset))) {
+    PLOG(ERROR) << "Failed to write string bss mapping offset to " << out->GetLocation();
+    return false;
+  }
+  oat_writer->size_bcp_bss_info_string_bss_mapping_offset_ += sizeof(string_bss_mapping_offset);
+
+  return true;
+}
+
 bool OatWriter::OatDexFile::WriteClassOffsets(OatWriter* oat_writer, OutputStream* out) {
   if (!out->WriteFully(class_offsets_.data(), GetClassOffsetsRawSize())) {
     PLOG(ERROR) << "Failed to write oat class offsets for " << GetLocation()
diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h
index ad14d60..21ae77a 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -244,6 +244,7 @@
   }
 
  private:
+  struct BssMappingInfo;
   class ChecksumUpdatingOutputStream;
   class DexFileSource;
   class OatClassHeader;
@@ -307,6 +308,7 @@
   size_t InitOatMaps(size_t offset);
   size_t InitIndexBssMappings(size_t offset);
   size_t InitOatDexFiles(size_t offset);
+  size_t InitBcpBssInfo(size_t offset);
   size_t InitOatCode(size_t offset);
   size_t InitOatCodeDexFiles(size_t offset);
   size_t InitDataBimgRelRoLayout(size_t offset);
@@ -317,9 +319,32 @@
   size_t WriteMaps(OutputStream* out, size_t file_offset, size_t relative_offset);
   size_t WriteIndexBssMappings(OutputStream* out, size_t file_offset, size_t relative_offset);
   size_t WriteOatDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset);
+  size_t WriteBcpBssInfo(OutputStream* out, size_t file_offset, size_t relative_offset);
   size_t WriteCode(OutputStream* out, size_t file_offset, size_t relative_offset);
   size_t WriteCodeDexFiles(OutputStream* out, size_t file_offset, size_t relative_offset);
   size_t WriteDataBimgRelRo(OutputStream* out, size_t file_offset, size_t relative_offset);
+  // These helpers extract common code from BCP and non-BCP DexFiles from its corresponding methods.
+  size_t WriteIndexBssMappingsHelper(OutputStream* out,
+                                     size_t file_offset,
+                                     size_t relative_offset,
+                                     const DexFile* dex_file,
+                                     uint32_t method_bss_mapping_offset,
+                                     uint32_t type_bss_mapping_offset,
+                                     uint32_t public_type_bss_mapping_offset,
+                                     uint32_t package_type_bss_mapping_offset,
+                                     uint32_t string_bss_mapping_offset);
+  size_t InitIndexBssMappingsHelper(size_t offset,
+                                    const DexFile* dex_file,
+                                    /*inout*/ size_t& number_of_method_dex_files,
+                                    /*inout*/ size_t& number_of_type_dex_files,
+                                    /*inout*/ size_t& number_of_public_type_dex_files,
+                                    /*inout*/ size_t& number_of_package_type_dex_files,
+                                    /*inout*/ size_t& number_of_string_dex_files,
+                                    /*inout*/ uint32_t& method_bss_mapping_offset,
+                                    /*inout*/ uint32_t& type_bss_mapping_offset,
+                                    /*inout*/ uint32_t& public_type_bss_mapping_offset,
+                                    /*inout*/ uint32_t& package_type_bss_mapping_offset,
+                                    /*inout*/ uint32_t& string_bss_mapping_offset);
 
   bool RecordOatDataOffset(OutputStream* out);
   void InitializeTypeLookupTables(
@@ -423,6 +448,9 @@
   // The offset of the GC roots in .bss section.
   size_t bss_roots_offset_;
 
+  // OatFile's information regarding the bss metadata for BCP DexFiles. Empty for multi-image.
+  std::vector<BssMappingInfo> bcp_bss_info_;
+
   // Map for allocating .data.bimg.rel.ro entries. Indexed by the boot image offset of the
   // relocation. The value is the assigned offset within the .data.bimg.rel.ro section.
   SafeMap<uint32_t, size_t> data_bimg_rel_ro_entries_;
@@ -534,6 +562,12 @@
   uint32_t size_oat_dex_file_public_type_bss_mapping_offset_;
   uint32_t size_oat_dex_file_package_type_bss_mapping_offset_;
   uint32_t size_oat_dex_file_string_bss_mapping_offset_;
+  uint32_t size_bcp_bss_info_size_;
+  uint32_t size_bcp_bss_info_method_bss_mapping_offset_;
+  uint32_t size_bcp_bss_info_type_bss_mapping_offset_;
+  uint32_t size_bcp_bss_info_public_type_bss_mapping_offset_;
+  uint32_t size_bcp_bss_info_package_type_bss_mapping_offset_;
+  uint32_t size_bcp_bss_info_string_bss_mapping_offset_;
   uint32_t size_oat_class_offsets_alignment_;
   uint32_t size_oat_class_offsets_;
   uint32_t size_oat_class_type_;
diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc
index b0ff362..6b2198d 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -502,7 +502,7 @@
 TEST_F(OatTest, OatHeaderSizeCheck) {
   // If this test is failing and you have to update these constants,
   // it is time to update OatHeader::kOatVersion
-  EXPECT_EQ(64U, sizeof(OatHeader));
+  EXPECT_EQ(68U, sizeof(OatHeader));
   EXPECT_EQ(4U, sizeof(OatMethodOffsets));
   EXPECT_EQ(4U, sizeof(OatQuickMethodHeader));
   EXPECT_EQ(167 * static_cast<size_t>(GetInstructionSetPointerSize(kRuntimeISA)),
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index f8d8128..c7e6c09 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -528,42 +528,48 @@
       }
 
       if (!options_.dump_header_only_) {
-        // Dump .bss entries.
-        DumpBssEntries(
-            os,
-            "ArtMethod",
-            oat_dex_file->GetMethodBssMapping(),
-            dex_file->NumMethodIds(),
-            static_cast<size_t>(GetInstructionSetPointerSize(instruction_set_)),
-            [=](uint32_t index) { return dex_file->PrettyMethod(index); });
-        DumpBssEntries(
-            os,
-            "Class",
-            oat_dex_file->GetTypeBssMapping(),
-            dex_file->NumTypeIds(),
-            sizeof(GcRoot<mirror::Class>),
-            [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
-        DumpBssEntries(
-            os,
-            "Public Class",
-            oat_dex_file->GetPublicTypeBssMapping(),
-            dex_file->NumTypeIds(),
-            sizeof(GcRoot<mirror::Class>),
-            [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
-        DumpBssEntries(
-            os,
-            "Package Class",
-            oat_dex_file->GetPackageTypeBssMapping(),
-            dex_file->NumTypeIds(),
-            sizeof(GcRoot<mirror::Class>),
-            [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
-        DumpBssEntries(
-            os,
-            "String",
-            oat_dex_file->GetStringBssMapping(),
-            dex_file->NumStringIds(),
-            sizeof(GcRoot<mirror::Class>),
-            [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); });
+        DumpBssMappings(os,
+                        dex_file,
+                        oat_dex_file->GetMethodBssMapping(),
+                        oat_dex_file->GetTypeBssMapping(),
+                        oat_dex_file->GetPublicTypeBssMapping(),
+                        oat_dex_file->GetPackageTypeBssMapping(),
+                        oat_dex_file->GetStringBssMapping());
+      }
+    }
+
+    if (!options_.dump_header_only_) {
+      Runtime* const runtime = Runtime::Current();
+      ClassLinker* const linker = runtime != nullptr ? runtime->GetClassLinker() : nullptr;
+
+      if (linker != nullptr) {
+        ArrayRef<const DexFile* const> bcp_dex_files(linker->GetBootClassPath());
+        // The guarantee that we have is that we can safely take a look the BCP DexFiles in
+        // [0..number_of_compiled_bcp_dexfiles) since the runtime may add more DexFiles after that.
+        // As a note, in the case of not having mappings or in the case of multi image we
+        // purposively leave `oat_file_.bcp_bss_info` empty.
+        CHECK_LE(oat_file_.bcp_bss_info_.size(), bcp_dex_files.size());
+        for (size_t i = 0; i < oat_file_.bcp_bss_info_.size(); i++) {
+          const DexFile* const dex_file = bcp_dex_files[i];
+          os << "Dumping entries for BCP DexFile: " << dex_file->GetLocation() << "\n";
+          DumpBssMappings(os,
+                          dex_file,
+                          oat_file_.bcp_bss_info_[i].method_bss_mapping,
+                          oat_file_.bcp_bss_info_[i].type_bss_mapping,
+                          oat_file_.bcp_bss_info_[i].public_type_bss_mapping,
+                          oat_file_.bcp_bss_info_[i].package_type_bss_mapping,
+                          oat_file_.bcp_bss_info_[i].string_bss_mapping);
+        }
+      } else {
+        // We don't have a runtime, just dump the offsets
+        for (size_t i = 0; i < oat_file_.bcp_bss_info_.size(); i++) {
+          os << "We don't have a runtime, just dump the offsets for BCP Dexfile " << i << "\n";
+          DumpBssOffsets(os, "ArtMethod", oat_file_.bcp_bss_info_[i].method_bss_mapping);
+          DumpBssOffsets(os, "Class", oat_file_.bcp_bss_info_[i].type_bss_mapping);
+          DumpBssOffsets(os, "Public Class", oat_file_.bcp_bss_info_[i].public_type_bss_mapping);
+          DumpBssOffsets(os, "Package Class", oat_file_.bcp_bss_info_[i].package_type_bss_mapping);
+          DumpBssOffsets(os, "String", oat_file_.bcp_bss_info_[i].string_bss_mapping);
+        }
       }
     }
 
@@ -1680,6 +1686,71 @@
     os << std::dec;
   }
 
+  void DumpBssMappings(std::ostream& os,
+                       const DexFile* dex_file,
+                       const IndexBssMapping* method_bss_mapping,
+                       const IndexBssMapping* type_bss_mapping,
+                       const IndexBssMapping* public_type_bss_mapping,
+                       const IndexBssMapping* package_type_bss_mapping,
+                       const IndexBssMapping* string_bss_mapping) {
+    DumpBssEntries(os,
+                   "ArtMethod",
+                   method_bss_mapping,
+                   dex_file->NumMethodIds(),
+                   static_cast<size_t>(GetInstructionSetPointerSize(instruction_set_)),
+                   [=](uint32_t index) { return dex_file->PrettyMethod(index); });
+    DumpBssEntries(os,
+                   "Class",
+                   type_bss_mapping,
+                   dex_file->NumTypeIds(),
+                   sizeof(GcRoot<mirror::Class>),
+                   [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
+    DumpBssEntries(os,
+                   "Public Class",
+                   public_type_bss_mapping,
+                   dex_file->NumTypeIds(),
+                   sizeof(GcRoot<mirror::Class>),
+                   [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
+    DumpBssEntries(os,
+                   "Package Class",
+                   package_type_bss_mapping,
+                   dex_file->NumTypeIds(),
+                   sizeof(GcRoot<mirror::Class>),
+                   [=](uint32_t index) { return dex_file->PrettyType(dex::TypeIndex(index)); });
+    DumpBssEntries(
+        os,
+        "String",
+        string_bss_mapping,
+        dex_file->NumStringIds(),
+        sizeof(GcRoot<mirror::Class>),
+        [=](uint32_t index) { return dex_file->StringDataByIdx(dex::StringIndex(index)); });
+  }
+
+  void DumpBssOffsets(std::ostream& os, const char* slot_type, const IndexBssMapping* mapping) {
+    os << ".bss offset for " << slot_type << ": ";
+    if (mapping == nullptr) {
+      os << "empty.\n";
+      return;
+    }
+
+    os << "Mapping size: " << mapping->size() << "\n";
+    for (size_t i = 0; i < mapping->size(); ++i) {
+      os << "Entry[" << i << "]: index_and_mask: "
+         << mapping->At(i).index_and_mask
+         << ", bss_offset: "
+         << mapping->At(i).bss_offset << "\n";
+    }
+
+    // TODO(solanes, 154012332): We are dumping the raw values but we could make assumptions about
+    // ordering of the entries and deconstruct even the `index_and_mask`. This would allow us to use
+    // DumpBssEntries and dump more information. The size and alignment of the entry (ArtMethod*
+    // depends on instruction set but Class and String references are 32-bit) and the difference
+    // from the previous `bss_offset` (or from the "oatbss" symbol for the first item) tell us how
+    // many .bss entries a single `IndexBssMappingEntry` should describe. So we know how many most
+    // significant set bits represent the mask and the rest is the actual index. And the position of
+    // the mask bits would allow reconstructing the other indexes.
+  }
+
   const OatFile& oat_file_;
   const std::vector<const OatDexFile*> oat_dex_files_;
   const OatDumperOptions& options_;
diff --git a/runtime/entrypoints/entrypoint_utils.cc b/runtime/entrypoints/entrypoint_utils.cc
index 1c0127a..63d2aa4 100644
--- a/runtime/entrypoints/entrypoint_utils.cc
+++ b/runtime/entrypoints/entrypoint_utils.cc
@@ -36,6 +36,7 @@
 #include "mirror/object_array-inl.h"
 #include "nth_caller_visitor.h"
 #include "oat_file.h"
+#include "oat_file-inl.h"
 #include "oat_quick_method_header.h"
 #include "reflection.h"
 #include "scoped_thread_state_change-inl.h"
@@ -284,22 +285,46 @@
   return method_type;
 }
 
-void MaybeUpdateBssMethodEntry(ArtMethod* callee, MethodReference callee_reference) {
-  DCHECK(callee != nullptr);
-  if (callee_reference.dex_file->GetOatDexFile() != nullptr) {
-    size_t bss_offset = IndexBssMappingLookup::GetBssOffset(
-        callee_reference.dex_file->GetOatDexFile()->GetMethodBssMapping(),
-        callee_reference.index,
-        callee_reference.dex_file->NumMethodIds(),
-        static_cast<size_t>(kRuntimePointerSize));
+void MaybeUpdateBssMethodEntry(ArtMethod* callee,
+                               MethodReference callee_reference,
+                               ArtMethod* outer_method) {
+  DCHECK_NE(callee, nullptr);
+  if (outer_method->GetDexFile()->GetOatDexFile() == nullptr ||
+      outer_method->GetDexFile()->GetOatDexFile()->GetOatFile() == nullptr) {
+    // No OatFile to update.
+    return;
+  }
+  const OatFile* outer_oat_file = outer_method->GetDexFile()->GetOatDexFile()->GetOatFile();
+
+  const DexFile* dex_file = callee_reference.dex_file;
+  const OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
+  const IndexBssMapping* mapping = nullptr;
+  if (oat_dex_file != nullptr && oat_dex_file->GetOatFile() == outer_oat_file) {
+    // DexFiles compiled together to an oat file case.
+    mapping = oat_dex_file->GetMethodBssMapping();
+  } else {
+    // Try to find the DexFile in the BCP of the outer_method.
+    const OatFile::BssMappingInfo* mapping_info = outer_oat_file->FindBcpMappingInfo(dex_file);
+    if (mapping_info != nullptr) {
+      mapping = mapping_info->method_bss_mapping;
+    }
+  }
+
+  // Perform the update if we found a mapping.
+  if (mapping != nullptr) {
+    size_t bss_offset =
+        IndexBssMappingLookup::GetBssOffset(mapping,
+                                            callee_reference.index,
+                                            dex_file->NumMethodIds(),
+                                            static_cast<size_t>(kRuntimePointerSize));
     if (bss_offset != IndexBssMappingLookup::npos) {
       DCHECK_ALIGNED(bss_offset, static_cast<size_t>(kRuntimePointerSize));
-      const OatFile* oat_file = callee_reference.dex_file->GetOatDexFile()->GetOatFile();
-      ArtMethod** method_entry = reinterpret_cast<ArtMethod**>(const_cast<uint8_t*>(
-          oat_file->BssBegin() + bss_offset));
-      DCHECK_GE(method_entry, oat_file->GetBssMethods().data());
+      DCHECK_NE(outer_oat_file, nullptr);
+      ArtMethod** method_entry = reinterpret_cast<ArtMethod**>(
+          const_cast<uint8_t*>(outer_oat_file->BssBegin() + bss_offset));
+      DCHECK_GE(method_entry, outer_oat_file->GetBssMethods().data());
       DCHECK_LT(method_entry,
-                oat_file->GetBssMethods().data() + oat_file->GetBssMethods().size());
+                outer_oat_file->GetBssMethods().data() + outer_oat_file->GetBssMethods().size());
       std::atomic<ArtMethod*>* atomic_entry =
           reinterpret_cast<std::atomic<ArtMethod*>*>(method_entry);
       if (kIsDebugBuild) {
diff --git a/runtime/entrypoints/entrypoint_utils.h b/runtime/entrypoints/entrypoint_utils.h
index 5faf387..8b6fc69 100644
--- a/runtime/entrypoints/entrypoint_utils.h
+++ b/runtime/entrypoints/entrypoint_utils.h
@@ -213,9 +213,14 @@
 ObjPtr<mirror::Object> GetGenericJniSynchronizationObject(Thread* self, ArtMethod* called)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-// Update .bss method entrypoint if the `callee_reference` has an associated oat file
-// and that oat file has a .bss entry for the `callee_reference`.
-void MaybeUpdateBssMethodEntry(ArtMethod* callee, MethodReference callee_reference);
+// Update .bss method entrypoint if the `outer_method` has a valid OatFile, and either
+//   A) the `callee_reference` has the same OatFile as `outer_method`, or
+//   B) the `callee_reference` comes from a BCP DexFile that was present during `outer_method`'s
+//      OatFile compilation.
+// In both cases, we require that the oat file has a .bss entry for the `callee_reference`.
+void MaybeUpdateBssMethodEntry(ArtMethod* callee,
+                               MethodReference callee_reference,
+                               ArtMethod* outer_method) REQUIRES_SHARED(Locks::mutator_lock_);
 
 }  // namespace art
 
diff --git a/runtime/entrypoints/jni/jni_entrypoints.cc b/runtime/entrypoints/jni/jni_entrypoints.cc
index ddc6839..690f16a 100644
--- a/runtime/entrypoints/jni/jni_entrypoints.cc
+++ b/runtime/entrypoints/jni/jni_entrypoints.cc
@@ -69,7 +69,9 @@
     // Note that the BSS also contains entries used for super calls. Given we
     // only deal with invokestatic in this code path, we don't need to adjust
     // the method index.
-    MaybeUpdateBssMethodEntry(target_method, MethodReference(method->GetDexFile(), method_idx));
+    MaybeUpdateBssMethodEntry(target_method,
+                              MethodReference(method->GetDexFile(), method_idx),
+                              GetCalleeSaveOuterMethod(self, CalleeSaveType::kSaveRefsAndArgs));
 
     // These calls do not have an explicit class initialization check, so do the check now.
     // (When going through the stub or GenericJNI, the check was already done.)
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index 8061315..60a5875 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -28,11 +28,12 @@
 #include "mirror/object-inl.h"
 #include "mirror/object_array-inl.h"
 #include "oat_file.h"
+#include "oat_file-inl.h"
 #include "runtime.h"
 
 namespace art {
 
-static void StoreObjectInBss(ArtMethod* caller,
+static void StoreObjectInBss(ArtMethod* outer_method,
                              const OatFile* oat_file,
                              size_t bss_offset,
                              ObjPtr<mirror::Object> object) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -41,6 +42,7 @@
   static_assert(sizeof(GcRoot<mirror::String>) == sizeof(GcRoot<mirror::Object>), "Size check.");
   DCHECK_NE(bss_offset, IndexBssMappingLookup::npos);
   DCHECK_ALIGNED(bss_offset, sizeof(GcRoot<mirror::Object>));
+  DCHECK_NE(oat_file, nullptr);
   if (UNLIKELY(!oat_file->IsExecutable())) {
     // There are situations where we execute bytecode tied to an oat file opened
     // as non-executable (i.e. the AOT-compiled code cannot be executed) and we
@@ -58,13 +60,13 @@
     static_assert(sizeof(*slot) == sizeof(*atomic_slot), "Size check");
     atomic_slot->store(GcRoot<mirror::Object>(object), std::memory_order_release);
     // We need a write barrier for the class loader that holds the GC roots in the .bss.
-    ObjPtr<mirror::ClassLoader> class_loader = caller->GetClassLoader();
+    ObjPtr<mirror::ClassLoader> class_loader = outer_method->GetClassLoader();
     Runtime* runtime = Runtime::Current();
     if (kIsDebugBuild) {
       ClassTable* class_table = runtime->GetClassLinker()->ClassTableForClassLoader(class_loader);
       CHECK(class_table != nullptr && !class_table->InsertOatFile(oat_file))
           << "Oat file with .bss GC roots was not registered in class table: "
-          << oat_file->GetLocation();
+          << oat_file->GetLocation() << ", " << outer_method->PrettyMethod();
     }
     if (class_loader != nullptr) {
       WriteBarrier::ForEveryFieldWrite(class_loader);
@@ -79,71 +81,91 @@
 
 static inline void StoreTypeInBss(ArtMethod* caller,
                                   dex::TypeIndex type_idx,
-                                  ObjPtr<mirror::Class> resolved_type)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
+                                  ObjPtr<mirror::Class> resolved_type,
+                                  ArtMethod* outer_method) REQUIRES_SHARED(Locks::mutator_lock_) {
   const DexFile* dex_file = caller->GetDexFile();
-  DCHECK(dex_file != nullptr);
+  DCHECK_NE(dex_file, nullptr);
+
+  if (outer_method->GetDexFile()->GetOatDexFile() == nullptr ||
+      outer_method->GetDexFile()->GetOatDexFile()->GetOatFile() == nullptr) {
+    // No OatFile to update.
+    return;
+  }
+  const OatFile* outer_oat_file = outer_method->GetDexFile()->GetOatDexFile()->GetOatFile();
+
+  // DexFiles compiled together to an oat file case.
   const OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
-  if (oat_dex_file != nullptr) {
-    auto store = [=](const IndexBssMapping* mapping) REQUIRES_SHARED(Locks::mutator_lock_) {
-      size_t bss_offset = IndexBssMappingLookup::GetBssOffset(mapping,
-                                                              type_idx.index_,
-                                                              dex_file->NumTypeIds(),
-                                                              sizeof(GcRoot<mirror::Class>));
+  const IndexBssMapping* type_mapping = nullptr;
+  const IndexBssMapping* public_type_mapping = nullptr;
+  const IndexBssMapping* package_type_mapping = nullptr;
+  if (oat_dex_file != nullptr && oat_dex_file->GetOatFile() == outer_oat_file) {
+    type_mapping = oat_dex_file->GetTypeBssMapping();
+    public_type_mapping = oat_dex_file->GetPublicTypeBssMapping();
+    package_type_mapping = oat_dex_file->GetPackageTypeBssMapping();
+  } else {
+    // Try to find the DexFile in the BCP of the outer_method.
+    const OatFile::BssMappingInfo* mapping_info = outer_oat_file->FindBcpMappingInfo(dex_file);
+    if (mapping_info != nullptr) {
+      type_mapping = mapping_info->type_bss_mapping;
+      public_type_mapping = mapping_info->public_type_bss_mapping;
+      package_type_mapping = mapping_info->package_type_bss_mapping;
+    }
+  }
+
+  // Perform the update if we found a mapping.
+  auto store = [=](const IndexBssMapping* mapping) REQUIRES_SHARED(Locks::mutator_lock_) {
+    if (mapping != nullptr) {
+      size_t bss_offset = IndexBssMappingLookup::GetBssOffset(
+          mapping, type_idx.index_, dex_file->NumTypeIds(), sizeof(GcRoot<mirror::Class>));
       if (bss_offset != IndexBssMappingLookup::npos) {
-        StoreObjectInBss(caller, oat_dex_file->GetOatFile(), bss_offset, resolved_type);
+        StoreObjectInBss(outer_method, outer_oat_file, bss_offset, resolved_type);
       }
-    };
-    store(oat_dex_file->GetTypeBssMapping());
-    if (resolved_type->IsPublic()) {
-      store(oat_dex_file->GetPublicTypeBssMapping());
     }
-    if (resolved_type->IsPublic() ||
-        resolved_type->GetClassLoader() == caller->GetClassLoader()) {
-      store(oat_dex_file->GetPackageTypeBssMapping());
-    }
+  };
+  store(type_mapping);
+  if (resolved_type->IsPublic()) {
+    store(public_type_mapping);
+  }
+  if (resolved_type->IsPublic() || resolved_type->GetClassLoader() == caller->GetClassLoader()) {
+    store(package_type_mapping);
   }
 }
 
 static inline void StoreStringInBss(ArtMethod* caller,
                                     dex::StringIndex string_idx,
-                                    ObjPtr<mirror::String> resolved_string)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
+                                    ObjPtr<mirror::String> resolved_string,
+                                    ArtMethod* outer_method) REQUIRES_SHARED(Locks::mutator_lock_) {
   const DexFile* dex_file = caller->GetDexFile();
-  DCHECK(dex_file != nullptr);
+  DCHECK_NE(dex_file, nullptr);
+
+  if (outer_method->GetDexFile()->GetOatDexFile() == nullptr ||
+      outer_method->GetDexFile()->GetOatDexFile()->GetOatFile() == nullptr) {
+    // No OatFile to update.
+    return;
+  }
+  const OatFile* outer_oat_file = outer_method->GetDexFile()->GetOatDexFile()->GetOatFile();
+
   const OatDexFile* oat_dex_file = dex_file->GetOatDexFile();
-  if (oat_dex_file != nullptr) {
-    size_t bss_offset = IndexBssMappingLookup::GetBssOffset(oat_dex_file->GetStringBssMapping(),
-                                                            string_idx.index_,
-                                                            dex_file->NumStringIds(),
-                                                            sizeof(GcRoot<mirror::Class>));
-    if (bss_offset != IndexBssMappingLookup::npos) {
-      StoreObjectInBss(caller, oat_dex_file->GetOatFile(), bss_offset, resolved_string);
+  const IndexBssMapping* mapping = nullptr;
+  if (oat_dex_file != nullptr && oat_dex_file->GetOatFile() == outer_oat_file) {
+    // DexFiles compiled together to an oat file case.
+    mapping = oat_dex_file->GetStringBssMapping();
+  } else {
+    // Try to find the DexFile in the BCP of the outer_method.
+    const OatFile::BssMappingInfo* mapping_info = outer_oat_file->FindBcpMappingInfo(dex_file);
+    if (mapping_info != nullptr) {
+      mapping = mapping_info->string_bss_mapping;
     }
   }
-}
 
-static ALWAYS_INLINE bool CanReferenceBss(ArtMethod* outer_method, ArtMethod* caller)
-    REQUIRES_SHARED(Locks::mutator_lock_) {
-  // .bss references are used only for AOT-compiled code. As we do not want to check if the call is
-  // coming from AOT-compiled code (that could be expensive), we can simply check if the caller has
-  // the same dex file.
-  //
-  // When we are JIT compiling, if the caller and outer method have the same dex file we may or may
-  // not find a .bss slot to update; if we do, this can still benefit AOT-compiled code executed
-  // later.
-  const DexFile* outer_dex_file = outer_method->GetDexFile();
-  const DexFile* caller_dex_file = caller->GetDexFile();
-  if (outer_dex_file == caller_dex_file) {
-    return true;
+  // Perform the update if we found a mapping.
+  if (mapping != nullptr) {
+    size_t bss_offset = IndexBssMappingLookup::GetBssOffset(
+        mapping, string_idx.index_, dex_file->NumStringIds(), sizeof(GcRoot<mirror::Class>));
+    if (bss_offset != IndexBssMappingLookup::npos) {
+      StoreObjectInBss(outer_method, outer_oat_file, bss_offset, resolved_string);
+    }
   }
-
-  // We allow AOT-compiled code to reference .bss slots for all dex files compiled together to an
-  // oat file.
-  return caller_dex_file->GetOatDexFile() != nullptr &&
-         outer_dex_file->GetOatDexFile() != nullptr &&
-         caller_dex_file->GetOatDexFile()->GetOatFile() ==
-             outer_dex_file->GetOatDexFile()->GetOatFile();
 }
 
 extern "C" mirror::Class* artInitializeStaticStorageFromCode(mirror::Class* klass, Thread* self)
@@ -176,8 +198,9 @@
                                                         self,
                                                         /* can_run_clinit= */ false,
                                                         /* verify_access= */ false);
-  if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) {
-    StoreTypeInBss(caller, dex::TypeIndex(type_idx), result);
+  ArtMethod* outer_method = caller_and_outer.outer_method;
+  if (LIKELY(result != nullptr)) {
+    StoreTypeInBss(caller, dex::TypeIndex(type_idx), result, outer_method);
   }
   return result.Ptr();
 }
@@ -194,8 +217,9 @@
                                                         self,
                                                         /* can_run_clinit= */ false,
                                                         /* verify_access= */ true);
-  if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) {
-    StoreTypeInBss(caller, dex::TypeIndex(type_idx), result);
+  ArtMethod* outer_method = caller_and_outer.outer_method;
+  if (LIKELY(result != nullptr)) {
+    StoreTypeInBss(caller, dex::TypeIndex(type_idx), result, outer_method);
   }
   return result.Ptr();
 }
@@ -229,8 +253,9 @@
   ArtMethod* caller = caller_and_outer.caller;
   ObjPtr<mirror::String> result =
       Runtime::Current()->GetClassLinker()->ResolveString(dex::StringIndex(string_idx), caller);
-  if (LIKELY(result != nullptr) && CanReferenceBss(caller_and_outer.outer_method, caller)) {
-    StoreStringInBss(caller, dex::StringIndex(string_idx), result);
+  ArtMethod* outer_method = caller_and_outer.outer_method;
+  if (LIKELY(result != nullptr)) {
+    StoreStringInBss(caller, dex::StringIndex(string_idx), result, outer_method);
   }
   return result.Ptr();
 }
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index 0b4398d..e54126f 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -1375,7 +1375,8 @@
           DCHECK_NE(called_method.index, dex::kDexNoIndex);
         }
       }
-      MaybeUpdateBssMethodEntry(called, called_method);
+      ArtMethod* outer_method = QuickArgumentVisitor::GetOuterMethod(sp);
+      MaybeUpdateBssMethodEntry(called, called_method, outer_method);
     }
 
     // Static invokes need class initialization check but instance invokes can proceed even if
@@ -2405,7 +2406,9 @@
       CHECK(self->IsExceptionPending());
       return GetTwoWordFailureValue();  // Failure.
     }
-    MaybeUpdateBssMethodEntry(interface_method, MethodReference(&dex_file, dex_method_idx));
+    ArtMethod* outer_method = QuickArgumentVisitor::GetOuterMethod(sp);
+    MaybeUpdateBssMethodEntry(
+        interface_method, MethodReference(&dex_file, dex_method_idx), outer_method);
 
     // Refresh `raw_this_object` which may have changed after resolution.
     raw_this_object = this_object.Get();
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 3698ddf..64ed14a 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -71,6 +71,7 @@
       instruction_set_features_bitmap_(instruction_set_features->AsBitmap()),
       dex_file_count_(dex_file_count),
       oat_dex_files_offset_(0),
+      bcp_bss_info_offset_(0),
       executable_offset_(0),
       jni_dlsym_lookup_trampoline_offset_(0),
       jni_dlsym_lookup_critical_trampoline_offset_(0),
@@ -183,6 +184,22 @@
   oat_dex_files_offset_ = oat_dex_files_offset;
 }
 
+uint32_t OatHeader::GetBcpBssInfoOffset() const {
+  DCHECK(IsValid());
+  DCHECK(bcp_bss_info_offset_ == 0u || bcp_bss_info_offset_ > sizeof(OatHeader))
+      << "bcp_bss_info_offset_: " << bcp_bss_info_offset_
+      << "sizeof(OatHeader): " << sizeof(OatHeader);
+  return bcp_bss_info_offset_;
+}
+
+void OatHeader::SetBcpBssInfoOffset(uint32_t bcp_info_offset) {
+  DCHECK_GT(bcp_info_offset, sizeof(OatHeader));
+  DCHECK(IsValid());
+  DCHECK_EQ(bcp_bss_info_offset_, 0u);
+
+  bcp_bss_info_offset_ = bcp_info_offset;
+}
+
 uint32_t OatHeader::GetExecutableOffset() const {
   DCHECK(IsValid());
   DCHECK_ALIGNED(executable_offset_, kPageSize);
diff --git a/runtime/oat.h b/runtime/oat.h
index f6a04d8..462d41c 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,8 +32,8 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr std::array<uint8_t, 4> kOatMagic { { 'o', 'a', 't', '\n' } };
-  // Last oat version changed reason: Revert "bss support for inlining BCP into non-BCP".
-  static constexpr std::array<uint8_t, 4> kOatVersion { { '2', '2', '3', '\0' } };
+  // Last oat version changed reason: Revert^4 "bss support for inlining BCP into non-BCP".
+  static constexpr std::array<uint8_t, 4> kOatVersion { { '2', '2', '5', '\0' } };
 
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDebuggableKey = "debuggable";
@@ -68,6 +68,8 @@
   }
   uint32_t GetOatDexFilesOffset() const;
   void SetOatDexFilesOffset(uint32_t oat_dex_files_offset);
+  uint32_t GetBcpBssInfoOffset() const;
+  void SetBcpBssInfoOffset(uint32_t bcp_info_offset);
   uint32_t GetExecutableOffset() const;
   void SetExecutableOffset(uint32_t executable_offset);
 
@@ -130,6 +132,7 @@
   uint32_t instruction_set_features_bitmap_;
   uint32_t dex_file_count_;
   uint32_t oat_dex_files_offset_;
+  uint32_t bcp_bss_info_offset_;
   uint32_t executable_offset_;
   uint32_t jni_dlsym_lookup_trampoline_offset_;
   uint32_t jni_dlsym_lookup_critical_trampoline_offset_;
diff --git a/runtime/oat_file-inl.h b/runtime/oat_file-inl.h
index 772566f..fd42e45 100644
--- a/runtime/oat_file-inl.h
+++ b/runtime/oat_file-inl.h
@@ -21,6 +21,7 @@
 
 #include "base/utils.h"
 #include "oat_quick_method_header.h"
+#include "runtime-inl.h"
 
 namespace art {
 
@@ -97,6 +98,20 @@
   return reinterpret_cast<const void *>(begin_ + code_offset_);
 }
 
+inline const OatFile::BssMappingInfo* OatFile::FindBcpMappingInfo(const DexFile* dex_file) const {
+  ArrayRef<const OatFile::BssMappingInfo> mapping_info_vector(GetBcpBssInfo());
+  ArrayRef<const DexFile* const> bcp_dexfiles(
+      Runtime::Current()->GetClassLinker()->GetBootClassPath());
+  // Create a sub array to limit search range.
+  bcp_dexfiles = bcp_dexfiles.SubArray(/*pos=*/ 0u, mapping_info_vector.size());
+  auto it = std::find(bcp_dexfiles.begin(), bcp_dexfiles.end(), dex_file);
+  if (it != bcp_dexfiles.end()) {
+    return &mapping_info_vector[std::distance(bcp_dexfiles.begin(), it)];
+  } else {
+    return nullptr;
+  }
+}
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_OAT_FILE_INL_H_
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index afa3d4d..7809095 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1002,6 +1002,85 @@
       oat_dex_files_.Put(canonical_key, oat_dex_file);
     }
   }
+
+  size_t bcp_info_offset = GetOatHeader().GetBcpBssInfoOffset();
+  // `bcp_info_offset` will be 0 for multi-image, or for the case of no mappings.
+  if (bcp_info_offset != 0) {
+    // Consistency check.
+    if (bcp_info_offset < GetOatHeader().GetHeaderSize() || bcp_info_offset > Size()) {
+      *error_msg = StringPrintf(
+          "In oat file '%s' found invalid bcp info offset: "
+          "%zu is not in [%zu, %zu]",
+          GetLocation().c_str(),
+          bcp_info_offset,
+          GetOatHeader().GetHeaderSize(),
+          Size());
+      return false;
+    }
+    const uint8_t* bcp_info_begin = Begin() + bcp_info_offset;  // Jump to the BCP_info records.
+
+    uint32_t number_of_bcp_dexfiles;
+    if (UNLIKELY(!ReadOatDexFileData(*this, &bcp_info_begin, &number_of_bcp_dexfiles))) {
+      *error_msg = StringPrintf("Failed to read the number of BCP dex files");
+      return false;
+    }
+    Runtime* const runtime = Runtime::Current();
+    ClassLinker* const linker = runtime != nullptr ? runtime->GetClassLinker() : nullptr;
+    if (linker != nullptr && UNLIKELY(number_of_bcp_dexfiles > linker->GetBootClassPath().size())) {
+      // If we compiled with more DexFiles than what we have at runtime, we expect to discard this
+      // OatFile after verifying its checksum in OatFileAssistant. Therefore, we set
+      // `number_of_bcp_dexfiles` to 0 to avoid reading data that will ultimately be discarded.
+      number_of_bcp_dexfiles = 0;
+    }
+
+    DCHECK(bcp_bss_info_.empty());
+    bcp_bss_info_.resize(number_of_bcp_dexfiles);
+    // At runtime, there might be more DexFiles added to the BCP that we didn't compile with.
+    // We only care about the ones in [0..number_of_bcp_dexfiles).
+    for (size_t i = 0, size = number_of_bcp_dexfiles; i != size; ++i) {
+      const std::string& dex_file_location = linker != nullptr ?
+                                                 linker->GetBootClassPath()[i]->GetLocation() :
+                                                 "No runtime/linker therefore no DexFile location";
+      if (!ReadIndexBssMapping(this,
+                               &bcp_info_begin,
+                               i,
+                               dex_file_location,
+                               "method",
+                               &bcp_bss_info_[i].method_bss_mapping,
+                               error_msg) ||
+          !ReadIndexBssMapping(this,
+                               &bcp_info_begin,
+                               i,
+                               dex_file_location,
+                               "type",
+                               &bcp_bss_info_[i].type_bss_mapping,
+                               error_msg) ||
+          !ReadIndexBssMapping(this,
+                               &bcp_info_begin,
+                               i,
+                               dex_file_location,
+                               "type",
+                               &bcp_bss_info_[i].public_type_bss_mapping,
+                               error_msg) ||
+          !ReadIndexBssMapping(this,
+                               &bcp_info_begin,
+                               i,
+                               dex_file_location,
+                               "type",
+                               &bcp_bss_info_[i].package_type_bss_mapping,
+                               error_msg) ||
+          !ReadIndexBssMapping(this,
+                               &bcp_info_begin,
+                               i,
+                               dex_file_location,
+                               "string",
+                               &bcp_bss_info_[i].string_bss_mapping,
+                               error_msg)) {
+        return false;
+      }
+    }
+  }
+
   if (!dex_filenames.empty() && dex_filenames_pos != dex_filenames.size()) {
     *error_msg = StringPrintf("Oat file '%s' contains only %zu primary dex locations, expected %zu",
                               GetLocation().c_str(),
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 53a267f..c1b1acb 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -379,6 +379,21 @@
   // Returns whether an image (e.g. app image) is required to safely execute this OAT file.
   bool RequiresImage() const;
 
+  struct BssMappingInfo {
+    const IndexBssMapping* method_bss_mapping = nullptr;
+    const IndexBssMapping* type_bss_mapping = nullptr;
+    const IndexBssMapping* public_type_bss_mapping = nullptr;
+    const IndexBssMapping* package_type_bss_mapping = nullptr;
+    const IndexBssMapping* string_bss_mapping = nullptr;
+  };
+
+  ArrayRef<const BssMappingInfo> GetBcpBssInfo() const {
+    return ArrayRef<const BssMappingInfo>(bcp_bss_info_);
+  }
+
+  // Returns the mapping info of `dex_file` if found in the BcpBssInfo, or nullptr otherwise.
+  const BssMappingInfo* FindBcpMappingInfo(const DexFile* dex_file) const;
+
  protected:
   OatFile(const std::string& filename, bool executable);
 
@@ -427,6 +442,9 @@
   // Owning storage for the OatDexFile objects.
   std::vector<const OatDexFile*> oat_dex_files_storage_;
 
+  // Mapping info for DexFiles in the BCP.
+  std::vector<BssMappingInfo> bcp_bss_info_;
+
   // NOTE: We use a std::string_view as the key type to avoid a memory allocation on every
   // lookup with a const char* key. The std::string_view doesn't own its backing storage,
   // therefore we're using the OatFile's stored dex location as the backing storage