Use pre-allocated Throwables from the boot image.

The pre-allocated OOMEs and NoClassDefFoundError were stored
in the boot image but they were not used, we instead used to
allocate and use new objects. This change adds references to
the image roots, so that these Throwables can be used when
starting the runtime using the boot image.

Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Bug: 77947463
Change-Id: I2079344dee61242bf0bef5c32770c33ac8a6b7a4
diff --git a/dex2oat/linker/image_writer.cc b/dex2oat/linker/image_writer.cc
index 028de34..dc07090 100644
--- a/dex2oat/linker/image_writer.cc
+++ b/dex2oat/linker/image_writer.cc
@@ -1342,6 +1342,14 @@
       ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size)));
   image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
   image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots());
+  image_roots->Set<false>(ImageHeader::kOomeWhenThrowingException,
+                          runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingException());
+  image_roots->Set<false>(ImageHeader::kOomeWhenThrowingOome,
+                          runtime->GetPreAllocatedOutOfMemoryErrorWhenThrowingOOME());
+  image_roots->Set<false>(ImageHeader::kOomeWhenHandlingStackOverflow,
+                          runtime->GetPreAllocatedOutOfMemoryErrorWhenHandlingStackOverflow());
+  image_roots->Set<false>(ImageHeader::kNoClassDefFoundError,
+                          runtime->GetPreAllocatedNoClassDefFoundError());
   // image_roots[ImageHeader::kClassLoader] will be set later for app image.
   static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
                 "Class loader should be the last image root.");
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 7ac9e98..7b72e18 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -105,6 +105,10 @@
 const char* image_roots_descriptions_[] = {
   "kDexCaches",
   "kClassRoots",
+  "kOomeWhenThrowingException",
+  "kOomeWhenThrowingOome",
+  "kOomeWhenHandlingStackOverflow",
+  "kNoClassDefFoundError",
   "kClassLoader",
 };
 
@@ -1942,17 +1946,17 @@
     os << "COMPILE PIC: " << (image_header_.CompilePic() ? "yes" : "no") << "\n\n";
 
     {
-      os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
+      os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots().Ptr()) << "\n";
       static_assert(arraysize(image_roots_descriptions_) ==
           static_cast<size_t>(ImageHeader::kImageRootsMax), "sizes must match");
       DCHECK_LE(image_header_.GetImageRoots()->GetLength(), ImageHeader::kImageRootsMax);
       for (int32_t i = 0, size = image_header_.GetImageRoots()->GetLength(); i != size; ++i) {
         ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
         const char* image_root_description = image_roots_descriptions_[i];
-        mirror::Object* image_root_object = image_header_.GetImageRoot(image_root);
-        indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
+        ObjPtr<mirror::Object> image_root_object = image_header_.GetImageRoot(image_root);
+        indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object.Ptr());
         if (image_root_object != nullptr && image_root_object->IsObjectArray()) {
-          mirror::ObjectArray<mirror::Object>* image_root_object_array
+          ObjPtr<mirror::ObjectArray<mirror::Object>> image_root_object_array
               = image_root_object->AsObjectArray<mirror::Object>();
           ScopedIndentation indent2(&vios_);
           for (int j = 0; j < image_root_object_array->GetLength(); j++) {
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index a6d3903..3c0b3e4 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -973,7 +973,7 @@
   ImageHeader* image_header = reinterpret_cast<ImageHeader*>(image_->Begin());
   CHECK_GT(image_->Size(), sizeof(ImageHeader));
   // These are the roots from the original file.
-  auto* img_roots = image_header->GetImageRoots();
+  mirror::ObjectArray<mirror::Object>* img_roots = image_header->GetImageRoots().Ptr();
   image_header->RelocateImage(delta_);
 
   PatchArtFields(image_header);
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 941c7ec..d0fe394 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -197,8 +197,7 @@
     }
   } else {
     // Previous error has been stored as an instance. Just rethrow.
-    ObjPtr<mirror::Class> throwable_class =
-        self->DecodeJObject(WellKnownClasses::java_lang_Throwable)->AsClass();
+    ObjPtr<mirror::Class> throwable_class = GetClassRoot<mirror::Throwable>(class_linker);
     ObjPtr<mirror::Class> error_class = obj->GetClass();
     CHECK(throwable_class->IsAssignableFrom(error_class));
     self->SetException(obj->AsThrowable());
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index dbe09e8..e754fbc 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1287,7 +1287,7 @@
       bitmap->VisitMarkedRange(objects_begin, objects_end, fixup_object_visitor);
       // Fixup image roots.
       CHECK(app_image.InSource(reinterpret_cast<uintptr_t>(
-          image_header.GetImageRoots<kWithoutReadBarrier>())));
+          image_header.GetImageRoots<kWithoutReadBarrier>().Ptr())));
       image_header.RelocateImageObjects(app_image.Delta());
       CHECK_EQ(image_header.GetImageBegin(), target_base);
       // Fix up dex cache DexFile pointers.
