Create a dual view for zygote data cache.

Test: boots.
Bug: 119800099
Change-Id: I61a6e578554e4630990d94500f36a91279f8e382
diff --git a/runtime/jit/jit_memory_region.cc b/runtime/jit/jit_memory_region.cc
index bcf90ce..865a7d4 100644
--- a/runtime/jit/jit_memory_region.cc
+++ b/runtime/jit/jit_memory_region.cc
@@ -125,10 +125,14 @@
     // cache, and the executable view of the code cache has fixed RX memory protections.
     //
     // This memory needs to be mapped shared as the code portions will have two mappings.
+    //
+    // Additionally, the zyzote will create a dual view of the data portion of
+    // the cache. This mapping will be read-only, whereas the second mapping
+    // will be writable.
     base_flags = MAP_SHARED;
     data_pages = MemMap::MapFile(
         data_capacity + exec_capacity,
-        kProtRW,
+        is_zygote ? kProtR : kProtRW,
         base_flags,
         mem_fd,
         /* start= */ 0,
@@ -168,6 +172,7 @@
 
   MemMap exec_pages;
   MemMap non_exec_pages;
+  MemMap writable_data_pages;
   if (exec_capacity > 0) {
     uint8_t* const divider = data_pages.Begin() + data_capacity;
     // Set initial permission for executable view to catch any SELinux permission problems early
@@ -209,12 +214,28 @@
           return false;
         }
       }
