#![deny(missing_docs)]
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub mod macos;
#[cfg(any(
target_os = "linux",
all(target_os = "android", feature = "dl_iterate_phdr")
))]
pub mod linux;
#[cfg(target_os = "windows")]
pub mod windows;
use std::ffi::OsStr;
use std::fmt::{self, Debug};
use std::usize;
pub mod unsupported;
#[cfg(any(
target_os = "linux",
all(target_os = "android", feature = "dl_iterate_phdr")
))]
use crate::linux as native_mod;
#[cfg(any(target_os = "macos", target_os = "ios"))]
use crate::macos as native_mod;
#[cfg(target_os = "windows")]
use crate::windows as native_mod;
#[cfg(not(any(
target_os = "macos",
target_os = "ios",
target_os = "linux",
all(target_os = "android", feature = "dl_iterate_phdr"),
target_os = "windows"
)))]
use unsupported as native_mod;
pub type TargetSharedLibrary<'a> = native_mod::SharedLibrary<'a>;
pub const TARGET_SUPPORTED: bool = cfg!(any(
target_os = "macos",
target_os = "ios",
target_os = "linux",
all(target_os = "android", feature = "dl_iterate_phdr"),
target_os = "windows"
));
macro_rules! simple_newtypes {
(
$(
$(#[$attr:meta])*
type $name:ident = $oldty:ty
where
default = $default:expr ,
display = $format:expr ;
)*
) => {
$(
$(#[$attr])*
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct $name(pub $oldty);
impl Default for $name {
#[inline]
fn default() -> Self {
$name( $default )
}
}
impl From<$oldty> for $name {
fn from(x: $oldty) -> $name {
$name(x)
}
}
impl From<$name> for $oldty {
fn from($name(x): $name) -> $oldty {
x
}
}
impl fmt::Display for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, $format, self.0)
}
}
)*
}
}
simple_newtypes! {
type Svma = usize
where
default = 0,
display = "{:#x}";
type Avma = usize
where
default = 0,
display = "{:#x}";
type Bias = usize
where
default = 0,
display = "{:#x}";
}
#[allow(clippy::len_without_is_empty)]
pub trait Segment: Sized + Debug {
type SharedLibrary: SharedLibrary<Segment = Self>;
fn name(&self) -> &str;
#[inline]
fn is_code(&self) -> bool {
false
}
#[inline]
fn is_load(&self) -> bool {
self.is_code()
}
fn stated_virtual_memory_address(&self) -> Svma;
fn len(&self) -> usize;
#[inline]
fn actual_virtual_memory_address(&self, shlib: &Self::SharedLibrary) -> Avma {
let svma = self.stated_virtual_memory_address();
let bias = shlib.virtual_memory_bias();
Avma(svma.0 + bias.0)
}
#[inline]
fn contains_svma(&self, address: Svma) -> bool {
let start = self.stated_virtual_memory_address().0;
let end = start + self.len();
let address = address.0;
start <= address && address < end
}
#[inline]
fn contains_avma(&self, shlib: &Self::SharedLibrary, address: Avma) -> bool {
let start = self.actual_virtual_memory_address(shlib).0;
let end = start + self.len();
let address = address.0;
start <= address && address < end
}
}
#[derive(PartialEq, Eq, Hash)]
pub enum SharedLibraryId {
Uuid([u8; 16]),
GnuBuildId(Vec<u8>),
PeSignature(u32, u32),
PdbSignature([u8; 16], u32),
}
impl SharedLibraryId {
pub fn as_bytes(&self) -> &[u8] {
match *self {
SharedLibraryId::Uuid(ref bytes) => &*bytes,
SharedLibraryId::GnuBuildId(ref bytes) => bytes,
SharedLibraryId::PeSignature(_, _) => &[][..],
SharedLibraryId::PdbSignature(ref bytes, _) => &*bytes,
}
}
}
impl fmt::Display for SharedLibraryId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SharedLibraryId::Uuid(ref bytes) => {
for (idx, byte) in bytes.iter().enumerate() {
if idx == 4 || idx == 6 || idx == 8 || idx == 10 {
write!(f, "-")?;
}
write!(f, "{:02x}", byte)?;
}
}
SharedLibraryId::GnuBuildId(ref bytes) => {
for byte in bytes {
write!(f, "{:02x}", byte)?;
}
}
SharedLibraryId::PeSignature(timestamp, size_of_image) => {
write!(f, "{:08X}{:x}", timestamp, size_of_image)?;
}
SharedLibraryId::PdbSignature(ref bytes, age) => {
for (idx, byte) in bytes.iter().enumerate() {
if idx == 4 || idx == 6 || idx == 8 || idx == 10 {
write!(f, "-")?;
}
write!(f, "{:02X}", byte)?;
}
write!(f, "{:x}", age)?;
}
}
Ok(())
}
}
impl fmt::Debug for SharedLibraryId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let name = match *self {
SharedLibraryId::Uuid(..) => "Uuid",
SharedLibraryId::GnuBuildId(..) => "GnuBuildId",
SharedLibraryId::PeSignature(..) => "PeSignature",
SharedLibraryId::PdbSignature(..) => "PdbSignature",
};
write!(f, "{}(\"{}\")", name, self)
}
}
#[allow(clippy::len_without_is_empty)]
pub trait SharedLibrary: Sized + Debug {
type Segment: Segment<SharedLibrary = Self>;
type SegmentIter: Debug + Iterator<Item = Self::Segment>;
fn name(&self) -> &OsStr;
fn debug_name(&self) -> Option<&OsStr> {
None
}
fn id(&self) -> Option<SharedLibraryId>;
fn debug_id(&self) -> Option<SharedLibraryId> {
self.id()
}
fn actual_load_addr(&self) -> Avma {
self.segments()
.find(|x| x.is_load())
.map(|x| x.actual_virtual_memory_address(self))
.unwrap_or(Avma(usize::MAX))
}
#[inline]
#[doc(hidden)]
#[deprecated(note = "use stated_load_address() instead")]
fn load_addr(&self) -> Svma {
self.stated_load_addr()
}
fn stated_load_addr(&self) -> Svma {
self.segments()
.find(|x| x.is_load())
.map(|x| x.stated_virtual_memory_address())
.unwrap_or(Svma(usize::MAX))
}
fn len(&self) -> usize {
let end_address = self
.segments()
.filter(|x| x.is_load())
.map(|x| x.actual_virtual_memory_address(self).0 + x.len())
.max()
.unwrap_or(usize::MAX);
end_address - self.actual_load_addr().0
}
fn segments(&self) -> Self::SegmentIter;
fn virtual_memory_bias(&self) -> Bias;
#[inline]
fn avma_to_svma(&self, address: Avma) -> Svma {
let bias = self.virtual_memory_bias();
Svma(address.0 - bias.0)
}
fn each<F, C>(f: F)
where
F: FnMut(&Self) -> C,
C: Into<IterationControl>;
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum IterationControl {
Break,
Continue,
}
impl From<()> for IterationControl {
#[inline]
fn from(_: ()) -> Self {
IterationControl::Continue
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn panic_in_each() {
use std::panic;
match panic::catch_unwind(|| {
TargetSharedLibrary::each(|_| panic!("uh oh"));
}) {
Ok(()) => panic!("Expected a panic, but didn't get one"),
Err(any) => {
assert!(
any.is::<&'static str>(),
"panic value should be a &'static str"
);
assert_eq!(*any.downcast_ref::<&'static str>().unwrap(), "uh oh");
}
}
}
#[test]
fn test_load_address_bias() {
TargetSharedLibrary::each(|lib| {
let svma = lib.stated_load_addr();
let avma = lib.actual_load_addr();
assert_eq!(lib.avma_to_svma(avma), svma);
});
}
}