# Btf-rs
Rust library for the [BPF Type Format (BTF)](https://www.kernel.org/doc/html/latest/bpf/btf.html).
The BPF Type Format is a metadata format encoding debugging information such as
types, function prototypes, structure layouts, etc. and is often used, but not
limited, to deal with [eBPF](https://ebpf.io) programs.
```toml
[dependencies]
btf-rs = "2.0"
```
The [integration tests](btf-rs/tests/integration_test.rs) give good examples on
how to use this library. We recommend reading the [official BTF
documentation](https://www.kernel.org/doc/html/latest/bpf/btf.html) as this
library is offering a low-level API.
This library was initially developed for a Linux kernel packet tracking tool,
[Retis](https://github.com/retis-org/retis), but is exported on its own as there
are no specific ties with the mentioned tool and can be (re)used in all kinds of
Rust projects.
## Overview
### Parsing BTF
The main object this library offers is `Btf`, which represents a parsed
BTF object. It offers helpers to resolve ids (`u32`), names (`String`)
and types / chained types (`Type`).
`Btf` can be constructed using a BTF file or a split BTF one. BTF files
hold self-contained information, while split BTF files are built upon a base
BTF file and extend it. For example, in a standard Linux environment BTF
files and split files can be found under `/sys/kernel/btf`,
`/sys/kernel/btf/vmlinux` being the BTF file for the kernel and other files
matching `/sys/kernel/btf/<module-name>` being BTF split files for its
modules.
```rust
use btf_rs::Btf;
let base = Btf::from_file("/sys/kernel/btf/vmlinux").unwrap();
let ovs = Btf::from_split_file("/sys/kernel/btf/openvswitch", &base).unwrap();
let bbr = Btf::from_split_file("/sys/kernel/btf/tcp_bbr", &base).unwrap();
```
*Btf-rs* also supports constructing `Btf` using byte slices.
```rust
use std::fs;
use btf_rs::Btf;
let base = Btf::from_bytes(&fs::read("/sys/kernel/btf/vmlinux").unwrap()).unwrap();
let ovs = Btf::from_split_bytes(&fs::read("/sys/kernel/btf/openvswitch").unwrap(), &base)
.unwrap();
let bbr = Btf::from_split_bytes(&fs::read("/sys/kernel/btf/bbr").unwrap(), &base).unwrap();
```
### Resolving types
Types can be resolved using a `Btf` object. The following is an
example of how a function can be inspected to retrieve information about its
first parameter. Here the function `kfree_skb_reason` is taking a `struct
sk_buff *` as its first argument.
```rust
use btf_rs::*;
let btf = Btf::from_file("/sys/kernel/btf/vmlinux").unwrap();
let func = match btf
.resolve_types_by_name("kfree_skb_reason")
.unwrap()
.pop()
.unwrap()
{
Type::Func(func) => func,
_ => panic!("Resolved type is not a function"),
};
let proto = match btf.resolve_chained_type(&func).unwrap() {
Type::FuncProto(proto) => proto,
_ => panic!("Resolved type is not a function proto"),
};
assert!(proto.parameters.len() > 1);
// The following prints "skb".
println!("{}", btf.resolve_name(&proto.parameters[0]).unwrap());
let ptr = match btf.resolve_chained_type(&proto.parameters[0]).unwrap() {
Type::Ptr(ptr) => ptr,
_ => panic!("Resolved type is not a pointer"),
};
let r#struct = match btf.resolve_chained_type(&ptr).unwrap() {
Type::Struct(r#struct) => r#struct,
_ => panic!("Resolved type is not a struct"),
};
// The following prints "sk_buff".
println!("{}", btf.resolve_name(&r#struct).unwrap());
```
Other information such as function scope and return value, structure size
and members, etc. can be retrieved. For all those see the `Type` and its
associated structures documentation.
### Additional objects
Additional objects and helpers built on top of the ones described here can
be found in the `utils` sub-module. Those are aimed at easing BTF
consumption in common cases, such as parsing all BTF files of a running
Linux kernel and its modules or dealing with .BTF ELF sections.
### Feature flags
- `elf`: Enable helpers parsing the .BTF section of ELF files in
`utils::elf`.
- `elf-compression`: Enable handling of compressed ELF files (e.g.
compressed kernel modules) in `utils::elf`. The Bzip2, Gzip, Xz and Zstd
compression algorithms are currently supported.
- `regex`: Enable name resolutions by regex (`regex::Regex`).
### `Btf` backends
The `Btf` object supports different internal backends (`Backend`),
optimized for different uses: `Backend::Cache` and `Backend::Mmap`. The
former provides a faster API at the cost of slower initialization and
increase in memory footprint. The latter provides a fast initialization time
and lower memory footprint at the cast of slower API performances.
`Backend::Cache` is used by default. The backend can also be explicitly
selected using dedicated constructors (see `from_*_with_backend` helpers).
All BTF data read from bytes use `Backend::Cache` (this includes all `elf`
feature helpers) and `Backend::Mmap` is only supported for base BTF files.
A [benchmark utility](https://github.com/retis-org/btf-rs/blob/main/btf-rs/examples/benchmark.rs)
is provided to gather actual numbers. It should be run on the *target*
machine, to investigate how *btf-rs* would perform. E.g.
```shell
$ cargo build --release -F regex --example benchmark
$ ./target/release/examples/benchmark --help
[...]
$ ./target/release/examples/benchmark -i 100 \
--id 77378 --name __kfree_skb --regex "^[[:alnum:]]+_drop_reason$" \
--backend cache
[...]
```
A few parameters are required to configure the type id and name to use for
querying the BTF data. Those can be selected by first inspecting the BTF data
using `bpftool` (`bpftool btf dump file /sys/kernel/btf/vmlinux`).