use crate::input::ElfReader;
use crate::os::{MapFlags, Mmap, ProtFlags};
use crate::{Result, elf::Phdr, relocation::RelocValue};
use alloc::vec::Vec;
use core::ffi::c_void;
use core::fmt::Debug;
pub(crate) mod program;
pub(crate) mod section;
pub const PAGE_SIZE: usize = 0x1000;
pub const MASK: usize = !(PAGE_SIZE - 1);
enum Address {
Relative(usize),
Absolute(usize),
}
impl Address {
fn absolute_addr(&self) -> usize {
match self {
Address::Relative(_) => unreachable!(),
Address::Absolute(addr) => *addr,
}
}
fn relative_addr(&self) -> usize {
match self {
Address::Relative(addr) => *addr,
Address::Absolute(_) => unreachable!(),
}
}
}
#[derive(Debug)]
struct FileMapInfo {
start: usize,
filesz: usize,
offset: usize,
}
pub(crate) struct ElfSegment {
addr: Address,
prot: ProtFlags,
flags: MapFlags,
len: usize,
zero_size: usize,
content_size: usize,
map_info: Vec<FileMapInfo>,
need_copy: bool,
from_relocatable: bool,
}
impl ElfSegment {
fn rebase(&mut self, base: usize) {
self.addr = Address::Absolute(base + self.addr.relative_addr());
}
fn mmap_segment<M: Mmap>(&mut self, object: &mut impl ElfReader) -> Result<()> {
let mut need_copy = false;
let len = self.len;
let addr = self.addr.absolute_addr();
let prot = if self.from_relocatable {
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE
} else {
self.prot
};
debug_assert!(len % PAGE_SIZE == 0);
if self.map_info.len() == 1 {
debug_assert!(self.map_info[0].offset % PAGE_SIZE == 0);
unsafe {
M::mmap(
Some(addr),
len,
prot,
self.flags,
self.map_info[0].offset,
object.as_fd(),
&mut need_copy,
)
}?
} else {
unsafe { M::mmap(Some(addr), len, prot, self.flags, 0, None, &mut need_copy) }?
};
#[cfg(feature = "log")]
log::trace!(
"[Mmap] address: 0x{:x}, length: {}, flags: {:?}, zero_size: {}, map_info: {:?}",
addr,
len,
prot,
self.zero_size,
self.map_info
);
self.need_copy = need_copy;
Ok(())
}
fn copy_data(&self, object: &mut impl ElfReader) -> Result<()> {
if self.need_copy {
let ptr = self.addr.absolute_addr() as *mut u8;
for info in self.map_info.iter() {
unsafe {
let dest = core::slice::from_raw_parts_mut(ptr.add(info.start), info.filesz);
object.read(dest, info.offset)?;
}
}
}
Ok(())
}
fn mprotect<M: Mmap>(&self) -> Result<()> {
if self.need_copy || self.from_relocatable {
let len = self.len;
debug_assert!(len % PAGE_SIZE == 0);
let addr = self.addr.absolute_addr();
unsafe { M::mprotect(addr as _, len, self.prot) }?;
#[cfg(feature = "log")]
log::trace!(
"[Mprotect] address: 0x{:x}, length: {}, prot: {:?}",
addr,
len,
self.prot,
);
}
Ok(())
}
fn fill_zero<M: Mmap>(&self) -> Result<()> {
if self.zero_size > 0 {
let zero_start = self.addr.absolute_addr() + self.content_size;
let zero_end = roundup(zero_start, PAGE_SIZE);
let write_len = zero_end - zero_start;
let ptr = zero_start as *mut u8;
unsafe {
ptr.write_bytes(0, write_len);
};
if write_len < self.zero_size {
let zero_mmap_addr = zero_end;
let zero_mmap_len = self.zero_size - write_len;
let prot = if self.from_relocatable {
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE
} else {
self.prot
};
unsafe {
M::mmap_anonymous(
zero_mmap_addr,
zero_mmap_len,
prot,
MapFlags::MAP_PRIVATE | MapFlags::MAP_FIXED,
)?;
}
}
}
Ok(())
}
}
pub(crate) trait SegmentBuilder {
fn create_space<M: Mmap>(&mut self) -> Result<ElfSegments>;
fn create_segments(&mut self) -> Result<()>;
fn segments_mut(&mut self) -> &mut [ElfSegment];
fn segments(&self) -> &[ElfSegment];
fn load_segments<M: Mmap>(&mut self, object: &mut impl ElfReader) -> Result<ElfSegments> {
let space = self.create_space::<M>()?;
self.create_segments()?;
let segments = self.segments_mut();
let base = space.base();
#[cfg(windows)]
let mut last_addr = space.memory as usize;
for segment in segments.iter_mut() {
segment.rebase(base);
#[cfg(windows)]
if object.as_fd().is_some() {
let addr = segment.addr.absolute_addr();
let len = segment.len;
if addr > last_addr {
crate::os::virtual_free(last_addr, addr - last_addr)?;
}
let space_end = space.memory as usize + space.len();
if addr + len < space_end {
crate::os::virtual_free(addr + len, space_end - (addr + len))?;
}
last_addr = addr + len;
}
segment.mmap_segment::<M>(object)?;
segment.copy_data(object)?;
segment.fill_zero::<M>()?;
}
Ok(space)
}
fn mprotect<M: Mmap>(&self) -> Result<()> {
let segments = self.segments();
for segment in segments.iter() {
segment.mprotect::<M>()?;
}
Ok(())
}
}
#[allow(unused)]
pub(crate) struct ELFRelro {
addr: usize,
len: usize,
mprotect: unsafe fn(*mut c_void, usize, ProtFlags) -> Result<()>,
}
impl ELFRelro {
pub(crate) fn new<M: Mmap>(phdr: &Phdr, base: usize) -> ELFRelro {
ELFRelro {
addr: base + phdr.p_vaddr as usize,
len: phdr.p_memsz as usize,
mprotect: M::mprotect,
}
}
}
#[inline]
fn roundup(x: usize, align: usize) -> usize {
if align == 0 {
return x;
}
(x + align - 1) & !(align - 1)
}
#[inline]
fn rounddown(x: usize, align: usize) -> usize {
x & !(align - 1)
}
pub struct ElfSegments {
pub(crate) memory: *mut c_void,
pub(crate) offset: usize,
pub(crate) len: usize,
pub(crate) munmap: unsafe fn(*mut c_void, usize) -> Result<()>,
}
impl Debug for ElfSegments {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ElfSegments")
.field("base", &format_args!("0x{:x}", self.base()))
.field("memory", &self.memory)
.field("len", &self.len)
.field("offset", &format_args!("0x{:x}", self.offset))
.finish()
}
}
impl ELFRelro {
#[inline]
pub(crate) fn relro(&self) -> Result<()> {
let end = roundup(self.addr + self.len, PAGE_SIZE);
let start = self.addr & MASK;
let start_addr = start as *mut c_void;
unsafe {
(self.mprotect)(start_addr, end - start, ProtFlags::PROT_READ)?;
}
Ok(())
}
}
impl Drop for ElfSegments {
fn drop(&mut self) {
unsafe {
(self.munmap)(self.memory, self.len).unwrap();
}
}
}
impl ElfSegments {
pub(crate) fn new(
memory: *mut c_void,
len: usize,
munmap: unsafe fn(*mut c_void, usize) -> Result<()>,
) -> Self {
ElfSegments {
memory,
offset: 0,
len,
munmap,
}
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub(crate) fn get_slice<T>(&self, start: usize, len: usize) -> &'static [T] {
unsafe {
debug_assert!(start + len - self.offset <= self.len);
core::slice::from_raw_parts(self.get_ptr::<T>(start), len / size_of::<T>())
}
}
pub(crate) fn get_slice_mut<T>(&self, start: usize, len: usize) -> &'static mut [T] {
unsafe {
debug_assert!(start + len - self.offset <= self.len);
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 {
debug_assert!(offset - self.offset < self.len);
(self.base() + offset) as *const T
}
#[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.0) };
}
#[inline]
pub fn base(&self) -> usize {
self.memory as usize - self.offset
}
}