use core::fmt;
use crate::constants::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Encoding(pub u8);
impl Encoding {
pub const OMIT: Encoding = Encoding(DW_EH_PE_OMIT);
pub const ABSPTR: Encoding = Encoding(DW_EH_PE_ABSPTR);
pub const ULEB128: Encoding = Encoding(DW_EH_PE_ULEB128);
pub const UDATA2: Encoding = Encoding(DW_EH_PE_UDATA2);
pub const UDATA4: Encoding = Encoding(DW_EH_PE_UDATA4);
pub const UDATA8: Encoding = Encoding(DW_EH_PE_UDATA8);
pub const SLEB128: Encoding = Encoding(DW_EH_PE_SLEB128);
pub const SDATA2: Encoding = Encoding(DW_EH_PE_SDATA2);
pub const SDATA4: Encoding = Encoding(DW_EH_PE_SDATA4);
pub const SDATA8: Encoding = Encoding(DW_EH_PE_SDATA8);
pub const PCREL_SDATA4: Encoding = Encoding(DW_EH_PE_PCREL | DW_EH_PE_SDATA4);
pub const INDIRECT_PCREL_SDATA4: Encoding =
Encoding(DW_EH_PE_INDIRECT | DW_EH_PE_PCREL | DW_EH_PE_SDATA4);
#[inline]
#[must_use]
pub fn is_omit(self) -> bool {
self.0 == DW_EH_PE_OMIT
}
#[inline]
#[must_use]
pub fn format(self) -> u8 {
self.0 & 0x0f
}
#[inline]
#[must_use]
pub fn application(self) -> u8 {
self.0 & 0x70
}
#[inline]
#[must_use]
pub fn is_indirect(self) -> bool {
self.0 & DW_EH_PE_INDIRECT != 0
}
#[inline]
#[must_use]
pub fn is_signed(self) -> bool {
self.0 & DW_EH_PE_SIGNED != 0
}
#[must_use]
pub fn is_valid(self) -> bool {
if self.application() == DW_EH_PE_ALIGNED {
return self.0 == DW_EH_PE_ALIGNED;
}
!self.is_omit()
&& matches!(
self.format(),
DW_EH_PE_ABSPTR
| DW_EH_PE_ULEB128
| DW_EH_PE_UDATA2
| DW_EH_PE_UDATA4
| DW_EH_PE_UDATA8
| DW_EH_PE_SLEB128
| DW_EH_PE_SDATA2
| DW_EH_PE_SDATA4
| DW_EH_PE_SDATA8
)
&& self.application() < DW_EH_PE_ALIGNED
}
#[cfg(any(feature = "read", feature = "write"))]
pub(crate) fn is_offset_encoding(self) -> bool {
self.is_valid() && self.application() == DW_EH_PE_ABSPTR && !self.is_indirect()
}
#[must_use]
pub fn size(self, address_size: u8) -> Option<u8> {
if !self.is_valid() {
return None;
}
if self.application() == DW_EH_PE_ALIGNED {
return Some(address_size);
}
match self.format() {
DW_EH_PE_ABSPTR => Some(address_size),
DW_EH_PE_UDATA2 | DW_EH_PE_SDATA2 => Some(2),
DW_EH_PE_UDATA4 | DW_EH_PE_SDATA4 => Some(4),
DW_EH_PE_UDATA8 | DW_EH_PE_SDATA8 => Some(8),
_ => None,
}
}
}
impl Default for Encoding {
fn default() -> Self {
Encoding::OMIT
}
}
impl fmt::Display for Encoding {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_omit() {
return f.write_str("DW_EH_PE_omit");
}
if !self.is_valid() {
return write!(f, "<invalid encoding 0x{:02x}>", self.0);
}
if self.0 == DW_EH_PE_ALIGNED {
return f.write_str("DW_EH_PE_aligned");
}
let format = match self.format() {
DW_EH_PE_ABSPTR => "absptr",
DW_EH_PE_ULEB128 => "uleb128",
DW_EH_PE_UDATA2 => "udata2",
DW_EH_PE_UDATA4 => "udata4",
DW_EH_PE_UDATA8 => "udata8",
DW_EH_PE_SLEB128 => "sleb128",
DW_EH_PE_SDATA2 => "sdata2",
DW_EH_PE_SDATA4 => "sdata4",
_ => "sdata8",
};
if self.is_indirect() {
f.write_str("DW_EH_PE_indirect|")?;
}
match self.application() {
DW_EH_PE_PCREL => f.write_str("DW_EH_PE_pcrel|")?,
DW_EH_PE_TEXTREL => f.write_str("DW_EH_PE_textrel|")?,
DW_EH_PE_DATAREL => f.write_str("DW_EH_PE_datarel|")?,
DW_EH_PE_FUNCREL => f.write_str("DW_EH_PE_funcrel|")?,
_ => {}
}
write!(f, "DW_EH_PE_{format}")
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Unwinder {
#[default]
Dwarf,
ArmEhabi,
SjLj,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Format {
pub address_size: u8,
pub unwinder: Unwinder,
}
impl Format {
#[inline]
#[must_use]
pub fn new(address_size: u8) -> Self {
Format {
address_size,
unwinder: Unwinder::Dwarf,
}
}
#[inline]
#[must_use]
pub fn arm_ehabi() -> Self {
Format {
address_size: 4,
unwinder: Unwinder::ArmEhabi,
}
}
#[inline]
#[must_use]
pub fn with_unwinder(mut self, unwinder: Unwinder) -> Self {
self.unwinder = unwinder;
self
}
#[cfg(feature = "write")]
#[inline]
pub(crate) fn address_mask(self) -> u64 {
size_mask(self.address_size)
}
#[cfg(any(feature = "read", feature = "write"))]
#[inline]
pub(crate) fn has_valid_address_size(self) -> bool {
match self.unwinder {
Unwinder::ArmEhabi => self.address_size == 4,
_ => matches!(self.address_size, 2 | 4 | 8),
}
}
}
#[cfg(any(feature = "read", feature = "write"))]
pub(crate) const ARM_TARGET2_SIZE: u8 = 4;
#[cfg(any(feature = "read", feature = "write"))]
#[inline]
pub(crate) fn size_mask(size: u8) -> u64 {
if size >= 8 {
u64::MAX
} else {
(1u64 << (u32::from(size) * 8)) - 1
}
}
#[cfg(any(feature = "read", feature = "write"))]
#[inline]
pub(crate) fn sign_extend(value: u64, size: u8) -> u64 {
let shift = 64 - u32::from(size) * 8;
((value << shift).cast_signed() >> shift).cast_unsigned()
}
#[cfg(any(feature = "read", feature = "write"))]
#[inline]
pub(crate) fn padding(offset: usize, alignment: usize) -> usize {
debug_assert!(alignment.is_power_of_two());
offset.wrapping_neg() & (alignment - 1)
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct BaseAddresses {
pub section: Option<u64>,
pub text: Option<u64>,
pub data: Option<u64>,
pub function: Option<u64>,
}
impl BaseAddresses {
#[inline]
#[must_use]
pub fn set_section(mut self, address: u64) -> Self {
self.section = Some(address);
self
}
#[inline]
#[must_use]
pub fn set_text(mut self, address: u64) -> Self {
self.text = Some(address);
self
}
#[inline]
#[must_use]
pub fn set_data(mut self, address: u64) -> Self {
self.data = Some(address);
self
}
#[inline]
#[must_use]
pub fn set_function(mut self, address: u64) -> Self {
self.function = Some(address);
self
}
#[inline]
#[must_use]
pub fn get(&self, base: Base) -> Option<u64> {
match base {
Base::Section => self.section,
Base::Text => self.text,
Base::Data => self.data,
Base::Function => self.function,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Base {
Section,
Text,
Data,
Function,
}
impl fmt::Display for Base {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Base::Section => "section",
Base::Text => "text",
Base::Data => "data",
Base::Function => "function",
})
}
}
impl BaseAddresses {
#[cfg(any(feature = "read", feature = "write"))]
pub(crate) fn relative_base(&self, encoding: Encoding, offset: usize) -> Result<u64, Base> {
let base = match encoding.application() {
DW_EH_PE_PCREL => Base::Section,
DW_EH_PE_TEXTREL => Base::Text,
DW_EH_PE_DATAREL => Base::Data,
DW_EH_PE_FUNCREL => Base::Function,
_ => return Ok(0),
};
let address = self.get(base).ok_or(base)?;
Ok(if base == Base::Section {
address.wrapping_add(offset as u64)
} else {
address
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LsdaOffset(pub usize);
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::ToString;
#[test]
fn test_encoding() {
let e = Encoding::INDIRECT_PCREL_SDATA4;
assert_eq!(e.0, 0x9b);
assert_eq!(e.format(), DW_EH_PE_SDATA4);
assert_eq!(e.application(), DW_EH_PE_PCREL);
assert!(e.is_indirect() && e.is_signed() && e.is_valid());
assert_eq!(e.size(8), Some(4));
assert_eq!(Encoding::ABSPTR.size(8), Some(8));
assert_eq!(Encoding::ULEB128.size(8), None);
assert_eq!(Encoding(DW_EH_PE_ALIGNED).size(4), Some(4));
assert_eq!(Encoding(DW_EH_PE_ALIGNED).to_string(), "DW_EH_PE_aligned");
assert!(!Encoding(DW_EH_PE_ALIGNED | DW_EH_PE_UDATA4).is_valid());
assert!(!Encoding(DW_EH_PE_ALIGNED | DW_EH_PE_INDIRECT).is_valid());
assert!(!Encoding::OMIT.is_valid());
assert!(!Encoding(0x05).is_valid());
assert!(!Encoding(0x60).is_valid());
assert_eq!(
e.to_string(),
"DW_EH_PE_indirect|DW_EH_PE_pcrel|DW_EH_PE_sdata4"
);
assert_eq!(Encoding::ULEB128.to_string(), "DW_EH_PE_uleb128");
assert_eq!(Encoding(0x0d).to_string(), "<invalid encoding 0x0d>");
}
#[test]
#[cfg(any(feature = "read", feature = "write"))]
fn test_helpers() {
assert_eq!(size_mask(4), 0xffff_ffff);
assert_eq!(size_mask(8), u64::MAX);
assert_eq!(sign_extend(0xfffc, 2), (-4i64).cast_unsigned());
assert_eq!(sign_extend(0x7ffc, 2), 0x7ffc);
assert_eq!(padding(5, 4), 3);
assert_eq!(padding(8, 8), 0);
let bases = BaseAddresses::default().set_section(0x1000);
assert_eq!(bases.relative_base(Encoding::PCREL_SDATA4, 4), Ok(0x1004));
assert_eq!(bases.relative_base(Encoding::ABSPTR, 4), Ok(0));
assert_eq!(bases.relative_base(Encoding(0x23), 4), Err(Base::Text));
}
}