Allocate thread local buffers in __init_tls.

Thread local buffers were using pthread_setspecific for storage with
lazy initialization. pthread_setspecific shares TLS slots between the
linker and libc.so, so thread local buffers being initialized in a
different order between libc.so and the linker meant that bad things
would happen (manifesting as snprintf not working because the
locale was mangled)

Bug: http://b/20464031
Test: /data/nativetest64/bionic-unit-tests/bionic-unit-tests
      everything passes
Test: /data/nativetest/bionic-unit-tests/bionic-unit-tests
      thread_local tests are failing both before and after (KUSER_HELPERS?)
Test: /data/nativetest64/bionic-unit-tests-static/bionic-unit-tests-static
      no additional failures
Change-Id: I9f445a77c6e86979f3fa49c4a5feecf6ec2b0c3f
diff --git a/libc/private/bionic_tls.h b/libc/private/bionic_tls.h
index c61e2ff..852b9ae 100644
--- a/libc/private/bionic_tls.h
+++ b/libc/private/bionic_tls.h
@@ -29,10 +29,15 @@
 #ifndef __BIONIC_PRIVATE_BIONIC_TLS_H_
 #define __BIONIC_PRIVATE_BIONIC_TLS_H_
 
+#include <locale.h>
+#include <mntent.h>
+#include <stdio.h>
 #include <sys/cdefs.h>
+#include <sys/param.h>
 
 #include "bionic_macros.h"
 #include "__get_tls.h"
+#include "grp_pwd.h"
 
 __BEGIN_DECLS
 
@@ -77,6 +82,28 @@
   BIONIC_TLS_SLOTS // Must come last!
 };
 
+// ~3 pages.
+struct bionic_tls {
+  locale_t locale;
+
+  char basename_buf[MAXPATHLEN];
+  char dirname_buf[MAXPATHLEN];
+
+  mntent mntent_buf;
+  char mntent_strings[BUFSIZ];
+
+  char ptsname_buf[32];
+  char ttyname_buf[64];
+
+  char strerror_buf[NL_TEXTMAX];
+  char strsignal_buf[NL_TEXTMAX];
+
+  group_state_t group;
+  passwd_state_t passwd;
+};
+
+#define BIONIC_TLS_SIZE (BIONIC_ALIGN(sizeof(bionic_tls), PAGE_SIZE))
+
 /*
  * Bionic uses some pthread keys internally. All pthread keys used internally
  * should be created in constructors, except for keys that may be used in or
@@ -86,22 +113,10 @@
  * pthread_test should fail if we forget.
  *
  * These are the pthread keys currently used internally by libc:
- *
- *  basename               libc (ThreadLocalBuffer)
- *  dirname                libc (ThreadLocalBuffer)
- *  uselocale              libc (can be used in constructors)
- *  getmntent_mntent       libc (ThreadLocalBuffer)
- *  getmntent_strings      libc (ThreadLocalBuffer)
- *  ptsname                libc (ThreadLocalBuffer)
- *  ttyname                libc (ThreadLocalBuffer)
- *  strerror               libc (ThreadLocalBuffer)
- *  strsignal              libc (ThreadLocalBuffer)
- *  passwd                 libc (ThreadLocalBuffer)
- *  group                  libc (ThreadLocalBuffer)
  *  _res_key               libc (constructor in BSD code)
  */
 
-#define LIBC_PTHREAD_KEY_RESERVED_COUNT 12
+#define LIBC_PTHREAD_KEY_RESERVED_COUNT 1
 
 /* Internally, jemalloc uses a single key for per thread data. */
 #define JEMALLOC_PTHREAD_KEY_RESERVED_COUNT 1
diff --git a/libc/private/ThreadLocalBuffer.h b/libc/private/grp_pwd.h
similarity index 60%
rename from libc/private/ThreadLocalBuffer.h
rename to libc/private/grp_pwd.h
index 5e43665..e1aff4f 100644
--- a/libc/private/ThreadLocalBuffer.h
+++ b/libc/private/grp_pwd.h
@@ -1,5 +1,7 @@
+#pragma once
+
 /*
- * Copyright (C) 2012 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,36 +28,21 @@
  * SUCH DAMAGE.
  */
 
-#ifndef _BIONIC_THREAD_LOCAL_BUFFER_H_included
-#define _BIONIC_THREAD_LOCAL_BUFFER_H_included
+#include <grp.h>
+#include <pwd.h>
 
-#include <malloc.h>
-#include <pthread.h>
-
-// TODO: use __thread instead?
-
-template <typename T, size_t Size = sizeof(T)>
-class ThreadLocalBuffer {
- public:
-  ThreadLocalBuffer() {
-    // We used to use pthread_once to initialize the keys, but life is more predictable
-    // if we allocate them all up front when the C library starts up, via __constructor__.
-    pthread_key_create(&key_, free);
-  }
-
-  T* get() {
-    T* result = reinterpret_cast<T*>(pthread_getspecific(key_));
-    if (result == nullptr) {
-      result = reinterpret_cast<T*>(calloc(1, Size));
-      pthread_setspecific(key_, result);
-    }
-    return result;
-  }
-
-  size_t size() { return Size; }
-
- private:
-  pthread_key_t key_;
+struct group_state_t {
+  group group_;
+  char* group_members_[2];
+  char group_name_buffer_[32];
+  // Must be last so init_group_state can run a simple memset for the above
+  ssize_t getgrent_idx;
 };
 
-#endif // _BIONIC_THREAD_LOCAL_BUFFER_H_included
+struct passwd_state_t {
+  passwd passwd_;
+  char name_buffer_[32];
+  char dir_buffer_[32];
+  char sh_buffer_[32];
+  ssize_t getpwent_idx;
+};