Skip to content

Commit 9a6636d

Browse files
committed
docs: Add documentation for BPF targets
1 parent 7349f6b commit 9a6636d

File tree

3 files changed

+160
-2
lines changed

3 files changed

+160
-2
lines changed

src/doc/rustc/src/SUMMARY.md

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
- [\*-unknown-fuchsia](platform-support/fuchsia.md)
5454
- [\*-unknown-trusty](platform-support/trusty.md)
5555
- [\*-kmc-solid_\*](platform-support/kmc-solid.md)
56+
- [bpf\*-unknown-none](platform-support/bpf-unknown-none.md)
5657
- [csky-unknown-linux-gnuabiv2\*](platform-support/csky-unknown-linux-gnuabiv2.md)
5758
- [hexagon-unknown-linux-musl](platform-support/hexagon-unknown-linux-musl.md)
5859
- [hexagon-unknown-none-elf](platform-support/hexagon-unknown-none-elf.md)

src/doc/rustc/src/platform-support.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,8 @@ target | std | host | notes
296296
[`armv7s-apple-ios`](platform-support/apple-ios.md) | ✓ | | Armv7-A Apple-A6 Apple iOS
297297
[`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat
298298
`avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core`
299-
`bpfeb-unknown-none` | * | | BPF (big endian)
300-
`bpfel-unknown-none` | * | | BPF (little endian)
299+
[`bpfeb-unknown-none`](platform-support/bpf-unknown-none.md) | * | | BPF (big endian)
300+
[`bpfel-unknown-none`](platform-support/bpf-unknown-none.md) | * | | BPF (little endian)
301301
`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian)
302302
`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian)
303303
[`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
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]. The BPF backend in LLVM
60+
simply 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 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 a recoverable manner. Therefore most BPF programs
73+
written in Rust use the following no-op panic handler implementation:
74+
75+
```rust
76+
#[cfg(not(test))]
77+
#[panic_handler]
78+
fn panic(_info: &core::panic::PanicInfo) -> ! {
79+
loop {}
80+
}
81+
```
82+
83+
Infinite loops are forbidden by the BPF verifier. Therefore, if the program
84+
contains any code which can panic, the BPF VM rejects to load it.
85+
86+
## Testing
87+
88+
BPF bytecode needs to be executed on a BPF virtual machine, like the one
89+
provided by the Linux kernel or one of the user-space implementations like
90+
[rbpf][rbpf]. None of them support running Rust testsuite. One of the reasons
91+
is the lack of support for panicking.
92+
93+
Currently the best way of unit testing the code used in BPF programs is
94+
extracting it to a separate crate, which can be built and ran on a host target,
95+
and using it as a dependency.
96+
97+
## Cross-compilation toolchains
98+
99+
BPF programs are always cross-compiled from a host (e.g.
100+
`x86_64-unknown-linux-*`) for a BPF target (e.g. `bpfel-unknown-none`).
101+
102+
The endianness of a chosen BPF target needs to match the endianness of the BPF
103+
VM host on which the program is supposed to run.
104+
105+
The architecture of the BPF VM host often has an impact on types that the BPF
106+
programs should use. For example [kprobes][kprobe], [fprobes][fprobe] and
107+
[uprobes][uprobe] allow dynamic function tracing and lookup into registers.
108+
Registers and calling convenctions are different across architectures. Due to
109+
`char` in C being signed or unsigned on different architectures, the signatures
110+
of [BPF helpers][bpf-helpers] and [kfuncs][kfunc] also end up different.
111+
112+
That difference is still not a concern of the compiler. Instead, it should be
113+
handled by the developers. [Aya][aya] (the library for writing Linux BPF
114+
programs and the main consumer of BPF targets in Rust) handles that by
115+
providing the [`aya-ebpf-cty`][aya-ebpf-cty] crate, with type aliases similar
116+
to those provided by [`core:ffi`][core-ffi]. [`aya-ebpf-cty`][aya-ebpf-cty]
117+
allows to specify the VM target through the `CARGO_CFG_BPF_TARGET_ARCH`
118+
environment variable (e.g. `CARGO_CFG_BPF_TARGET_ARCH=aarch64`).
119+
120+
## C code
121+
122+
It's possible to link a Rust BPF project to bitcode or object files which are
123+
built from C code with [clang][clang]. It can be done using a `rustc-link-lib`
124+
instruction in `build.rs`. Example:
125+
126+
```rust
127+
use std::{env, process::Command};
128+
129+
let out_dir = env::var("OUT_DIR").unwrap();
130+
let c_module = "my_module.bpf.c";
131+
let s = Command::new("clang")
132+
.arg("-I")
133+
.arg("src/")
134+
.arg("-O2")
135+
.arg("-emit-llvm")
136+
.arg("-target")
137+
.arg("bpf")
138+
.arg("-c")
139+
.arg("-g")
140+
.arg(c_module)
141+
.arg("-o")
142+
.arg(format!("{out_dir}/my_module.bpf.o"))
143+
.status()
144+
.unwrap();
145+
assert!(s.success());
146+
println!("cargo:rustc-link-search=native={out_dir}");
147+
println!("cargo:rustc-link-lib=link-arg={out_dir}/my_module.bpf.o");
148+
```
149+
150+
[ebpf]: https://ebpf.io/
151+
[bpf-linker]: https://github.com/aya-rs/bpf-linker
152+
[bpf-abi]: https://www.kernel.org/doc/html/v6.13-rc5/bpf/standardization/abi.html
153+
[rbpf]: https://github.com/qmonnet/rbpf
154+
[aya]: https://aya-rs.dev
155+
[aya-ebpf-cty]: https://github.com/aya-rs/aya/tree/main/ebpf/aya-ebpf-cty
156+
[core-ffi]: https://doc.rust-lang.org/stable/core/ffi/index.html
157+
[clang]: https://clang.llvm.org/

0 commit comments

Comments
 (0)