Issue |
143361
|
Summary |
[BPF] `BTFDebug` doesn't generate BTF for all structs, if BPF map type is wrapped in Rust wrapper types
|
Labels |
new issue
|
Assignees |
|
Reporter |
vadorovsky
|
This issue was detected during an attempt of supporting BTF maps in Aya (aya-rs/aya#1117).
BTF map definitions have the following format in C:
```c
struct my_key {
int a;
};
struct my_value {
int a;
};
struct {
int (*type)[BPF_MAP_TYPE_HASH];
typeof(struct my_key) *key;
typeof(struct my_value) *value;
int (*max_entries)[10];
} map_1 SEC(".maps");
```
The `map_1` instance is then used as `*void` in libbpf functions like `bpf_map_lookup_elem`, `bpf_map_update_elem` etc..
The key and value structs can be anything as long as they hold primitive/POD types and as long as they are aligned.
The program above produces the following BTF:
```
#0: <VOID>
#1: <PTR> --> [3]
#2: <INT> 'int' bits:32 off:0 enc:signed
#3: <ARRAY> n:1 idx-->[4] val-->[2]
#4: <INT> '__ARRAY_SIZE_TYPE__' bits:32 off:0
#5: <PTR> --> [6]
#6: <STRUCT> 'my_key' sz:4 n:1
#00 'a' off:0 --> [2]
#7: <PTR> --> [8]
#8: <STRUCT> 'my_value' sz:4 n:1
#00 'a' off:0 --> [2]
#9: <PTR> --> [10]
#10: <ARRAY> n:10 idx-->[4] val-->[2]
#11: <STRUCT> '<anon>' sz:32 n:4
#00 'type' off:0 --> [1]
#01 'key' off:64 --> [5]
#02 'value' off:128 --> [7]
#03 'max_entries' off:192 --> [9]
#12: <VAR> 'map_1' kind:global-alloc --> [11]
#13: <DATASEC> '.maps' sz:0 n:1
#00 off:0 sz:32 --> [12]
```
We can see both the map struct (`#11`) and the types used as key (`#6`) and value (`#8`).
However, in Rust, we want to wrap such map definitions in two wrapper types:
* A wrapper type representing a specific map type (e.g. `HashMap`, `RingBuf`), which provide methods (`get`, `update`), so people interact with those wrapper types instead of working with void pointers.
* Another wrapper type, which wraps the type above in `UnsafeCell`, so the Rust compiler doesn't complain about concurrent mutability and doesn't consider such action unsafe. It's basically a way of telling compiler, that we (Aya) guarantee that this type provides a thread-safe mutabiity (and it does out of the box, because of RCU in Linux kernel).
This ends up looking like:
```rust
#![no_std]
#![no_main]
pub const BPF_MAP_TYPE_HASH: usize = 1;
// The real map definition.
pub struct HashMapDef<K, V, const M: usize, const F: usize> {
r#type: *const [i32; BPF_MAP_TYPE_HASH],
key: *const K,
value: *const V,
max_entries: *const [i32; M],
map_flags: *const [i32; F],
}
impl<K, V, const M: usize, const F: usize> HashMapDef<K, V, M, F> {
pub const fn new() -> Self {
Self {
r#type: &[0i32; BPF_MAP_TYPE_HASH],
key: ::core::ptr::null(),
value: ::core::ptr::null(),
max_entries: &[0i32; M],
map_flags: &[0i32; F],
}
}
}
// Use `UnsafeCell` to allow the mutability by multiple threads.
pub struct HashMap<K, V, const M: usize, const F: usize = 0>(
core::cell::UnsafeCell<HashMapDef<K, V, M, F>>,
);
impl<K, V, const M: usize, const F: usize> HashMap<K, V, M, F> {
pub const fn new() -> Self {
Self(core::cell::UnsafeCell::new(HashMapDef::new()))
}
}
/// Tell Rust that `HashMap` is thread-safe.
unsafe impl<K: Sync, V: Sync, const M: usize, const F: usize> Sync for HashMap<K, V, M, F> {}
// Define custom structs for key and values.
pub struct MyKey(u32);
pub struct MyValue(u32);
#[link_section = ".maps"]
#[export_name = "HASH_MAP"]
pub static HASH_MAP: HashMap<MyKey, MyValue, 10> = HashMap::new();
```
However, for this Rust program, the BTF for `MyKey` and `MyValue` is `<FORWARD>` and does not contain the actual `<STRUCT>` definition.
The problem is reproducible only if the key and/or value type are custom structs.
I'm working on the fix, which is mostly ready, apart from llvm-lit test which will make sure it doesn't regress in the future.
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs