3
0
mirror of https://github.com/Qortal/Brooklyn.git synced 2025-01-31 15:22:18 +00:00
Brooklyn/tools/testing/selftests/rseq/rseq.c

151 lines
3.3 KiB
C
Raw Normal View History

2022-04-02 13:24:21 +00:00
// SPDX-License-Identifier: LGPL-2.1
/*
* rseq.c
*
* Copyright (C) 2016 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; only
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
#define _GNU_SOURCE
#include <errno.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syscall.h>
#include <assert.h>
#include <signal.h>
#include <limits.h>
#include <dlfcn.h>
#include <stddef.h>
2022-04-02 13:24:21 +00:00
#include "../kselftest.h"
#include "rseq.h"
static const ptrdiff_t *libc_rseq_offset_p;
static const unsigned int *libc_rseq_size_p;
static const unsigned int *libc_rseq_flags_p;
2022-04-02 13:24:21 +00:00
/* Offset from the thread pointer to the rseq area. */
ptrdiff_t rseq_offset;
2022-04-02 13:24:21 +00:00
/* Size of the registered rseq area. 0 if the registration was
unsuccessful. */
unsigned int rseq_size = -1U;
2022-04-02 13:24:21 +00:00
/* Flags used during rseq registration. */
unsigned int rseq_flags;
2022-04-02 13:24:21 +00:00
static int rseq_ownership;
2022-04-02 13:24:21 +00:00
static
__thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"))) = {
.cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED,
};
2022-04-02 13:24:21 +00:00
static int sys_rseq(struct rseq_abi *rseq_abi, uint32_t rseq_len,
2022-04-02 13:24:21 +00:00
int flags, uint32_t sig)
{
return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig);
}
int rseq_available(void)
2022-04-02 13:24:21 +00:00
{
int rc;
2022-04-02 13:24:21 +00:00
rc = sys_rseq(NULL, 0, 0, 0);
if (rc != -1)
abort();
switch (errno) {
case ENOSYS:
2022-04-02 13:24:21 +00:00
return 0;
case EINVAL:
return 1;
default:
abort();
2022-04-02 13:24:21 +00:00
}
}
int rseq_register_current_thread(void)
2022-04-02 13:24:21 +00:00
{
int rc;
2022-04-02 13:24:21 +00:00
if (!rseq_ownership) {
/* Treat libc's ownership as a successful registration. */
2022-04-02 13:24:21 +00:00
return 0;
}
rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), 0, RSEQ_SIG);
if (rc)
return -1;
assert(rseq_current_cpu_raw() >= 0);
return 0;
2022-04-02 13:24:21 +00:00
}
int rseq_unregister_current_thread(void)
2022-04-02 13:24:21 +00:00
{
int rc;
2022-04-02 13:24:21 +00:00
if (!rseq_ownership) {
/* Treat libc's ownership as a successful unregistration. */
return 0;
2022-04-02 13:24:21 +00:00
}
rc = sys_rseq(&__rseq_abi, sizeof(struct rseq_abi), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG);
if (rc)
return -1;
return 0;
2022-04-02 13:24:21 +00:00
}
static __attribute__((constructor))
void rseq_init(void)
2022-04-02 13:24:21 +00:00
{
libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset");
libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size");
libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags");
2022-09-09 09:21:57 +00:00
if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p &&
*libc_rseq_size_p != 0) {
/* rseq registration owned by glibc */
rseq_offset = *libc_rseq_offset_p;
rseq_size = *libc_rseq_size_p;
rseq_flags = *libc_rseq_flags_p;
return;
}
if (!rseq_available())
2022-04-02 13:24:21 +00:00
return;
rseq_ownership = 1;
rseq_offset = (void *)&__rseq_abi - rseq_thread_pointer();
rseq_size = sizeof(struct rseq_abi);
rseq_flags = 0;
2022-04-02 13:24:21 +00:00
}
static __attribute__((destructor))
void rseq_exit(void)
2022-04-02 13:24:21 +00:00
{
if (!rseq_ownership)
return;
rseq_offset = 0;
rseq_size = -1U;
2022-04-02 13:24:21 +00:00
rseq_ownership = 0;
}
int32_t rseq_fallback_current_cpu(void)
{
int32_t cpu;
cpu = sched_getcpu();
if (cpu < 0) {
perror("sched_getcpu()");
abort();
}
return cpu;
}