use crate::{
Result,
relocation::{RelocAddr, RelocValue},
sync::Arc,
};
use alloc::{boxed::Box, vec::Vec};
use core::{ffi::c_void, fmt::Debug, mem::size_of, ptr::NonNull};
use super::{ElfSegmentBacking, ElfSegmentSlice};
pub struct ElfSegments {
base: usize,
slices: Box<[ElfSegmentSlice]>,
}
impl Debug for ElfSegments {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let slices = self
.slices
.iter()
.map(|slice| (slice.offset(), slice.len()))
.collect::<Vec<_>>();
f.debug_struct("ElfSegments")
.field("base", &format_args!("0x{:x}", self.base()))
.field("slices", &slices)
.finish()
}
}
impl ElfSegments {
#[inline]
fn find_slice(&self, start: usize, len: usize) -> Option<&ElfSegmentSlice> {
self.slices
.iter()
.find(|slice| slice.contains_range(start, len))
}
#[inline]
fn slice_base(&self, slice: &ElfSegmentSlice) -> usize {
self.base.saturating_add(slice.offset)
}
#[inline]
fn slice_end(&self, slice: &ElfSegmentSlice) -> usize {
self.slice_base(slice).saturating_add(slice.len)
}
pub(crate) fn new(
memory: *mut c_void,
len: usize,
munmap: unsafe fn(*mut c_void, usize) -> Result<()>,
) -> Self {
Self::with_base(memory, len, munmap, memory as usize, 0)
}
pub(crate) fn with_base(
memory: *mut c_void,
len: usize,
munmap: unsafe fn(*mut c_void, usize) -> Result<()>,
base: usize,
offset: usize,
) -> Self {
let backing = Arc::new(ElfSegmentBacking::new(memory, len, munmap));
let slice = ElfSegmentSlice::new(offset, len, backing);
Self {
base,
slices: alloc::vec![slice].into_boxed_slice(),
}
}
pub(crate) fn from_slices(base: usize, mut slices: Vec<ElfSegmentSlice>) -> Self {
slices.sort_by_key(|slice| (slice.offset(), slice.len()));
for pair in slices.windows(2) {
let previous = &pair[0];
let next = &pair[1];
let previous_end = previous
.offset
.checked_add(previous.len)
.expect("ELF segment slice range overflowed");
assert!(
previous_end <= next.offset,
"ELF segment slices must not overlap",
);
}
let slices = slices.into_boxed_slice();
Self { base, slices }
}
pub(crate) fn create_backing(
memory: *mut c_void,
len: usize,
munmap: unsafe fn(*mut c_void, usize) -> Result<()>,
) -> Arc<ElfSegmentBacking> {
Arc::new(ElfSegmentBacking::new(memory, len, munmap))
}
#[inline]
pub(crate) fn slice(
offset: usize,
len: usize,
backing: Arc<ElfSegmentBacking>,
) -> ElfSegmentSlice {
ElfSegmentSlice::new(offset, len, backing)
}
#[inline]
pub(crate) fn set_base(&mut self, base: usize) {
self.base = base;
}
#[inline]
#[cfg(windows)]
pub(crate) fn primary_backing(&self) -> Option<(*mut c_void, usize)> {
self.slices
.first()
.map(|slice| (slice.backing.memory, slice.backing.len))
}
#[inline]
pub fn is_empty(&self) -> bool {
self.slices.is_empty()
}
#[inline]
pub fn mapped_base(&self) -> usize {
self.slices
.first()
.map(|slice| self.slice_base(slice))
.unwrap_or(0)
}
#[inline]
pub fn mapped_len(&self) -> usize {
let Some((first, last)) = self.slices.first().zip(self.slices.last()) else {
return 0;
};
let base = self.slice_base(first);
let end = self.slice_end(last);
end.saturating_sub(base)
}
#[inline]
pub fn contains_addr(&self, addr: usize) -> bool {
self.slices.iter().any(|slice| {
addr.checked_sub(self.slice_base(slice))
.is_some_and(|offset| offset < slice.len)
})
}
pub fn is_contiguous_mapping(&self) -> bool {
self.slices
.windows(2)
.all(|pair| self.slice_end(&pair[0]) == self.slice_base(&pair[1]))
}
#[inline]
pub(crate) fn get_slice<T>(&self, start: usize, len: usize) -> &'static [T] {
if len == 0 {
return unsafe { core::slice::from_raw_parts(NonNull::<T>::dangling().as_ptr(), 0) };
}
let byte_len = len;
assert!(
self.find_slice(start, byte_len).is_some(),
"ELF segment range is not fully backed by one mapped slice",
);
unsafe { core::slice::from_raw_parts(self.get_ptr::<T>(start), len / size_of::<T>()) }
}
#[inline]
pub(crate) fn get_slice_mut<T>(&self, start: usize, len: usize) -> &'static mut [T] {
if len == 0 {
return unsafe {
core::slice::from_raw_parts_mut(NonNull::<T>::dangling().as_ptr(), 0)
};
}
let byte_len = len;
assert!(
self.find_slice(start, byte_len).is_some(),
"ELF segment range is not fully backed by one mapped slice",
);
unsafe {
core::slice::from_raw_parts_mut(self.get_mut_ptr::<T>(start), len / size_of::<T>())
}
}
#[inline]
pub(crate) fn get_ptr<T>(&self, offset: usize) -> *const T {
assert!(
self.find_slice(offset, size_of::<T>()).is_some() || size_of::<T>() == 0,
"ELF segment pointer is not backed by a mapped slice",
);
self.base_addr().offset(offset).as_ptr()
}
#[inline]
pub(crate) fn get_mut_ptr<T>(&self, offset: usize) -> *mut T {
self.get_ptr::<T>(offset) as *mut T
}
#[inline]
pub(crate) fn write<T>(&self, r_offset: usize, val: RelocValue<T>) {
unsafe { self.get_mut_ptr::<T>(r_offset).write(val.into_inner()) };
}
#[inline]
pub fn base(&self) -> usize {
self.base_addr().into_inner()
}
#[inline]
pub(crate) fn base_addr(&self) -> RelocAddr {
RelocAddr::new(self.base)
}
}