1. /**
    
  2.  * Copyright (c) Meta Platforms, Inc. and affiliates.
    
  3.  *
    
  4.  * This source code is licensed under the MIT license found in the
    
  5.  * LICENSE file in the root directory of this source tree.
    
  6.  */
    
  7. 
    
  8. #include "thread-local.h"
    
  9. 
    
  10. #ifdef __linux__
    
  11. #include <link.h>
    
  12. #include <asm/prctl.h>
    
  13. #include <sys/prctl.h>
    
  14. extern "C" {
    
  15. extern int arch_prctl(int, unsigned long*);
    
  16. }
    
  17. #endif //__linux__
    
  18. 
    
  19. namespace HPHP {
    
  20. 
    
  21. #ifdef USE_GCC_FAST_TLS
    
  22. 
    
  23. void ThreadLocalManager::OnThreadExit(void* p) {
    
  24.   auto list = getList(p);
    
  25.   p = list->head;
    
  26.   delete list;
    
  27.   while (p != nullptr) {
    
  28.     auto* pNode = static_cast<ThreadLocalNode<void>*>(p);
    
  29.     if (pNode->m_on_thread_exit_fn) {
    
  30.       pNode->m_on_thread_exit_fn(p);
    
  31.     }
    
  32.     p = pNode->m_next;
    
  33.   }
    
  34. }
    
  35. 
    
  36. void ThreadLocalManager::PushTop(void* nodePtr, size_t nodeSize) {
    
  37.   auto& node = *static_cast<ThreadLocalNode<void>*>(nodePtr);
    
  38.   auto key = GetManager().m_key;
    
  39.   auto list = getList(pthread_getspecific(key));
    
  40.   if (UNLIKELY(!list)) {
    
  41.     ThreadLocalSetValue(key, list = new ThreadLocalList);
    
  42.   }
    
  43.   node.m_next = list->head;
    
  44.   node.m_size = nodeSize;
    
  45.   list->head = node.m_next;
    
  46. }
    
  47. 
    
  48. ThreadLocalManager& ThreadLocalManager::GetManager() {
    
  49.   static ThreadLocalManager m;
    
  50.   return m;
    
  51. }
    
  52. 
    
  53. #ifdef __APPLE__
    
  54. ThreadLocalManager::ThreadLocalList::ThreadLocalList() {
    
  55.   pthread_t self = pthread_self();
    
  56.   handler.__routine = ThreadLocalManager::OnThreadExit;
    
  57.   handler.__arg = this;
    
  58.   handler.__next = self->__cleanup_stack;
    
  59.   self->__cleanup_stack = &handler;
    
  60. }
    
  61. #endif
    
  62. 
    
  63. #endif
    
  64. 
    
  65. #ifdef __linux__
    
  66. 
    
  67. static int visit_phdr(dl_phdr_info* info, size_t, void*) {
    
  68.   for (size_t i = 0, n = info->dlpi_phnum; i < n; ++i) {
    
  69.     const auto& hdr = info->dlpi_phdr[i];
    
  70.     auto addr = info->dlpi_addr + hdr.p_vaddr;
    
  71.     if (addr < 0x100000000LL && hdr.p_type == PT_TLS) {
    
  72.       // found the main thread-local section
    
  73.       assert(int(hdr.p_memsz) == hdr.p_memsz); // ensure no truncation
    
  74.       return hdr.p_memsz;
    
  75.     }
    
  76.   }
    
  77.   return 0;
    
  78. }
    
  79. 
    
  80. std::pair<void*,size_t> getCppTdata() {
    
  81.   uintptr_t addr;
    
  82.   if (!arch_prctl(ARCH_GET_FS, &addr)) {
    
  83.     // fs points to the end of the threadlocal area.
    
  84.     size_t size = dl_iterate_phdr(&visit_phdr, nullptr);
    
  85.     return {(void*)(addr - size), size};
    
  86.   }
    
  87.   return {nullptr, 0};
    
  88. }
    
  89. 
    
  90. #else
    
  91. 
    
  92. // how do you find the thread local section on your system?
    
  93. std::pair<void*,size_t> getCppTdata() {
    
  94.   return {nullptr, 0};
    
  95. }
    
  96. 
    
  97. #endif //__linux__
    
  98. 
    
  99. }