|
| 1 | +# `bpf*-unknown-none` |
| 2 | + |
| 3 | +**Tier: 3** |
| 4 | + |
| 5 | +* `bpfeb-unknown-none` (big endian) |
| 6 | +* `bpfel-unknown-none` (little endian) |
| 7 | + |
| 8 | +Targets for the [BPF virtual machine][ebpf]. |
| 9 | + |
| 10 | +## Target maintainers |
| 11 | + |
| 12 | +- [@alessandrod](https://github.com/alessandrod) |
| 13 | +- [@dave-tucker](https://github.com/dave-tucker) |
| 14 | +- [@tamird](https://github.com/tamird) |
| 15 | +- [@vadorovsky](https://github.com/vadorovsky) |
| 16 | + |
| 17 | +## Requirements |
| 18 | + |
| 19 | +BPF targets require a Rust toolchain with the `rust-src` component and |
| 20 | +[bpf-linker][bpf-linker]. |
| 21 | + |
| 22 | +They don't support std and alloc and are meant for a `no_std` environment. |
| 23 | + |
| 24 | +`extern "C"` uses the [BPF ABI calling convention][bpf-abi]. |
| 25 | + |
| 26 | +Produced binaries use the ELF format. |
| 27 | + |
| 28 | +## Building the target |
| 29 | + |
| 30 | +You can build Rust with support for BPF targets by adding them to the `target` |
| 31 | +list in `config.toml`: |
| 32 | + |
| 33 | +```toml |
| 34 | +[build] |
| 35 | +target = ["bpfeb-unknown-none", "bpfel-unknown-none"] |
| 36 | +``` |
| 37 | + |
| 38 | +## Building Rust programs |
| 39 | + |
| 40 | +Rust does not yet ship pre-compiled artifacts for this target. To compile for |
| 41 | +this target, you will either need to build Rust with the target enabled (see |
| 42 | +"Building the target" above), or build your own copy of `core` by using |
| 43 | +`build-std` or similar. |
| 44 | + |
| 45 | +Building the BPF target requires specifying it explicitly. Users can either |
| 46 | +add it to the `target` list in `config.toml`: |
| 47 | + |
| 48 | +```toml |
| 49 | +[build] |
| 50 | +target = ["bpfel-unknown-none"] |
| 51 | +``` |
| 52 | + |
| 53 | +Or specify it directly in the `cargo build` invocation: |
| 54 | + |
| 55 | +```console |
| 56 | +cargo +nightly build -Z build-std=core --target bpfel-unknown-none |
| 57 | +``` |
| 58 | + |
| 59 | +BPF has its own debug info format called [BTF][btf]. BPF backend in LLVM simply |
| 60 | +converts the LLVM debug info into BTF. |
| 61 | + |
| 62 | +BPF targets use [bpf-linker][bpf-linker], an LLVM bitcode linker, which by |
| 63 | +default strips the debug info, but it has an experimental feature of emitting |
| 64 | +BTF, which can be enabled by adding `-C link-arg=--btf` to `RUSTFLAGS`. With |
| 65 | +that feature enabled, [bpf-linker][bpf-linker] does not only link different |
| 66 | +crates/modules, but also performs a necessary sanitization of debug info, which |
| 67 | +is required to produce a valid [BTF][btf] acceptable by the Linux kernel. |
| 68 | + |
| 69 | +## Error handling |
| 70 | + |
| 71 | +There is no concept of stack unwinding in BPF, therefore BPF programs are |
| 72 | +expected to handle errors in recoverable manner and return early. Therefore, |
| 73 | +the most of BPF programs written in Rust use the following noop implementation |
| 74 | +of panic handler: |
| 75 | + |
| 76 | +```rust |
| 77 | +#[cfg(not(test))] |
| 78 | +#[panic_handler] |
| 79 | +fn panic(_info: &core::panic::PanicInfo) -> ! { |
| 80 | + loop {} |
| 81 | +} |
| 82 | +``` |
| 83 | + |
| 84 | +Infinite loops are forbidden by the BPF verifier. Therefore, if the program |
| 85 | +contains any code which can panic, BPF VM rejects to load it. |
| 86 | + |
| 87 | +## Testing |
| 88 | + |
| 89 | +BPF bytecode needs to be executed on a BPF virtual machine, like the one |
| 90 | +provided by the Linux kernel or one of user-space implementations like |
| 91 | +[rbpf][rbpf]. None of them supports running Rust testsuite. One of the reasons |
| 92 | +is lack of support for panicking. |
| 93 | + |
| 94 | +Currently the best way of unit testing the code used in BPF programs is |
| 95 | +extracting it to a separate crate, which can be built and ran on a host target, |
| 96 | +and using it as a dependency. |
| 97 | + |
| 98 | +## Cross-compilation toolchains |
| 99 | + |
| 100 | +BPF programs are always cross-compiled from a host (e.g. |
| 101 | +`x86_64-unknown-linux-*`) for a BPF target (e.g. `bpfel-unknown-none`). |
| 102 | + |
| 103 | +The endianness of a chosen BPF target needs to match the endianness of the BPF |
| 104 | +VM host on which the program is supposed to run. |
| 105 | + |
| 106 | +The architecture of the BPF VM host often has an impact on types that the BPF |
| 107 | +programs should use. For example [kprobes][kprobe], [fprobes][fprobe] and |
| 108 | +[uprobes][uprobe] allow dynamic function tracing and lookup into registers. |
| 109 | +Registers and calling convenctions are different across architectures. Due to |
| 110 | +`char` in C being signed or unsigned on different architectures, the signatures |
| 111 | +of [BPF helpers][bpf-helpers] and [kfuncs][kfunc] also end up different. |
| 112 | + |
| 113 | +That difference is still not a concern of the compiler. Instead, it should be |
| 114 | +handled by the developers. [Aya][aya] (the library for writing Linux BPF |
| 115 | +programs and the main consumer of BPF targets in Rust) handles that by |
| 116 | +providing the [`aya-ebpf-cty`][aya-ebpf-cty] crate, with type aliases similar |
| 117 | +to those provided by [`core:ffi`][core-ffi]. [`aya-ebpf-cty`][aya-ebpf-cty] |
| 118 | +allows to specify the VM target through the `CARGO_CFG_BPF_TARGET_ARCH` |
| 119 | +environment variable (e.g. `CARGO_CFG_BPF_TARGET_ARCH=aarch64`). |
| 120 | + |
| 121 | +## C code |
| 122 | + |
| 123 | +It's possible to link a Rust BPF project to bitcode or object files which are |
| 124 | +built from C code with [clang][clang]. It can be done using a `rustc-link-lib` |
| 125 | +instruction in `build.rs`. Example: |
| 126 | + |
| 127 | +```rust |
| 128 | +let out_dir = std::env::var("OUT_DIR").unwrap(); |
| 129 | +let c_module = "my_module.bpf.c" |
| 130 | +let s = Command::new("clang") |
| 131 | + .arg("-I") |
| 132 | + .arg("src/") |
| 133 | + .arg("-O2") |
| 134 | + .arg("-emit-llvm") |
| 135 | + .arg("-target") |
| 136 | + .arg("bpf") |
| 137 | + .arg("-c") |
| 138 | + .arg("-g") |
| 139 | + .arg(c_module) |
| 140 | + .arg("-o") |
| 141 | + .arg(format!("{out_dir}/my_module.bpf.o")) |
| 142 | + .status() |
| 143 | + .unwrap(); |
| 144 | +assert!(s.success()); |
| 145 | +println!("cargo:rustc-link-search=native={out_dir}"); |
| 146 | +println!("cargo:rustc-link-lib=link-arg={out_dir}/my_module.bpf.o"); |
| 147 | +``` |
| 148 | + |
| 149 | +[ebpf]: https://ebpf.io/ |
| 150 | +[bpf-linker]: https://github.com/aya-rs/bpf-linker |
| 151 | +[bpf-abi]: https://www.kernel.org/doc/html/v6.13-rc5/bpf/standardization/abi.html |
| 152 | +[rbpf]: https://github.com/qmonnet/rbpf |
| 153 | +[aya]: https://aya-rs.dev |
| 154 | +[aya-ebpf-cty]: https://github.com/aya-rs/aya/tree/main/ebpf/aya-ebpf-cty |
| 155 | +[core-ffi]: https://doc.rust-lang.org/stable/core/ffi/index.html |
| 156 | +[clang]: https://clang.llvm.org/ |
0 commit comments