aya-obj: add no_std feature

The crate has few libstd dependencies. Since it should be platform-
independent in principle, making it no_std like the object crate would
seem reasonable.

However, the feature `error_in_core` is not yet stabilized, and the
thiserror crate currently offers no no_std support. When the feature
no_std is selected, we enable the `error_in_core` feature, switch to
thiserror-core and replace the HashMap with the one in hashbrown.
pull/475/head
Shenghui Ye 2 years ago
parent 311ead6760
commit 30f1fabc05

@ -13,8 +13,15 @@ edition = "2021"
[dependencies] [dependencies]
bytes = "1" bytes = "1"
log = "0.4" log = "0.4"
object = { version = "0.30", default-features = false, features = ["std", "read_core", "elf"] } object = { version = "0.30", default-features = false, features = ["read_core", "elf"] }
thiserror = "1" hashbrown = { version = "0.13", optional = true }
thiserror-std = { package = "thiserror", version = "1" }
thiserror-core = { version = "1", default-features = false, features = [], optional = true }
[dev-dependencies] [dev-dependencies]
matches = "0.1.8" matches = "0.1.8"
rbpf = "0.1.0"
[features]
default = []
no_std = ["hashbrown", "thiserror-core"]

@ -17,11 +17,11 @@ object files.
This example loads a simple eBPF program and runs it with [rbpf]. This example loads a simple eBPF program and runs it with [rbpf].
```rust ```rust
use aya_bpf::Object; use aya_obj::{generated::bpf_insn, Object};
// Parse the object file // Parse the object file
let bytes = std::fs::read("program.o").unwrap(); let bytes = std::fs::read("program.o").unwrap();
let mut object = Object::parse(bytes).unwrap(); let mut object = Object::parse(&bytes).unwrap();
// Relocate the programs // Relocate the programs
object.relocate_calls().unwrap(); object.relocate_calls().unwrap();
object.relocate_maps(std::iter::empty()).unwrap(); object.relocate_maps(std::iter::empty()).unwrap();
@ -30,9 +30,9 @@ object.relocate_maps(std::iter::empty()).unwrap();
let program = object.programs.iter().next().unwrap().1; let program = object.programs.iter().next().unwrap().1;
let instructions = &program.function.instructions; let instructions = &program.function.instructions;
let data = unsafe { let data = unsafe {
from_raw_parts( core::slice::from_raw_parts(
instructions.as_ptr() as *const u8, instructions.as_ptr() as *const u8,
instructions.len() * size_of::<bpf_insn>(), instructions.len() * core::mem::size_of::<bpf_insn>(),
) )
}; };
let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap(); let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();

