├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ ├── publish.yml │ └── stale.yml ├── .gitignore ├── .gitmodules ├── Cargo.toml ├── LICENSE ├── README.md ├── bindings.h ├── build.rs ├── rebuild.sh ├── src ├── bindings.rs └── lib.rs └── tests └── tests.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Please see the documentation for all configuration options: 2 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 3 | 4 | version: 2 5 | updates: 6 | - package-ecosystem: github-actions 7 | rebase-strategy: auto 8 | directory: / 9 | schedule: 10 | interval: daily 11 | - package-ecosystem: cargo 12 | versioning-strategy: auto 13 | directory: / 14 | schedule: 15 | interval: daily 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | push: 5 | workflow_call: 6 | 7 | jobs: 8 | test-gnu: 9 | # dynamically linked glibc 10 | name: Test on Ubuntu ${{ matrix.os-arch }} (${{ matrix.args }}) 11 | strategy: 12 | matrix: 13 | include: 14 | - rust-target: x86_64-unknown-linux-gnu 15 | os-target: x86_64-linux-gnu 16 | os-arch: amd64 17 | args: '' 18 | 19 | - rust-target: x86_64-unknown-linux-gnu 20 | os-target: x86_64-linux-gnu 21 | os-arch: amd64 22 | args: '-F static,vendored' 23 | 24 | - rust-target: x86_64-unknown-linux-gnu 25 | os-target: x86_64-linux-gnu 26 | os-arch: amd64 27 | args: --no-default-features 28 | install-sys-libbpf: y 29 | 30 | - rust-target: aarch64-unknown-linux-gnu 31 | os-target: aarch64-linux-gnu 32 | os-arch: arm64 33 | args: '' 34 | # Test cross-compilation to aarch64 35 | - rust-target: aarch64-unknown-linux-gnu 36 | os-target: aarch64-linux-gnu 37 | os-arch: arm64 38 | args: '-F static,vendored' 39 | # Test cross-compilation to riscv64 40 | - rust-target: riscv64gc-unknown-linux-gnu 41 | os-target: riscv64-linux-gnu 42 | os-arch: riscv64 43 | args: '-F static,vendored' 44 | runs-on: ubuntu-22.04 45 | env: 46 | CARGO_BUILD_TARGET: ${{ matrix.rust-target }} 47 | CARGO_TERM_VERBOSE: 'true' 48 | RUSTFLAGS: -C linker=/usr/bin/${{ matrix.os-target }}-gcc 49 | steps: 50 | - name: Checkout repository 51 | uses: actions/checkout@v4 52 | with: 53 | submodules: recursive 54 | 55 | - name: Add apt sources for ${{ matrix.os-arch }} 56 | if: matrix.os-arch != 'amd64' 57 | run: | 58 | dpkg --add-architecture ${{ matrix.os-arch }} 59 | 60 | release=$(. /etc/os-release && echo "$UBUNTU_CODENAME") 61 | sed -i 's/^deb /deb [arch=amd64] /' /etc/apt/sources.list 62 | printf 'deb [arch=${{ matrix.os-arch }}] http://ports.ubuntu.com/ %s main restricted\n' \ 63 | $release $release-updates $release-security \ 64 | >> /etc/apt/sources.list 65 | shell: sudo sh -e {0} 66 | 67 | - name: Install system dependencies 68 | run: | 69 | sudo apt-get update 70 | sudo apt-get install \ 71 | build-essential \ 72 | autopoint \ 73 | gettext \ 74 | libelf-dev:${{ matrix.os-arch }} \ 75 | zlib1g-dev:${{ matrix.os-arch }} 76 | 77 | - name: Install libbpf-dev 78 | if: matrix.install-sys-libbpf == 'y' 79 | run: sudo apt-get install libbpf-dev:${{ matrix.os-arch }} 80 | 81 | - name: Install linker for ${{ matrix.os-target }} 82 | if: matrix.os-arch != 'amd64' 83 | run: sudo apt-get install gcc-${{ matrix.os-target }} 84 | 85 | - name: Install Rust stable for ${{ matrix.rust-target }} 86 | uses: dtolnay/rust-toolchain@stable 87 | with: 88 | targets: ${{ matrix.rust-target }} 89 | 90 | - run: cargo build ${{ matrix.args }} 91 | 92 | - run: cargo test ${{ matrix.args }} 93 | if: matrix.os-arch == 'amd64' 94 | 95 | test-musl: 96 | # dynamically linked musl libc 97 | name: Test on Alpine Linux x86_64 (${{ matrix.args }}) 98 | runs-on: ubuntu-22.04 99 | strategy: 100 | matrix: 101 | include: 102 | - args: '' 103 | - args: --no-default-features 104 | install-sys-libbpf: y 105 | env: 106 | CARGO_TERM_VERBOSE: 'true' 107 | steps: 108 | - name: Checkout repository 109 | uses: actions/checkout@v4 110 | with: 111 | submodules: recursive 112 | 113 | - name: Install Alpine Linux with dependencies 114 | uses: jirutka/setup-alpine@v1 115 | with: 116 | branch: latest-stable 117 | packages: > 118 | build-base 119 | cargo 120 | elfutils-dev 121 | linux-headers 122 | zlib-dev 123 | 124 | - name: Install libbpf-dev 125 | if: matrix.install-sys-libbpf == 'y' 126 | run: apk add libbpf-dev 127 | shell: alpine.sh --root {0} 128 | 129 | - run: cargo build ${{ matrix.args }} 130 | shell: alpine.sh {0} 131 | 132 | - run: cargo test ${{ matrix.args }} 133 | shell: alpine.sh {0} 134 | 135 | test-libbpf-rs: 136 | # check that libbpf-rs, one of the main consumers of the library, works with 137 | # this version of libbpf-sys 138 | name: Test libbpf-rs integration 139 | runs-on: ubuntu-22.04 140 | env: 141 | CARGO_TERM_VERBOSE: 'true' 142 | steps: 143 | - name: Checkout repository 144 | uses: actions/checkout@v4 145 | with: 146 | submodules: recursive 147 | 148 | - name: Install system dependencies 149 | run: | 150 | sudo apt-get update 151 | sudo apt-get install \ 152 | build-essential \ 153 | libelf-dev \ 154 | zlib1g-dev 155 | 156 | - name: Build libbpf-rs with libbpf-sys 157 | run: | 158 | dir=$(pwd) 159 | cd /tmp/ 160 | cargo init --bin libbpf-rs-test-project 161 | cd libbpf-rs-test-project 162 | # Add libbpf-rs dependency and override libbpf-sys in use with our 163 | # current one. 164 | cat >> Cargo.toml </# 21 | # but could also be along the lines of 22 | # file:///#@ 23 | version="$(echo ${pkgid} | cut -d '#' -f2 | cut -d '@' -f2 | grep -o '[^:]*$')" 24 | if [ -z "${version}" ]; then 25 | echo "Invalid version string: ${pkgid}" 26 | exit 1 27 | fi 28 | echo "Determined crate version: ${version}" 29 | echo "version=${version}" >> $GITHUB_OUTPUT 30 | test: 31 | uses: ./.github/workflows/ci.yml 32 | secrets: inherit 33 | publish: 34 | needs: [test, version] 35 | runs-on: ubuntu-latest 36 | steps: 37 | - uses: actions/checkout@v4 38 | with: 39 | submodules: recursive 40 | - uses: dtolnay/rust-toolchain@stable 41 | - name: Dry-run package creation 42 | run: cargo package --no-verify 43 | - name: Create git tag 44 | env: 45 | version: ${{ needs.version.outputs.version }} 46 | run: | 47 | curl --location \ 48 | --fail-with-body \ 49 | --request POST \ 50 | --url https://api.github.com/repos/${{ github.repository }}/releases \ 51 | --header "Accept: application/vnd.github+json" \ 52 | --header "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ 53 | --header "X-GitHub-Api-Version: 2022-11-28" \ 54 | --data "{ 55 | \"tag_name\":\"v${version}\", 56 | \"target_commitish\":\"${{ github.ref }}\", 57 | \"name\":\"v${version}\", 58 | \"draft\":false, 59 | \"prerelease\":false, 60 | \"generate_release_notes\":false 61 | }" 62 | - name: Publish 63 | run: cargo publish --no-verify --token "${CARGO_REGISTRY_TOKEN}" 64 | env: 65 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} 66 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close stale PRs 2 | on: 3 | schedule: 4 | - cron: '30 1 * * *' 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9 11 | with: 12 | # Disable staleness logic for issues. 13 | days-before-stale: -1 14 | days-before-close: -1 15 | # Explicitly enable staleness logic for pull requests. 16 | days-before-pr-stale: 30 17 | days-before-pr-close: 5 18 | stale-pr-message: 'This pull request is considered stale because it has been open 30 days with no activity. Remove stale label or comment or it will be closed in 5 days.' 19 | close-pr-message: 'Closing pull request as it is stale.' 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | .idea/ 5 | 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/libbpf"] 2 | path = libbpf 3 | url = https://github.com/libbpf/libbpf.git 4 | [submodule "elfutils"] 5 | path = elfutils 6 | url = https://github.com/libbpf/elfutils-mirror.git 7 | [submodule "zlib"] 8 | path = zlib 9 | url = https://github.com/madler/zlib.git 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libbpf-sys" 3 | version = "1.5.1+v1.5.1" 4 | description = "Rust bindings to libbpf from the Linux kernel" 5 | readme = "README.md" 6 | repository = "https://github.com/libbpf/libbpf-sys" 7 | homepage = "https://github.com/libbpf/libbpf-sys" 8 | documentation = "https://docs.rs/libbpf-sys" 9 | keywords = ["bpf", "ebpf", "xdp"] 10 | authors = [ 11 | "Alex Forster ", 12 | "Dan Siemon ", 13 | "Daniel Xu ", 14 | ] 15 | license = "BSD-2-Clause" 16 | edition = "2018" 17 | build = "build.rs" 18 | links = "bpf" 19 | exclude = [ 20 | "/elfutils/tests/*.bz2", 21 | "/libbpf/assets", 22 | "/zlib/contrib", 23 | ] 24 | 25 | [badges] 26 | github = { repository = "libbpf/libbpf-sys" } 27 | maintenance = { status = "passively-maintained" } 28 | 29 | [build-dependencies] 30 | bindgen = { version = "^0.71.1", optional = true } 31 | cc = "^1.1.6" 32 | pkg-config = "^0.3.30" 33 | nix = { version = "^0.30.0", default-features = false, features = ["fs"] } 34 | 35 | [lib] 36 | crate-type = ["lib", "staticlib"] 37 | 38 | [features] 39 | default = ["vendored-libbpf"] 40 | # Don't vendor anything. 41 | # This feature is for backward-compatibility only. 42 | # Set default-features = false instead. 43 | novendor = [] 44 | # Meta-feature to use vendored versions of all dependencies. 45 | vendored = ["vendored-libbpf", "vendored-libelf", "vendored-zlib"] 46 | # Use vendored `libbpf`. Implies linking it statically. 47 | vendored-libbpf = ["static-libbpf"] 48 | # Use vendored `libelf`. Implies linking it statically. 49 | vendored-libelf = ["static-libelf"] 50 | # Use vendored `zlib`. Implies linking it statically. 51 | vendored-zlib = ["static-zlib"] 52 | # Meta-feature to link against all dependencies statically. 53 | static = ["static-libbpf", "static-libelf", "static-zlib"] 54 | # Link libbpf statically. 55 | static-libbpf = [] 56 | # Link libelf statically. Implies linking libbpf statically, because libbpf is 57 | # the libelf consumer. 58 | static-libelf = ["static-libbpf"] 59 | # Link zlib statically. Implies linking libbpf statically, because libbpf is 60 | # the zlib consumer. 61 | static-zlib = ["static-libbpf"] 62 | # Generate bindings into source directory, should only be used for local 63 | # binding source updating. User should use "bindgen" feature flag instead. 64 | bindgen-source = ["bindgen"] 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2019 Alex Forster 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libbpf-sys [![Build status](https://github.com/libbpf/libbpf-sys/workflows/CI/badge.svg)](https://github.com/libbpf/libbpf-sys/actions?query=workflow%3A%22CI%22) [![crates.io version number badge](https://img.shields.io/crates/v/libbpf-sys.svg)](https://crates.io/crates/libbpf-sys) 2 | 3 | **Rust bindings to _libbpf_ from the Linux kernel** 4 | 5 | **Maintainer:** Alex Forster \
6 | **License:** `BSD-2-Clause` 7 | 8 | _libbpf-sys_ is the packaged result of using _bindgen_ to automatically generate Rust FFI bindings to _libbpf_ from the Linux kernel. 9 | 10 | **Warning:** this crate does not provide a high-level or "safe" API wrapper around _libbpf_. If you are looking for an easier way to use _libbpf_, check out these other crates that implement higher-level APIs using _libbpf-sys_... 11 | 12 | * **afxdp:** a Rust interface for AF_XDP – [GitHub](https://github.com/aterlo/afxdp-rs) | [Crates.io](https://crates.io/crates/afxdp) 13 | * **libbpf-cargo:** Cargo plugin to build bpf programs – [GitHub](https://github.com/libbpf/libbpf-rs) | [Crates.io](https://crates.io/crates/libbpf-cargo) 14 | * **libbpf-rs:** a safe, idiomatic, and opinionated wrapper around libbpf-sys – [GitHub](https://github.com/libbpf/libbpf-rs) | [Crates.io](https://crates.io/crates/libbpf-rs) 15 | * **rebpf:** write and load eBPF programs in Rust – [GitHub](https://github.com/uccidibuti/rebpf) | [Crates.io](https://crates.io/crates/rebpf) 16 | * **xsk-rs:** a Rust interface for Linux AF_XDP sockets – [Github](https://github.com/DouglasGray/xsk-rs) | [Crates.io](https://crates.io/crates/xsk-rs) 17 | 18 | The community is encouraged to build higher-level crates using _libbpf-sys_. Please let me know if you do! 19 | 20 | ### Building 21 | 22 | As part of the `cargo build` process, an included copy of _libbpf_ is compiled and statically linked into the resulting binary. This means that, in order to build a project that depends on this crate, your system must provide a working C compiler toolchain (GCC and Clang should both work). Additionally, your system must provide development headers for _zlib_ and _libelf_, and they must be discoverable via _pkgconfig_. 23 | 24 | Building on a fresh Debian/Ubuntu installation: 25 | 26 | ```sh 27 | $ apt-get install git rustc cargo build-essential pkgconf zlib1g-dev libelf-dev 28 | $ git clone --recurse-submodules https://github.com/libbpf/libbpf-sys.git && cd libbpf-sys 29 | $ cargo build 30 | ``` 31 | 32 | Building on a fresh RHEL/Fedora installation: 33 | 34 | ```sh 35 | $ yum install git rust cargo gcc make pkgconf zlib-devel elfutils-libelf-devel 36 | $ git clone --recurse-submodules https://github.com/libbpf/libbpf-sys.git && cd libbpf-sys 37 | $ cargo build 38 | ``` 39 | 40 | #### Environment Variables 41 | 42 | - `LIBBPF_SYS_EXTRA_CFLAGS` can be used to pass extra cflags when vendoring libbpf, libz or libelf. 43 | - `LIBBPF_SYS_LIBRARY_PATH`: colon separated paths for the linker to find native libs. 44 | 45 | ### Distribution 46 | 47 | When you add this crate as a dependency to your project, your resulting binaries will dynamically link with `libz` and `libelf`. This means that the systems where you run your binaries must have these libraries installed. 48 | 49 | ### Versioning 50 | 51 | Because the API of this crate is automatically generated from _libbpf_ sources, it uses a versioning scheme based on the version of _libbpf_ that it provides. 52 | 53 | The "Major.Minor" semver numbers correspond exactly to the _libbpf_ version that each release provides. For example, the `0.6.x` releases of this crate provides the API for the _libbpf v0.6.x_ releases. 54 | 55 | In order to allow for human error, the "Patch" semver number is used by this crate and does not necessarily match the provided _libbpf_ version. For example, both the `0.6.1` and `0.6.2` releases of this crate contain bindings to _libbpf v0.6.1_, but the later release contains bugfixes and/or enhancements to the crate itself. 56 | 57 | The exact version of _libbpf_ that is provided by any given release can be found in the "Build Metadata" semver section, which comes after the `+` in the version string. For example, `0.6.2+v0.6.1` indicates that the crate version is `0.6.2` and the upstream _libbpf_ version is `v0.6.1`. 58 | 59 | ### Licensing and Dependencies 60 | 61 | This crate is released under the BSD 2-Clause license, and is careful to avoid infecting users with viral licenses. 62 | 63 | It currently depends on the following third-party libraries: 64 | 65 | | | Website | License | Linkage | 66 | |------------|---------------------------------------------------------------|------------------------------------------|---------| 67 | | **libbpf** | [github.com/libbpf/libbpf](https://github.com/libbpf/libbpf/) | `LGPL-2.1-only OR BSD-2-Clause` | Static | 68 | | **libelf** | [sourceware.org/elfutils](https://sourceware.org/elfutils/) | `LGPL-2.1-or-later OR LGPL-3.0-or-later` | Dynamic | 69 | | **zlib** | [zlib.net](https://www.zlib.net/) | `Zlib` | Dynamic | 70 | -------------------------------------------------------------------------------- /bindings.h: -------------------------------------------------------------------------------- 1 | #ifdef __LIBBPF_SYS_NOVENDOR 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #else 8 | #include "libbpf/include/uapi/linux/if_link.h" 9 | #include "libbpf/include/uapi/linux/perf_event.h" 10 | #include "libbpf/src/bpf.h" 11 | #include "libbpf/src/btf.h" 12 | #include "libbpf/src/libbpf.h" 13 | #endif /* __LIBBPF_SYS_NOVENDOR */ 14 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // build.rs 2 | 3 | use std::env; 4 | use std::ffi; 5 | use std::fs; 6 | use std::fs::read_dir; 7 | use std::fs::File; 8 | use std::io; 9 | use std::path; 10 | use std::path::Path; 11 | use std::process; 12 | 13 | use nix::fcntl; 14 | 15 | fn emit_rerun_directives_for_contents(dir: &Path) { 16 | for result in read_dir(dir).unwrap() { 17 | let file = result.unwrap(); 18 | println!("cargo:rerun-if-changed={}", file.path().display()); 19 | } 20 | } 21 | 22 | #[cfg(feature = "bindgen")] 23 | fn generate_bindings(src_dir: path::PathBuf) { 24 | use std::collections::HashSet; 25 | 26 | #[derive(Debug)] 27 | struct IgnoreMacros(HashSet<&'static str>); 28 | 29 | impl bindgen::callbacks::ParseCallbacks for IgnoreMacros { 30 | fn will_parse_macro(&self, name: &str) -> bindgen::callbacks::MacroParsingBehavior { 31 | if self.0.contains(name) { 32 | bindgen::callbacks::MacroParsingBehavior::Ignore 33 | } else { 34 | bindgen::callbacks::MacroParsingBehavior::Default 35 | } 36 | } 37 | } 38 | 39 | let ignored_macros = IgnoreMacros( 40 | vec![ 41 | "BTF_KIND_FUNC", 42 | "BTF_KIND_FUNC_PROTO", 43 | "BTF_KIND_VAR", 44 | "BTF_KIND_DATASEC", 45 | "BTF_KIND_FLOAT", 46 | "BTF_KIND_DECL_TAG", 47 | "BTF_KIND_TYPE_TAG", 48 | "BTF_KIND_ENUM64", 49 | ] 50 | .into_iter() 51 | .collect(), 52 | ); 53 | 54 | #[cfg(feature = "bindgen-source")] 55 | let out_dir = &src_dir.join("src"); 56 | #[cfg(not(feature = "bindgen-source"))] 57 | let out_dir = 58 | &path::PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR should always be set")); 59 | 60 | bindgen::Builder::default() 61 | .derive_default(true) 62 | .explicit_padding(true) 63 | .default_enum_style(bindgen::EnumVariation::Consts) 64 | .size_t_is_usize(false) 65 | .prepend_enum_name(false) 66 | .layout_tests(false) 67 | .generate_comments(false) 68 | .emit_builtins() 69 | .allowlist_function("bpf_.+") 70 | .allowlist_function("btf_.+") 71 | .allowlist_function("libbpf_.+") 72 | .allowlist_function("perf_.+") 73 | .allowlist_function("ring_buffer_.+") 74 | .allowlist_function("user_ring_buffer_.+") 75 | .allowlist_function("vdprintf") 76 | .allowlist_type("bpf_.+") 77 | .allowlist_type("btf_.+") 78 | .allowlist_type("xdp_.+") 79 | .allowlist_type("perf_.+") 80 | .allowlist_var("BPF_.+") 81 | .allowlist_var("BTF_.+") 82 | .allowlist_var("XDP_.+") 83 | .allowlist_var("PERF_.+") 84 | .parse_callbacks(Box::new(ignored_macros)) 85 | .header("bindings.h") 86 | .clang_arg(format!("-I{}", src_dir.join("libbpf/include").display())) 87 | .clang_arg(format!( 88 | "-I{}", 89 | src_dir.join("libbpf/include/uapi").display() 90 | )) 91 | .generate() 92 | .expect("Unable to generate bindings") 93 | .write_to_file(out_dir.join("bindings.rs")) 94 | .expect("Couldn't write bindings"); 95 | } 96 | 97 | #[cfg(not(feature = "bindgen"))] 98 | fn generate_bindings(_: path::PathBuf) {} 99 | 100 | fn pkg_check(pkg: &str) { 101 | if process::Command::new(pkg) 102 | .stdout(process::Stdio::null()) 103 | .stderr(process::Stdio::null()) 104 | .status() 105 | .is_err() 106 | { 107 | panic!( 108 | "{} is required to compile libbpf-sys with the selected set of features", 109 | pkg 110 | ); 111 | } 112 | } 113 | 114 | fn main() { 115 | let src_dir = path::PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()); 116 | 117 | generate_bindings(src_dir.clone()); 118 | 119 | let vendored_libbpf = cfg!(feature = "vendored-libbpf"); 120 | let vendored_libelf = cfg!(feature = "vendored-libelf"); 121 | let vendored_zlib = cfg!(feature = "vendored-zlib"); 122 | println!("Using feature vendored-libbpf={}", vendored_libbpf); 123 | println!("Using feature vendored-libelf={}", vendored_libelf); 124 | println!("Using feature vendored-zlib={}", vendored_zlib); 125 | 126 | let static_libbpf = cfg!(feature = "static-libbpf"); 127 | let static_libelf = cfg!(feature = "static-libelf"); 128 | let static_zlib = cfg!(feature = "static-zlib"); 129 | println!("Using feature static-libbpf={}", static_libbpf); 130 | println!("Using feature static-libelf={}", static_libelf); 131 | println!("Using feature static-zlib={}", static_zlib); 132 | 133 | if cfg!(feature = "novendor") { 134 | println!("cargo:warning=the `novendor` feature of `libbpf-sys` is deprecated; build without features instead"); 135 | println!( 136 | "cargo:rustc-link-lib={}bpf", 137 | if static_libbpf { "static=" } else { "" } 138 | ); 139 | return; 140 | } 141 | 142 | let out_dir = path::PathBuf::from(env::var_os("OUT_DIR").unwrap()); 143 | 144 | // check for all necessary compilation tools 145 | if vendored_libelf { 146 | pkg_check("autoreconf"); 147 | pkg_check("autopoint"); 148 | pkg_check("flex"); 149 | pkg_check("bison"); 150 | pkg_check("gawk"); 151 | } 152 | 153 | let (compiler, mut cflags) = if vendored_libbpf || vendored_libelf || vendored_zlib { 154 | pkg_check("make"); 155 | pkg_check("pkg-config"); 156 | 157 | let compiler = cc::Build::new().try_get_compiler().expect( 158 | "a C compiler is required to compile libbpf-sys using the vendored copy of libbpf", 159 | ); 160 | let mut cflags = compiler.cflags_env(); 161 | println!("cargo:rerun-if-env-changed=LIBBPF_SYS_EXTRA_CFLAGS"); 162 | if let Some(extra_cflags) = env::var_os("LIBBPF_SYS_EXTRA_CFLAGS") { 163 | cflags.push(" "); 164 | cflags.push(extra_cflags); 165 | } 166 | (Some(compiler), cflags) 167 | } else { 168 | (None, ffi::OsString::new()) 169 | }; 170 | 171 | if vendored_zlib { 172 | make_zlib(compiler.as_ref().unwrap(), &src_dir, &out_dir); 173 | cflags.push(format!(" -I{}/zlib/", src_dir.display())); 174 | } 175 | 176 | if vendored_libelf { 177 | make_elfutils(compiler.as_ref().unwrap(), &src_dir, &out_dir); 178 | cflags.push(format!(" -I{}/elfutils/libelf/", src_dir.display())); 179 | } 180 | 181 | if vendored_libbpf { 182 | make_libbpf(compiler.as_ref().unwrap(), &cflags, &src_dir, &out_dir); 183 | } 184 | 185 | println!( 186 | "cargo:rustc-link-search=native={}", 187 | out_dir.to_string_lossy() 188 | ); 189 | println!( 190 | "cargo:rustc-link-lib={}elf", 191 | if static_libelf { "static=" } else { "" } 192 | ); 193 | println!( 194 | "cargo:rustc-link-lib={}z", 195 | if static_zlib { "static=" } else { "" } 196 | ); 197 | println!( 198 | "cargo:rustc-link-lib={}bpf", 199 | if static_libbpf { "static=" } else { "" } 200 | ); 201 | println!("cargo:include={}/include", out_dir.to_string_lossy()); 202 | 203 | println!("cargo:rerun-if-env-changed=LIBBPF_SYS_LIBRARY_PATH"); 204 | if let Ok(lib_path) = env::var("LIBBPF_SYS_LIBRARY_PATH") { 205 | for path in lib_path.split(':') { 206 | if !path.is_empty() { 207 | println!("cargo:rustc-link-search=native={}", path); 208 | } 209 | } 210 | } 211 | } 212 | 213 | fn open_lockable(path: &Path) -> io::Result { 214 | let result = File::options() 215 | .read(true) 216 | // Open with write permissions because flock(2) may require them 217 | // on some platforms. 218 | .write(true) 219 | .open(path); 220 | match result { 221 | Ok(file) => Ok(file), 222 | Err(err) if err.kind() == io::ErrorKind::PermissionDenied => { 223 | // On a read-only file system we may not be able to open 224 | // with write permissions. So just open for reading and hope 225 | // for the best. 226 | File::open(path) 227 | }, 228 | e @ Err(..) => e, 229 | } 230 | } 231 | 232 | fn make_zlib(compiler: &cc::Tool, src_dir: &path::Path, out_dir: &path::Path) { 233 | let src_dir = src_dir.join("zlib"); 234 | // lock README such that if two crates are trying to compile 235 | // this at the same time (eg libbpf-rs libbpf-cargo) 236 | // they wont trample each other 237 | let file = open_lockable(&src_dir.join("README")).unwrap(); 238 | let _lock = fcntl::Flock::lock(file, fcntl::FlockArg::LockExclusive).unwrap(); 239 | 240 | let status = process::Command::new("./configure") 241 | .arg("--static") 242 | .arg("--prefix") 243 | .arg(".") 244 | .arg("--libdir") 245 | .arg(out_dir) 246 | .env("CC", compiler.path()) 247 | .env("CFLAGS", compiler.cflags_env()) 248 | .current_dir(&src_dir) 249 | .status() 250 | .expect("could not execute make"); 251 | 252 | assert!(status.success(), "make failed"); 253 | 254 | let status = process::Command::new("make") 255 | .arg("install") 256 | .arg("-j") 257 | .arg(format!("{}", num_cpus())) 258 | .current_dir(&src_dir) 259 | .status() 260 | .expect("could not execute make"); 261 | 262 | assert!(status.success(), "make failed"); 263 | 264 | let status = process::Command::new("make") 265 | .arg("distclean") 266 | .current_dir(&src_dir) 267 | .status() 268 | .expect("could not execute make"); 269 | 270 | assert!(status.success(), "make failed"); 271 | emit_rerun_directives_for_contents(&src_dir); 272 | } 273 | 274 | fn make_elfutils(compiler: &cc::Tool, src_dir: &path::Path, out_dir: &path::Path) { 275 | // lock README such that if two crates are trying to compile 276 | // this at the same time (eg libbpf-rs libbpf-cargo) 277 | // they wont trample each other 278 | let file = open_lockable(&src_dir.join("elfutils/README")).unwrap(); 279 | let _lock = fcntl::Flock::lock(file, fcntl::FlockArg::LockExclusive).unwrap(); 280 | 281 | let flags = compiler 282 | .cflags_env() 283 | .into_string() 284 | .expect("failed to get cflags"); 285 | let mut cflags: String = flags 286 | .split_whitespace() 287 | .filter_map(|arg| { 288 | if arg != "-static" { 289 | // compilation fails with -static flag 290 | Some(format!(" {arg}")) 291 | } else { 292 | None 293 | } 294 | }) 295 | .collect(); 296 | 297 | #[cfg(target_arch = "aarch64")] 298 | cflags.push_str(" -Wno-error=stringop-overflow"); 299 | cflags.push_str(&format!(" -I{}/zlib/", src_dir.display())); 300 | 301 | let status = process::Command::new("autoreconf") 302 | .arg("--install") 303 | .arg("--force") 304 | .current_dir(src_dir.join("elfutils")) 305 | .status() 306 | .expect("could not execute make"); 307 | 308 | assert!(status.success(), "make failed"); 309 | 310 | // location of libz.a 311 | let out_lib = format!("-L{}", out_dir.display()); 312 | let status = process::Command::new("./configure") 313 | .arg("--enable-maintainer-mode") 314 | .arg("--disable-debuginfod") 315 | .arg("--disable-libdebuginfod") 316 | .arg("--disable-demangler") 317 | .arg("--without-zstd") 318 | .arg("--prefix") 319 | .arg(src_dir.join("elfutils/prefix_dir")) 320 | .arg("--host") 321 | .arg({ 322 | let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 323 | let arch = match arch.as_str() { 324 | "riscv64gc" => "riscv64", 325 | "riscv32gc" => "riscv32", 326 | other => other, 327 | }; 328 | let vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); 329 | let env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); 330 | let os = env::var("CARGO_CFG_TARGET_OS").unwrap(); 331 | format!("{arch}-{vendor}-{os}-{env}") 332 | }) 333 | .arg("--libdir") 334 | .arg(out_dir) 335 | .env("CC", compiler.path()) 336 | .env("CXX", compiler.path()) 337 | .env("CFLAGS", &cflags) 338 | .env("CXXFLAGS", &cflags) 339 | .env("LDFLAGS", &out_lib) 340 | .current_dir(src_dir.join("elfutils")) 341 | .status() 342 | .expect("could not execute make"); 343 | 344 | assert!(status.success(), "make failed"); 345 | 346 | // Build in elfutils/lib because building libelf requires it. 347 | let status = process::Command::new("make") 348 | .arg("-j") 349 | .arg(format!("{}", num_cpus())) 350 | .arg("BUILD_STATIC_ONLY=y") 351 | .current_dir(src_dir.join("elfutils/lib")) 352 | .status() 353 | .expect("could not execute make"); 354 | 355 | assert!(status.success(), "make failed"); 356 | 357 | // Build libelf only 358 | let status = process::Command::new("make") 359 | .arg("install") 360 | .arg("-j") 361 | .arg(format!("{}", num_cpus())) 362 | .arg("BUILD_STATIC_ONLY=y") 363 | .current_dir(src_dir.join("elfutils/libelf")) 364 | .status() 365 | .expect("could not execute make"); 366 | 367 | assert!(status.success(), "make failed"); 368 | 369 | let status = process::Command::new("make") 370 | .arg("distclean") 371 | .current_dir(src_dir.join("elfutils")) 372 | .status() 373 | .expect("could not execute make"); 374 | 375 | assert!(status.success(), "make failed"); 376 | emit_rerun_directives_for_contents(&src_dir.join("elfutils").join("src")); 377 | } 378 | 379 | fn make_libbpf( 380 | compiler: &cc::Tool, 381 | cflags: &ffi::OsStr, 382 | src_dir: &path::Path, 383 | out_dir: &path::Path, 384 | ) { 385 | let src_dir = src_dir.join("libbpf/src"); 386 | // create obj_dir if it doesn't exist 387 | let obj_dir = path::PathBuf::from(&out_dir.join("obj").into_os_string()); 388 | let _ = fs::create_dir(&obj_dir); 389 | 390 | let status = process::Command::new("make") 391 | .arg("install") 392 | .arg("-j") 393 | .arg(format!("{}", num_cpus())) 394 | .env("BUILD_STATIC_ONLY", "y") 395 | .env("PREFIX", "/") 396 | .env("LIBDIR", "") 397 | .env("OBJDIR", &obj_dir) 398 | .env("DESTDIR", out_dir) 399 | .env("CC", compiler.path()) 400 | .env("CFLAGS", cflags) 401 | .current_dir(&src_dir) 402 | .status() 403 | .expect("could not execute make"); 404 | 405 | assert!(status.success(), "make failed"); 406 | 407 | let status = process::Command::new("make") 408 | .arg("clean") 409 | .current_dir(&src_dir) 410 | .status() 411 | .expect("could not execute make"); 412 | 413 | assert!(status.success(), "make failed"); 414 | emit_rerun_directives_for_contents(&src_dir); 415 | } 416 | 417 | fn num_cpus() -> usize { 418 | std::thread::available_parallelism().map_or(1, |count| count.get()) 419 | } 420 | -------------------------------------------------------------------------------- /rebuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | DOCKER="docker" 4 | 5 | set -eu -o pipefail 6 | 7 | ${DOCKER} build --platform linux/amd64 -t libbpf-sys-builder - <<'EOF' 8 | FROM ubuntu:jammy AS libbpf-sys-builder 9 | 10 | ENV LANG=C.UTF-8 \ 11 | LC_ALL=C.UTF-8 12 | 13 | VOLUME /usr/local/src/libbpf-sys 14 | WORKDIR /usr/local/src/libbpf-sys 15 | 16 | SHELL ["/bin/bash", "-eu", "-o", "pipefail", "-c"] 17 | 18 | RUN \ 19 | export DEBIAN_FRONTEND=noninteractive; \ 20 | apt-get -q update; \ 21 | apt-get -q install -y curl build-essential zlib1g-dev libelf-dev libclang-dev llvm clang pkg-config; \ 22 | apt-get -q clean autoclean; 23 | 24 | RUN \ 25 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable; 26 | 27 | ENTRYPOINT \ 28 | source $HOME/.cargo/env; \ 29 | cargo build --features bindgen-source --release --verbose; 30 | EOF 31 | 32 | ${DOCKER} run --platform linux/amd64 --rm -v "$(pwd):/usr/local/src/libbpf-sys" libbpf-sys-builder 33 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // src/lib.rs 2 | 3 | #![allow(non_upper_case_globals)] 4 | #![allow(non_camel_case_types)] 5 | #![allow(non_snake_case)] 6 | 7 | #[allow(clippy::all)] 8 | mod bindings { 9 | #[cfg(all(feature = "bindgen", not(feature = "bindgen-source")))] 10 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 11 | #[cfg(any(not(feature = "bindgen"), feature = "bindgen-source"))] 12 | include!("bindings.rs"); 13 | } 14 | 15 | pub use bindings::*; 16 | 17 | #[cfg(feature = "vendored-libbpf")] 18 | macro_rules! header { 19 | ($file:literal) => { 20 | ($file, include_str!(concat!("../libbpf/src/", $file))) 21 | }; 22 | } 23 | 24 | /// Vendored libbpf headers 25 | /// 26 | /// Tuple format is: (header filename, header contents) 27 | #[cfg(feature = "vendored-libbpf")] 28 | pub const API_HEADERS: [(&str, &str); 10] = [ 29 | header!("bpf.h"), 30 | header!("libbpf.h"), 31 | header!("btf.h"), 32 | header!("bpf_helpers.h"), 33 | header!("bpf_helper_defs.h"), 34 | header!("bpf_tracing.h"), 35 | header!("bpf_endian.h"), 36 | header!("bpf_core_read.h"), 37 | header!("libbpf_common.h"), 38 | header!("usdt.bpf.h"), 39 | ]; 40 | -------------------------------------------------------------------------------- /tests/tests.rs: -------------------------------------------------------------------------------- 1 | // tests/tests.rs 2 | 3 | #[cfg(test)] 4 | mod tests { 5 | use libbpf_sys::*; 6 | 7 | unsafe extern "C" fn print_fn( 8 | _level: libbpf_print_level, 9 | _arg1: *const std::os::raw::c_char, 10 | _ap: *mut __va_list_tag, 11 | ) -> std::os::raw::c_int { 12 | 0 13 | } 14 | 15 | #[test] 16 | fn test() { 17 | unsafe { 18 | // just tests that we can call into the library 19 | assert!(libbpf_set_print(Some(print_fn as _)).is_some()); 20 | } 21 | } 22 | } 23 | --------------------------------------------------------------------------------