ART: add hook for framework to set SELinux context

Adds a new zygote hook for system server, nativePostForkSystemServer,
so the process can transition between the system_server_startup and
system_server SELinux domains.

Memory resources for the JIT are allocated in the hook as setting the
SELinux domain with setcon() requires that the process is still single
threaded.

Bug: 66095511
Test: device boots
Test: art/test.py --host --64
Change-Id: Ic840634c5c59906b8d344c2edffafeb9b13a409f
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index ef893ee..e876a1b 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -168,34 +168,26 @@
   cumulative_timings_.AddLogger(logger);
 }
 
-Jit::Jit(JitOptions* options) : options_(options),
-                                cumulative_timings_("JIT timings"),
-                                memory_use_("Memory used for compilation", 16),
-                                lock_("JIT memory use lock") {}
+Jit::Jit(JitCodeCache* code_cache, JitOptions* options)
+    : code_cache_(code_cache),
+      options_(options),
+      cumulative_timings_("JIT timings"),
+      memory_use_("Memory used for compilation", 16),
+      lock_("JIT memory use lock") {}
 
-Jit* Jit::Create(JitOptions* options, std::string* error_msg) {
-  DCHECK(options->UseJitCompilation() || options->GetProfileSaverOptions().IsEnabled());
-  std::unique_ptr<Jit> jit(new Jit(options));
-  if (jit_compiler_handle_ == nullptr && !LoadCompiler(error_msg)) {
+Jit* Jit::Create(JitCodeCache* code_cache, JitOptions* options) {
+  CHECK(jit_compiler_handle_ != nullptr) << "Jit::LoadLibrary() needs to be called first";
+  std::unique_ptr<Jit> jit(new Jit(code_cache, options));
+  if (jit_compiler_handle_ == nullptr) {
     return nullptr;
   }
-  bool code_cache_only_for_profile_data = !options->UseJitCompilation();
-  jit->code_cache_.reset(JitCodeCache::Create(
-      options->GetCodeCacheInitialCapacity(),
-      options->GetCodeCacheMaxCapacity(),
-      jit->generate_debug_info_,
-      code_cache_only_for_profile_data,
-      error_msg));
-  if (jit->GetCodeCache() == nullptr) {
-    return nullptr;
-  }
+
   VLOG(jit) << "JIT created with initial_capacity="
       << PrettySize(options->GetCodeCacheInitialCapacity())
       << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity())
       << ", compile_threshold=" << options->GetCompileThreshold()
       << ", profile_saver_options=" << options->GetProfileSaverOptions();
 
-
   jit->CreateThreadPool();
 
   // Notify native debugger about the classes already loaded before the creation of the jit.
@@ -203,7 +195,7 @@
   return jit.release();
 }
 
-bool Jit::LoadCompilerLibrary(std::string* error_msg) {
+bool Jit::BindCompilerMethods(std::string* error_msg) {
   jit_library_handle_ = dlopen(
       kIsDebugBuild ? "libartd-compiler.so" : "libart-compiler.so", RTLD_NOW);
   if (jit_library_handle_ == nullptr) {
@@ -243,7 +235,7 @@
 }
 
 bool Jit::LoadCompiler(std::string* error_msg) {
-  if (jit_library_handle_ == nullptr && !LoadCompilerLibrary(error_msg)) {
+  if (jit_library_handle_ == nullptr && !BindCompilerMethods(error_msg)) {
     return false;
   }
   bool will_generate_debug_symbols = false;
@@ -308,6 +300,11 @@
   return success;
 }
 
+bool Jit::ShouldGenerateDebugInfo() {
+  CHECK(CompilerIsLoaded());
+  return generate_debug_info_;
+}
+
 void Jit::CreateThreadPool() {
   // There is a DCHECK in the 'AddSamples' method to ensure the tread pool
   // is not null when we instrument.
@@ -347,10 +344,7 @@
 void Jit::StartProfileSaver(const std::string& filename,
                             const std::vector<std::string>& code_paths) {
   if (options_->GetSaveProfilingInfo()) {
-    ProfileSaver::Start(options_->GetProfileSaverOptions(),
-                        filename,
-                        code_cache_.get(),
-                        code_paths);
+    ProfileSaver::Start(options_->GetProfileSaverOptions(), filename, code_cache_, code_paths);
   }
 }
 
@@ -391,7 +385,7 @@
     return;
   }
   jit::Jit* jit = Runtime::Current()->GetJit();
-  if (jit->generate_debug_info_) {
+  if (generate_debug_info_) {
     DCHECK(jit->jit_types_loaded_ != nullptr);
     jit->jit_types_loaded_(jit->jit_compiler_handle_, &type, 1);
   }
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index edaf348..b0ea19b 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -100,6 +100,10 @@
     return use_jit_compilation_;
   }
 
+  bool RWXMemoryAllowed() const {
+    return rwx_memory_allowed_;
+  }
+
   void SetUseJitCompilation(bool b) {
     use_jit_compilation_ = b;
   }
@@ -121,6 +125,10 @@
     compile_threshold_ = 0;
   }
 
+  void SetRWXMemoryAllowed(bool rwx_allowed) {
+    rwx_memory_allowed_ = rwx_allowed;
+  }
+
  private:
   bool use_jit_compilation_;
   size_t code_cache_initial_capacity_;
@@ -132,6 +140,7 @@
   uint16_t invoke_transition_weight_;
   bool dump_info_on_shutdown_;
   int thread_pool_pthread_priority_;
+  bool rwx_memory_allowed_;
   ProfileSaverOptions profile_saver_options_;
 
   JitOptions()
@@ -144,7 +153,8 @@
         priority_thread_weight_(0),
         invoke_transition_weight_(0),
         dump_info_on_shutdown_(false),
-        thread_pool_pthread_priority_(kJitPoolThreadPthreadDefaultPriority) {}
+        thread_pool_pthread_priority_(kJitPoolThreadPthreadDefaultPriority),
+        rwx_memory_allowed_(true) {}
 
   DISALLOW_COPY_AND_ASSIGN(JitOptions);
 };
@@ -157,20 +167,24 @@
   static constexpr int16_t kJitRecheckOSRThreshold = 100;
 
   virtual ~Jit();
-  static Jit* Create(JitOptions* options, std::string* error_msg);
+
+  // Create JIT itself.
+  static Jit* Create(JitCodeCache* code_cache, JitOptions* options);
+
   bool CompileMethod(ArtMethod* method, Thread* self, bool osr)
       REQUIRES_SHARED(Locks::mutator_lock_);
-  void CreateThreadPool();
 
   const JitCodeCache* GetCodeCache() const {
-    return code_cache_.get();
+    return code_cache_;
   }
 
   JitCodeCache* GetCodeCache() {
-    return code_cache_.get();
+    return code_cache_;
   }
 
+  void CreateThreadPool();
   void DeleteThreadPool();
+
   // Dump interesting info: #methods compiled, code vs data size, compile / verify cumulative
   // loggers.
   void DumpInfo(std::ostream& os) REQUIRES(!lock_);
@@ -268,7 +282,13 @@
                                         JValue* result)
       REQUIRES_SHARED(Locks::mutator_lock_);
 
-  static bool LoadCompilerLibrary(std::string* error_msg);
+  // Load and initialize compiler.
+  static bool LoadCompiler(std::string* error_msg);
+
+  static bool CompilerIsLoaded() { return jit_compiler_handle_ != nullptr; }
+
+  // Return whether debug info should be generated. Requires LoadCompiler() to have been called.
+  static bool ShouldGenerateDebugInfo();
 
   ThreadPool* GetThreadPool() const {
     return thread_pool_.get();
@@ -281,9 +301,9 @@
   void Start();
 
  private:
-  explicit Jit(JitOptions* options);
+  Jit(JitCodeCache* code_cache, JitOptions* options);
 
-  static bool LoadCompiler(std::string* error_msg);
+  static bool BindCompilerMethods(std::string* error_msg);
 
   // JIT compiler
   static void* jit_library_handle_;
@@ -296,9 +316,10 @@
   // We make this static to simplify the interaction with libart-compiler.so.
   static bool generate_debug_info_;
 
+  // JIT resources owned by runtime.
+  jit::JitCodeCache* const code_cache_;
   const JitOptions* const options_;
 
-  std::unique_ptr<jit::JitCodeCache> code_cache_;
   std::unique_ptr<ThreadPool> thread_pool_;
 
   // Performance monitoring.
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 082b311..a15a9be 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -185,18 +185,12 @@
 
 JitCodeCache* JitCodeCache::Create(size_t initial_capacity,
                                    size_t max_capacity,
-                                   bool generate_debug_info,
                                    bool used_only_for_profile_data,
+                                   bool rwx_memory_allowed,
                                    std::string* error_msg) {
   ScopedTrace trace(__PRETTY_FUNCTION__);
   CHECK_GE(max_capacity, initial_capacity);
 
-  // With 'perf', we want a 1-1 mapping between an address and a method.
-  // We aren't able to keep method pointers live during the instrumentation method entry trampoline
-  // so we will just disable jit-gc if we are doing that.
-  bool garbage_collect_code = !generate_debug_info &&
-      !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled();
-
   // We need to have 32 bit offsets from method headers in code cache which point to things
   // in the data cache. If the maps are more than 4G apart, having multiple maps wouldn't work.
   // Ensure we're below 1 GB to be safe.
@@ -224,8 +218,15 @@
   // Bionic supports memfd_create, but the call may fail on older kernels.
   mem_fd = unique_fd(art::memfd_create("/jit-cache", /* flags= */ 0));
   if (mem_fd.get() < 0) {
-    VLOG(jit) << "Failed to initialize dual view JIT. memfd_create() error: "
-              << strerror(errno);
+    std::ostringstream oss;
+    oss << "Failed to initialize dual view JIT. memfd_create() error: " << strerror(errno);
+    if (!rwx_memory_allowed) {
+      // Without using RWX page permissions, the JIT can not fallback to single mapping as it
+      // requires tranitioning the code pages to RWX for updates.
+      *error_msg = oss.str();
+      return nullptr;
+    }
+    VLOG(jit) << oss.str();
   }
 
   if (mem_fd.get() >= 0 && ftruncate(mem_fd, max_capacity) != 0) {
@@ -350,8 +351,14 @@
                                        "jit-code-cache-rw",
                                        &error_str);
       if (!non_exec_pages.IsValid()) {
-        // Log and continue as single view JIT.
-        VLOG(jit) << "Failed to map non-executable view of JIT code cache";
+        static const char* kFailedNxView = "Failed to map non-executable view of JIT code cache";
+        if (rwx_memory_allowed) {
+          // Log and continue as single view JIT (requires RWX memory).
+          VLOG(jit) << kFailedNxView;
+        } else {
+          *error_msg = kFailedNxView;
+          return nullptr;
+        }
       }
     }
   } else {
@@ -369,8 +376,7 @@
       std::move(non_exec_pages),
       initial_data_capacity,
       initial_exec_capacity,
-      max_capacity,
-      garbage_collect_code);
+      max_capacity);
 }
 
 JitCodeCache::JitCodeCache(MemMap&& data_pages,
@@ -378,8 +384,7 @@
                            MemMap&& non_exec_pages,
                            size_t initial_data_capacity,
                            size_t initial_exec_capacity,
-                           size_t max_capacity,
-                           bool garbage_collect_code)
+                           size_t max_capacity)
     : lock_("Jit code cache", kJitCodeCacheLock),
       lock_cond_("Jit code cache condition variable", lock_),
       collection_in_progress_(false),
@@ -391,7 +396,7 @@
       data_end_(initial_data_capacity),
       exec_end_(initial_exec_capacity),
       last_collection_increased_code_cache_(false),
-      garbage_collect_code_(garbage_collect_code),
+      garbage_collect_code_(true),
       used_memory_for_data_(0),
       used_memory_for_code_(0),
       number_of_compilations_(0),
@@ -431,6 +436,12 @@
     SetFootprintLimit(current_capacity_);
   }
 
+  // With 'perf', we want a 1-1 mapping between an address and a method.
+  // We aren't able to keep method pointers live during the instrumentation method entry trampoline
+  // so we will just disable jit-gc if we are doing that.
+  garbage_collect_code_ = !Jit::ShouldGenerateDebugInfo() &&
+      !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled();
+
   VLOG(jit) << "Created jit code cache: initial data size="
             << PrettySize(initial_data_capacity)
             << ", initial code size="
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 76ad8db..126fd44 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -92,8 +92,8 @@
   // in the out arg error_msg.
   static JitCodeCache* Create(size_t initial_capacity,
                               size_t max_capacity,
-                              bool generate_debug_info,
                               bool used_only_for_profile_data,
+                              bool rwx_memory_allowed,
                               std::string* error_msg);
   ~JitCodeCache();
 