-    }
-    if (is_zygote) {
-      // Now that we have created the writable and executable mappings, prevent creating any new
-      // ones.
-      if (!ProtectZygoteMemory(mem_fd.get(), error_msg)) {
-        return false;
+      // For the zygote, create a dual view of the data cache.
+      if (is_zygote) {
+        name = data_cache_name + "-rw";
+        writable_data_pages = MemMap::MapFile(data_capacity,
+                                              kProtRW,
+                                              base_flags,
+                                              mem_fd,
+                                              /* start= */ 0,
+                                              /* low_4GB= */ false,
+                                              name.c_str(),
+                                              &error_str);
+        if (!writable_data_pages.IsValid()) {
+          std::ostringstream oss;
+          oss << "Failed to create dual data view for zygote: " << error_str;
+          *error_msg = oss.str();
+          return false;
+        }
+        // Now that we have created the writable and executable mappings, prevent creating any new
+        // ones.
+        if (!ProtectZygoteMemory(mem_fd.get(), error_msg)) {
+          return false;
+        }
       }
     }
   } else {
@@ -224,14 +245,24 @@
   data_pages_ = std::move(data_pages);
   exec_pages_ = std::move(exec_pages);
   non_exec_pages_ = std::move(non_exec_pages);
+  writable_data_pages_ = std::move(writable_data_pages);
+
+  VLOG(jit) << "Created JitMemoryRegion"
+            << ": data_pages=" << reinterpret_cast<void*>(data_pages_.Begin())
+            << ", exec_pages=" << reinterpret_cast<void*>(exec_pages_.Begin())
+            << ", non_exec_pages=" << reinterpret_cast<void*>(non_exec_pages_.Begin())
+            << ", writable_data_pages=" << reinterpret_cast<void*>(writable_data_pages_.Begin());
 
   // Now that the pages are initialized, initialize the spaces.
 
-  // Initialize the data heap
-  data_mspace_ = create_mspace_with_base(data_pages_.Begin(), data_end_, false /*locked*/);
+  // Initialize the data heap.
+  data_mspace_ = create_mspace_with_base(
+      HasDualDataMapping() ? writable_data_pages_.Begin() : data_pages_.Begin(),
+      data_end_,
+      /* locked= */ false);
   CHECK(data_mspace_ != nullptr) << "create_mspace_with_base (data) failed";
 
-  // Initialize the code heap
+  // Initialize the code heap.
   MemMap* code_heap = nullptr;
   if (non_exec_pages_.IsValid()) {
     code_heap = &non_exec_pages_;
@@ -293,14 +324,15 @@
 // is already held.
 void* JitMemoryRegion::MoreCore(const void* mspace, intptr_t increment) NO_THREAD_SAFETY_ANALYSIS {
   if (mspace == exec_mspace_) {
-    DCHECK(exec_mspace_ != nullptr);
+    CHECK(exec_mspace_ != nullptr);
     const MemMap* const code_pages = GetUpdatableCodeMapping();
     void* result = code_pages->Begin() + exec_end_;
     exec_end_ += increment;
     return result;
   } else {
-    DCHECK_EQ(data_mspace_, mspace);
-    void* result = data_pages_.Begin() + data_end_;
+    CHECK_EQ(data_mspace_, mspace);
+    const MemMap* const writable_data_pages = GetWritableDataMapping();
+    void* result = writable_data_pages->Begin() + data_end_;
     data_end_ += increment;
     return result;
   }
@@ -412,6 +444,7 @@
                                  const std::vector<Handle<mirror::Object>>& roots,
                                  const uint8_t* stack_map,
                                  size_t stack_map_size) {
+  roots_data = GetWritableDataAddress(roots_data);
   size_t root_table_size = ComputeRootTableSize(roots.size());
   uint8_t* stack_map_data = roots_data + root_table_size;
   FillRootTable(roots_data, roots);
@@ -434,10 +467,11 @@
 uint8_t* JitMemoryRegion::AllocateData(size_t data_size) {
   void* result = mspace_malloc(data_mspace_, data_size);
   used_memory_for_data_ += mspace_usable_size(result);
-  return reinterpret_cast<uint8_t*>(result);
+  return reinterpret_cast<uint8_t*>(GetNonWritableDataAddress(result));
 }
 
 void JitMemoryRegion::FreeData(uint8_t* data) {
+  data = GetWritableDataAddress(data);
   used_memory_for_data_ -= mspace_usable_size(data);
   mspace_free(data_mspace_, data);
 }
diff --git a/runtime/jit/jit_memory_region.h b/runtime/jit/jit_memory_region.h
index af0ca83..fa74378 100644
--- a/runtime/jit/jit_memory_region.h
+++ b/runtime/jit/jit_memory_region.h
@@ -58,6 +58,8 @@
         exec_end_(0),
         used_memory_for_code_(0),
         used_memory_for_data_(0),
+        data_pages_(),
+        writable_data_pages_(),
         exec_pages_(),
         non_exec_pages_(),
         data_mspace_(nullptr),
@@ -104,6 +106,10 @@
     return non_exec_pages_.IsValid();
   }
 
+  bool HasDualDataMapping() const {
+    return writable_data_pages_.IsValid();
+  }
+
   bool HasCodeMapping() const {
     return exec_pages_.IsValid();
   }
@@ -145,9 +151,6 @@
  private:
   template <typename T>
   T* TranslateAddress(T* src_ptr, const MemMap& src, const MemMap& dst) {
-    if (!HasDualCodeMapping()) {
-      return src_ptr;
-    }
     CHECK(src.HasAddress(src_ptr)) << reinterpret_cast<const void*>(src_ptr);
     const uint8_t* const raw_src_ptr = reinterpret_cast<const uint8_t*>(src_ptr);
     return reinterpret_cast<T*>(raw_src_ptr - src.Begin() + dst.Begin());
@@ -163,11 +166,39 @@
     }
   }
 
+  const MemMap* GetWritableDataMapping() const {
+    if (HasDualDataMapping()) {
+      return &writable_data_pages_;
+    } else {
+      return &data_pages_;
+    }
+  }
+
+  template <typename T> T* GetNonWritableDataAddress(T* src_ptr) {
+    if (!HasDualDataMapping()) {
+      return src_ptr;
+    }
+    return TranslateAddress(src_ptr, writable_data_pages_, data_pages_);
+  }
+
+  template <typename T> T* GetWritableDataAddress(T* src_ptr) {
+    if (!HasDualDataMapping()) {
+      return src_ptr;
+    }
+    return TranslateAddress(src_ptr, data_pages_, writable_data_pages_);
+  }
+
   template <typename T> T* GetExecutableAddress(T* src_ptr) {
+    if (!HasDualCodeMapping()) {
+      return src_ptr;
+    }
     return TranslateAddress(src_ptr, non_exec_pages_, exec_pages_);
   }
 
   template <typename T> T* GetNonExecutableAddress(T* src_ptr) {
+    if (!HasDualCodeMapping()) {
+      return src_ptr;
+    }
     return TranslateAddress(src_ptr, exec_pages_, non_exec_pages_);
   }
 
@@ -198,6 +229,10 @@
   // Mem map which holds data (stack maps and profiling info).
   MemMap data_pages_;
 
+  // Mem map which holds data with writable permission. Only valid for dual view
+  // JIT when this is the writable view and data_pages_ is the readable view.
+  MemMap writable_data_pages_;
+
   // Mem map which holds code and has executable permission.
   MemMap exec_pages_;