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

Reply via email to