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.