//! A byte-offset based string table. //! Commonly used in ELF binaries, Unix archives, and even PE binaries.
use core::fmt; use core::ops::Index; use core::str; use scroll::{ctx, Pread};
if_alloc! { usecrate::error; use alloc::vec::Vec;
}
/// A common string table format which is indexed by byte offsets (and not /// member index). Constructed using [`parse`](#method.parse) /// with your choice of delimiter. Please be careful. pubstruct Strtab<'a> {
delim: ctx::StrCtx,
bytes: &'a [u8], #[cfg(feature = "alloc")]
strings: Vec<(usize, &'a str)>,
}
impl<'a> Strtab<'a> { /// Creates a `Strtab` with `bytes` as the backing string table, using `delim` as the delimiter between entries. /// /// NB: this does *not* preparse the string table, which can have non-optimal access patterns. /// See <https://github.com/m4b/goblin/pull/275> pubfn new(bytes: &'a [u8], delim: u8) -> Self { Self::from_slice_unparsed(bytes, 0, bytes.len(), delim)
}
/// Returns the length of this `Strtab` in bytes pubfn len(&self) -> usize { self.bytes.len()
}
/// Creates a `Strtab` directly without bounds check and without parsing it. /// /// This is potentially unsafe and should only be used if `feature = "alloc"` is disabled. pubfn from_slice_unparsed(bytes: &'a [u8], offset: usize, len: usize, delim: u8) -> Self { Self {
delim: ctx::StrCtx::Delimiter(delim),
bytes: &bytes[offset..offset + len], #[cfg(feature = "alloc")]
strings: Vec::new(),
}
} /// Gets a str reference from the backing bytes starting at byte `offset`. /// /// If the index is out of bounds, `None` is returned. Panics if bytes are invalid UTF-8. /// Use this method if the `Strtab` was created using `from_slice_unparsed()`. pubfn get_unsafe(&self, offset: usize) -> Option<&'a str> { if offset >= self.bytes.len() {
None
} else {
Some(get_str(offset, self.bytes, self.delim).unwrap())
}
} #[cfg(feature = "alloc")] /// Parses a `Strtab` from `bytes` at `offset` with `len` size as the backing string table, using `delim` as the delimiter. /// /// Errors if bytes are invalid UTF-8. /// Requires `feature = "alloc"` pubfn parse(bytes: &'a [u8], offset: usize, len: usize, delim: u8) -> error::Result<Self> { let (end, overflow) = offset.overflowing_add(len); if overflow || end > bytes.len() { return Err(error::Error::Malformed(format!( "Strtable size ({}) + offset ({}) is out of bounds for {} #bytes. Overflowed: {}",
len,
offset,
bytes.len(),
overflow
)));
} letmut result = Self::from_slice_unparsed(bytes, offset, len, delim); letmut i = 0; while i < result.bytes.len() { let string = get_str(i, result.bytes, result.delim)?;
result.strings.push((i, string));
i += string.len() + 1;
}
Ok(result)
} #[cfg(feature = "alloc")] /// Parses a `Strtab` with `bytes` as the backing string table, using `delim` as the delimiter between entries. /// /// Requires `feature = "alloc"` pubfn new_preparsed(bytes: &'a [u8], delim: u8) -> error::Result<Self> { Self::parse(bytes, 0, bytes.len(), delim)
} #[cfg(feature = "alloc")] /// Converts the string table to a vector of parsed strings. /// /// Note: This method is used to check the parsed contents of `strtab`. /// If you want to get the correct contents of `strtab` as `Vec`, use the following example. /// /// # Examples /// ```rust /// use goblin::error::Error; /// /// pub fn show_shdr_strtab(bytes: &[u8]) -> Result<(), Error> { /// let elf = goblin::elf::Elf::parse(&bytes)?; /// /// for section in elf.section_headers { /// println!("{}", elf.shdr_strtab.get_at(section.sh_name).unwrap_or("")); /// } /// /// Ok(()) /// } /// ``` /// /// Requires `feature = "alloc"` pubfn to_vec(&self) -> error::Result<Vec<&>'a str>> { // Fallback in case `Strtab` was created using `from_slice_unparsed()`. ifself.strings.is_empty() { letmut result = Vec::new(); letmut i = 0; while i < self.bytes.len() { let string = get_str(i, self.bytes, self.delim)?;
result.push(string);
i += string.len() + 1;
} return Ok(result);
}
Ok(self.strings.iter().map(|&(_key, value)| value).collect())
} #[cfg(feature = "alloc")] /// Safely gets a str reference from the parsed table starting at byte `offset`. /// /// If the index is out of bounds, `None` is returned. /// Requires `feature = "alloc"` pubfn get_at(&self, offset: usize) -> Option<&'a str> { matchself
.strings
.binary_search_by_key(&offset, |&(key, _value)| key)
{
Ok(index) => Some(self.strings[index].1),
Err(index) => { if index == 0 { return None;
} let (string_begin_offset, entire_string) = self.strings[index - 1];
entire_string.get(offset - string_begin_offset..)
}
}
} #[deprecated(since = "0.4.2", note = "Use from_slice_unparsed() instead")] /// Construct a strtab from a `ptr`, and a `size`, using `delim` as the delimiter /// /// # Safety /// This function creates a `Strtab` directly from a raw pointer and size pubunsafefn from_raw(ptr: *const u8, len: usize, delim: u8) -> Strtab<'a> { Self::from_slice_unparsed(core::slice::from_raw_parts(ptr, len), 0, len, delim)
} #[deprecated(since = "0.4.2", note = "Bad performance, use get_at() instead")] #[cfg(feature = "alloc")] /// Parses a str reference from the parsed table starting at byte `offset`. /// /// If the index is out of bounds, `None` is returned. /// Requires `feature = "alloc"` pubfn get(&self, offset: usize) -> Option<error::Result<&='color:blue'>'a str>> { if offset >= self.bytes.len() {
None
} else {
Some(get_str(offset, self.bytes, self.delim).map_err(core::convert::Into::into))
}
}
}
impl<'a> Index<usize> for Strtab<'a> { type Output = str; /// Gets str reference at starting at byte `offset`. /// **NB**: this will panic if the underlying bytes are not valid utf8, or the offset is invalid #[inline(always)] fn index(&self, offset: usize) -> &Self::Output { // This can't delegate to get() because get() requires #[cfg(features = "alloc")] // It's also slightly less useful than get() because the lifetime -- specified by the Index // trait -- matches &self, even though we could return &'a instead
get_str(offset, self.bytes, self.delim).unwrap()
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.