Skip to content

Commit

Permalink
aya: Implement .kconfig support
Browse files Browse the repository at this point in the history
Implement support for external data symbols (kconfig) following libbpf
logic.
  • Loading branch information
marysaka authored and davibe committed Sep 3, 2024
1 parent ab000ad commit 3dd4a80
Show file tree
Hide file tree
Showing 15 changed files with 483 additions and 24 deletions.
212 changes: 209 additions & 3 deletions aya-obj/src/btf/btf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ use crate::{
info::{FuncSecInfo, LineSecInfo},
relocation::Relocation,
Array, BtfEnum, BtfKind, BtfMember, BtfType, Const, Enum, FuncInfo, FuncLinkage, Int,
IntEncoding, LineInfo, Struct, Typedef, Union, VarLinkage,
IntEncoding, LineInfo, Struct, Typedef, Union, Var, VarLinkage,
},
generated::{btf_ext_header, btf_header},
generated::{bpf_map_type, btf_ext_header, btf_header, BPF_F_RDONLY_PROG},
maps::{bpf_map_def, LegacyMap},
util::{bytes_of, HashMap},
Object,
EbpfSectionKind, Map, Object,
};

pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
Expand Down Expand Up @@ -157,6 +158,20 @@ pub enum BtfError {
/// unable to get symbol name
#[error("Unable to get symbol name")]
InvalidSymbolName,

/// external symbol is invalid
#[error("Invalid extern symbol `{symbol_name}`")]
InvalidExternalSymbol {
/// name of the symbol
symbol_name: String,
},

/// external symbol not found
#[error("Extern symbol not found `{symbol_name}`")]
ExternalSymbolNotFound {
/// name of the symbol
symbol_name: String,
},
}

/// Available BTF features
Expand Down Expand Up @@ -463,6 +478,57 @@ impl Btf {
})
}

pub(crate) fn type_align(&self, root_type_id: u32) -> Result<usize, BtfError> {
let mut type_id = root_type_id;
for _ in 0..MAX_RESOLVE_DEPTH {
let ty = self.types.type_by_id(type_id)?;
let size = match ty {
BtfType::Array(Array { array, .. }) => {
type_id = array.element_type;
continue;
}
BtfType::Struct(Struct { size, members, .. })
| BtfType::Union(Union { size, members, .. }) => {
let mut max_align = 1;

for m in members {
let align = self.type_align(m.btf_type)?;
max_align = usize::max(align, max_align);

if ty.member_bit_field_size(m).unwrap() == 0
|| m.offset % (8 * align as u32) != 0
{
return Ok(1);
}
}

if size % max_align as u32 != 0 {
return Ok(1);
}

return Ok(max_align);
}

other => {
if let Some(size) = other.size() {
u32::min(BtfType::ptr_size(), size)
} else if let Some(next) = other.btf_type() {
type_id = next;
continue;
} else {
return Err(BtfError::UnexpectedBtfType { type_id });
}
}
};

return Ok(size as usize);
}

Err(BtfError::MaximumTypeDepthReached {
type_id: root_type_id,
})
}

/// Encodes the metadata as BTF format
pub fn to_bytes(&self) -> Vec<u8> {
// Safety: btf_header is POD
Expand All @@ -473,6 +539,37 @@ impl Btf {
buf
}

pub(crate) fn get_extern_data_sec_entry_info(
&self,
target_var_name: &str,
) -> Result<(String, Var), BtfError> {
for t in &self.types.types {
if let BtfType::DataSec(d) = t {
let sec_name = self.string_at(d.name_offset)?;

for d in &d.entries {
if let BtfType::Var(var) = self.types.type_by_id(d.btf_type)? {
let var_name = self.string_at(var.name_offset)?;

if target_var_name == var_name {
if var.linkage != VarLinkage::Extern {
return Err(BtfError::InvalidExternalSymbol {
symbol_name: var_name.into(),
});
}

return Ok((sec_name.into(), var.clone()));
}
}
}
}
}

Err(BtfError::ExternalSymbolNotFound {
symbol_name: target_var_name.into(),
})
}

// This follows the same logic as libbpf's bpf_object__sanitize_btf() function.
// https://github.com/libbpf/libbpf/blob/05f94ddbb837f5f4b3161e341eed21be307eaa04/src/libbpf.c#L2701
//
Expand Down Expand Up @@ -610,6 +707,14 @@ impl Btf {
}
};
e.offset = *offset as u32;

