1 //===-- sanitizer_tls_get_addr.cpp ----------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // Handle the __tls_get_addr call.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_tls_get_addr.h"
15 #include "sanitizer_flags.h"
16 #include "sanitizer_platform_interceptors.h"
18 namespace __sanitizer
{
19 #if SANITIZER_INTERCEPT_TLS_GET_ADDR
21 // The actual parameter that comes to __tls_get_addr
22 // is a pointer to a struct with two words in it:
23 struct TlsGetAddrParam
{
28 // Glibc starting from 2.19 allocates tls using __signal_safe_memalign,
29 // which has such header.
30 struct Glibc_2_19_tls_header
{
35 // This must be static TLS
36 __attribute__((tls_model("initial-exec")))
37 static __thread DTLS dtls
;
39 // Make sure we properly destroy the DTLS objects:
40 // this counter should never get too large.
41 static atomic_uintptr_t number_of_live_dtls
;
43 static const uptr kDestroyedThread
= -1;
45 static inline void DTLS_Deallocate(DTLS::DTV
*dtv
, uptr size
) {
47 VReport(2, "__tls_get_addr: DTLS_Deallocate %p %zd\n", dtv
, size
);
48 UnmapOrDie(dtv
, size
* sizeof(DTLS::DTV
));
49 atomic_fetch_sub(&number_of_live_dtls
, 1, memory_order_relaxed
);
52 static inline void DTLS_Resize(uptr new_size
) {
53 if (dtls
.dtv_size
>= new_size
) return;
54 new_size
= RoundUpToPowerOfTwo(new_size
);
55 new_size
= Max(new_size
, 4096UL / sizeof(DTLS::DTV
));
57 (DTLS::DTV
*)MmapOrDie(new_size
* sizeof(DTLS::DTV
), "DTLS_Resize");
59 atomic_fetch_add(&number_of_live_dtls
, 1, memory_order_relaxed
);
60 VReport(2, "__tls_get_addr: DTLS_Resize %p %zd\n", &dtls
, num_live_dtls
);
61 CHECK_LT(num_live_dtls
, 1 << 20);
62 uptr old_dtv_size
= dtls
.dtv_size
;
63 DTLS::DTV
*old_dtv
= dtls
.dtv
;
65 internal_memcpy(new_dtv
, dtls
.dtv
, dtls
.dtv_size
* sizeof(DTLS::DTV
));
67 dtls
.dtv_size
= new_size
;
69 DTLS_Deallocate(old_dtv
, old_dtv_size
);
73 if (!common_flags()->intercept_tls_get_addr
) return;
74 VReport(2, "__tls_get_addr: DTLS_Destroy %p %zd\n", &dtls
, dtls
.dtv_size
);
75 uptr s
= dtls
.dtv_size
;
76 dtls
.dtv_size
= kDestroyedThread
; // Do this before unmap for AS-safety.
77 DTLS_Deallocate(dtls
.dtv
, s
);
80 #if defined(__powerpc64__) || defined(__mips__)
81 // This is glibc's TLS_DTV_OFFSET:
82 // "Dynamic thread vector pointers point 0x8000 past the start of each
84 static const uptr kDtvOffset
= 0x8000;
86 static const uptr kDtvOffset
= 0;
89 DTLS::DTV
*DTLS_on_tls_get_addr(void *arg_void
, void *res
,
90 uptr static_tls_begin
, uptr static_tls_end
) {
91 if (!common_flags()->intercept_tls_get_addr
) return 0;
92 TlsGetAddrParam
*arg
= reinterpret_cast<TlsGetAddrParam
*>(arg_void
);
93 uptr dso_id
= arg
->dso_id
;
94 if (dtls
.dtv_size
== kDestroyedThread
) return 0;
95 DTLS_Resize(dso_id
+ 1);
96 if (dtls
.dtv
[dso_id
].beg
) return 0;
98 uptr tls_beg
= reinterpret_cast<uptr
>(res
) - arg
->offset
- kDtvOffset
;
99 VReport(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p "
100 "num_live_dtls %zd\n",
101 arg
, arg
->dso_id
, arg
->offset
, res
, tls_beg
, &tls_beg
,
102 atomic_load(&number_of_live_dtls
, memory_order_relaxed
));
103 if (dtls
.last_memalign_ptr
== tls_beg
) {
104 tls_size
= dtls
.last_memalign_size
;
105 VReport(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n",
107 } else if (tls_beg
>= static_tls_begin
&& tls_beg
< static_tls_end
) {
108 // This is the static TLS block which was initialized / unpoisoned at thread
110 VReport(2, "__tls_get_addr: static tls: %p\n", tls_beg
);
112 } else if ((tls_beg
% 4096) == sizeof(Glibc_2_19_tls_header
)) {
113 // We may want to check gnu_get_libc_version().
114 Glibc_2_19_tls_header
*header
= (Glibc_2_19_tls_header
*)tls_beg
- 1;
115 tls_size
= header
->size
;
116 tls_beg
= header
->start
;
117 VReport(2, "__tls_get_addr: glibc >=2.19 suspected; tls={%p %p}\n",
120 VReport(2, "__tls_get_addr: Can't guess glibc version\n");
121 // This may happen inside the DTOR of main thread, so just ignore it.
124 dtls
.dtv
[dso_id
].beg
= tls_beg
;
125 dtls
.dtv
[dso_id
].size
= tls_size
;
126 return dtls
.dtv
+ dso_id
;
129 void DTLS_on_libc_memalign(void *ptr
, uptr size
) {
130 if (!common_flags()->intercept_tls_get_addr
) return;
131 VReport(2, "DTLS_on_libc_memalign: %p %p\n", ptr
, size
);
132 dtls
.last_memalign_ptr
= reinterpret_cast<uptr
>(ptr
);
133 dtls
.last_memalign_size
= size
;
136 DTLS
*DTLS_Get() { return &dtls
; }
138 bool DTLSInDestruction(DTLS
*dtls
) {
139 return dtls
->dtv_size
== kDestroyedThread
;
143 void DTLS_on_libc_memalign(void *ptr
, uptr size
) {}
144 DTLS::DTV
*DTLS_on_tls_get_addr(void *arg
, void *res
,
145 unsigned long, unsigned long) { return 0; }
146 DTLS
*DTLS_Get() { return 0; }
147 void DTLS_Destroy() {}
148 bool DTLSInDestruction(DTLS
*dtls
) {
149 UNREACHABLE("dtls is unsupported on this platform!");
152 #endif // SANITIZER_INTERCEPT_TLS_GET_ADDR
154 } // namespace __sanitizer