Record TLS modules and layout static TLS memory

Bug: http://b/78026329
Test: bionic unit tests
Change-Id: Ibf1bf5ec864c7830e4cd1cb882842b644e6182ae
diff --git a/libc/bionic/libc_init_static.cpp b/libc/bionic/libc_init_static.cpp
index 6ba6583..1920727 100644
--- a/libc/bionic/libc_init_static.cpp
+++ b/libc/bionic/libc_init_static.cpp
@@ -83,10 +83,25 @@
   }
 }
 
-static void layout_static_tls() {
+static void layout_static_tls(KernelArgumentBlock& args) {
   StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout;
   layout.reserve_bionic_tls();
-  layout.reserve_exe_segment_and_tcb(nullptr);
+
+  const char* progname = args.argv[0];
+  ElfW(Phdr)* phdr_start = reinterpret_cast<ElfW(Phdr)*>(getauxval(AT_PHDR));
+  size_t phdr_ct = getauxval(AT_PHNUM);
+
+  static TlsModule module;
+  if (__bionic_get_tls_segment(phdr_start, phdr_ct, 0, progname, &module.segment)) {
+    module.static_offset = layout.reserve_exe_segment_and_tcb(&module.segment, progname);
+    module.first_generation = 1;
+    __libc_shared_globals()->tls_modules.generation = 1;
+    __libc_shared_globals()->tls_modules.module_count = 1;
+    __libc_shared_globals()->tls_modules.module_table = &module;
+  } else {
+    layout.reserve_exe_segment_and_tcb(nullptr, progname);
+  }
+
   layout.finish_layout();
 }
 
@@ -111,7 +126,7 @@
   __libc_init_globals();
   __libc_shared_globals()->init_progname = args.argv[0];
   __libc_init_AT_SECURE(args.envp);
-  layout_static_tls();
+  layout_static_tls(args);
   __libc_init_main_thread_final();
   __libc_init_common();
 
diff --git a/libc/private/bionic_elf_tls.h b/libc/private/bionic_elf_tls.h
index c67c221..e95560b 100644
--- a/libc/private/bionic_elf_tls.h
+++ b/libc/private/bionic_elf_tls.h
@@ -29,6 +29,8 @@
 #pragma once
 
 #include <link.h>
+#include <pthread.h>
+#include <stdatomic.h>
 #include <stdint.h>
 #include <sys/cdefs.h>
 
@@ -79,3 +81,37 @@
 
   size_t round_up_with_overflow_check(size_t value, size_t alignment);
 };
+
+// A descriptor for a single ELF TLS module.
+struct TlsModule {
+  TlsSegment segment;
+
+  // Offset into the static TLS block or SIZE_MAX for a dynamic module.
+  size_t static_offset = SIZE_MAX;
+
+  // The generation in which this module was loaded. Dynamic TLS lookups use
+  // this field to detect when a module has been unloaded.
+  size_t first_generation = 0;
+
+  // Used by the dynamic linker to track the associated soinfo* object.
+  void* soinfo_ptr = nullptr;
+};
+
+// Table of the ELF TLS modules. Either the dynamic linker or the static
+// initialization code prepares this table, and it's then used during thread
+// creation and for dynamic TLS lookups.
+struct TlsModules {
+  constexpr TlsModules() {}
+
+  // A generation counter. The value is incremented each time an solib is loaded
+  // or unloaded.
+  _Atomic(size_t) generation = 0;
+
+  // Access to the TlsModule[] table requires taking this lock.
+  pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
+
+  // Pointer to a block of TlsModule objects. The first module has ID 1 and
+  // is stored at index 0 in this table.
+  size_t module_count = 0;
+  TlsModule* module_table = nullptr;
+};
diff --git a/libc/private/bionic_globals.h b/libc/private/bionic_globals.h
index b5e677e..4d40476 100644
--- a/libc/private/bionic_globals.h
+++ b/libc/private/bionic_globals.h
@@ -69,6 +69,7 @@
   abort_msg_t* abort_msg = nullptr;
 
   StaticTlsLayout static_tls_layout;
+  TlsModules tls_modules;
 
   // Values passed from the linker to libc.so.
   const char* init_progname = nullptr;