├── .clang-format ├── .deepsource.toml ├── .envrc ├── .github └── workflows │ ├── build-static-wasm-bpf.yml │ ├── c-cpp-lint.yml │ ├── c-cpp.yml │ ├── codeql-analysis.yml │ ├── deploy-asserts.yml │ ├── nix.yml │ ├── publish-example-images.yml │ ├── release-container.yml │ └── rust.yml ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── README_zh.md ├── codecov.yaml ├── docs ├── WASI-bpf.png ├── build-image.md ├── build.md ├── logo.png ├── podman.md └── wasm-bpf-no-bcc.png ├── examples ├── .gitignore ├── Makefile ├── README.md ├── bootstrap-libbpf-rs │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── bootstrap.bpf.c │ ├── bootstrap.h │ ├── build.rs │ └── src │ │ └── main.rs ├── bootstrap │ ├── Makefile │ ├── README.md │ ├── README_zh.md │ ├── bootstrap.bpf.c │ ├── bootstrap.c │ ├── bootstrap.h │ ├── bootstrap.wasm.h │ └── libbpf-wasm.h ├── execve │ ├── Makefile │ ├── execve.bpf.c │ ├── execve.c │ ├── execve.h │ └── libbpf-wasm.h ├── go-execve │ ├── Makefile │ ├── README.md │ ├── execve.bpf.c │ ├── execve.h │ └── main.go ├── go-lsm │ ├── Makefile │ ├── lsm.bpf.c │ └── main.go ├── lsm │ ├── Makefile │ ├── README.md │ ├── libbpf-wasm.h │ ├── lsm.bpf.c │ └── lsm.c ├── opensnoop │ ├── Makefile │ ├── README.md │ ├── libbpf-wasm.h │ ├── opensnoop.bpf.c │ ├── opensnoop.c │ ├── opensnoop.h │ ├── trace_helpers.c │ └── trace_helpers.h ├── runqlat │ ├── Makefile │ ├── README.md │ ├── README_zh.md │ ├── bits.bpf.h │ ├── core_fixes.bpf.h │ ├── libbpf-wasm.h │ ├── maps.bpf.h │ ├── runqlat.bpf.c │ ├── runqlat.c │ ├── runqlat.h │ ├── trace_helpers.c │ └── trace_helpers.h ├── rust-bootstrap │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── bootstrap.bpf.c │ ├── bootstrap.h │ ├── btf │ │ ├── Makefile │ │ ├── event-def.c │ │ ├── event-def.wit │ │ ├── import.c │ │ └── import.wit │ ├── build.rs │ ├── src │ │ └── main.rs │ └── wit │ │ └── import.wit ├── sockfilter │ ├── Makefile │ ├── README.md │ ├── libbpf-wasm.h │ ├── sockfilter.bpf.c │ └── sockfilter.c ├── sockops │ ├── Makefile │ ├── README.md │ ├── libbpf-wasm.h │ ├── sockops.bpf.c │ └── sockops.c ├── tcpconnlat-libbpf-rs │ ├── .gitignore │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── build.rs │ ├── src │ │ └── main.rs │ ├── tcpconnlat.bpf.c │ └── tcpconnlat.h ├── uprobe │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── libbpf-wasm.h │ ├── target.c │ ├── uprobe.bpf.c │ └── uprobe.c └── xdp │ ├── Makefile │ ├── README.md │ ├── libbpf-wasm.h │ ├── xdp.bpf.c │ └── xdp.c ├── flake.lock ├── flake.nix ├── go.mod ├── runtime ├── .cargo │ └── config ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── c-wrapper │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── cli │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ └── src │ │ ├── log_format.rs │ │ └── main.rs ├── cpp │ ├── CMakeLists.txt │ ├── Makefile │ ├── cmake │ │ ├── CompilerWarnings.cmake │ │ ├── FindBpfObject.cmake │ │ ├── FindLibBpf.cmake │ │ ├── SourcesAndHeaders.cmake │ │ ├── StandardSettings.cmake │ │ ├── version.hpp.in │ │ └── wasm-bpfConfig.cmake.in │ ├── include │ │ └── bpf-api.h │ ├── src │ │ ├── README.md │ │ ├── main.cpp │ │ └── wasm-bpf.cpp │ ├── test │ │ ├── CMakeLists.txt │ │ ├── Makefile │ │ ├── asserts │ │ │ ├── .gitignore │ │ │ ├── Makefile │ │ │ └── runqlat.bpf.o │ │ ├── src │ │ │ ├── bpf_api_test.cpp │ │ │ ├── maps_test.h │ │ │ └── memory_check_test_driver.cpp │ │ └── wasm-apps │ │ │ ├── .gitignore │ │ │ ├── Makefile │ │ │ ├── api.h │ │ │ ├── base64decode.h │ │ │ ├── binding.cpp │ │ │ ├── memory_test_1.cpp │ │ │ └── memory_test_2.cpp │ └── third_party │ │ ├── bpf │ │ ├── bpf.h │ │ ├── bpf_core_read.h │ │ ├── bpf_endian.h │ │ ├── bpf_helper_defs.h │ │ ├── bpf_helpers.h │ │ ├── bpf_tracing.h │ │ ├── btf.h │ │ ├── libbpf.h │ │ ├── libbpf_common.h │ │ ├── libbpf_legacy.h │ │ ├── libbpf_version.h │ │ ├── skel_internal.h │ │ └── usdt.bpf.h │ │ └── vmlinux │ │ ├── arm64 │ │ ├── vmlinux.h │ │ └── vmlinux_516.h │ │ ├── mips │ │ ├── vmlinux.h │ │ └── vmlinux_610.h │ │ ├── powerpc │ │ ├── vmlinux.h │ │ └── vmlinux_514.h │ │ ├── riscv │ │ ├── vmlinux.h │ │ └── vmlinux_519.h │ │ ├── vmlinux.h │ │ └── x86 │ │ ├── vmlinux.h │ │ └── vmlinux_508.h └── wasm-bpf-rs │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Makefile │ ├── README.md │ ├── src │ ├── bpf │ │ ├── attach.rs │ │ ├── close.rs │ │ ├── fd_by_name.rs │ │ ├── load.rs │ │ ├── map_operate.rs │ │ ├── mod.rs │ │ ├── poll.rs │ │ └── wrapper_poll.rs │ ├── handle.rs │ ├── lib.rs │ ├── pipe.rs │ ├── runner.rs │ ├── state.rs │ ├── tests │ │ └── mod.rs │ └── utils.rs │ └── tests │ ├── abnormal_exit.wasm │ ├── bootstrap.bpf.o │ ├── bootstrap.wasm │ ├── custom_host_func.wasm │ ├── custom_host_func │ ├── .gitignore │ ├── Makefile │ ├── README.md │ └── custom_host_func.c │ ├── execve.wasm │ ├── exit_code │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── abnormal_exit.c │ ├── long_sleep.c │ └── normal_exit.c │ ├── go-execve.wasm │ ├── interruption_in_callback.wasm │ ├── interruption_in_callback │ ├── .gitignore │ ├── Makefile │ ├── base64decode.h │ ├── bootstrap.bpf.o │ ├── interruption_in_callback.c │ └── libbpf-wasm.h │ ├── interruption_in_hostfunc.wasm │ ├── interruption_in_hostfunc │ ├── .gitignore │ ├── Makefile │ ├── README.md │ └── interruption_in_hostfunc.c │ ├── long_sleep.wasm │ ├── lsm.wasm │ ├── normal_exit.wasm │ ├── opensnoop.wasm │ ├── runqlat.wasm │ ├── rust-bootstrap.wasm │ ├── sockfilter.wasm │ ├── sockops.wasm │ ├── tick.wasm │ └── tick │ ├── .gitignore │ ├── Makefile │ ├── README.md │ └── tick.c └── wasm-sdk └── README.md /.clang-format: -------------------------------------------------------------------------------- 1 | {BasedOnStyle: Chromium, IndentWidth: 4, ReflowComments: false, SortIncludes: "Never"} -------------------------------------------------------------------------------- /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "rust" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | msrv = "stable" 9 | 10 | [[analyzers]] 11 | name = "shell" 12 | enabled = true 13 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.github/workflows/build-static-wasm-bpf.yml: -------------------------------------------------------------------------------- 1 | name: Build the static library of `wasm-bpf` 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: 'recursive' 20 | 21 | - name: install deps 22 | run: | 23 | sudo make install-deps 24 | 25 | - name: Build wasm_bpf 26 | run: | 27 | cd runtime/c-wrapper 28 | cargo build --release 29 | - name: Merge several archives 30 | run: | 31 | cp ./runtime/target/release/libwasm_bpf.a . 32 | cp /usr/lib/x86_64-linux-gnu/libz.a . 33 | cp /usr/lib/x86_64-linux-gnu/libelf.a . 34 | llvm-ar x libwasm_bpf.a 35 | llvm-ar x libelf.a 36 | llvm-ar x libz.a 37 | rm *.a 38 | llvm-ar q libwasm_bpf.a *.o 39 | 40 | - name: Upload build result 41 | uses: actions/upload-artifact@v2.3.1 42 | with: 43 | path: "libwasm_bpf.a" 44 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp-lint.yml: -------------------------------------------------------------------------------- 1 | name: "c-cpp-lint" 2 | 3 | on: 4 | push: 5 | branches: "main" 6 | pull_request: 7 | branches: "*" 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | with: 15 | submodules: "recursive" 16 | - uses: DoozyX/clang-format-lint-action@v0.14 17 | with: 18 | source: 'examples wasm-sdk runtime/cpp/include runtime/cpp/test runtime/cpp/src' 19 | extensions: 'h,cpp,c,hpp,cxx,hxx' 20 | clangFormatVersion: 14 21 | inplace: True 22 | - uses: EndBug/add-and-commit@v4 23 | with: 24 | message: 'Committing clang-format changes' 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | -------------------------------------------------------------------------------- /.github/workflows/c-cpp.yml: -------------------------------------------------------------------------------- 1 | name: "c-cpp" 2 | 3 | on: 4 | push: 5 | branches: "main" 6 | pull_request: 7 | branches: "*" 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | with: 16 | submodules: 'recursive' 17 | - name: install deps 18 | run: | 19 | sudo make install-deps 20 | make /opt/wasi-sdk 21 | make tinygo 22 | - name: make 23 | run: make -C runtime/cpp 24 | - name: make build-lib 25 | run: make -C runtime/cpp build-lib 26 | - name: examples with cpp runtime 27 | run: IMPL=cpp make -C examples 28 | - name: make test 29 | run: | 30 | make clean 31 | make -C runtime/cpp test 32 | - name: Upload build result 33 | uses: actions/upload-artifact@v2.3.1 34 | with: 35 | path: "wasm-bpf" 36 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ "master" ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ "master" ] 20 | schedule: 21 | - cron: '19 6 * * 5' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: ubuntu-latest 27 | permissions: 28 | actions: read 29 | contents: read 30 | security-events: write 31 | 32 | strategy: 33 | fail-fast: false 34 | matrix: 35 | language: [ 'cpp' ] 36 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | with: 43 | submodules: 'recursive' 44 | - name: install deps 45 | run: | 46 | sudo make install-deps && sudo make /opt/wasi-sdk 47 | 48 | # Initializes the CodeQL tools for scanning. 49 | - name: Initialize CodeQL 50 | uses: github/codeql-action/init@v2 51 | with: 52 | languages: ${{ matrix.language }} 53 | # If you wish to specify custom queries, you can do so here or in a config file. 54 | # By default, queries listed here will override any specified in a config file. 55 | # Prefix the list here with "+" to use these queries and those in the config file. 56 | 57 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 58 | # queries: security-extended,security-and-quality 59 | 60 | 61 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 62 | # If this step fails, then you should remove it and run the build manually (see below) 63 | - name: Autobuild 64 | uses: github/codeql-action/autobuild@v2 65 | 66 | # ℹ️ Command-line programs to run using the OS shell. 67 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 68 | 69 | # If the Autobuild fails above, remove it and uncomment the following three lines. 70 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 71 | 72 | # - run: | 73 | # echo "Run, Build Application using script" 74 | # ./location_of_script_within_repo/buildscript.sh 75 | 76 | - name: Perform CodeQL Analysis 77 | uses: github/codeql-action/analyze@v2 78 | with: 79 | category: "/language:${{matrix.language}}" 80 | -------------------------------------------------------------------------------- /.github/workflows/deploy-asserts.yml: -------------------------------------------------------------------------------- 1 | # Simple workflow for deploying static content to GitHub Pages 2 | name: Deploy Wasm-eBPF asserts 3 | 4 | on: 5 | # Runs on pushes targeting the default branch 6 | push: 7 | branches: ["master", "main"] 8 | 9 | # Allows you to run this workflow manually from the Actions tab 10 | workflow_dispatch: 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | # Allow one concurrent deployment 19 | concurrency: 20 | group: "pages" 21 | cancel-in-progress: true 22 | 23 | # In that case do the job 'make_and_deploy_doxygen' 24 | jobs: 25 | deploy: 26 | environment: 27 | name: github-pages 28 | url: ${{ steps.deployment.outputs.page_url }} 29 | runs-on: ubuntu-latest 30 | steps: 31 | - name: Checkout 32 | uses: actions/checkout@v3 33 | with: 34 | submodules: 'recursive' 35 | - name: install deps 36 | run: | 37 | sudo make install-deps 38 | make /opt/wasi-sdk 39 | make tinygo 40 | - name: Cache 41 | uses: actions/cache@v3.3.0 42 | 43 | with: 44 | path: | 45 | Makefile 46 | key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} 47 | - name: make 48 | run: make -C runtime/cpp 49 | - name: examples with cpp runtime 50 | run: IMPL=cpp make -C examples 51 | 52 | - name: Setup Pages 53 | uses: actions/configure-pages@v1 54 | if: ${{ github.repository_owner == 'eunomia-bpf' }} 55 | - name: Upload artifact 56 | uses: actions/upload-pages-artifact@v1 57 | if: ${{ github.repository_owner == 'eunomia-bpf' }} 58 | with: 59 | # Upload entire repository 60 | path: '.' 61 | - name: Deploy to GitHub Pages 62 | if: ${{ github.repository_owner == 'eunomia-bpf' }} 63 | id: deploymen 64 | uses: actions/deploy-pages@main 65 | -------------------------------------------------------------------------------- /.github/workflows/nix.yml: -------------------------------------------------------------------------------- 1 | name: "nix-test" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | pull_request: 7 | 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3.3.0 13 | - uses: nixbuild/nix-quick-install-action@v22 14 | with: 15 | nix_conf: | 16 | experimental-features = nix-command flakes 17 | access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} 18 | 19 | - uses: cachix/cachix-action@v12 20 | with: 21 | name: zeek 22 | 23 | - name: Nix Flake Show 24 | run: nix flake show --all-systems 25 | 26 | - name: Check nix develop 27 | run: nix develop 28 | 29 | - name: Build wasm-bpf 30 | run: nix build .#wasm-bpf 31 | -------------------------------------------------------------------------------- /.github/workflows/publish-example-images.yml: -------------------------------------------------------------------------------- 1 | name: Publish examples images to ghcr 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | env: 7 | GITHUB_REPO_OWNER: ${{ github.repository_owner }} 8 | jobs: 9 | build-examples: 10 | strategy: 11 | matrix: 12 | example-name: [bootstrap, bootstrap-libbpf-rs, execve, go-execve, go-lsm, lsm, opensnoop, runqlat, rust-bootstrap, sockfilter, sockops, tcpconnlat-libbpf-rs, uprobe, xdp] 13 | runs-on: ubuntu-latest 14 | name: Build example ${{ matrix.example-name }} and publish to ghcr.io 15 | steps: 16 | - uses: actions/checkout@v3 17 | with: 18 | submodules: 'recursive' 19 | - name: Install ecli 20 | run: | 21 | sudo apt install -y libelf-dev zlib1g-dev llvm 22 | cargo install ecli-rs 23 | - name: Install wasi-sdk 24 | run: | 25 | wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-20/wasi-sdk-20.0-linux.tar.gz -O /opt/wasi-sdk.tar.gz 26 | cd /opt 27 | tar -zxvf wasi-sdk.tar.gz 28 | mv wasi-sdk-20.0 wasi-sdk 29 | /opt/wasi-sdk/bin/clang --version 30 | - name: Install tinygo 31 | if: startsWith(matrix.example-name, 'go') 32 | run: | 33 | wget https://github.com/tinygo-org/tinygo/releases/download/v0.28.1/tinygo_0.28.1_amd64.deb 34 | sudo dpkg -i tinygo_0.28.1_amd64.deb 35 | tinygo version 36 | - name: downcase REPO_OWNER 37 | run: | 38 | echo "REPO_OWNER=${GITHUB_REPO_OWNER,,}" >>${GITHUB_ENV} 39 | - name: use REPO 40 | run: | 41 | echo "The value of REPO_OWNER is: ${REPO_OWNER}" 42 | - name: Login to ghcr.io 43 | uses: docker/login-action@v2 44 | with: 45 | registry: ghcr.io 46 | username: ${{ github.repository_owner }} 47 | password: ${{ secrets.GITHUB_TOKEN }} 48 | - name: Build the example and push it to ghcr 49 | run: | 50 | cd examples/${{matrix.example-name}} 51 | make 52 | ecli-rs push -m $(ls *.wasm) ghcr.io/$REPO_OWNER/${{matrix.example-name}}:latest -a org.opencontainers.image.title=${{matrix.example-name}} 53 | -------------------------------------------------------------------------------- /.github/workflows/release-container.yml: -------------------------------------------------------------------------------- 1 | name: Build and public wasm-bpf docker image 2 | 3 | on: 4 | push: 5 | branches: "main" 6 | 7 | jobs: 8 | # define job to build and publish docker image 9 | build-and-push-wasm-bpf-image: 10 | runs-on: ubuntu-latest 11 | # run only when code is compiling and tests are passing 12 | if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[ci skip]')" 13 | # steps to perform in job 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v3 17 | with: 18 | submodules: 'recursive' 19 | 20 | # setup Docker buld action 21 | - name: Set up Docker Buildx 22 | id: buildx 23 | uses: docker/setup-buildx-action@v2 24 | 25 | - name: Login to Github Packages 26 | uses: docker/login-action@v2 27 | if: github.repository_owner == 'eunomia-bpf' 28 | with: 29 | registry: ghcr.io 30 | username: ${{ github.repository_owner }} 31 | password: ${{ secrets.GITHUB_TOKEN }} 32 | - name: Install dependencies 33 | run: sudo make install-deps 34 | 35 | - name: Cache rust 36 | uses: Swatinem/rust-cache@v2 37 | with: 38 | workspaces: runtime 39 | 40 | - name: Build runtime 41 | run: make build-rust 42 | 43 | - name: Build wasm-bpf image and push to GitHub Container Registry 44 | uses: docker/build-push-action@v2 45 | if: github.repository_owner == 'eunomia-bpf' 46 | with: 47 | # relative path to the place where source code with Dockerfile is located 48 | context: ./ 49 | file: ./Dockerfile 50 | platforms: linux/arm64 51 | # Note: tags has to be all lower-case 52 | tags: | 53 | ghcr.io/${{ github.repository_owner }}/wasm-bpf:latest 54 | push: true 55 | 56 | - name: Image digest 57 | run: echo ${{ steps.docker_build.outputs.digest }} 58 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Test rust runtime 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | with: 19 | submodules: 'recursive' 20 | 21 | - name: install deps 22 | run: | 23 | sudo make install-deps 24 | make /opt/wasi-sdk 25 | make tinygo 26 | 27 | - name: Cache rust 28 | uses: Swatinem/rust-cache@v2 29 | with: 30 | workspaces: runtime 31 | 32 | - name: Build 33 | run: make build-rust 34 | - name: test wasm-bpf-rs 35 | run: cd runtime/wasm-bpf-rs && make test 36 | - name: Upload analysis results to GitHub 37 | uses: github/codeql-action/upload-sarif@v2 38 | if: github.repository_owner == 'eunomia-bpf' 39 | with: 40 | sarif_file: runtime/wasm-bpf-rs/rust-clippy-results.sarif 41 | wait-for-processing: true 42 | 43 | - name: Code coverage using Codecov 44 | if: github.repository_owner == 'eunomia-bpf' 45 | run: bash <(curl -s https://codecov.io/bash) 46 | - name: examples with rust runtime 47 | run: IMPL=rust make -C examples 48 | - name: Upload build result 49 | uses: actions/upload-artifact@v2.3.1 50 | if: github.repository_owner == 'eunomia-bpf' 51 | with: 52 | path: "wasm-bpf-rs" 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv 2 | .vscode/ 3 | build/ 4 | result 5 | examples/wasm-bpf 6 | wasi-sdk-*-linux.tar.gz 7 | tinygo_0.27.0_amd64.deb 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third_party/bpftool"] 2 | path = runtime/cpp/third_party/bpftool 3 | url = https://github.com/eunomia-bpf/bpftool 4 | [submodule "third_party/wasm-micro-runtime"] 5 | path = runtime/cpp/third_party/wasm-micro-runtime 6 | url = https://github.com/bytecodealliance/wasm-micro-runtime 7 | [submodule "wasm-sdk/c"] 8 | path = wasm-sdk/c 9 | url = https://github.com/eunomia-bpf/wasm-bpf-guest-sdk-c 10 | [submodule "wasm-sdk/go_sdk"] 11 | path = wasm-sdk/go_sdk 12 | url = https://github.com/eunomia-bpf/wasm-bpf-guest-sdk-go 13 | [submodule "wasm-sdk/rust/wasm-bpf-binding"] 14 | path = wasm-sdk/rust/wasm-bpf-binding 15 | url = https://github.com/eunomia-bpf/wasm-bpf-guest-sdk-rust 16 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:22.04 2 | RUN apt-get update \ 3 | && apt-get install -y --no-install-recommends libelf1 \ 4 | && rm -rf /var/lib/apt/lists/* 5 | 6 | COPY ./runtime/target/release/wasm-bpf /root/wasm-bpf 7 | 8 | WORKDIR /root 9 | 10 | ENTRYPOINT ["./wasm-bpf"] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 eunomia-bpf org. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: build-rust build-cpp 2 | 3 | build-rust: 4 | $(MAKE) -C runtime/cli build 5 | cp ./runtime/target/release/wasm-bpf ./wasm-bpf-rs 6 | 7 | build-cpp: 8 | $(MAKE) -C runtime/cpp build 9 | cp ./runtime/cpp/build/bin/Release/wasm-bpf . 10 | 11 | clean: 12 | $(MAKE) -C examples clean 13 | $(MAKE) -C runtime/cpp clean 14 | $(MAKE) -C runtime/cli clean 15 | rm -rf wasm-bpf-rs 16 | rm -rf wasm-bpf 17 | 18 | install-deps: ## install deps 19 | apt update 20 | apt-get install libcurl4-openssl-dev libelf-dev clang llvm pahole zlib1g-dev -y ## libgtest-dev 21 | 22 | /opt/wasi-sdk: 23 | wget https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-17/wasi-sdk-17.0-linux.tar.gz 24 | tar -zxf wasi-sdk-17.0-linux.tar.gz 25 | sudo mkdir -p /opt/wasi-sdk/ && sudo mv wasi-sdk-17.0/* /opt/wasi-sdk/ 26 | 27 | tinygo: 28 | wget https://github.com/tinygo-org/tinygo/releases/download/v0.27.0/tinygo_0.27.0_amd64.deb 29 | sudo dpkg -i tinygo_0.27.0_amd64.deb 30 | 31 | test: 32 | rm -rf runtime/cpp/build 33 | cd runtime/cpp && mkdir build && cd build && cmake .. && make 34 | -------------------------------------------------------------------------------- /codecov.yaml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "examples" 3 | 4 | coverage: 5 | status: 6 | project: 7 | default: 8 | target: auto 9 | # adjust accordingly based on how flaky your tests are 10 | # this allows a 0.5% drop from the previous base commit coverage 11 | threshold: 3% 12 | 13 | patch: 14 | default: 15 | target: auto 16 | base: auto 17 | only_pulls: false 18 | -------------------------------------------------------------------------------- /docs/WASI-bpf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/docs/WASI-bpf.png -------------------------------------------------------------------------------- /docs/build-image.md: -------------------------------------------------------------------------------- 1 | ## Docker image 2 | 3 | build the docker images 4 | 5 | ```sh 6 | make build-rust 7 | docker build -t ghcr.io/eunomia-bpf/wasm-bpf:latest . 8 | ``` 9 | 10 | ```sh 11 | wget https://eunomia-bpf.github.io/wasm-bpf/examples/bootstrap/bootstrap.wasm 12 | docker run --rm -it --privileged \ 13 | -v $(pwd):/examples \ 14 | -v /sys/kernel/debug:/sys/kernel/debug:ro \ 15 | -v /sys/fs/cgroup:/sys/fs/cgroup:ro \ 16 | ghcr.io/eunomia-bpf/wasm-bpf:latest /examples/bootstrap.wasm 17 | ``` 18 | -------------------------------------------------------------------------------- /docs/build.md: -------------------------------------------------------------------------------- 1 | # Build the runtime and examples 2 | 3 | We have two types of runtime examples: 4 | 5 | - A C/C++ runtime example, which is a minimal runtime based on WAMR. see [runtime/cpp](../runtime/cpp) for more details. 6 | - A Rust runtime example, which is a more complex runtime based on Wasmtime. see [runtime/cli](../runtime/cli) for more details. 7 | 8 | A new runtime is easy to implement with only a few hundred lines of code, in any language, using any wasm runtime or any ebpf user space library. 9 | 10 | ## Build the C++ minimal runtime based on WAMR[^1] 11 | 12 | The dependencies are libbpf and wasm-micro-runtime only, they are 13 | registered as git submodules. 14 | 15 | ```sh 16 | git submodule update --init --recursive 17 | cd runtime/cpp 18 | ``` 19 | 20 | ### Install Dependencies 21 | 22 | You will need `clang`, `libelf` and `zlib` to build the examples, 23 | package names may vary across distros. 24 | 25 | on Ubuntu/Debian, you need: 26 | 27 | ```shell 28 | sudo apt install clang libelf1 libelf-dev zlib1g-dev 29 | ``` 30 | 31 | on CentOS / Fedora, you need: 32 | 33 | ```shell 34 | sudo dnf install clang elfutils-libelf elfutils-libelf-devel zlib-devel 35 | ``` 36 | 37 | ### Build runtime as a executable tool 38 | 39 | Run `make` in the `runtime/cpp` directory to build the runtime, which will be placed in the `build` 40 | directory. `cmake` is required to build the runtime. 41 | 42 | ```sh 43 | make build 44 | ``` 45 | 46 | ### Build runtime as a library 47 | 48 | ```sh 49 | make build-lib 50 | ``` 51 | 52 | You may refer to [CI](.github/workflows/c-cpp.yml) for more details on how 53 | to build and run the examples. 54 | 55 | [^1]: WAMR (WebAssembly Micro Runtime): https://github.com/bytecodealliance/wasm-micro-runtime 56 | 57 | ## Build the Rust runtime based on Wasmtime[^2] 58 | 59 | install rust toolchain 60 | 61 | ```shell 62 | curl https://sh.rustup.rs -sSf | sh -s 63 | ``` 64 | 65 | ### Build runtime as a executable tool 66 | 67 | Run `make` in the `runtime/cli` directory to build the runtime, which will be placed in the `target` 68 | directory. 69 | 70 | ```sh 71 | make build 72 | ``` 73 | 74 | [^2]: wasmtime: https://github.com/bytecodealliance/wasmtime 75 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/docs/logo.png -------------------------------------------------------------------------------- /docs/wasm-bpf-no-bcc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/docs/wasm-bpf-no-bcc.png -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.bpf.o 2 | *.skel.h 3 | *.wasm 4 | */vmlinux.h 5 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all bootstrap runqlat 2 | all: test 3 | 4 | 5 | TEST_TIME := 3 6 | 7 | # test with the files in bpf-loader 8 | TEST_CASES_DIRS=$(shell ls -l . | grep ^d | grep -v 'go-lsm\|go-execve' | awk '{print $$9}') 9 | test: $(TEST_CASES_DIRS) 10 | 11 | .PHONY:$(TEST_CASES_DIRS) 12 | # build the test cases 13 | $(TEST_CASES_DIRS): wasm-bpf 14 | make -C $@ 15 | make -C $@ test 16 | 17 | clean: 18 | for name in $(TEST_CASES_DIRS); do \ 19 | $(MAKE) -C $$name clean; \ 20 | done 21 | rm -rf wasm-bpf 22 | 23 | wasm-bpf: 24 | case $$IMPL in \ 25 | cpp) \ 26 | make wasm-bpf-cpp \ 27 | ;; \ 28 | rust) \ 29 | make wasm-bpf-rs \ 30 | ;; \ 31 | *) \ 32 | echo "\e[31mNo runtime specified. Set env variable IMPL to either \"rust\" or \"cpp\" to specify which runtime to use\e[0m" && \ 33 | exit 1 \ 34 | ;; \ 35 | esac 36 | 37 | wasm-bpf-cpp: 38 | make -C ../runtime/cpp 39 | cp ../runtime/cpp/build/bin/Release/wasm-bpf . 40 | 41 | wasm-bpf-rs: 42 | cd ../runtime/cli && cargo build --release 43 | cp ../runtime/target/release/wasm-bpf wasm-bpf -------------------------------------------------------------------------------- /examples/bootstrap-libbpf-rs/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.o 3 | wit/event-def.wit 4 | wasm-bpf-binding/target 5 | dump.rs 6 | Cargo.lock 7 | -------------------------------------------------------------------------------- /examples/bootstrap-libbpf-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bootstrap-libbpf-rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.71" 10 | wasm-bpf-libbpf-rs = { git = "https://github.com/eunomia-bpf/wasm-bpf-guest-sdk-rust" } 11 | -------------------------------------------------------------------------------- /examples/bootstrap-libbpf-rs/Makefile: -------------------------------------------------------------------------------- 1 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 2 | THIRD_PARTY := ../../runtime/cpp/third_party 3 | 4 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 5 | BPF_HEADERS := $(THIRD_PARTY)/ 6 | # Use our own libbpf API headers and Linux UAPI headers distributed with 7 | # libbpf to avoid dependency on system-wide headers, which could be missing or 8 | # outdated 9 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 10 | CFLAGS := -g -Wall 11 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 12 | CLANG := clang 13 | LLVM_STRIP := llvm-strip 14 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 15 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 16 | 17 | 18 | # Get Clang's default includes on this system. We'll explicitly add these dirs 19 | # to the includes list when compiling with `-target bpf` because otherwise some 20 | # architecture-specific dirs will be "missing" on some architectures/distros - 21 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 22 | # sys/cdefs.h etc. might be missing. 23 | # 24 | # Use '-idirafter': Don't interfere with include mechanics except where the 25 | # build would have failed anyways. 26 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 27 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 28 | 29 | APP = bootstrap 30 | 31 | .PHONY: all 32 | all: build 33 | 34 | .PHONY: clean 35 | clean: 36 | cargo clean 37 | rm -rf *.o *.json *.wasm *.skel.h 38 | 39 | # Build BPF code 40 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 41 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 42 | llvm-strip -g $@ # strip useless DWARF info 43 | 44 | .PHONY: build 45 | build: $(APP).bpf.o 46 | rustup target add wasm32-wasi 47 | cargo build --target wasm32-wasi --release 48 | cp target/wasm32-wasi/release/bootstrap-libbpf-rs.wasm bootstrap-libbpf-rs.wasm 49 | 50 | TEST_TIME := 3 51 | .PHONY: test 52 | test: 53 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf bootstrap-libbpf-rs.wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 54 | -------------------------------------------------------------------------------- /examples/bootstrap-libbpf-rs/README.md: -------------------------------------------------------------------------------- 1 | # bootstrap 2 | 3 | `bootstrap`, but using the `libbpf-rs`-like SDK. 4 | -------------------------------------------------------------------------------- /examples/bootstrap-libbpf-rs/bootstrap.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2020 Facebook */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include "bootstrap.h" 8 | 9 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 10 | 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_HASH); 13 | __uint(max_entries, 8192); 14 | __type(key, pid_t); 15 | __type(value, u64); 16 | } exec_start SEC(".maps"); 17 | 18 | struct { 19 | __uint(type, BPF_MAP_TYPE_RINGBUF); 20 | __uint(max_entries, 256 * 1024); 21 | } rb SEC(".maps"); 22 | 23 | const volatile unsigned long long min_duration_ns = 0; 24 | 25 | SEC("tp/sched/sched_process_exec") 26 | int handle_exec(struct trace_event_raw_sched_process_exec* ctx) { 27 | struct task_struct* task; 28 | unsigned fname_off; 29 | struct event* e; 30 | pid_t pid; 31 | u64 ts; 32 | 33 | /* remember time exec() was executed for this PID */ 34 | pid = bpf_get_current_pid_tgid() >> 32; 35 | ts = bpf_ktime_get_ns(); 36 | bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY); 37 | 38 | /* don't emit exec events when minimum duration is specified */ 39 | if (min_duration_ns) 40 | return 0; 41 | 42 | /* reserve sample from BPF ringbuf */ 43 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 44 | if (!e) 45 | return 0; 46 | 47 | /* fill out the sample with data */ 48 | task = (struct task_struct*)bpf_get_current_task(); 49 | 50 | e->exit_event = false; 51 | e->pid = pid; 52 | e->ppid = BPF_CORE_READ(task, real_parent, tgid); 53 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 54 | 55 | fname_off = ctx->__data_loc_filename & 0xFFFF; 56 | bpf_probe_read_str(&e->filename, sizeof(e->filename), 57 | (void*)ctx + fname_off); 58 | 59 | /* successfully submit it to user-space for post-processing */ 60 | bpf_ringbuf_submit(e, 0); 61 | return 0; 62 | } 63 | 64 | SEC("tp/sched/sched_process_exit") 65 | int handle_exit(struct trace_event_raw_sched_process_template* ctx) { 66 | struct task_struct* task; 67 | struct event* e; 68 | pid_t pid, tid; 69 | u64 id, ts, *start_ts, duration_ns = 0; 70 | 71 | /* get PID and TID of exiting thread/process */ 72 | id = bpf_get_current_pid_tgid(); 73 | pid = id >> 32; 74 | tid = (u32)id; 75 | 76 | /* ignore thread exits */ 77 | if (pid != tid) 78 | return 0; 79 | 80 | /* if we recorded start of the process, calculate lifetime duration */ 81 | start_ts = bpf_map_lookup_elem(&exec_start, &pid); 82 | if (start_ts) 83 | duration_ns = bpf_ktime_get_ns() - *start_ts; 84 | else if (min_duration_ns) 85 | return 0; 86 | bpf_map_delete_elem(&exec_start, &pid); 87 | 88 | /* if process didn't live long enough, return early */ 89 | if (min_duration_ns && duration_ns < min_duration_ns) 90 | return 0; 91 | 92 | /* reserve sample from BPF ringbuf */ 93 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 94 | if (!e) 95 | return 0; 96 | 97 | /* fill out the sample with data */ 98 | task = (struct task_struct*)bpf_get_current_task(); 99 | 100 | e->exit_event = true; 101 | e->duration_ns = duration_ns; 102 | e->pid = pid; 103 | e->ppid = BPF_CORE_READ(task, real_parent, tgid); 104 | e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff; 105 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 106 | 107 | /* send data to user-space for post-processing */ 108 | bpf_ringbuf_submit(e, 0); 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /examples/bootstrap-libbpf-rs/bootstrap.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2020 Facebook */ 3 | #ifndef __BOOTSTRAP_H 4 | #define __BOOTSTRAP_H 5 | 6 | #define TASK_COMM_LEN 16 7 | #define MAX_FILENAME_LEN 127 8 | 9 | struct event { 10 | int pid; 11 | int ppid; 12 | unsigned int exit_code; 13 | char pad0[4]; 14 | unsigned long long duration_ns; 15 | char comm[16]; 16 | char filename[127]; 17 | char exit_event; 18 | } __attribute__((packed)); 19 | 20 | #endif /* __BOOTSTRAP_H */ 21 | -------------------------------------------------------------------------------- /examples/bootstrap-libbpf-rs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rustc-link-arg=--export-table") 3 | } 4 | -------------------------------------------------------------------------------- /examples/bootstrap-libbpf-rs/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{ffi::CStr, slice, time::Duration}; 2 | 3 | use anyhow::{anyhow, Context, Result}; 4 | use wasm_bpf_libbpf_rs::{object::ObjectBuilder, poll::PollBuilder}; 5 | 6 | #[repr(C, packed)] 7 | #[derive(Debug, Copy, Clone)] 8 | struct Event { 9 | pid: i32, 10 | ppid: i32, 11 | exit_code: u32, 12 | __pad0: [u8; 4], 13 | duration_ns: u64, 14 | comm: [u8; 16], 15 | filename: [u8; 127], 16 | exit_event: u8, 17 | } 18 | 19 | fn main() -> Result<()> { 20 | let bpf_object = include_bytes!("../bootstrap.bpf.o"); 21 | let obj = ObjectBuilder::default() 22 | .open_memory(bpf_object) 23 | .with_context(|| anyhow!("Failed to open"))? 24 | .load() 25 | .with_context(|| anyhow!("Failed to load"))?; 26 | obj.prog("handle_exec").unwrap().attach()?; 27 | let map = obj.map("rb").unwrap(); 28 | let mut buf = [0u8; 2048]; 29 | let poll = PollBuilder::new(&map, &mut buf) 30 | .sample_cb(|v| { 31 | let event_slice = unsafe { slice::from_raw_parts(v.as_ptr() as *const Event, 1) }; 32 | let event = &event_slice[0]; 33 | let pid = event.pid; 34 | let ppid = event.ppid; 35 | let exit_code = event.exit_code; 36 | if event.exit_event == 1 { 37 | print!( 38 | "{:<8} {:<5} {:<16} {:<7} {:<7} [{}]", 39 | "TIME", 40 | "EXIT", 41 | unsafe { CStr::from_ptr(event.comm.as_ptr() as *const i8) } 42 | .to_str() 43 | .unwrap(), 44 | pid, 45 | ppid, 46 | exit_code 47 | ); 48 | if event.duration_ns != 0 { 49 | print!(" ({}ms)", event.duration_ns / 1000000); 50 | } 51 | println!(); 52 | } else { 53 | println!( 54 | "{:<8} {:<5} {:<16} {:<7} {:<7} {}", 55 | "TIME", 56 | "EXEC", 57 | unsafe { CStr::from_ptr(event.comm.as_ptr() as *const i8) } 58 | .to_str() 59 | .unwrap(), 60 | pid, 61 | ppid, 62 | unsafe { CStr::from_ptr(event.filename.as_ptr() as *const i8) } 63 | .to_str() 64 | .unwrap() 65 | ); 66 | } 67 | 0 68 | }) 69 | .build(); 70 | loop { 71 | poll.poll(Duration::from_millis(100))?; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/bootstrap/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY)/ 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = bootstrap 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | 40 | # Build BPF code 41 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 42 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 43 | llvm-strip -g $@ # strip useless DWARF info 44 | 45 | # compile bpftool 46 | $(BPFTOOL): 47 | cd $(BPFTOOL_SRC) && make 48 | 49 | # generate c skeleton 50 | %.skel.h: %.bpf.o $(BPFTOOL) 51 | $(BPFTOOL) gen skeleton -j $< > $@ 52 | 53 | # generate wasm bpf header for pass struct event 54 | $(APP).wasm.h: $(APP).bpf.o $(BPFTOOL) 55 | ecc $(APP).h --header-only 56 | $(BPFTOOL) btf dump file $< format c -j > $@ 57 | 58 | # compile for wasm with wasi-sdk 59 | WASI_CLANG = /opt/wasi-sdk/bin/clang 60 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 61 | 62 | $(APP).wasm: $(APP).c $(APP).skel.h 63 | ln -f -s ../../wasm-sdk/c/libbpf-wasm.h libbpf-wasm.h 64 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 65 | 66 | TEST_TIME := 3 67 | .PHONY: test 68 | test: 69 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 70 | -------------------------------------------------------------------------------- /examples/bootstrap/bootstrap.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2020 Facebook */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include "bootstrap.h" 8 | 9 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 10 | 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_HASH); 13 | __uint(max_entries, 8192); 14 | __type(key, pid_t); 15 | __type(value, u64); 16 | } exec_start SEC(".maps"); 17 | 18 | struct { 19 | __uint(type, BPF_MAP_TYPE_RINGBUF); 20 | __uint(max_entries, 256 * 1024); 21 | } rb SEC(".maps"); 22 | 23 | const volatile unsigned long long min_duration_ns = 0; 24 | 25 | SEC("tp/sched/sched_process_exec") 26 | int handle_exec(struct trace_event_raw_sched_process_exec* ctx) { 27 | struct task_struct* task; 28 | unsigned fname_off; 29 | struct event* e; 30 | pid_t pid; 31 | u64 ts; 32 | 33 | /* remember time exec() was executed for this PID */ 34 | pid = bpf_get_current_pid_tgid() >> 32; 35 | ts = bpf_ktime_get_ns(); 36 | bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY); 37 | 38 | /* don't emit exec events when minimum duration is specified */ 39 | if (min_duration_ns) 40 | return 0; 41 | 42 | /* reserve sample from BPF ringbuf */ 43 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 44 | if (!e) 45 | return 0; 46 | 47 | /* fill out the sample with data */ 48 | task = (struct task_struct*)bpf_get_current_task(); 49 | 50 | e->exit_event = false; 51 | e->pid = pid; 52 | e->ppid = BPF_CORE_READ(task, real_parent, tgid); 53 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 54 | 55 | fname_off = ctx->__data_loc_filename & 0xFFFF; 56 | bpf_probe_read_str(&e->filename, sizeof(e->filename), 57 | (void*)ctx + fname_off); 58 | 59 | /* successfully submit it to user-space for post-processing */ 60 | bpf_ringbuf_submit(e, 0); 61 | return 0; 62 | } 63 | 64 | SEC("tp/sched/sched_process_exit") 65 | int handle_exit(struct trace_event_raw_sched_process_template* ctx) { 66 | struct task_struct* task; 67 | struct event* e; 68 | pid_t pid, tid; 69 | u64 id, ts, *start_ts, duration_ns = 0; 70 | 71 | /* get PID and TID of exiting thread/process */ 72 | id = bpf_get_current_pid_tgid(); 73 | pid = id >> 32; 74 | tid = (u32)id; 75 | 76 | /* ignore thread exits */ 77 | if (pid != tid) 78 | return 0; 79 | 80 | /* if we recorded start of the process, calculate lifetime duration */ 81 | start_ts = bpf_map_lookup_elem(&exec_start, &pid); 82 | if (start_ts) 83 | duration_ns = bpf_ktime_get_ns() - *start_ts; 84 | else if (min_duration_ns) 85 | return 0; 86 | bpf_map_delete_elem(&exec_start, &pid); 87 | 88 | /* if process didn't live long enough, return early */ 89 | if (min_duration_ns && duration_ns < min_duration_ns) 90 | return 0; 91 | 92 | /* reserve sample from BPF ringbuf */ 93 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 94 | if (!e) 95 | return 0; 96 | 97 | /* fill out the sample with data */ 98 | task = (struct task_struct*)bpf_get_current_task(); 99 | 100 | e->exit_event = true; 101 | e->duration_ns = duration_ns; 102 | e->pid = pid; 103 | e->ppid = BPF_CORE_READ(task, real_parent, tgid); 104 | e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff; 105 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 106 | 107 | /* send data to user-space for post-processing */ 108 | bpf_ringbuf_submit(e, 0); 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /examples/bootstrap/bootstrap.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | /* Copyright (c) 2020 Facebook */ 3 | #include 4 | #include 5 | #include 6 | #include "bootstrap.wasm.h" 7 | #include "bootstrap.skel.h" 8 | 9 | static struct env { 10 | bool verbose; 11 | long min_duration_ms; 12 | } env; 13 | 14 | const char* argp_program_version = "bootstrap 0.0"; 15 | const char* argp_program_bug_address = ""; 16 | const char argp_program_doc[] = 17 | "BPF bootstrap demo application.\n" 18 | "\n" 19 | "It traces process start and exits and shows associated \n" 20 | "information (filename, process duration, PID and PPID, etc).\n" 21 | "\n" 22 | "USAGE: ./bootstrap [-d ] -v\n"; 23 | 24 | static void print_usage(void) { 25 | printf("%s\n", argp_program_version); 26 | printf("%s\n", argp_program_doc); 27 | } 28 | 29 | static int handle_event(void* ctx, void* data, size_t data_sz) { 30 | const struct event* e = data; 31 | struct tm* tm; 32 | char ts[32]; 33 | time_t t; 34 | 35 | time(&t); 36 | tm = localtime(&t); 37 | strftime(ts, sizeof(ts), "%H:%M:%S", tm); 38 | 39 | if (e->exit_event) { 40 | printf("%-8s %-5s %-16s %-7d %-7d [%u]", ts, "EXIT", e->comm, e->pid, 41 | e->ppid, e->exit_code); 42 | if (e->duration_ns) 43 | printf(" (%llums)", e->duration_ns / 1000000); 44 | printf("\n"); 45 | } else { 46 | printf("%-8s %-5s %-16s %-7d %-7d %s\n", ts, "EXEC", e->comm, e->pid, 47 | e->ppid, e->filename); 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | static bool exiting = false; 54 | 55 | int main(int argc, char** argv) { 56 | struct bpf_buffer* rb = NULL; 57 | struct bootstrap_bpf* skel; 58 | int err; 59 | 60 | // parse the args manually for demo purpose 61 | if (argc > 3 || strcmp(argv[1], "-h") == 0 || 62 | strcmp(argv[1], "--help") == 0) { 63 | print_usage(); 64 | return 0; 65 | } else if ((strcmp(argv[1], "-d") == 0 || 66 | strcmp(argv[1], "--duration") == 0) && 67 | argc == 3) { 68 | env.min_duration_ms = strtol(argv[2], NULL, 10); 69 | } 70 | 71 | /* Load and verify BPF application */ 72 | skel = bootstrap_bpf__open(); 73 | if (!skel) { 74 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 75 | return 1; 76 | } 77 | 78 | /* Parameterize BPF code with minimum duration parameter */ 79 | skel->rodata->min_duration_ns = env.min_duration_ms * 1000000ULL; 80 | 81 | /* Load & verify BPF programs */ 82 | err = bootstrap_bpf__load(skel); 83 | if (err) { 84 | fprintf(stderr, "Failed to load and verify BPF skeleton\n"); 85 | goto cleanup; 86 | } 87 | 88 | /* Attach tracepoints */ 89 | err = bootstrap_bpf__attach(skel); 90 | if (err) { 91 | fprintf(stderr, "Failed to attach BPF skeleton\n"); 92 | goto cleanup; 93 | } 94 | 95 | /* Set up ring buffer polling */ 96 | rb = bpf_buffer__open(skel->maps.rb, handle_event, NULL); 97 | if (!rb) { 98 | err = -1; 99 | fprintf(stderr, "Failed to create ring buffer\n"); 100 | goto cleanup; 101 | } 102 | /* Process events */ 103 | printf("%-8s %-5s %-16s %-7s %-7s %s\n", "TIME", "EVENT", "COMM", "PID", 104 | "PPID", "FILENAME/EXIT CODE"); 105 | while (!exiting) { 106 | // poll buffer 107 | err = bpf_buffer__poll(rb, 100 /* timeout, ms */); 108 | /* Ctrl-C will cause -EINTR */ 109 | if (err == -EINTR) { 110 | err = 0; 111 | break; 112 | } 113 | if (err < 0) { 114 | printf("Error polling perf buffer: %d\n", err); 115 | break; 116 | } 117 | } 118 | 119 | cleanup: 120 | bpf_buffer__free(rb); 121 | bootstrap_bpf__destroy(skel); 122 | return err < 0 ? -err : 0; 123 | } 124 | -------------------------------------------------------------------------------- /examples/bootstrap/bootstrap.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2020 Facebook */ 3 | #ifndef __BOOTSTRAP_H 4 | #define __BOOTSTRAP_H 5 | 6 | #define TASK_COMM_LEN 16 7 | #define MAX_FILENAME_LEN 127 8 | 9 | struct event { 10 | int pid; 11 | int ppid; 12 | unsigned exit_code; 13 | unsigned long long duration_ns; 14 | char comm[TASK_COMM_LEN]; 15 | char filename[MAX_FILENAME_LEN]; 16 | char exit_event; 17 | }; 18 | 19 | #endif /* __BOOTSTRAP_H */ 20 | -------------------------------------------------------------------------------- /examples/bootstrap/bootstrap.wasm.h: -------------------------------------------------------------------------------- 1 | /* THIS FILE IS AUTOGENERATED BY BPFTOOL! */ 2 | #ifndef _BPF_WASM_EVENT_H_ 3 | #define _BPF_WASM_EVENT_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct event { 11 | int pid; 12 | int ppid; 13 | unsigned int exit_code; 14 | char __pad0[4]; 15 | unsigned long long duration_ns; 16 | char comm[16]; 17 | char filename[127]; 18 | char exit_event; 19 | } __attribute__((packed)); 20 | static_assert(sizeof(struct event) == 168, "Size of event is not 168"); 21 | 22 | #endif /* __VMLINUX_H__ */ 23 | -------------------------------------------------------------------------------- /examples/bootstrap/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /examples/execve/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY)/ 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = execve 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | 40 | # Build BPF code 41 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 42 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 43 | llvm-strip -g $@ # strip useless DWARF info 44 | 45 | # compile bpftool 46 | $(BPFTOOL): 47 | cd $(BPFTOOL_SRC) && make 48 | 49 | # generate c skeleton 50 | %.skel.h: %.bpf.o $(BPFTOOL) 51 | $(BPFTOOL) gen skeleton -j $< > $@ 52 | 53 | # generate wasm bpf header for pass struct event 54 | $(APP).wasm.h: $(APP).bpf.o $(BPFTOOL) 55 | ecc $(APP).h --header-only 56 | $(BPFTOOL) btf dump file $< format c -j > $@ 57 | 58 | # compile for wasm with wasi-sdk 59 | WASI_CLANG = /opt/wasi-sdk/bin/clang 60 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 61 | 62 | $(APP).wasm: $(APP).c $(APP).skel.h 63 | ln -f -s ../../wasm-sdk/c/libbpf-wasm.h libbpf-wasm.h 64 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 65 | 66 | TEST_TIME := 3 67 | .PHONY: test 68 | test: 69 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 70 | -------------------------------------------------------------------------------- /examples/execve/execve.bpf.c: -------------------------------------------------------------------------------- 1 | #include "execve.h" 2 | #include "vmlinux.h" 3 | #include 4 | 5 | char _license[4] SEC("license") = "GPL"; 6 | 7 | struct { 8 | __uint(type, BPF_MAP_TYPE_RINGBUF); 9 | __uint(max_entries, 256 * 1024); 10 | } comm_event SEC(".maps"); 11 | 12 | struct execve_args { 13 | struct trace_entry common; 14 | int unused; 15 | char* file; 16 | char* const* argv; 17 | char* const* envp; 18 | }; 19 | 20 | SEC("tp/syscalls/sys_enter_execve") 21 | int sys_enter_execve(struct execve_args* ctx) { 22 | struct comm_event comm; 23 | comm.pid = (int)(bpf_get_current_pid_tgid() & 0xFFFFFFFF); 24 | bpf_get_current_comm(&(comm.parent_proc[0]), sizeof(comm.parent_proc)); 25 | 26 | __builtin_memset(&(comm.command[0]), 0, sizeof(comm.command)); 27 | int start = 0; 28 | int end = COMM_SIZE - 1; 29 | 30 | char* args[MAX_ARG_NUM]; 31 | int idx = 0; 32 | for (; idx < MAX_ARG_NUM; idx++) { 33 | if (bpf_probe_read_user(&args[idx], sizeof(args[idx]), 34 | &ctx->argv[idx]) != 0) 35 | break; 36 | } 37 | 38 | for (int i = 0; i < idx && start < end; i++) { 39 | long n = 40 | bpf_probe_read_user_str(&comm.command[start], ARG_LEN, args[i]); 41 | if (n <= 0) { 42 | break; 43 | } 44 | start += (int)(n); 45 | comm.command[start - 1] = ' '; 46 | } 47 | if (start < end) { 48 | comm.command[start] = 0; 49 | } 50 | 51 | bpf_printk("pid: %d, proc: %s, execve: %s", comm.pid, comm.parent_proc, 52 | comm.command); 53 | 54 | bpf_ringbuf_output(&comm_event, &comm, sizeof(struct comm_event), 0); 55 | 56 | return 0; 57 | } -------------------------------------------------------------------------------- /examples/execve/execve.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "execve.skel.h" 4 | #include "libbpf-wasm.h" 5 | #include "execve.h" 6 | static int handle_event(void* ctx, void* data, size_t data_sz) { 7 | struct comm_event* st = (struct comm_event*)data; 8 | printf("[%d] %s -> %s\n", st->pid, st->parent_proc, st->command); 9 | return 0; 10 | } 11 | 12 | int main() { 13 | struct execve_bpf* skel = execve_bpf__open_and_load(); 14 | execve_bpf__attach(skel); 15 | struct bpf_buffer* buf = 16 | bpf_buffer__open(skel->maps.comm_event, handle_event, NULL); 17 | 18 | while (1) { 19 | if (bpf_buffer__poll(buf, 0) < 0) 20 | break; 21 | } 22 | return 0; 23 | } -------------------------------------------------------------------------------- /examples/execve/execve.h: -------------------------------------------------------------------------------- 1 | #define COMM_SIZE 352 2 | #define MAX_ARG_NUM 8 3 | 4 | #define ARG_LEN COMM_SIZE / MAX_ARG_NUM 5 | 6 | struct comm_event { 7 | int pid; 8 | char parent_proc[16]; 9 | char command[COMM_SIZE]; 10 | }; 11 | -------------------------------------------------------------------------------- /examples/execve/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /examples/go-execve/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY)/ 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | TINYGO ?= $(shell which tinygo) 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = execve 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | 40 | # Build BPF code 41 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 42 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 43 | llvm-strip -g $@ # strip useless DWARF info 44 | 45 | # compile bpftool 46 | $(BPFTOOL): 47 | cd $(BPFTOOL_SRC) && make 48 | 49 | $(APP).wasm: main.go $(APP).bpf.o 50 | $(TINYGO) build -target wasi -o $@ $< 51 | 52 | TEST_TIME := 3 53 | .PHONY: test 54 | test: 55 | case $$IMPL in \ 56 | cpp) \ 57 | echo "ignore in cpp" \ 58 | ;; \ 59 | rust) \ 60 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi \ 61 | ;; \ 62 | *) \ 63 | echo "\e[31mNo runtime specified. Set env variable IMPL to either \"rust\" or \"cpp\" to specify which runtime to use\e[0m" && \ 64 | exit 1 \ 65 | ;; \ 66 | esac -------------------------------------------------------------------------------- /examples/go-execve/README.md: -------------------------------------------------------------------------------- 1 | # go example of execve 2 | 3 | ## build 4 | 5 | install go and tinygo: 6 | 7 | ```sh 8 | wget https://github.com/tinygo-org/tinygo/releases/download/v0.27.0/tinygo_0.27.0_amd64.deb 9 | sudo dpkg -i tinygo_0.27.0_amd64.deb 10 | ``` 11 | 12 | run make: 13 | 14 | ``` 15 | make 16 | ``` 17 | -------------------------------------------------------------------------------- /examples/go-execve/execve.bpf.c: -------------------------------------------------------------------------------- 1 | #include "execve.h" 2 | #include "vmlinux.h" 3 | #include 4 | 5 | char _license[4] SEC("license") = "GPL"; 6 | 7 | struct { 8 | __uint(type, BPF_MAP_TYPE_RINGBUF); 9 | __uint(max_entries, 256 * 1024); 10 | } comm_event SEC(".maps"); 11 | 12 | struct execve_args { 13 | struct trace_entry common; 14 | int unused; 15 | char* file; 16 | char* const* argv; 17 | char* const* envp; 18 | }; 19 | 20 | SEC("tp/syscalls/sys_enter_execve") 21 | int sys_enter_execve(struct execve_args* ctx) { 22 | struct comm_event comm; 23 | comm.pid = (int)(bpf_get_current_pid_tgid() & 0xFFFFFFFF); 24 | bpf_get_current_comm(&(comm.parent_proc[0]), sizeof(comm.parent_proc)); 25 | 26 | __builtin_memset(&(comm.command[0]), 0, sizeof(comm.command)); 27 | int start = 0; 28 | int end = COMM_SIZE - 1; 29 | 30 | char* args[MAX_ARG_NUM]; 31 | int idx = 0; 32 | for (; idx < MAX_ARG_NUM; idx++) { 33 | if (bpf_probe_read_user(&args[idx], sizeof(args[idx]), 34 | &ctx->argv[idx]) != 0) 35 | break; 36 | } 37 | 38 | for (int i = 0; i < idx && start < end; i++) { 39 | long n = 40 | bpf_probe_read_user_str(&comm.command[start], ARG_LEN, args[i]); 41 | if (n <= 0) { 42 | break; 43 | } 44 | start += (int)(n); 45 | comm.command[start - 1] = ' '; 46 | } 47 | if (start < end) { 48 | comm.command[start] = 0; 49 | } 50 | 51 | bpf_printk("pid: %d, proc: %s, execve: %s", comm.pid, comm.parent_proc, 52 | comm.command); 53 | 54 | bpf_ringbuf_output(&comm_event, &comm, sizeof(struct comm_event), 0); 55 | 56 | return 0; 57 | } -------------------------------------------------------------------------------- /examples/go-execve/execve.h: -------------------------------------------------------------------------------- 1 | #define COMM_SIZE 352 2 | #define MAX_ARG_NUM 8 3 | 4 | #define ARG_LEN COMM_SIZE / MAX_ARG_NUM 5 | 6 | struct comm_event { 7 | int pid; 8 | char parent_proc[16]; 9 | char command[COMM_SIZE]; 10 | }; -------------------------------------------------------------------------------- /examples/go-execve/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | _ "embed" 6 | "fmt" 7 | helper "github.com/eunomia-bpf/wasm-bpf/wasm-sdk/go_sdk" 8 | "unsafe" 9 | ) 10 | 11 | type CString struct { 12 | s []byte 13 | } 14 | 15 | func (c CString) new(s string) *CString { 16 | c.s = bytes.Join([][]byte{[]byte(s), {0}}, nil) 17 | return &c 18 | } 19 | 20 | func (c *CString) toWasmPtr() int32 { 21 | return int32(uintptr(unsafe.Pointer(&(c.s[0])))) 22 | 23 | } 24 | 25 | //go:embed execve.bpf.o 26 | var obj []byte 27 | 28 | //export go-callback 29 | func callback(ctx, data, size uint32) uint32 { 30 | /* 31 | #define COMM_SIZE 352 32 | struct comm_event { 33 | int pid; 34 | char parent_proc[16]; 35 | char command[COMM_SIZE]; 36 | }; 37 | */ 38 | ptr := (unsafe.Pointer(uintptr(data))) 39 | pid := (unsafe.Slice((*int32)(ptr), 1))[0] 40 | byte_arr := unsafe.Slice((*byte)(ptr), size) 41 | parent_proc := string(byte_arr[4 : 4+16]) 42 | command := string(byte_arr[4+16 : 4+16+352]) 43 | 44 | fmt.Printf("[%d] %s -> %s\n", pid, parent_proc, command) 45 | return 0 46 | } 47 | 48 | func main() { 49 | var bpfObj int32 = int32(uintptr(unsafe.Pointer(&obj[0]))) 50 | objPtr := helper.WasmLoadBpfObject(bpfObj, int32(len(obj))) 51 | helper.WasmAttachBpfProgram(objPtr, CString{}.new("sys_enter_execve").toWasmPtr(), 0) 52 | mapFd := helper.WasmBpfMapFdByName(objPtr, CString{}.new("comm_event").toWasmPtr()) 53 | buf := make([]byte, 512, 512) 54 | 55 | for { 56 | helper.PerfBufferPoll(objPtr, mapFd, 0, int32(uintptr(unsafe.Pointer(&buf[0]))), int32(len(buf)), 100) 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /examples/go-lsm/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY)/ 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | TINYGO ?= $(shell which tinygo) 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = lsm 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | 40 | # Build BPF code 41 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 42 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 43 | llvm-strip -g $@ # strip useless DWARF info 44 | 45 | # compile bpftool 46 | $(BPFTOOL): 47 | cd $(BPFTOOL_SRC) && make 48 | 49 | $(APP).wasm: main.go $(APP).bpf.o 50 | $(TINYGO) build -target wasi -o $@ $< 51 | 52 | TEST_TIME := 3 53 | .PHONY: test 54 | test: 55 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi -------------------------------------------------------------------------------- /examples/go-lsm/lsm.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | 4 | char _license[] SEC("license") = "GPL"; 5 | 6 | // all lsm the hook point refer https://www.kernel.org/doc/html/v5.2/security/LSM.html 7 | SEC("lsm/path_rmdir") 8 | int path_rmdir(const struct path* dir, struct dentry* dentry) { 9 | char comm[16]; 10 | bpf_get_current_comm(comm, sizeof(comm)); 11 | 12 | unsigned char dir_name[] = "can_not_rm"; 13 | unsigned char d_iname[32]; 14 | bpf_probe_read_kernel(&d_iname[0], sizeof(d_iname), 15 | &(dir->dentry->d_iname[0])); 16 | 17 | bpf_printk("comm %s try to rmdir %s", comm, d_iname); 18 | for (int i = 0; i < sizeof(dir_name); i++) { 19 | if (d_iname[i] != dir_name[i]) { 20 | return 0; 21 | } 22 | } 23 | 24 | return -1; 25 | } 26 | -------------------------------------------------------------------------------- /examples/go-lsm/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | _ "embed" 6 | "fmt" 7 | helper "github.com/eunomia-bpf/wasm-bpf/wasm-sdk/go_sdk" 8 | "time" 9 | "unsafe" 10 | ) 11 | 12 | type CString struct { 13 | s []byte 14 | } 15 | 16 | func (c CString) new(s string) *CString { 17 | c.s = bytes.Join([][]byte{[]byte(s), {0}}, nil) 18 | return &c 19 | } 20 | 21 | func (c *CString) toWasmPtr() int32 { 22 | return int32(uintptr(unsafe.Pointer(&(c.s[0])))) 23 | 24 | } 25 | 26 | //go:embed lsm.bpf.o 27 | var obj []byte 28 | 29 | func main() { 30 | var bpfObj int32 = int32(uintptr(unsafe.Pointer(&obj[0]))) 31 | objPtr := helper.WasmLoadBpfObject(bpfObj, int32(len(obj))) 32 | fmt.Println("objptr=", objPtr) 33 | attachResult := helper.WasmAttachBpfProgram(objPtr, CString{}.new("path_rmdir").toWasmPtr(), 0) 34 | fmt.Println("attachResult=", attachResult) 35 | for { 36 | time.Sleep(10 * time.Second) 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /examples/lsm/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY)/ 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = lsm 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | cd go_wasm/ && rm -rf *.o *.json *.wasm *.skel.h 40 | 41 | # Build BPF code 42 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 43 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 44 | llvm-strip -g $@ # strip useless DWARF info 45 | 46 | # compile bpftool 47 | $(BPFTOOL): 48 | cd $(BPFTOOL_SRC) && make 49 | 50 | # generate c skeleton 51 | %.skel.h: %.bpf.o $(BPFTOOL) 52 | $(BPFTOOL) gen skeleton -j $< > $@ 53 | 54 | # generate wasm bpf header for pass struct event 55 | $(APP).wasm.h: $(APP).bpf.o $(BPFTOOL) 56 | ecc $(APP).h --header-only 57 | $(BPFTOOL) btf dump file $< format c -j > $@ 58 | 59 | # compile for wasm with wasi-sdk 60 | WASI_CLANG = /opt/wasi-sdk/bin/clang 61 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 62 | 63 | $(APP).wasm: $(APP).c $(APP).skel.h 64 | ln -f -s ../../wasm-sdk/c/libbpf-wasm.h libbpf-wasm.h 65 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 66 | 67 | $(APP)_go_wasm: $(APP).bpf.o 68 | cp ./$(APP).bpf.o go-lsm 69 | cd go-lsm && $(TINYGO) build -target wasi -o $(APP).go.wasm main.go 70 | 71 | 72 | TEST_TIME := 3 73 | .PHONY: test 74 | test: 75 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 76 | -------------------------------------------------------------------------------- /examples/lsm/README.md: -------------------------------------------------------------------------------- 1 | # Demo BPF applications 2 | 3 | ## lsm-rmdir 4 | 5 | `lsm-rmdir` hook in dir remove and check the permission to remove a directory. If dir 6 | name with `can_not_rm` will raise Operation not permitted. 7 | 8 | We can provide a similar developing experience as the [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap) development. Just run `make` to build the wasm binary: 9 | 10 | ```sh 11 | make 12 | ``` 13 | 14 | Note: LSM may failed to load if the kernel is not configured as: 15 | 16 | ```sh 17 | CONFIG_DEBUG_INFO_BTF=y 18 | CONFIG_BPF_LSM=y 19 | CONFIG_LSM="[other LSMs],bpf" 20 | ``` 21 | 22 | go wasm example(need tinygo): 23 | ```sh 24 | make lsm_go_wasm # will output to go_wasm/lsm.go.wasm 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/lsm/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /examples/lsm/lsm.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | 4 | char _license[] SEC("license") = "GPL"; 5 | 6 | // all lsm the hook point refer https://www.kernel.org/doc/html/v5.2/security/LSM.html 7 | SEC("lsm/path_rmdir") 8 | int path_rmdir(const struct path* dir, struct dentry* dentry) { 9 | char comm[16]; 10 | bpf_get_current_comm(comm, sizeof(comm)); 11 | 12 | unsigned char dir_name[] = "can_not_rm"; 13 | unsigned char d_iname[32]; 14 | bpf_probe_read_kernel(&d_iname[0], sizeof(d_iname), 15 | &(dir->dentry->d_iname[0])); 16 | 17 | bpf_printk("comm %s try to rmdir %s", comm, d_iname); 18 | for (int i = 0; i < sizeof(dir_name); i++) { 19 | if (d_iname[i] != dir_name[i]) { 20 | return 0; 21 | } 22 | } 23 | 24 | return -1; 25 | } 26 | -------------------------------------------------------------------------------- /examples/lsm/lsm.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "lsm.skel.h" 8 | 9 | int main(void) { 10 | struct lsm_bpf* skel = NULL; 11 | int err; 12 | 13 | skel = lsm_bpf__open_and_load(); 14 | if (!skel) { 15 | printf("Failed to open and load BPF skeleton\n"); 16 | return -1; 17 | } 18 | 19 | err = lsm_bpf__attach(skel); 20 | if (err) { 21 | printf("Failed to attach BPF skeleton\n"); 22 | return -1; 23 | } 24 | printf("Load and attach BPF lsm successfully\n"); 25 | while (1) { 26 | sleep(10); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/opensnoop/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY)/ 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = opensnoop 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | 40 | # Build BPF code 41 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 42 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 43 | llvm-strip -g $@ # strip useless DWARF info 44 | 45 | # compile bpftool 46 | $(BPFTOOL): 47 | cd $(BPFTOOL_SRC) && make 48 | 49 | # generate c skeleton 50 | %.skel.h: %.bpf.o $(BPFTOOL) 51 | $(BPFTOOL) gen skeleton -j $< > $@ 52 | 53 | # generate wasm bpf header for pass struct event 54 | $(APP).wasm.h: $(APP).bpf.o $(BPFTOOL) 55 | ecc $(APP).h --header-only 56 | $(BPFTOOL) btf dump file $< format c -j > $@ 57 | 58 | # compile for wasm with wasi-sdk 59 | WASI_CLANG = /opt/wasi-sdk/bin/clang 60 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 61 | 62 | $(APP).wasm: $(APP).c $(APP).skel.h 63 | ln -f -s ../../wasm-sdk/c/libbpf-wasm.h libbpf-wasm.h 64 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< trace_helpers.c 65 | 66 | # install emadk 67 | emsdk: 68 | git clone https://github.com/emscripten-core/emsdk.git 69 | cd emsdk && ./emsdk install latest && ./emsdk activate latest 70 | source ./emsdk_env.sh 71 | 72 | TEST_TIME := 3 73 | .PHONY: test 74 | test: 75 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 76 | -------------------------------------------------------------------------------- /examples/opensnoop/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /examples/opensnoop/opensnoop.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __OPENSNOOP_H 3 | #define __OPENSNOOP_H 4 | 5 | #define TASK_COMM_LEN 16 6 | #define NAME_MAX 255 7 | #define INVALID_UID ((uid_t)-1) 8 | 9 | typedef unsigned long long uint64_t; 10 | 11 | struct args_t { 12 | const char* fname; 13 | int flags; 14 | }; 15 | 16 | struct event { 17 | /* user terminology for pid: */ 18 | uint64_t ts; 19 | pid_t pid; 20 | uid_t uid; 21 | int ret; 22 | int flags; 23 | uint64_t callers[2]; 24 | char comm[TASK_COMM_LEN]; 25 | char fname[NAME_MAX]; 26 | }; 27 | 28 | #endif /* __OPENSNOOP_H */ -------------------------------------------------------------------------------- /examples/opensnoop/trace_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __TRACE_HELPERS_H 3 | #define __TRACE_HELPERS_H 4 | 5 | #include 6 | 7 | #define NSEC_PER_SEC 1000000000ULL 8 | 9 | void print_log2_hist(unsigned int* vals, int vals_size, const char* val_type); 10 | void print_linear_hist(unsigned int* vals, 11 | int vals_size, 12 | unsigned int base, 13 | unsigned int step, 14 | const char* val_type); 15 | 16 | unsigned long long get_ktime_ns(void); 17 | 18 | bool is_kernel_module(const char* name); 19 | 20 | /* 21 | * When attempting to use kprobe/kretprobe, please check out new fentry/fexit 22 | * probes, as they provide better performance and usability. But in some 23 | * situations we have to fallback to kprobe/kretprobe probes. This helper 24 | * is used to detect fentry/fexit support for the specified kernel function. 25 | * 26 | * 1. A gap between kernel versions, kernel BTF is exposed 27 | * starting from 5.4 kernel. but fentry/fexit is actually 28 | * supported starting from 5.5. 29 | * 2. Whether kernel supports module BTF or not 30 | * 31 | * *name* is the name of a kernel function to be attached to, which can be 32 | * from vmlinux or a kernel module. 33 | * *mod* is a hint that indicates the *name* may reside in module BTF, 34 | * if NULL, it means *name* belongs to vmlinux. 35 | */ 36 | bool fentry_can_attach(const char* name, const char* mod); 37 | 38 | /* 39 | * The name of a kernel function to be attached to may be changed between 40 | * kernel releases. This helper is used to confirm whether the target kernel 41 | * uses a certain function name before attaching. 42 | * 43 | * It is achieved by scaning 44 | * /sys/kernel/debug/tracing/available_filter_functions 45 | * If this file does not exist, it fallbacks to parse /proc/kallsyms, 46 | * which is slower. 47 | */ 48 | bool kprobe_exists(const char* name); 49 | bool tracepoint_exists(const char* category, const char* event); 50 | 51 | bool vmlinux_btf_exists(void); 52 | bool module_btf_exists(const char* mod); 53 | 54 | bool probe_tp_btf(const char* name); 55 | bool probe_ringbuf(); 56 | 57 | #endif /* __TRACE_HELPERS_H */ 58 | -------------------------------------------------------------------------------- /examples/runqlat/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY)/ 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = runqlat 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | 40 | # Build BPF code 41 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 42 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 43 | llvm-strip -g $@ # strip useless DWARF info 44 | 45 | # compile bpftool 46 | $(BPFTOOL): 47 | cd $(BPFTOOL_SRC) && make 48 | 49 | # generate c skeleton 50 | %.skel.h: %.bpf.o $(BPFTOOL) 51 | $(BPFTOOL) gen skeleton -j $< > $@ 52 | 53 | # generate wasm bpf header for pass struct event 54 | $(APP).wasm.h: $(APP).bpf.o $(BPFTOOL) 55 | ecc $(APP).h --header-only 56 | $(BPFTOOL) btf dump file $< format c -j > $@ 57 | 58 | # compile for wasm with wasi-sdk 59 | WASI_CLANG = /opt/wasi-sdk/bin/clang 60 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 61 | 62 | $(APP).wasm: $(APP).c $(APP).skel.h 63 | ln -f -s ../../wasm-sdk/c/libbpf-wasm.h libbpf-wasm.h 64 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< trace_helpers.c 65 | 66 | # install emadk 67 | emsdk: 68 | git clone https://github.com/emscripten-core/emsdk.git 69 | cd emsdk && ./emsdk install latest && ./emsdk activate latest 70 | source ./emsdk_env.sh 71 | 72 | TEST_TIME := 3 73 | .PHONY: test 74 | test: 75 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 76 | -------------------------------------------------------------------------------- /examples/runqlat/README_zh.md: -------------------------------------------------------------------------------- 1 | # 示例 BPF 程序 2 | 3 | ## runqlat 4 | 5 | Linux eBPF/bcc 版本的 runqlat 的演示。 6 | 7 | 这个程序通过直方图展示调度器运行队列延迟,给我们展现了任务等了多久才能轮到 CPU 用。 8 | 9 | ```console 10 | $ sudo ./wasm-bpf runqlat.wasm -h 11 | Summarize run queue (scheduler) latency as a histogram. 12 | 13 | USAGE: runqlat [--help] [interval] [count] 14 | 15 | EXAMPLES: 16 | runqlat # summarize run queue latency as a histogram 17 | runqlat 1 10 # print 1 second summaries, 10 times 18 | $ sudo ./wasm-bpf runqlat.wasm 1 19 | 20 | Tracing run queue latency... Hit Ctrl-C to end. 21 | 22 | usecs : count distribution 23 | 0 -> 1 : 72 |***************************** | 24 | 2 -> 3 : 93 |************************************* | 25 | 4 -> 7 : 98 |****************************************| 26 | 8 -> 15 : 96 |*************************************** | 27 | 16 -> 31 : 38 |*************** | 28 | 32 -> 63 : 4 |* | 29 | 64 -> 127 : 5 |** | 30 | 128 -> 255 : 6 |** | 31 | 256 -> 511 : 0 | | 32 | 512 -> 1023 : 0 | | 33 | 1024 -> 2047 : 0 | | 34 | 2048 -> 4095 : 1 | | 35 | ``` 36 | 37 | 图形显示的分布有两个峰,一个峰在 0 到 15 微秒间,另一个峰在 16 到 65 微秒。 这些分布可以从字符画统计图中的尖峰来观察到(其实只是 `数量` 这个值的可视化表现)。 38 | 39 | 再比如看一看 16384 微秒到 32767 微秒那一行,那一行有 809 个事件。 40 | 41 | `runqlat` 也是个简单但有实际意义的 BPF 程序的例子。不过它稍微复杂一些,有超过一个文件,并且直接读内核 map 而不是从内核的环形缓冲区获取数据。 42 | 43 | ## runqlat.wasm 的编译过程 44 | 45 | 我们提供了与 [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap) 类似的开发体验。 只需要运行 `make` 就能构建 wasm 程序: 46 | 47 | ```sh 48 | make 49 | ``` 50 | 51 | 对于构建过程的具体描述,以及一些可能遇到的问题,请查阅 [bootstrap/README.md](../bootstrap/README.md)。 52 | 53 | ## `maps` API 54 | 55 | 可以使用 `map` API 来从用户态访问内核里的 `map`,例如: 56 | 57 | ```c 58 | while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { 59 | err = bpf_map_lookup_elem(fd, &next_key, &hist); 60 | ... 61 | lookup_key = next_key; 62 | } 63 | lookup_key = -2; 64 | while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) { 65 | err = bpf_map_delete_elem(fd, &next_key); 66 | ... 67 | lookup_key = next_key; 68 | } 69 | ``` 70 | 71 | 运行时将会使用共享内存来访问内核 map,同时内核将会更新在共享内存中的 map ,所以 wasm 代码可以直接访问 eBPF map,而不需要面对用户态主机侧程序和 Wasm 运行时之间的额外拷贝开销。 72 | 73 | 可以使用 `bpf_map_update_elem` 在用户态程序内更新内核的 eBPF map,比如: 74 | 75 | ```c 76 | cg_map_fd = bpf_map__fd(obj->maps.cgroup_map); 77 | cgfd = open(env.cgroupspath, O_RDONLY); 78 | if (cgfd < 0) { 79 | ... 80 | } 81 | if (bpf_map_update_elem(cg_map_fd, &idx, &cgfd, BPF_ANY)) { 82 | ... 83 | } 84 | ``` 85 | 86 | 所以内核的 eBPF 程序可以从 Wasm 侧的程序获取配置,或者在运行的时候接收消息。 87 | -------------------------------------------------------------------------------- /examples/runqlat/bits.bpf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __BITS_BPF_H 3 | #define __BITS_BPF_H 4 | 5 | #define READ_ONCE(x) (*(volatile typeof(x)*)&(x)) 6 | #define WRITE_ONCE(x, val) ((*(volatile typeof(x)*)&(x)) = val) 7 | 8 | static __always_inline u64 log2(u32 v) { 9 | u32 shift, r; 10 | 11 | r = (v > 0xFFFF) << 4; 12 | v >>= r; 13 | shift = (v > 0xFF) << 3; 14 | v >>= shift; 15 | r |= shift; 16 | shift = (v > 0xF) << 2; 17 | v >>= shift; 18 | r |= shift; 19 | shift = (v > 0x3) << 1; 20 | v >>= shift; 21 | r |= shift; 22 | r |= (v >> 1); 23 | 24 | return r; 25 | } 26 | 27 | static __always_inline u64 log2l(u64 v) { 28 | u32 hi = v >> 32; 29 | 30 | if (hi) 31 | return log2(hi) + 32; 32 | else 33 | return log2(v); 34 | } 35 | 36 | #endif /* __BITS_BPF_H */ 37 | -------------------------------------------------------------------------------- /examples/runqlat/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /examples/runqlat/maps.bpf.h: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) 2 | // Copyright (c) 2020 Anton Protopopov 3 | #ifndef __MAPS_BPF_H 4 | #define __MAPS_BPF_H 5 | 6 | #include 7 | #include 8 | 9 | static __always_inline void* bpf_map_lookup_or_try_init(void* map, 10 | const void* key, 11 | const void* init) { 12 | void* val; 13 | long err; 14 | 15 | val = bpf_map_lookup_elem(map, key); 16 | if (val) 17 | return val; 18 | 19 | err = bpf_map_update_elem(map, key, init, BPF_NOEXIST); 20 | if (err && err != -EEXIST) 21 | return 0; 22 | 23 | return bpf_map_lookup_elem(map, key); 24 | } 25 | 26 | #endif /* __MAPS_BPF_H */ 27 | -------------------------------------------------------------------------------- /examples/runqlat/runqlat.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __RUNQLAT_H 3 | #define __RUNQLAT_H 4 | 5 | #define TASK_COMM_LEN 16 6 | #define MAX_SLOTS 26 7 | 8 | struct hist { 9 | unsigned int slots[MAX_SLOTS]; 10 | char comm[TASK_COMM_LEN]; 11 | } __attribute__((packed)); 12 | 13 | #endif /* __RUNQLAT_H */ 14 | -------------------------------------------------------------------------------- /examples/runqlat/trace_helpers.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __TRACE_HELPERS_H 3 | #define __TRACE_HELPERS_H 4 | 5 | #include 6 | 7 | #define NSEC_PER_SEC 1000000000ULL 8 | 9 | void print_log2_hist(unsigned int* vals, int vals_size, const char* val_type); 10 | void print_linear_hist(unsigned int* vals, 11 | int vals_size, 12 | unsigned int base, 13 | unsigned int step, 14 | const char* val_type); 15 | 16 | unsigned long long get_ktime_ns(void); 17 | 18 | bool is_kernel_module(const char* name); 19 | 20 | /* 21 | * When attempting to use kprobe/kretprobe, please check out new fentry/fexit 22 | * probes, as they provide better performance and usability. But in some 23 | * situations we have to fallback to kprobe/kretprobe probes. This helper 24 | * is used to detect fentry/fexit support for the specified kernel function. 25 | * 26 | * 1. A gap between kernel versions, kernel BTF is exposed 27 | * starting from 5.4 kernel. but fentry/fexit is actually 28 | * supported starting from 5.5. 29 | * 2. Whether kernel supports module BTF or not 30 | * 31 | * *name* is the name of a kernel function to be attached to, which can be 32 | * from vmlinux or a kernel module. 33 | * *mod* is a hint that indicates the *name* may reside in module BTF, 34 | * if NULL, it means *name* belongs to vmlinux. 35 | */ 36 | bool fentry_can_attach(const char* name, const char* mod); 37 | 38 | /* 39 | * The name of a kernel function to be attached to may be changed between 40 | * kernel releases. This helper is used to confirm whether the target kernel 41 | * uses a certain function name before attaching. 42 | * 43 | * It is achieved by scaning 44 | * /sys/kernel/debug/tracing/available_filter_functions 45 | * If this file does not exist, it fallbacks to parse /proc/kallsyms, 46 | * which is slower. 47 | */ 48 | bool kprobe_exists(const char* name); 49 | bool tracepoint_exists(const char* category, const char* event); 50 | 51 | bool vmlinux_btf_exists(void); 52 | bool module_btf_exists(const char* mod); 53 | 54 | bool probe_tp_btf(const char* name); 55 | bool probe_ringbuf(); 56 | 57 | #endif /* __TRACE_HELPERS_H */ 58 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.o 3 | wit/event-def.wit 4 | wasm-bpf-binding/target 5 | dump.rs 6 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-bootstrap" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | # wit-bindgen-guest-rust = { git = "https://github.com/bytecodealliance/wit-bindgen", version = "0.3.0" } 8 | 9 | wasm-bpf-binding = { path = "../../wasm-sdk/rust/wasm-bpf-binding" } 10 | 11 | wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen", rev = "0fb4018" } 12 | # [patch.crates-io] 13 | # wit-component = {git = "https://github.com/bytecodealliance/wasm-tools", version = "0.5.0", rev = "9640d187a73a516c42b532cf2a10ba5403df5946"} 14 | # wit-parser = {git = "https://github.com/bytecodealliance/wasm-tools", version = "0.5.0", rev = "9640d187a73a516c42b532cf2a10ba5403df5946"} 15 | 16 | 17 | [features] 18 | wasmtime = [] 19 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/Makefile: -------------------------------------------------------------------------------- 1 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 2 | THIRD_PARTY := ../../runtime/cpp/third_party 3 | 4 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 5 | BPF_HEADERS := $(THIRD_PARTY)/ 6 | # Use our own libbpf API headers and Linux UAPI headers distributed with 7 | # libbpf to avoid dependency on system-wide headers, which could be missing or 8 | # outdated 9 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 10 | CFLAGS := -g -Wall 11 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 12 | CLANG := clang 13 | LLVM_STRIP := llvm-strip 14 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 15 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 16 | 17 | 18 | # Get Clang's default includes on this system. We'll explicitly add these dirs 19 | # to the includes list when compiling with `-target bpf` because otherwise some 20 | # architecture-specific dirs will be "missing" on some architectures/distros - 21 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 22 | # sys/cdefs.h etc. might be missing. 23 | # 24 | # Use '-idirafter': Don't interfere with include mechanics except where the 25 | # build would have failed anyways. 26 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 27 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 28 | 29 | APP = bootstrap 30 | 31 | .PHONY: all 32 | all: build 33 | 34 | .PHONY: clean 35 | clean: 36 | cargo clean 37 | rm -rf *.o *.json *.wasm *.skel.h 38 | 39 | # Build BPF code 40 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 41 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 42 | llvm-strip -g $@ # strip useless DWARF info 43 | $(MAKE) -C btf 44 | 45 | .PHONY: build 46 | build: $(APP).bpf.o 47 | rustup target add wasm32-wasi 48 | cargo build --target wasm32-wasi --release 49 | cp target/wasm32-wasi/release/rust-bootstrap.wasm rust-bootstrap.wasm 50 | 51 | TEST_TIME := 3 52 | .PHONY: test 53 | test: 54 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf rust-bootstrap.wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 55 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/bootstrap.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2020 Facebook */ 3 | #include "vmlinux.h" 4 | #include 5 | #include 6 | #include 7 | #include "bootstrap.h" 8 | 9 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 10 | 11 | struct { 12 | __uint(type, BPF_MAP_TYPE_HASH); 13 | __uint(max_entries, 8192); 14 | __type(key, pid_t); 15 | __type(value, u64); 16 | } exec_start SEC(".maps"); 17 | 18 | struct { 19 | __uint(type, BPF_MAP_TYPE_RINGBUF); 20 | __uint(max_entries, 256 * 1024); 21 | } rb SEC(".maps"); 22 | 23 | const volatile unsigned long long min_duration_ns = 0; 24 | 25 | SEC("tp/sched/sched_process_exec") 26 | int handle_exec(struct trace_event_raw_sched_process_exec* ctx) { 27 | struct task_struct* task; 28 | unsigned fname_off; 29 | struct event* e; 30 | pid_t pid; 31 | u64 ts; 32 | 33 | /* remember time exec() was executed for this PID */ 34 | pid = bpf_get_current_pid_tgid() >> 32; 35 | ts = bpf_ktime_get_ns(); 36 | bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY); 37 | 38 | /* don't emit exec events when minimum duration is specified */ 39 | if (min_duration_ns) 40 | return 0; 41 | 42 | /* reserve sample from BPF ringbuf */ 43 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 44 | if (!e) 45 | return 0; 46 | 47 | /* fill out the sample with data */ 48 | task = (struct task_struct*)bpf_get_current_task(); 49 | 50 | e->exit_event = false; 51 | e->pid = pid; 52 | e->ppid = BPF_CORE_READ(task, real_parent, tgid); 53 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 54 | 55 | fname_off = ctx->__data_loc_filename & 0xFFFF; 56 | bpf_probe_read_str(&e->filename, sizeof(e->filename), 57 | (void*)ctx + fname_off); 58 | 59 | /* successfully submit it to user-space for post-processing */ 60 | bpf_ringbuf_submit(e, 0); 61 | return 0; 62 | } 63 | 64 | SEC("tp/sched/sched_process_exit") 65 | int handle_exit(struct trace_event_raw_sched_process_template* ctx) { 66 | struct task_struct* task; 67 | struct event* e; 68 | pid_t pid, tid; 69 | u64 id, ts, *start_ts, duration_ns = 0; 70 | 71 | /* get PID and TID of exiting thread/process */ 72 | id = bpf_get_current_pid_tgid(); 73 | pid = id >> 32; 74 | tid = (u32)id; 75 | 76 | /* ignore thread exits */ 77 | if (pid != tid) 78 | return 0; 79 | 80 | /* if we recorded start of the process, calculate lifetime duration */ 81 | start_ts = bpf_map_lookup_elem(&exec_start, &pid); 82 | if (start_ts) 83 | duration_ns = bpf_ktime_get_ns() - *start_ts; 84 | else if (min_duration_ns) 85 | return 0; 86 | bpf_map_delete_elem(&exec_start, &pid); 87 | 88 | /* if process didn't live long enough, return early */ 89 | if (min_duration_ns && duration_ns < min_duration_ns) 90 | return 0; 91 | 92 | /* reserve sample from BPF ringbuf */ 93 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 94 | if (!e) 95 | return 0; 96 | 97 | /* fill out the sample with data */ 98 | task = (struct task_struct*)bpf_get_current_task(); 99 | 100 | e->exit_event = true; 101 | e->duration_ns = duration_ns; 102 | e->pid = pid; 103 | e->ppid = BPF_CORE_READ(task, real_parent, tgid); 104 | e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff; 105 | bpf_get_current_comm(&e->comm, sizeof(e->comm)); 106 | 107 | /* send data to user-space for post-processing */ 108 | bpf_ringbuf_submit(e, 0); 109 | return 0; 110 | } 111 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/bootstrap.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (c) 2020 Facebook */ 3 | #ifndef __BOOTSTRAP_H 4 | #define __BOOTSTRAP_H 5 | 6 | #define TASK_COMM_LEN 16 7 | #define MAX_FILENAME_LEN 127 8 | 9 | struct event { 10 | int pid; 11 | int ppid; 12 | unsigned int exit_code; 13 | char pad0[4]; 14 | unsigned long long duration_ns; 15 | char comm[16]; 16 | char filename[127]; 17 | char exit_event; 18 | } __attribute__((packed)); 19 | 20 | #endif /* __BOOTSTRAP_H */ 21 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/btf/Makefile: -------------------------------------------------------------------------------- 1 | generate: install-deps import.wit 2 | clang -target bpf -g event-def.c -c -o event.def.o 3 | cargo install btf2wit 4 | btf2wit event.def.o -o event-def.wit --world-name event-def 5 | cp *.wit ../wit/ 6 | 7 | install-deps: 8 | cargo install btf2wit 9 | sudo apt install pahole 10 | 11 | import.o: import.c 12 | gcc import.c -c -o import.o -g 13 | pahole -J import.o 14 | 15 | import.wit: import.o 16 | btf2wit import.o -o import.wit --world-name import 17 | 18 | # Currently, WIT doesn't support identifiers with two continuous `-` 19 | sed -i 's/--/s-/g' import.wit 20 | 21 | host.rs: import.wit 22 | wit-bindgen import.wit 23 | 24 | clean: 25 | rm -rf *.o *.wit 26 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/btf/event-def.c: -------------------------------------------------------------------------------- 1 | #include "../bootstrap.h" 2 | 3 | struct event* ptr; -------------------------------------------------------------------------------- /examples/rust-bootstrap/btf/event-def.wit: -------------------------------------------------------------------------------- 1 | default world %event-def { 2 | record %event { 3 | %pid: s32, 4 | %ppid: s32, 5 | %exit-code: u32, 6 | %pad0: list, 7 | %duration-ns: u64, 8 | %comm: list, 9 | %filename: list, 10 | %exit-event: s8, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/btf/import.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | /// should be externref type for bpf_object_skel. 5 | typedef uint64_t bpf_object_skel; 6 | /// lookup a bpf map fd by name. 7 | int wasm_bpf_map_fd_by_name(bpf_object_skel obj, const char* name) { 8 | return 0; 9 | } 10 | /// detach and close a bpf program. 11 | int wasm_close_bpf_object(bpf_object_skel obj) { 12 | return 0; 13 | } 14 | /// CO-RE load a bpf object into the kernel. 15 | bpf_object_skel wasm_load_bpf_object(const void* obj_buf, int obj_buf_sz) { 16 | return 0; 17 | } 18 | /// attach a bpf program to a kernel hook. 19 | int wasm_attach_bpf_program(bpf_object_skel obj, 20 | const char* name, 21 | const char* attach_target) { 22 | return 0; 23 | } 24 | /// poll a bpf buffer, and call a wasm callback indicated by sample_func. 25 | /// the first time to call this function will open and create a bpf buffer. 26 | int wasm_bpf_buffer_poll(bpf_object_skel program, 27 | int fd, 28 | int32_t sample_func, 29 | uint32_t ctx, 30 | char* data, 31 | int max_size, 32 | int timeout_ms) { 33 | return 0; 34 | } 35 | /// lookup, update, delete, and get_next_key operations on a bpf map. 36 | int wasm_bpf_map_operate(int fd, 37 | int cmd, 38 | void* key, 39 | void* value, 40 | void* next_key, 41 | uint64_t flags) { 42 | return 0; 43 | } -------------------------------------------------------------------------------- /examples/rust-bootstrap/btf/import.wit: -------------------------------------------------------------------------------- 1 | default world %import { 2 | type %s-int32-t = s32 3 | type %s-uint32-t = u32 4 | type %s-uint64-t = u64 5 | type %int32-t = %s-int32-t 6 | type %uint32-t = %s-uint32-t 7 | type %uint64-t = %s-uint64-t 8 | type %bpf-object-skel = %uint64-t 9 | import %wasm-bpf-map-operate: func (%fd: s32, %cmd: s32, %key: u32 /* pointer to <()> */, %value: u32 /* pointer to <()> */, %next-key: u32 /* pointer to <()> */, %flags: %uint64-t) -> s32 /* linkage: static */ 10 | import %wasm-bpf-buffer-poll: func (%program: %bpf-object-skel, %fd: s32, %sample-func: %int32-t, %ctx: %uint32-t, %data: u32 /* pointer to */, %max-size: s32, %timeout-ms: s32) -> s32 /* linkage: static */ 11 | import %wasm-attach-bpf-program: func (%obj: %bpf-object-skel, %name: u32 /* pointer to */, %attach-target: u32 /* pointer to */) -> s32 /* linkage: static */ 12 | import %wasm-load-bpf-object: func (%obj-buf: u32 /* pointer to */, %obj-buf-sz: s32) -> %bpf-object-skel /* linkage: static */ 13 | import %wasm-close-bpf-object: func (%obj: %bpf-object-skel) -> s32 /* linkage: static */ 14 | import %wasm-bpf-map-fd-by-name: func (%obj: %bpf-object-skel, %name: u32 /* pointer to */) -> s32 /* linkage: static */ 15 | } 16 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rustc-link-arg=--export-table") 3 | } 4 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/src/main.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | mod wit_binding { 4 | wit_bindgen::generate!("import"); 5 | } 6 | use std::{ffi::CStr, slice}; 7 | #[cfg(not(feature = "wasmtime"))] 8 | use wasm_bpf_binding::binding as binding; 9 | #[cfg(feature = "wasmtime")] 10 | use wit_binding as binding; 11 | #[export_name = "__main_argc_argv"] 12 | fn main(_env_json: u32, _str_len: i32) -> i32 { 13 | // embed and open the bpf object 14 | let bpf_object = include_bytes!("../bootstrap.bpf.o"); 15 | // load the object 16 | let obj_ptr = 17 | binding::wasm_load_bpf_object(bpf_object.as_ptr() as u32, bpf_object.len() as i32); 18 | if obj_ptr == 0 { 19 | println!("Failed to load bpf object"); 20 | return 1; 21 | } 22 | // attach bpf program to func 23 | let attach_result = binding::wasm_attach_bpf_program( 24 | obj_ptr, 25 | "handle_exec\0".as_ptr() as u32, 26 | "\0".as_ptr() as u32, 27 | ); 28 | if attach_result < 0 { 29 | println!("Attach handle_exit failed: {}", attach_result); 30 | return 1; 31 | } 32 | // attach bpf program to func 33 | let attach_result = binding::wasm_attach_bpf_program( 34 | obj_ptr, 35 | "handle_exit\0".as_ptr() as u32, 36 | "\0".as_ptr() as u32, 37 | ); 38 | if attach_result < 0 { 39 | println!("Attach handle_exit failed: {}", attach_result); 40 | return 1; 41 | } 42 | // get the map fd for ring buffer 43 | let map_fd = binding::wasm_bpf_map_fd_by_name(obj_ptr, "rb\0".as_ptr() as u32); 44 | if map_fd < 0 { 45 | println!("Failed to get map fd: {}", map_fd); 46 | return 1; 47 | } 48 | // binding::wasm 49 | let buffer = [0u8; 256]; 50 | loop { 51 | // polling the buffer 52 | binding::wasm_bpf_buffer_poll( 53 | obj_ptr, 54 | map_fd, 55 | handle_event as i32, 56 | 0, 57 | buffer.as_ptr() as u32, 58 | buffer.len() as i32, 59 | 100, 60 | ); 61 | } 62 | } 63 | 64 | #[repr(C, packed)] 65 | #[derive(Debug, Copy, Clone)] 66 | struct Event { 67 | pid: i32, 68 | ppid: i32, 69 | exit_code: u32, 70 | __pad0: [u8; 4], 71 | duration_ns: u64, 72 | comm: [u8; 16], 73 | filename: [u8; 127], 74 | exit_event: u8, 75 | } 76 | 77 | /// handle ring buffer events 78 | extern "C" fn handle_event(_ctx: u32, data: u32, _data_sz: u32) -> i32{ 79 | let event_slice = unsafe { slice::from_raw_parts(data as *const Event, 1) }; 80 | let event = &event_slice[0]; 81 | let pid = event.pid; 82 | let ppid = event.ppid; 83 | let exit_code = event.exit_code; 84 | if event.exit_event == 1 { 85 | print!( 86 | "{:<8} {:<5} {:<16} {:<7} {:<7} [{}]", 87 | "TIME", 88 | "EXIT", 89 | unsafe { CStr::from_ptr(event.comm.as_ptr() as *const i8) } 90 | .to_str() 91 | .unwrap(), 92 | pid, 93 | ppid, 94 | exit_code 95 | ); 96 | if event.duration_ns != 0 { 97 | print!(" ({}ms)", event.duration_ns / 1000000); 98 | } 99 | println!(); 100 | } else { 101 | println!( 102 | "{:<8} {:<5} {:<16} {:<7} {:<7} {}", 103 | "TIME", 104 | "EXEC", 105 | unsafe { CStr::from_ptr(event.comm.as_ptr() as *const i8) } 106 | .to_str() 107 | .unwrap(), 108 | pid, 109 | ppid, 110 | unsafe { CStr::from_ptr(event.filename.as_ptr() as *const i8) } 111 | .to_str() 112 | .unwrap() 113 | ); 114 | } 115 | return 0; 116 | } 117 | -------------------------------------------------------------------------------- /examples/rust-bootstrap/wit/import.wit: -------------------------------------------------------------------------------- 1 | default world %import { 2 | type %s-int32-t = s32 3 | type %s-uint32-t = u32 4 | type %s-uint64-t = u64 5 | type %int32-t = %s-int32-t 6 | type %uint32-t = %s-uint32-t 7 | type %uint64-t = %s-uint64-t 8 | type %bpf-object-skel = %uint64-t 9 | import %wasm-bpf-map-operate: func (%fd: s32, %cmd: s32, %key: u32 /* pointer to <()> */, %value: u32 /* pointer to <()> */, %next-key: u32 /* pointer to <()> */, %flags: %uint64-t) -> s32 /* linkage: static */ 10 | import %wasm-bpf-buffer-poll: func (%program: %bpf-object-skel, %fd: s32, %sample-func: %int32-t, %ctx: %uint32-t, %data: u32 /* pointer to */, %max-size: s32, %timeout-ms: s32) -> s32 /* linkage: static */ 11 | import %wasm-attach-bpf-program: func (%obj: %bpf-object-skel, %name: u32 /* pointer to */, %attach-target: u32 /* pointer to */) -> s32 /* linkage: static */ 12 | import %wasm-load-bpf-object: func (%obj-buf: u32 /* pointer to */, %obj-buf-sz: s32) -> %bpf-object-skel /* linkage: static */ 13 | import %wasm-close-bpf-object: func (%obj: %bpf-object-skel) -> s32 /* linkage: static */ 14 | import %wasm-bpf-map-fd-by-name: func (%obj: %bpf-object-skel, %name: u32 /* pointer to */) -> s32 /* linkage: static */ 15 | } 16 | -------------------------------------------------------------------------------- /examples/sockfilter/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY)/ 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = sockfilter 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | 40 | # Build BPF code 41 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 42 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 43 | llvm-strip -g $@ # strip useless DWARF info 44 | 45 | # compile bpftool 46 | $(BPFTOOL): 47 | cd $(BPFTOOL_SRC) && make 48 | 49 | # generate c skeleton 50 | %.skel.h: %.bpf.o $(BPFTOOL) 51 | $(BPFTOOL) gen skeleton -j $< > $@ 52 | 53 | # generate wasm bpf header for pass struct event 54 | $(APP).wasm.h: $(APP).bpf.o $(BPFTOOL) 55 | ecc $(APP).h --header-only 56 | $(BPFTOOL) btf dump file $< format c -j > $@ 57 | 58 | # compile for wasm with wasi-sdk 59 | WASI_CLANG = /opt/wasi-sdk/bin/clang 60 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 61 | 62 | $(APP).wasm: $(APP).c $(APP).skel.h 63 | ln -f -s ../../wasm-sdk/c/libbpf-wasm.h libbpf-wasm.h 64 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 65 | 66 | TEST_TIME := 3 67 | .PHONY: test 68 | test: 69 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 70 | -------------------------------------------------------------------------------- /examples/sockfilter/README.md: -------------------------------------------------------------------------------- 1 | # Demo BPF applications 2 | 3 | ## Socket filter 4 | 5 | sockfilter is an example of monitoring packet and dealing with __sk_buff structure. 6 | 7 | see [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap) for more details. 8 | 9 | We can provide a similar developing experience as the [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap) development. Just run `make` to build the wasm binary: 10 | 11 | ```sh 12 | make 13 | ``` 14 | -------------------------------------------------------------------------------- /examples/sockfilter/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /examples/sockfilter/sockfilter.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct so_event { 12 | __be32 src_addr; 13 | __be32 dst_addr; 14 | union { 15 | __be32 ports; 16 | __be16 port16[2]; 17 | }; 18 | __u32 ip_proto; 19 | __u32 pkt_type; 20 | __u32 ifindex; 21 | }; 22 | 23 | #define IP_MF 0x2000 24 | #define IP_OFFSET 0x1FFF 25 | 26 | char LICENSE[] SEC("license") = "Dual BSD/GPL"; 27 | 28 | struct { 29 | __uint(type, BPF_MAP_TYPE_RINGBUF); 30 | __uint(max_entries, 256 * 1024); 31 | } rb SEC(".maps"); 32 | 33 | static inline int ip_is_fragment(struct __sk_buff* skb, __u32 nhoff) { 34 | __u16 frag_off; 35 | 36 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, frag_off), &frag_off, 37 | 2); 38 | frag_off = __bpf_ntohs(frag_off); 39 | return frag_off & (IP_MF | IP_OFFSET); 40 | } 41 | 42 | SEC("socket") 43 | int socket_handler(struct __sk_buff* skb) { 44 | struct so_event* e; 45 | __u8 verlen; 46 | __u16 proto; 47 | __u32 nhoff = ETH_HLEN; 48 | 49 | bpf_skb_load_bytes(skb, 12, &proto, 2); 50 | proto = __bpf_ntohs(proto); 51 | if (proto != ETH_P_IP) 52 | return 0; 53 | 54 | if (ip_is_fragment(skb, nhoff)) 55 | return 0; 56 | 57 | /* reserve sample from BPF ringbuf */ 58 | e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0); 59 | if (!e) 60 | return 0; 61 | 62 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, protocol), 63 | &e->ip_proto, 1); 64 | 65 | if (e->ip_proto != IPPROTO_GRE) { 66 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, saddr), 67 | &(e->src_addr), 4); 68 | bpf_skb_load_bytes(skb, nhoff + offsetof(struct iphdr, daddr), 69 | &(e->dst_addr), 4); 70 | } 71 | 72 | bpf_skb_load_bytes(skb, nhoff + 0, &verlen, 1); 73 | bpf_skb_load_bytes(skb, nhoff + ((verlen & 0xF) << 2), &(e->ports), 4); 74 | e->pkt_type = skb->pkt_type; 75 | e->ifindex = skb->ifindex; 76 | bpf_ringbuf_submit(e, 0); 77 | 78 | return skb->len; 79 | } 80 | -------------------------------------------------------------------------------- /examples/sockfilter/sockfilter.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | /* Copyright (c) 2022 Jacky Yin */ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "sockfilter.skel.h" 16 | 17 | #define SO_ATTACH_BPF 50 18 | 19 | int main(int argc, char** argv) { 20 | struct sockfilter_bpf* skel; 21 | int err, prog_fd, sock; 22 | 23 | /* Load and verify BPF programs*/ 24 | skel = sockfilter_bpf__open_and_load(); 25 | if (!skel) { 26 | fprintf(stderr, "Failed to open and load BPF skeleton\n"); 27 | return 1; 28 | } 29 | 30 | printf("[Client] Create TCP socket\n"); 31 | // TODO: add socket api from wasi 32 | // sock = socket(AF_INET, SOCK_STREAM, 0); 33 | // if (sock == -1) { 34 | // perror("Create socket failed"); 35 | // return EXIT_FAILURE; 36 | // } 37 | 38 | // /* Attach BPF program to raw socket */ 39 | // prog_fd = bpf_program__fd(skel->progs.socket_handler); 40 | // if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, &prog_fd, sizeof(prog_fd))) { 41 | // err = -3; 42 | // fprintf(stderr, "Failed to attach to raw socket\n"); 43 | // goto cleanup; 44 | // } 45 | 46 | sleep(10); 47 | cleanup: 48 | sockfilter_bpf__destroy(skel); 49 | return -err; 50 | } 51 | -------------------------------------------------------------------------------- /examples/sockops/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY)/ 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = sockops 32 | 33 | .PHONY: all 34 | all: $(BPFTOOL) $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | 40 | # Build BPF code 41 | %.bpf.o: %.bpf.c $(wildcard %.h) 42 | $(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h 43 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 44 | llvm-strip -g $@ # strip useless DWARF info 45 | 46 | # compile bpftool 47 | $(BPFTOOL): 48 | cd $(BPFTOOL_SRC) && make 49 | 50 | # generate c skeleton 51 | %.skel.h: %.bpf.o $(BPFTOOL) 52 | $(BPFTOOL) gen skeleton -j $< > $@ 53 | 54 | # generate wasm bpf header for pass struct event 55 | $(APP).wasm.h: $(APP).bpf.o $(BPFTOOL) 56 | ecc $(APP).h --header-only 57 | $(BPFTOOL) btf dump file $< format c -j > $@ 58 | 59 | # compile for wasm with wasi-sdk 60 | WASI_CLANG = /opt/wasi-sdk/bin/clang 61 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 62 | 63 | $(APP).wasm: $(APP).c $(APP).skel.h 64 | ln -f -s ../../wasm-sdk/c/libbpf-wasm.h libbpf-wasm.h 65 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 66 | 67 | TEST_TIME := 3 68 | .PHONY: test 69 | test: 70 | # sudo timeout --preserve-status -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm 71 | # disable on sckopts because CI kernel version not support. 72 | -------------------------------------------------------------------------------- /examples/sockops/README.md: -------------------------------------------------------------------------------- 1 | # Demo BPF applications 2 | 3 | ## sockops 4 | 5 | `sockops` add the pid int tcp option in syn packet. 6 | 7 | We can provide a similar developing experience as the [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap) development. Just run `make` to build the wasm binary: 8 | 9 | ```sh 10 | make 11 | ``` 12 | 13 | test the prog load and attach correctly 14 | ```sh 15 | bpftool prog list | grep -i pid_tcp_opt_inject -A 3 16 | ``` 17 | 18 | open wireshark to capture and verify the tcp option(kind 254) is add to option in syn packet 19 | -------------------------------------------------------------------------------- /examples/sockops/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /examples/sockops/sockops.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | #include 4 | 5 | char LICENSE[] SEC("license") = "GPL"; 6 | 7 | struct { 8 | __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY); 9 | __uint(max_entries, 1); 10 | __type(key, int); // key always 0 11 | __type(value, u32); // pid 12 | } write_pid_array SEC(".maps"); 13 | 14 | SEC("tp/syscalls/sys_enter_connect") 15 | int tp_write(struct trace_entry* ctx) { 16 | u32 pid = bpf_get_current_pid_tgid() >> 32; 17 | int key = 0; 18 | bpf_map_update_elem(&write_pid_array, &key, &pid, 0); 19 | return 0; 20 | } 21 | 22 | static inline void set_hdr_cb_flags(struct bpf_sock_ops* skops) { 23 | bpf_sock_ops_cb_flags_set(skops, skops->bpf_sock_ops_cb_flags | 24 | BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG); 25 | } 26 | 27 | static inline void clear_hdr_cb_flags(struct bpf_sock_ops* skops) { 28 | bpf_sock_ops_cb_flags_set(skops, skops->bpf_sock_ops_cb_flags & 29 | ~(BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG)); 30 | } 31 | 32 | static inline u32 current_pid() { 33 | int key = 0; 34 | u32* pid = bpf_map_lookup_elem(&write_pid_array, &key); 35 | if (!pid) { 36 | return 0; 37 | } 38 | return *pid; 39 | } 40 | 41 | struct pid_opt { 42 | unsigned char kind; 43 | unsigned char len; 44 | u32 pid; 45 | } __attribute__((__packed__)); 46 | 47 | SEC("sockops") 48 | int pid_tcp_opt_inject(struct bpf_sock_ops* skops) { 49 | switch (skops->op) { 50 | case BPF_SOCK_OPS_TCP_CONNECT_CB: 51 | set_hdr_cb_flags(skops); 52 | break; 53 | case BPF_SOCK_OPS_HDR_OPT_LEN_CB: 54 | bpf_reserve_hdr_opt(skops, 6, 0); 55 | break; 56 | case BPF_SOCK_OPS_WRITE_HDR_OPT_CB: { 57 | // only set in syn 58 | if (skops->skb_tcp_flags != 0x2) { 59 | return 0; 60 | } 61 | struct pid_opt opt = { 62 | .kind = 254, 63 | .len = 6, 64 | .pid = bpf_htonl(current_pid()), 65 | }; 66 | 67 | __u16 sport, dport; 68 | u32 saddr, daddr; 69 | 70 | u16 local_port = (__u16)skops->local_port; 71 | u16 remote_port = bpf_ntohs(skops->remote_port >> 16); 72 | saddr = skops->local_ip4; 73 | daddr = skops->remote_ip4; 74 | 75 | bpf_printk("%pI4:%d -> %pI4:%d set tcp option kind: %d, pid: %d ", 76 | &saddr, local_port, &daddr, remote_port, 254, opt.pid); 77 | bpf_store_hdr_opt(skops, &opt, 6, 0); 78 | clear_hdr_cb_flags(skops); 79 | break; 80 | } 81 | } 82 | 83 | return 1; 84 | } 85 | -------------------------------------------------------------------------------- /examples/sockops/sockops.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "libbpf-wasm.h" 6 | #include "sockops.skel.h" 7 | 8 | int main(void) { 9 | struct sockops_bpf* skel = NULL; 10 | int err; 11 | 12 | skel = sockops_bpf__open_and_load(); 13 | if (!skel) { 14 | printf("Failed to open and load BPF skeleton\n"); 15 | return -1; 16 | } 17 | bpf_set_prog_attach_target(skel->progs.pid_tcp_opt_inject, 18 | "/sys/fs/cgroup/"); 19 | 20 | err = sockops_bpf__attach(skel); 21 | if (err) { 22 | printf("Failed to attach BPF skeleton\n"); 23 | return -1; 24 | } 25 | printf("Load and attach BPF sockops successfully\n"); 26 | while (1) { 27 | sleep(10); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/tcpconnlat-libbpf-rs/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.o 3 | wit/event-def.wit 4 | wasm-bpf-binding/target 5 | dump.rs 6 | Cargo.lock 7 | -------------------------------------------------------------------------------- /examples/tcpconnlat-libbpf-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tcpconnlat-libbpf-rs" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | anyhow = "1.0.71" 10 | plain = "0.2.3" 11 | wasm-bpf-libbpf-rs = { git = "https://github.com/eunomia-bpf/wasm-bpf-guest-sdk-rust" } 12 | -------------------------------------------------------------------------------- /examples/tcpconnlat-libbpf-rs/Makefile: -------------------------------------------------------------------------------- 1 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 2 | THIRD_PARTY := ../../runtime/cpp/third_party 3 | 4 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 5 | BPF_HEADERS := $(THIRD_PARTY)/ 6 | # Use our own libbpf API headers and Linux UAPI headers distributed with 7 | # libbpf to avoid dependency on system-wide headers, which could be missing or 8 | # outdated 9 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 10 | CFLAGS := -g -Wall 11 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 12 | CLANG := clang 13 | LLVM_STRIP := llvm-strip 14 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 15 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 16 | 17 | 18 | # Get Clang's default includes on this system. We'll explicitly add these dirs 19 | # to the includes list when compiling with `-target bpf` because otherwise some 20 | # architecture-specific dirs will be "missing" on some architectures/distros - 21 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 22 | # sys/cdefs.h etc. might be missing. 23 | # 24 | # Use '-idirafter': Don't interfere with include mechanics except where the 25 | # build would have failed anyways. 26 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 27 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 28 | 29 | APP = tcpconnlat 30 | 31 | .PHONY: all 32 | all: build 33 | 34 | .PHONY: clean 35 | clean: 36 | cargo clean 37 | rm -rf *.o *.json *.wasm *.skel.h 38 | 39 | # Build BPF code 40 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 41 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 42 | llvm-strip -g $@ # strip useless DWARF info 43 | 44 | .PHONY: build 45 | build: $(APP).bpf.o 46 | rustup target add wasm32-wasi 47 | cargo build --target wasm32-wasi --release 48 | cp target/wasm32-wasi/release/tcpconnlat-libbpf-rs.wasm tcpconnlat-libbpf-rs.wasm 49 | 50 | TEST_TIME := 3 51 | .PHONY: test 52 | test: 53 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf tcpconnlat-libbpf-rs.wasm || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 54 | -------------------------------------------------------------------------------- /examples/tcpconnlat-libbpf-rs/README.md: -------------------------------------------------------------------------------- 1 | # tcpconnlat 2 | 3 | An ebpf program to print the connection latency of each TCP connection. 4 | 5 | The user-space program was develeoped with the new `libbpf-rs`-like SDK. 6 | 7 | This example was adapted from [bcc's tcpconnlat](https://github.com/iovisor/bcc/blob/master/libbpf-tools/tcpconnlat.c) 8 | -------------------------------------------------------------------------------- /examples/tcpconnlat-libbpf-rs/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("cargo:rustc-link-arg=--export-table") 3 | } 4 | -------------------------------------------------------------------------------- /examples/tcpconnlat-libbpf-rs/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{anyhow, Context}; 2 | use plain::Plain; 3 | use std::ffi::c_char; 4 | use std::net::{Ipv4Addr, Ipv6Addr}; 5 | use std::time::Duration; 6 | use std::{ffi::CStr, fmt::Debug}; 7 | use wasm_bpf_libbpf_rs::{object::ObjectBuilder, poll::PollBuilder}; 8 | 9 | const AF_INET: i32 = 2; 10 | const AF_INET6: i32 = 10; 11 | 12 | fn main() -> anyhow::Result<()> { 13 | let bpf_object = include_bytes!("../tcpconnlat.bpf.o"); 14 | let obj = ObjectBuilder::default() 15 | .open_memory(&bpf_object[..]) 16 | .with_context(|| anyhow!("Failed to open object"))? 17 | .load() 18 | .with_context(|| anyhow!("Failed to load object"))?; 19 | obj.prog("fentry_tcp_v4_connect").unwrap().attach()?; 20 | obj.prog("fentry_tcp_v6_connect").unwrap().attach()?; 21 | obj.prog("fentry_tcp_rcv_state_process").unwrap().attach()?; 22 | obj.prog("tcp_destroy_sock").unwrap().attach()?; 23 | let map = obj.map("events").unwrap(); 24 | let mut data_buf = [0u8; 2048]; 25 | let mut start_ts = 0; 26 | let poll = PollBuilder::new(&map, &mut data_buf) 27 | .sample_cb(|v| handle_event(v, &mut start_ts)) 28 | .build(); 29 | println!( 30 | "{:<9} {:<6} {:<12} {:<2} {:<16} {:<6} {:<16} {:<5} LAT(ms)", 31 | "TIME(s)", "PID", "COMM", "IP", "SADDR", "LPORT", "DADDR", "DPORT" 32 | ); 33 | loop { 34 | poll.poll(Duration::from_millis(100))?; 35 | } 36 | } 37 | 38 | fn handle_event(data: &[u8], start_ts: &mut u64) -> i32 { 39 | let event = Event::from_bytes(data).unwrap(); 40 | if *start_ts == 0 { 41 | *start_ts = event.ts_us; 42 | } 43 | let (sddr, daddr) = match event.af { 44 | AF_INET => ( 45 | Ipv4Addr::from(unsafe { event.saddr.v4 }.to_ne_bytes()).to_string(), 46 | Ipv4Addr::from(unsafe { event.daddr.v4 }.to_ne_bytes()).to_string(), 47 | ), 48 | AF_INET6 => ( 49 | Ipv6Addr::from(unsafe { event.saddr.v6 }).to_string(), 50 | Ipv6Addr::from(unsafe { event.daddr.v6 }).to_string(), 51 | ), 52 | s => { 53 | eprintln!("Invalid AF: {}", s); 54 | return 0; 55 | } 56 | }; 57 | print!("{:<9.3} ", (event.ts_us - *start_ts) as f64 / 1000000.0); 58 | println!( 59 | "{:<6} {:<12} {:<2} {:<16} {:<6} {:<16} {:<5} {:.2}", 60 | event.tgid, 61 | unsafe { CStr::from_ptr(event.comm.as_ptr() as *const c_char) }.to_string_lossy(), 62 | if event.af == AF_INET { "v4" } else { "v6" }, 63 | sddr, 64 | event.lport, 65 | daddr, 66 | u16::from_be(event.dport), 67 | event.delta_us as f64 / 1000.0 68 | ); 69 | 70 | 0 71 | } 72 | 73 | #[repr(C)] 74 | #[derive(Default, Debug)] 75 | struct Event { 76 | saddr: Addr, 77 | daddr: Addr, 78 | comm: [u8; 16], 79 | delta_us: u64, 80 | ts_us: u64, 81 | tgid: u32, 82 | af: i32, 83 | lport: u16, 84 | dport: u16, 85 | } 86 | 87 | // SAFE: Event satisfies all the requirements of `Plain`. 88 | unsafe impl Plain for Event {} 89 | 90 | #[repr(C)] 91 | union Addr { 92 | v4: u32, 93 | v6: [u8; 16], 94 | } 95 | 96 | impl Default for Addr { 97 | fn default() -> Self { 98 | Self { 99 | v6: <[u8; 16]>::default(), 100 | } 101 | } 102 | } 103 | 104 | impl Debug for Addr { 105 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 106 | let v4 = unsafe { self.v4 }; 107 | let v6 = unsafe { self.v6 }; 108 | write!(f, "v4: {}, v6:{:?}", v4, v6) 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /examples/tcpconnlat-libbpf-rs/tcpconnlat.bpf.c: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-2.0 2 | // Copyright (c) 2020 Wenbo Zhang 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "tcpconnlat.h" 8 | 9 | #define AF_INET 2 10 | #define AF_INET6 10 11 | 12 | const volatile __u64 targ_min_us = 0; 13 | const volatile pid_t targ_tgid = 0; 14 | 15 | struct piddata { 16 | char comm[TASK_COMM_LEN]; 17 | u64 ts; 18 | u32 tgid; 19 | }; 20 | 21 | struct { 22 | __uint(type, BPF_MAP_TYPE_HASH); 23 | __uint(max_entries, 4096); 24 | __type(key, struct sock*); 25 | __type(value, struct piddata); 26 | } start SEC(".maps"); 27 | 28 | struct { 29 | __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); 30 | __uint(key_size, sizeof(u32)); 31 | __uint(value_size, sizeof(u32)); 32 | } events SEC(".maps"); 33 | 34 | static int trace_connect(struct sock* sk) { 35 | u32 tgid = bpf_get_current_pid_tgid() >> 32; 36 | struct piddata piddata = {}; 37 | 38 | if (targ_tgid && targ_tgid != tgid) 39 | return 0; 40 | 41 | bpf_get_current_comm(&piddata.comm, sizeof(piddata.comm)); 42 | piddata.ts = bpf_ktime_get_ns(); 43 | piddata.tgid = tgid; 44 | bpf_map_update_elem(&start, &sk, &piddata, 0); 45 | return 0; 46 | } 47 | 48 | static int handle_tcp_rcv_state_process(void* ctx, struct sock* sk) { 49 | struct piddata* piddatap; 50 | struct event event = {}; 51 | s64 delta; 52 | u64 ts; 53 | 54 | if (BPF_CORE_READ(sk, __sk_common.skc_state) != TCP_SYN_SENT) 55 | return 0; 56 | 57 | piddatap = bpf_map_lookup_elem(&start, &sk); 58 | if (!piddatap) 59 | return 0; 60 | 61 | ts = bpf_ktime_get_ns(); 62 | delta = (s64)(ts - piddatap->ts); 63 | if (delta < 0) 64 | goto cleanup; 65 | 66 | event.delta_us = delta / 1000U; 67 | if (targ_min_us && event.delta_us < targ_min_us) 68 | goto cleanup; 69 | __builtin_memcpy(&event.comm, piddatap->comm, sizeof(event.comm)); 70 | event.ts_us = ts / 1000; 71 | event.tgid = piddatap->tgid; 72 | event.lport = BPF_CORE_READ(sk, __sk_common.skc_num); 73 | event.dport = BPF_CORE_READ(sk, __sk_common.skc_dport); 74 | event.af = BPF_CORE_READ(sk, __sk_common.skc_family); 75 | if (event.af == AF_INET) { 76 | event.saddr_v4 = BPF_CORE_READ(sk, __sk_common.skc_rcv_saddr); 77 | event.daddr_v4 = BPF_CORE_READ(sk, __sk_common.skc_daddr); 78 | } else { 79 | BPF_CORE_READ_INTO(&event.saddr_v6, sk, 80 | __sk_common.skc_v6_rcv_saddr.in6_u.u6_addr32); 81 | BPF_CORE_READ_INTO(&event.daddr_v6, sk, 82 | __sk_common.skc_v6_daddr.in6_u.u6_addr32); 83 | } 84 | bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, 85 | sizeof(event)); 86 | 87 | cleanup: 88 | bpf_map_delete_elem(&start, &sk); 89 | return 0; 90 | } 91 | 92 | SEC("tracepoint/tcp/tcp_destroy_sock") 93 | int tcp_destroy_sock(struct trace_event_raw_tcp_event_sk* ctx) { 94 | const struct sock* sk = ctx->skaddr; 95 | 96 | bpf_map_delete_elem(&start, &sk); 97 | return 0; 98 | } 99 | 100 | SEC("fentry/tcp_v4_connect") 101 | int BPF_PROG(fentry_tcp_v4_connect, struct sock* sk) { 102 | return trace_connect(sk); 103 | } 104 | 105 | SEC("fentry/tcp_v6_connect") 106 | int BPF_PROG(fentry_tcp_v6_connect, struct sock* sk) { 107 | return trace_connect(sk); 108 | } 109 | 110 | SEC("fentry/tcp_rcv_state_process") 111 | int BPF_PROG(fentry_tcp_rcv_state_process, struct sock* sk) { 112 | return handle_tcp_rcv_state_process(ctx, sk); 113 | } 114 | 115 | char LICENSE[] SEC("license") = "GPL"; 116 | -------------------------------------------------------------------------------- /examples/tcpconnlat-libbpf-rs/tcpconnlat.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | #ifndef __TCPCONNLAT_H 3 | #define __TCPCONNLAT_H 4 | 5 | #define TASK_COMM_LEN 16 6 | 7 | struct event { 8 | union { 9 | __u32 saddr_v4; 10 | __u8 saddr_v6[16]; 11 | }; 12 | union { 13 | __u32 daddr_v4; 14 | __u8 daddr_v6[16]; 15 | }; 16 | char comm[TASK_COMM_LEN]; 17 | __u64 delta_us; 18 | __u64 ts_us; 19 | __u32 tgid; 20 | int af; 21 | __u16 lport; 22 | __u16 dport; 23 | }; 24 | 25 | #endif /* __TCPCONNLAT_H_ */ 26 | -------------------------------------------------------------------------------- /examples/uprobe/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /examples/uprobe/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY) 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = uprobe 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | rm target 40 | 41 | # Build BPF code 42 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 43 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 44 | llvm-strip -g $@ # strip useless DWARF info 45 | 46 | # compile bpftool 47 | $(BPFTOOL): 48 | cd $(BPFTOOL_SRC) && make 49 | 50 | # generate c skeleton 51 | %.skel.h: %.bpf.o $(BPFTOOL) 52 | $(BPFTOOL) gen skeleton -j $< > $@ 53 | 54 | # generate wasm bpf header for pass struct event 55 | $(APP).wasm.h: $(APP).bpf.o $(BPFTOOL) 56 | ecc $(APP).h --header-only 57 | $(BPFTOOL) btf dump file $< format c -j > $@ 58 | 59 | # compile for wasm with wasi-sdk 60 | WASI_CLANG = /opt/wasi-sdk/bin/clang 61 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 62 | 63 | $(APP).wasm: $(APP).c $(APP).skel.h 64 | ln -f -s ../../wasm-sdk/c/libbpf-wasm.h libbpf-wasm.h 65 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 66 | 67 | TEST_TIME := 3 68 | .PHONY: test 69 | target: target.c 70 | clang target.c -o target -g -O 71 | test: target 72 | ./target & \ 73 | PID=$$! && \ 74 | echo "target PID=$$PID" && \ 75 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm || if [ $$? = 124 ]; then kill $$PID && exit 0; else kill $$PID && exit $$?; fi 76 | -------------------------------------------------------------------------------- /examples/uprobe/README.md: -------------------------------------------------------------------------------- 1 | # Demo BPF applications 2 | 3 | ## uprobe 4 | 5 | `uprobe` can be attached to certain functions of specified process. 6 | 7 | First we need to compile and run the target program: 8 | 9 | ```console 10 | # clang target.c -o target 11 | # ./target 12 | ``` 13 | 14 | Now we can try to trace function calls of the target process by 15 | 16 | ```console 17 | # make 18 | # ../wasm-bpf uprobe.wasm 19 | ``` 20 | 21 | After that we can see it's output by: 22 | 23 | ```console 24 | # cat /sys/kernel/debug/tracing/trace_pipe 25 | ``` 26 | 27 | It will look like: 28 | 29 | ```console 30 | target-217543 [000] d...1 759561.724568: bpf_trace_printk: uprobed_add ENTRY: a = 1, b = 1 31 | target-217543 [000] d...1 759561.724574: bpf_trace_printk: uprobed_sub ENTRY: a = 1, b = 1 32 | target-217543 [000] d...1 759563.724751: bpf_trace_printk: uprobed_add ENTRY: a = 1, b = 1 33 | target-217543 [000] d...1 759563.724756: bpf_trace_printk: uprobed_sub ENTRY: a = 1, b = 1 34 | target-217543 [000] d...1 759565.724898: bpf_trace_printk: uprobed_add ENTRY: a = 1, b = 1 35 | target-217543 [000] d...1 759565.724902: bpf_trace_printk: uprobed_sub ENTRY: a = 1, b = 1 36 | target-217543 [000] d...1 759567.725020: bpf_trace_printk: uprobed_add ENTRY: a = 1, b = 1 37 | target-217543 [000] d...1 759567.725024: bpf_trace_printk: uprobed_sub ENTRY: a = 1, b = 1 38 | ``` 39 | 40 | > In the example of libbpf-bootstrap(https://github.com/libbpf/libbpf-bootstrap/blob/master/examples/c/uprobe.bpf.c), the BPF code use `SEC("uprobe//proc/self/exe:uprobed_sub")` to attach to the process of itself. This won't work in wasm-bpf because you program will be compiled to WASM and be executed by wasm runtime rather than the machine, so kernel can't see your code of userpace directly. -------------------------------------------------------------------------------- /examples/uprobe/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /examples/uprobe/target.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int uprobe_add(int a, int b) { 6 | return a + b; 7 | } 8 | 9 | int uprobe_sub(int a, int b) { 10 | return a - b; 11 | } 12 | 13 | int main() { 14 | while (1) { 15 | int a = 1; 16 | int b = 1; 17 | printf("%d %d\n", uprobe_add(a, b), uprobe_sub(a, b)); 18 | sleep(2); 19 | } 20 | } -------------------------------------------------------------------------------- /examples/uprobe/uprobe.bpf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | char LICENSE[] SEC("license") = "GPL"; 7 | 8 | SEC("uprobe/./target:uprobe_add") 9 | int BPF_KPROBE(uprobe_add, int a, int b) { 10 | bpf_printk("uprobed_add ENTRY: a = %d, b = %d", a, b); 11 | return 0; 12 | } 13 | 14 | SEC("uprobe/./target:uprobe_sub") 15 | int BPF_KPROBE(uprobe_sub, int a, int b) { 16 | bpf_printk("uprobed_sub ENTRY: a = %d, b = %d", a, b); 17 | return 0; 18 | } -------------------------------------------------------------------------------- /examples/uprobe/uprobe.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "libbpf-wasm.h" 6 | #include "uprobe.skel.h" 7 | 8 | int main(int argc, char* argv[]) { 9 | struct uprobe_bpf* skel = NULL; 10 | int err; 11 | 12 | skel = uprobe_bpf__open_and_load(); 13 | if (!skel) { 14 | printf("Failed to open and load BPF skeleton\n"); 15 | return -1; 16 | } 17 | 18 | err = uprobe_bpf__attach(skel); 19 | if (err) { 20 | printf("Failed to attach BPF skeleton\n"); 21 | return -1; 22 | } 23 | 24 | printf("Load and attach BPF uprobe successfully\n"); 25 | while (1) { 26 | sleep(10); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/xdp/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | ARCH ?= $(shell uname -m | sed 's/x86_64/x86/' | sed 's/aarch64/arm64/' | sed 's/ppc64le/powerpc/' | sed 's/mips.*/mips/') 4 | THIRD_PARTY := ../../runtime/cpp/third_party 5 | 6 | VMLINUX := $(THIRD_PARTY)/vmlinux/$(ARCH)/vmlinux.h 7 | BPF_HEADERS := $(THIRD_PARTY) 8 | # Use our own libbpf API headers and Linux UAPI headers distributed with 9 | # libbpf to avoid dependency on system-wide headers, which could be missing or 10 | # outdated 11 | INCLUDES := -I$(dir $(VMLINUX)) -I$(BPF_HEADERS) 12 | CFLAGS := -g -Wall 13 | ALL_LDFLAGS := $(LDFLAGS) $(EXTRA_LDFLAGS) 14 | CLANG := clang 15 | LLVM_STRIP := llvm-strip 16 | BPFTOOL_SRC := $(THIRD_PARTY)/bpftool/src 17 | BPFTOOL := $(BPFTOOL_SRC)/bpftool 18 | 19 | 20 | # Get Clang's default includes on this system. We'll explicitly add these dirs 21 | # to the includes list when compiling with `-target bpf` because otherwise some 22 | # architecture-specific dirs will be "missing" on some architectures/distros - 23 | # headers such as asm/types.h, asm/byteorder.h, asm/socket.h, asm/sockios.h, 24 | # sys/cdefs.h etc. might be missing. 25 | # 26 | # Use '-idirafter': Don't interfere with include mechanics except where the 27 | # build would have failed anyways. 28 | CLANG_BPF_SYS_INCLUDES = $(shell $(CLANG) -v -E - &1 \ 29 | | sed -n '/<...> search starts here:/,/End of search list./{ s| \(/.*\)|-idirafter \1|p }') 30 | 31 | APP = xdp 32 | 33 | .PHONY: all 34 | all: $(APP).wasm $(APP).bpf.o 35 | 36 | .PHONY: clean 37 | clean: 38 | rm -rf *.o *.json *.wasm *.skel.h 39 | 40 | # Build BPF code 41 | %.bpf.o: %.bpf.c $(wildcard %.h) $(VMLINUX) 42 | clang -g -O2 -target bpf -D__TARGET_ARCH_$(ARCH) $(INCLUDES) $(CLANG_BPF_SYS_INCLUDES) -c $(filter %.c,$^) -o $@ 43 | llvm-strip -g $@ # strip useless DWARF info 44 | 45 | # compile bpftool 46 | $(BPFTOOL): 47 | cd $(BPFTOOL_SRC) && make 48 | 49 | # generate c skeleton 50 | %.skel.h: %.bpf.o $(BPFTOOL) 51 | $(BPFTOOL) gen skeleton -j $< > $@ 52 | 53 | # generate wasm bpf header for pass struct event 54 | $(APP).wasm.h: $(APP).bpf.o $(BPFTOOL) 55 | ecc $(APP).h --header-only 56 | $(BPFTOOL) btf dump file $< format c -j > $@ 57 | 58 | # compile for wasm with wasi-sdk 59 | WASI_CLANG = /opt/wasi-sdk/bin/clang 60 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 61 | 62 | $(APP).wasm: $(APP).c $(APP).skel.h 63 | ln -f -s ../../wasm-sdk/c/libbpf-wasm.h libbpf-wasm.h 64 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 65 | 66 | TEST_TIME := 3 67 | .PHONY: test 68 | test: 69 | sudo timeout -s 2 $(TEST_TIME) ../wasm-bpf $(APP).wasm lo || if [ $$? = 124 ]; then exit 0; else exit $$?; fi 70 | -------------------------------------------------------------------------------- /examples/xdp/README.md: -------------------------------------------------------------------------------- 1 | # Demo BPF applications 2 | 3 | ## xdp 4 | 5 | `xdp` just print the size of every packet it receives and then let them pass. 6 | 7 | The original c code of kernel side is from [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap). 8 | 9 | We can provide a similar developing experience as the [libbpf-bootstrap](https://github.com/libbpf/libbpf-bootstrap) development. Just run `make` to build the wasm binary: 10 | 11 | ```sh 12 | make 13 | ``` 14 | 15 | You can attach the xdp program to certain network interface by: 16 | 17 | ```sh 18 | ../wasm-bpf xdp.wasm device_name 19 | ``` 20 | 21 | For example `../wasm-bpf ./xdp.wasm enp1s0`. 22 | 23 | After that you can see it's output by: 24 | 25 | ```sh 26 | cat /sys/kernel/debug/tracing/trace_pipe 27 | ``` 28 | 29 | It will look like: 30 | 31 | ``` 32 | -0 [000] d.s.. 89309.534085: bpf_trace_printk: packet size: 66 33 | 34 | -0 [000] d.s.. 89309.595121: bpf_trace_printk: packet size: 66 35 | 36 | -0 [000] d.s.. 89309.686768: bpf_trace_printk: packet size: 126 37 | 38 | -0 [000] d.s.. 89309.689973: bpf_trace_printk: packet size: 66 39 | 40 | -0 [000] d.s.. 89309.836179: bpf_trace_printk: packet size: 54 41 | ``` -------------------------------------------------------------------------------- /examples/xdp/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /examples/xdp/xdp.bpf.c: -------------------------------------------------------------------------------- 1 | #include "vmlinux.h" 2 | #include 3 | 4 | SEC("xdp") 5 | int xdp_pass(struct xdp_md* ctx) { 6 | void* data = (void*)(long)ctx->data; 7 | void* data_end = (void*)(long)ctx->data_end; 8 | int pkt_sz = data_end - data; 9 | 10 | bpf_printk("packet size: %d\n", pkt_sz); 11 | return XDP_PASS; 12 | } 13 | 14 | char __license[] SEC("license") = "GPL"; -------------------------------------------------------------------------------- /examples/xdp/xdp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "libbpf-wasm.h" 6 | #include "xdp.skel.h" 7 | 8 | int main(int argc, char* argv[]) { 9 | struct xdp_bpf* skel = NULL; 10 | int err; 11 | 12 | skel = xdp_bpf__open_and_load(); 13 | if (!skel) { 14 | printf("Failed to open and load BPF skeleton\n"); 15 | return -1; 16 | } 17 | bpf_set_prog_attach_target(skel->progs.xdp_pass, argv[1]); 18 | 19 | err = xdp_bpf__attach(skel); 20 | if (err) { 21 | printf("Failed to attach BPF skeleton\n"); 22 | return -1; 23 | } 24 | printf("Load and attach BPF xdp successfully\n"); 25 | while (1) { 26 | sleep(10); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "fenix": { 4 | "inputs": { 5 | "nixpkgs": [ 6 | "nixpkgs" 7 | ], 8 | "rust-analyzer-src": "rust-analyzer-src" 9 | }, 10 | "locked": { 11 | "lastModified": 1679638953, 12 | "narHash": "sha256-OFATLLft/9vXHf+zqa6OQGTQ2rrCuD97KRzsrPsJRdY=", 13 | "owner": "nix-community", 14 | "repo": "fenix", 15 | "rev": "1336152ce31c0f781ae121aa297a5699c642a9fe", 16 | "type": "github" 17 | }, 18 | "original": { 19 | "owner": "nix-community", 20 | "repo": "fenix", 21 | "type": "github" 22 | } 23 | }, 24 | "flake-utils": { 25 | "locked": { 26 | "lastModified": 1678901627, 27 | "narHash": "sha256-U02riOqrKKzwjsxc/400XnElV+UtPUQWpANPlyazjH0=", 28 | "owner": "numtide", 29 | "repo": "flake-utils", 30 | "rev": "93a2b84fc4b70d9e089d029deacc3583435c2ed6", 31 | "type": "github" 32 | }, 33 | "original": { 34 | "owner": "numtide", 35 | "repo": "flake-utils", 36 | "type": "github" 37 | } 38 | }, 39 | "naersk": { 40 | "inputs": { 41 | "nixpkgs": [ 42 | "nixpkgs" 43 | ] 44 | }, 45 | "locked": { 46 | "lastModified": 1679567394, 47 | "narHash": "sha256-ZvLuzPeARDLiQUt6zSZFGOs+HZmE+3g4QURc8mkBsfM=", 48 | "owner": "nix-community", 49 | "repo": "naersk", 50 | "rev": "88cd22380154a2c36799fe8098888f0f59861a15", 51 | "type": "github" 52 | }, 53 | "original": { 54 | "owner": "nix-community", 55 | "repo": "naersk", 56 | "type": "github" 57 | } 58 | }, 59 | "nixpkgs": { 60 | "locked": { 61 | "lastModified": 1679437018, 62 | "narHash": "sha256-vOuiDPLHSEo/7NkiWtxpHpHgoXoNmrm+wkXZ6a072Fc=", 63 | "owner": "NixOS", 64 | "repo": "nixpkgs", 65 | "rev": "19cf008bb18e47b6e3b4e16e32a9a4bdd4b45f7e", 66 | "type": "github" 67 | }, 68 | "original": { 69 | "id": "nixpkgs", 70 | "ref": "nixos-unstable", 71 | "type": "indirect" 72 | } 73 | }, 74 | "root": { 75 | "inputs": { 76 | "fenix": "fenix", 77 | "flake-utils": "flake-utils", 78 | "naersk": "naersk", 79 | "nixpkgs": "nixpkgs" 80 | } 81 | }, 82 | "rust-analyzer-src": { 83 | "flake": false, 84 | "locked": { 85 | "lastModified": 1679520343, 86 | "narHash": "sha256-AJGSGWRfoKWD5IVTu1wEsR990wHbX0kIaolPqNMEh0c=", 87 | "owner": "rust-lang", 88 | "repo": "rust-analyzer", 89 | "rev": "eb791f31e688ae00908eb75d4c704ef60c430a92", 90 | "type": "github" 91 | }, 92 | "original": { 93 | "owner": "rust-lang", 94 | "ref": "nightly", 95 | "repo": "rust-analyzer", 96 | "type": "github" 97 | } 98 | } 99 | }, 100 | "root": "root", 101 | "version": 7 102 | } 103 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "wasm-bpf packages and dev shell flake"; 3 | 4 | outputs = 5 | { self 6 | , fenix 7 | , flake-utils 8 | , naersk 9 | , nixpkgs 10 | }: 11 | flake-utils.lib.eachDefaultSystem (system: 12 | let 13 | pkgs = nixpkgs.legacyPackages.${system}; 14 | toolchain = with fenix.packages.${system}; combine [ 15 | minimal.cargo 16 | minimal.rustc 17 | ]; 18 | fenix-naersk = naersk.lib.${system}.override { 19 | cargo = toolchain; 20 | rustc = toolchain; 21 | }; 22 | in 23 | { 24 | packages = rec { 25 | wasm-bpf = fenix-naersk.buildPackage { 26 | pname = "wasm-bpf"; 27 | hardeningDisable = [ "all" ]; 28 | cargoBuildOptions = x: x ++ [ "-p" "wasm-bpf" ]; 29 | buildInputs = with pkgs; [ 30 | zlib 31 | ]; 32 | nativeBuildInputs = with pkgs; [ 33 | clang 34 | elfutils 35 | pkg-config 36 | ]; 37 | src = ./runtime; 38 | }; 39 | 40 | default = wasm-bpf; 41 | }; 42 | 43 | devShell = pkgs.mkShell { 44 | buildInputs = with pkgs; [ 45 | toolchain 46 | zlib 47 | openssl 48 | ]; 49 | nativeBuildInputs = with pkgs; [ 50 | clang 51 | elfutils 52 | pkg-config 53 | ]; 54 | }; 55 | } 56 | ); 57 | 58 | inputs = { 59 | nixpkgs.url = "nixpkgs/nixos-unstable"; 60 | fenix = { 61 | url = "github:nix-community/fenix"; 62 | inputs.nixpkgs.follows = "nixpkgs"; 63 | }; 64 | flake-utils.url = "github:numtide/flake-utils"; 65 | naersk = { 66 | url = "github:nix-community/naersk"; 67 | inputs.nixpkgs.follows = "nixpkgs"; 68 | }; 69 | }; 70 | } 71 | 72 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/eunomia-bpf/wasm-bpf 2 | 3 | go 1.21 4 | -------------------------------------------------------------------------------- /runtime/.cargo/config: -------------------------------------------------------------------------------- 1 | [target.x86_64-unknown-linux-gnu] 2 | runner = "sudo -E" 3 | -------------------------------------------------------------------------------- /runtime/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | dump* 3 | wasm-bpf 4 | *.profraw 5 | -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["cli", "wasm-bpf-rs", "c-wrapper"] 3 | 4 | [profile.release] 5 | opt-level = 3 6 | lto = true 7 | debug = 0 8 | overflow-checks = false 9 | -------------------------------------------------------------------------------- /runtime/README.md: -------------------------------------------------------------------------------- 1 | # Runtime implementation example 2 | 3 | We have three runtime implementations, based on WAMR, wasmtime, and WasmEdge. 4 | 5 | This directory contains two runtime sample implementations that can be used to execute eBPF user programs in Wasm module format. 6 | 7 | - [cpp](./cpp): A minimal runtime implemented in `C++`, using [wasm-micro-runtime](https://github.com/bytecodealliance/wasm-micro-runtime) as runtime. 8 | - [wasm-bpf-rs](./rust): A runtime implemented in `Rust`, using [wasmtime](https://github.com/bytecodealliance/wasmtime) as runtime. Currently only this one supports running wasm modules geneated by `tinygo` 9 | - [cmd](cmd): A runtime executable that can be used to run wasm modules. 10 | 11 | Wasmedge runtime plugin can be found in https://github.com/eunomia-bpf/WasmEdge/tree/master/plugins%2Fwasm-bpf 12 | -------------------------------------------------------------------------------- /runtime/c-wrapper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-bpf-c-wrapper" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | [lib] 8 | name = "wasm_bpf" 9 | crate-type = ["cdylib", "staticlib"] 10 | 11 | [dependencies] 12 | anyhow = "1.0.71" 13 | wasm-bpf-rs = { path = "../wasm-bpf-rs", version = "*" } 14 | -------------------------------------------------------------------------------- /runtime/cli/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-bpf" 3 | version = "0.2.0" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "A WebAssembly runtime cli for eBPF programs based on libbpf and wasmtime (part of wasm-bpf project)" 7 | documentation = "https://docs.eunomia.dev/" 8 | readme = "README.md" 9 | repository = "https://github.com/eunomia-bpf/wasm-bpf" 10 | 11 | [dependencies] 12 | anyhow = "1.0.69" 13 | clap = { version = "4.1.4", features = ["derive"] } 14 | flexi_logger = "0.25.1" 15 | log = "0.4.17" 16 | wasm-bpf-rs = { path = "../wasm-bpf-rs", version = "0.3.3" } 17 | -------------------------------------------------------------------------------- /runtime/cli/Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | cargo build --release 3 | clean: 4 | cargo clean 5 | -------------------------------------------------------------------------------- /runtime/cli/src/log_format.rs: -------------------------------------------------------------------------------- 1 | //! SPDX-License-Identifier: MIT 2 | //! 3 | //! Copyright (c) 2023, eunomia-bpf 4 | //! All rights reserved. 5 | //! 6 | use flexi_logger::{DeferredNow, Record, TS_DASHES_BLANK_COLONS_DOT_BLANK}; 7 | 8 | pub fn my_log_format( 9 | w: &mut dyn std::io::Write, 10 | now: &mut DeferredNow, 11 | record: &Record, 12 | ) -> Result<(), std::io::Error> { 13 | write!( 14 | w, 15 | "[{}] {} [{}:{}] {}", 16 | now.format(TS_DASHES_BLANK_COLONS_DOT_BLANK), 17 | record.level(), 18 | record.module_path().unwrap_or(""), 19 | record.line().unwrap_or(0), 20 | &record.args() 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /runtime/cli/src/main.rs: -------------------------------------------------------------------------------- 1 | //! SPDX-License-Identifier: MIT 2 | //! 3 | //! Copyright (c) 2023, eunomia-bpf 4 | //! All rights reserved. 5 | //! 6 | use anyhow::{anyhow, Context}; 7 | use clap::Parser; 8 | use flexi_logger::Logger; 9 | use log_format::my_log_format; 10 | use std::fs; 11 | use wasm_bpf_rs::{run_wasm_bpf_module, Config}; 12 | 13 | mod log_format; 14 | 15 | #[derive(Parser, Debug)] 16 | #[command( 17 | author, 18 | version, 19 | about, 20 | long_about = "A WebAssembly runtime for eBPF user-space programs." 21 | )] 22 | struct CommandArgs { 23 | #[arg(help = "The WebAssembly Module file to run")] 24 | wasm_module_file: String, 25 | #[arg(long, help = "Display more logs")] 26 | verbose: bool, 27 | #[arg(short = 'm', long, help = "Wrapper module name", default_value_t = String::from("callback-wrapper"))] 28 | wrapper_module_name: String, 29 | #[arg(short = 'c', long, help = "Callback export name", default_value_t = String::from("go-callback"))] 30 | callback_export_name: String, 31 | #[arg(help = "Arguments that will be passed to the Wasm program")] 32 | args_to_wasm: Vec, 33 | } 34 | 35 | fn main() -> anyhow::Result<()> { 36 | let args = CommandArgs::parse(); 37 | Logger::try_with_str(if args.verbose { "debug" } else { "info" })? 38 | .format(my_log_format) 39 | .start()?; 40 | let mut args_to_wasm = args.args_to_wasm; 41 | args_to_wasm.insert(0, args.wasm_module_file.clone()); 42 | let binary = fs::read(&args.wasm_module_file) 43 | .with_context(|| anyhow!("Failed to read wasm module file"))?; 44 | run_wasm_bpf_module( 45 | &binary, 46 | &args_to_wasm[..], 47 | Config { 48 | callback_export_name: args.callback_export_name, 49 | wrapper_module_name: args.wrapper_module_name, 50 | ..Default::default() 51 | }, 52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /runtime/cpp/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: install coverage test docs help generate_tools build 2 | .DEFAULT_GOAL := build 3 | 4 | define BROWSER_PYSCRIPT 5 | import os, webbrowser, sys 6 | 7 | try: 8 | from urllib import pathname2url 9 | except: 10 | from urllib.request import pathname2url 11 | 12 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 13 | endef 14 | export BROWSER_PYSCRIPT 15 | 16 | define PRINT_HELP_PYSCRIPT 17 | import re, sys 18 | 19 | for line in sys.stdin: 20 | match = re.match(r'^([a-zA-Z_-]+):.*?## (.*)$$', line) 21 | if match: 22 | target, help = match.groups() 23 | print("%-20s %s" % (target, help)) 24 | endef 25 | export PRINT_HELP_PYSCRIPT 26 | 27 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 28 | INSTALL_LOCATION := ~/.local 29 | 30 | build: ## build as a tool 31 | cmake -Bbuild -DCMAKE_BUILD_TYPE=Release # -Dwasm-bpf_ENABLE_ASAN=1 32 | cmake --build build --config Release 33 | 34 | build-debug: ## build debug version 35 | cmake -Bbuild -DCMAKE_BUILD_TYPE=Debug -Dwasm-bpf_ENABLE_ASAN=1 36 | cmake --build build --config Debug 37 | 38 | build-lib: ## build as a library 39 | cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -Dwasm-bpf_BUILD_EXECUTABLE=0 # -Dwasm-bpf_ENABLE_ASAN=1 40 | cmake --build build --config Release 41 | 42 | # build-lib: ## build as a library 43 | # rm -rf build/ 44 | # cmake -Bbuild -DCMAKE_BUILD_TYPE=Release -Dwasm-bpf_BUILD_EXECUTABLE=0 # -Dwasm-bpf_ENABLE_ASAN=1 45 | # cmake --build build --config Release 46 | 47 | help: 48 | @python -c "$$PRINT_HELP_PYSCRIPT" < $(MAKEFILE_LIST) 49 | 50 | 51 | test: ## run tests quickly with ctest 52 | cd ./test && make 53 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -Dwasm-bpf_ENABLE_UNIT_TESTING=1 -Dwasm-bpf_ENABLE_ASAN=1 -Dwasm-bpf_ENABLE_CODE_COVERAGE=1 54 | cmake --build build 55 | cd build/ && sudo ctest -VV 56 | 57 | 58 | test-wasm: /opt/wasi-sdk 59 | cd test/wasm-apps && ./build.sh 60 | 61 | coverage: ## check code coverage quickly GCC 62 | rm -rf build/ 63 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -Dwasm-bpf_ENABLE_CODE_COVERAGE=1 64 | cmake --build build --config Release 65 | cd build/ && ctest -C Release -VV 66 | cd .. && (bash -c "find . -type f -name '*.gcno' -exec gcov -pb {} +" || true) 67 | 68 | install: ## install the package to the `INSTALL_LOCATION` 69 | rm -rf build/ 70 | cmake -Bbuild -DCMAKE_INSTALL_PREFIX=$(INSTALL_LOCATION) -DCMAKE_BUILD_TYPE=Release -Dwasm-bpf_ENABLE_UNIT_TESTING=0 71 | cmake --build build --config Release 72 | cmake --build build --target install --config Release 73 | 74 | format: ## format the project sources 75 | cmake -Bbuild 76 | cmake --build build --target clang-format 77 | 78 | clean: ## clean the project build files 79 | rm -rf build/ 80 | rm -rf docs/ 81 | cd test && make clean -------------------------------------------------------------------------------- /runtime/cpp/cmake/FindLibBpf.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | 3 | find_path(LIBBPF_INCLUDE_DIRS 4 | NAMES 5 | bpf/bpf.h 6 | bpf/btf.h 7 | bpf/libbpf.h 8 | PATHS 9 | /usr/include 10 | /usr/local/include 11 | /opt/local/include 12 | /sw/include 13 | ENV CPATH) 14 | 15 | find_library(LIBBPF_LIBRARIES 16 | NAMES 17 | bpf 18 | PATHS 19 | /usr/lib 20 | /usr/local/lib 21 | /opt/local/lib 22 | /sw/lib 23 | ENV LIBRARY_PATH 24 | ENV LD_LIBRARY_PATH) 25 | 26 | include (FindPackageHandleStandardArgs) 27 | 28 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibBpf "Please install the libbpf development package" 29 | LIBBPF_LIBRARIES 30 | LIBBPF_INCLUDE_DIRS) 31 | 32 | mark_as_advanced(LIBBPF_INCLUDE_DIRS LIBBPF_LIBRARIES) 33 | -------------------------------------------------------------------------------- /runtime/cpp/cmake/SourcesAndHeaders.cmake: -------------------------------------------------------------------------------- 1 | set(sources 2 | src/wasm-bpf.cpp 3 | ) 4 | 5 | set(exe_sources 6 | src/main.cpp 7 | ${sources} 8 | ) 9 | 10 | set(headers 11 | include/ 12 | ) 13 | 14 | EXECUTE_PROCESS( COMMAND uname -m COMMAND tr -d '\n' OUTPUT_VARIABLE ARCHITECTURE ) 15 | 16 | set(third_party_headers 17 | third_party/includes/ 18 | third_party/bpftool/libbpf/include/uapi 19 | third_party/bpftool/libbpf/ 20 | ) 21 | 22 | set(skel_includes 23 | ) 24 | 25 | set(test_sources 26 | src/bpf_api_test.cpp 27 | src/memory_check_test_driver.cpp 28 | ) 29 | -------------------------------------------------------------------------------- /runtime/cpp/cmake/version.hpp.in: -------------------------------------------------------------------------------- 1 | #ifndef @PROJECT_NAME_UPPERCASE@_VERSION_H_ 2 | #define @PROJECT_NAME_UPPERCASE@_VERSION_H_ 3 | 4 | #define @PROJECT_NAME_UPPERCASE@_VERSION "@PROJECT_VERSION@" 5 | 6 | #define @PROJECT_NAME_UPPERCASE@_MAJOR_VERSION @PROJECT_VERSION_MAJOR@ 7 | #define @PROJECT_NAME_UPPERCASE@_MINOR_VERSION @PROJECT_VERSION_MINOR@ 8 | #define @PROJECT_NAME_UPPERCASE@_PATCH_VERSION @PROJECT_VERSION_PATCH@ 9 | 10 | #endif // @PROJECT_NAME_UPPERCASE@_VERSION_H_ 11 | 12 | -------------------------------------------------------------------------------- /runtime/cpp/cmake/wasm-bpfConfig.cmake.in: -------------------------------------------------------------------------------- 1 | set(@PROJECT_NAME@_VERSION @PROJECT_VERSION@) 2 | 3 | @PACKAGE_INIT@ 4 | 5 | set_and_check(@PROJECT_NAME@_INCLUDE_DIR "@CMAKE_INSTALL_FULL_INCLUDEDIR@") 6 | 7 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 8 | 9 | check_required_components(@PROJECT_NAME@) 10 | -------------------------------------------------------------------------------- /runtime/cpp/include/bpf-api.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 |  * 3 |  * Copyright (c) 2023, eunomia-bpf 4 |  * All rights reserved. 5 | * 6 | * This is a minimal working example of wasm-bpf runtime implementation. 7 |  */ 8 | #ifndef __BPF_WASM_API_H 9 | #define __BPF_WASM_API_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "wasm_export.h" 18 | 19 | #define POLL_TIMEOUT_MS 100 20 | #define DEBUG_LIBBPF_RUNTIME 0 21 | 22 | extern "C" { 23 | struct bpf_map; 24 | struct bpf_object; 25 | struct bpf_link; 26 | void bpf_object__close(struct bpf_object* object); 27 | int bpf_link__destroy(struct bpf_link* link); 28 | } 29 | 30 | /// @brief init libbpf callbacks 31 | void init_libbpf(void); 32 | 33 | typedef int (*bpf_buffer_sample_fn)(void* ctx, void* data, size_t size); 34 | 35 | /// An absraction of a bpf ring buffer or perf buffer copied from bcc. 36 | /// see https://github.com/iovisor/bcc/blob/master/libbpf-tools/compat.c 37 | class bpf_buffer { 38 | bpf_buffer_sample_fn fn; 39 | wasm_exec_env_t callback_exec_env; 40 | uint32_t wasm_ctx; 41 | uint32_t wasm_sample_function; 42 | void* poll_data; 43 | size_t max_poll_size; 44 | 45 | public: 46 | /// @brief sample callback which calls the wasm handler indirectly 47 | int bpf_buffer_sample(void* data, size_t size); 48 | /// @brief set the wasm callback parameters 49 | void set_callback_params(wasm_exec_env_t exec_env, 50 | uint32_t sample_func, 51 | void* data, 52 | size_t max_size, 53 | uint32_t ctx); 54 | /// @brief polling the bpf buffer 55 | virtual int bpf_buffer__poll(int timeout_ms) = 0; 56 | /// @brief open the bpf buffer map 57 | virtual int bpf_buffer__open(int fd, 58 | bpf_buffer_sample_fn sample_cb, 59 | void* ctx) = 0; 60 | virtual ~bpf_buffer() = default; 61 | }; 62 | 63 | /// @brief bpf program instance 64 | class wasm_bpf_program { 65 | std::unique_ptr obj{ 66 | nullptr, bpf_object__close}; 67 | std::unique_ptr buffer; 68 | std::unordered_set> links; 69 | 70 | public: 71 | int bpf_map_fd_by_name(const char* name); 72 | int load_bpf_object(const void* obj_buf, size_t obj_buf_sz); 73 | int attach_bpf_program(const char* name, const char* attach_target); 74 | int bpf_buffer_poll(wasm_exec_env_t exec_env, 75 | int fd, 76 | int32_t sample_func, 77 | uint32_t ctx, 78 | void* buffer_data, 79 | size_t max_size, 80 | int timeout_ms); 81 | bpf_map* map_ptr_by_fd(int fd); 82 | }; 83 | 84 | /// @brief A map to hold bpf programs handles. Instance of this type will be shared in a wasm program 85 | using bpf_program_manager = 86 | std::unordered_map>; 87 | 88 | enum bpf_map_cmd { 89 | _BPF_MAP_LOOKUP_ELEM = 1, 90 | _BPF_MAP_UPDATE_ELEM, 91 | _BPF_MAP_DELETE_ELEM, 92 | _BPF_MAP_GET_NEXT_KEY, 93 | }; 94 | /// Operate on a bpf map. 95 | int bpf_map_operate(int fd, 96 | int cmd, 97 | void* key, 98 | void* value, 99 | void* next_key, 100 | uint64_t flags); 101 | extern "C" { 102 | /// The main entry, argc and argv will be passed to the wasm module. 103 | int wasm_main(unsigned char* buf, unsigned int size, int argc, char* argv[]); 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /runtime/cpp/src/README.md: -------------------------------------------------------------------------------- 1 | # runtime 2 | 3 | A minimal working example of wasm-bpf runtime 4 | -------------------------------------------------------------------------------- /runtime/cpp/src/main.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | * 3 | * Copyright (c) 2022, eunomia-bpf org 4 | * All rights reserved. 5 | */ 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "bpf-api.h" 13 | 14 | int main(int argc, char* argv[]) { 15 | if (argc < 2) { 16 | printf("Usage: %s [wasm args]\n", argv[0]); 17 | return -1; 18 | } 19 | signal(SIGINT, [](int x) { 20 | std::cerr << "Ctrl C exit..." << std::endl; 21 | exit(0); 22 | }); 23 | std::ifstream file(argv[1]); 24 | std::vector wasm_module((std::istreambuf_iterator(file)), 25 | std::istreambuf_iterator()); 26 | return wasm_main(wasm_module.data(), (unsigned int)wasm_module.size(), 27 | argc - 1, argv + 1); 28 | } 29 | -------------------------------------------------------------------------------- /runtime/cpp/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | # 4 | # Project details 5 | # 6 | 7 | project( 8 | ${CMAKE_PROJECT_NAME}Tests 9 | LANGUAGES CXX C 10 | ) 11 | 12 | verbose_message("Adding tests under ${CMAKE_PROJECT_NAME}Tests...") 13 | 14 | foreach(file ${test_sources}) 15 | string(REGEX REPLACE "(.*/)([a-zA-Z0-9_ ]+)(\.cpp|\.c)" "\\2" test_name ${file}) 16 | add_executable(${test_name}_Tests ${file}) 17 | add_dependencies(${test_name}_Tests libbpf-build) 18 | target_link_libraries(${test_name}_Tests PUBLIC ${LIBBPF_LIBRARIES} -lelf -lz) 19 | target_include_directories(${test_name}_Tests SYSTEM INTERFACE ${LIBBPF_INCLUDE_DIRS}) 20 | 21 | # 22 | # Set the compiler standard 23 | # 24 | 25 | target_compile_features(${test_name}_Tests PUBLIC cxx_std_17) 26 | target_include_directories(${test_name}_Tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include) 27 | 28 | # 29 | # Setup code coverage if enabled 30 | # 31 | 32 | if (${CMAKE_PROJECT_NAME}_ENABLE_CODE_COVERAGE) 33 | target_compile_options(${CMAKE_PROJECT_NAME} PUBLIC -O0 -g -fprofile-arcs -ftest-coverage) 34 | target_link_options(${CMAKE_PROJECT_NAME} PUBLIC -fprofile-arcs -ftest-coverage) 35 | verbose_message("Code coverage is enabled and provided with GCC.") 36 | endif() 37 | 38 | # 39 | # Load the desired unit testing framework 40 | # 41 | # Currently supported: GoogleTest (and GoogleMock), Catch2. 42 | 43 | if(${CMAKE_PROJECT_NAME}_BUILD_EXECUTABLE) 44 | set(${CMAKE_PROJECT_NAME}_TEST_LIB ${CMAKE_PROJECT_NAME}_LIB) 45 | else() 46 | set(${CMAKE_PROJECT_NAME}_TEST_LIB ${CMAKE_PROJECT_NAME}) 47 | endif() 48 | 49 | if(${CMAKE_PROJECT_NAME}_USE_GTEST) 50 | find_package(GTest REQUIRED) 51 | 52 | if(${CMAKE_PROJECT_NAME}_USE_GOOGLE_MOCK) 53 | set(GOOGLE_MOCK_LIBRARIES GTest::gmock GTest::gmock_main) 54 | endif() 55 | 56 | target_link_libraries( 57 | ${test_name}_Tests 58 | PUBLIC 59 | GTest::GTest 60 | GTest::Main 61 | ${GOOGLE_MOCK_LIBRARIES} 62 | ${${CMAKE_PROJECT_NAME}_TEST_LIB} 63 | ) 64 | elseif(${CMAKE_PROJECT_NAME}_USE_CATCH2) 65 | find_package(Catch2 REQUIRED) 66 | target_link_libraries( 67 | ${test_name}_Tests 68 | PUBLIC 69 | Catch2::Catch2WithMain 70 | ${${CMAKE_PROJECT_NAME}_TEST_LIB} 71 | ) 72 | else() 73 | target_link_libraries( 74 | ${test_name}_Tests 75 | PUBLIC 76 | ${${CMAKE_PROJECT_NAME}_TEST_LIB} vmlib ${LIBBPF_LIBRARIES} -lelf -lz 77 | ) 78 | message("Unknown testing library. Please setup your desired unit testing library by using `target_link_libraries`.") 79 | endif() 80 | 81 | # 82 | # Add the unit tests 83 | # 84 | 85 | add_test( 86 | NAME 87 | ${test_name} 88 | COMMAND 89 | ${test_name}_Tests 90 | ) 91 | endforeach() 92 | 93 | verbose_message("Finished adding unit tests for ${CMAKE_PROJECT_NAME}.") 94 | -------------------------------------------------------------------------------- /runtime/cpp/test/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | cd asserts && make 3 | cd wasm-apps && make 4 | 5 | DEL = rm -rf 6 | 7 | clean: 8 | cd asserts && make clean 9 | cd wasm-apps && make clean -------------------------------------------------------------------------------- /runtime/cpp/test/asserts/.gitignore: -------------------------------------------------------------------------------- 1 | *.data.h -------------------------------------------------------------------------------- /runtime/cpp/test/asserts/Makefile: -------------------------------------------------------------------------------- 1 | 2 | DEL = rm -rf 3 | FILES = $(shell ls *.bpf.o | awk '{split($$0,a,".");print a[1]}') 4 | 5 | all: $(FILES) 6 | 7 | $(FILES) : % : %.bpf.o 8 | $(DEL) $@.data.h 9 | touch $@.data.h 10 | echo "#ifndef _$@_H" >> $@.data.h 11 | echo "#define _$@_H" >> $@.data.h 12 | echo "const char* $@_data = \"$$(cat $< | base64 -w 0)\";" >> $@.data.h 13 | echo "#endif" >> $@.data.h 14 | 15 | %.data.h : % 16 | 17 | clean: 18 | $(DEL) *.data.h -------------------------------------------------------------------------------- /runtime/cpp/test/asserts/runqlat.bpf.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/cpp/test/asserts/runqlat.bpf.o -------------------------------------------------------------------------------- /runtime/cpp/test/src/bpf_api_test.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause 2 | * 3 | * Copyright (c) 2023, eunomia-bpf org 4 | * All rights reserved. 5 | */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "bpf-api.h" 17 | extern "C" { 18 | #include 19 | } 20 | 21 | #define TASK_COMM_LEN 16 22 | #define MAX_SLOTS 26 23 | 24 | struct hist { 25 | unsigned int slots[MAX_SLOTS]; 26 | char comm[TASK_COMM_LEN]; 27 | } __attribute__((packed)); 28 | 29 | static int print_log2_hists(bpf_map* map) { 30 | int err; 31 | uint32_t lookup_key = -2, next_key; 32 | struct hist hist; 33 | while (!(err = bpf_map__get_next_key(map, &lookup_key, &next_key, 34 | sizeof(next_key)))) { 35 | err = bpf_map__lookup_elem(map, &next_key, sizeof(next_key), &hist, 36 | sizeof(hist), 0); 37 | if (err < 0) { 38 | fprintf(stderr, "failed to lookup hist: %d\n", err); 39 | return -1; 40 | } 41 | printf("%-8d %-8d %-8d %-8d %-8d %-8d %-8d %-8d %-8d %-8d\n", next_key, 42 | hist.slots[0], hist.slots[1], hist.slots[2], hist.slots[3], 43 | hist.slots[4], hist.slots[5], hist.slots[6], hist.slots[7], 44 | hist.slots[8]); 45 | lookup_key = next_key; 46 | } 47 | printf("err %d\n", err); 48 | 49 | lookup_key = -2; 50 | while (!(err = bpf_map__get_next_key(map, &lookup_key, &next_key, 51 | sizeof(next_key)))) { 52 | err = bpf_map__delete_elem(map, &next_key, sizeof(next_key), 0); 53 | if (err < 0) { 54 | fprintf(stderr, "failed to cleanup hist : %d\n", err); 55 | return -1; 56 | } 57 | lookup_key = next_key; 58 | } 59 | return 0; 60 | } 61 | 62 | int main(int argc, char** argv) { 63 | init_libbpf(); 64 | wasm_bpf_program* program = new wasm_bpf_program(); 65 | std::ifstream runqlat("../../test/asserts/runqlat.bpf.o"); 66 | std::vector runqlat_str((std::istreambuf_iterator(runqlat)), 67 | std::istreambuf_iterator()); 68 | int res = program->load_bpf_object(runqlat_str.data(), runqlat_str.size()); 69 | if (res < 0) { 70 | printf("load_bpf_object failed\n"); 71 | delete program; 72 | return 0; 73 | } 74 | res = program->attach_bpf_program("handle_sched_wakeup", NULL); 75 | if (res < 0) { 76 | printf("attach_bpf_program failed handle_sched_wakeup\n"); 77 | delete program; 78 | return -1; 79 | } 80 | res = program->attach_bpf_program("handle_sched_wakeup_new", NULL); 81 | if (res < 0) { 82 | printf("attach_bpf_program failed\n"); 83 | delete program; 84 | return -1; 85 | } 86 | res = program->attach_bpf_program("sched_switch", NULL); 87 | if (res < 0) { 88 | printf("attach_bpf_program failed\n"); 89 | delete program; 90 | return -1; 91 | } 92 | struct tm* tm; 93 | char ts[32]; 94 | time_t t; 95 | int fd = program->bpf_map_fd_by_name("hists"); 96 | printf("fd = %d\n", fd); 97 | /* main: poll */ 98 | int count = 0; 99 | while (count < 5) { 100 | sleep(1); 101 | printf("\n"); 102 | time(&t); 103 | tm = localtime(&t); 104 | strftime(ts, sizeof(ts), "%H:%M:%S", tm); 105 | printf("%-8s\n", ts); 106 | bpf_map* map = program->map_ptr_by_fd(fd); 107 | int err = print_log2_hists(map); 108 | if (err) 109 | break; 110 | count++; 111 | } 112 | delete program; 113 | return 0; 114 | } 115 | -------------------------------------------------------------------------------- /runtime/cpp/test/src/memory_check_test_driver.cpp: -------------------------------------------------------------------------------- 1 | #include "bpf-api.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | struct TestModule { 8 | const char* file_name; 9 | const char* desc; 10 | }; 11 | 12 | static TestModule TEST_MODULES[] = { 13 | {.file_name = "memory_test_1.wasm", 14 | .desc = "Test that if wasm-bpf an handle a buffer of invalid length when " 15 | "loading bpf program"}, 16 | {.file_name = "memory_test_2.wasm", 17 | .desc = "Test that if wasm-bpf can handle an invalid string"}}; 18 | 19 | int main() { 20 | for (const auto& item : TEST_MODULES) { 21 | using namespace std; 22 | cout << "Performing test for `" << item.file_name << "`" << endl; 23 | cout << "Desc: " << item.desc << endl; 24 | ifstream module_file(string("../../test/wasm-apps/") + item.file_name); 25 | assert((bool)module_file); 26 | vector wasm_module((istreambuf_iterator(module_file)), 27 | istreambuf_iterator()); 28 | const char* args[] = {"wasm-bpf", item.file_name}; 29 | int ret = wasm_main(wasm_module.data(), 30 | (unsigned int)wasm_module.size(), 2, (char**)args); 31 | cout << "exit code = " << ret << endl; 32 | assert(ret == 0); 33 | cout << "Test for " << item.file_name << " done" << endl; 34 | cout << endl; 35 | cout << endl; 36 | } 37 | return 0; 38 | } -------------------------------------------------------------------------------- /runtime/cpp/test/wasm-apps/.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm -------------------------------------------------------------------------------- /runtime/cpp/test/wasm-apps/Makefile: -------------------------------------------------------------------------------- 1 | 2 | DEL = rm -rf 3 | 4 | # all: binding.wasm memory_test_1.wasm memory_test_2.wasm 5 | 6 | MODULES = binding memory_test_1 memory_test_2 7 | 8 | MODULE_WASM_FILES = $(foreach n, $(MODULES), $(n).wasm) 9 | 10 | all: $(MODULE_WASM_FILES) 11 | 12 | $(MODULE_WASM_FILES) : %.wasm : %.cpp /opt/wasi-sdk/bin/clang 13 | /opt/wasi-sdk/bin/clang \ 14 | --target=wasm32-wasi \ 15 | -O0 -z stack-size=4096 -Wl,--initial-memory=1048576 \ 16 | --sysroot=/opt/wasi-sdk/share/wasi-sysroot \ 17 | -Wl,--export=all \ 18 | -Wl,--export=bpf_main \ 19 | -Wl,--export=process_event \ 20 | -Wl,--export-table \ 21 | -Wl,--strip-all,--no-entry \ 22 | -Wl,--allow-undefined \ 23 | -o $@ $< 24 | 25 | clean: 26 | $(DEL) *.wasm 27 | 28 | -------------------------------------------------------------------------------- /runtime/cpp/test/wasm-apps/api.h: -------------------------------------------------------------------------------- 1 | #ifndef _API_H 2 | #define _API_H 3 | #include 4 | #define IMPORT_MODULE "wasm_bpf" 5 | #define ATTR(name) \ 6 | __attribute__((import_module(IMPORT_MODULE), import_name(name))) 7 | /// should be externref type for bpf_object_skel. 8 | typedef uint64_t bpf_object_skel; 9 | /// lookup a bpf map fd by name. 10 | ATTR("wasm_bpf_map_fd_by_name") 11 | int wasm_bpf_map_fd_by_name(bpf_object_skel obj, const char* name); 12 | /// detach and close a bpf program. 13 | ATTR("wasm_close_bpf_object") 14 | int wasm_close_bpf_object(bpf_object_skel obj); 15 | /// CO-RE load a bpf object into the kernel. 16 | ATTR("wasm_load_bpf_object") 17 | bpf_object_skel wasm_load_bpf_object(const void* obj_buf, int obj_buf_sz); 18 | /// attach a bpf program to a kernel hook. 19 | ATTR("wasm_attach_bpf_program") 20 | int wasm_attach_bpf_program(bpf_object_skel obj, 21 | const char* name, 22 | const char* attach_target); 23 | /// poll a bpf buffer, and call a wasm callback indicated by sample_func. 24 | /// the first time to call this function will open and create a bpf buffer. 25 | ATTR("wasm_bpf_buffer_poll") 26 | int wasm_bpf_buffer_poll(bpf_object_skel program, 27 | int fd, 28 | int32_t sample_func, 29 | uint32_t ctx, 30 | char* data, 31 | int max_size, 32 | int timeout_ms); 33 | /// lookup, update, delete, and get_next_key operations on a bpf map. 34 | ATTR("wasm_bpf_map_operate") 35 | int wasm_bpf_map_operate(int fd, 36 | int cmd, 37 | void* key, 38 | void* value, 39 | void* next_key, 40 | uint64_t flags); 41 | 42 | #endif -------------------------------------------------------------------------------- /runtime/cpp/test/wasm-apps/base64decode.h: -------------------------------------------------------------------------------- 1 | #ifndef _BASE64DECODE_H 2 | #define _BASE64DECODE_H 3 | 4 | // Copy from 5 | // https://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c 6 | 7 | #include 8 | #include 9 | 10 | static char encoding_table[] = { 11 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 12 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 13 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 14 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 15 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; 16 | static char decoding_table[256] = { 17 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19 | 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 20 | 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 22 | 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 23 | 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30 | 0, 0, 0, 0, 0, 0, 0, 0, 0}; 31 | static int mod_table[] = {0, 2, 1}; 32 | 33 | unsigned char* base64_decode(const char* data, 34 | size_t input_length, 35 | size_t* output_length) { 36 | if (input_length % 4 != 0) 37 | return NULL; 38 | 39 | *output_length = input_length / 4 * 3; 40 | if (data[input_length - 1] == '=') 41 | (*output_length)--; 42 | if (data[input_length - 2] == '=') 43 | (*output_length)--; 44 | 45 | unsigned char* decoded_data = (unsigned char*)malloc(*output_length); 46 | if (decoded_data == NULL) 47 | return NULL; 48 | 49 | for (int i = 0, j = 0; i < input_length;) { 50 | uint32_t sextet_a = 51 | data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; 52 | uint32_t sextet_b = 53 | data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; 54 | uint32_t sextet_c = 55 | data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; 56 | uint32_t sextet_d = 57 | data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; 58 | 59 | uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + 60 | (sextet_c << 1 * 6) + (sextet_d << 0 * 6); 61 | 62 | if (j < *output_length) 63 | decoded_data[j++] = (triple >> 2 * 8) & 0xFF; 64 | if (j < *output_length) 65 | decoded_data[j++] = (triple >> 1 * 8) & 0xFF; 66 | if (j < *output_length) 67 | decoded_data[j++] = (triple >> 0 * 8) & 0xFF; 68 | } 69 | 70 | return decoded_data; 71 | } 72 | 73 | #endif -------------------------------------------------------------------------------- /runtime/cpp/test/wasm-apps/binding.cpp: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: MIT 2 |  * 3 |  * Copyright (c) 2023, eunomia-bpf 4 |  * All rights reserved. 5 |  */ 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | int main() { 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /runtime/cpp/test/wasm-apps/memory_test_1.cpp: -------------------------------------------------------------------------------- 1 | #include "api.h" 2 | #include 3 | int main(int argc, char** argv) { 4 | int a; 5 | auto ret1 = wasm_load_bpf_object((const void*)(&a), 114514); 6 | assert(ret1 == 0); 7 | return 0; 8 | } -------------------------------------------------------------------------------- /runtime/cpp/test/wasm-apps/memory_test_2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "../asserts/runqlat.data.h" 7 | #include "api.h" 8 | #include "base64decode.h" 9 | // char buf[1 << 20]; 10 | #define SIZE (1 << 20) 11 | int main(int argc, char** argv) { 12 | using namespace std; 13 | size_t len = strlen(runqlat_data); 14 | size_t out_len; 15 | const char* buf = (const char*)base64_decode(runqlat_data, len, &out_len); 16 | uint64_t handle = wasm_load_bpf_object(buf, out_len); 17 | assert(handle != 0); 18 | char* p = (char*)malloc(SIZE); 19 | for (int i = 0; i < SIZE; i++) 20 | p[i] = 1; 21 | int ret = wasm_attach_bpf_program(handle, p, p); 22 | printf("attach ret = %d\n", ret); 23 | assert(ret != 0); 24 | return 0; 25 | } -------------------------------------------------------------------------------- /runtime/cpp/third_party/bpf/libbpf_common.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | 3 | /* 4 | * Common user-facing libbpf helpers. 5 | * 6 | * Copyright (c) 2019 Facebook 7 | */ 8 | 9 | #ifndef __LIBBPF_LIBBPF_COMMON_H 10 | #define __LIBBPF_LIBBPF_COMMON_H 11 | 12 | #include 13 | #include "libbpf_version.h" 14 | 15 | #ifndef LIBBPF_API 16 | #define LIBBPF_API __attribute__((visibility("default"))) 17 | #endif 18 | 19 | #define LIBBPF_DEPRECATED(msg) __attribute__((deprecated(msg))) 20 | 21 | /* Mark a symbol as deprecated when libbpf version is >= {major}.{minor} */ 22 | #define LIBBPF_DEPRECATED_SINCE(major, minor, msg) \ 23 | __LIBBPF_MARK_DEPRECATED_ ## major ## _ ## minor \ 24 | (LIBBPF_DEPRECATED("libbpf v" # major "." # minor "+: " msg)) 25 | 26 | #define __LIBBPF_CURRENT_VERSION_GEQ(major, minor) \ 27 | (LIBBPF_MAJOR_VERSION > (major) || \ 28 | (LIBBPF_MAJOR_VERSION == (major) && LIBBPF_MINOR_VERSION >= (minor))) 29 | 30 | /* Add checks for other versions below when planning deprecation of API symbols 31 | * with the LIBBPF_DEPRECATED_SINCE macro. 32 | */ 33 | #if __LIBBPF_CURRENT_VERSION_GEQ(1, 0) 34 | #define __LIBBPF_MARK_DEPRECATED_1_0(X) X 35 | #else 36 | #define __LIBBPF_MARK_DEPRECATED_1_0(X) 37 | #endif 38 | 39 | /* This set of internal macros allows to do "function overloading" based on 40 | * number of arguments provided by used in backwards-compatible way during the 41 | * transition to libbpf 1.0 42 | * It's ugly but necessary evil that will be cleaned up when we get to 1.0. 43 | * See bpf_prog_load() overload for example. 44 | */ 45 | #define ___libbpf_cat(A, B) A ## B 46 | #define ___libbpf_select(NAME, NUM) ___libbpf_cat(NAME, NUM) 47 | #define ___libbpf_nth(_1, _2, _3, _4, _5, _6, N, ...) N 48 | #define ___libbpf_cnt(...) ___libbpf_nth(__VA_ARGS__, 6, 5, 4, 3, 2, 1) 49 | #define ___libbpf_overload(NAME, ...) ___libbpf_select(NAME, ___libbpf_cnt(__VA_ARGS__))(__VA_ARGS__) 50 | 51 | /* Helper macro to declare and initialize libbpf options struct 52 | * 53 | * This dance with uninitialized declaration, followed by memset to zero, 54 | * followed by assignment using compound literal syntax is done to preserve 55 | * ability to use a nice struct field initialization syntax and **hopefully** 56 | * have all the padding bytes initialized to zero. It's not guaranteed though, 57 | * when copying literal, that compiler won't copy garbage in literal's padding 58 | * bytes, but that's the best way I've found and it seems to work in practice. 59 | * 60 | * Macro declares opts struct of given type and name, zero-initializes, 61 | * including any extra padding, it with memset() and then assigns initial 62 | * values provided by users in struct initializer-syntax as varargs. 63 | */ 64 | #define LIBBPF_OPTS(TYPE, NAME, ...) \ 65 | struct TYPE NAME = ({ \ 66 | memset(&NAME, 0, sizeof(struct TYPE)); \ 67 | (struct TYPE) { \ 68 | .sz = sizeof(struct TYPE), \ 69 | __VA_ARGS__ \ 70 | }; \ 71 | }) 72 | 73 | #endif /* __LIBBPF_LIBBPF_COMMON_H */ 74 | -------------------------------------------------------------------------------- /runtime/cpp/third_party/bpf/libbpf_version.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ 2 | /* Copyright (C) 2021 Facebook */ 3 | #ifndef __LIBBPF_VERSION_H 4 | #define __LIBBPF_VERSION_H 5 | 6 | #define LIBBPF_MAJOR_VERSION 1 7 | #define LIBBPF_MINOR_VERSION 1 8 | 9 | #endif /* __LIBBPF_VERSION_H */ 10 | -------------------------------------------------------------------------------- /runtime/cpp/third_party/vmlinux/arm64/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_516.h -------------------------------------------------------------------------------- /runtime/cpp/third_party/vmlinux/mips/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_610.h -------------------------------------------------------------------------------- /runtime/cpp/third_party/vmlinux/powerpc/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_514.h -------------------------------------------------------------------------------- /runtime/cpp/third_party/vmlinux/riscv/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_519.h -------------------------------------------------------------------------------- /runtime/cpp/third_party/vmlinux/vmlinux.h: -------------------------------------------------------------------------------- 1 | x86/vmlinux_508.h -------------------------------------------------------------------------------- /runtime/cpp/third_party/vmlinux/x86/vmlinux.h: -------------------------------------------------------------------------------- 1 | vmlinux_508.h -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | dump* 3 | wasm-bpf 4 | coverage 5 | lcov.info 6 | rust-clippy-results.sarif 7 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wasm-bpf-rs" 3 | version = "0.3.3" 4 | edition = "2021" 5 | license = "MIT" 6 | description = "A WebAssembly runtime library for eBPF programs based on libbpf and wasmtime" 7 | documentation = "https://docs.eunomia.dev/" 8 | readme = "README.md" 9 | repository = "https://github.com/eunomia-bpf/wasm-bpf" 10 | 11 | [dependencies] 12 | anyhow = { version = "1.0.69", features = ["backtrace"] } 13 | flexi_logger = "0.25.1" 14 | libbpf-rs = "0.20.1" 15 | log = "0.4.17" 16 | wasmtime = "5.0.1" 17 | wasmtime-wasi = "5.0.0" 18 | wasi-common = "5.0.0" 19 | wiggle = "5.0.1" 20 | ouroboros = "0.16.0" 21 | libc = "0.2.147" 22 | errno = "0.3.1" 23 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean 2 | build: 3 | cargo build --release 4 | clean: 5 | cargo clean 6 | 7 | .PHONY: test 8 | test: 9 | cargo install clippy-sarif sarif-fmt grcov 10 | rustup component add llvm-tools-preview 11 | CARGO_INCREMENTAL=0 RUSTFLAGS="-Cinstrument-coverage -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off" RUSTDOCFLAGS="-Cpanic=abort" cargo test 12 | grcov . --binary-path ../target/debug/ --llvm -s . -t html --branch --ignore-not-existing -o ./coverage/ 13 | grcov . --binary-path ../target/debug/ --llvm -s . -t lcov --branch --ignore-not-existing -o ./lcov.info 14 | cargo clippy --all-features --message-format=json | clippy-sarif | tee rust-clippy-results.sarif | sarif-fmt 15 | cargo fmt --check -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/src/bpf/close.rs: -------------------------------------------------------------------------------- 1 | //! SPDX-License-Identifier: MIT 2 | //! 3 | //! Copyright (c) 2023, eunomia-bpf 4 | //! All rights reserved. 5 | //! 6 | use std::collections::hash_map::Entry; 7 | 8 | use log::debug; 9 | 10 | use crate::state::CallerType; 11 | 12 | use super::BpfObjectType; 13 | 14 | /// close and detach a bpf object 15 | pub fn wasm_close_bpf_object(mut caller: CallerType, program: BpfObjectType) -> i32 { 16 | debug!("Close bpf object: {}", program); 17 | let state = caller.data_mut(); 18 | match state.object_map.entry(program) { 19 | Entry::Occupied(v) => { 20 | v.remove(); 21 | 0 22 | } 23 | Entry::Vacant(_) => { 24 | debug!("Invalid bpf object id: {}", program); 25 | -1 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/src/bpf/fd_by_name.rs: -------------------------------------------------------------------------------- 1 | //! SPDX-License-Identifier: MIT 2 | //! 3 | //! Copyright (c) 2023, eunomia-bpf 4 | //! All rights reserved. 5 | //! 6 | use log::debug; 7 | 8 | use crate::{ensure_c_str, ensure_program_mut_by_caller, state::CallerType}; 9 | 10 | use super::{BpfObjectType, WasmString}; 11 | 12 | /// get map fd by name from a bpf object 13 | pub fn wasm_bpf_map_fd_by_name( 14 | mut caller: CallerType, 15 | program: BpfObjectType, 16 | name: WasmString, 17 | ) -> i32 { 18 | debug!("map fd by name"); 19 | let map_name = ensure_c_str!(caller, name); 20 | let object = ensure_program_mut_by_caller!(caller, program); 21 | let object_guard = object.get_object(); 22 | let map = match object_guard.map(&map_name) { 23 | Some(v) => v, 24 | None => { 25 | debug!("Invalid map name: {}", map_name); 26 | return -1; 27 | } 28 | }; 29 | 30 | map.fd() 31 | } 32 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/src/bpf/load.rs: -------------------------------------------------------------------------------- 1 | //! SPDX-License-Identifier: MIT 2 | //! 3 | //! Copyright (c) 2023, eunomia-bpf 4 | //! All rights reserved. 5 | //! 6 | use std::{cell::RefCell, rc::Rc}; 7 | 8 | use libbpf_rs::ObjectBuilder; 9 | use log::debug; 10 | 11 | use crate::{ 12 | state::{CallerType, WrapperObject}, 13 | utils::CallerUtils, 14 | }; 15 | 16 | use super::WasmPointer; 17 | 18 | /// load a bpf object from memory into the kernel 19 | pub fn wasm_load_bpf_object( 20 | mut caller: CallerType, 21 | obj_buf: WasmPointer, 22 | obj_buf_size: u32, 23 | ) -> u64 { 24 | debug!("Load bpf object caller"); 25 | let memory = caller.get_memory().expect("Expected exported `memory`"); 26 | let mut buf = vec![0u8]; 27 | if let Err(err) = memory.read( 28 | &mut caller, 29 | obj_buf as usize + obj_buf_size as usize - 1, 30 | &mut buf[..], 31 | ) { 32 | debug!( 33 | "Invalid pointer passed from wasm guest {}, size={}, err={}", 34 | obj_buf, obj_buf_size, err 35 | ); 36 | return 0; 37 | } 38 | let open_object = match ObjectBuilder::default().open_memory( 39 | "", 40 | &memory.data(&mut caller)[obj_buf as usize..(obj_buf + obj_buf_size) as usize], 41 | ) { 42 | Ok(v) => v, 43 | Err(err) => { 44 | debug!("Failed to open bpf object: {}", err); 45 | return 0; 46 | } 47 | }; 48 | let object = match open_object.load() { 49 | Ok(v) => v, 50 | Err(err) => { 51 | debug!("Failed to load bpf object: {}", err); 52 | return 0; 53 | } 54 | }; 55 | let mut state = caller.data_mut(); 56 | let next_id = state.next_object_id; 57 | state.next_object_id += 1; 58 | state.object_map.insert( 59 | next_id, 60 | WrapperObject { 61 | object: Rc::new(RefCell::new(object)), 62 | poll_buffer: None, 63 | }, 64 | ); 65 | debug!("Load bpf object done, id={}", next_id); 66 | next_id 67 | } 68 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/src/bpf/mod.rs: -------------------------------------------------------------------------------- 1 | //! SPDX-License-Identifier: MIT 2 | //! 3 | //! Copyright (c) 2023, eunomia-bpf 4 | //! All rights reserved. 5 | //! 6 | pub(crate) const EINVAL: i32 = 22; 7 | pub(crate) const ENOENT: i32 = 2; 8 | 9 | pub(crate) mod attach; 10 | pub(crate) mod close; 11 | pub(crate) mod fd_by_name; 12 | pub(crate) mod load; 13 | pub(crate) mod map_operate; 14 | pub(crate) mod poll; 15 | pub(crate) mod wrapper_poll; 16 | 17 | #[macro_export] 18 | macro_rules! ensure_program_mut_by_state { 19 | ($state: expr, $program: expr) => { 20 | match $state.object_map.get_mut(&$program) { 21 | Some(v) => v, 22 | None => { 23 | log::debug!("Invalid program: {}", $program); 24 | return -1; 25 | } 26 | } 27 | }; 28 | } 29 | 30 | #[macro_export] 31 | macro_rules! ensure_program_by_state { 32 | ($state: expr, $program: expr) => { 33 | match $state.object_map.get(&$program) { 34 | Some(v) => v, 35 | None => { 36 | log::debug!("Invalid program: {}", $program); 37 | return -1; 38 | } 39 | } 40 | }; 41 | } 42 | 43 | #[macro_export] 44 | macro_rules! ensure_program_mut_by_caller { 45 | ($caller: expr, $program: expr) => {{ 46 | use $crate::ensure_program_mut_by_state; 47 | ensure_program_mut_by_state!($caller.data_mut(), $program) 48 | }}; 49 | } 50 | 51 | #[macro_export] 52 | macro_rules! ensure_program_by_caller { 53 | ($caller: expr, $program: expr) => {{ 54 | use $crate::ensure_program_by_state; 55 | ensure_program_by_state!($caller.data_mut(), $program) 56 | }}; 57 | } 58 | 59 | #[macro_export] 60 | macro_rules! ensure_c_str { 61 | ($caller: expr, $var_name: expr) => {{ 62 | use $crate::utils::CallerUtils; 63 | match $caller.read_zero_terminated_str($var_name as usize) { 64 | Ok(v) => v.to_string(), 65 | Err(err) => { 66 | log::debug!("Failed to read `{}`: {}", stringify!($var_name), err); 67 | return -1; 68 | } 69 | } 70 | }}; 71 | } 72 | /// The pointer type in 32bit wasm 73 | pub type WasmPointer = u32; 74 | /// The handle to a bpf object 75 | pub type BpfObjectType = u64; 76 | /// The string type in wasm, is also a pointer 77 | pub type WasmString = u32; 78 | 79 | #[macro_export] 80 | macro_rules! ensure_enough_memory { 81 | ($caller: expr, $pointer:expr, $size: expr, $return_val: expr) => {{ 82 | use $crate::utils::CallerUtils; 83 | let mut buf = vec![0u8]; 84 | match $caller 85 | .get_memory() 86 | .expect("Expected exported memory!") 87 | .read( 88 | &mut $caller, 89 | $pointer as usize + $size as usize - 1, 90 | &mut buf, 91 | ) { 92 | Ok(_) => {} 93 | Err(err) => { 94 | debug!("Invalid pointer for {}: {}", stringify!($pointer), err); 95 | return $return_val; 96 | } 97 | } 98 | }}; 99 | } 100 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/src/bpf/wrapper_poll.rs: -------------------------------------------------------------------------------- 1 | //! SPDX-License-Identifier: MIT 2 | //! 3 | //! Copyright (c) 2023, eunomia-bpf 4 | //! All rights reserved. 5 | //! 6 | use log::error; 7 | 8 | use crate::{bpf::EINVAL, state::CallerType}; 9 | 10 | use super::{poll::wasm_bpf_buffer_poll, BpfObjectType, WasmPointer}; 11 | 12 | pub fn bpf_buffer_poll_wrapper( 13 | mut caller: CallerType, 14 | program: BpfObjectType, 15 | fd: i32, 16 | ctx: WasmPointer, 17 | data: WasmPointer, 18 | max_size: i32, 19 | timeout_ms: i32, 20 | ) -> i32 { 21 | let callback_func_name = caller.data().callback_func_name.clone(); 22 | caller.data_mut().wrapper_called = true; 23 | if let Some(export) = caller.get_export(&callback_func_name) { 24 | if export.into_func().is_none() { 25 | error!("Export {} is not func", callback_func_name); 26 | return EINVAL; 27 | } 28 | } else { 29 | error!("Callback export named {} not found", callback_func_name); 30 | return EINVAL; 31 | } 32 | wasm_bpf_buffer_poll(caller, program, fd, 0, ctx, data, max_size, timeout_ms) 33 | } 34 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/src/handle.rs: -------------------------------------------------------------------------------- 1 | use std::sync::mpsc; 2 | 3 | use anyhow::{anyhow, bail, Context}; 4 | use log::debug; 5 | use wasmtime::Engine; 6 | 7 | /// This is the signal that will be sended to the hanging epoch interruption callback function 8 | pub enum ProgramOperation { 9 | /// Resume the program 10 | Resume, 11 | /// Terminate the program 12 | Terminate, 13 | } 14 | 15 | /// This is a handle to the wasm program 16 | pub struct WasmProgramHandle { 17 | operation_tx: mpsc::Sender, 18 | paused: bool, 19 | engine: Engine, 20 | } 21 | 22 | impl WasmProgramHandle { 23 | pub(crate) fn new(operation_tx: mpsc::Sender, engine: Engine) -> Self { 24 | Self { 25 | operation_tx, 26 | engine, 27 | paused: false, 28 | } 29 | } 30 | /// Pause the wasm program 31 | /// Error will be returned when the program was already paused 32 | pub fn pause(&mut self) -> anyhow::Result<()> { 33 | if self.paused { 34 | bail!("Already paused!"); 35 | } 36 | self.engine.increment_epoch(); 37 | self.paused = true; 38 | Ok(()) 39 | } 40 | /// Resume the wasm program 41 | /// Error will be returned when the program was already running, or when the program as terminated 42 | pub fn resume(&mut self) -> anyhow::Result<()> { 43 | if !self.paused { 44 | bail!("Already running!"); 45 | } 46 | self.operation_tx 47 | .send(ProgramOperation::Resume) 48 | .with_context(|| anyhow!("Failed to send resume operation"))?; 49 | self.paused = false; 50 | Ok(()) 51 | } 52 | /// Terminate the wasm program 53 | /// Error will be returned when the program was already terminated 54 | pub fn terminate(self) -> anyhow::Result<()> { 55 | debug!("Terminating wasm program"); 56 | self.engine.increment_epoch(); 57 | self.operation_tx 58 | .send(ProgramOperation::Terminate) 59 | .with_context(|| anyhow!("Failed to send terminate operation"))?; 60 | Ok(()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/src/pipe.rs: -------------------------------------------------------------------------------- 1 | //! SPDX-License-Identifier: MIT 2 | //! 3 | //! Copyright (c) 2023, eunomia-bpf 4 | //! All rights reserved. 5 | //! 6 | use std::{ 7 | any::Any, 8 | io::{self, Cursor, Write}, 9 | sync::{Arc, RwLock}, 10 | }; 11 | 12 | use wasi_common::{ 13 | file::{FdFlags, FileType}, 14 | Error, ErrorExt, WasiFile, 15 | }; 16 | 17 | /// This is a pipe that can be read from and written to. 18 | /// The original wasmtime pipe is only writable when the wasm program is running. 19 | pub struct ReadableWritePipe { 20 | buf: Arc>, 21 | } 22 | 23 | #[wiggle::async_trait] 24 | impl WasiFile for ReadableWritePipe { 25 | fn as_any(&self) -> &dyn std::any::Any { 26 | self 27 | } 28 | async fn get_filetype(&mut self) -> Result { 29 | Ok(FileType::Pipe) 30 | } 31 | async fn get_fdflags(&mut self) -> Result { 32 | Ok(FdFlags::APPEND) 33 | } 34 | async fn write_vectored<'a>(&mut self, bufs: &[std::io::IoSlice<'a>]) -> Result { 35 | let n = self.borrow().write_vectored(bufs)?; 36 | Ok(n.try_into()?) 37 | } 38 | async fn writable(&self) -> Result<(), Error> { 39 | Ok(()) 40 | } 41 | async fn write_vectored_at<'a>( 42 | &mut self, 43 | _bufs: &[io::IoSlice<'a>], 44 | _offset: u64, 45 | ) -> Result { 46 | Err(Error::seek_pipe()) 47 | } 48 | async fn seek(&mut self, _pos: std::io::SeekFrom) -> Result { 49 | Err(Error::seek_pipe()) 50 | } 51 | fn isatty(&mut self) -> bool { 52 | false 53 | } 54 | } 55 | 56 | impl ReadableWritePipe { 57 | pub fn borrow(&self) -> std::sync::RwLockWriteGuard { 58 | RwLock::write(&self.buf).unwrap() 59 | } 60 | pub fn get_read_lock(&self) -> std::sync::RwLockReadGuard { 61 | self.buf.read().unwrap() 62 | } 63 | pub fn new(inner: W) -> Self { 64 | Self { 65 | buf: Arc::new(RwLock::new(inner)), 66 | } 67 | } 68 | } 69 | 70 | impl ReadableWritePipe>> { 71 | pub fn new_vec_buf() -> Self { 72 | Self::new(Cursor::new(vec![])) 73 | } 74 | } 75 | 76 | impl Clone for ReadableWritePipe { 77 | fn clone(&self) -> Self { 78 | Self { 79 | buf: self.buf.clone(), 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/abnormal_exit.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/abnormal_exit.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/bootstrap.bpf.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/bootstrap.bpf.o -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/bootstrap.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/bootstrap.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/custom_host_func.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/custom_host_func.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/custom_host_func/.gitignore: -------------------------------------------------------------------------------- 1 | custom_host_func.wasm 2 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/custom_host_func/Makefile: -------------------------------------------------------------------------------- 1 | WASI_CLANG = /opt/wasi-sdk/bin/clang 2 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 3 | 4 | custom_host_func.wasm: custom_host_func.c 5 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 6 | cp custom_host_func.wasm .. 7 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/custom_host_func/README.md: -------------------------------------------------------------------------------- 1 | # Test program for custom host func 2 | 3 | It invokes a host function to calculate `a+b`, and verifies the result. 4 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/custom_host_func/custom_host_func.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | __attribute__((import_module("host_func_test"), import_name("plus_i32"))) 4 | int32_t 5 | plus_i32(int32_t a, int32_t b); 6 | int main() { 7 | int32_t c = plus_i32(0xABCD, 0x1234); 8 | assert(c == 0xABCD + 0x1234); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/execve.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/execve.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/exit_code/.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/exit_code/Makefile: -------------------------------------------------------------------------------- 1 | WASI_CLANG = /opt/wasi-sdk/bin/clang 2 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 3 | 4 | DEL = rm -rf 5 | 6 | FILES = abnormal_exit normal_exit long_sleep 7 | 8 | all: $(FILES) 9 | 10 | $(FILES) : % : %.c 11 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@.wasm $< 12 | cp $@.wasm .. 13 | 14 | clean: 15 | $(DEL) *.wasm 16 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/exit_code/README.md: -------------------------------------------------------------------------------- 1 | There several wasm programs that will be used to test the handle of exit codes. 2 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/exit_code/abnormal_exit.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 1; 3 | } 4 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/exit_code/long_sleep.c: -------------------------------------------------------------------------------- 1 | #include 2 | int main() { 3 | // Sleep 10 seconds to wait for the interruption.. 4 | usleep(10 * 1000 * 1000); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/exit_code/normal_exit.c: -------------------------------------------------------------------------------- 1 | int main() { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/go-execve.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/go-execve.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_callback.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/interruption_in_callback.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_callback/.gitignore: -------------------------------------------------------------------------------- 1 | interruption_in_callback.wasm 2 | *.data.h 3 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_callback/Makefile: -------------------------------------------------------------------------------- 1 | WASI_CLANG = /opt/wasi-sdk/bin/clang 2 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 3 | 4 | DEL = rm -rf 5 | 6 | 7 | interruption_in_callback.wasm: interruption_in_callback.c bootstrap.data.h 8 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 9 | cp $@ .. 10 | 11 | bootstrap: bootstrap.bpf.o 12 | $(DEL) $@.data.h 13 | touch $@.data.h 14 | echo "#ifndef _$@_H" >> $@.data.h 15 | echo "#define _$@_H" >> $@.data.h 16 | echo "const char* $@_data = \"$$(cat $< | base64 -w 0)\";" >> $@.data.h 17 | echo "#endif" >> $@.data.h 18 | 19 | bootstrap.data.h: bootstrap 20 | 21 | 22 | clean: 23 | $(DEL) bootstrap.data.h 24 | $(DEL) *.wasm 25 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_callback/base64decode.h: -------------------------------------------------------------------------------- 1 | #ifndef _BASE64DECODE_H 2 | #define _BASE64DECODE_H 3 | 4 | // Copy from 5 | // https://stackoverflow.com/questions/342409/how-do-i-base64-encode-decode-in-c 6 | 7 | #include 8 | #include 9 | 10 | static char encoding_table[] = { 11 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 12 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 13 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 14 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 15 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; 16 | static char decoding_table[256] = { 17 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19 | 0, 0, 0, 0, 0, 62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 20 | 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 21 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 22 | 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 23 | 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30 | 0, 0, 0, 0, 0, 0, 0, 0, 0}; 31 | static int mod_table[] = {0, 2, 1}; 32 | 33 | unsigned char* base64_decode(const char* data, 34 | size_t input_length, 35 | size_t* output_length) { 36 | if (input_length % 4 != 0) 37 | return NULL; 38 | 39 | *output_length = input_length / 4 * 3; 40 | if (data[input_length - 1] == '=') 41 | (*output_length)--; 42 | if (data[input_length - 2] == '=') 43 | (*output_length)--; 44 | 45 | unsigned char* decoded_data = (unsigned char*)malloc(*output_length); 46 | if (decoded_data == NULL) 47 | return NULL; 48 | 49 | for (int i = 0, j = 0; i < input_length;) { 50 | uint32_t sextet_a = 51 | data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; 52 | uint32_t sextet_b = 53 | data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; 54 | uint32_t sextet_c = 55 | data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; 56 | uint32_t sextet_d = 57 | data[i] == '=' ? 0 & i++ : decoding_table[data[i++]]; 58 | 59 | uint32_t triple = (sextet_a << 3 * 6) + (sextet_b << 2 * 6) + 60 | (sextet_c << 1 * 6) + (sextet_d << 0 * 6); 61 | 62 | if (j < *output_length) 63 | decoded_data[j++] = (triple >> 2 * 8) & 0xFF; 64 | if (j < *output_length) 65 | decoded_data[j++] = (triple >> 1 * 8) & 0xFF; 66 | if (j < *output_length) 67 | decoded_data[j++] = (triple >> 0 * 8) & 0xFF; 68 | } 69 | 70 | return decoded_data; 71 | } 72 | 73 | #endif -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_callback/bootstrap.bpf.o: -------------------------------------------------------------------------------- 1 | ../bootstrap.bpf.o -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_callback/interruption_in_callback.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "libbpf-wasm.h" 3 | #include "bootstrap.data.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "base64decode.h" 10 | #include 11 | static int handle_event(void* ctx, void* data, size_t data_sz) { 12 | puts("Sleep started.."); 13 | // Just sleep for 5 seconds 14 | usleep(5 * 1000000); 15 | puts("Sleep done"); 16 | return 0; 17 | } 18 | int main(void) { 19 | size_t len = strlen(bootstrap_data); 20 | size_t out_len; 21 | const char* buf = (const char*)base64_decode(bootstrap_data, len, &out_len); 22 | uint64_t handle = wasm_load_bpf_object(buf, out_len); 23 | // This is only a test program, so just assert 24 | assert(handle != 0); 25 | int attach_result; 26 | attach_result = wasm_attach_bpf_program(handle, "handle_exec", ""); 27 | assert(attach_result == 0); 28 | attach_result = wasm_attach_bpf_program(handle, "handle_exit", ""); 29 | assert(attach_result == 0); 30 | int map_fd = wasm_bpf_map_fd_by_name(handle, "rb"); 31 | assert(map_fd > 0); 32 | char buffer[256]; 33 | // Infinite poll 34 | while (true) { 35 | wasm_bpf_buffer_poll(handle, map_fd, (int32_t)(&handle_event), (uint32_t)NULL, buffer, 36 | sizeof(buffer), 100); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_callback/libbpf-wasm.h: -------------------------------------------------------------------------------- 1 | ../../../../wasm-sdk/c/libbpf-wasm.h -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_hostfunc.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/interruption_in_hostfunc.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_hostfunc/.gitignore: -------------------------------------------------------------------------------- 1 | *.wasm 2 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_hostfunc/Makefile: -------------------------------------------------------------------------------- 1 | WASI_CLANG = /opt/wasi-sdk/bin/clang 2 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 3 | 4 | interruption_in_hostfunc.wasm: interruption_in_hostfunc.c 5 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 6 | cp $@ .. 7 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_hostfunc/README.md: -------------------------------------------------------------------------------- 1 | # Test program for interruption in host function 2 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/interruption_in_hostfunc/interruption_in_hostfunc.c: -------------------------------------------------------------------------------- 1 | #include 2 | __attribute__((import_module("test"), import_name("long_sleep"))) void 3 | long_sleep(void); 4 | int main() { 5 | puts("Call to long sleep.."); 6 | long_sleep(); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/long_sleep.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/long_sleep.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/lsm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/lsm.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/normal_exit.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/normal_exit.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/opensnoop.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/opensnoop.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/runqlat.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/runqlat.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/rust-bootstrap.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/rust-bootstrap.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/sockfilter.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/sockfilter.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/sockops.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/sockops.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/tick.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eunomia-bpf/wasm-bpf/c9c6012d1d2c2d66d928a8e1134af12450126a13/runtime/wasm-bpf-rs/tests/tick.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/tick/.gitignore: -------------------------------------------------------------------------------- 1 | tick.wasm -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/tick/Makefile: -------------------------------------------------------------------------------- 1 | WASI_CLANG = /opt/wasi-sdk/bin/clang 2 | WASI_CFLAGS = -O2 --sysroot=/opt/wasi-sdk/share/wasi-sysroot -Wl,--allow-undefined,--export-table 3 | 4 | tick.wasm: tick.c 5 | $(WASI_CLANG) $(WASI_CFLAGS) -o $@ $< 6 | cp tick.wasm .. -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/tick/README.md: -------------------------------------------------------------------------------- 1 | # Test program for pause & resume 2 | 3 | This wasm program will keep printing `Tick!\n` one time per second. 4 | -------------------------------------------------------------------------------- /runtime/wasm-bpf-rs/tests/tick/tick.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | int main() { 5 | while (1) { 6 | char buf[] = "Tick!\n"; 7 | int ret = fwrite(buf, 1, sizeof(buf), stdout); 8 | fflush(stdout); 9 | // Write debug information to stderr 10 | fprintf(stderr, "ret=%d\n", ret); 11 | usleep(1000 * 1000); 12 | } 13 | // Although it will never return.. 14 | return 0; 15 | } -------------------------------------------------------------------------------- /wasm-sdk/README.md: -------------------------------------------------------------------------------- 1 | # SDK 2 | 3 | This directory contains SDKs that can be used by eBPF user-space programs. 4 | 5 | - [c](./c): A C Header contains the prototype of host functions provided by the runtime, and some wrapper functions around them. 6 | - [rust](./rust) A rust crate `wasm-bpf-binding` that contains the prototype of several host-provided functions. 7 | 8 | Don't worry you are using other languages, you can just add these function imports (in the format your language used) to use functions provided by the runtime: 9 | 10 | ```c 11 | /// lookup a bpf map fd by name. 12 | i32 wasm_bpf_map_fd_by_name(u64 obj, u32 name); 13 | /// detach and close a bpf program. 14 | i32 wasm_close_bpf_object(u64 obj); 15 | /// CO-RE load a bpf object into the kernel. 16 | u64 wasm_load_bpf_object(u32 obj_buf, u32 obj_buf_sz); 17 | /// attach a bpf program to a kernel hook. 18 | i32 wasm_attach_bpf_program(u64 obj, u32 name, 19 | u32 attach_target); 20 | /// poll a bpf buffer, and call a wasm callback indicated by sample_func. 21 | /// the first time to call this function will open and create a bpf buffer. 22 | i32 wasm_bpf_buffer_poll(u64 program, i32 fd, u32 sample_func, 23 | u32 ctx, u32 data, i32 max_size, 24 | i32 timeout_ms); 25 | /// lookup, update, delete, and get_next_key operations on a bpf map. 26 | i32 wasm_bpf_map_operate(u64 fd, i32 cmd, u32 key, u32 value, 27 | u32 next_key, u64 flags); 28 | ``` 29 | 30 | - `iXX` denotes signed integer with `XX` bits 31 | - `uXX` denotes unsigned integer with `XX` bits 32 | 33 | --------------------------------------------------------------------------------