runtime: Properly unload partially loaded image spaces

When one of the (non-app) image space successfully loads,
it sets up runtime callee-save methods.

If it is later unloaded, callee-save methods are now pointing to memory
that is no longer valid (viewed as all 0s in the debugger).

Runtime::Init skips creating its own runtime methods if it already sees
that the runtime methods were set to non-null, thus dangling runtime
methods.

This crash would nominally manifest itself in unwinding the first time, or as a DCHECK
failure in the interpreter bridge invocation during aborting if debugging was enabled.

To get into this state:
* Fill up the /data partition (but perhaps leave a little bit of room
for one image, but not all images)
* Reboot the device or run zygote manually.

Test: adb shell dd if=/dev/zero of=/data/local/tmp/tempFiller.deleteMe bs=1024 count=50g ; adb reboot
Bug: 36033084
Change-Id: I728c1058b003fcf5e98dc2746d53e44b688c4605
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index e9f0758..0f51b87 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -1695,6 +1695,29 @@
   return true;
 }
 
+ImageSpace::~ImageSpace() {
+  Runtime* runtime = Runtime::Current();
+  if (runtime == nullptr) {
+    return;
+  }
+
+  if (GetImageHeader().IsAppImage()) {
+    // This image space did not modify resolution method then in Init.
+    return;
+  }
+
+  if (!runtime->HasResolutionMethod()) {
+    // Another image space has already unloaded the below methods.
+    return;
+  }
+
+  runtime->ClearInstructionSet();
+  runtime->ClearResolutionMethod();
+  runtime->ClearImtConflictMethod();
+  runtime->ClearImtUnimplementedMethod();
+  runtime->ClearCalleeSaveMethods();
+}
+
 std::unique_ptr<ImageSpace> ImageSpace::CreateFromAppImage(const char* image,
                                                            const OatFile* oat_file,
                                                            std::string* error_msg) {
diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h
index 199bbdd..aa3dd42 100644
--- a/runtime/gc/space/image_space.h
+++ b/runtime/gc/space/image_space.h
@@ -159,6 +159,9 @@
 
   void DumpSections(std::ostream& os) const;
 
+  // De-initialize the image-space by undoing the effects in Init().
+  virtual ~ImageSpace();
+
  protected:
   // Tries to initialize an ImageSpace from the given image path, returning null on error.
   //
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index e61ec23..eb068b3 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1963,12 +1963,23 @@
   }
 }
 
+void Runtime::ClearInstructionSet() {
+  instruction_set_ = InstructionSet::kNone;
+}
+
 void Runtime::SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type) {
   DCHECK_LT(static_cast<int>(type), static_cast<int>(kLastCalleeSaveType));
   CHECK(method != nullptr);
   callee_save_methods_[type] = reinterpret_cast<uintptr_t>(method);
 }
 
+void Runtime::ClearCalleeSaveMethods() {
+  for (size_t i = 0; i < static_cast<size_t>(kLastCalleeSaveType); ++i) {
+    CalleeSaveType type = static_cast<CalleeSaveType>(i);
+    callee_save_methods_[type] = reinterpret_cast<uintptr_t>(nullptr);
+  }
+}
+
 void Runtime::RegisterAppInfo(const std::vector<std::string>& code_paths,
                               const std::string& profile_output_filename) {
   if (jit_.get() == nullptr) {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index df13b70..3ba0f2c 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -356,6 +356,9 @@
   }
 
   void SetResolutionMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
+  void ClearResolutionMethod() {
+    resolution_method_ = nullptr;
+  }
 
   ArtMethod* CreateResolutionMethod() REQUIRES_SHARED(Locks::mutator_lock_);
 
@@ -367,6 +370,10 @@
     return imt_conflict_method_ != nullptr;
   }
 
+  void ClearImtConflictMethod() {
+    imt_conflict_method_ = nullptr;
+  }
+
   void FixupConflictTables();
   void SetImtConflictMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
   void SetImtUnimplementedMethod(ArtMethod* method) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -374,6 +381,10 @@
   ArtMethod* CreateImtConflictMethod(LinearAlloc* linear_alloc)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
+  void ClearImtUnimplementedMethod() {
+    imt_unimplemented_method_ = nullptr;
+  }
+
   // Returns a special method that describes all callee saves being spilled to the stack.
   enum CalleeSaveType {
     kSaveAllCalleeSaves,  // All callee-save registers.
@@ -409,8 +420,10 @@
   }
 
   void SetInstructionSet(InstructionSet instruction_set);
+  void ClearInstructionSet();
 
   void SetCalleeSaveMethod(ArtMethod* method, CalleeSaveType type);
+  void ClearCalleeSaveMethods();
 
   ArtMethod* CreateCalleeSaveMethod() REQUIRES_SHARED(Locks::mutator_lock_);