@@ -261,8 +261,8 @@
   void MoveObsoleteMethod(ArtMethod* old_method, ArtMethod* new_method)
       REQUIRES(!lock_) REQUIRES(Locks::mutator_lock_);
 
-  // Dynamically change whether we want to garbage collect code. Should only be used
-  // by tests.
+  // Dynamically change whether we want to garbage collect code. Should only be used during JIT
+  // initialization or by tests.
   void SetGarbageCollectCode(bool value) {
     garbage_collect_code_ = value;
   }
@@ -284,8 +284,7 @@
                MemMap&& non_exec_pages,
                size_t initial_data_capacity,
                size_t initial_exec_capacity,
-               size_t max_capacity,
-               bool garbage_collect_code);
+               size_t max_capacity);
 
   // Internal version of 'CommitCode' that will not retry if the
   // allocation fails. Return null if the allocation fails.
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index f5c0704..4d3ad62 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -282,6 +282,15 @@
   return reinterpret_cast<jlong>(ThreadForEnv(env));
 }
 
+static void ZygoteHooks_nativePostForkSystemServer(JNIEnv* env ATTRIBUTE_UNUSED,
+                                                   jclass klass ATTRIBUTE_UNUSED) {
+  // This JIT code cache for system server is created whilst the runtime is still single threaded.
+  // System server has a window where it can create executable pages for this purpose, but this is
+  // turned off after this hook. Consequently, the only JIT mode supported is the dual-view JIT
+  // where one mapping is R->RW and the other is RX. Single view requires RX->RWX->RX.
+  Runtime::Current()->CreateJitCodeCache(/*rwx_memory_allowed=*/false);
+}
+
 static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
                                             jclass,
                                             jlong token,
