Add RTLD_NODELETE flag support

Bug: 18186310
Bug: https://code.google.com/p/android/issues/detail?id=64069

(cherry picked from commit 1b20dafdbe65e43b9f4c95057e8482380833ea91)

Change-Id: Ic02eec22a7c322ece65eb40730a3404f611526b1
diff --git a/linker/linker.cpp b/linker/linker.cpp
index d918bb5..7b27cd7 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -987,6 +987,11 @@
 }
 
 static void soinfo_unload(soinfo* si) {
+  if (!si->can_unload()) {
+    TRACE("not unloading '%s' - the binary is flagged with NODELETE", si->name);
+    return;
+  }
+
   if (si->ref_count == 1) {
     TRACE("unloading '%s'", si->name);
     si->CallDestructors();
@@ -1045,7 +1050,7 @@
 }
 
 soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo) {
-  if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL|RTLD_NOLOAD)) != 0) {
+  if ((flags & ~(RTLD_NOW|RTLD_LAZY|RTLD_LOCAL|RTLD_GLOBAL|RTLD_NODELETE|RTLD_NOLOAD)) != 0) {
     DL_ERR("invalid flags to dlopen: %x", flags);
     return nullptr;
   }
@@ -1808,6 +1813,9 @@
   return strtab + index;
 }
 
+bool soinfo::can_unload() const {
+  return (rtld_flags & (RTLD_NODELETE | RTLD_GLOBAL)) == 0;
+}
 /* Force any of the closed stdin, stdout and stderr to be associated with
    /dev/null. */
 static int nullify_closed_stdio() {
@@ -2076,9 +2084,13 @@
         if ((d->d_un.d_val & DF_1_GLOBAL) != 0) {
           rtld_flags |= RTLD_GLOBAL;
         }
+
+        if ((d->d_un.d_val & DF_1_NODELETE) != 0) {
+          rtld_flags |= RTLD_NODELETE;
+        }
         // TODO: Implement other flags
 
-        if ((d->d_un.d_val & ~(DF_1_NOW | DF_1_GLOBAL)) != 0) {
+        if ((d->d_un.d_val & ~(DF_1_NOW | DF_1_GLOBAL | DF_1_NODELETE)) != 0) {
           DL_WARN("Unsupported flags DT_FLAGS_1=%p", reinterpret_cast<void*>(d->d_un.d_val));
         }
         break;