Showing preview only (662K chars total). Download the full file or copy to clipboard to get everything.
Repository: rex-rs/rex
Branch: main
Commit: fbb60edcdb10
Files: 226
Total size: 606.4 KB
Directory structure:
gitextract_lgp5l1or/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── dco-check.yml
│ ├── memcached_benchmark.yml
│ ├── meson.yml
│ ├── rustfmt.yml
│ └── update-nix.yml
├── .gitignore
├── .gitmodules
├── Cargo.toml
├── LICENSE
├── README.md
├── docs/
│ ├── entry-insertion.md
│ ├── exception-handling.md
│ ├── getting-started.md
│ ├── kernel-symbols.md
│ ├── librex.md
│ └── rust_rex_subset.md
├── flake.nix
├── librex/
│ ├── .gitignore
│ ├── include/
│ │ └── librex.h
│ ├── lib/
│ │ ├── bindings.h
│ │ └── librex.cpp
│ └── meson.build
├── meson.build
├── rex/
│ ├── .cargo/
│ │ └── config.toml
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── build.py
│ ├── build.rs
│ ├── librexstub/
│ │ └── lib.c
│ ├── meson.build
│ └── src/
│ ├── base_helper.rs
│ ├── bindings/
│ │ ├── linux/
│ │ │ ├── kernel.rs
│ │ │ └── mod.rs
│ │ ├── mod.rs
│ │ └── uapi/
│ │ ├── linux/
│ │ │ ├── bpf.rs
│ │ │ ├── errno.rs
│ │ │ ├── in.rs
│ │ │ ├── mod.rs
│ │ │ ├── perf_event.rs
│ │ │ ├── pkt_cls.rs
│ │ │ ├── ptrace.rs
│ │ │ ├── seccomp.rs
│ │ │ └── unistd.rs
│ │ └── mod.rs
│ ├── debug.rs
│ ├── ffi.rs
│ ├── kprobe/
│ │ ├── kprobe_impl.rs
│ │ └── mod.rs
│ ├── lib.rs
│ ├── log.rs
│ ├── map.rs
│ ├── panic.rs
│ ├── per_cpu.rs
│ ├── perf_event/
│ │ ├── mod.rs
│ │ └── perf_event_impl.rs
│ ├── pt_regs.rs
│ ├── random32.rs
│ ├── sched_cls/
│ │ ├── mod.rs
│ │ └── sched_cls_impl.rs
│ ├── spinlock.rs
│ ├── task_struct.rs
│ ├── tracepoint/
│ │ ├── binding.rs
│ │ ├── mod.rs
│ │ └── tp_impl.rs
│ ├── utils.rs
│ └── xdp/
│ ├── mod.rs
│ └── xdp_impl.rs
├── rex-macros/
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src/
│ ├── args.rs
│ ├── kprobe.rs
│ ├── lib.rs
│ ├── perf_event.rs
│ ├── tc.rs
│ ├── tracepoint.rs
│ └── xdp.rs
├── rex-native.ini
├── rustfmt.toml
├── samples/
│ ├── .clang-format
│ ├── .gitignore
│ ├── atomic/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── bmc/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── entry.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── electrode/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── config.txt
│ │ ├── entry.c
│ │ ├── fast_common.h
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ ├── common.rs
│ │ ├── main.rs
│ │ └── maps.rs
│ ├── error_injector/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── src/
│ │ │ └── main.rs
│ │ ├── tests/
│ │ │ └── runtest.py
│ │ └── userapp.c
│ ├── hello/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── tests/
│ │ └── runtest.py
│ ├── map_bench/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── bench.sh
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── map_test/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── tests/
│ │ └── runtest.py
│ ├── map_test_2/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── tests/
│ │ └── runtest.py
│ ├── meson.build
│ ├── recursive/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── bench.sh
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── spinlock_cleanup_benchmark/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── spinlock_test/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── startup_overhead_benchmark/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── syscount/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ └── xdp_test/
│ ├── .cargo/
│ │ └── config.toml
│ ├── Cargo.toml
│ ├── README.md
│ ├── clippy.toml
│ ├── entry.c
│ ├── meson.build
│ ├── rustfmt.toml
│ ├── src/
│ │ └── main.rs
│ └── tests/
│ └── runtest.py
├── scripts/
│ ├── cargo-wrapper.pl
│ ├── q-script/
│ │ ├── .config
│ │ ├── nix-q
│ │ ├── sanity-test-q
│ │ └── yifei-q
│ └── sanity_tests/
│ └── run_tests.py
└── tools/
└── memcached_benchmark/
├── .cargo/
│ └── config.toml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── bench.py
├── flake.nix
├── rustfmt.toml
├── src/
│ ├── cli.rs
│ ├── dict.rs
│ ├── fs.rs
│ ├── get_values.rs
│ ├── main.rs
│ └── set_values.rs
└── tests/
└── benchmark_test.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: cargo
directories:
- /
- tools/memcached_benchmark
schedule:
interval: weekly
cooldown: # applies only to non-security updates
semver-patch-days: 3 # wait 3 days before applying patch updates
semver-minor-days: 7
semver-major-days: 14
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
# - package-ecosystem: gitsubmodule
# directory: /
# schedule:
# interval: weekly
================================================
FILE: .github/workflows/dco-check.yml
================================================
name: DCO Check
on:
pull_request:
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: KineticCafe/actions-dco@6e1652ef3027ce128e65e6edd215ae053350bd16 # v2.1.1
================================================
FILE: .github/workflows/memcached_benchmark.yml
================================================
name: memcached_benchmark
on:
push:
pull_request:
env:
CARGO_TERM_COLOR: always
jobs:
changes:
if: github.repository == 'rex-rs/rex'
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
bench: ${{ steps.filter.outputs.bench }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
filters: |
bench:
- 'tools/memcached_benchmark/**'
build:
needs: changes
if: ${{ needs.changes.outputs.bench == 'true' }}
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./tools/memcached_benchmark
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- id: filter
uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
with:
filters: |
bench:
- 'tools/memcached_benchmark/**'
- uses: cachix/install-nix-action@616559265b40713947b9c190a8ff4b507b5df49b # v31.10.4
- name: Build
run: nix develop --command bash -e -c 'cargo fmt --check --verbose && cargo build -r --verbose'
- name: Run tests
run: nix develop --command bash -e -c 'cargo test --verbose && cargo test -r --verbose'
================================================
FILE: .github/workflows/meson.yml
================================================
name: Meson Build and Test
on:
push:
branches: [main, ci]
pull_request:
branches: [main]
env:
CARGO_TERM_COLOR: always
jobs:
changes:
if: github.repository == 'rex-rs/rex'
runs-on: ubuntu-latest
permissions:
pull-requests: read
outputs:
meson: ${{ steps.filter.outputs.meson }}
nix: ${{ steps.filter.outputs.nix }}
steps:
# For pull requests it's not necessary to checkout the code
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1
id: filter
with:
predicate-quantifier: 'every'
filters: |
nix: &nix
- '**'
- '!docs/**'
- '!tools/**'
- '!**/*.md'
- '!.github/workflows/!(meson).yml'
meson:
- *nix
- '!flake.nix'
- '!flake.lock'
build_and_test:
needs: changes
if: ${{ needs.changes.outputs.meson == 'true' }}
runs-on: [self-hosted, gentoo]
concurrency:
group: build_and_test-${{ github.ref_name }}
cancel-in-progress: true
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
- name: Cache Rex build directory
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: build
key: ${{ runner.os }}-${{ runner.name }}-meson-${{ hashFiles('rex-native.ini', 'meson.build') }}
- name: Setup Rex build directory
run: meson setup --native-file rex-native.ini --reconfigure ./build
- name: Compile Rex deps
run: meson compile -C build build_deps
- name: Compile Rex samples
run: meson compile -C build
- name: Run Rex sanity tests
run: meson test -C build
# build_with_nix:
# needs: changes
# if: ${{ needs.changes.outputs.nix == 'true' }}
# runs-on: [self-hosted, nix]
# concurrency:
# group: build_with_nix_fhs-${{ github.ref_name }}
# cancel-in-progress: true
# steps:
# - name: Checkout repository
# uses: actions/checkout@v4
# with:
# submodules: recursive
#
# - name: Setup Rex build directory
# run: meson setup --native-file rex-native.ini --reconfigure ./build
# shell: nix develop -v -L .#rex --command bash -e {0}
#
# - name: Compile Rex deps
# run: meson compile -C build build_deps
# shell: nix develop -v -L .#rex --command bash -e {0}
#
# - name: Compile Rex samples
# run: meson compile -C build
# shell: nix develop -v -L .#rex --command bash -e {0}
build_with_nix_fhs:
needs: changes
if: ${{ needs.changes.outputs.nix == 'true' }}
runs-on: [self-hosted, nix]
concurrency:
group: build_with_nix_fhs-${{ github.ref_name }}
cancel-in-progress: true
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: recursive
- name: Setup Rex build directory
run: nix run .#fhsExec -- -c "meson setup --native-file rex-native.ini --reconfigure ./build"
- name: Compile Rex deps
run: nix run .#fhsExec -- -c "meson compile -C build build_deps"
- name: Compile Rex samples
run: nix run .#fhsExec -- -c "meson compile -C build"
================================================
FILE: .github/workflows/rustfmt.yml
================================================
name: Formatting check
on:
push:
pull_request:
jobs:
formatting:
if: github.repository == 'rex-rs/rex'
name: cargo fmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: true
- name: Determine nightly date from rust submodule
id: rust-version
run: |
# compiler_date in src/stage0 is the previous stable's release date,
# which is the same day the beta branch is cut and nightly bumps forward.
# The day before is the last nightly that matches this version's rustfmt.
COMPILER_DATE=$(grep '^compiler_date=' rust/src/stage0 | cut -d= -f2)
NIGHTLY_DATE=$(date -d "${COMPILER_DATE} - 1 day" +%Y-%m-%d)
echo "nightly_date=${NIGHTLY_DATE}" >> "$GITHUB_OUTPUT"
echo "Using nightly-${NIGHTLY_DATE}"
- name: Install matching rust nightly
env:
NIGHTLY_DATE: ${{ steps.rust-version.outputs.nightly_date }}
run: |
rustup toolchain install "nightly-${NIGHTLY_DATE}" --component rustfmt
rustup default "nightly-${NIGHTLY_DATE}"
- name: formatting rex code
run: cargo fmt --verbose --check
- name: formatting samples code
run: |
for d in $(find ./samples -name Cargo.toml); do
echo "→ Processing $d"
cargo fmt --manifest-path $d --verbose --check
done
================================================
FILE: .github/workflows/update-nix.yml
================================================
name: Update Nix Dependencies
on:
workflow_dispatch:
# push:
# branches: [ ci ] # for testing
schedule:
- cron: '0 0 * * 1' # Monday morning at 00:00 UTC
permissions:
contents: write
pull-requests: write
jobs:
update-nix:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
- name: Update root flake.lock
uses: DeterminateSystems/update-flake-lock@main
with:
commit-msg: "nix: update root flake.lock"
pr-title: "nix-flake: update nix dependencies"
pr-labels: |
dependencies
nix
pr-body: |
Automated update of nix flake dependencies.
Updated lock files:
- `flake.lock`
- `tools/memcached_benchmark/flake.lock`
- name: Update memcached_benchmark flake.lock
uses: DeterminateSystems/update-flake-lock@main
with:
flake-lock-path: tools/memcached_benchmark/flake.lock
commit-msg: "nix: update memcached_benchmark flake.lock"
pr-title: "nix-flake: update nix dependencies"
pr-labels: |
dependencies
nix
pr-body: |
Automated update of nix flake dependencies.
Updated lock files:
- `flake.lock`
- `tools/memcached_benchmark/flake.lock`
================================================
FILE: .gitignore
================================================
.vscode*
.cargo
!samples/*/.cargo
!tools/*/.cargo
samples/*/event-trigger
samples/*/loader
samples/*/entry
samples/*/.cache
test_dict.yml.zst
bench_entries.yml.zst
compile_commands.json
!rex/.cargo
# Generated by Cargo
# will have compiled files and executables
target/
.direnv
.envrc
.cache
================================================
FILE: .gitmodules
================================================
[submodule "linux"]
path = linux
url = git@github.com:rex-rs/linux.git
branch = rex-linux
[submodule "rust"]
path = rust
url = git@github.com:rex-rs/rust.git
branch = rex-rust
================================================
FILE: Cargo.toml
================================================
[workspace]
members = [
# rex lib
"rex",
# macros
"rex-macros",
]
resolver = "2"
exclude = ["samples", "rust", "tools", "scripts"]
[workspace.package]
authors = ["Rex Contributors"]
repository = "https://github.com/rex-rs/rex"
edition = "2021"
[workspace.dependencies]
proc-macro-error = { version = "1.0", default-features = false }
proc-macro2 = { version = "1", default-features = false }
paste = { version = "1" }
syn = { version = "2", features = ["full"] }
quote = { version = "1" }
rex-macros = { path = "./rex-macros" }
[profile.dev]
debug = 0
panic = "abort"
[profile.release]
debug = 0
panic = "abort"
lto = true
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
================================================
FILE: README.md
================================================
```
____ _______ __ _____ _ _
| _ \| ____\ \/ / | ____|_ _| |_ ___ _ __ ___(_) ___ _ __ ___
| |_) | _| \ / | _| \ \/ / __/ _ \ '_ \/ __| |/ _ \| '_ \/ __|
| _ <| |___ / \ | |___ > <| || __/ | | \__ \ | (_) | | | \__ \
|_| \_\_____/_/\_\ |_____/_/\_\\__\___|_| |_|___/_|\___/|_| |_|___/
```
# Rex Kernel Extensions
[](https://github.com/rex-rs/rex/actions)
[](https://www.gentoo.org)
#### Table of Contents
- [What is Rex](#what-is-rex)
- [Example program](#example-program)
- [Build and run](#build-and-run)
- [Documentations](#documentations)
- [Why Rex](#why-rex)
- [License](#license)
## What is Rex
Rex is a safe and usable kernel extension framework that allows loading and
executing Rust kernel extension programs in the place of eBPF. Unlike
eBPF-based frameworks such as [Aya](https://aya-rs.dev), Rex programs do
not go through the in-kernel verifier, instead, the programs are
implemented in the safe subset of Rust, on which the Rust compiler performs
the needed safety checks and generates native code directly. This approach
avoids the overly restricted verification requirements (e.g., program
complexity constraints) and the resulting arcane verification errors, while
at the same time potentially provides a better optimization opportunity in
the native compiler backend (i.e., LLVM) than the eBPF backend + in-kernel
JIT approach.
Rex currently supports the following features:
- 5 eBPF program types: `kprobe`, `perf_event`, `tracepoint`, `xdp`, and
`tc`.
- invocation of eBPF helper functions that are commonly used by these
programs
- interaction with eBPF maps
- RAII-style management of kernel resources obtainable by programs
- cleanup and in-kernel exception handling of Rust runtime panics with call
stack traces
- kernel stack (only when CFG cannot be computed statically) and
termination safety from a thin in-kernel runtime
- bindings and abstractions of kernel data types commonly needed by eBPF
programs
## Example program
The following example implements a kprobe program that attaches to a
selected system call and injects an error (specified by `errno`) to the
system call on a process (specified by its `pid`). The full example,
including the loader program, can be found under
[samples/error_injector](samples/error_injector).
```Rust
#![no_std]
#![no_main]
use rex::kprobe::kprobe;
use rex::map::RexHashMap;
use rex::pt_regs::PtRegs;
use rex::rex_kprobe;
use rex::rex_map;
use rex::Result;
#[allow(non_upper_case_globals)]
#[rex_map]
static pid_to_errno: RexHashMap<i32, u64> = RexHashMap::new(1, 0);
#[rex_kprobe]
pub fn err_injector(obj: &kprobe, ctx: &mut PtRegs) -> Result {
obj.bpf_get_current_task()
.map(|t| t.get_pid())
.and_then(|p| obj.bpf_map_lookup_elem(&pid_to_errno, &p).cloned())
.map(|e| obj.bpf_override_return(ctx, e))
.ok_or(0)
}
```
More sample programs can be found under [samples](samples).
## Build and run
You can find the detailed guide [here](docs/getting-started.md).
## Documentations
Additional design documentations can be found under [docs](docs).
## Why Rex
The existing eBPF extension relies on the in-kernel eBPF verifier to
provide safety guarantees. This unfortunately leads to usability issues
where safe programs are rejected by the verifier, including but not limited
to:
- programs may exceed the inherent complexity contraints of static
verification
- compilers may not generate verifier-friendly code
- same logic may need to be implemented in a certain way to please the
verifier
Rex aims to address these issues by directly leveraging the safety
guarantee from _safe Rust_. Developers can implement their programs in any
way that can be written in safe Rust with [few
restrictions](docs/rust_rex_subset.md), and no longer need to worry about
program complexity, the code generator, or finding the (many time
counter-intuitive) way of expressing the same logic to please the verifier.
We demonstrate this with the implementation of the [BPF Memcached Cache
(BMC)](https://github.com/Orange-OpenSource/bmc-cache), a state-of-the-art
extension program for Memcached acceleration. As a complex eBPF program,
BMC is forced to be splitted into several components connected by BPF
tail-calls and use awkward loop/branch implementations to please the
verifier, which are totally not needed in [its Rex
implementation](samples/bmc).
For example, we show the code in cache invalidation logic of the BPF-BMC
that searches for a `SET` command in the packet payload:
```C
// Searches for SET command in payload
for (unsigned int off = 0;
off < BMC_MAX_PACKET_LENGTH && payload + off + 1 <= data_end;
off++) {
if (set_found == 0 && payload[off] == 's' &&
payload + off + 3 <= data_end && payload[off + 1] == 'e' &&
payload[off + 2] == 't') {
off += 3;
set_found = 1;
}
...
}
```
The code not only introduces an extra constraint in the loop (`off <
BMC_MAX_PACKET_LENGTH`) solely for passing the verifier, but also employs
repeated boilerplate code to check packet ends (`data_end`) and cumbersome
logic to match the `"set"` string in the packet.
None of these burdens is needed with the power of safe Rust in Rex, which
has no complexity limits and provides more freedom on the implementation:
```rust
let set_iter = payload.windows(4).enumerate().filter_map(|(i, v)| {
if v == b"set " {
Some(i)
} else {
None
}
});
```
The full implementation of BMC in Rex can be found at
[samples/bmc](samples/bmc).
## License
Rex is licensed under the GPLv2 license. The submodules (Linux, Rust, LLVM)
in this repo are licensed under their own terms. Please see the
corresponding license files for more details. Additionally, the memcached
benchmark is licensed under the MIT license.
## Talks
- Open Source Summit North America 2025: https://youtu.be/4r7ECxEaGqM
- USENIX ATC 2025: https://youtu.be/phJ-fb5lEA8
- Linux Plumbers Conference 2025: https://youtu.be/ivcLS4LFfKE
================================================
FILE: docs/entry-insertion.md
================================================
# Entry point code Insertion
## Motivation
To allow Rust extension code to be called from the kernel, an FFI
entry-point function is needed to wrap around the user-defined extension
function. This wrapper function needs to handle certain unsafe operations,
for example, context conversion for XDP and perf event programs. Because of
this, it should never be implemented by the user. For example, interpreting
an XDP context as perf event context and perform the context conversion
specific to perf-event clearly violates memory and type safety and could
result in undefined behavior.
Therefore, we choose to automatically generate the entry point code during
compilation for the Rust extension programs. Since Rust by default uses
LLVM as its code generation backend. We performs the generation of entry
code in the middle-end on LLVM IR.
## Implementation
The entry point insertion is implemented as an LLVM pass
(`RexEntryInsertion`) that operates on the compilation unit that contains
the Rust extension programs. This LLVM pass can be enabled via the
`enable_rex` codegen option in rustc, which sets the corresponding pass for
the LLVM backend.
Take the [error_injector sample](../samples//error_injector/src/main.rs) as
an example:
```Rust
#![no_std]
#![no_main]
use rex::kprobe::kprobe;
use rex::map::RexHashMap;
use rex::pt_regs::PtRegs;
use rex::rex_kprobe;
use rex::rex_map;
use rex::Result;
#[allow(non_upper_case_globals)]
#[rex_map]
static pid_to_errno: RexHashMap<i32, u64> = RexHashMap::new(1, 0);
#[rex_kprobe]
pub fn err_injector(obj: &kprobe, ctx: &mut PtRegs) -> Result {
obj.bpf_get_current_task()
.map(|t| t.get_pid())
.and_then(|p| obj.bpf_map_lookup_elem(&pid_to_errno, &p).cloned())
.map(|e| obj.bpf_override_return(ctx, e))
.ok_or(0)
}
```
Here, the
[`rex_kprobe`](https://github.com/rex-rs/rex/blob/93777ca3ad238ad3ace1d45614933f277ab587e8/rex-macros/src/lib.rs#L47)
proc-macro defines a kprobe program object in section (`rex/kprobe/*`)
using the
[`const`](https://doc.rust-lang.org/std/keyword.const.html#compile-time-evaluable-functions)
function `kprobe::new`, which takes the program function `rex_kprobe` is
specified on and its literal name as arguments:
```Rust
#![no_std]
#![no_main]
use rex::kprobe::kprobe;
use rex::map::RexHashMap;
use rex::pt_regs::PtRegs;
use rex::rex_kprobe;
use rex::rex_map;
use rex::Result;
#[allow(non_upper_case_globals)]
#[rex_map]
static pid_to_errno: RexHashMap<i32, u64> = RexHashMap::new(1, 0);
pub fn err_injector(obj: &kprobe, ctx: &mut PtRegs) -> Result {
obj.bpf_get_current_task()
.map(|t| t.get_pid())
.and_then(|p| obj.bpf_map_lookup_elem(&pid_to_errno, &p).cloned())
.map(|e| obj.bpf_override_return(ctx, e))
.ok_or(0)
}
#[used]
#[link_section = "rex/kprobe"]
static PROG_err_injector: kprobe = kprobe::new(err_injector, "err_injector");
```
Under the hood, the `kprobe` object is defined as the following:
```Rust
#[repr(C)]
pub struct kprobe {
rtti: u64,
prog: fn(&Self, &mut PtRegs) -> Result,
name: &'static str,
}
```
The `rtti` field stores the corresponding
[`bpf_prog_type`](https://elixir.bootlin.com/linux/v5.15.128/source/include/uapi/linux/bpf.h#L919)
enum value (i.e. `BPF_PROG_TYPE_KPROBE` in this case). `prog` is a function
pointer that points to the user-defined extension program function. `name`
holds the user-intended name of the program, in a string literal form (as
mentioned above, the proc-macros in `rex-macros` always set this to the
literal name of the program function).
At LLVM-IR level, the `RexEntryInsertion` will iterate over all global
variables and look for variables with the special `rex/*` sections
generated by proc-macros from `rex-macros`. For the found program objects,
it will then generate the entry point based on the object contents.
Because the `kprobe::new` function is a `const` function. The object is
initialized with a constant expression that can be parsed by the
`RexEntryInsertion` pass. This effectively allows the pass to obtain the
program type (via `rtti`), the actual extension function (via `prog`), and
the intended name (via `name`).
The pass will construct a new `fn (*const()) -> u32` function with the
specified name and eBPF link section, which will be used as the entry point
function the kernel can invoke. This function takes in the context pointer
(as `*mut ()`) and invokes the special program-type-specific entry function
in the `rex` crate. The code of the aforementioned example would be
modified as (the process happens at LLVM-IR stage, but here Rust is used
for clarity):
```Rust
#![no_std]
#![no_main]
use rex::kprobe::kprobe;
use rex::map::RexHashMap;
use rex::pt_regs::PtRegs;
use rex::rex_kprobe;
use rex::rex_map;
use rex::Result;
#[allow(non_upper_case_globals)]
#[rex_map]
static pid_to_errno: RexHashMap<i32, u64> = RexHashMap::new(1, 0);
#[inline(always)]
pub fn err_injector(obj: &kprobe, ctx: &mut PtRegs) -> Result {
obj.bpf_get_current_task()
.map(|t| t.get_pid())
.and_then(|p| obj.bpf_map_lookup_elem(&pid_to_errno, &p).cloned())
.map(|e| obj.bpf_override_return(ctx, e))
.ok_or(0)
}
#[used]
#[link_section = "rex/kprobe"]
static PROG_err_injector: kprobe = kprobe::new(err_injector, "err_injector"););
#[link_section = "kprobe"]
#[no_mangle]
pub fn err_injector(ctx: *mut ()) -> u32 {
rex::__rex_entry_kprobe(&PROG_err_injector, ctx)
}
```
`__rex_entry_kprobe` is the tracepoint specific entry function defined in
the `rex` crate (not to be confused with the generated kernel entry point).
The function essentially calls `kprobe::prog_run` that converts the context
to the type specific to the program and invokes the `prog` function. In
this way the program context conversion and other preparation for execution
is safely abstracted away from the users.
### Add new program type support
The only file needs to be updated is
[llvm/include/llvm/Transforms/Rex/RexProgType.def](https://github.com/rex-rs/llvm-project/blob/rex-llvm-rebase/llvm/include/llvm/Transforms/Rex/RexProgType.def).
The basic syntex is:
```C
REX_PROG_TYPE_1(<BPF_PROG_TYPE_ENUM>, <program_type in RT crate>, <sec name>)
```
If the program type has more than 1 section names, use `REX_PROG_TYPE_2`
instead, which will support 2 names. Therefore, for `tracepoint` this is:
```C
REX_PROG_TYPE_2(BPF_PROG_TYPE_TRACEPOINT, tracepoint, "tracepoint", "tp")
```
Relevant files:
- LLVM pass:
- [llvm/lib/Transforms/Rex/RexInsertEntry.cpp](https://github.com/rex-rs/llvm-project/blob/rex-llvm-rebase/llvm/lib/Transforms/Rex/RexInsertEntry.cpp)
- [llvm/include/llvm/Transforms/Rex/RexInsertEntry.h](https://github.com/rex-rs/llvm-project/blob/rex-llvm-rebase/llvm/include/llvm/Transforms/Rex/RexInsertEntry.h)
- [llvm/include/llvm/Transforms/Rex/RexProgType.def](https://github.com/rex-rs/llvm-project/blob/rex-llvm-rebase/llvm/include/llvm/Transforms/Rex/RexProgType.def)
- Program-type-specific entry function (defined using the
`define_prog_entry` macro):
- [rex/src/lib.rs](https://github.com/rex-rs/rex/blob/main/rex/src/lib.rs)
- Kprobe implementation (can be generalized to other programs):
- [rex/src/kprobe/kprobe_impl.rs](https://github.com/rex-rs/rex/blob/main/rex/src/kprobe/kprobe_impl.rs)
================================================
FILE: docs/exception-handling.md
================================================
# Exception handling and runtime mechanism
This document will cover the following:
- [Handling of Rust panics (language exceptions)](#handling-of-rust-panics-in-kernel-space)
- [Kernel dispatch and landingpad](#kernel-stack-unwinding)
- [Rust panic handler and cleanup mechanism](#resource-cleanup-in-rust)
## Handling of Rust panics in kernel space
In userspace, Rust panics are essentially the same as C++ exceptions and
are handled based on [Itanium
ABI](https://llvm.org/docs/ExceptionHandling.html#itanium-abi-zero-cost-exception-handling).
That is, when a panic is triggered, the control flow will be redirect to
the unwind library (e.g. `libgcc` or `llvm-libunwind`). The unwind library
will then unwind the program stack. For each function call frame, it will
invoke the `personality` routine to look for feasible landingpads which
contains cleaup and (possibly) exception handling code. The unwind process
ends when either the exception is handled (e.g. by a C++ `catch`) or no
handler is found.
P.S. The actual Itanium ABI is more complicated than described here, e.g.
the unwind library runs two passes on the stack, once for searching
landingpads, and another time for executing landingpads code.
However, the Itanium EH ABI is not suitable in our case for the following
reasons:
- It adds too much complexity, as the userspace unwind libraries are not
directly usable.
- It usually requires dynamic allocation for certain exception contexts,
which may not be available to kernel extensions (e.g. kprobe executes in
interrupt contexts and is therefore not sleepable).
- It allows failures during the unwinding which cannot be tolerated in
kernel space. Incomplete cleanup means leaking kernel resources.
- It generally executes destructors for all existing objects on the stack,
but executing untrusted, user-defined destructors (via the `Drop` trait
in Rust) may not be safe.
Therefore, in our framework we implement our exception handling mechanism.
It can be divided into two parts: stack unwinding in the kernel and
resource cleanup in the runtime crate.
### Kernel stack unwinding
We need to be able support graceful exception handling in kernel space,
i.e. the extension program should be terminated without bringing down the
kernel. The idea is to transfer the exceptional control flow back to the
return address of the extension program and reset the stack and frame
pointer to ensure the context remains valid. In this way, the program would
act as if it just returned normally.
The implementation consists the `rex_dispatcher_func` to dispatch Rex
programs so that rust panics can be handled. The dispatcher have a
prototype of:
```C
extern asmlinkage unsigned int rex_dispatcher_func(
const void *ctx,
const struct bpf_prog *prog,
unsigned int (*bpf_func)(const void *,
const struct bpf_insn *));
```
which shares a similar signature as `bpf_dispatcher_nop_func` but with the
`struct bpf_insn` array argument replaced by a pointer to the program
struct, as Rex does not work with eBPF bytecode.
The function first saves the current stack pointer to the top of the
per-CPU Rex-specific stack, and then switches the stack before calling into
the program.
If the execution is successful (i.e. no exceptions), the function will just
return normally and the old stack pointer will be restored with a `pop`.
```
+-----------------------+
| rex_dispatcher_func: |
| ... |
| movq %gs:rex_sp, %rbp |
| movq %rsp, (%rbp) |
| movq %rbp, %rsp | +-----------+
| call *%rdx |--------------->| rex_prog: |
| | | ... |
| rex_exit: |<---------------| ret |
| popq %rsp | +-----------+
| ... |
| ret |
+-----------------------+
```
Under exceptional cases (where a rust panic is fired), `rust_begin_unwind`
(i.e. panic handler) will transfer the control flow to the `rex_landingpad`
C function in the kernel, which, after dumping some information to the
kernel ring buffer, will call `rex_landingpad_asm`. `rex_landingpad_asm`
sets a default reutrn value, restores the stack pointer to the top of the
Rex stack, i.e. the same address when program returns without an exception,
and issues an direct jump to the `rex_exit` label in the middle of
`rex_dispatcher_func`.
```
+-----------------------+
| rex_dispatcher_func: |
| ... |
| movq %gs:rex_sp, %rbp |
| movq %rsp, (%rbp) |
| movq %rbp, %rsp | +-----------+
| call *%rdx |--------------->| rex_prog: |
| | +------| ... |
+---->| rex_exit: | | | ret |
| | popq %rsp | | +-----------+
| | ... | |
| | ret | | panic!()
| | | |
| | rex_landingpad_asm: |<-----+ | +-------------------------+
| | ... | | +----->| rex_landingpad: |
| | movq %gs:rex_sp, %rsp | | | ... |
+-----| jmp rex_exit | +---------| call rex_landingpad_asm |
+-----------------------+ +-------------------------+
```
The Rex stack uses the same layout as other kernel stacks -- the pointer to
the previous stack is stored in the top-most entry of the Rex stack. This,
combined with `bpf-ksyms`, allows smooth integration with the ORC unwinder
and provides meaningful stack traces:
```console
[ 12.568364][ T208] rex: Panic from Rex prog: called `Option::unwrap()` on a `None` value
[ 12.568622][ T208] CPU: 3 UID: 0 PID: 208 Comm: userapp Not tainted 6.13.0-rex+ #226
[ 12.568854][ T208] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.3-20240910_120124-localhost 04/01/2014
[ 12.569236][ T208] Call Trace:
[ 12.569345][ T208] <REX>
[ 12.569428][ T208] dump_stack_lvl+0x6e/0xa8
[ 12.569559][ T208] rex_landingpad+0x64/0xb0
[ 12.569704][ T208] rex_prog_4168211f00000000::rust_begin_unwind+0x15a/0x1a0
[ 12.569944][ T208] rex_prog_4168211f00000000::core::panicking::panic_fmt+0x9/0x10
[ 12.570188][ T208] rex_prog_4168211f00000000::core::panicking::panic+0x53/0x60
[ 12.570423][ T208] rex_prog_4168211f00000000::core::option::unwrap_failed+0x9/0x10
[ 12.570668][ T208] rex_prog_4168211f00000000::err_injector+0x8f/0xa0
[ 12.570879][ T208] rex_dispatcher_func+0x32/0x32
[ 12.571045][ T208] </REX>
[ 12.571137][ T208] <TASK>
[ 12.571229][ T208] trace_call_bpf+0x1a1/0x1f0
[ 12.571363][ T208] ? __x64_sys_dup+0x1/0xd0
[ 12.571468][ T208] kprobe_perf_func+0x4e/0x260
[ 12.571582][ T208] ? kmem_cache_free+0x29/0x290
[ 12.571718][ T208] ? __cfi___x64_sys_dup+0x10/0x10
[ 12.571879][ T208] kprobe_ftrace_handler+0x115/0x1a0
[ 12.572046][ T208] ? __x64_sys_dup+0x5/0xd0
[ 12.572189][ T208] 0xffffffffa02010c8
[ 12.572310][ T208] ? __x64_sys_dup+0x1/0xd0
[ 12.572452][ T208] __x64_sys_dup+0x5/0xd0
[ 12.572582][ T208] do_syscall_64+0x42/0xb0
[ 12.572722][ T208] entry_SYSCALL_64_after_hwframe+0x4b/0x53
[ 12.572910][ T208] RIP: 0033:0x7f6039f0ee9d
[ 12.573050][ T208] Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d 33 bf 0b 00 f7 d8 64 89 01 48
[ 12.573579][ T208] RSP: 002b:00007ffd562cee08 EFLAGS: 00000246 ORIG_RAX: 0000000000000020
[ 12.573779][ T208] RAX: ffffffffffffffda RBX: 00007ffd562cef28 RCX: 00007f6039f0ee9d
[ 12.573967][ T208] RDX: 000055cc27f9b988 RSI: 00007ffd562cef38 RDI: 0000000000000000
[ 12.574150][ T208] RBP: 0000000000000001 R08: 00007f6039ffada0 R09: 000055cc27f9a730
[ 12.574348][ T208] R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
[ 12.574600][ T208] R13: 00007ffd562cef38 R14: 00007f603a02a000 R15: 000055cc27f9b988
[ 12.574810][ T208] </TASK>
```
For further information please refer to the [actual
code](https://github.com/rex-rs/linux/blob/rex-linux/arch/x86/net/rex_64.S).
### Resource cleanup in Rust
Not using the existing ABI-based exception handling / stack unwinding
scheme means we need to handle resource cleanup in our own way. We make the
observation that the only resources that requires cleanup are the resources
obtained from kernel helper functions. This is because of the restricted
programing interface exposed to these extension programs, which disallow
direct kernel resource alloation (e.g. allocate memory, directly access a
lock, etc).
This brings us chance to create a light-weight resource clean up scheme. We
can record allocated kernel resources and their destructors on-the-fly
during program execution. When termination is needed, the destructors of
allocated resources are invoked to release the resources. Since only the
trusted kernel crate that interfaces with the kernel resources is
responsible for implementing the aforementioned destructors, all the
cleanup code is trusted and guaranteed not to fail.
The PoC implemention uses `CleanupEntry` to represent an allocated
resource:
```Rust
pub(crate) type CleanupFn = fn(*const ()) -> ();
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub(crate) struct CleanupEntry {
pub(crate) valid: u64,
pub(crate) cleanup_fn: Option<CleanupFn>,
pub(crate) cleanup_arg: *const (),
}
```
An instance of the struct is supposed to be instantiated in the constructor
of the kernel resource binding types in the runtime crate:
- `valid` should be set to 1
- `cleanup_fn` should point to a function provided in `impl` of the binding
type, which takes in a pointer to the object and runs `drop` on it. The
`drop` handler is also responsible for setting `valid` to 0.
- `cleanup_arg` should point to the newly created object. It is used as the
argument to `cleanup_fn`.
Note:
1. `valid` field is of type `u64`. This may not seem space effcient at a
glance, but since both `cleanup_fn` and `cleanup_arg` are 64 bit large.
The alignment of the struct is 64 bits anyway.
2. According to
[Rustonomicon](https://doc.rust-lang.org/nomicon/repr-rust.html) and
[Rust doc](https://doc.rust-lang.org/std/option/#representation),
`Option<CleanupFn>` has the same size and bit-level representation as
`CleanupFn` as long as it is a `Some`. This is important because on the
kernel side it can be treated as a `void (*)(void *)`.
The created struct is then stored in a per-CPU array `rex_cleanup_entries`
in the kernel. This also implies that a C binding for `CleanupEntry` is
needed in the kernel:
```C
struct rex_cleanup_entry {
u64 valid;
void *cleanup_fn;
void *cleanup_arg;
};
```
Note:
1. Using `void *` to store function pointer is not standard compliant,
though at ABI level it is always a 64-bit value and should work
correctly. We should change it to a real function pointer: `void
(*)(void *)`.
2. Currently, the array is statically allocated with a capacity of 64. This
**may not** be sustainable.
During normal execution, the `drop` handlers are executed normally so the
kernel resource will be released and the `CleanupEntry` will be
invalidated.
Upon a panic, the control flow will transfer to `rust_begin_unwind` (i.e.
the Rust panic handler). `rust_begin_unwind` will traverse the array on
current CPU and free any resources allocated by invoking
`(cleanup_fn)(cleanup_arg)`. It then invalidate these entries.
Code references:
1. [Rust side `CleanupEntry` and panic handler
implementation](https://github.com/rex-rs/rex/blob/main/rex/src/panic.rs)
2. [Kernel side binding type and per-CPU
array](https://github.com/rex-rs/linux/blob/cd07f685c08b6087da0b1468a97d75c3de51e296/kernel/bpf/core.c#L3146)
================================================
FILE: docs/getting-started.md
================================================
# Getting started (p20250206)
Building Rex extensions requires modifications to the toolchain (Rust and
LLVM) and running Rex extensions requires modifications to the Linux
kernel. The steps below describe how to set up both the toolchain and
kernel for running Rex extensions in a VM.
Rex currently only supports the `x86-64` (`amd64`) architecture. Running
Rex in VMs addtionally requires
[`KVM`](https://linux-kvm.org/page/Main_Page) to be available on the host
machine.
## Nix flake
Using Nix, a package manager, allows you to bypass these dependency
requirements below.
Check out the https://nixos.org/download/ for installation instructions,
the single-user installation should be sufficient.
## Dependencies:
The following tools/libraries are required. Older versions are not
guaranteed to (or guaranteed not to) work. This list does not include
standard kernel build dependencies.
- `clang+LLVM (>= 18.1.0)`
- `cmake`
- `elfutils`
- `libstdc++ (>=13)` for missing `c++23` support in LLVM's `libcxx`
- `meson`
- `mold`
- `ninja`
- `python (>= 3.11)`
- `QEMU`
- `rust-bindgen`
## Repo setup and build
Clone this repo and its submodules:
```bash
git clone https://github.com/rex-rs/rex.git rex-kernel
cd rex-kernel
git submodule update --init --progress
```
If you are using Nix, the following additional step is required.
```bash
nix develop --extra-experimental-features nix-command --extra-experimental-features flakes
```
It will launch a Nix shell with all necessary dependencies installed.
All subsequent steps should be carried out within this shell.
Rex uses `meson` as its build system, to get started, first set up `meson`
in Rex:
```bash
meson setup --native-file rex-native.ini ./build/
```
Rex requries the modified kernel and its `libbpf` library, which resides in
the `linux` directory after submodule initialization. Rex also uses custom
LLVM passes in the Rust compiler to generate additional code and
instruments the extension programs, therefore,
[bootstraping](https://en.wikipedia.org/wiki/Bootstrapping_(compilers)) the
Rust compiler is required. The Rust toolchain source can be found under the
`rust` directory as another submodule.
Building these dependencies is a one-time effort with the following
command:
```bash
meson compile -C build build_deps
```
This will build the kernel and its `libbpf`. It will also bootstrap the
Rust compiler and build the relevant tools (e.g., `cargo`, `clippy`, etc).
With the linux and Rust setup, all Rex sample programs can then be built
with:
```bash
meson compile -C build
```
## Run `hello` example
First boot the VM:
```bash
cd build/linux
../../scripts/q-script/yifei-q # use ../scripts/q-script/nix-q instead if you are using Nix
```
Inside the VM:
```bash
cd ../samples/hello
./loader &
./event-trigger
```
The following output should be printed out:
```console
<...>-245 [002] d...1 18.417331: bpf_trace_printk: Rust triggered from PID 245.
```
## Troubleshooting
### Building dependencies:
This step includes compiling the Linux kernel which can get quite resource
intensive. In our tests `6GB` is the minimum value for which compiling
Linux is possible, this means you might not be able to use Rex on machines
with 6GB or less RAM. A sign that you ran into Out-Of-Memory (OOM) error is
if you encounter this warning:
```bash
/root/rex/linux/scripts/link-vmlinux.sh: line 113: 55407 Killed LLVM_OBJCOPY="${OBJCOPY}" ${PAHOLE} -J ${PAHOLE_FLAGS} ${1}
```
And error:
```bash
FAILED: load BTF from vmlinux: invalid argument
```
Or similar problems.
For WSL users, it is recommended to allocate more RAM to WSL before
starting this step since WSL by default only utilizes half the RAM
available on the host machine:
From a Powershell instance, create and open a `.wslconfig` file in your
home directory:
```bash
notepad $HOME/.wslconfig
```
Add the following lines to the file then save:
```bash
[wsl2]
memory=8GB
swap=8GB
```
You should change the value to how much memory you want to allocate to WSL.
Another issue that may happen is bootstrap failure due to the missing
`libLLVM-19-rex.so`:
```console
--- stderr
llvm-config: error: libLLVM-19-rex.so is missing
thread 'main' panicked at compiler/rustc_llvm/build.rs:264:16:
command did not execute successfully: "/home/chin39/Documents/rex-kernel/build/rust-build/x86_64-unknown-linux-gnu/llvm/bin/llvm-config" "--link-shared" "--libs" "--system-libs" "asmparser" "bitreader" "bitwriter" "coverage" "instrumentation" "ipo" "linker" "lto" "x86"
expected success, got: exit status: 1
stack backtrace:
0: rust_begin_unwind
at /rustc/9fc6b43126469e3858e2fe86cafb4f0fd5068869/library/std/src/panicking.rs:665:5
1: core::panicking::panic_fmt
at /rustc/9fc6b43126469e3858e2fe86cafb4f0fd5068869/library/core/src/panicking.rs:76:14
2: build_script_build::output
3: build_script_build::main
4: core::ops::function::FnOnce::call_once
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
Build completed unsuccessfully in 0:00:12
FAILED: cargo rustc
env 'RUSTFLAGS=-Z threads=8 -C target-cpu=native -C codegen-units=1 -C link-arg=-fuse-ld=mold -C link-arg=-Wl,-O1 -C link-arg=-Wl,--as-needed -C link-arg=-flto=thin' /usr/bin/python3 ../rust/x.py install --config=../rust/rex-config.toml --build-dir=./rust-build --set install.prefix=./rust-dist
ninja: build stopped: subcommand failed.
```
Notably this may happen as a result of [`ba85ec815c2f ("rust: enable more
optimizations and features in bootstrap
config")`](https://github.com/rex-rs/rex/commit/ba85ec815c2fc9721e3b466d1c296bd7dd79b1b3),
as it changes the linkage of `libLLVM` from static to dynamic, but rust
bootstrap process does not rebuild `libLLVM.so` following the change.
The issue can be fixed by removing the build directory created by meson and
starting a clean build.
### Building the Rex samples:
There are some caveats before you run this step. By default the `ninja`
build tool uses a quite high level of parallelism, which might again cause
OOM on personal machines. A sign of this happenning is if you try this step
and run into similar errors to:
```bash
error: could not compile `core` (lib)
Caused by:
process didn't exit successfully:
```
To resolve this problem, try running with fewer commands in parallel using
the `-j` argument, for example to run with 4 commands in parallel:
```bash
meson compile -C build -j 4
```
Our tests indicate a peak memory usage of 12GB with `-j 8`, so if you have
less RAM it's helpful to keep the `-j` argument below 8.
### Booting the QEMU VM:
By default our QEMU VM runs on 8GB of memory. To reduce this, open the qemu
scripts using an editor and locate line 300:
```bash
MEMORY=8192
```
And change this value to the number you want. Rex has been tested to work
with 4GB or `MEMORY=4096`.
================================================
FILE: docs/kernel-symbols.md
================================================
# Handling of kernel symbols
This document covers dynamic kernel symbol resolution.
The `rex` crate serves as an interface for the extension programs to
interact with the kernel. To accomplish this, the crate will need to access
kernel symbols. For example, invoking kernel helper functions requires
knowing the kernel address of the target helper function symbol. These
kernel symbols includes not only BPF helper functions, but also certain
per-cpu variables and other kernel functions (these comes partly from our
previous effort on rewriting kernel helpers, but it is also used by the
stack unwind/protection and panic cleanup mechanism).
Before [`36b91e1aab92: ("libiu: support dynamic symbol
relocation")`](https://github.com/rex-rs/rex/commit/36b91e1aab92a28cf341852c1ffd187597736d60),
the `rex` crate directly compiles the address of the kernel symbols in. The
build script uses `nm` to resolve addresses of the needed kernel symbols
(specified in a special section of `Cargo.toml`) and generates the
`stub.rs` file. The rest of the crate can use the generated `xxx_addr`
function to get the address of kernel symbol `xxx` as an `u64`. The code
can then transmute the value into the appropriate type (e.g. a function
pointer for helper functions). This way of kernel symbol resolution causes
several problems:
1. The compiled executable will contain kernel addresses, which should not
be leaked to userspace.
2. It requires KASLR to be turned off (because addresses from
`nm`/`System.map` are static), which is not portable on KASLR-enabled
kernels.
3. When ever kernel image layout changes (e.g. changes in function offset),
the extension program needs to be re-compiled.
4. The transmuted stub becomes a function pointer for helper functions,
which, after inlining, hinders further optimization on stack
instrumentation (the instrumentation can only be omitted if there are no
indirect calls / recursions).
Therefore, it is reasonable to defer the kernel symbol resolution to load
time. At this point, the kernel always knows where the symbols are located
(even with KASLR), which solves problems 2 and 3. At the same time, the
final executable will not contain any actual kernel address, addressing
problem 1. In order to avoid the need of an indirect call (problem 4), we
choose to implement the kernel symbol resolution the same way dynamic
linking works in userspace -- declare the needed kernel symbols as external
symbols and let the compiler generate relocation entries for these symbols.
The new implementation involves the following:
1. Declare the needed kernel symbols as `extern "C"` and with appropriate
type. For example `bpf_probe_read_kernel` is declared as:
```Rust
extern "C" {
/// `long bpf_probe_read_kernel(void *dst, u32 size, const void *unsafe_ptr)`
pub(crate) fn bpf_probe_read_kernel(
dst: *mut (),
size: u32,
unsafe_ptr: *const (),
) -> i64;
}
```
`extern "C"` specifies that the symbol uses the C ABI, which matches
that of the actual in-kernel object/function (check the
[Rustonomicon](https://doc.rust-lang.org/nomicon/other-reprs.html) for
more information). The declarations can be found in
[`src/stub.rs`](https://github.com/rex-rs/rex/blob/main/rex/src/stub.rs).
2. To make it easy to generate the relocations, we add a dummy library,
[`librexstub`](https://github.com/rex-rs/rex/tree/main/rex/librexstub),
that provides "fake" definitions of the symbols. The extension programs
will link against this library dynamically so that a dynamic relocation
entry is generated for each kernel symbol and the linker will not
complain about undefined symbols. For example, dumping relocations for
the
[`error_injector`](https://github.com/rex-rs/rex/tree/main/samples/error_injector)
sample gives the following:
```console
$ objdump -R target/x86_64-unknown-none/release/error_injector
target/x86_64-unknown-none/release/error_injector: file format elf64-x86-64
DYNAMIC RELOCATION RECORDS
OFFSET TYPE VALUE
0000000000003e58 R_X86_64_RELATIVE *ABS*+0x0000000000002029
0000000000003e68 R_X86_64_RELATIVE *ABS*+0x0000000000002000
0000000000003fc0 R_X86_64_RELATIVE *ABS*+0x0000000000001210
0000000000003ff8 R_X86_64_RELATIVE *ABS*+0x0000000000001020
0000000000003fb8 R_X86_64_GLOB_DAT this_cpu_off
0000000000003fc8 R_X86_64_GLOB_DAT pcpu_hot
0000000000003fd0 R_X86_64_GLOB_DAT rex_cleanup_entries
0000000000003fd8 R_X86_64_GLOB_DAT rex_landingpad
0000000000003fe0 R_X86_64_GLOB_DAT just_return_func
0000000000003fe8 R_X86_64_GLOB_DAT rex_termination_state
0000000000003ff0 R_X86_64_GLOB_DAT bpf_map_lookup_elem
```
The relocations with type `R_X86_64_GLOB_DAT` are the kernel symbol
relocations generated from dynamic linking, where the `OFFSET` denotes the
address offset within the binary and the `VALUE` specifies the actual symbol
name.
Other relocations with type `R_X86_64_RELATIVE` are a result from
position-independent executables (PIE). In PIE, function invocations
involve a IP-relative call that indexes into the global offset table
(GOT) that stores the absolute address of the function. The GOT entries
are generated as relocations that are patched at the program load time.
For example, `3fc0 R_X86_64_RELATIVE *ABS*+0x1270` specifies that the
value at offset `3fc0` of the binary needs to be patched to the absolute
start address of the binary (after it is mapped into memory) plus
`0x1270`.
The library exists solely for the generation of relocations, it is never
mapped into the kernel with the program and the symbols defined in the
library are therefore never accessed. At the same time, because the
symbols are not accessed, their types are not relevant, only the name
matters. The build script of the `rex` crate automatically builds the
library and adds the needed linker flags so that users can just use
`cargo` to build the programs.
3. At load time, librex parses the relocation entries to find out the
offsets and symbol names (accessible by symbol table index), and send
them to the kernel. The decision to let the loader library parse the
relocation entries is to reduce the complexity of code in the kernel and
take advantage of the existing userspace ELF libraries (we use
`elfutils`, the same library used by `libbpf`). Each dynamic symbol
relocation is the stored in an
[`rex_dyn_sym`](https://github.com/rex-rs/linux/blob/cd07f685c08b6087da0b1468a97d75c3de51e296/include/uapi/linux/bpf.h#L1472-L1475)
struct:
```C
struct rex_dyn_sym {
__u64 offset; // symbol offset
__u64 symbol; // symbol name string (actually a char *)
};
```
When invoking the `bpf(2)` syscall to load the program, the library will
pass an array of `struct rex_dyn_sym` to the kernel (by setting pointer
to the start address and the size in the
[`bpf_attr`](https://github.com/rex-rs/linux/blob/cd07f685c08b6087da0b1468a97d75c3de51e296/include/uapi/linux/bpf.h#L1591-L1592)
union).
The kernel copies the array into kernel space and queries each symbol
name against `kallsyms` to lookup the in-kernel address of the symbol,
it then patches the value at the specified offset to that address.
The same symbol resolution mechanism is applied to maps as well.
================================================
FILE: docs/librex.md
================================================
# LIBREX
`librex` serves as the loader library for Rex programs -- similar to what
`libbpf` is to eBPF programs -- but in a simpler way.
### APIs
`librex` only contains the code that loads Rex programs from ELF binaries,
other operations (e.g., attachment, map manipulation, etc) are delegated to
the existing code in `libbpf`.
Therefore, the library only exposes the following simple APIs:
```c
struct rex_obj *rex_obj_load(const char *file_path);
struct bpf_object *rex_obj_get_bpf(struct rex_obj *obj);
void rex_set_debug(int val);
```
The `rex_obj_load` function loads the Rex programs from the ELF binary
identified by the `file_path` argument into the kernel. It returns a
pointer to the corresponding `rex_obj` that encapsulates information about
the loaded programs and created maps. If there is an error, a null pointer
is returned.
The `rex_obj_get_bpf` returns a pointer to the equivalent `bpf_object` of
the `obj` argument, which can be subsequently passed to `libbpf` APIs. If
there is an error, a null pointer is returned.
**Note**: The returned pointer from both functions above are **non-owning**
pointers, which means the caller of these function should not try to
`free`/`realloc`/`delete` the pointer. The ownership of the pointers is
managed by `librex` internally and will be automatically freed after the
program terminates.
The `rex_set_debug` function can be used to toggle the internal logging
mechanism of `librex` (with `(bool)val` determining whether logging is
enabled). This will most likely be helpful during debugging.
================================================
FILE: docs/rust_rex_subset.md
================================================
### Rex subset of Rust
- `std`
- depends on libc, therefore not available in standalone mode
- `alloc`
- Rex currently does not hook onto kernel's allocator
- unsafe
- can break any safety guarrantee
- `core::mem::forget` and `core::mem::ManuallyDrop`
- take ownership and “forget” about the value **without running its
destructor**.
- lifetime related: disrupts resource cleanup
- `core::intrinsics::abort`:
- uses an illegal instruction and therefore can crash the kernel.
- `core::simd`
- no simd/fp allowed in the kernel in the first place
================================================
FILE: flake.nix
================================================
{
description = "A flake for the REX project";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
};
outputs = { self, nixpkgs, ... }:
let
system = "x86_64-linux";
basePkgs = import nixpkgs {
inherit system;
};
remoteNixpkgsPatches = [
{
meta.description = "cc-wrapper: remove -nostdlibinc";
url = "https://github.com/chinrw/nixpkgs/commit/2a5bd9cecd9ae28d899eb9bf434255a9fa09cbb0.patch";
sha256 = "sha256-TBmNtH8C5Vp1UArLtXDk+dxEzUR3tohjPMpJc9pIEN8=";
}
];
patchedNixpkgsSrc = basePkgs.applyPatches {
name = "nixpkgs-patched";
src = basePkgs.path;
patches = map basePkgs.fetchpatch remoteNixpkgsPatches;
};
# patchedBindgen =
# (self: super: {
# rust-bindgen-unwrapped = super.rust-bindgen-unwrapped.overrideAttrs (finalAttrs: oldAttrs: {
# src = super.fetchFromGitHub {
# owner = "rust-lang";
# repo = "rust-bindgen";
# rev = "20aa65a0b9edfd5f8ab3e038197da5cb2c52ff18";
# sha256 = "sha256-OrwPpXXfbkeS7SAmZDZDUXZV4BfSF3e/58LJjedY1vA=";
# };
# cargoDeps = pkgs.rustPlatform.fetchCargoVendor {
# inherit (finalAttrs) pname src version;
# hash = finalAttrs.cargoHash;
# };
# cargoHash = "sha256-e94pwjeGOv/We6uryQedj7L41dhCUc2wzi/lmKYnEMA=";
# });
# });
patchedPkgs = import patchedNixpkgsSrc {
inherit system;
# overlays = [ patchedBindgen ];
};
pkgs = import nixpkgs {
inherit system;
# overlays = [ overrideLLVM ];
};
wrapCC = cc: pkgs.wrapCCWith {
inherit cc;
extraBuildCommands = ''
# Remove the line that contains "-nostdlibinc"
sed -i 's|-nostdlibinc||g' "$out/nix-support/cc-cflags"
echo " -resource-dir=${pkgs.llvmPackages.clang}/resource-root" >> "$out/nix-support/cc-cflags"
echo > "$out/nix-support/add-local-cc-cflags-before.sh"
'';
};
# wrappedClang = wrapCC pkgs.llvmPackages.clang.cc;
# lib = nixpkgs.lib;
# Use unwrapped clang & lld to avoid warnings about multi-target usage
rexPackages = with pkgs; [
# Kernel builds
autoconf
bc
binutils
bison
cmake
diffutils
elfutils
elfutils.dev
fakeroot
findutils
flex
git
gcc
getopt
gnumake
ncurses
openssl
openssl.dev
pkg-config
xz
xz.dev
zlib
zlib.dev
bpftools
cargo-pgo
ninja
patchedPkgs.rust-bindgen
pahole
strace
zstd
perf-tools
# linuxKernel.packages.linux_latest.perf
# Clang kernel builds
patchedPkgs.llvmPackages.clang
patchedPkgs.llvmPackages.llvm
# wrappedClang
# llvmPackages.libcxxStdenv
lld
mold
qemu
file
util-linux
hostname
sysctl
perf-tools
# for llvm/Demangle/Demangle.h
libllvm.lib
libllvm.dev
libgcc
libclang.lib
libclang.dev
# meson deps
meson
curl
perl
bear # generate compile commands
rsync # for make headers_install
gdb
# bmc deps
iproute2
memcached
python3
# Rex utils
zoxide # in case host is using zoxide
openssh # q-script ssh support
bat
fd
eza
zsh
];
llvmBuildFHSEnv = pkgs.buildFHSEnv.override { stdenv = pkgs.llvmPackages.stdenv; };
fhsBase =
{
name = "rex-env";
targetPkgs = pkgs: rexPackages ++ [ pkgs.systemd pkgs.file ];
profile = ''
export NIX_ENFORCE_NO_NATIVE=0
export PATH=$(realpath "./build/rust-dist/bin"):$PATH
export RUST_BACKTRACE=1
'';
};
fhsRex = llvmBuildFHSEnv (fhsBase // {
runScript = "zsh";
});
# FHS environment for running arbitrary commands
fhsExec = llvmBuildFHSEnv (fhsBase // {
name = "rex-exec";
runScript = pkgs.writeScript "fhs-exec-wrapper" ''
#!${pkgs.bash}/bin/bash
exec bash "$@"
'';
});
in
{
devShells."${system}" = {
default = fhsRex.env;
rex = pkgs.mkShell {
packages = rexPackages;
# Disable default hardening flags. These are very confusing when doing
# development and they break builds of packages/systems that don't
# expect these flags to be on. Automatically enables stuff like
# FORTIFY_SOURCE, -Werror=format-security, -fPIE, etc. See:
# - https://nixos.org/manual/nixpkgs/stable/#sec-hardening-in-nixpkgs
# - https://nixos.wiki/wiki/C#Hardening_flags
hardeningDisable = [ "all" ];
LD_LIBRARY_PATH = "${pkgs.stdenv.cc.cc.lib.outPath}/lib:${pkgs.lib.makeLibraryPath rexPackages}:$LD_LIBRARY_PATH";
shellHook = ''
export NIX_CC_WRAPPER_SUPPRESS_TARGET_WARNING=1
export PATH=$(realpath "./build/rust-dist/bin"):$PATH
# Add required llvm-config
export PATH=${patchedPkgs.llvmPackages.libllvm.out}/bin:$PATH
export PATH=${patchedPkgs.llvmPackages.libllvm.dev}/bin:$PATH
export RUST_BACKTRACE=1
export NIX_ENFORCE_NO_NATIVE=0
export LLVM_SRC_INC="$PWD/rust/src/llvm-project/llvm/include"
# export LLVM_BUILD_INCLUDE="$PWD/build/rust-build/x86_64-unknown-linux-gnu/llvm/build/include"
export NIX_CFLAGS_COMPILE_BEFORE="-I$LLVM_SRC_INC"
echo "loading rex env"
'';
};
};
packages."${system}" = {
fhsRex = fhsRex;
fhsExec = fhsExec;
};
};
}
================================================
FILE: librex/.gitignore
================================================
librex.a
librex.o
librex.so
.cache
================================================
FILE: librex/include/librex.h
================================================
#ifndef LIBREX_H
#define LIBREX_H
struct bpf_object;
struct rex_obj;
#ifdef __cplusplus
extern "C" {
#endif
[[gnu::visibility("default")]] void rex_set_debug(int val);
[[nodiscard, gnu::visibility("default")]] struct rex_obj *
rex_obj_load(const char *file_path);
[[nodiscard, gnu::visibility("default")]] struct bpf_object *
rex_obj_get_bpf(struct rex_obj *obj);
#ifdef __cplusplus
}
#endif
#endif // LIBREX_H
================================================
FILE: librex/lib/bindings.h
================================================
// This file contains the non-portable part, it has to mirror some libbpf types
#ifndef _LIBREX_BINDINGS_H
#define _LIBREX_BINDINGS_H
#include <bpf/libbpf.h>
#include <gelf.h>
/* From tools/lib/bpf/libbpf_internal.h */
#define SHA256_DIGEST_LENGTH 32
struct elf_state {
int fd;
const void *obj_buf;
size_t obj_buf_sz;
Elf *elf;
Elf64_Ehdr *ehdr;
Elf_Data *symbols;
Elf_Data *arena_data;
size_t shstrndx; /* section index for section name strings */
size_t strtabidx;
struct elf_sec_desc *secs;
size_t sec_cnt;
int btf_maps_shndx;
__u32 btf_maps_sec_btf_id;
int text_shndx;
int symbols_shndx;
bool has_st_ops;
int arena_data_shndx;
int jumptables_data_shndx;
};
struct bpf_sec_def {
char *sec;
enum bpf_prog_type prog_type;
enum bpf_attach_type expected_attach_type;
long cookie;
int handler_id;
libbpf_prog_setup_fn_t prog_setup_fn;
libbpf_prog_prepare_load_fn_t prog_prepare_load_fn;
libbpf_prog_attach_fn_t prog_attach_fn;
};
enum bpf_object_state {
OBJ_OPEN,
OBJ_PREPARED,
OBJ_LOADED,
};
struct bpf_object {
char name[BPF_OBJ_NAME_LEN];
char license[64];
__u32 kern_version;
enum bpf_object_state state;
struct bpf_program *programs;
size_t nr_programs;
struct bpf_map *maps;
size_t nr_maps;
size_t maps_cap;
char *kconfig;
struct extern_desc *externs;
int nr_extern;
int kconfig_map_idx;
bool has_subcalls;
bool has_rodata;
struct bpf_gen *gen_loader;
/* Information when doing ELF related work. Only valid if efile.elf is not
* NULL */
struct elf_state efile;
unsigned char byteorder;
struct btf *btf;
struct btf_ext *btf_ext;
/* Parse and load BTF vmlinux if any of the programs in the object need
* it at load time.
*/
struct btf *btf_vmlinux;
/* Path to the custom BTF to be used for BPF CO-RE relocations as an
* override for vmlinux BTF.
*/
char *btf_custom_path;
/* vmlinux BTF override for CO-RE relocations */
struct btf *btf_vmlinux_override;
/* Lazily initialized kernel module BTFs */
struct module_btf *btf_modules;
bool btf_modules_loaded;
size_t btf_module_cnt;
size_t btf_module_cap;
/* optional log settings passed to BPF_BTF_LOAD and BPF_PROG_LOAD commands */
char *log_buf;
size_t log_size;
__u32 log_level;
int *fd_array;
size_t fd_array_cap;
size_t fd_array_cnt;
struct usdt_manager *usdt_man;
int arena_map_idx;
void *arena_data;
size_t arena_data_sz;
void *jumptables_data;
size_t jumptables_data_sz;
struct {
struct bpf_program *prog;
int sym_off;
int fd;
} *jumptable_maps;
size_t jumptable_map_cnt;
struct kern_feature_cache *feat_cache;
char *token_path;
int token_fd;
char path[];
};
struct bpf_light_subprog;
/*
* bpf_prog should be a better name but it has been used in
* linux/filter.h.
*/
struct bpf_program {
char *name;
char *sec_name;
size_t sec_idx;
const struct bpf_sec_def *sec_def;
/* this program's instruction offset (in number of instructions)
* within its containing ELF section
*/
size_t sec_insn_off;
/* number of original instructions in ELF section belonging to this
* program, not taking into account subprogram instructions possible
* appended later during relocation
*/
size_t sec_insn_cnt;
/* Offset (in number of instructions) of the start of instruction
* belonging to this BPF program within its containing main BPF
* program. For the entry-point (main) BPF program, this is always
* zero. For a sub-program, this gets reset before each of main BPF
* programs are processed and relocated and is used to determined
* whether sub-program was already appended to the main program, and
* if yes, at which instruction offset.
*/
size_t sub_insn_off;
/* instructions that belong to BPF program; insns[0] is located at
* sec_insn_off instruction within its ELF section in ELF file, so
* when mapping ELF file instruction index to the local instruction,
* one needs to subtract sec_insn_off; and vice versa.
*/
struct bpf_insn *insns;
/* actual number of instruction in this BPF program's image; for
* entry-point BPF programs this includes the size of main program
* itself plus all the used sub-programs, appended at the end
*/
size_t insns_cnt;
struct reloc_desc *reloc_desc;
int nr_reloc;
/* BPF verifier log settings */
char *log_buf;
size_t log_size;
__u32 log_level;
struct bpf_object *obj;
int fd;
bool autoload;
bool autoattach;
bool sym_global;
bool mark_btf_static;
enum bpf_prog_type type;
enum bpf_attach_type expected_attach_type;
int exception_cb_idx;
int prog_ifindex;
__u32 attach_btf_obj_fd;
__u32 attach_btf_id;
__u32 attach_prog_fd;
void *func_info;
__u32 func_info_rec_size;
__u32 func_info_cnt;
void *line_info;
__u32 line_info_rec_size;
__u32 line_info_cnt;
__u32 prog_flags;
__u8 hash[SHA256_DIGEST_LENGTH];
struct bpf_light_subprog *subprogs;
__u32 subprog_cnt;
};
enum libbpf_map_type {
LIBBPF_MAP_UNSPEC,
LIBBPF_MAP_DATA,
LIBBPF_MAP_BSS,
LIBBPF_MAP_RODATA,
LIBBPF_MAP_KCONFIG,
};
struct bpf_map_def {
unsigned int type;
unsigned int key_size;
unsigned int value_size;
unsigned int max_entries;
unsigned int map_flags;
};
struct bpf_map {
struct bpf_object *obj;
char *name;
/* real_name is defined for special internal maps (.rodata*,
* .data*, .bss, .kconfig) and preserves their original ELF section
* name. This is important to be able to find corresponding BTF
* DATASEC information.
*/
char *real_name;
int fd;
int sec_idx;
size_t sec_offset;
int map_ifindex;
int inner_map_fd;
struct bpf_map_def def;
__u32 numa_node;
__u32 btf_var_idx;
int mod_btf_fd;
__u32 btf_key_type_id;
__u32 btf_value_type_id;
__u32 btf_vmlinux_value_type_id;
enum libbpf_map_type libbpf_type;
void *mmaped;
struct bpf_struct_ops *st_ops;
struct bpf_map *inner_map;
void **init_slots;
int init_slots_sz;
char *pin_path;
bool pinned;
bool reused;
bool autocreate;
bool autoattach;
__u64 map_extra;
struct bpf_program *excl_prog;
};
#endif // _LIBREX_BINDINGS_H
================================================
FILE: librex/lib/librex.cpp
================================================
#include <fcntl.h>
#include <libelf.h>
#include <linux/bpf.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <llvm/Demangle/Demangle.h>
#include <memory>
#include <optional>
#include <ranges>
#include <stdexcept>
#include <string>
#include <system_error>
#include <unordered_map>
#include <utility>
#include <vector>
#include "bindings.h"
#include "librex.h"
using namespace std::literals;
namespace {
int debug = 0;
bool sec_def_matches(const struct bpf_sec_def *sec_def,
std::string_view sec_name) {
std::string_view sec_pfx(sec_def->sec);
// "type/" always has to have proper SEC("type/extras") form
if (sec_pfx.back() == '/')
return sec_name.starts_with(sec_pfx);
// "type+" means it can be either exact SEC("type") or
// well-formed SEC("type/extras") with proper '/' separator
if (sec_pfx.back() == '+') {
size_t pfx_size;
sec_pfx.remove_suffix(1);
pfx_size = sec_pfx.size();
// not even a prefix
if (!sec_name.starts_with(sec_pfx))
return false;
// exact match or has '/' separator
return sec_name.size() == pfx_size || sec_name[pfx_size] == '/';
}
return sec_name == sec_pfx;
}
/// @brief Walk through the static const struct bpf_sec_def section_defs
/// in libbpf.c and figure out the valid bpf section
///
/// @param sec_name section for our own rex prog
/// @return section_defs
const bpf_sec_def *find_sec_def(std::string_view sec_name) {
for (size_t i = 0; i < global_bpf_section_defs.size; i++) {
if (!sec_def_matches(&global_bpf_section_defs.arr[i], sec_name))
continue;
return &global_bpf_section_defs.arr[i];
}
return nullptr;
}
inline long bpf(__u64 cmd, union bpf_attr *attr, unsigned int size) {
return syscall(__NR_bpf, cmd, attr, size);
}
inline uint64_t align_up_16(uint64_t val) {
return (val & 0xf) ? (val & ~0xf) + 0x10 : val;
}
} // end anonymous namespace
// This struct is POD, meaning the C++ standard guarantees the same memory
// layout as that of the equivalent C struct
// https://stackoverflow.com/questions/422830/structure-of-a-c-object-in-memory-vs-a-struct
struct map_def {
uint32_t map_type;
uint32_t key_size;
uint32_t val_size;
uint32_t max_size;
uint32_t map_flag;
void *kptr;
};
struct rex_map {
private:
map_def def;
std::optional<int> map_fd;
std::string name;
public:
rex_map() = delete;
rex_map(const Elf_Data *data, Elf64_Addr base, Elf64_Off off,
const char *c_name)
: map_fd(), name(c_name) {
auto def_addr = reinterpret_cast<uint64_t>(data->d_buf) + off - base;
this->def = *reinterpret_cast<map_def *>(def_addr);
if (debug) {
std::clog << "sym_name=" << c_name << std::endl;
std::clog << "map_type=" << this->def.map_type << std::endl;
std::clog << "key_size=" << this->def.key_size << std::endl;
std::clog << "val_size=" << this->def.val_size << std::endl;
std::clog << "max_size=" << this->def.max_size << std::endl;
std::clog << "map_flag=" << this->def.map_flag << std::endl;
}
}
rex_map(const rex_map &) = delete;
rex_map(rex_map &&) = delete;
~rex_map() {
map_fd.transform([](int fd) { return close(fd); });
}
rex_map &operator=(const rex_map &) = delete;
rex_map &operator=(rex_map &&) = delete;
std::optional<int> create() {
int ret;
union bpf_attr attr{
.map_type = def.map_type,
.key_size = def.key_size,
.value_size = def.val_size,
.max_entries = def.max_size,
.map_flags = def.map_flag,
};
memcpy(attr.map_name, name.c_str(),
std::min(name.size(), sizeof(attr.map_name) - 1));
ret = bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
if (ret >= 0)
this->map_fd = ret;
return this->map_fd;
}
// rename bpf_map into bpfmap to avoid name collision
std::optional<bpf_map> bpfmap() {
// Do not create a bpf_map if the map has not been loaded
if (!map_fd)
return std::nullopt;
return bpf_map{
.name = name.data(),
.fd = map_fd.value(),
.inner_map_fd = -1,
.def =
{
.type = def.map_type,
.key_size = def.key_size,
.value_size = def.val_size,
.max_entries = def.max_size,
.map_flags = def.map_flag,
},
.libbpf_type = LIBBPF_MAP_UNSPEC,
};
}
friend struct ::rex_obj;
};
struct rex_prog {
private:
std::string name;
std::string scn_name;
const struct bpf_sec_def *sec_def;
Elf64_Off offset;
std::optional<int> prog_fd;
public:
rex_prog() = delete;
rex_prog(const char *nm, const char *scn_nm, Elf64_Off off)
: name(nm), scn_name(scn_nm), offset(off) {
sec_def = find_sec_def(scn_name);
}
rex_prog(const rex_prog &) = delete;
// std::optional only moves its underlying value and does not set the source
// object to std::nullopt. This prevents us from using a compiler-generated
// default implementation, because it would just copy the fd without
// invalidating afterwards (as int is trivially movable).
rex_prog(rex_prog &&other) noexcept
: name(std::move(other.name)), scn_name(std::move(other.scn_name)),
sec_def(std::move(other.sec_def)), offset(std::move(other.offset)),
prog_fd(std::move(other.prog_fd)) {
other.sec_def = nullptr;
other.offset = -1;
other.prog_fd = std::nullopt;
}
~rex_prog() {
prog_fd.transform([](int fd) { return close(fd); });
}
rex_prog &operator=(const rex_prog &) = delete;
rex_prog &operator=(rex_prog &&) = delete;
std::optional<bpf_program> bpf_prog() {
// Do not create a bpf_program if the prog has not been loaded
if (!prog_fd)
return std::nullopt;
// bpf_program::obj will be initliazed by the caller
// bpf_program will never outlive "this" as both are managed by rex_obj,
// so just redirect pointers
return bpf_program{
.name = name.data(),
.sec_name = scn_name.data(),
.sec_idx = (size_t)-1,
.sec_def = sec_def,
.fd = prog_fd.value(),
.type = sec_def->prog_type,
};
}
friend struct ::rex_obj;
};
struct rex_obj {
private:
struct elf_del {
[[gnu::always_inline]] void operator()(Elf *ep) const { elf_end(ep); }
};
struct file_map_del {
size_t size;
file_map_del() = default;
explicit file_map_del(size_t sz) : size(sz) {}
[[gnu::always_inline]] void operator()(unsigned char *addr) const {
munmap(addr, size);
}
};
struct bpf_obj_del {
[[gnu::always_inline]] void operator()(bpf_object *bp) const {
delete[] bp->programs;
delete[] bp->maps;
delete bp;
}
};
std::vector<rex_prog> progs;
std::unordered_map<Elf64_Off, rex_map> map_defs;
std::unique_ptr<Elf, elf_del> elf;
Elf_Scn *symtab_scn;
Elf_Scn *dynsym_scn;
Elf_Scn *maps_scn;
// Global Offset Table for PIE
Elf_Scn *got_scn;
// Dynamic relocation for PIE
Elf_Scn *rela_dyn_scn;
std::vector<rex_rela_dyn> dyn_relas;
std::vector<rex_dyn_sym> dyn_syms;
std::vector<std::string> rela_sym_name;
std::optional<unsigned long> timeout_handler_off;
std::vector<std::tuple<std::string, uint64_t, uint64_t>> text_syms;
std::unique_ptr<unsigned char[], file_map_del> file_map;
std::optional<int> prog_fd;
bool loaded;
std::string basename;
std::unique_ptr<bpf_object, bpf_obj_del> bpf_obj_ptr;
int parse_scns();
int parse_maps();
int parse_progs();
int parse_got();
int parse_rela_dyn();
public:
rex_obj() = delete;
explicit rex_obj(const char *);
rex_obj(const rex_obj &) = delete;
rex_obj(rex_obj &&) = delete;
~rex_obj();
rex_obj &operator=(const rex_obj &) = delete;
rex_obj &operator=(rex_obj &&) = delete;
int parse_elf();
int fix_maps();
int load();
bpf_object *bpf_obj();
};
rex_obj::rex_obj(const char *c_path)
: map_defs(), symtab_scn(nullptr), dynsym_scn(nullptr), maps_scn(nullptr),
prog_fd(-1), loaded(false) {
struct stat st;
void *mmap_ret;
int fd = open(c_path, 0, O_RDONLY);
Elf *ep = elf_begin(fd, ELF_C_READ_MMAP, NULL);
if (!ep)
throw std::invalid_argument("elf: failed to open file "s + c_path);
elf = std::unique_ptr<Elf, elf_del>(ep, elf_del());
if (fstat(fd, &st) < 0)
throw std::system_error(errno, std::system_category(), "fstat");
// MAP_PRIVATE ensures the changes to the memory mapped by mmap(2) are not
// carried through to the backing file
mmap_ret = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (mmap_ret == MAP_FAILED)
throw std::system_error(errno, std::system_category(), "mmap");
file_map = std::unique_ptr<unsigned char[], file_map_del>(
reinterpret_cast<unsigned char *>(mmap_ret), file_map_del(st.st_size));
std::string copy(c_path);
copy += ".base";
basename = ::basename(copy.data());
close(fd);
}
rex_obj::~rex_obj() {
prog_fd.transform([](int fd) { return close(fd); });
}
int rex_obj::parse_scns() {
size_t shstrndx;
if (elf_getshdrstrndx(elf.get(), &shstrndx)) {
std::cerr << "elf: failed to get section names section index for "
<< std::endl;
return -1;
}
for (auto scn = elf_nextscn(elf.get(), NULL); scn;
scn = elf_nextscn(elf.get(), scn)) {
char *name;
int idx = elf_ndxscn(scn);
Elf64_Shdr *sh = elf64_getshdr(scn);
if (!sh) {
std::cerr << "elf: failed to get section header, idx=" << idx
<< std::endl;
return -1;
}
name = elf_strptr(elf.get(), shstrndx, sh->sh_name);
if (!name) {
std::cerr << "elf: failed to get section name" << std::endl;
return -1;
}
if (debug)
std::clog << "section " << name << ", idx=" << idx << std::endl;
if (sh->sh_type == SHT_SYMTAB && !strcmp(".symtab", name))
this->symtab_scn = scn;
else if (sh->sh_type == SHT_DYNSYM && !strcmp(".dynsym", name))
this->dynsym_scn = scn;
else if (!strcmp(".maps", name))
this->maps_scn = scn;
else if (sh->sh_type == SHT_RELA && !strcmp(".rela.dyn", name))
this->rela_dyn_scn = scn;
}
if (!this->maps_scn && debug)
std::clog << "section .maps not found" << std::endl;
if (!this->rela_dyn_scn && debug)
std::clog << "section .rela.dyn not found" << std::endl;
return 0;
}
int rex_obj::parse_maps() {
Elf_Data *maps, *syms;
int nr_syms, nr_maps = 0, maps_shndx;
size_t strtabidx;
Elf64_Addr maps_shaddr;
if (!this->maps_scn)
return 0;
maps = elf_getdata(maps_scn, 0);
syms = elf_getdata(symtab_scn, 0);
if (!syms) {
std::cerr << "elf: failed to get symbol definitions" << std::endl;
return -1;
}
if (!maps) {
std::cerr << "elf: failed to get map definitions" << std::endl;
return -1;
}
strtabidx = elf64_getshdr(symtab_scn)->sh_link;
maps_shndx = elf_ndxscn(maps_scn);
maps_shaddr = elf64_getshdr(maps_scn)->sh_addr;
nr_syms = syms->d_size / sizeof(Elf64_Sym);
for (int i = 0; i < nr_syms; i++) {
Elf64_Sym *sym = reinterpret_cast<Elf64_Sym *>(syms->d_buf) + i;
char *name;
if (sym->st_shndx != maps_shndx ||
ELF64_ST_TYPE(sym->st_info) != STT_OBJECT)
continue;
name = elf_strptr(elf.get(), strtabidx, sym->st_name);
if (debug) {
std::clog << "symbol: " << name << ", st_value=0x" << std::hex
<< sym->st_value << ", st_size=" << std::dec << sym->st_size
<< std::endl;
}
if (sym->st_size == sizeof(map_def)) {
map_defs.try_emplace(sym->st_value, maps, maps_shaddr, sym->st_value,
name);
}
nr_maps++;
}
if (debug)
std::clog << "# of symbols in \".maps\": " << nr_maps << std::endl;
return 0;
}
// get sec name
// get function symbols
int rex_obj::parse_progs() {
size_t shstrndx, strtabidx;
Elf_Data *syms;
int nr_syms;
strtabidx = elf64_getshdr(symtab_scn)->sh_link;
if (elf_getshdrstrndx(elf.get(), &shstrndx)) {
std::cerr << "elf: failed to get section names section index" << std::endl;
return -1;
}
syms = elf_getdata(symtab_scn, 0);
if (!syms) {
std::cerr << "elf: failed to get symbol definitions" << std::endl;
return -1;
}
nr_syms = syms->d_size / sizeof(Elf64_Sym);
for (int i = 0; i < nr_syms; i++) {
Elf64_Sym *sym = reinterpret_cast<Elf64_Sym *>(syms->d_buf) + i;
Elf_Scn *scn = elf_getscn(elf.get(), sym->st_shndx);
char *scn_name, *sym_name;
const bpf_sec_def *sec_def;
if (!scn || ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
continue;
scn_name = elf_strptr(elf.get(), shstrndx, elf64_getshdr(scn)->sh_name);
sym_name = elf_strptr(elf.get(), strtabidx, sym->st_name);
if (sym->st_value) {
// Align symbol size up to 16-byte boundary, which is the default function
// alignment in x86-64, to account for trailing nops
text_syms.emplace_back(llvm::demangle(sym_name), sym->st_value,
align_up_16(sym->st_size));
}
if ("__rex_handle_timeout"sv == sym_name)
timeout_handler_off = sym->st_value;
if (debug) {
std::clog << "symbol: \"" << sym_name << "\"" << ", section: \""
<< scn_name << "\"" << ", st_value=0x" << std::hex
<< sym->st_value << ", st_size=" << sym->st_size << std::dec
<< std::endl;
}
sec_def = find_sec_def(scn_name);
if (!sec_def)
continue;
if (debug)
std::clog << "successfully matched" << std::endl;
sym_name = elf_strptr(elf.get(), strtabidx, sym->st_name);
progs.emplace_back(sym_name, scn_name, sym->st_value);
}
if (!timeout_handler_off) {
std::cerr << "elf: __rex_handle_timeout not found" << std::endl;
return -1;
}
if (debug) {
std::clog << "Found __rex_handle_timeout at offset" << std::hex
<< timeout_handler_off.value() << std::dec << std::endl;
}
return 0;
};
int rex_obj::parse_rela_dyn() {
Elf64_Shdr *rela_dyn;
rex_rela_dyn *rela_dyn_data;
uint64_t rela_dyn_addr, rela_dyn_size, nr_dyn_relas;
size_t idx;
if (!this->rela_dyn_scn)
return 0;
rela_dyn = elf64_getshdr(rela_dyn_scn);
if (!rela_dyn) {
std::cerr << "elf: failed to get .rela.dyn section" << std::endl;
return -1;
}
rela_dyn_data =
reinterpret_cast<rex_rela_dyn *>(elf_getdata(rela_dyn_scn, 0)->d_buf);
rela_dyn_addr = rela_dyn->sh_addr;
rela_dyn_size = rela_dyn->sh_size;
if (debug) {
std::clog << ".rela.dyn offset=" << std::hex << rela_dyn_addr
<< ", .rela.dyn size=" << std::dec << rela_dyn_size << std::endl;
}
if (rela_dyn_size % sizeof(rex_rela_dyn)) {
std::cerr << "elf: ill-formed .rela.dyn section" << std::endl;
return -1;
}
nr_dyn_relas = rela_dyn_size / sizeof(rex_rela_dyn);
for (idx = 0; idx < nr_dyn_relas; idx++) {
if (ELF64_R_TYPE(rela_dyn_data[idx].info) == R_X86_64_RELATIVE) {
dyn_relas.push_back(rela_dyn_data[idx]);
} else if (ELF64_R_TYPE(rela_dyn_data[idx].info) == R_X86_64_GLOB_DAT) {
uint32_t dynsym_idx = ELF64_R_SYM(rela_dyn_data[idx].info);
Elf_Data *syms = elf_getdata(dynsym_scn, 0);
size_t strtabidx = elf64_getshdr(dynsym_scn)->sh_link;
Elf64_Sym *sym = reinterpret_cast<Elf64_Sym *>(syms->d_buf) + dynsym_idx;
rex_dyn_sym dyn_sym = {};
char *name = elf_strptr(elf.get(), strtabidx, sym->st_name);
dyn_sym.offset = rela_dyn_data[idx].offset;
dyn_sym.symbol = name;
dyn_syms.push_back(dyn_sym);
} else {
std::cerr << "elf: relocation type not supported" << std::endl;
return -1;
}
}
if (debug) {
std::clog << ".rela.dyn: " << std::hex << std::endl;
for (auto &dyn_rela : dyn_relas) {
std::clog << "0x" << dyn_rela.offset << ", 0x" << dyn_rela.info << ", 0x"
<< dyn_rela.addend << std::endl;
}
for (auto &dyn_sym : dyn_syms) {
std::clog << "0x" << dyn_sym.offset << ", " << dyn_sym.symbol
<< std::endl;
}
std::clog << std::dec;
}
return 0;
}
int rex_obj::parse_elf() {
int ret;
if (!elf) {
std::cerr << "elf: failed to open object" << std::endl;
return -1;
}
ret = this->parse_scns();
ret = ret < 0 ? ret : this->parse_maps();
ret = ret < 0 ? ret : this->parse_progs();
ret = ret < 0 ? ret : this->parse_rela_dyn();
return ret;
}
int rex_obj::fix_maps() {
Elf64_Addr maps_shaddr;
Elf64_Off maps_shoff;
if (!this->maps_scn) {
return 0;
}
maps_shaddr = elf64_getshdr(maps_scn)->sh_addr;
maps_shoff = elf64_getshdr(maps_scn)->sh_offset;
if (debug) {
std::clog << ".maps section file offset=0x" << std::hex
<< elf64_getshdr(maps_scn)->sh_offset << std::dec << std::endl;
}
for (auto &[m_off, m_def] : map_defs) {
size_t kptr_file_off =
m_off + offsetof(map_def, kptr) - maps_shaddr + maps_shoff;
if (debug) {
std::clog << "map_ptr=0x" << std::hex << m_off << std::dec << std::endl;
std::clog << "map_name=\"" << m_def.name << '\"' << std::endl;
}
std::optional<int> map_fd = m_def.create();
if (!map_fd) {
perror("bpf_map_create");
return -1;
}
if (debug)
std::clog << "map_fd=" << map_fd.value() << std::endl;
*reinterpret_cast<uint64_t *>(&this->file_map[kptr_file_off]) =
map_fd.value();
}
return 0;
}
int rex_obj::load() {
int fd;
auto arr = std::make_unique<uint64_t[]>(map_defs.size());
union bpf_attr attr = {};
int idx = 0, ret = 0;
std::filesystem::path tmp_file = "/tmp/rex-" + std::to_string(gettid());
std::unique_ptr<rex_text_sym[]> tsym_arr(new rex_text_sym[text_syms.size()]);
// TODO: Will have race condition if multiple objs loaded at same time
std::ofstream output(tmp_file, std::ios::out | std::ios::binary);
output.write((char *)this->file_map.get(), this->file_map.get_deleter().size);
output.close();
fd = open(tmp_file.c_str(), O_RDONLY);
for (auto &def : map_defs)
arr[idx++] = def.first + offsetof(map_def, kptr);
attr.prog_type = BPF_PROG_TYPE_REX_BASE;
// progname was zero-initialized so we don't copy the null terminator
memcpy(attr.prog_name, basename.c_str(),
std::min(basename.size(), sizeof(attr.prog_name) - 1));
attr.rustfd = fd;
attr.license = reinterpret_cast<__u64>("GPL");
attr.map_offs = reinterpret_cast<__u64>(arr.get());
attr.map_cnt = map_defs.size();
attr.dyn_relas = reinterpret_cast<__u64>(dyn_relas.data());
attr.nr_dyn_relas = dyn_relas.size();
attr.dyn_syms = reinterpret_cast<__u64>(dyn_syms.data());
attr.nr_dyn_syms = dyn_syms.size();
// Copy data into the array that will be sent to the kernel
for (const auto &[idx, sym] : std::views::enumerate(text_syms)) {
tsym_arr[idx].symbol = std::get<0>(sym).c_str();
std::tie(std::ignore, tsym_arr[idx].offset, tsym_arr[idx].size) = sym;
}
attr.text_syms = reinterpret_cast<__u64>(tsym_arr.get());
attr.nr_text_syms = text_syms.size();
ret = bpf(BPF_PROG_LOAD_REX_BASE, &attr, sizeof(attr));
if (ret < 0) {
perror("bpf_prog_load_rex_base");
return -1;
}
this->prog_fd = ret;
if (debug)
std::clog << "Base program loaded, fd = " << ret << std::endl;
close(fd);
if (!std::filesystem::remove(tmp_file)) {
perror("remove");
goto close_fds;
}
for (auto &prog : progs) {
int curr_fd;
attr.prog_type = prog.sec_def->prog_type;
strncpy(attr.prog_name, prog.name.c_str(), sizeof(attr.prog_name) - 1);
attr.base_prog_fd = this->prog_fd.value();
attr.prog_offset = prog.offset;
attr.license = (__u64) "GPL";
attr.unwinder_insn_off = timeout_handler_off.value();
curr_fd = bpf(BPF_PROG_LOAD_REX, &attr, sizeof(attr));
if (curr_fd < 0) {
perror("bpf_prog_load_rex");
goto close_fds;
}
prog.prog_fd = curr_fd;
if (debug)
std::clog << "Program " << prog.name
<< " loaded, fd = " << prog.prog_fd.value_or(-1) << std::endl;
}
loaded = true;
return ret;
close_fds:
for (auto &prog : progs) {
prog.prog_fd = prog.prog_fd.and_then([](int fd) -> std::optional<int> {
close(fd);
return std::nullopt;
});
}
prog_fd = prog_fd.and_then([](int fd) -> std::optional<int> {
close(fd);
return std::nullopt;
});
return -1;
}
bpf_object *rex_obj::bpf_obj() {
size_t i;
// Do not create a bpf_object if the obj has not been loaded
if (!loaded)
return nullptr;
// Return the previously created ptr
if (bpf_obj_ptr)
return bpf_obj_ptr.get();
// Create a new ptr
decltype(bpf_obj_ptr) ptr(new bpf_object, bpf_obj_del());
ptr->maps = new bpf_map[map_defs.size()];
ptr->programs = new bpf_program[progs.size()];
// Fill in maps
i = 0;
for (auto &[_, m_def] : map_defs) {
if (std::optional<bpf_map> map = m_def.bpfmap()) {
ptr->maps[i] = std::move(map.value());
ptr->maps[i++].obj = ptr.get();
} else {
return nullptr;
}
}
ptr->nr_maps = i;
// Fill in programs
i = 0;
for (auto &prog : progs) {
if (std::optional<bpf_program> bpf_prog = prog.bpf_prog()) {
ptr->programs[i] = std::move(bpf_prog.value());
ptr->programs[i].obj = ptr.get();
i++;
} else {
return nullptr;
}
}
ptr->nr_programs = i;
ptr->state = OBJ_LOADED;
// Now transfer the ownership
bpf_obj_ptr = std::move(ptr);
return bpf_obj_ptr.get();
}
[[gnu::visibility("default")]] void rex_set_debug(int val) { debug = val; }
static std::vector<std::unique_ptr<rex_obj>> objs;
[[nodiscard, gnu::visibility("default")]] rex_obj *
rex_obj_load(const char *file_path) {
int ret;
if (elf_version(EV_CURRENT) == EV_NONE) {
std::cerr << "elf: failed to init libelf" << std::endl;
return nullptr;
}
try {
auto obj = std::make_unique<rex_obj>(file_path);
ret = obj->parse_elf();
ret = ret ? ret : obj->fix_maps();
ret = ret ? ret : obj->load();
if (ret >= 0) {
objs.push_back(std::move(obj));
return objs.back().get();
} else {
return nullptr;
}
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
return nullptr;
}
}
[[nodiscard, gnu::visibility("default")]] bpf_object *
rex_obj_get_bpf(rex_obj *obj) {
try {
return obj->bpf_obj();
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
return nullptr;
}
}
================================================
FILE: librex/meson.build
================================================
rex_rootdir = '..'
llvm_dep = dependency('llvm', version : '>=18')
elf_dep = dependency('libelf')
librex_public_inc = include_directories('include')
librex_sources = [
'lib/librex.cpp'
]
librex = library(
'rex',
librex_sources,
cpp_args: ['-Wno-missing-designated-field-initializers'],
build_rpath: join_paths(meson.project_source_root(), 'linux/tools/lib/bpf'),
build_by_default: false,
dependencies: [elf_dep, llvm_dep, kernel_dep, libbpf_dep],
gnu_symbol_visibility: 'hidden',
include_directories: librex_public_inc,
pic: true
)
librex_dep = declare_dependency(
link_with: librex,
include_directories: librex_public_inc
)
================================================
FILE: meson.build
================================================
project('rex-compile',
['c', 'cpp'],
version: '0.1.0',
default_options : [
'buildtype=debugoptimized',
'warning_level=2',
'werror=true',
'b_lto=true',
'b_lto_mode=thin',
'b_pie=true',
'c_std=gnu23',
'cpp_std=c++23'
]
)
add_project_arguments(
[
'-pipe',
'-march=native',
'-ffunction-sections',
'-fdata-sections',
'-fno-semantic-interposition'
],
language: ['c', 'cpp']
)
add_project_link_arguments(
[
'-Wl,-O1',
'-Wl,--gc-sections',
'-Wl,-z,now',
'-Wl,-z,relro'
],
language: ['c', 'cpp']
)
bindgen = find_program('bindgen')
cmake = find_program('cmake')
ninja = find_program('ninja')
perl = find_program('perl')
python3_bin = find_program('python3', version: '>=3.11')
realpath = find_program('realpath')
lld = find_program('ld.lld')
bc = find_program('bc')
flex = find_program('flex')
bison = find_program('bison')
subdir('linux')
subdir('librex')
rust_bootstrap_config = files('./rust/rex-config.toml')
rust_bootstrap = custom_target(
'rust',
output : ['cargo', 'rustc'],
command: [
python3_bin, '@SOURCE_ROOT@/rust/x.py', 'install',
'--config=@SOURCE_ROOT@/rust/rex-config.toml',
'--build-dir=@OUTDIR@/rust-build',
'--set', 'install.prefix=@OUTDIR@/rust-dist'
],
console: true,
build_by_default: false,
env: ['RUSTFLAGS=-C link-arg=-fuse-ld=mold']
)
all_programs = custom_target('build_deps',
output: ['kernel', 'kernel_libbpf', 'rust'],
command: ['echo', 'Build all depends'],
depends: [kernel_build, kernel_libbpf, rust_bootstrap],
console: true
)
rust_bin = join_paths(meson.current_build_dir(), 'rust-dist/bin')
cargo_wrapper = join_paths(meson.project_source_root(), 'scripts/cargo-wrapper.pl')
sanity_test_scripts = join_paths(meson.project_source_root(), 'scripts/sanity_tests/run_tests.py')
runtest_deps = []
subdir('samples')
subdir('rex')
================================================
FILE: rex/.cargo/config.toml
================================================
[build]
target = "x86_64-unknown-none"
[target.x86_64-unknown-none]
linker = "ld.mold"
rustflags = [
"-Zthreads=8",
"-Cforce-frame-pointers=y",
"-Csymbol-mangling-version=v0",
"-Ccodegen-units=1",
"-Crelocation-model=pie",
"-Crelro-level=full",
]
[unstable]
build-std = ["core"]
================================================
FILE: rex/.gitignore
================================================
Cargo.lock
target
libiustub/libiustub.so
================================================
FILE: rex/Cargo.toml
================================================
[package]
name = "rex"
version = "0.2.0"
build = "build.rs"
autotests = false
repository.workspace = true
edition.workspace = true
authors.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
test = false
doctest = false
[dependencies]
paste = { workspace = true }
rex-macros = { workspace = true }
[features]
default = ["debug_printk"]
debug_printk = []
[rex]
uheaders = [
"linux/bpf.h",
"linux/errno.h",
"linux/in.h",
"linux/perf_event.h",
"linux/pkt_cls.h",
"linux/ptrace.h",
"linux/seccomp.h",
"linux/unistd.h",
]
kheaders = [
"linux/filter.h",
"linux/gfp_types.h",
"linux/if_ether.h",
"linux/ip.h",
"linux/kcsan.h",
"linux/perf_event.h",
"linux/prandom.h",
"linux/sched.h",
"linux/seqlock.h",
"linux/skbuff.h",
"linux/tcp.h",
"linux/timekeeper_internal.h",
"linux/udp.h",
"net/xdp.h",
]
kconfigs = ["CONFIG_BPF_KPROBE_OVERRIDE", "CONFIG_KALLSYMS_ALL"]
================================================
FILE: rex/build.py
================================================
import os
import re
import subprocess
import sys
import tomllib
# https://github.com/rust-lang/rust-bindgen
bindgen_cmd = '''bindgen $LINUX_OBJ/usr/include/%s --use-core
--with-derive-default --ctypes-prefix core::ffi --no-layout-tests
--no-debug '.*' --no-doc-comments --rust-target=1.85.0 --rust-edition=2024
--translate-enum-integer-types --no-prepend-enum-name --blocklist-type
pt_regs --wrap-unsafe-ops -o %s -- -I$LINUX_OBJ/usr/include'''
k_structs = ['task_struct', 'tk_read_base', 'seqcount_raw_spinlock_t',
'clocksource', 'seqcount_t', 'seqcount_latch_t', 'timekeeper',
'kcsan_ctx', 'rnd_state', 'timespec64', 'bpf_spin_lock',
'bpf_sysctl_kern', 'xdp_buff', 'ethhdr', 'iphdr', 'tcphdr',
'udphdr', 'sk_buff', 'sock', 'pcpu_hot',
'bpf_perf_event_data_kern']
bindgen_kernel_cmd = '''bindgen %s --allowlist-type="%s"
--allowlist-var="(___GFP.*|CONFIG_.*|MAX_BPRINTF_BUF)"
--opaque-type xregs_state
--opaque-type desc_struct --opaque-type arch_lbr_state --opaque-type
local_apic --opaque-type alt_instr --opaque-type x86_msi_data --opaque-type
x86_msi_addr_lo --opaque-type kunit_try_catch --opaque-type spinlock
--no-doc-comments --blocklist-function __list_.*_report --use-core
--with-derive-default --ctypes-prefix core::ffi --no-layout-tests
--no-debug '.*' --rust-target=1.85.0 --rust-edition=2024 --wrap-unsafe-ops
-o %s -- -nostdinc -I$LINUX_SRC/arch/x86/include
-I$LINUX_OBJ/arch/x86/include/generated
-I$LINUX_SRC/include -I$LINUX_SRC/arch/x86/include/uapi
-I$LINUX_OBJ/arch/x86/include/generated/uapi -I$LINUX_SRC/include/uapi
-I$LINUX_OBJ/include
-I$LINUX_OBJ/include/generated/uapi -include
$LINUX_SRC/include/linux/compiler-version.h -include
$LINUX_SRC/include/linux/kconfig.h -include
$LINUX_SRC/include/linux/compiler_types.h -D__KERNEL__
--target=x86_64-linux-gnu -fintegrated-as -Werror=unknown-warning-option
-Werror=ignored-optimization-argument -Werror=option-ignored
-Werror=unused-command-line-argument -fmacro-prefix-map=./= -std=gnu11
-fshort-wchar -funsigned-char -fno-common -fno-PIE -fno-strict-aliasing
-mno-sse -mno-mmx -mno-sse2 -mno-3dnow -mno-avx -fcf-protection=branch
-fno-jump-tables -m64 -falign-loops=1 -mno-80387 -mno-fp-ret-in-387
-mstack-alignment=8 -mskip-rax-setup -mtune=generic -mno-red-zone
-mcmodel=kernel -Wno-sign-compare -fno-asynchronous-unwind-tables
-fno-delete-null-pointer-checks -O2 -fstack-protector-strong
-fno-stack-clash-protection -pg -mfentry -DCC_USING_NOP_MCOUNT
-DCC_USING_FENTRY -fno-lto -falign-functions=16 -fstrict-flex-arrays=3
-fms-extensions -fno-strict-overflow -fno-stack-check -Wall -Wundef
-Werror=implicit-function-declaration -Werror=implicit-int
-Werror=return-type -Werror=strict-prototypes -Wno-format-security
-Wno-trigraphs -Wno-frame-address -Wno-address-of-packed-member
-Wmissing-declarations -Wmissing-prototypes -Wframe-larger-than=2048
-Wno-gnu -Wno-microsoft-anon-tag -Wvla -Wno-pointer-sign -Wcast-function-type
-Wimplicit-fallthrough -Werror=date-time -Werror=incompatible-pointer-types
-Wenum-conversion -Wextra -Wunused -Wno-unused-but-set-variable
-Wno-unused-const-variable -Wno-format-overflow
-Wno-format-overflow-non-kprintf -Wno-format-truncation-non-kprintf
-Wno-override-init -Wno-pointer-to-enum-cast
-Wno-tautological-constant-out-of-range-compare -Wno-unaligned-access
-Wno-enum-compare-conditional -Wno-enum-enum-conversion
-Wno-missing-field-initializers -Wno-type-limits -Wno-shift-negative-value
-Wno-sign-compare -Wno-unused-parameter -g
-DKBUILD_MODFILE='"rex/rex_generated"' -DKBUILD_BASENAME='"rex_generated"'
-DKBUILD_MODNAME='"rex_generated"' -D__KBUILD_MODNAME=kmod_rex_generated
-D__BINDGEN__ -DMODULE'''
def prep_uapi_headers(linux_path, headers, out_dir):
for header in headers:
subdir, hfile = os.path.split(header)
subdir = os.path.join(out_dir, subdir)
if not os.path.exists(subdir):
os.makedirs(subdir)
out_f = os.path.join(subdir, '%s.rs' % os.path.splitext(hfile)[0])
cmd = bindgen_cmd.replace('\n', ' ').replace('$LINUX_OBJ', linux_path)
subprocess.run(cmd % (header, out_f), check=True, shell=True)
def parse_cargo_toml(cargo_toml_path):
with open(cargo_toml_path, 'rb') as toml_f:
cargo_toml = tomllib.load(toml_f)
uheaders = cargo_toml['rex'].get('uheaders', [])
kheaders = cargo_toml['rex'].get('kheaders', [])
kconfigs = cargo_toml['rex'].get('kconfigs', [])
return uheaders, kheaders, kconfigs
def prep_kernel_headers(headers, linux_src, linux_obj, out_dir):
bindings_h = os.path.join(out_dir, 'bindings.h')
out_subdir = os.path.join(out_dir, 'linux')
if not os.path.exists(out_subdir):
os.makedirs(out_subdir)
kernel_rs = os.path.join(out_subdir, 'kernel.rs')
with open(bindings_h, 'w') as bindings:
for h in headers:
bindings.write('#include <%s>\n' % h)
cmd = (
bindgen_kernel_cmd.replace("\n", " ")
.replace("$LINUX_SRC", linux_src)
.replace("$LINUX_OBJ", linux_obj)
)
subprocess.run(
cmd % (bindings_h, "|".join(k_structs), kernel_rs), check=True, shell=True
)
def parse_kconfigs(dot_config_path, kconfigs):
if len(kconfigs) == 0:
return
with open(dot_config_path) as dot_config:
dot_config_content = dot_config.readlines()
ptn = re.compile('(%s)' % '|'.join(kconfigs))
fmt = 'cargo:rustc-cfg=%s="%s"\n' + \
'cargo::rustc-check-cfg=cfg(%s, values("%s"))'
print('\n'.join(map(lambda l: fmt % (l * 2),
map(lambda l: tuple(l.strip().split('=')),
filter(lambda l: l[0] != '#' and ptn.match(l),
dot_config_content)))))
def main(argv):
linux_obj = argv[1]
linux_src = argv[2]
out_dir = argv[3]
target_path = os.getcwd()
result = parse_cargo_toml(os.path.join(target_path, 'Cargo.toml'))
uheaders, kheaders, kconfigs = result
u_out_dir = os.path.join(out_dir, 'uapi')
prep_uapi_headers(linux_obj, uheaders, u_out_dir)
prep_kernel_headers(kheaders, linux_src, linux_obj, out_dir)
parse_kconfigs(os.path.join(linux_obj, '.config'), kconfigs)
return 0
if __name__ == '__main__':
exit(main(sys.argv))
================================================
FILE: rex/build.rs
================================================
#![feature(exit_status_error)]
use std::io::Result;
use std::path::Path;
use std::process::Command;
use std::string::String;
use std::{env, fs};
fn main() -> Result<()> {
let out_dir = env::var("OUT_DIR").unwrap();
let linux_obj = env::var("LINUX_OBJ").unwrap();
let linux_src = env::var("LINUX_SRC").unwrap();
let output = Command::new("python3")
.arg("build.py")
.arg(&linux_obj)
.arg(&linux_src)
.arg(&out_dir)
.output()?;
output
.status
.exit_ok()
.map(|_| print!("{}", String::from_utf8_lossy(&output.stdout)))
.map_err(|_| panic!("\n{}", String::from_utf8_lossy(&output.stderr)))
.unwrap();
let mut rexstub_outdir = Path::new(&out_dir).join("librexstub");
if !rexstub_outdir.exists() {
fs::create_dir(&rexstub_outdir)?;
}
let rexstub_so = rexstub_outdir.join("librexstub.so");
Command::new("clang")
.arg("-fPIC")
.arg("-nostartfiles")
.arg("-nodefaultlibs")
.arg("--shared")
.arg("-o")
.arg(rexstub_so.to_string_lossy().to_mut())
.arg("./librexstub/lib.c")
.output()?;
rexstub_outdir = rexstub_outdir.canonicalize()?;
println!("cargo:rerun-if-changed=Cargo.toml");
println!("cargo:rerun-if-changed=./src/*");
println!("cargo:rerun-if-changed=./librexstub/*");
println!("cargo:rustc-link-lib=dylib=rexstub");
println!(
"cargo:rustc-link-search=native={}",
rexstub_outdir.to_string_lossy()
);
Ok(())
}
================================================
FILE: rex/librexstub/lib.c
================================================
// Functions
#define KSYM_FUNC(func) \
int func() { return 0; }
KSYM_FUNC(bpf_get_current_pid_tgid)
KSYM_FUNC(bpf_trace_printk)
KSYM_FUNC(bpf_map_lookup_elem)
KSYM_FUNC(bpf_map_update_elem)
KSYM_FUNC(bpf_map_delete_elem)
KSYM_FUNC(bpf_map_push_elem)
KSYM_FUNC(bpf_map_pop_elem)
KSYM_FUNC(bpf_map_peek_elem)
KSYM_FUNC(bpf_probe_read_kernel)
KSYM_FUNC(ktime_get_mono_fast_ns)
KSYM_FUNC(ktime_get_boot_fast_ns)
KSYM_FUNC(get_random_u32)
KSYM_FUNC(bpf_snprintf)
KSYM_FUNC(vprintk)
KSYM_FUNC(rex_landingpad)
KSYM_FUNC(bpf_spin_lock)
KSYM_FUNC(bpf_spin_unlock)
KSYM_FUNC(just_return_func)
KSYM_FUNC(bpf_get_stackid_pe)
KSYM_FUNC(bpf_perf_prog_read_value)
KSYM_FUNC(bpf_perf_event_output_tp)
KSYM_FUNC(bpf_perf_event_read_value)
KSYM_FUNC(bpf_skb_event_output)
KSYM_FUNC(bpf_xdp_event_output)
KSYM_FUNC(bpf_xdp_adjust_head)
KSYM_FUNC(bpf_xdp_adjust_tail)
KSYM_FUNC(bpf_clone_redirect)
KSYM_FUNC(bpf_ringbuf_output)
KSYM_FUNC(bpf_ringbuf_reserve)
KSYM_FUNC(bpf_ringbuf_submit)
KSYM_FUNC(bpf_ringbuf_discard)
KSYM_FUNC(bpf_ringbuf_query)
KSYM_FUNC(bpf_ktime_get_ns)
KSYM_FUNC(bpf_ktime_get_boot_ns)
KSYM_FUNC(bpf_ktime_get_coarse_ns)
KSYM_FUNC(rex_trace_printk)
KSYM_FUNC(bpf_task_from_pid)
KSYM_FUNC(bpf_task_release)
// Global variables
unsigned long jiffies;
int numa_node;
void *rex_cleanup_entries;
unsigned long rex_stack_ptr;
void *current_task;
int cpu_number;
unsigned char rex_termination_state;
unsigned long this_cpu_off;
char *rex_log_buf;
================================================
FILE: rex/meson.build
================================================
build_dir = run_command(
realpath,
'--relative-to',
meson.current_source_dir(),
meson.current_build_dir(),
capture: true,
check: true
).stdout().strip()
env = environment()
env.prepend('PATH', rust_bin)
env.set('LINUX_OBJ', kbuild_dir)
env.set('LINUX_SRC', join_paths(meson.project_source_root(), './linux'))
env.set('CARGO_TARGET_DIR', join_paths(build_dir, 'target'))
rex_build = custom_target(
'rex-build',
output: ['target'],
command: [
cargo_wrapper, rust_bin, '-Z',
'unstable-options',
'-C', meson.current_source_dir(),
'rustc', '-qr', '--',
'-Cenable_rex'
],
env: env,
console: false,
build_always_stale: true,
build_by_default: false
)
================================================
FILE: rex/src/base_helper.rs
================================================
// use crate::timekeeping::*;
use core::intrinsics::unlikely;
use core::mem::MaybeUninit;
use crate::ffi;
use crate::linux::bpf::bpf_map_type;
use crate::linux::errno::EINVAL;
use crate::map::*;
use crate::per_cpu::this_cpu_read;
use crate::random32::bpf_user_rnd_u32;
use crate::utils::{to_result, NoRef, Result};
macro_rules! termination_check {
($func:expr) => {{
// Declare and initialize the termination flag pointer
let termination_flag: *mut u8;
unsafe {
termination_flag = crate::per_cpu::this_cpu_ptr_mut(
&raw mut crate::ffi::rex_termination_state,
);
// Set the termination flag
*termination_flag = 1;
}
// Call the provided function
let res = $func;
// Check the termination flag and handle timeout
unsafe {
if core::intrinsics::unlikely(*termination_flag == 2) {
crate::panic::__rex_handle_timeout();
} else {
// Reset the termination flag upon exiting
*termination_flag = 0;
}
}
// Return the result of the function call
res
}};
}
pub(crate) fn bpf_get_smp_processor_id() -> i32 {
unsafe { this_cpu_read(&raw const ffi::cpu_number) }
}
pub(crate) fn bpf_map_lookup_elem<'a, const MT: bpf_map_type, K, V>(
map: &'static RexMapHandle<MT, K, V>,
key: &'a K,
) -> Option<&'a mut V>
where
V: Copy + NoRef,
{
let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
if unlikely(map_kptr.is_null()) {
return None;
}
let value = termination_check!(unsafe {
ffi::bpf_map_lookup_elem(map_kptr, key as *const K as *const ())
as *mut V
});
if value.is_null() {
None
} else {
Some(unsafe { &mut *value })
}
}
pub(crate) fn bpf_map_update_elem<const MT: bpf_map_type, K, V>(
map: &'static RexMapHandle<MT, K, V>,
key: &K,
value: &V,
flags: u64,
) -> Result
where
V: Copy + NoRef,
{
let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
if unlikely(map_kptr.is_null()) {
return Err(EINVAL as i32);
}
termination_check!(unsafe {
to_result!(ffi::bpf_map_update_elem(
map_kptr,
key as *const K as *const (),
value as *const V as *const (),
flags
) as i32)
})
}
pub(crate) fn bpf_map_delete_elem<const MT: bpf_map_type, K, V>(
map: &'static RexMapHandle<MT, K, V>,
key: &K,
) -> Result
where
V: Copy + NoRef,
{
let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
if unlikely(map_kptr.is_null()) {
return Err(EINVAL as i32);
}
termination_check!(unsafe {
to_result!(ffi::bpf_map_delete_elem(
map_kptr,
key as *const K as *const ()
) as i32)
})
}
pub(crate) fn bpf_map_push_elem<const MT: bpf_map_type, K, V>(
map: &'static RexMapHandle<MT, K, V>,
value: &V,
flags: u64,
) -> Result
where
V: Copy + NoRef,
{
let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
if unlikely(map_kptr.is_null()) {
return Err(EINVAL as i32);
}
termination_check!(unsafe {
to_result!(ffi::bpf_map_push_elem(
map_kptr,
value as *const V as *const (),
flags
) as i32)
})
}
pub(crate) fn bpf_map_pop_elem<const MT: bpf_map_type, K, V>(
map: &'static RexMapHandle<MT, K, V>,
) -> Option<V>
where
V: Copy + NoRef,
{
let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
if unlikely(map_kptr.is_null()) {
return None;
}
let mut value: MaybeUninit<V> = MaybeUninit::uninit();
let res = termination_check!(unsafe {
to_result!(ffi::bpf_map_pop_elem(
map_kptr,
value.as_mut_ptr() as *mut ()
) as i32)
});
res.map(|_| unsafe { value.assume_init() }).ok()
}
pub(crate) fn bpf_map_peek_elem<const MT: bpf_map_type, K, V>(
map: &'static RexMapHandle<MT, K, V>,
) -> Option<V>
where
V: Copy + NoRef,
{
let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
if unlikely(map_kptr.is_null()) {
return None;
}
let mut value: MaybeUninit<V> = MaybeUninit::uninit();
let res = termination_check!(unsafe {
to_result!(ffi::bpf_map_peek_elem(
map_kptr,
value.as_mut_ptr() as *mut ()
) as i32)
});
res.map(|_| unsafe { value.assume_init() }).ok()
}
// pub(crate) fn bpf_for_each_map_elem<const MT: bpf_map_type, K, V, C>(
// map: &'static RexMapHandle<MT, K, V>,
// callback_fn: extern "C" fn(*const (), *const K, *const V, *const C) ->
// i64, callback_ctx: &C,
// flags: u64,
// ) -> Result {
// let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
// if unlikely(map_kptr.is_null()) {
// return Err(EINVAL as i32);
// }
// unsafe {
// to_result!(ffi::bpf_for_each_map_elem(map_kptr, callback_fn as
// *const (), callback_ctx as *const C as *const (), flags) as i32) }
// }
// Design decision: Make the destination a generic type so that probe read
// kernel can directly fill in variables of certain type. This also achieves
// size checking, since T is known at compile time for monomorphization
pub(crate) fn bpf_probe_read_kernel<T>(
dst: &mut T,
unsafe_ptr: *const (),
) -> Result
where
T: Copy + NoRef,
{
termination_check!(unsafe {
to_result!(ffi::bpf_probe_read_kernel(
dst as *mut T as *mut (),
core::mem::size_of::<T>() as u32,
unsafe_ptr,
))
})
}
pub(crate) fn bpf_jiffies64() -> u64 {
unsafe { core::ptr::read_volatile(&ffi::jiffies) }
}
/// Assumes `CONFIG_USE_PERCPU_NUMA_NODE_ID`
pub(crate) fn bpf_get_numa_node_id() -> i32 {
unsafe { this_cpu_read(&raw const ffi::numa_node) }
}
// This two functions call the original helper directly, so that confirm the
// return value is correct
/*
pub(crate) fn bpf_ktime_get_ns_origin() -> u64 {
unsafe { ffi::ktime_get_mono_fast_ns() }
}
pub(crate) fn bpf_ktime_get_boot_ns_origin() -> u64 {
unsafe { ffi::ktime_get_boot_fast_ns() }
}
*/
pub(crate) fn bpf_ktime_get_ns() -> u64 {
termination_check!(unsafe { ffi::bpf_ktime_get_ns() })
}
pub(crate) fn bpf_ktime_get_boot_ns() -> u64 {
termination_check!(unsafe { ffi::bpf_ktime_get_boot_ns() })
}
pub(crate) fn bpf_ktime_get_coarse_ns() -> u64 {
termination_check!(unsafe { ffi::bpf_ktime_get_coarse_ns() })
}
/*
pub(crate) fn bpf_ktime_get_ns() -> u64 {
ktime_get_mono_fast_ns()
}
pub(crate) fn bpf_ktime_get_boot_ns() -> u64 {
ktime_get_boot_fast_ns()
}
pub(crate) fn bpf_ktime_get_coarse_ns() -> u64 {
ktime_get_coarse() as u64
}
*/
pub(crate) fn bpf_get_prandom_u32() -> u32 {
termination_check!(bpf_user_rnd_u32())
}
// In document it says that data is a pointer to an array of 64-bit values.
pub(crate) fn bpf_snprintf<const N: usize, const M: usize>(
str: &mut [u8; N],
fmt: &str,
data: &[u64; M],
) -> Result {
termination_check!(unsafe {
to_result!(ffi::bpf_snprintf(
str.as_mut_ptr(),
N as u32,
fmt.as_ptr(),
data.as_ptr(),
M as u32,
) as i32)
})
}
macro_rules! base_helper_defs {
() => {
#[inline(always)]
pub fn bpf_get_smp_processor_id(&self) -> i32 {
crate::base_helper::bpf_get_smp_processor_id()
}
// Self should already have impl<'a>
#[inline(always)]
pub fn bpf_map_lookup_elem<'b, const MT: bpf_map_type, K, V>(
&self,
map: &'static crate::map::RexMapHandle<MT, K, V>,
key: &'b K,
) -> Option<&'b mut V>
where
V: Copy + crate::utils::NoRef,
{
crate::base_helper::bpf_map_lookup_elem(map, key)
}
#[inline(always)]
pub fn bpf_map_update_elem<const MT: bpf_map_type, K, V>(
&self,
map: &'static crate::map::RexMapHandle<MT, K, V>,
key: &K,
value: &V,
flags: u64,
) -> crate::Result
where
V: Copy + crate::utils::NoRef,
{
crate::base_helper::bpf_map_update_elem(map, key, value, flags)
}
#[inline(always)]
pub fn bpf_map_delete_elem<const MT: bpf_map_type, K, V>(
&self,
map: &'static crate::map::RexMapHandle<MT, K, V>,
key: &K,
) -> crate::Result
where
V: Copy + crate::utils::NoRef,
{
crate::base_helper::bpf_map_delete_elem(map, key)
}
#[inline(always)]
pub fn bpf_map_push_elem<const MT: bpf_map_type, K, V>(
&self,
map: &'static crate::map::RexMapHandle<MT, K, V>,
value: &V,
flags: u64,
) -> crate::Result
where
V: Copy + crate::utils::NoRef,
{
crate::base_helper::bpf_map_push_elem(map, value, flags)
}
#[inline(always)]
pub fn bpf_map_pop_elem<const MT: bpf_map_type, K, V>(
&self,
map: &'static crate::map::RexMapHandle<MT, K, V>,
) -> Option<V>
where
V: Copy + crate::utils::NoRef,
{
crate::base_helper::bpf_map_pop_elem(map)
}
#[inline(always)]
pub fn bpf_map_peek_elem<const MT: bpf_map_type, K, V>(
&self,
map: &'static crate::map::RexMapHandle<MT, K, V>,
) -> Option<V>
where
V: Copy + crate::utils::NoRef,
{
crate::base_helper::bpf_map_peek_elem(map)
}
#[inline(always)]
pub fn bpf_probe_read_kernel<T>(
&self,
dst: &mut T,
unsafe_ptr: *const (),
) -> crate::Result
where
T: Copy + crate::utils::NoRef,
{
crate::base_helper::bpf_probe_read_kernel(dst, unsafe_ptr)
}
#[inline(always)]
pub fn bpf_jiffies64(&self) -> u64 {
crate::base_helper::bpf_jiffies64()
}
#[inline(always)]
pub fn bpf_get_numa_node_id(&self) -> i32 {
crate::base_helper::bpf_get_numa_node_id()
}
/*
#[inline(always)]
pub fn bpf_ktime_get_ns_origin(&self) -> u64 {
crate::base_helper::bpf_ktime_get_ns_origin()
}
#[inline(always)]
pub fn bpf_ktime_get_boot_ns_origin(&self) -> u64 {
crate::base_helper::bpf_ktime_get_boot_ns_origin()
}
*/
#[inline(always)]
pub fn bpf_ktime_get_ns(&self) -> u64 {
crate::base_helper::bpf_ktime_get_ns()
}
#[inline(always)]
pub fn bpf_ktime_get_boot_ns(&self) -> u64 {
crate::base_helper::bpf_ktime_get_boot_ns()
}
#[inline(always)]
pub fn bpf_ktime_get_coarse_ns(&self) -> u64 {
crate::base_helper::bpf_ktime_get_coarse_ns()
}
#[inline(always)]
pub fn bpf_get_prandom_u32(&self) -> u32 {
crate::base_helper::bpf_get_prandom_u32()
}
#[inline(always)]
pub fn bpf_snprintf<const N: usize, const M: usize>(
&self,
buf: &mut [u8; N],
fmt: &str,
data: &[u64; M],
) -> crate::Result {
crate::base_helper::bpf_snprintf(buf, fmt, data)
}
// #[inline(always)]
// pub fn bpf_ringbuf_reserve<T>(
// &self,
// map: &'static RexRingBuf,
// size: u64,
// flags: u64,
// ) -> *mut T {
// crate::base_helper::bpf_ringbuf_reserve(map, flags)
// }
//
// #[inline(always)]
// pub fn bpf_ringbuf_submit<T>(&self, data: &mut T, flags: u64) {
// crate::base_helper::bpf_ringbuf_submit(data, flags)
// }
};
}
pub(crate) use base_helper_defs;
pub(crate) use termination_check;
================================================
FILE: rex/src/bindings/linux/kernel.rs
================================================
include!(concat!(env!("OUT_DIR"), "/linux/kernel.rs"));
================================================
FILE: rex/src/bindings/linux/mod.rs
================================================
pub mod kernel;
================================================
FILE: rex/src/bindings/mod.rs
================================================
#![allow(
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unused,
unnecessary_transmutes
)]
pub(crate) mod linux;
pub mod uapi;
================================================
FILE: rex/src/bindings/uapi/linux/bpf.rs
================================================
include!(concat!(env!("OUT_DIR"), "/uapi/linux/bpf.rs"));
================================================
FILE: rex/src/bindings/uapi/linux/errno.rs
================================================
include!(concat!(env!("OUT_DIR"), "/uapi/linux/errno.rs"));
================================================
FILE: rex/src/bindings/uapi/linux/in.rs
================================================
include!(concat!(env!("OUT_DIR"), "/uapi/linux/in.rs"));
================================================
FILE: rex/src/bindings/uapi/linux/mod.rs
================================================
pub mod bpf;
pub mod errno;
pub mod r#in;
pub mod perf_event;
pub mod pkt_cls;
pub mod ptrace;
pub mod seccomp;
pub mod unistd;
================================================
FILE: rex/src/bindings/uapi/linux/perf_event.rs
================================================
include!(concat!(env!("OUT_DIR"), "/uapi/linux/perf_event.rs"));
================================================
FILE: rex/src/bindings/uapi/linux/pkt_cls.rs
================================================
include!(concat!(env!("OUT_DIR"), "/uapi/linux/pkt_cls.rs"));
================================================
FILE: rex/src/bindings/uapi/linux/ptrace.rs
================================================
include!(concat!(env!("OUT_DIR"), "/uapi/linux/ptrace.rs"));
================================================
FILE: rex/src/bindings/uapi/linux/seccomp.rs
================================================
include!(concat!(env!("OUT_DIR"), "/uapi/linux/seccomp.rs"));
================================================
FILE: rex/src/bindings/uapi/linux/unistd.rs
================================================
include!(concat!(env!("OUT_DIR"), "/uapi/linux/unistd.rs"));
================================================
FILE: rex/src/bindings/uapi/mod.rs
================================================
pub mod linux;
================================================
FILE: rex/src/debug.rs
================================================
use core::ffi::{c_int, c_uchar};
use crate::ffi;
// WARN must add "\0" at the end of &str, c_str is different from rust str
#[unsafe(no_mangle)]
pub(crate) unsafe extern "C" fn printk(fmt: &str, mut ap: ...) -> c_int {
unsafe { ffi::vprintk(fmt.as_ptr() as *const c_uchar, ap) }
}
================================================
FILE: rex/src/ffi.rs
================================================
// All kernel symbols we need should be declared here
#![allow(dead_code)]
use core::ffi::{c_uchar, VaList};
use crate::bindings::linux::kernel::{
bpf_perf_event_data_kern, sk_buff, task_struct, xdp_buff, MAX_BPRINTF_BUF,
};
use crate::bindings::uapi::linux::bpf::{bpf_perf_event_value, bpf_spin_lock};
use crate::panic::{CleanupEntry, ENTRIES_SIZE};
// Functions
unsafe extern "C" {
/// `void *bpf_map_lookup_elem(struct bpf_map *map, const void *key)`
///
/// `struct bpf_map` is opaque in our case so make it a `*mut ()`
pub(crate) fn bpf_map_lookup_elem(map: *mut (), key: *const ()) -> *mut ();
/// `long bpf_map_update_elem(struct bpf_map *map, const void *key, const
/// void *value, u64 flags)`
///
/// `struct bpf_map` is opaque in our case so make it a `*mut ()`
pub(crate) fn bpf_map_update_elem(
map: *mut (),
key: *const (),
value: *const (),
flags: u64,
) -> i64;
/// `long bpf_map_delete_elem(struct bpf_map *map, const void *key)`
///
/// `struct bpf_map` is opaque in our case so make it a `*mut ()`
pub(crate) fn bpf_map_delete_elem(map: *mut (), key: *const ()) -> i64;
/// `long bpf_map_push_elem(struct bpf_map *map, const void *value, u64
/// flags)`
///
/// `struct bpf_map` is opaque in our case so make it a `*mut ()`
pub(crate) fn bpf_map_push_elem(
map: *mut (),
value: *const (),
flags: u64,
) -> i64;
/// `long bpf_map_pop_elem(struct bpf_map *map, void *value)`
///
/// `struct bpf_map` is opaque in our case so make it a `*mut ()`
pub(crate) fn bpf_map_pop_elem(map: *mut (), value: *const ()) -> i64;
/// `long bpf_map_peek_elem(struct bpf_map *map, void *value)`
///
/// `struct bpf_map` is opaque in our case so make it a `*mut ()`
pub(crate) fn bpf_map_peek_elem(map: *mut (), value: *const ()) -> i64;
/// `long bpf_probe_read_kernel(void *dst, u32 size, const void
/// *unsafe_ptr)`
pub(crate) fn bpf_probe_read_kernel(
dst: *mut (),
size: u32,
unsafe_ptr: *const (),
) -> i64;
/// `u64 notrace ktime_get_mono_fast_ns(void)`
pub(crate) fn ktime_get_mono_fast_ns() -> u64;
/// `u64 notrace ktime_get_boot_fast_ns(void)`
pub(crate) fn ktime_get_boot_fast_ns() -> u64;
/// `u64 bpf_ktime_get_ns(void)`
pub(crate) fn bpf_ktime_get_ns() -> u64;
/// `u64 bpf_ktime_get_boot_ns(void)`
pub(crate) fn bpf_ktime_get_boot_ns() -> u64;
/// `u64 bpf_ktime_get_coarse_ns(void)`
pub(crate) fn bpf_ktime_get_coarse_ns() -> u64;
/// `u32 get_random_u32(void)`
pub(crate) fn get_random_u32() -> u32;
/// `long bpf_snprintf_btf(char *str, u32 str_size, struct btf_ptr *ptr, u32
/// btf_ptr_size, u64 flags)`
pub(crate) fn bpf_snprintf(
str: *mut u8,
str_size: u32,
fmt: *const u8,
data: *const u64,
data_len: u32,
) -> i64;
/// `asmlinkage int vprintk(const char *fmt, va_list args)`
pub(crate) fn vprintk(fmt: *const c_uchar, args: VaList) -> i32;
/// `__nocfi noinline void notrace __noreturn rex_landingpad(char *msg)`
///
/// The in-kernel panic landingpad for panic recovery
pub(crate) fn rex_landingpad() -> !;
/// `long bpf_spin_lock(struct bpf_spin_lock *lock)`
pub(crate) fn bpf_spin_lock(lock: *mut bpf_spin_lock) -> i64;
/// `long bpf_spin_unlock(struct bpf_spin_lock *lock)`
pub(crate) fn bpf_spin_unlock(lock: *mut bpf_spin_lock) -> i64;
/// `asmlinkage void just_return_func(void)`
pub(crate) fn just_return_func();
/// `long bpf_get_stackid_pe(struct bpf_perf_event_data_kern *ctx, struct
/// bpf_map *map, u64 flags)`
///
/// The specialized version of `bpf_get_stackid` for perf event programs
///
/// Also allow improper_ctypes here since the empty lock_class_key is
/// guaranteed not touched by us
#[allow(improper_ctypes)]
pub(crate) fn bpf_get_stackid_pe(
ctx: *const bpf_perf_event_data_kern,
map: *mut (),
flags: u64,
) -> i64;
/// `long bpf_perf_prog_read_value(struct bpf_perf_event_data *ctx, struct
/// bpf_perf_event_value *buf, u32 buf_size)`
///
/// Also allow improper_ctypes here since the empty lock_class_key is
/// guaranteed not touched by us
#[allow(improper_ctypes)]
pub(crate) fn bpf_perf_prog_read_value(
ctx: *const bpf_perf_event_data_kern,
buf: &mut bpf_perf_event_value,
size: u32,
) -> i64;
/// `long bpf_perf_event_output_tp(void *tp_buff, struct bpf_map *map, u64
/// flags, void *data, u64 size)`
pub(crate) fn bpf_perf_event_output_tp(
tp_buff: *const (),
map: *mut (),
flags: u64,
data: *const (),
size: u64,
) -> i64;
/// `long bpf_perf_event_read_value(struct bpf_map *map, u64 flags,
/// struct bpf_perf_event_value *buf, u32 buf_size)`
/// same reason for use of improper_ctypes as bpf_perf_prog_read_value
#[allow(improper_ctypes)]
pub(crate) fn bpf_perf_event_read_value(
map: *mut (),
flags: u64,
buf: &mut bpf_perf_event_value,
buf_size: u32,
) -> i64;
/// `long bpf_skb_event_output(struct sk_buff *skb, struct bpf_map *map, u64
/// flags, void *meta, u64 meta_size)`
/// The compiler complains about some non-FFI safe type, but since the
/// kernel is using it fine it should be safe for an FFI call using C ABI
#[allow(improper_ctypes)]
pub(crate) fn bpf_skb_event_output(
skb: *const sk_buff,
map: *mut (),
flags: u64,
meta: *const (),
meta_size: u64,
) -> i64;
/// `long bpf_xdp_event_output(struct xdp_buff *xdp, struct bpf_map *map,
/// u64 flags, void *meta, u64 meta_size)`
/// The compiler complains about some non-FFI safe type, but since the
/// kernel is using it fine it should be safe for an FFI call using C ABI
#[allow(improper_ctypes)]
pub(crate) fn bpf_xdp_event_output(
xdp: *const xdp_buff,
map: *mut (),
flags: u64,
meta: *const (),
meta_size: u64,
) -> i64;
/// `long bpf_xdp_adjust_head(struct xdp_buff *xdp, int offset)`
///
/// The compiler complains about some non-FFI safe type, but since the
/// kernel is using it fine it should be safe for an FFI call using C ABI
#[allow(improper_ctypes)]
pub(crate) fn bpf_xdp_adjust_head(xdp: *mut xdp_buff, offset: i32) -> i32;
/// long bpf_xdp_adjust_tail(struct xdp_buff *xdp, int offset)
///
/// The compiler complains about some non-FFI safe type, but since the
/// kernel is using it fine it should be safe for an FFI call using C ABI
#[allow(improper_ctypes)]
pub(crate) fn bpf_xdp_adjust_tail(xdp: *mut xdp_buff, offset: i32) -> i32;
/// long bpf_clone_redirect(struct sk_buff *skb, u32 ifindex, u64 flags)
///
/// The compiler complains about some non-FFI safe type, but since the
/// kernel is using it fine it should be safe for an FFI call using C ABI
#[allow(improper_ctypes)]
pub(crate) fn bpf_clone_redirect(
skb: *mut sk_buff,
ifindex: u32,
flags: u64,
) -> i32;
/// void *bpf_ringbuf_reserve(void *ringbuf, u64 size, u64 flags)
pub(crate) fn bpf_ringbuf_reserve(
ringbuf: *mut (),
size: u64,
flags: u64,
) -> *mut ();
/// long bpf_ringbuf_output(void *ringbuf, void *data, u64 size, u64 flags)
// data is marked `ARG_PTR_TO_MEM | MEM_RDONLY`, which implies the argument
// is comptiable with readonly memory and does not modifiy the memory
// pointed by the pointer, therefore, we use a *const () type.
pub(crate) fn bpf_ringbuf_output(
ringbuf: *mut (),
data: *const (),
size: u64,
flags: u64,
) -> i64;
/// void bpf_ringbuf_submit(void *data, u64 flags)
pub(crate) fn bpf_ringbuf_submit(data: *mut (), flags: u64);
/// void bpf_ringbuf_discard(void *data, u64 flags)
pub(crate) fn bpf_ringbuf_discard(data: *mut (), flags: u64);
/// u64 bpf_ringbuf_query(void *ringbuf, u64 flags)
pub(crate) fn bpf_ringbuf_query(ringbuf: *mut (), flags: u64) -> u64;
/// void rex_trace_printk(void)
pub(crate) fn rex_trace_printk();
/// __bpf_kfunc struct task_struct *bpf_task_from_pid(s32 pid)
pub(crate) fn bpf_task_from_pid(pid: i32) -> *mut task_struct;
/// __bpf_kfunc void bpf_task_release(struct task_struct *p)
pub(crate) fn bpf_task_release(task: *mut task_struct);
}
// Global variables
unsafe extern "C" {
/// `extern unsigned long volatile __cacheline_aligned_in_smp
/// __jiffy_arch_data jiffies;`
///
/// Real definition done via linker script (`arch/x86/kernel/vmlinux.lds.S`)
/// and is made an alias to `jiffies_64` on x86
pub(crate) static jiffies: u64;
/// `DEFINE_PER_CPU(int, numa_node);`
pub(crate) static numa_node: i32;
/// `DEFINE_PER_CPU(struct rex_cleanup_entry[64], rex_cleanup_entries)
/// ____cacheline_aligned = { 0 };`
///
/// Used for cleanup upon panic
///
/// Pointee type omitted since this per-cpu variable will never be directly
/// dereferenced, it is always used for per-cpu address calculation
///
/// Allow the use of rust fn ptr as the function is only called in Rust
#[allow(improper_ctypes)]
pub(crate) static mut rex_cleanup_entries: [CleanupEntry; ENTRIES_SIZE];
/// `DEFINE_PER_CPU(void *, rex_stack_ptr);`
///
/// Top of the per-cpu stack for rex programs
pub(crate) static rex_stack_ptr: u64;
/// `DECLARE_PER_CPU_CACHE_HOT(struct task_struct *, current_task);`
///
/// Per-cpu pointer of the current task
// rustc properly treats the empty `lock_class_key` as zero-sized
#[allow(improper_ctypes)]
pub(crate) static current_task: *mut task_struct;
/// `DECLARE_PER_CPU_CACHE_HOT(int, cpu_number);`
///
/// Current CPU number
pub(crate) static cpu_number: i32;
/// `DEFINE_PER_CPU(int, rex_termination_state);`
///
/// Used to indidicate whether a BPF program in a CPU is executing
/// inside a helper, or inside a panic handler, or just in BPF text.
pub(crate) static mut rex_termination_state: u8;
/// DEFINE_PER_CPU_READ_MOSTLY(unsigned long, this_cpu_off) =
/// BOOT_PERCPU_OFFSET;
///
/// Offset on the current
pub(crate) static this_cpu_off: u64;
/// DEFINE_PER_CPU(char[MAX_BPRINTF_BUF], rex_log_buf) = { 0 };
pub(crate) static mut rex_log_buf: [u8; MAX_BPRINTF_BUF as usize];
}
================================================
FILE: rex/src/kprobe/kprobe_impl.rs
================================================
use core::marker::PhantomData;
use crate::bindings::uapi::linux::bpf::bpf_map_type;
use crate::ffi;
use crate::pt_regs::PtRegs;
use crate::task_struct::TaskStruct;
#[repr(C)]
pub struct kprobe {
_placeholder: PhantomData<()>,
}
impl kprobe {
crate::base_helper::base_helper_defs!();
pub const unsafe fn new() -> kprobe {
Self {
_placeholder: PhantomData,
}
}
// Now returns a mutable ref, but since every reg is private the user prog
// cannot change reg contents. The user should not be able to directly
// assign this reference a new value either, given that they will not able
// to create another instance of pt_regs (private fields, no pub ctor)
pub unsafe fn convert_ctx(&self, ctx: *mut ()) -> &'static mut PtRegs {
// ctx has actual type *mut crate::bindings::linux::kernel::pt_regs
// therefore it is safe to just interpret it as a *mut pt_regs
// since the later is #[repr(transparent)] over the former
unsafe { &mut *(ctx as *mut PtRegs) }
}
#[cfg(CONFIG_BPF_KPROBE_OVERRIDE = "y")]
pub fn bpf_override_return(&self, regs: &mut PtRegs, rc: u64) -> i32 {
regs.regs.ax = rc;
regs.regs.ip = ffi::just_return_func as *const () as u64;
0
}
pub fn bpf_get_current_task(&self) -> Option<TaskStruct> {
TaskStruct::get_current_task()
}
}
================================================
FILE: rex/src/kprobe/mod.rs
================================================
mod kprobe_impl;
pub use kprobe_impl::*;
================================================
FILE: rex/src/lib.rs
================================================
#![no_std]
#![feature(
array_ptr_get,
auto_traits,
c_variadic,
core_intrinsics,
negative_impls
)]
#![allow(non_camel_case_types, internal_features)]
pub mod kprobe;
pub mod map;
pub mod perf_event;
pub mod pt_regs;
pub mod sched_cls;
pub mod spinlock;
pub mod task_struct;
pub mod tracepoint;
pub mod utils;
pub mod xdp;
mod base_helper;
mod bindings;
mod debug;
mod ffi;
mod log;
mod panic;
mod per_cpu;
mod random32;
extern crate paste;
pub use rex_macros::*;
#[cfg(not(CONFIG_KALLSYMS_ALL = "y"))]
compile_error!("CONFIG_KALLSYMS_ALL is required for rex");
pub use bindings::uapi::*;
pub use log::rex_trace_printk;
pub use utils::Result;
================================================
FILE: rex/src/log.rs
================================================
use core::fmt::{self, Write};
use crate::base_helper::termination_check;
use crate::bindings::uapi::linux::errno::E2BIG;
use crate::ffi;
use crate::per_cpu::this_cpu_ptr_mut;
/// An abstraction over the in-kernel per-cpu log buffer
/// This struct implements [`Write`], and therefore can be used for formatting
pub(crate) struct LogBuf {
buf: &'static mut [u8],
off: usize,
}
impl LogBuf {
/// Construct a new `LogBuf` from the kernel log buffer on the current CPU
pub(crate) fn new() -> Self {
let buf = unsafe {
&mut *this_cpu_ptr_mut(&raw mut ffi::rex_log_buf).as_mut_slice()
};
Self { buf, off: 0 }
}
/// Reset the offset of the buffer, i.e. discarding all contents not yet
/// sent to the kernel
#[inline(always)]
pub(crate) fn reset(&mut self) {
self.off = 0;
}
}
impl Write for LogBuf {
/// Writes a string slice into the kernel buffer on this CPU
fn write_str(&mut self, s: &str) -> fmt::Result {
let input_len = s.len();
let available = self.buf.len() - self.off - 1;
// Make sure we have enough space
if input_len > available {
return Err(fmt::Error);
}
// Copy and null-terminated the buf
let end = self.off + input_len;
self.buf[self.off..end].copy_from_slice(s.as_bytes());
self.buf[end] = 0;
// Update the write offset
self.off = end;
Ok(())
}
}
#[macro_export]
macro_rules! function_name {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
core::any::type_name::<T>()
}
let data = type_name_of(f);
&data[..data.len() - 3]
}};
}
/// Prints a message defined by `args` to the TraceFS file
/// `/sys/kernel/debug/tracing/trace`.
pub fn rex_trace_printk(args: fmt::Arguments<'_>) -> crate::Result {
// Format and write message to the per-cpu buf, then print it out
write!(&mut LogBuf::new(), "{}", args).map_err(|_| -(E2BIG as i32))?;
termination_check!(unsafe { ffi::rex_trace_printk() });
Ok(0)
}
/// `println`-style convenience macro for [`rex_trace_printk`].
/// Different from `println`, this macro produces the value of [`crate::Result`]
/// from [`rex_trace_printk`]
#[cfg(feature = "debug_printk")]
#[macro_export]
macro_rules! rex_printk {
($($arg:tt)*) => {{
$crate::rex_trace_printk(
format_args!(
"[{}] {}",
$crate::function_name!(),
format_args!($($arg)*)
),
)
}};
}
#[cfg(not(feature = "debug_printk"))]
#[macro_export]
macro_rules! rex_printk {
($($arg:tt)*) => {
$crate::Result::Ok(0)
};
}
================================================
FILE: rex/src/map.rs
================================================
use core::fmt::{self, Write};
use core::intrinsics::unlikely;
use core::marker::PhantomData;
use core::ops::{Deref, DerefMut};
use core::{mem, ptr, slice};
use crate::base_helper::{
bpf_map_delete_elem, bpf_map_lookup_elem, bpf_map_peek_elem,
bpf_map_pop_elem, bpf_map_push_elem, bpf_map_update_elem,
termination_check,
};
use crate::ffi;
use crate::linux::bpf::{
bpf_map_type, BPF_ANY, BPF_EXIST, BPF_MAP_TYPE_ARRAY, BPF_MAP_TYPE_HASH,
BPF_MAP_TYPE_PERCPU_ARRAY, BPF_MAP_TYPE_PERF_EVENT_ARRAY,
BPF_MAP_TYPE_QUEUE, BPF_MAP_TYPE_RINGBUF, BPF_MAP_TYPE_STACK,
BPF_MAP_TYPE_STACK_TRACE, BPF_NOEXIST, BPF_RB_AVAIL_DATA, BPF_RB_CONS_POS,
BPF_RB_PROD_POS, BPF_RB_RING_SIZE,
};
use crate::linux::errno::EINVAL;
use crate::utils::{
to_result, NoRef, PerfEventMaskedCPU, PerfEventStreamer, Result,
};
/// Rex equivalent to be used for map APIs in place of the `struct bpf_map`.
/// The key and the value type are encoded as generics types `K` and `V`.
/// The map type is encoded as a const-generic using the `bpf_map_type` enum.
#[repr(C)]
pub struct RexMapHandle<const MT: bpf_map_type, K, V>
where
V: Copy + NoRef,
{
// Map metadata
map_type: u32,
key_size: u32,
val_size: u32,
max_size: u32,
map_flag: u32,
// Actual kernel side map pointer
pub(crate) kptr: *mut (),
// Zero-sized marker
key_type: PhantomData<K>,
val_type: PhantomData<V>,
}
impl<const MT: bpf_map_type, K, V> RexMapHandle<MT, K, V>
where
V: Copy + NoRef,
{
pub const fn new(ms: u32, mf: u32) -> RexMapHandle<MT, K, V> {
Self {
map_type: MT,
key_size: mem::size_of::<K>() as u32,
val_size: mem::size_of::<V>() as u32,
max_size: ms,
map_flag: mf,
kptr: ptr::null_mut(),
key_type: PhantomData,
val_type: PhantomData,
}
}
}
unsafe impl<const MT: bpf_map_type, K, V> Sync for RexMapHandle<MT, K, V> where
V: Copy + NoRef
{
}
pub type RexStackTrace<K, V> = RexMapHandle<BPF_MAP_TYPE_STACK_TRACE, K, V>;
pub type RexPerCPUArrayMap<V> = RexMapHandle<BPF_MAP_TYPE_PERCPU_ARRAY, u32, V>;
pub type RexPerfEventArray<V> =
RexMapHandle<BPF_MAP_TYPE_PERF_EVENT_ARRAY, u32, V>;
pub type RexArrayMap<V> = RexMapHandle<BPF_MAP_TYPE_ARRAY, u32, V>;
pub type RexHashMap<K, V> = RexMapHandle<BPF_MAP_TYPE_HASH, K, V>;
pub type RexStack<V> = RexMapHandle<BPF_MAP_TYPE_STACK, (), V>;
pub type RexQueue<V> = RexMapHandle<BPF_MAP_TYPE_QUEUE, (), V>;
pub type RexRingBuf = RexMapHandle<BPF_MAP_TYPE_RINGBUF, (), ()>;
impl<'a, K, V> RexHashMap<K, V>
where
V: Copy + NoRef,
{
pub fn insert(&'static self, key: &K, value: &V) -> Result {
bpf_map_update_elem(self, key, value, BPF_ANY as u64)
}
pub fn insert_new(&'static self, key: &K, value: &V) -> Result {
bpf_map_update_elem(self, key, value, BPF_NOEXIST as u64)
}
pub fn update(&'static self, key: &K, value: &V) -> Result {
bpf_map_update_elem(self, key, value, BPF_EXIST as u64)
}
pub fn get_mut(&'static self, key: &'a K) -> Option<&'a mut V> {
bpf_map_lookup_elem(self, key)
}
pub fn delete(&'static self, key: &K) -> Result {
bpf_map_delete_elem(self, key)
}
}
impl<'a, V> RexArrayMap<V>
where
V: Copy + NoRef,
{
pub fn insert(&'static self, key: &u32, value: &V) -> Result {
bpf_map_update_elem(self, key, value, BPF_ANY as u64)
}
pub fn get_mut(&'static self, key: &'a u32) -> Option<&'a mut V> {
bpf_map_lookup_elem(self, key)
}
pub fn delete(&'static self, key: &u32) -> Result {
bpf_map_delete_elem(self, key)
}
}
impl<V> RexPerfEventArray<V>
where
V: Copy + NoRef,
{
pub fn output<P: PerfEventStreamer>(
&'static self,
program: &P,
ctx: &P::Context,
data: &V,
cpu: PerfEventMaskedCPU,
) -> Result {
program.output_event(ctx, self, data, cpu)
}
}
impl<V> RexStack<V>
where
V: Copy + NoRef,
{
pub fn push(&'static self, value: &V) -> Result {
bpf_map_push_elem(self, value, BPF_ANY as u64)
}
pub fn force_push(&'static self, value: &V) -> Result {
bpf_map_push_elem(self, value, BPF_EXIST as u64)
}
pub fn pop(&'static self) -> Option<V> {
bpf_map_pop_elem(self)
}
pub fn peek(&'static self) -> Option<V> {
bpf_map_peek_elem(self)
}
}
impl<V> RexQueue<V>
where
V: Copy + NoRef,
{
pub fn push(&'static self, value: &V) -> Result {
bpf_map_push_elem(self, value, BPF_ANY as u64)
}
pub fn force_push(&'static self, value: &V) -> Result {
bpf_map_push_elem(self, value, BPF_EXIST as u64)
}
pub fn pop(&'static self) -> Option<V> {
bpf_map_pop_elem(self)
}
pub fn peek(&'static self) -> Option<V> {
bpf_map_peek_elem(self)
}
}
impl RexRingBuf {
/// Reserves `size` bytes of payload in the ring buffer.
///
/// If the operation succeeds, A [`RexRingBufEntry`] representing the
/// payload is returned, otherwise (e.g., there is not enough memory
/// available), `None` is returned.
pub fn reserve<'a>(
&'static self,
size: usize,
) -> Option<RexRingBufEntry<'a>> {
let map_kptr = unsafe { core::ptr::read_volatile(&self.kptr) };
if unlikely(map_kptr.is_null()) {
return None;
}
let data = termination_check!(unsafe {
ffi::bpf_ringbuf_reserve(map_kptr, size as u64, 0)
});
if data.is_null() {
None
} else {
let data =
unsafe { slice::from_raw_parts_mut(data as *mut u8, size) };
Some(RexRingBufEntry { data, off: 0 })
}
}
/// Copies bytes from the `data` slice into the ring buffer.
///
/// If [`crate::linux::bpf::BPF_RB_NO_WAKEUP`] is specified in `flags`,
/// no notification of new data availability is sent.
/// If [`crate::linux::bpf::BPF_RB_FORCE_WAKEUP`] is specified in `flags`,
/// notification of new data availability is sent unconditionally.
/// If `0` is specified in `flags`, an adaptive notification of new data
/// availability is sent.
///
/// Returns a [`crate::Result`] on whether the operation is successful
pub fn output(&'static self, data: &[u8], flags: u64) -> crate::Result {
let map_kptr = unsafe { core::ptr::read_volatile(&self.kptr) };
if unlikely(map_kptr.is_null()) {
return Err(EINVAL as i32);
}
termination_check!(unsafe {
to_result!(ffi::bpf_ringbuf_output(
map_kptr,
data.as_ptr() as *const (),
data.len() as u64,
flags
))
})
}
/// Queries the amount of data not yet consumed.
///
/// Returns `None` is `self` is not a valid ring buffer.
pub fn available_bytes(&'static self) -> Option<u64> {
let map_kptr = unsafe { core::ptr::read_volatile(&self.kptr) };
if unlikely(map_kptr.is_null()) {
return None;
}
termination_check!(unsafe {
Some(ffi::bpf_ringbuf_query(map_kptr, BPF_RB_AVAIL_DATA as u64))
})
}
/// Queries the size of ring buffer.
///
/// Returns `None` is `self` is not a valid ring buffer.
pub fn size(&'static self) -> Option<u64> {
let map_kptr = unsafe { core::ptr::read_volatile(&self.kptr) };
if unlikely(map_kptr.is_null()) {
return None;
}
termination_check!(unsafe {
Some(ffi::bpf_ringbuf_query(map_kptr, BPF_RB_RING_SIZE as u64))
})
}
/// Queries the consumer position, which may wrap around.
///
/// Returns `None` is `self` is not a valid ring buffer.
pub fn consumer_position(&'static self) -> Option<u64> {
let map_kptr = unsafe { core::ptr::read_volatile(&self.kptr) };
if unlikely(map_kptr.is_null()) {
return None;
}
termination_check!(unsafe {
Some(ffi::bpf_ringbuf_query(map_kptr, BPF_RB_CONS_POS as u64))
})
}
/// Queries the Producer(s) position which may wrap around.
///
/// Returns `None` is `self` is not a valid ring buffer.
pub fn producer_position(&'static self) -> Option<u64> {
let map_kptr = unsafe { core::ptr::read_volatile(&self.kptr) };
if unlikely(map_kptr.is_null()) {
return None;
}
termination_check!(unsafe {
Some(ffi::bpf_ringbuf_query(map_kptr, BPF_RB_PROD_POS as u64))
})
}
}
pub struct RexRingBufEntry<'a> {
data: &'a mut [u8],
off: usize, // offset when the entry is used as a &str
}
impl RexRingBufEntry<'_> {
/// Consumes the reserved payload and submits it to the ring buffer.
///
/// If [`crate::linux::bpf::BPF_RB_NO_WAKEUP`] is specified in `flags`,
/// no notification of new data availability is sent.
/// If [`crate::linux::bpf::BPF_RB_FORCE_WAKEUP`] is specified in `flags`,
/// notification of new data availability is sent unconditionally.
/// If `0` is specified in `flags`, an adaptive notification of new data
/// availability is sent.
///
/// This method always succeeds.
pub fn submit(self, flags: u64) {
termination_check!(unsafe {
ffi::bpf_ringbuf_submit(self.data.as_mut_ptr() as *mut (), flags)
});
// Avoid calling ringbuf_discard twice
mem::forget(self);
}
/// Consumes the reserved payload and discards it.
///
/// If [`crate::linux::bpf::BPF_RB_NO_WAKEUP`] is specified in `flags`,
/// no notification of new data availability is sent.
/// If [`crate::linux::bpf::BPF_RB_FORCE_WAKEUP`] is specified in `flags`,
/// notification of new data availability is sent unconditionally.
/// If `0` is specified in `flags`, an adaptive notification of new data
/// availability is sent.
///
/// This method always succeeds.
pub fn discard(self, flags: u64) {
termination_check!(unsafe {
ffi::bpf_ringbuf_discard(self.data.as_mut_ptr() as *mut (), flags)
});
// Avoid calling ringbuf_discard twice
mem::forget(self);
}
}
impl Write for RexRingBufEntry<'_> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let input_len = s.len();
// Remaining capacity plus the space reserved for the null-terminated
let available = self.data.len().saturating_sub(self.off + 1);
// Make sure we have enough space
if input_len > available {
return Err(fmt::Error);
}
// Copy and null-terminated the buf
let end = self.off + input_len;
self.data[self.off..end].copy_from_slice(s.as_bytes());
self.data[end] = 0;
// Update the write offset
self.off = end;
Ok(())
}
}
impl Deref for RexRingBufEntry<'_> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.data
}
}
impl DerefMut for RexRingBufEntry<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.data
}
}
impl core::ops::Drop for RexRingBufEntry<'_> {
/// Discard reserved payload when dropped
fn drop(&mut self) {
termination_check!(unsafe {
ffi::bpf_ringbuf_discard(self.data.as_mut_ptr() as *mut (), 0)
});
}
}
================================================
FILE: rex/src/panic.rs
================================================
//! Implementation of various routines/frameworks related to EH/termination
use core::fmt::Write;
use core::panic::PanicInfo;
use crate::ffi;
use crate::log::LogBuf;
use crate::per_cpu::this_cpu_ptr_mut;
/// Needs to match the kernel side per-cpu definition
pub(crate) const ENTRIES_SIZE: usize = 64;
pub(crate) type CleanupFn = unsafe fn(*mut ()) -> ();
/// Aggregate to hold cleanup information of a specific object. The information
/// is used during panics to ensure proper cleanup of allocated kernel
/// resources. The `valid` field is used to mark whether this given entry holds
/// valid information. The cleanup will happen in the form of
/// `cleanup_fn(cleanup_arg)`
///
/// `#[repr(C)]` is needed because this struct is used from the kernel side.
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub(crate) struct CleanupEntry {
pub(crate) valid: u64,
pub(crate) cleanup_fn: Option<CleanupFn>,
pub(crate) cleanup_arg: *mut (),
}
impl CleanupEntry {
/// Create a new entry with valid function and argument
#[inline]
pub(crate) fn new(cleanup_fn: CleanupFn, cleanup_arg: *mut ()) -> Self {
Self {
valid: 1,
cleanup_fn: Some(cleanup_fn),
cleanup_arg,
}
}
/// Run cleanup function
#[inline]
pub(crate) unsafe fn cleanup(&self) {
if self.valid != 0 {
if let Some(cleanup_fn) = self.cleanup_fn {
unsafe {
(cleanup_fn)(self.cleanup_arg);
}
}
}
}
}
impl Default for CleanupEntry {
/// Create a default entry without valid function and argument
#[inline]
fn default() -> Self {
Self {
valid: 0,
cleanup_fn: None,
cleanup_arg: core::ptr::null_mut(),
}
}
}
/// Represents an array of `CleanupEntry` on a given CPU. The backing storage
/// is defined as a per-cpu array in the kernel.
pub(crate) struct CleanupEntries<'a> {
entries: &'a mut [CleanupEntry],
}
impl<'a> CleanupEntries<'a> {
/// Retrieve the array of `CleanupEntry` on the current CPU.
#[inline]
fn this_cpu_cleanup_entries() -> CleanupEntries<'a> {
let entries: &mut [CleanupEntry];
unsafe {
entries = &mut *this_cpu_ptr_mut(&raw mut ffi::rex_cleanup_entries)
.as_mut_slice();
}
Self { entries }
}
/// Finds the next empty entry in the array
///
/// Triggers a panic when the array is full. This is allowed because
/// `CleanupEntries::register_cleanup` is its only caller and is only
/// called by object constructors
#[inline]
fn find_next_emtpy_entry(&mut self) -> (usize, &mut CleanupEntry) {
for (idx, entry) in self.entries.iter_mut().enumerate() {
if entry.valid == 0 {
return (idx, entry);
}
}
panic!("Object count exceeded\n");
}
/// This function is (and must only be) called by object constructors
///
/// Panic is allowed here
pub(crate) fn register_cleanup(
cleanup_fn: CleanupFn,
cleanup_arg: *mut (),
) -> usize {
let mut entries = Self::this_cpu_cleanup_entries();
let (idx, entry) = entries.find_next_emtpy_entry();
*entry = CleanupEntry::new(cleanup_fn, cleanup_arg);
idx
}
/// This function is called by the object drop handler. It invalidates the
/// entry corresponding to the object.
pub(crate) fn deregister_cleanup(idx: usize) {
let entries = Self::this_cpu_cleanup_entries();
entries.entries[idx].valid = 0;
}
/// This function is called on panic to cleanup everything on the current
/// CPU. It **must** not cause another panic
pub(crate) unsafe fn cleanup_all() {
let entries = Self::this_cpu_cleanup_entries();
for entry in entries.entries.iter_mut() {
unsafe {
entry.cleanup();
}
entry.valid = 0;
}
}
}
// The best way to deal with this is probably insert it directly in LLVM IR as
// an inline asm block
// For now, use inline(always) to hint the compiler for inlining if LTO is on
#[allow(unused_attributes)]
#[unsafe(no_mangle)]
// Note: Rust warns that inline(always) is ignored on no_mangle functions,
// #[inline(always)] + #[no_mangle] under fat LTO generally means: inline
// internally where possible, but still keep the exported symbol
#[inline(always)]
unsafe fn __rex_check_stack() {
// The program can only use the top 4 pages of the stack, therefore subtract
// 0x4000
unsafe {
core::arch::asm!(
"mov {1:r}, gs:[{0:r}]",
"sub {1:r}, 0x4000",
"cmp rsp, {1:r}",
"ja 2f",
"call __rex_handle_stack_overflow",
"2:",
in(reg) &ffi::rex_stack_ptr as *const u64 as u64,
lateout(reg) _,
);
}
}
#[unsafe(no_mangle)]
pub(crate) unsafe fn __rex_handle_timeout() -> ! {
panic!("Timeout in Rex program");
}
#[unsafe(no_mangle)]
unsafe fn __rex_handle_stack_overflow() -> ! {
panic!("Stack overflow in Rex program");
}
// This function is called on panic.
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
// Set the termination flag
unsafe {
let termination_flag: *mut u8 = crate::per_cpu::this_cpu_ptr_mut(
&raw mut crate::ffi::rex_termination_state,
);
*termination_flag = 1;
};
unsafe { CleanupEntries::cleanup_all() };
let mut buf = LogBuf::new();
if write!(&mut buf, "{}", info).is_err() {
buf.reset();
// This string always fits in the buffer, so we ignore the Result
write!(&mut buf, "unknown rust panic").ok();
}
unsafe { ffi::rex_landingpad() }
}
================================================
FILE: rex/src/per_cpu.rs
================================================
use crate::ffi;
pub(crate) trait PerCPURead {
unsafe fn this_cpu_read(addr: *const Self) -> Self;
}
// rustc does not auto-detect subregs for us
macro_rules! reg_template {
(u64) => {
":r"
};
(i64) => {
":r"
};
(u32) => {
":e"
};
(i32) => {
":e"
};
(u16) => {
":x"
};
(i16) => {
":x"
};
}
macro_rules! impl_pcpu_read_integral {
($t:tt $($ts:tt)*) => {
impl PerCPURead for $t {
#[inline(always)]
unsafe fn this_cpu_read(addr: *const Self) -> Self {
let mut var: Self;
unsafe {
core::arch::asm!(
concat!("mov {0", reg_template!($t), "}, gs:[{1:r}]"),
lateout(reg) var,
in(reg) addr,
options(readonly, nostack),
);
}
var
}
}
impl_pcpu_read_integral!($($ts)*);
};
() => {};
}
macro_rules! impl_pcpu_read_byte {
($t:tt $($ts:tt)*) => {
impl PerCPURead for $t {
#[inline(always)]
unsafe fn this_cpu_read(addr: *const Self) -> Self {
let mut var: Self;
unsafe {
core::arch::asm!(
concat!("mov {0}, gs:[{1:r}]"),
lateout(reg_byte) var,
in(reg) addr,
options(readonly, nostack),
);
}
var
}
}
impl_pcpu_read_byte!($($ts)*);
};
() => {};
}
macro_rules! impl_pcpu_read_ptr {
($t:tt $($ts:tt)*) => {
impl<T> PerCPURead for *$t T {
#[inline(always)]
unsafe fn this_cpu_read(addr: *const Self) -> Self {
let mut var: Self;
unsafe {
core::arch::asm!(
concat!("mov {0:r}, gs:[{1:r}]"),
lateout(reg) var,
in(reg) addr,
options(readonly, nostack),
);
}
var
}
}
impl_pcpu_read_ptr!($($ts)*);
};
() => {};
}
impl_pcpu_read_integral!(u64 i64 u32 i32 u16 i16);
impl_pcpu_read_byte!(u8 i8);
// Mut: we have the CPU and assume no nesting
impl_pcpu_read_ptr!(const mut);
/// For values of per-cpu variables
#[inline(always)]
pub(crate) unsafe fn this_cpu_read<T: PerCPURead>(pcp_addr: *const T) -> T {
unsafe { T::this_cpu_read(pcp_addr) }
}
/// For addresses of per-cpu variables
/// This is more expensive (in terms of # of insns)
#[allow(dead_code)]
#[inline(always)]
pub unsafe fn this_cpu_ptr<T>(pcp_addr: *const T) -> *const T {
unsafe {
pcp_addr.byte_add(this_cpu_read(&raw const ffi::this_cpu_off) as usize)
}
}
#[inline(always)]
pub unsafe fn this_cpu_ptr_mut<T>(pcp_addr: *mut T) -> *mut T {
unsafe {
pcp_addr.byte_add(this_cpu_read(&raw const ffi::this_cpu_off) as usize)
}
}
================================================
FILE: rex/src/perf_event/mod.rs
================================================
mod perf_event_impl;
pub use perf_event_impl::*;
================================================
FILE: rex/src/perf_event/perf_event_impl.rs
================================================
use core::intrinsics::unlikely;
use core::marker::PhantomData;
use crate::base_helper::termination_check;
use crate::bindings::linux::kernel::bpf_perf_event_data_kern;
use crate::bindings::uapi::linux::bpf::{bpf_map_type, bpf_perf_event_value};
use crate::ffi;
use crate::linux::errno::EINVAL;
use crate::map::*;
use crate::pt_regs::PtRegs;
use crate::task_struct::TaskStruct;
use crate::utils::{to_result, NoRef, Result};
#[repr(transparent)]
pub struct bpf_perf_event_data {
kdata: bpf_perf_event_data_kern,
}
impl bpf_perf_event_data {
#[inline(always)]
pub fn regs(&self) -> &PtRegs {
unsafe { &*(self.kdata.regs as *const PtRegs) }
}
#[inline(always)]
pub fn sample_period(&self) -> u64 {
unsafe { (*self.kdata.data).period }
}
#[inline(always)]
pub fn addr(&self) -> u64 {
unsafe { (*self.kdata.data).addr }
}
}
#[repr(C)]
pub struct perf_event {
_placeholder: PhantomData<()>,
}
impl perf_event {
crate::base_helper::base_helper_defs!();
pub const unsafe fn new() -> perf_event {
Self {
_placeholder: PhantomData,
}
}
pub unsafe fn convert_ctx(
&self,
ctx: *mut (),
) -> &'static bpf_perf_event_data {
unsafe { &*(ctx as *mut bpf_perf_event_data) }
}
pub fn bpf_perf_prog_read_value(
&self,
ctx: &bpf_perf_event_data,
buf: &mut bpf_perf_event_value,
) -> Result {
let size = core::mem::size_of::<bpf_perf_event_value>() as u32;
let ctx_kptr = ctx as *const bpf_perf_event_data
as *const bpf_perf_event_data_kern;
termination_check!(unsafe {
to_result!(ffi::bpf_perf_prog_read_value(ctx_kptr, buf, size))
})
}
pub fn bpf_get_stackid_pe<K, V>(
&self,
ctx: &bpf_perf_event_data,
map: &'static RexStackTrace<K, V>,
flags: u64,
) -> Result
where
V: Copy + NoRef,
{
let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
if unlikely(map_kptr.is_null()) {
return Err(EINVAL as i32);
}
let ctx_kptr = ctx as *const bpf_perf_event_data
as *const bpf_perf_event_data_kern;
termination_check!(unsafe {
to_result!(ffi::bpf_get_stackid_pe(ctx_kptr, map_kptr, flags))
})
}
pub fn bpf_get_current_task(&self) -> Option<TaskStruct> {
TaskStruct::get_current_task()
}
}
================================================
FILE: rex/src/pt_regs.rs
================================================
use paste::paste;
use crate::bindings::linux::kernel::pt_regs;
/// Transparently wraps around the kernel-internal `struct pt_regs` and make the
/// fields read-only to prevent user-defined code from modifying the registers
#[repr(transparent)]
pub struct PtRegs {
pub(crate) regs: pt_regs,
}
macro_rules! decl_reg_accessors_1 {
($t:ident $($ts:ident)*) => {
#[inline(always)]
pub fn $t(&self) -> u64 {
self.regs.$t
}
decl_reg_accessors_1!($($ts)*);
};
() => {};
}
macro_rules! decl_reg_accessors_2 {
($t:ident $($ts:ident)*) => {
paste! {
#[inline(always)]
pub fn [<r $t>](&self) -> u64 {
self.regs.$t
}
}
decl_reg_accessors_2!($($ts)*);
};
() => {};
}
macro_rules! decl_reg_accessors {
($t1:ident $($ts1:ident)*, $t2:ident $($ts2:ident)*) => {
// regs that does not require special handling
decl_reg_accessors_1!($t1 $($ts1)*);
// regs that does not have the 'r' prefix in kernel pt_regs
decl_reg_accessors_2!($t2 $($ts2)*);
}
}
impl PtRegs {
decl_reg_accessors!(r15 r14 r13 r12 r11 r10 r9 r8,
bp bx ax cx dx si di ip sp);
// orig_rax cs eflags ss cannot be batch-processed by macros
#[inline(always)]
pub fn orig_rax(&self) -> u64 {
self.regs.orig_ax
}
#[inline(always)]
pub fn cs(&self) -> u64 {
unsafe { self.regs.__bindgen_anon_1.cs as u64 }
}
#[inline(always)]
pub fn eflags(&self) -> u64 {
self.regs.flags
}
#[inline(always)]
pub fn ss(&self) -> u64 {
unsafe { self.regs.__bindgen_anon_2.ss as u64 }
}
}
================================================
FILE: rex/src/random32.rs
================================================
use crate::ffi;
// fn get_cpu_var()
#[inline(always)]
pub(crate) fn bpf_user_rnd_u32() -> u32 {
// directly use get_random_u32
unsafe { ffi::get_random_u32() }
}
================================================
FILE: rex/src/sched_cls/mod.rs
================================================
// mod binding;
mod sched_cls_impl;
pub use sched_cls_impl::*;
================================================
FILE: rex/src/sched_cls/sched_cls_impl.rs
================================================
use core::ffi::{c_char, c_uchar};
use core::marker::PhantomData;
use core::{mem, slice};
use crate::base_helper::termination_check;
use crate::bindings::linux::kernel::{
ethhdr, iphdr, sk_buff, sock, tcphdr, udphdr,
};
use crate::bindings::uapi::linux::bpf::bpf_map_type;
pub use crate::bindings::uapi::linux::pkt_cls::{
TC_ACT_OK, TC_ACT_REDIRECT, TC_ACT_SHOT,
};
use crate::ffi;
use crate::utils::*;
pub struct __sk_buff<'a> {
pub data_slice: &'a mut [c_uchar],
kptr: &'static mut sk_buff,
}
// Define accessors of program-accessible fields
// TODO: may need to append more based on __sk_buff
impl<'a> __sk_buff<'a> {
#[inline(always)]
pub fn len(&self) -> u32 {
self.kptr.len
}
#[inline(always)]
pub fn data_len(&self) -> u32 {
self.kptr.data_len
}
#[inline(always)]
pub fn protocol(&self) -> u16be {
u16be(unsafe {
(self.kptr.__bindgen_anon_4.__bindgen_anon_1)
.as_ref()
.protocol
})
}
#[inline(always)]
pub fn priority(&self) -> u32 {
unsafe {
(self.kptr.__bindgen_anon_4.__bindgen_anon_1)
.as_ref()
.priority
}
}
#[inline(always)]
// TODO: may need to update based on __sk_buff
pub fn ingress_ifindex(&self) -> u32 {
0
}
#[inline(always)]
pub fn ifindex(&self) -> u32 {
unsafe {
(*self
.kptr
.__bindgen_anon_1
.__bindgen_anon_1
.__bindgen_anon_1
.dev)
.ifindex as u32
}
}
#[inline(always)]
pub fn hash(&self) -> u32 {
unsafe { (self.kptr.__bindgen_anon_4.__bindgen_anon_1).as_ref().hash }
}
#[inline(always)]
// TODO: may need to update based on __sk_buff
pub fn mark(&self) -> u32 {
0
}
#[inline(always)]
// TODO: may need to update based on __sk_buff
pub fn pkt_type(&self) -> u32 {
0
}
#[inline(always)]
// TODO: may need to update based on __sk_buff
pub fn queue_mapping(&self) -> u16 {
self.kptr.queue_mapping
}
#[inline(always)]
// TODO: may need to update based on __sk_buff
pub fn vlan_present(&self) -> u32 {
0
}
#[inline(always)]
// TODO: may need to update based on __sk_buff
pub fn vlan_tci(&self) -> u16 {
unsafe {
self.kptr
.__bindgen_anon_4
.__bindgen_anon_1
.as_ref()
.__bindgen_anon_2
.__bindgen_anon_1
.vlan_tci
}
}
#[inline(always)]
pub fn vlan_proto(&self) -> u16be {
u16be(unsafe {
self.kptr
.__bindgen_anon_4
.__bindgen_anon_1
.as_ref()
.__bindgen_anon_2
.__bindgen_anon_1
.vlan_proto
})
}
#[inline(always)]
pub fn cb(&self) -> [c_char; 20] {
let mut cb = [0; 20];
cb[0..20].clone_from_slice(&self.kptr.cb[0..20]);
cb
}
#[inline(always)]
// TODO: may need to update based on __sk_buff
pub fn tc_classid(&self) -> u32 {
0
}
#[inline(always)]
pub fn tc_index(&self) -> u16 {
unsafe {
(self.kptr.__bindgen_anon_4.__bindgen_anon_1)
.as_ref()
.tc_index
}
}
#[inline(always)]
// TODO: may need to update based on __sk_buff
pub fn napi_id(&self) -> u32 {
0
}
#[inline(always)]
// TODO: may need to update based on __sk_buff
pub fn data_meta(&self) -> u32 {
0
}
#[inline(always)]
pub fn sk(&self) -> &'a sock {
unsafe { &*self.kptr.sk }
}
}
#[repr(C)]
pub struct sched_cls {
_placeholder: PhantomData<()>,
}
impl sched_cls {
crate::base_helper::base_helper_defs!();
pub const unsafe fn new() -> sched_cls {
Self {
_placeholder: PhantomData,
}
}
// NOTE: copied from xdp impl, may change in the future
#[inline(always)]
pub fn eth_header<'b>(
&self,
skb: &'b mut __sk_buff,
) -> AlignedMut<'b, ethhdr> {
convert_slice_to_struct_mut::<ethhdr>(
&mut skb.data_slice[0..mem::size_of::<ethhdr>()],
)
}
#[inline(always)]
pub fn udp_header<'b>(
&self,
skb: &'b mut __sk_buff,
) -> AlignedMut<'b, udphdr> {
// NOTE: this assumes packet has ethhdr and iphdr
let begin = mem::size_of::<ethhdr>() + mem::size_of::<iphdr>();
let end = mem::size_of::<udphdr>() + begin;
convert_slice_to_struct_mut::<udphdr>(&mut skb.data_slice[begin..end])
}
#[inline(always)]
pub fn tcp_header<'b>(
&self,
skb: &'b mut __sk_buff,
) -> AlignedMut<'b, tcphdr> {
// NOTE: this assumes packet has ethhdr and iphdr
let begin = mem::size_of::<ethhdr>() + mem::size_of::<iphdr>();
let end = mem::size_of::<tcphdr>() + begin;
convert_slice_to_struct_mut::<tcphdr>(&mut skb.data_slice[begin..end])
}
#[inline(always)]
pub fn ip_header<'b>(
&self,
skb: &'b mut __sk_buff,
) -> AlignedMut<'b, iphdr> {
// NOTE: this assumes packet has ethhdr
let begin = mem::size_of::<ethhdr>();
let end = mem::size_of::<iphdr>() + begin;
convert_slice_to_struct_mut::<iphdr>(&mut skb.data_slice[begin..end])
}
#[inline(always)]
pub fn bpf_clone_redirect(
&self,
skb: &mut __sk_buff,
ifindex: u32,
flags: u64,
) -> Result {
let ret = termination_check!(unsafe {
ffi::bpf_clone_redirect(skb.kptr, ifindex, flags)
});
if ret != 0 {
return Err(ret);
}
// WARN: bpf_clone_redirect does not update skb.kptr?
skb.data_slice = unsafe {
slice::from_raw_parts_mut(
skb.kptr.data as *mut c_uchar,
skb.len() as usize,
)
};
Ok(0)
}
// Now returns a mutable ref, but since every reg is private the user prog
// cannot change reg contents. The user should not be able to directly
// assign this reference a new value either, given that they will not able
// to create another instance of pt_regs (private fields, no pub ctor)
#[inline(always)]
pub unsafe fn convert_ctx(&self, ctx: *mut ()) -> __sk_buff<'_> {
let kptr = unsafe { &mut *(ctx as *mut sk_buff) };
// NOTE: not support jumobo frame yet with non-linear sk_buff
let data_length = (kptr.len - kptr.data_len) as usize;
let data_slice = unsafe {
slice::from_raw_parts_mut(kptr.data as *mut c_uchar, data_length)
};
__sk_buff { data_slice, kptr }
}
}
================================================
FILE: rex/src/spinlock.rs
================================================
use crate::base_helper::termination_check;
pub use crate::bindings::uapi::linux::bpf::bpf_spin_lock;
use crate::ffi;
use crate::panic::CleanupEntries;
/// An RAII implementation of a "scoped lock" of a bpf spinlock. When this
/// structure is dropped (falls out of scope), the lock will be unlocked.
///
/// Ref: <https://doc.rust-lang.org/src/std/sync/mutex.rs.html#206-209>
#[must_use = "if unused the spinlock will immediately unlock"]
#[clippy::has_significant_drop]
pub struct rex_spinlock_guard<'a> {
lock: &'a mut bpf_spin_lock,
cleanup_idx: usize,
}
impl<'a> rex_spinlock_guard<'a> {
/// Constructor function that locks the spinlock
pub fn new(lock: &'a mut bpf_spin_lock) -> Self {
termination_check!({
// Put it before lock so if it panics we will not be holding the
// lock without a valid cleanup entry for it
let cleanup_idx = CleanupEntries::register_cleanup(
Self::panic_cleanup,
lock as *mut bpf_spin_lock as *mut (),
);
// Lock
unsafe { ffi::bpf_spin_lock(lock) };
Self { lock, cleanup_idx }
})
}
/// Function that unlocks the spinlock, used by cleanup list and drop
pub(crate) unsafe fn panic_cleanup(lock: *mut ()) {
unsafe {
ffi::bpf_spin_unlock(lock as *mut bpf_spin_lock);
}
}
}
impl Drop for rex_spinlock_guard<'_> {
/// Unlock the spinlock when the guard is out-of-scope
fn drop(&mut self) {
termination_check!({
// Put it before unlock so if it panics we will not unlock twice
// (once here in the drop handler, the other in the
// panic handler triggered by this function)
CleanupEntries::deregister_cleanup(self.cleanup_idx);
// Unlock
unsafe { ffi::bpf_spin_unlock(self.lock) };
})
}
}
/// Unimplement Send and Sync
/// Ref: <https://doc.rust-lang.org/nomicon/send-and-sync.html>
impl !Send for rex_spinlock_guard<'_> {}
impl !Sync for rex_spinlock_guard<'_> {}
================================================
FILE: rex/src/task_struct.rs
================================================
use core::ffi::{self as core_ffi, CStr};
use core::ops::Deref;
use crate::base_helper::termination_check;
use crate::bindings::linux::kernel::task_struct;
use crate::ffi;
use crate::panic::CleanupEntries;
use crate::per_cpu::this_cpu_read;
use crate::pt_regs::PtRegs;
// Bindgen has problem generating these constants
const TOP_OF_KERNEL_STACK_PADDING: u64 = 0;
const PAGE_SHIFT: u64 = 12;
const PAGE_SIZE: u64 = 1u64 << PAGE_SHIFT;
const THREAD_SIZE_ORDER: u64 = 2; // assume no kasan
const THREAD_SIZE: u64 = PAGE_SIZE << THREAD_SIZE_ORDER;
pub struct TaskStruct {
// struct task_struct * should always live longer than program execution
// given the RCU read lock
task: &'static task_struct,
kptr: *mut task_struct,
}
impl TaskStruct {
pub(crate) fn get_current_task() -> Option<Self> {
let current: *mut task_struct =
unsafe { this_cpu_read(&raw const ffi::current_task) };
if current.is_null() {
None
} else {
Some(TaskStruct {
task: unsafe { &*current },
kptr: current,
})
}
}
#[inline(always)]
pub fn get_pid(&self) -> i32 {
self.task.pid
}
#[inline(always)]
pub fn get_tgid(&self) -> i32 {
self.task.tgid
}
#[inline(always)]
pub fn get_utime(&self) -> u64 {
self.task.utime
}
// Design decision: the equivalent BPF helper writes the program name to
// a user-provided buffer, here we can take advantage of Rust's ownership by
// just providing a &CStr instead
pub fn get_comm(&self) -> Result<&CStr, core_ffi::FromBytesUntilNulError> {
// casting from c_char to u8 is sound, see:
// https://doc.rust-lang.org/src/core/ffi/c_str.rs.html#264
let comm_bytes =
unsafe { &*(&self.task.comm[..] as *const _ as *const [u8]) };
CStr::from_bytes_until_nul(comm_bytes)
}
pub fn get_pt_regs(&self) -> &'static PtRegs {
// X86 specific
// stack_top is actually bottom of the kernel stack, it refers to the
// highest address of the stack pages
let stack_top =
self.task.stack as u64 + THREAD_SIZE - TOP_OF_KERNEL_STACK_PADDING;
let reg_addr = stack_top - core::mem::size_of::<PtRegs>() as u64;
// The pt_regs should always be on the top of the stack
unsafe { &*(reg_addr as *const PtRegs) }
}
}
pub struct OwnedTaskStruct {
inner: TaskStruct,
cleanup_idx: usize,
}
impl OwnedTaskStruct {
pub fn from_pid(pid: i32) -> Option<Self> {
termination_check!({
let task = unsafe { ffi::bpf_task_from_pid(pid) };
if task.is_null() {
None
} else {
let cleanup_idx = CleanupEntries::register_cleanup(
Self::panic_cleanup,
task as *mut (),
);
Some(Self {
inner: TaskStruct {
task: unsafe { &*task },
kptr: task,
},
cleanup_idx,
})
}
})
}
pub(crate) unsafe fn panic_cleanup(task: *mut ()) {
unsafe {
ffi::bpf_task_release(task as *mut task_struct);
}
}
}
impl Deref for OwnedTaskStruct {
type Target = TaskStruct;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl Drop for OwnedTaskStruct {
fn drop(&mut self) {
termination_check!({
unsafe {
CleanupEntries::deregister_cleanup(self.cleanup_idx);
ffi::bpf_task_release(self.inner.kptr);
}
})
}
}
================================================
FILE: rex/src/tracepoint/binding.rs
================================================
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SyscallsEnterOpenCtx {
unused: u64,
pub syscall_nr: i64,
pub filename_ptr: i64,
pub flags: i64,
pub mode: i64,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SyscallsEnterOpenatCtx {
unused: u64,
pub syscall_nr: i64,
pub dfd: i64,
pub filename_ptr: i64,
pub flags: i64,
pub mode: i64,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SyscallsExitOpenCtx {
unused: u64,
pub syscall_nr: i64,
pub ret: i64,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SyscallsExitOpenatCtx {
unused: u64,
pub syscall_nr: i64,
pub ret: i64,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SyscallsEnterDupCtx {
unused: u64,
pub syscall_nr: i64,
pub fildes: u64,
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct RawSyscallsEnterCtx {
unused: u64,
pub id: i64,
pub args: [u64; 6],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct RawSyscallsExitCtx {
unused: u64,
pub id: i64,
pub ret: i64,
}
================================================
FILE: rex/src/tracepoint/mod.rs
================================================
mod binding;
mod tp_impl;
pub use binding::*;
pub use tp_impl::*;
================================================
FILE: rex/src/tracepoint/tp_impl.rs
================================================
use core::marker::PhantomData;
use super::{
RawSyscallsEnterCtx, RawSyscallsExitCtx, SyscallsEnterDupCtx,
SyscallsEnterOpenCtx, SyscallsEnterOpenatCtx, SyscallsExitOpenCtx,
SyscallsExitOpenatCtx,
};
use crate::base_helper::termination_check;
use crate::bindings::uapi::linux::bpf::bpf_map_type;
use crate::map::RexPerfEventArray;
use crate::task_struct::TaskStruct;
use crate::utils::sealed::PerfEventStreamerBase;
use crate::utils::{to_result, NoRef, PerfEventMaskedCPU, PerfEventStreamer};
use crate::{ffi, Result};
pub trait TracepointContext {}
impl TracepointContext for SyscallsEnterOpenCtx {}
impl TracepointContext for SyscallsEnterOpenatCtx {}
impl TracepointContext for SyscallsExitOpenCtx {}
impl TracepointContext for SyscallsExitOpenatCtx {}
impl TracepointContext for SyscallsEnterDupCtx {}
impl TracepointContext for RawSyscallsEnterCtx {}
impl TracepointContext for RawSyscallsExitCtx {}
#[repr(C)]
pub struct tracepoint<C: TracepointContext + 'static> {
_placeholder: PhantomData<C>,
}
impl<C: TracepointContext + 'static> tracepoint<C> {
crate::base_helper::base_helper_defs!();
pub const unsafe fn new() -> tracepoint<C> {
Self {
_placeholder: PhantomData,
}
}
pub unsafe fn convert_ctx(&self, ctx: *mut ()) -> &'static C {
unsafe { &*(ctx as *mut C) }
}
pub fn bpf_get_current_task(&self) -> Option<TaskStruct> {
TaskStruct::get_current_task()
}
}
impl<C: TracepointContext + 'static> PerfEventStreamerBase for tracepoint<C> {}
impl<C: TracepointContext + 'static> PerfEventStreamer for tracepoint<C> {
type Context = C;
fn output_event<T: Copy + NoRef>(
&self,
ctx: &Self::Context,
map: &'static RexPerfEventArray<T>,
data: &T,
cpu: PerfEventMaskedCPU,
) -> Result {
let map_kptr = unsafe { core::ptr::read_volatile(&map.kptr) };
let ctx_ptr = ctx as *const C as *const ();
termination_check!(unsafe {
to_result!(ffi::bpf_perf_event_output_tp(
ctx_ptr,
map_kptr,
cpu.masked_cpu,
data as *const T as *const (),
core::mem::size_of::<T>() as u64
))
})
}
}
================================================
FILE: rex/src/utils.rs
================================================
use core::ffi::{c_int, c_uchar};
use core::mem;
use core::ops::{Deref, DerefMut, Drop};
use crate::bindings::uapi::linux::bpf::{BPF_F_CURRENT_CPU, BPF_F_INDEX_MASK};
use crate::map::RexPerfEventArray;
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct u16be(pub(crate) u16);
impl From<u16be> for u16 {
// Required method
fn from(value: u16be) -> Self {
u16::from_be(value.0)
}
}
/// A specialized Result for typical int return value in the kernel
///
/// To be used as the return type for functions that may fail.
///
/// Ref: linux/rust/kernel/error.rs
pub type Result = core::result::Result<c_int, c_int>;
/// Converts an integer as returned by a C kernel function to an error if it's
/// negative, and `Ok(val)` otherwise.
///
/// Ref: linux/rust/kernel/error.rs
// genetic specialization to Macro
#[macro_export]
macro_rules! to_result {
($retval:expr) => {{
let val = $retval;
if val < 0 {
Err(val as i32)
} else {
Ok(val as i32)
}
}};
}
pub(crate) use to_result;
/// A marker trait that prevents derivation on types that contain references or
/// raw pointers. This avoids accidental dereference of invalid pointers in
/// foreign objects obtained from the kernel (e.g. via `bpf_map_lookup_elem` or
/// `bpf_probe_read_kernel`).
///
/// Though dererferencing raw pointers are not possible in Rex programs as it
/// requires `unsafe`, we still need to consider the case where a core library
/// type wraps the unsafe deref operation under a safe interface (an example is
/// `core::slice::Iter`).
pub unsafe auto trait NoRef {}
impl<T: ?Sized> !NoRef for &T {}
impl<T: ?Sized> !NoRef for &mut T {}
impl<T: ?Sized> !NoRef for *const T {}
impl<T: ?Sized> !NoRef for *mut T {}
/// An enum used internally by `Aligned` and `AlignedMut`, the encapsulated
/// value is either a reference to the data if the aligned requirement is
/// satisfied, or a copied value of the data if it is not aligned and a
/// reference cannot be taken directly.
enum AlignedInner<RefT, ValT> {
Ref(RefT),
Val(ValT),
}
/// An abstraction over a `&T` for both aligned and unaligned accesses. This
/// struct can be constructed with [`convert_slice_to_struct`] from an
/// underlying data slice. The underlying data is either a direct `&'a T` by
/// reborrowing the slice or a copied value of `T` from the slice, depending on
/// whether the slice pointer is properly aligned for `T`.
///
/// The abstraction provided by this str
gitextract_lgp5l1or/
├── .github/
│ ├── dependabot.yml
│ └── workflows/
│ ├── dco-check.yml
│ ├── memcached_benchmark.yml
│ ├── meson.yml
│ ├── rustfmt.yml
│ └── update-nix.yml
├── .gitignore
├── .gitmodules
├── Cargo.toml
├── LICENSE
├── README.md
├── docs/
│ ├── entry-insertion.md
│ ├── exception-handling.md
│ ├── getting-started.md
│ ├── kernel-symbols.md
│ ├── librex.md
│ └── rust_rex_subset.md
├── flake.nix
├── librex/
│ ├── .gitignore
│ ├── include/
│ │ └── librex.h
│ ├── lib/
│ │ ├── bindings.h
│ │ └── librex.cpp
│ └── meson.build
├── meson.build
├── rex/
│ ├── .cargo/
│ │ └── config.toml
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── build.py
│ ├── build.rs
│ ├── librexstub/
│ │ └── lib.c
│ ├── meson.build
│ └── src/
│ ├── base_helper.rs
│ ├── bindings/
│ │ ├── linux/
│ │ │ ├── kernel.rs
│ │ │ └── mod.rs
│ │ ├── mod.rs
│ │ └── uapi/
│ │ ├── linux/
│ │ │ ├── bpf.rs
│ │ │ ├── errno.rs
│ │ │ ├── in.rs
│ │ │ ├── mod.rs
│ │ │ ├── perf_event.rs
│ │ │ ├── pkt_cls.rs
│ │ │ ├── ptrace.rs
│ │ │ ├── seccomp.rs
│ │ │ └── unistd.rs
│ │ └── mod.rs
│ ├── debug.rs
│ ├── ffi.rs
│ ├── kprobe/
│ │ ├── kprobe_impl.rs
│ │ └── mod.rs
│ ├── lib.rs
│ ├── log.rs
│ ├── map.rs
│ ├── panic.rs
│ ├── per_cpu.rs
│ ├── perf_event/
│ │ ├── mod.rs
│ │ └── perf_event_impl.rs
│ ├── pt_regs.rs
│ ├── random32.rs
│ ├── sched_cls/
│ │ ├── mod.rs
│ │ └── sched_cls_impl.rs
│ ├── spinlock.rs
│ ├── task_struct.rs
│ ├── tracepoint/
│ │ ├── binding.rs
│ │ ├── mod.rs
│ │ └── tp_impl.rs
│ ├── utils.rs
│ └── xdp/
│ ├── mod.rs
│ └── xdp_impl.rs
├── rex-macros/
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src/
│ ├── args.rs
│ ├── kprobe.rs
│ ├── lib.rs
│ ├── perf_event.rs
│ ├── tc.rs
│ ├── tracepoint.rs
│ └── xdp.rs
├── rex-native.ini
├── rustfmt.toml
├── samples/
│ ├── .clang-format
│ ├── .gitignore
│ ├── atomic/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── bmc/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── entry.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── electrode/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── config.txt
│ │ ├── entry.c
│ │ ├── fast_common.h
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ ├── common.rs
│ │ ├── main.rs
│ │ └── maps.rs
│ ├── error_injector/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── src/
│ │ │ └── main.rs
│ │ ├── tests/
│ │ │ └── runtest.py
│ │ └── userapp.c
│ ├── hello/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── tests/
│ │ └── runtest.py
│ ├── map_bench/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── bench.sh
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── map_test/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── tests/
│ │ └── runtest.py
│ ├── map_test_2/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ ├── src/
│ │ │ └── main.rs
│ │ └── tests/
│ │ └── runtest.py
│ ├── meson.build
│ ├── recursive/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── bench.sh
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── spinlock_cleanup_benchmark/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── spinlock_test/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── startup_overhead_benchmark/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── .gitignore
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── event-trigger.c
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ ├── syscount/
│ │ ├── .cargo/
│ │ │ └── config.toml
│ │ ├── Cargo.toml
│ │ ├── clippy.toml
│ │ ├── loader.c
│ │ ├── meson.build
│ │ ├── rustfmt.toml
│ │ └── src/
│ │ └── main.rs
│ └── xdp_test/
│ ├── .cargo/
│ │ └── config.toml
│ ├── Cargo.toml
│ ├── README.md
│ ├── clippy.toml
│ ├── entry.c
│ ├── meson.build
│ ├── rustfmt.toml
│ ├── src/
│ │ └── main.rs
│ └── tests/
│ └── runtest.py
├── scripts/
│ ├── cargo-wrapper.pl
│ ├── q-script/
│ │ ├── .config
│ │ ├── nix-q
│ │ ├── sanity-test-q
│ │ └── yifei-q
│ └── sanity_tests/
│ └── run_tests.py
└── tools/
└── memcached_benchmark/
├── .cargo/
│ └── config.toml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── bench.py
├── flake.nix
├── rustfmt.toml
├── src/
│ ├── cli.rs
│ ├── dict.rs
│ ├── fs.rs
│ ├── get_values.rs
│ ├── main.rs
│ └── set_values.rs
└── tests/
└── benchmark_test.rs
SYMBOL INDEX (520 symbols across 84 files)
FILE: librex/include/librex.h
type bpf_object (line 4) | struct bpf_object
type rex_obj (line 5) | struct rex_obj
type rex_obj (line 13) | struct rex_obj
type bpf_object (line 16) | struct bpf_object
type rex_obj (line 17) | struct rex_obj
FILE: librex/lib/bindings.h
type elf_state (line 12) | struct elf_state {
type bpf_sec_def (line 33) | struct bpf_sec_def {
type bpf_object_state (line 45) | enum bpf_object_state {
type bpf_object (line 51) | struct bpf_object {
type bpf_light_subprog (line 130) | struct bpf_light_subprog
type bpf_program (line 136) | struct bpf_program {
type libbpf_map_type (line 210) | enum libbpf_map_type {
type bpf_map_def (line 218) | struct bpf_map_def {
type bpf_map (line 226) | struct bpf_map {
FILE: librex/lib/librex.cpp
function sec_def_matches (line 34) | bool sec_def_matches(const struct bpf_sec_def *sec_def,
function bpf_sec_def (line 64) | const bpf_sec_def *find_sec_def(std::string_view sec_name) {
function bpf (line 74) | inline long bpf(__u64 cmd, union bpf_attr *attr, unsigned int size) {
function align_up_16 (line 78) | inline uint64_t align_up_16(uint64_t val) {
type map_def (line 87) | struct map_def {
type rex_map (line 96) | struct rex_map {
method rex_map (line 103) | rex_map() = delete;
method rex_map (line 104) | rex_map(const Elf_Data *data, Elf64_Addr base, Elf64_Off off,
method rex_map (line 120) | rex_map(const rex_map &) = delete;
method rex_map (line 121) | rex_map(rex_map &&) = delete;
method rex_map (line 127) | rex_map &operator=(const rex_map &) = delete;
method rex_map (line 128) | rex_map &operator=(rex_map &&) = delete;
method create (line 130) | std::optional<int> create() {
method bpfmap (line 152) | std::optional<bpf_map> bpfmap() {
type rex_prog (line 176) | struct rex_prog {
type bpf_sec_def (line 180) | struct bpf_sec_def
method rex_prog (line 185) | rex_prog() = delete;
method rex_prog (line 186) | rex_prog(const char *nm, const char *scn_nm, Elf64_Off off)
method rex_prog (line 191) | rex_prog(const rex_prog &) = delete;
method rex_prog (line 197) | rex_prog(rex_prog &&other) noexcept
method rex_prog (line 210) | rex_prog &operator=(const rex_prog &) = delete;
method rex_prog (line 211) | rex_prog &operator=(rex_prog &&) = delete;
method bpf_prog (line 213) | std::optional<bpf_program> bpf_prog() {
type rex_obj (line 234) | struct rex_obj {
type elf_del (line 236) | struct elf_del {
type file_map_del (line 240) | struct file_map_del {
method file_map_del (line 243) | file_map_del() = default;
method file_map_del (line 244) | explicit file_map_del(size_t sz) : size(sz) {}
type bpf_obj_del (line 251) | struct bpf_obj_del {
method rex_obj (line 291) | rex_obj() = delete;
method rex_obj (line 293) | rex_obj(const rex_obj &) = delete;
method rex_obj (line 294) | rex_obj(rex_obj &&) = delete;
method rex_obj (line 297) | rex_obj &operator=(const rex_obj &) = delete;
method rex_obj (line 298) | rex_obj &operator=(rex_obj &&) = delete;
type stat (line 309) | struct stat
function bpf_object (line 749) | bpf_object *rex_obj::bpf_obj() {
function rex_set_debug (line 796) | [[gnu::visibility("default")]] void rex_set_debug(int val) { debug = val; }
function rex_obj (line 800) | [[nodiscard, gnu::visibility("default")]] rex_obj *
type elf_del (line 236) | struct elf_del {
type file_map_del (line 240) | struct file_map_del {
method file_map_del (line 243) | file_map_del() = default;
method file_map_del (line 244) | explicit file_map_del(size_t sz) : size(sz) {}
type bpf_obj_del (line 251) | struct bpf_obj_del {
method rex_obj (line 291) | rex_obj() = delete;
method rex_obj (line 293) | rex_obj(const rex_obj &) = delete;
method rex_obj (line 294) | rex_obj(rex_obj &&) = delete;
method rex_obj (line 297) | rex_obj &operator=(const rex_obj &) = delete;
method rex_obj (line 298) | rex_obj &operator=(rex_obj &&) = delete;
function bpf_object (line 827) | [[nodiscard, gnu::visibility("default")]] bpf_object *
FILE: rex-macros/src/args.rs
function parse_string_args (line 14) | pub(crate) fn parse_string_args(
FILE: rex-macros/src/kprobe.rs
type KprobeFlavor (line 10) | pub enum KprobeFlavor {
method fmt (line 18) | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
type KProbe (line 28) | pub(crate) struct KProbe {
method parse (line 35) | pub(crate) fn parse(
method expand (line 47) | pub(crate) fn expand(&self, flavor: KprobeFlavor) -> Result<TokenStrea...
FILE: rex-macros/src/lib.rs
function rex_xdp (line 23) | pub fn rex_xdp(attrs: TokenStream, item: TokenStream) -> TokenStream {
function rex_tc (line 35) | pub fn rex_tc(attrs: TokenStream, item: TokenStream) -> TokenStream {
function rex_kprobe (line 47) | pub fn rex_kprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
function rex_uprobe (line 59) | pub fn rex_uprobe(attrs: TokenStream, item: TokenStream) -> TokenStream {
function rex_tracepoint (line 71) | pub fn rex_tracepoint(attrs: TokenStream, item: TokenStream) -> TokenStr...
function rex_perf_event (line 83) | pub fn rex_perf_event(attrs: TokenStream, item: TokenStream) -> TokenStr...
function rex_map (line 95) | pub fn rex_map(_: TokenStream, item: TokenStream) -> TokenStream {
FILE: rex-macros/src/perf_event.rs
type PerfEvent (line 5) | pub(crate) struct PerfEvent {
method parse (line 11) | pub(crate) fn parse(
method expand (line 19) | pub(crate) fn expand(&self) -> Result<TokenStream> {
FILE: rex-macros/src/tc.rs
type SchedCls (line 5) | pub(crate) struct SchedCls {
method parse (line 11) | pub(crate) fn parse(_: TokenStream, item: TokenStream) -> Result<Sched...
method expand (line 23) | pub(crate) fn expand(&self) -> Result<TokenStream> {
FILE: rex-macros/src/tracepoint.rs
type TracePoint (line 6) | pub(crate) struct TracePoint {
method parse (line 12) | pub(crate) fn parse(
method expand (line 20) | pub(crate) fn expand(&self) -> Result<TokenStream> {
FILE: rex-macros/src/xdp.rs
type Xdp (line 5) | pub(crate) struct Xdp {
method parse (line 11) | pub(crate) fn parse(_: TokenStream, item: TokenStream) -> Result<Xdp> {
method expand (line 20) | pub(crate) fn expand(&self) -> Result<TokenStream> {
FILE: rex/build.py
function prep_uapi_headers (line 70) | def prep_uapi_headers(linux_path, headers, out_dir):
function parse_cargo_toml (line 82) | def parse_cargo_toml(cargo_toml_path):
function prep_kernel_headers (line 93) | def prep_kernel_headers(headers, linux_src, linux_obj, out_dir):
function parse_kconfigs (line 114) | def parse_kconfigs(dot_config_path, kconfigs):
function main (line 132) | def main(argv):
FILE: rex/build.rs
function main (line 9) | fn main() -> Result<()> {
FILE: rex/src/base_helper.rs
function bpf_get_smp_processor_id (line 44) | pub(crate) fn bpf_get_smp_processor_id() -> i32 {
function bpf_map_lookup_elem (line 48) | pub(crate) fn bpf_map_lookup_elem<'a, const MT: bpf_map_type, K, V>(
function bpf_map_update_elem (line 72) | pub(crate) fn bpf_map_update_elem<const MT: bpf_map_type, K, V>(
function bpf_map_delete_elem (line 96) | pub(crate) fn bpf_map_delete_elem<const MT: bpf_map_type, K, V>(
function bpf_map_push_elem (line 116) | pub(crate) fn bpf_map_push_elem<const MT: bpf_map_type, K, V>(
function bpf_map_pop_elem (line 138) | pub(crate) fn bpf_map_pop_elem<const MT: bpf_map_type, K, V>(
function bpf_map_peek_elem (line 160) | pub(crate) fn bpf_map_peek_elem<const MT: bpf_map_type, K, V>(
function bpf_probe_read_kernel (line 202) | pub(crate) fn bpf_probe_read_kernel<T>(
function bpf_jiffies64 (line 218) | pub(crate) fn bpf_jiffies64() -> u64 {
function bpf_get_numa_node_id (line 223) | pub(crate) fn bpf_get_numa_node_id() -> i32 {
function bpf_ktime_get_ns (line 239) | pub(crate) fn bpf_ktime_get_ns() -> u64 {
function bpf_ktime_get_boot_ns (line 243) | pub(crate) fn bpf_ktime_get_boot_ns() -> u64 {
function bpf_ktime_get_coarse_ns (line 247) | pub(crate) fn bpf_ktime_get_coarse_ns() -> u64 {
function bpf_get_prandom_u32 (line 265) | pub(crate) fn bpf_get_prandom_u32() -> u32 {
function bpf_snprintf (line 270) | pub(crate) fn bpf_snprintf<const N: usize, const M: usize>(
FILE: rex/src/debug.rs
function printk (line 7) | pub(crate) unsafe extern "C" fn printk(fmt: &str, mut ap: ...) -> c_int {
FILE: rex/src/ffi.rs
function bpf_map_lookup_elem (line 18) | pub(crate) fn bpf_map_lookup_elem(map: *mut (), key: *const ()) -> *mut ();
function bpf_map_update_elem (line 24) | pub(crate) fn bpf_map_update_elem(
function bpf_map_delete_elem (line 34) | pub(crate) fn bpf_map_delete_elem(map: *mut (), key: *const ()) -> i64;
function bpf_map_push_elem (line 40) | pub(crate) fn bpf_map_push_elem(
function bpf_map_pop_elem (line 49) | pub(crate) fn bpf_map_pop_elem(map: *mut (), value: *const ()) -> i64;
function bpf_map_peek_elem (line 54) | pub(crate) fn bpf_map_peek_elem(map: *mut (), value: *const ()) -> i64;
function bpf_probe_read_kernel (line 58) | pub(crate) fn bpf_probe_read_kernel(
function ktime_get_mono_fast_ns (line 65) | pub(crate) fn ktime_get_mono_fast_ns() -> u64;
function ktime_get_boot_fast_ns (line 68) | pub(crate) fn ktime_get_boot_fast_ns() -> u64;
function bpf_ktime_get_ns (line 71) | pub(crate) fn bpf_ktime_get_ns() -> u64;
function bpf_ktime_get_boot_ns (line 74) | pub(crate) fn bpf_ktime_get_boot_ns() -> u64;
function bpf_ktime_get_coarse_ns (line 77) | pub(crate) fn bpf_ktime_get_coarse_ns() -> u64;
function get_random_u32 (line 80) | pub(crate) fn get_random_u32() -> u32;
function bpf_snprintf (line 84) | pub(crate) fn bpf_snprintf(
function vprintk (line 93) | pub(crate) fn vprintk(fmt: *const c_uchar, args: VaList) -> i32;
function rex_landingpad (line 98) | pub(crate) fn rex_landingpad() -> !;
function bpf_spin_lock (line 101) | pub(crate) fn bpf_spin_lock(lock: *mut bpf_spin_lock) -> i64;
function bpf_spin_unlock (line 104) | pub(crate) fn bpf_spin_unlock(lock: *mut bpf_spin_lock) -> i64;
function just_return_func (line 107) | pub(crate) fn just_return_func();
function bpf_get_stackid_pe (line 117) | pub(crate) fn bpf_get_stackid_pe(
function bpf_perf_prog_read_value (line 129) | pub(crate) fn bpf_perf_prog_read_value(
function bpf_perf_event_output_tp (line 137) | pub(crate) fn bpf_perf_event_output_tp(
function bpf_perf_event_read_value (line 149) | pub(crate) fn bpf_perf_event_read_value(
function bpf_skb_event_output (line 161) | pub(crate) fn bpf_skb_event_output(
function bpf_xdp_event_output (line 174) | pub(crate) fn bpf_xdp_event_output(
function bpf_xdp_adjust_head (line 187) | pub(crate) fn bpf_xdp_adjust_head(xdp: *mut xdp_buff, offset: i32) -> i32;
function bpf_xdp_adjust_tail (line 194) | pub(crate) fn bpf_xdp_adjust_tail(xdp: *mut xdp_buff, offset: i32) -> i32;
function bpf_clone_redirect (line 201) | pub(crate) fn bpf_clone_redirect(
function bpf_ringbuf_reserve (line 208) | pub(crate) fn bpf_ringbuf_reserve(
function bpf_ringbuf_output (line 218) | pub(crate) fn bpf_ringbuf_output(
function bpf_ringbuf_submit (line 226) | pub(crate) fn bpf_ringbuf_submit(data: *mut (), flags: u64);
function bpf_ringbuf_discard (line 229) | pub(crate) fn bpf_ringbuf_discard(data: *mut (), flags: u64);
function bpf_ringbuf_query (line 232) | pub(crate) fn bpf_ringbuf_query(ringbuf: *mut (), flags: u64) -> u64;
function rex_trace_printk (line 235) | pub(crate) fn rex_trace_printk();
function bpf_task_from_pid (line 238) | pub(crate) fn bpf_task_from_pid(pid: i32) -> *mut task_struct;
function bpf_task_release (line 241) | pub(crate) fn bpf_task_release(task: *mut task_struct);
FILE: rex/src/kprobe/kprobe_impl.rs
type kprobe (line 9) | pub struct kprobe {
method new (line 16) | pub const unsafe fn new() -> kprobe {
method convert_ctx (line 26) | pub unsafe fn convert_ctx(&self, ctx: *mut ()) -> &'static mut PtRegs {
method bpf_override_return (line 34) | pub fn bpf_override_return(&self, regs: &mut PtRegs, rc: u64) -> i32 {
method bpf_get_current_task (line 40) | pub fn bpf_get_current_task(&self) -> Option<TaskStruct> {
FILE: rex/src/log.rs
type LogBuf (line 10) | pub(crate) struct LogBuf {
method new (line 17) | pub(crate) fn new() -> Self {
method reset (line 27) | pub(crate) fn reset(&mut self) {
method write_str (line 34) | fn write_str(&mut self, s: &str) -> fmt::Result {
function rex_trace_printk (line 69) | pub fn rex_trace_printk(args: fmt::Arguments<'_>) -> crate::Result {
FILE: rex/src/map.rs
type RexMapHandle (line 29) | pub struct RexMapHandle<const MT: bpf_map_type, K, V>
function new (line 52) | pub const fn new(ms: u32, mf: u32) -> RexMapHandle<MT, K, V> {
type RexStackTrace (line 71) | pub type RexStackTrace<K, V> = RexMapHandle<BPF_MAP_TYPE_STACK_TRACE, K,...
type RexPerCPUArrayMap (line 72) | pub type RexPerCPUArrayMap<V> = RexMapHandle<BPF_MAP_TYPE_PERCPU_ARRAY, ...
type RexPerfEventArray (line 73) | pub type RexPerfEventArray<V> =
type RexArrayMap (line 75) | pub type RexArrayMap<V> = RexMapHandle<BPF_MAP_TYPE_ARRAY, u32, V>;
type RexHashMap (line 76) | pub type RexHashMap<K, V> = RexMapHandle<BPF_MAP_TYPE_HASH, K, V>;
type RexStack (line 77) | pub type RexStack<V> = RexMapHandle<BPF_MAP_TYPE_STACK, (), V>;
type RexQueue (line 78) | pub type RexQueue<V> = RexMapHandle<BPF_MAP_TYPE_QUEUE, (), V>;
type RexRingBuf (line 79) | pub type RexRingBuf = RexMapHandle<BPF_MAP_TYPE_RINGBUF, (), ()>;
method reserve (line 186) | pub fn reserve<'a>(
method output (line 218) | pub fn output(&'static self, data: &[u8], flags: u64) -> crate::Result {
method available_bytes (line 237) | pub fn available_bytes(&'static self) -> Option<u64> {
method size (line 251) | pub fn size(&'static self) -> Option<u64> {
method consumer_position (line 265) | pub fn consumer_position(&'static self) -> Option<u64> {
method producer_position (line 279) | pub fn producer_position(&'static self) -> Option<u64> {
function insert (line 85) | pub fn insert(&'static self, key: &K, value: &V) -> Result {
function insert_new (line 89) | pub fn insert_new(&'static self, key: &K, value: &V) -> Result {
function update (line 93) | pub fn update(&'static self, key: &K, value: &V) -> Result {
function get_mut (line 97) | pub fn get_mut(&'static self, key: &'a K) -> Option<&'a mut V> {
function delete (line 101) | pub fn delete(&'static self, key: &K) -> Result {
function insert (line 110) | pub fn insert(&'static self, key: &u32, value: &V) -> Result {
function get_mut (line 114) | pub fn get_mut(&'static self, key: &'a u32) -> Option<&'a mut V> {
function delete (line 118) | pub fn delete(&'static self, key: &u32) -> Result {
function output (line 127) | pub fn output<P: PerfEventStreamer>(
function push (line 142) | pub fn push(&'static self, value: &V) -> Result {
function force_push (line 146) | pub fn force_push(&'static self, value: &V) -> Result {
function pop (line 150) | pub fn pop(&'static self) -> Option<V> {
function peek (line 154) | pub fn peek(&'static self) -> Option<V> {
function push (line 163) | pub fn push(&'static self, value: &V) -> Result {
function force_push (line 167) | pub fn force_push(&'static self, value: &V) -> Result {
function pop (line 171) | pub fn pop(&'static self) -> Option<V> {
function peek (line 175) | pub fn peek(&'static self) -> Option<V> {
type RexRingBufEntry (line 291) | pub struct RexRingBufEntry<'a> {
function submit (line 307) | pub fn submit(self, flags: u64) {
function discard (line 325) | pub fn discard(self, flags: u64) {
method write_str (line 335) | fn write_str(&mut self, s: &str) -> fmt::Result {
type Target (line 359) | type Target = [u8];
method deref (line 361) | fn deref(&self) -> &Self::Target {
method deref_mut (line 367) | fn deref_mut(&mut self) -> &mut Self::Target {
function drop (line 374) | fn drop(&mut self) {
FILE: rex/src/panic.rs
constant ENTRIES_SIZE (line 11) | pub(crate) const ENTRIES_SIZE: usize = 64;
type CleanupFn (line 13) | pub(crate) type CleanupFn = unsafe fn(*mut ()) -> ();
type CleanupEntry (line 24) | pub(crate) struct CleanupEntry {
method new (line 33) | pub(crate) fn new(cleanup_fn: CleanupFn, cleanup_arg: *mut ()) -> Self {
method cleanup (line 43) | pub(crate) unsafe fn cleanup(&self) {
method default (line 57) | fn default() -> Self {
type CleanupEntries (line 68) | pub(crate) struct CleanupEntries<'a> {
function this_cpu_cleanup_entries (line 75) | fn this_cpu_cleanup_entries() -> CleanupEntries<'a> {
function find_next_emtpy_entry (line 90) | fn find_next_emtpy_entry(&mut self) -> (usize, &mut CleanupEntry) {
function register_cleanup (line 102) | pub(crate) fn register_cleanup(
function deregister_cleanup (line 114) | pub(crate) fn deregister_cleanup(idx: usize) {
function cleanup_all (line 121) | pub(crate) unsafe fn cleanup_all() {
function __rex_check_stack (line 141) | unsafe fn __rex_check_stack() {
function __rex_handle_timeout (line 159) | pub(crate) unsafe fn __rex_handle_timeout() -> ! {
function __rex_handle_stack_overflow (line 164) | unsafe fn __rex_handle_stack_overflow() -> ! {
function panic (line 170) | fn panic(info: &PanicInfo) -> ! {
FILE: rex/src/per_cpu.rs
type PerCPURead (line 3) | pub(crate) trait PerCPURead {
method this_cpu_read (line 4) | unsafe fn this_cpu_read(addr: *const Self) -> Self;
function this_cpu_read (line 102) | pub(crate) unsafe fn this_cpu_read<T: PerCPURead>(pcp_addr: *const T) ->...
function this_cpu_ptr (line 110) | pub unsafe fn this_cpu_ptr<T>(pcp_addr: *const T) -> *const T {
function this_cpu_ptr_mut (line 117) | pub unsafe fn this_cpu_ptr_mut<T>(pcp_addr: *mut T) -> *mut T {
FILE: rex/src/perf_event/perf_event_impl.rs
type bpf_perf_event_data (line 15) | pub struct bpf_perf_event_data {
method regs (line 21) | pub fn regs(&self) -> &PtRegs {
method sample_period (line 26) | pub fn sample_period(&self) -> u64 {
method addr (line 31) | pub fn addr(&self) -> u64 {
type perf_event (line 37) | pub struct perf_event {
method new (line 44) | pub const unsafe fn new() -> perf_event {
method convert_ctx (line 50) | pub unsafe fn convert_ctx(
method bpf_perf_prog_read_value (line 57) | pub fn bpf_perf_prog_read_value(
method bpf_get_stackid_pe (line 71) | pub fn bpf_get_stackid_pe<K, V>(
method bpf_get_current_task (line 93) | pub fn bpf_get_current_task(&self) -> Option<TaskStruct> {
FILE: rex/src/pt_regs.rs
type PtRegs (line 8) | pub struct PtRegs {
method orig_rax (line 51) | pub fn orig_rax(&self) -> u64 {
method cs (line 56) | pub fn cs(&self) -> u64 {
method eflags (line 61) | pub fn eflags(&self) -> u64 {
method ss (line 66) | pub fn ss(&self) -> u64 {
FILE: rex/src/random32.rs
function bpf_user_rnd_u32 (line 5) | pub(crate) fn bpf_user_rnd_u32() -> u32 {
FILE: rex/src/sched_cls/sched_cls_impl.rs
type __sk_buff (line 16) | pub struct __sk_buff<'a> {
function len (line 25) | pub fn len(&self) -> u32 {
function data_len (line 30) | pub fn data_len(&self) -> u32 {
function protocol (line 35) | pub fn protocol(&self) -> u16be {
function priority (line 44) | pub fn priority(&self) -> u32 {
function ingress_ifindex (line 54) | pub fn ingress_ifindex(&self) -> u32 {
function ifindex (line 59) | pub fn ifindex(&self) -> u32 {
function hash (line 72) | pub fn hash(&self) -> u32 {
function mark (line 78) | pub fn mark(&self) -> u32 {
function pkt_type (line 84) | pub fn pkt_type(&self) -> u32 {
function queue_mapping (line 90) | pub fn queue_mapping(&self) -> u16 {
function vlan_present (line 96) | pub fn vlan_present(&self) -> u32 {
function vlan_tci (line 102) | pub fn vlan_tci(&self) -> u16 {
function vlan_proto (line 115) | pub fn vlan_proto(&self) -> u16be {
function cb (line 128) | pub fn cb(&self) -> [c_char; 20] {
function tc_classid (line 136) | pub fn tc_classid(&self) -> u32 {
function tc_index (line 141) | pub fn tc_index(&self) -> u16 {
function napi_id (line 151) | pub fn napi_id(&self) -> u32 {
function data_meta (line 157) | pub fn data_meta(&self) -> u32 {
function sk (line 162) | pub fn sk(&self) -> &'a sock {
type sched_cls (line 168) | pub struct sched_cls {
method new (line 175) | pub const unsafe fn new() -> sched_cls {
method eth_header (line 183) | pub fn eth_header<'b>(
method udp_header (line 193) | pub fn udp_header<'b>(
method tcp_header (line 205) | pub fn tcp_header<'b>(
method ip_header (line 217) | pub fn ip_header<'b>(
method bpf_clone_redirect (line 229) | pub fn bpf_clone_redirect(
method convert_ctx (line 259) | pub unsafe fn convert_ctx(&self, ctx: *mut ()) -> __sk_buff<'_> {
FILE: rex/src/spinlock.rs
type rex_spinlock_guard (line 12) | pub struct rex_spinlock_guard<'a> {
function new (line 19) | pub fn new(lock: &'a mut bpf_spin_lock) -> Self {
function panic_cleanup (line 36) | pub(crate) unsafe fn panic_cleanup(lock: *mut ()) {
method drop (line 45) | fn drop(&mut self) {
FILE: rex/src/task_struct.rs
constant TOP_OF_KERNEL_STACK_PADDING (line 12) | const TOP_OF_KERNEL_STACK_PADDING: u64 = 0;
constant PAGE_SHIFT (line 13) | const PAGE_SHIFT: u64 = 12;
constant PAGE_SIZE (line 14) | const PAGE_SIZE: u64 = 1u64 << PAGE_SHIFT;
constant THREAD_SIZE_ORDER (line 15) | const THREAD_SIZE_ORDER: u64 = 2;
constant THREAD_SIZE (line 16) | const THREAD_SIZE: u64 = PAGE_SIZE << THREAD_SIZE_ORDER;
type TaskStruct (line 18) | pub struct TaskStruct {
method get_current_task (line 26) | pub(crate) fn get_current_task() -> Option<Self> {
method get_pid (line 41) | pub fn get_pid(&self) -> i32 {
method get_tgid (line 46) | pub fn get_tgid(&self) -> i32 {
method get_utime (line 51) | pub fn get_utime(&self) -> u64 {
method get_comm (line 58) | pub fn get_comm(&self) -> Result<&CStr, core_ffi::FromBytesUntilNulErr...
method get_pt_regs (line 66) | pub fn get_pt_regs(&self) -> &'static PtRegs {
type OwnedTaskStruct (line 78) | pub struct OwnedTaskStruct {
method from_pid (line 84) | pub fn from_pid(pid: i32) -> Option<Self> {
method panic_cleanup (line 107) | pub(crate) unsafe fn panic_cleanup(task: *mut ()) {
type Target (line 115) | type Target = TaskStruct;
method deref (line 117) | fn deref(&self) -> &Self::Target {
method drop (line 123) | fn drop(&mut self) {
FILE: rex/src/tracepoint/binding.rs
type SyscallsEnterOpenCtx (line 3) | pub struct SyscallsEnterOpenCtx {
type SyscallsEnterOpenatCtx (line 13) | pub struct SyscallsEnterOpenatCtx {
type SyscallsExitOpenCtx (line 24) | pub struct SyscallsExitOpenCtx {
type SyscallsExitOpenatCtx (line 32) | pub struct SyscallsExitOpenatCtx {
type SyscallsEnterDupCtx (line 40) | pub struct SyscallsEnterDupCtx {
type RawSyscallsEnterCtx (line 48) | pub struct RawSyscallsEnterCtx {
type RawSyscallsExitCtx (line 56) | pub struct RawSyscallsExitCtx {
FILE: rex/src/tracepoint/tp_impl.rs
type TracepointContext (line 16) | pub trait TracepointContext {}
type tracepoint (line 26) | pub struct tracepoint<C: TracepointContext + 'static> {
function new (line 33) | pub const unsafe fn new() -> tracepoint<C> {
function convert_ctx (line 39) | pub unsafe fn convert_ctx(&self, ctx: *mut ()) -> &'static C {
function bpf_get_current_task (line 43) | pub fn bpf_get_current_task(&self) -> Option<TaskStruct> {
type Context (line 51) | type Context = C;
method output_event (line 53) | fn output_event<T: Copy + NoRef>(
FILE: rex/src/utils.rs
type u16be (line 10) | pub struct u16be(pub(crate) u16);
function from (line 14) | fn from(value: u16be) -> Self {
type Result (line 24) | pub type Result = core::result::Result<c_int, c_int>;
type NoRef (line 53) | pub unsafe auto trait NoRef {}
type AlignedInner (line 63) | enum AlignedInner<RefT, ValT> {
type Aligned (line 76) | pub struct Aligned<'a, T> {
function from_ref (line 83) | pub(crate) const fn from_ref(aligned_ref: &'a T) -> Self {
function from_val (line 92) | pub(crate) const fn from_val(copied_val: T) -> Self {
type Target (line 102) | type Target = T;
method deref (line 108) | fn deref(&self) -> &Self::Target {
type AlignedMut (line 125) | pub struct AlignedMut<'a, T: Copy> {
function from_ref (line 132) | pub(crate) const fn from_ref(aligned_ref: &'a mut T) -> Self {
function from_val (line 141) | pub(crate) const fn from_val(
type Target (line 154) | type Target = T;
method deref (line 160) | fn deref(&self) -> &Self::Target {
method deref_mut (line 175) | fn deref_mut(&mut self) -> &mut Self::Target {
method drop (line 186) | fn drop(&mut self) {
function convert_slice_to_struct (line 211) | pub fn convert_slice_to_struct<T>(slice: &[c_uchar]) -> Aligned<'_, T>
function convert_slice_to_struct_mut (line 245) | pub fn convert_slice_to_struct_mut<T>(
type PerfEventStreamerBase (line 294) | pub trait PerfEventStreamerBase {}
type PerfEventStreamer (line 301) | pub trait PerfEventStreamer: sealed::PerfEventStreamerBase {
method output_event (line 303) | fn output_event<T: Copy + NoRef>(
type PerfEventMaskedCPU (line 316) | pub struct PerfEventMaskedCPU {
method current_cpu (line 321) | pub fn current_cpu() -> Self {
method any_cpu (line 327) | pub fn any_cpu(cpu: u64) -> Self {
FILE: rex/src/xdp/xdp_impl.rs
method saddr (line 21) | pub fn saddr(&mut self) -> &mut u32 {
method daddr (line 25) | pub fn daddr(&mut self) -> &mut u32 {
function compute_ip_checksum (line 31) | pub fn compute_ip_checksum(ip_header: &mut iphdr) -> u16 {
type xdp_md (line 49) | pub struct xdp_md<'a> {
function data_length (line 57) | pub fn data_length(&self) -> usize {
function data_meta (line 62) | pub fn data_meta(&self) -> usize {
function ingress_ifindex (line 67) | pub fn ingress_ifindex(&self) -> u32 {
function rx_qeueu_index (line 72) | pub fn rx_qeueu_index(&self) -> u32 {
function egress_ifindex (line 77) | pub fn egress_ifindex(&self) -> u32 {
type xdp (line 85) | pub struct xdp {
method new (line 92) | pub const unsafe fn new() -> xdp {
method convert_ctx (line 103) | pub unsafe fn convert_ctx(&self, ctx: *mut ()) -> xdp_md<'_> {
method tcp_header (line 117) | pub fn tcp_header<'b>(
method udp_header (line 129) | pub fn udp_header<'b>(
method ip_header (line 141) | pub fn ip_header<'b>(&self, ctx: &'b mut xdp_md) -> AlignedMut<'b, iph...
method eth_header (line 150) | pub fn eth_header<'b>(
method bpf_xdp_adjust_tail (line 166) | pub fn bpf_xdp_adjust_tail(&self, ctx: &mut xdp_md, offset: i32) -> Re...
FILE: samples/atomic/event-trigger.c
function main (line 7) | int main(int argc, char *argv[])
FILE: samples/atomic/loader.c
function main (line 16) | int main(void)
FILE: samples/atomic/src/main.rs
function rex_prog1 (line 14) | fn rex_prog1(obj: &kprobe, _ctx: &mut PtRegs) -> Result {
FILE: samples/bmc/entry.c
type bpf_progs_desc (line 26) | struct bpf_progs_desc {
type bmc_stats (line 34) | struct bmc_stats {
function libbpf_print_fn (line 48) | static int libbpf_print_fn(enum libbpf_print_level level, const char *fo...
function write_stats_to_file (line 57) | int write_stats_to_file(char *filename, int map_fd)
function main (line 102) | int main(int argc, char *argv[])
FILE: samples/bmc/src/main.rs
constant BMC_MAX_PACKET_LENGTH (line 14) | const BMC_MAX_PACKET_LENGTH: usize = 1500;
constant BMC_CACHE_ENTRY_COUNT (line 15) | const BMC_CACHE_ENTRY_COUNT: u32 = 3250000;
constant BMC_MAX_KEY_LENGTH (line 16) | const BMC_MAX_KEY_LENGTH: usize = 230;
constant BMC_MAX_VAL_LENGTH (line 17) | const BMC_MAX_VAL_LENGTH: usize = 1000;
constant BMC_MAX_ADDITIONAL_PAYLOAD_BYTES (line 18) | const BMC_MAX_ADDITIONAL_PAYLOAD_BYTES: usize = 53;
constant BMC_MAX_CACHE_DATA_SIZE (line 19) | const BMC_MAX_CACHE_DATA_SIZE: usize =
constant BMC_MAX_KEY_IN_PACKET (line 21) | const BMC_MAX_KEY_IN_PACKET: u32 = 30;
constant FNV_OFFSET_BASIS_32 (line 23) | const FNV_OFFSET_BASIS_32: u32 = 2166136261;
constant FNV_PRIME_32 (line 24) | const FNV_PRIME_32: u32 = 16777619;
constant ETH_ALEN (line 25) | const ETH_ALEN: usize = 6;
constant MEMCACHED_PORT (line 26) | const MEMCACHED_PORT: u16 = 11211;
type memcached_udp_header (line 29) | pub struct memcached_udp_header {
type bmc_cache_entry (line 38) | pub struct bmc_cache_entry {
type memcached_key (line 48) | struct memcached_key {
type bmc_stats (line 56) | pub struct bmc_stats {
type parsing_context (line 68) | struct parsing_context {
function hash_key (line 102) | fn hash_key(
function write_pkt_reply (line 189) | fn write_pkt_reply(
function bmc_invalidate_cache (line 278) | fn bmc_invalidate_cache(obj: &xdp, ctx: &mut xdp_md) -> Result {
function xdp_rx_filter (line 332) | fn xdp_rx_filter(obj: &xdp, ctx: &mut xdp_md) -> Result {
function bmc_update_cache (line 395) | fn bmc_update_cache(
function xdp_tx_filter (line 469) | fn xdp_tx_filter(obj: &sched_cls, skb: &mut __sk_buff) -> Result {
FILE: samples/electrode/entry.c
type bpf_progs_desc (line 25) | struct bpf_progs_desc {
type bpf_object (line 33) | struct bpf_object
type bpf_object_load_attr (line 34) | struct bpf_object_load_attr
type bpf_program (line 35) | struct bpf_program
type rlimit (line 44) | struct rlimit
type bpf_progs_desc (line 52) | struct bpf_progs_desc
function libbpf_print_fn (line 57) | static int libbpf_print_fn(enum libbpf_print_level level, const char *fo...
function read_config (line 66) | static void read_config(void)
function create_object (line 121) | static void create_object(void)
function add_interrupt (line 146) | static void add_interrupt(void)
function main (line 188) | int main(int argc, char *argv[])
FILE: samples/electrode/fast_common.h
type ReplicaStatus (line 33) | enum ReplicaStatus { STATUS_NORMAL, STATUS_VIEW_CHANGE, STATUS_RECOVERING }
type paxos_configure (line 51) | struct paxos_configure {
FILE: samples/electrode/src/common.rs
constant ETH_ALEN (line 14) | pub(crate) const ETH_ALEN: usize = 6;
constant MTU (line 15) | pub(crate) const MTU: u64 = 1500;
constant MAX_DATA_LEN (line 16) | pub(crate) const MAX_DATA_LEN: usize = 64;
constant CLUSTER_SIZE (line 18) | pub(crate) const CLUSTER_SIZE: u8 = 3;
constant FAST_REPLICA_MAX (line 19) | pub(crate) const FAST_REPLICA_MAX: u32 = 100;
constant NONFRAG_MAGIC (line 20) | pub(crate) const NONFRAG_MAGIC: u32 = 0x20050318;
constant FRAG_MAGIC (line 21) | pub(crate) const FRAG_MAGIC: u32 = 0x20101010;
constant MAGIC_LEN (line 23) | pub(crate) const MAGIC_LEN: usize = 4;
constant REQUEST_TYPE_LEN (line 24) | pub(crate) const REQUEST_TYPE_LEN: usize = 33;
constant PREPARE_TYPE_LEN (line 25) | pub(crate) const PREPARE_TYPE_LEN: usize = 33;
constant PREPAREOK_TYPE_LEN (line 26) | pub(crate) const PREPAREOK_TYPE_LEN: usize = 35;
constant MYPREPAREOK_TYPE_LEN (line 27) | pub(crate) const MYPREPAREOK_TYPE_LEN: usize = 24;
constant FAST_PAXOS_DATA_LEN (line 29) | pub(crate) const FAST_PAXOS_DATA_LEN: usize = 12;
constant BROADCAST_SIGN_BIT (line 30) | pub(crate) const BROADCAST_SIGN_BIT: u32 = 1 << 31;
constant QUORUM_SIZE (line 31) | pub(crate) const QUORUM_SIZE: u32 = (CLUSTER_SIZE as u32 + 1) >> 1;
constant QUORUM_BITSET_ENTRY (line 32) | pub(crate) const QUORUM_BITSET_ENTRY: u32 = 1024;
constant PAXOS_PORT (line 34) | pub(crate) const PAXOS_PORT: u16 = 12345;
constant MAGIC_BITS (line 35) | pub(crate) const MAGIC_BITS: [u8; MAGIC_LEN] = [0x18, 0x03, 0x05, 0x20];
type ReplicaStatus (line 39) | pub(crate) enum ReplicaStatus {
type FastProgXdp (line 47) | pub(crate) enum FastProgXdp {
type FastProgTc (line 59) | pub(crate) enum FastProgTc {
type PaxosConfigure (line 66) | pub(crate) struct PaxosConfigure {
FILE: samples/electrode/src/main.rs
function fast_paxos_main (line 34) | fn fast_paxos_main(obj: &xdp, ctx: &mut xdp_md) -> Result {
function fast_broad_cast_main (line 81) | fn fast_broad_cast_main(obj: &sched_cls, skb: &mut __sk_buff) -> Result {
function handle_udp_fast_broad_cast (line 121) | fn handle_udp_fast_broad_cast(obj: &sched_cls, skb: &mut __sk_buff) -> R...
function handle_udp_fast_paxos (line 235) | fn handle_udp_fast_paxos(obj: &xdp, ctx: &mut xdp_md) -> Result {
function compute_message_type (line 287) | fn compute_message_type(payload: &[u8]) -> FastProgXdp {
function handle_prepare (line 320) | fn handle_prepare(obj: &xdp, ctx: &mut xdp_md, payload_index: usize) -> ...
function write_buffer (line 370) | fn write_buffer(obj: &xdp, ctx: &mut xdp_md, payload_index: usize) -> Re...
function prepare_fast_reply (line 397) | fn prepare_fast_reply(
function handle_prepare_ok (line 498) | fn handle_prepare_ok(
FILE: samples/electrode/src/maps.rs
type paxos_quorum (line 9) | pub(crate) struct paxos_quorum {
type paxos_ctr_state (line 17) | pub(crate) struct paxos_ctr_state {
type paxos_batch (line 28) | pub(crate) struct paxos_batch {
FILE: samples/error_injector/loader.c
function main (line 14) | int main(int argc, char *argv[])
FILE: samples/error_injector/src/main.rs
function err_injector (line 14) | pub fn err_injector(obj: &kprobe, ctx: &mut PtRegs) -> Result {
FILE: samples/error_injector/tests/runtest.py
function count_bpf_programs (line 11) | def count_bpf_programs():
function run_loader (line 37) | def run_loader():
function capture_output (line 47) | def capture_output() -> bool:
function main (line 69) | def main():
FILE: samples/error_injector/userapp.c
function main (line 6) | int main(void)
FILE: samples/hello/event-trigger.c
function main (line 4) | int main(void)
FILE: samples/hello/loader.c
function main (line 16) | int main(void)
FILE: samples/hello/src/main.rs
function rex_prog1 (line 10) | fn rex_prog1(
FILE: samples/hello/tests/runtest.py
function count_bpf_programs (line 10) | def count_bpf_programs():
function run_loader (line 36) | def run_loader():
function trigger_prog (line 46) | def trigger_prog():
function capture_output (line 53) | def capture_output() -> bool:
function main (line 79) | def main():
FILE: samples/map_bench/event-trigger.c
function main (line 7) | int main(int argc, char *argv[])
FILE: samples/map_bench/loader.c
function main (line 16) | int main(void)
FILE: samples/map_bench/src/main.rs
function rex_prog1 (line 19) | fn rex_prog1(obj: &kprobe, _ctx: &mut PtRegs) -> Result {
FILE: samples/map_test/event-trigger.c
function main (line 4) | int main(void)
FILE: samples/map_test/loader.c
function main (line 16) | int main(void)
FILE: samples/map_test/src/main.rs
function map_test1 (line 17) | fn map_test1(obj: &tracepoint<SyscallsEnterDupCtx>) -> Result {
function map_test2 (line 65) | fn map_test2(obj: &tracepoint<SyscallsEnterDupCtx>) -> Result {
function rex_prog1 (line 95) | fn rex_prog1(
FILE: samples/map_test/tests/runtest.py
function count_bpf_programs (line 10) | def count_bpf_programs():
function run_loader (line 36) | def run_loader():
function trigger_prog (line 46) | def trigger_prog():
function capture_output (line 53) | def capture_output() -> bool:
function main (line 82) | def main():
FILE: samples/map_test_2/event-trigger.c
function main (line 4) | int main(void)
FILE: samples/map_test_2/loader.c
function main (line 16) | int main(void)
FILE: samples/map_test_2/src/main.rs
function map_test_hash (line 28) | fn map_test_hash(obj: &tracepoint<SyscallsEnterDupCtx>) -> Result {
function map_test_array (line 76) | fn map_test_array(obj: &tracepoint<SyscallsEnterDupCtx>) -> Result {
function map_test_stack (line 103) | fn map_test_stack(obj: &tracepoint<SyscallsEnterDupCtx>) -> Result {
function map_test_queue (line 135) | fn map_test_queue(obj: &tracepoint<SyscallsEnterDupCtx>) -> Result {
function map_test_ringbuf (line 168) | fn map_test_ringbuf(obj: &tracepoint<SyscallsEnterDupCtx>) -> Result {
function rex_prog1 (line 203) | fn rex_prog1(
FILE: samples/map_test_2/tests/runtest.py
function count_bpf_programs (line 10) | def count_bpf_programs():
function run_loader (line 36) | def run_loader():
function trigger_prog (line 46) | def trigger_prog():
function capture_output (line 53) | def capture_output() -> bool:
function main (line 91) | def main():
FILE: samples/recursive/event-trigger.c
function main (line 7) | int main(int argc, char *argv[])
FILE: samples/recursive/loader.c
function main (line 17) | int main(void)
FILE: samples/recursive/src/main.rs
function rex_recursive (line 17) | fn rex_recursive(obj: &kprobe, ctx: &mut PtRegs) -> Result {
function calculate_tail_fib (line 49) | fn calculate_tail_fib(n: u32) {
FILE: samples/spinlock_cleanup_benchmark/loader.c
function main (line 23) | int main(int __unused argc, char **argv)
FILE: samples/spinlock_cleanup_benchmark/src/main.rs
type MapEntry (line 14) | struct MapEntry {
function rex_prog1 (line 23) | fn rex_prog1(obj: &xdp, _: &mut xdp_md) -> Result {
FILE: samples/spinlock_test/event-trigger.c
function main (line 4) | int main(void)
FILE: samples/spinlock_test/loader.c
function main (line 15) | int main(void)
FILE: samples/spinlock_test/src/main.rs
type MapEntry (line 14) | struct MapEntry {
function test1 (line 22) | fn test1(obj: &tracepoint<SyscallsEnterDupCtx>) {
function test2 (line 31) | fn test2(obj: &tracepoint<SyscallsEnterDupCtx>) {
function rex_prog1 (line 42) | fn rex_prog1(
FILE: samples/startup_overhead_benchmark/event-trigger.c
function main (line 7) | int main(int argc, char *argv[])
FILE: samples/startup_overhead_benchmark/loader.c
function main (line 16) | int main(void)
FILE: samples/startup_overhead_benchmark/src/main.rs
function rex_prog1 (line 11) | fn rex_prog1(_obj: &kprobe, _ctx: &mut PtRegs) -> Result {
FILE: samples/syscount/loader.c
function sig_handler (line 48) | static void sig_handler(int __unused sig)
type stats_type_t (line 53) | enum stats_type_t { COUNT_ONLY, SHOW_ERRORS, SHOW_LATENCY, SHOW_BOTH }
function print_header (line 55) | static void print_header(bool timestamp, enum stats_type_t type)
type options (line 100) | struct options {
function print_usage (line 111) | static void print_usage(const char *prog_name)
function parse_options (line 127) | static struct options parse_options(int argc, char *argv[])
function main (line 188) | int main(int argc, char *argv[])
FILE: samples/syscount/src/main.rs
function trace_syscall_enter (line 24) | fn trace_syscall_enter(
function trace_syscall_exit (line 62) | fn trace_syscall_exit(
FILE: samples/xdp_test/entry.c
type bpf_progs_desc (line 23) | struct bpf_progs_desc {
function main (line 33) | int main(int argc, char *argv[])
FILE: samples/xdp_test/src/main.rs
function xdp_rx_filter (line 15) | fn xdp_rx_filter(obj: &xdp, ctx: &mut xdp_md) -> Result {
function xdp_tx_filter (line 35) | fn xdp_tx_filter(obj: &sched_cls, skb: &mut __sk_buff) -> Result {
FILE: samples/xdp_test/tests/runtest.py
function count_bpf_programs (line 11) | def count_bpf_programs():
function generate_traffic (line 35) | def generate_traffic():
function get_trace_log (line 59) | def get_trace_log():
function test_xdp_program (line 77) | def test_xdp_program():
function main (line 165) | def main():
FILE: scripts/sanity_tests/run_tests.py
function main (line 32) | def main():
FILE: tools/memcached_benchmark/bench.py
class MemcachedCtx (line 12) | class MemcachedCtx:
method __init__ (line 13) | def __init__(self, start, stop):
method __enter__ (line 17) | def __enter__(self):
method __exit__ (line 20) | def __exit__(self, *args):
function increase_fd_limit (line 23) | def increase_fd_limit(new_limit):
function run_bench (line 38) | def run_bench():
function run_vanilla (line 47) | def run_vanilla(nr_threads):
function run_bpf (line 59) | def run_bpf(nr_threads):
function run_rust (line 71) | def run_rust(nr_threads):
function main (line 83) | def main():
FILE: tools/memcached_benchmark/src/cli.rs
type Cli (line 7) | pub(crate) struct Cli {
type Commands (line 13) | pub(crate) enum Commands {
FILE: tools/memcached_benchmark/src/dict.rs
constant SEED (line 15) | const SEED: u64 = 12312;
function generate_random_str (line 18) | pub(crate) fn generate_random_str(len: usize) -> String {
function generate_memcached_test_dict (line 23) | pub(crate) fn generate_memcached_test_dict(
function generate_test_dict_write_to_disk (line 52) | pub(crate) fn generate_test_dict_write_to_disk(
function generate_test_entries (line 72) | pub(crate) fn generate_test_entries(
function test_entries_statistics (line 97) | pub(crate) fn test_entries_statistics(
FILE: tools/memcached_benchmark/src/fs.rs
function write_hashmap_to_file (line 18) | pub(crate) fn write_hashmap_to_file<T: Serialize>(
function load_bench_entries_from_disk (line 45) | pub(crate) fn load_bench_entries_from_disk(
function load_test_dict (line 61) | pub(crate) fn load_test_dict(
FILE: tools/memcached_benchmark/src/get_values.rs
type Protocol (line 21) | pub(crate) enum Protocol {
type TaskData (line 26) | struct TaskData {
function wrap_get_command (line 37) | fn wrap_get_command(key: Arc<String>, seq: u16) -> Vec<u8> {
function socket_task (line 47) | async fn socket_task(
function get_command_benchmark (line 119) | pub(crate) async fn get_command_benchmark(
FILE: tools/memcached_benchmark/src/main.rs
type BenchConfig (line 16) | pub struct BenchConfig {
method get (line 25) | pub fn get() -> &'static BenchConfig {
constant BUFFER_SIZE (line 47) | const BUFFER_SIZE: usize = 1500;
constant BENCH_ENTRIES_PATH (line 48) | const BENCH_ENTRIES_PATH: &str = "bench_entries.yml.zst";
function get_server (line 52) | fn get_server(
function run_bench (line 69) | fn run_bench() -> Result<()> {
function main (line 278) | fn main() -> Result<()> {
FILE: tools/memcached_benchmark/src/set_values.rs
function set_memcached_value (line 12) | pub(super) async fn set_memcached_value(
FILE: tools/memcached_benchmark/tests/benchmark_test.rs
type ProcessGuard (line 12) | struct ProcessGuard {
method drop (line 17) | fn drop(&mut self) {
function test_memcached_benchmark (line 24) | fn test_memcached_benchmark() -> Result<()> {
Condensed preview — 226 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (665K chars).
[
{
"path": ".github/dependabot.yml",
"chars": 516,
"preview": "version: 2\nupdates:\n - package-ecosystem: cargo\n directories:\n - /\n - tools/memcached_benchmark\n schedu"
},
{
"path": ".github/workflows/dco-check.yml",
"chars": 179,
"preview": "name: DCO Check\n\non:\n pull_request:\n\njobs:\n check:\n runs-on: ubuntu-latest\n steps:\n - uses: KineticCafe/act"
},
{
"path": ".github/workflows/memcached_benchmark.yml",
"chars": 1396,
"preview": "name: memcached_benchmark\n\non:\n push:\n pull_request:\n\nenv:\n CARGO_TERM_COLOR: always\n\njobs:\n changes:\n if: github"
},
{
"path": ".github/workflows/meson.yml",
"chars": 3567,
"preview": "name: Meson Build and Test\n\non:\n push:\n branches: [main, ci]\n pull_request:\n branches: [main]\n\nenv:\n CARGO_TERM"
},
{
"path": ".github/workflows/rustfmt.yml",
"chars": 1473,
"preview": "name: Formatting check\n\non:\n push:\n pull_request:\n\njobs:\n formatting:\n if: github.repository == 'rex-rs/rex'\n n"
},
{
"path": ".github/workflows/update-nix.yml",
"chars": 1531,
"preview": "name: Update Nix Dependencies\non:\n workflow_dispatch:\n # push:\n # branches: [ ci ] # for testing\n schedule:\n - "
},
{
"path": ".gitignore",
"chars": 293,
"preview": ".vscode*\n.cargo\n!samples/*/.cargo\n!tools/*/.cargo\nsamples/*/event-trigger\nsamples/*/loader\nsamples/*/entry\nsamples/*/.ca"
},
{
"path": ".gitmodules",
"chars": 182,
"preview": "[submodule \"linux\"]\n\tpath = linux\n\turl = git@github.com:rex-rs/linux.git\n\tbranch = rex-linux\n[submodule \"rust\"]\n\tpath = "
},
{
"path": "Cargo.toml",
"chars": 633,
"preview": "[workspace]\nmembers = [\n\t# rex lib\n\t\"rex\",\n\n\t# macros\n\t\"rex-macros\",\n]\n\nresolver = \"2\"\nexclude = [\"samples\", \"rust\", \"to"
},
{
"path": "LICENSE",
"chars": 18092,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 2, June 1991\n\n Copyright (C) 1989, 1991 Fr"
},
{
"path": "README.md",
"chars": 6193,
"preview": "```\n ____ _______ __ _____ _ _\n| _ \\| ____\\ \\/ / | ____|_ _| |_ ___ _ __ ___(_) ___ _ __ __"
},
{
"path": "docs/entry-insertion.md",
"chars": 7306,
"preview": "# Entry point code Insertion\n\n## Motivation\n\nTo allow Rust extension code to be called from the kernel, an FFI\nentry-poi"
},
{
"path": "docs/exception-handling.md",
"chars": 12068,
"preview": "# Exception handling and runtime mechanism\n\nThis document will cover the following:\n\n- [Handling of Rust panics (languag"
},
{
"path": "docs/getting-started.md",
"chars": 6903,
"preview": "# Getting started (p20250206)\n\nBuilding Rex extensions requires modifications to the toolchain (Rust and\nLLVM) and runni"
},
{
"path": "docs/kernel-symbols.md",
"chars": 7471,
"preview": "# Handling of kernel symbols\n\nThis document covers dynamic kernel symbol resolution.\n\nThe `rex` crate serves as an inter"
},
{
"path": "docs/librex.md",
"chars": 1559,
"preview": "# LIBREX\n\n`librex` serves as the loader library for Rex programs -- similar to what\n`libbpf` is to eBPF programs -- but "
},
{
"path": "docs/rust_rex_subset.md",
"chars": 563,
"preview": "### Rex subset of Rust\n\n- `std`\n - depends on libc, therefore not available in standalone mode\n- `alloc`\n - Rex curren"
},
{
"path": "flake.nix",
"chars": 6059,
"preview": "{\n description = \"A flake for the REX project\";\n\n inputs = {\n nixpkgs.url = \"github:nixos/nixpkgs/nixos-unstable\";\n"
},
{
"path": "librex/.gitignore",
"chars": 35,
"preview": "librex.a\nlibrex.o\nlibrex.so\n.cache\n"
},
{
"path": "librex/include/librex.h",
"chars": 418,
"preview": "#ifndef LIBREX_H\n#define LIBREX_H\n\nstruct bpf_object;\nstruct rex_obj;\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n[[gnu::vi"
},
{
"path": "librex/lib/bindings.h",
"chars": 6182,
"preview": "// This file contains the non-portable part, it has to mirror some libbpf types\n\n#ifndef _LIBREX_BINDINGS_H\n#define _LIB"
},
{
"path": "librex/lib/librex.cpp",
"chars": 22565,
"preview": "#include <fcntl.h>\n#include <libelf.h>\n#include <linux/bpf.h>\n#include <sys/mman.h>\n#include <sys/stat.h>\n#include <unis"
},
{
"path": "librex/meson.build",
"chars": 654,
"preview": "rex_rootdir = '..'\n\nllvm_dep = dependency('llvm', version : '>=18')\nelf_dep = dependency('libelf')\n\nlibrex_public_inc = "
},
{
"path": "meson.build",
"chars": 1894,
"preview": "project('rex-compile',\n ['c', 'cpp'],\n version: '0.1.0',\n default_options : [\n 'buildtype=debugoptimized',\n 'wa"
},
{
"path": "rex/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "rex/.gitignore",
"chars": 41,
"preview": "Cargo.lock\ntarget\nlibiustub/libiustub.so\n"
},
{
"path": "rex/Cargo.toml",
"chars": 976,
"preview": "[package]\nname = \"rex\"\nversion = \"0.2.0\"\nbuild = \"build.rs\"\nautotests = false\nrepository.workspace = true\nedition.worksp"
},
{
"path": "rex/build.py",
"chars": 6291,
"preview": "import os\nimport re\nimport subprocess\nimport sys\nimport tomllib\n\n# https://github.com/rust-lang/rust-bindgen\nbindgen_cmd"
},
{
"path": "rex/build.rs",
"chars": 1555,
"preview": "#![feature(exit_status_error)]\n\nuse std::io::Result;\nuse std::path::Path;\nuse std::process::Command;\nuse std::string::St"
},
{
"path": "rex/librexstub/lib.c",
"chars": 1448,
"preview": "// Functions\n#define KSYM_FUNC(func) \\\n int func() { return 0; }\n\nKSYM_FUNC(bpf_get_current_pid_tgid)\nKSYM_FUNC(bpf_tra"
},
{
"path": "rex/meson.build",
"chars": 694,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "rex/src/base_helper.rs",
"chars": 12167,
"preview": "// use crate::timekeeping::*;\nuse core::intrinsics::unlikely;\nuse core::mem::MaybeUninit;\n\nuse crate::ffi;\nuse crate::li"
},
{
"path": "rex/src/bindings/linux/kernel.rs",
"chars": 56,
"preview": "include!(concat!(env!(\"OUT_DIR\"), \"/linux/kernel.rs\"));\n"
},
{
"path": "rex/src/bindings/linux/mod.rs",
"chars": 16,
"preview": "pub mod kernel;\n"
},
{
"path": "rex/src/bindings/mod.rs",
"chars": 163,
"preview": "#![allow(\n non_camel_case_types,\n non_snake_case,\n non_upper_case_globals,\n unused,\n unnecessary_transmut"
},
{
"path": "rex/src/bindings/uapi/linux/bpf.rs",
"chars": 58,
"preview": "include!(concat!(env!(\"OUT_DIR\"), \"/uapi/linux/bpf.rs\"));\n"
},
{
"path": "rex/src/bindings/uapi/linux/errno.rs",
"chars": 60,
"preview": "include!(concat!(env!(\"OUT_DIR\"), \"/uapi/linux/errno.rs\"));\n"
},
{
"path": "rex/src/bindings/uapi/linux/in.rs",
"chars": 57,
"preview": "include!(concat!(env!(\"OUT_DIR\"), \"/uapi/linux/in.rs\"));\n"
},
{
"path": "rex/src/bindings/uapi/linux/mod.rs",
"chars": 128,
"preview": "pub mod bpf;\npub mod errno;\npub mod r#in;\npub mod perf_event;\npub mod pkt_cls;\npub mod ptrace;\npub mod seccomp;\npub mod "
},
{
"path": "rex/src/bindings/uapi/linux/perf_event.rs",
"chars": 65,
"preview": "include!(concat!(env!(\"OUT_DIR\"), \"/uapi/linux/perf_event.rs\"));\n"
},
{
"path": "rex/src/bindings/uapi/linux/pkt_cls.rs",
"chars": 62,
"preview": "include!(concat!(env!(\"OUT_DIR\"), \"/uapi/linux/pkt_cls.rs\"));\n"
},
{
"path": "rex/src/bindings/uapi/linux/ptrace.rs",
"chars": 61,
"preview": "include!(concat!(env!(\"OUT_DIR\"), \"/uapi/linux/ptrace.rs\"));\n"
},
{
"path": "rex/src/bindings/uapi/linux/seccomp.rs",
"chars": 62,
"preview": "include!(concat!(env!(\"OUT_DIR\"), \"/uapi/linux/seccomp.rs\"));\n"
},
{
"path": "rex/src/bindings/uapi/linux/unistd.rs",
"chars": 61,
"preview": "include!(concat!(env!(\"OUT_DIR\"), \"/uapi/linux/unistd.rs\"));\n"
},
{
"path": "rex/src/bindings/uapi/mod.rs",
"chars": 15,
"preview": "pub mod linux;\n"
},
{
"path": "rex/src/debug.rs",
"chars": 287,
"preview": "use core::ffi::{c_int, c_uchar};\n\nuse crate::ffi;\n\n// WARN must add \"\\0\" at the end of &str, c_str is different from rus"
},
{
"path": "rex/src/ffi.rs",
"chars": 10688,
"preview": "// All kernel symbols we need should be declared here\n\n#![allow(dead_code)]\n\nuse core::ffi::{c_uchar, VaList};\n\nuse crat"
},
{
"path": "rex/src/kprobe/kprobe_impl.rs",
"chars": 1399,
"preview": "use core::marker::PhantomData;\n\nuse crate::bindings::uapi::linux::bpf::bpf_map_type;\nuse crate::ffi;\nuse crate::pt_regs:"
},
{
"path": "rex/src/kprobe/mod.rs",
"chars": 42,
"preview": "mod kprobe_impl;\n\npub use kprobe_impl::*;\n"
},
{
"path": "rex/src/lib.rs",
"chars": 667,
"preview": "#![no_std]\n#![feature(\n array_ptr_get,\n auto_traits,\n c_variadic,\n core_intrinsics,\n negative_impls\n)]\n#!"
},
{
"path": "rex/src/log.rs",
"chars": 2735,
"preview": "use core::fmt::{self, Write};\n\nuse crate::base_helper::termination_check;\nuse crate::bindings::uapi::linux::errno::E2BIG"
},
{
"path": "rex/src/map.rs",
"chars": 11469,
"preview": "use core::fmt::{self, Write};\nuse core::intrinsics::unlikely;\nuse core::marker::PhantomData;\nuse core::ops::{Deref, Dere"
},
{
"path": "rex/src/panic.rs",
"chars": 5819,
"preview": "//! Implementation of various routines/frameworks related to EH/termination\n\nuse core::fmt::Write;\nuse core::panic::Pani"
},
{
"path": "rex/src/per_cpu.rs",
"chars": 3122,
"preview": "use crate::ffi;\n\npub(crate) trait PerCPURead {\n unsafe fn this_cpu_read(addr: *const Self) -> Self;\n}\n\n// rustc does "
},
{
"path": "rex/src/perf_event/mod.rs",
"chars": 50,
"preview": "mod perf_event_impl;\n\npub use perf_event_impl::*;\n"
},
{
"path": "rex/src/perf_event/perf_event_impl.rs",
"chars": 2479,
"preview": "use core::intrinsics::unlikely;\nuse core::marker::PhantomData;\n\nuse crate::base_helper::termination_check;\nuse crate::bi"
},
{
"path": "rex/src/pt_regs.rs",
"chars": 1721,
"preview": "use paste::paste;\n\nuse crate::bindings::linux::kernel::pt_regs;\n\n/// Transparently wraps around the kernel-internal `str"
},
{
"path": "rex/src/random32.rs",
"chars": 171,
"preview": "use crate::ffi;\n\n// fn get_cpu_var()\n#[inline(always)]\npub(crate) fn bpf_user_rnd_u32() -> u32 {\n // directly use get"
},
{
"path": "rex/src/sched_cls/mod.rs",
"chars": 64,
"preview": "// mod binding;\nmod sched_cls_impl;\n\npub use sched_cls_impl::*;\n"
},
{
"path": "rex/src/sched_cls/sched_cls_impl.rs",
"chars": 6929,
"preview": "use core::ffi::{c_char, c_uchar};\nuse core::marker::PhantomData;\nuse core::{mem, slice};\n\nuse crate::base_helper::termin"
},
{
"path": "rex/src/spinlock.rs",
"chars": 2090,
"preview": "use crate::base_helper::termination_check;\npub use crate::bindings::uapi::linux::bpf::bpf_spin_lock;\nuse crate::ffi;\nuse"
},
{
"path": "rex/src/task_struct.rs",
"chars": 3732,
"preview": "use core::ffi::{self as core_ffi, CStr};\nuse core::ops::Deref;\n\nuse crate::base_helper::termination_check;\nuse crate::bi"
},
{
"path": "rex/src/tracepoint/binding.rs",
"chars": 1070,
"preview": "#[repr(C)]\n#[derive(Debug, Copy, Clone)]\npub struct SyscallsEnterOpenCtx {\n unused: u64,\n pub syscall_nr: i64,\n "
},
{
"path": "rex/src/tracepoint/mod.rs",
"chars": 67,
"preview": "mod binding;\nmod tp_impl;\n\npub use binding::*;\npub use tp_impl::*;\n"
},
{
"path": "rex/src/tracepoint/tp_impl.rs",
"chars": 2264,
"preview": "use core::marker::PhantomData;\n\nuse super::{\n RawSyscallsEnterCtx, RawSyscallsExitCtx, SyscallsEnterDupCtx,\n Sysca"
},
{
"path": "rex/src/utils.rs",
"chars": 11069,
"preview": "use core::ffi::{c_int, c_uchar};\nuse core::mem;\nuse core::ops::{Deref, DerefMut, Drop};\n\nuse crate::bindings::uapi::linu"
},
{
"path": "rex/src/xdp/mod.rs",
"chars": 52,
"preview": "// mod binding;\nmod xdp_impl;\n\npub use xdp_impl::*;\n"
},
{
"path": "rex/src/xdp/xdp_impl.rs",
"chars": 5311,
"preview": "use core::ffi::c_uchar;\nuse core::marker::PhantomData;\nuse core::mem::size_of;\nuse core::{mem, slice};\n\nuse crate::base_"
},
{
"path": "rex-macros/.gitignore",
"chars": 414,
"preview": "# Generated by Cargo\n# will have compiled files and executables\ndebug/\ntarget/\n\n# Remove Cargo.lock from gitignore if cr"
},
{
"path": "rex-macros/Cargo.toml",
"chars": 397,
"preview": "[package]\nname = \"rex-macros\"\nversion = \"0.2.0\"\nrepository.workspace = true\nedition.workspace = true\nauthors.workspace ="
},
{
"path": "rex-macros/src/args.rs",
"chars": 1327,
"preview": "use std::collections::HashMap;\n\nuse proc_macro2::TokenStream;\nuse proc_macro_error::abort;\nuse syn::spanned::Spanned;\nus"
},
{
"path": "rex-macros/src/kprobe.rs",
"chars": 2206,
"preview": "use std::fmt;\n\nuse proc_macro2::TokenStream;\nuse quote::{format_ident, quote};\nuse syn::{parse2, ItemFn, Result};\n\nuse c"
},
{
"path": "rex-macros/src/lib.rs",
"chars": 3133,
"preview": "#[macro_use]\npub(crate) mod args;\nmod kprobe;\nmod perf_event;\nmod tc;\nmod tracepoint;\nmod xdp;\n\nuse std::borrow::Cow;\n\nu"
},
{
"path": "rex-macros/src/perf_event.rs",
"chars": 1356,
"preview": "use proc_macro2::TokenStream;\nuse quote::{format_ident, quote};\nuse syn::{ItemFn, Result};\n\npub(crate) struct PerfEvent "
},
{
"path": "rex-macros/src/tc.rs",
"chars": 1642,
"preview": "use proc_macro2::TokenStream;\nuse quote::{format_ident, quote};\nuse syn::{ItemFn, Result};\n\npub(crate) struct SchedCls {"
},
{
"path": "rex-macros/src/tracepoint.rs",
"chars": 3160,
"preview": "use proc_macro2::TokenStream;\nuse proc_macro_error::abort_call_site;\nuse quote::{format_ident, quote, ToTokens};\nuse syn"
},
{
"path": "rex-macros/src/xdp.rs",
"chars": 1574,
"preview": "use proc_macro2::TokenStream;\nuse quote::{format_ident, quote};\nuse syn::{ItemFn, Result};\n\npub(crate) struct Xdp {\n "
},
{
"path": "rex-native.ini",
"chars": 85,
"preview": "[binaries]\nc = 'clang'\nc_ld = 'mold'\ncpp = 'clang++'\ncpp_ld = 'mold'\nar = 'llvm-ar'\n\n"
},
{
"path": "rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/.clang-format",
"chars": 22189,
"preview": "# SPDX-License-Identifier: GPL-2.0\n#\n# clang-format configuration file. Intended for clang-format >= 11.\n#\n# For more in"
},
{
"path": "samples/.gitignore",
"chars": 99,
"preview": "*/loader\n*/event-trigger\n*/src/linux\n*/src/stub.rs\n*/target\n*/.gdbinit\n*/.gdb_history\n*/syscall_tp\n"
},
{
"path": "samples/atomic/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/atomic/Cargo.toml",
"chars": 519,
"preview": "[package]\nname = \"atomic\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.rust-"
},
{
"path": "samples/atomic/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/atomic/event-trigger.c",
"chars": 423,
"preview": "#include <fcntl.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <sys/ioctl.h>\n#include <unistd.h>\n\nint main(int argc,"
},
{
"path": "samples/atomic/loader.c",
"chars": 875,
"preview": "#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <unistd.h>"
},
{
"path": "samples/atomic/meson.build",
"chars": 1218,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/atomic/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/atomic/src/main.rs",
"chars": 621,
"preview": "#![no_std]\n#![no_main]\nextern crate rex;\n\nuse core::sync::atomic::{AtomicU64, Ordering};\n\nuse rex::kprobe::*;\nuse rex::p"
},
{
"path": "samples/bmc/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/bmc/Cargo.toml",
"chars": 517,
"preview": "[package]\nname = \"bmc\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.rust-lan"
},
{
"path": "samples/bmc/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/bmc/entry.c",
"chars": 6742,
"preview": "#include <bpf/bpf.h>\n#include <bpf/libbpf.h>\n#include <linux/bpf.h>\n#include <linux/if_link.h>\n#include <linux/limits.h>"
},
{
"path": "samples/bmc/meson.build",
"chars": 1084,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/bmc/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/bmc/src/main.rs",
"chars": 13938,
"preview": "#![no_std]\n#![no_main]\n#![allow(non_camel_case_types)]\n\nuse core::mem::{size_of, swap};\n\nuse rex::map::*;\nuse rex::sched"
},
{
"path": "samples/electrode/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/electrode/Cargo.toml",
"chars": 585,
"preview": "[package]\nname = \"electrode\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.ru"
},
{
"path": "samples/electrode/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/electrode/config.txt",
"chars": 76,
"preview": "f 1\nreplica 10.10.1.1:12345\nreplica 10.10.1.2:12345\nreplica 10.10.1.3:12345\n"
},
{
"path": "samples/electrode/entry.c",
"chars": 8338,
"preview": "#include <arpa/inet.h>\n#include <asm-generic/posix_types.h>\n#include <assert.h>\n#include <bpf/bpf.h>\n#include <bpf/libbp"
},
{
"path": "samples/electrode/fast_common.h",
"chars": 1269,
"preview": "/*\n * Software Name : fast-paxos\n * SPDX-FileCopyrightText: Copyright (c) 2022 Orange\n * SPDX-License-Identifier: LGP"
},
{
"path": "samples/electrode/meson.build",
"chars": 1321,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/electrode/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/electrode/src/common.rs",
"chars": 2029,
"preview": "#![allow(unused)]\n//\n// Software Name : fast-paxos\n// SPDX-FileCopyrightText: Copyright (c) 2022 Orange\n// SPDX-License-"
},
{
"path": "samples/electrode/src/main.rs",
"chars": 16595,
"preview": "#![no_std]\n#![no_main]\n#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n\nextern crate rex;\n\nuse core::mem::{siz"
},
{
"path": "samples/electrode/src/maps.rs",
"chars": 1325,
"preview": "use rex::linux::bpf::bpf_spin_lock;\nuse rex::map::*;\nuse rex::rex_map;\n\nuse crate::common::*;\n\n#[repr(C)]\n#[derive(Clone"
},
{
"path": "samples/error_injector/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/error_injector/.gitignore",
"chars": 15,
"preview": "target\nuserapp\n"
},
{
"path": "samples/error_injector/Cargo.toml",
"chars": 527,
"preview": "[package]\nname = \"error_injector\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://d"
},
{
"path": "samples/error_injector/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/error_injector/loader.c",
"chars": 1851,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/wait.h>\n#include <unistd.h>\n\n#include <librex.h"
},
{
"path": "samples/error_injector/meson.build",
"chars": 2032,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/error_injector/src/main.rs",
"chars": 538,
"preview": "#![no_std]\n#![no_main]\n\nuse rex::kprobe::kprobe;\nuse rex::map::RexHashMap;\nuse rex::pt_regs::PtRegs;\nuse rex::{Result, r"
},
{
"path": "samples/error_injector/tests/runtest.py",
"chars": 1984,
"preview": "#!/usr/bin/env python\n\n\nimport re\nimport subprocess\nfrom time import sleep\n\nprocess = 0\n\n\ndef count_bpf_programs():\n "
},
{
"path": "samples/error_injector/userapp.c",
"chars": 162,
"preview": "#define _DEFAULT_SOURCE\n#include <stdio.h>\n#include <unistd.h>\n#include <linux/unistd.h>\n\nint main(void)\n{\n\tif (syscall("
},
{
"path": "samples/hello/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/hello/.gitignore",
"chars": 7,
"preview": "target\n"
},
{
"path": "samples/hello/Cargo.toml",
"chars": 518,
"preview": "[package]\nname = \"hello\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.rust-l"
},
{
"path": "samples/hello/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/hello/event-trigger.c",
"chars": 96,
"preview": "#include <unistd.h>\n#include <linux/unistd.h>\n\nint main(void)\n{\n\treturn syscall(__NR_dup, 1);\n}\n"
},
{
"path": "samples/hello/loader.c",
"chars": 1045,
"preview": "#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <unistd.h>"
},
{
"path": "samples/hello/meson.build",
"chars": 1944,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/hello/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/hello/src/main.rs",
"chars": 506,
"preview": "#![no_std]\n#![no_main]\n\nextern crate rex;\n\nuse rex::tracepoint::*;\nuse rex::{Result, rex_printk, rex_tracepoint};\n\n#[rex"
},
{
"path": "samples/hello/tests/runtest.py",
"chars": 2205,
"preview": "#!/bin/python\n\nimport re\nimport subprocess\nfrom time import sleep\n\nprocess = 0\n\n\ndef count_bpf_programs():\n try:\n "
},
{
"path": "samples/map_bench/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/map_bench/Cargo.toml",
"chars": 522,
"preview": "[package]\nname = \"map_bench\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.ru"
},
{
"path": "samples/map_bench/bench.sh",
"chars": 519,
"preview": "#!/usr/bin/env bash\n\nif [ \"$#\" -ne 2 ]; then\n\techo \"Usage: $0 <rex|bpf> <atomic|hash|array>\"\n\texit 1\nfi\n\nmkdir output\nec"
},
{
"path": "samples/map_bench/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/map_bench/event-trigger.c",
"chars": 423,
"preview": "#include <fcntl.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <sys/ioctl.h>\n#include <unistd.h>\n\nint main(int argc,"
},
{
"path": "samples/map_bench/loader.c",
"chars": 878,
"preview": "#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <unistd.h>"
},
{
"path": "samples/map_bench/meson.build",
"chars": 1228,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/map_bench/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/map_bench/src/main.rs",
"chars": 1117,
"preview": "#![no_std]\n#![no_main]\n\nextern crate rex;\n\nuse rex::kprobe::*;\nuse rex::linux::bpf::*;\nuse rex::map::*;\nuse rex::pt_regs"
},
{
"path": "samples/map_test/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/map_test/Cargo.toml",
"chars": 521,
"preview": "[package]\nname = \"map_test\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.rus"
},
{
"path": "samples/map_test/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/map_test/event-trigger.c",
"chars": 96,
"preview": "#include <unistd.h>\n#include <linux/unistd.h>\n\nint main(void)\n{\n\treturn syscall(__NR_dup, 1);\n}\n"
},
{
"path": "samples/map_test/loader.c",
"chars": 1049,
"preview": "#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <unistd.h>"
},
{
"path": "samples/map_test/meson.build",
"chars": 1979,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/map_test/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/map_test/src/main.rs",
"chars": 2614,
"preview": "#![no_std]\n#![no_main]\n\nextern crate rex;\n\nuse rex::linux::bpf::BPF_ANY;\nuse rex::map::*;\nuse rex::tracepoint::*;\nuse re"
},
{
"path": "samples/map_test/tests/runtest.py",
"chars": 2451,
"preview": "#!/bin/python\n\nimport re\nimport subprocess\nfrom time import sleep\n\nprocess = 0\n\n\ndef count_bpf_programs():\n try:\n "
},
{
"path": "samples/map_test_2/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/map_test_2/Cargo.toml",
"chars": 523,
"preview": "[package]\nname = \"map_test_2\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.r"
},
{
"path": "samples/map_test_2/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/map_test_2/event-trigger.c",
"chars": 96,
"preview": "#include <unistd.h>\n#include <linux/unistd.h>\n\nint main(void)\n{\n\treturn syscall(__NR_dup, 1);\n}\n"
},
{
"path": "samples/map_test_2/loader.c",
"chars": 1051,
"preview": "#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <unistd.h>"
},
{
"path": "samples/map_test_2/meson.build",
"chars": 2002,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/map_test_2/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/map_test_2/src/main.rs",
"chars": 5421,
"preview": "#![no_std]\n#![no_main]\n\nextern crate rex;\n\nuse core::mem;\n\nuse rex::map::*;\nuse rex::tracepoint::*;\nuse rex::utils::conv"
},
{
"path": "samples/map_test_2/tests/runtest.py",
"chars": 3155,
"preview": "#!/bin/python\n\nimport re\nimport subprocess\nfrom time import sleep\n\nprocess = 0\n\n\ndef count_bpf_programs():\n try:\n "
},
{
"path": "samples/meson.build",
"chars": 369,
"preview": "subdir('atomic')\nsubdir('bmc')\nsubdir('electrode')\nsubdir('error_injector')\nsubdir('hello')\nsubdir('map_bench')\nsubdir('"
},
{
"path": "samples/recursive/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/recursive/Cargo.toml",
"chars": 522,
"preview": "[package]\nname = \"recursive\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.ru"
},
{
"path": "samples/recursive/bench.sh",
"chars": 624,
"preview": "#!/usr/bin/env bash\n\nif [ \"$#\" -ne 1 ]; then\n\techo \"Usage: $0 <bpf|rex>\"\n\texit 1\nfi\n\necho 8192 > /sys/kernel/debug/traci"
},
{
"path": "samples/recursive/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/recursive/event-trigger.c",
"chars": 423,
"preview": "#include <fcntl.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <sys/ioctl.h>\n#include <unistd.h>\n\nint main(int argc,"
},
{
"path": "samples/recursive/loader.c",
"chars": 912,
"preview": "#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <unistd.h>"
},
{
"path": "samples/recursive/meson.build",
"chars": 1228,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/recursive/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/recursive/src/main.rs",
"chars": 1318,
"preview": "#![no_std]\n#![no_main]\nextern crate rex;\n\n// use rex::tracepoint::*;\nuse core::hint::black_box;\n\nuse rex::kprobe::*;\nuse"
},
{
"path": "samples/spinlock_cleanup_benchmark/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/spinlock_cleanup_benchmark/.gitignore",
"chars": 7,
"preview": "target\n"
},
{
"path": "samples/spinlock_cleanup_benchmark/Cargo.toml",
"chars": 539,
"preview": "[package]\nname = \"spinlock_cleanup_benchmark\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions "
},
{
"path": "samples/spinlock_cleanup_benchmark/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/spinlock_cleanup_benchmark/loader.c",
"chars": 1153,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <sched.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <sys/types.h>\n"
},
{
"path": "samples/spinlock_cleanup_benchmark/meson.build",
"chars": 1156,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/spinlock_cleanup_benchmark/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/spinlock_cleanup_benchmark/src/main.rs",
"chars": 892,
"preview": "#![no_std]\n#![no_main]\n\nextern crate rex;\n\nuse rex::linux::bpf::bpf_spin_lock;\nuse rex::map::RexArrayMap;\nuse rex::spinl"
},
{
"path": "samples/spinlock_test/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/spinlock_test/.gitignore",
"chars": 7,
"preview": "target\n"
},
{
"path": "samples/spinlock_test/Cargo.toml",
"chars": 526,
"preview": "[package]\nname = \"spinlock_test\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://do"
},
{
"path": "samples/spinlock_test/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/spinlock_test/event-trigger.c",
"chars": 96,
"preview": "#include <unistd.h>\n#include <linux/unistd.h>\n\nint main(void)\n{\n\treturn syscall(__NR_dup, 1);\n}\n"
},
{
"path": "samples/spinlock_test/loader.c",
"chars": 1012,
"preview": "#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <unistd.h>\n\n#include <linux/perf_"
},
{
"path": "samples/spinlock_test/meson.build",
"chars": 1240,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/spinlock_test/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/spinlock_test/src/main.rs",
"chars": 1270,
"preview": "#![no_std]\n#![no_main]\n\nextern crate rex;\n\nuse rex::linux::bpf::bpf_spin_lock;\nuse rex::map::RexArrayMap;\nuse rex::spinl"
},
{
"path": "samples/startup_overhead_benchmark/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/startup_overhead_benchmark/.gitignore",
"chars": 7,
"preview": "target\n"
},
{
"path": "samples/startup_overhead_benchmark/Cargo.toml",
"chars": 539,
"preview": "[package]\nname = \"startup_overhead_benchmark\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions "
},
{
"path": "samples/startup_overhead_benchmark/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/startup_overhead_benchmark/event-trigger.c",
"chars": 423,
"preview": "#include <fcntl.h>\n#include <stdlib.h>\n#include <stdio.h>\n#include <sys/ioctl.h>\n#include <unistd.h>\n\nint main(int argc,"
},
{
"path": "samples/startup_overhead_benchmark/loader.c",
"chars": 901,
"preview": "#include <fcntl.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <sys/ioctl.h>\n#include <unistd.h>"
},
{
"path": "samples/startup_overhead_benchmark/meson.build",
"chars": 1279,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/startup_overhead_benchmark/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/startup_overhead_benchmark/src/main.rs",
"chars": 239,
"preview": "#![no_std]\n#![no_main]\n\nextern crate rex;\n\nuse rex::kprobe::*;\nuse rex::pt_regs::PtRegs;\nuse rex::{Result, rex_kprobe};\n"
},
{
"path": "samples/syscount/.cargo/config.toml",
"chars": 305,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\","
},
{
"path": "samples/syscount/Cargo.toml",
"chars": 423,
"preview": "[package]\nname = \"syscount\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[dependencies]\n\n[dependencies.rex]\npath = \"../../rex\"\n\n["
},
{
"path": "samples/syscount/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
},
{
"path": "samples/syscount/loader.c",
"chars": 12043,
"preview": "#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <signal.h>\n#include <string.h>\n#include <bpf/libbpf."
},
{
"path": "samples/syscount/meson.build",
"chars": 1124,
"preview": "build_dir = run_command(\n realpath,\n '--relative-to',\n meson.current_source_dir(),\n meson.current_build_dir(),\n cap"
},
{
"path": "samples/syscount/rustfmt.toml",
"chars": 153,
"preview": "max_width = 80\nbinop_separator = \"Back\"\nreorder_impl_items = true\nwrap_comments = true\nimports_granularity = \"Module\"\ngr"
},
{
"path": "samples/syscount/src/main.rs",
"chars": 3035,
"preview": "#![no_std]\n#![no_main]\nextern crate rex;\n\nuse rex::linux::bpf::BPF_ANY;\nuse rex::map::RexHashMap;\nuse rex::tracepoint::*"
},
{
"path": "samples/xdp_test/.cargo/config.toml",
"chars": 293,
"preview": "[build]\ntarget = \"x86_64-unknown-none\"\n\n[target.x86_64-unknown-none]\nlinker = \"ld.mold\"\nrustflags = [\n \"-Zthreads=8\",\n "
},
{
"path": "samples/xdp_test/Cargo.toml",
"chars": 522,
"preview": "[package]\nname = \"xdp_test\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n# See more keys and their definitions at https://doc.rus"
},
{
"path": "samples/xdp_test/README.md",
"chars": 8317,
"preview": "<div style=\"text-align: justify;\">\n\n# Onboarding Task: Rust-based extensions\n\n**Note: You need to be on the x86-64 archi"
},
{
"path": "samples/xdp_test/clippy.toml",
"chars": 99,
"preview": "disallowed-methods = [\n\t\"core::mem::forget\",\n]\n\ndisallowed-types = [\n\t\"core::mem::ManuallyDrop\",\n]\n"
}
]
// ... and 26 more files (download for full content)
About this extraction
This page contains the full source code of the rex-rs/rex GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 226 files (606.4 KB), approximately 186.1k tokens, and a symbol index with 520 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.