1 | //===-- sanitizer_tls_get_addr.h --------------------------------*- C++ -*-===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Handle the __tls_get_addr call. |
10 | // |
11 | // All this magic is specific to glibc and is required to workaround |
12 | // the lack of interface that would tell us about the Dynamic TLS (DTLS). |
13 | // https://sourceware.org/bugzilla/show_bug.cgi?id=16291 |
14 | // |
15 | // Before 2.25: every DTLS chunk is allocated with __libc_memalign, |
16 | // which we intercept and thus know where is the DTLS. |
17 | // |
18 | // Since 2.25: DTLS chunks are allocated with malloc. We could co-opt |
19 | // the malloc interceptor to keep track of the last allocation, similar |
20 | // to how we handle __libc_memalign; however, this adds some overhead |
21 | // (since malloc, unlike __libc_memalign, is commonly called), and |
22 | // requires care to avoid false negatives for LeakSanitizer. |
23 | // Instead, we rely on our internal allocators - which keep track of all |
24 | // its allocations - to determine if an address points to a malloc |
25 | // allocation. |
26 | // |
27 | // There exists a since-deprecated version of Google's internal glibc fork |
28 | // that used __signal_safe_memalign. DTLS_on_tls_get_addr relied on a |
29 | // heuristic check (is the allocation 16 bytes from the start of a page |
30 | // boundary?), which was sometimes erroneous: |
31 | // https://bugs.chromium.org/p/chromium/issues/detail?id=1275223#c15 |
32 | // Since that check has no practical use anymore, we have removed it. |
33 | // |
34 | //===----------------------------------------------------------------------===// |
35 | |
36 | #ifndef SANITIZER_TLS_GET_ADDR_H |
37 | #define SANITIZER_TLS_GET_ADDR_H |
38 | |
39 | #include "sanitizer_atomic.h" |
40 | #include "sanitizer_common.h" |
41 | |
42 | namespace __sanitizer { |
43 | |
44 | struct DTLS { |
45 | // Array of DTLS chunks for the current Thread. |
46 | // If beg == 0, the chunk is unused. |
47 | struct DTV { |
48 | uptr beg, size; |
49 | }; |
50 | struct DTVBlock { |
51 | atomic_uintptr_t next; |
52 | DTV dtvs[(4096UL - sizeof(next)) / sizeof(DTLS::DTV)]; |
53 | }; |
54 | |
55 | static_assert(sizeof(DTVBlock) <= 4096UL, "Unexpected block size" ); |
56 | |
57 | atomic_uintptr_t dtv_block; |
58 | |
59 | // Auxiliary fields, don't access them outside sanitizer_tls_get_addr.cpp |
60 | uptr last_memalign_size; |
61 | uptr last_memalign_ptr; |
62 | }; |
63 | |
64 | template <typename Fn> |
65 | void ForEachDVT(DTLS *dtls, const Fn &fn) { |
66 | DTLS::DTVBlock *block = |
67 | (DTLS::DTVBlock *)atomic_load(a: &dtls->dtv_block, mo: memory_order_acquire); |
68 | while (block) { |
69 | int id = 0; |
70 | for (auto &d : block->dtvs) fn(d, id++); |
71 | block = (DTLS::DTVBlock *)atomic_load(a: &block->next, mo: memory_order_acquire); |
72 | } |
73 | } |
74 | |
75 | // Returns pointer and size of a linker-allocated TLS block. |
76 | // Each block is returned exactly once. |
77 | DTLS::DTV *DTLS_on_tls_get_addr(void *arg, void *res, uptr static_tls_begin, |
78 | uptr static_tls_end); |
79 | void DTLS_on_libc_memalign(void *ptr, uptr size); |
80 | DTLS *DTLS_Get(); |
81 | void DTLS_Destroy(); // Make sure to call this before the thread is destroyed. |
82 | // Returns true if DTLS of suspended thread is in destruction process. |
83 | bool DTLSInDestruction(DTLS *dtls); |
84 | |
85 | } // namespace __sanitizer |
86 | |
87 | #endif // SANITIZER_TLS_GET_ADDR_H |
88 | |