Bionic libs and the dynamic linker are bind mounted am: 2599088ff6 am: 82a0d5be74
am: 90aae8d6c7

Change-Id: I6302778662b0b38362496c4f935f884e18cbb75f
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 52828c0..4a66e46 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -1093,6 +1093,86 @@
     }
 }
 
+static Result<Success> bind_mount_file(const char* source, const char* mount_point,
+                                       bool remount_private) {
+    if (remount_private && mount(nullptr, mount_point, nullptr, MS_PRIVATE, nullptr) == -1) {
+        return ErrnoError() << "Could not change " << mount_point << " to a private mount point";
+    }
+    if (mount(source, mount_point, nullptr, MS_BIND, nullptr) == -1) {
+        return ErrnoError() << "Could not bind-mount " << source << " to " << mount_point;
+    }
+    return Success();
+}
+
+static Result<Success> bind_mount_bionic(const char* linker_source, const char* lib_dir_source,
+                                         const char* linker_mount_point, const char* lib_mount_dir,
+                                         bool remount_private) {
+    if (access(linker_source, F_OK) != 0) {
+        return Success();
+    }
+    if (auto result = bind_mount_file(linker_source, linker_mount_point, remount_private);
+        !result) {
+        return result;
+    }
+    for (auto libname : kBionicLibFileNames) {
+        std::string mount_point = lib_mount_dir + libname;
+        std::string source = lib_dir_source + libname;
+        if (auto result = bind_mount_file(source.c_str(), mount_point.c_str(), remount_private);
+            !result) {
+            return result;
+        }
+    }
+    return Success();
+}
+
+// The bootstrap bionic libs and the bootstrap linker are bind-mounted to
+// the mount points for pre-apexd processes.
+static Result<Success> do_prepare_bootstrap_bionic(const BuiltinArguments& args) {
+    static bool prepare_bootstrap_bionic_done = false;
+    if (prepare_bootstrap_bionic_done) {
+        return Error() << "prepare_bootstrap_bionic was already executed. Cannot be executed again";
+    }
+    if (auto result = bind_mount_bionic(kBootstrapLinkerPath, kBootstrapBionicLibsDir,
+                                        kLinkerMountPoint, kBionicLibsMountPointDir, false);
+        !result) {
+        return result;
+    }
+    if (auto result = bind_mount_bionic(kBootstrapLinkerPath64, kBootstrapBionicLibsDir64,
+                                        kLinkerMountPoint64, kBionicLibsMountPointDir64, false);
+        !result) {
+        return result;
+    }
+
+    LOG(INFO) << "prepare_bootstrap_bionic done";
+    prepare_bootstrap_bionic_done = true;
+    return Success();
+}
+
+// The bionic libs and the dynamic linker from the runtime APEX are bind-mounted
+// to the mount points. As a result, the previous mounts done by
+// prepare_bootstrap_bionic become hidden.
+static Result<Success> do_setup_runtime_bionic(const BuiltinArguments& args) {
+    static bool setup_runtime_bionic_done = false;
+    if (setup_runtime_bionic_done) {
+        return Error() << "setup_runtime_bionic was already executed. Cannot be executed again";
+    }
+    if (auto result = bind_mount_bionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir,
+                                        kLinkerMountPoint, kBionicLibsMountPointDir, true);
+        !result) {
+        return result;
+    }
+    if (auto result = bind_mount_bionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64,
+                                        kLinkerMountPoint64, kBionicLibsMountPointDir64, true);
+        !result) {
+        return result;
+    }
+
+    ServiceList::GetInstance().MarkRuntimeAvailable();
+    LOG(INFO) << "setup_runtime_bionic done";
+    setup_runtime_bionic_done = true;
+    return Success();
+}
+
 // Builtin-function-map start
 const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
@@ -1131,6 +1211,7 @@
         {"mount_all",               {1,     kMax, {false,  do_mount_all}}},
         {"mount",                   {3,     kMax, {false,  do_mount}}},
         {"parse_apex_configs",      {0,     0,    {false,  do_parse_apex_configs}}},
+        {"prepare_bootstrap_bionic",{0,     0,    {false,  do_prepare_bootstrap_bionic}}},
         {"umount",                  {1,     1,    {false,  do_umount}}},
         {"readahead",               {1,     2,    {true,   do_readahead}}},
         {"restart",                 {1,     1,    {false,  do_restart}}},
@@ -1139,6 +1220,7 @@
         {"rm",                      {1,     1,    {true,   do_rm}}},
         {"rmdir",                   {1,     1,    {true,   do_rmdir}}},
         {"setprop",                 {2,     2,    {true,   do_setprop}}},
