use crate::common::{BaseAddresses, Encoding, padding, sign_extend, size_mask};
use crate::constants::{DW_EH_PE_ALIGNED, DW_EH_PE_SLEB128};
macro_rules! impl_iterators {
($name:ident, $item:ty) => {
impl<R: $crate::read::Reader> Iterator for $name<R> {
type Item = $crate::read::Result<$item>;
fn next(&mut self) -> Option<Self::Item> {
$name::next(self).transpose()
}
}
#[cfg(feature = "fallible-iterator")]
impl<R: $crate::read::Reader> fallible_iterator::FallibleIterator for $name<R> {
type Item = $item;
type Error = $crate::read::Error;
fn next(&mut self) -> $crate::read::Result<Option<Self::Item>> {
$name::next(self)
}
}
};
}
mod error;
pub use self::error::*;
mod reader;
pub use self::reader::*;
mod endian_slice;
pub use self::endian_slice::*;
mod relocate;
pub use self::relocate::*;
mod lsda;
pub use self::lsda::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Pointer {
offset: usize,
encoding: Encoding,
value: u64,
address_size: u8,
}
impl Pointer {
#[inline]
#[must_use]
pub fn offset(&self) -> usize {
self.offset
}
#[inline]
#[must_use]
pub fn encoding(&self) -> Encoding {
self.encoding
}
#[inline]
#[must_use]
pub fn value(&self) -> u64 {
self.value
}
#[inline]
#[must_use]
pub fn is_null(&self) -> bool {
self.value & size_mask(self.address_size) == 0
}
#[inline]
#[must_use]
pub fn is_indirect(&self) -> bool {
self.encoding.is_indirect()
}
pub fn address(&self, bases: &BaseAddresses) -> Result<u64> {
if self.is_null() {
return Ok(0);
}
let base = bases
.relative_base(self.encoding, self.offset)
.map_err(Error::MissingBaseAddress)?;
Ok(self.value.wrapping_add(base) & size_mask(self.address_size))
}
}
pub(crate) fn read_pointer<R: Reader>(
input: &mut R,
section: &R,
encoding: Encoding,
address_size: u8,
) -> Result<Pointer> {
if !encoding.is_valid() {
return Err(Error::InvalidEncoding(encoding));
}
let aligned = encoding.application() == DW_EH_PE_ALIGNED;
if aligned {
let offset = input.offset_from(section);
input.skip(padding(offset, usize::from(address_size)))?;
}
let offset = input.offset_from(section);
let value = match encoding.size(address_size) {
Some(size) if encoding.is_signed() && !aligned => {
sign_extend(input.read_relocatable(size)?, size)
}
Some(size) => input.read_relocatable(size)?,
None if encoding.format() == DW_EH_PE_SLEB128 => {
let value = input.read_sleb128()?.cast_unsigned();
if sign_extend(value, address_size) != value {
return Err(Error::BadSignedLeb128);
}
value
}
None => {
let value = input.read_uleb128()?;
if value & !size_mask(address_size) != 0 {
return Err(Error::BadUnsignedLeb128);
}
value
}
};
Ok(Pointer {
offset,
encoding,
value,
address_size,
})
}
pub(crate) fn target2_pointer(offset: usize, value: u64) -> Pointer {
Pointer {
offset,
encoding: Encoding::UDATA4,
value,
address_size: 4,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Base;
use crate::endianity::LittleEndian;
fn read(data: &[u8], skip: usize, encoding: u8, address_size: u8) -> Result<Pointer> {
let section = EndianSlice::new(data, LittleEndian);
let mut input = section.range_from(skip)?;
read_pointer(&mut input, §ion, Encoding(encoding), address_size)
}
#[test]
fn test_read_pointer() {
let data = [0xfc, 0xff, 0xff, 0xff, 0x10, 0x20, 0x30, 0x40];
let bases = BaseAddresses::default().set_section(0x1000);
let p = read(&data, 0, 0x1b, 8).unwrap();
assert_eq!(p.value(), (-4i64).cast_unsigned());
assert_eq!(p.address(&bases), Ok(0x0ffc));
assert_eq!(read(&data, 0, 0x03, 8).unwrap().value(), 0xffff_fffc);
assert_eq!(read(&data, 0, 0x00, 4).unwrap().value(), 0xffff_fffc);
assert_eq!(
read(&data, 0, 0x00, 8).unwrap().value(),
0x4030_2010_ffff_fffc
);
assert_eq!(
read(&[0x7c], 0, 0x09, 8).unwrap().value(),
(-4i64).cast_unsigned()
);
let p = read(&data, 4, 0x13, 4).unwrap();
assert_eq!(p.offset(), 4);
assert_eq!(p.address(&bases), Ok(0x4030_3014));
let p = read(&data, 1, 0x50, 4).unwrap();
assert_eq!((p.offset(), p.value()), (4, 0x4030_2010));
let p = read(&data, 4, 0x23, 4).unwrap();
assert_eq!(
p.address(&BaseAddresses::default()),
Err(Error::MissingBaseAddress(Base::Text))
);
assert_eq!(
read(&data, 0, 0xff, 4),
Err(Error::InvalidEncoding(Encoding::OMIT))
);
assert_eq!(
read(&data, 0, 0x05, 4),
Err(Error::InvalidEncoding(Encoding(5)))
);
assert_eq!(read(&data, 6, 0x03, 4), Err(Error::UnexpectedEof));
assert_eq!(
read(&data, 0, 0x53, 4),
Err(Error::InvalidEncoding(Encoding(0x53)))
);
let wide = [0x80, 0x80, 0x80, 0x80, 0x10];
assert_eq!(read(&wide, 0, 0x01, 8).unwrap().value(), 0x1_0000_0000);
assert_eq!(read(&wide, 0, 0x01, 4), Err(Error::BadUnsignedLeb128));
assert_eq!(read(&wide, 0, 0x09, 4), Err(Error::BadSignedLeb128));
let negative = [0x80, 0x80, 0x80, 0x80, 0x78];
assert_eq!(
read(&negative, 0, 0x09, 4).unwrap().value(),
(-0x8000_0000i64).cast_unsigned()
);
}
#[test]
fn test_null_pointer() {
let data = [0, 0, 0, 0];
let p = read(&data, 0, 0x9b, 4).unwrap();
assert!(p.is_null() && p.is_indirect());
assert_eq!(p.address(&BaseAddresses::default()), Ok(0));
let data = [0, 0, 0, 0, 1, 0, 0, 0];
let bases = BaseAddresses::default().set_section(0x1000);
let p = read(&data, 0, 0x14, 4).unwrap();
assert!(p.is_null());
assert_eq!(p.address(&bases), Ok(0));
let p = read(&data, 0, 0x14, 8).unwrap();
assert!(!p.is_null());
assert_eq!(p.address(&bases), Ok(0x1_0000_1000));
}
}