├── .gitignore ├── .gitreview ├── LICENSE ├── LICENSES ├── BSD-2-Clause.txt ├── FSFAP.txt ├── GPL-2.0-or-later.txt ├── GPL-3.0-or-later.txt ├── LGPL-2.1-or-later.txt ├── LicenseRef-Autoconf-exception-macro.txt ├── Linux-man-pages-copyleft.txt ├── Linux-syscall-note.txt └── MIT.txt ├── Makefile.am ├── README.md ├── bootstrap ├── configure.ac ├── doc ├── Makefile.am └── man │ ├── Makefile.am │ └── rseq.2 ├── include ├── Makefile.am └── rseq │ ├── abi.h │ ├── arch.h │ ├── arch │ ├── aarch64.h │ ├── aarch64 │ │ └── bits.h │ ├── arm.h │ ├── arm │ │ └── bits.h │ ├── generic │ │ ├── common.h │ │ └── thread-pointer.h │ ├── loongarch.h │ ├── loongarch │ │ ├── bits.h │ │ └── thread-pointer.h │ ├── mips.h │ ├── mips │ │ └── bits.h │ ├── ppc.h │ ├── ppc │ │ ├── bits.h │ │ └── thread-pointer.h │ ├── riscv.h │ ├── riscv │ │ ├── bits.h │ │ └── thread-pointer.h │ ├── s390.h │ ├── s390 │ │ └── bits.h │ ├── templates │ │ ├── bits-reset.h │ │ └── bits.h │ ├── x86.h │ └── x86 │ │ ├── bits.h │ │ └── thread-pointer.h │ ├── compiler.h │ ├── inject.h │ ├── mempool.h │ ├── pseudocode.h │ ├── rseq.h │ ├── thread-pointer.h │ └── utils.h ├── m4 ├── ae_config_feature.m4 ├── ax_append_compile_flags.m4 ├── ax_append_flag.m4 ├── ax_c___attribute__.m4 ├── ax_check_compile_flag.m4 ├── ax_cxx_compile_stdcxx.m4 ├── ax_pthread.m4 ├── ax_require_defined.m4 └── pprint.m4 ├── src ├── Makefile.am ├── librseq.pc.in ├── list.h ├── rseq-mempool.c ├── rseq-utils.h ├── rseq.c ├── smp.c └── smp.h └── tests ├── Makefile.am ├── basic_percpu_benchmark.c ├── basic_percpu_benchmark_cxx.cpp ├── basic_percpu_ops_test.c ├── basic_percpu_ops_test_cxx.cpp ├── basic_test.c ├── basic_test_cxx.cpp ├── disable-rseq-syscall.c ├── fork_test.c ├── fork_test_cxx.cpp ├── mempool_cow_race_test.c ├── mempool_cow_race_test_cxx.cpp ├── mempool_test.c ├── mempool_test_cxx.cpp ├── no_syscall_test.c ├── no_syscall_test_cxx.cpp ├── param_test.c ├── param_test_cxx.cpp ├── run_fork_test.tap ├── run_fork_test_cxx.tap ├── run_no_syscall_test.tap ├── run_no_syscall_test_cxx.tap ├── run_param_test.tap ├── run_param_test_cxx.tap ├── run_syscall_errors_test.tap ├── run_syscall_errors_test_cxx.tap ├── run_unregistered_test.tap ├── run_unregistered_test_cxx.tap ├── syscall_errors_test.c ├── syscall_errors_test_cxx.cpp ├── unit ├── .gitignore ├── Makefile.am ├── arch-mo-cxx.cpp └── arch-mo.c ├── unregistered_test.c ├── unregistered_test_cxx.cpp └── utils ├── Makefile.am ├── tap-driver.sh ├── tap.c ├── tap.h ├── tap.sh └── utils.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | # Prerequisites 6 | *.d 7 | 8 | # Object files 9 | *.o 10 | *.ko 11 | *.obj 12 | *.elf 13 | 14 | # Linker output 15 | *.ilk 16 | *.map 17 | *.exp 18 | 19 | # Precompiled Headers 20 | *.gch 21 | *.pch 22 | 23 | # Libraries 24 | *.lib 25 | *.a 26 | *.la 27 | *.lo 28 | 29 | # Shared objects (inc. Windows DLLs) 30 | *.dll 31 | *.so 32 | *.so.* 33 | *.dylib 34 | 35 | # Executables 36 | *.exe 37 | *.out 38 | *.app 39 | *.i*86 40 | *.x86_64 41 | *.hex 42 | 43 | # Debug files 44 | *.dSYM/ 45 | *.su 46 | *.idb 47 | *.pdb 48 | 49 | # Kernel Module Compile Results 50 | *.mod* 51 | *.cmd 52 | .tmp_versions/ 53 | modules.order 54 | Module.symvers 55 | Mkfile.old 56 | dkms.conf 57 | 58 | /src/librseq.pc 59 | /tests/basic_percpu_ops_test.tap 60 | /tests/basic_percpu_ops_test_cxx.tap 61 | /tests/basic_percpu_ops_mm_cid_test.tap 62 | /tests/basic_percpu_ops_mm_cid_test_cxx.tap 63 | /tests/basic_percpu_benchmark.tap 64 | /tests/basic_percpu_benchmark_cxx.tap 65 | /tests/basic_percpu_mm_cid_benchmark.tap 66 | /tests/basic_percpu_mm_cid_benchmark_cxx.tap 67 | /tests/basic_test.tap 68 | /tests/basic_test_cxx.tap 69 | /tests/fork_test.tap 70 | /tests/fork_test_cxx.tap 71 | /tests/mempool_cow_race_test.tap 72 | /tests/mempool_cow_race_test_cxx.tap 73 | /tests/mempool_test.tap 74 | /tests/mempool_test_cxx.tap 75 | /tests/param_test 76 | /tests/param_test_cxx 77 | /tests/param_test_benchmark 78 | /tests/param_test_benchmark_cxx 79 | /tests/param_test_compare_twice 80 | /tests/param_test_compare_twice_cxx 81 | /tests/param_test_mm_cid 82 | /tests/param_test_mm_cid_cxx 83 | /tests/param_test_mm_cid_benchmark 84 | /tests/param_test_mm_cid_benchmark_cxx 85 | /tests/param_test_mm_cid_compare_twice 86 | /tests/param_test_mm_cid_compare_twice_cxx 87 | /tests/no_syscall_test_cxx.tap 88 | /tests/no_syscall_test.tap 89 | /tests/syscall_errors_test_cxx.tap 90 | /tests/syscall_errors_test.tap 91 | /tests/unregistered_test_cxx.tap 92 | /tests/unregistered_test.tap 93 | 94 | #automake 95 | /include/config.h 96 | .deps/ 97 | .libs/ 98 | Makefile.in 99 | Makefile 100 | *.la 101 | *.bz2 102 | *.o 103 | *.lo 104 | *.loT 105 | *.log 106 | *.trs 107 | /config.log 108 | /configure 109 | /config/compile 110 | /config/config.guess 111 | /config/config.sub 112 | /config/depcomp 113 | /config/install-sh 114 | /config/ltmain.sh 115 | /config/missing 116 | /config/test-driver 117 | /aclocal.m4 118 | /m4/libtool.m4 119 | /m4/lt~obsolete.m4 120 | /m4/ltoptions.m4 121 | /m4/ltsugar.m4 122 | /m4/ltversion.m4 123 | /libtool 124 | /include/stamp-h1 125 | /include/config.h.in 126 | /include/config.h.in~ 127 | /config.status 128 | /autom4te.cache/ 129 | /config 130 | *~ 131 | .*.swp 132 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | [gerrit] 6 | host=review.lttng.org 7 | port=29418 8 | project=librseq.git 9 | defaultbranch=master 10 | defaultremote=review 11 | defaultrebase=0 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This library is provided under: 2 | 3 | SPDX-License-Identifier: MIT 4 | 5 | According with: 6 | 7 | LICENSES/MIT.txt 8 | 9 | 10 | The C TAP test libraries are provided under the terms of the BSD 2-Clause 11 | "Simplified" License: 12 | 13 | SPDX-License-Identifier: BSD-2-Clause 14 | 15 | According with: 16 | 17 | LICENSES/BSD-2-Clause.txt 18 | 19 | They are only used when running the tests in the source tree. This applies 20 | to: 21 | 22 | tests/utils/tap.h 23 | tests/utils/tap.c 24 | 25 | 26 | The BASH TAP library is provided under the terms of the GNU General Public 27 | License v3.0 or later: 28 | 29 | SPDX-License-Identifier: GPL-3.0-or-later 30 | 31 | According with: 32 | 33 | LICENSES/GPL-3.0-or-later.txt 34 | 35 | It's only used when running the tests in the source tree. This applies to: 36 | 37 | tests/utils/tap.sh 38 | 39 | 40 | In addition, other licenses may also apply. See individual files SPDX License 41 | Identifiers for details. 42 | -------------------------------------------------------------------------------- /LICENSES/BSD-2-Clause.txt: -------------------------------------------------------------------------------- 1 | Valid-License-Identifier: BSD-2-Clause 2 | SPDX-URL: https://spdx.org/licenses/BSD-2-Clause.html 3 | Usage-Guide: 4 | To use the BSD 2-clause "Simplified" License put the following SPDX 5 | tag/value pair into a comment according to the placement guidelines in 6 | the licensing rules documentation: 7 | SPDX-License-Identifier: BSD-2-Clause 8 | License-Text: 9 | 10 | Copyright (c) . All rights reserved. 11 | 12 | Redistribution and use in source and binary forms, with or without 13 | modification, are permitted provided that the following conditions are met: 14 | 15 | 1. Redistributions of source code must retain the above copyright notice, 16 | this list of conditions and the following disclaimer. 17 | 18 | 2. Redistributions in binary form must reproduce the above copyright 19 | notice, this list of conditions and the following disclaimer in the 20 | documentation and/or other materials provided with the distribution. 21 | 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 23 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 26 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 29 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 30 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /LICENSES/FSFAP.txt: -------------------------------------------------------------------------------- 1 | Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright notice and this notice are preserved. This file is offered as-is, without any warranty. 2 | -------------------------------------------------------------------------------- /LICENSES/LicenseRef-Autoconf-exception-macro.txt: -------------------------------------------------------------------------------- 1 | As a special exception, the respective Autoconf Macro's copyright owner 2 | gives unlimited permission to copy, distribute and modify the configure 3 | scripts that are the output of Autoconf when processing the Macro. You 4 | need not follow the terms of the GNU General Public License when using 5 | or distributing such scripts, even though portions of the text of the 6 | Macro appear in them. The GNU General Public License (GPL) does govern 7 | all other use of the material that constitutes the Autoconf Macro. 8 | 9 | This special exception to the GPL applies to versions of the Autoconf 10 | Macro released by the Autoconf Archive. When you make and distribute a 11 | modified version of the Autoconf Macro, you may extend this special 12 | exception to the GPL to apply to your modified version as well. 13 | -------------------------------------------------------------------------------- /LICENSES/Linux-man-pages-copyleft.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) All rights reserved. 2 | 3 | Permission is granted to make and distribute verbatim copies of this 4 | manual provided the copyright notice and this permission notice are 5 | preserved on all copies. 6 | 7 | Permission is granted to copy and distribute modified versions of 8 | this manual under the conditions for verbatim copying, provided that 9 | the entire resulting derived work is distributed under the terms of 10 | a permission notice identical to this one. 11 | 12 | Since the Linux kernel and libraries are constantly changing, this 13 | manual page may be incorrect or out-of-date. The author(s) assume 14 | no responsibility for errors or omissions, or for damages resulting 15 | from the use of the information contained herein. The author(s) may 16 | not have taken the same level of care in the production of this 17 | manual, which is licensed free of charge, as they might when working 18 | professionally. 19 | 20 | Formatted or processed versions of this manual, if unaccompanied by 21 | the source, must acknowledge the copyright and authors of this work. 22 | -------------------------------------------------------------------------------- /LICENSES/Linux-syscall-note.txt: -------------------------------------------------------------------------------- 1 | NOTE! This copyright does *not* cover user programs that use kernel 2 | services by normal system calls - this is merely considered normal use 3 | of the kernel, and does *not* fall under the heading of "derived work". 4 | Also note that the GPL below is copyrighted by the Free Software 5 | Foundation, but the instance of code that it refers to (the Linux 6 | kernel) is copyrighted by me and others who actually wrote it. 7 | 8 | Also note that the only valid version of the GPL as far as the kernel 9 | is concerned is _this_ particular version of the license (ie v2, not 10 | v2.2 or v3.x or whatever), unless explicitly otherwise stated. 11 | 12 | Linus Torvalds 13 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | Valid-License-Identifier: MIT 2 | SPDX-URL: https://spdx.org/licenses/MIT.html 3 | Usage-Guide: 4 | To use the MIT License put the following SPDX tag/value pair into a 5 | comment according to the placement guidelines in the licensing rules 6 | documentation: 7 | SPDX-License-Identifier: MIT 8 | License-Text: 9 | 10 | MIT License 11 | 12 | Copyright (c) 13 | 14 | Permission is hereby granted, free of charge, to any person obtaining a 15 | copy of this software and associated documentation files (the "Software"), 16 | to deal in the Software without restriction, including without limitation 17 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 18 | and/or sell copies of the Software, and to permit persons to whom the 19 | Software is furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall be included in 22 | all copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 29 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 30 | DEALINGS IN THE SOFTWARE. 31 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 3 | 4 | ACLOCAL_AMFLAGS = -I m4 5 | 6 | SUBDIRS = \ 7 | doc \ 8 | include \ 9 | src \ 10 | tests 11 | 12 | dist_doc_DATA = README.md 13 | 14 | EXTRA_DIST = \ 15 | LICENSE \ 16 | LICENSES/LicenseRef-Autoconf-exception-macro.txt \ 17 | LICENSES/BSD-2-Clause.txt \ 18 | LICENSES/FSFAP.txt \ 19 | LICENSES/GPL-2.0-or-later.txt \ 20 | LICENSES/GPL-3.0-or-later.txt \ 21 | LICENSES/LGPL-2.1-or-later.txt \ 22 | LICENSES/Linux-syscall-note.txt \ 23 | LICENSES/MIT.txt 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | Library for Restartable Sequences 8 | ================================= 9 | 10 | by Mathieu Desnoyers 11 | 12 | 13 | Required and optional dependencies 14 | ---------------------------------- 15 | 16 | The following dependencies are optional: 17 | 18 | - [libnuma](https://github.com/numactl/numactl) 19 | To build without this dependency run `./configure` with `--disable-numa` 20 | - [libseccomp](https://github.com/seccomp/libseccomp/) 21 | 22 | Building 23 | -------- 24 | 25 | ### Prerequisites 26 | 27 | This source tree is based on the Autotools suite from GNU to simplify 28 | portability. Here are some things you should have on your system in order to 29 | compile the Git repository tree: 30 | 31 | - [GNU Autotools](http://www.gnu.org/software/autoconf/) 32 | (**Automake >= 1.12**, **Autoconf >= 2.69**, 33 | **Autoheader >= 2.69**; 34 | make sure your system-wide `automake` points to a recent version!) 35 | - **[GNU Libtool](https://www.gnu.org/software/libtool/) >= 2.2** 36 | - **[GNU Make](https://www.gnu.org/software/make/)** 37 | - **[pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config)** 38 | - **Linux kernel headers** from kernel **>= 4.18** to build on x86, arm, 39 | ppc, and mips and from kernel **>= 4.19** to build on s390. 40 | 41 | 42 | ### Building steps 43 | 44 | If you get the tree from the Git repository, you will need to run 45 | 46 | ./bootstrap 47 | 48 | in its root. It calls all the GNU tools needed to prepare the tree 49 | configuration. 50 | 51 | To build and install, do: 52 | 53 | ./configure 54 | make 55 | sudo make install 56 | sudo ldconfig 57 | 58 | **Note:** the `configure` script sets `/usr/local` as the default prefix for 59 | files it installs. However, this path is not part of most distributions' 60 | default library path, which will cause builds depending on `librseq` 61 | to fail unless `-L/usr/local/lib` is added to `LDFLAGS`. You may provide a 62 | custom prefix to `configure` by using the `--prefix` switch 63 | (e.g., `--prefix=/usr`). 64 | 65 | 66 | ### Building against a local version of the kernel headers 67 | 68 | cd /path/to/kernel/sources 69 | make headers_install 70 | cd /path/to/librseq 71 | CPPFLAGS=-I/path/to/kernel/sources/usr/include ./configure 72 | make 73 | sudo make install 74 | sudo ldconfig 75 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # SPDX-License-Identifier: MIT 4 | # 5 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 6 | 7 | set -x 8 | if [ ! -d "config" ]; then 9 | mkdir config 10 | fi 11 | 12 | autoreconf -vif -W all,error 13 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl SPDX-License-Identifier: MIT 2 | dnl 3 | dnl SPDX-FileCopyrightText: 2021 EfficiOS, Inc. 4 | dnl 5 | dnl Process this file with autoconf to produce a configure script. 6 | 7 | # Project version information 8 | m4_define([rseq_version_major], [0]) 9 | m4_define([rseq_version_minor], [1]) 10 | m4_define([rseq_version_patch], [0]) 11 | m4_define([rseq_version_dev_stage], [-pre]) 12 | m4_define([rseq_version], rseq_version_major[.]rseq_version_minor[.]rseq_version_patch[]rseq_version_dev_stage) 13 | 14 | # Library version information of "librseq" 15 | # Following the numbering scheme proposed by libtool for the library version 16 | # http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html 17 | m4_define([rseq_lib_version_current], [0]) 18 | m4_define([rseq_lib_version_revision], [0]) 19 | m4_define([rseq_lib_version_age], [0]) 20 | m4_define([rseq_lib_version], rseq_lib_version_current[:]rseq_lib_version_revision[:]rseq_lib_version_age) 21 | 22 | 23 | ## ## 24 | ## Autoconf base setup ## 25 | ## ## 26 | 27 | AC_PREREQ([2.69]) 28 | AC_INIT([librseq],[rseq_version],[mathieu dot desnoyers at efficios dot com],[],[https://github.com/compudj/librseq/]) 29 | 30 | AC_CONFIG_HEADERS([include/config.h]) 31 | AC_CONFIG_AUX_DIR([config]) 32 | AC_CONFIG_MACRO_DIR([m4]) 33 | 34 | AC_CANONICAL_TARGET 35 | AC_CANONICAL_HOST 36 | 37 | 38 | ## ## 39 | ## Automake base setup ## 40 | ## ## 41 | 42 | AM_INIT_AUTOMAKE([1.12 foreign dist-bzip2 no-dist-gzip nostdinc -Wall -Werror]) 43 | AM_MAINTAINER_MODE([enable]) 44 | 45 | # Enable silent rules by default 46 | AM_SILENT_RULES([yes]) 47 | 48 | 49 | ## ## 50 | ## C compiler checks ## 51 | ## ## 52 | 53 | # Choose the C compiler 54 | AC_PROG_CC 55 | # AC_PROG_CC_STDC was merged in AC_PROG_CC in autoconf 2.70 56 | m4_version_prereq([2.70], [], [AC_PROG_CC_STDC]) 57 | 58 | # Make sure the C compiler supports C99 59 | AS_IF([test "$ac_cv_prog_cc_c99" = "no"], [AC_MSG_ERROR([The compiler does not support C99])]) 60 | 61 | # Enable available system extensions and LFS support 62 | AC_USE_SYSTEM_EXTENSIONS 63 | AC_SYS_LARGEFILE 64 | 65 | # Make sure the C compiler supports __attribute__ 66 | AX_C___ATTRIBUTE__ 67 | AS_IF([test "x$ax_cv___attribute__" != "xyes"], 68 | [AC_MSG_ERROR([The compiler does not support __attribute__ extensions])]) 69 | 70 | # Make sure we have pthread support 71 | AX_PTHREAD([], [AC_MSG_ERROR([Could not configure pthread support])]) 72 | 73 | # Checks for typedefs, structures, and compiler characteristics. 74 | AC_C_INLINE 75 | AC_C_TYPEOF 76 | AC_TYPE_INT32_T 77 | AC_TYPE_INT64_T 78 | AC_TYPE_OFF_T 79 | AC_TYPE_SIZE_T 80 | AC_TYPE_UINT32_T 81 | AC_TYPE_UINT64_T 82 | 83 | # Detect warning flags supported by the C compiler and append them to 84 | # WARN_CFLAGS. 85 | m4_define([WARN_FLAGS_LIST], [ dnl 86 | -Wall dnl 87 | -Wextra dnl 88 | -Wmissing-prototypes dnl 89 | -Wmissing-declarations dnl 90 | -Wnull-dereference dnl 91 | -Wundef dnl 92 | -Wshadow dnl 93 | -Wjump-misses-init dnl 94 | -Wsuggest-attribute=format dnl 95 | -Wtautological-constant-out-of-range-compare dnl 96 | -Wnested-externs dnl 97 | -Wwrite-strings dnl 98 | -Wformat=2 dnl 99 | -Wstrict-aliasing dnl 100 | -Wmissing-noreturn dnl 101 | -Winit-self dnl 102 | -Wduplicated-cond dnl 103 | -Wduplicated-branches dnl 104 | -Wlogical-op dnl 105 | -Wredundant-decls dnl 106 | ]) 107 | 108 | # Pass -Werror as an extra flag during the test: this is needed to make the 109 | # -Wunknown-warning-option diagnostic fatal with clang. 110 | AC_LANG_PUSH([C]) 111 | AX_APPEND_COMPILE_FLAGS([WARN_FLAGS_LIST], [WARN_CFLAGS], [-Werror]) 112 | AC_LANG_POP([C]) 113 | 114 | AC_LANG_PUSH([C++]) 115 | AX_APPEND_COMPILE_FLAGS([WARN_FLAGS_LIST], [WARN_CXXFLAGS], [-Werror]) 116 | AC_LANG_POP([C++]) 117 | 118 | AE_IF_FEATURE_ENABLED([Werror], [WARN_CFLAGS="${WARN_CFLAGS} -Werror"]) 119 | AE_IF_FEATURE_ENABLED([Werror], [WARN_CXXFLAGS="${WARN_CXXFLAGS} -Werror"]) 120 | 121 | 122 | ## ## 123 | ## C++ compiler checks ## 124 | ## ## 125 | 126 | # Require a C++11 compiler without GNU extensions (-std=c++11) 127 | AX_CXX_COMPILE_STDCXX([11], [noext], [mandatory]) 128 | 129 | 130 | ## ## 131 | ## Header checks ## 132 | ## ## 133 | 134 | AC_HEADER_STDBOOL 135 | AC_CHECK_HEADERS([ \ 136 | limits.h \ 137 | stddef.h \ 138 | sys/time.h \ 139 | ]) 140 | 141 | 142 | ## ## 143 | ## Programs checks ## 144 | ## ## 145 | 146 | AM_PROG_AR 147 | AC_PROG_AWK 148 | AC_PROG_MAKE_SET 149 | 150 | # Initialize and configure libtool 151 | LT_INIT 152 | 153 | 154 | ## ## 155 | ## Library checks ## 156 | ## ## 157 | 158 | # Checks for library functions. 159 | AC_FUNC_MMAP 160 | AC_FUNC_FORK 161 | AC_CHECK_FUNCS([ \ 162 | atexit \ 163 | memset \ 164 | strerror \ 165 | ]) 166 | 167 | # AC_FUNC_MALLOC causes problems when cross-compiling. 168 | #AC_FUNC_MALLOC 169 | 170 | # Check dor dlopen() in -ldl or -lc 171 | AC_CHECK_LIB([dl], [dlopen], [ 172 | libdl_name=dl 173 | DL_LIBS="-ldl" 174 | ], [ 175 | # dlopen not found in libdl, check in libc 176 | AC_CHECK_LIB([c], [dlopen], [ 177 | libdl_name=c 178 | DL_LIBS="-lc" 179 | ], [ 180 | AC_MSG_ERROR([Cannot find dlopen in libdl nor libc. Use [LDFLAGS]=-Ldir to specify their location.]) 181 | ]) 182 | ]) 183 | AC_SUBST(DL_LIBS) 184 | 185 | PKG_CHECK_MODULES([SECCOMP], [libseccomp], 186 | [ 187 | dnl PKG_CHECK_MODULES defines SECCOMP_LIBS 188 | have_seccomp=yes 189 | ], 190 | [ 191 | have_seccomp=no 192 | ]) 193 | 194 | 195 | ## ## 196 | ## Optional features selection ## 197 | ## ## 198 | 199 | # Enabled by default 200 | AE_FEATURE_DEFAULT_ENABLE 201 | AE_FEATURE([numa],[disable NUMA support]) 202 | 203 | # When given, add -Werror to WARN_CFLAGS and WARN_CXXFLAGS. 204 | # Disabled by default 205 | AE_FEATURE_DEFAULT_DISABLE 206 | AE_FEATURE([Werror], [Treat compiler warnings as errors.]) 207 | 208 | ## ## 209 | ## Check for optional features dependencies ## 210 | ## ## 211 | 212 | # The numa integration requires libnuma 213 | AE_IF_FEATURE_ENABLED([numa], [ 214 | AC_CHECK_LIB([numa], [numa_available], [ 215 | AC_DEFINE([HAVE_LIBNUMA], [1], [Define to 1 if libnuma is available.]) 216 | ], [ 217 | AC_MSG_ERROR([dnl 218 | libnuma is not available. Please either install it (e.g. libnuma-dev) or use 219 | [LDFLAGS]=-Ldir to specify the right location, or use --disable-numa configure 220 | argument to disable NUMA support. 221 | ]) 222 | ]) 223 | ]) 224 | 225 | 226 | ## ## 227 | ## Set automake variables for optional feature conditionnals in Makefile.am ## 228 | ## ## 229 | 230 | AM_CONDITIONAL([ENABLE_SHARED], [test "x${enable_shared}" = "xyes"]) 231 | AM_CONDITIONAL([ENABLE_SECCOMP], [test "x${have_seccomp}" = "xyes"]) 232 | AM_CONDITIONAL([ENABLE_NUMA], AE_IS_FEATURE_ENABLED([numa])) 233 | 234 | ## ## 235 | ## Substitute variables for use in Makefile.am ## 236 | ## ## 237 | 238 | # Library versions for libtool 239 | AC_SUBST([RSEQ_LIBRARY_VERSION], [rseq_lib_version]) 240 | 241 | # The order in which the include folders are searched is important. 242 | # The top_builddir should always be searched first in the event that a build 243 | # time generated file is included. 244 | AM_CPPFLAGS="-I\$(top_builddir)/include -I\$(top_srcdir)/include -include config.h" 245 | AC_SUBST(AM_CPPFLAGS) 246 | 247 | AM_CFLAGS="$WARN_CFLAGS $PTHREAD_CFLAGS" 248 | AC_SUBST(AM_CFLAGS) 249 | 250 | AM_CXXFLAGS="$WARN_CXXFLAGS $PTHREAD_CFLAGS" 251 | AC_SUBST(AM_CXXFLAGS) 252 | 253 | 254 | ## ## 255 | ## Output files generated by configure ## 256 | ## ## 257 | 258 | AC_CONFIG_FILES([ 259 | Makefile 260 | doc/Makefile 261 | doc/man/Makefile 262 | include/Makefile 263 | src/Makefile 264 | src/librseq.pc 265 | tests/Makefile 266 | tests/utils/Makefile 267 | tests/unit/Makefile 268 | ]) 269 | 270 | AC_OUTPUT 271 | 272 | 273 | # 274 | # Mini-report on what will be built. 275 | # 276 | 277 | PPRINT_INIT 278 | PPRINT_SET_INDENT(1) 279 | PPRINT_SET_TS(38) 280 | 281 | AS_ECHO 282 | AS_ECHO("${PPRINT_COLOR_BLDBLU}librseq $PACKAGE_VERSION${PPRINT_COLOR_RST}") 283 | AS_ECHO 284 | 285 | PPRINT_SUBTITLE([Features]) 286 | 287 | PPRINT_PROP_STRING([Target architecture], $host_cpu) 288 | AE_IS_FEATURE_ENABLED([numa]) && value=1 || value=0 289 | PPRINT_PROP_BOOL([NUMA], $value) 290 | 291 | report_bindir="`eval eval echo $bindir`" 292 | report_libdir="`eval eval echo $libdir`" 293 | 294 | # Print the bindir and libdir this `make install' will install into. 295 | AS_ECHO 296 | PPRINT_SUBTITLE([Install directories]) 297 | PPRINT_PROP_STRING([Binaries], [$report_bindir]) 298 | PPRINT_PROP_STRING([Libraries], [$report_libdir]) 299 | -------------------------------------------------------------------------------- /doc/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 3 | 4 | SUBDIRS = man 5 | -------------------------------------------------------------------------------- /doc/man/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 3 | 4 | dist_man2_MANS = rseq.2 5 | -------------------------------------------------------------------------------- /include/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: 2022-2024 EfficiOS Inc. 3 | 4 | nobase_include_HEADERS = \ 5 | rseq/arch/aarch64/bits.h \ 6 | rseq/arch/aarch64.h \ 7 | rseq/arch/arm/bits.h \ 8 | rseq/arch/arm.h \ 9 | rseq/arch/generic/common.h \ 10 | rseq/arch/generic/thread-pointer.h \ 11 | rseq/arch/mips/bits.h \ 12 | rseq/arch/mips.h \ 13 | rseq/arch/ppc/bits.h \ 14 | rseq/arch/ppc.h \ 15 | rseq/arch/ppc/thread-pointer.h \ 16 | rseq/arch/riscv/bits.h \ 17 | rseq/arch/riscv.h \ 18 | rseq/arch/riscv/thread-pointer.h \ 19 | rseq/arch/s390/bits.h \ 20 | rseq/arch/s390.h \ 21 | rseq/arch/templates/bits.h \ 22 | rseq/arch/templates/bits-reset.h \ 23 | rseq/arch/x86/bits.h \ 24 | rseq/arch/x86.h \ 25 | rseq/arch/x86/thread-pointer.h \ 26 | rseq/abi.h \ 27 | rseq/arch.h \ 28 | rseq/compiler.h \ 29 | rseq/inject.h \ 30 | rseq/mempool.h \ 31 | rseq/pseudocode.h \ 32 | rseq/rseq.h \ 33 | rseq/thread-pointer.h \ 34 | rseq/utils.h 35 | -------------------------------------------------------------------------------- /include/rseq/abi.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0-or-later WITH Linux-syscall-note */ 2 | /* SPDX-FileCopyrightText: 2015-2022 Mathieu Desnoyers */ 3 | #ifndef _RSEQ_ABI_H 4 | #define _RSEQ_ABI_H 5 | 6 | /* 7 | * rseq/abi.h 8 | * 9 | * Restartable sequences system call ABI 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | enum rseq_abi_cpu_id_state { 16 | RSEQ_ABI_CPU_ID_UNINITIALIZED = -1, 17 | RSEQ_ABI_CPU_ID_REGISTRATION_FAILED = -2, 18 | }; 19 | 20 | enum rseq_abi_flags { 21 | RSEQ_ABI_FLAG_UNREGISTER = (1 << 0), 22 | }; 23 | 24 | enum rseq_abi_cs_flags_bit { 25 | RSEQ_ABI_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT = 0, 26 | RSEQ_ABI_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT = 1, 27 | RSEQ_ABI_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT = 2, 28 | }; 29 | 30 | enum rseq_abi_cs_flags { 31 | RSEQ_ABI_CS_FLAG_NO_RESTART_ON_PREEMPT = 32 | (1U << RSEQ_ABI_CS_FLAG_NO_RESTART_ON_PREEMPT_BIT), 33 | RSEQ_ABI_CS_FLAG_NO_RESTART_ON_SIGNAL = 34 | (1U << RSEQ_ABI_CS_FLAG_NO_RESTART_ON_SIGNAL_BIT), 35 | RSEQ_ABI_CS_FLAG_NO_RESTART_ON_MIGRATE = 36 | (1U << RSEQ_ABI_CS_FLAG_NO_RESTART_ON_MIGRATE_BIT), 37 | }; 38 | 39 | /* 40 | * struct rseq_abi_cs is aligned on 4 * 8 bytes to ensure it is always 41 | * contained within a single cache-line. It is usually declared as 42 | * link-time constant data. 43 | */ 44 | struct rseq_abi_cs { 45 | /* Version of this structure. */ 46 | __u32 version; 47 | /* enum rseq_abi_cs_flags */ 48 | __u32 flags; 49 | __u64 start_ip; 50 | /* Offset from start_ip. */ 51 | __u64 post_commit_offset; 52 | __u64 abort_ip; 53 | } __attribute__((aligned(4 * sizeof(__u64)))); 54 | 55 | /* 56 | * struct rseq is aligned on 4 * 8 bytes to ensure it is always 57 | * contained within a single cache-line. 58 | * 59 | * A single struct rseq per thread is allowed. 60 | */ 61 | struct rseq_abi { 62 | /* 63 | * Restartable sequences cpu_id_start field. Updated by the 64 | * kernel. Read by user-space with single-copy atomicity 65 | * semantics. This field should only be read by the thread which 66 | * registered this data structure. Aligned on 32-bit. Always 67 | * contains a value in the range of possible CPUs, although the 68 | * value may not be the actual current CPU (e.g. if rseq is not 69 | * initialized). This CPU number value should always be compared 70 | * against the value of the cpu_id field before performing a rseq 71 | * commit or returning a value read from a data structure indexed 72 | * using the cpu_id_start value. 73 | */ 74 | __u32 cpu_id_start; 75 | /* 76 | * Restartable sequences cpu_id field. Updated by the kernel. 77 | * Read by user-space with single-copy atomicity semantics. This 78 | * field should only be read by the thread which registered this 79 | * data structure. Aligned on 32-bit. Values 80 | * RSEQ_ABI_CPU_ID_UNINITIALIZED and RSEQ_ABI_CPU_ID_REGISTRATION_FAILED 81 | * have a special semantic: the former means "rseq uninitialized", 82 | * and latter means "rseq initialization failed". This value is 83 | * meant to be read within rseq critical sections and compared 84 | * with the cpu_id_start value previously read, before performing 85 | * the commit instruction, or read and compared with the 86 | * cpu_id_start value before returning a value loaded from a data 87 | * structure indexed using the cpu_id_start value. 88 | */ 89 | __u32 cpu_id; 90 | /* 91 | * Restartable sequences rseq_cs field. 92 | * 93 | * Contains NULL when no critical section is active for the current 94 | * thread, or holds a pointer to the currently active struct rseq_abi_cs. 95 | * 96 | * Updated by user-space, which sets the address of the currently 97 | * active rseq_cs at the beginning of assembly instruction sequence 98 | * block, and set to NULL by the kernel when it restarts an assembly 99 | * instruction sequence block, as well as when the kernel detects that 100 | * it is preempting or delivering a signal outside of the range 101 | * targeted by the rseq_cs. Also needs to be set to NULL by user-space 102 | * before reclaiming memory that contains the targeted struct rseq_abi_cs. 103 | * 104 | * Read and set by the kernel. Set by user-space with single-copy 105 | * atomicity semantics. This field should only be updated by the 106 | * thread which registered this data structure. Aligned on 64-bit. 107 | * 108 | * 32-bit architectures should update the low order bits of the 109 | * rseq_cs field, leaving the high order bits initialized to 0. 110 | */ 111 | union { 112 | __u64 ptr64; 113 | 114 | /* 115 | * The "arch" field provides architecture accessor for 116 | * the ptr field based on architecture pointer size and 117 | * endianness. 118 | */ 119 | struct { 120 | #ifdef __LP64__ 121 | __u64 ptr; 122 | #elif defined(__BYTE_ORDER) ? (__BYTE_ORDER == __BIG_ENDIAN) : defined(__BIG_ENDIAN) 123 | __u32 padding; /* Initialized to zero. */ 124 | __u32 ptr; 125 | #else 126 | __u32 ptr; 127 | __u32 padding; /* Initialized to zero. */ 128 | #endif 129 | } arch; 130 | } rseq_cs; 131 | 132 | /* 133 | * Restartable sequences flags field. 134 | * 135 | * This field should only be updated by the thread which 136 | * registered this data structure. Read by the kernel. 137 | * Mainly used for single-stepping through rseq critical sections 138 | * with debuggers. 139 | * 140 | * - RSEQ_ABI_CS_FLAG_NO_RESTART_ON_PREEMPT 141 | * Inhibit instruction sequence block restart on preemption 142 | * for this thread. 143 | * - RSEQ_ABI_CS_FLAG_NO_RESTART_ON_SIGNAL 144 | * Inhibit instruction sequence block restart on signal 145 | * delivery for this thread. 146 | * - RSEQ_ABI_CS_FLAG_NO_RESTART_ON_MIGRATE 147 | * Inhibit instruction sequence block restart on migration for 148 | * this thread. 149 | */ 150 | __u32 flags; 151 | 152 | /* 153 | * Restartable sequences node_id field. Updated by the kernel. Read by 154 | * user-space with single-copy atomicity semantics. This field should 155 | * only be read by the thread which registered this data structure. 156 | * Aligned on 32-bit. Contains the current NUMA node ID. 157 | */ 158 | __u32 node_id; 159 | 160 | /* 161 | * Restartable sequences mm_cid field. Updated by the kernel. Read by 162 | * user-space with single-copy atomicity semantics. This field should 163 | * only be read by the thread which registered this data structure. 164 | * Aligned on 32-bit. Contains the current thread's concurrency ID 165 | * (allocated uniquely within a memory map). 166 | */ 167 | __u32 mm_cid; 168 | 169 | /* 170 | * Flexible array member at end of structure, after last feature field. 171 | */ 172 | char end[]; 173 | } __attribute__((aligned(4 * sizeof(__u64)))); 174 | 175 | /* 176 | * Define the rseq system call number if not yet available in 177 | * the system headers. 178 | */ 179 | #ifdef __x86_64__ 180 | 181 | #ifndef __NR_rseq 182 | #define __NR_rseq 334 183 | #endif 184 | 185 | #elif defined(__i386__) 186 | 187 | #ifndef __NR_rseq 188 | #define __NR_rseq 386 189 | #endif 190 | 191 | #elif defined(__AARCH64EL__) 192 | 193 | #ifndef __NR_rseq 194 | #define __NR_rseq 293 195 | #endif 196 | 197 | #elif defined(__ARMEL__) 198 | 199 | #ifndef __NR_rseq 200 | #define __NR_rseq 398 201 | #endif 202 | 203 | #elif defined(__PPC__) 204 | 205 | #ifndef __NR_rseq 206 | #define __NR_rseq 387 207 | #endif 208 | 209 | #elif defined(__s390__) 210 | 211 | #ifndef __NR_rseq 212 | #define __NR_rseq 383 213 | #endif 214 | 215 | #elif defined(__riscv) 216 | 217 | #ifndef __NR_rseq 218 | #define __NR_rseq 293 219 | #endif 220 | 221 | #endif 222 | 223 | #endif /* _RSEQ_ABI_H */ 224 | -------------------------------------------------------------------------------- /include/rseq/arch.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2016-2022 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/arch.h 6 | */ 7 | 8 | #include 9 | 10 | /* 11 | * Architecture detection using compiler defines. 12 | * 13 | * The following defines are used internally for architecture specific code. 14 | * 15 | * RSEQ_ARCH_X86 : All x86 variants 32 and 64 bits 16 | * RSEQ_ARCH_I386 : Specific to the i386 17 | * RSEQ_ARCH_AMD64 : All 64 bits x86 variants 18 | * 19 | * RSEQ_ARCH_PPC : All PowerPC variants 32 and 64 bits 20 | * RSEQ_ARCH_PPC64 : Specific to 64 bits variants 21 | * 22 | * RSEQ_ARCH_S390 : All IBM s390 / s390x variants 23 | * RSEQ_ARCH_S390X : Specific to z/Architecture 64 bits 24 | * 25 | * RSEQ_ARCH_ARM : All ARM 32 bits variants 26 | * RSEQ_ARCH_AARCH64 : All ARM 64 bits variants 27 | * RSEQ_ARCH_MIPS : All MIPS variants 28 | * RSEQ_ARCH_RISCV : All RISC-V variants 29 | */ 30 | 31 | #ifndef _RSEQ_ARCH_H 32 | #define _RSEQ_ARCH_H 33 | 34 | #if (defined(__amd64__) \ 35 | || defined(__amd64) \ 36 | || defined(__x86_64__) \ 37 | || defined(__x86_64)) 38 | 39 | #define RSEQ_ARCH_X86 1 40 | #define RSEQ_ARCH_AMD64 1 41 | #include 42 | 43 | #elif (defined(__i386__) || defined(__i386)) 44 | 45 | #define RSEQ_ARCH_X86 1 46 | #include 47 | 48 | #elif (defined(__arm__) || defined(__arm)) 49 | 50 | #define RSEQ_ARCH_ARM 1 51 | #include 52 | 53 | #elif defined(__aarch64__) 54 | 55 | #define RSEQ_ARCH_AARCH64 1 56 | #include 57 | 58 | #elif (defined(__powerpc64__) || defined(__ppc64__)) 59 | 60 | #define RSEQ_ARCH_PPC 1 61 | #define RSEQ_ARCH_PPC64 1 62 | #include 63 | 64 | #elif (defined(__powerpc__) \ 65 | || defined(__powerpc) \ 66 | || defined(__ppc__)) 67 | 68 | #define RSEQ_ARCH_PPC 1 69 | #include 70 | 71 | #elif (defined(__mips__) || defined(__mips)) 72 | 73 | #define RSEQ_ARCH_MIPS 1 74 | #include 75 | 76 | #elif defined(__s390__) 77 | 78 | # if (defined(__s390x__) || defined(__zarch__)) 79 | # define RSEQ_ARCH_S390X 1 80 | # endif 81 | 82 | #define RSEQ_ARCH_S390 1 83 | #include 84 | 85 | #elif defined(__riscv) 86 | 87 | #define RSEQ_ARCH_RISCV 1 88 | #include 89 | 90 | #elif defined(__loongarch__) 91 | 92 | #define RSEQ_ARCH_LOONGARCH 1 93 | #include 94 | 95 | #else 96 | #error "Cannot build: unrecognized architecture, see ." 97 | #endif 98 | 99 | #endif /* _RSEQ_ARCH_H */ 100 | -------------------------------------------------------------------------------- /include/rseq/arch/arm.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2016-2024 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/arch/arm.h 6 | */ 7 | 8 | #ifndef _RSEQ_RSEQ_H 9 | #error "Never use directly; include instead." 10 | #endif 11 | 12 | /* 13 | * RSEQ_ASM_*() macro helpers are internal to the librseq headers. Those 14 | * are not part of the public API. 15 | */ 16 | 17 | /* 18 | * - ARM little endian 19 | * 20 | * RSEQ_SIG uses the udf A32 instruction with an uncommon immediate operand 21 | * value 0x5de3. This traps if user-space reaches this instruction by mistake, 22 | * and the uncommon operand ensures the kernel does not move the instruction 23 | * pointer to attacker-controlled code on rseq abort. 24 | * 25 | * The instruction pattern in the A32 instruction set is: 26 | * 27 | * e7f5def3 udf #24035 ; 0x5de3 28 | * 29 | * This translates to the following instruction pattern in the T16 instruction 30 | * set: 31 | * 32 | * little endian: 33 | * def3 udf #243 ; 0xf3 34 | * e7f5 b.n <7f5> 35 | * 36 | * - ARMv6+ big endian (BE8): 37 | * 38 | * ARMv6+ -mbig-endian generates mixed endianness code vs data: little-endian 39 | * code and big-endian data. The data value of the signature needs to have its 40 | * byte order reversed to generate the trap instruction: 41 | * 42 | * Data: 0xf3def5e7 43 | * 44 | * Translates to this A32 instruction pattern: 45 | * 46 | * e7f5def3 udf #24035 ; 0x5de3 47 | * 48 | * Translates to this T16 instruction pattern: 49 | * 50 | * def3 udf #243 ; 0xf3 51 | * e7f5 b.n <7f5> 52 | * 53 | * - Prior to ARMv6 big endian (BE32): 54 | * 55 | * Prior to ARMv6, -mbig-endian generates big-endian code and data 56 | * (which match), so the endianness of the data representation of the 57 | * signature should not be reversed. However, the choice between BE32 58 | * and BE8 is done by the linker, so we cannot know whether code and 59 | * data endianness will be mixed before the linker is invoked. So rather 60 | * than try to play tricks with the linker, the rseq signature is simply 61 | * data (not a trap instruction) prior to ARMv6 on big endian. This is 62 | * why the signature is expressed as data (.word) rather than as 63 | * instruction (.inst) in assembler. 64 | */ 65 | 66 | #ifdef __ARMEB__ /* Big endian */ 67 | # define RSEQ_SIG 0xf3def5e7 /* udf #24035 ; 0x5de3 (ARMv6+) */ 68 | #else /* Little endian */ 69 | # define RSEQ_SIG 0xe7f5def3 /* udf #24035 ; 0x5de3 */ 70 | #endif 71 | 72 | /* 73 | * Refer to the Linux kernel memory model (LKMM) for documentation of 74 | * the memory barriers. 75 | */ 76 | 77 | /* CPU memory barrier. */ 78 | #define rseq_smp_mb() __asm__ __volatile__ ("dmb" ::: "memory", "cc") 79 | /* CPU read memory barrier */ 80 | #define rseq_smp_rmb() __asm__ __volatile__ ("dmb" ::: "memory", "cc") 81 | /* CPU write memory barrier */ 82 | #define rseq_smp_wmb() __asm__ __volatile__ ("dmb" ::: "memory", "cc") 83 | 84 | /* Acquire: One-way permeable barrier. */ 85 | #define rseq_smp_load_acquire(p) \ 86 | __extension__ ({ \ 87 | rseq_unqual_scalar_typeof(*(p)) ____p1 = RSEQ_READ_ONCE(*(p)); \ 88 | rseq_smp_mb(); \ 89 | ____p1; \ 90 | }) 91 | 92 | /* Acquire barrier after control dependency. */ 93 | #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() 94 | 95 | /* Release: One-way permeable barrier. */ 96 | #define rseq_smp_store_release(p, v) \ 97 | do { \ 98 | rseq_smp_mb(); \ 99 | RSEQ_WRITE_ONCE(*(p), v); \ 100 | } while (0) 101 | 102 | #ifdef __ARMEB__ /* Big endian */ 103 | # define RSEQ_ASM_U64_PTR(x) ".word 0x0, " x 104 | #else /* Little endian */ 105 | # define RSEQ_ASM_U64_PTR(x) ".word " x ", 0x0" 106 | #endif 107 | 108 | #define RSEQ_ASM_U32(x) ".word " x 109 | 110 | /* Common architecture support macros. */ 111 | #include "rseq/arch/generic/common.h" 112 | 113 | /* 114 | * Store the address of the critical section descriptor structure at 115 | * @cs_label into the @rseq_cs pointer and emit the label @label, which 116 | * is the beginning of the sequence of consecutive assembly instructions. 117 | * 118 | * @label: 119 | * Local label to the beginning of the sequence of consecutive assembly 120 | * instructions. 121 | * @cs_label: 122 | * Source local label to the critical section descriptor structure. 123 | * @rseq_cs: 124 | * Destination pointer where to store the address of the critical 125 | * section descriptor structure. 126 | */ 127 | #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ 128 | RSEQ_INJECT_ASM(1) \ 129 | "adr r0, " __rseq_str(cs_label) "\n\t" \ 130 | "str r0, %[" __rseq_str(rseq_cs) "]\n\t" \ 131 | __rseq_str(label) ":\n\t" 132 | 133 | /* Only used in RSEQ_ASM_DEFINE_ABORT. */ 134 | #define __RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label, \ 135 | table_label, version, flags, \ 136 | start_ip, post_commit_offset, abort_ip) \ 137 | ".balign 32\n\t" \ 138 | __rseq_str(table_label) ":\n\t" \ 139 | __RSEQ_ASM_DEFINE_CS_FIELDS(version, flags, \ 140 | start_ip, post_commit_offset, abort_ip) "\n\t" \ 141 | RSEQ_ASM_U32(__rseq_str(RSEQ_SIG)) "\n\t" \ 142 | __rseq_str(label) ":\n\t" \ 143 | teardown \ 144 | "b %l[" __rseq_str(abort_label) "]\n\t" 145 | 146 | /* 147 | * Define a critical section abort handler. 148 | * 149 | * @label: 150 | * Local label to the abort handler. 151 | * @teardown: 152 | * Sequence of instructions to run on abort. 153 | * @abort_label: 154 | * C label to jump to at the end of the sequence. 155 | * @table_label: 156 | * Local label to the critical section descriptor copy placed near 157 | * the program counter. This is done for performance reasons because 158 | * computing this address is faster than accessing the program data. 159 | * 160 | * The purpose of @start_ip, @post_commit_ip, and @abort_ip are 161 | * documented in RSEQ_ASM_DEFINE_TABLE. 162 | */ 163 | #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label, \ 164 | table_label, start_ip, post_commit_ip, abort_ip) \ 165 | __RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label, \ 166 | table_label, 0x0, 0x0, start_ip, \ 167 | (post_commit_ip - start_ip), abort_ip) 168 | 169 | /* 170 | * Define a critical section teardown handler. 171 | * 172 | * @label: 173 | * Local label to the teardown handler. 174 | * @teardown: 175 | * Sequence of instructions to run on teardown. 176 | * @target_label: 177 | * C label to jump to at the end of the sequence. 178 | */ 179 | #define RSEQ_ASM_DEFINE_TEARDOWN(label, teardown, target_label) \ 180 | __rseq_str(label) ":\n\t" \ 181 | teardown \ 182 | "b %l[" __rseq_str(target_label) "]\n\t" 183 | 184 | /* Jump to local label @label when @cpu_id != @current_cpu_id. */ 185 | #define RSEQ_ASM_CBNE_CPU_ID(cpu_id, current_cpu_id, label) \ 186 | RSEQ_INJECT_ASM(2) \ 187 | "ldr r0, %[" __rseq_str(current_cpu_id) "]\n\t" \ 188 | "cmp %[" __rseq_str(cpu_id) "], r0\n\t" \ 189 | "bne " __rseq_str(label) "\n\t" 190 | 191 | /* Per-cpu-id indexing. */ 192 | 193 | #define RSEQ_TEMPLATE_INDEX_CPU_ID 194 | #define RSEQ_TEMPLATE_MO_RELAXED 195 | #include "rseq/arch/arm/bits.h" 196 | #undef RSEQ_TEMPLATE_MO_RELAXED 197 | 198 | #define RSEQ_TEMPLATE_MO_RELEASE 199 | #include "rseq/arch/arm/bits.h" 200 | #undef RSEQ_TEMPLATE_MO_RELEASE 201 | #undef RSEQ_TEMPLATE_INDEX_CPU_ID 202 | 203 | /* Per-mm-cid indexing. */ 204 | 205 | #define RSEQ_TEMPLATE_INDEX_MM_CID 206 | #define RSEQ_TEMPLATE_MO_RELAXED 207 | #include "rseq/arch/arm/bits.h" 208 | #undef RSEQ_TEMPLATE_MO_RELAXED 209 | 210 | #define RSEQ_TEMPLATE_MO_RELEASE 211 | #include "rseq/arch/arm/bits.h" 212 | #undef RSEQ_TEMPLATE_MO_RELEASE 213 | #undef RSEQ_TEMPLATE_INDEX_MM_CID 214 | 215 | /* APIs which are not indexed. */ 216 | 217 | #define RSEQ_TEMPLATE_INDEX_NONE 218 | #define RSEQ_TEMPLATE_MO_RELAXED 219 | #include "rseq/arch/arm/bits.h" 220 | #undef RSEQ_TEMPLATE_MO_RELAXED 221 | #undef RSEQ_TEMPLATE_INDEX_NONE 222 | -------------------------------------------------------------------------------- /include/rseq/arch/generic/common.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2024 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/arch/generic/common.h: Common architecture support macros. 6 | */ 7 | 8 | #ifndef _RSEQ_GENERIC_COMMON_H 9 | #define _RSEQ_GENERIC_COMMON_H 10 | 11 | /* 12 | * Define the rseq critical section descriptor fields. 13 | */ 14 | #define __RSEQ_ASM_DEFINE_CS_FIELDS(version, flags, \ 15 | start_ip, post_commit_offset, abort_ip) \ 16 | RSEQ_ASM_U32(__rseq_str(version)) "\n\t" \ 17 | RSEQ_ASM_U32(__rseq_str(flags)) "\n\t" \ 18 | RSEQ_ASM_U64_PTR(__rseq_str(start_ip)) "\n\t" \ 19 | RSEQ_ASM_U64_PTR(__rseq_str(post_commit_offset)) "\n\t" \ 20 | RSEQ_ASM_U64_PTR(__rseq_str(abort_ip)) 21 | 22 | /* Only used in RSEQ_ASM_DEFINE_TABLE. */ 23 | #define __RSEQ_ASM_DEFINE_TABLE(label, version, flags, \ 24 | start_ip, post_commit_offset, abort_ip) \ 25 | ".pushsection __rseq_cs, \"aw\"\n\t" \ 26 | ".balign 32\n\t" \ 27 | __rseq_str(label) ":\n\t" \ 28 | __RSEQ_ASM_DEFINE_CS_FIELDS(version, flags, \ 29 | start_ip, post_commit_offset, abort_ip) "\n\t" \ 30 | ".popsection\n\t" \ 31 | ".pushsection __rseq_cs_ptr_array, \"aw\"\n\t" \ 32 | RSEQ_ASM_U64_PTR(__rseq_str(label) "b") "\n\t" \ 33 | ".popsection\n\t" 34 | 35 | /* 36 | * Define an rseq critical section structure of version 0 with no flags. 37 | * 38 | * @label: 39 | * Local label for the beginning of the critical section descriptor 40 | * structure. 41 | * @start_ip: 42 | * Pointer to the first instruction of the sequence of consecutive assembly 43 | * instructions. 44 | * @post_commit_ip: 45 | * Pointer to the instruction after the last instruction of the sequence of 46 | * consecutive assembly instructions. 47 | * @abort_ip: 48 | * Pointer to the instruction where to move the execution flow in case of 49 | * abort of the sequence of consecutive assembly instructions. 50 | */ 51 | #define RSEQ_ASM_DEFINE_TABLE(label, start_ip, post_commit_ip, abort_ip) \ 52 | __RSEQ_ASM_DEFINE_TABLE(label, 0x0, 0x0, start_ip, \ 53 | (post_commit_ip) - (start_ip), abort_ip) 54 | 55 | /* 56 | * Define the @exit_ip pointer as an exit point for the sequence of consecutive 57 | * assembly instructions at @start_ip. 58 | * 59 | * @start_ip: 60 | * Pointer to the first instruction of the sequence of consecutive assembly 61 | * instructions. 62 | * @exit_ip: 63 | * Pointer to an exit point instruction. 64 | * 65 | * Exit points of a rseq critical section consist of all instructions outside 66 | * of the critical section where a critical section can either branch to or 67 | * reach through the normal course of its execution. The abort IP and the 68 | * post-commit IP are already part of the __rseq_cs section and should not be 69 | * explicitly defined as additional exit points. Knowing all exit points is 70 | * useful to assist debuggers stepping over the critical section. 71 | */ 72 | #define RSEQ_ASM_DEFINE_EXIT_POINT(start_ip, exit_ip) \ 73 | ".pushsection __rseq_exit_point_array, \"aw\"\n\t" \ 74 | RSEQ_ASM_U64_PTR(__rseq_str(start_ip)) "\n\t" \ 75 | RSEQ_ASM_U64_PTR(__rseq_str(exit_ip)) "\n\t" \ 76 | ".popsection\n\t" 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /include/rseq/arch/generic/thread-pointer.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2021 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/arch/generic/thread-pointer.h 6 | */ 7 | 8 | #ifndef _RSEQ_GENERIC_THREAD_POINTER_H 9 | #define _RSEQ_GENERIC_THREAD_POINTER_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | /* Use gcc builtin thread pointer. */ 16 | static inline __attribute__((always_inline)) 17 | void *rseq_thread_pointer(void) 18 | { 19 | return __builtin_thread_pointer(); 20 | } 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /include/rseq/arch/loongarch.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2024 Michael Jeanson */ 3 | /* SPDX-FileCopyrightText: 2023 Huang Pei */ 4 | /* SPDX-FileCopyrightText: 2023 Loongson Technology Corporation Limited */ 5 | /* SPDX-FileCopyrightText: 2016-2022 Mathieu Desnoyers */ 6 | /* SPDX-FileCopyrightText: 2018 MIPS Tech LLC */ 7 | /* SPDX-FileCopyrightText: 2018 Paul Burton */ 8 | 9 | /* 10 | * rseq/arch/loongarch.h 11 | */ 12 | 13 | #ifndef _RSEQ_RSEQ_H 14 | #error "Never use directly; include instead." 15 | #endif 16 | 17 | /* 18 | * RSEQ_ASM_*() macro helpers are internal to the librseq headers. Those 19 | * are not part of the public API. 20 | */ 21 | 22 | #if (RSEQ_BITS_PER_LONG != 64) 23 | # error unsupported RSEQ_BITS_PER_LONG 24 | #endif 25 | 26 | /* 27 | * RSEQ_SIG use "break 0x10" instruction. 28 | */ 29 | 30 | #define RSEQ_SIG 0x002a0010 31 | 32 | /* 33 | * Refer to the Linux kernel memory model (LKMM) for documentation of 34 | * the memory barriers. 35 | */ 36 | 37 | /* CPU memory barrier. */ 38 | #define rseq_smp_mb() __asm__ __volatile__ ("dbar 0x10" ::: "memory") 39 | /* CPU read memory barrier */ 40 | #define rseq_smp_rmb() __asm__ __volatile__ ("dbar 0x15" ::: "memory") 41 | /* CPU write memory barrier */ 42 | #define rseq_smp_wmb() __asm__ __volatile__ ("dbar 0x1a" ::: "memory") 43 | 44 | /* Acquire: One-way permeable barrier. */ 45 | #define rseq_smp_load_acquire(p) \ 46 | __extension__ ({ \ 47 | rseq_unqual_scalar_typeof(*(p)) ____p1 = RSEQ_READ_ONCE(*(p)); \ 48 | __asm__ __volatile__("dbar 0x14" ::: "memory"); \ 49 | ____p1; \ 50 | }) 51 | 52 | /* Acquire barrier after control dependency. */ 53 | #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() 54 | 55 | /* Release: One-way permeable barrier. */ 56 | #define rseq_smp_store_release(p, v) \ 57 | do { \ 58 | __asm__ __volatile__("dbar 0x12" ::: "memory"); \ 59 | RSEQ_WRITE_ONCE(*(p), v); \ 60 | } while (0) 61 | 62 | /* 63 | * Helper macros to define and access a variable of long integer type. 64 | * Only used internally in rseq headers. 65 | */ 66 | #define RSEQ_ASM_LONG ".dword" 67 | #define RSEQ_ASM_LONG_LA "la.local" 68 | #define RSEQ_ASM_LONG_L "ld.d" 69 | #define RSEQ_ASM_LONG_S "st.d" 70 | #define RSEQ_ASM_LONG_ADDI "addi.d" 71 | 72 | /* 73 | * Helper macros to define a variable of pointer type stored in a 64-bit 74 | * integer. Only used internally in rseq headers. 75 | */ 76 | #define RSEQ_ASM_U64_PTR(x) ".dword " x 77 | #define RSEQ_ASM_U32(x) ".word " x 78 | 79 | /* Temporary scratch registers. */ 80 | #define RSEQ_ASM_TMP_REG "$r4" 81 | 82 | /* Common architecture support macros. */ 83 | #include "rseq/arch/generic/common.h" 84 | 85 | /* Only used in RSEQ_ASM_DEFINE_ABORT. */ 86 | #define __RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label, \ 87 | table_label, version, flags, \ 88 | start_ip, post_commit_offset, abort_ip) \ 89 | ".balign 32\n\t" \ 90 | __rseq_str(table_label) ":\n\t" \ 91 | __RSEQ_ASM_DEFINE_CS_FIELDS(version, flags, \ 92 | start_ip, post_commit_offset, abort_ip) "\n\t" \ 93 | RSEQ_ASM_U32(__rseq_str(RSEQ_SIG)) "\n\t" \ 94 | __rseq_str(label) ":\n\t" \ 95 | teardown \ 96 | "b %l[" __rseq_str(abort_label) "]\n\t" 97 | 98 | /* 99 | * Define a critical section abort handler. 100 | * 101 | * @label: 102 | * Local label to the abort handler. 103 | * @teardown: 104 | * Sequence of instructions to run on abort. 105 | * @abort_label: 106 | * C label to jump to at the end of the sequence. 107 | * @table_label: 108 | * Local label to the critical section descriptor copy placed near 109 | * the program counter. This is done for performance reasons because 110 | * computing this address is faster than accessing the program data. 111 | * 112 | * The purpose of @start_ip, @post_commit_ip, and @abort_ip are 113 | * documented in RSEQ_ASM_DEFINE_TABLE. 114 | */ 115 | #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label, \ 116 | table_label, start_ip, post_commit_ip, abort_ip) \ 117 | __RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label, \ 118 | table_label, 0x0, 0x0, start_ip, \ 119 | (post_commit_ip) - (start_ip), abort_ip) 120 | 121 | /* 122 | * Define a critical section teardown handler. 123 | * 124 | * @label: 125 | * Local label to the teardown handler. 126 | * @teardown: 127 | * Sequence of instructions to run on teardown. 128 | * @target_label: 129 | * C label to jump to at the end of the sequence. 130 | */ 131 | #define RSEQ_ASM_DEFINE_TEARDOWN(label, teardown, target_label) \ 132 | __rseq_str(label) ":\n\t" \ 133 | teardown \ 134 | "b %l[" __rseq_str(target_label) "]\n\t" 135 | /* 136 | * Store the address of the critical section descriptor structure at 137 | * @cs_label into the @rseq_cs pointer and emit the label @label, which 138 | * is the beginning of the sequence of consecutive assembly instructions. 139 | * 140 | * @label: 141 | * Local label to the beginning of the sequence of consecutive assembly 142 | * instructions. 143 | * @cs_label: 144 | * Source local label to the critical section descriptor structure. 145 | * @rseq_cs: 146 | * Destination pointer where to store the address of the critical 147 | * section descriptor structure. 148 | */ 149 | #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ 150 | RSEQ_INJECT_ASM(1) \ 151 | RSEQ_ASM_LONG_LA " $r4, " __rseq_str(cs_label) "\n\t" \ 152 | RSEQ_ASM_LONG_S " $r4, %[" __rseq_str(rseq_cs) "]\n\t" \ 153 | __rseq_str(label) ":\n\t" 154 | 155 | /* Jump to local label @label when @cpu_id != @current_cpu_id. */ 156 | #define RSEQ_ASM_CBNE_CPU_ID(cpu_id, current_cpu_id, label) \ 157 | RSEQ_INJECT_ASM(2) \ 158 | "ld.w $r4, %[" __rseq_str(current_cpu_id) "]\n\t" \ 159 | "bne $r4, %[" __rseq_str(cpu_id) "], " __rseq_str(label) "\n\t" 160 | 161 | /* Per-cpu-id indexing. */ 162 | 163 | #define RSEQ_TEMPLATE_INDEX_CPU_ID 164 | #define RSEQ_TEMPLATE_MO_RELAXED 165 | #include "rseq/arch/loongarch/bits.h" 166 | #undef RSEQ_TEMPLATE_MO_RELAXED 167 | 168 | #define RSEQ_TEMPLATE_MO_RELEASE 169 | #include "rseq/arch/loongarch/bits.h" 170 | #undef RSEQ_TEMPLATE_MO_RELEASE 171 | #undef RSEQ_TEMPLATE_INDEX_CPU_ID 172 | 173 | /* Per-mm-cid indexing. */ 174 | 175 | #define RSEQ_TEMPLATE_INDEX_MM_CID 176 | #define RSEQ_TEMPLATE_MO_RELAXED 177 | #include "rseq/arch/loongarch/bits.h" 178 | #undef RSEQ_TEMPLATE_MO_RELAXED 179 | 180 | #define RSEQ_TEMPLATE_MO_RELEASE 181 | #include "rseq/arch/loongarch/bits.h" 182 | #undef RSEQ_TEMPLATE_MO_RELEASE 183 | #undef RSEQ_TEMPLATE_INDEX_MM_CID 184 | 185 | /* APIs which are not indexed. */ 186 | 187 | #define RSEQ_TEMPLATE_INDEX_NONE 188 | #define RSEQ_TEMPLATE_MO_RELAXED 189 | #include "rseq/arch/loongarch/bits.h" 190 | #undef RSEQ_TEMPLATE_MO_RELAXED 191 | #undef RSEQ_TEMPLATE_INDEX_NONE 192 | -------------------------------------------------------------------------------- /include/rseq/arch/loongarch/thread-pointer.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2024 Michael Jeanson */ 3 | /* SPDX-FileCopyrightText: 2023 Huang Pei */ 4 | 5 | /* 6 | * rseq/arch/loongarch/thread-pointer.h 7 | */ 8 | 9 | #ifndef _RSEQ_LOONGARCH_THREAD_POINTER 10 | #define _RSEQ_LOONGARCH_THREAD_POINTER 11 | 12 | #include 13 | 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #if __GNUC_PREREQ (13, 3) 19 | static inline __attribute__((always_inline)) 20 | void *rseq_thread_pointer(void) 21 | { 22 | return __builtin_thread_pointer(); 23 | } 24 | #else 25 | static inline __attribute__((always_inline)) 26 | void *rseq_thread_pointer(void) 27 | { 28 | register void *__result asm ("$2"); 29 | asm ("" : "=r" (__result)); 30 | return __result; 31 | } 32 | #endif /* !GCC 13.3 */ 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/rseq/arch/mips.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2018 MIPS Tech LLC */ 3 | /* SPDX-FileCopyrightText: 2016-2024 Mathieu Desnoyers */ 4 | 5 | /* 6 | * Author: Paul Burton 7 | */ 8 | 9 | #ifndef _RSEQ_RSEQ_H 10 | #error "Never use directly; include instead." 11 | #endif 12 | 13 | #include 14 | 15 | /* 16 | * RSEQ_ASM_*() macro helpers are internal to the librseq headers. Those 17 | * are not part of the public API. 18 | */ 19 | 20 | #if (RSEQ_BITS_PER_LONG != 64) && (RSEQ_BITS_PER_LONG != 32) 21 | # error unsupported RSEQ_BITS_PER_LONG 22 | #endif 23 | 24 | /* 25 | * RSEQ_SIG uses the break instruction. The instruction pattern is: 26 | * 27 | * On MIPS: 28 | * 0350000d break 0x350 29 | * 30 | * On nanoMIPS: 31 | * 00100350 break 0x350 32 | * 33 | * On microMIPS: 34 | * 0000d407 break 0x350 35 | * 36 | * For nanoMIPS32 and microMIPS, the instruction stream is encoded as 16-bit 37 | * halfwords, so the signature halfwords need to be swapped accordingly for 38 | * little-endian. 39 | */ 40 | #if defined(__nanomips__) 41 | # ifdef __MIPSEL__ 42 | # define RSEQ_SIG 0x03500010 43 | # else 44 | # define RSEQ_SIG 0x00100350 45 | # endif 46 | #elif defined(__mips_micromips) 47 | # ifdef __MIPSEL__ 48 | # define RSEQ_SIG 0xd4070000 49 | # else 50 | # define RSEQ_SIG 0x0000d407 51 | # endif 52 | #elif defined(__mips__) 53 | # define RSEQ_SIG 0x0350000d 54 | #else 55 | /* Unknown MIPS architecture. */ 56 | #endif 57 | 58 | /* 59 | * Refer to the Linux kernel memory model (LKMM) for documentation of 60 | * the memory barriers. 61 | */ 62 | 63 | /* CPU memory barrier. */ 64 | #define rseq_smp_mb() __asm__ __volatile__ ("sync" ::: "memory") 65 | /* CPU read memory barrier */ 66 | #define rseq_smp_rmb() rseq_smp_mb() 67 | /* CPU write memory barrier */ 68 | #define rseq_smp_wmb() rseq_smp_mb() 69 | 70 | /* Acquire: One-way permeable barrier. */ 71 | #define rseq_smp_load_acquire(p) \ 72 | __extension__ ({ \ 73 | rseq_unqual_scalar_typeof(*(p)) ____p1 = RSEQ_READ_ONCE(*(p)); \ 74 | rseq_smp_mb(); \ 75 | ____p1; \ 76 | }) 77 | 78 | /* Acquire barrier after control dependency. */ 79 | #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() 80 | 81 | /* Release: One-way permeable barrier. */ 82 | #define rseq_smp_store_release(p, v) \ 83 | do { \ 84 | rseq_smp_mb(); \ 85 | RSEQ_WRITE_ONCE(*(p), v); \ 86 | } while (0) 87 | 88 | /* 89 | * Helper macros to define and access a variable of long integer type. 90 | * Only used internally in rseq headers. 91 | */ 92 | #if RSEQ_BITS_PER_LONG == 64 93 | # define RSEQ_ASM_LONG ".dword" 94 | # define RSEQ_ASM_LONG_LA "dla" 95 | # define RSEQ_ASM_LONG_L "ld" 96 | # define RSEQ_ASM_LONG_S "sd" 97 | # define RSEQ_ASM_LONG_ADDI "daddiu" 98 | #else 99 | # define RSEQ_ASM_LONG ".word" 100 | # define RSEQ_ASM_LONG_LA "la" 101 | # define RSEQ_ASM_LONG_L "lw" 102 | # define RSEQ_ASM_LONG_S "sw" 103 | # define RSEQ_ASM_LONG_ADDI "addiu" 104 | #endif 105 | 106 | /* 107 | * Helper macros to define a variable of pointer type stored in a 64-bit 108 | * integer. Only used internally in rseq headers. 109 | */ 110 | #if RSEQ_BITS_PER_LONG == 64 111 | # define RSEQ_ASM_U64_PTR(x) ".dword " x 112 | #else 113 | # if defined(__BYTE_ORDER) ? (__BYTE_ORDER == __BIG_ENDIAN) : defined(__BIG_ENDIAN) 114 | # define RSEQ_ASM_U64_PTR(x) ".word 0x0, " x 115 | # else 116 | # define RSEQ_ASM_U64_PTR(x) ".word " x ", 0x0" 117 | # endif 118 | #endif 119 | 120 | #define RSEQ_ASM_U32(x) ".word " x 121 | 122 | /* Common architecture support macros. */ 123 | #include "rseq/arch/generic/common.h" 124 | 125 | /* Only used in RSEQ_ASM_DEFINE_ABORT. */ 126 | #define __RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label, \ 127 | table_label, version, flags, \ 128 | start_ip, post_commit_offset, abort_ip) \ 129 | ".balign 32\n\t" \ 130 | __rseq_str(table_label) ":\n\t" \ 131 | __RSEQ_ASM_DEFINE_CS_FIELDS(version, flags, \ 132 | start_ip, post_commit_offset, abort_ip) "\n\t" \ 133 | RSEQ_ASM_U32(__rseq_str(RSEQ_SIG)) "\n\t" \ 134 | __rseq_str(label) ":\n\t" \ 135 | teardown \ 136 | "b %l[" __rseq_str(abort_label) "]\n\t" 137 | 138 | /* 139 | * Define a critical section abort handler. 140 | * 141 | * @label: 142 | * Local label to the abort handler. 143 | * @teardown: 144 | * Sequence of instructions to run on abort. 145 | * @abort_label: 146 | * C label to jump to at the end of the sequence. 147 | * @table_label: 148 | * Local label to the critical section descriptor copy placed near 149 | * the program counter. This is done for performance reasons because 150 | * computing this address is faster than accessing the program data. 151 | * 152 | * The purpose of @start_ip, @post_commit_ip, and @abort_ip are 153 | * documented in RSEQ_ASM_DEFINE_TABLE. 154 | */ 155 | #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label, \ 156 | table_label, start_ip, post_commit_ip, abort_ip) \ 157 | __RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label, \ 158 | table_label, 0x0, 0x0, start_ip, \ 159 | (post_commit_ip) - (start_ip), abort_ip) 160 | 161 | /* 162 | * Define a critical section teardown handler. 163 | * 164 | * @label: 165 | * Local label to the teardown handler. 166 | * @teardown: 167 | * Sequence of instructions to run on teardown. 168 | * @target_label: 169 | * C label to jump to at the end of the sequence. 170 | */ 171 | #define RSEQ_ASM_DEFINE_TEARDOWN(label, teardown, target_label) \ 172 | __rseq_str(label) ":\n\t" \ 173 | teardown \ 174 | "b %l[" __rseq_str(target_label) "]\n\t" 175 | 176 | /* 177 | * Store the address of the critical section descriptor structure at 178 | * @cs_label into the @rseq_cs pointer and emit the label @label, which 179 | * is the beginning of the sequence of consecutive assembly instructions. 180 | * 181 | * @label: 182 | * Local label to the beginning of the sequence of consecutive assembly 183 | * instructions. 184 | * @cs_label: 185 | * Source local label to the critical section descriptor structure. 186 | * @rseq_cs: 187 | * Destination pointer where to store the address of the critical 188 | * section descriptor structure. 189 | */ 190 | #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ 191 | RSEQ_INJECT_ASM(1) \ 192 | RSEQ_ASM_LONG_LA " $4, " __rseq_str(cs_label) "\n\t" \ 193 | RSEQ_ASM_LONG_S " $4, %[" __rseq_str(rseq_cs) "]\n\t" \ 194 | __rseq_str(label) ":\n\t" 195 | 196 | /* Jump to local label @label when @cpu_id != @current_cpu_id. */ 197 | #define RSEQ_ASM_CBNE_CPU_ID(cpu_id, current_cpu_id, label) \ 198 | RSEQ_INJECT_ASM(2) \ 199 | "lw $4, %[" __rseq_str(current_cpu_id) "]\n\t" \ 200 | "bne $4, %[" __rseq_str(cpu_id) "], " __rseq_str(label) "\n\t" 201 | 202 | /* Per-cpu-id indexing. */ 203 | 204 | #define RSEQ_TEMPLATE_INDEX_CPU_ID 205 | #define RSEQ_TEMPLATE_MO_RELAXED 206 | #include "rseq/arch/mips/bits.h" 207 | #undef RSEQ_TEMPLATE_MO_RELAXED 208 | 209 | #define RSEQ_TEMPLATE_MO_RELEASE 210 | #include "rseq/arch/mips/bits.h" 211 | #undef RSEQ_TEMPLATE_MO_RELEASE 212 | #undef RSEQ_TEMPLATE_INDEX_CPU_ID 213 | 214 | /* Per-mm-cid indexing. */ 215 | 216 | #define RSEQ_TEMPLATE_INDEX_MM_CID 217 | #define RSEQ_TEMPLATE_MO_RELAXED 218 | #include "rseq/arch/mips/bits.h" 219 | #undef RSEQ_TEMPLATE_MO_RELAXED 220 | 221 | #define RSEQ_TEMPLATE_MO_RELEASE 222 | #include "rseq/arch/mips/bits.h" 223 | #undef RSEQ_TEMPLATE_MO_RELEASE 224 | #undef RSEQ_TEMPLATE_INDEX_MM_CID 225 | 226 | /* APIs which are not indexed. */ 227 | 228 | #define RSEQ_TEMPLATE_INDEX_NONE 229 | #define RSEQ_TEMPLATE_MO_RELAXED 230 | #include "rseq/arch/mips/bits.h" 231 | #undef RSEQ_TEMPLATE_MO_RELAXED 232 | #undef RSEQ_TEMPLATE_INDEX_NONE 233 | -------------------------------------------------------------------------------- /include/rseq/arch/ppc/thread-pointer.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2021 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/arch/ppc/thread-pointer.h 6 | */ 7 | 8 | #ifndef _RSEQ_PPC_THREAD_POINTER 9 | #define _RSEQ_PPC_THREAD_POINTER 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | static inline __attribute__((always_inline)) 16 | void *rseq_thread_pointer(void) 17 | { 18 | #ifdef __powerpc64__ 19 | register void *__result asm ("r13"); 20 | #else 21 | register void *__result asm ("r2"); 22 | #endif 23 | asm ("" : "=r" (__result)); 24 | return __result; 25 | } 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /include/rseq/arch/riscv.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2022 Vincent Chen */ 3 | /* SPDX-FileCopyrightText: 2024 Mathieu Desnoyers */ 4 | 5 | /* 6 | * rseq-riscv.h 7 | */ 8 | 9 | /* 10 | * RSEQ_ASM_*() macro helpers are internal to the librseq headers. Those 11 | * are not part of the public API. 12 | */ 13 | 14 | #ifndef _RSEQ_RSEQ_H 15 | #error "Never use directly; include instead." 16 | #endif 17 | 18 | /* 19 | * Select the instruction "csrw mhartid, x0" as the RSEQ_SIG. Unlike 20 | * other architectures, the ebreak instruction has no immediate field for 21 | * distinguishing purposes. Hence, ebreak is not suitable as RSEQ_SIG. 22 | * "csrw mhartid, x0" can also satisfy the RSEQ requirement because it 23 | * is an uncommon instruction and will raise an illegal instruction 24 | * exception when executed in all modes. 25 | */ 26 | #include 27 | 28 | #if defined(__BYTE_ORDER) ? (__BYTE_ORDER == __LITTLE_ENDIAN) : defined(__LITTLE_ENDIAN) 29 | #define RSEQ_SIG 0xf1401073 /* csrr mhartid, x0 */ 30 | #else 31 | #error "Currently, RSEQ only supports Little-Endian version" 32 | #endif 33 | 34 | /* 35 | * Instruction selection between 32-bit/64-bit. Used internally in the 36 | * rseq headers. 37 | */ 38 | #if __riscv_xlen == 64 39 | #define __RSEQ_ASM_REG_SEL(a, b) a 40 | #elif __riscv_xlen == 32 41 | #define __RSEQ_ASM_REG_SEL(a, b) b 42 | #endif 43 | 44 | #define RSEQ_ASM_REG_L __RSEQ_ASM_REG_SEL("ld ", "lw ") 45 | #define RSEQ_ASM_REG_S __RSEQ_ASM_REG_SEL("sd ", "sw ") 46 | 47 | /* 48 | * Refer to the Linux kernel memory model (LKMM) for documentation of 49 | * the memory barriers. 50 | */ 51 | 52 | /* Only used internally in rseq headers. */ 53 | #define RSEQ_ASM_RISCV_FENCE(p, s) \ 54 | __asm__ __volatile__ ("fence " #p "," #s : : : "memory") 55 | /* CPU memory barrier. */ 56 | #define rseq_smp_mb() RSEQ_ASM_RISCV_FENCE(rw, rw) 57 | /* CPU read memory barrier */ 58 | #define rseq_smp_rmb() RSEQ_ASM_RISCV_FENCE(r, r) 59 | /* CPU write memory barrier */ 60 | #define rseq_smp_wmb() RSEQ_ASM_RISCV_FENCE(w, w) 61 | 62 | /* Acquire: One-way permeable barrier. */ 63 | #define rseq_smp_load_acquire(p) \ 64 | __extension__ ({ \ 65 | rseq_unqual_scalar_typeof(*(p)) ____p1 = RSEQ_READ_ONCE(*(p)); \ 66 | RSEQ_ASM_RISCV_FENCE(r, rw); \ 67 | ____p1; \ 68 | }) 69 | 70 | /* Acquire barrier after control dependency. */ 71 | #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() 72 | 73 | /* Release: One-way permeable barrier. */ 74 | #define rseq_smp_store_release(p, v) \ 75 | do { \ 76 | RSEQ_ASM_RISCV_FENCE(rw, w); \ 77 | RSEQ_WRITE_ONCE(*(p), v); \ 78 | } while (0) 79 | 80 | #define RSEQ_ASM_U64_PTR(x) ".quad " x 81 | #define RSEQ_ASM_U32(x) ".long " x 82 | 83 | /* Temporary registers. */ 84 | #define RSEQ_ASM_TMP_REG_1 "t6" 85 | #define RSEQ_ASM_TMP_REG_2 "t5" 86 | #define RSEQ_ASM_TMP_REG_3 "t4" 87 | #define RSEQ_ASM_TMP_REG_4 "t3" 88 | 89 | /* Common architecture support macros. */ 90 | #include "rseq/arch/generic/common.h" 91 | 92 | /* 93 | * Define a critical section abort handler. 94 | * 95 | * @label: 96 | * Local label to the abort handler. 97 | * @teardown: 98 | * Sequence of instructions to run on abort. 99 | * @abort_label: 100 | * C label to jump to at the end of the sequence. 101 | */ 102 | #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \ 103 | "j 222f\n" \ 104 | ".balign 4\n" \ 105 | RSEQ_ASM_U32(__rseq_str(RSEQ_SIG)) "\n" \ 106 | __rseq_str(label) ":\n" \ 107 | teardown \ 108 | "j %l[" __rseq_str(abort_label) "]\n" \ 109 | "222:\n" 110 | 111 | /* 112 | * Store the address of the critical section descriptor structure at 113 | * @cs_label into the @rseq_cs pointer and emit the label @label, which 114 | * is the beginning of the sequence of consecutive assembly instructions. 115 | * 116 | * @label: 117 | * Local label to the beginning of the sequence of consecutive assembly 118 | * instructions. 119 | * @cs_label: 120 | * Source local label to the critical section descriptor structure. 121 | * @rseq_cs: 122 | * Destination pointer where to store the address of the critical 123 | * section descriptor structure. 124 | */ 125 | #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ 126 | RSEQ_INJECT_ASM(1) \ 127 | "la " RSEQ_ASM_TMP_REG_1 ", " __rseq_str(cs_label) "\n" \ 128 | RSEQ_ASM_REG_S RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(rseq_cs) "]\n" \ 129 | __rseq_str(label) ":\n" 130 | 131 | /* Store @value to address @var. */ 132 | #define RSEQ_ASM_OP_STORE(value, var) \ 133 | RSEQ_ASM_REG_S "%[" __rseq_str(value) "], %[" __rseq_str(var) "]\n" 134 | 135 | /* Jump to local label @label when @var != @expect. */ 136 | #define RSEQ_ASM_OP_CBNE(var, expect, label) \ 137 | RSEQ_ASM_REG_L RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(var) "]\n" \ 138 | "bne " RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(expect) "] ," \ 139 | __rseq_str(label) "\n" 140 | 141 | /* 142 | * Jump to local label @label when @var != @expect (32-bit register 143 | * comparison). 144 | */ 145 | #define RSEQ_ASM_OP_CBNE32(var, expect, label) \ 146 | "lw " RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(var) "]\n" \ 147 | "bne " RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(expect) "] ," \ 148 | __rseq_str(label) "\n" 149 | 150 | /* Jump to local label @label when @var == @expect. */ 151 | #define RSEQ_ASM_OP_CBEQ(var, expect, label) \ 152 | RSEQ_ASM_REG_L RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(var) "]\n" \ 153 | "beq " RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(expect) "] ," \ 154 | __rseq_str(label) "\n" 155 | 156 | /* Jump to local label @label when @cpu_id != @current_cpu_id. */ 157 | #define RSEQ_ASM_CBNE_CPU_ID(cpu_id, current_cpu_id, label) \ 158 | RSEQ_INJECT_ASM(2) \ 159 | RSEQ_ASM_OP_CBNE32(current_cpu_id, cpu_id, label) 160 | 161 | /* Load @var into temporary register. */ 162 | #define RSEQ_ASM_OP_R_LOAD(var) \ 163 | RSEQ_ASM_REG_L RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(var) "]\n" 164 | 165 | /* Store from temporary register into @var. */ 166 | #define RSEQ_ASM_OP_R_STORE(var) \ 167 | RSEQ_ASM_REG_S RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(var) "]\n" 168 | 169 | /* Load from address in temporary register+@offset into temporary register. */ 170 | #define RSEQ_ASM_OP_R_LOAD_OFF(offset) \ 171 | "add " RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(offset) "], " \ 172 | RSEQ_ASM_TMP_REG_1 "\n" \ 173 | RSEQ_ASM_REG_L RSEQ_ASM_TMP_REG_1 ", (" RSEQ_ASM_TMP_REG_1 ")\n" 174 | 175 | /* Add @count to temporary register. */ 176 | #define RSEQ_ASM_OP_R_ADD(count) \ 177 | "add " RSEQ_ASM_TMP_REG_1 ", " RSEQ_ASM_TMP_REG_1 \ 178 | ", %[" __rseq_str(count) "]\n" 179 | 180 | /* 181 | * End-of-sequence store of @value to address @var. Emit 182 | * @post_commit_label label after the store instruction. 183 | */ 184 | #define RSEQ_ASM_OP_FINAL_STORE(value, var, post_commit_label) \ 185 | RSEQ_ASM_OP_STORE(value, var) \ 186 | __rseq_str(post_commit_label) ":\n" 187 | 188 | /* 189 | * End-of-sequence store-release of @value to address @var. Emit 190 | * @post_commit_label label after the store instruction. 191 | */ 192 | #define RSEQ_ASM_OP_FINAL_STORE_RELEASE(value, var, post_commit_label) \ 193 | "fence rw, w\n" \ 194 | RSEQ_ASM_OP_STORE(value, var) \ 195 | __rseq_str(post_commit_label) ":\n" 196 | 197 | /* 198 | * End-of-sequence store of temporary register to address @var. Emit 199 | * @post_commit_label label after the store instruction. 200 | */ 201 | #define RSEQ_ASM_OP_R_FINAL_STORE(var, post_commit_label) \ 202 | RSEQ_ASM_REG_S RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(var) "]\n" \ 203 | __rseq_str(post_commit_label) ":\n" 204 | 205 | /* 206 | * Copy @len bytes from @src to @dst. This is an inefficient bytewise 207 | * copy and could be improved in the future. 208 | */ 209 | #define RSEQ_ASM_OP_R_BYTEWISE_MEMCPY(dst, src, len) \ 210 | "beqz %[" __rseq_str(len) "], 333f\n" \ 211 | "mv " RSEQ_ASM_TMP_REG_1 ", %[" __rseq_str(len) "]\n" \ 212 | "mv " RSEQ_ASM_TMP_REG_2 ", %[" __rseq_str(src) "]\n" \ 213 | "mv " RSEQ_ASM_TMP_REG_3 ", %[" __rseq_str(dst) "]\n" \ 214 | "222:\n" \ 215 | "lb " RSEQ_ASM_TMP_REG_4 ", 0(" RSEQ_ASM_TMP_REG_2 ")\n" \ 216 | "sb " RSEQ_ASM_TMP_REG_4 ", 0(" RSEQ_ASM_TMP_REG_3 ")\n" \ 217 | "addi " RSEQ_ASM_TMP_REG_1 ", " RSEQ_ASM_TMP_REG_1 ", -1\n" \ 218 | "addi " RSEQ_ASM_TMP_REG_2 ", " RSEQ_ASM_TMP_REG_2 ", 1\n" \ 219 | "addi " RSEQ_ASM_TMP_REG_3 ", " RSEQ_ASM_TMP_REG_3 ", 1\n" \ 220 | "bnez " RSEQ_ASM_TMP_REG_1 ", 222b\n" \ 221 | "333:\n" 222 | 223 | /* Per-cpu-id indexing. */ 224 | 225 | #define RSEQ_TEMPLATE_INDEX_CPU_ID 226 | #define RSEQ_TEMPLATE_MO_RELAXED 227 | #include "rseq/arch/riscv/bits.h" 228 | #undef RSEQ_TEMPLATE_MO_RELAXED 229 | 230 | #define RSEQ_TEMPLATE_MO_RELEASE 231 | #include "rseq/arch/riscv/bits.h" 232 | #undef RSEQ_TEMPLATE_MO_RELEASE 233 | #undef RSEQ_TEMPLATE_INDEX_CPU_ID 234 | 235 | /* Per-mm-cid indexing. */ 236 | 237 | #define RSEQ_TEMPLATE_INDEX_MM_CID 238 | #define RSEQ_TEMPLATE_MO_RELAXED 239 | #include "rseq/arch/riscv/bits.h" 240 | #undef RSEQ_TEMPLATE_MO_RELAXED 241 | 242 | #define RSEQ_TEMPLATE_MO_RELEASE 243 | #include "rseq/arch/riscv/bits.h" 244 | #undef RSEQ_TEMPLATE_MO_RELEASE 245 | #undef RSEQ_TEMPLATE_INDEX_MM_CID 246 | 247 | /* APIs which are not indexed. */ 248 | 249 | #define RSEQ_TEMPLATE_INDEX_NONE 250 | #define RSEQ_TEMPLATE_MO_RELAXED 251 | #include "rseq/arch/riscv/bits.h" 252 | #undef RSEQ_TEMPLATE_MO_RELAXED 253 | #undef RSEQ_TEMPLATE_INDEX_NONE 254 | -------------------------------------------------------------------------------- /include/rseq/arch/riscv/thread-pointer.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2024 Michael Jeanson */ 3 | 4 | /* 5 | * rseq/arch/riscv/thread-pointer.h 6 | */ 7 | 8 | #ifndef _RSEQ_RISCV_THREAD_POINTER 9 | #define _RSEQ_RISCV_THREAD_POINTER 10 | 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #if __GNUC_PREREQ (10, 3) 18 | static inline __attribute__((always_inline)) 19 | void *rseq_thread_pointer(void) 20 | { 21 | return __builtin_thread_pointer(); 22 | } 23 | #else 24 | static inline __attribute__((always_inline)) 25 | void *rseq_thread_pointer(void) 26 | { 27 | void *__result; 28 | 29 | __asm__ ("mv %0, tp" : "=r" (__result)); 30 | return __result; 31 | } 32 | #endif /* !GCC 10.3 */ 33 | 34 | #ifdef __cplusplus 35 | } 36 | #endif 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /include/rseq/arch/s390.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2018 Vasily Gorbik */ 3 | /* SPDX-FileCopyrightText: 2024 Mathieu Desnoyers */ 4 | 5 | /* 6 | * rseq-s390.h 7 | */ 8 | 9 | /* 10 | * RSEQ_ASM_*() macro helpers are internal to the librseq headers. Those 11 | * are not part of the public API. 12 | */ 13 | 14 | #ifndef _RSEQ_RSEQ_H 15 | #error "Never use directly; include instead." 16 | #endif 17 | 18 | /* 19 | * RSEQ_SIG uses the trap4 instruction. As Linux does not make use of the 20 | * access-register mode nor the linkage stack this instruction will always 21 | * cause a special-operation exception (the trap-enabled bit in the DUCT 22 | * is and will stay 0). The instruction pattern is 23 | * b2 ff 0f ff trap4 4095(%r0) 24 | */ 25 | #define RSEQ_SIG 0xB2FF0FFF 26 | 27 | /* 28 | * Refer to the Linux kernel memory model (LKMM) for documentation of 29 | * the memory barriers. 30 | */ 31 | 32 | /* CPU memory barrier. */ 33 | #define rseq_smp_mb() __asm__ __volatile__ ("bcr 15,0" ::: "memory") 34 | /* CPU read memory barrier */ 35 | #define rseq_smp_rmb() rseq_smp_mb() 36 | /* CPU write memory barrier */ 37 | #define rseq_smp_wmb() rseq_smp_mb() 38 | 39 | /* Acquire: One-way permeable barrier. */ 40 | #define rseq_smp_load_acquire(p) \ 41 | __extension__ ({ \ 42 | rseq_unqual_scalar_typeof(*(p)) ____p1 = RSEQ_READ_ONCE(*(p)); \ 43 | rseq_barrier(); \ 44 | ____p1; \ 45 | }) 46 | 47 | /* Acquire barrier after control dependency. */ 48 | #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() 49 | 50 | /* Release: One-way permeable barrier. */ 51 | #define rseq_smp_store_release(p, v) \ 52 | do { \ 53 | rseq_barrier(); \ 54 | RSEQ_WRITE_ONCE(*(p), v); \ 55 | } while (0) 56 | 57 | /* 58 | * Helper macros to access a variable of long integer type. Only used 59 | * internally in rseq headers. 60 | */ 61 | #ifdef RSEQ_ARCH_S390X 62 | # define RSEQ_ASM_LONG_L "lg" 63 | # define RSEQ_ASM_LONG_S "stg" 64 | # define RSEQ_ASM_LONG_LT_R "ltgr" 65 | # define RSEQ_ASM_LONG_CMP "cg" 66 | # define RSEQ_ASM_LONG_CMP_R "cgr" 67 | # define RSEQ_ASM_LONG_ADDI "aghi" 68 | # define RSEQ_ASM_LONG_ADD_R "agr" 69 | #else 70 | # define RSEQ_ASM_LONG_L "l" 71 | # define RSEQ_ASM_LONG_S "st" 72 | # define RSEQ_ASM_LONG_LT_R "ltr" 73 | # define RSEQ_ASM_LONG_CMP "c" 74 | # define RSEQ_ASM_LONG_CMP_R "cr" 75 | # define RSEQ_ASM_LONG_ADDI "ahi" 76 | # define RSEQ_ASM_LONG_ADD_R "ar" 77 | #endif 78 | 79 | #ifdef RSEQ_ARCH_S390X 80 | # define RSEQ_ASM_U64_PTR(x) ".quad " x 81 | #else 82 | /* 32-bit only supported on big endian. */ 83 | # define RSEQ_ASM_U64_PTR(x) ".long 0x0, " x 84 | #endif 85 | 86 | #define RSEQ_ASM_U32(x) ".long " x 87 | 88 | /* Common architecture support macros. */ 89 | #include "rseq/arch/generic/common.h" 90 | 91 | /* 92 | * Define a critical section abort handler. 93 | * 94 | * @label: 95 | * Local label to the abort handler. 96 | * @teardown: 97 | * Sequence of instructions to run on abort. 98 | * @abort_label: 99 | * C label to jump to at the end of the sequence. 100 | */ 101 | #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \ 102 | ".pushsection __rseq_failure, \"ax\"\n\t" \ 103 | RSEQ_ASM_U32(__rseq_str(RSEQ_SIG)) "\n\t" \ 104 | __rseq_str(label) ":\n\t" \ 105 | teardown \ 106 | "jg %l[" __rseq_str(abort_label) "]\n\t" \ 107 | ".popsection\n\t" 108 | 109 | /* 110 | * Define a critical section teardown handler. 111 | * 112 | * @label: 113 | * Local label to the teardown handler. 114 | * @teardown: 115 | * Sequence of instructions to run on teardown. 116 | * @target_label: 117 | * C label to jump to at the end of the sequence. 118 | */ 119 | #define RSEQ_ASM_DEFINE_TEARDOWN(label, teardown, target_label) \ 120 | ".pushsection __rseq_failure, \"ax\"\n\t" \ 121 | __rseq_str(label) ":\n\t" \ 122 | teardown \ 123 | "jg %l[" __rseq_str(target_label) "]\n\t" \ 124 | ".popsection\n\t" 125 | 126 | /* 127 | * Store the address of the critical section descriptor structure at 128 | * @cs_label into the @rseq_cs pointer and emit the label @label, which 129 | * is the beginning of the sequence of consecutive assembly instructions. 130 | * 131 | * @label: 132 | * Local label to the beginning of the sequence of consecutive assembly 133 | * instructions. 134 | * @cs_label: 135 | * Source local label to the critical section descriptor structure. 136 | * @rseq_cs: 137 | * Destination pointer where to store the address of the critical 138 | * section descriptor structure. 139 | */ 140 | #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ 141 | RSEQ_INJECT_ASM(1) \ 142 | "larl %%r0, " __rseq_str(cs_label) "\n\t" \ 143 | RSEQ_ASM_LONG_S " %%r0, %[" __rseq_str(rseq_cs) "]\n\t" \ 144 | __rseq_str(label) ":\n\t" 145 | 146 | /* Jump to local label @label when @cpu_id != @current_cpu_id. */ 147 | #define RSEQ_ASM_CBNE_CPU_ID(cpu_id, current_cpu_id, label) \ 148 | RSEQ_INJECT_ASM(2) \ 149 | "c %[" __rseq_str(cpu_id) "], %[" __rseq_str(current_cpu_id) "]\n\t" \ 150 | "jnz " __rseq_str(label) "\n\t" 151 | 152 | /* Per-cpu-id indexing. */ 153 | 154 | #define RSEQ_TEMPLATE_INDEX_CPU_ID 155 | #define RSEQ_TEMPLATE_MO_RELAXED 156 | #include "rseq/arch/s390/bits.h" 157 | #undef RSEQ_TEMPLATE_MO_RELAXED 158 | 159 | #define RSEQ_TEMPLATE_MO_RELEASE 160 | #include "rseq/arch/s390/bits.h" 161 | #undef RSEQ_TEMPLATE_MO_RELEASE 162 | #undef RSEQ_TEMPLATE_INDEX_CPU_ID 163 | 164 | /* Per-mm-cid indexing. */ 165 | 166 | #define RSEQ_TEMPLATE_INDEX_MM_CID 167 | #define RSEQ_TEMPLATE_MO_RELAXED 168 | #include "rseq/arch/s390/bits.h" 169 | #undef RSEQ_TEMPLATE_MO_RELAXED 170 | 171 | #define RSEQ_TEMPLATE_MO_RELEASE 172 | #include "rseq/arch/s390/bits.h" 173 | #undef RSEQ_TEMPLATE_MO_RELEASE 174 | #undef RSEQ_TEMPLATE_INDEX_MM_CID 175 | 176 | /* APIs which are not indexed. */ 177 | 178 | #define RSEQ_TEMPLATE_INDEX_NONE 179 | #define RSEQ_TEMPLATE_MO_RELAXED 180 | #include "rseq/arch/s390/bits.h" 181 | #undef RSEQ_TEMPLATE_MO_RELAXED 182 | #undef RSEQ_TEMPLATE_INDEX_NONE 183 | -------------------------------------------------------------------------------- /include/rseq/arch/templates/bits-reset.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2016-2022 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/arch/templates/bits-reset.h 6 | */ 7 | 8 | #undef RSEQ_TEMPLATE_IDENTIFIER 9 | #undef RSEQ_TEMPLATE_INDEX_CPU_ID_FIELD 10 | #undef RSEQ_TEMPLATE_INDEX_CPU_ID_OFFSET 11 | #undef RSEQ_TEMPLATE_SUFFIX 12 | -------------------------------------------------------------------------------- /include/rseq/arch/templates/bits.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2016-2022 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/arch/templates/bits.h 6 | */ 7 | 8 | #ifdef RSEQ_TEMPLATE_INDEX_CPU_ID 9 | # define RSEQ_TEMPLATE_INDEX_CPU_ID_OFFSET RSEQ_ASM_CPU_ID_OFFSET 10 | # define RSEQ_TEMPLATE_INDEX_CPU_ID_FIELD cpu_id 11 | # ifdef RSEQ_TEMPLATE_MO_RELEASE 12 | # define RSEQ_TEMPLATE_SUFFIX _release_cpu_id 13 | # elif defined (RSEQ_TEMPLATE_MO_RELAXED) 14 | # define RSEQ_TEMPLATE_SUFFIX _relaxed_cpu_id 15 | # else 16 | # error "Never use directly; include instead." 17 | # endif 18 | #elif defined(RSEQ_TEMPLATE_INDEX_MM_CID) 19 | # define RSEQ_TEMPLATE_INDEX_CPU_ID_OFFSET RSEQ_ASM_MM_CID_OFFSET 20 | # define RSEQ_TEMPLATE_INDEX_CPU_ID_FIELD mm_cid 21 | # ifdef RSEQ_TEMPLATE_MO_RELEASE 22 | # define RSEQ_TEMPLATE_SUFFIX _release_mm_cid 23 | # elif defined (RSEQ_TEMPLATE_MO_RELAXED) 24 | # define RSEQ_TEMPLATE_SUFFIX _relaxed_mm_cid 25 | # else 26 | # error "Never use directly; include instead." 27 | # endif 28 | #elif defined (RSEQ_TEMPLATE_INDEX_NONE) 29 | # ifdef RSEQ_TEMPLATE_MO_RELEASE 30 | # define RSEQ_TEMPLATE_SUFFIX _release 31 | # elif defined (RSEQ_TEMPLATE_MO_RELAXED) 32 | # define RSEQ_TEMPLATE_SUFFIX _relaxed 33 | # else 34 | # error "Never use directly; include instead." 35 | # endif 36 | #else 37 | # error "Never use directly; include instead." 38 | #endif 39 | 40 | #define RSEQ_TEMPLATE_IDENTIFIER(x) RSEQ_COMBINE_TOKENS(x, RSEQ_TEMPLATE_SUFFIX) 41 | 42 | -------------------------------------------------------------------------------- /include/rseq/arch/x86.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2016-2024 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/arch/x86.h 6 | */ 7 | 8 | #ifndef _RSEQ_RSEQ_H 9 | #error "Never use directly; include instead." 10 | #endif 11 | 12 | #include 13 | 14 | /* 15 | * RSEQ_ASM_*() macro helpers are internal to the librseq headers. Those 16 | * are not part of the public API. 17 | */ 18 | 19 | /* 20 | * RSEQ_SIG is used with the following reserved undefined instructions, which 21 | * trap in user-space: 22 | * 23 | * x86-32: 0f b9 3d 53 30 05 53 ud1 0x53053053,%edi 24 | * x86-64: 0f b9 3d 53 30 05 53 ud1 0x53053053(%rip),%edi 25 | */ 26 | #define RSEQ_SIG 0x53053053 27 | 28 | /* 29 | * Due to a compiler optimization bug in gcc-8 with asm goto and TLS asm input 30 | * operands, we cannot use "m" input operands, and rather pass the __rseq_abi 31 | * address through a "r" input operand. 32 | */ 33 | 34 | /* 35 | * Offset of cpu_id, rseq_cs, and mm_cid fields in struct rseq. Those 36 | * are defined explicitly as macros to be used from assembly. 37 | */ 38 | #define RSEQ_ASM_CPU_ID_OFFSET 4 39 | #define RSEQ_ASM_CS_OFFSET 8 40 | #define RSEQ_ASM_MM_CID_OFFSET 24 41 | 42 | /* 43 | * Refer to the Linux kernel memory model (LKMM) for documentation of 44 | * the memory barriers. Expect all x86 hardware to be x86-TSO (Total 45 | * Store Order). 46 | */ 47 | 48 | /* CPU memory barrier. */ 49 | #define rseq_smp_mb() \ 50 | __asm__ __volatile__ ("lock; addl $0,-128(%%rsp)" ::: "memory", "cc") 51 | /* CPU read memory barrier */ 52 | #define rseq_smp_rmb() rseq_barrier() 53 | /* CPU write memory barrier */ 54 | #define rseq_smp_wmb() rseq_barrier() 55 | 56 | /* Acquire: One-way permeable barrier. */ 57 | #define rseq_smp_load_acquire(p) \ 58 | __extension__ ({ \ 59 | rseq_unqual_scalar_typeof(*(p)) ____p1 = RSEQ_READ_ONCE(*(p)); \ 60 | rseq_barrier(); \ 61 | ____p1; \ 62 | }) 63 | 64 | /* Acquire barrier after control dependency. */ 65 | #define rseq_smp_acquire__after_ctrl_dep() rseq_smp_rmb() 66 | 67 | /* Release: One-way permeable barrier. */ 68 | #define rseq_smp_store_release(p, v) \ 69 | do { \ 70 | rseq_barrier(); \ 71 | RSEQ_WRITE_ONCE(*(p), v); \ 72 | } while (0) 73 | 74 | /* Segment selector for the thread pointer. */ 75 | #ifdef RSEQ_ARCH_AMD64 76 | # define RSEQ_ASM_TP_SEGMENT %%fs 77 | #else 78 | # define RSEQ_ASM_TP_SEGMENT %%gs 79 | #endif 80 | 81 | /* 82 | * Helper macro to define a variable of pointer type stored in a 64-bit 83 | * integer. Only used internally in rseq headers. 84 | */ 85 | #ifdef RSEQ_ARCH_AMD64 86 | # define RSEQ_ASM_U64_PTR(x) ".quad " x 87 | #else 88 | # define RSEQ_ASM_U64_PTR(x) ".long " x ", 0x0" 89 | #endif 90 | 91 | #define RSEQ_ASM_U32(x) ".long " x 92 | 93 | /* Common architecture support macros. */ 94 | #include "rseq/arch/generic/common.h" 95 | 96 | /* 97 | * Define a critical section abort handler. 98 | * 99 | * @label: 100 | * Local label to the abort handler. 101 | * @teardown: 102 | * Sequence of instructions to run on abort. 103 | * @abort_label: 104 | * C label to jump to at the end of the sequence. 105 | */ 106 | #define RSEQ_ASM_DEFINE_ABORT(label, teardown, abort_label) \ 107 | ".pushsection __rseq_failure, \"ax\"\n\t" \ 108 | /* \ 109 | * Disassembler-friendly signature: \ 110 | * x86-32: ud1 ,%edi \ 111 | * x86-64: ud1 (%rip),%edi \ 112 | */ \ 113 | ".byte 0x0f, 0xb9, 0x3d\n\t" \ 114 | ".long " __rseq_str(RSEQ_SIG) "\n\t" \ 115 | __rseq_str(label) ":\n\t" \ 116 | teardown \ 117 | "jmp %l[" __rseq_str(abort_label) "]\n\t" \ 118 | ".popsection\n\t" 119 | 120 | /* 121 | * Define a critical section teardown handler. 122 | * 123 | * @label: 124 | * Local label to the teardown handler. 125 | * @teardown: 126 | * Sequence of instructions to run on teardown. 127 | * @target_label: 128 | * C label to jump to at the end of the sequence. 129 | */ 130 | #define RSEQ_ASM_DEFINE_TEARDOWN(label, teardown, target_label) \ 131 | ".pushsection __rseq_failure, \"ax\"\n\t" \ 132 | __rseq_str(label) ":\n\t" \ 133 | teardown \ 134 | "jmp %l[" __rseq_str(target_label) "]\n\t" \ 135 | ".popsection\n\t" 136 | 137 | /* 138 | * Store the address of the critical section descriptor structure at 139 | * @cs_label into the @rseq_cs pointer and emit the label @label, which 140 | * is the beginning of the sequence of consecutive assembly instructions. 141 | * 142 | * @label: 143 | * Local label to the beginning of the sequence of consecutive assembly 144 | * instructions. 145 | * @cs_label: 146 | * Source local label to the critical section descriptor structure. 147 | * @rseq_cs: 148 | * Destination pointer where to store the address of the critical 149 | * section descriptor structure. 150 | */ 151 | #ifdef RSEQ_ARCH_AMD64 152 | #define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs) \ 153 | RSEQ_INJECT_ASM(1) \ 154 | "leaq " __rseq_str(cs_label) "(%%rip), %%rax\n\t" \ 155 | "movq %%rax, " __rseq_str(rseq_cs) "\n\t" \ 156 | __rseq_str(label) ":\n\t" 157 | #else 158 | # define RSEQ_ASM_REF_LABEL 881 159 | /* 160 | * Use ip-relative addressing to get the address to the rseq critical 161 | * section descriptor. On x86-32, this requires a "call" instruction to 162 | * get the instruction pointer, which modifies the stack. Beware of this 163 | * side-effect if this scheme is used within a rseq critical section. 164 | * This computation is performed immediately before storing the rseq_cs, 165 | * which is outside of the critical section. 166 | * Balance call/ret to help speculation. 167 | * Save this ip address to ref_ip for use by the critical section so 168 | * ip-relative addressing can be done without modifying the stack 169 | * pointer by using ref_ip and calculating the relative offset from 170 | * ref_label. 171 | */ 172 | # define RSEQ_ASM_STORE_RSEQ_CS(label, cs_label, rseq_cs, ref_ip, ref_label) \ 173 | "jmp 779f\n\t" \ 174 | "880:\n\t" \ 175 | "movl (%%esp), %%eax\n\t" \ 176 | "ret\n\t" \ 177 | "779:\n\t" \ 178 | "call 880b\n\t" \ 179 | __rseq_str(ref_label) ":\n\t" \ 180 | "movl %%eax, " __rseq_str(ref_ip) "\n\t" \ 181 | "leal (" __rseq_str(cs_label) " - " __rseq_str(ref_label) "b)(%%eax), %%eax\n\t" \ 182 | "movl %%eax, " __rseq_str(rseq_cs) "\n\t" \ 183 | __rseq_str(label) ":\n\t" 184 | #endif 185 | 186 | /* Jump to local label @label when @cpu_id != @current_cpu_id. */ 187 | #define RSEQ_ASM_CBNE_CPU_ID(cpu_id, current_cpu_id, label) \ 188 | RSEQ_INJECT_ASM(2) \ 189 | "cmpl %[" __rseq_str(cpu_id) "], " __rseq_str(current_cpu_id) "\n\t" \ 190 | "jnz " __rseq_str(label) "\n\t" 191 | 192 | /* Per-cpu-id indexing. */ 193 | 194 | #define RSEQ_TEMPLATE_INDEX_CPU_ID 195 | #define RSEQ_TEMPLATE_MO_RELAXED 196 | #include "rseq/arch/x86/bits.h" 197 | #undef RSEQ_TEMPLATE_MO_RELAXED 198 | 199 | #define RSEQ_TEMPLATE_MO_RELEASE 200 | #include "rseq/arch/x86/bits.h" 201 | #undef RSEQ_TEMPLATE_MO_RELEASE 202 | #undef RSEQ_TEMPLATE_INDEX_CPU_ID 203 | 204 | /* Per-mm-cid indexing. */ 205 | 206 | #define RSEQ_TEMPLATE_INDEX_MM_CID 207 | #define RSEQ_TEMPLATE_MO_RELAXED 208 | #include "rseq/arch/x86/bits.h" 209 | #undef RSEQ_TEMPLATE_MO_RELAXED 210 | 211 | #define RSEQ_TEMPLATE_MO_RELEASE 212 | #include "rseq/arch/x86/bits.h" 213 | #undef RSEQ_TEMPLATE_MO_RELEASE 214 | #undef RSEQ_TEMPLATE_INDEX_MM_CID 215 | 216 | /* APIs which are not indexed. */ 217 | 218 | #define RSEQ_TEMPLATE_INDEX_NONE 219 | #define RSEQ_TEMPLATE_MO_RELAXED 220 | #include "rseq/arch/x86/bits.h" 221 | #undef RSEQ_TEMPLATE_MO_RELAXED 222 | #undef RSEQ_TEMPLATE_INDEX_NONE 223 | -------------------------------------------------------------------------------- /include/rseq/arch/x86/thread-pointer.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2021 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/arch/x86/thread-pointer.h 6 | */ 7 | 8 | #ifndef _RSEQ_X86_THREAD_POINTER 9 | #define _RSEQ_X86_THREAD_POINTER 10 | 11 | #include 12 | 13 | #ifdef __cplusplus 14 | extern "C" { 15 | #endif 16 | 17 | #if __GNUC_PREREQ (11, 1) 18 | static inline __attribute__((always_inline)) 19 | void *rseq_thread_pointer(void) 20 | { 21 | return __builtin_thread_pointer(); 22 | } 23 | #else 24 | static inline __attribute__((always_inline)) 25 | void *rseq_thread_pointer(void) 26 | { 27 | void *__result; 28 | 29 | # ifdef __x86_64__ 30 | __asm__ ("mov %%fs:0, %0" : "=r" (__result)); 31 | # else 32 | __asm__ ("mov %%gs:0, %0" : "=r" (__result)); 33 | # endif 34 | return __result; 35 | } 36 | #endif /* !GCC 11 */ 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /include/rseq/compiler.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2021 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/compiler.h 6 | * 7 | * Work-around asm goto compiler bugs. 8 | */ 9 | 10 | #ifndef _RSEQ_COMPILER_H 11 | #define _RSEQ_COMPILER_H 12 | 13 | #if defined __cplusplus 14 | # include /* for std::remove_cv */ 15 | #endif 16 | 17 | #define rseq_likely(x) __builtin_expect(!!(x), 1) 18 | #define rseq_unlikely(x) __builtin_expect(!!(x), 0) 19 | #define rseq_barrier() __asm__ __volatile__("" : : : "memory") 20 | 21 | /* 22 | * Instruct the compiler to perform only a single access to a variable 23 | * (prohibits merging and refetching). The compiler is also forbidden to reorder 24 | * successive instances of RSEQ_ACCESS_ONCE(), but only when the compiler is aware of 25 | * particular ordering. Compiler ordering can be ensured, for example, by 26 | * putting two RSEQ_ACCESS_ONCE() in separate C statements. 27 | * 28 | * This macro does absolutely -nothing- to prevent the CPU from reordering, 29 | * merging, or refetching absolutely anything at any time. Its main intended 30 | * use is to mediate communication between process-level code and irq/NMI 31 | * handlers, all running on the same CPU. 32 | */ 33 | #define RSEQ_ACCESS_ONCE(x) (*(__volatile__ __typeof__(x) *)&(x)) 34 | 35 | #define RSEQ_WRITE_ONCE(x, v) __extension__ ({ RSEQ_ACCESS_ONCE(x) = (v); }) 36 | #define RSEQ_READ_ONCE(x) RSEQ_ACCESS_ONCE(x) 37 | 38 | /* 39 | * gcc prior to 4.8.2 miscompiles asm goto. 40 | * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58670 41 | * 42 | * gcc prior to 8.1.0 miscompiles asm goto at O1. 43 | * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103908 44 | * 45 | * clang prior to version 13.0.1 miscompiles asm goto at O2. 46 | * https://github.com/llvm/llvm-project/issues/52735 47 | * 48 | * Work around these issues by adding a volatile inline asm with 49 | * memory clobber in the fallthrough after the asm goto and at each 50 | * label target. Emit this for all compilers in case other similar 51 | * issues are found in the future. 52 | */ 53 | #define rseq_after_asm_goto() __asm__ __volatile__ ("" : : : "memory") 54 | 55 | /* Combine two tokens. */ 56 | #define RSEQ__COMBINE_TOKENS(_tokena, _tokenb) \ 57 | _tokena##_tokenb 58 | #define RSEQ_COMBINE_TOKENS(_tokena, _tokenb) \ 59 | RSEQ__COMBINE_TOKENS(_tokena, _tokenb) 60 | 61 | #if defined(__SIZEOF_LONG__) 62 | #define RSEQ_BITS_PER_LONG (__SIZEOF_LONG__ * 8) 63 | #elif defined(_LP64) 64 | #define RSEQ_BITS_PER_LONG 64 65 | #else 66 | #define RSEQ_BITS_PER_LONG 32 67 | #endif 68 | 69 | #ifdef __cplusplus 70 | #define rseq_unqual_scalar_typeof(x) \ 71 | std::remove_cv::type>::type 72 | #else 73 | #define rseq_scalar_type_to_expr(type) \ 74 | unsigned type: (unsigned type)0, \ 75 | signed type: (signed type)0 76 | 77 | /* 78 | * Use C11 _Generic to express unqualified type from expression. This removes 79 | * volatile qualifier from expression type. 80 | */ 81 | #define rseq_unqual_scalar_typeof(x) \ 82 | __typeof__( \ 83 | _Generic((x), \ 84 | char: (char)0, \ 85 | rseq_scalar_type_to_expr(char), \ 86 | rseq_scalar_type_to_expr(short), \ 87 | rseq_scalar_type_to_expr(int), \ 88 | rseq_scalar_type_to_expr(long), \ 89 | rseq_scalar_type_to_expr(long long), \ 90 | default: (x) \ 91 | ) \ 92 | ) 93 | #endif 94 | 95 | /* 96 | * RSEQ_PARAM_SELECT_ARG1 97 | * 98 | * Select second argument. Use inside macros to implement optional last 99 | * macro argument, such as: 100 | * 101 | * #define macro(_a, _b, _c, _optional...) \ 102 | * RSEQ_PARAM_SELECT_ARG1(_, ##_optional, do_default_macro()) 103 | */ 104 | #define RSEQ_PARAM_SELECT_ARG1(_arg0, _arg1, ...) _arg1 105 | 106 | #endif /* _RSEQ_COMPILER_H */ 107 | -------------------------------------------------------------------------------- /include/rseq/inject.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2016-2022 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/inject.h 6 | */ 7 | 8 | #ifndef _RSEQ_INJECT_H 9 | #define _RSEQ_INJECT_H 10 | 11 | /* 12 | * Empty code injection macros, override when testing. 13 | * It is important to consider that the ASM injection macros need to be 14 | * fully reentrant (e.g. do not modify the stack). 15 | */ 16 | 17 | #ifndef RSEQ_INJECT_ASM 18 | #define RSEQ_INJECT_ASM(n) 19 | #endif 20 | 21 | #ifndef RSEQ_INJECT_C 22 | #define RSEQ_INJECT_C(n) 23 | #endif 24 | 25 | #ifndef RSEQ_INJECT_INPUT 26 | #define RSEQ_INJECT_INPUT 27 | #endif 28 | 29 | #ifndef RSEQ_INJECT_CLOBBER 30 | #define RSEQ_INJECT_CLOBBER 31 | #endif 32 | 33 | #ifndef RSEQ_INJECT_FAILED 34 | #define RSEQ_INJECT_FAILED 35 | #endif 36 | 37 | #endif /* _RSEQ_INJECT_H */ 38 | -------------------------------------------------------------------------------- /include/rseq/pseudocode.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2024 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/pseudocode.h 6 | * 7 | * This file contains the pseudo-code of rseq critical section helpers, 8 | * to be used as reference for architecture implementation. 9 | */ 10 | 11 | #ifndef _RSEQ_PSEUDOCODE_H 12 | #define _RSEQ_PSEUDOCODE_H 13 | 14 | /* 15 | * Pseudo-code conventions: 16 | * 17 | * rX: Register X 18 | * [var]: Register associated with C variable "var". 19 | * [label]: Jump target associated with C label "label". 20 | * 21 | * load(rX, address): load from memory address to rX 22 | * store(rX, address): store to memory address from rX 23 | * cbne(rX, rY, target): compare-and-branch to target if rX != rY 24 | * cbeq(rX, rY, target): compare-and-branch to target if rX == rY 25 | * add(rX, rY): add rY to register rX 26 | * memcpy(dest_address, src_address, len): copy len bytes from src_address to dst_address 27 | * 28 | * Critical section helpers identifier convention: 29 | * - Begin with an "rseq_" prefix, 30 | * - Followed by their simplified pseudo-code, 31 | * - Followed by __ and the type (or eventually types) on which the API 32 | * applies (similar to the approach taken for C++ mangling). 33 | */ 34 | 35 | /* 36 | * rseq_load_cbne_store(v, expect, newv) 37 | * 38 | * Pseudo-code: 39 | * load(r1, [v]) 40 | * cbne(r1, [expect], [ne]) 41 | * store([newv], [v]) 42 | * 43 | * Return values: 44 | * success: 0 45 | * ne: 1 46 | * abort: -1 47 | */ 48 | 49 | /* 50 | * rseq_load_add_store(v, count) 51 | * 52 | * Pseudo-code: 53 | * load(r1, [v]) 54 | * add(r1, [count]) 55 | * store(r1, [v]) 56 | * 57 | * Return values: 58 | * success: 0 59 | * abort: -1 60 | */ 61 | 62 | /* 63 | * rseq_load_cbeq_store_add_load_store(v, expectnot, voffp, load) 64 | * 65 | * Pseudo-code: 66 | * load(r1, [v]) 67 | * cbeq(r1, [expectnot], [eq]) 68 | * store(r1, [load]) 69 | * add(r1, [voffp]) 70 | * load(r2, r1) 71 | * store(r2, [v]) 72 | * 73 | * Return values: 74 | * success: 0 75 | * eq: 1 76 | * abort: -1 77 | */ 78 | 79 | /* 80 | * rseq_load_add_load_load_add_store(ptr, off, inc) 81 | * 82 | * Pseudo-code: 83 | * load(r1, [ptr]) 84 | * add(r1, [off]) 85 | * load(r2, r1) 86 | * load(r3, r2) 87 | * add(r3, [inc]) 88 | * store(r3, r2) 89 | * 90 | * Return values: 91 | * success: 0 92 | * abort: -1 93 | */ 94 | 95 | /* 96 | * rseq_load_cbne_load_cbne_store(v, expect, v2, expect2, newv) 97 | * 98 | * Pseudo-code: 99 | * load(r1, [v]) 100 | * cbne(r1, [expect], [ne]) 101 | * load(r2, [v2]) 102 | * cbne(r2, [expect2], [ne]) 103 | * store([newv], [v]) 104 | * 105 | * Return values: 106 | * success: 0 107 | * ne: 1 108 | * abort: -1 109 | */ 110 | 111 | /* 112 | * rseq_load_cbne_store_store(v, expect, v2, newv2, newv) 113 | * 114 | * Pseudo-code: 115 | * load(r1, [v]) 116 | * cbne(r1, [expect], [ne]) 117 | * store([newv2], [v2]) // Store attempt 118 | * store([newv], [v]) // Final store 119 | * 120 | * Return values: 121 | * success: 0 122 | * ne: 1 123 | * abort: -1 124 | */ 125 | 126 | /* 127 | * rseq_load_cbne_memcpy_store(v, expect, dst, src, len, newv) 128 | * 129 | * Pseudo-code: 130 | * load(r1, [v]) 131 | * cbne(r1, [expect], [ne]) 132 | * memcpy([dst], [src], [len]) // Memory copy attempt 133 | * store([newv], [v]) // Final store 134 | * 135 | * Return values: 136 | * success: 0 137 | * ne: 1 138 | * abort: -1 139 | */ 140 | 141 | #endif /* _RSEQ_PSEUDOCODE_H */ 142 | -------------------------------------------------------------------------------- /include/rseq/thread-pointer.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2021 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/thread-pointer.h 6 | */ 7 | 8 | #ifndef _RSEQ_THREAD_POINTER_H 9 | #define _RSEQ_THREAD_POINTER_H 10 | 11 | #if (defined(__amd64__) \ 12 | || defined(__amd64) \ 13 | || defined(__x86_64__) \ 14 | || defined(__x86_64) \ 15 | || defined(__i386__) \ 16 | || defined(__i386)) 17 | 18 | #include 19 | 20 | #elif (defined(__powerpc64__) \ 21 | || defined(__ppc64__) \ 22 | || defined(__powerpc__) \ 23 | || defined(__powerpc) \ 24 | || defined(__ppc__)) 25 | 26 | #include 27 | 28 | #elif defined(__riscv) 29 | 30 | #include 31 | 32 | #elif defined(__loongarch__) 33 | 34 | #include 35 | 36 | #else 37 | 38 | #include 39 | 40 | #endif 41 | 42 | #endif /* _RSEQ_THREAD_POINTER_H */ 43 | -------------------------------------------------------------------------------- /include/rseq/utils.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | /* SPDX-FileCopyrightText: 2016-2022 Mathieu Desnoyers */ 3 | 4 | /* 5 | * rseq/utils.h 6 | */ 7 | 8 | #ifndef _RSEQ_UTILS_H 9 | #define _RSEQ_UTILS_H 10 | 11 | #include 12 | #include 13 | 14 | #ifndef rseq_sizeof_field 15 | #define rseq_sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) 16 | #endif 17 | 18 | #ifndef rseq_offsetofend 19 | #define rseq_offsetofend(TYPE, MEMBER) \ 20 | (offsetof(TYPE, MEMBER) + rseq_sizeof_field(TYPE, MEMBER)) 21 | #endif 22 | 23 | #define __rseq_str_1(x) #x 24 | #define __rseq_str(x) __rseq_str_1(x) 25 | 26 | #define rseq_log(fmt, ...) \ 27 | fprintf(stderr, fmt "(in %s() at " __FILE__ ":" __rseq_str(__LINE__)"\n", \ 28 | ## __VA_ARGS__, __func__) 29 | 30 | #define rseq_bug(fmt, ...) \ 31 | do { \ 32 | rseq_log(fmt, ## __VA_ARGS__); \ 33 | abort(); \ 34 | } while (0) 35 | 36 | #endif /* _RSEQ_UTILS_H */ 37 | -------------------------------------------------------------------------------- /m4/ae_config_feature.m4: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-Autoconf-exception-macro 2 | # SPDX-FileCopyrightText: 2020 Michael Jeanson 3 | # SPDX-FileCopyrightText: 2008 Francesco Salvestrini 4 | # 5 | # SYNOPSIS 6 | # 7 | # AE_FEATURE(FEATURE-NAME, FEATURE-DESCRIPTION, 8 | # ACTION-IF-GIVEN?, ACTION-IF-NOT-GIVEN?, 9 | # ACTION-IF-ENABLED?, ACTION-IF-NOT-ENABLED?) 10 | # 11 | # DESCRIPTION 12 | # 13 | # AE_FEATURE is a simple wrapper for AC_ARG_ENABLE. 14 | # 15 | # FEATURE-NAME should consist only of alphanumeric characters, dashes, 16 | # plus signs, and dots. 17 | # 18 | # FEATURE-DESCRIPTION will be formatted with AS_HELP_STRING. 19 | # 20 | # If the user gave configure the option --enable-FEATURE or --disable-FEATURE, 21 | # run shell commands ACTION-IF-GIVEN. If neither option was given, run shell 22 | # commands ACTION-IF-NOT-GIVEN. The name feature indicates an optional 23 | # 24 | # If the feature is enabled, run shell commands ACTION-IF-ENABLED, if it is 25 | # disabled, run shell commands ACTION-IF-NOT-ENABLED. 26 | # 27 | # A FEATURE has 3 different states, enabled, disabled and undefined. The first 28 | # two are self explanatory, the third state means it's disabled by default 29 | # and it was not explicitly enabled or disabled by the user or by the 30 | # AE_FEATURE_ENABLE and AE_FEATURE_DISABLE macros. 31 | # 32 | # A feature is disabled by default, in order to change this behaviour use the 33 | # AE_FEATURE_DEFAULT_ENABLE and AE_FEATURE_DEFAULT_DISABLE 34 | # macros. 35 | # 36 | # A simple example: 37 | # 38 | # AE_FEATURE_DEFAULT_ENABLE 39 | # AE_FEATURE(feature_xxxxx, [turns on/off XXXXX support]) 40 | # 41 | # ... 42 | # 43 | # AE_FEATURE_DEFAULT_DISABLE 44 | # AE_FEATURE(feature_yyyyy, [turns on/off YYYYY support]) 45 | # AM_CONDITIONAL(YYYYY, AE_IS_FEATURE_ENABLED([feature_yyyyy])) 46 | # 47 | # AE_FEATURE_DEFAULT_ENABLE 48 | # AE_FEATURE(...) 49 | # 50 | # ... 51 | # 52 | # Use AE_FEATURE_ENABLE or AE_FEATURE_DISABLE in order to 53 | # enable or disable a specific feature. 54 | # 55 | # Another simple example: 56 | # 57 | # AS_IF([some_test_here],[AE_FEATURE_ENABLE(feature_xxxxx)],[]) 58 | # 59 | # AE_FEATURE(feature_xxxxx, [turns on/off XXXXX support], 60 | # HAVE_XXXXX, [Define if you want XXXXX support]) 61 | # AE_FEATURE(feature_yyyyy, [turns on/off YYYYY support], 62 | # HAVE_YYYYY, [Define if you want YYYYY support]) 63 | # 64 | # ... 65 | # 66 | # NOTE: AE_FEATURE_ENABLE/DISABLE() must be placed first of the relative 67 | # AE_FEATURE() macro if you want the the proper ACTION-IF-ENABLED and 68 | # ACTION-IF-NOT-ENABLED to run. 69 | 70 | #serial 3 71 | 72 | 73 | # AE_FEATURE_DEFAULT_ENABLE: The next feature defined with AE_FEATURE will 74 | # default to enable. 75 | AC_DEFUN([AE_FEATURE_DEFAULT_ENABLE], [ 76 | m4_define([ae_feature_default_arg], [yes]) 77 | m4_define([ae_feature_default_switch], [disable]) 78 | ]) 79 | 80 | 81 | # AE_FEATURE_DEFAULT_DISABLE: The next feature defined with AE_FEATURE will 82 | # default to disable. 83 | # 84 | AC_DEFUN([AE_FEATURE_DEFAULT_DISABLE], [ 85 | m4_define([ae_feature_default_arg], [no]) 86 | m4_define([ae_feature_default_switch], [enable]) 87 | ]) 88 | 89 | 90 | # AE_FEATURE_ENABLE(FEATURE-NAME): Enable the FEATURE, this will override the 91 | # user's choice if it was made. 92 | # 93 | AC_DEFUN([AE_FEATURE_ENABLE],[ dnl 94 | enable_[]patsubst([$1], -, _)[]=yes 95 | ]) 96 | 97 | 98 | # AE_FEATURE_DISABLE(FEATURE-NAME): Disable the FEATURE, this will override the 99 | # user's choice if it was made. 100 | # 101 | AC_DEFUN([AE_FEATURE_DISABLE],[ dnl 102 | enable_[]patsubst([$1], -, _)[]=no 103 | ]) 104 | 105 | 106 | # AE_IF_FEATURE_ENABLED(FEATURE-NAME, ACTION-IF-ENABLED, ACTION-IF-NOT-ENABLED?): 107 | # Run shell code ACTION-IF-ENABLED if the FEATURE is enabled, otherwise run 108 | # shell code ACTION-IF-NOT-ENABLED. 109 | # 110 | AC_DEFUN([AE_IF_FEATURE_ENABLED],[ dnl 111 | m4_pushdef([FEATURE], patsubst([$1], -, _))dnl 112 | 113 | AS_IF([test "$enable_[]FEATURE[]" = yes],[ dnl 114 | $2 115 | ],[: dnl 116 | $3 117 | ]) 118 | ]) 119 | 120 | 121 | # AE_IF_FEATURE_NOT_ENABLED(FEATURE-NAME, ACTION-IF-NOT-ENABLED, 122 | # ACTION-IF-NOT-NOT-ENABLED?): 123 | # Run shell code ACTION-IF-NOT-ENABLED if the FEATURE is not explicitly 124 | # enabled, otherwise run shell code ACTION-IF-NOT-NOT-DISABLED. 125 | # 126 | # The distinction with AE_IF_FEATURE_DISABLED is that this will also 127 | # match a feature that is undefined. 128 | # 129 | # A feature is undefined when it's disabled by default and was not explicitly 130 | # enabled or disabled by the user or by AE_FEATURE_ENABLE/DISABLE. 131 | # 132 | AC_DEFUN([AE_IF_FEATURE_NOT_ENABLED],[ dnl 133 | m4_pushdef([FEATURE], patsubst([$1], -, _))dnl 134 | 135 | AS_IF([test "$enable_[]FEATURE[]" != yes],[ dnl 136 | $2 137 | ],[: dnl 138 | $3 139 | ]) 140 | ]) 141 | 142 | 143 | # AE_IF_FEATURE_DISABLED(FEATURE-NAME, ACTION-IF-DISABLED, ACTION-IF-NOT-DISABLED?): 144 | # Run shell code ACTION-IF-DISABLED if the FEATURE is disabled, otherwise run 145 | # shell code ACTION-IF-NOT-DISABLED. 146 | # 147 | AC_DEFUN([AE_IF_FEATURE_DISABLED],[ dnl 148 | m4_pushdef([FEATURE], patsubst([$1], -, _))dnl 149 | 150 | AS_IF([test "$enable_[]FEATURE[]" = no],[ dnl 151 | $2 152 | ],[: dnl 153 | $3 154 | ]) 155 | ]) 156 | 157 | 158 | # AE_IF_FEATURE_UNDEF(FEATURE-NAME, ACTION-IF-UNDEF, ACTION-IF-NOT-UNDEF?): 159 | # Run shell code ACTION-IF-UNDEF if the FEATURE is undefined, otherwise run 160 | # shell code ACTION-IF-NOT-UNDEF. 161 | # 162 | # A feature is undefined when it's disabled by default and was not explicitly 163 | # enabled or disabled by the user or by AE_FEATURE_ENABLE/DISABLE. 164 | # 165 | AC_DEFUN([AE_IF_FEATURE_UNDEF],[ dnl 166 | m4_pushdef([FEATURE], patsubst([$1], -, _))dnl 167 | 168 | AS_IF([test "x$enable_[]FEATURE[]" = x],[ dnl 169 | $2 170 | ],[: dnl 171 | $3 172 | ]) 173 | ]) 174 | 175 | 176 | # AE_IS_FEATURE_ENABLED(FEATURE-NAME): outputs a shell condition (suitable 177 | # for use in a shell if statement) that will return true if the FEATURE is 178 | # enabled. 179 | # 180 | AC_DEFUN([AE_IS_FEATURE_ENABLED],[dnl 181 | m4_pushdef([FEATURE], patsubst([$1], -, _))dnl 182 | test "x$enable_[]FEATURE[]" = xyes dnl 183 | ]) 184 | 185 | 186 | dnl Disabled by default, unless already overridden 187 | m4_ifndef([ae_feature_default_arg],[AE_FEATURE_DEFAULT_DISABLE]) 188 | 189 | 190 | # AE_FEATURE(FEATURE-NAME, FEATURE-DESCRIPTION, 191 | # ACTION-IF-GIVEN?, ACTION-IF-NOT-GIVEN?, 192 | # ACTION-IF-ENABLED?, ACTION-IF-NOT-ENABLED?): 193 | # 194 | # 195 | AC_DEFUN([AE_FEATURE],[ dnl 196 | m4_pushdef([FEATURE], patsubst([$1], -, _))dnl 197 | 198 | dnl If the option wasn't specified and the default is enabled, set enable_FEATURE to yes 199 | AS_IF([test "x$enable_[]FEATURE[]" = x && test "ae_feature_default_arg" = yes],[ dnl 200 | enable_[]FEATURE[]="ae_feature_default_arg" 201 | ]) 202 | 203 | AC_ARG_ENABLE([$1], 204 | AS_HELP_STRING([--ae_feature_default_switch-$1],dnl 205 | [$2]),[ 206 | case "${enableval}" in 207 | yes) 208 | enable_[]FEATURE[]=yes 209 | ;; 210 | no) 211 | enable_[]FEATURE[]=no 212 | ;; 213 | *) 214 | AC_MSG_ERROR([bad value ${enableval} for feature --$1]) 215 | ;; 216 | esac 217 | 218 | $3 219 | ],[: dnl 220 | $4 221 | ]) 222 | 223 | AS_IF([test "$enable_[]FEATURE[]" = yes],[: dnl 224 | $5 225 | ],[: dnl 226 | $6 227 | ]) 228 | 229 | m4_popdef([FEATURE])dnl 230 | ]) 231 | -------------------------------------------------------------------------------- /m4/ax_append_compile_flags.m4: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: FSFAP 2 | # ============================================================================ 3 | # https://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html 4 | # ============================================================================ 5 | # 6 | # SYNOPSIS 7 | # 8 | # AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) 9 | # 10 | # DESCRIPTION 11 | # 12 | # For every FLAG1, FLAG2 it is checked whether the compiler works with the 13 | # flag. If it does, the flag is added FLAGS-VARIABLE 14 | # 15 | # If FLAGS-VARIABLE is not specified, the current language's flags (e.g. 16 | # CFLAGS) is used. During the check the flag is always added to the 17 | # current language's flags. 18 | # 19 | # If EXTRA-FLAGS is defined, it is added to the current language's default 20 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 21 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 22 | # force the compiler to issue an error when a bad flag is given. 23 | # 24 | # INPUT gives an alternative input source to AC_COMPILE_IFELSE. 25 | # 26 | # NOTE: This macro depends on the AX_APPEND_FLAG and 27 | # AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with 28 | # AX_APPEND_LINK_FLAGS. 29 | # 30 | # LICENSE 31 | # 32 | # Copyright (c) 2011 Maarten Bosmans 33 | # 34 | # Copying and distribution of this file, with or without modification, are 35 | # permitted in any medium without royalty provided the copyright notice 36 | # and this notice are preserved. This file is offered as-is, without any 37 | # warranty. 38 | 39 | #serial 7 40 | 41 | AC_DEFUN([AX_APPEND_COMPILE_FLAGS], 42 | [AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) 43 | AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) 44 | for flag in $1; do 45 | AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4]) 46 | done 47 | ])dnl AX_APPEND_COMPILE_FLAGS 48 | -------------------------------------------------------------------------------- /m4/ax_append_flag.m4: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: FSFAP 2 | # =========================================================================== 3 | # https://www.gnu.org/software/autoconf-archive/ax_append_flag.html 4 | # =========================================================================== 5 | # 6 | # SYNOPSIS 7 | # 8 | # AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) 9 | # 10 | # DESCRIPTION 11 | # 12 | # FLAG is appended to the FLAGS-VARIABLE shell variable, with a space 13 | # added in between. 14 | # 15 | # If FLAGS-VARIABLE is not specified, the current language's flags (e.g. 16 | # CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains 17 | # FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly 18 | # FLAG. 19 | # 20 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. 21 | # 22 | # LICENSE 23 | # 24 | # Copyright (c) 2008 Guido U. Draheim 25 | # Copyright (c) 2011 Maarten Bosmans 26 | # 27 | # Copying and distribution of this file, with or without modification, are 28 | # permitted in any medium without royalty provided the copyright notice 29 | # and this notice are preserved. This file is offered as-is, without any 30 | # warranty. 31 | 32 | #serial 8 33 | 34 | AC_DEFUN([AX_APPEND_FLAG], 35 | [dnl 36 | AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF 37 | AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) 38 | AS_VAR_SET_IF(FLAGS,[ 39 | AS_CASE([" AS_VAR_GET(FLAGS) "], 40 | [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], 41 | [ 42 | AS_VAR_APPEND(FLAGS,[" $1"]) 43 | AC_RUN_LOG([: FLAGS="$FLAGS"]) 44 | ]) 45 | ], 46 | [ 47 | AS_VAR_SET(FLAGS,[$1]) 48 | AC_RUN_LOG([: FLAGS="$FLAGS"]) 49 | ]) 50 | AS_VAR_POPDEF([FLAGS])dnl 51 | ])dnl AX_APPEND_FLAG 52 | -------------------------------------------------------------------------------- /m4/ax_c___attribute__.m4: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-Autoconf-exception-macro 2 | # =========================================================================== 3 | # https://www.gnu.org/software/autoconf-archive/ax_c___attribute__.html 4 | # =========================================================================== 5 | # 6 | # SYNOPSIS 7 | # 8 | # AX_C___ATTRIBUTE__ 9 | # 10 | # DESCRIPTION 11 | # 12 | # Provides a test for the compiler support of __attribute__ extensions. 13 | # Defines HAVE___ATTRIBUTE__ if it is found. 14 | # 15 | # LICENSE 16 | # 17 | # Copyright (c) 2008 Stepan Kasal 18 | # Copyright (c) 2008 Christian Haggstrom 19 | # Copyright (c) 2008 Ryan McCabe 20 | # 21 | # This program is free software; you can redistribute it and/or modify it 22 | # under the terms of the GNU General Public License as published by the 23 | # Free Software Foundation; either version 2 of the License, or (at your 24 | # option) any later version. 25 | # 26 | # This program is distributed in the hope that it will be useful, but 27 | # WITHOUT ANY WARRANTY; without even the implied warranty of 28 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General 29 | # Public License for more details. 30 | # 31 | # You should have received a copy of the GNU General Public License along 32 | # with this program. If not, see . 33 | # 34 | # As a special exception, the respective Autoconf Macro's copyright owner 35 | # gives unlimited permission to copy, distribute and modify the configure 36 | # scripts that are the output of Autoconf when processing the Macro. You 37 | # need not follow the terms of the GNU General Public License when using 38 | # or distributing such scripts, even though portions of the text of the 39 | # Macro appear in them. The GNU General Public License (GPL) does govern 40 | # all other use of the material that constitutes the Autoconf Macro. 41 | # 42 | # This special exception to the GPL applies to versions of the Autoconf 43 | # Macro released by the Autoconf Archive. When you make and distribute a 44 | # modified version of the Autoconf Macro, you may extend this special 45 | # exception to the GPL to apply to your modified version as well. 46 | 47 | #serial 9 48 | 49 | AC_DEFUN([AX_C___ATTRIBUTE__], [ 50 | AC_CACHE_CHECK([for __attribute__], [ax_cv___attribute__], 51 | [AC_COMPILE_IFELSE( 52 | [AC_LANG_PROGRAM( 53 | [[#include 54 | static void foo(void) __attribute__ ((unused)); 55 | static void 56 | foo(void) { 57 | exit(1); 58 | } 59 | ]], [])], 60 | [ax_cv___attribute__=yes], 61 | [ax_cv___attribute__=no] 62 | ) 63 | ]) 64 | if test "$ax_cv___attribute__" = "yes"; then 65 | AC_DEFINE([HAVE___ATTRIBUTE__], 1, [define if your compiler has __attribute__]) 66 | fi 67 | ]) 68 | -------------------------------------------------------------------------------- /m4/ax_check_compile_flag.m4: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: FSFAP 2 | # =========================================================================== 3 | # https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html 4 | # =========================================================================== 5 | # 6 | # SYNOPSIS 7 | # 8 | # AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) 9 | # 10 | # DESCRIPTION 11 | # 12 | # Check whether the given FLAG works with the current language's compiler 13 | # or gives an error. (Warnings, however, are ignored) 14 | # 15 | # ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on 16 | # success/failure. 17 | # 18 | # If EXTRA-FLAGS is defined, it is added to the current language's default 19 | # flags (e.g. CFLAGS) when the check is done. The check is thus made with 20 | # the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to 21 | # force the compiler to issue an error when a bad flag is given. 22 | # 23 | # INPUT gives an alternative input source to AC_COMPILE_IFELSE. 24 | # 25 | # NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this 26 | # macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. 27 | # 28 | # LICENSE 29 | # 30 | # Copyright (c) 2008 Guido U. Draheim 31 | # Copyright (c) 2011 Maarten Bosmans 32 | # 33 | # Copying and distribution of this file, with or without modification, are 34 | # permitted in any medium without royalty provided the copyright notice 35 | # and this notice are preserved. This file is offered as-is, without any 36 | # warranty. 37 | 38 | #serial 6 39 | 40 | AC_DEFUN([AX_CHECK_COMPILE_FLAG], 41 | [AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF 42 | AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl 43 | AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ 44 | ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS 45 | _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" 46 | AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], 47 | [AS_VAR_SET(CACHEVAR,[yes])], 48 | [AS_VAR_SET(CACHEVAR,[no])]) 49 | _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) 50 | AS_VAR_IF(CACHEVAR,yes, 51 | [m4_default([$2], :)], 52 | [m4_default([$3], :)]) 53 | AS_VAR_POPDEF([CACHEVAR])dnl 54 | ])dnl AX_CHECK_COMPILE_FLAGS 55 | -------------------------------------------------------------------------------- /m4/ax_require_defined.m4: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: FSFAP 2 | # =========================================================================== 3 | # https://www.gnu.org/software/autoconf-archive/ax_require_defined.html 4 | # =========================================================================== 5 | # 6 | # SYNOPSIS 7 | # 8 | # AX_REQUIRE_DEFINED(MACRO) 9 | # 10 | # DESCRIPTION 11 | # 12 | # AX_REQUIRE_DEFINED is a simple helper for making sure other macros have 13 | # been defined and thus are available for use. This avoids random issues 14 | # where a macro isn't expanded. Instead the configure script emits a 15 | # non-fatal: 16 | # 17 | # ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found 18 | # 19 | # It's like AC_REQUIRE except it doesn't expand the required macro. 20 | # 21 | # Here's an example: 22 | # 23 | # AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) 24 | # 25 | # LICENSE 26 | # 27 | # Copyright (c) 2014 Mike Frysinger 28 | # 29 | # Copying and distribution of this file, with or without modification, are 30 | # permitted in any medium without royalty provided the copyright notice 31 | # and this notice are preserved. This file is offered as-is, without any 32 | # warranty. 33 | 34 | #serial 2 35 | 36 | AC_DEFUN([AX_REQUIRE_DEFINED], [dnl 37 | m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) 38 | ])dnl AX_REQUIRE_DEFINED 39 | -------------------------------------------------------------------------------- /m4/pprint.m4: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0-or-later WITH LicenseRef-Autoconf-exception-macro 2 | # SPDX-FileCopyrightText: 2019 Philippe Proulx 3 | # Pretty printing macros. 4 | # 5 | # Author: Philippe Proulx 6 | 7 | #serial 1 8 | 9 | # PPRINT_INIT(): initializes the pretty printing system. 10 | # 11 | # Use this macro before using any other PPRINT_* macro. 12 | AC_DEFUN([PPRINT_INIT], [ 13 | m4_define([PPRINT_CONFIG_TS], [50]) 14 | m4_define([PPRINT_CONFIG_INDENT], [2]) 15 | PPRINT_YES_MSG=yes 16 | PPRINT_NO_MSG=no 17 | 18 | # find tput, which tells us if colors are supported and gives us color codes 19 | AC_PATH_PROG([pprint_tput], [tput]) 20 | 21 | AS_IF([test -n "$pprint_tput"], [ 22 | AS_IF([test -n "$PS1" && test `"$pprint_tput" colors` -eq 256 && test -t 1], [ 23 | # interactive shell and colors supported and standard output 24 | # file descriptor is opened on a terminal 25 | PPRINT_COLOR_TXTBLK="`"$pprint_tput" setaf 0`" 26 | PPRINT_COLOR_TXTBLU="`"$pprint_tput" setaf 4`" 27 | PPRINT_COLOR_TXTGRN="`"$pprint_tput" setaf 2`" 28 | PPRINT_COLOR_TXTCYN="`"$pprint_tput" setaf 6`" 29 | PPRINT_COLOR_TXTRED="`"$pprint_tput" setaf 1`" 30 | PPRINT_COLOR_TXTPUR="`"$pprint_tput" setaf 5`" 31 | PPRINT_COLOR_TXTYLW="`"$pprint_tput" setaf 3`" 32 | PPRINT_COLOR_TXTWHT="`"$pprint_tput" setaf 7`" 33 | PPRINT_COLOR_BLD=`"$pprint_tput" bold` 34 | PPRINT_COLOR_BLDBLK="$PPRINT_COLOR_BLD$PPRINT_COLOR_TXTBLK" 35 | PPRINT_COLOR_BLDBLU="$PPRINT_COLOR_BLD$PPRINT_COLOR_TXTBLU" 36 | PPRINT_COLOR_BLDGRN="$PPRINT_COLOR_BLD$PPRINT_COLOR_TXTGRN" 37 | PPRINT_COLOR_BLDCYN="$PPRINT_COLOR_BLD$PPRINT_COLOR_TXTCYN" 38 | PPRINT_COLOR_BLDRED="$PPRINT_COLOR_BLD$PPRINT_COLOR_TXTRED" 39 | PPRINT_COLOR_BLDPUR="$PPRINT_COLOR_BLD$PPRINT_COLOR_TXTPUR" 40 | PPRINT_COLOR_BLDYLW="$PPRINT_COLOR_BLD$PPRINT_COLOR_TXTYLW" 41 | PPRINT_COLOR_BLDWHT="$PPRINT_COLOR_BLD$PPRINT_COLOR_TXTWHT" 42 | PPRINT_COLOR_RST="`"$pprint_tput" sgr0`" 43 | 44 | # colored yes and no 45 | PPRINT_YES_MSG="$PPRINT_COLOR_BLDGRN$PPRINT_YES_MSG$PPRINT_COLOR_RST" 46 | PPRINT_NO_MSG="$PPRINT_COLOR_BLDRED$PPRINT_NO_MSG$PPRINT_COLOR_RST" 47 | 48 | # subtitle color 49 | PPRINT_COLOR_SUBTITLE="$PPRINT_COLOR_BLDCYN" 50 | ]) 51 | ]) 52 | ]) 53 | 54 | # PPRINT_SET_INDENT(indent): sets the current indentation. 55 | # 56 | # Use PPRINT_INIT() before using this macro. 57 | AC_DEFUN([PPRINT_SET_INDENT], [ 58 | m4_define([PPRINT_CONFIG_INDENT], [$1]) 59 | ]) 60 | 61 | # PPRINT_SET_TS(ts): sets the current tab stop. 62 | # 63 | # Use PPRINT_INIT() before using this macro. 64 | AC_DEFUN([PPRINT_SET_TS], [ 65 | m4_define([PPRINT_CONFIG_TS], [$1]) 66 | ]) 67 | 68 | # PPRINT_SUBTITLE(subtitle): pretty prints a subtitle. 69 | # 70 | # The subtitle is put as is in a double-quoted shell string so the user 71 | # needs to escape ". 72 | # 73 | # Use PPRINT_INIT() before using this macro. 74 | AC_DEFUN([PPRINT_SUBTITLE], [ 75 | AS_ECHO(["${PPRINT_COLOR_SUBTITLE}$1$PPRINT_COLOR_RST"]) 76 | ]) 77 | 78 | AC_DEFUN([_PPRINT_INDENT], [ 79 | m4_if(PPRINT_CONFIG_INDENT, 0, [ 80 | ], [ 81 | m4_for([pprint_i], [0], m4_eval(PPRINT_CONFIG_INDENT * 2 - 1), [1], [ 82 | AS_ECHO_N([" "]) 83 | ]) 84 | ]) 85 | ]) 86 | 87 | # PPRINT_PROP_STRING(title, value, title_color?): pretty prints a 88 | # string property. 89 | # 90 | # The title is put as is in a double-quoted shell string so the user 91 | # needs to escape ". 92 | # 93 | # The $PPRINT_CONFIG_INDENT variable must be set to the desired indentation 94 | # level. 95 | # 96 | # Use PPRINT_INIT() before using this macro. 97 | AC_DEFUN([PPRINT_PROP_STRING], [ 98 | m4_pushdef([pprint_title], [$1]) 99 | m4_pushdef([pprint_value], [$2]) 100 | m4_pushdef([pprint_title_color], m4_default([$3], [])) 101 | m4_pushdef([pprint_title_len], m4_len(pprint_title)) 102 | m4_pushdef([pprint_spaces_cnt], m4_eval(PPRINT_CONFIG_TS - pprint_title_len - (PPRINT_CONFIG_INDENT * 2) - 1)) 103 | 104 | m4_if(m4_eval(pprint_spaces_cnt <= 0), [1], [ 105 | m4_define([pprint_spaces_cnt], [1]) 106 | ]) 107 | 108 | m4_pushdef([pprint_spaces], []) 109 | 110 | m4_for([pprint_i], 0, m4_eval(pprint_spaces_cnt - 1), [1], [ 111 | m4_append([pprint_spaces], [ ]) 112 | ]) 113 | 114 | _PPRINT_INDENT 115 | 116 | AS_ECHO_N(["pprint_title_color""pprint_title$PPRINT_COLOR_RST:pprint_spaces"]) 117 | AS_ECHO(["${PPRINT_COLOR_BLD}pprint_value$PPRINT_COLOR_RST"]) 118 | 119 | m4_popdef([pprint_spaces]) 120 | m4_popdef([pprint_spaces_cnt]) 121 | m4_popdef([pprint_title_len]) 122 | m4_popdef([pprint_title_color]) 123 | m4_popdef([pprint_value]) 124 | m4_popdef([pprint_title]) 125 | ]) 126 | 127 | # PPRINT_PROP_BOOL(title, value, title_color?): pretty prints a boolean 128 | # property. 129 | # 130 | # The title is put as is in a double-quoted shell string so the user 131 | # needs to escape ". 132 | # 133 | # The value is evaluated at shell runtime. Its evaluation must be 134 | # 0 (false) or 1 (true). 135 | # 136 | # Uses the PPRINT_PROP_STRING() with the "yes" or "no" string. 137 | # 138 | # Use PPRINT_INIT() before using this macro. 139 | AC_DEFUN([PPRINT_PROP_BOOL], [ 140 | m4_pushdef([pprint_title], [$1]) 141 | m4_pushdef([pprint_value], [$2]) 142 | 143 | test pprint_value -eq 0 && pprint_msg="$PPRINT_NO_MSG" || pprint_msg="$PPRINT_YES_MSG" 144 | 145 | m4_if([$#], [3], [ 146 | PPRINT_PROP_STRING(pprint_title, [$pprint_msg], $3) 147 | ], [ 148 | PPRINT_PROP_STRING(pprint_title, [$pprint_msg]) 149 | ]) 150 | 151 | m4_popdef([pprint_value]) 152 | m4_popdef([pprint_title]) 153 | ]) 154 | 155 | # PPRINT_PROP_BOOL_CUSTOM(title, value, no_msg, title_color?): pretty prints a boolean 156 | # property. 157 | # 158 | # The title is put as is in a double-quoted shell string so the user 159 | # needs to escape ". 160 | # 161 | # The value is evaluated at shell runtime. Its evaluation must be 162 | # 0 (false) or 1 (true). 163 | # 164 | # Uses the PPRINT_PROP_STRING() with the "yes" or "no" string. 165 | # 166 | # Use PPRINT_INIT() before using this macro. 167 | AC_DEFUN([PPRINT_PROP_BOOL_CUSTOM], [ 168 | m4_pushdef([pprint_title], [$1]) 169 | m4_pushdef([pprint_value], [$2]) 170 | m4_pushdef([pprint_value_no_msg], [$3]) 171 | 172 | test pprint_value -eq 0 && pprint_msg="$PPRINT_NO_MSG (pprint_value_no_msg)" || pprint_msg="$PPRINT_YES_MSG" 173 | 174 | m4_if([$#], [4], [ 175 | PPRINT_PROP_STRING(pprint_title, [$pprint_msg], $4) 176 | ], [ 177 | PPRINT_PROP_STRING(pprint_title, [$pprint_msg]) 178 | ]) 179 | 180 | m4_popdef([pprint_value_no_msg]) 181 | m4_popdef([pprint_value]) 182 | m4_popdef([pprint_title]) 183 | ]) 184 | 185 | # PPRINT_WARN(msg): pretty prints a warning message. 186 | # 187 | # The message is put as is in a double-quoted shell string so the user 188 | # needs to escape ". 189 | # 190 | # Use PPRINT_INIT() before using this macro. 191 | AC_DEFUN([PPRINT_WARN], [ 192 | m4_pushdef([pprint_msg], [$1]) 193 | 194 | _PPRINT_INDENT 195 | AS_ECHO(["${PPRINT_COLOR_TXTYLW}WARNING:$PPRINT_COLOR_RST ${PPRINT_COLOR_BLDYLW}pprint_msg$PPRINT_COLOR_RST"]) 196 | 197 | m4_popdef([pprint_msg]) 198 | ]) 199 | 200 | # PPRINT_ERROR(msg): pretty prints an error message and exits. 201 | # 202 | # The message is put as is in a double-quoted shell string so the user 203 | # needs to escape ". 204 | # 205 | # Use PPRINT_INIT() before using this macro. 206 | AC_DEFUN([PPRINT_ERROR], [ 207 | m4_pushdef([pprint_msg], [$1]) 208 | 209 | AC_MSG_ERROR([${PPRINT_COLOR_BLDRED}pprint_msg$PPRINT_COLOR_RST]) 210 | 211 | m4_popdef([pprint_msg]) 212 | ]) 213 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 3 | 4 | lib_LTLIBRARIES = librseq.la 5 | 6 | librseq_la_SOURCES = \ 7 | rseq.c rseq-mempool.c rseq-utils.h smp.c smp.h list.h 8 | 9 | librseq_la_LDFLAGS = -no-undefined -version-info $(RSEQ_LIBRARY_VERSION) 10 | librseq_la_LIBADD = $(DL_LIBS) 11 | 12 | if ENABLE_NUMA 13 | librseq_la_LIBADD += -lnuma 14 | endif 15 | 16 | pkgconfigdir = $(libdir)/pkgconfig 17 | pkgconfig_DATA = librseq.pc 18 | -------------------------------------------------------------------------------- /src/librseq.pc.in: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 3 | 4 | prefix=@prefix@ 5 | exec_prefix=@exec_prefix@ 6 | libdir=@libdir@ 7 | includedir=@includedir@ 8 | 9 | Name: Restartable sequence 10 | Description: Restartable sequence library 11 | Version: @PACKAGE_VERSION@ 12 | Requires: 13 | Libs: -L${libdir} -lrseq 14 | Cflags: -I${includedir} 15 | -------------------------------------------------------------------------------- /src/list.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2002 Free Software Foundation, Inc. 2 | // SPDX-FileCopyrightText: 2009 Pierre-Marc Fournier 3 | // SPDX-FileCopyrightText: 2010 Mathieu Desnoyers 4 | // 5 | // SPDX-License-Identifier: LGPL-2.1-or-later 6 | 7 | /* 8 | * (originally part of the GNU C Library) 9 | * Contributed by Ulrich Drepper , 2002. 10 | */ 11 | 12 | #ifndef _LIST_H 13 | #define _LIST_H 1 14 | 15 | /* 16 | * container_of - Get the address of an object containing a field. 17 | * 18 | * @ptr: pointer to the field. 19 | * @type: type of the object. 20 | * @member: name of the field within the object. 21 | */ 22 | #define container_of(ptr, type, member) \ 23 | __extension__ \ 24 | ({ \ 25 | const __typeof__(((type *) NULL)->member) * __ptr = (ptr); \ 26 | (type *)((char *)__ptr - offsetof(type, member)); \ 27 | }) 28 | 29 | /* 30 | * The definitions of this file are adopted from those which can be 31 | * found in the Linux kernel headers to enable people familiar with the 32 | * latter find their way in these sources as well. 33 | */ 34 | 35 | /* Basic type for the double-link list. */ 36 | struct list_head { 37 | struct list_head *next, *prev; 38 | }; 39 | 40 | /* Define a variable with the head and tail of the list. */ 41 | #define LIST_HEAD(name) \ 42 | struct list_head name = { &(name), &(name) } 43 | 44 | /* Initialize a new list head. */ 45 | #define INIT_LIST_HEAD(ptr) \ 46 | (ptr)->next = (ptr)->prev = (ptr) 47 | 48 | #define LIST_HEAD_INIT(name) { .next = &(name), .prev = &(name) } 49 | 50 | /* Add new element at the head of the list. */ 51 | static inline 52 | void list_add(struct list_head *newp, struct list_head *head) 53 | { 54 | head->next->prev = newp; 55 | newp->next = head->next; 56 | newp->prev = head; 57 | head->next = newp; 58 | } 59 | 60 | /* Add new element at the tail of the list. */ 61 | static inline 62 | void list_add_tail(struct list_head *newp, struct list_head *head) 63 | { 64 | head->prev->next = newp; 65 | newp->next = head; 66 | newp->prev = head->prev; 67 | head->prev = newp; 68 | } 69 | 70 | /* Remove element from list. */ 71 | static inline 72 | void __list_del(struct list_head *prev, struct list_head *next) 73 | { 74 | next->prev = prev; 75 | prev->next = next; 76 | } 77 | 78 | /* Remove element from list. */ 79 | static inline 80 | void list_del(struct list_head *elem) 81 | { 82 | __list_del(elem->prev, elem->next); 83 | } 84 | 85 | /* Remove element from list, initializing the element's list pointers. */ 86 | static inline 87 | void list_del_init(struct list_head *elem) 88 | { 89 | list_del(elem); 90 | INIT_LIST_HEAD(elem); 91 | } 92 | 93 | /* Delete from list, add to another list as head. */ 94 | static inline 95 | void list_move(struct list_head *elem, struct list_head *head) 96 | { 97 | __list_del(elem->prev, elem->next); 98 | list_add(elem, head); 99 | } 100 | 101 | /* Replace an old entry. */ 102 | static inline 103 | void list_replace(struct list_head *old, struct list_head *_new) 104 | { 105 | _new->next = old->next; 106 | _new->prev = old->prev; 107 | _new->prev->next = _new; 108 | _new->next->prev = _new; 109 | } 110 | 111 | /* Join two lists. */ 112 | static inline 113 | void list_splice(struct list_head *add, struct list_head *head) 114 | { 115 | /* Do nothing if the list which gets added is empty. */ 116 | if (add != add->next) { 117 | add->next->prev = head; 118 | add->prev->next = head->next; 119 | head->next->prev = add->prev; 120 | head->next = add->next; 121 | } 122 | } 123 | 124 | /* Get typed element from list at a given position. */ 125 | #define list_entry(ptr, type, member) container_of(ptr, type, member) 126 | 127 | /* Get first entry from a list. */ 128 | #define list_first_entry(ptr, type, member) \ 129 | list_entry((ptr)->next, type, member) 130 | 131 | /* Iterate forward over the elements of the list. */ 132 | #define list_for_each(pos, head) \ 133 | for (pos = (head)->next; (pos) != (head); pos = (pos)->next) 134 | 135 | /* 136 | * Iterate forward over the elements list. The list elements can be 137 | * removed from the list while doing this. 138 | */ 139 | #define list_for_each_safe(pos, p, head) \ 140 | for (pos = (head)->next, p = (pos)->next; \ 141 | (pos) != (head); \ 142 | pos = (p), p = (pos)->next) 143 | 144 | /* Iterate backward over the elements of the list. */ 145 | #define list_for_each_prev(pos, head) \ 146 | for (pos = (head)->prev; (pos) != (head); pos = (pos)->prev) 147 | 148 | /* 149 | * Iterate backwards over the elements list. The list elements can be 150 | * removed from the list while doing this. 151 | */ 152 | #define list_for_each_prev_safe(pos, p, head) \ 153 | for (pos = (head)->prev, p = (pos)->prev; \ 154 | (pos) != (head); \ 155 | pos = (p), p = (pos)->prev) 156 | 157 | #define list_for_each_entry(pos, head, member) \ 158 | for (pos = list_entry((head)->next, __typeof__(*(pos)), member); \ 159 | &(pos)->member != (head); \ 160 | pos = list_entry((pos)->member.next, __typeof__(*(pos)), member)) 161 | 162 | #define list_for_each_entry_reverse(pos, head, member) \ 163 | for (pos = list_entry((head)->prev, __typeof__(*(pos)), member); \ 164 | &(pos)->member != (head); \ 165 | pos = list_entry((pos)->member.prev, __typeof__(*(pos)), member)) 166 | 167 | #define list_for_each_entry_safe(pos, p, head, member) \ 168 | for (pos = list_entry((head)->next, __typeof__(*(pos)), member), \ 169 | p = list_entry((pos)->member.next, __typeof__(*(pos)), member); \ 170 | &(pos)->member != (head); \ 171 | pos = (p), p = list_entry((pos)->member.next, __typeof__(*(pos)), member)) 172 | 173 | /* 174 | * Same as list_for_each_entry_safe, but starts from "pos" which should 175 | * point to an entry within the list. 176 | */ 177 | #define list_for_each_entry_safe_from(pos, p, head, member) \ 178 | for (p = list_entry((pos)->member.next, __typeof__(*(pos)), member); \ 179 | &(pos)->member != (head); \ 180 | pos = (p), p = list_entry((pos)->member.next, __typeof__(*(pos)), member)) 181 | 182 | static inline 183 | int list_empty(struct list_head *head) 184 | { 185 | return head == head->next; 186 | } 187 | 188 | static inline 189 | void list_replace_init(struct list_head *old, struct list_head *_new) 190 | { 191 | struct list_head *head = old->next; 192 | 193 | list_del(old); 194 | list_add_tail(_new, head); 195 | INIT_LIST_HEAD(old); 196 | } 197 | 198 | #endif /* _LIST_H */ 199 | -------------------------------------------------------------------------------- /src/rseq-utils.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 Mathieu Desnoyers 3 | 4 | #ifndef _RSEQ_COMMON_UTILS_H 5 | #define _RSEQ_COMMON_UTILS_H 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #define RSEQ_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 14 | 15 | #define __rseq_align_mask(v, mask) (((v) + (mask)) & ~(mask)) 16 | #define rseq_align(v, align) __rseq_align_mask(v, (__typeof__(v)) (align) - 1) 17 | 18 | static inline 19 | unsigned int rseq_fls_u64(uint64_t x) 20 | { 21 | unsigned int r = 64; 22 | 23 | if (!x) 24 | return 0; 25 | 26 | if (!(x & 0xFFFFFFFF00000000ULL)) { 27 | x <<= 32; 28 | r -= 32; 29 | } 30 | if (!(x & 0xFFFF000000000000ULL)) { 31 | x <<= 16; 32 | r -= 16; 33 | } 34 | if (!(x & 0xFF00000000000000ULL)) { 35 | x <<= 8; 36 | r -= 8; 37 | } 38 | if (!(x & 0xF000000000000000ULL)) { 39 | x <<= 4; 40 | r -= 4; 41 | } 42 | if (!(x & 0xC000000000000000ULL)) { 43 | x <<= 2; 44 | r -= 2; 45 | } 46 | if (!(x & 0x8000000000000000ULL)) { 47 | x <<= 1; 48 | r -= 1; 49 | } 50 | return r; 51 | } 52 | 53 | static inline 54 | unsigned int rseq_fls_u32(uint32_t x) 55 | { 56 | unsigned int r = 32; 57 | 58 | if (!x) 59 | return 0; 60 | if (!(x & 0xFFFF0000U)) { 61 | x <<= 16; 62 | r -= 16; 63 | } 64 | if (!(x & 0xFF000000U)) { 65 | x <<= 8; 66 | r -= 8; 67 | } 68 | if (!(x & 0xF0000000U)) { 69 | x <<= 4; 70 | r -= 4; 71 | } 72 | if (!(x & 0xC0000000U)) { 73 | x <<= 2; 74 | r -= 2; 75 | } 76 | if (!(x & 0x80000000U)) { 77 | x <<= 1; 78 | r -= 1; 79 | } 80 | return r; 81 | } 82 | 83 | static inline 84 | unsigned int rseq_fls_ulong(unsigned long x) 85 | { 86 | #if RSEQ_BITS_PER_LONG == 32 87 | return rseq_fls_u32(x); 88 | #else 89 | return rseq_fls_u64(x); 90 | #endif 91 | } 92 | 93 | /* 94 | * Return the minimum order for which x <= (1UL << order). 95 | * Return -1 if x is 0. 96 | */ 97 | static inline 98 | int rseq_get_count_order_ulong(unsigned long x) 99 | { 100 | if (!x) 101 | return -1; 102 | 103 | return rseq_fls_ulong(x - 1); 104 | } 105 | 106 | #define RSEQ_DEFAULT_PAGE_SIZE 4096 107 | 108 | static inline 109 | unsigned long rseq_get_page_len(void) 110 | { 111 | long page_len = sysconf(_SC_PAGE_SIZE); 112 | 113 | if (page_len < 0) 114 | page_len = RSEQ_DEFAULT_PAGE_SIZE; 115 | return (unsigned long) page_len; 116 | } 117 | 118 | static inline 119 | int rseq_hweight_ulong(unsigned long v) 120 | { 121 | return __builtin_popcountl(v); 122 | } 123 | 124 | static inline 125 | bool is_pow2(uint64_t x) 126 | { 127 | return !(x & (x - 1)); 128 | } 129 | 130 | /* 131 | * Calculate offset needed to align p on alignment towards higher 132 | * addresses. Alignment must be a power of 2 133 | */ 134 | static inline 135 | off_t offset_align(uintptr_t p, size_t alignment) 136 | { 137 | return (alignment - p) & (alignment - 1); 138 | } 139 | 140 | #endif /* _RSEQ_COMMON_UTILS_H */ 141 | -------------------------------------------------------------------------------- /src/rseq.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2016 Mathieu Desnoyers 3 | 4 | #ifndef _GNU_SOURCE 5 | #define _GNU_SOURCE 6 | #endif 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include "smp.h" 25 | 26 | #ifndef AT_RSEQ_FEATURE_SIZE 27 | # define AT_RSEQ_FEATURE_SIZE 27 28 | #endif 29 | 30 | #ifndef AT_RSEQ_ALIGN 31 | # define AT_RSEQ_ALIGN 28 32 | #endif 33 | 34 | static __attribute__((constructor)) 35 | void rseq_init(void); 36 | 37 | static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER; 38 | static int init_done; 39 | 40 | static const ptrdiff_t *libc_rseq_offset_p; 41 | static const unsigned int *libc_rseq_size_p; 42 | static const unsigned int *libc_rseq_flags_p; 43 | 44 | /* Offset from the thread pointer to the rseq area. */ 45 | ptrdiff_t rseq_offset; 46 | 47 | /* 48 | * Size of the active rseq feature set. 0 if the registration was 49 | * unsuccessful. 50 | */ 51 | unsigned int rseq_size = -1U; 52 | 53 | /* Flags used during rseq registration. */ 54 | unsigned int rseq_flags; 55 | 56 | static int rseq_ownership; 57 | 58 | /* Allocate a large area for the TLS. */ 59 | #define RSEQ_THREAD_AREA_ALLOC_SIZE 1024 60 | 61 | /* Original struct rseq feature size is 20 bytes. */ 62 | #define ORIG_RSEQ_FEATURE_SIZE 20 63 | 64 | /* Original struct rseq allocation size is 32 bytes. */ 65 | #define ORIG_RSEQ_ALLOC_SIZE 32 66 | 67 | /* 68 | * The alignment on RSEQ_THREAD_AREA_ALLOC_SIZE guarantees that the 69 | * rseq_abi structure allocated size is at least 70 | * RSEQ_THREAD_AREA_ALLOC_SIZE bytes to hold extra space for yet unknown 71 | * kernel rseq extensions. 72 | */ 73 | static 74 | __thread struct rseq_abi __rseq_abi __attribute__((tls_model("initial-exec"), aligned(RSEQ_THREAD_AREA_ALLOC_SIZE))) = { 75 | .cpu_id = RSEQ_ABI_CPU_ID_UNINITIALIZED, 76 | }; 77 | 78 | static int sys_rseq(struct rseq_abi *rseq_abi, uint32_t rseq_len, 79 | int flags, uint32_t sig) 80 | { 81 | return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); 82 | } 83 | 84 | static int sys_getcpu(unsigned int *cpu, unsigned int *node) 85 | { 86 | return syscall(__NR_getcpu, cpu, node, NULL); 87 | } 88 | 89 | bool rseq_available(unsigned int query) 90 | { 91 | int rc; 92 | 93 | switch (query) { 94 | case RSEQ_AVAILABLE_QUERY_KERNEL: 95 | rc = sys_rseq(NULL, 0, 0, 0); 96 | if (rc != -1) 97 | abort(); 98 | switch (errno) { 99 | case ENOSYS: 100 | break; 101 | case EINVAL: 102 | return true; 103 | default: 104 | abort(); 105 | } 106 | break; 107 | case RSEQ_AVAILABLE_QUERY_LIBC: 108 | if (rseq_size && !rseq_ownership) 109 | return true; 110 | break; 111 | default: 112 | break; 113 | } 114 | return false; 115 | } 116 | 117 | /* The rseq areas need to be at least 32 bytes. */ 118 | static 119 | unsigned int get_rseq_min_alloc_size(void) 120 | { 121 | unsigned int alloc_size = rseq_size; 122 | 123 | if (alloc_size < ORIG_RSEQ_ALLOC_SIZE) 124 | alloc_size = ORIG_RSEQ_ALLOC_SIZE; 125 | return alloc_size; 126 | } 127 | 128 | /* 129 | * Return the feature size supported by the kernel. 130 | * 131 | * Depending on the value returned by getauxval(AT_RSEQ_FEATURE_SIZE): 132 | * 133 | * 0: Return ORIG_RSEQ_FEATURE_SIZE (20) 134 | * > 0: Return the value from getauxval(AT_RSEQ_FEATURE_SIZE). 135 | * 136 | * It should never return a value below ORIG_RSEQ_FEATURE_SIZE. 137 | */ 138 | static 139 | unsigned int get_rseq_kernel_feature_size(void) 140 | { 141 | unsigned long auxv_rseq_feature_size, auxv_rseq_align; 142 | 143 | auxv_rseq_align = getauxval(AT_RSEQ_ALIGN); 144 | assert(!auxv_rseq_align || auxv_rseq_align <= RSEQ_THREAD_AREA_ALLOC_SIZE); 145 | 146 | auxv_rseq_feature_size = getauxval(AT_RSEQ_FEATURE_SIZE); 147 | assert(!auxv_rseq_feature_size || auxv_rseq_feature_size <= RSEQ_THREAD_AREA_ALLOC_SIZE); 148 | if (auxv_rseq_feature_size) 149 | return auxv_rseq_feature_size; 150 | else 151 | return ORIG_RSEQ_FEATURE_SIZE; 152 | } 153 | 154 | int rseq_register_current_thread(void) 155 | { 156 | int rc; 157 | 158 | rseq_init(); 159 | 160 | if (!rseq_ownership) { 161 | /* Treat libc's ownership as a successful registration. */ 162 | return 0; 163 | } 164 | rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), 0, RSEQ_SIG); 165 | if (rc) { 166 | /* 167 | * After at least one thread has registered successfully 168 | * (rseq_size > 0), the registration of other threads should 169 | * never fail. 170 | */ 171 | if (RSEQ_READ_ONCE(rseq_size) > 0) { 172 | /* Incoherent success/failure within process. */ 173 | abort(); 174 | } 175 | return -1; 176 | } 177 | assert(rseq_current_cpu_raw() >= 0); 178 | 179 | /* 180 | * The first thread to register sets the rseq_size to mimic the libc 181 | * behavior. 182 | */ 183 | if (RSEQ_READ_ONCE(rseq_size) == 0) { 184 | RSEQ_WRITE_ONCE(rseq_size, get_rseq_kernel_feature_size()); 185 | } 186 | 187 | return 0; 188 | } 189 | 190 | int rseq_unregister_current_thread(void) 191 | { 192 | int rc; 193 | 194 | if (!rseq_ownership) { 195 | /* Treat libc's ownership as a successful unregistration. */ 196 | return 0; 197 | } 198 | rc = sys_rseq(&__rseq_abi, get_rseq_min_alloc_size(), RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); 199 | if (rc) 200 | return -1; 201 | return 0; 202 | } 203 | 204 | /* 205 | * Initialize the public symbols for the rseq offset, size, feature size and 206 | * flags prior to registering threads. If glibc owns the registration, get the 207 | * values from its public symbols. 208 | */ 209 | static 210 | void rseq_init(void) 211 | { 212 | /* 213 | * Ensure initialization is only done once. Use load-acquire to 214 | * observe the initialization performed by a concurrently 215 | * running thread. 216 | */ 217 | if (rseq_smp_load_acquire(&init_done)) 218 | return; 219 | 220 | /* 221 | * Take the mutex, check the initialization flag again and atomically 222 | * set it to ensure we are the only thread doing the initialization. 223 | */ 224 | pthread_mutex_lock(&init_lock); 225 | if (init_done) 226 | goto unlock; 227 | 228 | /* 229 | * Check for glibc rseq support, if the 3 public symbols are found and 230 | * the rseq_size is not zero, glibc owns the registration. 231 | */ 232 | libc_rseq_offset_p = dlsym(RTLD_NEXT, "__rseq_offset"); 233 | libc_rseq_size_p = dlsym(RTLD_NEXT, "__rseq_size"); 234 | libc_rseq_flags_p = dlsym(RTLD_NEXT, "__rseq_flags"); 235 | if (libc_rseq_size_p && libc_rseq_offset_p && libc_rseq_flags_p && 236 | *libc_rseq_size_p != 0) { 237 | unsigned int libc_rseq_size; 238 | 239 | /* rseq registration owned by glibc */ 240 | rseq_offset = *libc_rseq_offset_p; 241 | libc_rseq_size = *libc_rseq_size_p; 242 | rseq_flags = *libc_rseq_flags_p; 243 | 244 | /* 245 | * Previous versions of glibc expose the value 246 | * 32 even though the kernel only supported 20 247 | * bytes initially. Therefore treat 32 as a 248 | * special-case. glibc 2.40 exposes a 20 bytes 249 | * __rseq_size without using getauxval(3) to 250 | * query the supported size, while still allocating a 32 251 | * bytes area. Also treat 20 as a special-case. 252 | * 253 | * Special-cases are handled by using the following 254 | * value as active feature set size: 255 | * 256 | * rseq_size = min(32, get_rseq_kernel_feature_size()) 257 | */ 258 | switch (libc_rseq_size) { 259 | case ORIG_RSEQ_FEATURE_SIZE: /* Fallthrough. */ 260 | case ORIG_RSEQ_ALLOC_SIZE: 261 | { 262 | unsigned int rseq_kernel_feature_size = get_rseq_kernel_feature_size(); 263 | 264 | if (rseq_kernel_feature_size < ORIG_RSEQ_ALLOC_SIZE) 265 | rseq_size = rseq_kernel_feature_size; 266 | else 267 | rseq_size = ORIG_RSEQ_ALLOC_SIZE; 268 | break; 269 | } 270 | default: 271 | /* Otherwise just use the __rseq_size from libc as rseq_size. */ 272 | rseq_size = libc_rseq_size; 273 | break; 274 | } 275 | goto init_done; 276 | } 277 | 278 | /* librseq owns the registration */ 279 | rseq_ownership = 1; 280 | 281 | /* Calculate the offset of the rseq area from the thread pointer. */ 282 | rseq_offset = (uintptr_t)&__rseq_abi - (uintptr_t)rseq_thread_pointer(); 283 | 284 | /* rseq flags are deprecated, always set to 0. */ 285 | rseq_flags = 0; 286 | 287 | /* 288 | * Set the size to 0 until at least one thread registers to mimic the 289 | * libc behavior. 290 | */ 291 | rseq_size = 0; 292 | 293 | init_done: 294 | /* 295 | * Set init_done with store-release, to make sure concurrently 296 | * running threads observe the initialized state. 297 | */ 298 | rseq_smp_store_release(&init_done, 1); 299 | unlock: 300 | pthread_mutex_unlock(&init_lock); 301 | } 302 | 303 | static __attribute__((destructor)) 304 | void rseq_exit(void) 305 | { 306 | if (!rseq_ownership) 307 | return; 308 | rseq_offset = 0; 309 | rseq_size = -1U; 310 | rseq_ownership = 0; 311 | } 312 | 313 | int32_t rseq_fallback_current_cpu(void) 314 | { 315 | int32_t cpu; 316 | 317 | cpu = sched_getcpu(); 318 | if (cpu < 0) { 319 | perror("sched_getcpu()"); 320 | abort(); 321 | } 322 | return cpu; 323 | } 324 | 325 | int32_t rseq_fallback_current_node(void) 326 | { 327 | uint32_t cpu_id, node_id; 328 | int ret; 329 | 330 | ret = sys_getcpu(&cpu_id, &node_id); 331 | if (ret) { 332 | perror("sys_getcpu()"); 333 | return ret; 334 | } 335 | return (int32_t) node_id; 336 | } 337 | 338 | int rseq_get_max_nr_cpus(void) 339 | { 340 | return get_possible_cpus_array_len(); 341 | } 342 | -------------------------------------------------------------------------------- /src/smp.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * Copyright (C) 2011-2012 Mathieu Desnoyers 4 | * Copyright (C) 2019 Michael Jeanson 5 | */ 6 | 7 | #define _LGPL_SOURCE 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "smp.h" 23 | 24 | #define __max(a,b) ((a)>(b)?(a):(b)) 25 | 26 | #define RSEQ_CPUMASK_SIZE 4096 27 | 28 | static int possible_cpus_array_len_cache; 29 | 30 | static 31 | int _get_max_cpuid_from_sysfs(const char *path) 32 | { 33 | long max_cpuid = -1; 34 | 35 | DIR *cpudir; 36 | struct dirent *entry; 37 | 38 | assert(path); 39 | 40 | cpudir = opendir(path); 41 | if (cpudir == NULL) 42 | goto end; 43 | 44 | /* 45 | * Iterate on all directories named "cpu" followed by an integer. 46 | */ 47 | while ((entry = readdir(cpudir))) { 48 | if (entry->d_type == DT_DIR && 49 | strncmp(entry->d_name, "cpu", 3) == 0) { 50 | 51 | char *endptr; 52 | long cpu_id; 53 | 54 | cpu_id = strtol(entry->d_name + 3, &endptr, 10); 55 | if ((cpu_id < LONG_MAX) && (endptr != entry->d_name + 3) 56 | && (*endptr == '\0')) { 57 | if (cpu_id > max_cpuid) 58 | max_cpuid = cpu_id; 59 | } 60 | } 61 | } 62 | 63 | if (closedir(cpudir)) 64 | perror("closedir"); 65 | 66 | /* 67 | * If the max CPU id is out of bound, set it to -1 so it results in a 68 | * CPU num of 0. 69 | */ 70 | if (max_cpuid < 0 || max_cpuid > INT_MAX) 71 | max_cpuid = -1; 72 | 73 | end: 74 | return max_cpuid; 75 | } 76 | 77 | /* 78 | * Get the highest CPU id from sysfs. 79 | * 80 | * Iterate on all the folders in "/sys/devices/system/cpu" that start with 81 | * "cpu" followed by an integer, keep the highest CPU id encountered during 82 | * this iteration and add 1 to get a number of CPUs. 83 | * 84 | * Returns the highest CPU id, or -1 on error. 85 | */ 86 | static 87 | int get_max_cpuid_from_sysfs(void) 88 | { 89 | return _get_max_cpuid_from_sysfs("/sys/devices/system/cpu"); 90 | } 91 | 92 | /* 93 | * As a fallback to parsing the CPU mask in "/sys/devices/system/cpu/possible", 94 | * iterate on all the folders in "/sys/devices/system/cpu" that start with 95 | * "cpu" followed by an integer, keep the highest CPU id encountered during 96 | * this iteration and add 1 to get a number of CPUs. 97 | * 98 | * Then get the value from sysconf(_SC_NPROCESSORS_CONF) as a fallback and 99 | * return the highest one. 100 | * 101 | * On Linux, using the value from sysconf can be unreliable since the way it 102 | * counts CPUs varies between C libraries and even between versions of the same 103 | * library. If we used it directly, getcpu() could return a value greater than 104 | * this sysconf, in which case the arrays indexed by processor would overflow. 105 | * 106 | * As another example, the MUSL libc implementation of the _SC_NPROCESSORS_CONF 107 | * sysconf does not return the number of configured CPUs in the system but 108 | * relies on the cpu affinity mask of the current task. 109 | * 110 | * Returns 0 or less on error. 111 | */ 112 | static 113 | int get_num_possible_cpus_fallback(void) 114 | { 115 | /* 116 | * Get the sysconf value as a last resort. Keep the highest number. 117 | */ 118 | return __max(sysconf(_SC_NPROCESSORS_CONF), get_max_cpuid_from_sysfs() + 1); 119 | } 120 | 121 | /* 122 | * Get a CPU mask string from sysfs. 123 | * 124 | * buf: the buffer where the mask will be read. 125 | * max_bytes: the maximum number of bytes to write in the buffer. 126 | * path: file path to read the mask from. 127 | * 128 | * Returns the number of bytes read or -1 on error. 129 | */ 130 | static 131 | int get_cpu_mask_from_sysfs(char *buf, size_t max_bytes, const char *path) 132 | { 133 | ssize_t bytes_read = 0; 134 | size_t total_bytes_read = 0; 135 | int fd = -1, ret = -1; 136 | 137 | assert(path); 138 | 139 | if (buf == NULL) 140 | goto end; 141 | 142 | fd = open(path, O_RDONLY); 143 | if (fd < 0) 144 | goto end; 145 | 146 | do { 147 | bytes_read = read(fd, buf + total_bytes_read, 148 | max_bytes - total_bytes_read); 149 | 150 | if (bytes_read < 0) { 151 | if (errno == EINTR) { 152 | continue; /* retry operation */ 153 | } else { 154 | goto end; 155 | } 156 | } 157 | 158 | total_bytes_read += bytes_read; 159 | assert(total_bytes_read <= max_bytes); 160 | } while (max_bytes > total_bytes_read && bytes_read != 0); 161 | 162 | /* 163 | * Make sure the mask read is a null terminated string. 164 | */ 165 | if (total_bytes_read < max_bytes) 166 | buf[total_bytes_read] = '\0'; 167 | else 168 | buf[max_bytes - 1] = '\0'; 169 | 170 | if (total_bytes_read > INT_MAX) 171 | goto end; 172 | ret = (int) total_bytes_read; 173 | end: 174 | if (fd >= 0 && close(fd) < 0) 175 | perror("close"); 176 | return ret; 177 | } 178 | 179 | /* 180 | * Get the CPU possible mask string from sysfs. 181 | * 182 | * buf: the buffer where the mask will be read. 183 | * max_bytes: the maximum number of bytes to write in the buffer. 184 | * 185 | * Returns the number of bytes read or -1 on error. 186 | */ 187 | static 188 | int get_possible_cpu_mask_from_sysfs(char *buf, size_t max_bytes) 189 | { 190 | return get_cpu_mask_from_sysfs(buf, max_bytes, 191 | "/sys/devices/system/cpu/possible"); 192 | } 193 | 194 | /* 195 | * Get the highest CPU id from a CPU mask. 196 | * 197 | * pmask: the mask to parse. 198 | * len: the len of the mask excluding '\0'. 199 | * 200 | * Returns the highest CPU id from the mask or -1 on error. 201 | */ 202 | static 203 | int get_max_cpuid_from_mask(const char *pmask, size_t len) 204 | { 205 | ssize_t i; 206 | unsigned long cpu_index; 207 | char *endptr; 208 | 209 | /* We need at least one char to read */ 210 | if (len < 1) 211 | goto error; 212 | 213 | /* Start from the end to read the last CPU index. */ 214 | for (i = len - 1; i > 0; i--) { 215 | /* Break when we hit the first separator. */ 216 | if ((pmask[i] == ',') || (pmask[i] == '-')) { 217 | i++; 218 | break; 219 | } 220 | } 221 | 222 | cpu_index = strtoul(&pmask[i], &endptr, 10); 223 | 224 | if ((&pmask[i] != endptr) && (cpu_index < INT_MAX)) 225 | return (int) cpu_index; 226 | 227 | error: 228 | return -1; 229 | } 230 | 231 | static void update_possible_cpus_array_len_cache(void) 232 | { 233 | char buf[RSEQ_CPUMASK_SIZE]; 234 | int ret; 235 | 236 | /* Get the possible cpu mask from sysfs, fallback to sysconf. */ 237 | ret = get_possible_cpu_mask_from_sysfs((char *) &buf, RSEQ_CPUMASK_SIZE); 238 | if (ret <= 0) 239 | goto fallback; 240 | 241 | /* Parse the possible cpu mask, on failure fallback to sysconf. */ 242 | ret = get_max_cpuid_from_mask((char *) &buf, ret); 243 | if (ret >= 0) { 244 | /* Add 1 to convert from max cpuid to an array len. */ 245 | ret++; 246 | goto end; 247 | } 248 | 249 | fallback: 250 | /* Fallback to sysconf. */ 251 | ret = get_num_possible_cpus_fallback(); 252 | 253 | end: 254 | /* If all methods failed, don't store the value. */ 255 | if (ret < 1) 256 | return; 257 | 258 | possible_cpus_array_len_cache = ret; 259 | } 260 | 261 | /* 262 | * Returns the length of an array that could contain a per-CPU element for each 263 | * possible CPU id for the lifetime of the process. 264 | * 265 | * We currently assume CPU ids are contiguous up the maximum CPU id. 266 | * 267 | * If the cache is not yet initialized, get the value from 268 | * "/sys/devices/system/cpu/possible" or fallback to sysconf and cache it. 269 | * 270 | * If all methods fail, don't populate the cache and return 0. 271 | */ 272 | int get_possible_cpus_array_len(void) 273 | { 274 | if (rseq_unlikely(!possible_cpus_array_len_cache)) 275 | update_possible_cpus_array_len_cache(); 276 | 277 | return possible_cpus_array_len_cache; 278 | } 279 | -------------------------------------------------------------------------------- /src/smp.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | /* 3 | * Copyright (C) 2011-2012 Mathieu Desnoyers 4 | * Copyright (C) 2019 Michael Jeanson 5 | */ 6 | 7 | #ifndef _RSEQ_SMP_H 8 | #define _RSEQ_SMP_H 9 | 10 | int get_possible_cpus_array_len(void) __attribute__((visibility("hidden"))); 11 | 12 | #endif /* _RSEQ_SMP_H */ 13 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 3 | 4 | AM_CPPFLAGS += -I$(top_srcdir)/tests/utils 5 | 6 | SUBDIRS = utils unit 7 | 8 | TEST_EXTENSIONS = .tap 9 | TAP_LOG_DRIVER_FLAGS = --merge --comments 10 | TAP_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' \ 11 | RSEQ_TESTS_SRCDIR='$(abs_top_srcdir)/tests' \ 12 | RSEQ_TESTS_BUILDDIR='$(abs_top_builddir)/tests' \ 13 | $(SHELL) $(srcdir)/utils/tap-driver.sh 14 | 15 | noinst_PROGRAMS = \ 16 | basic_percpu_ops_test.tap \ 17 | basic_percpu_ops_test_cxx.tap \ 18 | basic_percpu_ops_mm_cid_test.tap \ 19 | basic_percpu_ops_mm_cid_test_cxx.tap \ 20 | basic_percpu_benchmark.tap \ 21 | basic_percpu_benchmark_cxx.tap \ 22 | basic_percpu_mm_cid_benchmark.tap \ 23 | basic_percpu_mm_cid_benchmark_cxx.tap \ 24 | basic_test.tap \ 25 | basic_test_cxx.tap \ 26 | fork_test.tap \ 27 | fork_test_cxx.tap \ 28 | mempool_test.tap \ 29 | mempool_test_cxx.tap \ 30 | mempool_cow_race_test.tap \ 31 | mempool_cow_race_test_cxx.tap \ 32 | param_test \ 33 | param_test_cxx \ 34 | param_test_mm_cid \ 35 | param_test_mm_cid_cxx \ 36 | param_test_benchmark \ 37 | param_test_benchmark_cxx \ 38 | param_test_mm_cid_benchmark \ 39 | param_test_mm_cid_benchmark_cxx \ 40 | param_test_compare_twice \ 41 | param_test_compare_twice_cxx \ 42 | param_test_mm_cid_compare_twice \ 43 | param_test_mm_cid_compare_twice_cxx \ 44 | no_syscall_test_cxx.tap \ 45 | no_syscall_test.tap \ 46 | syscall_errors_test.tap \ 47 | syscall_errors_test_cxx.tap \ 48 | unregistered_test_cxx.tap \ 49 | unregistered_test.tap 50 | 51 | dist_noinst_SCRIPTS = \ 52 | run_fork_test_cxx.tap \ 53 | run_fork_test.tap \ 54 | run_no_syscall_test_cxx.tap \ 55 | run_no_syscall_test.tap \ 56 | run_param_test_cxx.tap \ 57 | run_param_test.tap \ 58 | run_syscall_errors_test_cxx.tap \ 59 | run_syscall_errors_test.tap \ 60 | run_unregistered_test_cxx.tap \ 61 | run_unregistered_test.tap 62 | 63 | if ENABLE_SHARED 64 | if ENABLE_SECCOMP 65 | noinst_LTLIBRARIES = libdisable-rseq-syscall.la 66 | 67 | libdisable_rseq_syscall_la_SOURCES = disable-rseq-syscall.c 68 | libdisable_rseq_syscall_la_LDFLAGS = -module -shared -avoid-version -rpath $(abs_builddir)/.libs/ 69 | libdisable_rseq_syscall_la_LIBADD = $(SECCOMP_LIBS) 70 | endif 71 | endif 72 | 73 | basic_percpu_ops_test_tap_SOURCES = basic_percpu_ops_test.c 74 | basic_percpu_ops_test_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 75 | 76 | basic_percpu_ops_test_cxx_tap_SOURCES = basic_percpu_ops_test_cxx.cpp 77 | basic_percpu_ops_test_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 78 | 79 | basic_percpu_ops_mm_cid_test_tap_SOURCES = basic_percpu_ops_test.c 80 | basic_percpu_ops_mm_cid_test_tap_CPPFLAGS = $(AM_CPPFLAGS) -DBUILDOPT_RSEQ_PERCPU_MM_CID 81 | basic_percpu_ops_mm_cid_test_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 82 | 83 | basic_percpu_ops_mm_cid_test_cxx_tap_SOURCES = basic_percpu_ops_test_cxx.cpp 84 | basic_percpu_ops_mm_cid_test_cxx_tap_CPPFLAGS = $(AM_CPPFLAGS) -DBUILDOPT_RSEQ_PERCPU_MM_CID 85 | basic_percpu_ops_mm_cid_test_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 86 | 87 | basic_percpu_benchmark_tap_SOURCES = basic_percpu_benchmark.c 88 | basic_percpu_benchmark_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 89 | 90 | basic_percpu_benchmark_cxx_tap_SOURCES = basic_percpu_benchmark_cxx.cpp 91 | basic_percpu_benchmark_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 92 | 93 | basic_percpu_mm_cid_benchmark_tap_SOURCES = basic_percpu_benchmark.c 94 | basic_percpu_mm_cid_benchmark_tap_CPPFLAGS = $(AM_CPPFLAGS) -DBUILDOPT_RSEQ_PERCPU_MM_CID 95 | basic_percpu_mm_cid_benchmark_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 96 | 97 | basic_percpu_mm_cid_benchmark_cxx_tap_SOURCES = basic_percpu_benchmark_cxx.cpp 98 | basic_percpu_mm_cid_benchmark_cxx_tap_CPPFLAGS = $(AM_CPPFLAGS) -DBUILDOPT_RSEQ_PERCPU_MM_CID 99 | basic_percpu_mm_cid_benchmark_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 100 | 101 | syscall_errors_test_tap_SOURCES = syscall_errors_test.c 102 | syscall_errors_test_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 103 | 104 | syscall_errors_test_cxx_tap_SOURCES = syscall_errors_test_cxx.cpp 105 | syscall_errors_test_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 106 | 107 | unregistered_test_tap_SOURCES = unregistered_test.c 108 | unregistered_test_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 109 | 110 | unregistered_test_cxx_tap_SOURCES = unregistered_test_cxx.cpp 111 | unregistered_test_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 112 | 113 | no_syscall_test_tap_SOURCES = no_syscall_test.c 114 | no_syscall_test_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 115 | 116 | no_syscall_test_cxx_tap_SOURCES = no_syscall_test_cxx.cpp 117 | no_syscall_test_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 118 | 119 | basic_test_tap_SOURCES = basic_test.c 120 | basic_test_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 121 | 122 | basic_test_cxx_tap_SOURCES = basic_test_cxx.cpp 123 | basic_test_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 124 | 125 | fork_test_tap_SOURCES = fork_test.c 126 | fork_test_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 127 | 128 | fork_test_cxx_tap_SOURCES = fork_test_cxx.cpp 129 | fork_test_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 130 | 131 | mempool_test_tap_SOURCES = mempool_test.c 132 | mempool_test_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 133 | 134 | mempool_test_cxx_tap_SOURCES = mempool_test_cxx.cpp 135 | mempool_test_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 136 | 137 | mempool_cow_race_test_tap_SOURCES = mempool_cow_race_test.c 138 | mempool_cow_race_test_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 139 | 140 | mempool_cow_race_test_cxx_tap_SOURCES = mempool_cow_race_test_cxx.cpp 141 | mempool_cow_race_test_cxx_tap_LDADD = $(top_builddir)/src/librseq.la $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 142 | 143 | param_test_SOURCES = param_test.c 144 | param_test_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 145 | 146 | param_test_cxx_SOURCES = param_test_cxx.cpp 147 | param_test_cxx_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 148 | 149 | param_test_mm_cid_SOURCES = param_test.c 150 | param_test_mm_cid_CPPFLAGS = $(AM_CPPFLAGS) -DBUILDOPT_RSEQ_PERCPU_MM_CID 151 | param_test_mm_cid_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 152 | 153 | param_test_mm_cid_cxx_SOURCES = param_test_cxx.cpp 154 | param_test_mm_cid_cxx_CPPFLAGS = $(AM_CPPFLAGS) -DBUILDOPT_RSEQ_PERCPU_MM_CID 155 | param_test_mm_cid_cxx_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 156 | 157 | param_test_benchmark_SOURCES = param_test.c 158 | param_test_benchmark_CPPFLAGS = $(AM_CPPFLAGS) -DBENCHMARK 159 | param_test_benchmark_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 160 | 161 | param_test_benchmark_cxx_SOURCES = param_test_cxx.cpp 162 | param_test_benchmark_cxx_CPPFLAGS = $(AM_CPPFLAGS) -DBENCHMARK 163 | param_test_benchmark_cxx_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 164 | 165 | param_test_mm_cid_benchmark_SOURCES = param_test.c 166 | param_test_mm_cid_benchmark_CPPFLAGS = $(AM_CPPFLAGS) -DBENCHMARK -DBUILDOPT_RSEQ_PERCPU_MM_CID 167 | param_test_mm_cid_benchmark_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 168 | 169 | param_test_mm_cid_benchmark_cxx_SOURCES = param_test_cxx.cpp 170 | param_test_mm_cid_benchmark_cxx_CPPFLAGS = $(AM_CPPFLAGS) -DBENCHMARK -DBUILDOPT_RSEQ_PERCPU_MM_CID 171 | param_test_mm_cid_benchmark_cxx_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 172 | 173 | param_test_compare_twice_SOURCES = param_test.c 174 | param_test_compare_twice_CPPFLAGS = $(AM_CPPFLAGS) -DRSEQ_COMPARE_TWICE 175 | param_test_compare_twice_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 176 | 177 | param_test_compare_twice_cxx_SOURCES = param_test_cxx.cpp 178 | param_test_compare_twice_cxx_CPPFLAGS = $(AM_CPPFLAGS) -DRSEQ_COMPARE_TWICE 179 | param_test_compare_twice_cxx_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 180 | 181 | param_test_mm_cid_compare_twice_SOURCES = param_test.c 182 | param_test_mm_cid_compare_twice_CPPFLAGS = $(AM_CPPFLAGS) -DRSEQ_COMPARE_TWICE -DBUILDOPT_RSEQ_PERCPU_MM_CID 183 | param_test_mm_cid_compare_twice_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 184 | 185 | param_test_mm_cid_compare_twice_cxx_SOURCES = param_test_cxx.cpp 186 | param_test_mm_cid_compare_twice_cxx_CPPFLAGS = $(AM_CPPFLAGS) -DRSEQ_COMPARE_TWICE -DBUILDOPT_RSEQ_PERCPU_MM_CID 187 | param_test_mm_cid_compare_twice_cxx_LDADD = $(top_builddir)/src/librseq.la $(DL_LIBS) 188 | 189 | # Run shorter tests first 190 | TESTS = \ 191 | basic_test.tap \ 192 | basic_test_cxx.tap \ 193 | run_fork_test.tap \ 194 | run_fork_test_cxx.tap \ 195 | run_unregistered_test.tap \ 196 | run_unregistered_test_cxx.tap \ 197 | run_syscall_errors_test.tap \ 198 | run_syscall_errors_test_cxx.tap \ 199 | mempool_cow_race_test.tap \ 200 | mempool_cow_race_test_cxx.tap \ 201 | mempool_test.tap \ 202 | mempool_test_cxx.tap 203 | 204 | if ENABLE_SHARED 205 | if ENABLE_SECCOMP 206 | TESTS += \ 207 | run_no_syscall_test.tap \ 208 | run_no_syscall_test_cxx.tap 209 | endif 210 | endif 211 | 212 | # Run longer tests last 213 | TESTS += \ 214 | basic_percpu_ops_test.tap \ 215 | basic_percpu_ops_test_cxx.tap \ 216 | basic_percpu_ops_mm_cid_test.tap \ 217 | basic_percpu_ops_mm_cid_test_cxx.tap \ 218 | run_param_test.tap \ 219 | run_param_test_cxx.tap 220 | -------------------------------------------------------------------------------- /tests/basic_percpu_benchmark.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2018-2022 Mathieu Desnoyers 3 | #ifndef _GNU_SOURCE 4 | #define _GNU_SOURCE 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include "tap.h" 21 | 22 | /* 23 | * Test intermittent workloads. Invoke with the number of ms as delay 24 | * between individual thread execution as parameter. 25 | */ 26 | 27 | #define NR_CPUS 1024 28 | 29 | static int rand_order[NR_CPUS]; 30 | 31 | /* AREA_LEN must not fill stride. */ 32 | #define AREA_LEN (16 * 1024) /* 16 kB */ 33 | 34 | /* Delay between each thread */ 35 | static int thread_delay = 200; 36 | 37 | #define NR_TESTS 2 38 | #define LOOPS_PER_THREAD 5 39 | 40 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 41 | 42 | static int nr_threads; 43 | 44 | #ifdef BUILDOPT_RSEQ_PERCPU_MM_CID 45 | # define RSEQ_PERCPU RSEQ_PERCPU_MM_CID 46 | static 47 | int get_current_cpu_id(void) 48 | { 49 | return rseq_current_mm_cid(); 50 | } 51 | static 52 | bool rseq_validate_cpu_id(void) 53 | { 54 | return rseq_mm_cid_available(); 55 | } 56 | #else 57 | # define RSEQ_PERCPU RSEQ_PERCPU_CPU_ID 58 | static 59 | int get_current_cpu_id(void) 60 | { 61 | return rseq_cpu_start(); 62 | } 63 | static 64 | bool rseq_validate_cpu_id(void) 65 | { 66 | return rseq_current_cpu_raw() >= 0; 67 | } 68 | #endif 69 | 70 | static struct rseq_mempool *mempool; 71 | 72 | static char __rseq_percpu *percpudata; 73 | 74 | static int nr_active_threads, test_go, test_stop; 75 | 76 | static unsigned int cpu_affinities[NR_CPUS]; 77 | static unsigned int next_aff = 0; 78 | 79 | pthread_mutex_t affinity_mutex = PTHREAD_MUTEX_INITIALIZER; 80 | 81 | static void set_affinity(void) 82 | { 83 | cpu_set_t mask; 84 | int cpu, ret; 85 | 86 | ret = pthread_mutex_lock(&affinity_mutex); 87 | if (ret) { 88 | perror("Error in pthread mutex lock"); 89 | exit(-1); 90 | } 91 | cpu = cpu_affinities[next_aff++]; 92 | ret = pthread_mutex_unlock(&affinity_mutex); 93 | if (ret) { 94 | perror("Error in pthread mutex unlock"); 95 | exit(-1); 96 | } 97 | 98 | CPU_ZERO(&mask); 99 | CPU_SET(cpu, &mask); 100 | sched_setaffinity(0, sizeof(mask), &mask); 101 | } 102 | 103 | static void init_affinity(void) 104 | { 105 | cpu_set_t allowed_cpus; 106 | int cpu; 107 | 108 | if (sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus)) { 109 | perror("sched_getaffinity"); 110 | abort(); 111 | } 112 | for (cpu = 0; cpu < NR_CPUS; cpu++) { 113 | if (CPU_ISSET(cpu, &allowed_cpus)) 114 | cpu_affinities[next_aff++] = cpu; 115 | } 116 | next_aff = 0; 117 | } 118 | 119 | static int get_affinity_weight(void) 120 | { 121 | cpu_set_t allowed_cpus; 122 | 123 | if (sched_getaffinity(0, sizeof(allowed_cpus), &allowed_cpus)) { 124 | perror("sched_getaffinity"); 125 | abort(); 126 | } 127 | return CPU_COUNT(&allowed_cpus); 128 | } 129 | 130 | struct test_data { 131 | int64_t total_time; 132 | int thread_id; 133 | }; 134 | 135 | static int64_t difftimespec_ns(const struct timespec after, const struct timespec before) 136 | { 137 | return ((int64_t)after.tv_sec - (int64_t)before.tv_sec) * 1000000000LL 138 | + ((int64_t)after.tv_nsec - (int64_t)before.tv_nsec); 139 | } 140 | 141 | static void *test_percpu_benchmark_thread(void *arg) 142 | { 143 | struct test_data *data = (struct test_data *) arg; 144 | struct timespec t1, t2; 145 | int64_t total_time = 0; 146 | int i, cpu; 147 | int thread_index = rand_order[data->thread_id]; 148 | 149 | set_affinity(); 150 | 151 | if (rseq_register_current_thread()) { 152 | fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", 153 | errno, strerror(errno)); 154 | abort(); 155 | } 156 | 157 | /* 158 | * Rendez-vous across all threads to make sure the number of 159 | * threads >= number of possible CPUs for the entire test duration. 160 | */ 161 | if (__atomic_add_fetch(&nr_active_threads, 1, __ATOMIC_RELAXED) == nr_threads) 162 | __atomic_store_n(&test_go, 1, __ATOMIC_RELAXED); 163 | while (!__atomic_load_n(&test_go, __ATOMIC_RELAXED)) 164 | rseq_barrier(); 165 | 166 | printf("Thread %d running on cpu: %d delay: %dms\n", 167 | thread_index, rseq_current_cpu_raw(), thread_delay * thread_index); 168 | poll(NULL, 0, thread_delay * thread_index); 169 | 170 | for (i = 0; i < 20000; i++) { 171 | /* Access pages once to improve initial cache locality. */ 172 | char *pdata; 173 | int j; 174 | 175 | cpu = get_current_cpu_id(); 176 | pdata = rseq_percpu_ptr(percpudata, cpu); 177 | for (j = 0; j < AREA_LEN; j++) 178 | pdata[j]++; 179 | } 180 | for (i = 0; i < LOOPS_PER_THREAD; i++) { 181 | char *pdata; 182 | int j; 183 | 184 | cpu = get_current_cpu_id(); 185 | pdata = rseq_percpu_ptr(percpudata, cpu); 186 | clock_gettime(CLOCK_MONOTONIC, &t1); 187 | for (j = 0; j < AREA_LEN; j++) 188 | pdata[j]++; 189 | clock_gettime(CLOCK_MONOTONIC, &t2); 190 | total_time += difftimespec_ns(t2, t1); 191 | poll(NULL, 0, thread_delay * nr_threads); 192 | } 193 | 194 | /* 195 | * Rendez-vous before exiting all threads to make sure the 196 | * number of threads >= number of possible CPUs for the entire 197 | * test duration. 198 | */ 199 | if (__atomic_sub_fetch(&nr_active_threads, 1, __ATOMIC_RELAXED) == 0) 200 | __atomic_store_n(&test_stop, 1, __ATOMIC_RELAXED); 201 | while (!__atomic_load_n(&test_stop, __ATOMIC_RELAXED)) 202 | rseq_barrier(); 203 | 204 | if (rseq_unregister_current_thread()) { 205 | fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n", 206 | errno, strerror(errno)); 207 | abort(); 208 | } 209 | data->total_time += total_time; 210 | 211 | return NULL; 212 | } 213 | 214 | static void test_percpu_benchmark(void) 215 | { 216 | int i; 217 | pthread_t test_threads[nr_threads]; 218 | struct test_data test_data[nr_threads]; 219 | int64_t total_time = 0; 220 | 221 | diag("benchmark"); 222 | 223 | memset(test_data, 0, sizeof(struct test_data) * nr_threads); 224 | 225 | for (i = 0; i < nr_threads; i++) { 226 | test_data[i].thread_id = i; 227 | pthread_create(&test_threads[i], NULL, 228 | test_percpu_benchmark_thread, &test_data[i]); 229 | } 230 | 231 | for (i = 0; i < nr_threads; i++) 232 | pthread_join(test_threads[i], NULL); 233 | for (i = 0; i < nr_threads; i++) 234 | total_time += test_data[i].total_time; 235 | diag("Thread delay: %dms, total time: %" PRId64 "ns over %d threads, %d loops per thread -- %" PRId64 "ns per loop", 236 | thread_delay, total_time, nr_threads, LOOPS_PER_THREAD, 237 | total_time / (int64_t)(nr_threads * LOOPS_PER_THREAD)); 238 | } 239 | 240 | int main(int argc, char **argv) 241 | { 242 | struct rseq_mempool_attr *attr = rseq_mempool_attr_create(); 243 | int i; 244 | 245 | if (argc < 2) { 246 | printf("Missing thread delay first argument\n"); 247 | abort(); 248 | } 249 | thread_delay = atoi(argv[1]); 250 | 251 | printf("Thread delay: %dms\n", thread_delay); 252 | 253 | plan_tests(NR_TESTS); 254 | 255 | mempool = rseq_mempool_create(NULL, AREA_LEN, NULL); 256 | if (!mempool) 257 | abort(); 258 | rseq_mempool_attr_destroy(attr); 259 | percpudata = (char __rseq_percpu *) rseq_mempool_percpu_zmalloc(mempool); 260 | 261 | init_affinity(); 262 | nr_threads = get_affinity_weight(); 263 | 264 | srand(time(NULL)); 265 | 266 | for (i = 0; i < nr_threads; i++) 267 | rand_order[i] = i; 268 | for (i = 0; i < nr_threads; i++) { 269 | int index = rand() % nr_threads; 270 | int tmp = rand_order[i]; 271 | rand_order[i] = rand_order[index]; 272 | rand_order[index] = tmp; 273 | } 274 | 275 | if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) { 276 | skip(NR_TESTS, "The rseq syscall is unavailable"); 277 | goto end; 278 | } 279 | 280 | if (rseq_register_current_thread()) { 281 | fail("rseq_register_current_thread(...) failed(%d): %s\n", 282 | errno, strerror(errno)); 283 | goto end; 284 | } else { 285 | pass("Registered current thread with rseq"); 286 | } 287 | if (!rseq_validate_cpu_id()) { 288 | skip(NR_TESTS - 1, "Error: cpu id getter unavailable"); 289 | goto end; 290 | } 291 | test_percpu_benchmark(); 292 | 293 | if (rseq_unregister_current_thread()) { 294 | fail("rseq_unregister_current_thread(...) failed(%d): %s\n", 295 | errno, strerror(errno)); 296 | goto end; 297 | } else { 298 | pass("Unregistered current thread with rseq"); 299 | } 300 | end: 301 | rseq_mempool_percpu_free(percpudata); 302 | rseq_mempool_destroy(mempool); 303 | exit(exit_status()); 304 | } 305 | -------------------------------------------------------------------------------- /tests/basic_percpu_benchmark_cxx.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | // SPDX-FileCopyrightText: 2020 EfficiOS Inc. 3 | 4 | #include "basic_percpu_benchmark.c" 5 | -------------------------------------------------------------------------------- /tests/basic_percpu_ops_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2018-2022 Mathieu Desnoyers 3 | #ifndef _GNU_SOURCE 4 | #define _GNU_SOURCE 5 | #endif 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | 18 | #include "tap.h" 19 | 20 | #define NR_TESTS 4 21 | 22 | #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 23 | 24 | #ifdef BUILDOPT_RSEQ_PERCPU_MM_CID 25 | # define RSEQ_PERCPU RSEQ_PERCPU_MM_CID 26 | static 27 | int get_current_cpu_id(void) 28 | { 29 | return rseq_current_mm_cid(); 30 | } 31 | static 32 | bool rseq_validate_cpu_id(void) 33 | { 34 | return rseq_mm_cid_available(); 35 | } 36 | #else 37 | # define RSEQ_PERCPU RSEQ_PERCPU_CPU_ID 38 | static 39 | int get_current_cpu_id(void) 40 | { 41 | return rseq_cpu_start(); 42 | } 43 | static 44 | bool rseq_validate_cpu_id(void) 45 | { 46 | return rseq_current_cpu_raw() >= 0; 47 | } 48 | #endif 49 | 50 | struct percpu_lock_entry { 51 | intptr_t v; 52 | } __attribute__((aligned(128))); 53 | 54 | struct percpu_lock { 55 | struct percpu_lock_entry c[CPU_SETSIZE]; 56 | }; 57 | 58 | struct test_data_entry { 59 | intptr_t count; 60 | } __attribute__((aligned(128))); 61 | 62 | struct spinlock_test_data { 63 | struct percpu_lock lock; 64 | struct test_data_entry c[CPU_SETSIZE]; 65 | int reps; 66 | }; 67 | 68 | struct percpu_list_node { 69 | intptr_t data; 70 | struct percpu_list_node *next; 71 | }; 72 | 73 | struct percpu_list_entry { 74 | struct percpu_list_node *head; 75 | } __attribute__((aligned(128))); 76 | 77 | struct percpu_list { 78 | struct percpu_list_entry c[CPU_SETSIZE]; 79 | }; 80 | 81 | /* A simple percpu spinlock. Returns the cpu lock was acquired on. */ 82 | static int rseq_this_cpu_lock(struct percpu_lock *lock) 83 | { 84 | int cpu; 85 | 86 | for (;;) { 87 | int ret; 88 | 89 | cpu = get_current_cpu_id(); 90 | ret = rseq_load_cbne_store__ptr(RSEQ_MO_RELAXED, RSEQ_PERCPU, 91 | &lock->c[cpu].v, 0, 1, cpu); 92 | if (rseq_likely(!ret)) 93 | break; 94 | /* Retry if comparison fails or rseq aborts. */ 95 | } 96 | /* 97 | * Acquire semantic when taking lock after control dependency. 98 | * Matches rseq_smp_store_release(). 99 | */ 100 | rseq_smp_acquire__after_ctrl_dep(); 101 | return cpu; 102 | } 103 | 104 | static void rseq_percpu_unlock(struct percpu_lock *lock, int cpu) 105 | { 106 | assert(lock->c[cpu].v == 1); 107 | /* 108 | * Release lock, with release semantic. Matches 109 | * rseq_smp_acquire__after_ctrl_dep(). 110 | */ 111 | rseq_smp_store_release(&lock->c[cpu].v, 0); 112 | } 113 | 114 | static void *test_percpu_spinlock_thread(void *arg) 115 | { 116 | struct spinlock_test_data *data = (struct spinlock_test_data *) arg; 117 | int i, cpu; 118 | 119 | if (rseq_register_current_thread()) { 120 | fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", 121 | errno, strerror(errno)); 122 | abort(); 123 | } 124 | for (i = 0; i < data->reps; i++) { 125 | cpu = rseq_this_cpu_lock(&data->lock); 126 | data->c[cpu].count++; 127 | rseq_percpu_unlock(&data->lock, cpu); 128 | } 129 | if (rseq_unregister_current_thread()) { 130 | fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n", 131 | errno, strerror(errno)); 132 | abort(); 133 | } 134 | 135 | return NULL; 136 | } 137 | 138 | /* 139 | * A simple test which implements a sharded counter using a per-cpu 140 | * lock. Obviously real applications might prefer to simply use a 141 | * per-cpu increment; however, this is reasonable for a test and the 142 | * lock can be extended to synchronize more complicated operations. 143 | */ 144 | static void test_percpu_spinlock(void) 145 | { 146 | const int num_threads = 200; 147 | int i; 148 | uint64_t sum, expected_sum; 149 | pthread_t test_threads[num_threads]; 150 | struct spinlock_test_data data; 151 | 152 | diag("spinlock"); 153 | 154 | memset(&data, 0, sizeof(data)); 155 | data.reps = 5000; 156 | 157 | for (i = 0; i < num_threads; i++) 158 | pthread_create(&test_threads[i], NULL, 159 | test_percpu_spinlock_thread, &data); 160 | 161 | for (i = 0; i < num_threads; i++) 162 | pthread_join(test_threads[i], NULL); 163 | 164 | sum = 0; 165 | for (i = 0; i < CPU_SETSIZE; i++) 166 | sum += data.c[i].count; 167 | 168 | expected_sum = (uint64_t)data.reps * num_threads; 169 | 170 | ok(sum == expected_sum, "spinlock - sum (%" PRIu64 " == %" PRIu64 ")", sum, expected_sum); 171 | } 172 | 173 | static void this_cpu_list_push(struct percpu_list *list, 174 | struct percpu_list_node *node, 175 | int *_cpu) 176 | { 177 | int cpu; 178 | 179 | for (;;) { 180 | intptr_t *targetptr, newval, expect; 181 | int ret; 182 | 183 | cpu = get_current_cpu_id(); 184 | /* Load list->c[cpu].head with single-copy atomicity. */ 185 | expect = (intptr_t)RSEQ_READ_ONCE(list->c[cpu].head); 186 | newval = (intptr_t)node; 187 | targetptr = (intptr_t *)&list->c[cpu].head; 188 | node->next = (struct percpu_list_node *)expect; 189 | ret = rseq_load_cbne_store__ptr(RSEQ_MO_RELAXED, RSEQ_PERCPU, 190 | targetptr, expect, newval, cpu); 191 | if (rseq_likely(!ret)) 192 | break; 193 | /* Retry if comparison fails or rseq aborts. */ 194 | } 195 | if (_cpu) 196 | *_cpu = cpu; 197 | } 198 | 199 | /* 200 | * Unlike a traditional lock-less linked list; the availability of a 201 | * rseq primitive allows us to implement pop without concerns over 202 | * ABA-type races. 203 | */ 204 | static struct percpu_list_node *this_cpu_list_pop(struct percpu_list *list, 205 | int *_cpu) 206 | { 207 | for (;;) { 208 | struct percpu_list_node *head; 209 | intptr_t *targetptr, expectnot, *load; 210 | long offset; 211 | int ret, cpu; 212 | 213 | cpu = get_current_cpu_id(); 214 | targetptr = (intptr_t *)&list->c[cpu].head; 215 | expectnot = (intptr_t)NULL; 216 | offset = offsetof(struct percpu_list_node, next); 217 | load = (intptr_t *)&head; 218 | ret = rseq_load_cbeq_store_add_load_store__ptr(RSEQ_MO_RELAXED, RSEQ_PERCPU, 219 | targetptr, expectnot, 220 | offset, load, cpu); 221 | if (rseq_likely(!ret)) { 222 | if (_cpu) 223 | *_cpu = cpu; 224 | return head; 225 | } 226 | if (ret > 0) 227 | return NULL; 228 | /* Retry if rseq aborts. */ 229 | } 230 | } 231 | 232 | /* 233 | * __percpu_list_pop is not safe against concurrent accesses. Should 234 | * only be used on lists that are not concurrently modified. 235 | */ 236 | static struct percpu_list_node *__percpu_list_pop(struct percpu_list *list, int cpu) 237 | { 238 | struct percpu_list_node *node; 239 | 240 | node = list->c[cpu].head; 241 | if (!node) 242 | return NULL; 243 | list->c[cpu].head = node->next; 244 | return node; 245 | } 246 | 247 | static void *test_percpu_list_thread(void *arg) 248 | { 249 | int i; 250 | struct percpu_list *list = (struct percpu_list *)arg; 251 | 252 | if (rseq_register_current_thread()) { 253 | fprintf(stderr, "Error: rseq_register_current_thread(...) failed(%d): %s\n", 254 | errno, strerror(errno)); 255 | abort(); 256 | } 257 | 258 | for (i = 0; i < 100000; i++) { 259 | struct percpu_list_node *node; 260 | 261 | node = this_cpu_list_pop(list, NULL); 262 | sched_yield(); /* encourage shuffling */ 263 | if (node) 264 | this_cpu_list_push(list, node, NULL); 265 | } 266 | 267 | if (rseq_unregister_current_thread()) { 268 | fprintf(stderr, "Error: rseq_unregister_current_thread(...) failed(%d): %s\n", 269 | errno, strerror(errno)); 270 | abort(); 271 | } 272 | 273 | return NULL; 274 | } 275 | 276 | /* Simultaneous modification to a per-cpu linked list from many threads. */ 277 | static void test_percpu_list(void) 278 | { 279 | int i, j; 280 | uint64_t sum = 0, expected_sum = 0; 281 | struct percpu_list list; 282 | pthread_t test_threads[200]; 283 | 284 | diag("percpu_list"); 285 | 286 | memset(&list, 0, sizeof(list)); 287 | 288 | /* Generate list entries for every possible cpu. */ 289 | for (i = 0; i < CPU_SETSIZE; i++) { 290 | for (j = 1; j <= 100; j++) { 291 | struct percpu_list_node *node; 292 | 293 | expected_sum += j; 294 | 295 | node = (struct percpu_list_node *) malloc(sizeof(*node)); 296 | assert(node); 297 | node->data = j; 298 | node->next = list.c[i].head; 299 | list.c[i].head = node; 300 | } 301 | } 302 | 303 | for (i = 0; i < 200; i++) 304 | pthread_create(&test_threads[i], NULL, 305 | test_percpu_list_thread, &list); 306 | 307 | for (i = 0; i < 200; i++) 308 | pthread_join(test_threads[i], NULL); 309 | 310 | for (i = 0; i < CPU_SETSIZE; i++) { 311 | struct percpu_list_node *node; 312 | 313 | while ((node = __percpu_list_pop(&list, i))) { 314 | sum += node->data; 315 | free(node); 316 | } 317 | } 318 | 319 | /* 320 | * All entries should now be accounted for. 321 | */ 322 | ok(sum == expected_sum, "percpu_list - sum (%" PRIu64 " == %" PRIu64 ")", sum, expected_sum); 323 | } 324 | 325 | int main(void) 326 | { 327 | plan_tests(NR_TESTS); 328 | 329 | if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) { 330 | skip(NR_TESTS, "The rseq syscall is unavailable"); 331 | goto end; 332 | } 333 | 334 | if (rseq_register_current_thread()) { 335 | fail("rseq_register_current_thread(...) failed(%d): %s\n", 336 | errno, strerror(errno)); 337 | goto end; 338 | } else { 339 | pass("Registered current thread with rseq"); 340 | } 341 | if (!rseq_validate_cpu_id()) { 342 | skip(NR_TESTS - 1, "Error: cpu id getter unavailable"); 343 | goto end; 344 | } 345 | test_percpu_spinlock(); 346 | test_percpu_list(); 347 | 348 | if (rseq_unregister_current_thread()) { 349 | fail("rseq_unregister_current_thread(...) failed(%d): %s\n", 350 | errno, strerror(errno)); 351 | goto end; 352 | } else { 353 | pass("Unregistered current thread with rseq"); 354 | } 355 | end: 356 | exit(exit_status()); 357 | } 358 | -------------------------------------------------------------------------------- /tests/basic_percpu_ops_test_cxx.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | // SPDX-FileCopyrightText: 2020 EfficiOS Inc. 3 | 4 | #include "basic_percpu_ops_test.c" 5 | -------------------------------------------------------------------------------- /tests/basic_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2020 Mathieu Desnoyers 3 | /* 4 | * Basic test coverage for critical regions and rseq_current_cpu(). 5 | */ 6 | 7 | #ifndef _GNU_SOURCE 8 | #define _GNU_SOURCE 9 | #endif 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | #include "tap.h" 20 | 21 | /* 22 | * Ensure the main executable has at least one TLS variable which will be 23 | * allocated before the rseq area, making sure the rseq_offset is not 0. This 24 | * allows testing that the rseq_offset variable is properly initialized by 25 | * checking it is not 0. 26 | * 27 | * Most toolchains will add at least one main exec TLS variable but it's 28 | * currently not the case on RISC-V. 29 | */ 30 | __thread int dummy_tls = -1; 31 | 32 | static void test_registered(void) 33 | { 34 | struct rseq_abi *rseq_abi = rseq_get_abi(); 35 | 36 | ok(rseq_flags == 0, "rseq_flags after registration is 0 (%d)", rseq_flags); 37 | ok(rseq_size >= 20, "rseq_size after registration is 20 or greater (%d)", rseq_size); 38 | ok(rseq_offset != 0, "rseq_offset after registration is not 0 (%td)", rseq_offset); 39 | 40 | ok((int32_t) rseq_abi->cpu_id >= 0, 41 | "rseq->cpu_id after registration is 0 or greater (%d)", 42 | (int32_t) rseq_abi->cpu_id); 43 | } 44 | 45 | static void test_cpu_pointer(void) 46 | { 47 | cpu_set_t affinity, test_affinity; 48 | int ret, i; 49 | 50 | ret = sched_getaffinity(0, sizeof(affinity), &affinity); 51 | ok(ret == 0, "Get current thread affinity mask"); 52 | 53 | CPU_ZERO(&test_affinity); 54 | for (i = 0; i < CPU_SETSIZE; i++) { 55 | if (CPU_ISSET(i, &affinity)) { 56 | int node; 57 | 58 | CPU_SET(i, &test_affinity); 59 | 60 | ret = sched_setaffinity(0, sizeof(test_affinity), 61 | &test_affinity); 62 | ok(ret == 0, "Set affinity mask to CPU %d exclusively", i); 63 | 64 | ok(sched_getcpu() == i, "sched_getcpu returns CPU %d", i); 65 | ok(rseq_current_cpu() == (unsigned int) i, "rseq_current_cpu returns CPU %d", i); 66 | ok(rseq_current_cpu_raw() == i, "rseq_current_cpu_raw returns CPU %d", i); 67 | ok(rseq_cpu_start() == (unsigned int) i, "rseq_cpu_start returns CPU %d", i); 68 | node = rseq_fallback_current_node(); 69 | ok(rseq_fallback_current_node() == node, "rseq_fallback_current_node returns node %d", node); 70 | CPU_CLR(i, &test_affinity); 71 | } 72 | } 73 | 74 | ret = sched_setaffinity(0, sizeof(affinity), &affinity); 75 | ok(ret == 0, "Restore current thread initial affinity mask"); 76 | } 77 | 78 | int main(void) 79 | { 80 | /* 81 | * Skip all tests if the rseq syscall is unavailable 82 | */ 83 | if (rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) { 84 | plan_no_plan(); 85 | } else { 86 | plan_skip_all("The rseq syscall is unavailable"); 87 | } 88 | 89 | if (rseq_register_current_thread()) { 90 | fail("rseq_register_current_thread(...) failed(%d): %s\n", 91 | errno, strerror(errno)); 92 | goto end; 93 | } else { 94 | pass("Registered current thread with rseq"); 95 | } 96 | 97 | test_registered(); 98 | test_cpu_pointer(); 99 | 100 | if (rseq_unregister_current_thread()) { 101 | fail("rseq_unregister_current_thread(...) failed(%d): %s\n", 102 | errno, strerror(errno)); 103 | goto end; 104 | } else { 105 | pass("Unregistered current thread with rseq"); 106 | } 107 | 108 | end: 109 | exit(exit_status()); 110 | } 111 | -------------------------------------------------------------------------------- /tests/basic_test_cxx.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | // SPDX-FileCopyrightText: 2020 EfficiOS Inc. 3 | 4 | #include "basic_test.c" 5 | -------------------------------------------------------------------------------- /tests/disable-rseq-syscall.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 Michael Jeanson 3 | 4 | #include 5 | #include 6 | 7 | /* 8 | * Library constructor. 9 | * 10 | * Apply a seccomp policy that blocks access to the rseq syscall and returns 11 | * ENOSYS. 12 | */ 13 | static __attribute__((constructor)) 14 | void disable_rseq_syscall(void) 15 | { 16 | scmp_filter_ctx ctx; 17 | 18 | ctx = seccomp_init(SCMP_ACT_ALLOW); 19 | seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOSYS), SCMP_SYS(rseq), 0); 20 | seccomp_load(ctx); 21 | } 22 | -------------------------------------------------------------------------------- /tests/fork_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 Michael Jeanson 3 | 4 | #ifndef _GNU_SOURCE 5 | #define _GNU_SOURCE 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | 16 | #include "tap.h" 17 | 18 | #define NR_TESTS 4 19 | 20 | /* 21 | * Check that a registration from a parent is still active in the child. 22 | */ 23 | 24 | static int sys_rseq(void *rseq_abi, uint32_t rseq_len, 25 | int flags, uint32_t sig) 26 | { 27 | return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); 28 | } 29 | 30 | static 31 | int test_child(void) 32 | { 33 | int ret, errno_copy; 34 | struct rseq_abi *global_rseq = rseq_get_abi(); 35 | 36 | /* The registration from the parent should survive in the child. */ 37 | 38 | ret = sys_rseq(global_rseq, 32, 0, RSEQ_SIG); 39 | errno_copy = errno; 40 | ok(ret != 0 && errno_copy == EBUSY, "Registration is still active in the child"); 41 | 42 | ok((int32_t) global_rseq->cpu_id >= 0, 43 | "rseq->cpu_id after registration is 0 or greater (%d)", 44 | (int32_t) global_rseq->cpu_id); 45 | 46 | return exit_status(); 47 | } 48 | 49 | int main(void) 50 | { 51 | pid_t pid; 52 | int ret, wstatus; 53 | struct rseq_abi *global_rseq = rseq_get_abi(); 54 | 55 | /* 56 | * Skip all tests if the rseq syscall is unavailable 57 | */ 58 | if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) { 59 | plan_skip_all("The rseq syscall is unavailable"); 60 | } 61 | 62 | plan_tests(NR_TESTS); 63 | 64 | ret = rseq_register_current_thread(); 65 | ok(ret == 0, "Registered rseq in the parent"); 66 | 67 | ok((int32_t) global_rseq->cpu_id >= 0, 68 | "rseq->cpu_id after registration is 0 or greater (%d)", 69 | (int32_t) global_rseq->cpu_id); 70 | 71 | pid = fork(); 72 | switch (pid) { 73 | case -1: 74 | perror("fork"); 75 | ret = EXIT_FAILURE; 76 | break; 77 | case 0: 78 | /* Child */ 79 | ret = test_child(); 80 | break; 81 | default: 82 | /* Parent */ 83 | ret = waitpid(pid, &wstatus, 0); 84 | if (ret < 0) { 85 | ret = EXIT_FAILURE; 86 | } else { 87 | /* Let the child handle the tap cleanup. */ 88 | disable_cleanup(); 89 | 90 | ret = WEXITSTATUS(wstatus); 91 | } 92 | break; 93 | } 94 | 95 | return ret; 96 | } 97 | -------------------------------------------------------------------------------- /tests/fork_test_cxx.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 EfficiOS Inc. 3 | 4 | #include "fork_test.c" 5 | -------------------------------------------------------------------------------- /tests/mempool_cow_race_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 Mathieu Desnoyers 3 | 4 | /* 5 | * rseq memory pool COW race test. 6 | * 7 | * Test that the entire malloc init value is visible in CPU mappings. If 8 | * the COW page copy race vs init happens while init is in the middle of 9 | * storing to the newly allocated area, iteration on all CPUs comparing 10 | * the visible content to the init value is responsible for detecting 11 | * and mitigating uninitialized or partially initialized init value from 12 | * the point of view of a CPU. Validate that this scheme has the 13 | * intended effect wrt a concurrent COW caused by storing to a nearby 14 | * per-cpu area on the same page. 15 | */ 16 | 17 | #ifndef _GNU_SOURCE 18 | #define _GNU_SOURCE 19 | #endif 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include "../src/rseq-utils.h" 36 | 37 | #include "tap.h" 38 | 39 | #define TEST_DURATION_S 10 /* seconds */ 40 | #define TEST_ARRAY_LEN 256 41 | 42 | enum phase { 43 | PHASE_RESET_POOL, 44 | PHASE_WRITE_POOL, 45 | }; 46 | 47 | struct test_data { 48 | char c[TEST_ARRAY_LEN]; 49 | }; 50 | 51 | struct test_thread_args { 52 | struct rseq_mempool *mempool; 53 | int phase; /* enum phase */ 54 | int stop_init_thread; 55 | int stop_writer_thread; 56 | struct test_data *ptr1; 57 | struct test_data *ptr2; 58 | }; 59 | 60 | struct test_data init_value; 61 | 62 | static void *test_init_thread(void *arg) 63 | { 64 | struct test_thread_args *thread_args = (struct test_thread_args *) arg; 65 | 66 | while (!RSEQ_READ_ONCE(thread_args->stop_init_thread)) { 67 | struct rseq_mempool_attr *attr; 68 | struct rseq_mempool *mempool; 69 | struct test_data *p; 70 | int ret, i; 71 | 72 | attr = rseq_mempool_attr_create(); 73 | ret = rseq_mempool_attr_set_robust(attr); 74 | if (ret) 75 | abort(); 76 | ret = rseq_mempool_attr_set_percpu(attr, 0, 1); 77 | if (ret) 78 | abort(); 79 | ret = rseq_mempool_attr_set_max_nr_ranges(attr, 1); 80 | if (ret) 81 | abort(); 82 | ret = rseq_mempool_attr_set_populate_policy(attr, RSEQ_MEMPOOL_POPULATE_COW_INIT); 83 | if (ret) 84 | abort(); 85 | mempool = rseq_mempool_create("test_data", sizeof(struct test_data), attr); 86 | if (!mempool) 87 | abort(); 88 | thread_args->mempool = mempool; 89 | rseq_mempool_attr_destroy(attr); 90 | 91 | thread_args->ptr1 = (struct test_data __rseq_percpu *) rseq_mempool_percpu_malloc(mempool); 92 | if (!thread_args->ptr1) 93 | abort(); 94 | 95 | rseq_smp_store_release(&thread_args->phase, PHASE_WRITE_POOL); 96 | 97 | /* malloc init runs concurrently with COW. */ 98 | thread_args->ptr2 = (struct test_data __rseq_percpu *) 99 | rseq_mempool_percpu_malloc_init(mempool, 100 | &init_value, sizeof(struct test_data)); 101 | if (!thread_args->ptr2) 102 | abort(); 103 | 104 | p = rseq_percpu_ptr(thread_args->ptr2, 0); 105 | for (i = 0; i < TEST_ARRAY_LEN; i++) { 106 | if (p->c[i] != 0x22) { 107 | fprintf(stderr, "Unexpected value\n"); 108 | abort(); 109 | } 110 | } 111 | 112 | while (rseq_smp_load_acquire(&thread_args->phase) != PHASE_RESET_POOL) { } 113 | 114 | rseq_mempool_percpu_free(thread_args->ptr2); 115 | rseq_mempool_percpu_free(thread_args->ptr1); 116 | 117 | if (rseq_mempool_destroy(mempool)) 118 | abort(); 119 | } 120 | RSEQ_WRITE_ONCE(thread_args->stop_writer_thread, 1); 121 | rseq_smp_store_release(&thread_args->phase, PHASE_WRITE_POOL); 122 | return NULL; 123 | } 124 | 125 | static void *test_writer_thread(void *arg) 126 | { 127 | struct test_thread_args *thread_args = (struct test_thread_args *) arg; 128 | 129 | for (;;) { 130 | unsigned int loop, delay; 131 | 132 | delay = rand() % 10000; 133 | while (rseq_smp_load_acquire(&thread_args->phase) != PHASE_WRITE_POOL) { } 134 | 135 | if (RSEQ_READ_ONCE(thread_args->stop_writer_thread)) 136 | break; 137 | 138 | for (loop = 0; loop < delay; loop++) 139 | rseq_barrier(); 140 | 141 | /* Trigger COW. */ 142 | rseq_percpu_ptr(thread_args->ptr1, 0)->c[0] = 0x33; 143 | 144 | rseq_smp_store_release(&thread_args->phase, PHASE_RESET_POOL); 145 | } 146 | 147 | return NULL; 148 | } 149 | 150 | int main(void) 151 | { 152 | struct test_thread_args thread_args = {}; 153 | pthread_t writer_thread, init_thread; 154 | unsigned int remain; 155 | int ret; 156 | 157 | plan_no_plan(); 158 | 159 | diag("Beginning COW vs malloc init race validation (%u seconds)...", TEST_DURATION_S); 160 | srand(0x42); 161 | 162 | memset(&init_value.c, 0x22, TEST_ARRAY_LEN); 163 | 164 | thread_args.phase = PHASE_RESET_POOL; 165 | 166 | ret = pthread_create(&init_thread, NULL, test_init_thread, &thread_args); 167 | if (ret) { 168 | errno = ret; 169 | perror("pthread_create"); 170 | abort(); 171 | } 172 | 173 | ret = pthread_create(&writer_thread, NULL, test_writer_thread, &thread_args); 174 | if (ret) { 175 | errno = ret; 176 | perror("pthread_create"); 177 | abort(); 178 | } 179 | 180 | remain = TEST_DURATION_S; 181 | do { 182 | remain = sleep(remain); 183 | } while (remain > 0); 184 | 185 | RSEQ_WRITE_ONCE(thread_args.stop_init_thread, 1); 186 | 187 | ret = pthread_join(writer_thread, NULL); 188 | if (ret) { 189 | errno = ret; 190 | perror("pthread_join"); 191 | abort(); 192 | } 193 | 194 | ret = pthread_join(init_thread, NULL); 195 | if (ret) { 196 | errno = ret; 197 | perror("pthread_join"); 198 | abort(); 199 | } 200 | 201 | ok(1, "Validate COW vs malloc init race"); 202 | 203 | exit(exit_status()); 204 | } 205 | -------------------------------------------------------------------------------- /tests/mempool_cow_race_test_cxx.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | // SPDX-FileCopyrightText: 2024 EfficiOS Inc. 3 | 4 | #include "mempool_cow_race_test.c" 5 | -------------------------------------------------------------------------------- /tests/mempool_test_cxx.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | // SPDX-FileCopyrightText: 2024 EfficiOS Inc. 3 | 4 | #include "mempool_test.c" 5 | -------------------------------------------------------------------------------- /tests/no_syscall_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 Michael Jeanson 3 | 4 | #ifndef _GNU_SOURCE 5 | #define _GNU_SOURCE 6 | #endif 7 | #include 8 | 9 | #include 10 | 11 | #include "tap.h" 12 | 13 | #define NR_TESTS 4 14 | 15 | /* 16 | * Ensure the main executable has at least one TLS variable which will be 17 | * allocated before the rseq area, making sure the rseq_offset is not 0. This 18 | * allows testing that the rseq_offset variable is properly initialized by 19 | * checking it is not 0. 20 | * 21 | * Most toolchains will add at least one main exec TLS variable but it's 22 | * currently not the case on RISC-V. 23 | */ 24 | __thread int dummy_tls = -1; 25 | 26 | /* 27 | * Check the state of the public symbols when the rseq syscall is unavailable. 28 | * 29 | * This test must be used with an LD_PRELOAD library to deny access to the 30 | * syscall, or on a kernel that doesn't implement the syscall. 31 | */ 32 | 33 | int main(void) 34 | { 35 | struct rseq_abi *rseq_abi; 36 | 37 | plan_tests(NR_TESTS); 38 | 39 | if (rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) { 40 | fail("The rseq syscall should be unavailable"); 41 | goto end; 42 | } 43 | 44 | /* The rseq syscall is disabled, no registration is possible. */ 45 | 46 | ok(rseq_flags == 0, "rseq_flags prior to registration is 0 (%d)", rseq_flags); 47 | ok(rseq_size == 0, "rseq_size prior to registration is 0 (%d)", rseq_size); 48 | ok(rseq_offset != 0, "rseq_offset prior to registration is not 0 (%td)", rseq_offset); 49 | 50 | rseq_abi = rseq_get_abi(); 51 | ok((int32_t) rseq_abi->cpu_id == RSEQ_ABI_CPU_ID_UNINITIALIZED, 52 | "rseq->cpu_id is set to RSEQ_ABI_CPU_ID_UNINITIALIZED (%d)", 53 | (int32_t) rseq_abi->cpu_id); 54 | 55 | end: 56 | exit(exit_status()); 57 | } 58 | -------------------------------------------------------------------------------- /tests/no_syscall_test_cxx.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 EfficiOS Inc. 3 | 4 | #include "no_syscall_test.c" 5 | -------------------------------------------------------------------------------- /tests/param_test_cxx.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT */ 2 | // SPDX-FileCopyrightText: 2020 EfficiOS Inc. 3 | 4 | #include "param_test.c" 5 | -------------------------------------------------------------------------------- /tests/run_fork_test.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2024 Michael Jeanson 4 | 5 | SH_TAP=0 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" "${RSEQ_TESTS_BUILDDIR}/fork_test.tap" 17 | -------------------------------------------------------------------------------- /tests/run_fork_test_cxx.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2024 Michael Jeanson 4 | 5 | SH_TAP=0 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" "${RSEQ_TESTS_BUILDDIR}/fork_test_cxx.tap" 17 | -------------------------------------------------------------------------------- /tests/run_no_syscall_test.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2024 Michael Jeanson 4 | 5 | SH_TAP=0 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | CURDIR="${RSEQ_TESTS_BUILDDIR}/" 17 | 18 | LIBDISABLE_RSEQ_SYSCALL_PATH="${CURDIR}/.libs" 19 | LIBDISABLE_RSEQ_SYSCALL="${LIBDISABLE_RSEQ_SYSCALL_PATH}/libdisable-rseq-syscall.so" 20 | 21 | LD_PRELOAD="${LIBDISABLE_RSEQ_SYSCALL}" "${CURDIR}/no_syscall_test.tap" 22 | -------------------------------------------------------------------------------- /tests/run_no_syscall_test_cxx.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2024 Michael Jeanson 4 | 5 | SH_TAP=0 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | CURDIR="${RSEQ_TESTS_BUILDDIR}/" 17 | 18 | LIBDISABLE_RSEQ_SYSCALL_PATH="${CURDIR}/.libs" 19 | LIBDISABLE_RSEQ_SYSCALL="${LIBDISABLE_RSEQ_SYSCALL_PATH}/libdisable-rseq-syscall.so" 20 | 21 | LD_PRELOAD="${LIBDISABLE_RSEQ_SYSCALL}" "${CURDIR}/no_syscall_test_cxx.tap" 22 | -------------------------------------------------------------------------------- /tests/run_param_test.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2020 EfficiOS Inc. 4 | 5 | SH_TAP=1 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | 17 | EXTRA_ARGS=("${@}") 18 | 19 | REPS=1000 20 | NR_CPUS=$(nproc) 21 | NR_THREADS=$((6 * NR_CPUS)) 22 | 23 | 24 | function do_test() 25 | { 26 | local test_name=$1 27 | shift 28 | local args=("$@") 29 | 30 | "$RSEQ_TESTS_BUILDDIR"/param_test "${args[@]}" -r ${REPS} -t ${NR_THREADS} "${EXTRA_ARGS[@]}" 31 | ok $? "Running test ${test_name}" 32 | 33 | "$RSEQ_TESTS_BUILDDIR"/param_test_compare_twice "${args[@]}" -r ${REPS} -t ${NR_THREADS} "${EXTRA_ARGS[@]}" 34 | ok $? "Running compare-twice test ${test_name}" 35 | 36 | "$RSEQ_TESTS_BUILDDIR"/param_test_mm_cid "${args[@]}" -r ${REPS} -t ${NR_THREADS} "${EXTRA_ARGS[@]}" 37 | res=$? 38 | skip $(($res != 2 )) "Running mm_cid test ${test_name}" 1 || { 39 | ok $res "Running mm_cid test ${test_name}" 40 | } 41 | 42 | "$RSEQ_TESTS_BUILDDIR"/param_test_mm_cid_compare_twice "${args[@]}" -r ${REPS} -t ${NR_THREADS} "${EXTRA_ARGS[@]}" 43 | res=$? 44 | skip $(($res != 2)) "Running compare-twice mm_cid test ${test_name}" 1 || { 45 | ok $res "Running compare-twice mm_cid test ${test_name}" 46 | } 47 | } 48 | 49 | function do_tests() 50 | { 51 | local args=("$@") 52 | 53 | do_test "spinlock" -T s "${@}" 54 | do_test "list" -T l "${@}" 55 | do_test "buffer" -T b "${@}" 56 | do_test "buffer with barrier" -T b -M "${@}" 57 | do_test "memcpy" -T m "${@}" 58 | do_test "memcpy with barrier" -T m -M "${@}" 59 | do_test "increment" -T i "${@}" 60 | do_test "membarrier" -T r "${@}" 61 | } 62 | 63 | function do_tests_loops() 64 | { 65 | local nr_loops="$1" 66 | 67 | do_tests -1 "${nr_loops}" 68 | do_tests -2 "${nr_loops}" 69 | do_tests -3 "${nr_loops}" 70 | do_tests -4 "${nr_loops}" 71 | do_tests -5 "${nr_loops}" 72 | do_tests -6 "${nr_loops}" 73 | do_tests -7 "${nr_loops}" 74 | do_tests -8 "${nr_loops}" 75 | do_tests -9 "${nr_loops}" 76 | } 77 | 78 | function do_tests_inject() 79 | { 80 | local args=("$@") 81 | 82 | do_tests -7 -1 "${@}" 83 | do_tests -8 -1 "${@}" 84 | do_tests -9 -1 "${@}" 85 | } 86 | 87 | 88 | "$RSEQ_TESTS_BUILDDIR"/param_test -c 89 | if [[ $? == 2 ]]; then 90 | plan_skip_all "The rseq syscall is unavailable" 91 | else 92 | plan_tests $(( 4 * 8 * 37 )) 93 | fi 94 | 95 | diag "Default parameters" 96 | do_tests 97 | 98 | diag "Loop injection: 10000 loops" 99 | do_tests_loops 10000 100 | 101 | diag "Yield injection (25%)" 102 | do_tests_inject -m 4 -y 103 | 104 | diag "Yield injection (50%)" 105 | do_tests_inject -m 2 -y 106 | 107 | diag "Yield injection (100%)" 108 | do_tests_inject -m 1 -y 109 | 110 | diag "Kill injection (25%)" 111 | do_tests_inject -m 4 -k 112 | 113 | diag "Kill injection (50%)" 114 | do_tests_inject -m 2 -k 115 | 116 | diag "Kill injection (100%)" 117 | do_tests_inject -m 1 -k 118 | 119 | diag "Sleep injection (1ms, 25%)" 120 | do_tests_inject -m 4 -s 1 121 | 122 | diag "Sleep injection (1ms, 50%)" 123 | do_tests_inject -m 2 -s 1 124 | 125 | diag "Sleep injection (1ms, 100%)" 126 | do_tests_inject -m 1 -s 1 127 | -------------------------------------------------------------------------------- /tests/run_param_test_cxx.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2020 EfficiOS Inc. 4 | 5 | SH_TAP=1 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | 17 | EXTRA_ARGS=("${@}") 18 | 19 | REPS=1000 20 | NR_CPUS=$(nproc) 21 | NR_THREADS=$((6 * NR_CPUS)) 22 | 23 | 24 | function do_test() 25 | { 26 | local test_name=$1 27 | shift 28 | local args=("$@") 29 | 30 | "$RSEQ_TESTS_BUILDDIR"/param_test_cxx "${args[@]}" -r ${REPS} -t ${NR_THREADS} "${EXTRA_ARGS[@]}" 31 | ok $? "Running test ${test_name}" 32 | 33 | "$RSEQ_TESTS_BUILDDIR"/param_test_compare_twice_cxx "${args[@]}" -r ${REPS} -t ${NR_THREADS} "${EXTRA_ARGS[@]}" 34 | ok $? "Running compare-twice test ${test_name}" 35 | } 36 | 37 | function do_tests() 38 | { 39 | local args=("$@") 40 | 41 | do_test "spinlock" -T s "${@}" 42 | do_test "list" -T l "${@}" 43 | do_test "buffer" -T b "${@}" 44 | do_test "buffer with barrier" -T b -M "${@}" 45 | do_test "memcpy" -T m "${@}" 46 | do_test "memcpy with barrier" -T m -M "${@}" 47 | do_test "increment" -T i "${@}" 48 | } 49 | 50 | function do_tests_loops() 51 | { 52 | local nr_loops="$1" 53 | 54 | do_tests -1 "${nr_loops}" 55 | do_tests -2 "${nr_loops}" 56 | do_tests -3 "${nr_loops}" 57 | do_tests -4 "${nr_loops}" 58 | do_tests -5 "${nr_loops}" 59 | do_tests -6 "${nr_loops}" 60 | do_tests -7 "${nr_loops}" 61 | do_tests -8 "${nr_loops}" 62 | do_tests -9 "${nr_loops}" 63 | } 64 | 65 | function do_tests_inject() 66 | { 67 | local args=("$@") 68 | 69 | do_tests -7 -1 "${@}" 70 | do_tests -8 -1 "${@}" 71 | do_tests -9 -1 "${@}" 72 | } 73 | 74 | 75 | "$RSEQ_TESTS_BUILDDIR"/param_test -c 76 | if [[ $? == 2 ]]; then 77 | plan_skip_all "The rseq syscall is unavailable" 78 | else 79 | plan_tests $(( 2 * 7 * 37 )) 80 | fi 81 | 82 | diag "Default parameters" 83 | do_tests 84 | 85 | diag "Loop injection: 10000 loops" 86 | do_tests_loops 10000 87 | 88 | diag "Yield injection (25%)" 89 | do_tests_inject -m 4 -y 90 | 91 | diag "Yield injection (50%)" 92 | do_tests_inject -m 2 -y 93 | 94 | diag "Yield injection (100%)" 95 | do_tests_inject -m 1 -y 96 | 97 | diag "Kill injection (25%)" 98 | do_tests_inject -m 4 -k 99 | 100 | diag "Kill injection (50%)" 101 | do_tests_inject -m 2 -k 102 | 103 | diag "Kill injection (100%)" 104 | do_tests_inject -m 1 -k 105 | 106 | diag "Sleep injection (1ms, 25%)" 107 | do_tests_inject -m 4 -s 1 108 | 109 | diag "Sleep injection (1ms, 50%)" 110 | do_tests_inject -m 2 -s 1 111 | 112 | diag "Sleep injection (1ms, 100%)" 113 | do_tests_inject -m 1 -s 1 114 | -------------------------------------------------------------------------------- /tests/run_syscall_errors_test.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2024 Michael Jeanson 4 | 5 | SH_TAP=0 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" "${RSEQ_TESTS_BUILDDIR}/syscall_errors_test.tap" 17 | -------------------------------------------------------------------------------- /tests/run_syscall_errors_test_cxx.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2024 Michael Jeanson 4 | 5 | SH_TAP=0 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" "${RSEQ_TESTS_BUILDDIR}/syscall_errors_test_cxx.tap" 17 | -------------------------------------------------------------------------------- /tests/run_unregistered_test.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2024 Michael Jeanson 4 | 5 | SH_TAP=0 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" "${RSEQ_TESTS_BUILDDIR}/unregistered_test.tap" 17 | -------------------------------------------------------------------------------- /tests/run_unregistered_test_cxx.tap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # SPDX-License-Identifier: MIT 3 | # SPDX-FileCopyrightText: 2024 Michael Jeanson 4 | 5 | SH_TAP=0 6 | 7 | if [ "x${RSEQ_TESTS_SRCDIR:-}" != "x" ]; then 8 | UTILSSH="$RSEQ_TESTS_SRCDIR/utils/utils.sh" 9 | else 10 | UTILSSH="$(dirname "$0")/utils/utils.sh" 11 | fi 12 | 13 | # shellcheck source=./utils/utils.sh 14 | source "$UTILSSH" 15 | 16 | GLIBC_TUNABLES="${GLIBC_TUNABLES:-}:glibc.pthread.rseq=0" "${RSEQ_TESTS_BUILDDIR}/unregistered_test_cxx.tap" 17 | -------------------------------------------------------------------------------- /tests/syscall_errors_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 Michael Jeanson 3 | 4 | #ifndef _GNU_SOURCE 5 | #define _GNU_SOURCE 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | #include "tap.h" 16 | 17 | #if (RSEQ_BITS_PER_LONG == 64) && (!defined(RSEQ_ARCH_S390)) 18 | #define NR_TESTS 8 19 | #define RUN_RSEQ_INVALID_ADDRESS_TEST 1 20 | #else 21 | #define NR_TESTS 7 22 | #endif 23 | 24 | static int sys_rseq(void *rseq_abi, uint32_t rseq_len, 25 | int flags, uint32_t sig) 26 | { 27 | return syscall(__NR_rseq, rseq_abi, rseq_len, flags, sig); 28 | } 29 | 30 | /* 31 | * Check the value of errno on some expected failures of the rseq syscall. 32 | */ 33 | 34 | int main(void) 35 | { 36 | struct rseq_abi *global_rseq = rseq_get_abi(); 37 | int ret; 38 | int errno_copy; 39 | 40 | plan_tests(NR_TESTS); 41 | 42 | if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) { 43 | skip(NR_TESTS, "rseq syscall unavailable"); 44 | goto end; 45 | } 46 | 47 | /* The current thread is NOT registered. */ 48 | 49 | /* EINVAL */ 50 | errno = 0; 51 | ret = sys_rseq(global_rseq, 32, -1, RSEQ_SIG); 52 | errno_copy = errno; 53 | ok(ret != 0 && errno_copy == EINVAL, "Invalid flag set errno to EINVAL (ret = %d, errno = %s)", ret, strerrorname_np(errno_copy)); 54 | 55 | errno = 0; 56 | ret = sys_rseq((char *) global_rseq + 1, 32, 0, RSEQ_SIG); 57 | errno_copy = errno; 58 | ok(ret != 0 && errno_copy == EINVAL, "Unaligned rseq_abi set errno to EINVAL (ret = %d, errno = %s)", ret, strerrorname_np(errno_copy)); 59 | 60 | errno = 0; 61 | ret = sys_rseq(global_rseq, 31, 0, RSEQ_SIG); 62 | errno_copy = errno; 63 | ok(ret != 0 && errno_copy == EINVAL, "Invalid size set errno to EINVAL (ret = %d, errno = %s)", ret, strerrorname_np(errno_copy)); 64 | 65 | 66 | #if defined(RUN_RSEQ_INVALID_ADDRESS_TEST) 67 | /* 68 | * We haven't found a reliable way to find an invalid address when 69 | * running a 32bit userspace on a 64bit kernel, so only run this test 70 | * on 64bit builds for the moment. 71 | * 72 | * Also exclude architectures that select 73 | * CONFIG_ALTERNATE_USER_ADDRESS_SPACE where the kernel and userspace 74 | * have their own address space and this failure can't happen. 75 | */ 76 | 77 | /* EFAULT */ 78 | errno = 0; 79 | ret = sys_rseq((void *) -4096UL, 32, 0, RSEQ_SIG); 80 | errno_copy = errno; 81 | ok(ret != 0 && errno_copy == EFAULT, "Invalid address set errno to EFAULT (ret = %d, errno = %s)", ret, strerrorname_np(errno_copy)); 82 | #endif 83 | 84 | errno = 0; 85 | ret = sys_rseq(global_rseq, 32, 0, RSEQ_SIG); 86 | errno_copy = errno; 87 | ok(ret == 0, "Register rseq for the current thread (ret = %d, errno = %s)", ret, strerrorname_np(errno_copy)); 88 | 89 | /* The current thread is registered. */ 90 | 91 | /* EBUSY */ 92 | errno = 0; 93 | ret = sys_rseq(global_rseq, 32, 0, RSEQ_SIG); 94 | errno_copy = errno; 95 | ok(ret != 0 && errno_copy == EBUSY, "Same registration set errno to EBUSY (ret = %d, errno = %s)", ret, strerrorname_np(errno_copy)); 96 | 97 | /* EPERM */ 98 | errno = 0; 99 | ret = sys_rseq(global_rseq, 32, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG + 1); 100 | errno_copy = errno; 101 | ok(ret != 0 && errno_copy == EPERM, "Unregistration with wrong RSEQ_SIG set errno to EPERM (ret = %d, errno = %s)", ret, strerrorname_np(errno_copy)); 102 | 103 | errno = 0; 104 | ret = sys_rseq(global_rseq, 32, RSEQ_ABI_FLAG_UNREGISTER, RSEQ_SIG); 105 | errno_copy = errno; 106 | ok(ret == 0, "Unregister rseq for the current thread (ret = %d, errno = %s)", ret, strerrorname_np(errno_copy)); 107 | 108 | end: 109 | exit(exit_status()); 110 | } 111 | -------------------------------------------------------------------------------- /tests/syscall_errors_test_cxx.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 EfficiOS Inc. 3 | 4 | #include "syscall_errors_test.c" 5 | -------------------------------------------------------------------------------- /tests/unit/.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 EfficiOS Inc. 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | /arch_mo.tap 6 | /arch_mo_cxx.tap 7 | -------------------------------------------------------------------------------- /tests/unit/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2023 EfficiOS Inc. 2 | # 3 | # SPDX-License-Identifier: MIT 4 | 5 | AM_CPPFLAGS += -I$(top_srcdir)/tests/utils 6 | 7 | TEST_EXTENSIONS = .tap 8 | TAP_LOG_DRIVER_FLAGS = --merge --comments 9 | TAP_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' \ 10 | RSEQ_TESTS_SRCDIR='$(abs_top_srcdir)/tests' \ 11 | RSEQ_TESTS_BUILDDIR='$(abs_top_builddir)/tests' \ 12 | $(SHELL) $(abs_top_srcdir)/tests/utils/tap-driver.sh 13 | 14 | noinst_PROGRAMS = \ 15 | arch_mo.tap \ 16 | arch_mo_cxx.tap 17 | 18 | arch_mo_tap_SOURCES = arch-mo.c 19 | arch_mo_tap_LDADD = $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 20 | 21 | arch_mo_cxx_tap_SOURCES = arch-mo-cxx.cpp 22 | arch_mo_cxx_tap_LDADD = $(top_builddir)/tests/utils/libtap.la $(DL_LIBS) 23 | 24 | TESTS = \ 25 | arch_mo.tap \ 26 | arch_mo_cxx.tap 27 | -------------------------------------------------------------------------------- /tests/unit/arch-mo-cxx.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Mathieu Desnoyers 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include "arch-mo.c" 6 | -------------------------------------------------------------------------------- /tests/unit/arch-mo.c: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2023 Mathieu Desnoyers 2 | // 3 | // SPDX-License-Identifier: MIT 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "tap.h" 10 | 11 | struct { 12 | uint8_t v_u8; 13 | int8_t v_s8; 14 | uint16_t v_u16; 15 | int16_t v_s16; 16 | uint32_t v_u32; 17 | int32_t v_s32; 18 | #if RSEQ_BITS_PER_LONG == 64 19 | uint64_t v_u64; 20 | int64_t v_s64; 21 | #endif 22 | void *p; 23 | volatile int vol_int; 24 | const int const_int; 25 | } load_s = { 26 | .v_u8 = 0x11, 27 | .v_s8 = -(0x11), 28 | .v_u16 = 0x1122, 29 | .v_s16 = -(0x1122), 30 | .v_u32 = (0x11223344), 31 | .v_s32 = -(0x11223344), 32 | #if RSEQ_BITS_PER_LONG == 64 33 | .v_u64 = 0x1122334455667788ULL, 34 | .v_s64 = -(0x1122334455667788LL), 35 | .p = (void *)0x1122334455667788ULL, 36 | #else 37 | .p = (void *)0x11223344, 38 | #endif 39 | .vol_int = -(0x11223344), 40 | .const_int = -(0x11223344), 41 | }; 42 | 43 | static 44 | void test_load_acquire(void) 45 | { 46 | ok(rseq_smp_load_acquire(&load_s.v_u8) == 0x11, "load-acquire u8"); 47 | ok(rseq_smp_load_acquire(&load_s.v_s8) == -(0x11), "load-acquire s8"); 48 | ok(rseq_smp_load_acquire(&load_s.v_u16) == 0x1122, "load-acquire u16"); 49 | ok(rseq_smp_load_acquire(&load_s.v_s16) == -(0x1122), "load-acquire s16"); 50 | ok(rseq_smp_load_acquire(&load_s.v_u32) == 0x11223344, "load-acquire u32"); 51 | ok(rseq_smp_load_acquire(&load_s.v_s32) == -(0x11223344), "load-acquire s32"); 52 | #if RSEQ_BITS_PER_LONG == 64 53 | ok(rseq_smp_load_acquire(&load_s.v_u64) == 0x1122334455667788ULL, "load-acquire u64"); 54 | ok(rseq_smp_load_acquire(&load_s.v_s64) == -(0x1122334455667788LL), "load-acquire s64"); 55 | ok(rseq_smp_load_acquire(&load_s.p) == (void *)0x1122334455667788ULL, "load-acquire pointer"); 56 | #else 57 | ok(rseq_smp_load_acquire(&load_s.p) == (void *)0x11223344, "load-acquire pointer"); 58 | #endif 59 | ok(rseq_smp_load_acquire(&load_s.vol_int) == -(0x11223344), "load-acquire volatile int"); 60 | ok(rseq_smp_load_acquire(&load_s.const_int) == -(0x11223344), "load-acquire const int"); 61 | } 62 | 63 | struct { 64 | uint8_t v_u8; 65 | int8_t v_s8; 66 | uint16_t v_u16; 67 | int16_t v_s16; 68 | uint32_t v_u32; 69 | int32_t v_s32; 70 | #if RSEQ_BITS_PER_LONG == 64 71 | uint64_t v_u64; 72 | int64_t v_s64; 73 | #endif 74 | void *p; 75 | volatile int vol_int; 76 | } store_s; 77 | 78 | static 79 | void test_store_release(void) 80 | { 81 | rseq_smp_store_release(&store_s.v_u8, 0x11); 82 | ok(store_s.v_u8 == 0x11, "store-release u8"); 83 | rseq_smp_store_release(&store_s.v_s8, -(0x11)); 84 | ok(store_s.v_s8 == -(0x11), "store-release s8"); 85 | rseq_smp_store_release(&store_s.v_u16, 0x1122); 86 | ok(store_s.v_u16 == 0x1122, "store-release u16"); 87 | rseq_smp_store_release(&store_s.v_s16, -(0x1122)); 88 | ok(store_s.v_s16 == -(0x1122), "store-release s16"); 89 | rseq_smp_store_release(&store_s.v_u32, 0x11223344); 90 | ok(store_s.v_u32 == 0x11223344, "store-release u32"); 91 | rseq_smp_store_release(&store_s.v_s32, -(0x11223344)); 92 | ok(store_s.v_s32 == -(0x11223344), "store-release s32"); 93 | #if RSEQ_BITS_PER_LONG == 64 94 | rseq_smp_store_release(&store_s.v_u64, 0x1122334455667788ULL); 95 | ok(store_s.v_u64 == 0x1122334455667788ULL, "store-release u64"); 96 | rseq_smp_store_release(&store_s.v_s64, -(0x1122334455667788LL)); 97 | ok(store_s.v_s64 == -(0x1122334455667788LL), "store-release s64"); 98 | rseq_smp_store_release(&store_s.p, (void *)0x1122334455667788ULL); 99 | ok(store_s.p == (void *)0x1122334455667788ULL, "store-release pointer"); 100 | #else 101 | rseq_smp_store_release(&store_s.p, (void *)0x11223344); 102 | ok(store_s.p == (void *)0x11223344, "store-release pointer"); 103 | #endif 104 | rseq_smp_store_release(&store_s.vol_int, -(0x11223344)); 105 | ok(store_s.vol_int == -(0x11223344), "store-release volatile int"); 106 | } 107 | 108 | int main(void) 109 | { 110 | plan_no_plan(); 111 | test_load_acquire(); 112 | test_store_release(); 113 | exit(exit_status()); 114 | } 115 | -------------------------------------------------------------------------------- /tests/unregistered_test.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 Michael Jeanson 3 | 4 | #ifndef _GNU_SOURCE 5 | #define _GNU_SOURCE 6 | #endif 7 | #include 8 | 9 | #include 10 | 11 | #include "tap.h" 12 | 13 | #define NR_TESTS 4 14 | 15 | /* 16 | * Ensure the main executable has at least one TLS variable which will be 17 | * allocated before the rseq area, making sure the rseq_offset is not 0. This 18 | * allows testing that the rseq_offset variable is properly initialized by 19 | * checking it is not 0. 20 | * 21 | * Most toolchains will add at least one main exec TLS variable but it's 22 | * currently not the case on RISC-V. 23 | */ 24 | __thread int dummy_tls = -1; 25 | 26 | /* 27 | * Check the state of the public symbols when the rseq syscall is available but 28 | * no thread has registered. 29 | */ 30 | 31 | int main(void) 32 | { 33 | struct rseq_abi *rseq_abi; 34 | 35 | plan_tests(NR_TESTS); 36 | 37 | if (!rseq_available(RSEQ_AVAILABLE_QUERY_KERNEL)) { 38 | skip(NR_TESTS, "rseq syscall unavailable"); 39 | goto end; 40 | } 41 | 42 | /* The syscall is available but the current thread is not registered. */ 43 | 44 | ok(rseq_flags == 0, "rseq_flags prior to registration is 0 (%d)", rseq_flags); 45 | ok(rseq_size == 0, "rseq_size prior to registration is 0 (%d)", rseq_size); 46 | ok(rseq_offset != 0, "rseq_offset prior to registration is not 0 (%td)", rseq_offset); 47 | 48 | rseq_abi = rseq_get_abi(); 49 | ok((int32_t) rseq_abi->cpu_id == RSEQ_ABI_CPU_ID_UNINITIALIZED, 50 | "rseq->cpu_id is set to RSEQ_ABI_CPU_ID_UNINITIALIZED (%d)", 51 | (int32_t) rseq_abi->cpu_id); 52 | 53 | end: 54 | exit(exit_status()); 55 | } 56 | -------------------------------------------------------------------------------- /tests/unregistered_test_cxx.cpp: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | // SPDX-FileCopyrightText: 2024 EfficiOS Inc. 3 | 4 | #include "unregistered_test.c" 5 | -------------------------------------------------------------------------------- /tests/utils/Makefile.am: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # SPDX-FileCopyrightText: 2022 EfficiOS Inc. 3 | 4 | noinst_LTLIBRARIES = libtap.la 5 | libtap_la_SOURCES = tap.c tap.h 6 | 7 | dist_check_SCRIPTS = \ 8 | tap-driver.sh \ 9 | tap.sh \ 10 | utils.sh 11 | -------------------------------------------------------------------------------- /tests/utils/tap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-2-Clause 3 | * 4 | * Copyright (C) 2004 Nik Clayton 5 | * Copyright (C) 2017 Jérémie Galarneau 6 | */ 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /* '## __VA_ARGS__' is a gcc'ism. C99 doesn't allow the token pasting 13 | and requires the caller to add the final comma if they've ommitted 14 | the optional arguments */ 15 | #ifdef __GNUC__ 16 | # define ok(e, test, ...) ((e) ? \ 17 | _gen_result(1, __func__, __FILE__, __LINE__, \ 18 | test, ## __VA_ARGS__) : \ 19 | _gen_result(0, __func__, __FILE__, __LINE__, \ 20 | test, ## __VA_ARGS__)) 21 | 22 | # define ok1(e) ((e) ? \ 23 | _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \ 24 | _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) 25 | 26 | # define pass(test, ...) ok(1, test, ## __VA_ARGS__); 27 | # define fail(test, ...) ok(0, test, ## __VA_ARGS__); 28 | 29 | # define skip_start(test, n, fmt, ...) \ 30 | do { \ 31 | if((test)) { \ 32 | skip(n, fmt, ## __VA_ARGS__); \ 33 | continue; \ 34 | } 35 | #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) /* __GNUC__ */ 36 | # define ok(e, ...) ((e) ? \ 37 | _gen_result(1, __func__, __FILE__, __LINE__, \ 38 | __VA_ARGS__) : \ 39 | _gen_result(0, __func__, __FILE__, __LINE__, \ 40 | __VA_ARGS__)) 41 | 42 | # define ok1(e) ((e) ? \ 43 | _gen_result(1, __func__, __FILE__, __LINE__, "%s", #e) : \ 44 | _gen_result(0, __func__, __FILE__, __LINE__, "%s", #e)) 45 | 46 | # define pass(...) ok(1, __VA_ARGS__); 47 | # define fail(...) ok(0, __VA_ARGS__); 48 | 49 | # define skip_start(test, n, ...) \ 50 | do { \ 51 | if((test)) { \ 52 | skip(n, __VA_ARGS__); \ 53 | continue; \ 54 | } 55 | #else /* __STDC_VERSION__ */ 56 | # error "Needs gcc or C99 compiler for variadic macros." 57 | #endif /* __STDC_VERSION__ */ 58 | 59 | #define skip_end() } while(0); 60 | 61 | #ifdef __MINGW_PRINTF_FORMAT 62 | # define TAP_PRINTF_FORMAT __MINGW_PRINTF_FORMAT 63 | #else 64 | # define TAP_PRINTF_FORMAT printf 65 | #endif 66 | 67 | __attribute__((format(TAP_PRINTF_FORMAT, 5, 6))) 68 | unsigned int _gen_result(int, const char *, const char *, unsigned int, const char *, ...); 69 | 70 | int plan_no_plan(void); 71 | __attribute__((noreturn)) 72 | int plan_skip_all(const char *); 73 | int plan_tests(unsigned int); 74 | 75 | __attribute__((format(TAP_PRINTF_FORMAT, 1, 2))) 76 | unsigned int diag(const char *, ...); 77 | void diag_multiline(const char *); 78 | 79 | __attribute__((format(TAP_PRINTF_FORMAT, 2, 3))) 80 | int skip(unsigned int, const char *, ...); 81 | 82 | __attribute__((format(TAP_PRINTF_FORMAT, 1, 2))) 83 | void todo_start(const char *, ...); 84 | void todo_end(void); 85 | 86 | int exit_status(void); 87 | 88 | void disable_cleanup(void); 89 | 90 | #ifdef __cplusplus 91 | } 92 | #endif 93 | -------------------------------------------------------------------------------- /tests/utils/utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # SPDX-License-Identifier: MIT 4 | # 5 | # SPDX-FileCopyrightText: 2020 Michael Jeanson 6 | # 7 | 8 | # This file is meant to be sourced at the start of shell script-based tests. 9 | 10 | 11 | # Error out when encountering an undefined variable 12 | set -u 13 | 14 | # If "readlink -f" is available, get a resolved absolute path to the 15 | # tests source dir, otherwise make do with a relative path. 16 | scriptdir="$(dirname "${BASH_SOURCE[0]}")" 17 | if readlink -f "." >/dev/null 2>&1; then 18 | testsdir=$(readlink -f "$scriptdir/..") 19 | else 20 | testsdir="$scriptdir/.." 21 | fi 22 | 23 | # Allow overriding the source and build directories 24 | if [ "x${RSEQ_TESTS_SRCDIR:-}" = "x" ]; then 25 | RSEQ_TESTS_SRCDIR="$testsdir" 26 | fi 27 | export RSEQ_TESTS_SRCDIR 28 | 29 | if [ "x${RSEQ_TESTS_BUILDDIR:-}" = "x" ]; then 30 | RSEQ_TESTS_BUILDDIR="$testsdir" 31 | fi 32 | export RSEQ_TESTS_BUILDDIR 33 | 34 | # By default, it will not source tap.sh. If you to tap output directly from 35 | # the test script, define the 'SH_TAP' variable to '1' before sourcing this 36 | # script. 37 | if [ "x${SH_TAP:-}" = x1 ]; then 38 | # shellcheck source=./tap.sh 39 | . "${RSEQ_TESTS_SRCDIR}/utils/tap.sh" 40 | fi 41 | --------------------------------------------------------------------------------