]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
1 | //===-- sanitizer_tls_get_addr.cc -----------------------------------------===// |
2 | // | |
3 | // The LLVM Compiler Infrastructure | |
4 | // | |
5 | // This file is distributed under the University of Illinois Open Source | |
6 | // License. See LICENSE.TXT for details. | |
7 | // | |
8 | //===----------------------------------------------------------------------===// | |
9 | // | |
10 | // Handle the __tls_get_addr call. | |
11 | // | |
12 | //===----------------------------------------------------------------------===// | |
13 | ||
14 | #include "sanitizer_tls_get_addr.h" | |
15 | ||
16 | #include "sanitizer_flags.h" | |
17 | #include "sanitizer_platform_interceptors.h" | |
18 | ||
19 | namespace __sanitizer { | |
20 | #if SANITIZER_INTERCEPT_TLS_GET_ADDR | |
21 | ||
22 | // The actual parameter that comes to __tls_get_addr | |
23 | // is a pointer to a struct with two words in it: | |
24 | struct TlsGetAddrParam { | |
25 | uptr dso_id; | |
26 | uptr offset; | |
27 | }; | |
28 | ||
29 | // Glibc starting from 2.19 allocates tls using __signal_safe_memalign, | |
30 | // which has such header. | |
31 | struct Glibc_2_19_tls_header { | |
32 | uptr size; | |
33 | uptr start; | |
34 | }; | |
35 | ||
36 | // This must be static TLS | |
37 | __attribute__((tls_model("initial-exec"))) | |
38 | static __thread DTLS dtls; | |
39 | ||
40 | // Make sure we properly destroy the DTLS objects: | |
41 | // this counter should never get too large. | |
42 | static atomic_uintptr_t number_of_live_dtls; | |
43 | ||
44 | static const uptr kDestroyedThread = -1; | |
45 | ||
46 | static inline void DTLS_Deallocate(DTLS::DTV *dtv, uptr size) { | |
47 | if (!size) return; | |
48 | VPrintf(2, "__tls_get_addr: DTLS_Deallocate %p %zd\n", dtv, size); | |
49 | UnmapOrDie(dtv, size * sizeof(DTLS::DTV)); | |
50 | atomic_fetch_sub(&number_of_live_dtls, 1, memory_order_relaxed); | |
51 | } | |
52 | ||
53 | static inline void DTLS_Resize(uptr new_size) { | |
54 | if (dtls.dtv_size >= new_size) return; | |
55 | new_size = RoundUpToPowerOfTwo(new_size); | |
56 | new_size = Max(new_size, 4096UL / sizeof(DTLS::DTV)); | |
57 | DTLS::DTV *new_dtv = | |
58 | (DTLS::DTV *)MmapOrDie(new_size * sizeof(DTLS::DTV), "DTLS_Resize"); | |
59 | uptr num_live_dtls = | |
60 | atomic_fetch_add(&number_of_live_dtls, 1, memory_order_relaxed); | |
61 | VPrintf(2, "__tls_get_addr: DTLS_Resize %p %zd\n", &dtls, num_live_dtls); | |
62 | CHECK_LT(num_live_dtls, 1 << 20); | |
63 | uptr old_dtv_size = dtls.dtv_size; | |
64 | DTLS::DTV *old_dtv = dtls.dtv; | |
65 | if (old_dtv_size) | |
66 | internal_memcpy(new_dtv, dtls.dtv, dtls.dtv_size * sizeof(DTLS::DTV)); | |
67 | dtls.dtv = new_dtv; | |
68 | dtls.dtv_size = new_size; | |
69 | if (old_dtv_size) | |
70 | DTLS_Deallocate(old_dtv, old_dtv_size); | |
71 | } | |
72 | ||
73 | void DTLS_Destroy() { | |
74 | if (!common_flags()->intercept_tls_get_addr) return; | |
75 | VPrintf(2, "__tls_get_addr: DTLS_Destroy %p %zd\n", &dtls, dtls.dtv_size); | |
76 | uptr s = dtls.dtv_size; | |
77 | dtls.dtv_size = kDestroyedThread; // Do this before unmap for AS-safety. | |
78 | DTLS_Deallocate(dtls.dtv, s); | |
79 | } | |
80 | ||
5bcae85e | 81 | #if defined(__powerpc64__) || defined(__mips__) |
3157f602 XL |
82 | // This is glibc's TLS_DTV_OFFSET: |
83 | // "Dynamic thread vector pointers point 0x8000 past the start of each | |
84 | // TLS block." | |
85 | static const uptr kDtvOffset = 0x8000; | |
86 | #else | |
87 | static const uptr kDtvOffset = 0; | |
88 | #endif | |
89 | ||
92a42be0 SL |
90 | DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, |
91 | uptr static_tls_begin, uptr static_tls_end) { | |
92 | if (!common_flags()->intercept_tls_get_addr) return 0; | |
1a4d82fc JJ |
93 | TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void); |
94 | uptr dso_id = arg->dso_id; | |
92a42be0 | 95 | if (dtls.dtv_size == kDestroyedThread) return 0; |
1a4d82fc | 96 | DTLS_Resize(dso_id + 1); |
92a42be0 | 97 | if (dtls.dtv[dso_id].beg) return 0; |
1a4d82fc | 98 | uptr tls_size = 0; |
3157f602 | 99 | uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset; |
1a4d82fc JJ |
100 | VPrintf(2, "__tls_get_addr: %p {%p,%p} => %p; tls_beg: %p; sp: %p " |
101 | "num_live_dtls %zd\n", | |
102 | arg, arg->dso_id, arg->offset, res, tls_beg, &tls_beg, | |
103 | atomic_load(&number_of_live_dtls, memory_order_relaxed)); | |
104 | if (dtls.last_memalign_ptr == tls_beg) { | |
105 | tls_size = dtls.last_memalign_size; | |
106 | VPrintf(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n", | |
107 | tls_beg, tls_size); | |
92a42be0 SL |
108 | } else if (tls_beg >= static_tls_begin && tls_beg < static_tls_end) { |
109 | // This is the static TLS block which was initialized / unpoisoned at thread | |
110 | // creation. | |
111 | VPrintf(2, "__tls_get_addr: static tls: %p\n", tls_beg); | |
112 | tls_size = 0; | |
1a4d82fc JJ |
113 | } else if ((tls_beg % 4096) == sizeof(Glibc_2_19_tls_header)) { |
114 | // We may want to check gnu_get_libc_version(). | |
115 | Glibc_2_19_tls_header *header = (Glibc_2_19_tls_header *)tls_beg - 1; | |
116 | tls_size = header->size; | |
117 | tls_beg = header->start; | |
118 | VPrintf(2, "__tls_get_addr: glibc >=2.19 suspected; tls={%p %p}\n", | |
119 | tls_beg, tls_size); | |
120 | } else { | |
121 | VPrintf(2, "__tls_get_addr: Can't guess glibc version\n"); | |
122 | // This may happen inside the DTOR of main thread, so just ignore it. | |
123 | tls_size = 0; | |
124 | } | |
125 | dtls.dtv[dso_id].beg = tls_beg; | |
126 | dtls.dtv[dso_id].size = tls_size; | |
92a42be0 | 127 | return dtls.dtv + dso_id; |
1a4d82fc JJ |
128 | } |
129 | ||
130 | void DTLS_on_libc_memalign(void *ptr, uptr size) { | |
131 | if (!common_flags()->intercept_tls_get_addr) return; | |
132 | VPrintf(2, "DTLS_on_libc_memalign: %p %p\n", ptr, size); | |
133 | dtls.last_memalign_ptr = reinterpret_cast<uptr>(ptr); | |
134 | dtls.last_memalign_size = size; | |
135 | } | |
136 | ||
137 | DTLS *DTLS_Get() { return &dtls; } | |
138 | ||
139 | #else | |
140 | void DTLS_on_libc_memalign(void *ptr, uptr size) {} | |
92a42be0 | 141 | DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res) { return 0; } |
1a4d82fc JJ |
142 | DTLS *DTLS_Get() { return 0; } |
143 | void DTLS_Destroy() {} | |
144 | #endif // SANITIZER_INTERCEPT_TLS_GET_ADDR | |
145 | ||
146 | } // namespace __sanitizer |