@ -1,18 +1,17 @@
use std::{ use core::{ffi::CStr, mem, ptr};
use alloc::{
borrow::Cow, borrow::Cow,
collections::HashMap, ffi::CString,
convert::TryInto, format,
ffi::{CStr, CString}, string::{String, ToString},
fs, io, mem, vec,
path::{Path, PathBuf}, vec::Vec,
ptr,
}; };
use bytes::BufMut; use bytes::BufMut;
use log::debug; use log::debug;
use object::Endianness; use object::Endianness;
use thiserror::Error;
use crate::{ use crate::{
btf::{ btf::{
@ -22,7 +21,8 @@ use crate::{
IntEncoding, LineInfo, Struct, Typedef, VarLinkage, IntEncoding, LineInfo, Struct, Typedef, VarLinkage,
}, },
generated::{btf_ext_header, btf_header}, generated::{btf_ext_header, btf_header},
util::bytes_of, thiserror::{self, Error},
util::{bytes_of, HashMap},
Object, Object,
}; };
@ -32,14 +32,15 @@ pub(crate) const MAX_SPEC_LEN: usize = 64;
/// The error type returned when `BTF` operations fail. /// The error type returned when `BTF` operations fail.
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum BtfError { pub enum BtfError {
#[cfg(not(feature = "no_std"))]
/// Error parsing file /// Error parsing file
#[error("error parsing {path}")] #[error("error parsing {path}")]
FileError { FileError {
/// file path /// file path
path: PathBuf, path: std::path::PathBuf,
/// source of the error /// source of the error
#[source] #[source]
error: io::Error, error: std::io::Error,
}, },
/// Error parsing BTF header /// Error parsing BTF header
@ -125,12 +126,13 @@ pub enum BtfError {
type_id: u32, type_id: u32,
}, },
#[cfg(not(feature = "no_std"))]
/// Loading the btf failed /// Loading the btf failed
#[error("the BPF_BTF_LOAD syscall failed. Verifier output: {verifier_log}")] #[error("the BPF_BTF_LOAD syscall failed. Verifier output: {verifier_log}")]
LoadError { LoadError {
/// The [`io::Error`] returned by the `BPF_BTF_LOAD` syscall. /// The [`io::Error`] returned by the `BPF_BTF_LOAD` syscall.
#[source] #[source]
io_error: io::Error, io_error: std::io::Error,
/// The error log produced by the kernel verifier. /// The error log produced by the kernel verifier.
verifier_log: String, verifier_log: String,
}, },
@ -230,12 +232,18 @@ impl Btf {
} }
/// Loads BTF metadata from `/sys/kernel/btf/vmlinux`. /// Loads BTF metadata from `/sys/kernel/btf/vmlinux`.
#[cfg(not(feature = "no_std"))]
pub fn from_sys_fs() -> Result<Btf, BtfError> { pub fn from_sys_fs() -> Result<Btf, BtfError> {
Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default()) Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default())
} }
/// Loads BTF metadata from the given `path`. /// Loads BTF metadata from the given `path`.
pub fn parse_file<P: AsRef<Path>>(path: P, endianness: Endianness) -> Result<Btf, BtfError> { #[cfg(not(feature = "no_std"))]
pub fn parse_file<P: AsRef<std::path::Path>>(
path: P,
endianness: Endianness,
) -> Result<Btf, BtfError> {
use std::{borrow::ToOwned, fs};
let path = path.as_ref(); let path = path.as_ref();
Btf::parse( Btf::parse(
&fs::read(path).map_err(|error| BtfError::FileError { &fs::read(path).map_err(|error| BtfError::FileError {
@ -1436,6 +1444,7 @@ mod tests {
} }
#[test] #[test]
#[cfg(not(feature = "no_std"))]
#[cfg_attr(miri, ignore)] #[cfg_attr(miri, ignore)]
fn test_read_btf_from_sys_fs() { fn test_read_btf_from_sys_fs() {
let btf = Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default()).unwrap(); let btf = Btf::parse_file("/sys/kernel/btf/vmlinux", Endianness::default()).unwrap();

@ -1,12 +1,11 @@
use std::collections::HashMap; use alloc::{string::String, vec, vec::Vec};
use bytes::BufMut; use bytes::BufMut;
use object::Endianness; use object::Endianness;
use crate::{ use crate::{
generated::{bpf_func_info, bpf_line_info}, generated::{bpf_func_info, bpf_line_info},
relocation::INS_SIZE, relocation::INS_SIZE,
util::bytes_of, util::{bytes_of, HashMap},
}; };
/* The func_info subsection layout: /* The func_info subsection layout:

@ -1,6 +1,12 @@
use std::{collections::HashMap, io, mem, ptr, str::FromStr}; use core::{mem, ptr, str::FromStr};
use thiserror::Error; use alloc::{
borrow::ToOwned,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use crate::{ use crate::{
btf::{ btf::{
@ -11,6 +17,8 @@ use crate::{
bpf_core_relo, bpf_core_relo_kind::*, bpf_insn, BPF_ALU, BPF_ALU64, BPF_B, BPF_DW, BPF_H, bpf_core_relo, bpf_core_relo_kind::*, bpf_insn, BPF_ALU, BPF_ALU64, BPF_B, BPF_DW, BPF_H,
BPF_K, BPF_LD, BPF_LDX, BPF_ST, BPF_STX, BPF_W, BTF_INT_SIGNED, BPF_K, BPF_LD, BPF_LDX, BPF_ST, BPF_STX, BPF_W, BTF_INT_SIGNED,
}, },
thiserror::{self, Error},
util::HashMap,
Object, Program, ProgramSection, Object, Program, ProgramSection,
}; };
@ -28,9 +36,10 @@ pub struct BtfRelocationError {
/// Relocation failures /// Relocation failures
#[derive(Error, Debug)] #[derive(Error, Debug)]
enum RelocationError { enum RelocationError {
#[cfg(not(feature = "no_std"))]
/// I/O error /// I/O error
#[error(transparent)] #[error(transparent)]
IOError(#[from] io::Error), IOError(#[from] std::io::Error),
/// Program not found /// Program not found
#[error("program not found")] #[error("program not found")]
@ -254,7 +263,7 @@ fn relocate_btf_program<'target>(
) -> Result<(), RelocationError> { ) -> Result<(), RelocationError> {
for rel in relos { for rel in relos {
let instructions = &mut program.function.instructions; let instructions = &mut program.function.instructions;
let ins_index = rel.ins_offset / std::mem::size_of::<bpf_insn>(); let ins_index = rel.ins_offset / mem::size_of::<bpf_insn>();
if ins_index >= instructions.len() { if ins_index >= instructions.len() {
return Err(RelocationError::InvalidInstructionIndex { return Err(RelocationError::InvalidInstructionIndex {
index: ins_index, index: ins_index,
@ -817,7 +826,7 @@ impl ComputedRelocation {
) -> Result<(), RelocationError> { ) -> Result<(), RelocationError> {
let instructions = &mut program.function.instructions; let instructions = &mut program.function.instructions;
let num_instructions = instructions.len(); let num_instructions = instructions.len();
let ins_index = rel.ins_offset / std::mem::size_of::<bpf_insn>(); let ins_index = rel.ins_offset / mem::size_of::<bpf_insn>();
let mut ins = let mut ins =
instructions instructions
.get_mut(ins_index) .get_mut(ins_index)
@ -845,7 +854,7 @@ impl ComputedRelocation {
ins.imm = target_value as i32; ins.imm = target_value as i32;
} }
BPF_LDX | BPF_ST | BPF_STX => { BPF_LDX | BPF_ST | BPF_STX => {
if target_value > std::i16::MAX as u32 { if target_value > i16::MAX as u32 {
return Err(RelocationError::InvalidInstruction { return Err(RelocationError::InvalidInstruction {
relocation_number: rel.number, relocation_number: rel.number,
index: ins_index, index: ins_index,

@ -1,7 +1,8 @@
#![allow(missing_docs)] #![allow(missing_docs)]
use std::{fmt::Display, mem, ptr}; use core::{fmt::Display, mem, ptr};
use alloc::{string::ToString, vec, vec::Vec};
use object::Endianness; use object::Endianness;
use crate::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH}; use crate::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH};
@ -853,7 +854,7 @@ impl TryFrom<u32> for BtfKind {
} }
impl Display for BtfKind { impl Display for BtfKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self { match self {
BtfKind::Unknown => write!(f, "[UNKNOWN]"), BtfKind::Unknown => write!(f, "[UNKNOWN]"),
BtfKind::Int => write!(f, "[INT]"), BtfKind::Int => write!(f, "[INT]"),

@ -17,11 +17,11 @@
//! This example loads a simple eBPF program and runs it with [rbpf]. //! This example loads a simple eBPF program and runs it with [rbpf].
//! //!
//! ```no_run //! ```no_run
//! use aya_bpf::Object; //! use aya_obj::{generated::bpf_insn, Object};
//! //!
//! // Parse the object file //! // Parse the object file
//! let bytes = std::fs::read("program.o").unwrap(); //! let bytes = std::fs::read("program.o").unwrap();
//! let mut object = Object::parse(bytes).unwrap(); //! let mut object = Object::parse(&bytes).unwrap();
//! // Relocate the programs //! // Relocate the programs
//! object.relocate_calls().unwrap(); //! object.relocate_calls().unwrap();
//! object.relocate_maps(std::iter::empty()).unwrap(); //! object.relocate_maps(std::iter::empty()).unwrap();
@ -30,9 +30,9 @@
//! let program = object.programs.iter().next().unwrap().1; //! let program = object.programs.iter().next().unwrap().1;
//! let instructions = &program.function.instructions; //! let instructions = &program.function.instructions;
//! let data = unsafe { //! let data = unsafe {
//! from_raw_parts( //! core::slice::from_raw_parts(
//! instructions.as_ptr() as *const u8, //! instructions.as_ptr() as *const u8,
//! instructions.len() * size_of::<bpf_insn>(), //! instructions.len() * core::mem::size_of::<bpf_insn>(),
//! ) //! )
//! }; //! };
//! let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap(); //! let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();
@ -41,6 +41,7 @@
//! //!
//! [rbpf]: https://github.com/qmonnet/rbpf //! [rbpf]: https://github.com/qmonnet/rbpf
#![no_std]
#![doc( #![doc(
html_logo_url = "https://aya-rs.dev/assets/images/crabby.svg", html_logo_url = "https://aya-rs.dev/assets/images/crabby.svg",
html_favicon_url = "https://aya-rs.dev/assets/images/crabby.svg" html_favicon_url = "https://aya-rs.dev/assets/images/crabby.svg"
@ -49,6 +50,17 @@
#![deny(clippy::all, missing_docs)] #![deny(clippy::all, missing_docs)]
#![allow(clippy::missing_safety_doc, clippy::len_without_is_empty)] #![allow(clippy::missing_safety_doc, clippy::len_without_is_empty)]
#![cfg_attr(feature = "no_std", feature(error_in_core))]
#[cfg(not(feature = "no_std"))]
pub(crate) use thiserror_std as thiserror;
#[cfg(feature = "no_std")]
pub(crate) use thiserror_core as thiserror;
extern crate alloc;
#[cfg(not(feature = "no_std"))]
extern crate std;
pub mod btf; pub mod btf;
pub mod generated; pub mod generated;
pub mod maps; pub mod maps;

@ -2,7 +2,8 @@
use core::mem; use core::mem;
use thiserror::Error; use crate::thiserror::{self, Error};
use alloc::vec::Vec;
/// Invalid map type encontered /// Invalid map type encontered
pub struct InvalidMapTypeError { pub struct InvalidMapTypeError {

@ -1,22 +1,24 @@
//! Object file loading, parsing, and relocation. //! Object file loading, parsing, and relocation.
use alloc::{
borrow::ToOwned,
ffi::CString,
string::{String, ToString},
vec::Vec,
};
use core::{ffi::CStr, mem, ptr, str::FromStr};
use log::debug; use log::debug;
use object::{ use object::{
read::{Object as ElfObject, ObjectSection, Section as ObjSection}, read::{Object as ElfObject, ObjectSection, Section as ObjSection},
Endianness, ObjectSymbol, ObjectSymbolTable, RelocationTarget, SectionIndex, SectionKind, Endianness, ObjectSymbol, ObjectSymbolTable, RelocationTarget, SectionIndex, SectionKind,
SymbolKind, SymbolKind,
}; };
use std::{
collections::HashMap,
ffi::{CStr, CString},
mem, ptr,
str::FromStr,
};
use thiserror::Error;
use crate::{ use crate::{
maps::{BtfMap, LegacyMap, Map, MapKind, MINIMUM_MAP_SIZE}, maps::{BtfMap, LegacyMap, Map, MapKind, MINIMUM_MAP_SIZE},
relocation::*, relocation::*,
thiserror::{self, Error},
util::HashMap,
}; };
use crate::{ use crate::{
@ -25,7 +27,7 @@ use crate::{
maps::{bpf_map_def, BtfMapDef, PinningType}, maps::{bpf_map_def, BtfMapDef, PinningType},
programs::{CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType}, programs::{CgroupSockAddrAttachType, CgroupSockAttachType, CgroupSockoptAttachType},
}; };
use std::slice::from_raw_parts_mut; use core::slice::from_raw_parts_mut;
use crate::btf::{Array, DataSecEntry, FuncSecInfo, LineSecInfo}; use crate::btf::{Array, DataSecEntry, FuncSecInfo, LineSecInfo};
@ -844,7 +846,7 @@ impl Object {
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum ParseError { pub enum ParseError {
#[error("error parsing ELF data")] #[error("error parsing ELF data")]
ElfError(#[from] object::read::Error), ElfError(object::read::Error),
/// Error parsing BTF object /// Error parsing BTF object
#[error("BTF error")] #[error("BTF error")]
@ -862,8 +864,7 @@ pub enum ParseError {
#[error("error parsing section with index {index}")] #[error("error parsing section with index {index}")]
SectionError { SectionError {
index: usize, index: usize,
#[source] error: object::read::Error,
source: object::read::Error,
}, },
#[error("unsupported relocation target")] #[error("unsupported relocation target")]
@ -968,9 +969,9 @@ impl<'data, 'file, 'a> TryFrom<&'a ObjSection<'data, 'file>> for Section<'a> {
fn try_from(section: &'a ObjSection) -> Result<Section<'a>, ParseError> { fn try_from(section: &'a ObjSection) -> Result<Section<'a>, ParseError> {
let index = section.index(); let index = section.index();
let map_err = |source| ParseError::SectionError { let map_err = |error| ParseError::SectionError {
index: index.0, index: index.0,
source, error,
}; };
let name = section.name().map_err(map_err)?; let name = section.name().map_err(map_err)?;
let kind = match BpfSectionKind::from_name(name) { let kind = match BpfSectionKind::from_name(name) {
@ -1272,6 +1273,7 @@ pub fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use alloc::vec;
use matches::assert_matches; use matches::assert_matches;
use object::Endianness; use object::Endianness;

@ -1,7 +1,10 @@
//! Cgroup socket programs. //! Cgroup socket programs.
use thiserror::Error; use alloc::{borrow::ToOwned, string::String};
use crate::generated::bpf_attach_type; use crate::{
generated::bpf_attach_type,
thiserror::{self, Error},
};
/// Defines where to attach a `CgroupSock` program. /// Defines where to attach a `CgroupSock` program.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]

@ -1,7 +1,10 @@
//! Cgroup socket address programs. //! Cgroup socket address programs.
use thiserror::Error; use alloc::{borrow::ToOwned, string::String};
use crate::generated::bpf_attach_type; use crate::{
generated::bpf_attach_type,
thiserror::{self, Error},
};
/// Defines where to attach a `CgroupSockAddr` program. /// Defines where to attach a `CgroupSockAddr` program.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]

@ -1,7 +1,10 @@
//! Cgroup socket option programs. //! Cgroup socket option programs.
use thiserror::Error; use alloc::{borrow::ToOwned, string::String};
use crate::generated::bpf_attach_type; use crate::{
generated::bpf_attach_type,
thiserror::{self, Error},
};
/// Defines where to attach a `CgroupSockopt` program. /// Defines where to attach a `CgroupSockopt` program.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]

@ -1,10 +1,10 @@
//! Program relocation handling. //! Program relocation handling.
use std::{collections::HashMap, mem}; use core::mem;
use alloc::{borrow::ToOwned, string::String};
use log::debug; use log::debug;
use object::{SectionIndex, SymbolKind}; use object::{SectionIndex, SymbolKind};
use thiserror::Error;
use crate::{ use crate::{
generated::{ generated::{
@ -13,6 +13,8 @@ use crate::{
}, },
maps::Map, maps::Map,
obj::{Function, Object, Program}, obj::{Function, Object, Program},
thiserror::{self, Error},
util::HashMap,
}; };
pub(crate) const INS_SIZE: usize = mem::size_of::<bpf_insn>(); pub(crate) const INS_SIZE: usize = mem::size_of::<bpf_insn>();
@ -472,6 +474,8 @@ fn insn_is_call(ins: &bpf_insn) -> bool {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use alloc::{string::ToString, vec, vec::Vec};
use crate::maps::{bpf_map_def, BtfMap, BtfMapDef, LegacyMap, Map, MapKind}; use crate::maps::{bpf_map_def, BtfMap, BtfMapDef, LegacyMap, Map, MapKind};
use super::*; use super::*;
@ -489,7 +493,7 @@ mod test {
} }
fn ins(bytes: &[u8]) -> bpf_insn { fn ins(bytes: &[u8]) -> bpf_insn {
unsafe { std::ptr::read_unaligned(bytes.as_ptr() as *const _) } unsafe { core::ptr::read_unaligned(bytes.as_ptr() as *const _) }
} }
fn fake_legacy_map(symbol_index: usize) -> Map { fn fake_legacy_map(symbol_index: usize) -> Map {

@ -1,5 +1,10 @@
use core::{mem, slice}; use core::{mem, slice};
#[cfg(feature = "no_std")]
pub(crate) use hashbrown::HashMap;
#[cfg(not(feature = "no_std"))]
pub(crate) use std::collections::HashMap;
/// bytes_of converts a <T> to a byte slice /// bytes_of converts a <T> to a byte slice
pub(crate) unsafe fn bytes_of<T>(val: &T) -> &[u8] { pub(crate) unsafe fn bytes_of<T>(val: &T) -> &[u8] {
let size = mem::size_of::<T>(); let size = mem::size_of::<T>();

Loading…
Cancel
Save