Merge branch 'better_exec' into 'master'

Couple of improvements to the exec implementation

See merge request redox-os/relibc!645
This commit is contained in:
Jeremy Soller
2025-04-12 20:24:20 +00:00
4 changed files with 47 additions and 81 deletions
+21 -76
View File
@@ -8,12 +8,12 @@ use alloc::{boxed::Box, collections::BTreeMap, vec};
#[cfg(target_pointer_width = "32")]
use goblin::elf32::{
header::Header,
program_header::program_header32::{ProgramHeader, PF_W, PF_X, PT_INTERP, PT_LOAD},
program_header::program_header32::{ProgramHeader, PF_R, PF_W, PF_X, PT_INTERP, PT_LOAD},
};
#[cfg(target_pointer_width = "64")]
use goblin::elf64::{
header::Header,
program_header::program_header64::{ProgramHeader, PF_W, PF_X, PT_INTERP, PT_LOAD},
program_header::program_header64::{ProgramHeader, PF_R, PF_W, PF_X, PT_INTERP, PT_LOAD},
};
use syscall::{
@@ -78,7 +78,7 @@ where
// entirely.
let mut header_bytes = [0_u8; size_of::<Header>()];
read_all(*image_file, Some(0), &mut header_bytes)?;
pread_all(*image_file, 0, &mut header_bytes)?;
let header = Header::from_bytes(&header_bytes);
let grants_fd = {
@@ -107,14 +107,17 @@ where
|o| core::mem::take(&mut o.tree),
);
read_all(*image_file as usize, Some(header.e_phoff as u64), phs)
.map_err(|_| Error::new(EIO))?;
pread_all(*image_file as usize, header.e_phoff, phs).map_err(|_| Error::new(EIO))?;
for ph_idx in 0..phnum {
let ph_bytes = &phs[ph_idx * phentsize..(ph_idx + 1) * phentsize];
let segment: &ProgramHeader =
plain::from_bytes(ph_bytes).map_err(|_| Error::new(EINVAL))?;
let mut flags = syscall::PROT_READ;
let mut flags = if segment.p_flags & PF_R == PF_R {
syscall::PROT_READ
} else {
syscall::PROT_NONE
};
// W ^ X. If it is executable, do not allow it to be writable, even if requested
if segment.p_flags & PF_X == PF_X {
@@ -127,11 +130,7 @@ where
// PT_INTERP must come before any PT_LOAD, so we don't have to iterate twice.
PT_INTERP => {
let mut interp = vec![0_u8; segment.p_filesz as usize];
read_all(
*image_file as usize,
Some(segment.p_offset as u64),
&mut interp,
)?;
pread_all(*image_file as usize, segment.p_offset, &mut interp)?;
return Ok(FexecResult::Interp {
path: interp.into_boxed_slice(),
@@ -168,74 +167,22 @@ where
total_page_count * PAGE_SIZE,
flags,
)?;
syscall::lseek(*image_file, segment.p_offset as isize, SEEK_SET)
.map_err(|_| Error::new(EIO))?;
// If unaligned, read the head page separately.
let (first_aligned_page, remaining_filesz) = if voff > 0 {
let bytes_to_next_page = PAGE_SIZE - voff;
// TODO: Attempt to mmap with MAP_PRIVATE directly from the image file instead.
let (_guard, dst_page) =
unsafe { MmapGuard::map_mut_anywhere(*grants_fd, vaddr, PAGE_SIZE)? };
let length = core::cmp::min(bytes_to_next_page, filesz);
read_all(*image_file, None, &mut dst_page[voff..][..length])?;
(vaddr + PAGE_SIZE, filesz - length)
} else {
(vaddr, filesz)
};
let remaining_page_count = remaining_filesz.div_floor(PAGE_SIZE);
let tail_bytes = remaining_filesz % PAGE_SIZE;
// TODO: Unless the calling process if *very* memory-constrained, the max amount of
// pages per iteration has no limit other than the time it takes to setup page
// tables.
//
// TODO: Reserve PAGES_PER_ITER "scratch pages" of virtual memory for that type of
// situation?
const PAGES_PER_ITER: usize = 64;
// TODO: Before this loop, attempt to mmap with MAP_PRIVATE directly from the image
// file.
for page_idx in (0..remaining_page_count).step_by(PAGES_PER_ITER) {
// Use commented out lines to trigger kernel bug (FIXME).
//let pages_in_this_group = core::cmp::min(PAGES_PER_ITER, file_page_count - page_idx * PAGES_PER_ITER);
let pages_in_this_group =
core::cmp::min(PAGES_PER_ITER, remaining_page_count - page_idx);
if pages_in_this_group == 0 {
break;
}
// TODO: MAP_FIXED to optimize away funmap?
if filesz > 0 {
let (_guard, dst_memory) = unsafe {
MmapGuard::map_mut_anywhere(
*grants_fd,
first_aligned_page + page_idx * PAGE_SIZE, // offset
pages_in_this_group * PAGE_SIZE, // size
vaddr, // offset
(voff + filesz).next_multiple_of(PAGE_SIZE), // size
)?
};
// TODO: Are &mut [u8] and &mut [[u8; PAGE_SIZE]] interchangeable (if the
// lengths are aligned, obviously)?
read_all(*image_file, None, dst_memory)?;
}
if tail_bytes > 0 {
let (_guard, dst_page) = unsafe {
MmapGuard::map_mut_anywhere(
*grants_fd,
first_aligned_page + remaining_page_count * PAGE_SIZE,
PAGE_SIZE,
)?
};
read_all(*image_file, None, &mut dst_page[..tail_bytes])?;
pread_all(
*image_file,
segment.p_offset,
&mut dst_memory[voff..voff + filesz],
)?;
}
// file_page_count..file_page_count + zero_page_count are already zero-initialized
@@ -579,10 +526,8 @@ pub fn munmap_transfer(
],
)
}
fn read_all(fd: usize, offset: Option<u64>, buf: &mut [u8]) -> Result<()> {
if let Some(offset) = offset {
syscall::lseek(fd, offset as isize, SEEK_SET)?;
}
fn pread_all(fd: usize, offset: u64, buf: &mut [u8]) -> Result<()> {
syscall::lseek(fd, offset as isize, SEEK_SET)?;
let mut total_bytes_read = 0;
+8 -2
View File
@@ -93,10 +93,16 @@ global_asm!(include_str!("sigsetjmp/aarch64/sigsetjmp.s"));
global_asm!(include_str!("sigsetjmp/riscv64/sigsetjmp.s"));
#[cfg(target_arch = "x86")]
global_asm!(include_str!("sigsetjmp/i386/sigsetjmp.s"), options(att_syntax));
global_asm!(
include_str!("sigsetjmp/i386/sigsetjmp.s"),
options(att_syntax)
);
#[cfg(target_arch = "x86_64")]
global_asm!(include_str!("sigsetjmp/x86_64/sigsetjmp.s"), options(att_syntax));
global_asm!(
include_str!("sigsetjmp/x86_64/sigsetjmp.s"),
options(att_syntax)
);
extern "C" {
pub fn sigsetjmp(jb: *mut u64, savemask: i32) -> i32;
+12 -2
View File
@@ -40,7 +40,12 @@ unsafe fn scatter(iovs: &[iovec], vec: Vec<u8>) {
}
#[no_mangle]
pub unsafe extern "C" fn preadv(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t {
pub unsafe extern "C" fn preadv(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off_t,
) -> ssize_t {
if iovcnt < 0 || iovcnt > IOV_MAX {
platform::ERRNO.set(errno::EINVAL);
return -1;
@@ -57,7 +62,12 @@ pub unsafe extern "C" fn preadv(fd: c_int, iov: *const iovec, iovcnt: c_int, off
}
#[no_mangle]
pub unsafe extern "C" fn pwritev(fd: c_int, iov: *const iovec, iovcnt: c_int, offset: off_t) -> ssize_t {
pub unsafe extern "C" fn pwritev(
fd: c_int,
iov: *const iovec,
iovcnt: c_int,
offset: off_t,
) -> ssize_t {
if iovcnt < 0 || iovcnt > IOV_MAX {
platform::ERRNO.set(errno::EINVAL);
return -1;
+6 -1
View File
@@ -503,7 +503,12 @@ impl Pal for Sys {
fn gettid() -> pid_t {
// This is used by pthread mutexes for reentrant checks and must be nonzero
// and unique for each thread in the same process (but not cross-process)
Self::current_os_tid().thread_fd.checked_add(1).unwrap().try_into().unwrap()
Self::current_os_tid()
.thread_fd
.checked_add(1)
.unwrap()
.try_into()
.unwrap()
}
unsafe fn gettimeofday(tp: *mut timeval, tzp: *mut timezone) -> Result<()> {