Browse Source

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.
Shenghui Ye 2 years ago
parent
commit
30f1fabc05

+ 9 - 2
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"]

+ 4 - 4
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::<bpf_insn>(),
+        instructions.len() * core::mem::size_of::<bpf_insn>(),
     )
 };
 let vm = rbpf::EbpfVmNoData::new(Some(data)).unwrap();

+ 23 - 14
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, 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();

+ 2 - 3
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:

+ 16 - 7
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::<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,

+ 3 - 2
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<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]"),

+ 16 - 4
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::<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 - 1
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 {

+ 15 - 13
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<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;
 

+ 5 - 2
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)]

+ 5 - 2
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)]

+ 5 - 2
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)]

+ 7 - 3
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::<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 {

+ 5 - 0
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 <T> to a byte slice
 pub(crate) unsafe fn bytes_of<T>(val: &T) -> &[u8] {
     let size = mem::size_of::<T>();