@@ -419,6 +428,7 @@
 
 static JNINativeMethod gMethods[] = {
   NATIVE_METHOD(ZygoteHooks, nativePreFork, "()J"),
+  NATIVE_METHOD(ZygoteHooks, nativePostForkSystemServer, "()V"),
   NATIVE_METHOD(ZygoteHooks, nativePostForkChild, "(JIZZLjava/lang/String;)V"),
   NATIVE_METHOD(ZygoteHooks, startZygoteNoThreadCreation, "()V"),
   NATIVE_METHOD(ZygoteHooks, stopZygoteNoThreadCreation, "()V"),
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index c312126..34b84f5 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -407,6 +407,7 @@
   if (jit_ != nullptr) {
     VLOG(jit) << "Deleting jit";
     jit_.reset(nullptr);
+    jit_code_cache_.reset(nullptr);
   }
 
   // Shutdown the fault manager if it was initialized.
@@ -785,17 +786,15 @@
   // TODO(calin): We use the JIT class as a proxy for JIT compilation and for
   // recoding profiles. Maybe we should consider changing the name to be more clear it's
   // not only about compiling. b/28295073.
-  if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) {
+  if (!safe_mode_ && (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo())) {
+    // Try to load compiler pre zygote to reduce PSS. b/27744947
     std::string error_msg;
-    if (!IsZygote()) {
-    // If we are the zygote then we need to wait until after forking to create the code cache
-    // due to SELinux restrictions on r/w/x memory regions.
-      CreateJit();
-    } else if (jit_options_->UseJitCompilation()) {
-      if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
-        // Try to load compiler pre zygote to reduce PSS. b/27744947
-        LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
-      }
+    if (!jit::Jit::LoadCompiler(&error_msg)) {
+      LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
+    } else if (!IsZygote()) {
+      // If we are the zygote then we need to wait until after forking to create the code cache
+      // due to SELinux restrictions on r/w/x memory regions.
+      CreateJitCodeCache(/*rwx_memory_allowed=*/true);
     }
   }
 
@@ -892,29 +891,26 @@
     }
   }
 
