diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e985ad1e..dade358b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -103,6 +103,8 @@ jobs: steps: - uses: actions/checkout@v2 - run: cargo test --no-default-features + # Ensure gimli can be built without alloc. + - run: cargo check --no-default-features --features read-core - run: cargo test --no-default-features --features read - run: cargo test --no-default-features --features read,fallible-iterator - run: cargo test --no-default-features --features read,std diff --git a/Cargo.toml b/Cargo.toml index c41f3858..9f82433f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,8 +38,9 @@ test-assembler = "0.1.3" typed-arena = "2" [features] -read = [] -endian-reader = ["stable_deref_trait"] +read-core = [] +read = ["read-core"] +endian-reader = ["read", "stable_deref_trait"] write = ["indexmap"] std = ["fallible-iterator/std", "stable_deref_trait/std"] default = ["read", "write", "std", "fallible-iterator", "endian-reader"] diff --git a/src/constants.rs b/src/constants.rs index e393dd2e..96847984 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -81,9 +81,9 @@ macro_rules! dw { if let Some(s) = self.static_string() { f.pad(s) } else { - f.pad(&format!("Unknown {}: {}", + write!(f, "Unknown {}: {}", stringify!($struct_name), - self.0)) + self.0) } } } diff --git a/src/leb128.rs b/src/leb128.rs index b662ce1f..4fe185c4 100644 --- a/src/leb128.rs +++ b/src/leb128.rs @@ -45,7 +45,7 @@ //! ``` const CONTINUATION_BIT: u8 = 1 << 7; -#[cfg(feature = "read")] +#[cfg(feature = "read-core")] const SIGN_BIT: u8 = 1 << 6; #[inline] @@ -62,7 +62,7 @@ fn low_bits_of_u64(val: u64) -> u8 { /// A module for reading signed and unsigned integers that have been LEB128 /// encoded. -#[cfg(feature = "read")] +#[cfg(feature = "read-core")] pub mod read { use super::{low_bits_of_byte, CONTINUATION_BIT, SIGN_BIT}; use crate::read::{Error, Reader, Result}; diff --git a/src/lib.rs b/src/lib.rs index 4d4eef82..7ac1820e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,6 +37,7 @@ #![no_std] #[allow(unused_imports)] +#[cfg(any(feature = "read", feature = "write"))] #[macro_use] extern crate alloc; @@ -62,10 +63,10 @@ pub use crate::endianity::{BigEndian, Endianity, LittleEndian, NativeEndian, Run pub mod leb128; -#[cfg(feature = "read")] +#[cfg(feature = "read-core")] pub mod read; // For backwards compat. -#[cfg(feature = "read")] +#[cfg(feature = "read-core")] pub use crate::read::*; #[cfg(feature = "write")] diff --git a/src/read/cfi.rs b/src/read/cfi.rs index c715ecfd..61d39d5d 100644 --- a/src/read/cfi.rs +++ b/src/read/cfi.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "read")] use alloc::vec::Vec; use core::cmp::{Ord, Ordering}; @@ -10,9 +11,9 @@ use super::util::{ArrayLike, ArrayVec}; use crate::common::{DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId}; use crate::constants::{self, DwEhPe}; use crate::endianity::Endianity; -use crate::read::{ - EndianSlice, Error, Expression, Reader, ReaderOffset, Result, Section, StoreOnHeap, -}; +#[cfg(feature = "read")] +use crate::read::StoreOnHeap; +use crate::read::{EndianSlice, Error, Expression, Reader, ReaderOffset, Result, Section}; /// `DebugFrame` contains the `.debug_frame` section's frame unwinding /// information required to unwind to and recover registers from older frames on @@ -1738,8 +1739,13 @@ impl FrameDescriptionEntry { /// Specification of what storage should be used for [`UnwindContext`]. /// -/// Normally you would only need to use [`StoreOnHeap`], which places the stack -/// on the heap using [`Vec`]. This is the default storage type parameter for [`UnwindContext`]. +#[cfg_attr( + feature = "read", + doc = " +Normally you would only need to use [`StoreOnHeap`], which places the stack +on the heap using [`Vec`]. This is the default storage type parameter for [`UnwindContext`]. +" +)] /// /// If you need to avoid [`UnwindContext`] from allocating memory, e.g. for signal safety, /// you can provide you own storage specification: @@ -1780,8 +1786,10 @@ pub trait UnwindContextStorage: Sized { type Stack: ArrayLike>; } +#[cfg(feature = "read")] const MAX_RULES: usize = 192; +#[cfg(feature = "read")] impl UnwindContextStorage for StoreOnHeap { type Rules = [(Register, RegisterRule); MAX_RULES]; type Stack = Vec>; @@ -1814,7 +1822,11 @@ impl UnwindContextStorage for StoreOnHeap { /// # } /// ``` #[derive(Clone, PartialEq, Eq)] -pub struct UnwindContext = StoreOnHeap> { +pub struct UnwindContext< + R: Reader, + #[cfg(not(feature = "read"))] A: UnwindContextStorage, + #[cfg(feature = "read")] A: UnwindContextStorage = StoreOnHeap, +> { // Stack of rows. The last row is the row currently being built by the // program. There is always at least one row. The vast majority of CFI // programs will only ever have one row on the stack. @@ -1849,6 +1861,7 @@ impl> Default for UnwindContext { } } +#[cfg(feature = "read")] impl UnwindContext { /// Construct a new call frame unwinding context. pub fn new() -> Self { @@ -2036,7 +2049,13 @@ impl> UnwindContext { /// > recording just the differences starting at the beginning address of each /// > subroutine in the program. #[derive(Debug)] -pub struct UnwindTable<'a, 'ctx, R: Reader, A: UnwindContextStorage = StoreOnHeap> { +pub struct UnwindTable< + 'a, + 'ctx, + R: Reader, + #[cfg(not(feature = "read"))] A: UnwindContextStorage, + #[cfg(feature = "read")] A: UnwindContextStorage = StoreOnHeap, +> { code_alignment_factor: Wrapping, data_alignment_factor: Wrapping, next_start_address: u64, @@ -2347,7 +2366,11 @@ impl<'a, 'ctx, R: Reader, A: UnwindContextStorage> UnwindTable<'a, 'ctx, R, A // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-aarch64/dwarf-config.h#L32 // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-arm/dwarf-config.h#L31 // - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-mips/dwarf-config.h#L31 -struct RegisterRuleMap = StoreOnHeap> { +struct RegisterRuleMap< + R: Reader, + #[cfg(not(feature = "read"))] S: UnwindContextStorage, + #[cfg(feature = "read")] S: UnwindContextStorage = StoreOnHeap, +> { rules: ArrayVec, } @@ -2490,7 +2513,11 @@ impl<'iter, R: Reader> Iterator for RegisterRuleIter<'iter, R> { /// A row in the virtual unwind table that describes how to find the values of /// the registers in the *previous* frame for a range of PC addresses. #[derive(PartialEq, Eq)] -pub struct UnwindTableRow = StoreOnHeap> { +pub struct UnwindTableRow< + R: Reader, + #[cfg(not(feature = "read"))] S: UnwindContextStorage, + #[cfg(feature = "read")] S: UnwindContextStorage = StoreOnHeap, +> { start_address: u64, end_address: u64, saved_args_size: u64, diff --git a/src/read/endian_slice.rs b/src/read/endian_slice.rs index a07855f0..05262cde 100644 --- a/src/read/endian_slice.rs +++ b/src/read/endian_slice.rs @@ -1,6 +1,8 @@ //! Working with byte slices that have an associated endianity. +#[cfg(feature = "read")] use alloc::borrow::Cow; +#[cfg(feature = "read")] use alloc::string::String; use core::ops::{Deref, Index, Range, RangeFrom, RangeTo}; use core::str; @@ -82,6 +84,7 @@ where /// Converts the slice to a string, including invalid characters, /// using `String::from_utf8_lossy`. + #[cfg(feature = "read")] #[inline] pub fn to_string_lossy(&self) -> Cow<'input, str> { String::from_utf8_lossy(self.slice) @@ -284,11 +287,18 @@ where Ok(EndianSlice::new(slice, self.endian)) } + #[cfg(not(feature = "read"))] + fn cannot_implement() -> super::reader::seal_if_no_alloc::Sealed { + super::reader::seal_if_no_alloc::Sealed + } + + #[cfg(feature = "read")] #[inline] fn to_slice(&self) -> Result> { Ok(self.slice.into()) } + #[cfg(feature = "read")] #[inline] fn to_string(&self) -> Result> { match str::from_utf8(self.slice) { @@ -297,6 +307,7 @@ where } } + #[cfg(feature = "read")] #[inline] fn to_string_lossy(&self) -> Result> { Ok(String::from_utf8_lossy(self.slice)) diff --git a/src/read/mod.rs b/src/read/mod.rs index e63b7fd2..7291f3b9 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -186,7 +186,9 @@ pub use self::addr::*; mod cfi; pub use self::cfi::*; +#[cfg(feature = "read")] mod dwarf; +#[cfg(feature = "read")] pub use self::dwarf::*; mod endian_slice; @@ -200,7 +202,9 @@ pub use self::endian_reader::*; mod reader; pub use self::reader::*; +#[cfg(feature = "read")] mod abbrev; +#[cfg(feature = "read")] pub use self::abbrev::*; mod aranges; @@ -209,7 +213,9 @@ pub use self::aranges::*; mod index; pub use self::index::*; +#[cfg(feature = "read")] mod line; +#[cfg(feature = "read")] pub use self::line::*; mod lists; @@ -217,15 +223,20 @@ mod lists; mod loclists; pub use self::loclists::*; +#[cfg(feature = "read")] mod lookup; mod op; pub use self::op::*; +#[cfg(feature = "read")] mod pubnames; +#[cfg(feature = "read")] pub use self::pubnames::*; +#[cfg(feature = "read")] mod pubtypes; +#[cfg(feature = "read")] pub use self::pubtypes::*; mod rnglists; @@ -234,7 +245,13 @@ pub use self::rnglists::*; mod str; pub use self::str::*; +/// An offset into the current compilation or type unit. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] +pub struct UnitOffset(pub T); + +#[cfg(feature = "read")] mod unit; +#[cfg(feature = "read")] pub use self::unit::*; mod value; diff --git a/src/read/op.rs b/src/read/op.rs index f217ef9f..5665d08d 100644 --- a/src/read/op.rs +++ b/src/read/op.rs @@ -1,12 +1,15 @@ //! Functions for parsing and evaluating DWARF expressions. +#[cfg(feature = "read")] use alloc::vec::Vec; use core::mem; use super::util::{ArrayLike, ArrayVec}; use crate::common::{DebugAddrIndex, DebugInfoOffset, Encoding, Register}; use crate::constants; -use crate::read::{Error, Reader, ReaderOffset, Result, StoreOnHeap, UnitOffset, Value, ValueType}; +#[cfg(feature = "read")] +use crate::read::StoreOnHeap; +use crate::read::{Error, Reader, ReaderOffset, Result, UnitOffset, Value, ValueType}; /// A reference to a DIE, either relative to the current CU or /// relative to the section. @@ -938,6 +941,7 @@ impl Expression { /// let mut eval = expression.evaluation(unit.encoding()); /// let mut result = eval.evaluate().unwrap(); /// ``` + #[cfg(feature = "read")] #[inline] pub fn evaluation(self, encoding: Encoding) -> Evaluation { Evaluation::new(self.0, encoding) @@ -982,8 +986,13 @@ impl OperationIter { /// Specification of what storage should be used for [`Evaluation`]. /// -/// Normally you would only need to use [`StoreOnHeap`], which places the stacks and the results -/// on the heap using [`Vec`]. This is the default storage type parameter for [`Evaluation`]. +#[cfg_attr( + feature = "read", + doc = " +Normally you would only need to use [`StoreOnHeap`], which places the stacks and the results +on the heap using [`Vec`]. This is the default storage type parameter for [`Evaluation`]. +" +)] /// /// If you need to avoid [`Evaluation`] from allocating memory, e.g. for signal safety, /// you can provide you own storage specification: @@ -1030,6 +1039,7 @@ pub trait EvaluationStorage { type Result: ArrayLike>; } +#[cfg(feature = "read")] impl EvaluationStorage for StoreOnHeap { type Stack = Vec; type ExpressionStack = Vec<(R, R)>; @@ -1082,7 +1092,11 @@ impl EvaluationStorage for StoreOnHeap { /// println!("{:?}", result); /// ``` #[derive(Debug)] -pub struct Evaluation = StoreOnHeap> { +pub struct Evaluation< + R: Reader, + #[cfg(not(feature = "read"))] S: EvaluationStorage, + #[cfg(feature = "read")] S: EvaluationStorage = StoreOnHeap, +> { bytecode: R, encoding: Encoding, object_address: Option, @@ -1108,6 +1122,7 @@ pub struct Evaluation = StoreOnHeap> { result: ArrayVec, } +#[cfg(feature = "read")] impl Evaluation { /// Create a new DWARF expression evaluator. /// @@ -1116,6 +1131,19 @@ impl Evaluation { pub fn new(bytecode: R, encoding: Encoding) -> Self { Self::new_in(bytecode, encoding) } + + /// Get the result of this `Evaluation`. + /// + /// # Panics + /// Panics if this `Evaluation` has not been driven to completion. + pub fn result(self) -> Vec> { + match self.state { + EvaluationState::Complete => self.result.into_vec(), + _ => { + panic!("Called `Evaluation::result` on an `Evaluation` that has not been completed") + } + } + } } impl> Evaluation { @@ -1956,21 +1984,6 @@ impl> Evaluation { } } -impl Evaluation { - /// Get the result of this `Evaluation`. - /// - /// # Panics - /// Panics if this `Evaluation` has not been driven to completion. - pub fn result(self) -> Vec> { - match self.state { - EvaluationState::Complete => self.result.into_vec(), - _ => { - panic!("Called `Evaluation::result` on an `Evaluation` that has not been completed") - } - } - } -} - #[cfg(test)] // Tests require leb128::write. #[cfg(feature = "write")] diff --git a/src/read/reader.rs b/src/read/reader.rs index 2a6ec74e..6bb7a79d 100644 --- a/src/read/reader.rs +++ b/src/read/reader.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "read")] use alloc::borrow::Cow; use core::convert::TryInto; use core::fmt::Debug; @@ -186,6 +187,12 @@ impl ReaderOffset for usize { } } +#[cfg(not(feature = "read"))] +pub(crate) mod seal_if_no_alloc { + #[derive(Debug)] + pub struct Sealed; +} + /// A trait for reading the data from a DWARF section. /// /// All read operations advance the section offset of the reader @@ -251,12 +258,22 @@ pub trait Reader: Debug + Clone { /// `len` bytes, and `self` is advanced so that it reads the remainder. fn split(&mut self, len: Self::Offset) -> Result; + /// This trait cannot be implemented if "read" feature is not enabled. + /// + /// `Reader` trait has a few methods that depend on `alloc` crate. + /// Disallowing `Reader` trait implementation prevents a crate that only depends on + /// "read-core" from being broken if another crate depending on `gimli` enables + /// "read" feature. + #[cfg(not(feature = "read"))] + fn cannot_implement() -> seal_if_no_alloc::Sealed; + /// Return all remaining data as a clone-on-write slice. /// /// The slice will be borrowed where possible, but some readers may /// always return an owned vector. /// /// Does not advance the reader. + #[cfg(feature = "read")] fn to_slice(&self) -> Result>; /// Convert all remaining data to a clone-on-write string. @@ -267,6 +284,7 @@ pub trait Reader: Debug + Clone { /// Does not advance the reader. /// /// Returns an error if the data contains invalid characters. + #[cfg(feature = "read")] fn to_string(&self) -> Result>; /// Convert all remaining data to a clone-on-write string, including invalid characters. @@ -275,6 +293,7 @@ pub trait Reader: Debug + Clone { /// always return an owned string. /// /// Does not advance the reader. + #[cfg(feature = "read")] fn to_string_lossy(&self) -> Result>; /// Read exactly `buf.len()` bytes into `buf`. diff --git a/src/read/unit.rs b/src/read/unit.rs index 2024a033..e48135c0 100644 --- a/src/read/unit.rs +++ b/src/read/unit.rs @@ -15,7 +15,7 @@ use crate::constants; use crate::endianity::Endianity; use crate::read::{ Abbreviation, Abbreviations, AttributeSpecification, DebugAbbrev, DebugStr, EndianSlice, Error, - Expression, Reader, ReaderOffset, Result, Section, + Expression, Reader, ReaderOffset, Result, Section, UnitOffset, }; impl DebugTypesOffset { @@ -52,10 +52,6 @@ impl DebugInfoOffset { } } -/// An offset into the current compilation or type unit. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)] -pub struct UnitOffset(pub T); - impl UnitOffset { /// Convert an offset to be relative to the start of the .debug_info section, /// instead of relative to the start of the given unit. Returns None if the diff --git a/src/read/util.rs b/src/read/util.rs index e22eda96..d3fc1876 100644 --- a/src/read/util.rs +++ b/src/read/util.rs @@ -1,4 +1,6 @@ +#[cfg(feature = "read")] use alloc::boxed::Box; +#[cfg(feature = "read")] use alloc::vec::Vec; use core::fmt; use core::mem::MaybeUninit; @@ -70,6 +72,7 @@ macro_rules! impl_array { impl_array!(0 1 2 3 4 8 16 32 64 128 192); +#[cfg(feature = "read")] unsafe impl Sealed for Vec { type Storage = Box<[MaybeUninit]>; @@ -87,6 +90,7 @@ unsafe impl Sealed for Vec { } } +#[cfg(feature = "read")] impl ArrayLike for Vec { type Item = T; @@ -168,6 +172,7 @@ impl ArrayVec { } } +#[cfg(feature = "read")] impl ArrayVec> { pub fn into_vec(mut self) -> Vec { let storage = core::mem::replace(&mut self.storage, Box::new([])); diff --git a/src/read/value.rs b/src/read/value.rs index b5d29078..fbffda8e 100644 --- a/src/read/value.rs +++ b/src/read/value.rs @@ -3,7 +3,9 @@ use core::mem; use crate::constants; -use crate::read::{AttributeValue, DebuggingInformationEntry, Error, Reader, Result}; +#[cfg(feature = "read")] +use crate::read::{AttributeValue, DebuggingInformationEntry}; +use crate::read::{Error, Reader, Result}; /// Convert a u64 to an i64, with sign extension if required. /// @@ -107,6 +109,7 @@ impl ValueType { } /// Construct a `ValueType` from a base type DIE. + #[cfg(feature = "read")] pub fn from_entry( entry: &DebuggingInformationEntry, ) -> Result> {