Skip to content

Commit e323a85

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

File tree

2 files changed

+158
-2
lines changed

2 files changed

+158
-2
lines changed

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,156 @@
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

Comments
 (0)