-  // Create the thread pools.
-  heap_->CreateThreadPool();
-  // Reset the gc performance data at zygote fork so that the GCs
-  // before fork aren't attributed to an app.
-  heap_->ResetGcPerformanceInfo();
-
-  // We may want to collect profiling samples for system server, but we never want to JIT there.
   if (is_system_server) {
-    jit_options_->SetUseJitCompilation(false);
     jit_options_->SetSaveProfilingInfo(profile_system_server);
     if (profile_system_server) {
       jit_options_->SetWaitForJitNotificationsToSaveProfile(false);
       VLOG(profiler) << "Enabling system server profiles";
     }
   }
-  if (!safe_mode_ &&
-      (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) &&
-      jit_ == nullptr) {
+
+  if (jit_ == nullptr) {
     // Note that when running ART standalone (not zygote, nor zygote fork),
     // the jit may have already been created.
     CreateJit();
   }
 
+  // Create the thread pools.
+  heap_->CreateThreadPool();
+  // Reset the gc performance data at zygote fork so that the GCs
+  // before fork aren't attributed to an app.
+  heap_->ResetGcPerformanceInfo();
+
   StartSignalCatcher();
 
   // Start the JDWP thread. If the command-line debugger flags specified "suspend=y",
@@ -2484,18 +2480,43 @@
   argv->push_back(feature_string);
 }
 
-void Runtime::CreateJit() {
-  CHECK(!IsAotCompiler());
+void Runtime::CreateJitCodeCache(bool rwx_memory_allowed) {
   if (kIsDebugBuild && GetInstrumentation()->IsForcedInterpretOnly()) {
     DCHECK(!jit_options_->UseJitCompilation());
   }
-  std::string error_msg;
-  jit::Jit* jit = jit::Jit::Create(jit_options_.get(), &error_msg);
-  DoAndMaybeSwitchInterpreter([=](){ jit_.reset(jit); });
-  if (jit_.get() == nullptr) {
-    LOG(WARNING) << "Failed to create JIT " << error_msg;
+
+  if (safe_mode_ || (!jit_options_->UseJitCompilation() && !jit_options_->GetSaveProfilingInfo())) {
     return;
   }
+
+  // SystemServer has execmem blocked by SELinux so can not use RWX page permissions after the
+  // cache initialized.
+  jit_options_->SetRWXMemoryAllowed(rwx_memory_allowed);
+
+  std::string error_msg;
+  bool profiling_only = !jit_options_->UseJitCompilation();
+  jit_code_cache_.reset(jit::JitCodeCache::Create(jit_options_->GetCodeCacheInitialCapacity(),
+                                                  jit_options_->GetCodeCacheMaxCapacity(),
+                                                  profiling_only,
+                                                  jit_options_->RWXMemoryAllowed(),
+                                                  &error_msg));
+  if (jit_code_cache_.get() == nullptr) {
+    LOG(WARNING) << "Failed to create JIT Code Cache: " << error_msg;
+  }
+}
+
+void Runtime::CreateJit() {
+  if (jit_code_cache_.get() == nullptr) {
+    return;
+  }
+
+  jit::Jit* jit = jit::Jit::Create(jit_code_cache_.get(), jit_options_.get());
+  DoAndMaybeSwitchInterpreter([=](){ jit_.reset(jit); });
+  if (jit == nullptr) {
+    LOG(WARNING) << "Failed to allocate JIT";
+    // Release JIT code cache resources (several MB of memory).
+    jit_code_cache_.reset(nullptr);
+  }
 }
 
 bool Runtime::CanRelocate() const {
diff --git a/runtime/runtime.h b/runtime/runtime.h
index e27c87d..4fb0d2e 100644
--- a/runtime/runtime.h
+++ b/runtime/runtime.h
@@ -56,6 +56,7 @@
 
 namespace jit {
 class Jit;
+class JitCodeCache;
 class JitOptions;
 }  // namespace jit
 
@@ -614,6 +615,8 @@
     return (experimental_flags_ & flags) != ExperimentalFlags::kNone;
   }
 
+  void CreateJitCodeCache(bool rwx_memory_allowed);
+
   // Create the JIT and instrumentation and code cache.
   void CreateJit();
 
@@ -906,6 +909,7 @@
   std::unique_ptr<JavaVMExt> java_vm_;
 
   std::unique_ptr<jit::Jit> jit_;
+  std::unique_ptr<jit::JitCodeCache> jit_code_cache_;
   std::unique_ptr<jit::JitOptions> jit_options_;
 
   // Fault message, printed when we get a SIGSEGV.