use anyhow::{Context, Result, bail};
use btf_rs::{Btf, Type};
use super::{find_struct, member_byte_offset};
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub struct HtabOffsets {
pub htab_buckets: usize,
pub htab_n_buckets: usize,
pub bucket_size: usize,
pub bucket_head: usize,
pub hlist_nulls_head_first: usize,
pub hlist_nulls_node_next: usize,
pub htab_elem_size_base: usize,
}
fn find_bucket_struct(btf: &Btf) -> Result<(btf_rs::Struct, usize)> {
let types = btf
.resolve_types_by_name("bucket")
.with_context(|| "btf: type 'bucket' not found")?;
for t in &types {
if let Type::Struct(s) = t
&& let Ok(head_off) = member_byte_offset(btf, s, "head")
{
return Ok((s.clone(), head_off));
}
}
bail!("btf: no 'bucket' struct with 'head' field found");
}
pub(super) fn resolve_htab_offsets(btf: &Btf) -> Result<HtabOffsets> {
let (bpf_htab, _) = find_struct(btf, "bpf_htab")?;
let htab_buckets = member_byte_offset(btf, &bpf_htab, "buckets")?;
let htab_n_buckets = member_byte_offset(btf, &bpf_htab, "n_buckets")?;
let (bucket_struct, bucket_head) = find_bucket_struct(btf)?;
let bucket_size = bucket_struct.size();
let (hlist_nulls_head, _) = find_struct(btf, "hlist_nulls_head")?;
let hlist_nulls_head_first = member_byte_offset(btf, &hlist_nulls_head, "first")?;
let (hlist_nulls_node, _) = find_struct(btf, "hlist_nulls_node")?;
let hlist_nulls_node_next = member_byte_offset(btf, &hlist_nulls_node, "next")?;
let (htab_elem, _) = find_struct(btf, "htab_elem")?;
let htab_elem_size_base = htab_elem.size();
let hash_node_off = member_byte_offset(btf, &htab_elem, "hash_node")?;
if hash_node_off != 0 {
anyhow::bail!(
"htab_elem.hash_node at offset {} (expected 0): walker assumes \
elem_kva == hash_node_kva. A kernel that reorders htab_elem \
must teach the walker container_of math before this resolver \
returns Ok.",
hash_node_off,
);
}
Ok(HtabOffsets {
htab_buckets,
htab_n_buckets,
bucket_size,
bucket_head,
hlist_nulls_head_first,
hlist_nulls_node_next,
htab_elem_size_base,
})
}