if var.linkage == VarLinkage::Extern {
let mut var = var.clone();
var.linkage = VarLinkage::Global;

types.types[e.btf_type as usize] = BtfType::Var(var);
}

debug!(
"{} {}: VAR {}: fixup offset {}",
kind, name, var_name, offset
Expand Down Expand Up @@ -730,6 +835,107 @@ impl Default for Btf {
}

impl Object {
fn patch_extern_data_internal(
&mut self,
externs: &HashMap<String, Vec<u8>>,
) -> Result<Option<(SectionIndex, Vec<u8>)>, BtfError> {
if let Some(ref mut obj_btf) = &mut self.btf {
if obj_btf.is_empty() {
return Ok(None);
}

let mut kconfig_map_index = 0;

for map in self.maps.values() {
if map.section_index() >= kconfig_map_index {
kconfig_map_index = map.section_index() + 1;
}
}

let kconfig_map_index = self.maps.len();

let symbols = self
.symbol_table
.iter_mut()
.filter(|(_, s)| s.name.is_some() && s.section_index.is_none() && s.is_external)
.map(|(_, s)| (s.name.as_ref().unwrap().clone(), s));

let mut section_data = Vec::<u8>::new();
let mut offset = 0u64;
let mut has_extern_data = false;

for (name, symbol) in symbols {
let (datasec_name, var) = obj_btf.get_extern_data_sec_entry_info(&name)?;

if datasec_name == ".kconfig" {
has_extern_data = true;

let type_size = obj_btf.type_size(var.btf_type)?;
let type_align = obj_btf.type_align(var.btf_type)? as u64;

let mut external_value_opt = externs.get(&name);
let empty_data = vec![0; type_size];

if external_value_opt.is_none() && symbol.is_weak {
external_value_opt = Some(&empty_data);
}

if let Some(data) = external_value_opt {
symbol.address = (offset + (type_align - 1)) & !(type_align - 1);
symbol.size = type_size as u64;
symbol.section_index = Some(kconfig_map_index);

section_data.resize((symbol.address - offset) as usize, 0);

self.symbol_offset_by_name.insert(name, symbol.address);
section_data.extend(data);
offset = symbol.address + data.len() as u64;
} else {
return Err(BtfError::ExternalSymbolNotFound { symbol_name: name });
}
}
}

if has_extern_data {
self.section_infos.insert(
".kconfig".into(),
(SectionIndex(kconfig_map_index), section_data.len() as u64),
);

return Ok(Some((SectionIndex(kconfig_map_index), section_data)));
}
}
Ok(None)
}

/// Patches extern data
pub fn patch_extern_data(
&mut self,
externs: &HashMap<String, Vec<u8>>,
) -> Result<(), BtfError> {
if let Some((section_index, data)) = self.patch_extern_data_internal(externs)? {
self.maps.insert(
".kconfig".into(),
Map::Legacy(LegacyMap {
def: bpf_map_def {
map_type: bpf_map_type::BPF_MAP_TYPE_ARRAY as u32,
key_size: mem::size_of::<u32>() as u32,
value_size: data.len() as u32,
max_entries: 1,
map_flags: BPF_F_RDONLY_PROG,
..Default::default()
},
section_index: section_index.0,
section_kind: EbpfSectionKind::Rodata,
symbol_index: None,
data,
}),
);
}

Ok(())
}

/// Fixes up and sanitizes BTF data.
///
/// Mostly, it removes unsupported types and works around LLVM behaviours.
Expand Down
6 changes: 5 additions & 1 deletion aya-obj/src/btf/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1292,11 +1292,15 @@ impl BtfType {
BtfType::Struct(t) => Some(t.size),
BtfType::Union(t) => Some(t.size),
BtfType::DataSec(t) => Some(t.size),
BtfType::Ptr(_) => Some(mem::size_of::<&()>() as u32),
BtfType::Ptr(_) => Some(Self::ptr_size()),
_ => None,
}
}

pub(crate) fn ptr_size() -> u32 {
mem::size_of::<&()>() as u32
}

pub(crate) fn btf_type(&self) -> Option<u32> {
match self {
BtfType::Const(t) => Some(t.btf_type),
Expand Down
13 changes: 13 additions & 0 deletions aya-obj/src/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub struct Features {
bpf_cookie: bool,
cpumap_prog_id: bool,
devmap_prog_id: bool,
bpf_syscall_wrapper: bool,
btf: Option<BtfFeatures>,
}

Expand All @@ -61,6 +62,7 @@ impl Features {
bpf_cookie: bool,
cpumap_prog_id: bool,
devmap_prog_id: bool,
bpf_syscall_wrapper: bool,
btf: Option<BtfFeatures>,
) -> Self {
Self {
Expand All @@ -71,6 +73,7 @@ impl Features {
bpf_cookie,
cpumap_prog_id,
devmap_prog_id,
bpf_syscall_wrapper,
btf,
}
}
Expand Down Expand Up @@ -109,6 +112,10 @@ impl Features {
pub fn devmap_prog_id(&self) -> bool {
self.devmap_prog_id
}
/// Returns whether BPF syscall wrapper hooking is supported.
pub fn bpf_syscall_wrapper(&self) -> bool {
self.bpf_syscall_wrapper
}

/// If BTF is supported, returns which BTF features are supported.
pub fn btf(&self) -> Option<&BtfFeatures> {
Expand Down Expand Up @@ -464,6 +471,8 @@ impl Object {
address: symbol.address(),
size: symbol.size(),
is_definition: symbol.is_definition(),
is_external: symbol.is_undefined() && (symbol.is_global() || symbol.is_weak()),
is_weak: symbol.is_weak(),
kind: symbol.kind(),
};
bpf_obj.symbol_table.insert(symbol.index().0, sym);
Expand Down Expand Up @@ -1440,6 +1449,8 @@ mod tests {
size,
is_definition: false,
kind: SymbolKind::Text,
is_external: false,
is_weak: false,
},
);
obj.symbols_by_section
Expand Down Expand Up @@ -2576,6 +2587,8 @@ mod tests {
address: 0,
size: 3,
is_definition: true,
is_external: false,
is_weak: false,
kind: SymbolKind::Data,
},
);
Expand Down
17 changes: 12 additions & 5 deletions aya-obj/src/relocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ pub(crate) struct Symbol {
pub(crate) address: u64,
pub(crate) size: u64,
pub(crate) is_definition: bool,
pub(crate) is_external: bool,
pub(crate) is_weak: bool,
pub(crate) kind: SymbolKind,
}

Expand Down Expand Up @@ -218,7 +220,9 @@ fn relocate_maps<'a, I: Iterator<Item = &'a Relocation>>(
};

// calls and relocation to .text symbols are handled in a separate step
if insn_is_call(&instructions[ins_index]) || text_sections.contains(&section_index) {
if insn_is_call(&instructions[ins_index])
|| (text_sections.contains(&section_index) && !sym.is_external)
{
continue;
}

Expand Down Expand Up @@ -367,10 +371,11 @@ impl<'a> FunctionLinker<'a> {
// only consider text relocations, data relocations are
// relocated in relocate_maps()
sym.kind == SymbolKind::Text
|| sym
.section_index
.map(|section_index| self.text_sections.contains(&section_index))
.unwrap_or(false)
|| (!sym.is_external
&& sym
.section_index
.map(|section_index| self.text_sections.contains(&section_index))
.unwrap_or(false))
});

// not a call and not a text relocation, we don't need to do anything
Expand Down Expand Up @@ -510,6 +515,8 @@ mod test {
address,
size,
is_definition: false,
is_external: false,
is_weak: false,
kind: SymbolKind::Data,
}
}
Expand Down
8 changes: 7 additions & 1 deletion aya/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ libc = { workspace = true }
log = { workspace = true }
object = { workspace = true, features = ["elf", "read_core", "std", "write"] }
thiserror = { workspace = true }
tokio = { workspace = true, features = ["rt"], optional = true }
tokio = { workspace = true, features = [
"rt",
"macros",
"rt-multi-thread",
"net",
], optional = true }
flate2 = "1.0"

[dev-dependencies]
tempfile = { workspace = true }
Expand Down
Loading

0 comments on commit 3dd4a80

Please sign in to comment.