+        {"setup_runtime_bionic",    {0,     0,    {false,  do_setup_runtime_bionic}}},
         {"setrlimit",               {3,     3,    {false,  do_setrlimit}}},
         {"start",                   {1,     1,    {false,  do_start}}},
         {"stop",                    {1,     1,    {false,  do_stop}}},
diff --git a/init/service.cpp b/init/service.cpp
index 272809f..645c34f 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -140,6 +140,43 @@
     return Success();
 }
 
+Result<Success> Service::SetUpPreApexdMounts() const {
+    // If a pre-apexd service is 're' launched after the runtime APEX is
+    // available, unmount the linker and bionic libs which are currently
+    // bind mounted to the files in the runtime APEX. This will reveal
+    // the hidden mount points (targetting the bootstrap ones in the
+    // system partition) which were setup before the runtime APEX was
+    // started. Note that these unmounts are done in a separate mount namespace
+    // for the process. It does not affect other processes including the init.
+    if (pre_apexd_ && ServiceList::GetInstance().IsRuntimeAvailable()) {
+        if (access(kLinkerMountPoint, F_OK) == 0) {
+            if (umount(kLinkerMountPoint) == -1) {
+                return ErrnoError() << "Could not umount " << kLinkerMountPoint;
+            }
+            for (const auto& libname : kBionicLibFileNames) {
+                std::string mount_point = kBionicLibsMountPointDir + libname;
+                if (umount(mount_point.c_str()) == -1) {
+                    return ErrnoError() << "Could not umount " << mount_point;
+                }
+            }
+        }
+
+        if (access(kLinkerMountPoint64, F_OK) == 0) {
+            if (umount(kLinkerMountPoint64) == -1) {
+                return ErrnoError() << "Could not umount " << kLinkerMountPoint64;
+            }
+            for (const auto& libname : kBionicLibFileNames) {
+                std::string mount_point = kBionicLibsMountPointDir64 + libname;
+                std::string source = kBootstrapBionicLibsDir64 + libname;
+                if (umount(mount_point.c_str()) == -1) {
+                    return ErrnoError() << "Could not umount " << mount_point;
+                }
+            }
+        }
+    }
+    return Success();
+}
+
 Result<Success> Service::SetUpPidNamespace() const {
     if (prctl(PR_SET_NAME, name_.c_str()) == -1) {
         return ErrnoError() << "Could not set name";
@@ -929,6 +966,14 @@
         scon = *result;
     }
 
+    if (!ServiceList::GetInstance().IsRuntimeAvailable() && !pre_apexd_) {
+        // If this service is started before the runtime APEX gets available,
+        // mark it as pre-apexd one. Note that this marking is permanent. So
+        // for example, if the service is re-launched (e.g., due to crash),
+        // it is still recognized as pre-apexd... for consistency.
+        pre_apexd_ = true;
+    }
+
     LOG(INFO) << "starting service '" << name_ << "'...";
 
     pid_t pid = -1;
@@ -945,6 +990,26 @@
             LOG(FATAL) << "Service '" << name_ << "' could not enter namespaces: " << result.error();
         }
 
+        if (pre_apexd_) {
+            // pre-apexd process gets a private copy of the mount namespace.
+            // However, this does not mean that mount/unmount events are not
+            // shared across pre-apexd processes and post-apexd processes.
+            // *Most* of the events are still shared because the propagation
+            // type of / is set to 'shared'. (see `mount rootfs rootfs /shared
+            // rec` in init.rc)
+            //
+            // This unsharing is required to not propagate the mount events
+            // under /system/lib/{libc|libdl|libm}.so and /system/bin/linker(64)
+            // whose propagation type is set to private. With this,
+            // bind-mounting the bionic libs and the dynamic linker from the
+            // runtime APEX to the mount points does not affect pre-apexd
+            // processes which should use the bootstrap ones.
+            if (unshare(CLONE_NEWNS) != 0) {
+                LOG(FATAL) << "Creating a new mount namespace for service"
+                           << " '" << name_ << "' failed: " << strerror(errno);
+            }
+        }
+
         if (namespace_flags_ & CLONE_NEWNS) {
             if (auto result = SetUpMountNamespace(); !result) {
                 LOG(FATAL) << "Service '" << name_
@@ -952,6 +1017,13 @@
             }
         }
 
+        if (pre_apexd_ && ServiceList::GetInstance().IsRuntimeAvailable()) {
+            if (auto result = SetUpPreApexdMounts(); !result) {
+                LOG(FATAL) << "Pre-apexd service '" << name_
+                           << "' could not setup the mount points: " << result.error();
+            }
+        }
+
         if (namespace_flags_ & CLONE_NEWPID) {
             // This will fork again to run an init process inside the PID
             // namespace.
@@ -1324,6 +1396,10 @@
     delayed_service_names_.clear();
 }
 