diff --git a/runtime/image-inl.h b/runtime/image-inl.h
index 3a66a34..c527f6f 100644
--- a/runtime/image-inl.h
+++ b/runtime/image-inl.h
@@ -23,18 +23,19 @@
 #include "imt_conflict_table.h"
 #include "imtable.h"
 #include "mirror/object_array-inl.h"
+#include "obj_ptr-inl.h"
 #include "read_barrier-inl.h"
 
 namespace art {
 
 template <ReadBarrierOption kReadBarrierOption>
-inline mirror::Object* ImageHeader::GetImageRoot(ImageRoot image_root) const {
-  mirror::ObjectArray<mirror::Object>* image_roots = GetImageRoots<kReadBarrierOption>();
+inline ObjPtr<mirror::Object> ImageHeader::GetImageRoot(ImageRoot image_root) const {
+  ObjPtr<mirror::ObjectArray<mirror::Object>> image_roots = GetImageRoots<kReadBarrierOption>();
   return image_roots->Get<kVerifyNone, kReadBarrierOption>(static_cast<int32_t>(image_root));
 }
 
 template <ReadBarrierOption kReadBarrierOption>
-inline mirror::ObjectArray<mirror::Object>* ImageHeader::GetImageRoots() const {
+inline ObjPtr<mirror::ObjectArray<mirror::Object>> ImageHeader::GetImageRoots() const {
   // Need a read barrier as it's not visited during root scan.
   // Pass in the address of the local variable to the read barrier
   // rather than image_roots_ because it won't move (asserted below)
diff --git a/runtime/image.cc b/runtime/image.cc
index 7ad2e7b..17fc664 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -26,7 +26,7 @@
 namespace art {
 
 const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '0', '\0' };  // ClassRoot::MethodHandle.
+const uint8_t ImageHeader::kImageVersion[] = { '0', '6', '1', '\0' };  // Pre-allocated Throwables.
 
 ImageHeader::ImageHeader(uint32_t image_begin,
                          uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index 8acd5bc..c6fc052 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -27,6 +27,7 @@
 
 class ArtField;
 class ArtMethod;
+template <class MirrorType> class ObjPtr;
 
 namespace linker {
 class ImageWriter;
@@ -206,7 +207,11 @@
   enum ImageRoot {
     kDexCaches,
     kClassRoots,
-    kClassLoader,  // App image only.
+    kOomeWhenThrowingException,       // Pre-allocated OOME when throwing exception.
+    kOomeWhenThrowingOome,            // Pre-allocated OOME when throwing OOME.
+    kOomeWhenHandlingStackOverflow,   // Pre-allocated OOME when handling StackOverflowError.
+    kNoClassDefFoundError,            // Pre-allocated NoClassDefFoundError.
+    kClassLoader,                     // App image only.
     kImageRootsMax,
   };
 
@@ -277,11 +282,11 @@
   }
 
   template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-  mirror::Object* GetImageRoot(ImageRoot image_root) const
+  ObjPtr<mirror::Object> GetImageRoot(ImageRoot image_root) const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier>
-  mirror::ObjectArray<mirror::Object>* GetImageRoots() const
+  ObjPtr<mirror::ObjectArray<mirror::Object>> GetImageRoots() const
       REQUIRES_SHARED(Locks::mutator_lock_);
 
   void RelocateImage(off_t delta);
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 227ace0..44b0c2b 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -419,7 +419,7 @@
 }
 
 bool Class::IsThrowableClass() {
-  return WellKnownClasses::ToClass(WellKnownClasses::java_lang_Throwable)->IsAssignableFrom(this);
+  return GetClassRoot<mirror::Throwable>()->IsAssignableFrom(this);
 }
 
 void Class::SetClassLoader(ObjPtr<ClassLoader> new_class_loader) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 0d9d16c..1e327fc 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1090,6 +1090,17 @@
   sentinel_ = GcRoot<mirror::Object>(sentinel);
 }
 
+static inline void InitPreAllocatedException(Thread* self,
+                                             GcRoot<mirror::Throwable>* exception,
+                                             const char* exception_class_descriptor,
+                                             const char* msg)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK_EQ(self, Thread::Current());
+  self->ThrowNewException(exception_class_descriptor, msg);
+  *exception = GcRoot<mirror::Throwable>(self->GetException());
+  self->ClearException();
+}
+
 bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {
   // (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc.
   // Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc.
@@ -1505,34 +1516,54 @@
   // TODO: move this to just be an Trace::Start argument
   Trace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock));
 
-  // Pre-allocate an OutOfMemoryError for the case when we fail to
-  // allocate the exception to be thrown.
-  InitPreAllocatedException(self,
-                            &Runtime::pre_allocated_OutOfMemoryError_when_throwing_exception_,
-                            "Ljava/lang/OutOfMemoryError;",
-                            "OutOfMemoryError thrown while trying to throw an exception; "
-                            "no stack trace available");
-  // Pre-allocate an OutOfMemoryError for the double-OOME case.
-  InitPreAllocatedException(self,
-                            &Runtime::pre_allocated_OutOfMemoryError_when_throwing_oome_,
-                            "Ljava/lang/OutOfMemoryError;",
-                            "OutOfMemoryError thrown while trying to throw OutOfMemoryError; "
-                            "no stack trace available");
-  // Pre-allocate an OutOfMemoryError for the case when we fail to
-  // allocate while handling a stack overflow.
-  InitPreAllocatedException(self,
-                            &Runtime::pre_allocated_OutOfMemoryError_when_handling_stack_overflow_,
-                            "Ljava/lang/OutOfMemoryError;",
-                            "OutOfMemoryError thrown while trying to handle a stack overflow; "
-                            "no stack trace available");
+  if (GetHeap()->HasBootImageSpace()) {
+    const ImageHeader& image_header = GetHeap()->GetBootImageSpaces()[0]->GetImageHeader();
+    pre_allocated_OutOfMemoryError_when_throwing_exception_ = GcRoot<mirror::Throwable>(
+        image_header.GetImageRoot(ImageHeader::kOomeWhenThrowingException)->AsThrowable());
+    DCHECK(pre_allocated_OutOfMemoryError_when_throwing_exception_.Read()->GetClass()
+               ->DescriptorEquals("Ljava/lang/OutOfMemoryError;"));
+    pre_allocated_OutOfMemoryError_when_throwing_oome_ = GcRoot<mirror::Throwable>(
+        image_header.GetImageRoot(ImageHeader::kOomeWhenThrowingOome)->AsThrowable());
+    DCHECK(pre_allocated_OutOfMemoryError_when_throwing_oome_.Read()->GetClass()
+               ->DescriptorEquals("Ljava/lang/OutOfMemoryError;"));
+    pre_allocated_OutOfMemoryError_when_handling_stack_overflow_ = GcRoot<mirror::Throwable>(
+        image_header.GetImageRoot(ImageHeader::kOomeWhenHandlingStackOverflow)->AsThrowable());
+    DCHECK(pre_allocated_OutOfMemoryError_when_handling_stack_overflow_.Read()->GetClass()
+               ->DescriptorEquals("Ljava/lang/OutOfMemoryError;"));
+    pre_allocated_NoClassDefFoundError_ = GcRoot<mirror::Throwable>(
+        image_header.GetImageRoot(ImageHeader::kNoClassDefFoundError)->AsThrowable());
+    DCHECK(pre_allocated_NoClassDefFoundError_.Read()->GetClass()
+               ->DescriptorEquals("Ljava/lang/NoClassDefFoundError;"));
+  } else {
+    // Pre-allocate an OutOfMemoryError for the case when we fail to
+    // allocate the exception to be thrown.
+    InitPreAllocatedException(self,
+                              &pre_allocated_OutOfMemoryError_when_throwing_exception_,
+                              "Ljava/lang/OutOfMemoryError;",
+                              "OutOfMemoryError thrown while trying to throw an exception; "
+                              "no stack trace available");
+    // Pre-allocate an OutOfMemoryError for the double-OOME case.
+    InitPreAllocatedException(self,
+                              &pre_allocated_OutOfMemoryError_when_throwing_oome_,
+                              "Ljava/lang/OutOfMemoryError;",
+                              "OutOfMemoryError thrown while trying to throw OutOfMemoryError; "
+                              "no stack trace available");
+    // Pre-allocate an OutOfMemoryError for the case when we fail to
+    // allocate while handling a stack overflow.
+    InitPreAllocatedException(self,
+                              &pre_allocated_OutOfMemoryError_when_handling_stack_overflow_,
+                              "Ljava/lang/OutOfMemoryError;",
+                              "OutOfMemoryError thrown while trying to handle a stack overflow; "
+                              "no stack trace available");
 
-  // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class
-  // ahead of checking the application's class loader.
-  InitPreAllocatedException(self,
-                            &Runtime::pre_allocated_NoClassDefFoundError_,
-                            "Ljava/lang/NoClassDefFoundError;",
-                            "Class not found using the boot class loader; "
-                            "no stack trace available");
+    // Pre-allocate a NoClassDefFoundError for the common case of failing to find a system class
+    // ahead of checking the application's class loader.
+    InitPreAllocatedException(self,
+                              &pre_allocated_NoClassDefFoundError_,
+                              "Ljava/lang/NoClassDefFoundError;",
+                              "Class not found using the boot class loader; "
+                              "no stack trace available");
+  }
 
   // Runtime initialization is largely done now.
   // We load plugins first since that can modify the runtime state slightly.
@@ -1682,16 +1713,6 @@
   }
 }
 
