partition_tls.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_TLS_H_
  5. #define BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_TLS_H_
  6. #include "base/allocator/partition_allocator/partition_alloc_base/compiler_specific.h"
  7. #include "base/allocator/partition_allocator/partition_alloc_base/component_export.h"
  8. #include "base/allocator/partition_allocator/partition_alloc_base/immediate_crash.h"
  9. #include "base/allocator/partition_allocator/partition_alloc_check.h"
  10. #include "build/build_config.h"
  11. #if BUILDFLAG(IS_POSIX)
  12. #include <pthread.h>
  13. #endif
  14. #if BUILDFLAG(IS_WIN)
  15. #include "base/allocator/partition_allocator/partition_alloc_base/win/windows_types.h"
  16. #endif
  17. // Barebones TLS implementation for use in PartitionAlloc. This doesn't use the
  18. // general chromium TLS handling to avoid dependencies, but more importantly
  19. // because it allocates memory.
  20. namespace partition_alloc::internal {
  21. #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
  22. using PartitionTlsKey = pthread_key_t;
  23. // Only on x86_64, the implementation is not stable on ARM64. For instance, in
  24. // macOS 11, the TPIDRRO_EL0 registers holds the CPU index in the low bits,
  25. // which is not the case in macOS 12. See libsyscall/os/tsd.h in XNU
  26. // (_os_tsd_get_direct() is used by pthread_getspecific() internally).
  27. #if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64)
  28. namespace {
  29. PA_ALWAYS_INLINE void* FastTlsGet(PartitionTlsKey index) {
  30. // On macOS, pthread_getspecific() is in libSystem, so a call to it has to go
  31. // through PLT. However, and contrary to some other platforms, *all* TLS keys
  32. // are in a static array in the thread structure. So they are *always* at a
  33. // fixed offset from the segment register holding the thread structure
  34. // address.
  35. //
  36. // We could use _pthread_getspecific_direct(), but it is not
  37. // exported. However, on all macOS versions we support, the TLS array is at
  38. // %gs. This is used in V8 to back up InternalGetExistingThreadLocal(), and
  39. // can also be seen by looking at pthread_getspecific() disassembly:
  40. //
  41. // libsystem_pthread.dylib`pthread_getspecific:
  42. // libsystem_pthread.dylib[0x7ff800316099] <+0>: movq %gs:(,%rdi,8), %rax
  43. // libsystem_pthread.dylib[0x7ff8003160a2] <+9>: retq
  44. //
  45. // This function is essentially inlining the content of pthread_getspecific()
  46. // here.
  47. intptr_t result;
  48. static_assert(sizeof index <= sizeof(intptr_t));
  49. asm("movq %%gs:(,%1,8), %0;"
  50. : "=r"(result)
  51. : "r"(static_cast<intptr_t>(index)));
  52. return reinterpret_cast<void*>(result);
  53. }
  54. } // namespace
  55. #endif // BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64)
  56. PA_ALWAYS_INLINE bool PartitionTlsCreate(PartitionTlsKey* key,
  57. void (*destructor)(void*)) {
  58. return !pthread_key_create(key, destructor);
  59. }
  60. PA_ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) {
  61. #if BUILDFLAG(IS_MAC) && defined(ARCH_CPU_X86_64)
  62. PA_DCHECK(pthread_getspecific(key) == FastTlsGet(key));
  63. return FastTlsGet(key);
  64. #else
  65. return pthread_getspecific(key);
  66. #endif
  67. }
  68. PA_ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) {
  69. int ret = pthread_setspecific(key, value);
  70. PA_DCHECK(!ret);
  71. }
  72. #elif BUILDFLAG(IS_WIN)
  73. // Note: supports only a single TLS key on Windows. Not a hard constraint, may
  74. // be lifted.
  75. using PartitionTlsKey = unsigned long;
  76. PA_COMPONENT_EXPORT(PARTITION_ALLOC)
  77. bool PartitionTlsCreate(PartitionTlsKey* key, void (*destructor)(void*));
  78. PA_ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) {
  79. // Accessing TLS resets the last error, which then makes |GetLastError()|
  80. // return something misleading. While this means that properly using
  81. // |GetLastError()| is difficult, there is currently code in Chromium which
  82. // expects malloc() to *not* reset it. Meaning that we either have to fix this
  83. // code, or pay the cost of saving/restoring it.
  84. //
  85. // Source:
  86. // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-tlsgetvalue
  87. // "Functions that return indications of failure call SetLastError() when they
  88. // fail. They generally do not call SetLastError() when they succeed. The
  89. // TlsGetValue() function is an exception to this general rule. The
  90. // TlsGetValue() function calls SetLastError() to clear a thread's last error
  91. // when it succeeds."
  92. DWORD saved_error = GetLastError();
  93. void* ret = TlsGetValue(key);
  94. // Only non-zero errors need to be restored.
  95. if (PA_UNLIKELY(saved_error))
  96. SetLastError(saved_error);
  97. return ret;
  98. }
  99. PA_ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) {
  100. BOOL ret = TlsSetValue(key, value);
  101. PA_DCHECK(ret);
  102. }
  103. // Registers a callback for DLL_PROCESS_DETACH events.
  104. void PartitionTlsSetOnDllProcessDetach(void (*callback)());
  105. #else
  106. // Not supported.
  107. using PartitionTlsKey = int;
  108. PA_ALWAYS_INLINE bool PartitionTlsCreate(PartitionTlsKey* key,
  109. void (*destructor)(void*)) {
  110. // NOTIMPLEMENTED() may allocate, crash instead.
  111. PA_IMMEDIATE_CRASH();
  112. }
  113. PA_ALWAYS_INLINE void* PartitionTlsGet(PartitionTlsKey key) {
  114. PA_IMMEDIATE_CRASH();
  115. }
  116. PA_ALWAYS_INLINE void PartitionTlsSet(PartitionTlsKey key, void* value) {
  117. PA_IMMEDIATE_CRASH();
  118. }
  119. #endif // BUILDFLAG(IS_WIN)
  120. } // namespace partition_alloc::internal
  121. #endif // BASE_ALLOCATOR_PARTITION_ALLOCATOR_PARTITION_TLS_H_