+void ServiceList::MarkRuntimeAvailable() {
+    runtime_available_ = true;
+}
+
 void ServiceList::DelayService(const Service& service) {
     if (services_update_finished_) {
         LOG(ERROR) << "Cannot delay the start of service '" << service.name()
diff --git a/init/service.h b/init/service.h
index 56e75b0..676111f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -62,6 +62,24 @@
 namespace android {
 namespace init {
 
+static constexpr const char* kLinkerMountPoint = "/system/bin/linker";
+static constexpr const char* kBootstrapLinkerPath = "/system/bin/linker";
+static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker";
+
+static constexpr const char* kBionicLibsMountPointDir = "/system/lib/";
+static constexpr const char* kBootstrapBionicLibsDir = "/system/lib/";
+static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/";
+
+static constexpr const char* kLinkerMountPoint64 = "/system/bin/linker64";
+static constexpr const char* kBootstrapLinkerPath64 = "/system/bin/linker64";
+static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64";
+
+static constexpr const char* kBionicLibsMountPointDir64 = "/system/lib64/";
+static constexpr const char* kBootstrapBionicLibsDir64 = "/system/lib64/";
+static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/";
+
+static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"};
+
 class Service {
   public:
     Service(const std::string& name, Subcontext* subcontext_for_restart_commands,
@@ -124,6 +142,7 @@
     std::optional<std::chrono::seconds> timeout_period() const { return timeout_period_; }
     const std::vector<std::string>& args() const { return args_; }
     bool is_updatable() const { return updatable_; }
+    bool is_pre_apexd() const { return pre_apexd_; }
 
   private:
     using OptionParser = Result<Success> (Service::*)(std::vector<std::string>&& args);
@@ -132,6 +151,7 @@
     Result<Success> SetUpMountNamespace() const;
     Result<Success> SetUpPidNamespace() const;
     Result<Success> EnterNamespaces() const;
+    Result<Success> SetUpPreApexdMounts() const;
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
     void ZapStdio() const;
@@ -242,6 +262,8 @@
     std::vector<std::string> args_;
 
     std::vector<std::function<void(const siginfo_t& siginfo)>> reap_callbacks_;
+
+    bool pre_apexd_ = false;
 };
 
 class ServiceList {
@@ -284,13 +306,16 @@
     const std::vector<Service*> services_in_shutdown_order() const;
 
     void MarkServicesUpdate();
+    void MarkRuntimeAvailable();
     bool IsServicesUpdated() const { return services_update_finished_; }
+    bool IsRuntimeAvailable() const { return runtime_available_; }
     void DelayService(const Service& service);
 
   private:
     std::vector<std::unique_ptr<Service>> services_;
 
     bool services_update_finished_ = false;
+    bool runtime_available_ = false;
     std::vector<std::string> delayed_service_names_;
 };
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 0ec6e17..b34399d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -12,6 +12,12 @@
 import /init.${ro.zygote}.rc
 
 on early-init
+    # Mount shared so changes propagate into child namespaces
+    # Do this before other processes are started from init. Otherwise,
+    # processes launched while the propagation type of / is 'private'
+    # won't get mount events from others.
+    mount rootfs rootfs / shared rec
+
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_score_adj -1000
 
@@ -40,6 +46,8 @@
     # cgroup for system_server and surfaceflinger
     mkdir /dev/memcg/system 0550 system system
 
+    prepare_bootstrap_bionic
+
     start ueventd
 
 on init
@@ -350,8 +358,6 @@
     # Once everything is setup, no need to modify /.
     # The bind+remount combination allows this to work in containers.
     mount rootfs rootfs / remount bind ro nodev
-    # Mount shared so changes propagate into child namespaces
-    mount rootfs rootfs / shared rec
     # Mount default storage into root namespace
     mount none /mnt/runtime/default /storage bind rec
     mount none none /storage slave rec
@@ -587,6 +593,14 @@
     # Check any timezone data in /data is newer than the copy in the runtime module, delete if not.
     exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo
 
+    # Wait for apexd to finish activating APEXes before starting more processes.
+    # This certainly reduces the parallelism but is required to make as many processes
+    # as possible to use the bionic libs from the runtime APEX. This takes less than 50ms
+    # so the impact on the booting time is not significant.
+    wait_for_prop apexd.status ready
+    setup_runtime_bionic
+    parse_apex_configs
+
     # If there is no post-fs-data action in the init.<device>.rc file, you
     # must uncomment this line, otherwise encrypted filesystems
     # won't work.
@@ -808,6 +822,3 @@
 service flash_recovery /system/bin/install-recovery.sh
     class main
     oneshot
-
-on property:apexd.status=ready
-    parse_apex_configs