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]
bytes = "1"
log = "0.4"
object = { version = "0.30", default-features = false, features = ["std", "read_core", "elf"] }
thiserror = "1"
object = { version = "0.30", default-features = false, features = ["read_core", "elf"] }
hashbrown = { version = "0.13", optional = true }
thiserror-std = { package = "thiserror", version = "1" }
thiserror-core = { version = "1", default-features = false, features = [], optional = true }
[dev-dependencies]
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].
```rust
use aya_bpf::Object;
use aya_obj::{generated::bpf_insn, Object};
// Parse the object file
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
object.relocate_calls().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 instructions = &program.function.instructions;
let data = unsafe {
from_raw_parts(
core::slice::from_raw_parts(
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();

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

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

@ -1,7 +1,8 @@
#![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 crate::btf::{Btf, BtfError, MAX_RESOLVE_DEPTH};
@ -853,7 +854,7 @@ impl TryFrom<u32> 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 {
BtfKind::Unknown => write!(f, "[UNKNOWN]"),
BtfKind::Int => write!(f, "[INT]"),

@ -17,11 +17,11 @@
//! This example loads a simple eBPF program and runs it with [rbpf].
//!
//! ```no_run
//! use aya_bpf::Object;
//! use aya_obj::{generated::bpf_insn, Object};
//!
//! // Parse the object file
//! 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
//! object.relocate_calls().unwrap();
//! object.relocate_maps(std::iter::empty()).unwrap();
@ -30,9 +30,9 @@
//! let program = object.programs.iter().next().unwrap().1;
//! let instructions = &program.function.instructions;
//! let data = unsafe {
//! from_raw_parts(
//! core::slice::from_raw_parts(
//! 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();
@ -41,6 +41,7 @@
//!
//! [rbpf]: https://github.com/qmonnet/rbpf
#![no_std]
#![doc(
html_logo_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)]
#![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 generated;
pub mod maps;

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

@ -1,22 +1,24 @@
//! 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 object::{
read::{Object as ElfObject, ObjectSection, Section as ObjSection},
Endianness, ObjectSymbol, ObjectSymbolTable, RelocationTarget, SectionIndex, SectionKind,
SymbolKind,
};
use std::{
collections::HashMap,
ffi::{CStr, CString},
mem, ptr,
str::FromStr,
};
use thiserror::Error;
use crate::{
maps::{BtfMap, LegacyMap, Map, MapKind, MINIMUM_MAP_SIZE},
relocation::*,
thiserror::{self, Error},
util::HashMap,
};
use crate::{
@ -25,7 +27,7 @@ use crate::{
maps::{bpf_map_def, BtfMapDef, PinningType},
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};
@ -844,7 +846,7 @@ impl Object {
#[allow(missing_docs)]
pub enum ParseError {
#[error("error parsing ELF data")]
ElfError(#[from] object::read::Error),
ElfError(object::read::Error),
/// Error parsing BTF object
#[error("BTF error")]
@ -862,8 +864,7 @@ pub enum ParseError {
#[error("error parsing section with index {index}")]
SectionError {
index: usize,
#[source]
source: object::read::Error,
error: object::read::Error,
},
#[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> {
let index = section.index();
let map_err = |source| ParseError::SectionError {
let map_err = |error| ParseError::SectionError {
index: index.0,
source,
error,
};
let name = section.name().map_err(map_err)?;
let kind = match BpfSectionKind::from_name(name) {
@ -1272,6 +1273,7 @@ pub fn copy_instructions(data: &[u8]) -> Result<Vec<bpf_insn>, ParseError> {
#[cfg(test)]
mod tests {
use alloc::vec;
use matches::assert_matches;
use object::Endianness;

@ -1,7 +1,10 @@
//! 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.
#[derive(Copy, Clone, Debug)]

@ -1,7 +1,10 @@
//! 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.
#[derive(Copy, Clone, Debug)]

@ -1,7 +1,10 @@
//! 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.
#[derive(Copy, Clone, Debug)]

@ -1,10 +1,10 @@
//! Program relocation handling.
use std::{collections::HashMap, mem};
use core::mem;
use alloc::{borrow::ToOwned, string::String};
use log::debug;
use object::{SectionIndex, SymbolKind};
use thiserror::Error;
use crate::{
generated::{
@ -13,6 +13,8 @@ use crate::{
},
maps::Map,
obj::{Function, Object, Program},
thiserror::{self, Error},
util::HashMap,
};
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)]
mod test {
use alloc::{string::ToString, vec, vec::Vec};
use crate::maps::{bpf_map_def, BtfMap, BtfMapDef, LegacyMap, Map, MapKind};
use super::*;
@ -489,7 +493,7 @@ mod test {
}
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 {

@ -1,5 +1,10 @@
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
pub(crate) unsafe fn bytes_of<T>(val: &T) -> &[u8] {
let size = mem::size_of::<T>();

Loading…
Cancel
Save