rseq.c (3331B)
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 35static const ptrdiff_t *libc_rseq_offset_p; 36static const unsigned int *libc_rseq_size_p; 37static const unsigned int *libc_rseq_flags_p; 38 39/* Offset from the thread pointer to the rseq area. */ 40ptrdiff_t rseq_offset; 41 42/* Size of the registered rseq area. 0 if the registration was 43 unsuccessful. */ 44unsigned int rseq_size = -1U; 45 46/* Flags used during rseq registration. */ 47unsigned int rseq_flags; 48 49static int rseq_ownership; 50 51static 52__thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"))) = { 53 .cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED, 54}; 55 56static 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 62int 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 79int 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 94int 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 108static __attribute__((constructor)) 109void 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 /* rseq registration owned by glibc */ 116 rseq_offset = *libc_rseq_offset_p; 117 rseq_size = *libc_rseq_size_p; 118 rseq_flags = *libc_rseq_flags_p; 119 return; 120 } 121 if (!rseq_available()) 122 return; 123 rseq_ownership = 1; 124 rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer(); 125 rseq_size = sizeof(struct rseq_abi); 126 rseq_flags = 0; 127} 128 129static __attribute__((destructor)) 130void rseq_exit(void) 131{ 132 if (!rseq_ownership) 133 return; 134 rseq_offset = 0; 135 rseq_size = -1U; 136 rseq_ownership = 0; 137} 138 139int32_t rseq_fallback_current_cpu(void) 140{ 141 int32_t cpu; 142 143 cpu = sched_getcpu(); 144 if (cpu < 0) { 145 perror("sched_getcpu()"); 146 abort(); 147 } 148 return cpu; 149}