1 //===-- sanitizer_tls_get_addr.cc -----------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // Handle the __tls_get_addr call.
10 //===----------------------------------------------------------------------===//
12 #include "sanitizer_tls_get_addr.h"
14 #include "sanitizer_flags.h"
15 #include "sanitizer_platform_interceptors.h"
17 namespace __sanitizer
{
18 #if SANITIZER_INTERCEPT_TLS_GET_ADDR
20 // The actual parameter that comes to __tls_get_addr
21 // is a pointer to a struct with two words in it:
22 struct TlsGetAddrParam
{
27 // Glibc starting from 2.19 allocates tls using __signal_safe_memalign,
28 // which has such header.
29 struct Glibc_2_19_tls_header
{
34 // This must be static TLS
35 __attribute__((tls_model("initial-exec")))
36 static __thread DTLS dtls
;
38 // Make sure we properly destroy the DTLS objects:
39 // this counter should never get too large.
40 static atomic_uintptr_t number_of_live_dtls
;
42 static const uptr kDestroyedThread
= -1;
44 static inline void DTLS_Deallocate(DTLS::DTV
*dtv
, uptr size
) {
46 VReport(2, "__tls_get_addr: DTLS_Deallocate %p %zd\n", dtv
, size
);
47 UnmapOrDie(dtv
, size
* sizeof(DTLS::DTV
));
48 atomic_fetch_sub(&number_of_live_dtls
, 1, memory_order_relaxed
);
51 static inline void DTLS_Resize(uptr new_size
) {
52 if (dtls
.dtv_size
>= new_size
) return;
53 new_size
= RoundUpToPowerOfTwo(new_size
);
54 new_size
= Max(new_size
, 4096UL / sizeof(DTLS::DTV
));
56 (DTLS::DTV
*)MmapOrDie(new_size
* sizeof(DTLS::DTV
), "DTLS_Resize");
58 atomic_fetch_add(&number_of_live_dtls
, 1, memory_order_relaxed
);
59 VReport(2, "__tls_get_addr: DTLS_Resize %p %zd\n", &dtls
, num_live_dtls
);
60 CHECK_LT(num_live_dtls
, 1 << 20);
61 uptr old_dtv_size
= dtls
.dtv_size
;
62 DTLS::DTV
*old_dtv
= dtls
.dtv
;
64 internal_memcpy(new_dtv
, dtls
.dtv
, dtls
.dtv_size
* sizeof(DTLS::DTV
));
66 dtls
.dtv_size
= new_size
;
68 DTLS_Deallocate(old_dtv
, old_dtv_size
);
72 if (!common_flags()->intercept_tls_get_addr
) return;
73 VReport(2, "__tls_get_addr: DTLS_Destroy %p %zd\n", &dtls
, dtls
.dtv_size
);
74 uptr s
= dtls
.dtv_size
;
75 dtls
.dtv_size
= kDestroyedThread
; // Do this before unmap for AS-safety.
76 DTLS_Deallocate(dtls
.dtv
, s
);
79 #if defined(__powerpc64__) || defined(__mips__)
80 // This is glibc's TLS_DTV_OFFSET:
81 // "Dynamic thread vector pointers point 0x8000 past the start of each
83 static const uptr kDtvOffset
= 0x8000;
85 static const uptr kDtvOffset
= 0;
88 DTLS::DTV
*DTLS_on_tls_get_addr(void *arg_void
, void *res
,
89 uptr static_tls_begin
, uptr static_tls_end
) {
90 if (!common_flags()->intercept_tls_get_addr
) return 0;
91 TlsGetAddrParam
*arg
= reinterpret_cast<TlsGetAddrParam
*>(arg_void
);
92 uptr dso_id
= arg
->dso_id
;
93 if (dtls
.dtv_size
== kDestroyedThread
) return 0;
94 DTLS_Resize(dso_id
+ 1);
95 if (dtls
.dtv
[dso_id
].beg
) return 0;
97 uptr tls_beg
= reinterpret_cast<uptr
>(res
) - arg
->offset
- kDtvOffset
;
98 VReport(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p "
99 "num_live_dtls %zd\n",
100 arg
, arg
->dso_id
, arg
->offset
, res
, tls_beg
, &tls_beg
,
101 atomic_load(&number_of_live_dtls
, memory_order_relaxed
));
102 if (dtls
.last_memalign_ptr
== tls_beg
) {
103 tls_size
= dtls
.last_memalign_size
;
104 VReport(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n",
106 } else if (tls_beg
>= static_tls_begin
&& tls_beg
< static_tls_end
) {
107 // This is the static TLS block which was initialized / unpoisoned at thread
109 VReport(2, "__tls_get_addr: static tls: %p\n", tls_beg
);
111 } else if ((tls_beg
% 4096) == sizeof(Glibc_2_19_tls_header
)) {
112 // We may want to check gnu_get_libc_version().
113 Glibc_2_19_tls_header
*header
= (Glibc_2_19_tls_header
*)tls_beg
- 1;
114 tls_size
= header
->size
;
115 tls_beg
= header
->start
;
116 VReport(2, "__tls_get_addr: glibc >=2.19 suspected; tls={%p %p}\n",
119 VReport(2, "__tls_get_addr: Can't guess glibc version\n");
120 // This may happen inside the DTOR of main thread, so just ignore it.
123 dtls
.dtv
[dso_id
].beg
= tls_beg
;
124 dtls
.dtv
[dso_id
].size
= tls_size
;
125 return dtls
.dtv
+ dso_id
;
128 void DTLS_on_libc_memalign(void *ptr
, uptr size
) {
129 if (!common_flags()->intercept_tls_get_addr
) return;
130 VReport(2, "DTLS_on_libc_memalign: %p %p\n", ptr
, size
);
131 dtls
.last_memalign_ptr
= reinterpret_cast<uptr
>(ptr
);
132 dtls
.last_memalign_size
= size
;
135 DTLS
*DTLS_Get() { return &dtls
; }
137 bool DTLSInDestruction(DTLS
*dtls
) {
138 return dtls
->dtv_size
== kDestroyedThread
;
142 void DTLS_on_libc_memalign(void *ptr
, uptr size
) {}
143 DTLS::DTV
*DTLS_on_tls_get_addr(void *arg
, void *res
) { return 0; }
144 DTLS
*DTLS_Get() { return 0; }
145 void DTLS_Destroy() {}
146 bool DTLSInDestruction(DTLS
*dtls
) {
147 UNREACHABLE("dtls is unsupported on this platform!");
150 #endif // SANITIZER_INTERCEPT_TLS_GET_ADDR
152 } // namespace __sanitizer