Merge "fix shared gid support in getpwnam/getgrnam"
diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c
index 28d13f4..29f605c 100644
--- a/libc/tzcode/localtime.c
+++ b/libc/tzcode/localtime.c
@@ -1257,6 +1257,7 @@
         lclptr->ttis[0].tt_gmtoff = 0;
         lclptr->ttis[0].tt_abbrind = 0;
         (void) strcpy(lclptr->chars, gmt);
+        lclptr->defaulttype = 0;
     } else if (tzload(name, lclptr, TRUE) != 0)
         if (name[0] == ':' || tzparse(name, lclptr, FALSE) != 0)
             (void) gmtload(lclptr);
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 11d7b94..1d68db5 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -1135,28 +1135,27 @@
   return si;
 }
 
-static void soinfo_unload(soinfo* si) {
+static void soinfo_unload_schedule(soinfo::soinfo_list_t& unload_list, 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->call_destructors();
+    unload_list.push_back(si);
 
     if (si->has_min_version(0)) {
       soinfo* child = nullptr;
       while ((child = si->get_children().pop_front()) != nullptr) {
         TRACE("%s needs to unload %s", si->name, child->name);
-        soinfo_unload(child);
+        soinfo_unload_schedule(unload_list, child);
       }
     } else {
       for_each_dt_needed(si, [&] (const char* library_name) {
         TRACE("deprecated (old format of soinfo): %s needs to unload %s", si->name, library_name);
         soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr);
         if (needed != nullptr) {
-          soinfo_unload(needed);
+          soinfo_unload_schedule(unload_list, needed);
         } else {
           // Not found: for example if symlink was deleted between dlopen and dlclose
           // Since we cannot really handle errors at this point - print and continue.
@@ -1165,15 +1164,28 @@
       });
     }
 
-    notify_gdb_of_unload(si);
     si->ref_count = 0;
-    soinfo_free(si);
   } else {
     si->ref_count--;
     TRACE("not unloading '%s', decrementing ref_count to %zd", si->name, si->ref_count);
   }
 }
 
+static void soinfo_unload(soinfo* root) {
+  soinfo::soinfo_list_t unload_list;
+  soinfo_unload_schedule(unload_list, root);
+  unload_list.for_each([](soinfo* si) {
+    si->call_destructors();
+  });
+
+  soinfo* si = nullptr;
+  while ((si = unload_list.pop_front()) != nullptr) {
+    TRACE("unloading '%s'", si->name);
+    notify_gdb_of_unload(si);
+    soinfo_free(si);
+  }
+}
+
 void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
   // Use basic string manipulation calls to avoid snprintf.
   // snprintf indirectly calls pthread_getspecific to get the size of a buffer.
diff --git a/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp b/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp
index 065d1be..d6d1f09 100644
--- a/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp
+++ b/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp
@@ -19,3 +19,19 @@
 extern "C" int check_order_reloc_nephew_get_answer() {
   return check_order_reloc_get_answer_impl();
 }
+
+namespace {
+// The d-tor for this class is called on dlclose() -> __on_dlclose() -> __cxa_finalize()
+// We use it to detect calls to prematurely unmapped libraries during dlclose.
+// See also b/18338888
+class CallNephewInDtor {
+ public:
+  ~CallNephewInDtor() {
+    check_order_reloc_get_answer_impl();
+  }
+} instance;
+};
+
+extern "C" void* get_instance() {
+  return &instance;
+}
diff --git a/tests/time_test.cpp b/tests/time_test.cpp
index 7a551b4..e0231b1 100644
--- a/tests/time_test.cpp
+++ b/tests/time_test.cpp
@@ -71,6 +71,30 @@
   ASSERT_EQ(0, pthread_join(t, &result));
 }
 
+TEST(time, mktime_empty_TZ) {
+  // tzcode used to have a bug where it didn't reinitialize some internal state.
+
+  // Choose a time where DST is set.
+  struct tm t;
+  memset(&t, 0, sizeof(tm));
+  t.tm_year = 1980 - 1900;
+  t.tm_mon = 6;
+  t.tm_mday = 2;
+
+  setenv("TZ", "America/Los_Angeles", 1);
+  tzset();
+  ASSERT_EQ(static_cast<time_t>(331372800U), mktime(&t));
+
+  memset(&t, 0, sizeof(tm));
+  t.tm_year = 1980 - 1900;
+  t.tm_mon = 6;
+  t.tm_mday = 2;
+
+  setenv("TZ", "", 1); // Implies UTC.
+  tzset();
+  ASSERT_EQ(static_cast<time_t>(331344000U), mktime(&t));
+}
+
 TEST(time, mktime_10310929) {
   struct tm t;
   memset(&t, 0, sizeof(tm));