use std::path::Path;
use crate::error::Error;
#[derive(Clone, Copy, Debug)]
pub struct Source<'a> {
pub path: &'a Path,
pub member: Option<&'a str>,
pub base: u64,
}
impl<'a> Source<'a> {
#[must_use]
pub fn new(path: &'a Path) -> Self {
Self {
path,
member: None,
base: 0,
}
}
#[must_use]
pub fn member(path: &'a Path, member: &'a str) -> Self {
Self {
path,
member: Some(member),
base: 0,
}
}
#[must_use]
pub fn at(self, offset: u64) -> Self {
Self {
base: self.base.saturating_add(offset),
..self
}
}
#[cold]
#[inline(never)]
#[must_use]
pub fn malformed(&self, offset: u64, what: impl Into<String>) -> Error {
Error::Malformed {
file: self.path.to_path_buf(),
member: self.member.map(str::to_owned),
offset: self.base.saturating_add(offset),
what: what.into(),
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Endian {
big: bool,
}
impl Endian {
pub const LITTLE: Self = Self { big: false };
pub const BIG: Self = Self { big: true };
#[must_use]
pub fn is_big(self) -> bool {
self.big
}
#[inline]
#[must_use]
pub fn u16(self, data: &[u8], offset: usize) -> Option<u16> {
let bytes = array::<2>(data, offset)?;
Some(if self.big {
u16::from_be_bytes(bytes)
} else {
u16::from_le_bytes(bytes)
})
}
#[inline]
#[must_use]
pub fn u32(self, data: &[u8], offset: usize) -> Option<u32> {
let bytes = array::<4>(data, offset)?;
Some(if self.big {
u32::from_be_bytes(bytes)
} else {
u32::from_le_bytes(bytes)
})
}
#[inline]
#[must_use]
pub fn u64(self, data: &[u8], offset: usize) -> Option<u64> {
let bytes = array::<8>(data, offset)?;
Some(if self.big {
u64::from_be_bytes(bytes)
} else {
u64::from_le_bytes(bytes)
})
}
#[inline]
#[must_use]
pub fn word(self, data: &[u8], offset: usize, is64: bool) -> Option<u64> {
if is64 {
self.u64(data, offset)
} else {
self.u32(data, offset).map(u64::from)
}
}
}
#[inline]
pub(crate) fn array<const N: usize>(data: &[u8], offset: usize) -> Option<[u8; N]> {
let end = offset.checked_add(N)?;
data.get(offset..end)?.try_into().ok()
}
#[inline]
pub(crate) fn subslice(data: &[u8], offset: u64, size: u64) -> Option<&[u8]> {
let start = usize::try_from(offset).ok()?;
let len = usize::try_from(size).ok()?;
let end = start.checked_add(len)?;
data.get(start..end)
}
#[inline]
pub(crate) fn to_u64(value: usize) -> u64 {
u64::try_from(value).unwrap_or(u64::MAX)
}
#[inline]
pub(crate) fn cstr(data: &[u8]) -> Option<&[u8]> {
let len = data.iter().position(|&b| b == 0)?;
data.get(..len)
}
#[inline]
pub(crate) fn fixed_name(bytes: &[u8]) -> &[u8] {
match bytes.iter().position(|&b| b == 0) {
Some(len) => bytes.get(..len).unwrap_or(bytes),
None => bytes,
}
}
#[inline]
pub fn read_uleb(data: &[u8], pos: &mut usize) -> Option<u64> {
let mut result = 0u64;
let mut shift = 0u32;
loop {
let byte = *data.get(*pos)?;
*pos = pos.checked_add(1)?;
let low = u64::from(byte & 0x7f);
if shift >= 64 {
if low != 0 {
return None;
}
} else {
let shifted = low.checked_shl(shift)?;
if shifted >> shift != low {
return None;
}
result |= shifted;
}
if byte & 0x80 == 0 {
return Some(result);
}
shift = shift.saturating_add(7);
}
}
#[inline]
pub fn read_sleb(data: &[u8], pos: &mut usize) -> Option<i64> {
let mut result = 0i64;
let mut shift = 0u32;
loop {
let byte = *data.get(*pos)?;
*pos = pos.checked_add(1)?;
if shift < 64 {
result |= i64::from(byte & 0x7f).wrapping_shl(shift);
}
shift = shift.saturating_add(7);
if byte & 0x80 == 0 {
if shift < 64 && byte & 0x40 != 0 {
result |= (-1i64).wrapping_shl(shift);
}
return Some(result);
}
}
}
#[cfg(test)]
#[allow(clippy::arithmetic_side_effects)]
mod tests {
use super::*;
#[test]
fn leb128() {
let mut pos = 0;
assert_eq!(read_uleb(&[0xe5, 0x8e, 0x26], &mut pos), Some(624_485));
assert_eq!(pos, 3);
let mut pos = 0;
assert_eq!(read_sleb(&[0xc0, 0xbb, 0x78], &mut pos), Some(-123_456));
let mut pos = 0;
assert_eq!(read_sleb(&[0x7f], &mut pos), Some(-1));
let mut pos = 0;
assert_eq!(read_uleb(&[0x80, 0x80], &mut pos), None);
let max = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01];
let mut pos = 0;
assert_eq!(read_uleb(&max, &mut pos), Some(u64::MAX));
let over = [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x02];
let mut pos = 0;
assert_eq!(read_uleb(&over, &mut pos), None);
let mut pos = 0;
assert_eq!(
read_uleb(
&[
0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00
],
&mut pos
),
Some(1)
);
}
#[test]
fn names() {
assert_eq!(fixed_name(b"__TEXT\0\0\0\0\0\0\0\0\0\0"), b"__TEXT");
assert_eq!(fixed_name(b"__gcc_except_tab"), b"__gcc_except_tab");
assert_eq!(cstr(b"abc\0def"), Some(&b"abc"[..]));
assert_eq!(cstr(b"abc"), None);
}
}