1 // SPDX-License-Identifier: LGPL-2.1 2 /* 3 * rseq.c 4 * 5 * Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com> 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; only 10 * version 2.1 of the License. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 */ 17 18 #define _GNU_SOURCE 19 #include <errno.h> 20 #include <sched.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <unistd.h> 25 #include <syscall.h> 26 #include <assert.h> 27 #include <signal.h> 28 #include <limits.h> 29 #include <dlfcn.h> 30 #include <stddef.h> 31 32 #include "../kselftest.h" 33 #include "rseq.h" 34 35 static const ptrdiff_t *libc_rseq_offset_p; 36 static const unsigned int *libc_rseq_size_p; 37 static const unsigned int *libc_rseq_flags_p; 38 39 /* Offset from the thread pointer to the rseq area. */ 40 ptrdiff_t rseq_offset; 41 42 /* Size of the registered rseq area. 0 if the registration was 43 unsuccessful. */ 44 unsigned int rseq_size = -1U; 45 46 /* Flags used during rseq registration. */ 47 unsigned int rseq_flags; 48 49 static int rseq_ownership; 50 51 static 52 __thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"))) = { 53 .cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED, 54 }; 55 56 static int sys_rseq(struct rseq_abi *rseq_abi, uint32_t rseq_len, 57 int flags, uint32_t sig) 58 { 59 return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); 60 } 61 62 int rseq_available(void) 63 { 64 int rc; 65 66 rc = sys_rseq(NULL, 0, 0, 0); 67 if (rc != -1) 68 abort(); 69 switch (errno) { 70 case ENOSYS: 71 return 0; 72 case EINVAL: 73 return 1; 74 default: 75 abort(); 76 } 77 } 78 79 int rseq_register_current_thread(void) 80 { 81 int rc; 82 83 if (!rseq_ownership) { 84 /* Treat libc's ownership as a successful registration. */ 85 return 0; 86 } 87 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), 0, RSEQ_SIG); 88 if (rc) 89 return -1; 90 assert(rseq_current_cpu_raw() >= 0); 91 return 0; 92 } 93 94 int rseq_unregister_current_thread(void) 95 { 96 int rc; 97 98 if (!rseq_ownership) { 99 /* Treat libc's ownership as a successful unregistration. */ 100 return 0; 101 } 102 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); 103 if (rc) 104 return -1; 105 return 0; 106 } 107 108 static __attribute__((constructor)) 109 void rseq_init(void) 110 { 111 libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset"); 112 libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size"); 113 libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags"); 114 if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p && 115 *libc_rseq_size_p != 0) { 116 /* rseq registration owned by glibc */ 117 rseq_offset = *libc_rseq_offset_p; 118 rseq_size = *libc_rseq_size_p; 119 rseq_flags = *libc_rseq_flags_p; 120 return; 121 } 122 if (!rseq_available()) 123 return; 124 rseq_ownership = 1; 125 rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer(); 126 rseq_size = sizeof(struct rseq_abi); 127 rseq_flags = 0; 128 } 129 130 static __attribute__((destructor)) 131 void rseq_exit(void) 132 { 133 if (!rseq_ownership) 134 return; 135 rseq_offset = 0; 136 rseq_size = -1U; 137 rseq_ownership = 0; 138 } 139 140 int32_t rseq_fallback_current_cpu(void) 141 { 142 int32_t cpu; 143 144 cpu = sched_getcpu(); 145 if (cpu < 0) { 146 perror("sched_getcpu()"); 147 abort(); 148 } 149 return cpu; 150 } 151