From 30f1fabc05654e8d11dd2648767895123c141c3b Mon Sep 17 00:00:00 2001 From: Shenghui Ye Date: Wed, 28 Dec 2022 15:29:00 +0800 Subject: [PATCH] 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. --- aya-obj/Cargo.toml | 11 +++++-- aya-obj/README.md | 8 ++--- aya-obj/src/btf/btf.rs | 37 +++++++++++++++--------- aya-obj/src/btf/info.rs | 5 ++-- aya-obj/src/btf/relocation.rs | 23 ++++++++++----- aya-obj/src/btf/types.rs | 5 ++-- aya-obj/src/lib.rs | 20 ++++++++++--- aya-obj/src/maps.rs | 3 +- aya-obj/src/obj.rs | 28 +++++++++--------- aya-obj/src/programs/cgroup_sock.rs | 7 +++-- aya-obj/src/programs/cgroup_sock_addr.rs | 7 +++-- aya-obj/src/programs/cgroup_sockopt.rs | 7 +++-- aya-obj/src/relocation.rs | 10 +++++-- aya-obj/src/util.rs | 5 ++++ 14 files changed, 117 insertions(+), 59 deletions(-) diff --git a/aya-obj/Cargo.toml b/aya-obj/Cargo.toml index 0dc1e2b6..f1bf4e9c 100644 --- a/aya-obj/Cargo.toml +++ b/aya-obj/Cargo.toml @@ -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"] diff --git a/aya-obj/README.md b/aya-obj/README.md index 29037565..62450ff8 100644 --- a/aya-obj/README.md +++ b/aya-obj/README.md @@ -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::(), + instructions.len() * core::mem::size_of::(), ) }; let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap(); diff --git a/aya-obj/src/btf/btf.rs b/aya-obj/src/btf/btf.rs index 193e9528..a5fbcd1a 100644 --- a/aya-obj/src/btf/btf.rs +++ b/aya-obj/src/btf/btf.rs @@ -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::parse_file("/sys/kernel/btf/vmlinux", Endianness::default()) } /// Loads BTF metadata from the given `path`. - pub fn parse_file>(path: P, endianness: Endianness) -> Result { + #[cfg(not(feature = "no_std"))] + pub fn parse_file>( + path: P, + endianness: Endianness, + ) -> Result { + 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(); diff --git a/aya-obj/src/btf/info.rs b/aya-obj/src/btf/info.rs index 5d1652c4..97f5b866 100644 --- a/aya-obj/src/btf/info.rs +++ b/aya-obj/src/btf/info.rs @@ -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: diff --git a/aya-obj/src/btf/relocation.rs b/aya-obj/src/btf/relocation.rs index 641e7265..ec1bcde7 100644 --- a/aya-obj/src/btf/relocation.rs +++ b/aya-obj/src/btf/relocation.rs @@ -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::(); + let ins_index = rel.ins_offset / mem::size_of::(); 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::(); + let ins_index = rel.ins_offset / mem::size_of::(); 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, diff --git a/aya-obj/src/btf/types.rs b/aya-obj/src/btf/types.rs index 2f5cfc1e..177b97a5 100644 --- a/aya-obj/src/btf/types.rs +++ b/aya-obj/src/btf/types.rs @@ -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 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]"), diff --git a/aya-obj/src/lib.rs b/aya-obj/src/lib.rs index 45a781a1..120ae047 100644 --- a/aya-obj/src/lib.rs +++ b/aya-obj/src/lib.rs @@ -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::(), +//! instructions.len() * core::mem::size_of::(), //! ) //! }; //! 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; diff --git a/aya-obj/src/maps.rs b/aya-obj/src/maps.rs index b54f3bbb..b6467d94 100644 --- a/aya-obj/src/maps.rs +++ b/aya-obj/src/maps.rs @@ -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 { diff --git a/aya-obj/src/obj.rs b/aya-obj/src/obj.rs index cf4cb7a7..5c01c815 100644 --- a/aya-obj/src/obj.rs +++ b/aya-obj/src/obj.rs @@ -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, 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, ParseError> { #[cfg(test)] mod tests { + use alloc::vec; use matches::assert_matches; use object::Endianness; diff --git a/aya-obj/src/programs/cgroup_sock.rs b/aya-obj/src/programs/cgroup_sock.rs index 59cc254e..ed470f8a 100644 --- a/aya-obj/src/programs/cgroup_sock.rs +++ b/aya-obj/src/programs/cgroup_sock.rs @@ -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)] diff --git a/aya-obj/src/programs/cgroup_sock_addr.rs b/aya-obj/src/programs/cgroup_sock_addr.rs index 73ee00a2..39db0ae3 100644 --- a/aya-obj/src/programs/cgroup_sock_addr.rs +++ b/aya-obj/src/programs/cgroup_sock_addr.rs @@ -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)] diff --git a/aya-obj/src/programs/cgroup_sockopt.rs b/aya-obj/src/programs/cgroup_sockopt.rs index f85f52f1..b48e984a 100644 --- a/aya-obj/src/programs/cgroup_sockopt.rs +++ b/aya-obj/src/programs/cgroup_sockopt.rs @@ -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)] diff --git a/aya-obj/src/relocation.rs b/aya-obj/src/relocation.rs index 25d5c1f9..8b0b9170 100644 --- a/aya-obj/src/relocation.rs +++ b/aya-obj/src/relocation.rs @@ -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::(); @@ -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 { diff --git a/aya-obj/src/util.rs b/aya-obj/src/util.rs index 4aa177bb..36355a18 100644 --- a/aya-obj/src/util.rs +++ b/aya-obj/src/util.rs @@ -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 to a byte slice pub(crate) unsafe fn bytes_of(val: &T) -> &[u8] { let size = mem::size_of::();