]>
Commit | Line | Data |
---|---|---|
dee5ea7a KS |
1 | //===-- sanitizer_tls_get_addr.cc -----------------------------------------===// |
2 | // | |
3 | // This file is distributed under the University of Illinois Open Source | |
4 | // License. See LICENSE.TXT for details. | |
5 | // | |
6 | //===----------------------------------------------------------------------===// | |
7 | // | |
8 | // Handle the __tls_get_addr call. | |
9 | // | |
10 | //===----------------------------------------------------------------------===// | |
11 | ||
12 | #include "sanitizer_tls_get_addr.h" | |
13 | ||
14 | #include "sanitizer_flags.h" | |
15 | #include "sanitizer_platform_interceptors.h" | |
16 | ||
17 | namespace __sanitizer { | |
18 | #if SANITIZER_INTERCEPT_TLS_GET_ADDR | |
19 | ||
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 { | |
23 | uptr dso_id; | |
24 | uptr offset; | |
25 | }; | |
26 | ||
27 | // Glibc starting from 2.19 allocates tls using __signal_safe_memalign, | |
28 | // which has such header. | |
29 | struct Glibc_2_19_tls_header { | |
30 | uptr size; | |
31 | uptr start; | |
32 | }; | |
33 | ||
34 | // This must be static TLS | |
35 | __attribute__((tls_model("initial-exec"))) | |
36 | static __thread DTLS dtls; | |
37 | ||
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; | |
41 | ||
42 | static const uptr kDestroyedThread = -1; | |
43 | ||
44 | static inline void DTLS_Deallocate(DTLS::DTV *dtv, uptr size) { | |
45 | if (!size) return; | |
46 | VPrintf(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); | |
49 | } | |
50 | ||
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)); | |
55 | DTLS::DTV *new_dtv = | |
56 | (DTLS::DTV *)MmapOrDie(new_size * sizeof(DTLS::DTV), "DTLS_Resize"); | |
57 | uptr num_live_dtls = | |
58 | atomic_fetch_add(&number_of_live_dtls, 1, memory_order_relaxed); | |
59 | VPrintf(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; | |
63 | if (old_dtv_size) | |
64 | internal_memcpy(new_dtv, dtls.dtv, dtls.dtv_size * sizeof(DTLS::DTV)); | |
65 | dtls.dtv = new_dtv; | |
66 | dtls.dtv_size = new_size; | |
67 | if (old_dtv_size) | |
68 | DTLS_Deallocate(old_dtv, old_dtv_size); | |
69 | } | |
70 | ||
71 | void DTLS_Destroy() { | |
72 | if (!common_flags()->intercept_tls_get_addr) return; | |
73 | VPrintf(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); | |
77 | } | |
78 | ||
55aea9f5 MO |
79 | #if defined(__powerpc64__) |
80 | // This is glibc's TLS_DTV_OFFSET: | |
81 | // "Dynamic thread vector pointers point 0x8000 past the start of each | |
82 | // TLS block." | |
83 | static const uptr kDtvOffset = 0x8000; | |
84 | #else | |
85 | static const uptr kDtvOffset = 0; | |
86 | #endif | |
87 | ||
696d846a MO |
88 | DTLS::DTV *DTLS_on_tls_get_addr(void *arg_void, void *res, |
89 | uptr static_tls_begin, uptr static_tls_end) { | |
866e32ad | 90 | if (!common_flags()->intercept_tls_get_addr) return 0; |
dee5ea7a KS |
91 | TlsGetAddrParam *arg = reinterpret_cast<TlsGetAddrParam *>(arg_void); |
92 | uptr dso_id = arg->dso_id; | |
866e32ad | 93 | if (dtls.dtv_size == kDestroyedThread) return 0; |
dee5ea7a | 94 | DTLS_Resize(dso_id + 1); |
866e32ad | 95 | if (dtls.dtv[dso_id].beg) return 0; |
dee5ea7a | 96 | uptr tls_size = 0; |
55aea9f5 | 97 | uptr tls_beg = reinterpret_cast<uptr>(res) - arg->offset - kDtvOffset; |
dee5ea7a KS |
98 | VPrintf(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 | VPrintf(2, "__tls_get_addr: glibc <=2.18 suspected; tls={%p,%p}\n", | |
105 | tls_beg, tls_size); | |
696d846a MO |
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 | |
108 | // creation. | |
109 | VPrintf(2, "__tls_get_addr: static tls: %p\n", tls_beg); | |
110 | tls_size = 0; | |
dee5ea7a KS |
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 | VPrintf(2, "__tls_get_addr: glibc >=2.19 suspected; tls={%p %p}\n", | |
117 | tls_beg, tls_size); | |
118 | } else { | |
119 | VPrintf(2, "__tls_get_addr: Can't guess glibc version\n"); | |
120 | // This may happen inside the DTOR of main thread, so just ignore it. | |
121 | tls_size = 0; | |
122 | } | |
123 | dtls.dtv[dso_id].beg = tls_beg; | |
124 | dtls.dtv[dso_id].size = tls_size; | |
866e32ad | 125 | return dtls.dtv + dso_id; |
dee5ea7a KS |
126 | } |
127 | ||
128 | void DTLS_on_libc_memalign(void *ptr, uptr size) { | |
129 | if (!common_flags()->intercept_tls_get_addr) return; | |
130 | VPrintf(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; | |
133 | } | |
134 | ||
135 | DTLS *DTLS_Get() { return &dtls; } | |
136 | ||
137 | #else | |
138 | void DTLS_on_libc_memalign(void *ptr, uptr size) {} | |
866e32ad | 139 | DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res) { return 0; } |
dee5ea7a KS |
140 | DTLS *DTLS_Get() { return 0; } |
141 | void DTLS_Destroy() {} | |
142 | #endif // SANITIZER_INTERCEPT_TLS_GET_ADDR | |
143 | ||
144 | } // namespace __sanitizer |