1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #pragma once
6
7 #include <zircon/compiler.h>
8 #include <zircon/syscalls.h>
9 #include <zircon/syscalls/object.h>
10
11 __BEGIN_CDECLS
12
13 #pragma GCC visibility push(hidden)
14
15 // Get and set the thread pointer.
16 static inline void* zxr_tp_get(void);
17 static inline void zxr_tp_set(zx_handle_t self, void* tp);
18
19 #if defined(__aarch64__)
20
zxr_tp_get(void)21 __NO_SAFESTACK static inline void* zxr_tp_get(void) {
22 // This just emits "mrs %[reg], tpidr_el0", but the compiler
23 // knows what exactly it's doing (unlike an asm). So it can
24 // e.g. CSE it with another implicit thread-pointer fetch it
25 // generated for its own reasons.
26 return __builtin_thread_pointer();
27 }
28
zxr_tp_set(zx_handle_t self,void * tp)29 __NO_SAFESTACK static inline void zxr_tp_set(zx_handle_t self, void* tp) {
30 __asm__ volatile("msr tpidr_el0, %0"
31 :
32 : "r"(tp));
33 }
34
35 #elif defined(__x86_64__)
36
zxr_tp_get(void)37 __NO_SAFESTACK static inline void* zxr_tp_get(void) {
38 // This fetches %fs:0, but the compiler knows what it's doing.
39 // LLVM knows that in the Fuchsia ABI %fs:0 always stores the
40 // %fs.base address, and its optimizer will see through this
41 // to integrate *(zxr_tp_get() + N) as a direct "mov %fs:N, ...".
42 // Note that these special pointer types can be used to access
43 // memory, but they cannot be cast to a normal pointer type
44 // (which in the abstract should add in the base address,
45 // but the compiler doesn't know how to do that).
46 #ifdef __clang__
47 // Clang does it via magic address_space numbers (256 is %gs).
48 void* __attribute__((address_space(257)))* fs = 0;
49 // TODO(mcgrathr): GCC 6 supports this syntax instead (and __seg_gs):
50 // void* __seg_fs* fs = 0;
51 // Unfortunately, it allows it only in C and not in C++.
52 // It also requires -fasm under -std=c11 (et al), see:
53 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79609
54 // It's also buggy for the special case of 0, see:
55 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79619
56 return *fs;
57 #else
58 void* tp;
59 __asm__ __volatile__("mov %%fs:0,%0"
60 : "=r"(tp));
61 return tp;
62 #endif
63 }
64
zxr_tp_set(zx_handle_t self,void * tp)65 __NO_SAFESTACK static inline void zxr_tp_set(zx_handle_t self, void* tp) {
66 zx_status_t status = _zx_object_set_property(
67 self, ZX_PROP_REGISTER_FS, (uintptr_t*)&tp, sizeof(uintptr_t));
68 if (status != ZX_OK)
69 __builtin_trap();
70 }
71
72 #else
73
74 #error Unsupported architecture
75
76 #endif
77
78 #pragma GCC visibility pop
79
80 __END_CDECLS
81