a48f0e0f6f2c45a7588806bff4a4123d23a72fed
[librseq.git] / src / rseq.c
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
30 #include <rseq/rseq.h>
31
32 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
33
34 __attribute__((weak)) __thread
35 volatile struct rseq __rseq_abi = {
36 .cpu_id = RSEQ_CPU_ID_UNINITIALIZED,
37 };
38
39 __attribute__((weak)) __thread
40 volatile uint32_t __rseq_refcount;
41
42 static int sys_rseq(volatile struct rseq *rseq_abi, uint32_t rseq_len,
43 int flags, uint32_t sig)
44 {
45 return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
46 }
47
48 int rseq_available(void)
49 {
50 int rc;
51
52 rc = sys_rseq(NULL, 0, 0, 0);
53 if (rc != -1)
54 abort();
55 switch (errno) {
56 case ENOSYS:
57 return 0;
58 case EINVAL:
59 return 1;
60 default:
61 abort();
62 }
63 }
64
65 static void signal_off_save(sigset_t *oldset)
66 {
67 sigset_t set;
68 int ret;
69
70 sigfillset(&set);
71 ret = pthread_sigmask(SIG_BLOCK, &set, oldset);
72 if (ret)
73 abort();
74 }
75
76 static void signal_restore(sigset_t oldset)
77 {
78 int ret;
79
80 ret = pthread_sigmask(SIG_SETMASK, &oldset, NULL);
81 if (ret)
82 abort();
83 }
84
85 int rseq_register_current_thread(void)
86 {
87 int rc, ret = 0;
88 sigset_t oldset;
89
90 signal_off_save(&oldset);
91 if (__rseq_refcount == UINT_MAX) {
92 ret = -1;
93 goto end;
94 }
95 if (__rseq_refcount++)
96 goto end;
97 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq), 0, RSEQ_SIG);
98 if (!rc) {
99 assert(rseq_current_cpu_raw() >= 0);
100 goto end;
101 }
102 if (errno != EBUSY)
103 __rseq_abi.cpu_id = RSEQ_CPU_ID_REGISTRATION_FAILED;
104 ret = -1;
105 __rseq_refcount--;
106 end:
107 signal_restore(oldset);
108 return ret;
109 }
110
111 int rseq_unregister_current_thread(void)
112 {
113 int rc, ret = 0;
114 sigset_t oldset;
115
116 signal_off_save(&oldset);
117 if (!__rseq_refcount) {
118 ret = -1;
119 goto end;
120 }
121 if (--__rseq_refcount)
122 goto end;
123 rc = sys_rseq(&__rseq_abi, sizeof(struct rseq),
124 RSEQ_FLAG_UNREGISTER, RSEQ_SIG);
125 if (!rc)
126 goto end;
127 ret = -1;
128 end:
129 signal_restore(oldset);
130 return ret;
131 }
132
133 int32_t rseq_fallback_current_cpu(void)
134 {
135 int32_t cpu;
136
137 cpu = sched_getcpu();
138 if (cpu < 0) {
139 perror("sched_getcpu()");
140 abort();
141 }
142 return cpu;
143 }
This page took 0.033519 seconds and 3 git commands to generate.