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 committed Jun 26, 2023
1 parent 4c08b9b commit 5cb711c
Show file tree
Hide file tree
Showing 12 changed files with 492 additions and 18 deletions.
228 changes: 220 additions & 8 deletions aya-obj/src/btf/btf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ use crate::{
Array, BtfEnum, BtfKind, BtfMember, BtfType, Const, Enum, FuncInfo, FuncLinkage, Int,
IntEncoding, LineInfo, Struct, Typedef, 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,
BpfSectionKind, Map, Object,
};

#[cfg(not(feature = "std"))]
use crate::std;

use super::{Union, Var};

pub(crate) const MAX_RESOLVE_DEPTH: u8 = 32;
pub(crate) const MAX_SPEC_LEN: usize = 64;

Expand Down Expand Up @@ -160,6 +163,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 @@ -402,6 +419,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 @@ -412,6 +480,38 @@ 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)?;
let name = sec_name.to_string();

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)?.to_string();

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

return Ok((name, var.clone()));
}
}
}
}
}

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

pub(crate) fn fixup_and_sanitize(
&mut self,
section_infos: &HashMap<String, (SectionIndex, u64)>,
Expand Down Expand Up @@ -490,13 +590,15 @@ impl Btf {
// We need to get the size of the section from the ELF file
// Fortunately, we cached these when parsing it initially
// and we can this up by name in section_infos
let (_, size) = section_infos.get(&name).ok_or_else(|| {
BtfError::UnknownSectionSize {

if let Some((_, size)) = section_infos.get(&name) {
debug!("{} {}: fixup size to {}", kind, name, size);
fixed_ty.size = *size as u32;
} else if name != ".kconfig" && name != ".ksyms" {
return Err(BtfError::UnknownSectionSize {
section_name: name.clone(),
}
})?;
debug!("{} {}: fixup size to {}", kind, name, size);
fixed_ty.size = *size as u32;
});
};

// The Vec<btf_var_secinfo> contains BTF_KIND_VAR sections
// that need to have their offsets adjusted. To do this,
Expand All @@ -522,6 +624,14 @@ impl Btf {
},
)?;
d.offset = *offset as u32;

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

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

debug!(
"{} {}: {} {}: fixup offset {}",
kind, name, var_kind, var_name, offset
Expand Down Expand Up @@ -626,6 +736,108 @@ 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 mut empty_data = Vec::new();
empty_data.resize(type_size, 0u8);

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: BpfSectionKind::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 @@ -1112,11 +1112,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
8 changes: 8 additions & 0 deletions aya-obj/src/obj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ pub struct Features {
pub bpf_probe_read_kernel: bool,
pub bpf_perf_link: bool,
pub bpf_global_data: bool,
pub bpf_cookie: bool,
pub bpf_syscall_wrapper: bool,
pub btf: Option<BtfFeatures>,
}

Expand Down Expand Up @@ -546,6 +548,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 @@ -1487,6 +1491,8 @@ mod tests {
address,
size,
is_definition: false,
is_external: false,
is_weak: false,
kind: SymbolKind::Data,
},
);
Expand Down Expand Up @@ -2349,6 +2355,8 @@ mod tests {
address: 0,
size: 3,
is_definition: true,
is_external: false,
is_weak: false,
kind: SymbolKind::Data,
},
);
Expand Down
4 changes: 4 additions & 0 deletions aya-obj/src/relocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,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 @@ -530,6 +532,8 @@ mod test {
address,
size,
is_definition: false,
is_external: false,
is_weak: false,
kind: SymbolKind::Data,
}
}
Expand Down
1 change: 1 addition & 0 deletions aya/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ parking_lot = { version = "0.12.0", features = ["send_guard"] }
tokio = { version = "1.24.0", features = ["macros", "rt", "rt-multi-thread", "net"], optional = true }
async-io = { version = "1.3", optional = true }
log = "0.4"
flate2 = "1.0"

[dev-dependencies]
matches = "0.1.8"
Expand Down
Loading

0 comments on commit 5cb711c

Please sign in to comment.