-void Runtime::InitPreAllocatedException(Thread* self,
-                                        GcRoot<mirror::Throwable> Runtime::* exception,
-                                        const char* exception_class_descriptor,
-                                        const char* msg) {
-  DCHECK_EQ(self, Thread::Current());
-  self->ThrowNewException(exception_class_descriptor, msg);
-  this->*exception = GcRoot<mirror::Throwable>(self->GetException());
-  self->ClearException();
-}
-
 void Runtime::InitNativeMethods() {
   VLOG(startup) << "Runtime::InitNativeMethods entering";
   Thread* self = Thread::Current();
@@ -2048,9 +2069,10 @@
       auto* image_space = space->AsImageSpace();
       const auto& image_header = image_space->GetImageHeader();
       for (int32_t i = 0, size = image_header.GetImageRoots()->GetLength(); i != size; ++i) {
-        auto* obj = image_header.GetImageRoot(static_cast<ImageHeader::ImageRoot>(i));
+        mirror::Object* obj =
+            image_header.GetImageRoot(static_cast<ImageHeader::ImageRoot>(i)).Ptr();
         if (obj != nullptr) {
-          auto* after_obj = obj;
+          mirror::Object* after_obj = obj;
           visitor->VisitRoot(&after_obj, RootInfo(kRootStickyClass));
           CHECK_EQ(after_obj, obj);
         }
diff --git a/runtime/runtime.h b/runtime/runtime.h
index 10f72e7..d85490c 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -774,11 +774,6 @@
 
   bool Init(RuntimeArgumentMap&& runtime_options)
       SHARED_TRYLOCK_FUNCTION(true, Locks::mutator_lock_);
-  void InitPreAllocatedException(Thread* self,
-                                 GcRoot<mirror::Throwable> Runtime::* exception,
-                                 const char* exception_class_descriptor,
-                                 const char* msg)
-      REQUIRES_SHARED(Locks::mutator_lock_);
   void InitNativeMethods() REQUIRES(!Locks::mutator_lock_);
   void RegisterRuntimeNativeMethods(JNIEnv* env);