From 7a3aeef78d169b39e15e18f1573492c501b25c68 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 13 Oct 2021 02:53:18 +0100 Subject: [PATCH] Add read-core feature gate (#596) --- .github/workflows/rust.yml | 2 ++ Cargo.toml | 5 +++-- src/constants.rs | 11 +++++++--- src/leb128.rs | 4 ++-- src/lib.rs | 5 +++-- src/read/cfi.rs | 13 ++++++++++-- src/read/endian_slice.rs | 11 ++++++++++ src/read/mod.rs | 17 ++++++++++++++++ src/read/op.rs | 41 ++++++++++++++++++++++---------------- src/read/reader.rs | 19 ++++++++++++++++++ src/read/unit.rs | 6 +----- src/read/util.rs | 5 +++++ src/read/value.rs | 5 ++++- 13 files changed, 110 insertions(+), 34 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index e985ad1e7..dade358b7 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 c41f38584..9f82433f6 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 e393dd2e8..c8d6a1662 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -81,9 +81,14 @@ macro_rules! dw { if let Some(s) = self.static_string() { f.pad(s) } else { - f.pad(&format!("Unknown {}: {}", - stringify!($struct_name), - self.0)) + #[cfg(feature = "read")] + { + f.pad(&format!("Unknown {}: {}", stringify!($struct_name), self.0)) + } + #[cfg(not(feature = "read"))] + { + write!(f, "Unknown {}: {}", stringify!($struct_name), self.0) + } } } } diff --git a/src/leb128.rs b/src/leb128.rs index b662ce1f0..4fe185c42 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 4d4eef82d..7ac1820e3 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 c715ecfdc..b6a81f3f4 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}; @@ -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>; @@ -1849,6 +1857,7 @@ impl> Default for UnwindContext { } } +#[cfg(feature = "read")] impl UnwindContext { /// Construct a new call frame unwinding context. pub fn new() -> Self { diff --git a/src/read/endian_slice.rs b/src/read/endian_slice.rs index a07855f0a..05262cdec 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 e63b7fd20..7291f3b96 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 f217ef9fb..2ca6247bc 100644 --- a/src/read/op.rs +++ b/src/read/op.rs @@ -1,5 +1,6 @@ //! Functions for parsing and evaluating DWARF expressions. +#[cfg(feature = "read")] use alloc::vec::Vec; use core::mem; @@ -938,6 +939,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 +984,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 +1037,7 @@ pub trait EvaluationStorage { type Result: ArrayLike>; } +#[cfg(feature = "read")] impl EvaluationStorage for StoreOnHeap { type Stack = Vec; type ExpressionStack = Vec<(R, R)>; @@ -1108,6 +1116,7 @@ pub struct Evaluation = StoreOnHeap> { result: ArrayVec, } +#[cfg(feature = "read")] impl Evaluation { /// Create a new DWARF expression evaluator. /// @@ -1116,6 +1125,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 +1978,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 2a6ec74e3..6bb7a79db 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 2024a0339..e48135c0e 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 e22eda966..d3fc18767 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 b5d290781..fbffda8e1 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> {